From 258a214d31f06cc532368821798b861d2cffd800 Mon Sep 17 00:00:00 2001 From: skallweitNV <64953474+skallweitNV@users.noreply.github.com> Date: Mon, 22 May 2023 17:51:22 +0200 Subject: [PATCH] Falcor 6.0-preview --- .clang-format-ignore | 11 - .github/workflows/compile.yml | 4 +- .gitignore | 3 +- .gitmodules | 3 - CMakeLists.txt | 7 +- CMakePresets.json | 122 +- README.md | 6 +- Source/Falcor/CMakeLists.txt | 164 +- Source/Falcor/Core/API/BlendState.cpp | 7 +- Source/Falcor/Core/API/BlendState.h | 8 +- Source/Falcor/Core/API/BlitContext.cpp | 26 +- Source/Falcor/Core/API/BlitContext.h | 18 +- Source/Falcor/Core/API/Buffer.cpp | 118 +- Source/Falcor/Core/API/Buffer.h | 210 +- Source/Falcor/Core/API/ComputeContext.cpp | 6 +- Source/Falcor/Core/API/ComputeContext.h | 3 +- Source/Falcor/Core/API/ComputeStateObject.cpp | 6 +- Source/Falcor/Core/API/ComputeStateObject.h | 23 +- Source/Falcor/Core/API/CopyContext.cpp | 13 +- Source/Falcor/Core/API/CopyContext.h | 6 +- Source/Falcor/Core/API/DepthStencilState.cpp | 7 +- Source/Falcor/Core/API/DepthStencilState.h | 8 +- Source/Falcor/Core/API/Device.cpp | 228 +- Source/Falcor/Core/API/Device.h | 48 +- Source/Falcor/Core/API/FBO.cpp | 64 +- Source/Falcor/Core/API/FBO.h | 46 +- Source/Falcor/Core/API/FencedPool.h | 13 +- Source/Falcor/Core/API/Formats.cpp | 23 +- Source/Falcor/Core/API/GpuFence.cpp | 12 +- Source/Falcor/Core/API/GpuFence.h | 15 +- Source/Falcor/Core/API/GpuMemoryHeap.cpp | 18 +- Source/Falcor/Core/API/GpuMemoryHeap.h | 18 +- Source/Falcor/Core/API/GpuTimer.cpp | 48 +- Source/Falcor/Core/API/GpuTimer.h | 19 +- .../Falcor/Core/API/GraphicsStateObject.cpp | 18 +- Source/Falcor/Core/API/GraphicsStateObject.h | 53 +- Source/Falcor/Core/API/Handles.h | 4 +- .../Falcor/Core/API/LowLevelContextData.cpp | 3 +- Source/Falcor/Core/API/LowLevelContextData.h | 5 +- Source/Falcor/Core/API/NativeFormats.h | 4 +- Source/Falcor/Core/API/NvApiExDesc.h | 4 +- Source/Falcor/Core/API/ParameterBlock.cpp | 135 +- Source/Falcor/Core/API/ParameterBlock.h | 245 +- Source/Falcor/Core/API/QueryHeap.cpp | 12 +- Source/Falcor/Core/API/QueryHeap.h | 15 +- Source/Falcor/Core/API/RasterizerState.cpp | 7 +- Source/Falcor/Core/API/RasterizerState.h | 8 +- Source/Falcor/Core/API/RenderContext.cpp | 212 +- Source/Falcor/Core/API/RenderContext.h | 21 +- Source/Falcor/Core/API/Resource.cpp | 40 +- Source/Falcor/Core/API/Resource.h | 34 +- Source/Falcor/Core/API/ResourceViews.cpp | 94 +- Source/Falcor/Core/API/ResourceViews.h | 101 +- .../Core/API/RtAccelerationStructure.cpp | 12 +- .../Falcor/Core/API/RtAccelerationStructure.h | 24 +- ...AccelerationStructurePostBuildInfoPool.cpp | 7 +- ...RtAccelerationStructurePostBuildInfoPool.h | 10 +- Source/Falcor/Core/API/RtStateObject.cpp | 8 +- Source/Falcor/Core/API/RtStateObject.h | 24 +- Source/Falcor/Core/API/Sampler.cpp | 14 +- Source/Falcor/Core/API/Sampler.h | 58 +- Source/Falcor/Core/API/Shader.cpp | 7 +- Source/Falcor/Core/API/Shader.h | 159 +- Source/Falcor/Core/API/ShaderTable.h | 4 +- .../API/Shared/D3D12ConstantBufferView.cpp | 12 +- .../Core/API/Shared/D3D12ConstantBufferView.h | 15 +- .../Core/API/Shared/D3D12DescriptorData.h | 2 +- .../Core/API/Shared/D3D12DescriptorHeap.cpp | 18 +- .../Core/API/Shared/D3D12DescriptorHeap.h | 21 +- .../Core/API/Shared/D3D12DescriptorPool.cpp | 7 +- .../Core/API/Shared/D3D12DescriptorPool.h | 12 +- .../Core/API/Shared/D3D12DescriptorSet.cpp | 28 +- .../Core/API/Shared/D3D12DescriptorSet.h | 16 +- .../Core/API/Shared/D3D12RootSignature.cpp | 9 +- .../Core/API/Shared/D3D12RootSignature.h | 14 +- .../API/Shared/MockedD3D12StagingBuffer.cpp | 2 +- .../API/Shared/MockedD3D12StagingBuffer.h | 6 +- Source/Falcor/Core/API/Swapchain.cpp | 9 +- Source/Falcor/Core/API/Swapchain.h | 14 +- Source/Falcor/Core/API/Texture.cpp | 236 +- Source/Falcor/Core/API/Texture.h | 67 +- Source/Falcor/Core/API/VAO.cpp | 22 +- Source/Falcor/Core/API/VAO.h | 33 +- Source/Falcor/Core/API/VertexLayout.cpp | 3 +- Source/Falcor/Core/API/VertexLayout.h | 23 +- Source/Falcor/Core/API/fwd.h | 3 + Source/Falcor/Core/Assert.h | 32 +- Source/Falcor/Core/Macros.h | 18 +- Source/Falcor/Core/Object.cpp | 135 + Source/Falcor/Core/Object.h | 507 +++ .../Core/ObjectPython.h} | 18 +- Source/Falcor/Core/Pass/BaseGraphicsPass.cpp | 69 + .../Pass}/BaseGraphicsPass.h | 85 +- Source/Falcor/Core/Pass/ComputePass.cpp | 96 + .../BasePasses => Core/Pass}/ComputePass.h | 123 +- Source/Falcor/Core/Pass/FullScreenPass.cpp | 131 + .../Pass}/FullScreenPass.gs.slang | 12 +- Source/Falcor/Core/Pass/FullScreenPass.h | 89 + .../Pass}/FullScreenPass.vs.slang | 12 +- .../BasePasses => Core/Pass}/RasterPass.cpp | 49 +- Source/Falcor/Core/Pass/RasterPass.h | 91 + Source/Falcor/Core/Platform/Linux/Linux.cpp | 1 - Source/Falcor/Core/Platform/LockFile.cpp | 3 +- Source/Falcor/Core/Platform/OS.cpp | 10 +- Source/Falcor/Core/Platform/OS.h | 15 +- Source/Falcor/Core/Platform/PlatformHandles.h | 18 +- .../Core/Platform/Windows/ProgressBarWin.cpp | 6 +- .../Falcor/Core/Platform/Windows/Windows.cpp | 18 +- Source/Falcor/Core/Plugin.cpp | 8 +- Source/Falcor/Core/Program/ComputeProgram.cpp | 23 +- Source/Falcor/Core/Program/ComputeProgram.h | 12 +- .../Falcor/Core/Program/GraphicsProgram.cpp | 23 +- Source/Falcor/Core/Program/GraphicsProgram.h | 10 +- Source/Falcor/Core/Program/Program.cpp | 32 +- Source/Falcor/Core/Program/Program.h | 30 +- Source/Falcor/Core/Program/ProgramManager.cpp | 153 +- Source/Falcor/Core/Program/ProgramManager.h | 19 +- .../Falcor/Core/Program/ProgramReflection.cpp | 183 +- .../Falcor/Core/Program/ProgramReflection.h | 200 +- Source/Falcor/Core/Program/ProgramVars.cpp | 76 +- Source/Falcor/Core/Program/ProgramVars.h | 38 +- Source/Falcor/Core/Program/ProgramVersion.cpp | 34 +- Source/Falcor/Core/Program/ProgramVersion.h | 58 +- Source/Falcor/Core/Program/RtBindingTable.cpp | 4 +- Source/Falcor/Core/Program/RtBindingTable.h | 6 +- Source/Falcor/Core/Program/RtProgram.cpp | 20 +- Source/Falcor/Core/Program/RtProgram.h | 16 +- Source/Falcor/Core/Program/ShaderVar.cpp | 67 +- Source/Falcor/Core/Program/ShaderVar.h | 56 +- Source/Falcor/Core/SampleApp.cpp | 156 +- Source/Falcor/Core/SampleApp.h | 44 +- Source/Falcor/Core/State/ComputeState.cpp | 19 +- Source/Falcor/Core/State/ComputeState.h | 28 +- Source/Falcor/Core/State/GraphicsState.cpp | 39 +- Source/Falcor/Core/State/GraphicsState.h | 60 +- Source/Falcor/Core/State/StateGraph.h | 12 - Source/Falcor/Core/Testbed.cpp | 147 +- Source/Falcor/Core/Testbed.h | 31 +- Source/Falcor/Core/Window.cpp | 143 +- Source/Falcor/Core/Window.h | 10 +- Source/Falcor/Falcor.h | 7 +- Source/Falcor/Falcor.natvis | 86 +- Source/Falcor/FalcorPython.cpp | 4 +- .../BaseGraphicsPass.cpp => GlobalState.cpp} | 63 +- Source/Falcor/GlobalState.h | 54 + .../RenderGraph/BasePasses/ComputePass.cpp | 87 - .../RenderGraph/BasePasses/FullScreenPass.cpp | 159 - .../RenderGraph/BasePasses/FullScreenPass.h | 72 - .../RenderGraph/BasePasses/RasterPass.h | 76 - .../RenderGraph/BasePasses/RasterScenePass.h | 82 - Source/Falcor/RenderGraph/RenderGraph.cpp | 1215 +++---- Source/Falcor/RenderGraph/RenderGraph.h | 600 ++-- .../RenderGraph/RenderGraphCompiler.cpp | 658 ++-- .../Falcor/RenderGraph/RenderGraphCompiler.h | 74 +- Source/Falcor/RenderGraph/RenderGraphExe.cpp | 113 +- Source/Falcor/RenderGraph/RenderGraphExe.h | 114 +- Source/Falcor/RenderGraph/RenderGraphIR.cpp | 124 +- Source/Falcor/RenderGraph/RenderGraphIR.h | 58 +- .../RenderGraph/RenderGraphImportExport.cpp | 238 +- .../RenderGraph/RenderGraphImportExport.h | 49 +- Source/Falcor/RenderGraph/RenderGraphUI.cpp | 2279 ++++++------- Source/Falcor/RenderGraph/RenderGraphUI.h | 362 ++- Source/Falcor/RenderGraph/RenderPass.cpp | 50 +- Source/Falcor/RenderGraph/RenderPass.h | 383 ++- .../Falcor/RenderGraph/RenderPassHelpers.cpp | 50 +- Source/Falcor/RenderGraph/RenderPassHelpers.h | 241 +- .../RenderGraph/RenderPassReflection.cpp | 491 +-- .../Falcor/RenderGraph/RenderPassReflection.h | 304 +- .../RenderGraph/RenderPassStandardFlags.h | 48 +- Source/Falcor/RenderGraph/ResourceCache.cpp | 256 +- Source/Falcor/RenderGraph/ResourceCache.h | 126 +- Source/Falcor/RenderPasses/ResolvePass.cpp | 57 +- Source/Falcor/RenderPasses/ResolvePass.h | 38 +- .../Shared/Denoising/NRDBuffers.slang | 64 +- .../Shared/Denoising/NRDData.slang | 37 +- .../Shared/Denoising/NRDHelpers.slang | 19 +- .../Rendering/Lights/EmissiveLightSampler.h | 8 +- .../Rendering/Lights/EmissivePowerSampler.cpp | 9 +- .../Rendering/Lights/EmissivePowerSampler.h | 16 +- .../Lights/EmissiveUniformSampler.cpp | 9 +- .../Rendering/Lights/EmissiveUniformSampler.h | 12 +- .../Falcor/Rendering/Lights/EnvMapSampler.cpp | 61 +- .../Falcor/Rendering/Lights/EnvMapSampler.h | 26 +- Source/Falcor/Rendering/Lights/LightBVH.cpp | 16 +- Source/Falcor/Rendering/Lights/LightBVH.h | 20 +- .../Rendering/Lights/LightBVHBuilder.cpp | 55 +- .../Rendering/Lights/LightBVHSampler.cpp | 9 +- .../Falcor/Rendering/Lights/LightBVHSampler.h | 10 +- .../Rendering/Materials/BSDFIntegrator.cpp | 16 +- .../Materials/BSDFIntegrator.cs.slang | 8 +- .../Rendering/Materials/BSDFIntegrator.h | 22 +- .../Rendering/Materials/BSDFs/BeerBTDF.slang | 4 +- .../Materials/BSDFs/DielectricPlateBSDF.slang | 7 +- .../Materials/BSDFs/DiffuseSpecularBRDF.slang | 3 +- .../Materials/BSDFs/DisneyDiffuseBRDF.slang | 3 +- .../BSDFs/FrostbiteDiffuseBRDF.slang | 7 +- .../Materials/BSDFs/LambertDiffuseBRDF.slang | 3 +- .../Materials/BSDFs/LambertDiffuseBTDF.slang | 3 +- .../Materials/BSDFs/OrenNayarBRDF.slang | 3 +- .../Rendering/Materials/BSDFs/SheenBSDF.slang | 6 +- .../Materials/BSDFs/SimpleBTDF.slang | 4 +- .../Materials/BSDFs/SpecularMicrofacet.slang | 9 +- .../Materials/BSDFs/StandardBSDF.slang | 40 +- Source/Falcor/Rendering/Materials/BxDF.slang | 896 ------ .../Rendering/Materials/ClothMaterial.slang | 21 +- .../Materials/ClothMaterialInstance.slang | 8 +- .../Rendering/Materials/HairChiang16.slang | 8 +- .../Rendering/Materials/HairMaterial.slang | 15 +- .../Materials/HairMaterialInstance.slang | 24 +- Source/Falcor/Rendering/Materials/IBSDF.slang | 21 +- .../Rendering/Materials/IMaterial.slang | 12 +- .../Materials/IMaterialInstance.slang | 19 +- .../Rendering/Materials/MERLMaterial.slang | 10 +- .../Materials/MERLMaterialInstance.slang | 12 +- .../Rendering/Materials/MERLMixMaterial.slang | 10 +- .../Materials/MERLMixMaterialInstance.slang | 15 +- .../PBRT/PBRTCoatedConductorMaterial.slang | 25 +- .../PBRTCoatedConductorMaterialInstance.slang | 27 +- .../PBRT/PBRTCoatedDiffuseMaterial.slang | 25 +- .../PBRTCoatedDiffuseMaterialInstance.slang | 25 +- .../PBRT/PBRTConductorMaterial.slang | 21 +- .../PBRT/PBRTConductorMaterialInstance.slang | 28 +- .../PBRT/PBRTDielectricMaterial.slang | 21 +- .../PBRT/PBRTDielectricMaterialInstance.slang | 36 +- .../Materials/PBRT/PBRTDiffuseMaterial.slang | 21 +- .../PBRT/PBRTDiffuseMaterialInstance.slang | 25 +- .../PBRTDiffuseTransmissionMaterial.slang | 21 +- ...TDiffuseTransmissionMaterialInstance.slang | 15 +- .../Rendering/Materials/RGLAcquisition.cpp | 11 +- .../Materials/RGLAcquisition.cs.slang | 8 +- .../Rendering/Materials/RGLAcquisition.h | 50 +- .../Rendering/Materials/RGLMaterial.slang | 12 +- .../Materials/RGLMaterialInstance.slang | 14 +- .../Materials/StandardMaterial.slang | 21 +- .../Materials/StandardMaterialInstance.slang | 82 +- Source/Falcor/Rendering/RTXDI/RTXDI.cpp | 51 +- Source/Falcor/Rendering/RTXDI/RTXDI.h | 65 +- Source/Falcor/Rendering/Utils/PixelStats.cpp | 71 +- Source/Falcor/Rendering/Utils/PixelStats.h | 40 +- .../Rendering/Volumes/GridVolumeSampler.cpp | 15 +- .../Rendering/Volumes/GridVolumeSampler.h | 14 +- .../Rendering/Volumes/GridVolumeSampler.slang | 4 +- Source/Falcor/Scene/Animation/Animatable.cpp | 4 +- Source/Falcor/Scene/Animation/Animatable.h | 11 +- .../Scene/Animation/AnimatedVertexCache.cpp | 45 +- .../Scene/Animation/AnimatedVertexCache.h | 46 +- Source/Falcor/Scene/Animation/Animation.cpp | 77 +- Source/Falcor/Scene/Animation/Animation.h | 19 +- .../Scene/Animation/AnimationController.cpp | 49 +- .../Scene/Animation/AnimationController.h | 47 +- Source/Falcor/Scene/Camera/Camera.cpp | 77 +- Source/Falcor/Scene/Camera/Camera.h | 42 +- .../Falcor/Scene/Camera/CameraController.cpp | 42 +- Source/Falcor/Scene/Camera/CameraController.h | 24 +- .../Falcor/Scene/Curves/CurveTessellation.cpp | 32 +- .../Falcor/Scene/Curves/CurveTessellation.h | 4 +- Source/Falcor/Scene/HitInfo.h | 4 +- Source/Falcor/Scene/Importer.h | 7 +- Source/Falcor/Scene/Lights/EnvMap.cpp | 32 +- Source/Falcor/Scene/Lights/EnvMap.h | 23 +- Source/Falcor/Scene/Lights/Light.cpp | 118 +- Source/Falcor/Scene/Lights/Light.h | 61 +- .../Falcor/Scene/Lights/LightCollection.cpp | 125 +- Source/Falcor/Scene/Lights/LightCollection.h | 59 +- .../Scene/Lights/LightCollectionShared.slang | 22 +- Source/Falcor/Scene/Lights/LightProfile.cpp | 31 +- Source/Falcor/Scene/Lights/LightProfile.h | 16 +- .../Falcor/Scene/Lights/MeshLightData.slang | 2 +- .../Falcor/Scene/Material/BasicMaterial.cpp | 79 +- Source/Falcor/Scene/Material/BasicMaterial.h | 42 +- .../Scene/Material/BasicMaterialData.slang | 34 +- .../Falcor/Scene/Material/ClothMaterial.cpp | 15 +- Source/Falcor/Scene/Material/ClothMaterial.h | 9 +- .../Scene/Material/DiffuseSpecularData.slang | 2 +- .../Scene/Material/DiffuseSpecularUtils.cpp | 2 +- Source/Falcor/Scene/Material/HairMaterial.cpp | 17 +- Source/Falcor/Scene/Material/HairMaterial.h | 10 +- Source/Falcor/Scene/Material/MERLFile.cpp | 10 +- Source/Falcor/Scene/Material/MERLFile.h | 7 +- Source/Falcor/Scene/Material/MERLMaterial.cpp | 29 +- Source/Falcor/Scene/Material/MERLMaterial.h | 23 +- .../Scene/Material/MERLMaterialData.slang | 2 +- .../Falcor/Scene/Material/MERLMixMaterial.cpp | 31 +- .../Falcor/Scene/Material/MERLMixMaterial.h | 33 +- .../Scene/Material/MERLMixMaterialData.slang | 10 +- Source/Falcor/Scene/Material/Material.cpp | 29 +- Source/Falcor/Scene/Material/Material.h | 31 +- .../Falcor/Scene/Material/MaterialData.slang | 40 +- .../Scene/Material/MaterialFactory.slang | 30 +- .../Falcor/Scene/Material/MaterialSystem.cpp | 72 +- Source/Falcor/Scene/Material/MaterialSystem.h | 51 +- .../Scene/Material/MaterialTextureLoader.cpp | 14 +- .../Scene/Material/MaterialTextureLoader.h | 10 +- .../PBRT/PBRTCoatedConductorMaterial.cpp | 15 +- .../PBRT/PBRTCoatedConductorMaterial.h | 9 +- .../PBRT/PBRTCoatedDiffuseMaterial.cpp | 15 +- .../Material/PBRT/PBRTCoatedDiffuseMaterial.h | 9 +- .../Material/PBRT/PBRTConductorMaterial.cpp | 15 +- .../Material/PBRT/PBRTConductorMaterial.h | 9 +- .../Material/PBRT/PBRTDielectricMaterial.cpp | 15 +- .../Material/PBRT/PBRTDielectricMaterial.h | 9 +- .../Material/PBRT/PBRTDiffuseMaterial.cpp | 15 +- .../Scene/Material/PBRT/PBRTDiffuseMaterial.h | 10 +- .../PBRT/PBRTDiffuseTransmissionMaterial.cpp | 15 +- .../PBRT/PBRTDiffuseTransmissionMaterial.h | 10 +- Source/Falcor/Scene/Material/RGLMaterial.cpp | 55 +- Source/Falcor/Scene/Material/RGLMaterial.h | 41 +- .../Scene/Material/RGLMaterialData.slang | 4 +- .../Falcor/Scene/Material/ShadingUtils.slang | 198 +- .../Scene/Material/StandardMaterial.cpp | 21 +- .../Falcor/Scene/Material/StandardMaterial.h | 13 +- .../Falcor/Scene/Material/TextureHandle.slang | 10 +- .../SDFs/NormalizedDenseSDFGrid/NDSDFGrid.cpp | 53 +- .../SDFs/NormalizedDenseSDFGrid/NDSDFGrid.h | 17 +- .../Scene/SDFs/SDF3DPrimitiveFactory.cpp | 12 +- Source/Falcor/Scene/SDFs/SDFGrid.cpp | 58 +- Source/Falcor/Scene/SDFs/SDFGrid.h | 19 +- Source/Falcor/Scene/SDFs/SDFGridHitData.slang | 2 +- .../Scene/SDFs/SparseBrickSet/SDFSBS.cpp | 152 +- .../Falcor/Scene/SDFs/SparseBrickSet/SDFSBS.h | 81 +- .../Scene/SDFs/SparseVoxelOctree/SDFSVO.cpp | 107 +- .../Scene/SDFs/SparseVoxelOctree/SDFSVO.h | 50 +- .../Scene/SDFs/SparseVoxelSet/SDFSVS.cpp | 42 +- .../Falcor/Scene/SDFs/SparseVoxelSet/SDFSVS.h | 30 +- Source/Falcor/Scene/Scene.cpp | 447 +-- Source/Falcor/Scene/Scene.h | 192 +- Source/Falcor/Scene/SceneBuilder.cpp | 201 +- Source/Falcor/Scene/SceneBuilder.h | 91 +- Source/Falcor/Scene/SceneCache.cpp | 106 +- Source/Falcor/Scene/SceneCache.h | 44 +- Source/Falcor/Scene/SceneTypes.slang | 17 +- Source/Falcor/Scene/ShadingData.slang | 36 +- Source/Falcor/Scene/Transform.cpp | 42 +- Source/Falcor/Scene/Transform.h | 13 +- Source/Falcor/Scene/TriangleMesh.cpp | 45 +- Source/Falcor/Scene/TriangleMesh.h | 25 +- Source/Falcor/Scene/Volume/BrickedGrid.h | 8 +- Source/Falcor/Scene/Volume/Grid.cpp | 54 +- Source/Falcor/Scene/Volume/Grid.h | 25 +- Source/Falcor/Scene/Volume/GridConverter.h | 4 +- Source/Falcor/Scene/Volume/GridVolume.cpp | 63 +- Source/Falcor/Scene/Volume/GridVolume.h | 32 +- Source/Falcor/Testing/UnitTest.cpp | 10 +- Source/Falcor/Testing/UnitTest.h | 45 +- Source/Falcor/Utils/Algorithm/BitonicSort.cpp | 87 +- .../Utils/Algorithm/BitonicSort.cs.slang | 80 +- Source/Falcor/Utils/Algorithm/BitonicSort.h | 70 +- Source/Falcor/Utils/Algorithm/DirectedGraph.h | 313 +- .../Utils/Algorithm/DirectedGraphTraversal.h | 307 +- .../Utils/Algorithm/ParallelReduction.cpp | 280 +- .../Algorithm/ParallelReduction.cs.slang | 51 +- .../Utils/Algorithm/ParallelReduction.h | 114 +- .../Algorithm/ParallelReductionType.slangh | 14 +- Source/Falcor/Utils/Algorithm/PrefixSum.cpp | 215 +- .../Falcor/Utils/Algorithm/PrefixSum.cs.slang | 91 +- Source/Falcor/Utils/Algorithm/PrefixSum.h | 73 +- Source/Falcor/Utils/AlignedAllocator.h | 223 +- Source/Falcor/Utils/Attributes.slang | 10 +- Source/Falcor/Utils/BinaryFileStream.h | 285 +- Source/Falcor/Utils/BufferAllocator.cpp | 211 +- Source/Falcor/Utils/BufferAllocator.h | 360 ++- Source/Falcor/Utils/Color/ColorHelpers.slang | 124 +- Source/Falcor/Utils/Color/ColorMap.slang | 107 +- Source/Falcor/Utils/Color/ColorUtils.h | 325 +- Source/Falcor/Utils/Color/SampledSpectrum.h | 323 +- Source/Falcor/Utils/Color/Spectrum.cpp | 650 ++-- Source/Falcor/Utils/Color/Spectrum.h | 429 +-- Source/Falcor/Utils/Color/SpectrumUtils.cpp | 36 +- Source/Falcor/Utils/Color/SpectrumUtils.h | 237 +- Source/Falcor/Utils/Color/SpectrumUtils.slang | 26 +- Source/Falcor/Utils/CryptoUtils.cpp | 371 ++- Source/Falcor/Utils/CryptoUtils.h | 101 +- Source/Falcor/Utils/CudaUtils.cpp | 251 ++ Source/Falcor/Utils/CudaUtils.h | 131 + Source/Falcor/Utils/Debug/DebugConsole.h | 133 +- Source/Falcor/Utils/Debug/PixelDebug.cpp | 385 +-- Source/Falcor/Utils/Debug/PixelDebug.h | 162 +- Source/Falcor/Utils/Debug/PixelDebug.slang | 246 +- .../Falcor/Utils/Debug/PixelDebugTypes.slang | 19 +- .../Debug/ReflectPixelDebugTypes.cs.slang | 7 +- Source/Falcor/Utils/Debug/WarpProfiler.cpp | 8 +- Source/Falcor/Utils/Debug/WarpProfiler.h | 8 +- .../Utils/Geometry/GeometryHelpers.slang | 305 +- .../Utils/Geometry/IntersectionHelpers.slang | 118 +- Source/Falcor/Utils/HostDeviceShared.slangh | 59 +- .../Falcor/Utils/Image/AsyncTextureLoader.cpp | 196 +- .../Falcor/Utils/Image/AsyncTextureLoader.h | 130 +- Source/Falcor/Utils/Image/Bitmap.cpp | 965 +++--- Source/Falcor/Utils/Image/Bitmap.h | 241 +- .../Utils/Image/CopyColorChannel.cs.slang | 11 +- Source/Falcor/Utils/Image/ImageIO.cpp | 1246 +++---- Source/Falcor/Utils/Image/ImageIO.h | 152 +- Source/Falcor/Utils/Image/ImageProcessing.cpp | 123 +- Source/Falcor/Utils/Image/ImageProcessing.h | 57 +- Source/Falcor/Utils/Image/TextureAnalyzer.cpp | 203 +- .../Utils/Image/TextureAnalyzer.cs.slang | 35 +- Source/Falcor/Utils/Image/TextureAnalyzer.h | 201 +- Source/Falcor/Utils/Image/TextureManager.cpp | 888 ++--- Source/Falcor/Utils/Image/TextureManager.h | 477 +-- Source/Falcor/Utils/InternalDictionary.h | 114 +- Source/Falcor/Utils/Logger.cpp | 243 +- Source/Falcor/Utils/Logger.h | 263 +- Source/Falcor/Utils/Math/AABB.cpp | 91 +- Source/Falcor/Utils/Math/AABB.h | 392 +-- Source/Falcor/Utils/Math/AABB.slang | 178 +- Source/Falcor/Utils/Math/BitTricks.slang | 135 +- Source/Falcor/Utils/Math/Common.h | 66 +- Source/Falcor/Utils/Math/CubicSpline.h | 254 +- Source/Falcor/Utils/Math/FNVHash.h | 71 +- Source/Falcor/Utils/Math/FalcorMath.h | 315 +- Source/Falcor/Utils/Math/Float16.cpp | 265 ++ Source/Falcor/Utils/Math/Float16.h | 244 +- Source/Falcor/Utils/Math/FormatConversion.h | 93 + .../Falcor/Utils/Math/FormatConversion.slang | 295 +- Source/Falcor/Utils/Math/HalfUtils.slang | 43 +- Source/Falcor/Utils/Math/HashUtils.slang | 41 +- .../Utils/Math/IntervalArithmetic.slang | 136 +- Source/Falcor/Utils/Math/MathConstants.slangh | 50 +- Source/Falcor/Utils/Math/MathHelpers.h | 162 +- Source/Falcor/Utils/Math/MathHelpers.slang | 439 +-- Source/Falcor/Utils/Math/Matrix.h | 11 +- Source/Falcor/Utils/Math/Matrix/Matrix.h | 461 --- Source/Falcor/Utils/Math/MatrixMath.h | 782 +++++ Source/Falcor/Utils/Math/MatrixTypes.h | 221 ++ Source/Falcor/Utils/Math/MatrixUtils.slang | 22 +- Source/Falcor/Utils/Math/PackedFormats.h | 107 +- Source/Falcor/Utils/Math/PackedFormats.slang | 82 +- Source/Falcor/Utils/Math/Quaternion.h | 31 + Source/Falcor/Utils/Math/Quaternion.slang | 19 +- Source/Falcor/Utils/Math/QuaternionMath.h | 533 +++ Source/Falcor/Utils/Math/QuaternionTypes.h | 81 + Source/Falcor/Utils/Math/Ray.h | 48 +- Source/Falcor/Utils/Math/Ray.slang | 38 +- Source/Falcor/Utils/Math/Rectangle.cpp | 65 +- Source/Falcor/Utils/Math/Rectangle.h | 94 +- Source/Falcor/Utils/Math/ScalarMath.h | 276 ++ Source/Falcor/Utils/Math/ScalarTypes.h | 149 + Source/Falcor/Utils/Math/ShadingFrame.slang | 150 + .../Utils/Math/SphericalHarmonics.slang | 36 +- Source/Falcor/Utils/Math/Vector.h | 82 +- Source/Falcor/Utils/Math/VectorMath.h | 1817 +++++++++++ Source/Falcor/Utils/Math/VectorSwizzle2.inl.h | 42 + Source/Falcor/Utils/Math/VectorSwizzle3.inl.h | 75 + Source/Falcor/Utils/Math/VectorSwizzle4.inl.h | 375 +++ Source/Falcor/Utils/Math/VectorTypes.h | 294 ++ Source/Falcor/Utils/NumericRange.h | 76 +- Source/Falcor/Utils/ObjectID.h | 232 +- .../ObjectIDPython.h} | 40 +- Source/Falcor/Utils/PathResolving.cpp | 1 - Source/Falcor/Utils/SDF/SDF2DDraw.slang | 118 +- Source/Falcor/Utils/SDF/SDF2DPrimitives.slang | 122 +- Source/Falcor/Utils/SDF/SDF3DShapes.slang | 115 +- Source/Falcor/Utils/SDF/SDFOperations.slang | 58 +- .../SampleGenerators/CPUSampleGenerator.h | 51 +- .../SampleGenerators/DxSamplePattern.cpp | 33 +- .../Utils/SampleGenerators/DxSamplePattern.h | 51 +- .../SampleGenerators/HaltonSamplePattern.cpp | 73 +- .../SampleGenerators/HaltonSamplePattern.h | 43 +- .../StratifiedSamplePattern.cpp | 92 +- .../StratifiedSamplePattern.h | 70 +- Source/Falcor/Utils/Sampling/AliasTable.cpp | 199 +- Source/Falcor/Utils/Sampling/AliasTable.h | 84 +- Source/Falcor/Utils/Sampling/AliasTable.slang | 49 +- .../Utils/Sampling/Pseudorandom/LCG.slang | 37 +- .../Sampling/Pseudorandom/SplitMix64.slang | 46 +- .../Sampling/Pseudorandom/Xorshift32.slang | 37 +- .../Utils/Sampling/Pseudorandom/Xoshiro.slang | 50 +- .../Falcor/Utils/Sampling/SampleGenerator.cpp | 80 +- .../Falcor/Utils/Sampling/SampleGenerator.h | 161 +- .../Utils/Sampling/SampleGenerator.slang | 33 +- .../Sampling/SampleGeneratorInterface.slang | 30 +- .../Utils/Sampling/SampleGeneratorType.slangh | 8 +- .../Sampling/TinyUniformSampleGenerator.slang | 47 +- .../Sampling/UniformSampleGenerator.slang | 37 +- Source/Falcor/Utils/Scripting/Console.cpp | 258 +- Source/Falcor/Utils/Scripting/Console.h | 67 +- Source/Falcor/Utils/Scripting/Dictionary.h | 191 +- .../Falcor/Utils/Scripting/ScriptBindings.cpp | 496 +-- .../Falcor/Utils/Scripting/ScriptBindings.h | 415 +-- Source/Falcor/Utils/Scripting/ScriptWriter.h | 134 +- Source/Falcor/Utils/Scripting/Scripting.cpp | 217 +- Source/Falcor/Utils/Scripting/Scripting.h | 223 +- Source/Falcor/Utils/Scripting/ndarray.cpp | 920 ++++++ Source/Falcor/Utils/Scripting/ndarray.h | 520 +++ Source/Falcor/Utils/Settings.cpp | 22 +- Source/Falcor/Utils/Settings.h | 60 +- Source/Falcor/Utils/SharedCache.h | 75 + Source/Falcor/Utils/SlangUtils.slang | 14 +- Source/Falcor/Utils/StringUtils.cpp | 455 +-- Source/Falcor/Utils/StringUtils.h | 316 +- Source/Falcor/Utils/TermColor.cpp | 87 +- Source/Falcor/Utils/TermColor.h | 37 +- Source/Falcor/Utils/Threading.cpp | 98 +- Source/Falcor/Utils/Threading.h | 166 +- Source/Falcor/Utils/Timing/Clock.cpp | 544 ++-- Source/Falcor/Utils/Timing/Clock.h | 378 ++- Source/Falcor/Utils/Timing/CpuTimer.h | 86 +- Source/Falcor/Utils/Timing/FrameRate.cpp | 17 +- Source/Falcor/Utils/Timing/FrameRate.h | 120 +- Source/Falcor/Utils/Timing/GpuTimer.slang | 52 +- Source/Falcor/Utils/Timing/Profiler.cpp | 667 ++-- Source/Falcor/Utils/Timing/Profiler.h | 442 +-- Source/Falcor/Utils/Timing/ProfilerUI.cpp | 638 ++-- Source/Falcor/Utils/Timing/ProfilerUI.h | 171 +- Source/Falcor/Utils/Timing/TimeReport.cpp | 69 +- Source/Falcor/Utils/Timing/TimeReport.h | 70 +- Source/Falcor/Utils/UI/Font.cpp | 136 +- Source/Falcor/Utils/UI/Font.h | 117 +- Source/Falcor/Utils/UI/Gui.cpp | 2864 +++++++++-------- Source/Falcor/Utils/UI/Gui.h | 1112 ++++--- Source/Falcor/Utils/UI/Gui.slang | 4 +- Source/Falcor/Utils/UI/InputState.cpp | 94 +- Source/Falcor/Utils/UI/InputState.h | 224 +- Source/Falcor/Utils/UI/InputTypes.h | 461 +-- Source/Falcor/Utils/UI/PixelZoom.cpp | 147 +- Source/Falcor/Utils/UI/PixelZoom.h | 79 +- Source/Falcor/Utils/UI/SpectrumUI.cpp | 1049 +++--- Source/Falcor/Utils/UI/SpectrumUI.h | 210 +- Source/Falcor/Utils/UI/TextRenderer.3d.slang | 22 +- Source/Falcor/Utils/UI/TextRenderer.cpp | 280 +- Source/Falcor/Utils/UI/TextRenderer.h | 98 +- Source/Falcor/Utils/Video/VideoEncoder.cpp | 435 --- Source/Falcor/Utils/Video/VideoEncoder.h | 100 - Source/Falcor/Utils/Video/VideoEncoderUI.cpp | 130 - Source/Falcor/Utils/Video/VideoEncoderUI.h | 99 - Source/Mogwai/CMakeLists.txt | 2 - .../Extensions/Capture/CaptureTrigger.cpp | 6 +- .../Extensions/Capture/CaptureTrigger.h | 6 +- .../Extensions/Capture/FrameCapture.cpp | 6 +- .../Extensions/Capture/VideoCapture.cpp | 224 -- .../Mogwai/Extensions/Capture/VideoCapture.h | 65 - .../Extensions/Profiler/TimingCapture.cpp | 4 +- .../Extensions/Profiler/TimingCapture.h | 4 +- Source/Mogwai/Mogwai.cpp | 48 +- Source/Mogwai/Mogwai.h | 34 +- Source/Mogwai/MogwaiScripting.cpp | 6 +- Source/Mogwai/MogwaiSettings.cpp | 2 +- .../AccumulatePass/AccumulatePass.cpp | 46 +- .../AccumulatePass/AccumulatePass.h | 27 +- Source/RenderPasses/BSDFViewer/BSDFViewer.cpp | 25 +- .../BSDFViewer/BSDFViewer.cs.slang | 20 +- Source/RenderPasses/BSDFViewer/BSDFViewer.h | 28 +- Source/RenderPasses/BlitPass/BlitPass.cpp | 19 +- Source/RenderPasses/BlitPass/BlitPass.h | 8 +- Source/RenderPasses/DLSSPass/DLSSPass.cpp | 35 +- Source/RenderPasses/DLSSPass/DLSSPass.h | 16 +- Source/RenderPasses/DLSSPass/NGXWrapper.cpp | 18 +- Source/RenderPasses/DLSSPass/NGXWrapper.h | 4 +- .../DebugPasses/ColorMapPass/ColorMapPass.cpp | 29 +- .../DebugPasses/ColorMapPass/ColorMapPass.h | 23 +- .../DebugPasses/ComparisonPass.cpp | 22 +- .../RenderPasses/DebugPasses/ComparisonPass.h | 17 +- .../InvalidPixelDetectionPass.cpp | 14 +- .../InvalidPixelDetectionPass.h | 15 +- .../SideBySidePass/SideBySidePass.cpp | 15 +- .../SideBySidePass/SideBySidePass.h | 6 +- .../SplitScreenPass/SplitScreenPass.cpp | 26 +- .../SplitScreenPass/SplitScreenPass.h | 8 +- .../ErrorMeasurePass/ErrorMeasurePass.cpp | 44 +- .../ErrorMeasurePass/ErrorMeasurePass.h | 17 +- Source/RenderPasses/FLIPPass/FLIPPass.cpp | 63 +- Source/RenderPasses/FLIPPass/FLIPPass.h | 24 +- .../RenderPasses/FLIPPass/ToneMappers.slang | 4 +- .../RenderPasses/GBuffer/GBuffer/GBuffer.cpp | 4 +- Source/RenderPasses/GBuffer/GBuffer/GBuffer.h | 2 +- .../GBuffer/GBuffer/GBufferHelpers.slang | 6 +- .../GBuffer/GBuffer/GBufferRT.cpp | 61 +- .../RenderPasses/GBuffer/GBuffer/GBufferRT.h | 19 +- .../GBuffer/GBuffer/GBufferRT.slang | 10 +- .../GBuffer/GBuffer/GBufferRaster.3d.slang | 5 +- .../GBuffer/GBuffer/GBufferRaster.cpp | 59 +- .../GBuffer/GBuffer/GBufferRaster.h | 22 +- Source/RenderPasses/GBuffer/GBufferBase.cpp | 8 +- Source/RenderPasses/GBuffer/GBufferBase.h | 11 +- .../GBuffer/VBuffer/VBufferRT.cpp | 57 +- .../RenderPasses/GBuffer/VBuffer/VBufferRT.h | 19 +- .../GBuffer/VBuffer/VBufferRaster.cpp | 51 +- .../GBuffer/VBuffer/VBufferRaster.h | 16 +- .../RenderPasses/ImageLoader/ImageLoader.cpp | 33 +- Source/RenderPasses/ImageLoader/ImageLoader.h | 11 +- .../MinimalPathTracer/MinimalPathTracer.cpp | 11 +- .../MinimalPathTracer/MinimalPathTracer.h | 18 +- .../MinimalPathTracer.rt.slang | 11 +- .../ModulateIllumination.cpp | 18 +- .../ModulateIllumination.h | 9 +- Source/RenderPasses/NRDPass/NRDPass.cpp | 190 +- Source/RenderPasses/NRDPass/NRDPass.h | 44 +- .../RenderPasses/OptixDenoiser/CMakeLists.txt | 5 +- .../OptixDenoiser/OptixDenoiser.cpp | 66 +- .../OptixDenoiser/OptixDenoiser.h | 38 +- .../{CudaUtils.cpp => OptixUtils.cpp} | 141 +- .../{CudaUtils.h => OptixUtils.h} | 40 - .../PathTracer/GeneratePaths.cs.slang | 7 +- .../PathTracer/LoadShadingData.slang | 27 +- .../RenderPasses/PathTracer/NRDHelpers.slang | 4 +- Source/RenderPasses/PathTracer/PathTracer.cpp | 73 +- Source/RenderPasses/PathTracer/PathTracer.h | 59 +- .../RenderPasses/PathTracer/PathTracer.slang | 55 +- .../PathTracer/PathTracerNRD.slang | 20 +- .../PathTracer/ResolvePass.cs.slang | 4 +- .../PixelInspector.cs.slang | 11 +- .../PixelInspectorPass/PixelInspectorPass.cpp | 39 +- .../PixelInspectorPass/PixelInspectorPass.h | 21 +- .../RTXDIPass/FinalShading.cs.slang | 9 +- .../RTXDIPass/LoadShadingData.slang | 17 +- .../RTXDIPass/PrepareSurfaceData.cs.slang | 5 +- Source/RenderPasses/RTXDIPass/RTXDIPass.cpp | 60 +- Source/RenderPasses/RTXDIPass/RTXDIPass.h | 25 +- .../RenderPassTemplate/RenderPassTemplate.cpp | 5 +- .../RenderPassTemplate/RenderPassTemplate.h | 13 +- Source/RenderPasses/SDFEditor/Marker2DSet.cpp | 7 +- Source/RenderPasses/SDFEditor/Marker2DSet.h | 13 +- Source/RenderPasses/SDFEditor/SDFEditor.cpp | 218 +- Source/RenderPasses/SDFEditor/SDFEditor.h | 48 +- .../RenderPasses/SDFEditor/SelectionWheel.cpp | 13 +- .../RenderPasses/SDFEditor/SelectionWheel.h | 11 +- Source/RenderPasses/SVGFPass/SVGFPass.cpp | 73 +- Source/RenderPasses/SVGFPass/SVGFPass.h | 49 +- .../SceneDebugger/SceneDebugger.cpp | 39 +- .../SceneDebugger/SceneDebugger.cs.slang | 14 +- .../SceneDebugger/SceneDebugger.h | 22 +- .../SceneDebugger/SharedTypes.slang | 2 +- .../SimplePostFX/SimplePostFX.cpp | 168 +- .../RenderPasses/SimplePostFX/SimplePostFX.h | 23 +- Source/RenderPasses/TAA/TAA.cpp | 38 +- Source/RenderPasses/TAA/TAA.h | 16 +- Source/RenderPasses/TestPasses/CMakeLists.txt | 3 + Source/RenderPasses/TestPasses/TestPasses.cpp | 6 +- .../TestPasses/TestPyTorchPass.cpp | 243 ++ .../TestPasses/TestPyTorchPass.cs.slang | 63 + .../RenderPasses/TestPasses/TestPyTorchPass.h | 75 + .../RenderPasses/TestPasses/TestRtProgram.cpp | 15 +- .../RenderPasses/TestPasses/TestRtProgram.h | 20 +- Source/RenderPasses/ToneMapper/ToneMapper.cpp | 83 +- Source/RenderPasses/ToneMapper/ToneMapper.h | 26 +- Source/RenderPasses/Utils/CMakeLists.txt | 4 + .../Utils/Composite/Composite.cpp | 25 +- .../RenderPasses/Utils/Composite/Composite.h | 11 +- .../Utils/CrossFade/CrossFade.cpp | 173 + .../Utils/CrossFade/CrossFade.cs.slang | 51 + .../RenderPasses/Utils/CrossFade/CrossFade.h | 66 + .../Utils/GaussianBlur/GaussianBlur.cpp | 41 +- .../Utils/GaussianBlur/GaussianBlur.h | 18 +- Source/RenderPasses/Utils/Utils.cpp | 5 +- .../WhittedRayTracer/WhittedRayTracer.cpp | 25 +- .../WhittedRayTracer/WhittedRayTracer.h | 23 +- .../WhittedRayTracer.rt.slang | 37 +- Source/Samples/CudaInterop/CudaInterop.cpp | 7 +- Source/Samples/CudaInterop/CudaInterop.h | 8 +- Source/Samples/CudaInterop/FalcorCUDA.cpp | 66 +- Source/Samples/CudaInterop/FalcorCUDA.h | 6 +- Source/Samples/HelloDXR/HelloDXR.cpp | 51 +- Source/Samples/HelloDXR/HelloDXR.h | 20 +- .../SampleAppTemplate/SampleAppTemplate.cpp | 4 +- .../SampleAppTemplate/SampleAppTemplate.h | 4 +- Source/Samples/ShaderToy/ShaderToy.cpp | 9 +- Source/Samples/ShaderToy/ShaderToy.h | 16 +- .../Visualization2D/Visualization2D.cpp | 19 +- .../Samples/Visualization2D/Visualization2D.h | 8 +- Source/Tools/FalcorTest/CMakeLists.txt | 9 +- Source/Tools/FalcorTest/FalcorTest.cpp | 2 +- Source/Tools/FalcorTest/FalcorTest.h | 4 +- .../Tests/Core/BufferAccessTests.cpp | 8 +- .../FalcorTest/Tests/Core/BufferTests.cpp | 6 +- .../FalcorTest/Tests/Core/DDSReadTests.cpp | 14 +- .../FalcorTest/Tests/Core/LargeBuffer.cpp | 8 +- .../FalcorTest/Tests/Core/ObjectTests.cpp | 172 + .../FalcorTest/Tests/Core/ParamBlockCB.cpp | 4 +- .../Tests/Core/ResourceAliasing.cpp | 49 +- .../Tests/Core/ResourceAliasing.cs.slang | 27 +- .../Tests/Core/RootBufferParamBlockTests.cpp | 22 +- .../Tests/Core/RootBufferStructTests.cpp | 12 +- .../FalcorTest/Tests/Core/RootBufferTests.cpp | 24 +- .../Tests/Core/TextureLoadTests.cs.slang | 12 +- .../FalcorTest/Tests/Core/TextureTests.cpp | 51 +- .../InvalidPixelDetectionTests.cpp | 10 +- .../Materials/BSDFIntegratorTests.cpp | 8 +- .../Rendering/Materials/MicrofacetTests.cpp | 6 +- .../Materials/RGLAcquisitionTests.cpp | 6 +- .../Tests/Sampling/AliasTableTests.cpp | 13 +- .../Tests/Sampling/PseudorandomTests.cpp | 10 +- .../Tests/Sampling/SampleGeneratorTests.cpp | 4 +- .../FalcorTest/Tests/Scene/EnvMapTests.cpp | 9 +- .../Tests/Scene/Material/BSDFTests.cpp | 136 +- .../Scene/Material/HairChiang16Tests.cpp | 8 +- .../Tests/Scene/Material/MERLFileTests.cpp | 2 +- .../FalcorTest/Tests/Slang/CastFloat16.cpp | 2 +- .../FalcorTest/Tests/Slang/Float16Tests.cpp | 2 +- .../FalcorTest/Tests/Slang/Float64Tests.cpp | 2 +- .../Tests/Slang/InheritanceTests.cpp | 4 +- .../FalcorTest/Tests/Slang/Int64Tests.cpp | 2 +- .../FalcorTest/Tests/Slang/ShaderString.cpp | 2 +- .../Tests/Slang/SlangInheritance.cpp | 2 +- .../Tests/Slang/SlangMutatingTests.cpp | 2 +- .../Tests/Slang/SlangReinterpretCast.cpp | 2 +- .../Tests/Slang/StructuredBufferMatrix.cpp | 4 +- .../FalcorTest/Tests/Slang/TemplatedLoad.cpp | 2 +- .../Tests/Slang/UnboundedDescriptorArray.cpp | 2 +- .../Tools/FalcorTest/Tests/Slang/WaveOps.cpp | 10 +- .../FalcorTest/Tests/Utils/BitTricksTests.cpp | 4 +- .../Tests/Utils/BitonicSortTests.cpp | 4 +- .../Tests/Utils/BufferAllocatorTests.cpp | 8 +- .../Tests/Utils/Color/SpectrumUtilsTests.cpp | 4 +- .../Tests/Utils/ColorUtilsTests.cpp | 18 +- .../Tests/Utils/Debug/WarpProfilerTests.cpp | 2 +- .../Tests/Utils/Float16TypesTests.cpp | 16 +- .../Tests/Utils/GeometryHelpersTests.cpp | 25 +- .../FalcorTest/Tests/Utils/HashUtilsTests.cpp | 8 +- .../Utils/Image/TextureManagerTests.cpp} | 42 +- .../Tests/Utils/ImageProcessing.cpp | 3 +- .../Tests/Utils/IntersectionHelpersTests.cpp | 22 +- .../Tests/Utils/MathHelpersTests.cpp | 5 +- .../FalcorTest/Tests/Utils/MatrixTests.cpp | 759 ++++- .../Tests/Utils/ParallelReductionTests.cpp | 23 +- .../FalcorTest/Tests/Utils/PrefixSumTests.cpp | 6 +- .../Tests/Utils/QuaternionTests.cpp | 531 +++ .../FalcorTest/Tests/Utils/SettingsTests.cpp | 21 +- .../Tests/Utils/StringUtilsTests.cpp | 8 + .../Tests/Utils/TextureAnalyzerTests.cpp | 13 +- .../FalcorTest/Tests/Utils/VectorTests.cpp | 4 +- Source/Tools/ImageCompare/ImageCompare.cpp | 12 +- .../RenderGraphEditor/RenderGraphEditor.cpp | 22 +- .../RenderGraphEditor/RenderGraphEditor.h | 8 +- .../AssimpImporter/AssimpImporter.cpp | 188 +- .../importers/AssimpImporter/AssimpImporter.h | 4 +- .../importers/PBRTImporter/Builder.cpp | 30 +- .../plugins/importers/PBRTImporter/Builder.h | 59 +- .../importers/PBRTImporter/EnvMapConverter.h | 18 +- .../importers/PBRTImporter/LoopSubdivide.cpp | 32 +- .../importers/PBRTImporter/PBRTImporter.cpp | 130 +- .../importers/PBRTImporter/PBRTImporter.h | 4 +- .../PythonImporter/PythonImporter.cpp | 21 +- .../importers/PythonImporter/PythonImporter.h | 4 +- .../importers/USDImporter/CMakeLists.txt | 5 +- .../importers/USDImporter/ImporterContext.cpp | 550 ++-- .../importers/USDImporter/ImporterContext.h | 53 +- .../importers/USDImporter/IndexedVector.h} | 90 +- .../USDImporter/PreviewSurfaceConverter.cpp | 172 +- .../USDImporter/PreviewSurfaceConverter.h | 46 +- .../USDImporter/StandardMaterialSpec.h | 16 +- .../importers/USDImporter/Subdivision.cpp | 296 -- .../importers/USDImporter/Tessellation.cpp | 537 ++++ .../importers/USDImporter/Tessellation.h | 100 + .../importers/USDImporter/USDHelpers.h | 15 +- .../importers/USDImporter/USDImporter.cpp | 52 +- .../importers/USDImporter/USDImporter.h | 4 +- Source/plugins/importers/USDImporter/Utils.h | 43 +- build_scripts/deploycommon.bat | 4 +- build_scripts/deploycommon.sh | 78 +- build_scripts/falcor__init__.py | 11 + build_scripts/generate_stubs.py | 399 +-- build_scripts/pybind11_stubgen.py | 1224 +++++++ data/tests/tiny_mip0.png | Bin 0 -> 127 bytes data/tests/tiny_mip1.png | Bin 0 -> 125 bytes data/tests/tiny_mip2.png | Bin 0 -> 119 bytes dependencies.xml | 27 +- docs/development/coding-conventions.md | 42 +- docs/development/unit-testing.md | 54 +- external/CMakeLists.txt | 144 +- external/glm | 1 - .../imguinodegrapheditor.cpp | 15 +- scripts/python/test_pytorch.py | 81 + setup.sh | 4 - setup_vs2019.bat | 36 - .../renderpasses/graphs/CrossFadePass.py | 18 + .../renderpasses/test_CrossFadePass.py | 16 + .../renderpasses/test_PathTracerMaterials.py | 4 + .../renderscripts/test_PathTracerNRD.py | 2 +- tests/python_tests/core/__init__.py | 1 + tests/python_tests/core/test_device.py | 26 + tests/python_tests/test_dummy.py | 12 + tests/run_python_tests.bat | 9 + tests/run_python_tests.sh | 12 + tests/testing/core/config.py | 46 +- tests/testing/core/environment.py | 28 +- tests/testing/libs/xmlrunner/LICENSE.txt | 26 + tests/testing/libs/xmlrunner/__init__.py | 13 + tests/testing/libs/xmlrunner/__main__.py | 19 + tests/testing/libs/xmlrunner/builder.py | 247 ++ tests/testing/libs/xmlrunner/result.py | 677 ++++ tests/testing/libs/xmlrunner/runner.py | 192 ++ tests/testing/libs/xmlrunner/unittest.py | 15 + tests/testing/libs/xmlrunner/version.py | 2 + tests/testing/run_image_tests.py | 48 +- tests/testing/run_python_tests.py | 120 + tests/testing/run_unit_tests.py | 75 +- tools/format_code.sh | 11 +- tools/pymacro.bat | 9 + tools/pymacro.py | 117 + tools/remove_hungarian_notation.py | 45 + 790 files changed, 42744 insertions(+), 31839 deletions(-) create mode 100644 Source/Falcor/Core/Object.cpp create mode 100644 Source/Falcor/Core/Object.h rename Source/{RenderPasses/OptixDenoiser/BootstrapUtils.cpp => Falcor/Core/ObjectPython.h} (77%) create mode 100644 Source/Falcor/Core/Pass/BaseGraphicsPass.cpp rename Source/Falcor/{RenderGraph/BasePasses => Core/Pass}/BaseGraphicsPass.h (52%) create mode 100644 Source/Falcor/Core/Pass/ComputePass.cpp rename Source/Falcor/{RenderGraph/BasePasses => Core/Pass}/ComputePass.h (50%) create mode 100644 Source/Falcor/Core/Pass/FullScreenPass.cpp rename Source/Falcor/{RenderGraph/BasePasses => Core/Pass}/FullScreenPass.gs.slang (91%) create mode 100644 Source/Falcor/Core/Pass/FullScreenPass.h rename Source/Falcor/{RenderGraph/BasePasses => Core/Pass}/FullScreenPass.vs.slang (87%) rename Source/Falcor/{RenderGraph/BasePasses => Core/Pass}/RasterPass.cpp (57%) create mode 100644 Source/Falcor/Core/Pass/RasterPass.h rename Source/Falcor/{RenderGraph/BasePasses/BaseGraphicsPass.cpp => GlobalState.cpp} (60%) create mode 100644 Source/Falcor/GlobalState.h delete mode 100644 Source/Falcor/RenderGraph/BasePasses/ComputePass.cpp delete mode 100644 Source/Falcor/RenderGraph/BasePasses/FullScreenPass.cpp delete mode 100644 Source/Falcor/RenderGraph/BasePasses/FullScreenPass.h delete mode 100644 Source/Falcor/RenderGraph/BasePasses/RasterPass.h delete mode 100644 Source/Falcor/RenderGraph/BasePasses/RasterScenePass.h delete mode 100644 Source/Falcor/Rendering/Materials/BxDF.slang create mode 100644 Source/Falcor/Utils/CudaUtils.cpp create mode 100644 Source/Falcor/Utils/CudaUtils.h create mode 100644 Source/Falcor/Utils/Math/Float16.cpp create mode 100644 Source/Falcor/Utils/Math/FormatConversion.h delete mode 100644 Source/Falcor/Utils/Math/Matrix/Matrix.h create mode 100644 Source/Falcor/Utils/Math/MatrixMath.h create mode 100644 Source/Falcor/Utils/Math/MatrixTypes.h create mode 100644 Source/Falcor/Utils/Math/Quaternion.h create mode 100644 Source/Falcor/Utils/Math/QuaternionMath.h create mode 100644 Source/Falcor/Utils/Math/QuaternionTypes.h create mode 100644 Source/Falcor/Utils/Math/ScalarMath.h create mode 100644 Source/Falcor/Utils/Math/ScalarTypes.h create mode 100644 Source/Falcor/Utils/Math/ShadingFrame.slang create mode 100644 Source/Falcor/Utils/Math/VectorMath.h create mode 100644 Source/Falcor/Utils/Math/VectorSwizzle2.inl.h create mode 100644 Source/Falcor/Utils/Math/VectorSwizzle3.inl.h create mode 100644 Source/Falcor/Utils/Math/VectorSwizzle4.inl.h create mode 100644 Source/Falcor/Utils/Math/VectorTypes.h rename Source/Falcor/{Scene/SceneBuilderAccess.h => Utils/ObjectIDPython.h} (64%) create mode 100644 Source/Falcor/Utils/Scripting/ndarray.cpp create mode 100644 Source/Falcor/Utils/Scripting/ndarray.h create mode 100644 Source/Falcor/Utils/SharedCache.h delete mode 100644 Source/Falcor/Utils/Video/VideoEncoder.cpp delete mode 100644 Source/Falcor/Utils/Video/VideoEncoder.h delete mode 100644 Source/Falcor/Utils/Video/VideoEncoderUI.cpp delete mode 100644 Source/Falcor/Utils/Video/VideoEncoderUI.h delete mode 100644 Source/Mogwai/Extensions/Capture/VideoCapture.cpp delete mode 100644 Source/Mogwai/Extensions/Capture/VideoCapture.h rename Source/RenderPasses/OptixDenoiser/{CudaUtils.cpp => OptixUtils.cpp} (52%) rename Source/RenderPasses/OptixDenoiser/{CudaUtils.h => OptixUtils.h} (61%) create mode 100644 Source/RenderPasses/TestPasses/TestPyTorchPass.cpp create mode 100644 Source/RenderPasses/TestPasses/TestPyTorchPass.cs.slang create mode 100644 Source/RenderPasses/TestPasses/TestPyTorchPass.h create mode 100644 Source/RenderPasses/Utils/CrossFade/CrossFade.cpp create mode 100644 Source/RenderPasses/Utils/CrossFade/CrossFade.cs.slang create mode 100644 Source/RenderPasses/Utils/CrossFade/CrossFade.h create mode 100644 Source/Tools/FalcorTest/Tests/Core/ObjectTests.cpp rename Source/{plugins/importers/USDImporter/Subdivision.h => Tools/FalcorTest/Tests/Utils/Image/TextureManagerTests.cpp} (69%) create mode 100644 Source/Tools/FalcorTest/Tests/Utils/QuaternionTests.cpp rename Source/{Falcor/RenderGraph/BasePasses/RasterScenePass.cpp => plugins/importers/USDImporter/IndexedVector.h} (50%) delete mode 100644 Source/plugins/importers/USDImporter/Subdivision.cpp create mode 100644 Source/plugins/importers/USDImporter/Tessellation.cpp create mode 100644 Source/plugins/importers/USDImporter/Tessellation.h create mode 100644 build_scripts/falcor__init__.py create mode 100644 build_scripts/pybind11_stubgen.py create mode 100644 data/tests/tiny_mip0.png create mode 100644 data/tests/tiny_mip1.png create mode 100644 data/tests/tiny_mip2.png delete mode 160000 external/glm create mode 100644 scripts/python/test_pytorch.py delete mode 100644 setup_vs2019.bat create mode 100644 tests/image_tests/renderpasses/graphs/CrossFadePass.py create mode 100644 tests/image_tests/renderpasses/test_CrossFadePass.py create mode 100644 tests/python_tests/core/__init__.py create mode 100644 tests/python_tests/core/test_device.py create mode 100644 tests/python_tests/test_dummy.py create mode 100644 tests/run_python_tests.bat create mode 100644 tests/run_python_tests.sh create mode 100644 tests/testing/libs/xmlrunner/LICENSE.txt create mode 100644 tests/testing/libs/xmlrunner/__init__.py create mode 100644 tests/testing/libs/xmlrunner/__main__.py create mode 100644 tests/testing/libs/xmlrunner/builder.py create mode 100644 tests/testing/libs/xmlrunner/result.py create mode 100644 tests/testing/libs/xmlrunner/runner.py create mode 100644 tests/testing/libs/xmlrunner/unittest.py create mode 100644 tests/testing/libs/xmlrunner/version.py create mode 100644 tests/testing/run_python_tests.py create mode 100644 tools/pymacro.bat create mode 100644 tools/pymacro.py create mode 100644 tools/remove_hungarian_notation.py diff --git a/.clang-format-ignore b/.clang-format-ignore index 0ed69364d..d6eeaacbf 100644 --- a/.clang-format-ignore +++ b/.clang-format-ignore @@ -1,21 +1,10 @@ -Source/Falcor/RenderGraph Source/Falcor/Rendering -Source/Falcor/RenderPasses Source/Falcor/Scene -Source/Falcor/Utils Source/Mogwai Source/plugins/importers/USDImporter Source/RenderPasses # Explicitly whitelisted files -!Source/Falcor/Utils/PathResolving.cpp -!Source/Falcor/Utils/PathResolving.h -!Source/Falcor/Utils/Settings.cpp -!Source/Falcor/Utils/Settings.h -!Source/Falcor/Utils/StringFormatters.h -!Source/Falcor/Utils/Math/Rectangle.cpp -!Source/Falcor/Utils/Math/Rectangle.h -!Source/Falcor/Utils/Algorithm/UnionFind.h !Source/Falcor/Rendering/Materials/BSDFs !Source/RenderPasses/DLSSPass/ diff --git a/.github/workflows/compile.yml b/.github/workflows/compile.yml index 152c1f3f4..e0139dfbc 100644 --- a/.github/workflows/compile.yml +++ b/.github/workflows/compile.yml @@ -6,7 +6,7 @@ on: env: CMAKE_EXE: tools\.packman\cmake\bin\cmake.exe - CMAKE_BUILD_PRESET: windows-ninja-msvc + CMAKE_BUILD_PRESET: windows-ninja-msvc-ci CMAKE_BUILD_CONFIG: Release jobs: @@ -26,7 +26,7 @@ jobs: with: arch: x64 sdk: 10.0.19041.0 - toolset: 14.29 + toolset: 14.30 - name: Build run: | %CMAKE_EXE% --preset %CMAKE_BUILD_PRESET% diff --git a/.gitignore b/.gitignore index 5dd5f2ffa..cee907802 100644 --- a/.gitignore +++ b/.gitignore @@ -4,8 +4,9 @@ # Test output folder. /tests/data/ -# Media folder. +# Media folders. /media +/media_internal # Packman folders. /external/packman/ diff --git a/.gitmodules b/.gitmodules index 3febc1839..d5b5353ff 100644 --- a/.gitmodules +++ b/.gitmodules @@ -13,9 +13,6 @@ [submodule "external/imgui"] path = external/imgui url = https://github.com/ocornut/imgui.git -[submodule "external/glm"] - path = external/glm - url = https://github.com/g-truc/glm.git [submodule "external/vulkan-headers"] path = external/vulkan-headers url = https://github.com/KhronosGroup/Vulkan-Headers diff --git a/CMakeLists.txt b/CMakeLists.txt index 809a6174c..6d9b9ed1f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -104,9 +104,12 @@ include(git_version) git_version_setup() # On Linux, we build with RPATH set to $ORIGIN to make build relocatable. +# Also, we link with precompiled libraries that are compiled with _GLIBCXX_USE_CXX11_ABI=0, +# so we need to compile with the same flag to avoid ABI incompatibilities. if(FALCOR_LINUX) set(CMAKE_INSTALL_RPATH $ORIGIN) set(CMAKE_BUILD_WITH_INSTALL_RPATH ON) + set(CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS} -D_GLIBCXX_USE_CXX11_ABI=0) endif() @@ -137,7 +140,7 @@ endif() # ----------------------------------------------------------------------------- if(FALCOR_USE_SYSTEM_PYTHON) - find_package(Python 3.7 EXACT COMPONENTS Interpreter Development REQUIRED) + find_package(Python COMPONENTS Interpreter Development REQUIRED) else() set(Python_ROOT_DIR ${CMAKE_SOURCE_DIR}/external/packman/python) find_package(Python COMPONENTS Interpreter Development REQUIRED) @@ -206,7 +209,6 @@ message(STATUS "FALCOR_HAS_NRD: ${FALCOR_HAS_NRD}") message(STATUS "FALCOR_HAS_DLSS: ${FALCOR_HAS_DLSS}") message(STATUS "FALCOR_HAS_NV_USD: ${FALCOR_HAS_NV_USD}") message(STATUS "FALCOR_HAS_MDL_SDK: ${FALCOR_HAS_MDL_SDK}") -message(STATUS "FALCOR_HAS_OPENSUBDIV: ${FALCOR_HAS_OPENSUBDIV}") message(STATUS "FALCOR_ENABLE_USD: ${FALCOR_ENABLE_USD}") # ----------------------------------------------------------------------------- @@ -455,6 +457,7 @@ file(GENERATE OUTPUT ${FALCOR_OUTPUT_DIRECTORY}/settings.json CONTENT "{ \"stand if(plugin_targets) add_dependencies(Mogwai ${plugin_targets}) add_dependencies(FalcorPython ${plugin_targets}) + add_dependencies(Mogwai FalcorPython) endif() # Make Mogwai the default startup project in VS. diff --git a/CMakePresets.json b/CMakePresets.json index 35724ccbb..120965679 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -33,36 +33,6 @@ "CMAKE_SYSTEM_VERSION": "10.0.19041.0" } }, - { - "name": "windows-vs2019-base", - "description": "Base Visual Studio 2019 configuration.", - "hidden": true, - "inherits": "windows-base", - "generator": "Visual Studio 16 2019", - "architecture": { - "value": "x64", - "strategy": "set" - }, - "toolset": { - "value": "host=x86", - "strategy": "set" - } - }, - { - "name": "windows-vs2019", - "displayName": "Windows VS2019", - "inherits": [ - "windows-vs2019-base" - ] - }, - { - "name": "windows-vs2019-ci", - "displayName": "Windows VS2019 (CI)", - "inherits": [ - "windows-vs2019-base", - "ci" - ] - }, { "name": "windows-vs2022-base", "description": "Base Visual Studio 2022 configuration.", @@ -99,12 +69,15 @@ "hidden": true, "inherits": "windows-base", "generator": "Ninja Multi-Config", + "environment": { + "VCToolsVersion": "14.30" + }, "architecture": { "value": "x64", "strategy": "external" }, "toolset": { - "value": "host=x86,version=14.29", + "value": "host=x86,version=14.3", "strategy": "external" }, "cacheVariables": { @@ -138,32 +111,59 @@ "name": "linux-base", "description": "Base Linux configuration.", "hidden": true, - "inherits": "base" + "inherits": "base", + "generator": "Ninja Multi-Config", + "cacheVariables": { + "CMAKE_MAKE_PROGRAM": "${sourceDir}/tools/.packman/ninja/ninja" + } }, { - "name": "linux-ninja-clang-base", + "name": "linux-clang-base", "description": "Base Linux/Clang configuration.", "hidden": true, "inherits": "linux-base", - "generator": "Ninja Multi-Config", "cacheVariables": { - "CMAKE_MAKE_PROGRAM": "${sourceDir}/tools/.packman/ninja/ninja", "CMAKE_C_COMPILER": "clang", "CMAKE_CXX_COMPILER": "clang++" } }, { - "name": "linux-ninja-clang", - "displayName": "Linux Ninja/Clang", + "name": "linux-clang", + "displayName": "Linux/Clang", + "inherits": [ + "linux-clang-base" + ] + }, + { + "name": "linux-clang-ci", + "displayName": "Linux/Clang (CI)", + "inherits": [ + "linux-clang-base", + "ci" + ] + }, + { + "name": "linux-gcc-base", + "description": "Base Linux/GCC configuration.", + "hidden": true, + "inherits": "linux-base", + "cacheVariables": { + "CMAKE_C_COMPILER": "gcc", + "CMAKE_CXX_COMPILER": "g++" + } + }, + { + "name": "linux-gcc", + "displayName": "Linux/GCC", "inherits": [ - "linux-ninja-clang-base" + "linux-gcc-base" ] }, { - "name": "linux-ninja-clang-ci", - "displayName": "Linux Ninja/Clang (CI)", + "name": "linux-gcc-ci", + "displayName": "Linux/GCC (CI)", "inherits": [ - "linux-ninja-clang-base", + "linux-gcc-base", "ci" ] } @@ -194,27 +194,51 @@ "configuration": "Debug" }, { - "name": "linux-ninja-clang-release", + "name": "linux-clang-release", + "displayName": "Release", + "configurePreset": "linux-clang", + "configuration": "Release" + }, + { + "name": "linux-clang-debug", + "displayName": "Debug", + "configurePreset": "linux-clang", + "configuration": "Debug" + }, + { + "name": "linux-clang-ci-release", + "displayName": "Release", + "configurePreset": "linux-clang-ci", + "configuration": "Release" + }, + { + "name": "linux-clang-ci-debug", + "displayName": "Debug", + "configurePreset": "linux-clang-ci", + "configuration": "Debug" + }, + { + "name": "linux-gcc-release", "displayName": "Release", - "configurePreset": "linux-ninja-clang", + "configurePreset": "linux-gcc", "configuration": "Release" }, { - "name": "linux-ninja-clang-debug", + "name": "linux-gcc-debug", "displayName": "Debug", - "configurePreset": "linux-ninja-clang", + "configurePreset": "linux-gcc", "configuration": "Debug" }, { - "name": "linux-ninja-clang-ci-release", + "name": "linux-gcc-ci-release", "displayName": "Release", - "configurePreset": "linux-ninja-clang-ci", + "configurePreset": "linux-gcc-ci", "configuration": "Release" }, { - "name": "linux-ninja-clang-ci-debug", + "name": "linux-gcc-ci-debug", "displayName": "Debug", - "configurePreset": "linux-ninja-clang-ci", + "configurePreset": "linux-gcc-ci", "configuration": "Debug" } ] diff --git a/README.md b/README.md index 6146b9021..00c729a0f 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,7 @@ Optional: Falcor uses the [CMake](https://cmake.org) build system. Additional information on how to use Falcor with CMake is available in the [CMake](docs/development/cmake.md) development documetation page. ### Visual Studio -If you are working with Visual Studio, you can setup a native Visual Studio solution by running `setup_vs2019.bat` (or `setup_vs2022.bat`, same process) after cloning this repository. The solution files are written to `build/windows-vs2019` and the binary output is located in `build/windows-vs2019/bin`. +If you are working with Visual Studio 2022, you can setup a native Visual Studio solution by running `setup_vs2022.bat` after cloning this repository. The solution files are written to `build/windows-vs2022` and the binary output is located in `build/windows-vs2022/bin`. ### Visual Studio Code @@ -45,10 +45,10 @@ Falcor uses _CMake Presets_ store in `CMakePresets.json` to provide a set of com $ cmake --list-presets Available configure presets: - "windows-vs2019" - Windows VS2019 "windows-vs2022" - Windows VS2022 "windows-ninja-msvc" - Windows Ninja/MSVC - "linux-ninja-clang" - Linux Ninja/Clang + "linux-clang" - Linux Ninja/Clang + "linux-gcc" - Linux Ninja/GCC ``` Use `cmake --preset ` to generate the build tree for a given preset. The build tree is written to the `build/` folder and the binary output files are in `build//bin`. diff --git a/Source/Falcor/CMakeLists.txt b/Source/Falcor/CMakeLists.txt index d1e34a015..2ca962dd0 100644 --- a/Source/Falcor/CMakeLists.txt +++ b/Source/Falcor/CMakeLists.txt @@ -5,6 +5,8 @@ target_sources(Falcor PRIVATE Falcor.manifest Falcor.h + GlobalState.cpp + GlobalState.h Core/Assert.h Core/ErrorHandling.cpp @@ -14,6 +16,9 @@ target_sources(Falcor PRIVATE Core/FalcorConfig.h Core/GLFW.h Core/HotReloadFlags.h + Core/Object.cpp + Core/Object.h + Core/ObjectPython.h Core/Macros.h Core/Plugin.cpp Core/Plugin.h @@ -102,6 +107,17 @@ target_sources(Falcor PRIVATE Core/API/VertexLayout.cpp Core/API/VertexLayout.h + Core/Pass/BaseGraphicsPass.cpp + Core/Pass/BaseGraphicsPass.h + Core/Pass/ComputePass.cpp + Core/Pass/ComputePass.h + Core/Pass/FullScreenPass.cpp + Core/Pass/FullScreenPass.gs.slang + Core/Pass/FullScreenPass.h + Core/Pass/FullScreenPass.vs.slang + Core/Pass/RasterPass.cpp + Core/Pass/RasterPass.h + Core/Platform/LockFile.cpp Core/Platform/LockFile.h Core/Platform/MemoryMappedFile.cpp @@ -164,19 +180,6 @@ target_sources(Falcor PRIVATE RenderGraph/ResourceCache.cpp RenderGraph/ResourceCache.h - RenderGraph/BasePasses/BaseGraphicsPass.cpp - RenderGraph/BasePasses/BaseGraphicsPass.h - RenderGraph/BasePasses/ComputePass.cpp - RenderGraph/BasePasses/ComputePass.h - RenderGraph/BasePasses/FullScreenPass.cpp - RenderGraph/BasePasses/FullScreenPass.gs.slang - RenderGraph/BasePasses/FullScreenPass.h - RenderGraph/BasePasses/FullScreenPass.vs.slang - RenderGraph/BasePasses/RasterPass.cpp - RenderGraph/BasePasses/RasterPass.h - RenderGraph/BasePasses/RasterScenePass.cpp - RenderGraph/BasePasses/RasterScenePass.h - Rendering/Lights/EmissiveLightSampler.cpp Rendering/Lights/EmissiveLightSampler.h Rendering/Lights/EmissiveLightSampler.slang @@ -211,7 +214,6 @@ target_sources(Falcor PRIVATE Rendering/Materials/BSDFIntegrator.cpp Rendering/Materials/BSDFIntegrator.cs.slang Rendering/Materials/BSDFIntegrator.h - Rendering/Materials/BxDF.slang Rendering/Materials/BSDFConfig.slangh Rendering/Materials/ClothBRDF.slang Rendering/Materials/ClothMaterialInstance.slang @@ -326,7 +328,6 @@ target_sources(Falcor PRIVATE Scene/SceneBlock.slang Scene/SceneBuilder.cpp Scene/SceneBuilder.h - Scene/SceneBuilderAccess.h Scene/SceneCache.cpp Scene/SceneCache.h Scene/SceneDefines.slangh @@ -530,10 +531,12 @@ target_sources(Falcor PRIVATE Utils/NVAPI.slang Utils/NVAPI.slangh Utils/ObjectID.h + Utils/ObjectIDPython.h Utils/PathResolving.cpp Utils/PathResolving.h Utils/Settings.cpp Utils/Settings.h + Utils/SharedCache.h Utils/SlangUtils.slang Utils/StringFormatters.h Utils/StringUtils.cpp @@ -602,8 +605,10 @@ target_sources(Falcor PRIVATE Utils/Math/Common.h Utils/Math/CubicSpline.h Utils/Math/FalcorMath.h + Utils/Math/Float16.cpp Utils/Math/Float16.h Utils/Math/FNVHash.h + Utils/Math/FormatConversion.h Utils/Math/FormatConversion.slang Utils/Math/HalfUtils.slang Utils/Math/HashUtils.slang @@ -611,19 +616,27 @@ target_sources(Falcor PRIVATE Utils/Math/MathConstants.slangh Utils/Math/MathHelpers.h Utils/Math/MathHelpers.slang - Utils/Math/MatrixUtils.slang Utils/Math/Matrix.h + Utils/Math/MatrixMath.h + Utils/Math/MatrixTypes.h + Utils/Math/MatrixUtils.slang Utils/Math/PackedFormats.h Utils/Math/PackedFormats.slang + Utils/Math/Quaternion.h Utils/Math/Quaternion.slang + Utils/Math/QuaternionMath.h + Utils/Math/QuaternionTypes.h Utils/Math/Ray.h Utils/Math/Ray.slang Utils/Math/Rectangle.cpp Utils/Math/Rectangle.h + Utils/Math/ScalarMath.h + Utils/Math/ScalarTypes.h + Utils/Math/ShadingFrame.slang Utils/Math/SphericalHarmonics.slang Utils/Math/Vector.h - - Utils/Math/Matrix/Matrix.h + Utils/Math/VectorMath.h + Utils/Math/VectorTypes.h Utils/SampleGenerators/CPUSampleGenerator.h Utils/SampleGenerators/DxSamplePattern.cpp @@ -654,6 +667,8 @@ target_sources(Falcor PRIVATE Utils/Scripting/Console.cpp Utils/Scripting/Console.h Utils/Scripting/Dictionary.h + Utils/Scripting/ndarray.cpp + Utils/Scripting/ndarray.h Utils/Scripting/ScriptBindings.cpp Utils/Scripting/ScriptBindings.h Utils/Scripting/Scripting.cpp @@ -694,11 +709,6 @@ target_sources(Falcor PRIVATE Utils/UI/TextRenderer.cpp Utils/UI/TextRenderer.h Utils/UI/TextRenderer.3d.slang - - Utils/Video/VideoEncoder.cpp - Utils/Video/VideoEncoder.h - Utils/Video/VideoEncoderUI.cpp - Utils/Video/VideoEncoderUI.h ) @@ -735,6 +745,13 @@ if(FALCOR_HAS_D3D12) ) endif() +if(FALCOR_HAS_CUDA) + target_sources(Falcor PRIVATE + Utils/CudaUtils.cpp + Utils/CudaUtils.h + ) +endif() + target_source_group(Falcor "/") # ImGui source files @@ -766,28 +783,54 @@ target_compile_features(Falcor target_compile_options(Falcor PUBLIC # MSVC flags. - $<$:/Zi> # generate debug symbols - $<$:/WX> # warnings as errors - $<$:/W3> # increase warning level - $<$:/wd4819> # The file contains a character that cannot be represented in the current code page (932) - $<$:/w15038> # data member 'member1' will be initialized after data member 'member2' - $<$:/wd4251> # 'type' : class 'type1' needs to have dll-interface to be used by clients of class 'type2' - $<$:/wd4244> # 'conversion' conversion from 'type1' to 'type2', possible loss of data - $<$:/wd4267> # 'var' : conversion from 'size_t' to 'type', possible loss of data - $<$:/MP> # enable multi-processor compilation + $<$: + /Zi # generate debug symbols + /WX # warnings as errors + /W4 # increase warning level + /wd4251 # 'type' : class 'type1' needs to have dll-interface to be used by clients of class 'type2' + /wd4244 # 'conversion' conversion from 'type1' to 'type2', possible loss of data + /wd4267 # 'var' : conversion from 'size_t' to 'type', possible loss of data + /wd4100 # unreferenced formal parameter + /wd4201 # nonstandard extension used: nameless struct/union + /wd4245 # conversion from 'type1' to 'type2', signed/unsigned mismatch + /wd4189 # local variable is initialized but not referenced + /wd4127 # conditional expression is constant + /wd4701 # potentially uninitialized local variable 'name' used + /wd4703 # potentially uninitialized local pointer variable 'name' used + /wd4324 # structure was padded due to alignment specifier + /wd4505 # unreferenced local function has been removed + /wd4702 # unreachable code + /wd4389 # signed/unsigned mismatch + /wd4459 # declaration of 'identifier' hides global declaration + /wd4268 # 'identifier' : 'const' static/global data initialized with compiler generated default constructor fills the object with zeros + /MP # enable multi-processor compilation + > + # Clang/GCC flags. + $<$,$>: + -fms-extensions # enable MS extensions (among other things allow anonymous structs) + -fvisibility=hidden # hide symbols by default + -Wall # set warning level + -Wno-unused-function + -Wno-unused-variable + -Wno-unused-but-set-variable + -Wno-switch + -Wno-missing-braces + -Wno-invalid-offsetof + > # Clang flags. - $<$:-fms-extensions> # enable MS extensions (among other things allow glm swizzle to work) - $<$:-fvisibility=hidden> # hide symbols by default - $<$:-Wall> # set warning level - $<$:-Wno-unused-function> - $<$:-Wno-unused-variable> - $<$:-Wno-unused-but-set-variable> - $<$:-Wno-unused-private-field> - $<$:-Wno-switch> - $<$:-Wno-missing-braces> - $<$:-Wno-braced-scalar-init> - $<$:-Wno-invalid-offsetof> - $<$:-Wno-self-assign-overloaded> + $<$: + -Wno-unused-private-field + -Wno-braced-scalar-init + -Wno-self-assign-overloaded + > + # GCC flags. + $<$: + -fpermissive + -Wno-sign-compare + -Wno-literal-suffix + -Wno-class-memaccess + -Wno-strict-aliasing + > PRIVATE $<$:/bigobj> # big object files ) @@ -807,7 +850,6 @@ target_link_options(Falcor target_compile_definitions(Falcor PUBLIC - GLM_FORCE_DEPTH_ZERO_TO_ONE $<$:NDEBUG> $<$:_DEBUG> # Windows. @@ -837,9 +879,16 @@ target_compile_definitions(Falcor $<$:IMGUI_API=__attribute__\(\(visibility\("default"\)\)\)> ) +if(FALCOR_USE_SYSTEM_PYTHON) + target_compile_definitions(Falcor + PRIVATE + FALCOR_PYTHON_EXECUTABLE="${Python_EXECUTABLE}" + ) +endif() + target_link_libraries(Falcor PUBLIC - fmt pybind11::embed glm Vulkan::Headers + fmt pybind11::embed Vulkan::Headers slang slang-gfx imgui imgui_addons nanovdb @@ -849,7 +898,7 @@ target_link_libraries(Falcor $<$:CUDA::cudart_static> PRIVATE git_version - FreeImage assimp ffmpeg OpenEXR OpenVDB lz4 zlib pugixml + FreeImage assimp OpenEXR OpenVDB lz4 zlib pugixml glfw mikktspace nvtt $<$:d3d12> $<$:agility-sdk> @@ -880,29 +929,38 @@ validate_headers(Falcor) # Falcor python module +# The `falcor` python package is a wrapper around the `falcor_ext` extension module. +# The reason for this is that as of Python 3.8, we need to use os.add_dll_directory() +# to add the directory containing the main falcor DLL to the DLL search path. +# This is not possible to do from within the extension module, so we need to do it +# from the `falcor` package __init__.py file (which is copied from falcor__init__.py). + pybind11_add_module(FalcorPython FalcorPython.cpp) target_link_libraries(FalcorPython PRIVATE Falcor) set_target_properties(FalcorPython PROPERTIES - OUTPUT_NAME falcor + OUTPUT_NAME falcor_ext ARCHIVE_OUTPUT_NAME FalcorPython # Visual Studio generator clashes with the main Falcor library otherwise - RUNTIME_OUTPUT_DIRECTORY ${FALCOR_OUTPUT_DIRECTORY}/python - LIBRARY_OUTPUT_DIRECTORY ${FALCOR_OUTPUT_DIRECTORY}/python + RUNTIME_OUTPUT_DIRECTORY ${FALCOR_OUTPUT_DIRECTORY}/python/falcor + LIBRARY_OUTPUT_DIRECTORY ${FALCOR_OUTPUT_DIRECTORY}/python/falcor ) +# Generate falcor package __init__.py file. +file(GENERATE OUTPUT ${FALCOR_OUTPUT_DIRECTORY}/python/falcor/__init__.py INPUT ${CMAKE_SOURCE_DIR}/build_scripts/falcor__init__.py) + # Generate python stub files. if(FALCOR_WINDOWS) add_custom_command( TARGET FalcorPython POST_BUILD - COMMAND ${FALCOR_OUTPUT_DIRECTORY}/setpath.bat & ${Python_EXECUTABLE} ${CMAKE_SOURCE_DIR}/build_scripts/generate_stubs.py ${FALCOR_OUTPUT_DIRECTORY}/python - ) + COMMAND ${FALCOR_OUTPUT_DIRECTORY}/setpath.bat & ${Python_EXECUTABLE} ${CMAKE_SOURCE_DIR}/build_scripts/generate_stubs.py ${FALCOR_OUTPUT_DIRECTORY}/python || (exit 0) + ) endif() if(FALCOR_LINUX) add_custom_command( TARGET FalcorPython POST_BUILD WORKING_DIRECTORY ${FALCOR_OUTPUT_DIRECTORY} - COMMAND ${CMAKE_SOURCE_DIR}/build_scripts/wrap_setpath.sh ${Python_EXECUTABLE} ${CMAKE_SOURCE_DIR}/build_scripts/generate_stubs.py ${FALCOR_OUTPUT_DIRECTORY}/python + COMMAND ${CMAKE_SOURCE_DIR}/build_scripts/wrap_setpath.sh ${Python_EXECUTABLE} ${CMAKE_SOURCE_DIR}/build_scripts/generate_stubs.py ${FALCOR_OUTPUT_DIRECTORY}/python || (exit 0) ) endif() diff --git a/Source/Falcor/Core/API/BlendState.cpp b/Source/Falcor/Core/API/BlendState.cpp index e85f18485..f1aa877ab 100644 --- a/Source/Falcor/Core/API/BlendState.cpp +++ b/Source/Falcor/Core/API/BlendState.cpp @@ -27,13 +27,14 @@ **************************************************************************/ #include "BlendState.h" #include "FBO.h" +#include "Core/ObjectPython.h" #include "Utils/Scripting/ScriptBindings.h" namespace Falcor { -BlendState::SharedPtr BlendState::create(const Desc& desc) +ref BlendState::create(const Desc& desc) { - return SharedPtr(new BlendState(desc)); + return ref(new BlendState(desc)); } BlendState::Desc::Desc() @@ -83,6 +84,6 @@ BlendState::Desc& BlendState::Desc::setRenderTargetWriteMask( FALCOR_SCRIPT_BINDING(BlendState) { - pybind11::class_(m, "BlendState"); + pybind11::class_>(m, "BlendState"); } } // namespace Falcor diff --git a/Source/Falcor/Core/API/BlendState.h b/Source/Falcor/Core/API/BlendState.h index 65dc01bfe..3a6051283 100644 --- a/Source/Falcor/Core/API/BlendState.h +++ b/Source/Falcor/Core/API/BlendState.h @@ -28,8 +28,8 @@ #pragma once #include "Handles.h" #include "Core/Macros.h" +#include "Core/Object.h" #include "Utils/Math/Vector.h" -#include #include namespace Falcor @@ -37,11 +37,9 @@ namespace Falcor /** * Blend state */ -class FALCOR_API BlendState +class FALCOR_API BlendState : public Object { public: - using SharedPtr = std::shared_ptr; - /** * Defines how to combine the blend inputs */ @@ -185,7 +183,7 @@ class FALCOR_API BlendState * @param[in] Desc Blend state descriptor. * @return A new object, or throws an exception if creation failed. */ - static BlendState::SharedPtr create(const Desc& desc); + static ref create(const Desc& desc); /** * Get the constant blend factor color diff --git a/Source/Falcor/Core/API/BlitContext.cpp b/Source/Falcor/Core/API/BlitContext.cpp index f1ca56eb0..1dc062463 100644 --- a/Source/Falcor/Core/API/BlitContext.cpp +++ b/Source/Falcor/Core/API/BlitContext.cpp @@ -29,7 +29,7 @@ #include "Core/Assert.h" #include "Core/API/Device.h" #include "Core/Program/Program.h" -#include "RenderGraph/BasePasses/FullScreenPass.h" +#include "Core/Pass/FullScreenPass.h" namespace Falcor { @@ -46,8 +46,10 @@ BlitContext::BlitContext(Device* pDevice) }; Program::Desc d; d.addShaderLibrary("Core/API/BlitReduction.3d.slang").vsEntry("vsMain").psEntry("psMain"); - pPass = FullScreenPass::create(pDevice->shared_from_this(), d, defines); - pFbo = Fbo::create(pDevice); + pPass = FullScreenPass::create(ref(pDevice), d, defines); + pPass->breakStrongReferenceToDevice(); + pFbo = Fbo::create(ref(pDevice)); + pFbo->breakStrongReferenceToDevice(); FALCOR_ASSERT(pPass && pFbo); pBlitParamsBuffer = pPass->getVars()->getParameterBlock("BlitParamsCB"); @@ -60,21 +62,27 @@ BlitContext::BlitContext(Device* pDevice) desc.setAddressingMode(Sampler::AddressMode::Clamp, Sampler::AddressMode::Clamp, Sampler::AddressMode::Clamp); desc.setReductionMode(Sampler::ReductionMode::Standard); desc.setFilterMode(Sampler::Filter::Linear, Sampler::Filter::Linear, Sampler::Filter::Point); - pLinearSampler = Sampler::create(pDevice, desc); + pLinearSampler = Sampler::create(ref(pDevice), desc); + pLinearSampler->breakStrongReferenceToDevice(); desc.setFilterMode(Sampler::Filter::Point, Sampler::Filter::Point, Sampler::Filter::Point); - pPointSampler = Sampler::create(pDevice, desc); + pPointSampler = Sampler::create(ref(pDevice), desc); + pPointSampler->breakStrongReferenceToDevice(); // Min reductions. desc.setReductionMode(Sampler::ReductionMode::Min); desc.setFilterMode(Sampler::Filter::Linear, Sampler::Filter::Linear, Sampler::Filter::Point); - pLinearMinSampler = Sampler::create(pDevice, desc); + pLinearMinSampler = Sampler::create(ref(pDevice), desc); + pLinearMinSampler->breakStrongReferenceToDevice(); desc.setFilterMode(Sampler::Filter::Point, Sampler::Filter::Point, Sampler::Filter::Point); - pPointMinSampler = Sampler::create(pDevice, desc); + pPointMinSampler = Sampler::create(ref(pDevice), desc); + pPointMinSampler->breakStrongReferenceToDevice(); // Max reductions. desc.setReductionMode(Sampler::ReductionMode::Max); desc.setFilterMode(Sampler::Filter::Linear, Sampler::Filter::Linear, Sampler::Filter::Point); - pLinearMaxSampler = Sampler::create(pDevice, desc); + pLinearMaxSampler = Sampler::create(ref(pDevice), desc); + pLinearMaxSampler->breakStrongReferenceToDevice(); desc.setFilterMode(Sampler::Filter::Point, Sampler::Filter::Point, Sampler::Filter::Point); - pPointMaxSampler = Sampler::create(pDevice, desc); + pPointMaxSampler = Sampler::create(ref(pDevice), desc); + pPointMaxSampler->breakStrongReferenceToDevice(); const auto& pDefaultBlockReflection = pPass->getProgram()->getReflector()->getDefaultParameterBlock(); texBindLoc = pDefaultBlockReflection->getResourceBinding("gTex"); diff --git a/Source/Falcor/Core/API/BlitContext.h b/Source/Falcor/Core/API/BlitContext.h index 527184617..e64c67225 100644 --- a/Source/Falcor/Core/API/BlitContext.h +++ b/Source/Falcor/Core/API/BlitContext.h @@ -39,17 +39,17 @@ class FullScreenPass; struct BlitContext { - std::shared_ptr pPass; - Fbo::SharedPtr pFbo; + ref pPass; + ref pFbo; - Sampler::SharedPtr pLinearSampler; - Sampler::SharedPtr pPointSampler; - Sampler::SharedPtr pLinearMinSampler; - Sampler::SharedPtr pPointMinSampler; - Sampler::SharedPtr pLinearMaxSampler; - Sampler::SharedPtr pPointMaxSampler; + ref pLinearSampler; + ref pPointSampler; + ref pLinearMinSampler; + ref pPointMinSampler; + ref pLinearMaxSampler; + ref pPointMaxSampler; - ParameterBlock::SharedPtr pBlitParamsBuffer; + ref pBlitParamsBuffer; float2 prevSrcRectOffset = float2(0, 0); float2 prevSrcReftScale = float2(0, 0); diff --git a/Source/Falcor/Core/API/Buffer.cpp b/Source/Falcor/Core/API/Buffer.cpp index 53fbbfe05..cf33e7ffe 100644 --- a/Source/Falcor/Core/API/Buffer.cpp +++ b/Source/Falcor/Core/API/Buffer.cpp @@ -31,6 +31,7 @@ #include "NativeHandleTraits.h" #include "Core/Assert.h" #include "Core/Errors.h" +#include "Core/ObjectPython.h" #include "Core/Program/Program.h" #include "Core/Program/ShaderVar.h" #include "Utils/Logger.h" @@ -44,8 +45,8 @@ namespace Falcor // TODO: Replace with include? void getGFXResourceState(Resource::BindFlags flags, gfx::ResourceState& defaultState, gfx::ResourceStateSet& allowedStates); -static Buffer::SharedPtr createStructuredFromType( - Device* pDevice, +static ref createStructuredFromType( + ref pDevice, const ReflectionType* pType, const std::string& varName, uint32_t elementCount, @@ -96,7 +97,7 @@ static void prepareGFXBufferDesc( // TODO: This is also used in Device Slang::ComPtr createBuffer( - Device* pDevice, + ref pDevice, Buffer::State initState, size_t size, Buffer::BindFlags bindFlags, @@ -128,8 +129,8 @@ static size_t getBufferDataAlignment(const Buffer* pBuffer) return GFX_TEXTURE_DATA_PLACEMENT_ALIGNMENT; } -Buffer::Buffer(std::shared_ptr pDevice, size_t size, BindFlags bindFlags, CpuAccess cpuAccess, const void* pInitData) - : Resource(std::move(pDevice), Type::Buffer, bindFlags, size), mCpuAccess(cpuAccess) +Buffer::Buffer(ref pDevice, size_t size, BindFlags bindFlags, CpuAccess cpuAccess, const void* pInitData) + : Resource(pDevice, Type::Buffer, bindFlags, size), mCpuAccess(cpuAccess) { checkArgument(size > 0, "Can't create GPU buffer of size zero"); @@ -163,14 +164,14 @@ Buffer::Buffer(std::shared_ptr pDevice, size_t size, BindFlags bindFlags else if (mCpuAccess == CpuAccess::Read && mBindFlags == BindFlags::None) { mState.global = Resource::State::CopyDest; - mGfxBufferResource = createBuffer(mpDevice.get(), mState.global, mSize, mBindFlags, mCpuAccess); + mGfxBufferResource = createBuffer(mpDevice, mState.global, mSize, mBindFlags, mCpuAccess); } else { mState.global = Resource::State::Common; if (is_set(mBindFlags, BindFlags::AccelerationStructure)) mState.global = Resource::State::AccelerationStructure; - mGfxBufferResource = createBuffer(mpDevice.get(), mState.global, mSize, mBindFlags, mCpuAccess); + mGfxBufferResource = createBuffer(mpDevice, mState.global, mSize, mBindFlags, mCpuAccess); } if (pInitData) @@ -178,13 +179,13 @@ Buffer::Buffer(std::shared_ptr pDevice, size_t size, BindFlags bindFlags mElementCount = uint32_t(size); } -Buffer::SharedPtr Buffer::create(Device* pDevice, size_t size, BindFlags bindFlags, CpuAccess cpuAccess, const void* pInitData) +ref Buffer::create(ref pDevice, size_t size, BindFlags bindFlags, CpuAccess cpuAccess, const void* pInitData) { - return SharedPtr(new Buffer(pDevice->shared_from_this(), size, bindFlags, cpuAccess, pInitData)); + return ref(new Buffer(pDevice, size, bindFlags, cpuAccess, pInitData)); } -Buffer::SharedPtr Buffer::createTyped( - Device* pDevice, +ref Buffer::createTyped( + ref pDevice, ResourceFormat format, uint32_t elementCount, BindFlags bindFlags, @@ -193,7 +194,7 @@ Buffer::SharedPtr Buffer::createTyped( ) { size_t size = (size_t)elementCount * getFormatBytesPerBlock(format); - SharedPtr pBuffer = create(pDevice, size, bindFlags, cpuAccess, pInitData); + ref pBuffer = create(pDevice, size, bindFlags, cpuAccess, pInitData); FALCOR_ASSERT(pBuffer); pBuffer->mFormat = format; @@ -201,8 +202,8 @@ Buffer::SharedPtr Buffer::createTyped( return pBuffer; } -Buffer::SharedPtr Buffer::createStructured( - Device* pDevice, +ref Buffer::createStructured( + ref pDevice, uint32_t structSize, uint32_t elementCount, ResourceBindFlags bindFlags, @@ -212,7 +213,7 @@ Buffer::SharedPtr Buffer::createStructured( ) { size_t size = (size_t)structSize * elementCount; - Buffer::SharedPtr pBuffer = create(pDevice, size, bindFlags, cpuAccess, pInitData); + ref pBuffer = create(pDevice, size, bindFlags, cpuAccess, pInitData); FALCOR_ASSERT(pBuffer); pBuffer->mElementCount = elementCount; @@ -226,8 +227,8 @@ Buffer::SharedPtr Buffer::createStructured( return pBuffer; } -Buffer::SharedPtr Buffer::createStructured( - Device* pDevice, +ref Buffer::createStructured( + ref pDevice, const ShaderVar& shaderVar, uint32_t elementCount, ResourceBindFlags bindFlags, @@ -241,8 +242,8 @@ Buffer::SharedPtr Buffer::createStructured( ); } -Buffer::SharedPtr Buffer::createStructured( - Device* pDevice, +ref Buffer::createStructured( + ref pDevice, const Program* pProgram, const std::string& name, uint32_t elementCount, @@ -261,9 +262,9 @@ Buffer::SharedPtr Buffer::createStructured( return createStructuredFromType(pDevice, pVar->getType().get(), name, elementCount, bindFlags, cpuAccess, pInitData, createCounter); } -Buffer::SharedPtr Buffer::aliasResource( - Device* pDevice, - Buffer::SharedPtr pBaseResource, +ref Buffer::aliasResource( + ref pDevice, + ref pBaseResource, GpuAddress offset, size_t size, Resource::BindFlags bindFlags @@ -286,15 +287,15 @@ Buffer::SharedPtr Buffer::aliasResource( ); } - SharedPtr pBuffer = create(pDevice, size, bindFlags, CpuAccess::None); + ref pBuffer = create(pDevice, size, bindFlags, CpuAccess::None); pBuffer->mpAliasedResource = pBaseResource; pBuffer->mGfxBufferResource = pBaseResource->mGfxBufferResource; pBuffer->mGpuVaOffset = offset; return pBuffer; } -Buffer::SharedPtr Buffer::createFromResource( - Device* pDevice, +ref Buffer::createFromResource( + ref pDevice, gfx::IBufferResource* pResource, size_t size, Resource::BindFlags bindFlags, @@ -302,13 +303,13 @@ Buffer::SharedPtr Buffer::createFromResource( ) { FALCOR_ASSERT(pResource); - Buffer::SharedPtr pBuffer = create(pDevice, size, bindFlags, cpuAccess); + ref pBuffer = create(pDevice, size, bindFlags, cpuAccess); pBuffer->mGfxBufferResource = pResource; return pBuffer; } -Buffer::SharedPtr Buffer::createFromNativeHandle( - Device* pDevice, +ref Buffer::createFromNativeHandle( + ref pDevice, NativeHandle handle, size_t size, Resource::BindFlags bindFlags, @@ -365,58 +366,32 @@ gfx::IResource* Buffer::getGfxResource() const return mGfxBufferResource; } -template -using CreateFuncType = std::function; - -template -typename ViewClass::SharedPtr findViewCommon( - Buffer* pBuffer, - uint32_t firstElement, - uint32_t elementCount, - ViewMapType& viewMap, - CreateFuncType createFunc -) +ref Buffer::getSRV(uint32_t firstElement, uint32_t elementCount) { ResourceViewInfo view = ResourceViewInfo(firstElement, elementCount); - if (viewMap.find(view) == viewMap.end()) - { - viewMap[view] = createFunc(pBuffer, firstElement, elementCount); - } + if (mSrvs.find(view) == mSrvs.end()) + mSrvs[view] = ShaderResourceView::create(getDevice().get(), this, firstElement, elementCount); - return viewMap[view]; + return mSrvs[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->getDevice().get(), std::static_pointer_cast(pBuffer->shared_from_this()), firstElement, elementCount - ); - }; - - return findViewCommon(this, firstElement, elementCount, mSrvs, createFunc); -} - -ShaderResourceView::SharedPtr Buffer::getSRV() +ref Buffer::getSRV() { return getSRV(0); } -UnorderedAccessView::SharedPtr Buffer::getUAV(uint32_t firstElement, uint32_t elementCount) +ref Buffer::getUAV(uint32_t firstElement, uint32_t elementCount) { - auto createFunc = [](Buffer* pBuffer, uint32_t firstElement, uint32_t elementCount) - { - return UnorderedAccessView::create( - pBuffer->getDevice().get(), std::static_pointer_cast(pBuffer->shared_from_this()), firstElement, elementCount - ); - }; + ResourceViewInfo view = ResourceViewInfo(firstElement, elementCount); + + if (mUavs.find(view) == mUavs.end()) + mUavs[view] = UnorderedAccessView::create(getDevice().get(), this, firstElement, elementCount); - return findViewCommon(this, firstElement, elementCount, mUavs, createFunc); + return mUavs[view]; } -UnorderedAccessView::SharedPtr Buffer::getUAV() +ref Buffer::getUAV() { return getUAV(0); } @@ -492,7 +467,7 @@ void* Buffer::map(MapType type) ); if (mpStagingResource == nullptr) { - mpStagingResource = Buffer::create(mpDevice.get(), mSize, Buffer::BindFlags::None, Buffer::CpuAccess::Read, nullptr); + mpStagingResource = Buffer::create(mpDevice, mSize, Buffer::BindFlags::None, Buffer::CpuAccess::Read, nullptr); } // Copy the buffer and flush the pipeline @@ -552,6 +527,13 @@ uint64_t Buffer::getGpuAddress() const FALCOR_SCRIPT_BINDING(Buffer) { - pybind11::class_(m, "Buffer"); + FALCOR_SCRIPT_BINDING_DEPENDENCY(Resource) + + pybind11::class_> buffer(m, "Buffer"); + + pybind11::enum_ cpuAccess(buffer, "CpuAccess"); + cpuAccess.value("None_", Buffer::CpuAccess::None); + cpuAccess.value("Read", Buffer::CpuAccess::Read); + cpuAccess.value("Write", Buffer::CpuAccess::Write); } } // namespace Falcor diff --git a/Source/Falcor/Core/API/Buffer.h b/Source/Falcor/Core/API/Buffer.h index ba6dee3f1..4a5fe09b6 100644 --- a/Source/Falcor/Core/API/Buffer.h +++ b/Source/Falcor/Core/API/Buffer.h @@ -30,13 +30,85 @@ #include "Core/Macros.h" #include "Resource.h" #include "ResourceViews.h" -#include namespace Falcor { class Program; struct ShaderVar; +namespace detail +{ +/** + * Helper for converting host type to resource format for typed buffers. + * See list of supported formats for typed UAV loads: + * https://docs.microsoft.com/en-us/windows/win32/direct3d12/typed-unordered-access-view-loads + */ +template +struct FormatForElementType +{}; + +#define CASE(TYPE, FORMAT) \ + template<> \ + struct FormatForElementType \ + { \ + static constexpr ResourceFormat kFormat = FORMAT; \ + } + +// Guaranteed supported formats on D3D12. +CASE(float, ResourceFormat::R32Float); +CASE(uint32_t, ResourceFormat::R32Uint); +CASE(int32_t, ResourceFormat::R32Int); + +// Optionally supported formats as a set on D3D12. If one is supported all are supported. +CASE(float4, ResourceFormat::RGBA32Float); +CASE(uint4, ResourceFormat::RGBA32Uint); +CASE(int4, ResourceFormat::RGBA32Int); +// R16G16B16A16_FLOAT +// R16G16B16A16_UINT +// R16G16B16A16_SINT +// R8G8B8A8_UNORM +// R8G8B8A8_UINT +// R8G8B8A8_SINT +// R16_FLOAT +CASE(uint16_t, ResourceFormat::R16Uint); +CASE(int16_t, ResourceFormat::R16Int); +// R8_UNORM +CASE(uint8_t, ResourceFormat::R8Uint); +CASE(int8_t, ResourceFormat::R8Int); + +// Optionally and individually supported formats on D3D12. Query for support individually. +// R16G16B16A16_UNORM +// R16G16B16A16_SNORM +CASE(float2, ResourceFormat::RG32Float); +CASE(uint2, ResourceFormat::RG32Uint); +CASE(int2, ResourceFormat::RG32Int); +// R10G10B10A2_UNORM +// R10G10B10A2_UINT +// R11G11B10_FLOAT +// R8G8B8A8_SNORM +// R16G16_FLOAT +// R16G16_UNORM +// R16G16_UINT +// R16G16_SNORM +// R16G16_SINT +// R8G8_UNORM +// R8G8_UINT +// R8G8_SNORM +// 8G8_SINT +// R16_UNORM +// R16_SNORM +// R8_SNORM +// A8_UNORM +// B5G6R5_UNORM +// B5G5R5A1_UNORM +// B4G4R4A4_UNORM + +// Additional formats that may be supported on some hardware. +CASE(float3, ResourceFormat::RGB32Float); + +#undef CASE +} // namespace detail + /** * Low-level buffer object * This class abstracts the API's buffer creation and management @@ -44,9 +116,6 @@ struct ShaderVar; class FALCOR_API Buffer : public Resource { public: - using SharedPtr = std::shared_ptr; - using WeakPtr = std::weak_ptr; - /** * Buffer access flags. * These flags are hints the driver how the buffer will be used. @@ -76,8 +145,8 @@ class FALCOR_API Buffer : public Resource * @param[in] pInitData Optional parameter. Initial buffer data. Pointed buffer size should be at least 'size' bytes. * @return A pointer to a new buffer object, or throws an exception if creation failed. */ - static SharedPtr create( - Device* pDevice, + static ref create( + ref pDevice, size_t size, Resource::BindFlags bindFlags = Resource::BindFlags::ShaderResource | Resource::BindFlags::UnorderedAccess, CpuAccess cpuAccess = Buffer::CpuAccess::None, @@ -93,8 +162,8 @@ class FALCOR_API Buffer : public Resource * @param[in] pInitData Optional parameter. Initial buffer data. Pointed buffer should hold at least 'elementCount' elements. * @return A pointer to a new buffer object, or throws an exception if creation failed. */ - static SharedPtr createTyped( - Device* pDevice, + static ref createTyped( + ref pDevice, ResourceFormat format, uint32_t elementCount, Resource::BindFlags bindFlags = Resource::BindFlags::ShaderResource | Resource::BindFlags::UnorderedAccess, @@ -111,15 +180,15 @@ class FALCOR_API Buffer : public Resource * @return A pointer to a new buffer object, or throws an exception if creation failed. */ template - static SharedPtr createTyped( - Device* pDevice, + static ref createTyped( + ref pDevice, uint32_t elementCount, Resource::BindFlags bindFlags = Resource::BindFlags::ShaderResource | Resource::BindFlags::UnorderedAccess, CpuAccess cpuAccess = Buffer::CpuAccess::None, const T* pInitData = nullptr ) { - return createTyped(pDevice, FormatForElementType::kFormat, elementCount, bindFlags, cpuAccess, pInitData); + return createTyped(pDevice, detail::FormatForElementType::kFormat, elementCount, bindFlags, cpuAccess, pInitData); } /** @@ -132,8 +201,8 @@ class FALCOR_API Buffer : public Resource * @param[in] createCounter True if the associated UAV counter should be created. * @return A pointer to a new buffer object, or throws an exception if creation failed. */ - static SharedPtr createStructured( - Device* pDevice, + static ref createStructured( + ref pDevice, uint32_t structSize, uint32_t elementCount, ResourceBindFlags bindFlags = Resource::BindFlags::ShaderResource | Resource::BindFlags::UnorderedAccess, @@ -153,8 +222,8 @@ class FALCOR_API Buffer : public Resource * @param[in] createCounter True if the associated UAV counter should be created. * @return A pointer to a new buffer object, or throws an exception if creation failed. */ - static SharedPtr createStructured( - Device* pDevice, + static ref createStructured( + ref pDevice, const Program* pProgram, const std::string& name, uint32_t elementCount, @@ -174,8 +243,8 @@ class FALCOR_API Buffer : public Resource * @param[in] createCounter True if the associated UAV counter should be created. * @return A pointer to a new buffer object, or throws an exception if creation failed. */ - static SharedPtr createStructured( - Device* pDevice, + static ref createStructured( + ref pDevice, const ShaderVar& shaderVar, uint32_t elementCount, ResourceBindFlags bindFlags = Resource::BindFlags::ShaderResource | Resource::BindFlags::UnorderedAccess, @@ -184,9 +253,9 @@ class FALCOR_API Buffer : public Resource bool createCounter = true ); - static SharedPtr aliasResource( - Device* pDevice, - Buffer::SharedPtr pBaseResource, + static ref aliasResource( + ref pDevice, + ref pBaseResource, GpuAddress offset, size_t size, Resource::BindFlags bindFlags @@ -201,8 +270,8 @@ class FALCOR_API Buffer : public Resource * allocated on. * @return A pointer to a new buffer object, or throws an exception if creation failed. */ - static SharedPtr createFromResource( - Device* pDevice, + static ref createFromResource( + ref pDevice, gfx::IBufferResource* pResource, size_t size, Resource::BindFlags bindFlags, @@ -218,8 +287,8 @@ class FALCOR_API Buffer : public Resource * allocated on. * @return A pointer to a new buffer object, or throws an exception if creation failed. */ - static SharedPtr createFromNativeHandle( - Device* pDevice, + static ref createFromNativeHandle( + ref pDevice, NativeHandle handle, size_t size, Resource::BindFlags bindFlags, @@ -235,24 +304,24 @@ class FALCOR_API Buffer : public Resource * @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, uint32_t elementCount = kMaxPossible); + ref getSRV(uint32_t firstElement, 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, uint32_t elementCount = kMaxPossible); + ref getUAV(uint32_t firstElement, uint32_t elementCount = kMaxPossible); /** * Get a shader-resource view for the entire resource */ - virtual ShaderResourceView::SharedPtr getSRV() override; + virtual ref getSRV() override; /** * Get an unordered access view for the entire resource */ - virtual UnorderedAccessView::SharedPtr getUAV() override; + virtual ref getUAV() override; /** * Get the size of each element in this buffer. @@ -306,7 +375,7 @@ class FALCOR_API Buffer : public Resource /** * Get the UAV counter buffer */ - const Buffer::SharedPtr& getUAVCounter() const { return mpUAVCounter; } + const ref& getUAVCounter() const { return mpUAVCounter; } /** * Map the buffer. @@ -345,97 +414,24 @@ class FALCOR_API Buffer : public Resource bool isStructured() const { return mStructSize != 0; } template - void setElement(uint32_t index, T const& value) + void setElement(uint32_t index, const T& value) { setBlob(&value, sizeof(T) * index, sizeof(T)); } protected: - Buffer(std::shared_ptr pDevice, size_t size, BindFlags bindFlags, CpuAccess cpuAccess, const void* pInitData); + Buffer(ref pDevice, size_t size, BindFlags bindFlags, CpuAccess cpuAccess, const void* pInitData); Slang::ComPtr mGfxBufferResource; CpuAccess mCpuAccess; GpuMemoryHeap::Allocation mDynamicData; - Buffer::SharedPtr mpStagingResource; // For buffers that have both CPU read flag and can be used by the GPU - Resource::SharedPtr mpAliasedResource; + ref mpStagingResource; // For buffers that have both CPU read flag and can be used by the GPU + ref mpAliasedResource; uint32_t mElementCount = 0; ResourceFormat mFormat = ResourceFormat::Unknown; uint32_t mStructSize = 0; - Buffer::SharedPtr mpUAVCounter; // For structured-buffers - - mutable void* mCUDAExternalMemory = nullptr; - mutable void* mCUDADeviceAddress = nullptr; - - /** - * Helper for converting host type to resource format for typed buffers. - * See list of supported formats for typed UAV loads: - * https://docs.microsoft.com/en-us/windows/win32/direct3d12/typed-unordered-access-view-loads - */ - template - struct FormatForElementType - {}; - -#define CASE(TYPE, FORMAT) \ - template<> \ - struct FormatForElementType \ - { \ - static const ResourceFormat kFormat = FORMAT; \ - } - - // Guaranteed supported formats on D3D12. - CASE(float, ResourceFormat::R32Float); - CASE(uint32_t, ResourceFormat::R32Uint); - CASE(int32_t, ResourceFormat::R32Int); - - // Optionally supported formats as a set on D3D12. If one is supported all are supported. - CASE(float4, ResourceFormat::RGBA32Float); - CASE(uint4, ResourceFormat::RGBA32Uint); - CASE(int4, ResourceFormat::RGBA32Int); - // R16G16B16A16_FLOAT - // R16G16B16A16_UINT - // R16G16B16A16_SINT - // R8G8B8A8_UNORM - // R8G8B8A8_UINT - // R8G8B8A8_SINT - // R16_FLOAT - CASE(uint16_t, ResourceFormat::R16Uint); - CASE(int16_t, ResourceFormat::R16Int); - // R8_UNORM - CASE(uint8_t, ResourceFormat::R8Uint); - CASE(int8_t, ResourceFormat::R8Int); - - // Optionally and individually supported formats on D3D12. Query for support individually. - // R16G16B16A16_UNORM - // R16G16B16A16_SNORM - CASE(float2, ResourceFormat::RG32Float); - CASE(uint2, ResourceFormat::RG32Uint); - CASE(int2, ResourceFormat::RG32Int); - // R10G10B10A2_UNORM - // R10G10B10A2_UINT - // R11G11B10_FLOAT - // R8G8B8A8_SNORM - // R16G16_FLOAT - // R16G16_UNORM - // R16G16_UINT - // R16G16_SNORM - // R16G16_SINT - // R8G8_UNORM - // R8G8_UINT - // R8G8_SNORM - // 8G8_SINT - // R16_UNORM - // R16_SNORM - // R8_SNORM - // A8_UNORM - // B5G6R5_UNORM - // B5G5R5A1_UNORM - // B4G4R4A4_UNORM - - // Additional formats that may be supported on some hardware. - CASE(float3, ResourceFormat::RGB32Float); - -#undef CASE + ref mpUAVCounter; // For structured-buffers }; inline std::string to_string(Buffer::CpuAccess c) diff --git a/Source/Falcor/Core/API/ComputeContext.cpp b/Source/Falcor/Core/API/ComputeContext.cpp index bd2247247..0b6e6b0c3 100644 --- a/Source/Falcor/Core/API/ComputeContext.cpp +++ b/Source/Falcor/Core/API/ComputeContext.cpp @@ -62,7 +62,7 @@ void ComputeContext::dispatchIndirect(ComputeState* pState, ComputeVars* pVars, void ComputeContext::clearUAV(const UnorderedAccessView* pUav, const float4& value) { - resourceBarrier(pUav->getResource().get(), Resource::State::UnorderedAccess); + resourceBarrier(pUav->getResource(), Resource::State::UnorderedAccess); auto resourceEncoder = mpLowLevelData->getResourceCommandEncoder(); gfx::ClearValue clearValue = {}; @@ -73,7 +73,7 @@ void ComputeContext::clearUAV(const UnorderedAccessView* pUav, const float4& val void ComputeContext::clearUAV(const UnorderedAccessView* pUav, const uint4& value) { - resourceBarrier(pUav->getResource().get(), Resource::State::UnorderedAccess); + resourceBarrier(pUav->getResource(), Resource::State::UnorderedAccess); auto resourceEncoder = mpLowLevelData->getResourceCommandEncoder(); gfx::ClearValue clearValue = {}; @@ -82,7 +82,7 @@ void ComputeContext::clearUAV(const UnorderedAccessView* pUav, const uint4& valu mCommandsPending = true; } -void ComputeContext::clearUAVCounter(const Buffer::SharedPtr& pBuffer, uint32_t value) +void ComputeContext::clearUAVCounter(const ref& pBuffer, uint32_t value) { if (pBuffer->getUAVCounter()) { diff --git a/Source/Falcor/Core/API/ComputeContext.h b/Source/Falcor/Core/API/ComputeContext.h index ef0abcd90..6f3f5978f 100644 --- a/Source/Falcor/Core/API/ComputeContext.h +++ b/Source/Falcor/Core/API/ComputeContext.h @@ -32,7 +32,6 @@ #include "LowLevelContextData.h" #include "Core/Macros.h" #include "Utils/Math/Vector.h" -#include namespace Falcor { @@ -83,7 +82,7 @@ class FALCOR_API ComputeContext : public CopyContext * @param[in] pBuffer Structured Buffer containing UAV counter * @param[in] value Value to clear counter to */ - void clearUAVCounter(const Buffer::SharedPtr& pBuffer, uint32_t value); + void clearUAVCounter(const ref& pBuffer, uint32_t value); /** * Submit the command list diff --git a/Source/Falcor/Core/API/ComputeStateObject.cpp b/Source/Falcor/Core/API/ComputeStateObject.cpp index 9ba8197ed..e3ac8e13a 100644 --- a/Source/Falcor/Core/API/ComputeStateObject.cpp +++ b/Source/Falcor/Core/API/ComputeStateObject.cpp @@ -48,7 +48,7 @@ ComputeStateObject::~ComputeStateObject() mpDevice->releaseResource(mGfxPipelineState); } -ComputeStateObject::ComputeStateObject(std::shared_ptr pDevice, const Desc& desc) : mpDevice(std::move(pDevice)), mDesc(desc) +ComputeStateObject::ComputeStateObject(ref pDevice, const Desc& desc) : mpDevice(pDevice), mDesc(desc) { gfx::ComputePipelineStateDesc computePipelineDesc = {}; computePipelineDesc.program = mDesc.mpProgram->getGfxProgram(); @@ -64,9 +64,9 @@ ComputeStateObject::ComputeStateObject(std::shared_ptr pDevice, const De FALCOR_GFX_CALL(mpDevice->getGfxDevice()->createComputePipelineState(computePipelineDesc, mGfxPipelineState.writeRef())); } -ComputeStateObject::SharedPtr ComputeStateObject::create(Device* pDevice, const Desc& desc) +ref ComputeStateObject::create(ref pDevice, const Desc& desc) { - return SharedPtr(new ComputeStateObject(pDevice->shared_from_this(), desc)); + return ref(new ComputeStateObject(pDevice, desc)); } NativeHandle ComputeStateObject::getNativeHandle() const diff --git a/Source/Falcor/Core/API/ComputeStateObject.h b/Source/Falcor/Core/API/ComputeStateObject.h index 499c23270..8e127c20a 100644 --- a/Source/Falcor/Core/API/ComputeStateObject.h +++ b/Source/Falcor/Core/API/ComputeStateObject.h @@ -26,10 +26,12 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **************************************************************************/ #pragma once +#include "fwd.h" #include "Device.h" #include "Handles.h" #include "NativeHandle.h" #include "Core/Macros.h" +#include "Core/Object.h" #include "Core/Program/ProgramVersion.h" namespace Falcor @@ -38,15 +40,13 @@ namespace Falcor class D3D12RootSignature; #endif -class FALCOR_API ComputeStateObject +class FALCOR_API ComputeStateObject : public Object { public: - using SharedPtr = std::shared_ptr; - class FALCOR_API Desc { public: - Desc& setProgramKernels(const ProgramKernels::SharedConstPtr& pProgram) + Desc& setProgramKernels(const ref& pProgram) { mpProgram = pProgram; return *this; @@ -58,21 +58,20 @@ class FALCOR_API ComputeStateObject * This function is supported on D3D12 only. * @param[in] pRootSignature An overriding D3D12RootSignature object to use in the compute state. */ - Desc& setD3D12RootSignatureOverride(const std::shared_ptr& pRootSignature) + Desc& setD3D12RootSignatureOverride(const ref& pRootSignature) { mpD3D12RootSignatureOverride = pRootSignature; return *this; } #endif - const ProgramKernels::SharedConstPtr getProgramKernels() const { return mpProgram; } - ProgramVersion::SharedConstPtr getProgramVersion() const { return mpProgram->getProgramVersion(); } + ref getProgramKernels() const { return mpProgram; } bool operator==(const Desc& other) const; private: friend class ComputeStateObject; - ProgramKernels::SharedConstPtr mpProgram; + ref mpProgram; #if FALCOR_HAS_D3D12 - std::shared_ptr mpD3D12RootSignatureOverride; + ref mpD3D12RootSignatureOverride; #endif }; @@ -83,7 +82,7 @@ class FALCOR_API ComputeStateObject * @param[in] desc State object description. * @return New object, or throws an exception if creation failed. */ - static SharedPtr create(Device* pDevice, const Desc& desc); + static ref create(ref pDevice, const Desc& desc); gfx::IPipelineState* getGfxPipelineState() const { return mGfxPipelineState; } @@ -97,9 +96,9 @@ class FALCOR_API ComputeStateObject const Desc& getDesc() const { return mDesc; } private: - ComputeStateObject(std::shared_ptr pDevice, const Desc& desc); + ComputeStateObject(ref pDevice, const Desc& desc); - std::shared_ptr mpDevice; + ref mpDevice; Desc mDesc; Slang::ComPtr mGfxPipelineState; }; diff --git a/Source/Falcor/Core/API/CopyContext.cpp b/Source/Falcor/Core/API/CopyContext.cpp index b646d169d..b61ab5799 100644 --- a/Source/Falcor/Core/API/CopyContext.cpp +++ b/Source/Falcor/Core/API/CopyContext.cpp @@ -51,9 +51,9 @@ CopyContext::CopyContext(Device* pDevice, gfx::ICommandQueue* pQueue) : mpDevice CopyContext::~CopyContext() = default; -Device* CopyContext::getDevice() const +ref CopyContext::getDevice() const { - return mpDevice; + return ref(mpDevice); } Profiler* CopyContext::getProfiler() const @@ -212,7 +212,7 @@ void CopyContext::unbindCustomGPUDescriptorPool() #if FALCOR_HAS_D3D12 mpDevice->requireD3D12(); - ComPtr d3d12CommandBuffer; + Slang::ComPtr d3d12CommandBuffer; mpLowLevelData->getGfxCommandBuffer()->queryInterface( SlangUUID SLANG_UUID_ICommandBufferD3D12, reinterpret_cast(d3d12CommandBuffer.writeRef()) ); @@ -231,7 +231,7 @@ void CopyContext::updateTextureSubresources( { resourceBarrier(pTexture, Resource::State::CopyDest); - bool copyRegion = (offset != uint3(0)) || (size != uint3(-1)); + bool copyRegion = any(offset != uint3(0)) || any(size != uint3(-1)); FALCOR_ASSERT(subresourceCount == 1 || (copyRegion == false)); uint8_t* dataPtr = (uint8_t*)pData; auto resourceEncoder = getLowLevelData()->getResourceCommandEncoder(); @@ -306,7 +306,8 @@ CopyContext::ReadTextureTask::SharedPtr CopyContext::ReadTextureTask::create( pCtx->setPendingCommands(true); // Create a fence and signal - pThis->mpFence = GpuFence::create(pCtx->mpDevice); + pThis->mpFence = GpuFence::create(pCtx->getDevice()); + pThis->mpFence->breakStrongReferenceToDevice(); pCtx->flush(false); pThis->mpFence->gpuSignal(pCtx->getLowLevelData()->getCommandQueue()); pThis->mRowCount = (uint32_t)rowCount; @@ -516,7 +517,7 @@ void CopyContext::copySubresourceRegion( gfx::ITextureResource::Extents copySize = {(int)size.x, (int)size.y, (int)size.z}; - if (size.x == glm::uint(-1)) + if (size.x == uint(-1)) { copySize.width = pSrc->getWidth(srcSubresource.mipLevel) - srcOffset.x; copySize.height = pSrc->getHeight(srcSubresource.mipLevel) - srcOffset.y; diff --git a/Source/Falcor/Core/API/CopyContext.h b/Source/Falcor/Core/API/CopyContext.h index dccc7aefd..fad4640ef 100644 --- a/Source/Falcor/Core/API/CopyContext.h +++ b/Source/Falcor/Core/API/CopyContext.h @@ -53,8 +53,8 @@ class FALCOR_API CopyContext private: ReadTextureTask() = default; - GpuFence::SharedPtr mpFence; - Buffer::SharedPtr mpBuffer; + ref mpFence; + ref mpBuffer; CopyContext* mpContext; uint32_t mRowCount; uint32_t mRowSize; @@ -71,7 +71,7 @@ class FALCOR_API CopyContext CopyContext(Device* pDevice, gfx::ICommandQueue* pQueue); virtual ~CopyContext(); - Device* getDevice() const; + ref getDevice() const; Profiler* getProfiler() const; diff --git a/Source/Falcor/Core/API/DepthStencilState.cpp b/Source/Falcor/Core/API/DepthStencilState.cpp index 56c16ef48..05dc35681 100644 --- a/Source/Falcor/Core/API/DepthStencilState.cpp +++ b/Source/Falcor/Core/API/DepthStencilState.cpp @@ -27,13 +27,14 @@ **************************************************************************/ #include "DepthStencilState.h" #include "Core/Assert.h" +#include "Core/ObjectPython.h" #include "Utils/Scripting/ScriptBindings.h" namespace Falcor { -DepthStencilState::SharedPtr DepthStencilState::create(const Desc& desc) +ref DepthStencilState::create(const Desc& desc) { - return SharedPtr(new DepthStencilState(desc)); + return ref(new DepthStencilState(desc)); } DepthStencilState::~DepthStencilState() = default; @@ -92,6 +93,6 @@ const DepthStencilState::StencilDesc& DepthStencilState::getStencilDesc(Face fac FALCOR_SCRIPT_BINDING(DepthStencilState) { - pybind11::class_(m, "DepthStencilState"); + pybind11::class_>(m, "DepthStencilState"); } } // namespace Falcor diff --git a/Source/Falcor/Core/API/DepthStencilState.h b/Source/Falcor/Core/API/DepthStencilState.h index 8233696e5..d4d92ec35 100644 --- a/Source/Falcor/Core/API/DepthStencilState.h +++ b/Source/Falcor/Core/API/DepthStencilState.h @@ -29,18 +29,16 @@ #include "Common.h" #include "Handles.h" #include "Core/Macros.h" -#include +#include "Core/Object.h" namespace Falcor { /** * Depth-Stencil state */ -class FALCOR_API DepthStencilState +class FALCOR_API DepthStencilState : public Object { public: - using SharedPtr = std::shared_ptr; - /** * Used for stencil control. */ @@ -180,7 +178,7 @@ class FALCOR_API DepthStencilState * @param desc Depth-stencil descriptor. * @return New object, or throws an exception if an error occurred. */ - static SharedPtr create(const Desc& desc); + static ref create(const Desc& desc); /** * Check if depth test is enabled or disabled diff --git a/Source/Falcor/Core/API/Device.cpp b/Source/Falcor/Core/API/Device.cpp index 1e270aeb4..adf26bd53 100644 --- a/Source/Falcor/Core/API/Device.cpp +++ b/Source/Falcor/Core/API/Device.cpp @@ -33,6 +33,7 @@ #include "Core/Macros.h" #include "Core/Assert.h" #include "Core/Errors.h" +#include "Core/ObjectPython.h" #include "Core/Program/Program.h" #include "Core/Program/ProgramManager.h" #include "Utils/Logger.h" @@ -68,13 +69,6 @@ static_assert(getMaxViewportCount() <= 8); static const uint32_t kTransientHeapConstantBufferSize = 16 * 1024 * 1024; -std::shared_ptr gpDevice; - -std::shared_ptr& getGlobalDevice() -{ - return gpDevice; -} - class GFXDebugCallBack : public gfx::IDebugCallback { virtual SLANG_NO_THROW void SLANG_MCALL @@ -385,33 +379,7 @@ inline Device::ShaderModel querySupportedShaderModel(gfx::IDevice* pDevice) return Device::ShaderModel::Unknown; } -Device::Device(const Desc& desc) : mDesc(desc) {} -Device::~Device() -{ - mGfxDevice.setNull(); - -#if FALCOR_NVAPI_AVAILABLE - mpAPIDispatcher.reset(); -#endif -} - -std::shared_ptr Device::create(const Device::Desc& desc) -{ - if (gpDevice) - { - throw RuntimeError("Falcor only supports a single device."); - } - - gpDevice = std::make_shared(desc); - if (!gpDevice->init()) - { - throw RuntimeError("Failed to create device."); - } - - return gpDevice; -} - -bool Device::init() +Device::Device(const Desc& desc) : mDesc(desc) { // Create a global slang session passed to GFX and used for compiling programs in ProgramManager. slang::createGlobalSession(mSlangGlobalSession.writeRef()); @@ -428,19 +396,19 @@ bool Device::init() throw RuntimeError("Vulkan device not supported."); #endif - gfx::IDevice::Desc desc = {}; - desc.deviceType = getGfxDeviceType(mDesc.type); - desc.slang.slangGlobalSession = mSlangGlobalSession; + gfx::IDevice::Desc gfxDesc = {}; + gfxDesc.deviceType = getGfxDeviceType(mDesc.type); + gfxDesc.slang.slangGlobalSession = mSlangGlobalSession; // Setup shader cache. - desc.shaderCache.maxEntryCount = mDesc.maxShaderCacheEntryCount; + gfxDesc.shaderCache.maxEntryCount = mDesc.maxShaderCacheEntryCount; if (mDesc.shaderCachePath == "") { - desc.shaderCache.shaderCachePath = nullptr; + gfxDesc.shaderCache.shaderCachePath = nullptr; } else { - desc.shaderCache.shaderCachePath = mDesc.shaderCachePath.c_str(); + gfxDesc.shaderCache.shaderCachePath = mDesc.shaderCachePath.c_str(); // If the supplied shader cache path does not exist, we will need to create it before creating the device. if (std::filesystem::exists(mDesc.shaderCachePath)) { @@ -463,15 +431,15 @@ bool Device::init() gfx::D3D12ExperimentalFeaturesDesc experimentalFeaturesDesc = {}; experimentalFeaturesDesc.numFeatures = (uint32_t)mDesc.experimentalFeatures.size(); experimentalFeaturesDesc.featureIIDs = mDesc.experimentalFeatures.data(); - if (desc.deviceType == gfx::DeviceType::DirectX12) + if (gfxDesc.deviceType == gfx::DeviceType::DirectX12) extendedDescs.push_back(&experimentalFeaturesDesc); #endif - desc.extendedDescCount = extendedDescs.size(); - desc.extendedDescs = extendedDescs.data(); + gfxDesc.extendedDescCount = extendedDescs.size(); + gfxDesc.extendedDescs = extendedDescs.data(); #if FALCOR_NVAPI_AVAILABLE mpAPIDispatcher.reset(new PipelineCreationAPIDispatcher()); - desc.apiCommandDispatcher = static_cast(mpAPIDispatcher.get()); + gfxDesc.apiCommandDispatcher = static_cast(mpAPIDispatcher.get()); #endif // Setup debug layer. @@ -490,17 +458,17 @@ bool Device::init() // Try to create device on specific GPU. { - desc.adapterLUID = &gpus[mDesc.gpu].luid; - if (SLANG_FAILED(gfxCreateDevice(&desc, mGfxDevice.writeRef()))) + gfxDesc.adapterLUID = &gpus[mDesc.gpu].luid; + if (SLANG_FAILED(gfxCreateDevice(&gfxDesc, mGfxDevice.writeRef()))) logWarning("Failed to create device on GPU {} ({}).", mDesc.gpu, gpus[mDesc.gpu].name); } // Otherwise try create device on any available GPU. if (!mGfxDevice) { - desc.adapterLUID = nullptr; - if (SLANG_FAILED(gfxCreateDevice(&desc, mGfxDevice.writeRef()))) - return false; + gfxDesc.adapterLUID = nullptr; + if (SLANG_FAILED(gfxCreateDevice(&gfxDesc, mGfxDevice.writeRef()))) + throw RuntimeError("Failed to create device"); } const auto& deviceInfo = mGfxDevice->getDeviceInfo(); @@ -569,17 +537,29 @@ bool Device::init() transientHeapDesc.constantBufferDescriptorCount = 1000000; transientHeapDesc.accelerationStructureDescriptorCount = 1000000; if (SLANG_FAILED(mGfxDevice->createTransientResourceHeap(transientHeapDesc, mpTransientResourceHeaps[i].writeRef()))) - { - return false; - } + throw RuntimeError("Failed to create transient resource heap"); } gfx::ICommandQueue::Desc queueDesc = {}; queueDesc.type = gfx::ICommandQueue::QueueType::Graphics; if (SLANG_FAILED(mGfxDevice->createCommandQueue(queueDesc, mGfxCommandQueue.writeRef()))) - return false; + throw RuntimeError("Failed to create command queue"); - mpFrameFence = GpuFence::create(this); + // The Device class contains a bunch of nested resource objects that have strong references to the device. + // This is because we want a strong reference to the device when those objects are returned to the user. + // However, here it immediately creates cyclic references device->resource->device upon creation of the device. + // To break the cycles, we break the strong reference to the device for the resources that it owns. + + // Here, we temporarily increase the refcount of the device, so it won't be destroyed upon breaking the + // nested strong references to it. + this->incRef(); + +#if FALCOR_ENABLE_REF_TRACKING + this->setEnableRefTracking(true); +#endif + + mpFrameFence = GpuFence::create(ref(this)); + mpFrameFence->breakStrongReferenceToDevice(); #if FALCOR_HAS_D3D12 if (getType() == Type::D3D12) @@ -595,24 +575,60 @@ bool Device::init() } #endif // FALCOR_HAS_D3D12 - mpProgramManager = std::make_unique(shared_from_this()); - mpProfiler = std::make_unique(this); + mpProgramManager = std::make_unique(this); + + mpProfiler = std::make_unique(ref(this)); + mpProfiler->breakStrongReferenceToDevice(); + + mpDefaultSampler = Sampler::create(ref(this), Sampler::Desc()); + mpDefaultSampler->breakStrongReferenceToDevice(); + + mpUploadHeap = GpuMemoryHeap::create(ref(this), GpuMemoryHeap::Type::Upload, 1024 * 1024 * 2, mpFrameFence); + mpUploadHeap->breakStrongReferenceToDevice(); - mpDefaultSampler = Sampler::create(this, Sampler::Desc()); + mpTimestampQueryHeap = QueryHeap::create(ref(this), QueryHeap::Type::Timestamp, 1024 * 1024); + mpTimestampQueryHeap->breakStrongReferenceToDevice(); - mpUploadHeap = GpuMemoryHeap::create(this, GpuMemoryHeap::Type::Upload, 1024 * 1024 * 2, mpFrameFence); mpRenderContext = std::make_unique(this, mGfxCommandQueue); - mpRenderContext->flush(); // This will bind the descriptor heaps. + // TODO: Do we need to flush here or should RenderContext::create() bind the descriptor heaps automatically without flush? See #749. + mpRenderContext->flush(); // This will bind the descriptor heaps. - return true; + this->decRef(false); } -std::weak_ptr Device::createQueryHeap(QueryHeap::Type type, uint32_t count) +Device::~Device() { - QueryHeap::SharedPtr pHeap = QueryHeap::create(this, type, count); - mTimestampQueryHeaps.push_back(pHeap); - return pHeap; + mpRenderContext->flush(true); + + mpProfiler.reset(); + + // Release all the bound resources. Need to do that before deleting the RenderContext + mGfxCommandQueue.setNull(); + mDeferredReleases = decltype(mDeferredReleases)(); + mpRenderContext.reset(); + mpUploadHeap.reset(); + mpTimestampQueryHeap.reset(); + for (size_t i = 0; i < kInFlightFrameCount; ++i) + mpTransientResourceHeaps[i].setNull(); + + mpDefaultSampler.reset(); + mpFrameFence.reset(); + +#if FALCOR_HAS_D3D12 + mpD3D12CpuDescPool.reset(); + mpD3D12GpuDescPool.reset(); +#endif // FALCOR_HAS_D3D12 + + mpProgramManager.reset(); + + mDeferredReleases = decltype(mDeferredReleases)(); + + mGfxDevice.setNull(); + +#if FALCOR_NVAPI_AVAILABLE + mpAPIDispatcher.reset(); +#endif } void Device::releaseResource(ISlangUnknown* pResource) @@ -655,32 +671,6 @@ void Device::executeDeferredReleases() #endif // FALCOR_HAS_D3D12 } -void Device::cleanup() -{ - mpRenderContext->flush(true); - // Release all the bound resources. Need to do that before deleting the RenderContext - mGfxCommandQueue.setNull(); - mDeferredReleases = decltype(mDeferredReleases)(); - mpRenderContext.reset(); - mpUploadHeap.reset(); - for (size_t i = 0; i < kInFlightFrameCount; ++i) - mpTransientResourceHeaps[i].setNull(); - - mpFrameFence.reset(); - for (auto& heap : mTimestampQueryHeaps) - heap.reset(); - -#if FALCOR_HAS_D3D12 - mpD3D12CpuDescPool.reset(); - mpD3D12GpuDescPool.reset(); -#endif // FALCOR_HAS_D3D12 - - mpProfiler.reset(); - mpProgramManager.reset(); - - mDeferredReleases = decltype(mDeferredReleases)(); -} - void Device::flushAndSync() { mpRenderContext->flush(true); @@ -876,16 +866,62 @@ NativeHandle Device::getNativeHandle(uint32_t index) const FALCOR_SCRIPT_BINDING(Device) { + using namespace pybind11::literals; + + FALCOR_SCRIPT_BINDING_DEPENDENCY(Formats) + FALCOR_SCRIPT_BINDING_DEPENDENCY(Buffer) + FALCOR_SCRIPT_BINDING_DEPENDENCY(Texture) + FALCOR_SCRIPT_BINDING_DEPENDENCY(Profiler) + FALCOR_SCRIPT_BINDING_DEPENDENCY(RenderContext) + + pybind11::class_> device(m, "Device"); + pybind11::enum_ deviceType(m, "DeviceType"); deviceType.value("Default", Device::Type::Default); deviceType.value("D3D12", Device::Type::D3D12); deviceType.value("Vulkan", Device::Type::Vulkan); - ScriptBindings::SerializableStruct deviceDesc(m, "DeviceDesc"); -#define field(f_) field(#f_, &Device::Desc::f_) - deviceDesc.field(type); - deviceDesc.field(gpu); - deviceDesc.field(enableDebugLayer); -#undef field + pybind11::class_ limits(device, "Limits"); + limits.def_readonly("max_compute_dispatch_thread_groups", &Device::Limits::maxComputeDispatchThreadGroups); + limits.def_readonly("max_shader_visible_samplers", &Device::Limits::maxShaderVisibleSamplers); + + device.def( + pybind11::init( + [](Device::Type type, uint32_t gpu, bool enable_debug_layer) + { + Device::Desc desc; + desc.type = type; + desc.gpu = gpu; + desc.enableDebugLayer = enable_debug_layer; + return make_ref(desc); + } + ), + "type"_a = Device::Type::Default, "gpu"_a = 0, "enable_debug_layer"_a = false + ); + device.def( + "create_buffer", + [](ref self, size_t size, ResourceBindFlags bind_flags, Buffer::CpuAccess cpu_access) + { return Buffer::create(self, size, bind_flags, cpu_access); }, + "size"_a, "bind_flags"_a = ResourceBindFlags::None, "cpu_access"_a = Buffer::CpuAccess::None + ); + device.def( + "create_texture", + [](ref self, uint32_t width, uint32_t height, uint32_t depth, ResourceFormat format, uint32_t array_size, + uint32_t mip_levels, ResourceBindFlags bind_flags) + { + if (depth > 0) + return Texture::create3D(self, width, height, depth, format, mip_levels, nullptr, bind_flags); + else if (height > 0) + return Texture::create2D(self, width, height, format, array_size, mip_levels, nullptr, bind_flags); + else + return Texture::create1D(self, width, format, array_size, mip_levels, nullptr, bind_flags); + }, + "width"_a, "height"_a = 0, "depth"_a = 0, "format"_a = ResourceFormat::Unknown, "array_size"_a = 1, + "mip_levels"_a = uint32_t(Texture::kMaxPossible), "bind_flags"_a = ResourceBindFlags::None + ); + + device.def_property_readonly("profiler", &Device::getProfiler); + device.def_property_readonly("limits", &Device::getLimits); + device.def_property_readonly("render_context", &Device::getRenderContext); } } // namespace Falcor diff --git a/Source/Falcor/Core/API/Device.h b/Source/Falcor/Core/API/Device.h index e6a1cefe4..cd2c1ff6c 100644 --- a/Source/Falcor/Core/API/Device.h +++ b/Source/Falcor/Core/API/Device.h @@ -34,6 +34,7 @@ #include "RenderContext.h" #include "GpuMemoryHeap.h" #include "Core/Macros.h" +#include "Core/Object.h" #if FALCOR_HAS_D3D12 #include @@ -62,7 +63,7 @@ class PipelineCreationAPIDispatcher; class ProgramManager; class Profiler; -class FALCOR_API Device : public std::enable_shared_from_this +class FALCOR_API Device : public Object { public: /** @@ -144,20 +145,12 @@ class FALCOR_API Device : public std::enable_shared_from_this }; /** - * Create a new device. + * Constructor. Throws an exception if creation failed. * @param[in] desc Device configuration descriptor. - * @return A pointer to a new device object, or throws an exception if creation failed. */ - static std::shared_ptr create(const Desc& desc); - Device(const Desc& desc); ~Device(); - /** - * Acts as the destructor for Device. Some resources use the global device pointer their cleanup. - */ - void cleanup(); - ProgramManager* getProgramManager() const { return mpProgramManager.get(); } Profiler* getProfiler() const { return mpProfiler.get(); } @@ -220,30 +213,23 @@ class FALCOR_API Device : public std::enable_shared_from_this /** * Get an object that represents a default sampler. */ - std::shared_ptr getDefaultSampler() const { return mpDefaultSampler; } - - /** - * Create a new query heap. - * @param[in] type Type of queries. - * @param[in] count Number of queries. - * @return New query heap. - */ - std::weak_ptr createQueryHeap(QueryHeap::Type type, uint32_t count); + const ref& getDefaultSampler() const { return mpDefaultSampler; } #if FALCOR_HAS_D3D12 - const std::shared_ptr& getD3D12CpuDescriptorPool() const + const ref& getD3D12CpuDescriptorPool() const { requireD3D12(); return mpD3D12CpuDescPool; } - const std::shared_ptr& getD3D12GpuDescriptorPool() const + const ref& getD3D12GpuDescriptorPool() const { requireD3D12(); return mpD3D12GpuDescPool; } #endif // FALCOR_HAS_D3D12 - const GpuMemoryHeap::SharedPtr& getUploadHeap() const { return mpUploadHeap; } + const ref& getUploadHeap() const { return mpUploadHeap; } + const ref& getTimestampQueryHeap() const { return mpTimestampQueryHeap; } void releaseResource(ISlangUnknown* pResource); double getGpuTimestampFrequency() const { return mGpuTimestampFrequency; } // ms/tick @@ -316,7 +302,6 @@ class FALCOR_API Device : public std::enable_shared_from_this }; std::queue mDeferredReleases; - bool init(); void executeDeferredReleases(); Desc mDesc; @@ -326,17 +311,16 @@ class FALCOR_API Device : public std::enable_shared_from_this Slang::ComPtr mpTransientResourceHeaps[kInFlightFrameCount]; uint32_t mCurrentTransientResourceHeapIndex = 0; - std::shared_ptr mpDefaultSampler; - - GpuMemoryHeap::SharedPtr mpUploadHeap; + ref mpDefaultSampler; + ref mpUploadHeap; + ref mpTimestampQueryHeap; #if FALCOR_HAS_D3D12 - std::shared_ptr mpD3D12CpuDescPool; - std::shared_ptr mpD3D12GpuDescPool; + ref mpD3D12CpuDescPool; + ref mpD3D12GpuDescPool; #endif - GpuFence::SharedPtr mpFrameFence; + ref mpFrameFence; std::unique_ptr mpRenderContext; - std::list mTimestampQueryHeaps; double mGpuTimestampFrequency; Limits mLimits; @@ -358,9 +342,5 @@ inline constexpr uint32_t getMaxViewportCount() return 8; } -/// !!! DO NOT USE THIS !!! -/// This is only available during the migration away from having only a single global GPU device in Falcor. -FALCOR_API std::shared_ptr& getGlobalDevice(); - FALCOR_ENUM_CLASS_OPERATORS(Device::SupportedFeatures); } // namespace Falcor diff --git a/Source/Falcor/Core/API/FBO.cpp b/Source/Falcor/Core/API/FBO.cpp index df42c5ef8..a05a2be74 100644 --- a/Source/Falcor/Core/API/FBO.cpp +++ b/Source/Falcor/Core/API/FBO.cpp @@ -29,6 +29,7 @@ #include "Device.h" #include "GFXAPI.h" #include "Core/Errors.h" +#include "Core/ObjectPython.h" #include "Utils/Scripting/ScriptBindings.h" namespace Falcor @@ -113,8 +114,8 @@ void checkAttachArguments(const Texture* pTexture, uint32_t mipLevel, uint32_t f } } -Texture::SharedPtr createTexture2D( - Device* pDevice, +ref createTexture2D( + ref pDevice, uint32_t w, uint32_t h, ResourceFormat format, @@ -196,12 +197,12 @@ Fbo::Desc::Desc() mColorTargets.resize(Fbo::getMaxColorTargetCount()); } -Fbo::SharedPtr Fbo::create(Device* pDevice) +ref Fbo::create(ref pDevice) { - return SharedPtr(new Fbo(pDevice->shared_from_this())); + return ref(new Fbo(pDevice)); } -Fbo::SharedPtr Fbo::create(Device* pDevice, const std::vector& colors, const Texture::SharedPtr& pDepth) +ref Fbo::create(ref pDevice, const std::vector>& colors, const ref& pDepth) { auto pFbo = create(pDevice); for (uint32_t i = 0; i < colors.size(); i++) @@ -216,7 +217,7 @@ Fbo::SharedPtr Fbo::create(Device* pDevice, const std::vector pDevice) : mpDevice(std::move(pDevice)) +Fbo::Fbo(ref pDevice) : mpDevice(pDevice) { mColorAttachments.resize(getMaxColorTargetCount()); } @@ -305,7 +306,7 @@ void Fbo::initFramebuffer() const FALCOR_GFX_CALL(mpDevice->getGfxDevice()->createFramebuffer(desc, mGfxFramebuffer.writeRef())); } -RenderTargetView::SharedPtr Fbo::getRenderTargetView(uint32_t rtIndex) const +ref Fbo::getRenderTargetView(uint32_t rtIndex) const { auto& rt = mColorAttachments[rtIndex]; if (rt.pTexture) @@ -318,13 +319,13 @@ RenderTargetView::SharedPtr Fbo::getRenderTargetView(uint32_t rtIndex) const { // TODO: mColorAttachments doesn't contain enough information to fully determine the view dimension. Assume 2D for now. auto dimension = rt.arraySize > 1 ? RenderTargetView::Dimension::Texture2DArray : RenderTargetView::Dimension::Texture2D; - rt.pNullView = RenderTargetView::create(mpDevice.get(), dimension); + rt.pNullView = RenderTargetView::create(mpDevice, dimension); } - return std::static_pointer_cast(rt.pNullView); + return static_ref_cast(rt.pNullView); } } -DepthStencilView::SharedPtr Fbo::getDepthStencilView() const +ref Fbo::getDepthStencilView() const { if (mDepthStencil.pTexture) { @@ -337,13 +338,13 @@ DepthStencilView::SharedPtr Fbo::getDepthStencilView() const // TODO: mDepthStencil doesn't contain enough information to fully determine the view dimension. Assume 2D for now. auto dimension = mDepthStencil.arraySize > 1 ? DepthStencilView::Dimension::Texture2DArray : DepthStencilView::Dimension::Texture2D; - mDepthStencil.pNullView = DepthStencilView::create(mpDevice.get(), dimension); + mDepthStencil.pNullView = DepthStencilView::create(mpDevice, dimension); } - return std::static_pointer_cast(mDepthStencil.pNullView); + return static_ref_cast(mDepthStencil.pNullView); } } -void Fbo::attachDepthStencilTarget(const Texture::SharedPtr& pDepthStencil, uint32_t mipLevel, uint32_t firstArraySlice, uint32_t arraySize) +void Fbo::attachDepthStencilTarget(const ref& pDepthStencil, uint32_t mipLevel, uint32_t firstArraySlice, uint32_t arraySize) { bool changed = (mDepthStencil.pTexture != pDepthStencil); changed |= (mDepthStencil.mipLevel != mipLevel); @@ -369,13 +370,7 @@ void Fbo::attachDepthStencilTarget(const Texture::SharedPtr& pDepthStencil, uint applyDepthAttachment(); } -void Fbo::attachColorTarget( - const Texture::SharedPtr& pTexture, - uint32_t rtIndex, - uint32_t mipLevel, - uint32_t firstArraySlice, - uint32_t arraySize -) +void Fbo::attachColorTarget(const ref& pTexture, uint32_t rtIndex, uint32_t mipLevel, uint32_t firstArraySlice, uint32_t arraySize) { checkArgument( rtIndex < mColorAttachments.size(), "'rtIndex' ({}) is out of range. Only {} color targets are available.", rtIndex, @@ -474,7 +469,7 @@ void Fbo::calcAndValidateProperties() const mpDesc = &(*(getGlobalDescCache().insert(mTempDesc).first)); } -Texture::SharedPtr Fbo::getColorTexture(uint32_t index) const +ref Fbo::getColorTexture(uint32_t index) const { checkArgument( index < mColorAttachments.size(), "'index' ({}) is out of range. Only {} color slots are available.", index, @@ -483,7 +478,7 @@ Texture::SharedPtr Fbo::getColorTexture(uint32_t index) const return mColorAttachments[index].pTexture; } -const Texture::SharedPtr& Fbo::getDepthStencilTexture() const +const ref& Fbo::getDepthStencilTexture() const { return mDepthStencil.pTexture; } @@ -511,8 +506,8 @@ void Fbo::setSamplePositions(uint32_t samplesPerPixel, uint32_t pixelCount, cons } } -Fbo::SharedPtr Fbo::create2D( - Device* pDevice, +ref Fbo::create2D( + ref pDevice, uint32_t width, uint32_t height, const Fbo::Desc& fboDesc, @@ -528,7 +523,7 @@ Fbo::SharedPtr Fbo::create2D( checkArgument(mipLevels > 0, "'mipLevels' must not be zero."); checkArgument(sampleCount == 1 || mipLevels == 1, "Cannot create multi-sampled texture with more than one mip-level."); - Fbo::SharedPtr pFbo = create(pDevice); + ref pFbo = create(pDevice); // Create the color targets for (uint32_t i = 0; i < Fbo::getMaxColorTargetCount(); i++) @@ -536,7 +531,7 @@ Fbo::SharedPtr Fbo::create2D( if (fboDesc.getColorTargetFormat(i) != ResourceFormat::Unknown) { Texture::BindFlags flags = getBindFlags(false, fboDesc.isColorTargetUav(i)); - Texture::SharedPtr pTex = + ref pTex = createTexture2D(pDevice, width, height, fboDesc.getColorTargetFormat(i), sampleCount, arraySize, mipLevels, flags); pFbo->attachColorTarget(pTex, i, 0, 0, kAttachEntireMipLevel); } @@ -545,7 +540,7 @@ Fbo::SharedPtr Fbo::create2D( if (fboDesc.getDepthStencilFormat() != ResourceFormat::Unknown) { Texture::BindFlags flags = getBindFlags(true, fboDesc.isDepthStencilUav()); - Texture::SharedPtr pDepth = + ref pDepth = createTexture2D(pDevice, width, height, fboDesc.getDepthStencilFormat(), sampleCount, arraySize, mipLevels, flags); pFbo->attachDepthStencilTarget(pDepth, 0, 0, kAttachEntireMipLevel); } @@ -553,8 +548,8 @@ Fbo::SharedPtr Fbo::create2D( return pFbo; } -Fbo::SharedPtr Fbo::createCubemap( - Device* pDevice, +ref Fbo::createCubemap( + ref pDevice, uint32_t width, uint32_t height, const Desc& fboDesc, @@ -568,7 +563,7 @@ Fbo::SharedPtr Fbo::createCubemap( checkArgument(mipLevels > 0, "'mipLevels' must not be zero."); checkArgument(fboDesc.getSampleCount() == 1, "Cannot create multi-sampled cube map."); - Fbo::SharedPtr pFbo = create(pDevice); + ref pFbo = create(pDevice); // Create the color targets for (uint32_t i = 0; i < getMaxColorTargetCount(); i++) @@ -588,15 +583,20 @@ Fbo::SharedPtr Fbo::createCubemap( return pFbo; } -Fbo::SharedPtr Fbo::create2D(Device* pDevice, uint32_t width, uint32_t height, ResourceFormat color, ResourceFormat depth) +ref Fbo::create2D(ref pDevice, uint32_t width, uint32_t height, ResourceFormat color, ResourceFormat depth) { Desc d; d.setColorTarget(0, color).setDepthStencilTarget(depth); return create2D(pDevice, width, height, d); } +void Fbo::breakStrongReferenceToDevice() +{ + mpDevice.breakStrongReference(); +} + FALCOR_SCRIPT_BINDING(Fbo) { - pybind11::class_(m, "Fbo"); + pybind11::class_>(m, "Fbo"); } } // namespace Falcor diff --git a/Source/Falcor/Core/API/FBO.h b/Source/Falcor/Core/API/FBO.h index d4658802a..b8aa2a9e0 100644 --- a/Source/Falcor/Core/API/FBO.h +++ b/Source/Falcor/Core/API/FBO.h @@ -32,7 +32,7 @@ #include "ResourceViews.h" #include "Texture.h" #include "Core/Macros.h" -#include +#include "Core/Object.h" #include #include @@ -42,11 +42,9 @@ namespace Falcor * Low level framebuffer object. * This class abstracts the API's framebuffer creation and management. */ -class FALCOR_API Fbo +class FALCOR_API Fbo : public Object { public: - using SharedPtr = std::shared_ptr; - class FALCOR_API Desc { public: @@ -142,7 +140,7 @@ class FALCOR_API Fbo /** * Used to tell some functions to attach all array slices of a specific mip-level. */ - static const uint32_t kAttachEntireMipLevel = uint32_t(-1); + static constexpr uint32_t kAttachEntireMipLevel = uint32_t(-1); /** * Destructor. Releases the API object @@ -153,7 +151,7 @@ class FALCOR_API Fbo * Create a new empty FBO. * @return A new object, or throws an exception if creation failed. */ - static SharedPtr create(Device* pDevice); + static ref create(ref pDevice); /** * Create an FBO from a list of textures. It will bind mip 0 and the all of the array slices. @@ -163,7 +161,7 @@ class FALCOR_API Fbo * @return A new object. An exception is thrown if creation failed, for example due to texture size mismatch, bind flags issues, illegal * formats, etc. */ - static SharedPtr create(Device* pDevice, const std::vector& colors, const Texture::SharedPtr& pDepth = nullptr); + static ref create(ref pDevice, const std::vector>& colors, const ref& pDepth = nullptr); /** * Create a color-only 2D framebuffer. @@ -174,8 +172,8 @@ class FALCOR_API Fbo * @param[in] mipLevels Optional. The number of mip levels to create. You can use Texture#kMaxPossible to create the entire chain. * @return A new object. An exception is thrown if creation failed, for example due to invalid parameters. */ - static SharedPtr create2D( - Device* pDevice, + static ref create2D( + ref pDevice, uint32_t width, uint32_t height, const Desc& fboDesc, @@ -192,8 +190,8 @@ class FALCOR_API Fbo * @param[in] mipLevels Optional. The number of mip levels to create. You can use Texture#kMaxPossible to create the entire chain. * @return A new object. An exception is thrown if creation failed, for example due to invalid parameters. */ - static SharedPtr createCubemap( - Device* pDevice, + static ref createCubemap( + ref pDevice, uint32_t width, uint32_t height, const Desc& fboDesc, @@ -209,8 +207,8 @@ class FALCOR_API Fbo * @param[in] depth The depth-format. If a depth-buffer is not required, use ResourceFormat::Unknown. * @return A new object. An exception is thrown if creation failed, for example due to invalid parameters. */ - static SharedPtr create2D( - Device* pDevice, + static ref create2D( + ref pDevice, uint32_t width, uint32_t height, ResourceFormat color, @@ -227,7 +225,7 @@ class FALCOR_API Fbo * pTexture->getArraySize()] */ void attachDepthStencilTarget( - const Texture::SharedPtr& pDepthStencil, + const ref& pDepthStencil, uint32_t mipLevel = 0, uint32_t firstArraySlice = 0, uint32_t arraySize = kAttachEntireMipLevel @@ -244,7 +242,7 @@ class FALCOR_API Fbo * pTexture->getArraySize()] */ void attachColorTarget( - const Texture::SharedPtr& pColorTexture, + const ref& pColorTexture, uint32_t rtIndex, uint32_t mipLevel = 0, uint32_t firstArraySlice = 0, @@ -264,12 +262,12 @@ class FALCOR_API Fbo /** * Get an attached color texture. If no texture is attached will return nullptr. */ - Texture::SharedPtr getColorTexture(uint32_t index) const; + ref getColorTexture(uint32_t index) const; /** * Get the attached depth-stencil texture, or nullptr if no texture is attached. */ - const Texture::SharedPtr& getDepthStencilTexture() const; + const ref& getDepthStencilTexture() const; /** * Get the width of the FBO @@ -310,12 +308,12 @@ class FALCOR_API Fbo /** * Get a depth-stencil view to the depth-stencil target. */ - DepthStencilView::SharedPtr getDepthStencilView() const; + ref getDepthStencilView() const; /** * Get a render target view to a color target. */ - RenderTargetView::SharedPtr getRenderTargetView(uint32_t rtIndex) const; + ref getRenderTargetView(uint32_t rtIndex) const; struct SamplePosition { @@ -344,13 +342,15 @@ class FALCOR_API Fbo struct Attachment { - Texture::SharedPtr pTexture; - ResourceView::SharedPtr pNullView; + ref pTexture; + ref pNullView; uint32_t mipLevel = 0; uint32_t arraySize = 1; uint32_t firstArraySlice = 0; }; + void breakStrongReferenceToDevice(); + private: void validateAttachment(const Attachment& attachment) const; void calcAndValidateProperties() const; @@ -365,9 +365,9 @@ class FALCOR_API Fbo */ void finalize() const; - Fbo(std::shared_ptr pDevice); + Fbo(ref pDevice); - std::shared_ptr mpDevice; + mutable BreakableReference mpDevice; std::vector mSamplePositions; uint32_t mSamplePositionsPixelCount = 0; diff --git a/Source/Falcor/Core/API/FencedPool.h b/Source/Falcor/Core/API/FencedPool.h index 868a64993..af1d90577 100644 --- a/Source/Falcor/Core/API/FencedPool.h +++ b/Source/Falcor/Core/API/FencedPool.h @@ -30,16 +30,15 @@ #include "Core/Assert.h" #include "Core/Macros.h" #include "Core/Errors.h" +#include "Core/Object.h" #include -#include namespace Falcor { template -class FALCOR_API FencedPool +class FALCOR_API FencedPool : public Object { public: - using SharedPtr = std::shared_ptr>; using NewObjectFuncType = ObjectType (*)(void*); /** @@ -49,9 +48,9 @@ class FALCOR_API FencedPool * @param[in] pUserData Optional ptr to user data passed to the object creation function. * @return A new object, or throws an exception if creation failed. */ - static SharedPtr create(GpuFence::SharedConstPtr pFence, NewObjectFuncType newFunc, void* pUserData = nullptr) + static ref create(ref pFence, NewObjectFuncType newFunc, void* pUserData = nullptr) { - return SharedPtr(new FencedPool(pFence, newFunc, pUserData)); + return new FencedPool(pFence, newFunc, pUserData); } /** @@ -82,7 +81,7 @@ class FALCOR_API FencedPool } private: - FencedPool(GpuFence::SharedConstPtr pFence, NewObjectFuncType newFunc, void* pUserData) + FencedPool(ref pFence, NewObjectFuncType newFunc, void* pUserData) : mNewObjFunc(newFunc), mpFence(pFence), mpUserData(pUserData) { FALCOR_ASSERT(pFence && newFunc); @@ -106,7 +105,7 @@ class FALCOR_API FencedPool ObjectType mActiveObject; NewObjectFuncType mNewObjFunc = nullptr; std::queue mQueue; - GpuFence::SharedConstPtr mpFence; + ref mpFence; void* mpUserData; }; } // namespace Falcor diff --git a/Source/Falcor/Core/API/Formats.cpp b/Source/Falcor/Core/API/Formats.cpp index 9a3b6d345..6c9a43b04 100644 --- a/Source/Falcor/Core/API/Formats.cpp +++ b/Source/Falcor/Core/API/Formats.cpp @@ -114,7 +114,21 @@ static_assert(std::size(kFormatDesc) == (size_t)ResourceFormat::BC7UnormSrgb + 1 FALCOR_SCRIPT_BINDING(Formats) { - // Resource formats + pybind11::enum_ resourceBindFlags(m, "ResourceBindFlags"); + resourceBindFlags.value("None_", ResourceBindFlags::None); + resourceBindFlags.value("Vertex", ResourceBindFlags::Vertex); + resourceBindFlags.value("Index", ResourceBindFlags::Index); + resourceBindFlags.value("Constant", ResourceBindFlags::Constant); + resourceBindFlags.value("StreamOutput", ResourceBindFlags::StreamOutput); + resourceBindFlags.value("ShaderResource", ResourceBindFlags::ShaderResource); + resourceBindFlags.value("UnorderedAccess", ResourceBindFlags::UnorderedAccess); + resourceBindFlags.value("RenderTarget", ResourceBindFlags::RenderTarget); + resourceBindFlags.value("DepthStencil", ResourceBindFlags::DepthStencil); + resourceBindFlags.value("IndirectArg", ResourceBindFlags::IndirectArg); + resourceBindFlags.value("Shared", ResourceBindFlags::Shared); + resourceBindFlags.value("AccelerationStructure", ResourceBindFlags::AccelerationStructure); + ScriptBindings::addEnumBinaryOperators(resourceBindFlags); + pybind11::enum_ resourceFormat(m, "ResourceFormat"); for (uint32_t i = 0; i < (uint32_t)ResourceFormat::Count; i++) { @@ -123,9 +137,10 @@ FALCOR_SCRIPT_BINDING(Formats) pybind11::enum_ textureChannels(m, "TextureChannelFlags"); // TODO: These generate an "invalid format string" error from Python. - // textureChannels.value("Red", TextureChannelFlags::Red); - // textureChannels.value("Green", TextureChannelFlags::Green); - // textureChannels.value("Blue", TextureChannelFlags::Blue); + textureChannels.value("None_", TextureChannelFlags::None); + textureChannels.value("Red", TextureChannelFlags::Red); + textureChannels.value("Green", TextureChannelFlags::Green); + textureChannels.value("Blue", TextureChannelFlags::Blue); textureChannels.value("Alpha", TextureChannelFlags::Alpha); textureChannels.value("RGB", TextureChannelFlags::RGB); textureChannels.value("RGBA", TextureChannelFlags::RGBA); diff --git a/Source/Falcor/Core/API/GpuFence.cpp b/Source/Falcor/Core/API/GpuFence.cpp index 487ce77b7..c4f2aa01d 100644 --- a/Source/Falcor/Core/API/GpuFence.cpp +++ b/Source/Falcor/Core/API/GpuFence.cpp @@ -34,7 +34,7 @@ namespace Falcor { -GpuFence::GpuFence(std::shared_ptr pDevice, bool shared) : mpDevice(std::move(pDevice)), mCpuValue(1) +GpuFence::GpuFence(ref pDevice, bool shared) : mpDevice(pDevice), mCpuValue(1) { FALCOR_ASSERT(mpDevice); gfx::IFence::Desc fenceDesc = {}; @@ -44,9 +44,9 @@ GpuFence::GpuFence(std::shared_ptr pDevice, bool shared) : mpDevice(std: GpuFence::~GpuFence() = default; -GpuFence::SharedPtr GpuFence::create(Device* pDevice, bool shared) +ref GpuFence::create(ref pDevice, bool shared) { - return SharedPtr(new GpuFence(pDevice->shared_from_this(), shared)); + return ref(new GpuFence(pDevice, shared)); } uint64_t GpuFence::gpuSignal(CommandQueueHandle pQueue) @@ -107,4 +107,10 @@ NativeHandle GpuFence::getNativeHandle() const #endif return {}; } + +void GpuFence::breakStrongReferenceToDevice() +{ + mpDevice.breakStrongReference(); +} + } // namespace Falcor diff --git a/Source/Falcor/Core/API/GpuFence.h b/Source/Falcor/Core/API/GpuFence.h index c5056d44b..4c00df7be 100644 --- a/Source/Falcor/Core/API/GpuFence.h +++ b/Source/Falcor/Core/API/GpuFence.h @@ -30,7 +30,7 @@ #include "Handles.h" #include "NativeHandle.h" #include "Core/Macros.h" -#include +#include "Core/Object.h" #include namespace Falcor @@ -43,19 +43,16 @@ using CommandQueueHandle = gfx::ICommandQueue*; * 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 FALCOR_API GpuFence +class FALCOR_API GpuFence : public Object { public: - using SharedPtr = std::shared_ptr; - using SharedConstPtr = std::shared_ptr; - ~GpuFence(); /** * Create a new GPU fence. * @return A new object, or throws an exception if creation failed. */ - static SharedPtr create(Device* pDevice, bool shared = false); + static ref create(ref pDevice, bool shared = false); /** * Get the internal API handle @@ -109,10 +106,12 @@ class FALCOR_API GpuFence */ SharedResourceApiHandle getSharedApiHandle() const; + void breakStrongReferenceToDevice(); + private: - GpuFence(std::shared_ptr pDevice, bool shared); + GpuFence(ref pDevice, bool shared); - std::shared_ptr mpDevice; + BreakableReference mpDevice; Slang::ComPtr mGfxFence; uint64_t mCpuValue; mutable SharedResourceApiHandle mSharedApiHandle = 0; diff --git a/Source/Falcor/Core/API/GpuMemoryHeap.cpp b/Source/Falcor/Core/API/GpuMemoryHeap.cpp index 8c058572a..cd7c98f33 100644 --- a/Source/Falcor/Core/API/GpuMemoryHeap.cpp +++ b/Source/Falcor/Core/API/GpuMemoryHeap.cpp @@ -40,15 +40,15 @@ GpuMemoryHeap::~GpuMemoryHeap() mDeferredReleases = decltype(mDeferredReleases)(); } -GpuMemoryHeap::GpuMemoryHeap(std::shared_ptr pDevice, Type type, size_t pageSize, const GpuFence::SharedPtr& pFence) - : mpDevice(std::move(pDevice)), mType(type), mpFence(pFence), mPageSize(pageSize) +GpuMemoryHeap::GpuMemoryHeap(ref pDevice, Type type, size_t pageSize, ref pFence) + : mpDevice(pDevice), mType(type), mpFence(pFence), mPageSize(pageSize) { allocateNewPage(); } -GpuMemoryHeap::SharedPtr GpuMemoryHeap::create(Device* pDevice, Type type, size_t pageSize, const GpuFence::SharedPtr& pFence) +ref GpuMemoryHeap::create(ref pDevice, Type type, size_t pageSize, ref pFence) { - return SharedPtr(new GpuMemoryHeap(pDevice->shared_from_this(), type, pageSize, pFence)); + return ref(new GpuMemoryHeap(pDevice, type, pageSize, pFence)); } void GpuMemoryHeap::allocateNewPage() @@ -144,7 +144,7 @@ void GpuMemoryHeap::executeDeferredReleases() } Slang::ComPtr createBuffer( - Device* pDevice, + ref pDevice, Buffer::State initState, size_t size, Buffer::BindFlags bindFlags, @@ -189,10 +189,16 @@ Buffer::State getInitState(GpuMemoryHeap::Type t) void GpuMemoryHeap::initBasePageData(BaseData& data, size_t size) { data.gfxBufferResource = createBuffer( - mpDevice.get(), getInitState(mType), size, Buffer::BindFlags::Vertex | Buffer::BindFlags::Index | Buffer::BindFlags::Constant, + mpDevice, getInitState(mType), size, Buffer::BindFlags::Vertex | Buffer::BindFlags::Index | Buffer::BindFlags::Constant, getCpuAccess(mType) ); data.offset = 0; FALCOR_GFX_CALL(data.gfxBufferResource->map(nullptr, (void**)&data.pData)); } + +void GpuMemoryHeap::breakStrongReferenceToDevice() +{ + mpDevice.breakStrongReference(); +} + } // namespace Falcor diff --git a/Source/Falcor/Core/API/GpuMemoryHeap.h b/Source/Falcor/Core/API/GpuMemoryHeap.h index 9b6d8d800..1bbce3998 100644 --- a/Source/Falcor/Core/API/GpuMemoryHeap.h +++ b/Source/Falcor/Core/API/GpuMemoryHeap.h @@ -30,17 +30,15 @@ #include "Handles.h" #include "GpuFence.h" #include "Core/Macros.h" -#include +#include "Core/Object.h" #include #include namespace Falcor { -class FALCOR_API GpuMemoryHeap +class FALCOR_API GpuMemoryHeap : public Object { public: - using SharedPtr = std::shared_ptr; - enum class Type { Default, @@ -60,7 +58,7 @@ class FALCOR_API GpuMemoryHeap uint64_t pageID = 0; uint64_t fenceValue = 0; - static const uint64_t kMegaPageId = -1; + static constexpr uint64_t kMegaPageId = -1; bool operator<(const Allocation& other) const { return fenceValue > other.fenceValue; } }; @@ -73,15 +71,17 @@ class FALCOR_API GpuMemoryHeap * @param[in] pFence Fence to use for synchronization. * @return A new object, or throws an exception if creation failed. */ - static SharedPtr create(Device* pDevice, Type type, size_t pageSize, const GpuFence::SharedPtr& pFence); + static ref create(ref pDevice, Type type, size_t pageSize, ref pFence); Allocation allocate(size_t size, size_t alignment = 1); void release(Allocation& data); size_t getPageSize() const { return mPageSize; } void executeDeferredReleases(); + void breakStrongReferenceToDevice(); + private: - GpuMemoryHeap(std::shared_ptr pDevice, Type type, size_t pageSize, const GpuFence::SharedPtr& pFence); + GpuMemoryHeap(ref pDevice, Type type, size_t pageSize, ref pFence); struct PageData : public BaseData { @@ -91,9 +91,9 @@ class FALCOR_API GpuMemoryHeap using UniquePtr = std::unique_ptr; }; - std::shared_ptr mpDevice; + BreakableReference mpDevice; Type mType; - GpuFence::SharedPtr mpFence; + ref mpFence; size_t mPageSize = 0; size_t mCurrentPageId = 0; PageData::UniquePtr mpActivePage; diff --git a/Source/Falcor/Core/API/GpuTimer.cpp b/Source/Falcor/Core/API/GpuTimer.cpp index c2989abb5..f94758511 100644 --- a/Source/Falcor/Core/API/GpuTimer.cpp +++ b/Source/Falcor/Core/API/GpuTimer.cpp @@ -33,36 +33,28 @@ #include "GFXAPI.h" #include "Core/Assert.h" +#include "Core/ObjectPython.h" #include "Utils/Logger.h" #include "Utils/Scripting/ScriptBindings.h" namespace Falcor { -std::weak_ptr GpuTimer::spHeap; -GpuTimer::SharedPtr GpuTimer::create(Device* pDevice) +ref GpuTimer::create(ref pDevice) { - return SharedPtr(new GpuTimer(pDevice->shared_from_this())); + return ref(new GpuTimer(pDevice)); } -GpuTimer::GpuTimer(std::shared_ptr pDevice) : mpDevice(std::move(pDevice)) +GpuTimer::GpuTimer(ref pDevice) : mpDevice(pDevice) { FALCOR_ASSERT(mpDevice); - mpResolveBuffer = Buffer::create(mpDevice.get(), sizeof(uint64_t) * 2, Buffer::BindFlags::None, Buffer::CpuAccess::None, nullptr); - mpResolveStagingBuffer = - Buffer::create(mpDevice.get(), sizeof(uint64_t) * 2, Buffer::BindFlags::None, Buffer::CpuAccess::Read, nullptr); + mpResolveBuffer = Buffer::create(mpDevice, sizeof(uint64_t) * 2, Buffer::BindFlags::None, Buffer::CpuAccess::None, nullptr); + mpResolveStagingBuffer = Buffer::create(mpDevice, sizeof(uint64_t) * 2, Buffer::BindFlags::None, Buffer::CpuAccess::Read, nullptr); // Create timestamp query heap upon first use. - // We're allocating pairs of adjacent queries, so need our own heap to meet this requirement. - if (spHeap.expired()) - { - spHeap = mpDevice->createQueryHeap(QueryHeap::Type::Timestamp, 16 * 1024); - } - auto pHeap = spHeap.lock(); - FALCOR_ASSERT(pHeap); - mStart = pHeap->allocate(); - mEnd = pHeap->allocate(); + mStart = mpDevice->getTimestampQueryHeap()->allocate(); + mEnd = mpDevice->getTimestampQueryHeap()->allocate(); if (mStart == QueryHeap::kInvalidIndex || mEnd == QueryHeap::kInvalidIndex) { throw RuntimeError("Can't create GPU timer, no available timestamp queries."); @@ -72,11 +64,8 @@ GpuTimer::GpuTimer(std::shared_ptr pDevice) : mpDevice(std::move(pDevice GpuTimer::~GpuTimer() { - if (auto pHeap = spHeap.lock(); pHeap) - { - pHeap->release(mStart); - pHeap->release(mEnd); - } + mpDevice->getTimestampQueryHeap()->release(mStart); + mpDevice->getTimestampQueryHeap()->release(mEnd); } void GpuTimer::begin() @@ -97,7 +86,9 @@ void GpuTimer::begin() ); } - mpDevice->getRenderContext()->getLowLevelData()->getResourceCommandEncoder()->writeTimestamp(spHeap.lock()->getGfxQueryPool(), mStart); + mpDevice->getRenderContext()->getLowLevelData()->getResourceCommandEncoder()->writeTimestamp( + mpDevice->getTimestampQueryHeap()->getGfxQueryPool(), mStart + ); mStatus = Status::Begin; } @@ -109,7 +100,9 @@ void GpuTimer::end() return; } - mpDevice->getRenderContext()->getLowLevelData()->getResourceCommandEncoder()->writeTimestamp(spHeap.lock()->getGfxQueryPool(), mEnd); + mpDevice->getRenderContext()->getLowLevelData()->getResourceCommandEncoder()->writeTimestamp( + mpDevice->getTimestampQueryHeap()->getGfxQueryPool(), mEnd + ); mStatus = Status::End; } @@ -133,7 +126,7 @@ void GpuTimer::resolve() // Resolve timestamps into buffer. auto encoder = mpDevice->getRenderContext()->getLowLevelData()->getResourceCommandEncoder(); - encoder->resolveQuery(spHeap.lock()->getGfxQueryPool(), mStart, 2, mpResolveBuffer->getGfxBufferResource(), 0); + encoder->resolveQuery(mpDevice->getTimestampQueryHeap()->getGfxQueryPool(), mStart, 2, mpResolveBuffer->getGfxBufferResource(), 0); // Copy resolved timestamps to staging buffer for readback. This inserts the necessary barriers. mpDevice->getRenderContext()->copyResource(mpResolveStagingBuffer.get(), mpResolveBuffer.get()); @@ -173,8 +166,13 @@ double GpuTimer::getElapsedTime() return mElapsedTime; } +void GpuTimer::breakStrongReferenceToDevice() +{ + mpDevice.breakStrongReference(); +} + FALCOR_SCRIPT_BINDING(GpuTimer) { - pybind11::class_(m, "GpuTimer"); + pybind11::class_>(m, "GpuTimer"); } } // namespace Falcor diff --git a/Source/Falcor/Core/API/GpuTimer.h b/Source/Falcor/Core/API/GpuTimer.h index 05fb26bed..a71873afe 100644 --- a/Source/Falcor/Core/API/GpuTimer.h +++ b/Source/Falcor/Core/API/GpuTimer.h @@ -30,7 +30,7 @@ #include "QueryHeap.h" #include "Buffer.h" #include "Core/Macros.h" -#include +#include "Core/Object.h" namespace Falcor { @@ -38,16 +38,14 @@ namespace Falcor * Abstracts GPU timer queries. * This class provides mechanism to get elapsed time in milliseconds between a pair of begin()/end() calls. */ -class FALCOR_API GpuTimer +class FALCOR_API GpuTimer : public Object { public: - using SharedPtr = std::shared_ptr; - /** * Create a new timer object. * @return A new object, or throws an exception if creation failed. */ - static SharedPtr create(Device* pDevice); + static ref create(ref pDevice); /** * Destroy a new object @@ -81,8 +79,10 @@ class FALCOR_API GpuTimer */ double getElapsedTime(); + void breakStrongReferenceToDevice(); + private: - GpuTimer(std::shared_ptr pDevice); + GpuTimer(ref pDevice); enum class Status { @@ -91,15 +91,14 @@ class FALCOR_API GpuTimer Idle }; - static std::weak_ptr spHeap; // TODO: REMOVEGLOBAL - std::shared_ptr mpDevice; + BreakableReference mpDevice; Status mStatus = Status::Idle; uint32_t mStart = 0; uint32_t mEnd = 0; double mElapsedTime = 0.0; bool mDataPending = false; ///< Set to true when resolved timings are available for readback. - Buffer::SharedPtr mpResolveBuffer; ///< GPU memory used as destination for resolving timestamp queries. - Buffer::SharedPtr mpResolveStagingBuffer; ///< CPU mappable memory for readback of resolved timings. + ref mpResolveBuffer; ///< GPU memory used as destination for resolving timestamp queries. + ref mpResolveStagingBuffer; ///< CPU mappable memory for readback of resolved timings. }; } // namespace Falcor diff --git a/Source/Falcor/Core/API/GraphicsStateObject.cpp b/Source/Falcor/Core/API/GraphicsStateObject.cpp index f4f09b6e9..ce34a3fc4 100644 --- a/Source/Falcor/Core/API/GraphicsStateObject.cpp +++ b/Source/Falcor/Core/API/GraphicsStateObject.cpp @@ -222,9 +222,9 @@ gfx::InputSlotClass getGFXInputSlotClass(VertexBufferLayout::InputClass cls) } } -BlendState::SharedPtr GraphicsStateObject::spDefaultBlendState; -RasterizerState::SharedPtr GraphicsStateObject::spDefaultRasterizerState; -DepthStencilState::SharedPtr GraphicsStateObject::spDefaultDepthStencilState; +ref GraphicsStateObject::spDefaultBlendState; +ref GraphicsStateObject::spDefaultRasterizerState; +ref GraphicsStateObject::spDefaultDepthStencilState; bool GraphicsStateObject::Desc::operator==(const GraphicsStateObject::Desc& other) const { @@ -271,7 +271,7 @@ GraphicsStateObject::~GraphicsStateObject() mpDevice->releaseResource(mpGFXRenderPassLayout); } -GraphicsStateObject::GraphicsStateObject(std::shared_ptr pDevice, const Desc& desc) : mpDevice(std::move(pDevice)), mDesc(desc) +GraphicsStateObject::GraphicsStateObject(ref pDevice, const Desc& desc) : mpDevice(pDevice), mDesc(desc) { if (spDefaultBlendState == nullptr) { @@ -457,8 +457,14 @@ GraphicsStateObject::GraphicsStateObject(std::shared_ptr pDevice, const FALCOR_GFX_CALL(mpDevice->getGfxDevice()->createGraphicsPipelineState(gfxDesc, mGfxPipelineState.writeRef())); } -GraphicsStateObject::SharedPtr GraphicsStateObject::create(Device* pDevice, const Desc& desc) +ref GraphicsStateObject::create(ref pDevice, const Desc& desc) { - return SharedPtr(new GraphicsStateObject(pDevice->shared_from_this(), desc)); + return ref(new GraphicsStateObject(pDevice, desc)); } + +void GraphicsStateObject::breakStrongReferenceToDevice() +{ + mpDevice.breakStrongReference(); +} + } // namespace Falcor diff --git a/Source/Falcor/Core/API/GraphicsStateObject.h b/Source/Falcor/Core/API/GraphicsStateObject.h index 18efaa5ad..77e988c32 100644 --- a/Source/Falcor/Core/API/GraphicsStateObject.h +++ b/Source/Falcor/Core/API/GraphicsStateObject.h @@ -28,22 +28,20 @@ #pragma once #include "Handles.h" #include "Core/Macros.h" +#include "Core/Object.h" #include "Core/API/VertexLayout.h" #include "Core/API/FBO.h" #include "Core/API/RasterizerState.h" #include "Core/API/DepthStencilState.h" #include "Core/API/BlendState.h" #include "Core/Program/ProgramVersion.h" -#include namespace Falcor { -class FALCOR_API GraphicsStateObject +class FALCOR_API GraphicsStateObject : public Object { public: - using SharedPtr = std::shared_ptr; - - static const uint32_t kSampleMaskAll = -1; + static constexpr uint32_t kSampleMaskAll = -1; /** * Primitive topology @@ -60,7 +58,7 @@ class FALCOR_API GraphicsStateObject class FALCOR_API Desc { public: - Desc& setVertexLayout(VertexLayout::SharedConstPtr pLayout) + Desc& setVertexLayout(ref pLayout) { mpLayout = pLayout; return *this; @@ -72,25 +70,25 @@ class FALCOR_API GraphicsStateObject return *this; } - Desc& setProgramKernels(ProgramKernels::SharedConstPtr pProgram) + Desc& setProgramKernels(ref pProgram) { mpProgram = pProgram; return *this; } - Desc& setBlendState(BlendState::SharedPtr pBlendState) + Desc& setBlendState(ref pBlendState) { mpBlendState = pBlendState; return *this; } - Desc& setRasterizerState(RasterizerState::SharedPtr pRasterizerState) + Desc& setRasterizerState(ref pRasterizerState) { mpRasterizerState = pRasterizerState; return *this; } - Desc& setDepthStencilState(DepthStencilState::SharedPtr pDepthStencilState) + Desc& setDepthStencilState(ref pDepthStencilState) { mpDepthStencilState = pDepthStencilState; return *this; @@ -108,13 +106,12 @@ class FALCOR_API GraphicsStateObject return *this; } - BlendState::SharedPtr getBlendState() const { return mpBlendState; } - RasterizerState::SharedPtr getRasterizerState() const { return mpRasterizerState; } - DepthStencilState::SharedPtr getDepthStencilState() const { return mpDepthStencilState; } - ProgramKernels::SharedConstPtr getProgramKernels() const { return mpProgram; } - ProgramVersion::SharedConstPtr getProgramVersion() const { return mpProgram->getProgramVersion(); } + ref getBlendState() const { return mpBlendState; } + ref getRasterizerState() const { return mpRasterizerState; } + ref getDepthStencilState() const { return mpDepthStencilState; } + ref getProgramKernels() const { return mpProgram; } uint32_t getSampleMask() const { return mSampleMask; } - VertexLayout::SharedConstPtr getVertexLayout() const { return mpLayout; } + ref getVertexLayout() const { return mpLayout; } PrimitiveType getPrimitiveType() const { return mPrimType; } Fbo::Desc getFboDesc() const { return mFboDesc; } @@ -123,11 +120,11 @@ class FALCOR_API GraphicsStateObject private: friend class GraphicsStateObject; Fbo::Desc mFboDesc; - VertexLayout::SharedConstPtr mpLayout; - ProgramKernels::SharedConstPtr mpProgram; - RasterizerState::SharedPtr mpRasterizerState; - DepthStencilState::SharedPtr mpDepthStencilState; - BlendState::SharedPtr mpBlendState; + ref mpLayout; + ref mpProgram; + ref mpRasterizerState; + ref mpDepthStencilState; + ref mpBlendState; uint32_t mSampleMask = kSampleMaskAll; PrimitiveType mPrimType = PrimitiveType::Undefined; }; @@ -139,7 +136,7 @@ class FALCOR_API GraphicsStateObject * @param[in] desc State object description. * @return New object, or throws an exception if creation failed. */ - static SharedPtr create(Device* pDevice, const Desc& desc); + static ref create(ref pDevice, const Desc& desc); gfx::IPipelineState* getGfxPipelineState() const { return mGfxPipelineState; } @@ -147,10 +144,12 @@ class FALCOR_API GraphicsStateObject gfx::IRenderPassLayout* getGFXRenderPassLayout() const { return mpGFXRenderPassLayout.get(); } + void breakStrongReferenceToDevice(); + private: - GraphicsStateObject(std::shared_ptr pDevice, const Desc& desc); + GraphicsStateObject(ref pDevice, const Desc& desc); - std::shared_ptr mpDevice; + BreakableReference mpDevice; Desc mDesc; Slang::ComPtr mGfxPipelineState; @@ -159,8 +158,8 @@ class FALCOR_API GraphicsStateObject Slang::ComPtr mpGFXRenderPassLayout; // Default state objects - static BlendState::SharedPtr spDefaultBlendState; // TODO: REMOVEGLOBAL - static RasterizerState::SharedPtr spDefaultRasterizerState; // TODO: REMOVEGLOBAL - static DepthStencilState::SharedPtr spDefaultDepthStencilState; // TODO: REMOVEGLOBAL + static ref spDefaultBlendState; // TODO: REMOVEGLOBAL + static ref spDefaultRasterizerState; // TODO: REMOVEGLOBAL + static ref spDefaultDepthStencilState; // TODO: REMOVEGLOBAL }; } // namespace Falcor diff --git a/Source/Falcor/Core/API/Handles.h b/Source/Falcor/Core/API/Handles.h index 5b96a04b3..b8858feaa 100644 --- a/Source/Falcor/Core/API/Handles.h +++ b/Source/Falcor/Core/API/Handles.h @@ -37,8 +37,8 @@ namespace Falcor { using GpuAddress = uint64_t; #if FALCOR_WINDOWS -using SharedResourceApiHandle = HANDLE; -using SharedFenceApiHandle = HANDLE; +using SharedResourceApiHandle = void*; // HANDLE +using SharedFenceApiHandle = void*; // HANDLE #elif FALCOR_LINUX using SharedResourceApiHandle = void*; using SharedFenceApiHandle = void*; diff --git a/Source/Falcor/Core/API/LowLevelContextData.cpp b/Source/Falcor/Core/API/LowLevelContextData.cpp index 5fb31f72f..0407c8311 100644 --- a/Source/Falcor/Core/API/LowLevelContextData.cpp +++ b/Source/Falcor/Core/API/LowLevelContextData.cpp @@ -36,7 +36,8 @@ namespace Falcor { LowLevelContextData::LowLevelContextData(Device* pDevice, gfx::ICommandQueue* pQueue) : mpDevice(pDevice), mpGfxCommandQueue(pQueue) { - mpFence = GpuFence::create(mpDevice); + mpFence = GpuFence::create(ref(mpDevice)); + mpFence->breakStrongReferenceToDevice(); FALCOR_ASSERT(mpFence); openCommandBuffer(); diff --git a/Source/Falcor/Core/API/LowLevelContextData.h b/Source/Falcor/Core/API/LowLevelContextData.h index 09ea24c3e..1492125b3 100644 --- a/Source/Falcor/Core/API/LowLevelContextData.h +++ b/Source/Falcor/Core/API/LowLevelContextData.h @@ -30,7 +30,6 @@ #include "Handles.h" #include "NativeHandle.h" #include "Core/Macros.h" -#include namespace Falcor { @@ -68,7 +67,7 @@ class FALCOR_API LowLevelContextData // TODO(@skallweit): This should be removed. CommandQueueHandle getCommandQueue() const { return mpGfxCommandQueue; } - const GpuFence::SharedPtr& getFence() const { return mpFence; } + const ref& getFence() const { return mpFence; } void closeCommandBuffer(); void openCommandBuffer(); @@ -91,7 +90,7 @@ class FALCOR_API LowLevelContextData Device* mpDevice; gfx::ICommandQueue* mpGfxCommandQueue; Slang::ComPtr mGfxCommandBuffer; - GpuFence::SharedPtr mpFence; + ref mpFence; gfx::ICommandBuffer* mpCommandBuffer = nullptr; bool mIsCommandBufferOpen = false; diff --git a/Source/Falcor/Core/API/NativeFormats.h b/Source/Falcor/Core/API/NativeFormats.h index 7fa14b748..802ea2cab 100644 --- a/Source/Falcor/Core/API/NativeFormats.h +++ b/Source/Falcor/Core/API/NativeFormats.h @@ -153,8 +153,8 @@ enum DXGI_FORMAT DXGI_FORMAT_P208 = 130, DXGI_FORMAT_V208 = 131, DXGI_FORMAT_V408 = 132, - DXGI_FORMAT_SAMPLER_FEEDBACK_MIN_MIP_OPAQUE, - DXGI_FORMAT_SAMPLER_FEEDBACK_MIP_REGION_USED_OPAQUE, + DXGI_FORMAT_SAMPLER_FEEDBACK_MIN_MIP_OPAQUE = 189, + DXGI_FORMAT_SAMPLER_FEEDBACK_MIP_REGION_USED_OPAQUE = 190, DXGI_FORMAT_FORCE_UINT = 0xffffffff }; #endif diff --git a/Source/Falcor/Core/API/NvApiExDesc.h b/Source/Falcor/Core/API/NvApiExDesc.h index dd5b82d55..280f6e6d4 100644 --- a/Source/Falcor/Core/API/NvApiExDesc.h +++ b/Source/Falcor/Core/API/NvApiExDesc.h @@ -95,7 +95,7 @@ inline void createNvApiUavSlotExDesc(NvApiPsoExDesc& ret, uint32_t uavSlot) desc.registerSpace = 0; // SM5.1+: If the "space" keyword is omitted, the default space index of 0 is implicitly assigned } -inline std::optional findNvApiShaderRegister(ProgramKernels::SharedConstPtr const& pKernels) +inline std::optional findNvApiShaderRegister(const ref& pKernels) { auto pBlock = pKernels->getReflector()->getDefaultParameterBlock(); auto pVar = pBlock->getResource("g_NvidiaExt"); @@ -116,7 +116,7 @@ inline void createNvApiUavSlotExDesc(NvApiPsoExDesc& ret, uint32_t uavSlot) { FALCOR_UNREACHABLE(); } -inline std::optional findNvApiShaderRegister(ProgramKernels::SharedConstPtr const& pKernels, uint32_t& outRegisterIndex) +inline std::optional findNvApiShaderRegister(const ref& pKernels, uint32_t& outRegisterIndex) { return std::optional(); } diff --git a/Source/Falcor/Core/API/ParameterBlock.cpp b/Source/Falcor/Core/API/ParameterBlock.cpp index 328f51488..59527e675 100644 --- a/Source/Falcor/Core/API/ParameterBlock.cpp +++ b/Source/Falcor/Core/API/ParameterBlock.cpp @@ -56,7 +56,7 @@ gfx::ShaderOffset getGFXShaderOffset(const ParameterBlock::BindLocation& bindLoc return gfxOffset; } -bool isSrvType(const ReflectionType::SharedConstPtr& pType) +bool isSrvType(const ref& pType) { auto resourceType = pType->unwrapArray()->asResourceType(); if (resourceType->getType() == ReflectionResourceType::Type::Sampler || @@ -75,7 +75,7 @@ bool isSrvType(const ReflectionType::SharedConstPtr& pType) } } -bool isUavType(const ReflectionType::SharedConstPtr& pType) +bool isUavType(const ref& pType) { auto resourceType = pType->unwrapArray()->asResourceType(); if (resourceType->getType() == ReflectionResourceType::Type::Sampler || @@ -94,7 +94,7 @@ bool isUavType(const ReflectionType::SharedConstPtr& pType) } } -bool isCbvType(const ReflectionType::SharedConstPtr& pType) +bool isCbvType(const ref& pType) { auto resourceType = pType->unwrapArray()->asResourceType(); if (resourceType->getType() == ReflectionResourceType::Type::ConstantBuffer) @@ -106,10 +106,10 @@ bool isCbvType(const ReflectionType::SharedConstPtr& pType) } } // namespace -ParameterBlock::SharedPtr ParameterBlock::create( - Device* pDevice, - const std::shared_ptr& pProgramVersion, - const ReflectionType::SharedConstPtr& pElementType +ref ParameterBlock::create( + ref pDevice, + const ref& pProgramVersion, + const ref& pElementType ) { if (!pElementType) @@ -118,15 +118,18 @@ ParameterBlock::SharedPtr ParameterBlock::create( return create(pDevice, pReflection); } -ParameterBlock::SharedPtr ParameterBlock::create(Device* pDevice, const ParameterBlockReflection::SharedConstPtr& pReflection) +ref ParameterBlock::create(ref pDevice, const ref& pReflection) { FALCOR_ASSERT(pReflection); - return SharedPtr(new ParameterBlock(pDevice->shared_from_this(), pReflection->getProgramVersion(), pReflection)); + // TODO(@skallweit) we convert the weak pointer to a shared pointer here because we tie + // the lifetime of the parameter block to the lifetime of the program version. + // The ownership for programs/versions/kernels and parameter blocks needs to be revisited. + return ref(new ParameterBlock(pDevice, ref(pReflection->getProgramVersion()), pReflection)); } -ParameterBlock::SharedPtr ParameterBlock::create( - Device* pDevice, - const std::shared_ptr& pProgramVersion, +ref ParameterBlock::create( + ref pDevice, + const ref& pProgramVersion, const std::string& typeName ) { @@ -182,7 +185,7 @@ void ParameterBlock::createConstantBuffers(const ShaderVar& var) { case ReflectionResourceType::Type::ConstantBuffer: { - auto pCB = ParameterBlock::create(mpDevice.get(), pResourceType->getParameterBlockReflector()); + auto pCB = ParameterBlock::create(ref(mpDevice), pResourceType->getParameterBlockReflector()); var.setParameterBlock(pCB); } break; @@ -258,22 +261,22 @@ FALCOR_API bool ParameterBlock::setVariable(UniformShaderVarOffset offset, const ParameterBlock::~ParameterBlock() {} -ParameterBlock::ParameterBlock(std::shared_ptr pDevice, const ProgramReflection::SharedConstPtr& pReflector) - : mpDevice(std::move(pDevice)), mpProgramVersion(pReflector->getProgramVersion()), mpReflector(pReflector->getDefaultParameterBlock()) +ParameterBlock::ParameterBlock(ref pDevice, const ref& pReflector) + : mpDevice(pDevice.get()), mpProgramVersion(pReflector->getProgramVersion()), mpReflector(pReflector->getDefaultParameterBlock()) { FALCOR_GFX_CALL(mpDevice->getGfxDevice()->createMutableRootShaderObject( - pReflector->getProgramVersion()->getKernels(mpDevice.get(), nullptr)->getGfxProgram(), mpShaderObject.writeRef() + pReflector->getProgramVersion()->getKernels(mpDevice, nullptr)->getGfxProgram(), mpShaderObject.writeRef() )); initializeResourceBindings(); createConstantBuffers(getRootVar()); } ParameterBlock::ParameterBlock( - std::shared_ptr pDevice, - const std::shared_ptr& pProgramVersion, - const ParameterBlockReflection::SharedConstPtr& pReflection + ref pDevice, + const ref& pProgramVersion, + const ref& pReflection ) - : mpDevice(std::move(pDevice)), mpProgramVersion(pProgramVersion), mpReflector(pReflection) + : mpDevice(pDevice.get()), mpProgramVersion(pProgramVersion), mpReflector(pReflection) { FALCOR_GFX_CALL(mpDevice->getGfxDevice()->createMutableShaderObjectFromTypeLayout( pReflection->getElementType()->getSlangTypeLayout(), mpShaderObject.writeRef() @@ -327,13 +330,13 @@ bool ParameterBlock::setBlob(const void* pSrc, size_t offset, size_t size) return SLANG_SUCCEEDED(mpShaderObject->setData(gfxOffset, pSrc, size)); } -bool ParameterBlock::setBuffer(const std::string& name, const Buffer::SharedPtr& pBuffer) +bool ParameterBlock::setBuffer(const std::string& name, const ref& pBuffer) { auto var = getRootVar()[name]; return var.setBuffer(pBuffer); } -bool ParameterBlock::setBuffer(const BindLocation& bindLoc, const Buffer::SharedPtr& pResource) +bool ParameterBlock::setBuffer(const BindLocation& bindLoc, const ref& pResource) { gfx::ShaderOffset gfxOffset = getGFXShaderOffset(bindLoc); if (isUavType(bindLoc.getType())) @@ -358,13 +361,13 @@ bool ParameterBlock::setBuffer(const BindLocation& bindLoc, const Buffer::Shared return true; } -Buffer::SharedPtr ParameterBlock::getBuffer(const std::string& name) const +ref ParameterBlock::getBuffer(const std::string& name) const { auto var = getRootVar()[name]; return var.getBuffer(); } -Buffer::SharedPtr ParameterBlock::getBuffer(const BindLocation& bindLoc) const +ref ParameterBlock::getBuffer(const BindLocation& bindLoc) const { gfx::ShaderOffset gfxOffset = getGFXShaderOffset(bindLoc); if (isUavType(bindLoc.getType())) @@ -390,26 +393,26 @@ Buffer::SharedPtr ParameterBlock::getBuffer(const BindLocation& bindLoc) const } } -bool ParameterBlock::setParameterBlock(const std::string& name, const ParameterBlock::SharedPtr& pBlock) +bool ParameterBlock::setParameterBlock(const std::string& name, const ref& pBlock) { auto var = getRootVar()[name]; return var.setParameterBlock(pBlock); } -bool ParameterBlock::setParameterBlock(const BindLocation& bindLocation, const ParameterBlock::SharedPtr& pBlock) +bool ParameterBlock::setParameterBlock(const BindLocation& bindLocation, const ref& pBlock) { auto gfxOffset = getGFXShaderOffset(bindLocation); mParameterBlocks[gfxOffset] = pBlock; return SLANG_SUCCEEDED(mpShaderObject->setObject(gfxOffset, pBlock ? pBlock->mpShaderObject : nullptr)); } -ParameterBlock::SharedPtr ParameterBlock::getParameterBlock(const std::string& name) const +ref ParameterBlock::getParameterBlock(const std::string& name) const { auto var = getRootVar()[name]; return var.getParameterBlock(); } -ParameterBlock::SharedPtr ParameterBlock::getParameterBlock(const BindLocation& bindLocation) const +ref ParameterBlock::getParameterBlock(const BindLocation& bindLocation) const { auto gfxOffset = getGFXShaderOffset(bindLocation); auto iter = mParameterBlocks.find(gfxOffset); @@ -441,22 +444,22 @@ set_constant_by_offset(float2); set_constant_by_offset(float3); set_constant_by_offset(float4); -set_constant_by_offset(rmcv::mat1x4); -set_constant_by_offset(rmcv::mat2x4); -set_constant_by_offset(rmcv::mat3x4); -set_constant_by_offset(rmcv::mat4x4); +set_constant_by_offset(float1x4); +set_constant_by_offset(float2x4); +set_constant_by_offset(float3x4); +set_constant_by_offset(float4x4); set_constant_by_offset(uint64_t); #undef set_constant_by_offset -bool ParameterBlock::setTexture(const std::string& name, const Texture::SharedPtr& pTexture) +bool ParameterBlock::setTexture(const std::string& name, const ref& pTexture) { auto var = getRootVar()[name]; return var.setTexture(pTexture); } -bool ParameterBlock::setTexture(const BindLocation& bindLocation, const Texture::SharedPtr& pTexture) +bool ParameterBlock::setTexture(const BindLocation& bindLocation, const ref& pTexture) { const auto& bindingInfo = mpReflector->getResourceRangeBindingInfo(bindLocation.getResourceRangeIndex()); gfx::ShaderOffset gfxOffset = getGFXShaderOffset(bindLocation); @@ -482,13 +485,13 @@ bool ParameterBlock::setTexture(const BindLocation& bindLocation, const Texture: return true; } -Texture::SharedPtr ParameterBlock::getTexture(const std::string& name) const +ref ParameterBlock::getTexture(const std::string& name) const { auto var = getRootVar()[name]; return var.getTexture(); } -Texture::SharedPtr ParameterBlock::getTexture(const BindLocation& bindLocation) const +ref ParameterBlock::getTexture(const BindLocation& bindLocation) const { gfx::ShaderOffset gfxOffset = getGFXShaderOffset(bindLocation); if (isUavType(bindLocation.getType())) @@ -514,14 +517,15 @@ Texture::SharedPtr ParameterBlock::getTexture(const BindLocation& bindLocation) } } -bool ParameterBlock::setSrv(const BindLocation& bindLocation, const ShaderResourceView::SharedPtr& pSrv) +bool ParameterBlock::setSrv(const BindLocation& bindLocation, const ref& pSrv) { gfx::ShaderOffset gfxOffset = getGFXShaderOffset(bindLocation); if (isSrvType(bindLocation.getType())) { mpShaderObject->setResource(gfxOffset, pSrv ? pSrv->getGfxResourceView() : nullptr); mSRVs[gfxOffset] = pSrv; - mResources[gfxOffset] = pSrv ? pSrv->getResource() : nullptr; + // Note: The resource view does not hold a strong reference to the resource, so we need to keep it alive here. + mResources[gfxOffset] = ref(pSrv ? pSrv->getResource() : nullptr); } else { @@ -531,14 +535,15 @@ bool ParameterBlock::setSrv(const BindLocation& bindLocation, const ShaderResour return true; } -bool ParameterBlock::setUav(const BindLocation& bindLocation, const UnorderedAccessView::SharedPtr& pUav) +bool ParameterBlock::setUav(const BindLocation& bindLocation, const ref& pUav) { gfx::ShaderOffset gfxOffset = getGFXShaderOffset(bindLocation); if (isUavType(bindLocation.getType())) { mpShaderObject->setResource(gfxOffset, pUav ? pUav->getGfxResourceView() : nullptr); mUAVs[gfxOffset] = pUav; - mResources[gfxOffset] = pUav ? pUav->getResource() : nullptr; + // Note: The resource view does not hold a strong reference to the resource, so we need to keep it alive here. + mResources[gfxOffset] = ref(pUav ? pUav->getResource() : nullptr); } else { @@ -548,14 +553,14 @@ bool ParameterBlock::setUav(const BindLocation& bindLocation, const UnorderedAcc return true; } -bool ParameterBlock::setAccelerationStructure(const BindLocation& bindLocation, const RtAccelerationStructure::SharedPtr& pAccl) +bool ParameterBlock::setAccelerationStructure(const BindLocation& bindLocation, const ref& pAccl) { gfx::ShaderOffset gfxOffset = getGFXShaderOffset(bindLocation); mAccelerationStructures[gfxOffset] = pAccl; return SLANG_SUCCEEDED(mpShaderObject->setResource(gfxOffset, pAccl ? pAccl->getGfxAccelerationStructure() : nullptr)); } -ShaderResourceView::SharedPtr ParameterBlock::getSrv(const BindLocation& bindLocation) const +ref ParameterBlock::getSrv(const BindLocation& bindLocation) const { gfx::ShaderOffset gfxOffset = getGFXShaderOffset(bindLocation); auto iter = mSRVs.find(gfxOffset); @@ -564,7 +569,7 @@ ShaderResourceView::SharedPtr ParameterBlock::getSrv(const BindLocation& bindLoc return iter->second; } -UnorderedAccessView::SharedPtr ParameterBlock::getUav(const BindLocation& bindLocation) const +ref ParameterBlock::getUav(const BindLocation& bindLocation) const { gfx::ShaderOffset gfxOffset = getGFXShaderOffset(bindLocation); auto iter = mUAVs.find(gfxOffset); @@ -573,7 +578,7 @@ UnorderedAccessView::SharedPtr ParameterBlock::getUav(const BindLocation& bindLo return iter->second; } -RtAccelerationStructure::SharedPtr ParameterBlock::getAccelerationStructure(const BindLocation& bindLocation) const +ref ParameterBlock::getAccelerationStructure(const BindLocation& bindLocation) const { gfx::ShaderOffset gfxOffset = getGFXShaderOffset(bindLocation); auto iter = mAccelerationStructures.find(gfxOffset); @@ -582,34 +587,30 @@ RtAccelerationStructure::SharedPtr ParameterBlock::getAccelerationStructure(cons return iter->second; } -bool ParameterBlock::setSampler(const std::string& name, const Sampler::SharedPtr& pSampler) +bool ParameterBlock::setSampler(const std::string& name, const ref& pSampler) { auto var = getRootVar()[name]; return var.setSampler(pSampler); } -bool ParameterBlock::setSampler(const BindLocation& bindLocation, const Sampler::SharedPtr& pSampler) +bool ParameterBlock::setSampler(const BindLocation& bindLocation, const ref& pSampler) { gfx::ShaderOffset gfxOffset = getGFXShaderOffset(bindLocation); - auto pBoundSampler = pSampler ? pSampler : mpDevice->getDefaultSampler(); + const ref& pBoundSampler = pSampler ? pSampler : mpDevice->getDefaultSampler(); mSamplers[gfxOffset] = pBoundSampler; return SLANG_SUCCEEDED(mpShaderObject->setSampler(gfxOffset, pBoundSampler->getGfxSamplerState())); } -const Sampler::SharedPtr& ParameterBlock::getSampler(const BindLocation& bindLocation) const +ref ParameterBlock::getSampler(const BindLocation& bindLocation) const { - static const Sampler::SharedPtr pNull; - gfx::ShaderOffset gfxOffset = getGFXShaderOffset(bindLocation); auto iter = mSamplers.find(gfxOffset); if (iter == mSamplers.end()) - { - return pNull; - } + return nullptr; return iter->second; } -Sampler::SharedPtr ParameterBlock::getSampler(const std::string& name) const +ref ParameterBlock::getSampler(const std::string& name) const { auto var = getRootVar()[name]; return var.getSampler(); @@ -630,11 +631,11 @@ bool ParameterBlock::prepareDescriptorSets(CopyContext* pCopyContext) // Insert necessary resource barriers for bound resources. for (auto& srv : mSRVs) { - prepareResource(pCopyContext, srv.second ? srv.second->getResource().get() : nullptr, false); + prepareResource(pCopyContext, srv.second ? srv.second->getResource() : nullptr, false); } for (auto& uav : mUAVs) { - prepareResource(pCopyContext, uav.second ? uav.second->getResource().get() : nullptr, true); + prepareResource(pCopyContext, uav.second ? uav.second->getResource() : nullptr, true); } for (auto& subObj : this->mParameterBlocks) { @@ -643,9 +644,9 @@ bool ParameterBlock::prepareDescriptorSets(CopyContext* pCopyContext) return true; } -const ParameterBlock::SharedPtr& ParameterBlock::getParameterBlock(uint32_t resourceRangeIndex, uint32_t arrayIndex) const +const ref& ParameterBlock::getParameterBlock(uint32_t resourceRangeIndex, uint32_t arrayIndex) const { - static const ParameterBlock::SharedPtr pNull; + static const ref pNull; gfx::ShaderOffset gfxOffset = {}; gfxOffset.bindingRangeIndex = resourceRangeIndex; @@ -660,30 +661,14 @@ const ParameterBlock::SharedPtr& ParameterBlock::getParameterBlock(uint32_t reso void ParameterBlock::collectSpecializationArgs(SpecializationArgs& ioArgs) const {} -void ParameterBlock::markUniformDataDirty() const -{ - throw "unimplemented"; -} - void const* ParameterBlock::getRawData() const { return mpShaderObject->getRawData(); } -const Buffer::SharedPtr& ParameterBlock::getUnderlyingConstantBuffer() const +ref ParameterBlock::getUnderlyingConstantBuffer() const { throw "unimplemented"; } -#if FALCOR_HAS_CUDA -void* ParameterBlock::getCUDAHostBuffer(size_t& outSize) -{ - return nullptr; -} - -void* ParameterBlock::getCUDADeviceBuffer(size_t& outSize) -{ - return nullptr; -} -#endif } // namespace Falcor diff --git a/Source/Falcor/Core/API/ParameterBlock.h b/Source/Falcor/Core/API/ParameterBlock.h index 49e12a6d3..0c7f7481e 100644 --- a/Source/Falcor/Core/API/ParameterBlock.h +++ b/Source/Falcor/Core/API/ParameterBlock.h @@ -32,6 +32,7 @@ #include "Sampler.h" #include "RtAccelerationStructure.h" #include "Core/Macros.h" +#include "Core/Object.h" #include "Core/Program/ProgramReflection.h" #include "Core/Program/ShaderVar.h" #include "Utils/UI/Gui.h" @@ -50,62 +51,12 @@ namespace Falcor class ProgramVersion; class CopyContext; -/** - * Shared pointer class for `ParameterBlock` and derived classes. - * This smart pointer type adds syntax sugar for `operator[]` so that it implicitly operates on a `ShaderVar` derived from the contents of - * the buffer. - */ -template -class ParameterBlockSharedPtr : public std::shared_ptr -{ -public: - ParameterBlockSharedPtr() : std::shared_ptr() {} - explicit ParameterBlockSharedPtr(T* pObject) : std::shared_ptr(pObject) {} - constexpr ParameterBlockSharedPtr(std::nullptr_t) : std::shared_ptr(nullptr) {} - ParameterBlockSharedPtr(const std::shared_ptr& pObject) : std::shared_ptr(pObject) {} - - /** - * Implicitly convert a `ShaderVar` to a `ParameterBlock` pointer. - */ - ParameterBlockSharedPtr(const ShaderVar& var) : std::shared_ptr(var.getParameterBlock()) {} - - /** - * Get a shader variable that points to the root/contents of the parameter block. - */ - ShaderVar getRootVar() const { return std::shared_ptr::get()->getRootVar(); } - - /** - * Get a shader variable that points at the field with the given `name`. - * This is an alias for `getRootVar()[name]`. - */ - ShaderVar operator[](const std::string& name) const { return getRootVar()[name]; } - - /** - * Get a shader variable that points at the field with the given `name`. - * This is an alias for `getRootVar()[name]`. - */ - ShaderVar operator[](const char* name) const { return getRootVar()[name]; } - - /** - * Get a shader variable that points at the field/element with the given `index`. - * This is an alias for `getRootVar()[index]`. - */ - ShaderVar operator[](size_t index) const { return getRootVar()[index]; } - - /** - * Get a shader variable that points at the field/element with the given `offset`. - * This is an alias for `getRootVar()[offset]`. - */ - ShaderVar operator[](UniformShaderVarOffset offset) const { return getRootVar()[offset]; } -}; - /** * A parameter block. This block stores all the parameter data associated with a specific type in shader code */ -class FALCOR_API ParameterBlock +class FALCOR_API ParameterBlock : public Object { public: - using SharedPtr = ParameterBlockSharedPtr; ~ParameterBlock(); using BindLocation = ParameterBlockReflection::BindLocation; @@ -113,23 +64,23 @@ class FALCOR_API ParameterBlock /** * Create a new object that holds a value of the given type. */ - static SharedPtr create( - Device* pDevice, - const std::shared_ptr& pProgramVersion, - const ReflectionType::SharedConstPtr& pType + static ref create( + ref pDevice, + const ref& pProgramVersion, + const ref& pType ); /** * Create a new object that holds a value described by the given reflector. */ - static SharedPtr create(Device* pDevice, const ParameterBlockReflection::SharedConstPtr& pReflection); + static ref create(ref pDevice, const ref& pReflection); /** * Create a new object that holds a value of the type with the given name in the given program. * @param[in] pProgramVersion Program version object. * @param[in] typeName Name of the type. If the type does not exist an exception is thrown. */ - static SharedPtr create(Device* pDevice, const std::shared_ptr& pProgramVersion, const std::string& typeName); + static ref create(ref pDevice, const ref& pProgramVersion, const std::string& typeName); gfx::IShaderObject* getShaderObject() const { return mpShaderObject.get(); } @@ -175,7 +126,7 @@ class FALCOR_API ParameterBlock * @param[in] pBuffer The buffer object * @return false is the call failed, otherwise true */ - bool setBuffer(const std::string& name, const Buffer::SharedPtr& pBuffer); + bool setBuffer(const std::string& name, const ref& pBuffer); /** * Bind a buffer object by index @@ -185,21 +136,21 @@ class FALCOR_API ParameterBlock * @param[in] pBuffer The buffer object * @return false is the call failed, otherwise true */ - bool setBuffer(const BindLocation& bindLocation, const Buffer::SharedPtr& pBuffer); + bool setBuffer(const BindLocation& bindLocation, const ref& pBuffer); /** * Get a buffer * @param[in] name The name of the buffer * @return If the name is valid, a shared pointer to the buffer. Otherwise returns nullptr */ - Buffer::SharedPtr getBuffer(const std::string& name) const; + ref getBuffer(const std::string& name) const; /** * Get a buffer * @param[in] bindLocation The bind location of the buffer * @return If the name is valid, a shared pointer to the buffer. Otherwise returns nullptr */ - Buffer::SharedPtr getBuffer(const BindLocation& bindLocation) const; + ref getBuffer(const BindLocation& bindLocation) const; /** * Bind a parameter block by name. @@ -208,7 +159,7 @@ class FALCOR_API ParameterBlock * @param[in] pBlock The parameter block * @return false is the call failed, otherwise true */ - bool setParameterBlock(const std::string& name, const ParameterBlock::SharedPtr& pBlock); + bool setParameterBlock(const std::string& name, const ref& pBlock); /** * Bind a parameter block by index. @@ -218,51 +169,51 @@ class FALCOR_API ParameterBlock * @param[in] pBlock The parameter block * @return false is the call failed, otherwise true */ - bool setParameterBlock(const BindLocation& bindLocation, const ParameterBlock::SharedPtr& pBlock); + bool setParameterBlock(const BindLocation& bindLocation, const ref& pBlock); /** * Get a parameter block. * @param[in] name The name of the parameter block * @return If the name is valid, a shared pointer to the parameter block. Otherwise returns nullptr */ - ParameterBlock::SharedPtr getParameterBlock(const std::string& name) const; + ref getParameterBlock(const std::string& name) const; /** * Get a parameter block. * @param[in] bindLocation The location of the block * @return If the indices is valid, a shared pointer to the parameter block. Otherwise returns nullptr */ - ParameterBlock::SharedPtr getParameterBlock(const BindLocation& bindLocation) const; + ref getParameterBlock(const BindLocation& bindLocation) const; /** * Bind a texture. Based on the shader reflection, it will be bound as either an SRV or a UAV * @param[in] name The name of the texture object in the shader * @param[in] pTexture The texture object to bind */ - bool setTexture(const std::string& name, const Texture::SharedPtr& pTexture); - bool setTexture(const BindLocation& bindLocation, const Texture::SharedPtr& pTexture); + bool setTexture(const std::string& name, const ref& pTexture); + bool setTexture(const BindLocation& bindLocation, const ref& pTexture); /** * Get a texture object. * @param[in] name The name of the texture * @return If the name is valid, a shared pointer to the texture object. Otherwise returns nullptr */ - Texture::SharedPtr getTexture(const std::string& name) const; - Texture::SharedPtr getTexture(const BindLocation& bindLocation) const; + ref getTexture(const std::string& name) const; + ref getTexture(const BindLocation& bindLocation) const; /** * Bind an SRV. * @param[in] bindLocation The bind-location in the block * @param[in] pSrv The shader-resource-view object to bind */ - bool setSrv(const BindLocation& bindLocation, const ShaderResourceView::SharedPtr& pSrv); + bool setSrv(const BindLocation& bindLocation, const ref& pSrv); /** * Bind a UAV. * @param[in] bindLocation The bind-location in the block * @param[in] pSrv The unordered-access-view object to bind */ - bool setUav(const BindLocation& bindLocation, const UnorderedAccessView::SharedPtr& pUav); + bool setUav(const BindLocation& bindLocation, const ref& pUav); /** * Bind an acceleration structure. @@ -270,28 +221,28 @@ class FALCOR_API ParameterBlock * @param[in] pAccl The acceleration structure object to bind * @return false if the binding location does not accept an acceleration structure, true otherwise. */ - bool setAccelerationStructure(const BindLocation& bindLocation, const RtAccelerationStructure::SharedPtr& pAccl); + bool setAccelerationStructure(const BindLocation& bindLocation, const ref& pAccl); /** * Get an SRV object. * @param[in] bindLocation The bind-location in the block * @return If the bind-location is valid, a shared pointer to the SRV. Otherwise returns nullptr */ - ShaderResourceView::SharedPtr getSrv(const BindLocation& bindLocation) const; + ref getSrv(const BindLocation& bindLocation) const; /** * Get a UAV object * @param[in] bindLocation The bind-location in the block * @return If the bind-location is valid, a shared pointer to the UAV. Otherwise returns nullptr */ - UnorderedAccessView::SharedPtr getUav(const BindLocation& bindLocation) const; + ref getUav(const BindLocation& bindLocation) const; /** * Get an acceleration structure object. * @param[in] bindLocation The bind-location in the block * @return If the bind-location is valid, a shared pointer to the acceleration structure. Otherwise returns nullptr */ - RtAccelerationStructure::SharedPtr getAccelerationStructure(const BindLocation& bindLocation) const; + ref getAccelerationStructure(const BindLocation& bindLocation) const; /** * Bind a sampler to the program in the global namespace. @@ -299,7 +250,7 @@ class FALCOR_API ParameterBlock * @param[in] pSampler The sampler object to bind * @return false if the sampler was not found in the program, otherwise true */ - bool setSampler(const std::string& name, const Sampler::SharedPtr& pSampler); + bool setSampler(const std::string& name, const ref& pSampler); /** * Bind a sampler to the program in the global namespace. @@ -307,30 +258,30 @@ class FALCOR_API ParameterBlock * @param[in] pSampler The sampler object to bind * @return false if the sampler was not found in the program, otherwise true */ - bool setSampler(const BindLocation& bindLocation, const Sampler::SharedPtr& pSampler); + bool setSampler(const BindLocation& bindLocation, const ref& pSampler); /** * Gets a sampler object. * @param[in] bindLocation The bind-location in the block * @return If the bind-location is valid, a shared pointer to the sampler. Otherwise returns nullptr */ - const Sampler::SharedPtr& getSampler(const BindLocation& bindLocation) const; + ref getSampler(const BindLocation& bindLocation) const; /** * Gets a sampler object. * @return If the name is valid, a shared pointer to the sampler. Otherwise returns nullptr */ - Sampler::SharedPtr getSampler(const std::string& name) const; + ref getSampler(const std::string& name) const; /** * Get the parameter block's reflection interface */ - ParameterBlockReflection::SharedConstPtr getReflection() const { return mpReflector; } + ref getReflection() const { return mpReflector; } /** * Get the block reflection type */ - ReflectionType::SharedConstPtr getElementType() const { return mpReflector->getElementType(); } + ref getElementType() const { return mpReflector->getElementType(); } /** * Get the size of the reflection type @@ -367,21 +318,19 @@ class FALCOR_API ParameterBlock size_t getSize() const; bool updateSpecialization() const; - ParameterBlockReflection::SharedConstPtr getSpecializedReflector() const { return mpSpecializedReflector; } + ref getSpecializedReflector() const { return mpSpecializedReflector; } bool prepareDescriptorSets(CopyContext* pCopyContext); - const ParameterBlock::SharedPtr& getParameterBlock(uint32_t resourceRangeIndex, uint32_t arrayIndex) const; + const ref& getParameterBlock(uint32_t resourceRangeIndex, uint32_t arrayIndex) const; // 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) = delete; - bool setSampler(uint32_t, const Sampler::SharedPtr&) = delete; + ref getSampler(uint32_t) = delete; + bool setSampler(uint32_t, ref) = delete; using SpecializationArgs = std::vector; void collectSpecializationArgs(SpecializationArgs& ioArgs) const; - void markUniformDataDirty() const; - void const* getRawData() const; /** @@ -389,123 +338,37 @@ class FALCOR_API ParameterBlock * Be cautious with the returned buffer as it can be invalidated any time you set/bind something * to the parameter block (or one if its internal sub-blocks). */ - const Buffer::SharedPtr& getUnderlyingConstantBuffer() const; - -#if FALCOR_HAS_CUDA - /** - * Get a host-memory pointer that represents the contents of this shader object - * as a CUDA-compatible buffer. - * - * In the case where this parameter block represents a `ProgramVars`, the resulting - * buffer can be passed as argument data for a kernel launch. - * - * @return Host-memory pointer to a copy of the block, or throws on error - * (e.g., parameter types that are unsupported on CUDA). - * - * The lifetime of the returned pointer is tied to the `ParameterBlock`, - * and does not need to be explicitly deleted by the caller. The pointer - * may become invalid if: - * - * * The parameter block is deleted - * * A call to `getCUDADeviceBuffer()` is made on the same parameter block - * * Another call it made to `getCUDAHostBuffer()` after changes have been made to parameters in the block - */ - void* getCUDAHostBuffer(size_t& outSize); - - /** - * Get a device-memory pointer that represents the contents of this shader object - * as a CUDA-compatible buffer. - * - * The resulting buffer can be used to represent this shader object when it - * is used as a constant buffer or parameter block. - * - * @return Device-memory pointer to a copy of the block, or throws on error - * (e.g., parameter types that are unsupported on CUDA). - * - * The lifetime of the returned pointer is tied to the `ParameterBlock`, - * and does not need to be explicitly deleted by the caller. The pointer - * may become invalid if: - * - * * The parameter block is deleted - * * A call to `getCUDAHostBuffer()` is made on the same parameter block - * * Another call it made to `getCUDADeviceBuffer()` after changes have been made to parameters in the block - */ - void* getCUDADeviceBuffer(size_t& outSize); -#endif - - typedef uint64_t ChangeEpoch; + ref getUnderlyingConstantBuffer() const; protected: ParameterBlock( - std::shared_ptr pDevice, - const std::shared_ptr& pProgramVersion, - const ParameterBlockReflection::SharedConstPtr& pReflection + ref pDevice, + const ref& pProgramVersion, + const ref& pReflection ); - ParameterBlock(std::shared_ptr pDevice, const ProgramReflection::SharedConstPtr& pReflector); + ParameterBlock(ref pDevice, const ref& pReflector); void initializeResourceBindings(); void createConstantBuffers(const ShaderVar& var); static void prepareResource(CopyContext* pContext, Resource* pResource, bool isUav); - std::shared_ptr mpDevice; + /// Note: We hold an unowned pointer to the device but a strong pointer to the program version. + /// We tie the lifetime of the program version to the lifetime of the parameter block. + /// This is because the program version holds the reflection data for the parameter block. - std::shared_ptr mpProgramVersion; - ParameterBlockReflection::SharedConstPtr mpReflector; - mutable ParameterBlockReflection::SharedConstPtr mpSpecializedReflector; + Device* mpDevice; + ref mpProgramVersion; + ref mpReflector; + mutable ref mpSpecializedReflector; Slang::ComPtr mpShaderObject; - std::map mParameterBlocks; - std::map mSRVs; - std::map mUAVs; - std::map mResources; - std::map mSamplers; - std::map mAccelerationStructures; - -#if FALCOR_HAS_CUDA - - // The following members pertain to the issue of exposing the - // current state/contents of a shader object to CUDA kernels. - - /** - * A kind of data buffer used for communicating with CUDA. - */ - enum class CUDABufferKind - { - Host, ///< A buffer in host memory - Device, ///< A buffer in device memory - }; - - /** - * Get a CUDA-compatible buffer that represents the contents of this shader object. - */ - void* getCUDABuffer(CUDABufferKind bufferKind, size_t& outSize); - - /** - * Get a CUDA-compatible buffer that represents the contents of this shader object. - */ - void* getCUDABuffer(const ParameterBlockReflection* pReflector, CUDABufferKind bufferKind, size_t& outSize); - - /** - * Update the CUDA-compatible buffer stored on this parameter block to reflect - * the current state of the shader object. - */ - void updateCUDABuffer(const ParameterBlockReflection* pReflector, CUDABufferKind bufferKind); - - /** - * Information about the CUDA buffer (if any) used to represnet the state of - * this shader object - */ - struct UnderlyingCUDABuffer - { - Buffer::SharedPtr pBuffer; - void* pData = nullptr; - ChangeEpoch epochOfLastObservedChange = 0; - size_t size = 0; - CUDABufferKind kind = CUDABufferKind::Host; - }; - UnderlyingCUDABuffer mUnderlyingCUDABuffer; -#endif + std::map> mParameterBlocks; + std::map> mSRVs; + std::map> mUAVs; + std::map> mResources; + std::map> mSamplers; + std::map> mAccelerationStructures; }; template diff --git a/Source/Falcor/Core/API/QueryHeap.cpp b/Source/Falcor/Core/API/QueryHeap.cpp index 809d4db42..6388ee5c1 100644 --- a/Source/Falcor/Core/API/QueryHeap.cpp +++ b/Source/Falcor/Core/API/QueryHeap.cpp @@ -32,12 +32,12 @@ namespace Falcor { -QueryHeap::SharedPtr QueryHeap::create(Device* pDevice, Type type, uint32_t count) +ref QueryHeap::create(ref pDevice, Type type, uint32_t count) { - return SharedPtr(new QueryHeap(pDevice->shared_from_this(), type, count)); + return ref(new QueryHeap(pDevice, type, count)); } -QueryHeap::QueryHeap(std::shared_ptr pDevice, Type type, uint32_t count) : mCount(count), mType(type) +QueryHeap::QueryHeap(ref pDevice, Type type, uint32_t count) : mpDevice(pDevice), mCount(count), mType(type) { FALCOR_ASSERT(pDevice); gfx::IQueryPool::Desc desc = {}; @@ -53,4 +53,10 @@ QueryHeap::QueryHeap(std::shared_ptr pDevice, Type type, uint32_t count) } FALCOR_GFX_CALL(pDevice->getGfxDevice()->createQueryPool(desc, mGfxQueryPool.writeRef())); } + +void QueryHeap::breakStrongReferenceToDevice() +{ + mpDevice.breakStrongReference(); +} + } // namespace Falcor diff --git a/Source/Falcor/Core/API/QueryHeap.h b/Source/Falcor/Core/API/QueryHeap.h index d6efdf07f..3fd0ec6a0 100644 --- a/Source/Falcor/Core/API/QueryHeap.h +++ b/Source/Falcor/Core/API/QueryHeap.h @@ -30,16 +30,14 @@ #include "Handles.h" #include "Core/Assert.h" #include "Core/Macros.h" +#include "Core/Object.h" #include -#include namespace Falcor { -class FALCOR_API QueryHeap +class FALCOR_API QueryHeap : public Object { public: - using SharedPtr = std::shared_ptr; - enum class Type { Timestamp, @@ -47,7 +45,7 @@ class FALCOR_API QueryHeap PipelineStats }; - static const uint32_t kInvalidIndex = 0xffffffff; + static constexpr uint32_t kInvalidIndex = 0xffffffff; /** * Create a new query heap. @@ -55,7 +53,7 @@ class FALCOR_API QueryHeap * @param[in] count Number of queries. * @return New object, or throws an exception if creation failed. */ - static SharedPtr create(Device* pDevice, Type type, uint32_t count); + static ref create(ref pDevice, Type type, uint32_t count); gfx::IQueryPool* getGfxQueryPool() const { return mGfxQueryPool; } uint32_t getQueryCount() const { return mCount; } @@ -85,9 +83,12 @@ class FALCOR_API QueryHeap mFreeQueries.push_back(entry); } + void breakStrongReferenceToDevice(); + private: - QueryHeap(std::shared_ptr pDevice, Type type, uint32_t count); + QueryHeap(ref pDevice, Type type, uint32_t count); + BreakableReference mpDevice; Slang::ComPtr mGfxQueryPool; uint32_t mCount = 0; uint32_t mCurrentObject = 0; diff --git a/Source/Falcor/Core/API/RasterizerState.cpp b/Source/Falcor/Core/API/RasterizerState.cpp index f942a6a18..d600e479b 100644 --- a/Source/Falcor/Core/API/RasterizerState.cpp +++ b/Source/Falcor/Core/API/RasterizerState.cpp @@ -27,20 +27,21 @@ **************************************************************************/ #include "RasterizerState.h" #include "GFXAPI.h" +#include "Core/ObjectPython.h" #include "Utils/Scripting/ScriptBindings.h" namespace Falcor { -RasterizerState::SharedPtr RasterizerState::create(const Desc& desc) +ref RasterizerState::create(const Desc& desc) { - return SharedPtr(new RasterizerState(desc)); + return ref(new RasterizerState(desc)); } RasterizerState::~RasterizerState() = default; FALCOR_SCRIPT_BINDING(RasterizerState) { - pybind11::class_(m, "RasterizerState"); + pybind11::class_>(m, "RasterizerState"); pybind11::enum_ cullMode(m, "CullMode"); cullMode.value("CullBack", RasterizerState::CullMode::Back); diff --git a/Source/Falcor/Core/API/RasterizerState.h b/Source/Falcor/Core/API/RasterizerState.h index 18e94c0c6..588363315 100644 --- a/Source/Falcor/Core/API/RasterizerState.h +++ b/Source/Falcor/Core/API/RasterizerState.h @@ -28,18 +28,16 @@ #pragma once #include "Handles.h" #include "Core/Macros.h" -#include +#include "Core/Object.h" namespace Falcor { /** * Rasterizer state */ -class FALCOR_API RasterizerState +class FALCOR_API RasterizerState : public Object { public: - using SharedPtr = std::shared_ptr; - /** * Cull mode */ @@ -179,7 +177,7 @@ class FALCOR_API RasterizerState * @param[in] desc Rasterizer state descriptor. * @return A new object, or throws an exception if creation failed. */ - static SharedPtr create(const Desc& desc); + static ref create(const Desc& desc); /** * Get the cull mode diff --git a/Source/Falcor/Core/API/RenderContext.cpp b/Source/Falcor/Core/API/RenderContext.cpp index f3f975a6e..131565fe4 100644 --- a/Source/Falcor/Core/API/RenderContext.cpp +++ b/Source/Falcor/Core/API/RenderContext.cpp @@ -34,8 +34,9 @@ #include "GFXAPI.h" #include "Core/State/GraphicsState.h" #include "Core/Program/ProgramVars.h" +#include "Core/Pass/FullScreenPass.h" #include "Utils/Logger.h" -#include "RenderGraph/BasePasses/FullScreenPass.h" +#include "Utils/Scripting/ScriptBindings.h" #include // for offsetof namespace Falcor @@ -105,74 +106,6 @@ gfx::PrimitiveTopology getGFXPrimitiveTopology(Vao::Topology topology) } } -gfx::IRenderCommandEncoder* drawCallCommon(RenderContext* pContext, GraphicsState* pState, GraphicsVars* pVars) -{ - static GraphicsStateObject* spLastGso = nullptr; // TODO: REMOVEGLOBAL - - // Insert barriers for bound resources. - pVars->prepareDescriptorSets(pContext); - - // Insert barriers for render targets. - ensureFboAttachmentResourceStates(pContext, pState->getFbo().get()); - - // Insert barriers for vertex/index buffers. - auto pGso = pState->getGSO(pVars).get(); - if (pGso != spLastGso) - { - auto pVao = pState->getVao().get(); - for (uint32_t i = 0; i < pVao->getVertexBuffersCount(); i++) - { - auto vertexBuffer = pVao->getVertexBuffer(i).get(); - pContext->resourceBarrier(vertexBuffer, Resource::State::VertexBuffer); - } - if (pVao->getIndexBuffer()) - { - auto indexBuffer = pVao->getIndexBuffer().get(); - pContext->resourceBarrier(indexBuffer, Resource::State::IndexBuffer); - } - } - - bool isNewEncoder = false; - auto encoder = pContext->getLowLevelData()->getRenderCommandEncoder( - pGso->getGFXRenderPassLayout(), pState->getFbo() ? pState->getFbo()->getGfxFramebuffer() : nullptr, isNewEncoder - ); - - FALCOR_GFX_CALL(encoder->bindPipelineWithRootObject(pGso->getGfxPipelineState(), pVars->getShaderObject())); - - if (isNewEncoder || pGso != spLastGso) - { - spLastGso = pGso; - auto pVao = pState->getVao().get(); - auto pVertexLayout = pVao->getVertexLayout().get(); - for (uint32_t i = 0; i < pVao->getVertexBuffersCount(); i++) - { - auto bufferLayout = pVertexLayout->getBufferLayout(i); - auto vertexBuffer = pVao->getVertexBuffer(i).get(); - encoder->setVertexBuffer( - i, pVao->getVertexBuffer(i)->getGfxBufferResource(), - bufferLayout->getElementOffset(0) + (uint32_t)vertexBuffer->getGpuAddressOffset() - ); - } - if (pVao->getIndexBuffer()) - { - auto indexBuffer = pVao->getIndexBuffer().get(); - encoder->setIndexBuffer( - indexBuffer->getGfxBufferResource(), getGFXFormat(pVao->getIndexBufferFormat()), - (uint32_t)indexBuffer->getGpuAddressOffset() - ); - } - encoder->setPrimitiveTopology(getGFXPrimitiveTopology(pVao->getPrimitiveTopology())); - encoder->setViewports( - (uint32_t)pState->getViewports().size(), reinterpret_cast(pState->getViewports().data()) - ); - encoder->setScissorRects( - (uint32_t)pState->getScissors().size(), reinterpret_cast(pState->getScissors().data()) - ); - } - - return encoder; -} - gfx::AccelerationStructureCopyMode getGFXAcclerationStructureCopyMode(RenderContext::RtAccelerationStructureCopyMode mode) { switch (mode) @@ -270,8 +203,8 @@ void RenderContext::flush(bool wait) } void RenderContext::blit( - const ShaderResourceView::SharedPtr& pSrc, - const RenderTargetView::SharedPtr& pDst, + const ref& pSrc, + const ref& pDst, uint4 srcRect, uint4 dstRect, Sampler::Filter filter @@ -287,8 +220,8 @@ void RenderContext::blit( } void RenderContext::blit( - const ShaderResourceView::SharedPtr& pSrc, - const RenderTargetView::SharedPtr& pDst, + const ref& pSrc, + const ref& pDst, uint4 srcRect, uint4 dstRect, Sampler::Filter filter, @@ -303,13 +236,14 @@ void RenderContext::blit( FALCOR_ASSERT(pSrc && pDst); auto pSrcResource = pSrc->getResource(); auto pDstResource = pDst->getResource(); + FALCOR_ASSERT(pSrcResource && pDstResource); if (pSrcResource->getType() == Resource::Type::Buffer || pDstResource->getType() == Resource::Type::Buffer) { throw ArgumentError("RenderContext::blit does not support buffers"); } - const Texture* pSrcTexture = dynamic_cast(pSrcResource.get()); - const Texture* pDstTexture = dynamic_cast(pDstResource.get()); + const Texture* pSrcTexture = dynamic_cast(pSrcResource); + const Texture* pDstTexture = dynamic_cast(pDstResource); FALCOR_ASSERT(pSrcTexture != nullptr && pDstTexture != nullptr); // Clamp rectangles to the dimensions of the source/dest views. @@ -334,8 +268,8 @@ void RenderContext::blit( const bool complexBlit = !((componentsReduction[0] == Sampler::ReductionMode::Standard) && (componentsReduction[1] == Sampler::ReductionMode::Standard) && (componentsReduction[2] == Sampler::ReductionMode::Standard) && (componentsReduction[3] == Sampler::ReductionMode::Standard) && - (componentsTransform[0] == float4(1.0f, 0.0f, 0.0f, 0.0f)) && (componentsTransform[1] == float4(0.0f, 1.0f, 0.0f, 0.0f)) && - (componentsTransform[2] == float4(0.0f, 0.0f, 1.0f, 0.0f)) && (componentsTransform[3] == float4(0.0f, 0.0f, 0.0f, 1.0f))); + all(componentsTransform[0] == float4(1.0f, 0.0f, 0.0f, 0.0f)) && all(componentsTransform[1] == float4(0.0f, 1.0f, 0.0f, 0.0f)) && + all(componentsTransform[2] == float4(0.0f, 0.0f, 1.0f, 0.0f)) && all(componentsTransform[3] == float4(0.0f, 0.0f, 0.0f, 1.0f))); auto isFullView = [](const auto& view, const Texture* tex) { @@ -353,7 +287,7 @@ void RenderContext::blit( // the source/dest must have identical size/format/etc. and the views and rects must cover the full resources. if (fullCopy) { - copyResource(pDstResource.get(), pSrcResource.get()); + copyResource(pDstResource, pSrcResource); return; } @@ -391,7 +325,7 @@ void RenderContext::blit( { FALCOR_ASSERT(sampleCount <= 1); - Sampler::SharedPtr usedSampler[4]; + ref usedSampler[4]; for (uint32_t i = 0; i < 4; i++) { FALCOR_ASSERT(componentsReduction[i] != Sampler::ReductionMode::Comparison); // Comparison mode not supported. @@ -412,7 +346,7 @@ void RenderContext::blit( // Parameters for complex blit for (uint32_t i = 0; i < 4; i++) { - if (blitCtx.prevComponentsTransform[i] != componentsTransform[i]) + if (any(blitCtx.prevComponentsTransform[i] != componentsTransform[i])) { blitCtx.pBlitParamsBuffer->setVariable(blitCtx.compTransVarOffset[i], componentsTransform[i]); blitCtx.prevComponentsTransform[i] = componentsTransform[i]; @@ -443,19 +377,19 @@ void RenderContext::blit( } // Update buffer/state - if (srcRectOffset != blitCtx.prevSrcRectOffset) + if (any(srcRectOffset != blitCtx.prevSrcRectOffset)) { blitCtx.pBlitParamsBuffer->setVariable(blitCtx.offsetVarOffset, srcRectOffset); blitCtx.prevSrcRectOffset = srcRectOffset; } - if (srcRectScale != blitCtx.prevSrcReftScale) + if (any(srcRectScale != blitCtx.prevSrcReftScale)) { blitCtx.pBlitParamsBuffer->setVariable(blitCtx.scaleVarOffset, srcRectScale); blitCtx.prevSrcReftScale = srcRectScale; } - Texture::SharedPtr pSharedTex = std::static_pointer_cast(pDstResource); + ref pSharedTex = pDstResource->asTexture(); blitCtx.pFbo->attachColorTarget( pSharedTex, 0, pDst->getViewInfo().mostDetailedMip, pDst->getViewInfo().firstArraySlice, pDst->getViewInfo().arraySize ); @@ -464,12 +398,13 @@ void RenderContext::blit( blitCtx.pPass->execute(this, blitCtx.pFbo, false); // Release the resources we bound + blitCtx.pFbo->attachColorTarget(nullptr, 0); blitCtx.pPass->getVars()->setSrv(blitCtx.texBindLoc, nullptr); } void RenderContext::clearRtv(const RenderTargetView* pRtv, const float4& color) { - resourceBarrier(pRtv->getResource().get(), Resource::State::RenderTarget); + resourceBarrier(pRtv->getResource(), Resource::State::RenderTarget); gfx::ClearValue clearValue = {}; memcpy(clearValue.color.floatValues, &color, sizeof(float) * 4); auto encoder = getLowLevelData()->getResourceCommandEncoder(); @@ -479,7 +414,7 @@ void RenderContext::clearRtv(const RenderTargetView* pRtv, const float4& color) void RenderContext::clearDsv(const DepthStencilView* pDsv, float depth, uint8_t stencil, bool clearDepth, bool clearStencil) { - resourceBarrier(pDsv->getResource().get(), Resource::State::DepthStencil); + resourceBarrier(pDsv->getResource(), Resource::State::DepthStencil); gfx::ClearValue clearValue = {}; clearValue.depthStencil.depth = depth; clearValue.depthStencil.stencil = stencil; @@ -502,14 +437,14 @@ void RenderContext::drawInstanced( uint32_t startInstanceLocation ) { - auto encoder = drawCallCommon(this, pState, pVars); + auto encoder = drawCallCommon(pState, pVars); encoder->drawInstanced(vertexCount, instanceCount, startVertexLocation, startInstanceLocation); mCommandsPending = true; } void RenderContext::draw(GraphicsState* pState, GraphicsVars* pVars, uint32_t vertexCount, uint32_t startVertexLocation) { - auto encoder = drawCallCommon(this, pState, pVars); + auto encoder = drawCallCommon(pState, pVars); encoder->draw(vertexCount, startVertexLocation); mCommandsPending = true; } @@ -524,7 +459,7 @@ void RenderContext::drawIndexedInstanced( uint32_t startInstanceLocation ) { - auto encoder = drawCallCommon(this, pState, pVars); + auto encoder = drawCallCommon(pState, pVars); encoder->drawIndexedInstanced(indexCount, instanceCount, startIndexLocation, baseVertexLocation, startInstanceLocation); mCommandsPending = true; } @@ -537,7 +472,7 @@ void RenderContext::drawIndexed( int32_t baseVertexLocation ) { - auto encoder = drawCallCommon(this, pState, pVars); + auto encoder = drawCallCommon(pState, pVars); encoder->drawIndexed(indexCount, startIndexLocation, baseVertexLocation); mCommandsPending = true; } @@ -553,7 +488,7 @@ void RenderContext::drawIndirect( ) { resourceBarrier(pArgBuffer, Resource::State::IndirectArg); - auto encoder = drawCallCommon(this, pState, pVars); + auto encoder = drawCallCommon(pState, pVars); encoder->drawIndirect( maxCommandCount, pArgBuffer->getGfxBufferResource(), argBufferOffset, pCountBuffer ? pCountBuffer->getGfxBufferResource() : nullptr, countBufferOffset @@ -572,7 +507,7 @@ void RenderContext::drawIndexedIndirect( ) { resourceBarrier(pArgBuffer, Resource::State::IndirectArg); - auto encoder = drawCallCommon(this, pState, pVars); + auto encoder = drawCallCommon(pState, pVars); encoder->drawIndexedIndirect( maxCommandCount, pArgBuffer->getGfxBufferResource(), argBufferOffset, pCountBuffer ? pCountBuffer->getGfxBufferResource() : nullptr, countBufferOffset @@ -593,12 +528,7 @@ void RenderContext::raytrace(RtProgram* pProgram, RtProgramVars* pVars, uint32_t mCommandsPending = true; } -void RenderContext::resolveSubresource( - const Texture::SharedPtr& pSrc, - uint32_t srcSubresource, - const Texture::SharedPtr& pDst, - uint32_t dstSubresource -) +void RenderContext::resolveSubresource(const ref& pSrc, uint32_t srcSubresource, const ref& pDst, uint32_t dstSubresource) { auto resourceEncoder = getLowLevelData()->getResourceCommandEncoder(); gfx::SubresourceRange srcRange = {}; @@ -620,7 +550,7 @@ void RenderContext::resolveSubresource( mCommandsPending = true; } -void RenderContext::resolveResource(const Texture::SharedPtr& pSrc, const Texture::SharedPtr& pDst) +void RenderContext::resolveResource(const ref& pSrc, const ref& pDst) { resourceBarrier(pSrc.get(), Resource::State::ResolveSource); resourceBarrier(pDst.get(), Resource::State::ResolveDest); @@ -675,4 +605,90 @@ void RenderContext::copyAccelerationStructure( ); mCommandsPending = true; } + +gfx::IRenderCommandEncoder* RenderContext::drawCallCommon(GraphicsState* pState, GraphicsVars* pVars) +{ + // Insert barriers for bound resources. + pVars->prepareDescriptorSets(this); + + // Insert barriers for render targets. + ensureFboAttachmentResourceStates(this, pState->getFbo().get()); + + // Insert barriers for vertex/index buffers. + auto pGso = pState->getGSO(pVars).get(); + if (pGso != mpLastBoundGraphicsStateObject) + { + auto pVao = pState->getVao().get(); + for (uint32_t i = 0; i < pVao->getVertexBuffersCount(); i++) + { + auto vertexBuffer = pVao->getVertexBuffer(i).get(); + resourceBarrier(vertexBuffer, Resource::State::VertexBuffer); + } + if (pVao->getIndexBuffer()) + { + auto indexBuffer = pVao->getIndexBuffer().get(); + resourceBarrier(indexBuffer, Resource::State::IndexBuffer); + } + } + + bool isNewEncoder = false; + auto encoder = getLowLevelData()->getRenderCommandEncoder( + pGso->getGFXRenderPassLayout(), pState->getFbo() ? pState->getFbo()->getGfxFramebuffer() : nullptr, isNewEncoder + ); + + FALCOR_GFX_CALL(encoder->bindPipelineWithRootObject(pGso->getGfxPipelineState(), pVars->getShaderObject())); + + if (isNewEncoder || pGso != mpLastBoundGraphicsStateObject) + { + mpLastBoundGraphicsStateObject = pGso; + auto pVao = pState->getVao().get(); + auto pVertexLayout = pVao->getVertexLayout().get(); + for (uint32_t i = 0; i < pVao->getVertexBuffersCount(); i++) + { + auto bufferLayout = pVertexLayout->getBufferLayout(i); + auto vertexBuffer = pVao->getVertexBuffer(i).get(); + encoder->setVertexBuffer( + i, pVao->getVertexBuffer(i)->getGfxBufferResource(), + bufferLayout->getElementOffset(0) + (uint32_t)vertexBuffer->getGpuAddressOffset() + ); + } + if (pVao->getIndexBuffer()) + { + auto indexBuffer = pVao->getIndexBuffer().get(); + encoder->setIndexBuffer( + indexBuffer->getGfxBufferResource(), getGFXFormat(pVao->getIndexBufferFormat()), + (uint32_t)indexBuffer->getGpuAddressOffset() + ); + } + encoder->setPrimitiveTopology(getGFXPrimitiveTopology(pVao->getPrimitiveTopology())); + encoder->setViewports( + (uint32_t)pState->getViewports().size(), reinterpret_cast(pState->getViewports().data()) + ); + encoder->setScissorRects( + (uint32_t)pState->getScissors().size(), reinterpret_cast(pState->getScissors().data()) + ); + } + + return encoder; +} + +FALCOR_SCRIPT_BINDING(CopyContext) +{ + pybind11::class_ copyContext(m, "CopyContext"); +} + +FALCOR_SCRIPT_BINDING(ComputeContext) +{ + FALCOR_SCRIPT_BINDING_DEPENDENCY(CopyContext) + + pybind11::class_ computeContext(m, "ComputeContext"); +} + +FALCOR_SCRIPT_BINDING(RenderContext) +{ + FALCOR_SCRIPT_BINDING_DEPENDENCY(ComputeContext) + + pybind11::class_ renderContext(m, "RenderContext"); +} + } // namespace Falcor diff --git a/Source/Falcor/Core/API/RenderContext.h b/Source/Falcor/Core/API/RenderContext.h index ca451be6e..061a70aa6 100644 --- a/Source/Falcor/Core/API/RenderContext.h +++ b/Source/Falcor/Core/API/RenderContext.h @@ -40,6 +40,7 @@ namespace Falcor { +class GraphicsStateObject; class GraphicsState; class GraphicsVars; @@ -251,8 +252,8 @@ class FALCOR_API RenderContext : public ComputeContext * @param[in] dstRect Target rectangle to blit to, specified by [left, up, right, down]. */ void blit( - const ShaderResourceView::SharedPtr& pSrc, - const RenderTargetView::SharedPtr& pDst, + const ref& pSrc, + const ref& pDst, uint4 srcRect = kMaxRect, uint4 dstRect = kMaxRect, Sampler::Filter = Sampler::Filter::Linear @@ -271,8 +272,8 @@ class FALCOR_API RenderContext : public ComputeContext * @param[in] componentsTransform Linear combination factors of the input components for each output component. */ void blit( - const ShaderResourceView::SharedPtr& pSrc, - const RenderTargetView::SharedPtr& pDst, + const ref& pSrc, + const ref& pDst, uint4 srcRect, uint4 dstRect, Sampler::Filter filter, @@ -299,17 +300,12 @@ class FALCOR_API RenderContext : public ComputeContext * Resolve an entire multi-sampled resource. The dst and src resources must have the same dimensions, array-size, mip-count and format. * If any of these properties don't match, you'll have to use `resolveSubresource` */ - void resolveResource(const Texture::SharedPtr& pSrc, const Texture::SharedPtr& pDst); + void resolveResource(const ref& pSrc, const ref& pDst); /** * Resolve a multi-sampled sub-resource */ - void resolveSubresource( - const Texture::SharedPtr& pSrc, - uint32_t srcSubresource, - const Texture::SharedPtr& pDst, - uint32_t dstSubresource - ); + void resolveSubresource(const ref& pSrc, uint32_t srcSubresource, const ref& pDst, uint32_t dstSubresource); /** * Submit a raytrace command. This function doesn't change the state of the render-context. Graphics/compute vars and state will stay @@ -334,9 +330,12 @@ class FALCOR_API RenderContext : public ComputeContext private: RenderContext(gfx::ICommandQueue* pQueue); + gfx::IRenderCommandEncoder* drawCallCommon(GraphicsState* pState, GraphicsVars* pVars); + std::unique_ptr mpBlitContext; StateBindFlags mBindFlags = StateBindFlags::All; + GraphicsStateObject* mpLastBoundGraphicsStateObject = nullptr; GraphicsVars* mpLastBoundGraphicsVars = nullptr; }; diff --git a/Source/Falcor/Core/API/Resource.cpp b/Source/Falcor/Core/API/Resource.cpp index bcf5dcf97..bd399b4fa 100644 --- a/Source/Falcor/Core/API/Resource.cpp +++ b/Source/Falcor/Core/API/Resource.cpp @@ -32,6 +32,7 @@ #include "GFXAPI.h" #include "NativeHandleTraits.h" #include "Core/Assert.h" +#include "Core/ObjectPython.h" #include "Utils/Logger.h" #include "Utils/StringUtils.h" #include "Utils/Scripting/ScriptBindings.h" @@ -39,8 +40,8 @@ namespace Falcor { -Resource::Resource(std::shared_ptr pDevice, Type type, BindFlags bindFlags, uint64_t size) - : mpDevice(std::move(pDevice)), mType(type), mBindFlags(bindFlags), mSize(size) +Resource::Resource(ref pDevice, Type type, BindFlags bindFlags, uint64_t size) + : mpDevice(pDevice), mType(type), mBindFlags(bindFlags), mSize(size) {} Resource::~Resource() = default; @@ -100,12 +101,24 @@ const std::string to_string(Resource::State state) return s; } +ref Resource::getDevice() const +{ + return mpDevice; +} + void Resource::invalidateViews() const { - mSrvs.clear(); - mUavs.clear(); - mRtvs.clear(); - mDsvs.clear(); + auto invalidateAll = [](auto& vec) + { + for (const auto& item : vec) + item.second->invalidate(); + vec.clear(); + }; + + invalidateAll(mSrvs); + invalidateAll(mUavs); + invalidateAll(mRtvs); + invalidateAll(mDsvs); } void Resource::setName(const std::string& name) @@ -114,18 +127,18 @@ void Resource::setName(const std::string& name) getGfxResource()->setDebugName(mName.c_str()); } -std::shared_ptr Resource::asTexture() +ref Resource::asTexture() { // In the past, Falcor relied on undefined behavior checking `this` for nullptr, returning nullptr if `this` was nullptr. FALCOR_ASSERT(this); - return std::dynamic_pointer_cast(shared_from_this()); + return ref(dynamic_cast(this)); } -std::shared_ptr Resource::asBuffer() +ref Resource::asBuffer() { // In the past, Falcor relied on undefined behavior checking `this` for nullptr, returning nullptr if `this` was nullptr. FALCOR_ASSERT(this); - return std::dynamic_pointer_cast(shared_from_this()); + return ref(dynamic_cast(this)); } Resource::State Resource::getGlobalState() const @@ -211,8 +224,13 @@ NativeHandle Resource::getNativeHandle() const return {}; } +void Resource::breakStrongReferenceToDevice() +{ + mpDevice.breakStrongReference(); +} + FALCOR_SCRIPT_BINDING(Resource) { - pybind11::class_(m, "Resource"); + pybind11::class_>(m, "Resource"); } } // namespace Falcor diff --git a/Source/Falcor/Core/API/Resource.h b/Source/Falcor/Core/API/Resource.h index 65967dc87..890c68936 100644 --- a/Source/Falcor/Core/API/Resource.h +++ b/Source/Falcor/Core/API/Resource.h @@ -26,25 +26,25 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **************************************************************************/ #pragma once +#include "fwd.h" #include "Handles.h" #include "NativeHandle.h" #include "Formats.h" #include "ResourceViews.h" #include "Core/Macros.h" -#include +#include "Core/Object.h" #include #include #include namespace Falcor { -class Device; class Texture; class Buffer; class ParameterBlock; struct ResourceViewInfo; -class FALCOR_API Resource : public std::enable_shared_from_this +class FALCOR_API Resource : public Object { public: using BindFlags = ResourceBindFlags; @@ -91,16 +91,14 @@ class FALCOR_API Resource : public std::enable_shared_from_this AccelerationStructure, }; - using SharedPtr = std::shared_ptr; - /** * Default value used in create*() methods */ - static const uint32_t kMaxPossible = RenderTargetView::kMaxPossible; + static constexpr uint32_t kMaxPossible = RenderTargetView::kMaxPossible; virtual ~Resource() = 0; - const std::shared_ptr& getDevice() const { return mpDevice; } + ref getDevice() const; /** * Get the bind flags @@ -178,21 +176,23 @@ class FALCOR_API Resource : public std::enable_shared_from_this * Get a SRV/UAV for the entire resource. * Buffer and Texture have overloads which allow you to create a view into part of the resource */ - virtual ShaderResourceView::SharedPtr getSRV() = 0; - virtual UnorderedAccessView::SharedPtr getUAV() = 0; + virtual ref getSRV() = 0; + virtual ref getUAV() = 0; /** * Conversions to derived classes */ - std::shared_ptr asTexture(); - std::shared_ptr asBuffer(); + ref asTexture(); + ref asBuffer(); + + void breakStrongReferenceToDevice(); protected: friend class CopyContext; - Resource(std::shared_ptr pDevice, Type type, BindFlags bindFlags, uint64_t size); + Resource(ref pDevice, Type type, BindFlags bindFlags, uint64_t size); - std::shared_ptr mpDevice; + BreakableReference mpDevice; Type mType; BindFlags mBindFlags; struct @@ -210,10 +210,10 @@ class FALCOR_API Resource : public std::enable_shared_from_this std::string mName; mutable SharedResourceApiHandle mSharedApiHandle = 0; - mutable std::unordered_map mSrvs; - mutable std::unordered_map mRtvs; - mutable std::unordered_map mDsvs; - mutable std::unordered_map mUavs; + mutable std::unordered_map, ViewInfoHashFunc> mSrvs; + mutable std::unordered_map, ViewInfoHashFunc> mRtvs; + mutable std::unordered_map, ViewInfoHashFunc> mDsvs; + mutable std::unordered_map, ViewInfoHashFunc> mUavs; }; const std::string FALCOR_API to_string(Resource::Type); diff --git a/Source/Falcor/Core/API/ResourceViews.cpp b/Source/Falcor/Core/API/ResourceViews.cpp index bdefd5dc2..cf7f00791 100644 --- a/Source/Falcor/Core/API/ResourceViews.cpp +++ b/Source/Falcor/Core/API/ResourceViews.cpp @@ -32,6 +32,7 @@ #include "GFXHelpers.h" #include "GFXAPI.h" #include "NativeHandleTraits.h" +#include "Core/ObjectPython.h" #include "Utils/Scripting/ScriptBindings.h" namespace Falcor @@ -39,6 +40,7 @@ namespace Falcor NativeHandle ResourceView::getNativeHandle() const { + FALCOR_ASSERT(mpDevice != nullptr && mpResource != nullptr); gfx::InteropHandle gfxNativeHandle = {}; FALCOR_GFX_CALL(mGfxResourceView->getNativeHandle(&gfxNativeHandle)); #if FALCOR_HAS_D3D12 @@ -48,9 +50,9 @@ NativeHandle ResourceView::getNativeHandle() const #if FALCOR_HAS_VULKAN if (mpDevice->getType() == Device::Type::Vulkan) { - if (auto pResource = mpResource.lock()) + if (mpResource) { - if (pResource->getType() == Resource::Type::Buffer) + if (mpResource->getType() == Resource::Type::Buffer) { if (mGfxResourceView->getViewDesc()->format == gfx::Format::Unknown) return NativeHandle(reinterpret_cast(gfxNativeHandle.handleValue)); @@ -69,12 +71,24 @@ NativeHandle ResourceView::getNativeHandle() const ResourceView::~ResourceView() { - mpDevice->releaseResource(mGfxResourceView); + if (mGfxResourceView) + mpDevice->releaseResource(mGfxResourceView); } -ShaderResourceView::SharedPtr ShaderResourceView::create( +void ResourceView::invalidate() +{ + if (mpDevice) + { + mpDevice->releaseResource(mGfxResourceView); + mGfxResourceView = nullptr; + mpResource = nullptr; + mpDevice = nullptr; + } +} + +ref ShaderResourceView::create( Device* pDevice, - ConstTextureSharedPtrRef pTexture, + Texture* pTexture, uint32_t mostDetailedMip, uint32_t mipCount, uint32_t firstArraySlice, @@ -90,17 +104,11 @@ ShaderResourceView::SharedPtr ShaderResourceView::create( desc.subresourceRange.mipLevel = mostDetailedMip; desc.subresourceRange.mipLevelCount = mipCount; FALCOR_GFX_CALL(pDevice->getGfxDevice()->createTextureView(pTexture->getGfxTextureResource(), desc, handle.writeRef())); - return SharedPtr( - new ShaderResourceView(pDevice->shared_from_this(), pTexture, handle, mostDetailedMip, mipCount, firstArraySlice, arraySize) + return ref(new ShaderResourceView(pDevice, pTexture, handle, mostDetailedMip, mipCount, firstArraySlice, arraySize) ); } -static void fillBufferViewDesc( - gfx::IResourceView::Desc& desc, - ConstBufferSharedPtrRef pBuffer, - uint32_t firstElement, - uint32_t elementCount -) +static void fillBufferViewDesc(gfx::IResourceView::Desc& desc, Buffer* pBuffer, uint32_t firstElement, uint32_t elementCount) { auto format = depthToColorFormat(pBuffer->getFormat()); desc.format = getGFXFormat(format); @@ -133,12 +141,7 @@ static void fillBufferViewDesc( desc.bufferRange.elementCount = useDefaultCount ? (bufferElementCount - firstElement) : elementCount; } -ShaderResourceView::SharedPtr ShaderResourceView::create( - Device* pDevice, - ConstBufferSharedPtrRef pBuffer, - uint32_t firstElement, - uint32_t elementCount -) +ref ShaderResourceView::create(Device* pDevice, Buffer* pBuffer, uint32_t firstElement, uint32_t elementCount) { Slang::ComPtr handle; gfx::IResourceView::Desc desc = {}; @@ -146,18 +149,18 @@ ShaderResourceView::SharedPtr ShaderResourceView::create( fillBufferViewDesc(desc, pBuffer, firstElement, elementCount); FALCOR_GFX_CALL(pDevice->getGfxDevice()->createBufferView(pBuffer->getGfxBufferResource(), nullptr, desc, handle.writeRef())); - return SharedPtr(new ShaderResourceView(pDevice->shared_from_this(), pBuffer, handle, firstElement, elementCount)); + return ref(new ShaderResourceView(pDevice, pBuffer, handle, firstElement, elementCount)); } -ShaderResourceView::SharedPtr ShaderResourceView::create(Device* pDevice, Dimension dimension) +ref ShaderResourceView::create(Device* pDevice, Dimension dimension) { // Create a null view of the specified dimension. - return SharedPtr(new ShaderResourceView(pDevice->shared_from_this(), std::weak_ptr(), nullptr, 0, 0)); + return ref(new ShaderResourceView(pDevice, nullptr, nullptr, 0, 0)); } -DepthStencilView::SharedPtr DepthStencilView::create( +ref DepthStencilView::create( Device* pDevice, - ConstTextureSharedPtrRef pTexture, + Texture* pTexture, uint32_t mipLevel, uint32_t firstArraySlice, uint32_t arraySize @@ -174,17 +177,17 @@ DepthStencilView::SharedPtr DepthStencilView::create( desc.subresourceRange.aspectMask = gfx::TextureAspect::Depth; desc.renderTarget.shape = pTexture->getGfxTextureResource()->getDesc()->type; FALCOR_GFX_CALL(pDevice->getGfxDevice()->createTextureView(pTexture->getGfxTextureResource(), desc, handle.writeRef())); - return SharedPtr(new DepthStencilView(pDevice->shared_from_this(), pTexture, handle, mipLevel, firstArraySlice, arraySize)); + return ref(new DepthStencilView(pDevice, pTexture, handle, mipLevel, firstArraySlice, arraySize)); } -DepthStencilView::SharedPtr DepthStencilView::create(Device* pDevice, Dimension dimension) +ref DepthStencilView::create(Device* pDevice, Dimension dimension) { - return SharedPtr(new DepthStencilView(pDevice->shared_from_this(), std::weak_ptr(), nullptr, 0, 0, 0)); + return ref(new DepthStencilView(pDevice, nullptr, nullptr, 0, 0, 0)); } -UnorderedAccessView::SharedPtr UnorderedAccessView::create( +ref UnorderedAccessView::create( Device* pDevice, - ConstTextureSharedPtrRef pTexture, + Texture* pTexture, uint32_t mipLevel, uint32_t firstArraySlice, uint32_t arraySize @@ -199,15 +202,10 @@ UnorderedAccessView::SharedPtr UnorderedAccessView::create( desc.subresourceRange.mipLevel = mipLevel; desc.subresourceRange.mipLevelCount = 1; FALCOR_GFX_CALL(pDevice->getGfxDevice()->createTextureView(pTexture->getGfxTextureResource(), desc, handle.writeRef())); - return SharedPtr(new UnorderedAccessView(pDevice->shared_from_this(), pTexture, handle, mipLevel, firstArraySlice, arraySize)); + return ref(new UnorderedAccessView(pDevice, pTexture, handle, mipLevel, firstArraySlice, arraySize)); } -UnorderedAccessView::SharedPtr UnorderedAccessView::create( - Device* pDevice, - ConstBufferSharedPtrRef pBuffer, - uint32_t firstElement, - uint32_t elementCount -) +ref UnorderedAccessView::create(Device* pDevice, Buffer* pBuffer, uint32_t firstElement, uint32_t elementCount) { Slang::ComPtr handle; gfx::IResourceView::Desc desc = {}; @@ -217,17 +215,17 @@ UnorderedAccessView::SharedPtr UnorderedAccessView::create( pBuffer->getGfxBufferResource(), pBuffer->getUAVCounter() ? pBuffer->getUAVCounter()->getGfxBufferResource() : nullptr, desc, handle.writeRef() )); - return SharedPtr(new UnorderedAccessView(pDevice->shared_from_this(), pBuffer, handle, firstElement, elementCount)); + return ref(new UnorderedAccessView(pDevice, pBuffer, handle, firstElement, elementCount)); } -UnorderedAccessView::SharedPtr UnorderedAccessView::create(Device* pDevice, Dimension dimension) +ref UnorderedAccessView::create(Device* pDevice, Dimension dimension) { - return SharedPtr(new UnorderedAccessView(pDevice->shared_from_this(), std::weak_ptr(), nullptr, 0, 0)); + return ref(new UnorderedAccessView(pDevice, nullptr, nullptr, 0, 0)); } -RenderTargetView::SharedPtr RenderTargetView::create( +ref RenderTargetView::create( Device* pDevice, - ConstTextureSharedPtrRef pTexture, + Texture* pTexture, uint32_t mipLevel, uint32_t firstArraySlice, uint32_t arraySize @@ -244,7 +242,7 @@ RenderTargetView::SharedPtr RenderTargetView::create( desc.subresourceRange.aspectMask = gfx::TextureAspect::Color; desc.renderTarget.shape = pTexture->getGfxTextureResource()->getDesc()->type; FALCOR_GFX_CALL(pDevice->getGfxDevice()->createTextureView(pTexture->getGfxTextureResource(), desc, handle.writeRef())); - return SharedPtr(new RenderTargetView(pDevice->shared_from_this(), pTexture, handle, mipLevel, firstArraySlice, arraySize)); + return ref(new RenderTargetView(pDevice, pTexture, handle, mipLevel, firstArraySlice, arraySize)); } gfx::IResource::Type getGFXResourceType(RenderTargetView::Dimension dim) @@ -272,7 +270,7 @@ gfx::IResource::Type getGFXResourceType(RenderTargetView::Dimension dim) } } -RenderTargetView::SharedPtr RenderTargetView::create(Device* pDevice, Dimension dimension) +ref RenderTargetView::create(Device* pDevice, Dimension dimension) { Slang::ComPtr handle; gfx::IResourceView::Desc desc = {}; @@ -285,14 +283,14 @@ RenderTargetView::SharedPtr RenderTargetView::create(Device* pDevice, Dimension desc.subresourceRange.aspectMask = gfx::TextureAspect::Color; desc.renderTarget.shape = getGFXResourceType(dimension); FALCOR_GFX_CALL(pDevice->getGfxDevice()->createTextureView(nullptr, desc, handle.writeRef())); - return SharedPtr(new RenderTargetView(pDevice->shared_from_this(), std::weak_ptr(), handle, 0, 0, 0)); + return ref(new RenderTargetView(pDevice, nullptr, handle, 0, 0, 0)); } FALCOR_SCRIPT_BINDING(ResourceView) { - pybind11::class_(m, "ShaderResourceView"); - pybind11::class_(m, "RenderTargetView"); - pybind11::class_(m, "UnorderedAccessView"); - pybind11::class_(m, "DepthStencilView"); + pybind11::class_>(m, "ShaderResourceView"); + pybind11::class_>(m, "RenderTargetView"); + pybind11::class_>(m, "UnorderedAccessView"); + pybind11::class_>(m, "DepthStencilView"); } } // namespace Falcor diff --git a/Source/Falcor/Core/API/ResourceViews.h b/Source/Falcor/Core/API/ResourceViews.h index c7f2fc571..02ec45743 100644 --- a/Source/Falcor/Core/API/ResourceViews.h +++ b/Source/Falcor/Core/API/ResourceViews.h @@ -30,19 +30,16 @@ #include "Handles.h" #include "NativeHandle.h" #include "Core/Macros.h" +#include "Core/Object.h" #include "Core/Program/ProgramReflection.h" #include -#include namespace Falcor { class Resource; class Texture; class Buffer; -using ResourceWeakPtr = std::weak_ptr; -using ResourceSharedPtr = std::shared_ptr; -using ConstTextureSharedPtrRef = const std::shared_ptr&; -using ConstBufferSharedPtrRef = const std::shared_ptr&; +class Resource; struct FALCOR_API ResourceViewInfo { @@ -53,7 +50,7 @@ struct FALCOR_API ResourceViewInfo ResourceViewInfo(uint32_t firstElement, uint32_t elementCount) : firstElement(firstElement), elementCount(elementCount) {} - static const uint32_t kMaxPossible = -1; + static constexpr uint32_t kMaxPossible = -1; // Textures uint32_t mostDetailedMip = 0; @@ -75,42 +72,40 @@ struct FALCOR_API ResourceViewInfo /** * Abstracts API resource views. */ -class FALCOR_API ResourceView +class FALCOR_API ResourceView : public Object { public: - using SharedPtr = std::shared_ptr; - using Dimension = ReflectionResourceType::Dimensions; static const uint32_t kMaxPossible = -1; virtual ~ResourceView(); ResourceView( - std::shared_ptr pDevice, - ResourceWeakPtr& pResource, + Device* pDevice, + Resource* pResource, Slang::ComPtr gfxResourceView, uint32_t mostDetailedMip, uint32_t mipCount, uint32_t firstArraySlice, uint32_t arraySize ) - : mpDevice(std::move(pDevice)) + : mpDevice(pDevice) , mGfxResourceView(gfxResourceView) , mViewInfo(mostDetailedMip, mipCount, firstArraySlice, arraySize) , mpResource(pResource) {} ResourceView( - std::shared_ptr pDevice, - ResourceWeakPtr& pResource, + Device* pDevice, + Resource* pResource, Slang::ComPtr gfxResourceView, uint32_t firstElement, uint32_t elementCount ) - : mpDevice(std::move(pDevice)), mGfxResourceView(gfxResourceView), mViewInfo(firstElement, elementCount), mpResource(pResource) + : mpDevice(pDevice), mGfxResourceView(gfxResourceView), mViewInfo(firstElement, elementCount), mpResource(pResource) {} - ResourceView(std::shared_ptr pDevice, ResourceWeakPtr& pResource, Slang::ComPtr gfxResourceView) - : mpDevice(std::move(pDevice)), mGfxResourceView(gfxResourceView), mpResource(pResource) + ResourceView(Device* pDevice, Resource* pResource, Slang::ComPtr gfxResourceView) + : mpDevice(pDevice), mGfxResourceView(gfxResourceView), mpResource(pResource) {} gfx::IResourceView* getGfxResourceView() const { return mGfxResourceView; } @@ -130,35 +125,37 @@ class FALCOR_API ResourceView /** * Get the resource referenced by the view. */ - ResourceSharedPtr getResource() const { return mpResource.lock(); } + Resource* getResource() const { return mpResource; } protected: - std::shared_ptr mpDevice; + friend class Resource; + + void invalidate(); + + Device* mpDevice; Slang::ComPtr mGfxResourceView; ResourceViewInfo mViewInfo; - ResourceWeakPtr mpResource; + Resource* mpResource; }; class FALCOR_API ShaderResourceView : public ResourceView { public: - using SharedPtr = std::shared_ptr; - - static SharedPtr create( + static ref create( Device* pDevice, - ConstTextureSharedPtrRef pTexture, + Texture* pTexture, uint32_t mostDetailedMip, uint32_t mipCount, uint32_t firstArraySlice, uint32_t arraySize ); - static SharedPtr create(Device* pDevice, ConstBufferSharedPtrRef pBuffer, uint32_t firstElement, uint32_t elementCount); - static SharedPtr create(Device* pDevice, Dimension dimension); + static ref create(Device* pDevice, Buffer* pBuffer, uint32_t firstElement, uint32_t elementCount); + static ref create(Device* pDevice, Dimension dimension); private: ShaderResourceView( - std::shared_ptr pDevice, - ResourceWeakPtr pResource, + Device* pDevice, + Resource* pResource, Slang::ComPtr gfxResourceView, uint32_t mostDetailedMip, uint32_t mipCount, @@ -168,15 +165,15 @@ class FALCOR_API ShaderResourceView : public ResourceView : ResourceView(pDevice, pResource, gfxResourceView, mostDetailedMip, mipCount, firstArraySlice, arraySize) {} ShaderResourceView( - std::shared_ptr pDevice, - ResourceWeakPtr pResource, + Device* pDevice, + Resource* pResource, Slang::ComPtr gfxResourceView, uint32_t firstElement, uint32_t elementCount ) : ResourceView(pDevice, pResource, gfxResourceView, firstElement, elementCount) {} - ShaderResourceView(std::shared_ptr pDevice, ResourceWeakPtr pResource, Slang::ComPtr gfxResourceView) + ShaderResourceView(Device* pDevice, Resource* pResource, Slang::ComPtr gfxResourceView) : ResourceView(pDevice, pResource, gfxResourceView) {} }; @@ -184,21 +181,19 @@ class FALCOR_API ShaderResourceView : public ResourceView class FALCOR_API DepthStencilView : public ResourceView { public: - using SharedPtr = std::shared_ptr; - - static SharedPtr create( + static ref create( Device* pDevice, - ConstTextureSharedPtrRef pTexture, + Texture* pTexture, uint32_t mipLevel, uint32_t firstArraySlice, uint32_t arraySize ); - static SharedPtr create(Device* pDevice, Dimension dimension); + static ref create(Device* pDevice, Dimension dimension); private: DepthStencilView( - std::shared_ptr pDevice, - ResourceWeakPtr pResource, + Device* pDevice, + Resource* pResource, Slang::ComPtr gfxResourceView, uint32_t mipLevel, uint32_t firstArraySlice, @@ -211,22 +206,20 @@ class FALCOR_API DepthStencilView : public ResourceView class FALCOR_API UnorderedAccessView : public ResourceView { public: - using SharedPtr = std::shared_ptr; - - static SharedPtr create( + static ref create( Device* pDevice, - ConstTextureSharedPtrRef pTexture, + Texture* pTexture, uint32_t mipLevel, uint32_t firstArraySlice, uint32_t arraySize ); - static SharedPtr create(Device* pDevice, ConstBufferSharedPtrRef pBuffer, uint32_t firstElement, uint32_t elementCount); - static SharedPtr create(Device* pDevice, Dimension dimension); + static ref create(Device* pDevice, Buffer* pBuffer, uint32_t firstElement, uint32_t elementCount); + static ref create(Device* pDevice, Dimension dimension); private: UnorderedAccessView( - std::shared_ptr pDevice, - ResourceWeakPtr pResource, + Device* pDevice, + Resource* pResource, Slang::ComPtr gfxResourceView, uint32_t mipLevel, uint32_t firstArraySlice, @@ -236,8 +229,8 @@ class FALCOR_API UnorderedAccessView : public ResourceView {} UnorderedAccessView( - std::shared_ptr pDevice, - ResourceWeakPtr pResource, + Device* pDevice, + Resource* pResource, Slang::ComPtr gfxResourceView, uint32_t firstElement, uint32_t elementCount @@ -249,21 +242,19 @@ class FALCOR_API UnorderedAccessView : public ResourceView class FALCOR_API RenderTargetView : public ResourceView { public: - using SharedPtr = std::shared_ptr; - - static SharedPtr create( + static ref create( Device* pDevice, - ConstTextureSharedPtrRef pTexture, + Texture* pTexture, uint32_t mipLevel, uint32_t firstArraySlice, uint32_t arraySize ); - static SharedPtr create(Device* pDevice, Dimension dimension); + static ref create(Device* pDevice, Dimension dimension); private: RenderTargetView( - std::shared_ptr pDevice, - ResourceWeakPtr pResource, + Device* pDevice, + Resource* pResource, Slang::ComPtr gfxResourceView, uint32_t mipLevel, uint32_t firstArraySlice, diff --git a/Source/Falcor/Core/API/RtAccelerationStructure.cpp b/Source/Falcor/Core/API/RtAccelerationStructure.cpp index cbfa44b98..d36cd8172 100644 --- a/Source/Falcor/Core/API/RtAccelerationStructure.cpp +++ b/Source/Falcor/Core/API/RtAccelerationStructure.cpp @@ -52,7 +52,7 @@ RtAccelerationStructure::Desc& RtAccelerationStructure::Desc::setKind(RtAccelera return *this; } -RtAccelerationStructure::Desc& RtAccelerationStructure::Desc::setBuffer(Buffer::SharedPtr buffer, uint64_t offset, uint64_t size) +RtAccelerationStructure::Desc& RtAccelerationStructure::Desc::setBuffer(ref buffer, uint64_t offset, uint64_t size) { mBuffer = buffer; mOffset = offset; @@ -60,9 +60,9 @@ RtAccelerationStructure::Desc& RtAccelerationStructure::Desc::setBuffer(Buffer:: return *this; } -RtAccelerationStructure::SharedPtr RtAccelerationStructure::create(Device* pDevice, const Desc& desc) +ref RtAccelerationStructure::create(ref pDevice, const Desc& desc) { - return SharedPtr(new RtAccelerationStructure(pDevice->shared_from_this(), desc)); + return ref(new RtAccelerationStructure(pDevice, desc)); } uint64_t RtAccelerationStructure::getGpuAddress() @@ -70,14 +70,14 @@ uint64_t RtAccelerationStructure::getGpuAddress() return mDesc.mBuffer->getGpuAddress() + mDesc.mOffset; } -RtInstanceDesc& RtInstanceDesc::setTransform(const rmcv::mat4& matrix) +RtInstanceDesc& RtInstanceDesc::setTransform(const float4x4& matrix) { std::memcpy(transform, &matrix, sizeof(transform)); return *this; } -RtAccelerationStructure::RtAccelerationStructure(std::shared_ptr pDevice, const RtAccelerationStructure::Desc& desc) - : mpDevice(std::move(pDevice)), mDesc(desc) +RtAccelerationStructure::RtAccelerationStructure(ref pDevice, const RtAccelerationStructure::Desc& desc) + : mpDevice(pDevice), mDesc(desc) { gfx::IAccelerationStructure::CreateDesc createDesc = {}; createDesc.buffer = mDesc.getBuffer()->getGfxBufferResource(); diff --git a/Source/Falcor/Core/API/RtAccelerationStructure.h b/Source/Falcor/Core/API/RtAccelerationStructure.h index c2f961fe0..749baa51f 100644 --- a/Source/Falcor/Core/API/RtAccelerationStructure.h +++ b/Source/Falcor/Core/API/RtAccelerationStructure.h @@ -32,8 +32,9 @@ #include "Formats.h" #include "Buffer.h" #include "ResourceViews.h" -#include "Utils/Math/Matrix/Matrix.h" +#include "Utils/Math/Matrix.h" #include "Core/Macros.h" +#include "Core/Object.h" #include #include #include @@ -70,11 +71,10 @@ struct RtInstanceDesc DeviceAddress accelerationStructure; /** - * Sets the transform matrix using a rmcv::mat4 value. - * If this accepted GLM, it would have to transpose, but RMCV is row major so it doesn't + * Sets the transform matrix using a float4x4 value. * @param[in] matrix A 4x4 matrix to set into transform. */ - RtInstanceDesc& setTransform(const rmcv::mat4& matrix); + RtInstanceDesc& setTransform(const float4x4& matrix); }; enum class RtAccelerationStructureKind @@ -178,11 +178,9 @@ struct RtAccelerationStructureBuildInputs * of an acceleration structure. It does not own the backing buffer resource, which is similar to * a resource view. */ -class FALCOR_API RtAccelerationStructure +class FALCOR_API RtAccelerationStructure : public Object { public: - using SharedPtr = std::shared_ptr; - class FALCOR_API Desc { public: @@ -200,9 +198,9 @@ class FALCOR_API RtAccelerationStructure * @param[in] offset The offset within the buffer for the acceleration structure contents. * @param[in] offset The size in bytes to use for the acceleration structure. */ - Desc& setBuffer(Buffer::SharedPtr buffer, uint64_t offset, uint64_t size); + Desc& setBuffer(ref buffer, uint64_t offset, uint64_t size); - Buffer::SharedPtr getBuffer() const { return mBuffer; } + ref getBuffer() const { return mBuffer; } uint64_t getOffset() const { return mOffset; } @@ -212,7 +210,7 @@ class FALCOR_API RtAccelerationStructure protected: RtAccelerationStructureKind mKind = RtAccelerationStructureKind::BottomLevel; - Buffer::SharedPtr mBuffer = nullptr; + ref mBuffer = nullptr; uint64_t mOffset = 0; uint64_t mSize = 0; }; @@ -230,7 +228,7 @@ class FALCOR_API RtAccelerationStructure * @param[in] desc Describes acceleration structure settings. * @return A new object, or throws an exception if creation failed. */ - static SharedPtr create(Device* pDevice, const Desc& desc); + static ref create(ref pDevice, const Desc& desc); static RtAccelerationStructurePrebuildInfo getPrebuildInfo(Device* pDevice, const RtAccelerationStructureBuildInputs& inputs); @@ -243,9 +241,9 @@ class FALCOR_API RtAccelerationStructure gfx::IAccelerationStructure* getGfxAccelerationStructure() const { return mGfxAccelerationStructure; } protected: - RtAccelerationStructure(std::shared_ptr pDevice, const Desc& desc); + RtAccelerationStructure(ref pDevice, const Desc& desc); - std::shared_ptr mpDevice; + ref mpDevice; Desc mDesc; Slang::ComPtr mGfxAccelerationStructure; diff --git a/Source/Falcor/Core/API/RtAccelerationStructurePostBuildInfoPool.cpp b/Source/Falcor/Core/API/RtAccelerationStructurePostBuildInfoPool.cpp index a760aa7f6..30ab0e5e3 100644 --- a/Source/Falcor/Core/API/RtAccelerationStructurePostBuildInfoPool.cpp +++ b/Source/Falcor/Core/API/RtAccelerationStructurePostBuildInfoPool.cpp @@ -33,13 +33,12 @@ namespace Falcor { -RtAccelerationStructurePostBuildInfoPool::SharedPtr RtAccelerationStructurePostBuildInfoPool::create(Device* pDevice, const Desc& desc) +ref RtAccelerationStructurePostBuildInfoPool::create(Device* pDevice, const Desc& desc) { - return SharedPtr(new RtAccelerationStructurePostBuildInfoPool(pDevice->shared_from_this(), desc)); + return ref(new RtAccelerationStructurePostBuildInfoPool(pDevice, desc)); } -RtAccelerationStructurePostBuildInfoPool::RtAccelerationStructurePostBuildInfoPool(std::shared_ptr pDevice, const Desc& desc) - : mDesc(desc) +RtAccelerationStructurePostBuildInfoPool::RtAccelerationStructurePostBuildInfoPool(Device* pDevice, const Desc& desc) : mDesc(desc) { gfx::IQueryPool::Desc queryPoolDesc = {}; queryPoolDesc.count = desc.elementCount; diff --git a/Source/Falcor/Core/API/RtAccelerationStructurePostBuildInfoPool.h b/Source/Falcor/Core/API/RtAccelerationStructurePostBuildInfoPool.h index 1462bd77d..233ed7b8a 100644 --- a/Source/Falcor/Core/API/RtAccelerationStructurePostBuildInfoPool.h +++ b/Source/Falcor/Core/API/RtAccelerationStructurePostBuildInfoPool.h @@ -28,8 +28,8 @@ #pragma once #include "fwd.h" #include "Core/Macros.h" +#include "Core/Object.h" #include -#include namespace Falcor { @@ -40,24 +40,22 @@ enum class RtAccelerationStructurePostBuildInfoQueryType CurrentSize, }; -class FALCOR_API RtAccelerationStructurePostBuildInfoPool +class FALCOR_API RtAccelerationStructurePostBuildInfoPool : public Object { public: - using SharedPtr = std::shared_ptr; - struct Desc { RtAccelerationStructurePostBuildInfoQueryType queryType; uint32_t elementCount; }; - static SharedPtr create(Device* pDevice, const Desc& desc); + static ref create(Device* pDevice, const Desc& desc); ~RtAccelerationStructurePostBuildInfoPool(); uint64_t getElement(CopyContext* pContext, uint32_t index); void reset(CopyContext* pContext); gfx::IQueryPool* getGFXQueryPool() const { return mpGFXQueryPool.get(); } protected: - RtAccelerationStructurePostBuildInfoPool(std::shared_ptr pDevice, const Desc& desc); + RtAccelerationStructurePostBuildInfoPool(Device* pDevice, const Desc& desc); private: Desc mDesc; diff --git a/Source/Falcor/Core/API/RtStateObject.cpp b/Source/Falcor/Core/API/RtStateObject.cpp index 1d2284eee..8e4a39026 100644 --- a/Source/Falcor/Core/API/RtStateObject.cpp +++ b/Source/Falcor/Core/API/RtStateObject.cpp @@ -33,12 +33,12 @@ namespace Falcor { -RtStateObject::SharedPtr RtStateObject::create(Device* pDevice, const Desc& desc) +ref RtStateObject::create(ref pDevice, const Desc& desc) { - return SharedPtr(new RtStateObject(pDevice->shared_from_this(), desc)); + return ref(new RtStateObject(pDevice, desc)); } -RtStateObject::RtStateObject(std::shared_ptr pDevice, const Desc& desc) : mpDevice(std::move(pDevice)), mDesc(desc) +RtStateObject::RtStateObject(ref pDevice, const Desc& desc) : mpDevice(pDevice), mDesc(desc) { auto pKernels = getKernels(); gfx::RayTracingPipelineStateDesc rtpDesc = {}; @@ -69,7 +69,7 @@ RtStateObject::RtStateObject(std::shared_ptr pDevice, const Desc& desc) static_assert((uint32_t)gfx::RayTracingPipelineFlags::SkipTriangles == (uint32_t)RtPipelineFlags::SkipTriangles); rtpDesc.flags = (gfx::RayTracingPipelineFlags::Enum)mDesc.pipelineFlags; - auto rtProgram = std::dynamic_pointer_cast(mDesc.pKernels->getProgramVersion()->getProgram()); + auto rtProgram = dynamic_cast(mDesc.pKernels->getProgramVersion()->getProgram()); FALCOR_ASSERT(rtProgram); rtpDesc.maxRayPayloadSize = rtProgram->getRtDesc().getMaxPayloadSize(); rtpDesc.maxAttributeSizeInBytes = rtProgram->getRtDesc().getMaxAttributeSize(); diff --git a/Source/Falcor/Core/API/RtStateObject.h b/Source/Falcor/Core/API/RtStateObject.h index 567b645ac..242f40548 100644 --- a/Source/Falcor/Core/API/RtStateObject.h +++ b/Source/Falcor/Core/API/RtStateObject.h @@ -30,37 +30,35 @@ #include "Handles.h" #include "Raytracing.h" #include "Core/Macros.h" +#include "Core/Object.h" #include "Core/Program/ProgramVersion.h" -#include #include #include namespace Falcor { -class FALCOR_API RtStateObject +class FALCOR_API RtStateObject : public Object { public: - using SharedPtr = std::shared_ptr; - struct Desc { - ProgramKernels::SharedConstPtr pKernels; + ref pKernels; uint32_t maxTraceRecursionDepth = 0; RtPipelineFlags pipelineFlags = RtPipelineFlags::None; - Desc& setKernels(const ProgramKernels::SharedConstPtr& pKernels) + Desc& setKernels(const ref& pKernels_) { - this->pKernels = pKernels; + pKernels = pKernels_; return *this; } Desc& setMaxTraceRecursionDepth(uint32_t maxDepth) { - this->maxTraceRecursionDepth = maxDepth; + maxTraceRecursionDepth = maxDepth; return *this; } Desc& setPipelineFlags(RtPipelineFlags flags) { - this->pipelineFlags = flags; + pipelineFlags = flags; return *this; } @@ -71,18 +69,18 @@ class FALCOR_API RtStateObject } }; - static SharedPtr create(Device* pDevice, const Desc& desc); + static ref create(ref pDevice, const Desc& desc); gfx::IPipelineState* getGfxPipelineState() const { return mGfxPipelineState; } - const ProgramKernels::SharedConstPtr& getKernels() const { return mDesc.pKernels; }; + const ref& getKernels() const { return mDesc.pKernels; }; uint32_t getMaxTraceRecursionDepth() const { return mDesc.maxTraceRecursionDepth; } void const* getShaderIdentifier(uint32_t index) const { return mEntryPointGroupExportNames[index].c_str(); } const Desc& getDesc() const { return mDesc; } private: - RtStateObject(std::shared_ptr pDevice, const Desc& desc); + RtStateObject(ref pDevice, const Desc& desc); - std::shared_ptr mpDevice; + ref mpDevice; Desc mDesc; Slang::ComPtr mGfxPipelineState; std::vector mEntryPointGroupExportNames; diff --git a/Source/Falcor/Core/API/Sampler.cpp b/Source/Falcor/Core/API/Sampler.cpp index 7d6123d32..f71f8bcbd 100644 --- a/Source/Falcor/Core/API/Sampler.cpp +++ b/Source/Falcor/Core/API/Sampler.cpp @@ -29,6 +29,7 @@ #include "Device.h" #include "GFXAPI.h" #include "NativeHandleTraits.h" +#include "Core/ObjectPython.h" #include "Utils/Scripting/ScriptBindings.h" namespace Falcor @@ -90,7 +91,7 @@ gfx::TextureReductionOp getGFXReductionMode(Sampler::ReductionMode mode) gfx::ComparisonFunc getGFXComparisonFunc(ComparisonFunc func); -Sampler::Sampler(std::shared_ptr pDevice, const Desc& desc) : mpDevice(std::move(pDevice)), mDesc(desc) +Sampler::Sampler(ref pDevice, const Desc& desc) : mpDevice(pDevice), mDesc(desc) { gfx::ISamplerState::Desc gfxDesc = {}; gfxDesc.addressU = getGFXAddressMode(desc.addressModeU); @@ -119,9 +120,9 @@ Sampler::~Sampler() mpDevice->releaseResource(mGfxSamplerState); } -Sampler::SharedPtr Sampler::create(Device* pDevice, const Desc& desc) +ref Sampler::create(ref pDevice, const Desc& desc) { - return Sampler::SharedPtr(new Sampler(pDevice->shared_from_this(), desc)); + return ref(new Sampler(pDevice, desc)); } NativeHandle Sampler::getNativeHandle() const @@ -144,9 +145,14 @@ uint32_t Sampler::getApiMaxAnisotropy() return 16; } +void Sampler::breakStrongReferenceToDevice() +{ + mpDevice.breakStrongReference(); +} + FALCOR_SCRIPT_BINDING(Sampler) { - pybind11::class_(m, "Sampler"); + pybind11::class_>(m, "Sampler"); pybind11::enum_ filter(m, "SamplerFilter"); filter.value("Linear", Sampler::Filter::Linear); diff --git a/Source/Falcor/Core/API/Sampler.h b/Source/Falcor/Core/API/Sampler.h index 7b759f9d8..be279771c 100644 --- a/Source/Falcor/Core/API/Sampler.h +++ b/Source/Falcor/Core/API/Sampler.h @@ -31,19 +31,17 @@ #include "Handles.h" #include "NativeHandle.h" #include "Core/Macros.h" +#include "Core/Object.h" #include "Utils/Math/Vector.h" -#include namespace Falcor { /** * Abstract the API sampler state object */ -class FALCOR_API Sampler +class FALCOR_API Sampler : public Object { public: - using SharedPtr = std::shared_ptr; - /** * Filter mode */ @@ -102,24 +100,24 @@ class FALCOR_API Sampler /** * Set the filter mode - * @param[in] minFilter Filter mode in case of minification. - * @param[in] magFilter Filter mode in case of magnification. - * @param[in] mipFilter Mip-level sampling mode + * @param[in] minFilter_ Filter mode in case of minification. + * @param[in] magFilter_ Filter mode in case of magnification. + * @param[in] mipFilter_ Mip-level sampling mode */ - Desc& setFilterMode(Filter minFilter, Filter magFilter, Filter mipFilter) + Desc& setFilterMode(Filter minFilter_, Filter magFilter_, Filter mipFilter_) { - this->magFilter = magFilter; - this->minFilter = minFilter; - this->mipFilter = mipFilter; + magFilter = magFilter_; + minFilter = minFilter_; + mipFilter = mipFilter_; return *this; } /** * Set the maximum anisotropic filtering value. If MaxAnisotropy > 1, min/mag/mip filter modes are ignored */ - Desc& setMaxAnisotropy(uint32_t maxAnisotropy) + Desc& setMaxAnisotropy(uint32_t maxAnisotropy_) { - this->maxAnisotropy = maxAnisotropy; + maxAnisotropy = maxAnisotropy_; return *this; } @@ -129,11 +127,11 @@ class FALCOR_API Sampler * @param[in] maxLod Maximum LOD that will be used when sampling * @param[in] lodBias Bias to apply to the LOD */ - Desc& setLodParams(float minLod, float maxLod, float lodBias) + Desc& setLodParams(float minLod_, float maxLod_, float lodBias_) { - this->minLod = minLod; - this->maxLod = maxLod; - this->lodBias = lodBias; + minLod = minLod_; + maxLod = maxLod_; + lodBias = lodBias_; return *this; } @@ -142,7 +140,7 @@ class FALCOR_API Sampler */ Desc& setComparisonMode(ComparisonMode mode) { - this->comparisonMode = mode; + comparisonMode = mode; return *this; } @@ -151,7 +149,7 @@ class FALCOR_API Sampler */ Desc& setReductionMode(ReductionMode mode) { - this->reductionMode = mode; + reductionMode = mode; return *this; } @@ -163,18 +161,18 @@ class FALCOR_API Sampler */ Desc& setAddressingMode(AddressMode modeU, AddressMode modeV, AddressMode modeW) { - this->addressModeU = modeU; - this->addressModeV = modeV; - this->addressModeW = modeW; + addressModeU = modeU; + addressModeV = modeV; + addressModeW = modeW; return *this; } /** * Set the border color. Only applies when the addressing mode is ClampToBorder */ - Desc& setBorderColor(const float4& borderColor) + Desc& setBorderColor(const float4& borderColor_) { - this->borderColor = borderColor; + borderColor = borderColor_; return *this; } @@ -186,7 +184,7 @@ class FALCOR_API Sampler return magFilter == other.magFilter && minFilter == other.minFilter && mipFilter == other.mipFilter && maxAnisotropy == other.maxAnisotropy && maxLod == other.maxLod && minLod == other.minLod && lodBias == other.lodBias && comparisonMode == other.comparisonMode && reductionMode == other.reductionMode && addressModeU == other.addressModeU && - addressModeV == other.addressModeV && addressModeW == other.addressModeW && borderColor == other.borderColor; + addressModeV == other.addressModeV && addressModeW == other.addressModeW && all(borderColor == other.borderColor); } /** @@ -195,7 +193,7 @@ class FALCOR_API Sampler bool operator!=(const Desc& other) const { return !(*this == other); } }; - Sampler(std::shared_ptr pDevice, const Desc& desc); + Sampler(ref pDevice, const Desc& desc); ~Sampler(); /** @@ -203,7 +201,7 @@ class FALCOR_API Sampler * @param[in] desc Describes sampler settings. * @return A new object, or throws an exception if creation failed. */ - static SharedPtr create(Device* pDevice, const Desc& desc); + static ref create(ref pDevice, const Desc& desc); /** * Get the sampler state. @@ -287,10 +285,14 @@ class FALCOR_API Sampler */ const Desc& getDesc() const { return mDesc; } + void breakStrongReferenceToDevice(); + private: - std::shared_ptr mpDevice; + BreakableReference mpDevice; Desc mDesc; Slang::ComPtr mGfxSamplerState; static uint32_t getApiMaxAnisotropy(); + + friend class Device; }; } // namespace Falcor diff --git a/Source/Falcor/Core/API/Shader.cpp b/Source/Falcor/Core/API/Shader.cpp index 22207d0eb..3d39cd105 100644 --- a/Source/Falcor/Core/API/Shader.cpp +++ b/Source/Falcor/Core/API/Shader.cpp @@ -59,7 +59,12 @@ Shader::Shader(ShaderType type) : mType(type) Shader::~Shader() {} -bool Shader::init(ComPtr slangEntryPoint, const std::string& entryPointName, CompilerFlags flags, std::string& log) +bool Shader::init( + Slang::ComPtr slangEntryPoint, + const std::string& entryPointName, + CompilerFlags flags, + std::string& log +) { // In GFX, we do not generate actual shader code at program creation. // The actual shader code will only be generated and cached when all specialization arguments diff --git a/Source/Falcor/Core/API/Shader.h b/Source/Falcor/Core/API/Shader.h index 8ca963965..39a74a10a 100644 --- a/Source/Falcor/Core/API/Shader.h +++ b/Source/Falcor/Core/API/Shader.h @@ -26,9 +26,10 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **************************************************************************/ #pragma once -#include "GFXAPI.h" +#include "Handles.h" #include "Core/Macros.h" #include "Core/Assert.h" +#include "Core/Object.h" #include #include @@ -40,140 +41,6 @@ struct ISlangBlob; namespace Falcor { -/** - * Minimal smart pointer for working with COM objects. - */ -template -struct FALCOR_API ComPtr -{ -public: - /// Type of the smart pointer itself - typedef ComPtr ThisType; - - /// Initialize to a null pointer. - ComPtr() : mpObject(nullptr) {} - ComPtr(std::nullptr_t) : mpObject(nullptr) {} - - /// Release reference to the pointed-to object. - ~ComPtr() - { - if (mpObject) - (mpObject)->Release(); - } - - /// Add a new reference to an existing object. - explicit ComPtr(T* pObject) : mpObject(pObject) - { - if (pObject) - (pObject)->AddRef(); - } - - /// Add a new reference to an existing object. - ComPtr(const ThisType& rhs) : mpObject(rhs.mpObject) - { - if (mpObject) - (mpObject)->AddRef(); - } - - /// Add a new reference to an existing object - T* operator=(T* in) - { - if (in) - in->AddRef(); - if (mpObject) - mpObject->Release(); - mpObject = in; - return in; - } - - /// Add a new reference to an existing object - const ThisType& operator=(const ThisType& rhs) - { - if (rhs.mpObject) - rhs.mpObject->AddRef(); - if (mpObject) - mpObject->Release(); - mpObject = rhs.mpObject; - return *this; - } - - /// Transfer ownership of a reference. - ComPtr(ThisType&& rhs) : mpObject(rhs.mpObject) { rhs.mpObject = nullptr; } - - /// Transfer ownership of a reference. - ComPtr& operator=(ThisType&& rhs) - { - T* swap = mpObject; - mpObject = rhs.mpObject; - rhs.mpObject = swap; - return *this; - } - - /// Clear out object pointer. - void setNull() - { - if (mpObject) - { - mpObject->Release(); - mpObject = nullptr; - } - } - - /// Swap pointers with another reference. - void swap(ThisType& rhs) - { - T* tmp = mpObject; - mpObject = rhs.mpObject; - rhs.mpObject = tmp; - } - - /// Get the underlying object pointer. - T* get() const { return mpObject; } - - /// Cast to object pointer type. - operator T*() const { return mpObject; } - - /// Access members of underlying object. - T* operator->() const { return mpObject; } - - /// Dereference underlying pointer. - T& operator*() { return *mpObject; } - - /// Transfer ownership of reference to the caller. - T* detach() - { - T* ptr = mpObject; - mpObject = nullptr; - return ptr; - } - - /// Transfer ownership of reference from the caller. - void attach(T* in) - { - T* old = mpObject; - mpObject = in; - if (old) - old->Release(); - } - - /// Get a writable reference suitable for use as a function output argument. - T** writeRef() - { - setNull(); - return &mpObject; - } - - /// Get a readable reference suitable for use as a function input argument. - T* const* readRef() const { return &mpObject; } - -protected: - // Disabled: do not take the address of a smart pointer. - T** operator&(); - - /// The underlying raw object pointer - T* mpObject; -}; - /** * Falcor shader types */ @@ -243,12 +110,10 @@ struct ShaderData; * Low-level shader object * This class abstracts the API's shader creation and management */ -class FALCOR_API Shader +class FALCOR_API Shader : public Object { public: - using SharedPtr = std::shared_ptr; - - typedef ComPtr Blob; + typedef Slang::ComPtr Blob; enum class CompilerFlags { @@ -327,13 +192,13 @@ class FALCOR_API Shader std::string mTypeName; std::string mInterfaceName; TypeConformance() = default; - TypeConformance(std::string const& typeName, std::string const& interfaceName) : mTypeName(typeName), mInterfaceName(interfaceName) + TypeConformance(const std::string& typeName, const std::string& interfaceName) : mTypeName(typeName), mInterfaceName(interfaceName) {} - bool operator<(TypeConformance const& other) const + bool operator<(const TypeConformance& other) const { return mTypeName < other.mTypeName || (mTypeName == other.mTypeName && mInterfaceName < other.mInterfaceName); } - bool operator==(TypeConformance const& other) const + bool operator==(const TypeConformance& other) const { return mTypeName == other.mTypeName && mInterfaceName == other.mInterfaceName; } @@ -409,15 +274,15 @@ class FALCOR_API Shader * @param[out] log This string will contain the error log message in case shader compilation failed * @return If success, a new shader object, otherwise nullptr */ - static SharedPtr create( - ComPtr linkedSlangEntryPoint, + static ref create( + Slang::ComPtr linkedSlangEntryPoint, ShaderType type, - std::string const& entryPointName, + const std::string& entryPointName, CompilerFlags flags, std::string& log ) { - SharedPtr pShader = SharedPtr(new Shader(type)); + ref pShader = ref(new Shader(type)); pShader->mEntryPointName = entryPointName; return pShader->init(linkedSlangEntryPoint, entryPointName, flags, log) ? pShader : nullptr; } @@ -439,7 +304,7 @@ class FALCOR_API Shader protected: // API handle depends on the shader Type, so it stored be stored as part of the private data bool init( - ComPtr linkedSlangEntryPoint, + Slang::ComPtr linkedSlangEntryPoint, const std::string& entryPointName, CompilerFlags flags, std::string& log diff --git a/Source/Falcor/Core/API/ShaderTable.h b/Source/Falcor/Core/API/ShaderTable.h index 895355349..de8464e03 100644 --- a/Source/Falcor/Core/API/ShaderTable.h +++ b/Source/Falcor/Core/API/ShaderTable.h @@ -67,7 +67,7 @@ class RenderContext; class ShaderTablePtr { public: - ShaderTablePtr(std::shared_ptr pDevice) : mpDevice(std::move(pDevice)) {} + ShaderTablePtr(ref pDevice) : mpDevice(pDevice) {} gfx::IShaderTable& operator*() { return *mTable; } @@ -82,7 +82,7 @@ class ShaderTablePtr ~ShaderTablePtr() { mpDevice->releaseResource(mTable); } private: - std::shared_ptr mpDevice; + ref mpDevice; Slang::ComPtr mTable; }; } // namespace Falcor diff --git a/Source/Falcor/Core/API/Shared/D3D12ConstantBufferView.cpp b/Source/Falcor/Core/API/Shared/D3D12ConstantBufferView.cpp index e95415b8c..3efae6ea8 100644 --- a/Source/Falcor/Core/API/Shared/D3D12ConstantBufferView.cpp +++ b/Source/Falcor/Core/API/Shared/D3D12ConstantBufferView.cpp @@ -32,19 +32,19 @@ namespace Falcor { -D3D12DescriptorSet::SharedPtr createCbvDescriptor(Device* pDevice, const D3D12_CONSTANT_BUFFER_VIEW_DESC& desc) +ref createCbvDescriptor(ref pDevice, const D3D12_CONSTANT_BUFFER_VIEW_DESC& desc) { pDevice->requireD3D12(); D3D12DescriptorSetLayout layout; layout.addRange(ShaderResourceType::Cbv, 0, 1); - D3D12DescriptorSet::SharedPtr handle = D3D12DescriptorSet::create(pDevice, pDevice->getD3D12CpuDescriptorPool(), layout); + ref handle = D3D12DescriptorSet::create(pDevice, pDevice->getD3D12CpuDescriptorPool(), layout); pDevice->getNativeHandle().as()->CreateConstantBufferView(&desc, handle->getCpuHandle(0)); return handle; } -D3D12ConstantBufferView::SharedPtr D3D12ConstantBufferView::create(Device* pDevice, Buffer::SharedPtr pBuffer) +ref D3D12ConstantBufferView::create(ref pDevice, ref pBuffer) { FALCOR_ASSERT(pDevice); pDevice->requireD3D12(); @@ -54,10 +54,10 @@ D3D12ConstantBufferView::SharedPtr D3D12ConstantBufferView::create(Device* pDevi desc.BufferLocation = pBuffer->getGpuAddress(); desc.SizeInBytes = (uint32_t)pBuffer->getSize(); - return SharedPtr(new D3D12ConstantBufferView(pBuffer, createCbvDescriptor(pDevice, desc))); + return ref(new D3D12ConstantBufferView(pBuffer, createCbvDescriptor(pDevice, desc))); } -D3D12ConstantBufferView::SharedPtr D3D12ConstantBufferView::create(Device* pDevice) +ref D3D12ConstantBufferView::create(ref pDevice) { FALCOR_ASSERT(pDevice); pDevice->requireD3D12(); @@ -68,7 +68,7 @@ D3D12ConstantBufferView::SharedPtr D3D12ConstantBufferView::create(Device* pDevi // Create a null view. D3D12_CONSTANT_BUFFER_VIEW_DESC desc = {}; - return SharedPtr(new D3D12ConstantBufferView(std::weak_ptr(), createCbvDescriptor(pDevice, desc))); + return ref(new D3D12ConstantBufferView(nullptr, createCbvDescriptor(pDevice, desc))); } D3D12_CPU_DESCRIPTOR_HANDLE D3D12ConstantBufferView::getD3D12CpuHeapHandle() const diff --git a/Source/Falcor/Core/API/Shared/D3D12ConstantBufferView.h b/Source/Falcor/Core/API/Shared/D3D12ConstantBufferView.h index 62d0bb339..264b3ecdb 100644 --- a/Source/Falcor/Core/API/Shared/D3D12ConstantBufferView.h +++ b/Source/Falcor/Core/API/Shared/D3D12ConstantBufferView.h @@ -29,6 +29,7 @@ #include "D3D12DescriptorSet.h" #include "Core/API/Buffer.h" #include "Core/Macros.h" +#include "Core/Object.h" #include @@ -37,13 +38,11 @@ namespace Falcor // GFX doesn't need constant buffer view. // We provide a raw D3D12 implementation for applications // that wish to use the raw D3D12DescriptorSet API. -class FALCOR_API D3D12ConstantBufferView +class FALCOR_API D3D12ConstantBufferView : public Object { public: - using SharedPtr = std::shared_ptr; - - static SharedPtr create(Device* pDevice, Buffer::SharedPtr pBuffer); - static SharedPtr create(Device* pDevice); + static ref create(ref pDevice, ref pBuffer); + static ref create(ref pDevice); /** * Get the D3D12 CPU descriptor handle representing this resource view. @@ -52,11 +51,11 @@ class FALCOR_API D3D12ConstantBufferView D3D12_CPU_DESCRIPTOR_HANDLE getD3D12CpuHeapHandle() const; private: - D3D12ConstantBufferView(std::weak_ptr pBuffer, D3D12DescriptorSet::SharedPtr pDescriptorSet) + D3D12ConstantBufferView(ref pBuffer, ref pDescriptorSet) : mpBuffer(pBuffer), mpDescriptorSet(pDescriptorSet) {} - std::weak_ptr mpBuffer; - D3D12DescriptorSet::SharedPtr mpDescriptorSet; + ref mpBuffer; + ref mpDescriptorSet; }; } // namespace Falcor diff --git a/Source/Falcor/Core/API/Shared/D3D12DescriptorData.h b/Source/Falcor/Core/API/Shared/D3D12DescriptorData.h index f5f167f3d..bb9f026d0 100644 --- a/Source/Falcor/Core/API/Shared/D3D12DescriptorData.h +++ b/Source/Falcor/Core/API/Shared/D3D12DescriptorData.h @@ -36,7 +36,7 @@ namespace Falcor struct DescriptorPoolApiData { static constexpr size_t kHeapCount = D3D12_DESCRIPTOR_HEAP_TYPE_NUM_TYPES; - D3D12DescriptorHeap::SharedPtr pHeaps[kHeapCount]; + ref pHeaps[kHeapCount]; }; struct DescriptorSetApiData diff --git a/Source/Falcor/Core/API/Shared/D3D12DescriptorHeap.cpp b/Source/Falcor/Core/API/Shared/D3D12DescriptorHeap.cpp index b23920694..6b94e6fe8 100644 --- a/Source/Falcor/Core/API/Shared/D3D12DescriptorHeap.cpp +++ b/Source/Falcor/Core/API/Shared/D3D12DescriptorHeap.cpp @@ -31,12 +31,7 @@ namespace Falcor { -D3D12DescriptorHeap::D3D12DescriptorHeap( - std::shared_ptr pDevice, - D3D12_DESCRIPTOR_HEAP_TYPE type, - uint32_t chunkCount, - bool shaderVisible -) +D3D12DescriptorHeap::D3D12DescriptorHeap(Device* pDevice, D3D12_DESCRIPTOR_HEAP_TYPE type, uint32_t chunkCount, bool shaderVisible) : mMaxChunkCount(chunkCount), mType(type), mShaderVisible(shaderVisible) { ID3D12Device* pD3D12Device = pDevice->getNativeHandle().as(); @@ -46,7 +41,7 @@ D3D12DescriptorHeap::D3D12DescriptorHeap( D3D12DescriptorHeap::~D3D12DescriptorHeap() = default; -D3D12DescriptorHeap::SharedPtr D3D12DescriptorHeap::create( +ref D3D12DescriptorHeap::create( Device* pDevice, D3D12_DESCRIPTOR_HEAP_TYPE type, uint32_t descCount, @@ -58,7 +53,7 @@ D3D12DescriptorHeap::SharedPtr D3D12DescriptorHeap::create( ID3D12Device* pD3D12Device = pDevice->getNativeHandle().as(); uint32_t chunkCount = (descCount + kDescPerChunk - 1) / kDescPerChunk; - D3D12DescriptorHeap::SharedPtr pHeap = SharedPtr(new D3D12DescriptorHeap(pDevice->shared_from_this(), type, chunkCount, shaderVisible)); + ref pHeap = ref(new D3D12DescriptorHeap(pDevice, type, chunkCount, shaderVisible)); D3D12_DESCRIPTOR_HEAP_DESC desc = {}; desc.Flags = shaderVisible ? D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE : D3D12_DESCRIPTOR_HEAP_FLAG_NONE; @@ -110,7 +105,8 @@ D3D12DescriptorHeap::Allocation::SharedPtr D3D12DescriptorHeap::allocateDescript return nullptr; } - Allocation::SharedPtr pAlloc = Allocation::create(shared_from_this(), mpCurrentChunk->getCurrentAbsoluteIndex(), count, mpCurrentChunk); + Allocation::SharedPtr pAlloc = + Allocation::create(ref(this), mpCurrentChunk->getCurrentAbsoluteIndex(), count, mpCurrentChunk); // Update the chunk mpCurrentChunk->allocCount++; @@ -183,7 +179,7 @@ void D3D12DescriptorHeap::releaseChunk(Chunk::SharedPtr pChunk) } D3D12DescriptorHeap::Allocation::SharedPtr D3D12DescriptorHeap::Allocation::create( - D3D12DescriptorHeap::SharedPtr pHeap, + ref pHeap, uint32_t baseIndex, uint32_t descCount, std::shared_ptr pChunk @@ -193,7 +189,7 @@ D3D12DescriptorHeap::Allocation::SharedPtr D3D12DescriptorHeap::Allocation::crea } D3D12DescriptorHeap::Allocation::Allocation( - D3D12DescriptorHeap::SharedPtr pHeap, + ref pHeap, uint32_t baseIndex, uint32_t descCount, std::shared_ptr pChunk diff --git a/Source/Falcor/Core/API/Shared/D3D12DescriptorHeap.h b/Source/Falcor/Core/API/Shared/D3D12DescriptorHeap.h index 8f2ac270c..64e39d665 100644 --- a/Source/Falcor/Core/API/Shared/D3D12DescriptorHeap.h +++ b/Source/Falcor/Core/API/Shared/D3D12DescriptorHeap.h @@ -28,6 +28,7 @@ #pragma once #include "D3D12Handles.h" #include "Core/Assert.h" +#include "Core/Object.h" #include "Core/API/fwd.h" #include #include @@ -37,16 +38,15 @@ namespace Falcor { -class D3D12DescriptorHeap : public std::enable_shared_from_this +class D3D12DescriptorHeap : public Object { public: - using SharedPtr = std::shared_ptr; using ApiHandle = ID3D12DescriptorHeapPtr; using CpuHandle = D3D12_CPU_DESCRIPTOR_HANDLE; using GpuHandle = D3D12_GPU_DESCRIPTOR_HANDLE; ~D3D12DescriptorHeap(); - static const uint32_t kDescPerChunk = 64; + static constexpr uint32_t kDescPerChunk = 64; /** * Create a new descriptor heap. @@ -56,7 +56,7 @@ class D3D12DescriptorHeap : public std::enable_shared_from_this create(Device* pDevice, D3D12_DESCRIPTOR_HEAP_TYPE type, uint32_t descCount, bool shaderVisible = true); CpuHandle getBaseCpuHandle() const { return mCpuHeapStart; } GpuHandle getBaseGpuHandle() const; @@ -89,14 +89,9 @@ class D3D12DescriptorHeap : public std::enable_shared_from_this pChunk - ); - Allocation(D3D12DescriptorHeap::SharedPtr pHeap, uint32_t baseIndex, uint32_t descCount, std::shared_ptr pChunk); - D3D12DescriptorHeap::SharedPtr mpHeap; + static SharedPtr create(ref pHeap, uint32_t baseIndex, uint32_t descCount, std::shared_ptr pChunk); + Allocation(ref pHeap, uint32_t baseIndex, uint32_t descCount, std::shared_ptr pChunk); + ref mpHeap; uint32_t mBaseIndex; uint32_t mDescCount; std::shared_ptr mpChunk; @@ -111,7 +106,7 @@ class D3D12DescriptorHeap : public std::enable_shared_from_this pDevice, D3D12_DESCRIPTOR_HEAP_TYPE type, uint32_t chunkCount, bool shaderVisible); + D3D12DescriptorHeap(Device* pDevice, D3D12_DESCRIPTOR_HEAP_TYPE type, uint32_t chunkCount, bool shaderVisible); CpuHandle getCpuHandle(uint32_t index) const; GpuHandle getGpuHandle(uint32_t index) const; // Only valid if heap is shader visible diff --git a/Source/Falcor/Core/API/Shared/D3D12DescriptorPool.cpp b/Source/Falcor/Core/API/Shared/D3D12DescriptorPool.cpp index ceaa24524..9b4e22ff0 100644 --- a/Source/Falcor/Core/API/Shared/D3D12DescriptorPool.cpp +++ b/Source/Falcor/Core/API/Shared/D3D12DescriptorPool.cpp @@ -64,15 +64,14 @@ const D3D12DescriptorPool::ApiHandle& D3D12DescriptorPool::getApiHandle(uint32_t return mpApiData->pHeaps[heapIndex]->getApiHandle(); } -D3D12DescriptorPool::SharedPtr D3D12DescriptorPool::create(Device* pDevice, const Desc& desc, const GpuFence::SharedPtr& pFence) +ref D3D12DescriptorPool::create(Device* pDevice, const Desc& desc, ref pFence) { FALCOR_ASSERT(pDevice); pDevice->requireD3D12(); - return SharedPtr(new D3D12DescriptorPool(pDevice, desc, pFence)); + return ref(new D3D12DescriptorPool(pDevice, desc, pFence)); } -D3D12DescriptorPool::D3D12DescriptorPool(Device* pDevice, const Desc& desc, const GpuFence::SharedPtr& pFence) - : mDesc(desc), mpFence(pFence) +D3D12DescriptorPool::D3D12DescriptorPool(Device* pDevice, const Desc& desc, ref pFence) : mDesc(desc), mpFence(pFence) { // Find out how many heaps we need static_assert(D3D12DescriptorPool::kTypeCount == 13, "Unexpected desc count, make sure all desc types are supported"); diff --git a/Source/Falcor/Core/API/Shared/D3D12DescriptorPool.h b/Source/Falcor/Core/API/Shared/D3D12DescriptorPool.h index 1169f88a4..c9b157b55 100644 --- a/Source/Falcor/Core/API/Shared/D3D12DescriptorPool.h +++ b/Source/Falcor/Core/API/Shared/D3D12DescriptorPool.h @@ -28,6 +28,7 @@ #pragma once #include "D3D12Handles.h" #include "Core/Macros.h" +#include "Core/Object.h" #include "Core/API/ShaderResourceType.h" #include "Core/API/GpuFence.h" #include @@ -38,10 +39,9 @@ namespace Falcor struct DescriptorPoolApiData; struct DescriptorSetApiData; -class FALCOR_API D3D12DescriptorPool +class FALCOR_API D3D12DescriptorPool : public Object { public: - using SharedPtr = std::shared_ptr; using ApiHandle = ID3D12DescriptorHeapPtr; using CpuHandle = D3D12_CPU_DESCRIPTOR_HANDLE; using GpuHandle = D3D12_GPU_DESCRIPTOR_HANDLE; @@ -50,7 +50,7 @@ class FALCOR_API D3D12DescriptorPool ~D3D12DescriptorPool(); - static const uint32_t kTypeCount = uint32_t(Type::Count); + static constexpr uint32_t kTypeCount = uint32_t(Type::Count); class FALCOR_API Desc { @@ -84,7 +84,7 @@ class FALCOR_API D3D12DescriptorPool * @param[in] pFence Fence object for synchronization. * @return A new object, or throws an exception if creation failed. */ - static SharedPtr create(Device* pDevice, const Desc& desc, const GpuFence::SharedPtr& pFence); + static ref create(Device* pDevice, const Desc& desc, ref pFence); uint32_t getDescCount(Type type) const { return mDesc.mDescCount[(uint32_t)type]; } uint32_t getTotalDescCount() const { return mDesc.mTotalDescCount; } @@ -95,11 +95,11 @@ class FALCOR_API D3D12DescriptorPool private: friend class D3D12DescriptorSet; - D3D12DescriptorPool(Device* pDevice, const Desc& desc, const GpuFence::SharedPtr& pFence); + D3D12DescriptorPool(Device* pDevice, const Desc& desc, ref pFence); void releaseAllocation(std::shared_ptr pData); Desc mDesc; std::shared_ptr mpApiData; - GpuFence::SharedPtr mpFence; + ref mpFence; struct DeferredRelease { diff --git a/Source/Falcor/Core/API/Shared/D3D12DescriptorSet.cpp b/Source/Falcor/Core/API/Shared/D3D12DescriptorSet.cpp index 605902d6e..7cc61d248 100644 --- a/Source/Falcor/Core/API/Shared/D3D12DescriptorSet.cpp +++ b/Source/Falcor/Core/API/Shared/D3D12DescriptorSet.cpp @@ -137,7 +137,7 @@ void D3D12DescriptorSet::bindForGraphics(CopyContext* pCtx, const D3D12RootSigna mpApiData->pAllocation->getHeap()->getShaderVisible() == false && "DescriptorSet must be created on CPU heap for bind operation in GFX." ); - ComPtr commandBufferD3D12; + Slang::ComPtr commandBufferD3D12; pCtx->getLowLevelData()->getGfxCommandBuffer()->queryInterface( SlangUUID SLANG_UUID_ICommandBufferD3D12, (void**)commandBufferD3D12.writeRef() ); @@ -155,7 +155,7 @@ void D3D12DescriptorSet::bindForCompute(CopyContext* pCtx, const D3D12RootSignat mpApiData->pAllocation->getHeap()->getShaderVisible() == false && "DescriptorSet must be created on CPU heap for bind operation in GFX." ); - ComPtr commandBufferD3D12; + Slang::ComPtr commandBufferD3D12; pCtx->getLowLevelData()->getGfxCommandBuffer()->queryInterface( SlangUUID SLANG_UUID_ICommandBufferD3D12, (void**)commandBufferD3D12.writeRef() ); @@ -173,39 +173,35 @@ void D3D12DescriptorSet::setCbv(uint32_t rangeIndex, uint32_t descIndex, D3D12Co setCpuHandle(rangeIndex, descIndex, pView->getD3D12CpuHeapHandle()); } -D3D12DescriptorSet::SharedPtr D3D12DescriptorSet::create( - Device* pDevice, - const D3D12DescriptorPool::SharedPtr& pPool, +ref D3D12DescriptorSet::create( + ref pDevice, + ref pPool, const D3D12DescriptorSetLayout& layout ) { FALCOR_ASSERT(pDevice); pDevice->requireD3D12(); - return SharedPtr(new D3D12DescriptorSet(pDevice->shared_from_this(), pPool, layout)); + return ref(new D3D12DescriptorSet(pDevice, pPool, layout)); } -D3D12DescriptorSet::SharedPtr D3D12DescriptorSet::create( - Device* pDevice, +ref D3D12DescriptorSet::create( + ref pDevice, const D3D12DescriptorSetLayout& layout, D3D12DescriptorSetBindingUsage bindingUsage ) { FALCOR_ASSERT(pDevice); pDevice->requireD3D12(); - return SharedPtr(new D3D12DescriptorSet( - pDevice->shared_from_this(), + return ref(new D3D12DescriptorSet( + pDevice, bindingUsage == D3D12DescriptorSetBindingUsage::RootSignatureOffset ? pDevice->getD3D12GpuDescriptorPool() : pDevice->getD3D12CpuDescriptorPool(), layout )); } -D3D12DescriptorSet::D3D12DescriptorSet( - std::shared_ptr pDevice, - D3D12DescriptorPool::SharedPtr pPool, - const D3D12DescriptorSetLayout& layout -) - : mpDevice(std::move(pDevice)), mLayout(layout), mpPool(pPool) +D3D12DescriptorSet::D3D12DescriptorSet(ref pDevice, ref pPool, const D3D12DescriptorSetLayout& layout) + : mpDevice(pDevice), mLayout(layout), mpPool(pPool) { mpApiData = std::make_shared(); uint32_t count = 0; diff --git a/Source/Falcor/Core/API/Shared/D3D12DescriptorSet.h b/Source/Falcor/Core/API/Shared/D3D12DescriptorSet.h index 31adff5e3..c1522a37a 100644 --- a/Source/Falcor/Core/API/Shared/D3D12DescriptorSet.h +++ b/Source/Falcor/Core/API/Shared/D3D12DescriptorSet.h @@ -29,6 +29,7 @@ #include "D3D12DescriptorSetLayout.h" #include "D3D12DescriptorPool.h" #include "Core/Macros.h" +#include "Core/Object.h" #include "Core/API/fwd.h" #include "Core/API/Shader.h" #include @@ -51,10 +52,9 @@ enum class D3D12DescriptorSetBindingUsage RootSignatureOffset //< The descriptor set will be implicitly bound via a root signature offsets. }; -class FALCOR_API D3D12DescriptorSet +class FALCOR_API D3D12DescriptorSet : public Object { public: - using SharedPtr = std::shared_ptr; using Type = ShaderResourceType; using CpuHandle = D3D12DescriptorPool::CpuHandle; using GpuHandle = D3D12DescriptorPool::GpuHandle; @@ -69,7 +69,7 @@ class FALCOR_API D3D12DescriptorSet * @param[in] layout The layout. * @return A new object, or throws an exception if creation failed. */ - static SharedPtr create(Device* pDevice, const D3D12DescriptorPool::SharedPtr& pPool, const D3D12DescriptorSetLayout& layout); + static ref create(ref pDevice, ref pPool, const D3D12DescriptorSetLayout& layout); /** * Create a new descriptor set with a specified binding usage flag. @@ -82,8 +82,8 @@ class FALCOR_API D3D12DescriptorSet * @param[in] bindingUsage The mechanism that will be used to bind this descriptor set. * @return A new object, or throws an exception if creation failed. */ - static SharedPtr create( - Device* pDevice, + static ref create( + ref pDevice, const D3D12DescriptorSetLayout& layout, D3D12DescriptorSetBindingUsage bindingUsage = D3D12DescriptorSetBindingUsage::ExplicitBind ); @@ -106,11 +106,11 @@ class FALCOR_API D3D12DescriptorSet void bindForCompute(CopyContext* pCtx, const D3D12RootSignature* pRootSig, uint32_t rootIndex); private: - D3D12DescriptorSet(std::shared_ptr pDevice, D3D12DescriptorPool::SharedPtr pPool, const D3D12DescriptorSetLayout& layout); + D3D12DescriptorSet(ref pDevice, ref pPool, const D3D12DescriptorSetLayout& layout); - std::shared_ptr mpDevice; + ref mpDevice; D3D12DescriptorSetLayout mLayout; std::shared_ptr mpApiData; - D3D12DescriptorPool::SharedPtr mpPool; + ref mpPool; }; } // namespace Falcor diff --git a/Source/Falcor/Core/API/Shared/D3D12RootSignature.cpp b/Source/Falcor/Core/API/Shared/D3D12RootSignature.cpp index c3a02d4d7..e793359ed 100644 --- a/Source/Falcor/Core/API/Shared/D3D12RootSignature.cpp +++ b/Source/Falcor/Core/API/Shared/D3D12RootSignature.cpp @@ -257,7 +257,7 @@ D3D12RootSignature::Desc& D3D12RootSignature::Desc::addRootConstants(uint32_t re return *this; } -D3D12RootSignature::D3D12RootSignature(std::shared_ptr pDevice, const Desc& desc) : mpDevice(std::move(pDevice)), mDesc(desc) +D3D12RootSignature::D3D12RootSignature(ref pDevice, const Desc& desc) : mpDevice(pDevice), mDesc(desc) { // Get vector of root parameters RootSignatureParams params; @@ -300,11 +300,11 @@ D3D12RootSignature::D3D12RootSignature(std::shared_ptr pDevice, const De D3D12RootSignature::~D3D12RootSignature() {} -D3D12RootSignature::SharedPtr D3D12RootSignature::create(Device* pDevice, const Desc& desc) +ref D3D12RootSignature::create(ref pDevice, const Desc& desc) { FALCOR_ASSERT(pDevice); pDevice->requireD3D12(); - return SharedPtr(new D3D12RootSignature(pDevice->shared_from_this(), desc)); + return ref(new D3D12RootSignature(pDevice, desc)); } ReflectionResourceType::ShaderAccess getRequiredShaderAccess(D3D12RootSignature::DescType type) @@ -399,7 +399,7 @@ static void addRootDescriptors(const ParameterBlockReflection* pBlock, D3D12Root } } -D3D12RootSignature::SharedPtr D3D12RootSignature::create(Device* pDevice, const ProgramReflection* pReflector) +ref D3D12RootSignature::create(ref pDevice, const ProgramReflection* pReflector) { FALCOR_ASSERT(pReflector); D3D12RootSignature::Desc d; @@ -437,4 +437,5 @@ void D3D12RootSignature::bindForGraphics(CopyContext* pCtx) { bindRootSigCommon(pCtx, mApiHandle); } + } // namespace Falcor diff --git a/Source/Falcor/Core/API/Shared/D3D12RootSignature.h b/Source/Falcor/Core/API/Shared/D3D12RootSignature.h index 8322ed8e3..86cdeaefe 100644 --- a/Source/Falcor/Core/API/Shared/D3D12RootSignature.h +++ b/Source/Falcor/Core/API/Shared/D3D12RootSignature.h @@ -29,6 +29,7 @@ #include "D3D12Handles.h" #include "D3D12DescriptorSet.h" #include "Core/Macros.h" +#include "Core/Object.h" #include "Core/API/ShaderResourceType.h" #include #include @@ -54,13 +55,10 @@ class CopyContext; * The get*BaseIndex() functions return the base index of the * corresponding root parameter type in the root signature. */ -class FALCOR_API D3D12RootSignature +class FALCOR_API D3D12RootSignature : public Object { public: - using SharedPtr = std::shared_ptr; - using SharedConstPtr = std::shared_ptr; using ApiHandle = ID3D12RootSignaturePtr; - using DescType = ShaderResourceType; struct RootDescriptorDesc @@ -111,14 +109,14 @@ class FALCOR_API D3D12RootSignature * @param[in] desc Root signature description. * @return New object, or throws an exception if creation failed. */ - static SharedPtr create(Device* pDevice, const Desc& desc); + static ref create(ref pDevice, const Desc& desc); /** * Create a root signature from program reflection. * @param[in] pReflection Reflection object. * @return New object, or throws an exception if creation failed. */ - static SharedPtr create(Device* pDevice, const ProgramReflection* pReflection); + static ref create(ref pDevice, const ProgramReflection* pReflection); const ApiHandle& getApiHandle() const { return mApiHandle; } @@ -138,9 +136,9 @@ class FALCOR_API D3D12RootSignature const Desc& getDesc() const { return mDesc; } protected: - D3D12RootSignature(std::shared_ptr pDevice, const Desc& desc); + D3D12RootSignature(ref pDevice, const Desc& desc); void createApiHandle(ID3DBlobPtr pSigBlob); - std::shared_ptr mpDevice; + ref mpDevice; Desc mDesc; ApiHandle mApiHandle; diff --git a/Source/Falcor/Core/API/Shared/MockedD3D12StagingBuffer.cpp b/Source/Falcor/Core/API/Shared/MockedD3D12StagingBuffer.cpp index 6322700f0..4ea5034ad 100644 --- a/Source/Falcor/Core/API/Shared/MockedD3D12StagingBuffer.cpp +++ b/Source/Falcor/Core/API/Shared/MockedD3D12StagingBuffer.cpp @@ -31,7 +31,7 @@ namespace Falcor { -void MockedD3D12StagingBuffer::resize(Device* pDevice, size_t size) +void MockedD3D12StagingBuffer::resize(ref pDevice, size_t size) { mData.resize(size); mpGpuBuffer = Buffer::create( diff --git a/Source/Falcor/Core/API/Shared/MockedD3D12StagingBuffer.h b/Source/Falcor/Core/API/Shared/MockedD3D12StagingBuffer.h index 84a024d13..5df900835 100644 --- a/Source/Falcor/Core/API/Shared/MockedD3D12StagingBuffer.h +++ b/Source/Falcor/Core/API/Shared/MockedD3D12StagingBuffer.h @@ -59,7 +59,7 @@ namespace Falcor class FALCOR_API MockedD3D12StagingBuffer : public ID3D12Resource { public: - void resize(Device* pDevice, size_t size); + void resize(ref pDevice, size_t size); size_t getSize() const { return mData.size(); } const void* getData() const { return mData.data(); } @@ -94,8 +94,8 @@ class FALCOR_API MockedD3D12StagingBuffer : public ID3D12Resource virtual HRESULT __stdcall GetHeapProperties(D3D12_HEAP_PROPERTIES* pHeapProperties, D3D12_HEAP_FLAGS* pHeapFlags) override; private: - std::vector mData; // CPU Buffer. - Buffer::SharedPtr mpGpuBuffer; // GPU Buffer. + std::vector mData; // CPU Buffer. + ref mpGpuBuffer; // GPU Buffer. }; } // namespace Falcor #endif // FALCOR_HAS_D3D12 diff --git a/Source/Falcor/Core/API/Swapchain.cpp b/Source/Falcor/Core/API/Swapchain.cpp index 23f13b8cf..6f9d6442a 100644 --- a/Source/Falcor/Core/API/Swapchain.cpp +++ b/Source/Falcor/Core/API/Swapchain.cpp @@ -33,8 +33,7 @@ namespace Falcor { -Swapchain::Swapchain(std::shared_ptr pDevice, const Desc& desc, WindowHandle windowHandle) - : mpDevice(std::move(pDevice)), mDesc(desc) +Swapchain::Swapchain(ref pDevice, const Desc& desc, WindowHandle windowHandle) : mpDevice(pDevice), mDesc(desc) { FALCOR_ASSERT(mpDevice); @@ -60,7 +59,7 @@ Swapchain::Swapchain(std::shared_ptr pDevice, const Desc& desc, WindowHa prepareImages(); } -const Texture::SharedPtr& Swapchain::getImage(uint32_t index) const +const ref& Swapchain::getImage(uint32_t index) const { FALCOR_ASSERT(index <= mImages.size()); return mImages[index]; @@ -104,8 +103,8 @@ void Swapchain::prepareImages() Slang::ComPtr resource; FALCOR_GFX_CALL(mGfxSwapchain->getImage(i, resource.writeRef())); mImages.push_back(Texture::createFromResource( - mpDevice.get(), resource, Texture::Type::Texture2D, mDesc.width, mDesc.height, 1, mDesc.format, 1, 1, 1, - Resource::State::Undefined, Texture::BindFlags::RenderTarget + mpDevice, resource, Texture::Type::Texture2D, mDesc.width, mDesc.height, 1, mDesc.format, 1, 1, 1, Resource::State::Undefined, + Texture::BindFlags::RenderTarget )); } } diff --git a/Source/Falcor/Core/API/Swapchain.h b/Source/Falcor/Core/API/Swapchain.h index 8c107244b..09c6698d7 100644 --- a/Source/Falcor/Core/API/Swapchain.h +++ b/Source/Falcor/Core/API/Swapchain.h @@ -30,12 +30,12 @@ #include "Texture.h" #include "Formats.h" #include "Core/Macros.h" +#include "Core/Object.h" #include -#include namespace Falcor { -class FALCOR_API Swapchain +class FALCOR_API Swapchain : public Object { public: struct Desc @@ -52,12 +52,12 @@ class FALCOR_API Swapchain * @param desc Swapchain description. * @param windowHandle Handle of window to create swapchain for. */ - Swapchain(std::shared_ptr pDevice, const Desc& desc, WindowHandle windowHandle); + Swapchain(ref pDevice, const Desc& desc, WindowHandle windowHandle); const Desc& getDesc() const { return mDesc; } /// Returns the back buffer image at `index`. - const Texture::SharedPtr& getImage(uint32_t index) const; + const ref& getImage(uint32_t index) const; /// Present the next image in the swapchain. void present(); @@ -80,13 +80,11 @@ class FALCOR_API Swapchain gfx::ISwapchain* getGfxSwapchain() const { return mGfxSwapchain; } private: - Swapchain(const Desc& desc, WindowHandle windowHandle); - void prepareImages(); - std::shared_ptr mpDevice; + ref mpDevice; Desc mDesc; Slang::ComPtr mGfxSwapchain; - std::vector mImages; + std::vector> mImages; }; } // namespace Falcor diff --git a/Source/Falcor/Core/API/Texture.cpp b/Source/Falcor/Core/API/Texture.cpp index 3410dd822..176c2809d 100644 --- a/Source/Falcor/Core/API/Texture.cpp +++ b/Source/Falcor/Core/API/Texture.cpp @@ -33,12 +33,13 @@ #include "GFXAPI.h" #include "Core/Assert.h" #include "Core/Errors.h" +#include "Core/ObjectPython.h" #include "Utils/Logger.h" #include "Utils/Threading.h" #include "Utils/Math/Common.h" #include "Utils/Image/ImageIO.h" #include "Utils/Scripting/ScriptBindings.h" -#include "RenderGraph/BasePasses/FullScreenPass.h" +#include "Core/Pass/FullScreenPass.h" #include @@ -48,10 +49,10 @@ namespace Falcor { namespace { -static const bool kTopDown = true; // Memory layout when loading from file +static constexpr bool kTopDown = true; // Memory layout when loading from file Texture::BindFlags updateBindFlags( - Device* pDevice, + ref pDevice, Texture::BindFlags flags, bool hasInitData, uint32_t mipLevels, @@ -79,8 +80,8 @@ Texture::BindFlags updateBindFlags( } } // namespace -Texture::SharedPtr Texture::createFromResource( - Device* pDevice, +ref Texture::createFromResource( + ref pDevice, gfx::ITextureResource* pResource, Type type, uint32_t width, @@ -116,17 +117,16 @@ Texture::SharedPtr Texture::createFromResource( FALCOR_UNREACHABLE(); break; } - Texture::SharedPtr pTexture = - SharedPtr(new Texture(pDevice->shared_from_this(), width, height, depth, arraySize, mipLevels, sampleCount, format, type, bindFlags) - ); + ref pTexture = + ref(new Texture(pDevice, width, height, depth, arraySize, mipLevels, sampleCount, format, type, bindFlags)); pTexture->mGfxTextureResource = pResource; pTexture->mState.global = initState; pTexture->mState.isGlobal = true; return pTexture; } -Texture::SharedPtr Texture::create1D( - Device* pDevice, +ref Texture::create1D( + ref pDevice, uint32_t width, ResourceFormat format, uint32_t arraySize, @@ -136,14 +136,13 @@ Texture::SharedPtr Texture::create1D( ) { bindFlags = updateBindFlags(pDevice, bindFlags, pData != nullptr, mipLevels, format, "Texture1D"); - Texture::SharedPtr pTexture = - SharedPtr(new Texture(pDevice->shared_from_this(), width, 1, 1, arraySize, mipLevels, 1, format, Type::Texture1D, bindFlags)); + ref pTexture = ref(new Texture(pDevice, width, 1, 1, arraySize, mipLevels, 1, format, Type::Texture1D, bindFlags)); pTexture->apiInit(pData, (mipLevels == kMaxPossible)); return pTexture; } -Texture::SharedPtr Texture::create2D( - Device* pDevice, +ref Texture::create2D( + ref pDevice, uint32_t width, uint32_t height, ResourceFormat format, @@ -154,14 +153,14 @@ Texture::SharedPtr Texture::create2D( ) { bindFlags = updateBindFlags(pDevice, bindFlags, pData != nullptr, mipLevels, format, "Texture2D"); - Texture::SharedPtr pTexture = - SharedPtr(new Texture(pDevice->shared_from_this(), width, height, 1, arraySize, mipLevels, 1, format, Type::Texture2D, bindFlags)); + ref pTexture = + ref(new Texture(pDevice, width, height, 1, arraySize, mipLevels, 1, format, Type::Texture2D, bindFlags)); pTexture->apiInit(pData, (mipLevels == kMaxPossible)); return pTexture; } -Texture::SharedPtr Texture::create3D( - Device* pDevice, +ref Texture::create3D( + ref pDevice, uint32_t width, uint32_t height, uint32_t depth, @@ -173,14 +172,13 @@ Texture::SharedPtr Texture::create3D( ) { bindFlags = updateBindFlags(pDevice, bindFlags, pData != nullptr, mipLevels, format, "Texture3D"); - Texture::SharedPtr pTexture = - SharedPtr(new Texture(pDevice->shared_from_this(), width, height, depth, 1, mipLevels, 1, format, Type::Texture3D, bindFlags)); + ref pTexture = ref(new Texture(pDevice, width, height, depth, 1, mipLevels, 1, format, Type::Texture3D, bindFlags)); pTexture->apiInit(pData, (mipLevels == kMaxPossible)); return pTexture; } -Texture::SharedPtr Texture::createCube( - Device* pDevice, +ref Texture::createCube( + ref pDevice, uint32_t width, uint32_t height, ResourceFormat format, @@ -191,15 +189,14 @@ Texture::SharedPtr Texture::createCube( ) { bindFlags = updateBindFlags(pDevice, bindFlags, pData != nullptr, mipLevels, format, "TextureCube"); - Texture::SharedPtr pTexture = - SharedPtr(new Texture(pDevice->shared_from_this(), width, height, 1, arraySize, mipLevels, 1, format, Type::TextureCube, bindFlags) - ); + ref pTexture = + ref(new Texture(pDevice, width, height, 1, arraySize, mipLevels, 1, format, Type::TextureCube, bindFlags)); pTexture->apiInit(pData, (mipLevels == kMaxPossible)); return pTexture; } -Texture::SharedPtr Texture::create2DMS( - Device* pDevice, +ref Texture::create2DMS( + ref pDevice, uint32_t width, uint32_t height, ResourceFormat format, @@ -209,15 +206,104 @@ Texture::SharedPtr Texture::create2DMS( ) { bindFlags = updateBindFlags(pDevice, bindFlags, false, 1, format, "Texture2DMultisample"); - Texture::SharedPtr pTexture = SharedPtr( - new Texture(pDevice->shared_from_this(), width, height, 1, arraySize, 1, sampleCount, format, Type::Texture2DMultisample, bindFlags) - ); + ref pTexture = + ref(new Texture(pDevice, width, height, 1, arraySize, 1, sampleCount, format, Type::Texture2DMultisample, bindFlags)); pTexture->apiInit(nullptr, false); return pTexture; } -Texture::SharedPtr Texture::createFromFile( - Device* pDevice, +ref Texture::createMippedFromFiles( + ref pDevice, + fstd::span paths, + bool loadAsSrgb, + Texture::BindFlags bindFlags +) +{ + std::vector mips; + mips.reserve(paths.size()); + size_t combinedSize = 0; + std::filesystem::path fullPathMip0; + + for (const auto& path : paths) + { + Bitmap::UniqueConstPtr pBitmap; + if (hasExtension(path, "dds")) + { + pBitmap = ImageIO::loadBitmapFromDDS(path); + } + else + { + pBitmap = Bitmap::createFromFile(path, kTopDown); + } + if (!pBitmap) + { + logWarning("Error loading mip {}. Loading failed for image file '{}'.", mips.size(), path); + break; + } + + if (!mips.empty()) + { + if (mips.back()->getFormat() != pBitmap->getFormat()) + { + logWarning("Error loading mip {} from file {}. Texture format of all mip levels must match.", mips.size(), path); + break; + } + if (std::max(mips.back()->getWidth() / 2, 1u) != pBitmap->getWidth() || + std::max(mips.back()->getHeight() / 2, 1u) != pBitmap->getHeight()) + { + logWarning( + "Error loading mip {} from file {}. Image resolution must decrease by half. ({}, {}) != ({}, {})/2", mips.size(), path, + pBitmap->getWidth(), pBitmap->getHeight(), mips.back()->getWidth(), mips.back()->getHeight() + ); + break; + } + } + else + { + fullPathMip0 = path; + } + combinedSize += pBitmap->getSize(); + mips.emplace_back(std::move(pBitmap)); + } + + ref pTex; + if (!mips.empty()) + { + // Combine all the mip data into a single buffer + size_t copyDst = 0; + std::unique_ptr combinedData(new uint8_t[combinedSize]); + for (auto& mip : mips) + { + std::memcpy(&combinedData[copyDst], mip->getData(), mip->getSize()); + copyDst += mip->getSize(); + } + + ResourceFormat texFormat = mips[0]->getFormat(); + if (loadAsSrgb) + texFormat = linearToSrgbFormat(texFormat); + + // Create mip mapped latent texture + pTex = + Texture::create2D(pDevice, mips[0]->getWidth(), mips[0]->getHeight(), texFormat, 1, mips.size(), combinedData.get(), bindFlags); + } + + if (pTex != nullptr) + { + pTex->setSourcePath(fullPathMip0); + + // Log debug info. + std::string str = fmt::format( + "Loaded texture: size={}x{} mips={} format={} path={}", pTex->getWidth(), pTex->getHeight(), pTex->getMipCount(), + to_string(pTex->getFormat()), fullPathMip0 + ); + logDebug(str); + } + + return pTex; +} + +ref Texture::createFromFile( + ref pDevice, const std::filesystem::path& path, bool generateMipLevels, bool loadAsSrgb, @@ -231,7 +317,7 @@ Texture::SharedPtr Texture::createFromFile( return nullptr; } - Texture::SharedPtr pTex; + ref pTex; if (hasExtension(fullPath, "dds")) { try @@ -264,13 +350,20 @@ Texture::SharedPtr Texture::createFromFile( if (pTex != nullptr) { pTex->setSourcePath(fullPath); + + // Log debug info. + std::string str = fmt::format( + "Loaded texture: size={}x{} mips={} format={} path={}", pTex->getWidth(), pTex->getHeight(), pTex->getMipCount(), + to_string(pTex->getFormat()), fullPath + ); + logDebug(str); } return pTex; } Texture::Texture( - std::shared_ptr pDevice, + ref pDevice, uint32_t width, uint32_t height, uint32_t depth, @@ -281,7 +374,7 @@ Texture::Texture( Type type, BindFlags bindFlags ) - : Resource(std::move(pDevice), type, bindFlags, 0) + : Resource(pDevice, type, bindFlags, 0) , mWidth(width) , mHeight(height) , mDepth(depth) @@ -309,11 +402,10 @@ gfx::IResource* Texture::getGfxResource() const template using CreateFuncType = std::function< - typename ViewClass:: - SharedPtr(Texture* pTexture, uint32_t mostDetailedMip, uint32_t mipCount, uint32_t firstArraySlice, uint32_t arraySize)>; + ref(Texture* pTexture, uint32_t mostDetailedMip, uint32_t mipCount, uint32_t firstArraySlice, uint32_t arraySize)>; template -typename ViewClass::SharedPtr findViewCommon( +ref findViewCommon( Texture* pTexture, uint32_t mostDetailedMip, uint32_t mipCount, @@ -371,64 +463,44 @@ typename ViewClass::SharedPtr findViewCommon( return viewMap[view]; } -DepthStencilView::SharedPtr Texture::getDSV(uint32_t mipLevel, uint32_t firstArraySlice, uint32_t arraySize) +ref 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->getDevice().get(), std::static_pointer_cast(pTexture->shared_from_this()), mostDetailedMip, firstArraySlice, - arraySize - ); - }; + { return DepthStencilView::create(pTexture->getDevice().get(), pTexture, 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) +ref 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->getDevice().get(), std::static_pointer_cast(pTexture->shared_from_this()), mostDetailedMip, firstArraySlice, - arraySize - ); - }; + { return UnorderedAccessView::create(pTexture->getDevice().get(), pTexture, mostDetailedMip, firstArraySlice, arraySize); }; return findViewCommon(this, mipLevel, 1, firstArraySlice, arraySize, mUavs, createFunc); } -ShaderResourceView::SharedPtr Texture::getSRV() +ref Texture::getSRV() { return getSRV(0); } -UnorderedAccessView::SharedPtr Texture::getUAV() +ref Texture::getUAV() { return getUAV(0); } -RenderTargetView::SharedPtr Texture::getRTV(uint32_t mipLevel, uint32_t firstArraySlice, uint32_t arraySize) +ref 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->getDevice().get(), std::static_pointer_cast(pTexture->shared_from_this()), mostDetailedMip, firstArraySlice, - arraySize - ); - }; + { return RenderTargetView::create(pTexture->getDevice().get(), pTexture, 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) +ref 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->getDevice().get(), std::static_pointer_cast(pTexture->shared_from_this()), mostDetailedMip, mipCount, - firstArraySlice, arraySize - ); - }; + { return ShaderResourceView::create(pTexture->getDevice().get(), pTexture, mostDetailedMip, mipCount, firstArraySlice, arraySize); }; return findViewCommon(this, mostDetailedMip, mipCount, firstArraySlice, arraySize, mSrvs, createFunc); } @@ -438,7 +510,8 @@ void Texture::captureToFile( uint32_t arraySlice, const std::filesystem::path& path, Bitmap::FileFormat format, - Bitmap::ExportFlags exportFlags + Bitmap::ExportFlags exportFlags, + bool async ) { if (format == Bitmap::FileFormat::DdsFile) @@ -459,8 +532,8 @@ void Texture::captureToFile( if (type == FormatType::Float && channels < 3) { - Texture::SharedPtr pOther = Texture::create2D( - mpDevice.get(), getWidth(mipLevel), getHeight(mipLevel), ResourceFormat::RGBA32Float, 1, 1, nullptr, + ref pOther = Texture::create2D( + mpDevice, 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)); @@ -475,9 +548,13 @@ void Texture::captureToFile( uint32_t width = getWidth(mipLevel); uint32_t height = getHeight(mipLevel); + auto func = [=]() { Bitmap::saveImage(path, width, height, format, exportFlags, resourceFormat, true, (void*)textureData.data()); }; - Threading::dispatchTask(func); + if (async) + Threading::dispatchTask(func); + else + func(); } void Texture::uploadInitData(RenderContext* pRenderContext, const void* pData, bool autoGenMips) @@ -568,7 +645,7 @@ bool Texture::compareDesc(const Texture* pOther) const { return mWidth == pOther->mWidth && mHeight == pOther->mHeight && mDepth == pOther->mDepth && mMipLevels == pOther->mMipLevels && mSampleCount == pOther->mSampleCount && mArraySize == pOther->mArraySize && mFormat == pOther->mFormat && - mIsSparse == pOther->mIsSparse && mSparsePageRes == pOther->mSparsePageRes; + mIsSparse == pOther->mIsSparse && all(mSparsePageRes == pOther->mSparsePageRes); } gfx::IResource::Type getResourceType(Texture::Type type) @@ -784,16 +861,23 @@ FALCOR_SCRIPT_BINDING(Texture) { using namespace pybind11::literals; - pybind11::class_ texture(m, "Texture"); + FALCOR_SCRIPT_BINDING_DEPENDENCY(Resource) + + pybind11::class_> texture(m, "Texture"); texture.def_property_readonly("width", [](const Texture& texture) { return texture.getWidth(); }); texture.def_property_readonly("height", [](const Texture& texture) { return texture.getHeight(); }); texture.def_property_readonly("depth", [](const Texture& texture) { return texture.getDepth(); }); - texture.def_property_readonly("mipCount", &Texture::getMipCount); - texture.def_property_readonly("arraySize", &Texture::getArraySize); + texture.def_property_readonly("mip_count", &Texture::getMipCount); + texture.def_property_readonly("array_size", &Texture::getArraySize); texture.def_property_readonly("samples", &Texture::getSampleCount); texture.def_property_readonly("format", &Texture::getFormat); + texture.def("get_image", pyTextureGetImage, "mip_level"_a = 0, "array_slice"_a = 0); + texture.def("get_data", pyTextureGetData, "mip_level"_a = 0, "array_slice"_a = 0); + + // PYTHONDEPRECATED BEGIN texture.def("getImage", pyTextureGetImage, "mipLevel"_a = 0, "arraySlice"_a = 0); texture.def("getData", pyTextureGetData, "mipLevel"_a = 0, "arraySlice"_a = 0); + // PYTHONDEPRECATED END } } // namespace Falcor diff --git a/Source/Falcor/Core/API/Texture.h b/Source/Falcor/Core/API/Texture.h index 9df743a68..6291464f6 100644 --- a/Source/Falcor/Core/API/Texture.h +++ b/Source/Falcor/Core/API/Texture.h @@ -26,19 +26,19 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **************************************************************************/ #pragma once +#include "fwd.h" #include "Handles.h" #include "Formats.h" #include "Resource.h" #include "ResourceViews.h" #include "Core/Macros.h" #include "Utils/Image/Bitmap.h" -#include #include +#include namespace Falcor { class Sampler; -class Device; class RenderContext; /** @@ -47,9 +47,6 @@ class RenderContext; class FALCOR_API Texture : public Resource { public: - using SharedPtr = std::shared_ptr; - using WeakPtr = std::weak_ptr; - ~Texture(); /** @@ -126,8 +123,8 @@ class FALCOR_API Texture : public Resource * @param[in] bindFlags Texture bind flags. Flags must match the bind flags of the original resource. * @return A pointer to a new texture, or throws an exception if creation failed. */ - static SharedPtr createFromResource( - Device* pDevice, + static ref createFromResource( + ref pDevice, gfx::ITextureResource* pResource, Type type, uint32_t width, @@ -152,8 +149,8 @@ class FALCOR_API Texture : public Resource * @param[in] bindFlags The requested bind flags for the resource. * @return A pointer to a new texture, or throws an exception if creation failed. */ - static SharedPtr create1D( - Device* pDevice, + static ref create1D( + ref pDevice, uint32_t width, ResourceFormat format, uint32_t arraySize = 1, @@ -174,8 +171,8 @@ class FALCOR_API Texture : public Resource * @param[in] bindFlags The requested bind flags for the resource. * @return A pointer to a new texture, or throws an exception if creation failed. */ - static SharedPtr create2D( - Device* pDevice, + static ref create2D( + ref pDevice, uint32_t width, uint32_t height, ResourceFormat format, @@ -198,8 +195,8 @@ class FALCOR_API Texture : public Resource * @param[in] isSparse If true, the texture is created using sparse texture options supported by the API. * @return A pointer to a new texture, or throws an exception if creation failed. */ - static SharedPtr create3D( - Device* pDevice, + static ref create3D( + ref pDevice, uint32_t width, uint32_t height, uint32_t depth, @@ -222,8 +219,8 @@ class FALCOR_API Texture : public Resource * @param[in] bindFlags The requested bind flags for the resource. * @return A pointer to a new texture, or throws an exception if creation failed. */ - static SharedPtr createCube( - Device* pDevice, + static ref createCube( + ref pDevice, uint32_t width, uint32_t height, ResourceFormat format, @@ -243,8 +240,8 @@ class FALCOR_API Texture : public Resource * @param[in] bindFlags The requested bind flags for the resource. * @return A pointer to a new texture, or throws an exception if creation failed. */ - static SharedPtr create2DMS( - Device* pDevice, + static ref create2DMS( + ref pDevice, uint32_t width, uint32_t height, ResourceFormat format, @@ -253,6 +250,20 @@ class FALCOR_API Texture : public Resource BindFlags bindFlags = BindFlags::ShaderResource ); + /** + * Create a new texture object with mips specified explicitly from individual files. + * @param[in] paths List of full paths of all mips, starting from mip0. + * @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. + * @return A new texture, or nullptr if the texture failed to load. + */ + static ref createMippedFromFiles( + ref pDevice, + fstd::span paths, + bool loadAsSrgb, + Texture::BindFlags bindFlags = BindFlags::ShaderResource + ); + /** * Create a new texture object from a file. * @param[in] path File path of the image. Can also include a full path or relative path from a data directory. @@ -261,8 +272,8 @@ class FALCOR_API Texture : public Resource * @param[in] bindFlags The bind flags to create the texture with. * @return A new texture, or nullptr if the texture failed to load. */ - static SharedPtr createFromFile( - Device* pDevice, + static ref createFromFile( + ref pDevice, const std::filesystem::path& path, bool generateMipLevels, bool loadAsSrgb, @@ -276,12 +287,12 @@ class FALCOR_API Texture : public Resource /** * Get a shader-resource view for the entire resource */ - virtual ShaderResourceView::SharedPtr getSRV() override; + virtual ref getSRV() override; /** * Get an unordered access view for the entire resource */ - virtual UnorderedAccessView::SharedPtr getUAV() override; + virtual ref getUAV() override; /** * Get a shader-resource view. @@ -292,7 +303,7 @@ class FALCOR_API Texture : public Resource * @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( + ref getSRV( uint32_t mostDetailedMip, uint32_t mipCount = kMaxPossible, uint32_t firstArraySlice = 0, @@ -306,7 +317,7 @@ class FALCOR_API Texture : public Resource * @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); + ref getRTV(uint32_t mipLevel = 0, uint32_t firstArraySlice = 0, uint32_t arraySize = kMaxPossible); /** * Get a depth stencil view. @@ -315,7 +326,7 @@ class FALCOR_API Texture : public Resource * @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); + ref getDSV(uint32_t mipLevel = 0, uint32_t firstArraySlice = 0, uint32_t arraySize = kMaxPossible); /** * Get an unordered access view. @@ -324,7 +335,7 @@ class FALCOR_API Texture : public Resource * @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, uint32_t firstArraySlice = 0, uint32_t arraySize = kMaxPossible); + ref getUAV(uint32_t mipLevel, uint32_t firstArraySlice = 0, uint32_t arraySize = kMaxPossible); /** * Capture the texture to an image file. @@ -333,13 +344,15 @@ class FALCOR_API Texture : public Resource * @param[in] path Path of the file to save. * @param[in] fileFormat Destination image file format (e.g., PNG, PFM, etc.) * @param[in] exportFlags Save flags, see Bitmap::ExportFlags + * @param[in] async Save asynchronously, otherwise the function blocks until the texture is saved. */ void captureToFile( uint32_t mipLevel, uint32_t arraySlice, const std::filesystem::path& path, Bitmap::FileFormat format = Bitmap::FileFormat::PngFile, - Bitmap::ExportFlags exportFlags = Bitmap::ExportFlags::None + Bitmap::ExportFlags exportFlags = Bitmap::ExportFlags::None, + bool async = true ); /** @@ -378,7 +391,7 @@ class FALCOR_API Texture : public Resource protected: Texture( - std::shared_ptr pDevice, + ref pDevice, uint32_t width, uint32_t height, uint32_t depth, diff --git a/Source/Falcor/Core/API/VAO.cpp b/Source/Falcor/Core/API/VAO.cpp index cccf22e2b..bbbd2538c 100644 --- a/Source/Falcor/Core/API/VAO.cpp +++ b/Source/Falcor/Core/API/VAO.cpp @@ -27,34 +27,22 @@ **************************************************************************/ #include "VAO.h" #include "GFXAPI.h" +#include "Core/ObjectPython.h" #include "Utils/Scripting/ScriptBindings.h" namespace Falcor { -Vao::Vao( - const BufferVec& pVBs, - const VertexLayout::SharedPtr& pLayout, - const Buffer::SharedPtr& pIB, - ResourceFormat ibFormat, - Topology topology -) +Vao::Vao(const BufferVec& pVBs, ref pLayout, ref pIB, ResourceFormat ibFormat, Topology topology) : mpVertexLayout(pLayout), mpVBs(pVBs), mpIB(pIB), mIbFormat(ibFormat), mTopology(topology) {} -Vao::SharedPtr Vao::create( - Topology topology, - const VertexLayout::SharedPtr& pLayout, - const BufferVec& pVBs, - const Buffer::SharedPtr& pIB, - ResourceFormat ibFormat -) +ref Vao::create(Topology topology, ref pLayout, const BufferVec& pVBs, ref pIB, ResourceFormat ibFormat) { // TODO: Check number of vertex buffers match with pLayout. checkArgument( !pIB || (ibFormat == ResourceFormat::R16Uint || ibFormat == ResourceFormat::R32Uint), "'ibFormat' must be R16Uint or R32Uint." ); - SharedPtr pVao = SharedPtr(new Vao(pVBs, pLayout, pIB, ibFormat, topology)); - return pVao; + return ref(new Vao(pVBs, pLayout, pIB, ibFormat, topology)); } Vao::ElementDesc Vao::getElementIndexByLocation(uint32_t elementLocaion) const @@ -81,6 +69,6 @@ Vao::ElementDesc Vao::getElementIndexByLocation(uint32_t elementLocaion) const FALCOR_SCRIPT_BINDING(Vao) { - pybind11::class_(m, "Vao"); + pybind11::class_>(m, "Vao"); } } // namespace Falcor diff --git a/Source/Falcor/Core/API/VAO.h b/Source/Falcor/Core/API/VAO.h index dcb85cbdc..c653eea96 100644 --- a/Source/Falcor/Core/API/VAO.h +++ b/Source/Falcor/Core/API/VAO.h @@ -30,7 +30,7 @@ #include "Buffer.h" #include "Core/Macros.h" #include "Core/Assert.h" -#include +#include "Core/Object.h" #include namespace Falcor @@ -40,12 +40,9 @@ namespace Falcor * 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 FALCOR_API Vao +class FALCOR_API Vao : public Object { public: - using SharedPtr = std::shared_ptr; - using WeakPtr = std::weak_ptr; - using SharedConstPtr = std::shared_ptr; ~Vao() = default; /** @@ -63,12 +60,12 @@ class FALCOR_API Vao struct ElementDesc { - static const uint32_t kInvalidIndex = -1; + static constexpr uint32_t kInvalidIndex = -1; uint32_t vbIndex = kInvalidIndex; uint32_t elementIndex = kInvalidIndex; }; - using BufferVec = std::vector; + using BufferVec = std::vector>; /** * Create a new vertex array object. @@ -79,11 +76,11 @@ class FALCOR_API Vao * @param ibFormat The resource format of the index buffer. Can be either R16Uint or R32Uint. * @return New object, or throws an exception on error. */ - static SharedPtr create( + static ref create( Topology primTopology, - const VertexLayout::SharedPtr& pLayout = nullptr, + ref pLayout = nullptr, const BufferVec& pVBs = BufferVec(), - const Buffer::SharedPtr& pIB = nullptr, + ref pIB = nullptr, ResourceFormat ibFormat = ResourceFormat::Unknown ); @@ -95,7 +92,7 @@ class FALCOR_API Vao /** * Get a vertex buffer */ - const Buffer::SharedPtr& getVertexBuffer(uint32_t index) const + const ref& getVertexBuffer(uint32_t index) const { FALCOR_ASSERT(index < (uint32_t)mpVBs.size()); return mpVBs[index]; @@ -104,7 +101,7 @@ class FALCOR_API Vao /** * Get a vertex buffer layout */ - const VertexLayout::SharedPtr& getVertexLayout() const { return mpVertexLayout; } + const ref& getVertexLayout() const { return mpVertexLayout; } /** * Return the vertex buffer index and the element index by its location. @@ -115,7 +112,7 @@ class FALCOR_API Vao /** * Get the index buffer */ - const Buffer::SharedPtr& getIndexBuffer() const { return mpIB; } + const ref& getIndexBuffer() const { return mpIB; } /** * Get the index buffer format @@ -131,15 +128,11 @@ class FALCOR_API Vao friend class RenderContext; private: - Vao(const BufferVec& pVBs, - const VertexLayout::SharedPtr& pLayout, - const Buffer::SharedPtr& pIB, - ResourceFormat ibFormat, - Topology primTopology); + Vao(const BufferVec& pVBs, ref pLayout, ref pIB, ResourceFormat ibFormat, Topology primTopology); - VertexLayout::SharedPtr mpVertexLayout; + ref mpVertexLayout; BufferVec mpVBs; - Buffer::SharedPtr mpIB; + ref mpIB; void* mpPrivateData = nullptr; ResourceFormat mIbFormat; Topology mTopology; diff --git a/Source/Falcor/Core/API/VertexLayout.cpp b/Source/Falcor/Core/API/VertexLayout.cpp index 2d9630507..41d363ed3 100644 --- a/Source/Falcor/Core/API/VertexLayout.cpp +++ b/Source/Falcor/Core/API/VertexLayout.cpp @@ -26,12 +26,13 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **************************************************************************/ #include "VertexLayout.h" +#include "Core/ObjectPython.h" #include "Utils/Scripting/ScriptBindings.h" namespace Falcor { FALCOR_SCRIPT_BINDING(VertexLayout) { - pybind11::class_(m, "VertexLayout"); + pybind11::class_>(m, "VertexLayout"); } } // namespace Falcor diff --git a/Source/Falcor/Core/API/VertexLayout.h b/Source/Falcor/Core/API/VertexLayout.h index 15a14e02e..eff13ab87 100644 --- a/Source/Falcor/Core/API/VertexLayout.h +++ b/Source/Falcor/Core/API/VertexLayout.h @@ -27,6 +27,7 @@ **************************************************************************/ #pragma once #include "Resource.h" +#include "Core/Object.h" #include "Scene/VertexAttrib.slangh" #include @@ -35,12 +36,9 @@ namespace Falcor /** * Describes the layout of a vertex buffer that will be bound to a render operation as part of a VAO. */ -class FALCOR_API VertexBufferLayout +class FALCOR_API VertexBufferLayout : public Object { public: - using SharedPtr = std::shared_ptr; - using SharedConstPtr = std::shared_ptr; - enum class InputClass { PerVertexData, ///< Buffer elements will represent per-vertex data @@ -51,7 +49,7 @@ class FALCOR_API VertexBufferLayout * Create a new vertex buffer layout object. * @return New object, or throws an exception on error. */ - static SharedPtr create() { return SharedPtr(new VertexBufferLayout()); } + static ref create() { return ref(new VertexBufferLayout()); } /** * Add a new element to the layout. @@ -131,7 +129,7 @@ class FALCOR_API VertexBufferLayout mInstanceStepRate = stepRate; } - static const uint32_t kInvalidShaderLocation = uint32_t(-1); + static constexpr uint32_t kInvalidShaderLocation = uint32_t(-1); private: VertexBufferLayout() = default; @@ -155,22 +153,19 @@ class FALCOR_API VertexBufferLayout /** * Container to hold layouts for every vertex layout that will be bound at once to a VAO. */ -class VertexLayout +class VertexLayout : public Object { public: - using SharedPtr = std::shared_ptr; - using SharedConstPtr = std::shared_ptr; - /** * Create a new vertex layout object. * @return New object, or throws an exception on error. */ - static SharedPtr create() { return SharedPtr(new VertexLayout()); } + static ref create() { return ref(new VertexLayout()); } /** * Add a layout description for a buffer. */ - void addBufferLayout(uint32_t index, VertexBufferLayout::SharedConstPtr pLayout) + void addBufferLayout(uint32_t index, ref pLayout) { if (mpBufferLayouts.size() <= index) { @@ -182,7 +177,7 @@ class VertexLayout /** * Get a buffer layout. */ - const VertexBufferLayout::SharedConstPtr& getBufferLayout(size_t index) const { return mpBufferLayouts[index]; } + const ref& getBufferLayout(size_t index) const { return mpBufferLayouts[index]; } /** * Get how many buffer descriptions there are. @@ -191,6 +186,6 @@ class VertexLayout private: VertexLayout() { mpBufferLayouts.reserve(16); } - std::vector mpBufferLayouts; + std::vector> mpBufferLayouts; }; } // namespace Falcor diff --git a/Source/Falcor/Core/API/fwd.h b/Source/Falcor/Core/API/fwd.h index e359cacaf..c2157454b 100644 --- a/Source/Falcor/Core/API/fwd.h +++ b/Source/Falcor/Core/API/fwd.h @@ -27,10 +27,13 @@ **************************************************************************/ #pragma once +#include "Core/Object.h" + namespace Falcor { class Device; + class CopyContext; class ComputeContext; class RenderContext; diff --git a/Source/Falcor/Core/Assert.h b/Source/Falcor/Core/Assert.h index 136e57fdc..575dab92e 100644 --- a/Source/Falcor/Core/Assert.h +++ b/Source/Falcor/Core/Assert.h @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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,23 +33,23 @@ #ifdef _DEBUG -#define FALCOR_ASSERT(a) \ - if (!(a)) \ - { \ - std::string str = fmt::format("Assertion failed: {}\n{}:{}", #a, __FILE__, __LINE__); \ - Falcor::reportFatalError(str); \ +#define FALCOR_ASSERT(a) \ + if (!(a)) \ + { \ + std::string str_ = fmt::format("Assertion failed: {}\n{}:{}", #a, __FILE__, __LINE__); \ + Falcor::reportFatalError(str_); \ } -#define FALCOR_ASSERT_MSG(a, msg) \ - if (!(a)) \ - { \ - std::string str = fmt::format("Assertion failed: {} ({})\n{}:{}", #a, msg, __FILE__, __LINE__); \ - Falcor::reportFatalError(str); \ +#define FALCOR_ASSERT_MSG(a, msg) \ + if (!(a)) \ + { \ + std::string str_ = fmt::format("Assertion failed: {} ({})\n{}:{}", #a, msg, __FILE__, __LINE__); \ + Falcor::reportFatalError(str_); \ } -#define FALCOR_ASSERT_OP(a, b, OP) \ - if (!(a OP b)) \ - { \ - std::string str = fmt::format("Assertion failed: {} {} {} ({} {} {})\n{}:{}", #a, #OP, #b, a, #OP, b, __FILE__, __LINE__); \ - Falcor::reportFatalError(str); \ +#define FALCOR_ASSERT_OP(a, b, OP) \ + if (!(a OP b)) \ + { \ + std::string str_ = fmt::format("Assertion failed: {} {} {} ({} {} {})\n{}:{}", #a, #OP, #b, a, #OP, b, __FILE__, __LINE__); \ + Falcor::reportFatalError(str_); \ } #define FALCOR_ASSERT_EQ(a, b) FALCOR_ASSERT_OP(a, b, ==) #define FALCOR_ASSERT_NE(a, b) FALCOR_ASSERT_OP(a, b, !=) diff --git a/Source/Falcor/Core/Macros.h b/Source/Falcor/Core/Macros.h index 872781c4f..44931cab6 100644 --- a/Source/Falcor/Core/Macros.h +++ b/Source/Falcor/Core/Macros.h @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -84,14 +84,14 @@ #define FALCOR_D3D12_AGILITY_SDK_VERSION 4 #define FALCOR_D3D12_AGILITY_SDK_PATH ".\\D3D12\\" // To enable the D3D12 Agility SDK, this macro needs to be added to the main source file of the executable. -#define FALCOR_EXPORT_D3D12_AGILITY_SDK \ - extern "C" \ - { \ - FALCOR_API_EXPORT extern const UINT D3D12SDKVersion = FALCOR_D3D12_AGILITY_SDK_VERSION; \ - } \ - extern "C" \ - { \ - FALCOR_API_EXPORT extern const char* D3D12SDKPath = FALCOR_D3D12_AGILITY_SDK_PATH; \ +#define FALCOR_EXPORT_D3D12_AGILITY_SDK \ + extern "C" \ + { \ + FALCOR_API_EXPORT extern const unsigned int D3D12SDKVersion = FALCOR_D3D12_AGILITY_SDK_VERSION; \ + } \ + extern "C" \ + { \ + FALCOR_API_EXPORT extern const char* D3D12SDKPath = FALCOR_D3D12_AGILITY_SDK_PATH; \ } #else #define FALCOR_EXPORT_D3D12_AGILITY_SDK diff --git a/Source/Falcor/Core/Object.cpp b/Source/Falcor/Core/Object.cpp new file mode 100644 index 000000000..e4ee8dbdf --- /dev/null +++ b/Source/Falcor/Core/Object.cpp @@ -0,0 +1,135 @@ +/*************************************************************************** + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. + # + # Redistribution and use in source and binary forms, with or without + # modification, are permitted provided that the following conditions + # are met: + # * Redistributions of source code must retain the above copyright + # notice, this list of conditions and the following disclaimer. + # * Redistributions in binary form must reproduce the above copyright + # notice, this list of conditions and the following disclaimer in the + # documentation and/or other materials provided with the distribution. + # * Neither the name of NVIDIA CORPORATION nor the names of its + # contributors may be used to endorse or promote products derived + # from this software without specific prior written permission. + # + # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY + # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + **************************************************************************/ +#include "Object.h" +#include "Assert.h" +#include "ErrorHandling.h" +#include + +namespace Falcor +{ + +#if FALCOR_ENABLE_REF_TRACKING +static std::mutex sTrackedObjectsMutex; +static std::set sTrackedObjects; +#endif + +void Object::incRef() const +{ + ++mRefCount; +} + +void Object::decRef(bool dealloc) const noexcept +{ + uint32_t refCount = mRefCount.fetch_sub(1); + if (refCount <= 0) + { + reportFatalError("Internal error: Object reference count < 0!"); + } + else if (refCount == 1 && dealloc) + { +#if FALCOR_ENABLE_REF_TRACKING + if (mEnableRefTracking) + { + std::lock_guard lock(sTrackedObjectsMutex); + sTrackedObjects.erase(this); + } +#endif + delete this; + } +} + +#if FALCOR_ENABLE_REF_TRACKING + +void Object::incRef(uint64_t refId) const +{ + if (mEnableRefTracking) + { + std::lock_guard lock(mRefTrackerMutex); + auto it = mRefTrackers.find(refId); + if (it != mRefTrackers.end()) + { + it->second.count++; + } + else + { + mRefTrackers.emplace(refId, getStackTrace()); + } + } + + incRef(); +} + +void Object::decRef(uint64_t refId, bool dealloc) const noexcept +{ + if (mEnableRefTracking) + { + std::lock_guard lock(mRefTrackerMutex); + auto it = mRefTrackers.find(refId); + FALCOR_ASSERT(it != mRefTrackers.end()); + if (--it->second.count == 0) + { + mRefTrackers.erase(it); + } + } + + decRef(); +} + +void Object::setEnableRefTracking(bool enable) +{ + if (enable != mEnableRefTracking) + { + std::lock_guard lock(sTrackedObjectsMutex); + if (enable) + sTrackedObjects.insert(this); + else + sTrackedObjects.erase(this); + mEnableRefTracking = enable; + } +} + +void Object::dumpRefs() const +{ + std::lock_guard lock(mRefTrackerMutex); + logInfo("Object {} has {} references:", fmt::ptr(this), mRefTrackers.size()); + for (const auto& it : mRefTrackers) + { + logInfo("ref={} count={}\n{}\n", it.first, it.second.count, it.second.origin); + } +} + +void Object::dumpAllRefs() +{ + std::lock_guard lock(sTrackedObjectsMutex); + for (const Object* object : sTrackedObjects) + object->dumpRefs(); +} + +#endif // FALCOR_ENABLE_REF_TRACKING + +} // namespace Falcor diff --git a/Source/Falcor/Core/Object.h b/Source/Falcor/Core/Object.h new file mode 100644 index 000000000..68a776816 --- /dev/null +++ b/Source/Falcor/Core/Object.h @@ -0,0 +1,507 @@ +/*************************************************************************** + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. + # + # Redistribution and use in source and binary forms, with or without + # modification, are permitted provided that the following conditions + # are met: + # * Redistributions of source code must retain the above copyright + # notice, this list of conditions and the following disclaimer. + # * Redistributions in binary form must reproduce the above copyright + # notice, this list of conditions and the following disclaimer in the + # documentation and/or other materials provided with the distribution. + # * Neither the name of NVIDIA CORPORATION nor the names of its + # contributors may be used to endorse or promote products derived + # from this software without specific prior written permission. + # + # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY + # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + # (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/Macros.h" + +#include + +#include +#include +#include +#include + +/** + * Enable/disable reference tracking. + * When enabled, the reference count of each object that has tracking + * enabled using setEnableRefTracking() is tracked. Each time the reference + * count is increased, the current stack trace is stored. This helps identify + * owners of objects that are not properly releasing their references. + */ +#define FALCOR_ENABLE_REF_TRACKING 0 + +#if FALCOR_ENABLE_REF_TRACKING +#include +#include +#endif + +namespace Falcor +{ + +/** + * @brief Base class for reference counted objects. + * + * This class (in conjunction with the ``ref`` reference counter) is the + * foundation of an efficient reference-counted object system. The + * implementation here is an alternative to standard mechanisms for reference + * counting such as ``std::shared_ptr`` from the STL. + * + * There are a few reasons for using a custom reference counting system + * in Falcor: + * + * The reference count is stored in a 32-bit integer and each reference + * is a single pointer. This is more efficient than the 64-bit reference + * count and two-pointer reference of ``std::shared_ptr``. + * + * Objects are always allocated with a single allocation. With + * ``std::shared_ptr`` one has to use ``std::make_shared`` to ensure that + * the object and the reference count are allocated together. + * + * We can track references to objects. This is useful for debugging complex + * ownership scenarios. + * + * Finally, if we want to migrate from pybind11 to nanobind at some point in + * the future, we will need to use a custom reference counting system. + */ +class FALCOR_API Object +{ +public: + /// Default constructor. + Object() = default; + + /// Copy constructor. + /// Note: We don't copy the reference counter, so that the new object + /// starts with a reference count of 0. + Object(const Object&) {} + + /// Copy assignment. + /// Note: We don't copy the reference counter, but leave the reference + /// counts of the two objects unchanged. This results in the same semantics + /// that we would get if we used `std::shared_ptr` where the reference + /// counter is stored in a separate place from the object. + Object& operator=(const Object&) { return *this; } + + /// Make the object non-movable. + Object(Object&&) = delete; + Object& operator=(Object&&) = delete; + + /// Destructor. + virtual ~Object() = default; + + /// Return the current reference count. + int refCount() const { return mRefCount; }; + + /// Increase the object's reference count by one. + void incRef() const; + + /// Decrease the reference count of the object and possibly deallocate it. + void decRef(bool dealloc = true) const noexcept; + +#if FALCOR_ENABLE_REF_TRACKING + void incRef(uint64_t refId) const; + void decRef(uint64_t refId, bool dealloc = true) const noexcept; + + /// Enable/disable reference tracking of this object. + void setEnableRefTracking(bool enable); + + /// Dump references of this object. + void dumpRefs() const; + + /// Dump references of all objects that have reference tracking enabled. + static void dumpAllRefs(); +#endif + +private: + mutable std::atomic mRefCount{0}; + +#if FALCOR_ENABLE_REF_TRACKING + struct RefTracker + { + uint32_t count; + std::string origin; + RefTracker(std::string origin_) : count(1), origin(std::move(origin_)) {} + }; + mutable std::map mRefTrackers; + mutable std::mutex mRefTrackerMutex; + bool mEnableRefTracking = false; +#endif +}; + +#if FALCOR_ENABLE_REF_TRACKING +static uint64_t nextRefId() +{ + static std::atomic sNextId = 0; + return sNextId.fetch_add(1); +} +#endif + +/** + * @brief Reference counting helper. + * + * The @a ref template is a simple wrapper to store a pointer to an object. It + * takes care of increasing and decreasing the object's reference count as + * needed. When the last reference goes out of scope, the associated object + * will be deallocated. + * + * This class follows similar semantics to the ``std::shared_ptr`` class from + * the STL. In particular, we avoid implicit conversion to and from raw + * pointers. + */ +template +class ref +{ +public: + /// Default constructor (nullptr). + ref() {} + + /// Construct a reference from a nullptr. + ref(std::nullptr_t) {} + + /// Construct a reference from a convertible pointer. + template + explicit ref(T2* ptr) : mPtr(ptr) + { + static_assert(std::is_base_of_v, "Cannot create reference to object not inheriting from Object class."); + static_assert(std::is_convertible_v, "Cannot create reference to object from unconvertible pointer type."); + if (mPtr) + incRef((const Object*)(mPtr)); + } + + /// Copy constructor. + ref(const ref& r) : mPtr(r.mPtr) + { + if (mPtr) + incRef((const Object*)(mPtr)); + } + + /// Construct a reference from a convertible reference. + template + ref(const ref& r) : mPtr(r.mPtr) + { + static_assert(std::is_base_of_v, "Cannot create reference to object not inheriting from Object class."); + static_assert(std::is_convertible_v, "Cannot create reference to object from unconvertible reference."); + if (mPtr) + incRef((const Object*)(mPtr)); + } + + /// Move constructor. + ref(ref&& r) noexcept + : mPtr(r.mPtr) +#if FALCOR_ENABLE_REF_TRACKING + , mRefId(r.mRefId) +#endif + { + r.mPtr = nullptr; +#if FALCOR_ENABLE_REF_TRACKING + r.mRefId = uint64_t(-1); +#endif + } + + /// Construct a reference by moving from a convertible reference. + template + ref(ref&& r) noexcept + : mPtr(r.mPtr) +#if FALCOR_ENABLE_REF_TRACKING + , mRefId(r.mRefId) +#endif + { + static_assert(std::is_base_of_v, "Cannot create reference to object not inheriting from Object class."); + static_assert(std::is_convertible_v, "Cannot create reference to object from unconvertible reference."); + r.mPtr = nullptr; +#if FALCOR_ENABLE_REF_TRACKING + r.mRefId = uint64_t(-1); +#endif + } + + /// Destructor. + ~ref() + { + if (mPtr) + decRef((const Object*)(mPtr)); + } + + /// Assign another reference into the current one. + ref& operator=(const ref& r) noexcept + { + if (r != *this) + { + if (r.mPtr) + incRef((const Object*)(r.mPtr)); + T* prevPtr = mPtr; + mPtr = r.mPtr; + if (prevPtr) + decRef((const Object*)(prevPtr)); + } + return *this; + } + + /// Assign another convertible reference into the current one. + template + ref& operator=(const ref& r) noexcept + { + static_assert(std::is_convertible_v, "Cannot assign reference to object from unconvertible reference."); + if (r != *this) + { + if (r.mPtr) + incRef((const Object*)(r.mPtr)); + T* prevPtr = mPtr; + mPtr = r.mPtr; + if (prevPtr) + decRef((const Object*)(prevPtr)); + } + return *this; + } + + /// Move another reference into the current one. + ref& operator=(ref&& r) noexcept + { + if (static_cast(&r) != this) + { + if (mPtr) + decRef((const Object*)(mPtr)); + mPtr = r.mPtr; + r.mPtr = nullptr; +#if FALCOR_ENABLE_REF_TRACKING + mRefId = r.mRefId; + r.mRefId = uint64_t(-1); +#endif + } + return *this; + } + + /// Move another convertible reference into the current one. + template + ref& operator=(ref&& r) noexcept + { + static_assert(std::is_convertible_v, "Cannot move reference to object from unconvertible reference."); + if (static_cast(&r) != this) + { + if (mPtr) + decRef((const Object*)(mPtr)); + mPtr = r.mPtr; + r.mPtr = nullptr; +#if FALCOR_ENABLE_REF_TRACKING + mRefId = r.mRefId; + r.mRefId = uint64_t(-1); +#endif + } + return *this; + } + + /// Overwrite this reference with a pointer to another object + template + void reset(T2* ptr = nullptr) noexcept + { + static_assert(std::is_convertible_v, "Cannot assign reference to object from unconvertible pointer."); + if (ptr != mPtr) + { + if (ptr) + incRef((const Object*)(ptr)); + T* prevPtr = mPtr; + mPtr = ptr; + if (prevPtr) + decRef((const Object*)(prevPtr)); + } + } + + /// Compare this reference to another reference. + template + bool operator==(const ref& r) const + { + static_assert( + std::is_convertible_v || std::is_convertible_v, "Cannot compare references of non-convertible types." + ); + return mPtr == r.mPtr; + } + + /// Compare this reference to another reference. + template + bool operator!=(const ref& r) const + { + static_assert( + std::is_convertible_v || std::is_convertible_v, "Cannot compare references of non-convertible types." + ); + return mPtr != r.mPtr; + } + + /// Compare this reference to another reference. + template + bool operator<(const ref& r) const + { + static_assert( + std::is_convertible_v || std::is_convertible_v, "Cannot compare references of non-convertible types." + ); + return mPtr < r.mPtr; + } + + /// Compare this reference to a pointer. + template + bool operator==(const T2* ptr) const + { + static_assert(std::is_convertible_v, "Cannot compare reference to pointer of non-convertible types."); + return mPtr == ptr; + } + + /// Compare this reference to a pointer. + template + bool operator!=(const T2* ptr) const + { + static_assert(std::is_convertible_v, "Cannot compare reference to pointer of non-convertible types."); + return mPtr != ptr; + } + + /// Compare this reference to a null pointer. + bool operator==(std::nullptr_t) const { return mPtr == nullptr; } + + /// Compare this reference to a null pointer. + bool operator!=(std::nullptr_t) const { return mPtr != nullptr; } + + /// Compare this reference to a null pointer. + bool operator<(std::nullptr_t) const { return mPtr < nullptr; } + + /// Access the object referenced by this reference. + T* operator->() const { return mPtr; } + + /// Return a C++ reference to the referenced object. + T& operator*() const { return *mPtr; } + + /// Return a pointer to the referenced object. + T* get() const { return mPtr; } + + /// Check if the object is defined + operator bool() const { return mPtr != nullptr; } + + /// Swap this reference with another reference. + void swap(ref& r) noexcept + { + std::swap(mPtr, r.mPtr); +#if FALCOR_ENABLE_REF_TRACKING + std::swap(mRefId, r.mRefId); +#endif + } + + friend std::ostream& operator<<(std::ostream& os, const ref& r) { return os << r.mPtr; } + +private: + inline void incRef(const Object* object) + { +#if FALCOR_ENABLE_REF_TRACKING + object->incRef(mRefId); +#else + object->incRef(); +#endif + } + + inline void decRef(const Object* object) + { +#if FALCOR_ENABLE_REF_TRACKING + object->decRef(mRefId); +#else + object->decRef(); +#endif + } + + T* mPtr{nullptr}; +#if FALCOR_ENABLE_REF_TRACKING + uint64_t mRefId{nextRefId()}; +#endif + +private: + template + friend class ref; +}; + +template +ref make_ref(Args&&... args) +{ + return ref(new T(std::forward(args)...)); +} + +template +ref static_ref_cast(const ref& r) noexcept +{ + return ref(static_cast(r.get())); +} + +template +ref dynamic_ref_cast(const ref& r) noexcept +{ + return ref(dynamic_cast(r.get())); +} + +/** + * @brief Breakable reference counting helper for avoding reference cycles. + * + * This helper represents a strong reference (ref) that can be broken. + * This is accomplished by storing both a strong reference and a raw pointer. + * When the strong reference is broken, we access the referenced object through + * the raw pointer. + * + * This helper can be used in scenarios where some object holds nested objects + * that themselves hold a reference to the parent object. In such cases, the + * nested objects should hold a breakable reference to the parent object. + * When the nested objects are created, we can immediately break the strong + * reference to the parent object. This allows the parent object to be destroyed + * when all of the external references to it are released. + * + * This helper can be used in place of a @a ref, but it cannot be reassigned. + */ +template +class BreakableReference +{ +public: + BreakableReference(const ref& r) : mStrongRef(r), mWeakRef(mStrongRef.get()) {} + BreakableReference(ref&& r) : mStrongRef(r), mWeakRef(mStrongRef.get()) {} + + BreakableReference() = delete; + BreakableReference& operator=(const ref&) = delete; + BreakableReference& operator=(ref&&) = delete; + + T* get() const { return mWeakRef; } + T* operator->() const { return get(); } + T& operator*() const { return *get(); } + operator ref() const { return ref(get()); } + operator T*() const { return get(); } + operator bool() const { return get() != nullptr; } + + void breakStrongReference() { mStrongRef.reset(); } + + friend std::ostream& operator<<(std::ostream& os, const BreakableReference& r) { return os << r.get(); } + +private: + ref mStrongRef; + T* mWeakRef = nullptr; +}; + +} // namespace Falcor + +namespace std +{ +template +void swap(::Falcor::ref& x, ::Falcor::ref& y) noexcept +{ + return x.swap(y); +} + +template +struct hash<::Falcor::ref> +{ + constexpr int operator()(const ::Falcor::ref& r) const { return std::hash()(r.get()); } +}; + +} // namespace std diff --git a/Source/RenderPasses/OptixDenoiser/BootstrapUtils.cpp b/Source/Falcor/Core/ObjectPython.h similarity index 77% rename from Source/RenderPasses/OptixDenoiser/BootstrapUtils.cpp rename to Source/Falcor/Core/ObjectPython.h index 22d205453..55e7a656d 100644 --- a/Source/RenderPasses/OptixDenoiser/BootstrapUtils.cpp +++ b/Source/Falcor/Core/ObjectPython.h @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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,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 "Falcor.h" +#pragma once -/** Trampoline for indirectly calling Falcor's reportFatalError() function - due to name clashing when including both Falcor and Optix headers. -*/ -void reportFatalError(std::string str) -{ - Falcor::reportFatalError(str); -} +#include "Object.h" +#include -void optixLogCallback(unsigned int level, const char* tag, const char* message, void*) -{ - Falcor::logWarning("[Optix][{:2}][{:12}]: {}", level, tag, message); -} +PYBIND11_DECLARE_HOLDER_TYPE(T, Falcor::ref, true); diff --git a/Source/Falcor/Core/Pass/BaseGraphicsPass.cpp b/Source/Falcor/Core/Pass/BaseGraphicsPass.cpp new file mode 100644 index 000000000..ef3fe7967 --- /dev/null +++ b/Source/Falcor/Core/Pass/BaseGraphicsPass.cpp @@ -0,0 +1,69 @@ +/*************************************************************************** + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. + # + # Redistribution and use in source and binary forms, with or without + # modification, are permitted provided that the following conditions + # are met: + # * Redistributions of source code must retain the above copyright + # notice, this list of conditions and the following disclaimer. + # * Redistributions in binary form must reproduce the above copyright + # notice, this list of conditions and the following disclaimer in the + # documentation and/or other materials provided with the distribution. + # * Neither the name of NVIDIA CORPORATION nor the names of its + # contributors may be used to endorse or promote products derived + # from this software without specific prior written permission. + # + # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY + # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + **************************************************************************/ +#include "BaseGraphicsPass.h" + +namespace Falcor +{ +BaseGraphicsPass::BaseGraphicsPass(ref pDevice, const Program::Desc& progDesc, const Program::DefineList& programDefines) + : mpDevice(pDevice) +{ + auto pProg = GraphicsProgram::create(mpDevice, progDesc, programDefines); + pProg->breakStrongReferenceToDevice(); + + mpState = GraphicsState::create(mpDevice); + mpState->breakStrongReferenceToDevice(); + mpState->setProgram(pProg); + + mpVars = GraphicsVars::create(mpDevice, pProg.get()); +} + +void BaseGraphicsPass::addDefine(const std::string& name, const std::string& value, bool updateVars) +{ + mpState->getProgram()->addDefine(name, value); + if (updateVars) + mpVars = GraphicsVars::create(mpDevice, mpState->getProgram().get()); +} + +void BaseGraphicsPass::removeDefine(const std::string& name, bool updateVars) +{ + mpState->getProgram()->removeDefine(name); + if (updateVars) + mpVars = GraphicsVars::create(mpDevice, mpState->getProgram().get()); +} + +void BaseGraphicsPass::setVars(const ref& pVars) +{ + mpVars = pVars ? pVars : GraphicsVars::create(mpDevice, mpState->getProgram().get()); +} + +void BaseGraphicsPass::breakStrongReferenceToDevice() +{ + mpDevice.breakStrongReference(); +} + +} // namespace Falcor diff --git a/Source/Falcor/RenderGraph/BasePasses/BaseGraphicsPass.h b/Source/Falcor/Core/Pass/BaseGraphicsPass.h similarity index 52% rename from Source/Falcor/RenderGraph/BasePasses/BaseGraphicsPass.h rename to Source/Falcor/Core/Pass/BaseGraphicsPass.h index 2367216e7..804410603 100644 --- a/Source/Falcor/RenderGraph/BasePasses/BaseGraphicsPass.h +++ b/Source/Falcor/Core/Pass/BaseGraphicsPass.h @@ -27,6 +27,7 @@ **************************************************************************/ #pragma once #include "Core/Macros.h" +#include "Core/Object.h" #include "Core/State/GraphicsState.h" #include "Core/Program/GraphicsProgram.h" #include "Core/Program/Program.h" @@ -36,49 +37,59 @@ namespace Falcor { - class FALCOR_API BaseGraphicsPass - { - public: - virtual ~BaseGraphicsPass() = default; +class FALCOR_API BaseGraphicsPass : public Object +{ +public: + virtual ~BaseGraphicsPass() = default; + + /** + * Add a define + */ + void addDefine(const std::string& name, const std::string& value = "", bool updateVars = false); - /** 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); - /** Remove a define - */ - void removeDefine(const std::string& name, bool updateVars = false); + /** + * Get the program + */ + ref getProgram() const { return mpState->getProgram(); } - /** Get the program - */ - GraphicsProgram::SharedPtr getProgram() const { return mpState->getProgram(); } + /** + * Get the state + */ + const ref& getState() const { return mpState; } - /** Get the state - */ - const GraphicsState::SharedPtr& getState() const { return mpState; } + /** + * Get the vars + */ + const ref& getVars() const { return mpVars; } - /** Get the vars - */ - const GraphicsVars::SharedPtr& getVars() const { return mpVars; } + ShaderVar getRootVar() const { return mpVars->getRootVar(); } - ShaderVar getRootVar() const { return mpVars->getRootVar(); } + /** + * 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 ref& pVars); - /** 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); + void breakStrongReferenceToDevice(); - protected: - /** Create a new object. - \param[in] pDevice GPU device. - \param[in] progDesc The program description. - \param[in] programDefines List of macro definitions to set into the program. The macro definitions will be set on all shader stages. - \return A new object, or an exception is thrown if creation failed. - */ - BaseGraphicsPass(std::shared_ptr pDevice, const Program::Desc& progDesc, const Program::DefineList& programDefines); +protected: + /** + * Create a new object. + * @param[in] pDevice GPU device. + * @param[in] progDesc The program description. + * @param[in] programDefines List of macro definitions to set into the program. The macro definitions will be set on all shader stages. + * @return A new object, or an exception is thrown if creation failed. + */ + BaseGraphicsPass(ref pDevice, const Program::Desc& progDesc, const Program::DefineList& programDefines); - std::shared_ptr mpDevice; - GraphicsVars::SharedPtr mpVars; - GraphicsState::SharedPtr mpState; - }; -} + BreakableReference mpDevice; + ref mpVars; + ref mpState; +}; +} // namespace Falcor diff --git a/Source/Falcor/Core/Pass/ComputePass.cpp b/Source/Falcor/Core/Pass/ComputePass.cpp new file mode 100644 index 000000000..933c67499 --- /dev/null +++ b/Source/Falcor/Core/Pass/ComputePass.cpp @@ -0,0 +1,96 @@ +/*************************************************************************** + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. + # + # Redistribution and use in source and binary forms, with or without + # modification, are permitted provided that the following conditions + # are met: + # * Redistributions of source code must retain the above copyright + # notice, this list of conditions and the following disclaimer. + # * Redistributions in binary form must reproduce the above copyright + # notice, this list of conditions and the following disclaimer in the + # documentation and/or other materials provided with the distribution. + # * Neither the name of NVIDIA CORPORATION nor the names of its + # contributors may be used to endorse or promote products derived + # from this software without specific prior written permission. + # + # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY + # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + **************************************************************************/ +#include "ComputePass.h" +#include "Core/API/ComputeContext.h" +#include "Utils/Math/Common.h" + +namespace Falcor +{ +ComputePass::ComputePass(ref pDevice, const Program::Desc& desc, const Program::DefineList& defines, bool createVars) + : mpDevice(pDevice) +{ + auto pProg = ComputeProgram::create(mpDevice, desc, defines); + mpState = ComputeState::create(mpDevice); + mpState->setProgram(pProg); + if (createVars) + mpVars = ComputeVars::create(mpDevice, pProg.get()); + FALCOR_ASSERT(pProg && mpState && (!createVars || mpVars)); +} + +ref ComputePass::create( + ref pDevice, + const std::filesystem::path& path, + const std::string& csEntry, + const Program::DefineList& defines, + bool createVars +) +{ + Program::Desc desc; + desc.addShaderLibrary(path).csEntry(csEntry); + return create(pDevice, desc, defines, createVars); +} + +ref ComputePass::create(ref pDevice, const Program::Desc& desc, const Program::DefineList& defines, bool createVars) +{ + return ref(new ComputePass(pDevice, desc, defines, createVars)); +} + +void ComputePass::execute(ComputeContext* pContext, uint32_t nThreadX, uint32_t nThreadY, uint32_t nThreadZ) +{ + FALCOR_ASSERT(mpVars); + uint3 threadGroupSize = mpState->getProgram()->getReflector()->getThreadGroupSize(); + uint3 groups = div_round_up(uint3(nThreadX, nThreadY, nThreadZ), threadGroupSize); + pContext->dispatch(mpState.get(), mpVars.get(), groups); +} + +void ComputePass::executeIndirect(ComputeContext* pContext, const Buffer* pArgBuffer, uint64_t argBufferOffset) +{ + FALCOR_ASSERT(mpVars); + pContext->dispatchIndirect(mpState.get(), mpVars.get(), pArgBuffer, argBufferOffset); +} + +void ComputePass::addDefine(const std::string& name, const std::string& value, bool updateVars) +{ + mpState->getProgram()->addDefine(name, value); + if (updateVars) + mpVars = ComputeVars::create(mpDevice, mpState->getProgram().get()); +} + +void ComputePass::removeDefine(const std::string& name, bool updateVars) +{ + mpState->getProgram()->removeDefine(name); + if (updateVars) + mpVars = ComputeVars::create(mpDevice, mpState->getProgram().get()); +} + +void ComputePass::setVars(const ref& pVars) +{ + mpVars = pVars ? pVars : ComputeVars::create(mpDevice, mpState->getProgram().get()); + FALCOR_ASSERT(mpVars); +} +} // namespace Falcor diff --git a/Source/Falcor/RenderGraph/BasePasses/ComputePass.h b/Source/Falcor/Core/Pass/ComputePass.h similarity index 50% rename from Source/Falcor/RenderGraph/BasePasses/ComputePass.h rename to Source/Falcor/Core/Pass/ComputePass.h index 900d94c86..ef25eb185 100644 --- a/Source/Falcor/RenderGraph/BasePasses/ComputePass.h +++ b/Source/Falcor/Core/Pass/ComputePass.h @@ -27,6 +27,7 @@ **************************************************************************/ #pragma once #include "Core/Macros.h" +#include "Core/Object.h" #include "Core/State/ComputeState.h" #include "Core/Program/ComputeProgram.h" #include "Core/Program/Program.h" @@ -37,70 +38,75 @@ namespace Falcor { -class FALCOR_API ComputePass +class FALCOR_API ComputePass : public Object { public: - using SharedPtr = ParameterBlockSharedPtr; - - /** Create a new compute pass from file. - \param[in] pDevice GPU device. - \param[in] path Compute program file path. - \param[in] csEntry Name of the entry point in the program. If not specified "main" will be used. - \param[in] defines Optional list of macro definitions to set into the program. - \param[in] createVars Create program vars automatically, otherwise use setVars(). - \return A new object, or throws an exception if creation failed. - */ - static SharedPtr create( - std::shared_ptr pDevice, + /** + * Create a new compute pass from file. + * @param[in] pDevice GPU device. + * @param[in] path Compute program file path. + * @param[in] csEntry Name of the entry point in the program. If not specified "main" will be used. + * @param[in] defines Optional list of macro definitions to set into the program. + * @param[in] createVars Create program vars automatically, otherwise use setVars(). + * @return A new object, or throws an exception if creation failed. + */ + static ref create( + ref pDevice, const std::filesystem::path& path, const std::string& csEntry = "main", const Program::DefineList& defines = Program::DefineList(), bool createVars = true ); - /** Create a new compute pass. - \param[in] pDevice GPU device. - \param[in] desc The program's description. - \param[in] defines Optional list of macro definitions to set into the program. - \param[in] createVars Create program vars automatically, otherwise use setVars(). - \return A new object, or throws an exception if creation failed. - */ - static SharedPtr create( - std::shared_ptr pDevice, + /** + * Create a new compute pass. + * @param[in] pDevice GPU device. + * @param[in] desc The program's description. + * @param[in] defines Optional list of macro definitions to set into the program. + * @param[in] createVars Create program vars automatically, otherwise use setVars(). + * @return A new object, or throws an exception if creation failed. + */ + static ref create( + ref pDevice, 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) - */ + /** + * 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) - */ + /** + * 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 uint3& nThreads) { execute(pContext, nThreads.x, nThreads.y, nThreads.z); } - /** Execute the pass using indirect dispatch given the compute-context and argument buffer - \param[in] pContext The compute context - \param[in] pArgBuffer Argument buffer - \param[in] argBufferOffset Offset in argument buffer - */ + /** + * Execute the pass using indirect dispatch given the compute-context and argument buffer + * @param[in] pContext The compute context + * @param[in] pArgBuffer Argument buffer + * @param[in] argBufferOffset Offset in argument buffer + */ virtual void executeIndirect(ComputeContext* context, const Buffer* pArgBuffer, uint64_t argBufferOffset = 0); - /** Check if a vars object exists. If not, use setVars() to set or create a new vars object. - \return True if a vars object exists. - */ + /** + * Check if a vars object exists. If not, use setVars() to set or create a new vars object. + * @return True if a vars object exists. + */ bool hasVars() const { return mpVars != nullptr; } - /** Get the vars. + /** + * Get the vars. */ - const ComputeVars::SharedPtr& getVars() const + const ref& getVars() const { FALCOR_ASSERT(mpVars); return mpVars; @@ -108,33 +114,38 @@ class FALCOR_API ComputePass ShaderVar getRootVar() const { return mpVars->getRootVar(); } - /** Add a define + /** + * Add a define */ void addDefine(const std::string& name, const std::string& value = "", bool updateVars = false); - /** Remove a define + /** + * Remove a define */ void removeDefine(const std::string& name, bool updateVars = false); - /** Get the program + /** + * Get the program */ - ComputeProgram::SharedPtr getProgram() const { return mpState->getProgram(); } + ref 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. The function throws an exception on error. - \param[in] pVars The new GraphicsVars object. If this is nullptr, then the pass will automatically create a new vars object. - */ - void setVars(const ComputeVars::SharedPtr& pVars); + /** + * 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. The function throws an exception on error. + * @param[in] pVars The new GraphicsVars object. If this is nullptr, then the pass will automatically create a new vars object. + */ + void setVars(const ref& pVars); - /** Get the thread group size from the program + /** + * Get the thread group size from the program */ uint3 getThreadGroupSize() const { return mpState->getProgram()->getReflector()->getThreadGroupSize(); } protected: - ComputePass(std::shared_ptr pDevice, const Program::Desc& desc, const Program::DefineList& defines, bool createVars); + ComputePass(ref pDevice, const Program::Desc& desc, const Program::DefineList& defines, bool createVars); - std::shared_ptr mpDevice; - ComputeVars::SharedPtr mpVars; - ComputeState::SharedPtr mpState; + ref mpDevice; + ref mpVars; + ref mpState; }; } // namespace Falcor diff --git a/Source/Falcor/Core/Pass/FullScreenPass.cpp b/Source/Falcor/Core/Pass/FullScreenPass.cpp new file mode 100644 index 000000000..327f29b07 --- /dev/null +++ b/Source/Falcor/Core/Pass/FullScreenPass.cpp @@ -0,0 +1,131 @@ +/*************************************************************************** + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. + # + # Redistribution and use in source and binary forms, with or without + # modification, are permitted provided that the following conditions + # are met: + # * Redistributions of source code must retain the above copyright + # notice, this list of conditions and the following disclaimer. + # * Redistributions in binary form must reproduce the above copyright + # notice, this list of conditions and the following disclaimer in the + # documentation and/or other materials provided with the distribution. + # * Neither the name of NVIDIA CORPORATION nor the names of its + # contributors may be used to endorse or promote products derived + # from this software without specific prior written permission. + # + # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY + # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + **************************************************************************/ +#include "FullScreenPass.h" +#include "Core/API/RenderContext.h" +#include "Utils/SharedCache.h" + +namespace Falcor +{ +namespace +{ +struct Vertex +{ + float2 screenPos; + float2 texCoord; +}; + +const Vertex kVertices[] = { + {float2(-1, 1), float2(0, 0)}, + {float2(-1, -1), float2(0, 1)}, + {float2(1, 1), float2(1, 0)}, + {float2(1, -1), float2(1, 1)}, +}; +} // namespace + +struct FullScreenPass::SharedData +{ + ref pVertexBuffer; + ref pVao; + uint64_t objectCount = 0; + + SharedData(ref pDevice) + { + const uint32_t vbSize = (uint32_t)(sizeof(Vertex) * std::size(kVertices)); + pVertexBuffer = Buffer::create(pDevice, vbSize, Buffer::BindFlags::Vertex, Buffer::CpuAccess::Write, (void*)kVertices); + pVertexBuffer->breakStrongReferenceToDevice(); + + ref pLayout = VertexLayout::create(); + ref 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{pVertexBuffer}; + pVao = Vao::create(Vao::Topology::TriangleStrip, pLayout, buffers); + } +}; + +static SharedCache sSharedCache; + +FullScreenPass::FullScreenPass(ref pDevice, const Program::Desc& progDesc, const Program::DefineList& programDefines) + : BaseGraphicsPass(pDevice, progDesc, programDefines) +{ + // Get shared VB and VAO. + mpSharedData = sSharedCache.acquire(mpDevice, [this]() { return std::make_shared(mpDevice); }); + + // Create depth stencil state + FALCOR_ASSERT(mpState); + auto pDsState = DepthStencilState::create(DepthStencilState::Desc().setDepthEnabled(false)); + mpState->setDepthStencilState(pDsState); + + mpState->setVao(mpSharedData->pVao); +} + +FullScreenPass::~FullScreenPass() = default; + +ref FullScreenPass::create( + ref pDevice, + 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)); + defs.add("_OUTPUT_VERTEX_COUNT", std::to_string(3 * popcount(viewportMask))); + d.addShaderLibrary("Core/Pass/FullScreenPass.gs.slang").gsEntry("main"); + } + if (!d.hasEntryPoint(ShaderType::Vertex)) + d.addShaderLibrary("Core/Pass/FullScreenPass.vs.slang").vsEntry("main"); + + return ref(new FullScreenPass(pDevice, d, defs)); +} + +ref FullScreenPass::create( + ref pDevice, + const std::filesystem::path& path, + const Program::DefineList& defines, + uint32_t viewportMask +) +{ + Program::Desc desc; + desc.addShaderLibrary(path).psEntry("main"); + return create(pDevice, desc, defines, viewportMask); +} + +void FullScreenPass::execute(RenderContext* pRenderContext, const ref& pFbo, bool autoSetVpSc) const +{ + mpState->setFbo(pFbo, autoSetVpSc); + pRenderContext->draw(mpState.get(), mpVars.get(), (uint32_t)std::size(kVertices), 0); +} +} // namespace Falcor diff --git a/Source/Falcor/RenderGraph/BasePasses/FullScreenPass.gs.slang b/Source/Falcor/Core/Pass/FullScreenPass.gs.slang similarity index 91% rename from Source/Falcor/RenderGraph/BasePasses/FullScreenPass.gs.slang rename to Source/Falcor/Core/Pass/FullScreenPass.gs.slang index b90a4afef..7098563ec 100644 --- a/Source/Falcor/RenderGraph/BasePasses/FullScreenPass.gs.slang +++ b/Source/Falcor/Core/Pass/FullScreenPass.gs.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-21, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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,8 +27,8 @@ **************************************************************************/ struct VsOut { - float2 texC : TEXCOORD; - float4 posH : POSITION; + float2 texC : TEXCOORD; + float4 posH : POSITION; }; struct GsOut @@ -44,11 +44,11 @@ void main(triangle VsOut input[3], inout TriangleStream outStream) GsOut output; uint mask = _VIEWPORT_MASK; - while(mask != 0) + while (mask != 0) { uint layer = firstbitlow(mask); - - for(int i = 0 ; i < 3 ; i++) + + for (int i = 0; i < 3; i++) { output.rtIndex = layer; output.posH = input[i].posH; diff --git a/Source/Falcor/Core/Pass/FullScreenPass.h b/Source/Falcor/Core/Pass/FullScreenPass.h new file mode 100644 index 000000000..25eb4e751 --- /dev/null +++ b/Source/Falcor/Core/Pass/FullScreenPass.h @@ -0,0 +1,89 @@ +/*************************************************************************** + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. + # + # Redistribution and use in source and binary forms, with or without + # modification, are permitted provided that the following conditions + # are met: + # * Redistributions of source code must retain the above copyright + # notice, this list of conditions and the following disclaimer. + # * Redistributions in binary form must reproduce the above copyright + # notice, this list of conditions and the following disclaimer in the + # documentation and/or other materials provided with the distribution. + # * Neither the name of NVIDIA CORPORATION nor the names of its + # contributors may be used to endorse or promote products derived + # from this software without specific prior written permission. + # + # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY + # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + # (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 "Core/Macros.h" +#include "Core/Program/Program.h" +#include +#include + +namespace Falcor +{ +class FALCOR_API FullScreenPass : public BaseGraphicsPass +{ +public: + struct SharedData; + + virtual ~FullScreenPass(); + + /** + * Create a new fullscreen pass from file. + * @param[in] pDevice GPU device. + * @param[in] path Pixel shader file path. This method expects a pixel shader named "main()" in the file. + * @param[in] defines Optional list of macro definitions to set into the program. + * @param[in] viewportMask Optional value to initialize viewport mask with. Useful for multi-projection passes. + * @return A new object, or throws an exception if creation failed. + */ + static ref create( + ref pDevice, + const std::filesystem::path& path, + const Program::DefineList& defines = Program::DefineList(), + uint32_t viewportMask = 0 + ); + + /** + * Create a new fullscreen pass. + * @param[in] pDevice GPU device. + * @param[in] desc The program description. + * @param[in] defines Optional list of macro definitions to set into the program. + * @param[in] viewportMask Optional value to initialize viewport mask with. Useful for multi-projection passes. + * @return A new object, or throws an exception if creation failed. + */ + static ref create( + ref pDevice, + 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 ref& pFbo, bool autoSetVpSc = true) const; + +protected: + FullScreenPass(ref pDevice, const Program::Desc& progDesc, const Program::DefineList& programDefines); + +private: + std::shared_ptr mpSharedData; +}; +} // namespace Falcor diff --git a/Source/Falcor/RenderGraph/BasePasses/FullScreenPass.vs.slang b/Source/Falcor/Core/Pass/FullScreenPass.vs.slang similarity index 87% rename from Source/Falcor/RenderGraph/BasePasses/FullScreenPass.vs.slang rename to Source/Falcor/Core/Pass/FullScreenPass.vs.slang index f869f4687..c2f2645f9 100644 --- a/Source/Falcor/RenderGraph/BasePasses/FullScreenPass.vs.slang +++ b/Source/Falcor/Core/Pass/FullScreenPass.vs.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-21, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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,18 +27,18 @@ **************************************************************************/ struct VsOut { - float2 texC : TEXCOORD; + float2 texC : TEXCOORD; #ifndef _VIEWPORT_MASK - float4 posH : SV_POSITION; + float4 posH : SV_POSITION; #else - float4 posH : POSITION; + float4 posH : POSITION; #endif }; -VsOut main(float4 posS : POSITION, float2 texC : TEXCOORD) +VsOut main(float4 posS: POSITION, float2 texC: TEXCOORD) { VsOut vOut; vOut.texC = texC; - vOut.posH = posS; + vOut.posH = posS; return vOut; } diff --git a/Source/Falcor/RenderGraph/BasePasses/RasterPass.cpp b/Source/Falcor/Core/Pass/RasterPass.cpp similarity index 57% rename from Source/Falcor/RenderGraph/BasePasses/RasterPass.cpp rename to Source/Falcor/Core/Pass/RasterPass.cpp index a6fcf584d..35538ac65 100644 --- a/Source/Falcor/RenderGraph/BasePasses/RasterPass.cpp +++ b/Source/Falcor/Core/Pass/RasterPass.cpp @@ -30,30 +30,35 @@ namespace Falcor { - RasterPass::SharedPtr RasterPass::create(std::shared_ptr pDevice, const Program::Desc& desc, const Program::DefineList& defines) - { - return SharedPtr(new RasterPass(std::move(pDevice), desc, defines)); - } +ref RasterPass::create(ref pDevice, const Program::Desc& desc, const Program::DefineList& defines) +{ + return ref(new RasterPass(pDevice, desc, defines)); +} - RasterPass::SharedPtr RasterPass::create(std::shared_ptr pDevice, const std::filesystem::path& path, const std::string& vsEntry, const std::string& psEntry, const Program::DefineList& defines) - { - Program::Desc desc; - desc.addShaderLibrary(path).vsEntry(vsEntry).psEntry(psEntry); - return create(std::move(pDevice), desc, defines); - } +ref RasterPass::create( + ref pDevice, + const std::filesystem::path& path, + const std::string& vsEntry, + const std::string& psEntry, + const Program::DefineList& defines +) +{ + Program::Desc desc; + desc.addShaderLibrary(path).vsEntry(vsEntry).psEntry(psEntry); + return create(pDevice, desc, defines); +} - RasterPass::RasterPass(std::shared_ptr pDevice, const Program::Desc& progDesc, const Program::DefineList& programDefines) - : BaseGraphicsPass(std::move(pDevice), progDesc, programDefines) - { - } +RasterPass::RasterPass(ref pDevice, const Program::Desc& progDesc, const Program::DefineList& programDefines) + : BaseGraphicsPass(pDevice, progDesc, programDefines) +{} - void RasterPass::drawIndexed(RenderContext* pRenderContext, uint32_t indexCount, uint32_t startIndexLocation, int32_t baseVertexLocation) - { - pRenderContext->drawIndexed(mpState.get(), mpVars.get(), indexCount, startIndexLocation, baseVertexLocation); - } +void RasterPass::drawIndexed(RenderContext* pRenderContext, uint32_t indexCount, uint32_t startIndexLocation, int32_t baseVertexLocation) +{ + pRenderContext->drawIndexed(mpState.get(), mpVars.get(), indexCount, startIndexLocation, baseVertexLocation); +} - void RasterPass::draw(RenderContext* pRenderContext, uint32_t vertexCount, uint32_t startVertexLocation) - { - pRenderContext->draw(mpState.get(), mpVars.get(), vertexCount, startVertexLocation); - } +void RasterPass::draw(RenderContext* pRenderContext, uint32_t vertexCount, uint32_t startVertexLocation) +{ + pRenderContext->draw(mpState.get(), mpVars.get(), vertexCount, startVertexLocation); } +} // namespace Falcor diff --git a/Source/Falcor/Core/Pass/RasterPass.h b/Source/Falcor/Core/Pass/RasterPass.h new file mode 100644 index 000000000..6e3c2795f --- /dev/null +++ b/Source/Falcor/Core/Pass/RasterPass.h @@ -0,0 +1,91 @@ +/*************************************************************************** + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. + # + # Redistribution and use in source and binary forms, with or without + # modification, are permitted provided that the following conditions + # are met: + # * Redistributions of source code must retain the above copyright + # notice, this list of conditions and the following disclaimer. + # * Redistributions in binary form must reproduce the above copyright + # notice, this list of conditions and the following disclaimer in the + # documentation and/or other materials provided with the distribution. + # * Neither the name of NVIDIA CORPORATION nor the names of its + # contributors may be used to endorse or promote products derived + # from this software without specific prior written permission. + # + # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY + # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + # (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 "Core/Macros.h" +#include "Core/Program/Program.h" +#include +#include + +namespace Falcor +{ +class FALCOR_API RasterPass : public BaseGraphicsPass +{ +public: + /** + * Create a new object. + * @param[in] pDevice GPU devuce. + * @param[in] path Program file path. + * @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 Optional list of macro definitions to set into the program. The macro definitions will be set on all shader + * stages. + * @return A new object, or throws an exception if creation failed. + */ + static ref create( + ref pDevice, + const std::filesystem::path& path, + const std::string& vsEntry, + const std::string& psEntry, + const Program::DefineList& defines = Program::DefineList() + ); + + /** + * Create a new object. + * @param[in] pDevice GPU devuce. + * @param[in] progDesc The program description. + * @param[in] programDefines Optional list of macro definitions to set into the program. The macro definitions will be set on all shader + * stages. + * @return A new object, or throws an exception if creation failed. + */ + static ref create( + ref pDevice, + 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* pRenderContext, 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* pRenderContext, uint32_t indexCount, uint32_t startIndexLocation, int32_t baseVertexLocation); + +protected: + RasterPass(ref pDevice, const Program::Desc& progDesc, const Program::DefineList& programDefines); +}; +} // namespace Falcor diff --git a/Source/Falcor/Core/Platform/Linux/Linux.cpp b/Source/Falcor/Core/Platform/Linux/Linux.cpp index abe4400cb..f22ed8deb 100644 --- a/Source/Falcor/Core/Platform/Linux/Linux.cpp +++ b/Source/Falcor/Core/Platform/Linux/Linux.cpp @@ -48,7 +48,6 @@ namespace Falcor { -void setMainWindowHandle(WindowHandle windowHandle) {} MsgBoxButton msgBox(const std::string& title, const std::string& msg, MsgBoxType type, MsgBoxIcon icon) { diff --git a/Source/Falcor/Core/Platform/LockFile.cpp b/Source/Falcor/Core/Platform/LockFile.cpp index 87dd9a0d4..6f6eb7cd2 100644 --- a/Source/Falcor/Core/Platform/LockFile.cpp +++ b/Source/Falcor/Core/Platform/LockFile.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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 @@ #define WINDOWS_LEAN_AND_MEAN #include #elif FALCOR_LINUX +#include #include #include #else diff --git a/Source/Falcor/Core/Platform/OS.cpp b/Source/Falcor/Core/Platform/OS.cpp index 2cdaf3526..87a0c29ee 100644 --- a/Source/Falcor/Core/Platform/OS.cpp +++ b/Source/Falcor/Core/Platform/OS.cpp @@ -35,7 +35,6 @@ #include #include #include -#include #include namespace Falcor @@ -112,7 +111,7 @@ void addDataDirectory(const std::filesystem::path& dir, bool addToFront) FALCOR_CHECK_ARG_MSG(!dir.empty(), "Do not add empty directories to search paths"); if (std::find_if( - gDataDirectories.begin(), gDataDirectories.end(), [&dir](auto& dataDir) { return std::filesystem::equivalent(dir, dataDir); } + gDataDirectories.begin(), gDataDirectories.end(), [&dir](const std::filesystem::path& d) { return isSamePath(dir, d); } ) == gDataDirectories.end()) { if (addToFront) @@ -131,7 +130,7 @@ void removeDataDirectory(const std::filesystem::path& dir) FALCOR_CHECK_ARG_MSG(!dir.empty(), "Do not remove empty directories to search paths"); auto it = std::find_if( - gDataDirectories.begin(), gDataDirectories.end(), [&dir](auto& dataDir) { return std::filesystem::equivalent(dir, dataDir); } + gDataDirectories.begin(), gDataDirectories.end(), [&dir](const std::filesystem::path& d) { return isSamePath(dir, d); } ); if (it != gDataDirectories.end()) { @@ -150,6 +149,11 @@ bool isDevelopmentMode() return devMode; } +bool isSamePath(const std::filesystem::path& lhs, const std::filesystem::path& rhs) +{ + return std::filesystem::weakly_canonical(lhs) == std::filesystem::weakly_canonical(rhs); +} + bool findFileInDataDirectories(const std::filesystem::path& path, std::filesystem::path& fullPath) { return findFileInDirectories(path, fullPath, getDataDirectoriesList()); diff --git a/Source/Falcor/Core/Platform/OS.h b/Source/Falcor/Core/Platform/OS.h index e3fdb42d7..999db7131 100644 --- a/Source/Falcor/Core/Platform/OS.h +++ b/Source/Falcor/Core/Platform/OS.h @@ -48,13 +48,6 @@ class OSServices static void stop(); }; -/** - * Sets the main window handle. - * This is used to set the parent window when showing message boxes. - * @param[in] windowHandle Window handle. - */ -FALCOR_API void setMainWindowHandle(WindowHandle windowHandle); - /** * Adds an icon to the foreground window. * @param[in] path Icon file path. @@ -152,6 +145,14 @@ FALCOR_API uint32_t msgBox( uint32_t defaultButtonId = uint32_t(-1) ); +/** + * Compares two paths in their weakly canonical form, returning true if they match. + * Operator == on path does a string comparison, ignoring the fact that windows paths are case insensitive. + * STL's equivalent comparator throws when either of the paths does not exist. + * This complements the two by allowing comparing non-existent paths, but at the same time ignoring case on windows. + */ +FALCOR_API bool isSamePath(const std::filesystem::path& lhs, const std::filesystem::path& rhs); + /** * Finds a file in one of the data search directories. * @param[in] path The file path to look for. diff --git a/Source/Falcor/Core/Platform/PlatformHandles.h b/Source/Falcor/Core/Platform/PlatformHandles.h index a2df0cb58..b03da13f4 100644 --- a/Source/Falcor/Core/Platform/PlatformHandles.h +++ b/Source/Falcor/Core/Platform/PlatformHandles.h @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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,23 +28,11 @@ #pragma once #include "Core/Macros.h" -#if FALCOR_WINDOWS -// Instead of including windows.h and crippling compile time we only -// include windef.h. We need to define _AMD64_ manually as it is only -// defined in windows.h (if on x64 architecture). We ensure that we -// are indeed compiling for x64 first, then define _AMD64_ manually. -#ifndef _M_AMD64 -#error "Compilation only supported on x64!" -#endif -#define _AMD64_ -#include -#endif - namespace Falcor { #if FALCOR_WINDOWS -using SharedLibraryHandle = HMODULE; -using WindowHandle = HWND; +using SharedLibraryHandle = void*; // HANDLE +using WindowHandle = void*; // HWND #elif FALCOR_LINUX using SharedLibraryHandle = void*; struct WindowHandle diff --git a/Source/Falcor/Core/Platform/Windows/ProgressBarWin.cpp b/Source/Falcor/Core/Platform/Windows/ProgressBarWin.cpp index 2a804b966..3e8b451f4 100644 --- a/Source/Falcor/Core/Platform/Windows/ProgressBarWin.cpp +++ b/Source/Falcor/Core/Platform/Windows/ProgressBarWin.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -54,7 +54,7 @@ struct ProgressBar::Window thread.join(); } - static void threadFunc(ProgressBar::Window* pThis, std::string msg) + static void threadFunc(ProgressBar::Window* pThis, std::string msgText) { // Create the window int w = 200; @@ -65,7 +65,7 @@ struct ProgressBar::Window 0, PROGRESS_CLASS, nullptr, WS_VISIBLE | PBS_MARQUEE, x, y, w, h, nullptr, nullptr, GetModuleHandle(nullptr), nullptr ); - SetWindowTextA(hwnd, msg.c_str()); + SetWindowTextA(hwnd, msgText.c_str()); SetForegroundWindow(hwnd); setWindowIcon(getRuntimeDirectory() / "data/framework/nvidia.ico", hwnd); diff --git a/Source/Falcor/Core/Platform/Windows/Windows.cpp b/Source/Falcor/Core/Platform/Windows/Windows.cpp index fe5da8be8..eee1c2638 100644 --- a/Source/Falcor/Core/Platform/Windows/Windows.cpp +++ b/Source/Falcor/Core/Platform/Windows/Windows.cpp @@ -65,12 +65,6 @@ extern "C" namespace Falcor { -static HWND gMainWindowHandle; // TODO: REMOVEGLOBAL - -void setMainWindowHandle(HWND windowHandle) -{ - gMainWindowHandle = windowHandle; -} MsgBoxButton msgBox(const std::string& title, const std::string& msg, MsgBoxType type, MsgBoxIcon icon) { @@ -134,7 +128,6 @@ uint32_t msgBox( LONG textWidth = 0; // Query windows for common metrics. - HWND hwnd = gMainWindowHandle; UINT dpi = GetDpiForSystem(); NONCLIENTMETRICS metrics; metrics.cbSize = sizeof(metrics); @@ -142,7 +135,7 @@ uint32_t msgBox( return textWidth; // Setup DC with message font. - HDC hdc = GetDC(hwnd); + HDC hdc = GetDC(NULL); HFONT font = CreateFontIndirect(&metrics.lfMessageFont); HGDIOBJ oldFont = SelectObject(hdc, font); @@ -157,7 +150,7 @@ uint32_t msgBox( // Restore DC. SelectObject(hdc, oldFont); - ReleaseDC(hwnd, hdc); + ReleaseDC(NULL, hdc); DeleteObject(font); return textWidth; @@ -194,7 +187,6 @@ uint32_t msgBox( // Set up dialog config TASKDIALOGCONFIG config = {}; config.cbSize = sizeof(TASKDIALOGCONFIG); - config.hwndParent = gMainWindowHandle; config.dwFlags = TDF_ALLOW_DIALOG_CANCELLATION; config.pszWindowTitle = wideTitle.c_str(); switch (icon) @@ -565,7 +557,7 @@ void setWindowIcon(const std::filesystem::path& path, WindowHandle windowHandle) HANDLE hIcon = LoadImageW(GetModuleHandleW(NULL), path.c_str(), IMAGE_ICON, 0, 0, LR_DEFAULTSIZE | LR_LOADFROMFILE); if (!hIcon) throw RuntimeError("Failed to load icon from '{}'.", path); - HWND hWnd = windowHandle ? windowHandle : GetActiveWindow(); + HWND hWnd = windowHandle ? static_cast(windowHandle) : GetActiveWindow(); SendMessage(hWnd, WM_SETICON, ICON_BIG, (LPARAM)hIcon); } @@ -942,7 +934,7 @@ SharedLibraryHandle loadSharedLibrary(const std::filesystem::path& path) */ void releaseSharedLibrary(SharedLibraryHandle library) { - FreeLibrary(library); + FreeLibrary(static_cast(library)); } /** @@ -950,7 +942,7 @@ void releaseSharedLibrary(SharedLibraryHandle library) */ void* getProcAddress(SharedLibraryHandle library, const std::string& funcName) { - return reinterpret_cast(GetProcAddress(library, funcName.c_str())); + return reinterpret_cast(GetProcAddress(static_cast(library), funcName.c_str())); } void OSServices::start() diff --git a/Source/Falcor/Core/Plugin.cpp b/Source/Falcor/Core/Plugin.cpp index 3d49581f3..9be5be27f 100644 --- a/Source/Falcor/Core/Plugin.cpp +++ b/Source/Falcor/Core/Plugin.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -67,7 +67,7 @@ bool PluginManager::loadPlugin(const std::filesystem::path& path) SharedLibraryHandle library = loadSharedLibrary(path); if (library == nullptr) - throw RuntimeError("Failed to load plugin library from {}. File is not a shared library.", path); + throw RuntimeError("Failed to load plugin library from {}. Cannot load shared library.", path); using RegisterPluginProc = void (*)(PluginRegistry&); @@ -92,7 +92,7 @@ bool PluginManager::loadPlugin(const std::filesystem::path& path) bool PluginManager::releasePlugin(const std::filesystem::path& path) { - std::lock_guard lock(mLibrariesMutex); + std::lock_guard librariesLock(mLibrariesMutex); auto libraryIt = mLibraries.find(path); if (libraryIt == mLibraries.end()) @@ -106,7 +106,7 @@ bool PluginManager::releasePlugin(const std::filesystem::path& path) // Delete all the classes that were owned by the library. { - std::lock_guard lock(mClassDescsMutex); + std::lock_guard classDescsLock(mClassDescsMutex); for (auto it = mClassDescs.begin(); it != mClassDescs.end();) { if (it->second->library == library) diff --git a/Source/Falcor/Core/Program/ComputeProgram.cpp b/Source/Falcor/Core/Program/ComputeProgram.cpp index 75757c346..6774d003b 100644 --- a/Source/Falcor/Core/Program/ComputeProgram.cpp +++ b/Source/Falcor/Core/Program/ComputeProgram.cpp @@ -27,14 +27,15 @@ **************************************************************************/ #include "ComputeProgram.h" #include "ProgramManager.h" +#include "Core/ObjectPython.h" #include "Core/API/ComputeContext.h" #include "Core/State/ComputeState.h" #include "Utils/Scripting/ScriptBindings.h" namespace Falcor { -ComputeProgram::SharedPtr ComputeProgram::createFromFile( - std::shared_ptr pDevice, +ref ComputeProgram::createFromFile( + ref pDevice, const std::filesystem::path& path, const std::string& csEntry, const DefineList& programDefines, @@ -47,29 +48,27 @@ ComputeProgram::SharedPtr ComputeProgram::createFromFile( d.setShaderModel(shaderModel); d.setCompilerFlags(flags); d.csEntry(csEntry); - return create(std::move(pDevice), d, programDefines); + return create(pDevice, d, programDefines); } -ComputeProgram::SharedPtr ComputeProgram::create(std::shared_ptr pDevice, const Desc& desc, const DefineList& programDefines) +ref ComputeProgram::create(ref pDevice, const Desc& desc, const DefineList& programDefines) { - auto pProgram = SharedPtr(new ComputeProgram(pDevice, desc, programDefines)); - pDevice->getProgramManager()->registerProgramForReload(pProgram); - return pProgram; + return ref(new ComputeProgram(pDevice, desc, programDefines)); } -ComputeProgram::ComputeProgram(std::shared_ptr pDevice, const Desc& desc, const DefineList& programDefines) - : Program(std::move(pDevice), desc, programDefines) +ComputeProgram::ComputeProgram(ref pDevice, const Desc& desc, const DefineList& programDefines) + : Program(pDevice, desc, programDefines) {} -void ComputeProgram::dispatchCompute(ComputeContext* pContext, ComputeVars* pVars, uint3 const& threadGroupCount) +void ComputeProgram::dispatchCompute(ComputeContext* pContext, ComputeVars* pVars, const uint3& threadGroupCount) { auto pState = ComputeState::create(mpDevice); - pState->setProgram(std::static_pointer_cast(shared_from_this())); + pState->setProgram(ref(this)); pContext->dispatch(pState.get(), pVars, threadGroupCount); } FALCOR_SCRIPT_BINDING(ComputeProgram) { - pybind11::class_(m, "ComputeProgram"); + pybind11::class_>(m, "ComputeProgram"); } } // namespace Falcor diff --git a/Source/Falcor/Core/Program/ComputeProgram.h b/Source/Falcor/Core/Program/ComputeProgram.h index 82ba05ae3..632939257 100644 --- a/Source/Falcor/Core/Program/ComputeProgram.h +++ b/Source/Falcor/Core/Program/ComputeProgram.h @@ -44,8 +44,6 @@ class ComputeVars; class FALCOR_API ComputeProgram : public Program { public: - using SharedPtr = std::shared_ptr; - ~ComputeProgram() = default; /** @@ -59,8 +57,8 @@ class FALCOR_API ComputeProgram : public Program * @param[in] shaderModel Optional string describing which shader model to use. * @return A new object, or an exception is thrown if creation failed. */ - static SharedPtr createFromFile( - std::shared_ptr pDevice, + static ref createFromFile( + ref pDevice, const std::filesystem::path& path, const std::string& csEntry, const DefineList& programDefines = DefineList(), @@ -76,14 +74,14 @@ class FALCOR_API ComputeProgram : public Program * @param[in] programDefines Optional list of macro definitions to set into the program. * @return A new object, or an exception is thrown if creation failed. */ - static SharedPtr create(std::shared_ptr pDevice, const Desc& desc, const DefineList& programDefines = DefineList()); + static ref create(ref pDevice, const Desc& desc, const DefineList& programDefines = DefineList()); /** * Dispatch the program using the argument values set in `pVars`. */ - virtual void dispatchCompute(ComputeContext* pContext, ComputeVars* pVars, uint3 const& threadGroupCount); + virtual void dispatchCompute(ComputeContext* pContext, ComputeVars* pVars, const uint3& threadGroupCount); protected: - ComputeProgram(std::shared_ptr pDevice, const Desc& desc, const DefineList& programDefines); + ComputeProgram(ref pDevice, const Desc& desc, const DefineList& programDefines); }; } // namespace Falcor diff --git a/Source/Falcor/Core/Program/GraphicsProgram.cpp b/Source/Falcor/Core/Program/GraphicsProgram.cpp index e71935c45..c12c62be8 100644 --- a/Source/Falcor/Core/Program/GraphicsProgram.cpp +++ b/Source/Falcor/Core/Program/GraphicsProgram.cpp @@ -27,24 +27,19 @@ **************************************************************************/ #include "GraphicsProgram.h" #include "ProgramManager.h" +#include "Core/ObjectPython.h" #include "Core/API/Device.h" #include "Utils/Scripting/ScriptBindings.h" namespace Falcor { -GraphicsProgram::SharedPtr GraphicsProgram::create( - std::shared_ptr pDevice, - const Desc& desc, - const Program::DefineList& programDefines -) +ref GraphicsProgram::create(ref pDevice, const Desc& desc, const Program::DefineList& programDefines) { - auto pProgram = SharedPtr(new GraphicsProgram(pDevice, desc, programDefines)); - pDevice->getProgramManager()->registerProgramForReload(pProgram); - return pProgram; + return ref(new GraphicsProgram(pDevice, desc, programDefines)); } -GraphicsProgram::SharedPtr GraphicsProgram::createFromFile( - std::shared_ptr pDevice, +ref GraphicsProgram::createFromFile( + ref pDevice, const std::filesystem::path& path, const std::string& vsEntry, const std::string& psEntry, @@ -53,15 +48,15 @@ GraphicsProgram::SharedPtr GraphicsProgram::createFromFile( { Desc d(path); d.vsEntry(vsEntry).psEntry(psEntry); - return create(std::move(pDevice), d, programDefines); + return create(pDevice, d, programDefines); } -GraphicsProgram::GraphicsProgram(std::shared_ptr pDevice, const Desc& desc, const Program::DefineList& programDefines) - : Program(std::move(pDevice), desc, programDefines) +GraphicsProgram::GraphicsProgram(ref pDevice, const Desc& desc, const Program::DefineList& programDefines) + : Program(pDevice, desc, programDefines) {} FALCOR_SCRIPT_BINDING(GraphicsProgram) { - pybind11::class_(m, "GraphicsProgram"); + pybind11::class_>(m, "GraphicsProgram"); } } // namespace Falcor diff --git a/Source/Falcor/Core/Program/GraphicsProgram.h b/Source/Falcor/Core/Program/GraphicsProgram.h index 16a6c8518..1934c1ed0 100644 --- a/Source/Falcor/Core/Program/GraphicsProgram.h +++ b/Source/Falcor/Core/Program/GraphicsProgram.h @@ -40,8 +40,6 @@ namespace Falcor class FALCOR_API GraphicsProgram : public Program { public: - using SharedPtr = std::shared_ptr; - ~GraphicsProgram() = default; /** @@ -53,7 +51,7 @@ class FALCOR_API GraphicsProgram : public Program * stages. * @return A new object, or an exception is thrown if creation failed. */ - static SharedPtr create(std::shared_ptr pDevice, const Desc& desc, const Program::DefineList& programDefines = DefineList()); + static ref create(ref pDevice, const Desc& desc, const Program::DefineList& programDefines = DefineList()); /** * Create a new graphics program from file. @@ -66,8 +64,8 @@ class FALCOR_API GraphicsProgram : public Program * stages. * @return A new object, or an exception is thrown if creation failed. */ - static SharedPtr createFromFile( - std::shared_ptr pDevice, + static ref createFromFile( + ref pDevice, const std::filesystem::path& path, const std::string& vsEntry, const std::string& psEntry, @@ -75,6 +73,6 @@ class FALCOR_API GraphicsProgram : public Program ); private: - GraphicsProgram(std::shared_ptr pDevice, const Desc& desc, const Program::DefineList& programDefines); + GraphicsProgram(ref pDevice, const Desc& desc, const Program::DefineList& programDefines); }; } // namespace Falcor diff --git a/Source/Falcor/Core/Program/Program.cpp b/Source/Falcor/Core/Program/Program.cpp index 420ca1564..1971e5b24 100644 --- a/Source/Falcor/Core/Program/Program.cpp +++ b/Source/Falcor/Core/Program/Program.cpp @@ -28,6 +28,7 @@ #include "Program.h" #include "ProgramManager.h" #include "ProgramVars.h" +#include "Core/ObjectPython.h" #include "Core/Platform/OS.h" #include "Core/API/Device.h" #include "Core/API/ParameterBlock.h" @@ -96,7 +97,7 @@ Program::Desc& Program::Desc::beginEntryPointGroup(const std::string& entryPoint return *this; } -Program::Desc& Program::Desc::entryPoint(ShaderType shaderType, std::string const& name) +Program::Desc& Program::Desc::entryPoint(ShaderType shaderType, const std::string& name) { checkArgument(!name.empty(), "Missing entry point name."); @@ -182,12 +183,22 @@ Program::Desc& Program::Desc::setShaderModel(const std::string& sm) return *this; } -Program::Program(std::shared_ptr pDevice, Desc const& desc, DefineList const& defineList) - : mpDevice(std::move(pDevice)), mDesc(desc), mDefineList(defineList), mTypeConformanceList(desc.mTypeConformances) +Program::Program(ref pDevice, const Desc& desc, const DefineList& defineList) + : mpDevice(pDevice), mDesc(desc), mDefineList(defineList), mTypeConformanceList(desc.mTypeConformances) { + mpDevice->getProgramManager()->registerProgramForReload(this); validateEntryPoints(); } +Program::~Program() +{ + mpDevice->getProgramManager()->unregisterProgramForReload(this); + + // Invalidate program versions. + for (auto& version : mProgramVersions) + version.second->mpProgram = nullptr; +} + void Program::validateEntryPoints() const { // Check that all exported entry point names are unique for each shader type. @@ -203,8 +214,6 @@ void Program::validateEntryPoints() const } } -Program::~Program() {} - std::string Program::getProgramDescString() const { std::string desc; @@ -383,11 +392,11 @@ bool Program::checkIfFilesChanged() return false; } -const ProgramVersion::SharedConstPtr& Program::getActiveVersion() const +const ref& Program::getActiveVersion() const { if (mLinkRequired) { - const auto& it = mProgramVersions.find(mDefineList); + const auto& it = mProgramVersions.find(ProgramVersionKey{mDefineList, mTypeConformanceList}); if (it == mProgramVersions.end()) { // Note that link() updates mActiveProgram only if the operation was successful. @@ -398,7 +407,7 @@ const ProgramVersion::SharedConstPtr& Program::getActiveVersion() const } else { - mProgramVersions[mDefineList] = mpActiveVersion; + mProgramVersions[ProgramVersionKey{mDefineList, mTypeConformanceList}] = mpActiveVersion; } } else @@ -448,8 +457,13 @@ void Program::reset() mLinkRequired = true; } +void Program::breakStrongReferenceToDevice() +{ + mpDevice.breakStrongReference(); +} + FALCOR_SCRIPT_BINDING(Program) { - pybind11::class_(m, "Program"); + pybind11::class_>(m, "Program"); } } // namespace Falcor diff --git a/Source/Falcor/Core/Program/Program.h b/Source/Falcor/Core/Program/Program.h index 09398284f..331252835 100644 --- a/Source/Falcor/Core/Program/Program.h +++ b/Source/Falcor/Core/Program/Program.h @@ -28,6 +28,7 @@ #pragma once #include "ProgramVersion.h" #include "Core/Macros.h" +#include "Core/Object.h" #include "Core/API/fwd.h" #include "Core/API/Shader.h" #include @@ -45,11 +46,9 @@ namespace Falcor * 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 FALCOR_API Program : public std::enable_shared_from_this +class FALCOR_API Program : public Object { public: - using SharedPtr = std::shared_ptr; - using DefineList = Shader::DefineList; using ArgumentList = std::vector; using TypeConformanceList = Shader::TypeConformanceList; @@ -298,7 +297,7 @@ class FALCOR_API Program : public std::enable_shared_from_this * Get the API handle of the active program. * @return The active program version, or an exception is thrown on failure. */ - const ProgramVersion::SharedConstPtr& getActiveVersion() const; + const ref& getActiveVersion() const; /** * Adds a macro definition to the program. If the macro already exists, it will be replaced. @@ -380,7 +379,7 @@ class FALCOR_API Program : public std::enable_shared_from_this * Get the program reflection for the active program. * @return Program reflection object, or an exception is thrown on failure. */ - const ProgramReflection::SharedPtr& getReflector() const { return getActiveVersion()->getReflector(); } + const ref& getReflector() const { return getActiveVersion()->getReflector(); } uint32_t getEntryPointGroupCount() const { return uint32_t(mDesc.mGroups.size()); } uint32_t getGroupEntryPointCount(uint32_t groupIndex) const { return (uint32_t)mDesc.mGroups[groupIndex].entryPoints.size(); } @@ -389,17 +388,19 @@ class FALCOR_API Program : public std::enable_shared_from_this return mDesc.mGroups[groupIndex].entryPoints[entryPointIndexInGroup]; } + void breakStrongReferenceToDevice(); + protected: friend class ProgramManager; friend class ProgramVersion; friend class ParameterBlockReflection; - Program(std::shared_ptr pDevice, Desc const& desc, DefineList const& programDefines); + Program(ref pDevice, const Desc& desc, const DefineList& programDefines); void validateEntryPoints() const; bool link() const; - std::shared_ptr mpDevice; + BreakableReference mpDevice; // The description used to create this program const Desc mDesc; @@ -407,10 +408,21 @@ class FALCOR_API Program : public std::enable_shared_from_this DefineList mDefineList; TypeConformanceList mTypeConformanceList; + struct ProgramVersionKey + { + DefineList defineList; + TypeConformanceList typeConformanceList; + + bool operator<(const ProgramVersionKey& rhs) const + { + return std::tie(defineList, typeConformanceList) < std::tie(rhs.defineList, rhs.typeConformanceList); + } + }; + // We are doing lazy compilation, so these are mutable mutable bool mLinkRequired = true; - mutable std::map mProgramVersions; - mutable ProgramVersion::SharedConstPtr mpActiveVersion; + mutable std::map> mProgramVersions; + mutable ref mpActiveVersion; void markDirty() { mLinkRequired = true; } std::string getProgramDescString() const; diff --git a/Source/Falcor/Core/Program/ProgramManager.cpp b/Source/Falcor/Core/Program/ProgramManager.cpp index 542b0976a..d8633e376 100644 --- a/Source/Falcor/Core/Program/ProgramManager.cpp +++ b/Source/Falcor/Core/Program/ProgramManager.cpp @@ -28,6 +28,7 @@ #include "ProgramManager.h" #include "Core/API/Device.h" #include "Core/Platform/OS.h" +#include "Utils/Logger.h" #include "Utils/Timing/CpuTimer.h" #include @@ -77,8 +78,8 @@ inline std::string getSlangProfileString(const std::string& shaderModel) inline bool doSlangReflection( const ProgramVersion& programVersion, slang::IComponentType* pSlangGlobalScope, - std::vector> pSlangLinkedEntryPoints, - ProgramReflection::SharedPtr& pReflector, + std::vector> pSlangLinkedEntryPoints, + ref& pReflector, std::string& log ) { @@ -99,9 +100,9 @@ inline bool doSlangReflection( return true; } -ProgramManager::ProgramManager(std::weak_ptr pDevice) : mpDevice(pDevice) {} +ProgramManager::ProgramManager(Device* pDevice) : mpDevice(pDevice) {} -ProgramVersion::SharedPtr ProgramManager::createProgramVersion(const Program& program, std::string& log) const +ref ProgramManager::createProgramVersion(const Program& program, std::string& log) const { CpuTimer timer; timer.update(); @@ -118,17 +119,17 @@ ProgramVersion::SharedPtr ProgramManager::createProgramVersion(const Program& pr return nullptr; } - ComPtr pSlangGlobalScope; + Slang::ComPtr pSlangGlobalScope; spCompileRequest_getProgram(pSlangRequest, pSlangGlobalScope.writeRef()); - ComPtr pSlangSession(pSlangGlobalScope->getSession()); + Slang::ComPtr pSlangSession(pSlangGlobalScope->getSession()); // Prepare entry points. - std::vector> pSlangEntryPoints; + std::vector> pSlangEntryPoints; uint32_t entryPointCount = (uint32_t)program.mDesc.mEntryPoints.size(); for (uint32_t ee = 0; ee < entryPointCount; ++ee) { - ComPtr pSlangEntryPoint; + Slang::ComPtr pSlangEntryPoint; spCompileRequest_getEntryPoint(pSlangRequest, ee, pSlangEntryPoint.writeRef()); // Rename entry point in the generated code if the exported name differs from the source name. @@ -137,7 +138,7 @@ ProgramVersion::SharedPtr ProgramManager::createProgramVersion(const Program& pr const auto& entryPointDesc = program.mDesc.mEntryPoints[ee]; if (entryPointDesc.exportName != entryPointDesc.name) { - ComPtr pRenamedEntryPoint; + Slang::ComPtr pRenamedEntryPoint; pSlangEntryPoint->renameEntryPoint(entryPointDesc.exportName.c_str(), pRenamedEntryPoint.writeRef()); pSlangEntryPoints.push_back(pRenamedEntryPoint); } @@ -167,7 +168,7 @@ ProgramVersion::SharedPtr ProgramManager::createProgramVersion(const Program& pr // of Falcor they could be the same object. // // TODO @skallweit remove const cast - ProgramVersion::SharedPtr pVersion = ProgramVersion::createEmpty(const_cast(&program), pSlangGlobalScope); + ref pVersion = ProgramVersion::createEmpty(const_cast(&program), pSlangGlobalScope); // Note: Because of interactions between how `SV_Target` outputs // and `u` register bindings work in Slang today (as a compatibility @@ -179,10 +180,10 @@ ProgramVersion::SharedPtr ProgramManager::createProgramVersion(const Program& pr // to just use `pSlangGlobalScope` for the reflection step instead // of `pSlangProgram`. // - ComPtr pSlangProgram; + Slang::ComPtr pSlangProgram; spCompileRequest_getProgram(pSlangRequest, pSlangProgram.writeRef()); - ProgramReflection::SharedPtr pReflector; + ref pReflector; if (!doSlangReflection(*pVersion, pSlangGlobalScope, pSlangEntryPoints, pReflector, log)) { return nullptr; @@ -201,16 +202,13 @@ ProgramVersion::SharedPtr ProgramManager::createProgramVersion(const Program& pr return pVersion; } -ProgramKernels::SharedPtr ProgramManager::createProgramKernels( +ref ProgramManager::createProgramKernels( const Program& program, const ProgramVersion& programVersion, const ProgramVars& programVars, std::string& log ) const { - Device* pDevice = mpDevice.lock().get(); - FALCOR_ASSERT(pDevice); - CpuTimer timer; timer.update(); @@ -222,16 +220,16 @@ ProgramKernels::SharedPtr ProgramManager::createProgramKernels( // Create a composite component type that represents all type conformances // linked into the `ProgramVersion`. auto createTypeConformanceComponentList = [&](const Program::TypeConformanceList& typeConformances - ) -> std::optional> + ) -> std::optional> { - ComPtr pTypeConformancesCompositeComponent; - std::vector> typeConformanceComponentList; + Slang::ComPtr pTypeConformancesCompositeComponent; + std::vector> typeConformanceComponentList; std::vector typeConformanceComponentRawPtrList; for (auto& typeConformance : typeConformances) { - ComPtr pSlangDiagnostics; - ComPtr pTypeConformanceComponent; + Slang::ComPtr pSlangDiagnostics; + Slang::ComPtr pTypeConformanceComponent; // Look for the type and interface type specified by the type conformance. // If not found we'll log an error and return. @@ -270,7 +268,7 @@ ProgramKernels::SharedPtr ProgramManager::createProgramKernels( } if (!typeConformanceComponentList.empty()) { - ComPtr pSlangDiagnostics; + Slang::ComPtr pSlangDiagnostics; auto res = pSlangSession->createCompositeComponentType( &typeConformanceComponentRawPtrList[0], (SlangInt)typeConformanceComponentRawPtrList.size(), pTypeConformancesCompositeComponent.writeRef(), pSlangDiagnostics.writeRef() @@ -286,7 +284,7 @@ ProgramKernels::SharedPtr ProgramManager::createProgramKernels( // Create one composite component type for the type conformances of each entry point group. // The type conformances for each group is the combination of the global and group type conformances. - std::vector> typeConformancesCompositeComponents; + std::vector> typeConformancesCompositeComponents; typeConformancesCompositeComponents.reserve(program.getEntryPointGroupCount()); for (const auto& group : program.mDesc.mGroups) { @@ -301,9 +299,9 @@ ProgramKernels::SharedPtr ProgramManager::createProgramKernels( // Create a `IComponentType` for each entry point. uint32_t allEntryPointCount = uint32_t(program.mDesc.mEntryPoints.size()); - std::vector> pTypeConformanceSpecializedEntryPoints; + std::vector> pTypeConformanceSpecializedEntryPoints; std::vector pTypeConformanceSpecializedEntryPointsRawPtr; - std::vector> pLinkedEntryPoints; + std::vector> pLinkedEntryPoints; for (uint32_t ee = 0; ee < allEntryPointCount; ++ee) { @@ -312,9 +310,9 @@ ProgramKernels::SharedPtr ProgramManager::createProgramKernels( int32_t groupIndex = program.mDesc.mEntryPoints[ee].groupIndex; FALCOR_ASSERT(groupIndex >= 0 && groupIndex < typeConformancesCompositeComponents.size()); - ComPtr pSlangDiagnostics; + Slang::ComPtr pSlangDiagnostics; - ComPtr pTypeComformanceSpecializedEntryPoint; + Slang::ComPtr pTypeComformanceSpecializedEntryPoint; if (typeConformancesCompositeComponents[groupIndex]) { slang::IComponentType* componentTypes[] = {pSlangEntryPoint, typeConformancesCompositeComponents[groupIndex]}; @@ -334,7 +332,7 @@ ProgramKernels::SharedPtr ProgramManager::createProgramKernels( pTypeConformanceSpecializedEntryPoints.push_back(pTypeComformanceSpecializedEntryPoint); pTypeConformanceSpecializedEntryPointsRawPtr.push_back(pTypeComformanceSpecializedEntryPoint.get()); - ComPtr pLinkedSlangEntryPoint; + Slang::ComPtr pLinkedSlangEntryPoint; { slang::IComponentType* componentTypes[] = {pSpecializedSlangGlobalScope, pTypeComformanceSpecializedEntryPoint}; @@ -385,7 +383,7 @@ ProgramKernels::SharedPtr ProgramManager::createProgramKernels( // of `pSpecializedSlangProgram`, so long as we are okay with dropping // support for SM5.0 and below. // - ComPtr pSpecializedSlangProgram; + Slang::ComPtr pSpecializedSlangProgram; { // We are going to compose the global scope (specialized) with // all the entry points. Note that we do *not* use the "linked" @@ -425,17 +423,17 @@ ProgramKernels::SharedPtr ProgramManager::createProgramKernels( } } - ProgramReflection::SharedPtr pReflector; + ref pReflector; doSlangReflection(programVersion, pSpecializedSlangProgram, pLinkedEntryPoints, pReflector, log); // Create Shader objects for each entry point and cache them here. - std::vector allShaders; + std::vector> allShaders; for (uint32_t i = 0; i < allEntryPointCount; i++) { auto pLinkedEntryPoint = pLinkedEntryPoints[i]; auto entryPointDesc = program.mDesc.mEntryPoints[i]; - Shader::SharedPtr shader = + ref shader = Shader::create(pLinkedEntryPoint, entryPointDesc.stage, entryPointDesc.exportName, program.mDesc.getCompilerFlags(), log); if (!shader) return nullptr; @@ -446,7 +444,7 @@ ProgramKernels::SharedPtr ProgramManager::createProgramKernels( // In order to construct the `ProgramKernels` we need to extract // the kernels for each entry-point group. // - std::vector entryPointGroups; + std::vector> entryPointGroups; // TODO: Because we aren't actually specializing entry-point groups, // we will again loop over the original unspecialized entry point @@ -461,7 +459,7 @@ ProgramKernels::SharedPtr ProgramManager::createProgramKernels( // code for its constituent entry points, using the "linked" // version of the entry-point group. // - std::vector shaders; + std::vector> shaders; for (auto entryPointIndex : entryPointGroupDesc.entryPoints) { shaders.push_back(allShaders[entryPointIndex]); @@ -472,8 +470,8 @@ ProgramKernels::SharedPtr ProgramManager::createProgramKernels( } auto descStr = program.getProgramDescString(); - ProgramKernels::SharedPtr pProgramKernels = ProgramKernels::create( - pDevice, &programVersion, pSpecializedSlangGlobalScope, pTypeConformanceSpecializedEntryPointsRawPtr, pReflector, entryPointGroups, + ref pProgramKernels = ProgramKernels::create( + mpDevice, &programVersion, pSpecializedSlangGlobalScope, pTypeConformanceSpecializedEntryPointsRawPtr, pReflector, entryPointGroups, log, descStr ); @@ -487,9 +485,9 @@ ProgramKernels::SharedPtr ProgramManager::createProgramKernels( return pProgramKernels; } -EntryPointGroupKernels::SharedPtr ProgramManager::createEntryPointGroupKernels( - const std::vector& shaders, - EntryPointBaseReflection::SharedPtr const& pReflector +ref ProgramManager::createEntryPointGroupKernels( + const std::vector>& shaders, + const ref& pReflector ) const { FALCOR_ASSERT(shaders.size() != 0); @@ -527,70 +525,29 @@ EntryPointGroupKernels::SharedPtr ProgramManager::createEntryPointGroupKernels( return nullptr; } -void ProgramManager::registerProgramForReload(const Program::SharedPtr& pProg) +void ProgramManager::registerProgramForReload(Program* program) +{ + mLoadedPrograms.push_back(program); +} + +void ProgramManager::unregisterProgramForReload(Program* program) { - mLoadedPrograms.push_back(pProg); + mLoadedPrograms.erase(std::remove(mLoadedPrograms.begin(), mLoadedPrograms.end(), program), mLoadedPrograms.end()); } bool ProgramManager::reloadAllPrograms(bool forceReload) { bool hasReloaded = false; - // The `mLoadedPrograms` array stores weak pointers, and we will - // use this step as a chance to clean up the contents of - // the array that might have changed to `nullptr` because - // the `Program` has been deleted. - // - // We will do this cleanup in a single pass without creating - // a copy of the array by tracking two iterators: one for - // reading and one for writing. The write iterator will - // be explicit: - // - auto writeIter = mLoadedPrograms.begin(); - // - // The read iterator will be implicit in our loop over the - // entire array of programs: - // - for (auto& pWeakProgram : mLoadedPrograms) + for (auto program : mLoadedPrograms) { - // We will skip any programs where the weak pointer - // has changed to `nullptr` because the object was - // already deleted. - // - auto pProgram = pWeakProgram.lock(); - if (!pProgram) - continue; - - // Now we know that we have a valid (non-null) `Program`, - // so we wnat to keep it in the array for next time. - // - *writeIter++ = pProgram; - - // Next we check if any of the files that affected the - // compilation of `pProgram` has been changed. If not, - // we can skip further processing of this program - // (unless forceReload flag is set). - // - if (!(pProgram->checkIfFilesChanged() || forceReload)) - continue; - - // If any files have changed, then we need to reset - // the caches of compiled information for the program. - // - pProgram->reset(); - - hasReloaded = true; + if (program->checkIfFilesChanged() || forceReload) + { + program->reset(); + hasReloaded = true; + } } - // Once we are done, we will have written a compacted - // version of `mLoadedPrograms` (skipping the null elements) - // to the first N elements of the vector. To make the - // vector only contain those first N elements, we - // then need to erase everything past the last point - // we wrote to. - // - mLoadedPrograms.erase(writeIter, mLoadedPrograms.end()); - return hasReloaded; } @@ -629,10 +586,7 @@ ProgramManager::ForcedCompilerFlags ProgramManager::getForcedCompilerFlags() SlangCompileRequest* ProgramManager::createSlangCompileRequest(const Program& program) const { - auto pDevice = mpDevice.lock(); - FALCOR_ASSERT(pDevice); - - slang::IGlobalSession* pSlangGlobalSession = pDevice->getSlangGlobalSession(); + slang::IGlobalSession* pSlangGlobalSession = mpDevice->getSlangGlobalSession(); FALCOR_ASSERT(pSlangGlobalSession); slang::SessionDesc sessionDesc; @@ -693,7 +647,7 @@ SlangCompileRequest* ProgramManager::createSlangCompileRequest(const Program& pr const char* targetMacroName; // Pick the right target based on the current graphics API - switch (pDevice->getType()) + switch (mpDevice->getType()) { case Device::Type::D3D12: targetDesc.format = SLANG_DXIL; @@ -738,7 +692,7 @@ SlangCompileRequest* ProgramManager::createSlangCompileRequest(const Program& pr bool useColumnMajor = is_set(compilerFlags, Shader::CompilerFlags::MatrixLayoutColumnMajor); sessionDesc.defaultMatrixLayoutMode = useColumnMajor ? SLANG_MATRIX_LAYOUT_COLUMN_MAJOR : SLANG_MATRIX_LAYOUT_ROW_MAJOR; - ComPtr pSlangSession; + Slang::ComPtr pSlangSession; pSlangGlobalSession->createSession(sessionDesc, pSlangSession.writeRef()); FALCOR_ASSERT(pSlangSession); @@ -762,6 +716,7 @@ SlangCompileRequest* ProgramManager::createSlangCompileRequest(const Program& pr FALCOR_ASSERT(pSlangRequest); // Disable noisy warnings enabled in newer slang versions. + spOverrideDiagnosticSeverity(pSlangRequest, 15602, SLANG_SEVERITY_DISABLED); // #pragma once in modules spOverrideDiagnosticSeverity(pSlangRequest, 30081, SLANG_SEVERITY_DISABLED); // implicit conversion // Enable/disable intermediates dump diff --git a/Source/Falcor/Core/Program/ProgramManager.h b/Source/Falcor/Core/Program/ProgramManager.h index b843abc0c..87443e391 100644 --- a/Source/Falcor/Core/Program/ProgramManager.h +++ b/Source/Falcor/Core/Program/ProgramManager.h @@ -37,7 +37,7 @@ namespace Falcor class ProgramManager { public: - ProgramManager(std::weak_ptr pDevice); + ProgramManager(Device* pDevice); /** * Defines flags that should be forcefully disabled or enabled on all shaders. @@ -60,20 +60,21 @@ class ProgramManager }; Program::Desc applyForcedCompilerFlags(Program::Desc desc) const; - void registerProgramForReload(const Program::SharedPtr& pProg); + void registerProgramForReload(Program* program); + void unregisterProgramForReload(Program* program); - ProgramVersion::SharedPtr createProgramVersion(const Program& program, std::string& log) const; + ref createProgramVersion(const Program& program, std::string& log) const; - ProgramKernels::SharedPtr ProgramManager::createProgramKernels( + ref createProgramKernels( const Program& program, const ProgramVersion& programVersion, const ProgramVars& programVars, std::string& log ) const; - EntryPointGroupKernels::SharedPtr createEntryPointGroupKernels( - const std::vector& shaders, - EntryPointBaseReflection::SharedPtr const& pReflector + ref createEntryPointGroupKernels( + const std::vector>& shaders, + const ref& pReflector ) const; /** @@ -127,9 +128,9 @@ class ProgramManager private: SlangCompileRequest* createSlangCompileRequest(const Program& program) const; - std::weak_ptr mpDevice; + Device* mpDevice; - std::vector> mLoadedPrograms; + std::vector mLoadedPrograms; mutable CompilationStats mCompilationStats; Program::DefineList mGlobalDefineList; diff --git a/Source/Falcor/Core/Program/ProgramReflection.cpp b/Source/Falcor/Core/Program/ProgramReflection.cpp index 7c13aa5b9..57c1e3618 100644 --- a/Source/Falcor/Core/Program/ProgramReflection.cpp +++ b/Source/Falcor/Core/Program/ProgramReflection.cpp @@ -44,8 +44,7 @@ namespace const char* kRootDescriptorAttribute = "root"; } -TypedShaderVarOffset::TypedShaderVarOffset(const ReflectionType* pType, ShaderVarOffset offset) - : ShaderVarOffset(offset), mpType(pType->shared_from_this()) +TypedShaderVarOffset::TypedShaderVarOffset(ref pType, ShaderVarOffset offset) : ShaderVarOffset(offset), mpType(pType) {} TypedShaderVarOffset TypedShaderVarOffset::operator[](const std::string& name) const @@ -59,7 +58,7 @@ TypedShaderVarOffset TypedShaderVarOffset::operator[](const std::string& name) c { if (auto pMember = pStructType->findMember(name)) { - return TypedShaderVarOffset(pMember->getType().get(), (*this) + pMember->getBindLocation()); + return TypedShaderVarOffset(pMember->getType(), (*this) + pMember->getBindLocation()); } } @@ -79,7 +78,7 @@ TypedShaderVarOffset TypedShaderVarOffset::operator[](size_t index) const TypedShaderVarOffset ReflectionType::getZeroOffset() const { - return TypedShaderVarOffset(this, ShaderVarOffset::kZero); + return TypedShaderVarOffset(ref(this), ShaderVarOffset::kZero); } TypedShaderVarOffset ReflectionType::getMemberOffset(const std::string& name) const @@ -612,7 +611,7 @@ static ReflectionResourceType::StructuredType getStructuredBufferType(TypeReflec } }; -ReflectionVar::SharedPtr reflectVariable( +ref reflectVariable( VariableLayoutReflection* pSlangLayout, ShaderVarOffset::RangeIndex rangeIndex, ParameterBlockReflection* pBlock, @@ -620,7 +619,7 @@ ReflectionVar::SharedPtr reflectVariable( ProgramVersion const* pProgramVersion ); -ReflectionType::SharedPtr reflectType( +ref reflectType( TypeLayoutReflection* pSlangType, ParameterBlockReflection* pBlock, ReflectionPath* pPath, @@ -753,7 +752,7 @@ static void extractDefaultConstantBufferBinding( } } -ReflectionType::SharedPtr reflectResourceType( +ref reflectResourceType( TypeLayoutReflection* pSlangType, ParameterBlockReflection* pBlock, ReflectionPath* pPath, @@ -818,7 +817,7 @@ ReflectionType::SharedPtr reflectResourceType( ); // We shouldn't get here otherwise } - ReflectionResourceType::SharedPtr pType = ReflectionResourceType::create(type, dims, structuredType, retType, shaderAccess, pSlangType); + ref pType = ReflectionResourceType::create(type, dims, structuredType, retType, shaderAccess, pSlangType); ParameterCategory category = getParameterCategory(pSlangType); ParameterBlockReflection::ResourceRangeBindingInfo bindingInfo; @@ -888,7 +887,7 @@ ReflectionType::SharedPtr reflectResourceType( return pType; } -ReflectionType::SharedPtr reflectStructType( +ref reflectStructType( TypeLayoutReflection* pSlangType, ParameterBlockReflection* pBlock, ReflectionPath* pPath, @@ -902,8 +901,7 @@ ReflectionType::SharedPtr reflectStructType( auto pSlangName = pSlangType->getName(); auto name = pSlangName ? std::string(pSlangName) : std::string(); - ReflectionStructType::SharedPtr pType = - ReflectionStructType::create(pSlangType->getSize(SLANG_PARAMETER_CATEGORY_UNIFORM), name, pSlangType); + ref pType = ReflectionStructType::create(pSlangType->getSize(SLANG_PARAMETER_CATEGORY_UNIFORM), name, pSlangType); ReflectionStructType::BuildState buildState; @@ -912,7 +910,7 @@ ReflectionType::SharedPtr reflectStructType( auto pSlangField = pSlangType->getFieldByIndex(i); ExtendedReflectionPath fieldPath(pPath, pSlangField); - ReflectionVar::SharedPtr pVar = reflectVariable(pSlangField, pType->getResourceRangeCount(), pBlock, &fieldPath, pProgramVersion); + ref pVar = reflectVariable(pSlangField, pType->getResourceRangeCount(), pBlock, &fieldPath, pProgramVersion); if (pVar) pType->addMember(pVar, buildState); } @@ -924,7 +922,7 @@ static ReflectionType::ByteSize getByteSize(TypeLayoutReflection* pSlangType) return pSlangType->getSize(SLANG_PARAMETER_CATEGORY_UNIFORM); } -ReflectionType::SharedPtr reflectArrayType( +ref reflectArrayType( TypeLayoutReflection* pSlangType, ParameterBlockReflection* pBlock, ReflectionPath* pPath, @@ -934,21 +932,21 @@ ReflectionType::SharedPtr reflectArrayType( uint32_t elementCount = (uint32_t)pSlangType->getElementCount(); uint32_t elementByteStride = (uint32_t)pSlangType->getElementStride(SLANG_PARAMETER_CATEGORY_UNIFORM); - ReflectionType::SharedPtr pElementType = reflectType(pSlangType->getElementTypeLayout(), pBlock, pPath, pProgramVersion); - ReflectionArrayType::SharedPtr pArrayType = + ref pElementType = reflectType(pSlangType->getElementTypeLayout(), pBlock, pPath, pProgramVersion); + ref pArrayType = ReflectionArrayType::create(elementCount, elementByteStride, pElementType, getByteSize(pSlangType), pSlangType); return pArrayType; } -ReflectionType::SharedPtr reflectBasicType(TypeLayoutReflection* pSlangType) +ref reflectBasicType(TypeLayoutReflection* pSlangType) { const bool isRowMajor = pSlangType->getMatrixLayoutMode() == SLANG_MATRIX_LAYOUT_ROW_MAJOR; ReflectionBasicType::Type type = getVariableType(pSlangType->getScalarType(), pSlangType->getRowCount(), pSlangType->getColumnCount()); - ReflectionType::SharedPtr pType = ReflectionBasicType::create(type, isRowMajor, pSlangType->getSize(), pSlangType); + ref pType = ReflectionBasicType::create(type, isRowMajor, pSlangType->getSize(), pSlangType); return pType; } -ReflectionType::SharedPtr reflectInterfaceType( +ref reflectInterfaceType( TypeLayoutReflection* pSlangType, ParameterBlockReflection* pBlock, ReflectionPath* pPath, @@ -995,7 +993,7 @@ ReflectionType::SharedPtr reflectInterfaceType( return pType; } -ReflectionType::SharedPtr reflectSpecializedType( +ref reflectSpecializedType( TypeLayoutReflection* pSlangType, ParameterBlockReflection* pBlock, ReflectionPath* pPath, @@ -1017,7 +1015,7 @@ ReflectionType::SharedPtr reflectSpecializedType( return reflectType(pSlangBaseType, pBlock, &path, pProgramVersion); } -ReflectionType::SharedPtr reflectType( +ref reflectType( TypeLayoutReflection* pSlangType, ParameterBlockReflection* pBlock, ReflectionPath* pPath, @@ -1080,7 +1078,7 @@ static ParameterCategory getParameterCategory(VariableLayoutReflection* pVarLayo return getParameterCategory(pVarLayout->getTypeLayout()); } -ReflectionVar::SharedPtr reflectVariable( +ref reflectVariable( VariableLayoutReflection* pSlangLayout, ShaderVarOffset::RangeIndex rangeIndex, ParameterBlockReflection* pBlock, @@ -1091,16 +1089,16 @@ ReflectionVar::SharedPtr reflectVariable( FALCOR_ASSERT(pPath); std::string name(pSlangLayout->getName()); - ReflectionType::SharedPtr pType = reflectType(pSlangLayout->getTypeLayout(), pBlock, pPath, pProgramVersion); + ref pType = reflectType(pSlangLayout->getTypeLayout(), pBlock, pPath, pProgramVersion); auto byteOffset = (ShaderVarOffset::ByteOffset)pSlangLayout->getOffset(SLANG_PARAMETER_CATEGORY_UNIFORM); - ReflectionVar::SharedPtr pVar = + ref pVar = ReflectionVar::create(name, pType, ShaderVarOffset(UniformShaderVarOffset(byteOffset), ResourceShaderVarOffset(rangeIndex, 0))); return pVar; } -ReflectionVar::SharedPtr reflectTopLevelVariable( +ref reflectTopLevelVariable( VariableLayoutReflection* pSlangLayout, ShaderVarOffset::RangeIndex rangeIndex, ParameterBlockReflection* pBlock, @@ -1209,24 +1207,14 @@ static void reflectShaderIO( } } -ProgramReflection::SharedPtr ProgramReflection::create( +ref ProgramReflection::create( ProgramVersion const* pProgramVersion, slang::ShaderReflection* pSlangReflector, - std::vector const& pSlangEntryPointReflectors, + const std::vector& pSlangEntryPointReflectors, std::string& log ) { - return SharedPtr(new ProgramReflection(pProgramVersion, pSlangReflector, pSlangEntryPointReflectors, log)); -} - -ProgramReflection::SharedPtr ProgramReflection::createEmpty() -{ - return SharedPtr(new ProgramReflection(nullptr)); -} - -std::shared_ptr ParameterBlockReflection::getProgramVersion() const -{ - return mpProgramVersion->shared_from_this(); + return ref(new ProgramReflection(pProgramVersion, pSlangReflector, pSlangEntryPointReflectors, log)); } void ProgramReflection::finalize() @@ -1236,9 +1224,9 @@ void ProgramReflection::finalize() ProgramReflection::ProgramReflection(ProgramVersion const* pProgramVersion) : mpProgramVersion(pProgramVersion) { - ReflectionStructType::SharedPtr pGlobalStruct = ReflectionStructType::create(0, "", nullptr); + ref pGlobalStruct = ReflectionStructType::create(0, "", nullptr); - ParameterBlockReflection::SharedPtr pDefaultBlock = ParameterBlockReflection::createEmpty(pProgramVersion); + ref pDefaultBlock = ParameterBlockReflection::createEmpty(pProgramVersion); pDefaultBlock->setElementType(pGlobalStruct); setDefaultParameterBlock(pDefaultBlock); } @@ -1316,10 +1304,10 @@ static uint32_t getUniformParameterCount(slang::EntryPointReflection* pSlangEntr return uniformParamCount; } -EntryPointGroupReflection::SharedPtr EntryPointGroupReflection::create( +ref EntryPointGroupReflection::create( ProgramVersion const* pProgramVersion, uint32_t groupIndex, - std::vector const& pSlangEntryPointReflectors + const std::vector& pSlangEntryPointReflectors ) { // We are going to expect/require that all the entry points have the @@ -1344,7 +1332,7 @@ EntryPointGroupReflection::SharedPtr EntryPointGroupReflection::create( } } - auto pGroup = SharedPtr(new EntryPointGroupReflection(pProgramVersion)); + auto pGroup = ref(new EntryPointGroupReflection(pProgramVersion)); // The layout for an entry point either represents a Slang `struct` type // for the entry-point parameters, or it represents a Slang `ConstantBuffer` @@ -1485,7 +1473,7 @@ static ShaderType getShaderTypeFromSlangStage(SlangStage stage) ProgramReflection::ProgramReflection( ProgramVersion const* pProgramVersion, slang::ShaderReflection* pSlangReflector, - std::vector const& pSlangEntryPointReflectors, + const std::vector& pSlangEntryPointReflectors, std::string& log ) : mpProgramVersion(pProgramVersion), mpSlangReflector(pSlangReflector) @@ -1523,8 +1511,8 @@ ProgramReflection::ProgramReflection( // size_t slangGlobalParamsSize = pSlangGlobalParamsTypeLayout->getSize(SlangParameterCategory(slang::ParameterCategory::Uniform)); - ReflectionStructType::SharedPtr pGlobalStruct = ReflectionStructType::create(slangGlobalParamsSize, "", nullptr); - ParameterBlockReflection::SharedPtr pDefaultBlock = ParameterBlockReflection::createEmpty(pProgramVersion); + ref pGlobalStruct = ReflectionStructType::create(slangGlobalParamsSize, "", nullptr); + ref pDefaultBlock = ParameterBlockReflection::createEmpty(pProgramVersion); pDefaultBlock->setElementType(pGlobalStruct); ReflectionStructType::BuildState buildState; @@ -1532,7 +1520,7 @@ ProgramReflection::ProgramReflection( { VariableLayoutReflection* pSlangLayout = pSlangReflector->getParameterByIndex(i); - ReflectionVar::SharedPtr pVar = + ref pVar = reflectTopLevelVariable(pSlangLayout, pGlobalStruct->getResourceRangeCount(), pDefaultBlock.get(), pProgramVersion); if (pVar) pGlobalStruct->addMember(pVar, buildState); @@ -1547,7 +1535,7 @@ ProgramReflection::ProgramReflection( for (uint32_t gg = 0; gg < entryPointGroupCount; ++gg) { - EntryPointGroupReflection::SharedPtr pEntryPointGroup = + ref pEntryPointGroup = EntryPointGroupReflection::create(pProgramVersion, gg, pSlangEntryPointReflectors); mEntryPointGroups.push_back(pEntryPointGroup); } @@ -1589,13 +1577,13 @@ ProgramReflection::ProgramReflection( } } -void ProgramReflection::setDefaultParameterBlock(const ParameterBlockReflection::SharedPtr& pBlock) +void ProgramReflection::setDefaultParameterBlock(const ref& pBlock) { mpDefaultBlock = pBlock; } int32_t ReflectionStructType::addMemberIgnoringNameConflicts( - const std::shared_ptr& pVar, + const ref& pVar, ReflectionStructType::BuildState& ioBuildState ) { @@ -1652,7 +1640,7 @@ int32_t ReflectionStructType::addMemberIgnoringNameConflicts( return memberIndex; } -int32_t ReflectionStructType::addMember(const std::shared_ptr& pVar, ReflectionStructType::BuildState& ioBuildState) +int32_t ReflectionStructType::addMember(const ref& pVar, ReflectionStructType::BuildState& ioBuildState) { if (mNameToIndex.find(pVar->getName()) != mNameToIndex.end()) { @@ -1671,16 +1659,16 @@ int32_t ReflectionStructType::addMember(const std::shared_ptr ReflectionVar::create( const std::string& name, - const ReflectionType::SharedConstPtr& pType, - ShaderVarOffset const& bindLocation + const ref& pType, + const ShaderVarOffset& bindLocation ) { - return SharedPtr(new ReflectionVar(name, pType, bindLocation)); + return ref(new ReflectionVar(name, pType, bindLocation)); } -ReflectionVar::ReflectionVar(const std::string& name, const ReflectionType::SharedConstPtr& pType, ShaderVarOffset const& bindLocation) +ReflectionVar::ReflectionVar(const std::string& name, const ref& pType, const ShaderVarOffset& bindLocation) : mName(name), mpType(pType), mBindLocation(bindLocation) {} @@ -1688,20 +1676,20 @@ ReflectionVar::ReflectionVar(const std::string& name, const ReflectionType::Shar ParameterBlockReflection::ParameterBlockReflection(ProgramVersion const* pProgramVersion) : mpProgramVersion(pProgramVersion) {} -ParameterBlockReflection::SharedPtr ParameterBlockReflection::createEmpty(ProgramVersion const* pProgramVersion) +ref ParameterBlockReflection::createEmpty(ProgramVersion const* pProgramVersion) { - return SharedPtr(new ParameterBlockReflection(pProgramVersion)); + return ref(new ParameterBlockReflection(pProgramVersion)); } -void ParameterBlockReflection::setElementType(ReflectionType::SharedConstPtr const& pElementType) +void ParameterBlockReflection::setElementType(const ref& pElementType) { FALCOR_ASSERT(!mpElementType); mpElementType = pElementType; } -ParameterBlockReflection::SharedPtr ParameterBlockReflection::create( +ref ParameterBlockReflection::create( ProgramVersion const* pProgramVersion, - ReflectionType::SharedConstPtr const& pElementType + const ref& pElementType ) { auto pResult = createEmpty(pProgramVersion); @@ -1715,7 +1703,7 @@ ParameterBlockReflection::SharedPtr ParameterBlockReflection::create( auto rangeCount = pElementType->getResourceRangeCount(); for (uint32_t rangeIndex = 0; rangeIndex < rangeCount; ++rangeIndex) { - auto const& rangeInfo = pElementType->getResourceRange(rangeIndex); + const auto& rangeInfo = pElementType->getResourceRange(rangeIndex); ResourceRangeBindingInfo bindingInfo; @@ -1775,7 +1763,7 @@ ParameterBlockReflection::SharedPtr ParameterBlockReflection::create( return pResult; } -ParameterBlockReflection::SharedPtr ParameterBlockReflection::create( +ref ParameterBlockReflection::create( ProgramVersion const* pProgramVersion, slang::TypeLayoutReflection* pSlangElementType ) @@ -1828,12 +1816,12 @@ static ShaderResourceType getShaderResourceType(const ReflectionResourceType* pT } } -const ReflectionVar::SharedConstPtr ParameterBlockReflection::getResource(const std::string& name) const +const ref ParameterBlockReflection::getResource(const std::string& name) const { return getElementType()->findMember(name); } -void ParameterBlockReflection::addResourceRange(ResourceRangeBindingInfo const& bindingInfo) +void ParameterBlockReflection::addResourceRange(const ResourceRangeBindingInfo& bindingInfo) { mResourceRanges.push_back(bindingInfo); } @@ -1919,8 +1907,8 @@ struct ParameterBlockReflectionFinalizer continue; } - auto subRange = subSet.layout.getRange(r); - setInfo.layout.addRange(subRange.type, subRange.baseRegIndex, subRange.descCount, subRange.regSpace); + auto range = subSet.layout.getRange(r); + setInfo.layout.addRange(range.type, range.baseRegIndex, range.descCount, range.regSpace); } } } @@ -2034,12 +2022,12 @@ bool ParameterBlockReflection::hasDefaultConstantBuffer() const return getElementType()->getByteSize() != 0; } -void ParameterBlockReflection::setDefaultConstantBufferBindingInfo(DefaultConstantBufferBindingInfo const& info) +void ParameterBlockReflection::setDefaultConstantBufferBindingInfo(const DefaultConstantBufferBindingInfo& info) { mDefaultConstantBufferBindingInfo = info; } -ParameterBlockReflection::DefaultConstantBufferBindingInfo const& ParameterBlockReflection::getDefaultConstantBufferBindingInfo() const +const ParameterBlockReflection::DefaultConstantBufferBindingInfo& ParameterBlockReflection::getDefaultConstantBufferBindingInfo() const { return mDefaultConstantBufferBindingInfo; } @@ -2056,18 +2044,12 @@ void ParameterBlockReflection::finalize() #endif } -std::shared_ptr ProgramReflection::getProgramVersion() const -{ - return mpProgramVersion ? mpProgramVersion->shared_from_this() : ProgramVersion::SharedPtr(); -} - -ParameterBlockReflection::SharedConstPtr ProgramReflection::getParameterBlock(const std::string& name) const +ref ProgramReflection::getParameterBlock(const std::string& name) const { if (name == "") return mpDefaultBlock; - return mpDefaultBlock->getElementType()->findMember(name)->getType()->asResourceType()->getParameterBlockReflector()->shared_from_this( - ); + return mpDefaultBlock->getElementType()->findMember(name)->getType()->asResourceType()->getParameterBlockReflector(); } TypedShaderVarOffset ReflectionType::findMemberByOffset(size_t offset) const @@ -2093,7 +2075,7 @@ TypedShaderVarOffset ReflectionStructType::findMemberByOffset(size_t offset) con { if (offset < memberUniformOffset + memberByteSize) { - return TypedShaderVarOffset(pMemberType.get(), memberOffset); + return TypedShaderVarOffset(pMemberType, memberOffset); } } } @@ -2101,7 +2083,7 @@ TypedShaderVarOffset ReflectionStructType::findMemberByOffset(size_t offset) con return TypedShaderVarOffset::kInvalid; } -ReflectionVar::SharedConstPtr ReflectionType::findMember(const std::string& name) const +ref ReflectionType::findMember(const std::string& name) const { if (auto pStructType = asStructType()) { @@ -2123,9 +2105,9 @@ int32_t ReflectionStructType::getMemberIndex(const std::string& name) const return it->second; } -const ReflectionVar::SharedConstPtr& ReflectionStructType::getMember(const std::string& name) const +const ref& ReflectionStructType::getMember(const std::string& name) const { - static const ReflectionVar::SharedConstPtr pNull; + static const ref pNull; auto index = getMemberIndex(name); return (index == kInvalidMemberIndex) ? pNull : getMember(index); } @@ -2188,21 +2170,21 @@ uint32_t ReflectionType::getTotalArrayElementCount() const return result; } -ReflectionArrayType::SharedPtr ReflectionArrayType::create( +ref ReflectionArrayType::create( uint32_t arraySize, uint32_t arrayStride, - const ReflectionType::SharedConstPtr& pType, + const ref& pType, ByteSize byteSize, slang::TypeLayoutReflection* pSlangTypeLayout ) { - return SharedPtr(new ReflectionArrayType(arraySize, arrayStride, pType, byteSize, pSlangTypeLayout)); + return ref(new ReflectionArrayType(arraySize, arrayStride, pType, byteSize, pSlangTypeLayout)); } ReflectionArrayType::ReflectionArrayType( uint32_t elementCount, uint32_t elementByteStride, - const ReflectionType::SharedConstPtr& pElementType, + const ref& pElementType, ByteSize byteSize, slang::TypeLayoutReflection* pSlangTypeLayout ) @@ -2221,7 +2203,7 @@ ReflectionArrayType::ReflectionArrayType( } } -ReflectionResourceType::SharedPtr ReflectionResourceType::create( +ref ReflectionResourceType::create( Type type, Dimensions dims, StructuredType structuredType, @@ -2230,7 +2212,7 @@ ReflectionResourceType::SharedPtr ReflectionResourceType::create( slang::TypeLayoutReflection* pSlangTypeLayout ) { - return SharedPtr(new ReflectionResourceType(type, dims, structuredType, retType, shaderAccess, pSlangTypeLayout)); + return ref(new ReflectionResourceType(type, dims, structuredType, retType, shaderAccess, pSlangTypeLayout)); } ReflectionResourceType::ReflectionResourceType( @@ -2256,32 +2238,23 @@ ReflectionResourceType::ReflectionResourceType( mResourceRanges.push_back(range); } -void ReflectionResourceType::setStructType(const ReflectionType::SharedConstPtr& pType) +void ReflectionResourceType::setStructType(const ref& pType) { mpStructType = pType; } -ReflectionBasicType::SharedPtr ReflectionBasicType::create( - Type type, - bool isRowMajor, - size_t size, - slang::TypeLayoutReflection* pSlangTypeLayout -) +ref ReflectionBasicType::create(Type type, bool isRowMajor, size_t size, slang::TypeLayoutReflection* pSlangTypeLayout) { - return SharedPtr(new ReflectionBasicType(type, isRowMajor, size, pSlangTypeLayout)); + return ref(new ReflectionBasicType(type, isRowMajor, size, pSlangTypeLayout)); } ReflectionBasicType::ReflectionBasicType(Type type, bool isRowMajor, size_t size, slang::TypeLayoutReflection* pSlangTypeLayout) : ReflectionType(ReflectionType::Kind::Basic, size, pSlangTypeLayout), mType(type), mIsRowMajor(isRowMajor) {} -ReflectionStructType::SharedPtr ReflectionStructType::create( - size_t size, - const std::string& name, - slang::TypeLayoutReflection* pSlangTypeLayout -) +ref ReflectionStructType::create(size_t size, const std::string& name, slang::TypeLayoutReflection* pSlangTypeLayout) { - return SharedPtr(new ReflectionStructType(size, name, pSlangTypeLayout)); + return ref(new ReflectionStructType(size, name, pSlangTypeLayout)); } ReflectionStructType::ReflectionStructType(size_t size, const std::string& name, slang::TypeLayoutReflection* pSlangTypeLayout) @@ -2293,7 +2266,7 @@ ParameterBlockReflection::BindLocation ParameterBlockReflection::getResourceBind return getElementType()->getMemberOffset(name); } -const ReflectionVar::SharedConstPtr ProgramReflection::getResource(const std::string& name) const +const ref ProgramReflection::getResource(const std::string& name) const { return mpDefaultBlock->getResource(name); } @@ -2423,7 +2396,7 @@ const ProgramReflection::ShaderVariable* ProgramReflection::getPixelShaderOutput return getShaderAttribute(name, mPsOut, "getPixelShaderOutput()"); } -ReflectionType::SharedPtr ProgramReflection::findType(const std::string& name) const +ref ProgramReflection::findType(const std::string& name) const { auto iter = mMapNameToType.find(name); if (iter != mMapNameToType.end()) @@ -2443,14 +2416,14 @@ ReflectionType::SharedPtr ProgramReflection::findType(const std::string& name) c return pFalcorTypeLayout; } -ReflectionVar::SharedConstPtr ProgramReflection::findMember(const std::string& name) const +ref ProgramReflection::findMember(const std::string& name) const { return mpDefaultBlock->findMember(name); } -ReflectionInterfaceType::SharedPtr ReflectionInterfaceType::create(slang::TypeLayoutReflection* pSlangTypeLayout) +ref ReflectionInterfaceType::create(slang::TypeLayoutReflection* pSlangTypeLayout) { - return SharedPtr(new ReflectionInterfaceType(pSlangTypeLayout)); + return ref(new ReflectionInterfaceType(pSlangTypeLayout)); } ReflectionInterfaceType::ReflectionInterfaceType(slang::TypeLayoutReflection* pSlangTypeLayout) diff --git a/Source/Falcor/Core/Program/ProgramReflection.h b/Source/Falcor/Core/Program/ProgramReflection.h index dd93074e5..1efbedf5f 100644 --- a/Source/Falcor/Core/Program/ProgramReflection.h +++ b/Source/Falcor/Core/Program/ProgramReflection.h @@ -28,6 +28,7 @@ #pragma once #include "Core/Assert.h" #include "Core/Macros.h" +#include "Core/Object.h" #include "Core/API/ShaderResourceType.h" #include "Utils/Math/Vector.h" #if FALCOR_HAS_D3D12 @@ -37,7 +38,6 @@ #include #include -#include #include #include #include @@ -137,12 +137,12 @@ struct UniformShaderVarOffset /** * Compare this offset to another offset. */ - bool operator==(UniformShaderVarOffset const& other) const { return mByteOffset == other.mByteOffset; } + bool operator==(const UniformShaderVarOffset& other) const { return mByteOffset == other.mByteOffset; } /** * Compare this offset to another offset. */ - bool operator!=(UniformShaderVarOffset const& other) const { return mByteOffset != other.mByteOffset; } + bool operator!=(const UniformShaderVarOffset& other) const { return mByteOffset != other.mByteOffset; } /** * Compare this offset to an invalid offset. @@ -268,7 +268,7 @@ struct ResourceShaderVarOffset * * If either `this` or `other` is invalid, returns an invalid offset. */ - ResourceShaderVarOffset operator+(ResourceShaderVarOffset const& other) const + ResourceShaderVarOffset operator+(const ResourceShaderVarOffset& other) const { if (!isValid()) return kInvalid; @@ -281,7 +281,7 @@ struct ResourceShaderVarOffset /** * Compare with another offset. */ - bool operator==(ResourceShaderVarOffset const& other) const + bool operator==(const ResourceShaderVarOffset& other) const { return mRangeIndex == other.mRangeIndex && mArrayIndex == other.mArrayIndex; } @@ -289,7 +289,7 @@ struct ResourceShaderVarOffset /** * Compare with another offset. */ - bool operator!=(ResourceShaderVarOffset const& other) const { return !(*this == other); } + bool operator!=(const ResourceShaderVarOffset& other) const { return !(*this == other); } /** * Type used to store the resource/descriptor range. @@ -439,7 +439,7 @@ struct ShaderVarOffset * * If either `this` or `other` is invalid, returns an invalid offset. */ - ShaderVarOffset operator+(ShaderVarOffset const& other) const + ShaderVarOffset operator+(const ShaderVarOffset& other) const { if (!isValid()) return kInvalid; @@ -452,12 +452,12 @@ struct ShaderVarOffset /** * Compare to another offset. */ - bool operator==(ShaderVarOffset const& other) const { return mUniform == other.mUniform && mResource == other.mResource; } + bool operator==(const ShaderVarOffset& other) const { return mUniform == other.mUniform && mResource == other.mResource; } /** * Compare to another offset. */ - bool operator!=(ShaderVarOffset const& other) const { return !(*this == other); } + bool operator!=(const ShaderVarOffset& other) const { return !(*this == other); } /** * Type used to store the underlying uniform byte offset. @@ -543,7 +543,7 @@ struct TypedShaderVarOffset : ShaderVarOffset /** * Get the type of the shader variable. */ - std::shared_ptr getType() const { return mpType; } + ref getType() const { return mpType; } /** * Check if `this` represents a valid offset. @@ -571,21 +571,18 @@ struct TypedShaderVarOffset : ShaderVarOffset * The caller takes responsibility for ensuring that `pType` is a valid type * for the data at `offset`. */ - TypedShaderVarOffset(const ReflectionType* pType, ShaderVarOffset offset); + TypedShaderVarOffset(ref pType, ShaderVarOffset offset); private: - std::shared_ptr mpType; + ref mpType; }; /** * Reflection and layout information for a type in shader code. */ -class FALCOR_API ReflectionType : public std::enable_shared_from_this +class FALCOR_API ReflectionType : public Object { public: - using SharedPtr = std::shared_ptr; - using SharedConstPtr = std::shared_ptr; - virtual ~ReflectionType() = default; /** @@ -669,7 +666,7 @@ class FALCOR_API ReflectionType : public std::enable_shared_from_this findMember(const std::string& name) const; + ref findMember(const std::string& name) const; /** * Get the (type and) offset of a field/member with the given `name`. @@ -762,7 +759,7 @@ class FALCOR_API ReflectionType : public std::enable_shared_from_this; - using SharedConstPtr = std::shared_ptr; - /** * Create a new object */ - static SharedPtr create( + static ref create( uint32_t elementCount, uint32_t elementByteStride, - const ReflectionType::SharedConstPtr& pElementType, + const ref& pElementType, ByteSize byteSize, slang::TypeLayoutReflection* pSlangTypeLayout ); @@ -816,7 +810,7 @@ class FALCOR_API ReflectionArrayType : public ReflectionType /** * Get the type of the array elements. */ - const ReflectionType::SharedConstPtr& getElementType() const { return mpElementType; } + const ref& getElementType() const { return mpElementType; } bool operator==(const ReflectionArrayType& other) const; bool operator==(const ReflectionType& other) const override; @@ -825,14 +819,14 @@ class FALCOR_API ReflectionArrayType : public ReflectionType ReflectionArrayType( uint32_t elementCount, uint32_t elementByteStride, - const ReflectionType::SharedConstPtr& pElementType, + const ref& pElementType, ByteSize totalByteSize, slang::TypeLayoutReflection* pSlangTypeLayout ); uint32_t mElementCount = 0; uint32_t mElementByteStride = 0; - ReflectionType::SharedConstPtr mpElementType; + ref mpElementType; }; /** @@ -841,9 +835,6 @@ class FALCOR_API ReflectionArrayType : public ReflectionType class FALCOR_API ReflectionStructType : public ReflectionType { public: - using SharedPtr = std::shared_ptr; - using SharedConstPtr = std::shared_ptr; - /** * Get the name of the struct type */ @@ -857,17 +848,17 @@ class FALCOR_API ReflectionStructType : public ReflectionType /** * Get member by index */ - const std::shared_ptr& getMember(size_t index) const { return mMembers[index]; } + const ref& getMember(size_t index) const { return mMembers[index]; } /** * Get member by name */ - const std::shared_ptr& getMember(const std::string& name) const; + const ref& getMember(const std::string& name) const; /** * Constant used to indicate that member lookup failed. */ - static const int32_t kInvalidMemberIndex = -1; + static constexpr int32_t kInvalidMemberIndex = -1; /** * Get the index of a member @@ -891,7 +882,7 @@ class FALCOR_API ReflectionStructType : public ReflectionType * @param[in] size The size of the struct in bytes * @param[in] name The name of the struct */ - static SharedPtr create(size_t byteSize, const std::string& name, slang::TypeLayoutReflection* pSlangTypeLayout); + static ref create(size_t byteSize, const std::string& name, slang::TypeLayoutReflection* pSlangTypeLayout); struct BuildState { @@ -904,14 +895,14 @@ class FALCOR_API ReflectionStructType : public ReflectionType /** * Add a new member */ - int32_t addMember(const std::shared_ptr& pVar, BuildState& ioBuildState); + int32_t addMember(const ref& pVar, BuildState& ioBuildState); - int32_t addMemberIgnoringNameConflicts(const std::shared_ptr& pVar, BuildState& ioBuildState); + int32_t addMemberIgnoringNameConflicts(const ref& pVar, BuildState& ioBuildState); private: ReflectionStructType(size_t size, const std::string& name, slang::TypeLayoutReflection* pSlangTypeLayout); - std::vector> mMembers; // Struct members - std::unordered_map mNameToIndex; // Translates from a name to an index in mMembers + std::vector> mMembers; // Struct members + std::unordered_map mNameToIndex; // Translates from a name to an index in mMembers std::string mName; }; @@ -921,9 +912,6 @@ class FALCOR_API ReflectionStructType : public ReflectionType class FALCOR_API ReflectionBasicType : public ReflectionType { public: - using SharedPtr = std::shared_ptr; - using SharedConstPtr = std::shared_ptr; - /** * The type of the object */ @@ -1019,7 +1007,7 @@ class FALCOR_API ReflectionBasicType : public ReflectionType * @param[in] isRowMajor For matrices, true means row-major, otherwise it's column-major * @param[in] size The size of the object */ - static SharedPtr create(Type type, bool isRowMajor, size_t size, slang::TypeLayoutReflection* pSlangTypeLayout); + static ref create(Type type, bool isRowMajor, size_t size, slang::TypeLayoutReflection* pSlangTypeLayout); /** * Get the object's type @@ -1046,9 +1034,6 @@ class FALCOR_API ReflectionBasicType : public ReflectionType class FALCOR_API ReflectionResourceType : public ReflectionType { public: - using SharedPtr = std::shared_ptr; - using SharedConstPtr = std::shared_ptr; - /** * Describes how the shader will access the resource */ @@ -1121,7 +1106,7 @@ class FALCOR_API ReflectionResourceType : public ReflectionType /** * Create a new object */ - static SharedPtr create( + static ref create( Type type, Dimensions dims, StructuredType structuredType, @@ -1133,18 +1118,15 @@ class FALCOR_API ReflectionResourceType : public ReflectionType /** * For structured- and constant-buffers, set a reflection-type describing the buffer's layout */ - void setStructType(const ReflectionType::SharedConstPtr& pType); + void setStructType(const ref& pType); /** * Get the struct-type */ - const ReflectionType::SharedConstPtr& getStructType() const { return mpStructType; } + const ref& getStructType() const { return mpStructType; } - const std::shared_ptr& getParameterBlockReflector() const { return mpParameterBlockReflector; } - void setParameterBlockReflector(const std::shared_ptr& pReflector) - { - mpParameterBlockReflector = pReflector; - } + const ref& getParameterBlockReflector() const { return mpParameterBlockReflector; } + void setParameterBlockReflector(const ref& pReflector) { mpParameterBlockReflector = pReflector; } /** * Get the dimensions @@ -1194,8 +1176,8 @@ class FALCOR_API ReflectionResourceType : public ReflectionType ReturnType mReturnType; ShaderAccess mShaderAccess; Type mType; - ReflectionType::SharedConstPtr mpStructType; // For constant- and structured-buffers - std::shared_ptr mpParameterBlockReflector; // For constant buffers and parameter blocks + ref mpStructType; // For constant- and structured-buffers + ref mpParameterBlockReflector; // For constant buffers and parameter blocks }; /** @@ -1204,42 +1186,33 @@ class FALCOR_API ReflectionResourceType : public ReflectionType class FALCOR_API ReflectionInterfaceType : public ReflectionType { public: - using SharedPtr = std::shared_ptr; - using SharedConstPtr = std::shared_ptr; - - static SharedPtr create(slang::TypeLayoutReflection* pSlangTypeLayout); + static ref create(slang::TypeLayoutReflection* pSlangTypeLayout); bool operator==(const ReflectionInterfaceType& other) const; bool operator==(const ReflectionType& other) const override; - const std::shared_ptr& getParameterBlockReflector() const { return mpParameterBlockReflector; } - void setParameterBlockReflector(const std::shared_ptr& pReflector) - { - mpParameterBlockReflector = pReflector; - } + const ref& getParameterBlockReflector() const { return mpParameterBlockReflector; } + void setParameterBlockReflector(const ref& pReflector) { mpParameterBlockReflector = pReflector; } private: ReflectionInterfaceType(slang::TypeLayoutReflection* pSlangTypeLayout); - std::shared_ptr mpParameterBlockReflector; // For interface types that have been specialized + ref mpParameterBlockReflector; // For interface types that have been specialized }; /** * An object describing a variable */ -class FALCOR_API ReflectionVar +class FALCOR_API ReflectionVar : public Object { public: - using SharedPtr = std::shared_ptr; - using SharedConstPtr = std::shared_ptr; - /** * Create a new object * @param[in] name The name of the variable * @param[in] pType The type of the variable * @param[in] bindLocation The offset of the variable relative to the parent object */ - static SharedPtr create(const std::string& name, const ReflectionType::SharedConstPtr& pType, ShaderVarOffset const& bindLocation); + static ref create(const std::string& name, const ref& pType, const ShaderVarOffset& bindLocation); /** * Get the variable name @@ -1249,7 +1222,7 @@ class FALCOR_API ReflectionVar /** * Get the variable type */ - const ReflectionType::SharedConstPtr& getType() const { return mpType; } + const ref& getType() const { return mpType; } /** * Get the variable offset @@ -1262,10 +1235,10 @@ class FALCOR_API ReflectionVar bool operator!=(const ReflectionVar& other) const { return !(*this == other); } private: - ReflectionVar(const std::string& name, const ReflectionType::SharedConstPtr& pType, ShaderVarOffset const& bindLocation); + ReflectionVar(const std::string& name, const ref& pType, const ShaderVarOffset& bindLocation); std::string mName; - ReflectionType::SharedConstPtr mpType; + ref mpType; ShaderVarOffset mBindLocation; }; @@ -1274,28 +1247,25 @@ class ProgramReflection; /** * A reflection object describing a parameter block */ -class FALCOR_API ParameterBlockReflection : public std::enable_shared_from_this +class FALCOR_API ParameterBlockReflection : public Object { public: - using SharedPtr = std::shared_ptr; - using SharedConstPtr = std::shared_ptr; - - static const uint32_t kInvalidIndex = 0xffffffff; + static constexpr uint32_t kInvalidIndex = 0xffffffff; /** * Create a new parameter block reflector, for the given element type. */ - static SharedPtr create(ProgramVersion const* pProgramVersion, ReflectionType::SharedConstPtr const& pElementType); + static ref create(ProgramVersion const* pProgramVersion, const ref& pElementType); /** * Create a new shader object reflector, for the given element type. */ - static SharedPtr create(ProgramVersion const* pProgramVersion, slang::TypeLayoutReflection* pElementType); + static ref create(ProgramVersion const* pProgramVersion, slang::TypeLayoutReflection* pElementType); /** * Get the type of the contents of the parameter block. */ - ReflectionType::SharedConstPtr getElementType() const { return mpElementType; } + ref getElementType() const { return mpElementType; } using BindLocation = TypedShaderVarOffset; @@ -1304,7 +1274,7 @@ class FALCOR_API ParameterBlockReflection : public std::enable_shared_from_this< /** * Get the variable for a resource in the block */ - const ReflectionVar::SharedConstPtr getResource(const std::string& name) const; + const ref getResource(const std::string& name) const; /** * Get the bind-location for a resource in the block @@ -1395,7 +1365,7 @@ class FALCOR_API ParameterBlockReflection : public std::enable_shared_from_this< uint32_t descriptorSetIndex = kInvalidIndex; ///< The index of the descriptor set to be bound into, when flavor is Flavor::Simple. /// The reflection object for a sub-object range. - ParameterBlockReflection::SharedConstPtr pSubObjectReflector; + ref pSubObjectReflector; bool isDescriptorSet() const { return flavor == Flavor::Simple; } bool isRootDescriptor() const { return flavor == Flavor::RootDescriptor; } @@ -1409,30 +1379,30 @@ class FALCOR_API ParameterBlockReflection : public std::enable_shared_from_this< bool useRootConstants = false; }; - static SharedPtr createEmpty(ProgramVersion const* pProgramVersion); + static ref createEmpty(ProgramVersion const* pProgramVersion); - void setElementType(ReflectionType::SharedConstPtr const& pElementType); + void setElementType(const ref& pElementType); - void addResourceRange(ResourceRangeBindingInfo const& bindingInfo); + void addResourceRange(const ResourceRangeBindingInfo& bindingInfo); friend struct ParameterBlockReflectionFinalizer; void finalize(); bool hasDefaultConstantBuffer() const; - void setDefaultConstantBufferBindingInfo(DefaultConstantBufferBindingInfo const& info); - DefaultConstantBufferBindingInfo const& getDefaultConstantBufferBindingInfo() const; + void setDefaultConstantBufferBindingInfo(const DefaultConstantBufferBindingInfo& info); + const DefaultConstantBufferBindingInfo& getDefaultConstantBufferBindingInfo() const; /** * Get the number of descriptor ranges contained in this type. */ uint32_t getResourceRangeCount() const { return (uint32_t)mResourceRanges.size(); } - ReflectionType::ResourceRange const& getResourceRange(uint32_t index) const { return getElementType()->getResourceRange(index); } + const ReflectionType::ResourceRange& getResourceRange(uint32_t index) const { return getElementType()->getResourceRange(index); } /** * Get binding information on a contained descriptor range. */ - ResourceRangeBindingInfo const& getResourceRangeBindingInfo(uint32_t index) const { return mResourceRanges[index]; } + const ResourceRangeBindingInfo& getResourceRangeBindingInfo(uint32_t index) const { return mResourceRanges[index]; } uint32_t getRootDescriptorRangeCount() const { return (uint32_t)mRootDescriptorRangeIndices.size(); } uint32_t getRootDescriptorRangeIndex(uint32_t index) const { return mRootDescriptorRangeIndices[index]; } @@ -1440,9 +1410,9 @@ class FALCOR_API ParameterBlockReflection : public std::enable_shared_from_this< uint32_t getParameterBlockSubObjectRangeCount() const { return (uint32_t)mParameterBlockSubObjectRangeIndices.size(); } uint32_t getParameterBlockSubObjectRangeIndex(uint32_t index) const { return mParameterBlockSubObjectRangeIndices[index]; } - std::shared_ptr getProgramVersion() const; + ProgramVersion const* getProgramVersion() const { return mpProgramVersion; } - std::shared_ptr findMember(const std::string& name) const { return getElementType()->findMember(name); } + ref findMember(const std::string& name) const { return getElementType()->findMember(name); } protected: ParameterBlockReflection(ProgramVersion const* pProgramVersion); @@ -1453,7 +1423,7 @@ class FALCOR_API ParameterBlockReflection : public std::enable_shared_from_this< /// For a `ConstantBuffer` or `ParameterBlock`, /// this will be the type `T`. /// - ReflectionType::SharedConstPtr mpElementType; + ref mpElementType; /// Binding information for the "default" constant buffer, if needed. /// @@ -1504,13 +1474,10 @@ typedef ParameterBlockReflection ParameterBlockReflection; class FALCOR_API EntryPointGroupReflection : public ParameterBlockReflection { public: - using SharedPtr = std::shared_ptr; - using SharedConstPtr = std::shared_ptr; - - static SharedPtr create( + static ref create( ProgramVersion const* pProgramVersion, uint32_t groupIndex, - std::vector const& pSlangEntryPointReflectors + const std::vector& pSlangEntryPointReflectors ); private: @@ -1521,13 +1488,9 @@ typedef EntryPointGroupReflection EntryPointBaseReflection; /** * Reflection object for an entire program. Essentially, it's a collection of ParameterBlocks */ -class FALCOR_API ProgramReflection +class FALCOR_API ProgramReflection : public Object { public: - using SharedPtr = std::shared_ptr; - using SharedConstPtr = std::shared_ptr; - static const uint32_t kInvalidLocation = -1; - /** * Data structured describing a shader input/output variable. Used mostly to communicate VS inputs and PS outputs */ @@ -1553,27 +1516,26 @@ class FALCOR_API ProgramReflection /** * Create a new object for a Slang reflector object */ - static SharedPtr create( + static ref create( ProgramVersion const* pProgramVersion, slang::ShaderReflection* pSlangReflector, - std::vector const& pSlangEntryPointReflectors, + const std::vector& pSlangEntryPointReflectors, std::string& log ); - static SharedPtr createEmpty(); void finalize(); - std::shared_ptr getProgramVersion() const; + ProgramVersion const* getProgramVersion() const { return mpProgramVersion; } /** * Get parameter block by name */ - ParameterBlockReflection::SharedConstPtr getParameterBlock(const std::string& name) const; + ref getParameterBlock(const std::string& name) const; /** * Get the default (unnamed) parameter block. */ - ParameterBlockReflection::SharedConstPtr getDefaultParameterBlock() const { return mpDefaultBlock; } + ref getDefaultParameterBlock() const { return mpDefaultBlock; } /** * For compute-shaders, return the required thread-group size @@ -1588,7 +1550,7 @@ class FALCOR_API ProgramReflection /** * Get a resource from the default parameter block */ - const ReflectionVar::SharedConstPtr getResource(const std::string& name) const; + const ref getResource(const std::string& name) const; /** * Search for a vertex attribute by its semantic name @@ -1609,30 +1571,30 @@ class FALCOR_API ProgramReflection * Look up a type by name. * @return nullptr if the type does not exist. */ - ReflectionType::SharedPtr findType(const std::string& name) const; + ref findType(const std::string& name) const; - ReflectionVar::SharedConstPtr findMember(const std::string& name) const; + ref findMember(const std::string& name) const; - std::vector const& getEntryPointGroups() const { return mEntryPointGroups; } + const std::vector>& getEntryPointGroups() const { return mEntryPointGroups; } - EntryPointGroupReflection::SharedPtr const& getEntryPointGroup(uint32_t index) const { return mEntryPointGroups[index]; } + const ref& getEntryPointGroup(uint32_t index) const { return mEntryPointGroups[index]; } - std::vector const& getHashedStrings() const { return mHashedStrings; } + const std::vector& getHashedStrings() const { return mHashedStrings; } private: ProgramReflection( ProgramVersion const* pProgramVersion, slang::ShaderReflection* pSlangReflector, - std::vector const& pSlangEntryPointReflectors, + const std::vector& pSlangEntryPointReflectors, std::string& log ); ProgramReflection(ProgramVersion const* pProgramVersion); ProgramReflection(const ProgramReflection&) = default; - void setDefaultParameterBlock(const ParameterBlockReflection::SharedPtr& pBlock); + void setDefaultParameterBlock(const ref& pBlock); ProgramVersion const* mpProgramVersion; - ParameterBlockReflection::SharedPtr mpDefaultBlock; + ref mpDefaultBlock; uint3 mThreadGroupSize; bool mIsSampleFrequency = false; @@ -1641,9 +1603,9 @@ class FALCOR_API ProgramReflection VariableMap mVertAttrBySemantic; slang::ShaderReflection* mpSlangReflector = nullptr; - mutable std::map mMapNameToType; + mutable std::map> mMapNameToType; - std::vector mEntryPointGroups; + std::vector> mEntryPointGroups; std::vector mHashedStrings; }; diff --git a/Source/Falcor/Core/Program/ProgramVars.cpp b/Source/Falcor/Core/Program/ProgramVars.cpp index ab03acc6e..fecea7c1c 100644 --- a/Source/Falcor/Core/Program/ProgramVars.cpp +++ b/Source/Falcor/Core/Program/ProgramVars.cpp @@ -41,60 +41,52 @@ namespace Falcor { -ProgramVars::ProgramVars(std::shared_ptr pDevice, const ProgramReflection::SharedConstPtr& pReflector) - : ParameterBlock(std::move(pDevice), pReflector), mpReflector(pReflector) +ProgramVars::ProgramVars(ref pDevice, const ref& pReflector) + : ParameterBlock(pDevice, pReflector), mpReflector(pReflector) { FALCOR_ASSERT(pReflector); } -GraphicsVars::GraphicsVars(std::shared_ptr pDevice, const ProgramReflection::SharedConstPtr& pReflector) - : ProgramVars(std::move(pDevice), pReflector) -{} +GraphicsVars::GraphicsVars(ref pDevice, const ref& pReflector) : ProgramVars(pDevice, pReflector) {} -GraphicsVars::SharedPtr GraphicsVars::create(std::shared_ptr pDevice, const ProgramReflection::SharedConstPtr& pReflector) +ref GraphicsVars::create(ref pDevice, const ref& pReflector) { if (pReflector == nullptr) throw ArgumentError("Can't create a GraphicsVars object without a program reflector"); - return SharedPtr(new GraphicsVars(std::move(pDevice), pReflector)); + return ref(new GraphicsVars(pDevice, pReflector)); } -GraphicsVars::SharedPtr GraphicsVars::create(std::shared_ptr pDevice, const GraphicsProgram* pProg) +ref GraphicsVars::create(ref pDevice, const GraphicsProgram* pProg) { if (pProg == nullptr) throw ArgumentError("Can't create a GraphicsVars object without a program"); - return create(std::move(pDevice), pProg->getReflector()); + return create(pDevice, pProg->getReflector()); } -ComputeVars::SharedPtr ComputeVars::create(std::shared_ptr pDevice, const ProgramReflection::SharedConstPtr& pReflector) +ref ComputeVars::create(ref pDevice, const ref& pReflector) { if (pReflector == nullptr) throw ArgumentError("Can't create a ComputeVars object without a program reflector"); - return SharedPtr(new ComputeVars(std::move(pDevice), pReflector)); + return ref(new ComputeVars(pDevice, pReflector)); } -ComputeVars::SharedPtr ComputeVars::create(std::shared_ptr pDevice, const ComputeProgram* pProg) +ref ComputeVars::create(ref pDevice, const ComputeProgram* pProg) { if (pProg == nullptr) throw ArgumentError("Can't create a ComputeVars object without a program"); - return create(std::move(pDevice), pProg->getReflector()); + return create(pDevice, pProg->getReflector()); } -ComputeVars::ComputeVars(std::shared_ptr pDevice, const ProgramReflection::SharedConstPtr& pReflector) - : ProgramVars(pDevice, pReflector) -{} +ComputeVars::ComputeVars(ref pDevice, const ref& pReflector) : ProgramVars(pDevice, pReflector) {} -void ComputeVars::dispatchCompute(ComputeContext* pContext, uint3 const& threadGroupCount) +void ComputeVars::dispatchCompute(ComputeContext* pContext, const uint3& threadGroupCount) { - auto pProgram = std::dynamic_pointer_cast(getReflection()->getProgramVersion()->getProgram()); + auto pProgram = dynamic_cast(getReflection()->getProgramVersion()->getProgram()); FALCOR_ASSERT(pProgram); pProgram->dispatchCompute(pContext, this, threadGroupCount); } -RtProgramVars::RtProgramVars( - std::shared_ptr pDevice, - const RtProgram::SharedPtr& pProgram, - const RtBindingTable::SharedPtr& pBindingTable -) +RtProgramVars::RtProgramVars(ref pDevice, const ref& pProgram, const ref& pBindingTable) : ProgramVars(pDevice, pProgram->getReflector()), mpShaderTable(pDevice) { if (pProgram == nullptr) @@ -109,16 +101,12 @@ RtProgramVars::RtProgramVars( init(pBindingTable); } -RtProgramVars::SharedPtr RtProgramVars::create( - std::shared_ptr pDevice, - const RtProgram::SharedPtr& pProgram, - const RtBindingTable::SharedPtr& pBindingTable -) +ref RtProgramVars::create(ref pDevice, const ref& pProgram, const ref& pBindingTable) { - return SharedPtr(new RtProgramVars(pDevice, pProgram, pBindingTable)); + return ref(new RtProgramVars(pDevice, pProgram, pBindingTable)); } -void RtProgramVars::init(const RtBindingTable::SharedPtr& pBindingTable) +void RtProgramVars::init(const ref& pBindingTable) { mRayTypeCount = pBindingTable->getRayTypeCount(); mGeometryCount = pBindingTable->getGeometryCount(); @@ -127,8 +115,8 @@ void RtProgramVars::init(const RtBindingTable::SharedPtr& pBindingTable) // groups that are used by the supplied binding table. // FALCOR_ASSERT(mpProgramVersion); - FALCOR_ASSERT(dynamic_cast(mpProgramVersion->getProgram().get())); - auto pProgram = static_cast(mpProgramVersion->getProgram().get()); + auto pProgram = dynamic_cast(mpProgramVersion->getProgram()); + FALCOR_ASSERT(pProgram); auto pReflector = mpProgramVersion->getReflector(); auto& rtDesc = pProgram->getRtDesc(); @@ -137,27 +125,27 @@ void RtProgramVars::init(const RtBindingTable::SharedPtr& pBindingTable) // Ray generation and miss programs are easy: we just allocate space // for one parameter block per entry-point of the given type in the binding table. // - const auto& info = pBindingTable->getRayGen(); - FALCOR_ASSERT(info.isValid()); + const auto& rayGenInfo = pBindingTable->getRayGen(); + FALCOR_ASSERT(rayGenInfo.isValid()); mRayGenVars.resize(1); - mRayGenVars[0].entryPointGroupIndex = info.groupIndex; - entryPointGroupIndices.insert(info.groupIndex); + mRayGenVars[0].entryPointGroupIndex = rayGenInfo.groupIndex; + entryPointGroupIndices.insert(rayGenInfo.groupIndex); uint32_t missCount = pBindingTable->getMissCount(); mMissVars.resize(missCount); for (uint32_t i = 0; i < missCount; ++i) { - const auto& info = pBindingTable->getMiss(i); - if (!info.isValid()) + const auto& missInfo = pBindingTable->getMiss(i); + if (!missInfo.isValid()) { logWarning("Raytracing binding table has no shader at miss index {}. Is that intentional?", i); continue; } - mMissVars[i].entryPointGroupIndex = info.groupIndex; + mMissVars[i].entryPointGroupIndex = missInfo.groupIndex; - entryPointGroupIndices.insert(info.groupIndex); + entryPointGroupIndices.insert(missInfo.groupIndex); } // Hit groups are more complicated than ray generation and miss shaders. @@ -174,13 +162,13 @@ void RtProgramVars::init(const RtBindingTable::SharedPtr& pBindingTable) { for (uint32_t geometryID = 0; geometryID < mGeometryCount; geometryID++) { - const auto& info = pBindingTable->getHitGroup(rayType, geometryID); - if (!info.isValid()) + const auto& hitGroupInfo = pBindingTable->getHitGroup(rayType, geometryID); + if (!hitGroupInfo.isValid()) continue; - mHitVars[mRayTypeCount * geometryID + rayType].entryPointGroupIndex = info.groupIndex; + mHitVars[mRayTypeCount * geometryID + rayType].entryPointGroupIndex = hitGroupInfo.groupIndex; - entryPointGroupIndices.insert(info.groupIndex); + entryPointGroupIndices.insert(hitGroupInfo.groupIndex); } } diff --git a/Source/Falcor/Core/Program/ProgramVars.h b/Source/Falcor/Core/Program/ProgramVars.h index ea723ef51..5bc0c7700 100644 --- a/Source/Falcor/Core/Program/ProgramVars.h +++ b/Source/Falcor/Core/Program/ProgramVars.h @@ -48,31 +48,27 @@ class ComputeContext; class FALCOR_API ProgramVars : public ParameterBlock { public: - using SharedPtr = ParameterBlockSharedPtr; - /** * Get the program reflection interface */ - const ProgramReflection::SharedConstPtr& getReflection() const { return mpReflector; } + const ref& getReflection() const { return mpReflector; } protected: - ProgramVars(std::shared_ptr pDevice, const ProgramReflection::SharedConstPtr& pReflector); + ProgramVars(ref pDevice, const ref& pReflector); - ProgramReflection::SharedConstPtr mpReflector; + ref mpReflector; }; class FALCOR_API GraphicsVars : public ProgramVars { public: - using SharedPtr = ParameterBlockSharedPtr; - /** * Create a new graphics vars object. * @param[in] pDevice GPU device. * @param[in] pReflector A program reflection object containing the requested declarations. * @return A new object, or an exception is thrown if creation failed. */ - static SharedPtr create(std::shared_ptr pDevice, const ProgramReflection::SharedConstPtr& pReflector); + static ref create(ref pDevice, const ref& pReflector); /** * Create a new graphics vars object. @@ -80,24 +76,22 @@ class FALCOR_API GraphicsVars : public ProgramVars * @param[in] pProg A program containing the requested declarations. The active version of the program is used. * @return A new object, or an exception is thrown if creation failed. */ - static SharedPtr create(std::shared_ptr pDevice, const GraphicsProgram* pProg); + static ref create(ref pDevice, const GraphicsProgram* pProg); protected: - GraphicsVars(std::shared_ptr pDevice, const ProgramReflection::SharedConstPtr& pReflector); + GraphicsVars(ref pDevice, const ref& pReflector); }; class FALCOR_API ComputeVars : public ProgramVars { public: - using SharedPtr = ParameterBlockSharedPtr; - /** * Create a new compute vars object. * @param[in] pDevice GPU device. * @param[in] pReflector A program reflection object containing the requested declarations. * @return A new object, or an exception is thrown if creation failed. */ - static SharedPtr create(std::shared_ptr pDevice, const ProgramReflection::SharedConstPtr& pReflector); + static ref create(ref pDevice, const ref& pReflector); /** * Create a new compute vars object. @@ -105,15 +99,15 @@ class FALCOR_API ComputeVars : public ProgramVars * @param[in] pProg A program containing the requested declarations. The active version of the program is used. * @return A new object, or an exception is thrown if creation failed. */ - static SharedPtr create(std::shared_ptr pDevice, const ComputeProgram* pProg); + static ref create(ref pDevice, const ComputeProgram* pProg); /** * Dispatch the program using the argument values set in this object. */ - void dispatchCompute(ComputeContext* pContext, uint3 const& threadGroupCount); + void dispatchCompute(ComputeContext* pContext, const uint3& threadGroupCount); protected: - ComputeVars(std::shared_ptr pDevice, const ProgramReflection::SharedConstPtr& pReflector); + ComputeVars(ref pDevice, const ref& pReflector); }; class RtStateObject; @@ -124,8 +118,6 @@ class RtStateObject; class FALCOR_API RtProgramVars : public ProgramVars { public: - using SharedPtr = ParameterBlockSharedPtr; - /** * Create a new ray tracing vars object. * @param[in] pDevice GPU device. @@ -133,11 +125,7 @@ class FALCOR_API RtProgramVars : public ProgramVars * @param[in] pBindingTable The raytracing binding table. * @return A new object, or an exception is thrown if creation failed. */ - static SharedPtr create( - std::shared_ptr pDevice, - const RtProgram::SharedPtr& pProgram, - const RtBindingTable::SharedPtr& pBindingTable - ); + static ref create(ref pDevice, const ref& pProgram, const ref& pBindingTable); bool prepareShaderTable(RenderContext* pCtx, RtStateObject* pRtso); @@ -157,9 +145,9 @@ class FALCOR_API RtProgramVars : public ProgramVars using VarsVector = std::vector; - RtProgramVars(std::shared_ptr pDevice, const RtProgram::SharedPtr& pProgram, const RtBindingTable::SharedPtr& pBindingTable); + RtProgramVars(ref pDevice, const ref& pProgram, const ref& pBindingTable); - void init(const RtBindingTable::SharedPtr& pBindingTable); + void init(const ref& pBindingTable); uint32_t mRayTypeCount = 0; ///< Number of ray types (= number of hit groups per geometry). uint32_t mGeometryCount = 0; ///< Number of geometries. diff --git a/Source/Falcor/Core/Program/ProgramVersion.cpp b/Source/Falcor/Core/Program/ProgramVersion.cpp index 0ea6564bd..9e6405a33 100644 --- a/Source/Falcor/Core/Program/ProgramVersion.cpp +++ b/Source/Falcor/Core/Program/ProgramVersion.cpp @@ -44,13 +44,13 @@ namespace Falcor // EntryPointGroupKernels // -EntryPointGroupKernels::SharedPtr EntryPointGroupKernels::create( +ref EntryPointGroupKernels::create( EntryPointGroupKernels::Type type, const EntryPointGroupKernels::Shaders& shaders, const std::string& exportName ) { - return SharedPtr(new EntryPointGroupKernels(type, shaders, exportName)); + return ref(new EntryPointGroupKernels(type, shaders, exportName)); } EntryPointGroupKernels::EntryPointGroupKernels(Type type, const Shaders& shaders, const std::string& exportName) @@ -73,25 +73,25 @@ const Shader* EntryPointGroupKernels::getShader(ShaderType type) const ProgramKernels::ProgramKernels( const ProgramVersion* pVersion, - const ProgramReflection::SharedPtr& pReflector, + const ref& pReflector, const ProgramKernels::UniqueEntryPointGroups& uniqueEntryPointGroups, const std::string& name ) : mName(name), mUniqueEntryPointGroups(uniqueEntryPointGroups), mpReflector(pReflector), mpVersion(pVersion) {} -ProgramKernels::SharedPtr ProgramKernels::create( +ref ProgramKernels::create( Device* pDevice, const ProgramVersion* pVersion, slang::IComponentType* pSpecializedSlangGlobalScope, const std::vector& pTypeConformanceSpecializedEntryPoints, - const ProgramReflection::SharedPtr& pReflector, + const ref& pReflector, const ProgramKernels::UniqueEntryPointGroups& uniqueEntryPointGroups, std::string& log, const std::string& name ) { - SharedPtr pProgram = SharedPtr(new ProgramKernels(pVersion, pReflector, uniqueEntryPointGroups, name)); + ref pProgram = ref(new ProgramKernels(pVersion, pReflector, uniqueEntryPointGroups, name)); gfx::IShaderProgram::Desc programDesc = {}; programDesc.linkingStyle = gfx::IShaderProgram::LinkingStyle::SeparateEntryPointCompilation; @@ -152,11 +152,6 @@ ProgramKernels::SharedPtr ProgramKernels::create( return pProgram; } -ProgramVersion::SharedConstPtr ProgramKernels::getProgramVersion() const -{ - return mpVersion->shared_from_this(); -} - const Shader* ProgramKernels::getShader(ShaderType type) const { for (auto& pEntryPointGroup : mUniqueEntryPointGroups) @@ -168,16 +163,16 @@ const Shader* ProgramKernels::getShader(ShaderType type) const } ProgramVersion::ProgramVersion(Program* pProgram, slang::IComponentType* pSlangGlobalScope) - : mpProgram(pProgram->shared_from_this()), mpSlangGlobalScope(pSlangGlobalScope) + : mpProgram(pProgram), mpSlangGlobalScope(pSlangGlobalScope) { FALCOR_ASSERT(pProgram); } void ProgramVersion::init( const DefineList& defineList, - const ProgramReflection::SharedPtr& pReflector, + const ref& pReflector, const std::string& name, - std::vector> const& pSlangEntryPoints + const std::vector>& pSlangEntryPoints ) { FALCOR_ASSERT(pReflector); @@ -187,12 +182,12 @@ void ProgramVersion::init( mpSlangEntryPoints = pSlangEntryPoints; } -ProgramVersion::SharedPtr ProgramVersion::createEmpty(Program* pProgram, slang::IComponentType* pSlangGlobalScope) +ref ProgramVersion::createEmpty(Program* pProgram, slang::IComponentType* pSlangGlobalScope) { - return SharedPtr(new ProgramVersion(pProgram, pSlangGlobalScope)); + return ref(new ProgramVersion(pProgram, pSlangGlobalScope)); } -ProgramKernels::SharedConstPtr ProgramVersion::getKernels(Device* pDevice, ProgramVars const* pVars) const +ref ProgramVersion::getKernels(Device* pDevice, ProgramVars const* pVars) const { // We need are going to look up or create specialized kernels // based on how parameters are bound in `pVars`. @@ -224,14 +219,13 @@ ProgramKernels::SharedConstPtr ProgramVersion::getKernels(Device* pDevice, Progr return foundKernels->second; } - auto pProgram = mpProgram.lock(); - FALCOR_ASSERT(pProgram); + FALCOR_ASSERT(mpProgram); // Loop so that user can trigger recompilation on error for (;;) { std::string log; - auto pKernels = pDevice->getProgramManager()->createProgramKernels(*pProgram, *this, *pVars, log); + auto pKernels = pDevice->getProgramManager()->createProgramKernels(*mpProgram, *this, *pVars, log); if (pKernels) { // Success diff --git a/Source/Falcor/Core/Program/ProgramVersion.h b/Source/Falcor/Core/Program/ProgramVersion.h index e02e4940e..30aba922a 100644 --- a/Source/Falcor/Core/Program/ProgramVersion.h +++ b/Source/Falcor/Core/Program/ProgramVersion.h @@ -28,6 +28,7 @@ #pragma once #include "ProgramReflection.h" #include "Core/Macros.h" +#include "Core/Object.h" #include "Core/API/fwd.h" #include "Core/API/Shader.h" #include "Core/API/Handles.h" @@ -47,11 +48,9 @@ class FALCOR_API ProgramVersion; /** * A collection of one or more entry points in a program kernels object. */ -class FALCOR_API EntryPointGroupKernels +class FALCOR_API EntryPointGroupKernels : public Object { public: - using SharedPtr = std::shared_ptr; - /** * Types of entry point groups. */ @@ -63,9 +62,9 @@ class FALCOR_API EntryPointGroupKernels RtHitGroup, ///< A ray tracing "hit group" }; - using Shaders = std::vector; + using Shaders = std::vector>; - static SharedPtr create(Type type, const Shaders& shaders, const std::string& exportName); + static ref create(Type type, const Shaders& shaders, const std::string& exportName); virtual ~EntryPointGroupKernels() = default; @@ -89,13 +88,10 @@ class FALCOR_API EntryPointGroupKernels * Low-level program object * This class abstracts the API's program creation and management */ -class FALCOR_API ProgramKernels : public std::enable_shared_from_this +class FALCOR_API ProgramKernels : public Object { public: - using SharedPtr = std::shared_ptr; - using SharedConstPtr = std::shared_ptr; - - typedef std::vector UniqueEntryPointGroups; + typedef std::vector> UniqueEntryPointGroups; /** * Create a new program object for graphics. @@ -109,12 +105,12 @@ class FALCOR_API ProgramKernels : public std::enable_shared_from_this create( Device* pDevice, const ProgramVersion* pVersion, slang::IComponentType* pSpecializedSlangGlobalScope, const std::vector& pTypeConformanceSpecializedEntryPoints, - const ProgramReflection::SharedPtr& pReflector, + const ref& pReflector, const UniqueEntryPointGroups& uniqueEntryPointGroups, std::string& log, const std::string& name = "" @@ -135,20 +131,20 @@ class FALCOR_API ProgramKernels : public std::enable_shared_from_this& getReflector() const { return mpReflector; } - std::shared_ptr getProgramVersion() const; + ProgramVersion const* getProgramVersion() const { return mpVersion; } const UniqueEntryPointGroups& getUniqueEntryPointGroups() const { return mUniqueEntryPointGroups; } - const EntryPointGroupKernels::SharedPtr& getUniqueEntryPointGroup(uint32_t index) const { return mUniqueEntryPointGroups[index]; } + const ref& getUniqueEntryPointGroup(uint32_t index) const { return mUniqueEntryPointGroups[index]; } gfx::IShaderProgram* getGfxProgram() const { return mGfxProgram; } protected: ProgramKernels( const ProgramVersion* pVersion, - const ProgramReflection::SharedPtr& pReflector, + const ref& pReflector, const UniqueEntryPointGroups& uniqueEntryPointGroups, const std::string& name = "" ); @@ -159,27 +155,25 @@ class FALCOR_API ProgramKernels : public std::enable_shared_from_this mpReflector; ProgramVersion const* mpVersion = nullptr; }; -class ProgramVersion : public std::enable_shared_from_this +class ProgramVersion : public Object { public: - using SharedPtr = std::shared_ptr; - using SharedConstPtr = std::shared_ptr; using DefineList = Shader::DefineList; /** * Get the program that this version was created from */ - std::shared_ptr getProgram() const { return mpProgram.lock(); } + Program* getProgram() const { return mpProgram; } /** * Get the defines that were used to create this version */ - DefineList const& getDefines() const { return mDefines; } + const DefineList& getDefines() const { return mDefines; } /** * Get the program name @@ -190,7 +184,7 @@ class ProgramVersion : public std::enable_shared_from_this * Get the reflection object. * @return A program reflection object. */ - const ProgramReflection::SharedPtr& getReflector() const + const ref& getReflector() const { FALCOR_ASSERT(mpReflector); return mpReflector; @@ -200,7 +194,7 @@ class ProgramVersion : public std::enable_shared_from_this * Get executable kernels based on state in a `ProgramVars` */ // TODO @skallweit passing pDevice here is a bit of a WAR - ProgramKernels::SharedConstPtr getKernels(Device* pDevice, ProgramVars const* pVars) const; + ref getKernels(Device* pDevice, ProgramVars const* pVars) const; slang::ISession* getSlangSession() const; slang::IComponentType* getSlangGlobalScope() const; @@ -211,25 +205,25 @@ class ProgramVersion : public std::enable_shared_from_this friend class RtProgram; friend class ProgramManager; - static SharedPtr createEmpty(Program* pProgram, slang::IComponentType* pSlangGlobalScope); + static ref createEmpty(Program* pProgram, slang::IComponentType* pSlangGlobalScope); ProgramVersion(Program* pProgram, slang::IComponentType* pSlangGlobalScope); void init( const DefineList& defineList, - const ProgramReflection::SharedPtr& pReflector, + const ref& pReflector, const std::string& name, - std::vector> const& pSlangEntryPoints + const std::vector>& pSlangEntryPoints ); - std::weak_ptr mpProgram; + mutable Program* mpProgram; DefineList mDefines; - ProgramReflection::SharedPtr mpReflector; + ref mpReflector; std::string mName; - ComPtr mpSlangGlobalScope; - std::vector> mpSlangEntryPoints; + Slang::ComPtr mpSlangGlobalScope; + std::vector> mpSlangEntryPoints; // Cached version of compiled kernels for this program version - mutable std::unordered_map mpKernels; + mutable std::unordered_map> mpKernels; }; } // namespace Falcor diff --git a/Source/Falcor/Core/Program/RtBindingTable.cpp b/Source/Falcor/Core/Program/RtBindingTable.cpp index 8a92025c4..e10c765b7 100644 --- a/Source/Falcor/Core/Program/RtBindingTable.cpp +++ b/Source/Falcor/Core/Program/RtBindingTable.cpp @@ -38,9 +38,9 @@ const uint32_t kMaxMissCount = (1 << 16); const uint32_t kMaxRayTypeCount = (1 << 4); } // namespace -RtBindingTable::SharedPtr RtBindingTable::create(uint32_t missCount, uint32_t rayTypeCount, uint32_t geometryCount) +ref RtBindingTable::create(uint32_t missCount, uint32_t rayTypeCount, uint32_t geometryCount) { - return SharedPtr(new RtBindingTable(missCount, rayTypeCount, geometryCount)); + return ref(new RtBindingTable(missCount, rayTypeCount, geometryCount)); } RtBindingTable::RtBindingTable(uint32_t missCount, uint32_t rayTypeCount, uint32_t geometryCount) diff --git a/Source/Falcor/Core/Program/RtBindingTable.h b/Source/Falcor/Core/Program/RtBindingTable.h index c50b2df84..7e3507832 100644 --- a/Source/Falcor/Core/Program/RtBindingTable.h +++ b/Source/Falcor/Core/Program/RtBindingTable.h @@ -28,6 +28,7 @@ #pragma once #include "RtProgram.h" #include "Core/Macros.h" +#include "Core/Object.h" #include "Scene/SceneIDs.h" #include #include @@ -44,10 +45,9 @@ namespace Falcor * The user is responsible for creating a binding table for use with a particular * RtProgram and Scene before creating an RtProgramVars object. */ -class FALCOR_API RtBindingTable +class FALCOR_API RtBindingTable : public Object { public: - using SharedPtr = std::shared_ptr; using ShaderID = RtProgram::ShaderID; /** @@ -57,7 +57,7 @@ class FALCOR_API RtBindingTable * @param[in] geometryCount Number of geometries. * @return A new object, or throws an exception on error. */ - static SharedPtr create(uint32_t missCount, uint32_t rayTypeCount, uint32_t geometryCount); + static ref create(uint32_t missCount, uint32_t rayTypeCount, uint32_t geometryCount); /** * Set the raygen shader ID. diff --git a/Source/Falcor/Core/Program/RtProgram.cpp b/Source/Falcor/Core/Program/RtProgram.cpp index 919d94ae4..bbaf05234 100644 --- a/Source/Falcor/Core/Program/RtProgram.cpp +++ b/Source/Falcor/Core/Program/RtProgram.cpp @@ -100,15 +100,13 @@ RtProgram::ShaderID RtProgram::Desc::addHitGroup( return {mBaseDesc.mActiveGroup}; } -RtProgram::SharedPtr RtProgram::create(std::shared_ptr pDevice, Desc desc, const DefineList& programDefines) +ref RtProgram::create(ref pDevice, Desc desc, const DefineList& programDefines) { - auto pProgram = SharedPtr(new RtProgram(pDevice, desc, programDefines)); - pDevice->getProgramManager()->registerProgramForReload(pProgram); - return pProgram; + return ref(new RtProgram(pDevice, desc, programDefines)); } -RtProgram::RtProgram(std::shared_ptr pDevice, const RtProgram::Desc& desc, const DefineList& programDefines) - : Program(std::move(pDevice), desc.mBaseDesc, programDefines), mRtDesc(desc) +RtProgram::RtProgram(ref pDevice, const RtProgram::Desc& desc, const DefineList& programDefines) + : Program(pDevice, desc.mBaseDesc, programDefines), mRtDesc(desc) { if (desc.mRayGenCount == 0) { @@ -124,14 +122,14 @@ RtProgram::RtProgram(std::shared_ptr pDevice, const RtProgram::Desc& des } } -RtStateObject::SharedPtr RtProgram::getRtso(RtProgramVars* pVars) +ref RtProgram::getRtso(RtProgramVars* pVars) { auto pProgramVersion = getActiveVersion(); - auto pProgramKernels = pProgramVersion->getKernels(mpDevice.get(), pVars); + auto pProgramKernels = pProgramVersion->getKernels(mpDevice, pVars); mRtsoGraph.walk((void*)pProgramKernels.get()); - RtStateObject::SharedPtr pRtso = mRtsoGraph.getCurrentNode(); + ref pRtso = mRtsoGraph.getCurrentNode(); if (pRtso == nullptr) { @@ -140,7 +138,7 @@ RtStateObject::SharedPtr RtProgram::getRtso(RtProgramVars* pVars) desc.setMaxTraceRecursionDepth(mRtDesc.mMaxTraceRecursionDepth); desc.setPipelineFlags(mRtDesc.mPipelineFlags); - StateGraph::CompareFunc cmpFunc = [&desc](RtStateObject::SharedPtr pRtso) -> bool { return pRtso && (desc == pRtso->getDesc()); }; + StateGraph::CompareFunc cmpFunc = [&desc](ref pRtso) -> bool { return pRtso && (desc == pRtso->getDesc()); }; if (mRtsoGraph.scanForMatchingNode(cmpFunc)) { @@ -148,7 +146,7 @@ RtStateObject::SharedPtr RtProgram::getRtso(RtProgramVars* pVars) } else { - pRtso = RtStateObject::create(mpDevice.get(), desc); + pRtso = RtStateObject::create(mpDevice, desc); mRtsoGraph.setCurrentNodeData(pRtso); } } diff --git a/Source/Falcor/Core/Program/RtProgram.h b/Source/Falcor/Core/Program/RtProgram.h index 1bffebcf5..3ae921e90 100644 --- a/Source/Falcor/Core/Program/RtProgram.h +++ b/Source/Falcor/Core/Program/RtProgram.h @@ -59,8 +59,6 @@ class RtProgramVars; class FALCOR_API RtProgram : public Program { public: - using SharedPtr = std::shared_ptr; - struct ShaderID { int32_t groupIndex = -1; ///< Entry point group index. @@ -356,24 +354,24 @@ class FALCOR_API RtProgram : public Program * @param[in] programDefines Optional list of macro definitions to set into the program. * @return A new object, or an exception is thrown if creation failed. */ - static RtProgram::SharedPtr create(std::shared_ptr pDevice, Desc desc, const DefineList& programDefines = DefineList()); + static ref create(ref pDevice, Desc desc, const DefineList& programDefines = DefineList()); /** * Get the raytracing state object for this program. */ - RtStateObject::SharedPtr getRtso(RtProgramVars* pVars); + ref getRtso(RtProgramVars* pVars); - Desc const& getRtDesc() const { return mRtDesc; } + const Desc& getRtDesc() const { return mRtDesc; } private: - RtProgram(RtProgram const&) = delete; - RtProgram& operator=(RtProgram const&) = delete; + RtProgram(const RtProgram&) = delete; + RtProgram& operator=(const RtProgram&) = delete; - RtProgram(std::shared_ptr pDevice, const Desc& desc, const DefineList& programDefines); + RtProgram(ref pDevice, const Desc& desc, const DefineList& programDefines); Desc mRtDesc; - using StateGraph = Falcor::StateGraph; + using StateGraph = Falcor::StateGraph, void*>; StateGraph mRtsoGraph; }; } // namespace Falcor diff --git a/Source/Falcor/Core/Program/ShaderVar.cpp b/Source/Falcor/Core/Program/ShaderVar.cpp index f055ff820..b04b8a5ba 100644 --- a/Source/Falcor/Core/Program/ShaderVar.cpp +++ b/Source/Falcor/Core/Program/ShaderVar.cpp @@ -33,7 +33,7 @@ namespace Falcor ShaderVar::ShaderVar() : mpBlock(nullptr) {} ShaderVar::ShaderVar(const ShaderVar& other) : mpBlock(other.mpBlock), mOffset(other.mOffset) {} ShaderVar::ShaderVar(ParameterBlock* pObject, const TypedShaderVarOffset& offset) : mpBlock(pObject), mOffset(offset) {} -ShaderVar::ShaderVar(ParameterBlock* pObject) : mpBlock(pObject), mOffset(pObject->getElementType().get(), ShaderVarOffset::kZero) {} +ShaderVar::ShaderVar(ParameterBlock* pObject) : mpBlock(pObject), mOffset(pObject->getElementType(), ShaderVarOffset::kZero) {} ShaderVar ShaderVar::findMember(const std::string& name) const { @@ -63,7 +63,7 @@ ShaderVar ShaderVar::findMember(const std::string& name) const if (auto pMember = pStructType->findMember(name)) { // Need to apply the offsets from member - TypedShaderVarOffset newOffset = TypedShaderVarOffset(pMember->getType().get(), mOffset + pMember->getBindLocation()); + TypedShaderVarOffset newOffset = TypedShaderVarOffset(pMember->getType(), mOffset + pMember->getBindLocation()); return ShaderVar(mpBlock, newOffset); } } @@ -101,7 +101,7 @@ ShaderVar ShaderVar::findMember(uint32_t index) const auto pMember = pStructType->getMember(index); // Need to apply the offsets from member - TypedShaderVarOffset newOffset = TypedShaderVarOffset(pMember->getType().get(), mOffset + pMember->getBindLocation()); + TypedShaderVarOffset newOffset = TypedShaderVarOffset(pMember->getType(), mOffset + pMember->getBindLocation()); return ShaderVar(mpBlock, newOffset); } } @@ -176,7 +176,7 @@ ShaderVar ShaderVar::operator[](size_t index) const mOffset.getResource().getArrayIndex() * elementCount + ResourceShaderVarOffset::ArrayIndex(index) ); TypedShaderVarOffset newOffset = - TypedShaderVarOffset(pArrayType->getElementType().get(), ShaderVarOffset(elementUniformLocation, elementResourceLocation)); + TypedShaderVarOffset(pArrayType->getElementType(), ShaderVarOffset(elementUniformLocation, elementResourceLocation)); return ShaderVar(mpBlock, newOffset); } } @@ -186,7 +186,7 @@ ShaderVar ShaderVar::operator[](size_t index) const { auto pMember = pStructType->getMember(index); // Need to apply the offsets from member - TypedShaderVarOffset newOffset = TypedShaderVarOffset(pMember->getType().get(), mOffset + pMember->getBindLocation()); + TypedShaderVarOffset newOffset = TypedShaderVarOffset(pMember->getType(), mOffset + pMember->getBindLocation()); return ShaderVar(mpBlock, newOffset); } } @@ -195,7 +195,7 @@ ShaderVar ShaderVar::operator[](size_t index) const return ShaderVar(); } -ShaderVar ShaderVar::operator[](TypedShaderVarOffset const& offset) const +ShaderVar ShaderVar::operator[](const TypedShaderVarOffset& offset) const { if (!isValid()) return *this; @@ -217,10 +217,10 @@ ShaderVar ShaderVar::operator[](TypedShaderVarOffset const& offset) const } } - return ShaderVar(mpBlock, TypedShaderVarOffset(offset.getType().get(), mOffset + offset)); + return ShaderVar(mpBlock, TypedShaderVarOffset(offset.getType(), mOffset + offset)); } -ShaderVar ShaderVar::operator[](UniformShaderVarOffset const& loc) const +ShaderVar ShaderVar::operator[](const UniformShaderVarOffset& loc) const { if (!isValid()) return *this; @@ -256,9 +256,8 @@ ShaderVar ShaderVar::operator[](UniformShaderVarOffset const& loc) const auto elementIndex = byteOffset / elementStride; auto offsetIntoElement = byteOffset % elementStride; - TypedShaderVarOffset elementOffset = TypedShaderVarOffset( - pElementType.get(), ShaderVarOffset(mOffset.getUniform() + elementIndex * elementStride, mOffset.getResource()) - ); + TypedShaderVarOffset elementOffset = + TypedShaderVarOffset(pElementType, ShaderVarOffset(mOffset.getUniform() + elementIndex * elementStride, mOffset.getResource())); ShaderVar elementCursor(mpBlock, elementOffset); return elementCursor[UniformShaderVarOffset(offsetIntoElement)]; } @@ -282,7 +281,7 @@ ShaderVar ShaderVar::operator[](UniformShaderVarOffset const& loc) const continue; auto offsetIntoMember = byteOffset - memberByteOffset; - TypedShaderVarOffset memberOffset = TypedShaderVarOffset(pMember->getType().get(), mOffset + pMember->getBindLocation()); + TypedShaderVarOffset memberOffset = TypedShaderVarOffset(pMember->getType(), mOffset + pMember->getBindLocation()); ShaderVar memberCursor(mpBlock, memberOffset); return memberCursor[UniformShaderVarOffset(offsetIntoMember)]; } @@ -297,57 +296,57 @@ bool ShaderVar::isValid() const return mOffset.isValid(); } -bool ShaderVar::setTexture(const Texture::SharedPtr& pTexture) const +bool ShaderVar::setTexture(const ref& pTexture) const { return mpBlock->setTexture(mOffset, pTexture); } -bool ShaderVar::setSampler(const Sampler::SharedPtr& pSampler) const +bool ShaderVar::setSampler(const ref& pSampler) const { return mpBlock->setSampler(mOffset, pSampler); } -bool ShaderVar::setBuffer(const Buffer::SharedPtr& pBuffer) const +bool ShaderVar::setBuffer(const ref& pBuffer) const { return mpBlock->setBuffer(mOffset, pBuffer); } -bool ShaderVar::setSrv(const ShaderResourceView::SharedPtr& pSrv) const +bool ShaderVar::setSrv(const ref& pSrv) const { return mpBlock->setSrv(mOffset, pSrv); } -bool ShaderVar::setUav(const UnorderedAccessView::SharedPtr& pUav) const +bool ShaderVar::setUav(const ref& pUav) const { return mpBlock->setUav(mOffset, pUav); } -bool ShaderVar::setAccelerationStructure(const RtAccelerationStructure::SharedPtr& pAccl) const +bool ShaderVar::setAccelerationStructure(const ref& pAccl) const { return mpBlock->setAccelerationStructure(mOffset, pAccl); } -bool ShaderVar::setParameterBlock(const std::shared_ptr& pBlock) const +bool ShaderVar::setParameterBlock(const ref& pBlock) const { return mpBlock->setParameterBlock(mOffset, pBlock); } -bool ShaderVar::setImpl(const Texture::SharedPtr& pTexture) const +bool ShaderVar::setImpl(const ref& pTexture) const { return mpBlock->setTexture(mOffset, pTexture); } -bool ShaderVar::setImpl(const Sampler::SharedPtr& pSampler) const +bool ShaderVar::setImpl(const ref& pSampler) const { return mpBlock->setSampler(mOffset, pSampler); } -bool ShaderVar::setImpl(const Buffer::SharedPtr& pBuffer) const +bool ShaderVar::setImpl(const ref& pBuffer) const { return mpBlock->setBuffer(mOffset, pBuffer); } -bool ShaderVar::setImpl(const std::shared_ptr& pBlock) const +bool ShaderVar::setImpl(const ref& pBlock) const { return mpBlock->setParameterBlock(mOffset, pBlock); } @@ -372,17 +371,17 @@ bool ShaderVar::setBlob(void const* data, size_t size) const return mpBlock->setBlob(mOffset, data, size); } -ShaderVar::operator Buffer::SharedPtr() const +ShaderVar::operator ref() const { return mpBlock->getBuffer(mOffset); } -ShaderVar::operator Texture::SharedPtr() const +ShaderVar::operator ref() const { return mpBlock->getTexture(mOffset); } -ShaderVar::operator Sampler::SharedPtr() const +ShaderVar::operator ref() const { return mpBlock->getSampler(mOffset); } @@ -392,37 +391,37 @@ ShaderVar::operator UniformShaderVarOffset() const return mOffset.getUniform(); } -std::shared_ptr ShaderVar::getParameterBlock() const +ref ShaderVar::getParameterBlock() const { return mpBlock->getParameterBlock(mOffset); } -Buffer::SharedPtr ShaderVar::getBuffer() const +ref ShaderVar::getBuffer() const { return mpBlock->getBuffer(mOffset); } -Texture::SharedPtr ShaderVar::getTexture() const +ref ShaderVar::getTexture() const { return mpBlock->getTexture(mOffset); } -Sampler::SharedPtr ShaderVar::getSampler() const +ref ShaderVar::getSampler() const { return mpBlock->getSampler(mOffset); } -ShaderResourceView::SharedPtr ShaderVar::getSrv() const +ref ShaderVar::getSrv() const { return mpBlock->getSrv(mOffset); } -UnorderedAccessView::SharedPtr ShaderVar::getUav() const +ref ShaderVar::getUav() const { return mpBlock->getUav(mOffset); } -RtAccelerationStructure::SharedPtr ShaderVar::getAccelerationStructure() const +ref ShaderVar::getAccelerationStructure() const { return mpBlock->getAccelerationStructure(mOffset); } @@ -432,7 +431,7 @@ size_t ShaderVar::getByteOffset() const return mOffset.getUniform().getByteOffset(); } -ReflectionType::SharedConstPtr ShaderVar::getType() const +ref ShaderVar::getType() const { return mOffset.getType(); } diff --git a/Source/Falcor/Core/Program/ShaderVar.h b/Source/Falcor/Core/Program/ShaderVar.h index e0d55da28..c60e379b8 100644 --- a/Source/Falcor/Core/Program/ShaderVar.h +++ b/Source/Falcor/Core/Program/ShaderVar.h @@ -41,8 +41,6 @@ namespace Falcor { class ParameterBlock; -template -class ParameterBlockSharedPtr; /** * A "pointer" to a shader variable stored in some parameter block. @@ -114,7 +112,7 @@ struct FALCOR_API ShaderVar * For an invalid/null shader variable the result will be null. * */ - ReflectionType::SharedConstPtr getType() const; + ref getType() const; /** * Get the offset that this shader variable points to inside the parameter block. @@ -209,85 +207,85 @@ struct FALCOR_API ShaderVar * Set a buffer into this variable * Logs an error and returns `false` if this variable doesn't point at a buffer */ - bool setBuffer(const Buffer::SharedPtr& pBuffer) const; + bool setBuffer(const ref& pBuffer) const; /** * Set the texture that this variable points to. * Logs an error and returns `false` if this variable doesn't point at a texture. */ - bool setTexture(const Texture::SharedPtr& pTexture) const; + bool setTexture(const ref& pTexture) const; /** * Get the texture that this variable points to. * Logs an error and returns null if this variable doesn't point at a texture. */ - Texture::SharedPtr getTexture() const; + ref getTexture() const; /** * Set the sampler that this variable points to. * Logs an error and returns `false` if this variable doesn't point at a sampler. */ - bool setSampler(const Sampler::SharedPtr& pSampler) const; + bool setSampler(const ref& pSampler) const; /** * Get the sampler that this variable points to. * Logs an error and returns null if this variable doesn't point at a sampler. */ - Sampler::SharedPtr getSampler() const; + ref getSampler() const; /** * Get the buffer that this variable points to. * Logs an error and returns nullptr if this variable doesn't point at a buffer. */ - Buffer::SharedPtr getBuffer() const; + ref getBuffer() const; /** * Set the shader resource view that this variable points to. * Logs an error and returns `false` if this variable doesn't point at a shader resource view. */ - bool setSrv(const ShaderResourceView::SharedPtr& pSrv) const; + bool setSrv(const ref& pSrv) const; /** * Get the shader resource view that this variable points to. * Logs an error and returns null if this variable doesn't point at a shader resource view. */ - ShaderResourceView::SharedPtr getSrv() const; + ref getSrv() const; /** * Set the unordered access view that this variable points to. * Logs an error and returns `false` if this variable doesn't point at an unordered access view. */ - bool setUav(const UnorderedAccessView::SharedPtr& pUav) const; + bool setUav(const ref& pUav) const; /** * Get the unordered access view that this variable points to. * Logs an error and returns null if this variable doesn't point at an unordered access view. */ - UnorderedAccessView::SharedPtr getUav() const; + ref getUav() const; /** * Set the acceleration structure that this variable points to. * Logs an error and returns `false` if this variable doesn't point at an acceleration structure. */ - bool setAccelerationStructure(const RtAccelerationStructure::SharedPtr& pAccl) const; + bool setAccelerationStructure(const ref& pAccl) const; /** * Get the acceleration structure that this variable points to. * Logs an error and returns null if this variable doesn't point at an acceleration structure. */ - RtAccelerationStructure::SharedPtr getAccelerationStructure() const; + ref getAccelerationStructure() const; /** * Set the parameter block that this variable points to. * Logs an error and returns `false` if this variable doesn't point at a parameter block. */ - bool setParameterBlock(const std::shared_ptr& pBlock) const; + bool setParameterBlock(const ref& pBlock) const; /** * Get the parameter block that this variable points to. * Logs an error and returns null if this variable doesn't point at a parameter block. */ - std::shared_ptr getParameterBlock() const; + ref getParameterBlock() const; /** * Set the value of the data pointed to by this shader variable. @@ -364,7 +362,7 @@ struct FALCOR_API ShaderVar * variable points to. The resulting shader variable will have the type encoded in `offset`, and will have an offset that is the sum of * this variables offset and the provided `offset`. */ - ShaderVar operator[](TypedShaderVarOffset const& offset) const; + ShaderVar operator[](const TypedShaderVarOffset& offset) const; /** * Create a shader variable that points to some pre-computed `offset` from this one. @@ -376,28 +374,28 @@ struct FALCOR_API ShaderVar * given `offset` and use its type information in the resulting shader variable. If no appropriate field/element can be found, an error * will be logged. */ - ShaderVar operator[](UniformShaderVarOffset const& offset) const; + ShaderVar operator[](const UniformShaderVarOffset& offset) const; /** * Implicit conversion from a shader variable to a texture. * This operation allows a bound texture to be queried using the `[]` syntax: * pTexture = pVars["someTexture"]; */ - operator Texture::SharedPtr() const; + operator ref() const; /** * Implicit conversion from a shader variable to a sampler. * This operation allows a bound sampler to be queried using the `[]` syntax: * pSampler = pVars["someSampler"]; */ - operator Sampler::SharedPtr() const; + operator ref() const; /** * Implicit conversion from a shader variable to a buffer. * This operation allows a bound buffer to be queried using the `[]` syntax: * pBuffer = pVars["someBuffer"]; */ - operator Buffer::SharedPtr() const; + operator ref() const; /** * Get access to the underlying bytes of the variable. @@ -431,16 +429,10 @@ struct FALCOR_API ShaderVar */ TypedShaderVarOffset mOffset; - bool setImpl(const Texture::SharedPtr& pTexture) const; - bool setImpl(const Sampler::SharedPtr& pSampler) const; - bool setImpl(const Buffer::SharedPtr& pBuffer) const; - bool setImpl(const std::shared_ptr& pBlock) const; - - template - bool setImpl(const ParameterBlockSharedPtr& pBlock) const - { - return setImpl(std::static_pointer_cast(pBlock)); - } + bool setImpl(const ref& pTexture) const; + bool setImpl(const ref& pSampler) const; + bool setImpl(const ref& pBuffer) const; + bool setImpl(const ref& pBlock) const; template bool setImpl(const T& val) const; diff --git a/Source/Falcor/Core/SampleApp.cpp b/Source/Falcor/Core/SampleApp.cpp index bd863f44f..e1345230c 100644 --- a/Source/Falcor/Core/SampleApp.cpp +++ b/Source/Falcor/Core/SampleApp.cpp @@ -36,13 +36,13 @@ #include "Utils/Logger.h" #include "Utils/Scripting/Console.h" #include "Utils/Scripting/Scripting.h" +#include "Utils/Scripting/ScriptBindings.h" #include "Utils/UI/TextRenderer.h" #include "Utils/Settings.h" #include "Utils/StringUtils.h" #include -#include #include namespace Falcor @@ -63,7 +63,7 @@ SampleApp::SampleApp(const SampleAppConfig& config) mClock.pause(); // Create GPU device - mpDevice = Device::create(config.deviceDesc); + mpDevice = make_ref(config.deviceDesc); if (!config.headless) { @@ -83,7 +83,7 @@ SampleApp::SampleApp(const SampleAppConfig& config) desc.height = mpWindow->getClientAreaSize().y; desc.imageCount = 3; desc.enableVSync = mVsyncOn; - mpSwapchain = std::make_unique(mpDevice, desc, mpWindow->getApiHandle()); + mpSwapchain = make_ref(mpDevice, desc, mpWindow->getApiHandle()); // Show the progress bar (unless window is minimized) if (windowDesc.mode != Window::WindowMode::Minimized) @@ -94,7 +94,7 @@ SampleApp::SampleApp(const SampleAppConfig& config) // Create target frame buffer uint2 fboSize = mpWindow ? mpWindow->getClientAreaSize() : uint2(config.windowDesc.width, config.windowDesc.height); - mpTargetFBO = Fbo::create2D(mpDevice.get(), fboSize.x, fboSize.y, config.colorFormat, config.depthFormat); + mpTargetFBO = Fbo::create2D(mpDevice, fboSize.x, fboSize.y, config.colorFormat, config.depthFormat); // Load settings.toml files getSettings().addOptions(getRuntimeDirectory() / "settings.json"); @@ -131,36 +131,31 @@ SampleApp::SampleApp(const SampleAppConfig& config) SampleApp::~SampleApp() { + mpPausedRenderOutput.reset(); mpProfilerUI.reset(); - if (mVideoCapture.pVideoCapture) - endVideoCapture(); - mpDevice->flushAndSync(); // contains Python dictionaries, needs to be terminated before Scripting::shutdown() mpSettings.reset(); - Clock::shutdown(); Threading::shutdown(); Scripting::shutdown(); PluginManager::instance().releaseAllPlugins(); - TextRenderer::shutdown(); mpGui.reset(); + mpTextRenderer.reset(); mpTargetFBO.reset(); mpPixelZoom.reset(); mpSwapchain.reset(); mpWindow.reset(); +#if FALCOR_ENABLE_REF_TRACKING + Object::dumpAllRefs(); +#endif mpDevice.reset(); - if (getGlobalDevice()) - { - getGlobalDevice()->cleanup(); - getGlobalDevice().reset(); #ifdef _DEBUG - Device::reportLiveObjects(); + Device::reportLiveObjects(); #endif - } OSServices::stop(); Logger::shutdown(); @@ -215,11 +210,7 @@ void SampleApp::handleKeyboardEvent(const KeyboardEvent& keyEvent) // Consume system messages first if (keyEvent.type == KeyboardEvent::Type::KeyPressed) { - if (keyEvent.hasModifier(Input::Modifier::Shift) && keyEvent.key == Input::Key::F12) - { - initVideoCapture(); - } - else if (keyEvent.hasModifier(Input::Modifier::Ctrl)) + if (keyEvent.hasModifier(Input::Modifier::Ctrl)) { switch (keyEvent.key) { @@ -261,14 +252,7 @@ void SampleApp::handleKeyboardEvent(const KeyboardEvent& keyEvent) } break; case Input::Key::Escape: - if (mVideoCapture.pVideoCapture) - { - endVideoCapture(); - } - else - { - mpWindow->shutdown(); - } + mpWindow->shutdown(); break; case Input::Key::Pause: case Input::Key::Space: @@ -309,8 +293,6 @@ void SampleApp::handleDroppedFile(const std::filesystem::path& path) void SampleApp::runInternal() { - Clock::start(mpDevice.get()); - // Load and run onLoad(getRenderContext()); @@ -353,7 +335,7 @@ bool screenSizeUI(Gui::Widgets& widget, uint2& screenDims) { for (uint32_t i = 0; i < count; i++) { - if (screenDims == resolutions[i]) + if (all(screenDims == resolutions[i])) return i; } return 0u; @@ -379,7 +361,6 @@ std::string SampleApp::getKeyboardShortcutsStr() "F3 - Capture current camera location\n" "F5 - Reload shaders\n" "F12 - Capture screenshot\n" - "Shift+F12 - Capture video\n" "V - Toggle VSync\n" "Pause|Space - Pause/resume the global timer\n" "Ctrl+Pause|Space - Pause/resume the renderer\n" @@ -441,8 +422,6 @@ void SampleApp::renderGlobalUI(Gui* pGui) controlsGroup.separator(); mCaptureScreen = controlsGroup.button("Screen Capture"); - if (controlsGroup.button("Video Capture", true)) - initVideoCapture(); if (controlsGroup.button("Save Config")) saveConfigToFile(); @@ -463,11 +442,6 @@ void SampleApp::renderUI() if (mShowUI) onGuiRender(mpGui.get()); - if (mVideoCapture.displayUI && mVideoCapture.pUI) - { - Gui::Window w(mpGui.get(), "Video Capture", mVideoCapture.displayUI, {350, 250}, {300, 280}); - mVideoCapture.pUI->render(w); - } if (pProfiler->isEnabled()) { @@ -508,8 +482,6 @@ void SampleApp::renderFrame() // Handle clock. mClock.tick(); mFrameRate.newFrame(); - if (mVideoCapture.fixedTimeDelta) - mClock.setTime(mVideoCapture.currentTime); RenderContext* pRenderContext = mpDevice->getRenderContext(); @@ -528,7 +500,7 @@ void SampleApp::renderFrame() { auto srcTexture = mpTargetFBO->getColorTexture(0); mpPausedRenderOutput = - Texture::create2D(mpDevice.get(), srcTexture->getWidth(), srcTexture->getHeight(), srcTexture->getFormat(), 1, 1); + Texture::create2D(mpDevice, srcTexture->getWidth(), srcTexture->getHeight(), srcTexture->getFormat(), 1, 1); pRenderContext->copyResource(mpPausedRenderOutput.get(), srcTexture.get()); } else @@ -537,12 +509,6 @@ void SampleApp::renderFrame() } } - // Capture video frame before UI is rendered. - // Check capture mode here once only, as its value may change after renderUI(). - bool captureVideoUI = mVideoCapture.pUI && mVideoCapture.pUI->captureUI(); - if (!captureVideoUI) - captureVideoFrame(mpTargetFBO->getColorTexture(0).get()); - // Render the UI. renderUI(); @@ -553,9 +519,6 @@ void SampleApp::renderFrame() mpDevice->getProfiler()->endFrame(pRenderContext); #endif - // Capture video frame after UI is rendered. - if (captureVideoUI) - captureVideoFrame(mpTargetFBO->getColorTexture(0).get()); if (mCaptureScreen) captureScreen(mpTargetFBO->getColorTexture(0).get()); @@ -564,7 +527,7 @@ void SampleApp::renderFrame() { int imageIndex = mpSwapchain->acquireNextImage(); FALCOR_ASSERT(imageIndex >= 0 && imageIndex < (int)mpSwapchain->getDesc().imageCount); - Texture* pSwapchainImage = mpSwapchain->getImage(imageIndex).get(); + const Texture* pSwapchainImage = mpSwapchain->getImage(imageIndex).get(); pRenderContext->copyResource(pSwapchainImage, mpTargetFBO->getColorTexture(0).get()); pRenderContext->resourceBarrier(pSwapchainImage, Resource::State::Present); pRenderContext->flush(); @@ -583,7 +546,7 @@ void SampleApp::resizeTargetFBO(uint32_t width, uint32_t height) { // Resize target frame buffer. auto pPrevFBO = mpTargetFBO; - mpTargetFBO = Fbo::create2D(mpDevice.get(), width, height, pPrevFBO->getDesc()); + mpTargetFBO = Fbo::create2D(mpDevice, width, height, pPrevFBO->getDesc()); mpDevice->getRenderContext()->blit(pPrevFBO->getColorTexture(0)->getSRV(), mpTargetFBO->getRenderTargetView(0)); // Tell the GUI the swap-chain size changed @@ -602,7 +565,7 @@ void SampleApp::initUI() { float scaling = getDisplayScaleFactor(); mpGui = std::make_unique(mpDevice, mpTargetFBO->getWidth(), mpTargetFBO->getHeight(), scaling); - TextRenderer::start(mpDevice.get()); + mpTextRenderer = std::make_unique(mpDevice); } void SampleApp::resizeFrameBuffer(uint32_t width, uint32_t height) @@ -630,81 +593,6 @@ void SampleApp::captureScreen(Texture* pTexture) pTexture->captureToFile(0, 0, path); } -void SampleApp::initVideoCapture() -{ - if (mVideoCapture.pUI == nullptr) - { - mVideoCapture.pUI = VideoEncoderUI::create([this]() { return startVideoCapture(); }, [this]() { endVideoCapture(); }); - } - mVideoCapture.displayUI = true; -} - -bool SampleApp::startVideoCapture() -{ - // Create the Capture Object and Framebuffer. - VideoEncoder::Desc desc; - desc.flipY = false; - desc.codec = mVideoCapture.pUI->getCodec(); - desc.path = mVideoCapture.pUI->getPath(); - desc.format = mpTargetFBO->getColorTexture(0)->getFormat(); - desc.fps = mVideoCapture.pUI->getFPS(); - desc.height = mpTargetFBO->getHeight(); - desc.width = mpTargetFBO->getWidth(); - desc.bitrateMbps = mVideoCapture.pUI->getBitrate(); - desc.gopSize = mVideoCapture.pUI->getGopSize(); - - mVideoCapture.pVideoCapture = VideoEncoder::create(desc); - if (!mVideoCapture.pVideoCapture) - return false; - - FALCOR_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(); - } - return true; -} - -void SampleApp::endVideoCapture() -{ - if (mVideoCapture.pVideoCapture) - { - mVideoCapture.pVideoCapture->endCapture(); - mShowUI = true; - } - mVideoCapture = {}; -} - -void SampleApp::captureVideoFrame(Texture* pTexture) -{ - if (mVideoCapture.pVideoCapture) - { - mVideoCapture.pVideoCapture->appendFrame(getRenderContext()->readTextureSubresource(pTexture, 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; - } -} - void SampleApp::shutdown(int returnCode) { mShouldTerminate = true; @@ -737,23 +625,15 @@ void SampleApp::saveConfigToFile() void SampleApp::startScripting() { - Scripting::start(); auto bindFunc = [this](pybind11::module& m) { this->registerScriptBindings(m); }; ScriptBindings::registerBinding(bindFunc); + Scripting::start(); } void SampleApp::registerScriptBindings(pybind11::module& m) { using namespace pybind11::literals; - ScriptBindings::SerializableStruct sampleConfig(m, "SampleAppConfig"); -#define field(f_) field(#f_, &SampleAppConfig::f_) - sampleConfig.field(windowDesc); - sampleConfig.field(deviceDesc); - sampleConfig.field(timeScale); - sampleConfig.field(pauseTime); - sampleConfig.field(showUI); -#undef field auto exit = [this](int32_t errorCode) { this->shutdown(errorCode); }; m.def("exit", exit, "errorCode"_a = 0); diff --git a/Source/Falcor/Core/SampleApp.h b/Source/Falcor/Core/SampleApp.h index 4140a84e2..c8f6f0c70 100644 --- a/Source/Falcor/Core/SampleApp.h +++ b/Source/Falcor/Core/SampleApp.h @@ -37,17 +37,21 @@ #include "Utils/UI/Gui.h" #include "Utils/UI/PixelZoom.h" #include "Utils/UI/InputState.h" -#include "Utils/Video/VideoEncoderUI.h" -#include "Utils/Scripting/ScriptBindings.h" #include "Utils/Scripting/Console.h" #include #include #include #include +namespace pybind11 +{ +class module_; +using module = module_; +} // namespace pybind11 namespace Falcor { class Settings; +class TextRenderer; /** * Sample application configuration. @@ -103,7 +107,7 @@ class FALCOR_API SampleApp : public Window::ICallbacks /** * Called on each frame render. */ - virtual void onFrameRender(RenderContext* pRenderContext, const std::shared_ptr& pTargetFbo) {} + virtual void onFrameRender(RenderContext* pRenderContext, const ref& pTargetFbo) {} /** * Called after onFrameRender(). @@ -176,7 +180,7 @@ class FALCOR_API SampleApp : public Window::ICallbacks /** * Get the GPU device for this application. */ - std::shared_ptr getDevice() const { return mpDevice; } + const ref& getDevice() const { return mpDevice; } /** * Get the render-context for the current frame. This might change each frame. @@ -186,7 +190,7 @@ class FALCOR_API SampleApp : public Window::ICallbacks /** * Get the current FBO. */ - Fbo::SharedPtr getTargetFbo() const { return mpTargetFBO; } + const ref& getTargetFbo() const { return mpTargetFBO; } /** * Get the window. @@ -198,6 +202,11 @@ class FALCOR_API SampleApp : public Window::ICallbacks */ ProgressBar& getProgressBar() { return mProgressBar; } + /** + * Get the text renderer. + */ + TextRenderer& getTextRenderer() { return *mpTextRenderer; } + /** * Get the console. */ @@ -299,10 +308,6 @@ class FALCOR_API SampleApp : public Window::ICallbacks void captureScreen(Texture* pTexture); - void initVideoCapture(); - bool startVideoCapture(); - void endVideoCapture(); - void captureVideoFrame(Texture* pTexture); void renderUI(); void runInternal(); @@ -310,14 +315,15 @@ class FALCOR_API SampleApp : public Window::ICallbacks void startScripting(); void registerScriptBindings(pybind11::module& m); - std::shared_ptr mpDevice; ///< GPU device. - Window::SharedPtr mpWindow; ///< Main window (nullptr if headless). - std::unique_ptr mpSwapchain; ///< Main swapchain (nullptr if headless). - Fbo::SharedPtr mpTargetFBO; ///< FBO available to renderers. - Texture::SharedPtr mpPausedRenderOutput; ///< Contains the renderer output during pausing. + ref mpDevice; ///< GPU device. + ref mpWindow; ///< Main window (nullptr if headless). + ref mpSwapchain; ///< Main swapchain (nullptr if headless). + ref mpTargetFBO; ///< FBO available to renderers. + ref mpPausedRenderOutput; ///< Contains the renderer output during pausing. ProgressBar mProgressBar; std::unique_ptr mpGui; + std::unique_ptr mpTextRenderer; InputState mInputState; std::unique_ptr mpProfilerUI; std::unique_ptr mpPixelZoom; @@ -336,16 +342,6 @@ class FALCOR_API SampleApp : public Window::ICallbacks int mReturnCode = 0; - struct VideoCaptureData - { - VideoEncoderUI::UniquePtr pUI; - VideoEncoder::UniquePtr pVideoCapture; - std::vector pFrame; - double fixedTimeDelta = 0; - double currentTime = 0; - bool displayUI = false; - } mVideoCapture; - SampleApp(const SampleApp&) = delete; SampleApp& operator=(const SampleApp&) = delete; }; diff --git a/Source/Falcor/Core/State/ComputeState.cpp b/Source/Falcor/Core/State/ComputeState.cpp index d40d7f302..4f7bfba30 100644 --- a/Source/Falcor/Core/State/ComputeState.cpp +++ b/Source/Falcor/Core/State/ComputeState.cpp @@ -26,23 +26,24 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **************************************************************************/ #include "ComputeState.h" +#include "Core/ObjectPython.h" #include "Core/Program/ProgramVars.h" #include "Utils/Scripting/ScriptBindings.h" namespace Falcor { -ComputeState::SharedPtr ComputeState::create(std::shared_ptr pDevice) +ref ComputeState::create(ref pDevice) { - return SharedPtr(new ComputeState(std::move(pDevice))); + return ref(new ComputeState(pDevice)); } -ComputeState::ComputeState(std::shared_ptr pDevice) : mpDevice(std::move(pDevice)) +ComputeState::ComputeState(ref pDevice) : mpDevice(pDevice) { - mpCsoGraph = StateGraph::create(); + mpCsoGraph = std::make_unique(); } -ComputeStateObject::SharedPtr ComputeState::getCSO(const ComputeVars* pVars) +ref ComputeState::getCSO(const ComputeVars* pVars) { auto pProgramKernels = mpProgram ? mpProgram->getActiveVersion()->getKernels(mpDevice.get(), pVars) : nullptr; bool newProgram = (pProgramKernels.get() != mCachedData.pProgramKernels); @@ -52,13 +53,13 @@ ComputeStateObject::SharedPtr ComputeState::getCSO(const ComputeVars* pVars) mpCsoGraph->walk((void*)mCachedData.pProgramKernels); } - ComputeStateObject::SharedPtr pCso = mpCsoGraph->getCurrentNode(); + ref pCso = mpCsoGraph->getCurrentNode(); if (pCso == nullptr) { mDesc.setProgramKernels(pProgramKernels); - StateGraph::CompareFunc cmpFunc = [&desc = mDesc](ComputeStateObject::SharedPtr pCso) -> bool + ComputeStateGraph::CompareFunc cmpFunc = [&desc = mDesc](ref pCso) -> bool { return pCso && (desc == pCso->getDesc()); }; if (mpCsoGraph->scanForMatchingNode(cmpFunc)) @@ -67,7 +68,7 @@ ComputeStateObject::SharedPtr ComputeState::getCSO(const ComputeVars* pVars) } else { - pCso = ComputeStateObject::create(mpDevice.get(), mDesc); + pCso = ComputeStateObject::create(mpDevice, mDesc); mpCsoGraph->setCurrentNodeData(pCso); } } @@ -77,6 +78,6 @@ ComputeStateObject::SharedPtr ComputeState::getCSO(const ComputeVars* pVars) FALCOR_SCRIPT_BINDING(ComputeState) { - pybind11::class_(m, "ComputeState"); + pybind11::class_>(m, "ComputeState"); } } // namespace Falcor diff --git a/Source/Falcor/Core/State/ComputeState.h b/Source/Falcor/Core/State/ComputeState.h index 680f50e12..03297bdfe 100644 --- a/Source/Falcor/Core/State/ComputeState.h +++ b/Source/Falcor/Core/State/ComputeState.h @@ -28,6 +28,7 @@ #pragma once #include "StateGraph.h" #include "Core/Macros.h" +#include "Core/Object.h" #include "Core/API/ComputeStateObject.h" #include "Core/Program/ComputeProgram.h" #include @@ -41,11 +42,9 @@ class ComputeVars; * 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 FALCOR_API ComputeState +class FALCOR_API ComputeState : public Object { public: - using SharedPtr = std::shared_ptr; - ~ComputeState() = default; /** @@ -53,17 +52,12 @@ class FALCOR_API ComputeState * @param pDevice GPU device. * @return A new object, or an exception is thrown if creation failed. */ - static SharedPtr create(std::shared_ptr pDevice); - - /** - * Copy constructor. Useful if you need to make minor changes to an already existing object - */ - SharedPtr operator=(const SharedPtr& other); + static ref create(ref pDevice); /** * Bind a program to the pipeline */ - ComputeState& setProgram(const ComputeProgram::SharedPtr& pProgram) + ComputeState& setProgram(ref pProgram) { mpProgram = pProgram; return *this; @@ -72,18 +66,18 @@ class FALCOR_API ComputeState /** * Get the currently bound program */ - ComputeProgram::SharedPtr getProgram() const { return mpProgram; } + ref getProgram() const { return mpProgram; } /** * Get the active compute state object */ - ComputeStateObject::SharedPtr getCSO(const ComputeVars* pVars); + ref getCSO(const ComputeVars* pVars); private: - ComputeState(std::shared_ptr pDevice); + ComputeState(ref pDevice); - std::shared_ptr mpDevice; - ComputeProgram::SharedPtr mpProgram; + ref mpDevice; + ref mpProgram; ComputeStateObject::Desc mDesc; struct CachedData @@ -92,7 +86,7 @@ class FALCOR_API ComputeState }; CachedData mCachedData; - using StateGraph = StateGraph; - StateGraph::SharedPtr mpCsoGraph; + using ComputeStateGraph = StateGraph, void*>; + std::unique_ptr mpCsoGraph; }; } // namespace Falcor diff --git a/Source/Falcor/Core/State/GraphicsState.cpp b/Source/Falcor/Core/State/GraphicsState.cpp index 75c16b5bf..af548f9df 100644 --- a/Source/Falcor/Core/State/GraphicsState.cpp +++ b/Source/Falcor/Core/State/GraphicsState.cpp @@ -26,6 +26,7 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **************************************************************************/ #include "GraphicsState.h" +#include "Core/ObjectPython.h" #include "Core/API/Device.h" #include "Core/Program/ProgramVars.h" #include "Utils/Scripting/ScriptBindings.h" @@ -50,12 +51,12 @@ static GraphicsStateObject::PrimitiveType topology2Type(Vao::Topology t) } } -GraphicsState::SharedPtr GraphicsState::create(std::shared_ptr pDevice) +ref GraphicsState::create(ref pDevice) { - return SharedPtr(new GraphicsState(std::move(pDevice))); + return ref(new GraphicsState(pDevice)); } -GraphicsState::GraphicsState(std::shared_ptr pDevice) : mpDevice(std::move(pDevice)) +GraphicsState::GraphicsState(ref pDevice) : mpDevice(pDevice) { uint32_t vpCount = getMaxViewportCount(); @@ -69,14 +70,14 @@ GraphicsState::GraphicsState(std::shared_ptr pDevice) : mpDevice(std::mo setViewport(i, mViewports[i], true); } - mpGsoGraph = StateGraph::create(); + mpGsoGraph = std::make_unique(); } GraphicsState::~GraphicsState() = default; -GraphicsStateObject::SharedPtr GraphicsState::getGSO(const GraphicsVars* pVars) +ref GraphicsState::getGSO(const GraphicsVars* pVars) { - auto pProgramKernels = mpProgram ? mpProgram->getActiveVersion()->getKernels(mpDevice.get(), pVars) : nullptr; + auto pProgramKernels = mpProgram ? mpProgram->getActiveVersion()->getKernels(mpDevice, pVars) : nullptr; bool newProgVersion = pProgramKernels.get() != mCachedData.pProgramKernels; if (newProgVersion) { @@ -91,7 +92,7 @@ GraphicsStateObject::SharedPtr GraphicsState::getGSO(const GraphicsVars* pVars) mCachedData.pFboDesc = pFboDesc; } - GraphicsStateObject::SharedPtr pGso = mpGsoGraph->getCurrentNode(); + ref pGso = mpGsoGraph->getCurrentNode(); if (pGso == nullptr) { mDesc.setProgramKernels(pProgramKernels); @@ -99,7 +100,7 @@ GraphicsStateObject::SharedPtr GraphicsState::getGSO(const GraphicsVars* pVars) mDesc.setVertexLayout(mpVao->getVertexLayout()); mDesc.setPrimitiveType(topology2Type(mpVao->getPrimitiveTopology())); - StateGraph::CompareFunc cmpFunc = [&desc = mDesc](GraphicsStateObject::SharedPtr pGso) -> bool + GraphicsStateGraph::CompareFunc cmpFunc = [&desc = mDesc](ref pGso) -> bool { return pGso && (desc == pGso->getDesc()); }; if (mpGsoGraph->scanForMatchingNode(cmpFunc)) @@ -108,14 +109,15 @@ GraphicsStateObject::SharedPtr GraphicsState::getGSO(const GraphicsVars* pVars) } else { - pGso = GraphicsStateObject::create(mpDevice.get(), mDesc); + pGso = GraphicsStateObject::create(mpDevice, mDesc); + pGso->breakStrongReferenceToDevice(); mpGsoGraph->setCurrentNodeData(pGso); } } return pGso; } -GraphicsState& GraphicsState::setFbo(const Fbo::SharedPtr& pFbo, bool setVp0Sc0) +GraphicsState& GraphicsState::setFbo(const ref& pFbo, bool setVp0Sc0) { mpFbo = pFbo; @@ -129,7 +131,7 @@ GraphicsState& GraphicsState::setFbo(const Fbo::SharedPtr& pFbo, bool setVp0Sc0) return *this; } -void GraphicsState::pushFbo(const Fbo::SharedPtr& pFbo, bool setVp0Sc0) +void GraphicsState::pushFbo(const ref& pFbo, bool setVp0Sc0) { mFboStack.push(mpFbo); setFbo(pFbo, setVp0Sc0); @@ -143,7 +145,7 @@ void GraphicsState::popFbo(bool setVp0Sc0) mFboStack.pop(); } -GraphicsState& GraphicsState::setVao(const Vao::SharedConstPtr& pVao) +GraphicsState& GraphicsState::setVao(const ref& pVao) { if (mpVao != pVao) { @@ -153,7 +155,7 @@ GraphicsState& GraphicsState::setVao(const Vao::SharedConstPtr& pVao) return *this; } -GraphicsState& GraphicsState::setBlendState(BlendState::SharedPtr pBlendState) +GraphicsState& GraphicsState::setBlendState(ref pBlendState) { if (mDesc.getBlendState() != pBlendState) { @@ -163,7 +165,7 @@ GraphicsState& GraphicsState::setBlendState(BlendState::SharedPtr pBlendState) return *this; } -GraphicsState& GraphicsState::setRasterizerState(RasterizerState::SharedPtr pRasterizerState) +GraphicsState& GraphicsState::setRasterizerState(ref pRasterizerState) { if (mDesc.getRasterizerState() != pRasterizerState) { @@ -183,7 +185,7 @@ GraphicsState& GraphicsState::setSampleMask(uint32_t sampleMask) return *this; } -GraphicsState& GraphicsState::setDepthStencilState(DepthStencilState::SharedPtr pDepthStencilState) +GraphicsState& GraphicsState::setDepthStencilState(ref pDepthStencilState) { if (mDesc.getDepthStencilState() != pDepthStencilState) { @@ -249,8 +251,13 @@ void GraphicsState::setScissors(uint32_t index, const GraphicsState::Scissor& sc mScissors[index] = sc; } +void GraphicsState::breakStrongReferenceToDevice() +{ + mpDevice.breakStrongReference(); +} + FALCOR_SCRIPT_BINDING(GraphicsState) { - pybind11::class_(m, "GraphicsState"); + pybind11::class_>(m, "GraphicsState"); } } // namespace Falcor diff --git a/Source/Falcor/Core/State/GraphicsState.h b/Source/Falcor/Core/State/GraphicsState.h index 8e1968d45..64435db95 100644 --- a/Source/Falcor/Core/State/GraphicsState.h +++ b/Source/Falcor/Core/State/GraphicsState.h @@ -28,6 +28,7 @@ #pragma once #include "StateGraph.h" #include "Core/Macros.h" +#include "Core/Object.h" #include "Core/API/FBO.h" #include "Core/API/VAO.h" #include "Core/API/DepthStencilState.h" @@ -35,9 +36,9 @@ #include "Core/API/BlendState.h" #include "Core/API/GraphicsStateObject.h" #include "Core/Program/GraphicsProgram.h" -#include #include #include +#include namespace Falcor { @@ -48,11 +49,9 @@ class GraphicsVars; * 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 FALCOR_API GraphicsState +class FALCOR_API GraphicsState : public Object { public: - using SharedPtr = std::shared_ptr; - virtual ~GraphicsState(); /** @@ -92,31 +91,26 @@ class FALCOR_API GraphicsState * @param pDevice GPU device. * @return A new object, or an exception is thrown if creation failed. */ - static SharedPtr create(std::shared_ptr pDevice); - - /** - * Copy constructor. Useful if you need to make minor changes to an already existing object - */ - SharedPtr operator=(const SharedPtr& other); + static ref create(ref pDevice); /** * Get current FBO. */ - Fbo::SharedPtr getFbo() const { return mpFbo; } + ref getFbo() const { return mpFbo; } /** * Set an FBO. This function doesn't store the current FBO state. * @param[in] pFbo An FBO object. If nullptr is used, will detach the current FBO * @param[in] setVp0Sc0 If true, will set viewport 0 and scissor 0 to match the FBO dimensions */ - GraphicsState& setFbo(const Fbo::SharedPtr& pFbo, bool setVp0Sc0 = true); + GraphicsState& setFbo(const ref& pFbo, bool setVp0Sc0 = true); /** * Set a new FBO and store the current FBO into a stack. Useful for multi-pass effects. * @param[in] pFbo - a new FBO object. If nullptr is used, will bind an empty framebuffer object * @param[in] setVp0Sc0 If true, viewport 0 and scissor 0 will be set to match the FBO dimensions */ - void pushFbo(const Fbo::SharedPtr& pFbo, bool setVp0Sc0 = true); + void pushFbo(const ref& pFbo, bool setVp0Sc0 = true); /** * Restore the last FBO pushed into the FBO stack. If the stack is empty, an error will be logged. @@ -128,12 +122,12 @@ class FALCOR_API GraphicsState * Set a new vertex array object. By default, no VAO is bound. * @param[in] pVao The Vao object to bind. If this is nullptr, will unbind the current VAO. */ - GraphicsState& setVao(const Vao::SharedConstPtr& pVao); + GraphicsState& setVao(const ref& pVao); /** * Get the currently bound VAO. */ - Vao::SharedConstPtr getVao() const { return mpVao; } + ref getVao() const { return mpVao; } /** * Set the stencil reference value. @@ -217,7 +211,7 @@ class FALCOR_API GraphicsState /** * Bind a program to the pipeline. */ - GraphicsState& setProgram(const GraphicsProgram::SharedPtr& pProgram) + GraphicsState& setProgram(const ref& pProgram) { FALCOR_ASSERT(pProgram); mpProgram = pProgram; @@ -227,37 +221,37 @@ class FALCOR_API GraphicsState /** * Get the currently bound program. */ - GraphicsProgram::SharedPtr getProgram() const { return mpProgram; } + ref getProgram() const { return mpProgram; } /** * Set a blend-state. */ - GraphicsState& setBlendState(BlendState::SharedPtr pBlendState); + GraphicsState& setBlendState(ref pBlendState); /** * Get the currently bound blend-state. */ - BlendState::SharedPtr getBlendState() const { return mDesc.getBlendState(); } + ref getBlendState() const { return mDesc.getBlendState(); } /** * Set a rasterizer-state. */ - GraphicsState& setRasterizerState(RasterizerState::SharedPtr pRasterizerState); + GraphicsState& setRasterizerState(ref pRasterizerState); /** * Get the currently bound rasterizer-state. */ - RasterizerState::SharedPtr getRasterizerState() const { return mDesc.getRasterizerState(); } + ref getRasterizerState() const { return mDesc.getRasterizerState(); } /** * Set a depth-stencil state. */ - GraphicsState& setDepthStencilState(DepthStencilState::SharedPtr pDepthStencilState); + GraphicsState& setDepthStencilState(ref pDepthStencilState); /** * Get the currently bound depth-stencil state. */ - DepthStencilState::SharedPtr getDepthStencilState() const { return mDesc.getDepthStencilState(); } + ref getDepthStencilState() const { return mDesc.getDepthStencilState(); } /** * Set the sample mask. @@ -272,26 +266,28 @@ class FALCOR_API GraphicsState /** * Get the active graphics state object. */ - virtual GraphicsStateObject::SharedPtr getGSO(const GraphicsVars* pVars); + virtual ref getGSO(const GraphicsVars* pVars); /** * Get the desc */ const GraphicsStateObject::Desc& getDesc() const { return mDesc; } + void breakStrongReferenceToDevice(); + private: - GraphicsState(std::shared_ptr pDevice); + GraphicsState(ref pDevice); - std::shared_ptr mpDevice; - Vao::SharedConstPtr mpVao; - Fbo::SharedPtr mpFbo; - GraphicsProgram::SharedPtr mpProgram; + BreakableReference mpDevice; + ref mpVao; + ref mpFbo; + ref mpProgram; GraphicsStateObject::Desc mDesc; uint8_t mStencilRef = 0; std::vector mViewports; std::vector mScissors; - std::stack mFboStack; + std::stack> mFboStack; std::vector> mVpStack; std::vector> mScStack; @@ -302,7 +298,7 @@ class FALCOR_API GraphicsState }; CachedData mCachedData; - using StateGraph = StateGraph; - StateGraph::SharedPtr mpGsoGraph; + using GraphicsStateGraph = StateGraph, void*>; + std::unique_ptr mpGsoGraph; }; } // namespace Falcor diff --git a/Source/Falcor/Core/State/StateGraph.h b/Source/Falcor/Core/State/StateGraph.h index 7984d10b4..793f7f52b 100644 --- a/Source/Falcor/Core/State/StateGraph.h +++ b/Source/Falcor/Core/State/StateGraph.h @@ -27,7 +27,6 @@ **************************************************************************/ #pragma once #include -#include #include #include @@ -37,21 +36,10 @@ template; - using CompareFunc = std::function; StateGraph() : mGraph(1) {} - /** - * Create a new state graph. - * @return New object, or throws an exception if creation failed. - */ - static SharedPtr create() // #SHADER_VAR remove this - { - return SharedPtr(new StateGraph()); - } - bool isEdgeExists(const EdgeType& e) const { return (getEdgeIt(e) != mGraph[mCurrentNode].edges.end()); } bool walk(const EdgeType& e) diff --git a/Source/Falcor/Core/Testbed.cpp b/Source/Falcor/Core/Testbed.cpp index b7724a077..eb5082f9a 100644 --- a/Source/Falcor/Core/Testbed.cpp +++ b/Source/Falcor/Core/Testbed.cpp @@ -26,6 +26,7 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **************************************************************************/ #include "Testbed.h" +#include "Core/ObjectPython.h" #include "Core/Program/ProgramManager.h" #include "Utils/Scripting/ScriptBindings.h" #include "Utils/Threading.h" @@ -39,15 +40,6 @@ namespace Falcor { -namespace -{ -/// Global pointer holding on to the first created Testbed instance. -/// Currently, we are limited to only have one instance of the Testbed at runtime due to various global state in Falcor -/// (such as the graphics device). We also want to keep the instance alive until the end of the runtime in order to -/// allow graceful shutdown as some other objects that expect global state to still be available when shutting down. -TestbedSharedPtr spTestbed; -} // namespace - Testbed::Testbed(const Options& options) { internalInit(options); @@ -58,13 +50,15 @@ Testbed::~Testbed() internalShutdown(); } -TestbedSharedPtr Testbed::create(const Options& options) -{ - if (spTestbed) - throw RuntimeError("Only one instance of Testbed can be created during the lifetime of the Falcor runtime."); +/// Keep a list of all created testbed instances. +/// This is a workaround to make sure they are destroyed after Python has shutdown. +static std::vector> spTestbeds; // TODO: REMOVEGLOBAL - spTestbed = std::make_shared(options); - return spTestbed; +ref Testbed::create(const Options& options) +{ + ref pTestbed = make_ref(options); + spTestbeds.push_back(pTestbed); + return pTestbed; } void Testbed::run() @@ -111,13 +105,13 @@ void Testbed::frame() // Execute the render graph. if (mpRenderGraph) { - (*mpRenderGraph->getPassesDictionary())[kRenderPassRefreshFlags] = RenderPassRefreshFlags::None; + mpRenderGraph->getPassesDictionary()[kRenderPassRefreshFlags] = RenderPassRefreshFlags::None; mpRenderGraph->execute(pRenderContext); // Blit main graph output to frame buffer. if (mpRenderGraph->getOutputCount() > 0) { - Texture::SharedPtr pOutTex = std::dynamic_pointer_cast(mpRenderGraph->getOutput(0)); + ref pOutTex = mpRenderGraph->getOutput(0)->asTexture(); FALCOR_ASSERT(pOutTex); pRenderContext->blit(pOutTex->getSRV(), mpTargetFBO->getRenderTargetView(0)); } @@ -168,28 +162,38 @@ void Testbed::loadScene(const std::filesystem::path& path) mpRenderGraph->setScene(mpScene); } -void Testbed::setRenderGraph(const RenderGraph::SharedPtr& graph) +ref Testbed::getScene() const { - mpRenderGraph = graph; + return mpScene; +} - if (mpRenderGraph) - { - mpRenderGraph->onResize(mpTargetFBO.get()); - mpRenderGraph->setScene(mpScene); - } +Clock& Testbed::getClock() +{ + return mClock; } -Scene::SharedPtr Testbed::getScene() const +ref Testbed::createRenderGraph(const std::string& name) { - return mpScene; + return RenderGraph::create(mpDevice, name); } -Clock& Testbed::getClock() +ref Testbed::loadRenderGraph(const std::filesystem::path& path) { - return mClock; + return RenderGraph::createFromFile(mpDevice, path); } -const RenderGraph::SharedPtr& Testbed::getRenderGraph() const +void Testbed::setRenderGraph(const ref& graph) +{ + mpRenderGraph = graph; + + if (mpRenderGraph) + { + mpRenderGraph->onResize(mpTargetFBO.get()); + mpRenderGraph->setScene(mpScene); + } +} + +const ref& Testbed::getRenderGraph() const { return mpRenderGraph; } @@ -265,7 +269,7 @@ void Testbed::internalInit(const Options& options) Threading::start(); // Create the device. - mpDevice = Device::create(options.deviceDesc); + mpDevice = make_ref(options.deviceDesc); // Create the window & swapchain. if (options.createWindow) @@ -279,12 +283,12 @@ void Testbed::internalInit(const Options& options) desc.height = mpWindow->getClientAreaSize().y; desc.imageCount = 3; desc.enableVSync = options.windowDesc.enableVSync; - mpSwapchain = std::make_unique(mpDevice, desc, mpWindow->getApiHandle()); + mpSwapchain = make_ref(mpDevice, desc, mpWindow->getApiHandle()); } // Create target frame buffer uint2 fboSize = mpWindow ? mpWindow->getClientAreaSize() : uint2(options.windowDesc.width, options.windowDesc.height); - mpTargetFBO = Fbo::create2D(mpDevice.get(), fboSize.x, fboSize.y, options.colorFormat, options.depthFormat); + mpTargetFBO = Fbo::create2D(mpDevice, fboSize.x, fboSize.y, options.colorFormat, options.depthFormat); // Set global shader defines. Program::DefineList globalDefines = { @@ -312,7 +316,6 @@ void Testbed::internalShutdown() if (mpDevice) mpDevice->flushAndSync(); - Clock::shutdown(); Threading::shutdown(); mpGui.reset(); @@ -320,7 +323,6 @@ void Testbed::internalShutdown() mpSwapchain.reset(); mpWindow.reset(); - mpDevice->cleanup(); mpDevice.reset(); #ifdef _DEBUG Device::reportLiveObjects(); @@ -333,7 +335,7 @@ void Testbed::resizeTargetFBO(uint32_t width, uint32_t height) { // Resize target frame buffer. auto pPrevFBO = mpTargetFBO; - mpTargetFBO = Fbo::create2D(mpDevice.get(), width, height, pPrevFBO->getDesc()); + mpTargetFBO = Fbo::create2D(mpDevice, width, height, pPrevFBO->getDesc()); mpDevice->getRenderContext()->blit(pPrevFBO->getColorTexture(0)->getSRV(), mpTargetFBO->getRenderTargetView(0)); if (mpGui) @@ -440,7 +442,7 @@ void Testbed::captureOutput(std::string filename, uint32_t outputIndex) RenderContext* pRenderContext = mpDevice->getRenderContext(); const std::string outputName = mpRenderGraph->getOutputName(outputIndex); - const Texture::SharedPtr pOutput = mpRenderGraph->getOutput(outputName)->asTexture(); + const ref pOutput = mpRenderGraph->getOutput(outputName)->asTexture(); if (!pOutput) throw RuntimeError("Graph output {} is not a texture", outputName); @@ -484,7 +486,7 @@ void Testbed::captureOutput(std::string filename, uint32_t outputIndex) } // Copy relevant channels into new texture if necessary. - Texture::SharedPtr pTex = pOutput; + ref pTex = pOutput; if (outputChannels == 1 && channels > 1) { // Determine output format. @@ -550,7 +552,7 @@ void Testbed::captureOutput(std::string filename, uint32_t outputIndex) // Copy color channel into temporary texture. pTex = Texture::create2D( - mpDevice.get(), pOutput->getWidth(), pOutput->getHeight(), outputFormat, 1, 1, nullptr, + mpDevice, pOutput->getWidth(), pOutput->getHeight(), outputFormat, 1, 1, nullptr, ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess ); mpImageProcessing->copyColorChannel(pRenderContext, pOutput->getSRV(0, 1, 0, 1), pTex->getUAV(), mask); @@ -563,41 +565,74 @@ void Testbed::captureOutput(std::string filename, uint32_t outputIndex) if (mask == TextureChannelFlags::RGBA) flags |= Bitmap::ExportFlags::ExportAlpha; - pTex->captureToFile(0, 0, filename, fileformat, flags); + pTex->captureToFile(0, 0, filename, fileformat, flags, false /* async */); } } FALCOR_SCRIPT_BINDING(Testbed) { + FALCOR_SCRIPT_BINDING_DEPENDENCY(Device) + FALCOR_SCRIPT_BINDING_DEPENDENCY(RenderGraph) + FALCOR_SCRIPT_BINDING_DEPENDENCY(Clock) + FALCOR_SCRIPT_BINDING_DEPENDENCY(Profiler) + FALCOR_SCRIPT_BINDING_DEPENDENCY(Scene) + using namespace pybind11::literals; - pybind11::class_> testbed(m, "Testbed"); + pybind11::class_> testbed(m, "Testbed"); - auto createTestbed = [](uint32_t width, uint32_t height, bool createWindow, uint32_t gpu) - { - Testbed::Options options; - options.windowDesc.width = width; - options.windowDesc.height = height; - options.createWindow = createWindow; - options.deviceDesc.gpu = gpu; - return Testbed::create(options); - }; - - Testbed::Options defaultOptions; testbed.def( - pybind11::init(createTestbed), "width"_a = defaultOptions.windowDesc.width, "height"_a = defaultOptions.windowDesc.height, - "createWindow"_a = defaultOptions.createWindow, "gpu"_a = defaultOptions.deviceDesc.gpu + pybind11::init( + [](uint32_t width, uint32_t height, bool create_window, Device::Type device_type, uint32_t gpu) + { + Testbed::Options options; + options.windowDesc.width = width; + options.windowDesc.height = height; + options.createWindow = create_window; + options.deviceDesc.type = device_type; + options.deviceDesc.gpu = gpu; + return Testbed::create(options); + } + ), + "width"_a = 1920, "height"_a = 1080, "create_window"_a = false, "device_type"_a = Device::Type::Default, "gpu"_a = 0 ); testbed.def("run", &Testbed::run); testbed.def("frame", &Testbed::frame); - testbed.def("resizeFrameBuffer", &Testbed::resizeFrameBuffer, "width"_a, "height"_a); - testbed.def("loadScene", &Testbed::loadScene, "path"_a); - testbed.def("captureOutput", &Testbed::captureOutput, "filename"_a, pybind11::arg("outputIndex") = uint32_t(0)); // PYTHONDEPRECATED + testbed.def("resize_frame_buffer", &Testbed::resizeFrameBuffer, "width"_a, "height"_a); + testbed.def("load_scene", &Testbed::loadScene, "path"_a); + testbed.def("create_render_graph", &Testbed::createRenderGraph, "name"_a = ""); + testbed.def("load_render_graph", &Testbed::loadRenderGraph, "path"_a); + testbed.def("capture_output", &Testbed::captureOutput, "path"_a, "output_index"_a = uint32_t(0)); // PYTHONDEPRECATED testbed.def_property_readonly("profiler", [](Testbed* pTestbed) { return pTestbed->getDevice()->getProfiler(); }); + testbed.def_property_readonly("device", &Testbed::getDevice); testbed.def_property_readonly("scene", &Testbed::getScene); testbed.def_property_readonly("clock", &Testbed::getClock); // PYTHONDEPRECATED + testbed.def_property("render_graph", &Testbed::getRenderGraph, &Testbed::setRenderGraph); + + // PYTHONDEPRECATED BEGIN + testbed.def( + pybind11::init( + [](uint32_t width, uint32_t height, bool createWindow, Device::Type deviceType, uint32_t gpu) + { + Testbed::Options options; + options.windowDesc.width = width; + options.windowDesc.height = height; + options.createWindow = createWindow; + options.deviceDesc.type = deviceType; + options.deviceDesc.gpu = gpu; + return Testbed::create(options); + } + ), + "width"_a = 1920, "height"_a = 1080, "createWindow"_a = false, "deviceType"_a = Device::Type::Default, "gpu"_a = 0 + ); + testbed.def("resizeFrameBuffer", &Testbed::resizeFrameBuffer, "width"_a, "height"_a); + testbed.def("loadScene", &Testbed::loadScene, "path"_a); + testbed.def("createRenderGraph", &Testbed::createRenderGraph, "name"_a = ""); + testbed.def("loadRenderGraph", &Testbed::loadRenderGraph, "path"_a); + testbed.def("captureOutput", &Testbed::captureOutput, "filename"_a, "outputIndex"_a = uint32_t(0)); // PYTHONDEPRECATED testbed.def_property("renderGraph", &Testbed::getRenderGraph, &Testbed::setRenderGraph); + // PYTHONDEPRECATED END } } // namespace Falcor diff --git a/Source/Falcor/Core/Testbed.h b/Source/Falcor/Core/Testbed.h index a821bc35a..c332cb5df 100644 --- a/Source/Falcor/Core/Testbed.h +++ b/Source/Falcor/Core/Testbed.h @@ -27,6 +27,7 @@ **************************************************************************/ #pragma once #include "Window.h" +#include "Core/Object.h" #include "Core/API/Device.h" #include "Core/API/Swapchain.h" #include "Core/API/Formats.h" @@ -43,12 +44,9 @@ namespace Falcor class ProfilerUI; -class Testbed; -using TestbedSharedPtr = std::shared_ptr; - /// Falcor testbed application class. /// This is the main Falcor application available through the Python API. -class Testbed : private Window::ICallbacks +class Testbed : public Object, private Window::ICallbacks { public: struct Options @@ -66,9 +64,9 @@ class Testbed : private Window::ICallbacks Testbed(const Options& options = Options()); virtual ~Testbed(); - static TestbedSharedPtr create(const Options& options); + static ref create(const Options& options); - const std::shared_ptr& getDevice() const { return mpDevice; } + const ref& getDevice() const { return mpDevice; } /// Run the main loop. /// This only returns if the application window is closed or the main loop is interrupted by calling interrupt(). @@ -86,11 +84,14 @@ class Testbed : private Window::ICallbacks void loadScene(const std::filesystem::path& path); - Scene::SharedPtr getScene() const; + ref getScene() const; Clock& getClock(); - void setRenderGraph(const RenderGraph::SharedPtr& graph); - const RenderGraph::SharedPtr& getRenderGraph() const; + ref createRenderGraph(const std::string& name = ""); + ref loadRenderGraph(const std::filesystem::path& path); + + void setRenderGraph(const ref& graph); + const ref& getRenderGraph() const; void captureOutput(std::string filename, uint32_t outputIndex = 0); @@ -112,15 +113,15 @@ class Testbed : private Window::ICallbacks void renderUI(); - std::shared_ptr mpDevice; - std::shared_ptr mpWindow; - std::unique_ptr mpSwapchain; - std::shared_ptr mpTargetFBO; + ref mpDevice; + ref mpWindow; + ref mpSwapchain; + ref mpTargetFBO; std::unique_ptr mpGui; std::unique_ptr mpProfilerUI; - Scene::SharedPtr mpScene; - RenderGraph::SharedPtr mpRenderGraph; + ref mpScene; + ref mpRenderGraph; std::unique_ptr mpImageProcessing; diff --git a/Source/Falcor/Core/Window.cpp b/Source/Falcor/Core/Window.cpp index c8453946a..1028b1639 100644 --- a/Source/Falcor/Core/Window.cpp +++ b/Source/Falcor/Core/Window.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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,6 +28,7 @@ #include "Window.h" #include "Macros.h" #include "Assert.h" +#include "ObjectPython.h" #include "GLFW.h" #include "Platform/OS.h" #include "Utils/Logger.h" @@ -368,53 +369,24 @@ class ApiCallbacks } }; -Window::Window(ICallbacks* pCallbacks, const Desc& desc) - : mDesc(desc), mMouseScale(1.0f / (float)desc.width, 1.0f / (float)desc.height), mpCallbacks(pCallbacks) -{} - -void Window::updateWindowSize() -{ - // Actual window size may be clamped to slightly lower than monitor resolution - int32_t width, height; - glfwGetWindowSize(mpGLFWWindow, &width, &height); - setWindowSize(width, height); -} - -void Window::setWindowSize(uint32_t width, uint32_t height) -{ - FALCOR_ASSERT(width > 0 && height > 0); - - mDesc.width = width; - mDesc.height = height; - mMouseScale.x = 1.0f / (float)mDesc.width; - mMouseScale.y = 1.0f / (float)mDesc.height; -} +static std::atomic sWindowCount; -Window::~Window() +ref Window::create(const Desc& desc, ICallbacks* pCallbacks) { - glfwDestroyWindow(mpGLFWWindow); - glfwTerminate(); -} - -void Window::shutdown() -{ - glfwSetWindowShouldClose(mpGLFWWindow, 1); + return ref(new Window(desc, pCallbacks)); } -bool Window::shouldClose() const -{ - return glfwWindowShouldClose(mpGLFWWindow); -} - -Window::SharedPtr Window::create(const Desc& desc, ICallbacks* pCallbacks) +Window::Window(const Desc& desc, ICallbacks* pCallbacks) + : mDesc(desc), mMouseScale(1.0f / (float)desc.width, 1.0f / (float)desc.height), mpCallbacks(pCallbacks) { // Set error callback glfwSetErrorCallback(ApiCallbacks::errorCallback); - // Init GLFW - if (glfwInit() == GLFW_FALSE) + // Init GLFW when first window is created. + if (sWindowCount.fetch_add(1) == 0) { - throw RuntimeError("Failed to initialize GLFW."); + if (glfwInit() == GLFW_FALSE) + throw RuntimeError("Failed to initialize GLFW."); } // Create the window @@ -443,52 +415,82 @@ Window::SharedPtr Window::create(const Desc& desc, ICallbacks* pCallbacks) glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); } - GLFWwindow* pGLFWWindow = glfwCreateWindow(w, h, desc.title.c_str(), nullptr, nullptr); - if (!pGLFWWindow) + mpGLFWWindow = glfwCreateWindow(w, h, desc.title.c_str(), nullptr, nullptr); + if (!mpGLFWWindow) { throw RuntimeError("Failed to create GLFW window."); } - SharedPtr pWindow = SharedPtr(new Window(pCallbacks, desc)); - // Init handles - pWindow->mpGLFWWindow = pGLFWWindow; #if FALCOR_WINDOWS - pWindow->mApiHandle = glfwGetWin32Window(pGLFWWindow); - FALCOR_ASSERT(pWindow->mApiHandle); + mApiHandle = glfwGetWin32Window(mpGLFWWindow); + FALCOR_ASSERT(mApiHandle); #elif FALCOR_LINUX - pWindow->mApiHandle.pDisplay = glfwGetX11Display(); - pWindow->mApiHandle.window = glfwGetX11Window(pGLFWWindow); - FALCOR_ASSERT(pWindow->mApiHandle.pDisplay != nullptr); + mApiHandle.pDisplay = glfwGetX11Display(); + mApiHandle.window = glfwGetX11Window(mpGLFWWindow); + FALCOR_ASSERT(mApiHandle.pDisplay != nullptr); #endif - setMainWindowHandle(pWindow->mApiHandle); - - pWindow->updateWindowSize(); + updateWindowSize(); - glfwSetWindowUserPointer(pGLFWWindow, pWindow.get()); + glfwSetWindowUserPointer(mpGLFWWindow, this); // Set callbacks - glfwSetWindowSizeCallback(pGLFWWindow, ApiCallbacks::windowSizeCallback); - glfwSetKeyCallback(pGLFWWindow, ApiCallbacks::keyboardCallback); - glfwSetMouseButtonCallback(pGLFWWindow, ApiCallbacks::mouseButtonCallback); - glfwSetCursorPosCallback(pGLFWWindow, ApiCallbacks::mouseMoveCallback); - glfwSetScrollCallback(pGLFWWindow, ApiCallbacks::mouseWheelCallback); - glfwSetCharCallback(pGLFWWindow, ApiCallbacks::charInputCallback); - glfwSetDropCallback(pGLFWWindow, ApiCallbacks::droppedFileCallback); + glfwSetWindowSizeCallback(mpGLFWWindow, ApiCallbacks::windowSizeCallback); + glfwSetKeyCallback(mpGLFWWindow, ApiCallbacks::keyboardCallback); + glfwSetMouseButtonCallback(mpGLFWWindow, ApiCallbacks::mouseButtonCallback); + glfwSetCursorPosCallback(mpGLFWWindow, ApiCallbacks::mouseMoveCallback); + glfwSetScrollCallback(mpGLFWWindow, ApiCallbacks::mouseWheelCallback); + glfwSetCharCallback(mpGLFWWindow, ApiCallbacks::charInputCallback); + glfwSetDropCallback(mpGLFWWindow, ApiCallbacks::droppedFileCallback); if (desc.mode == WindowMode::Minimized) { // Iconify and show window to make it available if user clicks on it - glfwIconifyWindow(pWindow->mpGLFWWindow); - glfwShowWindow(pWindow->mpGLFWWindow); + glfwIconifyWindow(mpGLFWWindow); + glfwShowWindow(mpGLFWWindow); } else { - glfwShowWindow(pWindow->mpGLFWWindow); - glfwFocusWindow(pWindow->mpGLFWWindow); + glfwShowWindow(mpGLFWWindow); + glfwFocusWindow(mpGLFWWindow); } +} + +Window::~Window() +{ + glfwDestroyWindow(mpGLFWWindow); + + // Shutdown GLFW when last window is destroyed. + if (sWindowCount.fetch_sub(1) == 1) + glfwTerminate(); +} + +void Window::updateWindowSize() +{ + // Actual window size may be clamped to slightly lower than monitor resolution + int32_t width, height; + glfwGetWindowSize(mpGLFWWindow, &width, &height); + setWindowSize(width, height); +} + +void Window::setWindowSize(uint32_t width, uint32_t height) +{ + FALCOR_ASSERT(width > 0 && height > 0); - return pWindow; + mDesc.width = width; + mDesc.height = height; + mMouseScale.x = 1.0f / (float)mDesc.width; + mMouseScale.y = 1.0f / (float)mDesc.height; +} + +void Window::shutdown() +{ + glfwSetWindowShouldClose(mpGLFWWindow, 1); +} + +bool Window::shouldClose() const +{ + return glfwWindowShouldClose(mpGLFWWindow); } void Window::resize(uint32_t width, uint32_t height) @@ -656,21 +658,12 @@ void Window::handleGamepadInput() FALCOR_SCRIPT_BINDING(Window) { - pybind11::class_ window(m, "Window"); + pybind11::class_> window(m, "Window"); window.def("setWindowPos", &Window::setWindowPos); pybind11::enum_ windowMode(m, "WindowMode"); windowMode.value("Normal", Window::WindowMode::Normal); windowMode.value("Fullscreen", Window::WindowMode::Fullscreen); windowMode.value("Minimized", Window::WindowMode::Minimized); - - ScriptBindings::SerializableStruct windowDesc(m, "WindowDesc"); -#define field(f_) field(#f_, &Window::Desc::f_) - windowDesc.field(width); - windowDesc.field(height); - windowDesc.field(title); - windowDesc.field(mode); - windowDesc.field(resizableWindow); -#undef field } } // namespace Falcor diff --git a/Source/Falcor/Core/Window.h b/Source/Falcor/Core/Window.h index fb90b751d..acbb0310c 100644 --- a/Source/Falcor/Core/Window.h +++ b/Source/Falcor/Core/Window.h @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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,6 +27,7 @@ **************************************************************************/ #pragma once #include "Macros.h" +#include "Object.h" #include "Platform/PlatformHandles.h" #include "Utils/Math/Vector.h" #include @@ -42,10 +43,9 @@ struct MouseEvent; struct GamepadEvent; struct GamepadState; -class FALCOR_API Window +class FALCOR_API Window : public Object { public: - using SharedPtr = std::shared_ptr; using ApiHandle = WindowHandle; /** @@ -92,7 +92,7 @@ class FALCOR_API Window * @param[in] pCallbacks User callbacks * @return A new object, or throws an exception if creation failed. */ - static SharedPtr create(const Desc& desc, ICallbacks* pCallbacks); + static ref create(const Desc& desc, ICallbacks* pCallbacks); /** * Destructor @@ -165,7 +165,7 @@ class FALCOR_API Window private: friend class ApiCallbacks; - Window(ICallbacks* pCallbacks, const Desc& desc); + Window(const Desc& desc, ICallbacks* pCallbacks); void updateWindowSize(); void setWindowSize(uint32_t width, uint32_t height); diff --git a/Source/Falcor/Falcor.h b/Source/Falcor/Falcor.h index 41df6568c..b8c254032 100644 --- a/Source/Falcor/Falcor.h +++ b/Source/Falcor/Falcor.h @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -98,18 +98,13 @@ #include "Scene/Animation/Animation.h" #include "Scene/Animation/AnimationController.h" -// @skallweit: This is temporary to allow renderpasses to be compiled unmodified. Needs to be removed. -#include "RenderGraph/RenderPass.h" - // Utils #include "Utils/StringFormatters.h" #include "Utils/Math/Common.h" #include "Utils/Math/Vector.h" -#include "Utils/Math/Float16.h" #include "Utils/Logger.h" #include "Utils/UI/InputTypes.h" #include "Utils/Timing/Profiler.h" -#include "Utils/Scripting/Scripting.h" #include // TODO C++20: Replace with #include // TODO C++20: Replace with diff --git a/Source/Falcor/Falcor.natvis b/Source/Falcor/Falcor.natvis index a3ddd8939..dff7ae82d 100644 --- a/Source/Falcor/Falcor.natvis +++ b/Source/Falcor/Falcor.natvis @@ -1,35 +1,40 @@ - + + + mPtr + nullptr + {(void*)mPtr} refCount={mPtr->mRefCount} {*mPtr} + + {mID} InvalidID - - - - {x} + + + {x,g} x - - {x}, {y} + + {x,g}, {y,g} x y - - {x}, {y}, {z} + + {x,g}, {y,g}, {z,g} x y z - - {x}, {y}, {z}, {w} + + {x,g}, {y,g}, {z,g}, {w,g} x y @@ -37,25 +42,53 @@ w - - [{value[0],g}] [{value[1],g}] [{value[2],g}] [{value[3],g}] + + [{mRows[0],g}] [{mRows[1],g}] [{mRows[2],g}] + + + [{mRows[0]}] + + + [{mRows[1]}] + + + [{mRows[2]}] + + + + + [{mRows[0],g}] [{mRows[1],g}] [{mRows[2],g}] + + + [{mRows[0]}] + + + [{mRows[1]}] + + + [{mRows[2]}] + + + + + [{mRows[0],g}] [{mRows[1],g}] [{mRows[2],g}] [{mRows[3],g}] - - [{value[0]}] + + [{mRows[0]}] - - [{value[1]}] + + [{mRows[1]}] - - [{value[2]}] + + [{mRows[2]}] - - [{value[3]}] + + [{mRows[3]}] - - ({x}, {y}, {z}), {w} + + ({x,g}, {y,g}, {z,g}), {w,g} x y @@ -63,13 +96,6 @@ w - - (({real.x}, {real.y}, {real.z}), {real.w}), (({dual.x}, {dual.y}, {dual.z}), {dual.w}) - - real - dual - - diff --git a/Source/Falcor/FalcorPython.cpp b/Source/Falcor/FalcorPython.cpp index 1da3b0599..61b77f1f7 100644 --- a/Source/Falcor/FalcorPython.cpp +++ b/Source/Falcor/FalcorPython.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -49,7 +49,7 @@ static bool isLoadedFromEmbeddedPython() return false; } -PYBIND11_MODULE(falcor, m) +PYBIND11_MODULE(falcor_ext, m) { if (!isLoadedFromEmbeddedPython()) { diff --git a/Source/Falcor/RenderGraph/BasePasses/BaseGraphicsPass.cpp b/Source/Falcor/GlobalState.cpp similarity index 60% rename from Source/Falcor/RenderGraph/BasePasses/BaseGraphicsPass.cpp rename to Source/Falcor/GlobalState.cpp index 83d46f874..0a568b28e 100644 --- a/Source/Falcor/RenderGraph/BasePasses/BaseGraphicsPass.cpp +++ b/Source/Falcor/GlobalState.cpp @@ -25,35 +25,42 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **************************************************************************/ -#include "BaseGraphicsPass.h" +#include "GlobalState.h" +#include "Core/Errors.h" namespace Falcor { - BaseGraphicsPass::BaseGraphicsPass(std::shared_ptr pDevice, const Program::Desc& progDesc, const Program::DefineList& programDefines) - : mpDevice(std::move(pDevice)) - { - auto pProg = GraphicsProgram::create(mpDevice, progDesc, programDefines); - - mpState = GraphicsState::create(mpDevice); - mpState->setProgram(pProg); - - mpVars = GraphicsVars::create(mpDevice, pProg.get()); - } - - void BaseGraphicsPass::addDefine(const std::string& name, const std::string& value, bool updateVars) - { - mpState->getProgram()->addDefine(name, value); - if (updateVars) mpVars = GraphicsVars::create(mpDevice, mpState->getProgram().get()); - } - - void BaseGraphicsPass::removeDefine(const std::string& name, bool updateVars) - { - mpState->getProgram()->removeDefine(name); - if (updateVars) mpVars = GraphicsVars::create(mpDevice, mpState->getProgram().get()); - } - - void BaseGraphicsPass::setVars(const GraphicsVars::SharedPtr& pVars) - { - mpVars = pVars ? pVars : GraphicsVars::create(mpDevice, mpState->getProgram().get()); - } + +static SceneBuilder* spActivePythonSceneBuilder; // TODO: REMOVEGLOBAL +static ref spActivePythonRenderGraphDevice; // TODO: REMOVEGLOBAL + +void setActivePythonSceneBuilder(SceneBuilder* pSceneBuilder) +{ + spActivePythonSceneBuilder = pSceneBuilder; +} + +SceneBuilder& accessActivePythonSceneBuilder() +{ + if (!spActivePythonSceneBuilder) + throw RuntimeError("This can only be called in a Python scene building context!"); + return *spActivePythonSceneBuilder; +} + +void setActivePythonRenderGraphDevice(ref pDevice) +{ + spActivePythonRenderGraphDevice = pDevice; +} + +ref getActivePythonRenderGraphDevice() +{ + return spActivePythonRenderGraphDevice; } + +ref accessActivePythonRenderGraphDevice() +{ + if (!spActivePythonRenderGraphDevice) + throw RuntimeError("This can only be called from a script executed in Mogwai or when loading a render graph file!"); + return spActivePythonRenderGraphDevice; +} + +} // namespace Falcor diff --git a/Source/Falcor/GlobalState.h b/Source/Falcor/GlobalState.h new file mode 100644 index 000000000..a4a95996e --- /dev/null +++ b/Source/Falcor/GlobalState.h @@ -0,0 +1,54 @@ +/*************************************************************************** + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. + # + # Redistribution and use in source and binary forms, with or without + # modification, are permitted provided that the following conditions + # are met: + # * Redistributions of source code must retain the above copyright + # notice, this list of conditions and the following disclaimer. + # * Redistributions in binary form must reproduce the above copyright + # notice, this list of conditions and the following disclaimer in the + # documentation and/or other materials provided with the distribution. + # * Neither the name of NVIDIA CORPORATION nor the names of its + # contributors may be used to endorse or promote products derived + # from this software without specific prior written permission. + # + # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY + # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + # (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/Macros.h" +#include "Core/API/Device.h" +#include "Scene/SceneBuilder.h" + +namespace Falcor +{ + +/// This file is a temporary workaround to give access to global state in deprecated python bindings. +/// Some of the Python API was originally designed to allow creation of objects "out of thin air". +/// Two places are affected: +/// - Loading `.pyscene` files: Here many of the scene objects can just be created without a factory. +/// - Creating/loading render graphs and passes +/// The C++ side is currently being refactored to get rid of all that global state (for example, the GPU device). +/// In order to not break the existing Python API, we use global state in very specific contexts only. +/// All of the affected python bindings are marked with PYTHONDEPRECATED. Once these bindings are removed, +/// this file can also be removed as well. + +FALCOR_API void setActivePythonSceneBuilder(SceneBuilder* pSceneBuilder); +FALCOR_API SceneBuilder& accessActivePythonSceneBuilder(); + +FALCOR_API void setActivePythonRenderGraphDevice(ref pDevice); +FALCOR_API ref getActivePythonRenderGraphDevice(); +FALCOR_API ref accessActivePythonRenderGraphDevice(); + +} // namespace Falcor diff --git a/Source/Falcor/RenderGraph/BasePasses/ComputePass.cpp b/Source/Falcor/RenderGraph/BasePasses/ComputePass.cpp deleted file mode 100644 index d87623731..000000000 --- a/Source/Falcor/RenderGraph/BasePasses/ComputePass.cpp +++ /dev/null @@ -1,87 +0,0 @@ -/*************************************************************************** - # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. - # - # Redistribution and use in source and binary forms, with or without - # modification, are permitted provided that the following conditions - # are met: - # * Redistributions of source code must retain the above copyright - # notice, this list of conditions and the following disclaimer. - # * Redistributions in binary form must reproduce the above copyright - # notice, this list of conditions and the following disclaimer in the - # documentation and/or other materials provided with the distribution. - # * Neither the name of NVIDIA CORPORATION nor the names of its - # contributors may be used to endorse or promote products derived - # from this software without specific prior written permission. - # - # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY - # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - **************************************************************************/ -#include "ComputePass.h" -#include "Core/API/ComputeContext.h" -#include "Utils/Math/Common.h" - -namespace Falcor -{ - ComputePass::ComputePass(std::shared_ptr pDevice, const Program::Desc& desc, const Program::DefineList& defines, bool createVars) - : mpDevice(std::move(pDevice)) - { - auto pProg = ComputeProgram::create(mpDevice, desc, defines); - mpState = ComputeState::create(mpDevice); - mpState->setProgram(pProg); - if (createVars) mpVars = ComputeVars::create(mpDevice, pProg.get()); - FALCOR_ASSERT(pProg && mpState && (!createVars || mpVars)) ; - } - - ComputePass::SharedPtr ComputePass::create(std::shared_ptr pDevice, const std::filesystem::path& path, const std::string& csEntry, const Program::DefineList& defines, bool createVars) - { - Program::Desc desc; - desc.addShaderLibrary(path).csEntry(csEntry); - return create(pDevice, desc, defines, createVars); - } - - ComputePass::SharedPtr ComputePass::create(std::shared_ptr pDevice, const Program::Desc& desc, const Program::DefineList& defines, bool createVars) - { - return SharedPtr(new ComputePass(std::move(pDevice), desc,defines, createVars)); - } - - void ComputePass::execute(ComputeContext* pContext, uint32_t nThreadX, uint32_t nThreadY, uint32_t nThreadZ) - { - FALCOR_ASSERT(mpVars); - uint3 threadGroupSize = mpState->getProgram()->getReflector()->getThreadGroupSize(); - uint3 groups = div_round_up(uint3(nThreadX, nThreadY, nThreadZ), threadGroupSize); - pContext->dispatch(mpState.get(), mpVars.get(), groups); - } - - void ComputePass::executeIndirect(ComputeContext* pContext, const Buffer* pArgBuffer, uint64_t argBufferOffset) - { - FALCOR_ASSERT(mpVars); - pContext->dispatchIndirect(mpState.get(), mpVars.get(), pArgBuffer, argBufferOffset); - } - - void ComputePass::addDefine(const std::string& name, const std::string& value, bool updateVars) - { - mpState->getProgram()->addDefine(name, value); - if (updateVars) mpVars = ComputeVars::create(mpDevice, mpState->getProgram().get()); - } - - void ComputePass::removeDefine(const std::string& name, bool updateVars) - { - mpState->getProgram()->removeDefine(name); - if (updateVars) mpVars = ComputeVars::create(mpDevice, mpState->getProgram().get()); - } - - void ComputePass::setVars(const ComputeVars::SharedPtr& pVars) - { - mpVars = pVars ? pVars : ComputeVars::create(mpDevice, mpState->getProgram().get()); - FALCOR_ASSERT(mpVars); - } -} diff --git a/Source/Falcor/RenderGraph/BasePasses/FullScreenPass.cpp b/Source/Falcor/RenderGraph/BasePasses/FullScreenPass.cpp deleted file mode 100644 index 3178471c2..000000000 --- a/Source/Falcor/RenderGraph/BasePasses/FullScreenPass.cpp +++ /dev/null @@ -1,159 +0,0 @@ -/*************************************************************************** - # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. - # - # Redistribution and use in source and binary forms, with or without - # modification, are permitted provided that the following conditions - # are met: - # * Redistributions of source code must retain the above copyright - # notice, this list of conditions and the following disclaimer. - # * Redistributions in binary form must reproduce the above copyright - # notice, this list of conditions and the following disclaimer in the - # documentation and/or other materials provided with the distribution. - # * Neither the name of NVIDIA CORPORATION nor the names of its - # contributors may be used to endorse or promote products derived - # from this software without specific prior written permission. - # - # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY - # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - **************************************************************************/ -#include "FullScreenPass.h" -#include "Core/API/RenderContext.h" - -namespace Falcor -{ - namespace - { - struct Vertex - { - float2 screenPos; - float2 texCoord; - }; - -#ifdef FALCOR_FLIP_Y -#define ADJUST_Y(a) (-(a)) -#else -#define ADJUST_Y(a) a -#endif - - const Vertex kVertices[] = - { - {float2(-1, ADJUST_Y(1)), float2(0, 0)}, - {float2(-1, ADJUST_Y(-1)), float2(0, 1)}, - {float2(1, ADJUST_Y(1)), float2(1, 0)}, - {float2(1, ADJUST_Y(-1)), float2(1, 1)}, - }; -#undef ADJUST_Y - - struct FullScreenPassData - { - Buffer::SharedPtr pVertexBuffer; - Vao::SharedPtr pVao; - uint64_t objectCount = 0; - - void init(Device* pDevice) - { - // First time we got here. create VB and VAO - const uint32_t vbSize = (uint32_t)(sizeof(Vertex)*std::size(kVertices)); - pVertexBuffer = Buffer::create(pDevice, 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{ pVertexBuffer }; - pVao = Vao::create(Vao::Topology::TriangleStrip, pLayout, buffers); - } - - void free() - { - pVertexBuffer.reset(); - pVao.reset(); - } - - static std::map& getCache() - { - static std::map data; - return data; - } - - static FullScreenPassData* acquire(Device* pDevice) - { - auto& cache = getCache(); - FullScreenPassData& data = cache[pDevice]; - if (data.objectCount == 0) - data.init(pDevice); - data.objectCount++; - return &data; - } - - static void release(Device* pDevice) - { - auto& cache = getCache(); - FullScreenPassData& data = cache[pDevice]; - FALCOR_ASSERT(data.objectCount > 0); - data.objectCount--; - if (data.objectCount == 0) - data.free(); - } - }; - } - - FullScreenPass::FullScreenPass(std::shared_ptr pDevice, const Program::Desc& progDesc, const Program::DefineList& programDefines) - : BaseGraphicsPass(std::move(pDevice), progDesc, programDefines) - { - // Create depth stencil state - FALCOR_ASSERT(mpState); - auto pDsState = DepthStencilState::create(DepthStencilState::Desc().setDepthEnabled(false)); - mpState->setDepthStencilState(pDsState); - - FullScreenPassData* data = FullScreenPassData::acquire(mpDevice.get()); - mpState->setVao(data->pVao); - } - - FullScreenPass::~FullScreenPass() - { - FullScreenPassData::release(mpDevice.get()); - } - - FullScreenPass::SharedPtr FullScreenPass::create(std::shared_ptr pDevice, 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)); - defs.add("_OUTPUT_VERTEX_COUNT", std::to_string(3 * popcount(viewportMask))); - d.addShaderLibrary("RenderGraph/BasePasses/FullScreenPass.gs.slang").gsEntry("main"); - } - if (!d.hasEntryPoint(ShaderType::Vertex)) d.addShaderLibrary("RenderGraph/BasePasses/FullScreenPass.vs.slang").vsEntry("main"); - - return SharedPtr(new FullScreenPass(std::move(pDevice), d, defs)); - } - - FullScreenPass::SharedPtr FullScreenPass::create(std::shared_ptr pDevice, const std::filesystem::path& path, const Program::DefineList& defines, uint32_t viewportMask) - { - Program::Desc desc; - desc.addShaderLibrary(path).psEntry("main"); - return create(std::move(pDevice), desc, defines, viewportMask); - } - - void FullScreenPass::execute(RenderContext* pRenderContext, const Fbo::SharedPtr& pFbo, bool autoSetVpSc) const - { - mpState->setFbo(pFbo, autoSetVpSc); - pRenderContext->draw(mpState.get(), mpVars.get(), (uint32_t)std::size(kVertices), 0); - } -} diff --git a/Source/Falcor/RenderGraph/BasePasses/FullScreenPass.h b/Source/Falcor/RenderGraph/BasePasses/FullScreenPass.h deleted file mode 100644 index 88729ca66..000000000 --- a/Source/Falcor/RenderGraph/BasePasses/FullScreenPass.h +++ /dev/null @@ -1,72 +0,0 @@ -/*************************************************************************** - # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. - # - # Redistribution and use in source and binary forms, with or without - # modification, are permitted provided that the following conditions - # are met: - # * Redistributions of source code must retain the above copyright - # notice, this list of conditions and the following disclaimer. - # * Redistributions in binary form must reproduce the above copyright - # notice, this list of conditions and the following disclaimer in the - # documentation and/or other materials provided with the distribution. - # * Neither the name of NVIDIA CORPORATION nor the names of its - # contributors may be used to endorse or promote products derived - # from this software without specific prior written permission. - # - # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY - # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - # (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 "Core/Macros.h" -#include "Core/Program/Program.h" -#include - -namespace Falcor -{ - class FALCOR_API FullScreenPass : public BaseGraphicsPass - { - public: - using SharedPtr = ParameterBlockSharedPtr; - - virtual ~FullScreenPass(); - - /** Create a new fullscreen pass from file. - \param[in] pDevice GPU device. - \param[in] path Pixel shader file path. This method expects a pixel shader named "main()" in the file. - \param[in] defines Optional list of macro definitions to set into the program. - \param[in] viewportMask Optional value to initialize viewport mask with. Useful for multi-projection passes. - \return A new object, or throws an exception if creation failed. - */ - static SharedPtr create(std::shared_ptr pDevice, const std::filesystem::path& path, const Program::DefineList& defines = Program::DefineList(), uint32_t viewportMask = 0); - - /** Create a new fullscreen pass. - \param[in] pDevice GPU device. - \param[in] desc The program description. - \param[in] defines Optional list of macro definitions to set into the program. - \param[in] viewportMask Optional value to initialize viewport mask with. Useful for multi-projection passes. - \return A new object, or throws an exception if creation failed. - */ - static SharedPtr create(std::shared_ptr pDevice, 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(std::shared_ptr pDevice, const Program::Desc& progDesc, const Program::DefineList& programDefines); - }; -} - diff --git a/Source/Falcor/RenderGraph/BasePasses/RasterPass.h b/Source/Falcor/RenderGraph/BasePasses/RasterPass.h deleted file mode 100644 index 3b7155813..000000000 --- a/Source/Falcor/RenderGraph/BasePasses/RasterPass.h +++ /dev/null @@ -1,76 +0,0 @@ -/*************************************************************************** - # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. - # - # Redistribution and use in source and binary forms, with or without - # modification, are permitted provided that the following conditions - # are met: - # * Redistributions of source code must retain the above copyright - # notice, this list of conditions and the following disclaimer. - # * Redistributions in binary form must reproduce the above copyright - # notice, this list of conditions and the following disclaimer in the - # documentation and/or other materials provided with the distribution. - # * Neither the name of NVIDIA CORPORATION nor the names of its - # contributors may be used to endorse or promote products derived - # from this software without specific prior written permission. - # - # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY - # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - # (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 "Core/Macros.h" -#include "Core/Program/Program.h" -#include -#include - -namespace Falcor -{ - class FALCOR_API RasterPass : public BaseGraphicsPass - { - public: - using SharedPtr = ParameterBlockSharedPtr; - - /** Create a new object. - \param[in] pDevice GPU devuce. - \param[in] path Program file path. - \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 Optional list of macro definitions to set into the program. The macro definitions will be set on all shader stages. - \return A new object, or throws an exception if creation failed. - */ - static SharedPtr create(std::shared_ptr pDevice, const std::filesystem::path& path, const std::string& vsEntry, const std::string& psEntry, const Program::DefineList& defines = Program::DefineList()); - - /** Create a new object. - \param[in] pDevice GPU devuce. - \param[in] progDesc The program description. - \param[in] programDefines Optional list of macro definitions to set into the program. The macro definitions will be set on all shader stages. - \return A new object, or throws an exception if creation failed. - */ - static SharedPtr create(std::shared_ptr pDevice, 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* pRenderContext, 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* pRenderContext, uint32_t indexCount, uint32_t startIndexLocation, int32_t baseVertexLocation); - - protected: - RasterPass(std::shared_ptr pDevice, const Program::Desc& progDesc, const Program::DefineList& programDefines); - }; -} diff --git a/Source/Falcor/RenderGraph/BasePasses/RasterScenePass.h b/Source/Falcor/RenderGraph/BasePasses/RasterScenePass.h deleted file mode 100644 index 08899060a..000000000 --- a/Source/Falcor/RenderGraph/BasePasses/RasterScenePass.h +++ /dev/null @@ -1,82 +0,0 @@ -/*************************************************************************** - # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. - # - # Redistribution and use in source and binary forms, with or without - # modification, are permitted provided that the following conditions - # are met: - # * Redistributions of source code must retain the above copyright - # notice, this list of conditions and the following disclaimer. - # * Redistributions in binary form must reproduce the above copyright - # notice, this list of conditions and the following disclaimer in the - # documentation and/or other materials provided with the distribution. - # * Neither the name of NVIDIA CORPORATION nor the names of its - # contributors may be used to endorse or promote products derived - # from this software without specific prior written permission. - # - # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY - # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - # (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 "Scene/Scene.h" -#include -#include -#include - -namespace Falcor -{ - class FALCOR_API RasterScenePass : public BaseGraphicsPass - { - public: - using SharedPtr = std::shared_ptr; - - /** Create a new object. - \param[in] pDevice GPU device. - \param[in] pScene The scene object. - \param[in] progDesc The program description. - \param[in] programDefines Optional list of macro definitions to set into the program. The macro definitions will be set on all shader stages. - \return A new object, or throws an exception if creation failed. - */ - static SharedPtr create(std::shared_ptr pDevice, const Scene::SharedPtr& pScene, const Program::Desc& progDesc, const Program::DefineList& programDefines = Program::DefineList()); - - /** Create a new object. - \param[in] pDevice GPU device. - \param[in] pScene The scene object - \param[in] path Program file path. - \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 Optional list of macro definitions to set into the program. The macro definitions will be set on all shader stages. - \return A new object, or throws an exception if creation failed. - */ - static SharedPtr create(std::shared_ptr pDevice, const Scene::SharedPtr& pScene, const std::filesystem::path& path, const std::string& vsEntry, const std::string& psEntry, const Program::DefineList& programDefines = Program::DefineList()); - - /** Render the scene into the dst FBO - */ - void renderScene(RenderContext* pRenderContext, 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(std::shared_ptr pDevice, const Scene::SharedPtr& pScene, const Program::Desc& progDesc, const Program::DefineList& programDefines); - Scene::SharedPtr mpScene; - }; -} - diff --git a/Source/Falcor/RenderGraph/RenderGraph.cpp b/Source/Falcor/RenderGraph/RenderGraph.cpp index 2b61e8196..429c3a212 100644 --- a/Source/Falcor/RenderGraph/RenderGraph.cpp +++ b/Source/Falcor/RenderGraph/RenderGraph.cpp @@ -29,6 +29,8 @@ #include "RenderGraphIR.h" #include "RenderGraphImportExport.h" #include "RenderGraphCompiler.h" +#include "GlobalState.h" +#include "Core/ObjectPython.h" #include "Core/API/Device.h" #include "Utils/Algorithm/DirectedGraphTraversal.h" #include "Utils/Scripting/Scripting.h" @@ -36,743 +38,816 @@ namespace Falcor { - const FileDialogFilterVec RenderGraph::kFileExtensionFilters = { { "py", "Render Graph Files"} }; +const FileDialogFilterVec RenderGraph::kFileExtensionFilters = {{"py", "Render Graph Files"}}; - RenderGraph::SharedPtr RenderGraph::create(std::shared_ptr pDevice, const std::string& name) - { - return SharedPtr(new RenderGraph(std::move(pDevice), name)); - } +RenderGraph::RenderGraph(ref pDevice, const std::string& name) : mpDevice(pDevice), mName(name) +{ + mpGraph = std::make_unique(); +} - RenderGraph::SharedPtr RenderGraph::createFromFile(std::shared_ptr pDevice, const std::filesystem::path& path) - { - using namespace pybind11::literals; +RenderGraph::~RenderGraph() {} - RenderGraph::SharedPtr pGraph; +ref RenderGraph::create(ref pDevice, const std::string& name) +{ + return ref(new RenderGraph(pDevice, name)); +} - // Setup a temporary scripting context that defines a local variable 'm' that - // has a 'addGraph' function exposed. This mimmicks the old Mogwai Python API - // allowing to load python based render graph scripts. - auto addGraph = pybind11::cpp_function([&pGraph] (RenderGraph::SharedPtr graph) { - pGraph = graph; - }); +ref RenderGraph::createFromFile(ref pDevice, const std::filesystem::path& path) +{ + using namespace pybind11::literals; - auto SimpleNamespace = pybind11::module_::import("types").attr("SimpleNamespace"); - pybind11::object m = SimpleNamespace("addGraph"_a = addGraph); + ref pGraph; - Scripting::Context ctx; - ctx.setObject("m", m); + // Setup a temporary scripting context that defines a local variable 'm' that + // has a 'addGraph' function exposed. This mimmicks the old Mogwai Python API + // allowing to load python based render graph scripts. + auto addGraph = pybind11::cpp_function([&pGraph](ref graph) { pGraph = graph; }); - Scripting::runScriptFromFile(path, ctx); + auto SimpleNamespace = pybind11::module_::import("types").attr("SimpleNamespace"); + pybind11::object m = SimpleNamespace("addGraph"_a = addGraph); - return pGraph; - } + Scripting::Context ctx; + ctx.setObject("m", m); - RenderGraph::RenderGraph(std::shared_ptr pDevice, const std::string& name) - : mpDevice(std::move(pDevice)) - , mName(name) - { - mpGraph = DirectedGraph::create(); - mpPassDictionary = InternalDictionary::create(); - } + ref pPrevDevice = getActivePythonRenderGraphDevice(); + setActivePythonRenderGraphDevice(pDevice); + Scripting::runScriptFromFile(path, ctx); + setActivePythonRenderGraphDevice(pPrevDevice); + + return pGraph; +} + +uint32_t RenderGraph::getPassIndex(const std::string& name) const +{ + auto it = mNameToIndex.find(name); + return (it == mNameToIndex.end()) ? kInvalidIndex : it->second; +} + +void RenderGraph::setScene(const ref& pScene) +{ + if (mpScene == pScene) + return; - RenderGraph::~RenderGraph() + // @skallweit: check that scene resides on the same GPU device + + mpScene = pScene; + for (auto& it : mNodeData) { + it.second.pPass->setScene(mpDevice->getRenderContext(), pScene); } + mRecompile = true; +} + +ref RenderGraph::createPass(const std::string& passName, const std::string& passType, const Dictionary& dict) +{ + ref pPass = RenderPass::create(passType, mpDevice, dict); + if (pPass) + addPass(pPass, passName); + return pPass; +} - uint32_t RenderGraph::getPassIndex(const std::string& name) const +uint32_t RenderGraph::addPass(const ref& pPass, const std::string& passName) +{ + FALCOR_ASSERT(pPass); + uint32_t passIndex = getPassIndex(passName); + if (passIndex != kInvalidIndex) { - auto it = mNameToIndex.find(name); - return (it == mNameToIndex.end()) ? kInvalidIndex : it->second; + reportError("Pass named '" + passName + "' already exists. Ignoring call"); + return kInvalidIndex; } - - void RenderGraph::setScene(const Scene::SharedPtr& pScene) + else { - if (mpScene == pScene) return; - - // @skallweit: check that scene resides on the same GPU device - - mpScene = pScene; - for (auto& it : mNodeData) - { - it.second.pPass->setScene(mpDevice->getRenderContext(), pScene); - } - mRecompile = true; + passIndex = mpGraph->addNode(); + mNameToIndex[passName] = passIndex; } - uint32_t RenderGraph::addPass(const RenderPass::SharedPtr& pPass, const std::string& passName) - { - FALCOR_ASSERT(pPass); - uint32_t passIndex = getPassIndex(passName); - if (passIndex != kInvalidIndex) - { - reportError("Pass named '" + passName + "' already exists. Ignoring call"); - return kInvalidIndex; - } - else - { - passIndex = mpGraph->addNode(); - mNameToIndex[passName] = passIndex; - } + pPass->mPassChangedCB = [this]() { mRecompile = true; }; + pPass->mName = passName; - pPass->mPassChangedCB = [this]() { mRecompile = true; }; - pPass->mName = passName; + if (mpScene) + pPass->setScene(mpDevice->getRenderContext(), mpScene); + mNodeData[passIndex] = {passName, pPass}; + mRecompile = true; + return passIndex; +} - if (mpScene) pPass->setScene(mpDevice->getRenderContext(), mpScene); - mNodeData[passIndex] = { passName, pPass }; - mRecompile = true; - return passIndex; +void RenderGraph::removePass(const std::string& name) +{ + uint32_t index = getPassIndex(name); + if (index == kInvalidIndex) + { + logWarning("Can't remove pass '{}'. Pass doesn't exist.", name); + return; } - void RenderGraph::removePass(const std::string& name) + // Unmark graph outputs that belong to this pass. + // Because the way std::vector works, we can't call unmarkOutput() immediately, so we store the outputs in a vector + std::vector outputsToDelete; + const std::string& outputPrefix = name + '.'; + for (auto& o : mOutputs) { - uint32_t index = getPassIndex(name); - if (index == kInvalidIndex) - { - logWarning("Can't remove pass '{}'. Pass doesn't exist.", name); - return; - } + if (o.nodeId == index) + outputsToDelete.push_back(outputPrefix + o.field); + } - // Unmark graph outputs that belong to this pass. - // Because the way std::vector works, we can't call unmarkOutput() immediately, so we store the outputs in a vector - std::vector outputsToDelete; - const std::string& outputPrefix = name + '.'; - for (auto& o : mOutputs) - { - if (o.nodeId == index) outputsToDelete.push_back(outputPrefix + o.field); - } + // Remove all the edges, indices and pass-data associated with this pass + for (const auto& outputName : outputsToDelete) + unmarkOutput(outputName); + mNameToIndex.erase(name); + mNodeData.erase(index); + const auto& removedEdges = mpGraph->removeNode(index); + for (const auto& e : removedEdges) + mEdgeData.erase(e); + mRecompile = true; +} - // Remove all the edges, indices and pass-data associated with this pass - for (const auto& name : outputsToDelete) unmarkOutput(name); - mNameToIndex.erase(name); - mNodeData.erase(index); - const auto& removedEdges = mpGraph->removeNode(index); - for (const auto& e : removedEdges) mEdgeData.erase(e); - mRecompile = true; - } +void RenderGraph::applyPassSettings(const std::string& passName, const Dictionary& dict) +{ + uint32_t index = getPassIndex(passName); + const auto pPassIt = mNodeData.find(index); - void RenderGraph::applyPassSettings(const std::string& passName, const Dictionary& dict) + if (pPassIt == mNodeData.end()) { - uint32_t index = getPassIndex(passName); - const auto pPassIt = mNodeData.find(index); + logError("Error in RenderGraph::updatePass(). Unable to find pass " + passName); + return; + } + auto pPass = pPassIt->second.pPass; - if (pPassIt == mNodeData.end()) - { - logError("Error in RenderGraph::updatePass(). Unable to find pass " + passName); - return; - } - auto pPass = pPassIt->second.pPass; + pPass->applySettings(dict); +} - pPass->applySettings(dict); - } +void RenderGraph::updatePass(const std::string& passName, const Dictionary& dict) +{ + uint32_t index = getPassIndex(passName); + const auto pPassIt = mNodeData.find(index); - void RenderGraph::updatePass(const std::string& passName, const Dictionary& dict) + if (pPassIt == mNodeData.end()) { - uint32_t index = getPassIndex(passName); - const auto pPassIt = mNodeData.find(index); - - if (pPassIt == mNodeData.end()) - { - reportError("Error in RenderGraph::updatePass(). Unable to find pass " + passName); - return; - } + reportError("Error in RenderGraph::updatePass(). Unable to find pass " + passName); + return; + } - // Recreate pass without changing graph using new dictionary - auto pOldPass = pPassIt->second.pPass; - std::string passTypeName = pOldPass->getType(); - auto pPass = RenderPass::create(passTypeName, mpDevice, dict); - pPassIt->second.pPass = pPass; - pPass->mPassChangedCB = [this]() { mRecompile = true; }; - pPass->mName = pOldPass->getName(); + // Recreate pass without changing graph using new dictionary + auto pOldPass = pPassIt->second.pPass; + std::string passTypeName = pOldPass->getType(); + auto pPass = RenderPass::create(passTypeName, mpDevice, dict); + pPassIt->second.pPass = pPass; + pPass->mPassChangedCB = [this]() { mRecompile = true; }; + pPass->mName = pOldPass->getName(); - if (mpScene) pPass->setScene(mpDevice->getRenderContext(), mpScene); - mRecompile = true; - } + if (mpScene) + pPass->setScene(mpDevice->getRenderContext(), mpScene); + mRecompile = true; +} - const RenderPass::SharedPtr& RenderGraph::getPass(const std::string& name) const +const ref& RenderGraph::getPass(const std::string& name) const +{ + uint32_t index = getPassIndex(name); + if (index == kInvalidIndex) { - uint32_t index = getPassIndex(name); - if (index == kInvalidIndex) - { - static const RenderPass::SharedPtr pNull; - reportError("RenderGraph::getRenderPass() - can't find a pass named '" + name + "'"); - return pNull; - } - return mNodeData.at(index).pPass; + static const ref pNull; + reportError("RenderGraph::getRenderPass() - can't find a pass named '" + name + "'"); + return pNull; } + return mNodeData.at(index).pPass; +} - using str_pair = std::pair; +using str_pair = std::pair; - static bool checkRenderPassIoExist(RenderPass* pPass, const std::string& name, const bool input, const RenderPass::CompileData& compileData) +static bool checkRenderPassIoExist(RenderPass* pPass, const std::string& name, const bool input, const RenderPass::CompileData& compileData) +{ + FALCOR_ASSERT(pPass); + RenderPassReflection reflect = pPass->reflect(compileData); + for (size_t i = 0; i < reflect.getFieldCount(); i++) { - FALCOR_ASSERT(pPass); - RenderPassReflection reflect = pPass->reflect(compileData); - for (size_t i = 0; i < reflect.getFieldCount(); i++) + const auto& f = *reflect.getField(i); + if (f.getName() == name) { - 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); - } + return input ? is_set(f.getVisibility(), RenderPassReflection::Field::Visibility::Input) + : is_set(f.getVisibility(), RenderPassReflection::Field::Visibility::Output); } - - return false; } - static str_pair parseFieldName(const std::string& fullname) - { - str_pair strPair; - if (std::count(fullname.begin(), fullname.end(), '.') == 0) - { - // No field name - strPair.first = fullname; - } - else - { - size_t dot = fullname.find_last_of('.'); - strPair.first = fullname.substr(0, dot); - strPair.second = fullname.substr(dot + 1); - } - return strPair; - } + return false; +} - RenderPass* RenderGraph::getRenderPassAndNamePair(const bool input, const std::string& fullname, const std::string& errorPrefix, std::pair& nameAndField) const +static str_pair parseFieldName(const std::string& fullname) +{ + str_pair strPair; + if (std::count(fullname.begin(), fullname.end(), '.') == 0) { - nameAndField = parseFieldName(fullname); - - RenderPass* pPass = getPass(nameAndField.first).get(); - if (!pPass) - { - reportError(errorPrefix + " - can't find render pass named '" + nameAndField.first + "'"); - return nullptr; - } - - RenderPass::CompileData compileData; - compileData.defaultTexDims = mCompilerDeps.defaultResourceProps.dims; - compileData.defaultTexFormat = mCompilerDeps.defaultResourceProps.format; - - if (nameAndField.second.size() && checkRenderPassIoExist(pPass, nameAndField.second, input, compileData) == false) - { - reportError(errorPrefix + "- can't find field named '" + nameAndField.second + "' in render pass '" + nameAndField.first + "'"); - return nullptr; - } - return pPass; + // No field name + strPair.first = fullname; } - - static bool checkMatchingEdgeTypes(const std::string& srcField, const std::string& dstField) + else { - if (srcField.empty() && dstField.empty()) return true; - if (dstField.size() && dstField.size()) return true; - return false; + size_t dot = fullname.find_last_of('.'); + strPair.first = fullname.substr(0, dot); + strPair.second = fullname.substr(dot + 1); } + return strPair; +} - uint32_t RenderGraph::addEdge(const std::string& src, const std::string& dst) - { - EdgeData newEdge; - str_pair srcPair, dstPair; - const auto& pSrc = getRenderPassAndNamePair(false, src, "Invalid src string in RenderGraph::addEdge()", srcPair); - const auto& pDst = getRenderPassAndNamePair(true, dst, "Invalid dst string in RenderGraph::addEdge()", dstPair); - newEdge.srcField = srcPair.second; - newEdge.dstField = dstPair.second; +RenderPass* RenderGraph::getRenderPassAndNamePair( + const bool input, + const std::string& fullname, + const std::string& errorPrefix, + std::pair& nameAndField +) const +{ + nameAndField = parseFieldName(fullname); - if (pSrc == nullptr || pDst == nullptr) return kInvalidIndex; - if (checkMatchingEdgeTypes(newEdge.srcField, newEdge.dstField) == false) - { - reportError("RenderGraph::addEdge() - can't add the edge [" + src + ", " + dst + "]. One of the nodes is a resource while the other is a pass. Can't tell if you want a data-dependency or an execution-dependency"); - return kInvalidIndex; - } + RenderPass* pPass = getPass(nameAndField.first).get(); + if (!pPass) + { + reportError(errorPrefix + " - can't find render pass named '" + nameAndField.first + "'"); + return nullptr; + } - uint32_t srcIndex = mNameToIndex[srcPair.first]; - uint32_t dstIndex = mNameToIndex[dstPair.first]; + RenderPass::CompileData compileData; + compileData.defaultTexDims = mCompilerDeps.defaultResourceProps.dims; + compileData.defaultTexFormat = mCompilerDeps.defaultResourceProps.format; - // If this is a data edge, check that the dst field is not already initialized - if(newEdge.dstField.size()) - { - const DirectedGraph::Node* pNode = mpGraph->getNode(dstIndex); + if (nameAndField.second.size() && checkRenderPassIoExist(pPass, nameAndField.second, input, compileData) == false) + { + reportError(errorPrefix + "- can't find field named '" + nameAndField.second + "' in render pass '" + nameAndField.first + "'"); + return nullptr; + } + return pPass; +} - for (uint32_t e = 0; e < pNode->getIncomingEdgeCount(); e++) - { - uint32_t incomingEdgeId = pNode->getIncomingEdge(e); - const auto& edgeData = mEdgeData[incomingEdgeId]; - - if (edgeData.dstField == newEdge.dstField) - { - reportError("RenderGraph::addEdge() - destination '" + dst + "' is already initialized. Please remove the existing connection before trying to add an edge"); - return kInvalidIndex; - } - } - } +static bool checkMatchingEdgeTypes(const std::string& srcField, const std::string& dstField) +{ + if (srcField.empty() && dstField.empty()) + return true; + if (dstField.size() && dstField.size()) + return true; + return false; +} - // Make sure that this doesn't create a cycle - if (DirectedGraphPathDetector::hasPath(mpGraph, dstIndex, srcIndex)) - { - reportError("RenderGraph::addEdge() - can't add the edge [" + src + ", " + dst + "]. The edge will create a cycle in the graph which is not allowed"); - return kInvalidIndex; - } +uint32_t RenderGraph::addEdge(const std::string& src, const std::string& dst) +{ + EdgeData newEdge; + str_pair srcPair, dstPair; + const auto& pSrc = getRenderPassAndNamePair(false, src, "Invalid src string in RenderGraph::addEdge()", srcPair); + const auto& pDst = getRenderPassAndNamePair(true, dst, "Invalid dst string in RenderGraph::addEdge()", dstPair); + newEdge.srcField = srcPair.second; + newEdge.dstField = dstPair.second; - uint32_t e = mpGraph->addEdge(srcIndex, dstIndex); - mEdgeData[e] = newEdge; - mRecompile = true; - return e; + if (pSrc == nullptr || pDst == nullptr) + return kInvalidIndex; + if (checkMatchingEdgeTypes(newEdge.srcField, newEdge.dstField) == false) + { + reportError( + "RenderGraph::addEdge() - can't add the edge [" + src + ", " + dst + + "]. One of the nodes is a resource while the other is a pass. Can't tell if you want a data-dependency or an " + "execution-dependency" + ); + return kInvalidIndex; } - void RenderGraph::removeEdge(const std::string& src, const std::string& dst) + uint32_t srcIndex = mNameToIndex[srcPair.first]; + uint32_t dstIndex = mNameToIndex[dstPair.first]; + + // If this is a data edge, check that the dst field is not already initialized + if (newEdge.dstField.size()) { - str_pair srcPair, dstPair; - const auto& pSrc = getRenderPassAndNamePair(false, src, "Invalid src string in RenderGraph::addEdge()", srcPair); - const auto& pDst = getRenderPassAndNamePair(true, dst, "Invalid dst string in RenderGraph::addEdge()", dstPair); + const DirectedGraph::Node* pNode = mpGraph->getNode(dstIndex); - if (pSrc == nullptr || pDst == nullptr) + for (uint32_t e = 0; e < pNode->getIncomingEdgeCount(); e++) { - reportError("Unable to remove edge. Input or output node not found."); - return; - } - - uint32_t srcIndex = mNameToIndex[srcPair.first]; + uint32_t incomingEdgeId = pNode->getIncomingEdge(e); + const auto& edgeData = mEdgeData[incomingEdgeId]; - const DirectedGraph::Node* pSrcNode = mpGraph->getNode(srcIndex); - - for (uint32_t i = 0; i < pSrcNode->getOutgoingEdgeCount(); ++i) - { - uint32_t edgeID = pSrcNode->getOutgoingEdge(i); - if (mEdgeData[edgeID].srcField == srcPair.second) + if (edgeData.dstField == newEdge.dstField) { - if (mEdgeData[edgeID].dstField == dstPair.second) - { - removeEdge(edgeID); - return; - } + reportError( + "RenderGraph::addEdge() - destination '" + dst + + "' is already initialized. Please remove the existing connection before trying to add an edge" + ); + return kInvalidIndex; } } } - void RenderGraph::removeEdge(uint32_t edgeID) + // Make sure that this doesn't create a cycle + if (DirectedGraphPathDetector::hasPath(*mpGraph, dstIndex, srcIndex)) { - if (mEdgeData.find(edgeID) == mEdgeData.end()) - { - reportError("Can't remove edge with index " + std::to_string(edgeID) + ". The edge doesn't exist"); - return; - } - mEdgeData.erase(edgeID); - mpGraph->removeEdge(edgeID); - mRecompile = true; + reportError( + "RenderGraph::addEdge() - can't add the edge [" + src + ", " + dst + + "]. The edge will create a cycle in the graph which is not allowed" + ); + return kInvalidIndex; } - uint32_t RenderGraph::getEdge(const std::string& src, const std::string& dst) - { - str_pair srcPair = parseFieldName(src); - str_pair dstPair = parseFieldName(dst); - - for (uint32_t i = 0; i < mpGraph->getCurrentEdgeId(); ++i) - { - if (!mpGraph->doesEdgeExist(i)) { continue; } - - const DirectedGraph::Edge* pEdge = mpGraph->getEdge(i); - 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; - } - } + uint32_t e = mpGraph->addEdge(srcIndex, dstIndex); + mEdgeData[e] = newEdge; + mRecompile = true; + return e; +} - return static_cast(-1); - } +void RenderGraph::removeEdge(const std::string& src, const std::string& dst) +{ + str_pair srcPair, dstPair; + const auto& pSrc = getRenderPassAndNamePair(false, src, "Invalid src string in RenderGraph::addEdge()", srcPair); + const auto& pDst = getRenderPassAndNamePair(true, dst, "Invalid dst string in RenderGraph::addEdge()", dstPair); - bool RenderGraph::isGraphOutput(const GraphOut& graphOut) const + if (pSrc == nullptr || pDst == nullptr) { - for (const GraphOut& currentOut : mOutputs) - { - if (graphOut == currentOut) return true; - } - - return false; + reportError("Unable to remove edge. Input or output node not found."); + return; } - std::vector RenderGraph::getAvailableOutputs() const - { - std::vector outputs; + uint32_t srcIndex = mNameToIndex[srcPair.first]; - RenderPass::CompileData compileData; - compileData.defaultTexDims = mCompilerDeps.defaultResourceProps.dims; - compileData.defaultTexFormat = mCompilerDeps.defaultResourceProps.format; + const DirectedGraph::Node* pSrcNode = mpGraph->getNode(srcIndex); - for (const auto& node : mNodeData) + for (uint32_t i = 0; i < pSrcNode->getOutgoingEdgeCount(); ++i) + { + uint32_t edgeID = pSrcNode->getOutgoingEdge(i); + if (mEdgeData[edgeID].srcField == srcPair.second) { - RenderPassReflection reflection = node.second.pPass->reflect(compileData); - for (size_t i = 0; i < reflection.getFieldCount(); i++) + if (mEdgeData[edgeID].dstField == dstPair.second) { - const auto& f = *reflection.getField(i); - if(is_set(f.getVisibility(), RenderPassReflection::Field::Visibility::Output)) outputs.push_back(node.second.name + "." + f.getName()); + removeEdge(edgeID); + return; } } - return outputs; } +} - std::vector RenderGraph::getUnmarkedOutputs() const +void RenderGraph::removeEdge(uint32_t edgeID) +{ + if (mEdgeData.find(edgeID) == mEdgeData.end()) { - std::vector outputs; - - for (const auto& output : getAvailableOutputs()) - { - if (!isGraphOutput(output)) outputs.push_back(output); - } - - return outputs; + reportError("Can't remove edge with index " + std::to_string(edgeID) + ". The edge doesn't exist"); + return; } + mEdgeData.erase(edgeID); + mpGraph->removeEdge(edgeID); + mRecompile = true; +} - bool RenderGraph::compile(RenderContext* pRenderContext, std::string& log) - { - if (!mRecompile) return true; - mpExe = nullptr; +uint32_t RenderGraph::getEdge(const std::string& src, const std::string& dst) +{ + str_pair srcPair = parseFieldName(src); + str_pair dstPair = parseFieldName(dst); - try + for (uint32_t i = 0; i < mpGraph->getCurrentEdgeId(); ++i) + { + if (!mpGraph->doesEdgeExist(i)) { - mpExe = RenderGraphCompiler::compile(*this, pRenderContext, mCompilerDeps); - mRecompile = false; - return true; + continue; } - catch (const std::exception& e) + + const DirectedGraph::Edge* pEdge = mpGraph->getEdge(i); + if (dstPair.first == mNodeData[pEdge->getDestNode()].name && srcPair.first == mNodeData[pEdge->getSourceNode()].name) { - log = e.what(); - return false; + if (mEdgeData[i].dstField == dstPair.second && mEdgeData[i].srcField == srcPair.second) + return i; } } - void RenderGraph::execute(RenderContext* pRenderContext) - { - std::string log; - if (!compile(pRenderContext, log)) - { - reportError("Failed to compile RenderGraph\n" + log + "Ignoring RenderGraph::execute() call"); - return; - } + return static_cast(-1); +} - FALCOR_ASSERT(mpExe); - RenderGraphExe::Context c; - c.pGraphDictionary = mpPassDictionary; - c.pRenderContext = pRenderContext; - c.defaultTexDims = mCompilerDeps.defaultResourceProps.dims; - c.defaultTexFormat = mCompilerDeps.defaultResourceProps.format; - mpExe->execute(c); +bool RenderGraph::isGraphOutput(const GraphOut& graphOut) const +{ + for (const GraphOut& currentOut : mOutputs) + { + if (graphOut == currentOut) + return true; } - void RenderGraph::update(const SharedPtr& pGraph) + return false; +} + +std::vector RenderGraph::getAvailableOutputs() const +{ + std::vector outputs; + + RenderPass::CompileData compileData; + compileData.defaultTexDims = mCompilerDeps.defaultResourceProps.dims; + compileData.defaultTexFormat = mCompilerDeps.defaultResourceProps.format; + + for (const auto& node : mNodeData) { - // Fill in missing passes from referenced graph. - for (const auto& nameIndexPair : pGraph->mNameToIndex) + RenderPassReflection reflection = node.second.pPass->reflect(compileData); + for (size_t i = 0; i < reflection.getFieldCount(); i++) { - RenderPass::SharedPtr pRenderPass = pGraph->mNodeData[nameIndexPair.second].pPass; - if (!doesPassExist(nameIndexPair.first)) addPass(pRenderPass, nameIndexPair.first); + 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; +} - // Remove nodes that should no longer be within the graph. - std::vector passesToRemove; +std::vector RenderGraph::getUnmarkedOutputs() const +{ + std::vector outputs; - for (const auto& nameIndexPair : mNameToIndex) - { - if (!pGraph->doesPassExist(nameIndexPair.first)) - { - passesToRemove.push_back(nameIndexPair.first); - } - } + for (const auto& output : getAvailableOutputs()) + { + if (!isGraphOutput(output)) + outputs.push_back(output); + } - for (const std::string& passName : passesToRemove) - { - removePass(passName); - } + return outputs; +} - // Remove all edges from this graph. - for (uint32_t i = 0; i < mpGraph->getCurrentEdgeId(); ++i) - { - if (!mpGraph->doesEdgeExist(i)) { continue; } +bool RenderGraph::compile(RenderContext* pRenderContext, std::string& log) +{ + if (!mRecompile) + return true; + mpExe = nullptr; - mpGraph->removeEdge(i); - } - mEdgeData.clear(); + try + { + mpExe = RenderGraphCompiler::compile(*this, pRenderContext, mCompilerDeps); + mRecompile = false; + return true; + } + catch (const std::exception& e) + { + log = e.what(); + return false; + } +} - // Add all edges from the other graph. - for (uint32_t i = 0; i < pGraph->mpGraph->getCurrentEdgeId(); ++i) - { - if (!pGraph->mpGraph->doesEdgeExist(i)) { continue; } +void RenderGraph::execute(RenderContext* pRenderContext) +{ + std::string log; + if (!compile(pRenderContext, log)) + { + reportError("Failed to compile RenderGraph\n" + log + "Ignoring RenderGraph::execute() call"); + return; + } - const DirectedGraph::Edge* pEdge = pGraph->mpGraph->getEdge(i); - std::string dst = pGraph->mNodeData.find(pEdge->getDestNode())->second.name; - std::string src = pGraph->mNodeData.find(pEdge->getSourceNode())->second.name; + FALCOR_ASSERT(mpExe); + RenderGraphExe::Context c{ + pRenderContext, mPassesDictionary, mCompilerDeps.defaultResourceProps.dims, mCompilerDeps.defaultResourceProps.format}; + mpExe->execute(c); +} - if ((mNameToIndex.find(src) != mNameToIndex.end()) && (mNameToIndex.find(dst) != mNameToIndex.end())) - { +void RenderGraph::update(const ref& pGraph) +{ + // Fill in missing passes from referenced graph. + for (const auto& nameIndexPair : pGraph->mNameToIndex) + { + ref pRenderPass = pGraph->mNodeData[nameIndexPair.second].pPass; + if (!doesPassExist(nameIndexPair.first)) + addPass(pRenderPass, nameIndexPair.first); + } - if (pGraph->mEdgeData[i].dstField.size()) dst += std::string(".") + pGraph->mEdgeData[i].dstField; - if (pGraph->mEdgeData[i].srcField.size()) src += std::string(".") + pGraph->mEdgeData[i].srcField; - addEdge(src, dst); - } - } + // Remove nodes that should no longer be within the graph. + std::vector passesToRemove; - // Mark all unmarked outputs from referenced graph. - for (uint32_t i = 0; i < pGraph->getOutputCount(); ++i) + for (const auto& nameIndexPair : mNameToIndex) + { + if (!pGraph->doesPassExist(nameIndexPair.first)) { - auto name = pGraph->getOutputName(i); - for (auto mask : pGraph->getOutputMasks(i)) - { - markOutput(name, mask); - } + passesToRemove.push_back(nameIndexPair.first); } } - void RenderGraph::setInput(const std::string& name, const Resource::SharedPtr& pResource) + for (const std::string& passName : passesToRemove) { - str_pair strPair; - RenderPass* pPass = getRenderPassAndNamePair(true, name, "RenderGraph::setInput()", strPair); - if (pPass == nullptr) return; + removePass(passName); + } - if (pResource) - { - mCompilerDeps.externalResources[name] = pResource; - } - else + // Remove all edges from this graph. + for (uint32_t i = 0; i < mpGraph->getCurrentEdgeId(); ++i) + { + if (!mpGraph->doesEdgeExist(i)) { - if (mCompilerDeps.externalResources.find(name) == mCompilerDeps.externalResources.end()) - { - logWarning("RenderGraph::setInput() - Trying to remove an external resource named '{}' but the resource wasn't registered before. Ignoring call.", name); - return; - } - mCompilerDeps.externalResources.erase(name); + continue; } - if (mpExe) mpExe->setInput(name, pResource); + mpGraph->removeEdge(i); } + mEdgeData.clear(); - void RenderGraph::markOutput(const std::string& name, TextureChannelFlags mask) + // Add all edges from the other graph. + for (uint32_t i = 0; i < pGraph->mpGraph->getCurrentEdgeId(); ++i) { - if (mask == TextureChannelFlags::None) throw RuntimeError("RenderGraph::markOutput() mask must be non-empty"); - - // Recursive call to handle '*' wildcard. - if (name == "*") + if (!pGraph->mpGraph->doesEdgeExist(i)) { - auto outputs = getAvailableOutputs(); - for (const auto& o : outputs) markOutput(o, mask); - return; + continue; } - str_pair strPair; - const auto& pPass = getRenderPassAndNamePair(false, name, "RenderGraph::markOutput()", strPair); - if (pPass == nullptr) return; - - GraphOut newOut; - newOut.field = strPair.second; - newOut.nodeId = mNameToIndex[strPair.first]; + const DirectedGraph::Edge* pEdge = pGraph->mpGraph->getEdge(i); + std::string dst = pGraph->mNodeData.find(pEdge->getDestNode())->second.name; + std::string src = pGraph->mNodeData.find(pEdge->getSourceNode())->second.name; - // Check if output is already marked. - // If it is, add the mask to its set of generated masks. - auto it = std::find(mOutputs.begin(), mOutputs.end(), newOut); - if (it != mOutputs.end()) - { - it->masks.insert(mask); - // No recompile necessary as output is already generated. - } - else + if ((mNameToIndex.find(src) != mNameToIndex.end()) && (mNameToIndex.find(dst) != mNameToIndex.end())) { - newOut.masks.insert(mask); - mOutputs.push_back(newOut); - mRecompile = true; + if (pGraph->mEdgeData[i].dstField.size()) + dst += std::string(".") + pGraph->mEdgeData[i].dstField; + if (pGraph->mEdgeData[i].srcField.size()) + src += std::string(".") + pGraph->mEdgeData[i].srcField; + addEdge(src, dst); } } - void RenderGraph::unmarkOutput(const std::string& name) + // Mark all unmarked outputs from referenced graph. + for (uint32_t i = 0; i < pGraph->getOutputCount(); ++i) { - str_pair strPair; - const auto& pPass = getRenderPassAndNamePair(false, name, "RenderGraph::unmarkOutput()", strPair); - if (pPass == nullptr) return; - - GraphOut removeMe; - removeMe.field = strPair.second; - removeMe.nodeId = mNameToIndex[strPair.first]; - - auto it = std::find(mOutputs.begin(), mOutputs.end(), removeMe); - if (it != mOutputs.end()) + auto name = pGraph->getOutputName(i); + for (auto mask : pGraph->getOutputMasks(i)) { - mOutputs.erase(it); - mRecompile = true; + markOutput(name, mask); } } +} + +void RenderGraph::setInput(const std::string& name, const ref& pResource) +{ + str_pair strPair; + RenderPass* pPass = getRenderPassAndNamePair(true, name, "RenderGraph::setInput()", strPair); + if (pPass == nullptr) + return; - bool RenderGraph::isGraphOutput(const std::string& name) const + if (pResource) { - str_pair strPair; - const auto& pPass = getRenderPassAndNamePair(false, name, "RenderGraph::isGraphOutput()", strPair); - if (pPass == nullptr) return false; - uint32_t passIndex = getPassIndex(strPair.first); - GraphOut thisOutput = { passIndex, strPair.second }; - return isGraphOutput(thisOutput); + mCompilerDeps.externalResources[name] = pResource; } - - Resource::SharedPtr RenderGraph::getOutput(const std::string& name) + else { - if (mRecompile) + if (mCompilerDeps.externalResources.find(name) == mCompilerDeps.externalResources.end()) { - reportError("RenderGraph::getOutput() - can't fetch an output resource because the graph wasn't successfuly compiled yet"); - return nullptr; + logWarning( + "RenderGraph::setInput() - Trying to remove an external resource named '{}' but the resource wasn't registered before. " + "Ignoring call.", + name + ); + return; } + mCompilerDeps.externalResources.erase(name); + } - str_pair strPair; - RenderPass* pPass = getRenderPassAndNamePair(false, name, "RenderGraph::getOutput()", strPair); - if (!pPass) return nullptr; - - uint32_t passIndex = getPassIndex(strPair.first); - GraphOut thisOutput = { passIndex, strPair.second }; - bool isOutput = isGraphOutput(thisOutput); - if (!isOutput) - { - reportError("RenderGraph::getOutput() - can't fetch the output '" + name + "'. The resource is wasn't marked as an output"); - return nullptr; - } + if (mpExe) + mpExe->setInput(name, pResource); +} - return mpExe->getResource(name); - } +void RenderGraph::markOutput(const std::string& name, TextureChannelFlags mask) +{ + if (mask == TextureChannelFlags::None) + throw RuntimeError("RenderGraph::markOutput() mask must be non-empty"); - Resource::SharedPtr RenderGraph::getOutput(uint32_t index) + // Recursive call to handle '*' wildcard. + if (name == "*") { - auto name = getOutputName(index); - return getOutput(name); + auto outputs = getAvailableOutputs(); + for (const auto& o : outputs) + markOutput(o, mask); + return; } - std::string RenderGraph::getOutputName(size_t index) const - { - FALCOR_ASSERT(index < mOutputs.size()); - const GraphOut& graphOut = mOutputs[index]; - return mNodeData.find(graphOut.nodeId)->second.name + "." + graphOut.field; - } + str_pair strPair; + const auto& pPass = getRenderPassAndNamePair(false, name, "RenderGraph::markOutput()", strPair); + if (pPass == nullptr) + return; - std::unordered_set RenderGraph::getOutputMasks(size_t index) const + GraphOut newOut; + newOut.field = strPair.second; + newOut.nodeId = mNameToIndex[strPair.first]; + + // Check if output is already marked. + // If it is, add the mask to its set of generated masks. + auto it = std::find(mOutputs.begin(), mOutputs.end(), newOut); + if (it != mOutputs.end()) { - FALCOR_ASSERT(index < mOutputs.size()); - return mOutputs[index].masks; + it->masks.insert(mask); + // No recompile necessary as output is already generated. } - - void RenderGraph::onResize(const Fbo* pTargetFbo) + else { - // Store the back-buffer values - const Texture* pColor = pTargetFbo ? pTargetFbo->getColorTexture(0).get() : nullptr; - if (pColor == nullptr) throw RuntimeError("Can't resize render graph without a frame buffer."); - - // Store the values - 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 + newOut.masks.insert(mask); + mOutputs.push_back(newOut); mRecompile = true; } +} - bool canFieldsConnect(const RenderPassReflection::Field& src, const RenderPassReflection::Field& dst) - { - FALCOR_ASSERT(is_set(src.getVisibility(), RenderPassReflection::Field::Visibility::Output) && is_set(dst.getVisibility(), RenderPassReflection::Field::Visibility::Input)); +void RenderGraph::unmarkOutput(const std::string& name) +{ + str_pair strPair; + const auto& pPass = getRenderPassAndNamePair(false, name, "RenderGraph::unmarkOutput()", strPair); + if (pPass == nullptr) + return; - return src.getName() == dst.getName() && - (dst.getWidth() == 0 || src.getWidth() == dst.getWidth()) && - (dst.getHeight() == 0 || src.getHeight() == dst.getHeight()) && - (dst.getDepth() == 0 || src.getDepth() == dst.getDepth()) && - (dst.getFormat() == ResourceFormat::Unknown || src.getFormat() == dst.getFormat()) && - src.getSampleCount() == dst.getSampleCount() && // TODO: allow dst sample count to be 1 when auto MSAA resolve is implemented in graph compilation - src.getType() == dst.getType() && - src.getSampleCount() == dst.getSampleCount(); - } + GraphOut removeMe; + removeMe.field = strPair.second; + removeMe.nodeId = mNameToIndex[strPair.first]; - void RenderGraph::renderUI(RenderContext* pRenderContext, Gui::Widgets& widget) + auto it = std::find(mOutputs.begin(), mOutputs.end(), removeMe); + if (it != mOutputs.end()) { - if (mpExe) mpExe->renderUI(pRenderContext, widget); + mOutputs.erase(it); + mRecompile = true; } +} - void RenderGraph::onSceneUpdates(RenderContext* pRenderContext, Scene::UpdateFlags sceneUpdates) - { - // Notify all passes in graph about scene updates. - // Note we don't rely on `mpExe` here because it is not created until the graph is compiled in `execute()`. - for (auto& it : mNodeData) - { - it.second.pPass->onSceneUpdates(pRenderContext, sceneUpdates); - } - } +bool RenderGraph::isGraphOutput(const std::string& name) const +{ + str_pair strPair; + const auto& pPass = getRenderPassAndNamePair(false, name, "RenderGraph::isGraphOutput()", strPair); + if (pPass == nullptr) + return false; + uint32_t passIndex = getPassIndex(strPair.first); + GraphOut thisOutput = {passIndex, strPair.second}; + return isGraphOutput(thisOutput); +} - bool RenderGraph::onMouseEvent(const MouseEvent& mouseEvent) +ref RenderGraph::getOutput(const std::string& name) +{ + if (mRecompile) { - return mpExe ? mpExe->onMouseEvent(mouseEvent) : false; + reportError("RenderGraph::getOutput() - can't fetch an output resource because the graph wasn't successfuly compiled yet"); + return nullptr; } - bool RenderGraph::onKeyEvent(const KeyboardEvent& keyEvent) - { - return mpExe ? mpExe->onKeyEvent(keyEvent) : false; - } + str_pair strPair; + RenderPass* pPass = getRenderPassAndNamePair(false, name, "RenderGraph::getOutput()", strPair); + if (!pPass) + return nullptr; - void RenderGraph::onHotReload(HotReloadFlags reloaded) + uint32_t passIndex = getPassIndex(strPair.first); + GraphOut thisOutput = {passIndex, strPair.second}; + bool isOutput = isGraphOutput(thisOutput); + if (!isOutput) { - if (mpExe) mpExe->onHotReload(reloaded); + reportError("RenderGraph::getOutput() - can't fetch the output '" + name + "'. The resource is wasn't marked as an output"); + return nullptr; } - FALCOR_SCRIPT_BINDING(RenderGraph) - { - using namespace pybind11::literals; + return mpExe->getResource(name); +} + +ref RenderGraph::getOutput(uint32_t index) +{ + auto name = getOutputName(index); + return getOutput(name); +} - FALCOR_SCRIPT_BINDING_DEPENDENCY(Formats); +std::string RenderGraph::getOutputName(size_t index) const +{ + FALCOR_ASSERT(index < mOutputs.size()); + const GraphOut& graphOut = mOutputs[index]; + return mNodeData.find(graphOut.nodeId)->second.name + "." + graphOut.field; +} - pybind11::class_ renderGraph(m, "RenderGraph"); +std::unordered_set RenderGraph::getOutputMasks(size_t index) const +{ + FALCOR_ASSERT(index < mOutputs.size()); + return mOutputs[index].masks; +} - auto create = [] (const std::string& name) - { - auto pDevice = getGlobalDevice(); - if (!pDevice) - throw RuntimeError("Can't construct RenderGraph - GPU device is not initialized"); - return RenderGraph::create(pDevice, name); - }; - renderGraph.def(pybind11::init(create), "name"_a); // PYTHONDEPRECATED - auto createFromFile = [] (const std::filesystem::path& path) - { - auto pDevice = getGlobalDevice(); - if (!pDevice) - throw RuntimeError("Can't construct RenderGraph - GPU device is not initialized"); - return RenderGraph::createFromFile(pDevice, path); - }; - renderGraph.def_static("createFromFile", createFromFile, "path"_a); // PYTHONDEPRECATED - renderGraph.def_property("name", &RenderGraph::getName, &RenderGraph::setName); - renderGraph.def(RenderGraphIR::kAddPass, &RenderGraph::addPass, "pass_"_a, "name"_a); - renderGraph.def(RenderGraphIR::kRemovePass, &RenderGraph::removePass, "name"_a); - renderGraph.def(RenderGraphIR::kAddEdge, &RenderGraph::addEdge, "src"_a, "dst"_a); - renderGraph.def(RenderGraphIR::kRemoveEdge, pybind11::overload_cast(&RenderGraph::removeEdge), "src"_a, "dst"_a); - renderGraph.def(RenderGraphIR::kMarkOutput, &RenderGraph::markOutput, "name"_a, "mask"_a = TextureChannelFlags::RGB); - renderGraph.def(RenderGraphIR::kUnmarkOutput, &RenderGraph::unmarkOutput, "name"_a); - renderGraph.def("getPass", &RenderGraph::getPass, "name"_a); - renderGraph.def("getOutput", pybind11::overload_cast(&RenderGraph::getOutput), "name"_a); - auto printGraph = [](RenderGraph::SharedPtr pGraph) { pybind11::print(RenderGraphExporter::getIR(pGraph)); }; - renderGraph.def("print", printGraph); - - // RenderPass - pybind11::class_ renderPass(m, "RenderPass"); - renderPass.def_property_readonly("name", &RenderPass::getName); - renderPass.def_property_readonly("type", &RenderPass::getType); - renderPass.def_property_readonly("desc", &RenderPass::getDesc); - auto getDictionary = [](RenderPass::SharedPtr pPass) { return pPass->getScriptingDictionary().toPython(); }; - renderPass.def("getDictionary", getDictionary); - - // RenderPassLibrary - const auto& createRenderPass = [](const std::string& type, pybind11::dict d = {}) - { - std::shared_ptr pDevice = getGlobalDevice(); - if (!pDevice) - throw RuntimeError("Can't create render pass without a device being initialized."); - auto pPass = RenderPass::create(type, pDevice, Dictionary(d)); - if (!pPass) - throw RuntimeError("Can't create a render pass of type '{}'. Make sure the required plugin library was loaded.", type); - return pPass; - }; - m.def("createPass", createRenderPass, "type"_a, "dict"_a = pybind11::dict()); // PYTHONDEPRECATED - - const auto& loadPassLibrary = [](const std::string& library) - { - PluginManager::instance().loadPluginByName(library); - }; - m.def(RenderGraphIR::kLoadPassLibrary, loadPassLibrary, "name"_a); // PYTHONDEPRECATED +void RenderGraph::onResize(const Fbo* pTargetFbo) +{ + // Store the back-buffer values + const Texture* pColor = pTargetFbo ? pTargetFbo->getColorTexture(0).get() : nullptr; + if (pColor == nullptr) + throw RuntimeError("Can't resize render graph without a frame buffer."); - const auto& updateRenderPass = [](const RenderGraph::SharedPtr& pGraph, const std::string& passName, pybind11::dict d) - { - pGraph->updatePass(passName, Dictionary(d)); - }; - renderGraph.def(RenderGraphIR::kUpdatePass, updateRenderPass, "name"_a, "dict"_a); + // Store the values + 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; +} + +bool canFieldsConnect(const RenderPassReflection::Field& src, const RenderPassReflection::Field& dst) +{ + FALCOR_ASSERT( + is_set(src.getVisibility(), RenderPassReflection::Field::Visibility::Output) && + is_set(dst.getVisibility(), RenderPassReflection::Field::Visibility::Input) + ); + + return src.getName() == dst.getName() && (dst.getWidth() == 0 || src.getWidth() == dst.getWidth()) && + (dst.getHeight() == 0 || src.getHeight() == dst.getHeight()) && (dst.getDepth() == 0 || src.getDepth() == dst.getDepth()) && + (dst.getFormat() == ResourceFormat::Unknown || src.getFormat() == dst.getFormat()) && + src.getSampleCount() == dst.getSampleCount() && // TODO: allow dst sample count to be 1 when auto MSAA resolve is implemented in + // graph compilation + src.getType() == dst.getType() && src.getSampleCount() == dst.getSampleCount(); +} + +void RenderGraph::renderUI(RenderContext* pRenderContext, Gui::Widgets& widget) +{ + if (mpExe) + mpExe->renderUI(pRenderContext, widget); +} + +void RenderGraph::onSceneUpdates(RenderContext* pRenderContext, Scene::UpdateFlags sceneUpdates) +{ + // Notify all passes in graph about scene updates. + // Note we don't rely on `mpExe` here because it is not created until the graph is compiled in `execute()`. + for (auto& it : mNodeData) + { + it.second.pPass->onSceneUpdates(pRenderContext, sceneUpdates); } } + +bool RenderGraph::onMouseEvent(const MouseEvent& mouseEvent) +{ + return mpExe ? mpExe->onMouseEvent(mouseEvent) : false; +} + +bool RenderGraph::onKeyEvent(const KeyboardEvent& keyEvent) +{ + return mpExe ? mpExe->onKeyEvent(keyEvent) : false; +} + +void RenderGraph::onHotReload(HotReloadFlags reloaded) +{ + if (mpExe) + mpExe->onHotReload(reloaded); +} + +FALCOR_SCRIPT_BINDING(RenderGraph) +{ + using namespace pybind11::literals; + + FALCOR_SCRIPT_BINDING_DEPENDENCY(Formats) + FALCOR_SCRIPT_BINDING_DEPENDENCY(Resource) + + // RenderPass + pybind11::class_> renderPass(m, "RenderPass"); + renderPass.def_property_readonly("name", &RenderPass::getName); + renderPass.def_property_readonly("type", &RenderPass::getType); + renderPass.def_property_readonly("desc", &RenderPass::getDesc); + renderPass.def("get_dictionary", [](RenderPass& pass) { return pass.getScriptingDictionary().toPython(); }); + + // PYTHONDEPRECATED BEGIN + renderPass.def("getDictionary", [](RenderPass& pass) { return pass.getScriptingDictionary().toPython(); }); + // PYTHONDEPRECATED END + + // RenderGraph + pybind11::class_> renderGraph(m, "RenderGraph"); + renderGraph.def_property("name", &RenderGraph::getName, &RenderGraph::setName); + + renderGraph.def( + "create_pass", + [](RenderGraph& graph, const std::string& pass_name, const std::string& pass_type, pybind11::dict dict = {}) + { return graph.createPass(pass_name, pass_type, Dictionary(dict)); }, + "pass_name"_a, "pass_type"_a, "dict"_a = pybind11::dict() + ); + renderGraph.def("remove_pass", &RenderGraph::removePass, "name"_a); + renderGraph.def( + "update_pass", [](RenderGraph& graph, const std::string& name, pybind11::dict dict) { graph.updatePass(name, Dictionary(dict)); }, + "name"_a, "dict"_a + ); + renderGraph.def("add_edge", &RenderGraph::addEdge, "src"_a, "dst"_a); + renderGraph.def( + "remove_edge", pybind11::overload_cast(&RenderGraph::removeEdge), "src"_a, "dst"_a + ); + renderGraph.def("mark_output", &RenderGraph::markOutput, "name"_a, "mask"_a = TextureChannelFlags::RGB); + renderGraph.def("unmark_output", &RenderGraph::unmarkOutput, "name"_a); + renderGraph.def("get_pass", &RenderGraph::getPass, "name"_a); + renderGraph.def("get_output", pybind11::overload_cast(&RenderGraph::getOutput), "name"_a); + + // PYTHONDEPRECATED BEGIN + renderGraph.def( + pybind11::init([](const std::string& name) { return RenderGraph::create(accessActivePythonRenderGraphDevice(), name); }), "name"_a + ); + renderGraph.def_static( + "createFromFile", + [](const std::filesystem::path& path) { return RenderGraph::createFromFile(accessActivePythonRenderGraphDevice(), path); }, "path"_a + ); + + renderGraph.def("print", [](ref graph) { pybind11::print(RenderGraphExporter::getIR(graph)); }); + + renderGraph.def( + "createPass", + [](RenderGraph& graph, const std::string& passName, const std::string& passType, pybind11::dict dict = {}) + { return graph.createPass(passName, passType, Dictionary(dict)); }, + "passName"_a, "passType"_a, "dict"_a = pybind11::dict() + ); + renderGraph.def("addPass", &RenderGraph::addPass, "pass_"_a, "name"_a); + renderGraph.def("removePass", &RenderGraph::removePass, "name"_a); + renderGraph.def( + "updatePass", [](RenderGraph& graph, const std::string& passName, pybind11::dict d) { graph.updatePass(passName, Dictionary(d)); }, + "name"_a, "dict"_a + ); + renderGraph.def("addEdge", &RenderGraph::addEdge, "src"_a, "dst"_a); + renderGraph.def( + "removeEdge", pybind11::overload_cast(&RenderGraph::removeEdge), "src"_a, "dst"_a + ); + renderGraph.def("markOutput", &RenderGraph::markOutput, "name"_a, "mask"_a = TextureChannelFlags::RGB); + renderGraph.def("unmarkOutput", &RenderGraph::unmarkOutput, "name"_a); + renderGraph.def("getPass", &RenderGraph::getPass, "name"_a); + renderGraph.def("getOutput", pybind11::overload_cast(&RenderGraph::getOutput), "name"_a); + // PYTHONDEPRECATED END + + // RenderPassLibrary + const auto& globalCreateRenderPass = [](const std::string& type, pybind11::dict d = {}) + { + auto pPass = RenderPass::create(type, accessActivePythonRenderGraphDevice(), Dictionary(d)); + if (!pPass) + throw RuntimeError("Can't create a render pass of type '{}'. Make sure the required plugin library was loaded.", type); + return pPass; + }; + m.def("createPass", globalCreateRenderPass, "type"_a, "dict"_a = pybind11::dict()); // PYTHONDEPRECATED +} +} // namespace Falcor diff --git a/Source/Falcor/RenderGraph/RenderGraph.h b/Source/Falcor/RenderGraph/RenderGraph.h index 8af87c46c..5205003a7 100644 --- a/Source/Falcor/RenderGraph/RenderGraph.h +++ b/Source/Falcor/RenderGraph/RenderGraph.h @@ -30,6 +30,7 @@ #include "RenderGraphExe.h" #include "RenderGraphCompiler.h" #include "Core/Macros.h" +#include "Core/Object.h" #include "Core/API/fwd.h" #include "Core/API/Formats.h" #include "Utils/UI/Gui.h" @@ -44,271 +45,344 @@ namespace Falcor { - /** Represents a render graph. - The render graph is a direct acyclic graph (DAG) of render passes. - */ - class FALCOR_API RenderGraph +/** + * Represents a render graph. + * The render graph is a direct acyclic graph (DAG) of render passes. + */ +class FALCOR_API RenderGraph : public Object +{ +public: + static const FileDialogFilterVec kFileExtensionFilters; + static constexpr uint32_t kInvalidIndex = -1; + + RenderGraph(ref pDevice, const std::string& name); + ~RenderGraph(); + + /** + * Create a new render graph. + * @param[in] pDevice GPU device. + * @param[in] name Name of the render graph. + * @return New object, or throws an exception if creation failed. + */ + static ref create(ref pDevice, const std::string& name = ""); + + /** + * Create a render graph from loading a python render graph script. + * @param[in] pDevice GPU device. + * @param[in] path Path to the script. + * @return New object, or throws an exception if creation failed. + */ + static ref createFromFile(ref pDevice, const std::filesystem::path& path); + + /** + * Return the associated GPU device. + */ + ref getDevice() const { return mpDevice; } + + /** + * Set a scene. + * @param[in] pScene New scene. This may be nullptr to unset the scene. + */ + void setScene(const ref& pScene); + + /** + * Create and add a new render pass. The name has to be unique, otherwise nullptr is returned. + * @param[in] passName Render pass name in graph. + * @param[in] passType Render pass type. + * @param[in] dict Render pass options. + * @return The new render pass. + */ + ref createPass(const std::string& passName, const std::string& passType, const Dictionary& dict); + + /** + * Add a render pass. The name has to be unique, otherwise the call will be ignored. + * @return Render pass ID, or kInvalidIndex upon failure. + */ + uint32_t addPass(const ref& pPass, const std::string& passName); + + /** + * Get a render pass by name. + * @return Ptr to render pass, or nullptr if pass wasn't found. + */ + const ref& getPass(const std::string& name) const; + + /** + * Remove a render pass and all its edges. You need to make sure the graph is still valid after the pass was removed. + */ + void removePass(const std::string& name); + + /** + * Update render pass using the specified dictionary. This function recreates the pass in place. + */ + void updatePass(const std::string& passName, const Dictionary& dict); + + /** + * Update render pass using the specified dictionary. This function calls the pass' applySettings method. + */ + void applyPassSettings(const std::string& passName, const Dictionary& dict); + + /** + * Insert an edge from a render pass' output to a different render pass input. + * The render passes must be different, the graph must be a DAG. + * There are 2 types of edges: + * - Data dependency edge - Connecting as pass output resource to another pass input resource. + * The src/dst strings have the format `renderPassName.resourceName`, where the `renderPassName` is the name used in `addPass()` and the + * `resourceName` is the resource name as described by the render pass `reflect()` call. + * - Execution dependency edge - As the name implies, it creates an execution dependency between 2 passes, even if there's no data + * dependency. You can use it to control the execution order of the graph, or to force execution of passes which have no inputs/outputs. + * The src/dst string are `srcPass` and `dstPass` as used in `addPass()`. + * + * Note that data-dependency edges may be optimized out of the execution, if they are determined not to influence the requested graph + * output. Execution-dependency edges are never optimized and will always execute. + * @return Edge ID, or kInvalidIndex upon failure. + */ + uint32_t addEdge(const std::string& src, const std::string& dst); + + /** + * Remove an edge by name. See naming conventions in addEdge(). + */ + void removeEdge(const std::string& src, const std::string& dst); + + /** + * Remove an edge by ID. + */ + void removeEdge(uint32_t edgeID); + + /** + * Execute the graph. + */ + void execute(RenderContext* pRenderContext); + + /** + * Update graph based on another graph's topology. + */ + void update(const ref& pGraph); + + /** + * 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 ref& 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()); } + + /** + * Return the index of a pass from a name, or kInvalidIndex if the pass doesn't exists. + */ + uint32_t getPassIndex(const std::string& name) const; + + /** + * Get an output resource by name. + * This is an alias for `getRenderPass(renderPassName)->getOutput(resourceName)`. + * @param[in] name Output name. Has format `renderPassName.resourceName`. + * @return Resource object, or nullptr if output not available. + */ + ref getOutput(const std::string& name); + + /** + * Get an output resource by index. + * If markOutput() or unmarkOutput() was called, the indices might change so don't cache them. + * @param[in] index Output index. The index corresponds to getOutputCount(). + * @return Resource object, or nullptr if output not available. + */ + ref getOutput(uint32_t index); + + /** + * Mark a render pass output as the graph's output. + * 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 the graph has no outputs it is invalid. If name is '*', it + * will mark all available outputs. + * @param[in] name Render pass output. + * @param[in] mask Mask of color channels. The default is RGB. + */ + void markOutput(const std::string& name, const TextureChannelFlags mask = TextureChannelFlags::RGB); + + /** + * Unmark a graph output. + * The name has the format `renderPassName.resourceName`. You can also use `renderPassName` which will unmark all the render pass + * outputs. + * @param[in] name Render pass output. + */ + void unmarkOutput(const std::string& name); + + /** + * Check if a name is marked as output. + * @param[in] name Render pass output. + */ + bool isGraphOutput(const std::string& name) const; + + /** + * Call this when the swap chain was resized. + */ + void onResize(const Fbo* pTargetFbo); + + /** + * Get the attached scene. + */ + const ref& getScene() const { return mpScene; } + + /** + * Get the number of outputs from this graph. + */ + size_t getOutputCount() const { return mOutputs.size(); } + + /** + * Get an graph output name from the graph output index. + * @param[in] index Output index. The index corresponds to getOutputCount(). + * @return Output name. + */ + std::string getOutputName(size_t index) const; + + /** + * Get the set of output masks from the graph output index. + * @param[in] index Output index. The index corresponds to getOutputCount(). + * @return Set of color channel masks. + */ + std::unordered_set getOutputMasks(size_t index) const; + + /** + * Get all output names for the render graph. + */ + std::vector getAvailableOutputs() const; + + /** + * Get all output names for the render graph that are currently unmarked. + */ + std::vector getUnmarkedOutputs() const; + + /** + * Render the graph UI. + */ + void renderUI(RenderContext* pRenderContext, Gui::Widgets& widget); + + /** + * Called upon scene updates. + * @param[in] pRenderContext The render context. + * @param[in] sceneUpdates Accumulated scene update flags. + */ + void onSceneUpdates(RenderContext* pRenderContext, Scene::UpdateFlags sceneUpdates); + + /** + * Mouse event handler. + * @return True if the event was handled by the object, false otherwise. + */ + bool onMouseEvent(const MouseEvent& mouseEvent); + + /** + * Keyboard event handler. + * @return True if the event was handled by the object, false otherwise. + */ + bool onKeyEvent(const KeyboardEvent& keyEvent); + + /** + * Called upon hot reload (by pressing F5). + * @param[in] reloaded Resources that have been reloaded. + */ + void onHotReload(HotReloadFlags reloaded); + + /** + * Get the dictionary objects used to communicate app data to the render passes. + */ + InternalDictionary& getPassesDictionary() { return mPassesDictionary; } + + /** + * Get the graph name. + */ + const std::string& getName() const { return mName; } + + /** + * Set the graph name. + */ + void setName(const std::string& name) { mName = name; } + + /** + * Compile the graph. + */ + bool compile(RenderContext* pRenderContext, std::string& log); + bool compile(RenderContext* pRenderContext) { - public: - using SharedPtr = std::shared_ptr; - static const FileDialogFilterVec kFileExtensionFilters; - - static const uint32_t kInvalidIndex = -1; - - ~RenderGraph(); - - /** Create a new render graph. - \param[in] pDevice GPU device. - \param[in] name Name of the render graph. - \return New object, or throws an exception if creation failed. - */ - static SharedPtr create(std::shared_ptr pDevice, const std::string& name = ""); - - /** Create a render graph from loading a python render graph script. - \param[in] pDevice GPU device. - \param[in] path Path to the script. - \return New object, or throws an exception if creation failed. - */ - static SharedPtr createFromFile(std::shared_ptr pDevice, const std::filesystem::path& path); - - /** Return the associated GPU device. - */ - const std::shared_ptr& getDevice() const { return mpDevice; } - - /** Set a scene. - \param[in] pScene New scene. This may be nullptr to unset the scene. - */ - void setScene(const Scene::SharedPtr& pScene); - - /** Add a render pass. The name has to be unique, otherwise the call will be ignored. - \return Render pass ID, or kInvalidIndex upon failure. - */ - uint32_t addPass(const RenderPass::SharedPtr& pPass, const std::string& passName); - - /** Get a render pass by name. - \return Ptr to render pass, or nullptr if pass wasn't found. - */ - const RenderPass::SharedPtr& getPass(const std::string& name) const; - - /** Remove a render pass and all its edges. You need to make sure the graph is still valid after the pass was removed. - */ - void removePass(const std::string& name); - - /** Update render pass using the specified dictionary. This function recreates the pass in place. - */ - void updatePass(const std::string& passName, const Dictionary& dict); - - /** Update render pass using the specified dictionary. This function calls the pass' applySettings method. - */ - void applyPassSettings(const std::string& passName, const Dictionary& dict); - - /** Insert an edge from a render pass' output to a different render pass input. - The render passes must be different, the graph must be a DAG. - There are 2 types of edges: - - Data dependency edge - Connecting as pass output resource to another pass input resource. - The src/dst strings have the format `renderPassName.resourceName`, where the `renderPassName` is the name used in `addPass()` and the `resourceName` is the resource name as described by the render pass `reflect()` call. - - Execution dependency edge - As the name implies, it creates an execution dependency between 2 passes, even if there's no data dependency. You can use it to control the execution order of the graph, or to force execution of passes which have no inputs/outputs. - The src/dst string are `srcPass` and `dstPass` as used in `addPass()`. - - Note that data-dependency edges may be optimized out of the execution, if they are determined not to influence the requested graph output. Execution-dependency edges are never optimized and will always execute. - \return Edge ID, or kInvalidIndex upon failure. - */ - uint32_t addEdge(const std::string& src, const std::string& dst); - - /** Remove an edge by name. See naming conventions in addEdge(). - */ - void removeEdge(const std::string& src, const std::string& dst); - - /** Remove an edge by ID. - */ - void removeEdge(uint32_t edgeID); - - /** Execute the graph. - */ - void execute(RenderContext* pRenderContext); - - /** Update graph based on another graph's topology. - */ - void update(const SharedPtr& pGraph); - - /** 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); - - /** 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()); } - - /** Return the index of a pass from a name, or kInvalidIndex if the pass doesn't exists. - */ - uint32_t getPassIndex(const std::string& name) const; - - /** Get an output resource by name. - This is an alias for `getRenderPass(renderPassName)->getOutput(resourceName)`. - \param[in] name Output name. Has format `renderPassName.resourceName`. - \return Resource object, or nullptr if output not available. - */ - Resource::SharedPtr getOutput(const std::string& name); - - /** Get an output resource by index. - If markOutput() or unmarkOutput() was called, the indices might change so don't cache them. - \param[in] index Output index. The index corresponds to getOutputCount(). - \return Resource object, or nullptr if output not available. - */ - Resource::SharedPtr getOutput(uint32_t index); - - /** Mark a render pass output as the graph's output. - 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 the graph has no outputs it is invalid. - If name is '*', it will mark all available outputs. - \param[in] name Render pass output. - \param[in] mask Mask of color channels. The default is RGB. - */ - void markOutput(const std::string& name, const TextureChannelFlags mask = TextureChannelFlags::RGB); - - /** Unmark a graph output. - The name has the format `renderPassName.resourceName`. You can also use `renderPassName` which will unmark all the render pass outputs. - \param[in] name Render pass output. - */ - void unmarkOutput(const std::string& name); - - /** Check if a name is marked as output. - \param[in] name Render pass output. - */ - bool isGraphOutput(const std::string& name) const; - - /** Call this when the swap chain was resized. - */ - void onResize(const Fbo* pTargetFbo); - - /** Get the attached scene. - */ - const Scene::SharedPtr& getScene() const { return mpScene; } - - /** Get the number of outputs from this graph. - */ - size_t getOutputCount() const { return mOutputs.size(); } - - /** Get an graph output name from the graph output index. - \param[in] index Output index. The index corresponds to getOutputCount(). - \return Output name. - */ - std::string getOutputName(size_t index) const; - - /** Get the set of output masks from the graph output index. - \param[in] index Output index. The index corresponds to getOutputCount(). - \return Set of color channel masks. - */ - std::unordered_set getOutputMasks(size_t index) const; - - /** Get all output names for the render graph. - */ - std::vector getAvailableOutputs() const; - - /** Get all output names for the render graph that are currently unmarked. - */ - std::vector getUnmarkedOutputs() const; - - /** Render the graph UI. - */ - void renderUI(RenderContext* pRenderContext, Gui::Widgets& widget); - - /** Called upon scene updates. - \param[in] pRenderContext The render context. - \param[in] sceneUpdates Accumulated scene update flags. - */ - void onSceneUpdates(RenderContext* pRenderContext, Scene::UpdateFlags sceneUpdates); - - /** Mouse event handler. - \return True if the event was handled by the object, false otherwise. - */ - bool onMouseEvent(const MouseEvent& mouseEvent); - - /** Keyboard event handler. - \return True if the event was handled by the object, false otherwise. - */ - bool onKeyEvent(const KeyboardEvent& keyEvent); - - /** Called upon hot reload (by pressing F5). - \param[in] reloaded Resources that have been reloaded. - */ - void onHotReload(HotReloadFlags reloaded); - - /** Get the dictionary objects used to communicate app data to the render passes. - */ - const InternalDictionary::SharedPtr& getPassesDictionary() const { return mpPassDictionary; } - - /** Get the graph name. - */ - const std::string& getName() const { return mName; } - - /** Set the graph name. - */ - void setName(const std::string& name) { mName = name; } - - /** Compile the graph. - */ - bool compile(RenderContext* pRenderContext, std::string& log); - bool compile(RenderContext* pRenderContext) { std::string s; return compile(pRenderContext, s); } - - private: - RenderGraph(std::shared_ptr pDevice, const std::string& name); - - struct EdgeData - { - std::string srcField; - std::string dstField; - }; + std::string s; + return compile(pRenderContext, s); + } - struct NodeData - { - std::string name; - RenderPass::SharedPtr pPass; - }; +private: + struct EdgeData + { + std::string srcField; + std::string dstField; + }; - struct GraphOut + struct NodeData + { + std::string name; + ref pPass; + }; + + struct GraphOut + { + uint32_t nodeId = kInvalidIndex; ///< Render pass node ID. + std::string field; ///< Name of the output field. + std::unordered_set masks; ///< Set of output color channel masks. + + bool operator==(const GraphOut& other) const { - uint32_t nodeId = kInvalidIndex; ///< Render pass node ID. - std::string field; ///< Name of the output field. - std::unordered_set masks; ///< Set of output color channel masks. - - bool operator==(const GraphOut& other) const - { - if (nodeId != other.nodeId) return false; - if (field != other.field) return false; - // Do not compare masks, the other fields uniquely identify the output. - return true; - } - - bool operator!=(const GraphOut& other) const { return !(*this == other); } - }; - - RenderPass* getRenderPassAndNamePair(const bool input, const std::string& fullname, const std::string& errorPrefix, std::pair& nameAndField) const; - 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 isGraphOutput(const GraphOut& graphOut) const; - - std::shared_ptr mpDevice; - - std::string mName; ///< Name of render graph. - Scene::SharedPtr mpScene; ///< Current scene. This may be nullptr. - - DirectedGraph::SharedPtr mpGraph; ///< DAG of render passes. Only IDs are stored, not the actual passes. - std::unordered_map mNameToIndex; ///< Map from render pass name to node ID in graph. - std::unordered_map mEdgeData; - std::unordered_map mNodeData; ///< Map from node ID to render pass name and ptr. - std::vector mOutputs; ///< Array of all outputs marked as graph outputs. GRAPH_TODO should this be an unordered set? - - InternalDictionary::SharedPtr mpPassDictionary; ///< Dictionary used to communicate between passes. - RenderGraphExe::SharedPtr mpExe; ///< Helper for allocating resources and executing the graph. - RenderGraphCompiler::Dependencies mCompilerDeps; ///< Data needed by the graph compiler. - bool mRecompile = false; ///< Set to true to trigger a recompilation after any graph changes (topology/scene/size/passes/etc.) - - friend class RenderGraphUI; - friend class RenderGraphExporter; - friend class RenderGraphCompiler; + if (nodeId != other.nodeId) + return false; + if (field != other.field) + return false; + // Do not compare masks, the other fields uniquely identify the output. + return true; + } + + bool operator!=(const GraphOut& other) const { return !(*this == other); } }; -} + + RenderPass* getRenderPassAndNamePair( + const bool input, + const std::string& fullname, + const std::string& errorPrefix, + std::pair& nameAndField + ) const; + + 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 isGraphOutput(const GraphOut& graphOut) const; + + ref mpDevice; + + std::string mName; ///< Name of render graph. + ref mpScene; ///< Current scene. This may be nullptr. + + std::unique_ptr mpGraph; ///< DAG of render passes. Only IDs are stored, not the actual passes. + std::unordered_map mNameToIndex; ///< Map from render pass name to node ID in graph. + std::unordered_map mEdgeData; + std::unordered_map mNodeData; ///< Map from node ID to render pass name and ptr. + std::vector mOutputs; ///< Array of all outputs marked as graph outputs. GRAPH_TODO should this be an unordered set? + + InternalDictionary mPassesDictionary; ///< Dictionary used to communicate between passes. + std::unique_ptr mpExe; ///< Helper for allocating resources and executing the graph. + RenderGraphCompiler::Dependencies mCompilerDeps; ///< Data needed by the graph compiler. + bool mRecompile = false; ///< Set to true to trigger a recompilation after any graph changes (topology/scene/size/passes/etc.) + + friend class RenderGraphUI; + friend class RenderGraphExporter; + friend class RenderGraphCompiler; +}; +} // namespace Falcor diff --git a/Source/Falcor/RenderGraph/RenderGraphCompiler.cpp b/Source/Falcor/RenderGraph/RenderGraphCompiler.cpp index 44f337e8d..04586180f 100644 --- a/Source/Falcor/RenderGraph/RenderGraphCompiler.cpp +++ b/Source/Falcor/RenderGraph/RenderGraphCompiler.cpp @@ -33,416 +33,446 @@ namespace Falcor { - namespace - { - bool canAutoResolve(const RenderPassReflection::Field& src, const RenderPassReflection::Field& dst) - { - return src.getSampleCount() > 1 && dst.getSampleCount() == 1; - } - } +namespace +{ +bool canAutoResolve(const RenderPassReflection::Field& src, const RenderPassReflection::Field& dst) +{ + return src.getSampleCount() > 1 && dst.getSampleCount() == 1; +} +} // namespace - RenderGraphCompiler::RenderGraphCompiler(RenderGraph& graph, const Dependencies& dependencies) - : mGraph(graph) - , mpDevice(graph.getDevice()) - , mDependencies(dependencies) - {} +RenderGraphCompiler::RenderGraphCompiler(RenderGraph& graph, const Dependencies& dependencies) + : mGraph(graph), mpDevice(graph.getDevice()), mDependencies(dependencies) +{} - RenderGraphExe::SharedPtr RenderGraphCompiler::compile(RenderGraph& graph, RenderContext* pRenderContext, const Dependencies& dependencies) - { - RenderGraphCompiler c = RenderGraphCompiler(graph, dependencies); +std::unique_ptr RenderGraphCompiler::compile( + RenderGraph& graph, + RenderContext* pRenderContext, + 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); + // Register the external resources + auto pResourcesCache = std::make_unique(); + for (const auto& [name, pRes] : dependencies.externalResources) + pResourcesCache->registerExternalResource(name, pRes); + c.resolveExecutionOrder(); + c.compilePasses(pRenderContext); + if (c.insertAutoPasses()) c.resolveExecutionOrder(); - c.compilePasses(pRenderContext); - if (c.insertAutoPasses()) c.resolveExecutionOrder(); - c.validateGraph(); - c.allocateResources(pRenderContext->getDevice(), pResourcesCache.get()); + c.validateGraph(); + c.allocateResources(pRenderContext->getDevice(), pResourcesCache.get()); - auto pExe = RenderGraphExe::create(); - pExe->mExecutionList.reserve(c.mExecutionList.size()); + auto pExe = std::make_unique(); + pExe->mExecutionList.reserve(c.mExecutionList.size()); - for (auto e : c.mExecutionList) - { - pExe->insertPass(e.name, e.pPass); - } - c.restoreCompilationChanges(); - pExe->mpResourceCache = pResourcesCache; - return pExe; + for (auto e : c.mExecutionList) + { + pExe->insertPass(e.name, e.pPass); } + c.restoreCompilationChanges(); + pExe->mpResourceCache = std::move(pResourcesCache); + return pExe; +} - void RenderGraphCompiler::validateGraph() const - { - std::string err; +void RenderGraphCompiler::validateGraph() const +{ + std::string err; - for (const auto& p : mExecutionList) + for (const auto& p : mExecutionList) + { + // Make sure all the inputs are satisfied + for (uint32_t i = 0; i < p.reflector.getFieldCount(); i++) { - // 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& 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"; + 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 (mGraph.getOutputCount() == 0) err += "Graph must have at least one output.\n"; + if (mGraph.getOutputCount() == 0) + err += "Graph must have at least one output.\n"; - if (err.size()) throw RuntimeError(err); - } + if (err.size()) + throw RuntimeError(err); +} - void RenderGraphCompiler::resolveExecutionOrder() - { - mExecutionList.clear(); +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 + // 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 + for (auto& e : mGraph.mEdgeData) // Add all the passes which have an execution-edge connected to them + { + if (e.second.dstField.empty()) { - if (e.second.dstField.empty()) - { - FALCOR_ASSERT(e.second.srcField.empty()); - const auto& edge = mGraph.mpGraph->getEdge(e.first); - mandatoryPasses.insert(edge->getDestNode()); - mandatoryPasses.insert(edge->getSourceNode()); - } + FALCOR_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) + // 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) { - 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(); - } + participatingPasses.insert(nodeId); + nodeId = dfs.traverse(); } + } - // Run topological sort - auto topologicalSort = DirectedGraphTopologicalSort::sort(mGraph.mpGraph.get()); + // Run topological sort + auto topologicalSort = DirectedGraphTopologicalSort::sort(*mGraph.mpGraph); - RenderPass::CompileData compileData; - compileData.defaultTexDims = mDependencies.defaultResourceProps.dims; - compileData.defaultTexFormat = mDependencies.defaultResourceProps.format; + RenderPass::CompileData compileData; + compileData.defaultTexDims = mDependencies.defaultResourceProps.dims; + compileData.defaultTexFormat = mDependencies.defaultResourceProps.format; - // For each object in the vector, if it's being used in the execution, put it in the list - for (auto& node : topologicalSort) + // 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()) { - if (participatingPasses.find(node) != participatingPasses.end()) - { - const auto pData = mGraph.mNodeData[node]; - mExecutionList.push_back({ node, pData.pPass, pData.name, pData.pPass->reflect(compileData) }); - } + const auto pData = mGraph.mNodeData[node]; + mExecutionList.push_back({node, pData.pPass, pData.name, pData.pPass->reflect(compileData)}); } } +} - bool RenderGraphCompiler::insertAutoPasses() +bool RenderGraphCompiler::insertAutoPasses() +{ + bool addedPasses = false; + for (size_t i = 0; i < mExecutionList.size(); i++) { - bool addedPasses = false; - for (size_t i = 0; i < mExecutionList.size(); i++) - { - const RenderPassReflection& passReflection = mExecutionList[i].reflector; + 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++) + // 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++) { - // 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; + uint32_t edgeIndex = pNode->getOutgoingEdge(e); + const auto& edgeData = mGraph.mEdgeData.at(edgeIndex); - const DirectedGraph::Node* pNode = mGraph.mpGraph->getNode(mExecutionList[i].index); - for (uint32_t e = 0; e < pNode->getOutgoingEdgeCount(); e++) + // For every output field, iterate over all edges extending from that field + if (srcField.getName() == edgeData.srcField) { - uint32_t edgeIndex = pNode->getOutgoingEdge(e); - const auto& edgeData = mGraph.mEdgeData.at(edgeIndex); + const auto& pEdge = mGraph.mpGraph->getEdge(edgeIndex); + const std::string& dstPassName = mGraph.mNodeData.at(pEdge->getDestNode()).name; - // For every output field, iterate over all edges extending from that field - if (srcField.getName() == edgeData.srcField) + // If edge is connected to something that isn't executed, ignore + auto getPassReflection = [&](uint32_t index) -> std::optional { - 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); - - FALCOR_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(mpDevice); - 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) + 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); + + FALCOR_ASSERT(srcField.isValid() && dstField.isValid()); + if (canAutoResolve(srcField, dstField)) { - // 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); + std::string dstFieldName = dstPassName + '.' + dstField.getName(); + dstFieldNames.push_back(dstFieldName); } - - mCompilationChanges.generatedPasses.push_back(resolvePassName); - addedPasses = true; } } - } - - return addedPasses; - } - - void RenderGraphCompiler::allocateResources(Device* pDevice, 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); - FALCOR_ASSERT(pNode); - RenderPass* pCurrPass = mGraph.mNodeData[nodeIndex].pPass.get(); - const auto& passReflection = mExecutionList[i].reflector; - auto isResourceUsed = [&](auto field) + // If there are connections to add MSAA Resolve + if (dstFieldNames.size() > 0) { - 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; - }; + // One resolve pass is made for every output that requires it + auto pResolvePass = ResolvePass::create(mpDevice); + pResolvePass->setFormat(srcField.getFormat()); // Match input texture format - // 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(); + // Create pass and attach src to it + std::string resolvePassName = srcFieldName + "-ResolvePass"; + mGraph.addPass(pResolvePass, resolvePassName); + mGraph.addEdge(srcFieldName, resolvePassName + ".src"); - // Skip input resources, we never allocate them - if (!is_set(field.getVisibility(), RenderPassReflection::Field::Visibility::Input)) + // For every input the src field is connected to, connect the resolve pass output to the input + for (const auto& dstFieldName : dstFieldNames) { - 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); - } - } + // Remove original edge + mGraph.removeEdge(srcFieldName, dstFieldName); + // Replace with edge coming from resolve output + mGraph.addEdge(resolvePassName + ".dst", dstFieldName); - // 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()) - { - FALCOR_ASSERT(edgeData.srcField.empty()); - continue; + // Log changes made to user's graph by compilation process + mCompilationChanges.removedEdges.emplace_back(srcFieldName, dstFieldName); } - const auto& dstField = *passReflection.getField(edgeData.dstField); - FALCOR_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); + mCompilationChanges.generatedPasses.push_back(resolvePassName); + addedPasses = true; } } - - pResourceCache->allocateResources(pDevice, mDependencies.defaultResourceProps); } + return addedPasses; +} - void RenderGraphCompiler::restoreCompilationChanges() +void RenderGraphCompiler::allocateResources(ref pDevice, 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++) { - 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(); + passToIndex.emplace(mExecutionList[i].pPass.get(), uint32_t(i)); } - RenderPass::CompileData RenderGraphCompiler::prepPassCompilationData(const PassData& passData) + for (size_t i = 0; i < mExecutionList.size(); i++) { - RenderPass::CompileData compileData; - compileData.defaultTexDims = mDependencies.defaultResourceProps.dims; - compileData.defaultTexFormat = mDependencies.defaultResourceProps.format; + uint32_t nodeIndex = mExecutionList[i].index; - auto isExecutionEdge = [this](uint32_t edgeId) + const DirectedGraph::Node* pNode = mGraph.mpGraph->getNode(nodeIndex); + FALCOR_ASSERT(pNode); + RenderPass* pCurrPass = mGraph.mNodeData[nodeIndex].pPass.get(); + const auto& passReflection = mExecutionList[i].reflector; + + auto isResourceUsed = [&](auto field) { - return mGraph.mEdgeData[edgeId].srcField.empty(); + 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; }; - // Get the list of input resources - const auto pNode = mGraph.mpGraph->getNode(passData.index); - for (uint32_t i = 0; i < pNode->getIncomingEdgeCount(); i++) + // Register all pass outputs + for (size_t f = 0; f < passReflection.getFieldCount(); f++) { - uint32_t e = pNode->getIncomingEdge(i); - if (isExecutionEdge(e)) continue; + auto field = *passReflection.getField(f); + std::string fullFieldName = mGraph.mNodeData[nodeIndex].name + '.' + field.getName(); - uint32_t incomingPass = mGraph.mpGraph->getEdge(e)->getSourceNode(); - for (const auto& otherPass : mExecutionList) + // Skip input resources, we never allocate them + if (!is_set(field.getVisibility(), RenderPassReflection::Field::Visibility::Input)) { - 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; + 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); } } - // Add the external resources - for (auto& [name, pRes] : mDependencies.externalResources) + // 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++) { - if (hasPrefix(name, passData.name + ".")) + 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()) { - 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()); + FALCOR_ASSERT(edgeData.srcField.empty()); + continue; } + + const auto& dstField = *passReflection.getField(edgeData.dstField); + FALCOR_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); } + } - // 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; + pResourceCache->allocateResources(pDevice, 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; - uint32_t outgoingPass = mGraph.mpGraph->getEdge(e)->getDestNode(); - for (const auto& otherPass : mExecutionList) + 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) { - 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); - } - } + 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; } + } - return compileData; + // 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() + ); + } } - void RenderGraphCompiler::compilePasses(RenderContext* pRenderContext) + // 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++) { - while(1) + uint32_t e = pNode->getOutgoingEdge(i); + if (isExecutionEdge(e)) + continue; + + uint32_t outgoingPass = mGraph.mpGraph->getEdge(e)->getDestNode(); + for (const auto& otherPass : mExecutionList) { - std::string log; - bool success = true; - for (auto& p : mExecutionList) + if (otherPass.index == outgoingPass) { - try + auto f = *otherPass.reflector.getField(mGraph.mEdgeData[e].dstField); + auto pField = compileData.connectedResources.getField(mGraph.mEdgeData[e].srcField); + if (pField) { - p.pPass->compile(pRenderContext, prepPassCompilationData(p)); + const_cast(pField)->merge(f); } - catch (const std::exception& e) + else { - log += std::string(e.what()) + "\n"; - success = false; + const auto& fOut = *passData.reflector.getField(mGraph.mEdgeData[e].srcField); + f.name(fOut.getName()).visibility(fOut.getVisibility()).desc(fOut.getDesc()); + compileData.connectedResources.addField(f); } } + } + } - if (success) return; + return compileData; +} - // Retry - bool changed = false; - for (auto& p : mExecutionList) +void RenderGraphCompiler::compilePasses(RenderContext* pRenderContext) +{ + while (1) + { + std::string log; + bool success = true; + for (auto& p : mExecutionList) + { + try { - auto newR = p.pPass->reflect(prepPassCompilationData(p)); - if (newR != p.reflector) - { - p.reflector = newR; - changed = true; - } + p.pPass->compile(pRenderContext, prepPassCompilationData(p)); } + catch (const std::exception& e) + { + log += std::string(e.what()) + "\n"; + success = false; + } + } + + if (success) + return; - if (!changed) + // Retry + bool changed = false; + for (auto& p : mExecutionList) + { + auto newR = p.pPass->reflect(prepPassCompilationData(p)); + if (newR != p.reflector) { - reportError("Graph compilation failed.\n" + log); - return; + p.reflector = newR; + changed = true; } } + + if (!changed) + { + reportError("Graph compilation failed.\n" + log); + return; + } } } +} // namespace Falcor diff --git a/Source/Falcor/RenderGraph/RenderGraphCompiler.h b/Source/Falcor/RenderGraph/RenderGraphCompiler.h index 35526c3f4..89acedf0c 100644 --- a/Source/Falcor/RenderGraph/RenderGraphCompiler.h +++ b/Source/Falcor/RenderGraph/RenderGraphCompiler.h @@ -37,47 +37,47 @@ namespace Falcor { - class RenderGraph; +class RenderGraph; - class FALCOR_API RenderGraphCompiler +class FALCOR_API RenderGraphCompiler +{ +public: + struct Dependencies { - public: - struct Dependencies - { - ResourceCache::DefaultProperties defaultResourceProps; - ResourceCache::ResourcesMap externalResources; - }; - static RenderGraphExe::SharedPtr compile(RenderGraph& graph, RenderContext* pRenderContext, const Dependencies& dependencies); + ResourceCache::DefaultProperties defaultResourceProps; + ResourceCache::ResourcesMap externalResources; + }; + static std::unique_ptr compile(RenderGraph& graph, RenderContext* pRenderContext, const Dependencies& dependencies); - private: - RenderGraphCompiler(RenderGraph& graph, const Dependencies& dependencies); +private: + RenderGraphCompiler(RenderGraph& graph, const Dependencies& dependencies); - RenderGraph& mGraph; - std::shared_ptr mpDevice; - const Dependencies& mDependencies; + RenderGraph& mGraph; + ref mpDevice; + const Dependencies& mDependencies; - struct PassData - { - uint32_t index; - RenderPass::SharedPtr pPass; - std::string name; - RenderPassReflection reflector; - }; - std::vector mExecutionList; + struct PassData + { + uint32_t index; + ref pPass; + std::string name; + RenderPassReflection reflector; + }; + std::vector mExecutionList; - // TODO Better way to track history, or avoid changing the original graph altogether? - struct - { - std::vector generatedPasses; - std::vector> removedEdges; - } mCompilationChanges; + // TODO Better way to track history, or avoid changing the original graph altogether? + struct + { + std::vector generatedPasses; + std::vector> removedEdges; + } mCompilationChanges; - void resolveExecutionOrder(); - void compilePasses(RenderContext* pRenderContext); - bool insertAutoPasses(); - void allocateResources(Device* pDevice, ResourceCache* pResourceCache); - void validateGraph() const; - void restoreCompilationChanges(); - RenderPass::CompileData prepPassCompilationData(const PassData& passData); - }; -} + void resolveExecutionOrder(); + void compilePasses(RenderContext* pRenderContext); + bool insertAutoPasses(); + void allocateResources(ref pDevice, ResourceCache* pResourceCache); + void validateGraph() const; + void restoreCompilationChanges(); + RenderPass::CompileData prepPassCompilationData(const PassData& passData); +}; +} // namespace Falcor diff --git a/Source/Falcor/RenderGraph/RenderGraphExe.cpp b/Source/Falcor/RenderGraph/RenderGraphExe.cpp index 63e6a014a..a6c24fae0 100644 --- a/Source/Falcor/RenderGraph/RenderGraphExe.cpp +++ b/Source/Falcor/RenderGraph/RenderGraphExe.cpp @@ -30,81 +30,82 @@ namespace Falcor { - void RenderGraphExe::execute(const Context& ctx) - { - FALCOR_PROFILE(ctx.pRenderContext, "RenderGraphExe::execute()"); +void RenderGraphExe::execute(const Context& ctx) +{ + FALCOR_PROFILE(ctx.pRenderContext, "RenderGraphExe::execute()"); - for (const auto& pass : mExecutionList) - { - FALCOR_PROFILE(ctx.pRenderContext, pass.name); + for (const auto& pass : mExecutionList) + { + FALCOR_PROFILE(ctx.pRenderContext, pass.name); - RenderData renderData(pass.name, mpResourceCache, ctx.pGraphDictionary, ctx.defaultTexDims, ctx.defaultTexFormat); - pass.pPass->execute(ctx.pRenderContext, renderData); - } + RenderData renderData(pass.name, *mpResourceCache, ctx.passesDictionary, ctx.defaultTexDims, ctx.defaultTexFormat); + pass.pPass->execute(ctx.pRenderContext, renderData); } +} - void RenderGraphExe::renderUI(RenderContext* pRenderContext, Gui::Widgets& widget) +void RenderGraphExe::renderUI(RenderContext* pRenderContext, Gui::Widgets& widget) +{ + for (const auto& p : mExecutionList) { - for (const auto& p : mExecutionList) - { - const auto& pPass = p.pPass; + const auto& pPass = p.pPass; - if (auto passGroup = widget.group(p.name)) - { - // Create a unique ID scope per render pass so we can have multiple instances of a render render using the same widget IDs. - IDScope idScope(p.pPass.get()); + if (auto passGroup = widget.group(p.name)) + { + // Create a unique ID scope per render pass so we can have multiple instances of a render render using the same widget IDs. + IDScope idScope(p.pPass.get()); - const auto& desc = pPass->getDesc(); - if (desc.size()) passGroup.tooltip(desc); - pPass->renderUI(pRenderContext, passGroup); - } + const auto& desc = pPass->getDesc(); + if (desc.size()) + passGroup.tooltip(desc); + pPass->renderUI(pRenderContext, passGroup); } } +} - bool RenderGraphExe::onMouseEvent(const MouseEvent& mouseEvent) +bool RenderGraphExe::onMouseEvent(const MouseEvent& mouseEvent) +{ + bool b = false; + for (const auto& p : mExecutionList) { - bool b = false; - for (const auto& p : mExecutionList) - { - const auto& pPass = p.pPass; - b = b || pPass->onMouseEvent(mouseEvent); - } - return b; + const auto& pPass = p.pPass; + b = b || pPass->onMouseEvent(mouseEvent); } + return b; +} - bool RenderGraphExe::onKeyEvent(const KeyboardEvent& keyEvent) +bool RenderGraphExe::onKeyEvent(const KeyboardEvent& keyEvent) +{ + bool b = false; + for (const auto& p : mExecutionList) { - bool b = false; - for (const auto& p : mExecutionList) - { - const auto& pPass = p.pPass; - b = b || pPass->onKeyEvent(keyEvent); - } - return b; + const auto& pPass = p.pPass; + b = b || pPass->onKeyEvent(keyEvent); } + return b; +} - void RenderGraphExe::onHotReload(HotReloadFlags reloaded) +void RenderGraphExe::onHotReload(HotReloadFlags reloaded) +{ + for (const auto& p : mExecutionList) { - for (const auto& p : mExecutionList) - { - const auto& pPass = p.pPass; - pPass->onHotReload(reloaded); - } + const auto& pPass = p.pPass; + pPass->onHotReload(reloaded); } +} - void RenderGraphExe::insertPass(const std::string& name, const RenderPass::SharedPtr& pPass) - { - mExecutionList.push_back(Pass(name, pPass)); - } +void RenderGraphExe::insertPass(const std::string& name, const ref& pPass) +{ + mExecutionList.push_back(Pass(name, pPass)); +} - Resource::SharedPtr RenderGraphExe::getResource(const std::string& name) const - { - FALCOR_ASSERT(mpResourceCache); - return mpResourceCache->getResource(name); - } +ref RenderGraphExe::getResource(const std::string& name) const +{ + FALCOR_ASSERT(mpResourceCache); + return mpResourceCache->getResource(name); +} - void RenderGraphExe::setInput(const std::string& name, const Resource::SharedPtr& pResource) - { - mpResourceCache->registerExternalResource(name, pResource); - } +void RenderGraphExe::setInput(const std::string& name, const ref& pResource) +{ + mpResourceCache->registerExternalResource(name, pResource); } +} // namespace Falcor diff --git a/Source/Falcor/RenderGraph/RenderGraphExe.h b/Source/Falcor/RenderGraph/RenderGraphExe.h index 551c1f675..008fbdfb6 100644 --- a/Source/Falcor/RenderGraph/RenderGraphExe.h +++ b/Source/Falcor/RenderGraph/RenderGraphExe.h @@ -40,72 +40,76 @@ namespace Falcor { - class RenderGraphCompiler; - class RenderContext; +class RenderGraphCompiler; +class RenderContext; - class FALCOR_API RenderGraphExe +class FALCOR_API RenderGraphExe +{ +public: + struct Context { - public: - using SharedPtr = std::shared_ptr; - - struct Context - { - RenderContext* pRenderContext; - InternalDictionary::SharedPtr pGraphDictionary; - uint2 defaultTexDims; - ResourceFormat defaultTexFormat; - }; + RenderContext* pRenderContext; + InternalDictionary& passesDictionary; + uint2 defaultTexDims; + ResourceFormat defaultTexFormat; + }; - /** Execute the graph - */ - void execute(const Context& ctx); + /** + * Execute the graph + */ + void execute(const Context& ctx); - /** Render the UI - */ - void renderUI(RenderContext* pRenderContext, Gui::Widgets& widget); + /** + * Render the UI + */ + void renderUI(RenderContext* pRenderContext, Gui::Widgets& widget); - /** Mouse event handler. - Returns true if the event was handled by the object, false otherwise - */ - bool onMouseEvent(const MouseEvent& mouseEvent); + /** + * 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); + /** + * Keyboard event handler + * Returns true if the event was handled by the object, false otherwise + */ + bool onKeyEvent(const KeyboardEvent& keyEvent); - /** Called upon hot reload (by pressing F5). - \param[in] reloaded Resources that have been reloaded. - */ - void onHotReload(HotReloadFlags reloaded); + /** + * Called upon hot reload (by pressing F5). + * @param[in] reloaded Resources that have been reloaded. + */ + void onHotReload(HotReloadFlags reloaded); - /** Get a resource from the cache - */ - Resource::SharedPtr getResource(const std::string& name) const; + /** + * Get a resource from the cache + */ + ref 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); + /** + * 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 ref& pResource); - private: - friend class RenderGraphCompiler; - static SharedPtr create() { return SharedPtr(new RenderGraphExe); } - RenderGraphExe() = default; +private: + friend class RenderGraphCompiler; - void insertPass(const std::string& name, const RenderPass::SharedPtr& pPass); + void insertPass(const std::string& name, const ref& 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_) {} - }; + struct Pass + { + std::string name; + ref pPass; - std::vector mExecutionList; - ResourceCache::SharedPtr mpResourceCache; + private: + friend class RenderGraphExe; // Force RenderGraphCompiler to use insertPass() by hiding this Ctor from it + Pass(const std::string& name_, const ref& pPass_) : name(name_), pPass(pPass_) {} }; -} + + std::vector mExecutionList; + std::unique_ptr mpResourceCache; +}; +} // namespace Falcor diff --git a/Source/Falcor/RenderGraph/RenderGraphIR.cpp b/Source/Falcor/RenderGraph/RenderGraphIR.cpp index d31ef51a6..331991663 100644 --- a/Source/Falcor/RenderGraph/RenderGraphIR.cpp +++ b/Source/Falcor/RenderGraph/RenderGraphIR.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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,94 +33,66 @@ 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::kUpdatePass = "updatePass"; - const char* RenderGraphIR::kLoadPassLibrary = "loadRenderPassLibrary"; - const char* RenderGraphIR::kCreatePass = "createPass"; - const char* RenderGraphIR::kRenderGraph = "RenderGraph"; - std::string RenderGraphIR::getFuncName(const std::string& graphName) +RenderGraphIR::RenderGraphIR(const std::string& name, bool newGraph) : mName(name) +{ + if (newGraph) { - return "render_graph_" + graphName; + mIR += "from falcor import *\n\n"; + mIR += "def " + getFuncName(mName) + "():\n"; + mIndentation = " "; + mGraphPrefix += mIndentation; + mIR += mIndentation + "g" + " = " + ScriptWriter::makeFunc("RenderGraph", mName); } + mGraphPrefix += "g."; +} - RenderGraphIR::RenderGraphIR(const std::string& name, bool newGraph) : mName(name) - { - if (newGraph) - { - mIR += "from falcor import *\n\n"; - mIR += "def " + getFuncName(mName) + "():\n"; - mIndentation = " "; - mGraphPrefix += mIndentation; - mIR += mIndentation + "g" + " = " + ScriptWriter::makeFunc(kRenderGraph, mName); - } - mGraphPrefix += "g."; - }; - - RenderGraphIR::SharedPtr RenderGraphIR::create(const std::string& name, bool newGraph) - { - return SharedPtr(new RenderGraphIR(name, newGraph)); - } +void RenderGraphIR::createPass(const std::string& passClass, const std::string& passName, const Dictionary& dictionary) +{ + mIR += mGraphPrefix + ScriptWriter::makeFunc("create_pass", passName, passClass, dictionary); +} - void RenderGraphIR::addPass(const std::string& passClass, const std::string& passName, const Dictionary& dictionary) - { - mIR += mIndentation + passName + " = "; - if (dictionary.size()) - { - mIR += ScriptWriter::makeFunc(RenderGraphIR::kCreatePass, passClass, dictionary); - } - else - { - mIR += ScriptWriter::makeFunc(RenderGraphIR::kCreatePass, passClass); - } - mIR += mGraphPrefix + RenderGraphIR::kAddPass + "(" + passName + ", " + ScriptWriter::getArgString(passName) + ")\n"; - } +void RenderGraphIR::updatePass(const std::string& passName, const Dictionary& dictionary) +{ + mIR += mGraphPrefix + ScriptWriter::makeFunc("update_pass", passName, dictionary); +} - void RenderGraphIR::updatePass(const std::string& passName, const Dictionary& dictionary) - { - mIR += mGraphPrefix + ScriptWriter::makeFunc(RenderGraphIR::kUpdatePass, passName, dictionary); - } +void RenderGraphIR::removePass(const std::string& passName) +{ + mIR += mGraphPrefix + ScriptWriter::makeFunc("remove_pass", passName); +} - void RenderGraphIR::removePass(const std::string& passName) - { - mIR += mGraphPrefix + ScriptWriter::makeFunc(RenderGraphIR::kRemovePass, passName); - } +void RenderGraphIR::addEdge(const std::string& src, const std::string& dst) +{ + mIR += mGraphPrefix + ScriptWriter::makeFunc("add_edge", src, dst); +} - void RenderGraphIR::addEdge(const std::string& src, const std::string& dst) - { - mIR += mGraphPrefix + ScriptWriter::makeFunc(RenderGraphIR::kAddEdge, src, dst); - } +void RenderGraphIR::removeEdge(const std::string& src, const std::string& dst) +{ + mIR += mGraphPrefix + ScriptWriter::makeFunc("remove_edge", src, dst); +} - void RenderGraphIR::removeEdge(const std::string& src, const std::string& dst) +void RenderGraphIR::markOutput(const std::string& name, const TextureChannelFlags mask) +{ + if (mask == TextureChannelFlags::RGB) { - mIR += mGraphPrefix + ScriptWriter::makeFunc(RenderGraphIR::kRemoveEdge, src, dst); + // Leave out mask parameter for the default case (RGB). + mIR += mGraphPrefix + ScriptWriter::makeFunc("mark_output", name); } - - void RenderGraphIR::markOutput(const std::string& name, const TextureChannelFlags mask) + else { - if (mask == TextureChannelFlags::RGB) - { - // Leave out mask parameter for the default case (RGB). - mIR += mGraphPrefix + ScriptWriter::makeFunc(RenderGraphIR::kMarkOutput, name); - } - else - { - mIR += mGraphPrefix + ScriptWriter::makeFunc(RenderGraphIR::kMarkOutput, name, mask); - } + mIR += mGraphPrefix + ScriptWriter::makeFunc("mark_output", name, mask); } +} - void RenderGraphIR::unmarkOutput(const std::string& name) - { - mIR += mGraphPrefix + ScriptWriter::makeFunc(RenderGraphIR::kUnmarkOutput, name); - } +void RenderGraphIR::unmarkOutput(const std::string& name) +{ + mIR += mGraphPrefix + ScriptWriter::makeFunc("unmark_output", name); +} - void RenderGraphIR::loadPassLibrary(const std::string& name) - { - mIR += mIndentation + ScriptWriter::makeFunc(RenderGraphIR::kLoadPassLibrary, name); - } +std::string RenderGraphIR::getFuncName(const std::string& graphName) +{ + return "render_graph_" + graphName; } + +} // namespace Falcor diff --git a/Source/Falcor/RenderGraph/RenderGraphIR.h b/Source/Falcor/RenderGraph/RenderGraphIR.h index 132ae9666..e745707bb 100644 --- a/Source/Falcor/RenderGraph/RenderGraphIR.h +++ b/Source/Falcor/RenderGraph/RenderGraphIR.h @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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,43 +34,29 @@ namespace Falcor { - class Scene; +class Scene; - class FALCOR_API RenderGraphIR - { - public: - using SharedPtr = std::shared_ptr; +class FALCOR_API RenderGraphIR +{ +public: + RenderGraphIR(const std::string& name, bool newGraph = true); - static std::string getFuncName(const std::string& graphName); - static SharedPtr create(const std::string& name, bool newGraph = true); + void createPass(const std::string& passClass, const std::string& passName, const Dictionary& = Dictionary()); + void updatePass(const std::string& passName, const Dictionary& dictionary); + void removePass(const std::string& passName); + void addEdge(const std::string& src, const std::string& dst); + void removeEdge(const std::string& src, const std::string& dst); + void markOutput(const std::string& name, const TextureChannelFlags mask = TextureChannelFlags::RGB); + void unmarkOutput(const std::string& name); - void addPass(const std::string& passClass, const std::string& passName, const Dictionary& = Dictionary()); - void updatePass(const std::string& passName, const Dictionary& dictionary); - void removePass(const std::string& passName); - void addEdge(const std::string& src, const std::string& dst); - void removeEdge(const std::string& src, const std::string& dst); - void markOutput(const std::string& name, const TextureChannelFlags mask = TextureChannelFlags::RGB); - void unmarkOutput(const std::string& name); - void loadPassLibrary(const std::string& name); + std::string getIR() { return mIR + mIndentation + (mIndentation.size() ? "return g\n" : "\n"); } - std::string getIR() { return mIR + mIndentation + (mIndentation.size() ? "return g\n" : "\n"); } + static std::string getFuncName(const std::string& graphName); - 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* kRenderPass; - static const char* kRenderGraph; - static const char* kUpdatePass; - static const char* kLoadPassLibrary; - static const char* kCreatePass; - private: - RenderGraphIR(const std::string& name, bool newGraph); - std::string mName; - std::string mIR; - std::string mIndentation; - std::string mGraphPrefix; - }; -} +private: + std::string mName; + std::string mIR; + std::string mIndentation; + std::string mGraphPrefix; +}; +} // namespace Falcor diff --git a/Source/Falcor/RenderGraph/RenderGraphImportExport.cpp b/Source/Falcor/RenderGraph/RenderGraphImportExport.cpp index 4af7156f3..a37e0e4fb 100644 --- a/Source/Falcor/RenderGraph/RenderGraphImportExport.cpp +++ b/Source/Falcor/RenderGraph/RenderGraphImportExport.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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,144 +32,152 @@ namespace Falcor { - namespace - { - void updateGraphStrings(std::string& graph, std::filesystem::path& path, std::string& func) - { - graph = graph.empty() ? "renderGraph" : graph; - path = path.empty() ? std::filesystem::path(graph + ".py") : path; - func = func.empty() ? RenderGraphIR::getFuncName(graph) : func; - } +namespace +{ +void updateGraphStrings(std::string& graph, std::filesystem::path& path, std::string& func) +{ + graph = graph.empty() ? "renderGraph" : graph; + path = path.empty() ? std::filesystem::path(graph + ".py") : path; + func = func.empty() ? RenderGraphIR::getFuncName(graph) : func; +} - void runScriptFile(const std::filesystem::path& path, const std::string& custom) - { - std::filesystem::path fullPath; - if (findFileInDataDirectories(path, fullPath)) - { - std::string script = readFile(fullPath) + custom; - Scripting::runScript(script); - } - else - { - throw RuntimeError("Can't find the file '{}'", path); - } - } +void runScriptFile(const std::filesystem::path& path, const std::string& custom) +{ + std::filesystem::path fullPath; + if (findFileInDataDirectories(path, fullPath)) + { + std::string script = readFile(fullPath) + custom; + Scripting::runScript(script); } - - bool loadFailed(std::exception e, const std::filesystem::path& path) + else { - logError(e.what()); - auto res = msgBox("Error", fmt::format("Error when importing graph from file '{}'\n{}\n\nWould you like to try and reload the file?", path.string(), e.what()), MsgBoxType::YesNo); - return (res == MsgBoxButton::No); + throw RuntimeError("Can't find the file '{}'", path); } +} +} // namespace + +bool loadFailed(std::exception e, const std::filesystem::path& path) +{ + logError(e.what()); + auto res = msgBox( + "Error", + fmt::format("Error when importing graph from file '{}'\n{}\n\nWould you like to try and reload the file?", path.string(), e.what()), + MsgBoxType::YesNo + ); + return (res == MsgBoxButton::No); +} - RenderGraph::SharedPtr RenderGraphImporter::import(std::string graphName, std::filesystem::path path, std::string funcName) +ref RenderGraphImporter::import(std::string graphName, std::filesystem::path path, std::string funcName) +{ + while (true) { - while(true) + try { - try - { - updateGraphStrings(graphName, path, funcName); - std::string custom; - if (funcName.size()) custom += "\n" + graphName + '=' + funcName + "()"; - // TODO: Rendergraph scripts should be executed in an isolated scripting context. - runScriptFile(path, custom); - - auto pGraph = Scripting::getDefaultContext().getObject(graphName); - if (!pGraph) throw("Unspecified error"); - - pGraph->setName(graphName); - return pGraph; - } - catch (const std::exception& e) - { - if (loadFailed(e, path)) return nullptr; - } + updateGraphStrings(graphName, path, funcName); + std::string custom; + if (funcName.size()) + custom += "\n" + graphName + '=' + funcName + "()"; + // TODO: Rendergraph scripts should be executed in an isolated scripting context. + runScriptFile(path, custom); + + auto pGraph = Scripting::getDefaultContext().getObject>(graphName); + if (!pGraph) + throw("Unspecified error"); + + pGraph->setName(graphName); + return pGraph; + } + catch (const std::exception& e) + { + if (loadFailed(e, path)) + return nullptr; } } +} - std::vector RenderGraphImporter::importAllGraphs(const std::filesystem::path& path) +std::vector> RenderGraphImporter::importAllGraphs(const std::filesystem::path& path) +{ + while (true) { - while(true) + try { - try - { - // TODO: Rendergraph scripts should be executed in an isolated scripting context. - runScriptFile(path, {}); - auto scriptObj = Scripting::getDefaultContext().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 (const std::exception& e) + // TODO: Rendergraph scripts should be executed in an isolated scripting context. + runScriptFile(path, {}); + auto scriptObj = Scripting::getDefaultContext().getObjects>(); + std::vector> res; + res.reserve(scriptObj.size()); + + for (const auto& s : scriptObj) { - if (loadFailed(e, path)) return {}; + s.obj->setName(s.name); + res.push_back(s.obj); } + + return res; + } + catch (const std::exception& e) + { + if (loadFailed(e, path)) + return {}; } } +} - std::string RenderGraphExporter::getFuncName(const std::string& graphName) +std::string RenderGraphExporter::getFuncName(const std::string& graphName) +{ + return RenderGraphIR::getFuncName(graphName); +} + +std::string RenderGraphExporter::getIR(const ref& pGraph) +{ + RenderGraphIR ir(pGraph->getName()); + + // Add the passes + for (const auto& node : pGraph->mNodeData) { - return RenderGraphIR::getFuncName(graphName); + const auto& nodeData = node.second; + ir.createPass(nodeData.pPass->getType(), nodeData.name, nodeData.pPass->getScriptingDictionary()); } - std::string RenderGraphExporter::getIR(const RenderGraph::SharedPtr& pGraph) + // Add the edges + for (const auto& edge : pGraph->mEdgeData) { - RenderGraphIR::SharedPtr pIR = RenderGraphIR::create(pGraph->getName()); - - // Add the passes - for (const auto& node : pGraph->mNodeData) - { - const auto& nodeData = node.second; - pIR->addPass(nodeData.pPass->getType(), nodeData.name, nodeData.pPass->getScriptingDictionary()); - } - - // Add the edges - for (const auto& edge : pGraph->mEdgeData) - { - const auto& edgeData = 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 + (edgeData.srcField.size() ? '.' + edgeData.srcField : edgeData.srcField); - std::string dst = dstPass + (edgeData.dstField.size() ? '.' + edgeData.dstField : edgeData.dstField); - pIR->addEdge(src, dst); - } + const auto& edgeData = 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 + (edgeData.srcField.size() ? '.' + edgeData.srcField : edgeData.srcField); + std::string dst = dstPass + (edgeData.dstField.size() ? '.' + edgeData.dstField : edgeData.dstField); + ir.addEdge(src, dst); + } - // Graph outputs - for (const auto& out : pGraph->mOutputs) + // Graph outputs + for (const auto& out : pGraph->mOutputs) + { + std::string str = pGraph->mNodeData[out.nodeId].name + '.' + out.field; + for (auto mask : out.masks) { - std::string str = pGraph->mNodeData[out.nodeId].name + '.' + out.field; - for (auto mask : out.masks) - { - pIR->markOutput(str, mask); - } + ir.markOutput(str, mask); } - - return pIR->getIR(); } - bool RenderGraphExporter::save(const std::shared_ptr& pGraph, std::filesystem::path path) - { - std::string ir = getIR(pGraph); - std::string funcName; - std::string graphName = pGraph->getName(); - updateGraphStrings(graphName, path, funcName); - - // Save it to file - std::ofstream f(path); - 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; - } + return ir.getIR(); +} + +bool RenderGraphExporter::save(const ref& pGraph, std::filesystem::path path) +{ + std::string ir = getIR(pGraph); + std::string funcName; + std::string graphName = pGraph->getName(); + updateGraphStrings(graphName, path, funcName); + + // Save it to file + std::ofstream f(path); + 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; } +} // namespace Falcor diff --git a/Source/Falcor/RenderGraph/RenderGraphImportExport.h b/Source/Falcor/RenderGraph/RenderGraphImportExport.h index 1ed2cbb1b..7e2447545 100644 --- a/Source/Falcor/RenderGraph/RenderGraphImportExport.h +++ b/Source/Falcor/RenderGraph/RenderGraphImportExport.h @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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,27 +34,30 @@ namespace Falcor { - class FALCOR_API RenderGraphImporter - { - public: - /** Import a graph from a file. - \param[in] graphName The name of the graph to import - \param[in] path The graphs file path. If the path is empty, the function will search for a file called `.py` - \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 RenderGraph::SharedPtr import(std::string graphName, std::filesystem::path path = {}, std::string funcName = {}); +class FALCOR_API RenderGraphImporter +{ +public: + /** + * Import a graph from a file. + * @param[in] graphName The name of the graph to import + * @param[in] path The graphs file path. If the path is empty, the function will search for a file called `.py` + * @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 ref import(std::string graphName, std::filesystem::path path = {}, std::string funcName = {}); - /** Import all the graphs found in the script's global namespace - */ - static std::vector importAllGraphs(const std::filesystem::path& path); - }; + /** + * Import all the graphs found in the script's global namespace + */ + static std::vector> importAllGraphs(const std::filesystem::path& path); +}; - class FALCOR_API RenderGraphExporter - { - public: - static std::string getIR(const RenderGraph::SharedPtr& pGraph); - static std::string getFuncName(const std::string& graphName); - static bool save(const RenderGraph::SharedPtr& pGraph, std::filesystem::path path = {}); - }; -} +class FALCOR_API RenderGraphExporter +{ +public: + static std::string getIR(const ref& pGraph); + static std::string getFuncName(const std::string& graphName); + static bool save(const ref& pGraph, std::filesystem::path path = {}); +}; +} // namespace Falcor diff --git a/Source/Falcor/RenderGraph/RenderGraphUI.cpp b/Source/Falcor/RenderGraph/RenderGraphUI.cpp index 1ee546e1b..fc6c08dcd 100644 --- a/Source/Falcor/RenderGraph/RenderGraphUI.cpp +++ b/Source/Falcor/RenderGraph/RenderGraphUI.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -37,1394 +37,1517 @@ namespace Falcor { - const float kUpdateTimeInterval = 2.0f; - const float kPinRadius = 7.0f; - - static const float kTimeTillPopup = 2.0f; - static const uint32_t kPinColor = 0xFFFFFFFF; - static const uint32_t kEdgesColor = 0xFFFFFFFF; - static const uint32_t kAutoResolveEdgesColor = 0xFF0104FF; - static const uint32_t kGraphOutputsColor = 0xAF0101FF; - static const uint32_t kExecutionEdgeColor = 0xFF0AF1FF; - static const std::string kInPrefix = "##inEx_ "; - static const std::string kOutPrefix = "##outEx_"; - - class RenderGraphUI::NodeGraphEditorGui : public ImGui::NodeGraphEditor - { - public: - NodeGraphEditorGui(RenderGraphUI* pRenderGraphUI) : mpRenderGraphUI(pRenderGraphUI) {} +const float kUpdateTimeInterval = 2.0f; +const float kPinRadius = 7.0f; + +static const float kTimeTillPopup = 2.0f; +static const uint32_t kPinColor = 0xFFFFFFFF; +static const uint32_t kEdgesColor = 0xFFFFFFFF; +static const uint32_t kAutoResolveEdgesColor = 0xFF0104FF; +static const uint32_t kGraphOutputsColor = 0xAF0101FF; +static const uint32_t kExecutionEdgeColor = 0xFF0AF1FF; +static const std::string kInPrefix = "##inEx_ "; +static const std::string kOutPrefix = "##outEx_"; + +class RenderGraphUI::NodeGraphEditorGui : public ImGui::NodeGraphEditor +{ +public: + NodeGraphEditorGui(RenderGraphUI* pRenderGraphUI) : mpRenderGraphUI(pRenderGraphUI) {} - // call on beginning a new frame - void setCurrentGui(Gui* pGui) { mpGui = pGui; } + // call on beginning a new frame + void setCurrentGui(Gui* pGui) { mpGui = pGui; } - Gui* getCurrentGui() const { FALCOR_ASSERT(mpGui); return mpGui; } + Gui* getCurrentGui() const + { + FALCOR_ASSERT(mpGui); + return mpGui; + } - void reset() { inited = false; } + void reset() { inited = false; } - float2 getOffsetPos() const { return { offset.x, offset.y }; } + float2 getOffsetPos() const { return {offset.x, offset.y}; } - ImGui::NodeLink& getLink(int32_t index) { return links[index]; } + ImGui::NodeLink& getLink(int32_t index) { return links[index]; } - void setLinkColor(uint32_t index, uint32_t col) { links[index].LinkColor = col; } + void setLinkColor(uint32_t index, uint32_t col) { links[index].LinkColor = col; } - RenderGraphUI* getRenderGraphUI() { return mpRenderGraphUI; } + RenderGraphUI* getRenderGraphUI() { return mpRenderGraphUI; } - void setPopupNode(ImGui::Node* pFocusedNode) { mpFocusedNode = pFocusedNode; } + void setPopupNode(ImGui::Node* pFocusedNode) { mpFocusedNode = pFocusedNode; } - ImGui::Node* getPopupNode() { return mpFocusedNode; } + ImGui::Node* getPopupNode() { return mpFocusedNode; } - void setPopupPin(uint32_t pinIndex, bool isInput) + void setPopupPin(uint32_t pinIndex, bool isInput) + { + if (ImGui::IsAnyMouseDown()) { - if (ImGui::IsAnyMouseDown()) - { - mPopupPinHoverTime = 0.0f; - mPinIndexToDisplay = -1; - } - - if ((pinIndex != -1) )//&& pinIndex != mPinIndexToDisplay) - { - std::chrono::system_clock::time_point thisTime = std::chrono::system_clock::now(); - float timeDiff = (std::chrono::duration(thisTime - mLastTime)).count(); - if(timeDiff < kTimeTillPopup) mPopupPinHoverTime += timeDiff; - mLastTime = thisTime; - if (mPopupPinHoverTime < kTimeTillPopup) return; - } - - if (mPopupPinHoverTime >= kTimeTillPopup) mPopupPinHoverTime = 0.0f; - mPinIndexToDisplay = pinIndex; mPopupPinIsInput = isInput; + mPopupPinHoverTime = 0.0f; + mPinIndexToDisplay = -1; } - void deselectPopupPin() + if ((pinIndex != -1)) //&& pinIndex != mPinIndexToDisplay) { std::chrono::system_clock::time_point thisTime = std::chrono::system_clock::now(); float timeDiff = (std::chrono::duration(thisTime - mLastTime)).count(); - if (timeDiff < kTimeTillPopup) mNothingHoveredTime += timeDiff; - mLastTimeDeselect = thisTime; + if (timeDiff < kTimeTillPopup) + mPopupPinHoverTime += timeDiff; + mLastTime = thisTime; + if (mPopupPinHoverTime < kTimeTillPopup) + return; + } - if (mNothingHoveredTime >= kTimeTillPopup) - { - mNothingHoveredTime = 0.0f; - mPinIndexToDisplay = static_cast(-1); - } + if (mPopupPinHoverTime >= kTimeTillPopup) + mPopupPinHoverTime = 0.0f; + mPinIndexToDisplay = pinIndex; + mPopupPinIsInput = isInput; + } + + void deselectPopupPin() + { + std::chrono::system_clock::time_point thisTime = std::chrono::system_clock::now(); + float timeDiff = (std::chrono::duration(thisTime - mLastTime)).count(); + if (timeDiff < kTimeTillPopup) + mNothingHoveredTime += timeDiff; + mLastTimeDeselect = thisTime; + + if (mNothingHoveredTime >= kTimeTillPopup) + { + mNothingHoveredTime = 0.0f; + mPinIndexToDisplay = static_cast(-1); } + } - uint32_t getPopupPinIndex() const { return mPinIndexToDisplay; } + uint32_t getPopupPinIndex() const { return mPinIndexToDisplay; } - bool isPopupPinInput() const { return mPopupPinIsInput; } + bool isPopupPinInput() const { return mPopupPinIsInput; } - ImGui::Node*& getNodeFromID(uint32_t nodeID) { return mpIDtoNode[nodeID]; } + ImGui::Node*& getNodeFromID(uint32_t nodeID) { return mpIDtoNode[nodeID]; } - // wraps around creating link to avoid setting static flag - uint32_t addLinkFromGraph(ImGui::Node* inputNode, int input_slot, ImGui::Node* outputNode, int output_slot, bool checkIfAlreadyPresent = false, ImU32 col = ImColor(200, 200, 100)) - { - // tell addLink to call a different callback func - auto oldCallback = linkCallback; - linkCallback = setLinkFromGraph; + // wraps around creating link to avoid setting static flag + uint32_t addLinkFromGraph( + ImGui::Node* inputNode, + int input_slot, + ImGui::Node* outputNode, + int output_slot, + bool checkIfAlreadyPresent = false, + ImU32 col = ImColor(200, 200, 100) + ) + { + // tell addLink to call a different callback func + auto oldCallback = linkCallback; + linkCallback = setLinkFromGraph; - bool insert = addLink(inputNode, input_slot, outputNode, output_slot, checkIfAlreadyPresent, col); - linkCallback = oldCallback; - return insert ? (links.size() - 1) : uint32_t(-1); - } + bool insert = addLink(inputNode, input_slot, outputNode, output_slot, checkIfAlreadyPresent, col); + linkCallback = oldCallback; + return insert ? (links.size() - 1) : uint32_t(-1); + } - ImGui::Node* addAndInitNode(int nodeType, const std::string& name, const std::string& outputsString, const std::string& inputsString, uint32_t guiNodeID, RenderPass* pCurrentRenderPass, const ImVec2& pos = ImVec2(0, 0)); + ImGui::Node* addAndInitNode( + int nodeType, + const std::string& name, + const std::string& outputsString, + const std::string& inputsString, + uint32_t guiNodeID, + RenderPass* pCurrentRenderPass, + const ImVec2& pos = ImVec2(0, 0) + ); + + void setNodeCallbackFunc(NodeCallback cb, void* pUserData) + { + mpCBUserData = pUserData; + setNodeCallback(cb); + } - void setNodeCallbackFunc(NodeCallback cb, void* pUserData) - { - mpCBUserData = pUserData; - setNodeCallback(cb); - } + // callback function defined in derived class definition to access data without making class public to the rest of Falcor + static void setNode(ImGui::Node*& node, ImGui::NodeGraphEditor::NodeState state, ImGui::NodeGraphEditor& editor); + // callback for ImGui setting link between to nodes in the visual interface + static void setLinkFromGui(ImGui::NodeLink& link, ImGui::NodeGraphEditor::LinkState state, ImGui::NodeGraphEditor& editor); + static void setLinkFromGraph(ImGui::NodeLink& link, ImGui::NodeGraphEditor::LinkState state, ImGui::NodeGraphEditor& editor); + static ImGui::Node* createNode(int, const ImVec2& pos, const ImGui::NodeGraphEditor&); + + RenderGraphUI* mpRenderGraphUI; + +private: + friend class RenderGraphNode; + + float mPopupPinHoverTime = 0.0f; + float mNothingHoveredTime = 0.0f; + std::chrono::system_clock::time_point mLastTime = std::chrono::system_clock::now(); + std::chrono::system_clock::time_point mLastTimeDeselect = std::chrono::system_clock::now(); + Gui* mpGui = nullptr; + void* mpCBUserData = nullptr; + ImGui::Node* mpFocusedNode = nullptr; + uint32_t mPinIndexToDisplay = uint32_t(-1); + bool mPopupPinIsInput = false; + std::unordered_map mpIDtoNode; +}; + +class RenderGraphUI::RenderGraphNode : public ImGui::Node +{ +public: + bool mDisplayProperties; + bool mOutputPinConnected[IMGUINODE_MAX_OUTPUT_SLOTS]; + bool mInputPinConnected[IMGUINODE_MAX_INPUT_SLOTS]; + RenderPass* mpRenderPass; - // callback function defined in derived class definition to access data without making class public to the rest of Falcor - static void setNode(ImGui::Node*& node, ImGui::NodeGraphEditor::NodeState state, ImGui::NodeGraphEditor& editor); - // callback for ImGui setting link between to nodes in the visual interface - static void setLinkFromGui( ImGui::NodeLink& link, ImGui::NodeGraphEditor::LinkState state, ImGui::NodeGraphEditor& editor); - static void setLinkFromGraph( ImGui::NodeLink& link, ImGui::NodeGraphEditor::LinkState state, ImGui::NodeGraphEditor& editor); - static ImGui::Node* createNode(int, const ImVec2& pos, const ImGui::NodeGraphEditor&); - - RenderGraphUI* mpRenderGraphUI; - - private: - friend class RenderGraphNode; - - float mPopupPinHoverTime = 0.0f; - float mNothingHoveredTime = 0.0f; - std::chrono::system_clock::time_point mLastTime = std::chrono::system_clock::now(); - std::chrono::system_clock::time_point mLastTimeDeselect = std::chrono::system_clock::now(); - Gui* mpGui = nullptr; - void* mpCBUserData = nullptr; - ImGui::Node* mpFocusedNode = nullptr; - uint32_t mPinIndexToDisplay = uint32_t(-1); - bool mPopupPinIsInput = false; - std::unordered_map mpIDtoNode; - }; - - class RenderGraphUI::RenderGraphNode : public ImGui::Node + bool pinIsConnected(uint32_t id, bool isInput) { - public: - bool mDisplayProperties; - bool mOutputPinConnected[IMGUINODE_MAX_OUTPUT_SLOTS]; - bool mInputPinConnected[IMGUINODE_MAX_INPUT_SLOTS]; - RenderPass* mpRenderPass; + FALCOR_ASSERT(isInput ? id < IMGUINODE_MAX_INPUT_SLOTS : id < IMGUINODE_MAX_OUTPUT_SLOTS); + return isInput ? mInputPinConnected[id] : mOutputPinConnected[id]; + } - bool pinIsConnected(uint32_t id, bool isInput) - { - FALCOR_ASSERT(isInput ? id < IMGUINODE_MAX_INPUT_SLOTS : id < IMGUINODE_MAX_OUTPUT_SLOTS); - return isInput ? mInputPinConnected[id] : mOutputPinConnected[id]; - } + void setPinColor(uint32_t color, uint32_t index, bool isInput = false) + { + if (isInput) + inputColors[index] = color; + else + outputColors[index] = color; + } - void setPinColor(uint32_t color, uint32_t index, bool isInput = false) - { - if (isInput) inputColors[index] = color; - else outputColors[index] = color; - } + std::string getInputName(uint32_t index) { return InputNames[index]; } - std::string getInputName(uint32_t index) - { - return InputNames[index]; - } + std::string getOutputName(uint32_t index) { return OutputNames[index]; } - std::string getOutputName(uint32_t index) - { - return OutputNames[index]; - } + ImGui::FieldInfoVector& getFields() { return fields; } - ImGui::FieldInfoVector& getFields() - { - return fields; - } + // Allow the renderGraphUI to set the position of each node + void setPos(const float2& pos) + { + Pos.x = pos.x; + Pos.y = pos.y; + } - // Allow the renderGraphUI to set the position of each node - void setPos(const float2& pos) - { - Pos.x = pos.x; - Pos.y = pos.y; - } + float2 getPos() { return {Pos.x, Pos.y}; } - float2 getPos() - { - return {Pos.x, Pos.y}; - } + // render Gui within the nodes + static bool renderUI(ImGui::FieldInfo& field) + { + // with this library there is no way of modifying the positioning of the labels on the node + // manually making labels to align correctly from within the node + + // grab all of the fields again + RenderGraphNode* pCurrentNode = static_cast(field.userData); + RenderGraphUI::NodeGraphEditorGui* pGraphEditorGui = + static_cast(&pCurrentNode->getNodeGraphEditor()); + if (pGraphEditorGui->isInited()) + return false; - // render Gui within the nodes - static bool renderUI(ImGui::FieldInfo& field) + Gui* pGui = pGraphEditorGui->getCurrentGui(); + RenderPass* pRenderPass = static_cast(pCurrentNode->mpRenderPass); + int32_t paddingSpace = std::max(pCurrentNode->OutputsCount, pCurrentNode->InputsCount) / 2 + 1; + ImVec2 oldScreenPos = ImGui::GetCursorScreenPos(); + ImVec2 currentScreenPos{ + pGraphEditorGui->offset.x + pCurrentNode->Pos.x * ImGui::GetCurrentWindow()->FontWindowScale, + pGraphEditorGui->offset.y + pCurrentNode->Pos.y * ImGui::GetCurrentWindow()->FontWindowScale}; + ImVec2 pinRectBoundsOffsetx{-kPinRadius * 2.0f, kPinRadius * 4.0f}; + float slotNum = 1.0f; + float pinOffsetx = kPinRadius * 2.0f; + uint32_t pinCount = static_cast(pCurrentNode->InputsCount); + bool isInputs = true; + + std::string dummyText; + dummyText.resize(32, ' '); + 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) { - // with this library there is no way of modifying the positioning of the labels on the node - // manually making labels to align correctly from within the node - - // grab all of the fields again - RenderGraphNode* pCurrentNode = static_cast(field.userData); - RenderGraphUI::NodeGraphEditorGui* pGraphEditorGui = static_cast(&pCurrentNode->getNodeGraphEditor()); - if (pGraphEditorGui->isInited()) return false; - - Gui* pGui = pGraphEditorGui->getCurrentGui(); - RenderPass* pRenderPass = static_cast(pCurrentNode->mpRenderPass); - int32_t paddingSpace = glm::max(pCurrentNode->OutputsCount, pCurrentNode->InputsCount) / 2 + 1; - ImVec2 oldScreenPos = ImGui::GetCursorScreenPos(); - ImVec2 currentScreenPos{ pGraphEditorGui->offset.x + pCurrentNode->Pos.x * ImGui::GetCurrentWindow()->FontWindowScale, - pGraphEditorGui->offset.y + pCurrentNode->Pos.y * ImGui::GetCurrentWindow()->FontWindowScale }; - ImVec2 pinRectBoundsOffsetx{ -kPinRadius * 2.0f, kPinRadius * 4.0f }; - float slotNum = 1.0f; - float pinOffsetx = kPinRadius * 2.0f; - uint32_t pinCount = static_cast(pCurrentNode->InputsCount); - bool isInputs = true; - - std::string dummyText; dummyText.resize(32, ' '); - 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) + for (uint32_t j = 0; j < 2; ++j) + { + for (uint32_t i = 0; i < pinCount; ++i) { - ImGui::TextUnformatted(dummyText.c_str()); - } + // custom pins as an extension of the built ones + ImVec2 inputPos = currentScreenPos; + inputPos.y += pCurrentNode->Size.y * ((i + 1) / static_cast(pinCount + 1)); + ImU32 pinColor = isInputs ? pCurrentNode->inputColors[i] : pCurrentNode->outputColors[i]; - for (uint32_t j = 0; j < 2; ++j) - { - for (uint32_t i = 0; i < pinCount; ++i) + // fill in circle for the pin if connected to a link + if (pCurrentNode->pinIsConnected(i, isInputs)) { - // custom pins as an extension of the built ones - ImVec2 inputPos = currentScreenPos; - inputPos.y += pCurrentNode->Size.y * ((i + 1) / static_cast(pinCount + 1)); - ImU32 pinColor = isInputs ? pCurrentNode->inputColors[i] : pCurrentNode->outputColors[i]; - - // fill in circle for the pin if connected to a link - if (pCurrentNode->pinIsConnected(i, isInputs)) - { - ImGui::GetWindowDrawList()->AddCircleFilled(ImVec2(inputPos.x, inputPos.y), kPinRadius,pinColor); - } - - if (ImGui::IsMouseHoveringRect(ImVec2(inputPos.x + pinRectBoundsOffsetx.x, inputPos.y - kPinRadius), ImVec2(inputPos.x + pinRectBoundsOffsetx.y, inputPos.y + kPinRadius))) - { - uint32_t hoveredPinColor = (pinColor == kGraphOutputsColor) ? (pinColor | 0xFF000000) : ImGui::GetColorU32(pGraphEditorGui->GetStyle().color_node_title); - ImGui::GetWindowDrawList()->AddCircleFilled(ImVec2(inputPos.x, inputPos.y), kPinRadius, hoveredPinColor); - - if (pRenderPass) - { - pGraphEditorGui->setPopupNode(pCurrentNode); - pGraphEditorGui->setPopupPin(i, !static_cast(j)); - } - } - else - { - ImGui::GetWindowDrawList()->AddCircle(ImVec2(inputPos.x, inputPos.y), kPinRadius, pinColor); - } + ImGui::GetWindowDrawList()->AddCircleFilled(ImVec2(inputPos.x, inputPos.y), kPinRadius, pinColor); + } - auto pText = isInputs ? pCurrentNode->InputNames[i] : pCurrentNode->OutputNames[i]; - bool drawLabel = !(pText[0] == '#'); + if (ImGui::IsMouseHoveringRect( + ImVec2(inputPos.x + pinRectBoundsOffsetx.x, inputPos.y - kPinRadius), + ImVec2(inputPos.x + pinRectBoundsOffsetx.y, inputPos.y + kPinRadius) + )) + { + uint32_t hoveredPinColor = (pinColor == kGraphOutputsColor) + ? (pinColor | 0xFF000000) + : ImGui::GetColorU32(pGraphEditorGui->GetStyle().color_node_title); + ImGui::GetWindowDrawList()->AddCircleFilled(ImVec2(inputPos.x, inputPos.y), kPinRadius, hoveredPinColor); - if (pinColor == kGraphOutputsColor) + if (pRenderPass) { - ImVec2 arrowPoints[3] = { { inputPos.x + kPinRadius * 3.0f / 2.0f, inputPos.y + kPinRadius }, - { inputPos.x + kPinRadius * 3.0f / 2.0f + kPinRadius, inputPos.y }, - { inputPos.x + kPinRadius * 3.0f / 2.0f, inputPos.y - kPinRadius } }; - ImGui::GetWindowDrawList()->AddPolyline(arrowPoints, 3, pinColor, false, 3.0f); - } - else if (!drawLabel && pinColor == kExecutionEdgeColor) - { - // we can draw anything special for execution edge inputs here + pGraphEditorGui->setPopupNode(pCurrentNode); + pGraphEditorGui->setPopupPin(i, !static_cast(j)); } + } + else + { + ImGui::GetWindowDrawList()->AddCircle(ImVec2(inputPos.x, inputPos.y), kPinRadius, pinColor); + } - ImGui::SetCursorScreenPos({ inputPos.x + pinOffsetx - ((pinOffsetx < 0.0f) ? ImGui::CalcTextSize(isInputs ? pCurrentNode->InputNames[i] : pCurrentNode->OutputNames[i]).x : 0.0f), inputPos.y - kPinRadius }); + auto pText = isInputs ? pCurrentNode->InputNames[i] : pCurrentNode->OutputNames[i]; + bool drawLabel = !(pText[0] == '#'); - slotNum++; - if (drawLabel) ImGui::TextUnformatted(pText); + if (pinColor == kGraphOutputsColor) + { + ImVec2 arrowPoints[3] = { + {inputPos.x + kPinRadius * 3.0f / 2.0f, inputPos.y + kPinRadius}, + {inputPos.x + kPinRadius * 3.0f / 2.0f + kPinRadius, inputPos.y}, + {inputPos.x + kPinRadius * 3.0f / 2.0f, inputPos.y - kPinRadius}}; + ImGui::GetWindowDrawList()->AddPolyline(arrowPoints, 3, pinColor, false, 3.0f); + } + else if (!drawLabel && pinColor == kExecutionEdgeColor) + { + // we can draw anything special for execution edge inputs here } - // reset and set up offsets for the output pins - slotNum = 1.0f; - currentScreenPos.x += pCurrentNode->Size.x; - pinOffsetx *= -1.0f; - pinRectBoundsOffsetx.y = kPinRadius; - pinCount = static_cast(pCurrentNode->OutputsCount); - isInputs = false; - } - - ImGui::SetCursorScreenPos(oldScreenPos); + ImGui::SetCursorScreenPos( + {inputPos.x + pinOffsetx - + ((pinOffsetx < 0.0f) ? ImGui::CalcTextSize(isInputs ? pCurrentNode->InputNames[i] : pCurrentNode->OutputNames[i]).x + : 0.0f), + inputPos.y - kPinRadius} + ); - for (int32_t i = 0; i < paddingSpace; ++i) - { - ImGui::TextUnformatted(dummyText.c_str()); + slotNum++; + if (drawLabel) + ImGui::TextUnformatted(pText); } - ImGui::TextUnformatted(dummyText.c_str()); - - return false; + // reset and set up offsets for the output pins + slotNum = 1.0f; + currentScreenPos.x += pCurrentNode->Size.x; + pinOffsetx *= -1.0f; + pinRectBoundsOffsetx.y = kPinRadius; + pinCount = static_cast(pCurrentNode->OutputsCount); + isInputs = false; } - void initialize(const std::string& name, const std::string& outputsString, - const std::string& inputsString, uint32_t guiNodeID, RenderPass* pRenderPass) - { - init(name.c_str(), Pos, inputsString.c_str(), outputsString.c_str(), guiNodeID); + ImGui::SetCursorScreenPos(oldScreenPos); - if (pRenderPass) - { - mpRenderPass = pRenderPass; - const float4 nodeColor = Gui::pickUniqueColor(pRenderPass->getType()); - overrideTitleBgColor = ImGui::GetColorU32({ nodeColor.x, nodeColor.y, nodeColor.z, nodeColor.w }); - } - - bool isInputs = true; - uint32_t pinCount = static_cast(InputsCount); - for (uint32_t j = 0; j < 2; ++j) - { - for (uint32_t i = 0; i < pinCount; ++i) - { - if (isInputs) inputColors[i] = ImColor(150, 150, 150, 150); - else outputColors[i] = ImColor(150, 150, 150, 150); - } - pinCount = static_cast(OutputsCount); - isInputs = false; - } - } - - static RenderGraphNode* create(const ImVec2& pos) + for (int32_t i = 0; i < paddingSpace; ++i) { - RenderGraphNode* node = (RenderGraphNode*)ImGui::MemAlloc(sizeof(RenderGraphNode)); - IM_PLACEMENT_NEW(node) RenderGraphNode(); - - node->fields.addFieldCustom(static_cast(renderUI), nullptr, node); - node->Pos = pos; - return node; + ImGui::TextUnformatted(dummyText.c_str()); } - private: - }; - ImGui::Node* RenderGraphUI::NodeGraphEditorGui::addAndInitNode(int nodeType, const std::string& name, - const std::string& outputsString, const std::string& inputsString, uint32_t guiNodeID, - RenderPass* pCurrentRenderPass, const ImVec2& pos) - { - // set init data for new node to obtain - RenderGraphNode* newNode = static_cast(addNode(nodeType, pos, nullptr)); - newNode->initialize(name, outputsString, inputsString, guiNodeID, pCurrentRenderPass); - return newNode; + ImGui::TextUnformatted(dummyText.c_str()); + + return false; } - void RenderGraphUI::NodeGraphEditorGui::setLinkFromGui(ImGui::NodeLink& link, ImGui::NodeGraphEditor::LinkState state, ImGui::NodeGraphEditor& editor) + void initialize( + const std::string& name, + const std::string& outputsString, + const std::string& inputsString, + uint32_t guiNodeID, + RenderPass* pRenderPass + ) { - if (state == ImGui::NodeGraphEditor::LinkState::LS_ADDED) - { - RenderGraphNode* inputNode = static_cast(link.InputNode), - *outputNode = static_cast(link.OutputNode); - RenderGraphUI::NodeGraphEditorGui* pGraphEditorGui = static_cast(&editor); - - bool addStatus = false; - std::string outputName = inputNode->getOutputName(link.InputSlot); - addStatus = pGraphEditorGui->getRenderGraphUI()->addLink( - inputNode->getName(), outputNode->getName(), outputName, outputNode->getInputName(link.OutputSlot), link.LinkColor); + init(name.c_str(), Pos, inputsString.c_str(), outputsString.c_str(), guiNodeID); - // immediately remove link if it is not a legal edge in the render graph - if (!addStatus && !editor.isInited()) // only call after graph is setup - { - // does not call link callback surprisingly enough - editor.removeLink(link.InputNode, link.InputSlot, link.OutputNode, link.OutputSlot); - return; - } - - if (outputName[0] == '#') - { - static_cast(link.InputNode)->setPinColor(link.LinkColor, link.InputSlot, false); - static_cast(link.OutputNode)->setPinColor(link.LinkColor, link.OutputSlot, true); - } + if (pRenderPass) + { + mpRenderPass = pRenderPass; + const float4 nodeColor = Gui::pickUniqueColor(pRenderPass->getType()); + overrideTitleBgColor = ImGui::GetColorU32({nodeColor.x, nodeColor.y, nodeColor.z, nodeColor.w}); } - } - // callback for ImGui setting link from render graph changes - void RenderGraphUI::NodeGraphEditorGui::setLinkFromGraph(ImGui::NodeLink& link, ImGui::NodeGraphEditor::LinkState state, ImGui::NodeGraphEditor& editor) - { - if (state == ImGui::NodeGraphEditor::LinkState::LS_ADDED) + bool isInputs = true; + uint32_t pinCount = static_cast(InputsCount); + for (uint32_t j = 0; j < 2; ++j) { - bool addStatus = false; - - // immediately remove link if it is not a legal edge in the render graph - if (!addStatus && !editor.isInited()) // only call after graph is setup + for (uint32_t i = 0; i < pinCount; ++i) { - // does not call link callback surprisingly enough - editor.removeLink(link.InputNode, link.InputSlot, link.OutputNode, link.OutputSlot); - return; + if (isInputs) + inputColors[i] = ImColor(150, 150, 150, 150); + else + outputColors[i] = ImColor(150, 150, 150, 150); } + pinCount = static_cast(OutputsCount); + isInputs = false; } } - ImGui::Node* RenderGraphUI::NodeGraphEditorGui::createNode(int, const ImVec2& pos, const ImGui::NodeGraphEditor&) + static RenderGraphNode* create(const ImVec2& pos) { - return RenderGraphUI::RenderGraphNode::create(pos); - } + RenderGraphNode* node = (RenderGraphNode*)ImGui::MemAlloc(sizeof(RenderGraphNode)); + IM_PLACEMENT_NEW(node) RenderGraphNode(); - void RenderGraphUI::addRenderPass(const std::string& name, const std::string& nodeTypeName) - { - mpIr->addPass(nodeTypeName, name); - mShouldUpdate = true; + node->fields.addFieldCustom(static_cast(renderUI), nullptr, node); + node->Pos = pos; + return node; } - void RenderGraphUI::addOutput(const std::string& outputParam) - { - size_t offset = outputParam.find('.'); - std::string outputPass = outputParam.substr(0, offset); - std::string outputField = outputParam.substr(offset + 1, outputParam.size()); +private: +}; + +ImGui::Node* RenderGraphUI::NodeGraphEditorGui::addAndInitNode( + int nodeType, + const std::string& name, + const std::string& outputsString, + const std::string& inputsString, + uint32_t guiNodeID, + RenderPass* pCurrentRenderPass, + const ImVec2& pos +) +{ + // set init data for new node to obtain + RenderGraphNode* newNode = static_cast(addNode(nodeType, pos, nullptr)); + newNode->initialize(name, outputsString, inputsString, guiNodeID, pCurrentRenderPass); + return newNode; +} - const auto passUIIt = mRenderPassUI.find(outputPass); - if (passUIIt == mRenderPassUI.end()) +void RenderGraphUI::NodeGraphEditorGui::setLinkFromGui( + ImGui::NodeLink& link, + ImGui::NodeGraphEditor::LinkState state, + ImGui::NodeGraphEditor& editor +) +{ + if (state == ImGui::NodeGraphEditor::LinkState::LS_ADDED) + { + RenderGraphNode *inputNode = static_cast(link.InputNode), + *outputNode = static_cast(link.OutputNode); + RenderGraphUI::NodeGraphEditorGui* pGraphEditorGui = static_cast(&editor); + + bool addStatus = false; + std::string outputName = inputNode->getOutputName(link.InputSlot); + addStatus = pGraphEditorGui->getRenderGraphUI()->addLink( + inputNode->getName(), outputNode->getName(), outputName, outputNode->getInputName(link.OutputSlot), link.LinkColor + ); + + // immediately remove link if it is not a legal edge in the render graph + if (!addStatus && !editor.isInited()) // only call after graph is setup { - msgBox("Error", "Error setting graph output. Can't find node name."); + // does not call link callback surprisingly enough + editor.removeLink(link.InputNode, link.InputSlot, link.OutputNode, link.OutputSlot); return; } - auto& passUI = passUIIt->second; - const auto outputIt = passUI.mNameToIndexOutput.find(outputField); - if (outputIt == passUI.mNameToIndexOutput.end()) + + if (outputName[0] == '#') { - msgBox("Error", "Error setting graph output. Can't find output name."); - return; + static_cast(link.InputNode)->setPinColor(link.LinkColor, link.InputSlot, false); + static_cast(link.OutputNode)->setPinColor(link.LinkColor, link.OutputSlot, true); } - passUI.mOutputPins[outputIt->second].mIsGraphOutput = true; - mpIr->markOutput(outputParam); - mRebuildDisplayData = true; - mShouldUpdate = true; } +} - void RenderGraphUI::addOutput(const std::string& outputPass, const std::string& outputField) +// callback for ImGui setting link from render graph changes +void RenderGraphUI::NodeGraphEditorGui::setLinkFromGraph( + ImGui::NodeLink& link, + ImGui::NodeGraphEditor::LinkState state, + ImGui::NodeGraphEditor& editor +) +{ + if (state == ImGui::NodeGraphEditor::LinkState::LS_ADDED) { - std::string outputParam = outputPass + "." + outputField; - addOutput(outputParam); - } + bool addStatus = false; - void RenderGraphUI::removeOutput(const std::string& outputPass, const std::string& outputField) - { - std::string outputParam = outputPass + "." + outputField; - mpIr->unmarkOutput(outputParam); - auto& passUI = mRenderPassUI[outputPass]; - passUI.mOutputPins[passUI.mNameToIndexOutput[outputField]].mIsGraphOutput = false; - mShouldUpdate = true; + // immediately remove link if it is not a legal edge in the render graph + if (!addStatus && !editor.isInited()) // only call after graph is setup + { + // does not call link callback surprisingly enough + editor.removeLink(link.InputNode, link.InputSlot, link.OutputNode, link.OutputSlot); + return; + } } +} - bool RenderGraphUI::autoResolveWarning(const std::string& srcString, const std::string& dstString) - { - std::string warningMsg = std::string("Warning: Edge ") + srcString + " - " + dstString + " can auto-resolve.\n"; - MsgBoxButton button = msgBox("Warning", warningMsg, MsgBoxType::OkCancel); +ImGui::Node* RenderGraphUI::NodeGraphEditorGui::createNode(int, const ImVec2& pos, const ImGui::NodeGraphEditor&) +{ + return RenderGraphUI::RenderGraphNode::create(pos); +} - if (button == MsgBoxButton::Ok) - { - mLogString += warningMsg; - return true; - } +void RenderGraphUI::addRenderPass(const std::string& name, const std::string& nodeTypeName) +{ + mpIr->createPass(nodeTypeName, name); + mShouldUpdate = true; +} - return false; - } +void RenderGraphUI::addOutput(const std::string& outputParam) +{ + size_t offset = outputParam.find('.'); + std::string outputPass = outputParam.substr(0, offset); + std::string outputField = outputParam.substr(offset + 1, outputParam.size()); - bool RenderGraphUI::addLink(const std::string& srcPass, const std::string& dstPass, const std::string& srcField, const std::string& dstField, uint32_t& color) + const auto passUIIt = mRenderPassUI.find(outputPass); + if (passUIIt == mRenderPassUI.end()) { - // outputs warning if edge could not be created - std::string srcString = srcPass + (srcField[0] == '#' ? "" : ".") + srcField; - std::string dstString = dstPass + (dstField[0] == '#' ? "" : ".") + dstField; - bool canCreateEdge = (mpRenderGraph->getEdge(srcString, dstString) == ((uint32_t)-1)); - if (!canCreateEdge) return canCreateEdge; - - // update the ui to reflect the connections. This data is used for removal - RenderPassUI& srcRenderPassUI = mRenderPassUI[srcPass]; - RenderPassUI& dstRenderPassUI = mRenderPassUI[dstPass]; - const auto outputIt = srcRenderPassUI.mNameToIndexOutput.find(srcField); - const auto inputIt = dstRenderPassUI.mNameToIndexInput.find(dstField); - // if one filed is empty, check that the other is as well - if ((dstField[0] == '#') || (srcField[0] == '#')) - canCreateEdge &= (srcField[0] == '#') && (dstField[0] == '#'); - // check that link could exist - canCreateEdge &= (outputIt != srcRenderPassUI.mNameToIndexOutput.end()) && - (inputIt != dstRenderPassUI.mNameToIndexInput.end()); - // check that the input is not already connected - canCreateEdge &= (mInputPinStringToLinkID.find(dstString) == mInputPinStringToLinkID.end()); + msgBox("Error", "Error setting graph output. Can't find node name."); + return; + } + auto& passUI = passUIIt->second; + const auto outputIt = passUI.mNameToIndexOutput.find(outputField); + if (outputIt == passUI.mNameToIndexOutput.end()) + { + msgBox("Error", "Error setting graph output. Can't find output name."); + return; + } + passUI.mOutputPins[outputIt->second].mIsGraphOutput = true; + mpIr->markOutput(outputParam); + mRebuildDisplayData = true; + mShouldUpdate = true; +} +void RenderGraphUI::addOutput(const std::string& outputPass, const std::string& outputField) +{ + std::string outputParam = outputPass + "." + outputField; + addOutput(outputParam); +} - if (canCreateEdge) - { - uint32_t srcPinIndex = outputIt->second; - uint32_t dstPinIndex = inputIt->second; - srcRenderPassUI.mOutputPins[srcPinIndex].mConnectedPinName = dstField; - srcRenderPassUI.mOutputPins[srcPinIndex].mConnectedNodeName = dstPass; - dstRenderPassUI.mInputPins[dstPinIndex].mConnectedPinName = srcField; - dstRenderPassUI.mInputPins[dstPinIndex].mConnectedNodeName = srcPass; - - if (!(dstField[0] == '#')) - { - color = kEdgesColor; - - // TODO: Auto-resolve is not fully working and will be removed (see #1275). - //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)); - //if (canAutoResolve && mDisplayAutoResolvePopup) canCreateEdge = autoResolveWarning(srcString, dstString); - //color = canAutoResolve ? kAutoResolveEdgesColor : kEdgesColor; - } +void RenderGraphUI::removeOutput(const std::string& outputPass, const std::string& outputField) +{ + std::string outputParam = outputPass + "." + outputField; + mpIr->unmarkOutput(outputParam); + auto& passUI = mRenderPassUI[outputPass]; + passUI.mOutputPins[passUI.mNameToIndexOutput[outputField]].mIsGraphOutput = false; + mShouldUpdate = true; +} - if (canCreateEdge) - { +bool RenderGraphUI::autoResolveWarning(const std::string& srcString, const std::string& dstString) +{ + std::string warningMsg = std::string("Warning: Edge ") + srcString + " - " + dstString + " can auto-resolve.\n"; + MsgBoxButton button = msgBox("Warning", warningMsg, MsgBoxType::OkCancel); - mShouldUpdate = true; + if (button == MsgBoxButton::Ok) + { + mLogString += warningMsg; + return true; + } - if (dstField[0] == '#') - { - // rebuilds data to avoid repeated code - mRebuildDisplayData = true; - color = kExecutionEdgeColor; - mpIr->addEdge(srcPass, dstPass); - } - else - { - mpIr->addEdge(srcString, dstString); - } - } - } - else - { - mLogString += "Unable to create edge for graph. \n"; - } + return false; +} +bool RenderGraphUI::addLink( + const std::string& srcPass, + const std::string& dstPass, + const std::string& srcField, + const std::string& dstField, + uint32_t& color +) +{ + // outputs warning if edge could not be created + std::string srcString = srcPass + (srcField[0] == '#' ? "" : ".") + srcField; + std::string dstString = dstPass + (dstField[0] == '#' ? "" : ".") + dstField; + bool canCreateEdge = (mpRenderGraph->getEdge(srcString, dstString) == ((uint32_t)-1)); + if (!canCreateEdge) return canCreateEdge; - } - void RenderGraphUI::removeEdge(const std::string& srcPass, const std::string& dstPass, const std::string& srcField, const std::string& dstField) + // update the ui to reflect the connections. This data is used for removal + RenderPassUI& srcRenderPassUI = mRenderPassUI[srcPass]; + RenderPassUI& dstRenderPassUI = mRenderPassUI[dstPass]; + const auto outputIt = srcRenderPassUI.mNameToIndexOutput.find(srcField); + const auto inputIt = dstRenderPassUI.mNameToIndexInput.find(dstField); + // if one filed is empty, check that the other is as well + if ((dstField[0] == '#') || (srcField[0] == '#')) + canCreateEdge &= (srcField[0] == '#') && (dstField[0] == '#'); + // check that link could exist + canCreateEdge &= (outputIt != srcRenderPassUI.mNameToIndexOutput.end()) && (inputIt != dstRenderPassUI.mNameToIndexInput.end()); + // check that the input is not already connected + canCreateEdge &= (mInputPinStringToLinkID.find(dstString) == mInputPinStringToLinkID.end()); + + if (canCreateEdge) { - if (dstField[0] == '#') + uint32_t srcPinIndex = outputIt->second; + uint32_t dstPinIndex = inputIt->second; + srcRenderPassUI.mOutputPins[srcPinIndex].mConnectedPinName = dstField; + srcRenderPassUI.mOutputPins[srcPinIndex].mConnectedNodeName = dstPass; + dstRenderPassUI.mInputPins[dstPinIndex].mConnectedPinName = srcField; + dstRenderPassUI.mInputPins[dstPinIndex].mConnectedNodeName = srcPass; + + if (!(dstField[0] == '#')) { - FALCOR_ASSERT(srcField[0] == '#'); - mpIr->removeEdge(srcPass, dstPass); + color = kEdgesColor; + + // TODO: Auto-resolve is not fully working and will be removed (see #1275). + // 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)); + // if (canAutoResolve && mDisplayAutoResolvePopup) canCreateEdge = autoResolveWarning(srcString, dstString); + // color = canAutoResolve ? kAutoResolveEdgesColor : kEdgesColor; } - else + + if (canCreateEdge) { - mpIr->removeEdge(srcPass + "." + srcField, dstPass + "." + dstField); + mShouldUpdate = true; + + if (dstField[0] == '#') + { + // rebuilds data to avoid repeated code + mRebuildDisplayData = true; + color = kExecutionEdgeColor; + mpIr->addEdge(srcPass, dstPass); + } + else + { + mpIr->addEdge(srcString, dstString); + } } - mShouldUpdate = true; } - - void RenderGraphUI::removeRenderPass(const std::string& name) + else { - mRebuildDisplayData = true; - mpIr->removePass(name); - mShouldUpdate = true; + mLogString += "Unable to create edge for graph. \n"; } - void RenderGraphUI::updateGraph(RenderContext* pRenderContext) + return canCreateEdge; +} + +void RenderGraphUI::removeEdge( + const std::string& srcPass, + const std::string& dstPass, + const std::string& srcField, + const std::string& dstField +) +{ + if (dstField[0] == '#') { - if (!mShouldUpdate) return; - std::string newCommands = mpIr->getIR(); - mpIr = RenderGraphIR::create(mRenderGraphName, false); // reset - if (mLastCommand == newCommands) return; - - mLastCommand = newCommands; - if (mRecordUpdates) mUpdateCommands += newCommands; - - // update reference graph to check if valid before sending to next - // TODO: Rendergraph scripts should be executed in an isolated scripting context. - Scripting::getDefaultContext().setObject("g", mpRenderGraph); - Scripting::runScript(newCommands); - if(newCommands.size()) mLogString += newCommands; - - // only send updates that we know are valid. - if (mpRenderGraph->compile(pRenderContext) == false) mLogString += "Graph is currently invalid\n"; - mShouldUpdate = false; - mRebuildDisplayData = true; + FALCOR_ASSERT(srcField[0] == '#'); + mpIr->removeEdge(srcPass, dstPass); } - - void RenderGraphUI::writeUpdateScriptToFile(RenderContext* pRenderContext, const std::filesystem::path& filePath, float lastFrameTime) + else { - if ((mTimeSinceLastUpdate += lastFrameTime) < kUpdateTimeInterval) return; - mTimeSinceLastUpdate = 0.0f; - if (!mUpdateCommands.size()) return; - - // only send delta of updates once the graph is valid - if (mpRenderGraph->compile(pRenderContext) == false) return; - std::ofstream outputFileStream(filePath, std::ios_base::out); - outputFileStream << mUpdateCommands; - mUpdateCommands.clear(); + mpIr->removeEdge(srcPass + "." + srcField, dstPass + "." + dstField); } + mShouldUpdate = true; +} + +void RenderGraphUI::removeRenderPass(const std::string& name) +{ + mRebuildDisplayData = true; + mpIr->removePass(name); + mShouldUpdate = true; +} + +void RenderGraphUI::updateGraph(RenderContext* pRenderContext) +{ + if (!mShouldUpdate) + return; + std::string newCommands = mpIr->getIR(); + mpIr = std::make_shared(mRenderGraphName, false); // reset + if (mLastCommand == newCommands) + return; + + mLastCommand = newCommands; + if (mRecordUpdates) + mUpdateCommands += newCommands; + + // update reference graph to check if valid before sending to next + // TODO: Rendergraph scripts should be executed in an isolated scripting context. + Scripting::getDefaultContext().setObject("g", mpRenderGraph); + Scripting::runScript(newCommands); + if (newCommands.size()) + mLogString += newCommands; + + // only send updates that we know are valid. + if (mpRenderGraph->compile(pRenderContext) == false) + mLogString += "Graph is currently invalid\n"; + mShouldUpdate = false; + mRebuildDisplayData = true; +} + +void RenderGraphUI::writeUpdateScriptToFile(RenderContext* pRenderContext, const std::filesystem::path& 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 + if (mpRenderGraph->compile(pRenderContext) == false) + return; + std::ofstream outputFileStream(filePath, std::ios_base::out); + outputFileStream << mUpdateCommands; + mUpdateCommands.clear(); +} + +RenderGraphUI::RenderGraphUI() : mNewNodeStartPosition(-40.0f, 100.0f) +{ + mNextPassName.resize(255, 0); +} + +RenderGraphUI::RenderGraphUI(const ref& pGraph, const std::string& graphName) + : mpRenderGraph(pGraph), mNewNodeStartPosition(-40.0f, 100.0f), mRenderGraphName(graphName) +{ + mNextPassName.resize(255, 0); + mpNodeGraphEditor = std::make_shared(this); + mpIr = std::make_shared(graphName, false); +} + +RenderGraphUI::~RenderGraphUI() {} - RenderGraphUI::RenderGraphUI() - : mNewNodeStartPosition(-40.0f, 100.0f) +void RenderGraphUI::NodeGraphEditorGui::setNode(ImGui::Node*& node, ImGui::NodeGraphEditor::NodeState state, ImGui::NodeGraphEditor& editor) +{ + RenderGraphNode* pRenderGraphNode = static_cast(node); + RenderGraphUI::NodeGraphEditorGui* pGraphEditor = static_cast(&editor); + if (!editor.isInited()) { - mNextPassName.resize(255, 0); + if (state == ImGui::NodeGraphEditor::NodeState::NS_DELETED) + { + pRenderGraphNode->getFields().clear(); + pGraphEditor->getRenderGraphUI()->removeRenderPass(node->getName()); + } } - - RenderGraphUI::RenderGraphUI(const RenderGraph::SharedPtr& pGraph, const std::string& graphName) - : mpRenderGraph(pGraph), mNewNodeStartPosition(-40.0f, 100.0f), mRenderGraphName(graphName) + if (state == ImGui::NodeGraphEditor::NodeState::NS_ADDED) { - mNextPassName.resize(255, 0); - mpNodeGraphEditor = std::make_shared(this); - mpIr = RenderGraphIR::create(graphName, false); + pGraphEditor->getRenderGraphUI()->addRenderPass(node->getName(), pRenderGraphNode->mpRenderPass->getType()); } +} - RenderGraphUI::~RenderGraphUI() +void RenderPassUI::addUIPin( + const std::string& fieldName, + uint32_t guiPinID, + bool isInput, + const std::string& connectedPinName, + const std::string& connectedNodeName, + bool isGraphOutput +) +{ + auto& pinsRef = isInput ? mInputPins : mOutputPins; + auto& nameToIndexMapRef = isInput ? mNameToIndexInput : mNameToIndexOutput; + + if (pinsRef.size() <= guiPinID) { + pinsRef.resize(guiPinID + 1); } - void RenderGraphUI::NodeGraphEditorGui::setNode(ImGui::Node*& node, ImGui::NodeGraphEditor::NodeState state, ImGui::NodeGraphEditor& editor) + PinUI& pinUIData = pinsRef[guiPinID]; + pinUIData.mPinName = fieldName; + pinUIData.mGuiPinID = guiPinID; + pinUIData.mConnectedPinName = connectedPinName; + pinUIData.mConnectedNodeName = connectedNodeName; + pinUIData.mIsGraphOutput = isGraphOutput; + + nameToIndexMapRef.insert(std::make_pair(pinUIData.mPinName, static_cast(guiPinID))); +} + +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 = -1; + for (size_t i = 0; i < mReflection.getFieldCount(); ++i) { - RenderGraphNode* pRenderGraphNode = static_cast(node); - RenderGraphUI::NodeGraphEditorGui* pGraphEditor = static_cast(&editor); - if (!editor.isInited()) - { - if (state == ImGui::NodeGraphEditor::NodeState::NS_DELETED) - { - pRenderGraphNode->getFields().clear(); - pGraphEditor->getRenderGraphUI()->removeRenderPass(node->getName()); - } - } - if (state == ImGui::NodeGraphEditor::NodeState::NS_ADDED) + if (mReflection.getField(i)->getName() == pinUI.mPinName) { - pGraphEditor->getRenderGraphUI()->addRenderPass(node->getName(), pRenderGraphNode->mpRenderPass->getType()); + fieldIndex = i; + break; } } - void RenderPassUI::addUIPin(const std::string& fieldName, uint32_t guiPinID, bool isInput, const std::string& connectedPinName, const std::string& connectedNodeName, bool isGraphOutput) + if (fieldIndex != -1) { - auto& pinsRef = isInput ? mInputPins : mOutputPins; - auto& nameToIndexMapRef = isInput ? mNameToIndexInput : mNameToIndexOutput; - - if (pinsRef.size() <= guiPinID) + // 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)) { - pinsRef.resize(guiPinID + 1); + pinUI.renderUI(field, pGraphUI, passName); } + } +} - PinUI& pinUIData = pinsRef[guiPinID]; - pinUIData.mPinName = fieldName; - pinUIData.mGuiPinID = guiPinID; - pinUIData.mConnectedPinName = connectedPinName; - pinUIData.mConnectedNodeName = connectedNodeName; - pinUIData.mIsGraphOutput = isGraphOutput; +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); + uint32_t isOutput = is_set(type, RenderPassReflection::Field::Visibility::Output); + bool isExecutionPin = fieldName[0] == '#'; - nameToIndexMapRef.insert(std::make_pair(pinUIData.mPinName, static_cast(guiPinID) )); + if (isExecutionPin) + { + ImGui::TextUnformatted("Execution Pin"); + return; } - void RenderPassUI::renderPinUI(const std::string& passName, RenderGraphUI* pGraphUI, uint32_t index, bool input) - { - RenderPassUI::PinUI& pinUI = input ? mInputPins[index] : mOutputPins[index]; + ImGui::SameLine(); + ImGui::TextUnformatted((std::string("Field Name: ") + fieldName).c_str()); + ImGui::Separator(); + ImGui::TextUnformatted((field.getDesc() + "\n\n").c_str()); - size_t fieldIndex = -1; - for (size_t i = 0; i < mReflection.getFieldCount(); ++i) - { - if (mReflection.getField(i)->getName() == pinUI.mPinName) - { - fieldIndex = i; - break; - } - } + ImGui::TextUnformatted("ResourceFlags : "); - if (fieldIndex != -1) - { - // 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); - } - } + if (isInput && isOutput) + { + ImGui::SameLine(); + ImGui::TextUnformatted("InputOutput"); } - - void RenderPassUI::PinUI::renderFieldInfo(const RenderPassReflection::Field& field, RenderGraphUI* pGraphUI, const std::string& passName, const std::string& fieldName) + else if (isInput) { - RenderPassReflection::Field::Visibility type = field.getVisibility(); - uint32_t isInput = is_set(type, RenderPassReflection::Field::Visibility::Input); - uint32_t isOutput = is_set(type, RenderPassReflection::Field::Visibility::Output); - bool isExecutionPin = fieldName[0] == '#'; + ImGui::SameLine(); + ImGui::TextUnformatted("Input"); + } + else if (isOutput) + { + ImGui::SameLine(); + ImGui::TextUnformatted("Output"); + } - if (isExecutionPin) - { - ImGui::TextUnformatted("Execution Pin"); - return; - } + ImGui::TextUnformatted("ResourceType : "); + ImGui::SameLine(); + ImGui::TextUnformatted(to_string(field.getType()).c_str()); - ImGui::SameLine(); - ImGui::TextUnformatted((std::string("Field Name: ") + fieldName).c_str()); - ImGui::Separator(); - ImGui::TextUnformatted((field.getDesc() + "\n\n").c_str()); + ImGui::TextUnformatted("Width: "); + ImGui::SameLine(); + ImGui::TextUnformatted(std::to_string(field.getWidth()).c_str()); - ImGui::TextUnformatted("ResourceFlags : "); + ImGui::TextUnformatted("Height: "); + ImGui::SameLine(); + ImGui::TextUnformatted(std::to_string(field.getHeight()).c_str()); - if (isInput && isOutput) - { - ImGui::SameLine(); - ImGui::TextUnformatted("InputOutput"); - } - else if (isInput) - { - ImGui::SameLine(); - ImGui::TextUnformatted("Input"); - } - else if (isOutput) - { - ImGui::SameLine(); - ImGui::TextUnformatted("Output"); - } + ImGui::TextUnformatted("Depth: "); + ImGui::SameLine(); + ImGui::TextUnformatted(std::to_string(field.getDepth()).c_str()); - ImGui::TextUnformatted("ResourceType : "); - ImGui::SameLine(); - ImGui::TextUnformatted(to_string(field.getType()).c_str()); + ImGui::TextUnformatted("Sample Count: "); + ImGui::SameLine(); + ImGui::TextUnformatted(std::to_string(field.getSampleCount()).c_str()); - ImGui::TextUnformatted("Width: "); - ImGui::SameLine(); - ImGui::TextUnformatted(std::to_string(field.getWidth()).c_str()); + ImGui::TextUnformatted("ResourceFormat: "); + ImGui::SameLine(); + ImGui::TextUnformatted(to_string(field.getFormat()).c_str()); - ImGui::TextUnformatted("Height: "); - ImGui::SameLine(); - ImGui::TextUnformatted(std::to_string(field.getHeight()).c_str()); + ImGui::TextUnformatted("BindFlags: "); + ImGui::SameLine(); + ImGui::TextUnformatted(to_string(field.getBindFlags()).c_str()); - ImGui::TextUnformatted("Depth: "); + ImGui::TextUnformatted("Flags: "); + switch (field.getFlags()) + { + case RenderPassReflection::Field::Flags::None: ImGui::SameLine(); - ImGui::TextUnformatted(std::to_string(field.getDepth()).c_str()); - - ImGui::TextUnformatted("Sample Count: "); + ImGui::TextUnformatted("None"); + break; + case RenderPassReflection::Field::Flags::Optional: ImGui::SameLine(); - ImGui::TextUnformatted(std::to_string(field.getSampleCount()).c_str()); - - ImGui::TextUnformatted("ResourceFormat: "); + ImGui::TextUnformatted("Optional"); + break; + case RenderPassReflection::Field::Flags::Persistent: ImGui::SameLine(); - ImGui::TextUnformatted(to_string(field.getFormat()).c_str()); + ImGui::TextUnformatted("Persistent"); + break; + default: + FALCOR_UNREACHABLE(); + } +} - ImGui::TextUnformatted("BindFlags: "); - ImGui::SameLine(); - ImGui::TextUnformatted(to_string(field.getBindFlags()).c_str()); +void RenderPassUI::PinUI::renderUI(const RenderPassReflection::Field& field, RenderGraphUI* pGraphUI, const std::string& passName) +{ + ImGui::TextUnformatted(mIsGraphOutput ? "Graph Output : " : ""); + renderFieldInfo(field, pGraphUI, passName, mPinName); + bool isExecutionPin = mPinName[0] == '#'; - ImGui::TextUnformatted("Flags: "); - switch (field.getFlags()) + if (!isExecutionPin && is_set(field.getVisibility(), RenderPassReflection::Field::Visibility::Output)) + { + bool isGraphOut = mIsGraphOutput; + if (ImGui::Checkbox("Graph Output", &mIsGraphOutput)) { - case RenderPassReflection::Field::Flags::None: - ImGui::SameLine(); - ImGui::TextUnformatted("None"); - break; - case RenderPassReflection::Field::Flags::Optional: - ImGui::SameLine(); - ImGui::TextUnformatted("Optional"); - break; - case RenderPassReflection::Field::Flags::Persistent: - ImGui::SameLine(); - ImGui::TextUnformatted("Persistent"); - break; - default: - FALCOR_UNREACHABLE(); + if (isGraphOut && !mIsGraphOutput) + { + pGraphUI->removeOutput(passName, mPinName); + } + else if (!isGraphOut && mIsGraphOutput) + { + pGraphUI->addOutput(passName, mPinName); + } } } - void RenderPassUI::PinUI::renderUI(const RenderPassReflection::Field& field, RenderGraphUI* pGraphUI, const std::string& passName) + ImGui::Separator(); +} + +void RenderGraphUI::renderPopupMenu() +{ + bool isPopupOpen = ImGui::IsPopupOpen("PinMenu"); + if (!isPopupOpen) { - ImGui::TextUnformatted(mIsGraphOutput ? "Graph Output : " : ""); - renderFieldInfo(field, pGraphUI, passName, mPinName); - bool isExecutionPin = mPinName[0] == '#'; + ImGui::OpenPopup("PinMenu"); + } - if (!isExecutionPin && is_set(field.getVisibility(), RenderPassReflection::Field::Visibility::Output)) + ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(8, 8)); + if (ImGui::BeginPopup("PinMenu")) + { + if (!isPopupOpen) + ImGui::SetWindowPos({ImGui::GetWindowPos().x - 8, ImGui::GetWindowPos().y - 8}); + else if (isPopupOpen && !ImGui::IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem | ImGuiHoveredFlags_ChildWindows)) { - bool isGraphOut = mIsGraphOutput; - if (ImGui::Checkbox("Graph Output", &mIsGraphOutput)) + if (ImGui::IsMouseClicked(0) || ImGui::IsMouseClicked(1)) { - if (isGraphOut && !mIsGraphOutput) - { - pGraphUI->removeOutput(passName, mPinName); - } - else if (!isGraphOut && mIsGraphOutput) - { - pGraphUI->addOutput(passName, mPinName); - } + mpNodeGraphEditor->selectedLink = -1; + mpNodeGraphEditor->setPopupNode(nullptr); + mpNodeGraphEditor->setPopupPin(-1, false); } + else + mpNodeGraphEditor->deselectPopupPin(); } - ImGui::Separator(); - } - - void RenderGraphUI::renderPopupMenu() - { - bool isPopupOpen = false; - bool first = false; - - if (!(isPopupOpen = ImGui::IsPopupOpen("PinMenu"))) + if (mpNodeGraphEditor->getPopupNode() && mpNodeGraphEditor->getPopupPinIndex() != -1) { - ImGui::OpenPopup("PinMenu"); - first = true; + const std::string& passName = mpNodeGraphEditor->getPopupNode()->getName(); + RenderPassUI& renderPassUI = mRenderPassUI[passName]; + renderPassUI.renderPinUI(passName, this, mpNodeGraphEditor->getPopupPinIndex(), mpNodeGraphEditor->isPopupPinInput()); + ImGui::Separator(); } - ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(8, 8)); - if (ImGui::BeginPopup("PinMenu")) + if (mpNodeGraphEditor->selectedLink != -1) { - if (first) ImGui::SetWindowPos({ ImGui::GetWindowPos().x - 8, ImGui::GetWindowPos().y - 8 }); - else if (isPopupOpen && !ImGui::IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem | ImGuiHoveredFlags_ChildWindows)) + ImGui::NodeLink& selectedLink = mpNodeGraphEditor->getLink(mpNodeGraphEditor->selectedLink); + std::string srcPassName = std::string(selectedLink.InputNode->getName()); + std::string dstPassName = std::string(selectedLink.OutputNode->getName()); + 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]; + auto edgeIt = mpRenderGraph->mEdgeData.find(linkID); + + // link exists, but is not an edge (such as graph output edge) + if (edgeIt != mpRenderGraph->mEdgeData.end()) { - if (ImGui::IsMouseClicked(0) || ImGui::IsMouseClicked(1)) + RenderGraph::EdgeData& edgeData = edgeIt->second; + + if (edgeData.srcField.size()) { - mpNodeGraphEditor->selectedLink = -1; - mpNodeGraphEditor->setPopupNode(nullptr); - mpNodeGraphEditor->setPopupPin(-1, false); + ImGui::TextUnformatted("Src Field : "); + ImGui::SameLine(); + ImGui::TextUnformatted(edgeData.srcField.c_str()); } - else mpNodeGraphEditor->deselectPopupPin(); - } - - if (mpNodeGraphEditor->getPopupNode() && mpNodeGraphEditor->getPopupPinIndex() != -1) - { - const std::string& passName = mpNodeGraphEditor->getPopupNode()->getName(); - RenderPassUI& renderPassUI = mRenderPassUI[passName]; - renderPassUI.renderPinUI(passName, this, mpNodeGraphEditor->getPopupPinIndex(), mpNodeGraphEditor->isPopupPinInput()); - ImGui::Separator(); - } - if (mpNodeGraphEditor->selectedLink != -1) - { - ImGui::NodeLink& selectedLink = mpNodeGraphEditor->getLink(mpNodeGraphEditor->selectedLink); - std::string srcPassName = std::string(selectedLink.InputNode->getName()); - std::string dstPassName = std::string(selectedLink.OutputNode->getName()); - 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]; - auto edgeIt = mpRenderGraph->mEdgeData.find(linkID); - - // link exists, but is not an edge (such as graph output edge) - if (edgeIt != mpRenderGraph->mEdgeData.end()) + if (edgeData.dstField.size()) { - RenderGraph::EdgeData& edgeData = edgeIt->second; - - if (edgeData.srcField.size()) - { - ImGui::TextUnformatted("Src Field : "); - ImGui::SameLine(); - ImGui::TextUnformatted(edgeData.srcField.c_str()); - } - - if (edgeData.dstField.size()) - { - ImGui::TextUnformatted("Dst Field : "); - ImGui::SameLine(); - ImGui::TextUnformatted(edgeData.dstField.c_str()); - } + ImGui::TextUnformatted("Dst Field : "); + ImGui::SameLine(); + ImGui::TextUnformatted(edgeData.dstField.c_str()); } } - - ImGui::EndPopup(); } - ImGui::PopStyleVar(); + + ImGui::EndPopup(); } + ImGui::PopStyleVar(); +} - void RenderGraphUI::renderUI(RenderContext* pRenderContext, Gui* pGui) +void RenderGraphUI::renderUI(RenderContext* pRenderContext, Gui* pGui) +{ + static std::string dragAndDropText; + ImGui::GetIO().FontAllowUserScaling = true; // FIXME + mpNodeGraphEditor->mpRenderGraphUI = this; + mpNodeGraphEditor->setCurrentGui(pGui); + mpNodeGraphEditor->show_top_pane = false; + mpNodeGraphEditor->show_node_copy_paste_buttons = false; + mpNodeGraphEditor->show_connection_names = false; + mpNodeGraphEditor->show_left_pane = false; + mpNodeGraphEditor->setLinkCallback(NodeGraphEditorGui::setLinkFromGui); + mpNodeGraphEditor->setNodeCallback(NodeGraphEditorGui::setNode); + + ImVec2 mousePos = ImGui::GetMousePos(); + ImGui::NodeGraphEditor::Style& style = mpNodeGraphEditor->GetStyle(); + style.color_node_frame_selected = ImGui::ColorConvertFloat4ToU32({226.0f / 255.0f, 190.0f / 255.0f, 42.0f / 255.0f, 0.8f}); + style.color_node_frame_active = style.color_node_frame_selected; + style.node_slots_radius = kPinRadius; + + // update the deleted links from the GUI since the library doesn't call its own callback + if (mRebuildDisplayData) { - static std::string dragAndDropText; - ImGui::GetIO().FontAllowUserScaling = true; // FIXME - mpNodeGraphEditor->mpRenderGraphUI = this; - mpNodeGraphEditor->setCurrentGui(pGui); - mpNodeGraphEditor->show_top_pane = false; - mpNodeGraphEditor->show_node_copy_paste_buttons = false; - mpNodeGraphEditor->show_connection_names = false; - mpNodeGraphEditor->show_left_pane = false; - mpNodeGraphEditor->setLinkCallback(NodeGraphEditorGui::setLinkFromGui); - mpNodeGraphEditor->setNodeCallback(NodeGraphEditorGui::setNode); - - ImVec2 mousePos = ImGui::GetMousePos(); - ImGui::NodeGraphEditor::Style& style = mpNodeGraphEditor->GetStyle(); - style.color_node_frame_selected = ImGui::ColorConvertFloat4ToU32({ 226.0f / 255.0f, 190.0f / 255.0f, 42.0f / 255.0f, 0.8f }); - style.color_node_frame_active = style.color_node_frame_selected; - style.node_slots_radius = kPinRadius; - - // update the deleted links from the GUI since the library doesn't call its own callback - if (mRebuildDisplayData) - { - mRebuildDisplayData = false; - mpNodeGraphEditor->setNodeCallback(nullptr); - mpNodeGraphEditor->reset(); - } - else - { - updatePins(false); - } + mRebuildDisplayData = false; + mpNodeGraphEditor->setNodeCallback(nullptr); + mpNodeGraphEditor->reset(); + } + else + { + updatePins(false); + } - ImVector selectedNodes; - mpNodeGraphEditor->getSelectedNodes(selectedNodes); + ImVector selectedNodes; + mpNodeGraphEditor->getSelectedNodes(selectedNodes); - // push update commands for the open pop-up - 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(); + // push update commands for the open pop-up + 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 (auto renderGroup = renderWindow.group(renderUIName, true)) - { - auto pPass = mpRenderGraph->getPass(renderUIName); - bool internalResources = false; + if (auto renderGroup = renderWindow.group(renderUIName, true)) + { + auto pPass = mpRenderGraph->getPass(renderUIName); + bool internalResources = false; - renderGroup.separator(); - ImGui::TextWrapped("Description: %s", pPass->getDesc().c_str()); - renderGroup.separator(); + renderGroup.separator(); + ImGui::TextWrapped("Description: %s", pPass->getDesc().c_str()); + renderGroup.separator(); - pPass->renderUI(renderGroup); + pPass->renderUI(renderGroup); - for (uint32_t i = 0; i < mRenderPassUI[renderUIName].mReflection.getFieldCount(); ++i) + for (uint32_t j = 0; j < mRenderPassUI[renderUIName].mReflection.getFieldCount(); ++j) + { + const auto& field = *mRenderPassUI[renderUIName].mReflection.getField(j); + if (is_set(field.getVisibility(), RenderPassReflection::Field::Visibility::Internal)) { - const auto& field = *mRenderPassUI[renderUIName].mReflection.getField(i); - if (is_set(field.getVisibility(), RenderPassReflection::Field::Visibility::Internal)) + if (!internalResources) { - if (!internalResources) - { - renderGroup.separator(); renderGroup.text("\n\n"); renderGroup.separator(); - renderGroup.text("Internal Resources:"); - renderGroup.text("\n\n"); - renderGroup.separator(); - } - RenderPassUI::PinUI::renderFieldInfo(field, this, renderUIName, field.getName()); - internalResources = true; + renderGroup.separator(); + renderGroup.text("\n\n"); + renderGroup.separator(); + renderGroup.text("Internal Resources:"); + renderGroup.text("\n\n"); + renderGroup.separator(); } + RenderPassUI::PinUI::renderFieldInfo(field, this, renderUIName, field.getName()); + internalResources = true; } - if (internalResources) renderGroup.separator(); - // TODO -- only call this with data change - if (ImGui::IsWindowFocused()) - { - mpIr->updatePass(renderUIName, pPass->getScriptingDictionary()); - } - mShouldUpdate = true; } + if (internalResources) + renderGroup.separator(); + // TODO -- only call this with data change + if (ImGui::IsWindowFocused()) + { + mpIr->updatePass(renderUIName, pPass->getScriptingDictionary()); + } + mShouldUpdate = true; } + } - renderWindow.release(); + renderWindow.release(); - if (mpNodeGraphEditor->getPopupPinIndex() != uint32_t(-1) || (mpNodeGraphEditor->selectedLink != -1)) + if (mpNodeGraphEditor->getPopupPinIndex() != uint32_t(-1) || (mpNodeGraphEditor->selectedLink != -1)) + { + renderPopupMenu(); + } + else + { + if (ImGui::IsPopupOpen("PinMenu")) { - renderPopupMenu(); + ImGui::CloseCurrentPopup(); } - else + } + + if (!mpNodeGraphEditor->isInited()) + { + mpNodeGraphEditor->render(); + + std::string statement; + bool addPass = false; + bool b = false; + if (ImGui::BeginDragDropTarget()) { - if (ImGui::IsPopupOpen("PinMenu")) + auto dragDropPayload = ImGui::AcceptDragDropPayload("RenderPassType"); + b = dragDropPayload && dragDropPayload->IsDataType("RenderPassType") && (dragDropPayload->Data != nullptr); + if (b) { - ImGui::CloseCurrentPopup(); + statement.resize(dragDropPayload->DataSize); + std::memcpy(&statement.front(), dragDropPayload->Data, dragDropPayload->DataSize); } + + ImGui::EndDragDropTarget(); } - if (!mpNodeGraphEditor->isInited()) + if (b) { - mpNodeGraphEditor->render(); + dragAndDropText = statement; + mNewNodeStartPosition = {-mpNodeGraphEditor->offset.x + mousePos.x, -mpNodeGraphEditor->offset.y + mousePos.y}; + mNewNodeStartPosition /= ImGui::GetCurrentWindow()->FontWindowScale; + mNextPassName = statement; + // only open pop-up if right clicked + mDisplayDragAndDropPopup = ImGui::GetIO().KeyCtrl; + addPass = !mDisplayDragAndDropPopup; + } - std::string statement; - bool addPass = false; - 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); - } + if (mDisplayDragAndDropPopup) + { + Gui::Window createGraphWindow( + pGui, "CreateNewGraph", mDisplayDragAndDropPopup, {256, 128}, {(uint32_t)mousePos.x, (uint32_t)mousePos.y} + ); - ImGui::EndDragDropTarget(); + createGraphWindow.textbox("Pass Name", mNextPassName); + if (createGraphWindow.button("create##renderpass")) + { + addPass = true; } - - if (b) + if (createGraphWindow.button("cancel##renderPass")) { - dragAndDropText = statement; - mNewNodeStartPosition = { -mpNodeGraphEditor->offset.x + mousePos.x, -mpNodeGraphEditor->offset.y + mousePos.y }; - mNewNodeStartPosition /= ImGui::GetCurrentWindow()->FontWindowScale; - mNextPassName = statement; - // only open pop-up if right clicked - mDisplayDragAndDropPopup = ImGui::GetIO().KeyCtrl; - addPass = !mDisplayDragAndDropPopup; + mDisplayDragAndDropPopup = false; } - if (mDisplayDragAndDropPopup) + createGraphWindow.release(); + } + + if (addPass) + { + // Get unique name by adding incrementing number suffix if necessary. + const std::string passName = mNextPassName; + uint32_t passIndex = 0; + while (mpRenderGraph->doesPassExist(mNextPassName)) { - Gui::Window createGraphWindow(pGui, "CreateNewGraph", mDisplayDragAndDropPopup, { 256, 128 }, { (uint32_t)mousePos.x, (uint32_t)mousePos.y }); + mNextPassName = passName + std::to_string(passIndex++); + } - createGraphWindow.textbox("Pass Name", mNextPassName); - if (createGraphWindow.button("create##renderpass")) - { - addPass = true; - } - if (createGraphWindow.button("cancel##renderPass")) - { - mDisplayDragAndDropPopup = false; - } + mpIr->createPass(dragAndDropText, mNextPassName); + mAddedFromDragAndDrop = true; + mDisplayDragAndDropPopup = false; + mShouldUpdate = true; + if (mMaxNodePositionX < mNewNodeStartPosition.x) + mMaxNodePositionX = mNewNodeStartPosition.x; + } - createGraphWindow.release(); - } + // set editor window behind other windows. set top menu bar to always be infront.this is repeated for other render call + ImGui::BringWindowToDisplayBack(ImGui::FindWindowByName("Graph Editor")); + ImGui::BringWindowToDisplayFront(ImGui::FindWindowByName("##MainMenuBar")); + return; + } - if (addPass) - { - // Get unique name by adding incrementing number suffix if necessary. - const std::string passName = mNextPassName; - uint32_t passIndex = 0; - while (mpRenderGraph->doesPassExist(mNextPassName)) - { - mNextPassName = passName + std::to_string(passIndex++); - } + updateDisplayData(pRenderContext); - mpIr->addPass(dragAndDropText, mNextPassName); - mAddedFromDragAndDrop = true; - mDisplayDragAndDropPopup = false; - mShouldUpdate = true; - if (mMaxNodePositionX < mNewNodeStartPosition.x) mMaxNodePositionX = mNewNodeStartPosition.x; - } + mAllNodeTypes.clear(); - // set editor window behind other windows. set top menu bar to always be infront.this is repeated for other render call - ImGui::BringWindowToDisplayBack(ImGui::FindWindowByName("Graph Editor")); - ImGui::BringWindowToDisplayFront(ImGui::FindWindowByName("##MainMenuBar")); - return; - } + // reset internal data of node if all nodes deleted + if (!mRenderPassUI.size()) + { + mpNodeGraphEditor->clear(); + mpNodeGraphEditor->render(); + return; + } + + for (auto& nodeTypeString : mAllNodeTypeStrings) + { + mAllNodeTypes.push_back(nodeTypeString.c_str()); + } - updateDisplayData(pRenderContext); + mpNodeGraphEditor->registerNodeTypes( + mAllNodeTypes.data(), static_cast(mAllNodeTypes.size()), NodeGraphEditorGui::createNode, 0, -1, 0, 0 + ); - mAllNodeTypes.clear(); + for (auto& currentPass : mRenderPassUI) + { + auto& currentPassUI = currentPass.second; + std::string inputsString; + std::string outputsString; + std::string nameString; - // reset internal data of node if all nodes deleted - if (!mRenderPassUI.size()) + for (const auto& currentPinUI : currentPassUI.mInputPins) { - mpNodeGraphEditor->clear(); - mpNodeGraphEditor->render(); - return; + // Connect the graph nodes for each of the edges + // need to iterate in here in order to use the right indices + const std::string& currentPinName = currentPinUI.mPinName; + inputsString += inputsString.size() ? (";" + currentPinName) : currentPinName; } - for (auto& nodeTypeString : mAllNodeTypeStrings) + for (const auto& currentPinUI : currentPassUI.mOutputPins) { - mAllNodeTypes.push_back(nodeTypeString.c_str()); + const std::string& currentPinName = currentPinUI.mPinName; + outputsString += outputsString.size() ? (";" + currentPinName) : currentPinName; } - mpNodeGraphEditor->registerNodeTypes(mAllNodeTypes.data(), static_cast(mAllNodeTypes.size()), NodeGraphEditorGui::createNode, 0, -1, 0, 0); + uint32_t guiNodeID = currentPassUI.mGuiNodeID; + RenderPass* pNodeRenderPass = mpRenderGraph->getPass(currentPass.first).get(); + nameString = currentPass.first; - for (auto& currentPass : mRenderPassUI) + if (!mpNodeGraphEditor->getAllNodesOfType(currentPassUI.mGuiNodeID, nullptr, false)) { - auto& currentPassUI = currentPass.second; - std::string inputsString; - std::string outputsString; - std::string nameString; - - for (const auto& currentPinUI : currentPassUI.mInputPins) - { - // Connect the graph nodes for each of the edges - // need to iterate in here in order to use the right indices - const std::string& currentPinName = currentPinUI.mPinName; - inputsString += inputsString.size() ? (";" + currentPinName) : currentPinName; - } - - for (const auto& currentPinUI : currentPassUI.mOutputPins) - { - const std::string& currentPinName = currentPinUI.mPinName; - outputsString += outputsString.size() ? (";" + currentPinName) : currentPinName; - } + float2 nextPosition = + mAddedFromDragAndDrop ? mNewNodeStartPosition : getNextNodePosition(mpRenderGraph->getPassIndex(nameString)); - uint32_t guiNodeID = currentPassUI.mGuiNodeID; - RenderPass* pNodeRenderPass = mpRenderGraph->getPass(currentPass.first).get(); - nameString = currentPass.first; - - if (!mpNodeGraphEditor->getAllNodesOfType(currentPassUI.mGuiNodeID, nullptr, false)) - { - float2 nextPosition = mAddedFromDragAndDrop ? mNewNodeStartPosition : getNextNodePosition(mpRenderGraph->getPassIndex(nameString)); - - mpNodeGraphEditor->getNodeFromID(guiNodeID) = mpNodeGraphEditor->addAndInitNode(guiNodeID, - nameString, outputsString, inputsString, guiNodeID, pNodeRenderPass, - ImVec2(nextPosition.x, nextPosition.y)); - mAddedFromDragAndDrop = false; - } + mpNodeGraphEditor->getNodeFromID(guiNodeID) = mpNodeGraphEditor->addAndInitNode( + guiNodeID, nameString, outputsString, inputsString, guiNodeID, pNodeRenderPass, ImVec2(nextPosition.x, nextPosition.y) + ); + mAddedFromDragAndDrop = false; } - - updatePins(); - mpNodeGraphEditor->render(); - ImGui::BringWindowToDisplayBack(ImGui::FindWindowByName("Graph Editor")); - ImGui::BringWindowToDisplayFront(ImGui::FindWindowByName("##MainMenuBar")); } - void RenderGraphUI::reset() - { - mpNodeGraphEditor->reset(); - mpNodeGraphEditor->clear(); - mRebuildDisplayData = true; - } + updatePins(); + mpNodeGraphEditor->render(); + ImGui::BringWindowToDisplayBack(ImGui::FindWindowByName("Graph Editor")); + ImGui::BringWindowToDisplayFront(ImGui::FindWindowByName("##MainMenuBar")); +} - void RenderGraphUI::setRecordUpdates(bool recordUpdates) - { - mRecordUpdates = recordUpdates; - } +void RenderGraphUI::reset() +{ + mpNodeGraphEditor->reset(); + mpNodeGraphEditor->clear(); + mRebuildDisplayData = true; +} + +void RenderGraphUI::setRecordUpdates(bool recordUpdates) +{ + mRecordUpdates = recordUpdates; +} - void RenderGraphUI::updatePins(bool addLinks) +void RenderGraphUI::updatePins(bool addLinks) +{ + // Draw pin connections. All the nodes have to be added to the GUI before the connections can be drawn + for (auto& currentPass : mRenderPassUI) { - // Draw pin connections. All the nodes have to be added to the GUI before the connections can be drawn - for (auto& currentPass : mRenderPassUI) + auto& currentPassUI = currentPass.second; + + for (auto& currentPinUI : currentPassUI.mOutputPins) { - auto& currentPassUI = currentPass.second; + const std::string& currentPinName = currentPinUI.mPinName; - for (auto& currentPinUI : currentPassUI.mOutputPins) + if (addLinks) { - const std::string& currentPinName = currentPinUI.mPinName; - - if (addLinks) + const auto& inputPins = mOutputToInputPins.find(currentPass.first + "." + currentPinName); + if (inputPins != mOutputToInputPins.end()) { - const auto& inputPins = mOutputToInputPins.find(currentPass.first + "." + currentPinName); - if (inputPins != mOutputToInputPins.end()) + for (const auto& connectedPin : (inputPins->second)) { - for (const auto& connectedPin : (inputPins->second)) + if (!mpNodeGraphEditor->isLinkPresent( + mpNodeGraphEditor->getNodeFromID(currentPassUI.mGuiNodeID), currentPinUI.mGuiPinID, + mpNodeGraphEditor->getNodeFromID(connectedPin.second), connectedPin.first + )) { - if (!mpNodeGraphEditor->isLinkPresent(mpNodeGraphEditor->getNodeFromID(currentPassUI.mGuiNodeID), currentPinUI.mGuiPinID, - mpNodeGraphEditor->getNodeFromID(connectedPin.second), connectedPin.first)) + RenderGraphNode* pNode = static_cast(mpNodeGraphEditor->getNodeFromID(connectedPin.second)); + std::string dstName = pNode->getInputName(connectedPin.first); + uint32_t edgeColor = kEdgesColor; + // execution edges + if (dstName[0] == '#') { - RenderGraphNode* pNode = static_cast(mpNodeGraphEditor->getNodeFromID(connectedPin.second)); - std::string dstName = pNode->getInputName(connectedPin.first); - uint32_t edgeColor = kEdgesColor; - // execution edges - if (dstName[0] == '#') - { - edgeColor = kExecutionEdgeColor; - } - else - { - 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); - - if (false/*mpRenderGraph->canAutoResolve(srcPin, dstPin)*/) - { - mLogString += std::string("Warning: Edge ") + srcString + " - " + dstName + " can auto-resolve.\n"; - edgeColor = kAutoResolveEdgesColor; - } - } - - mpNodeGraphEditor->addLinkFromGraph(mpNodeGraphEditor->getNodeFromID(currentPassUI.mGuiNodeID), currentPinUI.mGuiPinID, - mpNodeGraphEditor->getNodeFromID(connectedPin.second), connectedPin.first, false, edgeColor); + edgeColor = kExecutionEdgeColor; + } + else + { + 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); - RenderGraphNode* pDstGraphNode = static_cast(mpNodeGraphEditor->getNodeFromID(connectedPin.second)); - RenderGraphNode* pSrcGraphNode = static_cast(mpNodeGraphEditor->getNodeFromID(currentPassUI.mGuiNodeID)); - pDstGraphNode->mInputPinConnected[connectedPin.first] = true; - pSrcGraphNode->mOutputPinConnected[currentPinUI.mGuiPinID] = true; - if (dstName[0] == '#') + if (false /*mpRenderGraph->canAutoResolve(srcPin, dstPin)*/) { - pSrcGraphNode->setPinColor(kExecutionEdgeColor, currentPinUI.mGuiPinID, false); - pDstGraphNode->setPinColor(kExecutionEdgeColor, connectedPin.first, true); + mLogString += std::string("Warning: Edge ") + srcString + " - " + dstName + " can auto-resolve.\n"; + edgeColor = kAutoResolveEdgesColor; } } - } - } - // mark graph outputs to graph output node - if (currentPinUI.mIsGraphOutput) - { - static_cast(mpNodeGraphEditor->getNodeFromID(currentPassUI.mGuiNodeID))->setPinColor(kGraphOutputsColor, currentPinUI.mGuiPinID); + mpNodeGraphEditor->addLinkFromGraph( + mpNodeGraphEditor->getNodeFromID(currentPassUI.mGuiNodeID), currentPinUI.mGuiPinID, + mpNodeGraphEditor->getNodeFromID(connectedPin.second), connectedPin.first, false, edgeColor + ); + + RenderGraphNode* pDstGraphNode = + static_cast(mpNodeGraphEditor->getNodeFromID(connectedPin.second)); + RenderGraphNode* pSrcGraphNode = + static_cast(mpNodeGraphEditor->getNodeFromID(currentPassUI.mGuiNodeID)); + pDstGraphNode->mInputPinConnected[connectedPin.first] = true; + pSrcGraphNode->mOutputPinConnected[currentPinUI.mGuiPinID] = true; + if (dstName[0] == '#') + { + pSrcGraphNode->setPinColor(kExecutionEdgeColor, currentPinUI.mGuiPinID, false); + pDstGraphNode->setPinColor(kExecutionEdgeColor, connectedPin.first, true); + } + } } } - else + + // mark graph outputs to graph output node + if (currentPinUI.mIsGraphOutput) { - if (!currentPinUI.mIsGraphOutput) - { - static_cast(mpNodeGraphEditor->getNodeFromID(currentPassUI.mGuiNodeID))->setPinColor(ImColor(150, 150, 150, 150), currentPinUI.mGuiPinID); - } + static_cast(mpNodeGraphEditor->getNodeFromID(currentPassUI.mGuiNodeID)) + ->setPinColor(kGraphOutputsColor, currentPinUI.mGuiPinID); } } - - if (!addLinks) + else { - for (auto& currentPinUI : currentPassUI.mInputPins) + if (!currentPinUI.mIsGraphOutput) { - const std::string& currentPinName = currentPinUI.mPinName; - if (!currentPinUI.mConnectedNodeName.size()) continue; - - std::pair inputIDs{ currentPinUI.mGuiPinID, currentPassUI.mGuiNodeID }; - const auto& connectedNodeUI = mRenderPassUI[currentPinUI.mConnectedNodeName]; - uint32_t inputPinID = connectedNodeUI.mNameToIndexOutput.find(currentPinUI.mConnectedPinName)->second; - - if (!mpNodeGraphEditor->isLinkPresent(mpNodeGraphEditor->getNodeFromID(connectedNodeUI.mGuiNodeID), inputPinID, - mpNodeGraphEditor->getNodeFromID(inputIDs.second),inputIDs.first )) - { - auto edgeIt = mInputPinStringToLinkID.find(currentPass.first + "." + currentPinName); - FALCOR_ASSERT(edgeIt != mInputPinStringToLinkID.end()); - uint32_t edgeID = edgeIt->second; - - removeEdge(mpNodeGraphEditor->getNodeFromID(connectedNodeUI.mGuiNodeID)->getName(), - mpNodeGraphEditor->getNodeFromID(inputIDs.second)->getName(), mpRenderGraph->mEdgeData[edgeID].srcField, mpRenderGraph->mEdgeData[edgeID].dstField); - mpRenderGraph->removeEdge(edgeID); + static_cast(mpNodeGraphEditor->getNodeFromID(currentPassUI.mGuiNodeID)) + ->setPinColor(ImColor(150, 150, 150, 150), currentPinUI.mGuiPinID); + } + } + } - currentPinUI.mConnectedNodeName = ""; - RenderGraphNode* pDstGraphNode = static_cast(mpNodeGraphEditor->getNodeFromID(inputIDs.second)); - RenderGraphNode* pSrcGraphNode = static_cast(mpNodeGraphEditor->getNodeFromID(connectedNodeUI.mGuiNodeID)); + if (!addLinks) + { + for (auto& currentPinUI : currentPassUI.mInputPins) + { + const std::string& currentPinName = currentPinUI.mPinName; + if (!currentPinUI.mConnectedNodeName.size()) + continue; - pDstGraphNode->mInputPinConnected[inputIDs.first] = false; - pSrcGraphNode->mOutputPinConnected[inputPinID] = false; - pSrcGraphNode->setPinColor(kPinColor, inputPinID, false); - pDstGraphNode->setPinColor(kPinColor, inputIDs.first, true); + std::pair inputIDs{currentPinUI.mGuiPinID, currentPassUI.mGuiNodeID}; + const auto& connectedNodeUI = mRenderPassUI[currentPinUI.mConnectedNodeName]; + uint32_t inputPinID = connectedNodeUI.mNameToIndexOutput.find(currentPinUI.mConnectedPinName)->second; - continue; - } + if (!mpNodeGraphEditor->isLinkPresent( + mpNodeGraphEditor->getNodeFromID(connectedNodeUI.mGuiNodeID), inputPinID, + mpNodeGraphEditor->getNodeFromID(inputIDs.second), inputIDs.first + )) + { + auto edgeIt = mInputPinStringToLinkID.find(currentPass.first + "." + currentPinName); + FALCOR_ASSERT(edgeIt != mInputPinStringToLinkID.end()); + uint32_t edgeID = edgeIt->second; + + removeEdge( + mpNodeGraphEditor->getNodeFromID(connectedNodeUI.mGuiNodeID)->getName(), + mpNodeGraphEditor->getNodeFromID(inputIDs.second)->getName(), mpRenderGraph->mEdgeData[edgeID].srcField, + mpRenderGraph->mEdgeData[edgeID].dstField + ); + mpRenderGraph->removeEdge(edgeID); + + currentPinUI.mConnectedNodeName = ""; + RenderGraphNode* pDstGraphNode = static_cast(mpNodeGraphEditor->getNodeFromID(inputIDs.second)); + RenderGraphNode* pSrcGraphNode = + static_cast(mpNodeGraphEditor->getNodeFromID(connectedNodeUI.mGuiNodeID)); + + pDstGraphNode->mInputPinConnected[inputIDs.first] = false; + pSrcGraphNode->mOutputPinConnected[inputPinID] = false; + pSrcGraphNode->setPinColor(kPinColor, inputPinID, false); + pDstGraphNode->setPinColor(kPinColor, inputIDs.first, true); + + continue; } } } } +} - float2 RenderGraphUI::getNextNodePosition(uint32_t nodeID) - { - const float offsetX = 384.0f; - const float offsetY = 128.0f; - float2 newNodePosition = mNewNodeStartPosition; +float2 RenderGraphUI::getNextNodePosition(uint32_t nodeID) +{ + const float offsetX = 384.0f; + const float offsetY = 128.0f; + float2 newNodePosition = mNewNodeStartPosition; - auto topologicalSort = DirectedGraphTopologicalSort::sort(mpRenderGraph->mpGraph.get()); + auto topologicalSort = DirectedGraphTopologicalSort::sort(*mpRenderGraph->mpGraph); + + // For each object in the vector, if it's being used in the execution, put it in the list + for (auto& node : topologicalSort) + { + newNodePosition.x += offsetX; - // For each object in the vector, if it's being used in the execution, put it in the list - for (auto& node : topologicalSort) + 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 (node == 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() * nodeID; + // 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; - return newNodePosition; } - void RenderGraphUI::updateDisplayData(RenderContext* pRenderContext) + if (newNodePosition.x > mMaxNodePositionX) + mMaxNodePositionX = newNodePosition.x; + return newNodePosition; +} + +void RenderGraphUI::updateDisplayData(RenderContext* pRenderContext) +{ + uint32_t nodeIndex = 0; + + mOutputToInputPins.clear(); + + // set of field names that have a connection and are represented in the graph + std::unordered_set nodeConnectedInput; + std::unordered_set nodeConnectedOutput; + std::unordered_map previousGuiNodeIDs; + std::unordered_set existingIDs; + + for (const auto& currentRenderPassUI : mRenderPassUI) { - uint32_t nodeIndex = 0; + existingIDs.insert(currentRenderPassUI.second.mGuiNodeID); + previousGuiNodeIDs.insert(std::make_pair(currentRenderPassUI.first, currentRenderPassUI.second.mGuiNodeID)); + } + + mpRenderGraph->compile(pRenderContext); + mRenderPassUI.clear(); + mInputPinStringToLinkID.clear(); - mOutputToInputPins.clear(); + // build information for displaying graph + for (const auto& nameToIndex : mpRenderGraph->mNameToIndex) + { + auto pCurrentPass = mpRenderGraph->mpGraph->getNode(nameToIndex.second); + RenderPassUI renderPassUI; + bool addedExecutionInput = false; + bool addedExecutionOutput = false; - // set of field names that have a connection and are represented in the graph - std::unordered_set nodeConnectedInput; - std::unordered_set nodeConnectedOutput; - std::unordered_map previousGuiNodeIDs; - std::unordered_set existingIDs; + mAllNodeTypeStrings.insert(nameToIndex.first); - for (const auto& currentRenderPassUI : mRenderPassUI) + while (existingIDs.find(nodeIndex) != existingIDs.end()) { - existingIDs.insert(currentRenderPassUI.second.mGuiNodeID); - previousGuiNodeIDs.insert(std::make_pair(currentRenderPassUI.first, currentRenderPassUI.second.mGuiNodeID)); + nodeIndex++; } - mpRenderGraph->compile(pRenderContext); - mRenderPassUI.clear(); - mInputPinStringToLinkID.clear(); - - // build information for displaying graph - for (const auto& nameToIndex : mpRenderGraph->mNameToIndex) + // keep the GUI id from the previous frame + auto pPreviousID = previousGuiNodeIDs.find(nameToIndex.first); + if (pPreviousID != previousGuiNodeIDs.end()) + { + renderPassUI.mGuiNodeID = pPreviousID->second; + } + else { - auto pCurrentPass = mpRenderGraph->mpGraph->getNode(nameToIndex.second); - RenderPassUI renderPassUI; - bool addedExecutionInput = false; - bool addedExecutionOutput = false; + renderPassUI.mGuiNodeID = nodeIndex; + nodeIndex++; + } - mAllNodeTypeStrings.insert(nameToIndex.first); + // clear and rebuild reflection for each pass. + // TODO: This is unsafe because render pass reflection may depend on the compile data, which isn't available here. + renderPassUI.mReflection = mpRenderGraph->mNodeData[nameToIndex.second].pPass->reflect({}); - while (existingIDs.find(nodeIndex) != existingIDs.end()) - { - nodeIndex++; - } + // test to see if we have hit a graph output + std::unordered_set passGraphOutputs; - // keep the GUI id from the previous frame - auto pPreviousID = previousGuiNodeIDs.find(nameToIndex.first); - if (pPreviousID != previousGuiNodeIDs.end()) - { - renderPassUI.mGuiNodeID = pPreviousID->second; - } - else + for (const auto& output : mpRenderGraph->mOutputs) + { + if (output.nodeId == nameToIndex.second) { - renderPassUI.mGuiNodeID = nodeIndex; - nodeIndex++; + passGraphOutputs.insert(output.field); } + } - // clear and rebuild reflection for each pass. - // TODO: This is unsafe because render pass reflection may depend on the compile data, which isn't available here. - renderPassUI.mReflection = mpRenderGraph->mNodeData[nameToIndex.second].pPass->reflect({}); + uint32_t inputPinIndex = 0; + uint32_t outputPinIndex = 0; - // test to see if we have hit a graph output - std::unordered_set passGraphOutputs; + // add all of the incoming connections + for (uint32_t i = 0; i < pCurrentPass->getIncomingEdgeCount(); ++i) + { + uint32_t edgeID = pCurrentPass->getIncomingEdge(i); + auto currentEdge = mpRenderGraph->mEdgeData[edgeID]; + uint32_t pinIndex = 0; + inputPinIndex = 0; - for (const auto& output : mpRenderGraph->mOutputs) + while (pinIndex < renderPassUI.mReflection.getFieldCount()) { - if (output.nodeId == nameToIndex.second) + bool isInput = + is_set(renderPassUI.mReflection.getField(pinIndex)->getVisibility(), RenderPassReflection::Field::Visibility::Input); + if (isInput) { - passGraphOutputs.insert(output.field); + if (renderPassUI.mReflection.getField(pinIndex)->getName() == currentEdge.dstField) + { + break; + } + inputPinIndex++; } + pinIndex++; } - uint32_t inputPinIndex = 0; - uint32_t outputPinIndex = 0; + auto pSourceNode = mpRenderGraph->mNodeData.find(mpRenderGraph->mpGraph->getEdge(edgeID)->getSourceNode()); + FALCOR_ASSERT(pSourceNode != mpRenderGraph->mNodeData.end()); + addedExecutionInput = currentEdge.dstField.empty(); - // add all of the incoming connections - for (uint32_t i = 0; i < pCurrentPass->getIncomingEdgeCount(); ++i) + 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.name : currentEdge.srcField; + std::string outputPinString = pSourceNode->second.name + "." + srcFieldName; + if (nodeConnectedInput.find(inputPinString) == nodeConnectedInput.end()) { - uint32_t edgeID = pCurrentPass->getIncomingEdge(i); - auto currentEdge = mpRenderGraph->mEdgeData[edgeID]; - uint32_t pinIndex = 0; - inputPinIndex = 0; - - while (pinIndex < renderPassUI.mReflection.getFieldCount()) - { - bool isInput = is_set(renderPassUI.mReflection.getField(pinIndex)->getVisibility(),RenderPassReflection::Field::Visibility::Input); - if (isInput) - { - if (renderPassUI.mReflection.getField(pinIndex)->getName() == currentEdge.dstField) { break; } - inputPinIndex++; - } - pinIndex++; - } - - auto pSourceNode = mpRenderGraph->mNodeData.find( mpRenderGraph->mpGraph->getEdge(edgeID)->getSourceNode()); - FALCOR_ASSERT(pSourceNode != mpRenderGraph->mNodeData.end()); - addedExecutionInput = currentEdge.dstField.empty(); + nodeConnectedInput.insert(inputPinString); + } - 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.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.name); + mOutputToInputPins[outputPinString].push_back(std::make_pair(inputPinIndex, renderPassUI.mGuiNodeID)); + mInputPinStringToLinkID.insert(std::make_pair(inputPinString, edgeID)); + } - 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)); - } + // add all of the outgoing connections + for (uint32_t i = 0; i < pCurrentPass->getOutgoingEdgeCount(); ++i) + { + uint32_t edgeID = pCurrentPass->getOutgoingEdge(i); + auto currentEdge = mpRenderGraph->mEdgeData[edgeID]; + outputPinIndex = 0; - // add all of the outgoing connections - for (uint32_t i = 0; i < pCurrentPass->getOutgoingEdgeCount(); ++i) + std::string pinString = nameToIndex.first + "." + currentEdge.srcField; + if (nodeConnectedOutput.find(pinString) != nodeConnectedOutput.end()) { - uint32_t edgeID = pCurrentPass->getOutgoingEdge(i); - auto currentEdge = mpRenderGraph->mEdgeData[edgeID]; - outputPinIndex = 0; - - std::string pinString = nameToIndex.first + "." + currentEdge.srcField; - if (nodeConnectedOutput.find(pinString) != nodeConnectedOutput.end()) - { - break; - } + break; + } - bool isGraphOutput = passGraphOutputs.find(currentEdge.srcField) != passGraphOutputs.end(); - uint32_t pinIndex = 0; + bool isGraphOutput = passGraphOutputs.find(currentEdge.srcField) != passGraphOutputs.end(); + uint32_t pinIndex = 0; - while (pinIndex < renderPassUI.mReflection.getFieldCount()) + while (pinIndex < renderPassUI.mReflection.getFieldCount()) + { + bool isOutput = + (static_cast( + renderPassUI.mReflection.getField(pinIndex)->getVisibility() & RenderPassReflection::Field::Visibility::Output + ) != 0); + if (isOutput) { - bool isOutput = (static_cast(renderPassUI.mReflection.getField(pinIndex)->getVisibility() & RenderPassReflection::Field::Visibility::Output) != 0); - if (isOutput) + if (renderPassUI.mReflection.getField(pinIndex)->getName() == currentEdge.srcField) { - if (renderPassUI.mReflection.getField(pinIndex)->getName() == currentEdge.srcField) { break; } - outputPinIndex++; + break; } - pinIndex++; + outputPinIndex++; } + pinIndex++; + } - auto pDestNode = mpRenderGraph->mNodeData.find(mpRenderGraph->mpGraph->getEdge(edgeID)->getDestNode()); - FALCOR_ASSERT(pDestNode != mpRenderGraph->mNodeData.end()); - addedExecutionOutput = currentEdge.dstField.empty(); + auto pDestNode = mpRenderGraph->mNodeData.find(mpRenderGraph->mpGraph->getEdge(edgeID)->getDestNode()); + FALCOR_ASSERT(pDestNode != mpRenderGraph->mNodeData.end()); + addedExecutionOutput = currentEdge.dstField.empty(); - 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.name + "." + srcFieldName; + 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.name + "." + srcFieldName; - renderPassUI.addUIPin(srcFieldName, outputPinIndex, false, dstFieldName, pDestNode->second.name, isGraphOutput); - nodeConnectedOutput.insert(pinString); - } + renderPassUI.addUIPin(srcFieldName, outputPinIndex, false, dstFieldName, pDestNode->second.name, isGraphOutput); + nodeConnectedOutput.insert(pinString); + } - // Now we know which nodes are connected within the graph and not - inputPinIndex = 0; - outputPinIndex = 0; + // Now we know which nodes are connected within the graph and not + inputPinIndex = 0; + outputPinIndex = 0; - for (uint32_t i = 0; i < renderPassUI.mReflection.getFieldCount(); ++i) - { - const auto& currentField = *renderPassUI.mReflection.getField(i); + for (uint32_t i = 0; i < renderPassUI.mReflection.getFieldCount(); ++i) + { + const auto& currentField = *renderPassUI.mReflection.getField(i); - if (is_set(currentField.getVisibility(), RenderPassReflection::Field::Visibility::Input)) + if (is_set(currentField.getVisibility(), RenderPassReflection::Field::Visibility::Input)) + { + if (nodeConnectedInput.find(nameToIndex.first + "." + currentField.getName()) == nodeConnectedInput.end()) { - if (nodeConnectedInput.find(nameToIndex.first + "." + currentField.getName()) == nodeConnectedInput.end()) - { - renderPassUI.addUIPin(currentField.getName(), inputPinIndex, true, ""); - } - - inputPinIndex++; + renderPassUI.addUIPin(currentField.getName(), inputPinIndex, true, ""); } - if (is_set(currentField.getVisibility(), RenderPassReflection::Field::Visibility::Output)) - { - if (nodeConnectedOutput.find(nameToIndex.first + "." + currentField.getName()) == nodeConnectedOutput.end()) - { - bool isGraphOutput = passGraphOutputs.find(currentField.getName()) != passGraphOutputs.end(); - renderPassUI.addUIPin(currentField.getName(), outputPinIndex, false, "", "", isGraphOutput); - } - - outputPinIndex++; - } + inputPinIndex++; } - // unconnected nodes will be renamed when they are connected - if (!addedExecutionInput) renderPassUI.addUIPin(kInPrefix + nameToIndex.first, static_cast( renderPassUI.mInputPins.size()), true, "", "", false); - if (!addedExecutionOutput) renderPassUI.addUIPin(kOutPrefix + nameToIndex.first, static_cast(renderPassUI.mOutputPins.size()), false, "", "", false); + if (is_set(currentField.getVisibility(), RenderPassReflection::Field::Visibility::Output)) + { + if (nodeConnectedOutput.find(nameToIndex.first + "." + currentField.getName()) == nodeConnectedOutput.end()) + { + bool isGraphOutput = passGraphOutputs.find(currentField.getName()) != passGraphOutputs.end(); + renderPassUI.addUIPin(currentField.getName(), outputPinIndex, false, "", "", isGraphOutput); + } - mRenderPassUI.emplace(std::make_pair(nameToIndex.first, std::move(renderPassUI))); + outputPinIndex++; + } } + + // unconnected nodes will be renamed when they are connected + if (!addedExecutionInput) + renderPassUI.addUIPin( + kInPrefix + nameToIndex.first, static_cast(renderPassUI.mInputPins.size()), true, "", "", false + ); + if (!addedExecutionOutput) + renderPassUI.addUIPin( + kOutPrefix + nameToIndex.first, static_cast(renderPassUI.mOutputPins.size()), false, "", "", false + ); + + mRenderPassUI.emplace(std::make_pair(nameToIndex.first, std::move(renderPassUI))); } } +} // namespace Falcor diff --git a/Source/Falcor/RenderGraph/RenderGraphUI.h b/Source/Falcor/RenderGraph/RenderGraphUI.h index 19e6fdcda..2642ee50c 100644 --- a/Source/Falcor/RenderGraph/RenderGraphUI.h +++ b/Source/Falcor/RenderGraph/RenderGraphUI.h @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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,187 +38,227 @@ namespace Falcor { - class NodeGraphEditorGui; - class RenderGraphUI; - class RenderContext; - - /** Class for drawing UI elements for a render pass in the render graph editor. - */ - class FALCOR_API RenderPassUI +class NodeGraphEditorGui; +class RenderGraphUI; +class RenderContext; + +/** + * Class for drawing UI elements for a render pass in the render graph editor. + */ +class FALCOR_API 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(const std::string& passName, RenderGraphUI* pGraphUI, uint32_t index = 0, bool input = false); + + friend class RenderGraphUI; + +private: + class PinUI { 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(const std::string& passName, RenderGraphUI* pGraphUI, uint32_t index = 0, bool input = false); - - friend class RenderGraphUI; - - private: - class PinUI - { - public: - - std::string mPinName; - uint32_t mGuiPinID; - std::string mConnectedPinName; - std::string mConnectedNodeName; - bool mIsGraphOutput; - - 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; - std::unordered_map mNameToIndexInput; - - std::vector mOutputPins; - std::unordered_map mNameToIndexOutput; - - uint32_t mGuiNodeID; - RenderPassReflection mReflection; + std::string mPinName; + uint32_t mGuiPinID; + std::string mConnectedPinName; + std::string mConnectedNodeName; + bool mIsGraphOutput; + + 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); }; - /** Class for drawing UI elements for a graph in the render graph editor. - */ - class FALCOR_API RenderGraphUI - { - public: - RenderGraphUI(); - - RenderGraphUI(const RenderGraph::SharedPtr& pGraph, const std::string& graphName); - - ~RenderGraphUI(); - - /** Display enter graph in GUI. - */ - void renderUI(RenderContext* pRenderContext, Gui* pGui); + std::vector mInputPins; + std::unordered_map mNameToIndexInput; - /** Clear graph ui for rebuilding node graph - */ - void reset(); + std::vector mOutputPins; + std::unordered_map mNameToIndexOutput; - /** Set ui to rebuild all display data before next render ui - */ - void setToRebuild() { mRebuildDisplayData = true; } + uint32_t mGuiNodeID; + RenderPassReflection mReflection; +}; - /** Writes out all the changes made to the graph - */ - void writeUpdateScriptToFile(RenderContext* pRenderContext, const std::filesystem::path& filePath, float lastFrameTimes); - - /** function used to add an edge for the internally referenced render graph and update ui data - */ - bool addLink(const std::string& srcPass, const std::string& dstPass, const std::string& srcField, const std::string& dstField, uint32_t& color); - - /** function used to remove edge referenced graph and update ui data - */ - void removeEdge(const std::string& srcPass, const std::string& dstPass, const std::string& srcField, const std::string& dstField); - - /** function used to remove pass on referenced graph and update ui data - */ - void removeRenderPass(const std::string& name); - - /** function used to add a graph output on referenced graph from one string - */ - void addOutput(const std::string& outputParam); - - /** function used to add a graph output on referenced graph and update ui data - */ - void addOutput(const std::string& outputPass, const std::string& outputField); - - /** function used to remove graph output on referenced graph and update ui data - */ - void removeOutput(const std::string& outputPass, const std::string& outputField); - - /** function used to add a new node for a render pass referenced graph and update ui data - */ - void addRenderPass(const std::string& name, const std::string& nodeTypeName); - - /** Returns the current log from the events in the editor - */ - std::string getCurrentLog() const { return mLogString; } - - /** Toggle building up delta changes for live preview - */ - void setRecordUpdates(bool recordUpdates); - - /** Clears the current log - */ - void clearCurrentLog() { mLogString.clear(); } - - /** Update change for the graph based on script - */ - void updateGraph(RenderContext* pRenderContext); - - /** Get name of reference graph - */ - std::string getName() { return mRenderGraphName; } - - private: - // forward declaration. private to avoid initialization outside of implementation file - class NodeGraphEditorGui; - class RenderGraphNode; +/** + * Class for drawing UI elements for a graph in the render graph editor. + */ +class FALCOR_API RenderGraphUI +{ +public: + RenderGraphUI(); + + RenderGraphUI(const ref& pGraph, const std::string& graphName); + + ~RenderGraphUI(); + + /** + * Display enter graph in GUI. + */ + void renderUI(RenderContext* pRenderContext, Gui* pGui); + + /** + * Clear graph ui for rebuilding node graph + */ + void reset(); + + /** + * Set ui to rebuild all display data before next render ui + */ + void setToRebuild() { mRebuildDisplayData = true; } + + /** + * Writes out all the changes made to the graph + */ + void writeUpdateScriptToFile(RenderContext* pRenderContext, const std::filesystem::path& filePath, float lastFrameTimes); + + /** + * function used to add an edge for the internally referenced render graph and update ui data + */ + bool addLink( + const std::string& srcPass, + const std::string& dstPass, + const std::string& srcField, + const std::string& dstField, + uint32_t& color + ); + + /** + * function used to remove edge referenced graph and update ui data + */ + void removeEdge(const std::string& srcPass, const std::string& dstPass, const std::string& srcField, const std::string& dstField); + + /** + * function used to remove pass on referenced graph and update ui data + */ + void removeRenderPass(const std::string& name); + + /** + * function used to add a graph output on referenced graph from one string + */ + void addOutput(const std::string& outputParam); + + /** + * function used to add a graph output on referenced graph and update ui data + */ + void addOutput(const std::string& outputPass, const std::string& outputField); + + /** + * function used to remove graph output on referenced graph and update ui data + */ + void removeOutput(const std::string& outputPass, const std::string& outputField); + + /** + * function used to add a new node for a render pass referenced graph and update ui data + */ + void addRenderPass(const std::string& name, const std::string& nodeTypeName); + + /** + * Returns the current log from the events in the editor + */ + std::string getCurrentLog() const { return mLogString; } + + /** + * Toggle building up delta changes for live preview + */ + void setRecordUpdates(bool recordUpdates); + + /** + * Clears the current log + */ + void clearCurrentLog() { mLogString.clear(); } + + /** + * Update change for the graph based on script + */ + void updateGraph(RenderContext* pRenderContext); + + /** + * Get name of reference graph + */ + std::string getName() { return mRenderGraphName; } + +private: + // forward declaration. private to avoid initialization outside of implementation file + class NodeGraphEditorGui; + class RenderGraphNode; - /** Updates structure for drawing the GUI graph - */ - void updateDisplayData(RenderContext* pRenderContext); + /** + * Updates structure for drawing the GUI graph + */ + void updateDisplayData(RenderContext* pRenderContext); - /** Updates information about pin connections and graph output. - */ - void updatePins(bool addLinks = true); + /** + * Updates information about pin connections and graph output. + */ + void updatePins(bool addLinks = true); - /** Helper function to calculate position of the next node in execution order - */ - float2 getNextNodePosition(uint32_t nodeID); + /** + * Helper function to calculate position of the next node in execution order + */ + float2 getNextNodePosition(uint32_t nodeID); - /** Renders specialized pop up menu. - */ - void renderPopupMenu(); + /** + * Renders specialized pop up menu. + */ + void renderPopupMenu(); - /** Displays pop-up message if can auto resolve on an edge - */ - bool autoResolveWarning(const std::string& srcString, const std::string& dstString); + /** + * Displays pop-up message if can auto resolve on an edge + */ + bool autoResolveWarning(const std::string& srcString, const std::string& dstString); - /** String containing the most recent log results from and isValid render graph call - */ - std::string mLogString; + /** + * String containing the most recent log results from and isValid render graph call + */ + std::string mLogString; - // start with reference of render graph - RenderGraph::SharedPtr mpRenderGraph; + // start with reference of render graph + ref mpRenderGraph; - RenderGraphIR::SharedPtr mpIr; + std::shared_ptr mpIr; - float2 mNewNodeStartPosition{ -40.0f, 100.0f }; - float mMaxNodePositionX = 0.0f; + float2 mNewNodeStartPosition{-40.0f, 100.0f}; + float mMaxNodePositionX = 0.0f; - std::unordered_set mAllNodeTypeStrings; - std::vector mAllNodeTypes; + std::unordered_set mAllNodeTypeStrings; + std::vector mAllNodeTypes; - std::unordered_map mRenderPassUI; + std::unordered_map mRenderPassUI; - std::unordered_map mInputPinStringToLinkID; + std::unordered_map mInputPinStringToLinkID; - // maps output pin name to input pin ids. Pair first is pin id, second is node id - std::unordered_map > > mOutputToInputPins; + // maps output pin name to input pin ids. Pair first is pin id, second is node id + std::unordered_map > > mOutputToInputPins; - // if in external editing mode, building list of commands for changes to send to the other process - std::string mUpdateCommands; - std::string mLastCommand; - bool mRecordUpdates = false; + // if in external editing mode, building list of commands for changes to send to the other process + std::string mUpdateCommands; + std::string mLastCommand; + bool mRecordUpdates = false; - // to avoid attempting to write changes every frame. - float mTimeSinceLastUpdate = 0.0f; - bool mDisplayDragAndDropPopup = false; - bool mAddedFromDragAndDrop = false; - std::string mNextPassName = ""; - std::string mRenderGraphName; - bool mDisplayAutoResolvePopup = true; + // to avoid attempting to write changes every frame. + float mTimeSinceLastUpdate = 0.0f; + bool mDisplayDragAndDropPopup = false; + bool mAddedFromDragAndDrop = false; + std::string mNextPassName = ""; + std::string mRenderGraphName; + bool mDisplayAutoResolvePopup = true; - // internal node GUi structure - std::shared_ptr mpNodeGraphEditor; + // internal node GUi structure + std::shared_ptr mpNodeGraphEditor; - // Flag to re-traverse the graph and build on of the intermediate data again. - bool mRebuildDisplayData = true; - bool mShouldUpdate = false; - }; -} + // Flag to re-traverse the graph and build on of the intermediate data again. + bool mRebuildDisplayData = true; + bool mShouldUpdate = false; +}; +} // namespace Falcor diff --git a/Source/Falcor/RenderGraph/RenderPass.cpp b/Source/Falcor/RenderGraph/RenderPass.cpp index 94c34a6e8..5bfebe00b 100644 --- a/Source/Falcor/RenderGraph/RenderPass.cpp +++ b/Source/Falcor/RenderGraph/RenderPass.cpp @@ -29,33 +29,33 @@ namespace Falcor { - RenderData::RenderData(const std::string& passName, const ResourceCache::SharedPtr& pResourceCache, const InternalDictionary::SharedPtr& pDict, const uint2& defaultTexDims, ResourceFormat defaultTexFormat) - : mName(passName) - , mpResources(pResourceCache) - , mpDictionary(pDict) - , mDefaultTexDims(defaultTexDims) - , mDefaultTexFormat(defaultTexFormat) - { - if (!mpDictionary) mpDictionary = InternalDictionary::create(); - } +RenderData::RenderData( + const std::string& passName, + ResourceCache& resources, + InternalDictionary& dictionary, + const uint2& defaultTexDims, + ResourceFormat defaultTexFormat +) + : mName(passName), mResources(resources), mDictionary(dictionary), mDefaultTexDims(defaultTexDims), mDefaultTexFormat(defaultTexFormat) +{} - const Resource::SharedPtr& RenderData::getResource(const std::string_view name) const - { - return mpResources->getResource(fmt::format("{}.{}", mName, name)); - } +const ref& RenderData::getResource(const std::string_view name) const +{ + return mResources.getResource(fmt::format("{}.{}", mName, name)); +} - Texture::SharedPtr RenderData::getTexture(const std::string_view name) const - { - auto pResource = getResource(name); - return pResource ? pResource->asTexture() : nullptr; - } +ref RenderData::getTexture(const std::string_view name) const +{ + auto pResource = getResource(name); + return pResource ? pResource->asTexture() : nullptr; +} - RenderPass::SharedPtr RenderPass::create(std::string_view type, std::shared_ptr pDevice, const Dictionary& dict, PluginManager& pm) - { - // Try to load a plugin of the same name, if render pass class is not registered yet. - if (!pm.hasClass(type)) - pm.loadPluginByName(type); +ref RenderPass::create(std::string_view type, ref pDevice, const Dictionary& dict, PluginManager& pm) +{ + // Try to load a plugin of the same name, if render pass class is not registered yet. + if (!pm.hasClass(type)) + pm.loadPluginByName(type); - return pm.createClass(type, std::move(pDevice), dict); - } + return pm.createClass(type, pDevice, dict); } +} // namespace Falcor diff --git a/Source/Falcor/RenderGraph/RenderPass.h b/Source/Falcor/RenderGraph/RenderPass.h index ee9fb9f77..4f94a1ef5 100644 --- a/Source/Falcor/RenderGraph/RenderPass.h +++ b/Source/Falcor/RenderGraph/RenderPass.h @@ -28,6 +28,7 @@ #pragma once #include "ResourceCache.h" #include "Core/Macros.h" +#include "Core/Object.h" #include "Core/Plugin.h" #include "Core/HotReloadFlags.h" #include "Core/API/Resource.h" @@ -44,185 +45,215 @@ namespace Falcor { - /** Helper class that's passed to the user during `RenderPass::execute()` - */ - class FALCOR_API RenderData +/** + * Helper class that's passed to the user during `RenderPass::execute()` + */ +class FALCOR_API RenderData +{ +public: + /** + * 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 ref& operator[](const std::string_view name) const { return getResource(name); } + + /** + * 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 ref& getResource(const std::string_view name) const; + + /** + * Get a texture + * @param[in] name The name of the pass' texture (i.e. "outputColor"). No need to specify the pass' name + * @return If the texture exists, a pointer to the texture. Otherwise, nullptr + */ + ref getTexture(const std::string_view name) const; + + /** + * Get the global dictionary. You can use it to pass data between different passes + */ + InternalDictionary& getDictionary() const { return mDictionary; } + + /** + * Get the default dimensions used for Texture2Ds (when `0` is specified as the dimensions in `RenderPassReflection`) + */ + const uint2& 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: + RenderData( + const std::string& passName, + ResourceCache& resources, + InternalDictionary& dictionary, + const uint2& defaultTexDims, + ResourceFormat defaultTexFormat + ); + + const std::string& mName; + ResourceCache& mResources; + InternalDictionary& mDictionary; + uint2 mDefaultTexDims; + ResourceFormat mDefaultTexFormat; + + friend class RenderGraphExe; +}; + +/** + * Base class for render passes. + * + * Render passes are expected to implement a static create() function that returns + * a ref<> to a new object, or throws an exception if creation failed. + * + * Render passes are inserted in a render graph, which is executed at runtime. + * Each render pass declares its I/O requirements in the reflect() function, + * and as part of the render graph compilation their compile() function is called. + * At runtime, execute() is called each frame to generate the pass outputs. + */ +class FALCOR_API RenderPass : public Object +{ +public: + using PluginCreate = std::function(ref pDevice, const Dictionary& dict)>; + struct PluginInfo { - public: - /** 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_view name) const { return getResource(name); } - - /** 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_view name) const; - - /** Get a texture - \param[in] name The name of the pass' texture (i.e. "outputColor"). No need to specify the pass' name - \return If the texture exists, a pointer to the texture. Otherwise, nullptr - */ - Texture::SharedPtr getTexture(const std::string_view name) const; - - /** Get the global dictionary. You can use it to pass data between different passes - */ - InternalDictionary& getDictionary() const { return (*mpDictionary); } - - /** Get the global dictionary. You can use it to pass data between different passes - */ - InternalDictionary::SharedPtr getDictionaryPtr() const { return mpDictionary; } - - /** Get the default dimensions used for Texture2Ds (when `0` is specified as the dimensions in `RenderPassReflection`) - */ - const uint2& 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: - RenderData(const std::string& passName, const ResourceCache::SharedPtr& pResourceCache, const InternalDictionary::SharedPtr& pDict, const uint2& defaultTexDims, ResourceFormat defaultTexFormat); - - const std::string& mName; - ResourceCache::SharedPtr mpResources; - InternalDictionary::SharedPtr mpDictionary; - uint2 mDefaultTexDims; - ResourceFormat mDefaultTexFormat; - - friend class RenderGraphExe; + std::string desc; ///< Brief textual description of what the render pass does. }; - /** Base class for render passes. + FALCOR_PLUGIN_BASE_CLASS(RenderPass); - Render passes are expected to implement a static create() function that returns - a shared pointer to a new object, or throws an exception if creation failed. - The constructor should be private to force creation of shared pointers. - - Render passes are inserted in a render graph, which is executed at runtime. - Each render pass declares its I/O requirements in the reflect() function, - and as part of the render graph compilation their compile() function is called. - At runtime, execute() is called each frame to generate the pass outputs. - */ - class FALCOR_API RenderPass + struct CompileData { - public: - using PluginCreate = std::function(std::shared_ptr pDevice, const Dictionary& dict)>; - struct PluginInfo - { - std::string desc; ///< Brief textual description of what the render pass does. - }; - - FALCOR_PLUGIN_BASE_CLASS(RenderPass); - - using SharedPtr = std::shared_ptr; - - struct CompileData - { - uint2 defaultTexDims; ///< Default texture dimension (same as the swap chain size). - ResourceFormat defaultTexFormat; ///< Default texture format (same as the swap chain format). - RenderPassReflection connectedResources; ///< Reflection data for connected resources, if available. This field may be empty when reflect() is called. - }; - - /** Create a render pass object of the given type. - Uses the plugin manager to create the render pass. - If the type is not yet registered, it tries to load a plugin of the same name as the render pass type. - */ - static SharedPtr create(std::string_view type, std::shared_ptr pDevice, const Dictionary& dict = {}, PluginManager& pm = PluginManager::instance()); - - virtual ~RenderPass() = default; - - /** Get the render pass type. - */ - const std::string& getType() const { return getPluginType(); } - - /** Get the render pass description. - */ - const std::string& getDesc() const { return getPluginInfo().desc; } - - /** Called before render graph compilation. Describes I/O requirements of the pass. - The function may be called repeatedly and should not perform any expensive operations. - The requirements can't change after the graph is compiled. If the I/O are dynamic, you'll need to - trigger re-compilation of the render graph yourself by calling 'requestRecompile()'. - */ - 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* pRenderContext, const CompileData& compileData) {} - - /** Executes the pass. - */ - virtual void execute(RenderContext* pRenderContext, const RenderData& renderData) = 0; - - /** Get a dictionary that can be used to reconstruct the object - */ - virtual Dictionary getScriptingDictionary() { return {}; } - - /** Render the pass's UI - * Note: This is deprecated. - */ - virtual void renderUI(Gui::Widgets& widget) {} - - /** Render the pass's UI - */ - virtual void renderUI(RenderContext* pRenderContext, Gui::Widgets& widget) { renderUI(widget); } - - /** Set a scene into the render pass. - This function is called when a new scene is loaded. - \param[in] pRenderContext The render context. - \param[in] pScene New scene, or nullptr if no scene. - */ - virtual void setScene(RenderContext* pRenderContext, const std::shared_ptr& pScene) {} - - /** Called upon scene updates. - This function is called when the loaded scene changes. The pass is responsible for handling - scene changes that affect its internal resources, for example, shader programs and data structures. - \param[in] pRenderContext The render context. - \param[in] sceneUpdates Accumulated scene update flags since the last call, or since `setScene()` for a new scene. - */ - virtual void onSceneUpdates(RenderContext* pRenderContext, Scene::UpdateFlags sceneUpdates) {} - - /** Mouse event handler. - Returns true if the event was handled by the object, false otherwise - */ - virtual bool onMouseEvent(const MouseEvent& mouseEvent) { return false; } - - /** Keyboard event handler - Returns true if the event was handled by the object, false otherwise - */ - virtual bool onKeyEvent(const KeyboardEvent& keyEvent) { return false; } - - /** Called upon hot reload (by pressing F5). - \param[in] reloaded Resources that have been reloaded. - */ - virtual void onHotReload(HotReloadFlags reloaded) {} - - /** Applies graph presets to this render pass. - */ - virtual void applySettings(const Dictionary& dict) {} - - /** Get the current pass' name as defined in the graph - */ - const std::string& getName() const { return mName; } - - protected: - RenderPass(std::shared_ptr pDevice) : mpDevice(std::move(pDevice)) {} - - /** Request a recompilation of the render graph. - Call this function if the I/O requirements of the pass have changed. - During the recompile, reflect() will be called for the pass to report the new requirements. - */ - void requestRecompile() { mPassChangedCB(); } - - std::shared_ptr mpDevice; - - std::string mName; - - std::function mPassChangedCB = [] {}; - - friend class RenderGraph; + uint2 defaultTexDims; ///< Default texture dimension (same as the swap chain size). + ResourceFormat defaultTexFormat; ///< Default texture format (same as the swap chain format). + RenderPassReflection connectedResources; ///< Reflection data for connected resources, if available. This field may be empty when + ///< reflect() is called. }; -} + + /** + * Create a render pass object of the given type. + * Uses the plugin manager to create the render pass. + * If the type is not yet registered, it tries to load a plugin of the same name as the render pass type. + */ + static ref create( + std::string_view type, + ref pDevice, + const Dictionary& dict = {}, + PluginManager& pm = PluginManager::instance() + ); + + virtual ~RenderPass() = default; + + /** + * Get the render pass type. + */ + const std::string& getType() const { return getPluginType(); } + + /** + * Get the render pass description. + */ + const std::string& getDesc() const { return getPluginInfo().desc; } + + /** + * Called before render graph compilation. Describes I/O requirements of the pass. + * The function may be called repeatedly and should not perform any expensive operations. + * The requirements can't change after the graph is compiled. If the I/O are dynamic, you'll need to + * trigger re-compilation of the render graph yourself by calling 'requestRecompile()'. + */ + 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* pRenderContext, const CompileData& compileData) {} + + /** + * Executes the pass. + */ + virtual void execute(RenderContext* pRenderContext, const RenderData& renderData) = 0; + + /** + * Get a dictionary that can be used to reconstruct the object + */ + virtual Dictionary getScriptingDictionary() { return {}; } + + /** + * Render the pass's UI + * * Note: This is deprecated. + */ + virtual void renderUI(Gui::Widgets& widget) {} + + /** + * Render the pass's UI + */ + virtual void renderUI(RenderContext* pRenderContext, Gui::Widgets& widget) { renderUI(widget); } + + /** + * Set a scene into the render pass. + * This function is called when a new scene is loaded. + * @param[in] pRenderContext The render context. + * @param[in] pScene New scene, or nullptr if no scene. + */ + virtual void setScene(RenderContext* pRenderContext, const ref& pScene) {} + + /** + * Called upon scene updates. + * This function is called when the loaded scene changes. The pass is responsible for handling + * scene changes that affect its internal resources, for example, shader programs and data structures. + * @param[in] pRenderContext The render context. + * @param[in] sceneUpdates Accumulated scene update flags since the last call, or since `setScene()` for a new scene. + */ + virtual void onSceneUpdates(RenderContext* pRenderContext, Scene::UpdateFlags sceneUpdates) {} + + /** + * Mouse event handler. + * Returns true if the event was handled by the object, false otherwise + */ + virtual bool onMouseEvent(const MouseEvent& mouseEvent) { return false; } + + /** + * Keyboard event handler + * Returns true if the event was handled by the object, false otherwise + */ + virtual bool onKeyEvent(const KeyboardEvent& keyEvent) { return false; } + + /** + * Called upon hot reload (by pressing F5). + * @param[in] reloaded Resources that have been reloaded. + */ + virtual void onHotReload(HotReloadFlags reloaded) {} + + /** + * Applies graph presets to this render pass. + */ + virtual void applySettings(const Dictionary& dict) {} + + /** + * Get the current pass' name as defined in the graph + */ + const std::string& getName() const { return mName; } + +protected: + RenderPass(ref pDevice) : mpDevice(pDevice) {} + + /** + * Request a recompilation of the render graph. + * Call this function if the I/O requirements of the pass have changed. + * During the recompile, reflect() will be called for the pass to report the new requirements. + */ + void requestRecompile() { mPassChangedCB(); } + + ref mpDevice; + + std::string mName; + + std::function mPassChangedCB = [] {}; + + friend class RenderGraph; +}; +} // namespace Falcor diff --git a/Source/Falcor/RenderGraph/RenderPassHelpers.cpp b/Source/Falcor/RenderGraph/RenderPassHelpers.cpp index 2ed6040ee..ab786d61f 100644 --- a/Source/Falcor/RenderGraph/RenderPassHelpers.cpp +++ b/Source/Falcor/RenderGraph/RenderPassHelpers.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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,26 +32,32 @@ namespace Falcor { - uint2 RenderPassHelpers::calculateIOSize(const IOSize selection, const uint2 fixedSize, const uint2 windowSize) - { - uint2 sz = {}; - if (selection == RenderPassHelpers::IOSize::Fixed) sz = fixedSize; - else if (selection == RenderPassHelpers::IOSize::Full) sz = windowSize; - else if (selection == RenderPassHelpers::IOSize::Half) sz = windowSize / uint2(2); - else if (selection == RenderPassHelpers::IOSize::Quarter) sz = windowSize / uint2(4); - else if (selection == RenderPassHelpers::IOSize::Double) sz = windowSize * uint2(2); - else FALCOR_ASSERT(selection == RenderPassHelpers::IOSize::Default); - return sz; - } +uint2 RenderPassHelpers::calculateIOSize(const IOSize selection, const uint2 fixedSize, const uint2 windowSize) +{ + uint2 sz = {}; + if (selection == RenderPassHelpers::IOSize::Fixed) + sz = fixedSize; + else if (selection == RenderPassHelpers::IOSize::Full) + sz = windowSize; + else if (selection == RenderPassHelpers::IOSize::Half) + sz = windowSize / uint2(2); + else if (selection == RenderPassHelpers::IOSize::Quarter) + sz = windowSize / uint2(4); + else if (selection == RenderPassHelpers::IOSize::Double) + sz = windowSize * uint2(2); + else + FALCOR_ASSERT(selection == RenderPassHelpers::IOSize::Default); + return sz; +} - FALCOR_SCRIPT_BINDING(RenderPassHelpers) - { - pybind11::enum_ sz(m, "IOSize"); - sz.value("Default", RenderPassHelpers::IOSize::Default); - sz.value("Fixed", RenderPassHelpers::IOSize::Fixed); - sz.value("Full", RenderPassHelpers::IOSize::Full); - sz.value("Half", RenderPassHelpers::IOSize::Half); - sz.value("Quarter", RenderPassHelpers::IOSize::Quarter); - sz.value("Double", RenderPassHelpers::IOSize::Double); - } +FALCOR_SCRIPT_BINDING(RenderPassHelpers) +{ + pybind11::enum_ sz(m, "IOSize"); + sz.value("Default", RenderPassHelpers::IOSize::Default); + sz.value("Fixed", RenderPassHelpers::IOSize::Fixed); + sz.value("Full", RenderPassHelpers::IOSize::Full); + sz.value("Half", RenderPassHelpers::IOSize::Half); + sz.value("Quarter", RenderPassHelpers::IOSize::Quarter); + sz.value("Double", RenderPassHelpers::IOSize::Double); } +} // namespace Falcor diff --git a/Source/Falcor/RenderGraph/RenderPassHelpers.h b/Source/Falcor/RenderGraph/RenderPassHelpers.h index 9df6f6f16..b5ff5a1b8 100644 --- a/Source/Falcor/RenderGraph/RenderPassHelpers.h +++ b/Source/Falcor/RenderGraph/RenderPassHelpers.h @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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,142 +38,157 @@ namespace Falcor { - struct FALCOR_API RenderPassHelpers +struct FALCOR_API RenderPassHelpers +{ + /** + * Enum for commonly used render pass I/O sizes. + */ + enum class IOSize : uint32_t { - /** Enum for commonly used render pass I/O sizes. - */ - enum class IOSize : uint32_t - { - Default, ///< Use the default size. The size is determined based on whatever is bound (the system will use the window size by default). - Fixed, ///< Use fixed size in pixels. - Full, ///< Use full window size. - Half, ///< Use half window size. - Quarter, ///< Use quarter window size. - Double, ///< Use double window size. - }; - - /** UI dropdown for the IOSize enum values. - */ - static inline Gui::DropdownList kIOSizeList = - { - { (uint32_t)IOSize::Default, "Default" }, - { (uint32_t)IOSize::Fixed, "Fixed" }, - { (uint32_t)IOSize::Full, "Full window" }, - { (uint32_t)IOSize::Half, "Half window" }, - { (uint32_t)IOSize::Quarter, "Quarter window" }, - { (uint32_t)IOSize::Double, "Double window" }, - }; + Default, ///< Use the default size. The size is determined based on whatever is bound (the system will use the window size by + ///< default). + Fixed, ///< Use fixed size in pixels. + Full, ///< Use full window size. + Half, ///< Use half window size. + Quarter, ///< Use quarter window size. + Double, ///< Use double window size. + }; - /** Helper for calculating desired I/O size in pixels based on selected mode. - */ - static uint2 calculateIOSize(const IOSize selection, const uint2 fixedSize, const uint2 windowSize); + /** + * UI dropdown for the IOSize enum values. + */ + static inline Gui::DropdownList kIOSizeList = { + {(uint32_t)IOSize::Default, "Default"}, {(uint32_t)IOSize::Fixed, "Fixed"}, + {(uint32_t)IOSize::Full, "Full window"}, {(uint32_t)IOSize::Half, "Half window"}, + {(uint32_t)IOSize::Quarter, "Quarter window"}, {(uint32_t)IOSize::Double, "Double window"}, }; - // TODO: Move below out of the global scope, e.g. into RenderPassHelpers struct. - // TODO: Update render passes to use addRenderPass*() helpers. + /** + * Helper for calculating desired I/O size in pixels based on selected mode. + */ + static uint2 calculateIOSize(const IOSize selection, const uint2 fixedSize, const uint2 windowSize); +}; - /** Helper struct with metadata for a render pass input/output. - */ - struct ChannelDesc - { - 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::Unknown; ///< Default format is 'Unknown', which means let the system decide. - }; +// TODO: Move below out of the global scope, e.g. into RenderPassHelpers struct. +// TODO: Update render passes to use addRenderPass*() helpers. - using ChannelList = std::vector; +/** + * Helper struct with metadata for a render pass input/output. + */ +struct ChannelDesc +{ + 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::Unknown; ///< Default format is 'Unknown', which means let the system decide. +}; - /** Creates a list of defines to determine if optional render pass resources are valid to be accessed. - This function creates a define for every optional channel in the form of: +using ChannelList = std::vector; - #define 1 if resource is available - #define 0 otherwise +/** + * Creates a list of defines to determine if optional render pass resources are valid to be accessed. + * This function creates a define for every optional channel in the form of: + * + * #define 1 if resource is available + * #define 0 otherwise + * + * @param[in] channels List of channel descriptors. + * @param[in] renderData Render data containing the channel resources. + * @param[in] prefix Prefix used for defines. + * @return Returns a list of defines to add to the progrem. + */ +inline Program::DefineList getValidResourceDefines( + const ChannelList& channels, + const RenderData& renderData, + const std::string& prefix = "is_valid_" +) +{ + Program::DefineList defines; - \param[in] channels List of channel descriptors. - \param[in] renderData Render data containing the channel resources. - \param[in] prefix Prefix used for defines. - \return Returns a list of defines to add to the progrem. - */ - inline Program::DefineList getValidResourceDefines(const ChannelList& channels, const RenderData& renderData, const std::string& prefix = "is_valid_") + for (const auto& desc : channels) { - Program::DefineList defines; - - for (const auto& desc : channels) + if (desc.optional && !desc.texname.empty()) { - if (desc.optional && !desc.texname.empty()) - { - defines.add(prefix + desc.texname, renderData[desc.name] != nullptr ? "1" : "0"); - } + defines.add(prefix + desc.texname, renderData[desc.name] != nullptr ? "1" : "0"); } - - return defines; } - /** Adds a list of input channels to the render pass reflection. - \param[in] reflector Render pass reflection object. - \param[in] channels List of channels. - \param[in] bindFlags Optional bind flags. The default is 'ShaderResource' for all inputs. - \param[in] dim Optional dimension. The default (0,0) means use whatever is bound (the system will use the window size by default). - */ - inline void addRenderPassInputs( - RenderPassReflection& reflector, - const ChannelList& channels, - ResourceBindFlags bindFlags = ResourceBindFlags::ShaderResource, - const uint2 dim = {}) + return defines; +} + +/** + * Adds a list of input channels to the render pass reflection. + * @param[in] reflector Render pass reflection object. + * @param[in] channels List of channels. + * @param[in] bindFlags Optional bind flags. The default is 'ShaderResource' for all inputs. + * @param[in] dim Optional dimension. The default (0,0) means use whatever is bound (the system will use the window size by default). + */ +inline void addRenderPassInputs( + RenderPassReflection& reflector, + const ChannelList& channels, + ResourceBindFlags bindFlags = ResourceBindFlags::ShaderResource, + const uint2 dim = {} +) +{ + for (const auto& it : channels) { - for (const auto& it : channels) - { - auto& tex = reflector.addInput(it.name, it.desc).texture2D(dim.x, dim.y); - tex.bindFlags(bindFlags); - if (it.format != ResourceFormat::Unknown) tex.format(it.format); - if (it.optional) tex.flags(RenderPassReflection::Field::Flags::Optional); - } + auto& tex = reflector.addInput(it.name, it.desc).texture2D(dim.x, dim.y); + tex.bindFlags(bindFlags); + if (it.format != ResourceFormat::Unknown) + tex.format(it.format); + if (it.optional) + tex.flags(RenderPassReflection::Field::Flags::Optional); } +} - /** Adds a list of output channels to the render pass reflection. - \param[in] reflector Render pass reflection object. - \param[in] channels List of channels. - \param[in] bindFlags Optional bind flags. The default is 'UnorderedAccess' for all outputs. - \param[in] dim Optional dimension. The default (0,0) means use whatever is bound (the system will use the window size by default). - */ - inline void addRenderPassOutputs( - RenderPassReflection& reflector, - const ChannelList& channels, - ResourceBindFlags bindFlags = ResourceBindFlags::UnorderedAccess, - const uint2 dim = {}) +/** + * Adds a list of output channels to the render pass reflection. + * @param[in] reflector Render pass reflection object. + * @param[in] channels List of channels. + * @param[in] bindFlags Optional bind flags. The default is 'UnorderedAccess' for all outputs. + * @param[in] dim Optional dimension. The default (0,0) means use whatever is bound (the system will use the window size by default). + */ +inline void addRenderPassOutputs( + RenderPassReflection& reflector, + const ChannelList& channels, + ResourceBindFlags bindFlags = ResourceBindFlags::UnorderedAccess, + const uint2 dim = {} +) +{ + for (const auto& it : channels) { - for (const auto& it : channels) - { - auto& tex = reflector.addOutput(it.name, it.desc).texture2D(dim.x, dim.y); - tex.bindFlags(bindFlags); - if (it.format != ResourceFormat::Unknown) tex.format(it.format); - if (it.optional) tex.flags(RenderPassReflection::Field::Flags::Optional); - } + auto& tex = reflector.addOutput(it.name, it.desc).texture2D(dim.x, dim.y); + tex.bindFlags(bindFlags); + if (it.format != ResourceFormat::Unknown) + tex.format(it.format); + if (it.optional) + tex.flags(RenderPassReflection::Field::Flags::Optional); } +} - /** Clears all available channels. - \param[in] pRenderContext Render context. - \param[in] channels List of channel descriptors. - \param[in] renderData Render data containing the channel resources. - */ - inline void clearRenderPassChannels(RenderContext* pRenderContext, const ChannelList& channels, const RenderData& renderData) +/** + * Clears all available channels. + * @param[in] pRenderContext Render context. + * @param[in] channels List of channel descriptors. + * @param[in] renderData Render data containing the channel resources. + */ +inline void clearRenderPassChannels(RenderContext* pRenderContext, const ChannelList& channels, const RenderData& renderData) +{ + for (const auto& channel : channels) { - for (const auto& channel : channels) + auto pTex = renderData.getTexture(channel.name); + if (pTex) { - auto pTex = renderData.getTexture(channel.name); - if (pTex) + if (isIntegerFormat(pTex->getFormat())) + { + pRenderContext->clearUAV(pTex->getUAV().get(), uint4(0)); + } + else { - if (isIntegerFormat(pTex->getFormat())) - { - pRenderContext->clearUAV(pTex->getUAV().get(), uint4(0)); - } - else - { - pRenderContext->clearUAV(pTex->getUAV().get(), float4(0.f)); - } + pRenderContext->clearUAV(pTex->getUAV().get(), float4(0.f)); } } } } +} // namespace Falcor diff --git a/Source/Falcor/RenderGraph/RenderPassReflection.cpp b/Source/Falcor/RenderGraph/RenderPassReflection.cpp index 7ecec4f85..b726ab87e 100644 --- a/Source/Falcor/RenderGraph/RenderPassReflection.cpp +++ b/Source/Falcor/RenderGraph/RenderPassReflection.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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,261 +31,330 @@ namespace Falcor { - RenderPassReflection::Field::Field(const std::string& name, const std::string& desc, Visibility v) : mName(name), mDesc(desc), mVisibility(v) - { - } +RenderPassReflection::Field::Field(const std::string& name, const std::string& desc, Visibility v) + : mName(name), mDesc(desc), mVisibility(v) +{} - RenderPassReflection::Field& RenderPassReflection::Field::rawBuffer(uint32_t size) - { - mType = Type::RawBuffer; - mWidth = size; - mHeight = mDepth = mArraySize = mMipCount = 0; - return *this; - } +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; - mSampleCount = 1; - mMipCount = mipCount; - mArraySize = arraySize; - 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; + mSampleCount = 1; + mMipCount = mipCount; + mArraySize = arraySize; + 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; - mDepth = 1; - mSampleCount = sampleCount; - mMipCount = mipCount; - mArraySize = arraySize; - 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; + mDepth = 1; + mSampleCount = sampleCount; + mMipCount = mipCount; + mArraySize = arraySize; + 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; - mSampleCount = 1; - mMipCount = 1; - mArraySize = arraySize; - 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; + mSampleCount = 1; + mMipCount = 1; + 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; - mSampleCount = 1; - mMipCount = mipCount; - 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; + mSampleCount = 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) +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) { - switch (type) - { - case RenderPassReflection::Field::Type::RawBuffer: - if (height > 0 || depth > 0 || sampleCount > 0) logWarning("RenderPassReflection::Field::resourceType - height, depth, sampleCount for {} must be 0.", to_string(type)); - return rawBuffer(width); - case RenderPassReflection::Field::Type::Texture1D: - if (height > 1 || depth > 1 || sampleCount > 1) logWarning("RenderPassReflection::Field::resourceType - height, depth, sampleCount for {} must be either 0 or 1.", to_string(type)); - return texture1D(width, mipCount, arraySize); - case RenderPassReflection::Field::Type::Texture2D: - if (depth > 1) logWarning("RenderPassReflection::Field::resourceType - depth for {} must be either 0 or 1.", to_string(type)); - return texture2D(width, height, sampleCount, mipCount, arraySize); - case RenderPassReflection::Field::Type::Texture3D: - if (sampleCount > 1 || mipCount > 1) logWarning("RenderPassReflection::Field::resourceType - sampleCount, mipCount for {} must be either 0 or 1.", to_string(type)); - return texture3D(width, height, depth, arraySize); - case RenderPassReflection::Field::Type::TextureCube: - if (depth > 1 || sampleCount > 1) logWarning("RenderPassReflection::Field::resourceType - depth, sampleCount for {} must be either 0 or 1.", to_string(type)); - return textureCube(width, height, mipCount, arraySize); - default: - throw RuntimeError("RenderPassReflection::Field::resourceType - {} is not a valid Field type", to_string(type)); - } + case RenderPassReflection::Field::Type::RawBuffer: + if (height > 0 || depth > 0 || sampleCount > 0) + logWarning("RenderPassReflection::Field::resourceType - height, depth, sampleCount for {} must be 0.", to_string(type)); + return rawBuffer(width); + case RenderPassReflection::Field::Type::Texture1D: + if (height > 1 || depth > 1 || sampleCount > 1) + logWarning( + "RenderPassReflection::Field::resourceType - height, depth, sampleCount for {} must be either 0 or 1.", to_string(type) + ); + return texture1D(width, mipCount, arraySize); + case RenderPassReflection::Field::Type::Texture2D: + if (depth > 1) + logWarning("RenderPassReflection::Field::resourceType - depth for {} must be either 0 or 1.", to_string(type)); + return texture2D(width, height, sampleCount, mipCount, arraySize); + case RenderPassReflection::Field::Type::Texture3D: + if (sampleCount > 1 || mipCount > 1) + logWarning("RenderPassReflection::Field::resourceType - sampleCount, mipCount for {} must be either 0 or 1.", to_string(type)); + return texture3D(width, height, depth, arraySize); + case RenderPassReflection::Field::Type::TextureCube: + if (depth > 1 || sampleCount > 1) + logWarning("RenderPassReflection::Field::resourceType - depth, sampleCount for {} must be either 0 or 1.", to_string(type)); + return textureCube(width, height, mipCount, arraySize); + default: + throw RuntimeError("RenderPassReflection::Field::resourceType - {} is not a valid Field type", to_string(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; +} - 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 +bool RenderPassReflection::Field::isValid() const +{ + if (mSampleCount > 1 && mMipCount > 1) { - if (mSampleCount > 1 && mMipCount > 1) - { - reportError("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)) - { - reportError("Internal resource can't be optional, since there will never be a graph edge that forces their creation"); - return false; - } + reportError( + "Trying to create a multisampled RenderPassReflection::Field '" + mName + "' with mip-count larger than 1. This is illegal." + ); + return false; + } - return true; + if (is_set(mVisibility, Visibility::Internal) && is_set(mFlags, Flags::Optional)) + { + reportError("Internal resource can't be optional, since there will never be a graph edge that forces their creation"); + return false; } - RenderPassReflection::Field& RenderPassReflection::addField(const Field& field) + return true; +} + +RenderPassReflection::Field& RenderPassReflection::addField(const Field& field) +{ + // See if the field already exists + for (auto& existingF : mFields) { - // See if the field already exists - for (auto& existingF : mFields) + if (existingF.getName() == field.getName()) { - 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()) { - // 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()) - { - reportError("Trying to add an existing field '" + field.getName() + "' to RenderPassReflection, but the visibility flags mismatch. Overriding the previous definition"); - } - return existingF; + reportError( + "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)); - } + mFields.push_back(field); + 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::addField(const std::string& name, const std::string& desc, Field::Visibility visibility) +{ + return addField(Field(name, desc, visibility)); +} - RenderPassReflection::Field& RenderPassReflection::addOutput(const std::string& name, const std::string& desc) - { - return addField(name, desc, Field::Visibility::Output); - } +RenderPassReflection::Field& RenderPassReflection::addInput(const std::string& name, const std::string& desc) +{ + return addField(name, desc, Field::Visibility::Input); +} - 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::addOutput(const std::string& name, const std::string& desc) +{ + return addField(name, desc, Field::Visibility::Output); +} - RenderPassReflection::Field& RenderPassReflection::addInternal(const std::string& name, const std::string& desc) - { - return addField(name, desc, Field::Visibility::Internal); - } +RenderPassReflection::Field& RenderPassReflection::addInputOutput(const std::string& name, const std::string& desc) +{ + return addField(name, desc, Field::Visibility::Input | Field::Visibility::Output); +} - const RenderPassReflection::Field* RenderPassReflection::getField(const std::string& name) const +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) { - for (const auto& field : mFields) - { - if (field.getName() == name) return &field; - } - return nullptr; + if (field.getName() == name) + return &field; } + return nullptr; +} - RenderPassReflection::Field* RenderPassReflection::getField(const std::string& name) +RenderPassReflection::Field* RenderPassReflection::getField(const std::string& name) +{ + for (auto& field : mFields) { - for (auto& field : mFields) - { - if (field.getName() == name) return &field; - } - return nullptr; + if (field.getName() == name) + return &field; } + return nullptr; +} - RenderPassReflection::Field& RenderPassReflection::Field::merge(const RenderPassReflection::Field& other) +RenderPassReflection::Field& RenderPassReflection::Field::merge(const RenderPassReflection::Field& other) +{ + auto err = [&](const std::string& msg) { - auto err = [&](const std::string& msg) - { - const std::string s = "Can't merge RenderPassReflection::Fields. base(" + getName() + "), newField(" + other.getName() + "). "; - throw RuntimeError(s + msg); - }; + const std::string s = "Can't merge RenderPassReflection::Fields. base(" + getName() + "), newField(" + other.getName() + "). "; + throw RuntimeError(s + msg); + }; - if (mType != other.mType) err("mismatching types"); + 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) + // 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) { - 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"); - } - }; + 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); + merge_field(Width); + merge_field(Height); + merge_field(Depth); + merge_field(ArraySize); + merge_field(MipCount); + merge_field(SampleCount); + merge_field(Format); #undef merge_field - FALCOR_ASSERT(is_set(other.mVisibility, RenderPassReflection::Field::Visibility::Internal) == false); // We can't alias/merge internal fields - FALCOR_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; - } + // We can't alias/merge internal fields. + FALCOR_ASSERT(is_set(other.mVisibility, RenderPassReflection::Field::Visibility::Internal) == false); + // We can't alias/merge internal fields. + FALCOR_ASSERT(is_set(mVisibility, RenderPassReflection::Field::Visibility::Internal) == false); - bool RenderPassReflection::Field::operator==(const Field& other) const - { -#define check(_f) if(_f != other._f) return false + mVisibility = mVisibility | other.mVisibility; + mBindFlags = mBindFlags | other.mBindFlags; + return *this; +} - 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); +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; - } + return true; +} - bool RenderPassReflection::operator==(const RenderPassReflection& other) const +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 { - 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 (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; + for (const auto& f : mFields) + { + auto otherF = findField(f.mName, other.mFields); + if (!otherF) + return false; + if (otherF.value() != f) + return false; } + + return true; } +} // namespace Falcor diff --git a/Source/Falcor/RenderGraph/RenderPassReflection.h b/Source/Falcor/RenderGraph/RenderPassReflection.h index 1e1c1b461..d21025f48 100644 --- a/Source/Falcor/RenderGraph/RenderPassReflection.h +++ b/Source/Falcor/RenderGraph/RenderPassReflection.h @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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,159 +36,185 @@ namespace Falcor { - class FALCOR_API RenderPassReflection +class FALCOR_API RenderPassReflection +{ +public: + class FALCOR_API Field { public: - class FALCOR_API Field + /** + * The type of visibility the field has + */ + enum class Visibility { - public: - /** The type of visibility the field has - */ - enum class Visibility - { - Undefined = 0x0, - Input = 0x1, ///< Input field - Output = 0x2, ///< Output field - Internal = 0x4, ///< Internal field. You can use this value to ask the resource-cache for any required internal resource - }; - - /** Miscellaneous flags to control allocation and lifetime policy - */ - enum class Flags - { - None = 0x0, ///< None - Optional = 0x1, ///< Mark that field as optional. For output resources, it means that they don't have to be bound unless their result is required by the caller. For input resources, it means that the pass can function correctly without them being bound (but the behavior might be different) - Persistent = 0x2, ///< The resource bound to this field must not change between execute() calls (not the pointer nor the data). It can change only during the RenderGraph recompilation. - }; - - /** Field type - */ - enum class Type - { - Texture1D, - Texture2D, - Texture3D, - TextureCube, - RawBuffer, - }; - - Field(const std::string& name, const std::string& desc, Visibility v); - Field() = default; - - bool isValid() const; - - /** 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& format(ResourceFormat f); - 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; } - uint32_t getWidth() const { return mWidth; } - uint32_t getHeight() const { return mHeight; } - uint32_t getDepth() const { return mDepth; } - uint32_t getSampleCount() const { return mSampleCount; } - uint32_t getArraySize() const { return mArraySize; } - uint32_t getMipCount() const { return mMipCount; } - ResourceFormat getFormat() const { return mFormat; } - 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; - - Type mType = Type::Texture2D; - std::string mName; ///< The field's name. - std::string mDesc; ///< A description of the field. - uint32_t mWidth = 0; ///< For texture, the width in texels. For buffers, the size in bytes. 0 means don't care - the pass will use whatever is bound (the RenderGraph will use the window size by default). - uint32_t mHeight = 0; ///< For texture, the height in texels. 0 means don't care - the pass will use whatever is bound (the RenderGraph will use the window size by default). - uint32_t mDepth = 0; ///< For texture, the depth in texels. 0 means don't care - the pass will use whatever is bound (the RenderGraph will use the window size by default). - uint32_t mSampleCount = 1; ///< The required sample 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. - 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; + Undefined = 0x0, + Input = 0x1, ///< Input field + Output = 0x2, ///< Output field + Internal = 0x4, ///< Internal field. You can use this value to ask the resource-cache for any required internal resource }; - Field& addInput(const std::string& name, const std::string& desc); - Field& addOutput(const std::string& name, const std::string& desc); - Field& addInputOutput(const std::string& name, const std::string& desc); - Field& addInternal(const std::string& name, const std::string& desc); + /** + * Miscellaneous flags to control allocation and lifetime policy + */ + enum class Flags + { + None = 0x0, ///< None + Optional = 0x1, ///< Mark that field as optional. For output resources, it means that they don't have to be bound unless their + ///< result is required by the caller. For input resources, it means that the pass can function correctly + ///< without them being bound (but the behavior might be different) + Persistent = 0x2, ///< The resource bound to this field must not change between execute() calls (not the pointer nor the data). + ///< It can change only during the RenderGraph recompilation. + }; - size_t getFieldCount() const { return mFields.size(); } - const Field* getField(size_t f) const { return f <= mFields.size() ? &mFields[f] : nullptr; } - const Field* getField(const std::string& name) const; - Field* getField(const std::string& name); - Field& addField(const Field& field); + /** + * Field type + */ + enum class Type + { + Texture1D, + Texture2D, + Texture3D, + TextureCube, + RawBuffer, + }; - bool operator==(const RenderPassReflection& other) const; - bool operator!=(const RenderPassReflection& other) const { return !(*this == other); } + Field(const std::string& name, const std::string& desc, Visibility v); + Field() = default; + + bool isValid() const; + + /** + * 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 constexpr 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& format(ResourceFormat f); + 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; } + uint32_t getWidth() const { return mWidth; } + uint32_t getHeight() const { return mHeight; } + uint32_t getDepth() const { return mDepth; } + uint32_t getSampleCount() const { return mSampleCount; } + uint32_t getArraySize() const { return mArraySize; } + uint32_t getMipCount() const { return mMipCount; } + ResourceFormat getFormat() const { return mFormat; } + 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: - Field& addField(const std::string& name, const std::string& desc, Field::Visibility visibility); - std::vector mFields; + friend class RenderPassReflection; + + Type mType = Type::Texture2D; + std::string mName; ///< The field's name. + std::string mDesc; ///< A description of the field. + uint32_t mWidth = 0; ///< For texture, the width in texels. For buffers, the size in bytes. 0 means don't care - the pass will use + ///< whatever is bound (the RenderGraph will use the window size by default). + uint32_t mHeight = 0; ///< For texture, the height in texels. 0 means don't care - the pass will use whatever is bound (the + ///< RenderGraph will use the window size by default). + uint32_t mDepth = 0; ///< For texture, the depth in texels. 0 means don't care - the pass will use whatever is bound (the + ///< RenderGraph will use the window size by default). + uint32_t mSampleCount = 1; ///< The required sample 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. + 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; }; - FALCOR_ENUM_CLASS_OPERATORS(RenderPassReflection::Field::Visibility); - FALCOR_ENUM_CLASS_OPERATORS(RenderPassReflection::Field::Flags); + Field& addInput(const std::string& name, const std::string& desc); + Field& addOutput(const std::string& name, const std::string& desc); + Field& addInputOutput(const std::string& name, const std::string& desc); + 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 f <= mFields.size() ? &mFields[f] : nullptr; } + const Field* getField(const std::string& name) const; + Field* getField(const std::string& name); + 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; +}; - inline std::string to_string(RenderPassReflection::Field::Type t) +FALCOR_ENUM_CLASS_OPERATORS(RenderPassReflection::Field::Visibility); +FALCOR_ENUM_CLASS_OPERATORS(RenderPassReflection::Field::Flags); + +inline std::string to_string(RenderPassReflection::Field::Type t) +{ +#define t2s(ft) \ + case RenderPassReflection::Field::Type::ft: \ + return #ft; + switch (t) { -#define t2s(ft) case RenderPassReflection::Field::Type::ft: return #ft; - switch (t) - { - t2s(RawBuffer); - t2s(Texture1D); - t2s(Texture2D); - t2s(Texture3D); - t2s(TextureCube); - default: - FALCOR_UNREACHABLE(); - return ""; - } -#undef t2s + t2s(RawBuffer); + t2s(Texture1D); + t2s(Texture2D); + t2s(Texture3D); + t2s(TextureCube); + default: + FALCOR_UNREACHABLE(); + return ""; } +#undef t2s +} - inline RenderPassReflection::Field::Type resourceTypeToFieldType(Resource::Type t) +inline RenderPassReflection::Field::Type resourceTypeToFieldType(Resource::Type t) +{ + switch (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 RuntimeError("resourceTypeToFieldType - No RenderPassReflection::Field::Type exists for Resource::Type::{}", to_string(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 RuntimeError("resourceTypeToFieldType - No RenderPassReflection::Field::Type exists for Resource::Type::{}", to_string(t)); } } +} // namespace Falcor diff --git a/Source/Falcor/RenderGraph/RenderPassStandardFlags.h b/Source/Falcor/RenderGraph/RenderPassStandardFlags.h index a6c85019a..c1857aee5 100644 --- a/Source/Falcor/RenderGraph/RenderPassStandardFlags.h +++ b/Source/Falcor/RenderGraph/RenderPassStandardFlags.h @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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,28 +31,32 @@ namespace Falcor { - /** 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. - }; +/** + * 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. +}; - /** The refresh flags above are passed to RenderPass::execute() via a - field with this name in the dictionary. - */ - static const char kRenderPassRefreshFlags[] = "_refreshFlags"; +/** + * The refresh flags above are passed to RenderPass::execute() via a + * field with this name in the dictionary. + */ +static const char kRenderPassRefreshFlags[] = "_refreshFlags"; - /** First available preudorandom number generator dimension. - */ - static const char kRenderPassPRNGDimension[] = "_prngDimension"; +/** + * First available preudorandom number generator dimension. + */ +static const char kRenderPassPRNGDimension[] = "_prngDimension"; - /** Adjust shading normals on primary hits. - */ - static const char kRenderPassGBufferAdjustShadingNormals[] = "_gbufferAdjustShadingNormals"; +/** + * Adjust shading normals on primary hits. + */ +static const char kRenderPassGBufferAdjustShadingNormals[] = "_gbufferAdjustShadingNormals"; - FALCOR_ENUM_CLASS_OPERATORS(RenderPassRefreshFlags); -} +FALCOR_ENUM_CLASS_OPERATORS(RenderPassRefreshFlags); +} // namespace Falcor diff --git a/Source/Falcor/RenderGraph/ResourceCache.cpp b/Source/Falcor/RenderGraph/ResourceCache.cpp index 55caa85c2..06cb5ec08 100644 --- a/Source/Falcor/RenderGraph/ResourceCache.cpp +++ b/Source/Falcor/RenderGraph/ResourceCache.cpp @@ -33,162 +33,172 @@ namespace Falcor { - ResourceCache::SharedPtr ResourceCache::create() - { - return SharedPtr(new ResourceCache()); - } +void ResourceCache::reset() +{ + mNameToIndex.clear(); + mResourceData.clear(); +} + +const ref& ResourceCache::getResource(const std::string& name) const +{ + static const ref pNull; + auto extIt = mExternalResources.find(name); - void ResourceCache::reset() + // Search external resources if not found in render graph resources + if (extIt == mExternalResources.end()) { - mNameToIndex.clear(); - mResourceData.clear(); + const auto& it = mNameToIndex.find(name); + if (it == mNameToIndex.end()) + return pNull; + return mResourceData[it->second].pResource; } - const Resource::SharedPtr& ResourceCache::getResource(const std::string& name) const - { - static const Resource::SharedPtr pNull; - auto extIt = mExternalResources.find(name); + return extIt->second; +} + +const RenderPassReflection::Field& ResourceCache::getResourceReflection(const std::string& name) const +{ + uint32_t i = mNameToIndex.at(name); + return mResourceData[i].field; +} - // Search external resources if not found in render graph resources - if (extIt == mExternalResources.end()) +void ResourceCache::registerExternalResource(const std::string& name, const ref& pResource) +{ + if (pResource) + mExternalResources[name] = pResource; + else + { + auto it = mExternalResources.find(name); + if (it == mExternalResources.end()) { - const auto& it = mNameToIndex.find(name); - if (it == mNameToIndex.end()) return pNull; - return mResourceData[it->second].pResource; + logWarning("ResourceCache::registerExternalResource: '{}' does not exist.", name); + return; } - return extIt->second; + mExternalResources.erase(it); } +} + +void mergeTimePoint(std::pair& range, uint32_t newTime) +{ + range.first = std::min(range.first, newTime); + range.second = std::max(range.second, newTime); +} + +void ResourceCache::registerField( + const std::string& name, + const RenderPassReflection::Field& field, + uint32_t timePoint, + const std::string& alias +) +{ + FALCOR_ASSERT(mNameToIndex.find(name) == mNameToIndex.end()); - const RenderPassReflection::Field& ResourceCache::getResourceReflection(const std::string& name) const + bool addAlias = (alias.empty() == false); + if (addAlias && mNameToIndex.count(alias) == 0) { - uint32_t i = mNameToIndex.at(name); - return mResourceData[i].field; + throw RuntimeError("Field named '{}' not found. Cannot register '{}' as an alias.", alias, name); } - void ResourceCache::registerExternalResource(const std::string& name, const Resource::SharedPtr& pResource) + // Add a new field + if (addAlias == false) { - if(pResource) mExternalResources[name] = pResource; - else - { - auto it = mExternalResources.find(name); - if (it == mExternalResources.end()) - { - logWarning("ResourceCache::registerExternalResource: '{}' does not exist.", name); - return; - } - - mExternalResources.erase(it); - } + FALCOR_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}); } - - void mergeTimePoint(std::pair& range, uint32_t newTime) + else // Add alias { - range.first = std::min(range.first, newTime); - range.second = std::max(range.second, newTime); + 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); } +} - void ResourceCache::registerField(const std::string& name, const RenderPassReflection::Field& field, uint32_t timePoint, const std::string& alias) - { - FALCOR_ASSERT(mNameToIndex.find(name) == mNameToIndex.end()); +inline ref createResourceForPass( + ref pDevice, + 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(); - bool addAlias = (alias.empty() == false); - if (addAlias && mNameToIndex.count(alias) == 0) - { - throw RuntimeError("Field named '{}' not found. Cannot register '{}' as an alias.", alias, name); - } + ResourceFormat format = ResourceFormat::Unknown; - // Add a new field - if (addAlias == false) - { - FALCOR_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 + if (field.getType() != RenderPassReflection::Field::Type::RawBuffer) + { + format = field.getFormat() == ResourceFormat::Unknown ? params.format : field.getFormat(); + if (resolveBindFlags) { - 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); + 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 = pDevice->getFormatBindFlags(format); + mask &= supported; + bindFlags |= mask; } } - - inline Resource::SharedPtr createResourceForPass(Device* pDevice, const ResourceCache::DefaultProperties& params, const RenderPassReflection::Field& field, bool resolveBindFlags, const std::string& resourceName) + else // RawBuffer { - 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 (resolveBindFlags) + bindFlags = Resource::BindFlags::UnorderedAccess | Resource::BindFlags::ShaderResource; + } + ref pResource; - 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 = pDevice->getFormatBindFlags(format); - mask &= supported; - bindFlags |= mask; - } - } - else // RawBuffer + switch (field.getType()) + { + case RenderPassReflection::Field::Type::RawBuffer: + pResource = Buffer::create(pDevice, width, bindFlags, Buffer::CpuAccess::None); + break; + case RenderPassReflection::Field::Type::Texture1D: + pResource = Texture::create1D(pDevice, width, format, arraySize, mipLevels, nullptr, bindFlags); + break; + case RenderPassReflection::Field::Type::Texture2D: + if (sampleCount > 1) { - if (resolveBindFlags) bindFlags = Resource::BindFlags::UnorderedAccess | Resource::BindFlags::ShaderResource; + pResource = Texture::create2DMS(pDevice, width, height, format, sampleCount, arraySize, bindFlags); } - Resource::SharedPtr pResource; - - switch (field.getType()) + else { - case RenderPassReflection::Field::Type::RawBuffer: - pResource = Buffer::create(pDevice, width, bindFlags, Buffer::CpuAccess::None); - break; - case RenderPassReflection::Field::Type::Texture1D: - pResource = Texture::create1D(pDevice, width, format, arraySize, mipLevels, nullptr, bindFlags); - break; - case RenderPassReflection::Field::Type::Texture2D: - if (sampleCount > 1) - { - pResource = Texture::create2DMS(pDevice, width, height, format, sampleCount, arraySize, bindFlags); - } - else - { - pResource = Texture::create2D(pDevice, width, height, format, arraySize, mipLevels, nullptr, bindFlags); - } - break; - case RenderPassReflection::Field::Type::Texture3D: - pResource = Texture::create3D(pDevice, width, height, depth, format, mipLevels, nullptr, bindFlags); - break; - case RenderPassReflection::Field::Type::TextureCube: - pResource = Texture::createCube(pDevice, width, height, format, arraySize, mipLevels, nullptr, bindFlags); - break; - default: - FALCOR_UNREACHABLE(); - return nullptr; + pResource = Texture::create2D(pDevice, width, height, format, arraySize, mipLevels, nullptr, bindFlags); } - pResource->setName(resourceName); - return pResource; + break; + case RenderPassReflection::Field::Type::Texture3D: + pResource = Texture::create3D(pDevice, width, height, depth, format, mipLevels, nullptr, bindFlags); + break; + case RenderPassReflection::Field::Type::TextureCube: + pResource = Texture::createCube(pDevice, width, height, format, arraySize, mipLevels, nullptr, bindFlags); + break; + default: + FALCOR_UNREACHABLE(); + return nullptr; } + pResource->setName(resourceName); + return pResource; +} - void ResourceCache::allocateResources(Device* pDevice, const DefaultProperties& params) +void ResourceCache::allocateResources(ref pDevice, const DefaultProperties& params) +{ + for (auto& data : mResourceData) { - for (auto& data : mResourceData) + if ((data.pResource == nullptr) && (data.field.isValid())) { - if ((data.pResource == nullptr) && (data.field.isValid())) - { - data.pResource = createResourceForPass(pDevice, params, data.field, data.resolveBindFlags, data.name); - } + data.pResource = createResourceForPass(pDevice, params, data.field, data.resolveBindFlags, data.name); } } } +} // namespace Falcor diff --git a/Source/Falcor/RenderGraph/ResourceCache.h b/Source/Falcor/RenderGraph/ResourceCache.h index 68bf78bfa..f2800f485 100644 --- a/Source/Falcor/RenderGraph/ResourceCache.h +++ b/Source/Falcor/RenderGraph/ResourceCache.h @@ -30,7 +30,6 @@ #include "Core/Macros.h" #include "Core/API/fwd.h" #include "Core/API/Resource.h" -#include #include #include #include @@ -38,74 +37,79 @@ namespace Falcor { - class FALCOR_API ResourceCache - { - 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 - { - uint2 dims; ///< Width, height of the swap chain - ResourceFormat format = ResourceFormat::Unknown; ///< Format to use for texture creation - }; +class FALCOR_API ResourceCache +{ +public: + using ResourcesMap = std::unordered_map>; - /** 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); + /** + * Properties to use during resource creation when its property has not been fully specified. + */ + struct DefaultProperties + { + uint2 dims; ///< Width, height of the swap chain + ResourceFormat format = ResourceFormat::Unknown; ///< Format to use for texture creation + }; - /** 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 - \param[in] timePoint The point in time for when this field is used. Normally this is an index into the execution order. - \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, const RenderPassReflection::Field& field, uint32_t timePoint, const std::string& alias = ""); + /** + * 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 ref& pResource); - /** Get a resource by name. Includes external resources known by the cache. - */ - const Resource::SharedPtr& getResource(const std::string& name) const; + /** + * 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 + * @param[in] timePoint The point in time for when this field is used. Normally this is an index into the execution order. + * @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, + const RenderPassReflection::Field& field, + uint32_t timePoint, + const std::string& alias = "" + ); - /** Get the field-reflection of a resource - */ - const RenderPassReflection::Field& getResourceReflection(const std::string& name) const; + /** + * Get a resource by name. Includes external resources known by the cache. + */ + const ref& getResource(const std::string& name) const; - /** Allocate all resources that need to be created/updated. - This includes new resources, resources whose properties have been updated since last allocation call. - */ - void allocateResources(Device* pDevice, const DefaultProperties& params); + /** + * Get the field-reflection of a resource + */ + const RenderPassReflection::Field& getResourceReflection(const std::string& name) const; - /** Clears all registered field/resource properties and allocated resources. - */ - void reset(); + /** + * Allocate all resources that need to be created/updated. + * This includes new resources, resources whose properties have been updated since last allocation call. + */ + void allocateResources(ref pDevice, const DefaultProperties& params); - private: - ResourceCache() = default; + /** + * Clears all registered field/resource properties and allocated resources. + */ + void reset(); - struct ResourceData - { - 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 - }; +private: + struct ResourceData + { + RenderPassReflection::Field field; // Holds merged properties for aliased resources + std::pair lifetime; // Time range where this resource is being used + ref 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 - std::unordered_map mNameToIndex; - std::vector mResourceData; + // Resources and properties for fields within (and therefore owned by) a render graph + std::unordered_map mNameToIndex; + std::vector mResourceData; - // References to output resources not to be allocated by the render graph - ResourcesMap mExternalResources; - }; + // References to output resources not to be allocated by the render graph + ResourcesMap mExternalResources; +}; -} +} // namespace Falcor diff --git a/Source/Falcor/RenderPasses/ResolvePass.cpp b/Source/Falcor/RenderPasses/ResolvePass.cpp index d76d186aa..b59264d7f 100644 --- a/Source/Falcor/RenderPasses/ResolvePass.cpp +++ b/Source/Falcor/RenderPasses/ResolvePass.cpp @@ -31,44 +31,37 @@ namespace Falcor { - static const std::string kDst = "dst"; - static const std::string kSrc = "src"; +static const std::string kDst = "dst"; +static const std::string kSrc = "src"; - RenderPassReflection ResolvePass::reflect(const CompileData& compileData) - { - RenderPassReflection reflector; - reflector.addInput(kSrc, "Multi-sampled texture").format(mFormat).texture2D(0, 0, 0); - reflector.addOutput(kDst, "Destination texture. Must have a single sample").format(mFormat).texture2D(0, 0, 1); - return reflector; - } +ResolvePass::ResolvePass(ref pDevice) : RenderPass(pDevice) {} - ResolvePass::SharedPtr ResolvePass::create(std::shared_ptr pDevice, const Dictionary& dictionary) - { - return SharedPtr(new ResolvePass(pDevice)); - } +RenderPassReflection ResolvePass::reflect(const CompileData& compileData) +{ + RenderPassReflection reflector; + reflector.addInput(kSrc, "Multi-sampled texture").format(mFormat).texture2D(0, 0, 0); + reflector.addOutput(kDst, "Destination texture. Must have a single sample").format(mFormat).texture2D(0, 0, 1); + return reflector; +} - ResolvePass::ResolvePass(std::shared_ptr pDevice) - : RenderPass(std::move(pDevice)) - {} +void ResolvePass::execute(RenderContext* pRenderContext, const RenderData& renderData) +{ + auto pSrcTex = renderData.getTexture(kSrc); + auto pDstTex = renderData.getTexture(kDst); - void ResolvePass::execute(RenderContext* pRenderContext, const RenderData& renderData) + if (pSrcTex && pDstTex) { - auto pSrcTex = renderData.getTexture(kSrc); - auto pDstTex = renderData.getTexture(kDst); - - if (pSrcTex && pDstTex) - { - if (pSrcTex->getSampleCount() == 1) - { - logWarning("ResolvePass::execute() - Cannot resolve from a non-multisampled texture."); - return; - } - - pRenderContext->resolveResource(pSrcTex, pDstTex); - } - else + if (pSrcTex->getSampleCount() == 1) { - logWarning("ResolvePass::execute() - missing an input or output resource."); + logWarning("ResolvePass::execute() - Cannot resolve from a non-multisampled texture."); + return; } + + pRenderContext->resolveResource(pSrcTex, pDstTex); + } + else + { + logWarning("ResolvePass::execute() - missing an input or output resource."); } } +} // namespace Falcor diff --git a/Source/Falcor/RenderPasses/ResolvePass.h b/Source/Falcor/RenderPasses/ResolvePass.h index 42ce03325..b66352ed7 100644 --- a/Source/Falcor/RenderPasses/ResolvePass.h +++ b/Source/Falcor/RenderPasses/ResolvePass.h @@ -33,26 +33,26 @@ namespace Falcor { - class RenderContext; - class Dictionary; - class RenderData; +class RenderContext; +class Dictionary; +class RenderData; - class FALCOR_API ResolvePass : public RenderPass - { - public: - // This pass is not dynamically loaded from a plugin library, - // but we still need to provide plugin type and info fields. - FALCOR_PLUGIN_CLASS(ResolvePass, "ResolvePass", "Resolve a multi-sampled texture."); +class FALCOR_API ResolvePass : public RenderPass +{ +public: + // This pass is not dynamically loaded from a plugin library, + // but we still need to provide plugin type and info fields. + FALCOR_PLUGIN_CLASS(ResolvePass, "ResolvePass", "Resolve a multi-sampled texture."); + + static ref create(ref pDevice, const Dictionary& dictionary = {}) { return make_ref(pDevice); } - using SharedPtr = std::shared_ptr; + ResolvePass(ref pDevice); - static SharedPtr create(std::shared_ptr pDevice, const Dictionary& dictionary = {}); + void setFormat(ResourceFormat format) { mFormat = format; } + virtual RenderPassReflection reflect(const CompileData& compileData) override; + virtual void execute(RenderContext* pRenderContext, const RenderData& renderData) override; - void setFormat(ResourceFormat format) { mFormat = format; } - virtual RenderPassReflection reflect(const CompileData& compileData) override; - virtual void execute(RenderContext* pRenderContext, const RenderData& renderData) override; - private: - ResolvePass(std::shared_ptr pDevice); - ResourceFormat mFormat = ResourceFormat::Unknown; - }; -} +private: + ResourceFormat mFormat = ResourceFormat::Unknown; +}; +} // namespace Falcor diff --git a/Source/Falcor/RenderPasses/Shared/Denoising/NRDBuffers.slang b/Source/Falcor/RenderPasses/Shared/Denoising/NRDBuffers.slang index cf30ba4e1..9579a514d 100644 --- a/Source/Falcor/RenderPasses/Shared/Denoising/NRDBuffers.slang +++ b/Source/Falcor/RenderPasses/Shared/Denoising/NRDBuffers.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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,25 +36,45 @@ static const bool kOutputNRDAdditionalData = OUTPUT_NRD_ADDITIONAL_DATA; struct NRDBuffers { - RWStructuredBuffer sampleRadiance; ///< Output per-sample NRD radiance data. Only valid if kOutputNRDData == true. - RWStructuredBuffer samplePrimaryHitNEEOnDelta; ///< Output per-sample NEE on paths classified as delta, from delta+diffuse materials. Only valid if kOutputNRDData == true. - RWStructuredBuffer sampleHitDist; ///< Output per-sample NRD hit distance data. Only valid if kOutputNRDData == true. - RWStructuredBuffer sampleEmission; ///< Output per-sample NRD emission data. Only valid if kOutputNRDData == true. - RWStructuredBuffer sampleReflectance; ///< Output per-sample NRD reflectance data. Only valid if kOutputNRDData == true. - - RWTexture2D primaryHitEmission; ///< Output per-pixel primary hit emission. Only valid if kOutputNRDData == true. - RWTexture2D primaryHitDiffuseReflectance; ///< Output per-pixel primary hit diffuse reflectance. Only valid if kOutputNRDData == true. - RWTexture2D primaryHitSpecularReflectance; ///< Output per-pixel primary hit specular reflectance. Only valid if kOutputNRDData == true. - - RWTexture2D deltaReflectionReflectance; ///< Output per-pixel delta reflection reflectance. Only valid if kOutputNRDAdditionalData == true. - RWTexture2D deltaReflectionEmission; ///< Output per-pixel delta reflection emission. Only valid if kOutputNRDAdditionalData == true. - RWTexture2D deltaReflectionNormWRoughMaterialID; ///< Output per-pixel delta reflection world normal, roughness, and material ID. Only valid if kOutputNRDAdditionalData == true. - RWTexture2D deltaReflectionPathLength; ///< Output per-pixel delta reflection path length. Only valid if kOutputNRDAdditionalData == true. - RWTexture2D deltaReflectionHitDist; ///< Output per-pixel delta reflection hit distance. Only valid if kOutputNRDAdditionalData == true. - - RWTexture2D deltaTransmissionReflectance; ///< Output per-pixel delta transmission reflectance. Only valid if kOutputNRDAdditionalData == true. - RWTexture2D deltaTransmissionEmission; ///< Output per-pixel delta transmission emission. Only valid if kOutputNRDAdditionalData == true. - RWTexture2D deltaTransmissionNormWRoughMaterialID; ///< Output per-pixel delta transmission world normal, roughness, and material ID. Only valid if kOutputNRDAdditionalData == true. - RWTexture2D deltaTransmissionPathLength; ///< Output per-pixel delta transmission path length. Only valid if kOutputNRDAdditionalData == true. - RWTexture2D deltaTransmissionPosW; ///< Output per-pixel delta transmission world position. Only valid if kOutputNRDAdditionalData == true. + /// Output per-sample NRD radiance data. Only valid if kOutputNRDData == true. + RWStructuredBuffer sampleRadiance; + /// Output per-sample NEE on paths classified as delta, from delta+diffuse materials. Only valid if kOutputNRDData == true. + RWStructuredBuffer samplePrimaryHitNEEOnDelta; + + /// Output per-sample NRD hit distance data. Only valid if kOutputNRDData == true. + RWStructuredBuffer sampleHitDist; + /// Output per-sample NRD emission data. Only valid if kOutputNRDData == true. + RWStructuredBuffer sampleEmission; + /// Output per-sample NRD reflectance data. Only valid if kOutputNRDData == true. + RWStructuredBuffer sampleReflectance; + + /// Output per-pixel primary hit emission. Only valid if kOutputNRDData == true. + RWTexture2D primaryHitEmission; + /// Output per-pixel primary hit diffuse reflectance. Only valid if kOutputNRDData == true. + RWTexture2D primaryHitDiffuseReflectance; + /// Output per-pixel primary hit specular reflectance. Only valid if kOutputNRDData == true. + RWTexture2D primaryHitSpecularReflectance; + + /// Output per-pixel delta reflection reflectance. Only valid if kOutputNRDAdditionalData == true. + RWTexture2D deltaReflectionReflectance; + /// Output per-pixel delta reflection emission. Only valid if kOutputNRDAdditionalData == true. + RWTexture2D deltaReflectionEmission; + /// Output per-pixel delta reflection world normal, roughness, and material ID. Only valid if kOutputNRDAdditionalData == true. + RWTexture2D deltaReflectionNormWRoughMaterialID; + + /// Output per-pixel delta reflection path length. Only valid if kOutputNRDAdditionalData == true. + RWTexture2D deltaReflectionPathLength; + /// Output per-pixel delta reflection hit distance. Only valid if kOutputNRDAdditionalData == true. + RWTexture2D deltaReflectionHitDist; + + /// Output per-pixel delta transmission reflectance. Only valid if kOutputNRDAdditionalData == true. + RWTexture2D deltaTransmissionReflectance; + /// Output per-pixel delta transmission emission. Only valid if kOutputNRDAdditionalData == true. + RWTexture2D deltaTransmissionEmission; + /// Output per-pixel delta transmission world normal, roughness, and material ID. Only valid if kOutputNRDAdditionalData == true. + RWTexture2D deltaTransmissionNormWRoughMaterialID; + /// Output per-pixel delta transmission path length. Only valid if kOutputNRDAdditionalData == true. + RWTexture2D deltaTransmissionPathLength; + /// Output per-pixel delta transmission world position. Only valid if kOutputNRDAdditionalData == true. + RWTexture2D deltaTransmissionPosW; }; diff --git a/Source/Falcor/RenderPasses/Shared/Denoising/NRDData.slang b/Source/Falcor/RenderPasses/Shared/Denoising/NRDData.slang index bf6ad041c..36b7e8455 100644 --- a/Source/Falcor/RenderPasses/Shared/Denoising/NRDData.slang +++ b/Source/Falcor/RenderPasses/Shared/Denoising/NRDData.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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,35 +27,26 @@ **************************************************************************/ enum class NRDPathType { - Residual = 0, ///< Path not assigned to any of the below types. - Diffuse = 1, ///< Primary hit was sampled from the diffuse BSDF lobe. - Specular = 2, ///< Primary hit was sampled from the specular BSDF lobe. - DeltaReflection = 3, ///< Primary hit was sampled as the delta reflection. - DeltaTransmission = 4, ///< Path started with and followed delta transmission events (whenever possible - TIR could be an exception) until it hit the first non-delta event. + Residual = 0, ///< Path not assigned to any of the below types. + Diffuse = 1, ///< Primary hit was sampled from the diffuse BSDF lobe. + Specular = 2, ///< Primary hit was sampled from the specular BSDF lobe. + DeltaReflection = 3, ///< Primary hit was sampled as the delta reflection. + DeltaTransmission = 4, ///< Path started with and followed delta transmission events (whenever possible - TIR could be an exception) + ///< until it hit the first non-delta event. }; struct NRDRadiance { - uint pathType = uint(NRDPathType::Residual); + uint pathType = uint(NRDPathType::Residual); float3 radiance = 0.f; - NRDPathType getPathType() - { - return NRDPathType(pathType); - } + NRDPathType getPathType() { return NRDPathType(pathType); } - [mutating] void setPathType(NRDPathType pType) - { - pathType = uint(pType); - } + [mutating] + void setPathType(NRDPathType pType) { pathType = uint(pType); } - float3 getRadiance() - { - return radiance; - } + float3 getRadiance() { return radiance; } - [mutating] void setRadiance(float3 color) - { - radiance = color; - } + [mutating] + void setRadiance(float3 color) { radiance = color; } }; diff --git a/Source/Falcor/RenderPasses/Shared/Denoising/NRDHelpers.slang b/Source/Falcor/RenderPasses/Shared/Denoising/NRDHelpers.slang index 1be37aa5f..639669c06 100644 --- a/Source/Falcor/RenderPasses/Shared/Denoising/NRDHelpers.slang +++ b/Source/Falcor/RenderPasses/Shared/Denoising/NRDHelpers.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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,8 +33,13 @@ import Scene.Scene; import Scene.ShadingData; /** Get material reflectance based on the metallic value. -*/ -float3 getMaterialReflectanceForDeltaPaths(const MaterialType materialType, const bool hasDeltaLobes, const ShadingData sd, const BSDFProperties bsdfProperties) + */ +float3 getMaterialReflectanceForDeltaPaths( + const MaterialType materialType, + const bool hasDeltaLobes, + const ShadingData sd, + const BSDFProperties bsdfProperties +) { if (materialType == MaterialType::Standard) { @@ -44,7 +49,8 @@ float3 getMaterialReflectanceForDeltaPaths(const MaterialType materialType, cons if (metallic < 1.f) { // Use sum of reflection/transmission albedo as they are denoised together. - const float3 diffuseReflectance = max(kNRDMinReflectance, bsdfProperties.diffuseReflectionAlbedo + bsdfProperties.diffuseTransmissionAlbedo); + const float3 diffuseReflectance = + max(kNRDMinReflectance, bsdfProperties.diffuseReflectionAlbedo + bsdfProperties.diffuseTransmissionAlbedo); return diffuseReflectance; } // Handle only non-delta specular lobes. @@ -61,7 +67,8 @@ float3 getMaterialReflectanceForDeltaPaths(const MaterialType materialType, cons else if (materialType == MaterialType::Hair) { // Use sum of reflection/transmission albedo as they are denoised together. - const float3 reflectance = max(kNRDMinReflectance, bsdfProperties.diffuseReflectionAlbedo + bsdfProperties.diffuseTransmissionAlbedo); + const float3 reflectance = + max(kNRDMinReflectance, bsdfProperties.diffuseReflectionAlbedo + bsdfProperties.diffuseTransmissionAlbedo); return reflectance; } @@ -75,7 +82,7 @@ bool isDeltaReflectionAllowedAlongDeltaTransmissionPath(const ShadingData sd) const float insideIoR = gScene.materials.evalIoR(sd.materialID); const float eta = sd.frontFacing ? (sd.IoR / insideIoR) : (insideIoR / sd.IoR); - bool totalInternalReflection = evalFresnelDielectric(eta, sd.toLocal(sd.V).z) == 1.f; + bool totalInternalReflection = evalFresnelDielectric(eta, sd.frame.toLocal(sd.V).z) == 1.f; bool nonTransmissiveMirror = (md.specularTransmission == 0.f) && (metallic == 1.f); diff --git a/Source/Falcor/Rendering/Lights/EmissiveLightSampler.h b/Source/Falcor/Rendering/Lights/EmissiveLightSampler.h index b73b015b7..a623a7c1b 100644 --- a/Source/Falcor/Rendering/Lights/EmissiveLightSampler.h +++ b/Source/Falcor/Rendering/Lights/EmissiveLightSampler.h @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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,6 @@ #include "Core/Macros.h" #include "Core/Program/Program.h" #include "Scene/Scene.h" -#include namespace Falcor { @@ -45,7 +44,6 @@ namespace Falcor class FALCOR_API EmissiveLightSampler { public: - using SharedPtr = std::shared_ptr; virtual ~EmissiveLightSampler() = default; /** Updates the sampler to the current frame. @@ -74,10 +72,10 @@ namespace Falcor EmissiveLightSamplerType getType() const { return mType; } protected: - EmissiveLightSampler(EmissiveLightSamplerType type, Scene::SharedPtr pScene) : mType(type), mpScene(pScene) {} + EmissiveLightSampler(EmissiveLightSamplerType type, ref pScene) : mType(type), mpScene(pScene) {} // Internal state const EmissiveLightSamplerType mType; ///< Type of emissive sampler. See EmissiveLightSamplerType.slangh. - Scene::SharedPtr mpScene; + ref mpScene; }; } diff --git a/Source/Falcor/Rendering/Lights/EmissivePowerSampler.cpp b/Source/Falcor/Rendering/Lights/EmissivePowerSampler.cpp index bdd394bf5..db0cd1f62 100644 --- a/Source/Falcor/Rendering/Lights/EmissivePowerSampler.cpp +++ b/Source/Falcor/Rendering/Lights/EmissivePowerSampler.cpp @@ -31,11 +31,6 @@ namespace Falcor { - EmissivePowerSampler::SharedPtr EmissivePowerSampler::create(RenderContext* pRenderContext, Scene::SharedPtr pScene) - { - return SharedPtr(new EmissivePowerSampler(pRenderContext, pScene)); - } - bool EmissivePowerSampler::update(RenderContext* pRenderContext) { FALCOR_PROFILE(pRenderContext, "EmissivePowerSampler::update"); @@ -76,7 +71,7 @@ namespace Falcor var["_emissivePower"]["triangleAliasTable"] = mTriangleTable.fullTable; } - EmissivePowerSampler::EmissivePowerSampler(RenderContext* pRenderContext, Scene::SharedPtr pScene) + EmissivePowerSampler::EmissivePowerSampler(RenderContext* pRenderContext, ref pScene) : EmissiveLightSampler(EmissiveLightSamplerType::Power, pScene) { // Make sure the light collection is created. @@ -167,7 +162,7 @@ namespace Falcor { float(sum), N, - Buffer::createTyped(mpScene->getDevice().get(), N), + Buffer::createTyped(mpScene->getDevice(), N), }; result.fullTable->setBlob(&fullTable[0], 0, N * sizeof(uint2)); diff --git a/Source/Falcor/Rendering/Lights/EmissivePowerSampler.h b/Source/Falcor/Rendering/Lights/EmissivePowerSampler.h index fe9e728b0..11a5fb0dd 100644 --- a/Source/Falcor/Rendering/Lights/EmissivePowerSampler.h +++ b/Source/Falcor/Rendering/Lights/EmissivePowerSampler.h @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -29,7 +29,6 @@ #include "EmissiveLightSampler.h" #include "Core/Macros.h" #include "Scene/Lights/LightCollection.h" -#include #include #include @@ -43,23 +42,20 @@ namespace Falcor class FALCOR_API EmissivePowerSampler : public EmissiveLightSampler { public: - using SharedPtr = std::shared_ptr; - struct AliasTable { float weightSum; ///< Total weight of all elements used to create the alias table uint32_t N; ///< Number of entries in the alias table (and # elements in the buffers) - Buffer::SharedPtr fullTable; ///< A compressed/packed merged table. Max 2^24 (16 million) entries per table. + ref fullTable; ///< A compressed/packed merged table. Max 2^24 (16 million) entries per table. }; - virtual ~EmissivePowerSampler() = default; - /** Creates a EmissivePowerSampler 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); + EmissivePowerSampler(RenderContext* pRenderContext, ref pScene); + virtual ~EmissivePowerSampler() = default; /** Updates the sampler to the current frame. \param[in] pRenderContext The render context. @@ -73,8 +69,6 @@ namespace Falcor virtual void setShaderData(const ShaderVar& var) const override; protected: - EmissivePowerSampler(RenderContext* pRenderContext, Scene::SharedPtr pScene); - /** Generate an alias table \param[in] weights The weights we'd like to sample each entry proportional to \returns The alias table @@ -84,7 +78,7 @@ namespace Falcor // Internal state bool mNeedsRebuild = true; ///< Trigger rebuild on the next call to update(). We should always build on the first call, so the initial value is true. - LightCollection::SharedConstPtr mpLightCollection; + ref mpLightCollection; std::mt19937 mAliasTableRng; AliasTable mTriangleTable; diff --git a/Source/Falcor/Rendering/Lights/EmissiveUniformSampler.cpp b/Source/Falcor/Rendering/Lights/EmissiveUniformSampler.cpp index fa04377b8..50ebdaa59 100644 --- a/Source/Falcor/Rendering/Lights/EmissiveUniformSampler.cpp +++ b/Source/Falcor/Rendering/Lights/EmissiveUniformSampler.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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,12 +30,7 @@ namespace Falcor { - EmissiveUniformSampler::SharedPtr EmissiveUniformSampler::create(RenderContext* pRenderContext, Scene::SharedPtr pScene, const Options& options) - { - return SharedPtr(new EmissiveUniformSampler(pRenderContext, pScene, options)); - } - - EmissiveUniformSampler::EmissiveUniformSampler(RenderContext* pRenderContext, Scene::SharedPtr pScene, const Options& options) + EmissiveUniformSampler::EmissiveUniformSampler(RenderContext* pRenderContext, ref pScene, const Options& options) : EmissiveLightSampler(EmissiveLightSamplerType::Uniform, pScene) , mOptions(options) { diff --git a/Source/Falcor/Rendering/Lights/EmissiveUniformSampler.h b/Source/Falcor/Rendering/Lights/EmissiveUniformSampler.h index 69c1513b1..2e15624db 100644 --- a/Source/Falcor/Rendering/Lights/EmissiveUniformSampler.h +++ b/Source/Falcor/Rendering/Lights/EmissiveUniformSampler.h @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -29,7 +29,6 @@ #include "EmissiveLightSampler.h" #include "Core/Macros.h" #include "Scene/Lights/LightCollection.h" -#include namespace Falcor { @@ -40,8 +39,6 @@ namespace Falcor class FALCOR_API EmissiveUniformSampler : public EmissiveLightSampler { public: - using SharedPtr = std::shared_ptr; - /** EmissiveUniformSampler configuration. Note if you change options, please update FALCOR_SCRIPT_BINDING in EmissiveUniformSampler.cpp */ @@ -51,22 +48,19 @@ namespace Falcor //bool usePreintegration = true; ///< Use pre-integrated flux per triangle to guide BVH build/sampling. Only relevant if mUseBVHTree == true. }; - 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 Options& options = Options()); + EmissiveUniformSampler(RenderContext* pRenderContext, ref pScene, const Options& options = Options()); + virtual ~EmissiveUniformSampler() = default; /** Returns the current configuration. */ const Options& getOptions() const { return mOptions; } protected: - EmissiveUniformSampler(RenderContext* pRenderContext, Scene::SharedPtr pScene, const Options& options); - // Configuration Options mOptions; }; diff --git a/Source/Falcor/Rendering/Lights/EnvMapSampler.cpp b/Source/Falcor/Rendering/Lights/EnvMapSampler.cpp index bf50edcc9..306bec006 100644 --- a/Source/Falcor/Rendering/Lights/EnvMapSampler.cpp +++ b/Source/Falcor/Rendering/Lights/EnvMapSampler.cpp @@ -28,8 +28,7 @@ #include "EnvMapSampler.h" #include "Core/Assert.h" #include "Core/API/RenderContext.h" -#include "RenderGraph/BasePasses/ComputePass.h" -#include +#include "Core/Pass/ComputePass.h" namespace Falcor { @@ -42,27 +41,8 @@ namespace Falcor const uint32_t kDefaultSpp = 64; } - EnvMapSampler::SharedPtr EnvMapSampler::create(std::shared_ptr pDevice, EnvMap::SharedPtr pEnvMap) - { - return SharedPtr(new EnvMapSampler(std::move(pDevice), pEnvMap)); - } - - void EnvMapSampler::setShaderData(const ShaderVar& var) const - { - FALCOR_ASSERT(var.isValid()); - - // Set variables. - float2 invDim = 1.f / float2(mpImportanceMap->getWidth(), mpImportanceMap->getHeight()); - var["importanceBaseMip"] = mpImportanceMap->getMipCount() - 1; // The base mip is 1x1 texels - var["importanceInvDim"] = invDim; - - // Bind resources. - var["importanceMap"] = mpImportanceMap; - var["importanceSampler"] = mpImportanceSampler; - } - - EnvMapSampler::EnvMapSampler(std::shared_ptr pDevice, EnvMap::SharedPtr pEnvMap) - : mpDevice(std::move(pDevice)) + EnvMapSampler::EnvMapSampler(ref pDevice, ref pEnvMap) + : mpDevice(pDevice) , mpEnvMap(pEnvMap) { FALCOR_ASSERT(pEnvMap); @@ -74,7 +54,7 @@ namespace Falcor Sampler::Desc 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(mpDevice.get(), samplerDesc); + mpImportanceSampler = Sampler::create(mpDevice, samplerDesc); // Create hierarchical importance map for sampling. if (!createImportanceMap(mpDevice->getRenderContext(), kDefaultDimension, kDefaultSpp)) @@ -83,32 +63,47 @@ namespace Falcor } } + void EnvMapSampler::setShaderData(const ShaderVar& var) const + { + FALCOR_ASSERT(var.isValid()); + + // Set variables. + float2 invDim = 1.f / float2(mpImportanceMap->getWidth(), mpImportanceMap->getHeight()); + var["importanceBaseMip"] = mpImportanceMap->getMipCount() - 1; // The base mip is 1x1 texels + var["importanceInvDim"] = invDim; + + // Bind resources. + var["importanceMap"] = mpImportanceMap; + var["importanceSampler"] = mpImportanceSampler; + } + bool EnvMapSampler::createImportanceMap(RenderContext* pRenderContext, uint32_t dimension, uint32_t samples) { FALCOR_ASSERT(isPowerOf2(dimension)); FALCOR_ASSERT(isPowerOf2(samples)); // We create log2(N)+1 mips from NxN...1x1 texels resolution. - uint32_t mips = glm::log2(dimension) + 1; + uint32_t mips = std::log2(dimension) + 1; FALCOR_ASSERT((1u << (mips - 1)) == dimension); FALCOR_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(mpDevice.get(), dimension, dimension, ResourceFormat::R32Float, 1, mips, nullptr, Resource::BindFlags::ShaderResource | Resource::BindFlags::RenderTarget | Resource::BindFlags::UnorderedAccess); + mpImportanceMap = Texture::create2D(mpDevice, dimension, dimension, ResourceFormat::R32Float, 1, mips, nullptr, Resource::BindFlags::ShaderResource | Resource::BindFlags::RenderTarget | Resource::BindFlags::UnorderedAccess); FALCOR_ASSERT(mpImportanceMap); - mpSetupPass["gEnvMap"] = mpEnvMap->getEnvMap(); - mpSetupPass["gEnvSampler"] = mpEnvMap->getEnvSampler(); - mpSetupPass["gImportanceMap"] = mpImportanceMap; + auto var = mpSetupPass->getRootVar(); + var["gEnvMap"] = mpEnvMap->getEnvMap(); + var["gEnvSampler"] = mpEnvMap->getEnvSampler(); + var["gImportanceMap"] = mpImportanceMap; uint32_t samplesX = std::max(1u, (uint32_t)std::sqrt(samples)); uint32_t samplesY = samples / samplesX; FALCOR_ASSERT(samples == samplesX * samplesY); - mpSetupPass["CB"]["outputDim"] = uint2(dimension); - mpSetupPass["CB"]["outputDimInSamples"] = uint2(dimension * samplesX, dimension * samplesY); - mpSetupPass["CB"]["numSamples"] = uint2(samplesX, samplesY); - mpSetupPass["CB"]["invSamples"] = 1.f / (samplesX * samplesY); + var["CB"]["outputDim"] = uint2(dimension); + var["CB"]["outputDimInSamples"] = uint2(dimension * samplesX, dimension * samplesY); + var["CB"]["numSamples"] = uint2(samplesX, samplesY); + var["CB"]["invSamples"] = 1.f / (samplesX * samplesY); // Execute setup pass to compute the square importance map (base mip). mpSetupPass->execute(pRenderContext, dimension, dimension); diff --git a/Source/Falcor/Rendering/Lights/EnvMapSampler.h b/Source/Falcor/Rendering/Lights/EnvMapSampler.h index ce7d1196a..2e0a97289 100644 --- a/Source/Falcor/Rendering/Lights/EnvMapSampler.h +++ b/Source/Falcor/Rendering/Lights/EnvMapSampler.h @@ -29,9 +29,8 @@ #include "Core/Macros.h" #include "Core/API/Texture.h" #include "Core/API/Sampler.h" +#include "Core/Pass/ComputePass.h" #include "Scene/Lights/EnvMap.h" -#include "RenderGraph/BasePasses/ComputePass.h" -#include namespace Falcor { @@ -43,37 +42,32 @@ namespace Falcor class FALCOR_API EnvMapSampler { public: - using SharedPtr = std::shared_ptr; - - virtual ~EnvMapSampler() = default; - /** Create a new object. \param[in] pDevice GPU device. \param[in] pEnvMap The environment map. */ - static SharedPtr create(std::shared_ptr pDevice, EnvMap::SharedPtr pEnvMap); + EnvMapSampler(ref pDevice, ref pEnvMap); + virtual ~EnvMapSampler() = default; /** Bind the environment map sampler to a given shader variable. \param[in] var Shader variable. */ void setShaderData(const ShaderVar& var) const; - const EnvMap::SharedPtr& getEnvMap() const { return mpEnvMap; } + const ref& getEnvMap() const { return mpEnvMap; } - const Texture::SharedPtr& getImportanceMap() const { return mpImportanceMap; } + const ref& getImportanceMap() const { return mpImportanceMap; } protected: - EnvMapSampler(std::shared_ptr pDevice, EnvMap::SharedPtr pEnvMap); - bool createImportanceMap(RenderContext* pRenderContext, uint32_t dimension, uint32_t samples); - std::shared_ptr mpDevice; + ref mpDevice; - EnvMap::SharedPtr mpEnvMap; ///< Environment map. + ref mpEnvMap; ///< Environment map. - ComputePass::SharedPtr mpSetupPass; ///< Compute pass for creating the importance map. + ref mpSetupPass; ///< Compute pass for creating the importance map. - Texture::SharedPtr mpImportanceMap; ///< Hierarchical importance map (luminance). - Sampler::SharedPtr mpImportanceSampler; + ref mpImportanceMap; ///< Hierarchical importance map (luminance). + ref mpImportanceSampler; }; } diff --git a/Source/Falcor/Rendering/Lights/LightBVH.cpp b/Source/Falcor/Rendering/Lights/LightBVH.cpp index 9bc237cb4..ee74951b5 100644 --- a/Source/Falcor/Rendering/Lights/LightBVH.cpp +++ b/Source/Falcor/Rendering/Lights/LightBVH.cpp @@ -37,8 +37,8 @@ namespace namespace Falcor { - LightBVH::LightBVH(std::shared_ptr pDevice, const LightCollection::SharedConstPtr& pLightCollection) - : mpDevice(std::move(pDevice)) + LightBVH::LightBVH(ref pDevice, const ref& pLightCollection) + : mpDevice(pDevice) , mpLightCollection(pLightCollection) { mLeafUpdater = ComputePass::create(mpDevice, kShaderFile, "updateLeafNodes"); @@ -54,7 +54,7 @@ namespace Falcor // Update all leaf nodes. { - auto var = mLeafUpdater->getVars()["CB"]; + auto var = mLeafUpdater->getRootVar()["CB"]; mpLightCollection->setShaderData(var["gLights"]); setShaderData(var["gLightBVH"]); var["gNodeIndices"] = mpNodeIndicesBuffer; @@ -69,7 +69,7 @@ namespace Falcor // Update all internal nodes. { - auto var = mInternalUpdater->getVars()["CB"]; + auto var = mInternalUpdater->getRootVar()["CB"]; mpLightCollection->setShaderData(var["gLights"]); setShaderData(var["gLightBVH"]); var["gNodeIndices"] = mpNodeIndicesBuffer; @@ -262,7 +262,7 @@ namespace Falcor if (!mpNodeIndicesBuffer || mpNodeIndicesBuffer->getElementCount() < mNodeIndices.size()) { - mpNodeIndicesBuffer = Buffer::createStructured(mpDevice.get(), sizeof(uint32_t), (uint32_t)mNodeIndices.size(), ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, nullptr, false); + mpNodeIndicesBuffer = Buffer::createStructured(mpDevice, sizeof(uint32_t), (uint32_t)mNodeIndices.size(), ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, nullptr, false); mpNodeIndicesBuffer->setName("LightBVH::mpNodeIndicesBuffer"); } @@ -275,17 +275,17 @@ namespace Falcor auto var = mLeafUpdater->getRootVar()["CB"]["gLightBVH"]; if (!mpBVHNodesBuffer || mpBVHNodesBuffer->getElementCount() < mNodes.size()) { - mpBVHNodesBuffer = Buffer::createStructured(mpDevice.get(), var["nodes"], (uint32_t)mNodes.size(), Resource::BindFlags::ShaderResource | Resource::BindFlags::UnorderedAccess, Buffer::CpuAccess::None, nullptr, false); + mpBVHNodesBuffer = Buffer::createStructured(mpDevice, var["nodes"], (uint32_t)mNodes.size(), Resource::BindFlags::ShaderResource | Resource::BindFlags::UnorderedAccess, Buffer::CpuAccess::None, nullptr, false); mpBVHNodesBuffer->setName("LightBVH::mpBVHNodesBuffer"); } if (!mpTriangleIndicesBuffer || mpTriangleIndicesBuffer->getElementCount() < triangleIndices.size()) { - mpTriangleIndicesBuffer = Buffer::createStructured(mpDevice.get(), var["triangleIndices"], (uint32_t)triangleIndices.size(), Resource::BindFlags::ShaderResource, Buffer::CpuAccess::None, nullptr, false); + mpTriangleIndicesBuffer = Buffer::createStructured(mpDevice, var["triangleIndices"], (uint32_t)triangleIndices.size(), Resource::BindFlags::ShaderResource, Buffer::CpuAccess::None, nullptr, false); mpTriangleIndicesBuffer->setName("LightBVH::mpTriangleIndicesBuffer"); } if (!mpTriangleBitmasksBuffer || mpTriangleBitmasksBuffer->getElementCount() < triangleBitmasks.size()) { - mpTriangleBitmasksBuffer = Buffer::createStructured(mpDevice.get(), var["triangleBitmasks"], (uint32_t)triangleBitmasks.size(), Resource::BindFlags::ShaderResource, Buffer::CpuAccess::None, nullptr, false); + mpTriangleBitmasksBuffer = Buffer::createStructured(mpDevice, var["triangleBitmasks"], (uint32_t)triangleBitmasks.size(), Resource::BindFlags::ShaderResource, Buffer::CpuAccess::None, nullptr, false); mpTriangleBitmasksBuffer->setName("LightBVH::mpTriangleBitmasksBuffer"); } diff --git a/Source/Falcor/Rendering/Lights/LightBVH.h b/Source/Falcor/Rendering/Lights/LightBVH.h index 877389586..8204e28c3 100644 --- a/Source/Falcor/Rendering/Lights/LightBVH.h +++ b/Source/Falcor/Rendering/Lights/LightBVH.h @@ -76,7 +76,7 @@ namespace Falcor \param[in] pDevice GPU device. \param[in] pLightCollection The light collection around which the BVH will be built. */ - LightBVH(std::shared_ptr pDevice, const LightCollection::SharedConstPtr& pLightCollection); + LightBVH(ref pDevice, const ref& pLightCollection); /** Refit all the BVH nodes to the underlying geometry, without changing the hierarchy. The BVH needs to have been built before trying to refit it. @@ -120,7 +120,7 @@ namespace Falcor /** Bind the light BVH into a shader variable. \param[in] var The shader variable to set the data into. */ - void setShaderData(ShaderVar const& var) const; + void setShaderData(const ShaderVar& var) const; protected: void finalize(); @@ -142,11 +142,11 @@ namespace Falcor }; // Internal state - std::shared_ptr mpDevice; - const LightCollection::SharedConstPtr mpLightCollection; + ref mpDevice; + ref mpLightCollection; - ComputePass::SharedPtr mLeafUpdater; ///< Compute pass for refitting the leaf nodes. - ComputePass::SharedPtr mInternalUpdater; ///< Compute pass for refitting internal nodes. + ref mLeafUpdater; ///< Compute pass for refitting the leaf nodes. + ref mInternalUpdater; ///< Compute pass for refitting internal nodes. // CPU resources mutable std::vector mNodes; ///< CPU-side copy of packed BVH nodes. @@ -158,10 +158,10 @@ namespace Falcor mutable bool mIsCpuDataValid = false; ///< Indicates whether the CPU-side data matches the GPU buffers. // GPU resources - Buffer::SharedPtr mpBVHNodesBuffer; ///< Buffer holding all BVH nodes. - Buffer::SharedPtr mpTriangleIndicesBuffer; ///< Triangle indices sorted by leaf node. Each leaf node refers to a contiguous array of triangle indices. - Buffer::SharedPtr mpTriangleBitmasksBuffer; ///< Array containing the per triangle bit pattern retracing the tree traversal to reach the triangle: 0=left child, 1=right child. - Buffer::SharedPtr mpNodeIndicesBuffer; ///< Buffer holding all node indices sorted by tree depth. This is used for BVH refit. + ref mpBVHNodesBuffer; ///< Buffer holding all BVH nodes. + ref mpTriangleIndicesBuffer; ///< Triangle indices sorted by leaf node. Each leaf node refers to a contiguous array of triangle indices. + ref mpTriangleBitmasksBuffer; ///< Array containing the per triangle bit pattern retracing the tree traversal to reach the triangle: 0=left child, 1=right child. + ref mpNodeIndicesBuffer; ///< Buffer holding all node indices sorted by tree depth. This is used for BVH refit. friend LightBVHBuilder; }; diff --git a/Source/Falcor/Rendering/Lights/LightBVHBuilder.cpp b/Source/Falcor/Rendering/Lights/LightBVHBuilder.cpp index 95c692731..2f1931e92 100644 --- a/Source/Falcor/Rendering/Lights/LightBVHBuilder.cpp +++ b/Source/Falcor/Rendering/Lights/LightBVHBuilder.cpp @@ -31,6 +31,7 @@ #include "Utils/Logger.h" #include "Utils/Timing/Profiler.h" #include "Utils/Scripting/ScriptBindings.h" +#include "Utils/Math/MathConstants.slangh" #include namespace @@ -47,7 +48,7 @@ namespace inline float safeACos(float v) { - return std::acos(glm::clamp(v, -1.0f, 1.0f)); + return std::acos(std::clamp(v, -1.0f, 1.0f)); } /** Returns sin(a) based on cos(a) for a in [0,pi]. @@ -69,7 +70,7 @@ namespace float cosResult = kInvalidCosConeAngle; if (cosTheta != kInvalidCosConeAngle && cosOtherTheta != kInvalidCosConeAngle) { - const float cosDiffTheta = glm::dot(coneDir, otherConeDir); + const float cosDiffTheta = dot(coneDir, otherConeDir); const float sinDiffTheta = sinFromCos(cosDiffTheta); const float sinOtherTheta = sinFromCos(cosOtherTheta); @@ -95,16 +96,16 @@ namespace float3 coneUnionOld(float3 aDir, float aCosTheta, float3 bDir, float bCosTheta, float& cosResult) { float3 dir = aDir + bDir; - if (aCosTheta == kInvalidCosConeAngle || bCosTheta == kInvalidCosConeAngle || dir == float3(0.0f)) + if (aCosTheta == kInvalidCosConeAngle || bCosTheta == kInvalidCosConeAngle || all(dir == float3(0.0f))) { cosResult = kInvalidCosConeAngle; return float3(0.0f); } - dir = glm::normalize(dir); + dir = normalize(dir); - const float aDiff = safeACos(glm::dot(dir, aDir)); - const float bDiff = safeACos(glm::dot(dir, bDir)); + const float aDiff = safeACos(dot(dir, aDir)); + const float bDiff = safeACos(dot(dir, bDir)); cosResult = std::cos(std::max(aDiff + std::acos(aCosTheta), bDiff + std::acos(bCosTheta))); return dir; } @@ -130,9 +131,9 @@ namespace } // TODO: this could be optimized to use fewer trig functions. - const float theta = safeACos(glm::dot(aDir, bDir)); + const float theta = safeACos(dot(aDir, bDir)); const float aTheta = safeACos(aCosTheta), bTheta = safeACos(bCosTheta); - if (std::min(theta + bTheta, glm::pi()) <= aTheta) + if (std::min(theta + bTheta, float(M_PI)) <= aTheta) { // a encloses b and we're done. cosResult = aCosTheta; @@ -142,7 +143,7 @@ namespace // Merge the two cones. First compute the spread angle of the cone // that will fit all of them. float oTheta = (theta + aTheta + bTheta) / 2; - if (oTheta > glm::pi()) + if (oTheta > float(M_PI)) { cosResult = kInvalidCosConeAngle; return float3(0.0f); @@ -151,9 +152,9 @@ namespace // Rotate a's axis toward b just enough so that that oTheta covers // both cones. const float rTheta = oTheta - aTheta; - const float3 rDir = glm::cross(aDir, bDir); + const float3 rDir = cross(aDir, bDir); float3 dir; - if (glm::dot(rDir, rDir) < 1e-8) + if (dot(rDir, rDir) < 1e-8) { // The two vectors are effectively pointing in opposite directions. @@ -170,14 +171,14 @@ namespace // aTheta) and then be able to use the tighter spread angle // oTheta computed before, but it probably doesn't matter much // in this (rare) case. - oTheta = std::min(glm::pi(), glm::half_pi() + aTheta); + oTheta = std::min(float(M_PI), float(M_PI_2) + aTheta); cosResult = std::cos(oTheta); } else { // Rotate aDir by an angle of rTheta around the axis rDir. - const rmcv::mat4 rotationMatrix = rmcv::rotate(rmcv::mat4(), rTheta, rDir); - dir = rotationMatrix * float4(aDir, 0); + const float4x4 rotationMatrix = math::matrixFromRotation(rTheta, rDir); + dir = transformVector(rotationMatrix, aDir); cosResult = std::cos(oTheta); } @@ -188,7 +189,7 @@ namespace // the two cone vectors and the spread angle // of the given cone is still within the // extent of the result cone. - float cosDelta = glm::dot(d, dir); + float cosDelta = dot(d, dir); float delta = safeACos(cosDelta); bool dInCone = (delta + theta <= oTheta * 1.01f || delta + theta <= oTheta + 1e-3f); @@ -213,7 +214,7 @@ namespace { return -std::numeric_limits::infinity(); } - const float3 dims = glm::max(float3(epsilon), bb.extent()); + const float3 dims = max(float3(epsilon), bb.extent()); return dims.x * dims.y * dims.z; } @@ -492,9 +493,9 @@ namespace Falcor { coneDirectionSum += data.trianglesData[triangleIdx].coneDirection; } - if (glm::length(coneDirectionSum) >= FLT_MIN) + if (length(coneDirectionSum) >= FLT_MIN) { - coneDirection = glm::normalize(coneDirectionSum); + coneDirection = normalize(coneDirectionSum); cosTheta = 1.f; for (uint32_t triangleIdx = triangleRange.begin; triangleIdx < triangleRange.end; ++triangleIdx) { @@ -666,10 +667,10 @@ namespace Falcor */ static float computeOrientationCost(const float theta_o) { - float theta_w = std::min(theta_o + glm::half_pi(), glm::pi()); + float theta_w = std::min(theta_o + float(M_PI_2), float(M_PI)); float sin_theta_o = std::sin(theta_o); float cos_theta_o = std::cos(theta_o); - return glm::two_pi() * (1.0f - cos_theta_o) + glm::half_pi() * (2.0f * theta_w * sin_theta_o - std::cos(theta_o - 2.0f * theta_w) - 2.0f * theta_o * sin_theta_o + cos_theta_o); + return float(M_2PI) * (1.0f - cos_theta_o) + float(M_PI_2) * (2.0f * theta_w * sin_theta_o - std::cos(theta_o - 2.0f * theta_w) - 2.0f * theta_o * sin_theta_o + cos_theta_o); }; /** Evaluates the SAOH cost metric for a node. @@ -680,7 +681,7 @@ namespace Falcor { float fluxCost = parameters.usePreintegration ? flux : 1.0f; float aabbCost = bounds.valid() ? (parameters.useVolumeOverSA ? aabbVolume(bounds, parameters.volumeEpsilon) : bounds.area()) : 0.f; - float theta = cosTheta != kInvalidCosConeAngle ? safeACos(cosTheta) : glm::pi(); + float theta = cosTheta != kInvalidCosConeAngle ? safeACos(cosTheta) : float(M_PI); float orientationCost = parameters.useLightingCones ? computeOrientationCost(theta) : 1.0f; float cost = fluxCost * aabbCost * orientationCost; FALCOR_ASSERT(cost >= 0.f && !std::isnan(cost) && !std::isinf(cost)); @@ -759,8 +760,8 @@ namespace Falcor // TODO: Switch to a more sophisticated algorithm to get narrower cones. for (Bin& bin : bins) { - bin.cosConeAngle = glm::length(bin.coneDirection) < FLT_MIN ? kInvalidCosConeAngle : 1.0f; - bin.coneDirection = glm::normalize(bin.coneDirection); + bin.cosConeAngle = length(bin.coneDirection) < FLT_MIN ? kInvalidCosConeAngle : 1.0f; + bin.coneDirection = normalize(bin.coneDirection); } for (uint32_t i = triangleRange.begin; i < triangleRange.end; ++i) { @@ -778,10 +779,10 @@ namespace Falcor // Compute the bounding cone angle for the union of bins 0..i. float cosTheta = kInvalidCosConeAngle; - if (glm::length(total.coneDirection) >= FLT_MIN) + if (length(total.coneDirection) >= FLT_MIN) { cosTheta = 1.f; - float3 coneDir = glm::normalize(total.coneDirection); + float3 coneDir = normalize(total.coneDirection); for (std::size_t j = 0; j <= i; ++j) { cosTheta = computeCosConeAngle(coneDir, cosTheta, bins[j].coneDirection, bins[j].cosConeAngle); @@ -799,10 +800,10 @@ namespace Falcor // Compute the bounding cone angle for the union of bins i..n-1. float cosTheta = kInvalidCosConeAngle; - if (glm::length(total.coneDirection) >= FLT_MIN) + if (length(total.coneDirection) >= FLT_MIN) { cosTheta = 1.f; - float3 coneDir = glm::normalize(total.coneDirection); + float3 coneDir = normalize(total.coneDirection); for (std::size_t j = i; j <= costs.size(); ++j) { cosTheta = computeCosConeAngle(coneDir, cosTheta, bins[j].coneDirection, bins[j].cosConeAngle); diff --git a/Source/Falcor/Rendering/Lights/LightBVHSampler.cpp b/Source/Falcor/Rendering/Lights/LightBVHSampler.cpp index b25c8e8e1..5ddd01f02 100644 --- a/Source/Falcor/Rendering/Lights/LightBVHSampler.cpp +++ b/Source/Falcor/Rendering/Lights/LightBVHSampler.cpp @@ -30,8 +30,6 @@ #include "Core/Errors.h" #include "Utils/Timing/Profiler.h" #include "Utils/Scripting/ScriptBindings.h" -#include -#include #include #include @@ -47,11 +45,6 @@ namespace Falcor }; } - LightBVHSampler::SharedPtr LightBVHSampler::create(RenderContext* pRenderContext, Scene::SharedPtr pScene, const Options& options) - { - return SharedPtr(new LightBVHSampler(pRenderContext, pScene, options)); - } - bool LightBVHSampler::update(RenderContext* pRenderContext) { FALCOR_PROFILE(pRenderContext, "LightBVHSampler::update"); @@ -147,7 +140,7 @@ namespace Falcor return optionsChanged; } - LightBVHSampler::LightBVHSampler(RenderContext* pRenderContext, Scene::SharedPtr pScene, const Options& options) + LightBVHSampler::LightBVHSampler(RenderContext* pRenderContext, ref pScene, const Options& options) : EmissiveLightSampler(EmissiveLightSamplerType::LightBVH, pScene) , mOptions(options) { diff --git a/Source/Falcor/Rendering/Lights/LightBVHSampler.h b/Source/Falcor/Rendering/Lights/LightBVHSampler.h index 1f16401de..b555fde47 100644 --- a/Source/Falcor/Rendering/Lights/LightBVHSampler.h +++ b/Source/Falcor/Rendering/Lights/LightBVHSampler.h @@ -51,9 +51,6 @@ namespace Falcor class FALCOR_API LightBVHSampler : public EmissiveLightSampler { public: - using SharedPtr = std::shared_ptr; - using SharedConstPtr = std::shared_ptr; - /** LightBVHSampler configuration. Note if you change options, please update FALCOR_SCRIPT_BINDING in LightBVHSampler.cpp */ @@ -74,14 +71,13 @@ namespace Falcor Options() {} }; - virtual ~LightBVHSampler() = default; - /** Creates a LightBVHSampler 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 Options& options = Options()); + LightBVHSampler(RenderContext* pRenderContext, ref pScene, const Options& options = Options()); + virtual ~LightBVHSampler() = default; /** Updates the sampler to the current frame. \param[in] pRenderContext The render context. @@ -109,8 +105,6 @@ namespace Falcor const Options& getOptions() const { return mOptions; } protected: - LightBVHSampler(RenderContext* pRenderContext, Scene::SharedPtr pScene, const Options& options); - /// Configuration options. Options mOptions; diff --git a/Source/Falcor/Rendering/Materials/BSDFIntegrator.cpp b/Source/Falcor/Rendering/Materials/BSDFIntegrator.cpp index 9a04656bc..223ef79ac 100644 --- a/Source/Falcor/Rendering/Materials/BSDFIntegrator.cpp +++ b/Source/Falcor/Rendering/Materials/BSDFIntegrator.cpp @@ -43,8 +43,8 @@ namespace Falcor const uint2 kGridSize = { 512, 512 }; } - BSDFIntegrator::BSDFIntegrator(std::shared_ptr pDevice, const Scene::SharedPtr& pScene) - : mpDevice(std::move(pDevice)) + BSDFIntegrator::BSDFIntegrator(ref pDevice, const ref& pScene) + : mpDevice(pDevice) , mpScene(pScene) { checkArgument(mpScene != nullptr, "'pDevice' must be a valid device"); @@ -75,7 +75,7 @@ namespace Falcor FALCOR_ASSERT(finalGroupSize.x == 256 && finalGroupSize.y == 1 && finalGroupSize.z == 1); FALCOR_ASSERT(finalGroupSize.x == mResultCount); - mpFence = GpuFence::create(mpDevice.get()); + mpFence = GpuFence::create(mpDevice); } float3 BSDFIntegrator::integrateIsotropic(RenderContext* pRenderContext, const MaterialID materialID, float cosTheta) @@ -100,7 +100,7 @@ namespace Falcor if (!mpCosThetaBuffer || mpCosThetaBuffer->getElementCount() < gridCount) { - mpCosThetaBuffer = Buffer::createStructured(mpDevice.get(), sizeof(float), gridCount, ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, cosThetas.data(), false); + mpCosThetaBuffer = Buffer::createStructured(mpDevice, sizeof(float), gridCount, ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, cosThetas.data(), false); } else { @@ -111,12 +111,12 @@ namespace Falcor uint32_t elemCount = gridCount * mResultCount; if (!mpResultBuffer || mpResultBuffer->getElementCount() < elemCount) { - mpResultBuffer = Buffer::createStructured(mpDevice.get(), sizeof(float3), elemCount, ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess, Buffer::CpuAccess::None, nullptr, false); + mpResultBuffer = Buffer::createStructured(mpDevice, sizeof(float3), elemCount, ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess, Buffer::CpuAccess::None, nullptr, false); } if (!mpFinalResultBuffer || mpFinalResultBuffer->getElementCount() < gridCount) { - mpFinalResultBuffer = Buffer::createStructured(mpDevice.get(), sizeof(float3), gridCount, ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess, Buffer::CpuAccess::None, nullptr, false); - mpStagingBuffer = Buffer::createStructured(mpDevice.get(), sizeof(float3), gridCount, ResourceBindFlags::None, Buffer::CpuAccess::Read, nullptr, false); + mpFinalResultBuffer = Buffer::createStructured(mpDevice, sizeof(float3), gridCount, ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess, Buffer::CpuAccess::None, nullptr, false); + mpStagingBuffer = Buffer::createStructured(mpDevice, sizeof(float3), gridCount, ResourceBindFlags::None, Buffer::CpuAccess::Read, nullptr, false); } // Execute GPU passes. @@ -153,7 +153,7 @@ namespace Falcor var["cosThetas"] = mpCosThetaBuffer; var["results"] = mpResultBuffer; - mpIntegrationPass["gScene"] = mpScene->getParameterBlock(); + mpIntegrationPass->getRootVar()["gScene"] = mpScene->getParameterBlock(); mpIntegrationPass->execute(pRenderContext, uint3(kGridSize, gridCount)); } diff --git a/Source/Falcor/Rendering/Materials/BSDFIntegrator.cs.slang b/Source/Falcor/Rendering/Materials/BSDFIntegrator.cs.slang index 3715786a1..929ec02a3 100644 --- a/Source/Falcor/Rendering/Materials/BSDFIntegrator.cs.slang +++ b/Source/Falcor/Rendering/Materials/BSDFIntegrator.cs.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -62,10 +62,8 @@ struct BSDFIntegrator // Setup shading data struct. ShadingData sd = {}; - sd.T = float3(1.f, 0.f, 0.f); - sd.B = float3(0.f, 1.f, 0.f); - sd.N = float3(0.f, 0.f, 1.f); - sd.faceN = sd.N; + sd.frame = ShadingFrame::createIdentity(); + sd.faceN = sd.frame.N; sd.frontFacing = true; sd.mtl = gScene.materials.getMaterialHeader(materialID); diff --git a/Source/Falcor/Rendering/Materials/BSDFIntegrator.h b/Source/Falcor/Rendering/Materials/BSDFIntegrator.h index 2a08f98f1..9f8b99d3f 100644 --- a/Source/Falcor/Rendering/Materials/BSDFIntegrator.h +++ b/Source/Falcor/Rendering/Materials/BSDFIntegrator.h @@ -29,8 +29,8 @@ #include "Core/Macros.h" #include "Core/API/Buffer.h" #include "Core/API/GpuFence.h" +#include "Core/Pass/ComputePass.h" #include "Utils/Math/Vector.h" -#include "RenderGraph/BasePasses/ComputePass.h" #include "Scene/Scene.h" #include "Scene/SceneIDs.h" #include @@ -43,7 +43,7 @@ namespace Falcor { public: /// Constructor. - BSDFIntegrator(std::shared_ptr pDevice, const Scene::SharedPtr& pScene); + BSDFIntegrator(ref pDevice, const ref& pScene); /** Integrate the BSDF for a material given a single incident direction. The BSDF is assumed to be isotropic and is integrated over outgoing directions in the upper hemisphere. @@ -67,15 +67,15 @@ namespace Falcor void integrationPass(RenderContext* pRenderContext, const MaterialID materialID, const uint32_t gridCount) const; void finalPass(RenderContext* pRenderContext, const uint32_t gridCount) const; - std::shared_ptr mpDevice; - Scene::SharedPtr mpScene; - ComputePass::SharedPtr mpIntegrationPass; ///< Integration pass. - ComputePass::SharedPtr mpFinalPass; ///< Final reduction pass. - Buffer::SharedPtr mpCosThetaBuffer; ///< Buffer for uploading incident cos theta angles. - Buffer::SharedPtr mpResultBuffer; ///< Buffer for intermediate results. - Buffer::SharedPtr mpFinalResultBuffer; ///< Buffer for final results after reduction. - Buffer::SharedPtr mpStagingBuffer; ///< Staging buffer for readback of final results. - GpuFence::SharedPtr mpFence; ///< Fence for synchronizing readback. + ref mpDevice; + ref mpScene; + ref mpIntegrationPass; ///< Integration pass. + ref mpFinalPass; ///< Final reduction pass. + ref mpCosThetaBuffer; ///< Buffer for uploading incident cos theta angles. + ref mpResultBuffer; ///< Buffer for intermediate results. + ref mpFinalResultBuffer; ///< Buffer for final results after reduction. + ref mpStagingBuffer; ///< Staging buffer for readback of final results. + ref mpFence; ///< Fence for synchronizing readback. uint32_t mResultCount; ///< Number of intermediate results per integration grid. }; } diff --git a/Source/Falcor/Rendering/Materials/BSDFs/BeerBTDF.slang b/Source/Falcor/Rendering/Materials/BSDFs/BeerBTDF.slang index c2524e133..29bc7e786 100644 --- a/Source/Falcor/Rendering/Materials/BSDFs/BeerBTDF.slang +++ b/Source/Falcor/Rendering/Materials/BSDFs/BeerBTDF.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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,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 "Utils/Math/MathConstants.slangh" - __exported import Rendering.Materials.IBSDF; /** diff --git a/Source/Falcor/Rendering/Materials/BSDFs/DielectricPlateBSDF.slang b/Source/Falcor/Rendering/Materials/BSDFs/DielectricPlateBSDF.slang index 42d756587..085620f7f 100644 --- a/Source/Falcor/Rendering/Materials/BSDFs/DielectricPlateBSDF.slang +++ b/Source/Falcor/Rendering/Materials/BSDFs/DielectricPlateBSDF.slang @@ -27,13 +27,10 @@ **************************************************************************/ #include "Utils/Math/MathConstants.slangh" -__exported import Rendering.Materials.IMaterialInstance; -__exported import Rendering.Materials.BxDF; -__exported import Rendering.Materials.AnisotropicGGX; -import Utils.Color.ColorHelpers; import Utils.Math.MathHelpers; import Rendering.Materials.Fresnel; -import Utils.Debug.PixelDebug; +import Rendering.Materials.AnisotropicGGX; +__exported import Rendering.Materials.IBSDF; /** * Thin (architectural) dielectric to simulate plane parallel materials on a single polygon. diff --git a/Source/Falcor/Rendering/Materials/BSDFs/DiffuseSpecularBRDF.slang b/Source/Falcor/Rendering/Materials/BSDFs/DiffuseSpecularBRDF.slang index 5d5f9abd9..66c29b5be 100644 --- a/Source/Falcor/Rendering/Materials/BSDFs/DiffuseSpecularBRDF.slang +++ b/Source/Falcor/Rendering/Materials/BSDFs/DiffuseSpecularBRDF.slang @@ -44,7 +44,8 @@ struct DiffuseSpecularBRDF : IBSDF float3 specular; ///< Specular albedo. float roughness; ///< Roughness (linear). - /** Initialize using parameters from the Disney parameterization. + /** + * Initialize using parameters from the Disney parameterization. */ __init(const DiffuseSpecularData params) { diff --git a/Source/Falcor/Rendering/Materials/BSDFs/DisneyDiffuseBRDF.slang b/Source/Falcor/Rendering/Materials/BSDFs/DisneyDiffuseBRDF.slang index 47b4c9a49..14c3c51f0 100644 --- a/Source/Falcor/Rendering/Materials/BSDFs/DisneyDiffuseBRDF.slang +++ b/Source/Falcor/Rendering/Materials/BSDFs/DisneyDiffuseBRDF.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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 @@ #include "Utils/Math/MathConstants.slangh" import Utils.Math.MathHelpers; -import Utils.Color.ColorHelpers; import Rendering.Materials.Fresnel; __exported import Rendering.Materials.IBSDF; diff --git a/Source/Falcor/Rendering/Materials/BSDFs/FrostbiteDiffuseBRDF.slang b/Source/Falcor/Rendering/Materials/BSDFs/FrostbiteDiffuseBRDF.slang index eed29cced..c54ad5afa 100644 --- a/Source/Falcor/Rendering/Materials/BSDFs/FrostbiteDiffuseBRDF.slang +++ b/Source/Falcor/Rendering/Materials/BSDFs/FrostbiteDiffuseBRDF.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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 @@ #include "Utils/Math/MathConstants.slangh" import Utils.Math.MathHelpers; -import Utils.Color.ColorHelpers; import Rendering.Materials.Fresnel; __exported import Rendering.Materials.IBSDF; /** * Frostbites's diffuse reflection. - * 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 + * 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 */ struct FrostbiteDiffuseBRDF : IBSDF { diff --git a/Source/Falcor/Rendering/Materials/BSDFs/LambertDiffuseBRDF.slang b/Source/Falcor/Rendering/Materials/BSDFs/LambertDiffuseBRDF.slang index 3cf072fe8..dff3a9ef4 100644 --- a/Source/Falcor/Rendering/Materials/BSDFs/LambertDiffuseBRDF.slang +++ b/Source/Falcor/Rendering/Materials/BSDFs/LambertDiffuseBRDF.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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 @@ #include "Utils/Math/MathConstants.slangh" import Utils.Math.MathHelpers; -import Utils.Color.ColorHelpers; __exported import Rendering.Materials.IBSDF; /** diff --git a/Source/Falcor/Rendering/Materials/BSDFs/LambertDiffuseBTDF.slang b/Source/Falcor/Rendering/Materials/BSDFs/LambertDiffuseBTDF.slang index a74f6b035..1d2caecde 100644 --- a/Source/Falcor/Rendering/Materials/BSDFs/LambertDiffuseBTDF.slang +++ b/Source/Falcor/Rendering/Materials/BSDFs/LambertDiffuseBTDF.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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 @@ #include "Utils/Math/MathConstants.slangh" import Utils.Math.MathHelpers; -import Utils.Color.ColorHelpers; __exported import Rendering.Materials.IBSDF; /** diff --git a/Source/Falcor/Rendering/Materials/BSDFs/OrenNayarBRDF.slang b/Source/Falcor/Rendering/Materials/BSDFs/OrenNayarBRDF.slang index dd05fed88..64fe9a387 100644 --- a/Source/Falcor/Rendering/Materials/BSDFs/OrenNayarBRDF.slang +++ b/Source/Falcor/Rendering/Materials/BSDFs/OrenNayarBRDF.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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 @@ #include "Utils/Math/MathConstants.slangh" import Utils.Math.MathHelpers; -import Utils.Color.ColorHelpers; __exported import Rendering.Materials.IBSDF; /** diff --git a/Source/Falcor/Rendering/Materials/BSDFs/SheenBSDF.slang b/Source/Falcor/Rendering/Materials/BSDFs/SheenBSDF.slang index 9b00e06b7..eb9e64006 100644 --- a/Source/Falcor/Rendering/Materials/BSDFs/SheenBSDF.slang +++ b/Source/Falcor/Rendering/Materials/BSDFs/SheenBSDF.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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,10 +27,8 @@ **************************************************************************/ #include "Utils/Math/MathConstants.slangh" -import Rendering.Materials.Fresnel; -__exported import Rendering.Materials.IBSDF; import Utils.Math.MathHelpers; -import Utils.Color.ColorHelpers; +__exported import Rendering.Materials.IBSDF; /** * Sheen-only implementation from Estevez and Kulla 2017, "Production Friendly Microfacet Sheen BRDF"; diff --git a/Source/Falcor/Rendering/Materials/BSDFs/SimpleBTDF.slang b/Source/Falcor/Rendering/Materials/BSDFs/SimpleBTDF.slang index dbfaefa1e..18e4f2579 100644 --- a/Source/Falcor/Rendering/Materials/BSDFs/SimpleBTDF.slang +++ b/Source/Falcor/Rendering/Materials/BSDFs/SimpleBTDF.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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,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 "Utils/Math/MathConstants.slangh" - __exported import Rendering.Materials.IBSDF; /** diff --git a/Source/Falcor/Rendering/Materials/BSDFs/SpecularMicrofacet.slang b/Source/Falcor/Rendering/Materials/BSDFs/SpecularMicrofacet.slang index 152f5850c..d69dd4b11 100644 --- a/Source/Falcor/Rendering/Materials/BSDFs/SpecularMicrofacet.slang +++ b/Source/Falcor/Rendering/Materials/BSDFs/SpecularMicrofacet.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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,18 +28,15 @@ #include "Utils/Math/MathConstants.slangh" #include "Rendering/Materials/BSDFConfig.slangh" -import Utils.Math.MathHelpers; -import Utils.Color.ColorHelpers; -__exported import Rendering.Materials.IBSDF; import Rendering.Materials.IsotropicGGX; import Rendering.Materials.Fresnel; +__exported import Rendering.Materials.IBSDF; // Enable support for delta reflection/transmission. #define EnableDeltaBSDF 1 // Enable GGX sampling using the distribution of visible normals (VNDF) instead of classic NDF sampling. // This should be the default as it has lower variance, disable for testing only. -// TODO: Make default when transmission with VNDF sampling is properly validated #define EnableVNDFSampling 1 // Enable explicitly computing sampling weights using eval(wi, wo) / evalPdf(wi, wo). @@ -195,7 +192,7 @@ struct SpecularMicrofacetBRDF : IBSDF /** * Specular reflection and transmission using microfacets. */ -struct SpecularMicrofacetBTDF : IBSDF +struct SpecularMicrofacetBSDF : IBSDF { float3 transmissionAlbedo; ///< Transmission albedo. float alpha; ///< GGX width parameter. diff --git a/Source/Falcor/Rendering/Materials/BSDFs/StandardBSDF.slang b/Source/Falcor/Rendering/Materials/BSDFs/StandardBSDF.slang index 70574b8bc..7bb91f9b0 100644 --- a/Source/Falcor/Rendering/Materials/BSDFs/StandardBSDF.slang +++ b/Source/Falcor/Rendering/Materials/BSDFs/StandardBSDF.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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 "Utils/Math/MathConstants.slangh" -#include "BSDFConfig.slangh" +#include "Rendering/Materials/BSDFConfig.slangh" import Scene.Material.MaterialData; -import Utils.Math.MathHelpers; import Utils.Color.ColorHelpers; import Rendering.Materials.Fresnel; #if DiffuseBrdf == DiffuseBrdfLambert @@ -52,7 +50,6 @@ __exported import Rendering.Materials.IBSDF; // before the clamp was removed for allowing delta events. We continue to use the same threshold. static const float kMinGGXAlpha = 0.0064f; -// TODO: Reduce to 52B /** * BSDF parameters for the standard MaterialInstance. * These are needed for initializing a `StandardBSDF` instance. @@ -70,11 +67,12 @@ struct StandardBSDFData float specularTransmission; } -/** Mixed BSDF used for the standard material in Falcor. - - This consists of a diffuse and specular BRDF. - A specular BSDF is mixed in using the specularTransmission parameter. -*/ +/** + * Mixed BSDF used for the standard material in Falcor. + * + * This consists of a diffuse and specular BRDF. + * A specular BSDF is mixed in using the specularTransmission parameter. + */ struct StandardBSDF : IBSDF { #if DiffuseBrdf == DiffuseBrdfLambert @@ -87,7 +85,7 @@ struct StandardBSDF : IBSDF LambertDiffuseBTDF diffuseTransmission; SpecularMicrofacetBRDF specularReflection; - SpecularMicrofacetBTDF specularTransmission; + SpecularMicrofacetBSDF specularTransmission; float diffTrans; ///< Mix between diffuse BRDF and diffuse BTDF. float specTrans; ///< Mix between dielectric BRDF and specular BSDF. @@ -97,11 +95,12 @@ struct StandardBSDF : IBSDF float pSpecularReflection; ///< Probability for sampling the specular BRDF. float pSpecularTransmission; ///< Probability for sampling the specular BSDF. - /** Initialize a new instance. - \param[in] wi Incident direction in the local frame. - \param[in] mtl Material header. - \param[in] data BSDF parameters. - */ + /** + * Initialize a new instance. + * @param[in] wi Incident direction in the local frame. + * @param[in] mtl Material header. + * @param[in] data BSDF parameters. + */ __init(const float3 wi, const MaterialHeader mtl, const StandardBSDFData data) { // TODO: Currently specular reflection and transmission lobes are not properly separated. @@ -172,10 +171,11 @@ struct StandardBSDF : IBSDF } } - /** Returns the set of BSDF lobes. - \param[in] data BSDF parameters. - \return Returns a set of lobes (see LobeType.slang). - */ + /** + * Returns the set of BSDF lobes. + * @param[in] data BSDF parameters. + * @return Returns a set of lobes (see LobeType.slang). + */ static uint getLobeTypes(const StandardBSDFData data) { #if EnableDeltaBSDF diff --git a/Source/Falcor/Rendering/Materials/BxDF.slang b/Source/Falcor/Rendering/Materials/BxDF.slang deleted file mode 100644 index 2a4bebb6e..000000000 --- a/Source/Falcor/Rendering/Materials/BxDF.slang +++ /dev/null @@ -1,896 +0,0 @@ -/*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. - # - # Redistribution and use in source and binary forms, with or without - # modification, are permitted provided that the following conditions - # are met: - # * Redistributions of source code must retain the above copyright - # notice, this list of conditions and the following disclaimer. - # * Redistributions in binary form must reproduce the above copyright - # notice, this list of conditions and the following disclaimer in the - # documentation and/or other materials provided with the distribution. - # * Neither the name of NVIDIA CORPORATION nor the names of its - # contributors may be used to endorse or promote products derived - # from this software without specific prior written permission. - # - # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY - # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - # (INCLUDING 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.slangh" -#include "BSDFConfig.slangh" - -import Scene.Material.MaterialData; -import Utils.Math.MathHelpers; -import Utils.Color.ColorHelpers; -import Rendering.Materials.Fresnel; -import Rendering.Materials.IsotropicGGX; -__exported import Rendering.Materials.IBSDF; - -// Enable support for delta reflection/transmission. -#define EnableDeltaBSDF 1 - -// Enable GGX sampling using the distribution of visible normals (VNDF) instead of classic NDF sampling. -// This should be the default as it has lower variance, disable for testing only. -// TODO: Make default when transmission with VNDF sampling is properly validated -#define EnableVNDFSampling 1 - -// Enable explicitly computing sampling weights using eval(wi, wo) / evalPdf(wi, wo). -// This is for testing only, as many terms of the equation cancel out allowing to save on computation. -#define ExplicitSampleWeights 0 - -// We clamp the GGX width parameter 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. -// The the value below is sufficient to avoid visible artifacts. -// Falcor used to clamp roughness to 0.08 before the clamp was removed for allowing delta events. We continue to use the same threshold. -static const float kMinGGXAlpha = 0.0064f; - -/** Lambertian diffuse reflection. - f_r(wi, wo) = albedo / pi -*/ -struct DiffuseReflectionLambert : IBSDF -{ - float3 albedo; ///< Diffuse albedo. - - float3 eval(const float3 wi, const float3 wo, inout S sg) - { - if (min(wi.z, wo.z) < kMinCosTheta) return float3(0.f); - - return M_1_PI * albedo * wo.z; - } - - bool sample(const float3 wi, out float3 wo, out float pdf, out float3 weight, out uint lobeType, inout S sg) - { - wo = sample_cosine_hemisphere_concentric(sampleNext2D(sg), pdf); - lobeType = (uint)LobeType::DiffuseReflection; - - if (min(wi.z, wo.z) < kMinCosTheta) - { - weight = {}; - return false; - } - - weight = albedo; - return true; - } - - float evalPdf(const float3 wi, const float3 wo) - { - if (min(wi.z, wo.z) < kMinCosTheta) return 0.f; - - return M_1_PI * wo.z; - } - - AlbedoContributions evalAlbedo(const float3 wi, const LobeType lobetype) - { - return AlbedoContributions(albedo, 1.f - albedo, 0.f, 0.f); - } - - RoughnessInformation getRoughnessInformation(const float3 wi) - { - RoughnessInformation r; - r.roughnessBSDFNotation = float2(0.5f, 0.5f); - return r; - } -}; - -/** Disney's diffuse reflection. - Based on https://blog.selfshadow.com/publications/s2012-shading-course/burley/s2012_pbs_disney_brdf_notes_v3.pdf -*/ -struct DiffuseReflectionDisney : IBSDF -{ - float3 albedo; ///< Diffuse albedo. - float roughness; ///< Roughness before remapping. - - float3 eval(const float3 wi, const float3 wo, inout S sg) - { - if (min(wi.z, wo.z) < kMinCosTheta) return float3(0.f); - - return evalWeight(wi, wo) * M_1_PI * wo.z; - } - - bool sample(const float3 wi, out float3 wo, out float pdf, out float3 weight, out uint lobeType, inout S sg) - { - wo = sample_cosine_hemisphere_concentric(sampleNext2D(sg), pdf); - lobeType = (uint)LobeType::DiffuseReflection; - - if (min(wi.z, wo.z) < kMinCosTheta) - { - weight = {}; - return false; - } - - weight = evalWeight(wi, wo); - return true; - } - - float evalPdf(const float3 wi, const float3 wo) - { - if (min(wi.z, wo.z) < kMinCosTheta) return 0.f; - - return M_1_PI * wo.z; - } - - // private - - // Returns f(wi, wo) * pi. - float3 evalWeight(float3 wi, float3 wo) - { - float3 h = normalize(wi + wo); - float woDotH = dot(wo, h); - float fd90 = 0.5f + 2.f * woDotH * woDotH * roughness; - float fd0 = 1.f; - float wiScatter = evalFresnelSchlick(fd0, fd90, wi.z); - float woScatter = evalFresnelSchlick(fd0, fd90, wo.z); - return albedo * wiScatter * woScatter; - } - - AlbedoContributions evalAlbedo(const float3 wi, const LobeType lobetype) - { - return AlbedoContributions(albedo, 1.f - albedo, 0.f, 0.f); - } - - RoughnessInformation getRoughnessInformation(const float3 wi) - { - RoughnessInformation r; - r.roughnessBSDFNotation = float2(0.5f, 0.5f); - return r; - } -}; - -/** Frostbites's diffuse reflection. - 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 -*/ -struct DiffuseReflectionFrostbite : IBSDF -{ - float3 albedo; ///< Diffuse albedo. - float roughness; ///< Roughness before remapping. - - float3 eval(const float3 wi, const float3 wo, inout S sg) - { - if (min(wi.z, wo.z) < kMinCosTheta) return float3(0.f); - - return evalWeight(wi, wo) * M_1_PI * wo.z; - } - - bool sample(const float3 wi, out float3 wo, out float pdf, out float3 weight, out uint lobeType, inout S sg) - { - wo = sample_cosine_hemisphere_concentric(sampleNext2D(sg), pdf); - lobeType = (uint)LobeType::DiffuseReflection; - - if (min(wi.z, wo.z) < kMinCosTheta) - { - weight = {}; - return false; - } - - weight = evalWeight(wi, wo); - return true; - } - - float evalPdf(const float3 wi, const float3 wo) - { - if (min(wi.z, wo.z) < kMinCosTheta) return 0.f; - - return M_1_PI * wo.z; - } - - // private - - // Returns f(wi, wo) * pi. - float3 evalWeight(float3 wi, float3 wo) - { - float3 h = normalize(wi + wo); - float woDotH = dot(wo, h); - float energyBias = lerp(0.f, 0.5f, roughness); - float energyFactor = lerp(1.f, 1.f / 1.51f, roughness); - float fd90 = energyBias + 2.f * woDotH * woDotH * roughness; - float fd0 = 1.f; - float wiScatter = evalFresnelSchlick(fd0, fd90, wi.z); - float woScatter = evalFresnelSchlick(fd0, fd90, wo.z); - return albedo * wiScatter * woScatter * energyFactor; - } - - AlbedoContributions evalAlbedo(const float3 wi, const LobeType lobetype) - { - return AlbedoContributions(albedo, 1.f - albedo, 0.f, 0.f); - } - - RoughnessInformation getRoughnessInformation(const float3 wi) - { - RoughnessInformation r; - r.roughnessBSDFNotation = float2(0.5f, 0.5f); - return r; - } -}; - -/** Lambertian diffuse transmission. -*/ -struct DiffuseTransmissionLambert : IBSDF -{ - float3 albedo; ///< Diffuse albedo. - - float3 eval(const float3 wi, const float3 wo, inout S sg) - { - if (min(wi.z, -wo.z) < kMinCosTheta) return float3(0.f); - - return M_1_PI * albedo * -wo.z; - } - - bool sample(const float3 wi, out float3 wo, out float pdf, out float3 weight, out uint lobeType, inout S sg) - { - wo = sample_cosine_hemisphere_concentric(sampleNext2D(sg), pdf); - wo.z = -wo.z; - lobeType = (uint)LobeType::DiffuseTransmission; - - if (min(wi.z, -wo.z) < kMinCosTheta) - { - weight = {}; - return false; - } - - weight = albedo; - return true; - } - - float evalPdf(const float3 wi, const float3 wo) - { - if (min(wi.z, -wo.z) < kMinCosTheta) return 0.f; - - return M_1_PI * -wo.z; - } - - AlbedoContributions evalAlbedo(const float3 wi, const LobeType lobetype) - { - return AlbedoContributions(0.f, 0.f, albedo, 1.f - albedo); - } - - RoughnessInformation getRoughnessInformation(const float3 wi) - { - RoughnessInformation r; - r.roughnessBSDFNotation = float2(0.5f, 0.5f); - return r; - } -} - -/** Specular reflection using microfacets. -*/ -struct SpecularReflectionMicrofacet : IBSDF -{ - float3 albedo; ///< Specular albedo. - float alpha; ///< GGX width parameter. - uint activeLobes; ///< BSDF lobes to include for sampling and evaluation. See LobeType.slang. - - bool hasLobe(LobeType lobeType) { return (activeLobes & (uint)lobeType) != 0; } - - float3 eval(const float3 wi, const float3 wo, inout S sg) - { - if (min(wi.z, wo.z) < kMinCosTheta) return float3(0.f); - -#if EnableDeltaBSDF - // Handle delta reflection. - if (alpha == 0.f) return float3(0.f); -#endif - - if (!hasLobe(LobeType::SpecularReflection)) return float3(0.f); - - float3 h = normalize(wi + wo); - float wiDotH = dot(wi, h); - - float D = evalNdfGGX(alpha, h.z); -#if SpecularMaskingFunction == SpecularMaskingFunctionSmithGGXSeparable - float G = evalMaskingSmithGGXSeparable(alpha, wi.z, wo.z); -#elif SpecularMaskingFunction == SpecularMaskingFunctionSmithGGXCorrelated - float G = evalMaskingSmithGGXCorrelated(alpha, wi.z, wo.z); -#endif - float3 F = evalFresnelSchlick(albedo, 1.f, wiDotH); - return F * D * G * 0.25f / wi.z; - } - - bool sample(const float3 wi, out float3 wo, out float pdf, out float3 weight, out uint lobeType, inout S sg) - { - // Default initialization to avoid divergence at returns. - wo = {}; - weight = {}; - pdf = 0.f; - lobeType = (uint)LobeType::SpecularReflection; - - if (wi.z < kMinCosTheta) return false; - -#if EnableDeltaBSDF - // Handle delta reflection. - if (alpha == 0.f) - { - if (!hasLobe(LobeType::DeltaReflection)) return false; - - wo = float3(-wi.x, -wi.y, wi.z); - pdf = 0.f; - weight = evalFresnelSchlick(albedo, 1.f, wi.z); - lobeType = (uint)LobeType::DeltaReflection; - return true; - } -#endif - - if (!hasLobe(LobeType::SpecularReflection)) return false; - - // Sample the GGX distribution to find a microfacet normal (half vector). -#if EnableVNDFSampling - float3 h = sampleGGX_VNDF(alpha, wi, sampleNext2D(sg), pdf); // pdf = G1(wi) * D(h) * max(0,dot(wi,h)) / wi.z -#else - float3 h = sampleGGX_NDF(alpha, sampleNext2D(sg), pdf); // pdf = D(h) * h.z -#endif - - // Reflect the incident direction to find the outgoing direction. - float wiDotH = dot(wi, h); - wo = 2.f * wiDotH * h - wi; - if (wo.z < kMinCosTheta) return false; - -#if ExplicitSampleWeights - // For testing. - pdf = evalPdf(wi, wo); - weight = eval(wi, wo, sg) / pdf; - lobeType = (uint)LobeType::SpecularReflection; - return true; -#endif - -#if SpecularMaskingFunction == SpecularMaskingFunctionSmithGGXSeparable - float G = evalMaskingSmithGGXSeparable(alpha, wi.z, wo.z); - float GOverG1wo = evalG1GGX(alpha * alpha, wo.z); -#elif SpecularMaskingFunction == SpecularMaskingFunctionSmithGGXCorrelated - float G = evalMaskingSmithGGXCorrelated(alpha, wi.z, wo.z); - float GOverG1wo = G * (1.f + evalLambdaGGX(alpha * alpha, wi.z)); -#endif - float3 F = evalFresnelSchlick(albedo, 1.f, wiDotH); - - pdf /= (4.f * wiDotH); // Jacobian of the reflection operator. -#if EnableVNDFSampling - weight = F * GOverG1wo; -#else - weight = F * G * wiDotH / (wi.z * h.z); -#endif - lobeType = (uint)LobeType::SpecularReflection; - return true; - } - - float evalPdf(const float3 wi, const float3 wo) - { - if (min(wi.z, wo.z) < kMinCosTheta) return 0.f; - -#if EnableDeltaBSDF - // Handle delta reflection. - if (alpha == 0.f) return 0.f; -#endif - - if (!hasLobe(LobeType::SpecularReflection)) return 0.f; - - float3 h = normalize(wi + wo); - float wiDotH = dot(wi, h); -#if EnableVNDFSampling - float pdf = evalPdfGGX_VNDF(alpha, wi, h); -#else - float pdf = evalPdfGGX_NDF(alpha, h.z); -#endif - return pdf / (4.f * wiDotH); - } - - AlbedoContributions evalAlbedo(const float3 wi, const LobeType lobetype) - { - float3 r = evalFresnelSchlick(albedo, 1.f, wi.z); - // The energy needs to go somewhere. This is a reflection-only BSDF --> put it into R - return AlbedoContributions(r, 1.f - r, 0.f, 0.f); - } - - RoughnessInformation getRoughnessInformation(const float3 wi) - { - RoughnessInformation r; - r.roughnessBSDFNotation = float2(alpha); - return r; - } -}; - -/** Specular reflection and transmission using microfacets. -*/ -struct SpecularReflectionTransmissionMicrofacet : IBSDF -{ - float3 transmissionAlbedo; ///< Transmission albedo. - float alpha; ///< GGX width parameter. - float eta; ///< Relative index of refraction (etaI / etaT). - uint activeLobes; ///< BSDF lobes to include for sampling and evaluation. See LobeType.slang. - - bool hasLobe(LobeType lobeType) { return (activeLobes & (uint)lobeType) != 0; } - - float3 eval(const float3 wi, const float3 wo, inout S sg) - { - if (min(wi.z, abs(wo.z)) < kMinCosTheta) return float3(0.f); - -#if EnableDeltaBSDF - // Handle delta reflection/transmission. - if (alpha == 0.f) return float3(0.f); -#endif - - const bool hasReflection = hasLobe(LobeType::SpecularReflection); - const bool hasTransmission = hasLobe(LobeType::SpecularTransmission); - const bool isReflection = wo.z > 0.f; - if ((isReflection && !hasReflection) || (!isReflection && !hasTransmission)) return float3(0.f); - - // Compute half-vector and make sure it's in the upper hemisphere. - float3 h = normalize(wo + wi * (isReflection ? 1.f : eta)); - h *= float(sign(h.z)); - - float wiDotH = dot(wi, h); - float woDotH = dot(wo, h); - - float D = evalNdfGGX(alpha, h.z); -#if SpecularMaskingFunction == SpecularMaskingFunctionSmithGGXSeparable - float G = evalMaskingSmithGGXSeparable(alpha, wi.z, abs(wo.z)); -#elif SpecularMaskingFunction == SpecularMaskingFunctionSmithGGXCorrelated - float G = evalMaskingSmithGGXCorrelated(alpha, wi.z, abs(wo.z)); -#endif - float F = evalFresnelDielectric(eta, wiDotH); - - if (isReflection) - { - return F * D * G * 0.25f / wi.z; - } - else - { - float sqrtDenom = woDotH + eta * wiDotH; - float t = eta * eta * wiDotH * woDotH / (wi.z * sqrtDenom * sqrtDenom); - return transmissionAlbedo * (1.f - F) * D * G * abs(t); - } - } - - bool sample(const float3 wi, out float3 wo, out float pdf, out float3 weight, out uint lobeType, inout S sg) - { - // Default initialization to avoid divergence at returns. - wo = {}; - weight = {}; - pdf = 0.f; - lobeType = (uint)LobeType::SpecularReflection; - - if (wi.z < kMinCosTheta) return false; - - // Get a random number to decide what lobe to sample. - float lobeSample = sampleNext1D(sg); - -#if EnableDeltaBSDF - // Handle delta reflection/transmission. - if (alpha == 0.f) - { - const bool hasReflection = hasLobe(LobeType::DeltaReflection); - const bool hasTransmission = hasLobe(LobeType::DeltaTransmission); - if (!(hasReflection || hasTransmission)) return false; - - float cosThetaT; - float F = evalFresnelDielectric(eta, wi.z, cosThetaT); - - bool isReflection = hasReflection; - if (hasReflection && hasTransmission) - { - isReflection = lobeSample < F; - } - else if (hasTransmission && F == 1.f) - { - return false; - } - - pdf = 0.f; - weight = isReflection ? float3(1.f) : transmissionAlbedo; - if (!(hasReflection && hasTransmission)) weight *= float3(isReflection ? F : 1.f - F); - wo = isReflection ? float3(-wi.x, -wi.y, wi.z) : float3(-wi.x * eta, -wi.y * eta, -cosThetaT); - lobeType = isReflection ? (uint)LobeType::DeltaReflection : (uint)LobeType::DeltaTransmission; - - if (abs(wo.z) < kMinCosTheta || (wo.z > 0.f != isReflection)) return false; - - return true; - } -#endif - - const bool hasReflection = hasLobe(LobeType::SpecularReflection); - const bool hasTransmission = hasLobe(LobeType::SpecularTransmission); - if (!(hasReflection || hasTransmission)) return false; - - // Sample the GGX distribution of (visible) normals. This is our half vector. -#if EnableVNDFSampling - float3 h = sampleGGX_VNDF(alpha, wi, sampleNext2D(sg), pdf); // pdf = G1(wi) * D(h) * max(0,dot(wi,h)) / wi.z -#else - float3 h = sampleGGX_NDF(alpha, sampleNext2D(sg), pdf); // pdf = D(h) * h.z -#endif - - // Reflect/refract the incident direction to find the outgoing direction. - float wiDotH = dot(wi, h); - - float cosThetaT; - float F = evalFresnelDielectric(eta, wiDotH, cosThetaT); - - bool isReflection = hasReflection; - if (hasReflection && hasTransmission) - { - isReflection = lobeSample < F; - } - else if (hasTransmission && F == 1.f) - { - return false; - } - - wo = isReflection ? - (2.f * wiDotH * h - wi) : - ((eta * wiDotH - cosThetaT) * h - eta * wi); - - if (abs(wo.z) < kMinCosTheta || (wo.z > 0.f != isReflection)) return false; - - float woDotH = dot(wo, h); - - lobeType = isReflection ? (uint)LobeType::SpecularReflection : (uint)LobeType::SpecularTransmission; - -#if ExplicitSampleWeights - // For testing. - pdf = evalPdf(wi, wo); - weight = pdf > 0.f ? eval(wi, wo, sg) / pdf : float3(0.f); - return true; -#endif - -#if SpecularMaskingFunction == SpecularMaskingFunctionSmithGGXSeparable - float G = evalMaskingSmithGGXSeparable(alpha, wi.z, abs(wo.z)); - float GOverG1wo = evalG1GGX(alpha * alpha, abs(wo.z)); -#elif SpecularMaskingFunction == SpecularMaskingFunctionSmithGGXCorrelated - float G = evalMaskingSmithGGXCorrelated(alpha, wi.z, abs(wo.z)); - float GOverG1wo = G * (1.f + evalLambdaGGX(alpha * alpha, wi.z)); -#endif - -#if EnableVNDFSampling - weight = GOverG1wo; -#else - weight = G * wiDotH / (wi.z * h.z); -#endif - - if (isReflection) - { - pdf /= 4.f * woDotH; // Jacobian of the reflection operator. - } - else - { - float sqrtDenom = woDotH + eta * wiDotH; - float denom = sqrtDenom * sqrtDenom; - pdf = (denom > 0.f) ? pdf * abs(woDotH) / denom : FLT_MAX; // Jacobian of the refraction operator. - weight *= transmissionAlbedo * eta * eta; - } - - if (hasReflection && hasTransmission) - { - pdf *= isReflection ? F : 1.f - F; - } - else - { - weight *= isReflection ? F : 1.f - F; - } - - return true; - } - - float evalPdf(const float3 wi, const float3 wo) - { - if (min(wi.z, abs(wo.z)) < kMinCosTheta) return 0.f; - -#if EnableDeltaBSDF - // Handle delta reflection/transmission. - if (alpha == 0.f) return 0.f; -#endif - - bool isReflection = wo.z > 0.f; - const bool hasReflection = hasLobe(LobeType::SpecularReflection); - const bool hasTransmission = hasLobe(LobeType::SpecularTransmission); - if ((isReflection && !hasReflection) || (!isReflection && !hasTransmission)) return 0.f; - - // Compute half-vector and make sure it's in the upper hemisphere. - float3 h = normalize(wo + wi * (isReflection ? 1.f : eta)); - h *= float(sign(h.z)); - - float wiDotH = dot(wi, h); - float woDotH = dot(wo, h); - - float F = evalFresnelDielectric(eta, wiDotH); - -#if EnableVNDFSampling - float pdf = evalPdfGGX_VNDF(alpha, wi, h); -#else - float pdf = evalPdfGGX_NDF(alpha, h.z); -#endif - if (isReflection) - { - pdf /= 4.f * woDotH; // Jacobian of the reflection operator. - } - else - { - if (woDotH > 0.f) return 0.f; - float sqrtDenom = woDotH + eta * wiDotH; - float denom = sqrtDenom * sqrtDenom; - pdf = (denom > 0.f) ? pdf * abs(woDotH) / denom : FLT_MAX; // Jacobian of the refraction operator. - } - - if (hasReflection && hasTransmission) - { - pdf *= isReflection ? F : 1.f - F; - } - - return pdf; - } - - AlbedoContributions evalAlbedo(const float3 wi, const LobeType lobetype) - { - float3 r = evalFresnelDielectric(eta, wi.z); - // The energy needs to go somewhere. This is a transmission-only BSDF --> put it into AT - return AlbedoContributions(0.f, 0.f, 1.f - r, r); - } - - RoughnessInformation getRoughnessInformation(const float3 wi) - { - RoughnessInformation r; - r.roughnessBSDFNotation = float2(alpha); - return r; - } -}; - -// TODO: Reduce to 52B -/** BSDF parameters for the standard MaterialInstance. - These are needed for initializing a `StandardBSDF` instance. -*/ -struct StandardBSDFData -{ - float3 diffuse; ///< Diffuse albedo. - float3 specular; ///< Specular albedo. - float roughness; ///< This is the original roughness, before remapping. - float metallic; ///< Metallic parameter, blends between dielectric and conducting BSDFs. - float eta; ///< Relative index of refraction (incident IoR / transmissive IoR). - float3 transmission; ///< Transmission color. - float diffuseTransmission; ///< Diffuse transmission, blends between diffuse reflection and transmission lobes. - float specularTransmission; ///< Specular transmission, blends between opaque dielectric BRDF and specular transmissive BSDF. -} - -/** Mixed BSDF used for the standard material in Falcor. - - This consists of a diffuse and specular BRDF. - A specular BSDF is mixed in using the specularTransmission parameter. -*/ -struct StandardBSDF : IBSDF -{ -#if DiffuseBrdf == DiffuseBrdfLambert - DiffuseReflectionLambert diffuseReflection; -#elif DiffuseBrdf == DiffuseBrdfDisney - DiffuseReflectionDisney diffuseReflection; -#elif DiffuseBrdf == DiffuseBrdfFrostbite - DiffuseReflectionFrostbite diffuseReflection; -#endif - DiffuseTransmissionLambert diffuseTransmission; - - SpecularReflectionMicrofacet specularReflection; - SpecularReflectionTransmissionMicrofacet specularReflectionTransmission; - - float diffTrans; ///< Mix between diffuse BRDF and diffuse BTDF. - float specTrans; ///< Mix between dielectric BRDF and specular BSDF. - - float pDiffuseReflection; ///< Probability for sampling the diffuse BRDF. - float pDiffuseTransmission; ///< Probability for sampling the diffuse BTDF. - float pSpecularReflection; ///< Probability for sampling the specular BRDF. - float pSpecularReflectionTransmission; ///< Probability for sampling the specular BSDF. - - /** Initialize a new instance. - \param[in] wi Incident direction in the local frame. - \param[in] mtl Material header. - \param[in] data BSDF parameters. - */ - __init(const float3 wi, const MaterialHeader mtl, const StandardBSDFData data) - { - // TODO: Currently specular reflection and transmission lobes are not properly separated. - // This leads to incorrect behaviour if only the specular reflection or transmission lobe is selected. - // Things work fine as long as both or none are selected. - - // Use square root if we can assume the shaded object is intersected twice. - float3 transmissionAlbedo = mtl.isThinSurface() ? data.transmission : sqrt(data.transmission); - - // Setup lobes. - diffuseReflection.albedo = data.diffuse; -#if DiffuseBrdf != DiffuseBrdfLambert - diffuseReflection.roughness = data.roughness; -#endif - diffuseTransmission.albedo = transmissionAlbedo; - - // Compute GGX alpha. - float alpha = data.roughness * data.roughness; -#if EnableDeltaBSDF - // Alpha below min alpha value means using delta reflection/transmission. - if (alpha < kMinGGXAlpha) alpha = 0.f; -#else - alpha = max(alpha, kMinGGXAlpha); -#endif - const uint activeLobes = mtl.getActiveLobes(); - - specularReflection.albedo = data.specular; - specularReflection.alpha = alpha; - specularReflection.activeLobes = activeLobes; - - specularReflectionTransmission.transmissionAlbedo = transmissionAlbedo; - // Transmission through rough interface with same IoR on both sides is not well defined, switch to delta lobe instead. - specularReflectionTransmission.alpha = data.eta == 1.f ? 0.f : alpha; - specularReflectionTransmission.eta = data.eta; - specularReflectionTransmission.activeLobes = activeLobes; - - diffTrans = data.diffuseTransmission; - specTrans = data.specularTransmission; - - // Compute sampling weights. - float metallicBRDF = data.metallic * (1.f - specTrans); - float dielectricBSDF = (1.f - data.metallic) * (1.f - specTrans); - float specularBSDF = specTrans; - - float diffuseWeight = luminance(data.diffuse); - float specularWeight = luminance(evalFresnelSchlick(data.specular, 1.f, wi.z)); - - pDiffuseReflection = (activeLobes & (uint)LobeType::DiffuseReflection) ? diffuseWeight * dielectricBSDF * (1.f - diffTrans) : 0.f; - pDiffuseTransmission = (activeLobes & (uint)LobeType::DiffuseTransmission) ? diffuseWeight * dielectricBSDF * diffTrans : 0.f; - pSpecularReflection = (activeLobes & ((uint)LobeType::SpecularReflection | (uint)LobeType::DeltaReflection)) ? specularWeight * (metallicBRDF + dielectricBSDF) : 0.f; - pSpecularReflectionTransmission = (activeLobes & ((uint)LobeType::SpecularReflection | (uint)LobeType::DeltaReflection | (uint)LobeType::SpecularTransmission | (uint)LobeType::DeltaTransmission)) ? specularBSDF : 0.f; - - float normFactor = pDiffuseReflection + pDiffuseTransmission + pSpecularReflection + pSpecularReflectionTransmission; - if (normFactor > 0.f) - { - normFactor = 1.f / normFactor; - pDiffuseReflection *= normFactor; - pDiffuseTransmission *= normFactor; - pSpecularReflection *= normFactor; - pSpecularReflectionTransmission *= normFactor; - } - } - - /** Returns the set of BSDF lobes. - \param[in] data BSDF parameters. - \return Returns a set of lobes (see LobeType.slang). - */ - static uint getLobeTypes(const StandardBSDFData data) - { -#if EnableDeltaBSDF - float alpha = data.roughness * data.roughness; - bool isDelta = alpha < kMinGGXAlpha; -#else - bool isDelta = false; -#endif - float diffTrans = data.diffuseTransmission; - float specTrans = data.specularTransmission; - - uint lobeTypes = isDelta ? (uint)LobeType::DeltaReflection : (uint)LobeType::SpecularReflection; - if (any(data.diffuse > 0.f) && specTrans < 1.f) - { - if (diffTrans < 1.f) lobeTypes |= (uint)LobeType::DiffuseReflection; - if (diffTrans > 0.f) lobeTypes |= (uint)LobeType::DiffuseTransmission; - } - if (specTrans > 0.f) lobeTypes |= (isDelta ? (uint)LobeType::DeltaTransmission : (uint)LobeType::SpecularTransmission); - - return lobeTypes; - } - - float3 eval(const float3 wi, const float3 wo, inout S sg) - { - float3 result = 0.f; - if (pDiffuseReflection > 0.f) result += (1.f - specTrans) * (1.f - diffTrans) * diffuseReflection.eval(wi, wo, sg); - if (pDiffuseTransmission > 0.f) result += (1.f - specTrans) * diffTrans * diffuseTransmission.eval(wi, wo, sg); - if (pSpecularReflection > 0.f) result += (1.f - specTrans) * specularReflection.eval(wi, wo, sg); - if (pSpecularReflectionTransmission > 0.f) result += specTrans * (specularReflectionTransmission.eval(wi, wo, sg)); - return result; - } - - bool sample(const float3 wi, out float3 wo, out float pdf, out float3 weight, out uint lobeType, inout S sg) - { - // Default initialization to avoid divergence at returns. - wo = {}; - weight = {}; - pdf = 0.f; - lobeType = (uint)LobeType::DiffuseReflection; - - bool valid = false; - float uSelect = sampleNext1D(sg); - - // Note: The commented-out pdf contributions below are always zero, so no need to compute them. - - if (uSelect < pDiffuseReflection) - { - valid = diffuseReflection.sample(wi, wo, pdf, weight, lobeType, sg); - weight /= pDiffuseReflection; - weight *= (1.f - specTrans) * (1.f - diffTrans); - pdf *= pDiffuseReflection; - // if (pDiffuseTransmission > 0.f) pdf += pDiffuseTransmission * diffuseTransmission.evalPdf(wi, wo); - if (pSpecularReflection > 0.f) pdf += pSpecularReflection * specularReflection.evalPdf(wi, wo); - if (pSpecularReflectionTransmission > 0.f) pdf += pSpecularReflectionTransmission * specularReflectionTransmission.evalPdf(wi, wo); - } - else if (uSelect < pDiffuseReflection + pDiffuseTransmission) - { - valid = diffuseTransmission.sample(wi, wo, pdf, weight, lobeType, sg); - weight /= pDiffuseTransmission; - weight *= (1.f - specTrans) * diffTrans; - pdf *= pDiffuseTransmission; - // if (pDiffuseReflection > 0.f) pdf += pDiffuseReflection * diffuseReflection.evalPdf(wi, wo); - // if (pSpecularReflection > 0.f) pdf += pSpecularReflection * specularReflection.evalPdf(wi, wo); - if (pSpecularReflectionTransmission > 0.f) pdf += pSpecularReflectionTransmission * specularReflectionTransmission.evalPdf(wi, wo); - } - else if (uSelect < pDiffuseReflection + pDiffuseTransmission + pSpecularReflection) - { - valid = specularReflection.sample(wi, wo, pdf, weight, lobeType, sg); - weight /= pSpecularReflection; - weight *= (1.f - specTrans); - pdf *= pSpecularReflection; - if (pDiffuseReflection > 0.f) pdf += pDiffuseReflection * diffuseReflection.evalPdf(wi, wo); - // if (pDiffuseTransmission > 0.f) pdf += pDiffuseTransmission * diffuseTransmission.evalPdf(wi, wo); - if (pSpecularReflectionTransmission > 0.f) pdf += pSpecularReflectionTransmission * specularReflectionTransmission.evalPdf(wi, wo); - } - else if (pSpecularReflectionTransmission > 0.f) - { - valid = specularReflectionTransmission.sample(wi, wo, pdf, weight, lobeType, sg); - weight /= pSpecularReflectionTransmission; - weight *= specTrans; - pdf *= pSpecularReflectionTransmission; - if (pDiffuseReflection > 0.f) pdf += pDiffuseReflection * diffuseReflection.evalPdf(wi, wo); - if (pDiffuseTransmission > 0.f) pdf += pDiffuseTransmission * diffuseTransmission.evalPdf(wi, wo); - if (pSpecularReflection > 0.f) pdf += pSpecularReflection * specularReflection.evalPdf(wi, wo); - } - - return valid; - } - - float evalPdf(const float3 wi, const float3 wo) - { - float pdf = 0.f; - if (pDiffuseReflection > 0.f) pdf += pDiffuseReflection * diffuseReflection.evalPdf(wi, wo); - if (pDiffuseTransmission > 0.f) pdf += pDiffuseTransmission * diffuseTransmission.evalPdf(wi, wo); - if (pSpecularReflection > 0.f) pdf += pSpecularReflection * specularReflection.evalPdf(wi, wo); - if (pSpecularReflectionTransmission > 0.f) pdf += pSpecularReflectionTransmission * specularReflectionTransmission.evalPdf(wi, wo); - return pdf; - } - - AlbedoContributions evalAlbedo(const float3 wi, const LobeType lobetype) - { - AlbedoContributions a = { 0.0f, 0.0f, 0.0f, 0.0f }; - - if (pDiffuseReflection > 0.f) a += (1.f - specTrans) * (1.f - diffTrans) * diffuseReflection.evalAlbedo(wi, lobetype); - if (pDiffuseTransmission > 0.f) a += (1.f - specTrans) * diffTrans * diffuseTransmission.evalAlbedo(wi, lobetype); - if (pSpecularReflection > 0.f) a += (1.f - specTrans) * specularReflection.evalAlbedo(wi, lobetype); - if (pSpecularReflectionTransmission > 0.f) a += specTrans * specularReflectionTransmission.evalAlbedo(wi, lobetype); - - return a; - } - - RoughnessInformation getRoughnessInformation(const float3 wi) - { - RoughnessInformation r; - float alpha = specularReflection.alpha; - r.roughnessBSDFNotation = float2(alpha < kMinGGXAlpha ? 0.f : alpha); - return r; - } -}; diff --git a/Source/Falcor/Rendering/Materials/ClothMaterial.slang b/Source/Falcor/Rendering/Materials/ClothMaterial.slang index 13e420a70..2ae8ef85e 100644 --- a/Source/Falcor/Rendering/Materials/ClothMaterial.slang +++ b/Source/Falcor/Rendering/Materials/ClothMaterial.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -69,18 +69,21 @@ struct ClothMaterial : MaterialBase, IMaterial } // Compute final shading frame. - ShadingFrame sf = ShadingFrame(sd); + ShadingFrame sf = sd.frame; + if (isNormalMappingEnabled(hints)) + { + float3 encodedNormal = ms.sampleTexture(data.texNormalMap, s, sd.uv, float4(0.f), lod).rgb; + sf = computeShadingFrameFromNormalMap(sd, data.getNormalMapType(), encodedNormal); + } + flipShadingNormal(sd, sf); + if (isAdjustShadingNormalEnabled(hints)) + { + adjustShadingNormal(sd, sf); + } return ClothMaterialInstance(sf, brdf); } - void modifyTangentSpace(const MaterialSystem ms, const ITextureSampler lod, inout ShadingData sd) - { - SamplerState s = ms.getTextureSampler(header.getDefaultTextureSamplerID()); - float3 encodedNormal = ms.sampleTexture(data.texNormalMap, s, sd.uv, float4(0.f), lod).rgb; - applyNormalMap(sd, data.getNormalMapType(), encodedNormal); - } - float evalOpacity(const MaterialSystem ms, const VertexData v, const ITextureSampler lod) { SamplerState s = ms.getTextureSampler(header.getDefaultTextureSamplerID()); diff --git a/Source/Falcor/Rendering/Materials/ClothMaterialInstance.slang b/Source/Falcor/Rendering/Materials/ClothMaterialInstance.slang index 5913af5d6..71cf1775d 100644 --- a/Source/Falcor/Rendering/Materials/ClothMaterialInstance.slang +++ b/Source/Falcor/Rendering/Materials/ClothMaterialInstance.slang @@ -49,6 +49,8 @@ struct ClothMaterialInstance : IMaterialInstance float3 wiLocal = sf.toLocal(sd.V); float3 woLocal = sf.toLocal(wo); + if (!isValidHemisphereReflection(sd, sf, wiLocal, woLocal, wo)) return float3(0.f); + return brdf.eval(wiLocal, woLocal, sg); } @@ -98,10 +100,10 @@ struct ClothMaterialInstance : IMaterialInstance { float3 wiLocal = sf.toLocal(sd.V); float3 woLocal = sample_cosine_hemisphere_concentric(sampleNext2D(sg), result.pdf); // pdf = cos(theta) / pi + result.wo = sf.fromLocal(woLocal); - if (min(wiLocal.z, woLocal.z) < kMinCosTheta || result.pdf == 0.f) return false; + if (!isValidHemisphereReflection(sd, sf, wiLocal, woLocal, result.wo) || result.pdf == 0.f) return false; - result.wo = sf.fromLocal(woLocal); result.weight = brdf.eval(wiLocal, woLocal, sg) / result.pdf; result.lobeType = (uint)LobeType::DiffuseReflection; @@ -118,7 +120,7 @@ struct ClothMaterialInstance : IMaterialInstance float3 wiLocal = sf.toLocal(sd.V); float3 woLocal = sf.toLocal(wo); - if (min(wiLocal.z, woLocal.z) < kMinCosTheta) return 0.f; + if (!isValidHemisphereReflection(sd, sf, wiLocal, woLocal, wo)) return 0.f; return woLocal.z * M_1_PI; // pdf = cos(theta) / pi } diff --git a/Source/Falcor/Rendering/Materials/HairChiang16.slang b/Source/Falcor/Rendering/Materials/HairChiang16.slang index e544ecd14..f5a4d047b 100644 --- a/Source/Falcor/Rendering/Materials/HairChiang16.slang +++ b/Source/Falcor/Rendering/Materials/HairChiang16.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -110,9 +110,9 @@ struct HairChiang16 : IBSDF eta = data.eta; // Compute offset h azimuthally with the unit circle cross section. - float3 wiProj = normalize(sd.V - dot(sd.V, sd.T) * sd.T); // Project wi to the (B, N) plane. - float3 wiProjPerp = cross(wiProj, sd.T); - h = dot(sd.N, wiProjPerp); + float3 wiProj = normalize(sd.V - dot(sd.V, sd.frame.T) * sd.frame.T); // Project wi to the (B, N) plane. + float3 wiProjPerp = cross(wiProj, sd.frame.T); + h = dot(sd.frame.N, wiProjPerp); precompute(); } diff --git a/Source/Falcor/Rendering/Materials/HairMaterial.slang b/Source/Falcor/Rendering/Materials/HairMaterial.slang index 9a737c35e..9b2950081 100644 --- a/Source/Falcor/Rendering/Materials/HairMaterial.slang +++ b/Source/Falcor/Rendering/Materials/HairMaterial.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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,11 +58,16 @@ struct HairMaterial : MaterialBase, IMaterial d.betaN = spec.y; d.alpha = spec.z; - return HairMaterialInstance(d); - } + // Compute final shading frame. + // Do not flip the shading normal for backfacing hits. The BSDF handles transmission from both sides. + ShadingFrame sf = sd.frame; + if (isAdjustShadingNormalEnabled(hints)) + { + adjustShadingNormal(sd, sf); + } - // Normal mapping is not supported by this material. - void modifyTangentSpace(const MaterialSystem ms, const ITextureSampler lod, inout ShadingData sd) {} + return HairMaterialInstance(sf, d); + } // Alpha testing is not supported by this material. float evalOpacity(const MaterialSystem ms, const VertexData v, const ITextureSampler lod) { return 1.f; } diff --git a/Source/Falcor/Rendering/Materials/HairMaterialInstance.slang b/Source/Falcor/Rendering/Materials/HairMaterialInstance.slang index 19eb0f26d..7f1247b73 100644 --- a/Source/Falcor/Rendering/Materials/HairMaterialInstance.slang +++ b/Source/Falcor/Rendering/Materials/HairMaterialInstance.slang @@ -42,44 +42,46 @@ import Utils.Math.MathHelpers; */ struct HairMaterialInstance : IMaterialInstance { + ShadingFrame sf; ///< Shading frame in world space. HairChiang16Data data; - __init(const HairChiang16Data data) + __init(const ShadingFrame sf, const HairChiang16Data data) { + this.sf = sf; this.data = data; } float3 eval(const ShadingData sd, const float3 wo, inout S sg) { #if BCSDF == HairChiang - float3 wiLocal = sd.toLocal(sd.V); - float3 woLocal = sd.toLocal(wo); + float3 wiLocal = sf.toLocal(sd.V); + float3 woLocal = sf.toLocal(wo); HairChiang16 bcsdf = HairChiang16(sd, data); return bcsdf.eval(wiLocal, woLocal, sg); #else // Diffuse BRDF - return data.baseColor * (float)M_1_PI * saturate(dot(sd.N, wo)); + return data.baseColor * (float)M_1_PI * saturate(dot(sf.N, wo)); #endif } bool sample(const ShadingData sd, inout S sg, out BSDFSample result, bool useImportanceSampling = true) { #if BCSDF == HairChiang - float3 wiLocal = sd.toLocal(sd.V); + float3 wiLocal = sf.toLocal(sd.V); float3 woLocal = {}; HairChiang16 bcsdf = HairChiang16(sd, data); bool valid = bcsdf.sample(wiLocal, woLocal, result.pdf, result.weight, result.lobeType, sg); - result.wo = sd.fromLocal(woLocal); + result.wo = sf.fromLocal(woLocal); return valid; #else // Diffuse BRDF float3 woLocal = sample_cosine_hemisphere_concentric(sampleNext2D(sg), result.pdf); - result.wo = sd.fromLocal(woLocal); + result.wo = sf.fromLocal(woLocal); result.weight = data.baseColor * (float)M_1_PI; result.lobeType = (uint)LobeType::DiffuseReflection; return true; @@ -89,15 +91,15 @@ struct HairMaterialInstance : IMaterialInstance float evalPdf(const ShadingData sd, const float3 wo, bool useImportanceSampling = true) { #if BCSDF == HairChiang - float3 wiLocal = sd.toLocal(sd.V); - float3 woLocal = sd.toLocal(wo); + float3 wiLocal = sf.toLocal(sd.V); + float3 woLocal = sf.toLocal(wo); HairChiang16 bcsdf = HairChiang16(sd, data); return bcsdf.evalPdf(wiLocal, woLocal); #else // Diffuse BRDF - return saturate(dot(sd.N, wo)); + return saturate(dot(sf.N, wo)); #endif } @@ -105,7 +107,7 @@ struct HairMaterialInstance : IMaterialInstance { BSDFProperties p = {}; - p.guideNormal = sd.N; + p.guideNormal = sf.N; // Compute approximation of the perceived roughness. // It's an open problem how to best do this. For now assume a medium roughness. diff --git a/Source/Falcor/Rendering/Materials/IBSDF.slang b/Source/Falcor/Rendering/Materials/IBSDF.slang index f55ab1a4b..d4be2195f 100644 --- a/Source/Falcor/Rendering/Materials/IBSDF.slang +++ b/Source/Falcor/Rendering/Materials/IBSDF.slang @@ -28,10 +28,10 @@ __exported import Rendering.Materials.LobeType; __exported import Utils.Sampling.SampleGeneratorInterface; -// Minimum cos(theta) for the incident and outgoing vectors. -// Some BSDF functions are not robust for cos(theta) == 0.0, -// so using a small epsilon for consistency. -// TODO: Move into IBSDF if possible +/// Minimum cos(theta) for the incident and outgoing vectors. +/// Some BSDF functions are not robust for cos(theta) == 0.0, +/// so using a small epsilon for consistency. +/// TODO: Move into IBSDF if possible static const float kMinCosTheta = 1e-6f; /** Low-level interface for BSDF functions. @@ -47,7 +47,6 @@ static const float kMinCosTheta = 1e-6f; /** Albedo is split into four components. They have to sum up to 1.0. */ - struct AlbedoContributions { float3 reflection; ///< Energy reflected into upper hemisphere @@ -135,7 +134,7 @@ interface IBSDF */ bool sample(const float3 wi, out float3 wo, out float pdf, out float3 weight, out uint lobeType, inout S sg); - /** Evaluates the BSSDF directional pdf for sampling outgoing direction wo. + /** Evaluates the directional pdf for sampling outgoing direction wo. \param[in] wi Incident direction. \param[in] wo Outgoing direction. \return Returns the pdf with respect to solid angle for sampling outgoing direction wo (0 for delta events). @@ -143,15 +142,15 @@ interface IBSDF float evalPdf(const float3 wi, const float3 wo); /** Albedo (hemispherical reflectance) of the BSDF. Relfection+transmission hemisphere should be <= 1.0. - \param[in] wi Incident direction. - \param[in] lobetype lobe types to be evaluated - \return Returns the albedo. + \param[in] wi Incident direction. + \param[in] lobetype lobe types to be evaluated + \return Returns the albedo. */ AlbedoContributions evalAlbedo(const float3 wi, const LobeType lobetype); /** Information about roughness of the BSDF in (various) forms. - \param[in] wi Incident direction. - \return Returns the roughness. + \param[in] wi Incident direction. + \return Returns the roughness. */ RoughnessInformation getRoughnessInformation(const float3 wi); } diff --git a/Source/Falcor/Rendering/Materials/IMaterial.slang b/Source/Falcor/Rendering/Materials/IMaterial.slang index 209acb68f..74e8e35aa 100644 --- a/Source/Falcor/Rendering/Materials/IMaterial.slang +++ b/Source/Falcor/Rendering/Materials/IMaterial.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -65,16 +65,6 @@ interface IMaterial */ MaterialInstance setupMaterialInstance(const MaterialSystem ms, const ShadingData sd, const ITextureSampler lod, const uint hints = (uint)MaterialInstanceHints::None); - /** Modify the tangent space at the given shading point. - This function is called by the material system when preparing the `ShadingData` struct. - It allows materials to implement normal mapping and other techniques that modify the local surface geometry. - Materials that do not need to modify the default tangent frame should leave the shading data unchanged. - \param[in] ms Material system. This provides the resources for pattern generation. - \param[in] lod Method to use for computing texture level of detail, must implement the `ITextureSampler` interface. - \param[in,out] sd Shading data. - */ - void modifyTangentSpace(const MaterialSystem ms, const ITextureSampler lod, inout ShadingData sd); - /** Evaluate opacity at the given shading point. \param[in] ms Material system. This provides the resources for opacity evaluation. \param[in] v Interpolated attributes at the shading point. diff --git a/Source/Falcor/Rendering/Materials/IMaterialInstance.slang b/Source/Falcor/Rendering/Materials/IMaterialInstance.slang index f6b70e3d0..d4c9d3995 100644 --- a/Source/Falcor/Rendering/Materials/IMaterialInstance.slang +++ b/Source/Falcor/Rendering/Materials/IMaterialInstance.slang @@ -25,9 +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. **************************************************************************/ -__exported import Rendering.Materials.LobeType; -__exported import Utils.Sampling.SampleGeneratorInterface; -__exported import Rendering.Materials.BxDF; // For StandardBSDFData #include "Utils/HostDeviceShared.slangh" // For bit packing macros __exported import Scene.ShadingData; @@ -41,8 +38,20 @@ __exported import Utils.Sampling.SampleGeneratorInterface; enum MaterialInstanceHints : uint { None = 0, + DisableNormalMapping = 0x1, ///< Turn off normal mapping if supported. + AdjustShadingNormal = 0x2, ///< Adjust shading normal to reduce risk of back-facing hits. }; +bool isNormalMappingEnabled(uint hints) +{ + return !(hints & (uint)MaterialInstanceHints::DisableNormalMapping); +} + +bool isAdjustShadingNormalEnabled(uint hints) +{ + return hints & (uint)MaterialInstanceHints::AdjustShadingNormal; +} + /** Describes a BSDF sample. */ struct BSDFSample @@ -139,10 +148,6 @@ interface IMaterialInstance \param[in,out] sg Sample generator. \return Returns f(wi, wo) * dot(wo, n). */ - // Minimum cos(theta) for the incident and outgoing vectors. - // Some BSDF functions are not robust for cos(theta) == 0.0, - // so using a small epsilon for consistency. - // TODO: Move into IBSDF if possible float3 eval(const ShadingData sd, const float3 wo, inout S sg); /** Samples the material instance. diff --git a/Source/Falcor/Rendering/Materials/MERLMaterial.slang b/Source/Falcor/Rendering/Materials/MERLMaterial.slang index 7bbbe207d..6024c8ba2 100644 --- a/Source/Falcor/Rendering/Materials/MERLMaterial.slang +++ b/Source/Falcor/Rendering/Materials/MERLMaterial.slang @@ -41,7 +41,12 @@ struct MERLMaterial : MaterialBase, IMaterial MERLMaterialInstance setupMaterialInstance(const MaterialSystem ms, const ShadingData sd, const ITextureSampler lod, const uint hints) { // Compute final shading frame. - ShadingFrame sf = ShadingFrame(sd); + ShadingFrame sf = sd.frame; + flipShadingNormal(sd, sf); + if (isAdjustShadingNormalEnabled(hints)) + { + adjustShadingNormal(sd, sf); + } // Evaluate the albedo for the current incident (view) direction. float3 albedo = {}; @@ -62,9 +67,6 @@ struct MERLMaterial : MaterialBase, IMaterial return MERLMaterialInstance(sf, data.bufferID, albedo, data.extraData); } - // Normal mapping is not supported by this material. - void modifyTangentSpace(const MaterialSystem ms, const ITextureSampler lod, inout ShadingData sd) {} - // Alpha testing is not supported by this material. float evalOpacity(const MaterialSystem ms, const VertexData v, const ITextureSampler lod) { return 1.f; } }; diff --git a/Source/Falcor/Rendering/Materials/MERLMaterialInstance.slang b/Source/Falcor/Rendering/Materials/MERLMaterialInstance.slang index 9ccbf7096..d0feda30a 100644 --- a/Source/Falcor/Rendering/Materials/MERLMaterialInstance.slang +++ b/Source/Falcor/Rendering/Materials/MERLMaterialInstance.slang @@ -64,7 +64,7 @@ struct MERLMaterialInstance : IMaterialInstance float3 wiLocal = sf.toLocal(sd.V); float3 woLocal = sf.toLocal(wo); - if (min(wiLocal.z, woLocal.z) < kMinCosTheta) return float3(0.f); + if (!isValidHemisphereReflection(sd, sf, wiLocal, woLocal, wo)) return float3(0.f); #if VISUALIZE_FITTED_BRDF return fittedBrdf.eval(wiLocal, woLocal, sg); @@ -80,10 +80,10 @@ struct MERLMaterialInstance : IMaterialInstance #if SAMPLE_FITTED_BRDF float3 woLocal = {}; if (!fittedBrdf.sample(wiLocal, woLocal, result.pdf, result.weight, result.lobeType, sg)) return false; + result.wo = sf.fromLocal(woLocal); - if (min(wiLocal.z, woLocal.z) < kMinCosTheta || result.pdf == 0.f) return false; + if (!isValidHemisphereReflection(sd, sf, wiLocal, woLocal, result.wo) || result.pdf == 0.f) return false; - result.wo = sf.fromLocal(woLocal); #if !VISUALIZE_FITTED_BRDF result.weight = evalLocal(wiLocal, woLocal) / result.pdf; #endif @@ -92,10 +92,10 @@ struct MERLMaterialInstance : IMaterialInstance // Draw cosine-weighted sample over the hemisphere. // TODO: Implement better importance sampling. float3 woLocal = sample_cosine_hemisphere_concentric(sampleNext2D(sg), result.pdf); // pdf = cos(theta) / pi + result.wo = sf.fromLocal(woLocal); - if (min(wiLocal.z, woLocal.z) < kMinCosTheta || result.pdf == 0.f) return false; + if (!isValidHemisphereReflection(sd, sf, wiLocal, woLocal, result.wo) || result.pdf == 0.f) return false; - result.wo = sf.fromLocal(woLocal); #if VISUALIZE_FITTED_BRDF result.weight = fittedBrdf.eval(wiLocal, woLocal, sg) / result.pdf; #else @@ -112,7 +112,7 @@ struct MERLMaterialInstance : IMaterialInstance float3 wiLocal = sf.toLocal(sd.V); float3 woLocal = sf.toLocal(wo); - if (min(wiLocal.z, woLocal.z) < kMinCosTheta) return 0.f; + if (!isValidHemisphereReflection(sd, sf, wiLocal, woLocal, wo)) return 0.f; #if SAMPLE_FITTED_BRDF return fittedBrdf.evalPdf(wiLocal, woLocal); diff --git a/Source/Falcor/Rendering/Materials/MERLMixMaterial.slang b/Source/Falcor/Rendering/Materials/MERLMixMaterial.slang index feee3a78f..6f5d0ee88 100644 --- a/Source/Falcor/Rendering/Materials/MERLMixMaterial.slang +++ b/Source/Falcor/Rendering/Materials/MERLMixMaterial.slang @@ -44,14 +44,15 @@ struct MERLMixMaterial : MaterialBase, IMaterial let explicitLod = ExplicitLodTextureSampler(0.f); // Compute final shading frame. - // TODO: Both normal mapping and shading normal adjustment should be conditional based on the supplied hints. - // The hints are not setup by the renderer yet though, so leaving it always on. - ShadingFrame sf = ShadingFrame(sd); + ShadingFrame sf = sd.frame; + if (isNormalMappingEnabled(hints)) { SamplerState s = ms.getTextureSampler(header.getDefaultTextureSamplerID()); float3 encodedNormal = ms.sampleTexture(data.texNormalMap, s, sd.uv, float4(0.f), lod).rgb; sf = computeShadingFrameFromNormalMap(sd, data.getNormalMapType(), encodedNormal); } + flipShadingNormal(sd, sf); + if (isAdjustShadingNormalEnabled(hints)) { adjustShadingNormal(sd, sf); } @@ -92,9 +93,6 @@ struct MERLMixMaterial : MaterialBase, IMaterial return MERLMixMaterialInstance(sf, data.bufferID, byteOffset, albedo, brdfIndex, extraData); } - // Note: Normal mapping is applied inside setupMaterialInstance() to be consistent with MxLayeredMaterial. - void modifyTangentSpace(const MaterialSystem ms, const ITextureSampler lod, inout ShadingData sd) {} - // Alpha testing is not supported by this material. float evalOpacity(const MaterialSystem ms, const VertexData v, const ITextureSampler lod) { return 1.f; } }; diff --git a/Source/Falcor/Rendering/Materials/MERLMixMaterialInstance.slang b/Source/Falcor/Rendering/Materials/MERLMixMaterialInstance.slang index e577d271b..1783433a9 100644 --- a/Source/Falcor/Rendering/Materials/MERLMixMaterialInstance.slang +++ b/Source/Falcor/Rendering/Materials/MERLMixMaterialInstance.slang @@ -152,24 +152,23 @@ struct MERLMixMaterialInstance : IMaterialInstance // TODO: Is linear roughness or squared roughness (GGX alpha) best? float alpha = fittedBrdf.roughness * fittedBrdf.roughness; - // Express normal tangent, and bitangent in the smooth interpolated shading frame (sd). - ShadingFrame smoothFrame = ShadingFrame(sd); + // Express normal tangent, and bitangent in the smooth interpolated shading frame (sd.frame). ExtraBSDFProperties result = {}; result.bsdfCount = 2; { - result.bsdfT[0] = smoothFrame.toLocal(sf.T); - result.bsdfB[0] = smoothFrame.toLocal(sf.B); - result.bsdfN[0] = smoothFrame.toLocal(sf.N); + result.bsdfT[0] = sd.frame.toLocal(sf.T); + result.bsdfB[0] = sd.frame.toLocal(sf.B); + result.bsdfN[0] = sd.frame.toLocal(sf.N); result.bsdfAlbedo[0] = fittedBrdf.diffuse; result.bsdfWeight[0] = float3(diffuseWeight); result.bsdfRoughness[0] = float2(1.f); result.bsdfIndex[0] = float(brdfIndex); } { - result.bsdfT[1] = smoothFrame.toLocal(sf.T); - result.bsdfB[1] = smoothFrame.toLocal(sf.B); - result.bsdfN[1] = smoothFrame.toLocal(sf.N); + result.bsdfT[1] = sd.frame.toLocal(sf.T); + result.bsdfB[1] = sd.frame.toLocal(sf.B); + result.bsdfN[1] = sd.frame.toLocal(sf.N); result.bsdfAlbedo[1] = fittedBrdf.specular; result.bsdfWeight[1] = float3(specularWeight); result.bsdfRoughness[1] = float2(alpha); diff --git a/Source/Falcor/Rendering/Materials/PBRT/PBRTCoatedConductorMaterial.slang b/Source/Falcor/Rendering/Materials/PBRT/PBRTCoatedConductorMaterial.slang index e7855eaf9..8f6a28338 100644 --- a/Source/Falcor/Rendering/Materials/PBRT/PBRTCoatedConductorMaterial.slang +++ b/Source/Falcor/Rendering/Materials/PBRT/PBRTCoatedConductorMaterial.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -49,18 +49,21 @@ struct PBRTCoatedConductorMaterial : MaterialBase, IMaterial float eta = 1.0f / data.IoR; // Compute final shading frame. - ShadingFrame sf = ShadingFrame(sd); + ShadingFrame sf = sd.frame; + if (isNormalMappingEnabled(hints)) + { + float3 encodedNormal = ms.sampleTexture(data.texNormalMap, s, sd.uv, float4(0.f), lod).rgb; + sf = computeShadingFrameFromNormalMap(sd, data.getNormalMapType(), encodedNormal); + } + flipShadingNormal(sd, sf); + if (isAdjustShadingNormalEnabled(hints)) + { + adjustShadingNormal(sd, sf); + } - PBRTCoatedConductorMaterialInstance brdf = {sf, AnisotropicGGX(roughnesses.rg), AnisotropicGGX(roughnesses.ba), eta, baseColor, transColor, 32}; + PBRTCoatedConductorMaterialInstance mi = {sf, AnisotropicGGX(roughnesses.rg), AnisotropicGGX(roughnesses.ba), eta, baseColor, transColor, 32}; - return brdf; - } - - void modifyTangentSpace(const MaterialSystem ms, const ITextureSampler lod, inout ShadingData sd) - { - SamplerState s = ms.getTextureSampler(header.getDefaultTextureSamplerID()); - float3 encodedNormal = ms.sampleTexture(data.texNormalMap, s, sd.uv, float4(0.f), lod).rgb; - applyNormalMap(sd, data.getNormalMapType(), encodedNormal); + return mi; } float evalOpacity(const MaterialSystem ms, const VertexData v, const ITextureSampler lod) diff --git a/Source/Falcor/Rendering/Materials/PBRT/PBRTCoatedConductorMaterialInstance.slang b/Source/Falcor/Rendering/Materials/PBRT/PBRTCoatedConductorMaterialInstance.slang index 5e1f2727f..830417257 100644 --- a/Source/Falcor/Rendering/Materials/PBRT/PBRTCoatedConductorMaterialInstance.slang +++ b/Source/Falcor/Rendering/Materials/PBRT/PBRTCoatedConductorMaterialInstance.slang @@ -25,18 +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 "Utils/Math/MathConstants.slangh" - __exported import Rendering.Materials.IMaterialInstance; -__exported import Rendering.Materials.BxDF; __exported import Rendering.Materials.AnisotropicGGX; -import Utils.Color.ColorHelpers; -import Utils.Math.MathHelpers; -import Rendering.Materials.Fresnel; import Rendering.Materials.LayeredBSDF; import Rendering.Materials.PBRT.PBRTConductorMaterialInstance; import Rendering.Materials.PBRT.PBRTDielectricMaterialInstance; -import Utils.Sampling.UniformSampleGenerator; struct PBRTCoatedConductorMaterialInstance : IMaterialInstance { @@ -52,7 +45,9 @@ struct PBRTCoatedConductorMaterialInstance : IMaterialInstance { float3 wiLocal = sf.toLocal(sd.V); float3 woLocal = sf.toLocal(wo); - + + if (!isValidHemisphereReflection(sd, sf, wiLocal, woLocal, wo)) return float3(0.f); + PBRTDielectricBSDF top = {interfaceD, interfaceEta}; PBRTConductorBSDF bottom = {conductorD, conductorEta, conductorK}; @@ -62,22 +57,30 @@ struct PBRTCoatedConductorMaterialInstance : IMaterialInstance bool sample(const ShadingData sd, inout S sg, out BSDFSample result, bool useImportanceSampling = true) { float3 wiLocal = sf.toLocal(sd.V); - + float3 woLocal = {}; + PBRTDielectricBSDF top = {interfaceD, interfaceEta}; PBRTConductorBSDF bottom = {conductorD, conductorEta, conductorK}; - bool valid = LayeredBSDF(top, bottom).sample(wiLocal, result.wo, result.pdf, result.weight, result.lobeType, sg); + bool valid = LayeredBSDF(top, bottom).sample(wiLocal, woLocal, result.pdf, result.weight, result.lobeType, sg); + result.wo = sf.fromLocal(woLocal); + + if (!isValidHemisphereReflection(sd, sf, wiLocal, woLocal, result.wo) || result.pdf == 0.f) return false; - result.wo = sf.fromLocal(result.wo); return valid; } float evalPdf(const ShadingData sd, const float3 wo, bool useImportanceSampling = true) { + float3 wiLocal = sf.toLocal(sd.V); + float3 woLocal = sf.toLocal(wo); + + if (!isValidHemisphereReflection(sd, sf, wiLocal, woLocal, wo)) return 0.f; + PBRTDielectricBSDF top = {interfaceD, interfaceEta}; PBRTConductorBSDF bottom = {conductorD, conductorEta, conductorK}; - return LayeredBSDF(top, bottom).evalPdf(sf.toLocal(sd.V), sf.toLocal(wo)); + return LayeredBSDF(top, bottom).evalPdf(wiLocal, woLocal); } BSDFProperties getProperties(const ShadingData sd) diff --git a/Source/Falcor/Rendering/Materials/PBRT/PBRTCoatedDiffuseMaterial.slang b/Source/Falcor/Rendering/Materials/PBRT/PBRTCoatedDiffuseMaterial.slang index 0ece554f0..3fa3d9039 100644 --- a/Source/Falcor/Rendering/Materials/PBRT/PBRTCoatedDiffuseMaterial.slang +++ b/Source/Falcor/Rendering/Materials/PBRT/PBRTCoatedDiffuseMaterial.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -46,18 +46,21 @@ struct PBRTCoatedDiffuseMaterial : MaterialBase, IMaterial float eta = 1.0f / data.IoR; // Compute final shading frame. - ShadingFrame sf = ShadingFrame(sd); + ShadingFrame sf = sd.frame; + if (isNormalMappingEnabled(hints)) + { + float3 encodedNormal = ms.sampleTexture(data.texNormalMap, s, sd.uv, float4(0.f), lod).rgb; + sf = computeShadingFrameFromNormalMap(sd, data.getNormalMapType(), encodedNormal); + } + flipShadingNormal(sd, sf); + if (isAdjustShadingNormalEnabled(hints)) + { + adjustShadingNormal(sd, sf); + } - PBRTCoatedDiffuseMaterialInstance brdf = {sf, AnisotropicGGX(roughness), eta, baseColor, 32}; + PBRTCoatedDiffuseMaterialInstance mi = {sf, AnisotropicGGX(roughness), eta, baseColor, 32}; - return brdf; - } - - void modifyTangentSpace(const MaterialSystem ms, const ITextureSampler lod, inout ShadingData sd) - { - SamplerState s = ms.getTextureSampler(header.getDefaultTextureSamplerID()); - float3 encodedNormal = ms.sampleTexture(data.texNormalMap, s, sd.uv, float4(0.f), lod).rgb; - applyNormalMap(sd, data.getNormalMapType(), encodedNormal); + return mi; } float evalOpacity(const MaterialSystem ms, const VertexData v, const ITextureSampler lod) diff --git a/Source/Falcor/Rendering/Materials/PBRT/PBRTCoatedDiffuseMaterialInstance.slang b/Source/Falcor/Rendering/Materials/PBRT/PBRTCoatedDiffuseMaterialInstance.slang index 5f46f9a82..a9f3070fb 100644 --- a/Source/Falcor/Rendering/Materials/PBRT/PBRTCoatedDiffuseMaterialInstance.slang +++ b/Source/Falcor/Rendering/Materials/PBRT/PBRTCoatedDiffuseMaterialInstance.slang @@ -25,18 +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 "Utils/Math/MathConstants.slangh" - __exported import Rendering.Materials.IMaterialInstance; -__exported import Rendering.Materials.BxDF; __exported import Rendering.Materials.AnisotropicGGX; -import Utils.Color.ColorHelpers; -import Utils.Math.MathHelpers; -import Rendering.Materials.Fresnel; import Rendering.Materials.LayeredBSDF; import Rendering.Materials.PBRT.PBRTDielectricMaterialInstance; import Rendering.Materials.PBRT.PBRTDiffuseMaterialInstance; -import Utils.Sampling.UniformSampleGenerator; struct PBRTCoatedDiffuseMaterialInstance : IMaterialInstance { @@ -50,7 +43,9 @@ struct PBRTCoatedDiffuseMaterialInstance : IMaterialInstance { float3 wiLocal = sf.toLocal(sd.V); float3 woLocal = sf.toLocal(wo); - + + if (!isValidHemisphereReflection(sd, sf, wiLocal, woLocal, wo)) return float3(0.f); + PBRTDielectricBSDF top = {interfaceD, interfaceEta}; PBRTDiffuseBSDF bottom = {diffuseAlbedo}; @@ -60,22 +55,30 @@ struct PBRTCoatedDiffuseMaterialInstance : IMaterialInstance bool sample(const ShadingData sd, inout S sg, out BSDFSample result, bool useImportanceSampling = true) { float3 wiLocal = sf.toLocal(sd.V); + float3 woLocal = {}; PBRTDielectricBSDF top = {interfaceD, interfaceEta}; PBRTDiffuseBSDF bottom = {diffuseAlbedo}; - bool valid = LayeredBSDF(top, bottom).sample(wiLocal, result.wo, result.pdf, result.weight, result.lobeType, sg); + bool valid = LayeredBSDF(top, bottom).sample(wiLocal, woLocal, result.pdf, result.weight, result.lobeType, sg); + result.wo = sf.fromLocal(woLocal); + + if (!isValidHemisphereReflection(sd, sf, wiLocal, woLocal, result.wo) || result.pdf == 0.f) return false; - result.wo = sf.fromLocal(result.wo); return valid; } float evalPdf(const ShadingData sd, const float3 wo, bool useImportanceSampling = true) { + float3 wiLocal = sf.toLocal(sd.V); + float3 woLocal = sf.toLocal(wo); + + if (!isValidHemisphereReflection(sd, sf, wiLocal, woLocal, wo)) return 0.f; + PBRTDielectricBSDF top = {interfaceD, interfaceEta}; PBRTDiffuseBSDF bottom = {diffuseAlbedo}; - return LayeredBSDF(top, bottom).evalPdf(sf.toLocal(sd.V), sf.toLocal(wo)); + return LayeredBSDF(top, bottom).evalPdf(wiLocal, woLocal); } BSDFProperties getProperties(const ShadingData sd) diff --git a/Source/Falcor/Rendering/Materials/PBRT/PBRTConductorMaterial.slang b/Source/Falcor/Rendering/Materials/PBRT/PBRTConductorMaterial.slang index d8fe7ec14..f6b928df3 100644 --- a/Source/Falcor/Rendering/Materials/PBRT/PBRTConductorMaterial.slang +++ b/Source/Falcor/Rendering/Materials/PBRT/PBRTConductorMaterial.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -46,20 +46,23 @@ struct PBRTConductorMaterial : MaterialBase, IMaterial const float2 roughness = ms.sampleTexture(data.texSpecular, s, sd.uv, data.specular, lod).rg; // Compute final shading frame. - ShadingFrame sf = ShadingFrame(sd); + ShadingFrame sf = sd.frame; + if (isNormalMappingEnabled(hints)) + { + float3 encodedNormal = ms.sampleTexture(data.texNormalMap, s, sd.uv, float4(0.f), lod).rgb; + sf = computeShadingFrameFromNormalMap(sd, data.getNormalMapType(), encodedNormal); + } + flipShadingNormal(sd, sf); + if (isAdjustShadingNormalEnabled(hints)) + { + adjustShadingNormal(sd, sf); + } PBRTConductorMaterialInstance mi = {sf, {AnisotropicGGX(roughness), baseColor, transColor}}; return mi; } - void modifyTangentSpace(const MaterialSystem ms, const ITextureSampler lod, inout ShadingData sd) - { - SamplerState s = ms.getTextureSampler(header.getDefaultTextureSamplerID()); - float3 encodedNormal = ms.sampleTexture(data.texNormalMap, s, sd.uv, float4(0.f), lod).rgb; - applyNormalMap(sd, data.getNormalMapType(), encodedNormal); - } - float evalOpacity(const MaterialSystem ms, const VertexData v, const ITextureSampler lod) { SamplerState s = ms.getTextureSampler(header.getDefaultTextureSamplerID()); diff --git a/Source/Falcor/Rendering/Materials/PBRT/PBRTConductorMaterialInstance.slang b/Source/Falcor/Rendering/Materials/PBRT/PBRTConductorMaterialInstance.slang index eafca220e..5e13e5fef 100644 --- a/Source/Falcor/Rendering/Materials/PBRT/PBRTConductorMaterialInstance.slang +++ b/Source/Falcor/Rendering/Materials/PBRT/PBRTConductorMaterialInstance.slang @@ -25,13 +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 "Utils/Math/MathConstants.slangh" - __exported import Rendering.Materials.IMaterialInstance; -__exported import Rendering.Materials.BxDF; __exported import Rendering.Materials.AnisotropicGGX; -import Utils.Color.ColorHelpers; -import Utils.Math.MathHelpers; import Rendering.Materials.Fresnel; struct PBRTConductorBSDF : IBSDF @@ -107,20 +102,35 @@ struct PBRTConductorMaterialInstance : IMaterialInstance float3 eval(const ShadingData sd, const float3 wo, inout S sg) { - return bsdf.eval(sf.toLocal(sd.V), sf.toLocal(wo), sg); + float3 wiLocal = sf.toLocal(sd.V); + float3 woLocal = sf.toLocal(wo); + + if (!isValidHemisphereReflection(sd, sf, wiLocal, woLocal, wo)) return float3(0.f); + + return bsdf.eval(wiLocal, woLocal, sg); } bool sample(const ShadingData sd, inout S sg, out BSDFSample result, bool useImportanceSampling = true) { - bool valid = bsdf.sample(sf.toLocal(sd.V), result.wo, result.pdf, result.weight, result.lobeType, sg); + float3 wiLocal = sf.toLocal(sd.V); + float3 woLocal = {}; + + bool valid = bsdf.sample(wiLocal, woLocal, result.pdf, result.weight, result.lobeType, sg); + result.wo = sf.fromLocal(woLocal); + + if (!isValidHemisphereReflection(sd, sf, wiLocal, woLocal, result.wo) || result.pdf == 0.f) return false; - result.wo = sf.fromLocal(result.wo); return valid; } float evalPdf(const ShadingData sd, const float3 wo, bool useImportanceSampling = true) { - return bsdf.evalPdf(sf.toLocal(sd.V), sf.toLocal(wo)); + float3 wiLocal = sf.toLocal(sd.V); + float3 woLocal = sf.toLocal(wo); + + if (!isValidHemisphereReflection(sd, sf, wiLocal, woLocal, wo)) return 0.f; + + return bsdf.evalPdf(wiLocal, woLocal); } BSDFProperties getProperties(const ShadingData sd) diff --git a/Source/Falcor/Rendering/Materials/PBRT/PBRTDielectricMaterial.slang b/Source/Falcor/Rendering/Materials/PBRT/PBRTDielectricMaterial.slang index 776385b1e..58d8c22f5 100644 --- a/Source/Falcor/Rendering/Materials/PBRT/PBRTDielectricMaterial.slang +++ b/Source/Falcor/Rendering/Materials/PBRT/PBRTDielectricMaterial.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -44,20 +44,23 @@ struct PBRTDielectricMaterial : MaterialBase, IMaterial float eta = !sd.frontFacing ? 1.0f / data.IoR : data.IoR; // Compute final shading frame. - ShadingFrame sf = ShadingFrame(sd); + // Do not flip the shading normal for backfacing hits. The BSDF handles transmission from both sides. + ShadingFrame sf = sd.frame; + if (isNormalMappingEnabled(hints)) + { + float3 encodedNormal = ms.sampleTexture(data.texNormalMap, s, sd.uv, float4(0.f), lod).rgb; + sf = computeShadingFrameFromNormalMap(sd, data.getNormalMapType(), encodedNormal); + } + if (isAdjustShadingNormalEnabled(hints)) + { + adjustShadingNormal(sd, sf); + } PBRTDielectricMaterialInstance mi = {sf, {AnisotropicGGX(roughness), eta}}; return mi; } - void modifyTangentSpace(const MaterialSystem ms, const ITextureSampler lod, inout ShadingData sd) - { - SamplerState s = ms.getTextureSampler(header.getDefaultTextureSamplerID()); - float3 encodedNormal = ms.sampleTexture(data.texNormalMap, s, sd.uv, float4(0.f), lod).rgb; - applyNormalMap(sd, data.getNormalMapType(), encodedNormal); - } - float evalOpacity(const MaterialSystem ms, const VertexData v, const ITextureSampler lod) { SamplerState s = ms.getTextureSampler(header.getDefaultTextureSamplerID()); diff --git a/Source/Falcor/Rendering/Materials/PBRT/PBRTDielectricMaterialInstance.slang b/Source/Falcor/Rendering/Materials/PBRT/PBRTDielectricMaterialInstance.slang index 4c22ebb7e..e097e8a96 100644 --- a/Source/Falcor/Rendering/Materials/PBRT/PBRTDielectricMaterialInstance.slang +++ b/Source/Falcor/Rendering/Materials/PBRT/PBRTDielectricMaterialInstance.slang @@ -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 "Utils/Math/MathConstants.slangh" - __exported import Rendering.Materials.IMaterialInstance; -__exported import Rendering.Materials.BxDF; __exported import Rendering.Materials.AnisotropicGGX; -import Utils.Color.ColorHelpers; import Utils.Math.MathHelpers; import Rendering.Materials.Fresnel; -import Utils.Debug.PixelDebug; struct PBRTDielectricBSDF : IBSDF { @@ -142,6 +137,10 @@ struct PBRTDielectricBSDF : IBSDF } }; +/** PBRT dielectric material instance. + Note that the BSDF is implemented to handle wi/wo over the full sphere. + The material is treated as single-sided and the shading normal should not be flipped. +*/ struct PBRTDielectricMaterialInstance : IMaterialInstance { ShadingFrame sf; ///< Shading frame in world space. @@ -149,19 +148,36 @@ struct PBRTDielectricMaterialInstance : IMaterialInstance float3 eval(const ShadingData sd, const float3 wo, inout S sg) { - return bsdf.eval(sf.toLocal(sd.V), sf.toLocal(wo), sg); + float3 wiLocal = sf.toLocal(sd.V); + float3 woLocal = sf.toLocal(wo); + + if (!isValidHemisphereReflectionOrTransmission(sd, sf, wiLocal, woLocal, wo, true)) return float3(0.f); + + return bsdf.eval(wiLocal, woLocal, sg); } bool sample(const ShadingData sd, inout S sg, out BSDFSample result, bool useImportanceSampling = true) { - bool valid = bsdf.sample(sf.toLocal(sd.V), result.wo, result.pdf, result.weight, result.lobeType, sg); - result.wo = sf.fromLocal(result.wo); - return valid; + float3 wiLocal = sf.toLocal(sd.V); + float3 woLocal = {}; + + if (!bsdf.sample(wiLocal, woLocal, result.pdf, result.weight, result.lobeType, sg)) + return false; + result.wo = sf.fromLocal(woLocal); + + if (!isValidHemisphereReflectionOrTransmission(sd, sf, wiLocal, woLocal, result.wo, true)) return false; + + return true; } float evalPdf(const ShadingData sd, const float3 wo, bool useImportanceSampling = true) { - return bsdf.evalPdf(sf.toLocal(sd.V), sf.toLocal(wo)); + float3 wiLocal = sf.toLocal(sd.V); + float3 woLocal = sf.toLocal(wo); + + if (!isValidHemisphereReflectionOrTransmission(sd, sf, wiLocal, woLocal, wo, true)) return 0.f; + + return bsdf.evalPdf(wiLocal, woLocal); } BSDFProperties getProperties(const ShadingData sd) diff --git a/Source/Falcor/Rendering/Materials/PBRT/PBRTDiffuseMaterial.slang b/Source/Falcor/Rendering/Materials/PBRT/PBRTDiffuseMaterial.slang index 2cf414102..1bfbb601b 100644 --- a/Source/Falcor/Rendering/Materials/PBRT/PBRTDiffuseMaterial.slang +++ b/Source/Falcor/Rendering/Materials/PBRT/PBRTDiffuseMaterial.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -43,20 +43,23 @@ struct PBRTDiffuseMaterial : MaterialBase, IMaterial const float3 baseColor = ms.sampleTexture(data.texBaseColor, s, sd.uv, data.baseColor, lod).rgb; // Compute final shading frame. - ShadingFrame sf = ShadingFrame(sd); + ShadingFrame sf = sd.frame; + if (isNormalMappingEnabled(hints)) + { + float3 encodedNormal = ms.sampleTexture(data.texNormalMap, s, sd.uv, float4(0.f), lod).rgb; + sf = computeShadingFrameFromNormalMap(sd, data.getNormalMapType(), encodedNormal); + } + flipShadingNormal(sd, sf); + if (isAdjustShadingNormalEnabled(hints)) + { + adjustShadingNormal(sd, sf); + } PBRTDiffuseMaterialInstance mi = {sf, {baseColor}}; return mi; } - void modifyTangentSpace(const MaterialSystem ms, const ITextureSampler lod, inout ShadingData sd) - { - SamplerState s = ms.getTextureSampler(header.getDefaultTextureSamplerID()); - float3 encodedNormal = ms.sampleTexture(data.texNormalMap, s, sd.uv, float4(0.f), lod).rgb; - applyNormalMap(sd, data.getNormalMapType(), encodedNormal); - } - float evalOpacity(const MaterialSystem ms, const VertexData v, const ITextureSampler lod) { SamplerState s = ms.getTextureSampler(header.getDefaultTextureSamplerID()); diff --git a/Source/Falcor/Rendering/Materials/PBRT/PBRTDiffuseMaterialInstance.slang b/Source/Falcor/Rendering/Materials/PBRT/PBRTDiffuseMaterialInstance.slang index dc8852839..c21a8b101 100644 --- a/Source/Falcor/Rendering/Materials/PBRT/PBRTDiffuseMaterialInstance.slang +++ b/Source/Falcor/Rendering/Materials/PBRT/PBRTDiffuseMaterialInstance.slang @@ -28,7 +28,6 @@ #include "Utils/Math/MathConstants.slangh" __exported import Rendering.Materials.IMaterialInstance; -__exported import Rendering.Materials.BxDF; import Utils.Math.MathHelpers; struct PBRTDiffuseBSDF : IBSDF @@ -74,20 +73,36 @@ struct PBRTDiffuseMaterialInstance : IMaterialInstance float3 eval(const ShadingData sd, const float3 wo, inout S sg) { - return bsdf.eval(sf.toLocal(sd.V), sf.toLocal(wo), sg); + float3 wiLocal = sf.toLocal(sd.V); + float3 woLocal = sf.toLocal(wo); + + if (!isValidHemisphereReflection(sd, sf, wiLocal, woLocal, wo)) return float3(0.f); + + return bsdf.eval(wiLocal, woLocal, sg); } bool sample(const ShadingData sd, inout S sg, out BSDFSample result, bool useImportanceSampling = true) { - bool valid = bsdf.sample(sf.toLocal(sd.V), result.wo, result.pdf, result.weight, result.lobeType, sg); - result.wo = sf.fromLocal(result.wo); + float3 wiLocal = sf.toLocal(sd.V); + float3 woLocal = {}; + + bool valid = bsdf.sample(wiLocal, woLocal, result.pdf, result.weight, result.lobeType, sg); + result.wo = sf.fromLocal(woLocal); + + if (!isValidHemisphereReflection(sd, sf, wiLocal, woLocal, result.wo) || result.pdf == 0.f) return false; + return valid; } float evalPdf(const ShadingData sd, const float3 wo, bool useImportanceSampling = true) { - return bsdf.evalPdf(sf.toLocal(sd.V), sf.toLocal(wo)); + float3 wiLocal = sf.toLocal(sd.V); + float3 woLocal = sf.toLocal(wo); + + if (!isValidHemisphereReflection(sd, sf, wiLocal, woLocal, wo)) return 0.f; + + return bsdf.evalPdf(wiLocal, woLocal); } BSDFProperties getProperties(const ShadingData sd) diff --git a/Source/Falcor/Rendering/Materials/PBRT/PBRTDiffuseTransmissionMaterial.slang b/Source/Falcor/Rendering/Materials/PBRT/PBRTDiffuseTransmissionMaterial.slang index d8ed83b57..39c58d8a0 100644 --- a/Source/Falcor/Rendering/Materials/PBRT/PBRTDiffuseTransmissionMaterial.slang +++ b/Source/Falcor/Rendering/Materials/PBRT/PBRTDiffuseTransmissionMaterial.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -44,20 +44,23 @@ struct PBRTDiffuseTransmissionMaterial : MaterialBase, IMaterial const float3 transColor = ms.sampleTexture(data.texTransmission, s, sd.uv, float4(data.transmission, 1.0f), lod).rgb; // Compute final shading frame. - ShadingFrame sf = ShadingFrame(sd); + // Do not flip the shading normal for backfacing hits. The BSDF handles transmission from both sides. + ShadingFrame sf = sd.frame; + if (isNormalMappingEnabled(hints)) + { + float3 encodedNormal = ms.sampleTexture(data.texNormalMap, s, sd.uv, float4(0.f), lod).rgb; + sf = computeShadingFrameFromNormalMap(sd, data.getNormalMapType(), encodedNormal); + } + if (isAdjustShadingNormalEnabled(hints)) + { + adjustShadingNormal(sd, sf); + } PBRTDiffuseTransmissionMaterialInstance mi = {sf, baseColor, transColor}; return mi; } - void modifyTangentSpace(const MaterialSystem ms, const ITextureSampler lod, inout ShadingData sd) - { - SamplerState s = ms.getTextureSampler(header.getDefaultTextureSamplerID()); - float3 encodedNormal = ms.sampleTexture(data.texNormalMap, s, sd.uv, float4(0.f), lod).rgb; - applyNormalMap(sd, data.getNormalMapType(), encodedNormal); - } - float evalOpacity(const MaterialSystem ms, const VertexData v, const ITextureSampler lod) { SamplerState s = ms.getTextureSampler(header.getDefaultTextureSamplerID()); diff --git a/Source/Falcor/Rendering/Materials/PBRT/PBRTDiffuseTransmissionMaterialInstance.slang b/Source/Falcor/Rendering/Materials/PBRT/PBRTDiffuseTransmissionMaterialInstance.slang index 4b34cc552..dd9972d36 100644 --- a/Source/Falcor/Rendering/Materials/PBRT/PBRTDiffuseTransmissionMaterialInstance.slang +++ b/Source/Falcor/Rendering/Materials/PBRT/PBRTDiffuseTransmissionMaterialInstance.slang @@ -28,10 +28,13 @@ #include "Utils/Math/MathConstants.slangh" __exported import Rendering.Materials.IMaterialInstance; -__exported import Rendering.Materials.BxDF; import Utils.Color.ColorHelpers; import Utils.Math.MathHelpers; +/** PBRT diffuse transmisson material instance. + Note that the BSDF is implemented to handle wi/wo over the full sphere. + The material is treated as single-sided and the shading normal should not be flipped. +*/ struct PBRTDiffuseTransmissionMaterialInstance : IMaterialInstance { ShadingFrame sf; ///< Shading frame in world space. @@ -42,7 +45,9 @@ struct PBRTDiffuseTransmissionMaterialInstance : IMaterialInstance { float3 wiLocal = sf.toLocal(sd.V); float3 woLocal = sf.toLocal(wo); - + + if (!isValidHemisphereReflectionOrTransmission(sd, sf, wiLocal, woLocal, wo, true)) return float3(0.f); + float3 albedo = (wiLocal.z * woLocal.z > 0.0f) ? albedoR : albedoT; return albedo * abs(woLocal.z) * M_1_PI; } @@ -71,6 +76,8 @@ struct PBRTDiffuseTransmissionMaterialInstance : IMaterialInstance } result.wo = sf.fromLocal(woLocal); + if (!isValidHemisphereReflectionOrTransmission(sd, sf, wiLocal, woLocal, result.wo, true)) return false; + return true; } @@ -78,7 +85,9 @@ struct PBRTDiffuseTransmissionMaterialInstance : IMaterialInstance { float3 wiLocal = sf.toLocal(sd.V); float3 woLocal = sf.toLocal(wo); - + + if (!isValidHemisphereReflectionOrTransmission(sd, sf, wiLocal, woLocal, wo, true)) return 0.f; + float PR = luminance(albedoR); float PT = luminance(albedoT); if (PR + PT == 0.0f) return 0.0f; diff --git a/Source/Falcor/Rendering/Materials/RGLAcquisition.cpp b/Source/Falcor/Rendering/Materials/RGLAcquisition.cpp index 412daf7e8..ee3037ab2 100644 --- a/Source/Falcor/Rendering/Materials/RGLAcquisition.cpp +++ b/Source/Falcor/Rendering/Materials/RGLAcquisition.cpp @@ -66,8 +66,8 @@ namespace Falcor const uint kVNDFN = kVNDFSize.x * kVNDFSize.y * kVNDFSize.z * kVNDFSize.w; } - RGLAcquisition::RGLAcquisition(std::shared_ptr pDevice, const Scene::SharedPtr& pScene) - : mpDevice(std::move(pDevice)) + RGLAcquisition::RGLAcquisition(ref pDevice, const ref& pScene) + : mpDevice(pDevice) , mpScene(pScene) { checkArgument(mpDevice != nullptr, "'pDevice' must be a valid device"); @@ -98,7 +98,7 @@ namespace Falcor auto createStructured = [&](size_t elemSize, size_t count, const void* srcData = nullptr) { return Buffer::createStructured( - mpDevice.get(), + mpDevice, uint32_t(elemSize), uint32_t(count), ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess, @@ -131,7 +131,7 @@ namespace Falcor CpuTimer timer; timer.update(); - auto setupParams = [&](const ComputePass::SharedPtr& pPass) + auto setupParams = [&](const ref& pPass) { auto var = pPass->getRootVar()[kParameterBlock]; var["materialID"] = materialID.getSlang(); @@ -155,7 +155,8 @@ namespace Falcor var["vndfConditionalBuf"] = mpVNDFCondBuffer; var["lumi"] = mpLumiBuffer; var["rgb"] = mpRGBBuffer; - pPass["gScene"] = mpScene->getParameterBlock(); + + pPass->getRootVar()["gScene"] = mpScene->getParameterBlock(); }; setupParams(mpRetroReflectionPass); diff --git a/Source/Falcor/Rendering/Materials/RGLAcquisition.cs.slang b/Source/Falcor/Rendering/Materials/RGLAcquisition.cs.slang index e178153bc..5fde9cc96 100644 --- a/Source/Falcor/Rendering/Materials/RGLAcquisition.cs.slang +++ b/Source/Falcor/Rendering/Materials/RGLAcquisition.cs.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -65,10 +65,8 @@ struct RGLAcquisition ShadingData buildShadingData(float3 wi) { ShadingData sd = {}; - sd.T = float3(1.0f, 0.0f, 0.0f); - sd.B = float3(0.0f, 1.0f, 0.0f); - sd.N = float3(0.0f, 0.0f, 1.0f); - sd.faceN = sd.N; + sd.frame = ShadingFrame::createIdentity(); + sd.faceN = sd.frame.N; sd.frontFacing = true; sd.mtl = gScene.materials.getMaterialHeader(materialID); diff --git a/Source/Falcor/Rendering/Materials/RGLAcquisition.h b/Source/Falcor/Rendering/Materials/RGLAcquisition.h index dd0f057cd..0848e9028 100644 --- a/Source/Falcor/Rendering/Materials/RGLAcquisition.h +++ b/Source/Falcor/Rendering/Materials/RGLAcquisition.h @@ -28,7 +28,7 @@ #pragma once #include "Core/Macros.h" #include "Core/API/Buffer.h" -#include "RenderGraph/BasePasses/ComputePass.h" +#include "Core/Pass/ComputePass.h" #include "Scene/Scene.h" #include "Scene/Material/RGLFile.h" #include @@ -44,35 +44,35 @@ namespace Falcor { public: /// Constructor. - RGLAcquisition(std::shared_ptr pDevice, const Scene::SharedPtr& pScene); + RGLAcquisition(ref pDevice, const ref& pScene); void acquireIsotropic(RenderContext* pRenderContext, const MaterialID materialID); RGLFile toRGLFile(); private: - std::shared_ptr mpDevice; - Scene::SharedPtr mpScene; - ComputePass::SharedPtr mpRetroReflectionPass; - ComputePass::SharedPtr mpBuildKernelPass; - ComputePass::SharedPtr mpPowerIterationPass; - ComputePass::SharedPtr mpIntegrateSigmaPass; - ComputePass::SharedPtr mpSumSigmaPass; - ComputePass::SharedPtr mpComputeThetaPass; - ComputePass::SharedPtr mpComputeVNDFPass; - ComputePass::SharedPtr mpAcquireBRDFPass; + ref mpDevice; + ref mpScene; + ref mpRetroReflectionPass; + ref mpBuildKernelPass; + ref mpPowerIterationPass; + ref mpIntegrateSigmaPass; + ref mpSumSigmaPass; + ref mpComputeThetaPass; + ref mpComputeVNDFPass; + ref mpAcquireBRDFPass; - Buffer::SharedPtr mpNDFDirectionsBuffer; ///< Stores hemispherical directions of entries in the NDF table. - Buffer::SharedPtr mpRetroBuffer; ///< 2D table storing measured retroreflecton of BRDF. - Buffer::SharedPtr mpNDFKernelBuffer; ///< Stores kernel matrix of Fredholm problem for retrieving NDF. - Buffer::SharedPtr mpNDFBuffer; ///< 2D table storing the retrieved NDF. - Buffer::SharedPtr mpNDFBufferTmp; - Buffer::SharedPtr mpSigmaBuffer; ///< 2D table of projected microfacet area, integrated numerically. - Buffer::SharedPtr mpThetaBuffer; ///< 1D tables storing angles at which measurements are taken. - Buffer::SharedPtr mpPhiBuffer; - Buffer::SharedPtr mpVNDFBuffer; ///< 4D table (over wi x wo domains) containing the visible distribution of normals. - Buffer::SharedPtr mpVNDFMargBuffer; ///< Auxiliary buffers for sampling the VNDF. - Buffer::SharedPtr mpVNDFCondBuffer; - Buffer::SharedPtr mpLumiBuffer; ///< 4D table of measured luminance and RGB reflectance of RGB. - Buffer::SharedPtr mpRGBBuffer; + ref mpNDFDirectionsBuffer; ///< Stores hemispherical directions of entries in the NDF table. + ref mpRetroBuffer; ///< 2D table storing measured retroreflecton of BRDF. + ref mpNDFKernelBuffer; ///< Stores kernel matrix of Fredholm problem for retrieving NDF. + ref mpNDFBuffer; ///< 2D table storing the retrieved NDF. + ref mpNDFBufferTmp; + ref mpSigmaBuffer; ///< 2D table of projected microfacet area, integrated numerically. + ref mpThetaBuffer; ///< 1D tables storing angles at which measurements are taken. + ref mpPhiBuffer; + ref mpVNDFBuffer; ///< 4D table (over wi x wo domains) containing the visible distribution of normals. + ref mpVNDFMargBuffer; ///< Auxiliary buffers for sampling the VNDF. + ref mpVNDFCondBuffer; + ref mpLumiBuffer; ///< 4D table of measured luminance and RGB reflectance of RGB. + ref mpRGBBuffer; }; } diff --git a/Source/Falcor/Rendering/Materials/RGLMaterial.slang b/Source/Falcor/Rendering/Materials/RGLMaterial.slang index 86c654c2d..74479fdb8 100644 --- a/Source/Falcor/Rendering/Materials/RGLMaterial.slang +++ b/Source/Falcor/Rendering/Materials/RGLMaterial.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -43,7 +43,12 @@ struct RGLMaterial : MaterialBase, IMaterial RGLMaterialInstance setupMaterialInstance(const MaterialSystem ms, const ShadingData sd, const ITextureSampler lod, const uint hints) { // Compute final shading frame. - ShadingFrame sf = ShadingFrame(sd); + ShadingFrame sf = sd.frame; + flipShadingNormal(sd, sf); + if (isAdjustShadingNormalEnabled(hints)) + { + adjustShadingNormal(sd, sf); + } // Evaluate the albedo for the current incident (view) direction. float3 albedo = {}; @@ -76,9 +81,6 @@ struct RGLMaterial : MaterialBase, IMaterial return RGLMaterialInstance(sf, slice, sigmaEval, albedo, data); } - // Normal mapping is not supported by this material. - void modifyTangentSpace(const MaterialSystem ms, const ITextureSampler lod, inout ShadingData sd) {} - // Alpha testing is not supported by this material. float evalOpacity(const MaterialSystem ms, const VertexData v, const ITextureSampler lod) { return 1.f; } }; diff --git a/Source/Falcor/Rendering/Materials/RGLMaterialInstance.slang b/Source/Falcor/Rendering/Materials/RGLMaterialInstance.slang index fc20df9c1..64f444d60 100644 --- a/Source/Falcor/Rendering/Materials/RGLMaterialInstance.slang +++ b/Source/Falcor/Rendering/Materials/RGLMaterialInstance.slang @@ -75,10 +75,7 @@ struct RGLMaterialInstance : IMaterialInstance float3 wiLocal = sf.toLocal(sd.V); float3 woLocal = sf.toLocal(wo); - if (min(wiLocal.z, woLocal.z) < kMinCosTheta) - { - return float3(0.0f); - } + if (!isValidHemisphereReflection(sd, sf, wiLocal, woLocal, wo)) return float3(0.f); uint4 ndfsSize = unpackSize(ndfVndfSize); uint4 lumiSize = unpackSize(this.lumiSize); @@ -144,9 +141,11 @@ struct RGLMaterialInstance : IMaterialInstance float3 f = sigma == 0.0f ? 0.0f : fr * ndf.eval(unitH) / (4.0f * sigma); float pdf = nvdfSamplePdf.z * lumiSamplePdf.z / jacobian; + result.wo = sf.fromLocal(woLocal); + if (jacobian == 0.0f || pdf == 0.0f) return false; + if (!isValidHemisphereReflection(sd, sf, wiLocal, woLocal, result.wo)) return false; - result.wo = sf.fromLocal(woLocal); result.pdf = pdf; result.weight = f / pdf; result.lobeType = (uint)LobeType::DiffuseReflection; @@ -159,10 +158,7 @@ struct RGLMaterialInstance : IMaterialInstance float3 wiLocal = sf.toLocal(sd.V); float3 woLocal = sf.toLocal(wo); - if (min(wiLocal.z, woLocal.z) < kMinCosTheta) - { - return 0.0f; - } + if (!isValidHemisphereReflection(sd, sf, wiLocal, woLocal, wo)) return 0.f; uint4 ndfsSize = unpackSize(ndfVndfSize); uint4 lumiSize = unpackSize(this.lumiSize); diff --git a/Source/Falcor/Rendering/Materials/StandardMaterial.slang b/Source/Falcor/Rendering/Materials/StandardMaterial.slang index f385443e7..84b7b9d38 100644 --- a/Source/Falcor/Rendering/Materials/StandardMaterial.slang +++ b/Source/Falcor/Rendering/Materials/StandardMaterial.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -100,7 +100,17 @@ struct StandardMaterial : MaterialBase, IMaterial } // Compute final shading frame. - ShadingFrame sf = ShadingFrame(sd); + ShadingFrame sf = sd.frame; + if (isNormalMappingEnabled(hints)) + { + float3 encodedNormal = ms.sampleTexture(data.texNormalMap, s, sd.uv, float4(0.f), lod).rgb; + sf = computeShadingFrameFromNormalMap(sd, data.getNormalMapType(), encodedNormal); + } + flipShadingNormal(sd, sf); + if (isAdjustShadingNormalEnabled(hints)) + { + adjustShadingNormal(sd, sf); + } // Sample the emissive texture. // The standard material supports uniform emission over the hemisphere. @@ -114,13 +124,6 @@ struct StandardMaterial : MaterialBase, IMaterial return StandardMaterialInstance(sf, d, emission); } - void modifyTangentSpace(const MaterialSystem ms, const ITextureSampler lod, inout ShadingData sd) - { - SamplerState s = ms.getTextureSampler(header.getDefaultTextureSamplerID()); - float3 encodedNormal = ms.sampleTexture(data.texNormalMap, s, sd.uv, float4(0.f), lod).rgb; - applyNormalMap(sd, data.getNormalMapType(), encodedNormal); - } - float evalOpacity(const MaterialSystem ms, const VertexData v, const ITextureSampler lod) { SamplerState s = ms.getTextureSampler(header.getDefaultTextureSamplerID()); diff --git a/Source/Falcor/Rendering/Materials/StandardMaterialInstance.slang b/Source/Falcor/Rendering/Materials/StandardMaterialInstance.slang index a871418bf..51a6f9bbd 100644 --- a/Source/Falcor/Rendering/Materials/StandardMaterialInstance.slang +++ b/Source/Falcor/Rendering/Materials/StandardMaterialInstance.slang @@ -28,7 +28,7 @@ #include "Utils/Math/MathConstants.slangh" __exported import Rendering.Materials.IMaterialInstance; -__exported import Rendering.Materials.BxDF; +__exported import Rendering.Materials.BSDFs.StandardBSDF; import Utils.Math.MathHelpers; /** Implementation of Falcor's standard surface BSDF. @@ -61,6 +61,8 @@ struct StandardMaterialInstance : IMaterialInstance float3 wiLocal = sf.toLocal(sd.V); float3 woLocal = sf.toLocal(wo); + if (!isValidHemisphereReflectionOrTransmission(sd, sf, wiLocal, woLocal, wo)) return float3(0.f); + StandardBSDF bsdf = StandardBSDF(wiLocal, sd.mtl, data); return bsdf.eval(wiLocal, woLocal, sg); @@ -68,29 +70,43 @@ struct StandardMaterialInstance : IMaterialInstance bool sample(const ShadingData sd, inout S sg, out BSDFSample result, bool useImportanceSampling = true) { - if (!useImportanceSampling) return sampleReference(sd, sg, result); - float3 wiLocal = sf.toLocal(sd.V); float3 woLocal = {}; - StandardBSDF bsdf = StandardBSDF(wiLocal, sd.mtl, data); - - bool valid = bsdf.sample(wiLocal, woLocal, result.pdf, result.weight, result.lobeType, sg); + if (!useImportanceSampling) + { + if (!sampleReference(sd, wiLocal, woLocal, result.pdf, result.weight, result.lobeType, sg)) + return false; + } + else + { + StandardBSDF bsdf = StandardBSDF(wiLocal, sd.mtl, data); + if (!bsdf.sample(wiLocal, woLocal, result.pdf, result.weight, result.lobeType, sg)) + return false; + } result.wo = sf.fromLocal(woLocal); - return valid; + if (!isValidHemisphereReflectionOrTransmission(sd, sf, wiLocal, woLocal, result.wo)) return false; + + return true; } float evalPdf(const ShadingData sd, const float3 wo, bool useImportanceSampling = true) { - if (!useImportanceSampling) return evalPdfReference(sd, wo); - float3 wiLocal = sf.toLocal(sd.V); float3 woLocal = sf.toLocal(wo); - StandardBSDF bsdf = StandardBSDF(wiLocal, sd.mtl, data); + if (!isValidHemisphereReflectionOrTransmission(sd, sf, wiLocal, woLocal, wo)) return 0.f; - return bsdf.evalPdf(wiLocal, woLocal); + if (!useImportanceSampling) + { + return evalPdfReference(sd, wiLocal, woLocal); + } + else + { + StandardBSDF bsdf = StandardBSDF(wiLocal, sd.mtl, data); + return bsdf.evalPdf(wiLocal, woLocal); + } } BSDFProperties getProperties(const ShadingData sd) @@ -131,61 +147,61 @@ struct StandardMaterialInstance : IMaterialInstance /** Reference implementation that uses cosine-weighted hemisphere sampling. This is for testing purposes only. \param[in] sd Shading data. - \param[in] sg Sample generator. - \param[out] result Generated sample. Only valid if true is returned. + \param[in] wi Incident direction in local space. + \param[out] wo Outgoing direction in local space. + \param[out] pdf pdf with respect to solid angle for sampling outgoing direction wo (0 if a delta event is sampled). + \param[out] weight Sample weight f(wi, wo) * dot(wo, n) / pdf(wo). + \param[out] lobeType Sampled lobeType (see LobeType). + \param[in,out] sg Sample generator. \return True if a sample was generated, false otherwise. */ - bool sampleReference(const ShadingData sd, inout S sg, out BSDFSample result) + bool sampleReference(const ShadingData sd, const float3 wi, out float3 wo, out float pdf, out float3 weight, out uint lobeType, inout S sg) { const bool isTransmissive = (getLobeTypes(sd) & (uint)LobeType::Transmission) != 0; - float3 wiLocal = sf.toLocal(sd.V); - float3 woLocal = sample_cosine_hemisphere_concentric(sampleNext2D(sg), result.pdf); // pdf = cos(theta) / pi + wo = sample_cosine_hemisphere_concentric(sampleNext2D(sg), pdf); // pdf = cos(theta) / pi if (isTransmissive) { if (sampleNext1D(sg) < 0.5f) { - woLocal.z = -woLocal.z; + wo.z = -wo.z; } - result.pdf *= 0.5f; - if (min(abs(wiLocal.z), abs(woLocal.z)) < kMinCosTheta || result.pdf == 0.f) return false; + pdf *= 0.5f; + if (min(abs(wi.z), abs(wo.z)) < kMinCosTheta || pdf == 0.f) return false; } else { - if (min(wiLocal.z, woLocal.z) < kMinCosTheta || result.pdf == 0.f) return false; + if (min(wi.z, wo.z) < kMinCosTheta || pdf == 0.f) return false; } - StandardBSDF bsdf = StandardBSDF(wiLocal, sd.mtl, data); + StandardBSDF bsdf = StandardBSDF(wi, sd.mtl, data); - result.wo = sf.fromLocal(woLocal); - result.weight = bsdf.eval(wiLocal, woLocal, sg) / result.pdf; - result.lobeType = (uint)(woLocal.z > 0.f ? LobeType::DiffuseReflection : LobeType::DiffuseTransmission); + weight = bsdf.eval(wi, wo, sg) / pdf; + lobeType = (uint)(wo.z > 0.f ? LobeType::DiffuseReflection : LobeType::DiffuseTransmission); return true; } /** Evaluates the directional pdf for sampling the given direction using the reference implementation. \param[in] sd Shading data. - \param[in] wo Outgoing direction. + \param[in] wi Incident direction in local space. + \param[in] wo Outgoing direction in local space. \return PDF with respect to solid angle for sampling direction wo. */ - float evalPdfReference(const ShadingData sd, const float3 wo) + float evalPdfReference(const ShadingData sd, const float3 wi, const float3 wo) { const bool isTransmissive = (getLobeTypes(sd) & (uint)LobeType::Transmission) != 0; - float3 wiLocal = sf.toLocal(sd.V); - float3 woLocal = sf.toLocal(wo); - if (isTransmissive) { - if (min(abs(wiLocal.z), abs(woLocal.z)) < kMinCosTheta) return 0.f; - return 0.5f * woLocal.z * M_1_PI; // pdf = 0.5 * cos(theta) / pi + if (min(abs(wi.z), abs(wo.z)) < kMinCosTheta) return 0.f; + return 0.5f * wo.z * M_1_PI; // pdf = 0.5 * cos(theta) / pi } else { - if (min(wiLocal.z, woLocal.z) < kMinCosTheta) return 0.f; - return woLocal.z * M_1_PI; // pdf = cos(theta) / pi + if (min(wi.z, wo.z) < kMinCosTheta) return 0.f; + return wo.z * M_1_PI; // pdf = cos(theta) / pi } } diff --git a/Source/Falcor/Rendering/RTXDI/RTXDI.cpp b/Source/Falcor/Rendering/RTXDI/RTXDI.cpp index 7f28dacb9..ccde10abf 100644 --- a/Source/Falcor/Rendering/RTXDI/RTXDI.cpp +++ b/Source/Falcor/Rendering/RTXDI/RTXDI.cpp @@ -94,22 +94,17 @@ namespace Falcor if (value < minValue || value > maxValue) { logWarning("RTXDI: '{}' is {}. Clamping to [{},{}].", name, value, minValue, maxValue); - value = clamp(value, minValue, maxValue); + value = std::clamp(value, minValue, maxValue); } }; } - RTXDI::SharedPtr RTXDI::create(const Scene::SharedPtr& pScene, const Options& options) - { - return SharedPtr(new RTXDI(pScene, options)); - } - - RTXDI::RTXDI(const Scene::SharedPtr& pScene, const Options& options) + RTXDI::RTXDI(const ref& pScene, const Options& options) : mpScene(pScene) , mpDevice(mpScene->getDevice()) , mOptions(options) { - mpPixelDebug = PixelDebug::create(mpDevice); + mpPixelDebug = std::make_unique(mpDevice); FALCOR_ASSERT(pScene); setOptions(options); @@ -194,7 +189,7 @@ namespace Falcor if (mFrameIndex == 0) mPrevCameraData = mpScene->getCamera()->getData(); // Update the screen resolution. - if (frameDim != mFrameDim) + if (any(frameDim != mFrameDim)) { mFrameDim = frameDim; // Resizes require reallocating resources. @@ -255,7 +250,7 @@ namespace Falcor #endif } - void RTXDI::update(RenderContext* pRenderContext, const Texture::SharedPtr& pMotionVectors) + void RTXDI::update(RenderContext* pRenderContext, const ref& pMotionVectors) { #if FALCOR_HAS_RTXDI FALCOR_PROFILE(pRenderContext, "RTXDI::update"); @@ -303,7 +298,7 @@ namespace Falcor #if FALCOR_HAS_RTXDI - void RTXDI::setShaderDataInternal(const ShaderVar& rootVar, const Texture::SharedPtr& pMotionVectors) + void RTXDI::setShaderDataInternal(const ShaderVar& rootVar, const ref& pMotionVectors) { auto var = rootVar["gRTXDI"]; @@ -411,7 +406,7 @@ namespace Falcor // Create GPU buffer for holding light IDs. if (!mLights.analyticLightIDs.empty() && (!mpAnalyticLightIDBuffer || mpAnalyticLightIDBuffer->getElementCount() < mLights.analyticLightIDs.size())) { - mpAnalyticLightIDBuffer = Buffer::createStructured(mpDevice.get(), sizeof(uint32_t), (uint32_t)mLights.analyticLightIDs.size()); + mpAnalyticLightIDBuffer = Buffer::createStructured(mpDevice, sizeof(uint32_t), (uint32_t)mLights.analyticLightIDs.size()); } // Update GPU buffer. @@ -436,7 +431,7 @@ namespace Falcor // Allocate buffer for light infos. if (!mpLightInfoBuffer || mpLightInfoBuffer->getElementCount() < totalLightCount) { - mpLightInfoBuffer = Buffer::createStructured(mpDevice.get(), mpReflectTypes["lightInfo"], totalLightCount); + mpLightInfoBuffer = Buffer::createStructured(mpDevice, mpReflectTypes->getRootVar()["lightInfo"], totalLightCount); } // Allocate local light PDF texture, which RTXDI uses for importance sampling. @@ -445,7 +440,7 @@ namespace Falcor rtxdi::ComputePdfTextureSize(localLightCount, width, height, mipLevels); if (!mpLocalLightPdfTexture || mpLocalLightPdfTexture->getWidth() != width || mpLocalLightPdfTexture->getHeight() != height || mpLocalLightPdfTexture->getMipCount() != mipLevels) { - mpLocalLightPdfTexture = Texture::create2D(mpDevice.get(), width, height, + mpLocalLightPdfTexture = Texture::create2D(mpDevice, width, height, ResourceFormat::R16Float, 1, mipLevels, nullptr, Resource::BindFlags::ShaderResource | Resource::BindFlags::UnorderedAccess | Resource::BindFlags::RenderTarget); } @@ -472,7 +467,7 @@ namespace Falcor // Compute launch dimensions. uint2 threadCount = { 8192u, div_round_up(totalLightCount, 8192u) }; - auto var = mpUpdateLightsPass["gLightUpdater"]; + auto var = mpUpdateLightsPass->getRootVar()["gLightUpdater"]; var["lightInfo"] = mpLightInfoBuffer; var["localLightPdf"] = mpLocalLightPdfTexture; var["analyticLightIDs"] = mpAnalyticLightIDBuffer; @@ -485,7 +480,7 @@ namespace Falcor var["updateEmissiveLightsFlux"] = mFlags.updateEmissiveLightsFlux; var["updateAnalyticLights"] = mFlags.updateAnalyticLights; var["updateAnalyticLightsFlux"] = mFlags.updateAnalyticLightsFlux; - mpUpdateLightsPass["gScene"] = mpScene->getParameterBlock(); + mpUpdateLightsPass->getRootVar()["gScene"] = mpScene->getParameterBlock(); mpUpdateLightsPass->execute(pRenderContext, threadCount.x, threadCount.y); } @@ -525,7 +520,7 @@ namespace Falcor if (!pLuminanceTexture || pLuminanceTexture->getWidth() != width || pLuminanceTexture->getHeight() != height) { pLuminanceTexture = Texture::create2D( - mpDevice.get(), width, height, ResourceFormat::R32Float, 1, 1, nullptr, + mpDevice, width, height, ResourceFormat::R32Float, 1, 1, nullptr, Resource::BindFlags::ShaderResource | Resource::BindFlags::UnorderedAccess | Resource::BindFlags::RenderTarget); } @@ -533,16 +528,16 @@ namespace Falcor if (!pPdfTexture || pPdfTexture->getWidth() != width || pPdfTexture->getHeight() != height) { pPdfTexture = Texture::create2D( - mpDevice.get(), width, height, ResourceFormat::R32Float, 1, Resource::kMaxPossible, nullptr, + mpDevice, width, height, ResourceFormat::R32Float, 1, Resource::kMaxPossible, nullptr, Resource::BindFlags::ShaderResource | Resource::BindFlags::UnorderedAccess | Resource::BindFlags::RenderTarget); } // Update env light textures. - auto var = mpUpdateEnvLightPass["gEnvLightUpdater"]; + auto var = mpUpdateEnvLightPass->getRootVar()["gEnvLightUpdater"]; var["envLightLuminance"] = pLuminanceTexture; var["envLightPdf"] = pPdfTexture; var["texDim"] = uint2(width, height); - mpUpdateEnvLightPass["gScene"] = mpScene->getParameterBlock(); + mpUpdateEnvLightPass->getRootVar()["gScene"] = mpScene->getParameterBlock(); mpUpdateEnvLightPass->execute(pRenderContext, width, height); // Create a mipmap chain for pdf texure. @@ -622,7 +617,7 @@ namespace Falcor return inputID; } - uint32_t RTXDI::temporalResampling(RenderContext* pRenderContext, const Texture::SharedPtr& pMotionVectors, + uint32_t RTXDI::temporalResampling(RenderContext* pRenderContext, const ref& pMotionVectors, uint32_t candidateReservoirID, uint32_t lastFrameReservoirID) { FALCOR_PROFILE(pRenderContext, "temporalResampling"); @@ -642,7 +637,7 @@ namespace Falcor return outputReservoirID; } - uint32_t RTXDI::spatiotemporalResampling(RenderContext* pRenderContext, const Texture::SharedPtr& pMotionVectors, + uint32_t RTXDI::spatiotemporalResampling(RenderContext* pRenderContext, const ref& pMotionVectors, uint32_t candidateReservoirID, uint32_t lastFrameReservoirID) { FALCOR_PROFILE(pRenderContext, "spatiotemporalResampling"); @@ -685,7 +680,7 @@ namespace Falcor desc.setShaderModel(kShaderModel); desc.csEntry(entryPoint); desc.addTypeConformances(mpScene->getTypeConformances()); - ComputePass::SharedPtr pPass = ComputePass::create(mpDevice, desc, defines); + ref pPass = ComputePass::create(mpDevice, desc, defines); pPass->setVars(nullptr); pPass->getRootVar()["gScene"] = mpScene->getParameterBlock(); return pPass; @@ -736,7 +731,7 @@ namespace Falcor uint32_t lightTileSampleCount = std::max(mpRTXDIContext->GetRisBufferElementCount(), 1u); if (!mpLightTileBuffer || mpLightTileBuffer->getElementCount() < lightTileSampleCount) { - mpLightTileBuffer = Buffer::createTyped(mpDevice.get(), ResourceFormat::RG32Uint, lightTileSampleCount); + mpLightTileBuffer = Buffer::createTyped(mpDevice, ResourceFormat::RG32Uint, lightTileSampleCount); } // Allocate buffer for compact light info used to improve coherence for presampled light tiles. @@ -744,7 +739,7 @@ namespace Falcor uint32_t elementCount = lightTileSampleCount * 2; if (!mpCompactLightInfoBuffer || mpCompactLightInfoBuffer->getElementCount() < elementCount) { - mpCompactLightInfoBuffer = Buffer::createStructured(mpDevice.get(), mpReflectTypes["lightInfo"], elementCount); + mpCompactLightInfoBuffer = Buffer::createStructured(mpDevice, mpReflectTypes->getRootVar()["lightInfo"], elementCount); } } @@ -753,7 +748,7 @@ namespace Falcor uint32_t elementCount = mpRTXDIContext->GetReservoirBufferElementCount() * kMaxReservoirs; if (!mpReservoirBuffer || mpReservoirBuffer->getElementCount() < elementCount) { - mpReservoirBuffer = Buffer::createStructured(mpDevice.get(), mpReflectTypes["reservoirs"], elementCount); + mpReservoirBuffer = Buffer::createStructured(mpDevice, mpReflectTypes->getRootVar()["reservoirs"], elementCount); } } @@ -762,7 +757,7 @@ namespace Falcor uint32_t elementCount = 2 * mFrameDim.x * mFrameDim.y; if (!mpSurfaceDataBuffer || mpSurfaceDataBuffer->getElementCount() < elementCount) { - mpSurfaceDataBuffer = Buffer::createStructured(mpDevice.get(), mpReflectTypes["surfaceData"], elementCount); + mpSurfaceDataBuffer = Buffer::createStructured(mpDevice, mpReflectTypes->getRootVar()["surfaceData"], elementCount); } } @@ -772,7 +767,7 @@ namespace Falcor std::vector offsets(2 * (size_t)mRTXDIContextParams.NeighborOffsetCount); mpRTXDIContext->FillNeighborOffsetBuffer(offsets.data()); mpNeighborOffsetsBuffer = Buffer::createTyped( - mpDevice.get(), + mpDevice, ResourceFormat::RG8Snorm, mRTXDIContextParams.NeighborOffsetCount, Resource::BindFlags::ShaderResource | Resource::BindFlags::UnorderedAccess, diff --git a/Source/Falcor/Rendering/RTXDI/RTXDI.h b/Source/Falcor/Rendering/RTXDI/RTXDI.h index d533446dd..0b8f65d0e 100644 --- a/Source/Falcor/Rendering/RTXDI/RTXDI.h +++ b/Source/Falcor/Rendering/RTXDI/RTXDI.h @@ -84,8 +84,6 @@ namespace Falcor class FALCOR_API RTXDI { public: - using SharedPtr = std::shared_ptr; - /** RTXDI sampling modes. */ enum class Mode @@ -166,12 +164,11 @@ namespace Falcor */ static bool isInstalled() { return (bool)FALCOR_HAS_RTXDI; } - /** Create a new instance of the RTXDI sampler. + /** Constructor. \param[in] pScene Scene. \param[in] options Configuration options. - \return A new instance. */ - static SharedPtr create(const Scene::SharedPtr& pScene, const Options& options = Options()); + RTXDI(const ref& pScene, const Options& options = Options()); /** Set the configuration options. \param[in] options Configuration options. @@ -217,21 +214,19 @@ namespace Falcor \param[in] pRenderContext Render context. \param[in] pMotionVectors Motion vectors for temporal reprojection. */ - void update(RenderContext* pRenderContext, const Texture::SharedPtr& pMotionVectors); + void update(RenderContext* pRenderContext, const ref& pMotionVectors); /** Get the pixel debug component. \return Returns the pixel debug component. */ - const PixelDebug::SharedPtr& getPixelDebug() const { return mpPixelDebug; } + PixelDebug& getPixelDebug() { return *mpPixelDebug; } private: - RTXDI(const Scene::SharedPtr& pScene, const Options& options); - - Scene::SharedPtr mpScene; ///< Scene (set on initialization). - std::shared_ptr mpDevice; ///< GPU device. + ref mpScene; ///< Scene (set on initialization). + ref mpDevice; ///< GPU device. Options mOptions; ///< Configuration options. - PixelDebug::SharedPtr mpPixelDebug; ///< Pixel debug component. + std::unique_ptr mpPixelDebug; ///< Pixel debug component. // If the SDK is not installed, we leave out most of the implementation. @@ -293,51 +288,51 @@ namespace Falcor // Resources. - Buffer::SharedPtr mpAnalyticLightIDBuffer; ///< Buffer storing a list of analytic light IDs used in the scene. - Buffer::SharedPtr mpLightInfoBuffer; ///< Buffer storing information about all the lights in the scene. - Texture::SharedPtr mpLocalLightPdfTexture; ///< Texture storing the PDF for sampling local lights proportional to radiant flux. - Texture::SharedPtr mpEnvLightLuminanceTexture; ///< Texture storing luminance of the environment light. - Texture::SharedPtr mpEnvLightPdfTexture; ///< Texture storing the PDF for sampling the environment light proportional to luminance (times solid angle). + ref mpAnalyticLightIDBuffer; ///< Buffer storing a list of analytic light IDs used in the scene. + ref mpLightInfoBuffer; ///< Buffer storing information about all the lights in the scene. + ref mpLocalLightPdfTexture; ///< Texture storing the PDF for sampling local lights proportional to radiant flux. + ref mpEnvLightLuminanceTexture; ///< Texture storing luminance of the environment light. + ref mpEnvLightPdfTexture; ///< Texture storing the PDF for sampling the environment light proportional to luminance (times solid angle). - Buffer::SharedPtr mpLightTileBuffer; ///< Buffer storing precomputed light tiles (see presampleLights()). This is called "ris buffer" in RTXDI. - Buffer::SharedPtr mpCompactLightInfoBuffer; ///< Optional buffer storing compact light info for samples in the light tile buffer for improved coherence. + ref mpLightTileBuffer; ///< Buffer storing precomputed light tiles (see presampleLights()). This is called "ris buffer" in RTXDI. + ref mpCompactLightInfoBuffer; ///< Optional buffer storing compact light info for samples in the light tile buffer for improved coherence. - Buffer::SharedPtr mpReservoirBuffer; ///< Buffer storing light reservoirs between kernels (and between frames) - Buffer::SharedPtr mpSurfaceDataBuffer; ///< Buffer storing the surface data for the current and previous frames. - Buffer::SharedPtr mpNeighborOffsetsBuffer; ///< Buffer storing a poisson(-ish) distribution of offsets for sampling randomized neighbors. + ref mpReservoirBuffer; ///< Buffer storing light reservoirs between kernels (and between frames) + ref mpSurfaceDataBuffer; ///< Buffer storing the surface data for the current and previous frames. + ref mpNeighborOffsetsBuffer; ///< Buffer storing a poisson(-ish) distribution of offsets for sampling randomized neighbors. // Compute passes. // Passes to pipe data from Falcor into RTXDI. - ComputePass::SharedPtr mpReflectTypes; ///< Helper pass for reflecting type information. - ComputePass::SharedPtr mpUpdateLightsPass; ///< Update the light infos and light PDF texture. - ComputePass::SharedPtr mpUpdateEnvLightPass; ///< Update the environment light luminance and PDF texture. + ref mpReflectTypes; ///< Helper pass for reflecting type information. + ref mpUpdateLightsPass; ///< Update the light infos and light PDF texture. + ref mpUpdateEnvLightPass; ///< Update the environment light luminance and PDF texture. // Passes for all RTXDI modes. - ComputePass::SharedPtr mpPresampleLocalLightsPass; ///< Presample local lights into light tiles. - ComputePass::SharedPtr mpPresampleEnvLightPass; ///< Presample the environment light into light tiles. - ComputePass::SharedPtr mpGenerateCandidatesPass; ///< Generate initial candidates. - ComputePass::SharedPtr mpTestCandidateVisibilityPass; ///< Test visibility for selected candidate. + ref mpPresampleLocalLightsPass; ///< Presample local lights into light tiles. + ref mpPresampleEnvLightPass; ///< Presample the environment light into light tiles. + ref mpGenerateCandidatesPass; ///< Generate initial candidates. + ref mpTestCandidateVisibilityPass; ///< Test visibility for selected candidate. // Passes for various types of reuse. - ComputePass::SharedPtr mpSpatialResamplingPass; ///< Spatial only resampling. - ComputePass::SharedPtr mpTemporalResamplingPass; ///< Temporal only resampling. - ComputePass::SharedPtr mpSpatiotemporalResamplingPass; ///< Spatiotemporal resampling. + ref mpSpatialResamplingPass; ///< Spatial only resampling. + ref mpTemporalResamplingPass; ///< Temporal only resampling. + ref mpSpatiotemporalResamplingPass; ///< Spatiotemporal resampling. // Compute pass launches. - void setShaderDataInternal(const ShaderVar& rootVar, const Texture::SharedPtr& pMotionVectors); + void setShaderDataInternal(const ShaderVar& rootVar, const ref& pMotionVectors); void updateLights(RenderContext* pRenderContext); void updateEnvLight(RenderContext* pRenderContext); void presampleLights(RenderContext* pRenderContext); void generateCandidates(RenderContext* pRenderContext, uint32_t outputReservoirID); void testCandidateVisibility(RenderContext* pRenderContext, uint32_t candidateReservoirID); uint32_t spatialResampling(RenderContext* pRenderContext, uint32_t inputReservoirID); - uint32_t temporalResampling(RenderContext* pRenderContext, const Texture::SharedPtr& pMotionVectors, uint32_t candidateReservoirID, uint32_t lastFrameReservoirID); - uint32_t spatiotemporalResampling(RenderContext* pRenderContext, const Texture::SharedPtr& pMotionVectors, uint32_t candidateReservoirID, uint32_t lastFrameReservoirID); + uint32_t temporalResampling(RenderContext* pRenderContext, const ref& pMotionVectors, uint32_t candidateReservoirID, uint32_t lastFrameReservoirID); + uint32_t spatiotemporalResampling(RenderContext* pRenderContext, const ref& pMotionVectors, uint32_t candidateReservoirID, uint32_t lastFrameReservoirID); // Internal routines. diff --git a/Source/Falcor/Rendering/Utils/PixelStats.cpp b/Source/Falcor/Rendering/Utils/PixelStats.cpp index 805d6fc90..f319900a4 100644 --- a/Source/Falcor/Rendering/Utils/PixelStats.cpp +++ b/Source/Falcor/Rendering/Utils/PixelStats.cpp @@ -37,15 +37,27 @@ namespace Falcor namespace { const char kComputeRayCountFilename[] = "Rendering/Utils/PixelStats.cs.slang"; - } - PixelStats::SharedPtr PixelStats::create(std::shared_ptr pDevice) - { - return SharedPtr(new PixelStats(pDevice)); + pybind11::dict toPython(const PixelStats::Stats& stats) + { + pybind11::dict d; + d["visibilityRays"] = stats.visibilityRays; + d["closestHitRays"] = stats.closestHitRays; + d["totalRays"] = stats.totalRays; + d["pathVertices"] = stats.pathVertices; + d["volumeLookups"] = stats.volumeLookups; + d["avgVisibilityRays"] = stats.avgVisibilityRays; + d["avgClosestHitRays"] = stats.avgClosestHitRays; + d["avgTotalRays"] = stats.avgTotalRays; + d["avgPathLength"] = stats.avgPathLength; + d["avgPathVertices"] = stats.avgPathVertices; + d["avgVolumeLookups"] = stats.avgVolumeLookups; + return d; + } } - PixelStats::PixelStats(std::shared_ptr pDevice) - : mpDevice(std::move(pDevice)) + PixelStats::PixelStats(ref pDevice) + : mpDevice(pDevice) { mpComputeRayCount = ComputePass::create(mpDevice, kComputeRayCountFilename, "main"); } @@ -70,7 +82,7 @@ namespace Falcor if (!mpParallelReduction) { mpParallelReduction = std::make_unique(mpDevice); - mpReductionResult = Buffer::create(mpDevice.get(), (kRayTypeCount + 3) * sizeof(uint4), ResourceBindFlags::None, Buffer::CpuAccess::Read); + mpReductionResult = Buffer::create(mpDevice, (kRayTypeCount + 3) * sizeof(uint4), ResourceBindFlags::None, Buffer::CpuAccess::Read); } // Prepare stats buffers. @@ -78,11 +90,11 @@ namespace Falcor { for (uint32_t i = 0; i < kRayTypeCount; i++) { - mpStatsRayCount[i] = Texture::create2D(mpDevice.get(), frameDim.x, frameDim.y, ResourceFormat::R32Uint, 1, 1, nullptr, ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess); + mpStatsRayCount[i] = Texture::create2D(mpDevice, frameDim.x, frameDim.y, ResourceFormat::R32Uint, 1, 1, nullptr, ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess); } - mpStatsPathLength = Texture::create2D(mpDevice.get(), frameDim.x, frameDim.y, ResourceFormat::R32Uint, 1, 1, nullptr, ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess); - mpStatsPathVertexCount = Texture::create2D(mpDevice.get(), frameDim.x, frameDim.y, ResourceFormat::R32Uint, 1, 1, nullptr, ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess); - mpStatsVolumeLookupCount = Texture::create2D(mpDevice.get(), frameDim.x, frameDim.y, ResourceFormat::R32Uint, 1, 1, nullptr, ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess); + mpStatsPathLength = Texture::create2D(mpDevice, frameDim.x, frameDim.y, ResourceFormat::R32Uint, 1, 1, nullptr, ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess); + mpStatsPathVertexCount = Texture::create2D(mpDevice, frameDim.x, frameDim.y, ResourceFormat::R32Uint, 1, 1, nullptr, ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess); + mpStatsVolumeLookupCount = Texture::create2D(mpDevice, frameDim.x, frameDim.y, ResourceFormat::R32Uint, 1, 1, nullptr, ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess); } for (uint32_t i = 0; i < kRayTypeCount; i++) @@ -103,7 +115,7 @@ namespace Falcor if (mEnabled) { // Create fence first time we need it. - if (!mpFence) mpFence = GpuFence::create(mpDevice.get()); + if (!mpFence) mpFence = GpuFence::create(mpDevice); // Sum of the per-pixel counters. The results are copied to a GPU buffer. for (uint32_t i = 0; i < kRayTypeCount; i++) @@ -123,7 +135,7 @@ namespace Falcor } } - void PixelStats::prepareProgram(const Program::SharedPtr& pProgram, const ShaderVar& var) + void PixelStats::prepareProgram(const ref& pProgram, const ShaderVar& var) { FALCOR_ASSERT(mRunning); @@ -196,7 +208,7 @@ namespace Falcor return true; } - const Texture::SharedPtr PixelStats::getRayCountTexture(RenderContext* pRenderContext) + const ref PixelStats::getRayCountTexture(RenderContext* pRenderContext) { FALCOR_ASSERT(!mRunning); if (!mStatsBuffersValid) return nullptr; @@ -215,7 +227,7 @@ namespace Falcor FALCOR_ASSERT(mStatsBuffersValid); if (!mpStatsRayCountTotal || mpStatsRayCountTotal->getWidth() != mFrameDim.x || mpStatsRayCountTotal->getHeight() != mFrameDim.y) { - mpStatsRayCountTotal = Texture::create2D(mpDevice.get(), mFrameDim.x, mFrameDim.y, ResourceFormat::R32Uint, 1, 1, nullptr, ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess); + mpStatsRayCountTotal = Texture::create2D(mpDevice, mFrameDim.x, mFrameDim.y, ResourceFormat::R32Uint, 1, 1, nullptr, ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess); } auto var = mpComputeRayCount->getRootVar(); @@ -230,19 +242,19 @@ namespace Falcor mRayCountTextureValid = true; } - const Texture::SharedPtr PixelStats::getPathLengthTexture() const + const ref PixelStats::getPathLengthTexture() const { FALCOR_ASSERT(!mRunning); return mStatsBuffersValid ? mpStatsPathLength : nullptr; } - const Texture::SharedPtr PixelStats::getPathVertexCountTexture() const + const ref PixelStats::getPathVertexCountTexture() const { FALCOR_ASSERT(!mRunning); return mStatsBuffersValid ? mpStatsPathVertexCount : nullptr; } - const Texture::SharedPtr PixelStats::getVolumeLookupCountTexture() const + const ref PixelStats::getVolumeLookupCountTexture() const { FALCOR_ASSERT(!mRunning); return mStatsBuffersValid ? mpStatsVolumeLookupCount : nullptr; @@ -287,33 +299,14 @@ namespace Falcor } } - pybind11::dict PixelStats::Stats::toPython() const - { - pybind11::dict d; - - d["visibilityRays"] = visibilityRays; - d["closestHitRays"] = closestHitRays; - d["totalRays"] = totalRays; - d["pathVertices"] = pathVertices; - d["volumeLookups"] = volumeLookups; - d["avgVisibilityRays"] = avgVisibilityRays; - d["avgClosestHitRays"] = avgClosestHitRays; - d["avgTotalRays"] = avgTotalRays; - d["avgPathLength"] = avgPathLength; - d["avgPathVertices"] = avgPathVertices; - d["avgVolumeLookups"] = avgVolumeLookups; - - return d; - } - FALCOR_SCRIPT_BINDING(PixelStats) { - pybind11::class_ pixelStats(m, "PixelStats"); + pybind11::class_ pixelStats(m, "PixelStats"); pixelStats.def_property("enabled", &PixelStats::isEnabled, &PixelStats::setEnabled); pixelStats.def_property_readonly("stats", [](PixelStats* pPixelStats) { PixelStats::Stats stats; pPixelStats->getStats(stats); - return stats.toPython(); + return toPython(stats); }); } } diff --git a/Source/Falcor/Rendering/Utils/PixelStats.h b/Source/Falcor/Rendering/Utils/PixelStats.h index d7bd53e08..e54982fa2 100644 --- a/Source/Falcor/Rendering/Utils/PixelStats.h +++ b/Source/Falcor/Rendering/Utils/PixelStats.h @@ -31,7 +31,7 @@ #include "Core/API/Buffer.h" #include "Core/API/Texture.h" #include "Core/API/GpuFence.h" -#include "RenderGraph/BasePasses/ComputePass.h" +#include "Core/Pass/ComputePass.h" #include "Utils/UI/Gui.h" #include "Utils/Algorithm/ParallelReduction.h" #include @@ -60,16 +60,9 @@ namespace Falcor float avgPathLength = 0.f; float avgPathVertices = 0.f; float avgVolumeLookups = 0.f; - - /** Convert to python dict. - */ - pybind11::dict toPython() const; }; - using SharedPtr = std::shared_ptr; - virtual ~PixelStats() = default; - - static SharedPtr create(std::shared_ptr pDevice); + PixelStats(ref pDevice); void setEnabled(bool enabled) { mEnabled = enabled; } bool isEnabled() const { return mEnabled; } @@ -80,7 +73,7 @@ namespace Falcor /** Perform program specialization and bind resources. This call doesn't change any resource declarations in the program. */ - void prepareProgram(const Program::SharedPtr& pProgram, const ShaderVar& var); + void prepareProgram(const ref& pProgram, const ShaderVar& var); void renderUI(Gui::Widgets& widget); @@ -94,36 +87,35 @@ namespace Falcor \param[in] pRenderContext The render context. \return Texture in R32Uint format containing per-pixel ray counts, or nullptr if not available. */ - const Texture::SharedPtr getRayCountTexture(RenderContext* pRenderContext); + const ref getRayCountTexture(RenderContext* pRenderContext); /** Returns the per-pixel path length texture or nullptr if not available. \return Texture in R32Uint format containing per-pixel path length, or nullptr if not available. */ - const Texture::SharedPtr getPathLengthTexture() const; + const ref getPathLengthTexture() const; /** Returns the per-pixel path vertex count texture or nullptr if not available. \return Texture in R32Uint format containing per-pixel path vertex counts, or nullptr if not available. */ - const Texture::SharedPtr getPathVertexCountTexture() const; + const ref getPathVertexCountTexture() const; /** Returns the per-pixel volume lookup count texture or nullptr if not available. \return Texture in R32Uint format containing per-pixel volume lookup counts, or nullptr if not available. */ - const Texture::SharedPtr getVolumeLookupCountTexture() const; + const ref getVolumeLookupCountTexture() const; protected: - PixelStats(std::shared_ptr pDevice); void copyStatsToCPU(); void computeRayCountTexture(RenderContext* pRenderContext); static const uint32_t kRayTypeCount = (uint32_t)PixelStatsRayType::Count; - std::shared_ptr mpDevice; + ref mpDevice; // Internal state std::unique_ptr 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. + ref mpReductionResult; ///< Results buffer for stats readback (CPU mappable). + ref mpFence; ///< GPU fence for sychronizing readback. // Configuration bool mEnabled = false; ///< Enable pixel statistics. @@ -138,13 +130,13 @@ namespace Falcor bool mRayCountTextureValid = false; ///< True if total ray count texture is valid. Stats mStats; ///< Traversal stats. - Texture::SharedPtr mpStatsRayCount[kRayTypeCount]; ///< Buffers for per-pixel ray count stats. - Texture::SharedPtr mpStatsRayCountTotal; ///< Buffer for per-pixel total ray count. Only generated if getRayCountTexture() is called. - Texture::SharedPtr mpStatsPathLength; ///< Buffer for per-pixel path length stats. - Texture::SharedPtr mpStatsPathVertexCount; ///< Buffer for per-pixel path vertex count. - Texture::SharedPtr mpStatsVolumeLookupCount; ///< Buffer for per-pixel volume lookup count. + ref mpStatsRayCount[kRayTypeCount]; ///< Buffers for per-pixel ray count stats. + ref mpStatsRayCountTotal; ///< Buffer for per-pixel total ray count. Only generated if getRayCountTexture() is called. + ref mpStatsPathLength; ///< Buffer for per-pixel path length stats. + ref mpStatsPathVertexCount; ///< Buffer for per-pixel path vertex count. + ref mpStatsVolumeLookupCount; ///< Buffer for per-pixel volume lookup count. bool mStatsBuffersValid = false; ///< True if per-pixel stats buffers contain valid data. - ComputePass::SharedPtr mpComputeRayCount; ///< Pass for computing per-pixel total ray count. + ref mpComputeRayCount; ///< Pass for computing per-pixel total ray count. }; } diff --git a/Source/Falcor/Rendering/Volumes/GridVolumeSampler.cpp b/Source/Falcor/Rendering/Volumes/GridVolumeSampler.cpp index 9582ed6fd..97cda2689 100644 --- a/Source/Falcor/Rendering/Volumes/GridVolumeSampler.cpp +++ b/Source/Falcor/Rendering/Volumes/GridVolumeSampler.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -46,9 +46,11 @@ namespace Falcor }; } - GridVolumeSampler::SharedPtr GridVolumeSampler::create(RenderContext* pRenderContext, Scene::SharedPtr pScene, const Options& options) + GridVolumeSampler::GridVolumeSampler(RenderContext* pRenderContext, ref pScene, const Options& options) + : mpScene(pScene) + , mOptions(options) { - return SharedPtr(new GridVolumeSampler(pRenderContext, pScene, options)); + FALCOR_ASSERT(pScene); } Program::DefineList GridVolumeSampler::getDefines() const @@ -94,13 +96,6 @@ namespace Falcor return dirty; } - GridVolumeSampler::GridVolumeSampler(RenderContext* pRenderContext, Scene::SharedPtr pScene, const Options& options) - : mpScene(pScene) - , mOptions(options) - { - FALCOR_ASSERT(pScene); - } - FALCOR_SCRIPT_BINDING(GridVolumeSampler) { pybind11::enum_ transmittanceEstimator(m, "TransmittanceEstimator"); diff --git a/Source/Falcor/Rendering/Volumes/GridVolumeSampler.h b/Source/Falcor/Rendering/Volumes/GridVolumeSampler.h index db18cc8ba..c73264ff0 100644 --- a/Source/Falcor/Rendering/Volumes/GridVolumeSampler.h +++ b/Source/Falcor/Rendering/Volumes/GridVolumeSampler.h @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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,6 @@ #include "Core/Macros.h" #include "Utils/UI/Gui.h" #include "Scene/Scene.h" -#include namespace Falcor { @@ -43,8 +42,6 @@ namespace Falcor class FALCOR_API GridVolumeSampler { public: - using SharedPtr = std::shared_ptr; - /** Grid volume sampler configuration options. */ struct Options @@ -57,14 +54,13 @@ namespace Falcor Options() {} }; - virtual ~GridVolumeSampler() = default; - /** Create a new object. \param[in] pRenderContext A render-context that will be used for processing. \param[in] pScene The scene. \param[in] options Configuration options. */ - static SharedPtr create(RenderContext* pRenderContext, Scene::SharedPtr pScene, const Options& options = Options()); + GridVolumeSampler(RenderContext* pRenderContext, ref pScene, const Options& options = Options()); + virtual ~GridVolumeSampler() = default; /** Get a list of shader defines for using the grid volume sampler. \return Returns a list of defines. @@ -86,9 +82,7 @@ namespace Falcor const Options& getOptions() const { return mOptions; } protected: - GridVolumeSampler(RenderContext* pRenderContext, Scene::SharedPtr pScene, const Options& options); - - Scene::SharedPtr mpScene; ///< Scene. + ref mpScene; ///< Scene. Options mOptions; }; diff --git a/Source/Falcor/Rendering/Volumes/GridVolumeSampler.slang b/Source/Falcor/Rendering/Volumes/GridVolumeSampler.slang index f65c26f0a..1c07115ed 100644 --- a/Source/Falcor/Rendering/Volumes/GridVolumeSampler.slang +++ b/Source/Falcor/Rendering/Volumes/GridVolumeSampler.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -126,7 +126,7 @@ struct GridVolumeSampler float stepDDA(const float3 rayOrigin, const float3 invRayDir, const float3 pos, const int mip) { const float dim = 8 << mip; - const float3 tofs = invRayDir * (((invRayDir >= 0.f) ? dim + 0.5f : -0.5f) - rayOrigin); + const float3 tofs = invRayDir * (select(invRayDir >= 0.f, dim + 0.5f, -0.5f) - rayOrigin); const float3 tmax = floor(pos * (1.f / dim)) * dim * invRayDir + tofs; return min(tmax.x, min(tmax.y, tmax.z)); } diff --git a/Source/Falcor/Scene/Animation/Animatable.cpp b/Source/Falcor/Scene/Animation/Animatable.cpp index 04dda0994..5022ab899 100644 --- a/Source/Falcor/Scene/Animation/Animatable.cpp +++ b/Source/Falcor/Scene/Animation/Animatable.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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 { FALCOR_SCRIPT_BINDING(Animatable) { - pybind11::class_ animatable(m, "Animatable"); + pybind11::class_> animatable(m, "Animatable"); animatable.def_property_readonly("hasAnimation", &Animatable::hasAnimation); animatable.def_property("animated", &Animatable::isAnimated, &Animatable::setIsAnimated); } diff --git a/Source/Falcor/Scene/Animation/Animatable.h b/Source/Falcor/Scene/Animation/Animatable.h index 9dbc0aa7d..97d185a2d 100644 --- a/Source/Falcor/Scene/Animation/Animatable.h +++ b/Source/Falcor/Scene/Animation/Animatable.h @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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,6 +27,7 @@ **************************************************************************/ #pragma once #include "Core/Macros.h" +#include "Core/Object.h" #include "Scene/SceneIDs.h" #include "Utils/Math/Matrix.h" #include @@ -35,13 +36,9 @@ namespace Falcor { /** Represents an object that has a transform which can be animated using a scene graph node. */ - class FALCOR_API Animatable + class FALCOR_API Animatable : public Object { public: - // While this is an abstract base class, we still need a holder type (shared_ptr) - // for pybind11 bindings to work on inherited types. - using SharedPtr = std::shared_ptr; - virtual ~Animatable() {} /** Set if object has animation data. @@ -70,7 +67,7 @@ namespace Falcor /** Update the transform of the animatable object. */ - virtual void updateFromAnimation(const rmcv::mat4& transform) = 0; + virtual void updateFromAnimation(const float4x4& transform) = 0; protected: bool mHasAnimation = false; diff --git a/Source/Falcor/Scene/Animation/AnimatedVertexCache.cpp b/Source/Falcor/Scene/Animation/AnimatedVertexCache.cpp index ac1cda974..019f7278b 100644 --- a/Source/Falcor/Scene/Animation/AnimatedVertexCache.cpp +++ b/Source/Falcor/Scene/Animation/AnimatedVertexCache.cpp @@ -96,8 +96,8 @@ namespace Falcor } } - AnimatedVertexCache::AnimatedVertexCache(std::shared_ptr pDevice, Scene* pScene, const Buffer::SharedPtr& pPrevVertexData, std::vector&& cachedCurves, std::vector&& cachedMeshes) - : mpDevice(std::move(pDevice)) + AnimatedVertexCache::AnimatedVertexCache(ref pDevice, Scene* pScene, const ref& pPrevVertexData, std::vector&& cachedCurves, std::vector&& cachedMeshes) + : mpDevice(pDevice) , mpScene(pScene) , mpPrevVertexData(pPrevVertexData) , mCachedCurves(cachedCurves) @@ -140,11 +140,6 @@ namespace Falcor } } - AnimatedVertexCache::UniquePtr AnimatedVertexCache::create(std::shared_ptr pDevice, Scene* pScene, const Buffer::SharedPtr& pPrevVertexData, std::vector&& cachedCurves, std::vector&& cachedMeshes) - { - return UniquePtr(new AnimatedVertexCache(std::move(pDevice), pScene, pPrevVertexData, std::move(cachedCurves), std::move(cachedMeshes))); - } - bool AnimatedVertexCache::animate(RenderContext* pRenderContext, double time) { if (!hasAnimations()) return false; @@ -236,12 +231,12 @@ namespace Falcor mpCurveVertexBuffers.resize(mCurveKeyframeTimes.size()); for (uint32_t i = 0; i < mCurveKeyframeTimes.size(); i++) { - mpCurveVertexBuffers[i] = Buffer::createStructured(mpDevice.get(), sizeof(DynamicCurveVertexData), mCurveVertexCount, vbBindFlags, Buffer::CpuAccess::None, nullptr, false); + mpCurveVertexBuffers[i] = Buffer::createStructured(mpDevice, sizeof(DynamicCurveVertexData), mCurveVertexCount, vbBindFlags, Buffer::CpuAccess::None, nullptr, false); mpCurveVertexBuffers[i]->setName("AnimatedVertexCache::mpCurveVertexBuffers[" + std::to_string(i) + "]"); } // Create buffers for previous vertex positions. - mpPrevCurveVertexBuffer = Buffer::createStructured(mpDevice.get(), sizeof(DynamicCurveVertexData), mCurveVertexCount, vbBindFlags, Buffer::CpuAccess::None, nullptr, false); + mpPrevCurveVertexBuffer = Buffer::createStructured(mpDevice, sizeof(DynamicCurveVertexData), mCurveVertexCount, vbBindFlags, Buffer::CpuAccess::None, nullptr, false); mpPrevCurveVertexBuffer->setName("AnimatedVertexCache::mpPrevCurveVertexBuffer"); // Initialize vertex buffers with cached positions. @@ -284,7 +279,7 @@ namespace Falcor // Create curve index buffer. vbBindFlags = ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess; - mpCurveIndexBuffer = Buffer::create(mpDevice.get(), sizeof(uint32_t) * mCurveIndexCount, vbBindFlags); + mpCurveIndexBuffer = Buffer::create(mpDevice, sizeof(uint32_t) * mCurveIndexCount, vbBindFlags); mpCurveIndexBuffer->setName("AnimatedVertexCache::mpCurveIndexBuffer"); // Initialize index buffer. @@ -338,10 +333,10 @@ namespace Falcor mCurvePolyTubeIndexCount += curveMeta.indexCount; } - mpCurvePolyTubeCurveMetadataBuffer = Buffer::createStructured(mpDevice.get(), sizeof(PerCurveMetadata), (uint32_t)curveMetadata.size(), ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, curveMetadata.data(), false); + mpCurvePolyTubeCurveMetadataBuffer = Buffer::createStructured(mpDevice, sizeof(PerCurveMetadata), (uint32_t)curveMetadata.size(), ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, curveMetadata.data(), false); mpCurvePolyTubeCurveMetadataBuffer->setName("AnimatedVertexCache::mpCurvePolyTubeCurveMetadataBuffer"); - mpCurvePolyTubeMeshMetadataBuffer = Buffer::createStructured(mpDevice.get(), sizeof(PerMeshMetadata), (uint32_t)meshMetadata.size(), ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, meshMetadata.data(), false); + mpCurvePolyTubeMeshMetadataBuffer = Buffer::createStructured(mpDevice, sizeof(PerMeshMetadata), (uint32_t)meshMetadata.size(), ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, meshMetadata.data(), false); mpCurvePolyTubeMeshMetadataBuffer->setName("AnimatedVertexCache::mpCurvePolyTubeMeshMetadataBuffer"); // Create buffers for vertex positions in curve vertex caches. @@ -349,7 +344,7 @@ namespace Falcor mpCurvePolyTubeVertexBuffers.resize(mCurveKeyframeTimes.size()); for (uint32_t i = 0; i < mCurveKeyframeTimes.size(); i++) { - mpCurvePolyTubeVertexBuffers[i] = Buffer::createStructured(mpDevice.get(), sizeof(DynamicCurveVertexData), mCurvePolyTubeVertexCount, vbBindFlags, Buffer::CpuAccess::None, nullptr, false); + mpCurvePolyTubeVertexBuffers[i] = Buffer::createStructured(mpDevice, sizeof(DynamicCurveVertexData), mCurvePolyTubeVertexCount, vbBindFlags, Buffer::CpuAccess::None, nullptr, false); mpCurvePolyTubeVertexBuffers[i]->setName("AnimatedVertexCache::mpCurvePolyTubeVertexBuffers[" + std::to_string(i) + "]"); } @@ -390,7 +385,7 @@ namespace Falcor // Create curve strand index buffer. vbBindFlags = ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess; - mpCurvePolyTubeStrandIndexBuffer = Buffer::create(mpDevice.get(), sizeof(uint32_t) * mCurvePolyTubeVertexCount, vbBindFlags); + mpCurvePolyTubeStrandIndexBuffer = Buffer::create(mpDevice, sizeof(uint32_t) * mCurvePolyTubeVertexCount, vbBindFlags); mpCurvePolyTubeStrandIndexBuffer->setName("AnimatedVertexCache::mpCurvePolyTubeStrandIndexBuffer"); // Initialize strand index buffer. @@ -463,18 +458,18 @@ namespace Falcor { auto& data = cache.vertexData[i]; size_t index = keyframeOffset + i; - mpMeshVertexBuffers[index] = Buffer::createStructured(mpDevice.get(), sizeof(PackedStaticVertexData), (uint32_t)data.size(), ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, data.data(), false); + mpMeshVertexBuffers[index] = Buffer::createStructured(mpDevice, sizeof(PackedStaticVertexData), (uint32_t)data.size(), ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, data.data(), false); mpMeshVertexBuffers[index]->setName("AnimatedVertexCache::mpMeshVertexBuffers[" + std::to_string(index) + "]"); } keyframeOffset += (uint32_t)cache.timeSamples.size(); } - mpMeshMetadataBuffer = Buffer::createStructured(mpDevice.get(), sizeof(PerMeshMetadata), (uint32_t)meshMetadata.size(), ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, meshMetadata.data(), false); + mpMeshMetadataBuffer = Buffer::createStructured(mpDevice, sizeof(PerMeshMetadata), (uint32_t)meshMetadata.size(), ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, meshMetadata.data(), false); mpMeshMetadataBuffer->setName("AnimatedVertexCache::mpMeshMetadataBuffer"); mMeshInterpolationInfo.resize(mCachedMeshes.size()); - mpMeshInterpolationBuffer = Buffer::createStructured(mpDevice.get(), sizeof(InterpolationInfo), (uint32_t)mMeshInterpolationInfo.size(), ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, nullptr, false); + mpMeshInterpolationBuffer = Buffer::createStructured(mpDevice, sizeof(InterpolationInfo), (uint32_t)mMeshInterpolationInfo.size(), ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, nullptr, false); mpMeshInterpolationBuffer->setName("AnimatedVertexCache::mpMeshInterpolationbuffer"); } @@ -487,7 +482,7 @@ namespace Falcor mpMeshVertexUpdatePass = ComputePass::create(mpDevice, "Scene/Animation/UpdateMeshVertices.slang", "main", defines); // Bind data - auto block = mpMeshVertexUpdatePass->getVars()["gMeshVertexUpdater"]; + auto block = mpMeshVertexUpdatePass->getRootVar()["gMeshVertexUpdater"]; auto keyframesVar = block["meshPerKeyframe"]; for (size_t i = 0; i < mpMeshVertexBuffers.size(); i++) keyframesVar[i]["vertexData"] = mpMeshVertexBuffers[i]; @@ -504,7 +499,7 @@ namespace Falcor defines.add("CURVE_KEYFRAME_COUNT", std::to_string(mCurveKeyframeTimes.size())); mpCurveVertexUpdatePass = ComputePass::create(mpDevice, kUpdateCurveVerticesFilename, "main", defines); - auto block = mpCurveVertexUpdatePass->getVars()["gCurveVertexUpdater"]; + auto block = mpCurveVertexUpdatePass->getRootVar()["gCurveVertexUpdater"]; auto var = block["curvePerKeyframe"]; // Bind curve vertex data. @@ -517,7 +512,7 @@ namespace Falcor mpCurveAABBUpdatePass = ComputePass::create(mpDevice, kUpdateCurveAABBsFilename); - auto block = mpCurveAABBUpdatePass->getVars()["gCurveAABBUpdater"]; + auto block = mpCurveAABBUpdatePass->getRootVar()["gCurveAABBUpdater"]; block["curveIndexData"] = mpCurveIndexBuffer; } @@ -529,7 +524,7 @@ namespace Falcor defines.add("CURVE_KEYFRAME_COUNT", std::to_string(mCurveKeyframeTimes.size())); mpCurvePolyTubeVertexUpdatePass = ComputePass::create(mpDevice, kUpdateCurvePolyTubeVerticesFilename, "main", defines); - auto block = mpCurvePolyTubeVertexUpdatePass->getVars()["gCurvePolyTubeVertexUpdater"]; + auto block = mpCurvePolyTubeVertexUpdatePass->getRootVar()["gCurvePolyTubeVertexUpdater"]; block["perCurveData"] = mpCurvePolyTubeCurveMetadataBuffer; block["curveStrandIndexData"] = mpCurvePolyTubeStrandIndexBuffer; @@ -555,7 +550,7 @@ namespace Falcor mpMeshInterpolationBuffer->setBlob(mMeshInterpolationInfo.data(), 0, mpMeshInterpolationBuffer->getSize()); - auto block = mpMeshVertexUpdatePass->getVars()["gMeshVertexUpdater"]; + auto block = mpMeshVertexUpdatePass->getRootVar()["gMeshVertexUpdater"]; block["sceneVertexData"] = mpScene->getMeshVao()->getVertexBuffer(Scene::kStaticDataBufferIndex); block["copyPrev"] = copyPrev; @@ -568,7 +563,7 @@ namespace Falcor FALCOR_PROFILE(pRenderContext, "update curve vertices"); - auto block = mpCurveVertexUpdatePass->getVars()["gCurveVertexUpdater"]; + auto block = mpCurveVertexUpdatePass->getRootVar()["gCurveVertexUpdater"]; block["keyframeIndices"] = info.keyframeIndices; block["t"] = info.t; block["copyPrev"] = copyPrev; @@ -589,7 +584,7 @@ namespace Falcor FALCOR_PROFILE(pRenderContext, "update curve AABBs"); - auto block = mpCurveAABBUpdatePass->getVars()["gCurveAABBUpdater"]; + auto block = mpCurveAABBUpdatePass->getRootVar()["gCurveAABBUpdater"]; block["curveVertices"] = mpScene->mpCurveVao->getVertexBuffer(0); block["curveAABBs"].setUav(mpScene->mpRtAABBBuffer->getUAV(0, mCurveIndexCount)); @@ -607,7 +602,7 @@ namespace Falcor FALCOR_PROFILE(pRenderContext, "Update curve poly-tube vertices"); - auto block = mpCurvePolyTubeVertexUpdatePass->getVars()["gCurvePolyTubeVertexUpdater"]; + auto block = mpCurvePolyTubeVertexUpdatePass->getRootVar()["gCurvePolyTubeVertexUpdater"]; block["keyframeIndices"] = info.keyframeIndices; block["t"] = info.t; block["copyPrev"] = copyPrev; diff --git a/Source/Falcor/Scene/Animation/AnimatedVertexCache.h b/Source/Falcor/Scene/Animation/AnimatedVertexCache.h index 9404e85a9..b4c87c80b 100644 --- a/Source/Falcor/Scene/Animation/AnimatedVertexCache.h +++ b/Source/Falcor/Scene/Animation/AnimatedVertexCache.h @@ -29,15 +29,14 @@ #include "Animation.h" #include "SharedTypes.slang" #include "Core/API/Buffer.h" +#include "Core/Pass/ComputePass.h" #include "Scene/Curves/CurveConfig.h" #include "Scene/SceneTypes.slang" #include "Scene/SceneIDs.h" #include "Utils/Sampling/SampleGenerator.h" -#include "RenderGraph/BasePasses/ComputePass.h" #include #include -#include #include namespace Falcor @@ -46,7 +45,7 @@ namespace Falcor struct CachedCurve { - static const uint32_t kInvalidID = std::numeric_limits::max(); + static constexpr uint32_t kInvalidID = std::numeric_limits::max(); CurveTessellationMode tessellationMode = CurveTessellationMode::LinearSweptSphere; ///< Curve tessellation mode. CurveOrMeshID geometryID{ CurveOrMeshID::kInvalidID }; ///< ID of the curve or mesh this data is animating. @@ -74,12 +73,9 @@ namespace Falcor class FALCOR_API AnimatedVertexCache { public: - using UniquePtr = std::unique_ptr; - using UniqueConstPtr = std::unique_ptr; + AnimatedVertexCache(ref pDevice, Scene* pScene, const ref& pPrevVertexData, std::vector&& cachedCurves, std::vector&& cachedMeshes); ~AnimatedVertexCache() = default; - static UniquePtr create(std::shared_ptr pDevice, Scene* pScene, const Buffer::SharedPtr& pPrevVertexData, std::vector&& cachedCurves, std::vector&& cachedMeshes); - void setIsLooped(bool looped) { mLoopAnimations = looped; } bool isLooped() const { return mLoopAnimations; }; @@ -98,13 +94,11 @@ namespace Falcor void copyToPrevVertices(RenderContext* pContext); - Buffer::SharedPtr getPrevCurveVertexData() const { return mpPrevCurveVertexBuffer; } + ref getPrevCurveVertexData() const { return mpPrevCurveVertexBuffer; } uint64_t getMemoryUsageInBytes() const; private: - AnimatedVertexCache(std::shared_ptr pDevice, Scene* pScene, const Buffer::SharedPtr& pPrevVertexData, std::vector&& cachedCurves, std::vector&& cachedMeshes); - void initCurveKeyframes(); void bindCurveLSSBuffers(); void bindCurvePolyTubeBuffers(); @@ -130,13 +124,13 @@ namespace Falcor void executeCurvePolyTubeVertexUpdatePass(RenderContext* pContext, const InterpolationInfo& info, bool copyPrev = false); - std::shared_ptr mpDevice; + ref mpDevice; bool mLoopAnimations = true; double mGlobalCurveAnimationLength = 0; double mGlobalMeshAnimationLength = 0; Scene* mpScene = nullptr; - Buffer::SharedPtr mpPrevVertexData; ///< Owned by AnimationController + ref mpPrevVertexData; ///< Owned by AnimationController Animation::Behavior mPreInfinityBehavior = Animation::Behavior::Constant; // How the animation behaves before the first keyframe. std::vector mCachedCurves; @@ -145,40 +139,40 @@ namespace Falcor std::vector mCurveKeyframeTimes; // Cached curve (LSS) animation. - ComputePass::SharedPtr mpCurveVertexUpdatePass; - ComputePass::SharedPtr mpCurveAABBUpdatePass; + ref mpCurveVertexUpdatePass; + ref mpCurveAABBUpdatePass; uint32_t mCurveVertexCount = 0; uint32_t mCurveIndexCount = 0; uint32_t mCurveAABBOffset = 0; - std::vector mpCurveVertexBuffers; - Buffer::SharedPtr mpPrevCurveVertexBuffer; - Buffer::SharedPtr mpCurveIndexBuffer; + std::vector> mpCurveVertexBuffers; + ref mpPrevCurveVertexBuffer; + ref mpCurveIndexBuffer; // Cached curve (poly-tube mesh) animation. - ComputePass::SharedPtr mpCurvePolyTubeVertexUpdatePass; + ref mpCurvePolyTubeVertexUpdatePass; uint32_t mCurvePolyTubeVertexCount = 0; uint32_t mCurvePolyTubeIndexCount = 0; uint32_t mMaxCurvePolyTubeVertexCount = 0; ///< Greatest vertex count a curve has - std::vector mpCurvePolyTubeVertexBuffers; - Buffer::SharedPtr mpCurvePolyTubeStrandIndexBuffer; - Buffer::SharedPtr mpCurvePolyTubeCurveMetadataBuffer; - Buffer::SharedPtr mpCurvePolyTubeMeshMetadataBuffer; + std::vector> mpCurvePolyTubeVertexBuffers; + ref mpCurvePolyTubeStrandIndexBuffer; + ref mpCurvePolyTubeCurveMetadataBuffer; + ref mpCurvePolyTubeMeshMetadataBuffer; // Cached mesh animations - ComputePass::SharedPtr mpMeshVertexUpdatePass; + ref mpMeshVertexUpdatePass; std::vector mCachedMeshes; std::vector mMeshInterpolationInfo; uint32_t mMeshKeyframeCount = 0; ///< Total count of all keyframes for all meshes uint32_t mMaxMeshVertexCount = 0; ///< Greatest vertex count a mesh has - std::vector mpMeshVertexBuffers; - Buffer::SharedPtr mpMeshInterpolationBuffer; - Buffer::SharedPtr mpMeshMetadataBuffer; + std::vector> mpMeshVertexBuffers; + ref mpMeshInterpolationBuffer; + ref mpMeshMetadataBuffer; }; } diff --git a/Source/Falcor/Scene/Animation/Animation.cpp b/Source/Falcor/Scene/Animation/Animation.cpp index ae2a37cea..0312b8763 100644 --- a/Source/Falcor/Scene/Animation/Animation.cpp +++ b/Source/Falcor/Scene/Animation/Animation.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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,11 +27,10 @@ **************************************************************************/ #include "Animation.h" #include "AnimationController.h" +#include "Utils/ObjectIDPython.h" #include "Utils/Math/Common.h" #include "Utils/Scripting/ScriptBindings.h" #include "Scene/Transform.h" -#include -#include namespace Falcor { @@ -66,19 +65,19 @@ namespace Falcor } // Bezier hermite slerp - glm::quat interpolateHermite(const glm::quat& r0, const glm::quat& r1, const glm::quat& r2, const glm::quat& r3, float t) + quatf interpolateHermite(const quatf& r0, const quatf& r1, const quatf& r2, const quatf& r3, float t) { - glm::quat b0 = r1; - glm::quat b1 = r1 + (r2 - r0) * 0.5f / 3.0f; - glm::quat b2 = r2 - (r3 - r1) * 0.5f / 3.0f; - glm::quat b3 = r2; + quatf b0 = r1; + quatf b1 = r1 + (r2 - r0) * 0.5f / 3.0f; + quatf b2 = r2 - (r3 - r1) * 0.5f / 3.0f; + quatf b3 = r2; - glm::quat q0 = slerp(b0, b1, t); - glm::quat q1 = slerp(b1, b2, t); - glm::quat q2 = slerp(b2, b3, t); + quatf q0 = slerp(b0, b1, t); + quatf q1 = slerp(b1, b2, t); + quatf q2 = slerp(b2, b3, t); - glm::quat qq0 = slerp(q0, q1, t); - glm::quat qq1 = slerp(q1, q2, t); + quatf qq0 = slerp(q0, q1, t); + quatf qq1 = slerp(q1, q2, t); return slerp(qq0, qq1, t); } @@ -90,7 +89,7 @@ namespace Falcor result.translation = lerp(k0.translation, k1.translation, t); result.scaling = lerp(k0.scaling, k1.scaling, t); result.rotation = slerp(k0.rotation, k1.rotation, t); - result.time = glm::lerp(k0.time, k1.time, (double)t); + result.time = math::lerp(k0.time, k1.time, (double)t); return result; } @@ -101,23 +100,18 @@ namespace Falcor result.translation = interpolateHermite(k0.translation, k1.translation, k2.translation, k3.translation, t); result.scaling = lerp(k1.scaling, k2.scaling, t); result.rotation = interpolateHermite(k0.rotation, k1.rotation, k2.rotation, k3.rotation, t); - result.time = glm::lerp(k1.time, k2.time, (double)t); + result.time = math::lerp(k1.time, k2.time, (double)t); return result; } } - Animation::SharedPtr Animation::create(const std::string& name, NodeID nodeID, double duration) - { - return SharedPtr(new Animation(name, nodeID, duration)); - } - Animation::Animation(const std::string& name, NodeID nodeID, double duration) : mName(name) , mNodeID(nodeID) , mDuration(duration) {} - rmcv::mat4 Animation::animate(double currentTime) + float4x4 Animation::animate(double currentTime) { // Calculate the sample time. double time = currentTime; @@ -153,10 +147,10 @@ namespace Falcor interpolated = interpolate(mInterpolationMode, time); } - rmcv::mat4 T = rmcv::translate(interpolated.translation); - rmcv::mat4 R = rmcv::mat4_cast(interpolated.rotation); - rmcv::mat4 S = rmcv::scale(interpolated.scaling); - rmcv::mat4 transform = T * R * S; + float4x4 T = math::matrixFromTranslation(interpolated.translation); + float4x4 R = math::matrixFromQuat(interpolated.rotation); + float4x4 S = math::matrixFromScaling(interpolated.scaling); + float4x4 transform = mul(mul(T, R), S); return transform; } @@ -166,7 +160,7 @@ namespace Falcor FALCOR_ASSERT(!mKeyframes.empty()); // Validate cached frame index. - size_t frameIndex = clamp(mCachedFrameIndex, (size_t)0, mKeyframes.size() - 1); + size_t frameIndex = std::clamp(mCachedFrameIndex, (size_t)0, mKeyframes.size() - 1); if (time < mKeyframes[frameIndex].time) frameIndex = 0; // Find frame index. @@ -183,7 +177,7 @@ namespace Falcor auto adjacentFrame = [this] (size_t frame, int32_t offset = 1) { size_t count = mKeyframes.size(); - return mEnableWarping ? (frame + count + offset) % count : clamp(frame + offset, (size_t)0, count - 1); + return mEnableWarping ? (frame + count + offset) % count : std::clamp(frame + offset, (size_t)0, count - 1); }; if (mode == InterpolationMode::Linear || mKeyframes.size() < 4) @@ -196,7 +190,7 @@ namespace Falcor double segmentDuration = k1.time - k0.time; if (mEnableWarping && segmentDuration < 0.0) segmentDuration += mDuration; - float t = (float)clamp((segmentDuration > 0.0 ? (time - k0.time) / segmentDuration : 1.0), 0.0, 1.0); + float t = (float)std::clamp((segmentDuration > 0.0 ? (time - k0.time) / segmentDuration : 1.0), 0.0, 1.0); return interpolateLinear(k0, k1, t); } @@ -214,7 +208,7 @@ namespace Falcor double segmentDuration = k2.time - k1.time; if (mEnableWarping && segmentDuration < 0.0) segmentDuration += mDuration; - float t = (float)clamp(segmentDuration > 0.0 ? (time - k1.time) / segmentDuration : 1.0, 0.0, 1.0); + float t = (float)std::clamp(segmentDuration > 0.0 ? (time - k1.time) / segmentDuration : 1.0, 0.0, 1.0); return interpolateHermite(k0, k1, k2, k3, t); } @@ -241,7 +235,7 @@ namespace Falcor switch (behavior) { case Behavior::Constant: - modifiedTime = clamp(currentTime, firstKeyframeTime, lastKeyframeTime); + modifiedTime = std::clamp(currentTime, firstKeyframeTime, lastKeyframeTime); break; case Behavior::Cycle: // Calculate the relative time @@ -330,7 +324,18 @@ namespace Falcor FALCOR_SCRIPT_BINDING_DEPENDENCY(Transform) - pybind11::class_ animation(m, "Animation"); + pybind11::class_> animation(m, "Animation"); + + pybind11::enum_ interpolationMode(animation, "InterpolationMode"); + interpolationMode.value("Linear", Animation::InterpolationMode::Linear); + interpolationMode.value("Hermite", Animation::InterpolationMode::Hermite); + + pybind11::enum_ behavior(animation, "Behavior"); + behavior.value("Constant", Animation::Behavior::Constant); + behavior.value("Linear", Animation::Behavior::Linear); + behavior.value("Cycle", Animation::Behavior::Cycle); + behavior.value("Oscillate", Animation::Behavior::Oscillate); + animation.def_property_readonly("name", &Animation::getName); animation.def_property_readonly("nodeID", &Animation::getNodeID); animation.def_property_readonly("duration", &Animation::getDuration); @@ -343,15 +348,5 @@ namespace Falcor Animation::Keyframe keyframe{ time, transform.getTranslation(), transform.getScaling(), transform.getRotation() }; pAnimation->addKeyframe(keyframe); }); - - pybind11::enum_ interpolationMode(animation, "InterpolationMode"); - interpolationMode.value("Linear", Animation::InterpolationMode::Linear); - interpolationMode.value("Hermite", Animation::InterpolationMode::Hermite); - - pybind11::enum_ behavior(animation, "Behavior"); - behavior.value("Constant", Animation::Behavior::Constant); - behavior.value("Linear", Animation::Behavior::Linear); - behavior.value("Cycle", Animation::Behavior::Cycle); - behavior.value("Oscillate", Animation::Behavior::Oscillate); } } diff --git a/Source/Falcor/Scene/Animation/Animation.h b/Source/Falcor/Scene/Animation/Animation.h index fef61a119..dd281e919 100644 --- a/Source/Falcor/Scene/Animation/Animation.h +++ b/Source/Falcor/Scene/Animation/Animation.h @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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,11 @@ **************************************************************************/ #pragma once #include "Core/Macros.h" +#include "Core/Object.h" #include "Scene/SceneIDs.h" #include "Utils/Math/Vector.h" #include "Utils/Math/Matrix.h" +#include "Utils/Math/Quaternion.h" #include "Utils/UI/Gui.h" #include #include @@ -39,11 +41,9 @@ namespace Falcor { class AnimationController; - class FALCOR_API Animation + class FALCOR_API Animation : public Object { public: - using SharedPtr = std::shared_ptr; - enum class InterpolationMode { Linear, @@ -63,16 +63,17 @@ namespace Falcor double time = 0; float3 translation = float3(0, 0, 0); float3 scaling = float3(1, 1, 1); - glm::quat rotation = glm::quat(1, 0, 0, 0); + quatf rotation = quatf::identity(); }; + static ref create(const std::string& name, NodeID nodeID, double duration) { return make_ref(name, nodeID, duration); } + /** Create a new animation. \param[in] name Animation name. \param[in] nodeID ID of the animated node. \param[in] Animation duration in seconds. - \return Returns a new animation. */ - static SharedPtr create(const std::string& name, NodeID nodeID, double duration); + Animation(const std::string& name, NodeID nodeID, double duration); /** Get the animation name. */ @@ -145,15 +146,13 @@ namespace Falcor \param time The current time in seconds. This can be larger then the animation time, in which case the animation will loop. \return Returns the animation's transform matrix for the specified time. */ - rmcv::mat4 animate(double currentTime); + float4x4 animate(double currentTime); /* Render the UI. */ void renderUI(Gui::Widgets& widget); private: - Animation(const std::string& name, NodeID nodeID, double duration); - Keyframe interpolate(InterpolationMode mode, double time) const; double calcSampleTime(double currentTime); diff --git a/Source/Falcor/Scene/Animation/AnimationController.cpp b/Source/Falcor/Scene/Animation/AnimationController.cpp index 9900704b3..df1aa575c 100644 --- a/Source/Falcor/Scene/Animation/AnimationController.cpp +++ b/Source/Falcor/Scene/Animation/AnimationController.cpp @@ -41,8 +41,8 @@ namespace Falcor const std::string kPrevInverseTransposeWorldMatrices = "prevInverseTransposeWorldMatrices"; } - AnimationController::AnimationController(std::shared_ptr pDevice, Scene* pScene, const StaticVertexVector& staticVertexData, const SkinningVertexVector& skinningVertexData, uint32_t prevVertexCount, const std::vector& animations) - : mpDevice(std::move(pDevice)) + AnimationController::AnimationController(ref pDevice, Scene* pScene, const StaticVertexVector& staticVertexData, const SkinningVertexVector& skinningVertexData, uint32_t prevVertexCount, const std::vector>& animations) + : mpDevice(pDevice) , mAnimations(animations) , mNodesEdited(pScene->mSceneGraph.size()) , mLocalMatrices(pScene->mSceneGraph.size()) @@ -56,13 +56,13 @@ namespace Falcor if (!mLocalMatrices.empty()) { - mpWorldMatricesBuffer = Buffer::createStructured(mpDevice.get(), sizeof(float4x4), (uint32_t)mLocalMatrices.size(), Resource::BindFlags::ShaderResource, Buffer::CpuAccess::None, nullptr, false); + mpWorldMatricesBuffer = Buffer::createStructured(mpDevice, sizeof(float4x4), (uint32_t)mLocalMatrices.size(), Resource::BindFlags::ShaderResource, Buffer::CpuAccess::None, nullptr, false); mpWorldMatricesBuffer->setName("AnimationController::mpWorldMatricesBuffer"); - mpPrevWorldMatricesBuffer = Buffer::createStructured(mpDevice.get(), sizeof(float4x4), (uint32_t)mLocalMatrices.size(), Resource::BindFlags::ShaderResource, Buffer::CpuAccess::None, nullptr, false); + mpPrevWorldMatricesBuffer = Buffer::createStructured(mpDevice, sizeof(float4x4), (uint32_t)mLocalMatrices.size(), Resource::BindFlags::ShaderResource, Buffer::CpuAccess::None, nullptr, false); mpPrevWorldMatricesBuffer->setName("AnimationController::mpPrevWorldMatricesBuffer"); - mpInvTransposeWorldMatricesBuffer = Buffer::createStructured(mpDevice.get(), sizeof(float4x4), (uint32_t)mLocalMatrices.size(), Resource::BindFlags::ShaderResource, Buffer::CpuAccess::None, nullptr, false); + mpInvTransposeWorldMatricesBuffer = Buffer::createStructured(mpDevice, sizeof(float4x4), (uint32_t)mLocalMatrices.size(), Resource::BindFlags::ShaderResource, Buffer::CpuAccess::None, nullptr, false); mpInvTransposeWorldMatricesBuffer->setName("AnimationController::mpInvTransposeWorldMatricesBuffer"); - mpPrevInvTransposeWorldMatricesBuffer = Buffer::createStructured(mpDevice.get(), sizeof(float4x4), (uint32_t)mLocalMatrices.size(), Resource::BindFlags::ShaderResource, Buffer::CpuAccess::None, nullptr, false); + mpPrevInvTransposeWorldMatricesBuffer = Buffer::createStructured(mpDevice, sizeof(float4x4), (uint32_t)mLocalMatrices.size(), Resource::BindFlags::ShaderResource, Buffer::CpuAccess::None, nullptr, false); mpPrevInvTransposeWorldMatricesBuffer->setName("AnimationController::mpPrevInvTransposeWorldMatricesBuffer"); } @@ -79,7 +79,7 @@ namespace Falcor uint32_t staticIndex = skinningVertexData[i].staticIndex; prevVertexData[i].position = staticVertexData[staticIndex].position; } - mpPrevVertexData = Buffer::createStructured(mpDevice.get(), sizeof(PrevVertexData), prevVertexCount, ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess, Buffer::CpuAccess::None, prevVertexData.data(), false); + mpPrevVertexData = Buffer::createStructured(mpDevice, sizeof(PrevVertexData), prevVertexCount, ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess, Buffer::CpuAccess::None, prevVertexData.data(), false); mpPrevVertexData->setName("AnimationController::mpPrevVertexData"); } @@ -92,11 +92,6 @@ namespace Falcor } } - AnimationController::UniquePtr AnimationController::create(std::shared_ptr pDevice, Scene* pScene, const StaticVertexVector& staticVertexData, const SkinningVertexVector& skinningVertexData, uint32_t prevVertexCount, const std::vector& animations) - { - return UniquePtr(new AnimationController(std::move(pDevice), pScene, staticVertexData, skinningVertexData, prevVertexCount, animations)); - } - void AnimationController::addAnimatedVertexCaches(std::vector&& cachedCurves, std::vector&& cachedMeshes, const StaticVertexVector& staticVertexData) { size_t totalAnimatedMeshVertexCount = 0; @@ -161,7 +156,7 @@ namespace Falcor mpPrevVertexData->setBlob(prevVertexData.data(), byteOffset, prevVertexData.size() * sizeof(PrevVertexData)); } - mpVertexCache = AnimatedVertexCache::create(mpDevice, mpScene, mpPrevVertexData, std::move(cachedCurves), std::move(cachedMeshes)); + mpVertexCache = std::make_unique(mpDevice, mpScene, mpPrevVertexData, std::move(cachedCurves), std::move(cachedMeshes)); // Note: It is a workaround to have two pre-infinity behaviors for the cached animation. // We need `Cycle` behavior when the length of cached animation is smaller than the length of mesh animation (e.g., tiger forest). @@ -266,8 +261,8 @@ namespace Falcor { FALCOR_ASSERT(mpWorldMatricesBuffer && mpPrevWorldMatricesBuffer); FALCOR_ASSERT(mpInvTransposeWorldMatricesBuffer && mpPrevInvTransposeWorldMatricesBuffer); - swap(mpPrevWorldMatricesBuffer, mpWorldMatricesBuffer); - swap(mpPrevInvTransposeWorldMatricesBuffer, mpInvTransposeWorldMatricesBuffer); + std::swap(mpPrevWorldMatricesBuffer, mpWorldMatricesBuffer); + std::swap(mpPrevInvTransposeWorldMatricesBuffer, mpInvTransposeWorldMatricesBuffer); updateLocalMatrices(time); updateWorldMatrices(); uploadWorldMatrices(); @@ -320,14 +315,14 @@ namespace Falcor if (mpScene->mSceneGraph[i].parent != NodeID::Invalid()) { - mGlobalMatrices[i] = mGlobalMatrices[sceneGraph[i].parent.get()] * mGlobalMatrices[i]; + mGlobalMatrices[i] = mul(mGlobalMatrices[sceneGraph[i].parent.get()], mGlobalMatrices[i]); } mInvTransposeGlobalMatrices[i] = transpose(inverse(mGlobalMatrices[i])); if (mpSkinningPass) { - mSkinningMatrices[i] = mGlobalMatrices[i] * sceneGraph[i].localToBindSpace; + mSkinningMatrices[i] = mul(mGlobalMatrices[i], sceneGraph[i].localToBindSpace); mInvTransposeSkinningMatrices[i] = transpose(inverse(mSkinningMatrices[i])); } } @@ -400,7 +395,7 @@ namespace Falcor // We always copy the static data, to initialize the non-skinned vertices. FALCOR_ASSERT(mpScene->getMeshVao()); - const Buffer::SharedPtr& pVB = mpScene->getMeshVao()->getVertexBuffer(Scene::kStaticDataBufferIndex); + const ref& pVB = mpScene->getMeshVao()->getVertexBuffer(Scene::kStaticDataBufferIndex); FALCOR_ASSERT(pVB->getSize() == staticVertexData.size() * sizeof(staticVertexData[0])); pVB->setBlob(staticVertexData.data(), 0, pVB->getSize()); @@ -411,22 +406,22 @@ namespace Falcor mMeshBindMatrices.resize(mpScene->mSceneGraph.size()); mpSkinningPass = ComputePass::create(mpDevice, "Scene/Animation/Skinning.slang"); - auto block = mpSkinningPass->getVars()["gData"]; + auto block = mpSkinningPass->getRootVar()["gData"]; // Initialize mesh bind transforms std::vector meshInvBindMatrices(mMeshBindMatrices.size()); for (size_t i = 0; i < mpScene->mSceneGraph.size(); i++) { mMeshBindMatrices[i] = mpScene->mSceneGraph[i].meshBind; - meshInvBindMatrices[i] = rmcv::inverse(mMeshBindMatrices[i]); + meshInvBindMatrices[i] = inverse(mMeshBindMatrices[i]); } // Bind vertex data. FALCOR_ASSERT(staticVertexData.size() <= std::numeric_limits::max()); FALCOR_ASSERT(skinningVertexData.size() <= std::numeric_limits::max()); - mpStaticVertexData = Buffer::createStructured(mpDevice.get(), block["staticData"], (uint32_t)staticVertexData.size(), ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, staticVertexData.data(), false); + mpStaticVertexData = Buffer::createStructured(mpDevice, block["staticData"], (uint32_t)staticVertexData.size(), ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, staticVertexData.data(), false); mpStaticVertexData->setName("AnimationController::mpStaticVertexData"); - mpSkinningVertexData = Buffer::createStructured(mpDevice.get(), block["skinningData"], (uint32_t)skinningVertexData.size(), ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, skinningVertexData.data(), false); + mpSkinningVertexData = Buffer::createStructured(mpDevice, block["skinningData"], (uint32_t)skinningVertexData.size(), ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, skinningVertexData.data(), false); mpSkinningVertexData->setName("AnimationController::mpSkinningVertexData"); block["staticData"] = mpStaticVertexData; @@ -436,13 +431,13 @@ namespace Falcor // Bind transforms. FALCOR_ASSERT(mSkinningMatrices.size() < std::numeric_limits::max()); - mpMeshBindMatricesBuffer = Buffer::createStructured(mpDevice.get(), sizeof(float4x4), (uint32_t)mSkinningMatrices.size(), ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, mMeshBindMatrices.data(), false); + mpMeshBindMatricesBuffer = Buffer::createStructured(mpDevice, sizeof(float4x4), (uint32_t)mSkinningMatrices.size(), ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, mMeshBindMatrices.data(), false); mpMeshBindMatricesBuffer->setName("AnimationController::mpMeshBindMatricesBuffer"); - mpMeshInvBindMatricesBuffer = Buffer::createStructured(mpDevice.get(), sizeof(float4x4), (uint32_t)mSkinningMatrices.size(), ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, meshInvBindMatrices.data(), false); + mpMeshInvBindMatricesBuffer = Buffer::createStructured(mpDevice, sizeof(float4x4), (uint32_t)mSkinningMatrices.size(), ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, meshInvBindMatrices.data(), false); mpMeshInvBindMatricesBuffer->setName("AnimationController::mpMeshInvBindMatricesBuffer"); - mpSkinningMatricesBuffer = Buffer::createStructured(mpDevice.get(), sizeof(float4x4), (uint32_t)mSkinningMatrices.size(), ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, nullptr, false); + mpSkinningMatricesBuffer = Buffer::createStructured(mpDevice, sizeof(float4x4), (uint32_t)mSkinningMatrices.size(), ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, nullptr, false); mpSkinningMatricesBuffer->setName("AnimationController::mpSkinningMatricesBuffer"); - mpInvTransposeSkinningMatricesBuffer = Buffer::createStructured(mpDevice.get(), sizeof(float4x4), (uint32_t)mSkinningMatrices.size(), ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, nullptr, false); + mpInvTransposeSkinningMatricesBuffer = Buffer::createStructured(mpDevice, sizeof(float4x4), (uint32_t)mSkinningMatrices.size(), ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, nullptr, false); mpInvTransposeSkinningMatricesBuffer->setName("AnimationController::mpInvTransposeSkinningMatricesBuffer"); block["boneMatrices"].setBuffer(mpSkinningMatricesBuffer); @@ -464,7 +459,7 @@ namespace Falcor mpInvTransposeSkinningMatricesBuffer->setBlob(mInvTransposeSkinningMatrices.data(), 0, mpInvTransposeSkinningMatricesBuffer->getSize()); // Execute skinning pass. - auto vars = mpSkinningPass->getVars()["gData"]; + auto vars = mpSkinningPass->getRootVar()["gData"]; vars["inverseTransposeWorldMatrices"].setBuffer(mpInvTransposeWorldMatricesBuffer); vars["worldMatrices"].setBuffer(mpWorldMatricesBuffer); vars["initPrev"] = initPrev; diff --git a/Source/Falcor/Scene/Animation/AnimationController.h b/Source/Falcor/Scene/Animation/AnimationController.h index ab69e8932..9cf3d25c3 100644 --- a/Source/Falcor/Scene/Animation/AnimationController.h +++ b/Source/Falcor/Scene/Animation/AnimationController.h @@ -30,8 +30,8 @@ #include "AnimatedVertexCache.h" #include "Core/Macros.h" #include "Core/API/Buffer.h" +#include "Core/Pass/ComputePass.h" #include "Utils/Math/Matrix.h" -#include "RenderGraph/BasePasses/ComputePass.h" #include "Scene/SceneTypes.slang" #include #include @@ -43,18 +43,14 @@ namespace Falcor class FALCOR_API AnimationController { public: - using UniquePtr = std::unique_ptr; - using UniqueConstPtr = std::unique_ptr; - static const uint32_t kInvalidBoneID = -1; ~AnimationController() = default; using StaticVertexVector = std::vector; using SkinningVertexVector = std::vector; - /** Create a new object. - \return A new object, or throws an exception if creation failed. + /** Constructor. Throws an exception if creation failed. */ - static UniquePtr create(std::shared_ptr pDevice, Scene* pScene, const StaticVertexVector& staticVertexData, const SkinningVertexVector& skinningVertexData, uint32_t prevVertexCount, const std::vector& animations); + AnimationController(ref pDevice, Scene* pScene, const StaticVertexVector& staticVertexData, const SkinningVertexVector& skinningVertexData, uint32_t prevVertexCount, const std::vector>& animations); /** Add animated vertex caches (curves and meshes) to the controller. */ @@ -82,7 +78,7 @@ namespace Falcor /** Returns a list of all animations. */ - std::vector& getAnimations() { return mAnimations; } + std::vector>& getAnimations() { return mAnimations; } /** Enable/disable animations. */ @@ -135,12 +131,12 @@ namespace Falcor /** Get the previous vertex data buffer for dynamic meshes. \return Buffer containing the previous vertex data, or nullptr if no dynamic meshes exist. */ - Buffer::SharedPtr getPrevVertexData() const { return mpPrevVertexData; } + ref getPrevVertexData() const { return mpPrevVertexData; } /** Get the previous curve vertex data buffer for dynamic curves. \return Buffer containing the previous curve vertex data, or nullptr if no dynamic curves exist. */ - Buffer::SharedPtr getPrevCurveVertexData() const { return mpVertexCache ? mpVertexCache->getPrevCurveVertexData() : nullptr; } + ref getPrevCurveVertexData() const { return mpVertexCache ? mpVertexCache->getPrevCurveVertexData() : nullptr; } /** Get the total GPU memory usage in bytes. */ @@ -148,7 +144,6 @@ namespace Falcor private: friend class SceneBuilder; - AnimationController(std::shared_ptr pDevice, Scene* pScene, const StaticVertexVector& staticVertexData, const SkinningVertexVector& skinningVertexData, uint32_t prevVertexCount, const std::vector& animations); void initLocalMatrices(); void updateLocalMatrices(double time); @@ -160,10 +155,10 @@ namespace Falcor void createSkinningPass(const std::vector& staticVertexData, const SkinningVertexVector& skinningVertexData); void executeSkinningPass(RenderContext* pRenderContext, bool initPrev = false); - std::shared_ptr mpDevice; + ref mpDevice; // Animation - std::vector mAnimations; + std::vector> mAnimations; std::vector mNodesEdited; std::vector mLocalMatrices; std::vector mGlobalMatrices; @@ -180,27 +175,27 @@ namespace Falcor double mGlobalAnimationLength = 0; Scene* mpScene = nullptr; - Buffer::SharedPtr mpWorldMatricesBuffer; - Buffer::SharedPtr mpPrevWorldMatricesBuffer; - Buffer::SharedPtr mpInvTransposeWorldMatricesBuffer; - Buffer::SharedPtr mpPrevInvTransposeWorldMatricesBuffer; + ref mpWorldMatricesBuffer; + ref mpPrevWorldMatricesBuffer; + ref mpInvTransposeWorldMatricesBuffer; + ref mpPrevInvTransposeWorldMatricesBuffer; // Skinning - ComputePass::SharedPtr mpSkinningPass; + ref mpSkinningPass; std::vector mMeshBindMatrices; // Optimization TODO: These are only needed per mesh std::vector mSkinningMatrices; std::vector mInvTransposeSkinningMatrices; uint32_t mSkinningDispatchSize = 0; - Buffer::SharedPtr mpMeshBindMatricesBuffer; - Buffer::SharedPtr mpMeshInvBindMatricesBuffer; - Buffer::SharedPtr mpSkinningMatricesBuffer; - Buffer::SharedPtr mpInvTransposeSkinningMatricesBuffer; - Buffer::SharedPtr mpStaticVertexData; - Buffer::SharedPtr mpSkinningVertexData; - Buffer::SharedPtr mpPrevVertexData; + ref mpMeshBindMatricesBuffer; + ref mpMeshInvBindMatricesBuffer; + ref mpSkinningMatricesBuffer; + ref mpInvTransposeSkinningMatricesBuffer; + ref mpStaticVertexData; + ref mpSkinningVertexData; + ref mpPrevVertexData; // Animated vertex caches - AnimatedVertexCache::UniquePtr mpVertexCache; + std::unique_ptr mpVertexCache; }; } diff --git a/Source/Falcor/Scene/Camera/Camera.cpp b/Source/Falcor/Scene/Camera/Camera.cpp index 8a66c25b9..240bb799d 100644 --- a/Source/Falcor/Scene/Camera/Camera.cpp +++ b/Source/Falcor/Scene/Camera/Camera.cpp @@ -33,7 +33,6 @@ #include "Utils/UI/Gui.h" #include "Utils/Scripting/ScriptBindings.h" #include "Utils/Scripting/ScriptWriter.h" -#include namespace Falcor { @@ -47,19 +46,11 @@ namespace Falcor static_assert(sizeof(CameraData) % (sizeof(float4)) == 0, "CameraData size should be a multiple of 16"); - // Default dimensions of full frame cameras and 35mm film - const float Camera::kDefaultFrameHeight = 24.0f; - Camera::Camera(const std::string& name) : mName(name) { } - Camera::SharedPtr Camera::create(const std::string& name) - { - return SharedPtr(new Camera(name)); - } - Camera::Changes Camera::beginFrame(bool firstFrame) { if (mJitterPattern.pGenerator) @@ -80,9 +71,9 @@ namespace Falcor 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 (any(mPrevData.posW != mData.posW)) mChanges |= Changes::Movement; + if (any(mPrevData.up != mData.up)) mChanges |= Changes::Movement; + if (any(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; @@ -129,7 +120,7 @@ namespace Falcor } else { - mData.viewMat = rmcv::lookAt(mData.posW, mData.target, mData.up); + mData.viewMat = math::matrixFromLookAt(mData.posW, mData.target, mData.up, math::Handedness::RightHanded); } // if camera projection is set to be persistent, don't override it. @@ -141,32 +132,32 @@ namespace Falcor { if (fovY != 0.f) { - mData.projMat = rmcv::perspective(fovY, mData.aspectRatio, mData.nearZ, mData.farZ); + mData.projMat = math::perspective(fovY, mData.aspectRatio, mData.nearZ, mData.farZ); } else { // Take the length of look-at vector as half a viewport size const float halfLookAtLength = length(mData.posW - mData.target) * 0.5f; - mData.projMat = rmcv::ortho(-halfLookAtLength, halfLookAtLength, -halfLookAtLength, halfLookAtLength, mData.nearZ, mData.farZ); + mData.projMat = math::ortho(-halfLookAtLength, halfLookAtLength, -halfLookAtLength, halfLookAtLength, mData.nearZ, mData.farZ); } } // Build jitter matrix // (jitterX and jitterY are expressed as subpixel quantities divided by the screen resolution // for instance to apply an offset of half pixel along the X axis we set jitterX = 0.5f / Width) - rmcv::mat4 jitterMat = rmcv::translate(float3(2.0f * mData.jitterX, 2.0f * mData.jitterY, 0.0f)); + float4x4 jitterMat = math::matrixFromTranslation(float3(2.0f * mData.jitterX, 2.0f * mData.jitterY, 0.0f)); // Apply jitter matrix to the projection matrix - mData.viewProjMatNoJitter = mData.projMat * mData.viewMat; + mData.viewProjMatNoJitter = mul(mData.projMat, mData.viewMat); mData.projMatNoJitter = mData.projMat; - mData.projMat = jitterMat * mData.projMat; + mData.projMat = mul(jitterMat, mData.projMat); - mData.viewProjMat = mData.projMat * mData.viewMat; - mData.invViewProj = rmcv::inverse(mData.viewProjMat); + mData.viewProjMat = mul(mData.projMat, mData.viewMat); + mData.invViewProj = inverse(mData.viewProjMat); // Extract camera space frustum planes from the VP matrix // See: https://fgiesen.wordpress.com/2012/08/31/frustum-planes-from-the-projection-matrix/ - rmcv::mat4 tempMat = rmcv::transpose(mData.viewProjMat); + float4x4 tempMat = transpose(mData.viewProjMat); for (int i = 0; i < 6; i++) { float4 plane = (i & 1) ? tempMat.getCol(i >> 1) : -tempMat.getCol(i >> 1); @@ -175,15 +166,15 @@ namespace Falcor plane += tempMat.getCol(3); } - mFrustumPlanes[i].xyz = float3(plane); - mFrustumPlanes[i].sign = glm::sign(mFrustumPlanes[i].xyz); + mFrustumPlanes[i].xyz = plane.xyz(); + mFrustumPlanes[i].sign = math::sign(mFrustumPlanes[i].xyz); mFrustumPlanes[i].negW = -plane.w; } // Ray tracing related vectors - mData.cameraW = glm::normalize(mData.target - mData.posW) * mData.focalDistance; - mData.cameraU = glm::normalize(glm::cross(mData.cameraW, mData.up)); - mData.cameraV = glm::normalize(glm::cross(mData.cameraU, mData.cameraW)); + mData.cameraW = normalize(mData.target - mData.posW) * mData.focalDistance; + mData.cameraU = normalize(cross(mData.cameraW, mData.up)); + mData.cameraV = normalize(cross(mData.cameraU, mData.cameraW)); const float ulen = mData.focalDistance * std::tan(fovY * 0.5f) * mData.aspectRatio; mData.cameraU *= ulen; const float vlen = mData.focalDistance * std::tan(fovY * 0.5f); @@ -193,49 +184,49 @@ namespace Falcor } } - const rmcv::mat4 Camera::getViewMatrix() const + const float4x4 Camera::getViewMatrix() const { calculateCameraParameters(); return mData.viewMat; } - const rmcv::mat4 Camera::getPrevViewMatrix() const + const float4x4 Camera::getPrevViewMatrix() const { return mData.prevViewMat; } - const rmcv::mat4 Camera::getProjMatrix() const + const float4x4 Camera::getProjMatrix() const { calculateCameraParameters(); return mData.projMat; } - const rmcv::mat4 Camera::getViewProjMatrix() const + const float4x4 Camera::getViewProjMatrix() const { calculateCameraParameters(); return mData.viewProjMat; } - const rmcv::mat4 Camera::getViewProjMatrixNoJitter() const + const float4x4 Camera::getViewProjMatrixNoJitter() const { calculateCameraParameters(); return mData.viewProjMatNoJitter; } - const rmcv::mat4 Camera::getInvViewProjMatrix() const + const float4x4 Camera::getInvViewProjMatrix() const { calculateCameraParameters(); return mData.invViewProj; } - void Camera::setProjectionMatrix(const rmcv::mat4& proj) + void Camera::setProjectionMatrix(const float4x4& proj) { mDirty = true; mPersistentProjMat = proj; togglePersistentProjectionMatrix(true); } - void Camera::setViewMatrix(const rmcv::mat4& view) + void Camera::setViewMatrix(const float4x4& view) { mDirty = true; mPersistentViewMat = view; @@ -262,7 +253,7 @@ namespace Falcor for (int plane = 0; plane < 6; plane++) { float3 signedHalfExtent = 0.5f * box.extent() * mFrustumPlanes[plane].sign; - float dr = glm::dot(box.center() + signedHalfExtent, mFrustumPlanes[plane].xyz); + float dr = dot(box.center() + signedHalfExtent, mFrustumPlanes[plane].xyz); isInside = isInside && (dr > mFrustumPlanes[plane].negW); } @@ -275,7 +266,7 @@ namespace Falcor var["data"].setBlob(mData); } - void Camera::setPatternGenerator(const CPUSampleGenerator::SharedPtr& pGenerator, const float2& scale) + void Camera::setPatternGenerator(const ref& pGenerator, const float2& scale) { mJitterPattern.pGenerator = pGenerator; mJitterPattern.scale = scale; @@ -323,20 +314,20 @@ namespace Falcor float2 ndc = float2(2.0f, -2.0f) * p + float2(-1.0f, 1.0f); // Compute the normalized ray direction assuming a pinhole camera. - ray.dir = glm::normalize(ndc.x * mData.cameraU + ndc.y * mData.cameraV + mData.cameraW); + ray.dir = normalize(ndc.x * mData.cameraU + ndc.y * mData.cameraV + mData.cameraW); - float invCos = 1.f / glm::dot(glm::normalize(mData.cameraW), ray.dir); + float invCos = 1.f / dot(normalize(mData.cameraW), ray.dir); ray.tMin = mData.nearZ * invCos; ray.tMax = mData.farZ * invCos; return ray; } - void Camera::updateFromAnimation(const rmcv::mat4& transform) + void Camera::updateFromAnimation(const float4x4& transform) { - float3 up = float3(transform.getCol(1)); - float3 fwd = float3(-transform.getCol(2)); - float3 pos = float3(transform.getCol(3)); + float3 up = transform.getCol(1).xyz(); + float3 fwd = -transform.getCol(2).xyz(); + float3 pos = transform.getCol(3).xyz(); setUpVector(up); setPosition(pos); setTarget(pos + fwd); @@ -416,7 +407,7 @@ namespace Falcor FALCOR_SCRIPT_BINDING_DEPENDENCY(Animatable) - pybind11::class_ camera(m, "Camera"); + pybind11::class_> camera(m, "Camera"); camera.def_property("name", &Camera::getName, &Camera::setName); camera.def_property("aspectRatio", &Camera::getAspectRatio, &Camera::setAspectRatio); camera.def_property("focalLength", &Camera::getFocalLength, &Camera::setFocalLength); diff --git a/Source/Falcor/Scene/Camera/Camera.h b/Source/Falcor/Scene/Camera/Camera.h index 87f920a99..bb59cb8b7 100644 --- a/Source/Falcor/Scene/Camera/Camera.h +++ b/Source/Falcor/Scene/Camera/Camera.h @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -35,7 +35,6 @@ #include "Utils/SampleGenerators/CPUSampleGenerator.h" #include "Utils/UI/Gui.h" #include "Scene/Animation/Animatable.h" -#include #include namespace Falcor @@ -47,14 +46,12 @@ namespace Falcor class FALCOR_API Camera : public Animatable { public: - using SharedPtr = std::shared_ptr; - // Default dimensions of full frame cameras and 35mm film - static const float kDefaultFrameHeight; + static constexpr float kDefaultFrameHeight = 24.f; - /** Create a new camera object. - */ - static SharedPtr create(const std::string& name = ""); + static ref create(const std::string& name = "") { return make_ref(name); } + + Camera(const std::string& name); ~Camera() = default; /** Name the camera. @@ -177,11 +174,11 @@ namespace Falcor \param[in] pGenerator Sample generator. This may be nullptr. \param[in] scale Jitter scale. This should normally be 1.0 / frameDim. */ - void setPatternGenerator(const CPUSampleGenerator::SharedPtr& pGenerator, const float2& scale); + void setPatternGenerator(const ref& pGenerator, const float2& scale); /** Get the bound pattern generator */ - const CPUSampleGenerator::SharedPtr& getPatternGenerator() const { return mJitterPattern.pGenerator; } + const ref& getPatternGenerator() const { return mJitterPattern.pGenerator; } /** Set the camera's jitter. \param[in] jitterX Subpixel offset along X axis divided by screen width (positive value shifts the image right). @@ -208,35 +205,35 @@ namespace Falcor /** Get the view matrix. */ - const rmcv::mat4 getViewMatrix() const; + const float4x4 getViewMatrix() const; /** Get the previous frame view matrix, which possibly includes the previous frame's camera jitter. */ - const rmcv::mat4 getPrevViewMatrix() const; + const float4x4 getPrevViewMatrix() const; /** Get the projection matrix. */ - const rmcv::mat4 getProjMatrix() const; + const float4x4 getProjMatrix() const; /** Get the view-projection matrix. */ - const rmcv::mat4 getViewProjMatrix() const; + const float4x4 getViewProjMatrix() const; /** Get the view-projection matrix, without jittering. */ - const rmcv::mat4 getViewProjMatrixNoJitter() const; + const float4x4 getViewProjMatrixNoJitter() const; /** Get the inverse of the view-projection matrix. */ - const rmcv::mat4 getInvViewProjMatrix() const; + const float4x4 getInvViewProjMatrix() const; /** Set the persistent projection matrix and sets camera to use the persistent matrix instead of calculating the matrix from its other settings. */ - void setProjectionMatrix(const rmcv::mat4& proj); + void setProjectionMatrix(const float4x4& proj); /** Set the persistent view matrix and sets camera to use the persistent matrix instead of calculating the matrix from its other settings. */ - void setViewMatrix(const rmcv::mat4& view); + void setViewMatrix(const float4x4& view); /** Enable or disable usage of persistent projection matrix \param[in] persistent whether to set it persistent @@ -257,7 +254,7 @@ namespace Falcor */ const CameraData& getData() const { calculateCameraParameters(); return mData; } - void updateFromAnimation(const rmcv::mat4& transform) override; + void updateFromAnimation(const float4x4& transform) override; /** Render the UI */ @@ -289,14 +286,13 @@ namespace Falcor void dumpProperties(); private: - Camera(const std::string& name); Changes mChanges = Changes::None; mutable bool mDirty = true; mutable bool mEnablePersistentProjMat = false; mutable bool mEnablePersistentViewMat = false; - mutable rmcv::mat4 mPersistentProjMat; - mutable rmcv::mat4 mPersistentViewMat; + mutable float4x4 mPersistentProjMat; + mutable float4x4 mPersistentViewMat; std::string mName; bool mPreserveHeight = true; ///< If true, preserve frame height on change of aspect ratio. Otherwise, preserve width. @@ -314,7 +310,7 @@ namespace Falcor struct { - CPUSampleGenerator::SharedPtr pGenerator; + ref pGenerator; float2 scale; } mJitterPattern; diff --git a/Source/Falcor/Scene/Camera/CameraController.cpp b/Source/Falcor/Scene/Camera/CameraController.cpp index 6bb5df71c..0f00a1339 100644 --- a/Source/Falcor/Scene/Camera/CameraController.cpp +++ b/Source/Falcor/Scene/Camera/CameraController.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -62,7 +62,7 @@ namespace Falcor mModelCenter = center; mModelRadius = radius; mCameraDistance = distanceInRadius; - mRotation = rmcv::mat3(1.f); + mRotation = float3x3::identity(); mbDirty = true; } @@ -95,9 +95,9 @@ namespace Falcor if(mIsLeftButtonDown) { float3 curVec = project2DCrdToUnitSphere(convertCamPosRange(mouseEvent.pos)); - glm::quat q = createQuaternionFromVectors(mLastVector, curVec); - rmcv::mat3 rot = rmcv::mat3_cast(q); - mRotation = rot * mRotation; + quatf q = quatFromRotationBetweenVectors(mLastVector, curVec); + float3x3 rot = math::matrixFromQuat(q); + mRotation = mul(rot, mRotation); mbDirty = true; mLastVector = curVec; handled = true; @@ -121,11 +121,11 @@ namespace Falcor float3 camPos = mModelCenter; // tdavidovic: Why do we multiply the rotation matrix from the left (i.e., as if we multiplied by a transpose?) - camPos += (float3(0,0,1) * mRotation) * mModelRadius * mCameraDistance; + camPos += mul(float3(0,0,1), mRotation) * mModelRadius * mCameraDistance; mpCamera->setPosition(camPos); float3 up(0, 1, 0); - up = up * mRotation; + up = mul(up, mRotation); mpCamera->setUpVector(up); return true; } @@ -140,7 +140,7 @@ namespace Falcor } template - FirstPersonCameraControllerCommon::FirstPersonCameraControllerCommon(const Camera::SharedPtr& pCamera) : CameraController(pCamera) + FirstPersonCameraControllerCommon::FirstPersonCameraControllerCommon(const ref& pCamera) : CameraController(pCamera) { mTimer.update(); } @@ -254,26 +254,26 @@ namespace Falcor float3 camTarget = mpCamera->getTarget(); float3 camUp = b6DoF ? mpCamera->getUpVector() : getUpVector(); - float3 viewDir = glm::normalize(camTarget - camPos); + float3 viewDir = normalize(camTarget - camPos); if (mIsLeftButtonDown || anyGamepadRotation) { - float3 sideway = glm::cross(viewDir, normalize(camUp)); + float3 sideway = cross(viewDir, normalize(camUp)); float2 mouseRotation = mIsLeftButtonDown ? mMouseDelta * mSpeedModifier : float2(0.f); float2 gamepadRotation = anyGamepadRotation ? mGamepadRightStick * kGamepadRotationSpeed * elapsedTime : float2(0.f); float2 rotation = mouseRotation + gamepadRotation; // Rotate around x-axis - glm::quat qy = glm::angleAxis(rotation.y, sideway); - rmcv::mat3 rotY(rmcv::mat3_cast(qy)); - viewDir = viewDir * rotY; - camUp = camUp * rotY; + quatf qy = math::quatFromAngleAxis(rotation.y, sideway); + float3x3 rotY = math::matrixFromQuat(qy); + viewDir = mul(viewDir, rotY); + camUp = mul(camUp, rotY); // Rotate around y-axis - glm::quat qx = glm::angleAxis(rotation.x, camUp); - rmcv::mat3 rotX(rmcv::mat3_cast(qx)); - viewDir = viewDir * rotX; + quatf qx = math::quatFromAngleAxis(rotation.x, camUp); + float3x3 rotX = math::matrixFromQuat(qx); + viewDir = mul(viewDir, rotX); mpCamera->setTarget(camPos + viewDir); mpCamera->setUpVector(camUp); @@ -283,9 +283,9 @@ namespace Falcor if (b6DoF && mIsRightButtonDown) { // Rotate around x-axis - glm::quat q = glm::angleAxis(mMouseDelta.x * mSpeedModifier, viewDir); - rmcv::mat3 rot(rmcv::mat3_cast(q)); - camUp = camUp * rot; + quatf q = math::quatFromAngleAxis(mMouseDelta.x * mSpeedModifier, viewDir); + float3x3 rot = math::matrixFromQuat(q); + camUp = mul(camUp, rot); mpCamera->setUpVector(camUp); dirty = true; } @@ -316,7 +316,7 @@ namespace Falcor float3 camUp = mpCamera->getUpVector(); float3 viewDir = normalize(camTarget - camPos); - float3 sideway = glm::cross(viewDir, normalize(camUp)); + float3 sideway = cross(viewDir, normalize(camUp)); float curMove = mSpeedModifier * mSpeed * elapsedTime; camPos += movement.z * curMove * viewDir; diff --git a/Source/Falcor/Scene/Camera/CameraController.h b/Source/Falcor/Scene/Camera/CameraController.h index 2c2579399..8c64c4b99 100644 --- a/Source/Falcor/Scene/Camera/CameraController.h +++ b/Source/Falcor/Scene/Camera/CameraController.h @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -45,8 +45,6 @@ namespace Falcor class FALCOR_API CameraController { public: - using SharedPtr = std::shared_ptr; - enum class UpDirection { XPos, XNeg, YPos, YNeg, ZPos, ZNeg, @@ -98,11 +96,11 @@ namespace Falcor void setCameraBounds(const AABB& aabb) { mBounds = aabb; } protected: - CameraController(const Camera::SharedPtr& pCamera) : mpCamera(pCamera) {} + CameraController(const ref& pCamera) : mpCamera(pCamera) {} float3 getUpVector() const; - Camera::SharedPtr mpCamera = nullptr; + ref mpCamera; UpDirection mUpDirection = UpDirection::YPos; float mSpeed = 1; AABB mBounds; @@ -116,12 +114,7 @@ namespace Falcor class FALCOR_API OrbiterCameraController : public CameraController { public: - using SharedPtr = std::shared_ptr; - OrbiterCameraController(const Camera::SharedPtr& pCamera) : CameraController(pCamera) {} - - /** Create a new object - */ - static SharedPtr create(const Camera::SharedPtr& pCamera) { return SharedPtr(new OrbiterCameraController(pCamera)); } + OrbiterCameraController(const ref& pCamera) : CameraController(pCamera) {} /** Handle mouse events */ @@ -149,7 +142,7 @@ namespace Falcor float mCameraDistance; bool mbDirty; - rmcv::mat3 mRotation; + float3x3 mRotation = float3x3::identity(); float3 mLastVector; bool mIsLeftButtonDown = false; bool mShouldRotate = false; @@ -169,12 +162,7 @@ namespace Falcor class FALCOR_API FirstPersonCameraControllerCommon : public CameraController { public: - FirstPersonCameraControllerCommon(const Camera::SharedPtr& pCamera); - using SharedPtr = std::shared_ptr; - - /** Create a new object - */ - static SharedPtr create(const Camera::SharedPtr& pCamera) { return SharedPtr(new FirstPersonCameraControllerCommon(pCamera)); } + FirstPersonCameraControllerCommon(const ref& pCamera); /** Handle mouse events */ diff --git a/Source/Falcor/Scene/Curves/CurveTessellation.cpp b/Source/Falcor/Scene/Curves/CurveTessellation.cpp index 1ca535102..081b4aa7a 100644 --- a/Source/Falcor/Scene/Curves/CurveTessellation.cpp +++ b/Source/Falcor/Scene/Curves/CurveTessellation.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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,8 +30,8 @@ #include "Utils/Math/Common.h" #include "Utils/Math/MathHelpers.h" #include "Utils/Math/CubicSpline.h" -#include "Utils/Math/Matrix/Matrix.h" -#include +#include "Utils/Math/Matrix.h" +#include "Utils/Math/Quaternion.h" #include namespace Falcor @@ -74,20 +74,20 @@ namespace Falcor // To achieve curveWidth on average, however, we need to scale the initial curveWidth by 1.11 (the number was deducted numerically). const float kMeshCompensationScale = 1.11f; - float4 transformSphere(const rmcv::mat4& xform, const float4& sphere) + float4 transformSphere(const float4x4& xform, const float4& sphere) { // Spheres are represented as (center.x, center.y, center.z, radius). // Assume the scaling is isotropic, i.e., the end points are still spheres after transformation. #if 1 float scale = std::sqrt(xform[0][0] * xform[0][0] + xform[0][1] * xform[0][1] + xform[0][2] * xform[0][2]); - float3 xyz = xform * float4(sphere.xyz, 1.f); + float3 xyz = transformPoint(xform, sphere.xyz()); return float4(xyz, sphere.w * scale); #else - float3 q = sphere.xyz + float3(sphere.w, 0, 0); - float4 xp = xform * float4(sphere.xyz, 1.f); + float3 q = sphere.xyz() + float3(sphere.w, 0, 0); + float4 xp = xform * float4(sphere.xyz(), 1.f); float4 xq = xform * float4(q, 1.f); - float xr = glm::length(xq.xyz - xp.xyz); - return float4(xp.xyz, xr); + float xr = length(xq.xyz() - xp.xyz()); + return float4(xp.xyz(), xr); #endif } @@ -100,7 +100,7 @@ namespace Falcor // Optimize geometry by removing duplicates. for (uint32_t j = 0; j < strandArrays.vertexCount - 1; j++) { - if (curveArrays.controlPoints[pointOffset + j] != curveArrays.controlPoints[pointOffset + j + 1]) + if (any(curveArrays.controlPoints[pointOffset + j] != curveArrays.controlPoints[pointOffset + j + 1])) { strandArrays.controlPoints.push_back(curveArrays.controlPoints[pointOffset + j]); strandArrays.widths.push_back(curveArrays.widths[pointOffset + j]); @@ -186,9 +186,9 @@ namespace Falcor } // Use quaternions to smoothly rotate the other vectors and update s & t vectors. - glm::quat rotQuat = glm::rotation(prevFwd, fwd); - s = glm::rotate(rotQuat, s); - t = glm::rotate(rotQuat, t); + quatf rotQuat = math::quatFromRotationBetweenVectors(prevFwd, fwd); + s = mul(rotQuat, s); + t = mul(rotQuat, t); } void updateMeshResultBuffers(CurveTessellation::MeshResult& result, const CurveArrays& curveArrays, StrandArrays& optimizedStrandArrays, const float3& fwd, const float3& s, const float3& t, uint32_t pointCountPerCrossSection, const float& widthScale, uint32_t j) @@ -229,7 +229,7 @@ namespace Falcor } } - CurveTessellation::SweptSphereResult CurveTessellation::convertToLinearSweptSphere(uint32_t strandCount, const uint32_t* vertexCountsPerStrand, const float3* controlPoints, const float* widths, const float2* UVs, uint32_t degree, uint32_t subdivPerSegment, uint32_t keepOneEveryXStrands, uint32_t keepOneEveryXVerticesPerStrand, float widthScale, const rmcv::mat4& xform) + CurveTessellation::SweptSphereResult CurveTessellation::convertToLinearSweptSphere(uint32_t strandCount, const uint32_t* vertexCountsPerStrand, const float3* controlPoints, const float* widths, const float2* UVs, uint32_t degree, uint32_t subdivPerSegment, uint32_t keepOneEveryXStrands, uint32_t keepOneEveryXVerticesPerStrand, float widthScale, const float4x4& xform) { SweptSphereResult result; @@ -289,7 +289,7 @@ namespace Falcor // Pre-transform curve points. float4 sph = transformSphere(xform, float4(splinePoints.interpolate(j, t), splineWidths.interpolate(j, t) * 0.5f * widthScale)); - result.points.push_back(sph.xyz); + result.points.push_back(sph.xyz()); result.radius.push_back(sph.w); } tmpCount++; @@ -298,7 +298,7 @@ namespace Falcor // Always keep the last vertex. float4 sph = transformSphere(xform, float4(splinePoints.interpolate(optimizedStrandArrays.vertexCount - 2, 1.f), splineWidths.interpolate(optimizedStrandArrays.vertexCount - 2, 1.f) * 0.5f * widthScale)); - result.points.push_back(sph.xyz); + result.points.push_back(sph.xyz()); result.radius.push_back(sph.w); // Texture coordinates. diff --git a/Source/Falcor/Scene/Curves/CurveTessellation.h b/Source/Falcor/Scene/Curves/CurveTessellation.h index 0da9122e1..6e2c98d0d 100644 --- a/Source/Falcor/Scene/Curves/CurveTessellation.h +++ b/Source/Falcor/Scene/Curves/CurveTessellation.h @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -61,7 +61,7 @@ namespace Falcor \param[in] xform Row-major 4x4 transformation matrix. We apply pre-transformation to curve geometry. \return Linear swept sphere segments. */ - static SweptSphereResult convertToLinearSweptSphere(uint32_t strandCount, const uint32_t* vertexCountsPerStrand, const float3* controlPoints, const float* widths, const float2* UVs, uint32_t degree, uint32_t subdivPerSegment, uint32_t keepOneEveryXStrands, uint32_t keepOneEveryXVerticesPerStrand, float widthScale, const rmcv::mat4& xform); + static SweptSphereResult convertToLinearSweptSphere(uint32_t strandCount, const uint32_t* vertexCountsPerStrand, const float3* controlPoints, const float* widths, const float2* UVs, uint32_t degree, uint32_t subdivPerSegment, uint32_t keepOneEveryXStrands, uint32_t keepOneEveryXVerticesPerStrand, float widthScale, const float4x4& xform); // Tessellated mesh diff --git a/Source/Falcor/Scene/HitInfo.h b/Source/Falcor/Scene/HitInfo.h index 1afaf9b4c..690214956 100644 --- a/Source/Falcor/Scene/HitInfo.h +++ b/Source/Falcor/Scene/HitInfo.h @@ -47,8 +47,8 @@ namespace Falcor class FALCOR_API HitInfo { public: - static const uint32_t kMaxPackedSizeInBytes = 16; - static const ResourceFormat kDefaultFormat = ResourceFormat::RGBA32Uint; + static constexpr uint32_t kMaxPackedSizeInBytes = 16; + static constexpr ResourceFormat kDefaultFormat = ResourceFormat::RGBA32Uint; HitInfo() = default; HitInfo(const Scene& scene, bool useCompression = false) { init(scene, useCompression); } diff --git a/Source/Falcor/Scene/Importer.h b/Source/Falcor/Scene/Importer.h index a28645e24..2ff82f85b 100644 --- a/Source/Falcor/Scene/Importer.h +++ b/Source/Falcor/Scene/Importer.h @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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,14 @@ #include "Core/Errors.h" #include "Core/Plugin.h" #include "Core/Platform/OS.h" -#include "Utils/Scripting/Dictionary.h" #include #include #include #include #include +#include + namespace Falcor { /** Exception thrown during scene import. @@ -102,7 +103,7 @@ namespace Falcor \param[in] dict Optional dictionary. Throws an ImporterError if something went wrong. */ - virtual void importScene(const std::filesystem::path& path, SceneBuilder& builder, const Dictionary& dict) = 0; + virtual void importScene(const std::filesystem::path& path, SceneBuilder& builder, const pybind11::dict& dict) = 0; // Importer factory diff --git a/Source/Falcor/Scene/Lights/EnvMap.cpp b/Source/Falcor/Scene/Lights/EnvMap.cpp index 0dbffea5a..a89b022ee 100644 --- a/Source/Falcor/Scene/Lights/EnvMap.cpp +++ b/Source/Falcor/Scene/Lights/EnvMap.cpp @@ -29,23 +29,21 @@ #include "Core/API/Device.h" #include "Core/Program/ShaderVar.h" #include "Utils/Scripting/ScriptBindings.h" -#include "Scene/SceneBuilderAccess.h" -#include -#include +#include "GlobalState.h" namespace Falcor { - EnvMap::SharedPtr EnvMap::create(std::shared_ptr pDevice, const Texture::SharedPtr& pTexture) + ref EnvMap::create(ref pDevice, const ref& pTexture) { - return SharedPtr(new EnvMap(std::move(pDevice), pTexture)); + return ref(new EnvMap(pDevice, pTexture)); } - EnvMap::SharedPtr EnvMap::createFromFile(std::shared_ptr pDevice, const std::filesystem::path& path) + ref EnvMap::createFromFile(ref pDevice, const std::filesystem::path& path) { // Load environment map from file. Set it to generate mips and use linear color. - auto pTexture = Texture::createFromFile(pDevice.get(), path, true, false); + auto pTexture = Texture::createFromFile(pDevice, path, true, false); if (!pTexture) return nullptr; - return create(std::move(pDevice), pTexture); + return create(pDevice, pTexture); } void EnvMap::renderUI(Gui::Widgets& widgets) @@ -59,14 +57,14 @@ namespace Falcor void EnvMap::setRotation(float3 degreesXYZ) { - if (degreesXYZ != mRotation) + if (any(degreesXYZ != mRotation)) { mRotation = degreesXYZ; - rmcv::mat4 transform = rmcv::eulerAngleXYZ(glm::radians(mRotation.x), glm::radians(mRotation.y), glm::radians(mRotation.z)); + float4x4 transform = math::matrixFromRotationXYZ(math::radians(mRotation.x), math::radians(mRotation.y), math::radians(mRotation.z)); mData.transform = transform; - mData.invTransform = rmcv::inverse(transform); + mData.invTransform = inverse(transform); } } @@ -98,7 +96,7 @@ namespace Falcor if (mData.transform != mPrevData.transform) mChanges |= Changes::Transform; if (mData.intensity != mPrevData.intensity) mChanges |= Changes::Intensity; - if (mData.tint != mPrevData.tint) mChanges |= Changes::Intensity; + if (any(mData.tint != mPrevData.tint)) mChanges |= Changes::Intensity; mPrevData = mData; @@ -110,8 +108,8 @@ namespace Falcor return mpEnvMap ? mpEnvMap->getTextureSizeInBytes() : 0; } - EnvMap::EnvMap(std::shared_ptr pDevice, const Texture::SharedPtr& pTexture) - : mpDevice(std::move(pDevice)) + EnvMap::EnvMap(ref pDevice, const ref& pTexture) + : mpDevice(pDevice) { checkArgument(mpDevice != nullptr, "'pDevice' must be a valid device"); checkArgument(pTexture != nullptr, "'pTexture' must be a valid texture"); @@ -123,16 +121,16 @@ namespace Falcor 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(mpDevice.get(), samplerDesc); + mpEnvSampler = Sampler::create(mpDevice, samplerDesc); } FALCOR_SCRIPT_BINDING(EnvMap) { using namespace pybind11::literals; - pybind11::class_ envMap(m, "EnvMap"); + pybind11::class_> envMap(m, "EnvMap"); auto createFromFile = [](const std::filesystem::path &path) { - return EnvMap::createFromFile(getActivePythonSceneBuilder().getDevice(), path); + return EnvMap::createFromFile(accessActivePythonSceneBuilder().getDevice(), path); }; envMap.def(pybind11::init(createFromFile), "path"_a); // PYTHONDEPRECATED envMap.def_static("createFromFile", createFromFile, "path"_a); diff --git a/Source/Falcor/Scene/Lights/EnvMap.h b/Source/Falcor/Scene/Lights/EnvMap.h index a8fdd0494..4369ee8dd 100644 --- a/Source/Falcor/Scene/Lights/EnvMap.h +++ b/Source/Falcor/Scene/Lights/EnvMap.h @@ -28,6 +28,7 @@ #pragma once #include "EnvMapData.slang" #include "Core/Macros.h" +#include "Core/Object.h" #include "Core/API/Texture.h" #include "Core/API/Sampler.h" #include "Utils/Math/Vector.h" @@ -42,25 +43,23 @@ namespace Falcor /** Environment map based radiance probe. Utily class for evaluating radiance stored in an lat-long environment map. */ - class FALCOR_API EnvMap + class FALCOR_API EnvMap : public Object { public: - using SharedPtr = std::shared_ptr; - virtual ~EnvMap() = default; /** Create a new environment map. \param[in] pDevice GPU device. \param[in] texture The environment map texture. */ - static SharedPtr create(std::shared_ptr pDevice, const Texture::SharedPtr& texture); + static ref create(ref pDevice, const ref& texture); /** Create a new environment map from file. \param[in] pDevice GPU device. \param[in] path The environment map texture file path. \return A new object, or nullptr if the environment map failed to load. */ - static SharedPtr createFromFile(std::shared_ptr pDevice, const std::filesystem::path& path); + static ref createFromFile(ref pDevice, const std::filesystem::path& path); /** Render the GUI. */ @@ -68,7 +67,7 @@ namespace Falcor /** Set rotation angles. Rotation is applied as rotation around Z, Y and X axes, in that order. - Note that glm::extractEulerAngleXYZ() may be used to extract these angles from + Note that math::extractEulerAngleXYZ() may be used to extract these angles from a transformation matrix. \param[in] degreesXYZ Rotation angles in degrees for XYZ. */ @@ -98,8 +97,8 @@ namespace Falcor */ const std::filesystem::path& getPath() const { return mpEnvMap->getSourcePath(); } - const Texture::SharedPtr& getEnvMap() const { return mpEnvMap; } - const Sampler::SharedPtr& getEnvSampler() const { return mpEnvSampler; } + const ref& getEnvMap() const { return mpEnvMap; } + const ref& getEnvSampler() const { return mpEnvSampler; } /** Bind the environment map to a given shader variable. \param[in] var Shader variable. @@ -126,11 +125,11 @@ namespace Falcor uint64_t getMemoryUsageInBytes() const; protected: - EnvMap(std::shared_ptr pDevice, const Texture::SharedPtr& texture); + EnvMap(ref pDevice, const ref& texture); - std::shared_ptr mpDevice; - Texture::SharedPtr mpEnvMap; ///< Loaded environment map (RGB). - Sampler::SharedPtr mpEnvSampler; + ref mpDevice; + ref mpEnvMap; ///< Loaded environment map (RGB). + ref mpEnvSampler; EnvMapData mData; EnvMapData mPrevData; diff --git a/Source/Falcor/Scene/Lights/Light.cpp b/Source/Falcor/Scene/Lights/Light.cpp index 2844135b8..ee9cdfdc8 100644 --- a/Source/Falcor/Scene/Lights/Light.cpp +++ b/Source/Falcor/Scene/Lights/Light.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -56,17 +56,17 @@ namespace Falcor { mChanges = Changes::None; if (mActiveChanged) mChanges |= Changes::Active; - 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 (any(mPrevData.posW != mData.posW)) mChanges |= Changes::Position; + if (any(mPrevData.dirW != mData.dirW)) mChanges |= Changes::Direction; + if (any(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.cosSubtendedAngle != mData.cosSubtendedAngle) mChanges |= Changes::SurfaceArea; if (mPrevData.surfaceArea != mData.surfaceArea) mChanges |= Changes::SurfaceArea; if (mPrevData.transMat != mData.transMat) mChanges |= (Changes::Position | Changes::Direction); - FALCOR_ASSERT(mPrevData.tangent == mData.tangent); - FALCOR_ASSERT(mPrevData.bitangent == mData.bitangent); + FALCOR_ASSERT(all(mPrevData.tangent == mData.tangent)); + FALCOR_ASSERT(all(mPrevData.bitangent == mData.bitangent)); mPrevData = mData; mActiveChanged = false; @@ -87,7 +87,7 @@ namespace Falcor float3 Light::getColorForUI() { - if ((mUiLightIntensityColor * mUiLightIntensityScale) != mData.intensity) + if (any((mUiLightIntensityColor * mUiLightIntensityScale) != mData.intensity)) { float mag = std::max(mData.intensity.x, std::max(mData.intensity.y, mData.intensity.z)); if (mag <= 1.f) @@ -113,7 +113,7 @@ namespace Falcor float Light::getIntensityForUI() { - if ((mUiLightIntensityColor * mUiLightIntensityScale) != mData.intensity) + if (any((mUiLightIntensityColor * mUiLightIntensityScale) != mData.intensity)) { float mag = std::max(mData.intensity.x, std::max(mData.intensity.y, mData.intensity.z)); if (mag <= 1.f) @@ -165,11 +165,6 @@ namespace Falcor // PointLight - PointLight::SharedPtr PointLight::create(const std::string& name) - { - return SharedPtr(new PointLight(name)); - } - PointLight::PointLight(const std::string& name) : Light(name, LightType::Point) { @@ -178,7 +173,7 @@ namespace Falcor void PointLight::setWorldDirection(const float3& dir) { - if (!(glm::length(dir) > 0.f)) // NaNs propagate + if (!(length(dir) > 0.f)) // NaNs propagate { logWarning("Can't set light direction to zero length vector. Ignoring call."); return; @@ -211,7 +206,7 @@ namespace Falcor void PointLight::setOpeningAngle(float openingAngle) { - openingAngle = glm::clamp(openingAngle, 0.f, (float)M_PI); + openingAngle = std::clamp(openingAngle, 0.f, (float)M_PI); if (openingAngle == mData.openingAngle) return; mData.openingAngle = openingAngle; @@ -223,15 +218,15 @@ namespace Falcor void PointLight::setPenumbraAngle(float angle) { - angle = glm::clamp(angle, 0.0f, mData.openingAngle); + angle = std::clamp(angle, 0.0f, mData.openingAngle); if (mData.penumbraAngle == angle) return; mData.penumbraAngle = angle; } - void PointLight::updateFromAnimation(const rmcv::mat4& transform) + void PointLight::updateFromAnimation(const float4x4& transform) { - float3 fwd = float3(-transform.getCol(2)); - float3 pos = float3(transform.getCol(3)); + float3 fwd = -transform.getCol(2).xyz(); + float3 pos = transform.getCol(3).xyz(); setWorldPosition(pos); setWorldDirection(fwd); } @@ -244,11 +239,6 @@ namespace Falcor mPrevData = mData; } - DirectionalLight::SharedPtr DirectionalLight::create(const std::string& name) - { - return SharedPtr(new DirectionalLight(name)); - } - void DirectionalLight::renderUI(Gui::Widgets& widget) { Light::renderUI(widget); @@ -261,7 +251,7 @@ namespace Falcor void DirectionalLight::setWorldDirection(const float3& dir) { - if (!(glm::length(dir) > 0.f)) // NaNs propagate + if (!(length(dir) > 0.f)) // NaNs propagate { logWarning("Can't set light direction to zero length vector. Ignoring call."); return; @@ -269,19 +259,14 @@ namespace Falcor mData.dirW = normalize(dir); } - void DirectionalLight::updateFromAnimation(const rmcv::mat4& transform) + void DirectionalLight::updateFromAnimation(const float4x4& transform) { - float3 fwd = float3(-transform.getCol(2)); + float3 fwd = -transform.getCol(2).xyz(); setWorldDirection(fwd); } // DistantLight - DistantLight::SharedPtr DistantLight::create(const std::string& name) - { - return SharedPtr(new DistantLight(name)); - } - DistantLight::DistantLight(const std::string& name) : Light(name, LightType::Distant) { @@ -309,14 +294,14 @@ namespace Falcor void DistantLight::setAngle(float angle) { - mAngle = glm::clamp(angle, 0.f, (float)M_PI_2); + mAngle = std::clamp(angle, 0.f, (float)M_PI_2); mData.cosSubtendedAngle = std::cos(mAngle); } void DistantLight::setWorldDirection(const float3& dir) { - if (!(glm::length(dir) > 0.f)) // NaNs propagate + if (!(length(dir) > 0.f)) // NaNs propagate { logWarning("Can't set light direction to zero length vector. Ignoring call."); return; @@ -330,23 +315,23 @@ namespace Falcor // Update transformation matrices // Assumes that mData.dirW is normalized const float3 up(0.f, 0.f, 1.f); - float3 vec = glm::cross(up, -mData.dirW); - float sinTheta = glm::length(vec); + float3 vec = cross(up, -mData.dirW); + float sinTheta = length(vec); if (sinTheta > 0.f) { - float cosTheta = glm::dot(up, -mData.dirW); - mData.transMat = rmcv::rotate(rmcv::mat4(), std::acos(cosTheta), vec); + float cosTheta = dot(up, -mData.dirW); + mData.transMat = math::matrixFromRotation(std::acos(cosTheta), vec); } else { - mData.transMat = rmcv::mat4(1.f); + mData.transMat = float4x4::identity(); } - mData.transMatIT = rmcv::inverse(rmcv::transpose(mData.transMat)); + mData.transMatIT = inverse(transpose(mData.transMat)); } - void DistantLight::updateFromAnimation(const rmcv::mat4& transform) + void DistantLight::updateFromAnimation(const float4x4& transform) { - float3 fwd = float3(-transform.getCol(2)); + float3 fwd = -transform.getCol(2).xyz(); setWorldDirection(fwd); } @@ -372,57 +357,42 @@ namespace Falcor void AnalyticAreaLight::update() { // Update matrix - mData.transMat = mTransformMatrix * rmcv::scale(rmcv::mat4(), mScaling); - mData.transMatIT = rmcv::inverse(rmcv::transpose(mData.transMat)); + mData.transMat = mul(mTransformMatrix, math::matrixFromScaling(mScaling)); + mData.transMatIT = inverse(transpose(mData.transMat)); } // RectLight - RectLight::SharedPtr RectLight::create(const std::string& name) - { - return SharedPtr(new RectLight(name)); - } - void RectLight::update() { AnalyticAreaLight::update(); - float rx = glm::length(mData.transMat * float4(1.0f, 0.0f, 0.0f, 0.0f)); - float ry = glm::length(mData.transMat * float4(0.0f, 1.0f, 0.0f, 0.0f)); + float rx = length(transformVector(mData.transMat, float3(1.0f, 0.0f, 0.0f))); + float ry = length(transformVector(mData.transMat, float3(0.0f, 1.0f, 0.0f))); mData.surfaceArea = 4.0f * rx * ry; } // DiscLight - DiscLight::SharedPtr DiscLight::create(const std::string& name) - { - return SharedPtr(new DiscLight(name)); - } - void DiscLight::update() { AnalyticAreaLight::update(); - float rx = glm::length(mData.transMat * float4(1.0f, 0.0f, 0.0f, 0.0f)); - float ry = glm::length(mData.transMat * float4(0.0f, 1.0f, 0.0f, 0.0f)); + float rx = length(transformVector(mData.transMat, float3(1.0f, 0.0f, 0.0f))); + float ry = length(transformVector(mData.transMat, float3(0.0f, 1.0f, 0.0f))); mData.surfaceArea = (float)M_PI * rx * ry; } // SphereLight - SphereLight::SharedPtr SphereLight::create(const std::string& name) - { - return SharedPtr(new SphereLight(name)); - } - void SphereLight::update() { AnalyticAreaLight::update(); - float rx = glm::length(mData.transMat * float4(1.0f, 0.0f, 0.0f, 0.0f)); - float ry = glm::length(mData.transMat * float4(0.0f, 1.0f, 0.0f, 0.0f)); - float rz = glm::length(mData.transMat * float4(0.0f, 0.0f, 1.0f, 0.0f)); + float rx = length(transformVector(mData.transMat, float3(1.0f, 0.0f, 0.0f))); + float ry = length(transformVector(mData.transMat, float3(0.0f, 1.0f, 0.0f))); + float rz = length(transformVector(mData.transMat, float3(0.0f, 0.0f, 1.0f))); mData.surfaceArea = 4.0f * (float)M_PI * std::pow(std::pow(rx * ry, 1.6f) + std::pow(ry * rz, 1.6f) + std::pow(rx * rz, 1.6f) / 3.0f, 1.0f / 1.6f); } @@ -434,37 +404,37 @@ namespace Falcor FALCOR_SCRIPT_BINDING_DEPENDENCY(Animatable) - pybind11::class_ light(m, "Light"); + pybind11::class_> light(m, "Light"); light.def_property("name", &Light::getName, &Light::setName); light.def_property("active", &Light::isActive, &Light::setActive); light.def_property("animated", &Light::isAnimated, &Light::setIsAnimated); light.def_property("intensity", &Light::getIntensity, &Light::setIntensity); - pybind11::class_ pointLight(m, "PointLight"); + pybind11::class_> pointLight(m, "PointLight"); pointLight.def(pybind11::init(&PointLight::create), "name"_a = ""); pointLight.def_property("position", &PointLight::getWorldPosition, &PointLight::setWorldPosition); pointLight.def_property("direction", &PointLight::getWorldDirection, &PointLight::setWorldDirection); pointLight.def_property("openingAngle", &PointLight::getOpeningAngle, &PointLight::setOpeningAngle); pointLight.def_property("penumbraAngle", &PointLight::getPenumbraAngle, &PointLight::setPenumbraAngle); - pybind11::class_ directionalLight(m, "DirectionalLight"); + pybind11::class_> directionalLight(m, "DirectionalLight"); directionalLight.def(pybind11::init(&DirectionalLight::create), "name"_a = ""); directionalLight.def_property("direction", &DirectionalLight::getWorldDirection, &DirectionalLight::setWorldDirection); - pybind11::class_ distantLight(m, "DistantLight"); + pybind11::class_> distantLight(m, "DistantLight"); distantLight.def(pybind11::init(&DistantLight::create), "name"_a = ""); distantLight.def_property("direction", &DistantLight::getWorldDirection, &DistantLight::setWorldDirection); distantLight.def_property("angle", &DistantLight::getAngle, &DistantLight::setAngle); - pybind11::class_ analyticLight(m, "AnalyticAreaLight"); + pybind11::class_> analyticLight(m, "AnalyticAreaLight"); - pybind11::class_ rectLight(m, "RectLight"); + pybind11::class_> rectLight(m, "RectLight"); rectLight.def(pybind11::init(&RectLight::create), "name"_a = ""); - pybind11::class_ discLight(m, "DiscLight"); + pybind11::class_> discLight(m, "DiscLight"); discLight.def(pybind11::init(&DiscLight::create), "name"_a = ""); - pybind11::class_ sphereLight(m, "SphereLight"); + pybind11::class_> sphereLight(m, "SphereLight"); sphereLight.def(pybind11::init(&SphereLight::create), "name"_a = ""); } } diff --git a/Source/Falcor/Scene/Lights/Light.h b/Source/Falcor/Scene/Lights/Light.h index 03106fad4..6e1575f5d 100644 --- a/Source/Falcor/Scene/Lights/Light.h +++ b/Source/Falcor/Scene/Lights/Light.h @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -45,8 +45,6 @@ namespace Falcor class FALCOR_API Light : public Animatable { public: - using SharedPtr = std::shared_ptr; - virtual ~Light() = default; /** Set the light parameters into a shader variable. To use this you need to include/import 'ShaderCommon' inside your shader. @@ -115,12 +113,12 @@ namespace Falcor */ Changes getChanges() const { return mChanges; } - void updateFromAnimation(const rmcv::mat4& transform) override {} + void updateFromAnimation(const float4x4& transform) override {} protected: Light(const std::string& name, LightType type); - static const size_t kDataSize = sizeof(LightData); + static constexpr size_t kDataSize = sizeof(LightData); // UI callbacks for keeping the intensity in-sync. float3 getColorForUI(); @@ -148,9 +146,9 @@ namespace Falcor class FALCOR_API PointLight : public Light { public: - using SharedPtr = std::shared_ptr; + static ref create(const std::string& name = "") { return make_ref(name); } - static SharedPtr create(const std::string& name = ""); + PointLight(const std::string& name); ~PointLight() = default; /** Render UI elements for this light. @@ -196,10 +194,7 @@ namespace Falcor */ float getOpeningAngle() const { return mData.openingAngle; } - void updateFromAnimation(const rmcv::mat4& transform) override; - - private: - PointLight(const std::string& name); + void updateFromAnimation(const float4x4& transform) override; }; @@ -208,9 +203,9 @@ namespace Falcor class FALCOR_API DirectionalLight : public Light { public: - using SharedPtr = std::shared_ptr; + static ref create(const std::string& name = "") { return make_ref(name); } - static SharedPtr create(const std::string& name = ""); + DirectionalLight(const std::string& name); ~DirectionalLight() = default; /** Render UI elements for this light. @@ -234,10 +229,7 @@ namespace Falcor */ float getPower() const override { return 0.f; } - void updateFromAnimation(const rmcv::mat4& transform) override; - - private: - DirectionalLight(const std::string& name); + void updateFromAnimation(const float4x4& transform) override; }; /** Distant light source. @@ -246,9 +238,9 @@ namespace Falcor class FALCOR_API DistantLight : public Light { public: - using SharedPtr = std::shared_ptr; + static ref create(const std::string& name = "") { return make_ref(name); } - static SharedPtr create(const std::string& name = ""); + DistantLight(const std::string& name); ~DistantLight() = default; /** Render UI elements for this light. @@ -277,10 +269,9 @@ namespace Falcor */ float getPower() const override { return 0.f; } - void updateFromAnimation(const rmcv::mat4& transform) override; + void updateFromAnimation(const float4x4& transform) override; private: - DistantLight(const std::string& name); void update(); float mAngle; ///<< Half-angle subtended by the source. @@ -292,8 +283,6 @@ namespace Falcor class FALCOR_API AnalyticAreaLight : public Light { public: - using SharedPtr = std::shared_ptr; - ~AnalyticAreaLight() = default; /** Set light source scaling @@ -312,13 +301,13 @@ namespace Falcor /** Set transform matrix \param[in] mtx object to world space transform matrix */ - void setTransformMatrix(const rmcv::mat4& mtx) { mTransformMatrix = mtx; update(); } + void setTransformMatrix(const float4x4& mtx) { mTransformMatrix = mtx; update(); } /** Get transform matrix */ - rmcv::mat4 getTransformMatrix() const { return mTransformMatrix; } + float4x4 getTransformMatrix() const { return mTransformMatrix; } - void updateFromAnimation(const rmcv::mat4& transform) override { setTransformMatrix(transform); } + void updateFromAnimation(const float4x4& transform) override { setTransformMatrix(transform); } protected: AnalyticAreaLight(const std::string& name, LightType type); @@ -326,7 +315,7 @@ namespace Falcor virtual void update(); float3 mScaling; ///< Scaling, controls the size of the light - rmcv::mat4 mTransformMatrix; ///< Transform matrix minus scaling component + float4x4 mTransformMatrix = float4x4::identity(); ///< Transform matrix minus scaling component friend class SceneCache; }; @@ -336,14 +325,12 @@ namespace Falcor class FALCOR_API RectLight : public AnalyticAreaLight { public: - using SharedPtr = std::shared_ptr; + static ref create(const std::string& name = "") { return make_ref(name); } - static SharedPtr create(const std::string& name = ""); + RectLight(const std::string& name) : AnalyticAreaLight(name, LightType::Rect) {} ~RectLight() = default; private: - RectLight(const std::string& name) : AnalyticAreaLight(name, LightType::Rect) {} - virtual void update() override; }; @@ -352,14 +339,12 @@ namespace Falcor class FALCOR_API DiscLight : public AnalyticAreaLight { public: - using SharedPtr = std::shared_ptr; + static ref create(const std::string& name = "") { return make_ref(name); } - static SharedPtr create(const std::string& name = ""); + DiscLight(const std::string& name) : AnalyticAreaLight(name, LightType::Disc) {} ~DiscLight() = default; private: - DiscLight(const std::string& name) : AnalyticAreaLight(name, LightType::Disc) {} - virtual void update() override; }; @@ -368,14 +353,12 @@ namespace Falcor class FALCOR_API SphereLight : public AnalyticAreaLight { public: - using SharedPtr = std::shared_ptr; + static ref create(const std::string& name = "") { return make_ref(name); } - static SharedPtr create(const std::string& name = ""); + SphereLight(const std::string& name) : AnalyticAreaLight(name, LightType::Sphere) {} ~SphereLight() = default; private: - SphereLight(const std::string& name) : AnalyticAreaLight(name, LightType::Sphere) {} - virtual void update() override; }; diff --git a/Source/Falcor/Scene/Lights/LightCollection.cpp b/Source/Falcor/Scene/Lights/LightCollection.cpp index aa647a22f..f33c6f4ab 100644 --- a/Source/Falcor/Scene/Lights/LightCollection.cpp +++ b/Source/Falcor/Scene/Lights/LightCollection.cpp @@ -33,7 +33,6 @@ #include "Utils/Logger.h" #include "Utils/Timing/TimeReport.h" #include "Utils/Timing/Profiler.h" -#include namespace Falcor { @@ -49,43 +48,35 @@ namespace Falcor const char kFinalizeIntegrationFile[] = "Scene/Lights/FinalizeIntegration.cs.slang"; } - LightCollection::SharedPtr LightCollection::create(std::shared_ptr pDevice, RenderContext* pRenderContext, const std::shared_ptr& pScene) - { - return SharedPtr(new LightCollection(std::move(pDevice), pRenderContext, pScene)); - } - - LightCollection::LightCollection(std::shared_ptr pDevice, RenderContext* pRenderContext, const std::shared_ptr& pScene) - : mpDevice(std::move(pDevice)) + LightCollection::LightCollection(ref pDevice, RenderContext* pRenderContext, Scene* pScene) + : mpDevice(pDevice) , mpScene(pScene) { - FALCOR_ASSERT(pScene); + FALCOR_ASSERT(mpScene); // Setup the lights. - setupMeshLights(*pScene); + setupMeshLights(*mpScene); // Create program for integrating emissive textures. // This should be done after lights are setup, so that we know which sampler state etc. to use. - initIntegrator(pRenderContext, *pScene); + initIntegrator(pRenderContext, *mpScene); // Create programs for building/updating the mesh lights. - Shader::DefineList defines = pScene->getSceneDefines(); + Shader::DefineList defines = mpScene->getSceneDefines(); mpTriangleListBuilder = ComputePass::create(mpDevice, kBuildTriangleListFile, "buildTriangleList", defines); mpTrianglePositionUpdater = ComputePass::create(mpDevice, kUpdateTriangleVerticesFile, "updateTriangleVertices", defines); mpFinalizeIntegration = ComputePass::create(mpDevice, kFinalizeIntegrationFile, "finalizeIntegration", defines); - mpStagingFence = GpuFence::create(mpDevice.get()); + mpStagingFence = GpuFence::create(mpDevice); // Now build the mesh light data. - build(pRenderContext, *pScene); + build(pRenderContext, *mpScene); } bool LightCollection::update(RenderContext* pRenderContext, UpdateStatus* pUpdateStatus) { FALCOR_PROFILE(pRenderContext, "LightCollection::update()"); - auto pScene = mpScene.lock(); - if (!pScene) return false; - if (pUpdateStatus) { pUpdateStatus->lightsUpdateInfo.clear(); @@ -99,11 +90,11 @@ namespace Falcor for (uint32_t lightIdx = 0; lightIdx < mMeshLights.size(); ++lightIdx) { - const GeometryInstanceData& instanceData = pScene->getGeometryInstance(mMeshLights[lightIdx].instanceID); + const GeometryInstanceData& instanceData = mpScene->getGeometryInstance(mMeshLights[lightIdx].instanceID); UpdateFlags updateFlags = UpdateFlags::None; // Check if instance transform changed. - if (pScene->getAnimationController()->isMatrixChanged(NodeID{ instanceData.globalMatrixID })) updateFlags |= UpdateFlags::MatrixChanged; + if (mpScene->getAnimationController()->isMatrixChanged(NodeID{ instanceData.globalMatrixID })) updateFlags |= UpdateFlags::MatrixChanged; // Store update status. if (updateFlags != UpdateFlags::None) updatedLights.push_back(lightIdx); @@ -113,7 +104,7 @@ namespace Falcor // Update light data if needed. if (!updatedLights.empty()) { - updateTrianglePositions(pRenderContext, *pScene, updatedLights); + updateTrianglePositions(pRenderContext, *mpScene, updatedLights); return true; } @@ -173,7 +164,7 @@ namespace Falcor // 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(mpDevice.get(), samplerDesc); + mIntegrator.pPointSampler = Sampler::create(mpDevice, samplerDesc); } void LightCollection::setupMeshLights(const Scene& scene) @@ -270,11 +261,11 @@ namespace Falcor FALCOR_ASSERT(mTriangleCount > 0); // Create GPU buffers. - mpTriangleData = Buffer::createStructured(mpDevice.get(), mpTriangleListBuilder["gTriangleData"], mTriangleCount, ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess, Buffer::CpuAccess::None, nullptr, false); + mpTriangleData = Buffer::createStructured(mpDevice, mpTriangleListBuilder->getRootVar()["gTriangleData"], mTriangleCount, ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess, Buffer::CpuAccess::None, nullptr, false); mpTriangleData->setName("LightCollection::mpTriangleData"); if (mpTriangleData->getStructSize() != sizeof(PackedEmissiveTriangle)) throw RuntimeError("Struct PackedEmissiveTriangle size mismatch between CPU/GPU"); - mpFluxData = Buffer::createStructured(mpDevice.get(), mpFinalizeIntegration["gFluxData"], mTriangleCount, ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess, Buffer::CpuAccess::None, nullptr, false); + mpFluxData = Buffer::createStructured(mpDevice, mpFinalizeIntegration->getRootVar()["gFluxData"], mTriangleCount, ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess, Buffer::CpuAccess::None, nullptr, false); mpFluxData->setName("LightCollection::mpFluxData"); if (mpFluxData->getStructSize() != sizeof(EmissiveFlux)) throw RuntimeError("Struct EmissiveFlux size mismatch between CPU/GPU"); @@ -288,8 +279,8 @@ namespace Falcor if (!mMeshLights.empty()) { mpMeshData = Buffer::createStructured( - mpDevice.get(), - mpTrianglePositionUpdater["gMeshData"], + mpDevice, + mpTrianglePositionUpdater->getRootVar()["gMeshData"], uint32_t(mMeshLights.size()), ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, nullptr, false); @@ -308,14 +299,14 @@ namespace Falcor uint32_t instanceCount = scene.getGeometryInstanceCount(); if (instanceCount > 0) { - std::vector triangleOffsets(instanceCount, uint32_t(MeshLightData::kInvalidIndex)); + std::vector triangleOffsets(instanceCount, MeshLightData::kInvalidIndex); for (const auto& it : mMeshLights) { FALCOR_ASSERT(it.instanceID < instanceCount); triangleOffsets[it.instanceID] = it.triangleOffset; } - mpPerMeshInstanceOffset = Buffer::createStructured(mpDevice.get(), sizeof(uint32_t), (uint32_t)triangleOffsets.size(), Resource::BindFlags::ShaderResource, Buffer::CpuAccess::None, triangleOffsets.data(), false); + mpPerMeshInstanceOffset = Buffer::createStructured(mpDevice, sizeof(uint32_t), (uint32_t)triangleOffsets.size(), Resource::BindFlags::ShaderResource, Buffer::CpuAccess::None, triangleOffsets.data(), false); mpPerMeshInstanceOffset->setName("LightCollection::mpPerMeshInstanceOffset"); } } @@ -326,25 +317,29 @@ namespace Falcor FALCOR_ASSERT(mMeshLights.size() > 0); // Prepare program vars. - mIntegrator.pVars = GraphicsVars::create(mpDevice, mIntegrator.pProgram.get()); - mIntegrator.pVars["gScene"] = scene.getParameterBlock(); - mIntegrator.pVars["gPointSampler"] = mIntegrator.pPointSampler; - setShaderData(mIntegrator.pVars["gLightCollection"]); + { + mIntegrator.pVars = GraphicsVars::create(mpDevice, mIntegrator.pProgram.get()); + auto var = mIntegrator.pVars->getRootVar(); + var["gScene"] = scene.getParameterBlock(); + var["gPointSampler"] = mIntegrator.pPointSampler; + setShaderData(var["gLightCollection"]); + } // 1st pass: Rasterize emissive triangles in texture space to find maximum texel value. // The maximum is needed to rescale the texels to fixed-point format in the accumulation pass. - Buffer::SharedPtr pTexelMax; + ref pTexelMax; { // Allocate intermediate buffer. // Move into a member variable if we're integrating multiple times to avoid re-allocation. - pTexelMax = Buffer::create(mpDevice.get(), mTriangleCount * sizeof(uint32_t), Resource::BindFlags::ShaderResource | Resource::BindFlags::UnorderedAccess, Buffer::CpuAccess::None); + pTexelMax = Buffer::create(mpDevice, mTriangleCount * sizeof(uint32_t), Resource::BindFlags::ShaderResource | Resource::BindFlags::UnorderedAccess, Buffer::CpuAccess::None); pTexelMax->setName("LightCollection: pTexelMax"); pRenderContext->clearUAV(pTexelMax->getUAV().get(), uint4(0)); // Bind our resources. - mIntegrator.pVars["gTexelMax"] = pTexelMax; - mIntegrator.pVars["gTexelSum"].setUav(nullptr); + auto var = mIntegrator.pVars->getRootVar(); + var["gTexelMax"] = pTexelMax; + var["gTexelSum"].setUav(nullptr); // Execute. mIntegrator.pProgram->addDefine("INTEGRATOR_PASS", "1"); @@ -359,14 +354,15 @@ namespace Falcor const uint32_t bufSize = mTriangleCount * 4 * sizeof(uint64_t); if (!mIntegrator.pResultBuffer || mIntegrator.pResultBuffer->getSize() < bufSize) { - mIntegrator.pResultBuffer = Buffer::create(mpDevice.get(), bufSize, Resource::BindFlags::ShaderResource | Resource::BindFlags::UnorderedAccess, Buffer::CpuAccess::None); + mIntegrator.pResultBuffer = Buffer::create(mpDevice, bufSize, Resource::BindFlags::ShaderResource | Resource::BindFlags::UnorderedAccess, Buffer::CpuAccess::None); mIntegrator.pResultBuffer->setName("LightCollection::mIntegrator::pResultBuffer"); } pRenderContext->clearUAV(mIntegrator.pResultBuffer->getUAV().get(), uint4(0)); // Bind our resources. - mIntegrator.pVars["gTexelSum"] = mIntegrator.pResultBuffer; + auto var = mIntegrator.pVars->getRootVar(); + var["gTexelSum"] = mIntegrator.pResultBuffer; // Execute. mIntegrator.pProgram->addDefine("INTEGRATOR_PASS", "2"); @@ -375,16 +371,18 @@ namespace Falcor // 3rd pass: Finalize the per-triangle flux values. { + auto var = mpFinalizeIntegration->getRootVar(); + // Bind scene. - mpFinalizeIntegration["gScene"] = scene.getParameterBlock(); + var["gScene"] = scene.getParameterBlock(); - mpFinalizeIntegration["gPointSampler"] = mIntegrator.pPointSampler; - mpFinalizeIntegration["gTexelMax"] = pTexelMax; - mpFinalizeIntegration["gTexelSum"] = mIntegrator.pResultBuffer; - mpFinalizeIntegration["gTriangleData"] = mpTriangleData; - mpFinalizeIntegration["gFluxData"] = mpFluxData; + var["gPointSampler"] = mIntegrator.pPointSampler; + var["gTexelMax"] = pTexelMax; + var["gTexelSum"] = mIntegrator.pResultBuffer; + var["gTriangleData"] = mpTriangleData; + var["gFluxData"] = mpFluxData; - mpFinalizeIntegration["CB"]["gTriangleCount"] = mTriangleCount; + var["CB"]["gTriangleCount"] = mTriangleCount; // Execute. FALCOR_ASSERT(mpFinalizeIntegration->getThreadGroupSize().y == 1); @@ -413,9 +411,6 @@ namespace Falcor { if (mStatsValid) return; - auto pScene = mpScene.lock(); - FALCOR_ASSERT(pScene); - // Read back the current data. This is potentially expensive. syncCPUData(pRenderContext); @@ -427,7 +422,7 @@ namespace Falcor uint32_t trianglesTotal = 0; for (const auto& meshLight : mMeshLights) { - auto pMaterial = pScene->getMaterial(MaterialID::fromSlang(meshLight.materialID))->toBasicMaterial(); + auto pMaterial = mpScene->getMaterial(MaterialID::fromSlang(meshLight.materialID))->toBasicMaterial(); FALCOR_ASSERT(pMaterial); bool isTextured = pMaterial->getEmissiveTexture() != nullptr; @@ -452,7 +447,7 @@ namespace Falcor { // 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. - auto pMaterial = pScene->getMaterial(MaterialID::fromSlang(mMeshLights[tri.lightIdx].materialID))->toBasicMaterial(); + auto pMaterial = mpScene->getMaterial(MaterialID::fromSlang(mMeshLights[tri.lightIdx].materialID))->toBasicMaterial(); FALCOR_ASSERT(pMaterial); bool isTextured = pMaterial->getEmissiveTexture() != nullptr; @@ -470,11 +465,13 @@ namespace Falcor { FALCOR_ASSERT(mMeshLights.size() > 0); + auto var = mpTriangleListBuilder->getRootVar(); + // Bind scene. - mpTriangleListBuilder["gScene"] = scene.getParameterBlock(); + var["gScene"] = scene.getParameterBlock(); // Bind our output buffer. - mpTriangleListBuilder["gTriangleData"] = mpTriangleData; + var["gTriangleData"] = mpTriangleData; // TODO: Single dispatch over all emissive triangles instead of per-mesh dispatches. // This code is not performance critical though, as it's currently only run once at init time. @@ -482,11 +479,11 @@ namespace Falcor { const MeshLightData& meshLight = mMeshLights[lightIdx]; - mpTriangleListBuilder["CB"]["gLightIdx"] = lightIdx; - mpTriangleListBuilder["CB"]["gMaterialID"] = meshLight.materialID; - mpTriangleListBuilder["CB"]["gInstanceID"] = meshLight.instanceID; - mpTriangleListBuilder["CB"]["gTriangleCount"] = meshLight.triangleCount; - mpTriangleListBuilder["CB"]["gTriangleOffset"] = meshLight.triangleOffset; + var["CB"]["gLightIdx"] = lightIdx; + var["CB"]["gMaterialID"] = meshLight.materialID; + var["CB"]["gInstanceID"] = meshLight.instanceID; + var["CB"]["gTriangleCount"] = meshLight.triangleCount; + var["CB"]["gTriangleOffset"] = meshLight.triangleOffset; // TODO: Disable automatic UAV barriers. // Each kernel writes to non-overlapping parts of the output buffers, but currently Falcor inserts barriers between each dispatch. @@ -530,7 +527,7 @@ namespace Falcor { if (!mpActiveTriangleList || mpActiveTriangleList->getElementCount() < activeCount) { - mpActiveTriangleList = Buffer::createStructured(mpDevice.get(), sizeof(uint32_t), activeCount, Resource::BindFlags::ShaderResource, Buffer::CpuAccess::None, mActiveTriangleList.data(), false); + mpActiveTriangleList = Buffer::createStructured(mpDevice, sizeof(uint32_t), activeCount, Resource::BindFlags::ShaderResource, Buffer::CpuAccess::None, mActiveTriangleList.data(), false); mpActiveTriangleList->setName("LightCollection::mpActiveTriangleList"); } else @@ -541,7 +538,7 @@ namespace Falcor if (!mpTriToActiveList || mpTriToActiveList->getElementCount() < triCount) { - mpTriToActiveList = Buffer::createStructured(mpDevice.get(), sizeof(uint32_t), triCount, Resource::BindFlags::ShaderResource, Buffer::CpuAccess::None, mTriToActiveList.data(), false); + mpTriToActiveList = Buffer::createStructured(mpDevice, sizeof(uint32_t), triCount, Resource::BindFlags::ShaderResource, Buffer::CpuAccess::None, mTriToActiveList.data(), false); mpTriToActiveList->setName("LightCollection::mpTriToActiveList"); } else @@ -559,14 +556,16 @@ namespace Falcor // Alternatively, upload the list of updated meshes and early out unnecessary threads at runtime. FALCOR_ASSERT(!updatedLights.empty()); + auto var = mpTrianglePositionUpdater->getRootVar(); + // Bind scene. - mpTrianglePositionUpdater["gScene"] = scene.getParameterBlock(); + var["gScene"] = scene.getParameterBlock(); // Bind our resources. - mpTrianglePositionUpdater["gTriangleData"] = mpTriangleData; - mpTrianglePositionUpdater["gMeshData"] = mpMeshData; + var["gTriangleData"] = mpTriangleData; + var["gMeshData"] = mpMeshData; - mpTrianglePositionUpdater["CB"]["gTriangleCount"] = mTriangleCount; + var["CB"]["gTriangleCount"] = mTriangleCount; // Run compute pass to update all triangles. mpTrianglePositionUpdater->execute(pRenderContext, mTriangleCount, 1u, 1u); @@ -617,7 +616,7 @@ namespace Falcor const size_t stagingSize = mpTriangleData->getSize() + mpFluxData->getSize(); if (!mpStagingBuffer || mpStagingBuffer->getSize() < stagingSize) { - mpStagingBuffer = Buffer::create(mpDevice.get(), stagingSize, Resource::BindFlags::None, Buffer::CpuAccess::Read); + mpStagingBuffer = Buffer::create(mpDevice, stagingSize, Resource::BindFlags::None, Buffer::CpuAccess::Read); mpStagingBuffer->setName("LightCollection::mpStagingBuffer"); mCPUInvalidData = CPUOutOfDateFlags::All; } diff --git a/Source/Falcor/Scene/Lights/LightCollection.h b/Source/Falcor/Scene/Lights/LightCollection.h index 21274849e..c43e93691 100644 --- a/Source/Falcor/Scene/Lights/LightCollection.h +++ b/Source/Falcor/Scene/Lights/LightCollection.h @@ -28,14 +28,15 @@ #pragma once #include "MeshLightData.slang" #include "Core/Macros.h" +#include "Core/Object.h" #include "Core/API/Buffer.h" #include "Core/API/Sampler.h" #include "Core/API/GpuFence.h" #include "Core/State/GraphicsState.h" #include "Core/Program/GraphicsProgram.h" #include "Core/Program/ProgramVars.h" +#include "Core/Pass/ComputePass.h" #include "Utils/Math/Vector.h" -#include "RenderGraph/BasePasses/ComputePass.h" #include #include @@ -53,12 +54,9 @@ namespace Falcor The LightCollection can be used standalone, but more commonly it will be wrapped by an emissive light sampler. */ - class FALCOR_API LightCollection + class FALCOR_API LightCollection : public Object { public: - using SharedPtr = std::shared_ptr; - using SharedConstPtr = std::shared_ptr; - enum class UpdateFlags : uint32_t { None = 0u, ///< Nothing was changed. @@ -115,9 +113,6 @@ namespace Falcor } }; - - ~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] pDevice GPU device. @@ -125,7 +120,13 @@ namespace Falcor \param[in] pScene The scene. \return A pointer to a new light collection object, or throws an exception if creation failed. */ - static SharedPtr create(std::shared_ptr pDevice, RenderContext* pRenderContext, const std::shared_ptr& pScene); + static ref create(ref pDevice, RenderContext* pRenderContext, Scene* pScene) + { + return make_ref(pDevice, pRenderContext, pScene); + } + + LightCollection(ref pDevice, RenderContext* pRenderContext, Scene* pScene); + ~LightCollection() = default; /** Updates the light collection to the current state of the scene. \param[in] pRenderContext The render context. @@ -184,8 +185,6 @@ namespace Falcor }; protected: - LightCollection(std::shared_ptr pDevice, RenderContext* pRenderContext, const std::shared_ptr& pScene); - void initIntegrator(RenderContext* pRenderContext, const Scene& scene); void setupMeshLights(const Scene& scene); void build(RenderContext* pRenderContext, const Scene& scene); @@ -201,8 +200,8 @@ namespace Falcor void syncCPUData(RenderContext* pRenderContext) const; // Internal state - std::shared_ptr mpDevice; - std::weak_ptr mpScene; ///< Weak pointer to scene (scene owns LightCollection). + ref mpDevice; + Scene* mpScene; ///< Unowning pointer to scene (scene owns LightCollection). 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. @@ -215,31 +214,31 @@ namespace Falcor mutable bool mStatsValid = false; ///< True when stats are valid. // GPU resources for the mesh lights and emissive triangles. - Buffer::SharedPtr mpTriangleData; ///< Per-triangle geometry data for emissive triangles (mTriangleCount elements). - Buffer::SharedPtr mpActiveTriangleList; ///< List of active (non-culled) emissive triangle. - Buffer::SharedPtr mpTriToActiveList; ///< Mapping of all light triangles to index in mActiveTriangleList. - Buffer::SharedPtr mpFluxData; ///< Per-triangle flux data for emissive triangles (mTriangleCount elements). - Buffer::SharedPtr mpMeshData; ///< Per-mesh data for emissive meshes (mMeshLights.size() elements). - Buffer::SharedPtr mpPerMeshInstanceOffset; ///< Per-mesh instance offset into emissive triangles array (Scene::getMeshInstanceCount() elements). + ref mpTriangleData; ///< Per-triangle geometry data for emissive triangles (mTriangleCount elements). + ref mpActiveTriangleList; ///< List of active (non-culled) emissive triangle. + ref mpTriToActiveList; ///< Mapping of all light triangles to index in mActiveTriangleList. + ref mpFluxData; ///< Per-triangle flux data for emissive triangles (mTriangleCount elements). + ref mpMeshData; ///< Per-mesh data for emissive meshes (mMeshLights.size() elements). + ref 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. + mutable ref mpStagingBuffer; ///< Staging buffer used for retrieving the vertex positions, texture coordinates and light IDs from the GPU. + ref mpStagingFence; ///< Fence used for waiting on the staging buffer being filled in. - Sampler::SharedPtr mpSamplerState; ///< Material sampler for emissive textures. + ref 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. + ref pProgram; + ref pVars; + ref pState; + ref pPointSampler; ///< Point sampler for fetching individual texels in integrator. Must use same wrap mode etc. as material sampler. + ref 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; + ref mpTriangleListBuilder; + ref mpTrianglePositionUpdater; + ref 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. diff --git a/Source/Falcor/Scene/Lights/LightCollectionShared.slang b/Source/Falcor/Scene/Lights/LightCollectionShared.slang index ad47a55ee..d800a456b 100644 --- a/Source/Falcor/Scene/Lights/LightCollectionShared.slang +++ b/Source/Falcor/Scene/Lights/LightCollectionShared.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-21, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -112,9 +112,8 @@ struct PackedEmissiveTriangle materialID = tri.materialID; lightIdx = tri.lightIdx; } -#endif - EmissiveTriangle unpack() CONST_FUNCTION + EmissiveTriangle unpack() { EmissiveTriangle tri; tri.posW[0] = posAndTexCoords[0].xyz; @@ -129,6 +128,23 @@ struct PackedEmissiveTriangle tri.lightIdx = lightIdx; return tri; } +#else + EmissiveTriangle unpack() const + { + EmissiveTriangle tri; + tri.posW[0] = posAndTexCoords[0].xyz(); + tri.posW[1] = posAndTexCoords[1].xyz(); + tri.posW[2] = posAndTexCoords[2].xyz(); + tri.texCoords[0] = decodeTexCoord(asuint(posAndTexCoords[0].w)); + tri.texCoords[1] = decodeTexCoord(asuint(posAndTexCoords[1].w)); + tri.texCoords[2] = decodeTexCoord(asuint(posAndTexCoords[2].w)); + tri.normal = decodeNormal2x16(normal); + tri.area = asfloat(area); + tri.materialID = materialID; + tri.lightIdx = lightIdx; + return tri; + } +#endif }; /** Per-triangle flux data for emissive triangles. diff --git a/Source/Falcor/Scene/Lights/LightProfile.cpp b/Source/Falcor/Scene/Lights/LightProfile.cpp index 9839d8178..1748b0285 100644 --- a/Source/Falcor/Scene/Lights/LightProfile.cpp +++ b/Source/Falcor/Scene/Lights/LightProfile.cpp @@ -30,7 +30,7 @@ #include "Core/API/RenderContext.h" #include "Utils/Logger.h" #include "Utils/Algorithm/ParallelReduction.h" -#include "RenderGraph/BasePasses/ComputePass.h" +#include "Core/Pass/ComputePass.h" #include #include @@ -42,7 +42,6 @@ namespace Falcor const uint32_t kBakeResolution = 256; const char kBakeIesProfileFile[] = "Scene/Lights/BakeIesProfile.cs.slang"; - ComputePass::SharedPtr pBakePass; const char* kSupportedProfiles[] = { "IESNA:LM-63-1986", @@ -149,11 +148,13 @@ namespace Falcor } numericData.reserve(numWhitespace); - while ((line = strtok(NULL, dataDelimiters))) + line = strtok(NULL, dataDelimiters); + while (line) { float value = 0.f; if (sscanf(line, "%f", &value) == 1) numericData.push_back(value); + line = strtok(NULL, dataDelimiters); } if (numericData.size() < 16) @@ -181,13 +182,13 @@ namespace Falcor } - LightProfile::LightProfile(std::shared_ptr pDevice, const std::string& name, const std::vector& rawData) - : mpDevice(std::move(pDevice)) + LightProfile::LightProfile(ref pDevice, const std::string& name, const std::vector& rawData) + : mpDevice(pDevice) , mName(name) , mRawData(rawData) {} - LightProfile::SharedPtr LightProfile::createFromIesProfile(std::shared_ptr pDevice, const std::filesystem::path& filename, bool normalize) + ref LightProfile::createFromIesProfile(ref pDevice, const std::filesystem::path& filename, bool normalize) { std::filesystem::path fullpath; if (!findFileInDataDirectories(filename, fullpath)) @@ -221,19 +222,15 @@ namespace Falcor std::string name = fullpath.filename().string(); - return SharedPtr(new LightProfile(std::move(pDevice), name, numericData)); + return ref(new LightProfile(pDevice, name, numericData)); } void LightProfile::bake(RenderContext* pRenderContext) { - if (!pBakePass) - { - pBakePass = ComputePass::create(mpDevice, kBakeIesProfileFile, "main"); - } - - auto pBuffer = Buffer::createTyped(mpDevice.get(), (uint32_t)mRawData.size(), ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, mRawData.data()); - mpTexture = Texture::create2D(mpDevice.get(), kBakeResolution, kBakeResolution, ResourceFormat::R16Float, 1, 1, nullptr, ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess); - auto pFluxTexture = Texture::create2D(mpDevice.get(), kBakeResolution, kBakeResolution, ResourceFormat::R32Float, 1, 1, nullptr, ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess); + auto pBakePass = ComputePass::create(mpDevice, kBakeIesProfileFile, "main"); + auto pBuffer = Buffer::createTyped(mpDevice, (uint32_t)mRawData.size(), ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, mRawData.data()); + mpTexture = Texture::create2D(mpDevice, kBakeResolution, kBakeResolution, ResourceFormat::R16Float, 1, 1, nullptr, ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess); + auto pFluxTexture = Texture::create2D(mpDevice, kBakeResolution, kBakeResolution, ResourceFormat::R32Float, 1, 1, nullptr, ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess); auto var = pBakePass->getRootVar(); var["gIesData"] = pBuffer; @@ -249,7 +246,7 @@ namespace Falcor Sampler::Desc desc; desc.setFilterMode(Sampler::Filter::Linear, Sampler::Filter::Linear, Sampler::Filter::Linear); - mpSampler = Sampler::create(mpDevice.get(), desc); + mpSampler = Sampler::create(mpDevice, desc); } void LightProfile::setShaderData(const ShaderVar& var) const @@ -265,7 +262,7 @@ namespace Falcor if (mpTexture) { widget.text("Texture info: " + std::to_string(mpTexture->getWidth()) + "x" + std::to_string(mpTexture->getHeight()) + " (" + to_string(mpTexture->getFormat()) + ")"); - widget.image("Texture", mpTexture, float2(100.f)); + widget.image("Texture", mpTexture.get(), float2(100.f)); } widget.text("Flux factor: " + std::to_string(mFluxFactor)); } diff --git a/Source/Falcor/Scene/Lights/LightProfile.h b/Source/Falcor/Scene/Lights/LightProfile.h index c5ff69655..86b990366 100644 --- a/Source/Falcor/Scene/Lights/LightProfile.h +++ b/Source/Falcor/Scene/Lights/LightProfile.h @@ -28,12 +28,12 @@ #pragma once #include "Core/Macros.h" +#include "Core/Object.h" #include "Core/API/Texture.h" #include "Core/API/Sampler.h" #include "Utils/UI/Gui.h" #include -#include #include #include @@ -41,12 +41,10 @@ namespace Falcor { struct ShaderVar; - class FALCOR_API LightProfile + class FALCOR_API LightProfile : public Object { public: - using SharedPtr = std::shared_ptr; - - static SharedPtr createFromIesProfile(std::shared_ptr pDevice, const std::filesystem::path& filename, bool normalize); + static ref createFromIesProfile(ref pDevice, const std::filesystem::path& filename, bool normalize); void bake(RenderContext* pRenderContext); @@ -59,13 +57,13 @@ namespace Falcor void renderUI(Gui::Widgets& widget); private: - LightProfile(std::shared_ptr pDevice, const std::string& name, const std::vector& rawData); + LightProfile(ref pDevice, const std::string& name, const std::vector& rawData); - std::shared_ptr mpDevice; + ref mpDevice; std::string mName; std::vector mRawData; - Texture::SharedPtr mpTexture; - Sampler::SharedPtr mpSampler; + ref mpTexture; + ref mpSampler; float mFluxFactor = 0.f; }; } diff --git a/Source/Falcor/Scene/Lights/MeshLightData.slang b/Source/Falcor/Scene/Lights/MeshLightData.slang index dc6c40734..6153e34ae 100644 --- a/Source/Falcor/Scene/Lights/MeshLightData.slang +++ b/Source/Falcor/Scene/Lights/MeshLightData.slang @@ -43,7 +43,7 @@ struct MeshLightData uint triangleCount = 0; ///< Number of triangles in mesh light. uint materialID = kInvalidIndex; ///< Material ID (index into scene.materials array). - static const uint kInvalidIndex = 0xffffffff; + static constexpr uint kInvalidIndex = 0xffffffff; }; END_NAMESPACE_FALCOR diff --git a/Source/Falcor/Scene/Material/BasicMaterial.cpp b/Source/Falcor/Scene/Material/BasicMaterial.cpp index 7fd1043c0..cd0794b31 100644 --- a/Source/Falcor/Scene/Material/BasicMaterial.cpp +++ b/Source/Falcor/Scene/Material/BasicMaterial.cpp @@ -35,7 +35,6 @@ #include "Utils/Math/Common.h" #include "Utils/Color/ColorHelpers.slang" #include "Utils/Scripting/ScriptBindings.h" -#include namespace Falcor { @@ -50,8 +49,8 @@ namespace Falcor const float kMaxVolumeAnisotropy = 0.99f; } - BasicMaterial::BasicMaterial(std::shared_ptr pDevice, const std::string& name, MaterialType type) - : Material(std::move(pDevice), name, type) + BasicMaterial::BasicMaterial(ref pDevice, const std::string& name, MaterialType type) + : Material(pDevice, name, type) { mHeader.setIsBasicMaterial(true); @@ -81,15 +80,13 @@ namespace Falcor bool alphaConst = mIsTexturedAlphaConstant && hasAlpha; bool colorConst = mIsTexturedBaseColorConstant; - std::ostringstream oss; - oss << "Texture info: " << pTexture->getWidth() << "x" << pTexture->getHeight() - << " (" << to_string(pTexture->getFormat()) << ")"; - if (colorConst && !alphaConst) oss << " (color constant)"; - else if (!colorConst && alphaConst) oss << " (alpha constant)"; - else if (colorConst && alphaConst) oss << " (color and alpha constant)"; // Shouldn't happen + std::string str = fmt::format("Texture info: {}x{} ({})", pTexture->getWidth(), pTexture->getHeight(), to_string(pTexture->getFormat())); + if (colorConst && !alphaConst) str += " (color constant)"; + else if (!colorConst && alphaConst) str += " (alpha constant)"; + else if (colorConst && alphaConst) str += " (color and alpha constant)"; // Shouldn't happen widget.text("Base color: " + pTexture->getSourcePath().string()); - widget.text(oss.str()); + widget.text(str); if (colorConst || alphaConst) { @@ -97,7 +94,7 @@ namespace Falcor if (widget.var("Base color", baseColor, 0.f, 1.f, 0.01f)) setBaseColor(baseColor); } - widget.image("Base color", pTexture, float2(100.f)); + widget.image("Base color", pTexture.get(), float2(100.f)); if (widget.button("Remove texture##BaseColor")) setBaseColorTexture(nullptr); } else @@ -110,7 +107,7 @@ namespace Falcor { widget.text("Specular params: " + pTexture->getSourcePath().string()); widget.text("Texture info: " + std::to_string(pTexture->getWidth()) + "x" + std::to_string(pTexture->getHeight()) + " (" + to_string(pTexture->getFormat()) + ")"); - widget.image("Specular params", pTexture, float2(100.f)); + widget.image("Specular params", pTexture.get(), float2(100.f)); if (widget.button("Remove texture##Specular")) setSpecularTexture(nullptr); } else @@ -126,7 +123,7 @@ namespace Falcor { widget.text("Normal map: " + pTexture->getSourcePath().string()); widget.text("Texture info: " + std::to_string(pTexture->getWidth()) + "x" + std::to_string(pTexture->getHeight()) + " (" + to_string(pTexture->getFormat()) + ")"); - widget.image("Normal map", pTexture, float2(100.f)); + widget.image("Normal map", pTexture.get(), float2(100.f)); if (widget.button("Remove texture##NormalMap")) setNormalMap(nullptr); } @@ -134,7 +131,7 @@ namespace Falcor { widget.text("Displacement map: " + pTexture->getSourcePath().string()); widget.text("Texture info: " + std::to_string(pTexture->getWidth()) + "x" + std::to_string(pTexture->getHeight()) + " (" + to_string(pTexture->getFormat()) + ")"); - widget.image("Displacement map", pTexture, float2(100.f)); + widget.image("Displacement map", pTexture.get(), float2(100.f)); if (widget.button("Remove texture##DisplacementMap")) setDisplacementMap(nullptr); float scale = getDisplacementScale(); @@ -148,7 +145,7 @@ namespace Falcor { widget.text("Transmission color: " + pTexture->getSourcePath().string()); widget.text("Texture info: " + std::to_string(pTexture->getWidth()) + "x" + std::to_string(pTexture->getHeight()) + " (" + to_string(pTexture->getFormat()) + ")"); - widget.image("Transmission color", pTexture, float2(100.f)); + widget.image("Transmission color", pTexture.get(), float2(100.f)); if (widget.button("Remove texture##Transmission")) setTransmissionTexture(nullptr); } else @@ -271,7 +268,7 @@ namespace Falcor } } - void BasicMaterial::setDefaultTextureSampler(const Sampler::SharedPtr& pSampler) + void BasicMaterial::setDefaultTextureSampler(const ref& pSampler) { if (pSampler != mpDefaultSampler) { @@ -281,15 +278,15 @@ namespace Falcor Sampler::Desc desc = pSampler->getDesc(); desc.setMaxAnisotropy(16); // Set 16x anisotropic filtering for improved min/max precision per triangle. desc.setReductionMode(Sampler::ReductionMode::Min); - mpDisplacementMinSampler = Sampler::create(mpDevice.get(), desc); + mpDisplacementMinSampler = Sampler::create(mpDevice, desc); desc.setReductionMode(Sampler::ReductionMode::Max); - mpDisplacementMaxSampler = Sampler::create(mpDevice.get(), desc); + mpDisplacementMaxSampler = Sampler::create(mpDevice, desc); markUpdates(UpdateFlags::ResourcesChanged); } } - bool BasicMaterial::setTexture(const TextureSlot slot, const Texture::SharedPtr& pTexture) + bool BasicMaterial::setTexture(const TextureSlot slot, const ref& pTexture) { if (!Material::setTexture(slot, pTexture)) return false; @@ -350,12 +347,12 @@ namespace Falcor float4 baseColor = getBaseColor(); if (isColorConstant) { - baseColor = float4(texInfo.value.rgb, baseColor.a); + baseColor = float4(texInfo.value.xyz(), baseColor.a); mIsTexturedBaseColorConstant = true; } if (hasAlpha && isAlphaConstant) { - baseColor = float4(baseColor.rgb, texInfo.value.a); + baseColor = float4(baseColor.xyz(), texInfo.value.a); mIsTexturedAlphaConstant = true; } setBaseColor(baseColor); @@ -393,7 +390,7 @@ namespace Falcor if (texInfo.isConstant(channelMask)) { clearTexture(Material::TextureSlot::Emissive); - setEmissiveColor(texInfo.value.rgb); + setEmissiveColor(texInfo.value.xyz()); stats.texturesRemoved[(size_t)slot]++; } break; @@ -428,7 +425,7 @@ namespace Falcor if (texInfo.isConstant(channelMask)) { clearTexture(Material::TextureSlot::Transmission); - setTransmissionColor(texInfo.value.rgb); + setTransmissionColor(texInfo.value.xyz()); stats.texturesRemoved[(size_t)slot]++; } break; @@ -462,7 +459,7 @@ namespace Falcor { Falcor::ResourceFormat newFormat = ResourceFormat::RGBA16Float; Resource::BindFlags bf = pDisplacementMap->getBindFlags() | Resource::BindFlags::UnorderedAccess | Resource::BindFlags::RenderTarget; - Texture::SharedPtr newDisplacementTex = Texture::create2D(mpDevice.get(), pDisplacementMap->getWidth(), pDisplacementMap->getHeight(), newFormat, pDisplacementMap->getArraySize(), Resource::kMaxPossible, nullptr, bf); + ref newDisplacementTex = Texture::create2D(mpDevice, pDisplacementMap->getWidth(), pDisplacementMap->getHeight(), newFormat, pDisplacementMap->getArraySize(), Resource::kMaxPossible, nullptr, bf); // Copy base level. uint32_t arraySize = pDisplacementMap->getArraySize(); @@ -505,7 +502,7 @@ namespace Falcor void BasicMaterial::setBaseColor(const float4& color) { - if (mData.baseColor != (float16_t4)color) + if (any(mData.baseColor != (float16_t4)color)) { mData.baseColor = (float16_t4)color; markUpdates(UpdateFlags::DataChanged); @@ -516,7 +513,7 @@ namespace Falcor void BasicMaterial::setSpecularParams(const float4& color) { - if (mData.specular != (float16_t4)color) + if (any(mData.specular != (float16_t4)color)) { mData.specular = (float16_t4)color; markUpdates(UpdateFlags::DataChanged); @@ -526,7 +523,7 @@ namespace Falcor void BasicMaterial::setTransmissionColor(const float3& transmissionColor) { - if (mData.transmission != (float16_t3)transmissionColor) + if (any(mData.transmission != (float16_t3)transmissionColor)) { mData.transmission = (float16_t3)transmissionColor; markUpdates(UpdateFlags::DataChanged); @@ -555,7 +552,7 @@ namespace Falcor void BasicMaterial::setVolumeAbsorption(const float3& volumeAbsorption) { - if (mData.volumeAbsorption != (float16_t3)volumeAbsorption) + if (any(mData.volumeAbsorption != (float16_t3)volumeAbsorption)) { mData.volumeAbsorption = (float16_t3)volumeAbsorption; markUpdates(UpdateFlags::DataChanged); @@ -564,7 +561,7 @@ namespace Falcor void BasicMaterial::setVolumeScattering(const float3& volumeScattering) { - if (mData.volumeScattering != (float16_t3)volumeScattering) + if (any(mData.volumeScattering != (float16_t3)volumeScattering)) { mData.volumeScattering = (float16_t3)volumeScattering; markUpdates(UpdateFlags::DataChanged); @@ -573,7 +570,7 @@ namespace Falcor void BasicMaterial::setVolumeAnisotropy(float volumeAnisotropy) { - auto clampedAnisotropy = clamp(volumeAnisotropy, -kMaxVolumeAnisotropy, kMaxVolumeAnisotropy); + auto clampedAnisotropy = math::clamp(volumeAnisotropy, -kMaxVolumeAnisotropy, kMaxVolumeAnisotropy); if (mData.volumeAnisotropy != (float16_t)clampedAnisotropy) { mData.volumeAnisotropy = (float16_t)clampedAnisotropy; @@ -581,9 +578,9 @@ namespace Falcor } } - bool BasicMaterial::isEqual(const Material::SharedPtr& pOther) const + bool BasicMaterial::isEqual(const ref& pOther) const { - auto other = std::dynamic_pointer_cast(pOther); + auto other = dynamic_ref_cast(pOther); if (!other) return false; return (*this) == (*other); @@ -594,21 +591,23 @@ namespace Falcor if (!isBaseEqual(other)) return false; #define compare_field(_a) if (mData._a != other.mData._a) return false +#define compare_vec_field(_a) if (any(mData._a != other.mData._a)) return false compare_field(flags); compare_field(displacementScale); compare_field(displacementOffset); - compare_field(baseColor); - compare_field(specular); - compare_field(emissive); + compare_vec_field(baseColor); + compare_vec_field(specular); + compare_vec_field(emissive); compare_field(emissiveFactor); compare_field(IoR); compare_field(diffuseTransmission); compare_field(specularTransmission); - compare_field(transmission); - compare_field(volumeAbsorption); + compare_vec_field(transmission); + compare_vec_field(volumeAbsorption); compare_field(volumeAnisotropy); - compare_field(volumeScattering); + compare_vec_field(volumeScattering); #undef compare_field +#undef compare_vec_field // Compare the sampler descs directly to identify functional differences. if (mpDefaultSampler->getDesc() != other.mpDefaultSampler->getDesc()) return false; @@ -657,7 +656,7 @@ namespace Falcor bool isEmissive = false; if (mData.emissiveFactor > 0.f) { - isEmissive = hasTextureSlotData(Material::TextureSlot::Emissive) || mData.emissive != float3(0.f); + isEmissive = hasTextureSlotData(Material::TextureSlot::Emissive) || any(mData.emissive != float3(0.f)); } if (mHeader.isEmissive() != isEmissive) { @@ -684,7 +683,7 @@ namespace Falcor { FALCOR_SCRIPT_BINDING_DEPENDENCY(Material) - pybind11::class_ material(m, "BasicMaterial"); + pybind11::class_> material(m, "BasicMaterial"); material.def_property("baseColor", &BasicMaterial::getBaseColor, &BasicMaterial::setBaseColor); material.def_property("specularParams", &BasicMaterial::getSpecularParams, &BasicMaterial::setSpecularParams); material.def_property("transmissionColor", &BasicMaterial::getTransmissionColor, &BasicMaterial::setTransmissionColor); diff --git a/Source/Falcor/Scene/Material/BasicMaterial.h b/Source/Falcor/Scene/Material/BasicMaterial.h index 1d0d53932..a27dd4f9e 100644 --- a/Source/Falcor/Scene/Material/BasicMaterial.h +++ b/Source/Falcor/Scene/Material/BasicMaterial.h @@ -44,8 +44,6 @@ namespace Falcor class FALCOR_API BasicMaterial : public Material { public: - using SharedPtr = std::shared_ptr; - /** Render the UI. \return True if the material was modified. */ @@ -65,7 +63,7 @@ namespace Falcor \param[in] pOther Other material. \return true if all materials properties *except* the name are identical. */ - bool isEqual(const Material::SharedPtr& pOther) const override; + bool isEqual(const ref& pOther) const override; /** Set the alpha mode. */ @@ -81,7 +79,7 @@ namespace Falcor \param[in] pTexture The texture. \return True if the texture slot was changed, false otherwise. */ - bool setTexture(const TextureSlot slot, const Texture::SharedPtr& pTexture) override; + bool setTexture(const TextureSlot slot, const ref& pTexture) override; /** Optimize texture usage for the given texture slot. This function may replace constant textures by uniform material parameters etc. @@ -93,11 +91,11 @@ namespace Falcor /** Set the default texture sampler for the material. */ - void setDefaultTextureSampler(const Sampler::SharedPtr& pSampler) override; + void setDefaultTextureSampler(const ref& pSampler) override; /** Get the default texture sampler for the material. */ - Sampler::SharedPtr getDefaultTextureSampler() const override { return mpDefaultSampler; } + ref getDefaultTextureSampler() const override { return mpDefaultSampler; } /** Get the material data blob for uploading to the GPU. */ @@ -108,51 +106,51 @@ namespace Falcor /** Set the base color texture. */ - void setBaseColorTexture(const Texture::SharedPtr& pBaseColor) { setTexture(TextureSlot::BaseColor, pBaseColor); } + void setBaseColorTexture(const ref& pBaseColor) { setTexture(TextureSlot::BaseColor, pBaseColor); } /** Get the base color texture. */ - Texture::SharedPtr getBaseColorTexture() const { return getTexture(TextureSlot::BaseColor); } + ref getBaseColorTexture() const { return getTexture(TextureSlot::BaseColor); } /** Set the specular texture. */ - void setSpecularTexture(const Texture::SharedPtr& pSpecular) { setTexture(TextureSlot::Specular, pSpecular); } + void setSpecularTexture(const ref& pSpecular) { setTexture(TextureSlot::Specular, pSpecular); } /** Get the specular texture. */ - Texture::SharedPtr getSpecularTexture() const { return getTexture(TextureSlot::Specular); } + ref getSpecularTexture() const { return getTexture(TextureSlot::Specular); } /** Set the emissive texture. */ - void setEmissiveTexture(const Texture::SharedPtr& pEmissive) { setTexture(TextureSlot::Emissive, pEmissive); } + void setEmissiveTexture(const ref& pEmissive) { setTexture(TextureSlot::Emissive, pEmissive); } /** Get the emissive texture. */ - Texture::SharedPtr getEmissiveTexture() const { return getTexture(TextureSlot::Emissive); } + ref getEmissiveTexture() const { return getTexture(TextureSlot::Emissive); } /** Set the specular transmission texture. */ - void setTransmissionTexture(const Texture::SharedPtr& pTransmission) { setTexture(TextureSlot::Transmission, pTransmission); } + void setTransmissionTexture(const ref& pTransmission) { setTexture(TextureSlot::Transmission, pTransmission); } /** Get the specular transmission texture. */ - Texture::SharedPtr getTransmissionTexture() const { return getTexture(TextureSlot::Transmission); } + ref getTransmissionTexture() const { return getTexture(TextureSlot::Transmission); } /** Set the normal map. */ - void setNormalMap(const Texture::SharedPtr& pNormalMap) { setTexture(TextureSlot::Normal, pNormalMap); } + void setNormalMap(const ref& pNormalMap) { setTexture(TextureSlot::Normal, pNormalMap); } /** Get the normal map. */ - Texture::SharedPtr getNormalMap() const { return getTexture(TextureSlot::Normal); } + ref getNormalMap() const { return getTexture(TextureSlot::Normal); } /** Set the displacement map. */ - void setDisplacementMap(const Texture::SharedPtr& pDisplacementMap) { setTexture(TextureSlot::Displacement, pDisplacementMap); } + void setDisplacementMap(const ref& pDisplacementMap) { setTexture(TextureSlot::Displacement, pDisplacementMap); } /** Get the displacement map. */ - Texture::SharedPtr getDisplacementMap() const { return getTexture(TextureSlot::Displacement); } + ref getDisplacementMap() const { return getTexture(TextureSlot::Displacement); } /** Set the displacement scale. */ @@ -256,7 +254,7 @@ namespace Falcor bool operator==(const BasicMaterial& other) const; protected: - BasicMaterial(std::shared_ptr pDevice, const std::string& name, MaterialType type); + BasicMaterial(ref pDevice, const std::string& name, MaterialType type); bool isAlphaSupported() const; void prepareDisplacementMapForRendering(); @@ -271,9 +269,9 @@ namespace Falcor BasicMaterialData mData; ///< Material parameters. - Sampler::SharedPtr mpDefaultSampler; - Sampler::SharedPtr mpDisplacementMinSampler; - Sampler::SharedPtr mpDisplacementMaxSampler; + ref mpDefaultSampler; + ref mpDisplacementMinSampler; + ref mpDisplacementMaxSampler; // Additional data for texture usage. float2 mAlphaRange = float2(0.f, 1.f); ///< Conservative range of opacity (alpha) values for the material. diff --git a/Source/Falcor/Scene/Material/BasicMaterialData.slang b/Source/Falcor/Scene/Material/BasicMaterialData.slang index 6fd8fce0d..72a0190e4 100644 --- a/Source/Falcor/Scene/Material/BasicMaterialData.slang +++ b/Source/Falcor/Scene/Material/BasicMaterialData.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -51,18 +51,18 @@ struct BasicMaterialData uint flags = 0; ///< Material flags and packed sampler IDs. See accessors below. float emissiveFactor = 1.f; ///< Multiplication factor for the emissive color to control light intensity. Range [0,inf). - float16_t4 baseColor = float16_t4(1.f); ///< Material base color (RGB) and opacity (A). - float16_t4 specular = float16_t4(0.f); ///< Material specular channel encoding occlusion (R), roughness (G), metallic (B) in the default MetalRough mode. In SpecGloss mode specular color (RGB) and glossiness (A). + float16_t4 baseColor = float16_t4(1.h); ///< Material base color (RGB) and opacity (A). + float16_t4 specular = float16_t4(0.h); ///< Material specular channel encoding occlusion (R), roughness (G), metallic (B) in the default MetalRough mode. In SpecGloss mode specular color (RGB) and glossiness (A). float3 emissive = float3(0.f); ///< Emissive color (RGB). - float16_t specularTransmission = float16_t(0.f); ///< Specular transmission. Range [0,1]. - float16_t3 transmission = float16_t3(1.f); ///< Transmission color. - float16_t diffuseTransmission = float16_t(0.f); ///< Diffuse transmission. Range [0,1]. + float16_t specularTransmission = 0.h; ///< Specular transmission. Range [0,1]. + float16_t3 transmission = float16_t3(1.h); ///< Transmission color. + float16_t diffuseTransmission = 0.h; ///< Diffuse transmission. Range [0,1]. - float16_t3 volumeScattering = float16_t3(0.f); ///< Volume scattering coefficient. Range [0,inf). - float16_t IoR = float16_t(1.5f); ///< Index of refraction. Range [1,inf). - float16_t3 volumeAbsorption = float16_t3(0.f); ///< Volume absorption coefficient. Range [0,inf). - float16_t volumeAnisotropy = float16_t(0.f); ///< Volume phase function anisotropy (g). Range [-1,1]. + float16_t3 volumeScattering = float16_t3(0.h); ///< Volume scattering coefficient. Range [0,inf). + float16_t IoR = 1.5h; ///< Index of refraction. Range [1,inf). + float16_t3 volumeAbsorption = float16_t3(0.h); ///< Volume absorption coefficient. Range [0,inf). + float16_t volumeAnisotropy = 0.h; ///< Volume phase function anisotropy (g). Range [-1,1]. float displacementScale = 0.f; ///< Displacement scale value. float displacementOffset = 0.f; ///< Displacement offset value. @@ -75,15 +75,15 @@ struct BasicMaterialData TextureHandle texTransmission; TextureHandle texDisplacementMap; - static const uint kShadingModelBits = 1; - static const uint kNormalMapTypeBits = 2; + static constexpr uint kShadingModelBits = 1; + static constexpr uint kNormalMapTypeBits = 2; - static const uint kShadingModelOffset = 0; - static const uint kNormalMapTypeOffset = kShadingModelOffset + kShadingModelBits; - static const uint kMinSamplerIDOffset = kNormalMapTypeOffset + kNormalMapTypeBits; - static const uint kMaxSamplerIDOffset = kMinSamplerIDOffset + MaterialHeader::kSamplerIDBits; + static constexpr uint kShadingModelOffset = 0; + static constexpr uint kNormalMapTypeOffset = kShadingModelOffset + kShadingModelBits; + static constexpr uint kMinSamplerIDOffset = kNormalMapTypeOffset + kNormalMapTypeBits; + static constexpr uint kMaxSamplerIDOffset = kMinSamplerIDOffset + MaterialHeader::kSamplerIDBits; - static const uint kTotalFlagsBits = kMaxSamplerIDOffset + MaterialHeader::kSamplerIDBits; + static constexpr uint kTotalFlagsBits = kMaxSamplerIDOffset + MaterialHeader::kSamplerIDBits; /** Set shading model. This is only used for the standard material. */ diff --git a/Source/Falcor/Scene/Material/ClothMaterial.cpp b/Source/Falcor/Scene/Material/ClothMaterial.cpp index 908cf37be..ff881e35b 100644 --- a/Source/Falcor/Scene/Material/ClothMaterial.cpp +++ b/Source/Falcor/Scene/Material/ClothMaterial.cpp @@ -26,7 +26,7 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **************************************************************************/ #include "ClothMaterial.h" -#include "Scene/SceneBuilderAccess.h" +#include "GlobalState.h" #include "Utils/Scripting/ScriptBindings.h" namespace Falcor @@ -36,13 +36,8 @@ namespace Falcor const char kShaderFile[] = "Rendering/Materials/ClothMaterial.slang"; } - ClothMaterial::SharedPtr ClothMaterial::create(std::shared_ptr pDevice, const std::string& name) - { - return SharedPtr(new ClothMaterial(std::move(pDevice), name)); - } - - ClothMaterial::ClothMaterial(std::shared_ptr pDevice, const std::string& name) - : BasicMaterial(std::move(pDevice), name, MaterialType::Cloth) + ClothMaterial::ClothMaterial(ref pDevice, const std::string& name) + : BasicMaterial(pDevice, name, MaterialType::Cloth) { // Setup additional texture slots. mTextureSlotInfo[(uint32_t)TextureSlot::BaseColor] = { "baseColor", TextureChannelFlags::RGBA, true }; @@ -81,10 +76,10 @@ namespace Falcor FALCOR_SCRIPT_BINDING_DEPENDENCY(BasicMaterial) - pybind11::class_ material(m, "ClothMaterial"); + pybind11::class_> material(m, "ClothMaterial"); auto create = [] (const std::string& name) { - return ClothMaterial::create(getActivePythonSceneBuilder().getDevice(), name); + return ClothMaterial::create(accessActivePythonSceneBuilder().getDevice(), name); }; material.def(pybind11::init(create), "name"_a = ""); // PYTHONDEPRECATED diff --git a/Source/Falcor/Scene/Material/ClothMaterial.h b/Source/Falcor/Scene/Material/ClothMaterial.h index 9811602d4..bd9219c3d 100644 --- a/Source/Falcor/Scene/Material/ClothMaterial.h +++ b/Source/Falcor/Scene/Material/ClothMaterial.h @@ -50,12 +50,9 @@ namespace Falcor class FALCOR_API ClothMaterial : public BasicMaterial { public: - using SharedPtr = std::shared_ptr; + static ref create(ref pDevice, const std::string& name) { return make_ref(pDevice, name); }; - /** Create a new cloth material. - \param[in] name The material name. - */ - static SharedPtr create(std::shared_ptr pDevice, const std::string& name = ""); + ClothMaterial(ref pDevice, const std::string& name); Program::ShaderModuleList getShaderModules() const override; Program::TypeConformanceList getTypeConformances() const override; @@ -69,8 +66,6 @@ namespace Falcor float getRoughness() const { return (float)mData.specular[1]; } protected: - ClothMaterial(std::shared_ptr pDevice, const std::string& name); - void renderSpecularUI(Gui::Widgets& widget) override; }; } diff --git a/Source/Falcor/Scene/Material/DiffuseSpecularData.slang b/Source/Falcor/Scene/Material/DiffuseSpecularData.slang index c25cc75d6..73a5f8812 100644 --- a/Source/Falcor/Scene/Material/DiffuseSpecularData.slang +++ b/Source/Falcor/Scene/Material/DiffuseSpecularData.slang @@ -44,7 +44,7 @@ struct DiffuseSpecularData #ifdef HOST_CODE bool operator== (const DiffuseSpecularData& other) const { - if (baseColor != other.baseColor) return false; + if (any(baseColor != other.baseColor)) return false; if (roughness != other.roughness) return false; if (specular != other.specular) return false; if (metallic != other.metallic) return false; diff --git a/Source/Falcor/Scene/Material/DiffuseSpecularUtils.cpp b/Source/Falcor/Scene/Material/DiffuseSpecularUtils.cpp index 95893a244..56aaa3278 100644 --- a/Source/Falcor/Scene/Material/DiffuseSpecularUtils.cpp +++ b/Source/Falcor/Scene/Material/DiffuseSpecularUtils.cpp @@ -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. **************************************************************************/ -#pragma once #include "DiffuseSpecularUtils.h" #include "DiffuseSpecularData.slang" +#include "Utils/Logger.h" #include "Utils/Color/ColorHelpers.slang" #include #include diff --git a/Source/Falcor/Scene/Material/HairMaterial.cpp b/Source/Falcor/Scene/Material/HairMaterial.cpp index 54bc5a432..657ee6acd 100644 --- a/Source/Falcor/Scene/Material/HairMaterial.cpp +++ b/Source/Falcor/Scene/Material/HairMaterial.cpp @@ -26,7 +26,7 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **************************************************************************/ #include "HairMaterial.h" -#include "Scene/SceneBuilderAccess.h" +#include "GlobalState.h" #include "Utils/Scripting/ScriptBindings.h" namespace Falcor @@ -36,13 +36,8 @@ namespace Falcor const char kShaderFile[] = "Rendering/Materials/HairMaterial.slang"; } - HairMaterial::SharedPtr HairMaterial::create(std::shared_ptr pDevice, const std::string& name) - { - return SharedPtr(new HairMaterial(std::move(pDevice), name)); - } - - HairMaterial::HairMaterial(std::shared_ptr pDevice, const std::string& name) - : BasicMaterial(std::move(pDevice), name, MaterialType::Hair) + HairMaterial::HairMaterial(ref pDevice, const std::string& name) + : BasicMaterial(pDevice, name, MaterialType::Hair) { // Setup additional texture slots. mTextureSlotInfo[(uint32_t)TextureSlot::BaseColor] = { "baseColor", TextureChannelFlags::RGB, true }; // Note: No alpha support @@ -69,7 +64,7 @@ namespace Falcor float3 HairMaterial::sigmaAFromColor(float3 color, float betaN) { const float tmp = 5.969f - 0.215f * betaN + 2.532f * betaN * betaN - 10.73f * std::pow(betaN, 3) + 5.574f * std::pow(betaN, 4) + 0.245f * std::pow(betaN, 5); - float3 sqrtSigmaA = log(max(color, 1e-4f)) / tmp; + float3 sqrtSigmaA = log(max(color, float3(1e-4f))) / tmp; return sqrtSigmaA * sqrtSigmaA; } @@ -84,10 +79,10 @@ namespace Falcor using namespace pybind11::literals; FALCOR_SCRIPT_BINDING_DEPENDENCY(BasicMaterial) - pybind11::class_ material(m, "HairMaterial"); + pybind11::class_> material(m, "HairMaterial"); auto create = [] (const std::string& name) { - return HairMaterial::create(getActivePythonSceneBuilder().getDevice(), name); + return HairMaterial::create(accessActivePythonSceneBuilder().getDevice(), name); }; material.def(pybind11::init(create), "name"_a = ""); // PYTHONDEPRECATED } diff --git a/Source/Falcor/Scene/Material/HairMaterial.h b/Source/Falcor/Scene/Material/HairMaterial.h index ce5af0de8..3106c2a63 100644 --- a/Source/Falcor/Scene/Material/HairMaterial.h +++ b/Source/Falcor/Scene/Material/HairMaterial.h @@ -48,12 +48,9 @@ namespace Falcor class FALCOR_API HairMaterial : public BasicMaterial { public: - using SharedPtr = std::shared_ptr; + static ref create(ref pDevice, const std::string& name) { return make_ref(pDevice, name); }; - /** Create a new hair material. - \param[in] name The material name. - */ - static SharedPtr create(std::shared_ptr pDevice, const std::string& name = ""); + HairMaterial(ref pDevice, const std::string& name); Program::ShaderModuleList getShaderModules() const override; Program::TypeConformanceList getTypeConformances() const override; @@ -69,8 +66,5 @@ namespace Falcor /** Compute RGB color from sigmaA (inverse of sigmaAFromColor). */ static float3 colorFromSigmaA(float3 sigmaA, float betaN); - - protected: - HairMaterial(std::shared_ptr pDevice, const std::string& name); }; } diff --git a/Source/Falcor/Scene/Material/MERLFile.cpp b/Source/Falcor/Scene/Material/MERLFile.cpp index cdcb6207a..ff2c3c113 100644 --- a/Source/Falcor/Scene/Material/MERLFile.cpp +++ b/Source/Falcor/Scene/Material/MERLFile.cpp @@ -142,7 +142,7 @@ namespace Falcor if (nanCount > 0) logWarning("MERL BRDF {} has {} samples with NaN values. Sample set to zero.", mDesc.name, nanCount); } - const std::vector& MERLFile::prepareAlbedoLUT(const std::shared_ptr& pDevice) + const std::vector& MERLFile::prepareAlbedoLUT(ref pDevice) { if (!mAlbedoLUT.empty()) return mAlbedoLUT; @@ -183,7 +183,7 @@ namespace Falcor return mAlbedoLUT; } - void MERLFile::computeAlbedoLUT(const std::shared_ptr& pDevice, const size_t binCount) + void MERLFile::computeAlbedoLUT(ref pDevice, const size_t binCount) { logInfo("MERLFile: Computing albedo LUT for MERL BRDF '{}'...", mDesc.name); @@ -191,14 +191,14 @@ namespace Falcor for (uint32_t i = 0; i < binCount; i++) cosThetas[i] = (float)(i + 1) / binCount; // Create MERL material based on loaded data. - MERLMaterial::SharedPtr pMaterial(new MERLMaterial(pDevice, *this)); + ref pMaterial = make_ref(pDevice, *this); // Create and update dummy scene containing the material. Scene::SceneData sceneData; - sceneData.pMaterials = MaterialSystem::create(pDevice); + sceneData.pMaterials = std::make_unique(pDevice); MaterialID materialID = sceneData.pMaterials->addMaterial(pMaterial); - Scene::SharedPtr pScene = Scene::create(pDevice, std::move(sceneData)); + ref pScene = Scene::create(pDevice, std::move(sceneData)); pScene->update(pDevice->getRenderContext(), 0.0); // Create BSDF integrator utility. diff --git a/Source/Falcor/Scene/Material/MERLFile.h b/Source/Falcor/Scene/Material/MERLFile.h index 5acd6b593..ec68937c5 100644 --- a/Source/Falcor/Scene/Material/MERLFile.h +++ b/Source/Falcor/Scene/Material/MERLFile.h @@ -26,6 +26,7 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **************************************************************************/ #pragma once +#include "Core/API/fwd.h" #include "Core/API/Formats.h" #include "Utils/Math/Vector.h" #include "Scene/Material/DiffuseSpecularData.slang" @@ -49,7 +50,7 @@ namespace Falcor DiffuseSpecularData extraData = {}; ///< Parameters for a best fit BRDF approximation. }; - static const ResourceFormat kAlbedoLUTFormat = ResourceFormat::RGBA32Float; + static constexpr ResourceFormat kAlbedoLUTFormat = ResourceFormat::RGBA32Float; MERLFile() = default; @@ -69,14 +70,14 @@ namespace Falcor \param[in] pDevice The device. \return Albedo lookup table that can be used with `kAlbedoLUTFormat`. */ - const std::vector& prepareAlbedoLUT(const std::shared_ptr& pDevice); + const std::vector& prepareAlbedoLUT(ref pDevice); const Desc& getDesc() const { return mDesc; } const std::vector& getData() const { return mData; } private: void prepareData(const int dims[3], const std::vector& data); - void computeAlbedoLUT(const std::shared_ptr&, const size_t binCount); + void computeAlbedoLUT(ref pDevice, const size_t binCount); Desc mDesc; ///< BRDF description and sampling parameters. std::vector mData; ///< BRDF data in RGB float format. diff --git a/Source/Falcor/Scene/Material/MERLMaterial.cpp b/Source/Falcor/Scene/Material/MERLMaterial.cpp index 52e700acf..e6e5d3881 100644 --- a/Source/Falcor/Scene/Material/MERLMaterial.cpp +++ b/Source/Falcor/Scene/Material/MERLMaterial.cpp @@ -29,7 +29,7 @@ #include "Core/API/Device.h" #include "Utils/Logger.h" #include "Utils/Scripting/ScriptBindings.h" -#include "Scene/SceneBuilderAccess.h" +#include "GlobalState.h" #include "Scene/Material/MERLFile.h" #include "Scene/Material/MaterialSystem.h" #include "Scene/Material/DiffuseSpecularUtils.h" @@ -43,13 +43,8 @@ namespace Falcor const char kShaderFile[] = "Rendering/Materials/MERLMaterial.slang"; } - MERLMaterial::SharedPtr MERLMaterial::create(std::shared_ptr pDevice, const std::string& name, const std::filesystem::path& path) - { - return SharedPtr(new MERLMaterial(std::move(pDevice), name, path)); - } - - MERLMaterial::MERLMaterial(std::shared_ptr pDevice, const std::string& name, const std::filesystem::path& path) - : Material(std::move(pDevice), name, MaterialType::MERL) + MERLMaterial::MERLMaterial(ref pDevice, const std::string& name, const std::filesystem::path& path) + : Material(pDevice, name, MaterialType::MERL) { std::filesystem::path fullPath; if (!findFileInDataDirectories(path, fullPath)) @@ -64,11 +59,11 @@ namespace Falcor auto lut = merlFile.prepareAlbedoLUT(mpDevice); checkInvariant(!lut.empty() && sizeof(lut[0]) == sizeof(float4), "Expected albedo LUT in float4 format."); static_assert(MERLFile::kAlbedoLUTFormat == ResourceFormat::RGBA32Float); - mpAlbedoLUT = Texture::create2D(mpDevice.get(), (uint32_t)lut.size(), 1, MERLFile::kAlbedoLUTFormat, 1, 1, lut.data(), ResourceBindFlags::ShaderResource); + mpAlbedoLUT = Texture::create2D(mpDevice, (uint32_t)lut.size(), 1, MERLFile::kAlbedoLUTFormat, 1, 1, lut.data(), ResourceBindFlags::ShaderResource); } - MERLMaterial::MERLMaterial(std::shared_ptr pDevice, const MERLFile& merlFile) - : Material(std::move(pDevice), "", MaterialType::MERL) + MERLMaterial::MERLMaterial(ref pDevice, const MERLFile& merlFile) + : Material(pDevice, "", MaterialType::MERL) { init(merlFile); } @@ -82,14 +77,14 @@ namespace Falcor // Create GPU buffer. const auto& brdf = merlFile.getData(); checkInvariant(!brdf.empty() && sizeof(brdf[0]) == sizeof(float3), "Expected BRDF data in float3 format."); - mpBRDFData = Buffer::create(mpDevice.get(), brdf.size() * sizeof(brdf[0]), ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, brdf.data()); + mpBRDFData = Buffer::create(mpDevice, brdf.size() * sizeof(brdf[0]), ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, brdf.data()); // Create sampler for albedo LUT. Sampler::Desc desc; desc.setFilterMode(Sampler::Filter::Linear, Sampler::Filter::Point, Sampler::Filter::Point); desc.setAddressingMode(Sampler::AddressMode::Clamp, Sampler::AddressMode::Clamp, Sampler::AddressMode::Clamp); desc.setMaxAnisotropy(1); - mpLUTSampler = Sampler::create(mpDevice.get(), desc); + mpLUTSampler = Sampler::create(mpDevice, desc); markUpdates(Material::UpdateFlags::ResourcesChanged); } @@ -139,9 +134,9 @@ namespace Falcor return flags; } - bool MERLMaterial::isEqual(const Material::SharedPtr& pOther) const + bool MERLMaterial::isEqual(const ref& pOther) const { - auto other = std::dynamic_pointer_cast(pOther); + auto other = dynamic_ref_cast(pOther); if (!other) return false; if (!isBaseEqual(*other)) return false; @@ -166,10 +161,10 @@ namespace Falcor FALCOR_SCRIPT_BINDING_DEPENDENCY(Material) - pybind11::class_ material(m, "MERLMaterial"); + pybind11::class_> material(m, "MERLMaterial"); auto create = [] (const std::string& name, const std::filesystem::path& path) { - return MERLMaterial::create(getActivePythonSceneBuilder().getDevice(), name, path); + return MERLMaterial::create(accessActivePythonSceneBuilder().getDevice(), name, path); }; material.def(pybind11::init(create), "name"_a, "path"_a); // PYTHONDEPRECATED } diff --git a/Source/Falcor/Scene/Material/MERLMaterial.h b/Source/Falcor/Scene/Material/MERLMaterial.h index d0d8a7dfe..dc8d1b3cf 100644 --- a/Source/Falcor/Scene/Material/MERLMaterial.h +++ b/Source/Falcor/Scene/Material/MERLMaterial.h @@ -43,18 +43,14 @@ namespace Falcor class FALCOR_API MERLMaterial : public Material { public: - using SharedPtr = std::shared_ptr; + static ref create(ref pDevice, const std::string& name, const std::filesystem::path& path) { return make_ref(pDevice, name, path); } - /** Create a new MERL material. - \param[in] name The material name. - \param[in] path Path of BRDF file to load. - \return A new object, or throws an exception if creation failed. - */ - static SharedPtr create(std::shared_ptr pDevice, const std::string& name, const std::filesystem::path& path); + MERLMaterial(ref pDevice, const std::string& name, const std::filesystem::path& path); + MERLMaterial(ref pDevice, const MERLFile& merlFile); bool renderUI(Gui::Widgets& widget) override; Material::UpdateFlags update(MaterialSystem* pOwner) override; - bool isEqual(const Material::SharedPtr& pOther) const override; + bool isEqual(const ref& pOther) const override; MaterialDataBlob getDataBlob() const override { return prepareDataBlob(mData); } Program::ShaderModuleList getShaderModules() const override; Program::TypeConformanceList getTypeConformances() const override; @@ -62,19 +58,14 @@ namespace Falcor int getBufferCount() const override { return 1; } protected: - MERLMaterial(std::shared_ptr pDevice, const std::string& name, const std::filesystem::path& path); - MERLMaterial(std::shared_ptr pDevice, const MERLFile& merlFile); - void init(const MERLFile& merlFile); std::filesystem::path mPath; ///< Full path to the BRDF loaded. std::string mBRDFName; ///< This is the file basename without extension. MERLMaterialData mData; ///< Material parameters. - Buffer::SharedPtr mpBRDFData; ///< GPU buffer holding all BRDF data as float3 array. - Texture::SharedPtr mpAlbedoLUT; ///< Precomputed albedo lookup table. - Sampler::SharedPtr mpLUTSampler; ///< Sampler for accessing the LUT texture. - - friend class MERLFile; + ref mpBRDFData; ///< GPU buffer holding all BRDF data as float3 array. + ref mpAlbedoLUT; ///< Precomputed albedo lookup table. + ref mpLUTSampler; ///< Sampler for accessing the LUT texture. }; } diff --git a/Source/Falcor/Scene/Material/MERLMaterialData.slang b/Source/Falcor/Scene/Material/MERLMaterialData.slang index 080e17bf8..80641b4af 100644 --- a/Source/Falcor/Scene/Material/MERLMaterialData.slang +++ b/Source/Falcor/Scene/Material/MERLMaterialData.slang @@ -48,7 +48,7 @@ struct MERLMaterialData DiffuseSpecularData extraData = {}; ///< Parameters for a best fit BRDF approximation. TextureHandle texAlbedoLUT; ///< Texture handle for albedo LUT. - static const uint kAlbedoLUTSize = 256; + static constexpr uint kAlbedoLUTSize = 256; }; END_NAMESPACE_FALCOR diff --git a/Source/Falcor/Scene/Material/MERLMixMaterial.cpp b/Source/Falcor/Scene/Material/MERLMixMaterial.cpp index 3285bfee5..74e5830fb 100644 --- a/Source/Falcor/Scene/Material/MERLMixMaterial.cpp +++ b/Source/Falcor/Scene/Material/MERLMixMaterial.cpp @@ -30,7 +30,7 @@ #include "Utils/Logger.h" #include "Utils/BufferAllocator.h" #include "Utils/Scripting/ScriptBindings.h" -#include "Scene/SceneBuilderAccess.h" +#include "GlobalState.h" #include "Scene/Material/MERLFile.h" #include "Scene/Material/MaterialSystem.h" #include "Scene/Material/DiffuseSpecularUtils.h" @@ -46,12 +46,7 @@ namespace Falcor const char kShaderFile[] = "Rendering/Materials/MERLMixMaterial.slang"; } - MERLMixMaterial::SharedPtr MERLMixMaterial::create(std::shared_ptr pDevice, const std::string& name, const std::vector& paths) - { - return SharedPtr(new MERLMixMaterial(pDevice, name, paths)); - } - - MERLMixMaterial::MERLMixMaterial(std::shared_ptr pDevice, const std::string& name, const std::vector& paths) + MERLMixMaterial::MERLMixMaterial(ref pDevice, const std::string& name, const std::vector& paths) : Material(pDevice, name, MaterialType::MERLMix) { checkArgument(!paths.empty(), "MERLMixMaterial: Expected at least one path."); @@ -106,10 +101,10 @@ namespace Falcor } // Create GPU data buffer. - mpBRDFData = buffer.getGPUBuffer(mpDevice.get()); + mpBRDFData = buffer.getGPUBuffer(mpDevice); // Create albedo LUT as 2D texture parameterization over (cosTehta, brdfIndex). - mpAlbedoLUT = Texture::create2D(mpDevice.get(), MERLMixMaterialData::kAlbedoLUTSize, mData.brdfCount, MERLFile::kAlbedoLUTFormat, 1, 1, albedoLut.data(), ResourceBindFlags::ShaderResource); + mpAlbedoLUT = Texture::create2D(mpDevice, MERLMixMaterialData::kAlbedoLUTSize, mData.brdfCount, MERLFile::kAlbedoLUTFormat, 1, 1, albedoLut.data(), ResourceBindFlags::ShaderResource); // Create sampler for albedo LUT. { @@ -117,7 +112,7 @@ namespace Falcor desc.setFilterMode(Sampler::Filter::Linear, Sampler::Filter::Point, Sampler::Filter::Point); desc.setAddressingMode(Sampler::AddressMode::Clamp, Sampler::AddressMode::Clamp, Sampler::AddressMode::Clamp); desc.setMaxAnisotropy(1); - mpLUTSampler = Sampler::create(mpDevice.get(), desc); + mpLUTSampler = Sampler::create(mpDevice, desc); } // Create sampler for index map. Using point sampling as indices are not interpolatable. @@ -126,7 +121,7 @@ namespace Falcor desc.setFilterMode(Sampler::Filter::Point, Sampler::Filter::Point, Sampler::Filter::Point); desc.setAddressingMode(Sampler::AddressMode::Wrap, Sampler::AddressMode::Wrap, Sampler::AddressMode::Wrap); desc.setMaxAnisotropy(1); - mpIndexSampler = Sampler::create(mpDevice.get(), desc); + mpIndexSampler = Sampler::create(mpDevice, desc); } updateNormalMapType(); @@ -159,7 +154,7 @@ namespace Falcor { widget.text("Normal map: " + pTexture->getSourcePath().string()); widget.text("Texture info: " + std::to_string(pTexture->getWidth()) + "x" + std::to_string(pTexture->getHeight()) + " (" + to_string(pTexture->getFormat()) + ")"); - widget.image("Normal map", pTexture, float2(100.f)); + widget.image("Normal map", pTexture.get(), float2(100.f)); if (widget.button("Remove texture##NormalMap")) setNormalMap(nullptr); } else @@ -219,9 +214,9 @@ namespace Falcor return flags; } - bool MERLMixMaterial::isEqual(const Material::SharedPtr& pOther) const + bool MERLMixMaterial::isEqual(const ref& pOther) const { - auto other = std::dynamic_pointer_cast(pOther); + auto other = dynamic_ref_cast(pOther); if (!other) return false; if (!isBaseEqual(*other)) return false; @@ -250,7 +245,7 @@ namespace Falcor return { {{"MERLMixMaterial", "IMaterial"}, (uint32_t)MaterialType::MERLMix} }; } - bool MERLMixMaterial::setTexture(const TextureSlot slot, const Texture::SharedPtr& pTexture) + bool MERLMixMaterial::setTexture(const TextureSlot slot, const ref& pTexture) { if (!Material::setTexture(slot, pTexture)) return false; @@ -270,7 +265,7 @@ namespace Falcor return true; } - void MERLMixMaterial::setDefaultTextureSampler(const Sampler::SharedPtr& pSampler) + void MERLMixMaterial::setDefaultTextureSampler(const ref& pSampler) { if (pSampler != mpDefaultSampler) { @@ -319,10 +314,10 @@ namespace Falcor FALCOR_SCRIPT_BINDING_DEPENDENCY(Material) - pybind11::class_ material(m, "MERLMixMaterial"); + pybind11::class_> material(m, "MERLMixMaterial"); auto create = [](const std::string& name, const std::vector& paths) { - return MERLMixMaterial::create(getActivePythonSceneBuilder().getDevice(), name, paths); + return MERLMixMaterial::create(accessActivePythonSceneBuilder().getDevice(), name, paths); }; material.def(pybind11::init(create), "name"_a, "paths"_a); // PYTHONDEPRECATED } diff --git a/Source/Falcor/Scene/Material/MERLMixMaterial.h b/Source/Falcor/Scene/Material/MERLMixMaterial.h index 321212b03..3ce0dc058 100644 --- a/Source/Falcor/Scene/Material/MERLMixMaterial.h +++ b/Source/Falcor/Scene/Material/MERLMixMaterial.h @@ -46,33 +46,26 @@ namespace Falcor class FALCOR_API MERLMixMaterial : public Material { public: - using SharedPtr = std::shared_ptr; + static ref create(ref pDevice, const std::string& name, const std::vector& paths) { return make_ref(pDevice, name, paths); } - /** Create a new MERLMix material. - \param[in] name The material name. - \param[in] paths List of paths of BRDF files to load. - \return A new object, or throws an exception if creation failed. - */ - static SharedPtr create(std::shared_ptr pDevice, const std::string& name, const std::vector& paths); + MERLMixMaterial(ref pDevice, const std::string& name, const std::vector& paths); bool renderUI(Gui::Widgets& widget) override; Material::UpdateFlags update(MaterialSystem* pOwner) override; - bool isEqual(const Material::SharedPtr& pOther) const override; + bool isEqual(const ref& pOther) const override; MaterialDataBlob getDataBlob() const override { return prepareDataBlob(mData); } Program::ShaderModuleList getShaderModules() const override; Program::TypeConformanceList getTypeConformances() const override; int getBufferCount() const override { return 1; } - bool setTexture(const TextureSlot slot, const Texture::SharedPtr& pTexture) override; - void setDefaultTextureSampler(const Sampler::SharedPtr& pSampler) override; - Sampler::SharedPtr getDefaultTextureSampler() const override { return mpDefaultSampler; } + bool setTexture(const TextureSlot slot, const ref& pTexture) override; + void setDefaultTextureSampler(const ref& pSampler) override; + ref getDefaultTextureSampler() const override { return mpDefaultSampler; } - void setNormalMap(const Texture::SharedPtr& pNormalMap) { setTexture(TextureSlot::Normal, pNormalMap); } - Texture::SharedPtr getNormalMap() const { return getTexture(TextureSlot::Normal); } + void setNormalMap(const ref& pNormalMap) { setTexture(TextureSlot::Normal, pNormalMap); } + ref getNormalMap() const { return getTexture(TextureSlot::Normal); } protected: - MERLMixMaterial(std::shared_ptr pDevice, const std::string& name, const std::vector& paths); - void updateNormalMapType(); void updateIndexMapType(); @@ -92,10 +85,10 @@ namespace Falcor std::vector mBRDFs; ///< List of loaded BRDFs. MERLMixMaterialData mData; ///< Material parameters. - Buffer::SharedPtr mpBRDFData; ///< GPU buffer holding all BRDF data as float3 arrays. - Texture::SharedPtr mpAlbedoLUT; ///< Precomputed albedo lookup table. - Sampler::SharedPtr mpLUTSampler; ///< Sampler for accessing the LUT texture. - Sampler::SharedPtr mpIndexSampler; ///< Sampler for accessing the index map. - Sampler::SharedPtr mpDefaultSampler; + ref mpBRDFData; ///< GPU buffer holding all BRDF data as float3 arrays. + ref mpAlbedoLUT; ///< Precomputed albedo lookup table. + ref mpLUTSampler; ///< Sampler for accessing the LUT texture. + ref mpIndexSampler; ///< Sampler for accessing the index map. + ref mpDefaultSampler; }; } diff --git a/Source/Falcor/Scene/Material/MERLMixMaterialData.slang b/Source/Falcor/Scene/Material/MERLMixMaterialData.slang index 9c9365ced..dd1a3f56e 100644 --- a/Source/Falcor/Scene/Material/MERLMixMaterialData.slang +++ b/Source/Falcor/Scene/Material/MERLMixMaterialData.slang @@ -57,11 +57,11 @@ struct MERLMixMaterialData TextureHandle texIndexMap; TextureHandle texAlbedoLUT; ///< Texture handle for albedo LUT. - static const uint kAlbedoLUTSize = 256; - static const uint kNormalMapTypeBits = 2; - static const uint kNormalMapTypeOffset = 0; - static const uint kLUTSamplerIDOffset = kNormalMapTypeOffset + kNormalMapTypeBits; - static const uint kIndexSamplerIDOffset = kLUTSamplerIDOffset + MaterialHeader::kSamplerIDBits; + static constexpr uint kAlbedoLUTSize = 256; + static constexpr uint kNormalMapTypeBits = 2; + static constexpr uint kNormalMapTypeOffset = 0; + static constexpr uint kLUTSamplerIDOffset = kNormalMapTypeOffset + kNormalMapTypeBits; + static constexpr uint kIndexSamplerIDOffset = kLUTSamplerIDOffset + MaterialHeader::kSamplerIDBits; SETTER_DECL void setNormalMapType(NormalMapType type) { flags = PACK_BITS(kNormalMapTypeBits, kNormalMapTypeOffset, flags, (uint)type); } NormalMapType getNormalMapType() CONST_FUNCTION { return NormalMapType(EXTRACT_BITS(kNormalMapTypeBits, kNormalMapTypeOffset, flags)); } diff --git a/Source/Falcor/Scene/Material/Material.cpp b/Source/Falcor/Scene/Material/Material.cpp index d566fc03e..2f28dfea9 100644 --- a/Source/Falcor/Scene/Material/Material.cpp +++ b/Source/Falcor/Scene/Material/Material.cpp @@ -52,11 +52,11 @@ namespace Falcor bool operator==(const MaterialHeader& lhs, const MaterialHeader& rhs) { - return lhs.packedData == rhs.packedData; + return all(lhs.packedData == rhs.packedData); } - Material::Material(std::shared_ptr pDevice, const std::string& name, MaterialType type) - : mpDevice(std::move(pDevice)) + Material::Material(ref pDevice, const std::string& name, MaterialType type) + : mpDevice(pDevice) , mName(name) { mHeader.setMaterialType(type); @@ -157,7 +157,7 @@ namespace Falcor return mTextureSlotData[(size_t)slot].pTexture != nullptr; } - bool Material::setTexture(const TextureSlot slot, const Texture::SharedPtr& pTexture) + bool Material::setTexture(const TextureSlot slot, const ref& pTexture) { if (!hasTextureSlot(slot)) { @@ -174,7 +174,7 @@ namespace Falcor return true; } - Texture::SharedPtr Material::getTexture(const TextureSlot slot) const + ref Material::getTexture(const TextureSlot slot) const { if (!hasTextureSlot(slot)) return nullptr; @@ -193,7 +193,7 @@ namespace Falcor std::filesystem::path fullPath; if (findFileInDataDirectories(path, fullPath)) { - auto texture = Texture::createFromFile(mpDevice.get(), fullPath, true, useSrgb && getTextureSlotInfo(slot).srgb); + auto texture = Texture::createFromFile(mpDevice, fullPath, true, useSrgb && getTextureSlotInfo(slot).srgb); if (texture) { setTexture(slot, texture); @@ -223,12 +223,12 @@ namespace Falcor mTextureTransform = textureTransform; } - std::shared_ptr Material::toBasicMaterial() + ref Material::toBasicMaterial() { if (mHeader.isBasicMaterial()) { - FALCOR_ASSERT(std::dynamic_pointer_cast(shared_from_this())); - return std::static_pointer_cast(shared_from_this()); + FALCOR_ASSERT(dynamic_ref_cast(ref(this))); + return static_ref_cast(ref(this)); } return nullptr; } @@ -242,14 +242,14 @@ namespace Falcor if (mUpdateCallback) mUpdateCallback(updates); } - void Material::updateTextureHandle(MaterialSystem* pOwner, const Texture::SharedPtr& pTexture, TextureHandle& handle) + void Material::updateTextureHandle(MaterialSystem* pOwner, const ref& pTexture, TextureHandle& handle) { TextureHandle prevHandle = handle; // Update the given texture handle. if (pTexture) { - auto h = pOwner->getTextureManager()->addTexture(pTexture); + auto h = pOwner->getTextureManager().addTexture(pTexture); FALCOR_ASSERT(h); handle.setTextureID(h.getID()); handle.setMode(TextureHandle::Mode::Texture); @@ -269,7 +269,7 @@ namespace Falcor updateTextureHandle(pOwner, pTexture, handle); }; - void Material::updateDefaultTextureSamplerID(MaterialSystem* pOwner, const Sampler::SharedPtr& pSampler) + void Material::updateDefaultTextureSamplerID(MaterialSystem* pOwner, const ref& pSampler) { const uint32_t samplerID = pOwner->addTextureSampler(pSampler); @@ -306,7 +306,7 @@ namespace Falcor return true; } - NormalMapType Material::detectNormalMapType(const Texture::SharedPtr& pNormalMap) + NormalMapType Material::detectNormalMapType(const ref& pNormalMap) { NormalMapType type = NormalMapType::None; if (pNormalMap != nullptr) @@ -332,6 +332,7 @@ namespace Falcor using namespace pybind11::literals; FALCOR_SCRIPT_BINDING_DEPENDENCY(Transform) + FALCOR_SCRIPT_BINDING_DEPENDENCY(Texture) pybind11::enum_ materialType(m, "MaterialType"); materialType.value("Standard", MaterialType::Standard); @@ -362,7 +363,7 @@ namespace Falcor // Register Material base class as IMaterial in python to allow deprecated script syntax. // TODO: Remove workaround when all scripts have been updated to create derived Material classes. - pybind11::class_ material(m, "IMaterial"); // PYTHONDEPRECATED + pybind11::class_> material(m, "IMaterial"); // PYTHONDEPRECATED material.def_property_readonly("type", &Material::getType); material.def_property("name", &Material::getName, &Material::setName); material.def_property("doubleSided", &Material::isDoubleSided, &Material::setDoubleSided); diff --git a/Source/Falcor/Scene/Material/Material.h b/Source/Falcor/Scene/Material/Material.h index 8b1702cf2..6eba105cd 100644 --- a/Source/Falcor/Scene/Material/Material.h +++ b/Source/Falcor/Scene/Material/Material.h @@ -31,6 +31,7 @@ #include "MaterialTypeRegistry.h" #include "Core/Macros.h" #include "Core/Errors.h" +#include "Core/Object.h" #include "Core/API/Formats.h" #include "Core/API/Texture.h" #include "Core/API/Sampler.h" @@ -51,13 +52,9 @@ namespace Falcor /** Abstract base class for materials. */ - class FALCOR_API Material : public std::enable_shared_from_this + class FALCOR_API Material : public Object { public: - // While this is an abstract base class, we still need a holder type (shared_ptr) - // for pybind11 bindings to work on inherited types. - using SharedPtr = std::shared_ptr; - /** Flags indicating if and what was updated in the material. */ enum class UpdateFlags : uint32_t @@ -99,7 +96,7 @@ namespace Falcor struct TextureSlotData { - Texture::SharedPtr pTexture; ///< Texture bound to texture slot. + ref pTexture; ///< Texture bound to texture slot. bool hasData() const { return pTexture != nullptr; } bool operator==(const TextureSlotData& rhs) const { return pTexture == rhs.pTexture; } @@ -155,7 +152,7 @@ namespace Falcor \param[in] pOther Other material. \return true if all materials properties *except* the name are identical. */ - virtual bool isEqual(const Material::SharedPtr& pOther) const = 0; + virtual bool isEqual(const ref& pOther) const = 0; /** Set the double-sided flag. This flag doesn't affect the cull state, just the shading. */ @@ -216,7 +213,7 @@ namespace Falcor \param[in] pTexture The texture. \return True if the texture slot was changed, false otherwise. */ - virtual bool setTexture(const TextureSlot slot, const Texture::SharedPtr& pTexture); + virtual bool setTexture(const TextureSlot slot, const ref& pTexture); /** Load one of the available texture slots. The call is ignored with a warning if the slot doesn't exist. @@ -237,7 +234,7 @@ namespace Falcor \param[in] The texture slot. \return Texture object if bound, or nullptr if unbound or slot doesn't exist. */ - virtual Texture::SharedPtr getTexture(const TextureSlot slot) const; + virtual ref getTexture(const TextureSlot slot) const; /** Optimize texture usage for the given texture slot. This function may replace constant textures by uniform material parameters etc. @@ -253,11 +250,11 @@ namespace Falcor /** Set the default texture sampler for the material. */ - virtual void setDefaultTextureSampler(const Sampler::SharedPtr& pSampler) {} + virtual void setDefaultTextureSampler(const ref& pSampler) {} /** Get the default texture sampler for the material. */ - virtual Sampler::SharedPtr getDefaultTextureSampler() const { return nullptr; } + virtual ref getDefaultTextureSampler() const { return nullptr; } /** Set the material texture transform. */ @@ -309,7 +306,7 @@ namespace Falcor // Temporary convenience function to downcast Material to BasicMaterial. // This is because a large portion of the interface hasn't been ported to the Material base class yet. // TODO: Remove this helper later - std::shared_ptr toBasicMaterial(); + ref toBasicMaterial(); /** Size of the material instance the material produces. Used to set `anyValueSize` on `IMaterialInstance` above the default (128B), for exceptionally large materials. @@ -318,18 +315,18 @@ namespace Falcor virtual size_t getMaterialInstanceByteSize() { return 128; } protected: - Material(std::shared_ptr pDevice, const std::string& name, MaterialType type); + Material(ref pDevice, const std::string& name, MaterialType type); using UpdateCallback = std::function; void registerUpdateCallback(const UpdateCallback& updateCallback) { mUpdateCallback = updateCallback; } void markUpdates(UpdateFlags updates); bool hasTextureSlotData(const TextureSlot slot) const; - void updateTextureHandle(MaterialSystem* pOwner, const Texture::SharedPtr& pTexture, TextureHandle& handle); + void updateTextureHandle(MaterialSystem* pOwner, const ref& pTexture, TextureHandle& handle); void updateTextureHandle(MaterialSystem* pOwner, const TextureSlot slot, TextureHandle& handle); - void updateDefaultTextureSamplerID(MaterialSystem* pOwner, const Sampler::SharedPtr& pSampler); + void updateDefaultTextureSamplerID(MaterialSystem* pOwner, const ref& pSampler); bool isBaseEqual(const Material& other) const; - static NormalMapType detectNormalMapType(const Texture::SharedPtr& pNormalMap); + static NormalMapType detectNormalMapType(const ref& pNormalMap); template MaterialDataBlob prepareDataBlob(const T& data) const @@ -342,7 +339,7 @@ namespace Falcor return blob; } - std::shared_ptr mpDevice; + ref mpDevice; std::string mName; ///< Name of the material. MaterialHeader mHeader; ///< Material header data available in all material types. diff --git a/Source/Falcor/Scene/Material/MaterialData.slang b/Source/Falcor/Scene/Material/MaterialData.slang index ba7dbd1fe..28c20e10b 100644 --- a/Source/Falcor/Scene/Material/MaterialData.slang +++ b/Source/Falcor/Scene/Material/MaterialData.slang @@ -42,32 +42,32 @@ struct MaterialHeader { uint2 packedData = {}; - static const uint kMaterialTypeBits = 16; - static const uint kNestedPriorityBits = 4; - static const uint kLobeTypeBits = 8; // Only 6 bits needed if packing LobeType - static const uint kSamplerIDBits = 8; - static const uint kAlphaModeBits = 1; - static const uint kAlphaThresholdBits = 16; // Using float16_t format + static constexpr uint kMaterialTypeBits = 16; + static constexpr uint kNestedPriorityBits = 4; + static constexpr uint kLobeTypeBits = 8; // Only 6 bits needed if packing LobeType + static constexpr uint kSamplerIDBits = 8; + static constexpr uint kAlphaModeBits = 1; + static constexpr uint kAlphaThresholdBits = 16; // Using float16_t format // packedData.x bit layout - static const uint kMaterialTypeOffset = 0; - static const uint kNestedPriorityOffset = kMaterialTypeOffset + kMaterialTypeBits; - static const uint kLobeTypeOffset = kNestedPriorityOffset + kNestedPriorityBits; - static const uint kDoubleSidedFlagOffset = kLobeTypeOffset + kLobeTypeBits; - static const uint kThinSurfaceFlagOffset = kDoubleSidedFlagOffset + 1; - static const uint kEmissiveFlagOffset = kThinSurfaceFlagOffset + 1; - static const uint kIsBasicMaterialFlagOffset = kEmissiveFlagOffset + 1; + static constexpr uint kMaterialTypeOffset = 0; + static constexpr uint kNestedPriorityOffset = kMaterialTypeOffset + kMaterialTypeBits; + static constexpr uint kLobeTypeOffset = kNestedPriorityOffset + kNestedPriorityBits; + static constexpr uint kDoubleSidedFlagOffset = kLobeTypeOffset + kLobeTypeBits; + static constexpr uint kThinSurfaceFlagOffset = kDoubleSidedFlagOffset + 1; + static constexpr uint kEmissiveFlagOffset = kThinSurfaceFlagOffset + 1; + static constexpr uint kIsBasicMaterialFlagOffset = kEmissiveFlagOffset + 1; - static const uint kTotalHeaderBitsX = kIsBasicMaterialFlagOffset + 1; + static constexpr uint kTotalHeaderBitsX = kIsBasicMaterialFlagOffset + 1; // packedData.y bit layout - static const uint kAlphaThresholdOffset = 0; - static const uint kAlphaModeOffset = kAlphaThresholdOffset + kAlphaThresholdBits; - static const uint kSamplerIDOffset = kAlphaModeOffset + kAlphaModeBits; - static const uint kEnableLightProfileOffset = kSamplerIDOffset + kSamplerIDBits; - static const uint kDeltaSpecularFlagOffset = kEnableLightProfileOffset + 1; + static constexpr uint kAlphaThresholdOffset = 0; + static constexpr uint kAlphaModeOffset = kAlphaThresholdOffset + kAlphaThresholdBits; + static constexpr uint kSamplerIDOffset = kAlphaModeOffset + kAlphaModeBits; + static constexpr uint kEnableLightProfileOffset = kSamplerIDOffset + kSamplerIDBits; + static constexpr uint kDeltaSpecularFlagOffset = kEnableLightProfileOffset + 1; - static const uint kTotalHeaderBitsY = kDeltaSpecularFlagOffset + 1; + static constexpr uint kTotalHeaderBitsY = kDeltaSpecularFlagOffset + 1; /** Set material type. diff --git a/Source/Falcor/Scene/Material/MaterialFactory.slang b/Source/Falcor/Scene/Material/MaterialFactory.slang index f7eacc080..f7fd1d1aa 100644 --- a/Source/Falcor/Scene/Material/MaterialFactory.slang +++ b/Source/Falcor/Scene/Material/MaterialFactory.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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,6 +28,7 @@ __exported import Rendering.Materials.IMaterial; import Scene.Material.ShadingUtils; import Scene.Material.AlphaTest; +import Utils.Math.ShadingFrame; /** Extension to add shading and material factory to MaterialSystem. Making this an extension avoids a circular dependency on MaterialSystem. @@ -57,6 +58,7 @@ extension MaterialSystem Then the material is queried to create the material instance, in which step pattern generation is performed to pre-compute all material parameters. This may involve sampling textures, executing procedural functions, etc. + This step also computes the final shading frame used by the material instance. \param[in] sd Shading data. \param[in] lod Method for computing texture level-of-detail, must implement the `ITextureSampler` interface. \param[in] hints Optional hints to control the behavior of the material instance. Combination of `MaterialInstanceHints` flags. @@ -73,22 +75,21 @@ extension MaterialSystem \param[in] materialID Material ID at the hit. \param[in] viewDir View direction. \param[in] lod Method for computing texture level-of-detail, must implement the `ITextureSampler` interface. - \param[in] modifyNormal Flag to specify if normal can be modified, for example by normal mapping. \return Shading data struct. */ - ShadingData prepareShadingData(const VertexData v, const uint materialID, const float3 viewDir, L lod, const bool modifyNormal = true) + ShadingData prepareShadingData(const VertexData v, const uint materialID, const float3 viewDir, L lod) { ShadingData sd = {}; sd.posW = v.posW; sd.uv = v.texC; sd.V = viewDir; - sd.N = v.normalW; // Compute shading frame based on the interpolated normal and given tangent (xyz) and sign (w). // We keep around the original tangent for later orthogonalization operations if valid, otherwise the invented tangent. - const bool validTangentSpace = computeTangentSpace(sd, v.tangentW); - sd.tangentW = validTangentSpace ? v.tangentW : float4(sd.T, 1.f); + bool valid; + sd.frame = ShadingFrame::createSafe(v.normalW, v.tangentW, valid); + sd.tangentW = valid ? v.tangentW : float4(sd.frame.T, 1.f); // Primitive data sd.faceN = v.faceNormalW; @@ -111,23 +112,6 @@ extension MaterialSystem // Query material to evaluate opacity. sd.opacity = material.evalOpacity(this, v, lod); - if (modifyNormal) - { - // Apply normal mapping only if we have a valid tangent space. - if (validTangentSpace) - { - material.modifyTangentSpace(this, lod, sd); - } - - // Finalize the normal. - // We currently flip the shading normal for back-facing hits on double-sided materials. - // This convention will eventually go away when the materials handle double-sidedness internally. - if (!sd.frontFacing && sd.mtl.isDoubleSided()) - { - sd.N = -sd.N; - } - } - return sd; } diff --git a/Source/Falcor/Scene/Material/MaterialSystem.cpp b/Source/Falcor/Scene/Material/MaterialSystem.cpp index 56384ca9d..569e4ddfb 100644 --- a/Source/Falcor/Scene/Material/MaterialSystem.cpp +++ b/Source/Falcor/Scene/Material/MaterialSystem.cpp @@ -49,34 +49,29 @@ namespace Falcor // Helper to check if a material is a standard material using the SpecGloss shading model. // We keep track of these as an optimization because most scenes do not use this shading model. - bool isSpecGloss(const Material::SharedPtr& pMaterial) + bool isSpecGloss(const ref& pMaterial) { if (pMaterial->getType() == MaterialType::Standard) { - return std::static_pointer_cast(pMaterial)->getShadingModel() == ShadingModel::SpecGloss; + return static_ref_cast(pMaterial)->getShadingModel() == ShadingModel::SpecGloss; } return false; } } - MaterialSystem::SharedPtr MaterialSystem::create(std::shared_ptr pDevice) - { - return SharedPtr(new MaterialSystem(pDevice)); - } - - MaterialSystem::MaterialSystem(std::shared_ptr pDevice) - : mpDevice(std::move(pDevice)) + MaterialSystem::MaterialSystem(ref pDevice) + : mpDevice(pDevice) { FALCOR_ASSERT(kMaxSamplerCount <= mpDevice->getLimits().maxShaderVisibleSamplers); - mpFence = GpuFence::create(mpDevice.get()); - mpTextureManager = TextureManager::create(mpDevice, kMaxTextureCount); + mpFence = GpuFence::create(mpDevice); + mpTextureManager = std::make_unique(mpDevice, kMaxTextureCount); // Create a default texture sampler. Sampler::Desc desc; desc.setFilterMode(Sampler::Filter::Linear, Sampler::Filter::Linear, Sampler::Filter::Linear); desc.setMaxAnisotropy(8); - mpDefaultTextureSampler = Sampler::create(mpDevice.get(), desc); + mpDefaultTextureSampler = Sampler::create(mpDevice, desc); } void MaterialSystem::renderUI(Gui::Widgets& widget) @@ -124,7 +119,7 @@ namespace Falcor }); } - void MaterialSystem::setDefaultTextureSampler(const Sampler::SharedPtr& pSampler) + void MaterialSystem::setDefaultTextureSampler(const ref& pSampler) { mpDefaultTextureSampler = pSampler; for (const auto& pMaterial : mMaterials) @@ -133,10 +128,10 @@ namespace Falcor } } - uint32_t MaterialSystem::addTextureSampler(const Sampler::SharedPtr& pSampler) + uint32_t MaterialSystem::addTextureSampler(const ref& pSampler) { FALCOR_ASSERT(pSampler); - auto isEqual = [&pSampler](const Sampler::SharedPtr& pOther) { + auto isEqual = [&pSampler](const ref& pOther) { return pSampler->getDesc() == pOther->getDesc(); }; @@ -159,7 +154,7 @@ namespace Falcor return samplerID; } - uint32_t MaterialSystem::addBuffer(const Buffer::SharedPtr& pBuffer) + uint32_t MaterialSystem::addBuffer(const ref& pBuffer) { FALCOR_ASSERT(pBuffer); @@ -183,7 +178,7 @@ namespace Falcor return bufferID; } - void MaterialSystem::replaceBuffer(uint32_t id, const Buffer::SharedPtr& pBuffer) + void MaterialSystem::replaceBuffer(uint32_t id, const ref& pBuffer) { FALCOR_ASSERT(pBuffer); checkArgument(id < mBuffers.size(), "'id' is out of bounds."); @@ -192,7 +187,7 @@ namespace Falcor mBuffersChanged = true; } - MaterialID MaterialSystem::addMaterial(const Material::SharedPtr& pMaterial) + MaterialID MaterialSystem::addMaterial(const ref& pMaterial) { checkArgument(pMaterial != nullptr, "'pMaterial' is missing"); @@ -221,7 +216,7 @@ namespace Falcor return materialID; } - void MaterialSystem::replaceMaterial(const Material::SharedPtr& pMaterial, const Material::SharedPtr& pReplacement) + void MaterialSystem::replaceMaterial(const ref& pMaterial, const ref& pReplacement) { checkArgument(pMaterial != nullptr, "'pMaterial' is missing"); checkArgument(pReplacement != nullptr, "'pReplacement' is missing"); @@ -270,13 +265,13 @@ namespace Falcor return materialID.isValid() ? materialID.get() < mMaterials.size() : false; } - const Material::SharedPtr& MaterialSystem::getMaterial(const MaterialID materialID) const + const ref& MaterialSystem::getMaterial(const MaterialID materialID) const { checkArgument(materialID.get() < mMaterials.size(), "MaterialID is out of range."); return mMaterials[materialID.get()]; } - Material::SharedPtr MaterialSystem::getMaterialByName(const std::string& name) const + ref MaterialSystem::getMaterialByName(const std::string& name) const { for (const auto& pMaterial : mMaterials) { @@ -287,7 +282,7 @@ namespace Falcor size_t MaterialSystem::removeDuplicateMaterials(std::vector& idMap) { - std::vector uniqueMaterials; + std::vector> uniqueMaterials; idMap.resize(mMaterials.size()); // Find unique set of materials. @@ -320,8 +315,8 @@ namespace Falcor void MaterialSystem::optimizeMaterials() { // Gather a list of all textures to analyze. - std::vector> materialSlots; - std::vector textures; + std::vector, Material::TextureSlot>> materialSlots; + std::vector> textures; size_t maxCount = mMaterials.size() * (size_t)Material::TextureSlot::Count; materialSlots.reserve(maxCount); textures.reserve(maxCount); @@ -346,14 +341,14 @@ namespace Falcor RenderContext* pRenderContext = mpDevice->getRenderContext(); - TextureAnalyzer::SharedPtr pAnalyzer = TextureAnalyzer::create(mpDevice); - auto pResults = Buffer::create(mpDevice.get(), textures.size() * TextureAnalyzer::getResultSize(), ResourceBindFlags::UnorderedAccess); - pAnalyzer->analyze(pRenderContext, textures, pResults); + TextureAnalyzer analyzer(mpDevice); + auto pResults = Buffer::create(mpDevice, textures.size() * TextureAnalyzer::getResultSize(), ResourceBindFlags::UnorderedAccess); + analyzer.analyze(pRenderContext, textures, pResults); // Copy result to staging buffer for readback. // This is mostly to avoid a full flush and the associated perf warning. // We do not have any other useful GPU work, but unrelated GPU tasks can be in flight. - auto pResultsStaging = Buffer::create(mpDevice.get(), textures.size() * TextureAnalyzer::getResultSize(), ResourceBindFlags::None, Buffer::CpuAccess::Read); + auto pResultsStaging = Buffer::create(mpDevice, textures.size() * TextureAnalyzer::getResultSize(), ResourceBindFlags::None, Buffer::CpuAccess::Read); pRenderContext->copyResource(pResultsStaging.get(), pResults.get()); pRenderContext->flush(false); mpFence->gpuSignal(pRenderContext->getLowLevelData()->getCommandQueue()); @@ -444,11 +439,12 @@ namespace Falcor } } + auto blockVar = mpMaterialsBlock->getRootVar(); // Update samplers. if (forceUpdate || mSamplersChanged) { - auto var = mpMaterialsBlock[kMaterialSamplersName]; + auto var = blockVar[kMaterialSamplersName]; for (size_t i = 0; i < mTextureSamplers.size(); i++) { var[i] = mTextureSamplers[i]; @@ -460,14 +456,14 @@ namespace Falcor if (forceUpdate || is_set(flags, Material::UpdateFlags::ResourcesChanged)) { FALCOR_ASSERT(!mMaterialsChanged); - mpTextureManager->setShaderData(mpMaterialsBlock[kMaterialTexturesName], mTextureDescCount, - mpMaterialsBlock["udimIndirection"]); + mpTextureManager->setShaderData(blockVar[kMaterialTexturesName], mTextureDescCount, + blockVar["udimIndirection"]); } // Update buffers. if (forceUpdate || mBuffersChanged) { - auto var = mpMaterialsBlock[kMaterialBuffersName]; + auto var = blockVar[kMaterialBuffersName]; for (size_t i = 0; i < mBuffers.size(); i++) { var[i] = mBuffers[i]; @@ -624,7 +620,7 @@ namespace Falcor return mShaderModules; } - const ParameterBlock::SharedPtr& MaterialSystem::getParameterBlock() const + const ref& MaterialSystem::getParameterBlock() const { checkInvariant(mpMaterialsBlock != nullptr && !mMaterialsChanged, "Parameter block is not ready. Call update() first."); return mpMaterialsBlock; @@ -639,7 +635,7 @@ namespace Falcor auto pReflector = pPass->getProgram()->getReflector()->getParameterBlock("gMaterialsBlock"); FALCOR_ASSERT(pReflector); - mpMaterialsBlock = ParameterBlock::create(mpDevice.get(), pReflector); + mpMaterialsBlock = ParameterBlock::create(mpDevice, pReflector); FALCOR_ASSERT(mpMaterialsBlock); // Verify that the material data struct size on the GPU matches the host-side size. @@ -653,16 +649,18 @@ namespace Falcor throw RuntimeError("MaterialSystem material data buffer has unexpected struct size"); } + auto blockVar = mpMaterialsBlock->getRootVar(); + // Create materials data buffer. if (!mMaterials.empty() && (!mpMaterialDataBuffer || mpMaterialDataBuffer->getElementCount() < mMaterials.size())) { - mpMaterialDataBuffer = Buffer::createStructured(mpDevice.get(), mpMaterialsBlock[kMaterialDataName], (uint32_t)mMaterials.size(), Resource::BindFlags::ShaderResource, Buffer::CpuAccess::None, nullptr, false); + mpMaterialDataBuffer = Buffer::createStructured(mpDevice, blockVar[kMaterialDataName], (uint32_t)mMaterials.size(), Resource::BindFlags::ShaderResource, Buffer::CpuAccess::None, nullptr, false); mpMaterialDataBuffer->setName("MaterialSystem::mpMaterialDataBuffer"); } // Bind resources to parameter block. - mpMaterialsBlock[kMaterialDataName] = !mMaterials.empty() ? mpMaterialDataBuffer : nullptr; - mpMaterialsBlock["materialCount"] = getMaterialCount(); + blockVar[kMaterialDataName] = !mMaterials.empty() ? mpMaterialDataBuffer : nullptr; + blockVar["materialCount"] = getMaterialCount(); } void MaterialSystem::uploadMaterial(const uint32_t materialID) diff --git a/Source/Falcor/Scene/Material/MaterialSystem.h b/Source/Falcor/Scene/Material/MaterialSystem.h index e4ddda20b..af00d7f9b 100644 --- a/Source/Falcor/Scene/Material/MaterialSystem.h +++ b/Source/Falcor/Scene/Material/MaterialSystem.h @@ -57,8 +57,6 @@ namespace Falcor class FALCOR_API MaterialSystem { public: - using SharedPtr = std::shared_ptr; - struct MaterialStats { uint64_t materialTypeCount = 0; ///< Number of material types. @@ -72,10 +70,9 @@ namespace Falcor uint64_t textureMemoryInBytes = 0; ///< Total memory in bytes used by the textures. }; - /** Create a material system. - \return New object, or throws an exception if creation failed. + /** Constructor. Throws an exception if creation failed. */ - static SharedPtr create(std::shared_ptr pDevice); + MaterialSystem(ref pDevice); /** Render the UI. */ @@ -112,18 +109,18 @@ namespace Falcor /** Get the parameter block with all material resources. */ - const ParameterBlock::SharedPtr& getParameterBlock() const; + const ref& getParameterBlock() const; /** Set a default texture sampler to use for all materials. */ - void setDefaultTextureSampler(const Sampler::SharedPtr& pSampler); + void setDefaultTextureSampler(const ref& pSampler); /** Add a texture sampler. If an identical sampler already exists, the sampler is not added and the existing ID returned. \param[in] pSampler The sampler. \return The ID of the sampler. */ - uint32_t addTextureSampler(const Sampler::SharedPtr& pSampler); + uint32_t addTextureSampler(const ref& pSampler); /** Get the total number of texture samplers. */ @@ -131,19 +128,19 @@ namespace Falcor /** Get a texture sampler by ID. */ - const Sampler::SharedPtr& getTextureSampler(const uint32_t samplerID) const { return mTextureSamplers[samplerID]; } + const ref& getTextureSampler(const uint32_t samplerID) const { return mTextureSamplers[samplerID]; } /** Add a buffer resource to be managed. \param[in] pBuffer The buffer. \return The ID of the buffer. */ - uint32_t addBuffer(const Buffer::SharedPtr& pBuffer); + uint32_t addBuffer(const ref& pBuffer); /** Replace a previously managed buffer by a new buffer. \param[in] id The ID of the buffer. \param[in] pBuffer The buffer. */ - void replaceBuffer(uint32_t id, const Buffer::SharedPtr& pBuffer); + void replaceBuffer(uint32_t id, const ref& pBuffer); /** Get the total number of managed buffers. */ @@ -154,17 +151,17 @@ namespace Falcor \param[in] pMaterial The material. \return The ID of the material. */ - MaterialID addMaterial(const Material::SharedPtr& pMaterial); + MaterialID addMaterial(const ref& pMaterial); /** Replace a material. \param pMaterial The material to replace. \param pReplacement The material to replace it with. */ - void replaceMaterial(const Material::SharedPtr& pMaterial, const Material::SharedPtr& pReplacement); + void replaceMaterial(const ref& pMaterial, const ref& pReplacement); /** Get a list of all materials. */ - const std::vector& getMaterials() const { return mMaterials; } + const std::vector>& getMaterials() const { return mMaterials; } /** Get the total number of materials. */ @@ -192,12 +189,12 @@ namespace Falcor \param[in] materialID The material ID. \return The material, or throws if the material doesn't exist. */ - const Material::SharedPtr& getMaterial(const MaterialID materialID) const; + const ref& getMaterial(const MaterialID materialID) const; /** Get a material by name. \return The material, or nullptr if material doesn't exist. */ - Material::SharedPtr getMaterialByName(const std::string& name) const; + ref getMaterialByName(const std::string& name) const; /** Remove all duplicate materials. \param[in] idMap Vector that holds for each material the ID of the material that replaces it. @@ -216,22 +213,20 @@ namespace Falcor /** Get texture manager. This holds all textures. */ - const TextureManager::SharedPtr& getTextureManager() { return mpTextureManager; } + TextureManager& getTextureManager() { return *mpTextureManager; } private: - MaterialSystem(std::shared_ptr pDevice); - void updateMetadata(); void updateUI(); void createParameterBlock(); void uploadMaterial(const uint32_t materialID); - std::shared_ptr mpDevice; + ref mpDevice; - std::vector mMaterials; ///< List of all materials. + std::vector> mMaterials; ///< List of all materials. std::vector mMaterialsUpdateFlags; ///< List of all material update flags, after the update() calls - TextureManager::SharedPtr mpTextureManager; ///< Texture manager holding all material textures. + std::unique_ptr mpTextureManager; ///< Texture manager holding all material textures. Program::ShaderModuleList mShaderModules; ///< Shader modules for all materials in use. std::map mTypeConformances; ///< Type conformances for each material type in use. @@ -250,12 +245,12 @@ namespace Falcor Material::UpdateFlags mMaterialUpdates = Material::UpdateFlags::None; ///< Material updates across all materials since last update. // GPU resources - GpuFence::SharedPtr mpFence; - ParameterBlock::SharedPtr mpMaterialsBlock; ///< Parameter block for binding all material resources. - Buffer::SharedPtr mpMaterialDataBuffer; ///< GPU buffer holding all material data. - Sampler::SharedPtr mpDefaultTextureSampler; ///< Default texture sampler to use for all materials. - std::vector mTextureSamplers; ///< Texture sampler states. These are indexed by ID in the materials. - std::vector mBuffers; ///< Buffers used by the materials. These are indexed by ID in the materials. + ref mpFence; + ref mpMaterialsBlock; ///< Parameter block for binding all material resources. + ref mpMaterialDataBuffer; ///< GPU buffer holding all material data. + ref mpDefaultTextureSampler; ///< Default texture sampler to use for all materials. + std::vector> mTextureSamplers; ///< Texture sampler states. These are indexed by ID in the materials. + std::vector> mBuffers; ///< Buffers used by the materials. These are indexed by ID in the materials. // UI variables std::vector mSortedMaterialIndices; ///< Indices of materials, sorted alphabetically by case-insensitive name. diff --git a/Source/Falcor/Scene/Material/MaterialTextureLoader.cpp b/Source/Falcor/Scene/Material/MaterialTextureLoader.cpp index 435905d9e..b7ab9f975 100644 --- a/Source/Falcor/Scene/Material/MaterialTextureLoader.cpp +++ b/Source/Falcor/Scene/Material/MaterialTextureLoader.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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,9 +30,9 @@ namespace Falcor { - MaterialTextureLoader::MaterialTextureLoader(const TextureManager::SharedPtr& pTextureManager, bool useSrgb) + MaterialTextureLoader::MaterialTextureLoader(TextureManager& textureManager, bool useSrgb) : mUseSrgb(useSrgb) - , mpTextureManager(pTextureManager) + , mTextureManager(textureManager) { } @@ -41,7 +41,7 @@ namespace Falcor assignTextures(); } - void MaterialTextureLoader::loadTexture(const Material::SharedPtr& pMaterial, Material::TextureSlot slot, const std::filesystem::path& path) + void MaterialTextureLoader::loadTexture(const ref& pMaterial, Material::TextureSlot slot, const std::filesystem::path& path) { FALCOR_ASSERT(pMaterial); if (!pMaterial->hasTextureSlot(slot)) @@ -53,7 +53,7 @@ namespace Falcor bool srgb = mUseSrgb && pMaterial->getTextureSlotInfo(slot).srgb; // Request texture to be loaded. - auto handle = mpTextureManager->loadTexture(path, true, srgb); + auto handle = mTextureManager.loadTexture(path, true, srgb); // Store assignment to material for later. mTextureAssignments.emplace_back(TextureAssignment{ pMaterial, slot, handle }); @@ -61,12 +61,12 @@ namespace Falcor void MaterialTextureLoader::assignTextures() { - mpTextureManager->waitForAllTexturesLoading(); + mTextureManager.waitForAllTexturesLoading(); // Assign textures to materials. for (const auto& assignment : mTextureAssignments) { - auto pTexture = mpTextureManager->getTexture(assignment.handle); + auto pTexture = mTextureManager.getTexture(assignment.handle); assignment.pMaterial->setTexture(assignment.textureSlot, pTexture); } } diff --git a/Source/Falcor/Scene/Material/MaterialTextureLoader.h b/Source/Falcor/Scene/Material/MaterialTextureLoader.h index 0ecf80eda..b976ed05e 100644 --- a/Source/Falcor/Scene/Material/MaterialTextureLoader.h +++ b/Source/Falcor/Scene/Material/MaterialTextureLoader.h @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -45,7 +45,7 @@ namespace Falcor class MaterialTextureLoader { public: - MaterialTextureLoader(const TextureManager::SharedPtr& pTextureManager, bool useSrgb); + MaterialTextureLoader(TextureManager& textureManager, bool useSrgb); ~MaterialTextureLoader(); /** Request loading a material texture. @@ -53,20 +53,20 @@ namespace Falcor \param[in] slot Slot to load texture into. \param[in] path Texture file path. */ - void loadTexture(const Material::SharedPtr& pMaterial, Material::TextureSlot slot, const std::filesystem::path& path); + void loadTexture(const ref& pMaterial, Material::TextureSlot slot, const std::filesystem::path& path); private: void assignTextures(); struct TextureAssignment { - Material::SharedPtr pMaterial; + ref pMaterial; Material::TextureSlot textureSlot; TextureManager::TextureHandle handle; }; bool mUseSrgb; std::vector mTextureAssignments; - TextureManager::SharedPtr mpTextureManager; + TextureManager& mTextureManager; }; } diff --git a/Source/Falcor/Scene/Material/PBRT/PBRTCoatedConductorMaterial.cpp b/Source/Falcor/Scene/Material/PBRT/PBRTCoatedConductorMaterial.cpp index a22741050..6031e2005 100644 --- a/Source/Falcor/Scene/Material/PBRT/PBRTCoatedConductorMaterial.cpp +++ b/Source/Falcor/Scene/Material/PBRT/PBRTCoatedConductorMaterial.cpp @@ -27,7 +27,7 @@ **************************************************************************/ #include "PBRTCoatedConductorMaterial.h" #include "Utils/Scripting/ScriptBindings.h" -#include "Scene/SceneBuilderAccess.h" +#include "GlobalState.h" namespace Falcor { @@ -36,13 +36,8 @@ namespace Falcor const char kShaderFile[] = "Rendering/Materials/PBRT/PBRTCoatedConductorMaterial.slang"; } - PBRTCoatedConductorMaterial::SharedPtr PBRTCoatedConductorMaterial::create(std::shared_ptr pDevice, const std::string& name) - { - return SharedPtr(new PBRTCoatedConductorMaterial(std::move(pDevice), name)); - } - - PBRTCoatedConductorMaterial::PBRTCoatedConductorMaterial(std::shared_ptr pDevice, const std::string& name) - : BasicMaterial(std::move(pDevice), name, MaterialType::PBRTCoatedConductor) + PBRTCoatedConductorMaterial::PBRTCoatedConductorMaterial(ref pDevice, const std::string& name) + : BasicMaterial(pDevice, name, MaterialType::PBRTCoatedConductor) { // Setup additional texture slots. mTextureSlotInfo[(uint32_t)TextureSlot::BaseColor] = { "baseColor", TextureChannelFlags::RGBA, false }; @@ -89,10 +84,10 @@ namespace Falcor FALCOR_SCRIPT_BINDING_DEPENDENCY(BasicMaterial) - pybind11::class_ material(m, "PBRTCoatedConductorMaterial"); + pybind11::class_> material(m, "PBRTCoatedConductorMaterial"); auto create = [] (const std::string& name) { - return PBRTCoatedConductorMaterial::create(getActivePythonSceneBuilder().getDevice(), name); + return PBRTCoatedConductorMaterial::create(accessActivePythonSceneBuilder().getDevice(), name); }; material.def(pybind11::init(create), "name"_a = ""); // PYTHONDEPRECATED diff --git a/Source/Falcor/Scene/Material/PBRT/PBRTCoatedConductorMaterial.h b/Source/Falcor/Scene/Material/PBRT/PBRTCoatedConductorMaterial.h index 8b9f90867..9c6359687 100644 --- a/Source/Falcor/Scene/Material/PBRT/PBRTCoatedConductorMaterial.h +++ b/Source/Falcor/Scene/Material/PBRT/PBRTCoatedConductorMaterial.h @@ -58,12 +58,9 @@ namespace Falcor class FALCOR_API PBRTCoatedConductorMaterial : public BasicMaterial { public: - using SharedPtr = std::shared_ptr; + static ref create(ref pDevice, const std::string& name) { return make_ref(pDevice, name); } - /** Create a new PBRTCoatedConductor material. - \param[in] name The material name. - */ - static SharedPtr create(std::shared_ptr pDevice, const std::string& name = ""); + PBRTCoatedConductorMaterial(ref pDevice, const std::string& name); Program::ShaderModuleList getShaderModules() const override; Program::TypeConformanceList getTypeConformances() const override; @@ -77,8 +74,6 @@ namespace Falcor float4 getRoughness() const { return float4(mData.specular[0], mData.specular[1], mData.specular[2], mData.specular[3]); } protected: - PBRTCoatedConductorMaterial(std::shared_ptr pDevice, const std::string& name); - void renderSpecularUI(Gui::Widgets& widget) override; }; } diff --git a/Source/Falcor/Scene/Material/PBRT/PBRTCoatedDiffuseMaterial.cpp b/Source/Falcor/Scene/Material/PBRT/PBRTCoatedDiffuseMaterial.cpp index 10313a641..8e2635b52 100644 --- a/Source/Falcor/Scene/Material/PBRT/PBRTCoatedDiffuseMaterial.cpp +++ b/Source/Falcor/Scene/Material/PBRT/PBRTCoatedDiffuseMaterial.cpp @@ -27,7 +27,7 @@ **************************************************************************/ #include "PBRTCoatedDiffuseMaterial.h" #include "Utils/Scripting/ScriptBindings.h" -#include "Scene/SceneBuilderAccess.h" +#include "GlobalState.h" namespace Falcor { @@ -36,13 +36,8 @@ namespace Falcor const char kShaderFile[] = "Rendering/Materials/PBRT/PBRTCoatedDiffuseMaterial.slang"; } - PBRTCoatedDiffuseMaterial::SharedPtr PBRTCoatedDiffuseMaterial::create(std::shared_ptr pDevice, const std::string& name) - { - return SharedPtr(new PBRTCoatedDiffuseMaterial(std::move(pDevice), name)); - } - - PBRTCoatedDiffuseMaterial::PBRTCoatedDiffuseMaterial(std::shared_ptr pDevice, const std::string& name) - : BasicMaterial(std::move(pDevice), name, MaterialType::PBRTCoatedDiffuse) + PBRTCoatedDiffuseMaterial::PBRTCoatedDiffuseMaterial(ref pDevice, const std::string& name) + : BasicMaterial(pDevice, name, MaterialType::PBRTCoatedDiffuse) { // Setup additional texture slots. mTextureSlotInfo[(uint32_t)TextureSlot::BaseColor] = { "baseColor", TextureChannelFlags::RGBA, true }; @@ -83,10 +78,10 @@ namespace Falcor FALCOR_SCRIPT_BINDING_DEPENDENCY(BasicMaterial) - pybind11::class_ material(m, "PBRTCoatedDiffuseMaterial"); + pybind11::class_> material(m, "PBRTCoatedDiffuseMaterial"); auto create = [] (const std::string& name) { - return PBRTCoatedDiffuseMaterial::create(getActivePythonSceneBuilder().getDevice(), name); + return PBRTCoatedDiffuseMaterial::create(accessActivePythonSceneBuilder().getDevice(), name); }; material.def(pybind11::init(create), "name"_a = ""); // PYTHONDEPRECATED diff --git a/Source/Falcor/Scene/Material/PBRT/PBRTCoatedDiffuseMaterial.h b/Source/Falcor/Scene/Material/PBRT/PBRTCoatedDiffuseMaterial.h index c9fedf7c9..6e01cfff7 100644 --- a/Source/Falcor/Scene/Material/PBRT/PBRTCoatedDiffuseMaterial.h +++ b/Source/Falcor/Scene/Material/PBRT/PBRTCoatedDiffuseMaterial.h @@ -54,12 +54,9 @@ namespace Falcor class FALCOR_API PBRTCoatedDiffuseMaterial : public BasicMaterial { public: - using SharedPtr = std::shared_ptr; + static ref create(ref pDevice, const std::string& name) { return make_ref(pDevice, name); } - /** Create a new PBRTCoatedDiffuse material. - \param[in] name The material name. - */ - static SharedPtr create(std::shared_ptr pDevice, const std::string& name = ""); + PBRTCoatedDiffuseMaterial(ref pDevice, const std::string& name); Program::ShaderModuleList getShaderModules() const override; Program::TypeConformanceList getTypeConformances() const override; @@ -73,8 +70,6 @@ namespace Falcor float2 getRoughness() const { return float2(mData.specular[0], mData.specular[1]); } protected: - PBRTCoatedDiffuseMaterial(std::shared_ptr pDevice, const std::string& name); - void renderSpecularUI(Gui::Widgets& widget) override; }; } diff --git a/Source/Falcor/Scene/Material/PBRT/PBRTConductorMaterial.cpp b/Source/Falcor/Scene/Material/PBRT/PBRTConductorMaterial.cpp index ae885040c..e3f2f56d5 100644 --- a/Source/Falcor/Scene/Material/PBRT/PBRTConductorMaterial.cpp +++ b/Source/Falcor/Scene/Material/PBRT/PBRTConductorMaterial.cpp @@ -27,7 +27,7 @@ **************************************************************************/ #include "PBRTConductorMaterial.h" #include "Utils/Scripting/ScriptBindings.h" -#include "Scene/SceneBuilderAccess.h" +#include "GlobalState.h" namespace Falcor { @@ -36,13 +36,8 @@ namespace Falcor const char kShaderFile[] = "Rendering/Materials/PBRT/PBRTConductorMaterial.slang"; } - PBRTConductorMaterial::SharedPtr PBRTConductorMaterial::create(std::shared_ptr pDevice, const std::string& name) - { - return SharedPtr(new PBRTConductorMaterial(std::move(pDevice), name)); - } - - PBRTConductorMaterial::PBRTConductorMaterial(std::shared_ptr pDevice, const std::string& name) - : BasicMaterial(std::move(pDevice), name, MaterialType::PBRTConductor) + PBRTConductorMaterial::PBRTConductorMaterial(ref pDevice, const std::string& name) + : BasicMaterial(pDevice, name, MaterialType::PBRTConductor) { // Setup additional texture slots. mTextureSlotInfo[(uint32_t)TextureSlot::BaseColor] = { "baseColor", TextureChannelFlags::RGBA, false }; @@ -84,10 +79,10 @@ namespace Falcor FALCOR_SCRIPT_BINDING_DEPENDENCY(BasicMaterial) - pybind11::class_ material(m, "PBRTConductorMaterial"); + pybind11::class_> material(m, "PBRTConductorMaterial"); auto create = [] (const std::string& name) { - return PBRTConductorMaterial::create(getActivePythonSceneBuilder().getDevice(), name); + return PBRTConductorMaterial::create(accessActivePythonSceneBuilder().getDevice(), name); }; material.def(pybind11::init(create), "name"_a = ""); // PYTHONDEPRECATED diff --git a/Source/Falcor/Scene/Material/PBRT/PBRTConductorMaterial.h b/Source/Falcor/Scene/Material/PBRT/PBRTConductorMaterial.h index 810337358..7a2cf6064 100644 --- a/Source/Falcor/Scene/Material/PBRT/PBRTConductorMaterial.h +++ b/Source/Falcor/Scene/Material/PBRT/PBRTConductorMaterial.h @@ -58,12 +58,9 @@ namespace Falcor class FALCOR_API PBRTConductorMaterial : public BasicMaterial { public: - using SharedPtr = std::shared_ptr; + static ref create(ref pDevice, const std::string& name) { return make_ref(pDevice, name); } - /** Create a new PBRTConductor material. - \param[in] name The material name. - */ - static SharedPtr create(std::shared_ptr pDevice, const std::string& name = ""); + PBRTConductorMaterial(ref pDevice, const std::string& name); Program::ShaderModuleList getShaderModules() const override; Program::TypeConformanceList getTypeConformances() const override; @@ -77,8 +74,6 @@ namespace Falcor float2 getRoughness() const { return float2(mData.specular[0], mData.specular[1]); } protected: - PBRTConductorMaterial(std::shared_ptr pDevice, const std::string& name); - void renderSpecularUI(Gui::Widgets& widget) override; }; } diff --git a/Source/Falcor/Scene/Material/PBRT/PBRTDielectricMaterial.cpp b/Source/Falcor/Scene/Material/PBRT/PBRTDielectricMaterial.cpp index d1f3062ed..f66cc290a 100644 --- a/Source/Falcor/Scene/Material/PBRT/PBRTDielectricMaterial.cpp +++ b/Source/Falcor/Scene/Material/PBRT/PBRTDielectricMaterial.cpp @@ -27,7 +27,7 @@ **************************************************************************/ #include "PBRTDielectricMaterial.h" #include "Utils/Scripting/ScriptBindings.h" -#include "Scene/SceneBuilderAccess.h" +#include "GlobalState.h" namespace Falcor { @@ -36,13 +36,8 @@ namespace Falcor const char kShaderFile[] = "Rendering/Materials/PBRT/PBRTDielectricMaterial.slang"; } - PBRTDielectricMaterial::SharedPtr PBRTDielectricMaterial::create(std::shared_ptr pDevice, const std::string& name) - { - return SharedPtr(new PBRTDielectricMaterial(std::move(pDevice), name)); - } - - PBRTDielectricMaterial::PBRTDielectricMaterial(std::shared_ptr pDevice, const std::string& name) - : BasicMaterial(std::move(pDevice), name, MaterialType::PBRTDielectric) + PBRTDielectricMaterial::PBRTDielectricMaterial(ref pDevice, const std::string& name) + : BasicMaterial(pDevice, name, MaterialType::PBRTDielectric) { // Setup additional texture slots. mTextureSlotInfo[(uint32_t)TextureSlot::Specular] = { "specular", TextureChannelFlags::Red | TextureChannelFlags::Green, false }; @@ -82,10 +77,10 @@ namespace Falcor FALCOR_SCRIPT_BINDING_DEPENDENCY(BasicMaterial) - pybind11::class_ material(m, "PBRTDielectricMaterial"); + pybind11::class_> material(m, "PBRTDielectricMaterial"); auto create = [] (const std::string& name) { - return PBRTDielectricMaterial::create(getActivePythonSceneBuilder().getDevice(), name); + return PBRTDielectricMaterial::create(accessActivePythonSceneBuilder().getDevice(), name); }; material.def(pybind11::init(create), "name"_a = ""); // PYTHONDEPRECATED diff --git a/Source/Falcor/Scene/Material/PBRT/PBRTDielectricMaterial.h b/Source/Falcor/Scene/Material/PBRT/PBRTDielectricMaterial.h index d079e16cf..cd032892e 100644 --- a/Source/Falcor/Scene/Material/PBRT/PBRTDielectricMaterial.h +++ b/Source/Falcor/Scene/Material/PBRT/PBRTDielectricMaterial.h @@ -52,12 +52,9 @@ namespace Falcor class FALCOR_API PBRTDielectricMaterial : public BasicMaterial { public: - using SharedPtr = std::shared_ptr; + static ref create(ref pDevice, const std::string& name) { return make_ref(pDevice, name); } - /** Create a new PBRTDielectric material. - \param[in] name The material name. - */ - static SharedPtr create(std::shared_ptr pDevice, const std::string& name = ""); + PBRTDielectricMaterial(ref pDevice, const std::string& name); Program::ShaderModuleList getShaderModules() const override; Program::TypeConformanceList getTypeConformances() const override; @@ -71,8 +68,6 @@ namespace Falcor float2 getRoughness() const { return float2(mData.specular[0], mData.specular[1]); } protected: - PBRTDielectricMaterial(std::shared_ptr pDevice, const std::string& name); - void renderSpecularUI(Gui::Widgets& widget) override; }; } diff --git a/Source/Falcor/Scene/Material/PBRT/PBRTDiffuseMaterial.cpp b/Source/Falcor/Scene/Material/PBRT/PBRTDiffuseMaterial.cpp index 878f44bfd..195cec432 100644 --- a/Source/Falcor/Scene/Material/PBRT/PBRTDiffuseMaterial.cpp +++ b/Source/Falcor/Scene/Material/PBRT/PBRTDiffuseMaterial.cpp @@ -27,7 +27,7 @@ **************************************************************************/ #include "PBRTDiffuseMaterial.h" #include "Utils/Scripting/ScriptBindings.h" -#include "Scene/SceneBuilderAccess.h" +#include "GlobalState.h" namespace Falcor { @@ -36,13 +36,8 @@ namespace Falcor const char kShaderFile[] = "Rendering/Materials/PBRT/PBRTDiffuseMaterial.slang"; } - PBRTDiffuseMaterial::SharedPtr PBRTDiffuseMaterial::create(std::shared_ptr pDevice, const std::string& name) - { - return SharedPtr(new PBRTDiffuseMaterial(std::move(pDevice), name)); - } - - PBRTDiffuseMaterial::PBRTDiffuseMaterial(std::shared_ptr pDevice, const std::string& name) - : BasicMaterial(std::move(pDevice), name, MaterialType::PBRTDiffuse) + PBRTDiffuseMaterial::PBRTDiffuseMaterial(ref pDevice, const std::string& name) + : BasicMaterial(pDevice, name, MaterialType::PBRTDiffuse) { // Setup additional texture slots. mTextureSlotInfo[(uint32_t)TextureSlot::BaseColor] = { "baseColor", TextureChannelFlags::RGBA, true }; @@ -65,10 +60,10 @@ namespace Falcor FALCOR_SCRIPT_BINDING_DEPENDENCY(BasicMaterial) - pybind11::class_ material(m, "PBRTDiffuseMaterial"); + pybind11::class_> material(m, "PBRTDiffuseMaterial"); auto create = [] (const std::string& name) { - return PBRTDiffuseMaterial::create(getActivePythonSceneBuilder().getDevice(), name); + return PBRTDiffuseMaterial::create(accessActivePythonSceneBuilder().getDevice(), name); }; material.def(pybind11::init(create), "name"_a = ""); // PYTHONDEPRECATED } diff --git a/Source/Falcor/Scene/Material/PBRT/PBRTDiffuseMaterial.h b/Source/Falcor/Scene/Material/PBRT/PBRTDiffuseMaterial.h index 0827e1e5d..073dab890 100644 --- a/Source/Falcor/Scene/Material/PBRT/PBRTDiffuseMaterial.h +++ b/Source/Falcor/Scene/Material/PBRT/PBRTDiffuseMaterial.h @@ -48,17 +48,11 @@ namespace Falcor class FALCOR_API PBRTDiffuseMaterial : public BasicMaterial { public: - using SharedPtr = std::shared_ptr; + static ref create(ref pDevice, const std::string& name) { return make_ref(pDevice, name); } - /** Create a new PBRTDiffuse material. - \param[in] name The material name. - */ - static SharedPtr create(std::shared_ptr pDevice, const std::string& name = ""); + PBRTDiffuseMaterial(ref pDevice, const std::string& name); Program::ShaderModuleList getShaderModules() const override; Program::TypeConformanceList getTypeConformances() const override; - - protected: - PBRTDiffuseMaterial(std::shared_ptr pDevice, const std::string& name); }; } diff --git a/Source/Falcor/Scene/Material/PBRT/PBRTDiffuseTransmissionMaterial.cpp b/Source/Falcor/Scene/Material/PBRT/PBRTDiffuseTransmissionMaterial.cpp index 917652302..ca92fce93 100644 --- a/Source/Falcor/Scene/Material/PBRT/PBRTDiffuseTransmissionMaterial.cpp +++ b/Source/Falcor/Scene/Material/PBRT/PBRTDiffuseTransmissionMaterial.cpp @@ -27,7 +27,7 @@ **************************************************************************/ #include "PBRTDiffuseTransmissionMaterial.h" #include "Utils/Scripting/ScriptBindings.h" -#include "Scene/SceneBuilderAccess.h" +#include "GlobalState.h" namespace Falcor { @@ -36,13 +36,8 @@ namespace Falcor const char kShaderFile[] = "Rendering/Materials/PBRT/PBRTDiffuseTransmissionMaterial.slang"; } - PBRTDiffuseTransmissionMaterial::SharedPtr PBRTDiffuseTransmissionMaterial::create(std::shared_ptr pDevice, const std::string& name) - { - return SharedPtr(new PBRTDiffuseTransmissionMaterial(std::move(pDevice), name)); - } - - PBRTDiffuseTransmissionMaterial::PBRTDiffuseTransmissionMaterial(std::shared_ptr pDevice, const std::string& name) - : BasicMaterial(std::move(pDevice), name, MaterialType::PBRTDiffuseTransmission) + PBRTDiffuseTransmissionMaterial::PBRTDiffuseTransmissionMaterial(ref pDevice, const std::string& name) + : BasicMaterial(pDevice, name, MaterialType::PBRTDiffuseTransmission) { // Setup additional texture slots. mTextureSlotInfo[(uint32_t)TextureSlot::BaseColor] = { "baseColor", TextureChannelFlags::RGBA, true }; @@ -66,10 +61,10 @@ namespace Falcor FALCOR_SCRIPT_BINDING_DEPENDENCY(BasicMaterial) - pybind11::class_ material(m, "PBRTDiffuseTransmissionMaterial"); + pybind11::class_> material(m, "PBRTDiffuseTransmissionMaterial"); auto create = [] (const std::string& name) { - return PBRTDiffuseTransmissionMaterial::create(getActivePythonSceneBuilder().getDevice(), name); + return PBRTDiffuseTransmissionMaterial::create(accessActivePythonSceneBuilder().getDevice(), name); }; material.def(pybind11::init(create), "name"_a = ""); // PYTHONDEPRECATED } diff --git a/Source/Falcor/Scene/Material/PBRT/PBRTDiffuseTransmissionMaterial.h b/Source/Falcor/Scene/Material/PBRT/PBRTDiffuseTransmissionMaterial.h index 766f90e5f..a42726668 100644 --- a/Source/Falcor/Scene/Material/PBRT/PBRTDiffuseTransmissionMaterial.h +++ b/Source/Falcor/Scene/Material/PBRT/PBRTDiffuseTransmissionMaterial.h @@ -52,17 +52,11 @@ namespace Falcor class FALCOR_API PBRTDiffuseTransmissionMaterial : public BasicMaterial { public: - using SharedPtr = std::shared_ptr; + static ref create(ref pDevice, const std::string& name) { return make_ref(pDevice, name); } - /** Create a new PBRTDiffuseTransmission material. - \param[in] name The material name. - */ - static SharedPtr create(std::shared_ptr pDevice, const std::string& name = ""); + PBRTDiffuseTransmissionMaterial(ref pDevice, const std::string& name); Program::ShaderModuleList getShaderModules() const override; Program::TypeConformanceList getTypeConformances() const override; - - protected: - PBRTDiffuseTransmissionMaterial(std::shared_ptr pDevice, const std::string& name); }; } diff --git a/Source/Falcor/Scene/Material/RGLMaterial.cpp b/Source/Falcor/Scene/Material/RGLMaterial.cpp index 9c61fe39d..b1948706a 100644 --- a/Source/Falcor/Scene/Material/RGLMaterial.cpp +++ b/Source/Falcor/Scene/Material/RGLMaterial.cpp @@ -32,7 +32,7 @@ #include "Utils/Logger.h" #include "Utils/Image/ImageIO.h" #include "Utils/Scripting/ScriptBindings.h" -#include "Scene/SceneBuilderAccess.h" +#include "GlobalState.h" #include "Rendering/Materials/BSDFIntegrator.h" #include @@ -50,13 +50,8 @@ namespace Falcor const std::string kLoadFile = "load"; } - RGLMaterial::SharedPtr RGLMaterial::create(std::shared_ptr pDevice, const std::string& name, const std::filesystem::path& path) - { - return SharedPtr(new RGLMaterial(std::move(pDevice), name, path)); - } - - RGLMaterial::RGLMaterial(std::shared_ptr pDevice, const std::string& name, const std::filesystem::path& path) - : Material(std::move(pDevice), name, MaterialType::RGL) + RGLMaterial::RGLMaterial(ref pDevice, const std::string& name, const std::filesystem::path& path) + : Material(pDevice, name, MaterialType::RGL) { if (!loadBRDF(path)) { @@ -68,7 +63,7 @@ namespace Falcor desc.setFilterMode(Sampler::Filter::Linear, Sampler::Filter::Point, Sampler::Filter::Point); desc.setAddressingMode(Sampler::AddressMode::Clamp, Sampler::AddressMode::Clamp, Sampler::AddressMode::Clamp); desc.setMaxAnisotropy(1); - mpSampler = Sampler::create(mpDevice.get(), desc); + mpSampler = Sampler::create(mpDevice, desc); prepareAlbedoLUT(mpDevice->getRenderContext()); } @@ -89,7 +84,7 @@ namespace Falcor auto flags = Material::UpdateFlags::None; if (mUpdates != Material::UpdateFlags::None) { - auto checkBuffer = [&](Buffer::SharedPtr& buf, uint& handle) + auto checkBuffer = [&](ref& buf, uint& handle) { // If a BRDF was already loaded and we're just updating data, // then buffer handles are already assigned. @@ -127,9 +122,9 @@ namespace Falcor return flags; } - bool RGLMaterial::isEqual(const Material::SharedPtr& pOther) const + bool RGLMaterial::isEqual(const ref& pOther) const { - auto other = std::dynamic_pointer_cast(pOther); + auto other = dynamic_ref_cast(pOther); if (!other) return false; if (!isBaseEqual(*other)) return false; @@ -217,18 +212,18 @@ namespace Falcor SamplableDistribution4D vndfDist(reinterpret_cast(vndf->data.get()), vndfSize); SamplableDistribution4D lumiDist(reinterpret_cast(lumi->data.get()), lumiSize); - mpVNDFMarginalBuf = Buffer::create(mpDevice.get(), prod3(vndfSize) * 4, ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, vndfDist.getMarginal()); - mpLumiMarginalBuf = Buffer::create(mpDevice.get(), prod3(lumiSize) * 4, ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, lumiDist.getMarginal()); - mpVNDFConditionalBuf = Buffer::create(mpDevice.get(), prod4(vndfSize) * 4, ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, vndfDist.getConditional()); - mpLumiConditionalBuf = Buffer::create(mpDevice.get(), prod4(lumiSize) * 4, ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, lumiDist.getConditional()); + mpVNDFMarginalBuf = Buffer::create(mpDevice, prod3(vndfSize) * 4, ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, vndfDist.getMarginal()); + mpLumiMarginalBuf = Buffer::create(mpDevice, prod3(lumiSize) * 4, ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, lumiDist.getMarginal()); + mpVNDFConditionalBuf = Buffer::create(mpDevice, prod4(vndfSize) * 4, ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, vndfDist.getConditional()); + mpLumiConditionalBuf = Buffer::create(mpDevice, prod4(lumiSize) * 4, ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, lumiDist.getConditional()); - mpThetaBuf = Buffer::create(mpDevice.get(), theta->numElems * sizeof(float), ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, theta->data.get()); - mpPhiBuf = Buffer::create(mpDevice.get(), phi ->numElems * sizeof(float), ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, phi ->data.get()); - mpSigmaBuf = Buffer::create(mpDevice.get(), sigma->numElems * sizeof(float), ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, sigma->data.get()); - mpNDFBuf = Buffer::create(mpDevice.get(), ndf ->numElems * sizeof(float), ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, ndf ->data.get()); - mpVNDFBuf = Buffer::create(mpDevice.get(), vndf ->numElems * sizeof(float), ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, vndfDist.getPDF()); - mpLumiBuf = Buffer::create(mpDevice.get(), lumi ->numElems * sizeof(float), ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, lumiDist.getPDF()); - mpRGBBuf = Buffer::create(mpDevice.get(), rgb ->numElems * sizeof(float), ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, rgb ->data.get()); + mpThetaBuf = Buffer::create(mpDevice, theta->numElems * sizeof(float), ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, theta->data.get()); + mpPhiBuf = Buffer::create(mpDevice, phi ->numElems * sizeof(float), ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, phi ->data.get()); + mpSigmaBuf = Buffer::create(mpDevice, sigma->numElems * sizeof(float), ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, sigma->data.get()); + mpNDFBuf = Buffer::create(mpDevice, ndf ->numElems * sizeof(float), ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, ndf ->data.get()); + mpVNDFBuf = Buffer::create(mpDevice, vndf ->numElems * sizeof(float), ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, vndfDist.getPDF()); + mpLumiBuf = Buffer::create(mpDevice, lumi ->numElems * sizeof(float), ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, lumiDist.getPDF()); + mpRGBBuf = Buffer::create(mpDevice, rgb ->numElems * sizeof(float), ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, rgb ->data.get()); markUpdates(Material::UpdateFlags::ResourcesChanged); @@ -246,7 +241,7 @@ namespace Falcor { // Load 1D texture in non-SRGB format, no mips. // If successful, verify dimensions/format/etc. match the expectations. - mpAlbedoLUT = Texture::createFromFile(mpDevice.get(), texPath.string(), false, false, ResourceBindFlags::ShaderResource); + mpAlbedoLUT = Texture::createFromFile(mpDevice, texPath.string(), false, false, ResourceBindFlags::ShaderResource); if (mpAlbedoLUT) { @@ -281,14 +276,14 @@ namespace Falcor for (uint32_t i = 0; i < kAlbedoLUTSize; i++) cosThetas[i] = (float)(i + 1) / kAlbedoLUTSize; // Create copy of material to avoid changing our local state. - auto pMaterial = SharedPtr(new RGLMaterial(*this)); + auto pMaterial = make_ref(*this); // Create and update scene containing the material. Scene::SceneData sceneData; - sceneData.pMaterials = MaterialSystem::create(mpDevice); + sceneData.pMaterials = std::make_unique(mpDevice); MaterialID materialID = sceneData.pMaterials->addMaterial(pMaterial); - Scene::SharedPtr pScene = Scene::create(mpDevice, std::move(sceneData)); + ref pScene = Scene::create(mpDevice, std::move(sceneData)); pScene->update(pRenderContext, 0.0); // Create BSDF integrator utility. @@ -306,7 +301,7 @@ namespace Falcor for (uint32_t i = 0; i < kAlbedoLUTSize; i++) initData[i] = float4(albedos[i], 1.f); // Create albedo LUT texture. - mpAlbedoLUT = Texture::create2D(mpDevice.get(), kAlbedoLUTSize, 1, kAlbedoLUTFormat, 1, 1, initData.data(), ResourceBindFlags::ShaderResource); + mpAlbedoLUT = Texture::create2D(mpDevice, kAlbedoLUTSize, 1, kAlbedoLUTFormat, 1, 1, initData.data(), ResourceBindFlags::ShaderResource); } FALCOR_SCRIPT_BINDING(RGLMaterial) @@ -315,10 +310,10 @@ namespace Falcor FALCOR_SCRIPT_BINDING_DEPENDENCY(Material) - pybind11::class_ material(m, "RGLMaterial"); + pybind11::class_> material(m, "RGLMaterial"); auto create = [] (const std::string& name, const std::filesystem::path& path) { - return RGLMaterial::create(getActivePythonSceneBuilder().getDevice(), name, path); + return RGLMaterial::create(accessActivePythonSceneBuilder().getDevice(), name, path); }; material.def(pybind11::init(create), "name"_a, "path"_a); // PYTHONDEPRECATED material.def(kLoadFile.c_str(), &RGLMaterial::loadBRDF, "path"_a); diff --git a/Source/Falcor/Scene/Material/RGLMaterial.h b/Source/Falcor/Scene/Material/RGLMaterial.h index 9057c894d..6fcbbdff2 100644 --- a/Source/Falcor/Scene/Material/RGLMaterial.h +++ b/Source/Falcor/Scene/Material/RGLMaterial.h @@ -42,18 +42,13 @@ namespace Falcor class FALCOR_API RGLMaterial : public Material { public: - using SharedPtr = std::shared_ptr; + static ref create(ref pDevice, const std::string& name, const std::filesystem::path& path) { return make_ref(pDevice, name, path); } - /** Create a new RGL material. - \param[in] name The material name. - \param[in] path Path of BRDF file to load. - \return A new object, or throws an exception if creation failed. - */ - static SharedPtr create(std::shared_ptr pDevice, const std::string& name, const std::filesystem::path& path); + RGLMaterial(ref pDevice, const std::string& name, const std::filesystem::path& path); bool renderUI(Gui::Widgets& widget) override; Material::UpdateFlags update(MaterialSystem* pOwner) override; - bool isEqual(const Material::SharedPtr& pOther) const override; + bool isEqual(const ref& pOther) const override; MaterialDataBlob getDataBlob() const override { return prepareDataBlob(mData); } Program::ShaderModuleList getShaderModules() const override; Program::TypeConformanceList getTypeConformances() const override; @@ -63,8 +58,6 @@ namespace Falcor bool loadBRDF(const std::filesystem::path& path); protected: - RGLMaterial(std::shared_ptr pDevice, const std::string& name, const std::filesystem::path& path); - void prepareData(const int dims[3], const std::vector& data); void prepareAlbedoLUT(RenderContext* pRenderContext); void computeAlbedoLUT(RenderContext* pRenderContext); @@ -75,20 +68,20 @@ namespace Falcor bool mBRDFUploaded = false; ///< True if BRDF data buffers have been uploaded to the material system. RGLMaterialData mData; ///< Material parameters. - Buffer::SharedPtr mpThetaBuf; - Buffer::SharedPtr mpPhiBuf; - Buffer::SharedPtr mpSigmaBuf; - Buffer::SharedPtr mpNDFBuf; - Buffer::SharedPtr mpVNDFBuf; - Buffer::SharedPtr mpLumiBuf; - Buffer::SharedPtr mpRGBBuf; - Buffer::SharedPtr mpVNDFMarginalBuf; - Buffer::SharedPtr mpLumiMarginalBuf; - Buffer::SharedPtr mpVNDFConditionalBuf; - Buffer::SharedPtr mpLumiConditionalBuf; - Texture::SharedPtr mpAlbedoLUT; ///< Precomputed albedo lookup table. - Sampler::SharedPtr mpSampler; ///< Sampler for accessing BRDF textures. + ref mpThetaBuf; + ref mpPhiBuf; + ref mpSigmaBuf; + ref mpNDFBuf; + ref mpVNDFBuf; + ref mpLumiBuf; + ref mpRGBBuf; + ref mpVNDFMarginalBuf; + ref mpLumiMarginalBuf; + ref mpVNDFConditionalBuf; + ref mpLumiConditionalBuf; + ref mpAlbedoLUT; ///< Precomputed albedo lookup table. + ref mpSampler; ///< Sampler for accessing BRDF textures. - ComputePass::SharedPtr mBRDFTesting; + ref mBRDFTesting; }; } diff --git a/Source/Falcor/Scene/Material/RGLMaterialData.slang b/Source/Falcor/Scene/Material/RGLMaterialData.slang index fee28ce10..d871e1761 100644 --- a/Source/Falcor/Scene/Material/RGLMaterialData.slang +++ b/Source/Falcor/Scene/Material/RGLMaterialData.slang @@ -62,9 +62,9 @@ struct RGLMaterialData TextureHandle texAlbedoLUT = {};///< Texture handle for albedo LUT. // Maximum resolution allowed for the various lookup tables. - static const uint kMaxResolution = 0xffff; + static constexpr uint kMaxResolution = 0xffff; - static const uint kAlbedoLUTSize = 256; + static constexpr uint kAlbedoLUTSize = 256; }; END_NAMESPACE_FALCOR diff --git a/Source/Falcor/Scene/Material/ShadingUtils.slang b/Source/Falcor/Scene/Material/ShadingUtils.slang index 98bf43fe4..a53963fc9 100644 --- a/Source/Falcor/Scene/Material/ShadingUtils.slang +++ b/Source/Falcor/Scene/Material/ShadingUtils.slang @@ -27,132 +27,13 @@ **************************************************************************/ import Rendering.Materials.IBSDF; import Scene.ShadingData; -import Scene.SceneTypes; import Scene.Material.MaterialTypes; -import Utils.Math.MathHelpers; import Utils.Color.ColorHelpers; +__exported import Utils.Math.ShadingFrame; // If set, the backfacing side in the hemisphere checks is forced to black. #define FALCOR_BACKFACE_BLACK 1 -/** Shading frame in world space. - The vectors TBN form an orthonormal basis. -*/ -struct ShadingFrame -{ - float3 T; ///< Shading tangent. Normalized. - float3 B; ///< Shading bitangent. Normalized. - float3 N; ///< Shading normal. Normalized. - - __init() {} - - __init(const ShadingData sd) - { - this.T = sd.T; - this.B = sd.B; - this.N = sd.N; - } - - /** Initialize shading frame from normal and tangent. - It is assumed the shading frame can be safely orthonormalized. If in doubt, use `createSafe` instead. - \param[in] Normal in world space. Not normalized. - \param[in] Target tangent in world space (xyz) and handedness sign (w). Not normalized. - */ - __init(const float3 normalW, const float4 tangentW) - { - this.N = normalize(normalW); - orthonormalize(tangentW); - } - - /** Create a shading frame from normal and tangent. - If the shading frame cannot be safely orthonormalized, a new tangent is invented based on the normal. - \param[in] Normal in world space. Not normalized. - \param[in] Target tangent in world space (xyz) and handedness sign (w). Not normalized. - \return New shading frame. - */ - static ShadingFrame createSafe(const float3 normalW, const float4 tangentW) - { - ShadingFrame sf; - sf.N = normalize(normalW); - sf.orthonormalizeSafe(tangentW); - return sf; - } - - /** Transform vector from the local shading frame to world space. - \param[in] v Vector in local space. - \return Vector in world space. - */ - float3 fromLocal(const float3 v) - { - return T * v.x + B * v.y + N * v.z; - } - - /** Transform vector from world space to the local shading frame. - \param[in] v Vector in world space. - \return Vector in local space. - */ - float3 toLocal(const float3 v) - { - return float3(dot(v, T), dot(v, B), dot(v, N)); - } - - /** Returns sign that gets applied to such that B = cross(N, T) * getHandednessSign(); - \return +1 for right handed, and -1 for left handed frames. - */ - float getHandednessSign() - { - return dot(cross(N, T), B) >= 0.f ? 1.f : -1.f; - } - - /** Orthonormalize the shading frame. - Vectors T and B are adjusted to make an orthonormal frame where T lies in the same plane as N and the supplied target tangent. - It is assumed that the tangent is nonzero and not parallel to the normal, and that the sign (w) is +-1.0. - \param[in] tangentW Target tangent in world space (xyz) and handedness sign (w). Not normalized. - */ - [mutating] - void orthonormalize(const float4 tangentW) - { - T = normalize(tangentW.xyz - N * dot(tangentW.xyz, N)); - B = cross(N, T) * tangentW.w; - } - - /** Orthonormalize the shading frame. - Vectors T and B are adjusted to make an orthonormal frame where T lies in the same plane as N and the supplied target tangent. - If the shading frame cannot be safely orthonormalized, a new tangent is invented based on the normal. - \param[in] tangentW Target tangent in world space (xyz) and handedness sign (w). Not normalized. - \return True if a valid tangent space was computed based on the supplied tangent, or false if a tangent space was invented. - */ - [mutating] - bool orthonormalizeSafe(const float4 tangentW) - { - // Check that tangent space exists and can be safely orthonormalized. - // Otherwise invent a tanget frame based on the normal. - // We check that: - // - Tangent exists, this is indicated by a sign (w) that is +-1.0. - // - It has nonzero length. Zeros can occur due to interpolation or bad assets. - // - It is not parallel to the normal. This can occur due to normal mapping or bad assets. - // - It does not have NaNs. These will propagate and trigger the fallback. - - float NdotT = dot(tangentW.xyz, N); - bool nonParallel = abs(NdotT) < 0.9999f; - bool nonZero = dot(tangentW.xyz, tangentW.xyz) > 0.f; - bool valid = abs(tangentW.w) == 1.0f && nonZero && nonParallel; - - if (valid) - { - T = normalize(tangentW.xyz - N * NdotT); - B = cross(N, T) * tangentW.w; - } - else - { - T = perp_stark(N); - B = cross(N, T); - } - - return valid; - } -}; - /** Convert RGB to normal (unnormalized). */ float3 rgbToNormal(float3 rgb) @@ -190,20 +71,6 @@ float getMetallic(float3 diffuse, float3 spec) return m; } -/** Apply normal map. - This function perturbs the shading normal using a local normal sampled from a normal map. - \param[in,out] sd ShadingData struct that is updated. - \param[in] type Normal map type. - \param[in] encodedNormal Encoded normal loaded from normal map. -*/ -void applyNormalMap(inout ShadingData sd, const NormalMapType type, const float3 encodedNormal) -{ - ShadingFrame sf = computeShadingFrameFromNormalMap(sd, type, encodedNormal); - sd.T = sf.T; - sd.B = sf.B; - sd.N = sf.N; -} - /** Compute shading frame at shading point by applying normal mapping. This function perturbs the shading normal using a local normal sampled from a normal map. \param[in] sd Shading data. @@ -224,7 +91,7 @@ ShadingFrame computeShadingFrameFromNormalMap(const ShadingData sd, const Normal mapN = rgToNormal(encodedNormal.rg); break; case NormalMapType::None: - return ShadingFrame(sd); + return sd.frame; } // Note if the normal ends up being parallel to the tangent, the tangent frame cannot be orthonormalized. @@ -232,34 +99,19 @@ ShadingFrame computeShadingFrameFromNormalMap(const ShadingData sd, const Normal // If it occurs we should foremost fix the asset, or if problems persist add a check here. // Apply the transformation. - return ShadingFrame(sd.T * mapN.x + sd.B * mapN.y + sd.N * mapN.z, sd.tangentW); -} - -/** Computes an orthonormal tangent space based on the normal and given tangent. - \param[in,out] sd ShadingData struct that is updated. - \param[in] tangent Interpolated tangent in world space (xyz) and bitangent sign (w). The tangent is *only* valid when w is +-1.0. - \return True if a valid tangent space was computed based on the supplied tangent, or false if a tangent space was invented. -*/ -bool computeTangentSpace(inout ShadingData sd, const float4 tangentW) -{ - ShadingFrame sf = ShadingFrame(sd); - bool valid = sf.orthonormalizeSafe(tangentW); - sd.T = sf.T; - sd.B = sf.B; - sd.N = sf.N; - return valid; + return ShadingFrame(sd.frame.fromLocal(mapN), sd.tangentW); } -/** Helper function to adjust the shading normal to reduce black pixels due to back-facing view direction. - Note: This breaks the reciprocity of the BSDF! +/** Flip shading normal for backfacing hit on double-sided material. + \param[in] sd Shading data. + \param[in,out] sf Shading frame that will be modified. */ -void adjustShadingNormal(inout ShadingData sd) +void flipShadingNormal(const ShadingData sd, inout ShadingFrame sf) { - ShadingFrame sf = ShadingFrame(sd); - adjustShadingNormal(sd, sf); - sd.T = sf.T; - sd.B = sf.B; - sd.N = sf.N; + if (!sd.frontFacing && sd.mtl.isDoubleSided()) + { + sf.N = -sf.N; + } } /** Adjusts the normal of the supplied shading frame to reduce black pixels due to back-facing view direction. @@ -269,8 +121,13 @@ void adjustShadingNormal(inout ShadingData sd) */ void adjustShadingNormal(const ShadingData sd, inout ShadingFrame sf) { - float3 Ng = sd.frontFacing ? sd.faceN : -sd.faceN; - float3 Ns = sf.N; + // Note: sd.V and Ng below always lie on the same side (as the front-facing flag is computed based on sd.V). + // The shading normal sf.N may lie on either side depending on whether we're shading the front or back. + // We orient Ns to lie on the same side as sd.V and Ng for the computations below. + // The final adjusted normal is oriented to lie on the same side as the original shading normal. + float3 Ng = sd.getOrientedFaceNormal(); + float signN = dot(sf.N, Ng) >= 0.f ? 1.f : -1.f; + float3 Ns = signN * sf.N; // Blend the shading normal towards the geometric normal at grazing angles. // This is to avoid the view vector from becoming back-facing. @@ -279,7 +136,7 @@ void adjustShadingNormal(const ShadingData sd, inout ShadingFrame sf) if (cosTheta <= kCosThetaThreshold) { float t = saturate(cosTheta * (1.f / kCosThetaThreshold)); - sf = ShadingFrame(lerp(Ng, Ns, t), sd.tangentW); + sf = ShadingFrame(signN * lerp(Ng, Ns, t), sd.tangentW); } } @@ -349,14 +206,16 @@ bool isValidHemisphereTransmission(const ShadingData sd, const ShadingFrame sf, \param[in] wiLocal Incident direction in local space. \param[in] woLocal Outgoing direction in local space. \param[in] wo Outgoing direction in world space. + \param[in] fullSphere If true allow the full sphere of directions, otherwise wi in the lower hemisphere is rejected and back-faces are black. \return True if wi/wo represent valid vectors for reflection or transmission. */ -bool isValidHemisphereReflectionOrTransmission(const ShadingData sd, const ShadingFrame sf, const float3 wiLocal, const float3 woLocal, const float3 wo) +bool isValidHemisphereReflectionOrTransmission(const ShadingData sd, const ShadingFrame sf, const float3 wiLocal, const float3 woLocal, const float3 wo, const bool fullSphere = false) { - const bool isTransmission = woLocal.z < 0.f; + const bool isTransmission = wiLocal.z * woLocal.z < 0.f; // Check that wi/wo are in the upper/lower hemisphere around the shading normal. - if (min(wiLocal.z, abs(woLocal.z)) < kMinCosTheta) return false; + float cosThetaI = fullSphere ? abs(wiLocal.z) : wiLocal.z; + if (min(cosThetaI, abs(woLocal.z)) < kMinCosTheta) return false; // Check that wi/wo are on the same geometric side. bool wiTop = sd.frontFacing; // The flag is computed dot(wi, faceN) >= 0.f. @@ -372,9 +231,12 @@ bool isValidHemisphereReflectionOrTransmission(const ShadingData sd, const Shadi } #if FALCOR_BACKFACE_BLACK - // Additionally check that we're on the same geometric side as the shading normal. - bool shadingTop = dot(sf.N, sd.faceN) >= 0.f; - if (wiTop != shadingTop) return false; + if (!fullSphere) + { + // Additionally check that we're on the same geometric side as the shading normal. + bool shadingTop = dot(sf.N, sd.faceN) >= 0.f; + if (wiTop != shadingTop) return false; + } #endif return true; diff --git a/Source/Falcor/Scene/Material/StandardMaterial.cpp b/Source/Falcor/Scene/Material/StandardMaterial.cpp index 0f1c29960..6db745021 100644 --- a/Source/Falcor/Scene/Material/StandardMaterial.cpp +++ b/Source/Falcor/Scene/Material/StandardMaterial.cpp @@ -28,7 +28,7 @@ #include "StandardMaterial.h" #include "Utils/Logger.h" #include "Utils/Scripting/ScriptBindings.h" -#include "Scene/SceneBuilderAccess.h" +#include "GlobalState.h" namespace Falcor { @@ -37,13 +37,8 @@ namespace Falcor const char kShaderFile[] = "Rendering/Materials/StandardMaterial.slang"; } - StandardMaterial::SharedPtr StandardMaterial::create(std::shared_ptr pDevice, const std::string& name, ShadingModel shadingModel) - { - return SharedPtr(new StandardMaterial(std::move(pDevice), name, shadingModel)); - } - - StandardMaterial::StandardMaterial(std::shared_ptr pDevice, const std::string& name, ShadingModel shadingModel) - : BasicMaterial(std::move(pDevice), name, MaterialType::Standard) + StandardMaterial::StandardMaterial(ref pDevice, const std::string& name, ShadingModel shadingModel) + : BasicMaterial(pDevice, name, MaterialType::Standard) { setShadingModel(shadingModel); bool specGloss = getShadingModel() == ShadingModel::SpecGloss; @@ -72,7 +67,7 @@ namespace Falcor { widget.text("Emissive color: " + pTexture->getSourcePath().string()); widget.text("Texture info: " + std::to_string(pTexture->getWidth()) + "x" + std::to_string(pTexture->getHeight()) + " (" + to_string(pTexture->getFormat()) + ")"); - widget.image("Emissive color", pTexture, float2(100.f)); + widget.image("Emissive color", pTexture.get(), float2(100.f)); if (widget.button("Remove texture##Emissive")) setEmissiveTexture(nullptr); } else @@ -94,7 +89,7 @@ namespace Falcor void StandardMaterial::updateDeltaSpecularFlag() { // Check if material has no diffuse lobe. - bool isNonDiffuse = !hasTextureSlotData(TextureSlot::BaseColor) && float3(getBaseColor()) == float3(0.f) && getDiffuseTransmission() == 0.f; + bool isNonDiffuse = !hasTextureSlotData(TextureSlot::BaseColor) && all(getBaseColor().xyz() == float3(0.f)) && getDiffuseTransmission() == 0.f; // Check if material is fully specular transmissive. bool isFullyTransmissive = getSpecularTransmission() >= 1.f; @@ -184,7 +179,7 @@ namespace Falcor void StandardMaterial::setEmissiveColor(const float3& color) { - if (mData.emissive != color) + if (any(mData.emissive != color)) { mData.emissive = color; markUpdates(UpdateFlags::DataChanged); @@ -212,10 +207,10 @@ namespace Falcor shadingModel.value("MetalRough", ShadingModel::MetalRough); shadingModel.value("SpecGloss", ShadingModel::SpecGloss); - pybind11::class_ material(m, "StandardMaterial"); + pybind11::class_> material(m, "StandardMaterial"); auto create = [] (const std::string& name, ShadingModel shadingModel) { - return StandardMaterial::create(getActivePythonSceneBuilder().getDevice(), name, shadingModel); + return StandardMaterial::create(accessActivePythonSceneBuilder().getDevice(), name, shadingModel); }; material.def(pybind11::init(create), "name"_a = "", "model"_a = ShadingModel::MetalRough); // PYTHONDEPRECATED diff --git a/Source/Falcor/Scene/Material/StandardMaterial.h b/Source/Falcor/Scene/Material/StandardMaterial.h index b93c5c158..0821cace6 100644 --- a/Source/Falcor/Scene/Material/StandardMaterial.h +++ b/Source/Falcor/Scene/Material/StandardMaterial.h @@ -68,13 +68,12 @@ namespace Falcor class FALCOR_API StandardMaterial : public BasicMaterial { public: - using SharedPtr = std::shared_ptr; + static ref create(ref pDevice, const std::string& name = "", ShadingModel shadingModel = ShadingModel::MetalRough) + { + return make_ref(pDevice, name, shadingModel); + } - /** Create a new standard material. - \param[in] name The material name. - \param[in] model Shading model. - */ - static SharedPtr create(std::shared_ptr pDevice, const std::string& name = "", ShadingModel shadingModel = ShadingModel::MetalRough); + StandardMaterial(ref pDevice, const std::string& name, ShadingModel shadingModel); /** Render the UI. \return True if the material was modified. @@ -131,8 +130,6 @@ namespace Falcor } protected: - StandardMaterial(std::shared_ptr pDevice, const std::string& name, ShadingModel shadingModel); - void updateDeltaSpecularFlag() override; void renderSpecularUI(Gui::Widgets& widget) override; diff --git a/Source/Falcor/Scene/Material/TextureHandle.slang b/Source/Falcor/Scene/Material/TextureHandle.slang index 9022c5824..110a4554f 100644 --- a/Source/Falcor/Scene/Material/TextureHandle.slang +++ b/Source/Falcor/Scene/Material/TextureHandle.slang @@ -51,12 +51,12 @@ struct TextureHandle Count // Must be last }; - static const uint kTextureIDBits = 29; - static const uint kModeBits = 2; - static const uint kUdimEnabledBits = 1; + static constexpr uint kTextureIDBits = 29; + static constexpr uint kModeBits = 2; + static constexpr uint kUdimEnabledBits = 1; - static const uint kModeOffset = kTextureIDBits; - static const uint kUdimEnabledOffset = kModeOffset + kModeBits; + static constexpr uint kModeOffset = kTextureIDBits; + static constexpr uint kUdimEnabledOffset = kModeOffset + kModeBits; /** Set mode. */ diff --git a/Source/Falcor/Scene/SDFs/NormalizedDenseSDFGrid/NDSDFGrid.cpp b/Source/Falcor/Scene/SDFs/NormalizedDenseSDFGrid/NDSDFGrid.cpp index 3b7c4e415..7b3f27b59 100644 --- a/Source/Falcor/Scene/SDFs/NormalizedDenseSDFGrid/NDSDFGrid.cpp +++ b/Source/Falcor/Scene/SDFs/NormalizedDenseSDFGrid/NDSDFGrid.cpp @@ -27,36 +27,42 @@ **************************************************************************/ #include "NDSDFGrid.h" #include "Core/API/RenderContext.h" +#include "Utils/SharedCache.h" +#include "Utils/Math/MathConstants.slangh" namespace Falcor { - Sampler::SharedPtr NDSDFGrid::spNDSDFGridSampler; // TODO: REMOVEGLOBAL - Buffer::SharedPtr NDSDFGrid::spNDSDFGridUnitAABBBuffer; // TODO: REMOVEGLOBAL - - NDSDFGrid::SharedPtr NDSDFGrid::create(std::shared_ptr pDevice, float normalizationFactor) + struct NDSDFGrid::SharedData { - if (!spNDSDFGridSampler) + ref pSampler; + ref pUnitAABBBuffer; + + SharedData(ref pDevice) { Sampler::Desc sdfGridSamplerDesc; sdfGridSamplerDesc.setFilterMode(Sampler::Filter::Linear, Sampler::Filter::Linear, Sampler::Filter::Linear); sdfGridSamplerDesc.setAddressingMode(Sampler::AddressMode::Clamp, Sampler::AddressMode::Clamp, Sampler::AddressMode::Clamp); - spNDSDFGridSampler = Sampler::create(pDevice.get(), sdfGridSamplerDesc); - } + pSampler = Sampler::create(pDevice, sdfGridSamplerDesc); - if (!spNDSDFGridUnitAABBBuffer) - { RtAABB unitAABB { float3(-0.5f), float3(0.5f) }; - spNDSDFGridUnitAABBBuffer = Buffer::create(pDevice.get(), sizeof(RtAABB), Resource::BindFlags::ShaderResource, Buffer::CpuAccess::None, &unitAABB); + pUnitAABBBuffer = Buffer::create(pDevice, sizeof(RtAABB), Resource::BindFlags::ShaderResource, Buffer::CpuAccess::None, &unitAABB); } + }; - return SharedPtr(new NDSDFGrid(std::move(pDevice), normalizationFactor)); + static SharedCache sSharedCache; + + NDSDFGrid::NDSDFGrid(ref pDevice, float narrowBandThickness) + : SDFGrid(pDevice) + , mNarrowBandThickness(std::max(narrowBandThickness, 1.0f)) + { + mpSharedData = sSharedCache.acquire(mpDevice.get(), [this]() { return std::make_shared(mpDevice); }); } size_t NDSDFGrid::getSize() const { - size_t totalSize = spNDSDFGridUnitAABBBuffer->getSize(); + size_t totalSize = mpSharedData->pUnitAABBBuffer->getSize(); - for (const Texture::SharedPtr& pNormalizedVolumeTexture : mNDSDFTextures) + for (const ref& pNormalizedVolumeTexture : mNDSDFTextures) { totalSize += pNormalizedVolumeTexture->getTextureSizeInBytes(); } @@ -88,18 +94,23 @@ namespace Falcor { uint32_t lodWidth = 1 + (mCoarsestLODGridWidth << lod); - Texture::SharedPtr& pNDSDFTexture = mNDSDFTextures[lod]; + ref& pNDSDFTexture = mNDSDFTextures[lod]; if (pNDSDFTexture && pNDSDFTexture->getWidth() == lodWidth) { pRenderContext->updateTextureData(pNDSDFTexture.get(), mValues[lod].data()); } else { - pNDSDFTexture = Texture::create3D(mpDevice.get(), lodWidth, lodWidth, lodWidth, ResourceFormat::R8Snorm, 1, mValues[lod].data()); + pNDSDFTexture = Texture::create3D(mpDevice, lodWidth, lodWidth, lodWidth, ResourceFormat::R8Snorm, 1, mValues[lod].data()); } } } + const ref& NDSDFGrid::getAABBBuffer() const + { + return mpSharedData->pUnitAABBBuffer; + } + void NDSDFGrid::setShaderData(const ShaderVar& var) const { if (mNDSDFTextures.empty()) @@ -107,7 +118,7 @@ namespace Falcor throw RuntimeError("NDSDFGrid::setShaderData() can't be called before calling NDSDFGrid::createResources()!"); } - var["sampler"] = spNDSDFGridSampler; + var["sampler"] = mpSharedData->pSampler; var["lodCount"] = uint32_t(mNDSDFTextures.size()); var["coarsestLODAsLevel"] = bitScanReverse(mCoarsestLODGridWidth); var["coarsestLODGridWidth"] = mCoarsestLODGridWidth; @@ -157,7 +168,7 @@ namespace Falcor uint32_t writeLocation = x + lodWidthInValues * (y + lodWidthInValues * z); uint32_t readLocation = lodReadStride * (x + gridWidthInValues * (y + gridWidthInValues * z)); - float normalizedValue = glm::clamp(cornerValues[readLocation] / normalizationFactor, -1.0f, 1.0f); + float normalizedValue = std::clamp(cornerValues[readLocation] / normalizationFactor, -1.0f, 1.0f); float integerScale = normalizedValue * float(INT8_MAX); lodFormattedValues[writeLocation] = integerScale >= 0.0f ? int8_t(integerScale + 0.5f) : int8_t(integerScale - 0.5f); @@ -169,12 +180,6 @@ namespace Falcor float NDSDFGrid::calculateNormalizationFactor(uint32_t gridWidth) const { - return 0.5f * glm::root_three() * mNarrowBandThickness / gridWidth; - } - - NDSDFGrid::NDSDFGrid(std::shared_ptr pDevice, float narrowBandThickness) - : SDFGrid(std::move(pDevice)) - , mNarrowBandThickness(std::max(narrowBandThickness, 1.0f)) - { + return 0.5f * float(M_SQRT3) * mNarrowBandThickness / gridWidth; } } diff --git a/Source/Falcor/Scene/SDFs/NormalizedDenseSDFGrid/NDSDFGrid.h b/Source/Falcor/Scene/SDFs/NormalizedDenseSDFGrid/NDSDFGrid.h index d9f1c7d82..3dedfc0b7 100644 --- a/Source/Falcor/Scene/SDFs/NormalizedDenseSDFGrid/NDSDFGrid.h +++ b/Source/Falcor/Scene/SDFs/NormalizedDenseSDFGrid/NDSDFGrid.h @@ -37,13 +37,15 @@ namespace Falcor class FALCOR_API NDSDFGrid : public SDFGrid { public: - using SharedPtr = std::shared_ptr; + struct SharedData; /** Create a new, empty normalized dense SDF grid. \param[in] narrowBandThickness NDSDFGrids operate on normalized distances, the distances are normalized so that a normalized distance of +- 1 represents a distance of "narrowBandThickness" voxel diameters. Should not be less than 1. \return NDSDFGrid object, or nullptr if errors occurred. */ - static SharedPtr create(std::shared_ptr pDevice, float narrowBandThickness); + static ref create(ref pDevice, float narrowBandThickness) { return make_ref(pDevice, narrowBandThickness); } + + NDSDFGrid(ref pDevice, float narrowBandThickness); virtual size_t getSize() const override; virtual uint32_t getMaxPrimitiveIDBits() const override; @@ -51,7 +53,7 @@ namespace Falcor virtual void createResources(RenderContext* pRenderContext, bool deleteScratchData = true) override; - virtual const Buffer::SharedPtr& getAABBBuffer() const override { return spNDSDFGridUnitAABBBuffer; } + virtual const ref& getAABBBuffer() const override; virtual uint32_t getAABBCount() const override { return 1; } virtual void setShaderData(const ShaderVar& var) const override; @@ -61,8 +63,6 @@ namespace Falcor float calculateNormalizationFactor(uint32_t gridWidth) const; private: - NDSDFGrid(std::shared_ptr pDevice, float narrowBandThickness); - // CPU data. std::vector> mValues; @@ -71,11 +71,8 @@ namespace Falcor float mCoarsestLODNormalizationFactor = 0.0f; float mNarrowBandThickness = 0.0f; - // Resources shared among all NDSDFGrids. - static Sampler::SharedPtr spNDSDFGridSampler; // TODO: REMOVEGLOBAL - static Buffer::SharedPtr spNDSDFGridUnitAABBBuffer; // TODO: REMOVEGLOBAL - // GPU data. - std::vector mNDSDFTextures; + std::vector> mNDSDFTextures; + std::shared_ptr mpSharedData; ///< Shared data among all instances. }; } diff --git a/Source/Falcor/Scene/SDFs/SDF3DPrimitiveFactory.cpp b/Source/Falcor/Scene/SDFs/SDF3DPrimitiveFactory.cpp index 49f8dbd4c..7ba558d3f 100644 --- a/Source/Falcor/Scene/SDFs/SDF3DPrimitiveFactory.cpp +++ b/Source/Falcor/Scene/SDFs/SDF3DPrimitiveFactory.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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,7 +38,7 @@ SDF3DPrimitive SDF3DPrimitiveFactory::initCommon(SDF3DShapeType shapeType, const primitive.shapeBlobbing = blobbing; primitive.operationSmoothing = operationSmoothing; primitive.translation = transform.getTranslation(); - primitive.invRotationScale = rmcv::inverse(rmcv::mat3(transform.getMatrix())); + primitive.invRotationScale = inverse(float3x3(transform.getMatrix())); return primitive; } @@ -65,7 +65,7 @@ AABB SDF3DPrimitiveFactory::computeAABB(const SDF3DPrimitive& primitive) case SDF3DShapeType::Ellipsoid: case SDF3DShapeType::Box: { - float3 halfExtents = primitive.shapeData.xyz + float3(rounding); + float3 halfExtents = primitive.shapeData + float3(rounding); aabb.include(float3(halfExtents.x, halfExtents.y, halfExtents.z)); aabb.include(float3(-halfExtents.x, halfExtents.y, halfExtents.z)); aabb.include(float3(halfExtents.x, -halfExtents.y, halfExtents.z)); @@ -123,7 +123,7 @@ AABB SDF3DPrimitiveFactory::computeAABB(const SDF3DPrimitive& primitive) throw RuntimeError("SDF Primitive has unknown primitive type"); } - rmcv::mat4 translate = rmcv::translate(rmcv::identity(), primitive.translation); - rmcv::mat4 rotScale = rmcv::inverse(rmcv::transpose(primitive.invRotationScale)); - return aabb.transform(translate * rotScale); + float4x4 translate = math::matrixFromTranslation(primitive.translation); + float4x4 rotScale = inverse(transpose(primitive.invRotationScale)); + return aabb.transform(mul(translate, rotScale)); } diff --git a/Source/Falcor/Scene/SDFs/SDFGrid.cpp b/Source/Falcor/Scene/SDFs/SDFGrid.cpp index 1cdf8cfbb..be31914ac 100644 --- a/Source/Falcor/Scene/SDFs/SDFGrid.cpp +++ b/Source/Falcor/Scene/SDFs/SDFGrid.cpp @@ -36,33 +36,30 @@ #include "Core/API/RenderContext.h" #include "Utils/Logger.h" #include "Utils/Math/Common.h" -#include "Utils/Math/Matrix/Matrix.h" +#include "Utils/Math/Matrix.h" #include "Utils/Scripting/ScriptBindings.h" -#include "Scene/SceneBuilderAccess.h" +#include "GlobalState.h" #include #include #include using json = nlohmann::json; -namespace glm +namespace Falcor::math { - void to_json(json& j, const glm::vec3& v) + void to_json(json& j, const float3& v) { j = { v.x, v.y, v.z }; } - void from_json(const json& j, glm::vec3& v) + void from_json(const json& j, float3& v) { j[0].get_to(v.x); j[1].get_to(v.y); j[2].get_to(v.z); } -} -namespace Falcor::rmcv -{ - void to_json(json& j, const Falcor::rmcv::mat3& m) + void to_json(json& j, const float3x3& m) { for (uint32_t i = 0; i < 9; ++i) { @@ -70,7 +67,7 @@ namespace Falcor::rmcv } } - void from_json(const json& j, Falcor::rmcv::mat3& m) + void from_json(const json& j, float3x3& m) { for (uint32_t i = 0; i < 9; ++i) { @@ -152,7 +149,7 @@ namespace Falcor j[kPrimitiveInvRotationScaleJSONKey].get_to(primitive.invRotationScale); } - SDFGrid::SDFGrid(std::shared_ptr pDevice) : mpDevice(std::move(pDevice)) {} + SDFGrid::SDFGrid(ref pDevice) : mpDevice(pDevice) {} uint32_t SDFGrid::setPrimitives(const std::vector& primitives, uint32_t gridWidth) { @@ -355,8 +352,8 @@ namespace Falcor // Create a Box. { float3 d = abs(pLocal) - float3(kHalfCheeseExtent); - float outsideDist = glm::length(float3(glm::max(d.x, 0.0f), glm::max(d.y, 0.0f), glm::max(d.z, 0.0f))); - float insideDist = glm::min(glm::max(glm::max(d.x, d.y), d.z), 0.0f); + float outsideDist = length(float3(std::max(d.x, 0.0f), std::max(d.y, 0.0f), std::max(d.z, 0.0f))); + float insideDist = std::min(std::max(std::max(d.x, d.y), d.z), 0.0f); sd = outsideDist + insideDist; } @@ -364,11 +361,11 @@ namespace Falcor for (uint32_t s = 0; s < kHoleCount; s++) { float4 holeData = holes[s]; - sd = glm::max(sd, -(glm::length(pLocal - holeData.xyz) - holeData.w)); + sd = std::max(sd, -(length(pLocal - holeData.xyz()) - holeData.w)); } // We don't care about distance further away than the length of the diagonal of the unit cube where the SDF grid is defined. - cornerValues[x + gridWidthInValues * (y + gridWidthInValues * z)] = glm::clamp(sd, -glm::root_three(), glm::root_three()); + cornerValues[x + gridWidthInValues * (y + gridWidthInValues * z)] = std::clamp(sd, -float(M_SQRT3), float(M_SQRT3)); } } } @@ -386,15 +383,16 @@ namespace Falcor uint32_t gridWidthInValues = mGridWidth + 1; uint32_t valueCount = gridWidthInValues * gridWidthInValues * gridWidthInValues; - Buffer::SharedPtr pValuesBuffer = Buffer::createTyped(mpDevice.get(), valueCount); - Buffer::SharedPtr pValuesStagingBuffer = Buffer::createTyped(mpDevice.get(), valueCount, Resource::BindFlags::None, Buffer::CpuAccess::Read); - GpuFence::SharedPtr pFence = GpuFence::create(mpDevice.get()); - - mpEvaluatePrimitivesPass["CB"]["gGridWidth"] = mGridWidth; - mpEvaluatePrimitivesPass["CB"]["gPrimitiveCount"] = (uint32_t)mPrimitives.size() - mBakedPrimitiveCount; - mpEvaluatePrimitivesPass["gPrimitives"] = mpPrimitivesBuffer; - mpEvaluatePrimitivesPass["gOldValues"] = mHasGridRepresentation ? mpSDFGridTexture : nullptr; - mpEvaluatePrimitivesPass["gValues"] = pValuesBuffer; + ref pValuesBuffer = Buffer::createTyped(mpDevice, valueCount); + ref pValuesStagingBuffer = Buffer::createTyped(mpDevice, valueCount, Resource::BindFlags::None, Buffer::CpuAccess::Read); + ref pFence = GpuFence::create(mpDevice); + + auto var = mpEvaluatePrimitivesPass->getRootVar(); + var["CB"]["gGridWidth"] = mGridWidth; + var["CB"]["gPrimitiveCount"] = (uint32_t)mPrimitives.size() - mBakedPrimitiveCount; + var["gPrimitives"] = mpPrimitivesBuffer; + var["gOldValues"] = mHasGridRepresentation ? mpSDFGridTexture : nullptr; + var["gValues"] = pValuesBuffer; mpEvaluatePrimitivesPass->execute(pRenderContext, uint3(gridWidthInValues)); pRenderContext->copyResource(pValuesStagingBuffer.get(), pValuesBuffer.get()); pRenderContext->flush(false); @@ -532,14 +530,14 @@ namespace Falcor compressed = pybind11::cast(value); } } - return SDFGrid::SharedPtr(SDFSBS::create(getActivePythonSceneBuilder().getDevice(), brickWidth, compressed, defaultGridWidth)); + return static_ref_cast(SDFSBS::create(accessActivePythonSceneBuilder().getDevice(), brickWidth, compressed, defaultGridWidth)); }; - pybind11::class_ sdfGrid(m, "SDFGrid"); - sdfGrid.def_static("createNDGrid", [](float narrowBandThickness) { return SDFGrid::SharedPtr(NDSDFGrid::create(getActivePythonSceneBuilder().getDevice(), narrowBandThickness)); }, "narrowBandThickness"_a); // PYTHONDEPRECATED - sdfGrid.def_static("createSVS", [](){ return SDFGrid::SharedPtr(SDFSVS::create(getActivePythonSceneBuilder().getDevice())); }); // PYTHONDEPRECATED + pybind11::class_> sdfGrid(m, "SDFGrid"); + sdfGrid.def_static("createNDGrid", [](float narrowBandThickness) { return static_ref_cast(NDSDFGrid::create(accessActivePythonSceneBuilder().getDevice(), narrowBandThickness)); }, "narrowBandThickness"_a); // PYTHONDEPRECATED + sdfGrid.def_static("createSVS", [](){ return static_ref_cast(SDFSVS::create(accessActivePythonSceneBuilder().getDevice())); }); // PYTHONDEPRECATED sdfGrid.def_static("createSBS", createSBS); // PYTHONDEPRECATED - sdfGrid.def_static("createSVO", [](){ return SDFGrid::SharedPtr(SDFSVO::create(getActivePythonSceneBuilder().getDevice())); }); // PYTHONDEPRECATED + sdfGrid.def_static("createSVO", [](){ return static_ref_cast(SDFSVO::create(accessActivePythonSceneBuilder().getDevice())); }); // PYTHONDEPRECATED sdfGrid.def("loadValuesFromFile", &SDFGrid::loadValuesFromFile, "path"_a); sdfGrid.def("loadPrimitivesFromFile", &SDFGrid::loadPrimitivesFromFile, "path"_a, "gridWidth"_a, "dir"_a = ""); sdfGrid.def("generateCheeseValues", &SDFGrid::generateCheeseValues, "gridWidth"_a, "seed"_a); @@ -582,7 +580,7 @@ namespace Falcor void* pData = (void*)&mPrimitives[mPrimitivesExcludedFromBuffer]; if (!mpPrimitivesBuffer || mpPrimitivesBuffer->getElementCount() < count) { - mpPrimitivesBuffer = Buffer::createStructured(mpDevice.get(), sizeof(SDF3DPrimitive), count, ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, pData, false); + mpPrimitivesBuffer = Buffer::createStructured(mpDevice, sizeof(SDF3DPrimitive), count, ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, pData, false); } else { diff --git a/Source/Falcor/Scene/SDFs/SDFGrid.h b/Source/Falcor/Scene/SDFs/SDFGrid.h index e651a4866..bc45c141e 100644 --- a/Source/Falcor/Scene/SDFs/SDFGrid.h +++ b/Source/Falcor/Scene/SDFs/SDFGrid.h @@ -27,10 +27,11 @@ **************************************************************************/ #pragma once #include "Core/Macros.h" +#include "Core/Object.h" #include "Core/API/Buffer.h" #include "Core/API/Texture.h" +#include "Core/Pass/ComputePass.h" #include "Scene/SDFs/SDF3DPrimitiveCommon.slang" -#include "RenderGraph/BasePasses/ComputePass.h" #include #include #include @@ -66,11 +67,9 @@ namespace Falcor out the AABB buffer constructed by the SDFSVO. This can then be used to intersect rays against the SDFSVO. Distances stored in the voxels of the octree are normalized to the range [-1, 1] so that a value of 1 represents half of a voxel diagonal. */ - class FALCOR_API SDFGrid + class FALCOR_API SDFGrid : public Object { public: - using SharedPtr = std::shared_ptr; - enum class Type { None = 0, @@ -91,7 +90,7 @@ namespace Falcor All = AABBsChanged | BuffersReallocated, }; - SDFGrid(std::shared_ptr pDevice); + SDFGrid(ref pDevice); virtual ~SDFGrid() = default; /** Set SDF primitives to be used to construct the SDF grid. @@ -200,7 +199,7 @@ namespace Falcor /** Returns an AABB buffer that can be used to create an accelerations strucure using this SDF grid. */ - virtual const Buffer::SharedPtr& getAABBBuffer() const = 0; + virtual const ref& getAABBBuffer() const = 0; /** Return the number of AABBs used to create this SDF grid. */ @@ -241,7 +240,7 @@ namespace Falcor void updatePrimitivesBuffer(); - std::shared_ptr mpDevice; + ref mpDevice; std::string mName; uint32_t mGridWidth = 0; @@ -251,15 +250,15 @@ namespace Falcor std::unordered_map mPrimitiveIDToIndex; uint32_t mNextPrimitiveID = 0; bool mPrimitivesDirty = false; ///< True if the primitives have changed. - Buffer::SharedPtr mpPrimitivesBuffer; ///< Holds the primitives that should be rendered. + ref mpPrimitivesBuffer; ///< Holds the primitives that should be rendered. uint32_t mPrimitivesExcludedFromBuffer = 0; ///< Number of primitives to exclude from the primitive buffer. uint32_t mBakedPrimitiveCount = 0; ///< Number of primitives that will be baked into the value representation. bool mBakePrimitives = false; ///< True if the primitives should be baked into the value representation. bool mHasGridRepresentation = false; ///< True if a value representation exists. bool mInitializedWithPrimitives = false; ///< True if the grid was initialized with primitives. - Texture::SharedPtr mpSDFGridTexture; ///< A texture on the GPU holding the value representation. - ComputePass::SharedPtr mpEvaluatePrimitivesPass; + ref mpSDFGridTexture; ///< A texture on the GPU holding the value representation. + ref mpEvaluatePrimitivesPass; }; FALCOR_ENUM_CLASS_OPERATORS(SDFGrid::UpdateFlags); diff --git a/Source/Falcor/Scene/SDFs/SDFGridHitData.slang b/Source/Falcor/Scene/SDFs/SDFGridHitData.slang index 0d53d2292..08047101e 100644 --- a/Source/Falcor/Scene/SDFs/SDFGridHitData.slang +++ b/Source/Falcor/Scene/SDFs/SDFGridHitData.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-21, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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/Scene/SDFs/SparseBrickSet/SDFSBS.cpp b/Source/Falcor/Scene/SDFs/SparseBrickSet/SDFSBS.cpp index f9dbb0278..3e30b9b40 100644 --- a/Source/Falcor/Scene/SDFs/SparseBrickSet/SDFSBS.cpp +++ b/Source/Falcor/Scene/SDFs/SparseBrickSet/SDFSBS.cpp @@ -30,12 +30,12 @@ #include "Core/API/RenderContext.h" #include "Core/API/IndirectCommands.h" #include "Utils/Math/MathHelpers.h" +#include "Utils/Math/MathConstants.slangh" +#include "Utils/SharedCache.h" #include "Scene/SDFs/SDFVoxelTypes.slang" namespace Falcor { - Sampler::SharedPtr SDFSBS::spSDFSBSSampler; // TODO: REMOVEGLOBAL - namespace { const std::string kAssignBrickValidityShaderName = "Scene/SDFs/SparseBrickSet/SDFSBSAssignBrickValidityFromSDFieldPass.cs.slang"; @@ -58,20 +58,31 @@ namespace Falcor const uint32_t kChunkWidth = 4; } - SDFSBS::SharedPtr SDFSBS::create(std::shared_ptr pDevice, uint32_t brickWidth, bool compressed, uint32_t defaultGridWidth) + struct SDFSBS::SharedData { - checkArgument(!compressed || (brickWidth + 1) % 4 == 0, "'brickWidth' ({}) must be a multiple of 4 minus 1 for compressed SDFSBSs", brickWidth); - + ref pSampler; - if (!spSDFSBSSampler) + SharedData(ref pDevice) { Sampler::Desc samplerDesc; samplerDesc.setFilterMode(Sampler::Filter::Linear, Sampler::Filter::Linear, Sampler::Filter::Linear); samplerDesc.setAddressingMode(Sampler::AddressMode::Clamp, Sampler::AddressMode::Clamp, Sampler::AddressMode::Clamp); - spSDFSBSSampler = Sampler::create(pDevice.get(), samplerDesc); + pSampler = Sampler::create(pDevice, samplerDesc); } + }; - return SharedPtr(new SDFSBS(std::move(pDevice), brickWidth, compressed, defaultGridWidth)); + static SharedCache sSharedCache; + + SDFSBS::SDFSBS(ref pDevice, uint32_t brickWidth, bool compressed, uint32_t defaultGridWidth) + : SDFGrid(pDevice) + , mDefaultGridWidth(defaultGridWidth) + , mBrickWidth(brickWidth) + , mCompressed(compressed) + { + checkArgument(!compressed || (brickWidth + 1) % 4 == 0, "'brickWidth' ({}) must be a multiple of 4 minus 1 for compressed SDFSBSs", brickWidth); + + + mpSharedData = sSharedCache.acquire(mpDevice.get(), [this]() { return std::make_shared(mpDevice); }); } size_t SDFSBS::getSize() const @@ -146,14 +157,14 @@ namespace Falcor var["aabbs"] = mpBrickAABBsBuffer; var["indirectionBuffer"] = mpIndirectionTexture; var["bricks"] = mpBrickTexture; - var["sampler"] = spSDFSBSSampler; + var["sampler"] = mpSharedData->pSampler; var["virtualGridWidth"] = mGridWidth; var["virtualBricksPerAxis"] = mVirtualBricksPerAxis; var["bricksPerAxis"] = mBricksPerAxis; var["brickTextureDimensions"] = mBrickTextureDimensions; var["brickWidth"] = mBrickWidth; - var["normalizationFactor"] = 0.5f * glm::root_three() / mGridWidth; + var["normalizationFactor"] = 0.5f * float(M_SQRT3) / mGridWidth; } void SDFSBS::createResourcesFromSDField(RenderContext* pRenderContext, bool deleteScratchData) @@ -183,20 +194,20 @@ namespace Falcor if (!mpIndirectionTexture || mpIndirectionTexture->getWidth() < mVirtualBricksPerAxis) { - mpIndirectionTexture = Texture::create3D(mpDevice.get(), mVirtualBricksPerAxis, mVirtualBricksPerAxis, mVirtualBricksPerAxis, ResourceFormat::R32Uint, 1, nullptr, ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess); + mpIndirectionTexture = Texture::create3D(mpDevice, mVirtualBricksPerAxis, mVirtualBricksPerAxis, mVirtualBricksPerAxis, ResourceFormat::R32Uint, 1, nullptr, ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess); mpIndirectionTexture->setName("SDFSBS::IndirectionTextureValues"); } if (!mpValidityBuffer || mpValidityBuffer->getElementCount() < virtualBrickCount) { - mpValidityBuffer = Buffer::createStructured(mpDevice.get(), sizeof(uint32_t), virtualBrickCount, ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess, Buffer::CpuAccess::None, nullptr, false); + mpValidityBuffer = Buffer::createStructured(mpDevice, sizeof(uint32_t), virtualBrickCount, ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess, Buffer::CpuAccess::None, nullptr, false); } else { pRenderContext->clearUAV(mpValidityBuffer->getUAV().get(), uint4(0)); } - auto paramBlock = mpAssignBrickValidityPass["gParamBlock"]; + auto paramBlock = mpAssignBrickValidityPass->getRootVar()["gParamBlock"]; paramBlock["virtualGridWidth"] = mGridWidth; paramBlock["virtualBricksPerAxis"] = mVirtualBricksPerAxis; paramBlock["brickWidthInVoxels"] = mBrickWidth; @@ -214,12 +225,12 @@ namespace Falcor if (!mpIndirectionBuffer || mpIndirectionBuffer->getElementCount() < virtualBrickCount) { - mpIndirectionBuffer = Buffer::createStructured(mpDevice.get(), sizeof(uint32_t), virtualBrickCount, ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess, Buffer::CpuAccess::None, nullptr, false); + mpIndirectionBuffer = Buffer::createStructured(mpDevice, sizeof(uint32_t), virtualBrickCount, ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess, Buffer::CpuAccess::None, nullptr, false); } if (!mpCountBuffer) { - mpCountBuffer = Buffer::create(mpDevice.get(), 4, ResourceBindFlags::None, Buffer::CpuAccess::None); + mpCountBuffer = Buffer::create(mpDevice, 4, ResourceBindFlags::None, Buffer::CpuAccess::None); } pRenderContext->copyResource(mpIndirectionBuffer.get(), mpValidityBuffer.get()); @@ -236,7 +247,7 @@ namespace Falcor mpResetBrickValidityPass = ComputePass::create(mpDevice, desc); } - auto paramBlock = mpResetBrickValidityPass["gParamBlock"]; + auto paramBlock = mpResetBrickValidityPass->getRootVar()["gParamBlock"]; paramBlock["virtualBrickCount"] = virtualBrickCount; paramBlock["brickValidity"] = mpValidityBuffer; paramBlock["indirectionBuffer"] = mpIndirectionBuffer; @@ -252,7 +263,7 @@ namespace Falcor mpCopyIndirectionBufferPass = ComputePass::create(mpDevice, desc); } - auto paramBlock = mpCopyIndirectionBufferPass["gParamBlock"]; + auto paramBlock = mpCopyIndirectionBufferPass->getRootVar()["gParamBlock"]; paramBlock["virtualBricksPerAxis"] = mVirtualBricksPerAxis; paramBlock["indirectionBuffer"] = mpIndirectionBuffer; paramBlock["indirectionTexture"] = mpIndirectionTexture; @@ -290,16 +301,16 @@ namespace Falcor if (mCompressed) { - mpBrickTexture = Texture::create2D(mpDevice.get(), textureWidth, textureHeight, ResourceFormat::BC4Snorm, 1, 1); + mpBrickTexture = Texture::create2D(mpDevice, textureWidth, textureHeight, ResourceFormat::BC4Snorm, 1, 1); // Compression scheme may change the actual width and height to something else. mBrickTextureDimensions = uint2(mpBrickTexture->getWidth(), mpBrickTexture->getHeight()); - mpBrickScratchTexture = Texture::create2D(mpDevice.get(), mBrickTextureDimensions.x / 4, mBrickTextureDimensions.y / 4, ResourceFormat::RG32Int, 1, 1, nullptr, Resource::BindFlags::UnorderedAccess); + mpBrickScratchTexture = Texture::create2D(mpDevice, mBrickTextureDimensions.x / 4, mBrickTextureDimensions.y / 4, ResourceFormat::RG32Int, 1, 1, nullptr, Resource::BindFlags::UnorderedAccess); } else { - mpBrickTexture = Texture::create2D(mpDevice.get(), textureWidth, textureHeight, ResourceFormat::R8Snorm, 1, 1, nullptr, ResourceBindFlags::UnorderedAccess | ResourceBindFlags::ShaderResource); + mpBrickTexture = Texture::create2D(mpDevice, textureWidth, textureHeight, ResourceFormat::R8Snorm, 1, 1, nullptr, ResourceBindFlags::UnorderedAccess | ResourceBindFlags::ShaderResource); mBrickTextureDimensions = uint2(textureWidth, textureHeight); } @@ -307,10 +318,10 @@ namespace Falcor if (!mpBrickAABBsBuffer || mpBrickAABBsBuffer->getElementCount() < mBrickCount) { - mpBrickAABBsBuffer = Buffer::createStructured(mpDevice.get(), sizeof(AABB), mBrickCount, ResourceBindFlags::UnorderedAccess | ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, nullptr, false); + mpBrickAABBsBuffer = Buffer::createStructured(mpDevice, sizeof(AABB), mBrickCount, ResourceBindFlags::UnorderedAccess | ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, nullptr, false); } - auto paramBlock = mpCreateBricksFromSDFieldPass["gParamBlock"]; + auto paramBlock = mpCreateBricksFromSDFieldPass->getRootVar()["gParamBlock"]; paramBlock["virtualGridWidth"] = mGridWidth; paramBlock["virtualBrickCount"] = virtualBrickCount; paramBlock["virtualBricksPerAxis"] = mVirtualBricksPerAxis; @@ -366,7 +377,7 @@ namespace Falcor if (!includeValues && mBakePrimitives) { // Create a grid to hold the values if the grid is missing a value representation but request to bake the primitives. - mpSDFGridTexture = Texture::create3D(mpDevice.get(), gridWidthInValues, gridWidthInValues, gridWidthInValues, ResourceFormat::R8Snorm, 1, nullptr, Resource::BindFlags::ShaderResource | Resource::BindFlags::UnorderedAccess); + mpSDFGridTexture = Texture::create3D(mpDevice, gridWidthInValues, gridWidthInValues, gridWidthInValues, ResourceFormat::R8Snorm, 1, nullptr, Resource::BindFlags::ShaderResource | Resource::BindFlags::UnorderedAccess); mpSDFGridTexture->setName("SDFSBS::SDFGridTexture"); pRenderContext->clearUAV(mpSDFGridTexture->getUAV().get(), float4(std::numeric_limits::max())); includeValues = true; @@ -384,7 +395,7 @@ namespace Falcor if (!mpChunkCoordsBuffer || mpChunkCoordsBuffer->getSize() < mBrickCount * sizeof(uint3)) { uint3 initialData(0); - mpChunkCoordsBuffer = Buffer::create(mpDevice.get(), mBrickCount * sizeof(uint3), Resource::BindFlags::ShaderResource | Resource::BindFlags::UnorderedAccess, Buffer::CpuAccess::None, (const void*)&initialData); + mpChunkCoordsBuffer = Buffer::create(mpDevice, mBrickCount * sizeof(uint3), Resource::BindFlags::ShaderResource | Resource::BindFlags::UnorderedAccess, Buffer::CpuAccess::None, (const void*)&initialData); mpChunkCoordsBuffer->setName("SDFSBS::chunkCoordsBuffer"); } createEmptyBrick = true; @@ -472,11 +483,11 @@ namespace Falcor // Create or update primitives buffer. { - mpCreateRootChunksFromPrimitives["gParamBlock"]["primitives"] = mpPrimitivesBuffer; - mpSubdivideChunksUsingPrimitives["gParamBlock"]["primitives"] = mpPrimitivesBuffer; - mpCoarselyPruneEmptyBricks["gParamBlock"]["primitives"] = mpPrimitivesBuffer; - mpFinelyPruneEmptyBricks["gParamBlock"]["primitives"] = mpPrimitivesBuffer; - mpCreateBricksFromChunks["gParamBlock"]["primitives"] = mpPrimitivesBuffer; + mpCreateRootChunksFromPrimitives->getRootVar()["gParamBlock"]["primitives"] = mpPrimitivesBuffer; + mpSubdivideChunksUsingPrimitives->getRootVar()["gParamBlock"]["primitives"] = mpPrimitivesBuffer; + mpCoarselyPruneEmptyBricks->getRootVar()["gParamBlock"]["primitives"] = mpPrimitivesBuffer; + mpFinelyPruneEmptyBricks->getRootVar()["gParamBlock"]["primitives"] = mpPrimitivesBuffer; + mpCreateBricksFromChunks->getRootVar()["gParamBlock"]["primitives"] = mpPrimitivesBuffer; } if (includeValues) @@ -492,7 +503,7 @@ namespace Falcor uint32_t currentSubChunkCount = currentGridWidth * currentGridWidth * currentGridWidth; if (!mpSubChunkValidityBuffer || mpSubChunkValidityBuffer->getSize() < currentSubChunkCount * sizeof(uint32_t)) { - mpSubChunkValidityBuffer = Buffer::create(mpDevice.get(), currentSubChunkCount * sizeof(uint32_t)); + mpSubChunkValidityBuffer = Buffer::create(mpDevice, currentSubChunkCount * sizeof(uint32_t)); mpSubChunkValidityBuffer->setName("SDFSBS::SubChunkValidityBuffer"); } else @@ -502,13 +513,13 @@ namespace Falcor if (!mpSubChunkCoordsBuffer || mpSubChunkCoordsBuffer->getElementCount() < currentSubChunkCount * sizeof(uint3)) { - mpSubChunkCoordsBuffer = Buffer::create(mpDevice.get(), currentSubChunkCount * sizeof(uint3)); + mpSubChunkCoordsBuffer = Buffer::create(mpDevice, currentSubChunkCount * sizeof(uint3)); mpSubChunkCoordsBuffer->setName("SDFSBS::SubChunkCoordsBuffer"); } // Create root chunk(s). { - auto paramBlock = mpCreateRootChunksFromPrimitives["gParamBlock"]; + auto paramBlock = mpCreateRootChunksFromPrimitives->getRootVar()["gParamBlock"]; paramBlock["primitiveCount"] = primitiveCount - mCurrentBakedPrimitiveCount; paramBlock["currentGridWidth"] = currentGridWidth; paramBlock["groupCount"] = 1; @@ -528,13 +539,13 @@ namespace Falcor if (!mpSubdivisionArgBuffer) { static const DispatchArguments baseIndirectArgs = { 0, 1, 1 }; - mpSubdivisionArgBuffer = Buffer::create(mpDevice.get(), sizeof(DispatchArguments), ResourceBindFlags::IndirectArg, Buffer::CpuAccess::None, &baseIndirectArgs); + mpSubdivisionArgBuffer = Buffer::create(mpDevice, sizeof(DispatchArguments), ResourceBindFlags::IndirectArg, Buffer::CpuAccess::None, &baseIndirectArgs); } // Subdivisions. if (!createEmptyBrick) { - auto subdivideParamBlock = mpSubdivideChunksUsingPrimitives["gParamBlock"]; + auto subdivideParamBlock = mpSubdivideChunksUsingPrimitives->getRootVar()["gParamBlock"]; subdivideParamBlock["primitiveCount"] = primitiveCount - mCurrentBakedPrimitiveCount; // The root pass performed one subdivision, subtract one from subdivisionCount to get the number of remaining subdivisions. @@ -544,7 +555,7 @@ namespace Falcor // Update chunk buffers for this subdivision. if (!mpChunkIndirectionBuffer || mpChunkIndirectionBuffer->getSize() < mpSubChunkValidityBuffer->getSize()) { - mpChunkIndirectionBuffer = Buffer::create(mpDevice.get(), mpSubChunkValidityBuffer->getSize()); + mpChunkIndirectionBuffer = Buffer::create(mpDevice, mpSubChunkValidityBuffer->getSize()); mpChunkIndirectionBuffer->setName("SDFSBS::ChunkIndirectionBuffer"); } @@ -561,7 +572,7 @@ namespace Falcor { if (!mpChunkCoordsBuffer || mpChunkCoordsBuffer->getSize() < currentChunkCount * sizeof(uint3)) { - mpChunkCoordsBuffer = Buffer::create(mpDevice.get(), currentChunkCount * sizeof(uint3)); + mpChunkCoordsBuffer = Buffer::create(mpDevice, currentChunkCount * sizeof(uint3)); mpChunkCoordsBuffer->setName("SDFSBS::ChunkCoordsBuffer"); } @@ -575,7 +586,7 @@ namespace Falcor // Clear or realloc sub chunk buffers. if (!mpSubChunkValidityBuffer || mpSubChunkValidityBuffer->getSize() < currentSubChunkCount * sizeof(uint32_t)) { - mpSubChunkValidityBuffer = Buffer::create(mpDevice.get(), currentSubChunkCount * sizeof(uint32_t)); + mpSubChunkValidityBuffer = Buffer::create(mpDevice, currentSubChunkCount * sizeof(uint32_t)); mpSubChunkValidityBuffer->setName("SDFSBS::subChunkValidityBuffer"); } else @@ -585,7 +596,7 @@ namespace Falcor if (!mpSubChunkCoordsBuffer || mpSubChunkCoordsBuffer->getElementCount() < currentSubChunkCount * sizeof(uint3)) { - mpSubChunkCoordsBuffer = Buffer::create(mpDevice.get(), currentSubChunkCount * sizeof(uint3)); + mpSubChunkCoordsBuffer = Buffer::create(mpDevice, currentSubChunkCount * sizeof(uint3)); mpSubChunkCoordsBuffer->setName("SDFSBS::subChunkCoordsBuffer"); } @@ -614,7 +625,7 @@ namespace Falcor // Update chunk buffer for brick creation. if (!mpChunkIndirectionBuffer || mpChunkIndirectionBuffer->getSize() < mpSubChunkValidityBuffer->getSize()) { - mpChunkIndirectionBuffer = Buffer::create(mpDevice.get(), mpSubChunkValidityBuffer->getSize()); + mpChunkIndirectionBuffer = Buffer::create(mpDevice, mpSubChunkValidityBuffer->getSize()); mpChunkIndirectionBuffer->setName("SDFSBS::ChunkIndirectionBuffer"); } @@ -627,7 +638,7 @@ namespace Falcor if (!mpChunkCoordsBuffer || mpChunkCoordsBuffer->getSize() < mBrickCount * sizeof(uint3)) { - mpChunkCoordsBuffer = Buffer::create(mpDevice.get(), mBrickCount * sizeof(uint3)); + mpChunkCoordsBuffer = Buffer::create(mpDevice, mBrickCount * sizeof(uint3)); mpChunkCoordsBuffer->setName("SDFSBS::chunkCoordsBuffer"); } @@ -644,7 +655,7 @@ namespace Falcor pRenderContext->clearUAV(mpSubChunkValidityBuffer->getUAV().get(), uint4(0)); pRenderContext->copyBufferRegion(mpSubChunkCoordsBuffer.get(), 0, mpChunkCoordsBuffer.get(), 0, preCoarsePruningBrickCount * sizeof(uint3)); - auto paramBlock = mpCoarselyPruneEmptyBricks["gParamBlock"]; + auto paramBlock = mpCoarselyPruneEmptyBricks->getRootVar()["gParamBlock"]; paramBlock["primitiveCount"] = primitiveCount - mCurrentBakedPrimitiveCount; paramBlock["gridWidth"] = mGridWidth; paramBlock["brickCount"] = preCoarsePruningBrickCount; @@ -674,7 +685,7 @@ namespace Falcor pRenderContext->clearUAV(mpSubChunkValidityBuffer->getUAV().get(), uint4(0)); pRenderContext->copyBufferRegion(mpSubChunkCoordsBuffer.get(), 0, mpChunkCoordsBuffer.get(), 0, preFinePruningBrickCount * sizeof(uint3)); - auto paramBlock = mpFinelyPruneEmptyBricks["gParamBlock"]; + auto paramBlock = mpFinelyPruneEmptyBricks->getRootVar()["gParamBlock"]; paramBlock["primitiveCount"] = primitiveCount - mCurrentBakedPrimitiveCount; paramBlock["gridWidth"] = mGridWidth; paramBlock["brickCount"] = preFinePruningBrickCount; @@ -700,14 +711,14 @@ namespace Falcor // Allocate AABB buffer, indirection buffer and brick texture. if (!mpBrickAABBsBuffer || mpBrickAABBsBuffer->getElementCount() < mBrickCount) { - mpBrickAABBsBuffer = Buffer::createStructured(mpDevice.get(), sizeof(AABB), mBrickCount, ResourceBindFlags::UnorderedAccess | ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, nullptr, false); + mpBrickAABBsBuffer = Buffer::createStructured(mpDevice, sizeof(AABB), mBrickCount, ResourceBindFlags::UnorderedAccess | ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, nullptr, false); mpBrickAABBsBuffer->setName("SDFSBS::BrickAABBsBuffer"); updateFlags |= UpdateFlags::BuffersReallocated; } if (!mpIndirectionTexture || mpIndirectionTexture->getWidth() < mVirtualBricksPerAxis) { - mpIndirectionTexture = Texture::create3D(mpDevice.get(), mVirtualBricksPerAxis, mVirtualBricksPerAxis, mVirtualBricksPerAxis, ResourceFormat::R32Uint, 1, nullptr, ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess); + mpIndirectionTexture = Texture::create3D(mpDevice, mVirtualBricksPerAxis, mVirtualBricksPerAxis, mVirtualBricksPerAxis, ResourceFormat::R32Uint, 1, nullptr, ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess); mpIndirectionTexture->setName("SDFSBS::IndirectionTextureValuesPrims"); updateFlags |= UpdateFlags::BuffersReallocated; } @@ -729,17 +740,17 @@ namespace Falcor if (mCompressed) { - mpBrickTexture = Texture::create2D(mpDevice.get(), textureWidth, textureHeight, ResourceFormat::BC4Snorm, 1, 1); + mpBrickTexture = Texture::create2D(mpDevice, textureWidth, textureHeight, ResourceFormat::BC4Snorm, 1, 1); mpBrickTexture->setName("SDFSBS::BrickTexture"); // Compression scheme may change the actual width and height to something else. mBrickTextureDimensions = uint2(mpBrickTexture->getWidth(), mpBrickTexture->getHeight()); - mpBrickScratchTexture = Texture::create2D(mpDevice.get(), mBrickTextureDimensions.x / 4, mBrickTextureDimensions.y / 4, ResourceFormat::RG32Int, 1, 1, nullptr, Resource::BindFlags::UnorderedAccess); + mpBrickScratchTexture = Texture::create2D(mpDevice, mBrickTextureDimensions.x / 4, mBrickTextureDimensions.y / 4, ResourceFormat::RG32Int, 1, 1, nullptr, Resource::BindFlags::UnorderedAccess); } else { - mpBrickTexture = Texture::create2D(mpDevice.get(), textureWidth, textureHeight, ResourceFormat::R8Snorm, 1, 1, nullptr, ResourceBindFlags::UnorderedAccess | ResourceBindFlags::ShaderResource); + mpBrickTexture = Texture::create2D(mpDevice, textureWidth, textureHeight, ResourceFormat::R8Snorm, 1, 1, nullptr, ResourceBindFlags::UnorderedAccess | ResourceBindFlags::ShaderResource); mpBrickTexture->setName("SDFSBS::BrickTexture"); mBrickTextureDimensions = uint2(textureWidth, textureHeight); @@ -751,17 +762,18 @@ namespace Falcor { if (!mpSDFGridTextureModified || mpSDFGridTextureModified->getWidth() < gridWidthInValues) { - mpSDFGridTextureModified = Texture::create3D(mpDevice.get(), gridWidthInValues, gridWidthInValues, gridWidthInValues, ResourceFormat::R8Snorm, 1, nullptr, Resource::BindFlags::ShaderResource | Resource::BindFlags::UnorderedAccess); + mpSDFGridTextureModified = Texture::create3D(mpDevice, gridWidthInValues, gridWidthInValues, gridWidthInValues, ResourceFormat::R8Snorm, 1, nullptr, Resource::BindFlags::ShaderResource | Resource::BindFlags::UnorderedAccess); mpSDFGridTextureModified->setName("SDFSBS::SDFGridTextureModified"); } createEvaluatePrimitivesPass(true, true); - mpEvaluatePrimitivesPass["CB"]["gGridWidth"] = mGridWidth; - mpEvaluatePrimitivesPass["CB"]["gPrimitiveCount"] = mBakedPrimitiveCount - mCurrentBakedPrimitiveCount; - mpEvaluatePrimitivesPass["gPrimitives"] = mpPrimitivesBuffer; - mpEvaluatePrimitivesPass["gOldValues"] = mpSDFGridTexture; - mpEvaluatePrimitivesPass["gValues"] = mpSDFGridTextureModified; + auto var = mpEvaluatePrimitivesPass->getRootVar(); + var["CB"]["gGridWidth"] = mGridWidth; + var["CB"]["gPrimitiveCount"] = mBakedPrimitiveCount - mCurrentBakedPrimitiveCount; + var["gPrimitives"] = mpPrimitivesBuffer; + var["gOldValues"] = mpSDFGridTexture; + var["gValues"] = mpSDFGridTextureModified; mpEvaluatePrimitivesPass->execute(pRenderContext, uint3(mGridWidth + 1)); pRenderContext->copyResource(mpSDFGridTexture.get(), mpSDFGridTextureModified.get()); @@ -772,7 +784,7 @@ namespace Falcor updatePrimitivesBuffer(); } - auto cparamBlock = mpCreateBricksFromChunks["gParamBlock"]; + auto cparamBlock = mpCreateBricksFromChunks->getRootVar()["gParamBlock"]; cparamBlock["primitiveCount"] = primitiveCount - mBakedPrimitiveCount; cparamBlock["gridWidth"] = mGridWidth; cparamBlock["brickCount"] = mBrickCount; @@ -843,7 +855,7 @@ namespace Falcor { if (!mpOldSDFGridTexture || mpOldSDFGridTexture->getWidth() != oldGridWidthInValues) { - mpOldSDFGridTexture = Texture::create3D(mpDevice.get(), oldGridWidthInValues, oldGridWidthInValues, oldGridWidthInValues, ResourceFormat::R8Snorm, 1, nullptr, Resource::BindFlags::ShaderResource | Resource::BindFlags::UnorderedAccess); + mpOldSDFGridTexture = Texture::create3D(mpDevice, oldGridWidthInValues, oldGridWidthInValues, oldGridWidthInValues, ResourceFormat::R8Snorm, 1, nullptr, Resource::BindFlags::ShaderResource | Resource::BindFlags::UnorderedAccess); mpOldSDFGridTexture->setName("SDFSBS::OldSDFGridTexture"); } @@ -853,7 +865,7 @@ namespace Falcor // Create destination grid texture to write to. if (!mpSDFGridTexture || mpSDFGridTexture->getWidth() < gridWidthInValues) { - mpSDFGridTexture = Texture::create3D(mpDevice.get(), gridWidthInValues, gridWidthInValues, gridWidthInValues, ResourceFormat::R8Snorm, 1, nullptr, Resource::BindFlags::ShaderResource | Resource::BindFlags::UnorderedAccess); + mpSDFGridTexture = Texture::create3D(mpDevice, gridWidthInValues, gridWidthInValues, gridWidthInValues, ResourceFormat::R8Snorm, 1, nullptr, Resource::BindFlags::ShaderResource | Resource::BindFlags::UnorderedAccess); mpSDFGridTexture->setName("SDFSBS::SDFGridTexture"); } @@ -863,7 +875,7 @@ namespace Falcor pRenderContext->clearUAV(mpSDFGridTexture->getUAV().get(), float4(std::numeric_limits::max())); - auto sdFieldData = mpExpandSDFieldPass["gSDFieldData"]; + auto sdFieldData = mpExpandSDFieldPass->getRootVar()["gSDFieldData"]; sdFieldData["gridWidthInValues"] = oldGridWidthInValues; sdFieldData["offset"] = offest; sdFieldData["expansionFactor"] = (float)(oldGridWidthInValues - 1) / (gridWidthInValues - 1); @@ -919,14 +931,14 @@ namespace Falcor auto& pTexture = mIntervalSDFieldMaps[i]; if (!pTexture || pTexture->getWidth() < width) { - pTexture = Texture::create3D(mpDevice.get(), width, width, width, ResourceFormat::RG8Snorm, 1, nullptr, ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess); + pTexture = Texture::create3D(mpDevice, width, width, width, ResourceFormat::RG8Snorm, 1, nullptr, ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess); pTexture->setName("SDFSBS::IntervalValuesMaps[" + std::to_string(i) + "]"); } if (i == 0) { uint32_t threadsWidth = mBrickWidth <= 8u ? mGridWidth : mBrickWidth; - auto paramBlock = mpComputeRootIntervalSDFieldFromGridPass["gParamBlock"]; + auto paramBlock = mpComputeRootIntervalSDFieldFromGridPass->getRootVar()["gParamBlock"]; paramBlock["virtualGridWidth"] = threadsWidth; paramBlock["sdfGrid"] = mpSDFGridTexture; paramBlock["intervalDistances"] = pTexture; @@ -934,7 +946,7 @@ namespace Falcor } else { - auto paramBlock = mpComputeIntervalSDFieldFromGridPass["gParamBlock"]; + auto paramBlock = mpComputeIntervalSDFieldFromGridPass->getRootVar()["gParamBlock"]; paramBlock["virtualGridWidth"] = width; paramBlock["sdfGrid"] = mpSDFGridTexture; paramBlock["intervalDistances"] = pTexture; @@ -967,10 +979,10 @@ namespace Falcor mSDField.resize(valueCount); // The grid is in the size [-1, 1] thus the longest distance that can be stored is sqrt(3) (the length from corner to corner) - float normalizationFactor = 2.0f * mGridWidth / glm::root_three(); + float normalizationFactor = 2.0f * mGridWidth / float(M_SQRT3); for (uint32_t v = 0; v < valueCount; v++) { - float normalizedValue = glm::clamp(cornerValues[v] * normalizationFactor, -1.0f, 1.0f); + float normalizedValue = std::clamp(cornerValues[v] * normalizationFactor, -1.0f, 1.0f); float integerScale = normalizedValue * float(INT8_MAX); mSDField[v] = integerScale >= 0.0f ? int8_t(integerScale + 0.5f) : int8_t(integerScale - 0.5f); } @@ -986,7 +998,7 @@ namespace Falcor } else { - mpSDFGridTexture = Texture::create3D(mpDevice.get(), mGridWidth + 1, mGridWidth + 1, mGridWidth + 1, ResourceFormat::R8Snorm, 1, sdField.data()); + mpSDFGridTexture = Texture::create3D(mpDevice, mGridWidth + 1, mGridWidth + 1, mGridWidth + 1, ResourceFormat::R8Snorm, 1, sdField.data()); } mSDFieldUpdated = true; @@ -995,11 +1007,11 @@ namespace Falcor mHasGridRepresentation = true; } - uint32_t SDFSBS::fetchCount(RenderContext* pRenderContext, const Buffer::SharedPtr& pBuffer) + uint32_t SDFSBS::fetchCount(RenderContext* pRenderContext, const ref& pBuffer) { if (!mpCountStagingBuffer) { - mpCountStagingBuffer = Buffer::create(mpDevice.get(), 4, ResourceBindFlags::None, Buffer::CpuAccess::Read); + mpCountStagingBuffer = Buffer::create(mpDevice, 4, ResourceBindFlags::None, Buffer::CpuAccess::Read); } // Copy result to staging buffer. @@ -1021,7 +1033,7 @@ namespace Falcor mpCompactifyChunks = ComputePass::create(mpDevice, desc); } - auto paramBlock = mpCompactifyChunks["gParamBlock"]; + auto paramBlock = mpCompactifyChunks->getRootVar()["gParamBlock"]; paramBlock["chunkIndirection"] = mpChunkIndirectionBuffer; paramBlock["chunkValidity"] = mpSubChunkValidityBuffer; paramBlock["chunkCoords"] = mpSubChunkCoordsBuffer; @@ -1029,12 +1041,4 @@ namespace Falcor paramBlock["chunkCount"] = chunkCount; mpCompactifyChunks->execute(pRenderContext, chunkCount, 1); } - - SDFSBS::SDFSBS(std::shared_ptr pDevice, uint32_t brickWidth, bool compressed, uint32_t defaultGridWidth) - : SDFGrid(std::move(pDevice)) - , mDefaultGridWidth(defaultGridWidth) - , mBrickWidth(brickWidth) - , mCompressed(compressed) - { - } } diff --git a/Source/Falcor/Scene/SDFs/SparseBrickSet/SDFSBS.h b/Source/Falcor/Scene/SDFs/SparseBrickSet/SDFSBS.h index d74f12c7b..9f91a6ee3 100644 --- a/Source/Falcor/Scene/SDFs/SparseBrickSet/SDFSBS.h +++ b/Source/Falcor/Scene/SDFs/SparseBrickSet/SDFSBS.h @@ -27,8 +27,8 @@ **************************************************************************/ #pragma once +#include "Core/Pass/ComputePass.h" #include "Scene/SDFs/SDFGrid.h" -#include "RenderGraph/BasePasses/ComputePass.h" #include "Utils/Algorithm/PrefixSum.h" namespace Falcor @@ -38,15 +38,16 @@ namespace Falcor class FALCOR_API SDFSBS : public SDFGrid { public: - using SharedPtr = std::shared_ptr; + struct SharedData; - /** Create a new, empty SDF sparse brick set. + static ref create(ref pDevice, uint32_t brickWidth = 7, bool compressed = false, uint32_t defaultGridWidth = 256) { return make_ref(pDevice, brickWidth, compressed, defaultGridWidth); } + + /** Create an empty SDF sparse brick set. \param[in] brickWidth The width of a brick in voxels. \param[in] compressed Selects if bricks should be compressed using lossy BC4 compression. brickWidth + 1 must be a multiple of 4 to enable compression. \param[in] defaultGridWidth The grid width used if the data was not loaded from a file (it is empty). - \return SDFSBS object, or nullptr if errors occurred. */ - static SharedPtr create(std::shared_ptr pDevice, uint32_t brickWidth = 7, bool compressed = false, uint32_t defaultGridWidth = 256); + SDFSBS(ref pDevice, uint32_t brickWidth, bool compressed, uint32_t defaultGridWidth); virtual UpdateFlags update(RenderContext* pRenderContext) override; @@ -60,7 +61,7 @@ namespace Falcor virtual void createResources(RenderContext* pRenderContext, bool deleteScratchData = true) override; - virtual const Buffer::SharedPtr& getAABBBuffer() const override { return mpBrickAABBsBuffer; } + virtual const ref& getAABBBuffer() const override { return mpBrickAABBsBuffer; } virtual uint32_t getAABBCount() const override { return mBrickCount; } virtual void setShaderData(const ShaderVar& var) const override; @@ -81,13 +82,11 @@ namespace Falcor void createSDFGridTexture(RenderContext* pRenderContext, const std::vector& sdField); - uint32_t fetchCount(RenderContext* pRenderContext, const Buffer::SharedPtr& pBuffer); + uint32_t fetchCount(RenderContext* pRenderContext, const ref& pBuffer); void compactifyChunks(RenderContext* pRenderContext, uint32_t chunkCount); private: - SDFSBS(std::shared_ptr pDevice, uint32_t brickWidth, bool compressed, uint32_t defaultGridWidth); - // CPU data. std::vector mSDField; @@ -109,54 +108,52 @@ namespace Falcor bool mBuildEmptyGrid = false; // GPU data. - Buffer::SharedPtr mpBrickAABBsBuffer; ///< A compact buffer containing AABBs for each brick. - Texture::SharedPtr mpIndirectionTexture; ///< An indirection texture to map from virtual brick coords to actual brick ID. - Texture::SharedPtr mpBrickTexture; ///< A texture of SDF bricks with data at corners. - - // Sampler, shared among all SDFSBS instances. - static Sampler::SharedPtr spSDFSBSSampler; // TODO: REMOVEGLOBAL + ref mpBrickAABBsBuffer; ///< A compact buffer containing AABBs for each brick. + ref mpIndirectionTexture; ///< An indirection texture to map from virtual brick coords to actual brick ID. + ref mpBrickTexture; ///< A texture of SDF bricks with data at corners. + std::shared_ptr mpSharedData; ///< Shared data among all instances. // Compute passes used to build the SBS from signed distance field. - ComputePass::SharedPtr mpAssignBrickValidityPass; - ComputePass::SharedPtr mpResetBrickValidityPass; - ComputePass::SharedPtr mpCopyIndirectionBufferPass; - ComputePass::SharedPtr mpCreateBricksFromSDFieldPass; + ref mpAssignBrickValidityPass; + ref mpResetBrickValidityPass; + ref mpCopyIndirectionBufferPass; + ref mpCreateBricksFromSDFieldPass; // Compute passes used to build the SBS from primitives. - ComputePass::SharedPtr mpCreateRootChunksFromPrimitives; - ComputePass::SharedPtr mpSubdivideChunksUsingPrimitives; - ComputePass::SharedPtr mpCompactifyChunks; - ComputePass::SharedPtr mpCoarselyPruneEmptyBricks; - ComputePass::SharedPtr mpFinelyPruneEmptyBricks; - ComputePass::SharedPtr mpCreateBricksFromChunks; + ref mpCreateRootChunksFromPrimitives; + ref mpSubdivideChunksUsingPrimitives; + ref mpCompactifyChunks; + ref mpCoarselyPruneEmptyBricks; + ref mpFinelyPruneEmptyBricks; + ref mpCreateBricksFromChunks; // Compute passes used to build the SBS from signed distance field and primitives. - ComputePass::SharedPtr mpComputeRootIntervalSDFieldFromGridPass; - ComputePass::SharedPtr mpComputeIntervalSDFieldFromGridPass; - ComputePass::SharedPtr mpExpandSDFieldPass; + ref mpComputeRootIntervalSDFieldFromGridPass; + ref mpComputeIntervalSDFieldFromGridPass; + ref mpExpandSDFieldPass; // Compute passes used to build the SBS from both the SD Field and primitives. std::unique_ptr mpPrefixSumPass; // Scratch data used for building from signed distance field. - Texture::SharedPtr mpBrickScratchTexture; - Buffer::SharedPtr mpIndirectionBuffer; - Buffer::SharedPtr mpValidityBuffer; + ref mpBrickScratchTexture; + ref mpIndirectionBuffer; + ref mpValidityBuffer; - Buffer::SharedPtr mpCountBuffer; + ref mpCountBuffer; // Scratch data used for building from primitives. - Buffer::SharedPtr mpChunkIndirectionBuffer; - Buffer::SharedPtr mpChunkCoordsBuffer; - Buffer::SharedPtr mpSubChunkValidityBuffer; - Buffer::SharedPtr mpSubChunkCoordsBuffer; - Buffer::SharedPtr mpSubdivisionArgBuffer; - GpuFence::SharedPtr mpReadbackFence; + ref mpChunkIndirectionBuffer; + ref mpChunkCoordsBuffer; + ref mpSubChunkValidityBuffer; + ref mpSubChunkCoordsBuffer; + ref mpSubdivisionArgBuffer; + ref mpReadbackFence; // Scratch data used for building from the SD Field and primitives. - Texture::SharedPtr mpOldSDFGridTexture; - Texture::SharedPtr mpSDFGridTextureModified; - std::vector mIntervalSDFieldMaps; - Buffer::SharedPtr mpCountStagingBuffer; + ref mpOldSDFGridTexture; + ref mpSDFGridTextureModified; + std::vector> mIntervalSDFieldMaps; + ref mpCountStagingBuffer; }; } diff --git a/Source/Falcor/Scene/SDFs/SparseVoxelOctree/SDFSVO.cpp b/Source/Falcor/Scene/SDFs/SparseVoxelOctree/SDFSVO.cpp index 4d8628cdc..f11b3c644 100644 --- a/Source/Falcor/Scene/SDFs/SparseVoxelOctree/SDFSVO.cpp +++ b/Source/Falcor/Scene/SDFs/SparseVoxelOctree/SDFSVO.cpp @@ -29,12 +29,12 @@ #include "Core/API/Device.h" #include "Core/API/RenderContext.h" #include "Utils/Math/MathHelpers.h" +#include "Utils/Math/MathConstants.slangh" +#include "Utils/SharedCache.h" #include "Scene/SDFs/SDFVoxelTypes.slang" namespace Falcor { - Buffer::SharedPtr SDFSVO::spSDFSVOGridUnitAABBBuffer; // TODO: REMOVEGLOBAL - namespace { const std::string kSDFCountSurfaceVoxelsShaderName = "Scene/SDFs/SDFSurfaceVoxelCounter.cs.slang"; @@ -57,24 +57,32 @@ namespace Falcor } } - SDFSVO::SharedPtr SDFSVO::create(std::shared_ptr pDevice) + struct SDFSVO::SharedData { -#if !FALCOR_NVAPI_AVAILABLE - throw RuntimeError("SDFSVO requires NVAPI. See installation instructions in README."); -#endif + ref pUnitAABBBuffer; - if (!spSDFSVOGridUnitAABBBuffer) + SharedData(ref pDevice) { RtAABB unitAABB { float3(-0.5f), float3(0.5f) }; - spSDFSVOGridUnitAABBBuffer = Buffer::create(pDevice.get(), sizeof(RtAABB), Resource::BindFlags::ShaderResource, Buffer::CpuAccess::None, &unitAABB); + pUnitAABBBuffer = Buffer::create(pDevice, sizeof(RtAABB), Resource::BindFlags::ShaderResource, Buffer::CpuAccess::None, &unitAABB); } + }; - return SharedPtr(new SDFSVO(std::move(pDevice))); + static SharedCache sSharedCache; + + SDFSVO::SDFSVO(ref pDevice) + : SDFGrid(pDevice) + { +#if !FALCOR_NVAPI_AVAILABLE + throw RuntimeError("SDFSVO requires NVAPI. See installation instructions in README."); +#endif + + mpSharedData = sSharedCache.acquire(mpDevice.get(), [this]() { return std::make_shared(mpDevice); }); } size_t SDFSVO::getSize() const { - return (spSDFSVOGridUnitAABBBuffer ? spSDFSVOGridUnitAABBBuffer->getSize() : 0) + (mpSVOBuffer ? mpSVOBuffer->getSize() : 0); + return mpSharedData->pUnitAABBBuffer->getSize() + (mpSVOBuffer ? mpSVOBuffer->getSize() : 0); } uint32_t SDFSVO::getMaxPrimitiveIDBits() const @@ -96,7 +104,7 @@ namespace Falcor } else { - mpSDFGridTexture = Texture::create3D(mpDevice.get(), mGridWidth + 1, mGridWidth + 1, mGridWidth + 1, ResourceFormat::R8Snorm, 1, mValues.data()); + mpSDFGridTexture = Texture::create3D(mpDevice, mGridWidth + 1, mGridWidth + 1, mGridWidth + 1, ResourceFormat::R8Snorm, 1, mValues.data()); } if (!mpCountSurfaceVoxelsPass) @@ -108,11 +116,11 @@ namespace Falcor if (!mpSurfaceVoxelCounter) { - mpReadbackFence = GpuFence::create(mpDevice.get()); + mpReadbackFence = GpuFence::create(mpDevice); static const uint32_t zero = 0; - mpSurfaceVoxelCounter = Buffer::create(mpDevice.get(), sizeof(uint32_t), Resource::BindFlags::UnorderedAccess, Buffer::CpuAccess::None, &zero); - mpSurfaceVoxelCounterStagingBuffer = Buffer::create(mpDevice.get(), sizeof(uint32_t), Resource::BindFlags::None, Buffer::CpuAccess::Read); + mpSurfaceVoxelCounter = Buffer::create(mpDevice, sizeof(uint32_t), Resource::BindFlags::UnorderedAccess, Buffer::CpuAccess::None, &zero); + mpSurfaceVoxelCounterStagingBuffer = Buffer::create(mpDevice, sizeof(uint32_t), Resource::BindFlags::None, Buffer::CpuAccess::Read); } else { @@ -123,9 +131,10 @@ namespace Falcor // Count the number of surface containing voxels in the texture. { - mpCountSurfaceVoxelsPass["CB"]["gGridWidth"] = mGridWidth; - mpCountSurfaceVoxelsPass["gSDFGrid"] = mpSDFGridTexture; - mpCountSurfaceVoxelsPass["gTotalVoxelCount"] = mpSurfaceVoxelCounter; + auto var = mpCountSurfaceVoxelsPass->getRootVar(); + var["CB"]["gGridWidth"] = mGridWidth; + var["gSDFGrid"] = mpSDFGridTexture; + var["gTotalVoxelCount"] = mpSurfaceVoxelCounter; mpCountSurfaceVoxelsPass->execute(pRenderContext, mGridWidth, mGridWidth, mGridWidth); // Copy surface containing voxels count to staging buffer. @@ -147,7 +156,7 @@ namespace Falcor { uint32_t levelWidth = 1 << l; uint32_t levelVoxelMax = levelWidth * levelWidth * levelWidth; - worstCaseTotalVoxels += glm::min(finestLevelVoxelCount, levelVoxelMax); + worstCaseTotalVoxels += std::min(finestLevelVoxelCount, levelVoxelMax); } // Create the hash table that will store all voxels during the building process. @@ -155,8 +164,8 @@ namespace Falcor if (hashTableCapacity < worstCaseTotalVoxels) { hashTableCapacity = ceilPow2(worstCaseTotalVoxels); - mpHashTableBuffer = Buffer::create(mpDevice.get(), hashTableCapacity * sizeof(SDFSVOHashTableVoxel)); - mpLocationCodesBuffer = Buffer::create(mpDevice.get(), hashTableCapacity * sizeof(uint64_t)); + mpHashTableBuffer = Buffer::create(mpDevice, hashTableCapacity * sizeof(SDFSVOHashTableVoxel)); + mpLocationCodesBuffer = Buffer::create(mpDevice, hashTableCapacity * sizeof(uint64_t)); } else { @@ -174,13 +183,14 @@ namespace Falcor // Create voxels for the bottom level. { - auto cbVar = mpBuildFinestLevelFromDistanceTexturePass["CB"]; + auto rootVar = mpBuildFinestLevelFromDistanceTexturePass->getRootVar(); + auto cbVar = rootVar["CB"]; cbVar["gLevel"] = (mLevelCount - 1); cbVar["gNumLevels"] = mLevelCount; cbVar["gLevelWidth"] = mGridWidth; - mpBuildFinestLevelFromDistanceTexturePass["gSDFGrid"] = mpSDFGridTexture; - mpBuildFinestLevelFromDistanceTexturePass["gLocationCodes"] = mpLocationCodesBuffer; - auto hashTableVar = mpBuildFinestLevelFromDistanceTexturePass["gVoxelHashTable"]; + rootVar["gSDFGrid"] = mpSDFGridTexture; + rootVar["gLocationCodes"] = mpLocationCodesBuffer; + auto hashTableVar = rootVar["gVoxelHashTable"]; hashTableVar["buffer"] = mpHashTableBuffer; hashTableVar["capacity"] = hashTableCapacity; mpBuildFinestLevelFromDistanceTexturePass->execute(pRenderContext, mGridWidth, mGridWidth, mGridWidth); @@ -198,21 +208,22 @@ namespace Falcor std::vector voxelCountsPerLevel(mLevelCount, 0); voxelCountsPerLevel.back() = finestLevelVoxelCount; { - mpVoxelCountPerLevelBuffer = Buffer::create(mpDevice.get(), sizeof(uint32_t) * (mLevelCount - 1), Resource::BindFlags::ShaderResource | Resource::BindFlags::UnorderedAccess, Buffer::CpuAccess::None, voxelCountsPerLevel.data()); - mpVoxelCountPerLevelStagingBuffer = Buffer::create(mpDevice.get(), sizeof(uint32_t) * (mLevelCount - 1), Resource::BindFlags::None, Buffer::CpuAccess::Read); + mpVoxelCountPerLevelBuffer = Buffer::create(mpDevice, sizeof(uint32_t) * (mLevelCount - 1), Resource::BindFlags::ShaderResource | Resource::BindFlags::UnorderedAccess, Buffer::CpuAccess::None, voxelCountsPerLevel.data()); + mpVoxelCountPerLevelStagingBuffer = Buffer::create(mpDevice, sizeof(uint32_t) * (mLevelCount - 1), Resource::BindFlags::None, Buffer::CpuAccess::Read); } // Create voxels for all the other levels, a voxel is only created if a child voxel has been created for that voxel. { // Set common shader vars. - auto cbVar = mpBuildLevelFromDistanceTexturePass["CB"]; + auto rootVar = mpBuildLevelFromDistanceTexturePass->getRootVar(); + auto cbVar = rootVar["CB"]; cbVar["gNumLevels"] = mLevelCount; - mpBuildLevelFromDistanceTexturePass["gSDFGrid"] = mpSDFGridTexture; - mpBuildLevelFromDistanceTexturePass["gLocationCodes"] = mpLocationCodesBuffer; - auto hashTableVar = mpBuildLevelFromDistanceTexturePass["gVoxelHashTable"]; + rootVar["gSDFGrid"] = mpSDFGridTexture; + rootVar["gLocationCodes"] = mpLocationCodesBuffer; + auto hashTableVar = rootVar["gVoxelHashTable"]; hashTableVar["buffer"] = mpHashTableBuffer; hashTableVar["capacity"] = hashTableCapacity; - mpBuildLevelFromDistanceTexturePass["gVoxelCounts"] = mpVoxelCountPerLevelBuffer; + rootVar["gVoxelCounts"] = mpVoxelCountPerLevelBuffer; for (int32_t l = mLevelCount - 2; l >= 0; l--) { @@ -237,7 +248,7 @@ namespace Falcor const uint32_t kSDFSVOLocationCodeBigFlip = 2; const uint32_t kSDFSVOLocationCodeBigDisperse = 3; - uint32_t groupSize = glm::min(kLocationCodeSorterMaxGroupSize, hashTableCapacity >> 1); + uint32_t groupSize = std::min(kLocationCodeSorterMaxGroupSize, hashTableCapacity >> 1); if (!mpSortLocationCodesPass) { @@ -259,8 +270,9 @@ namespace Falcor mpSortLocationCodesPass->addDefine("BUFFER_SIZE", std::to_string(hashTableCapacity)); } - auto cbVar = mpSortLocationCodesPass["CB"]; - mpSortLocationCodesPass["gBuffer"] = mpLocationCodesBuffer; + auto rootVar = mpSortLocationCodesPass->getRootVar(); + auto cbVar = rootVar["CB"]; + rootVar["gBuffer"] = mpLocationCodesBuffer; uint32_t dispatchSize = hashTableCapacity >> 1; // Dispatch a local bitonic merge sort (BMS) that will sort as many elements as possible in group memory. @@ -319,13 +331,14 @@ namespace Falcor mpWriteSVOOffsetsPass = ComputePass::create(mpDevice, desc); } - auto cbVar = mpWriteSVOOffsetsPass["CB"]; + auto rootVar = mpWriteSVOOffsetsPass->getRootVar(); + auto cbVar = rootVar["CB"]; cbVar["gLocationCodeStartOffset"] = hashTableCapacity - mSVOElementCount; cbVar["gVoxelCount"] = mSVOElementCount; - auto hashTableVar = mpWriteSVOOffsetsPass["gVoxelHashTable"]; + auto hashTableVar = rootVar["gVoxelHashTable"]; hashTableVar["buffer"] = mpHashTableBuffer; hashTableVar["capacity"] = hashTableCapacity; - mpWriteSVOOffsetsPass["gLocationCodes"] = mpLocationCodesBuffer; + rootVar["gLocationCodes"] = mpLocationCodesBuffer; mpWriteSVOOffsetsPass->execute(pRenderContext, mSVOElementCount, 1); } @@ -336,7 +349,7 @@ namespace Falcor uint32_t requiredSVOSize = mSVOElementCount * sizeof(SDFSVOVoxel); if (!mpSVOBuffer || mpSVOBuffer->getSize() < requiredSVOSize) { - mpSVOBuffer = Buffer::create(mpDevice.get(), requiredSVOSize); + mpSVOBuffer = Buffer::create(mpDevice, requiredSVOSize); } if (!mpBuildOctreePass) @@ -348,14 +361,15 @@ namespace Falcor // Build the SVO from the levels hash table. { - auto cbVar = mpBuildOctreePass["CB"]; + auto rootVar = mpBuildOctreePass->getRootVar(); + auto cbVar = rootVar["CB"]; cbVar["gLocationCodeStartOffset"] = hashTableCapacity - mSVOElementCount; cbVar["gVoxelCount"] = mSVOElementCount; - auto hashTableVar = mpBuildOctreePass["gVoxelHashTable"]; + auto hashTableVar = rootVar["gVoxelHashTable"]; hashTableVar["buffer"] = mpHashTableBuffer; hashTableVar["capacity"] = hashTableCapacity; - mpBuildOctreePass["gLocationCodes"] = mpLocationCodesBuffer; - mpBuildOctreePass["gSVO"] = mpSVOBuffer; + rootVar["gLocationCodes"] = mpLocationCodesBuffer; + rootVar["gSVO"] = mpSVOBuffer; mpBuildOctreePass->execute(pRenderContext, mSVOElementCount, 1); } @@ -378,6 +392,11 @@ namespace Falcor } } + const ref& SDFSVO::getAABBBuffer() const + { + return mpSharedData->pUnitAABBBuffer; + } + void SDFSVO::setShaderData(const ShaderVar& var) const { if (!mpSVOBuffer) throw RuntimeError("SDFSVO::setShaderData() can't be called before calling SDFSVO::createResources()!"); @@ -394,10 +413,10 @@ namespace Falcor uint32_t valueCount = gridWidthInValues * gridWidthInValues * gridWidthInValues; mValues.resize(valueCount); - float normalizationMultipler = mGridWidth / (0.5f * glm::root_three()); + float normalizationMultipler = mGridWidth / (0.5f * float(M_SQRT3)); for (uint32_t v = 0; v < valueCount; v++) { - float normalizedValue = glm::clamp(cornerValues[v] * normalizationMultipler, -1.0f, 1.0f); + float normalizedValue = std::clamp(cornerValues[v] * normalizationMultipler, -1.0f, 1.0f); float integerScale = normalizedValue * float(INT8_MAX); mValues[v] = integerScale >= 0.0f ? int8_t(integerScale + 0.5f) : int8_t(integerScale - 0.5f); diff --git a/Source/Falcor/Scene/SDFs/SparseVoxelOctree/SDFSVO.h b/Source/Falcor/Scene/SDFs/SparseVoxelOctree/SDFSVO.h index bd8a5e047..5dbf9bdc2 100644 --- a/Source/Falcor/Scene/SDFs/SparseVoxelOctree/SDFSVO.h +++ b/Source/Falcor/Scene/SDFs/SparseVoxelOctree/SDFSVO.h @@ -30,7 +30,7 @@ #include "Scene/SDFs/SDFGrid.h" #include "Core/API/Buffer.h" #include "Core/API/Texture.h" -#include "RenderGraph/BasePasses/ComputePass.h" +#include "Core/Pass/ComputePass.h" namespace Falcor { @@ -39,12 +39,12 @@ namespace Falcor class FALCOR_API SDFSVO : public SDFGrid { public: - using SharedPtr = std::shared_ptr; + struct SharedData; - /** Create a new, empty SDFSVO. - \return SDFSVO object, or nullptr if errors occurred. - */ - static SharedPtr create(std::shared_ptr pDevice); + static ref create(ref pDevice) { return make_ref(pDevice); } + + /// Create an empty SDFSVO. + SDFSVO(ref pDevice); uint32_t getSVOIndexBitCount() const { return mSVOIndexBitCount; } @@ -55,7 +55,7 @@ namespace Falcor virtual void createResources(RenderContext* pRenderContext, bool deleteScratchData = true) override; - virtual const Buffer::SharedPtr& getAABBBuffer() const override { return spSDFSVOGridUnitAABBBuffer; } + virtual const ref& getAABBBuffer() const override; virtual uint32_t getAABBCount() const override { return 1; } virtual void setShaderData(const ShaderVar& var) const override; @@ -64,8 +64,6 @@ namespace Falcor virtual void setValuesInternal(const std::vector& cornerValues) override; private: - SDFSVO(std::shared_ptr pDevice) : SDFGrid(std::move(pDevice)) {} - // CPU data. std::vector mValues; @@ -76,27 +74,25 @@ namespace Falcor uint32_t mSVOIndexBitCount = 0; // GPU Data. - Buffer::SharedPtr mpSVOBuffer; - - // Resources shared among all SDFSVOs. - static Buffer::SharedPtr spSDFSVOGridUnitAABBBuffer; // TODO: REMOVEGLOBAL + ref mpSVOBuffer; + std::shared_ptr mpSharedData; ///< Shared data among all instances. // Compute passes used to build the SVO. - ComputePass::SharedPtr mpCountSurfaceVoxelsPass; - ComputePass::SharedPtr mpBuildFinestLevelFromDistanceTexturePass; - ComputePass::SharedPtr mpBuildLevelFromDistanceTexturePass; - ComputePass::SharedPtr mpSortLocationCodesPass; - ComputePass::SharedPtr mpWriteSVOOffsetsPass; - ComputePass::SharedPtr mpBuildOctreePass; + ref mpCountSurfaceVoxelsPass; + ref mpBuildFinestLevelFromDistanceTexturePass; + ref mpBuildLevelFromDistanceTexturePass; + ref mpSortLocationCodesPass; + ref mpWriteSVOOffsetsPass; + ref mpBuildOctreePass; // Scratch data used for building. - Texture::SharedPtr mpSDFGridTexture; - Buffer::SharedPtr mpSurfaceVoxelCounter; - Buffer::SharedPtr mpSurfaceVoxelCounterStagingBuffer; - Buffer::SharedPtr mpVoxelCountPerLevelBuffer; - Buffer::SharedPtr mpVoxelCountPerLevelStagingBuffer; - Buffer::SharedPtr mpHashTableBuffer; - Buffer::SharedPtr mpLocationCodesBuffer; - GpuFence::SharedPtr mpReadbackFence; + ref mpSDFGridTexture; + ref mpSurfaceVoxelCounter; + ref mpSurfaceVoxelCounterStagingBuffer; + ref mpVoxelCountPerLevelBuffer; + ref mpVoxelCountPerLevelStagingBuffer; + ref mpHashTableBuffer; + ref mpLocationCodesBuffer; + ref mpReadbackFence; }; } diff --git a/Source/Falcor/Scene/SDFs/SparseVoxelSet/SDFSVS.cpp b/Source/Falcor/Scene/SDFs/SparseVoxelSet/SDFSVS.cpp index a3975c6d3..a9112097b 100644 --- a/Source/Falcor/Scene/SDFs/SparseVoxelSet/SDFSVS.cpp +++ b/Source/Falcor/Scene/SDFs/SparseVoxelSet/SDFSVS.cpp @@ -29,6 +29,7 @@ #include "Core/API/Device.h" #include "Core/API/RenderContext.h" #include "Utils/Math/MathHelpers.h" +#include "Utils/Math/MathConstants.slangh" #include "Scene/SDFs/SDFVoxelTypes.slang" namespace Falcor @@ -39,11 +40,6 @@ namespace Falcor const std::string kSDFSVSVoxelizerShaderName = "Scene/SDFs/SparseVoxelSet/SDFSVSVoxelizer.cs.slang"; } - SDFSVS::SharedPtr SDFSVS::create(std::shared_ptr pDevice) - { - return SharedPtr(new SDFSVS(std::move(pDevice))); - } - size_t SDFSVS::getSize() const { return (mpVoxelBuffer ? mpVoxelBuffer->getSize() : 0) + (mpVoxelAABBBuffer ? mpVoxelAABBBuffer->getSize() : 0); @@ -67,7 +63,7 @@ namespace Falcor } else { - mpSDFGridTexture = Texture::create3D(mpDevice.get(), mGridWidth + 1, mGridWidth + 1, mGridWidth + 1, ResourceFormat::R8Snorm, 1, mValues.data()); + mpSDFGridTexture = Texture::create3D(mpDevice, mGridWidth + 1, mGridWidth + 1, mGridWidth + 1, ResourceFormat::R8Snorm, 1, mValues.data()); } if (!mpCountSurfaceVoxelsPass) @@ -80,8 +76,8 @@ namespace Falcor if (!mpSurfaceVoxelCounter) { static uint32_t zero = 0; - mpSurfaceVoxelCounter = Buffer::create(mpDevice.get(), sizeof(uint32_t), Resource::BindFlags::UnorderedAccess, Buffer::CpuAccess::None, &zero); - mpSurfaceVoxelCounterStagingBuffer = Buffer::create(mpDevice.get(), sizeof(uint32_t), Resource::BindFlags::None, Buffer::CpuAccess::Read); + mpSurfaceVoxelCounter = Buffer::create(mpDevice, sizeof(uint32_t), Resource::BindFlags::UnorderedAccess, Buffer::CpuAccess::None, &zero); + mpSurfaceVoxelCounterStagingBuffer = Buffer::create(mpDevice, sizeof(uint32_t), Resource::BindFlags::None, Buffer::CpuAccess::Read); } else { @@ -90,14 +86,15 @@ namespace Falcor if (!mpReadbackFence) { - mpReadbackFence = GpuFence::create(mpDevice.get()); + mpReadbackFence = GpuFence::create(mpDevice); } // Count the number of surface containing voxels in the texture. { - mpCountSurfaceVoxelsPass["CB"]["gGridWidth"] = mGridWidth; - mpCountSurfaceVoxelsPass["gSDFGrid"] = mpSDFGridTexture; - mpCountSurfaceVoxelsPass["gTotalVoxelCount"] = mpSurfaceVoxelCounter; + auto var = mpCountSurfaceVoxelsPass->getRootVar(); + var["CB"]["gGridWidth"] = mGridWidth; + var["gSDFGrid"] = mpSDFGridTexture; + var["gTotalVoxelCount"] = mpSurfaceVoxelCounter; mpCountSurfaceVoxelsPass->execute(pRenderContext, mGridWidth, mGridWidth, mGridWidth); // Copy surface containing voxels count to staging buffer. @@ -117,12 +114,12 @@ namespace Falcor { if (!mpVoxelAABBBuffer || mpVoxelAABBBuffer->getElementCount() < mVoxelCount) { - mpVoxelAABBBuffer = Buffer::createStructured(mpDevice.get(), sizeof(AABB), mVoxelCount); + mpVoxelAABBBuffer = Buffer::createStructured(mpDevice, sizeof(AABB), mVoxelCount); } if (!mpVoxelBuffer || mpVoxelBuffer->getElementCount() < mVoxelCount) { - mpVoxelBuffer = Buffer::createStructured(mpDevice.get(), sizeof(SDFSVSVoxel), mVoxelCount); + mpVoxelBuffer = Buffer::createStructured(mpDevice, sizeof(SDFSVSVoxel), mVoxelCount); } } @@ -137,12 +134,13 @@ namespace Falcor pRenderContext->clearUAVCounter(mpVoxelBuffer, 0); - mpSDFSVSVoxelizerPass["CB"]["gVirtualGridLevel"] = bitScanReverse(mGridWidth) + 1; - mpSDFSVSVoxelizerPass["CB"]["gVirtualGridWidth"] = mGridWidth; - mpSDFSVSVoxelizerPass["gSDFGrid"] = mpSDFGridTexture; + auto var = mpSDFSVSVoxelizerPass->getRootVar(); + var["CB"]["gVirtualGridLevel"] = bitScanReverse(mGridWidth) + 1; + var["CB"]["gVirtualGridWidth"] = mGridWidth; + var["gSDFGrid"] = mpSDFGridTexture; - mpSDFSVSVoxelizerPass["gVoxelAABBs"] = mpVoxelAABBBuffer; - mpSDFSVSVoxelizerPass["gVoxels"] = mpVoxelBuffer; + var["gVoxelAABBs"] = mpVoxelAABBBuffer; + var["gVoxels"] = mpVoxelBuffer; mpSDFSVSVoxelizerPass->execute(pRenderContext, mGridWidth, mGridWidth, mGridWidth); } @@ -166,7 +164,7 @@ namespace Falcor var["virtualGridWidth"] = mGridWidth; var["oneDivVirtualGridWidth"] = 1.0f / mGridWidth; - var["normalizationFactor"] = 0.5f * glm::root_three() / mGridWidth; + var["normalizationFactor"] = 0.5f * float(M_SQRT3) / mGridWidth; var["aabbs"] = mpVoxelAABBBuffer; var["voxels"] = mpVoxelBuffer; @@ -178,10 +176,10 @@ namespace Falcor uint32_t valueCount = gridWidthInValues * gridWidthInValues * gridWidthInValues; mValues.resize(valueCount); - float normalizationMultipler = 2.0f * mGridWidth / glm::root_three(); + float normalizationMultipler = 2.0f * mGridWidth / float(M_SQRT3); for (uint32_t v = 0; v < valueCount; v++) { - float normalizedValue = glm::clamp(cornerValues[v] * normalizationMultipler, -1.0f, 1.0f); + float normalizedValue = std::clamp(cornerValues[v] * normalizationMultipler, -1.0f, 1.0f); float integerScale = normalizedValue * float(INT8_MAX); mValues[v] = integerScale >= 0.0f ? int8_t(integerScale + 0.5f) : int8_t(integerScale - 0.5f); diff --git a/Source/Falcor/Scene/SDFs/SparseVoxelSet/SDFSVS.h b/Source/Falcor/Scene/SDFs/SparseVoxelSet/SDFSVS.h index 82424abf3..bed14a0e0 100644 --- a/Source/Falcor/Scene/SDFs/SparseVoxelSet/SDFSVS.h +++ b/Source/Falcor/Scene/SDFs/SparseVoxelSet/SDFSVS.h @@ -30,7 +30,7 @@ #include "Scene/SDFs/SDFGrid.h" #include "Core/API/Buffer.h" #include "Core/API/Texture.h" -#include "RenderGraph/BasePasses/ComputePass.h" +#include "Core/Pass/ComputePass.h" namespace Falcor { @@ -39,12 +39,10 @@ namespace Falcor class FALCOR_API SDFSVS : public SDFGrid { public: - using SharedPtr = std::shared_ptr; + static ref create(ref pDevice) { return make_ref(pDevice); } - /** Create a new, empty SDF sparse voxel set. - \return SDFSVS object, or nullptr if errors occurred. - */ - static SharedPtr create(std::shared_ptr pDevice); + /// Create am empty SDF sparse voxel set. + SDFSVS(ref pDevice) : SDFGrid(pDevice) {} virtual size_t getSize() const override; virtual uint32_t getMaxPrimitiveIDBits() const override; @@ -52,7 +50,7 @@ namespace Falcor virtual void createResources(RenderContext* pRenderContext, bool deleteScratchData = true) override; - virtual const Buffer::SharedPtr& getAABBBuffer() const override { return mpVoxelAABBBuffer; } + virtual const ref& getAABBBuffer() const override { return mpVoxelAABBBuffer; } virtual uint32_t getAABBCount() const override { return mVoxelCount; } virtual void setShaderData(const ShaderVar& var) const override; @@ -61,24 +59,22 @@ namespace Falcor virtual void setValuesInternal(const std::vector& cornerValues) override; private: - SDFSVS(std::shared_ptr pDevice) : SDFGrid(std::move(pDevice)) {} - // CPU data. std::vector mValues; // Specs. - Buffer::SharedPtr mpVoxelAABBBuffer; - Buffer::SharedPtr mpVoxelBuffer; + ref mpVoxelAABBBuffer; + ref mpVoxelBuffer; uint32_t mVoxelCount = 0; // Compute passes used to build the SVS. - ComputePass::SharedPtr mpCountSurfaceVoxelsPass; - ComputePass::SharedPtr mpSDFSVSVoxelizerPass; + ref mpCountSurfaceVoxelsPass; + ref mpSDFSVSVoxelizerPass; // Scratch data used for building. - GpuFence::SharedPtr mpReadbackFence; - Buffer::SharedPtr mpSurfaceVoxelCounter; - Buffer::SharedPtr mpSurfaceVoxelCounterStagingBuffer; - Texture::SharedPtr mpSDFGridTexture; + ref mpReadbackFence; + ref mpSurfaceVoxelCounter; + ref mpSurfaceVoxelCounterStagingBuffer; + ref mpSDFGridTexture; }; } diff --git a/Source/Falcor/Scene/Scene.cpp b/Source/Falcor/Scene/Scene.cpp index ecf448b61..c8c0a9954 100644 --- a/Source/Falcor/Scene/Scene.cpp +++ b/Source/Falcor/Scene/Scene.cpp @@ -39,6 +39,7 @@ #include "Core/API/RenderContext.h" #include "Core/API/IndirectCommands.h" #include "Utils/StringUtils.h" +#include "Utils/ObjectIDPython.h" #include "Utils/Math/Common.h" #include "Utils/Math/MathHelpers.h" #include "Utils/Math/Vector.h" @@ -121,9 +122,9 @@ namespace Falcor }; // Checks if the transform flips the coordinate system handedness (its determinant is negative). - bool doesTransformFlip(const rmcv::mat4& m) + bool doesTransformFlip(const float4x4& m) { - return rmcv::determinant((rmcv::mat3)m) < 0.f; + return determinant(float3x3(m)) < 0.f; } } @@ -142,8 +143,8 @@ namespace Falcor return sFilters; } - Scene::Scene(std::shared_ptr pDevice, SceneData&& sceneData) - : mpDevice(std::move(pDevice)) + Scene::Scene(ref pDevice, SceneData&& sceneData) + : mpDevice(pDevice) { // Copy/move scene data to member variables. mPath = sceneData.path; @@ -209,7 +210,7 @@ namespace Falcor createMeshUVTiles(mMeshDesc, sceneData.meshIndexData, sceneData.meshStaticData); // Create animation controller. - mpAnimationController = AnimationController::create(mpDevice, this, sceneData.meshStaticData, sceneData.meshSkinningData, sceneData.prevVertexCount, sceneData.animations); + mpAnimationController = std::make_unique(mpDevice, this, sceneData.meshStaticData, sceneData.meshSkinningData, sceneData.prevVertexCount, sceneData.animations); // Some runtime mesh data validation. These are essentially asserts, but large scenes are mostly opened in Release for (const auto& mesh : mMeshDesc) @@ -243,14 +244,14 @@ namespace Falcor finalize(); } - Scene::SharedPtr Scene::create(std::shared_ptr pDevice, const std::filesystem::path& path, const Settings& settings) + ref Scene::create(ref pDevice, const std::filesystem::path& path, const Settings& settings) { - return SceneBuilder::create(pDevice, path, settings)->getScene(); + return SceneBuilder(pDevice, path, settings).getScene(); } - Scene::SharedPtr Scene::create(std::shared_ptr pDevice, SceneData&& sceneData) + ref Scene::create(ref pDevice, SceneData&& sceneData) { - return Scene::SharedPtr(new Scene(pDevice, std::move(sceneData))); + return ref(new Scene(pDevice, std::move(sceneData))); } void Scene::updateSceneDefines() @@ -322,14 +323,14 @@ namespace Falcor return mpMaterials->getShaderModules(); } - const LightCollection::SharedPtr& Scene::getLightCollection(RenderContext* pRenderContext) + const ref& Scene::getLightCollection(RenderContext* pRenderContext) { if (!mpLightCollection) { checkInvariant(mFinalized, "getLightCollection() called before scene is ready for use"); - mpLightCollection = LightCollection::create(mpDevice, pRenderContext, shared_from_this()); - mpLightCollection->setShaderData(mpSceneBlock["lightCollection"]); + mpLightCollection = LightCollection::create(mpDevice, pRenderContext, this); + mpLightCollection->setShaderData(mpSceneBlock->getRootVar()["lightCollection"]); mSceneStats.emissiveMemoryInBytes = mpLightCollection->getMemoryUsageInBytes(); } @@ -341,7 +342,7 @@ namespace Falcor rasterize(pRenderContext, pState, pVars, mFrontClockwiseRS[cullMode], mFrontCounterClockwiseRS[cullMode]); } - void Scene::rasterize(RenderContext* pRenderContext, GraphicsState* pState, GraphicsVars* pVars, const RasterizerState::SharedPtr& pRasterizerStateCW, const RasterizerState::SharedPtr& pRasterizerStateCCW) + void Scene::rasterize(RenderContext* pRenderContext, GraphicsState* pState, GraphicsVars* pVars, const ref& pRasterizerStateCW, const ref& pRasterizerStateCCW) { FALCOR_PROFILE(pRenderContext, "rasterizeScene"); @@ -383,7 +384,7 @@ namespace Falcor return 8; } - void Scene::raytrace(RenderContext* pRenderContext, RtProgram* pProgram, const std::shared_ptr& pVars, uint3 dispatchDims) + void Scene::raytrace(RenderContext* pRenderContext, RtProgram* pProgram, const ref& pVars, uint3 dispatchDims) { FALCOR_PROFILE(pRenderContext, "raytraceScene"); @@ -415,11 +416,11 @@ namespace Falcor throw RuntimeError("Index buffer size exceeds 4GB"); } - Buffer::SharedPtr pIB; + ref pIB; if (ibSize > 0) { ResourceBindFlags ibBindFlags = Resource::BindFlags::Index | ResourceBindFlags::ShaderResource; - pIB = Buffer::create(mpDevice.get(), ibSize, ibBindFlags, Buffer::CpuAccess::None, indexData.data()); + pIB = Buffer::create(mpDevice, ibSize, ibBindFlags, Buffer::CpuAccess::None, indexData.data()); } // Create the vertex data structured buffer. @@ -430,11 +431,11 @@ namespace Falcor throw RuntimeError("Vertex buffer size exceeds 4GB"); } - Buffer::SharedPtr pStaticBuffer; + ref pStaticBuffer; if (vertexCount > 0) { ResourceBindFlags vbBindFlags = ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess | ResourceBindFlags::Vertex; - pStaticBuffer = Buffer::createStructured(mpDevice.get(), sizeof(PackedStaticVertexData), (uint32_t)vertexCount, vbBindFlags, Buffer::CpuAccess::None, nullptr, false); + pStaticBuffer = Buffer::createStructured(mpDevice, sizeof(PackedStaticVertexData), (uint32_t)vertexCount, vbBindFlags, Buffer::CpuAccess::None, nullptr, false); } Vao::BufferVec pVBs(kVertexBufferCount); @@ -444,19 +445,19 @@ namespace Falcor // This is only needed when rasterizing meshes in the scene. ResourceFormat drawIDFormat = drawCount <= (1 << 16) ? ResourceFormat::R16Uint : ResourceFormat::R32Uint; - Buffer::SharedPtr pDrawIDBuffer; + ref pDrawIDBuffer; if (drawIDFormat == ResourceFormat::R16Uint) { FALCOR_ASSERT(drawCount <= (1 << 16)); std::vector drawIDs(drawCount); for (uint32_t i = 0; i < drawCount; i++) drawIDs[i] = i; - pDrawIDBuffer = Buffer::create(mpDevice.get(), drawCount * sizeof(uint16_t), ResourceBindFlags::Vertex, Buffer::CpuAccess::None, drawIDs.data()); + pDrawIDBuffer = Buffer::create(mpDevice, drawCount * sizeof(uint16_t), ResourceBindFlags::Vertex, Buffer::CpuAccess::None, drawIDs.data()); } else if (drawIDFormat == ResourceFormat::R32Uint) { std::vector drawIDs(drawCount); for (uint32_t i = 0; i < drawCount; i++) drawIDs[i] = i; - pDrawIDBuffer = Buffer::create(mpDevice.get(), drawCount * sizeof(uint32_t), ResourceBindFlags::Vertex, Buffer::CpuAccess::None, drawIDs.data()); + pDrawIDBuffer = Buffer::create(mpDevice, drawCount * sizeof(uint32_t), ResourceBindFlags::Vertex, Buffer::CpuAccess::None, drawIDs.data()); } else FALCOR_UNREACHABLE(); @@ -465,17 +466,17 @@ namespace Falcor // Create vertex layout. // The layout only initializes the vertex data and draw ID layout. The skinning data doesn't get passed into the vertex shader. - VertexLayout::SharedPtr pLayout = VertexLayout::create(); + ref pLayout = VertexLayout::create(); // Add the packed static vertex data layout. - VertexBufferLayout::SharedPtr pStaticLayout = VertexBufferLayout::create(); + ref pStaticLayout = VertexBufferLayout::create(); pStaticLayout->addElement(VERTEX_POSITION_NAME, offsetof(PackedStaticVertexData, position), ResourceFormat::RGB32Float, 1, VERTEX_POSITION_LOC); pStaticLayout->addElement(VERTEX_PACKED_NORMAL_TANGENT_CURVE_RADIUS_NAME, offsetof(PackedStaticVertexData, packedNormalTangentCurveRadius), ResourceFormat::RGB32Float, 1, VERTEX_PACKED_NORMAL_TANGENT_CURVE_RADIUS_LOC); pStaticLayout->addElement(VERTEX_TEXCOORD_NAME, offsetof(PackedStaticVertexData, texCrd), ResourceFormat::RG32Float, 1, VERTEX_TEXCOORD_LOC); pLayout->addBufferLayout(kStaticDataBufferIndex, pStaticLayout); // Add the draw ID layout. - VertexBufferLayout::SharedPtr pInstLayout = VertexBufferLayout::create(); + ref pInstLayout = VertexBufferLayout::create(); pInstLayout->addElement(INSTANCE_DRAW_ID_NAME, 0, drawIDFormat, 1, INSTANCE_DRAW_ID_LOC); pInstLayout->setInputClass(VertexBufferLayout::InputClass::PerInstanceData, 1); pLayout->addBufferLayout(kDrawIdBufferIndex, pInstLayout); @@ -498,11 +499,11 @@ namespace Falcor throw RuntimeError("Curve index buffer size exceeds 4GB"); } - Buffer::SharedPtr pIB = nullptr; + ref pIB = nullptr; if (ibSize > 0) { ResourceBindFlags ibBindFlags = Resource::BindFlags::Index | ResourceBindFlags::ShaderResource; - pIB = Buffer::create(mpDevice.get(), ibSize, ibBindFlags, Buffer::CpuAccess::None, indexData.data()); + pIB = Buffer::create(mpDevice, ibSize, ibBindFlags, Buffer::CpuAccess::None, indexData.data()); } // Create the vertex data as structured buffers. @@ -515,7 +516,7 @@ namespace Falcor ResourceBindFlags vbBindFlags = ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess | ResourceBindFlags::Vertex; // Also upload the curve vertex data. - Buffer::SharedPtr pStaticBuffer = Buffer::createStructured(mpDevice.get(), sizeof(StaticCurveVertexData), (uint32_t)vertexCount, vbBindFlags, Buffer::CpuAccess::None, staticData.data(), false); + ref pStaticBuffer = Buffer::createStructured(mpDevice, sizeof(StaticCurveVertexData), (uint32_t)vertexCount, vbBindFlags, Buffer::CpuAccess::None, staticData.data(), false); // Curves do not need DrawIDBuffer. Vao::BufferVec pVBs(kVertexBufferCount - 1); @@ -523,10 +524,10 @@ namespace Falcor // Create vertex layout. // The layout only initializes the vertex data layout. The skinning data doesn't get passed into the vertex shader. - VertexLayout::SharedPtr pLayout = VertexLayout::create(); + ref pLayout = VertexLayout::create(); // Add the packed static vertex data layout. - VertexBufferLayout::SharedPtr pStaticLayout = VertexBufferLayout::create(); + ref pStaticLayout = VertexBufferLayout::create(); pStaticLayout->addElement(CURVE_VERTEX_POSITION_NAME, offsetof(StaticCurveVertexData, position), ResourceFormat::RGB32Float, 1, CURVE_VERTEX_POSITION_LOC); pStaticLayout->addElement(CURVE_VERTEX_RADIUS_NAME, offsetof(StaticCurveVertexData, radius), ResourceFormat::R32Float, 1, CURVE_VERTEX_RADIUS_LOC); pStaticLayout->addElement(CURVE_VERTEX_TEXCOORD_NAME, offsetof(StaticCurveVertexData, texCrd), ResourceFormat::RG32Float, 1, CURVE_VERTEX_TEXCOORD_LOC); @@ -594,7 +595,7 @@ namespace Falcor int2 v2 = int2(std::floor(vertices[2].texCrd[0]), std::floor(vertices[2].texCrd[1])); Rectangle* tile; - if (v0 == v1 && v0 == v2) + if (all(v0 == v1 && v0 == v2)) tile = &tiles[v0]; else tile = &largeTriangleTile; @@ -621,7 +622,7 @@ namespace Falcor { if (mSDFGrids.empty()) return; - for (const SDFGrid::SharedPtr& pSDFGrid : mSDFGrids) + for (const ref& pSDFGrid : mSDFGrids) { if (mSDFGridConfig.implementation == SDFGrid::Type::None) { @@ -710,7 +711,7 @@ namespace Falcor mSDFGridConfig.implementationData.SVO.svoIndexBitCount = 0; } - for (const SDFGrid::SharedPtr& pSDFGrid : mSDFGrids) + for (const ref& pSDFGrid : mSDFGrids) { pSDFGrid->createResources(mpDevice->getRenderContext()); @@ -731,93 +732,96 @@ namespace Falcor void Scene::createParameterBlock() { // Create parameter block. - ComputeProgram::SharedPtr pProgram = ComputeProgram::createFromFile(mpDevice, "Scene/SceneBlock.slang", "main", getSceneDefines()); - ParameterBlockReflection::SharedConstPtr pReflection = pProgram->getReflector()->getParameterBlock(kParameterBlockName); + ref pProgram = ComputeProgram::createFromFile(mpDevice, "Scene/SceneBlock.slang", "main", getSceneDefines()); + ref pReflection = pProgram->getReflector()->getParameterBlock(kParameterBlockName); FALCOR_ASSERT(pReflection); - mpSceneBlock = ParameterBlock::create(mpDevice.get(), pReflection); + mpSceneBlock = ParameterBlock::create(mpDevice, pReflection); + auto var = mpSceneBlock->getRootVar(); // Create GPU buffers. if (!mGeometryInstanceData.empty() && (!mpGeometryInstancesBuffer || mpGeometryInstancesBuffer->getElementCount() < mGeometryInstanceData.size())) { - mpGeometryInstancesBuffer = Buffer::createStructured(mpDevice.get(), mpSceneBlock[kGeometryInstanceBufferName], (uint32_t)mGeometryInstanceData.size(), Resource::BindFlags::ShaderResource, Buffer::CpuAccess::None, nullptr, false); + mpGeometryInstancesBuffer = Buffer::createStructured(mpDevice, var[kGeometryInstanceBufferName], (uint32_t)mGeometryInstanceData.size(), Resource::BindFlags::ShaderResource, Buffer::CpuAccess::None, nullptr, false); mpGeometryInstancesBuffer->setName("Scene::mpGeometryInstancesBuffer"); } if (!mMeshDesc.empty() && (!mpMeshesBuffer || mpMeshesBuffer->getElementCount() < mMeshDesc.size())) { - mpMeshesBuffer = Buffer::createStructured(mpDevice.get(), mpSceneBlock[kMeshBufferName], (uint32_t)mMeshDesc.size(), Resource::BindFlags::ShaderResource, Buffer::CpuAccess::None, nullptr, false); + mpMeshesBuffer = Buffer::createStructured(mpDevice, var[kMeshBufferName], (uint32_t)mMeshDesc.size(), Resource::BindFlags::ShaderResource, Buffer::CpuAccess::None, nullptr, false); mpMeshesBuffer->setName("Scene::mpMeshesBuffer"); } if (!mCurveDesc.empty() && (!mpCurvesBuffer || mpCurvesBuffer->getElementCount() < mCurveDesc.size())) { - mpCurvesBuffer = Buffer::createStructured(mpDevice.get(), mpSceneBlock[kCurveBufferName], (uint32_t)mCurveDesc.size(), Resource::BindFlags::ShaderResource, Buffer::CpuAccess::None, nullptr, false); + mpCurvesBuffer = Buffer::createStructured(mpDevice, var[kCurveBufferName], (uint32_t)mCurveDesc.size(), Resource::BindFlags::ShaderResource, Buffer::CpuAccess::None, nullptr, false); mpCurvesBuffer->setName("Scene::mpCurvesBuffer"); } if (!mLights.empty() && (!mpLightsBuffer || mpLightsBuffer->getElementCount() < mLights.size())) { - mpLightsBuffer = Buffer::createStructured(mpDevice.get(), mpSceneBlock[kLightsBufferName], (uint32_t)mLights.size(), Resource::BindFlags::ShaderResource, Buffer::CpuAccess::None, nullptr, false); + mpLightsBuffer = Buffer::createStructured(mpDevice, var[kLightsBufferName], (uint32_t)mLights.size(), Resource::BindFlags::ShaderResource, Buffer::CpuAccess::None, nullptr, false); mpLightsBuffer->setName("Scene::mpLightsBuffer"); } if (!mGridVolumes.empty() && (!mpGridVolumesBuffer || mpGridVolumesBuffer->getElementCount() < mGridVolumes.size())) { - mpGridVolumesBuffer = Buffer::createStructured(mpDevice.get(), mpSceneBlock[kGridVolumesBufferName], (uint32_t)mGridVolumes.size(), Resource::BindFlags::ShaderResource, Buffer::CpuAccess::None, nullptr, false); + mpGridVolumesBuffer = Buffer::createStructured(mpDevice, var[kGridVolumesBufferName], (uint32_t)mGridVolumes.size(), Resource::BindFlags::ShaderResource, Buffer::CpuAccess::None, nullptr, false); mpGridVolumesBuffer->setName("Scene::mpGridVolumesBuffer"); } // Bind resources to parameter block. - mpSceneBlock[kGeometryInstanceBufferName] = mpGeometryInstancesBuffer; - mpSceneBlock[kMeshBufferName] = mpMeshesBuffer; - mpSceneBlock[kCurveBufferName] = mpCurvesBuffer; - mpSceneBlock[kLightsBufferName] = mpLightsBuffer; - mpSceneBlock[kGridVolumesBufferName] = mpGridVolumesBuffer; + var[kGeometryInstanceBufferName] = mpGeometryInstancesBuffer; + var[kMeshBufferName] = mpMeshesBuffer; + var[kCurveBufferName] = mpCurvesBuffer; + var[kLightsBufferName] = mpLightsBuffer; + var[kGridVolumesBufferName] = mpGridVolumesBuffer; // Bind materials parameter block. - mpSceneBlock->setParameterBlock(kMaterialsBlockName, mpMaterials->getParameterBlock()); + var[kMaterialsBlockName].setParameterBlock(mpMaterials->getParameterBlock()); } void Scene::uploadResources() { FALCOR_ASSERT(mpAnimationController); + auto var = mpSceneBlock->getRootVar(); + // Upload geometry. if (!mMeshDesc.empty()) mpMeshesBuffer->setBlob(mMeshDesc.data(), 0, sizeof(MeshDesc) * mMeshDesc.size()); if (!mCurveDesc.empty()) mpCurvesBuffer->setBlob(mCurveDesc.data(), 0, sizeof(CurveDesc) * mCurveDesc.size()); - auto sdfGridsVar = mpSceneBlock[kSDFGridsArrayName]; + auto sdfGridsVar = var[kSDFGridsArrayName]; for (uint32_t i = 0; i < mSDFGrids.size(); i++) { - const SDFGrid::SharedPtr& pGrid = mSDFGrids[i]; + const ref& pGrid = mSDFGrids[i]; pGrid->setShaderData(sdfGridsVar[i]); } if (mpMeshVao != nullptr) { - if (hasIndexBuffer()) mpSceneBlock->setBuffer(kIndexBufferName, mpMeshVao->getIndexBuffer()); - mpSceneBlock->setBuffer(kVertexBufferName, mpMeshVao->getVertexBuffer(Scene::kStaticDataBufferIndex)); - mpSceneBlock->setBuffer(kPrevVertexBufferName, mpAnimationController->getPrevVertexData()); // Can be nullptr + if (hasIndexBuffer()) var[kIndexBufferName] = mpMeshVao->getIndexBuffer(); + var[kVertexBufferName] = mpMeshVao->getVertexBuffer(Scene::kStaticDataBufferIndex); + var[kPrevVertexBufferName] = mpAnimationController->getPrevVertexData(); // Can be nullptr } if (mpCurveVao != nullptr) { - mpSceneBlock->setBuffer(kCurveIndexBufferName, mpCurveVao->getIndexBuffer()); - mpSceneBlock->setBuffer(kCurveVertexBufferName, mpCurveVao->getVertexBuffer(Scene::kStaticDataBufferIndex)); - mpSceneBlock->setBuffer(kPrevCurveVertexBufferName, mpAnimationController->getPrevCurveVertexData()); + var[kCurveIndexBufferName] = mpCurveVao->getIndexBuffer(); + var[kCurveVertexBufferName] = mpCurveVao->getVertexBuffer(Scene::kStaticDataBufferIndex); + var[kPrevCurveVertexBufferName] = mpAnimationController->getPrevCurveVertexData(); } } void Scene::uploadSelectedCamera() { - getCamera()->setShaderData(mpSceneBlock[kCamera]); + getCamera()->setShaderData(mpSceneBlock->getRootVar()[kCamera]); } void Scene::updateBounds() @@ -828,7 +832,7 @@ namespace Falcor for (const auto& inst : mGeometryInstanceData) { - const rmcv::mat4& transform = globalMatrices[inst.globalMatrixID]; + const float4x4& transform = globalMatrices[inst.globalMatrixID]; switch (inst.getType()) { case GeometryType::TriangleMesh: @@ -846,12 +850,12 @@ namespace Falcor } case GeometryType::SDFGrid: { - rmcv::mat3 transform3x3 = rmcv::mat3(transform); - transform3x3[0] = glm::abs(transform3x3[0]); - transform3x3[1] = glm::abs(transform3x3[1]); - transform3x3[2] = glm::abs(transform3x3[2]); - float3 center = transform.getCol(3); - float3 halfExtent = transform3x3 * float3(0.5f); + float3x3 transform3x3 = float3x3(transform); + transform3x3[0] = abs(transform3x3[0]); + transform3x3[1] = abs(transform3x3[1]); + transform3x3[2] = abs(transform3x3[2]); + float3 center = transform.getCol(3).xyz(); + float3 halfExtent = transformVector(transform3x3, float3(0.5f)); mSceneBB |= AABB(center - halfExtent, center + halfExtent); break; } @@ -883,7 +887,7 @@ namespace Falcor uint32_t prevFlags = inst.flags; FALCOR_ASSERT(inst.globalMatrixID < globalMatrices.size()); - const rmcv::mat4& transform = globalMatrices[inst.globalMatrixID]; + const float4x4& transform = globalMatrices[inst.globalMatrixID]; bool isTransformFlipped = doesTransformFlip(transform); bool isObjectFrontFaceCW = getMesh(MeshID::fromSlang(inst.geometryID)).isFrontFaceCW(); bool isWorldFrontFaceCW = isObjectFrontFaceCW ^ isTransformFlipped; @@ -934,7 +938,7 @@ namespace Falcor } mRtAABBRaw.resize(totalAABBCount); - uint32_t offset = 0; + uint32_t index = 0; size_t firstUpdated = std::numeric_limits::max(); size_t lastUpdated = 0; @@ -946,8 +950,8 @@ namespace Falcor { // Track range of updated AABBs. // TODO: Per-curve flag to indicate changes. For now assume all curves need updating. - firstUpdated = std::min(firstUpdated, (size_t)offset); - lastUpdated = std::max(lastUpdated, (size_t)offset + curve.indexCount); + firstUpdated = std::min(firstUpdated, (size_t)index); + lastUpdated = std::max(lastUpdated, (size_t)index + curve.indexCount); const auto* indexData = &mCurveIndexData[curve.ibOffset]; const auto* staticData = &mCurveStaticData[curve.vbOffset]; @@ -963,27 +967,27 @@ namespace Falcor curveSegBB.include(staticData[v + k].position + float3(staticData[v + k].radius)); } - mRtAABBRaw[offset++] = static_cast(curveSegBB); + mRtAABBRaw[index++] = static_cast(curveSegBB); } flags |= Scene::UpdateFlags::CurvesMoved; } - FALCOR_ASSERT(offset == curveAABBCount); + FALCOR_ASSERT(index == curveAABBCount); } - offset = (uint32_t)curveAABBCount; + index = (uint32_t)curveAABBCount; if (forceUpdate || mCustomPrimitivesChanged || mCustomPrimitivesMoved) { - mCustomPrimitiveAABBOffset = offset; + mCustomPrimitiveAABBOffset = index; // Track range of updated AABBs. - firstUpdated = std::min(firstUpdated, (size_t)offset); - lastUpdated = std::max(lastUpdated, (size_t)offset + customAABBCount); + firstUpdated = std::min(firstUpdated, (size_t)index); + lastUpdated = std::max(lastUpdated, (size_t)index + customAABBCount); for (auto& aabb : mCustomPrimitiveAABBs) { - mRtAABBRaw[offset++] = static_cast(aabb); + mRtAABBRaw[index++] = static_cast(aabb); } - FALCOR_ASSERT(offset == totalAABBCount); + FALCOR_ASSERT(index == totalAABBCount); flags |= Scene::UpdateFlags::CustomPrimitivesMoved; } @@ -991,7 +995,7 @@ namespace Falcor // Requires unordered access and will be in Non-Pixel Shader Resource state. if (mpRtAABBBuffer == nullptr || mpRtAABBBuffer->getElementCount() < (uint32_t)mRtAABBRaw.size()) { - mpRtAABBBuffer = Buffer::createStructured(mpDevice.get(), sizeof(RtAABB), (uint32_t)mRtAABBRaw.size(), Resource::BindFlags::ShaderResource | Resource::BindFlags::UnorderedAccess, Buffer::CpuAccess::None, mRtAABBRaw.data(), false); + mpRtAABBBuffer = Buffer::createStructured(mpDevice, sizeof(RtAABB), (uint32_t)mRtAABBRaw.size(), Resource::BindFlags::ShaderResource | Resource::BindFlags::UnorderedAccess, Buffer::CpuAccess::None, mRtAABBRaw.data(), false); mpRtAABBBuffer->setName("Scene::mpRtAABBBuffer"); // Bind the new buffer to the scene. @@ -1047,10 +1051,10 @@ namespace Falcor mDisplacement.updateTasks.push_back(task); } - mDisplacement.pAABBBuffer = Buffer::createStructured(mpDevice.get(), sizeof(RtAABB), AABBOffset, ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess); + mDisplacement.pAABBBuffer = Buffer::createStructured(mpDevice, sizeof(RtAABB), AABBOffset, ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess); FALCOR_ASSERT(mDisplacement.updateTasks.size() < std::numeric_limits::max()); - mDisplacement.pUpdateTasksBuffer = Buffer::createStructured(mpDevice.get(), (uint32_t)sizeof(DisplacementUpdateTask), (uint32_t)mDisplacement.updateTasks.size(), ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, mDisplacement.updateTasks.data()); + mDisplacement.pUpdateTasksBuffer = Buffer::createStructured(mpDevice, (uint32_t)sizeof(DisplacementUpdateTask), (uint32_t)mDisplacement.updateTasks.size(), ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, mDisplacement.updateTasks.data()); } FALCOR_ASSERT(!mDisplacement.updateTasks.empty()); @@ -1093,9 +1097,11 @@ namespace Falcor UpdateFlags updateFlags = UpdateFlags::None; if (!is_set(mGeometryTypes, GeometryTypeFlags::SDFGrid)) return updateFlags; + auto sdfGridsVar = mpSceneBlock->getRootVar()[kSDFGridsArrayName]; + for (uint32_t sdfGridID = 0; sdfGridID < mSDFGrids.size(); ++sdfGridID) { - SDFGrid::SharedPtr& pSDFGrid = mSDFGrids[sdfGridID]; + ref& pSDFGrid = mSDFGrids[sdfGridID]; SDFGrid::UpdateFlags sdfGridUpdateFlags = pSDFGrid->update(pRenderContext); if (is_set(sdfGridUpdateFlags, SDFGrid::UpdateFlags::AABBsChanged)) @@ -1111,7 +1117,7 @@ namespace Falcor if (is_set(sdfGridUpdateFlags, SDFGrid::UpdateFlags::BuffersReallocated)) { updateGeometryStats(); - pSDFGrid->setShaderData(mpSceneBlock[kSDFGridsArrayName][sdfGridID]); + pSDFGrid->setShaderData(sdfGridsVar[sdfGridID]); updateFlags |= Scene::UpdateFlags::SDFGeometryChanged; } } @@ -1128,12 +1134,14 @@ namespace Falcor // Update the procedural primitives metadata. if (forceUpdate || mCustomPrimitivesChanged) { + auto var = mpSceneBlock->getRootVar(); + // Update the custom primitives buffer. if (!mCustomPrimitiveDesc.empty()) { if (mpCustomPrimitivesBuffer == nullptr || mpCustomPrimitivesBuffer->getElementCount() < (uint32_t)mCustomPrimitiveDesc.size()) { - mpCustomPrimitivesBuffer = Buffer::createStructured(mpDevice.get(), mpSceneBlock[kCustomPrimitiveBufferName], (uint32_t)mCustomPrimitiveDesc.size(), Resource::BindFlags::ShaderResource, Buffer::CpuAccess::None, mCustomPrimitiveDesc.data(), false); + mpCustomPrimitivesBuffer = Buffer::createStructured(mpDevice, var[kCustomPrimitiveBufferName], (uint32_t)mCustomPrimitiveDesc.size(), Resource::BindFlags::ShaderResource, Buffer::CpuAccess::None, mCustomPrimitiveDesc.data(), false); mpCustomPrimitivesBuffer->setName("Scene::mpCustomPrimitivesBuffer"); // Bind the buffer to the scene. @@ -1152,7 +1160,6 @@ namespace Falcor uint32_t customPrimitiveInstanceOffset = getGeometryInstanceCount(); uint32_t customPrimitiveInstanceCount = getCustomPrimitiveCount(); - auto var = mpSceneBlock->getRootVar(); var["customPrimitiveInstanceOffset"] = customPrimitiveInstanceOffset; var["customPrimitiveInstanceCount"] = customPrimitiveInstanceCount; var["customPrimitiveAABBOffset"] = mCustomPrimitiveAABBOffset; @@ -1205,7 +1212,7 @@ namespace Falcor if (mpLightProfile) { mpLightProfile->bake(mpDevice->getRenderContext()); - mpLightProfile->setShaderData(mpSceneBlock[kLightProfile]); + mpLightProfile->setShaderData(mpSceneBlock->getRootVar()[kLightProfile]); } updateBounds(); @@ -1358,7 +1365,7 @@ namespace Falcor s.sdfGridMemoryInBytes = 0; - for (const SDFGrid::SharedPtr& pSDFGrid : mSDFGrids) + for (const ref& pSDFGrid : mSDFGrids) { s.sdfGridMemoryInBytes += pSDFGrid->getSize(); } @@ -1509,7 +1516,7 @@ namespace Falcor { if (!controller.isMatrixChanged(nodeID) && !force) return false; - rmcv::mat4 transform = controller.getGlobalMatrices()[nodeID.get()]; + float4x4 transform = controller.getGlobalMatrices()[nodeID.get()]; animatable.updateFromAnimation(transform); return true; } @@ -1580,7 +1587,7 @@ namespace Falcor if (combinedChanges != Light::Changes::None || forceUpdate) { - mpSceneBlock["lightCount"] = (uint32_t)mActiveLights.size(); + mpSceneBlock->getRootVar()["lightCount"] = (uint32_t)mActiveLights.size(); updateLightStats(); } @@ -1613,7 +1620,7 @@ namespace Falcor // Upload grids. if (forceUpdate) { - auto var = mpSceneBlock["grids"]; + auto var = mpSceneBlock->getRootVar()["grids"]; for (size_t i = 0; i < mGrids.size(); ++i) { mGrids[i]->setShaderData(var[i]); @@ -1634,8 +1641,8 @@ namespace Falcor const auto& densityGrid = pGridVolume->getDensityGrid(); if (densityGrid) { - data.transform = data.transform * densityGrid->getTransform(); - data.invTransform = densityGrid->getInvTransform() * data.invTransform; + data.transform = mul(data.transform, densityGrid->getTransform()); + data.invTransform = mul(densityGrid->getInvTransform(), data.invTransform); } mpGridVolumesBuffer->setElement(volumeIndex, data); } @@ -1643,7 +1650,7 @@ namespace Falcor volumeIndex++; } - mpSceneBlock["gridVolumeCount"] = (uint32_t)mGridVolumes.size(); + mpSceneBlock->getRootVar()["gridVolumeCount"] = (uint32_t)mGridVolumes.size(); UpdateFlags flags = UpdateFlags::None; if (is_set(combinedUpdates, GridVolume::UpdateFlags::TransformChanged)) flags |= UpdateFlags::GridVolumesMoved; @@ -1664,7 +1671,7 @@ namespace Falcor if (envMapChanges != EnvMap::Changes::None || mEnvMapChanged || forceUpdate) { if (envMapChanges != EnvMap::Changes::None) flags |= UpdateFlags::EnvMapPropertiesChanged; - mpEnvMap->setShaderData(mpSceneBlock[kEnvMap]); + mpEnvMap->setShaderData(mpSceneBlock->getRootVar()[kEnvMap]); } } mSceneStats.envMapMemoryInBytes = mpEnvMap ? mpEnvMap->getMemoryUsageInBytes() : 0; @@ -1743,7 +1750,7 @@ namespace Falcor Scene::UpdateFlags Scene::update(RenderContext* pRenderContext, double currentTime) { // Run scene update callback. - if (mUpdateCallback) mUpdateCallback(shared_from_this(), currentTime); + if (mUpdateCallback) mUpdateCallback(ref(this), currentTime); mUpdates = UpdateFlags::None; @@ -2200,7 +2207,7 @@ namespace Falcor return mRenderSettings.useGridVolumes && mGridVolumes.empty() == false; } - void Scene::setCamera(const Camera::SharedPtr& pCamera) + void Scene::setCamera(const ref& pCamera) { auto it = std::find(mCameras.begin(), mCameras.end(), pCamera); if (it != mCameras.end()) @@ -2253,7 +2260,7 @@ namespace Falcor void Scene::setCameraSpeed(float speed) { - mCameraSpeed = clamp(speed, 0.f, std::numeric_limits::max()); + mCameraSpeed = std::clamp(speed, 0.f, std::numeric_limits::max()); mpCamCtrl->setCameraSpeed(speed); } @@ -2427,7 +2434,7 @@ namespace Falcor return instanceIDs; } - Material::SharedPtr Scene::getGeometryMaterial(GlobalGeometryID geometryID) const + ref Scene::getGeometryMaterial(GlobalGeometryID geometryID) const { GlobalGeometryID::IntType geometryIdx = geometryID.get(); if (geometryIdx < mMeshDesc.size()) @@ -2545,7 +2552,7 @@ namespace Falcor } } - GridVolume::SharedPtr Scene::getGridVolumeByName(const std::string& name) const + ref Scene::getGridVolumeByName(const std::string& name) const { for (const auto& v : mGridVolumes) { @@ -2555,7 +2562,7 @@ namespace Falcor return nullptr; } - Light::SharedPtr Scene::getLightByName(const std::string& name) const + ref Scene::getLightByName(const std::string& name) const { for (const auto& l : mLights) { @@ -2597,7 +2604,7 @@ namespace Falcor if (drawMeshes.size() > 0) { DrawArgs draw; - draw.pBuffer = Buffer::create(mpDevice.get(), sizeof(drawMeshes[0]) * drawMeshes.size(), Resource::BindFlags::IndirectArg, Buffer::CpuAccess::None, drawMeshes.data()); + draw.pBuffer = Buffer::create(mpDevice, sizeof(drawMeshes[0]) * drawMeshes.size(), Resource::BindFlags::IndirectArg, Buffer::CpuAccess::None, drawMeshes.data()); draw.pBuffer->setName("Scene draw buffer"); FALCOR_ASSERT(drawMeshes.size() <= std::numeric_limits::max()); draw.count = (uint32_t)drawMeshes.size(); @@ -2679,9 +2686,9 @@ namespace Falcor if (!mMeshGroups.empty()) { FALCOR_ASSERT(mpMeshVao); - const VertexBufferLayout::SharedConstPtr& pVbLayout = mpMeshVao->getVertexLayout()->getBufferLayout(kStaticDataBufferIndex); - const Buffer::SharedPtr& pVb = mpMeshVao->getVertexBuffer(kStaticDataBufferIndex); - const Buffer::SharedPtr& pIb = mpMeshVao->getIndexBuffer(); + const ref& pVbLayout = mpMeshVao->getVertexLayout()->getBufferLayout(kStaticDataBufferIndex); + const ref& pVb = mpMeshVao->getVertexBuffer(kStaticDataBufferIndex); + const ref& pIb = mpMeshVao->getIndexBuffer(); const auto& globalMatrices = mpAnimationController->getGlobalMatrices(); // Normally static geometry is already pre-transformed to world space by the SceneBuilder, @@ -2694,12 +2701,12 @@ namespace Falcor { if (!mpBlasStaticWorldMatrices) { - std::vector transposedMatrices; + std::vector transposedMatrices; transposedMatrices.reserve(globalMatrices.size()); - for (const auto& m : globalMatrices) transposedMatrices.push_back(rmcv::transpose(m)); + for (const auto& m : globalMatrices) transposedMatrices.push_back(transpose(m)); uint32_t float4Count = (uint32_t)transposedMatrices.size() * 4; - mpBlasStaticWorldMatrices = Buffer::createStructured(mpDevice.get(), sizeof(float4), float4Count, Resource::BindFlags::ShaderResource, Buffer::CpuAccess::None, transposedMatrices.data(), false); + mpBlasStaticWorldMatrices = Buffer::createStructured(mpDevice, sizeof(float4), float4Count, Resource::BindFlags::ShaderResource, Buffer::CpuAccess::None, transposedMatrices.data(), false); mpBlasStaticWorldMatrices->setName("Scene::mpBlasStaticWorldMatrices"); // Transition the resource to non-pixel shader state as expected by DXR. @@ -2748,12 +2755,12 @@ namespace Falcor uint32_t matrixID = mGeometryInstanceData[instanceID].globalMatrixID; FALCOR_ASSERT(matrixID < globalMatrices.size()); - if (globalMatrices[matrixID] != rmcv::identity()) + if (globalMatrices[matrixID] != float4x4::identity()) { // Get the GPU address of the transform in row-major format. desc.content.triangles.transform3x4 = getStaticMatricesBuffer()->getGpuAddress() + matrixID * 64ull; - if (rmcv::determinant(globalMatrices[matrixID]) < 0.f) frontFaceCW = !frontFaceCW; + if (determinant(globalMatrices[matrixID]) < 0.f) frontFaceCW = !frontFaceCW; } } triangleWindings |= frontFaceCW ? 1 : 2; @@ -2868,7 +2875,7 @@ namespace Falcor mSDFGridConfig.implementation == SDFGrid::Type::SparseVoxelOctree) { // All ND SDF Grid instances share the same BLAS and AABB buffer. - const SDFGrid::SharedPtr& pSDFGrid = mSDFGrids.back(); + const ref& pSDFGrid = mSDFGrids.back(); auto& blas = mBlasData[blasDataIndex]; blas.hasProceduralPrimitives = true; @@ -2888,7 +2895,7 @@ namespace Falcor { for (uint32_t s = 0; s < mSDFGrids.size(); s++) { - const SDFGrid::SharedPtr& pSDFGrid = mSDFGrids[s]; + const ref& pSDFGrid = mSDFGrids[s]; auto& blas = mBlasData[blasDataIndex + s]; blas.hasProceduralPrimitives = true; @@ -3077,16 +3084,16 @@ namespace Falcor // Add barriers for the VB and IB which will be accessed by the build. if (mpMeshVao) { - const Buffer::SharedPtr& pVb = mpMeshVao->getVertexBuffer(kStaticDataBufferIndex); - const Buffer::SharedPtr& pIb = mpMeshVao->getIndexBuffer(); + const ref& pVb = mpMeshVao->getVertexBuffer(kStaticDataBufferIndex); + const ref& pIb = mpMeshVao->getIndexBuffer(); pRenderContext->resourceBarrier(pVb.get(), Resource::State::NonPixelShader); if (pIb) pRenderContext->resourceBarrier(pIb.get(), Resource::State::NonPixelShader); } if (mpCurveVao) { - const Buffer::SharedPtr& pCurveVb = mpCurveVao->getVertexBuffer(kStaticDataBufferIndex); - const Buffer::SharedPtr& pCurveIb = mpCurveVao->getIndexBuffer(); + const ref& pCurveVb = mpCurveVao->getVertexBuffer(kStaticDataBufferIndex); + const ref& pCurveIb = mpCurveVao->getIndexBuffer(); pRenderContext->resourceBarrier(pCurveVb.get(), Resource::State::NonPixelShader); pRenderContext->resourceBarrier(pCurveIb.get(), Resource::State::NonPixelShader); } @@ -3101,7 +3108,7 @@ namespace Falcor else if (mSDFGridConfig.implementation == SDFGrid::Type::SparseVoxelSet || mSDFGridConfig.implementation == SDFGrid::Type::SparseBrickSet) { - for (const SDFGrid::SharedPtr& pSDFGrid : mSDFGrids) + for (const ref& pSDFGrid : mSDFGrids) { pRenderContext->resourceBarrier(pSDFGrid->getAABBBuffer().get(), Resource::State::NonPixelShader); } @@ -3165,23 +3172,23 @@ namespace Falcor // TODO: Save memory by reducing the scratch buffer to the minimum required for the dynamic objects. if (mpBlasScratch == nullptr || mpBlasScratch->getSize() < scratchByteSize) { - mpBlasScratch = Buffer::create(mpDevice.get(), scratchByteSize, Buffer::BindFlags::UnorderedAccess, Buffer::CpuAccess::None); + mpBlasScratch = Buffer::create(mpDevice, scratchByteSize, Buffer::BindFlags::UnorderedAccess, Buffer::CpuAccess::None); mpBlasScratch->setName("Scene::mpBlasScratch"); } - Buffer::SharedPtr pResultBuffer = Buffer::create(mpDevice.get(), resultByteSize, Buffer::BindFlags::AccelerationStructure, Buffer::CpuAccess::None); + ref pResultBuffer = Buffer::create(mpDevice, resultByteSize, Buffer::BindFlags::AccelerationStructure, Buffer::CpuAccess::None); FALCOR_ASSERT(pResultBuffer && mpBlasScratch); // Create post-build info pool for readback. RtAccelerationStructurePostBuildInfoPool::Desc compactedSizeInfoPoolDesc; compactedSizeInfoPoolDesc.queryType = RtAccelerationStructurePostBuildInfoQueryType::CompactedSize; compactedSizeInfoPoolDesc.elementCount = (uint32_t)maxBlasCount; - RtAccelerationStructurePostBuildInfoPool::SharedPtr compactedSizeInfoPool = RtAccelerationStructurePostBuildInfoPool::create(mpDevice.get(), compactedSizeInfoPoolDesc); + ref compactedSizeInfoPool = RtAccelerationStructurePostBuildInfoPool::create(mpDevice.get(), compactedSizeInfoPoolDesc); RtAccelerationStructurePostBuildInfoPool::Desc currentSizeInfoPoolDesc; currentSizeInfoPoolDesc.queryType = RtAccelerationStructurePostBuildInfoQueryType::CurrentSize; currentSizeInfoPoolDesc.elementCount = (uint32_t)maxBlasCount; - RtAccelerationStructurePostBuildInfoPool::SharedPtr currentSizeInfoPool = RtAccelerationStructurePostBuildInfoPool::create(mpDevice.get(), currentSizeInfoPoolDesc); + ref currentSizeInfoPool = RtAccelerationStructurePostBuildInfoPool::create(mpDevice.get(), currentSizeInfoPoolDesc); bool hasDynamicGeometry = false; bool hasProceduralPrimitives = false; @@ -3194,7 +3201,7 @@ namespace Falcor auto& group = mBlasGroups[blasGroupIndex]; // Allocate array to hold intermediate blases for the group. - std::vector intermediateBlases(group.blasIndices.size()); + std::vector> intermediateBlases(group.blasIndices.size()); // Insert barriers. The buffers are now ready to be written. pRenderContext->uavBarrier(pResultBuffer.get()); @@ -3217,7 +3224,7 @@ namespace Falcor RtAccelerationStructure::Desc createDesc = {}; createDesc.setBuffer(pResultBuffer, blas.resultByteOffset, blas.resultByteSize); createDesc.setKind(RtAccelerationStructureKind::BottomLevel); - auto blasObject = RtAccelerationStructure::create(mpDevice.get(), createDesc); + auto blasObject = RtAccelerationStructure::create(mpDevice, createDesc); intermediateBlases[i] = blasObject; RtAccelerationStructure::BuildDesc asDesc = {}; @@ -3281,7 +3288,7 @@ namespace Falcor auto& pBlas = group.pBlas; if (pBlas == nullptr || pBlas->getSize() < group.finalByteSize) { - pBlas = Buffer::create(mpDevice.get(), group.finalByteSize, Buffer::BindFlags::AccelerationStructure, Buffer::CpuAccess::None); + pBlas = Buffer::create(mpDevice, group.finalByteSize, Buffer::BindFlags::AccelerationStructure, Buffer::CpuAccess::None); pBlas->setName("Scene::mBlasGroups[" + std::to_string(blasGroupIndex) + "].pBlas"); } else @@ -3303,7 +3310,7 @@ namespace Falcor RtAccelerationStructure::Desc blasDesc = {}; blasDesc.setBuffer(pBlas, blas.blasByteOffset, blas.blasByteSize); blasDesc.setKind(RtAccelerationStructureKind::BottomLevel); - mBlasObjects[blasId] = RtAccelerationStructure::create(mpDevice.get(), blasDesc); + mBlasObjects[blasId] = RtAccelerationStructure::create(mpDevice, blasDesc); pRenderContext->copyAccelerationStructure( mBlasObjects[blasId].get(), @@ -3410,9 +3417,9 @@ namespace Falcor // We expect all meshes in a group to have identical triangle winding. Verify that assumption here. FALCOR_ASSERT(!meshList.empty()); const bool frontFaceCW = mMeshDesc[meshList[0].get()].isFrontFaceCW(); - for (size_t i = 1; i < meshList.size(); i++) + for (size_t j = 1; j < meshList.size(); j++) { - FALCOR_ASSERT(mMeshDesc[meshList[i].get()].isFrontFaceCW() == frontFaceCW); + FALCOR_ASSERT(mMeshDesc[meshList[j].get()].isFrontFaceCW() == frontFaceCW); } // Set the triangle winding for the instance if it differs from the default. @@ -3450,7 +3457,7 @@ namespace Falcor desc.instanceID = instanceID; instanceID += (uint32_t)meshList.size(); - rmcv::mat4 transform4x4 = rmcv::identity(); + float4x4 transform4x4 = float4x4::identity(); if (!isStatic) { // For non-static meshes, the matrices for all meshes in an instance are guaranteed to be the same. @@ -3582,7 +3589,7 @@ namespace Falcor instanceContributionToHitGroupIndex += rayTypeCount * (uint32_t)mCustomPrimitiveDesc.size(); - rmcv::mat4 identityMat = rmcv::identity(); + float4x4 identityMat = float4x4::identity(); std::memcpy(desc.transform, &identityMat, sizeof(desc.transform)); instanceDescs.push_back(desc); } @@ -3629,7 +3636,7 @@ namespace Falcor { // Prebuild mTlasPrebuildInfo = RtAccelerationStructure::getPrebuildInfo(mpDevice.get(), inputs); - mpTlasScratch = Buffer::create(mpDevice.get(), mTlasPrebuildInfo.scratchDataSize, Buffer::BindFlags::UnorderedAccess, Buffer::CpuAccess::None); + mpTlasScratch = Buffer::create(mpDevice, mTlasPrebuildInfo.scratchDataSize, Buffer::BindFlags::UnorderedAccess, Buffer::CpuAccess::None); mpTlasScratch->setName("Scene::mpTlasScratch"); // #SCENE This isn't guaranteed according to the spec, and the scratch buffer being stored should be sized differently depending on update mode @@ -3647,7 +3654,7 @@ namespace Falcor // Allocate a new buffer for the TLAS only if the existing buffer isn't big enough. if (!tlas.pTlasBuffer || tlas.pTlasBuffer->getSize() < mTlasPrebuildInfo.resultDataMaxSize) { - tlas.pTlasBuffer = Buffer::create(mpDevice.get(), mTlasPrebuildInfo.resultDataMaxSize, Buffer::BindFlags::AccelerationStructure, Buffer::CpuAccess::None); + tlas.pTlasBuffer = Buffer::create(mpDevice, mTlasPrebuildInfo.resultDataMaxSize, Buffer::BindFlags::AccelerationStructure, Buffer::CpuAccess::None); tlas.pTlasBuffer->setName("Scene TLAS buffer"); } } @@ -3656,7 +3663,7 @@ namespace Falcor // Allocate a new buffer for the TLAS instance desc input only if the existing buffer isn't big enough. if (!tlas.pInstanceDescs || tlas.pInstanceDescs->getSize() < mInstanceDescs.size() * sizeof(RtInstanceDesc)) { - tlas.pInstanceDescs = Buffer::create(mpDevice.get(), (uint32_t)mInstanceDescs.size() * sizeof(RtInstanceDesc), Buffer::BindFlags::None, Buffer::CpuAccess::Write, mInstanceDescs.data()); + tlas.pInstanceDescs = Buffer::create(mpDevice, (uint32_t)mInstanceDescs.size() * sizeof(RtInstanceDesc), Buffer::BindFlags::None, Buffer::CpuAccess::Write, mInstanceDescs.data()); tlas.pInstanceDescs->setName("Scene instance descs buffer"); } else @@ -3668,7 +3675,7 @@ namespace Falcor RtAccelerationStructure::Desc asCreateDesc = {}; asCreateDesc.setKind(RtAccelerationStructureKind::TopLevel); asCreateDesc.setBuffer(tlas.pTlasBuffer, 0, mTlasPrebuildInfo.resultDataMaxSize); - tlas.pTlasObject = RtAccelerationStructure::create(mpDevice.get(), asCreateDesc); + tlas.pTlasObject = RtAccelerationStructure::create(mpDevice, asCreateDesc); } // Else update instance descs and barrier TLAS buffers else @@ -3736,10 +3743,10 @@ namespace Falcor // Bind TLAS. FALCOR_ASSERT(tlasIt != mTlasCache.end() && tlasIt->second.pTlasObject) - mpSceneBlock["rtAccel"].setAccelerationStructure(tlasIt->second.pTlasObject); + mpSceneBlock->getRootVar()["rtAccel"].setAccelerationStructure(tlasIt->second.pTlasObject); // Bind Scene parameter block. - getCamera()->setShaderData(mpSceneBlock[kCamera]); // TODO REMOVE: Shouldn't be needed anymore? + getCamera()->setShaderData(mpSceneBlock->getRootVar()[kCamera]); // TODO REMOVE: Shouldn't be needed anymore? var[kParameterBlockName] = mpSceneBlock; } @@ -3782,7 +3789,7 @@ namespace Falcor inputs.descCount = 0; inputs.flags = RtAccelerationStructureBuildFlags::None; - RtAccelerationStructurePrebuildInfo prebuildInfo = RtAccelerationStructure::getPrebuildInfo(pDevice, inputs); + RtAccelerationStructurePrebuildInfo prebuildInfo = RtAccelerationStructure::getPrebuildInfo(pDevice.get(), inputs); auto pScratch = Buffer::create(pDevice, prebuildInfo.scratchDataSize, Buffer::BindFlags::UnorderedAccess, Buffer::CpuAccess::None); auto pTlasBuffer = Buffer::create(pDevice, prebuildInfo.resultDataMaxSize, Buffer::BindFlags::AccelerationStructure, Buffer::CpuAccess::None); @@ -3790,7 +3797,7 @@ namespace Falcor RtAccelerationStructure::Desc createDesc = {}; createDesc.setKind(RtAccelerationStructureKind::TopLevel); createDesc.setBuffer(pTlasBuffer, 0, prebuildInfo.resultDataMaxSize); - RtAccelerationStructure::SharedPtr tlasObject = RtAccelerationStructure::create(pDevice, createDesc); + ref tlasObject = RtAccelerationStructure::create(pDevice, createDesc); RtAccelerationStructure::BuildDesc asDesc = {}; asDesc.inputs = inputs; @@ -3802,9 +3809,10 @@ namespace Falcor Program::Desc desc; desc.addShaderLibrary("Scene/NullTrace.cs.slang").csEntry("main").setShaderModel("6_5"); - auto pass = ComputePass::create(pDevice->shared_from_this(), desc); - pass["gOutput"] = Texture::create2D(pDevice, dim.x, dim.y, ResourceFormat::R8Uint, 1, 1, nullptr, ResourceBindFlags::UnorderedAccess); - pass["gTlas"].setAccelerationStructure(tlasObject); + auto pass = ComputePass::create(pDevice, desc); + auto var = pass->getRootVar(); + var["gOutput"] = Texture::create2D(pDevice, dim.x, dim.y, ResourceFormat::R8Uint, 1, 1, nullptr, ResourceBindFlags::UnorderedAccess); + var["gTlas"].setAccelerationStructure(tlasObject); for (size_t i = 0; i < 100; i++) { @@ -3812,7 +3820,7 @@ namespace Falcor } } - void Scene::setEnvMap(EnvMap::SharedPtr pEnvMap) + void Scene::setEnvMap(ref pEnvMap) { if (mpEnvMap == pEnvMap) return; mpEnvMap = pEnvMap; @@ -3850,14 +3858,14 @@ namespace Falcor switch (type) { case CameraControllerType::FirstPerson: - mpCamCtrl = FirstPersonCameraController::create(camera); + mpCamCtrl = std::make_unique(camera); break; case CameraControllerType::Orbiter: - mpCamCtrl = OrbiterCameraController::create(camera); + mpCamCtrl = std::make_unique(camera); ((OrbiterCameraController*)mpCamCtrl.get())->setModelParams(mSceneBB.center(), mSceneBB.radius(), 3.5f); break; case CameraControllerType::SixDOF: - mpCamCtrl = SixDoFCameraController::create(camera); + mpCamCtrl = std::make_unique(camera); break; default: FALCOR_UNREACHABLE(); @@ -3982,7 +3990,7 @@ namespace Falcor return c; } - void Scene::updateNodeTransform(uint32_t nodeID, const rmcv::mat4& transform) + void Scene::updateNodeTransform(uint32_t nodeID, const float4x4& transform) { FALCOR_ASSERT(nodeID < mSceneGraph.size()); @@ -3991,87 +3999,87 @@ namespace Falcor mpAnimationController->setNodeEdited(nodeID); } - pybind11::dict Scene::SceneStats::toPython() const + inline pybind11::dict toPython(const Scene::SceneStats& stats) { pybind11::dict d; // Geometry stats - d["meshCount"] = meshCount; - d["meshInstanceCount"] = meshInstanceCount; - d["meshInstanceOpaqueCount"] = meshInstanceOpaqueCount; - d["transformCount"] = transformCount; - d["uniqueTriangleCount"] = uniqueTriangleCount; - d["uniqueVertexCount"] = uniqueVertexCount; - d["instancedTriangleCount"] = instancedTriangleCount; - d["instancedVertexCount"] = instancedVertexCount; - d["indexMemoryInBytes"] = indexMemoryInBytes; - d["vertexMemoryInBytes"] = vertexMemoryInBytes; - d["geometryMemoryInBytes"] = geometryMemoryInBytes; - d["animationMemoryInBytes"] = animationMemoryInBytes; + d["meshCount"] = stats.meshCount; + d["meshInstanceCount"] = stats.meshInstanceCount; + d["meshInstanceOpaqueCount"] = stats.meshInstanceOpaqueCount; + d["transformCount"] = stats.transformCount; + d["uniqueTriangleCount"] = stats.uniqueTriangleCount; + d["uniqueVertexCount"] = stats.uniqueVertexCount; + d["instancedTriangleCount"] = stats.instancedTriangleCount; + d["instancedVertexCount"] = stats.instancedVertexCount; + d["indexMemoryInBytes"] = stats.indexMemoryInBytes; + d["vertexMemoryInBytes"] = stats.vertexMemoryInBytes; + d["geometryMemoryInBytes"] = stats.geometryMemoryInBytes; + d["animationMemoryInBytes"] = stats.animationMemoryInBytes; // Curve stats - d["curveCount"] = curveCount; - d["curveInstanceCount"] = curveInstanceCount; - d["uniqueCurveSegmentCount"] = uniqueCurveSegmentCount; - d["uniqueCurvePointCount"] = uniqueCurvePointCount; - d["instancedCurveSegmentCount"] = instancedCurveSegmentCount; - d["instancedCurvePointCount"] = instancedCurvePointCount; - d["curveIndexMemoryInBytes"] = curveIndexMemoryInBytes; - d["curveVertexMemoryInBytes"] = curveVertexMemoryInBytes; + d["curveCount"] = stats.curveCount; + d["curveInstanceCount"] = stats.curveInstanceCount; + d["uniqueCurveSegmentCount"] = stats.uniqueCurveSegmentCount; + d["uniqueCurvePointCount"] = stats.uniqueCurvePointCount; + d["instancedCurveSegmentCount"] = stats.instancedCurveSegmentCount; + d["instancedCurvePointCount"] = stats.instancedCurvePointCount; + d["curveIndexMemoryInBytes"] = stats.curveIndexMemoryInBytes; + d["curveVertexMemoryInBytes"] = stats.curveVertexMemoryInBytes; // SDF grid stats - d["sdfGridCount"] = sdfGridCount; - d["sdfGridDescriptorCount"] = sdfGridDescriptorCount; - d["sdfGridInstancesCount"] = sdfGridInstancesCount; - d["sdfGridMemoryInBytes"] = sdfGridMemoryInBytes; + d["sdfGridCount"] = stats.sdfGridCount; + d["sdfGridDescriptorCount"] = stats.sdfGridDescriptorCount; + d["sdfGridInstancesCount"] = stats.sdfGridInstancesCount; + d["sdfGridMemoryInBytes"] = stats.sdfGridMemoryInBytes; // Custom primitive stats - d["customPrimitiveCount"] = customPrimitiveCount; + d["customPrimitiveCount"] = stats.customPrimitiveCount; // Material stats - d["materialCount"] = materials.materialCount; - d["materialOpaqueCount"] = materials.materialOpaqueCount; - d["materialMemoryInBytes"] = materials.materialMemoryInBytes; - d["textureCount"] = materials.textureCount; - d["textureCompressedCount"] = materials.textureCompressedCount; - d["textureTexelCount"] = materials.textureTexelCount; - d["textureTexelChannelCount"] = materials.textureTexelChannelCount; - d["textureMemoryInBytes"] = materials.textureMemoryInBytes; + d["materialCount"] = stats.materials.materialCount; + d["materialOpaqueCount"] = stats.materials.materialOpaqueCount; + d["materialMemoryInBytes"] = stats.materials.materialMemoryInBytes; + d["textureCount"] = stats.materials.textureCount; + d["textureCompressedCount"] = stats.materials.textureCompressedCount; + d["textureTexelCount"] = stats.materials.textureTexelCount; + d["textureTexelChannelCount"] = stats.materials.textureTexelChannelCount; + d["textureMemoryInBytes"] = stats.materials.textureMemoryInBytes; // Raytracing stats - d["blasGroupCount"] = blasGroupCount; - d["blasCount"] = blasCount; - d["blasCompactedCount"] = blasCompactedCount; - d["blasOpaqueCount"] = blasOpaqueCount; - d["blasGeometryCount"] = blasGeometryCount; - d["blasOpaqueGeometryCount"] = blasOpaqueGeometryCount; - d["blasMemoryInBytes"] = blasMemoryInBytes; - d["blasScratchMemoryInBytes"] = blasScratchMemoryInBytes; - d["tlasCount"] = tlasCount; - d["tlasMemoryInBytes"] = tlasMemoryInBytes; - d["tlasScratchMemoryInBytes"] = tlasScratchMemoryInBytes; + d["blasGroupCount"] = stats.blasGroupCount; + d["blasCount"] = stats.blasCount; + d["blasCompactedCount"] = stats.blasCompactedCount; + d["blasOpaqueCount"] = stats.blasOpaqueCount; + d["blasGeometryCount"] = stats.blasGeometryCount; + d["blasOpaqueGeometryCount"] = stats.blasOpaqueGeometryCount; + d["blasMemoryInBytes"] = stats.blasMemoryInBytes; + d["blasScratchMemoryInBytes"] = stats.blasScratchMemoryInBytes; + d["tlasCount"] = stats.tlasCount; + d["tlasMemoryInBytes"] = stats.tlasMemoryInBytes; + d["tlasScratchMemoryInBytes"] = stats.tlasScratchMemoryInBytes; // Light stats - d["activeLightCount"] = activeLightCount; - d["totalLightCount"] = totalLightCount; - d["pointLightCount"] = pointLightCount; - d["directionalLightCount"] = directionalLightCount; - d["rectLightCount"] = rectLightCount; - d["discLightCount"] = discLightCount; - d["sphereLightCount"] = sphereLightCount; - d["distantLightCount"] = distantLightCount; - d["lightsMemoryInBytes"] = lightsMemoryInBytes; - d["envMapMemoryInBytes"] = envMapMemoryInBytes; - d["emissiveMemoryInBytes"] = emissiveMemoryInBytes; + d["activeLightCount"] = stats.activeLightCount; + d["totalLightCount"] = stats.totalLightCount; + d["pointLightCount"] = stats.pointLightCount; + d["directionalLightCount"] = stats.directionalLightCount; + d["rectLightCount"] = stats.rectLightCount; + d["discLightCount"] = stats.discLightCount; + d["sphereLightCount"] = stats.sphereLightCount; + d["distantLightCount"] = stats.distantLightCount; + d["lightsMemoryInBytes"] = stats.lightsMemoryInBytes; + d["envMapMemoryInBytes"] = stats.envMapMemoryInBytes; + d["emissiveMemoryInBytes"] = stats.emissiveMemoryInBytes; // Volume stats - d["gridVolumeCount"] = gridVolumeCount; - d["gridVolumeMemoryInBytes"] = gridVolumeMemoryInBytes; + d["gridVolumeCount"] = stats.gridVolumeCount; + d["gridVolumeMemoryInBytes"] = stats.gridVolumeMemoryInBytes; // Grid stats - d["gridCount"] = gridCount; - d["gridVoxelCount"] = gridVoxelCount; - d["gridMemoryInBytes"] = gridMemoryInBytes; + d["gridCount"] = stats.gridCount; + d["gridVoxelCount"] = stats.gridVoxelCount; + d["gridMemoryInBytes"] = stats.gridMemoryInBytes; return d; } @@ -4080,9 +4088,30 @@ namespace Falcor { using namespace pybind11::literals; - pybind11::class_ scene(m, "Scene"); + FALCOR_SCRIPT_BINDING_DEPENDENCY(Material) + FALCOR_SCRIPT_BINDING_DEPENDENCY(Rectangle) + FALCOR_SCRIPT_BINDING_DEPENDENCY(Light) + FALCOR_SCRIPT_BINDING_DEPENDENCY(GridVolume) + FALCOR_SCRIPT_BINDING_DEPENDENCY(Animation) + FALCOR_SCRIPT_BINDING_DEPENDENCY(AABB) + FALCOR_SCRIPT_BINDING_DEPENDENCY(Camera) + FALCOR_SCRIPT_BINDING_DEPENDENCY(EnvMap) + FALCOR_SCRIPT_BINDING_DEPENDENCY(SDFGrid) + + // RenderSettings + ScriptBindings::SerializableStruct renderSettings(m, "SceneRenderSettings"); +#define field(f_) field(#f_, &Scene::RenderSettings::f_) + renderSettings.field(useEnvLight); + renderSettings.field(useAnalyticLights); + renderSettings.field(useEmissiveLights); + renderSettings.field(useGridVolumes); + renderSettings.field(diffuseAlbedoMultiplier); +#undef field + + // Scene + pybind11::class_> scene(m, "Scene"); - scene.def_property_readonly(kStats.c_str(), [](const Scene* pScene) { return pScene->getSceneStats().toPython(); }); + scene.def_property_readonly(kStats.c_str(), [](const Scene* pScene) { return toPython(pScene->getSceneStats()); }); scene.def_property_readonly(kBounds.c_str(), &Scene::getSceneBounds, pybind11::return_value_policy::copy); scene.def_property(kCamera.c_str(), &Scene::getCamera, &Scene::setCamera); scene.def_property(kEnvMap.c_str(), &Scene::getEnvMap, &Scene::setEnvMap); @@ -4114,7 +4143,7 @@ namespace Falcor scene.def(kGetMaterial.c_str(), &Scene::getMaterial, "index"_a); scene.def(kGetMaterial.c_str(), &Scene::getMaterialByName, "name"_a); scene.def("addMaterial", &Scene::addMaterial, "material"_a); - scene.def("getGeometryIDsForMaterial", [](const Scene* scene, const Material::SharedPtr& pMaterial) + scene.def("getGeometryIDsForMaterial", [](const Scene* scene, const ref& pMaterial) { return scene->getGeometryIDs(pMaterial.get()); }, "material"_a); @@ -4125,15 +4154,5 @@ namespace Falcor scene.def(kAddViewpoint.c_str(), pybind11::overload_cast(&Scene::addViewpoint), "position"_a, "target"_a, "up"_a, "cameraIndex"_a = 0); // add specified viewpoint scene.def(kRemoveViewpoint.c_str(), &Scene::removeViewpoint); // remove the selected viewpoint scene.def(kSelectViewpoint.c_str(), &Scene::selectViewpoint, "index"_a); // select a viewpoint by index - - // RenderSettings - ScriptBindings::SerializableStruct renderSettings(m, "SceneRenderSettings"); -#define field(f_) field(#f_, &Scene::RenderSettings::f_) - renderSettings.field(useEnvLight); - renderSettings.field(useAnalyticLights); - renderSettings.field(useEmissiveLights); - renderSettings.field(useGridVolumes); - renderSettings.field(diffuseAlbedoMultiplier); -#undef field } } diff --git a/Source/Falcor/Scene/Scene.h b/Source/Falcor/Scene/Scene.h index b2dfb3d2a..587509a94 100644 --- a/Source/Falcor/Scene/Scene.h +++ b/Source/Falcor/Scene/Scene.h @@ -44,6 +44,7 @@ #include "SDFs/SDFGrid.h" #include "Core/Macros.h" +#include "Core/Object.h" #include "Core/API/VAO.h" #include "Core/API/RtAccelerationStructure.h" #include "Utils/Math/AABB.h" @@ -61,8 +62,6 @@ #include #include -#include "Utils/Math/Matrix/Matrix.h" - namespace Falcor { struct MouseEvent; @@ -107,19 +106,18 @@ namespace Falcor - "InstanceID() + GeometryIndex()" is used for indexing into GeometryInstanceData. - This is wrapped in getGeometryInstanceID() in Raytracing.slang. */ - class FALCOR_API Scene : public std::enable_shared_from_this + class FALCOR_API Scene : public Object { public: - using SharedPtr = std::shared_ptr; - using GeometryType = GeometryType; - using GeometryTypeFlags = GeometryTypeFlags; + using GeometryType = ::Falcor::GeometryType; + using GeometryTypeFlags = ::Falcor::GeometryTypeFlags; using UpDirection = CameraController::UpDirection; - using UpdateCallback = std::function; + using UpdateCallback = std::function& pScene, double currentTime)>; - static const uint32_t kMaxBonesPerVertex = 4; - static const uint32_t kInvalidAttributeIndex = -1; + static constexpr uint32_t kMaxBonesPerVertex = 4; + static constexpr uint32_t kInvalidAttributeIndex = -1; /** Flags indicating if and what was updated in the scene. */ @@ -292,12 +290,12 @@ namespace Falcor struct Node { Node() = default; - Node(const std::string& n, NodeID p, const rmcv::mat4& t, const rmcv::mat4& mb, const rmcv::mat4& l2b) : name(n), parent(p), transform(t), meshBind(mb), localToBindSpace(l2b) {}; + Node(const std::string& n, NodeID p, const float4x4& t, const float4x4& mb, const float4x4& l2b) : name(n), parent(p), transform(t), meshBind(mb), localToBindSpace(l2b) {}; std::string name; NodeID parent{ NodeID::Invalid() }; - rmcv::mat4 transform; ///< The node's transformation matrix. - rmcv::mat4 meshBind; ///< For skinned meshes. Mesh world space transform at bind time. - rmcv::mat4 localToBindSpace; ///< For bones. Skeleton to bind space transformation. AKA the inverse-bind transform. + float4x4 transform; ///< The node's transformation matrix. + float4x4 meshBind; ///< For skinned meshes. Mesh world space transform at bind time. + float4x4 localToBindSpace; ///< For bones. Skeleton to bind space transformation. AKA the inverse-bind transform. }; /** Full set of required data to create a scene object. @@ -307,17 +305,17 @@ namespace Falcor { std::filesystem::path path; ///< Path of the asset file the scene was loaded from. RenderSettings renderSettings; ///< Render settings. - std::vector cameras; ///< List of cameras. + std::vector> cameras; ///< List of cameras. uint32_t selectedCamera = 0; ///< Index of selected camera. float cameraSpeed = 1.f; ///< Camera speed. - std::vector lights; ///< List of light sources. - MaterialSystem::SharedPtr pMaterials; ///< Material system. This holds data and resources for all materials. - std::vector gridVolumes; ///< List of grid volumes. - std::vector grids; ///< List of grids. - EnvMap::SharedPtr pEnvMap; ///< Environment map. - LightProfile::SharedPtr pLightProfile; ///< DEMO21: Global light profile. + std::vector> lights; ///< List of light sources. + std::unique_ptr pMaterials; ///< Material system. This holds data and resources for all materials. + std::vector> gridVolumes; ///< List of grid volumes. + std::vector> grids; ///< List of grids. + ref pEnvMap; ///< Environment map. + ref pLightProfile; ///< DEMO21: Global light profile. std::vector sceneGraph; ///< Scene graph nodes. - std::vector animations; ///< List of animations. + std::vector> animations; ///< List of animations. Metadata metadata; ///< Scene meadata. // Mesh data @@ -349,7 +347,7 @@ namespace Falcor std::vector cachedCurves; ///< Vertex cache for dynamic (vertex animated) curves. // SDF grid data - std::vector sdfGrids; ///< List of SDF grids. + std::vector> sdfGrids; ///< List of SDF grids. std::vector sdfGridDesc; ///< List of SDF grid descriptors. std::vector sdfGridInstances; ///< List of SDG grid instances. uint32_t sdfGridMaxLODCount = 0; ///< The max LOD count of any SDF grid. @@ -444,10 +442,6 @@ namespace Falcor lightsMemoryInBytes + envMapMemoryInBytes + emissiveMemoryInBytes + gridVolumeMemoryInBytes + gridMemoryInBytes; } - - /** Convert to python dict. - */ - pybind11::dict toPython() const; }; /** Return list of file extensions filters for all supported file formats. @@ -459,18 +453,18 @@ namespace Falcor \param[in] path Import the scene from this file path. \return Scene object, or throws an ImporterError if import went wrong. */ - static SharedPtr create(std::shared_ptr pDevice, const std::filesystem::path& path, const Settings& settings = Settings()); + static ref create(ref pDevice, const std::filesystem::path& path, const Settings& settings = Settings()); /** Create scene from in-memory representation. \param[in] pDevice GPU device. \param[in] sceneData All scene data. \return Scene object or throws on error. */ - static SharedPtr create(std::shared_ptr pDevice, SceneData&& sceneData); + static ref create(ref pDevice, SceneData&& sceneData); /** Return the associated GPU device. */ - const std::shared_ptr& getDevice() const { return mpDevice; } + const ref& getDevice() const { return mpDevice; } /** Get scene defines. These defines must be set on all programs that access the scene. @@ -545,7 +539,7 @@ namespace Falcor /** Access the scene's currently selected camera to change properties or to use elsewhere. */ - const Camera::SharedPtr& getCamera() { return mCameras[mSelectedCamera]; } + const ref& getCamera() { return mCameras[mSelectedCamera]; } /** Get the camera bounds */ @@ -557,11 +551,11 @@ namespace Falcor /** Get a list of all cameras in the scene. */ - const std::vector& getCameras() { return mCameras; }; + const std::vector>& getCameras() { return mCameras; }; /** Select a different camera to use. The camera must already exist in the scene. */ - void setCamera(const Camera::SharedPtr& pCamera); + void setCamera(const ref& pCamera); /** Set the currently selected camera's aspect ratio. */ @@ -708,7 +702,7 @@ namespace Falcor \param[in] geometryID Global geometry ID. \return The material or nullptr if geometry has no material. */ - Material::SharedPtr getGeometryMaterial(GlobalGeometryID geometryID) const; + ref getGeometryMaterial(GlobalGeometryID geometryID) const; /** Get the number of triangle meshes. */ @@ -756,7 +750,7 @@ namespace Falcor /** Get an SDF grid. */ - const SDFGrid::SharedPtr& getSDFGrid(SdfGridID sdfGridID) const { return mSDFGrids[mSDFGridDesc[sdfGridID.get()].sdfGridID.get()]; } + const ref& getSDFGrid(SdfGridID sdfGridID) const { return mSDFGrids[mSDFGridDesc[sdfGridID.get()].sdfGridID.get()]; } /** Get the number of SDF grid geometries. */ @@ -776,7 +770,7 @@ namespace Falcor /** Updates a node in the graph. */ - void updateNodeTransform(uint32_t nodeID, const rmcv::mat4& transform); + void updateNodeTransform(uint32_t nodeID, const float4x4& transform); /** Get the number of custom primitives. */ @@ -831,11 +825,11 @@ namespace Falcor /** Get the material system. */ - const MaterialSystem::SharedPtr& getMaterialSystem() const { return mpMaterials; } + MaterialSystem& getMaterialSystem() const { return *mpMaterials; } /** Get a list of all materials in the scene. */ - const std::vector& getMaterials() const { return mpMaterials->getMaterials(); } + const std::vector>& getMaterials() const { return mpMaterials->getMaterials(); } /** Get the total number of materials in the scene. */ @@ -847,29 +841,29 @@ namespace Falcor /** Get a material. */ - const Material::SharedPtr& getMaterial(MaterialID materialID) const { return mpMaterials->getMaterial(materialID); } + const ref& getMaterial(MaterialID materialID) const { return mpMaterials->getMaterial(materialID); } /** Get a material by name. */ - Material::SharedPtr getMaterialByName(const std::string& name) const { return mpMaterials->getMaterialByName(name); } + ref getMaterialByName(const std::string& name) const { return mpMaterials->getMaterialByName(name); } /** Add a material. \param pMaterial The material. \return The ID of the material in the scene. */ - MaterialID addMaterial(const Material::SharedPtr& pMaterial) { return mpMaterials->addMaterial(pMaterial); } + MaterialID addMaterial(const ref& pMaterial) { return mpMaterials->addMaterial(pMaterial); } /** Get a list of all grid volumes in the scene. */ - const std::vector& getGridVolumes() const { return mGridVolumes; } + const std::vector>& getGridVolumes() const { return mGridVolumes; } /** Get a grid volume. */ - const GridVolume::SharedPtr& getGridVolume(uint32_t gridVolumeID) const { return mGridVolumes[gridVolumeID]; } + const ref& getGridVolume(uint32_t gridVolumeID) const { return mGridVolumes[gridVolumeID]; } /** Get a grid volume by name. */ - GridVolume::SharedPtr getGridVolumeByName(const std::string& name) const; + ref getGridVolumeByName(const std::string& name) const; /** Get the hit info requirements. */ @@ -889,7 +883,7 @@ namespace Falcor /** Get a list of all lights in the scene. */ - const std::vector& getLights() const { return mLights; }; + const std::vector>& getLights() const { return mLights; }; /** Get the number of lights in the scene. */ @@ -897,15 +891,15 @@ namespace Falcor /** Get a light. */ - const Light::SharedPtr& getLight(uint32_t lightID) const { return mLights[lightID]; } + const ref& getLight(uint32_t lightID) const { return mLights[lightID]; } /** Get a light by name. */ - Light::SharedPtr getLightByName(const std::string& name) const; + ref getLightByName(const std::string& name) const; /** Get a list of all active lights in the scene. */ - const std::vector& getActiveLights() const { return mActiveLights; } + const std::vector>& getActiveLights() const { return mActiveLights; } /** Get the number of active lights in the scene. */ @@ -913,7 +907,7 @@ namespace Falcor /** Get an active light. */ - const Light::SharedPtr& getActiveLight(uint32_t lightID) const { return mActiveLights[lightID]; } + const ref& getActiveLight(uint32_t lightID) const { return mActiveLights[lightID]; } /** Get the light collection representing all the mesh lights in the scene. The light collection is created lazily on the first call. It needs a render context. @@ -921,11 +915,11 @@ namespace Falcor \param[in] pRenderContext Render context. \return Returns the light collection. */ - const LightCollection::SharedPtr& getLightCollection(RenderContext* pRenderContext); + const ref& getLightCollection(RenderContext* pRenderContext); /** Get the environment map or nullptr if it doesn't exist. */ - const EnvMap::SharedPtr& getEnvMap() const { return mpEnvMap; } + const ref& getEnvMap() const { return mpEnvMap; } /** Set how the scene's TLASes are updated when raytracing. TLASes are REBUILT by default. @@ -975,7 +969,7 @@ namespace Falcor \param[in] pRasterizerStateCW Rasterizer state for meshes with clockwise triangle winding. \param[in] pRasterizerStateCCW Rasterizer state for meshes with counter-clockwise triangle winding. Can be the same as for clockwise. */ - void rasterize(RenderContext* pRenderContext, GraphicsState* pState, GraphicsVars* pVars, const RasterizerState::SharedPtr& pRasterizerStateCW, const RasterizerState::SharedPtr& pRasterizerStateCCW); + void rasterize(RenderContext* pRenderContext, GraphicsState* pState, GraphicsVars* pVars, const ref& pRasterizerStateCW, const ref& pRasterizerStateCCW); /** Get the required raytracing maximum attribute size for this scene. Note: This depends on what types of geometry are used in the scene. @@ -985,7 +979,7 @@ namespace Falcor /** Render the scene using raytracing. */ - void raytrace(RenderContext* pRenderContext, RtProgram* pProgram, const std::shared_ptr& pVars, uint3 dispatchDims); + void raytrace(RenderContext* pRenderContext, RtProgram* pProgram, const ref& pVars, uint3 dispatchDims); /** Render the UI. */ @@ -995,21 +989,21 @@ namespace Falcor The default VAO uses 32-bit vertex indices. For meshes with 16-bit indices, use getMeshVao16() instead. \return VAO object or nullptr if no meshes using 32-bit indices. */ - const Vao::SharedPtr& getMeshVao() const { return mpMeshVao; } + const ref& getMeshVao() const { return mpMeshVao; } /** Get the scene's VAO for 16-bit vertex indices. \return VAO object or nullptr if no meshes using 16-bit indices. */ - const Vao::SharedPtr& getMeshVao16() const { return mpMeshVao16Bit; } + const ref& getMeshVao16() const { return mpMeshVao16Bit; } /** Get the scene's VAO for curves. */ - const Vao::SharedPtr& getCurveVao() const { return mpCurveVao; } + const ref& getCurveVao() const { return mpCurveVao; } /** Set an environment map. \param[in] pEnvMap Environment map. Can be nullptr. */ - void setEnvMap(EnvMap::SharedPtr pEnvMap); + void setEnvMap(ref pEnvMap); /** Load an environment from an image. \param[in] path Texture path. @@ -1043,7 +1037,7 @@ namespace Falcor /** Get the scene's animations. */ - std::vector& getAnimations() { return mpAnimationController->getAnimations(); } + std::vector>& getAnimations() { return mpAnimationController->getAnimations(); } /** Returns true if scene has animation data. */ @@ -1071,7 +1065,7 @@ namespace Falcor /** Get the parameter block with all scene resources. */ - const ParameterBlock::SharedPtr& getParameterBlock() const { return mpSceneBlock; } + const ref& getParameterBlock() const { return mpSceneBlock; } /** Set the scene ray tracing resources into a shader var. The acceleration structure is created lazily, which requires the render context. @@ -1221,15 +1215,15 @@ namespace Falcor void updateLightStats(); void updateGridVolumeStats(); - Scene(std::shared_ptr pDevice, SceneData&& sceneData); + Scene(ref pDevice, SceneData&& sceneData); - std::shared_ptr mpDevice; ///< GPU device the scene resides on. + ref mpDevice; ///< GPU device the scene resides on. // Scene Geometry struct DrawArgs { - Buffer::SharedPtr pBuffer; ///< Buffer holding the draw-indirect arguments. + ref pBuffer; ///< Buffer holding the draw-indirect arguments. uint32_t count = 0; ///< Number of draws. bool ccw = true; ///< True if counterclockwise triangle winding. ResourceFormat ibFormat = ResourceFormat::Unknown; ///< Index buffer format. @@ -1243,9 +1237,9 @@ namespace Falcor bool mHas16BitIndices = false; ///< True if any meshes use 16-bit indices. bool mHas32BitIndices = false; ///< True if any meshes use 32-bit indices. - Vao::SharedPtr mpMeshVao; ///< Vertex array object for the global mesh vertex/index buffers. - Vao::SharedPtr mpMeshVao16Bit; ///< VAO for drawing meshes with 16-bit vertex indices. - Vao::SharedPtr mpCurveVao; ///< Vertex array object for the global curve vertex/index buffers. + ref mpMeshVao; ///< Vertex array object for the global mesh vertex/index buffers. + ref mpMeshVao16Bit; ///< VAO for drawing meshes with 16-bit vertex indices. + ref mpCurveVao; ///< Vertex array object for the global curve vertex/index buffers. std::vector mDrawArgs; ///< List of draw arguments for rasterizing the meshes in the scene. // Triangle meshes @@ -1262,9 +1256,9 @@ namespace Falcor struct DisplacementMeshData { uint32_t AABBOffset = 0; uint32_t AABBCount = 0; }; std::vector meshData; ///< List of displacement mesh data (reference to AABBs). std::vector updateTasks; ///< List of displacement AABB update tasks. - Buffer::SharedPtr pUpdateTasksBuffer; ///< GPU Buffer with list of displacement AABB update tasks. - ComputePass::SharedPtr pUpdatePass; ///< Comput epass to update displacement AABB data. - Buffer::SharedPtr pAABBBuffer; ///< GPU Buffer of raw displacement AABB data. Used for acceleration structure creation, and bound to the Scene for access in shaders. + ref pUpdateTasksBuffer; ///< GPU Buffer with list of displacement AABB update tasks. + ref pUpdatePass; ///< Comput epass to update displacement AABB data. + ref pAABBBuffer; ///< GPU Buffer of raw displacement AABB data. Used for acceleration structure creation, and bound to the Scene for access in shaders. } mDisplacement; // Curves @@ -1273,7 +1267,7 @@ namespace Falcor std::vector mCurveStaticData; ///< Vertex attributes for all curves. // SDF grids - std::vector mSDFGrids; ///< List of SDF grids. + std::vector> mSDFGrids; ///< List of SDF grids. std::vector mSDFGridDesc; ///< List of SDF grid descriptors. uint32_t mSDFGridMaxLODCount; ///< The max LOD count of any SDF grid. SDFGridConfig mSDFGridConfig; ///< SDF grid configuration. @@ -1288,21 +1282,21 @@ namespace Falcor // The following array and buffer records the AABBs of all procedural primitives, including custom primitives, curves, etc. std::vector mRtAABBRaw; ///< Raw AABB data (min, max) for all procedural primitives. - Buffer::SharedPtr mpRtAABBBuffer; ///< GPU Buffer of raw AABB data. Used for acceleration structure creation, and bound to the Scene for access in shaders. + ref mpRtAABBBuffer; ///< GPU Buffer of raw AABB data. Used for acceleration structure creation, and bound to the Scene for access in shaders. // Materials - MaterialSystem::SharedPtr mpMaterials; ///< Material system. This holds data and resources for all materials. + std::unique_ptr mpMaterials; ///< Material system. This holds data and resources for all materials. // Lights - std::vector mLights; ///< All analytic lights. Note that not all may be active. - std::vector mActiveLights; ///< All active analytic lights. - std::vector mGridVolumes; ///< All loaded grid volumes. - std::vector mGrids; ///< All loaded grids. - std::unordered_map mGridIDs; ///< Lookup table for grid IDs. - LightCollection::SharedPtr mpLightCollection; ///< Class for managing emissive geometry. This is created lazily upon first use. - EnvMap::SharedPtr mpEnvMap; ///< Environment map or nullptr if not loaded. + std::vector> mLights; ///< All analytic lights. Note that not all may be active. + std::vector> mActiveLights; ///< All active analytic lights. + std::vector> mGridVolumes; ///< All loaded grid volumes. + std::vector> mGrids; ///< All loaded grids. + std::unordered_map, SdfGridID> mGridIDs; ///< Lookup table for grid IDs. + ref mpLightCollection; ///< Class for managing emissive geometry. This is created lazily upon first use. + ref mpEnvMap; ///< Environment map or nullptr if not loaded. bool mEnvMapChanged = false; ///< Flag indicating that the environment map has changed since last frame. - LightProfile::SharedPtr mpLightProfile; ///< DEMO21: Global light profile. + ref mpLightProfile; ///< DEMO21: Global light profile. // Scene metadata (CPU only) std::vector mMeshBBs; ///< Bounding boxes for meshes (not instances) in object space. @@ -1322,19 +1316,19 @@ namespace Falcor UpdateCallback mUpdateCallback; ///< Scene update callback. // Scene block resources - Buffer::SharedPtr mpGeometryInstancesBuffer; - Buffer::SharedPtr mpMeshesBuffer; - Buffer::SharedPtr mpCurvesBuffer; - Buffer::SharedPtr mpCustomPrimitivesBuffer; - Buffer::SharedPtr mpLightsBuffer; - Buffer::SharedPtr mpGridVolumesBuffer; - ParameterBlock::SharedPtr mpSceneBlock; + ref mpGeometryInstancesBuffer; + ref mpMeshesBuffer; + ref mpCurvesBuffer; + ref mpCustomPrimitivesBuffer; + ref mpLightsBuffer; + ref mpGridVolumesBuffer; + ref mpSceneBlock; // Camera UpDirection mUpDirection = UpDirection::YPos; CameraControllerType mCamCtrlType = CameraControllerType::FirstPerson; - CameraController::SharedPtr mpCamCtrl; - std::vector mCameras; + std::unique_ptr mpCamCtrl; + std::vector> mCameras; uint32_t mSelectedCamera = 0; float mCameraSpeed = 1.0f; bool mCameraSwitched = false; @@ -1355,28 +1349,28 @@ namespace Falcor uint32_t mCurrentViewpoint = 0; // Rendering - std::map mFrontClockwiseRS; - std::map mFrontCounterClockwiseRS; + std::map> mFrontClockwiseRS; + std::map> mFrontCounterClockwiseRS; UpdateFlags mUpdates = UpdateFlags::All; - AnimationController::UniquePtr mpAnimationController; + std::unique_ptr 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. + std::vector mInstanceDescs; ///< Shared between TLAS builds to avoid reallocating CPU memory. struct TlasData { - RtAccelerationStructure::SharedPtr pTlasObject; - Buffer::SharedPtr pTlasBuffer; - Buffer::SharedPtr pInstanceDescs; ///< Buffer holding instance descs for the TLAS. + ref pTlasObject; + ref pTlasBuffer; + ref 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 type 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. + ref mpTlasScratch; ///< Scratch buffer used for TLAS builds. Can be shared as long as instance desc count is the same, which for now it is. RtAccelerationStructurePrebuildInfo mTlasPrebuildInfo; ///< This can be reused as long as the number of instance descs doesn't change. /** Describes one BLAS. @@ -1419,15 +1413,15 @@ namespace Falcor uint64_t scratchByteSize = 0; ///< Maximum scratch data size for all BLASes in the group, including padding. uint64_t finalByteSize = 0; ///< Size of the final BLASes in the group post-compaction, including padding. - Buffer::SharedPtr pBlas; ///< Buffer containing all final BLASes in the group. + ref pBlas; ///< Buffer containing all final BLASes in the group. }; // BLAS Data is ordered as all mesh BLAS's first, followed by one BLAS containing all AABBs. - std::vector mBlasObjects; ///< BLAS API objects. + std::vector> mBlasObjects; ///< BLAS API objects. std::vector mBlasData; ///< All data related to the scene's BLASes. std::vector mBlasGroups; ///< BLAS group data. - Buffer::SharedPtr mpBlasScratch; ///< Scratch buffer used for BLAS builds. - Buffer::SharedPtr mpBlasStaticWorldMatrices; ///< Object-to-world transform matrices in row-major format. Only valid for static meshes. + ref mpBlasScratch; ///< Scratch buffer used for BLAS builds. + ref mpBlasStaticWorldMatrices; ///< Object-to-world transform matrices in row-major format. Only valid for static meshes. bool mBlasDataValid = false; ///< Flag to indicate if the BLAS data is valid. This will be reset when geometry is changed. bool mRebuildBlas = true; ///< Flag to indicate BLASes need to be rebuilt. diff --git a/Source/Falcor/Scene/SceneBuilder.cpp b/Source/Falcor/Scene/SceneBuilder.cpp index 3caae9f8b..2fc50c805 100644 --- a/Source/Falcor/Scene/SceneBuilder.cpp +++ b/Source/Falcor/Scene/SceneBuilder.cpp @@ -26,7 +26,6 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **************************************************************************/ #include "SceneBuilder.h" -#include "SceneBuilderAccess.h" #include "SceneCache.h" #include "Importer.h" #include "Curves/CurveConfig.h" @@ -37,6 +36,7 @@ #include "Utils/Timing/TimeReport.h" #include "Utils/Scripting/ScriptBindings.h" #include "Utils/Math/MathHelpers.h" +#include "Utils/ObjectIDPython.h" #include #include #include @@ -136,14 +136,14 @@ namespace Falcor std::vector mTangents; std::vector mPositions; int32_t getFaceCount() const { return (int32_t)mMesh.faceCount; } - void getPosition(float position[], int32_t face, int32_t vert) const { FALCOR_ASSERT_LT(face * 3 + vert, (ssize_t)mPositions.size()); memcpy(position, mPositions.data() + (face * 3 + vert), sizeof(float3)); } + void getPosition(float position[], int32_t face, int32_t vert) const { FALCOR_ASSERT_LT(size_t(face) * 3 + vert, mPositions.size()); memcpy(position, mPositions.data() + (face * 3 + vert), sizeof(float3)); } void getNormal(float normal[], int32_t face, int32_t vert) { *reinterpret_cast(normal) = mMesh.getNormal(face, vert); } void getTexCrd(float texCrd[], int32_t face, int32_t vert) { *reinterpret_cast(texCrd) = mMesh.getTexCrd(face, vert); } void setTangent(const float tangent[], float sign, int32_t face, int32_t vert) { float3 T = *reinterpret_cast(tangent); - mTangents[face * 3 + vert] = float4(glm::normalize(T), sign); + mTangents[face * 3 + vert] = float4(normalize(T), sign); } }; @@ -151,11 +151,11 @@ namespace Falcor { auto isInvalid = [](const auto& x) { - return glm::any(glm::isinf(x) || glm::isnan(x)); + return any(isinf(x) || isnan(x)); }; auto isZero = [](const auto& x) { - return glm::length(x) < 1e-6f; + return length(x) < 1e-6f; }; if (isInvalid(v.position) || isInvalid(v.normal) || isInvalid(v.tangent) || isInvalid(v.texCrd) || isInvalid(v.boneWeights)) invalidCount++; @@ -164,15 +164,14 @@ namespace Falcor bool compareVertices(const SceneBuilder::Mesh::Vertex& lhs, const SceneBuilder::Mesh::Vertex& rhs, float threshold = 1e-6f) { - using namespace glm; - if (lhs.position != rhs.position) return false; // Position need to be exact to avoid cracks + if (any(lhs.position != rhs.position)) return false; // Position need to be exact to avoid cracks if (lhs.tangent.w != rhs.tangent.w) return false; if (lhs.curveRadius != rhs.curveRadius) return false; - if (lhs.boneIDs != rhs.boneIDs) return false; - if (any(greaterThan(abs(lhs.normal - rhs.normal), float3(threshold)))) return false; - if (any(greaterThan(abs(lhs.tangent.xyz - rhs.tangent.xyz), float3(threshold)))) return false; - if (any(greaterThan(abs(lhs.texCrd - rhs.texCrd), float2(threshold)))) return false; - if (any(greaterThan(abs(lhs.boneWeights - rhs.boneWeights), float4(threshold)))) return false; + if (any(lhs.boneIDs != rhs.boneIDs)) return false; + if (any(abs(lhs.normal - rhs.normal) > float3(threshold))) return false; + if (any(abs(lhs.tangent.xyz() - rhs.tangent.xyz()) > float3(threshold))) return false; + if (any(abs(lhs.texCrd - rhs.texCrd) > float2(threshold))) return false; + if (any(abs(lhs.boneWeights - rhs.boneWeights) > float4(threshold))) return false; return true; } @@ -202,21 +201,17 @@ namespace Falcor } } - SceneBuilder::SceneBuilder(std::shared_ptr pDevice, const Settings& settings, Flags flags) - : mpDevice(std::move(pDevice)) + SceneBuilder::SceneBuilder(ref pDevice, const Settings& settings, Flags flags) + : mpDevice(pDevice) , mSettings(settings) , mFlags(flags) { - mpFence = GpuFence::create(mpDevice.get()); - mSceneData.pMaterials = MaterialSystem::create(mpDevice); + mpFence = GpuFence::create(mpDevice); + mSceneData.pMaterials = std::make_unique(mpDevice); } - SceneBuilder::SharedPtr SceneBuilder::create(std::shared_ptr pDevice, const Settings& settings, Flags flags) - { - return SharedPtr(new SceneBuilder(pDevice, settings, flags)); - } - - SceneBuilder::SharedPtr SceneBuilder::create(std::shared_ptr pDevice, const std::filesystem::path& path, const Settings& settings, Flags buildFlags) + SceneBuilder::SceneBuilder(ref pDevice, const std::filesystem::path& path, const Settings& settings, Flags buildFlags) + : SceneBuilder(pDevice, settings, buildFlags) { std::filesystem::path fullPath; if (!findFileInDataDirectories(path, fullPath)) @@ -224,23 +219,21 @@ namespace Falcor throw ImporterError(path, "Can't find scene file '{}'.", path); } - auto pBuilder = create(pDevice, settings, buildFlags); - // Compute scene cache key based on absolute scene path and build flags. - pBuilder->mSceneCacheKey = computeSceneCacheKey(fullPath, buildFlags); + mSceneCacheKey = computeSceneCacheKey(fullPath, buildFlags); // Determine if scene cache should be written after import. bool useCache = is_set(buildFlags, Flags::UseCache); bool rebuildCache = is_set(buildFlags, Flags::RebuildCache); - pBuilder->mWriteSceneCache = useCache || rebuildCache; + mWriteSceneCache = useCache || rebuildCache; // Try to load scene cache if supported, available and requested. - if (useCache && !rebuildCache && SceneCache::hasValidCache(pBuilder->mSceneCacheKey)) + if (useCache && !rebuildCache && SceneCache::hasValidCache(mSceneCacheKey)) { try { - pBuilder->mpScene = Scene::create(pDevice, SceneCache::readCache(pDevice, pBuilder->mSceneCacheKey)); - return pBuilder; + mpScene = Scene::create(pDevice, SceneCache::readCache(pDevice, mSceneCacheKey)); + return; } catch (const std::exception& e) { @@ -248,11 +241,12 @@ namespace Falcor } } - pBuilder->import(path); - return pBuilder; + import(path); } - void SceneBuilder::import(const std::filesystem::path& path, const Dictionary& dict) + SceneBuilder::~SceneBuilder() {} + + void SceneBuilder::import(const std::filesystem::path& path, const pybind11::dict& dict) { logInfo("Importing scene: {}", path); std::filesystem::path fullPath; @@ -272,7 +266,7 @@ namespace Falcor } } - Scene::SharedPtr SceneBuilder::getScene() + ref SceneBuilder::getScene() { if (mpScene) return mpScene; @@ -288,7 +282,7 @@ namespace Falcor auto dummyMesh = TriangleMesh::createDummy(); auto dummyMaterial = StandardMaterial::create(mpDevice, "Dummy"); auto meshID = addTriangleMesh(dummyMesh, dummyMaterial); - Node dummyNode = { "Dummy", rmcv::identity(), rmcv::identity() }; + Node dummyNode = { "Dummy", float4x4::identity(), float4x4::identity() }; NodeID nodeID = addNode(dummyNode); addMeshInstance(nodeID, meshID); } @@ -364,7 +358,7 @@ namespace Falcor return addProcessedMesh(processMesh(mesh)); } - MeshID SceneBuilder::addTriangleMesh(const TriangleMesh::SharedPtr& pTriangleMesh, const Material::SharedPtr& pMaterial) + MeshID SceneBuilder::addTriangleMesh(const ref& pTriangleMesh, const ref& pMaterial) { checkArgument(pTriangleMesh != nullptr, "'pTriangleMesh' is missing"); checkArgument(pMaterial != nullptr, "'pMaterial' is missing"); @@ -457,23 +451,24 @@ namespace Falcor std::vector transformedTexCoords; if (mesh.texCrds.pData != nullptr) { - const rmcv::mat4 xform = mesh.pMaterial->getTextureTransform().getMatrix(); - if (xform != rmcv::identity()) + const float4x4 xform = mesh.pMaterial->getTextureTransform().getMatrix(); + if (xform != float4x4::identity()) { size_t texCoordCount = mesh.getAttributeCount(mesh.texCrds); transformedTexCoords.resize(texCoordCount); // The given matrix transforms the texture (e.g., scaling > 1 enlarges the texture). // Because we're transforming the input coordinates, apply the inverse. - const rmcv::mat4 invXform = rmcv::inverse(xform); + const float4x4 invXform = inverse(xform); // Because texture transforms are 2D and affine, we only need apply the corresponding 3x2 matrix - rmcv::matrix<2,3, float> coordTransform; - coordTransform.setCol(0, invXform.getCol(0).xy); - coordTransform.setCol(1, invXform.getCol(1).xy); - coordTransform.setCol(2, invXform.getCol(3).xy); + math::matrix coordTransform = matrixFromColumns( + invXform.getCol(0).xy(), + invXform.getCol(1).xy(), + invXform.getCol(3).xy() + ); for (size_t i = 0; i < texCoordCount; ++i) { - transformedTexCoords[i] = coordTransform * float3(mesh.texCrds.pData[i], 1.f); + transformedTexCoords[i] = mul(coordTransform, float3(mesh.texCrds.pData[i], 1.f)); } mesh.texCrds.pData = transformedTexCoords.data(); } @@ -616,13 +611,15 @@ namespace Falcor FALCOR_ASSERT(index < vertices.size()); const Mesh::Vertex& v = vertices[index].first; - StaticVertexData s; - s.position = v.position; - s.normal = v.normal; - s.texCrd = v.texCrd; - s.tangent = v.tangent; - s.curveRadius = v.curveRadius; - processedMesh.staticData.push_back(s); + { + StaticVertexData s; + s.position = v.position; + s.normal = v.normal; + s.texCrd = v.texCrd; + s.tangent = v.tangent; + s.curveRadius = v.curveRadius; + processedMesh.staticData.push_back(s); + } if (mesh.hasBones()) { @@ -810,7 +807,7 @@ namespace Falcor // SDFs - SdfDescID SceneBuilder::addSDFGrid(const SDFGrid::SharedPtr& pSDFGrid, const Material::SharedPtr& pMaterial) + SdfDescID SceneBuilder::addSDFGrid(const ref& pSDFGrid, const ref& pMaterial) { checkArgument(pSDFGrid != nullptr, "'pSDFGrid' is missing"); checkArgument(pMaterial != nullptr, "'pMaterial' is missing"); @@ -821,26 +818,26 @@ namespace Falcor mSceneData.sdfGrids.push_back(pSDFGrid); mSceneData.sdfGridDesc.push_back(desc); - mSceneData.sdfGridMaxLODCount = glm::max(bitScanReverse(pSDFGrid->getGridWidth()) + 1, mSceneData.sdfGridMaxLODCount); + mSceneData.sdfGridMaxLODCount = std::max(bitScanReverse(pSDFGrid->getGridWidth()) + 1, mSceneData.sdfGridMaxLODCount); return SdfDescID(mSceneData.sdfGridDesc.size() - 1); } // Materials - MaterialID SceneBuilder::addMaterial(const Material::SharedPtr& pMaterial) + MaterialID SceneBuilder::addMaterial(const ref& pMaterial) { checkArgument(pMaterial != nullptr, "'pMaterial' is missing"); return mSceneData.pMaterials->addMaterial(pMaterial); } - void SceneBuilder::replaceMaterial(const Material::SharedPtr& pMaterial, const Material::SharedPtr& pReplacement) + void SceneBuilder::replaceMaterial(const ref& pMaterial, const ref& pReplacement) { checkArgument(pMaterial != nullptr, "'pMaterial' is missing"); checkArgument(pReplacement != nullptr, "'pReplacement' is missing"); mSceneData.pMaterials->replaceMaterial(pMaterial, pReplacement); } - void SceneBuilder::loadMaterialTexture(const Material::SharedPtr& pMaterial, Material::TextureSlot slot, const std::filesystem::path& path) + void SceneBuilder::loadMaterialTexture(const ref& pMaterial, Material::TextureSlot slot, const std::filesystem::path& path) { checkArgument(pMaterial != nullptr, "'pMaterial' is missing"); if (!mpMaterialTextureLoader) @@ -857,7 +854,7 @@ namespace Falcor // GridVolumes - GridVolume::SharedPtr SceneBuilder::getGridVolume(const std::string& name) const + ref SceneBuilder::getGridVolume(const std::string& name) const { for (const auto& pGridVolume : mSceneData.gridVolumes) { @@ -866,7 +863,7 @@ namespace Falcor return nullptr; } - VolumeID SceneBuilder::addGridVolume(const GridVolume::SharedPtr& pGridVolume, NodeID nodeID) + VolumeID SceneBuilder::addGridVolume(const ref& pGridVolume, NodeID nodeID) { FALCOR_ASSERT(pGridVolume); checkArgument(nodeID == NodeID::Invalid() || nodeID.get() < mSceneGraph.size(), "'nodeID' ({}) is out of range", nodeID); @@ -884,7 +881,7 @@ namespace Falcor // Lights - Light::SharedPtr SceneBuilder::getLight(const std::string& name) const + ref SceneBuilder::getLight(const std::string& name) const { for (const auto& pLight : mSceneData.lights) { @@ -893,7 +890,7 @@ namespace Falcor return nullptr; } - LightID SceneBuilder::addLight(const Light::SharedPtr& pLight) + LightID SceneBuilder::addLight(const ref& pLight) { checkArgument(pLight != nullptr, "'pLight' is missing"); mSceneData.lights.push_back(pLight); @@ -908,7 +905,7 @@ namespace Falcor // Cameras - CameraID SceneBuilder::addCamera(const Camera::SharedPtr& pCamera) + CameraID SceneBuilder::addCamera(const ref& pCamera) { checkArgument(pCamera != nullptr, "'pCamera' is missing"); mSceneData.cameras.push_back(pCamera); @@ -916,12 +913,12 @@ namespace Falcor return CameraID(mSceneData.cameras.size() - 1); } - Camera::SharedPtr SceneBuilder::getSelectedCamera() const + ref SceneBuilder::getSelectedCamera() const { return mSceneData.selectedCamera < mSceneData.cameras.size() ? mSceneData.cameras[mSceneData.selectedCamera] : nullptr; } - void SceneBuilder::setSelectedCamera(const Camera::SharedPtr& pCamera) + void SceneBuilder::setSelectedCamera(const ref& pCamera) { auto it = std::find(mSceneData.cameras.begin(), mSceneData.cameras.end(), pCamera); mSceneData.selectedCamera = it != mSceneData.cameras.end() ? (uint32_t)std::distance(mSceneData.cameras.begin(), it) : 0; @@ -929,13 +926,13 @@ namespace Falcor // Animations - void SceneBuilder::addAnimation(const Animation::SharedPtr& pAnimation) + void SceneBuilder::addAnimation(const ref& pAnimation) { checkArgument(pAnimation != nullptr, "'pAnimation' is missing"); mSceneData.animations.push_back(pAnimation); } - Animation::SharedPtr SceneBuilder::createAnimation(Animatable::SharedPtr pAnimatable, const std::string& name, double duration) + ref SceneBuilder::createAnimation(ref pAnimatable, const std::string& name, double duration) { checkArgument(pAnimatable != nullptr, "'pAnimatable' is missing"); @@ -946,7 +943,7 @@ namespace Falcor logWarning("Animatable object is already animated."); return nullptr; } - if (nodeID == NodeID::Invalid()) nodeID = addNode(Node{ name, rmcv::identity(), rmcv::identity() }); + if (nodeID == NodeID::Invalid()) nodeID = addNode(Node{ name, float4x4::identity(), float4x4::identity() }); pAnimatable->setNodeID(nodeID); pAnimatable->setHasAnimation(true); @@ -962,7 +959,7 @@ namespace Falcor NodeID SceneBuilder::addNode(const Node& node) { // Validate node. - auto validateMatrix = [&](rmcv::mat4 m, const char* field) + auto validateMatrix = [&](float4x4 m, const char* field) { if (!isMatrixValid(m)) { @@ -972,7 +969,7 @@ namespace Falcor if (!isMatrixAffine(m)) { logWarning("SceneBuilder::addNode() - Node '{}' {} matrix is not affine. Setting last row to (0,0,0,1).", node.name, field); - m[3] = rmcv::vec4(0, 0, 0, 1); + m[3] = float4(0, 0, 0, 1); } return m; }; @@ -1129,7 +1126,7 @@ namespace Falcor // Compute the combined transform. auto& child = mSceneGraph[childNodeID.get()]; - rmcv::mat4 transform = child.transform; + float4x4 transform = child.transform; NodeID prevNodeID = childNodeID; NodeID nodeID = child.parent; @@ -1148,7 +1145,7 @@ namespace Falcor FALCOR_ASSERT(node.children[0] == prevNodeID); // Update the transform and step to the parent. - transform = node.transform * transform; + transform = mul(node.transform, transform); if (nodeID == parentNodeID) break; prevNodeID = nodeID; @@ -1421,12 +1418,12 @@ namespace Falcor // Compute the object->world transform for the node. FALCOR_ASSERT(nodeID != NodeID::Invalid()); - rmcv::mat4 transform = rmcv::identity(); + float4x4 transform = float4x4::identity(); NodeID curID = nodeID; while (curID != NodeID::Invalid()) { FALCOR_ASSERT_LT(curID.get(), mSceneGraph.size()); - transform = mSceneGraph[curID.get()].transform * transform; + transform = mul(mSceneGraph[curID.get()].transform, transform); curID = mSceneGraph[curID.get()].parent; } @@ -1440,7 +1437,7 @@ namespace Falcor prevNode.meshes.erase(it); // Link mesh to new top-level node. - NodeID newNodeID = addNode(Node{newMesh->name, transform, rmcv::identity()}); + NodeID newNodeID = addNode(Node{newMesh->name, transform, float4x4::identity()}); InternalNode& newNode = mSceneGraph[newNodeID.get()]; if (*instIter == *mesh.instances.rbegin() && newInstances.empty()) @@ -1502,8 +1499,8 @@ namespace Falcor // We build a set of unique nodes. If a node is identical to one of the // existing nodes, its contents are merged into the matching node. - auto lessThan = [](const rmcv::mat4& lhs, const rmcv::mat4& rhs) { - return rmcv::lex_lt(lhs, rhs); + auto lessThan = [](const float4x4& lhs, const float4x4& rhs) { + return math::lex_lt(lhs, rhs); }; // Comparison for strict weak ordering of scene graph nodes w r t to the fields we care about. @@ -1552,7 +1549,7 @@ namespace Falcor // This step is a prerequisite for the ray tracing optimizations we do later. // Add an identity transform node. - NodeID identityNodeID = addNode(Node{ "Identity", rmcv::identity(), rmcv::identity() }); + NodeID identityNodeID = addNode(Node{ "Identity", float4x4::identity(), float4x4::identity() }); auto& identityNode = mSceneGraph[identityNodeID.get()]; size_t transformedMeshCount = 0; @@ -1571,38 +1568,37 @@ namespace Falcor auto nodeID = *mesh.instances.begin(); FALCOR_ASSERT(nodeID != NodeID::Invalid()); - rmcv::mat4 transform = rmcv::identity(); + float4x4 transform = float4x4::identity(); while (nodeID != NodeID::Invalid()) { FALCOR_ASSERT_LT(nodeID.get(), mSceneGraph.size()); - transform = mSceneGraph[nodeID.get()].transform * transform; + transform = mul(mSceneGraph[nodeID.get()].transform, transform); nodeID = mSceneGraph[nodeID.get()].parent; } // Flip triangle winding flag if the transform flips the coordinate system handedness (negative determinant). - bool flippedWinding = rmcv::determinant((rmcv::mat3)transform) < 0.f; + bool flippedWinding = determinant(float3x3(transform)) < 0.f; if (flippedWinding) mesh.isFrontFaceCW = !mesh.isFrontFaceCW; // Transform vertices to world space if not already identity transform. - if (transform != rmcv::identity()) + if (transform != float4x4::identity()) { FALCOR_ASSERT(!mesh.staticData.empty()); FALCOR_ASSERT((size_t)mesh.vertexCount == mesh.staticData.size()); - rmcv::mat3 invTranspose3x3 = (rmcv::mat3)rmcv::transpose(rmcv::inverse(transform)); - rmcv::mat3 transform3x3 = (rmcv::mat3)transform; + float3x3 invTranspose3x3 = float3x3(transpose(inverse(transform))); + float3x3 transform3x3 = float3x3(transform); for (auto& v : mesh.staticData) { - float4 p = transform * float4(v.position, 1.f); - v.position = p.xyz; - v.normal = glm::normalize(invTranspose3x3 * v.normal); - v.tangent.xyz = glm::normalize(transform3x3 * float3(v.tangent.xyz)); // TODO: This cast shouldn't be necessary + v.position = transformPoint(transform, v.position); + v.normal = normalize(transformVector(invTranspose3x3, v.normal)); + v.tangent = float4(normalize(transformVector(transform3x3, v.tangent.xyz())), v.tangent.w); // TODO: We should flip the sign of v.tangent.w if flippedWinding is true. // Leaving that out for now for consistency with the shader code that needs the same fix. - v.curveRadius = glm::length(transform3x3 * float3(v.curveRadius, 0.f, 0.f)); + v.curveRadius = length(transformVector(transform3x3, float3(v.curveRadius, 0.f, 0.f))); } transformedMeshCount++; @@ -2459,13 +2455,13 @@ namespace Falcor void SceneBuilder::collectVolumeGrids() { // Collect grids from volumes. - std::set uniqueGrids; + std::set> uniqueGrids; for (auto& pGridVolume : mSceneData.gridVolumes) { auto grids = pGridVolume->getAllGrids(); uniqueGrids.insert(grids.begin(), grids.end()); } - mSceneData.grids = std::vector(uniqueGrids.begin(), uniqueGrids.end()); + mSceneData.grids = std::vector>(uniqueGrids.begin(), uniqueGrids.end()); } void SceneBuilder::quantizeTexCoords() @@ -2504,7 +2500,7 @@ namespace Falcor // Compute maximum quantization error in texels. // The texcoords are used for all texture channels so taking the maximum dimensions. uint2 maxTexDim = pMaterial->getMaxTextureDimensions(); - maxError *= maxTexDim; + maxError *= float2(maxTexDim); float maxTexelError = std::max(maxError.x, maxError.y); if (maxTexelError > kMaxTexelError) @@ -2525,7 +2521,7 @@ namespace Falcor { // Removes duplicate SDF grids. - std::vector uniqueSDFGrids; + std::vector> uniqueSDFGrids; std::unordered_set removedIDs; for (SdfGridID i{ 0 }; i.get() < mSceneData.sdfGrids.size(); ++i) @@ -2533,7 +2529,7 @@ namespace Falcor if (removedIDs.count(i) > 0) continue; - const SDFGrid::SharedPtr& pSDFGridA = mSceneData.sdfGrids[i.get()]; + const ref& pSDFGridA = mSceneData.sdfGrids[i.get()]; uniqueSDFGrids.push_back(pSDFGridA); if (removedIDs.size() > 0) @@ -2541,7 +2537,7 @@ namespace Falcor for (SdfGridID j{ i.get() + 1 }; j.get() < mSceneData.sdfGrids.size(); ++j) { - const SDFGrid::SharedPtr& pSDFGridB = mSceneData.sdfGrids[j.get()]; + const ref& pSDFGridB = mSceneData.sdfGrids[j.get()]; if (pSDFGridA == pSDFGridB) { @@ -2807,20 +2803,6 @@ namespace Falcor } } - static SceneBuilder* spActivePythonSceneBuilder; // TODO: REMOVEGLOBAL - - void setActivePythonSceneBuilder(SceneBuilder* pSceneBuilder) - { - spActivePythonSceneBuilder = pSceneBuilder; - } - - SceneBuilder& getActivePythonSceneBuilder() - { - if (!spActivePythonSceneBuilder) - throw RuntimeError("This can only be called in a Python scene building context!"); - return *spActivePythonSceneBuilder; - } - FALCOR_SCRIPT_BINDING(SceneBuilder) { using namespace pybind11::literals; @@ -2834,6 +2816,7 @@ namespace Falcor FALCOR_SCRIPT_BINDING_DEPENDENCY(Animation) FALCOR_SCRIPT_BINDING_DEPENDENCY(AABB) FALCOR_SCRIPT_BINDING_DEPENDENCY(GridVolume) + FALCOR_SCRIPT_BINDING_DEPENDENCY(Settings) pybind11::enum_ flags(m, "SceneBuilderFlags"); flags.value("Default", SceneBuilder::Flags::Default); @@ -2858,7 +2841,7 @@ namespace Falcor flags.value("RebuildCache", SceneBuilder::Flags::RebuildCache); ScriptBindings::addEnumBinaryOperators(flags); - pybind11::class_ sceneBuilder(m, "SceneBuilder"); + pybind11::class_ sceneBuilder(m, "SceneBuilder"); sceneBuilder.def_property_readonly("flags", &SceneBuilder::getFlags); sceneBuilder.def_property_readonly("materials", &SceneBuilder::getMaterials); sceneBuilder.def_property_readonly("gridVolumes", &SceneBuilder::getGridVolumes); @@ -2870,9 +2853,7 @@ namespace Falcor sceneBuilder.def_property("envMap", &SceneBuilder::getEnvMap, &SceneBuilder::setEnvMap); sceneBuilder.def_property("selectedCamera", &SceneBuilder::getSelectedCamera, &SceneBuilder::setSelectedCamera); sceneBuilder.def_property("cameraSpeed", &SceneBuilder::getCameraSpeed, &SceneBuilder::setCameraSpeed); - sceneBuilder.def("importScene", [] (SceneBuilder* pSceneBuilder, const std::filesystem::path& path, const pybind11::dict& dict) { - pSceneBuilder->import(path, Dictionary(dict)); - }, "path"_a, "dict"_a = pybind11::dict()); + sceneBuilder.def("importScene", &SceneBuilder::import, "path"_a, "dict"_a = pybind11::dict()); sceneBuilder.def("addTriangleMesh", &SceneBuilder::addTriangleMesh, "triangleMesh"_a, "material"_a); sceneBuilder.def("addSDFGrid", &SceneBuilder::addSDFGrid, "sdfGrid"_a, "material"_a); sceneBuilder.def("addMaterial", &SceneBuilder::addMaterial, "material"_a); diff --git a/Source/Falcor/Scene/SceneBuilder.h b/Source/Falcor/Scene/SceneBuilder.h index 33bcf2b0d..4d1e336d3 100644 --- a/Source/Falcor/Scene/SceneBuilder.h +++ b/Source/Falcor/Scene/SceneBuilder.h @@ -40,9 +40,10 @@ #include "Utils/Math/AABB.h" #include "Utils/Math/Vector.h" #include "Utils/Math/Matrix.h" -#include "Utils/Scripting/Dictionary.h" #include "Utils/Settings.h" +#include + #include #include #include @@ -53,8 +54,6 @@ namespace Falcor class FALCOR_API SceneBuilder { public: - using SharedPtr = std::shared_ptr; - /** Flags that control how the scene will be built. They can be combined together. */ enum class Flags @@ -113,7 +112,7 @@ namespace Falcor uint32_t indexCount = 0; ///< The number of indices the mesh has. const uint32_t* pIndices = nullptr; ///< Array of indices. The element count must match `indexCount`. This field is required. Vao::Topology topology = Vao::Topology::Undefined; ///< The primitive topology of the mesh - Material::SharedPtr pMaterial; ///< The mesh's material. Can't be nullptr. + ref pMaterial; ///< The mesh's material. Can't be nullptr. Attribute positions; ///< Array of vertex positions. This field is required. Attribute normals; ///< Array of vertex normals. This field is required. @@ -267,7 +266,7 @@ namespace Falcor { std::string name; Vao::Topology topology = Vao::Topology::Undefined; - Material::SharedPtr pMaterial; + ref pMaterial; NodeID skeletonNodeId{ NodeID::Invalid() }; ///< Forwarded from Mesh struct. uint64_t indexCount = 0; ///< Number of indices, or zero if non-indexed. @@ -295,7 +294,7 @@ namespace Falcor uint32_t vertexCount = 0; ///< The number of vertices. uint32_t indexCount = 0; ///< The number of indices (i.e., tube segments). const uint32_t* pIndices = nullptr; ///< Array of indices. The element count must match `indexCount`. This field is required. - Material::SharedPtr pMaterial; ///< The curve's material. Can't be nullptr. + ref pMaterial; ///< The curve's material. Can't be nullptr. Attribute positions; ///< Array of vertex positions. This field is required. Attribute radius; ///< Array of sphere radius. This field is required. @@ -310,7 +309,7 @@ namespace Falcor { std::string name; Vao::Topology topology = Vao::Topology::LineStrip; - Material::SharedPtr pMaterial; + ref pMaterial; std::vector indexData; std::vector staticData; @@ -319,33 +318,35 @@ namespace Falcor struct Node { std::string name; - rmcv::mat4 transform; - rmcv::mat4 meshBind; // For skinned meshes. World transform at bind time. - rmcv::mat4 localToBindPose; // For bones. Inverse bind transform. + float4x4 transform; + float4x4 meshBind; // For skinned meshes. World transform at bind time. + float4x4 localToBindPose; // For bones. Inverse bind transform. NodeID parent{ NodeID::Invalid() }; }; - /** Create a new object + /** Constructor. */ - static SharedPtr create(std::shared_ptr pDevice, const Settings& settings, Flags flags = Flags::Default); + SceneBuilder(ref pDevice, const Settings& settings, Flags flags = Flags::Default); - /** Create a new builder and import a scene/model file - \return A new object with the imported file already initialized, or throws an ImporterError if importing went wrong. + /** Create a new builder and import a scene/model file. + Throws an ImporterError if importing went wrong. */ - static SharedPtr create(std::shared_ptr pDevice, const std::filesystem::path& path, const Settings& settings, Flags flags = Flags::Default); + SceneBuilder(ref pDevice, const std::filesystem::path& path, const Settings& settings, Flags flags = Flags::Default); + + ~SceneBuilder(); /** Import a scene/model file \param path The file path to load Throws an ImporterError if something went wrong. */ - void import(const std::filesystem::path& path, const Dictionary& dict = Dictionary()); + void import(const std::filesystem::path& path, const pybind11::dict& dict = pybind11::dict()); /** 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(); + ref getScene(); - const std::shared_ptr& getDevice() const { return mpDevice; } + const ref& getDevice() const { return mpDevice; } const Settings& getSettings() const { return mSettings; } Settings& getSettings() { return mSettings; } @@ -392,7 +393,7 @@ namespace Falcor \param pMaterial The material to use for the mesh. \return The ID of the mesh in the scene. */ - MeshID addTriangleMesh(const TriangleMesh::SharedPtr& pTriangleMesh, const Material::SharedPtr& pMaterial); + MeshID addTriangleMesh(const ref& pTriangleMesh, const ref& pMaterial); /** Pre-process a mesh into the data format that is used in the global scene buffers. Throws an exception if something went wrong. @@ -461,39 +462,39 @@ namespace Falcor \param pMaterial The material to be used by this SDF grid. \return The ID of the SDG grid desc in the scene. */ - SdfDescID addSDFGrid(const SDFGrid::SharedPtr& pSDFGrid, const Material::SharedPtr& pMaterial); + SdfDescID addSDFGrid(const ref& pSDFGrid, const ref& pMaterial); // Materials /** Get the list of materials. */ - const std::vector& getMaterials() const { return mSceneData.pMaterials->getMaterials(); } + const std::vector>& getMaterials() const { return mSceneData.pMaterials->getMaterials(); } /** Get a material by name. Note: This returns the first material found with a matching name. \param name Material name. \return Returns the first material with a matching name or nullptr if none was found. */ - Material::SharedPtr getMaterial(const std::string& name) const { return mSceneData.pMaterials->getMaterialByName(name); } + ref getMaterial(const std::string& name) const { return mSceneData.pMaterials->getMaterialByName(name); } /** Add a material. \param pMaterial The material. \return The ID of the material in the scene. */ - MaterialID addMaterial(const Material::SharedPtr& pMaterial); + MaterialID addMaterial(const ref& pMaterial); /** Replace a material. \param pMaterial The material to replace. \param pReplacement The material to replace it with. */ - void replaceMaterial(const Material::SharedPtr& pMaterial, const Material::SharedPtr& pReplacement); + void replaceMaterial(const ref& pMaterial, const ref& pReplacement); /** Request loading a material texture. \param[in] pMaterial Material to load texture into. \param[in] slot Slot to load texture into. \param[in] path Texture file path. */ - void loadMaterialTexture(const Material::SharedPtr& pMaterial, Material::TextureSlot slot, const std::filesystem::path& path); + void loadMaterialTexture(const ref& pMaterial, Material::TextureSlot slot, const std::filesystem::path& path); /** Wait until all material textures are loaded. */ @@ -503,40 +504,40 @@ namespace Falcor /** Get the list of grid volumes. */ - const std::vector& getGridVolumes() const { return mSceneData.gridVolumes; } + const std::vector>& getGridVolumes() const { return mSceneData.gridVolumes; } /** Get a grid volume by name. Note: This returns the first volume found with a matching name. \param name Volume name. \return Returns the first volume with a matching name or nullptr if none was found. */ - GridVolume::SharedPtr getGridVolume(const std::string& name) const; + ref getGridVolume(const std::string& name) const; /** Add a grid volume. \param pGridVolume The grid volume. \param nodeID The node to attach the volume to (optional). \return The ID of the volume in the scene. */ - VolumeID addGridVolume(const GridVolume::SharedPtr& pGridVolume, NodeID nodeID = NodeID{ NodeID::Invalid() } ); + VolumeID addGridVolume(const ref& pGridVolume, NodeID nodeID = NodeID{ NodeID::Invalid() } ); // Lights /** Get the list of lights. */ - const std::vector& getLights() const { return mSceneData.lights; } + const std::vector>& getLights() const { return mSceneData.lights; } /** Get a light by name. Note: This returns the first light found with a matching name. \param name Light name. \return Returns the first light with a matching name or nullptr if none was found. */ - Light::SharedPtr getLight(const std::string& name) const; + ref getLight(const std::string& name) const; /** Add a light source \param pLight The light object. \return The light ID */ - LightID addLight(const Light::SharedPtr& pLight); + LightID addLight(const ref& pLight); /** DEMO21: Load global light profile. */ @@ -546,33 +547,33 @@ namespace Falcor /** Get the environment map. */ - const EnvMap::SharedPtr& getEnvMap() const { return mSceneData.pEnvMap; } + const ref& getEnvMap() const { return mSceneData.pEnvMap; } /** Set the environment map. \param[in] pEnvMap Environment map. Can be nullptr. */ - void setEnvMap(EnvMap::SharedPtr pEnvMap) { mSceneData.pEnvMap = pEnvMap; } + void setEnvMap(ref pEnvMap) { mSceneData.pEnvMap = pEnvMap; } // Cameras /** Get the list of cameras. */ - const std::vector& getCameras() const { return mSceneData.cameras; } + const std::vector>& getCameras() const { return mSceneData.cameras; } /** Add a camera. \param pCamera Camera to be added. \return The camera ID */ - CameraID addCamera(const Camera::SharedPtr& pCamera); + CameraID addCamera(const ref& pCamera); /** Get the selected camera. */ - Camera::SharedPtr getSelectedCamera() const; + ref getSelectedCamera() const; /** Set the selected camera. \param pCamera Camera to use as selected camera (needs to be added first). */ - void setSelectedCamera(const Camera::SharedPtr& pCamera); + void setSelectedCamera(const ref& pCamera); /** Get the camera speed. */ @@ -586,12 +587,12 @@ namespace Falcor /** Get the list of animations. */ - const std::vector& getAnimations() const { return mSceneData.animations; } + const std::vector>& getAnimations() const { return mSceneData.animations; } /** Add an animation \param pAnimation The animation */ - void addAnimation(const Animation::SharedPtr& pAnimation); + void addAnimation(const ref& pAnimation); /** Create an animation for an animatable object. \param pAnimatable Animatable object. @@ -599,7 +600,7 @@ namespace Falcor \param duration Duration of the animation in seconds. \return Returns a new animation or nullptr if an animation already exists. */ - Animation::SharedPtr createAnimation(Animatable::SharedPtr pAnimatable, const std::string& name, double duration); + ref createAnimation(ref pAnimatable, const std::string& name, double duration); // Scene graph @@ -613,6 +614,8 @@ namespace Falcor */ uint32_t getNodeCount() const { return uint32_t(mSceneGraph.size()); } + Node& getNode(NodeID nodeID) { return mSceneGraph[nodeID.get()]; } + /** Add a mesh instance to a node */ void addMeshInstance(NodeID nodeID, MeshID meshID); @@ -635,8 +638,6 @@ namespace Falcor void setNodeInterpolationMode(NodeID nodeID, Animation::InterpolationMode interpolationMode, bool enableWarping); private: - SceneBuilder(std::shared_ptr pDevice, const Settings& settings, Flags buildFlags); - struct InternalNode : Node { InternalNode() = default; @@ -729,14 +730,14 @@ namespace Falcor using MeshGroupList = std::vector; using CurveList = std::vector; - std::shared_ptr mpDevice; + ref mpDevice; /// Local copy of settings used to create the SceneBuilder. Edits do not propagate to the parent. Settings mSettings; const Flags mFlags; Scene::SceneData mSceneData; - Scene::SharedPtr mpScene; + ref mpScene; SceneCache::Key mSceneCacheKey; bool mWriteSceneCache = false; ///< True if scene cache should be written after import. @@ -748,7 +749,7 @@ namespace Falcor CurveList mCurves; std::unique_ptr mpMaterialTextureLoader; - GpuFence::SharedPtr mpFence; + ref mpFence; // Helpers bool doesNodeHaveAnimation(NodeID nodeID) const; diff --git a/Source/Falcor/Scene/SceneCache.cpp b/Source/Falcor/Scene/SceneCache.cpp index 4a12624fc..1209dc69a 100644 --- a/Source/Falcor/Scene/SceneCache.cpp +++ b/Source/Falcor/Scene/SceneCache.cpp @@ -34,7 +34,6 @@ #include -#include #include namespace Falcor @@ -83,7 +82,6 @@ namespace Falcor write(&value, sizeof(T)); } - template<> void write(const std::string& value) { uint64_t len = value.size(); @@ -91,7 +89,6 @@ namespace Falcor write(value.data(), len); } - template<> void write(const std::filesystem::path& path) { write(path.string()); @@ -108,7 +105,7 @@ namespace Falcor } else { - for (const auto& item : vec) write(item); + for (const auto& item : vec) write(item); } } @@ -142,7 +139,6 @@ namespace Falcor read(&value, sizeof(T)); } - template<> void read(std::string& value) { uint64_t len = read(); @@ -150,7 +146,6 @@ namespace Falcor read(value.data(), len); } - template<> void read(std::filesystem::path& path) { std::string str; @@ -177,7 +172,7 @@ namespace Falcor } else { - for (auto& item : vec) read(item); + for (auto& item : vec) read(item); } } @@ -185,7 +180,12 @@ namespace Falcor void read(std::optional& opt) { bool hasValue = read(); - if (hasValue) opt = read(); + if (hasValue) + { + T value; + read(value); + opt = value; + } } private: @@ -233,7 +233,7 @@ namespace Falcor if (fs.bad()) throw RuntimeError("Failed to write scene cache file to '{}'.", cachePath); } - Scene::SceneData SceneCache::readCache(std::shared_ptr pDevice, const Key& key) + Scene::SceneData SceneCache::readCache(ref pDevice, const Key& key) { auto cachePath = getCachePath(key); @@ -295,7 +295,7 @@ namespace Falcor if (hasEnvMap) writeEnvMap(stream, sceneData.pEnvMap); writeMarker(stream, "Materials"); - writeMaterials(stream, sceneData.pMaterials); + writeMaterials(stream, *sceneData.pMaterials); writeMarker(stream, "SceneGraph"); stream.write((uint32_t)sceneData.sceneGraph.size()); @@ -376,10 +376,10 @@ namespace Falcor writeMarker(stream, "End"); } - Scene::SceneData SceneCache::readSceneData(InputStream& stream, std::shared_ptr pDevice) + Scene::SceneData SceneCache::readSceneData(InputStream& stream, ref pDevice) { Scene::SceneData sceneData; - sceneData.pMaterials = MaterialSystem::create(pDevice); + sceneData.pMaterials = std::make_unique(pDevice); readMarker(stream, "Path"); stream.read(sceneData.path); @@ -419,7 +419,7 @@ namespace Falcor auto pMaterialTextureLoader = std::make_unique(sceneData.pMaterials->getTextureManager(), true); readMarker(stream, "Materials"); - readMaterials(stream, sceneData.pMaterials, *pMaterialTextureLoader, pDevice); + readMaterials(stream, *sceneData.pMaterials, *pMaterialTextureLoader, pDevice); readMarker(stream, "SceneGraph"); sceneData.sceneGraph.resize(stream.read()); @@ -535,7 +535,7 @@ namespace Falcor // Camera - void SceneCache::writeCamera(OutputStream& stream, const Camera::SharedPtr& pCamera) + void SceneCache::writeCamera(OutputStream& stream, const ref& pCamera) { stream.write(pCamera->mHasAnimation); stream.write(pCamera->mIsAnimated); @@ -546,7 +546,7 @@ namespace Falcor stream.write(pCamera->mData); } - Camera::SharedPtr SceneCache::readCamera(InputStream& stream) + ref SceneCache::readCamera(InputStream& stream) { auto pCamera = Camera::create(); @@ -563,7 +563,7 @@ namespace Falcor // Light - void SceneCache::writeLight(OutputStream& stream, const Light::SharedPtr& pLight) + void SceneCache::writeLight(OutputStream& stream, const ref& pLight) { LightType type = pLight->getType(); stream.write(type); @@ -582,20 +582,20 @@ namespace Falcor case LightType::Directional: break; case LightType::Distant: - stream.write(std::static_pointer_cast(pLight)->mAngle); + stream.write(static_ref_cast(pLight)->mAngle); break; case LightType::Rect: case LightType::Disc: case LightType::Sphere: - stream.write(std::static_pointer_cast(pLight)->mScaling); - stream.write(std::static_pointer_cast(pLight)->mTransformMatrix); + stream.write(static_ref_cast(pLight)->mScaling); + stream.write(static_ref_cast(pLight)->mTransformMatrix); break; } } - Light::SharedPtr SceneCache::readLight(InputStream& stream) + ref SceneCache::readLight(InputStream& stream) { - Light::SharedPtr pLight; + ref pLight; auto type = stream.read(); switch (type) @@ -634,13 +634,13 @@ namespace Falcor case LightType::Directional: break; case LightType::Distant: - stream.read(std::static_pointer_cast(pLight)->mAngle); + stream.read(static_ref_cast(pLight)->mAngle); break; case LightType::Rect: case LightType::Disc: case LightType::Sphere: - stream.read(std::static_pointer_cast(pLight)->mScaling); - stream.read(std::static_pointer_cast(pLight)->mTransformMatrix); + stream.read(static_ref_cast(pLight)->mScaling); + stream.read(static_ref_cast(pLight)->mTransformMatrix); break; } @@ -649,19 +649,19 @@ namespace Falcor // Materials - void SceneCache::writeMaterials(OutputStream& stream, const MaterialSystem::SharedPtr& pMaterials) + void SceneCache::writeMaterials(OutputStream& stream, const MaterialSystem& materialSystem) { - uint32_t materialCount = pMaterials->getMaterialCount(); + uint32_t materialCount = materialSystem.getMaterialCount(); stream.write(materialCount); for (MaterialID materialID{ 0 }; materialID.get() < materialCount; ++materialID) { - auto pMaterial = pMaterials->getMaterial(materialID); + auto pMaterial = materialSystem.getMaterial(materialID); writeMaterial(stream, pMaterial); } } - void SceneCache::writeMaterial(OutputStream& stream, const Material::SharedPtr& pMaterial) + void SceneCache::writeMaterial(OutputStream& stream, const ref& pMaterial) { // Write common fields. stream.write((uint32_t)pMaterial->getType()); @@ -691,7 +691,7 @@ namespace Falcor else throw RuntimeError("Unsupported material type"); } - void SceneCache::writeBasicMaterial(OutputStream& stream, const BasicMaterial::SharedPtr& pMaterial) + void SceneCache::writeBasicMaterial(OutputStream& stream, const ref& pMaterial) { stream.write(pMaterial->mData); stream.write(pMaterial->mAlphaRange); @@ -704,7 +704,7 @@ namespace Falcor writeSampler(stream, pMaterial->mpDisplacementMaxSampler); } - void SceneCache::readMaterials(InputStream& stream, const MaterialSystem::SharedPtr& pMaterials, MaterialTextureLoader& materialTextureLoader, std::shared_ptr pDevice) + void SceneCache::readMaterials(InputStream& stream, MaterialSystem& materialSystem, MaterialTextureLoader& materialTextureLoader, ref pDevice) { uint32_t materialCount = 0; stream.read(materialCount); @@ -712,27 +712,27 @@ namespace Falcor for (uint32_t i = 0; i < materialCount; i++) { auto pMaterial = readMaterial(stream, materialTextureLoader, pDevice); - pMaterials->addMaterial(pMaterial); + materialSystem.addMaterial(pMaterial); } } - Material::SharedPtr SceneCache::readMaterial(InputStream& stream, MaterialTextureLoader& materialTextureLoader, std::shared_ptr pDevice) + ref SceneCache::readMaterial(InputStream& stream, MaterialTextureLoader& materialTextureLoader, ref pDevice) { // Create derived material class of the right type. - Material::SharedPtr pMaterial; + ref pMaterial; { uint32_t type; stream.read(type); switch ((MaterialType)type) { case MaterialType::Standard: - pMaterial = StandardMaterial::create(pDevice); + pMaterial = StandardMaterial::create(pDevice, ""); break; case MaterialType::Hair: - pMaterial = HairMaterial::create(pDevice); + pMaterial = HairMaterial::create(pDevice, ""); break; case MaterialType::Cloth: - pMaterial = ClothMaterial::create(pDevice); + pMaterial = ClothMaterial::create(pDevice, ""); break; default: throw RuntimeError("Unsupported material type"); @@ -768,7 +768,7 @@ namespace Falcor return pMaterial; } - void SceneCache::readBasicMaterial(InputStream& stream, MaterialTextureLoader& materialTextureLoader, const BasicMaterial::SharedPtr& pMaterial, std::shared_ptr pDevice) + void SceneCache::readBasicMaterial(InputStream& stream, MaterialTextureLoader& materialTextureLoader, const ref& pMaterial, ref pDevice) { stream.read(pMaterial->mData); stream.read(pMaterial->mAlphaRange); @@ -776,12 +776,12 @@ namespace Falcor stream.read(pMaterial->mIsTexturedAlphaConstant); stream.read(pMaterial->mDisplacementMapChanged); - pMaterial->mpDefaultSampler = readSampler(stream, pDevice.get()); - pMaterial->mpDisplacementMinSampler = readSampler(stream, pDevice.get()); - pMaterial->mpDisplacementMaxSampler = readSampler(stream, pDevice.get()); + pMaterial->mpDefaultSampler = readSampler(stream, pDevice); + pMaterial->mpDisplacementMinSampler = readSampler(stream, pDevice); + pMaterial->mpDisplacementMaxSampler = readSampler(stream, pDevice); } - void SceneCache::writeSampler(OutputStream& stream, const Sampler::SharedPtr& pSampler) + void SceneCache::writeSampler(OutputStream& stream, const ref& pSampler) { bool valid = pSampler != nullptr; stream.write(valid); @@ -791,7 +791,7 @@ namespace Falcor } } - Sampler::SharedPtr SceneCache::readSampler(InputStream& stream, Device* pDevice) + ref SceneCache::readSampler(InputStream& stream, ref pDevice) { bool valid = stream.read(); if (valid) @@ -804,7 +804,7 @@ namespace Falcor // GridVolume - void SceneCache::writeGridVolume(OutputStream& stream, const GridVolume::SharedPtr& pGridVolume, const std::vector& grids) + void SceneCache::writeGridVolume(OutputStream& stream, const ref& pGridVolume, const std::vector>& grids) { stream.write(pGridVolume->mHasAnimation); stream.write(pGridVolume->mIsAnimated); @@ -826,9 +826,9 @@ namespace Falcor stream.write(pGridVolume->mData); } - GridVolume::SharedPtr SceneCache::readGridVolume(InputStream& stream, const std::vector& grids, std::shared_ptr pDevice) + ref SceneCache::readGridVolume(InputStream& stream, const std::vector>& grids, ref pDevice) { - GridVolume::SharedPtr pGridVolume = GridVolume::create(std::move(pDevice), ""); + ref pGridVolume = GridVolume::create(pDevice, ""); stream.read(pGridVolume->mHasAnimation); stream.read(pGridVolume->mIsAnimated); @@ -854,24 +854,24 @@ namespace Falcor // Grid - void SceneCache::writeGrid(OutputStream& stream, const Grid::SharedPtr& pGrid) + void SceneCache::writeGrid(OutputStream& stream, const ref& pGrid) { const nanovdb::HostBuffer& buffer = pGrid->mGridHandle.buffer(); stream.write((uint64_t)buffer.size()); stream.write(buffer.data(), buffer.size()); } - Grid::SharedPtr SceneCache::readGrid(InputStream& stream, std::shared_ptr pDevice) + ref SceneCache::readGrid(InputStream& stream, ref pDevice) { uint64_t size = stream.read(); auto buffer = nanovdb::HostBuffer::create(size); stream.read(buffer.data(), buffer.size()); - return Grid::SharedPtr(new Grid(std::move(pDevice), nanovdb::GridHandle(std::move(buffer)))); + return ref(new Grid(pDevice, nanovdb::GridHandle(std::move(buffer)))); } // EnvMap - void SceneCache::writeEnvMap(OutputStream& stream, const EnvMap::SharedPtr& pEnvMap) + void SceneCache::writeEnvMap(OutputStream& stream, const ref& pEnvMap) { auto path = pEnvMap->getEnvMap()->getSourcePath(); stream.write(path); @@ -879,7 +879,7 @@ namespace Falcor stream.write(pEnvMap->mRotation); } - EnvMap::SharedPtr SceneCache::readEnvMap(InputStream& stream, std::shared_ptr pDevice) + ref SceneCache::readEnvMap(InputStream& stream, ref pDevice) { auto path = stream.read(); auto pEnvMap = EnvMap::createFromFile(pDevice, path); @@ -909,7 +909,7 @@ namespace Falcor // Animation - void SceneCache::writeAnimation(OutputStream& stream, const Animation::SharedPtr& pAnimation) + void SceneCache::writeAnimation(OutputStream& stream, const ref& pAnimation) { stream.write(pAnimation->mName); stream.write(pAnimation->mNodeID); @@ -921,9 +921,9 @@ namespace Falcor stream.write(pAnimation->mKeyframes); } - Animation::SharedPtr SceneCache::readAnimation(InputStream& stream) + ref SceneCache::readAnimation(InputStream& stream) { - Animation::SharedPtr pAnimation = Animation::create("", NodeID(), 0.0); + ref pAnimation = Animation::create("", NodeID(), 0.0); stream.read(pAnimation->mName); stream.read(pAnimation->mNodeID); stream.read(pAnimation->mDuration); diff --git a/Source/Falcor/Scene/SceneCache.h b/Source/Falcor/Scene/SceneCache.h index c50a940f3..51e56c6b0 100644 --- a/Source/Falcor/Scene/SceneCache.h +++ b/Source/Falcor/Scene/SceneCache.h @@ -73,7 +73,7 @@ namespace Falcor \param[in] key Cache key. \return Returns the loaded scene data. */ - static Scene::SceneData readCache(std::shared_ptr pDevice, const Key& key); + static Scene::SceneData readCache(ref pDevice, const Key& key); private: class OutputStream; @@ -82,41 +82,41 @@ namespace Falcor static std::filesystem::path getCachePath(const Key& key); static void writeSceneData(OutputStream& stream, const Scene::SceneData& sceneData); - static Scene::SceneData readSceneData(InputStream& stream, std::shared_ptr pDevice); + static Scene::SceneData readSceneData(InputStream& stream, ref pDevice); static void writeMetadata(OutputStream& stream, const Scene::Metadata& metadata); static Scene::Metadata readMetadata(InputStream& stream); - static void writeCamera(OutputStream& stream, const Camera::SharedPtr& pCamera); - static Camera::SharedPtr readCamera(InputStream& stream); + static void writeCamera(OutputStream& stream, const ref& pCamera); + static ref readCamera(InputStream& stream); - static void writeLight(OutputStream& stream, const Light::SharedPtr& pLight); - static Light::SharedPtr readLight(InputStream& stream); + static void writeLight(OutputStream& stream, const ref& pLight); + static ref readLight(InputStream& stream); - static void writeMaterials(OutputStream& stream, const MaterialSystem::SharedPtr& pMaterials); - static void writeMaterial(OutputStream& stream, const Material::SharedPtr& pMaterial); - static void writeBasicMaterial(OutputStream& stream, const BasicMaterial::SharedPtr& pMaterial); - static void readMaterials(InputStream& stream, const MaterialSystem::SharedPtr& pMaterials, MaterialTextureLoader& materialTextureLoader, std::shared_ptr pDevice); - static Material::SharedPtr readMaterial(InputStream& stream, MaterialTextureLoader& materialTextureLoader, std::shared_ptr pDevice); - static void readBasicMaterial(InputStream& stream, MaterialTextureLoader& materialTextureLoader, const BasicMaterial::SharedPtr& pMaterial, std::shared_ptr pDevice); + static void writeMaterials(OutputStream& stream, const MaterialSystem& materialSystem); + static void writeMaterial(OutputStream& stream, const ref& pMaterial); + static void writeBasicMaterial(OutputStream& stream, const ref& pMaterial); + static void readMaterials(InputStream& stream, MaterialSystem& materialSystem, MaterialTextureLoader& materialTextureLoader, ref pDevice); + static ref readMaterial(InputStream& stream, MaterialTextureLoader& materialTextureLoader, ref pDevice); + static void readBasicMaterial(InputStream& stream, MaterialTextureLoader& materialTextureLoader, const ref& pMaterial, ref pDevice); - static void writeSampler(OutputStream& stream, const Sampler::SharedPtr& pSampler); - static Sampler::SharedPtr readSampler(InputStream& stream, Device* pDevice); + static void writeSampler(OutputStream& stream, const ref& pSampler); + static ref readSampler(InputStream& stream, ref pDevice); - static void writeGridVolume(OutputStream& stream, const GridVolume::SharedPtr& pVolume, const std::vector& grids); - static GridVolume::SharedPtr readGridVolume(InputStream& stream, const std::vector& grids, std::shared_ptr pDevice); + static void writeGridVolume(OutputStream& stream, const ref& pVolume, const std::vector>& grids); + static ref readGridVolume(InputStream& stream, const std::vector>& grids, ref pDevice); - static void writeGrid(OutputStream& stream, const Grid::SharedPtr& pGrid); - static Grid::SharedPtr readGrid(InputStream& stream, std::shared_ptr pDevice); + static void writeGrid(OutputStream& stream, const ref& pGrid); + static ref readGrid(InputStream& stream, ref pDevice); - static void writeEnvMap(OutputStream& stream, const EnvMap::SharedPtr& pEnvMap); - static EnvMap::SharedPtr readEnvMap(InputStream& stream, std::shared_ptr pDevice); + static void writeEnvMap(OutputStream& stream, const ref& pEnvMap); + static ref readEnvMap(InputStream& stream, ref pDevice); static void writeTransform(OutputStream& stream, const Transform& transform); static Transform readTransform(InputStream& stream); - static void writeAnimation(OutputStream& stream, const Animation::SharedPtr& pAnimation); - static Animation::SharedPtr readAnimation(InputStream& stream); + static void writeAnimation(OutputStream& stream, const ref& pAnimation); + static ref readAnimation(InputStream& stream); static void writeMarker(OutputStream& stream, const std::string& id); static void readMarker(InputStream& stream, const std::string& id); diff --git a/Source/Falcor/Scene/SceneTypes.slang b/Source/Falcor/Scene/SceneTypes.slang index c19f1434d..396ce459c 100644 --- a/Source/Falcor/Scene/SceneTypes.slang +++ b/Source/Falcor/Scene/SceneTypes.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -228,6 +228,8 @@ struct PackedStaticVertexData position = v.position; texCrd = v.texCrd; + uint3 n = f32tof16(v.normal); + float packedTangentSignCurveRadius = v.tangent.w; if (v.curveRadius > 0.f) @@ -236,10 +238,11 @@ struct PackedStaticVertexData FALCOR_ASSERT(v.tangent.w != 0.f); packedTangentSignCurveRadius *= v.curveRadius; } + uint t_w = f32tof16(packedTangentSignCurveRadius); - packedNormalTangentCurveRadius.x = asfloat(glm::packHalf2x16({ v.normal.x, v.normal.y })); - packedNormalTangentCurveRadius.y = asfloat(glm::packHalf2x16({ v.normal.z, packedTangentSignCurveRadius })); - packedNormalTangentCurveRadius.z = asfloat(encodeNormal2x16(v.tangent.xyz)); + packedNormalTangentCurveRadius.x = asfloat((n.y << 16) | n.x); + packedNormalTangentCurveRadius.y = asfloat((t_w << 16) | n.z); + packedNormalTangentCurveRadius.z = asfloat(encodeNormal2x16(v.tangent.xyz())); } #else // !HOST_CODE @@ -272,11 +275,11 @@ struct PackedStaticVertexData v.normal.z = f16tof32(asuint(packedNormalTangentCurveRadius.y) & 0xffff); v.normal = normalize(v.normal); - v.tangent.xyz = decodeNormal2x16(asuint(packedNormalTangentCurveRadius.z)); + float3 tangent = decodeNormal2x16(asuint(packedNormalTangentCurveRadius.z)); float packedTangentSignCurveRadius = f16tof32(asuint(packedNormalTangentCurveRadius.y) >> 16); - v.tangent.w = sign(packedTangentSignCurveRadius); + v.tangent = float4(tangent, sign(packedTangentSignCurveRadius)); - v.curveRadius = abs(packedTangentSignCurveRadius); + v.curveRadius = STD_NAMESPACE abs(packedTangentSignCurveRadius); return v; } diff --git a/Source/Falcor/Scene/ShadingData.slang b/Source/Falcor/Scene/ShadingData.slang index 22599798f..765c76def 100644 --- a/Source/Falcor/Scene/ShadingData.slang +++ b/Source/Falcor/Scene/ShadingData.slang @@ -28,6 +28,7 @@ import Scene.Material.MaterialData; import Scene.HitInfo; import Utils.Geometry.GeometryHelpers; +import Utils.Math.ShadingFrame; /** This struct holds information needed for shading a hit point. @@ -48,15 +49,12 @@ struct ShadingData // Geometry data float3 posW; ///< Shading hit position in world space. float3 V; ///< View direction, -incident direction (-ray.dir) - // N,T,B is an orthonormal basis representing the interpolated shading frame in world space at the shading point. - // Handedness is determined by the MikkTSpace library and stored in tangentW.w on the mesh, where B = cross(N, T) * tangentW.w. - float3 N; ///< Shading normal at shading hit. Normalized. - float3 T; ///< Shading tangent at shading hit. Normalized. - float3 B; ///< Shading bitangent at shading hit. Normalized. float2 uv; ///< Texture mapping coordinates. + + ShadingFrame frame; ///< Smooth interpolated shading frame in world space at the shading point. The normal is *not* automatically flipped for backfacing hits. float3 faceN; ///< Face normal in world space, always on the front-facing side. float4 tangentW; ///< Geometric tangent (xyz) and sign (w) in world space. This is used for orthogonalization. Not normalized, but it is guaranteed to be nonzero and sign (w) is +-1.0. - bool frontFacing; ///< True if primitive seen from the front-facing side. + bool frontFacing; ///< True if primitive seen from the front-facing side, i.e., dot(V, faceN) >= 0.0. float curveRadius; ///< Curve cross-sectional radius. Valid only for geometry generated from curves. // Material data @@ -79,29 +77,11 @@ struct ShadingData return computeRayOrigin(posW, (frontFacing == viewside) ? faceN : -faceN); } - /** Transform vector from the local surface frame to world space. - \param[in] v Vector in local space. - \return Vector in world space. - */ - float3 fromLocal(float3 v) - { - return T * v.x + B * v.y + N * v.z; - } - - /** Transform vector from world space to the local surface frame. - \param[in] v Vector in world space. - \return Vector in local space. - */ - float3 toLocal(float3 v) - { - return float3(dot(v, T), dot(v, B), dot(v, N)); - } - - /** Returns sign that gets applied to such that B = cross(N, T) * getHandednessSign(); - \return +1 for right handed, and -1 for left handed frames. + /** Returns the oriented face normal. + \return Face normal flipped to the same side as the view vector. */ - float getHandednessSign() + float3 getOrientedFaceNormal() { - return dot(cross(N, T), B) >= 0.f ? 1.f : -1.f; + return frontFacing ? faceN : -faceN; } }; diff --git a/Source/Falcor/Scene/Transform.cpp b/Source/Falcor/Scene/Transform.cpp index 4921982ce..bbb61b361 100644 --- a/Source/Falcor/Scene/Transform.cpp +++ b/Source/Falcor/Scene/Transform.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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,8 +27,6 @@ **************************************************************************/ #include "Transform.h" #include "Utils/Scripting/ScriptBindings.h" -#include -#include namespace Falcor { @@ -46,7 +44,7 @@ namespace Falcor mDirty = true; } - void Transform::setRotation(const glm::quat& rotation) + void Transform::setRotation(const quatf& rotation) { mRotation = rotation; mDirty = true; @@ -54,57 +52,57 @@ namespace Falcor float3 Transform::getRotationEuler() const { - return glm::eulerAngles(mRotation); + return math::eulerAngles(mRotation); } void Transform::setRotationEuler(const float3& angles) { - setRotation(glm::quat(angles)); + setRotation(math::quatFromEulerAngles(angles)); } float3 Transform::getRotationEulerDeg() const { - return glm::degrees(getRotationEuler()); + return math::degrees(getRotationEuler()); } void Transform::setRotationEulerDeg(const float3& angles) { - setRotationEuler(glm::radians(angles)); + setRotationEuler(math::radians(angles)); } void Transform::lookAt(const float3& position, const float3& target, const float3& up) { mTranslation = position; float3 dir = normalize(target - position); - mRotation = glm::quatLookAt(dir, up); + mRotation = math::quatFromLookAt(dir, up, math::Handedness::RightHanded); } - const rmcv::mat4& Transform::getMatrix() const + const float4x4& Transform::getMatrix() const { if (mDirty) { - rmcv::mat4 T = rmcv::translate(mTranslation); - rmcv::mat4 R = rmcv::mat4_cast(mRotation); - rmcv::mat4 S = rmcv::scale(mScaling); + float4x4 T = math::matrixFromTranslation(mTranslation); + float4x4 R = math::matrixFromQuat(mRotation); + float4x4 S = math::matrixFromScaling(mScaling); switch (mCompositionOrder) { case CompositionOrder::ScaleRotateTranslate: - mMatrix = T * R * S; + mMatrix = mul(mul(T, R), S); break; case CompositionOrder::ScaleTranslateRotate: - mMatrix = R * T * S; + mMatrix = mul(mul(R, T), S); break; case CompositionOrder::RotateScaleTranslate: - mMatrix = T * S * R; + mMatrix = mul(mul(T, S), R); break; case CompositionOrder::RotateTranslateScale: - mMatrix = S * T * R; + mMatrix = mul(mul(S, T), R); break; case CompositionOrder::TranslateRotateScale: - mMatrix = S * R * T; + mMatrix = mul(mul(S, R), T); break; case CompositionOrder::TranslateScaleRotate: - mMatrix = R * S * T; + mMatrix = mul(mul(R, S), T); break; case CompositionOrder::Unknown: throw RuntimeError("Unknown transform composition order."); @@ -140,9 +138,9 @@ namespace Falcor bool Transform::operator==(const Transform& other) const { - if (mTranslation != other.mTranslation) return false; - if (mScaling != other.mScaling) return false; - if (mRotation != other.mRotation) return false; + if (any(mTranslation != other.mTranslation)) return false; + if (any(mScaling != other.mScaling)) return false; + if (any(mRotation != other.mRotation)) return false; return true; } diff --git a/Source/Falcor/Scene/Transform.h b/Source/Falcor/Scene/Transform.h index 1ba0c6166..8f442ecde 100644 --- a/Source/Falcor/Scene/Transform.h +++ b/Source/Falcor/Scene/Transform.h @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -29,6 +29,7 @@ #include "Core/Macros.h" #include "Utils/Math/Vector.h" #include "Utils/Math/Matrix.h" +#include "Utils/Math/Quaternion.h" namespace Falcor { @@ -59,8 +60,8 @@ namespace Falcor const float3& getScaling() const { return mScaling; } void setScaling(const float3& scaling); - const glm::quat& getRotation() const { return mRotation; } - void setRotation(const glm::quat& rotation); + const quatf& getRotation() const { return mRotation; } + void setRotation(const quatf& rotation); float3 getRotationEuler() const; void setRotationEuler(const float3& angles); @@ -73,7 +74,7 @@ namespace Falcor CompositionOrder getCompositionOrder() const { return mCompositionOrder; } ; void setCompositionOrder(const CompositionOrder& order) { mCompositionOrder = order; mDirty = true; } - const rmcv::mat4& getMatrix() const; + const float4x4& getMatrix() const; bool operator==(const Transform& other) const; bool operator!=(const Transform& other) const { return !((*this) == other); } @@ -83,11 +84,11 @@ namespace Falcor private: float3 mTranslation = float3(0.f); float3 mScaling = float3(1.f); - glm::quat mRotation = glm::identity(); + quatf mRotation = quatf::identity(); CompositionOrder mCompositionOrder = CompositionOrder::Default; mutable bool mDirty = true; - mutable rmcv::mat4 mMatrix; + mutable float4x4 mMatrix; friend class SceneCache; }; diff --git a/Source/Falcor/Scene/TriangleMesh.cpp b/Source/Falcor/Scene/TriangleMesh.cpp index bcd76f888..99edab000 100644 --- a/Source/Falcor/Scene/TriangleMesh.cpp +++ b/Source/Falcor/Scene/TriangleMesh.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -37,24 +37,24 @@ namespace Falcor { - TriangleMesh::SharedPtr TriangleMesh::create() + ref TriangleMesh::create() { - return SharedPtr(new TriangleMesh()); + return ref(new TriangleMesh()); } - TriangleMesh::SharedPtr TriangleMesh::create(const VertexList& vertices, const IndexList& indices, bool frontFaceCW) + ref TriangleMesh::create(const VertexList& vertices, const IndexList& indices, bool frontFaceCW) { - return SharedPtr(new TriangleMesh(vertices, indices, frontFaceCW)); + return ref(new TriangleMesh(vertices, indices, frontFaceCW)); } - TriangleMesh::SharedPtr TriangleMesh::createDummy() + ref TriangleMesh::createDummy() { VertexList vertices = {{{0.f, 0.f, 0.f}, {0.f, 1.f, 0.f}, {0.f, 0.f}}}; IndexList indices = {0, 0, 0}; return create(vertices, indices); } - TriangleMesh::SharedPtr TriangleMesh::createQuad(float2 size) + ref TriangleMesh::createQuad(float2 size) { float2 hsize = 0.5f * size; float3 normal{0.f, 1.f, 0.f}; @@ -75,7 +75,7 @@ namespace Falcor return create(vertices, indices, frontFaceCW); } - TriangleMesh::SharedPtr TriangleMesh::createDisk(float radius, uint32_t segments) + ref TriangleMesh::createDisk(float radius, uint32_t segments) { std::vector vertices(segments + 1); std::vector indices(segments * 3); @@ -98,7 +98,7 @@ namespace Falcor return create(vertices, indices, false); } - TriangleMesh::SharedPtr TriangleMesh::createCube(float3 size) + ref TriangleMesh::createCube(float3 size) { const float3 positions[6][4] = { @@ -147,7 +147,7 @@ namespace Falcor return create(vertices, indices, frontFaceCW); } - TriangleMesh::SharedPtr TriangleMesh::createSphere(float radius, uint32_t segmentsU, uint32_t segmentsV) + ref TriangleMesh::createSphere(float radius, uint32_t segmentsU, uint32_t segmentsV) { VertexList vertices; IndexList indices; @@ -192,7 +192,7 @@ namespace Falcor return create(vertices, indices); } - TriangleMesh::SharedPtr TriangleMesh::createFromFile(const std::filesystem::path& path, bool smoothNormals) + ref TriangleMesh::createFromFile(const std::filesystem::path& path, bool smoothNormals) { std::filesystem::path fullPath; if (!findFileInDataDirectories(path, fullPath)) @@ -286,18 +286,18 @@ namespace Falcor applyTransform(transform.getMatrix()); } - void TriangleMesh::applyTransform(const rmcv::mat4& transform) + void TriangleMesh::applyTransform(const float4x4& transform) { - auto invTranspose = (rmcv::mat3)rmcv::transpose(rmcv::inverse(transform)); + auto invTranspose = float3x3(transpose(inverse(transform))); for (auto& vertex : mVertices) { - vertex.position = (transform * float4(vertex.position, 1.f)).xyz; - vertex.normal = glm::normalize(invTranspose * vertex.normal); + vertex.position = transformPoint(transform, vertex.position); + vertex.normal = normalize(transformVector(invTranspose, vertex.normal)); } // Check if triangle winding has flipped and adjust winding order accordingly. - bool flippedWinding = rmcv::determinant((rmcv::mat3)transform) < 0.f; + bool flippedWinding = determinant(float3x3(transform)) < 0.f; if (flippedWinding) mFrontFaceCW = !mFrontFaceCW; } @@ -314,7 +314,13 @@ namespace Falcor { using namespace pybind11::literals; - pybind11::class_ triangleMesh(m, "TriangleMesh"); + pybind11::class_> triangleMesh(m, "TriangleMesh"); + + pybind11::class_ vertex(triangleMesh, "Vertex"); + vertex.def_readwrite("position", &TriangleMesh::Vertex::position); + vertex.def_readwrite("normal", &TriangleMesh::Vertex::normal); + vertex.def_readwrite("texCoord", &TriangleMesh::Vertex::texCoord); + triangleMesh.def_property("name", &TriangleMesh::getName, &TriangleMesh::setName); triangleMesh.def_property("frontFaceCW", &TriangleMesh::getFrontFaceCW, &TriangleMesh::setFrontFaceCW); triangleMesh.def_property_readonly("vertices", &TriangleMesh::getVertices); @@ -327,10 +333,5 @@ namespace Falcor triangleMesh.def_static("createCube", &TriangleMesh::createCube, "size"_a = float3(1.f)); triangleMesh.def_static("createSphere", &TriangleMesh::createSphere, "radius"_a = 1.f, "segmentsU"_a = 32, "segmentsV"_a = 32); triangleMesh.def_static("createFromFile", &TriangleMesh::createFromFile, "path"_a, "smoothNormals"_a = false); - - pybind11::class_ vertex(triangleMesh, "Vertex"); - vertex.def_readwrite("position", &TriangleMesh::Vertex::position); - vertex.def_readwrite("normal", &TriangleMesh::Vertex::normal); - vertex.def_readwrite("texCoord", &TriangleMesh::Vertex::texCoord); } } diff --git a/Source/Falcor/Scene/TriangleMesh.h b/Source/Falcor/Scene/TriangleMesh.h index bbdeea787..2b9acb665 100644 --- a/Source/Falcor/Scene/TriangleMesh.h +++ b/Source/Falcor/Scene/TriangleMesh.h @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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,6 +28,7 @@ #pragma once #include "Transform.h" #include "Core/Macros.h" +#include "Core/Object.h" #include "Utils/Math/Vector.h" #include "Utils/Math/Matrix.h" #include @@ -41,11 +42,9 @@ namespace Falcor Vertices have position, normal and texture coordinate attributes. This class is used as a utility to pass simple geometry to the SceneBuilder. */ - class FALCOR_API TriangleMesh + class FALCOR_API TriangleMesh : public Object { public: - using SharedPtr = std::shared_ptr; - struct Vertex { float3 position; @@ -59,7 +58,7 @@ namespace Falcor /** Creates a triangle mesh. \return Returns the triangle mesh. */ - static SharedPtr create(); + static ref create(); /** Creates a triangle mesh. \param[in] vertices Vertex list. @@ -67,30 +66,30 @@ namespace Falcor \param[in] frontFaceCW Triangle winding. \return Returns the triangle mesh. */ - static SharedPtr create(const VertexList& vertices, const IndexList& indices, bool frontFaceCW = false); + static ref create(const VertexList& vertices, const IndexList& indices, bool frontFaceCW = false); /** Creates a dummy mesh (single degenerate triangle). \return Returns the triangle mesh. */ - static SharedPtr createDummy(); + static ref createDummy(); /** Creates a quad mesh, centered at the origin with normal pointing in positive Y direction. \param[in] size Size of the quad in X and Z. \return Returns the triangle mesh. */ - static SharedPtr createQuad(float2 size = float2(1.f)); + static ref createQuad(float2 size = float2(1.f)); /** Creates a disk mesh, centered at the origin with the normal pointing in positive Y direction. \param[in] radius Radius of the disk. \return Returns the triangle mesh. */ - static SharedPtr createDisk(float radius, uint32_t segments = 32); + static ref createDisk(float radius, uint32_t segments = 32); /** Creates a cube mesh, centered at the origin. \param[in] size Size of the cube in each dimension. \return Returns the triangle mesh. */ - static SharedPtr createCube(float3 size = float3(1.f)); + static ref createCube(float3 size = float3(1.f)); /** Creates a UV sphere mesh, centered at the origin with poles in positive/negative Y direction. \param[in] radius Radius of the sphere. @@ -98,7 +97,7 @@ namespace Falcor \param[in] segmentsV Number of segments along meridians. \return Returns the triangle mesh. */ - static SharedPtr createSphere(float radius = 0.5f, uint32_t segmentsU = 32, uint32_t segmentsV = 16); + static ref createSphere(float radius = 0.5f, uint32_t segmentsU = 32, uint32_t segmentsV = 16); /** Creates a triangle mesh from a file. This is using ASSIMP to support a wide variety of asset formats. @@ -107,7 +106,7 @@ namespace Falcor \param[in] smoothNormals If no normals are defined in the model, generate smooth instead of facet normals. \return Returns the triangle mesh or nullptr if the mesh failed to load. */ - static SharedPtr createFromFile(const std::filesystem::path& path, bool smoothNormals = false); + static ref createFromFile(const std::filesystem::path& path, bool smoothNormals = false); /** Get the name of the triangle mesh. \return Returns the name. @@ -166,7 +165,7 @@ namespace Falcor /** Applies a transform to the triangle mesh. \param[in] transform Transform to apply. */ - void applyTransform(const rmcv::mat4& transform); + void applyTransform(const float4x4& transform); private: TriangleMesh(); diff --git a/Source/Falcor/Scene/Volume/BrickedGrid.h b/Source/Falcor/Scene/Volume/BrickedGrid.h index 4f33ef45b..c614f6b76 100644 --- a/Source/Falcor/Scene/Volume/BrickedGrid.h +++ b/Source/Falcor/Scene/Volume/BrickedGrid.h @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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,8 +32,8 @@ namespace Falcor { struct BrickedGrid { - Texture::SharedPtr range; - Texture::SharedPtr indirection; - Texture::SharedPtr atlas; + ref range; + ref indirection; + ref atlas; }; } diff --git a/Source/Falcor/Scene/Volume/Grid.cpp b/Source/Falcor/Scene/Volume/Grid.cpp index 71269aacd..edd529a95 100644 --- a/Source/Falcor/Scene/Volume/Grid.cpp +++ b/Source/Falcor/Scene/Volume/Grid.cpp @@ -35,11 +35,11 @@ #include "Utils/Math/Common.h" #include "Utils/Math/Vector.h" #include "Utils/Math/Matrix.h" -#include "Scene/SceneBuilderAccess.h" +#include "GlobalState.h" #ifdef _MSC_VER #pragma warning(push) -#pragma warning(disable : 4146 4244 4267 4275 4996) +#pragma warning(disable : 4146 4244 4267 4275 4996 4456) #endif #include #include @@ -76,19 +76,19 @@ namespace Falcor } } - Grid::SharedPtr Grid::createSphere(std::shared_ptr pDevice, float radius, float voxelSize, float blendRange) + ref Grid::createSphere(ref pDevice, float radius, float voxelSize, float blendRange) { auto handle = nanovdb::createFogVolumeSphere(radius, nanovdb::Vec3f(0.f), voxelSize, blendRange); - return SharedPtr(new Grid(std::move(pDevice), std::move(handle))); + return ref(new Grid(pDevice, std::move(handle))); } - Grid::SharedPtr Grid::createBox(std::shared_ptr pDevice, float width, float height, float depth, float voxelSize, float blendRange) + ref Grid::createBox(ref pDevice, float width, float height, float depth, float voxelSize, float blendRange) { auto handle = nanovdb::createFogVolumeBox(width, height, depth, nanovdb::Vec3f(0.f), voxelSize, blendRange); - return SharedPtr(new Grid(std::move(pDevice), std::move(handle))); + return ref(new Grid(pDevice, std::move(handle))); } - Grid::SharedPtr Grid::createFromFile(std::shared_ptr pDevice, const std::filesystem::path& path, const std::string& gridname) + ref Grid::createFromFile(ref pDevice, const std::filesystem::path& path, const std::string& gridname) { std::filesystem::path fullPath; if (!findFileInDataDirectories(path, fullPath)) @@ -99,11 +99,11 @@ namespace Falcor if (hasExtension(fullPath, "nvdb")) { - return createFromNanoVDBFile(std::move(pDevice), fullPath, gridname); + return createFromNanoVDBFile(pDevice, fullPath, gridname); } else if (hasExtension(fullPath, "vdb")) { - return createFromOpenVDBFile(std::move(pDevice), fullPath, gridname); + return createFromOpenVDBFile(pDevice, fullPath, gridname); } else { @@ -186,24 +186,24 @@ namespace Falcor return mGridHandle; } - rmcv::mat4 Grid::getTransform() const + float4x4 Grid::getTransform() const { const auto& gridMap = mGridHandle.gridMetaData()->map(); - const rmcv::mat3 affine = rmcv::make_mat3(gridMap.mMatF); + const float3x3 affine = math::matrixFromCoefficients(gridMap.mMatF); const float3 translation = float3(gridMap.mVecF[0], gridMap.mVecF[1], gridMap.mVecF[2]); - return rmcv::translate(rmcv::mat4(affine), translation); + return math::translate(float4x4(affine), translation); } - rmcv::mat4 Grid::getInvTransform() const + float4x4 Grid::getInvTransform() const { const auto& gridMap = mGridHandle.gridMetaData()->map(); - const rmcv::mat3 invAffine = rmcv::make_mat3(gridMap.mInvMatF); + const float3x3 invAffine = math::matrixFromCoefficients(gridMap.mInvMatF); const float3 translation = float3(gridMap.mVecF[0], gridMap.mVecF[1], gridMap.mVecF[2]); - return rmcv::translate(rmcv::mat4(invAffine), -translation); + return math::translate(float4x4(invAffine), -translation); } - Grid::Grid(std::shared_ptr pDevice, nanovdb::GridHandle gridHandle) - : mpDevice(std::move(pDevice)) + Grid::Grid(ref pDevice, nanovdb::GridHandle gridHandle) + : mpDevice(pDevice) , mGridHandle(std::move(gridHandle)) , mpFloatGrid(mGridHandle.grid()) , mAccessor(mpFloatGrid->getAccessor()) @@ -215,7 +215,7 @@ namespace Falcor // Keep both NanoVDB and brick textures resident in GPU memory for simplicity for now (~15% increased footprint). mpBuffer = Buffer::createStructured( - mpDevice.get(), + mpDevice, sizeof(uint32_t), uint32_t(div_round_up(mGridHandle.size(), sizeof(uint32_t))), ResourceBindFlags::UnorderedAccess | ResourceBindFlags::ShaderResource, @@ -223,10 +223,10 @@ namespace Falcor mGridHandle.data() ); using NanoVDBGridConverter = NanoVDBConverterBC4; - mBrickedGrid = NanoVDBGridConverter(mpFloatGrid).convert(mpDevice.get()); + mBrickedGrid = NanoVDBGridConverter(mpFloatGrid).convert(mpDevice); } - Grid::SharedPtr Grid::createFromNanoVDBFile(std::shared_ptr pDevice, const std::filesystem::path& path, const std::string& gridname) + ref Grid::createFromNanoVDBFile(ref pDevice, const std::filesystem::path& path, const std::string& gridname) { if (!nanovdb::io::hasGrid(path.string(), gridname)) { @@ -254,10 +254,10 @@ namespace Falcor return nullptr; } - return SharedPtr(new Grid(std::move(pDevice), std::move(handle))); + return ref(new Grid(pDevice, std::move(handle))); } - Grid::SharedPtr Grid::createFromOpenVDBFile(std::shared_ptr pDevice, const std::filesystem::path& path, const std::string& gridname) + ref Grid::createFromOpenVDBFile(ref pDevice, const std::filesystem::path& path, const std::string& gridname) { openvdb::initialize(); @@ -297,7 +297,7 @@ namespace Falcor openvdb::FloatGrid::Ptr floatGrid = openvdb::gridPtrCast(baseGrid); auto handle = nanovdb::openToNanoVDB(floatGrid); - return SharedPtr(new Grid(std::move(pDevice), std::move(handle))); + return ref(new Grid(pDevice, std::move(handle))); } @@ -305,7 +305,7 @@ namespace Falcor { using namespace pybind11::literals; - pybind11::class_ grid(m, "Grid"); + pybind11::class_> grid(m, "Grid"); grid.def_property_readonly("voxelCount", &Grid::getVoxelCount); grid.def_property_readonly("minIndex", &Grid::getMinIndex); grid.def_property_readonly("maxIndex", &Grid::getMaxIndex); @@ -316,19 +316,19 @@ namespace Falcor auto createSphere = [] (float radius, float voxelSize, float blendRange) { - return Grid::createSphere(getActivePythonSceneBuilder().getDevice(), radius, voxelSize, blendRange); + return Grid::createSphere(accessActivePythonSceneBuilder().getDevice(), radius, voxelSize, blendRange); }; grid.def_static("createSphere", createSphere, "radius"_a, "voxelSize"_a, "blendRange"_a = 3.f); // PYTHONDEPRECATED auto createBox = [] (float width, float height, float depth, float voxelSize, float blendRange) { - return Grid::createBox(getActivePythonSceneBuilder().getDevice(), width, height, depth, voxelSize, blendRange); + return Grid::createBox(accessActivePythonSceneBuilder().getDevice(), width, height, depth, voxelSize, blendRange); }; grid.def_static("createBox", createBox, "width"_a, "height"_a, "depth"_a, "voxelSize"_a, "blendRange"_a = 3.f); // PYTHONDEPRECATED auto createFromFile = [] (const std::filesystem::path& path, const std::string& gridname) { - return Grid::createFromFile(getActivePythonSceneBuilder().getDevice(), path, gridname); + return Grid::createFromFile(accessActivePythonSceneBuilder().getDevice(), path, gridname); }; grid.def_static("createFromFile", createFromFile, "path"_a, "gridname"_a); // PYTHONDEPRECATED } diff --git a/Source/Falcor/Scene/Volume/Grid.h b/Source/Falcor/Scene/Volume/Grid.h index 2d05bab86..bcf0f1427 100644 --- a/Source/Falcor/Scene/Volume/Grid.h +++ b/Source/Falcor/Scene/Volume/Grid.h @@ -29,6 +29,7 @@ #include "BrickedGrid.h" #include "Core/Macros.h" +#include "Core/Object.h" #include "Core/API/Buffer.h" #include "Utils/Math/AABB.h" #include "Utils/Math/Matrix.h" @@ -55,11 +56,9 @@ namespace Falcor /** Voxel grid based on NanoVDB. */ - class FALCOR_API Grid + class FALCOR_API Grid : public Object { public: - using SharedPtr = std::shared_ptr; - /** Create a sphere voxel grid. \param[in] pDevice GPU device. \param[in] radius Radius of the sphere in world units. @@ -67,7 +66,7 @@ namespace Falcor \param[in] blendRange Range in voxels to blend from 0 to 1 (starting at surface inwards). \return A new grid. */ - static SharedPtr createSphere(std::shared_ptr pDevice, float radius, float voxelSize, float blendRange = 2.f); + static ref createSphere(ref pDevice, float radius, float voxelSize, float blendRange = 2.f); /** Create a box voxel grid. \param[in] pDevice GPU device. @@ -78,7 +77,7 @@ namespace Falcor \param[in] blendRange Range in voxels to blend from 0 to 1 (starting at surface inwards). \return A new grid. */ - static SharedPtr createBox(std::shared_ptr pDevice, float width, float height, float depth, float voxelSize, float blendRange = 2.f); + static ref createBox(ref pDevice, float width, float height, float depth, float voxelSize, float blendRange = 2.f); /** Create a grid from a file. Currently only OpenVDB and NanoVDB grids of type float are supported. @@ -87,7 +86,7 @@ namespace Falcor \param[in] gridname Name of the grid to load. \return A new grid, or nullptr if the grid failed to load. */ - static SharedPtr createFromFile(std::shared_ptr pDevice, const std::filesystem::path& path, const std::string& gridname); + static ref createFromFile(ref pDevice, const std::filesystem::path& path, const std::string& gridname); /** Render the UI. */ @@ -138,26 +137,26 @@ namespace Falcor /** Get the (affine) NanoVDB transformation matrix. */ - rmcv::mat4 getTransform() const; + float4x4 getTransform() const; /** Get the inverse (affine) NanoVDB transformation matrix. */ - rmcv::mat4 getInvTransform() const; + float4x4 getInvTransform() const; private: - Grid(std::shared_ptr pDevice, nanovdb::GridHandle gridHandle); + Grid(ref pDevice, nanovdb::GridHandle gridHandle); - static SharedPtr createFromNanoVDBFile(std::shared_ptr, const std::filesystem::path& path, const std::string& gridname); - static SharedPtr createFromOpenVDBFile(std::shared_ptr, const std::filesystem::path& path, const std::string& gridname); + static ref createFromNanoVDBFile(ref, const std::filesystem::path& path, const std::string& gridname); + static ref createFromOpenVDBFile(ref, const std::filesystem::path& path, const std::string& gridname); - std::shared_ptr mpDevice; + ref mpDevice; // Host data. nanovdb::GridHandle mGridHandle; nanovdb::FloatGrid* mpFloatGrid; nanovdb::FloatGrid::AccessorType mAccessor; // Device data. - Buffer::SharedPtr mpBuffer; + ref mpBuffer; BrickedGrid mBrickedGrid; friend class SceneCache; diff --git a/Source/Falcor/Scene/Volume/GridConverter.h b/Source/Falcor/Scene/Volume/GridConverter.h index 20c8e4f56..fd9220910 100644 --- a/Source/Falcor/Scene/Volume/GridConverter.h +++ b/Source/Falcor/Scene/Volume/GridConverter.h @@ -63,7 +63,7 @@ namespace Falcor NanoVDBToBricksConverter(const nanovdb::FloatGrid* grid); NanoVDBToBricksConverter(const NanoVDBToBricksConverter& rhs) = delete; - BrickedGrid convert(Device* pDevice); + BrickedGrid convert(ref pDevice); private: const static uint32_t kBrickSize = 8; // Must be 8, to match both NanoVDB leaf size. @@ -283,7 +283,7 @@ namespace Falcor } template - BrickedGrid NanoVDBToBricksConverter::convert(Device* pDevice) + BrickedGrid NanoVDBToBricksConverter::convert(ref pDevice) { auto t0 = CpuTimer::getCurrentTimePoint(); auto range = NumericRange(0, mLeafDim[0].z); diff --git a/Source/Falcor/Scene/Volume/GridVolume.cpp b/Source/Falcor/Scene/Volume/GridVolume.cpp index 7561a5901..5f53e4365 100644 --- a/Source/Falcor/Scene/Volume/GridVolume.cpp +++ b/Source/Falcor/Scene/Volume/GridVolume.cpp @@ -30,7 +30,7 @@ #include "Core/API/Device.h" #include "Utils/Logger.h" #include "Utils/Scripting/ScriptBindings.h" -#include "Scene/SceneBuilderAccess.h" +#include "GlobalState.h" #include #include @@ -53,17 +53,12 @@ namespace Falcor static_assert(sizeof(GridVolumeData) % 16 == 0, "GridVolumeData size should be a multiple of 16"); - GridVolume::GridVolume(std::shared_ptr pDevice, const std::string& name) - : mpDevice(std::move(pDevice)) + GridVolume::GridVolume(ref pDevice, const std::string& name) + : mpDevice(pDevice) , mName(name) { - mData.transform = rmcv::identity(); - mData.invTransform = rmcv::identity(); - } - - GridVolume::SharedPtr GridVolume::create(std::shared_ptr pDevice, const std::string& name) - { - return SharedPtr(new GridVolume(std::move(pDevice), name)); + mData.transform = float4x4::identity(); + mData.invTransform = float4x4::identity(); } bool GridVolume::renderUI(Gui::Widgets& widget) @@ -158,10 +153,9 @@ namespace Falcor // Enumerate grid files. std::vector paths; - for (auto p : std::filesystem::directory_iterator(fullPath)) + for (auto it : std::filesystem::directory_iterator(fullPath)) { - const auto& path = p.path(); - if (hasExtension(path, "nvdb") || hasExtension(path, "vdb")) paths.push_back(path); + if (hasExtension(it.path(), "nvdb") || hasExtension(it.path(), "vdb")) paths.push_back(it.path()); } // Sort by length first, then alpha-numerically. @@ -197,14 +191,14 @@ namespace Falcor return mGrids[slotIndex]; } - void GridVolume::setGrid(GridSlot slot, const Grid::SharedPtr& grid) + void GridVolume::setGrid(GridSlot slot, const ref& grid) { setGridSequence(slot, grid ? GridSequence{grid} : GridSequence{}); } - const Grid::SharedPtr& GridVolume::getGrid(GridSlot slot) const + const ref& GridVolume::getGrid(GridSlot slot) const { - static const Grid::SharedPtr kNullGrid; + static const ref kNullGrid; uint32_t slotIndex = (uint32_t)slot; FALCOR_ASSERT(slotIndex >= 0 && slotIndex < (uint32_t)GridSlot::Count); @@ -214,14 +208,14 @@ namespace Falcor return gridSequence.empty() ? kNullGrid : gridSequence[gridIndex]; } - std::vector GridVolume::getAllGrids() const + std::vector> GridVolume::getAllGrids() const { - std::set uniqueGrids; + std::set> uniqueGrids; for (const auto& grids : mGrids) { std::copy_if(grids.begin(), grids.end(), std::inserter(uniqueGrids, uniqueGrids.begin()), [] (const auto& grid) { return grid != nullptr; }); } - return std::vector(uniqueGrids.begin(), uniqueGrids.end()); + return std::vector>(uniqueGrids.begin(), uniqueGrids.end()); } void GridVolume::setGridFrame(uint32_t gridFrame) @@ -236,7 +230,7 @@ namespace Falcor void GridVolume::setFrameRate(double frameRate) { - mFrameRate = clamp(frameRate, kMinFrameRate, kMaxFrameRate); + mFrameRate = math::clamp(frameRate, kMinFrameRate, kMaxFrameRate); } void GridVolume::setPlaybackEnabled(bool enabled) @@ -275,7 +269,7 @@ namespace Falcor void GridVolume::setAlbedo(const float3& albedo) { auto clampedAlbedo = clamp(albedo, float3(0.f), float3(1.f)); - if (mData.albedo != clampedAlbedo) + if (any(mData.albedo != clampedAlbedo)) { mData.albedo = clampedAlbedo; markUpdates(UpdateFlags::PropertiesChanged); @@ -284,7 +278,7 @@ namespace Falcor void GridVolume::setAnisotropy(float anisotropy) { - auto clampedAnisotropy = clamp(anisotropy, -kMaxAnisotropy, kMaxAnisotropy); + auto clampedAnisotropy = math::clamp(anisotropy, -kMaxAnisotropy, kMaxAnisotropy); if (mData.anisotropy != clampedAnisotropy) { mData.anisotropy = clampedAnisotropy; @@ -315,12 +309,12 @@ namespace Falcor } } - void GridVolume::updateFromAnimation(const rmcv::mat4& transform) + void GridVolume::updateFromAnimation(const float4x4& transform) { if (mData.transform != transform) { mData.transform = transform; - mData.invTransform = rmcv::inverse(transform); + mData.invTransform = inverse(transform); markUpdates(UpdateFlags::TransformChanged); updateBounds(); } @@ -373,7 +367,16 @@ namespace Falcor FALCOR_SCRIPT_BINDING_DEPENDENCY(Animatable) FALCOR_SCRIPT_BINDING_DEPENDENCY(Grid) - pybind11::class_ volume(m, "GridVolume"); + pybind11::class_> volume(m, "GridVolume"); + + pybind11::enum_ gridSlot(volume, "GridSlot"); + gridSlot.value("Density", GridVolume::GridSlot::Density); + gridSlot.value("Emission", GridVolume::GridSlot::Emission); + + pybind11::enum_ emissionMode(volume, "EmissionMode"); + emissionMode.value("Direct", GridVolume::EmissionMode::Direct); + emissionMode.value("Blackbody", GridVolume::EmissionMode::Blackbody); + volume.def_property("name", &GridVolume::getName, &GridVolume::setName); volume.def_property("gridFrame", &GridVolume::getGridFrame, &GridVolume::setGridFrame); volume.def_property_readonly("gridFrameCount", &GridVolume::getGridFrameCount); @@ -389,7 +392,7 @@ namespace Falcor volume.def_property("emissionTemperature", &GridVolume::getEmissionTemperature, &GridVolume::setEmissionTemperature); auto create = [] (const std::string& name) { - return GridVolume::create(getActivePythonSceneBuilder().getDevice(), name); + return GridVolume::create(accessActivePythonSceneBuilder().getDevice(), name); }; volume.def(pybind11::init(create), "name"_a); // PYTHONDEPRECATED volume.def("loadGrid", &GridVolume::loadGrid, "slot"_a, "path"_a, "gridname"_a); @@ -400,14 +403,6 @@ namespace Falcor pybind11::overload_cast(&GridVolume::loadGridSequence), "slot"_a, "path"_a, "gridnames"_a, "keepEmpty"_a = true); - pybind11::enum_ gridSlot(volume, "GridSlot"); - gridSlot.value("Density", GridVolume::GridSlot::Density); - gridSlot.value("Emission", GridVolume::GridSlot::Emission); - - pybind11::enum_ emissionMode(volume, "EmissionMode"); - emissionMode.value("Direct", GridVolume::EmissionMode::Direct); - emissionMode.value("Blackbody", GridVolume::EmissionMode::Blackbody); - m.attr("Volume") = m.attr("GridVolume"); // PYTHONDEPRECATED } } diff --git a/Source/Falcor/Scene/Volume/GridVolume.h b/Source/Falcor/Scene/Volume/GridVolume.h index 054c305ab..e4d1489b1 100644 --- a/Source/Falcor/Scene/Volume/GridVolume.h +++ b/Source/Falcor/Scene/Volume/GridVolume.h @@ -50,9 +50,7 @@ namespace Falcor class FALCOR_API GridVolume : public Animatable { public: - using SharedPtr = std::shared_ptr; - - using GridSequence = std::vector; + using GridSequence = std::vector>; /** Flags indicating if and what was updated in the volume. */ @@ -83,11 +81,9 @@ namespace Falcor Blackbody, }; - /** Create a new volume. - \param[in] pDevice GPU device. - \param[in] name The volume name. - */ - static SharedPtr create(std::shared_ptr pDevice, const std::string& name); + static ref create(ref pDevice, const std::string& name) { return make_ref(pDevice, name); } + + GridVolume(ref pDevice, const std::string& name); /** Render the UI. \return True if the volume was modified. @@ -150,15 +146,15 @@ namespace Falcor /** Set the grid for the specified slot. Note: This will replace any existing grid sequence for that slot with just a single grid. */ - void setGrid(GridSlot slot, const Grid::SharedPtr& grid); + void setGrid(GridSlot slot, const ref& grid); /** Get the current grid from the specified slot. */ - const Grid::SharedPtr& getGrid(GridSlot slot) const; + const ref& getGrid(GridSlot slot) const; /** Get a list of all grids used for this volume. */ - std::vector getAllGrids() const; + std::vector> getAllGrids() const; /** Sets the current frame of the grid sequence to use. */ @@ -195,11 +191,11 @@ namespace Falcor /** Set the density grid. */ - void setDensityGrid(const Grid::SharedPtr& densityGrid) { setGrid(GridSlot::Density, densityGrid); }; + void setDensityGrid(const ref& densityGrid) { setGrid(GridSlot::Density, densityGrid); }; /** Get the density grid. */ - const Grid::SharedPtr& getDensityGrid() const { return getGrid(GridSlot::Density); } + const ref& getDensityGrid() const { return getGrid(GridSlot::Density); } /** Set the density scale factor. */ @@ -211,11 +207,11 @@ namespace Falcor /** Set the emission grid. */ - void setEmissionGrid(const Grid::SharedPtr& emissionGrid) { setGrid(GridSlot::Emission, emissionGrid); } + void setEmissionGrid(const ref& emissionGrid) { setGrid(GridSlot::Emission, emissionGrid); } /** Get the emission grid. */ - const Grid::SharedPtr& getEmissionGrid() const { return getGrid(GridSlot::Emission); } + const ref& getEmissionGrid() const { return getGrid(GridSlot::Emission); } /** Set the emission scale factor. */ @@ -265,18 +261,16 @@ namespace Falcor */ const AABB& getBounds() const { return mBounds; } - void updateFromAnimation(const rmcv::mat4& transform) override; + void updateFromAnimation(const float4x4& transform) override; private: - GridVolume(std::shared_ptr pDevice, const std::string& name); - void updateSequence(); void updateBounds(); void markUpdates(UpdateFlags updates); void setFlags(uint32_t flags); - std::shared_ptr mpDevice; + ref mpDevice; std::string mName; std::array mGrids; uint32_t mGridFrame = 0; diff --git a/Source/Falcor/Testing/UnitTest.cpp b/Source/Falcor/Testing/UnitTest.cpp index 67e9ff381..ac9bb6377 100644 --- a/Source/Falcor/Testing/UnitTest.cpp +++ b/Source/Falcor/Testing/UnitTest.cpp @@ -191,7 +191,7 @@ inline void writeXmlReport(const std::filesystem::path& path, const std::vector< doc.save_file(path.native().c_str()); } -inline TestResult runTest(const Test& test, std::shared_ptr pDevice, Fbo* pTargetFbo) +inline TestResult runTest(const Test& test, ref pDevice, Fbo* pTargetFbo) { if (!test.skipMessage.empty()) return {TestResult::Status::Skipped, {test.skipMessage}}; @@ -263,7 +263,7 @@ inline TestResult runTest(const Test& test, std::shared_ptr pDevice, Fbo } int32_t runTests( - std::shared_ptr pDevice, + ref pDevice, Fbo* pTargetFbo, UnitTestCategoryFlags categoryFlags, const std::string& testFilter, @@ -448,7 +448,7 @@ void GPUUnitTestContext::createProgram(const Program::Desc& desc, const Program: void GPUUnitTestContext::createVars() { // Create shader variables. - ProgramReflection::SharedConstPtr pReflection = mpProgram->getReflector(); + ref pReflection = mpProgram->getReflector(); mpVars = ComputeVars::create(mpDevice, pReflection); FALCOR_ASSERT(mpVars); @@ -461,7 +461,7 @@ void GPUUnitTestContext::createVars() void GPUUnitTestContext::allocateStructuredBuffer(const std::string& name, uint32_t nElements, const void* pInitData, size_t initDataSize) { checkInvariant(mpVars != nullptr, "Program vars not created"); - mStructuredBuffers[name].pBuffer = Buffer::createStructured(mpDevice.get(), mpProgram.get(), name, nElements); + mStructuredBuffers[name].pBuffer = Buffer::createStructured(mpDevice, mpProgram.get(), name, nElements); FALCOR_ASSERT(mStructuredBuffers[name].pBuffer); if (pInitData) { @@ -485,7 +485,7 @@ void GPUUnitTestContext::runProgram(const uint3& dimensions) uint3 groups = div_round_up(dimensions, mThreadGroupSize); // // Check dispatch dimensions. - if (glm::any(glm::greaterThan(groups, mpDevice->getLimits().maxComputeDispatchThreadGroups))) + if (any(groups > mpDevice->getLimits().maxComputeDispatchThreadGroups)) { throw ErrorRunningTestException("GPUUnitTestContext::runProgram() - Dispatch dimension exceeds maximum."); } diff --git a/Source/Falcor/Testing/UnitTest.h b/Source/Falcor/Testing/UnitTest.h index cee91785d..d59758b0a 100644 --- a/Source/Falcor/Testing/UnitTest.h +++ b/Source/Falcor/Testing/UnitTest.h @@ -35,7 +35,6 @@ #include "Utils/Math/Vector.h" #include "Utils/StringFormatters.h" -#include #include #include @@ -122,7 +121,7 @@ FALCOR_API void registerGPUTest( UnitTestDeviceFlags supportedDevices ); FALCOR_API int32_t runTests( - std::shared_ptr pDevice, + ref pDevice, Fbo* pTargetFbo, UnitTestCategoryFlags categoryFlags, const std::string& testFilterRegexp, @@ -154,7 +153,7 @@ class FALCOR_API CPUUnitTestContext : public UnitTestContext class FALCOR_API GPUUnitTestContext : public UnitTestContext { public: - GPUUnitTestContext(std::shared_ptr pDevice, Fbo* pTargetFbo) : mpDevice(std::move(pDevice)), mpTargetFbo(pTargetFbo) {} + GPUUnitTestContext(ref pDevice, Fbo* pTargetFbo) : mpDevice(pDevice), mpTargetFbo(pTargetFbo) {} /** * createProgram creates a compute program from the source code at the @@ -255,7 +254,7 @@ class FALCOR_API GPUUnitTestContext : public UnitTestContext /** * Returns the current Falcor render device. */ - const std::shared_ptr& getDevice() const { return mpDevice; } + const ref& getDevice() const { return mpDevice; } /** * Returns the current Falcor render context. @@ -276,16 +275,16 @@ class FALCOR_API GPUUnitTestContext : public UnitTestContext const void* mapRawRead(const char* bufferName); // Internal state - std::shared_ptr mpDevice; + ref mpDevice; Fbo* mpTargetFbo; - ComputeState::SharedPtr mpState; - ComputeProgram::SharedPtr mpProgram; - ComputeVars::SharedPtr mpVars; + ref mpState; + ref mpProgram; + ref mpVars; uint3 mThreadGroupSize = {0, 0, 0}; struct ParameterBuffer { - Buffer::SharedPtr pBuffer; + ref pBuffer; bool mapped = false; }; std::map mStructuredBuffers; @@ -393,6 +392,32 @@ inline std::optional createBoolMessage(std::string_view valueStr, b #define FTEST_TEST_BOOLEAN(valueStr, value, expected, userFailMsg, isAssert) \ FTEST_MESSAGE(::Falcor::unittest::createBoolMessage(valueStr, value, expected), userFailMsg, isAssert) +struct CmpHelperEQ +{ + template + static bool compare(const TLhs& lhs, const TRhs& rhs) + { + if constexpr (std::is_same_v) + return ::std::equal_to{}(lhs, rhs); + else + return lhs == rhs; + } + static const char* asString() { return "=="; } +}; + +struct CmpHelperNE +{ + template + static bool compare(const TLhs& lhs, const TRhs& rhs) + { + if constexpr (std::is_same_v) + return ::std::not_equal_to{}(lhs, rhs); + else + return lhs != rhs; + } + static const char* asString() { return "!="; } +}; + #define FTEST_COMPARISON_HELPER(opName, op) \ struct CmpHelper##opName \ { \ @@ -407,8 +432,6 @@ inline std::optional createBoolMessage(std::string_view valueStr, b } \ } -FTEST_COMPARISON_HELPER(EQ, ==); -FTEST_COMPARISON_HELPER(NE, !=); FTEST_COMPARISON_HELPER(LE, <=); FTEST_COMPARISON_HELPER(GE, >=); FTEST_COMPARISON_HELPER(LT, <); diff --git a/Source/Falcor/Utils/Algorithm/BitonicSort.cpp b/Source/Falcor/Utils/Algorithm/BitonicSort.cpp index 3781e0d00..0990d3b4a 100644 --- a/Source/Falcor/Utils/Algorithm/BitonicSort.cpp +++ b/Source/Falcor/Utils/Algorithm/BitonicSort.cpp @@ -33,60 +33,61 @@ namespace Falcor { - static const char kShaderFilename[] = "Utils/Algorithm/BitonicSort.cs.slang"; +static const char kShaderFilename[] = "Utils/Algorithm/BitonicSort.cs.slang"; - BitonicSort::BitonicSort(std::shared_ptr pDevice) - : mpDevice(std::move(pDevice)) - { +BitonicSort::BitonicSort(ref pDevice) : mpDevice(pDevice) +{ #if !FALCOR_NVAPI_AVAILABLE - throw RuntimeError("BitonicSort requires NVAPI. See installation instructions in README."); + throw RuntimeError("BitonicSort requires NVAPI. See installation instructions in README."); #endif - mSort.pState = ComputeState::create(mpDevice); + mSort.pState = ComputeState::create(mpDevice); - // 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"); - mSort.pProgram = ComputeProgram::createFromFile(mpDevice, kShaderFilename, "main", defines); - mSort.pState->setProgram(mSort.pProgram); - mSort.pVars = ComputeVars::create(mpDevice, mSort.pProgram.get()); - } + // 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"); + mSort.pProgram = ComputeProgram::createFromFile(mpDevice, kShaderFilename, "main", defines); + mSort.pState->setProgram(mSort.pProgram); + mSort.pVars = ComputeVars::create(mpDevice, mSort.pProgram.get()); +} - bool BitonicSort::execute(RenderContext* pRenderContext, Buffer::SharedPtr pData, uint32_t totalSize, uint32_t chunkSize, uint32_t groupSize) - { - FALCOR_PROFILE(pRenderContext, "BitonicSort::execute"); +bool BitonicSort::execute(RenderContext* pRenderContext, ref pData, uint32_t totalSize, uint32_t chunkSize, uint32_t groupSize) +{ + FALCOR_PROFILE(pRenderContext, "BitonicSort::execute"); - // Validate inputs. - FALCOR_ASSERT(pRenderContext); - FALCOR_ASSERT(pData); - FALCOR_ASSERT(chunkSize >= 1 && chunkSize <= groupSize && isPowerOf2(chunkSize)); - FALCOR_ASSERT(groupSize >= 1 && groupSize <= 1024 && isPowerOf2(groupSize)); + // Validate inputs. + FALCOR_ASSERT(pRenderContext); + FALCOR_ASSERT(pData); + FALCOR_ASSERT(chunkSize >= 1 && chunkSize <= groupSize && isPowerOf2(chunkSize)); + FALCOR_ASSERT(groupSize >= 1 && groupSize <= 1024 && isPowerOf2(groupSize)); - // Early out if there is nothing to be done. - if (totalSize == 0 || chunkSize <= 1) return true; + // 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)); + // 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); - FALCOR_ASSERT(groupsX * groupsY * groupSize >= totalSize); + // 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); + FALCOR_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; + // Constants. The buffer size as a runtime constant as it may be variable and we don't want to recompile each time it changes. + auto var = mSort.pVars->getRootVar(); + var["CB"]["gTotalSize"] = totalSize; + var["CB"]["gDispatchX"] = groupsX; - // Bind the data. - bool success = mSort.pVars->setBuffer("gData", pData); - FALCOR_ASSERT(success); + // Bind the data. + bool success = mSort.pVars->setBuffer("gData", pData); + FALCOR_ASSERT(success); - // Execute. - pRenderContext->dispatch(mSort.pState.get(), mSort.pVars.get(), {groupsX, groupsY, 1}); + // Execute. + pRenderContext->dispatch(mSort.pState.get(), mSort.pVars.get(), {groupsX, groupsY, 1}); - return true; - } + return true; } +} // namespace Falcor diff --git a/Source/Falcor/Utils/Algorithm/BitonicSort.cs.slang b/Source/Falcor/Utils/Algorithm/BitonicSort.cs.slang index d4d8dc9ac..5e3078765 100644 --- a/Source/Falcor/Utils/Algorithm/BitonicSort.cs.slang +++ b/Source/Falcor/Utils/Algorithm/BitonicSort.cs.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-21, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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,17 @@ # 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. -*/ +/** + * 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. + */ #include "Utils/NVAPI.slangh" // We need this to get shuffle-xor operations. #if (NV_WARP_SIZE != 32) @@ -50,47 +51,49 @@ cbuffer CB { - uint gTotalSize; ///< Total number of elements. - uint gDispatchX; ///< Number of thread groups in dispatch dimension X. + 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. +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). -*/ +/** + * 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) { for (; j > 0; j >>= 1) { // Get index of sorting partner in chunk - uint value_ixj = NvShflXor(value, j); // Value from current lane ^ j + 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; + if (pred) + value = value_ixj; } } -/** In-place bitonic sort. -*/ +/** + * In-place bitonic sort. + */ [numthreads(GROUP_SIZE, 1, 1)] -void main(uint3 groupID : SV_GroupID, uint groupIdx : SV_GroupIndex) +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 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 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). + 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. @@ -101,11 +104,11 @@ void main(uint3 groupID : SV_GroupID, uint groupIdx : SV_GroupIndex) } // Major steps for k = {2,4,...,32} are done within warp. - for (uint k = 2; k <= min(N,32); k <<= 1) + 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 + const bool dir = ((i & k) == 0); // Sort ascending (true) or descending (false) + uint j = k >> 1; // j <= 16 bitonicSortInWarp(value, i, j, dir); } @@ -117,7 +120,7 @@ void main(uint3 groupID : SV_GroupID, uint groupIdx : SV_GroupIndex) // Major steps for k = {64,128,...N} are done in shared memory. for (uint k = 64; k <= N; k <<= 1) { - const bool dir = ((i & k) == 0); // Sort ascending (true) or descending (false) + 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; @@ -130,7 +133,8 @@ void main(uint3 groupID : SV_GroupID, uint groupIdx : SV_GroupIndex) // Decide whether to swap. See comments in bitonicSortInWarp(). bool pred = (((i & j) == 0) != dir) == value < value_ixj; - if (pred) 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) diff --git a/Source/Falcor/Utils/Algorithm/BitonicSort.h b/Source/Falcor/Utils/Algorithm/BitonicSort.h index fb4027358..603a28dd8 100644 --- a/Source/Falcor/Utils/Algorithm/BitonicSort.h +++ b/Source/Falcor/Utils/Algorithm/BitonicSort.h @@ -34,40 +34,44 @@ namespace Falcor { - class RenderContext; +class RenderContext; - /** 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. - */ - class FALCOR_API BitonicSort - { - public: - /// Constructor. Throws an exception on error. - BitonicSort(std::shared_ptr pDevice); +/** + * 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. + */ +class FALCOR_API BitonicSort +{ +public: + /// Constructor. Throws an exception on error. + BitonicSort(ref pDevice); - /** 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); + /** + * 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, ref pData, uint32_t totalSize, uint32_t chunkSize, uint32_t groupSize = 256); - private: - std::shared_ptr mpDevice; +private: + ref mpDevice; - struct - { - ComputeState::SharedPtr pState; - ComputeProgram::SharedPtr pProgram; - ComputeVars::SharedPtr pVars; - } mSort; - }; -} + struct + { + ref pState; + ref pProgram; + ref pVars; + } mSort; +}; +} // namespace Falcor diff --git a/Source/Falcor/Utils/Algorithm/DirectedGraph.h b/Source/Falcor/Utils/Algorithm/DirectedGraph.h index 403709a7d..13f12fa05 100644 --- a/Source/Falcor/Utils/Algorithm/DirectedGraph.h +++ b/Source/Falcor/Utils/Algorithm/DirectedGraph.h @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -35,195 +35,198 @@ namespace Falcor { - class DirectedGraph - { - public: - using SharedPtr = std::shared_ptr; - static const uint32_t kInvalidID = (uint32_t)-1; +class DirectedGraph +{ +public: + static constexpr uint32_t kInvalidID = (uint32_t)-1; - class Node; - class Edge; + class Node; + class Edge; - /** Create a new graph. - \return A new object, or throws an exception if creation failed. - */ - static SharedPtr create() + /** + * Add a node. + * The returned value is a unique identifier of the node + */ + uint32_t addNode() + { + mNodes[mCurrentNodeId] = Node(); + return mCurrentNodeId++; + } + + /** + * Remove a node from the graph. This function will also remove all the incoming and outgoing edges associated with the node + * @return A list of edges that were removed + */ + std::unordered_set removeNode(uint32_t id) + { + if (mNodes.find(id) == mNodes.end()) { - return SharedPtr(new DirectedGraph); + logWarning("Can't remove node from DirectGraph, node ID doesn't exist"); + return {}; } - /** Add a node. - The returned value is a unique identifier of the node - */ - uint32_t addNode() + std::unordered_set removedEdges; + // Find all the edges we need to remove + for (auto& edgeId : mNodes[id].mIncomingEdges) + findEdgesToRemove(mNodes[mEdges[edgeId].mSrc].mOutgoingEdges, id, removedEdges); + for (auto& edgeId : mNodes[id].mOutgoingEdges) + findEdgesToRemove(mNodes[mEdges[edgeId].mDst].mIncomingEdges, id, removedEdges); + + // Remove them + for (auto& edgeId : removedEdges) + removeEdge(edgeId); + + // Remove the index from the map + mNodes.erase(id); + + return removedEdges; + } + + /** + * Add an edge + */ + uint32_t addEdge(uint32_t srcNode, uint32_t dstNode) + { + if (mNodes.find(srcNode) == mNodes.end()) { - mNodes[mCurrentNodeId] = Node(); - return mCurrentNodeId++; + logWarning("Can't add an edge to DirectGraph, src node ID doesn't exist"); + return kInvalidID; } - /** Remove a node from the graph. This function will also remove all the incoming and outgoing edges associated with the node - \return A list of edges that were removed - */ - std::unordered_set removeNode(uint32_t id) + if (mNodes.find(dstNode) == mNodes.end()) { - if (mNodes.find(id) == mNodes.end()) - { - logWarning("Can't remove node from DirectGraph, node ID doesn't exist"); - return {}; - } - - std::unordered_set removedEdges; - // Find all the edges we need to remove - for (auto& edgeId : mNodes[id].mIncomingEdges) findEdgesToRemove(mNodes[mEdges[edgeId].mSrc].mOutgoingEdges, id, removedEdges); - for (auto& edgeId : mNodes[id].mOutgoingEdges) findEdgesToRemove(mNodes[mEdges[edgeId].mDst].mIncomingEdges, id, removedEdges); + logWarning("Can't add an edge to DirectGraph, src node ID doesn't exist"); + return kInvalidID; + } - // Remove them - for (auto& edgeId : removedEdges) removeEdge(edgeId); + mNodes[srcNode].mOutgoingEdges.push_back(mCurrentEdgeId); + mNodes[dstNode].mIncomingEdges.push_back(mCurrentEdgeId); - // Remove the index from the map - mNodes.erase(id); + mEdges[mCurrentEdgeId] = (Edge(srcNode, dstNode)); + return mCurrentEdgeId++; + } - return removedEdges; + /** + * Remove an edge + */ + void removeEdge(uint32_t edgeId) + { + if (mEdges.find(edgeId) == mEdges.end()) + { + logWarning("Can't remove edge from DirectedGraph, edge ID doesn't exist"); + return; } - /** Add an edge - */ - uint32_t addEdge(uint32_t srcNode, uint32_t dstNode) - { - if (mNodes.find(srcNode) == mNodes.end()) - { - logWarning("Can't add an edge to DirectGraph, src node ID doesn't exist"); - return kInvalidID; - } + const auto& edge = mEdges[edgeId]; + removeEdgeFromNode(edgeId, mNodes[edge.getDestNode()]); + removeEdgeFromNode(edgeId, mNodes[edge.getSourceNode()]); - if (mNodes.find(dstNode) == mNodes.end()) - { - logWarning("Can't add an edge to DirectGraph, src node ID doesn't exist"); - return kInvalidID; - } + mEdges.erase(edgeId); + } - mNodes[srcNode].mOutgoingEdges.push_back(mCurrentEdgeId); - mNodes[dstNode].mIncomingEdges.push_back(mCurrentEdgeId); + class Node + { + public: + Node() = default; + uint32_t getOutgoingEdgeCount() const { return (uint32_t)mOutgoingEdges.size(); } + uint32_t getIncomingEdgeCount() const { return (uint32_t)mIncomingEdges.size(); } - mEdges[mCurrentEdgeId] = (Edge(srcNode, dstNode)); - return mCurrentEdgeId++; - } + uint32_t getIncomingEdge(uint32_t i) const { return mIncomingEdges[i]; } + uint32_t getOutgoingEdge(uint32_t i) const { return mOutgoingEdges[i]; } - /** Remove an edge - */ - void removeEdge(uint32_t edgeId) - { - if (mEdges.find(edgeId) == mEdges.end()) - { - logWarning("Can't remove edge from DirectedGraph, edge ID doesn't exist"); - return; - } + private: + friend DirectedGraph; + std::vector mIncomingEdges; + std::vector mOutgoingEdges; + }; - const auto& edge = mEdges[edgeId]; - removeEdgeFromNode(edgeId, mNodes[edge.getDestNode()]); - removeEdgeFromNode(edgeId, mNodes[edge.getSourceNode()]); + class Edge + { + public: + Edge() = default; + uint32_t getSourceNode() const { return mSrc; } + uint32_t getDestNode() const { return mDst; } + + private: + friend DirectedGraph; + Edge(uint32_t s, uint32_t d) : mSrc(s), mDst(d) {} + uint32_t mSrc = kInvalidID; + uint32_t mDst = kInvalidID; + }; - mEdges.erase(edgeId); - } + /** + * Check if a node exists + */ + bool doesNodeExist(uint32_t nodeId) const { return mNodes.find(nodeId) != mNodes.end(); } - class Node - { - public: - Node() = default; - uint32_t getOutgoingEdgeCount() const { return (uint32_t)mOutgoingEdges.size(); } - uint32_t getIncomingEdgeCount() const { return (uint32_t)mIncomingEdges.size(); } - - uint32_t getIncomingEdge(uint32_t i) const { return mIncomingEdges[i]; } - uint32_t getOutgoingEdge(uint32_t i) const { return mOutgoingEdges[i]; } - private: - friend DirectedGraph; - std::vector mIncomingEdges; - std::vector mOutgoingEdges; - }; - - class Edge - { - public: - Edge() = default; - uint32_t getSourceNode() const { return mSrc; } - uint32_t getDestNode() const { return mDst; } - private: - friend DirectedGraph; - Edge(uint32_t s, uint32_t d) : mSrc(s), mDst(d) {} - uint32_t mSrc = kInvalidID; - uint32_t mDst = kInvalidID; - }; - - /** Check if a node exists - */ - bool doesNodeExist(uint32_t nodeId) const { return mNodes.find(nodeId) != mNodes.end(); } - - /** Check if an edge exists - */ - bool doesEdgeExist(uint32_t edgeId) const { return mEdges.find(edgeId) != mEdges.end(); } - - /** Get a node - */ - const Node* getNode(uint32_t nodeId) const + /** + * Check if an edge exists + */ + bool doesEdgeExist(uint32_t edgeId) const { return mEdges.find(edgeId) != mEdges.end(); } + + /** + * Get a node + */ + const Node* getNode(uint32_t nodeId) const + { + if (doesNodeExist(nodeId) == false) { - if (doesNodeExist(nodeId) == false) - { - logWarning("DirectGraph::getNode() - node ID doesn't exist"); - return nullptr; - } - return &mNodes.at(nodeId); + logWarning("DirectGraph::getNode() - node ID doesn't exist"); + return nullptr; } + return &mNodes.at(nodeId); + } - /** Get an edge - */ - const Edge* getEdge(uint32_t edgeId) + /** + * Get an edge + */ + const Edge* getEdge(uint32_t edgeId) const + { + if (doesEdgeExist(edgeId) == false) { - if (doesEdgeExist(edgeId) == false) - { - logWarning("DirectGraph::getEdge() - edge ID doesn't exist"); - return nullptr; - } - return &mEdges[edgeId]; + logWarning("DirectGraph::getEdge() - edge ID doesn't exist"); + return nullptr; } + return &mEdges.at(edgeId); + } - uint32_t getCurrentNodeId() const { return mCurrentNodeId; } - uint32_t getCurrentEdgeId() const { return mCurrentEdgeId; } - private: - DirectedGraph() = default; + uint32_t getCurrentNodeId() const { return mCurrentNodeId; } + uint32_t getCurrentEdgeId() const { return mCurrentEdgeId; } - std::unordered_map mNodes; - std::unordered_map mEdges; - uint32_t mCurrentNodeId = 0; - uint32_t mCurrentEdgeId = 0; +private: + std::unordered_map mNodes; + std::unordered_map mEdges; + uint32_t mCurrentNodeId = 0; + uint32_t mCurrentEdgeId = 0; - template - void findEdgesToRemove(std::vector& edges, uint32_t nodeToRemove, std::unordered_set& removedEdges) + template + void findEdgesToRemove(std::vector& edges, uint32_t nodeToRemove, std::unordered_set& removedEdges) + { + for (size_t i = 0; i < edges.size(); i++) { - for (size_t i = 0; i < edges.size(); i++) + uint32_t edgeId = edges[i]; + const auto& edge = mEdges[edgeId]; + auto& otherNode = removeSrc ? edge.mSrc : edge.mDst; + if (otherNode == nodeToRemove) { - uint32_t edgeId = edges[i]; - const auto& edge = mEdges[edgeId]; - auto& otherNode = removeSrc ? edge.mSrc : edge.mDst; - if (otherNode == nodeToRemove) - { - removedEdges.insert(edges[i]); - } + removedEdges.insert(edges[i]); } } + } - template - void removeEdgeFromNode(uint32_t edgeId, Node& node) + template + void removeEdgeFromNode(uint32_t edgeId, Node& node) + { + auto& vec = removeInput ? node.mIncomingEdges : node.mOutgoingEdges; + for (auto e = vec.begin(); e != vec.end(); e++) { - auto& vec = removeInput ? node.mIncomingEdges : node.mOutgoingEdges; - for (auto e = vec.begin(); e != vec.end(); e++) + if (*e == edgeId) { - if (*e == edgeId) - { - vec.erase(e); - return; - } + vec.erase(e); + return; } - FALCOR_UNREACHABLE(); } - }; -} + FALCOR_UNREACHABLE(); + } +}; +} // namespace Falcor diff --git a/Source/Falcor/Utils/Algorithm/DirectedGraphTraversal.h b/Source/Falcor/Utils/Algorithm/DirectedGraphTraversal.h index 220bbdb5d..a96621d5e 100644 --- a/Source/Falcor/Utils/Algorithm/DirectedGraphTraversal.h +++ b/Source/Falcor/Utils/Algorithm/DirectedGraphTraversal.h @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -35,201 +35,206 @@ namespace Falcor { - class DirectedGraphTraversal +class DirectedGraphTraversal +{ +public: + enum class Flags { - public: - enum class Flags - { - None = 0x0, - Reverse = 0x1, - IgnoreVisited = 0x2, - }; + None = 0x0, + Reverse = 0x1, + IgnoreVisited = 0x2, + }; - DirectedGraphTraversal(const DirectedGraph::SharedPtr pGraph, Flags flags) : mpGraph(pGraph), mFlags(flags) - { - } + DirectedGraphTraversal(const DirectedGraph& graph, Flags flags) : mGraph(graph), mFlags(flags) {} + +protected: + virtual ~DirectedGraphTraversal() {} - protected: - virtual ~DirectedGraphTraversal() {} + const DirectedGraph& mGraph; + Flags mFlags; + std::vector mVisited; - typename DirectedGraph::SharedPtr mpGraph; - Flags mFlags; - std::vector mVisited; + bool reset(uint32_t rootNode) + { + if (mGraph.doesNodeExist(rootNode) == false) + return false; - bool reset(uint32_t rootNode) + if ((uint32_t)mFlags & (uint32_t)Flags::IgnoreVisited) { - if (mpGraph->doesNodeExist(rootNode) == false) return false; + mVisited.assign(mGraph.getCurrentNodeId(), false); + } - if ((uint32_t)mFlags & (uint32_t)Flags::IgnoreVisited) - { - mVisited.assign(mpGraph->getCurrentNodeId(), false); - } + return true; + } +}; - return true; - } - }; +FALCOR_ENUM_CLASS_OPERATORS(DirectedGraphTraversal::Flags); - FALCOR_ENUM_CLASS_OPERATORS(DirectedGraphTraversal::Flags); +template +class DirectedGraphTraversalTemplate : public DirectedGraphTraversal +{ +public: + DirectedGraphTraversalTemplate(const DirectedGraph& graph, uint32_t rootNode, Flags flags = Flags::None) + : DirectedGraphTraversal(graph, flags) + { + reset(rootNode); + } + ~DirectedGraphTraversalTemplate() = default; - template - class DirectedGraphTraversalTemplate : public DirectedGraphTraversal + uint32_t traverse() { - public: - DirectedGraphTraversalTemplate(const DirectedGraph::SharedPtr pGraph, uint32_t rootNode, Flags flags = Flags::None) : DirectedGraphTraversal(pGraph, flags) + if (mNodeList.empty()) { - reset(rootNode); + return DirectedGraph::kInvalidID; } - ~DirectedGraphTraversalTemplate() = default; - uint32_t traverse() + uint32_t curNode = Args::getTop(mNodeList); + if (is_set(mFlags, Flags::IgnoreVisited)) { - if (mNodeList.empty()) + while (mVisited[curNode]) { - return DirectedGraph::kInvalidID; - } - - uint32_t curNode = Args::getTop(mNodeList); - if (is_set(mFlags, Flags::IgnoreVisited)) - { - while (mVisited[curNode]) + mNodeList.pop(); + if (mNodeList.empty()) { - mNodeList.pop(); - if (mNodeList.empty()) - { - return DirectedGraph::kInvalidID; - } - curNode = Args::getTop(mNodeList); + return DirectedGraph::kInvalidID; } - - mVisited[curNode] = true; - } - mNodeList.pop(); - - // Insert all the children - const DirectedGraph::Node* pNode = mpGraph->getNode(curNode); - bool reverse = is_set(mFlags, Flags::Reverse); - uint32_t edgeCount = reverse ? pNode->getIncomingEdgeCount() : pNode->getOutgoingEdgeCount(); - - for (uint32_t i = 0; i < edgeCount; i++) - { - uint32_t e = reverse ? pNode->getIncomingEdge(i) : pNode->getOutgoingEdge(i); - const DirectedGraph::Edge* pEdge = mpGraph->getEdge(e); - uint32_t child = reverse ? pEdge->getSourceNode() : pEdge->getDestNode(); - mNodeList.push(child); + curNode = Args::getTop(mNodeList); } - return curNode; + mVisited[curNode] = true; } + mNodeList.pop(); - bool reset(uint32_t rootNode) + // Insert all the children + const DirectedGraph::Node* pNode = mGraph.getNode(curNode); + bool reverse = is_set(mFlags, Flags::Reverse); + uint32_t edgeCount = reverse ? pNode->getIncomingEdgeCount() : pNode->getOutgoingEdgeCount(); + + for (uint32_t i = 0; i < edgeCount; i++) { - bool b = DirectedGraphTraversal::reset(rootNode); - mNodeList = decltype(mNodeList)(); - if(b) mNodeList.push(rootNode); - return b; + uint32_t e = reverse ? pNode->getIncomingEdge(i) : pNode->getOutgoingEdge(i); + const DirectedGraph::Edge* pEdge = mGraph.getEdge(e); + uint32_t child = reverse ? pEdge->getSourceNode() : pEdge->getDestNode(); + mNodeList.push(child); } - private: - typename Args::Container mNodeList; - }; - struct DfsArgs - { - using Container = std::stack; - static const std::string getName() { return "DFS"; } - static const uint32_t& getTop(const Container& c) { return c.top(); }; - }; - using DirectedGraphDfsTraversal = DirectedGraphTraversalTemplate; + return curNode; + } - struct BfsArgs + bool reset(uint32_t rootNode) { - using Container = std::queue; - static const std::string getName() { return "BFS"; } - static const uint32_t& getTop(const Container& c) { return c.front(); }; - }; - using DirectedGraphBfsTraversal = DirectedGraphTraversalTemplate; + bool b = DirectedGraphTraversal::reset(rootNode); + mNodeList = decltype(mNodeList)(); + if (b) + mNodeList.push(rootNode); + return b; + } + +private: + typename Args::Container mNodeList; +}; + +struct DfsArgs +{ + using Container = std::stack; + static const std::string getName() { return "DFS"; } + static const uint32_t& getTop(const Container& c) { return c.top(); }; +}; +using DirectedGraphDfsTraversal = DirectedGraphTraversalTemplate; + +struct BfsArgs +{ + using Container = std::queue; + static const std::string getName() { return "BFS"; } + static const uint32_t& getTop(const Container& c) { return c.front(); }; +}; +using DirectedGraphBfsTraversal = DirectedGraphTraversalTemplate; - class DirectedGraphLoopDetector +class DirectedGraphLoopDetector +{ +public: + static bool hasLoop(const DirectedGraph& graph, uint32_t rootNode) { - public: - static bool hasLoop(const DirectedGraph::SharedPtr pGraph, uint32_t rootNode) + DirectedGraphDfsTraversal dfs(graph, rootNode); + // Skip the first node since it's the root + uint32_t n = dfs.traverse(); + while (n != DirectedGraph::kInvalidID) { - DirectedGraphDfsTraversal dfs(pGraph, rootNode); - // Skip the first node since it's the root - uint32_t n = dfs.traverse(); - while (n != DirectedGraph::kInvalidID) - { - n = dfs.traverse(); - if (n == rootNode) return true; - } - - return false; + n = dfs.traverse(); + if (n == rootNode) + return true; } - }; - class DirectedGraphTopologicalSort + return false; + } +}; + +class DirectedGraphTopologicalSort +{ +public: + static std::vector sort(const DirectedGraph& graph) { - public: - static std::vector sort(DirectedGraph* pGraph) + DirectedGraphTopologicalSort ts(graph); + for (uint32_t i = 0; i < ts.mGraph.getCurrentNodeId(); i++) { - DirectedGraphTopologicalSort ts(pGraph); - for (uint32_t i = 0; i < ts.mpGraph->getCurrentNodeId(); i++) + if (ts.mVisited[i] == false && ts.mGraph.getNode(i)) { - if (ts.mVisited[i] == false && ts.mpGraph->getNode(i)) - { - ts.sortInternal(i); - } - } - - std::vector result; - result.reserve(ts.mStack.size()); - while (ts.mStack.empty() == false) - { - result.push_back(ts.mStack.top()); - ts.mStack.pop(); + ts.sortInternal(i); } - return result; } - private: - DirectedGraphTopologicalSort(DirectedGraph* pGraph) : mpGraph(pGraph), mVisited(pGraph->getCurrentNodeId(), false) {} - DirectedGraph* mpGraph; - std::stack mStack; - std::vector mVisited; - void sortInternal(uint32_t node) + std::vector result; + result.reserve(ts.mStack.size()); + while (ts.mStack.empty() == false) { - mVisited[node] = true; - const DirectedGraph::Node* pNode = mpGraph->getNode(node); - for (uint32_t e = 0; e < pNode->getOutgoingEdgeCount(); e++) - { - uint32_t nextNode = mpGraph->getEdge(pNode->getOutgoingEdge(e))->getDestNode(); - if (!mVisited[nextNode]) - { - sortInternal(nextNode); - } - } - - mStack.push(node); + result.push_back(ts.mStack.top()); + ts.mStack.pop(); } - }; + return result; + } + +private: + DirectedGraphTopologicalSort(const DirectedGraph& graph) : mGraph(graph), mVisited(graph.getCurrentNodeId(), false) {} + const DirectedGraph& mGraph; + std::stack mStack; + std::vector mVisited; - namespace DirectedGraphPathDetector + void sortInternal(uint32_t node) { - inline bool hasPath(const DirectedGraph::SharedPtr& pGraph, uint32_t from, uint32_t to) + mVisited[node] = true; + const DirectedGraph::Node* pNode = mGraph.getNode(node); + for (uint32_t e = 0; e < pNode->getOutgoingEdgeCount(); e++) { - DirectedGraphDfsTraversal dfs(pGraph, from, DirectedGraphDfsTraversal::Flags::IgnoreVisited); - uint32_t node = dfs.traverse(); - node = dfs.traverse(); // skip the root node - while (node != DirectedGraph::kInvalidID) + uint32_t nextNode = mGraph.getEdge(pNode->getOutgoingEdge(e))->getDestNode(); + if (!mVisited[nextNode]) { - if (node == to) return true; - node = dfs.traverse(); + sortInternal(nextNode); } - return false; } - inline bool hasCycle(const DirectedGraph::SharedPtr& pGraph, uint32_t root) - { - return hasPath(pGraph, root, root); - } - }; + mStack.push(node); + } +}; + +namespace DirectedGraphPathDetector +{ +inline bool hasPath(const DirectedGraph& graph, uint32_t from, uint32_t to) +{ + DirectedGraphDfsTraversal dfs(graph, from, DirectedGraphDfsTraversal::Flags::IgnoreVisited); + uint32_t node = dfs.traverse(); + node = dfs.traverse(); // skip the root node + while (node != DirectedGraph::kInvalidID) + { + if (node == to) + return true; + node = dfs.traverse(); + } + return false; +} + +inline bool hasCycle(const DirectedGraph& graph, uint32_t root) +{ + return hasPath(graph, root, root); } +}; // namespace DirectedGraphPathDetector +} // namespace Falcor diff --git a/Source/Falcor/Utils/Algorithm/ParallelReduction.cpp b/Source/Falcor/Utils/Algorithm/ParallelReduction.cpp index ffee1a7c5..8820b42db 100644 --- a/Source/Falcor/Utils/Algorithm/ParallelReduction.cpp +++ b/Source/Falcor/Utils/Algorithm/ParallelReduction.cpp @@ -34,174 +34,192 @@ namespace Falcor { - static const char kShaderFile[] = "Utils/Algorithm/ParallelReduction.cs.slang"; +static const char kShaderFile[] = "Utils/Algorithm/ParallelReduction.cs.slang"; - ParallelReduction::ParallelReduction(std::shared_ptr pDevice) - : mpDevice(std::move(pDevice)) - { - // Create the programs. - // Set defines to avoid compiler warnings about undefined macros. Proper values will be assigned at runtime. - Program::DefineList defines = { { "REDUCTION_TYPE", "1" }, { "FORMAT_CHANNELS", "1" }, { "FORMAT_TYPE", "1" } }; - mpInitialProgram = ComputeProgram::createFromFile(mpDevice, kShaderFile, "initialPass", defines, Shader::CompilerFlags::None); - mpFinalProgram = ComputeProgram::createFromFile(mpDevice, kShaderFile, "finalPass", defines, Shader::CompilerFlags::None); - mpVars = ComputeVars::create(mpDevice, mpInitialProgram.get()); - - // Check assumptions on thread group sizes. The initial pass is a 2D dispatch, the final pass a 1D. - FALCOR_ASSERT(mpInitialProgram->getReflector()->getThreadGroupSize().z == 1); - FALCOR_ASSERT(mpFinalProgram->getReflector()->getThreadGroupSize().y == 1 && mpFinalProgram->getReflector()->getThreadGroupSize().z == 1); - - mpState = ComputeState::create(mpDevice); - } +ParallelReduction::ParallelReduction(ref pDevice) : mpDevice(pDevice) +{ + // Create the programs. + // Set defines to avoid compiler warnings about undefined macros. Proper values will be assigned at runtime. + Program::DefineList defines = {{"REDUCTION_TYPE", "1"}, {"FORMAT_CHANNELS", "1"}, {"FORMAT_TYPE", "1"}}; + mpInitialProgram = ComputeProgram::createFromFile(mpDevice, kShaderFile, "initialPass", defines, Shader::CompilerFlags::None); + mpFinalProgram = ComputeProgram::createFromFile(mpDevice, kShaderFile, "finalPass", defines, Shader::CompilerFlags::None); + mpVars = ComputeVars::create(mpDevice, mpInitialProgram.get()); + + // Check assumptions on thread group sizes. The initial pass is a 2D dispatch, the final pass a 1D. + FALCOR_ASSERT(mpInitialProgram->getReflector()->getThreadGroupSize().z == 1); + FALCOR_ASSERT( + mpFinalProgram->getReflector()->getThreadGroupSize().y == 1 && mpFinalProgram->getReflector()->getThreadGroupSize().z == 1 + ); + + mpState = ComputeState::create(mpDevice); +} - void ParallelReduction::allocate(uint32_t elementCount, uint32_t elementSize) +void ParallelReduction::allocate(uint32_t elementCount, uint32_t elementSize) +{ + if (mpBuffers[0] == nullptr || mpBuffers[0]->getElementCount() < elementCount * elementSize) { - if (mpBuffers[0] == nullptr || mpBuffers[0]->getElementCount() < elementCount * elementSize) + // Buffer 0 has one element per tile. + mpBuffers[0] = Buffer::createTyped(mpDevice, elementCount * elementSize); + mpBuffers[0]->setName("ParallelReduction::mpBuffers[0]"); + + // 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 * elementSize) { - // Buffer 0 has one element per tile. - mpBuffers[0] = Buffer::createTyped(mpDevice.get(), elementCount * elementSize); - mpBuffers[0]->setName("ParallelReduction::mpBuffers[0]"); - - // 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 * elementSize) - { - mpBuffers[1] = Buffer::createTyped(mpDevice.get(), numElem1 * elementSize); - mpBuffers[1]->setName("ParallelReduction::mpBuffers[1]"); - } + mpBuffers[1] = Buffer::createTyped(mpDevice, numElem1 * elementSize); + mpBuffers[1]->setName("ParallelReduction::mpBuffers[1]"); } } +} - template - void ParallelReduction::execute(RenderContext* pRenderContext, const Texture::SharedPtr& pInput, Type operation, T* pResult, Buffer::SharedPtr pResultBuffer, uint64_t resultOffset) - { - FALCOR_PROFILE(pRenderContext, "ParallelReduction::execute"); +template +void ParallelReduction::execute( + RenderContext* pRenderContext, + const ref& pInput, + Type operation, + T* pResult, + ref pResultBuffer, + uint64_t resultOffset +) +{ + FALCOR_PROFILE(pRenderContext, "ParallelReduction::execute"); - // Check texture array/mip/sample count. - if (pInput->getArraySize() != 1 || pInput->getMipCount() != 1 || pInput->getSampleCount() != 1) - { - throw RuntimeError("ParallelReduction::execute() - Input texture is unsupported."); - } + // Check texture array/mip/sample count. + if (pInput->getArraySize() != 1 || pInput->getMipCount() != 1 || pInput->getSampleCount() != 1) + { + throw RuntimeError("ParallelReduction::execute() - Input texture is unsupported."); + } - // 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: - throw RuntimeError("ParallelReduction::execute() - Input texture format unsupported."); - } + // 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: + throw RuntimeError("ParallelReduction::execute() - Input texture format unsupported."); + } - // Check that reduction type T is compatible with the resource format. - if (sizeof(typename 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))) - { - throw RuntimeError("ParallelReduction::execute() - Template type T is not compatible with resource format."); - } + // Check that reduction type T is compatible with the resource format. + if (sizeof(typename 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))) + { + throw RuntimeError("ParallelReduction::execute() - Template type T is not compatible with resource format."); + } - uint32_t reductionType = REDUCTION_TYPE_UNKNOWN; - uint32_t elementSize = 0; - switch (operation) - { - case Type::Sum: - reductionType = REDUCTION_TYPE_SUM; - elementSize = 1; - break; - case Type::MinMax: - reductionType = REDUCTION_TYPE_MINMAX; - elementSize = 2; - break; - default: - throw RuntimeError("ParallelReduction::execute() - Unknown reduction type."); - } + uint32_t reductionType = REDUCTION_TYPE_UNKNOWN; + uint32_t elementSize = 0; + switch (operation) + { + case Type::Sum: + reductionType = REDUCTION_TYPE_SUM; + elementSize = 1; + break; + case Type::MinMax: + reductionType = REDUCTION_TYPE_MINMAX; + elementSize = 2; + break; + default: + throw RuntimeError("ParallelReduction::execute() - Unknown reduction type."); + } - // Allocate intermediate buffers if needed. - const uint2 resolution = uint2(pInput->getWidth(), pInput->getHeight()); - FALCOR_ASSERT(resolution.x > 0 && resolution.y > 0); - FALCOR_ASSERT(elementSize > 0); + // Allocate intermediate buffers if needed. + const uint2 resolution = uint2(pInput->getWidth(), pInput->getHeight()); + FALCOR_ASSERT(resolution.x > 0 && resolution.y > 0); + FALCOR_ASSERT(elementSize > 0); - const uint2 numTiles = div_round_up(resolution, uint2(mpInitialProgram->getReflector()->getThreadGroupSize())); - allocate(numTiles.x * numTiles.y, elementSize); - FALCOR_ASSERT(mpBuffers[0]); - FALCOR_ASSERT(mpBuffers[1]); + const uint2 numTiles = div_round_up(resolution, mpInitialProgram->getReflector()->getThreadGroupSize().xy()); + allocate(numTiles.x * numTiles.y, elementSize); + FALCOR_ASSERT(mpBuffers[0]); + FALCOR_ASSERT(mpBuffers[1]); - // Configure program. - const uint32_t channelCount = getFormatChannelCount(pInput->getFormat()); - FALCOR_ASSERT(channelCount >= 1 && channelCount <= 4); + // Configure program. + const uint32_t channelCount = getFormatChannelCount(pInput->getFormat()); + FALCOR_ASSERT(channelCount >= 1 && channelCount <= 4); - Program::DefineList defines; - defines.add("REDUCTION_TYPE", std::to_string(reductionType)); - defines.add("FORMAT_CHANNELS", std::to_string(channelCount)); - defines.add("FORMAT_TYPE", std::to_string(formatType)); + Program::DefineList defines; + defines.add("REDUCTION_TYPE", std::to_string(reductionType)); + defines.add("FORMAT_CHANNELS", std::to_string(channelCount)); + defines.add("FORMAT_TYPE", std::to_string(formatType)); - mpInitialProgram->addDefines(defines); - mpFinalProgram->addDefines(defines); + mpInitialProgram->addDefines(defines); + mpFinalProgram->addDefines(defines); - // Initial pass: Reduction over tiles of pixels in input texture. - mpVars["PerFrameCB"]["gResolution"] = resolution; - mpVars["PerFrameCB"]["gNumTiles"] = numTiles; - mpVars["gInput"] = pInput; - mpVars->setBuffer("gInputBuffer", nullptr); // Unbind previously bound buffer from last call to execute() - mpVars->setBuffer("gResult", mpBuffers[0]); + // Initial pass: Reduction over tiles of pixels in input texture. + { + auto var = mpVars->getRootVar(); + var["PerFrameCB"]["gResolution"] = resolution; + var["PerFrameCB"]["gNumTiles"] = numTiles; + var["gInput"] = pInput; + var["gInputBuffer"].setBuffer(nullptr); // Unbind previously bound buffer from last call to execute() + var["gResult"] = mpBuffers[0]; mpState->setProgram(mpInitialProgram); uint3 numGroups = div_round_up(uint3(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. - uint32_t elems = numTiles.x * numTiles.y; - uint32_t inputsBufferIndex = 0; + uint32_t inputsBufferIndex = 0; + // Final pass(es): Reduction by a factor N for each pass. + { + uint32_t elems = numTiles.x * numTiles.y; while (elems > 1) { - mpVars["PerFrameCB"]["gElems"] = elems; - mpVars->setBuffer("gInputBuffer", mpBuffers[inputsBufferIndex]); - mpVars->setBuffer("gResult", mpBuffers[1 - inputsBufferIndex]); + auto var = mpVars->getRootVar(); + var["PerFrameCB"]["gElems"] = elems; + var["gInputBuffer"] = mpBuffers[inputsBufferIndex]; + var["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 }); + pRenderContext->dispatch(mpState.get(), mpVars.get(), {numGroups, 1, 1}); inputsBufferIndex = 1 - inputsBufferIndex; elems = numGroups; } + } - size_t resultSize = elementSize * 16; + size_t resultSize = elementSize * 16; - // Copy the result to GPU buffer. - if (pResultBuffer) + // Copy the result to GPU buffer. + if (pResultBuffer) + { + if (resultOffset + resultSize > pResultBuffer->getSize()) { - if (resultOffset + resultSize > pResultBuffer->getSize()) - { - throw RuntimeError("ParallelReduction::execute() - Results buffer is too small."); - } - - pRenderContext->copyBufferRegion(pResultBuffer.get(), resultOffset, mpBuffers[inputsBufferIndex].get(), 0, resultSize); + throw RuntimeError("ParallelReduction::execute() - Results buffer is too small."); } - // Read back the result to the CPU. - if (pResult) - { - const T* pBuf = static_cast(mpBuffers[inputsBufferIndex]->map(Buffer::MapType::Read)); - FALCOR_ASSERT(pBuf); - std::memcpy(pResult, pBuf, resultSize); - mpBuffers[inputsBufferIndex]->unmap(); - } + pRenderContext->copyBufferRegion(pResultBuffer.get(), resultOffset, mpBuffers[inputsBufferIndex].get(), 0, resultSize); } - // Explicit template instantiation of the supported types. - template FALCOR_API void ParallelReduction::execute(RenderContext* pRenderContext, const Texture::SharedPtr& pInput, Type operation, float4* pResult, Buffer::SharedPtr pResultBuffer, uint64_t resultOffset); - template FALCOR_API void ParallelReduction::execute(RenderContext* pRenderContext, const Texture::SharedPtr& pInput, Type operation, int4* pResult, Buffer::SharedPtr pResultBuffer, uint64_t resultOffset); - template FALCOR_API void ParallelReduction::execute(RenderContext* pRenderContext, const Texture::SharedPtr& pInput, Type operation, uint4* pResult, Buffer::SharedPtr pResultBuffer, uint64_t resultOffset); + // Read back the result to the CPU. + if (pResult) + { + const T* pBuf = static_cast(mpBuffers[inputsBufferIndex]->map(Buffer::MapType::Read)); + FALCOR_ASSERT(pBuf); + std::memcpy(pResult, pBuf, resultSize); + mpBuffers[inputsBufferIndex]->unmap(); + } } + +// Explicit template instantiation of the supported types. +// clang-format off +template FALCOR_API void ParallelReduction::execute(RenderContext* pRenderContext, const ref& pInput, Type operation, float4* pResult, ref pResultBuffer, uint64_t resultOffset); +template FALCOR_API void ParallelReduction::execute(RenderContext* pRenderContext, const ref& pInput, Type operation, int4* pResult, ref pResultBuffer, uint64_t resultOffset); +template FALCOR_API void ParallelReduction::execute(RenderContext* pRenderContext, const ref& pInput, Type operation, uint4* pResult, ref pResultBuffer, uint64_t resultOffset); +// clang-format on +} // namespace Falcor diff --git a/Source/Falcor/Utils/Algorithm/ParallelReduction.cs.slang b/Source/Falcor/Utils/Algorithm/ParallelReduction.cs.slang index db2f37771..9ad96a797 100644 --- a/Source/Falcor/Utils/Algorithm/ParallelReduction.cs.slang +++ b/Source/Falcor/Utils/Algorithm/ParallelReduction.cs.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-21, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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,20 +26,21 @@ # 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.slangh. -*/ +/** + * 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.slangh. + */ #include "ParallelReductionType.slangh" #include "Utils/Math/MathConstants.slangh" 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. + 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. @@ -69,7 +70,6 @@ groupshared DataType gIntermediateCache[32 /* = 1024 / 32 */]; groupshared DataType gIntermediateCache[64 /* = 1024 / 32 * 2 */]; #endif - DataType loadTexture(uint2 pixelCoords) { DataType value = gInput[pixelCoords]; @@ -80,14 +80,16 @@ DataType loadTexture(uint2 pixelCoords) return value; } -/** Performs reduction within a thread group and writes single result to the results buffer at 'dstIdx'. -*/ +/** + * Performs reduction within a thread group and writes single result to the results buffer at 'dstIdx'. + */ void reduceSum(DataType value, uint dstIdx, uint groupThreadIdx) { // Add all elements within warp. Store result to shared memory. { value = WaveActiveSum(value); - if (WaveIsFirstLane()) gIntermediateCache[groupThreadIdx / 32] = value; + if (WaveIsFirstLane()) + gIntermediateCache[groupThreadIdx / 32] = value; } GroupMemoryBarrierWithGroupSync(); @@ -96,12 +98,14 @@ void reduceSum(DataType value, uint dstIdx, uint groupThreadIdx) { value = gIntermediateCache[groupThreadIdx]; value = WaveActiveSum(value); - if (groupThreadIdx == 0) gResult[dstIdx] = value; + if (groupThreadIdx == 0) + gResult[dstIdx] = value; } } -/** Take the min/max of all elements within a thread group and writes single result to the results buffer at 'dstIdx'. -*/ +/** + * Take the min/max of all elements within a thread group and writes single result to the results buffer at 'dstIdx'. + */ void reduceMinMax(DataType minValue, DataType maxValue, uint dstIdx, uint groupThreadIdx) { // Add all elements within warp. Store result to shared memory. @@ -132,7 +136,7 @@ void reduceMinMax(DataType minValue, DataType maxValue, uint dstIdx, uint groupT } [numthreads(32, 32, 1)] -void initialPass(uint3 globalThreadId : SV_DispatchThreadID, uint groupThreadIdx : SV_GroupIndex, uint3 groupId : SV_GroupID) +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; @@ -140,26 +144,29 @@ void initialPass(uint3 globalThreadId : SV_DispatchThreadID, uint groupThreadIdx #if REDUCTION_TYPE == REDUCTION_TYPE_SUM // Load input from texture in tiles of 32x32 pixels. DataType value = 0; - if (all(pixelCoords < gResolution)) value = loadTexture(pixelCoords); + if (all(pixelCoords < gResolution)) + value = loadTexture(pixelCoords); reduceSum(value, tileIdx, groupThreadIdx); #elif REDUCTION_TYPE == REDUCTION_TYPE_MINMAX // Load input from texture in tiles of 32x32 pixels. DataType minValue = kMaxValue; DataType maxValue = kMinValue; - if (all(pixelCoords < gResolution)) minValue = maxValue = loadTexture(pixelCoords); + if (all(pixelCoords < gResolution)) + minValue = maxValue = loadTexture(pixelCoords); reduceMinMax(minValue, maxValue, tileIdx, groupThreadIdx); #endif } [numthreads(1024, 1, 1)] -void finalPass(uint3 globalThreadId : SV_DispatchThreadID, uint groupThreadIdx : SV_GroupIndex, uint3 groupId : SV_GroupID) +void finalPass(uint3 globalThreadId: SV_DispatchThreadID, uint groupThreadIdx: SV_GroupIndex, uint3 groupId: SV_GroupID) { #if REDUCTION_TYPE == REDUCTION_TYPE_SUM // Load input from buffer written in previous pass. DataType value = 0; - if (globalThreadId.x < gElems) value = gInputBuffer[globalThreadId.x]; + if (globalThreadId.x < gElems) + value = gInputBuffer[globalThreadId.x]; reduceSum(value, groupId.x, groupThreadIdx); #elif REDUCTION_TYPE == REDUCTION_TYPE_MINMAX diff --git a/Source/Falcor/Utils/Algorithm/ParallelReduction.h b/Source/Falcor/Utils/Algorithm/ParallelReduction.h index 5a40a7442..c0c6e6931 100644 --- a/Source/Falcor/Utils/Algorithm/ParallelReduction.h +++ b/Source/Falcor/Utils/Algorithm/ParallelReduction.h @@ -35,62 +35,72 @@ 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 FALCOR_API ParallelReduction +/** + * 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 FALCOR_API ParallelReduction +{ +public: + enum class Type { - public: - enum class Type - { - Sum, - MinMax, - }; - - /// Constructor. Throws an exception on failure. - ParallelReduction(std::shared_ptr pDevice); - - /** Perform parallel reduction. - The computations are performed in type T, which must be compatible with the texture format: - - float4 for floating-point texture formats (float, snorm, unorm). - - uint4 for unsigned integer texture formats. - - int4 for signed integer texture formats. - - For the Sum operation, 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. + Sum, + MinMax, + }; - The size of the result buffer depends on the executed operation: - - Sum needs 16B - - MinMax needs 32B + /// Constructor. Throws an exception on failure. + ParallelReduction(ref pDevice); - \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 or 32B). - \param[out] resultOffset (Optional) Byte offset into pResultBuffer to where the result should be stored. - */ - template - void execute(RenderContext* pRenderContext, const Texture::SharedPtr& pInput, Type operation, T* pResult = nullptr, Buffer::SharedPtr pResultBuffer = nullptr, uint64_t resultOffset = 0); + /** + * Perform parallel reduction. + * The computations are performed in type T, which must be compatible with the texture format: + * - float4 for floating-point texture formats (float, snorm, unorm). + * - uint4 for unsigned integer texture formats. + * - int4 for signed integer texture formats. + * + * For the Sum operation, 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. + * + * The size of the result buffer depends on the executed operation: + * - Sum needs 16B + * - MinMax needs 32B + * + * @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 or 32B). + * @param[out] resultOffset (Optional) Byte offset into pResultBuffer to where the result should be stored. + */ + template + void execute( + RenderContext* pRenderContext, + const ref& pInput, + Type operation, + T* pResult = nullptr, + ref pResultBuffer = nullptr, + uint64_t resultOffset = 0 + ); - private: - void allocate(uint32_t elementCount, uint32_t elementSize); +private: + void allocate(uint32_t elementCount, uint32_t elementSize); - std::shared_ptr mpDevice; + ref mpDevice; - ComputeState::SharedPtr mpState; - ComputeProgram::SharedPtr mpInitialProgram; - ComputeProgram::SharedPtr mpFinalProgram; - ComputeVars::SharedPtr mpVars; + ref mpState; + ref mpInitialProgram; + ref mpFinalProgram; + ref mpVars; - Buffer::SharedPtr mpBuffers[2]; ///< Intermediate buffers for reduction iterations. - }; -} + ref mpBuffers[2]; ///< Intermediate buffers for reduction iterations. +}; +} // namespace Falcor diff --git a/Source/Falcor/Utils/Algorithm/ParallelReductionType.slangh b/Source/Falcor/Utils/Algorithm/ParallelReductionType.slangh index eec1b894e..367ac6380 100644 --- a/Source/Falcor/Utils/Algorithm/ParallelReductionType.slangh +++ b/Source/Falcor/Utils/Algorithm/ParallelReductionType.slangh @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-21, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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,10 +30,10 @@ // 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 +#define FORMAT_TYPE_FLOAT 1 +#define FORMAT_TYPE_SINT 2 +#define FORMAT_TYPE_UINT 3 -#define REDUCTION_TYPE_UNKNOWN 0 -#define REDUCTION_TYPE_SUM 1 -#define REDUCTION_TYPE_MINMAX 2 +#define REDUCTION_TYPE_UNKNOWN 0 +#define REDUCTION_TYPE_SUM 1 +#define REDUCTION_TYPE_MINMAX 2 diff --git a/Source/Falcor/Utils/Algorithm/PrefixSum.cpp b/Source/Falcor/Utils/Algorithm/PrefixSum.cpp index 8ebcb5f4f..d2a2d861d 100644 --- a/Source/Falcor/Utils/Algorithm/PrefixSum.cpp +++ b/Source/Falcor/Utils/Algorithm/PrefixSum.cpp @@ -33,121 +33,138 @@ namespace Falcor { - namespace +namespace +{ +const char kShaderFile[] = "Utils/Algorithm/PrefixSum.cs.slang"; +const uint32_t kGroupSize = 1024; +} // namespace + +PrefixSum::PrefixSum(ref pDevice) : mpDevice(pDevice) +{ + // Create shaders and state. + Program::DefineList defines = {{"GROUP_SIZE", std::to_string(kGroupSize)}}; + mpPrefixSumGroupProgram = ComputeProgram::createFromFile(mpDevice, kShaderFile, "groupScan", defines); + mpPrefixSumGroupVars = ComputeVars::create(mpDevice, mpPrefixSumGroupProgram.get()); + mpPrefixSumFinalizeProgram = ComputeProgram::createFromFile(mpDevice, kShaderFile, "finalizeGroups", defines); + mpPrefixSumFinalizeVars = ComputeVars::create(mpDevice, mpPrefixSumFinalizeProgram.get()); + + mpComputeState = ComputeState::create(mpDevice); + + // Create and bind buffer for per-group sums and total sum. + mpPrefixGroupSums = Buffer::create( + mpDevice, kGroupSize * sizeof(uint32_t), Resource::BindFlags::ShaderResource | Resource::BindFlags::UnorderedAccess, + Buffer::CpuAccess::None, nullptr + ); + mpTotalSum = Buffer::create(mpDevice, sizeof(uint32_t), Resource::BindFlags::UnorderedAccess, Buffer::CpuAccess::None, nullptr); + mpPrevTotalSum = Buffer::create(mpDevice, sizeof(uint32_t), Resource::BindFlags::ShaderResource, Buffer::CpuAccess::None, nullptr); + { - const char kShaderFile[] = "Utils/Algorithm/PrefixSum.cs.slang"; - const uint32_t kGroupSize = 1024; + auto var = mpPrefixSumGroupVars->getRootVar(); + var["gPrefixGroupSums"] = mpPrefixGroupSums; + var["gTotalSum"] = mpTotalSum; + var["gPrevTotalSum"] = mpPrevTotalSum; } - - PrefixSum::PrefixSum(std::shared_ptr pDevice) - : mpDevice(std::move(pDevice)) { - // Create shaders and state. - Program::DefineList defines = { {"GROUP_SIZE", std::to_string(kGroupSize)} }; - mpPrefixSumGroupProgram = ComputeProgram::createFromFile(mpDevice, kShaderFile, "groupScan", defines); - mpPrefixSumGroupVars = ComputeVars::create(mpDevice, mpPrefixSumGroupProgram.get()); - mpPrefixSumFinalizeProgram = ComputeProgram::createFromFile(mpDevice, kShaderFile, "finalizeGroups", defines); - mpPrefixSumFinalizeVars = ComputeVars::create(mpDevice, mpPrefixSumFinalizeProgram.get()); - - mpComputeState = ComputeState::create(mpDevice); - - // Create and bind buffer for per-group sums and total sum. - mpPrefixGroupSums = Buffer::create(mpDevice.get(), kGroupSize * sizeof(uint32_t), Resource::BindFlags::ShaderResource | Resource::BindFlags::UnorderedAccess, Buffer::CpuAccess::None, nullptr); - mpTotalSum = Buffer::create(mpDevice.get(), sizeof(uint32_t), Resource::BindFlags::UnorderedAccess, Buffer::CpuAccess::None, nullptr); - mpPrevTotalSum = Buffer::create(mpDevice.get(), sizeof(uint32_t), Resource::BindFlags::ShaderResource, Buffer::CpuAccess::None, nullptr); - - mpPrefixSumGroupVars["gPrefixGroupSums"] = mpPrefixGroupSums; - mpPrefixSumGroupVars["gTotalSum"] = mpTotalSum; - mpPrefixSumGroupVars["gPrevTotalSum"] = mpPrevTotalSum; - mpPrefixSumFinalizeVars["gPrefixGroupSums"] = mpPrefixGroupSums; - mpPrefixSumFinalizeVars["gTotalSum"] = mpTotalSum; - mpPrefixSumFinalizeVars["gPrevTotalSum"] = mpPrevTotalSum; + auto var = mpPrefixSumFinalizeVars->getRootVar(); + var["gPrefixGroupSums"] = mpPrefixGroupSums; + var["gTotalSum"] = mpTotalSum; + var["gPrevTotalSum"] = mpPrevTotalSum; } +} - void PrefixSum::execute(RenderContext* pRenderContext, Buffer::SharedPtr pData, uint32_t elementCount, uint32_t* pTotalSum, Buffer::SharedPtr pTotalSumBuffer, uint64_t pTotalSumOffset) - { - FALCOR_PROFILE(pRenderContext, "PrefixSum::execute"); +void PrefixSum::execute( + RenderContext* pRenderContext, + ref pData, + uint32_t elementCount, + uint32_t* pTotalSum, + ref pTotalSumBuffer, + uint64_t pTotalSumOffset +) +{ + FALCOR_PROFILE(pRenderContext, "PrefixSum::execute"); + + FALCOR_ASSERT(pRenderContext); + FALCOR_ASSERT(elementCount > 0); + FALCOR_ASSERT(pData && pData->getSize() >= elementCount * sizeof(uint32_t)); + + // Clear total sum to zero. + pRenderContext->clearUAV(mpTotalSum->getUAV().get(), uint4(0)); - FALCOR_ASSERT(pRenderContext); - FALCOR_ASSERT(elementCount > 0); - FALCOR_ASSERT(pData && pData->getSize() >= elementCount * sizeof(uint32_t)); + uint32_t maxElementCountPerIteration = kGroupSize * kGroupSize * 2; + uint32_t totalElementCount = elementCount; + uint32_t iterationsCount = div_round_up(totalElementCount, maxElementCountPerIteration); - // Clear total sum to zero. - pRenderContext->clearUAV(mpTotalSum->getUAV().get(), uint4(0)); + for (uint32_t iter = 0; iter < iterationsCount; iter++) + { + // Compute number of thread groups in the first pass. Each thread operates on two elements. + uint32_t numPrefixGroups = std::max(1u, div_round_up(std::min(elementCount, maxElementCountPerIteration), kGroupSize * 2)); + FALCOR_ASSERT(numPrefixGroups > 0 && numPrefixGroups <= kGroupSize); - uint32_t maxElementCountPerIteration = kGroupSize * kGroupSize * 2; - uint32_t totalElementCount = elementCount; - uint32_t iterationsCount = div_round_up(totalElementCount, maxElementCountPerIteration); + // Copy previus iterations total sum to read buffer. + pRenderContext->copyResource(mpPrevTotalSum.get(), mpTotalSum.get()); - for (uint32_t iter = 0; iter < iterationsCount; iter++) + // Pass 1: compute per-thread group prefix sums. { - // Compute number of thread groups in the first pass. Each thread operates on two elements. - uint32_t numPrefixGroups = std::max(1u, div_round_up(std::min(elementCount, maxElementCountPerIteration), kGroupSize * 2)); - FALCOR_ASSERT(numPrefixGroups > 0 && numPrefixGroups <= kGroupSize); - - // Copy previus iterations total sum to read buffer. - pRenderContext->copyResource(mpPrevTotalSum.get(), mpTotalSum.get()); - - // Pass 1: compute per-thread group prefix sums. - { - // Clear group sums to zero. - pRenderContext->clearUAV(mpPrefixGroupSums->getUAV().get(), uint4(0)); - - // Set constants and data. - mpPrefixSumGroupVars["CB"]["gNumGroups"] = numPrefixGroups; - mpPrefixSumGroupVars["CB"]["gTotalNumElems"] = totalElementCount; - mpPrefixSumGroupVars["CB"]["gIter"] = iter; - 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 uint32_t dispatchSizeX = (numPrefixGroups - 1) * 2; - FALCOR_ASSERT(dispatchSizeX > 0); - - // Set constants and data. - mpPrefixSumFinalizeVars["CB"]["gNumGroups"] = numPrefixGroups; - mpPrefixSumFinalizeVars["CB"]["gTotalNumElems"] = totalElementCount; - mpPrefixSumFinalizeVars["CB"]["gIter"] = iter; - mpPrefixSumFinalizeVars["gData"] = pData; - - mpComputeState->setProgram(mpPrefixSumFinalizeProgram); - pRenderContext->dispatch(mpComputeState.get(), mpPrefixSumFinalizeVars.get(), { dispatchSizeX, 1, 1 }); - } - - // Subtract the number of elements handled this iteration. - elementCount -= maxElementCountPerIteration; + // Clear group sums to zero. + pRenderContext->clearUAV(mpPrefixGroupSums->getUAV().get(), uint4(0)); + + // Set constants and data. + auto var = mpPrefixSumGroupVars->getRootVar(); + var["CB"]["gNumGroups"] = numPrefixGroups; + var["CB"]["gTotalNumElems"] = totalElementCount; + var["CB"]["gIter"] = iter; + var["gData"] = pData; + + mpComputeState->setProgram(mpPrefixSumGroupProgram); + pRenderContext->dispatch(mpComputeState.get(), mpPrefixSumGroupVars.get(), {numPrefixGroups, 1, 1}); } - // Copy total sum to separate destination buffer, if specified. - if (pTotalSumBuffer) - { - if (pTotalSumOffset + 4 > pTotalSumBuffer->getSize()) - { - throw RuntimeError("PrefixSum::execute() - Results buffer is too small."); - } + // 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()); - pRenderContext->copyBufferRegion(pTotalSumBuffer.get(), pTotalSumOffset, mpTotalSum.get(), 0, 4); + // 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 uint32_t dispatchSizeX = (numPrefixGroups - 1) * 2; + FALCOR_ASSERT(dispatchSizeX > 0); + + // Set constants and data. + auto var = mpPrefixSumFinalizeVars->getRootVar(); + var["CB"]["gNumGroups"] = numPrefixGroups; + var["CB"]["gTotalNumElems"] = totalElementCount; + var["CB"]["gIter"] = iter; + var["gData"] = pData; + + mpComputeState->setProgram(mpPrefixSumFinalizeProgram); + pRenderContext->dispatch(mpComputeState.get(), mpPrefixSumFinalizeVars.get(), {dispatchSizeX, 1, 1}); } - // Read back sum of all elements to the CPU, if requested. - if (pTotalSum) + // Subtract the number of elements handled this iteration. + elementCount -= maxElementCountPerIteration; + } + + // Copy total sum to separate destination buffer, if specified. + if (pTotalSumBuffer) + { + if (pTotalSumOffset + 4 > pTotalSumBuffer->getSize()) { - uint32_t* pMappedTotalSum = (uint32_t*)mpTotalSum->map(Buffer::MapType::Read); - *pTotalSum = *pMappedTotalSum; - mpTotalSum->unmap(); + throw RuntimeError("PrefixSum::execute() - Results buffer is too small."); } + + pRenderContext->copyBufferRegion(pTotalSumBuffer.get(), pTotalSumOffset, mpTotalSum.get(), 0, 4); + } + + // Read back sum of all elements to the CPU, if requested. + if (pTotalSum) + { + uint32_t* pMappedTotalSum = (uint32_t*)mpTotalSum->map(Buffer::MapType::Read); + *pTotalSum = *pMappedTotalSum; + mpTotalSum->unmap(); } } +} // namespace Falcor diff --git a/Source/Falcor/Utils/Algorithm/PrefixSum.cs.slang b/Source/Falcor/Utils/Algorithm/PrefixSum.cs.slang index abb5557e7..68ebe9e3c 100644 --- a/Source/Falcor/Utils/Algorithm/PrefixSum.cs.slang +++ b/Source/Falcor/Utils/Algorithm/PrefixSum.cs.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-21, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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,47 @@ # 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 -*/ +/** + * 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 gTotalNumElems; ///< Total number of elements. This does not have to be a multiple of the group size. - uint gIter; ///< The current iteration, if there are more elements than 2 * GROUP_SIZE * GROUP_SIZE, the prefix sum will be split into multiple iterations. -}; + /// Number of groups we'll process, each group is 2N elements. + uint gNumGroups; -RWByteAddressBuffer gData; ///< Data buffer. -RWByteAddressBuffer gPrefixGroupSums; ///< One uint per group, each holds the sum of all elements in the group and to the left. -RWByteAddressBuffer gTotalSum; ///< One uint, holds the total sum of a prefix sum iteration. -ByteAddressBuffer gPrevTotalSum; ///< One uint, holds the previous total sum of the previous prefix sum iteration. + /// Total number of elements. This does not have to be a multiple of the group size. + uint gTotalNumElems; + + /// The current iteration, if there are more elements than 2 * GROUP_SIZE * GROUP_SIZE, the prefix sum will be split into multiple + /// iterations. + uint gIter; +}; -groupshared uint gSharedData[2 * GROUP_SIZE]; ///< Temporary working buffer in shared memory for 2N elements. +RWByteAddressBuffer gData; ///< Data buffer. +RWByteAddressBuffer gPrefixGroupSums; ///< One uint per group, each holds the sum of all elements in the group and to the left. +RWByteAddressBuffer gTotalSum; ///< One uint, holds the total sum of a prefix sum iteration. +ByteAddressBuffer gPrevTotalSum; ///< One uint, holds the previous total sum of the previous prefix sum iteration. +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. -*/ +/** + * 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) +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. + 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. @@ -85,7 +92,7 @@ void groupScan(uint3 groupID : SV_GroupID, uint3 groupThreadID : SV_GroupThreadI gSharedData[bi] += gSharedData[ai]; } - offset *= 2; // offset = 1, 2, ... N + offset *= 2; // offset = 1, 2, ... N } GroupMemoryBarrierWithGroupSync(); @@ -110,13 +117,14 @@ void groupScan(uint3 groupID : SV_GroupID, uint3 groupThreadID : SV_GroupThreadI // 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; + 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 + offset >>= 1; // offset = N, N/2, ..., 1 GroupMemoryBarrierWithGroupSync(); @@ -134,23 +142,26 @@ void groupScan(uint3 groupID : SV_GroupID, uint3 groupThreadID : SV_GroupThreadI GroupMemoryBarrierWithGroupSync(); // Write results to memory and add prev sum. Lower half first then upper half. - if (idx < gTotalNumElems) gData.Store(idx * 4, gSharedData[thid] + prevSum); - if ((idx + GROUP_SIZE) < gTotalNumElems) gData.Store((idx + GROUP_SIZE) * 4, gSharedData[thid + GROUP_SIZE] + prevSum); + if (idx < gTotalNumElems) + gData.Store(idx * 4, gSharedData[thid] + prevSum); + if ((idx + GROUP_SIZE) < gTotalNumElems) + gData.Store((idx + GROUP_SIZE) * 4, gSharedData[thid + GROUP_SIZE] + prevSum); } -/** 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). -*/ +/** + * 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) +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). + 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 * (1 + GROUP_SIZE * gIter); // Skip first 2N elements. + uint globalIdx = (groupIdx * GROUP_SIZE) + thid + 2 * GROUP_SIZE * (1 + GROUP_SIZE * gIter); // Skip first 2N elements. if (globalIdx < gTotalNumElems) { diff --git a/Source/Falcor/Utils/Algorithm/PrefixSum.h b/Source/Falcor/Utils/Algorithm/PrefixSum.h index 7d8d6e5aa..c2a904c30 100644 --- a/Source/Falcor/Utils/Algorithm/PrefixSum.h +++ b/Source/Falcor/Utils/Algorithm/PrefixSum.h @@ -35,42 +35,51 @@ namespace Falcor { - class RenderContext; +class RenderContext; - /** 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 FALCOR_API PrefixSum - { - public: - /// Constructor. Throws an exception if creation failed. - PrefixSum(std::shared_ptr pDevice); +/** + * 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 FALCOR_API PrefixSum +{ +public: + /// Constructor. Throws an exception if creation failed. + PrefixSum(ref pDevice); - /** 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. - */ - void execute(RenderContext* pRenderContext, Buffer::SharedPtr pData, uint32_t elementCount, uint32_t* pTotalSum = nullptr, Buffer::SharedPtr pTotalSumBuffer = nullptr, uint64_t pTotalSumOffset = 0); + /** + * 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. 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. + */ + void execute( + RenderContext* pRenderContext, + ref pData, + uint32_t elementCount, + uint32_t* pTotalSum = nullptr, + ref pTotalSumBuffer = nullptr, + uint64_t pTotalSumOffset = 0 + ); - private: - std::shared_ptr mpDevice; +private: + ref mpDevice; - ComputeState::SharedPtr mpComputeState; + ref mpComputeState; - ComputeProgram::SharedPtr mpPrefixSumGroupProgram; - ComputeVars::SharedPtr mpPrefixSumGroupVars; + ref mpPrefixSumGroupProgram; + ref mpPrefixSumGroupVars; - ComputeProgram::SharedPtr mpPrefixSumFinalizeProgram; - ComputeVars::SharedPtr mpPrefixSumFinalizeVars; + ref mpPrefixSumFinalizeProgram; + ref mpPrefixSumFinalizeVars; - Buffer::SharedPtr mpPrefixGroupSums; ///< Temporary buffer for prefix sum computation. - Buffer::SharedPtr mpTotalSum; ///< Temporary buffer for total sum of an iteration. - Buffer::SharedPtr mpPrevTotalSum; ///< Temporary buffer for prev total sum of an iteration. - }; -} + ref mpPrefixGroupSums; ///< Temporary buffer for prefix sum computation. + ref mpTotalSum; ///< Temporary buffer for total sum of an iteration. + ref mpPrevTotalSum; ///< Temporary buffer for prev total sum of an iteration. +}; +} // namespace Falcor diff --git a/Source/Falcor/Utils/AlignedAllocator.h b/Source/Falcor/Utils/AlignedAllocator.h index 67b933d7b..73d11324d 100644 --- a/Source/Falcor/Utils/AlignedAllocator.h +++ b/Source/Falcor/Utils/AlignedAllocator.h @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -35,125 +35,134 @@ 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 +/** + * 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) { - public: - /** Sets the minimum alignment for allocated objects. If a value of - zero is provided, no additional alignment is performed. - */ - void setMinimumAlignment(int minAlignment) - { - FALCOR_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) - { - FALCOR_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) - { - FALCOR_ASSERT(size >= sizeof(T)); - computeAndAllocatePadding(size); - void* ptr = allocInternal(size); - return new (ptr) T(std::forward(args)...); - } + FALCOR_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) + { + FALCOR_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) + { + FALCOR_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 + { + FALCOR_ASSERT(ptr >= mBuffer.data() && ptr < mBuffer.data() + mBuffer.size()); + return static_cast(ptr) - mBuffer.data(); + } - void reserve(size_t size) { mBuffer.reserve(size); } + void reset() { mBuffer.clear(); } - void resize(size_t size) { mBuffer.resize(size, 0); } + size_t getSize() const { return mBuffer.size(); } + size_t getCapacity() const { return mBuffer.capacity(); } - /** Returns the pointer to the start of the allocated buffer. - */ - void* getStartPointer() { return mBuffer.data(); } - const void* getStartPointer() const { return mBuffer.data(); } +private: + void computeAndAllocatePadding(size_t size) + { + size_t currentOffset = mBuffer.size(); - /** Returns of the offset of the given pointer inside the allocation buffer. - */ - size_t offsetOf(void* ptr) const + if (mMinAlignment > 0 && (currentOffset % mMinAlignment) != 0) { - FALCOR_ASSERT(ptr >= mBuffer.data() && ptr < mBuffer.data() + mBuffer.size()); - return static_cast(ptr) - mBuffer.data(); + // We're not at the minimum alignment; get aligned. + currentOffset += mMinAlignment - (currentOffset % mMinAlignment); } - void reset() { mBuffer.clear(); } - - size_t getSize() const { return mBuffer.size(); } - size_t getCapacity() const { return mBuffer.capacity(); } - - private: - void computeAndAllocatePadding(size_t size) + if (mCacheLineSize > 0) { - size_t currentOffset = mBuffer.size(); - - if (mMinAlignment > 0 && (currentOffset % mMinAlignment) != 0) + const size_t cacheLineOffset = currentOffset % mCacheLineSize; + if (size <= mCacheLineSize && cacheLineOffset + size > mCacheLineSize) { - // We're not at the minimum alignment; get aligned. - currentOffset += mMinAlignment - (currentOffset % mMinAlignment); + // The allocation is smaller than or equal to a cache line but + // would span two cache lines; move to the start of the next cache line. + currentOffset += mCacheLineSize - cacheLineOffset; } - - if (mCacheLineSize > 0) - { - const size_t cacheLineOffset = currentOffset % mCacheLineSize; - if (size <= mCacheLineSize && cacheLineOffset + size > mCacheLineSize) - { - // The allocation is smaller than or equal to a cache line but - // would span two cache lines; move to the start of the next cache line. - currentOffset += mCacheLineSize - cacheLineOffset; - } - } - - size_t pad = currentOffset - mBuffer.size(); - if (pad > 0) - { - allocInternal(pad); - } - FALCOR_ASSERT(mMinAlignment == 0 || mBuffer.size() % mMinAlignment == 0); } - void* allocInternal(size_t size) + size_t pad = currentOffset - mBuffer.size(); + if (pad > 0) { - auto iter = mBuffer.insert(mBuffer.end(), size, {}); - return &*iter; + allocInternal(pad); } + FALCOR_ASSERT(mMinAlignment == 0 || mBuffer.size() % mMinAlignment == 0); + } - size_t mMinAlignment = 16; - size_t mCacheLineSize = 128; - std::vector mBuffer; - }; -} + void* allocInternal(size_t size) + { + auto iter = mBuffer.insert(mBuffer.end(), size, {}); + return &*iter; + } + + size_t mMinAlignment = 16; + size_t mCacheLineSize = 128; + std::vector mBuffer; +}; +} // namespace Falcor diff --git a/Source/Falcor/Utils/Attributes.slang b/Source/Falcor/Utils/Attributes.slang index 1deaf22e9..906567966 100644 --- a/Source/Falcor/Utils/Attributes.slang +++ b/Source/Falcor/Utils/Attributes.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-21, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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,9 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **************************************************************************/ -/** Attribute for tagging resources that should be bound to a root descriptor. -*/ +/** + * Attribute for tagging resources that should be bound to a root descriptor. + */ [__AttributeUsage(_AttributeTargets.Var)] -struct rootAttribute {}; +struct rootAttribute +{}; diff --git a/Source/Falcor/Utils/BinaryFileStream.h b/Source/Falcor/Utils/BinaryFileStream.h index dc90390e2..e34084416 100644 --- a/Source/Falcor/Utils/BinaryFileStream.h +++ b/Source/Falcor/Utils/BinaryFileStream.h @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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,140 +31,159 @@ namespace Falcor { - /** Helper class to manage file I/O with binary files - */ - class BinaryFileStream +/** + * Helper class to manage file I/O with binary files + */ +class BinaryFileStream +{ +public: + /** + * Mode to open file as + */ + enum class Mode { - public: - - /** Mode to open file as - */ - enum class Mode - { - Read = 0x1, ///< Open file for reading - Write = 0x2, ///< Open file for writing - ReadWrite = 0x3 ///< Open file for both reading and writing - }; - - /** Default constructor. - */ - BinaryFileStream() {}; - - /** Constructor that opens a file - \param[in] path Path of file to open or create - \param[in] mode Mode to open file as - */ - BinaryFileStream(const std::filesystem::path& path, Mode mode = Mode::ReadWrite) - { - open(path, mode); - } + Read = 0x1, ///< Open file for reading + Write = 0x2, ///< Open file for writing + ReadWrite = 0x3 ///< Open file for both reading and writing + }; - /** Destructor - */ - ~BinaryFileStream() + /** + * Default constructor. + */ + BinaryFileStream(){}; + + /** + * Constructor that opens a file + * @param[in] path Path of file to open or create + * @param[in] mode Mode to open file as + */ + BinaryFileStream(const std::filesystem::path& path, Mode mode = Mode::ReadWrite) { open(path, mode); } + + /** + * Destructor + */ + ~BinaryFileStream() { close(); } + + /** + * Opens a file stream. Fails if a file is already open. + * @param[in] path Path of file to open or create + * @param[in] mode Mode to open file as + */ + void open(const std::filesystem::path& path, Mode mode = Mode::ReadWrite) + { + std::ios::openmode iosMode = std::ios::binary; + iosMode |= ((mode == Mode::Read) || (mode == Mode::ReadWrite)) ? std::ios::in : (std::ios::openmode)0; + iosMode |= ((mode == Mode::Write) || (mode == Mode::ReadWrite)) ? std::ios::out : (std::ios::openmode)0; + mStream.open(path, iosMode); + mPath = path; + } + + /** + * Close the file stream. + */ + void close() { mStream.close(); } + + /** + * Skip data in an input stream. Advances file stream without reading. + * @param[in] count Bytes to skip + */ + void skip(uint32_t count) { mStream.ignore(count); } + + /** + * Deletes the managed file. + */ + void remove() + { + if (mStream.is_open()) { close(); } - - /** Opens a file stream. Fails if a file is already open. - \param[in] path Path of file to open or create - \param[in] mode Mode to open file as - */ - void open(const std::filesystem::path& path, Mode mode = Mode::ReadWrite) - { - std::ios::openmode iosMode = std::ios::binary; - iosMode |= ((mode == Mode::Read) || (mode == Mode::ReadWrite)) ? std::ios::in : (std::ios::openmode)0; - iosMode |= ((mode == Mode::Write) || (mode == Mode::ReadWrite))? std::ios::out : (std::ios::openmode)0; - mStream.open(path, iosMode); - mPath = path; - } - - /** Close the file stream. - */ - void close() - { - mStream.close(); - } - - /** Skip data in an input stream. Advances file stream without reading. - \param[in] count Bytes to skip - */ - void skip(uint32_t count) - { - mStream.ignore(count); - } - - /** Deletes the managed file. - */ - void remove() - { - if (mStream.is_open()) - { - close(); - } - std::filesystem::remove(mPath); - } - - /** Calculates amount of remaining data in the file. - \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(); - mStream.seekg(currentPos); - return (uint32_t)(length - currentPos); - } - - /** Checks for validity of the stream - \return Returns true if no errors have been encountered and the end of the stream has not been reached - */ - bool isGood() { return mStream.good(); } - - /** Checks for stream errors. - \return Returns true if an error has occurred while reading or writing data. - */ - bool isBad() { return mStream.bad(); } - - /** Checks for stream errors. - \return Returns true if any error has occurred while reading the file. - */ - bool isFail() { return mStream.fail(); } - - /** Checks if the end of file has been reached. - \return Returns true if stream has reached the end of the file - */ - bool isEof() { return mStream.eof(); } - - /** Reads data from the file stream - \param[out] pData Pointer to a buffer to copy/read data into - \param[in] count Number of bytes to read - */ - BinaryFileStream& read(void* pData, size_t count) { mStream.read((char*)pData, count); return *this; } - - /** Writes data to the file stream - \param[in] pData Pointer to buffer containing data to write to the stream - \param[in] count Number of bytes to write - */ - BinaryFileStream& write(const void* pData, size_t count) { mStream.write((char*)pData, count); return *this; } - - // Operator overloads - - /** Extracts a single value from the stream - \param[out] val Reference of value to extract into - */ - template - BinaryFileStream& operator>>(T& val) { return read(&val, sizeof(T)); } - - /** Writes a value into the file stream - \param[in] val Value to write - */ - template - BinaryFileStream& operator<<(const T& val) { return write(&val, sizeof(T)); } - - private: - std::fstream mStream; - std::filesystem::path mPath; - }; -} + std::filesystem::remove(mPath); + } + + /** + * Calculates amount of remaining data in the file. + * @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(); + mStream.seekg(currentPos); + return (uint32_t)(length - currentPos); + } + + /** + * Checks for validity of the stream + * @return Returns true if no errors have been encountered and the end of the stream has not been reached + */ + bool isGood() { return mStream.good(); } + + /** + * Checks for stream errors. + * @return Returns true if an error has occurred while reading or writing data. + */ + bool isBad() { return mStream.bad(); } + + /** + * Checks for stream errors. + * @return Returns true if any error has occurred while reading the file. + */ + bool isFail() { return mStream.fail(); } + + /** + * Checks if the end of file has been reached. + * @return Returns true if stream has reached the end of the file + */ + bool isEof() { return mStream.eof(); } + + /** + * Reads data from the file stream + * @param[out] pData Pointer to a buffer to copy/read data into + * @param[in] count Number of bytes to read + */ + BinaryFileStream& read(void* pData, size_t count) + { + mStream.read((char*)pData, count); + return *this; + } + + /** + * Writes data to the file stream + * @param[in] pData Pointer to buffer containing data to write to the stream + * @param[in] count Number of bytes to write + */ + BinaryFileStream& write(const void* pData, size_t count) + { + mStream.write((char*)pData, count); + return *this; + } + + // Operator overloads + + /** + * Extracts a single value from the stream + * @param[out] val Reference of value to extract into + */ + template + BinaryFileStream& operator>>(T& val) + { + return read(&val, sizeof(T)); + } + + /** + * Writes a value into the file stream + * @param[in] val Value to write + */ + template + BinaryFileStream& operator<<(const T& val) + { + return write(&val, sizeof(T)); + } + +private: + std::fstream mStream; + std::filesystem::path mPath; +}; +} // namespace Falcor diff --git a/Source/Falcor/Utils/BufferAllocator.cpp b/Source/Falcor/Utils/BufferAllocator.cpp index 77dd5ad37..5afedda93 100644 --- a/Source/Falcor/Utils/BufferAllocator.cpp +++ b/Source/Falcor/Utils/BufferAllocator.cpp @@ -30,140 +30,139 @@ namespace Falcor { - BufferAllocator::BufferAllocator(size_t alignment, size_t elementSize, size_t cacheLineSize, ResourceBindFlags bindFlags) - : mAlignment(alignment) - , mElementSize(elementSize) - , mCacheLineSize(cacheLineSize) - , mBindFlags(bindFlags) +BufferAllocator::BufferAllocator(size_t alignment, size_t elementSize, size_t cacheLineSize, ResourceBindFlags bindFlags) + : mAlignment(alignment), mElementSize(elementSize), mCacheLineSize(cacheLineSize), mBindFlags(bindFlags) +{ + // Check the different alignment requirements. + // Some of these are stricter than they need be and can be relaxed in the future. + checkArgument(alignment == 0 || isPowerOf2(alignment), "Alignment must be a power of two."); + checkArgument(cacheLineSize == 0 || isPowerOf2(cacheLineSize), "Cache line size must be a power of two."); + checkArgument(cacheLineSize == 0 || alignment <= cacheLineSize, "Alignment must be smaller or equal to the cache line size."); + + // Check requirements on element size for structured buffers. + // The alignment can be smaller or larger than the element size, but they need to be integer multiples. + if (elementSize > 0 && alignment > 0) { - // Check the different alignment requirements. - // Some of these are stricter than they need be and can be relaxed in the future. - checkArgument(alignment == 0 || isPowerOf2(alignment), "Alignment must be a power of two."); - checkArgument(cacheLineSize == 0 || isPowerOf2(cacheLineSize), "Cache line size must be a power of two."); - checkArgument(cacheLineSize == 0 || alignment <= cacheLineSize, "Alignment must be smaller or equal to the cache line size."); - - // Check requirements on element size for structured buffers. - // The alignment can be smaller or larger than the element size, but they need to be integer multiples. - if (elementSize > 0 && alignment > 0) - { - size_t a = std::max(alignment, elementSize); - size_t b = std::min(alignment, elementSize); - checkArgument(a % b == 0, "Alignment and element size needs to be integer multiples."); - } + size_t a = std::max(alignment, elementSize); + size_t b = std::min(alignment, elementSize); + checkArgument(a % b == 0, "Alignment and element size needs to be integer multiples."); } +} - size_t BufferAllocator::allocate(size_t byteSize) - { - computeAndAllocatePadding(byteSize); - return allocInternal(byteSize); - } +size_t BufferAllocator::allocate(size_t byteSize) +{ + computeAndAllocatePadding(byteSize); + return allocInternal(byteSize); +} - void BufferAllocator::setBlob(const void* pData, size_t byteOffset, size_t byteSize) - { - checkArgument(pData != nullptr, "Invalid pointer."); - checkArgument(byteOffset + byteSize <= mBuffer.size(), "Memory region is out of range."); - std::memcpy(mBuffer.data() + byteOffset, pData, byteSize); - markAsDirty(byteOffset, byteSize); - } +void BufferAllocator::setBlob(const void* pData, size_t byteOffset, size_t byteSize) +{ + checkArgument(pData != nullptr, "Invalid pointer."); + checkArgument(byteOffset + byteSize <= mBuffer.size(), "Memory region is out of range."); + std::memcpy(mBuffer.data() + byteOffset, pData, byteSize); + markAsDirty(byteOffset, byteSize); +} - void BufferAllocator::modified(size_t byteOffset, size_t byteSize) - { - checkArgument(byteOffset + byteSize <= mBuffer.size(), "Memory region is out of range."); - markAsDirty(byteOffset, byteSize); - } +void BufferAllocator::modified(size_t byteOffset, size_t byteSize) +{ + checkArgument(byteOffset + byteSize <= mBuffer.size(), "Memory region is out of range."); + markAsDirty(byteOffset, byteSize); +} - void BufferAllocator::clear() +void BufferAllocator::clear() +{ + mBuffer.clear(); + mDirty = {}; +} + +ref BufferAllocator::getGPUBuffer(ref pDevice) +{ + if (mBuffer.empty()) { - mBuffer.clear(); - mDirty = {}; + // If there is no allocated data, we don't need a GPU buffer and return nullptr. + return nullptr; } - Buffer::SharedPtr BufferAllocator::getGPUBuffer(Device* pDevice) + // Compute required size of the buffer on the GPU including padding and allocate + // buffer of the right type (structured or raw buffer). + size_t elemSize = mElementSize > 0 ? mElementSize : 4ull; + size_t bufSize = align_to(elemSize, mBuffer.size()); + + if (mpGpuBuffer == nullptr || mpGpuBuffer->getSize() < bufSize) { - if (mBuffer.empty()) + if (mElementSize > 0) { - // If there is no allocated data, we don't need a GPU buffer and return nullptr. - return nullptr; + size_t elemCount = bufSize / mElementSize; + FALCOR_ASSERT(elemCount * mElementSize == bufSize); + mpGpuBuffer = Buffer::createStructured( + pDevice, mElementSize, elemCount, mBindFlags, Buffer::CpuAccess::None, nullptr, false /* no UAV counter */ + ); } - - // Compute required size of the buffer on the GPU including padding and allocate - // buffer of the right type (structured or raw buffer). - size_t elemSize = mElementSize > 0 ? mElementSize : 4ull; - size_t bufSize = align_to(elemSize, mBuffer.size()); - - if (mpGpuBuffer == nullptr || mpGpuBuffer->getSize() < bufSize) + else { - if (mElementSize > 0) - { - size_t elemCount = bufSize / mElementSize; - FALCOR_ASSERT(elemCount * mElementSize == bufSize); - mpGpuBuffer = Buffer::createStructured(pDevice, mElementSize, elemCount, mBindFlags, Buffer::CpuAccess::None, nullptr, false /* no UAV counter */); - } - else - { - mpGpuBuffer = Buffer::create(pDevice, bufSize, mBindFlags, Buffer::CpuAccess::None, nullptr); - } - - mDirty = Range(0, mBuffer.size()); // Mark entire buffer as dirty so the data gets uploaded. + mpGpuBuffer = Buffer::create(pDevice, bufSize, mBindFlags, Buffer::CpuAccess::None, nullptr); } - // If any range is dirty, upload the data from the CPU to the GPU. - if (mDirty.start < mDirty.end) - { - size_t byteSize = mDirty.end - mDirty.start; - size_t byteOffset = mDirty.start; - FALCOR_ASSERT(byteOffset + byteSize <= mBuffer.size()); - FALCOR_ASSERT(mBuffer.size() <= mpGpuBuffer->getSize()); - mpGpuBuffer->setBlob(mBuffer.data() + byteOffset, byteOffset, byteSize); + mDirty = Range(0, mBuffer.size()); // Mark entire buffer as dirty so the data gets uploaded. + } - mDirty = { mBuffer.size(), 0 }; // Reset dirty range to inverted range. Any min/max operation on it will work. - } + // If any range is dirty, upload the data from the CPU to the GPU. + if (mDirty.start < mDirty.end) + { + size_t byteSize = mDirty.end - mDirty.start; + size_t byteOffset = mDirty.start; + FALCOR_ASSERT(byteOffset + byteSize <= mBuffer.size()); + FALCOR_ASSERT(mBuffer.size() <= mpGpuBuffer->getSize()); + mpGpuBuffer->setBlob(mBuffer.data() + byteOffset, byteOffset, byteSize); - return mpGpuBuffer; + mDirty = {mBuffer.size(), 0}; // Reset dirty range to inverted range. Any min/max operation on it will work. } - // Private + return mpGpuBuffer; +} - void BufferAllocator::computeAndAllocatePadding(size_t byteSize) - { - size_t currentOffset = mBuffer.size(); +// Private - if (mAlignment > 0 && currentOffset % mAlignment > 0) - { - // We're not at the minimum alignment; get aligned. - currentOffset += mAlignment - (currentOffset % mAlignment); - } +void BufferAllocator::computeAndAllocatePadding(size_t byteSize) +{ + size_t currentOffset = mBuffer.size(); - if (mCacheLineSize > 0) - { - const size_t cacheLineOffset = currentOffset % mCacheLineSize; - if (byteSize <= mCacheLineSize && cacheLineOffset + byteSize > mCacheLineSize) - { - // The allocation is smaller than or equal to a cache line but - // would span two cache lines; move to the start of the next cache line. - currentOffset += mCacheLineSize - cacheLineOffset; - } - } + if (mAlignment > 0 && currentOffset % mAlignment > 0) + { + // We're not at the minimum alignment; get aligned. + currentOffset += mAlignment - (currentOffset % mAlignment); + } - size_t pad = currentOffset - mBuffer.size(); - if (pad > 0) + if (mCacheLineSize > 0) + { + const size_t cacheLineOffset = currentOffset % mCacheLineSize; + if (byteSize <= mCacheLineSize && cacheLineOffset + byteSize > mCacheLineSize) { - allocInternal(pad); + // The allocation is smaller than or equal to a cache line but + // would span two cache lines; move to the start of the next cache line. + currentOffset += mCacheLineSize - cacheLineOffset; } - FALCOR_ASSERT(mAlignment == 0 || mBuffer.size() % mAlignment == 0); } - size_t BufferAllocator::allocInternal(size_t byteSize) + size_t pad = currentOffset - mBuffer.size(); + if (pad > 0) { - size_t byteOffset = mBuffer.size(); - mBuffer.insert(mBuffer.end(), byteSize, {}); - return byteOffset; + allocInternal(pad); } + FALCOR_ASSERT(mAlignment == 0 || mBuffer.size() % mAlignment == 0); +} - void BufferAllocator::markAsDirty(const Range& range) - { - FALCOR_ASSERT(range.start < range.end); - mDirty.start = std::min(mDirty.start, range.start); - mDirty.end = std::max(mDirty.end, range.end); - } +size_t BufferAllocator::allocInternal(size_t byteSize) +{ + size_t byteOffset = mBuffer.size(); + mBuffer.insert(mBuffer.end(), byteSize, {}); + return byteOffset; +} + +void BufferAllocator::markAsDirty(const Range& range) +{ + FALCOR_ASSERT(range.start < range.end); + mDirty.start = std::min(mDirty.start, range.start); + mDirty.end = std::max(mDirty.end, range.end); } +} // namespace Falcor diff --git a/Source/Falcor/Utils/BufferAllocator.h b/Source/Falcor/Utils/BufferAllocator.h index 9853aa1ac..87e782537 100644 --- a/Source/Falcor/Utils/BufferAllocator.h +++ b/Source/Falcor/Utils/BufferAllocator.h @@ -34,169 +34,201 @@ namespace Falcor { - /** Utility class for memory management of a GPU buffer. - - The class maintains a dynamically sized backing buffer on the CPU - in which memory can be allocated and updated. - The GPU buffer is lazily created and updated upon access. - The caller should not hold on to pointers into the buffers as the - memory may get reallocated at any time. - - BufferAllocator can enforce various alignment requirements, - including minimum byte alignment and (optionally) that allocated - objects don't span multiple cache lines if possible. - It is assumed that the base pointer of the GPU buffer starts at a - cache line. The implementation doesn't provide any alignment - guarantees for the CPU side buffer (where it doesn't matter anyway). - */ - class FALCOR_API BufferAllocator +/** + * Utility class for memory management of a GPU buffer. + * + * The class maintains a dynamically sized backing buffer on the CPU + * in which memory can be allocated and updated. + * The GPU buffer is lazily created and updated upon access. + * The caller should not hold on to pointers into the buffers as the + * memory may get reallocated at any time. + * + * BufferAllocator can enforce various alignment requirements, + * including minimum byte alignment and (optionally) that allocated + * objects don't span multiple cache lines if possible. + * It is assumed that the base pointer of the GPU buffer starts at a + * cache line. The implementation doesn't provide any alignment + * guarantees for the CPU side buffer (where it doesn't matter anyway). + */ +class FALCOR_API BufferAllocator +{ +public: + /** + * Create a buffer allocator. + * @param[in] alignment Minimum alignment in bytes for any allocation. + * @param[in] elementSize Element size for structured buffer. If zero a raw buffer is created. + * @param[in] cacheLineSize Cache line size in bytes. Allocations are placed to not stride cache lines if it can be avoided. + * @param[in] bindFlags Resource bind flags for the GPU buffer. + */ + BufferAllocator( + size_t alignment, + size_t elementSize, + size_t cacheLineSize = 128, + ResourceBindFlags bindFlags = ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess + ); + + /** + * Allocates a memory region. + * @param[in] byteSize Amount of memory in bytes to allocate. + * @return Offset in bytes to the allocated memory. + */ + size_t allocate(size_t byteSize); + + /** + * Allocates memory to hold an array of the given type. + * @param[in] count Number of array elements. + * @return Offset in bytes to the allocated memory. + */ + template + size_t allocate(size_t count = 1) + { + return allocate(count * sizeof(T)); + } + + /** + * Allocates an object of given type and copies the data. + * @param[in] obj The object to copy. + * @return Offset in bytes to the allocated object. + */ + template + size_t pushBack(const T& obj) + { + const size_t byteSize = sizeof(T); + computeAndAllocatePadding(byteSize); + size_t byteOffset = allocInternal(byteSize); + T* ptr = reinterpret_cast(mBuffer.data() + byteOffset); + *ptr = obj; + markAsDirty(byteOffset, byteSize); + return byteOffset; + } + + /** + * Allocates an object of given type and executes its constructor. + * @param[in] args Arguments to pass to the constructor. + * @return Offset in bytes to the allocated object. + */ + template + size_t emplaceBack(Args&&... args) + { + const size_t byteSize = sizeof(T); + computeAndAllocatePadding(byteSize); + size_t byteOffset = allocInternal(byteSize); + void* ptr = mBuffer.data() + byteOffset; + new (ptr) T(std::forward(args)...); + markAsDirty(byteOffset, byteSize); + return byteOffset; + } + + /** + * Set data into a memory region. + * @param[in] pData Pointer to the source data. + * @param[in] byteOffset Offset in bytes to the destination memory region. + * @param[in] byteSize Size in bytes to copy. + */ + void setBlob(const void* pData, size_t byteOffset, size_t byteSize); + + /** + * Set an object of the given type. The memory must have been previously allocated. + * @param[in] obj The object to set. + * @param[in] byteOffset Offset in bytes of the object. + */ + template + void set(size_t byteOffset, const T& obj) + { + setBlob(&obj, byteOffset, sizeof(T)); + } + + /** + * Get an object of the given type. + * @param[in] byteOffset Offset in bytes of the object. + */ + template + const T& get(size_t byteOffset) const + { + return *reinterpret_cast(mBuffer.data() + byteOffset); + } + + /** + * Mark memory region as modified. The GPU buffer will get updated. + * @param[in] byteOffset Offset in bytes. + * @param[in] byteSize Size in bytes. + */ + void modified(size_t byteOffset, size_t byteSize); + + /** + * Mark an object of the given type as modified. The GPU buffer will get updated. + * @param[in] byteOffset Offset in bytes to the object. + */ + template + void modified(size_t byteOffset) + { + modified(byteOffset, sizeof(T)); + } + + /** + * Returns the pointer to the start of the allocated CPU buffer (read/write). + * The pointer is transient and only valid until the next allocation operation. + * This is for low-level access only. Use with care and call `modified` to mark any changes for the GPU buffer to get updated correctly. + * @return Pointer to CPU side buffer. + */ + uint8_t* getStartPointer() { return mBuffer.data(); } + + /** + * Returns the pointer to the start of the allocated CPU buffer (read only). + * The pointer is transient and only valid until the next allocation operation. + * @return Pointer to CPU side buffer. + */ + const uint8_t* getStartPointer() const { return mBuffer.data(); } + + /** + * Get size of the buffer in bytes. + * @return Size in bytes. + */ + size_t getSize() const { return mBuffer.size(); } + + /** + * Clear buffer. This removes all allocations. + */ + void clear(); + + /** + * Get GPU buffer. The buffer is updated and ready for use. + * The buffer is transient and only valid until the next allocation operation. + */ + ref getGPUBuffer(ref pDevice); + +private: + void computeAndAllocatePadding(size_t byteSize); + size_t allocInternal(size_t byteSize); + + struct Range { - public: - /** Create a buffer allocator. - \param[in] alignment Minimum alignment in bytes for any allocation. - \param[in] elementSize Element size for structured buffer. If zero a raw buffer is created. - \param[in] cacheLineSize Cache line size in bytes. Allocations are placed to not stride cache lines if it can be avoided. - \param[in] bindFlags Resource bind flags for the GPU buffer. - */ - BufferAllocator(size_t alignment, size_t elementSize, size_t cacheLineSize = 128, ResourceBindFlags bindFlags = ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess); - - /** Allocates a memory region. - \param[in] byteSize Amount of memory in bytes to allocate. - \return Offset in bytes to the allocated memory. - */ - size_t allocate(size_t byteSize); - - /** Allocates memory to hold an array of the given type. - \param[in] count Number of array elements. - \return Offset in bytes to the allocated memory. - */ - template - size_t allocate(size_t count = 1) - { - return allocate(count * sizeof(T)); - } - - /** Allocates an object of given type and copies the data. - \param[in] obj The object to copy. - \return Offset in bytes to the allocated object. - */ - template size_t pushBack(const T& obj) - { - const size_t byteSize = sizeof(T); - computeAndAllocatePadding(byteSize); - size_t byteOffset = allocInternal(byteSize); - T* ptr = reinterpret_cast(mBuffer.data() + byteOffset); - *ptr = obj; - markAsDirty(byteOffset, byteSize); - return byteOffset; - } - - /** Allocates an object of given type and executes its constructor. - \param[in] args Arguments to pass to the constructor. - \return Offset in bytes to the allocated object. - */ - template size_t emplaceBack(Args&&... args) - { - const size_t byteSize = sizeof(T); - computeAndAllocatePadding(byteSize); - size_t byteOffset = allocInternal(byteSize); - void* ptr = mBuffer.data() + byteOffset; - new (ptr) T(std::forward(args)...); - markAsDirty(byteOffset, byteSize); - return byteOffset; - } - - /** Set data into a memory region. - \param[in] pData Pointer to the source data. - \param[in] byteOffset Offset in bytes to the destination memory region. - \param[in] byteSize Size in bytes to copy. - */ - void setBlob(const void* pData, size_t byteOffset, size_t byteSize); - - /** Set an object of the given type. The memory must have been previously allocated. - \param[in] obj The object to set. - \param[in] byteOffset Offset in bytes of the object. - */ - template - void set(size_t byteOffset, const T& obj) - { - setBlob(&obj, byteOffset, sizeof(T)); - } - - /** Get an object of the given type. - \param[in] byteOffset Offset in bytes of the object. - */ - template - const T& get(size_t byteOffset) const - { - return *reinterpret_cast(mBuffer.data() + byteOffset); - } - - /** Mark memory region as modified. The GPU buffer will get updated. - \param[in] byteOffset Offset in bytes. - \param[in] byteSize Size in bytes. - */ - void modified(size_t byteOffset, size_t byteSize); - - /** Mark an object of the given type as modified. The GPU buffer will get updated. - \param[in] byteOffset Offset in bytes to the object. - */ - template - void modified(size_t byteOffset) - { - modified(byteOffset, sizeof(T)); - } - - /** Returns the pointer to the start of the allocated CPU buffer (read/write). - The pointer is transient and only valid until the next allocation operation. - This is for low-level access only. Use with care and call `modified` to mark any changes for the GPU buffer to get updated correctly. - \return Pointer to CPU side buffer. - */ - uint8_t* getStartPointer() { return mBuffer.data(); } - - /** Returns the pointer to the start of the allocated CPU buffer (read only). - The pointer is transient and only valid until the next allocation operation. - \return Pointer to CPU side buffer. - */ - const uint8_t* getStartPointer() const { return mBuffer.data(); } - - /** Get size of the buffer in bytes. - \return Size in bytes. - */ - size_t getSize() const { return mBuffer.size(); } - - /** Clear buffer. This removes all allocations. - */ - void clear(); - - /** Get GPU buffer. The buffer is updated and ready for use. - The buffer is transient and only valid until the next allocation operation. - */ - Buffer::SharedPtr getGPUBuffer(Device* pDevice); - - private: - void computeAndAllocatePadding(size_t byteSize); - size_t allocInternal(size_t byteSize); - - struct Range - { - size_t start = 0; - size_t end = 0; - Range() {}; - Range(size_t s, size_t e) : start(s), end(e) {} - }; - - void markAsDirty(const Range& range); - void markAsDirty(size_t byteOffset, size_t byteSize) { markAsDirty(Range(byteOffset, byteOffset + byteSize)); } - - const size_t mAlignment; ///< Minimum alignment for allocations from base address. A value of zero means no aligment is performed. - const size_t mElementSize; ///< Element size for structured buffers. A value of zero means a raw buffer is created. - const size_t mCacheLineSize; ///< Allocation are aligned to not span multiple cache lines (if possible). A value of zero means do not care about cache line alignment. - const ResourceBindFlags mBindFlags; ///< Bind flags for the GPU buffer. - - Range mDirty; ///< Range of buffer that is dirty and needs to be updatd on the GPU. We track a single dirty range for now to minimize the number of uploads, but this could be changed. - - std::vector mBuffer; ///< CPU buffer holding a copy of the data. - Buffer::SharedPtr mpGpuBuffer; ///< GPU buffer holding the data. + size_t start = 0; + size_t end = 0; + Range(){}; + Range(size_t s, size_t e) : start(s), end(e) {} }; -} + + void markAsDirty(const Range& range); + void markAsDirty(size_t byteOffset, size_t byteSize) { markAsDirty(Range(byteOffset, byteOffset + byteSize)); } + + /// Minimum alignment for allocations from base address. A value of zero means no aligment is performed. + const size_t mAlignment; + + /// Element size for structured buffers. A value of zero means a raw buffer is created. + const size_t mElementSize; + + /// Allocation are aligned to not span multiple cache lines (if possible). A value of zero means do not care about cache line alignment. + const size_t mCacheLineSize; + + /// Bind flags for the GPU buffer. + const ResourceBindFlags mBindFlags; + + /// Range of buffer that is dirty and needs to be updatd on the GPU. We track a single dirty range for now to minimize the number of + /// uploads, but this could be changed. + Range mDirty; + + std::vector mBuffer; ///< CPU buffer holding a copy of the data. + ref mpGpuBuffer; ///< GPU buffer holding the data. +}; +} // namespace Falcor diff --git a/Source/Falcor/Utils/Color/ColorHelpers.slang b/Source/Falcor/Utils/Color/ColorHelpers.slang index 91cef805d..015df49b4 100644 --- a/Source/Falcor/Utils/Color/ColorHelpers.slang +++ b/Source/Falcor/Utils/Color/ColorHelpers.slang @@ -30,12 +30,14 @@ BEGIN_NAMESPACE_FALCOR -/** This file contains host/device shared color utility functions. -*/ - -/** Returns a relative luminance of an input linear RGB color in the ITU-R BT.709 color space - \param RGBColor linear HDR RGB color in the ITU-R BT.709 color space -*/ +/** + * This file contains host/device shared color utility functions. + */ + +/** + * Returns a relative luminance of an input linear RGB color in the ITU-R BT.709 color space + * @param RGBColor linear HDR RGB color in the ITU-R BT.709 color space + */ inline float luminance(float3 rgb) { return dot(rgb, float3(0.2126f, 0.7152f, 0.0722f)); @@ -43,36 +45,37 @@ inline float luminance(float3 rgb) #ifndef HOST_CODE // TODO: Unify this code with the host-side functions in ColorUtils.h when #175 is solved. -/** Transforms an RGB color in Rec.709 to CIE XYZ. -*/ +/** + * Transforms an RGB color in Rec.709 to CIE XYZ. + */ float3 RGBtoXYZ_Rec709(float3 c) { - static const float3x3 M = - { - 0.4123907992659595, 0.3575843393838780, 0.1804807884018343, - 0.2126390058715104, 0.7151686787677559, 0.0721923153607337, - 0.0193308187155918, 0.1191947797946259, 0.9505321522496608 + static const float3x3 M = { + 0.4123907992659595, 0.3575843393838780, 0.1804807884018343, // row 0 + 0.2126390058715104, 0.7151686787677559, 0.0721923153607337, // row 1 + 0.0193308187155918, 0.1191947797946259, 0.9505321522496608, // row 2 }; return mul(M, c); } -/** Transforms an XYZ color to RGB in Rec.709. -*/ +/** + * Transforms an XYZ color to RGB in Rec.709. + */ float3 XYZtoRGB_Rec709(float3 c) { - static const float3x3 M = - { - 3.240969941904522, -1.537383177570094, -0.4986107602930032, - -0.9692436362808803, 1.875967501507721, 0.04155505740717569, - 0.05563007969699373, -0.2039769588889765, 1.056971514242878 + static const float3x3 M = { + 3.240969941904522, -1.537383177570094, -0.4986107602930032, // row 0 + -0.9692436362808803, 1.875967501507721, 0.04155505740717569, // row 1 + 0.05563007969699373, -0.2039769588889765, 1.056971514242878, // row 2 }; return mul(M, c); } #endif -/** Converts color from RGB to YCgCo space - \param RGBColor linear HDR RGB color -*/ +/** + * Converts color from RGB to YCgCo space + * @param RGBColor linear HDR RGB color + */ inline float3 RGBToYCgCo(float3 rgb) { float Y = dot(rgb, float3(0.25f, 0.50f, 0.25f)); @@ -81,9 +84,10 @@ inline float3 RGBToYCgCo(float3 rgb) return float3(Y, Cg, Co); } -/** Converts color from YCgCo to RGB space - \param YCgCoColor linear HDR YCgCo color -*/ +/** + * Converts color from YCgCo to RGB space + * @param YCgCoColor linear HDR YCgCo color + */ inline float3 YCgCoToRGB(float3 YCgCo) { float tmp = YCgCo.x - YCgCo.y; @@ -93,9 +97,10 @@ inline float3 YCgCoToRGB(float3 YCgCo) return float3(r, g, b); } -/** Returns a YUV version of an input linear RGB color in the ITU-R BT.709 color space - \param RGBColor linear HDR RGB color in the ITU-R BT.709 color space -*/ +/** + * Returns a YUV version of an input linear RGB color in the ITU-R BT.709 color space + * @param RGBColor linear HDR RGB color in the ITU-R BT.709 color space + */ inline float3 RGBToYUV(float3 rgb) { float3 ret; @@ -105,9 +110,10 @@ inline float3 RGBToYUV(float3 rgb) return ret; } -/** Returns a RGB version of an input linear YUV color in the ITU-R BT.709 color space - \param YUVColor linear HDR YUV color in the ITU-R BT.709 color space -*/ +/** + * Returns a RGB version of an input linear YUV color in the ITU-R BT.709 color space + * @param YUVColor linear HDR YUV color in the ITU-R BT.709 color space + */ inline float3 YUVToRGB(float3 yuv) { float3 ret; @@ -117,9 +123,10 @@ inline float3 YUVToRGB(float3 yuv) return ret; } -/** Returns a linear-space RGB version of an input RGB channel value in the ITU-R BT.709 color space - \param sRGBColor sRGB input channel value -*/ +/** + * Returns a linear-space RGB version of an input RGB channel value in the ITU-R BT.709 color space + * @param sRGBColor sRGB input channel value + */ inline float sRGBToLinear(float srgb) { if (srgb <= 0.04045f) @@ -132,20 +139,19 @@ inline float sRGBToLinear(float srgb) } } -/** Returns a linear-space RGB version of an input RGB color in the ITU-R BT.709 color space - \param sRGBColor sRGB input color -*/ +/** + * Returns a linear-space RGB version of an input RGB color in the ITU-R BT.709 color space + * @param sRGBColor sRGB input color + */ inline float3 sRGBToLinear(float3 srgb) { - return float3( - sRGBToLinear(srgb.x), - sRGBToLinear(srgb.y), - sRGBToLinear(srgb.z)); + return float3(sRGBToLinear(srgb.x), sRGBToLinear(srgb.y), sRGBToLinear(srgb.z)); } -/** Returns a sRGB version of an input linear RGB channel value in the ITU-R BT.709 color space - \param LinearColor linear input channel value -*/ +/** + * Returns a sRGB version of an input linear RGB channel value in the ITU-R BT.709 color space + * @param LinearColor linear input channel value + */ inline float linearToSRGB(float lin) { if (lin <= 0.0031308f) @@ -158,26 +164,26 @@ inline float linearToSRGB(float lin) } } -/** Returns a sRGB version of an input linear RGB color in the ITU-R BT.709 color space - \param LinearColor linear input color -*/ +/** + * Returns a sRGB version of an input linear RGB color in the ITU-R BT.709 color space + * @param LinearColor linear input color + */ inline float3 linearToSRGB(float3 lin) { - return float3( - linearToSRGB(lin.x), - linearToSRGB(lin.y), - linearToSRGB(lin.z)); + return float3(linearToSRGB(lin.x), linearToSRGB(lin.y), linearToSRGB(lin.z)); } - -/** Returns Michelson contrast given minimum and maximum intensities of an image region - \param iMin minimum intensity of an image region - \param iMax maximum intensity of an image region -*/ +/** + * Returns Michelson contrast given minimum and maximum intensities of an image region + * @param iMin minimum intensity of an image region + * @param iMax maximum intensity of an image region + */ inline float computeMichelsonContrast(float iMin, float iMax) { - if (iMin == 0.0f && iMax == 0.0f) return 0.0f; - else return (iMax - iMin) / (iMax + iMin); + if (iMin == 0.0f && iMax == 0.0f) + return 0.0f; + else + return (iMax - iMin) / (iMax + iMin); } static const float3 kD65ReferenceIlluminant = float3(0.950428545, 1.000000000, 1.088900371); diff --git a/Source/Falcor/Utils/Color/ColorMap.slang b/Source/Falcor/Utils/Color/ColorMap.slang index 5fb9c8e27..b4aa1388d 100644 --- a/Source/Falcor/Utils/Color/ColorMap.slang +++ b/Source/Falcor/Utils/Color/ColorMap.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-21, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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,49 +26,53 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **************************************************************************/ -/** Helpers for mapping scalar values to RGB color for visualization purposes. +/** + * Helpers for mapping scalar values to RGB color for visualization purposes. + * + * 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. + * + * The colormapViridis(), colormapPlasma(), colormapMagma() and colormapInferno() are + * based on https://www.shadertoy.com/view/WlfXRN. They are fitted from data found on + * https://github.com/BIDS/colormap/blob/master/colormaps.py. + */ - 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. - - The colormapViridis(), colormapPlasma(), colormapMagma() and colormapInferno() are - based on https://www.shadertoy.com/view/WlfXRN. They are fitted from data found on - https://github.com/BIDS/colormap/blob/master/colormaps.py. -*/ - -/** 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]. -*/ +/** + * 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); } -/** 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]. -*/ +/** + * 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)); + // 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 * saturate(x) - float3(3, 2, 1))); } -/** Maps scalar value to the 'viridis' color map. - 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]. -*/ +/** + * Maps scalar value to the 'viridis' color map. + * 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 colormapViridis(float x) { const float3 c0 = float3(0.2777273272234177, 0.005407344544966578, 0.3340998053353061); @@ -79,14 +83,15 @@ float3 colormapViridis(float x) const float3 c5 = float3(4.776384997670288, -13.74514537774601, -65.35303263337234); const float3 c6 = float3(-5.435455855934631, 4.645852612178535, 26.3124352495832); x = saturate(x); - return saturate(c0+x*(c1+x*(c2+x*(c3+x*(c4+x*(c5+x*c6)))))); + return saturate(c0 + x * (c1 + x * (c2 + x * (c3 + x * (c4 + x * (c5 + x * c6)))))); } -/** Maps scalar value to the 'plasma' color map. - 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]. -*/ +/** + * Maps scalar value to the 'plasma' color map. + * 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 colormapPlasma(float x) { const float3 c0 = float3(0.05873234392399702, 0.02333670892565664, 0.5433401826748754); @@ -97,14 +102,15 @@ float3 colormapPlasma(float x) const float3 c5 = float3(10.02306557647065, 71.41361770095349, -54.07218655560067); const float3 c6 = float3(-3.658713842777788, -22.93153465461149, 18.19190778539828); x = saturate(x); - return saturate(c0+x*(c1+x*(c2+x*(c3+x*(c4+x*(c5+x*c6)))))); + return saturate(c0 + x * (c1 + x * (c2 + x * (c3 + x * (c4 + x * (c5 + x * c6)))))); } -/** Maps scalar value to the 'magma' color map. - 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]. -*/ +/** + * Maps scalar value to the 'magma' color map. + * 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 colormapMagma(float x) { const float3 c0 = float3(-0.002136485053939582, -0.000749655052795221, -0.005386127855323933); @@ -115,14 +121,15 @@ float3 colormapMagma(float x) const float3 c5 = float3(-50.76852536473588, 29.04658282127291, 4.23415299384598); const float3 c6 = float3(18.65570506591883, -11.48977351997711, -5.601961508734096); x = saturate(x); - return saturate(c0+x*(c1+x*(c2+x*(c3+x*(c4+x*(c5+x*c6)))))); + return saturate(c0 + x * (c1 + x * (c2 + x * (c3 + x * (c4 + x * (c5 + x * c6)))))); } -/** Maps scalar value to the 'inferno' color map. - 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]. -*/ +/** + * Maps scalar value to the 'inferno' color map. + * 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 colormapInferno(float x) { const float3 c0 = float3(0.0002189403691192265, 0.001651004631001012, -0.01948089843709184); @@ -133,5 +140,5 @@ float3 colormapInferno(float x) const float3 c5 = float3(-71.31942824499214, 32.62606426397723, 73.20951985803202); const float3 c6 = float3(25.13112622477341, -12.24266895238567, -23.07032500287172); x = saturate(x); - return saturate(c0+x*(c1+x*(c2+x*(c3+x*(c4+x*(c5+x*c6)))))); + return saturate(c0 + x * (c1 + x * (c2 + x * (c3 + x * (c4 + x * (c5 + x * c6)))))); } diff --git a/Source/Falcor/Utils/Color/ColorUtils.h b/Source/Falcor/Utils/Color/ColorUtils.h index 053302ec5..36f279b1e 100644 --- a/Source/Falcor/Utils/Color/ColorUtils.h +++ b/Source/Falcor/Utils/Color/ColorUtils.h @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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,193 +27,192 @@ **************************************************************************/ #pragma once -#include "Utils/Math/Matrix/Matrix.h" #include "Core/ErrorHandling.h" #include "Utils/Math/Vector.h" - -/** 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 ] - -*/ +#include "Utils/Math/Matrix.h" + +/** + * 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 ] + * + */ namespace Falcor { - // Transform from RGB color in Rec.709 to CIE XYZ. - static const rmcv::mat3 kColorTransform_RGBtoXYZ_Rec709 = - { - 0.4123907992659595, 0.3575843393838780, 0.1804807884018343, - 0.2126390058715104, 0.7151686787677559, 0.0721923153607337, - 0.0193308187155918, 0.1191947797946259, 0.9505321522496608 - }; +// Transform from RGB color in Rec.709 to CIE XYZ. +static const float3x3 kColorTransform_RGBtoXYZ_Rec709 = { + 0.4123907992659595, 0.3575843393838780, 0.1804807884018343, // row 0 + 0.2126390058715104, 0.7151686787677559, 0.0721923153607337, // row 1 + 0.0193308187155918, 0.1191947797946259, 0.9505321522496608 // row 2 +}; + +// Transform from XYZ color to RGB in Rec.709. +static const float3x3 kColorTransform_XYZtoRGB_Rec709 = { + 3.2409699419045213, -1.5373831775700935, -0.4986107602930033, // row 0 + -0.9692436362808798, 1.8759675015077206, 0.0415550574071756, // row 1 + 0.0556300796969936, -0.2039769588889765, 1.0569715142428784 // row 2 +}; + +// Transform from CIE XYZ to LMS using the CAT02 transform. +static const float3x3 kColorTransform_XYZtoLMS_CAT02 = { + 0.7328, 0.4296, -0.1624, // row 0 + -0.7036, 1.6975, 0.0061, // row 1 + 0.0030, 0.0136, 0.9834 // row 2 +}; + +// Transform from LMS to CIE XYZ using the inverse CAT02 transform. +static const float3x3 kColorTransform_LMStoXYZ_CAT02 = { + 1.096123820835514, -0.278869000218287, 0.182745179382773, // row 0 + 0.454369041975359, 0.473533154307412, 0.072097803717229, // row 1 + -0.009627608738429, -0.005698031216113, 1.015325639954543 // row 2 +}; + +// Transform from CIE XYZ to LMS using the Bradford transform. +static const float3x3 kColorTransform_XYZtoLMS_Bradford = { + 0.8951, 0.2664, -0.1614, // row 0 + -0.7502, 1.7135, 0.0367, // row 1 + 0.0389, -0.0685, 1.0296 // row 2 +}; + +// Transform from LMS to CIE XYZ using the inverse Bradford transform. +static const float3x3 kColorTransform_LMStoXYZ_Bradford = { + 0.98699290546671214, -0.14705425642099013, 0.15996265166373122, // row 0 + 0.43230526972339445, 0.51836027153677744, 0.04929122821285559, // row 1 + -0.00852866457517732, 0.04004282165408486, 0.96848669578754998 // row 2 +}; + +/** + * Transforms an RGB color in Rec.709 to CIE XYZ. + */ +static float3 RGBtoXYZ_Rec709(float3 c) +{ + return mul(kColorTransform_RGBtoXYZ_Rec709, c); +} - // Transform from XYZ color to RGB in Rec.709. - static const rmcv::mat3 kColorTransform_XYZtoRGB_Rec709 = - { - 3.2409699419045213, -1.5373831775700935, -0.4986107602930033, - -0.9692436362808798, 1.8759675015077206, 0.0415550574071756, - 0.0556300796969936, -0.2039769588889765, 1.0569715142428784 - }; +/** + * Transforms an XYZ color to RGB in Rec.709. + */ +static float3 XYZtoRGB_Rec709(float3 c) +{ + return mul(kColorTransform_XYZtoRGB_Rec709, c); +} - // Transform from CIE XYZ to LMS using the CAT02 transform. - static const rmcv::mat3 kColorTransform_XYZtoLMS_CAT02 = - { - 0.7328, 0.4296, -0.1624, - -0.7036, 1.6975, 0.0061, - 0.0030, 0.0136, 0.9834 - }; +/** + * Converts (chromaticities, luminance) to XYZ color. + */ +static float3 xyYtoXYZ(float x, float y, float Y) +{ + return float3(x * Y / y, Y, (1.f - x - y) * Y / y); +} - // Transform from LMS to CIE XYZ using the inverse CAT02 transform. - static const rmcv::mat3 kColorTransform_LMStoXYZ_CAT02 = +/** + * 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 float3 colorTemperatureToXYZ(float T, float Y = 1.f) +{ + if (T < 1667.f || T > 25000.f) { - 1.096123820835514, -0.278869000218287, 0.182745179382773, - 0.454369041975359, 0.473533154307412, 0.072097803717229, - -0.009627608738429, -0.005698031216113, 1.015325639954543 - }; + reportError("colorTemperatureToXYZ() - T is out of range"); + return float3(0, 0, 0); + } + // We do the computations in double + double t = T; + double t2 = t * t; + double t3 = t * t * t; - // Transform from CIE XYZ to LMS using the Bradford transform. - static const rmcv::mat3 kColorTransform_XYZtoLMS_Bradford = + double xc = 0.0; + if (T < 4000.f) { - 0.8951, 0.2664, -0.1614, - -0.7502, 1.7135, 0.0367, - 0.0389, -0.0685, 1.0296 - }; - - // Transform from LMS to CIE XYZ using the inverse Bradford transform. - static const rmcv::mat3 kColorTransform_LMStoXYZ_Bradford = - { - 0.98699290546671214, -0.14705425642099013, 0.15996265166373122, - 0.43230526972339445, 0.51836027153677744, 0.04929122821285559, - -0.00852866457517732, 0.04004282165408486, 0.96848669578754998 - }; - - /** Transforms an RGB color in Rec.709 to CIE XYZ. - */ - static float3 RGBtoXYZ_Rec709(float3 c) + xc = -0.2661239e9 / t3 - 0.2343580e6 / t2 + 0.8776956e3 / t + 0.179910; + } + else { - return kColorTransform_RGBtoXYZ_Rec709 * c; + xc = -3.0258469e9 / t3 + 2.1070379e6 / t2 + 0.2226347e3 / t + 0.240390; } - /** Transforms an XYZ color to RGB in Rec.709. - */ - static float3 XYZtoRGB_Rec709(float3 c) + double x = xc; + double x2 = x * x; + double x3 = x * x * x; + + double yc = 0.0; + if (T < 2222.f) { - return kColorTransform_XYZtoRGB_Rec709 * c; + yc = -1.1063814 * x3 - 1.34811020 * x2 + 2.18555832 * x - 0.20219683; } - - /** Converts (chromaticities, luminance) to XYZ color. - */ - static float3 xyYtoXYZ(float x, float y, float Y) + else if (T < 4000.f) { - return float3(x * Y / y, Y, (1.f - x - y) * Y / y); + yc = -0.9549476 * x3 - 1.37418593 * x2 + 2.09137015 * x - 0.16748867; } - - /** 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 float3 colorTemperatureToXYZ(float T, float Y = 1.f) + else { - if (T < 1667.f || T > 25000.f) - { - reportError("colorTemperatureToXYZ() - T is out of range"); - return 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); + yc = +3.0817580 * x3 - 5.87338670 * x2 + 3.75112997 * x - 0.37001483; } - /** 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. + // Return as XYZ color. + return xyYtoXYZ((float)xc, (float)yc, Y); +} - \param[in] T Target color temperature (K). - \return 3x3 matrix M, which transforms linear RGB in Rec.709 using c' = M * c. - */ - static rmcv::mat3 calculateWhiteBalanceTransformRGB_Rec709(float T) - { - static const rmcv::mat3 MA = kColorTransform_XYZtoLMS_CAT02 * kColorTransform_RGBtoXYZ_Rec709; // RGB -> LMS - static const rmcv::mat3 invMA = kColorTransform_XYZtoRGB_Rec709 * kColorTransform_LMStoXYZ_CAT02; // LMS -> RGB +/** + * 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 float3x3 calculateWhiteBalanceTransformRGB_Rec709(float T) +{ + static const float3x3 MA = mul(kColorTransform_XYZtoLMS_CAT02, kColorTransform_RGBtoXYZ_Rec709); // RGB -> LMS + static const float3x3 invMA = mul(kColorTransform_XYZtoRGB_Rec709, kColorTransform_LMStoXYZ_CAT02); // LMS -> RGB - // Compute destination reference white in LMS space. - static const float3 wd = kColorTransform_XYZtoLMS_CAT02 * colorTemperatureToXYZ(6500.f); + // Compute destination reference white in LMS space. + static const float3 wd = mul(kColorTransform_XYZtoLMS_CAT02, colorTemperatureToXYZ(6500.f)); - // Compute source reference white in LMS space. - const float3 ws = kColorTransform_XYZtoLMS_CAT02 * colorTemperatureToXYZ(T); + // Compute source reference white in LMS space. + const float3 ws = mul(kColorTransform_XYZtoLMS_CAT02, colorTemperatureToXYZ(T)); - // Derive final 3x3 transform in RGB space. - float3 scale = wd / ws; - rmcv::mat3 D = rmcv::diagonal3x3(scale); + // Derive final 3x3 transform in RGB space. + float3 scale = wd / ws; + float3x3 D = math::matrixFromDiagonal(scale); - return invMA * D * MA; - } + return mul(mul(invMA, D), MA); } +} // namespace Falcor diff --git a/Source/Falcor/Utils/Color/SampledSpectrum.h b/Source/Falcor/Utils/Color/SampledSpectrum.h index 1ecd65514..2dc2709bf 100644 --- a/Source/Falcor/Utils/Color/SampledSpectrum.h +++ b/Source/Falcor/Utils/Color/SampledSpectrum.h @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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,167 +36,178 @@ namespace Falcor { - enum class SpectrumInterpolation +enum class SpectrumInterpolation +{ + Linear, ///< Piecewise linear interpolation of the two nearest samples, and zero outside of the end points. +}; + +/** + * Represents a uniformly sampled spectrum. + * This class is templated on the value type stored at each sample. + * The first sample is centered at lambdaStart and the last sample is centered at lambdaEnd. + * The spectrum is zero outside the stored wavelength range. + * Example: lambdaStart = 400 nm, lambdaEnd = 700, sampleCount = 4. This means that the bins are centered at 400, 500, 600, 700 nm. + * The bins are [400, 450], [450, 550], [550, 650], [650, 700]. + * Evaluation is done by evaluating basis functions at the center of the bins. + */ +template +class SampledSpectrum +{ +public: + using value_type = T; + static_assert( + std::is_floating_point_v || std::is_same_v || std::is_same_v || std::is_same_v, + "T must be a floating point scalar or vector" + ); + + /** + * Create a spectrum initialized to zero. + * @param[in] lambdaStart First sampled wavelength in nm. + * @param[in] lambdaEnd Last sampled wavelength in nm. + * @param[in] sampleCount Number of wavelength samples stored. + */ + SampledSpectrum(float lambdaStart, float lambdaEnd, size_t sampleCount) : mLambdaStart(lambdaStart), mLambdaEnd(lambdaEnd) { - Linear, ///< Piecewise linear interpolation of the two nearest samples, and zero outside of the end points. - }; - - /** Represents a uniformly sampled spectrum. - This class is templated on the value type stored at each sample. - The first sample is centered at lambdaStart and the last sample is centered at lambdaEnd. - The spectrum is zero outside the stored wavelength range. - Example: lambdaStart = 400 nm, lambdaEnd = 700, sampleCount = 4. This means that the bins are centered at 400, 500, 600, 700 nm. - The bins are [400, 450], [450, 550], [550, 650], [650, 700]. - Evaluation is done by evaluating basis functions at the center of the bins. - */ - template - class SampledSpectrum + checkArgument(lambdaEnd > lambdaStart, "'lambdaEnd' must be larger than 'lambdaStart'."); + checkArgument(sampleCount > 0, "'sampleCount' must be at least one."); + mSamples.resize(sampleCount, value_type(0)); + } + + /** + * Create a spectrum initialized from sample array. + * @param[in] lambdaStart First sampled wavelength in nm. + * @param[in] lambdaEnd Last sampled wavelength in nm. + * @param[in] sampleCount Number of wavelength samples stored. + * @param[in] pSamples Spectral samples. + */ + SampledSpectrum(float lambdaStart, float lambdaEnd, size_t sampleCount, const value_type* pSamples) + : SampledSpectrum(lambdaStart, lambdaEnd, sampleCount) { - public: - using value_type = T; - static_assert(std::is_floating_point_v || std::is_same_v || std::is_same_v || std::is_same_v, "T must be a floating point scalar or vector"); - - /** Create a spectrum initialized to zero. - \param[in] lambdaStart First sampled wavelength in nm. - \param[in] lambdaEnd Last sampled wavelength in nm. - \param[in] sampleCount Number of wavelength samples stored. - */ - SampledSpectrum(float lambdaStart, float lambdaEnd, size_t sampleCount) - : mLambdaStart(lambdaStart) - , mLambdaEnd(lambdaEnd) - { - checkArgument(lambdaEnd > lambdaStart, "'lambdaEnd' must be larger than 'lambdaStart'."); - checkArgument(sampleCount > 0, "'sampleCount' must be at least one."); - mSamples.resize(sampleCount, value_type(0)); - } - - /** Create a spectrum initialized from sample array. - \param[in] lambdaStart First sampled wavelength in nm. - \param[in] lambdaEnd Last sampled wavelength in nm. - \param[in] sampleCount Number of wavelength samples stored. - \param[in] pSamples Spectral samples. - */ - SampledSpectrum(float lambdaStart, float lambdaEnd, size_t sampleCount, const value_type* pSamples) - : SampledSpectrum(lambdaStart, lambdaEnd, sampleCount) - { - set(sampleCount, pSamples); - } - - /** Set spectrum samples. - \param[in] sampleCount Size of array in samples. - \param[in] pSamples Array of spectral samples. - */ - void set(const size_t sampleCount, const value_type* pSamples) - { - checkArgument(pSamples != nullptr, "'pSamples' is nullptr."); - checkArgument(sampleCount == mSamples.size(), "Sample count mismatch."); - mSamples.assign(pSamples, pSamples + sampleCount); - } - - /** Set spectrum samples. - \param[in] samples Spectral samples. - */ - void set(const std::vector& samples) - { - checkArgument(samples.size() == mSamples.size(), "Sample count mismatch."); - mSamples = samples; - } - - /** Set spectrum from unsorted spectral data. - The spectral data is intergrated using piecewise linear interpolation to compute the value of each sample. - \param[in] sampleCount Size of arrays in samples. - \param[in] pSamples Array of spectral samples. - \param[in] pLambdas Array of sample wavelengths. - */ - void set(const size_t sampleCount, const value_type* pSamples, const float* pLambdas) - { - checkArgument(pSamples != nullptr && pLambdas != nullptr, "'pSamples' or 'pLambdas' is nullptr."); - FALCOR_UNIMPLEMENTED(); - } - - /** Set spectrum from unsorted spectral data. - The spectral data is intergrated using piecewise linear interpolation to compute the value of each sample. - \param[in] samples Spectral samples. - \param[in] lambdas Sample wavelengths. - */ - void set(const std::vector& samples, const std::vector& lambdas) - { - checkArgument(!samples.empty() && samples.size() == lambdas.size(), "'samples' and 'lambdas' must be non-empty and of equal length."); - set(samples.size(), samples.data(), lambdas.data()); - } - - /** Evaluates the spectrum at the given wavelength. - \param[in] lambda Wavelength in nm. - \param[in] interpolationType Which type of interpolation should be used. Linear is the default. - \return Interpolated value. - */ - value_type eval(const float lambda, const SpectrumInterpolation interpolationType = SpectrumInterpolation::Linear) const; - - /** Return the CIE 1931 tristimulus values for the spectrum. - This only works on spectra of scalar types. - */ - float3 toXYZ_CIE1931() const; - - /** Returns the size of the spectrum in number of samples. - \return Return the number of wavelength samples in the spectrum. - */ - size_t size() const - { - return mSamples.size(); - } - - /** Return the sample with index. - \param[in] index Index to the sample that is wanted. - \return The sample. - */ - value_type get(size_t index) const - { - FALCOR_ASSERT(index < size()); - return mSamples[index]; - } - - /** Set the sample with index. - \param[in] index Index to the sample that should be set. - \param[in] value Value of the sample to be set. - */ - void set(size_t index, T value) - { - FALCOR_ASSERT(index < size()); - mSamples[index] = value; - } - - /** Return the wavelength range. - \return The wavelength range of the spectrum. - */ - float2 getWavelengthRange() const - { - return float2(mLambdaStart, mLambdaEnd); - } - - private: - float mLambdaStart; ///< First wavelength sample in nm. - float mLambdaEnd; ///< Last wavelength sample in nm. - std::vector mSamples; ///< Sample values. - }; - - - // Implementation - - template - float3 SampledSpectrum::toXYZ_CIE1931() const + set(sampleCount, pSamples); + } + + /** + * Set spectrum samples. + * @param[in] sampleCount Size of array in samples. + * @param[in] pSamples Array of spectral samples. + */ + void set(const size_t sampleCount, const value_type* pSamples) { + checkArgument(pSamples != nullptr, "'pSamples' is nullptr."); + checkArgument(sampleCount == mSamples.size(), "Sample count mismatch."); + mSamples.assign(pSamples, pSamples + sampleCount); + } + + /** + * Set spectrum samples. + * @param[in] samples Spectral samples. + */ + void set(const std::vector& samples) + { + checkArgument(samples.size() == mSamples.size(), "Sample count mismatch."); + mSamples = samples; + } + + /** + * Set spectrum from unsorted spectral data. + * The spectral data is intergrated using piecewise linear interpolation to compute the value of each sample. + * @param[in] sampleCount Size of arrays in samples. + * @param[in] pSamples Array of spectral samples. + * @param[in] pLambdas Array of sample wavelengths. + */ + void set(const size_t sampleCount, const value_type* pSamples, const float* pLambdas) + { + checkArgument(pSamples != nullptr && pLambdas != nullptr, "'pSamples' or 'pLambdas' is nullptr."); FALCOR_UNIMPLEMENTED(); } - template - T SampledSpectrum::eval(const float lambda, const SpectrumInterpolation interpolationType) const + /** + * Set spectrum from unsorted spectral data. + * The spectral data is intergrated using piecewise linear interpolation to compute the value of each sample. + * @param[in] samples Spectral samples. + * @param[in] lambdas Sample wavelengths. + */ + void set(const std::vector& samples, const std::vector& lambdas) + { + checkArgument( + !samples.empty() && samples.size() == lambdas.size(), "'samples' and 'lambdas' must be non-empty and of equal length." + ); + set(samples.size(), samples.data(), lambdas.data()); + } + + /** + * Evaluates the spectrum at the given wavelength. + * @param[in] lambda Wavelength in nm. + * @param[in] interpolationType Which type of interpolation should be used. Linear is the default. + * @return Interpolated value. + */ + value_type eval(const float lambda, const SpectrumInterpolation interpolationType = SpectrumInterpolation::Linear) const; + + /** + * Return the CIE 1931 tristimulus values for the spectrum. + * This only works on spectra of scalar types. + */ + float3 toXYZ_CIE1931() const; + + /** + * Returns the size of the spectrum in number of samples. + * @return Return the number of wavelength samples in the spectrum. + */ + size_t size() const { return mSamples.size(); } + + /** + * Return the sample with index. + * @param[in] index Index to the sample that is wanted. + * @return The sample. + */ + value_type get(size_t index) const { - checkInvariant(interpolationType == SpectrumInterpolation::Linear, "Interpolation type must be 'Linear'"); - if (lambda < mLambdaStart || lambda > mLambdaEnd) return T(0); - float x = ((lambda - mLambdaStart) / (mLambdaEnd - mLambdaStart))* (size() - 1.0f); - size_t i = (size_t)std::floor(x); - if (i + 1 >= mSamples.size()) return mSamples[size() - 1]; - float w = x - (float)i; - return lerp(mSamples[i], mSamples[i + 1], T(w)); + FALCOR_ASSERT(index < size()); + return mSamples[index]; } + + /** + * Set the sample with index. + * @param[in] index Index to the sample that should be set. + * @param[in] value Value of the sample to be set. + */ + void set(size_t index, T value) + { + FALCOR_ASSERT(index < size()); + mSamples[index] = value; + } + + /** + * Return the wavelength range. + * @return The wavelength range of the spectrum. + */ + float2 getWavelengthRange() const { return float2(mLambdaStart, mLambdaEnd); } + +private: + float mLambdaStart; ///< First wavelength sample in nm. + float mLambdaEnd; ///< Last wavelength sample in nm. + std::vector mSamples; ///< Sample values. +}; + +// Implementation + +template +float3 SampledSpectrum::toXYZ_CIE1931() const +{ + FALCOR_UNIMPLEMENTED(); +} + +template +T SampledSpectrum::eval(const float lambda, const SpectrumInterpolation interpolationType) const +{ + checkInvariant(interpolationType == SpectrumInterpolation::Linear, "Interpolation type must be 'Linear'"); + if (lambda < mLambdaStart || lambda > mLambdaEnd) + return T(0); + float x = ((lambda - mLambdaStart) / (mLambdaEnd - mLambdaStart)) * (size() - 1.0f); + size_t i = (size_t)std::floor(x); + if (i + 1 >= mSamples.size()) + return mSamples[size() - 1]; + float w = x - (float)i; + return math::lerp(mSamples[i], mSamples[i + 1], T(w)); } +} // namespace Falcor diff --git a/Source/Falcor/Utils/Color/Spectrum.cpp b/Source/Falcor/Utils/Color/Spectrum.cpp index 525e3f8bb..3f7e3cce1 100644 --- a/Source/Falcor/Utils/Color/Spectrum.cpp +++ b/Source/Falcor/Utils/Color/Spectrum.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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,476 +33,212 @@ namespace Falcor { - // ------------------------------------------------------------------------ - // PiecewiseLinearSpectrum - // ------------------------------------------------------------------------ - - PiecewiseLinearSpectrum::PiecewiseLinearSpectrum(fstd::span wavelengths, fstd::span values) - : mWavelengths(wavelengths.begin(), wavelengths.end()) - , mValues(values.begin(), values.end()) - , mMaxValue(*std::max_element(values.begin(), values.end())) - { - checkArgument(wavelengths.size() == values.size(), "'wavelengths' and 'values' need to contain the same number of elements"); - } - - PiecewiseLinearSpectrum PiecewiseLinearSpectrum::fromInterleaved(fstd::span interleaved, bool normalize) - { - checkArgument(interleaved.size() % 2 == 0, "'interleaved' must have an even number of elements."); - - size_t count = interleaved.size() / 2; - std::vector wavelengths(count); - std::vector values(count); - - for (size_t i = 0; i < count; ++i) - { - wavelengths[i] = interleaved[i * 2]; - values[i] = interleaved[i * 2 + 1]; - checkArgument(i == 0 || wavelengths[i] >= wavelengths[i - 1], "'interleaved' must have wavelengths that are monotonic increasing."); - } - - auto spec = PiecewiseLinearSpectrum(wavelengths, values); +// ------------------------------------------------------------------------ +// PiecewiseLinearSpectrum +// ------------------------------------------------------------------------ + +PiecewiseLinearSpectrum::PiecewiseLinearSpectrum(fstd::span wavelengths, fstd::span values) + : mWavelengths(wavelengths.begin(), wavelengths.end()) + , mValues(values.begin(), values.end()) + , mMaxValue(*std::max_element(values.begin(), values.end())) +{ + checkArgument(wavelengths.size() == values.size(), "'wavelengths' and 'values' need to contain the same number of elements"); +} - if (normalize) - { - spec.scale(Spectra::kCIE_Y_Integral / innerProduct(spec, Spectra::kCIE_Y)); - } +PiecewiseLinearSpectrum PiecewiseLinearSpectrum::fromInterleaved(fstd::span interleaved, bool normalize) +{ + checkArgument(interleaved.size() % 2 == 0, "'interleaved' must have an even number of elements."); - return spec; - } + size_t count = interleaved.size() / 2; + std::vector wavelengths(count); + std::vector values(count); - std::optional PiecewiseLinearSpectrum::fromFile(const std::filesystem::path& path) + for (size_t i = 0; i < count; ++i) { - FALCOR_UNIMPLEMENTED(); + wavelengths[i] = interleaved[i * 2]; + values[i] = interleaved[i * 2 + 1]; + checkArgument(i == 0 || wavelengths[i] >= wavelengths[i - 1], "'interleaved' must have wavelengths that are monotonic increasing."); } - void PiecewiseLinearSpectrum::scale(float factor) + auto spec = PiecewiseLinearSpectrum(wavelengths, values); + + if (normalize) { - checkArgument(factor >= 0.f, "'factor' ({}) needs to be positive.", factor); - for (auto &value : mValues) value *= factor; - mMaxValue *= factor; + spec.scale(Spectra::kCIE_Y_Integral / innerProduct(spec, Spectra::kCIE_Y)); } - // ------------------------------------------------------------------------ - // BlackbodySpectrum - // ------------------------------------------------------------------------ + return spec; +} - float blackbodyEmission(float wavelength, float temperature) - { - if (temperature <= 0.f) return 0.f; - const float c = 299792458.f; - const float h = 6.62606957e-34f; - const float kb = 1.3806488e-23f; - // Return emitted radiance for blackbody at wavelength lambda. - float l = wavelength * 1e-9f; - float Le = (2.f * h * c * c) / ((l * l * l * l * l) * (std::exp((h * c) / (l * kb * temperature)) - 1.f)); - FALCOR_ASSERT(!std::isnan(Le)); - return Le; - } +std::optional PiecewiseLinearSpectrum::fromFile(const std::filesystem::path& path) +{ + FALCOR_UNIMPLEMENTED(); +} - BlackbodySpectrum::BlackbodySpectrum(float temperature, bool normalize) - : mTemperature(temperature) - { - // Compute wavelength (nm) of the peak of the spectrum using Wien's displacement law. - float peakWavelength = (2.8977721e-3f / mTemperature) * 1e9f; +void PiecewiseLinearSpectrum::scale(float factor) +{ + checkArgument(factor >= 0.f, "'factor' ({}) needs to be positive.", factor); + for (auto& value : mValues) + value *= factor; + mMaxValue *= factor; +} - // Setup normalization and max value constants. - float peakValue = blackbodyEmission(peakWavelength, mTemperature); - mNormalizationFactor = normalize ? 1.f / peakValue : 1.f; - mMaxValue = normalize ? 1.f : peakValue; - } +// ------------------------------------------------------------------------ +// BlackbodySpectrum +// ------------------------------------------------------------------------ - namespace - { - #include "Spectra.inl" - } +float blackbodyEmission(float wavelength, float temperature) +{ + if (temperature <= 0.f) + return 0.f; + const float c = 299792458.f; + const float h = 6.62606957e-34f; + const float kb = 1.3806488e-23f; + // Return emitted radiance for blackbody at wavelength lambda. + float l = wavelength * 1e-9f; + float Le = (2.f * h * c * c) / ((l * l * l * l * l) * (std::exp((h * c) / (l * kb * temperature)) - 1.f)); + FALCOR_ASSERT(!std::isnan(Le)); + return Le; +} - const DenseleySampledSpectrum Spectra::kCIE_X(360.f, 830.f, CIE_X); - const DenseleySampledSpectrum Spectra::kCIE_Y(360.f, 830.f, CIE_Y); - const DenseleySampledSpectrum Spectra::kCIE_Z(360.f, 830.f, CIE_Z); +BlackbodySpectrum::BlackbodySpectrum(float temperature, bool normalize) : mTemperature(temperature) +{ + // Compute wavelength (nm) of the peak of the spectrum using Wien's displacement law. + float peakWavelength = (2.8977721e-3f / mTemperature) * 1e9f; - namespace - { - const std::unordered_map kNamedSpectra - { - { - "glass-BK7", - PiecewiseLinearSpectrum::fromInterleaved(GlassBK7_eta, false) - }, - { - "glass-BAF10", - PiecewiseLinearSpectrum::fromInterleaved(GlassBAF10_eta, false) - }, - { - "glass-FK51A", - PiecewiseLinearSpectrum::fromInterleaved(GlassFK51A_eta, false) - }, - { - "glass-LASF9", - PiecewiseLinearSpectrum::fromInterleaved(GlassLASF9_eta, false) - }, - { - "glass-F5", - PiecewiseLinearSpectrum::fromInterleaved(GlassSF5_eta, false) - }, - { - "glass-F10", - PiecewiseLinearSpectrum::fromInterleaved(GlassSF10_eta, false) - }, - { - "glass-F11", - PiecewiseLinearSpectrum::fromInterleaved(GlassSF11_eta, false) - }, - - { - "metal-Ag-eta", - PiecewiseLinearSpectrum::fromInterleaved(Ag_eta, false) - }, - { - "metal-Ag-k", - PiecewiseLinearSpectrum::fromInterleaved(Ag_k, false) - }, - { - "metal-Al-eta", - PiecewiseLinearSpectrum::fromInterleaved(Al_eta, false) - }, - { - "metal-Al-k", - PiecewiseLinearSpectrum::fromInterleaved(Al_k, false) - }, - { - "metal-Au-eta", - PiecewiseLinearSpectrum::fromInterleaved(Au_eta, false) - }, - { - "metal-Au-k", - PiecewiseLinearSpectrum::fromInterleaved(Au_k, false) - }, - { - "metal-Cu-eta", - PiecewiseLinearSpectrum::fromInterleaved(Cu_eta, false) - }, - { - "metal-Cu-k", - PiecewiseLinearSpectrum::fromInterleaved(Cu_k, false) - }, - { - "metal-CuZn-eta", - PiecewiseLinearSpectrum::fromInterleaved(CuZn_eta, false) - }, - { - "metal-CuZn-k", - PiecewiseLinearSpectrum::fromInterleaved(CuZn_k, false) - }, - { - "metal-MgO-eta", - PiecewiseLinearSpectrum::fromInterleaved(MgO_eta, false) - }, - { - "metal-MgO-k", - PiecewiseLinearSpectrum::fromInterleaved(MgO_k, false) - }, - { - "metal-TiO2-eta", - PiecewiseLinearSpectrum::fromInterleaved(TiO2_eta, false) - }, - { - "metal-TiO2-k", - PiecewiseLinearSpectrum::fromInterleaved(TiO2_k, false) - }, - - { - "stdillum-A", - PiecewiseLinearSpectrum::fromInterleaved(CIE_Illum_A, true) - }, - { - "stdillum-D50", - PiecewiseLinearSpectrum::fromInterleaved(CIE_Illum_D5000, true) - }, - { - "stdillum-D65", - PiecewiseLinearSpectrum::fromInterleaved(CIE_Illum_D6500, true) - }, - { - "stdillum-F1", - PiecewiseLinearSpectrum::fromInterleaved(CIE_Illum_F1, true) - }, - { - "stdillum-F2", - PiecewiseLinearSpectrum::fromInterleaved(CIE_Illum_F2, true) - }, - { - "stdillum-F3", - PiecewiseLinearSpectrum::fromInterleaved(CIE_Illum_F3, true) - }, - { - "stdillum-F4", - PiecewiseLinearSpectrum::fromInterleaved(CIE_Illum_F4, true) - }, - { - "stdillum-F5", - PiecewiseLinearSpectrum::fromInterleaved(CIE_Illum_F5, true) - }, - { - "stdillum-F6", - PiecewiseLinearSpectrum::fromInterleaved(CIE_Illum_F6, true) - }, - { - "stdillum-F7", - PiecewiseLinearSpectrum::fromInterleaved(CIE_Illum_F7, true) - }, - { - "stdillum-F8", - PiecewiseLinearSpectrum::fromInterleaved(CIE_Illum_F8, true) - }, - { - "stdillum-F9", - PiecewiseLinearSpectrum::fromInterleaved(CIE_Illum_F9, true) - }, - { - "stdillum-F10", - PiecewiseLinearSpectrum::fromInterleaved(CIE_Illum_F10, true) - }, - { - "stdillum-F11", - PiecewiseLinearSpectrum::fromInterleaved(CIE_Illum_F11, true) - }, - { - "stdillum-F12", - PiecewiseLinearSpectrum::fromInterleaved(CIE_Illum_F12, true) - }, - - { - "illum-acesD60", - PiecewiseLinearSpectrum::fromInterleaved(ACES_Illum_D60, true) - }, - - { - "canon_eos_100d_r", - PiecewiseLinearSpectrum::fromInterleaved(canon_eos_100d_r, false) - }, - { - "canon_eos_100d_g", - PiecewiseLinearSpectrum::fromInterleaved(canon_eos_100d_g, false) - }, - { - "canon_eos_100d_b", - PiecewiseLinearSpectrum::fromInterleaved(canon_eos_100d_b, false) - }, - - { - "canon_eos_1dx_mkii_r", - PiecewiseLinearSpectrum::fromInterleaved(canon_eos_1dx_mkii_r, false) - }, - { - "canon_eos_1dx_mkii_g", - PiecewiseLinearSpectrum::fromInterleaved(canon_eos_1dx_mkii_g, false) - }, - { - "canon_eos_1dx_mkii_b", - PiecewiseLinearSpectrum::fromInterleaved(canon_eos_1dx_mkii_b, false) - }, - - { - "canon_eos_200d_r", - PiecewiseLinearSpectrum::fromInterleaved(canon_eos_200d_r, false) - }, - { - "canon_eos_200d_g", - PiecewiseLinearSpectrum::fromInterleaved(canon_eos_200d_g, false) - }, - { - "canon_eos_200d_b", - PiecewiseLinearSpectrum::fromInterleaved(canon_eos_200d_b, false) - }, - - { - "canon_eos_200d_mkii_r", - PiecewiseLinearSpectrum::fromInterleaved(canon_eos_200d_mkii_r, false) - }, - { - "canon_eos_200d_mkii_g", - PiecewiseLinearSpectrum::fromInterleaved(canon_eos_200d_mkii_g, false) - }, - { - "canon_eos_200d_mkii_b", - PiecewiseLinearSpectrum::fromInterleaved(canon_eos_200d_mkii_b, false) - }, - - { - "canon_eos_5d_r", - PiecewiseLinearSpectrum::fromInterleaved(canon_eos_5d_r, false) - }, - { - "canon_eos_5d_g", - PiecewiseLinearSpectrum::fromInterleaved(canon_eos_5d_g, false) - }, - { - "canon_eos_5d_b", - PiecewiseLinearSpectrum::fromInterleaved(canon_eos_5d_b, false) - }, - - { - "canon_eos_5d_mkii_r", - PiecewiseLinearSpectrum::fromInterleaved(canon_eos_5d_mkii_r, false) - }, - { - "canon_eos_5d_mkii_g", - PiecewiseLinearSpectrum::fromInterleaved(canon_eos_5d_mkii_g, false) - }, - { - "canon_eos_5d_mkii_b", - PiecewiseLinearSpectrum::fromInterleaved(canon_eos_5d_mkii_b, false) - }, - - { - "canon_eos_5d_mkiii_r", - PiecewiseLinearSpectrum::fromInterleaved(canon_eos_5d_mkiii_r, false) - }, - { - "canon_eos_5d_mkiii_g", - PiecewiseLinearSpectrum::fromInterleaved(canon_eos_5d_mkiii_g, false) - }, - { - "canon_eos_5d_mkiii_b", - PiecewiseLinearSpectrum::fromInterleaved(canon_eos_5d_mkiii_b, false) - }, - - { - "canon_eos_5d_mkiv_r", - PiecewiseLinearSpectrum::fromInterleaved(canon_eos_5d_mkiv_r, false) - }, - { - "canon_eos_5d_mkiv_g", - PiecewiseLinearSpectrum::fromInterleaved(canon_eos_5d_mkiv_g, false) - }, - { - "canon_eos_5d_mkiv_b", - PiecewiseLinearSpectrum::fromInterleaved(canon_eos_5d_mkiv_b, false) - }, - - { - "canon_eos_5ds_r", - PiecewiseLinearSpectrum::fromInterleaved(canon_eos_5ds_r, false) - }, - { - "canon_eos_5ds_g", - PiecewiseLinearSpectrum::fromInterleaved(canon_eos_5ds_g, false) - }, - { - "canon_eos_5ds_b", - PiecewiseLinearSpectrum::fromInterleaved(canon_eos_5ds_b, false) - }, - - { - "canon_eos_m_r", - PiecewiseLinearSpectrum::fromInterleaved(canon_eos_m_r, false) - }, - { - "canon_eos_m_g", - PiecewiseLinearSpectrum::fromInterleaved(canon_eos_m_g, false) - }, - { - "canon_eos_m_b", - PiecewiseLinearSpectrum::fromInterleaved(canon_eos_m_b, false) - }, - - { - "hasselblad_l1d_20c_r", - PiecewiseLinearSpectrum::fromInterleaved(hasselblad_l1d_20c_r, false) - }, - { - "hasselblad_l1d_20c_g", - PiecewiseLinearSpectrum::fromInterleaved(hasselblad_l1d_20c_g, false) - }, - { - "hasselblad_l1d_20c_b", - PiecewiseLinearSpectrum::fromInterleaved(hasselblad_l1d_20c_b, false) - }, - - { - "nikon_d810_r", - PiecewiseLinearSpectrum::fromInterleaved(nikon_d810_r, false) - }, - { - "nikon_d810_g", - PiecewiseLinearSpectrum::fromInterleaved(nikon_d810_g, false) - }, - { - "nikon_d810_b", - PiecewiseLinearSpectrum::fromInterleaved(nikon_d810_b, false) - }, - - { - "nikon_d850_r", - PiecewiseLinearSpectrum::fromInterleaved(nikon_d850_r, false) - }, - { - "nikon_d850_g", - PiecewiseLinearSpectrum::fromInterleaved(nikon_d850_g, false) - }, - { - "nikon_d850_b", - PiecewiseLinearSpectrum::fromInterleaved(nikon_d850_b, false) - }, - - { - "sony_ilce_6400_r", - PiecewiseLinearSpectrum::fromInterleaved(sony_ilce_6400_r, false) - }, - { - "sony_ilce_6400_g", - PiecewiseLinearSpectrum::fromInterleaved(sony_ilce_6400_g, false) - }, - { - "sony_ilce_6400_b", - PiecewiseLinearSpectrum::fromInterleaved(sony_ilce_6400_b, false) - }, - - { - "sony_ilce_7m3_r", - PiecewiseLinearSpectrum::fromInterleaved(sony_ilce_7m3_r, false) - }, - { - "sony_ilce_7m3_g", - PiecewiseLinearSpectrum::fromInterleaved(sony_ilce_7m3_g, false) - }, - { - "sony_ilce_7m3_b", - PiecewiseLinearSpectrum::fromInterleaved(sony_ilce_7m3_b, false) - }, - - { - "sony_ilce_7rm3_r", - PiecewiseLinearSpectrum::fromInterleaved(sony_ilce_7rm3_r, false) - }, - { - "sony_ilce_7rm3_g", - PiecewiseLinearSpectrum::fromInterleaved(sony_ilce_7rm3_g, false) - }, - { - "sony_ilce_7rm3_b", - PiecewiseLinearSpectrum::fromInterleaved(sony_ilce_7rm3_b, false) - }, - - { - "sony_ilce_9_r", - PiecewiseLinearSpectrum::fromInterleaved(sony_ilce_9_r, false) - }, - { - "sony_ilce_9_g", - PiecewiseLinearSpectrum::fromInterleaved(sony_ilce_9_g, false) - }, - { - "sony_ilce_9_b", - PiecewiseLinearSpectrum::fromInterleaved(sony_ilce_9_b, false) - } - }; - } + // Setup normalization and max value constants. + float peakValue = blackbodyEmission(peakWavelength, mTemperature); + mNormalizationFactor = normalize ? 1.f / peakValue : 1.f; + mMaxValue = normalize ? 1.f : peakValue; +} - const PiecewiseLinearSpectrum* Spectra::getNamedSpectrum(const std::string& name) - { - auto it = kNamedSpectra.find(name); - if (it == kNamedSpectra.end()) return nullptr; - return &it->second; - } +namespace +{ +#include "Spectra.inl" +} + +const DenseleySampledSpectrum Spectra::kCIE_X(360.f, 830.f, CIE_X); +const DenseleySampledSpectrum Spectra::kCIE_Y(360.f, 830.f, CIE_Y); +const DenseleySampledSpectrum Spectra::kCIE_Z(360.f, 830.f, CIE_Z); + +namespace +{ +const std::unordered_map kNamedSpectra{ + {"glass-BK7", PiecewiseLinearSpectrum::fromInterleaved(GlassBK7_eta, false)}, + {"glass-BAF10", PiecewiseLinearSpectrum::fromInterleaved(GlassBAF10_eta, false)}, + {"glass-FK51A", PiecewiseLinearSpectrum::fromInterleaved(GlassFK51A_eta, false)}, + {"glass-LASF9", PiecewiseLinearSpectrum::fromInterleaved(GlassLASF9_eta, false)}, + {"glass-F5", PiecewiseLinearSpectrum::fromInterleaved(GlassSF5_eta, false)}, + {"glass-F10", PiecewiseLinearSpectrum::fromInterleaved(GlassSF10_eta, false)}, + {"glass-F11", PiecewiseLinearSpectrum::fromInterleaved(GlassSF11_eta, false)}, + + {"metal-Ag-eta", PiecewiseLinearSpectrum::fromInterleaved(Ag_eta, false)}, + {"metal-Ag-k", PiecewiseLinearSpectrum::fromInterleaved(Ag_k, false)}, + {"metal-Al-eta", PiecewiseLinearSpectrum::fromInterleaved(Al_eta, false)}, + {"metal-Al-k", PiecewiseLinearSpectrum::fromInterleaved(Al_k, false)}, + {"metal-Au-eta", PiecewiseLinearSpectrum::fromInterleaved(Au_eta, false)}, + {"metal-Au-k", PiecewiseLinearSpectrum::fromInterleaved(Au_k, false)}, + {"metal-Cu-eta", PiecewiseLinearSpectrum::fromInterleaved(Cu_eta, false)}, + {"metal-Cu-k", PiecewiseLinearSpectrum::fromInterleaved(Cu_k, false)}, + {"metal-CuZn-eta", PiecewiseLinearSpectrum::fromInterleaved(CuZn_eta, false)}, + {"metal-CuZn-k", PiecewiseLinearSpectrum::fromInterleaved(CuZn_k, false)}, + {"metal-MgO-eta", PiecewiseLinearSpectrum::fromInterleaved(MgO_eta, false)}, + {"metal-MgO-k", PiecewiseLinearSpectrum::fromInterleaved(MgO_k, false)}, + {"metal-TiO2-eta", PiecewiseLinearSpectrum::fromInterleaved(TiO2_eta, false)}, + {"metal-TiO2-k", PiecewiseLinearSpectrum::fromInterleaved(TiO2_k, false)}, + + {"stdillum-A", PiecewiseLinearSpectrum::fromInterleaved(CIE_Illum_A, true)}, + {"stdillum-D50", PiecewiseLinearSpectrum::fromInterleaved(CIE_Illum_D5000, true)}, + {"stdillum-D65", PiecewiseLinearSpectrum::fromInterleaved(CIE_Illum_D6500, true)}, + {"stdillum-F1", PiecewiseLinearSpectrum::fromInterleaved(CIE_Illum_F1, true)}, + {"stdillum-F2", PiecewiseLinearSpectrum::fromInterleaved(CIE_Illum_F2, true)}, + {"stdillum-F3", PiecewiseLinearSpectrum::fromInterleaved(CIE_Illum_F3, true)}, + {"stdillum-F4", PiecewiseLinearSpectrum::fromInterleaved(CIE_Illum_F4, true)}, + {"stdillum-F5", PiecewiseLinearSpectrum::fromInterleaved(CIE_Illum_F5, true)}, + {"stdillum-F6", PiecewiseLinearSpectrum::fromInterleaved(CIE_Illum_F6, true)}, + {"stdillum-F7", PiecewiseLinearSpectrum::fromInterleaved(CIE_Illum_F7, true)}, + {"stdillum-F8", PiecewiseLinearSpectrum::fromInterleaved(CIE_Illum_F8, true)}, + {"stdillum-F9", PiecewiseLinearSpectrum::fromInterleaved(CIE_Illum_F9, true)}, + {"stdillum-F10", PiecewiseLinearSpectrum::fromInterleaved(CIE_Illum_F10, true)}, + {"stdillum-F11", PiecewiseLinearSpectrum::fromInterleaved(CIE_Illum_F11, true)}, + {"stdillum-F12", PiecewiseLinearSpectrum::fromInterleaved(CIE_Illum_F12, true)}, + + {"illum-acesD60", PiecewiseLinearSpectrum::fromInterleaved(ACES_Illum_D60, true)}, + + {"canon_eos_100d_r", PiecewiseLinearSpectrum::fromInterleaved(canon_eos_100d_r, false)}, + {"canon_eos_100d_g", PiecewiseLinearSpectrum::fromInterleaved(canon_eos_100d_g, false)}, + {"canon_eos_100d_b", PiecewiseLinearSpectrum::fromInterleaved(canon_eos_100d_b, false)}, + + {"canon_eos_1dx_mkii_r", PiecewiseLinearSpectrum::fromInterleaved(canon_eos_1dx_mkii_r, false)}, + {"canon_eos_1dx_mkii_g", PiecewiseLinearSpectrum::fromInterleaved(canon_eos_1dx_mkii_g, false)}, + {"canon_eos_1dx_mkii_b", PiecewiseLinearSpectrum::fromInterleaved(canon_eos_1dx_mkii_b, false)}, + + {"canon_eos_200d_r", PiecewiseLinearSpectrum::fromInterleaved(canon_eos_200d_r, false)}, + {"canon_eos_200d_g", PiecewiseLinearSpectrum::fromInterleaved(canon_eos_200d_g, false)}, + {"canon_eos_200d_b", PiecewiseLinearSpectrum::fromInterleaved(canon_eos_200d_b, false)}, + + {"canon_eos_200d_mkii_r", PiecewiseLinearSpectrum::fromInterleaved(canon_eos_200d_mkii_r, false)}, + {"canon_eos_200d_mkii_g", PiecewiseLinearSpectrum::fromInterleaved(canon_eos_200d_mkii_g, false)}, + {"canon_eos_200d_mkii_b", PiecewiseLinearSpectrum::fromInterleaved(canon_eos_200d_mkii_b, false)}, + + {"canon_eos_5d_r", PiecewiseLinearSpectrum::fromInterleaved(canon_eos_5d_r, false)}, + {"canon_eos_5d_g", PiecewiseLinearSpectrum::fromInterleaved(canon_eos_5d_g, false)}, + {"canon_eos_5d_b", PiecewiseLinearSpectrum::fromInterleaved(canon_eos_5d_b, false)}, + + {"canon_eos_5d_mkii_r", PiecewiseLinearSpectrum::fromInterleaved(canon_eos_5d_mkii_r, false)}, + {"canon_eos_5d_mkii_g", PiecewiseLinearSpectrum::fromInterleaved(canon_eos_5d_mkii_g, false)}, + {"canon_eos_5d_mkii_b", PiecewiseLinearSpectrum::fromInterleaved(canon_eos_5d_mkii_b, false)}, + + {"canon_eos_5d_mkiii_r", PiecewiseLinearSpectrum::fromInterleaved(canon_eos_5d_mkiii_r, false)}, + {"canon_eos_5d_mkiii_g", PiecewiseLinearSpectrum::fromInterleaved(canon_eos_5d_mkiii_g, false)}, + {"canon_eos_5d_mkiii_b", PiecewiseLinearSpectrum::fromInterleaved(canon_eos_5d_mkiii_b, false)}, + + {"canon_eos_5d_mkiv_r", PiecewiseLinearSpectrum::fromInterleaved(canon_eos_5d_mkiv_r, false)}, + {"canon_eos_5d_mkiv_g", PiecewiseLinearSpectrum::fromInterleaved(canon_eos_5d_mkiv_g, false)}, + {"canon_eos_5d_mkiv_b", PiecewiseLinearSpectrum::fromInterleaved(canon_eos_5d_mkiv_b, false)}, + + {"canon_eos_5ds_r", PiecewiseLinearSpectrum::fromInterleaved(canon_eos_5ds_r, false)}, + {"canon_eos_5ds_g", PiecewiseLinearSpectrum::fromInterleaved(canon_eos_5ds_g, false)}, + {"canon_eos_5ds_b", PiecewiseLinearSpectrum::fromInterleaved(canon_eos_5ds_b, false)}, + + {"canon_eos_m_r", PiecewiseLinearSpectrum::fromInterleaved(canon_eos_m_r, false)}, + {"canon_eos_m_g", PiecewiseLinearSpectrum::fromInterleaved(canon_eos_m_g, false)}, + {"canon_eos_m_b", PiecewiseLinearSpectrum::fromInterleaved(canon_eos_m_b, false)}, + + {"hasselblad_l1d_20c_r", PiecewiseLinearSpectrum::fromInterleaved(hasselblad_l1d_20c_r, false)}, + {"hasselblad_l1d_20c_g", PiecewiseLinearSpectrum::fromInterleaved(hasselblad_l1d_20c_g, false)}, + {"hasselblad_l1d_20c_b", PiecewiseLinearSpectrum::fromInterleaved(hasselblad_l1d_20c_b, false)}, + + {"nikon_d810_r", PiecewiseLinearSpectrum::fromInterleaved(nikon_d810_r, false)}, + {"nikon_d810_g", PiecewiseLinearSpectrum::fromInterleaved(nikon_d810_g, false)}, + {"nikon_d810_b", PiecewiseLinearSpectrum::fromInterleaved(nikon_d810_b, false)}, + + {"nikon_d850_r", PiecewiseLinearSpectrum::fromInterleaved(nikon_d850_r, false)}, + {"nikon_d850_g", PiecewiseLinearSpectrum::fromInterleaved(nikon_d850_g, false)}, + {"nikon_d850_b", PiecewiseLinearSpectrum::fromInterleaved(nikon_d850_b, false)}, + + {"sony_ilce_6400_r", PiecewiseLinearSpectrum::fromInterleaved(sony_ilce_6400_r, false)}, + {"sony_ilce_6400_g", PiecewiseLinearSpectrum::fromInterleaved(sony_ilce_6400_g, false)}, + {"sony_ilce_6400_b", PiecewiseLinearSpectrum::fromInterleaved(sony_ilce_6400_b, false)}, + + {"sony_ilce_7m3_r", PiecewiseLinearSpectrum::fromInterleaved(sony_ilce_7m3_r, false)}, + {"sony_ilce_7m3_g", PiecewiseLinearSpectrum::fromInterleaved(sony_ilce_7m3_g, false)}, + {"sony_ilce_7m3_b", PiecewiseLinearSpectrum::fromInterleaved(sony_ilce_7m3_b, false)}, + + {"sony_ilce_7rm3_r", PiecewiseLinearSpectrum::fromInterleaved(sony_ilce_7rm3_r, false)}, + {"sony_ilce_7rm3_g", PiecewiseLinearSpectrum::fromInterleaved(sony_ilce_7rm3_g, false)}, + {"sony_ilce_7rm3_b", PiecewiseLinearSpectrum::fromInterleaved(sony_ilce_7rm3_b, false)}, + + {"sony_ilce_9_r", PiecewiseLinearSpectrum::fromInterleaved(sony_ilce_9_r, false)}, + {"sony_ilce_9_g", PiecewiseLinearSpectrum::fromInterleaved(sony_ilce_9_g, false)}, + {"sony_ilce_9_b", PiecewiseLinearSpectrum::fromInterleaved(sony_ilce_9_b, false)}}; +} + +const PiecewiseLinearSpectrum* Spectra::getNamedSpectrum(const std::string& name) +{ + auto it = kNamedSpectra.find(name); + if (it == kNamedSpectra.end()) + return nullptr; + return &it->second; } +} // namespace Falcor diff --git a/Source/Falcor/Utils/Color/Spectrum.h b/Source/Falcor/Utils/Color/Spectrum.h index 038cd92ba..a19b687f8 100644 --- a/Source/Falcor/Utils/Color/Spectrum.h +++ b/Source/Falcor/Utils/Color/Spectrum.h @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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,248 +38,251 @@ namespace Falcor { - /** Represents a piecewise linearly interpolated spectrum. - Stores wavelengths (in increasing order) and a value for each wavelength. - */ - class FALCOR_API PiecewiseLinearSpectrum - { - public: - /** Create a spectrum. - \param[in] wavelengths Wavelengths in nm. - \param[in] values Values. - */ - PiecewiseLinearSpectrum(fstd::span wavelengths, fstd::span values); +/** + * Represents a piecewise linearly interpolated spectrum. + * Stores wavelengths (in increasing order) and a value for each wavelength. + */ +class FALCOR_API PiecewiseLinearSpectrum +{ +public: + /** + * Create a spectrum. + * @param[in] wavelengths Wavelengths in nm. + * @param[in] values Values. + */ + PiecewiseLinearSpectrum(fstd::span wavelengths, fstd::span values); - /** Create a spectrum from interleaved data: - [wavelength_0, value_0, wavelength_1, value_1, .. wavelength_N-1, value_N-1] - \param[in] interleaved Interleaved data (needs to contain 2*N entries). - \param[in] normalize Normalize spectrum to have luminance of 1. - \return The spectrum. - */ - static PiecewiseLinearSpectrum fromInterleaved(fstd::span interleaved, bool normalize); + /** + * Create a spectrum from interleaved data: + * [wavelength_0, value_0, wavelength_1, value_1, .. wavelength_N-1, value_N-1] + * @param[in] interleaved Interleaved data (needs to contain 2*N entries). + * @param[in] normalize Normalize spectrum to have luminance of 1. + * @return The spectrum. + */ + static PiecewiseLinearSpectrum fromInterleaved(fstd::span interleaved, bool normalize); - /** Create a spectrum from a text file that contains interleaved data: - [wavelength_0, value_0, wavelength_1, value_1, .. wavelength_N-1, value_N-1] - \param[in] path File path. - \return The spectrum. - */ - static std::optional fromFile(const std::filesystem::path& path); + /** + * Create a spectrum from a text file that contains interleaved data: + * [wavelength_0, value_0, wavelength_1, value_1, .. wavelength_N-1, value_N-1] + * @param[in] path File path. + * @return The spectrum. + */ + static std::optional fromFile(const std::filesystem::path& path); - /** Scale all values of the spectrum by a constant. - \param[in] factor Scaling factor. - */ - void scale(float factor); + /** + * Scale all values of the spectrum by a constant. + * @param[in] factor Scaling factor. + */ + void scale(float factor); - /** Evaluate the spectrum at the given wavelength. - Note: Returns zero for wavelengths outside the defined range. - \param wavelength Wavelength in nm. - \return Interpolated value. - */ - float eval(float wavelength) const + /** + * Evaluate the spectrum at the given wavelength. + * Note: Returns zero for wavelengths outside the defined range. + * @param wavelength Wavelength in nm. + * @return Interpolated value. + */ + float eval(float wavelength) const + { + if (mWavelengths.empty() || wavelength < mWavelengths.front() || wavelength > mWavelengths.back()) { - if (mWavelengths.empty() || wavelength < mWavelengths.front() || wavelength > mWavelengths.back()) - { - return 0.f; - } - - auto it = std::lower_bound(mWavelengths.begin(), mWavelengths.end(), wavelength); - if (it == mWavelengths.begin()) - { - return mValues.front(); - } - - size_t index = std::distance(mWavelengths.begin(), it) - 1; - float t = (wavelength - mWavelengths[index]) / (mWavelengths[index + 1] - mWavelengths[index]); - float a = mValues[index]; - float b = mValues[index + 1]; - return lerp(a, b, t); + return 0.f; } - /** Return the wavelength range. - \return The wavelength range of the spectrum. - */ - float2 getWavelengthRange() const + auto it = std::lower_bound(mWavelengths.begin(), mWavelengths.end(), wavelength); + if (it == mWavelengths.begin()) { - return { mWavelengths.front(), mWavelengths.back() }; + return mValues.front(); } - /** Get the maximum value in the spectrum. - \return The maximum value. - */ - float getMaxValue() const - { - return mMaxValue; - } + size_t index = std::distance(mWavelengths.begin(), it) - 1; + float t = (wavelength - mWavelengths[index]) / (mWavelengths[index + 1] - mWavelengths[index]); + float a = mValues[index]; + float b = mValues[index + 1]; + return math::lerp(a, b, t); + } - private: - std::vector mWavelengths; ///< Wavelengths in nm. - std::vector mValues; ///< Values at each wavelength. - float mMaxValue; ///< Maximum value in mValues. - }; + /** + * Return the wavelength range. + * @return The wavelength range of the spectrum. + */ + float2 getWavelengthRange() const { return {mWavelengths.front(), mWavelengths.back()}; } - /** Represents a denseley sampled spectrum. - */ - class FALCOR_API DenseleySampledSpectrum - { - public: - DenseleySampledSpectrum(float minWavelength, float maxWavelength, fstd::span values) - : mMinWavelength(minWavelength) - , mMaxWavelength(maxWavelength) - , mWavelengthStep((maxWavelength - minWavelength) / (values.size() - 1)) - , mValues(values.begin(), values.end()) - , mMaxValue(*std::max_element(values.begin(), values.end())) - {} + /** + * Get the maximum value in the spectrum. + * @return The maximum value. + */ + float getMaxValue() const { return mMaxValue; } - template - DenseleySampledSpectrum(const S& spectrum, float wavelengthStep = 1.f) - { - auto range = spectrum.getWavelengthRange(); - size_t count = (size_t)std::ceil((range.y - range.x) / wavelengthStep); - mMinWavelength = range.x; - mMaxWavelength = range.y; - // max(1, count - 1) handles edge case where wavelengthStep > wavelength range. - mWavelengthStep = (mMaxWavelength - mMinWavelength) / std::max(1ul, count - 1); - mValues.resize(count); - for (size_t i = 0; i < count; ++i) - { - mValues[i] = spectrum.eval(mMinWavelength + i * mWavelengthStep); - } - mMaxValue = *std::max_element(mValues.begin(), mValues.end()); - } +private: + std::vector mWavelengths; ///< Wavelengths in nm. + std::vector mValues; ///< Values at each wavelength. + float mMaxValue; ///< Maximum value in mValues. +}; - /** Evaluate the spectrum at the given wavelength. - Note: Returns zero for wavelengths outside the defined range. - \param wavelength Wavelength in nm. - \return Interpolated value. - */ - float eval(float wavelength) const - { - int index = std::lroundf((wavelength - mMinWavelength) / mWavelengthStep); - if (index < 0 || index >= (int)mValues.size()) return 0.f; - return mValues[index]; - } +/** + * Represents a denseley sampled spectrum. + */ +class FALCOR_API DenseleySampledSpectrum +{ +public: + DenseleySampledSpectrum(float minWavelength, float maxWavelength, fstd::span values) + : mMinWavelength(minWavelength) + , mMaxWavelength(maxWavelength) + , mWavelengthStep((maxWavelength - minWavelength) / (values.size() - 1)) + , mValues(values.begin(), values.end()) + , mMaxValue(*std::max_element(values.begin(), values.end())) + {} - /** Return the wavelength range. - \return The wavelength range of the spectrum. - */ - float2 getWavelengthRange() const + template + DenseleySampledSpectrum(const S& spectrum, float wavelengthStep = 1.f) + { + auto range = spectrum.getWavelengthRange(); + size_t count = (size_t)std::ceil((range.y - range.x) / wavelengthStep); + mMinWavelength = range.x; + mMaxWavelength = range.y; + // max(1, count - 1) handles edge case where wavelengthStep > wavelength range. + mWavelengthStep = (mMaxWavelength - mMinWavelength) / std::max(1ul, count - 1); + mValues.resize(count); + for (size_t i = 0; i < count; ++i) { - return { mMinWavelength, mMaxWavelength }; + mValues[i] = spectrum.eval(mMinWavelength + i * mWavelengthStep); } + mMaxValue = *std::max_element(mValues.begin(), mValues.end()); + } - /** Get the maximum value in the spectrum. - \return The maximum value. - */ - float getMaxValue() const - { - return mMaxValue; - } + /** + * Evaluate the spectrum at the given wavelength. + * Note: Returns zero for wavelengths outside the defined range. + * @param wavelength Wavelength in nm. + * @return Interpolated value. + */ + float eval(float wavelength) const + { + int index = std::lroundf((wavelength - mMinWavelength) / mWavelengthStep); + if (index < 0 || index >= (int)mValues.size()) + return 0.f; + return mValues[index]; + } - private: - float mMinWavelength; - float mMaxWavelength; - float mWavelengthStep; - std::vector mValues; - float mMaxValue; - }; + /** + * Return the wavelength range. + * @return The wavelength range of the spectrum. + */ + float2 getWavelengthRange() const { return {mMinWavelength, mMaxWavelength}; } - /** Compute blackbody emission. - \param[in] wavelength Wavelength in nm. - \param[in] temperature Temperature in K. - \return The emitted radiance. - */ - FALCOR_API float blackbodyEmission(float wavelength, float temperature); + /** + * Get the maximum value in the spectrum. + * @return The maximum value. + */ + float getMaxValue() const { return mMaxValue; } - /** Represents a blackbody emission spectrum. - */ - class FALCOR_API BlackbodySpectrum - { - public: - /** Create blackbody emission spectrum. - \param[in] temperature Temperature in K. - \param[in] normalize Normalize spectrum such that the peak value is 1. - */ - BlackbodySpectrum(float temperature, bool normalize = true); +private: + float mMinWavelength; + float mMaxWavelength; + float mWavelengthStep; + std::vector mValues; + float mMaxValue; +}; - /** Evaluate the spectrum at the given wavelength. - \param wavelength Wavelength in nm. - \return Value. - */ - float eval(float wavelength) const - { - return blackbodyEmission(wavelength, mTemperature) * mNormalizationFactor; - } +/** + * Compute blackbody emission. + * @param[in] wavelength Wavelength in nm. + * @param[in] temperature Temperature in K. + * @return The emitted radiance. + */ +FALCOR_API float blackbodyEmission(float wavelength, float temperature); - /** Return the wavelength range. - \return The wavelength range of the spectrum. - */ - float2 getWavelengthRange() const - { - return { -std::numeric_limits::infinity(), std::numeric_limits::infinity() }; - } +/** + * Represents a blackbody emission spectrum. + */ +class FALCOR_API BlackbodySpectrum +{ +public: + /** + * Create blackbody emission spectrum. + * @param[in] temperature Temperature in K. + * @param[in] normalize Normalize spectrum such that the peak value is 1. + */ + BlackbodySpectrum(float temperature, bool normalize = true); - /** Get the maximum value in the spectrum. - \return The maximum value. - */ - float getMaxValue() const { return mMaxValue; } + /** + * Evaluate the spectrum at the given wavelength. + * @param wavelength Wavelength in nm. + * @return Value. + */ + float eval(float wavelength) const { return blackbodyEmission(wavelength, mTemperature) * mNormalizationFactor; } - private: - float mTemperature; ///< Temperature in K. - float mNormalizationFactor; - float mMaxValue; - }; + /** + * Return the wavelength range. + * @return The wavelength range of the spectrum. + */ + float2 getWavelengthRange() const { return {-std::numeric_limits::infinity(), std::numeric_limits::infinity()}; } - /** Collection of useful spectra. - */ - struct FALCOR_API Spectra - { - // CIE 1931 - static const DenseleySampledSpectrum kCIE_X; - static const DenseleySampledSpectrum kCIE_Y; - static const DenseleySampledSpectrum kCIE_Z; - static constexpr float kCIE_Y_Integral = 106.856895f; + /** + * Get the maximum value in the spectrum. + * @return The maximum value. + */ + float getMaxValue() const { return mMaxValue; } - /** Get a named spectrum. - \param[in] name Spectrum name. - \return The spectrum or nullptr if not found. - */ - static const PiecewiseLinearSpectrum* getNamedSpectrum(const std::string& name); - }; +private: + float mTemperature; ///< Temperature in K. + float mNormalizationFactor; + float mMaxValue; +}; - /** Compute the inner product of two spectra. - */ - template - float innerProduct(const A& a, const B& b) - { - auto rangeA = a.getWavelengthRange(); - auto rangeB = b.getWavelengthRange(); - float minWavelength = std::max(rangeA.x, rangeB.x); - float maxWavelength = std::min(rangeA.y, rangeB.y); - float integral = 0.f; - for (float wavelength = minWavelength; wavelength <= maxWavelength; wavelength += 1.f) - { - integral += a.eval(wavelength) * b.eval(wavelength); - } - return integral; - } +/** + * Collection of useful spectra. + */ +struct FALCOR_API Spectra +{ + // CIE 1931 + static const DenseleySampledSpectrum kCIE_X; + static const DenseleySampledSpectrum kCIE_Y; + static const DenseleySampledSpectrum kCIE_Z; + static constexpr float kCIE_Y_Integral = 106.856895f; - /** Convert spectrum to CIE 1931 XYZ. - */ - template - float3 spectrumToXYZ(const S& s) - { - return float3( - innerProduct(s, Spectra::kCIE_X), - innerProduct(s, Spectra::kCIE_Y), - innerProduct(s, Spectra::kCIE_Z) - ) / Spectra::kCIE_Y_Integral; - } + /** + * Get a named spectrum. + * @param[in] name Spectrum name. + * @return The spectrum or nullptr if not found. + */ + static const PiecewiseLinearSpectrum* getNamedSpectrum(const std::string& name); +}; - /** Convert spectrum to RGB in Rec.709. - */ - template - float3 spectrumToRGB(const S& s) +/** + * Compute the inner product of two spectra. + */ +template +float innerProduct(const A& a, const B& b) +{ + auto rangeA = a.getWavelengthRange(); + auto rangeB = b.getWavelengthRange(); + float minWavelength = std::max(rangeA.x, rangeB.x); + float maxWavelength = std::min(rangeA.y, rangeB.y); + float integral = 0.f; + for (float wavelength = minWavelength; wavelength <= maxWavelength; wavelength += 1.f) { - return XYZtoRGB_Rec709(spectrumToXYZ(s)); + integral += a.eval(wavelength) * b.eval(wavelength); } + return integral; +} + +/** + * Convert spectrum to CIE 1931 XYZ. + */ +template +float3 spectrumToXYZ(const S& s) +{ + return float3(innerProduct(s, Spectra::kCIE_X), innerProduct(s, Spectra::kCIE_Y), innerProduct(s, Spectra::kCIE_Z)) / + Spectra::kCIE_Y_Integral; +} + +/** + * Convert spectrum to RGB in Rec.709. + */ +template +float3 spectrumToRGB(const S& s) +{ + return XYZtoRGB_Rec709(spectrumToXYZ(s)); } +} // namespace Falcor diff --git a/Source/Falcor/Utils/Color/SpectrumUtils.cpp b/Source/Falcor/Utils/Color/SpectrumUtils.cpp index 4dedb15cf..428b0cfe1 100644 --- a/Source/Falcor/Utils/Color/SpectrumUtils.cpp +++ b/Source/Falcor/Utils/Color/SpectrumUtils.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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,23 +33,25 @@ namespace Falcor { - // Initialize static data. - const SampledSpectrum SpectrumUtils::sCIE_XYZ_1931_1nm(360.0f, 830.0f, 471, reinterpret_cast(xyz1931_1nm)); // 1 nm between samples. - const SampledSpectrum SpectrumUtils::sD65_5nm(300.0f, 830.0f, 107, reinterpret_cast(D65_1nm)); // 5 nm between samples. +// Initialize static data. +// clang-format off +const SampledSpectrum SpectrumUtils::sCIE_XYZ_1931_1nm(360.0f, 830.0f, 471, reinterpret_cast(xyz1931_1nm)); // 1 nm between samples. +const SampledSpectrum SpectrumUtils::sD65_5nm(300.0f, 830.0f, 107, reinterpret_cast(D65_1nm)); // 5 nm between samples. +// clang-format on - float3 SpectrumUtils::wavelengthToXYZ_CIE1931(float lambda) - { - return sCIE_XYZ_1931_1nm.eval(lambda); - } +float3 SpectrumUtils::wavelengthToXYZ_CIE1931(float lambda) +{ + return sCIE_XYZ_1931_1nm.eval(lambda); +} - float SpectrumUtils::wavelengthToD65(float lambda) - { - return sD65_5nm.eval(lambda); - } +float SpectrumUtils::wavelengthToD65(float lambda) +{ + return sD65_5nm.eval(lambda); +} - float3 SpectrumUtils::wavelengthToRGB_Rec709(const float lambda) - { - float3 XYZ = wavelengthToXYZ_CIE1931(lambda); - return XYZtoRGB_Rec709(XYZ); - } +float3 SpectrumUtils::wavelengthToRGB_Rec709(const float lambda) +{ + float3 XYZ = wavelengthToXYZ_CIE1931(lambda); + return XYZtoRGB_Rec709(XYZ); } +} // namespace Falcor diff --git a/Source/Falcor/Utils/Color/SpectrumUtils.h b/Source/Falcor/Utils/Color/SpectrumUtils.h index a516908c2..f852ebc87 100644 --- a/Source/Falcor/Utils/Color/SpectrumUtils.h +++ b/Source/Falcor/Utils/Color/SpectrumUtils.h @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -37,116 +37,149 @@ namespace Falcor { - template class SampledSpectrum; +template +class SampledSpectrum; - class FALCOR_API SpectrumUtils - { - public: - static const SampledSpectrum sCIE_XYZ_1931_1nm; - static const SampledSpectrum sD65_5nm; +class FALCOR_API SpectrumUtils +{ +public: + static const SampledSpectrum sCIE_XYZ_1931_1nm; + static const SampledSpectrum sD65_5nm; - /** Evaluates the 1931 CIE XYZ color matching curves. - This function uses curves sampled at 1nm and returns XYZ values linearly interpolated from the two nearest samples. - \param[in] lambda Wavelength in nm. - \return XYZ tristimulus values. - */ - static float3 wavelengthToXYZ_CIE1931(float lambda); + /** + * Evaluates the 1931 CIE XYZ color matching curves. + * This function uses curves sampled at 1nm and returns XYZ values linearly interpolated from the two nearest samples. + * @param[in] lambda Wavelength in nm. + * @return XYZ tristimulus values. + */ + static float3 wavelengthToXYZ_CIE1931(float lambda); - /** Evaluates D65 standard illuminant. - This function uses curves sampled at 5nm and returns the value linearly interpolated from the two nearest samples. - \param[in] lambda Wavelength in nm. - \return D65 value. - */ - static float wavelengthToD65(float lambda); + /** + * Evaluates D65 standard illuminant. + * This function uses curves sampled at 5nm and returns the value linearly interpolated from the two nearest samples. + * @param[in] lambda Wavelength in nm. + * @return D65 value. + */ + static float wavelengthToD65(float lambda); - /** Converts from wavelength to XYZ_CIE1931 and then to RGB Rec709. - \param[in] lambda Wavelength in nm. - \return RGB color. - */ - static float3 wavelengthToRGB_Rec709(const float lambda); + /** + * Converts from wavelength to XYZ_CIE1931 and then to RGB Rec709. + * @param[in] lambda Wavelength in nm. + * @return RGB color. + */ + static float3 wavelengthToRGB_Rec709(const float lambda); - /** Integrate over entire spectrum and apply user-supplied function to each integration. - \param[in] spectrum The spectrum to be converted. - \param[in] interpolationType Which type of interpolation that should be used. - \param[in] func A "ReturnType func(float wavelength)"-function that is applied in each integration step. - \param[in] componentIndex Which component to evaluate when T is a vector type. - \param[in] integrationSteps Number of integration steps per sample. - \return XYZ of the spectrum. - */ - template - static ReturnType integrate(SampledSpectrum& spectrum, const SpectrumInterpolation interpolationType, std::function func, const uint32_t componentIndex = 0, const uint32_t integrationSteps = 1) - { - FALCOR_ASSERT(integrationSteps >= 1); - float2 wavelengthRange = spectrum.getWavelengthRange(); - uint32_t numEvaluations = uint32_t(spectrum.size() + (integrationSteps - 1) * (spectrum.size() - 1)); - float waveLengthDelta = (wavelengthRange.y - wavelengthRange.x) / (numEvaluations - 1.0f); - ReturnType sum = ReturnType(0); + /** + * Integrate over entire spectrum and apply user-supplied function to each integration. + * @param[in] spectrum The spectrum to be converted. + * @param[in] interpolationType Which type of interpolation that should be used. + * @param[in] func A "ReturnType func(float wavelength)"-function that is applied in each integration step. + * @param[in] componentIndex Which component to evaluate when T is a vector type. + * @param[in] integrationSteps Number of integration steps per sample. + * @return XYZ of the spectrum. + */ + template + static ReturnType integrate( + SampledSpectrum& spectrum, + const SpectrumInterpolation interpolationType, + std::function func, + const uint32_t componentIndex = 0, + const uint32_t integrationSteps = 1 + ) + { + FALCOR_ASSERT(integrationSteps >= 1); + float2 wavelengthRange = spectrum.getWavelengthRange(); + uint32_t numEvaluations = uint32_t(spectrum.size() + (integrationSteps - 1) * (spectrum.size() - 1)); + float waveLengthDelta = (wavelengthRange.y - wavelengthRange.x) / (numEvaluations - 1.0f); + ReturnType sum = ReturnType(0); - // Riemann sum = integral approximation. - for (uint32_t q = 0; q < numEvaluations; q++) + // Riemann sum = integral approximation. + for (uint32_t q = 0; q < numEvaluations; q++) + { + float wavelength = std::min(wavelengthRange.x + waveLengthDelta * q, wavelengthRange.y); + T spectralIntensity = spectrum.eval(wavelength, interpolationType); + float s; + if constexpr (std::is_same_v) + { + s = spectralIntensity; + } + else { - float wavelength = std::min(wavelengthRange.x + waveLengthDelta * q, wavelengthRange.y); - T spectralIntensity = spectrum.eval(wavelength, interpolationType); - float s; - if constexpr (std::is_same_v) - { - s = spectralIntensity; - } - else - { - s = spectralIntensity[componentIndex]; - } - sum += func(wavelength) * s * waveLengthDelta * ((q == 0 || q == numEvaluations - 1) ? 0.5f : 1.0f); + s = spectralIntensity[componentIndex]; } - return sum; + sum += func(wavelength) * s * waveLengthDelta * ((q == 0 || q == numEvaluations - 1) ? 0.5f : 1.0f); } + return sum; + } - /** Convert entire spectrum to XYZ. - \param[in] spectrum The spectrum to be converted. - \param[in] interpolationType Which type of interpolation that should be used. - \param[in] componentIndex Which component to evaluate when T is a vector type. - \param[in] integrationSteps Number of integration steps per sample. - \return XYZ of the spectrum. - */ - template - static float3 toXYZ(SampledSpectrum& spectrum, const SpectrumInterpolation interpolationType = SpectrumInterpolation::Linear, const uint32_t componentIndex = 0, const uint32_t integrationSteps = 1) - { - return integrate(spectrum, interpolationType, - [](float wavelength) -> float3 { return SpectrumUtils::wavelengthToXYZ_CIE1931(wavelength); }, - componentIndex, integrationSteps); - } + /** + * Convert entire spectrum to XYZ. + * @param[in] spectrum The spectrum to be converted. + * @param[in] interpolationType Which type of interpolation that should be used. + * @param[in] componentIndex Which component to evaluate when T is a vector type. + * @param[in] integrationSteps Number of integration steps per sample. + * @return XYZ of the spectrum. + */ + template + static float3 toXYZ( + SampledSpectrum& spectrum, + const SpectrumInterpolation interpolationType = SpectrumInterpolation::Linear, + const uint32_t componentIndex = 0, + const uint32_t integrationSteps = 1 + ) + { + return integrate( + spectrum, interpolationType, [](float wavelength) -> float3 { return SpectrumUtils::wavelengthToXYZ_CIE1931(wavelength); }, + componentIndex, integrationSteps + ); + } - /** Convert entire spectrum to XYZ times D65. - \param[in] spectrum The spectrum to be converted. - \param[in] interpolationType Which type of interpolation that should be used. - \param[in] componentIndex Which component to evaluate when T is a vector type.. - \param[in] integrationSteps Number of integration steps per sample. - \return XYZ of the spectrum times D65. - */ - template - static float3 toXYZ_D65(SampledSpectrum& spectrum, const SpectrumInterpolation interpolationType = SpectrumInterpolation::Linear, const uint32_t componentIndex = 0, const uint32_t integrationSteps = 1) - { - return integrate(spectrum, interpolationType, - [](float wavelength) -> float3 { return SpectrumUtils::wavelengthToXYZ_CIE1931(wavelength) * SpectrumUtils::wavelengthToD65(wavelength); }, - componentIndex, integrationSteps); - } + /** + * Convert entire spectrum to XYZ times D65. + * @param[in] spectrum The spectrum to be converted. + * @param[in] interpolationType Which type of interpolation that should be used. + * @param[in] componentIndex Which component to evaluate when T is a vector type.. + * @param[in] integrationSteps Number of integration steps per sample. + * @return XYZ of the spectrum times D65. + */ + template + static float3 toXYZ_D65( + SampledSpectrum& spectrum, + const SpectrumInterpolation interpolationType = SpectrumInterpolation::Linear, + const uint32_t componentIndex = 0, + const uint32_t integrationSteps = 1 + ) + { + return integrate( + spectrum, interpolationType, + [](float wavelength) -> float3 + { return SpectrumUtils::wavelengthToXYZ_CIE1931(wavelength) * SpectrumUtils::wavelengthToD65(wavelength); }, + componentIndex, integrationSteps + ); + } - /** Convert entire spectrum to RGB under the assumption of using the D65 illuminant. - \param[in] spectrum The spectrum to be converted. - \param[in] interpolationType Which type of interpolation that should be used. - \param[in] componentIndex Which component to evaluate when T is a vector type. - \param[in] integrationSteps Number of integration steps per sample. - \return An RGB color. - */ - template - static float3 toRGB_D65(SampledSpectrum& spectrum, const SpectrumInterpolation interpolationType, const uint32_t componentIndex = 0, const uint32_t integrationSteps = 1) - { - // Equation 8 from "An OpenEXR Layout for Spectral Images", JCGT. - // https://jcgt.org/published/0010/03/01/ - float3 XYZ = toXYZ_D65(spectrum, interpolationType, componentIndex, integrationSteps); - float3 RGB = XYZtoRGB_Rec709(XYZ); - const float Y_D65 = 10567.0762f; // Computed as Y_D65 = SpectrumUtils::sD65_5nm.toXYZ(1.0f).y; See Equation 8 in the paper above. - return RGB * (1.0f / Y_D65); - } - }; -} + /** + * Convert entire spectrum to RGB under the assumption of using the D65 illuminant. + * @param[in] spectrum The spectrum to be converted. + * @param[in] interpolationType Which type of interpolation that should be used. + * @param[in] componentIndex Which component to evaluate when T is a vector type. + * @param[in] integrationSteps Number of integration steps per sample. + * @return An RGB color. + */ + template + static float3 toRGB_D65( + SampledSpectrum& spectrum, + const SpectrumInterpolation interpolationType, + const uint32_t componentIndex = 0, + const uint32_t integrationSteps = 1 + ) + { + // Equation 8 from "An OpenEXR Layout for Spectral Images", JCGT. + // https://jcgt.org/published/0010/03/01/ + float3 XYZ = toXYZ_D65(spectrum, interpolationType, componentIndex, integrationSteps); + float3 RGB = XYZtoRGB_Rec709(XYZ); + const float Y_D65 = 10567.0762f; // Computed as Y_D65 = SpectrumUtils::sD65_5nm.toXYZ(1.0f).y; See Equation 8 in the paper above. + return RGB * (1.0f / Y_D65); + } +}; +} // namespace Falcor diff --git a/Source/Falcor/Utils/Color/SpectrumUtils.slang b/Source/Falcor/Utils/Color/SpectrumUtils.slang index 7392e876e..95c1ce9f9 100644 --- a/Source/Falcor/Utils/Color/SpectrumUtils.slang +++ b/Source/Falcor/Utils/Color/SpectrumUtils.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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,28 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **************************************************************************/ -/** Utilities for converting tristimulus values to/from spectral data. -*/ +/** + * Utilities for converting tristimulus values to/from spectral data. + */ struct SpectrumUtils { - /** Evaluates the 1931 CIE XYZ color matching curves. - This uses a multi-lobe piecewise Gaussian fit described in: - Wyman et al., "Simple Analytic Approximations to the CIE XYZ Color - Matching Functions", JCGT, vol 2(2), 2013. - \param[in] lambda Wavelength in nm. - \return XYZ tristimulus values. - */ + /** + * Evaluates the 1931 CIE XYZ color matching curves. + * This uses a multi-lobe piecewise Gaussian fit described in: + * Wyman et al., "Simple Analytic Approximations to the CIE XYZ Color + * Matching Functions", JCGT, vol 2(2), 2013. + * @param[in] lambda Wavelength in nm. + * @return XYZ tristimulus values. + */ static float3 wavelengthToXYZ(float lambda) { - float x = 0.362f * G(lambda, 442.0f, 0.0624f, 0.0374f) + 1.056f * G(lambda, 599.8f, 0.0264f, 0.0323f) - 0.065f * G(lambda, 501.1f, 0.0490f, 0.0382f); + float x = 0.362f * G(lambda, 442.0f, 0.0624f, 0.0374f) + 1.056f * G(lambda, 599.8f, 0.0264f, 0.0323f) - + 0.065f * G(lambda, 501.1f, 0.0490f, 0.0382f); float y = 0.821f * G(lambda, 568.8f, 0.0213f, 0.0247f) + 0.286f * G(lambda, 530.9f, 0.0613f, 0.0322f); float z = 1.217f * G(lambda, 437.0f, 0.0845f, 0.0278f) + 0.681f * G(lambda, 459.0f, 0.0385f, 0.0725f); return float3(x, y, z); } - // Internal static float G(float x, float lambda, float invSigma1, float invSigma2) diff --git a/Source/Falcor/Utils/CryptoUtils.cpp b/Source/Falcor/Utils/CryptoUtils.cpp index 3fb01b299..1769506dd 100644 --- a/Source/Falcor/Utils/CryptoUtils.cpp +++ b/Source/Falcor/Utils/CryptoUtils.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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,219 +31,218 @@ namespace Falcor { - SHA1::SHA1() : - mIndex(0), - mBits(0) +SHA1::SHA1() : mIndex(0), mBits(0) +{ + mState[0] = 0x67452301; + mState[1] = 0xefcdab89; + mState[2] = 0x98badcfe; + mState[3] = 0x10325476; + mState[4] = 0xc3d2e1f0; +} + +void SHA1::update(uint8_t byte) +{ + addByte(byte); + mBits += 8; +} + +void SHA1::update(const void* data, size_t len) +{ + if (!data) + return; + + const uint8_t* ptr = reinterpret_cast(data); + + // Fill up buffer if not full. + while (len > 0 && mIndex != 0) { - mState[0] = 0x67452301; - mState[1] = 0xefcdab89; - mState[2] = 0x98badcfe; - mState[3] = 0x10325476; - mState[4] = 0xc3d2e1f0; + update(*ptr++); + len--; } - void SHA1::update(uint8_t byte) + // Process full blocks. + while (len >= sizeof(mBuf)) { - addByte(byte); - mBits += 8; + processBlock(ptr); + ptr += sizeof(mBuf); + len -= sizeof(mBuf); + mBits += sizeof(mBuf) * 8; } - void SHA1::update(const void* data, size_t len) + // Process remaining bytes. + while (len > 0) { - if (!data) return; - - const uint8_t *ptr = reinterpret_cast(data); - - // Fill up buffer if not full. - while (len > 0 && mIndex != 0) - { - update(*ptr++); - len--; - } - - // Process full blocks. - while (len >= sizeof(mBuf)) - { - processBlock(ptr); - ptr += sizeof(mBuf); - len -= sizeof(mBuf); - mBits += sizeof(mBuf) * 8; - } + update(*ptr++); + len--; + } +} - // Process remaining bytes. - while (len > 0) - { - update(*ptr++); - len--; - } +SHA1::MD SHA1::finalize() +{ + // Finalize with 0x80, some zero padding and the length in bits. + addByte(0x80); + while (mIndex % 64 != 56) + { + addByte(0); + } + for (int i = 7; i >= 0; --i) + { + addByte(mBits >> i * 8); } - SHA1::MD SHA1::finalize() + MD md; + for (int i = 0; i < 5; i++) { - // Finalize with 0x80, some zero padding and the length in bits. - addByte(0x80); - while (mIndex % 64 != 56) - { - addByte(0); - } - for (int i = 7; i >= 0; --i) + for (int j = 3; j >= 0; j--) { - addByte(mBits >> i * 8); + md[i * 4 + j] = (mState[i] >> ((3 - j) * 8)) & 0xff; } + } - MD md; - for (int i = 0; i < 5; i++) - { - for (int j = 3; j >= 0; j--) - { - md[i * 4 + j] = (mState[i] >> ((3 - j) * 8)) & 0xff; - } - } + return md; +} - return md; - } +SHA1::MD SHA1::compute(const void* data, size_t len) +{ + SHA1 sha1; + sha1.update(data, len); + return sha1.finalize(); +} - SHA1::MD SHA1::compute(const void* data, size_t len) - { - SHA1 sha1; - sha1.update(data, len); - return sha1.finalize(); - } +std::string SHA1::toString(const SHA1::MD& sha1) +{ + std::stringstream ss; + ss << std::hex << std::setfill('0') << std::setw(2); + for (auto c : sha1) + ss << (int)c; + return ss.str(); +} - std::string SHA1::toString(const SHA1::MD& sha1) - { - std::stringstream ss; - ss << std::hex << std::setfill('0') << std::setw(2); - for (auto c : sha1) ss << (int)c; - return ss.str(); - } +void SHA1::addByte(uint8_t byte) +{ + mBuf[mIndex++] = byte; - void SHA1::addByte(uint8_t byte) + if (mIndex >= sizeof(mBuf)) { - mBuf[mIndex++] = byte; - - if (mIndex >= sizeof(mBuf)){ - mIndex = 0; - processBlock(mBuf); - } + mIndex = 0; + processBlock(mBuf); } +} - void SHA1::processBlock(const uint8_t* ptr) - { - auto rol32 = [](uint32_t x, uint32_t n) { - return (x << n) | (x >> (32 - n)); - }; +void SHA1::processBlock(const uint8_t* ptr) +{ + auto rol32 = [](uint32_t x, uint32_t n) { return (x << n) | (x >> (32 - n)); }; - auto makeWord = [](const uint8_t* p) - { - return ((uint32_t)p[0] << 24) | ((uint32_t)p[1] << 16) | ((uint32_t)p[2] << 8) | (uint32_t)p[3]; - }; + auto makeWord = [](const uint8_t* p) + { return ((uint32_t)p[0] << 24) | ((uint32_t)p[1] << 16) | ((uint32_t)p[2] << 8) | (uint32_t)p[3]; }; - const uint32_t c0 = 0x5a827999; - const uint32_t c1 = 0x6ed9eba1; - const uint32_t c2 = 0x8f1bbcdc; - const uint32_t c3 = 0xca62c1d6; + const uint32_t c0 = 0x5a827999; + const uint32_t c1 = 0x6ed9eba1; + const uint32_t c2 = 0x8f1bbcdc; + const uint32_t c3 = 0xca62c1d6; - uint32_t a = mState[0]; - uint32_t b = mState[1]; - uint32_t c = mState[2]; - uint32_t d = mState[3]; - uint32_t e = mState[4]; + uint32_t a = mState[0]; + uint32_t b = mState[1]; + uint32_t c = mState[2]; + uint32_t d = mState[3]; + uint32_t e = mState[4]; - uint32_t w[16]; + uint32_t w[16]; - for (size_t i = 0; i < 16; i++) - { - w[i] = makeWord(ptr + i * 4); - } + for (size_t i = 0; i < 16; i++) + { + w[i] = makeWord(ptr + i * 4); + } +// clang-format off #define SHA1_LOAD(i) w[i&15] = rol32(w[(i + 13) & 15] ^ w[(i + 8) & 15] ^ w[(i + 2) & 15] ^ w[i & 15], 1); #define SHA1_ROUND_0(v,u,x,y,z,i) z += ((u & (x ^ y)) ^ y) + w[i & 15] + c0 + rol32(v, 5); u = rol32(u, 30); #define SHA1_ROUND_1(v,u,x,y,z,i) SHA1_LOAD(i) z += ((u & (x ^ y)) ^ y) + w[i & 15] + c0 + rol32(v, 5); u = rol32(u, 30); #define SHA1_ROUND_2(v,u,x,y,z,i) SHA1_LOAD(i) z += (u ^ x ^ y) + w[i & 15] + c1 + rol32(v, 5); u = rol32(u, 30); #define SHA1_ROUND_3(v,u,x,y,z,i) SHA1_LOAD(i) z += (((u | x) & y) | (u & x)) + w[i & 15] + c2 + rol32(v, 5); u = rol32(u, 30); #define SHA1_ROUND_4(v,u,x,y,z,i) SHA1_LOAD(i) z += (u ^ x ^ y) + w[i & 15] + c3 + rol32(v, 5); u = rol32(u, 30); + // clang-format on - SHA1_ROUND_0(a, b, c, d, e, 0); - SHA1_ROUND_0(e, a, b, c, d, 1); - SHA1_ROUND_0(d, e, a, b, c, 2); - SHA1_ROUND_0(c, d, e, a, b, 3); - SHA1_ROUND_0(b, c, d, e, a, 4); - SHA1_ROUND_0(a, b, c, d, e, 5); - SHA1_ROUND_0(e, a, b, c, d, 6); - SHA1_ROUND_0(d, e, a, b, c, 7); - SHA1_ROUND_0(c, d, e, a, b, 8); - SHA1_ROUND_0(b, c, d, e, a, 9); - SHA1_ROUND_0(a, b, c, d, e, 10); - SHA1_ROUND_0(e, a, b, c, d, 11); - SHA1_ROUND_0(d, e, a, b, c, 12); - SHA1_ROUND_0(c, d, e, a, b, 13); - SHA1_ROUND_0(b, c, d, e, a, 14); - SHA1_ROUND_0(a, b, c, d, e, 15); - SHA1_ROUND_1(e, a, b, c, d, 16); - SHA1_ROUND_1(d, e, a, b, c, 17); - SHA1_ROUND_1(c, d, e, a, b, 18); - SHA1_ROUND_1(b, c, d, e, a, 19); - SHA1_ROUND_2(a, b, c, d, e, 20); - SHA1_ROUND_2(e, a, b, c, d, 21); - SHA1_ROUND_2(d, e, a, b, c, 22); - SHA1_ROUND_2(c, d, e, a, b, 23); - SHA1_ROUND_2(b, c, d, e, a, 24); - SHA1_ROUND_2(a, b, c, d, e, 25); - SHA1_ROUND_2(e, a, b, c, d, 26); - SHA1_ROUND_2(d, e, a, b, c, 27); - SHA1_ROUND_2(c, d, e, a, b, 28); - SHA1_ROUND_2(b, c, d, e, a, 29); - SHA1_ROUND_2(a, b, c, d, e, 30); - SHA1_ROUND_2(e, a, b, c, d, 31); - SHA1_ROUND_2(d, e, a, b, c, 32); - SHA1_ROUND_2(c, d, e, a, b, 33); - SHA1_ROUND_2(b, c, d, e, a, 34); - SHA1_ROUND_2(a, b, c, d, e, 35); - SHA1_ROUND_2(e, a, b, c, d, 36); - SHA1_ROUND_2(d, e, a, b, c, 37); - SHA1_ROUND_2(c, d, e, a, b, 38); - SHA1_ROUND_2(b, c, d, e, a, 39); - SHA1_ROUND_3(a, b, c, d, e, 40); - SHA1_ROUND_3(e, a, b, c, d, 41); - SHA1_ROUND_3(d, e, a, b, c, 42); - SHA1_ROUND_3(c, d, e, a, b, 43); - SHA1_ROUND_3(b, c, d, e, a, 44); - SHA1_ROUND_3(a, b, c, d, e, 45); - SHA1_ROUND_3(e, a, b, c, d, 46); - SHA1_ROUND_3(d, e, a, b, c, 47); - SHA1_ROUND_3(c, d, e, a, b, 48); - SHA1_ROUND_3(b, c, d, e, a, 49); - SHA1_ROUND_3(a, b, c, d, e, 50); - SHA1_ROUND_3(e, a, b, c, d, 51); - SHA1_ROUND_3(d, e, a, b, c, 52); - SHA1_ROUND_3(c, d, e, a, b, 53); - SHA1_ROUND_3(b, c, d, e, a, 54); - SHA1_ROUND_3(a, b, c, d, e, 55); - SHA1_ROUND_3(e, a, b, c, d, 56); - SHA1_ROUND_3(d, e, a, b, c, 57); - SHA1_ROUND_3(c, d, e, a, b, 58); - SHA1_ROUND_3(b, c, d, e, a, 59); - SHA1_ROUND_4(a, b, c, d, e, 60); - SHA1_ROUND_4(e, a, b, c, d, 61); - SHA1_ROUND_4(d, e, a, b, c, 62); - SHA1_ROUND_4(c, d, e, a, b, 63); - SHA1_ROUND_4(b, c, d, e, a, 64); - SHA1_ROUND_4(a, b, c, d, e, 65); - SHA1_ROUND_4(e, a, b, c, d, 66); - SHA1_ROUND_4(d, e, a, b, c, 67); - SHA1_ROUND_4(c, d, e, a, b, 68); - SHA1_ROUND_4(b, c, d, e, a, 69); - SHA1_ROUND_4(a, b, c, d, e, 70); - SHA1_ROUND_4(e, a, b, c, d, 71); - SHA1_ROUND_4(d, e, a, b, c, 72); - SHA1_ROUND_4(c, d, e, a, b, 73); - SHA1_ROUND_4(b, c, d, e, a, 74); - SHA1_ROUND_4(a, b, c, d, e, 75); - SHA1_ROUND_4(e, a, b, c, d, 76); - SHA1_ROUND_4(d, e, a, b, c, 77); - SHA1_ROUND_4(c, d, e, a, b, 78); - SHA1_ROUND_4(b, c, d, e, a, 79); + SHA1_ROUND_0(a, b, c, d, e, 0); + SHA1_ROUND_0(e, a, b, c, d, 1); + SHA1_ROUND_0(d, e, a, b, c, 2); + SHA1_ROUND_0(c, d, e, a, b, 3); + SHA1_ROUND_0(b, c, d, e, a, 4); + SHA1_ROUND_0(a, b, c, d, e, 5); + SHA1_ROUND_0(e, a, b, c, d, 6); + SHA1_ROUND_0(d, e, a, b, c, 7); + SHA1_ROUND_0(c, d, e, a, b, 8); + SHA1_ROUND_0(b, c, d, e, a, 9); + SHA1_ROUND_0(a, b, c, d, e, 10); + SHA1_ROUND_0(e, a, b, c, d, 11); + SHA1_ROUND_0(d, e, a, b, c, 12); + SHA1_ROUND_0(c, d, e, a, b, 13); + SHA1_ROUND_0(b, c, d, e, a, 14); + SHA1_ROUND_0(a, b, c, d, e, 15); + SHA1_ROUND_1(e, a, b, c, d, 16); + SHA1_ROUND_1(d, e, a, b, c, 17); + SHA1_ROUND_1(c, d, e, a, b, 18); + SHA1_ROUND_1(b, c, d, e, a, 19); + SHA1_ROUND_2(a, b, c, d, e, 20); + SHA1_ROUND_2(e, a, b, c, d, 21); + SHA1_ROUND_2(d, e, a, b, c, 22); + SHA1_ROUND_2(c, d, e, a, b, 23); + SHA1_ROUND_2(b, c, d, e, a, 24); + SHA1_ROUND_2(a, b, c, d, e, 25); + SHA1_ROUND_2(e, a, b, c, d, 26); + SHA1_ROUND_2(d, e, a, b, c, 27); + SHA1_ROUND_2(c, d, e, a, b, 28); + SHA1_ROUND_2(b, c, d, e, a, 29); + SHA1_ROUND_2(a, b, c, d, e, 30); + SHA1_ROUND_2(e, a, b, c, d, 31); + SHA1_ROUND_2(d, e, a, b, c, 32); + SHA1_ROUND_2(c, d, e, a, b, 33); + SHA1_ROUND_2(b, c, d, e, a, 34); + SHA1_ROUND_2(a, b, c, d, e, 35); + SHA1_ROUND_2(e, a, b, c, d, 36); + SHA1_ROUND_2(d, e, a, b, c, 37); + SHA1_ROUND_2(c, d, e, a, b, 38); + SHA1_ROUND_2(b, c, d, e, a, 39); + SHA1_ROUND_3(a, b, c, d, e, 40); + SHA1_ROUND_3(e, a, b, c, d, 41); + SHA1_ROUND_3(d, e, a, b, c, 42); + SHA1_ROUND_3(c, d, e, a, b, 43); + SHA1_ROUND_3(b, c, d, e, a, 44); + SHA1_ROUND_3(a, b, c, d, e, 45); + SHA1_ROUND_3(e, a, b, c, d, 46); + SHA1_ROUND_3(d, e, a, b, c, 47); + SHA1_ROUND_3(c, d, e, a, b, 48); + SHA1_ROUND_3(b, c, d, e, a, 49); + SHA1_ROUND_3(a, b, c, d, e, 50); + SHA1_ROUND_3(e, a, b, c, d, 51); + SHA1_ROUND_3(d, e, a, b, c, 52); + SHA1_ROUND_3(c, d, e, a, b, 53); + SHA1_ROUND_3(b, c, d, e, a, 54); + SHA1_ROUND_3(a, b, c, d, e, 55); + SHA1_ROUND_3(e, a, b, c, d, 56); + SHA1_ROUND_3(d, e, a, b, c, 57); + SHA1_ROUND_3(c, d, e, a, b, 58); + SHA1_ROUND_3(b, c, d, e, a, 59); + SHA1_ROUND_4(a, b, c, d, e, 60); + SHA1_ROUND_4(e, a, b, c, d, 61); + SHA1_ROUND_4(d, e, a, b, c, 62); + SHA1_ROUND_4(c, d, e, a, b, 63); + SHA1_ROUND_4(b, c, d, e, a, 64); + SHA1_ROUND_4(a, b, c, d, e, 65); + SHA1_ROUND_4(e, a, b, c, d, 66); + SHA1_ROUND_4(d, e, a, b, c, 67); + SHA1_ROUND_4(c, d, e, a, b, 68); + SHA1_ROUND_4(b, c, d, e, a, 69); + SHA1_ROUND_4(a, b, c, d, e, 70); + SHA1_ROUND_4(e, a, b, c, d, 71); + SHA1_ROUND_4(d, e, a, b, c, 72); + SHA1_ROUND_4(c, d, e, a, b, 73); + SHA1_ROUND_4(b, c, d, e, a, 74); + SHA1_ROUND_4(a, b, c, d, e, 75); + SHA1_ROUND_4(e, a, b, c, d, 76); + SHA1_ROUND_4(d, e, a, b, c, 77); + SHA1_ROUND_4(c, d, e, a, b, 78); + SHA1_ROUND_4(b, c, d, e, a, 79); #undef SHA1_LOAD #undef SHA1_ROUND_0 @@ -252,10 +251,10 @@ namespace Falcor #undef SHA1_ROUND_3 #undef SHA1_ROUND_4 - mState[0] += a; - mState[1] += b; - mState[2] += c; - mState[3] += d; - mState[4] += e; - } + mState[0] += a; + mState[1] += b; + mState[2] += c; + mState[3] += d; + mState[4] += e; } +} // namespace Falcor diff --git a/Source/Falcor/Utils/CryptoUtils.h b/Source/Falcor/Utils/CryptoUtils.h index 3a30aaaa1..a64ca1384 100644 --- a/Source/Falcor/Utils/CryptoUtils.h +++ b/Source/Falcor/Utils/CryptoUtils.h @@ -34,59 +34,70 @@ namespace Falcor { - /** Helper to compute SHA-1 hash. - */ - class FALCOR_API SHA1 - { - public: - using MD = std::array; ///< Message digest. +/** + * Helper to compute SHA-1 hash. + */ +class FALCOR_API SHA1 +{ +public: + using MD = std::array; ///< Message digest. - SHA1(); + SHA1(); - /** Update hash by adding one byte. - \param[in] value Value to hash. - */ - void update(uint8_t value); + /** + * Update hash by adding one byte. + * @param[in] value Value to hash. + */ + void update(uint8_t value); - /** Update hash by adding the given data. - \param[in] data Data to hash. - \param[in] len Length of data in bytes. - */ - void update(const void* data, size_t len); + /** + * Update hash by adding the given data. + * @param[in] data Data to hash. + * @param[in] len Length of data in bytes. + */ + void update(const void* data, size_t len); - /** Update hash by adding one value of fundamental type T. - \param[in] Value to hash. - */ - template::value, bool> = true> - void update(const T& value) { update(&value, sizeof(value)); } + /** + * Update hash by adding one value of fundamental type T. + * @param[in] Value to hash. + */ + template::value, bool> = true> + void update(const T& value) + { + update(&value, sizeof(value)); + } - /** Update hash by adding the given string view. - */ - void update(const std::string_view str) { update(str.data(), str.size()); } + /** + * Update hash by adding the given string view. + */ + void update(const std::string_view str) { update(str.data(), str.size()); } - /** Return final message digest. - \return Returns the SHA-1 message digest. - */ - MD finalize(); + /** + * Return final message digest. + * @return Returns the SHA-1 message digest. + */ + MD finalize(); - /** Compute SHA-1 hash over the given data. - \param[in] data Data to hash. - \param[in] len Length of data in bytes. - \return Returns the SHA-1 message digest. - */ - static MD compute(const void* data, size_t len); + /** + * Compute SHA-1 hash over the given data. + * @param[in] data Data to hash. + * @param[in] len Length of data in bytes. + * @return Returns the SHA-1 message digest. + */ + static MD compute(const void* data, size_t len); - /** Convert SHA-1 hash to 40-character string in hexadecimal notation. - */ - static std::string toString(const MD& sha1); + /** + * Convert SHA-1 hash to 40-character string in hexadecimal notation. + */ + static std::string toString(const MD& sha1); - private: - void addByte(uint8_t x); - void processBlock(const uint8_t* ptr); +private: + void addByte(uint8_t x); + void processBlock(const uint8_t* ptr); - uint32_t mIndex; - uint64_t mBits; - uint32_t mState[5]; - uint8_t mBuf[64]; - }; + uint32_t mIndex; + uint64_t mBits; + uint32_t mState[5]; + uint8_t mBuf[64]; }; +}; // namespace Falcor diff --git a/Source/Falcor/Utils/CudaUtils.cpp b/Source/Falcor/Utils/CudaUtils.cpp new file mode 100644 index 000000000..60092cfe1 --- /dev/null +++ b/Source/Falcor/Utils/CudaUtils.cpp @@ -0,0 +1,251 @@ +/*************************************************************************** + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. + # + # Redistribution and use in source and binary forms, with or without + # modification, are permitted provided that the following conditions + # are met: + # * Redistributions of source code must retain the above copyright + # notice, this list of conditions and the following disclaimer. + # * Redistributions in binary form must reproduce the above copyright + # notice, this list of conditions and the following disclaimer in the + # documentation and/or other materials provided with the distribution. + # * Neither the name of NVIDIA CORPORATION nor the names of its + # contributors may be used to endorse or promote products derived + # from this software without specific prior written permission. + # + # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY + # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + **************************************************************************/ +#include "CudaUtils.h" +#include "Core/Errors.h" +#include +#include +#include + +// Some debug macros +#define CUDA_CHECK(call) \ + { \ + cudaError_t rc = call; \ + if (rc != cudaSuccess) \ + { \ + std::stringstream txt; \ + cudaError_t err = rc; /*cudaGetLastError();*/ \ + txt << "CUDA Error " << cudaGetErrorName(err) << " (" << cudaGetErrorString(err) << ")"; \ + Falcor::reportFatalError(txt.str()); \ + } \ + } +#define CUDA_CHECK_NOEXCEPT(call) \ + { \ + call; \ + } + +#define CUDA_SYNC_CHECK() \ + { \ + cudaDeviceSynchronize(); \ + cudaError_t error = cudaGetLastError(); \ + if (error != cudaSuccess) \ + { \ + char buf[1024]; \ + sprintf(buf, "error (%s: line %d): %s\n", __FILE__, __LINE__, cudaGetErrorString(error)); \ + Falcor::reportFatalError(std::string(buf)); \ + } \ + } + +#define CU_CHECK_SUCCESS(x) \ + do \ + { \ + CUresult result = x; \ + if (result != CUDA_SUCCESS) \ + { \ + const char* msg; \ + cuGetErrorName(result, &msg); \ + Falcor::reportFatalError("CUDA Error: " #x " failed with error " + std::string(msg)); \ + } \ + } while (0) + +#define CUDA_CHECK_SUCCESS(x) \ + do \ + { \ + cudaError_t result = x; \ + if (result != cudaSuccess) \ + { \ + Falcor::reportFatalError("CUDA Error: " #x " failed with error " + std::string(cudaGetErrorString(result))); \ + return 0; \ + } \ + } while (0) + +namespace +{ +uint32_t gNodeMask; +CUdevice gCudaDevice; +CUcontext gCudaContext; +CUstream gCudaStream; +} // namespace + +unsigned int initCuda(void) +{ +#if 0 + cudaFree(0); + int32_t numDevices; + cudaGetDeviceCount(&numDevices); + return numDevices; +#endif + + 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::reportFatalError("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; +} + +void setCUDAContext() +{ + CU_CHECK_SUCCESS(cuCtxSetCurrent(gCudaContext)); +} + +void syncCudaDevice() +{ + cudaDeviceSynchronize(); + cudaError_t error = cudaGetLastError(); + if (error != cudaSuccess) + { + Falcor::reportFatalError("Failed to sync CUDA device"); + } +} + +void myCudaMemset(void* devPtr, int value, size_t count) +{ + CUDA_CHECK(cudaMemset(devPtr, value, count)); +} + +void CudaBuffer::allocate(size_t size) +{ + if (mpDevicePtr) + free(); + mSizeBytes = size; + CUDA_CHECK(cudaMalloc((void**)&mpDevicePtr, mSizeBytes)); +} + +void CudaBuffer::resize(size_t size) +{ + allocate(size); +} + +void CudaBuffer::free(void) +{ + CUDA_CHECK(cudaFree(mpDevicePtr)); + mpDevicePtr = nullptr; + mSizeBytes = 0; +} + +template +bool CudaBuffer::download(T* t, size_t count) +{ + if (!mpDevicePtr) + return false; + if (mSizeBytes <= (count * sizeof(T))) + return false; + + CUDA_CHECK(cudaMemcpy((void*)t, mpDevicePtr, count * sizeof(T), cudaMemcpyDeviceToHost)); + return true; // might be an error caught by CUDA_CHECK? TODO: process any such error through +} + +template +bool CudaBuffer::upload(const T* t, size_t count) +{ + if (!mpDevicePtr) + return false; + if (mSizeBytes <= (count * sizeof(T))) + return false; + + CUDA_CHECK(cudaMemcpy(mpDevicePtr, (void*)t, count * sizeof(T), cudaMemcpyHostToDevice)); + return true; // might be an error caught by CUDA_CHECK? TODO: process any such error through +} + +template +void CudaBuffer::allocAndUpload(const std::vector& vt) +{ + allocate(vt.size() * sizeof(T)); + upload((const T*)vt.data(), vt.size()); +} + +void cudaCopyDeviceToDevice(void* dst, const void* src, size_t count) +{ + CUDA_CHECK(cudaMemcpy(dst, src, count, cudaMemcpyDeviceToDevice)); +} + +void cudaCopyHostToDevice(void* dst, const void* src, size_t count) +{ + CUDA_CHECK(cudaMemcpy(dst, src, count, cudaMemcpyHostToDevice)); +} + +void* getSharedDevicePtr(Falcor::SharedResourceApiHandle sharedHandle, uint32_t bytes) +{ + // No handle? No pointer! + if (sharedHandle == NULL) + { + Falcor::reportFatalError("FalcorCUDA::importBufferToCudaPointer - texture shared handle creation failed"); + return nullptr; + } + + // Create the descriptor of our shared memory buffer + cudaExternalMemoryHandleDesc externalMemoryHandleDesc; + memset(&externalMemoryHandleDesc, 0, sizeof(externalMemoryHandleDesc)); + externalMemoryHandleDesc.type = cudaExternalMemoryHandleTypeD3D12Resource; + externalMemoryHandleDesc.handle.win32.handle = sharedHandle; + externalMemoryHandleDesc.size = bytes; + externalMemoryHandleDesc.flags = cudaExternalMemoryDedicated; + + // Get a handle to that memory + cudaExternalMemory_t externalMemory; + CUDA_CHECK(cudaImportExternalMemory(&externalMemory, &externalMemoryHandleDesc)); + + // Create a descriptor for our shared buffer pointer + cudaExternalMemoryBufferDesc bufDesc; + memset(&bufDesc, 0, sizeof(bufDesc)); + bufDesc.size = bytes; + + // Actually map the buffer + void* devPtr = nullptr; + CUDA_CHECK(cudaExternalMemoryGetMappedBuffer(&devPtr, externalMemory, &bufDesc)); + + return devPtr; +} + +bool freeSharedDevicePtr(void* ptr) +{ + if (!ptr) + return false; + return cudaSuccess == cudaFree(ptr); +} diff --git a/Source/Falcor/Utils/CudaUtils.h b/Source/Falcor/Utils/CudaUtils.h new file mode 100644 index 000000000..16e1aabde --- /dev/null +++ b/Source/Falcor/Utils/CudaUtils.h @@ -0,0 +1,131 @@ +/*************************************************************************** + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. + # + # Redistribution and use in source and binary forms, with or without + # modification, are permitted provided that the following conditions + # are met: + # * Redistributions of source code must retain the above copyright + # notice, this list of conditions and the following disclaimer. + # * Redistributions in binary form must reproduce the above copyright + # notice, this list of conditions and the following disclaimer in the + # documentation and/or other materials provided with the distribution. + # * Neither the name of NVIDIA CORPORATION nor the names of its + # contributors may be used to endorse or promote products derived + # from this software without specific prior written permission. + # + # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY + # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + # (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/Macros.h" +#include "Core/API/Handles.h" +#include "Core/API/Buffer.h" +#include + +// Note: There's some CUDA / Falcor type conflicts. This header includes Falcor-facing functions. +// Including or was a pain we wanted to avoid, so CUDA-specific stuff lives in CudaUtils.cpp + +/// CUDA device pointer +typedef unsigned long long CUdeviceptr; + +/** + * Get CUDA device pointer for shared resource. + * This takes a Windows Handle (e.g., from Falcor::Resource::getSharedApiHandle()) on a resource + * that has been declared "shared" [with Resource::BindFlags::Shared] plus a size of that + * resource in bytes and returns a CUDA device pointer from that resource that can be + * passed into OptiX or CUDA. This pointer is a value returned from + * cudaExternalMemoryGetMappedBuffer(), so should follow its rules (e.g., the docs claim + * you are responsible for calling cudaFree() on this pointer). + */ +FALCOR_API void* getSharedDevicePtr(Falcor::SharedResourceApiHandle sharedHandle, uint32_t bytes); + +/** + * Calls cudaFree() on the provided pointer. + */ +FALCOR_API bool freeSharedDevicePtr(void* ptr); + +FALCOR_API unsigned int initCuda(void); + +FALCOR_API void setCUDAContext(void); + +FALCOR_API void myCudaMemset(void* devPtr, int value, size_t count); + +FALCOR_API void cudaCopyDeviceToDevice(void* dst, const void* src, size_t count); + +FALCOR_API void syncCudaDevice(); + +FALCOR_API void cudaCopyHostToDevice(void* dst, const void* src, size_t count); + +/** + * Utility class for a GPU/device buffer for use with CUDA. + * Adapted from Ingo Wald's SIGGRAPH 2019 tutorial code for OptiX 7. + */ +class FALCOR_API CudaBuffer +{ +public: + CudaBuffer() {} + + CUdeviceptr getDevicePtr() { return (CUdeviceptr)mpDevicePtr; } + size_t getSize() { return mSizeBytes; } + + void allocate(size_t size); + void resize(size_t size); + void free(); + + template + void allocAndUpload(const std::vector& vt); + + template + bool download(T* t, size_t count); + + template + bool upload(const T* t, size_t count); + +private: + size_t mSizeBytes = 0; + void* mpDevicePtr = nullptr; +}; + +namespace Falcor +{ +/** + * Structure to encapsulate DX <-> CUDA interop data for a buffer. + */ +struct InteropBuffer +{ + ref buffer; // Falcor buffer + CUdeviceptr devicePtr = (CUdeviceptr)0; // CUDA pointer to buffer + + void free() + { + if (devicePtr) + { + freeSharedDevicePtr((void*)devicePtr); + devicePtr = (CUdeviceptr)0; + } + } +}; + +inline InteropBuffer createInteropBuffer(ref pDevice, size_t byteSize) +{ + InteropBuffer interop; + + // Create a new DX <-> CUDA shared buffer using the Falcor API to create, then find its CUDA pointer. + interop.buffer = Buffer::create( + pDevice, byteSize, Resource::BindFlags::ShaderResource | Resource::BindFlags::UnorderedAccess | Resource::BindFlags::Shared + ); + interop.devicePtr = (CUdeviceptr)getSharedDevicePtr(interop.buffer->getSharedApiHandle(), (uint32_t)interop.buffer->getSize()); + checkInvariant(interop.devicePtr != (CUdeviceptr)0, "Failed to create CUDA device ptr for buffer"); + + return interop; +} +} // namespace Falcor diff --git a/Source/Falcor/Utils/Debug/DebugConsole.h b/Source/Falcor/Utils/Debug/DebugConsole.h index 72ff35e49..4a1e63dcd 100644 --- a/Source/Falcor/Utils/Debug/DebugConsole.h +++ b/Source/Falcor/Utils/Debug/DebugConsole.h @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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,84 +38,85 @@ 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 +/** + * 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) { - 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(); + // 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()); + // 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()); + mPrevCerr = std::cerr.rdbuf(); + mCerr.open("CONERR$"); + std::cerr.rdbuf(mCerr.rdbuf()); - mPrevCin = std::cin.rdbuf(); - mCin.open("CONIN$"); - std::cin.rdbuf(mCin.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(); - } + // Redirect stdout for printf() to our console + // freopen_s(&mFp, "CONOUT$", "w", stdout); + // std::cout.clear(); + } - virtual ~DebugConsole() + virtual ~DebugConsole() + { + flush(); + if (mWaitForKey) { - flush(); - if (mWaitForKey) - { - pause(); - } + pause(); + } - // Restore the streams - std::cin.rdbuf(mPrevCin); - std::cerr.rdbuf(mPrevCerr); - std::cout.rdbuf(mPrevCout); + // 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); + // Restore stdout to default + // freopen("OUT", "w", stdout); + // fclose(mFp); - // Close console window - FreeConsole(); - } + // Close console window + FreeConsole(); + } - void pause() const - { - std::cout << "Press any key to continue..." << std::endl; - flush(); - char c = std::cin.get(); - } + 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(); - } + 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; +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; - }; -} + bool mWaitForKey = true; +}; +} // namespace Falcor #endif // FALCOR_WINDOWS diff --git a/Source/Falcor/Utils/Debug/PixelDebug.cpp b/Source/Falcor/Utils/Debug/PixelDebug.cpp index ab07ab9ff..19a813889 100644 --- a/Source/Falcor/Utils/Debug/PixelDebug.cpp +++ b/Source/Falcor/Utils/Debug/PixelDebug.cpp @@ -26,6 +26,7 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **************************************************************************/ #include "PixelDebug.h" +#include "Core/API/Device.h" #include "Core/API/RenderContext.h" #include "Core/Program/ComputeProgram.h" #include "Core/Program/ShaderVar.h" @@ -37,243 +38,257 @@ namespace Falcor { - namespace - { - static_assert(sizeof(PixelLogValue) % 16 == 0, "PixelLogValue size should be a multiple of 16B"); +namespace +{ +static_assert(sizeof(PixelLogValue) % 16 == 0, "PixelLogValue size should be a multiple of 16B"); - const char kReflectPixelDebugTypesFile[] = "Utils/Debug/ReflectPixelDebugTypes.cs.slang"; - } +const char kReflectPixelDebugTypesFile[] = "Utils/Debug/ReflectPixelDebugTypes.cs.slang"; +} // namespace - PixelDebug::SharedPtr PixelDebug::create(std::shared_ptr pDevice, uint32_t logSize) +PixelDebug::PixelDebug(ref pDevice, uint32_t logSize) : mpDevice(pDevice), mLogSize(logSize) {} + +void PixelDebug::beginFrame(RenderContext* pRenderContext, const uint2& frameDim) +{ + mFrameDim = frameDim; + if (mRunning) { - return SharedPtr(new PixelDebug(std::move(pDevice), logSize)); + throw RuntimeError("PixelDebug::beginFrame() - Logging is already running, did you forget to call endFrame()?"); } + mRunning = true; - void PixelDebug::beginFrame(RenderContext* pRenderContext, const uint2& frameDim) + // Reset previous data. + mPixelLogData.clear(); + mAssertLogData.clear(); + mDataValid = false; + mWaitingForData = false; + + if (mEnabled) { - mFrameDim = frameDim; - if (mRunning) + // Prepare log buffers. + if (!mpPixelLog || mpPixelLog->getElementCount() != mLogSize) { - throw RuntimeError("PixelDebug::beginFrame() - Logging is already running, did you forget to call endFrame()?"); + // Create program for type reflection. + if (!mpReflectProgram) + mpReflectProgram = ComputeProgram::createFromFile(mpDevice, kReflectPixelDebugTypesFile, "main"); + + // Allocate GPU buffers. + const ref& pDevice = pRenderContext->getDevice(); + mpPixelLog = Buffer::createStructured(pDevice, mpReflectProgram.get(), "gPixelLog", mLogSize); + if (mpPixelLog->getStructSize() != sizeof(PixelLogValue)) + throw RuntimeError("Struct PixelLogValue size mismatch between CPU/GPU"); + + mpAssertLog = Buffer::createStructured(pDevice, mpReflectProgram.get(), "gAssertLog", mLogSize); + if (mpAssertLog->getStructSize() != sizeof(AssertLogValue)) + throw RuntimeError("Struct AssertLogValue size mismatch between CPU/GPU"); + + // Allocate staging buffers for readback. These are shared, the data is stored consecutively. + mpCounterBuffer = Buffer::create(pDevice, 2 * sizeof(uint32_t), ResourceBindFlags::None, Buffer::CpuAccess::Read); + mpDataBuffer = + Buffer::create(pDevice, mpPixelLog->getSize() + mpAssertLog->getSize(), ResourceBindFlags::None, Buffer::CpuAccess::Read); } - mRunning = true; - // Reset previous data. - mPixelLogData.clear(); - mAssertLogData.clear(); - mDataValid = false; - mWaitingForData = false; + pRenderContext->clearUAVCounter(mpPixelLog, 0); + pRenderContext->clearUAVCounter(mpAssertLog, 0); + } +} - if (mEnabled) - { - // Prepare log buffers. - if (!mpPixelLog || mpPixelLog->getElementCount() != mLogSize) - { - // Create program for type reflection. - if (!mpReflectProgram) mpReflectProgram = ComputeProgram::createFromFile(mpDevice, kReflectPixelDebugTypesFile, "main"); +void PixelDebug::endFrame(RenderContext* pRenderContext) +{ + if (!mRunning) + { + throw RuntimeError("PixelDebug::endFrame() - Logging is not running, did you forget to call beginFrame()?"); + } + mRunning = false; - // Allocate GPU buffers. - Device* pDevice = pRenderContext->getDevice(); - mpPixelLog = Buffer::createStructured(pDevice, mpReflectProgram.get(), "gPixelLog", mLogSize); - if (mpPixelLog->getStructSize() != sizeof(PixelLogValue)) throw RuntimeError("Struct PixelLogValue size mismatch between CPU/GPU"); + 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()); - mpAssertLog = Buffer::createStructured(pDevice, mpReflectProgram.get(), "gAssertLog", mLogSize); - if (mpAssertLog->getStructSize() != sizeof(AssertLogValue)) throw RuntimeError("Struct AssertLogValue size mismatch between CPU/GPU"); + // Create fence first time we need it. + if (!mpFence) + mpFence = GpuFence::create(mpDevice); - // Allocate staging buffers for readback. These are shared, the data is stored consecutively. - mpCounterBuffer = Buffer::create(pDevice, 2 * sizeof(uint32_t), ResourceBindFlags::None, Buffer::CpuAccess::Read); - mpDataBuffer = Buffer::create(pDevice, mpPixelLog->getSize() + mpAssertLog->getSize(), ResourceBindFlags::None, Buffer::CpuAccess::Read); - } + // Submit command list and insert signal. + pRenderContext->flush(false); + mpFence->gpuSignal(pRenderContext->getLowLevelData()->getCommandQueue()); - pRenderContext->clearUAVCounter(mpPixelLog, 0); - pRenderContext->clearUAVCounter(mpAssertLog, 0); - } + mWaitingForData = true; } +} - void PixelDebug::endFrame(RenderContext* pRenderContext) - { - if (!mRunning) - { - throw RuntimeError("PixelDebug::endFrame() - Logging is not running, did you forget to call beginFrame()?"); - } - mRunning = false; +void PixelDebug::prepareProgram(const ref& pProgram, const ShaderVar& var) +{ + FALCOR_ASSERT(mRunning); - if (mEnabled) + if (mEnabled) + { + pProgram->addDefine("_PIXEL_DEBUG_ENABLED"); + var["gPixelLog"] = mpPixelLog; + var["gAssertLog"] = mpAssertLog; + var["PixelDebugCB"]["gPixelLogSelected"] = mSelectedPixel; + var["PixelDebugCB"]["gPixelLogSize"] = mLogSize; + var["PixelDebugCB"]["gAssertLogSize"] = mLogSize; + + const auto& hashedStrings = pProgram->getReflector()->getHashedStrings(); + for (const auto& hashedString : hashedStrings) { - // 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(mpDevice.get()); - - // Submit command list and insert signal. - pRenderContext->flush(false); - mpFence->gpuSignal(pRenderContext->getLowLevelData()->getCommandQueue()); - - mWaitingForData = true; + mHashToString.insert(std::make_pair(hashedString.hash, hashedString.string)); } } + else + { + pProgram->removeDefine("_PIXEL_DEBUG_ENABLED"); + } +} - void PixelDebug::prepareProgram(const Program::SharedPtr& pProgram, const ShaderVar& var) +void PixelDebug::renderUI(Gui::Widgets* widget) +{ + if (mRunning) { - FALCOR_ASSERT(mRunning); + throw RuntimeError("PixelDebug::renderUI() - Logging is running, call end() before renderUI()."); + } + if (widget) + { + // 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(value) or print(msg, value) in the shader to print values for the selected pixel.\n" + "All basic types such as int, float2, etc. are supported.\n" + "Use assert(condition) or assert(condition, msg) in the shader to test a condition.", + true + ); if (mEnabled) { - pProgram->addDefine("_PIXEL_DEBUG_ENABLED"); - var["gPixelLog"] = mpPixelLog; - var["gAssertLog"] = mpAssertLog; - var["PixelDebugCB"]["gPixelLogSelected"] = mSelectedPixel; - var["PixelDebugCB"]["gPixelLogSize"] = mLogSize; - var["PixelDebugCB"]["gAssertLogSize"] = mLogSize; - - const auto &hashedStrings = pProgram->getReflector()->getHashedStrings(); - for (const auto& hashedString : hashedStrings) - { - mHashToString.insert(std::make_pair(hashedString.hash, hashedString.string)); - } - } - else - { - pProgram->removeDefine("_PIXEL_DEBUG_ENABLED"); + widget->var("Selected pixel", mSelectedPixel); } } - void PixelDebug::renderUI(Gui::Widgets* widget) + // Fetch stats and show log if available. + bool isNewData = copyDataToCPU(); + if (mDataValid) { - if (mRunning) - { - throw RuntimeError("PixelDebug::renderUI() - Logging is running, call end() before renderUI()."); - } + std::ostringstream oss; - if (widget) + // Print list of printed values. + oss << "Pixel log:" << (mPixelLogData.empty() ? " \n" : "\n"); + for (auto v : mPixelLogData) { - // 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(value) or print(msg, value) in the shader to print values of basic types (int, float2, etc.) for the selected pixel.\n" - "Use assert(condition) or assert(condition, msg) in the shader to test a condition.", true); - if (mEnabled) + // Print message. + auto it = mHashToString.find(v.msgHash); + if (it != mHashToString.end() && !it->second.empty()) + oss << it->second << " "; + + // Parse value and convert to string. + if (v.count > 1) + oss << "("; + for (uint32_t i = 0; i < v.count; i++) { - widget->var("Selected pixel", mSelectedPixel); + 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: + oss << fstd::bit_cast(bits); + break; + default: + oss << "INVALID VALUE"; + break; + } + if (i + 1 < v.count) + oss << ", "; } + if (v.count > 1) + oss << ")"; + oss << "\n"; } - // Fetch stats and show log if available. - bool isNewData = copyDataToCPU(); - if (mDataValid) + // Print list of asserts. + if (!mAssertLogData.empty()) { - std::ostringstream oss; - - // Print list of printed values. - oss << "Pixel log:" << (mPixelLogData.empty() ? " \n" : "\n"); - for (auto v : mPixelLogData) + oss << "\n"; + for (auto v : mAssertLogData) { - // Print message. + oss << "Assert at (" << v.launchIndex.x << ", " << v.launchIndex.y << ", " << v.launchIndex.z << ")"; auto it = mHashToString.find(v.msgHash); - if (it != mHashToString.end() && !it->second.empty()) oss << it->second << " "; - - // 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: - oss << fstd::bit_cast(bits); - break; - default: - oss << "INVALID VALUE"; - break; - } - if (i + 1 < v.count) oss << ", "; - } - if (v.count > 1) oss << ")"; + if (it != mHashToString.end() && !it->second.empty()) + oss << " " << it->second; 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 << ")"; - auto it = mHashToString.find(v.msgHash); - if (it != mHashToString.end() && !it->second.empty()) oss << " " << it->second; - oss << "\n"; - } - } - - if( widget ) - widget->text(oss.str()); + if (widget) + widget->text(oss.str()); - bool isEmpty = mPixelLogData.empty() && mAssertLogData.empty(); - if (isNewData && !isEmpty) logInfo("\n" + oss.str()); - } + bool isEmpty = mPixelLogData.empty() && mAssertLogData.empty(); + if (isNewData && !isEmpty) + logInfo("\n" + oss.str()); } +} - bool PixelDebug::onMouseEvent(const MouseEvent& mouseEvent) +bool PixelDebug::onMouseEvent(const MouseEvent& mouseEvent) +{ + if (mEnabled) { - if (mEnabled) + if (mouseEvent.type == MouseEvent::Type::ButtonDown && mouseEvent.button == Input::MouseButton::Left) { - if (mouseEvent.type == MouseEvent::Type::ButtonDown && mouseEvent.button == Input::MouseButton::Left) - { - mSelectedPixel = uint2(mouseEvent.pos * float2(mFrameDim)); - return true; - } + mSelectedPixel = uint2(mouseEvent.pos * float2(mFrameDim)); + return true; } - return false; } + return false; +} - bool PixelDebug::copyDataToCPU() +bool PixelDebug::copyDataToCPU() +{ + FALCOR_ASSERT(!mRunning); + if (mWaitingForData) { - FALCOR_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 uint32_t printCount = std::min(mpPixelLog->getElementCount(), uavCounters[0]); - const uint32_t assertCount = std::min(mpAssertLog->getElementCount(), uavCounters[1]); - mpCounterBuffer->unmap(); - - // Map the data buffer and copy the relevant sections. - uint8_t* pLog = (uint8_t*)mpDataBuffer->map(Buffer::MapType::Read); - - mPixelLogData.resize(printCount); - for (uint32_t i = 0; i < printCount; i++) mPixelLogData[i] = ((PixelLogValue*)pLog)[i]; - pLog += mpPixelLog->getSize(); - - mAssertLogData.resize(assertCount); - for (uint32_t i = 0; i < assertCount; i++) mAssertLogData[i] = ((AssertLogValue*)pLog)[i]; + // Wait for signal. + mpFence->syncCpu(); + mWaitingForData = false; - mpDataBuffer->unmap(); - mDataValid = true; - return true; - } + 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 uint32_t printCount = std::min(mpPixelLog->getElementCount(), uavCounters[0]); + const uint32_t assertCount = std::min(mpAssertLog->getElementCount(), uavCounters[1]); + mpCounterBuffer->unmap(); + + // Map the data buffer and copy the relevant sections. + uint8_t* pLog = (uint8_t*)mpDataBuffer->map(Buffer::MapType::Read); + + mPixelLogData.resize(printCount); + for (uint32_t i = 0; i < printCount; i++) + mPixelLogData[i] = ((PixelLogValue*)pLog)[i]; + pLog += mpPixelLog->getSize(); + + mAssertLogData.resize(assertCount); + for (uint32_t i = 0; i < assertCount; i++) + mAssertLogData[i] = ((AssertLogValue*)pLog)[i]; + + mpDataBuffer->unmap(); + mDataValid = true; + return true; } - - return false; } + return false; } + +} // namespace Falcor diff --git a/Source/Falcor/Utils/Debug/PixelDebug.h b/Source/Falcor/Utils/Debug/PixelDebug.h index 0abce3d42..dc05693e5 100644 --- a/Source/Falcor/Utils/Debug/PixelDebug.h +++ b/Source/Falcor/Utils/Debug/PixelDebug.h @@ -38,85 +38,83 @@ namespace Falcor { - class RenderContext; - - /** Helper class for shader debugging using print() and assert(). - - Host-side integration: - - Create PixelDebug object - - Call beginFrame()/endFrame() before and after executing programs with debugging. - - Call prepareProgram() before launching a program to use debugging. - - Call onMouseEvent() and renderUI() from the respective callbacks in the render pass. - - Runtime usage: - - Import PixelDebug.slang in your shader. - - Use printSetPixel() in the shader to set the current pixel. - - 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 FALCOR_API PixelDebug - { - public: - using SharedPtr = std::shared_ptr; - virtual ~PixelDebug() = default; - - /** Create debug object. - \param[in] pDevice GPU device. - \param[in] logSize Number of shader print() and assert() statements per frame. - \return New object, or throws an exception on error. - */ - static SharedPtr create(std::shared_ptr pDevice, uint32_t logSize = 100); - - void beginFrame(RenderContext* pRenderContext, const uint2& frameDim); - void endFrame(RenderContext* pRenderContext); - - /** Perform program specialization and bind resources. - This call doesn't change any resource declarations in the program. - */ - void prepareProgram(const Program::SharedPtr& pProgram, const ShaderVar& var); - - void renderUI(Gui::Widgets& widget) { renderUI(&widget); } - void renderUI(Gui::Widgets* widget = nullptr); - bool onMouseEvent(const MouseEvent& mouseEvent); - - void enable() { mEnabled = true; } - - protected: - PixelDebug(std::shared_ptr pDevice, uint32_t logSize) : mpDevice(std::move(pDevice)), mLogSize(logSize) {} - bool copyDataToCPU(); - - // Internal state - std::shared_ptr mpDevice; - Program::SharedPtr mpReflectProgram; ///< Program for reflection of types. - Buffer::SharedPtr mpPixelLog; ///< Pixel log on the GPU with UAV counter. - Buffer::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; ///< Enable debugging features. - uint2 mSelectedPixel = { 0, 0 }; ///< Currently selected pixel. - - // Runtime data - uint2 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::unordered_map mHashToString; ///< Map of string hashes to string values. - - 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. - }; -} +class RenderContext; + +/** + * Helper class for shader debugging using print() and assert(). + * + * Host-side integration: + * - Create PixelDebug object + * - Call beginFrame()/endFrame() before and after executing programs with debugging. + * - Call prepareProgram() before launching a program to use debugging. + * - Call onMouseEvent() and renderUI() from the respective callbacks in the render pass. + * + * Runtime usage: + * - Import PixelDebug.slang in your shader. + * - Use printSetPixel() in the shader to set the current pixel. + * - 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 FALCOR_API PixelDebug +{ +public: + /** + * Constructor. Throws an exception on error. + * @param[in] pDevice GPU device. + * @param[in] logSize Number of shader print() and assert() statements per frame. + */ + PixelDebug(ref pDevice, uint32_t logSize = 100); + + void beginFrame(RenderContext* pRenderContext, const uint2& frameDim); + void endFrame(RenderContext* pRenderContext); + + /** + * Perform program specialization and bind resources. + * This call doesn't change any resource declarations in the program. + */ + void prepareProgram(const ref& pProgram, const ShaderVar& var); + + void renderUI(Gui::Widgets& widget) { renderUI(&widget); } + void renderUI(Gui::Widgets* widget = nullptr); + bool onMouseEvent(const MouseEvent& mouseEvent); + + void enable() { mEnabled = true; } + +protected: + bool copyDataToCPU(); + + // Internal state + ref mpDevice; + ref mpReflectProgram; ///< Program for reflection of types. + ref mpPixelLog; ///< Pixel log on the GPU with UAV counter. + ref mpAssertLog; ///< Assert log on the GPU with UAV counter. + ref mpCounterBuffer; ///< Staging buffer for async readback of UAV counters. + ref mpDataBuffer; ///< Staging buffer for async readback of logged data. + ref mpFence; ///< GPU fence for sychronizing readback. + + // Configuration + bool mEnabled = false; ///< Enable debugging features. + uint2 mSelectedPixel = {0, 0}; ///< Currently selected pixel. + + // Runtime data + uint2 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::unordered_map mHashToString; ///< Map of string hashes to string values. + + 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. +}; +} // namespace Falcor diff --git a/Source/Falcor/Utils/Debug/PixelDebug.slang b/Source/Falcor/Utils/Debug/PixelDebug.slang index 0b117ce86..a286ff2a9 100644 --- a/Source/Falcor/Utils/Debug/PixelDebug.slang +++ b/Source/Falcor/Utils/Debug/PixelDebug.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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,28 +26,29 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **************************************************************************/ -/** GPU side implementation of pixel debugging utils. - - Call the function printSetPixel() in the shader program to set the - current pixel before calling any of the following functions: - - print(value) for printing basic types - print(msg, value) for printing basic types with a prepended string - assert(condition, msg) for asserting on a condition (msg is optional) - - The host sets the following defines: - - _PIXEL_DEBUG_ENABLED Nonzero when pixel debugging is enabled. - -*/ +/** + * GPU side implementation of pixel debugging utils. + * + * Call the function printSetPixel() in the shader program to set the + * current pixel before calling any of the following functions: + * + * print(value) for printing basic types + * print(msg, value) for printing basic types with a prepended string + * assert(condition, msg) for asserting on a condition (msg is optional) + * + * The host sets the following defines: + * + * _PIXEL_DEBUG_ENABLED Nonzero when pixel debugging is enabled. + * + */ import PixelDebugTypes; cbuffer PixelDebugCB { - uint2 gPixelLogSelected; // Currently selected pixel to log. - uint gPixelLogSize; // Number of elements in the output buffer. - uint gAssertLogSize; + uint2 gPixelLogSelected; // Currently selected pixel to log. + uint gPixelLogSize; // Number of elements in the output buffer. + uint gAssertLogSize; }; RWStructuredBuffer gPixelLog; @@ -64,142 +65,163 @@ void printSetPixel(uint2 pixel) #endif } -/** Interface for values that can be printed. -*/ +/// Interface for values that can be printed. interface IPrintable { - /** Convert this value to a single `uint` holding its bits. - */ + /// Convert this value to a single `uint` holding its bits. uint getPrintableDataVal(); - /** Get the `PixelLogValueType` that represents this type. - */ + /// Get the `PixelLogValueType` that represents this type. static PixelLogValueType getPrintableValueType(); } -/** Values of type `bool` are printable. -*/ +/// Values of type `bool` are printable. extension bool : IPrintable { - uint getPrintableDataVal() { return uint(this); } - static PixelLogValueType getPrintableValueType() { return PixelLogValueType::Bool; } + uint getPrintableDataVal() + { + return uint(this); + } + static PixelLogValueType getPrintableValueType() + { + return PixelLogValueType::Bool; + } } -/** Values of type `int` are printable. -*/ +/// Values of type `int` are printable. extension int : IPrintable { - uint getPrintableDataVal() { return asuint(this); } - static PixelLogValueType getPrintableValueType() { return PixelLogValueType::Int; } + uint getPrintableDataVal() + { + return asuint(this); + } + static PixelLogValueType getPrintableValueType() + { + return PixelLogValueType::Int; + } } -/** Values of type `uint` are printable. -*/ +/// Values of type `uint` are printable. extension uint : IPrintable { - uint getPrintableDataVal() { return this; } - static PixelLogValueType getPrintableValueType() { return PixelLogValueType::Uint; } + uint getPrintableDataVal() + { + return this; + } + static PixelLogValueType getPrintableValueType() + { + return PixelLogValueType::Uint; + } } -/** Values of type `float` are printable. -*/ +/// Values of type `float` are printable. extension float : IPrintable { - uint getPrintableDataVal() { return asuint(this); } - static PixelLogValueType getPrintableValueType() { return PixelLogValueType::Float; } + uint getPrintableDataVal() + { + return asuint(this); + } + static PixelLogValueType getPrintableValueType() + { + return PixelLogValueType::Float; + } } #ifdef _PIXEL_DEBUG_ENABLED - /** Add a value to the pixel log if the current pixel is the selected one. - \param[in] msg A string message to accompany the value. - \param[in] valueType The type of values encoded into `data` - \param[in] count The number of components of `valueType` encoded into `data` - \param[in] data The bits of the value to be printed. - - This is a lower-level worker routine used by `print()`. - */ - [ForceInline] - void _print(String msg, PixelLogValueType valueType, int count, uint4 data) +/** + * Add a value to the pixel log if the current pixel is the selected one. + * @param[in] msg A string message to accompany the value. + * @param[in] valueType The type of values encoded into `data` + * @param[in] count The number of components of `valueType` encoded into `data` + * @param[in] data The bits of the value to be printed. + * + * This is a lower-level worker routine used by `print()`. + */ +[ForceInline] +void _print(String msg, PixelLogValueType valueType, int count, uint4 data) +{ + // TODO: Previously this was an early out: + // if (all(gPixelDebugPixel == gPixelLogSelected)) return; + // Due to a bug in slang this leads to expontentially growing compile times. + // This will be fixed in the compiler. Once it is, we should revert this workaround. + if (all(gPixelDebugPixel == gPixelLogSelected)) { - // TODO: Previously this was an early out: - // if (all(gPixelDebugPixel == gPixelLogSelected)) return; - // Due to a bug in slang this leads to expontentially growing compile times. - // This will be fixed in the compiler. Once it is, we should revert this workaround. - if (all(gPixelDebugPixel == gPixelLogSelected)) + uint i = gPixelLog.IncrementCounter(); + if (i < gPixelLogSize) { - uint i = gPixelLog.IncrementCounter(); - if (i < gPixelLogSize) - { - PixelLogValue val = {}; - val.msgHash = getStringHash(msg); - val.type = (uint)valueType; - val.count = count; - val.data = data; - gPixelLog[i] = val; - } + PixelLogValue val = {}; + val.msgHash = getStringHash(msg); + val.type = (uint)valueType; + val.count = count; + val.data = data; + gPixelLog[i] = val; } } - /** Print a value if the current pixel is the selected one. - \param[in] msg A string message to accompany the value. - \param[in] v The value to print - */ - [ForceInline] - void print(String msg, T v) - { - vector u = 0; - u[0] = v.getPrintableDataVal(); - _print(msg, T.getPrintableValueType(), 1, u); - } +} - /** Print a vector or values if the current pixel is the selected one. - \param[in] msg A string message to accompany the value. - \param[in] v The value to print - */ - [ForceInline] - void print(String msg, vector v) +/** + * Print a value if the current pixel is the selected one. + * @param[in] msg A string message to accompany the value. + * @param[in] v The value to print + */ +[ForceInline] +void print(String msg, T v) +{ + vector u = 0; + u[0] = v.getPrintableDataVal(); + _print(msg, T.getPrintableValueType(), 1, u); +} + +/** + * Print a vector or values if the current pixel is the selected one. + * @param[in] msg A string message to accompany the value. + * @param[in] v The value to print + */ +[ForceInline] +void print(String msg, vector v) +{ + vector u = 0; + for (int i = 0; i < N; ++i) { - vector u = 0; - for(int i = 0; i < N; ++i) - { - u[i] = v[i].getPrintableDataVal(); - } - _print(msg, T.getPrintableValueType(), N, u); + u[i] = v[i].getPrintableDataVal(); } + _print(msg, T.getPrintableValueType(), N, u); +} - /** Shader assert. - \param[in] condition If false, the current pixel is recorded in the log. - */ - [ForceInline] - void assert(bool condition, String msg = "") +/** + * Shader assert. + * @param[in] condition If false, the current pixel is recorded in the log. + */ +[ForceInline] +void assert(bool condition, String msg = "") +{ + if (!condition) { - if (!condition) + uint i = gAssertLog.IncrementCounter(); + if (i < gAssertLogSize) { - uint i = gAssertLog.IncrementCounter(); - if (i < gAssertLogSize) - { - AssertLogValue val; - val.launchIndex = uint3(gPixelDebugPixel, 0); - val.msgHash = getStringHash(msg); - gAssertLog[i] = val; - } + AssertLogValue val; + val.launchIndex = uint3(gPixelDebugPixel, 0); + val.msgHash = getStringHash(msg); + gAssertLog[i] = val; } } +} #else - /** Define null functions if debugging is disabled. - */ +/** + * Define null functions if debugging is disabled. + */ - [ForceInline] - void print(String msg, vector v) - {} +[ForceInline] +void print(String msg, vector v) {} - [ForceInline] - void print(String msg, T v) - {} +[ForceInline] +void print(String msg, T v) {} - [ForceInline] - void assert(bool condition, String msg = "") {} +[ForceInline] +void assert(bool condition, String msg = "") {} #endif // !_PIXEL_DEBUG_ENABLED diff --git a/Source/Falcor/Utils/Debug/PixelDebugTypes.slang b/Source/Falcor/Utils/Debug/PixelDebugTypes.slang index cef7d6159..6f96cd30c 100644 --- a/Source/Falcor/Utils/Debug/PixelDebugTypes.slang +++ b/Source/Falcor/Utils/Debug/PixelDebugTypes.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-21, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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,8 +30,7 @@ BEGIN_NAMESPACE_FALCOR -/** Define the basic types that print() supports. -*/ +/// Define the basic types that print() supports. enum class PixelLogValueType { Bool = 0, @@ -42,17 +41,17 @@ enum class PixelLogValueType struct PixelLogValue { - uint msgHash; ///< String hash of print message. - uint type; ///< Value type (see PixelLogValueType). - uint count; ///< Number of components (1-4). - uint _pad0; - uint4 data; ///< The data bits. The encoding is determined by the data type. + uint msgHash; ///< String hash of print message. + uint type; ///< Value type (see PixelLogValueType). + uint count; ///< Number of components (1-4). + uint _pad0; ///< Padding. + uint4 data; ///< The data bits. The encoding is determined by the data type. }; struct AssertLogValue { - uint3 launchIndex; ///< Launch index for the assert. - uint msgHash; ///< String hash of assert message. + uint3 launchIndex; ///< Launch index for the assert. + uint msgHash; ///< String hash of assert message. }; END_NAMESPACE_FALCOR diff --git a/Source/Falcor/Utils/Debug/ReflectPixelDebugTypes.cs.slang b/Source/Falcor/Utils/Debug/ReflectPixelDebugTypes.cs.slang index 4d0fdd599..44a8bc36f 100644 --- a/Source/Falcor/Utils/Debug/ReflectPixelDebugTypes.cs.slang +++ b/Source/Falcor/Utils/Debug/ReflectPixelDebugTypes.cs.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-21, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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,9 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **************************************************************************/ -/** Dummy compute program for reflection of the PixelDebug types. -*/ +/** + * Dummy compute program for reflection of the PixelDebug types. + */ import Utils.Debug.PixelDebug; void main() {} diff --git a/Source/Falcor/Utils/Debug/WarpProfiler.cpp b/Source/Falcor/Utils/Debug/WarpProfiler.cpp index 576cc5c22..8d09173e9 100644 --- a/Source/Falcor/Utils/Debug/WarpProfiler.cpp +++ b/Source/Falcor/Utils/Debug/WarpProfiler.cpp @@ -33,13 +33,13 @@ namespace Falcor { -WarpProfiler::WarpProfiler(Device* pDevice, const uint32_t binCount) : mBinCount(binCount) +WarpProfiler::WarpProfiler(ref pDevice, const uint32_t binCount) : mBinCount(binCount) { mpFence = GpuFence::create(pDevice); uint32_t elemCount = binCount * kWarpSize; - mpHistogramBuffer = Buffer::createStructured(pDevice, - 4, elemCount, ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess, Buffer::CpuAccess::None, - nullptr, false + mpHistogramBuffer = Buffer::createStructured( + pDevice, 4, elemCount, ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess, Buffer::CpuAccess::None, nullptr, + false ); mpHistogramStagingBuffer = Buffer::createStructured(pDevice, 4, elemCount, ResourceBindFlags::None, Buffer::CpuAccess::Read, nullptr, false); diff --git a/Source/Falcor/Utils/Debug/WarpProfiler.h b/Source/Falcor/Utils/Debug/WarpProfiler.h index d06baea9e..15efee10e 100644 --- a/Source/Falcor/Utils/Debug/WarpProfiler.h +++ b/Source/Falcor/Utils/Debug/WarpProfiler.h @@ -53,7 +53,7 @@ class FALCOR_API WarpProfiler * @param[in] pDevice GPU device. * @param[in] binCount Number of profiling bins. */ - WarpProfiler(Device* pDevice, const uint32_t binCount); + WarpProfiler(ref pDevice, const uint32_t binCount); /** * @brief Binds the profiler data to shader vars. @@ -93,9 +93,9 @@ class FALCOR_API WarpProfiler private: void readBackData(); - GpuFence::SharedPtr mpFence; - Buffer::SharedPtr mpHistogramBuffer; - Buffer::SharedPtr mpHistogramStagingBuffer; + ref mpFence; + ref mpHistogramBuffer; + ref mpHistogramStagingBuffer; const uint32_t mBinCount; ///< Number of profiling bins. std::vector mHistograms; ///< Histograms for all profiling bins. diff --git a/Source/Falcor/Utils/Geometry/GeometryHelpers.slang b/Source/Falcor/Utils/Geometry/GeometryHelpers.slang index 39ce3fa3e..0bc411665 100644 --- a/Source/Falcor/Utils/Geometry/GeometryHelpers.slang +++ b/Source/Falcor/Utils/Geometry/GeometryHelpers.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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,17 +33,18 @@ import Utils.Math.MathHelpers; Ray tracing *******************************************************************/ -/** 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. - - 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] 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. -*/ +/** + * 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. + * + * 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] 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 computeRayOrigin(float3 pos, float3 normal) { @@ -53,36 +54,43 @@ float3 computeRayOrigin(float3 pos, float3 normal) // 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)); + float3 iPos = asfloat(asint(pos) + select(pos < 0.f, -iOff, iOff)); // 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; + return select(abs(pos) < origin, pos + fOff, iPos); } - /******************************************************************* Bounding cones *******************************************************************/ -/** 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) +/** + * 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 + 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); @@ -105,16 +113,17 @@ void boundBoxSubtendedConeAngleCenter(const float3 origin, const float3 aabbMin, 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). + // 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. + 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. + 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); @@ -127,19 +136,26 @@ void boundBoxSubtendedConeAngleCenter(const float3 origin, const float3 aabbMin, } } -/** 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) +/** + * 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)) { @@ -155,9 +171,7 @@ void boundBoxSubtendedConeAngleAverage(float3 origin, float3 aabbMin, float3 aab [unroll] for (int i = 0; i < 8; ++i) { - const float3 corner = float3((i & 1) ? aabbMin.x : aabbMax.x, - (i & 2) ? aabbMin.y : aabbMax.y, - (i & 4) ? aabbMin.z : aabbMax.z); + 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); @@ -168,23 +182,22 @@ void boundBoxSubtendedConeAngleAverage(float3 origin, float3 aabbMin, float3 aab [unroll] for (int i = 0; i < 8; ++i) { - const float3 corner = float3((i & 1) ? aabbMin.x : aabbMax.x, - (i & 2) ? aabbMin.y : aabbMax.y, - (i & 4) ? aabbMin.z : aabbMax.z); + 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] sqrRadius Square of 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. -*/ +/** + * 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] sqrRadius Square of 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 sqrRadius, out float sinTheta, out float cosTheta) { // Is the point inside the bounding sphere? @@ -205,17 +218,17 @@ void boundSphereSubtendedConeAngle(float3 center, float sqrRadius, out float sin } } - /******************************************************************* Triangle utilities *******************************************************************/ -/** 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. -*/ +/** + * 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). @@ -229,7 +242,7 @@ float computeSquaredMinDistanceToTriangle(const float3 vertices[3], const float3 const float3 edges[3] = { normalize(vertices[1] - vertices[0]), normalize(vertices[2] - vertices[1]), - normalize(vertices[0] - vertices[2]) + normalize(vertices[0] - vertices[2]), }; float sqrPlanarDistance = FLT_MAX; uint insideMask = 0u; @@ -271,41 +284,36 @@ float computeSquaredMinDistanceToTriangle(const float3 vertices[3], const float3 return projDistance * projDistance + sqrPlanarDistance; } -/** Utility function to classify a point with respect to an axis-aligned plane in 2D. - \param[in] p Point. - \param[in] axis Axis of clip plane (0=x, 1=y). - \param[in] sign Direction of clip plane normal (+1 positive, -1 negative). - \param[in] c Location of clip plane along the chosen axis. - \param[in] planeThickness Optional plane thickness. - \return 1 if point is in front, -1 if behind, or 0 if it lies on the plane. -*/ -int classifyPointPlane2D( - const float2 p, - const uint axis, - const float sign, - const float c, - const float planeThickness = 1e-6f) +/** + * Utility function to classify a point with respect to an axis-aligned plane in 2D. + * @param[in] p Point. + * @param[in] axis Axis of clip plane (0=x, 1=y). + * @param[in] sign Direction of clip plane normal (+1 positive, -1 negative). + * @param[in] c Location of clip plane along the chosen axis. + * @param[in] planeThickness Optional plane thickness. + * @return 1 if point is in front, -1 if behind, or 0 if it lies on the plane. + */ +int classifyPointPlane2D(const float2 p, const uint axis, const float sign, const float c, const float planeThickness = 1e-6f) { float d = sign * (p[axis] - c); - if (d > planeThickness) return 1; - else if (d < -planeThickness) return -1; - else return 0; + if (d > planeThickness) + return 1; + else if (d < -planeThickness) + return -1; + else + return 0; } -/** Utility function to clip a polygon against an axis-aligned plane in 2D. - The input can be at most a hexagon (n=6) as one new vertex may be added. - \param[in,out] p Polygon vertices before/after clipping. - \param[in,out] n Number of vertices before/after clipping. - \param[in] axis Axis of clip plane (0=x, 1=y). - \param[in] sign Direction of clip plane normal. - \param[in] c Location of clip plane along the chosen axis. -*/ -void clipPolygonPlane2D( - inout float2 p[7], - inout uint n, - const uint axis, - const float sign, - const float c) +/** + * Utility function to clip a polygon against an axis-aligned plane in 2D. + * The input can be at most a hexagon (n=6) as one new vertex may be added. + * @param[in,out] p Polygon vertices before/after clipping. + * @param[in,out] n Number of vertices before/after clipping. + * @param[in] axis Axis of clip plane (0=x, 1=y). + * @param[in] sign Direction of clip plane normal. + * @param[in] c Location of clip plane along the chosen axis. + */ +void clipPolygonPlane2D(inout float2 p[7], inout uint n, const uint axis, const float sign, const float c) { if (n <= 1) { @@ -328,7 +336,8 @@ void clipPolygonPlane2D( if (d2 == 0) // p2 lies on the plane { - if (d1 != 0) q[k++] = p2; + if (d1 != 0) + q[k++] = p2; } else // p2 is on either side { @@ -336,7 +345,8 @@ void clipPolygonPlane2D( if (d1 == 0) // p1 lies on the plane { - if (k == 0 || any(q[k - 1] != p1)) q[k++] = p1; + if (k == 0 || any(q[k - 1] != p1)) + q[k++] = p1; } else if (d1 != d2) // p1 and p2 are on opposite sides => clip { @@ -344,27 +354,31 @@ void clipPolygonPlane2D( q[k++] = lerp(p2, p1, alpha); } - if (d2 > 0) q[k++] = p2; + if (d2 > 0) + q[k++] = p2; } p1 = p2; d1 = d2; } - if (fullyOnPlane) return; + if (fullyOnPlane) + return; n = k; - for (uint i = 0; i < k; i++) p[i] = q[i]; + for (uint i = 0; i < k; i++) + p[i] = q[i]; } -/** Computes the signed area of a triangle clipped to an axis-aligned box in 2D. - The area is positive for counter-clockwise winding in the xy-plane, assuming - a right-handed Cartesian coordinate system with x-axis right and y-axis up. - \param[in] pos Positions of the three vertices. - \param[in] minPoint Minimum point of the box. - \param[in] maxPoint Maximum point of the box. It is assumed that maxPoint > minPoint. - \return Signed area of the triangle clipped to the box. -*/ +/** + * Computes the signed area of a triangle clipped to an axis-aligned box in 2D. + * The area is positive for counter-clockwise winding in the xy-plane, assuming + * a right-handed Cartesian coordinate system with x-axis right and y-axis up. + * @param[in] pos Positions of the three vertices. + * @param[in] minPoint Minimum point of the box. + * @param[in] maxPoint Maximum point of the box. It is assumed that maxPoint > minPoint. + * @return Signed area of the triangle clipped to the box. + */ float computeClippedTriangleArea2D(const float2 pos[3], const float2 minPoint, const float2 maxPoint) { // Clip triangle to axis-aligned box. @@ -380,7 +394,8 @@ float computeClippedTriangleArea2D(const float2 pos[3], const float2 minPoint, c clipPolygonPlane2D(p, n, 1, +1.f, minPoint.y); clipPolygonPlane2D(p, n, 1, -1.f, maxPoint.y); - if (n < 3) return 0.f; + if (n < 3) + return 0.f; // Compute area of convex polygon. // The vertices must be specified in order. If counter-clockwise, area is positive. @@ -398,13 +413,14 @@ float computeClippedTriangleArea2D(const float2 pos[3], const float2 minPoint, c Curve uv parameterization *******************************************************************/ -/** Computes the uv parameters (from 0 to 1) from ray-curve hit position. - - \param[in] hitPos Position of the ray-curve hit point. - \param[in] sphereA Sphere (3D position + radius) at one end point. - \param[in] sphereB Sphere (3D position + radius) at the other end point. - \return UV parameters. -*/ +/** + * Computes the uv parameters (from 0 to 1) from ray-curve hit position. + * + * @param[in] hitPos Position of the ray-curve hit point. + * @param[in] sphereA Sphere (3D position + radius) at one end point. + * @param[in] sphereB Sphere (3D position + radius) at the other end point. + * @return UV parameters. + */ float2 curveHitPosToUV(const float3 hitPos, const float4 sphereA, const float4 sphereB) { // Build a reference frame. @@ -428,15 +444,16 @@ float2 curveHitPosToUV(const float3 hitPos, const float4 sphereA, const float4 s return result; } -/** Computes the ray-curve hit position and the corresponding sphere center from uv parameters. - It is based on this paper: http://www.sci.utah.edu/publications/Han2019a/tubes-final.pdf - - \param[in] uv UV parameters of the hit point. - \param[in] sphereA Sphere (3D position + radius) at one end point. - \param[in] sphereB Sphere (3D position + radius) at the other end point. - \param[out] hitPos Position of the ray-curve hit point. - \param[out] sphereCenter Position of the corresponding sphere center (for computing the normal direction). -*/ +/** + * Computes the ray-curve hit position and the corresponding sphere center from uv parameters. + * It is based on this paper: http://www.sci.utah.edu/publications/Han2019a/tubes-final.pdf + * + * @param[in] uv UV parameters of the hit point. + * @param[in] sphereA Sphere (3D position + radius) at one end point. + * @param[in] sphereB Sphere (3D position + radius) at the other end point. + * @param[out] hitPos Position of the ray-curve hit point. + * @param[out] sphereCenter Position of the corresponding sphere center (for computing the normal direction). + */ void uvToCurveHitPos(const float2 uv, const float4 sphereA, const float4 sphereB, out float3 hitPos, out float3 sphereCenter) { // Build a reference frame. @@ -472,7 +489,6 @@ void uvToCurveHitPos(const float2 uv, const float4 sphereA, const float4 sphereB // Case 1: hitPos is on the end cap of sphereA. deltaZ = -z; radius = sphereA.w; - } else if (p1 + z > z2) { @@ -503,24 +519,25 @@ void uvToCurveHitPos(const float2 uv, const float4 sphereA, const float4 sphereB sphereCenter = sphereA.xyz + (z + deltaZ) * fwd; } -/** Extract scale from a 4x4 transformation matrix. - We assume scaling is isotropic; i.e., it keeps the sphere shape after transformation. - - \param[in] xform Transformation matrix. - \return Isotropic scaling factor. -*/ +/** + * Extract scale from a 4x4 transformation matrix. + * We assume scaling is isotropic; i.e., it keeps the sphere shape after transformation. + * + * @param[in] xform Transformation matrix. + * @return Isotropic scaling factor. + */ float extractScaleFromTransform(const float4x4 xform) { return sqrt(xform[0][0] * xform[0][0] + xform[0][1] * xform[0][1] + xform[0][2] * xform[0][2]); } - -/** Transform a sphere at curve's end. - - \param[in] sphere Sphere (3D position + radius) at curve's end point. - \param[in] worldMat Local-to-world transformation matrix. - \return Transformed sphere. -*/ +/** + * Transform a sphere at curve's end. + * + * @param[in] sphere Sphere (3D position + radius) at curve's end point. + * @param[in] worldMat Local-to-world transformation matrix. + * @return Transformed sphere. + */ float4 transformSphere(const float4 sphere, const float4x4 worldMat) { float scaleFactor = extractScaleFromTransform(worldMat); diff --git a/Source/Falcor/Utils/Geometry/IntersectionHelpers.slang b/Source/Falcor/Utils/Geometry/IntersectionHelpers.slang index 97ed8a634..edc3cf81d 100644 --- a/Source/Falcor/Utils/Geometry/IntersectionHelpers.slang +++ b/Source/Falcor/Utils/Geometry/IntersectionHelpers.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-21, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -29,16 +29,17 @@ import Utils.Math.MathHelpers; -/** 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[out] t Distance to the closest intersection. - \return True if the ray intersects the sphere. -*/ +/** + * 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[out] t Distance to the closest intersection. + * @return True if the ray intersects the sphere. + */ bool intersectRaySphere(const float3 rayOrigin, const float3 rayDir, const float3 sphereCenter, const float sphereRadius, out float t) { // Implementation is taken from Chapter 7 of Ray-Tracing Gems @@ -48,7 +49,8 @@ bool intersectRaySphere(const float3 rayOrigin, const float3 rayDir, const float float discriminant = sphereRadius * sphereRadius - dot(f + b / a * rayDir, f + b / a * rayDir); // Negative discriminant means ray missed sphere. - if (discriminant < 0.f) return false; + if (discriminant < 0.f) + return false; // If b and discriminant are both 0, then the ray's origin lies on the sphere if (b == 0 && discriminant == 0) @@ -65,20 +67,22 @@ bool intersectRaySphere(const float3 rayOrigin, const float3 rayDir, const float float t1 = q / a; float tc = t0 < 0.f ? t1 : t0; // tc is the closest hit we care about - if (tc < 0.f) return false; + if (tc < 0.f) + return false; t = tc; return true; } -/** Ray-AABB intersection. - \param[in] rayOrigin Ray origin. - \param[in] rayDir Ray direction (does not have to be normalized). - \param[in] aabbMin AABB minimum. - \param[in] aabbMax AABB maximum. - \param[out] nearFar Returns intersection interval along ray. - \return True if the ray intersects the AABB. -*/ +/** + * Ray-AABB intersection. + * @param[in] rayOrigin Ray origin. + * @param[in] rayDir Ray direction (does not have to be normalized). + * @param[in] aabbMin AABB minimum. + * @param[in] aabbMax AABB maximum. + * @param[out] nearFar Returns intersection interval along ray. + * @return True if the ray intersects the AABB. + */ bool intersectRayAABB(const float3 rayOrigin, const float3 rayDir, const float3 aabbMin, const float3 aabbMax, out float2 nearFar) { const float3 invDir = 1.f / rayDir; @@ -90,21 +94,24 @@ bool intersectRayAABB(const float3 rayOrigin, const float3 rayDir, const float3 return nearFar.x <= nearFar.y; } -/** Ray-Triangle intersection based on "Watertight Ray/Triangle Intersection" - Paper link: http://jcgt.org/published/0002/01/05/paper.pdf - \param[in] rayOrigin Ray origin. - \param[in] rayDir Ray direction (does not have to be normalized). - \param[in] vertices Triangle vertices. - \param[out] barycentrics Barycentric coordinates. - \param[out] t Intersection distance. - \return True if the ray intersects the triangle. -*/ +/** + * Ray-Triangle intersection based on "Watertight Ray/Triangle Intersection" + * Paper link: http://jcgt.org/published/0002/01/05/paper.pdf + * @param[in] rayOrigin Ray origin. + * @param[in] rayDir Ray direction (does not have to be normalized). + * @param[in] vertices Triangle vertices. + * @param[out] barycentrics Barycentric coordinates. + * @param[out] t Intersection distance. + * @return True if the ray intersects the triangle. + */ bool intersectRayTriangle(const float3 rayOrigin, const float3 rayDir, const float3 vertices[3], out float3 barycentrics, out float t) { const float3 absDir = abs(rayDir); uint axis = 0; - if (absDir.y > absDir.x && absDir.y > absDir.z) axis = 1; - if (absDir.z > absDir.x && absDir.z > absDir.y) axis = 2; + if (absDir.y > absDir.x && absDir.y > absDir.z) + axis = 1; + if (absDir.z > absDir.x && absDir.z > absDir.y) + axis = 2; uint kz = axis; uint kx = (kz + 1) % 3; @@ -148,10 +155,12 @@ bool intersectRayTriangle(const float3 rayOrigin, const float3 rayDir, const flo W = (float)(BxAy - ByAx); } - if ((U < 0.f || V < 0.f || W < 0.f) && (U > 0.f || V > 0.f || W > 0.f)) return false; + if ((U < 0.f || V < 0.f || W < 0.f) && (U > 0.f || V > 0.f || W > 0.f)) + return false; float det = U + V + W; - if (det == 0.f) return false; + if (det == 0.f) + return false; const float Az = Sz * A[kz]; const float Bz = Sz * B[kz]; @@ -165,17 +174,25 @@ bool intersectRayTriangle(const float3 rayOrigin, const float3 rayDir, const flo return true; } -/** Ray intersection against linear swept sphere based on [Han et al. 2019], Ray Tracing Generalized Tube Primitives: Method and Applications. - Paper link: http://www.sci.utah.edu/publications/Han2019a/tubes-final.pdf - \param[in] rayOrigin Ray origin position. - \param[in] rayDir Unit ray direction vector. - \param[in] sphereA Sphere (3D position + radius) at one end point. - \param[in] sphereB Sphere at the other end point. - \param[in] useSphereJoints Indicating whether we test ray-sphere intersection at curve joints or not. - \param[out] result The closest intersection distance t, and a parameter u for linear interpolation (between 0 and 1). - \return True if the ray intersects the linear swept sphere segment. -*/ -bool intersectLinearSweptSphereHan19(float3 rayOrigin, float3 rayDir, float4 sphereA, float4 sphereB, bool useSphereJoints, out float2 result) +/** + * Ray intersection against linear swept sphere based on [Han et al. 2019], Ray Tracing Generalized Tube Primitives: Method and + * Applications. Paper link: http://www.sci.utah.edu/publications/Han2019a/tubes-final.pdf + * @param[in] rayOrigin Ray origin position. + * @param[in] rayDir Unit ray direction vector. + * @param[in] sphereA Sphere (3D position + radius) at one end point. + * @param[in] sphereB Sphere at the other end point. + * @param[in] useSphereJoints Indicating whether we test ray-sphere intersection at curve joints or not. + * @param[out] result The closest intersection distance t, and a parameter u for linear interpolation (between 0 and 1). + * @return True if the ray intersects the linear swept sphere segment. + */ +bool intersectLinearSweptSphereHan19( + float3 rayOrigin, + float3 rayDir, + float4 sphereA, + float4 sphereB, + bool useSphereJoints, + out float2 result +) { result = float2(FLT_MAX); @@ -241,12 +258,11 @@ bool intersectLinearSweptSphereHan19(float3 rayOrigin, float3 rayDir, float4 sph const float tmp2 = p2 / w; // Row-major matrix. - float4x4 M = - { - tmp1 * tmp2 * float4(vx, -dot(vx, A)), - tmp1 * tmp2 * float4(vy, -dot(vy, A)), - tmp1 * float4(vz, -dot(vz, A)), - float4(0, 0, 0, 1) + float4x4 M = { + tmp1 * tmp2 * float4(vx, -dot(vx, A)), // row 0 + tmp1 * tmp2 * float4(vy, -dot(vy, A)), // row 1 + tmp1 * float4(vz, -dot(vz, A)), // row 2 + float4(0, 0, 0, 1) // row 3 }; const float zCap = z1 * tmp1; diff --git a/Source/Falcor/Utils/HostDeviceShared.slangh b/Source/Falcor/Utils/HostDeviceShared.slangh index 12ed66478..0d9355607 100644 --- a/Source/Falcor/Utils/HostDeviceShared.slangh +++ b/Source/Falcor/Utils/HostDeviceShared.slangh @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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,62 +31,45 @@ Glue code for CPU/GPU compilation *******************************************************************/ -#if (defined(__STDC_HOSTED__) || defined(__cplusplus)) // we're in C-compliant compiler, probably host +#if (defined(__STDC_HOSTED__) || defined(__cplusplus)) // we're in C-compliant compiler, probably host #define HOST_CODE 1 #endif // TODO: Replace by bit packing functions #define EXTRACT_BITS(bits, offset, value) (((value) >> (offset)) & ((1 << (bits)) - 1)) -#define IS_BIT_SET(value, bitOffset) ((value & (1 << bitOffset)) != 0) +#define IS_BIT_SET(value, bitOffset) ((value & (1 << bitOffset)) != 0) // TODO: Add check that "value" fits into "bits" -#define PACK_BITS(bits, offset, flags, value) ((((value) & ((1 << (bits)) - 1)) << (offset)) | ((flags) & (~(((1 << (bits)) - 1) << (offset))))) +#define PACK_BITS(bits, offset, flags, value) \ + ((((value) & ((1 << (bits)) - 1)) << (offset)) | ((flags) & (~(((1 << (bits)) - 1) << (offset))))) #define PACK_BITS_UNSAFE(bits, offset, flags, value) (((value) << (offset)) | ((flags) & (~(((1 << (bits)) - 1) << (offset))))) #ifdef HOST_CODE /******************************************************************* CPU declarations *******************************************************************/ -#define BEGIN_NAMESPACE_FALCOR namespace Falcor{ +#define BEGIN_NAMESPACE_FALCOR \ + namespace Falcor \ + { #define END_NAMESPACE_FALCOR } #define SETTER_DECL #define CONST_FUNCTION const +#define STD_NAMESPACE std:: +#include "Utils/Math/ScalarMath.h" #include "Utils/Math/Vector.h" -#include "Utils/Math/Float16.h" -#include // TODO C++20: Replace with -#include +#include "Utils/Math/Matrix.h" #include -#include "Utils/Math/Matrix/Matrix.h" namespace Falcor { - using float3x3 = rmcv::mat3; - using float4x4 = rmcv::mat4; - using float3x4 = rmcv::matrix<3,4, float>; - - inline float f16tof32(uint v) { return glm::unpackHalf2x16(v).x; } - inline float2 f16tof32(uint2 v) { return { f16tof32(v.x), f16tof32(v.y) }; }; - inline float3 f16tof32(uint3 v) { return { f16tof32(v.x), f16tof32(v.y), f16tof32(v.z) }; }; - inline float4 f16tof32(uint4 v) { return { f16tof32(v.x), f16tof32(v.y), f16tof32(v.z), f16tof32(v.w) }; }; - - inline uint f32tof16(float v) { return glm::packHalf2x16({ v, 0.f }); } - inline uint2 f32tof16(float2 v) { return { f32tof16(v.x), f32tof16(v.y) }; } - inline uint3 f32tof16(float3 v) { return { f32tof16(v.x), f32tof16(v.y), f32tof16(v.z) }; } - inline uint4 f32tof16(float4 v) { return { f32tof16(v.x), f32tof16(v.y), f32tof16(v.z), f32tof16(v.w) }; } - - inline float saturate(float v) { return std::max(0.0f, std::min(1.0f, v)); } - inline float2 saturate(float2 v) { return float2(saturate(v.x), saturate(v.y)); } - inline float3 saturate(float3 v) { return float3(saturate(v.x), saturate(v.y), saturate(v.z)); } - inline float4 saturate(float4 v) { return float4(saturate(v.x), saturate(v.y), saturate(v.z), saturate(v.w)); } - - inline float asfloat(uint32_t i) { return fstd::bit_cast(i); } - inline float asfloat(int32_t i) { return fstd::bit_cast(i); } - inline float16_t asfloat16(uint16_t i) { return fstd::bit_cast(i); } - inline uint32_t asuint(float f) { return fstd::bit_cast(f); } - inline int32_t asint(float f) { return fstd::bit_cast(f); } - inline uint16_t asuint16(float16_t f) { return fstd::bit_cast(f); } - - inline float sign(float f) { return std::copysign(f, 1.f); }; + using math::sign; + using math::f16tof32; + using math::f32tof16; + using math::asfloat; + using math::asfloat16; + using math::asint; + using math::asuint; + using math::asuint16; } #else // HOST_CODE @@ -95,9 +78,11 @@ namespace Falcor HLSL declarations *******************************************************************/ #define inline -#define SETTER_DECL [mutating] +#define constexpr const #define BEGIN_NAMESPACE_FALCOR #define END_NAMESPACE_FALCOR +#define SETTER_DECL [mutating] #define CONST_FUNCTION +#define STD_NAMESPACE #endif // HOST_CODE diff --git a/Source/Falcor/Utils/Image/AsyncTextureLoader.cpp b/Source/Falcor/Utils/Image/AsyncTextureLoader.cpp index 4841e398b..4c6357100 100644 --- a/Source/Falcor/Utils/Image/AsyncTextureLoader.cpp +++ b/Source/Falcor/Utils/Image/AsyncTextureLoader.cpp @@ -31,114 +31,148 @@ namespace Falcor { - namespace - { - constexpr size_t kUploadsPerFlush = 16; ///< Number of texture uploads before issuing a flush (to keep upload heap from growing). - } +namespace +{ +constexpr size_t kUploadsPerFlush = 16; ///< Number of texture uploads before issuing a flush (to keep upload heap from growing). +} - AsyncTextureLoader::AsyncTextureLoader(std::shared_ptr pDevice, size_t threadCount) - : mpDevice(std::move(pDevice)) - { - runWorkers(threadCount); - } +AsyncTextureLoader::AsyncTextureLoader(ref pDevice, size_t threadCount) : mpDevice(pDevice) +{ + runWorkers(threadCount); +} - AsyncTextureLoader::~AsyncTextureLoader() - { - terminateWorkers(); +AsyncTextureLoader::~AsyncTextureLoader() +{ + terminateWorkers(); - mpDevice->flushAndSync(); - } + mpDevice->flushAndSync(); +} - std::future AsyncTextureLoader::loadFromFile(const std::filesystem::path& path, bool generateMipLevels, bool loadAsSrgb, Resource::BindFlags bindFlags, LoadCallback callback) - { - std::lock_guard lock(mMutex); - mLoadRequestQueue.push(LoadRequest{path, generateMipLevels, loadAsSrgb, bindFlags, callback }); - mCondition.notify_one(); - return mLoadRequestQueue.back().promise.get_future(); - } +std::future> AsyncTextureLoader::loadMippedFromFiles( + fstd::span paths, + bool loadAsSrgb, + Resource::BindFlags bindFlags, + LoadCallback callback +) +{ + std::lock_guard lock(mMutex); + mLoadRequestQueue.push(LoadRequest{{paths.begin(), paths.end()}, false, loadAsSrgb, bindFlags, callback}); + mCondition.notify_one(); + return mLoadRequestQueue.back().promise.get_future(); +} - void AsyncTextureLoader::runWorkers(size_t threadCount) - { - // Create a barrier to synchronize worker threads before issuing a global flush. - mFlushBarrier = std::make_shared(threadCount, [&]() { +std::future> AsyncTextureLoader::loadFromFile( + const std::filesystem::path& path, + bool generateMipLevels, + bool loadAsSrgb, + Resource::BindFlags bindFlags, + LoadCallback callback +) +{ + std::lock_guard lock(mMutex); + mLoadRequestQueue.push(LoadRequest{{path}, generateMipLevels, loadAsSrgb, bindFlags, callback}); + mCondition.notify_one(); + return mLoadRequestQueue.back().promise.get_future(); +} + +void AsyncTextureLoader::runWorkers(size_t threadCount) +{ + // Create a barrier to synchronize worker threads before issuing a global flush. + mFlushBarrier = std::make_shared( + threadCount, + [&]() + { mpDevice->flushAndSync(); mFlushPending = false; mUploadCounter = 0; - }); - - for (size_t i = 0; i < threadCount; ++i) - { - mThreads.emplace_back(&AsyncTextureLoader::runWorker, this); } + ); + + for (size_t i = 0; i < threadCount; ++i) + { + mThreads.emplace_back(&AsyncTextureLoader::runWorker, this); } +} + +void AsyncTextureLoader::runWorker() +{ + // This function is the entry point for worker threads. + // The workers wait on the load request queue and load a texture when woken up. + // To avoid the upload heap growing too large, we synchronize the threads and + // issue a global GPU flush at regular intervals. - void AsyncTextureLoader::runWorker() + while (true) { - // This function is the entry point for worker threads. - // The workers wait on the load request queue and load a texture when woken up. - // To avoid the upload heap growing too large, we synchronize the threads and - // issue a global GPU flush at regular intervals. + // Wait on condition until more work is ready. + std::unique_lock lock(mMutex); + mCondition.wait(lock, [&]() { return mTerminate || !mLoadRequestQueue.empty() || mFlushPending; }); - while (true) + // Sync thread if a flush is pending. + if (mFlushPending) { - // Wait on condition until more work is ready. - std::unique_lock lock(mMutex); - mCondition.wait(lock, [&]() { return mTerminate || !mLoadRequestQueue.empty() || mFlushPending; }); - - // Sync thread if a flush is pending. - if (mFlushPending) - { - lock.unlock(); - mFlushBarrier->wait(); - mCondition.notify_one(); - continue; - } + lock.unlock(); + mFlushBarrier->wait(); + mCondition.notify_one(); + continue; + } - // Terminate thread unless there is more work to do. - if (mTerminate && mLoadRequestQueue.empty() && !mFlushPending) break; + // Terminate thread unless there is more work to do. + if (mTerminate && mLoadRequestQueue.empty() && !mFlushPending) + break; - // Go back waiting if queue is currently empty. - if (mLoadRequestQueue.empty()) continue; + // Go back waiting if queue is currently empty. + if (mLoadRequestQueue.empty()) + continue; - // Pop next load request from queue. - auto request = std::move(mLoadRequestQueue.front()); - mLoadRequestQueue.pop(); + // Pop next load request from queue. + auto request = std::move(mLoadRequestQueue.front()); + mLoadRequestQueue.pop(); - lock.unlock(); + lock.unlock(); - // Load the textures (this part is running in parallel). - Texture::SharedPtr pTexture = Texture::createFromFile(mpDevice.get(), request.path, request.generateMipLevels, request.loadAsSRGB, request.bindFlags); - request.promise.set_value(pTexture); + // Load the textures (this part is running in parallel). + ref pTexture; + if (request.paths.size() == 1) + { + pTexture = + Texture::createFromFile(mpDevice, request.paths[0], request.generateMipLevels, request.loadAsSRGB, request.bindFlags); + } + else + { + pTexture = Texture::createMippedFromFiles(mpDevice, request.paths, request.loadAsSRGB, request.bindFlags); + } - if (request.callback) - { - request.callback(pTexture); - } + request.promise.set_value(pTexture); - lock.lock(); + if (request.callback) + { + request.callback(pTexture); + } - // Issue a global flush if necessary. - // TODO: It would be better to check the size of the upload heap instead. - if (!mTerminate && pTexture != nullptr && - ++mUploadCounter >= kUploadsPerFlush) - { - mFlushPending = true; - mCondition.notify_all(); - } + lock.lock(); - mCondition.notify_one(); + // Issue a global flush if necessary. + // TODO: It would be better to check the size of the upload heap instead. + if (!mTerminate && pTexture != nullptr && ++mUploadCounter >= kUploadsPerFlush) + { + mFlushPending = true; + mCondition.notify_all(); } + + mCondition.notify_one(); } +} - void AsyncTextureLoader::terminateWorkers() +void AsyncTextureLoader::terminateWorkers() +{ { - { - std::lock_guard lock(mMutex); - mTerminate = true; - } + std::lock_guard lock(mMutex); + mTerminate = true; + } - mCondition.notify_all(); + mCondition.notify_all(); - for (auto& thread : mThreads) thread.join(); - } + for (auto& thread : mThreads) + thread.join(); } +} // namespace Falcor diff --git a/Source/Falcor/Utils/Image/AsyncTextureLoader.h b/Source/Falcor/Utils/Image/AsyncTextureLoader.h index c91372adc..468074a0d 100644 --- a/Source/Falcor/Utils/Image/AsyncTextureLoader.h +++ b/Source/Falcor/Utils/Image/AsyncTextureLoader.h @@ -38,71 +38,91 @@ #include #include #include +#include namespace Falcor { - class Barrier; +class Barrier; - /** Utility class to load textures asynchronously using multiple worker threads. - */ - class FALCOR_API AsyncTextureLoader - { - public: - using LoadCallback = std::function; +/** + * Utility class to load textures asynchronously using multiple worker threads. + */ +class FALCOR_API AsyncTextureLoader +{ +public: + using LoadCallback = std::function pTexture)>; + + /** + * Constructor. + * @param[in] threadCount Number of worker threads. + */ + AsyncTextureLoader(ref pDevice, size_t threadCount = std::thread::hardware_concurrency()); - /** Constructor. - \param[in] threadCount Number of worker threads. - */ - AsyncTextureLoader(std::shared_ptr pDevice, size_t threadCount = std::thread::hardware_concurrency()); + /** + * Destructor. + * Blocks until all threads have terminated. + */ + ~AsyncTextureLoader(); - /** Destructor. - Blocks until all threads have terminated. - */ - ~AsyncTextureLoader(); + /** + * Request loading a texture with mips specified explicitly from individual files. + * @param[in] path List of full paths of all mips, starting from mip0. + * @param[in] loadAsSRGB Load the texture as sRGB format if supported, otherwise linear color. + * @param[in] bindFlags The bind flags for the texture resource. + * @param[in] callback Function called after the texture load has finished. + * @return A future to a new texture, or nullptr if the texture failed to load. + */ + std::future> loadMippedFromFiles( + fstd::span paths, + bool loadAsSRGB, + Resource::BindFlags bindFlags = Resource::BindFlags::ShaderResource, + LoadCallback callback = {} + ); - /** Request loading a texture. - \param[in] path File path of the texture. This can be a full path or a relative path from a data directory. - \param[in] generateMipLevels Whether the full mip-chain should be generated. - \param[in] loadAsSRGB Load the texture as sRGB format if supported, otherwise linear color. - \param[in] bindFlags The bind flags for the texture resource. - \param[in] callback Function called after the texture load has finished. - \return A future to a new texture, or nullptr if the texture failed to load. - */ - std::future loadFromFile( - const std::filesystem::path& path, - bool generateMipLevels, - bool loadAsSRGB, - Resource::BindFlags bindFlags = Resource::BindFlags::ShaderResource, - LoadCallback callback = {} - ); + /** + * Request loading a texture. + * @param[in] path File path of the texture. This can be a full path or a relative path from a data directory. + * @param[in] generateMipLevels Whether the full mip-chain should be generated. + * @param[in] loadAsSRGB Load the texture as sRGB format if supported, otherwise linear color. + * @param[in] bindFlags The bind flags for the texture resource. + * @param[in] callback Function called after the texture load has finished. + * @return A future to a new texture, or nullptr if the texture failed to load. + */ + std::future> loadFromFile( + const std::filesystem::path& path, + bool generateMipLevels, + bool loadAsSRGB, + Resource::BindFlags bindFlags = Resource::BindFlags::ShaderResource, + LoadCallback callback = {} + ); - private: - void runWorkers(size_t threadCount); - void runWorker(); - void terminateWorkers(); +private: + void runWorkers(size_t threadCount); + void runWorker(); + void terminateWorkers(); - struct LoadRequest - { - std::filesystem::path path; - bool generateMipLevels; - bool loadAsSRGB; - Resource::BindFlags bindFlags; - LoadCallback callback; - std::promise promise; - }; + struct LoadRequest + { + std::vector paths; + bool generateMipLevels; + bool loadAsSRGB; + Resource::BindFlags bindFlags; + LoadCallback callback; + std::promise> promise; + }; - std::shared_ptr mpDevice; + ref mpDevice; - std::mutex mMutex; ///< Mutex for synchronizing access to shared resources. - std::condition_variable mCondition; ///< Condition variable for workers to wait on. - std::shared_ptr mFlushBarrier; ///< Barrier for flushing the GPU to upload textures. - std::vector mThreads; ///< Worker threads. + std::mutex mMutex; ///< Mutex for synchronizing access to shared resources. + std::condition_variable mCondition; ///< Condition variable for workers to wait on. + std::shared_ptr mFlushBarrier; ///< Barrier for flushing the GPU to upload textures. + std::vector mThreads; ///< Worker threads. - // Internal state. Do not access outside of critical section. - std::queue mLoadRequestQueue; ///< Texture loading request queue. + // Internal state. Do not access outside of critical section. + std::queue mLoadRequestQueue; ///< Texture loading request queue. - bool mTerminate = false; ///< Flag to terminate worker threads. - bool mFlushPending = false; ///< Flag to indicate a GPU flush is pending. - uint32_t mUploadCounter = 0; ///< Counter to issue a flush every few uploads. - }; -} + bool mTerminate = false; ///< Flag to terminate worker threads. + bool mFlushPending = false; ///< Flag to indicate a GPU flush is pending. + uint32_t mUploadCounter = 0; ///< Counter to issue a flush every few uploads. +}; +} // namespace Falcor diff --git a/Source/Falcor/Utils/Image/Bitmap.cpp b/Source/Falcor/Utils/Image/Bitmap.cpp index 600c05af5..d6b3be791 100644 --- a/Source/Falcor/Utils/Image/Bitmap.cpp +++ b/Source/Falcor/Utils/Image/Bitmap.cpp @@ -29,6 +29,7 @@ #include "Core/Macros.h" #include "Core/API/Texture.h" #include "Core/Platform/MemoryMappedFile.h" +#include "Utils/Math/ScalarMath.h" #include "Utils/Logger.h" #include "Utils/StringUtils.h" @@ -36,615 +37,635 @@ #ifndef WINDOWS_LEAN_AND_MEAN #define WINDOWS_LEAN_AND_MEAN #endif -#include +#include #endif #include namespace Falcor { - static bool isRGB32fSupported() { return false; } // FIX THIS +static bool isRGB32fSupported() +{ + return false; // FIX THIS +} - static void genWarning(const std::string& errMsg, const std::filesystem::path& path) - { - logWarning("Error when loading image file from '{}': {}", path, errMsg); - } +static void genWarning(const std::string& errMsg, const std::filesystem::path& path) +{ + logWarning("Error when loading image file from '{}': {}", path, errMsg); +} - static bool isConvertibleToRGBA32Float(ResourceFormat format) - { - FormatType type = getFormatType(format); - bool isHalfFormat = (type == FormatType::Float && getNumChannelBits(format, 0) == 16); - bool isLargeIntFormat = ((type == FormatType::Uint || type == FormatType::Sint) && getNumChannelBits(format, 0) >= 16); - return isHalfFormat || isLargeIntFormat; - } +static bool isConvertibleToRGBA32Float(ResourceFormat format) +{ + FormatType type = getFormatType(format); + bool isHalfFormat = (type == FormatType::Float && getNumChannelBits(format, 0) == 16); + bool isLargeIntFormat = ((type == FormatType::Uint || type == FormatType::Sint) && getNumChannelBits(format, 0) >= 16); + return isHalfFormat || isLargeIntFormat; +} - /** Converts half float image to RGBA float image. - */ - static std::vector convertHalfToRGBA32Float(uint32_t width, uint32_t height, uint32_t channelCount, const void* pData) - { - std::vector newData(width * height * 4u, 0.f); - const glm::detail::hdata* pSrc = reinterpret_cast(pData); - float* pDst = newData.data(); +/** + * Converts half float image to RGBA float image. + */ +static std::vector convertHalfToRGBA32Float(uint32_t width, uint32_t height, uint32_t channelCount, const void* pData) +{ + std::vector newData(width * height * 4u, 0.f); + const float16_t* pSrc = reinterpret_cast(pData); + float* pDst = newData.data(); - for (uint32_t i = 0; i < width * height; ++i) + for (uint32_t i = 0; i < width * height; ++i) + { + for (uint32_t c = 0; c < channelCount; ++c) { - for (uint32_t c = 0; c < channelCount; ++c) - { - *pDst++ = glm::detail::toFloat32(*pSrc++); - } - pDst += (4 - channelCount); + *pDst++ = float(*pSrc++); } - - return newData; + pDst += (4 - channelCount); } - /** Converts integer image to RGBA float image. - Unsigned integers are normalized to [0,1], signed integers to [-1,1]. - */ - template - static std::vector convertIntToRGBA32Float(uint32_t width, uint32_t height, uint32_t channelCount, const void* pData) - { - std::vector newData(width * height * 4u, 0.f); - const SrcT* pSrc = reinterpret_cast(pData); - float* pDst = newData.data(); + return newData; +} - for (uint32_t i = 0; i < width * height; ++i) +/** + * Converts integer image to RGBA float image. + * Unsigned integers are normalized to [0,1], signed integers to [-1,1]. + */ +template +static std::vector convertIntToRGBA32Float(uint32_t width, uint32_t height, uint32_t channelCount, const void* pData) +{ + std::vector newData(width * height * 4u, 0.f); + const SrcT* pSrc = reinterpret_cast(pData); + float* pDst = newData.data(); + + for (uint32_t i = 0; i < width * height; ++i) + { + for (uint32_t c = 0; c < channelCount; ++c) { - for (uint32_t c = 0; c < channelCount; ++c) - { - *pDst++ = float(*pSrc++) / float(std::numeric_limits::max()); - } - pDst += (4 - channelCount); + *pDst++ = float(*pSrc++) / float(std::numeric_limits::max()); } - - return newData; + pDst += (4 - channelCount); } - /** Converts an image of the given format to an RGBA float image. - */ - static std::vector convertToRGBA32Float(ResourceFormat format, uint32_t width, uint32_t height, const void* pData) - { - FALCOR_ASSERT(isConvertibleToRGBA32Float(format)); - - FormatType type = getFormatType(format); - uint32_t channelCount = getFormatChannelCount(format); - uint32_t channelBits = getNumChannelBits(format, 0); + return newData; +} - std::vector floatData; +/** + * Converts an image of the given format to an RGBA float image. + */ +static std::vector convertToRGBA32Float(ResourceFormat format, uint32_t width, uint32_t height, const void* pData) +{ + FALCOR_ASSERT(isConvertibleToRGBA32Float(format)); - if (type == FormatType::Float && channelBits == 16) - { - floatData = convertHalfToRGBA32Float(width, height, channelCount, pData); - } - else if (type == FormatType::Uint && channelBits == 16) - { - floatData = convertIntToRGBA32Float(width, height, channelCount, pData); - } - else if (type == FormatType::Uint && channelBits == 32) - { - floatData = convertIntToRGBA32Float(width, height, channelCount, pData); - } - else if (type == FormatType::Sint && channelBits == 16) - { - floatData = convertIntToRGBA32Float(width, height, channelCount, pData); - } - else if (type == FormatType::Sint && channelBits == 32) - { - floatData = convertIntToRGBA32Float(width, height, channelCount, pData); - } - else - { - FALCOR_UNREACHABLE(); - } + FormatType type = getFormatType(format); + uint32_t channelCount = getFormatChannelCount(format); + uint32_t channelBits = getNumChannelBits(format, 0); - // Default alpha channel to 1. - if (channelCount < 4) - { - for (uint32_t i = 0; i < width * height; ++i) floatData[i * 4 + 3] = 1.f; - } + std::vector floatData; - return floatData; + if (type == FormatType::Float && channelBits == 16) + { + floatData = convertHalfToRGBA32Float(width, height, channelCount, pData); + } + else if (type == FormatType::Uint && channelBits == 16) + { + floatData = convertIntToRGBA32Float(width, height, channelCount, pData); + } + else if (type == FormatType::Uint && channelBits == 32) + { + floatData = convertIntToRGBA32Float(width, height, channelCount, pData); + } + else if (type == FormatType::Sint && channelBits == 16) + { + floatData = convertIntToRGBA32Float(width, height, channelCount, pData); + } + else if (type == FormatType::Sint && channelBits == 32) + { + floatData = convertIntToRGBA32Float(width, height, channelCount, pData); + } + else + { + FALCOR_UNREACHABLE(); } - /** 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) + // Default alpha channel to 1. + if (channelCount < 4) { - const unsigned width = FreeImage_GetWidth(pDib); - const unsigned height = FreeImage_GetHeight(pDib); + for (uint32_t i = 0; i < width * height; ++i) + floatData[i * 4 + 3] = 1.f; + } - auto pNew = FreeImage_AllocateT(FIT_RGBAF, width, height); - FreeImage_CloneMetadata(pNew, pDib); + return floatData; +} - const unsigned src_pitch = FreeImage_GetPitch(pDib); - const unsigned dst_pitch = FreeImage_GetPitch(pNew); +/** + * 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); - const BYTE *src_bits = (BYTE*)FreeImage_GetBits(pDib); - BYTE* dst_bits = (BYTE*)FreeImage_GetBits(pNew); + auto pNew = FreeImage_AllocateT(FIT_RGBAF, width, height); + FreeImage_CloneMetadata(pNew, pDib); - for (unsigned y = 0; y < height; y++) - { - const FIRGBF *src_pixel = (FIRGBF*)src_bits; - FIRGBAF* dst_pixel = (FIRGBAF*)dst_bits; + const unsigned src_pitch = FreeImage_GetPitch(pDib); + const unsigned dst_pitch = FreeImage_GetPitch(pNew); - 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; + const BYTE* src_bits = (BYTE*)FreeImage_GetBits(pDib); + BYTE* dst_bits = (BYTE*)FreeImage_GetBits(pNew); - } - src_bits += src_pitch; - dst_bits += dst_pitch; + 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; } - return pNew; + src_bits += src_pitch; + dst_bits += dst_pitch; } + return pNew; +} + +Bitmap::UniqueConstPtr Bitmap::create(uint32_t width, uint32_t height, ResourceFormat format, const uint8_t* pData) +{ + return Bitmap::UniqueConstPtr(new Bitmap(width, height, format, pData)); +} - Bitmap::UniqueConstPtr Bitmap::create(uint32_t width, uint32_t height, ResourceFormat format, const uint8_t* pData) +Bitmap::UniqueConstPtr Bitmap::createFromFile(const std::filesystem::path& path, bool isTopDown) +{ + std::filesystem::path fullPath; + if (!findFileInDataDirectories(path, fullPath)) { - return Bitmap::UniqueConstPtr(new Bitmap(width, height, format, pData)); + logWarning("Error when loading image file. Can't find image file '{}'.", path); + return nullptr; } - Bitmap::UniqueConstPtr Bitmap::createFromFile(const std::filesystem::path& path, bool isTopDown) - { - std::filesystem::path fullPath; - if (!findFileInDataDirectories(path, fullPath)) - { - logWarning("Error when loading image file. Can't find image file '{}'.", path); - return nullptr; - } + FREE_IMAGE_FORMAT fifFormat = FIF_UNKNOWN; - FREE_IMAGE_FORMAT fifFormat = FIF_UNKNOWN; + fifFormat = FreeImage_GetFileType(fullPath.string().c_str(), 0); + if (fifFormat == FIF_UNKNOWN) + { + // Can't get the format from the file. Use file extension + fifFormat = FreeImage_GetFIFFromFilename(fullPath.string().c_str()); - fifFormat = FreeImage_GetFileType(fullPath.string().c_str(), 0); if (fifFormat == FIF_UNKNOWN) { - // Can't get the format from the file. Use file extension - fifFormat = FreeImage_GetFIFFromFilename(fullPath.string().c_str()); - - if (fifFormat == FIF_UNKNOWN) - { - genWarning("Image type unknown", path); - return nullptr; - } - } - - // Check the library supports loading this image type - if (FreeImage_FIFSupportsReading(fifFormat) == false) - { - genWarning("Library doesn't support the file format", path); + genWarning("Image type unknown", path); return nullptr; } + } - // Read file using memory mapped access which is much faster than regular file IO. - MemoryMappedFile file(fullPath, MemoryMappedFile::kWholeFile, MemoryMappedFile::AccessHint::SequentialScan); - if (!file.isOpen()) - { - genWarning("Can't open image file {}", path); - return nullptr; - } - FIMEMORY* memory = FreeImage_OpenMemory((BYTE *)file.getData(), file.getSize()); - FIBITMAP* pDib = FreeImage_LoadFromMemory(fifFormat, memory); - FreeImage_CloseMemory(memory); - file.close(); + // Check the library supports loading this image type + if (FreeImage_FIFSupportsReading(fifFormat) == false) + { + genWarning("Library doesn't support the file format", path); + return nullptr; + } - if (pDib == nullptr) - { - genWarning("Can't read image file", path); - return nullptr; - } + // Read file using memory mapped access which is much faster than regular file IO. + MemoryMappedFile file(fullPath, MemoryMappedFile::kWholeFile, MemoryMappedFile::AccessHint::SequentialScan); + if (!file.isOpen()) + { + genWarning("Can't open image file {}", path); + return nullptr; + } + FIMEMORY* memory = FreeImage_OpenMemory((BYTE*)file.getData(), file.getSize()); + FIBITMAP* pDib = FreeImage_LoadFromMemory(fifFormat, memory); + FreeImage_CloseMemory(memory); + file.close(); - // Create the bitmap - const uint32_t height = FreeImage_GetHeight(pDib); - const uint32_t width = FreeImage_GetWidth(pDib); + if (pDib == nullptr) + { + genWarning("Can't read image file", path); + return nullptr; + } - if (height == 0 || width == 0 || FreeImage_GetBits(pDib) == nullptr) - { - genWarning("Invalid image", path); - return nullptr; - } + // Create the bitmap + const uint32_t height = FreeImage_GetHeight(pDib); + const uint32_t width = FreeImage_GetWidth(pDib); - // Convert palettized images to RGBA. - FREE_IMAGE_COLOR_TYPE colorType = FreeImage_GetColorType(pDib); - if (colorType == FIC_PALETTE) - { - auto pNew = FreeImage_ConvertTo32Bits(pDib); - FreeImage_Unload(pDib); - pDib = pNew; + if (height == 0 || width == 0 || FreeImage_GetBits(pDib) == nullptr) + { + genWarning("Invalid image", path); + return nullptr; + } - if (pDib == nullptr) - { - genWarning("Failed to convert palettized image to RGBA format", path); - return nullptr; - } - } + // Convert palettized images to RGBA. + FREE_IMAGE_COLOR_TYPE colorType = FreeImage_GetColorType(pDib); + if (colorType == FIC_PALETTE) + { + auto pNew = FreeImage_ConvertTo32Bits(pDib); + FreeImage_Unload(pDib); + pDib = pNew; - ResourceFormat format = ResourceFormat::Unknown; - uint32_t bpp = FreeImage_GetBPP(pDib); - switch(bpp) + if (pDib == nullptr) { - case 128: - format = ResourceFormat::RGBA32Float; // 4xfloat32 HDR format - break; - case 96: - format = isRGB32fSupported() ? ResourceFormat::RGB32Float : ResourceFormat::RGBA32Float; // 3xfloat32 HDR format - break; - case 64: - format = ResourceFormat::RGBA16Float; // 4xfloat16 HDR format - break; - case 32: - format = ResourceFormat::BGRA8Unorm; - break; - case 24: - format = ResourceFormat::BGRX8Unorm; - break; - case 16: - format = (FreeImage_GetImageType(pDib) == FIT_UINT16) ? ResourceFormat::R16Unorm : ResourceFormat::RG8Unorm; - break; - case 8: - format = ResourceFormat::R8Unorm; - break; - default: - genWarning("Unknown bits-per-pixel", path); + genWarning("Failed to convert palettized image to RGBA format", path); return nullptr; } + } - // Convert the image to RGBX image - if (bpp == 24) - { - bpp = 32; - auto pNew = FreeImage_ConvertTo32Bits(pDib); - FreeImage_Unload(pDib); - pDib = pNew; - } - else if (bpp == 96 && (isRGB32fSupported() == false)) - { - bpp = 128; - auto pNew = convertToRGBAF(pDib); - FreeImage_Unload(pDib); - pDib = pNew; - } - - // PFM images are loaded y-flipped, fix this by inverting the isTopDown flag. - if (fifFormat == FIF_PFM) isTopDown = !isTopDown; + ResourceFormat format = ResourceFormat::Unknown; + uint32_t bpp = FreeImage_GetBPP(pDib); + switch (bpp) + { + case 128: + format = ResourceFormat::RGBA32Float; // 4xfloat32 HDR format + break; + case 96: + format = isRGB32fSupported() ? ResourceFormat::RGB32Float : ResourceFormat::RGBA32Float; // 3xfloat32 HDR format + break; + case 64: + format = ResourceFormat::RGBA16Float; // 4xfloat16 HDR format + break; + case 32: + format = ResourceFormat::BGRA8Unorm; + break; + case 24: + format = ResourceFormat::BGRX8Unorm; + break; + case 16: + format = (FreeImage_GetImageType(pDib) == FIT_UINT16) ? ResourceFormat::R16Unorm : ResourceFormat::RG8Unorm; + break; + case 8: + format = ResourceFormat::R8Unorm; + break; + default: + genWarning("Unknown bits-per-pixel", path); + return nullptr; + } - UniqueConstPtr pBmp = UniqueConstPtr(new Bitmap(width, height, format)); - FreeImage_ConvertToRawBits(pBmp->getData(), pDib, pBmp->getRowPitch(), bpp, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK, isTopDown); + // Convert the image to RGBX image + if (bpp == 24) + { + bpp = 32; + auto pNew = FreeImage_ConvertTo32Bits(pDib); FreeImage_Unload(pDib); - return pBmp; + pDib = pNew; } - - Bitmap::Bitmap(uint32_t width, uint32_t height, ResourceFormat format) - : mWidth(width) - , mHeight(height) - , mRowPitch(getFormatRowPitch(format, width)) - , mFormat(format) + else if (bpp == 96 && (isRGB32fSupported() == false)) { - if (isCompressedFormat(format)) - { - uint32_t blockSizeY = getFormatHeightCompressionRatio(format); - FALCOR_ASSERT(height % blockSizeY == 0); // Should divide evenly - mSize = mRowPitch * (height / blockSizeY); - } - else - { - mSize = height * mRowPitch; - } - - mpData = std::unique_ptr(new uint8_t[mSize]); + bpp = 128; + auto pNew = convertToRGBAF(pDib); + FreeImage_Unload(pDib); + pDib = pNew; } - Bitmap::Bitmap(uint32_t width, uint32_t height, ResourceFormat format, const uint8_t* pData) - : Bitmap(width, height, format) + // PFM images are loaded y-flipped, fix this by inverting the isTopDown flag. + if (fifFormat == FIF_PFM) + isTopDown = !isTopDown; + + UniqueConstPtr pBmp = UniqueConstPtr(new Bitmap(width, height, format)); + FreeImage_ConvertToRawBits( + pBmp->getData(), pDib, pBmp->getRowPitch(), bpp, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK, isTopDown + ); + FreeImage_Unload(pDib); + return pBmp; +} + +Bitmap::Bitmap(uint32_t width, uint32_t height, ResourceFormat format) + : mWidth(width), mHeight(height), mRowPitch(getFormatRowPitch(format, width)), mFormat(format) +{ + if (isCompressedFormat(format)) { - std::memcpy(mpData.get(), pData, mSize); + uint32_t blockSizeY = getFormatHeightCompressionRatio(format); + FALCOR_ASSERT(height % blockSizeY == 0); // Should divide evenly + mSize = mRowPitch * (height / blockSizeY); } + else + { + mSize = height * mRowPitch; + } + + mpData = std::unique_ptr(new uint8_t[mSize]); +} - static FREE_IMAGE_FORMAT toFreeImageFormat(Bitmap::FileFormat fmt) +Bitmap::Bitmap(uint32_t width, uint32_t height, ResourceFormat format, const uint8_t* pData) : Bitmap(width, height, format) +{ + std::memcpy(mpData.get(), pData, mSize); +} + +static FREE_IMAGE_FORMAT toFreeImageFormat(Bitmap::FileFormat fmt) +{ + switch (fmt) { - switch(fmt) - { - case Bitmap::FileFormat::PngFile: - return FIF_PNG; - case Bitmap::FileFormat::JpegFile: - return FIF_JPEG; - case Bitmap::FileFormat::TgaFile: - return FIF_TARGA; - case Bitmap::FileFormat::BmpFile: - return FIF_BMP; - case Bitmap::FileFormat::PfmFile: - return FIF_PFM; - case Bitmap::FileFormat::ExrFile: - return FIF_EXR; - default: - FALCOR_UNREACHABLE(); - } + case Bitmap::FileFormat::PngFile: return FIF_PNG; + case Bitmap::FileFormat::JpegFile: + return FIF_JPEG; + case Bitmap::FileFormat::TgaFile: + return FIF_TARGA; + case Bitmap::FileFormat::BmpFile: + return FIF_BMP; + case Bitmap::FileFormat::PfmFile: + return FIF_PFM; + case Bitmap::FileFormat::ExrFile: + return FIF_EXR; + default: + FALCOR_UNREACHABLE(); } + return FIF_PNG; +} - static FREE_IMAGE_TYPE getImageType(uint32_t bytesPerPixel) +static FREE_IMAGE_TYPE getImageType(uint32_t bytesPerPixel) +{ + switch (bytesPerPixel) { - switch(bytesPerPixel) - { - case 4: - return FIT_BITMAP; - case 12: - return FIT_RGBF; - case 16: - return FIT_RGBAF; - default: - FALCOR_UNREACHABLE(); - } + case 4: return FIT_BITMAP; + case 12: + return FIT_RGBF; + case 16: + return FIT_RGBAF; + default: + FALCOR_UNREACHABLE(); } + return FIT_BITMAP; +} - Bitmap::FileFormat Bitmap::getFormatFromFileExtension(const std::string& ext) +Bitmap::FileFormat Bitmap::getFormatFromFileExtension(const std::string& ext) +{ + // This array is in the order of the enum + static const char* kExtensions[] = { + /* PngFile */ "png", + /*JpegFile */ "jpg", + /* TgaFile */ "tga", + /* BmpFile */ "bmp", + /* PfmFile */ "pfm", + /* ExrFile */ "exr", + /* DdsFile */ "dds", + }; + + for (size_t i = 0; i < std::size(kExtensions); i++) { - // This array is in the order of the enum - static const char* kExtensions[] = { - /* PngFile */ "png", - /*JpegFile */ "jpg", - /* TgaFile */ "tga", - /* BmpFile */ "bmp", - /* PfmFile */ "pfm", - /* ExrFile */ "exr", - /* DdsFile */ "dds" - }; + if (kExtensions[i] == ext) + return Bitmap::FileFormat(i); + } + reportError("Can't find a matching format for file extension '" + ext + "'"); + return Bitmap::FileFormat(-1); +} - for (size_t i = 0 ; i < std::size(kExtensions) ; i++) - { - if (kExtensions[i] == ext) return Bitmap::FileFormat(i); - } - reportError("Can't find a matching format for file extension '" + ext + "'"); - return Bitmap::FileFormat(-1); +FileDialogFilterVec Bitmap::getFileDialogFilters(ResourceFormat format) +{ + FileDialogFilterVec filters; + bool showHdr = true; + bool showLdr = true; + + if (format != ResourceFormat::Unknown) + { + // Save float, half and large integer (16/32 bit) formats as HDR. + showHdr = getFormatType(format) == FormatType::Float || isConvertibleToRGBA32Float(format); + showLdr = !showHdr; } - FileDialogFilterVec Bitmap::getFileDialogFilters(ResourceFormat format) + if (showHdr) { - FileDialogFilterVec filters; - bool showHdr = true; - bool showLdr = true; + filters.push_back({"exr", "High Dynamic Range"}); + filters.push_back({"pfm", "Portable Float Map"}); + filters.push_back({"hdr", "Radiance HDR"}); + } - if (format != ResourceFormat::Unknown) - { - // Save float, half and large integer (16/32 bit) formats as HDR. - showHdr = getFormatType(format) == FormatType::Float || isConvertibleToRGBA32Float(format); - showLdr = !showHdr; - } + 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"}); + } - if (showHdr) - { - filters.push_back({ "exr", "High Dynamic Range" }); - filters.push_back({ "pfm", "Portable Float Map" }); - filters.push_back({ "hdr", "Radiance HDR" }); - } + // DDS can store all formats + filters.push_back({"dds", "DirectDraw Surface"}); - 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; +} - // DDS can store all formats - filters.push_back({ "dds", "DirectDraw Surface" }); +std::string Bitmap::getFileExtFromResourceFormat(ResourceFormat format) +{ + auto filters = getFileDialogFilters(format); + return filters.front().ext; +} - // List of formats we can only load from - if (format == ResourceFormat::Unknown) - { - filters.push_back({ "hdr", "High Dynamic Range" }); - } - return filters; +void Bitmap::saveImageDialog(Texture* pTexture) +{ + std::filesystem::path path; + auto supportExtensions = getFileDialogFilters(pTexture->getFormat()); + + if (saveFileDialog(supportExtensions, path)) + { + std::string ext = getExtensionFromPath(path); + auto format = getFormatFromFileExtension(ext); + pTexture->captureToFile(0, 0, path, format); + } +} + +void Bitmap::saveImage( + const std::filesystem::path& path, + uint32_t width, + uint32_t height, + FileFormat fileFormat, + ExportFlags exportFlags, + ResourceFormat resourceFormat, + bool isTopDown, + void* pData +) +{ + if (pData == nullptr) + { + reportError("Bitmap::saveImage provided no data to save."); + return; } - std::string Bitmap::getFileExtFromResourceFormat(ResourceFormat format) + if (is_set(exportFlags, ExportFlags::Uncompressed) && is_set(exportFlags, ExportFlags::Lossy)) { - auto filters = getFileDialogFilters(format); - return filters.front().ext; + reportError("Bitmap::saveImage incompatible flags: lossy cannot be combined with uncompressed."); + return; } - void Bitmap::saveImageDialog(Texture* pTexture) + if (fileFormat == FileFormat::DdsFile) { - std::filesystem::path path; - auto supportExtensions = getFileDialogFilters(pTexture->getFormat()); + reportError("Bitmap::saveImage cannot save DDS files. Use ImageIO instead."); + return; + } - if (saveFileDialog(supportExtensions, path)) + int flags = 0; + FIBITMAP* pImage = nullptr; + uint32_t bytesPerPixel = getFormatBytesPerBlock(resourceFormat); + + // Convert 8-bit RGBA to BGRA byte order. + // TODO: Replace this code for swapping channels. Can't use FreeImage masks b/c they only care about 16 bpp images. + if (resourceFormat == ResourceFormat::RGBA8Unorm || resourceFormat == ResourceFormat::RGBA8Snorm || + resourceFormat == ResourceFormat::RGBA8UnormSrgb) + { + for (uint32_t a = 0; a < width * height; a++) { - std::string ext = getExtensionFromPath(path); - auto format = getFormatFromFileExtension(ext); - pTexture->captureToFile(0, 0, path, format); + uint32_t* pPixel = (uint32_t*)pData; + pPixel += a; + uint8_t* ch = (uint8_t*)pPixel; + std::swap(ch[0], ch[2]); + if (is_set(exportFlags, ExportFlags::ExportAlpha) == false) + { + ch[3] = 0xff; + } } } - void Bitmap::saveImage(const std::filesystem::path& path, uint32_t width, uint32_t height, FileFormat fileFormat, ExportFlags exportFlags, ResourceFormat resourceFormat, bool isTopDown, void* pData) + if (fileFormat == Bitmap::FileFormat::PfmFile || fileFormat == Bitmap::FileFormat::ExrFile) { - if (pData == nullptr) + std::vector floatData; + if (isConvertibleToRGBA32Float(resourceFormat)) + { + floatData = convertToRGBA32Float(resourceFormat, width, height, pData); + pData = floatData.data(); + resourceFormat = ResourceFormat::RGBA32Float; + bytesPerPixel = 16; + } + else if (bytesPerPixel != 16 && bytesPerPixel != 12) { - reportError("Bitmap::saveImage provided no data to save."); + reportError("Bitmap::saveImage supports only 32-bit/channel RGB/RGBA or 16-bit RGBA images as PFM/EXR files."); return; } - if (is_set(exportFlags, ExportFlags::Uncompressed) && is_set(exportFlags, ExportFlags::Lossy)) + const bool exportAlpha = is_set(exportFlags, ExportFlags::ExportAlpha); + + if (fileFormat == Bitmap::FileFormat::PfmFile) { - reportError("Bitmap::saveImage incompatible flags: lossy cannot be combined with uncompressed."); - return; + if (is_set(exportFlags, ExportFlags::Lossy)) + { + reportError("Bitmap::saveImage: PFM does not support lossy compression mode."); + return; + } + if (exportAlpha) + { + reportError("Bitmap::saveImage: PFM does not support alpha channel."); + return; + } } - if (fileFormat == FileFormat::DdsFile) + if (exportAlpha && bytesPerPixel != 16) { - reportError("Bitmap::saveImage cannot save DDS files. Use ImageIO instead."); + reportError("Bitmap::saveImage requesting to export alpha-channel to EXR file, but the resource doesn't have an alpha-channel"); return; } - int flags = 0; - FIBITMAP* pImage = nullptr; - uint32_t bytesPerPixel = getFormatBytesPerBlock(resourceFormat); + // Upload the image manually and flip it vertically + bool scanlineCopy = exportAlpha ? bytesPerPixel == 16 : bytesPerPixel == 12; - // Convert 8-bit RGBA to BGRA byte order. - // TODO: Replace this code for swapping channels. Can't use FreeImage masks b/c they only care about 16 bpp images. - if (resourceFormat == ResourceFormat::RGBA8Unorm || resourceFormat == ResourceFormat::RGBA8Snorm || resourceFormat == ResourceFormat::RGBA8UnormSrgb) + pImage = FreeImage_AllocateT(exportAlpha ? FIT_RGBAF : FIT_RGBF, width, height); + BYTE* head = (BYTE*)pData; + for (unsigned y = 0; y < height; y++) { - for (uint32_t a = 0; a < width * height; a++) + float* dstBits = (float*)FreeImage_GetScanLine(pImage, height - y - 1); + if (scanlineCopy) { - uint32_t* pPixel = (uint32_t*)pData; - pPixel += a; - uint8_t* ch = (uint8_t*)pPixel; - std::swap(ch[0], ch[2]); - if (is_set(exportFlags, ExportFlags::ExportAlpha) == false) + std::memcpy(dstBits, head, bytesPerPixel * width); + } + else + { + FALCOR_ASSERT(exportAlpha == false); + for (unsigned x = 0; x < width; x++) { - ch[3] = 0xff; + dstBits[x * 3 + 0] = (((float*)head)[x * 4 + 0]); + dstBits[x * 3 + 1] = (((float*)head)[x * 4 + 1]); + dstBits[x * 3 + 2] = (((float*)head)[x * 4 + 2]); } } + head += bytesPerPixel * width; } - if (fileFormat == Bitmap::FileFormat::PfmFile || fileFormat == Bitmap::FileFormat::ExrFile) + if (fileFormat == Bitmap::FileFormat::ExrFile) { - std::vector floatData; - if (isConvertibleToRGBA32Float(resourceFormat)) + flags = 0; + if (is_set(exportFlags, ExportFlags::Uncompressed)) { - floatData = convertToRGBA32Float(resourceFormat, width, height, pData); - pData = floatData.data(); - resourceFormat = ResourceFormat::RGBA32Float; - bytesPerPixel = 16; + flags |= EXR_NONE | EXR_FLOAT; } - else if (bytesPerPixel != 16 && bytesPerPixel != 12) + else if (is_set(exportFlags, ExportFlags::Lossy)) { - reportError("Bitmap::saveImage supports only 32-bit/channel RGB/RGBA or 16-bit RGBA images as PFM/EXR files."); - return; + flags |= EXR_B44 | EXR_ZIP; } + } + } + 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) + { + pImage = FreeImage_ConvertTo24Bits(pTemp); + FreeImage_Unload(pTemp); + } + else + { + pImage = pTemp; + } - const bool exportAlpha = is_set(exportFlags, ExportFlags::ExportAlpha); - - if (fileFormat == Bitmap::FileFormat::PfmFile) + std::vector warnings; + switch (fileFormat) + { + case FileFormat::JpegFile: + if (is_set(exportFlags, ExportFlags::Lossy) == false || is_set(exportFlags, ExportFlags::Uncompressed)) { - if (is_set(exportFlags, ExportFlags::Lossy)) - { - reportError("Bitmap::saveImage: PFM does not support lossy compression mode."); - return; - } - if (exportAlpha) - { - reportError("Bitmap::saveImage: PFM does not support alpha channel."); - return; - } + flags = JPEG_QUALITYSUPERB | JPEG_SUBSAMPLING_444; } - - if (exportAlpha && bytesPerPixel != 16) + if (is_set(exportFlags, ExportFlags::ExportAlpha)) { - reportError("Bitmap::saveImage requesting to export alpha-channel to EXR file, but the resource doesn't have an alpha-channel"); - return; + warnings.push_back("JPEG format does not support alpha channel."); } + break; - // Upload the image manually and flip it vertically - bool scanlineCopy = exportAlpha ? bytesPerPixel == 16 : bytesPerPixel == 12; + // Lossless formats + case FileFormat::PngFile: + flags = is_set(exportFlags, ExportFlags::Uncompressed) ? PNG_Z_NO_COMPRESSION : PNG_Z_BEST_COMPRESSION; - pImage = FreeImage_AllocateT(exportAlpha ? FIT_RGBAF : FIT_RGBF, width, height); - BYTE* head = (BYTE*)pData; - for (unsigned y = 0; y < height; y++) + if (is_set(exportFlags, ExportFlags::Lossy)) { - float* dstBits = (float*)FreeImage_GetScanLine(pImage, height - y - 1); - if (scanlineCopy) - { - std::memcpy(dstBits, head, bytesPerPixel * width); - } - else - { - FALCOR_ASSERT(exportAlpha == false); - 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]); - dstBits[x*3 + 2] = (((float*)head)[x*4 + 2]); - } - } - head += bytesPerPixel * width; + warnings.push_back("PNG format does not support lossy compression mode."); } + break; - if (fileFormat == Bitmap::FileFormat::ExrFile) - { - flags = 0; - if (is_set(exportFlags, ExportFlags::Uncompressed)) - { - flags |= EXR_NONE | EXR_FLOAT; - } - else if (is_set(exportFlags, ExportFlags::Lossy)) - { - flags |= EXR_B44 | EXR_ZIP; - } - } - } - 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) + case FileFormat::TgaFile: + if (is_set(exportFlags, ExportFlags::Lossy)) { - pImage = FreeImage_ConvertTo24Bits(pTemp); - FreeImage_Unload(pTemp); - } - else - { - pImage = pTemp; + warnings.push_back("TGA format does not support lossy compression mode."); } + break; - std::vector warnings; - switch(fileFormat) + case FileFormat::BmpFile: + if (is_set(exportFlags, ExportFlags::Lossy)) { - case FileFormat::JpegFile: - if (is_set(exportFlags, ExportFlags::Lossy) == false || is_set(exportFlags, ExportFlags::Uncompressed)) - { - flags = JPEG_QUALITYSUPERB | JPEG_SUBSAMPLING_444; - } - if (is_set(exportFlags, ExportFlags::ExportAlpha)) - { - warnings.push_back("JPEG format does not support alpha channel."); - } - break; - - // Lossless formats - case FileFormat::PngFile: - flags = is_set(exportFlags, ExportFlags::Uncompressed) ? PNG_Z_NO_COMPRESSION : PNG_Z_BEST_COMPRESSION; - - if (is_set(exportFlags, ExportFlags::Lossy)) - { - warnings.push_back("PNG format does not support lossy compression mode."); - } - break; - - case FileFormat::TgaFile: - if (is_set(exportFlags, ExportFlags::Lossy)) - { - warnings.push_back("TGA format does not support lossy compression mode."); - } - break; - - case FileFormat::BmpFile: - if (is_set(exportFlags, ExportFlags::Lossy)) - { - warnings.push_back("BMP format does not support lossy compression mode."); - } - if (is_set(exportFlags, ExportFlags::ExportAlpha)) - { - warnings.push_back("BMP format does not support alpha channel."); - } - break; - - default: - FALCOR_UNREACHABLE(); + warnings.push_back("BMP format does not support lossy compression mode."); } - - if (warnings.empty() == false) + if (is_set(exportFlags, ExportFlags::ExportAlpha)) { - logWarning("Bitmap::saveImage: {}", joinStrings(warnings, " ")); + warnings.push_back("BMP format does not support alpha channel."); } + break; + + default: + FALCOR_UNREACHABLE(); } - if (!FreeImage_Save(toFreeImageFormat(fileFormat), pImage, path.string().c_str(), flags)) + if (warnings.empty() == false) { - reportError("Bitmap::saveImage: FreeImage failed to save image"); + logWarning("Bitmap::saveImage: {}", joinStrings(warnings, " ")); } - FreeImage_Unload(pImage); } + + if (!FreeImage_Save(toFreeImageFormat(fileFormat), pImage, path.string().c_str(), flags)) + { + reportError("Bitmap::saveImage: FreeImage failed to save image"); + } + FreeImage_Unload(pImage); } +} // namespace Falcor diff --git a/Source/Falcor/Utils/Image/Bitmap.h b/Source/Falcor/Utils/Image/Bitmap.h index 3eccbd2a3..2b8df69a8 100644 --- a/Source/Falcor/Utils/Image/Bitmap.h +++ b/Source/Falcor/Utils/Image/Bitmap.h @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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,119 +34,134 @@ namespace Falcor { - class Texture; +class Texture; - /** A class representing a memory bitmap - */ - class FALCOR_API Bitmap +/** + * A class representing a memory bitmap + */ +class FALCOR_API Bitmap +{ +public: + enum class ExportFlags : uint32_t + { + None = 0u, //< Default + ExportAlpha = 1u << 0, //< Save alpha channel as well + Lossy = 1u << 1, //< Try to store in a lossy format + Uncompressed = 1u << 2, //< Prefer faster load to a more compact file size + }; + + enum class FileFormat { - public: - enum class ExportFlags : uint32_t - { - None = 0u, //< Default - ExportAlpha = 1u << 0, //< Save alpha channel as well - Lossy = 1u << 1, //< Try to store in a lossy format - Uncompressed = 1u << 2, //< Prefer faster load to a more compact file size - }; - - enum class FileFormat - { - PngFile, //< PNG file for lossless compressed 8-bits images with optional alpha - JpegFile, //< JPEG file for lossy compressed 8-bits images without alpha - TgaFile, //< TGA file for lossless uncompressed 8-bits images with optional alpha - BmpFile, //< BMP file for lossless uncompressed 8-bits images with optional alpha - PfmFile, //< PFM file for floating point HDR images with 32-bit float per channel - ExrFile, //< EXR file for floating point HDR images with 16-bit float per channel - DdsFile, //< DDS file for storing GPU resource formats, including block compressed formats - //< See ImageIO. TODO: Remove(?) Bitmap IO implementation when ImageIO supports other formats - }; - - using UniquePtr = std::unique_ptr; - using UniqueConstPtr = std::unique_ptr; - - /** Create from memory. - \param[in] width Width in pixels. - \param[in] height Height in pixels - \param[in] format Resource format. - \param[in] pData Pointer to data. Data will be copied internally during creation and does not need to be managed by the caller. - \return A new bitmap object. - */ - static UniqueConstPtr create(uint32_t width, uint32_t height, ResourceFormat format, const uint8_t* pData); - - /** Create a new object from file. - \param[in] path Path to load from. If the file can't be found relative to the current directory, Falcor will search for it in the common directories. - \param[in] isTopDown Control the memory layout of the image. If true, the top-left pixel is the first pixel in the buffer, otherwise the bottom-left pixel is first. - \return If loading was successful, a new object. Otherwise, nullptr. - */ - static UniqueConstPtr createFromFile(const std::filesystem::path& path, bool isTopDown); - - /** Store a memory buffer to a file. - \param[in] path Path to write to. - \param[in] width The width of the image. - \param[in] height The height of the image. - \param[in] fileFormat The destination file format. See FileFormat enum above. - \param[in] exportFlags The flags to export the file. See ExportFlags above. - \param[in] ResourceFormat the format of the resource data - \param[in] isTopDown Control the memory layout of the image. If true, the top-left pixel will be stored first, otherwise the bottom-left pixel will be stored first - \param[in] pData Pointer to the buffer containing the image - */ - static void saveImage(const std::filesystem::path& path, uint32_t width, uint32_t height, FileFormat fileFormat, ExportFlags exportFlags, ResourceFormat resourceFormat, bool isTopDown, void* pData); - - /** Open dialog to save image to a file - \param[in] pTexture Texture to save to file - */ - static void saveImageDialog(Texture* pTexture); - - /** Get a pointer to the bitmap's data store - */ - uint8_t* getData() const { return mpData.get(); } - - /** Get the width of the bitmap - */ - uint32_t getWidth() const { return mWidth; } - - /** Get the height of the bitmap - */ - uint32_t getHeight() const { return mHeight; } - - /** Get the data format - */ - ResourceFormat getFormat() const { return mFormat; } - - /** Get the row pitch in bytes. For compressed formats this corresponds to one row of blocks, not pixels. - */ - uint32_t getRowPitch() const { return mRowPitch; } - - /** Get the data size in bytes - */ - uint32_t getSize() const { return mSize; } - - /** Get the file dialog filter vec for images. - \param[in] format If set to ResourceFormat::Unknown, will return all the supported image file formats. If set to something else, will only return file types which support this format. - */ - static FileDialogFilterVec getFileDialogFilters(ResourceFormat format = ResourceFormat::Unknown); - - /** 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 - */ - static FileFormat getFormatFromFileExtension(const std::string& ext); - - protected: - Bitmap() = default; - Bitmap(uint32_t width, uint32_t height, ResourceFormat format); - Bitmap(uint32_t width, uint32_t height, ResourceFormat format, const uint8_t* pData); - - std::unique_ptr mpData; - uint32_t mWidth = 0; - uint32_t mHeight = 0; - uint32_t mRowPitch = 0; - uint32_t mSize = 0; - ResourceFormat mFormat = ResourceFormat::Unknown; + PngFile, //< PNG file for lossless compressed 8-bits images with optional alpha + JpegFile, //< JPEG file for lossy compressed 8-bits images without alpha + TgaFile, //< TGA file for lossless uncompressed 8-bits images with optional alpha + BmpFile, //< BMP file for lossless uncompressed 8-bits images with optional alpha + PfmFile, //< PFM file for floating point HDR images with 32-bit float per channel + ExrFile, //< EXR file for floating point HDR images with 16-bit float per channel + DdsFile, //< DDS file for storing GPU resource formats, including block compressed formats + //< See ImageIO. TODO: Remove(?) Bitmap IO implementation when ImageIO supports other formats }; - FALCOR_ENUM_CLASS_OPERATORS(Bitmap::ExportFlags); -} + using UniquePtr = std::unique_ptr; + using UniqueConstPtr = std::unique_ptr; + + /** + * Create from memory. + * @param[in] width Width in pixels. + * @param[in] height Height in pixels + * @param[in] format Resource format. + * @param[in] pData Pointer to data. Data will be copied internally during creation and does not need to be managed by the caller. + * @return A new bitmap object. + */ + static UniqueConstPtr create(uint32_t width, uint32_t height, ResourceFormat format, const uint8_t* pData); + + /** + * Create a new object from file. + * @param[in] path Path to load from. If the file can't be found relative to the current directory, Falcor will search for it in the + * common directories. + * @param[in] isTopDown Control the memory layout of the image. If true, the top-left pixel is the first pixel in the buffer, otherwise + * the bottom-left pixel is first. + * @return If loading was successful, a new object. Otherwise, nullptr. + */ + static UniqueConstPtr createFromFile(const std::filesystem::path& path, bool isTopDown); + + /** + * Store a memory buffer to a file. + * @param[in] path Path to write to. + * @param[in] width The width of the image. + * @param[in] height The height of the image. + * @param[in] fileFormat The destination file format. See FileFormat enum above. + * @param[in] exportFlags The flags to export the file. See ExportFlags above. + * @param[in] ResourceFormat the format of the resource data + * @param[in] isTopDown Control the memory layout of the image. If true, the top-left pixel will be stored first, otherwise the + * bottom-left pixel will be stored first + * @param[in] pData Pointer to the buffer containing the image + */ + static void saveImage( + const std::filesystem::path& path, + uint32_t width, + uint32_t height, + FileFormat fileFormat, + ExportFlags exportFlags, + ResourceFormat resourceFormat, + bool isTopDown, + void* pData + ); + + /** + * Open dialog to save image to a file + * @param[in] pTexture Texture to save to file + */ + static void saveImageDialog(Texture* pTexture); + + /// Get a pointer to the bitmap's data store + uint8_t* getData() const { return mpData.get(); } + + /// Get the width of the bitmap + uint32_t getWidth() const { return mWidth; } + + /// Get the height of the bitmap + uint32_t getHeight() const { return mHeight; } + + /// Get the data format + ResourceFormat getFormat() const { return mFormat; } + + /// Get the row pitch in bytes. For compressed formats this corresponds to one row of blocks, not pixels. + uint32_t getRowPitch() const { return mRowPitch; } + + /// Get the data size in bytes + uint32_t getSize() const { return mSize; } + + /** + * Get the file dialog filter vec for images. + * @param[in] format If set to ResourceFormat::Unknown, will return all the supported image file formats. If set to something else, will + * only return file types which support this format. + */ + static FileDialogFilterVec getFileDialogFilters(ResourceFormat format = ResourceFormat::Unknown); + + /** + * 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 + */ + static FileFormat getFormatFromFileExtension(const std::string& ext); + +protected: + Bitmap() = default; + Bitmap(uint32_t width, uint32_t height, ResourceFormat format); + Bitmap(uint32_t width, uint32_t height, ResourceFormat format, const uint8_t* pData); + + std::unique_ptr mpData; + uint32_t mWidth = 0; + uint32_t mHeight = 0; + uint32_t mRowPitch = 0; + uint32_t mSize = 0; + ResourceFormat mFormat = ResourceFormat::Unknown; +}; + +FALCOR_ENUM_CLASS_OPERATORS(Bitmap::ExportFlags); +} // namespace Falcor diff --git a/Source/Falcor/Utils/Image/CopyColorChannel.cs.slang b/Source/Falcor/Utils/Image/CopyColorChannel.cs.slang index 2031362cc..3f99542d7 100644 --- a/Source/Falcor/Utils/Image/CopyColorChannel.cs.slang +++ b/Source/Falcor/Utils/Image/CopyColorChannel.cs.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-21, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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,15 +30,16 @@ RWTexture2D gDst; cbuffer CB { - uint2 viewDim; - uint channelIndex; + uint2 viewDim; + uint channelIndex; }; [numthreads(16, 16, 1)] -void main(uint3 dispatchThreadId : SV_DispatchThreadID) +void main(uint3 dispatchThreadId: SV_DispatchThreadID) { uint2 coord = dispatchThreadId.xy; - if (any(coord >= viewDim)) return; + if (any(coord >= viewDim)) + return; // Copy single color channel from source to all channels of destination. gDst[coord] = gSrc[coord][channelIndex]; diff --git a/Source/Falcor/Utils/Image/ImageIO.cpp b/Source/Falcor/Utils/Image/ImageIO.cpp index a588e8958..48a799a4d 100644 --- a/Source/Falcor/Utils/Image/ImageIO.cpp +++ b/Source/Falcor/Utils/Image/ImageIO.cpp @@ -30,6 +30,7 @@ #include "Core/API/CopyContext.h" #include "Core/API/NativeFormats.h" #include "Core/Platform/MemoryMappedFile.h" +#include "Utils/Math/ScalarMath.h" #include "Utils/Logger.h" #include @@ -39,737 +40,764 @@ namespace Falcor { - namespace +namespace +{ +struct ImportData +{ + // Commonly used values converted or casted for cleaner access + ResourceFormat format; + Resource::Type type; + uint32_t width; + uint32_t height; + uint32_t depth; + uint32_t arraySize; + uint32_t mipLevels; + bool hasDX10Header = false; + + // Data to be imported + std::vector imageData; +}; + +struct ExportData +{ + // Commonly used values converted or casted for cleaner access + nvtt::TextureType type; + ResourceFormat format; + uint32_t width; + uint32_t height; + uint32_t depth; + uint32_t faceCount; + uint32_t mipLevels; + + // Data to be exported + std::vector images; +}; + +ImageIO::CompressionMode convertFormatToMode(ResourceFormat format) +{ + switch (format) { - struct ImportData - { - // Commonly used values converted or casted for cleaner access - ResourceFormat format; - Resource::Type type; - uint32_t width; - uint32_t height; - uint32_t depth; - uint32_t arraySize; - uint32_t mipLevels; - bool hasDX10Header = false; - - // Data to be imported - std::vector imageData; - }; - - struct ExportData - { - // Commonly used values converted or casted for cleaner access - nvtt::TextureType type; - ResourceFormat format; - uint32_t width; - uint32_t height; - uint32_t depth; - uint32_t faceCount; - uint32_t mipLevels; - - // Data to be exported - std::vector images; - }; - - ImageIO::CompressionMode convertFormatToMode(ResourceFormat format) + case ResourceFormat::BC1Unorm: + case ResourceFormat::BC1UnormSrgb: + return ImageIO::CompressionMode::BC1; + case ResourceFormat::BC2Unorm: + case ResourceFormat::BC2UnormSrgb: + return ImageIO::CompressionMode::BC2; + case ResourceFormat::BC3Unorm: + case ResourceFormat::BC3UnormSrgb: + return ImageIO::CompressionMode::BC3; + case ResourceFormat::BC4Unorm: + return ImageIO::CompressionMode::BC4; + case ResourceFormat::BC5Snorm: + case ResourceFormat::BC5Unorm: + return ImageIO::CompressionMode::BC5; + case ResourceFormat::BC6HS16: + return ImageIO::CompressionMode::BC6; + case ResourceFormat::BC7Unorm: + case ResourceFormat::BC7UnormSrgb: + return ImageIO::CompressionMode::BC7; + default: + throw RuntimeError("No corresponding compression mode for the provided ResourceFormat."); + } +} + +// Returns the corresponding NVTT compression format for the provided compression mode. +nvtt::Format convertModeToNvttFormat(ImageIO::CompressionMode mode) +{ + switch (mode) + { + case ImageIO::CompressionMode::None: + return nvtt::Format::Format_RGBA; + case ImageIO::CompressionMode::BC1: + return nvtt::Format::Format_BC1; + case ImageIO::CompressionMode::BC2: + return nvtt::Format::Format_BC2; + case ImageIO::CompressionMode::BC3: + return nvtt::Format::Format_BC3; + case ImageIO::CompressionMode::BC4: + return nvtt::Format::Format_BC4; + case ImageIO::CompressionMode::BC5: + return nvtt::Format::Format_BC5; + case ImageIO::CompressionMode::BC6: + return nvtt::Format::Format_BC6S; + case ImageIO::CompressionMode::BC7: + return nvtt::Format::Format_BC7; + default: + throw RuntimeError("Invalid compression mode."); + } +} + +// Returns the corresponding NVTT compression format for the provided ResourceFormat. This conversion function should be used to convert +// formats for compressed textures. +nvtt::Format convertFormatToNvttFormat(ResourceFormat format) +{ + switch (format) + { + case ResourceFormat::BC1Unorm: + case ResourceFormat::BC1UnormSrgb: + return nvtt::Format::Format_BC1; + case ResourceFormat::BC2Unorm: + case ResourceFormat::BC2UnormSrgb: + return nvtt::Format::Format_BC2; + case ResourceFormat::BC3Unorm: + case ResourceFormat::BC3UnormSrgb: + return nvtt::Format::Format_BC3; + case ResourceFormat::BC4Unorm: + return nvtt::Format::Format_BC4; + case ResourceFormat::BC5Snorm: + case ResourceFormat::BC5Unorm: + return nvtt::Format::Format_BC5; + case ResourceFormat::BC6HS16: + return nvtt::Format::Format_BC6S; + case ResourceFormat::BC7Unorm: + case ResourceFormat::BC7UnormSrgb: + return nvtt::Format::Format_BC7; + default: + throw RuntimeError("No corresponding NVTT compression format for the specified ResourceFormat."); + } +} + +// Returns the corresponding NVTT input format for the provided ResourceFormat. Should only be used to convert formats for non-compressed +// textures. +nvtt::InputFormat convertToNvttInputFormat(ResourceFormat format) +{ + uint32_t channelCount = getFormatChannelCount(format); + uint32_t xBits = getNumChannelBits(format, 0); + uint32_t yBits = getNumChannelBits(format, 1); + uint32_t zBits = getNumChannelBits(format, 2); + uint32_t wBits = getNumChannelBits(format, 3); + + bool isR32Float = channelCount == 1 && xBits == 32; + bool isSupportedTwoChannel = channelCount == 2 && xBits == yBits; // all RG formats + bool isSupportedThreeChannel = channelCount == 3 && xBits == yBits && yBits == zBits; // all RGB formats + bool isSupportedFourChannel = xBits == yBits && yBits == zBits && zBits == wBits; + + // These are fairly broadly sorted into the five NVTT input formats. Most resource formats will require + // modifications to the data before being passed to NVTT for exporting; this is done later on in setImage(). + if (isR32Float || isSupportedTwoChannel || isSupportedThreeChannel || isSupportedFourChannel) + { + if (isSupportedThreeChannel) { - switch (format) - { - case ResourceFormat::BC1Unorm: - case ResourceFormat::BC1UnormSrgb: - return ImageIO::CompressionMode::BC1; - case ResourceFormat::BC2Unorm: - case ResourceFormat::BC2UnormSrgb: - return ImageIO::CompressionMode::BC2; - case ResourceFormat::BC3Unorm: - case ResourceFormat::BC3UnormSrgb: - return ImageIO::CompressionMode::BC3; - case ResourceFormat::BC4Unorm: - return ImageIO::CompressionMode::BC4; - case ResourceFormat::BC5Snorm: - case ResourceFormat::BC5Unorm: - return ImageIO::CompressionMode::BC5; - case ResourceFormat::BC6HS16: - return ImageIO::CompressionMode::BC6; - case ResourceFormat::BC7Unorm: - case ResourceFormat::BC7UnormSrgb: - return ImageIO::CompressionMode::BC7; - default: - throw RuntimeError("No corresponding compression mode for the provided ResourceFormat."); - } + logWarning("NVTT is incompatible with three channel images. This image will be padded with a solid alpha channel."); } - // Returns the corresponding NVTT compression format for the provided compression mode. - nvtt::Format convertModeToNvttFormat(ImageIO::CompressionMode mode) + if (isR32Float) { - switch (mode) - { - case ImageIO::CompressionMode::None: - return nvtt::Format::Format_RGBA; - case ImageIO::CompressionMode::BC1: - return nvtt::Format::Format_BC1; - case ImageIO::CompressionMode::BC2: - return nvtt::Format::Format_BC2; - case ImageIO::CompressionMode::BC3: - return nvtt::Format::Format_BC3; - case ImageIO::CompressionMode::BC4: - return nvtt::Format::Format_BC4; - case ImageIO::CompressionMode::BC5: - return nvtt::Format::Format_BC5; - case ImageIO::CompressionMode::BC6: - return nvtt::Format::Format_BC6S; - case ImageIO::CompressionMode::BC7: - return nvtt::Format::Format_BC7; - default: - throw RuntimeError("Invalid compression mode."); - } + return nvtt::InputFormat::InputFormat_R_32F; } - // Returns the corresponding NVTT compression format for the provided ResourceFormat. This conversion function should be used to convert formats for compressed textures. - nvtt::Format convertFormatToNvttFormat(ResourceFormat format) + if (xBits == 8) { - switch (format) + if (getFormatType(format) == FormatType::Uint || getFormatType(format) == FormatType::Unorm) { - case ResourceFormat::BC1Unorm: - case ResourceFormat::BC1UnormSrgb: - return nvtt::Format::Format_BC1; - case ResourceFormat::BC2Unorm: - case ResourceFormat::BC2UnormSrgb: - return nvtt::Format::Format_BC2; - case ResourceFormat::BC3Unorm: - case ResourceFormat::BC3UnormSrgb: - return nvtt::Format::Format_BC3; - case ResourceFormat::BC4Unorm: - return nvtt::Format::Format_BC4; - case ResourceFormat::BC5Snorm: - case ResourceFormat::BC5Unorm: - return nvtt::Format::Format_BC5; - case ResourceFormat::BC6HS16: - return nvtt::Format::Format_BC6S; - case ResourceFormat::BC7Unorm: - case ResourceFormat::BC7UnormSrgb: - return nvtt::Format::Format_BC7; - default: - throw RuntimeError("No corresponding NVTT compression format for the specified ResourceFormat."); + return nvtt::InputFormat::InputFormat_BGRA_8UB; } + else + return nvtt::InputFormat::InputFormat_BGRA_8SB; } + else if (xBits == 16) + return nvtt::InputFormat::InputFormat_RGBA_16F; + else if (xBits == 32) + return nvtt::InputFormat::InputFormat_RGBA_32F; + } - // Returns the corresponding NVTT input format for the provided ResourceFormat. Should only be used to convert formats for non-compressed textures. - nvtt::InputFormat convertToNvttInputFormat(ResourceFormat format) - { - uint32_t channelCount = getFormatChannelCount(format); - uint32_t xBits = getNumChannelBits(format, 0); - uint32_t yBits = getNumChannelBits(format, 1); - uint32_t zBits = getNumChannelBits(format, 2); - uint32_t wBits = getNumChannelBits(format, 3); - - bool isR32Float = channelCount == 1 && xBits == 32; - bool isSupportedTwoChannel = channelCount == 2 && xBits == yBits; // all RG formats - bool isSupportedThreeChannel = channelCount == 3 && xBits == yBits && yBits == zBits; // all RGB formats - bool isSupportedFourChannel = xBits == yBits && yBits == zBits && zBits == wBits; - - // These are fairly broadly sorted into the five NVTT input formats. Most resource formats will require - // modifications to the data before being passed to NVTT for exporting; this is done later on in setImage(). - if (isR32Float || isSupportedTwoChannel || isSupportedThreeChannel || isSupportedFourChannel) - { - if (isSupportedThreeChannel) - { - logWarning("NVTT is incompatible with three channel images. This image will be padded with a solid alpha channel."); - } + throw RuntimeError("Image is in an unsupported ResourceFormat."); +} - if (isR32Float) - { - return nvtt::InputFormat::InputFormat_R_32F; - } +// Check if any of base image dimensions need to be clamped to a multiple of 4. +// This function should only be called if the image is being compressed and mipmaps are being automatically generated. +bool clampIfNeeded(ExportData& image) +{ + bool clamped = false; + if (image.width > 1u && image.width % 4 != 0) + { + image.width = std::max(1u, image.width - image.width % 4); + clamped = true; + } + if (image.height > 1u && image.height % 4 != 0) + { + image.height = std::max(1u, image.height - image.height % 4); + clamped = true; + } + if (image.depth > 1u && image.depth % 4 != 0) + { + image.depth = std::max(1u, image.depth - image.depth % 4); + clamped = true; + } - if (xBits == 8) - { - if (getFormatType(format) == FormatType::Uint || getFormatType(format) == FormatType::Unorm) - { - return nvtt::InputFormat::InputFormat_BGRA_8UB; - } - else return nvtt::InputFormat::InputFormat_BGRA_8SB; - } - else if (xBits == 16) return nvtt::InputFormat::InputFormat_RGBA_16F; - else if (xBits == 32) return nvtt::InputFormat::InputFormat_RGBA_32F; - } + return clamped; +} - throw RuntimeError("Image is in an unsupported ResourceFormat."); - } +// Fill the alpha channel with 1's. +void fillAlphaChannel(nvtt::Surface& image) +{ + // Create a dummy Surface and fill with 1's then copy the alpha channel. DirectXTex fills the alpha channel + // with 0's for images that do not have an alpha, but NVTT does not have an equivalent alpha-less InputFormat. + // The alpha channel must thus be manually filled with 1's otherwise the resulting image may not display + // properly. BGRX8 is a unique case that it is a four channel format with no alpha. + nvtt::Surface alpha(image); + alpha.fill(1.0, 1.0, 1.0, 1.0); + image.copyChannel(alpha, 3, 3); +} - // Check if any of base image dimensions need to be clamped to a multiple of 4. - // This function should only be called if the image is being compressed and mipmaps are being automatically generated. - bool clampIfNeeded(ExportData& image) +// Prepare the original image data for being passed to NVTT for exporting. Certain image formats will also need +// the data to be modified to include empty blue and/or solid alpha channels. This is because NVTT only supports +// five specific input formats: 8-bit unsigned BGRA, 8-bit signed BGRA, 16-bit floating point RGBA, +// 32-bit floating point RGBA, and single channel 32-bit floating point. +// +// NVTT's Surface always holds a single image's worth of UNCOMPRESSED data. Re-compression is necessary +// if image compression needs to be maintained. +template +void setImage( + const void* subresourceData, + nvtt::Surface& surface, + ExportData image, + uint32_t srcWidth, + uint32_t srcHeight, + uint32_t srcDepth +) +{ + std::vector modified; + uint32_t pixelCount = srcWidth * srcHeight * srcDepth; + uint32_t channelCount = getFormatChannelCount(image.format); + T alpha = T(0); + + // Need to flip red and blue channels for all 8 bit formats that aren't BGRA/BGRX as NVTT only supports BGRA inputs for these cases + bool reverseRB = getNumChannelBits(image.format, 0) == 8 && image.format != ResourceFormat::BGRA8Unorm && + image.format != ResourceFormat::BGRA8UnormSrgb && image.format != ResourceFormat::BGRX8Unorm && + image.format != ResourceFormat::BGRX8UnormSrgb; + // Need to fill the alpha channel with 1's for all formats that do not have an alpha channel + bool fillAlpha = channelCount == 2 || channelCount == 3 || image.format == ResourceFormat::BGRX8Unorm || + image.format == ResourceFormat::BGRX8UnormSrgb; + + modified.resize(4 * pixelCount); + + T* src = (T*)subresourceData; + T* dst = (T*)modified.data(); + for (uint32_t h = 0; h < image.height; ++h) + { + for (uint32_t w = 0; w < image.width; ++w) { - bool clamped = false; - if (image.width > 1u && image.width % 4 != 0) + uint32_t i = h * srcWidth + w; // Source data index + uint32_t j = h * image.width + w; // Destination data index - Same as source index if no clamping is involved + if (channelCount == 1) { - image.width = std::max(1u, image.width - image.width % 4); - clamped = true; + dst[j] = T(src[i]); } - if (image.height > 1u && image.height % 4 != 0) + else if (channelCount == 2) { - image.height = std::max(1u, image.height - image.height % 4); - clamped = true; + dst[4 * j] = reverseRB ? T(0) : T(src[2 * i]); + dst[4 * j + 1] = T(src[2 * i + 1]); + dst[4 * j + 2] = reverseRB ? T(src[2 * i]) : T(0); + dst[4 * j + 3] = T(0); } - if (image.depth > 1u && image.depth % 4 != 0) + else if (channelCount == 3) { - image.depth = std::max(1u, image.depth - image.depth % 4); - clamped = true; + dst[4 * j] = reverseRB ? T(src[3 * i + 2]) : T(src[3 * i]); + dst[4 * j + 1] = T(src[3 * i + 1]); + dst[4 * j + 2] = reverseRB ? T(src[3 * i]) : T(src[3 * i + 2]); + dst[4 * j + 3] = T(0); + } + else if (channelCount == 4) + { + dst[4 * j] = reverseRB ? T(src[4 * i + 2]) : T(src[4 * i]); + dst[4 * j + 1] = T(src[4 * i + 1]); + dst[4 * j + 2] = reverseRB ? T(src[4 * i]) : T(src[4 * i + 2]); + dst[4 * j + 3] = T(src[4 * i + 3]); } - - return clamped; } + } - // Fill the alpha channel with 1's. - void fillAlphaChannel(nvtt::Surface& image) + if (isCompressedFormat(image.format)) + { + nvtt::Format compressionFormat = convertFormatToNvttFormat(image.format); + if (!surface.setImage3D(compressionFormat, (int)image.width, (int)image.height, (int)image.depth, modified.data())) { - // Create a dummy Surface and fill with 1's then copy the alpha channel. DirectXTex fills the alpha channel - // with 0's for images that do not have an alpha, but NVTT does not have an equivalent alpha-less InputFormat. - // The alpha channel must thus be manually filled with 1's otherwise the resulting image may not display - // properly. BGRX8 is a unique case that it is a four channel format with no alpha. - nvtt::Surface alpha(image); - alpha.fill(1.0, 1.0, 1.0, 1.0); - image.copyChannel(alpha, 3, 3); + throw RuntimeError("Failed to set image data."); } - - // Prepare the original image data for being passed to NVTT for exporting. Certain image formats will also need - // the data to be modified to include empty blue and/or solid alpha channels. This is because NVTT only supports - // five specific input formats: 8-bit unsigned BGRA, 8-bit signed BGRA, 16-bit floating point RGBA, - // 32-bit floating point RGBA, and single channel 32-bit floating point. - // - // NVTT's Surface always holds a single image's worth of UNCOMPRESSED data. Re-compression is necessary - // if image compression needs to be maintained. - template - void setImage(const void* subresourceData, nvtt::Surface& surface, ExportData image, uint32_t srcWidth, uint32_t srcHeight, uint32_t srcDepth) + } + else + { + nvtt::InputFormat inputFormat = convertToNvttInputFormat(image.format); + if (!surface.setImage(inputFormat, (int)image.width, (int)image.height, (int)image.depth, modified.data())) { - std::vector modified; - uint32_t pixelCount = srcWidth * srcHeight * srcDepth; - uint32_t channelCount = getFormatChannelCount(image.format); - T alpha = 0; - - // Need to flip red and blue channels for all 8 bit formats that aren't BGRA/BGRX as NVTT only supports BGRA inputs for these cases - bool reverseRB = getNumChannelBits(image.format, 0) == 8 && image.format != ResourceFormat::BGRA8Unorm && image.format != ResourceFormat::BGRA8UnormSrgb - && image.format != ResourceFormat::BGRX8Unorm && image.format != ResourceFormat::BGRX8UnormSrgb; - // Need to fill the alpha channel with 1's for all formats that do not have an alpha channel - bool fillAlpha = channelCount == 2 || channelCount == 3 || image.format == ResourceFormat::BGRX8Unorm || image.format == ResourceFormat::BGRX8UnormSrgb; - - modified.resize(4 * pixelCount); - - T* src = (T*)subresourceData; - T* dst = (T*)modified.data(); - for (uint32_t h = 0; h < image.height; ++h) - { - for (uint32_t w = 0; w < image.width; ++w) - { - uint32_t i = h * srcWidth + w; // Source data index - uint32_t j = h * image.width + w; // Destination data index - Same as source index if no clamping is involved - if (channelCount == 1) - { - dst[j] = src[i]; - } - else if (channelCount == 2) - { - dst[4 * j] = reverseRB ? 0 : src[2 * i]; - dst[4 * j + 1] = src[2 * i + 1]; - dst[4 * j + 2] = reverseRB ? src[2 * i] : 0; - dst[4 * j + 3] = 0; - } - else if (channelCount == 3) - { - dst[4 * j] = reverseRB ? src[3 * i + 2] : src[3 * i]; - dst[4 * j + 1] = src[3 * i + 1]; - dst[4 * j + 2] = reverseRB ? src[3 * i] : src[3 * i + 2]; - dst[4 * j + 3] = 0; - } - else if (channelCount == 4) - { - dst[4 * j] = reverseRB ? src[4 * i + 2] : src[4 * i]; - dst[4 * j + 1] = src[4 * i + 1]; - dst[4 * j + 2] = reverseRB ? src[4 * i] : src[4 * i + 2]; - dst[4 * j + 3] = src[4 * i + 3]; - } - } - } + throw RuntimeError("Failed to set image data."); + } + } - if (isCompressedFormat(image.format)) + if (fillAlpha) + fillAlphaChannel(surface); +} + +// Saves image data to a DDS file using the specified compression mode. Optionally generates mips. +void exportDDS(const std::filesystem::path& path, ExportData& image, ImageIO::CompressionMode mode, bool generateMips) +{ + nvtt::CompressionOptions compressionOptions; + nvtt::Format format = convertModeToNvttFormat(mode); + compressionOptions.setFormat(format); + if (format == nvtt::Format::Format_RGBA && !isCompressedFormat(image.format)) + { + if (getFormatType(image.format) == FormatType::Float) + { + compressionOptions.setPixelType(nvtt::PixelType::PixelType_Float); + if (image.format == ResourceFormat::R32Float) { - nvtt::Format compressionFormat = convertFormatToNvttFormat(image.format); - if (!surface.setImage3D(compressionFormat, (int)image.width, (int)image.height, (int)image.depth, modified.data())) - { - throw RuntimeError("Failed to set image data."); - } + compressionOptions.setPixelFormat(32, 0, 0, 0); } else { - nvtt::InputFormat inputFormat = convertToNvttInputFormat(image.format); - if (!surface.setImage(inputFormat, (int)image.width, (int)image.height, (int)image.depth, modified.data())) - { - throw RuntimeError("Failed to set image data."); - } + uint32_t bits = getNumChannelBits(image.format, 0); + compressionOptions.setPixelFormat(bits, bits, bits, bits); } - - if (fillAlpha) fillAlphaChannel(surface); } + } + else if (format == nvtt::Format::Format_BC6S) + { + compressionOptions.setPixelType(nvtt::PixelType::PixelType_Float); + } + + nvtt::OutputOptions outputOptions; + std::string pathStr = path.string(); + outputOptions.setFileName(pathStr.c_str()); + if (format == nvtt::Format::Format_BC6S || format == nvtt::Format::Format_BC7) + { + outputOptions.setContainer(nvtt::Container::Container_DDS10); + } + outputOptions.setSrgbFlag(isSrgbFormat(image.format)); - // Saves image data to a DDS file using the specified compression mode. Optionally generates mips. - void exportDDS(const std::filesystem::path& path, ExportData& image, ImageIO::CompressionMode mode, bool generateMips) + nvtt::Context context; + if (!context.outputHeader( + image.type, image.width, image.height, image.depth, image.mipLevels, image.images[0].isNormalMap(), compressionOptions, + outputOptions + )) + { + throw RuntimeError("Failed to output file header."); + } + + for (uint32_t f = 0; f < image.faceCount; ++f) + { + size_t faceIndex = f * image.mipLevels; + nvtt::Surface tmp = image.images[faceIndex]; + if (!context.compress(tmp, f, 0, compressionOptions, outputOptions)) { - nvtt::CompressionOptions compressionOptions; - nvtt::Format format = convertModeToNvttFormat(mode); - compressionOptions.setFormat(format); - if (format == nvtt::Format::Format_RGBA && !isCompressedFormat(image.format)) + throw RuntimeError("Failed to compress file."); + } + for (uint32_t m = 1; m < image.mipLevels; ++m) + { + if (generateMips) { - if (getFormatType(image.format) == FormatType::Float) - { - compressionOptions.setPixelType(nvtt::PixelType::PixelType_Float); - if (image.format == ResourceFormat::R32Float) - { - compressionOptions.setPixelFormat(32, 0, 0, 0); - } - else - { - uint32_t bits = getNumChannelBits(image.format, 0); - compressionOptions.setPixelFormat(bits, bits, bits, bits); - } - } + tmp.buildNextMipmap(nvtt::MipmapFilter::MipmapFilter_Box); } - else if (format == nvtt::Format::Format_BC6S) + else { - compressionOptions.setPixelType(nvtt::PixelType::PixelType_Float); + tmp = image.images[faceIndex + m]; } - nvtt::OutputOptions outputOptions; - std::string pathStr = path.string(); - outputOptions.setFileName(pathStr.c_str()); - if (format == nvtt::Format::Format_BC6S || format == nvtt::Format::Format_BC7) + if (!context.compress(tmp, f, m, compressionOptions, outputOptions)) { - outputOptions.setContainer(nvtt::Container::Container_DDS10); + throw RuntimeError("Failed to compress file."); } - outputOptions.setSrgbFlag(isSrgbFormat(image.format)); + } + } +} - nvtt::Context context; - if (!context.outputHeader(image.type, image.width, image.height, image.depth, image.mipLevels, image.images[0].isNormalMap(), compressionOptions, outputOptions)) - { - throw RuntimeError("Failed to output file header."); - } +// Reads image information from the DDS header data contained in pHeaderData. +void readDDSHeader(ImportData& data, const void* pHeaderData, size_t& headerSize, bool loadAsSrgb) +{ + // Check magic number + auto magic = *static_cast(pHeaderData); + if (magic != DDS_MAGIC) + { + throw RuntimeError("Unexpected magic number for a DDS file."); + } - for (uint32_t f = 0; f < image.faceCount; ++f) - { - size_t faceIndex = f * image.mipLevels; - nvtt::Surface tmp = image.images[faceIndex]; - if (!context.compress(tmp, f, 0, compressionOptions, outputOptions)) - { - throw RuntimeError("Failed to compress file."); - } - for (uint32_t m = 1; m < image.mipLevels; ++m) - { - if (generateMips) - { - tmp.buildNextMipmap(nvtt::MipmapFilter::MipmapFilter_Box); - } - else - { - tmp = image.images[faceIndex + m]; - } + // Check size fields for both the DDS_HEADER and DDS_PIXELFORMAT structs + auto pHeader = reinterpret_cast(static_cast(pHeaderData) + sizeof(uint32_t)); + if (pHeader->size != sizeof(DDS_HEADER) || pHeader->ddspf.size != sizeof(DDS_PIXELFORMAT)) + { + throw RuntimeError("DDS header size mismatch."); + } - if (!context.compress(tmp, f, m, compressionOptions, outputOptions)) - { - throw RuntimeError("Failed to compress file."); - } - } - } + // Check for the presence of the extended DX10 header and fill in ImportData fields with their corresponding values + data.mipLevels = (pHeader->mipMapCount == 0) ? 1 : pHeader->mipMapCount; + auto pixelFormat = pHeader->ddspf; + auto fourCC = pixelFormat.fourCC; + if (fourCC == MAKEFOURCC('D', 'X', '1', '0')) + { + // DX10 header extension is present + data.hasDX10Header = true; + if (headerSize != sizeof(uint32_t) + sizeof(DDS_HEADER) + sizeof(DDS_HEADER_DXT10)) + { + throw RuntimeError("DX10 header extension size mismatch."); } - - // Reads image information from the DDS header data contained in pHeaderData. - void readDDSHeader(ImportData& data, const void* pHeaderData, size_t& headerSize, bool loadAsSrgb) + auto pDX10Header = + reinterpret_cast(static_cast(pHeaderData) + sizeof(uint32_t) + sizeof(DDS_HEADER)); + data.arraySize = pDX10Header->arraySize; + if (data.arraySize == 0) + { + throw RuntimeError("Array size cannot be zero."); + } + data.format = getResourceFormat(pDX10Header->dxgiFormat); + switch (pDX10Header->resourceDimension) { - // Check magic number - auto magic = *static_cast(pHeaderData); - if (magic != DDS_MAGIC) + case DDS_DIMENSION_TEXTURE1D: + data.width = pHeader->width; + data.height = 1; + data.depth = 1; + data.type = Resource::Type::Texture1D; + break; + case DDS_DIMENSION_TEXTURE2D: + data.width = pHeader->width; + data.height = pHeader->height; + data.depth = 1; + if ((pDX10Header->miscFlag & DDS_RESOURCE_MISC_TEXTURECUBE) != 0) { - throw RuntimeError("Unexpected magic number for a DDS file."); + data.type = Resource::Type::TextureCube; + data.arraySize *= 6; } - - // Check size fields for both the DDS_HEADER and DDS_PIXELFORMAT structs - auto pHeader = reinterpret_cast(static_cast(pHeaderData) + sizeof(uint32_t)); - if (pHeader->size != sizeof(DDS_HEADER) || pHeader->ddspf.size != sizeof(DDS_PIXELFORMAT)) + else { - throw RuntimeError("DDS header size mismatch."); + data.type = Resource::Type::Texture2D; } + break; + case DDS_DIMENSION_TEXTURE3D: + data.width = pHeader->width; + data.height = pHeader->height; + data.depth = pHeader->depth; + data.type = Resource::Type::Texture3D; + break; + default: + throw RuntimeError("Unsupported texture dimension."); + } + } + else + { + // DX10 header extension is not present + headerSize -= sizeof(DDS_HEADER_DXT10); + data.arraySize = 1; - // Check for the presence of the extended DX10 header and fill in ImportData fields with their corresponding values - data.mipLevels = (pHeader->mipMapCount == 0) ? 1 : pHeader->mipMapCount; - auto pixelFormat = pHeader->ddspf; - auto fourCC = pixelFormat.fourCC; - if (fourCC == MAKEFOURCC('D', 'X', '1', '0')) + if (pHeader->flags & DDS_HEADER_FLAGS_VOLUME) + { + data.width = pHeader->width; + data.height = pHeader->height; + data.depth = pHeader->depth; + data.type = Resource::Type::Texture3D; + } + else + { + data.width = pHeader->width; + data.height = pHeader->height; + data.depth = 1; + if (pHeader->caps2 & DDS_CUBEMAP) { - // DX10 header extension is present - data.hasDX10Header = true; - if (headerSize != sizeof(uint32_t) + sizeof(DDS_HEADER) + sizeof(DDS_HEADER_DXT10)) + if (!(pHeader->caps2 & DDS_CUBEMAP_ALLFACES)) { - throw RuntimeError("DX10 header extension size mismatch."); - } - auto pDX10Header = reinterpret_cast(static_cast(pHeaderData) + sizeof(uint32_t) + sizeof(DDS_HEADER)); - data.arraySize = pDX10Header->arraySize; - if (data.arraySize == 0) - { - throw RuntimeError("Array size cannot be zero."); - } - data.format = getResourceFormat(pDX10Header->dxgiFormat); - switch (pDX10Header->resourceDimension) - { - case DDS_DIMENSION_TEXTURE1D: - data.width = pHeader->width; - data.height = 1; - data.depth = 1; - data.type = Resource::Type::Texture1D; - break; - case DDS_DIMENSION_TEXTURE2D: - data.width = pHeader->width; - data.height = pHeader->height; - data.depth = 1; - if ((pDX10Header->miscFlag & DDS_RESOURCE_MISC_TEXTURECUBE) != 0) - { - data.type = Resource::Type::TextureCube; - data.arraySize *= 6; - } - else - { - data.type = Resource::Type::Texture2D; - } - break; - case DDS_DIMENSION_TEXTURE3D: - data.width = pHeader->width; - data.height = pHeader->height; - data.depth = pHeader->depth; - data.type = Resource::Type::Texture3D; - break; - default: - throw RuntimeError("Unsupported texture dimension."); + throw RuntimeError("All six faces must be defined for a legacy D3D9 DDS texture cube."); } + data.arraySize *= 6; + data.type = Resource::Type::TextureCube; } else { - // DX10 header extension is not present - headerSize -= sizeof(DDS_HEADER_DXT10); - data.arraySize = 1; + data.type = Resource::Type::Texture2D; + } + } - if (pHeader->flags & DDS_HEADER_FLAGS_VOLUME) - { - data.width = pHeader->width; - data.height = pHeader->height; - data.depth = pHeader->depth; - data.type = Resource::Type::Texture3D; - } - else - { - data.width = pHeader->width; - data.height = pHeader->height; - data.depth = 1; - if (pHeader->caps2 & DDS_CUBEMAP) - { - if (!(pHeader->caps2 & DDS_CUBEMAP_ALLFACES)) - { - throw RuntimeError("All six faces must be defined for a legacy D3D9 DDS texture cube."); - } - data.arraySize *= 6; - data.type = Resource::Type::TextureCube; - } - else - { - data.type = Resource::Type::Texture2D; - } - } + data.format = getResourceFormat(GetDXGIFormat(pixelFormat)); + } - data.format = getResourceFormat(GetDXGIFormat(pixelFormat)); - } + if (loadAsSrgb) + { + data.format = linearToSrgbFormat(data.format); + } +} - if (loadAsSrgb) - { - data.format = linearToSrgbFormat(data.format); - } - } +// Loads the information and data for the specified image. This function does not handle creation of the texture for the image. +void loadDDS(const std::filesystem::path& path, bool loadAsSrgb, ImportData& data) +{ + MemoryMappedFile file(path, MemoryMappedFile::kWholeFile, MemoryMappedFile::AccessHint::SequentialScan); + if (!file.isOpen()) + { + throw RuntimeError("Failed to open file."); + } - // Loads the information and data for the specified image. This function does not handle creation of the texture for the image. - void loadDDS(const std::filesystem::path& path, bool loadAsSrgb, ImportData& data) - { - MemoryMappedFile file(path, MemoryMappedFile::kWholeFile, MemoryMappedFile::AccessHint::SequentialScan); - if (!file.isOpen()) - { - throw RuntimeError("Failed to open file."); - } + if (file.getSize() < (sizeof(uint32_t) + sizeof(DDS_HEADER))) + { + throw RuntimeError("Failed to read DDS header (file too small)."); + } - if (file.getSize() < (sizeof(uint32_t) + sizeof(DDS_HEADER))) - { - throw RuntimeError("Failed to read DDS header (file too small)."); - } + // Read the DDS header + const size_t maxHeaderSize = sizeof(uint32_t) + sizeof(DDS_HEADER) + sizeof(DDS_HEADER_DXT10); + uint8_t header[maxHeaderSize] = {}; + size_t headerSize = maxHeaderSize; - // Read the DDS header - const size_t maxHeaderSize = sizeof(uint32_t) + sizeof(DDS_HEADER) + sizeof(DDS_HEADER_DXT10); - uint8_t header[maxHeaderSize] = {}; - size_t headerSize = maxHeaderSize; + // The actual header size may be smaller than the max size; be sure not to read past the end of the file. + std::memcpy(header, file.getData(), std::min(file.getSize(), headerSize)); + readDDSHeader(data, header, headerSize, loadAsSrgb); - // The actual header size may be smaller than the max size; be sure not to read past the end of the file. - std::memcpy(header, file.getData(), std::min(file.getSize(), headerSize)); - readDDSHeader(data, header, headerSize, loadAsSrgb); + if (file.getSize() <= headerSize) + { + throw RuntimeError("No image data after DDS header."); + } - if (file.getSize() <= headerSize) - { - throw RuntimeError("No image data after DDS header."); - } + // Read image data. + size_t imageSize = file.getSize() - headerSize; + data.imageData.resize(imageSize); + std::memcpy(data.imageData.data(), reinterpret_cast(file.getData()) + headerSize, imageSize); +} +} // namespace - // Read image data. - size_t imageSize = file.getSize() - headerSize; - data.imageData.resize(imageSize); - std::memcpy(data.imageData.data(), reinterpret_cast(file.getData()) + headerSize, imageSize); - } +Bitmap::UniqueConstPtr ImageIO::loadBitmapFromDDS(const std::filesystem::path& path) +{ + ImportData data; + try + { + loadDDS(path, false, data); + } + catch (const RuntimeError& e) + { + logWarning("Failed to load DDS image from '{}': {}", path, e.what()); + return nullptr; } - Bitmap::UniqueConstPtr ImageIO::loadBitmapFromDDS(const std::filesystem::path& path) + if (data.type == Resource::Type::Texture3D || data.type == Resource::Type::TextureCube) { - ImportData data; - try - { - loadDDS(path, false, data); - } - catch (const RuntimeError& e) - { - logWarning("Failed to load DDS image from '{}': {}", path, e.what()); - return nullptr; - } + logWarning("Failed to load DDS image from '{}': Invalid resource type {}.", path, to_string(data.type)); + return nullptr; + } - if (data.type == Resource::Type::Texture3D || data.type == Resource::Type::TextureCube) - { - logWarning("Failed to load DDS image from '{}': Invalid resource type {}.", path, to_string(data.type)); - return nullptr; - } + // Create from first image + return Bitmap::create(data.width, data.height, data.format, data.imageData.data()); +} - // Create from first image - return Bitmap::create(data.width, data.height, data.format, data.imageData.data()); +ref ImageIO::loadTextureFromDDS(ref pDevice, const std::filesystem::path& path, bool loadAsSrgb) +{ + ImportData data; + try + { + loadDDS(path, loadAsSrgb, data); + } + catch (const RuntimeError& e) + { + logWarning("Failed to load DDS image from '{}': {}", path, e.what()); + return nullptr; } - Texture::SharedPtr ImageIO::loadTextureFromDDS(Device* pDevice, const std::filesystem::path& path, bool loadAsSrgb) + ref pTex; + // TODO: Automatic mip generation + switch (data.type) { - ImportData data; - try - { - loadDDS(path, loadAsSrgb, data); - } - catch (const RuntimeError& e) - { - logWarning("Failed to load DDS image from '{}': {}", path, e.what()); - return nullptr; - } + case Resource::Type::Texture1D: + pTex = Texture::create1D(pDevice, data.width, data.format, data.arraySize, data.mipLevels, data.imageData.data()); + break; + case Resource::Type::Texture2D: + pTex = Texture::create2D(pDevice, data.width, data.height, data.format, data.arraySize, data.mipLevels, data.imageData.data()); + break; + case Resource::Type::TextureCube: + pTex = + Texture::createCube(pDevice, data.width, data.height, data.format, data.arraySize / 6, data.mipLevels, data.imageData.data()); + break; + case Resource::Type::Texture3D: + pTex = Texture::create3D(pDevice, data.width, data.height, data.depth, data.format, data.mipLevels, data.imageData.data()); + break; + default: + logWarning("Failed to load DDS image from '{}': Unrecognized texture type.", path); + return nullptr; + } - Texture::SharedPtr pTex; - // TODO: Automatic mip generation - switch (data.type) - { - case Resource::Type::Texture1D: - pTex = Texture::create1D(pDevice, data.width, data.format, data.arraySize, data.mipLevels, data.imageData.data()); - break; - case Resource::Type::Texture2D: - pTex = Texture::create2D(pDevice, data.width, data.height, data.format, data.arraySize, data.mipLevels, data.imageData.data()); - break; - case Resource::Type::TextureCube: - pTex = Texture::createCube(pDevice, data.width, data.height, data.format, data.arraySize / 6, data.mipLevels, data.imageData.data()); - break; - case Resource::Type::Texture3D: - pTex = Texture::create3D(pDevice, data.width, data.height, data.depth, data.format, data.mipLevels, data.imageData.data()); - break; - default: - logWarning("Failed to load DDS image from '{}': Unrecognized texture type.", path); - return nullptr; - } + if (pTex != nullptr) + { + pTex->setSourcePath(path); + } - if (pTex != nullptr) - { - pTex->setSourcePath(path); - } + return pTex; +} - return pTex; +void ImageIO::saveToDDS(const std::filesystem::path& path, const Bitmap& bitmap, CompressionMode mode, bool generateMips) +{ + if (!hasExtension(path, "dds")) + { + logWarning("Saving DDS image to '{}' which does not have 'dds' file extension.", path); } - void ImageIO::saveToDDS(const std::filesystem::path& path, const Bitmap& bitmap, CompressionMode mode, bool generateMips) + try { - if (!hasExtension(path, "dds")) + ExportData image; + image.type = nvtt::TextureType::TextureType_2D; + image.width = bitmap.getWidth(); + image.height = bitmap.getHeight(); + image.depth = 1; + image.format = bitmap.getFormat(); + image.faceCount = 1; + image.mipLevels = generateMips ? nvtt::countMipmaps(image.width, image.height, image.depth) : 1; + + if (getFormatChannelCount(image.format) == 2 && mode != CompressionMode::BC5) { - logWarning("Saving DDS image to '{}' which does not have 'dds' file extension.", path); + throw RuntimeError("Only BC5 compression is supported for two channel images."); } - try + // The DX spec requires the dimensions of BC encoded textures to be a multiple of 4 at the base resolution. + // If the texture has already been rescaled to meet this requirement, skip clamping. + if (generateMips && (mode != CompressionMode::None)) { - ExportData image; - image.type = nvtt::TextureType::TextureType_2D; - image.width = bitmap.getWidth(); - image.height = bitmap.getHeight(); - image.depth = 1; - image.format = bitmap.getFormat(); - image.faceCount = 1; - image.mipLevels = generateMips ? nvtt::countMipmaps(image.width, image.height, image.depth) : 1; - - if (getFormatChannelCount(image.format) == 2 && mode != CompressionMode::BC5) - { - throw RuntimeError("Only BC5 compression is supported for two channel images."); - } - - // The DX spec requires the dimensions of BC encoded textures to be a multiple of 4 at the base resolution. - // If the texture has already been rescaled to meet this requirement, skip clamping. - if (generateMips && (mode != CompressionMode::None)) + bool clamped = clampIfNeeded(image); + if (clamped) { - bool clamped = clampIfNeeded(image); - if (clamped) - { - logWarning("Saving DDS image to '{}' with clamped image dimensions to accomodate mipmaps and compression.", path); - } + logWarning("Saving DDS image to '{}' with clamped image dimensions to accomodate mipmaps and compression.", path); } + } - uint32_t srcWidth = bitmap.getWidth(); - uint32_t srcHeight = bitmap.getHeight(); + uint32_t srcWidth = bitmap.getWidth(); + uint32_t srcHeight = bitmap.getHeight(); - nvtt::Surface surface; - FormatType type = getFormatType(image.format); - if (type == FormatType::Sint || type == FormatType::Snorm) + nvtt::Surface surface; + FormatType type = getFormatType(image.format); + if (type == FormatType::Sint || type == FormatType::Snorm) + { + setImage(bitmap.getData(), surface, image, srcWidth, srcHeight, image.depth); + } + else if (type == FormatType::Uint || type == FormatType::Unorm || type == FormatType::UnormSrgb) + { + setImage(bitmap.getData(), surface, image, srcWidth, srcHeight, image.depth); + } + else if (type == FormatType::Float) + { + if (getNumChannelBits(image.format, 0) == 16) { - setImage(bitmap.getData(), surface, image, srcWidth, srcHeight, image.depth); + setImage(bitmap.getData(), surface, image, srcWidth, srcHeight, image.depth); } - else if (type == FormatType::Uint || type == FormatType::Unorm || type == FormatType::UnormSrgb) + else if (getNumChannelBits(image.format, 0) == 32) { - setImage(bitmap.getData(), surface, image, srcWidth, srcHeight, image.depth); - } - else if (type == FormatType::Float) - { - if (getNumChannelBits(image.format, 0) == 16) - { - setImage(bitmap.getData(), surface, image, srcWidth, srcHeight, image.depth); - } - else if (getNumChannelBits(image.format, 0) == 32) - { - setImage(bitmap.getData(), surface, image, srcWidth, srcHeight, image.depth); - } + setImage(bitmap.getData(), surface, image, srcWidth, srcHeight, image.depth); } + } - image.images.push_back(surface); + image.images.push_back(surface); - // NVTT's Surface is designed to only hold uncompressed data, which means saving a compressed image as-is - // requires the data be re-compressed. The selected compression mode is updated here to reflect this. - if (isCompressedFormat(image.format) && mode == CompressionMode::None) - { - mode = convertFormatToMode(image.format); - } - - exportDDS(path, image, mode, generateMips); - } - catch (const RuntimeError& e) + // NVTT's Surface is designed to only hold uncompressed data, which means saving a compressed image as-is + // requires the data be re-compressed. The selected compression mode is updated here to reflect this. + if (isCompressedFormat(image.format) && mode == CompressionMode::None) { - throw RuntimeError("Failed to save DDS image to '{}': {}", path, e.what()); + mode = convertFormatToMode(image.format); } + + exportDDS(path, image, mode, generateMips); + } + catch (const RuntimeError& e) + { + throw RuntimeError("Failed to save DDS image to '{}': {}", path, e.what()); } +} - void ImageIO::saveToDDS(CopyContext* pContext, const std::filesystem::path& path, const Texture::SharedPtr& pTexture, CompressionMode mode, bool generateMips) +void ImageIO::saveToDDS( + CopyContext* pContext, + const std::filesystem::path& path, + const ref& pTexture, + CompressionMode mode, + bool generateMips +) +{ + if (!hasExtension(path, "dds")) { - if (!hasExtension(path, "dds")) + logWarning("Saving DDS image to '{}' which does not have 'dds' file extension.", path); + } + + try + { + ExportData image; + image.width = pTexture->getWidth(); + image.height = pTexture->getHeight(); + image.depth = pTexture->getDepth(); + image.mipLevels = generateMips ? nvtt::countMipmaps(image.width, image.height, image.depth) : pTexture->getMipCount(); + image.format = pTexture->getFormat(); + + if (getFormatChannelCount(image.format) == 2 && mode != CompressionMode::BC5) { - logWarning("Saving DDS image to '{}' which does not have 'dds' file extension.", path); + throw RuntimeError("Only BC5 compression is supported for two channel images."); } - try + // The DX spec requires the dimensions of BC encoded textures to be a multiple of 4 at the base resolution. + // If the texture has already been rescaled to meet this requirement, skip clamping. + if (generateMips && (mode != CompressionMode::None)) { - ExportData image; - image.width = pTexture->getWidth(); - image.height = pTexture->getHeight(); - image.depth = pTexture->getDepth(); - image.mipLevels = generateMips ? nvtt::countMipmaps(image.width, image.height, image.depth) : pTexture->getMipCount(); - image.format = pTexture->getFormat(); - - if (getFormatChannelCount(image.format) == 2 && mode != CompressionMode::BC5) - { - throw RuntimeError("Only BC5 compression is supported for two channel images."); - } - - // The DX spec requires the dimensions of BC encoded textures to be a multiple of 4 at the base resolution. - // If the texture has already been rescaled to meet this requirement, skip clamping. - if (generateMips && (mode != CompressionMode::None)) + bool clamped = clampIfNeeded(image); + if (clamped) { - bool clamped = clampIfNeeded(image); - if (clamped) - { - logWarning("Saving DDS image to '{}' with clamped image dimensions to accomodate mipmaps and compression.", path); - } + logWarning("Saving DDS image to '{}' with clamped image dimensions to accomodate mipmaps and compression.", path); } + } - switch (pTexture->getType()) - { - case Resource::Type::Texture2D: - image.type = nvtt::TextureType::TextureType_2D; - image.faceCount = 1; - break; - case Resource::Type::Texture3D: - image.type = nvtt::TextureType::TextureType_3D; - image.faceCount = 1; - break; - case Resource::Type::TextureCube: - image.type = nvtt::TextureType::TextureType_Cube; - image.faceCount = 6; - break; - default: - throw RuntimeError("Invalid texture type. Only 2D, 3D, and Cube are currently supported."); - } + switch (pTexture->getType()) + { + case Resource::Type::Texture2D: + image.type = nvtt::TextureType::TextureType_2D; + image.faceCount = 1; + break; + case Resource::Type::Texture3D: + image.type = nvtt::TextureType::TextureType_3D; + image.faceCount = 1; + break; + case Resource::Type::TextureCube: + image.type = nvtt::TextureType::TextureType_Cube; + image.faceCount = 6; + break; + default: + throw RuntimeError("Invalid texture type. Only 2D, 3D, and Cube are currently supported."); + } - for (uint32_t f = 0; f < image.faceCount; ++f) + for (uint32_t f = 0; f < image.faceCount; ++f) + { + for (uint32_t m = 0; m < image.mipLevels; ++m) { - for (uint32_t m = 0; m < image.mipLevels; ++m) - { - uint32_t subresource = pTexture->getSubresourceIndex(f, m); - std::vector subresourceData = pContext->readTextureSubresource(pTexture.get(), subresource); + uint32_t subresource = pTexture->getSubresourceIndex(f, m); + std::vector subresourceData = pContext->readTextureSubresource(pTexture.get(), subresource); - nvtt::Surface surface; - FormatType type = getFormatType(image.format); - uint32_t width = (uint32_t)pTexture->getWidth(m); - uint32_t height = (uint32_t)pTexture->getHeight(m); - uint32_t depth = (uint32_t)pTexture->getDepth(m); + nvtt::Surface surface; + FormatType type = getFormatType(image.format); + uint32_t width = (uint32_t)pTexture->getWidth(m); + uint32_t height = (uint32_t)pTexture->getHeight(m); + uint32_t depth = (uint32_t)pTexture->getDepth(m); - if (type == FormatType::Sint || type == FormatType::Snorm) - { - setImage(subresourceData.data(), surface, image, width, height, depth); - } - else if (type == FormatType::Uint || type == FormatType::Unorm || type == FormatType::UnormSrgb) + if (type == FormatType::Sint || type == FormatType::Snorm) + { + setImage(subresourceData.data(), surface, image, width, height, depth); + } + else if (type == FormatType::Uint || type == FormatType::Unorm || type == FormatType::UnormSrgb) + { + setImage(subresourceData.data(), surface, image, width, height, depth); + } + else if (type == FormatType::Float) + { + if (getNumChannelBits(image.format, 0) == 16) { - setImage(subresourceData.data(), surface, image, width, height, depth); + setImage(subresourceData.data(), surface, image, width, height, depth); } - else if (type == FormatType::Float) + else if (getNumChannelBits(image.format, 0) == 32) { - if (getNumChannelBits(image.format, 0) == 16) - { - setImage(subresourceData.data(), surface, image, width, height, depth); - } - else if (getNumChannelBits(image.format, 0) == 32) - { - setImage(subresourceData.data(), surface, image, width, height, depth); - } + setImage(subresourceData.data(), surface, image, width, height, depth); } - - image.images.push_back(surface); - - // Only need the base image if mipmaps are being generated - if (generateMips) break; } - } - // NVTT's Surface is designed to only hold uncompressed data, which means saving a compressed image as-is - // requires the data be re-compressed. The selected compression mode is updated here to reflect this. - if (isCompressedFormat(image.format) && mode == CompressionMode::None) - { - mode = convertFormatToMode(image.format); - } + image.images.push_back(surface); - exportDDS(path, image, mode, generateMips); + // Only need the base image if mipmaps are being generated + if (generateMips) + break; + } } - catch (const RuntimeError& e) + + // NVTT's Surface is designed to only hold uncompressed data, which means saving a compressed image as-is + // requires the data be re-compressed. The selected compression mode is updated here to reflect this. + if (isCompressedFormat(image.format) && mode == CompressionMode::None) { - throw RuntimeError("Failed to save DDS image to '{}': {}", path, e.what()); + mode = convertFormatToMode(image.format); } + + exportDDS(path, image, mode, generateMips); + } + catch (const RuntimeError& e) + { + throw RuntimeError("Failed to save DDS image to '{}': {}", path, e.what()); } } +} // namespace Falcor diff --git a/Source/Falcor/Utils/Image/ImageIO.h b/Source/Falcor/Utils/Image/ImageIO.h index a03ad3968..aabc192ed 100644 --- a/Source/Falcor/Utils/Image/ImageIO.h +++ b/Source/Falcor/Utils/Image/ImageIO.h @@ -33,88 +33,96 @@ namespace Falcor { - class CopyContext; +class CopyContext; - class FALCOR_API ImageIO +class FALCOR_API ImageIO +{ +public: + enum class CompressionMode { - public: - enum class CompressionMode - { - /** Stores RGB data with 1 bit of alpha. - 8 bytes per block. - */ - BC1, - - /** Stores RGBA data. Combines BC1 for RGB with 4 bits of alpha. - 16 bytes per block. - */ - BC2, - - /** Stores RGBA data. Combines BC1 for RGB and BC4 for alpha. - 16 bytes per block. - */ - BC3, + /// Stores RGB data with 1 bit of alpha. + /// 8 bytes per block. + BC1, - /** Stores a single grayscale channel. - 8 bytes per block. - */ - BC4, + /// Stores RGBA data. Combines BC1 for RGB with 4 bits of alpha. + /// 16 bytes per block. + BC2, - /** Stores two channels using BC4 for each channel. - 16 bytes per block. - */ - BC5, + /// Stores RGBA data. Combines BC1 for RGB and BC4 for alpha. + /// 16 bytes per block. + BC3, - /** Stores RGB 16-bit floating point data. - 16 bytes per block. - */ - BC6, + /// Stores a single grayscale channel. + /// 8 bytes per block. + BC4, - /** Stores 8-bit RGB or RGBA data. - 16 bytes per block. - */ - BC7, + /// Stores two channels using BC4 for each channel. + /// 16 bytes per block. + BC5, - /** No compression mode specified. - */ - None - }; + /// Stores RGB 16-bit floating point data. + /// 16 bytes per block. + BC6, - /** Load a DDS file to a Bitmap. If the file contains an image array and/or mips, only the first image will be loaded. - Throws an exception if the DDS file is malformed. - \param[in] path Path of file to load. - \return Bitmap object containing image data if loading was successful. Otherwise, nullptr. - */ - static Bitmap::UniqueConstPtr loadBitmapFromDDS(const std::filesystem::path& path); // top down = true + /// Stores 8-bit RGB or RGBA data. + /// 16 bytes per block. + BC7, - /** Load a DDS file to a Texture. - Throws an exception if the DDS file is malformed. - \param[in] path Path of file to load. - \param[in] loadAsSrgb If true, convert the image format property to a corresponding sRGB format if available. Image data is not changed. - \return Texture object containing image data if loading was successful. Otherwise, nullptr. - */ - static Texture::SharedPtr loadTextureFromDDS(Device* pDevice, const std::filesystem::path& path, bool loadAsSrgb); + /// No compression mode specified. + None + }; - /** Saves a bitmap to a DDS file. - Throws an exception if path is invalid or the image cannot be saved. - \param[in] path Path to save to. - \param[in] bitmap Bitmap object to save. - \param[in] mode Block compression mode. By default, will save data as-is and will not decompress if already compressed. - \param[in] if true, generate and save full mipmap chain; requires the caller to have initialized COM. - */ - static void saveToDDS(const std::filesystem::path& path, const Bitmap& bitmap, CompressionMode mode = CompressionMode::None, bool generateMips = false); + /** + * Load a DDS file to a Bitmap. If the file contains an image array and/or mips, only the first image will be loaded. + * Throws an exception if the DDS file is malformed. + * @param[in] path Path of file to load. + * @return Bitmap object containing image data if loading was successful. Otherwise, nullptr. + */ + static Bitmap::UniqueConstPtr loadBitmapFromDDS(const std::filesystem::path& path); // top down = true - /** Saves a Texture to a DDS file. All mips and array images are saved. - Throws an exception if the path is invalid or the image cannot be saved. + /** + * Load a DDS file to a Texture. + * Throws an exception if the DDS file is malformed. + * @param[in] path Path of file to load. + * @param[in] loadAsSrgb If true, convert the image format property to a corresponding sRGB format if available. Image data is not + * changed. + * @return Texture object containing image data if loading was successful. Otherwise, nullptr. + */ + static ref loadTextureFromDDS(ref pDevice, const std::filesystem::path& path, bool loadAsSrgb); - TODO: Support exporting single subresource. Options for one or all are probably enough? + /** + * Saves a bitmap to a DDS file. + * Throws an exception if path is invalid or the image cannot be saved. + * @param[in] path Path to save to. + * @param[in] bitmap Bitmap object to save. + * @param[in] mode Block compression mode. By default, will save data as-is and will not decompress if already compressed. + * @param[in] if true, generate and save full mipmap chain; requires the caller to have initialized COM. + */ + static void saveToDDS( + const std::filesystem::path& path, + const Bitmap& bitmap, + CompressionMode mode = CompressionMode::None, + bool generateMips = false + ); - \param[in] pContext Copy context used to read texture data from the GPU. - \param[in] path Path to save to. - \param[in] pBitmap Bitmap object to save. - \param[in] mode Block compression mode. By default, will save data as-is and will not decompress if already compressed. - \param[in] if true, generate and save full mipmap chain; requires the caller to have initialized COM. - */ - static void saveToDDS(CopyContext* pContext, const std::filesystem::path& path, const Texture::SharedPtr& pTexture, CompressionMode mode = CompressionMode::None, bool generateMips = false); - }; -} + /** + * Saves a Texture to a DDS file. All mips and array images are saved. + * Throws an exception if the path is invalid or the image cannot be saved. + * + * TODO: Support exporting single subresource. Options for one or all are probably enough? + * + * @param[in] pContext Copy context used to read texture data from the GPU. + * @param[in] path Path to save to. + * @param[in] pBitmap Bitmap object to save. + * @param[in] mode Block compression mode. By default, will save data as-is and will not decompress if already compressed. + * @param[in] if true, generate and save full mipmap chain; requires the caller to have initialized COM. + */ + static void saveToDDS( + CopyContext* pContext, + const std::filesystem::path& path, + const ref& pTexture, + CompressionMode mode = CompressionMode::None, + bool generateMips = false + ); +}; +} // namespace Falcor diff --git a/Source/Falcor/Utils/Image/ImageProcessing.cpp b/Source/Falcor/Utils/Image/ImageProcessing.cpp index 73052161e..2f13a4774 100644 --- a/Source/Falcor/Utils/Image/ImageProcessing.cpp +++ b/Source/Falcor/Utils/Image/ImageProcessing.cpp @@ -31,66 +31,81 @@ namespace Falcor { - namespace - { - const char kCopyColorChannelShader[] = "Utils/Image/CopyColorChannel.cs.slang"; - } +namespace +{ +const char kCopyColorChannelShader[] = "Utils/Image/CopyColorChannel.cs.slang"; +} - ImageProcessing::ImageProcessing(std::shared_ptr pDevice) - : mpDevice(std::move(pDevice)) - { - } +ImageProcessing::ImageProcessing(ref pDevice) : mpDevice(pDevice) {} - void ImageProcessing::copyColorChannel(RenderContext* pRenderContext, const ShaderResourceView::SharedPtr& pSrc, const UnorderedAccessView::SharedPtr& pDst, const TextureChannelFlags srcMask) - { - // Validate arguments. - FALCOR_ASSERT(pSrc && pDst); - if (pSrc->getResource()->getType() != Resource::Type::Texture2D) throw RuntimeError("Source resource type must be Texture2D"); - if (pDst->getResource()->getType() != Resource::Type::Texture2D) throw RuntimeError("Source resource type must be Texture2D"); +void ImageProcessing::copyColorChannel( + RenderContext* pRenderContext, + const ref& pSrc, + const ref& pDst, + const TextureChannelFlags srcMask +) +{ + // Validate arguments. + FALCOR_ASSERT(pSrc && pDst); + if (pSrc->getResource()->getType() != Resource::Type::Texture2D) + throw RuntimeError("Source resource type must be Texture2D"); + if (pDst->getResource()->getType() != Resource::Type::Texture2D) + throw RuntimeError("Source resource type must be Texture2D"); - auto pSrcTex = pSrc->getResource()->asTexture(); - auto pDstTex = pDst->getResource()->asTexture(); - uint srcMip = pSrc->getViewInfo().mostDetailedMip; - uint dstMip = pDst->getViewInfo().mostDetailedMip; - uint2 srcDim = { pSrcTex->getWidth(srcMip), pSrcTex->getHeight(srcMip) }; - uint2 dstDim = { pDstTex->getWidth(dstMip), pDstTex->getHeight(dstMip) }; - bool srcIsInt = isIntegerFormat(pSrcTex->getFormat()); - bool dstIsInt = isIntegerFormat(pDstTex->getFormat()); + auto pSrcTex = pSrc->getResource()->asTexture(); + auto pDstTex = pDst->getResource()->asTexture(); + uint srcMip = pSrc->getViewInfo().mostDetailedMip; + uint dstMip = pDst->getViewInfo().mostDetailedMip; + uint2 srcDim = {pSrcTex->getWidth(srcMip), pSrcTex->getHeight(srcMip)}; + uint2 dstDim = {pDstTex->getWidth(dstMip), pDstTex->getHeight(dstMip)}; + bool srcIsInt = isIntegerFormat(pSrcTex->getFormat()); + bool dstIsInt = isIntegerFormat(pDstTex->getFormat()); - if (srcDim != dstDim) throw RuntimeError("Source and destination views must have matching dimensions"); - if (srcIsInt != dstIsInt) throw RuntimeError("Source and destination texture must have matching format type"); + if (any(srcDim != dstDim)) + throw RuntimeError("Source and destination views must have matching dimensions"); + if (srcIsInt != dstIsInt) + throw RuntimeError("Source and destination texture must have matching format type"); - uint channelIndex = 0; - switch (srcMask) - { - case TextureChannelFlags::Red: channelIndex = 0; break; - case TextureChannelFlags::Green: channelIndex = 1; break; - case TextureChannelFlags::Blue: channelIndex = 2; break; - case TextureChannelFlags::Alpha: channelIndex = 3; break; - default: throw RuntimeError("'channelMask' parameter must be a single color channel."); - } + uint channelIndex = 0; + switch (srcMask) + { + case TextureChannelFlags::Red: + channelIndex = 0; + break; + case TextureChannelFlags::Green: + channelIndex = 1; + break; + case TextureChannelFlags::Blue: + channelIndex = 2; + break; + case TextureChannelFlags::Alpha: + channelIndex = 3; + break; + default: + throw RuntimeError("'channelMask' parameter must be a single color channel."); + } - // Prepare and execute program to copy color channel. - ComputePass::SharedPtr pPass; - if (srcIsInt) - { - if (!mpCopyIntPass) - mpCopyIntPass = ComputePass::create(mpDevice, kCopyColorChannelShader, "main", { {"TEXTURE_FORMAT", "uint4"} }); - pPass = mpCopyIntPass; - } - else - { - if (!mpCopyFloatPass) - mpCopyFloatPass = ComputePass::create(mpDevice, kCopyColorChannelShader, "main", { {"TEXTURE_FORMAT", "float4"} }); - pPass = mpCopyFloatPass; - } + // Prepare and execute program to copy color channel. + ref pPass; + if (srcIsInt) + { + if (!mpCopyIntPass) + mpCopyIntPass = ComputePass::create(mpDevice, kCopyColorChannelShader, "main", {{"TEXTURE_FORMAT", "uint4"}}); + pPass = mpCopyIntPass; + } + else + { + if (!mpCopyFloatPass) + mpCopyFloatPass = ComputePass::create(mpDevice, kCopyColorChannelShader, "main", {{"TEXTURE_FORMAT", "float4"}}); + pPass = mpCopyFloatPass; + } - auto var = pPass->getRootVar(); - var["gSrc"].setSrv(pSrc); - var["gDst"].setUav(pDst); - var["CB"]["viewDim"] = srcDim; - var["CB"]["channelIndex"] = channelIndex; + auto var = pPass->getRootVar(); + var["gSrc"].setSrv(pSrc); + var["gDst"].setUav(pDst); + var["CB"]["viewDim"] = srcDim; + var["CB"]["channelIndex"] = channelIndex; - pPass->execute(pRenderContext, uint3(srcDim, 1)); - } + pPass->execute(pRenderContext, uint3(srcDim, 1)); } +} // namespace Falcor diff --git a/Source/Falcor/Utils/Image/ImageProcessing.h b/Source/Falcor/Utils/Image/ImageProcessing.h index 3a07a97ae..87d558ab8 100644 --- a/Source/Falcor/Utils/Image/ImageProcessing.h +++ b/Source/Falcor/Utils/Image/ImageProcessing.h @@ -29,35 +29,42 @@ #include "Core/Macros.h" #include "Core/API/Formats.h" #include "Core/API/ResourceViews.h" -#include "RenderGraph/BasePasses/ComputePass.h" +#include "Core/Pass/ComputePass.h" #include namespace Falcor { - class RenderContext; +class RenderContext; - /** Image processing utilities. - */ - class FALCOR_API ImageProcessing - { - public: - /// Constructor. - ImageProcessing(std::shared_ptr pDevice); +/** + * Image processing utilities. + */ +class FALCOR_API ImageProcessing +{ +public: + /// Constructor. + ImageProcessing(ref pDevice); - /** Copy single mip level and color channel from source to destination. - The views must have matching dimension and format type (float vs integer). - The source value is written to all color channels of the destination. - The function throws if the requirements are not fulfilled. - \param[in] pRenderContxt The render context. - \param[in] pSrc Resource view for source texture. - \param[in] pDst Unordered access view for destination texture. - \param[in] srcMask Mask specifying which source color channel to copy. Must be a single channel. - */ - void copyColorChannel(RenderContext* pRenderContxt, const ShaderResourceView::SharedPtr& pSrc, const UnorderedAccessView::SharedPtr& pDst, const TextureChannelFlags srcMask); + /** + * Copy single mip level and color channel from source to destination. + * The views must have matching dimension and format type (float vs integer). + * The source value is written to all color channels of the destination. + * The function throws if the requirements are not fulfilled. + * @param[in] pRenderContxt The render context. + * @param[in] pSrc Resource view for source texture. + * @param[in] pDst Unordered access view for destination texture. + * @param[in] srcMask Mask specifying which source color channel to copy. Must be a single channel. + */ + void copyColorChannel( + RenderContext* pRenderContxt, + const ref& pSrc, + const ref& pDst, + const TextureChannelFlags srcMask + ); - private: - std::shared_ptr mpDevice; - ComputePass::SharedPtr mpCopyFloatPass; - ComputePass::SharedPtr mpCopyIntPass; - }; -} +private: + ref mpDevice; + ref mpCopyFloatPass; + ref mpCopyIntPass; +}; +} // namespace Falcor diff --git a/Source/Falcor/Utils/Image/TextureAnalyzer.cpp b/Source/Falcor/Utils/Image/TextureAnalyzer.cpp index d8b551973..1505a100b 100644 --- a/Source/Falcor/Utils/Image/TextureAnalyzer.cpp +++ b/Source/Falcor/Utils/Image/TextureAnalyzer.cpp @@ -30,127 +30,132 @@ namespace Falcor { - namespace - { - // Verify channel flags use same bit pattern as the shader. - static_assert((uint32_t)TextureChannelFlags::Red == 0x1); - static_assert((uint32_t)TextureChannelFlags::Green == 0x2); - static_assert((uint32_t)TextureChannelFlags::Blue == 0x4); - static_assert((uint32_t)TextureChannelFlags::Alpha == 0x8); +namespace +{ +// Verify channel flags use same bit pattern as the shader. +static_assert((uint32_t)TextureChannelFlags::Red == 0x1); +static_assert((uint32_t)TextureChannelFlags::Green == 0x2); +static_assert((uint32_t)TextureChannelFlags::Blue == 0x4); +static_assert((uint32_t)TextureChannelFlags::Alpha == 0x8); - const char kShaderFilename[] = "Utils/Image/TextureAnalyzer.cs.slang"; - } +const char kShaderFilename[] = "Utils/Image/TextureAnalyzer.cs.slang"; +} // namespace - // Verify that the result struct matches the size expected by the shader. - static_assert(sizeof(TextureAnalyzer::Result) == 64, "TextureAnalyzer::Result struct size mismatch"); +// Verify that the result struct matches the size expected by the shader. +static_assert(sizeof(TextureAnalyzer::Result) == 64, "TextureAnalyzer::Result struct size mismatch"); - size_t TextureAnalyzer::getResultSize() { return sizeof(TextureAnalyzer::Result); } +size_t TextureAnalyzer::getResultSize() +{ + return sizeof(TextureAnalyzer::Result); +} - TextureAnalyzer::SharedPtr TextureAnalyzer::create(std::shared_ptr pDevice) - { - return SharedPtr(new TextureAnalyzer(std::move(pDevice))); - } +TextureAnalyzer::TextureAnalyzer(ref pDevice) : mpDevice(pDevice) +{ + mpClearPass = ComputePass::create(mpDevice, kShaderFilename, "clear"); + mpAnalyzePass = ComputePass::create(mpDevice, kShaderFilename, "analyze"); +} - TextureAnalyzer::TextureAnalyzer(std::shared_ptr pDevice) - : mpDevice(std::move(pDevice)) - { - mpClearPass = ComputePass::create(mpDevice, kShaderFilename, "clear"); - mpAnalyzePass = ComputePass::create(mpDevice, kShaderFilename, "analyze"); - } +void TextureAnalyzer::analyze( + RenderContext* pRenderContext, + const ref pInput, + uint32_t mipLevel, + uint32_t arraySlice, + ref pResult, + uint64_t resultOffset, + bool clearResult +) +{ + FALCOR_ASSERT(pRenderContext && pInput); + FALCOR_ASSERT(pResult && resultOffset + getResultSize() <= pResult->getSize()); + FALCOR_ASSERT(resultOffset < std::numeric_limits::max()); + + checkFormatSupport(pInput, mipLevel, arraySlice); - void TextureAnalyzer::analyze(RenderContext* pRenderContext, const Texture::SharedPtr pInput, uint32_t mipLevel, uint32_t arraySlice, Buffer::SharedPtr pResult, uint64_t resultOffset, bool clearResult) + if (clearResult) { - FALCOR_ASSERT(pRenderContext && pInput); - FALCOR_ASSERT(pResult && resultOffset + getResultSize() <= pResult->getSize()); - FALCOR_ASSERT(resultOffset < std::numeric_limits::max()); + clear(pRenderContext, pResult, resultOffset, 1); + } - checkFormatSupport(pInput, mipLevel, arraySlice); + auto pSRV = pInput->getSRV(mipLevel, 1, arraySlice, 1); + FALCOR_ASSERT(pSRV); - if (clearResult) - { - clear(pRenderContext, pResult, resultOffset, 1); - } + uint2 dim = {pInput->getWidth(mipLevel), pInput->getHeight(mipLevel)}; + FALCOR_ASSERT(dim.x > 0 && dim.y > 0); - auto pSRV = pInput->getSRV(mipLevel, 1, arraySlice, 1); - FALCOR_ASSERT(pSRV); + // Bind resources. + auto var = mpAnalyzePass->getRootVar()["gTextureAnalyzer"]; + var["input"].setSrv(pSRV); + var["result"] = pResult; + var["resultOffset"] = (uint32_t)resultOffset; + var["inputDim"] = dim; - uint2 dim = { pInput->getWidth(mipLevel), pInput->getHeight(mipLevel) }; - FALCOR_ASSERT(dim.x > 0 && dim.y > 0); + mpAnalyzePass->execute(pRenderContext, uint3(dim, 1)); +} - // Bind resources. - auto var = mpAnalyzePass->getRootVar()["gTextureAnalyzer"]; - var["input"].setSrv(pSRV); - var["result"] = pResult; - var["resultOffset"] = (uint32_t)resultOffset; - var["inputDim"] = dim; +void TextureAnalyzer::analyze(RenderContext* pRenderContext, const std::vector>& inputs, ref pResult, bool clearResult) +{ + FALCOR_ASSERT(pRenderContext && !inputs.empty()); + FALCOR_ASSERT(pResult && inputs.size() * getResultSize() <= pResult->getSize()); - mpAnalyzePass->execute(pRenderContext, uint3(dim, 1)); + if (clearResult) + { + clear(pRenderContext, pResult, 0, inputs.size()); } - void TextureAnalyzer::analyze(RenderContext* pRenderContext, const std::vector& inputs, Buffer::SharedPtr pResult, bool clearResult) + // Iterate over the textures to analyze them one by one. + // Note that Falcor inserts a UAV barrier between each dispatch. This is unnecessary as the writes are non-overlapping. + // TODO: Update this code when there is a an interface for disabling UAV barriers. + for (size_t i = 0; i < inputs.size(); i++) { - FALCOR_ASSERT(pRenderContext && !inputs.empty()); - FALCOR_ASSERT(pResult && inputs.size() * getResultSize() <= pResult->getSize()); - - if (clearResult) - { - clear(pRenderContext, pResult, 0, inputs.size()); - } - - // Iterate over the textures to analyze them one by one. - // Note that Falcor inserts a UAV barrier between each dispatch. This is unnecessary as the writes are non-overlapping. - // TODO: Update this code when there is a an interface for disabling UAV barriers. - for (size_t i = 0; i < inputs.size(); i++) - { - analyze(pRenderContext, inputs[i], 0, 0, pResult, i * getResultSize(), false); - } + analyze(pRenderContext, inputs[i], 0, 0, pResult, i * getResultSize(), false); } +} - void TextureAnalyzer::clear(RenderContext* pRenderContext, Buffer::SharedPtr pResult, uint64_t resultOffset, size_t resultCount) const - { - FALCOR_ASSERT(pRenderContext); - FALCOR_ASSERT(pResult && resultOffset + resultCount * getResultSize() <= pResult->getSize()); - FALCOR_ASSERT(resultCount > 0 && resultOffset < std::numeric_limits::max()); +void TextureAnalyzer::clear(RenderContext* pRenderContext, ref pResult, uint64_t resultOffset, size_t resultCount) const +{ + FALCOR_ASSERT(pRenderContext); + FALCOR_ASSERT(pResult && resultOffset + resultCount * getResultSize() <= pResult->getSize()); + FALCOR_ASSERT(resultCount > 0 && resultOffset < std::numeric_limits::max()); + + // Bind resources. + auto var = mpClearPass->getRootVar()["gTextureAnalyzer"]; + var["result"] = pResult; + var["resultOffset"] = (uint32_t)resultOffset; + var["inputDim"] = uint2((uint32_t)resultCount, 1); - // Bind resources. - auto var = mpClearPass->getRootVar()["gTextureAnalyzer"]; - var["result"] = pResult; - var["resultOffset"] = (uint32_t)resultOffset; - var["inputDim"] = uint2((uint32_t)resultCount, 1); + mpClearPass->execute(pRenderContext, uint3(resultCount, 1, 1)); +} - mpClearPass->execute(pRenderContext, uint3(resultCount, 1, 1)); +void TextureAnalyzer::checkFormatSupport(const ref pInput, uint32_t mipLevel, uint32_t arraySlice) const +{ + // Validate that input is supported. + if (pInput->getDepth() > 1) + { + throw RuntimeError("3D textures are not supported"); + } + if (mipLevel >= pInput->getMipCount() || arraySlice >= pInput->getArraySize()) + { + throw RuntimeError("Mip level and/or array slice is out of range"); + } + if (pInput->getSampleCount() != 1) + { + throw RuntimeError("Multi-sampled textures are not supported"); } - void TextureAnalyzer::checkFormatSupport(const Texture::SharedPtr pInput, uint32_t mipLevel, uint32_t arraySlice) const + auto format = pInput->getFormat(); + switch (getFormatType(format)) { - // Validate that input is supported. - if (pInput->getDepth() > 1) - { - throw RuntimeError("3D textures are not supported"); - } - if (mipLevel >= pInput->getMipCount() || arraySlice >= pInput->getArraySize()) - { - throw RuntimeError("Mip level and/or array slice is out of range"); - } - if (pInput->getSampleCount() != 1) - { - throw RuntimeError("Multi-sampled textures are not supported"); - } - - auto format = pInput->getFormat(); - switch (getFormatType(format)) - { - case FormatType::Float: - case FormatType::Snorm: - case FormatType::Unorm: - case FormatType::UnormSrgb: - break; - case FormatType::Sint: - case FormatType::Uint: - throw RuntimeError("Format {} is not supported", to_string(format)); - default: - FALCOR_ASSERT(false); - throw RuntimeError("Unknown format type"); - } + case FormatType::Float: + case FormatType::Snorm: + case FormatType::Unorm: + case FormatType::UnormSrgb: + break; + case FormatType::Sint: + case FormatType::Uint: + throw RuntimeError("Format {} is not supported", to_string(format)); + default: + FALCOR_ASSERT(false); + throw RuntimeError("Unknown format type"); } } +} // namespace Falcor diff --git a/Source/Falcor/Utils/Image/TextureAnalyzer.cs.slang b/Source/Falcor/Utils/Image/TextureAnalyzer.cs.slang index 9cebb8524..0004127b8 100644 --- a/Source/Falcor/Utils/Image/TextureAnalyzer.cs.slang +++ b/Source/Falcor/Utils/Image/TextureAnalyzer.cs.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-21, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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,13 +36,14 @@ struct TextureAnalyzer void clear(const uint index) { - if (index >= inputDim.x) return; + if (index >= inputDim.x) + return; uint offset = resultOffset + index * 64; - result.Store4(offset, uint4(0)); // mask - result.Store4(offset + 16, asuint(float4(0))); // value - result.Store4(offset + 32, asuint(float4(FLT_MAX))); // minValue - result.Store4(offset + 48, asuint(float4(0))); // maxValue + result.Store4(offset, uint4(0)); // mask + result.Store4(offset + 16, asuint(float4(0))); // value + result.Store4(offset + 32, asuint(float4(FLT_MAX))); // minValue + result.Store4(offset + 48, asuint(float4(0))); // maxValue } void analyze(const uint2 texel) @@ -76,10 +77,14 @@ struct TextureAnalyzer uint range = 0; for (int i = 3; i >= 0; i--) { - if (val[i] > 0.f) range |= 1; - if (val[i] < 0.f) range |= 2; - if (isinf(val[i])) range |= 4; - if (isnan(val[i])) range |= 8; + if (val[i] > 0.f) + range |= 1; + if (val[i] < 0.f) + range |= 2; + if (isinf(val[i])) + range |= 4; + if (isnan(val[i])) + range |= 8; range <<= 4; } @@ -91,7 +96,8 @@ struct TextureAnalyzer valid = true; } - if (WaveActiveAllTrue(!valid)) return; + if (WaveActiveAllTrue(!valid)) + return; // Reduce the result across the warp. // These instructions are purposely placed outside of control flow. @@ -102,7 +108,8 @@ struct TextureAnalyzer // Let the first lane write out result atomically. if (WaveIsFirstLane()) { - if (mask != 0) result.InterlockedOr(resultOffset, mask); + if (mask != 0) + result.InterlockedOr(resultOffset, mask); // Clamp to zero since we're using integer atomics. minVal = max(minVal, float4(0)); @@ -124,13 +131,13 @@ struct TextureAnalyzer ConstantBuffer gTextureAnalyzer; [numthreads(256, 1, 1)] -void clear(uint3 dispatchThreadId : SV_DispatchThreadID) +void clear(uint3 dispatchThreadId: SV_DispatchThreadID) { gTextureAnalyzer.clear(dispatchThreadId.x); } [numthreads(16, 16, 1)] -void analyze(uint3 dispatchThreadId : SV_DispatchThreadID) +void analyze(uint3 dispatchThreadId: SV_DispatchThreadID) { gTextureAnalyzer.analyze(dispatchThreadId.xy); } diff --git a/Source/Falcor/Utils/Image/TextureAnalyzer.h b/Source/Falcor/Utils/Image/TextureAnalyzer.h index 0bf11fd12..ef2caec97 100644 --- a/Source/Falcor/Utils/Image/TextureAnalyzer.h +++ b/Source/Falcor/Utils/Image/TextureAnalyzer.h @@ -30,117 +30,128 @@ #include "Core/API/Formats.h" #include "Core/API/Buffer.h" #include "Core/API/Texture.h" -#include "RenderGraph/BasePasses/ComputePass.h" +#include "Core/Pass/ComputePass.h" #include "Utils/Math/Vector.h" #include #include namespace Falcor { - class RenderContext; +class RenderContext; - /** A class for analyzing texture contents. - */ - class FALCOR_API TextureAnalyzer +/** + * A class for analyzing texture contents. + */ +class FALCOR_API TextureAnalyzer +{ +public: + /// Texture analysis result. + struct Result { - public: - using SharedPtr = std::shared_ptr; - - /** Texture analysis result. - */ - struct Result - { - uint32_t mask; ///< Bits 0-3 indicate which color channels (RGBA) are varying (0 = constant, 1 = varying in i:th bit). - ///< Bits 4-19 indicate numerical range of texture (4 bits per channel). Bits 20-31 are reserved. - uint32_t reserved[3]; ///< Reserved bits. + uint32_t mask; ///< Bits 0-3 indicate which color channels (RGBA) are varying (0 = constant, 1 = varying in i:th bit). + ///< Bits 4-19 indicate numerical range of texture (4 bits per channel). Bits 20-31 are reserved. + uint32_t reserved[3]; ///< Reserved bits. - float4 value; ///< The constant color value in RGBA fp32 format. Only valid for channels that are identified as constant. - float4 minValue; ///< The minimum color value in RGBA fp32 format. NOTE: Clamped to zero. - float4 maxValue; ///< The maximum color value in RGBA fp32 format. NOTE: Clamped to zero. + float4 value; ///< The constant color value in RGBA fp32 format. Only valid for channels that are identified as constant. + float4 minValue; ///< The minimum color value in RGBA fp32 format. NOTE: Clamped to zero. + float4 maxValue; ///< The maximum color value in RGBA fp32 format. NOTE: Clamped to zero. - enum class RangeFlags : uint32_t - { - Pos = 0x1, ///< Texture channel has positive values > 0; - Neg = 0x2, ///< Texture channel has negative values < 0; - Inf = 0x4, ///< Texture channel has +/-inf values. - NaN = 0x8, ///< Texture channel has NaN values. - }; + enum class RangeFlags : uint32_t + { + Pos = 0x1, ///< Texture channel has positive values > 0; + Neg = 0x2, ///< Texture channel has negative values < 0; + Inf = 0x4, ///< Texture channel has +/-inf values. + NaN = 0x8, ///< Texture channel has NaN values. + }; - bool isConstant(uint32_t channelMask) const { return (mask & channelMask) == 0; } - bool isConstant(TextureChannelFlags channelMask) const { return isConstant((uint32_t)channelMask); } + bool isConstant(uint32_t channelMask) const { return (mask & channelMask) == 0; } + bool isConstant(TextureChannelFlags channelMask) const { return isConstant((uint32_t)channelMask); } - bool isPos(TextureChannelFlags channelMask) const { return getRange(channelMask) & (uint32_t)RangeFlags::Pos; } - bool isNeg(TextureChannelFlags channelMask) const { return getRange(channelMask) & (uint32_t)RangeFlags::Neg; } - bool isInf(TextureChannelFlags channelMask) const { return getRange(channelMask) & (uint32_t)RangeFlags::Inf; } - bool isNaN(TextureChannelFlags channelMask) const { return getRange(channelMask) & (uint32_t)RangeFlags::NaN; } + bool isPos(TextureChannelFlags channelMask) const { return getRange(channelMask) & (uint32_t)RangeFlags::Pos; } + bool isNeg(TextureChannelFlags channelMask) const { return getRange(channelMask) & (uint32_t)RangeFlags::Neg; } + bool isInf(TextureChannelFlags channelMask) const { return getRange(channelMask) & (uint32_t)RangeFlags::Inf; } + bool isNaN(TextureChannelFlags channelMask) const { return getRange(channelMask) & (uint32_t)RangeFlags::NaN; } - /** Returns the numerical range of texels in the given color channels. - \param[in] channelMask Which color channels to look at. - \return Union of 'RangeFlags' flags (0 = no texels, 1 = at least one texel). - */ - uint32_t getRange(TextureChannelFlags channelMask) const + /** + * Returns the numerical range of texels in the given color channels. + * @param[in] channelMask Which color channels to look at. + * @return Union of 'RangeFlags' flags (0 = no texels, 1 = at least one texel). + */ + uint32_t getRange(TextureChannelFlags channelMask) const + { + uint32_t range = 0; + for (int i = 0; i < 4; i++) { - uint32_t range = 0; - for (int i = 0; i < 4; i++) + if ((uint32_t)channelMask & (1 << i)) { - if ((uint32_t)channelMask & (1 << i)) - { - range |= mask >> (4 + 4 * i); - } + range |= mask >> (4 + 4 * i); } - return range & 0xf; } - }; - - /** Create a new texture analyzer. - \return A new object, or throws an exception if creation failed. - */ - static SharedPtr create(std::shared_ptr pDevice); - - /** Analyze 2D texture to check if it has a constant color. - Throws an exception if the input texture is of unsupported format or dimension. - The result is written in the format of the 'Result' struct (64B total). - \param[in] pRenderContext The context. - \param[in] pInput Input texture. This has to be a 2D non-MSAA texture of floating-point format. - \param[in] mipLevel The mip level. - \param[in] arraySlice The array slice. - \param[in] pResult GPU buffer to where the result is written. This is expected to have UAV bind flag and be cleared to zero before the call. - \param[in] resultOffset Offset into result buffer to where the result is written. - \param[in] clearResult Flag indicating whether the function should clear the result buffer first. - */ - void analyze(RenderContext* pRenderContext, const Texture::SharedPtr pInput, uint32_t mipLevel, uint32_t arraySlice, Buffer::SharedPtr pResult, uint64_t resultOffset = 0, bool clearResult = true); - - /** Batch analysis of a set of 2D textures. This is more efficient than calling analyze() repeatedly. - The first mip level and array slice of each texture is used. - Throws an exception if any input texture is of unsupported format or dimension. - The result is written as an arary of 'Result' structs (64B each). - \param[in] pRenderContext The context. - \param[in] inputs Array of input textures. These have to be 2D non-MSAA textures of floating-point formats. - \param[in] pResult GPU buffer to where the result is written. This is expected to have UAV bind flag. - \param[in] clearResult Flag indicating whether the function should clear the result buffer first. - */ - void analyze(RenderContext* pRenderContext, const std::vector& inputs, Buffer::SharedPtr pResult, bool clearResult = true); - - /** Helper function to clear the results buffer. - \param[in] pRenderContext The context. - \param[in] pResult GPU buffer to clear. This is expected to have UAV bind flag. - \param[in] resultOffset Offset into result buffer to where the first result is stored. - \param[in] resultsCount Number of result structs. - */ - void clear(RenderContext* pRenderContext, Buffer::SharedPtr pResult, uint64_t resultOffset, size_t resultCount) const; - - /** Returns the size of the generated result for one texture in bytes. - */ - static size_t getResultSize(); - - private: - TextureAnalyzer(std::shared_ptr pDevice); - void checkFormatSupport(const Texture::SharedPtr pInput, uint32_t mipLevel, uint32_t arraySlice) const; - - std::shared_ptr mpDevice; - ComputePass::SharedPtr mpClearPass; - ComputePass::SharedPtr mpAnalyzePass; + return range & 0xf; + } }; - FALCOR_ENUM_CLASS_OPERATORS(TextureAnalyzer::Result::RangeFlags); -} + /** + * Constructor. Throws an exception if creation failed. + */ + TextureAnalyzer(ref pDevice); + + /** + * Analyze 2D texture to check if it has a constant color. + * Throws an exception if the input texture is of unsupported format or dimension. + * The result is written in the format of the 'Result' struct (64B total). + * @param[in] pRenderContext The context. + * @param[in] pInput Input texture. This has to be a 2D non-MSAA texture of floating-point format. + * @param[in] mipLevel The mip level. + * @param[in] arraySlice The array slice. + * @param[in] pResult GPU buffer to where the result is written. This is expected to have UAV bind flag and be cleared to zero before + * the call. + * @param[in] resultOffset Offset into result buffer to where the result is written. + * @param[in] clearResult Flag indicating whether the function should clear the result buffer first. + */ + void analyze( + RenderContext* pRenderContext, + const ref pInput, + uint32_t mipLevel, + uint32_t arraySlice, + ref pResult, + uint64_t resultOffset = 0, + bool clearResult = true + ); + + /** + * Batch analysis of a set of 2D textures. This is more efficient than calling analyze() repeatedly. + * The first mip level and array slice of each texture is used. + * Throws an exception if any input texture is of unsupported format or dimension. + * The result is written as an arary of 'Result' structs (64B each). + * @param[in] pRenderContext The context. + * @param[in] inputs Array of input textures. These have to be 2D non-MSAA textures of floating-point formats. + * @param[in] pResult GPU buffer to where the result is written. This is expected to have UAV bind flag. + * @param[in] clearResult Flag indicating whether the function should clear the result buffer first. + */ + void analyze(RenderContext* pRenderContext, const std::vector>& inputs, ref pResult, bool clearResult = true); + + /** + * Helper function to clear the results buffer. + * @param[in] pRenderContext The context. + * @param[in] pResult GPU buffer to clear. This is expected to have UAV bind flag. + * @param[in] resultOffset Offset into result buffer to where the first result is stored. + * @param[in] resultsCount Number of result structs. + */ + void clear(RenderContext* pRenderContext, ref pResult, uint64_t resultOffset, size_t resultCount) const; + + /** + * Returns the size of the generated result for one texture in bytes. + */ + static size_t getResultSize(); + +private: + void checkFormatSupport(const ref pInput, uint32_t mipLevel, uint32_t arraySlice) const; + + ref mpDevice; + ref mpClearPass; + ref mpAnalyzePass; +}; + +FALCOR_ENUM_CLASS_OPERATORS(TextureAnalyzer::Result::RangeFlags); +} // namespace Falcor diff --git a/Source/Falcor/Utils/Image/TextureManager.cpp b/Source/Falcor/Utils/Image/TextureManager.cpp index cd1d3a2ab..75c3f4b66 100644 --- a/Source/Falcor/Utils/Image/TextureManager.cpp +++ b/Source/Falcor/Utils/Image/TextureManager.cpp @@ -38,529 +38,617 @@ namespace Falcor { - namespace - { - const size_t kMaxTextureHandleCount = std::numeric_limits::max(); - static_assert(TextureManager::TextureHandle::kInvalidID >= kMaxTextureHandleCount); - } +namespace +{ +const size_t kMaxTextureHandleCount = std::numeric_limits::max(); +static_assert(TextureManager::TextureHandle::kInvalidID >= kMaxTextureHandleCount); +} // namespace - TextureManager::SharedPtr TextureManager::create(std::shared_ptr pDevice, size_t maxTextureCount, size_t threadCount) - { - return SharedPtr(new TextureManager(pDevice, maxTextureCount, threadCount)); - } +TextureManager::TextureManager(ref pDevice, size_t maxTextureCount, size_t threadCount) + : mpDevice(pDevice), mAsyncTextureLoader(pDevice, threadCount), mMaxTextureCount(std::min(maxTextureCount, kMaxTextureHandleCount)) +{} + +TextureManager::~TextureManager() {} - TextureManager::TextureManager(std::shared_ptr pDevice, size_t maxTextureCount, size_t threadCount) - : mpDevice(pDevice) - , mAsyncTextureLoader(pDevice, threadCount) - , mMaxTextureCount(std::min(maxTextureCount, kMaxTextureHandleCount)) +TextureManager::TextureHandle TextureManager::addTexture(const ref& pTexture) +{ + FALCOR_ASSERT(pTexture); + if (pTexture->getType() != Resource::Type::Texture2D || pTexture->getSampleCount() != 1) { + throw ArgumentError("Only single-sample 2D textures can be added"); } - TextureManager::~TextureManager() + std::unique_lock lock(mMutex); + TextureHandle handle; + + if (auto it = mTextureToHandle.find(pTexture.get()); it != mTextureToHandle.end()) { + // Texture is already managed. Return its handle. + handle = it->second; } - - TextureManager::TextureHandle TextureManager::addTexture(const Texture::SharedPtr& pTexture) + else { - FALCOR_ASSERT(pTexture); - if (pTexture->getType() != Resource::Type::Texture2D || pTexture->getSampleCount() != 1) - { - throw ArgumentError("Only single-sample 2D textures can be added"); - } + // Texture is not already managed. Add new texture desc. + TextureDesc desc = {TextureState::Loaded, pTexture}; + handle = addDesc(desc); - std::unique_lock lock(mMutex); - TextureHandle handle; + // Add to texture-to-handle map. + mTextureToHandle[pTexture.get()] = handle; - if (auto it = mTextureToHandle.find(pTexture.get()); it != mTextureToHandle.end()) - { - // Texture is already managed. Return its handle. - handle = it->second; - } - else + // If texture was originally loaded from disk, add to key-to-handle map to avoid loading it again later if requested in + // loadTexture(). It's possible the user-provided texture has already been loaded by us. In that case, log a warning as the + // redundant load should be fixed. + if (!pTexture->getSourcePath().empty()) { - // Texture is not already managed. Add new texture desc. - TextureDesc desc = { TextureState::Loaded, pTexture }; - handle = addDesc(desc); - - // Add to texture-to-handle map. - mTextureToHandle[pTexture.get()] = handle; + bool hasMips = pTexture->getMipCount() > 1; + bool isSrgb = isSrgbFormat(pTexture->getFormat()); + TextureKey textureKey({pTexture->getSourcePath().string()}, hasMips, isSrgb, pTexture->getBindFlags()); - // If texture was originally loaded from disk, add to key-to-handle map to avoid loading it again later if requested in loadTexture(). - // It's possible the user-provided texture has already been loaded by us. In that case, log a warning as the redundant load should be fixed. - if (!pTexture->getSourcePath().empty()) + if (mKeyToHandle.find(textureKey) == mKeyToHandle.end()) { - bool hasMips = pTexture->getMipCount() > 1; - bool isSrgb = isSrgbFormat(pTexture->getFormat()); - TextureKey textureKey(pTexture->getSourcePath().string(), hasMips, isSrgb, pTexture->getBindFlags()); - - if (mKeyToHandle.find(textureKey) == mKeyToHandle.end()) - { - mKeyToHandle[textureKey] = handle; - } - else - { - logWarning("TextureManager::addTexture() - Texture loaded from '{}' appears to be identical to an already loaded texture. This could be optimized by getting it from TextureManager.", pTexture->getSourcePath()); - } + mKeyToHandle[textureKey] = handle; + } + else + { + logWarning( + "TextureManager::addTexture() - Texture loaded from '{}' appears to be identical to an already loaded texture. This " + "could be optimized by getting it from TextureManager.", + pTexture->getSourcePath() + ); } } - - return handle; } - TextureManager::TextureHandle TextureManager::loadUdimTexture(const std::filesystem::path& path, bool generateMipLevels, bool loadAsSRGB, Resource::BindFlags bindFlags, bool async, const SearchDirectories* searchDirectories, size_t* loadedTextureCount) - { - std::string filename = path.filename().string(); - auto pos = filename.find(""); - if (pos == std::string::npos) - return loadTexture(path, generateMipLevels, loadAsSRGB, bindFlags, async); - - std::filesystem::path dirpath = path.parent_path(); - filename.replace(pos, 6, "[1-9][0-9][0-9][0-9]"); - std::regex udimRegex(filename); - std::vector texturePaths; - // Find the first directory containing the pattern, in case the UDIM set lives in multiple available directories - if (searchDirectories) - texturePaths = globFilesInDirectories(dirpath, udimRegex, *searchDirectories, true /* firstHitOnly */); - else - texturePaths = globFilesInDataDirectories(dirpath, udimRegex, true /* firstHitOnly */); - - // nothing found, return an invalid handle - if (texturePaths.empty()) - { - logWarning("Can't find UDIM texture files '{}'.", path); - return TextureHandle(); - } - - // Now load all the files from that directory - std::filesystem::path loadedDir = texturePaths[0].parent_path(); - texturePaths = globFilesInDirectory(loadedDir, udimRegex); - - if (loadedTextureCount) - *loadedTextureCount = texturePaths.size(); + return handle; +} - std::vector udimIndices; - std::vector handles; - size_t maxIndex = 0; - for (auto& it : texturePaths) - { - std::string filename = it.filename().string(); - std::string udimStr = filename.substr(pos, 4); // the 4 digits; - size_t udim = std::stol(udimStr); - maxIndex = std::max(maxIndex, udim); - udimIndices.push_back(udim); - handles.push_back(loadTexture(it, generateMipLevels, loadAsSRGB, bindFlags, async)); - - FALCOR_CHECK_ARG_GE_MSG(udim, 1001, "Texture {} is not a valid UDIM texture, as it violates the valid UDIM range of 1001-9999", it); - } +TextureManager::TextureHandle TextureManager::loadUdimTexture( + const std::filesystem::path& path, + bool generateMipLevels, + bool loadAsSRGB, + Resource::BindFlags bindFlags, + bool async, + const SearchDirectories* searchDirectories, + size_t* loadedTextureCount +) +{ + std::string filename = path.filename().string(); + + // Search UDIMS based on mip0 + auto mipPos = filename.find(""); + if (mipPos != std::string::npos) + filename.replace(mipPos, 5, "mip0"); + + auto pos = filename.find(""); + if (pos == std::string::npos) + return loadTexture(path, generateMipLevels, loadAsSRGB, bindFlags, async); + + std::filesystem::path dirpath = path.parent_path(); + filename.replace(pos, 6, "[1-9][0-9][0-9][0-9]"); + std::regex udimRegex(filename); + std::vector texturePaths; + // Find the first directory containing the pattern, in case the UDIM set lives in multiple available directories + if (searchDirectories) + texturePaths = globFilesInDirectories(dirpath, udimRegex, *searchDirectories, true /* firstHitOnly */); + else + texturePaths = globFilesInDataDirectories(dirpath, udimRegex, true /* firstHitOnly */); + + // nothing found, return an invalid handle + if (texturePaths.empty()) + { + logWarning("Can't find UDIM texture files '{}'.", path); + return TextureHandle(); + } - // UDIM range needs to cover all numbers from 1001 to maxIndex inclusive, so 1001, 1002, 1003 needs 3 indices - size_t rangeStart = getUdimRange(maxIndex - 1001 + 1); + // Now load all the files from that directory + std::filesystem::path loadedDir = texturePaths[0].parent_path(); + texturePaths = globFilesInDirectory(loadedDir, udimRegex); - for (size_t i = 0; i < texturePaths.size(); ++i) - { - size_t index = udimIndices[i] - 1001; - mUdimIndirection[rangeStart + index] = handles[i].getID(); - } + if (loadedTextureCount) + *loadedTextureCount = texturePaths.size(); - return TextureManager::TextureHandle(rangeStart, true); + std::vector udimIndices; + std::vector handles; + size_t maxIndex = 0; + for (auto& it : texturePaths) + { + std::string textureFilename = it.filename().string(); + std::string udimStr = textureFilename.substr(pos, 4); // the 4 digits + // Insert the udim number into the original filename (before potentially stripping ) + std::string srcStr = path.filename().string(); + std::string newFilename = srcStr.replace(srcStr.find(""), 6, udimStr); + it = it.parent_path() / newFilename; + size_t udim = std::stol(udimStr); + maxIndex = std::max(maxIndex, udim); + udimIndices.push_back(udim); + handles.push_back(loadTexture(it, generateMipLevels, loadAsSRGB, bindFlags, async)); + + FALCOR_CHECK_ARG_GE_MSG(udim, 1001, "Texture {} is not a valid UDIM texture, as it violates the valid UDIM range of 1001-9999", it); } + // UDIM range needs to cover all numbers from 1001 to maxIndex inclusive, so 1001, 1002, 1003 needs 3 indices + size_t rangeStart = getUdimRange(maxIndex - 1001 + 1); - TextureManager::TextureHandle TextureManager::loadTexture(const std::filesystem::path& path, bool generateMipLevels, bool loadAsSRGB, Resource::BindFlags bindFlags, bool async, const SearchDirectories* searchDirectories, size_t* loadedTextureCount) + for (size_t i = 0; i < texturePaths.size(); ++i) { - if (path.string().find("") != std::string::npos) - return loadUdimTexture(path, generateMipLevels, loadAsSRGB, bindFlags, async, searchDirectories, loadedTextureCount); + size_t index = udimIndices[i] - 1001; + mUdimIndirection[rangeStart + index] = handles[i].getID(); + } - TextureHandle handle; + return TextureManager::TextureHandle(rangeStart, true); +} +TextureManager::TextureHandle TextureManager::loadTexture( + const std::filesystem::path& path, + bool generateMipLevels, + bool loadAsSRGB, + Resource::BindFlags bindFlags, + bool async, + const SearchDirectories* searchDirectories, + size_t* loadedTextureCount +) +{ + if (path.string().find("") != std::string::npos) + return loadUdimTexture(path, generateMipLevels, loadAsSRGB, bindFlags, async, searchDirectories, loadedTextureCount); + + std::vector paths; + auto addPath = [&](const std::filesystem::path& p) + { // Find the full path to the texture. std::filesystem::path fullPath; bool found = false; if (searchDirectories) - found = findFileInDirectories(path, fullPath, *searchDirectories); + found = findFileInDirectories(p, fullPath, *searchDirectories); else - found = findFileInDataDirectories(path, fullPath); - - if (loadedTextureCount) - *loadedTextureCount = found ? 1 : 0; + found = findFileInDataDirectories(p, fullPath); - if (!found) - { - logWarning("Can't find texture file '{}'.", path); - return handle; - } + if (found) + paths.emplace_back(std::move(fullPath)); - std::unique_lock lock(mMutex); - const TextureKey textureKey(fullPath, generateMipLevels, loadAsSRGB, bindFlags); + return found; + }; - if (auto it = mKeyToHandle.find(textureKey); it != mKeyToHandle.end()) + // If we find in the filename, locate all mip levels + std::string filename = path.filename().string(); + auto mipPos = filename.find(""); + if (mipPos != std::string::npos) + { + while (true) { - // Texture is already managed. Return its handle. - handle = it->second; + std::string basename = std::string(filename).replace(mipPos, 5, "mip" + std::to_string(paths.size())); + std::filesystem::path mip = path.parent_path() / basename; + if (!addPath(mip)) + break; } - else - { - if (mUseDeferredLoading) - { - // Add new texture desc. - TextureDesc desc = { TextureState::Referenced, nullptr }; - handle = addDesc(desc); - - // Add to key-to-handle map. - mKeyToHandle[textureKey] = handle; + } + else + { + addPath(path); + } - // Return early. - return handle; - } + if (loadedTextureCount) + *loadedTextureCount = paths.empty() ? 0 : 1; -#ifndef DISABLE_ASYNC_TEXTURE_LOADER - mLoadRequestsInProgress++; + TextureHandle handle; + if (paths.empty()) + { + logWarning("Can't find texture file '{}'.", path); + return handle; + } - // Texture is not already managed. Add new texture desc. - TextureDesc desc = { TextureState::Referenced, nullptr }; + std::unique_lock lock(mMutex); + const TextureKey textureKey(paths, generateMipLevels, loadAsSRGB, bindFlags); + if (auto it = mKeyToHandle.find(textureKey); it != mKeyToHandle.end()) + { + // Texture is already managed. Return its handle. + handle = it->second; + } + else + { + if (mUseDeferredLoading) + { + // Add new texture desc. + TextureDesc desc = {TextureState::Referenced, nullptr}; handle = addDesc(desc); // Add to key-to-handle map. mKeyToHandle[textureKey] = handle; - // Function called by the async texture loader when loading finishes. - // It's called by a worker thread so needs to acquire the mutex before changing any state. - auto callback = [=](Texture::SharedPtr pTexture) - { - std::unique_lock lock(mMutex); - - // Mark texture as loaded. - auto& desc = getDesc(handle); - desc.state = TextureState::Loaded; - desc.pTexture = pTexture; + // Return early. + return handle; + } - // Add to texture-to-handle map. - if (pTexture) mTextureToHandle[pTexture.get()] = handle; +#ifndef DISABLE_ASYNC_TEXTURE_LOADER + mLoadRequestsInProgress++; - mLoadRequestsInProgress--; - mCondition.notify_all(); - }; + // Texture is not already managed. Add new texture desc. + TextureDesc desc = {TextureState::Referenced, nullptr}; + handle = addDesc(desc); - // Issue load request to texture loader. - mAsyncTextureLoader.loadFromFile(fullPath, generateMipLevels, loadAsSRGB, bindFlags, callback); -#else - // Load texture from main thread. - Texture::SharedPtr pTexture = Texture::createFromFile(mpDevice.get(), fullPath, generateMipLevels, loadAsSRGB, bindFlags); + // Add to key-to-handle map. + mKeyToHandle[textureKey] = handle; - // Add new texture desc. - TextureDesc desc = { TextureState::Loaded, pTexture }; - handle = addDesc(desc); + // Function called by the async texture loader when loading finishes. + // It's called by a worker thread so needs to acquire the mutex before changing any state. + auto callback = [=](ref pTexture) + { + std::unique_lock lock(mMutex); - // Add to key-to-handle map. - mKeyToHandle[textureKey] = handle; + // Mark texture as loaded. + auto& desc = getDesc(handle); + desc.state = TextureState::Loaded; + desc.pTexture = pTexture; // Add to texture-to-handle map. - if (pTexture) mTextureToHandle[pTexture.get()] = handle; + if (pTexture) + mTextureToHandle[pTexture.get()] = handle; + mLoadRequestsInProgress--; mCondition.notify_all(); -#endif - } - - lock.unlock(); + }; - if (!async) + // Issue load request to texture loader. + if (paths.size() > 1) { - waitForTextureLoading(handle); + mAsyncTextureLoader.loadMippedFromFiles(paths, loadAsSRGB, bindFlags, callback); + } + else + { + mAsyncTextureLoader.loadFromFile(paths[0], generateMipLevels, loadAsSRGB, bindFlags, callback); + } +#else + // Load texture from main thread. + ref pTexture; + if (paths.size() > 1) + { + pTexture = Texture::createMippedFromFiles(mpDevice, paths, loadAsSRGB, bindFlags); + } + else + { + pTexture = Texture::createFromFile(mpDevice, paths[0], generateMipLevels, loadAsSRGB, bindFlags); } - return handle; - } + // Add new texture desc. + TextureDesc desc = {TextureState::Loaded, pTexture}; + handle = addDesc(desc); - void TextureManager::waitForTextureLoading(const TextureHandle& handle) - { - if (!handle) return; + // Add to key-to-handle map. + mKeyToHandle[textureKey] = handle; - // Acquire mutex and wait for texture state to change. - std::unique_lock lock(mMutex); - mCondition.wait(lock, [&]() { return getDesc(handle).state == TextureState::Loaded; }); + // Add to texture-to-handle map. + if (pTexture) + mTextureToHandle[pTexture.get()] = handle; - mpDevice->flushAndSync(); + mCondition.notify_all(); +#endif } - void TextureManager::waitForAllTexturesLoading() - { - // Acquire mutex and wait for all in-progress requests to finish. - std::unique_lock lock(mMutex); - mCondition.wait(lock, [&]() { return mLoadRequestsInProgress == 0; }); + lock.unlock(); - mpDevice->flushAndSync(); - } - - void TextureManager::beginDeferredLoading() + if (!mUseDeferredLoading && !async) { - mUseDeferredLoading = true; + waitForTextureLoading(handle); } - void TextureManager::endDeferredLoading() - { - struct Job { - TextureKey key; - TextureHandle handle; - }; + return handle; +} - // Get a list of textures to load. - std::vector jobs; - for (auto& [key, handle] : mKeyToHandle) - { - auto& desc = getDesc(handle); - if (desc.state == TextureState::Referenced) - jobs.push_back(Job{key, handle}); - } +void TextureManager::waitForTextureLoading(const TextureHandle& handle) +{ + if (!handle) + return; - // Load textures in parallel. - std::atomic texturesLoaded; - NumericRange jobRange(0, jobs.size()); - std::for_each(std::execution::par_unseq, jobRange.begin(), jobRange.end(), - [&](size_t i) - { - const auto& job = jobs[i]; - auto& desc = getDesc(job.handle); - desc.pTexture = Texture::createFromFile(mpDevice.get(), job.key.fullPath, job.key.generateMipLevels, job.key.loadAsSRGB, job.key.bindFlags); - logDebug("Loading texture from '{}'", job.key.fullPath); - if (texturesLoaded.fetch_add(1) % 10 == 9) - { - logDebug("Flush"); - std::lock_guard lock(mpDevice->getGlobalGfxMutex()); - mpDevice->flushAndSync(); - } - } - ); - mpDevice->flushAndSync(); + // Acquire mutex and wait for texture state to change. + std::unique_lock lock(mMutex); + mCondition.wait(lock, [&]() { return getDesc(handle).state == TextureState::Loaded; }); - // Mark loaded textures and add them to lookup table. - for (const auto& job : jobs) - { - auto& desc = getDesc(job.handle); - desc.state = desc.pTexture ? TextureState::Loaded : TextureState::Invalid; - mTextureToHandle[desc.pTexture.get()] = job.handle; - } + mpDevice->flushAndSync(); +} - mUseDeferredLoading = false; - } +void TextureManager::waitForAllTexturesLoading() +{ + // Acquire mutex and wait for all in-progress requests to finish. + std::unique_lock lock(mMutex); + mCondition.wait(lock, [&]() { return mLoadRequestsInProgress == 0; }); - void TextureManager::removeTexture(const TextureHandle& handle) - { - if (handle.isUdim()) - { - removeUdimTexture(handle); - } - if (!handle) return; + mpDevice->flushAndSync(); +} - waitForTextureLoading(handle); +void TextureManager::beginDeferredLoading() +{ + mUseDeferredLoading = true; +} - std::lock_guard lock(mMutex); +void TextureManager::endDeferredLoading() +{ + struct Job + { + TextureKey key; + TextureHandle handle; + }; - // Get texture desc. If it's already cleared, we're done. + // Get a list of textures to load. + std::vector jobs; + for (auto& [key, handle] : mKeyToHandle) + { auto& desc = getDesc(handle); - if (!desc.isValid()) return; - - // Remove handle from maps. - // Note not all handles exist in key-to-handle map so search for it. This can be optimized if needed. - auto it = std::find_if(mKeyToHandle.begin(), mKeyToHandle.end(), [handle](const auto& keyVal) { return keyVal.second == handle; }); - if (it != mKeyToHandle.end()) mKeyToHandle.erase(it); + if (desc.state == TextureState::Referenced) + jobs.push_back(Job{key, handle}); + } - if (desc.pTexture) + // Early out if there are no textures to load. + mUseDeferredLoading = false; + if (jobs.empty()) + return; + + // Load textures in parallel. + std::atomic texturesLoaded; + NumericRange jobRange(0, jobs.size()); + std::for_each( + std::execution::par_unseq, jobRange.begin(), jobRange.end(), + [&](size_t i) { - FALCOR_ASSERT(mTextureToHandle.find(desc.pTexture.get()) != mTextureToHandle.end()); - mTextureToHandle.erase(desc.pTexture.get()); + const auto& job = jobs[i]; + auto& desc = getDesc(job.handle); + if (job.key.fullPaths.size() == 1) + { + desc.pTexture = Texture::createFromFile( + mpDevice, job.key.fullPaths[0], job.key.generateMipLevels, job.key.loadAsSRGB, job.key.bindFlags + ); + logDebug("Loading texture from '{}'", job.key.fullPaths[0]); + } + else + { + desc.pTexture = Texture::createMippedFromFiles(mpDevice, job.key.fullPaths, job.key.loadAsSRGB, job.key.bindFlags); + logDebug("Loading mipped texture from '{}'", job.key.fullPaths[0]); + } + if (texturesLoaded.fetch_add(1) % 10 == 9) + { + logDebug("Flush"); + std::lock_guard lock(mpDevice->getGlobalGfxMutex()); + mpDevice->flushAndSync(); + } } + ); + mpDevice->flushAndSync(); - // Clear texture desc. - desc = {}; - - // Return handle to the free list. - mFreeList.push_back(handle); + // Mark loaded textures and add them to lookup table. + for (const auto& job : jobs) + { + auto& desc = getDesc(job.handle); + desc.state = desc.pTexture ? TextureState::Loaded : TextureState::Invalid; + mTextureToHandle[desc.pTexture.get()] = job.handle; } +} - TextureManager::TextureDesc TextureManager::getTextureDesc(const TextureHandle& handle) const +void TextureManager::removeTexture(const TextureHandle& handle) +{ + if (handle.isUdim()) { - if (handle.isUdim()) - return getTextureDesc(resolveUdimTexture(handle)); + removeUdimTexture(handle); + } + if (!handle) + return; - if (!handle) return {}; + waitForTextureLoading(handle); - std::lock_guard lock(mMutex); - FALCOR_ASSERT(handle && handle.getID() < mTextureDescs.size()); - return mTextureDescs[handle.getID()]; - } + std::lock_guard lock(mMutex); - size_t TextureManager::getTextureDescCount() const + // Get texture desc. If it's already cleared, we're done. + auto& desc = getDesc(handle); + if (!desc.isValid()) + return; + + // Remove handle from maps. + // Note not all handles exist in key-to-handle map so search for it. This can be optimized if needed. + auto it = std::find_if(mKeyToHandle.begin(), mKeyToHandle.end(), [handle](const auto& keyVal) { return keyVal.second == handle; }); + if (it != mKeyToHandle.end()) + mKeyToHandle.erase(it); + + if (desc.pTexture) { - std::lock_guard lock(mMutex); - return mTextureDescs.size(); + FALCOR_ASSERT(mTextureToHandle.find(desc.pTexture.get()) != mTextureToHandle.end()); + mTextureToHandle.erase(desc.pTexture.get()); } - void TextureManager::setShaderData(const ShaderVar& texturesVar, const size_t descCount, const ShaderVar& udimsVar) const - { - std::lock_guard lock(mMutex); + // Clear texture desc. + desc = {}; - if (mTextureDescs.size() > descCount) - { - throw RuntimeError("Descriptor array size ({}) is too small for the required number of textures ({})", descCount, mTextureDescs.size()); - } + // Return handle to the free list. + mFreeList.push_back(handle); +} - Texture::SharedPtr nullTexture; - for (size_t i = 0; i < mTextureDescs.size(); i++) - { - texturesVar[i] = mTextureDescs[i].pTexture; - } - for (size_t i = mTextureDescs.size(); i < descCount; i++) - { - texturesVar[i] = nullTexture; - } +TextureManager::TextureDesc TextureManager::getTextureDesc(const TextureHandle& handle) const +{ + if (handle.isUdim()) + return getTextureDesc(resolveUdimTexture(handle)); - if (mUdimIndirection.empty()) - { - mpUdimIndirection.reset(); - mUdimIndirectionDirty = false; - return; - } + if (!handle) + return {}; - if (!mpUdimIndirection || mUdimIndirection.size() > mpUdimIndirection->getElementCount()) - { - mpUdimIndirection = Buffer::createStructured(mpDevice.get(), sizeof(int32_t), mUdimIndirection.size(), Resource::BindFlags::ShaderResource | Resource::BindFlags::UnorderedAccess, Buffer::CpuAccess::None, - mUdimIndirection.data(), false); - mUdimIndirectionDirty = false; - } + std::lock_guard lock(mMutex); + FALCOR_ASSERT(handle && handle.getID() < mTextureDescs.size()); + return mTextureDescs[handle.getID()]; +} - if (mUdimIndirectionDirty) - { - mpUdimIndirection->setBlob(mUdimIndirection.data(), 0, mUdimIndirection.size() * sizeof(int32_t)); - } +size_t TextureManager::getTextureDescCount() const +{ + std::lock_guard lock(mMutex); + return mTextureDescs.size(); +} - udimsVar = mpUdimIndirection; - } +void TextureManager::setShaderData(const ShaderVar& texturesVar, const size_t descCount, const ShaderVar& udimsVar) const +{ + std::lock_guard lock(mMutex); - TextureManager::Stats TextureManager::getStats() const + if (mTextureDescs.size() > descCount) { - TextureManager::Stats s; - for (const auto& t : mTextureDescs) - { - if (!t.pTexture) - continue; - uint64_t texelCount = t.pTexture->getTexelCount(); - uint32_t channelCount = getFormatChannelCount(t.pTexture->getFormat()); - s.textureCount++; - s.textureTexelCount += texelCount; - s.textureTexelChannelCount += texelCount * channelCount; - s.textureMemoryInBytes += t.pTexture->getTextureSizeInBytes(); - if (isCompressedFormat(t.pTexture->getFormat())) s.textureCompressedCount++; - } - return s; + throw RuntimeError( + "Descriptor array size ({}) is too small for the required number of textures ({})", descCount, mTextureDescs.size() + ); } - TextureManager::TextureHandle TextureManager::addDesc(const TextureDesc& desc) + ref nullTexture; + for (size_t i = 0; i < mTextureDescs.size(); i++) { - TextureHandle handle; - - // Allocate new texture handle and insert desc. - if (!mFreeList.empty()) - { - handle = mFreeList.back(); - mFreeList.pop_back(); - getDesc(handle) = desc; - } - else - { - if (mTextureDescs.size() >= mMaxTextureCount) - { - throw RuntimeError("Out of texture handles"); - } - handle = TextureHandle{ static_cast(mTextureDescs.size()) }; - mTextureDescs.emplace_back(desc); - } + texturesVar[i] = mTextureDescs[i].pTexture; + } + for (size_t i = mTextureDescs.size(); i < descCount; i++) + { + texturesVar[i] = nullTexture; + } - return handle; + if (mUdimIndirection.empty()) + { + mpUdimIndirection.reset(); + mUdimIndirectionDirty = false; + return; } - TextureManager::TextureDesc& TextureManager::getDesc(const TextureHandle& handle) + if (!mpUdimIndirection || mUdimIndirection.size() > mpUdimIndirection->getElementCount()) { - if (handle.isUdim()) - return getDesc(resolveUdimTexture(handle)); + mpUdimIndirection = Buffer::createStructured( + mpDevice, sizeof(int32_t), mUdimIndirection.size(), Resource::BindFlags::ShaderResource | Resource::BindFlags::UnorderedAccess, + Buffer::CpuAccess::None, mUdimIndirection.data(), false + ); + mUdimIndirectionDirty = false; + } - FALCOR_ASSERT(handle && handle.getID() < mTextureDescs.size()); - return mTextureDescs[handle.getID()]; + if (mUdimIndirectionDirty) + { + mpUdimIndirection->setBlob(mUdimIndirection.data(), 0, mUdimIndirection.size() * sizeof(int32_t)); } - size_t TextureManager::getUdimRange(size_t requiredSize) + udimsVar = mpUdimIndirection; +} + +TextureManager::Stats TextureManager::getStats() const +{ + std::lock_guard lock(mMutex); + TextureManager::Stats s; + for (const auto& t : mTextureDescs) { - // But first look in the freed ranges for the smallest one that we can reuse - size_t smallestFound = std::numeric_limits::max(); - int64_t foundIndex = -1; - for (size_t i = 0; i < mFreeUdimRanges.size(); ++i) - { - size_t rangeSize = mUdimIndirectionSize[mFreeUdimRanges[i]]; - if (requiredSize <= rangeSize && rangeSize < smallestFound) - { - foundIndex = i; - smallestFound = rangeSize; - } - // If we found an exact match, no need to look further - if (smallestFound == requiredSize) - break; - } + if (!t.pTexture) + continue; + uint64_t texelCount = t.pTexture->getTexelCount(); + uint32_t channelCount = getFormatChannelCount(t.pTexture->getFormat()); + s.textureCount++; + s.textureTexelCount += texelCount; + s.textureTexelChannelCount += texelCount * channelCount; + s.textureMemoryInBytes += t.pTexture->getTextureSizeInBytes(); + if (isCompressedFormat(t.pTexture->getFormat())) + s.textureCompressedCount++; + } + return s; +} + +TextureManager::TextureHandle TextureManager::addDesc(const TextureDesc& desc) +{ + TextureHandle handle; - // haven't found any reusable, just add a new one - if (foundIndex == -1) + // Allocate new texture handle and insert desc. + if (!mFreeList.empty()) + { + handle = mFreeList.back(); + mFreeList.pop_back(); + getDesc(handle) = desc; + } + else + { + if (mTextureDescs.size() >= mMaxTextureCount) { - const size_t rangeStart = mUdimIndirection.size(); - mUdimIndirection.resize(rangeStart + requiredSize, -1); - mUdimIndirectionSize.resize(rangeStart + requiredSize, 0); - mUdimIndirectionSize[rangeStart] = requiredSize; - return rangeStart; + throw RuntimeError("Out of texture handles"); } + handle = TextureHandle{static_cast(mTextureDescs.size())}; + mTextureDescs.emplace_back(desc); + } - mUdimIndirectionDirty = true; + return handle; +} - // Range is already filled with -1 from the deletion - const size_t rangeStart = mFreeUdimRanges[foundIndex]; - mFreeUdimRanges[foundIndex] = mFreeUdimRanges.back(); - mFreeUdimRanges.pop_back(); - return rangeStart; - } +TextureManager::TextureDesc& TextureManager::getDesc(const TextureHandle& handle) +{ + if (handle.isUdim()) + return getDesc(resolveUdimTexture(handle)); - void TextureManager::freeUdimRange(size_t rangeStart) - { - mUdimIndirectionDirty = true; - mFreeUdimRanges.push_back(rangeStart); - } + FALCOR_ASSERT(handle && handle.getID() < mTextureDescs.size()); + return mTextureDescs[handle.getID()]; +} - void TextureManager::removeUdimTexture(const TextureHandle& handle) +size_t TextureManager::getUdimRange(size_t requiredSize) +{ + // But first look in the freed ranges for the smallest one that we can reuse + size_t smallestFound = std::numeric_limits::max(); + int64_t foundIndex = -1; + for (size_t i = 0; i < mFreeUdimRanges.size(); ++i) { - size_t rangeStart = handle.getID(); - size_t rangeSize = mUdimIndirectionSize[rangeStart]; - for (size_t i = rangeStart; i < rangeStart + rangeSize; ++i) + size_t rangeSize = mUdimIndirectionSize[mFreeUdimRanges[i]]; + if (requiredSize <= rangeSize && rangeSize < smallestFound) { - if (mUdimIndirection[i] < 0) - continue; - TextureHandle texHandle(mUdimIndirection[i]); - removeTexture(texHandle); - mUdimIndirection[i] = -1; + foundIndex = i; + smallestFound = rangeSize; } - - freeUdimRange(rangeStart); + // If we found an exact match, no need to look further + if (smallestFound == requiredSize) + break; } - TextureManager::TextureHandle TextureManager::resolveUdimTexture(const TextureHandle& handle) const + // haven't found any reusable, just add a new one + if (foundIndex == -1) { - return resolveUdimTexture(handle, float2(0.f)); + const size_t rangeStart = mUdimIndirection.size(); + mUdimIndirection.resize(rangeStart + requiredSize, -1); + mUdimIndirectionSize.resize(rangeStart + requiredSize, 0); + mUdimIndirectionSize[rangeStart] = requiredSize; + return rangeStart; } - TextureManager::TextureHandle TextureManager::resolveUdimTexture(const TextureHandle& handle, const float2& uv) const - { - if (!handle.isUdim()) - return handle; - size_t rangeStart = handle.getID(); - size_t udim = uint(uv[0]) + 10 * uint(uv[1]); - FALCOR_ASSERT_LT(udim, mUdimIndirectionSize[rangeStart]); + mUdimIndirectionDirty = true; - if (mUdimIndirection[rangeStart + udim] >= 0) - return TextureHandle(mUdimIndirection[rangeStart + udim]); - return TextureHandle(); + // Range is already filled with -1 from the deletion + const size_t rangeStart = mFreeUdimRanges[foundIndex]; + mFreeUdimRanges[foundIndex] = mFreeUdimRanges.back(); + mFreeUdimRanges.pop_back(); + return rangeStart; +} + +void TextureManager::freeUdimRange(size_t rangeStart) +{ + mUdimIndirectionDirty = true; + mFreeUdimRanges.push_back(rangeStart); +} + +void TextureManager::removeUdimTexture(const TextureHandle& handle) +{ + size_t rangeStart = handle.getID(); + size_t rangeSize = mUdimIndirectionSize[rangeStart]; + for (size_t i = rangeStart; i < rangeStart + rangeSize; ++i) + { + if (mUdimIndirection[i] < 0) + continue; + TextureHandle texHandle(mUdimIndirection[i]); + removeTexture(texHandle); + mUdimIndirection[i] = -1; } + + freeUdimRange(rangeStart); +} + +TextureManager::TextureHandle TextureManager::resolveUdimTexture(const TextureHandle& handle) const +{ + return resolveUdimTexture(handle, float2(0.f)); +} + +TextureManager::TextureHandle TextureManager::resolveUdimTexture(const TextureHandle& handle, const float2& uv) const +{ + if (!handle.isUdim()) + return handle; + size_t rangeStart = handle.getID(); + size_t udim = uint(uv[0]) + 10 * uint(uv[1]); + FALCOR_ASSERT_LT(udim, mUdimIndirectionSize[rangeStart]); + + if (mUdimIndirection[rangeStart + udim] >= 0) + return TextureHandle(mUdimIndirection[rangeStart + udim]); + return TextureHandle(); } +} // namespace Falcor diff --git a/Source/Falcor/Utils/Image/TextureManager.h b/Source/Falcor/Utils/Image/TextureManager.h index cf2cee149..49735c079 100644 --- a/Source/Falcor/Utils/Image/TextureManager.h +++ b/Source/Falcor/Utils/Image/TextureManager.h @@ -41,239 +41,264 @@ namespace Falcor { - class SearchDirectories; - - /** Multi-threaded texture manager. +class SearchDirectories; + +/** + * Multi-threaded texture manager. + * + * This class manages a collection of textures and implements + * asynchronous texture loading. All operations are thread-safe. + * + * Each managed texture is assigned a unique handle upon loading. + * This handle is used in shader code to reference the given texture + * in the array of GPU texture descriptors. + */ +class FALCOR_API TextureManager +{ +public: + /// State of a managed texture. + enum class TextureState + { + Invalid, ///< Invalid/unknown texture. + Referenced, ///< Texture is referenced, but not yet loaded. + Loaded, ///< Texture has finished loading. + }; - This class manages a collection of textures and implements - asynchronous texture loading. All operations are thread-safe. + struct Stats + { + uint64_t textureCount = 0; ///< Number of unique textures. A texture can be referenced by multiple materials. + uint64_t textureCompressedCount = 0; ///< Number of unique compressed textures. + uint64_t textureTexelCount = 0; ///< Total number of texels in all textures. + uint64_t textureTexelChannelCount = 0; ///< Total number of texel channels in all textures. + uint64_t textureMemoryInBytes = 0; ///< Total memory in bytes used by the textures. + }; - Each managed texture is assigned a unique handle upon loading. - This handle is used in shader code to reference the given texture - in the array of GPU texture descriptors. - */ - class FALCOR_API TextureManager + /** + * Handle to a managed texture. + */ + class TextureHandle { public: - using SharedPtr = std::shared_ptr; + static constexpr uint32_t kInvalidID = std::numeric_limits::max(); - ~TextureManager(); + public: + TextureHandle() = default; + explicit TextureHandle(uint32_t id) : mID(id) {} - /** State of a managed texture. - */ - enum class TextureState - { - Invalid, ///< Invalid/unknown texture. - Referenced, ///< Texture is referenced, but not yet loaded. - Loaded, ///< Texture has finished loading. - }; + explicit TextureHandle(uint32_t id, bool isUdim) : mID(id), mIsUdim(isUdim) {} + + bool isValid() const { return mID != kInvalidID; } + explicit operator bool() const { return isValid(); } + + uint32_t getID() const { return mID; } + bool isUdim() const { return mIsUdim; } + + bool operator==(const TextureHandle& other) const { return mID == other.mID && mIsUdim == other.mIsUdim; } - struct Stats - { - uint64_t textureCount = 0; ///< Number of unique textures. A texture can be referenced by multiple materials. - uint64_t textureCompressedCount = 0; ///< Number of unique compressed textures. - uint64_t textureTexelCount = 0; ///< Total number of texels in all textures. - uint64_t textureTexelChannelCount = 0; ///< Total number of texel channels in all textures. - uint64_t textureMemoryInBytes = 0; ///< Total memory in bytes used by the textures. - }; - - /** Handle to a managed texture. - */ - class TextureHandle - { - public: - static const uint32_t kInvalidID = std::numeric_limits::max(); - public: - TextureHandle() = default; - explicit TextureHandle(uint32_t id) - : mID(id) - {} - - explicit TextureHandle(uint32_t id, bool isUdim) - : mID(id) - , mIsUdim(isUdim) - {} - - bool isValid() const { return mID != kInvalidID; } - explicit operator bool() const { return isValid(); } - - uint32_t getID() const { return mID; } - bool isUdim() const { return mIsUdim; } - - bool operator==(const TextureHandle& other) const - { - return mID == other.mID && mIsUdim == other.mIsUdim; - } - private: - - uint32_t mID { kInvalidID }; - bool mIsUdim { false }; - }; - - /** Struct describing a managed texture. - */ - struct TextureDesc - { - TextureState state = TextureState::Invalid; ///< Current state of the texture. - Texture::SharedPtr pTexture; ///< Valid texture object when state is 'Loaded', or nullptr if loading failed. - - bool isValid() const { return state != TextureState::Invalid; } - }; - - /** Create a texture manager. - \param[in] pDevice GPU device. - \param[in] maxTextureCount Maximum number of textures that can be simultaneously managed. - \param[in] threadCount Number of worker threads. - \return A new object. - */ - static SharedPtr create(std::shared_ptr pDevice, size_t maxTextureCount, size_t threadCount = std::thread::hardware_concurrency()); - - /** Add a texture to the manager. - If the texture is already managed, its existing handle is returned. - \param[in] pTexture The texture resource. - \return Unique handle to the texture. - */ - TextureHandle addTexture(const Texture::SharedPtr& pTexture); - - /** Requst loading a texture from file. - This will add the texture to the set of managed textures. The function returns a handle immediately. - If asynchronous loading is requested, the texture data will not be available until loading completes. - The returned handle is valid for the entire lifetime of the texture, until removeTexture() is called. - \param[in] path File path of the texture. This can be a full path or a relative path from a data directory. - \param[in] generateMipLevels Whether the full mip-chain should be generated. - \param[in] loadAsSRGB Load the texture as sRGB format if supported, otherwise linear color. - \param[in] bindFlags The bind flags for the texture resource. - \param[in] async Load asynchronously, otherwise the function blocks until the texture data is loaded. - \param[in] searchDirectories Optionally can pass in search directories, will be used instead of the global data directories. - \param[out] loadedTextureCount Optionally can provided the number of actually loaded textures (2+ can happen with UDIMs) - \return Unique handle to the texture, or an invalid handle if the texture can't be found. - */ - TextureHandle loadTexture(const std::filesystem::path& path, bool generateMipLevels, bool loadAsSRGB, Resource::BindFlags bindFlags = Resource::BindFlags::ShaderResource, bool async = true, const SearchDirectories* searchDirectories = nullptr, size_t* loadedTextureCount = nullptr); - - /** Same as loadTexture, but explicitly handles Udim textures. If the texture isn't Udim, it falls back to loadTexture. - Also, loadTexture will detect UDIM and call loadUdimTexture if needed. - */ - TextureHandle loadUdimTexture(const std::filesystem::path& path, bool generateMipLevels, bool loadAsSRGB, Resource::BindFlags bindFlags = Resource::BindFlags::ShaderResource, bool async = true, const SearchDirectories* searchDirectories = nullptr, size_t* loadedTextureCount = nullptr); - - /** Wait for a requested texture to load. - If the handle is valid, the call blocks until the texture is loaded (or failed to load). - \param[in] handle Texture handle. - */ - void waitForTextureLoading(const TextureHandle& handle); - - /** Waits for all currently requested textures to be loaded. - */ - void waitForAllTexturesLoading(); - - /** Marks the beginning of a section where texture loading is deferred. - All loadTexture() and loadUdimTexture() calls after calling this will be put on a deferred list. - A later call to endDeferredLoading() will load all queued up textures in parallel. - WARNING: This is a dangerous operation because Falcor is generally not thread-safe. Only use this - from the main thread when it is guaranteed to not be interleaved with any other thread. - */ - void beginDeferredLoading(); - void endDeferredLoading(); - - /** Remove a texture. - \param[in] handle Texture handle. - */ - void removeTexture(const TextureHandle& handle); - - /** Get a loaded texture. Call getTextureDesc() for more info. - \param[in] handle Texture handle. - \return Texture if loaded, or nullptr if handle doesn't exist or texture isn't yet loaded. - */ - Texture::SharedPtr getTexture(const TextureHandle& handle) const { return getTextureDesc(handle).pTexture; } - - /** Get a texture desc. - \param[in] handle Texture handle. - \return Texture desc, or invalid desc if handle is invalid. - */ - TextureDesc getTextureDesc(const TextureHandle& handle) const; - - /** Get texture desc count. - \return Number of texture descs. - */ - size_t getTextureDescCount() const; - - /** Number of UDIM indirections allocated. - This is used to determine whether UDIMs should be enabled. - This number intentionally does not shrink when UDIM material is supported, - as the UDIM indirection can be sparse. - Also, there is no need to recompile just because the number of udims shrunk. - */ - size_t getUdimIndirectionCount() const { return mUdimIndirection.size(); } - - /** Bind all textures into a shader var. - The shader var should refer to a Texture2D descriptor array of fixed size. - The array must be large enough, otherwise an exception is thrown. - This restriction will go away when unbounded descriptor arrays are supported (see #1321). - \param[in] var Shader var for descriptor array. - \param[in] descCount Size of descriptor array. - */ - void setShaderData(const ShaderVar& texturesVar, const size_t descCount, const ShaderVar& udimsVar) const; - - /** Returns stats for the textures - */ - Stats getStats() const; private: - TextureManager(std::shared_ptr pDevice, size_t maxTextureCount, size_t threadCount); - size_t getUdimRange(size_t requiredSize); - void freeUdimRange(size_t rangeStart); - void removeUdimTexture(const TextureHandle& handle); - TextureHandle resolveUdimTexture(const TextureHandle& handle) const; - TextureHandle resolveUdimTexture(const TextureHandle& handle, const float2& uv) const; - - /** Key to uniquely identify a managed texture. - */ - struct TextureKey + uint32_t mID{kInvalidID}; + bool mIsUdim{false}; + }; + + /// Struct describing a managed texture. + struct TextureDesc + { + TextureState state = TextureState::Invalid; ///< Current state of the texture. + ref pTexture; ///< Valid texture object when state is 'Loaded', or nullptr if loading failed. + + bool isValid() const { return state != TextureState::Invalid; } + }; + + /** + * Constructor. + * @param[in] pDevice GPU device. + * @param[in] maxTextureCount Maximum number of textures that can be simultaneously managed. + * @param[in] threadCount Number of worker threads. + */ + TextureManager(ref pDevice, size_t maxTextureCount, size_t threadCount = std::thread::hardware_concurrency()); + + ~TextureManager(); + + /** + * Add a texture to the manager. + * If the texture is already managed, its existing handle is returned. + * @param[in] pTexture The texture resource. + * @return Unique handle to the texture. + */ + TextureHandle addTexture(const ref& pTexture); + + /** + * Requst loading a texture from file. + * This will add the texture to the set of managed textures. The function returns a handle immediately. + * If asynchronous loading is requested, the texture data will not be available until loading completes. + * The returned handle is valid for the entire lifetime of the texture, until removeTexture() is called. + * @param[in] path File path of the texture. This can be a full path or a relative path from a data directory. + * @param[in] generateMipLevels Whether the full mip-chain should be generated. + * @param[in] loadAsSRGB Load the texture as sRGB format if supported, otherwise linear color. + * @param[in] bindFlags The bind flags for the texture resource. + * @param[in] async Load asynchronously, otherwise the function blocks until the texture data is loaded. + * @param[in] searchDirectories Optionally can pass in search directories, will be used instead of the global data directories. + * @param[out] loadedTextureCount Optionally can provided the number of actually loaded textures (2+ can happen with UDIMs) + * @return Unique handle to the texture, or an invalid handle if the texture can't be found. + */ + TextureHandle loadTexture( + const std::filesystem::path& path, + bool generateMipLevels, + bool loadAsSRGB, + Resource::BindFlags bindFlags = Resource::BindFlags::ShaderResource, + bool async = true, + const SearchDirectories* searchDirectories = nullptr, + size_t* loadedTextureCount = nullptr + ); + + /** + * Same as loadTexture, but explicitly handles Udim textures. If the texture isn't Udim, it falls back to loadTexture. + * Also, loadTexture will detect UDIM and call loadUdimTexture if needed. + */ + TextureHandle loadUdimTexture( + const std::filesystem::path& path, + bool generateMipLevels, + bool loadAsSRGB, + Resource::BindFlags bindFlags = Resource::BindFlags::ShaderResource, + bool async = true, + const SearchDirectories* searchDirectories = nullptr, + size_t* loadedTextureCount = nullptr + ); + + /** + * Wait for a requested texture to load. + * If the handle is valid, the call blocks until the texture is loaded (or failed to load). + * @param[in] handle Texture handle. + */ + void waitForTextureLoading(const TextureHandle& handle); + + /** + * Waits for all currently requested textures to be loaded. + */ + void waitForAllTexturesLoading(); + + /** + * Marks the beginning of a section where texture loading is deferred. + * All loadTexture() and loadUdimTexture() calls after calling this will be put on a deferred list. + * A later call to endDeferredLoading() will load all queued up textures in parallel. + * WARNING: This is a dangerous operation because Falcor is generally not thread-safe. Only use this + * from the main thread when it is guaranteed to not be interleaved with any other thread. + */ + void beginDeferredLoading(); + void endDeferredLoading(); + + /** + * Remove a texture. + * @param[in] handle Texture handle. + */ + void removeTexture(const TextureHandle& handle); + + /** + * Get a loaded texture. Call getTextureDesc() for more info. + * @param[in] handle Texture handle. + * @return Texture if loaded, or nullptr if handle doesn't exist or texture isn't yet loaded. + */ + ref getTexture(const TextureHandle& handle) const { return getTextureDesc(handle).pTexture; } + + /** + * Get a texture desc. + * @param[in] handle Texture handle. + * @return Texture desc, or invalid desc if handle is invalid. + */ + TextureDesc getTextureDesc(const TextureHandle& handle) const; + + /** + * Get texture desc count. + * @return Number of texture descs. + */ + size_t getTextureDescCount() const; + + /** + * Number of UDIM indirections allocated. + * This is used to determine whether UDIMs should be enabled. + * This number intentionally does not shrink when UDIM material is supported, + * as the UDIM indirection can be sparse. + * Also, there is no need to recompile just because the number of udims shrunk. + */ + size_t getUdimIndirectionCount() const { return mUdimIndirection.size(); } + + /** + * Bind all textures into a shader var. + * The shader var should refer to a Texture2D descriptor array of fixed size. + * The array must be large enough, otherwise an exception is thrown. + * This restriction will go away when unbounded descriptor arrays are supported (see #1321). + * @param[in] var Shader var for descriptor array. + * @param[in] descCount Size of descriptor array. + */ + void setShaderData(const ShaderVar& texturesVar, const size_t descCount, const ShaderVar& udimsVar) const; + + /** + * Returns stats for the textures + */ + Stats getStats() const; + +private: + size_t getUdimRange(size_t requiredSize); + void freeUdimRange(size_t rangeStart); + void removeUdimTexture(const TextureHandle& handle); + TextureHandle resolveUdimTexture(const TextureHandle& handle) const; + TextureHandle resolveUdimTexture(const TextureHandle& handle, const float2& uv) const; + + /** + * Key to uniquely identify a managed texture. + */ + struct TextureKey + { + std::vector fullPaths; + bool generateMipLevels; + bool loadAsSRGB; + Resource::BindFlags bindFlags; + + TextureKey(const std::vector& paths, bool mips, bool srgb, Resource::BindFlags flags) + : fullPaths(paths), generateMipLevels(mips), loadAsSRGB(srgb), bindFlags(flags) + {} + + bool operator<(const TextureKey& rhs) const { - std::filesystem::path fullPath; - bool generateMipLevels; - bool loadAsSRGB; - Resource::BindFlags bindFlags; - - TextureKey(const std::filesystem::path& path, bool mips, bool srgb, Resource::BindFlags flags) - : fullPath(path), generateMipLevels(mips), loadAsSRGB(srgb), bindFlags(flags) - {} - - bool operator<(const TextureKey& rhs) const - { - if (fullPath != rhs.fullPath) return fullPath < rhs.fullPath; - else if (generateMipLevels != rhs.generateMipLevels) return generateMipLevels < rhs.generateMipLevels; - else if (loadAsSRGB != rhs.loadAsSRGB) return loadAsSRGB < rhs.loadAsSRGB; - else return bindFlags < rhs.bindFlags; - } - }; - - TextureHandle addDesc(const TextureDesc& desc); - TextureDesc& getDesc(const TextureHandle& handle); - - std::shared_ptr mpDevice; - - mutable std::mutex mMutex; ///< Mutex for synchronizing access to shared resources. - std::condition_variable mCondition; ///< Condition variable to wait on for loading to finish. - - // Internal state. Do not access outside of critical section. - std::vector mTextureDescs; ///< Array of all texture descs, indexed by handle ID. - std::vector mFreeList; ///< List of unused handles. - std::map mKeyToHandle; ///< Map from texture key to handle. - std::map mTextureToHandle; ///< Map from texture ptr to handle. - /// Map from UDIM-1001 to an actual textureID, -1 if the texture does not exist (e.g., there is 1001 and 1003, so 1002 [1] == -1) - std::vector mUdimIndirection; - /// For each udim indirection range, writes (at the first element), how long that range is (there is 0 everywhere else) - std::vector mUdimIndirectionSize; - /// Free ranges in the udimIndirection, when a UDIM texture is deleted (contains first position) - std::vector mFreeUdimRanges; - - mutable bool mUdimIndirectionDirty = true; - mutable Buffer::SharedPtr mpUdimIndirection; - - bool mUseDeferredLoading = false; - - AsyncTextureLoader mAsyncTextureLoader; ///< Utility for asynchronous texture loading. - size_t mLoadRequestsInProgress = 0; ///< Number of load requests currently in progress. - - const size_t mMaxTextureCount; ///< Maximum number of textures that can be simultaneously managed. + if (fullPaths != rhs.fullPaths) + return fullPaths < rhs.fullPaths; + else if (generateMipLevels != rhs.generateMipLevels) + return generateMipLevels < rhs.generateMipLevels; + else if (loadAsSRGB != rhs.loadAsSRGB) + return loadAsSRGB < rhs.loadAsSRGB; + else + return bindFlags < rhs.bindFlags; + } }; -} + + TextureHandle addDesc(const TextureDesc& desc); + TextureDesc& getDesc(const TextureHandle& handle); + + ref mpDevice; + + mutable std::mutex mMutex; ///< Mutex for synchronizing access to shared resources. + std::condition_variable mCondition; ///< Condition variable to wait on for loading to finish. + + // Internal state. Do not access outside of critical section. + std::vector mTextureDescs; ///< Array of all texture descs, indexed by handle ID. + std::vector mFreeList; ///< List of unused handles. + std::map mKeyToHandle; ///< Map from texture key to handle. + std::map mTextureToHandle; ///< Map from texture ptr to handle. + /// Map from UDIM-1001 to an actual textureID, -1 if the texture does not exist (e.g., there is 1001 and 1003, so 1002 [1] == -1) + std::vector mUdimIndirection; + /// For each udim indirection range, writes (at the first element), how long that range is (there is 0 everywhere else) + std::vector mUdimIndirectionSize; + /// Free ranges in the udimIndirection, when a UDIM texture is deleted (contains first position) + std::vector mFreeUdimRanges; + + mutable bool mUdimIndirectionDirty = true; + mutable ref mpUdimIndirection; + + bool mUseDeferredLoading = false; + + AsyncTextureLoader mAsyncTextureLoader; ///< Utility for asynchronous texture loading. + size_t mLoadRequestsInProgress = 0; ///< Number of load requests currently in progress. + + const size_t mMaxTextureCount; ///< Maximum number of textures that can be simultaneously managed. +}; +} // namespace Falcor diff --git a/Source/Falcor/Utils/InternalDictionary.h b/Source/Falcor/Utils/InternalDictionary.h index f7b5afcda..ff29b801c 100644 --- a/Source/Falcor/Utils/InternalDictionary.h +++ b/Source/Falcor/Utils/InternalDictionary.h @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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,75 +34,69 @@ namespace Falcor { - class InternalDictionary +class InternalDictionary +{ +public: + class Value { public: - class Value - { - public: - Value() = default; - Value(std::any& value) : mValue(value) {}; - - template - void operator=(const T& t) { mValue = t; } - - template - operator T() const { return std::any_cast(mValue); } - - private: - std::any mValue; - }; - - using Container = std::unordered_map; - - using SharedPtr = std::shared_ptr; - - InternalDictionary() = default; - InternalDictionary(const InternalDictionary& d) : mContainer(d.mContainer) {} - - /** Create a new dictionary. - \return A new object, or throws an exception if creation failed. - */ - static SharedPtr create() { return SharedPtr(new InternalDictionary); } - - Value& operator[](const std::string& key) { return mContainer[key]; } - const Value& operator[](const std::string& key) const { return mContainer.at(key); } - - Container::const_iterator begin() const { return mContainer.begin(); } - Container::const_iterator end() const { return mContainer.end(); } - - Container::iterator begin() { return mContainer.begin(); } - Container::iterator end() { return mContainer.end(); } - - size_t size() const { return mContainer.size(); } + Value() = default; + Value(std::any& value) : mValue(value){}; - /** Check if a key exists. - */ - bool keyExists(const std::string& key) const - { - return mContainer.find(key) != mContainer.end(); - } - - /** Get value by key. Throws an exception if key does not exist. - */ template - T getValue(const std::string& key) + void operator=(const T& t) { - auto it = mContainer.find(key); - if (it == mContainer.end()) throw ArgumentError("Key '{}' does not exist", key); - return it->second; + mValue = t; } - /** Get value by key. Returns the specified default value if key does not exist. - */ template - T getValue(const std::string& key, const T& defaultValue) + operator T() const { - auto it = mContainer.find(key); - return it != mContainer.end() ? it->second : defaultValue; + return std::any_cast(mValue); } private: - Container mContainer; + std::any mValue; }; -} + + using Container = std::unordered_map; + + InternalDictionary() = default; + InternalDictionary(const InternalDictionary& d) : mContainer(d.mContainer) {} + + Value& operator[](const std::string& key) { return mContainer[key]; } + const Value& operator[](const std::string& key) const { return mContainer.at(key); } + + Container::const_iterator begin() const { return mContainer.begin(); } + Container::const_iterator end() const { return mContainer.end(); } + + Container::iterator begin() { return mContainer.begin(); } + Container::iterator end() { return mContainer.end(); } + + size_t size() const { return mContainer.size(); } + + /// Check if a key exists. + bool keyExists(const std::string& key) const { return mContainer.find(key) != mContainer.end(); } + + /// Get value by key. Throws an exception if key does not exist. + template + T getValue(const std::string& key) + { + auto it = mContainer.find(key); + if (it == mContainer.end()) + throw ArgumentError("Key '{}' does not exist", key); + return it->second; + } + + /// Get value by key. Returns the specified default value if key does not exist. + template + T getValue(const std::string& key, const T& defaultValue) + { + auto it = mContainer.find(key); + return it != mContainer.end() ? it->second : defaultValue; + } + +private: + Container mContainer; +}; +} // namespace Falcor diff --git a/Source/Falcor/Utils/Logger.cpp b/Source/Falcor/Utils/Logger.cpp index faeff86fb..67ce7c4e3 100644 --- a/Source/Falcor/Utils/Logger.cpp +++ b/Source/Falcor/Utils/Logger.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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,145 +32,160 @@ namespace Falcor { - namespace - { - Logger::Level sVerbosity = Logger::Level::Info; - Logger::OutputFlags sOutputs = Logger::OutputFlags::Console | Logger::OutputFlags::File | Logger::OutputFlags::DebugWindow; - std::filesystem::path sLogFilePath; +namespace +{ +Logger::Level sVerbosity = Logger::Level::Info; +Logger::OutputFlags sOutputs = Logger::OutputFlags::Console | Logger::OutputFlags::File | Logger::OutputFlags::DebugWindow; +std::filesystem::path sLogFilePath; #if FALCOR_ENABLE_LOGGER - bool sInitialized = false; - FILE* sLogFile = nullptr; +bool sInitialized = false; +FILE* sLogFile = nullptr; - std::filesystem::path generateLogFilePath() - { - std::string prefix = getExecutableName(); - std::filesystem::path directory = getRuntimeDirectory(); - return findAvailableFilename(prefix, directory, "log"); - } +std::filesystem::path generateLogFilePath() +{ + std::string prefix = getExecutableName(); + std::filesystem::path directory = getRuntimeDirectory(); + return findAvailableFilename(prefix, directory, "log"); +} - FILE* openLogFile() - { - FILE* pFile = nullptr; - - if (sLogFilePath.empty()) - { - sLogFilePath = generateLogFilePath(); - } - - pFile = std::fopen(sLogFilePath.string().c_str(), "w"); - if (pFile != nullptr) - { - // Success - return pFile; - } - - // If we got here, we couldn't create a log file - FALCOR_UNREACHABLE(); - return pFile; - } +FILE* openLogFile() +{ + FILE* pFile = nullptr; - void printToLogFile(const std::string& s) - { - if (!sInitialized) - { - sLogFile = openLogFile(); - sInitialized = true; - } - - if (sLogFile) - { - std::fprintf(sLogFile, "%s", s.c_str()); - std::fflush(sLogFile); - } - } -#endif + if (sLogFilePath.empty()) + { + sLogFilePath = generateLogFilePath(); } - void Logger::shutdown() + pFile = std::fopen(sLogFilePath.string().c_str(), "w"); + if (pFile != nullptr) { -#if FALCOR_ENABLE_LOGGER - if(sLogFile) - { - fclose(sLogFile); - sLogFile = nullptr; - sInitialized = false; - } -#endif + // Success + return pFile; } - const char* getLogLevelString(Logger::Level level) + // If we got here, we couldn't create a log file + FALCOR_UNREACHABLE(); + return pFile; +} + +void printToLogFile(const std::string& s) +{ + if (!sInitialized) { - switch (level) - { - case Logger::Level::Fatal: - return "(Fatal)"; - case Logger::Level::Error: - return "(Error)"; - case Logger::Level::Warning: - return "(Warning)"; - case Logger::Level::Info: - return "(Info)"; - case Logger::Level::Debug: - return "(Debug)"; - default: - FALCOR_UNREACHABLE(); - return nullptr; - } + sLogFile = openLogFile(); + sInitialized = true; } - void Logger::log(Level level, const std::string_view msg) + if (sLogFile) { -#if FALCOR_ENABLE_LOGGER - if (level <= sVerbosity) - { - std::string s = fmt::format("{} {}\n", getLogLevelString(level), msg); - - // Write to console. - if (is_set(sOutputs, OutputFlags::Console)) - { - auto& os = level > Logger::Level::Error ? std::cout : std::cerr; - os << s; - os.flush(); - } - - // Write to file. - if (is_set(sOutputs, OutputFlags::File)) - { - printToLogFile(s); - } - - // Write to debug window if debugger is attached. - if (is_set(sOutputs, OutputFlags::DebugWindow) && isDebuggerPresent()) - { - printToDebugWindow(s); - } - } + std::fprintf(sLogFile, "%s", s.c_str()); + std::fflush(sLogFile); + } +} #endif +} // namespace + +void Logger::shutdown() +{ +#if FALCOR_ENABLE_LOGGER + if (sLogFile) + { + fclose(sLogFile); + sLogFile = nullptr; + sInitialized = false; } +#endif +} - bool Logger::setLogFilePath(const std::filesystem::path& path) +const char* getLogLevelString(Logger::Level level) +{ + switch (level) { + case Logger::Level::Fatal: + return "(Fatal)"; + case Logger::Level::Error: + return "(Error)"; + case Logger::Level::Warning: + return "(Warning)"; + case Logger::Level::Info: + return "(Info)"; + case Logger::Level::Debug: + return "(Debug)"; + default: + FALCOR_UNREACHABLE(); + return nullptr; + } +} + +void Logger::log(Level level, const std::string_view msg) +{ #if FALCOR_ENABLE_LOGGER - if (sLogFile) + if (level <= sVerbosity) + { + std::string s = fmt::format("{} {}\n", getLogLevelString(level), msg); + + // Write to console. + if (is_set(sOutputs, OutputFlags::Console)) { - return false; + auto& os = level > Logger::Level::Error ? std::cout : std::cerr; + os << s; + os.flush(); } - else + + // Write to file. + if (is_set(sOutputs, OutputFlags::File)) { - sLogFilePath = path; - return true; + printToLogFile(s); } -#else - return false; + + // Write to debug window if debugger is attached. + if (is_set(sOutputs, OutputFlags::DebugWindow) && isDebuggerPresent()) + { + printToDebugWindow(s); + } + } #endif +} + +bool Logger::setLogFilePath(const std::filesystem::path& path) +{ +#if FALCOR_ENABLE_LOGGER + if (sLogFile) + { + return false; + } + else + { + sLogFilePath = path; + return true; } +#else + return false; +#endif +} - void Logger::setVerbosity(Level level) { sVerbosity = level; } - Logger::Level Logger::getVerbosity() { return sVerbosity; } +void Logger::setVerbosity(Level level) +{ + sVerbosity = level; +} +Logger::Level Logger::getVerbosity() +{ + return sVerbosity; +} - void Logger::setOutputs(OutputFlags outputs) { sOutputs = outputs; } - Logger::OutputFlags Logger::getOutputs() { return sOutputs; } +void Logger::setOutputs(OutputFlags outputs) +{ + sOutputs = outputs; +} +Logger::OutputFlags Logger::getOutputs() +{ + return sOutputs; +} - const std::filesystem::path& Logger::getLogFilePath() { return sLogFilePath; } +const std::filesystem::path& Logger::getLogFilePath() +{ + return sLogFilePath; } +} // namespace Falcor diff --git a/Source/Falcor/Utils/Logger.h b/Source/Falcor/Utils/Logger.h index b9777f6bb..5a76f27a6 100644 --- a/Source/Falcor/Utils/Logger.h +++ b/Source/Falcor/Utils/Logger.h @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -35,144 +35,151 @@ namespace Falcor { - /** Container class for logging messages. - To enable log messages, make sure FALCOR_ENABLE_LOGGER is set to `1` in FalcorConfig.h. - Messages are only printed to the selected outputs if they match the verbosity level. - */ - class FALCOR_API Logger +/** + * Container class for logging messages. + * To enable log messages, make sure FALCOR_ENABLE_LOGGER is set to `1` in FalcorConfig.h. + * Messages are only printed to the selected outputs if they match the verbosity level. + */ +class FALCOR_API Logger +{ +public: + /// Log message severity. + enum class Level { - public: - /** Log message severity. - */ - enum class Level - { - Disabled, ///< Disable log messages. - Fatal, ///< Fatal messages. - Error, ///< Error messages. - Warning, ///< Warning messages. - Info, ///< Informative messages. - Debug, ///< Debugging messages. - - Count, ///< Keep this last. - }; - - /** Log output. - */ - enum class OutputFlags - { - Console = 0x2, ///< Output to console (stdout/stderr). - File = 0x1, ///< Output to log file. - DebugWindow = 0x4, ///< Output to debug window (if debugger is attached). - }; - - /** Shutdown the logger and close the log file. - */ - static void shutdown(); - - /** Set the logger verbosity. - \param level Log level. - */ - static void setVerbosity(Level level); - - /** Get the logger verbosity. - \return Return the log level. - */ - static Level getVerbosity(); - - /** Set the logger outputs. - \param outputs Log outputs. - */ - static void setOutputs(OutputFlags outputs); - - /** Get the logger outputs. - \return Return the log outputs. - */ - static OutputFlags getOutputs(); - - /** Set the path of the logfile. - Note: This only works if the logfile has not been opened for writing yet. - \param[in] path Logfile path - \return Returns true if path was set, false otherwise. - */ - static bool setLogFilePath(const std::filesystem::path& path); - - /** Get the path of the logfile. - \return Returns the path of the logfile. - */ - static const std::filesystem::path& getLogFilePath(); - - /** Check if the logger is enabled. - */ - static constexpr bool enabled() { return FALCOR_ENABLE_LOGGER != 0; } - - /** Log a message. - \param[in] level Log level. - \param[in] msg Log message. - */ - static void log(Level level, const std::string_view msg); - - private: - Logger() = delete; + Disabled, ///< Disable log messages. + Fatal, ///< Fatal messages. + Error, ///< Error messages. + Warning, ///< Warning messages. + Info, ///< Informative messages. + Debug, ///< Debugging messages. + Count, ///< Keep this last. }; - FALCOR_ENUM_CLASS_OPERATORS(Logger::OutputFlags); - - // We define two types of logging helpers, one taking raw strings, - // the other taking formatted strings. We don't want string formatting and - // errors being thrown due to missing arguments when passing raw strings. - - inline void logDebug(const std::string_view msg) + /// Log output. + enum class OutputFlags { - Logger::log(Logger::Level::Debug, msg); - } + Console = 0x2, ///< Output to console (stdout/stderr). + File = 0x1, ///< Output to log file. + DebugWindow = 0x4, ///< Output to debug window (if debugger is attached). + }; - template - inline void logDebug(fmt::format_string format, Args&&... args) - { - Logger::log(Logger::Level::Debug, fmt::format(format, std::forward(args)...)); - } + /** + * Shutdown the logger and close the log file. + */ + static void shutdown(); + + /** + * Set the logger verbosity. + * @param level Log level. + */ + static void setVerbosity(Level level); + + /** + * Get the logger verbosity. + * @return Return the log level. + */ + static Level getVerbosity(); + + /** + * Set the logger outputs. + * @param outputs Log outputs. + */ + static void setOutputs(OutputFlags outputs); + + /** + * Get the logger outputs. + * @return Return the log outputs. + */ + static OutputFlags getOutputs(); + + /** + * Set the path of the logfile. + * Note: This only works if the logfile has not been opened for writing yet. + * @param[in] path Logfile path + * @return Returns true if path was set, false otherwise. + */ + static bool setLogFilePath(const std::filesystem::path& path); + + /** + * Get the path of the logfile. + * @return Returns the path of the logfile. + */ + static const std::filesystem::path& getLogFilePath(); + + /** + * Check if the logger is enabled. + */ + static constexpr bool enabled() { return FALCOR_ENABLE_LOGGER != 0; } + + /** + * Log a message. + * @param[in] level Log level. + * @param[in] msg Log message. + */ + static void log(Level level, const std::string_view msg); + +private: + Logger() = delete; +}; + +FALCOR_ENUM_CLASS_OPERATORS(Logger::OutputFlags); + +// We define two types of logging helpers, one taking raw strings, +// the other taking formatted strings. We don't want string formatting and +// errors being thrown due to missing arguments when passing raw strings. + +inline void logDebug(const std::string_view msg) +{ + Logger::log(Logger::Level::Debug, msg); +} - inline void logInfo(const std::string_view msg) - { - Logger::log(Logger::Level::Info, msg); - } +template +inline void logDebug(fmt::format_string format, Args&&... args) +{ + Logger::log(Logger::Level::Debug, fmt::format(format, std::forward(args)...)); +} - template - inline void logInfo(fmt::format_string format, Args&&... args) - { - Logger::log(Logger::Level::Info, fmt::format(format, std::forward(args)...)); - } +inline void logInfo(const std::string_view msg) +{ + Logger::log(Logger::Level::Info, msg); +} - inline void logWarning(const std::string_view msg) - { - Logger::log(Logger::Level::Warning, msg); - } +template +inline void logInfo(fmt::format_string format, Args&&... args) +{ + Logger::log(Logger::Level::Info, fmt::format(format, std::forward(args)...)); +} - template - inline void logWarning(fmt::format_string format, Args&&... args) - { - Logger::log(Logger::Level::Warning, fmt::format(format, std::forward(args)...)); - } +inline void logWarning(const std::string_view msg) +{ + Logger::log(Logger::Level::Warning, msg); +} - inline void logError(const std::string_view msg) - { - Logger::log(Logger::Level::Error, msg); - } +template +inline void logWarning(fmt::format_string format, Args&&... args) +{ + Logger::log(Logger::Level::Warning, fmt::format(format, std::forward(args)...)); +} - template - inline void logError(fmt::format_string format, Args&&... args) - { - Logger::log(Logger::Level::Error, fmt::format(format, std::forward(args)...)); - } +inline void logError(const std::string_view msg) +{ + Logger::log(Logger::Level::Error, msg); +} - inline void logFatal(const std::string_view msg) - { - Logger::log(Logger::Level::Fatal, msg); - } +template +inline void logError(fmt::format_string format, Args&&... args) +{ + Logger::log(Logger::Level::Error, fmt::format(format, std::forward(args)...)); +} - template - inline void logFatal(fmt::format_string format, Args&&... args) - { - Logger::log(Logger::Level::Fatal, fmt::format(format, std::forward(args)...)); - } +inline void logFatal(const std::string_view msg) +{ + Logger::log(Logger::Level::Fatal, msg); +} + +template +inline void logFatal(fmt::format_string format, Args&&... args) +{ + Logger::log(Logger::Level::Fatal, fmt::format(format, std::forward(args)...)); } +} // namespace Falcor diff --git a/Source/Falcor/Utils/Math/AABB.cpp b/Source/Falcor/Utils/Math/AABB.cpp index 71ca2b46f..39f8b94a8 100644 --- a/Source/Falcor/Utils/Math/AABB.cpp +++ b/Source/Falcor/Utils/Math/AABB.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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,53 +31,58 @@ namespace Falcor { - FALCOR_SCRIPT_BINDING(AABB) - { - using namespace pybind11::literals; +FALCOR_SCRIPT_BINDING(AABB) +{ + using namespace pybind11::literals; + + pybind11::class_ aabb(m, "AABB"); - pybind11::class_ aabb(m, "AABB"); + aabb.def(pybind11::init<>()); + aabb.def(pybind11::init(), "p"_a); + aabb.def(pybind11::init(), "min_point"_a, "max_point"_a); - aabb.def(pybind11::init<>()); - aabb.def(pybind11::init(), "p"_a); - aabb.def(pybind11::init(), "pmin"_a, "pmax"_a); + aabb.def( + "__repr__", + [](const AABB& aabb) + { + return "AABB(min_point=" + std::string(pybind11::repr(pybind11::cast(aabb.minPoint))) + + ", max_point=" + std::string(pybind11::repr(pybind11::cast(aabb.maxPoint))) + ")"; + } + ); + aabb.def( + "__str__", + [](const AABB& aabb) + { + return "[" + std::string(pybind11::str(pybind11::cast(aabb.minPoint))) + ", " + + std::string(pybind11::str(pybind11::cast(aabb.maxPoint))) + "]"; + } + ); - aabb.def("__repr__", [] (const AABB& aabb) { - return - "AABB(minPoint=" + - std::string(pybind11::repr(pybind11::cast(aabb.minPoint))) + - ", maxPoint=" + - std::string(pybind11::repr(pybind11::cast(aabb.maxPoint))) + - ")"; - }); - aabb.def("__str__", [] (const AABB& aabb) { - return - "[" + - std::string(pybind11::str(pybind11::cast(aabb.minPoint))) + - ", " + - std::string(pybind11::str(pybind11::cast(aabb.maxPoint))) + - "]"; - }); + aabb.def_readwrite("min_point", &AABB::minPoint); + aabb.def_readwrite("max_point", &AABB::maxPoint); - aabb.def_readwrite("minPoint", &AABB::minPoint); - aabb.def_readwrite("maxPoint", &AABB::maxPoint); + aabb.def_property_readonly("valid", &AABB::valid); + aabb.def_property_readonly("center", &AABB::center); + aabb.def_property_readonly("extent", &AABB::extent); + aabb.def_property_readonly("area", &AABB::area); + aabb.def_property_readonly("volume", &AABB::volume); + aabb.def_property_readonly("radius", &AABB::radius); - aabb.def_property_readonly("valid", &AABB::valid); - aabb.def_property_readonly("center", &AABB::center); - aabb.def_property_readonly("extent", &AABB::extent); - aabb.def_property_readonly("area", &AABB::area); - aabb.def_property_readonly("volume", &AABB::volume); - aabb.def_property_readonly("radius", &AABB::radius); + aabb.def("invalidate", &AABB::invalidate); + aabb.def("include", pybind11::overload_cast(&AABB::include), "p"_a); + aabb.def("include", pybind11::overload_cast(&AABB::include), "b"_a); + aabb.def("intersection", &AABB::intersection); - aabb.def("invalidate", &AABB::invalidate); - aabb.def("include", pybind11::overload_cast(&AABB::include), "p"_a); - aabb.def("include", pybind11::overload_cast(&AABB::include), "b"_a); - aabb.def("intersection", &AABB::intersection); + aabb.def(pybind11::self == pybind11::self); + aabb.def(pybind11::self != pybind11::self); + aabb.def(pybind11::self | pybind11::self); + aabb.def(pybind11::self |= pybind11::self); + aabb.def(pybind11::self & pybind11::self); + aabb.def(pybind11::self &= pybind11::self); - aabb.def(pybind11::self == pybind11::self); - aabb.def(pybind11::self != pybind11::self); - aabb.def(pybind11::self | pybind11::self); - aabb.def(pybind11::self |= pybind11::self); - aabb.def(pybind11::self & pybind11::self); - aabb.def(pybind11::self &= pybind11::self); - } + // PYTHONDEPRECATED BEGIN + aabb.def_readwrite("minPoint", &AABB::minPoint); + aabb.def_readwrite("maxPoint", &AABB::maxPoint); + // PYTHONDEPRECATED END } +} // namespace Falcor diff --git a/Source/Falcor/Utils/Math/AABB.h b/Source/Falcor/Utils/Math/AABB.h index b90da77f2..22c5cb46c 100644 --- a/Source/Falcor/Utils/Math/AABB.h +++ b/Source/Falcor/Utils/Math/AABB.h @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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,220 +34,180 @@ namespace Falcor { - /** Axis-aligned bounding box (AABB) stored by its min/max points. +/** + * Axis-aligned bounding box (AABB) stored by its min/max points. + * + * The user is responsible for checking the validity of returned AABBs. + * There is an equivalent GPU-side implementation in the AABB.slang module. + */ +struct AABB +{ + float3 minPoint = float3(std::numeric_limits::infinity()); ///< Minimum point. + float3 maxPoint = float3(-std::numeric_limits::infinity()); ///< Maximum point. If any minPoint > maxPoint the box is invalid. + + /// Construct bounding box initialized to +/-inf. + AABB() = default; + + /// Construct bounding box initialized to single point. + AABB(const float3& p) : minPoint(p), maxPoint(p) {} + + /// Construct bounding box initialized to min/max point. + AABB(const float3& pmin, const float3& pmax) : minPoint(pmin), maxPoint(pmax) {} + + /// Set box to single point. + void set(const float3& p) { minPoint = maxPoint = p; } + + /// Set the box corners explicitly. + void set(const float3& pmin, const float3& pmax) + { + minPoint = pmin; + maxPoint = pmax; + } + + /// Invalidates the box. + void invalidate() + { + minPoint = float3(std::numeric_limits::infinity()); + maxPoint = float3(-std::numeric_limits::infinity()); + } + + /// 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; } + + /// Grows the box to include the point p. + AABB& include(const float3& p) + { + minPoint = min(minPoint, p); + maxPoint = max(maxPoint, p); + return *this; + } + + /// Grows the box to include another box. + AABB& include(const AABB& b) + { + minPoint = min(minPoint, b.minPoint); + maxPoint = max(maxPoint, b.maxPoint); + return *this; + } + + /// Make the box be the intersection between this and another box. + AABB& intersection(const AABB& b) + { + minPoint = max(minPoint, b.minPoint); + maxPoint = min(maxPoint, b.maxPoint); + return *this; + } + + /// Returns true if the two AABBs have any overlap. + bool overlaps(AABB b) + { + b.intersection(*this); + return b.valid() && b.volume() > 0.f; + } + + /// Returns true if the AABB `b` is fully contained within this AABB. + bool contains(const AABB& b) + { + AABB temp = *this; + return temp.include(b) == *this; + } + + /** + * Returns the box center. + * @return Center of the box if valid, undefined otherwise. + */ + float3 center() const { return (minPoint + maxPoint) * 0.5f; } + + /** + * Returns the box extent. + * @return Size of the box if valid, undefined otherwise. + */ + float3 extent() const { return maxPoint - minPoint; } + + /** + * Returns the surface area of the box. + * @return Surface area if box is valid, undefined otherwise. + */ + float area() const + { + 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() const + { + 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() const { return 0.5f * length(extent()); } + + /** + * Calculates the bounding box transformed by a matrix. + * @param[in] mat Transform matrix + * @return Bounding box after transformation. + */ + AABB transform(const float4x4& mat) const + { + if (!valid()) + return {}; + + float3 xa = mat.getCol(0).xyz() * minPoint.x; + float3 xb = mat.getCol(0).xyz() * maxPoint.x; + float3 xMin = min(xa, xb); + float3 xMax = max(xa, xb); + + float3 ya = mat.getCol(1).xyz() * minPoint.y; + float3 yb = mat.getCol(1).xyz() * maxPoint.y; + float3 yMin = min(ya, yb); + float3 yMax = max(ya, yb); + + float3 za = mat.getCol(2).xyz() * minPoint.z; + float3 zb = mat.getCol(2).xyz() * maxPoint.z; + float3 zMin = min(za, zb); + float3 zMax = max(za, zb); + + float3 newMin = xMin + yMin + zMin + mat.getCol(3).xyz(); + float3 newMax = xMax + yMax + zMax + mat.getCol(3).xyz(); + + return AABB(newMin, newMax); + } + + /// Checks whether two bounding boxes are equal. + bool operator==(const AABB& rhs) const { return all(minPoint == rhs.minPoint) && all(maxPoint == rhs.maxPoint); } + + /// Checks whether two bounding boxes are not equal. + bool operator!=(const AABB& rhs) const { return any(minPoint != rhs.minPoint) || any(maxPoint != rhs.maxPoint); } + + /// Union of two boxes. + AABB& operator|=(const AABB& rhs) { return include(rhs); } + + /// Union of two boxes. + AABB operator|(const AABB& rhs) const + { + AABB bb = *this; + return bb |= rhs; + } + + /// Intersection of two boxes. + AABB& operator&=(const AABB& rhs) { return intersection(rhs); } - The user is responsible for checking the validity of returned AABBs. - There is an equivalent GPU-side implementation in the AABB.slang module. - */ - struct AABB + /// Intersection of two boxes. + AABB operator&(const AABB& rhs) const { - float3 minPoint = float3(std::numeric_limits::infinity()); ///< Minimum point. - float3 maxPoint = float3(-std::numeric_limits::infinity()); ///< Maximum point. If any minPoint > maxPoint the box is invalid. - - /** Construct bounding box initialized to +/-inf. - */ - AABB() = default; - - /** Construct bounding box initialized to single point. - */ - AABB(const float3& p) : minPoint(p), maxPoint(p) {} - - /** Construct bounding box initialized to min/max point. - */ - AABB(const float3& pmin, const float3& pmax) : minPoint(pmin), maxPoint(pmax) {} - - /** Set box to single point. - */ - void set(const float3& p) { minPoint = maxPoint = p; } - - /** Set the box corners explicitly. - */ - void set(const float3& pmin, const float3& pmax) - { - minPoint = pmin; - maxPoint = pmax; - } - - /** Invalidates the box. - */ - void invalidate() - { - minPoint = float3(std::numeric_limits::infinity()); - maxPoint = float3(-std::numeric_limits::infinity()); - } - - /** 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; - } - - /** Grows the box to include the point p. - */ - AABB& include(const float3& p) - { - minPoint = min(minPoint, p); - maxPoint = max(maxPoint, p); - return *this; - } - - /** Grows the box to include another box. - */ - AABB& include(const AABB& b) - { - minPoint = min(minPoint, b.minPoint); - maxPoint = max(maxPoint, b.maxPoint); - return *this; - } - - /** Make the box be the intersection between this and another box. - */ - AABB& intersection(const AABB& b) - { - minPoint = glm::max(minPoint, b.minPoint); - maxPoint = glm::min(maxPoint, b.maxPoint); - return *this; - } - - /** Returns true if the two AABBs have any overlap. - */ - bool overlaps(AABB b) - { - b.intersection(*this); - return b.valid() && b.volume() > 0.f; - } - - /** Returns true if the AABB `b` is fully contained within this AABB. - */ - bool contains(const AABB& b) - { - AABB temp = *this; - return temp.include(b) == *this; - } - - /** Returns the box center. - \return Center of the box if valid, undefined otherwise. - */ - float3 center() const - { - return (minPoint + maxPoint) * 0.5f; - } - - /** Returns the box extent. - \return Size of the box if valid, undefined otherwise. - */ - float3 extent() const - { - return maxPoint - minPoint; - } - - /** Returns the surface area of the box. - \return Surface area if box is valid, undefined otherwise. - */ - float area() const - { - 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() const - { - 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() const - { - return 0.5f * glm::length(extent()); - } - - /** Calculates the bounding box transformed by a matrix. - \param[in] mat Transform matrix - \return Bounding box after transformation. - */ - AABB transform(const rmcv::mat4& rmcv_mat) const - { - if (!valid()) return {}; - - auto mat = rmcv::toGLM(rmcv_mat); - - float3 xa = float3(mat[0] * minPoint.x); - float3 xb = float3(mat[0] * maxPoint.x); - float3 xMin = glm::min(xa, xb); - float3 xMax = glm::max(xa, xb); - - float3 ya = float3(mat[1] * minPoint.y); - float3 yb = float3(mat[1] * maxPoint.y); - float3 yMin = glm::min(ya, yb); - float3 yMax = glm::max(ya, yb); - - float3 za = float3(mat[2] * minPoint.z); - float3 zb = float3(mat[2] * maxPoint.z); - float3 zMin = glm::min(za, zb); - float3 zMax = glm::max(za, zb); - - float3 newMin = xMin + yMin + zMin + float3(mat[3]); - float3 newMax = xMax + yMax + zMax + float3(mat[3]); - - return AABB(newMin, newMax); - } - - /** Checks whether two bounding boxes are equal. - */ - bool operator== (const AABB& rhs) const - { - return minPoint == rhs.minPoint && maxPoint == rhs.maxPoint; - } - - /** Checks whether two bounding boxes are not equal. - */ - bool operator!= (const AABB& rhs) const - { - return minPoint != rhs.minPoint || maxPoint != rhs.maxPoint; - } - - /** Union of two boxes. - */ - AABB& operator|= (const AABB& rhs) - { - return include(rhs); - } - - /** Union of two boxes. - */ - AABB operator| (const AABB& rhs) const - { - AABB bb = *this; - return bb |= rhs; - } - - /** Intersection of two boxes. - */ - AABB& operator&= (const AABB& rhs) - { - return intersection(rhs); - } - - /** Intersection of two boxes. - */ - AABB operator& (const AABB& rhs) const - { - AABB bb = *this; - return bb &= rhs; - } - - /** Conversion to RtAABB. - */ - explicit operator RtAABB() const - { - return { minPoint, maxPoint }; - } - }; -} + AABB bb = *this; + return bb &= rhs; + } + + /// Conversion to RtAABB. + explicit operator RtAABB() const { return {minPoint, maxPoint}; } +}; +} // namespace Falcor diff --git a/Source/Falcor/Utils/Math/AABB.slang b/Source/Falcor/Utils/Math/AABB.slang index 64c7bfb6a..4c7a9f49d 100644 --- a/Source/Falcor/Utils/Math/AABB.slang +++ b/Source/Falcor/Utils/Math/AABB.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-21, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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,134 +27,137 @@ **************************************************************************/ #include "Utils/Math/MathConstants.slangh" -/** Axis-aligned bounding box (AABB). -*/ +/** + * Axis-aligned bounding box (AABB). + */ struct AABB { - float3 minPoint; ///< Minimum point. - float3 maxPoint; ///< Maximum point. If any minPoint > maxPoint the box is invalid. + float3 minPoint; ///< Minimum point. + float3 maxPoint; ///< Maximum point. If any minPoint > maxPoint the box is invalid. - /** Initializes an AABB. - Note if minPoint > maxPoint in any component the box is invalid. - */ + /** + * Initializes an AABB. + * Note if minPoint > maxPoint in any component the box is invalid. + */ __init(float3 minPoint, float3 maxPoint) { this.minPoint = minPoint; this.maxPoint = maxPoint; } - /** Set box to single point. - */ - [mutating] void set(float3 p) + /** + * 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) + /** + * 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() + /** + * 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); - } + /** + * 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) + /** + * 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) + /** + * 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. - */ + /** + * 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. - */ + /** + * 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. - */ + /** + * 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; @@ -163,11 +166,12 @@ struct AABB 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. - */ + /** + * 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; diff --git a/Source/Falcor/Utils/Math/BitTricks.slang b/Source/Falcor/Utils/Math/BitTricks.slang index 2d02a550d..0037b3ef3 100644 --- a/Source/Falcor/Utils/Math/BitTricks.slang +++ b/Source/Falcor/Utils/Math/BitTricks.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-21, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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,32 +26,33 @@ # 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/ +/** + * 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. + */ - 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. -*/ +/** + * 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 + uint x = v.x & 0x0000ffff; // x = ---- ---- ---- ---- fedc ba98 7654 3210 + 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 + uint y = v.y & 0x0000ffff; y = (y | (y << 8)) & 0x00FF00FF; y = (y | (y << 4)) & 0x0F0F0F0F; y = (y | (y << 2)) & 0x33333333; @@ -60,61 +61,65 @@ uint interleave_32bit(uint2 v) 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. -*/ +/** + * 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 + 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. -*/ +/** + * 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 + 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. -*/ +/** + * 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 + 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. -*/ +/** + * 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 + 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/Source/Falcor/Utils/Math/Common.h b/Source/Falcor/Utils/Math/Common.h index ce6a5a543..d0d363ab3 100644 --- a/Source/Falcor/Utils/Math/Common.h +++ b/Source/Falcor/Utils/Math/Common.h @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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,56 +27,28 @@ **************************************************************************/ #pragma once -#include #include namespace Falcor { - /** Clamps a value within a range. - \param[in] val Value to clamp - \param[in] minVal Low end to clamp to - \param[in] maxVal High end to clamp to - \return Result - */ - template - inline T clamp(const T& val, const T& minVal, const T& maxVal) - { - return std::min(std::max(val, minVal), maxVal); - } - - /** Linearly interpolate two values. - \param[in] a The first value. - \param[in] b The second value. - \param[in] t Interpolation weight. - \return (1-t) * a + t * b. - */ - template - inline typename std::enable_if, T>::type lerp(const T& a, const T& b, const U& t) - { - return (U(1) - t) * a + t * b; - } - - /** Returns whether an integer number is a power of two. - */ - template - constexpr typename std::enable_if::value, bool>::type isPowerOf2(T a) - { - return (a & (a - (T)1)) == 0; - } +/// Returns whether an integer number is a power of two. +template +constexpr typename std::enable_if::value, bool>::type isPowerOf2(T a) +{ + return (a & (a - (T)1)) == 0; +} - /** Divide an a by b and round up to the next integer. - */ - template - constexpr T div_round_up(T a, T b) - { - return (a + b - T(1)) / b; - } +/// Divide an a by b and round up to the next integer. +template +constexpr T div_round_up(T a, T b) +{ + return (a + b - T(1)) / b; +} - /** Helper to align an integer value to a given alignment. - */ - template - constexpr typename std::enable_if::value, T>::type align_to(T alignment, T value) - { - return ((value + alignment - T(1)) / alignment) * alignment; - } +/// Helper to align an integer value to a given alignment. +template +constexpr typename std::enable_if::value, T>::type align_to(T alignment, T value) +{ + return ((value + alignment - T(1)) / alignment) * alignment; } +} // namespace Falcor diff --git a/Source/Falcor/Utils/Math/CubicSpline.h b/Source/Falcor/Utils/Math/CubicSpline.h index 727ae64b4..eb3ad1da4 100644 --- a/Source/Falcor/Utils/Math/CubicSpline.h +++ b/Source/Falcor/Utils/Math/CubicSpline.h @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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,157 +31,163 @@ namespace Falcor { - template - class CubicSpline +template +class CubicSpline +{ +public: + CubicSpline() = default; + void clear() { mCoefficient.clear(); } + + /** + * Creates a position-based cubic spline. + * @param[in] controlPoints Array of control points + * @param[in] pointCount Number of control points + */ + CubicSpline(const T* controlPoints, uint32_t pointCount) { setup(controlPoints, pointCount); } + + /** + * Create a position and time-based cubic spline + * @param[in] controlPoints Array of control points + * @param[in] pointCount Number of control points + * @param[in] durations Array containing durations/intervals for each control point + */ + CubicSpline(const T* points, uint32_t pointCount, float const* durations) { setup(points, pointCount, durations); } + + /** + * Creates a position-based cubic spline. + * @param[in] controlPoints Array of control points + * @param[in] pointCount Number of control points + */ + CubicSpline& setup(const T* controlPoints, uint32_t pointCount) { - public: - CubicSpline() = default; - void clear() { mCoefficient.clear(); } - - /** Creates a position-based cubic spline. - \param[in] controlPoints Array of control points - \param[in] pointCount Number of control points - */ - CubicSpline(const T* controlPoints, uint32_t pointCount) + mCoefficient.clear(); + + // The following code is based on the article from http://graphicsrunner.blogspot.co.uk/2008/05/camera-animation-part-ii.html + static const T kHalf = T(0.5f); + static const T kOne = T(1); + static const T kTwo = T(2); + static const T kThree = T(3); + static const T kFour = T(4); + + auto gamma = [&](unsigned i) -> T& { return mCoefficient[i].a; }; + auto delta = [&](unsigned i) -> T& { return mCoefficient[i].c; }; + auto D = [&](unsigned i) -> T& { return mCoefficient[i].b; }; + + mCoefficient.resize(pointCount); + // Calculate Gamma =: mCoefficient.a + gamma(0) = kHalf; + for (uint32_t i = 1; i < pointCount - 1; i++) + { + gamma(i) = kOne / (kFour - gamma(i - 1)); + } + gamma(pointCount - 1) = kOne / (kTwo - gamma(pointCount - 2)); + + // Calculate Delta := mCoefficient.c (b will be used straight for D) + delta(0) = kThree * (controlPoints[1] - controlPoints[0]) * gamma(0); + + for (uint32_t i = 1; i < pointCount; i++) { - setup(controlPoints, pointCount); + uint32_t index = (i == (pointCount - 1)) ? i : i + 1; + delta(i) = (kThree * (controlPoints[index] - controlPoints[i - 1]) - delta(i - 1)) * gamma(i); } - /** Create a position and time-based cubic spline - \param[in] controlPoints Array of control points - \param[in] pointCount Number of control points - \param[in] durations Array containing durations/intervals for each control point - */ - CubicSpline(const T* points, uint32_t pointCount, float const* durations) + // Calculate D := mCoefficient.b + D(pointCount - 1) = delta(pointCount - 1); + + for (int32_t i = int32_t(pointCount - 2); i >= 0; i--) { - setup(points, pointCount, durations); + D(i) = delta(i) - gamma(i) * D(i + 1); } - /** Creates a position-based cubic spline. - \param[in] controlPoints Array of control points - \param[in] pointCount Number of control points - */ - CubicSpline& setup(const T* controlPoints, uint32_t pointCount) + // Calculate the coefficients + for (uint32_t i = 0; i < pointCount - 1; i++) { - mCoefficient.clear(); + mCoefficient[i].a = controlPoints[i]; + // mCoefficient[i].b = D[i]; no-op + mCoefficient[i].c = kThree * (controlPoints[i + 1] - controlPoints[i]) - kTwo * D(i) - D(i + 1); + mCoefficient[i].d = kTwo * (controlPoints[i] - controlPoints[i + 1]) + D(i) + D(i + 1); + } - // The following code is based on the article from http://graphicsrunner.blogspot.co.uk/2008/05/camera-animation-part-ii.html - static const T kHalf = T(0.5f); - static const T kOne = T(1); - static const T kTwo = T(2); - static const T kThree = T(3); - static const T kFour = T(4); + // Resize from cache size to the final size + mCoefficient.resize(pointCount - 1); - auto gamma = [&](unsigned i) -> T& { return mCoefficient[i].a; }; - auto delta = [&](unsigned i) -> T& { return mCoefficient[i].c; }; - auto D = [&](unsigned i) -> T& { return mCoefficient[i].b; }; + return *this; + } + /** + * Create a position and time-based cubic spline + * @param[in] controlPoints Array of control points + * @param[in] pointCount Number of control points + * @param[in] durations Array containing durations/intervals for each control point + */ + CubicSpline& setup(const T* points, uint32_t pointCount, float const* durations) + { + mCoefficient.clear(); + // The following code is based on the article from http://graphicsrunner.blogspot.co.uk/2008/05/camera-animation-part-ii.html + // http://math.stackexchange.com/questions/62360/natural-cubic-splines-vs-piecewise-hermite-splines + // https://en.wikipedia.org/wiki/Tridiagonal_matrix_algorithm + if (pointCount >= 2) + { mCoefficient.resize(pointCount); + // Calculate Gamma =: mCoefficient.a - gamma(0) = kHalf; - for(uint32_t i = 1; i < pointCount - 1; i++) + mCoefficient[0].a = T(.5f); + for (size_t i = 1; i < pointCount - 1; i++) { - gamma(i) = kOne / (kFour - gamma(i - 1)); + mCoefficient[i].a = durations[i] / (T(2.f) * (durations[i - 1] + durations[i]) - durations[i - 1] * mCoefficient[i - 1].a); } - gamma(pointCount - 1) = kOne / (kTwo - gamma(pointCount - 2)); - - // Calculate Delta := mCoefficient.c (b will be used straight for D) - delta(0) = kThree * (controlPoints[1] - controlPoints[0]) * gamma(0); + mCoefficient[pointCount - 1].a = 1.0f / (T(2) - mCoefficient[pointCount - 2].a); - for(uint32_t i = 1; i < pointCount; i++) + // Calculate Delta =: mCoefficient.b + mCoefficient[0].b = T(3) / durations[0] * (points[1] - points[0]) * mCoefficient[0].a; + for (size_t i = 1; i < pointCount - 1; i++) { - uint32_t index = (i == (pointCount - 1)) ? i : i + 1; - delta(i) = (kThree * (controlPoints[index] - controlPoints[i - 1]) - delta(i - 1)) * gamma(i); + mCoefficient[i].b = (T(3) / (durations[i - 1] * durations[i]) * + (durations[i - 1] * durations[i - 1] * (points[i + 1] - points[i]) + + durations[i] * durations[i] * (points[i] - points[i - 1])) - + durations[i - 1] * mCoefficient[i - 1].b) * + mCoefficient[i].a / durations[i]; } + mCoefficient[pointCount - 1].b = + (T(3) / durations[pointCount - 2] * (points[pointCount - 1] - points[pointCount - 2]) - mCoefficient[pointCount - 2].b) * + mCoefficient[pointCount - 1].a; - // Calculate D := mCoefficient.b - D(pointCount - 1) = delta(pointCount - 1); - - for(int32_t i = int32_t(pointCount - 2); i >= 0; i--) + // Calculate D := mCoefficient.d + mCoefficient[pointCount - 1].d = mCoefficient[pointCount - 1].b; + for (size_t i = pointCount - 1; i-- > 0;) { - D(i) = delta(i) - gamma(i) * D(i + 1); + mCoefficient[i].d = mCoefficient[i].b - mCoefficient[i].a * mCoefficient[i + 1].d; } - // Calculate the coefficients - for(uint32_t i = 0; i < pointCount - 1; i++) + // Calculate actual spline + for (size_t i = 0; i < pointCount - 1; i++) { - mCoefficient[i].a = controlPoints[i]; - //mCoefficient[i].b = D[i]; no-op - mCoefficient[i].c = kThree * (controlPoints[i + 1] - controlPoints[i]) - kTwo * D(i) - D(i + 1); - mCoefficient[i].d = kTwo * (controlPoints[i] - controlPoints[i + 1]) + D(i) + D(i + 1); + mCoefficient[i].a = points[i]; + mCoefficient[i].b = mCoefficient[i].d * durations[i]; + mCoefficient[i].c = + T(3) * (points[i + 1] - points[i]) - T(2) * mCoefficient[i].d * durations[i] - mCoefficient[i + 1].d * durations[i]; + mCoefficient[i].d = + T(2) * (points[i] - points[i + 1]) + mCoefficient[i].d * durations[i] + mCoefficient[i + 1].d * durations[i]; } - // Resize from cache size to the final size mCoefficient.resize(pointCount - 1); - - return *this; } - /** Create a position and time-based cubic spline - \param[in] controlPoints Array of control points - \param[in] pointCount Number of control points - \param[in] durations Array containing durations/intervals for each control point - */ - CubicSpline& setup(const T* points, uint32_t pointCount, float const* durations) - { - mCoefficient.clear(); - // The following code is based on the article from http://graphicsrunner.blogspot.co.uk/2008/05/camera-animation-part-ii.html - // http://math.stackexchange.com/questions/62360/natural-cubic-splines-vs-piecewise-hermite-splines - // https://en.wikipedia.org/wiki/Tridiagonal_matrix_algorithm - if (pointCount >= 2) - { - mCoefficient.resize(pointCount); - - // Calculate Gamma =: mCoefficient.a - mCoefficient[0].a = T(.5f); - for (size_t i = 1; i < pointCount - 1; i++) { - mCoefficient[i].a = durations[i] / (T(2.f) * (durations[i - 1] + durations[i]) - durations[i - 1] * mCoefficient[i - 1].a); - } - mCoefficient[pointCount - 1].a = 1.0f / (T(2) - mCoefficient[pointCount - 2].a); - - // Calculate Delta =: mCoefficient.b - mCoefficient[0].b = T(3) / durations[0] * (points[1] - points[0]) * mCoefficient[0].a; - for (size_t i = 1; i < pointCount - 1; i++) { - mCoefficient[i].b = (T(3) / (durations[i - 1] * durations[i]) * ( - durations[i - 1] * durations[i - 1] * (points[i + 1] - points[i]) - + durations[i] * durations[i] * (points[i] - points[i - 1]) - ) - - durations[i - 1] * mCoefficient[i - 1].b) * mCoefficient[i].a / durations[i]; - } - mCoefficient[pointCount - 1].b = (T(3) / durations[pointCount - 2] * (points[pointCount - 1] - points[pointCount - 2]) - mCoefficient[pointCount - 2].b) * mCoefficient[pointCount - 1].a; - - // Calculate D := mCoefficient.d - mCoefficient[pointCount - 1].d = mCoefficient[pointCount - 1].b; - for (size_t i = pointCount - 1; i-- > 0;) { - mCoefficient[i].d = mCoefficient[i].b - mCoefficient[i].a * mCoefficient[i + 1].d; - } - - // Calculate actual spline - for (size_t i = 0; i < pointCount - 1; i++) { - mCoefficient[i].a = points[i]; - mCoefficient[i].b = mCoefficient[i].d * durations[i]; - mCoefficient[i].c = T(3) * (points[i + 1] - points[i]) - T(2) * mCoefficient[i].d * durations[i] - mCoefficient[i + 1].d * durations[i]; - mCoefficient[i].d = T(2) * (points[i] - points[i + 1]) + mCoefficient[i].d * durations[i] + mCoefficient[i + 1].d * durations[i]; - } - - mCoefficient.resize(pointCount - 1); - } - - return *this; - } + return *this; + } - T interpolate(uint32_t section, float point) const - { - const CubicCoeff& coeff = mCoefficient[section]; - T result = (((coeff.d * point) + coeff.c) * point + coeff.b) * point + coeff.a; - return result; - } + T interpolate(uint32_t section, float point) const + { + const CubicCoeff& coeff = mCoefficient[section]; + T result = (((coeff.d * point) + coeff.c) * point + coeff.b) * point + coeff.a; + return result; + } - private: - struct CubicCoeff - { - T a, b, c, d; - }; - std::vector mCoefficient; +private: + struct CubicCoeff + { + T a, b, c, d; }; -} + std::vector mCoefficient; +}; +} // namespace Falcor diff --git a/Source/Falcor/Utils/Math/FNVHash.h b/Source/Falcor/Utils/Math/FNVHash.h index 65f9c3bb1..a9afd5484 100644 --- a/Source/Falcor/Utils/Math/FNVHash.h +++ b/Source/Falcor/Utils/Math/FNVHash.h @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -35,56 +35,62 @@ namespace Falcor { -/** Accumulates Fowler-Noll-Vo hash for inserted data. - To hash multiple items, create one Hash and insert all the items into it if at all possible. - This is superior to hashing the items individually and combining the hashes. +namespace detail +{ +template +struct FNVHashConstants +{}; - \tparam T - type of the storage for the hash, either 32 or 64 unsigned integer +template<> +struct FNVHashConstants +{ + static constexpr uint64_t kOffsetBasis = UINT64_C(4695981039346656037); + static constexpr uint64_t kPrime = UINT64_C(1099511628211); +}; + +template<> +struct FNVHashConstants +{ + static constexpr uint32_t kOffsetBasis = UINT32_C(2166136261); + static constexpr uint32_t kPrime = UINT32_C(16777619); +}; +} // namespace detail + +/** + * Accumulates Fowler-Noll-Vo hash for inserted data. + * To hash multiple items, create one Hash and insert all the items into it if at all possible. + * This is superior to hashing the items individually and combining the hashes. + * + * @tparam T - type of the storage for the hash, either 32 or 64 unsigned integer */ template class FNVHash { - template - struct ConstantTraits - {}; - - template<> - struct ConstantTraits - { - static constexpr uint64_t kOffsetBasis = UINT64_C(4695981039346656037); - static constexpr uint64_t kPrime = UINT64_C(1099511628211); - }; - - template<> - struct ConstantTraits - { - static constexpr uint32_t kOffsetBasis = UINT32_C(2166136261); - static constexpr uint32_t kPrime = UINT32_C(16777619); - }; - public: - static constexpr T kOffsetBasis = ConstantTraits::kOffsetBasis; - static constexpr T kPrime = ConstantTraits::kPrime; + static constexpr T kOffsetBasis = detail::FNVHashConstants::kOffsetBasis; + static constexpr T kPrime = detail::FNVHashConstants::kPrime; - /** Inserts all data between [begin,end) into the hash. - \param[in] begin - \param[in] end + /** + * Inserts all data between [begin,end) into the hash. + * @param[in] begin + * @param[in] end */ void insert(const void* begin, const void* end) { FALCOR_ASSERT(begin <= end); const uint8_t* srcData8 = reinterpret_cast(begin); - for(; srcData8 != end; ++srcData8) + for (; srcData8 != end; ++srcData8) { mHash *= kPrime; mHash ^= *srcData8; } } - /** Inserts all data starting at data and going for size bytes into the hash - \param[in] data - \param[in] size + /** + * Inserts all data starting at data and going for size bytes into the hash + * @param[in] data + * @param[in] size */ void insert(const void* data, size_t size) { @@ -115,5 +121,4 @@ inline uint32_t fnvHashArray32(const void* data, size_t size) return hash.get(); } - } // namespace Falcor diff --git a/Source/Falcor/Utils/Math/FalcorMath.h b/Source/Falcor/Utils/Math/FalcorMath.h index 469439be9..c7f763c0d 100644 --- a/Source/Falcor/Utils/Math/FalcorMath.h +++ b/Source/Falcor/Utils/Math/FalcorMath.h @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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,203 +28,182 @@ #pragma once #include "Vector.h" #include "Matrix.h" +#include "Quaternion.h" #include "Core/Assert.h" #include #include namespace Falcor { - /*! - * \addtogroup Falcor - * @{ - */ - - /** Creates a quaternion representing rotation between 2 vectors - \param[in] from The source vector - \param[in] to The destination vector - */ - inline glm::quat createQuaternionFromVectors(const float3& from, const float3& to) - { - glm::quat quat; - float3 nFrom = glm::normalize(from); - float3 nTo = glm::normalize(to); - - float dot = glm::dot(nFrom, nTo); - dot = std::clamp(dot, -1.0f, 1.0f); - if(dot != 1) - { - float angle = std::acos(dot); - - float3 cross = glm::cross(nFrom, nTo); - float3 axis = glm::normalize(cross); - - quat = glm::angleAxis(angle, axis); - } - - return quat; - } - - /** Calculates a world-space ray direction from a screen-space mouse pos. - \param[in] mousePos Normalized coordinates in the range [0, 1] with (0, 0) being the top-left of the screen. Same coordinate space as MouseEvent. - \param[in] viewMat View matrix from the camera. - \param[in] projMat Projection matrix from the camera. - \return World space ray direction coming from the camera position in the direction of the mouse position - */ - inline float3 mousePosToWorldRay(const float2& mousePos, const rmcv::mat4& viewMat, const rmcv::mat4& projMat) - { - // Convert from [0, 1] to [-1, 1] range - const float x = mousePos.x * 2.0f - 1.0f; +/** + * Calculates a world-space ray direction from a screen-space mouse pos. + * @param[in] mousePos Normalized coordinates in the range [0, 1] with (0, 0) being the top-left of the screen. + * @param[in] viewMat View matrix from the camera. + * @param[in] projMat Projection matrix from the camera. + * @return World space ray direction coming from the camera position in the direction of the mouse position + */ +inline float3 mousePosToWorldRay(const float2& mousePos, const float4x4& viewMat, const float4x4& projMat) +{ + // Convert from [0, 1] to [-1, 1] range + const float x = mousePos.x * 2.0f - 1.0f; #ifdef FALCOR_FLIP_Y - // NDC Y is top-to-bottom - const float y = mousePos.y * 2.0f - 1.0f; + // NDC Y is top-to-bottom + const float y = mousePos.y * 2.0f - 1.0f; #else - // NDC Y is bottom-to-top - const float y = (1.0f - mousePos.y) * 2.0f - 1.0f; + // NDC Y is bottom-to-top + const float y = (1.0f - mousePos.y) * 2.0f - 1.0f; #endif - // NDC/Clip - float4 ray(x, y, -1.0f, 1.0f); + // NDC/Clip + float4 ray(x, y, -1.0f, 1.0f); - // View - ray = rmcv::inverse(projMat) * ray; - ray.z = -1.0f; - ray.w = 0.0f; + // View + ray = mul(inverse(projMat), ray); + ray.z = -1.0f; + ray.w = 0.0f; - // World - return glm::normalize(rmcv::inverse(viewMat) * ray); - } + // World + return normalize(mul(inverse(viewMat), ray)).xyz(); +} - /** Creates a rotation matrix from individual basis vectors. - \param[in] forward Forward vector. - \param[in] up Up vector. - \return 3x3 rotation matrix. - */ - inline rmcv::mat3 createMatrixFromBasis(const float3& forward, const float3& up) - { - float3 f = glm::normalize(forward); - float3 s = glm::normalize(glm::cross(up, forward)); - float3 u = glm::cross(f, s); +/** + * Creates a rotation matrix from individual basis vectors. + * @param[in] forward Forward vector. + * @param[in] up Up vector. + * @return 3x3 rotation matrix. + */ +inline float3x3 createMatrixFromBasis(const float3& forward, const float3& up) +{ + float3 f = normalize(forward); + float3 s = normalize(cross(up, forward)); + float3 u = cross(f, s); - return rmcv::make_mat3_fromCols(s, u, f); - } + return math::matrixFromColumns(s, u, f); +} - /** Creates a rotation matrix from look-at coordinates. - \param[in] position Object's position. - \param[in] target Object's look-at target. - \param[in] up Object's up vector. - \return 3x3 rotation matrix. - */ - inline rmcv::mat3 createMatrixFromLookAt(const float3& position, const float3& target, const float3& up) - { - return createMatrixFromBasis(target - position, up); - } +/** + * Creates a rotation matrix from look-at coordinates. + * @param[in] position Object's position. + * @param[in] target Object's look-at target. + * @param[in] up Object's up vector. + * @return 3x3 rotation matrix. + */ +inline float3x3 createMatrixFromLookAt(const float3& position, const float3& target, const float3& up) +{ + return createMatrixFromBasis(target - position, up); +} - /** Projects a 2D coordinate onto a unit sphere - \param xy The 2D coordinate. if x and y are in the [0,1) range, then a z value can be calculate. Otherwise, xy is normalized and z is zero. - */ - inline float3 project2DCrdToUnitSphere(float2 xy) - { - float xyLengthSquared = glm::dot(xy, xy); - - float z = 0; - if(xyLengthSquared < 1) - { - z = std::sqrt(1 - xyLengthSquared); - } - else - { - xy = glm::normalize(xy); - } - return float3(xy.x, xy.y, z); - } +/** + * Projects a 2D coordinate onto a unit sphere + * @param xy The 2D coordinate. if x and y are in the [0,1) range, then a z value can be calculate. Otherwise, xy is normalized and z is + * zero. + */ +inline float3 project2DCrdToUnitSphere(float2 xy) +{ + float xyLengthSquared = dot(xy, xy); - /** Calculates vertical FOV in radians from camera parameters. - \param[in] focalLength Focal length in mm. - \param[in] frameHeight Height of film/sensor in mm. - */ - inline float focalLengthToFovY(float focalLength, float frameHeight) + float z = 0; + if (xyLengthSquared < 1) { - return 2.0f * std::atan(0.5f * frameHeight / focalLength); + z = std::sqrt(1 - xyLengthSquared); } - - /** Calculates camera focal length from vertical FOV. - \param[in] fovY Vertical FOV in radians. - \param[in] frameHeight Height of film/sensor in mm. - */ - inline float fovYToFocalLength(float fovY, float frameHeight) + else { - return frameHeight / (2.0f * std::tan(0.5f * fovY)); + xy = normalize(xy); } + return float3(xy.x, xy.y, z); +} - /** Calculates camera aperture radius in scene units. - \param[in] fNumber Aperture f-number. - \param[in] focalLength Focal length in mm. - \param[in] sceneUnit Scene unit in meters. - */ - inline float apertureFNumberToRadius(float fNumber, float focalLength, float sceneUnit) - { - FALCOR_ASSERT(fNumber > 0.0f && focalLength > 0.f && sceneUnit > 0.f); - float radius = 0.5f * focalLength / fNumber; // in mm - return radius * 0.001f / sceneUnit; - } +/** + * Calculates vertical FOV in radians from camera parameters. + * @param[in] focalLength Focal length in mm. + * @param[in] frameHeight Height of film/sensor in mm. + */ +inline float focalLengthToFovY(float focalLength, float frameHeight) +{ + return 2.0f * std::atan(0.5f * frameHeight / focalLength); +} - /** Calculates camera aperture f-number from camera parameters. - \param[in] apertureRadius Aperture radius in scene units. - \param[in] focalLength Focal length in mm. - \param[in] sceneUnit Scene unit in meters. - */ - inline float apertureRadiusToFNumber(float apertureRadius, float focalLength, float sceneUnit) - { - FALCOR_ASSERT(focalLength > 0.f && sceneUnit > 0.f); - float radius = apertureRadius * sceneUnit * 1000.f; // in mm - return 0.5f * focalLength / radius; - } +/** + * Calculates camera focal length from vertical FOV. + * @param[in] fovY Vertical FOV in radians. + * @param[in] frameHeight Height of film/sensor in mm. + */ +inline float fovYToFocalLength(float fovY, float frameHeight) +{ + return frameHeight / (2.0f * std::tan(0.5f * fovY)); +} - // Base 2 Van der Corput radical inverse - inline float radicalInverse(uint32_t i) - { - i = (i & 0x55555555) << 1 | (i & 0xAAAAAAAA) >> 1; - i = (i & 0x33333333) << 2 | (i & 0xCCCCCCCC) >> 2; - i = (i & 0x0F0F0F0F) << 4 | (i & 0xF0F0F0F0) >> 4; - i = (i & 0x00FF00FF) << 8 | (i & 0xFF00FF00) >> 8; - i = (i << 16) | (i >> 16); - return float(i) * 2.3283064365386963e-10f; - } +/** + * Calculates camera aperture radius in scene units. + * @param[in] fNumber Aperture f-number. + * @param[in] focalLength Focal length in mm. + * @param[in] sceneUnit Scene unit in meters. + */ +inline float apertureFNumberToRadius(float fNumber, float focalLength, float sceneUnit) +{ + FALCOR_ASSERT(fNumber > 0.0f && focalLength > 0.f && sceneUnit > 0.f); + float radius = 0.5f * focalLength / fNumber; // in mm + return radius * 0.001f / sceneUnit; +} - inline float3 hammersleyUniform(uint32_t i, uint32_t n) - { - float2 uv((float)i / (float)n, radicalInverse(i)); +/** + * Calculates camera aperture f-number from camera parameters. + * @param[in] apertureRadius Aperture radius in scene units. + * @param[in] focalLength Focal length in mm. + * @param[in] sceneUnit Scene unit in meters. + */ +inline float apertureRadiusToFNumber(float apertureRadius, float focalLength, float sceneUnit) +{ + FALCOR_ASSERT(focalLength > 0.f && sceneUnit > 0.f); + float radius = apertureRadius * sceneUnit * 1000.f; // in mm + return 0.5f * focalLength / radius; +} - // Map to radius 1 hemisphere - float phi = uv.y * 2.0f * (float)M_PI; - float t = 1.0f - uv.x; - float s = std::sqrt(1.0f - t * t); - return float3(s * std::cos(phi), s * std::sin(phi), t); - } +/// Base 2 Van der Corput radical inverse +inline float radicalInverse(uint32_t i) +{ + i = (i & 0x55555555) << 1 | (i & 0xAAAAAAAA) >> 1; + i = (i & 0x33333333) << 2 | (i & 0xCCCCCCCC) >> 2; + i = (i & 0x0F0F0F0F) << 4 | (i & 0xF0F0F0F0) >> 4; + i = (i & 0x00FF00FF) << 8 | (i & 0xFF00FF00) >> 8; + i = (i << 16) | (i >> 16); + return float(i) * 2.3283064365386963e-10f; +} - inline float3 hammersleyCosine(uint32_t i, uint32_t n) - { - float2 uv((float)i / (float)n, radicalInverse(i)); +inline float3 hammersleyUniform(uint32_t i, uint32_t n) +{ + float2 uv((float)i / (float)n, radicalInverse(i)); - // Map to radius 1 hemisphere - float phi = uv.y * 2.0f * (float)M_PI; - float t = std::sqrt(1.0f - uv.x); - float s = std::sqrt(1.0f - t * t); - return float3(s * std::cos(phi), s * std::sin(phi), t); - } + // Map to radius 1 hemisphere + float phi = uv.y * 2.0f * (float)M_PI; + float t = 1.0f - uv.x; + float s = std::sqrt(1.0f - t * t); + return float3(s * std::cos(phi), s * std::sin(phi), t); +} - inline float smoothstep(const float t) - { - const float s = std::clamp(t, 0.0f, 1.0f); - return s * s * (3.0f - 2.0f * s); - } +inline float3 hammersleyCosine(uint32_t i, uint32_t n) +{ + float2 uv((float)i / (float)n, radicalInverse(i)); - inline float smoothstep(const float start, const float end, const float t) - { - float s = (t - start) / (end - start); - return smoothstep(s); - } + // Map to radius 1 hemisphere + float phi = uv.y * 2.0f * (float)M_PI; + float t = std::sqrt(1.0f - uv.x); + float s = std::sqrt(1.0f - t * t); + return float3(s * std::cos(phi), s * std::sin(phi), t); +} -/*! @} */ +inline float smoothstep(const float t) +{ + const float s = std::clamp(t, 0.0f, 1.0f); + return s * s * (3.0f - 2.0f * s); } + +inline float smoothstep(const float start, const float end, const float t) +{ + float s = (t - start) / (end - start); + return smoothstep(s); +} + +} // namespace Falcor diff --git a/Source/Falcor/Utils/Math/Float16.cpp b/Source/Falcor/Utils/Math/Float16.cpp new file mode 100644 index 000000000..30b0d24a1 --- /dev/null +++ b/Source/Falcor/Utils/Math/Float16.cpp @@ -0,0 +1,265 @@ +/*************************************************************************** + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. + # + # Redistribution and use in source and binary forms, with or without + # modification, are permitted provided that the following conditions + # are met: + # * Redistributions of source code must retain the above copyright + # notice, this list of conditions and the following disclaimer. + # * Redistributions in binary form must reproduce the above copyright + # notice, this list of conditions and the following disclaimer in the + # documentation and/or other materials provided with the distribution. + # * Neither the name of NVIDIA CORPORATION nor the names of its + # contributors may be used to endorse or promote products derived + # from this software without specific prior written permission. + # + # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY + # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + **************************************************************************/ + +/** + * Most of this code is derived from the GLM library at https://github.com/g-truc/glm + * + * License: https://github.com/g-truc/glm/blob/master/copying.txt + */ + +#include "Float16.h" + +namespace Falcor +{ +namespace math +{ + +static float overflow() +{ + volatile float f = 1e10; + for (int i = 0; i < 10; ++i) + f *= f; // this will overflow before the for loop terminates + return f; +} + +union uif32 +{ + float f; + unsigned int i; +}; + +uint16_t float32ToFloat16(float value) +{ + uif32 entry; + entry.f = value; + int i = static_cast(entry.i); + + // + // Our floating point number, f, is represented by the bit + // pattern in integer i. Disassemble that bit pattern into + // the sign, s, the exponent, e, and the significand, m. + // Shift s into the position where it will go in the + // resulting half number. + // Adjust e, accounting for the different exponent bias + // of float and half (127 versus 15). + // + + int s = (i >> 16) & 0x00008000; + int e = ((i >> 23) & 0x000000ff) - (127 - 15); + int m = i & 0x007fffff; + + // + // Now reassemble s, e and m into a half: + // + + if (e <= 0) + { + if (e < -10) + { + // + // E is less than -10. The absolute value of f is + // less than half_MIN (f may be a small normalized + // float, a denormalized float or a zero). + // + // We convert f to a half zero. + // + + return uint16_t(s); + } + + // + // E is between -10 and 0. F is a normalized float, + // whose magnitude is less than __half_NRM_MIN. + // + // We convert f to a denormalized half. + // + + m = (m | 0x00800000) >> (1 - e); + + // + // Round to nearest, round "0.5" up. + // + // Rounding may cause the significand to overflow and make + // our number normalized. Because of the way a half's bits + // are laid out, we don't have to treat this case separately; + // the code below will handle it correctly. + // + + if (m & 0x00001000) + m += 0x00002000; + + // + // Assemble the half from s, e (zero) and m. + // + + return uint16_t(s | (m >> 13)); + } + else if (e == 0xff - (127 - 15)) + { + if (m == 0) + { + // + // F is an infinity; convert f to a half + // infinity with the same sign as f. + // + + return uint16_t(s | 0x7c00); + } + else + { + // + // F is a NAN; we produce a half NAN that preserves + // the sign bit and the 10 leftmost bits of the + // significand of f, with one exception: If the 10 + // leftmost bits are all zero, the NAN would turn + // into an infinity, so we have to set at least one + // bit in the significand. + // + + m >>= 13; + + return uint16_t(s | 0x7c00 | m | (m == 0)); + } + } + else + { + // + // E is greater than zero. F is a normalized float. + // We try to convert f to a normalized half. + // + + // + // Round to nearest, round "0.5" up + // + + if (m & 0x00001000) + { + m += 0x00002000; + + if (m & 0x00800000) + { + m = 0; // overflow in significand, + e += 1; // adjust exponent + } + } + + // + // Handle exponent overflow + // + + if (e > 30) + { + overflow(); // Cause a hardware floating point overflow; + + return uint16_t(s | 0x7c00); // Return infinity with same sign as f. + } + + // + // Assemble the half from s, e and m. + // + + return uint16_t(s | (e << 10) | (m >> 13)); + } +} + +float float16ToFloat32(uint16_t value) +{ + int s = (value >> 15) & 0x00000001; + int e = (value >> 10) & 0x0000001f; + int m = value & 0x000003ff; + + if (e == 0) + { + if (m == 0) + { + // + // Plus or minus zero + // + + uif32 result; + result.i = static_cast(s << 31); + return result.f; + } + else + { + // + // Denormalized number -- renormalize it + // + + while (!(m & 0x00000400)) + { + m <<= 1; + e -= 1; + } + + e += 1; + m &= ~0x00000400; + } + } + else if (e == 31) + { + if (m == 0) + { + // + // Positive or negative infinity + // + + uif32 result; + result.i = static_cast((s << 31) | 0x7f800000); + return result.f; + } + else + { + // + // Nan -- preserve sign and significand bits + // + + uif32 result; + result.i = static_cast((s << 31) | 0x7f800000 | (m << 13)); + return result.f; + } + } + + // + // Normalized number + // + + e = e + (127 - 15); + m = m << 13; + + // + // Assemble s, e and m. + // + + uif32 result; + result.i = static_cast((s << 31) | (e << 23) | m); + return result.f; +} + +} // namespace math +} // namespace Falcor diff --git a/Source/Falcor/Utils/Math/Float16.h b/Source/Falcor/Utils/Math/Float16.h index 9cdd7b03f..c5acfddc7 100644 --- a/Source/Falcor/Utils/Math/Float16.h +++ b/Source/Falcor/Utils/Math/Float16.h @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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,166 +26,140 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **************************************************************************/ #pragma once -#include "Core/Assert.h" -#include "Vector.h" -#include -#include -#include -namespace Falcor -{ - /** Represents a IEEE 754-2008 compatible binary16 type (half precision). - Numbers outside the representable range +-65504 are stored as +-inf. - */ - class float16_t - { - public: - float16_t() = default; - - // Float conversion - explicit float16_t(float v) : bits(glm::detail::toFloat16(v)) {} - explicit operator float() const { return glm::detail::toFloat32(bits); } - - bool operator==(const float16_t other) const { return bits == other.bits; } - bool operator!=(const float16_t other) const { return bits != other.bits; } - - friend bool operator< (const float16_t lhs, const float16_t rhs) { return float(lhs) < float(rhs); } - friend bool operator> (const float16_t lhs, const float16_t rhs) { return rhs < lhs; } - friend bool operator<=(const float16_t lhs, const float16_t rhs) { return !(lhs > rhs); } - friend bool operator>=(const float16_t lhs, const float16_t rhs) { return !(lhs < rhs); } - - // TODO: Implement math operators in native fp16 precision. For now using fp32. - friend float16_t operator+ (const float16_t lhs, const float16_t rhs) { return float16_t((float)lhs + float(rhs)); } - friend float16_t operator- (const float16_t lhs, const float16_t rhs) { return float16_t((float)lhs - float(rhs)); } - friend float16_t operator* (const float16_t lhs, const float16_t rhs) { return float16_t((float)lhs * float(rhs)); } - friend float16_t operator/ (const float16_t lhs, const float16_t rhs) { return float16_t((float)lhs / float(rhs)); } - - float16_t& operator+= (const float16_t rhs) { *this = *this + rhs; return *this; } - float16_t& operator-= (const float16_t rhs) { *this = *this - rhs; return *this; } - float16_t& operator*= (const float16_t rhs) { *this = *this * rhs; return *this; } - float16_t& operator/= (const float16_t rhs) { *this = *this / rhs; return *this; } - - float16_t operator- () const - { - float16_t h; - h.bits = bits ^ 0x8000; - return h; - } - - private: - glm::detail::hdata bits; - }; +#include "Core/Macros.h" - inline std::string to_string(const float16_t& v) { return std::to_string((float)v); } +#include +#include +namespace Falcor +{ +namespace math +{ - // Vector types - - template - struct tfloat16_vec - { - }; - - template<> - struct tfloat16_vec<2> - { - using value_type = float16_t; - - float16_t x, y; - - // Constructors - tfloat16_vec() = default; - tfloat16_vec(const float16_t& v) : x(v), y(v) {} - tfloat16_vec(const float16_t& v1, const float16_t& v2) : x(v1), y(v2) {} +FALCOR_API uint16_t float32ToFloat16(float value); +FALCOR_API float float16ToFloat32(uint16_t value); - // Float conversion - explicit tfloat16_vec(float v) : x(v), y(v) {} - explicit tfloat16_vec(const float2& v) : x(v.x), y(v.y) {} - explicit tfloat16_vec(float v1, float v2) : x(v1), y(v2) {} - explicit operator float2() const { return float2(float(x), float(y)); } +struct float16_t +{ + float16_t() = default; - // Access - float16_t& operator[](size_t i) { FALCOR_ASSERT(i < length()); return (&x)[i]; } - const float16_t& operator[](size_t i) const { FALCOR_ASSERT(i < length()); return (&x)[i]; } + float16_t(uint32_t sign, uint32_t exponent, uint32_t fraction) + : mBits((sign & 0x01) << 15 | (exponent & 0x1f) << 10 | (fraction & 0x03ff)) + {} - bool operator==(const tfloat16_vec& other) const { return x == other.x && y == other.y; } - bool operator!=(const tfloat16_vec& other) const { return x != other.x || y != other.y; } + explicit float16_t(float value) : mBits(float32ToFloat16(value)) {} - static constexpr size_t length() { return 2; } - }; + template + explicit float16_t(T value) : mBits(float32ToFloat16(static_cast(value))) + {} - template<> - struct tfloat16_vec<3> - { - using value_type = float16_t; + operator float() const { return float16ToFloat32(mBits); } - float16_t x, y, z; + static constexpr float16_t fromBits(uint16_t bits) { return float16_t(bits, FromBits); } + uint16_t toBits() const { return mBits; } - // Constructors - tfloat16_vec() = default; - tfloat16_vec(const float16_t& v) : x(v), y(v), z(v) {} - tfloat16_vec(const float16_t& v1, const float16_t& v2, const float16_t& v3) : x(v1), y(v2), z(v3) {} + bool operator==(const float16_t other) const { return mBits == other.mBits; } + bool operator!=(const float16_t other) const { return mBits != other.mBits; } + bool operator<(const float16_t other) const { return static_cast(*this) < static_cast(other); } + bool operator<=(const float16_t other) const { return static_cast(*this) <= static_cast(other); } + bool operator>(const float16_t other) const { return static_cast(*this) > static_cast(other); } + bool operator>=(const float16_t other) const { return static_cast(*this) >= static_cast(other); } - // Float conversion - explicit tfloat16_vec(float v) : x(v), y(v), z(v) {} - explicit tfloat16_vec(const float3& v) : x(v.x), y(v.y), z(v.z) {} - explicit tfloat16_vec(float v1, float v2, float v3) : x(v1), y(v2), z(v3) {} - explicit operator float3() const { return float3(float(x), float(y), float(z)); } + float16_t operator+() const { return *this; } + float16_t operator-() const { return fromBits(mBits ^ 0x8000); } - // Access - float16_t& operator[](size_t i) { FALCOR_ASSERT(i < length()); return (&x)[i]; } - const float16_t& operator[](size_t i) const { FALCOR_ASSERT(i < length()); return (&x)[i]; } + // TODO: Implement math operators in native fp16 precision. For now using fp32. + float16_t operator+(const float16_t other) const { return float16_t(static_cast(*this) + static_cast(other)); } + float16_t operator-(const float16_t other) const { return float16_t(static_cast(*this) - static_cast(other)); } + float16_t operator*(const float16_t other) const { return float16_t(static_cast(*this) * static_cast(other)); } + float16_t operator/(const float16_t other) const { return float16_t(static_cast(*this) / static_cast(other)); } - bool operator==(const tfloat16_vec& other) const { return x == other.x && y == other.y && z == other.z; } - bool operator!=(const tfloat16_vec& other) const { return x != other.x || y != other.y || z != other.z; } + float16_t operator+=(const float16_t other) { return *this = *this + other; } + float16_t operator-=(const float16_t other) { return *this = *this - other; } + float16_t operator*=(const float16_t other) { return *this = *this * other; } + float16_t operator/=(const float16_t other) { return *this = *this / other; } - static constexpr size_t length() { return 3; } - }; + constexpr bool isFinite() const noexcept { return exponent() < 31; } + constexpr bool isInf() const noexcept { return exponent() == 31 && mantissa() == 0; } + constexpr bool isNan() const noexcept { return exponent() == 31 && mantissa() != 0; } + constexpr bool isNormalized() const noexcept { return exponent() > 0 && exponent() < 31; } + constexpr bool isDenormalized() const noexcept { return exponent() == 0 && mantissa() != 0; } - template<> - struct tfloat16_vec<4> +private: + enum Tag { - using value_type = float16_t; + FromBits + }; - float16_t x, y, z, w; + constexpr float16_t(uint16_t bits, Tag) : mBits(bits) {} - // Constructors - tfloat16_vec() = default; - tfloat16_vec(const float16_t& v) : x(v), y(v), z(v), w(v) {} - tfloat16_vec(const float16_t& v1, const float16_t& v2, const float16_t& v3, const float16_t& v4) : x(v1), y(v2), z(v3), w(v4) {} + constexpr uint16_t mantissa() const noexcept { return mBits & 0x3ff; } + constexpr uint16_t exponent() const noexcept { return (mBits >> 10) & 0x001f; } - // Float conversion - explicit tfloat16_vec(float v) : x(v), y(v), z(v), w(v) {} - explicit tfloat16_vec(const float4& v) : x(v.x), y(v.y), z(v.z), w(v.w) {} - explicit tfloat16_vec(float v1, float v2, float v3, float v4) : x(v1), y(v2), z(v3), w(v4) {} - explicit operator float4() const { return float4(float(x), float(y), float(z), float(w)); } + uint16_t mBits; +}; - // Access - float16_t& operator[](size_t i) { FALCOR_ASSERT(i < length()); return (&x)[i]; } - const float16_t& operator[](size_t i) const { FALCOR_ASSERT(i < length()); return (&x)[i]; } +#if FALCOR_MSVC +#pragma warning(push) +#pragma warning(disable : 4455) // disable warning about literal suffixes not starting with an underscore +#elif FALCOR_CLANG +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wuser-defined-literals" +#endif - bool operator==(const tfloat16_vec& other) const { return x == other.x && y == other.y && z == other.z && w == other.w; } - bool operator!=(const tfloat16_vec& other) const { return x != other.x || y != other.y || z != other.z || w != other.w; } +/// h suffix for "half float" literals. +inline float16_t operator""h(long double value) +{ + return float16_t(static_cast(value)); +} - static constexpr size_t length() { return 4; } - }; +#if FALCOR_MSVC +#pragma warning(pop) +#elif FALCOR_CLANG +#pragma clang diagnostic pop +#endif - using float16_t2 = tfloat16_vec<2>; - using float16_t3 = tfloat16_vec<3>; - using float16_t4 = tfloat16_vec<4>; +} // namespace math +} // namespace Falcor - inline std::string to_string(const float16_t2& v) { return "float16_t2(" + to_string(v.x) + "," + to_string(v.y) + ")"; } - inline std::string to_string(const float16_t3& v) { return "float16_t3(" + to_string(v.x) + "," + to_string(v.y) + "," + to_string(v.z) + ")"; } - inline std::string to_string(const float16_t4& v) { return "float16_t4(" + to_string(v.x) + "," + to_string(v.y) + "," + to_string(v.z) + "," + to_string(v.w) + ")"; } -} +namespace std +{ -// Formatter for the float16_t. template<> -struct ::fmt::formatter : formatter +class numeric_limits { - template - auto format(Falcor::float16_t value, FormatContext& ctx) const - { - return formatter::format(float(value), ctx); - } +public: + static constexpr bool is_specialized = true; + static constexpr Falcor::math::float16_t min() noexcept { return Falcor::math::float16_t::fromBits(0x0200); } + static constexpr Falcor::math::float16_t max() noexcept { return Falcor::math::float16_t::fromBits(0x7bff); } + static constexpr Falcor::math::float16_t lowest() noexcept { return Falcor::math::float16_t::fromBits(0xfbff); } + static constexpr int digits = 11; + static constexpr int digits10 = 3; + static constexpr bool is_signed = true; + static constexpr bool is_integer = false; + static constexpr bool is_exact = false; + static constexpr int radix = 2; + static constexpr Falcor::math::float16_t epsilon() noexcept { return Falcor::math::float16_t::fromBits(0x1200); } + static constexpr Falcor::math::float16_t round_error() noexcept { return Falcor::math::float16_t::fromBits(0x3c00); } + static constexpr int min_exponent = -13; + static constexpr int min_exponent10 = -4; + static constexpr int max_exponent = 16; + static constexpr int max_exponent10 = 4; + static constexpr bool has_infinity = true; + static constexpr bool has_quiet_NaN = true; + static constexpr bool has_signaling_NaN = true; + static constexpr float_denorm_style has_denorm = denorm_absent; + static constexpr bool has_denorm_loss = false; + static constexpr Falcor::math::float16_t infinity() noexcept { return Falcor::math::float16_t::fromBits(0x7c00); } + static constexpr Falcor::math::float16_t quiet_NaN() noexcept { return Falcor::math::float16_t::fromBits(0x7fff); } + static constexpr Falcor::math::float16_t signaling_NaN() noexcept { return Falcor::math::float16_t::fromBits(0x7dff); } + static constexpr Falcor::math::float16_t denorm_min() noexcept { return Falcor::math::float16_t::fromBits(0); } + static constexpr bool is_iec559 = false; + static constexpr bool is_bounded = false; + static constexpr bool is_modulo = false; + static constexpr bool traps = false; + static constexpr bool tinyness_before = false; + static constexpr float_round_style round_style = round_to_nearest; }; +} // namespace std diff --git a/Source/Falcor/Utils/Math/FormatConversion.h b/Source/Falcor/Utils/Math/FormatConversion.h new file mode 100644 index 000000000..9b726e954 --- /dev/null +++ b/Source/Falcor/Utils/Math/FormatConversion.h @@ -0,0 +1,93 @@ +/*************************************************************************** + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. + # + # Redistribution and use in source and binary forms, with or without + # modification, are permitted provided that the following conditions + # are met: + # * Redistributions of source code must retain the above copyright + # notice, this list of conditions and the following disclaimer. + # * Redistributions in binary form must reproduce the above copyright + # notice, this list of conditions and the following disclaimer in the + # documentation and/or other materials provided with the distribution. + # * Neither the name of NVIDIA CORPORATION nor the names of its + # contributors may be used to endorse or promote products derived + # from this software without specific prior written permission. + # + # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY + # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + # (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 "ScalarMath.h" +#include "Vector.h" + +namespace Falcor +{ + +/////////////////////////////////////////////////////////////////////////////// +// 16-bit snorm +/////////////////////////////////////////////////////////////////////////////// + +/** + * Convert float value to 16-bit snorm value. + * Values outside [-1,1] are clamped and NaN is encoded as zero. + * @return 16-bit snorm value in low bits, high bits are all zeros or ones depending on sign. + */ +inline int floatToSnorm16(float v) +{ + v = math::isnan(v) ? 0.f : math::min(math::max(v, -1.f), 1.f); + return (int)math::trunc(v * 32767.f + (v >= 0.f ? 0.5f : -0.5f)); +} + +/** + * Unpack a single 16-bit snorm from the lower bits of a dword. + * @param[in] packed 16-bit snorm in low bits, high bits don't care. + * @return Float value in [-1,1]. + */ +inline float unpackSnorm16(uint packed) +{ + int bits = (int)(packed << 16) >> 16; + float unpacked = math::max((float)bits / 32767.f, -1.0f); + return unpacked; +} + +/** + * Pack single float into a 16-bit snorm in the lower bits of the returned dword. + * @return 16-bit snorm in low bits, high bits all zero. + */ +inline uint packSnorm16(float v) +{ + return floatToSnorm16(v) & 0x0000ffff; +} + +/** + * Unpack two 16-bit snorm values from the lo/hi bits of a dword. + * @param[in] packed Two 16-bit snorm in low/high bits. + * @return Two float values in [-1,1]. + */ +inline float2 unpackSnorm2x16(uint packed) +{ + int2 bits = int2(packed << 16, packed) >> 16; + float2 unpacked = math::max((float2)bits / 32767.f, float2(-1.0f)); + return unpacked; +} + +/** + * Pack two floats into 16-bit snorm values in the lo/hi bits of a dword. + * @return Two 16-bit snorm in low/high bits. + */ +inline uint packSnorm2x16(float2 v) +{ + return (floatToSnorm16(v.x) & 0x0000ffff) | (floatToSnorm16(v.y) << 16); +} + +} // namespace Falcor diff --git a/Source/Falcor/Utils/Math/FormatConversion.slang b/Source/Falcor/Utils/Math/FormatConversion.slang index e42664664..c05d424d3 100644 --- a/Source/Falcor/Utils/Math/FormatConversion.slang +++ b/Source/Falcor/Utils/Math/FormatConversion.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-21, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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,34 +26,37 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **************************************************************************/ -/** Utility code for converting between various packed formats. - - 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. - - 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. -*/ +/** + * Utility code for converting between various packed formats. + * + * 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. + * + * 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. + */ /////////////////////////////////////////////////////////////////////////////// // 8-bit snorm /////////////////////////////////////////////////////////////////////////////// -/** Convert float value to 8-bit snorm value. - Values outside [-1,1] are clamped and NaN is encoded as zero. - \return 8-bit snorm value in low bits, high bits are all zeros or ones depending on sign. -*/ +/** + * Convert float value to 8-bit snorm value. + * Values outside [-1,1] are clamped and NaN is encoded as zero. + * @return 8-bit snorm value in low bits, high bits are all zeros or ones depending on sign. + */ int floatToSnorm8(float v) { v = isnan(v) ? 0.f : min(max(v, -1.f), 1.f); return (int)trunc(v * 127.f + (v >= 0.f ? 0.5f : -0.5f)); } -/** Unpack a single 8-bit snorm from the lower bits of a dword. - \param[in] packed 8-bit snorm in low bits, high bits don't care. - \return Float value in [-1,1]. -*/ +/** + * Unpack a single 8-bit snorm from the lower bits of a dword. + * @param[in] packed 8-bit snorm in low bits, high bits don't care. + * @return Float value in [-1,1]. + */ float unpackSnorm8(uint packed) { int bits = (int)(packed << 24) >> 24; @@ -61,18 +64,20 @@ float unpackSnorm8(uint packed) return unpacked; } -/** Pack single float into a 8-bit snorm in the lower bits of the returned dword. - \return 8-bit snorm in low bits, high bits all zero. -*/ +/** + * Pack single float into a 8-bit snorm in the lower bits of the returned dword. + * @return 8-bit snorm in low bits, high bits all zero. + */ uint packSnorm8(precise float v) { return floatToSnorm8(v) & 0x000000ff; } -/** Unpack two 8-bit snorm values from the lo bits of a dword. - \param[in] packed Two 8-bit snorm in low bits, high bits don't care. - \return Two float values in [-1,1]. -*/ +/** + * Unpack two 8-bit snorm values from the lo bits of a dword. + * @param[in] packed Two 8-bit snorm in low bits, high bits don't care. + * @return Two float values in [-1,1]. + */ float2 unpackSnorm2x8(uint packed) { int2 bits = int2((int)(packed << 24), (int)(packed << 16)) >> 24; @@ -80,9 +85,10 @@ float2 unpackSnorm2x8(uint packed) return unpacked; } -/** Pack two floats into 8-bit snorm values in the lo bits of a dword. - \return Two 8-bit snorm in low bits, high bits all zero. -*/ +/** + * Pack two floats into 8-bit snorm values in the lo bits of a dword. + * @return Two 8-bit snorm in low bits, high bits all zero. + */ uint packSnorm2x8(precise float2 v) { return (floatToSnorm8(v.x) & 0x000000ff) | ((floatToSnorm8(v.y) << 8) & 0x0000ff00); @@ -92,114 +98,126 @@ uint packSnorm2x8(precise float2 v) // 8-bit unorm /////////////////////////////////////////////////////////////////////////////// -/** Convert float value to 8-bit unorm (unsafe version). - \param[in] v Float value assumed to be in [0,1]. - \return 8-bit unorm in low bits, high bits all zeros. -*/ +/** + * Convert float value to 8-bit unorm (unsafe version). + * @param[in] v Float value assumed to be in [0,1]. + * @return 8-bit unorm in low bits, high bits all zeros. + */ uint packUnorm8_unsafe(float v) { return (uint)trunc(v * 255.f + 0.5f); } -/** Convert float value to 8-bit unorm. - Values outside [0,1] are clamped and NaN is encoded as zero. - \param[in] v Float value. - \return 8-bit unorm in low bits, high bits all zeros. -*/ +/** + * Convert float value to 8-bit unorm. + * Values outside [0,1] are clamped and NaN is encoded as zero. + * @param[in] v Float value. + * @return 8-bit unorm in low bits, high bits all zeros. + */ uint packUnorm8(float v) { v = isnan(v) ? 0.f : saturate(v); return packUnorm8_unsafe(v); } -/** Pack two floats into 8-bit unorm values. - Values outside [0,1] are clamped and NaN is encoded as zero. - \param[in] v Two float values. - \return Packed 8-bit unorm values in low bits, high bits all zeros. -*/ +/** + * Pack two floats into 8-bit unorm values. + * Values outside [0,1] are clamped and NaN is encoded as zero. + * @param[in] v Two float values. + * @return Packed 8-bit unorm values in low bits, high bits all zeros. + */ uint packUnorm2x8(float2 v) { return (packUnorm8(v.y) << 8) | packUnorm8(v.x); } -/** Pack two floats into 8-bit unorm values (unsafe version) - \param[in] v Two float values assumed to be in [0,1]. - \return Packed 8-bit unorm values in low bits, high bits all zeros. -*/ +/** + * Pack two floats into 8-bit unorm values (unsafe version) + * @param[in] v Two float values assumed to be in [0,1]. + * @return Packed 8-bit unorm values in low bits, high bits all zeros. + */ uint packUnorm2x8_unsafe(float2 v) { return (packUnorm8_unsafe(v.y) << 8) | packUnorm8_unsafe(v.x); } -/** Pack three floats into 8-bit unorm values. - Values outside [0,1] are clamped and NaN is encoded as zero. - \param[in] v Three float values. - \return Packed 8-bit unorm values in low bits, high bits all zeros. -*/ +/** + * Pack three floats into 8-bit unorm values. + * Values outside [0,1] are clamped and NaN is encoded as zero. + * @param[in] v Three float values. + * @return Packed 8-bit unorm values in low bits, high bits all zeros. + */ uint packUnorm3x8(float3 v) { return (packUnorm8(v.z) << 16) | (packUnorm8(v.y) << 8) | packUnorm8(v.x); } -/** Pack three floats into 8-bit unorm values (unsafe version) - \param[in] v Three float values assumed to be in [0,1]. - \return Packed 8-bit unorm values in low bits, high bits all zeros. -*/ +/** + * Pack three floats into 8-bit unorm values (unsafe version) + * @param[in] v Three float values assumed to be in [0,1]. + * @return Packed 8-bit unorm values in low bits, high bits all zeros. + */ uint packUnorm3x8_unsafe(float3 v) { return (packUnorm8_unsafe(v.z) << 16) | (packUnorm8_unsafe(v.y) << 8) | packUnorm8_unsafe(v.x); } -/** Pack four floats into 8-bit unorm values. - Values outside [0,1] are clamped and NaN is encoded as zero. - \param[in] v Four float values. - \return Packed 8-bit unorm values. -*/ +/** + * Pack four floats into 8-bit unorm values. + * Values outside [0,1] are clamped and NaN is encoded as zero. + * @param[in] v Four float values. + * @return Packed 8-bit unorm values. + */ uint packUnorm4x8(float4 v) { return (packUnorm8(v.w) << 24) | (packUnorm8(v.z) << 16) | (packUnorm8(v.y) << 8) | packUnorm8(v.x); } -/** Pack four floats into 8-bit unorm values (unsafe version) - \param[in] v Four float values assumed to be in [0,1]. - \return Packed 8-bit unorm values. -*/ +/** + * Pack four floats into 8-bit unorm values (unsafe version) + * @param[in] v Four float values assumed to be in [0,1]. + * @return Packed 8-bit unorm values. + */ uint packUnorm4x8_unsafe(float4 v) { return (packUnorm8_unsafe(v.w) << 24) | (packUnorm8_unsafe(v.z) << 16) | (packUnorm8_unsafe(v.y) << 8) | packUnorm8_unsafe(v.x); } -/** Convert 8-bit unorm to float value. - \param[in] packed 8-bit unorm in low bits, high bits don't care. - \return Float value in [0,1]. -*/ +/** + * Convert 8-bit unorm to float value. + * @param[in] packed 8-bit unorm in low bits, high bits don't care. + * @return Float value in [0,1]. + */ float unpackUnorm8(uint packed) { return float(packed & 0xff) * (1.f / 255); } -/** Unpack two 8-bit unorm values. - \param[in] packed 8-bit unorm values in low bits, high bits don't care. - \return Two float values in [0,1]. -*/ +/** + * Unpack two 8-bit unorm values. + * @param[in] packed 8-bit unorm values in low bits, high bits don't care. + * @return Two float values in [0,1]. + */ float2 unpackUnorm2x8(uint packed) { return float2(uint2(packed, packed >> 8) & 0xff) * (1.f / 255); } -/** Unpack three 8-bit unorm values. - \param[in] packed 8-bit unorm values in low bits, high bits don't care. - \return Three float values in [0,1]. -*/ +/** + * Unpack three 8-bit unorm values. + * @param[in] packed 8-bit unorm values in low bits, high bits don't care. + * @return Three float values in [0,1]. + */ float3 unpackUnorm3x8(uint packed) { return float3(uint3(packed, packed >> 8, packed >> 16) & 0xff) * (1.f / 255); } -/** Unpack four 8-bit unorm values. - \param[in] packed 8-bit unorm values. - \return Four float values in [0,1]. -*/ +/** + * Unpack four 8-bit unorm values. + * @param[in] packed 8-bit unorm values. + * @return Four float values in [0,1]. + */ float4 unpackUnorm4x8(uint packed) { return float4(uint4(packed, packed >> 8, packed >> 16, packed >> 24) & 0xff) * (1.f / 255); @@ -209,20 +227,22 @@ float4 unpackUnorm4x8(uint packed) // 16-bit snorm /////////////////////////////////////////////////////////////////////////////// -/** Convert float value to 16-bit snorm value. - Values outside [-1,1] are clamped and NaN is encoded as zero. - \return 16-bit snorm value in low bits, high bits are all zeros or ones depending on sign. -*/ +/** + * Convert float value to 16-bit snorm value. + * Values outside [-1,1] are clamped and NaN is encoded as zero. + * @return 16-bit snorm value in low bits, high bits are all zeros or ones depending on sign. + */ 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)); } -/** Unpack a single 16-bit snorm from the lower bits of a dword. - \param[in] packed 16-bit snorm in low bits, high bits don't care. - \return Float value in [-1,1]. -*/ +/** + * Unpack a single 16-bit snorm from the lower bits of a dword. + * @param[in] packed 16-bit snorm in low bits, high bits don't care. + * @return Float value in [-1,1]. + */ float unpackSnorm16(uint packed) { int bits = (int)(packed << 16) >> 16; @@ -230,18 +250,20 @@ float unpackSnorm16(uint packed) return unpacked; } -/** Pack single float into a 16-bit snorm in the lower bits of the returned dword. - \return 16-bit snorm in low bits, high bits all zero. -*/ +/** + * Pack single float into a 16-bit snorm in the lower bits of the returned dword. + * @return 16-bit snorm in low bits, high bits all zero. + */ uint packSnorm16(precise float v) { return floatToSnorm16(v) & 0x0000ffff; } -/** Unpack two 16-bit snorm values from the lo/hi bits of a dword. - \param[in] packed Two 16-bit snorm in low/high bits. - \return Two float values in [-1,1]. -*/ +/** + * Unpack two 16-bit snorm values from the lo/hi bits of a dword. + * @param[in] packed Two 16-bit snorm in low/high bits. + * @return Two float values in [-1,1]. + */ float2 unpackSnorm2x16(uint packed) { int2 bits = int2(packed << 16, packed) >> 16; @@ -249,9 +271,10 @@ float2 unpackSnorm2x16(uint packed) return unpacked; } -/** Pack two floats into 16-bit snorm values in the lo/hi bits of a dword. - \return Two 16-bit snorm in low/high bits. -*/ +/** + * Pack two floats into 16-bit snorm values in the lo/hi bits of a dword. + * @return Two 16-bit snorm in low/high bits. + */ uint packSnorm2x16(precise float2 v) { return (floatToSnorm16(v.x) & 0x0000ffff) | (floatToSnorm16(v.y) << 16); @@ -261,51 +284,57 @@ uint packSnorm2x16(precise float2 v) // 16-bit unorm /////////////////////////////////////////////////////////////////////////////// -/** Convert float value to 16-bit unorm (unsafe version). - \param[in] v Value assumed to be in [0,1]. - \return 16-bit unorm in low bits, high bits all zeros. -*/ +/** + * Convert float value to 16-bit unorm (unsafe version). + * @param[in] v Value assumed to be in [0,1]. + * @return 16-bit unorm in low bits, high bits all zeros. + */ uint packUnorm16_unsafe(float v) { return (uint)trunc(v * 65535.f + 0.5f); } -/** Convert float value to 16-bit unorm. - Values outside [0,1] are clamped and NaN is encoded as zero. - \return 16-bit unorm in low bits, high bits all zeros. -*/ +/** + * Convert float value to 16-bit unorm. + * Values outside [0,1] are clamped and NaN is encoded as zero. + * @return 16-bit unorm in low bits, high bits all zeros. + */ uint packUnorm16(float v) { v = isnan(v) ? 0.f : saturate(v); return packUnorm16_unsafe(v); } -/** Pack two floats into 16-bit unorm values in a dword. -*/ +/** + * Pack two floats into 16-bit unorm values in a dword. + */ uint packUnorm2x16(float2 v) { return (packUnorm16(v.y) << 16) | packUnorm16(v.x); } -/** Pack two floats into 16-bit unorm values in a dword (unsafe version) - \param[in] v Two values assumed to be in [0,1]. -*/ +/** + * Pack two floats into 16-bit unorm values in a dword (unsafe version) + * @param[in] v Two values assumed to be in [0,1]. + */ uint packUnorm2x16_unsafe(float2 v) { return (packUnorm16_unsafe(v.y) << 16) | packUnorm16_unsafe(v.x); } -/** Convert 16-bit unorm to float value. - \param[in] packed 16-bit unorm in low bits, high bits don't care. - \return Float value in [0,1]. -*/ +/** + * Convert 16-bit unorm to float value. + * @param[in] packed 16-bit unorm in low bits, high bits don't care. + * @return Float value in [0,1]. + */ float unpackUnorm16(uint packed) { return float(packed & 0xffff) * (1.f / 65535); } -/** Unpack two 16-bit unorm values from a dword. -*/ +/** + * Unpack two 16-bit unorm values from a dword. + */ float2 unpackUnorm2x16(uint packed) { return float2(packed & 0xffff, packed >> 16) * (1.f / 65535); @@ -315,9 +344,10 @@ float2 unpackUnorm2x16(uint packed) // 32-bit HDR color format /////////////////////////////////////////////////////////////////////////////// -/** Pack three positive floats into a dword. - https://github.com/microsoft/DirectX-Graphics-Samples/blob/master/MiniEngine/Core/Shaders/PixelPacking_R11G11B10.hlsli -*/ +/** + * Pack three positive floats into a dword. + * https://github.com/microsoft/DirectX-Graphics-Samples/blob/master/MiniEngine/Core/Shaders/PixelPacking_R11G11B10.hlsli + */ uint packR11G11B10(float3 v) { // Clamp upper bound so that it doesn't accidentally round up to INF @@ -329,13 +359,14 @@ uint packR11G11B10(float3 v) return r | g | b; } -/** Unpack three positive floats from a dword. - https://github.com/microsoft/DirectX-Graphics-Samples/blob/master/MiniEngine/Core/Shaders/PixelPacking_R11G11B10.hlsli -*/ +/** + * Unpack three positive floats from a dword. + * https://github.com/microsoft/DirectX-Graphics-Samples/blob/master/MiniEngine/Core/Shaders/PixelPacking_R11G11B10.hlsli + */ float3 unpackR11G11B10(uint packed) { - float r = f16tof32((packed << 4 ) & 0x7FF0); - float g = f16tof32((packed >> 7 ) & 0x7FF0); + float r = f16tof32((packed << 4) & 0x7FF0); + float g = f16tof32((packed >> 7) & 0x7FF0); float b = f16tof32((packed >> 17) & 0x7FE0); return float3(r, g, b); } @@ -344,17 +375,19 @@ float3 unpackR11G11B10(uint packed) // 64-bit unsigned integer /////////////////////////////////////////////////////////////////////////////// -/** Encodes the 32-bit unsigned integers of v in a 64-bit unsigned integer. - v.x will become the low bits of the return value while v.y will become the high bits. -*/ +/** + * Encodes the 32-bit unsigned integers of v in a 64-bit unsigned integer. + * v.x will become the low bits of the return value while v.y will become the high bits. + */ uint64_t u2x32to64(uint2 v) { return (uint64_t(v.y) << 32) | uint64_t(v.x); } -/** Encodes the 64-bit unsigned integer v in two 32-bit unsigned integers. - The return value will store the low bits of v in the x-component and the high bits of v in the y-component. -*/ +/** + * Encodes the 64-bit unsigned integer v in two 32-bit unsigned integers. + * The return value will store the low bits of v in the x-component and the high bits of v in the y-component. + */ uint2 u64to2x32(uint64_t v) { return uint2(v & 0xffffffff, v >> 32); diff --git a/Source/Falcor/Utils/Math/HalfUtils.slang b/Source/Falcor/Utils/Math/HalfUtils.slang index 73b0a73ac..a9e46e624 100644 --- a/Source/Falcor/Utils/Math/HalfUtils.slang +++ b/Source/Falcor/Utils/Math/HalfUtils.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-21, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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,15 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **************************************************************************/ -/** Utility functions for fp16 math. -*/ +/** + * 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. -*/ +/** + * 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); @@ -44,17 +46,21 @@ uint f32tof16_roundDown(float value) // 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; + 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. -*/ +/** + * 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); @@ -66,9 +72,12 @@ uint f32tof16_roundUp(float value) // 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; + 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 index fb71a1d10..b23cb8a91 100644 --- a/Source/Falcor/Utils/Math/HashUtils.slang +++ b/Source/Falcor/Utils/Math/HashUtils.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-21, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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,16 @@ # 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. -*/ +/** + * 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. -*/ +/** + * 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); @@ -45,17 +47,18 @@ uint jenkinsHash(uint a) 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)). -*/ +/** + * 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; diff --git a/Source/Falcor/Utils/Math/IntervalArithmetic.slang b/Source/Falcor/Utils/Math/IntervalArithmetic.slang index 442397374..d02dd7e55 100644 --- a/Source/Falcor/Utils/Math/IntervalArithmetic.slang +++ b/Source/Falcor/Utils/Math/IntervalArithmetic.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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,185 +28,161 @@ #pragma once #include "Utils/Math/MathConstants.slangh" - /** Utility functions for interval arithmetic. - All interval functions operate on float2 where interval.x is the minimum value and interval.y is the maximum value. +/** + * Utility functions for interval arithmetic. + * All interval functions operate on float2 where interval.x is the minimum value and interval.y is the maximum value. */ -/** Combine two intervals into one. -*/ +/// Combine two intervals into one. float2 ivlCombine(float2 a, float2 b) { return float2(min(a.x, b.x), max(a.y, b.y)); } -/** Checks if the interval is valid. -*/ +/// Checks if the interval is valid. bool ivlValid(float2 interval) { return interval.x <= interval.y; } -/** Checks if the interval contains zero. -*/ +/// Checks if the interval contains zero. bool ivlContainsZero(float2 interval) { return interval.x <= 0.0f && interval.y >= 0.0f; } -/** Calculates the min interval of an interval and a scalar. -*/ +/// Calculates the min interval of an interval and a scalar. float2 ivlMin(float2 interval, float scalar) { return min(interval, scalar); } -/** Calculates the min interval of two intervals. -*/ +/// Calculates the min interval of two intervals. float2 ivlMin(float2 intervalA, float2 intervalB) { return min(intervalA, intervalB); } -/** Calculates the max interval of an interval and a scalar. -*/ +/// Calculates the max interval of an interval and a scalar. float2 ivlMax(float2 interval, float scalar) { return max(interval, scalar); } -/** Calculates the max interval of two intervals. -*/ +/// Calculates the max interval of two intervals. float2 ivlMax(float2 intervalA, float2 intervalB) { return max(intervalA, intervalB); } -/** Clamps the interval between two scalars. -*/ +/// Clamps the interval between two scalars. float2 ivlClamp(float2 interval, float scalarMin, float scalarMax) { return clamp(interval, scalarMin, scalarMax); } -/** Clamps the interval between 0 and 1. -*/ +/// Clamps the interval between 0 and 1. float2 ivlSaturate(float2 interval) { return clamp(interval, 0.0f, 1.0f); } -/** Calculates the interval sum of an interval and a scalar. -*/ +/// Calculates the interval sum of an interval and a scalar. float2 ivlAdd(float2 interval, float scalar) { return interval + scalar; } -/** Calculates the interval sum of two intervals. -*/ +/// Calculates the interval sum of two intervals. float2 ivlAdd(float2 intervalA, float2 intervalB) { return intervalA + intervalB; } -/** Calculates the interval that results when subtracting a scalar from an interval. -*/ +/// Calculates the interval that results when subtracting a scalar from an interval. float2 ivlSub(float2 interval, float scalar) { return interval - scalar; } -/** Calculates the interval that results when subtracting intervalB from intervalA. -*/ +/// Calculates the interval that results when subtracting intervalB from intervalA. float2 ivlSub(float2 intervalA, float2 intervalB) { return intervalA - intervalB.yx; } -/** Calculates the product of an interval and a scalar. -*/ +/// Calculates the product of an interval and a scalar. float2 ivlMul(float2 interval, float scalar) { float2 prod = interval * scalar; return float2(min(prod.x, prod.y), max(prod.x, prod.y)); } -/** Calculates the product of an interval and a scalar, the interval must be >= 0. -*/ +/// Calculates the product of an interval and a scalar, the interval must be >= 0. float2 ivlPosMul(float2 interval, float scalar) { return interval * scalar; } -/** Calculates the product of an interval and a scalar, the interval must be <= 0. -*/ +/// Calculates the product of an interval and a scalar, the interval must be <= 0. float2 ivlNegMul(float2 interval, float scalar) { return interval.yx * scalar; } -/** Calculates the product of two intervals. -*/ +/// Calculates the product of two intervals. float2 ivlMul(float2 intervalA, float2 intervalB) { float4 prod = float4(intervalA * intervalB, intervalA.yx * intervalB); return float2(min(min(min(prod.x, prod.y), prod.z), prod.w), max(max(max(prod.x, prod.y), prod.z), prod.w)); } -/** Calculates the product of two intervals, intervalA must be >= 0. -*/ +/// Calculates the product of two intervals, intervalA must be >= 0. float2 ivlPosMul(float2 intervalA, float2 intervalB) { float4 prod = float4(intervalA * intervalB, intervalA.yx * intervalB); return float2(min(prod.x, prod.z), max(prod.y, prod.w)); } -/** Calculates the product of two intervals, intervalA must be <= 0. -*/ +/// Calculates the product of two intervals, intervalA must be <= 0. float2 ivlNegMul(float2 intervalA, float2 intervalB) { float4 prod = float4(intervalA * intervalB, intervalA.yx * intervalB); return float2(min(prod.y, prod.w), max(prod.x, prod.z)); } -/** Calculates the product of two intervals, intervalA and intervalB must be >= 0. -*/ +/// Calculates the product of two intervals, intervalA and intervalB must be >= 0. float2 ivlPosMulPos(float2 intervalA, float2 intervalB) { return intervalA * intervalB; } -/** Calculates the product of two intervals, intervalA and intervalB must be <= 0. -*/ +/// Calculates the product of two intervals, intervalA and intervalB must be <= 0. float2 ivlNegMulNeg(float2 intervalA, float2 intervalB) { return (intervalA * intervalB).yx; } -/** Calculates the product of two intervals, intervalA must be >= 0 and intervalB must be <= 0. -*/ +/// Calculates the product of two intervals, intervalA must be >= 0 and intervalB must be <= 0. float2 ivlPosMulNeg(float2 intervalA, float2 intervalB) { return intervalA.yx * intervalB; } -/** Calculates the product of two intervals, intervalA must be <= 0 and intervalB must be >= 0. -*/ +/// Calculates the product of two intervals, intervalA must be <= 0 and intervalB must be >= 0. float2 ivlNegMulPos(float2 intervalA, float2 intervalB) { return intervalA * intervalB.yx; } -/** Calculates the interval that results when dividing an interval with a scalar. -*/ +/// Calculates the interval that results when dividing an interval with a scalar. float2 ivlDiv(float2 interval, float scalar) { float2 fract = interval / scalar; return float2(min(fract.x, fract.y), max(fract.x, fract.y)); } -/** Calculates the interval that results when dividing intervalA with intervalB. -*/ +/// Calculates the interval that results when dividing intervalA with intervalB. float2 ivlDiv(float2 intervalA, float2 intervalB) { bool bContainsZero = ivlContainsZero(intervalB); @@ -215,88 +191,66 @@ float2 ivlDiv(float2 intervalA, float2 intervalB) return ivlMul(intervalA, denom); } -/** Calculates the absolute value of an interval. -*/ +/// Calculates the absolute value of an interval. float2 ivlAbs(float2 interval) { return float2(max(max(interval.x, -interval.y), 0.0f), max(-interval.x, interval.y)); } -/** Calculates the absolute value of an interval, the interval must be <= 0. -*/ +/// Calculates the absolute value of an interval, the interval must be <= 0. float2 ivlNegAbs(float2 interval) { return -interval.yx; } -/** Calculates the square an interval. -*/ +/// Calculates the square an interval. float2 ivlSquare(float2 interval) { interval = ivlAbs(interval); return interval * interval; } -/** Calculates the square an interval, the interval must be >= 0. -*/ +/// Calculates the square an interval, the interval must be >= 0. float2 ivlPosSquare(float2 interval) { return interval * interval; } -/** Calculates the square an interval, the interval must be <= 0. -*/ +/// Calculates the square an interval, the interval must be <= 0. float2 ivlNegSquare(float2 interval) { return interval.yx * interval.yx; } -/** Calculates the square an interval, the interval must be <= 0. -*/ +/// Calculates the square an interval, the interval must be <= 0. float2 ivlSqrt(float2 interval) { return sqrt(interval); } -/** Calculates the square an interval, the interval must be <= 0. -*/ +/// Calculates the square an interval, the interval must be <= 0. float2 ivlNegate(float2 interval) { return -interval.yx; } -/** Calculates the length of a 2D vector with interval components. -*/ +/// Calculates the length of a 2D vector with interval components. float2 ivlLength(float2 xInterval, float2 yInterval) { - return ivlSqrt( // sqrt((x * x) + (y * y)) - ivlAdd( // (x * x) + (y * y) - ivlSquare(xInterval), // x * x - ivlSquare(yInterval))); // y * y + // sqrt((x * x) + (y * y)) + return ivlSqrt(ivlAdd(ivlSquare(xInterval), ivlSquare(yInterval))); } -/** Calculates the length of a 3D vector with interval components. -*/ +/// Calculates the length of a 3D vector with interval components. float2 ivlLength(float2 xInterval, float2 yInterval, float2 zInterval) { - return ivlSqrt( // sqrt((x * x) + (y * y) + (z * z)) - ivlAdd( // (x * x) + (y * y) + (z * z) - ivlAdd( - ivlSquare(xInterval), // x * x - ivlSquare(yInterval)), // y * y - ivlSquare(zInterval))); // z * z + // sqrt((x * x) + (y * y) + (z * z)) + return ivlSqrt(ivlAdd(ivlAdd(ivlSquare(xInterval), ivlSquare(yInterval)), ivlSquare(zInterval))); } -/** Calculates the length of a 3D vector with interval components. -*/ +/// Calculates the length of a 3D vector with interval components. float2 ivlLength(float2 xInterval, float2 yInterval, float2 zInterval, float2 wInterval) { - return ivlSqrt( // sqrt((x * x) + (y * y) + (z * z) + (w * w)) - ivlAdd( // (x * x) + (y * y) + (z * z) + (w * w) - ivlAdd( - ivlAdd( - ivlSquare(xInterval), // x * x - ivlSquare(yInterval)), // y * y - ivlSquare(zInterval)), // z * z - ivlSquare(wInterval))); // w * w + // sqrt((x * x) + (y * y) + (z * z) + (w * w)) + return ivlSqrt(ivlAdd(ivlAdd(ivlAdd(ivlSquare(xInterval), ivlSquare(yInterval)), ivlSquare(zInterval)), ivlSquare(wInterval))); } diff --git a/Source/Falcor/Utils/Math/MathConstants.slangh b/Source/Falcor/Utils/Math/MathConstants.slangh index 9a2392416..7bb10bca4 100644 --- a/Source/Falcor/Utils/Math/MathConstants.slangh +++ b/Source/Falcor/Utils/Math/MathConstants.slangh @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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,18 +28,19 @@ #pragma once #include "Utils/HostDeviceShared.slangh" -/** This file contains useful numeric constants for use on the GPU. - - 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. -*/ +/** + * This file contains useful numeric constants for use on the GPU. + * + * 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. + */ #ifdef HOST_CODE // @skallweit: We should use a different way to make math constants available on the host. @@ -49,6 +50,8 @@ #else +// clang-format off + // Constants from #define M_E 2.71828182845904523536 // e #define M_LOG2E 1.44269504088896340736 // log2(e) @@ -64,15 +67,6 @@ #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 UINT32_MAX 4294967295 #define INT32_MIN -2147483648 @@ -111,6 +105,16 @@ #endif // !HOST_CODE +// 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_SQRT3 1.732050807568877293527 // sqrt(3) +#define M_SQRTPI 1.77245385090551602730 // sqrt(pi) +#define M_1_SQRT2 0.707106781186547524401 // 1/sqrt(2) + // 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 @@ -121,3 +125,5 @@ #define HLF_MIN_EXP (-14) // min binary exponent #define HLF_RADIX 2 #define HLF_TRUE_MIN 5.960464477539063e-08F // min positive value + +// clang-format on diff --git a/Source/Falcor/Utils/Math/MathHelpers.h b/Source/Falcor/Utils/Math/MathHelpers.h index 8eeaf673f..3d6625ad0 100644 --- a/Source/Falcor/Utils/Math/MathHelpers.h +++ b/Source/Falcor/Utils/Math/MathHelpers.h @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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,101 +27,107 @@ **************************************************************************/ #pragma once -#include "Utils/Math/Matrix/Matrix.h" #include "Vector.h" +#include "Matrix.h" #include "Core/Errors.h" #include "Utils/Logger.h" namespace Falcor { - /** Generate 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. - \param[in] u Unit vector. - \return v Unit vector that is orthogonal to u. - */ - inline float3 perp_stark(const float3& u) - { - // TODO: Validate this and look at numerical precision etc. Are there better ways to do it? - float3 a = abs(u); - uint32_t uyx = (a.x - a.y) < 0 ? 1 : 0; - uint32_t uzx = (a.x - a.z) < 0 ? 1 : 0; - uint32_t uzy = (a.y - a.z) < 0 ? 1 : 0; - uint32_t xm = uyx & uzx; - uint32_t ym = (1 ^ xm) & uzy; - uint32_t zm = 1 ^ (xm | ym); // 1 ^ (xm & ym) - float3 v = normalize(cross(u, float3(xm, ym, zm))); - return v; - } +/** + * Generate 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. + * @param[in] u Unit vector. + * @return v Unit vector that is orthogonal to u. + */ +inline float3 perp_stark(const float3& u) +{ + // TODO: Validate this and look at numerical precision etc. Are there better ways to do it? + float3 a = abs(u); + uint32_t uyx = (a.x - a.y) < 0 ? 1 : 0; + uint32_t uzx = (a.x - a.z) < 0 ? 1 : 0; + uint32_t uzy = (a.y - a.z) < 0 ? 1 : 0; + uint32_t xm = uyx & uzx; + uint32_t ym = (1 ^ xm) & uzy; + uint32_t zm = 1 ^ (xm | ym); // 1 ^ (xm & ym) + float3 v = normalize(cross(u, float3(xm, ym, zm))); + return v; +} - /** Builds a local frame from a unit normal vector. - \param[in] n Unit normal vector. - \param[out] t Unit tangent vector. - \param[out] b Unit bitangent vector. - */ - inline void buildFrame(const float3& n, float3& t, float3& b) - { - t = perp_stark(n); - b = cross(n, t); - } +/** + * Builds a local frame from a unit normal vector. + * @param[in] n Unit normal vector. + * @param[out] t Unit tangent vector. + * @param[out] b Unit bitangent vector. + */ +inline void buildFrame(const float3& n, float3& t, float3& b) +{ + t = perp_stark(n); + b = cross(n, t); +} - /** Check if the specified matrix has no inf or nan values. - \param[in] matrix The matrix to check. - \return True if valid else false. - */ - template - inline bool isMatrixValid(const rmcv::matrix& m) +/** + * Check if the specified matrix has no inf or nan values. + * @param[in] matrix The matrix to check. + * @return True if valid else false. + */ +template +inline bool isMatrixValid(const math::matrix& m) +{ + for (int r = 0; r < R; r++) { - for (int r = 0; r < R; r++) - { - if (glm::any(glm::isinf(m[r])) || glm::any(glm::isnan(m[r]))) - return false; - } - return true; + if (any(isinf(m[r])) || any(isnan(m[r]))) + return false; } + return true; +} - /** Check if the specified matrix is affine. - \param[in] matrix The matrix to check. - \return True if affine else false. - */ - template - inline bool isMatrixAffine(const rmcv::matrix& m) - { - GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || GLM_CONFIG_UNRESTRICTED_GENTYPE, "'isMatrixAffine' only accept floating-point inputs"); - - const int lastRow = R - 1; - const int lastCol = C - 1; +/** + * Check if the specified matrix is affine. + * @param[in] matrix The matrix to check. + * @return True if affine else false. + */ +template +inline bool isMatrixAffine(const math::matrix& m) +{ + static_assert(std::numeric_limits::is_iec559, "'isMatrixAffine' only accept floating-point inputs"); - for (int c = 0; c < lastCol; c++) - { - if (m[lastRow][c] != 0.f) - return false; - } + const int lastRow = R - 1; + const int lastCol = C - 1; - if (m[lastRow][lastCol] != 1.f) + for (int c = 0; c < lastCol; c++) + { + if (m[lastRow][c] != 0.f) return false; - - return true; } - /** Check if transform matrix have no inf/nan values and if it is affine. If it is not affine, it will return an affine matrix and if it is not valid, it will throw a runtime error. - \param[in] transform Transform matrix. - \return A copy of the matrix that is affine. - */ - inline rmcv::mat4 validateTransformMatrix(const rmcv::mat4& transform) - { - rmcv::mat4 newMatrix(transform); + if (m[lastRow][lastCol] != 1.f) + return false; + + return true; +} - if (!isMatrixValid(newMatrix)) - { - throw RuntimeError("Transform matrix has inf/nan values!"); - } +/** + * Check if transform matrix have no inf/nan values and if it is affine. If it is not affine, it will return an affine matrix and if it is + * not valid, it will throw a runtime error. + * @param[in] transform Transform matrix. + * @return A copy of the matrix that is affine. + */ +inline float4x4 validateTransformMatrix(const float4x4& transform) +{ + float4x4 newMatrix(transform); - if (!isMatrixAffine(newMatrix)) - { - logWarning("Transform matrix is not affine. Setting last row to (0,0,0,1)."); - newMatrix[3] = rmcv::vec4(0, 0, 0, 1); - } + if (!isMatrixValid(newMatrix)) + { + throw RuntimeError("Transform matrix has inf/nan values!"); + } - return newMatrix; + if (!isMatrixAffine(newMatrix)) + { + logWarning("Transform matrix is not affine. Setting last row to (0,0,0,1)."); + newMatrix[3] = float4(0, 0, 0, 1); } + + return newMatrix; } +} // namespace Falcor diff --git a/Source/Falcor/Utils/Math/MathHelpers.slang b/Source/Falcor/Utils/Math/MathHelpers.slang index 1dc5f6ef3..474463be0 100644 --- a/Source/Falcor/Utils/Math/MathHelpers.slang +++ b/Source/Falcor/Utils/Math/MathHelpers.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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,17 @@ # 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.) - -*/ +/** + * 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. @@ -54,12 +55,13 @@ ******************************************************************************/ -/** 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). -*/ +/** + * 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); @@ -69,12 +71,13 @@ float2 cartesian_to_spherical_unorm(float3 p) 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). -*/ +/** + * 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); @@ -84,11 +87,12 @@ float2 cartesian_to_spherical_rad(float3 p) return sph; } -/** Converts spherical coordinates (radians) to Cartesian coordinates. - Inverse of cartesian_to_spherical_rad. - \param[in] sph Spherical coordinates (theta,phi). - \return Cartesian coordinates (x,y,z). -*/ +/** + * Converts spherical coordinates (radians) to Cartesian coordinates. + * Inverse of cartesian_to_spherical_rad. + * @param[in] sph Spherical coordinates (theta,phi). + * @return Cartesian coordinates (x,y,z). + */ float3 spherical_to_cartesian_rad(float2 sph) { float3 p; @@ -98,11 +102,12 @@ float3 spherical_to_cartesian_rad(float2 sph) return p; } -/** 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. -*/ +/** + * 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); @@ -112,11 +117,12 @@ float2 world_to_latlong_map(float3 dir) return uv; } -/** Convert a coordinate in latitude-longitude map (unsigned normalized) to a world space direction. - The map is centered around the -z axis and wrapping around in clockwise order (left to right). - \param[in] latlong Position in latitude-longitude map in [0,1] for each component. - \return Normalized direction in world space. -*/ +/** + * Convert a coordinate in latitude-longitude map (unsigned normalized) to a world space direction. + * The map is centered around the -z axis and wrapping around in clockwise order (left to right). + * @param[in] latlong Position in latitude-longitude map in [0,1] for each component. + * @return Normalized direction in world space. + */ float3 latlong_map_to_world(float2 latlong) { float phi = M_PI * (2.f * saturate(latlong.x) - 1.f); @@ -143,18 +149,20 @@ float3 latlong_map_to_world(float2 latlong) ******************************************************************************/ -/** Helper function to reflect the folds of the lower hemisphere - over the diagonals in the octahedral map. -*/ +/** + * 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); + return (1.f - abs(v.yx)) * select(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. -*/ +/** + * 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. @@ -163,19 +171,21 @@ float2 ndir_to_oct_snorm(float3 n) 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. -*/ +/** + * 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. -*/ +/** + * 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)); @@ -183,19 +193,21 @@ float3 oct_to_ndir_snorm(float2 p) 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. -*/ +/** + * 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. -*/ +/** + * 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). @@ -208,16 +220,18 @@ float2 ndir_to_oct_equal_area_unorm(float3 n) 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; + 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. -*/ +/** + * 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; @@ -231,10 +245,10 @@ float3 oct_to_ndir_equal_area_unorm(float2 p) 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 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); + float z = sign(d) * (1.f - r * r); return float3(x, y, z); } @@ -245,10 +259,11 @@ float3 oct_to_ndir_equal_area_unorm(float2 p) ******************************************************************************/ -/** 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. -*/ +/** + * 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; @@ -259,23 +274,25 @@ float2 sample_disk(float2 u) return p; } -/** Uniform sampling of direction within a cone - \param[in] u Uniform random number in [0,1)^2. - \param[in] cosTheta Cosine of the cone half-angle - \return Sampled direction within the cone with (0,0,1) axis -*/ +/** + * Uniform sampling of direction within a cone + * @param[in] u Uniform random number in [0,1)^2. + * @param[in] cosTheta Cosine of the cone half-angle + * @return Sampled direction within the cone with (0,0,1) axis + */ float3 sample_cone(float2 u, float cosTheta) { float z = u.x * (1.f - cosTheta) + cosTheta; - float r = sqrt(1.f - z*z); + float r = sqrt(1.f - z * z); float phi = M_2PI * u.y; return float3(r * cos(phi), r * sin(phi), z); } -/** 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. -*/ +/** + * 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; @@ -284,10 +301,11 @@ float3 sample_sphere(float2 u) return float3(sinTheta * cos(phi), sinTheta * sin(phi), cosTheta); } -/** Uniform sampling of the unit hemisphere using sphere sampling. - \param[in] u Uniform random numbers in [0,1)^2. - \return Sampled point on the unit hemisphere. -*/ +/** + * Uniform sampling of the unit hemisphere using sphere sampling. + * @param[in] u Uniform random numbers in [0,1)^2. + * @return Sampled point on the unit hemisphere. + */ float3 sample_hemisphere(float2 u) { float3 w = sample_sphere(u); @@ -295,14 +313,16 @@ float3 sample_hemisphere(float2 u) return w; } -/** 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. -*/ +/** + * 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; + if (u.x == 0.f && u.y == 0.f) + return u; float phi, r; if (abs(u.x) > abs(u.y)) { @@ -317,11 +337,12 @@ float2 sample_disk_concentric(float2 u) 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). -*/ +/** + * 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); @@ -330,11 +351,12 @@ float3 sample_cosine_hemisphere_concentric(float2 u, out float pdf) 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). -*/ +/** + * 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; @@ -347,21 +369,23 @@ float3 sample_cosine_hemisphere_polar(float2 u, out float pdf) 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). -*/ +/** + * 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. -*/ +/** + * 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); @@ -375,12 +399,14 @@ float3 sample_triangle(float2 u) ******************************************************************************/ -/** 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. -*/ +/** + * 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; @@ -400,8 +426,9 @@ float2 calcMotionVector(float2 pixelCrd, float4 prevPosH, float2 renderTargetDim ******************************************************************************/ -/** Inverts a 2x2 matrix. -*/ +/** + * Inverts a 2x2 matrix. + */ float2x2 inverse(float2x2 M) { float2x2 inv; @@ -413,8 +440,9 @@ float2x2 inverse(float2x2 M) return inv; } -/** Inverts a 2x3 matrix. -*/ +/** + * Inverts a 2x3 matrix. + */ float2x3 inverse(float2x3 M) { float2x2 N = float2x2(M._m00, M._m01, M._m10, M._m11); @@ -424,8 +452,9 @@ float2x3 inverse(float2x3 M) return Mi; } -/** Inverts a 3x3 matrix. -*/ +/** + * Inverts a 3x3 matrix. + */ float3x3 inverse(float3x3 M) { float3x3 inv; @@ -442,11 +471,12 @@ float3x3 inverse(float3x3 M) return inv; } -/** Generate 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. - \param[in] u Unit vector. - \return v Unit vector that is orthogonal to u. -*/ +/** + * Generate 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. + * @param[in] u Unit vector. + * @return v Unit vector that is orthogonal to u. + */ float3 perp_stark(float3 u) { // TODO: Validate this and look at numerical precision etc. Are there better ways to do it? @@ -456,43 +486,44 @@ float3 perp_stark(float3 u) 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) + uint zm = 1 ^ (xm | ym); // 1 ^ (xm & ym) float3 v = normalize(cross(u, float3(xm, ym, zm))); return v; } -float sqr(float v) { return v*v; } - -float2 sqr(float2 v) { return v*v; } +// clang-format off +float sqr(float v) { return v * v; } +float2 sqr(float2 v) { return v * v; } +float3 sqr(float3 v) { return v * v; } +float4 sqr(float4 v) { return v * v; } +// clang-format on -float3 sqr(float3 v) { return v*v; } - -float4 sqr(float4 v) { return v*v; } - -/** Error function. -*/ +/** + * Error function. + */ float erf(float x) { // From "Numerical Recipes in C, The Art of Scientific Computing" // (Second Edition) by Press et al. 1992. Page 221. // Maxiumum error: 1.2 x 10^-7. float t = 1.0f / (1.0f + 0.5f * abs(x)); - float p = 0.17087277f; + float p = 0.17087277f; p = -0.82215223f + p * t; - p = 1.48851587f + p * t; + p = 1.48851587f + p * t; p = -1.13520398f + p * t; - p = 0.27886807f + p * t; + p = 0.27886807f + p * t; p = -0.18628806f + p * t; - p = 0.09678418f + p * t; - p = 0.37409196f + p * t; - p = 1.00002368f + p * t; + p = 0.09678418f + p * t; + p = 0.37409196f + p * t; + p = 1.00002368f + p * t; p = -1.26551223f + p * t; float tau = t * exp(-x * x + p); return x >= 0.0f ? 1.0f - tau : tau - 1.0f; } -/** Inverse error function. -*/ +/** + * Inverse error function. + */ float erfinv(float x) { // From "Approximating the erfinv function" by Mike Giles 2012. @@ -506,39 +537,40 @@ float erfinv(float x) return 1.0f / 0.0f; } - float w = - log((1.0f - x) * (1.0f + x)); + float w = -log((1.0f - x) * (1.0f + x)); float p; if (w < 5.0f) { w = w - 2.5f; - p = 2.81022636e-08f; - p = 3.43273939e-07f + p * w; - p = -3.5233877e-06f + p * w; + p = 2.81022636e-08f; + p = 3.43273939e-07f + p * w; + p = -3.5233877e-06f + p * w; p = -4.39150654e-06f + p * w; - p = 0.00021858087f + p * w; - p = -0.00125372503f + p * w; - p = -0.00417768164f + p * w; - p = 0.246640727f + p * w; - p = 1.50140941f + p * w; + p = 0.00021858087f + p * w; + p = -0.00125372503f + p * w; + p = -0.00417768164f + p * w; + p = 0.246640727f + p * w; + p = 1.50140941f + p * w; } else { w = sqrt(w) - 3.0f; p = -0.000200214257f; - p = 0.000100950558f + p * w; - p = 0.00134934322f + p * w; - p = -0.00367342844f + p * w; - p = 0.00573950773f + p * w; - p = -0.0076224613f + p * w; - p = 0.00943887047f + p * w; - p = 1.00167406f + p * w; - p = 2.83297682f + p * w; + p = 0.000100950558f + p * w; + p = 0.00134934322f + p * w; + p = -0.00367342844f + p * w; + p = 0.00573950773f + p * w; + p = -0.0076224613f + p * w; + p = 0.00943887047f + p * w; + p = 1.00167406f + p * w; + p = 2.83297682f + p * w; } return p * x; } -/** Logarithm of the Gamma function. -*/ +/** + * Logarithm of the Gamma function. + */ float lgamma(float x_) { // Lanczos approximation from @@ -547,13 +579,8 @@ float lgamma(float x_) // Maxiumum error: 2 x 10^-10. const float g = 5.0; const float coeffs[7] = { - 1.000000000190015, - 76.18009172947146, - -86.50532032941677, - 24.01409824083091, - -1.231739572450155, - 0.1208650973866179e-2, - -0.5395239384953e-5 + 1.000000000190015, 76.18009172947146, -86.50532032941677, 24.01409824083091, + -1.231739572450155, 0.1208650973866179e-2, -0.5395239384953e-5, }; bool reflect = x_ < 0.5f; @@ -563,11 +590,11 @@ float lgamma(float x_) float s = 0.0f; for (int i = 6; i >= 1; --i) { - s += coeffs[i] / (x + (float) i); + s += coeffs[i] / (x + (float)i); } s += coeffs[0]; - float res = ((log(sqrt(2*M_PI)) + log(s)) - base) + log(base) * (x + 0.5f); + float res = ((log(sqrt(2 * M_PI)) + log(s)) - base) + log(base) * (x + 0.5f); if (reflect) { // Apply the Gamma reflection formula (in log-space). @@ -576,38 +603,42 @@ float lgamma(float x_) return res; } -/** Gamma function. -*/ +/** + * Gamma function. + */ float gamma(float x) { return exp(lgamma(x)); } -/** Beta function. -*/ +/** + * Beta function. + */ float beta(float x, float y) { return gamma(x) * gamma(y) / gamma(x + y); } -/** Given two vectors, will orthonormalized them, preserving v1 and v2. - Vector v1 must be already normalized, v2 will be renormalized as part of the adjustment. - Vector v2 is assumed to be non-zero and already somewhat orthogonal to v1, as this code does not handle v2=0 or v2=v1 - - \param[in] v1 A normalized vector to orthogonalize v2 to. - \param[in,out] v2 Vector to be adjusted to be orthonormal to v2. -*/ +/** + * Given two vectors, will orthonormalized them, preserving v1 and v2. + * Vector v1 must be already normalized, v2 will be renormalized as part of the adjustment. + * Vector v2 is assumed to be non-zero and already somewhat orthogonal to v1, as this code does not handle v2=0 or v2=v1 + * + * @param[in] v1 A normalized vector to orthogonalize v2 to. + * @param[in,out] v2 Vector to be adjusted to be orthonormal to v2. + */ void orthogonalizeVectors(const float3 v1, inout float3 v2) { v2 = normalize(v2 - dot(v1, v2) * v1); } -/** Rotate a 3D vector counterclockwise around the X-axis. - The rotation angle is positive for a rotation that is counterclockwise when looking along the x-axis towards the origin. - \param[in] v Original vector. - \param[in] angle Angle in radians. - \return Transformed vector. -*/ +/** + * Rotate a 3D vector counterclockwise around the X-axis. + * The rotation angle is positive for a rotation that is counterclockwise when looking along the x-axis towards the origin. + * @param[in] v Original vector. + * @param[in] angle Angle in radians. + * @return Transformed vector. + */ float3 rotate_x(const float3 v, const float angle) { float c, s; @@ -615,12 +646,13 @@ float3 rotate_x(const float3 v, const float angle) return float3(v.x, v.y * c - v.z * s, v.y * s + v.z * c); } -/** Rotate a 3D vector counterclockwise around the Y-axis. - The rotation angle is positive for a rotation that is counterclockwise when looking along the y-axis towards the origin. - \param[in] v Original vector. - \param[in] angle Angle in radians. - \return Transformed vector. -*/ +/** + * Rotate a 3D vector counterclockwise around the Y-axis. + * The rotation angle is positive for a rotation that is counterclockwise when looking along the y-axis towards the origin. + * @param[in] v Original vector. + * @param[in] angle Angle in radians. + * @return Transformed vector. + */ float3 rotate_y(const float3 v, const float angle) { float c, s; @@ -628,12 +660,13 @@ float3 rotate_y(const float3 v, const float angle) return float3(v.x * c + v.z * s, v.y, -v.x * s + v.z * c); } -/** Rotate a 3D vector counterclockwise around the Z-axis. - The rotation angle is positive for a rotation that is counterclockwise when looking along the z-axis towards the origin. - \param[in] v Original vector. - \param[in] angle Angle in radians. - \return Transformed vector. -*/ +/** + * Rotate a 3D vector counterclockwise around the Z-axis. + * The rotation angle is positive for a rotation that is counterclockwise when looking along the z-axis towards the origin. + * @param[in] v Original vector. + * @param[in] angle Angle in radians. + * @return Transformed vector. + */ float3 rotate_z(const float3 v, const float angle) { float c, s; diff --git a/Source/Falcor/Utils/Math/Matrix.h b/Source/Falcor/Utils/Math/Matrix.h index a0884a6ec..733f983ea 100644 --- a/Source/Falcor/Utils/Math/Matrix.h +++ b/Source/Falcor/Utils/Math/Matrix.h @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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,6 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **************************************************************************/ #pragma once -#include "Utils/Math/Matrix/Matrix.h" -namespace Falcor -{ - using float3x3 = rmcv::mat3; - using float4x4 = rmcv::mat4; - using float3x4 = rmcv::matrix<3,4, float>; -} +#include "MatrixTypes.h" +#include "MatrixMath.h" diff --git a/Source/Falcor/Utils/Math/Matrix/Matrix.h b/Source/Falcor/Utils/Math/Matrix/Matrix.h deleted file mode 100644 index d489cda88..000000000 --- a/Source/Falcor/Utils/Math/Matrix/Matrix.h +++ /dev/null @@ -1,461 +0,0 @@ -/*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. - # - # Redistribution and use in source and binary forms, with or without - # modification, are permitted provided that the following conditions - # are met: - # * Redistributions of source code must retain the above copyright - # notice, this list of conditions and the following disclaimer. - # * Redistributions in binary form must reproduce the above copyright - # notice, this list of conditions and the following disclaimer in the - # documentation and/or other materials provided with the distribution. - # * Neither the name of NVIDIA CORPORATION nor the names of its - # contributors may be used to endorse or promote products derived - # from this software without specific prior written permission. - # - # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY - # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - # (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/Assert.h" - -#define GLM_FORCE_CTOR_INIT -#define GLM_ENABLE_EXPERIMENTAL -#define GLM_FORCE_SWIZZLE -#include -#include -#include -#include -#include -#include -#include -#include "glm/packing.hpp" -#include "Utils/Math/Vector.h" - -#include -#include - -namespace Falcor -{ -/** The new matrix library (or wrapper for one). - Name means "Row-Major, Column Vector" and signifies that the matrices are Row Major, and the vectors are column vectors (meaning M.v, translation in the last column) - */ -namespace rmcv -{ - template - using vec = glm::vec; - - using float1 = vec<1>; - using float2 = vec<2>; - using float3 = vec<3>; - using float4 = vec<4>; - - using vec1 = vec<1>; - using vec2 = vec<2>; - using vec3 = vec<3>; - using vec4 = vec<4>; - - template - class matrix - { - // For now, we only support float, but want to have the type T visible - static_assert(std::is_same_v); - - private: - template - friend class matrix; - public: - using Type = T; - using RowType = vec; - using ColType = vec; - - static constexpr int kRowCount = TRowCount; - static constexpr int kColCount = TColCount; - - static constexpr int getRowCount() { return TRowCount; } - static constexpr int getColCount() { return TColCount; } - - matrix(float diagonal = 1.f) - { - memset(this, 0, sizeof(*this)); - for (int i = 0; i < std::min(TRowCount, TColCount); ++i) - mRows[i][i] = diagonal; - } - - template - matrix(std::initializer_list v) - { - float* f = &mRows[0][0]; - for (auto it = v.begin(); it != v.end(); ++it, ++f) - *f = static_cast(*it); - } - - matrix(const matrix&) = default; - matrix(matrix&&) noexcept = default; - - template - matrix(const matrix& other) : matrix(1.f) - { - for (int r = 0; r < std::min(TRowCount, R); ++r) - { - memcpy(&mRows[r], &other.mRows[r], std::min(TColCount, C) * sizeof(T)); - } - } - - matrix& operator=(const matrix&) = default; - matrix& operator=(matrix&&) = default; - - template - matrix& operator=(const matrix& other) - { - for (int r = 0; r < std::min(TRowCount, R); ++r) - { - memcpy(&mRows[r], &other.mRows[r], std::min(TColCount, C) * sizeof(float)); - } - - return *this; - } - - float* data() { return &mRows[0][0]; } - const float* data() const { return &mRows[0][0]; } - - RowType& operator[](unsigned r) { FALCOR_ASSERT_LT(r, TRowCount); return mRows[r]; } - const RowType& operator[](unsigned r) const { FALCOR_ASSERT_LT(r, TRowCount); return mRows[r]; } - - RowType& getRow(int r) { FALCOR_ASSERT_LT(r, TRowCount); return mRows[r]; } - const RowType& getRow(int r) const { FALCOR_ASSERT_LT(r, TRowCount); return mRows[r]; } - - void setRow(int r, const RowType& v) { FALCOR_ASSERT_LT(r, TRowCount); mRows[r] = v; } - - ColType getCol(int col) const - { - ColType result; - for (int r = 0; r < TRowCount; ++r) - result[r] = mRows[r][col]; - return result; - } - - void setCol(int col, const ColType& v) - { - for (int r = 0; r < TRowCount; ++r) - mRows[r][col] = v[r]; - } - - matrix getTranspose() const - { - matrix result; - for (int r = 0; r < TRowCount; ++r) - { - for (int c = 0; c < TColCount; ++c) - { - result.mRows[c][r] = mRows[r][c]; - } - } - - return result; - } - - bool operator==(const matrix& rhs) const - { - return memcmp(this, &rhs, sizeof(*this)) == 0; - } - - bool operator!=(const matrix& rhs) const - { - return !(*this == rhs); - } - - friend std::ostream& operator<<(std::ostream& os, const matrix& x) - { - os << "{"; - for (unsigned r = 0; r < x.kRowCount; ++r) - { - os << "{"; - for (unsigned c = 0; c < x.kColCount; ++c) - { - if (c > 0) - os << ", "; - os << x[r][c]; - } - os << "}"; - } - os << "}"; - return os; - } - private: - RowType mRows[TRowCount]; - }; - - using mat2 = rmcv::matrix<2, 2, float>; - using mat3 = rmcv::matrix<3, 3, float>; - using mat4 = rmcv::matrix<4, 4, float>; - - using mat1x1 = rmcv::matrix<1, 1, float>; - using mat2x1 = rmcv::matrix<2, 1, float>; - using mat3x1 = rmcv::matrix<3, 1, float>; - using mat4x1 = rmcv::matrix<4, 1, float>; - - using mat2x1 = rmcv::matrix<2, 1, float>; - using mat2x2 = rmcv::matrix<2, 2, float>; - using mat3x2 = rmcv::matrix<3, 2, float>; - using mat4x2 = rmcv::matrix<4, 2, float>; - - using mat1x3 = rmcv::matrix<1, 3, float>; - using mat2x3 = rmcv::matrix<2, 3, float>; - using mat3x3 = rmcv::matrix<3, 3, float>; - using mat4x3 = rmcv::matrix<4, 3, float>; - - using mat1x4 = rmcv::matrix<1, 4, float>; - using mat2x4 = rmcv::matrix<2, 4, float>; - using mat3x4 = rmcv::matrix<3, 4, float>; - using mat4x4 = rmcv::matrix<4, 4, float>; - - template - glm::mat toGLM(const matrix& src) - { - glm::mat result; - for (int c = 0; c < C; ++c) - result[c] = src.getCol(c); - return result; - } - - template - rmcv::matrix toRMCV(const glm::mat& src) - { - rmcv::matrix result; - for (int c = 0; c < C; ++c) - result.setCol(c, src[c]); - return result; - } - - template - matrix transpose(const matrix& m) - { - return m.getTranspose(); - } - - template - matrix<4, 4, T> translate(const matrix<4, 4, T>& m, const vec<3, T>& v) - { - return toRMCV(glm::translate(toGLM(m), v)); - } - - template - matrix<4, 4, T> translate(const vec<3, T>& v) - { - return translate(matrix<4, 4, T>(T(1)), v); - } - - - template - matrix<4, 4, T> rotate(const matrix<4, 4, T>& m, T angle, const vec<3, T>& v) - { - return toRMCV(glm::rotate(toGLM(m), angle, v)); - } - - template - matrix<4, 4, T> rotate(T angle, const vec<3, T>& v) - { - return rotate(matrix<4, 4, T>(T(1)), angle, v); - } - - template - matrix<4, 4, T> scale(const matrix<4, 4, T>& m, const vec<3, T>& v) - { - return toRMCV(glm::scale(toGLM(m), v)); - } - - template - matrix<4, 4, T> scale(const vec<3, T>& v) - { - return scale(matrix<4, 4, T>(T(1)), v); - } - - - template - matrix inverse(const matrix& m) - { - return toRMCV(glm::inverse(toGLM(m))); - } - - template - Matrix identity() - { - return Matrix(1.f); - } - - template - T determinant(const matrix& m) - { - return glm::determinant(toGLM(m)); - } - - template - matrix operator*(const matrix& lhs, const matrix& rhs) - { - return toRMCV(toGLM(lhs) * toGLM(rhs)); - - // Causes precision difference - //matrix result(0.f); - //for (int m = 0; m < M; ++m) - // for (int p = 0; p < P; ++p) - // result.setElement(m, p, glm::dot(lhs.getRow(m), rhs.getCol(p))); - - //return result; - } - - template - matrix operator*(const matrix& lhs, const T& rhs) - { - return toRMCV(toGLM(lhs) * rhs); - } - - template - vec operator*(const matrix& lhs, const vec& rhs) - { - return toGLM(lhs) * rhs; - } - - template - vec operator*(const vec& lhs, const matrix& rhs) - { - return lhs * toGLM(rhs); - } - - - template - std::string to_string(const matrix& m) - { - return glm::to_string(toGLM(m)); - } - - template - void extractEulerAngleXYZ(const matrix<4, 4, T>& m, float& t1, float& t2, float& t3) - { - glm::extractEulerAngleXYZ(toGLM(m), t1, t2, t3); - } - - inline mat4 lookAtLH(const vec3& eye, const vec3& center, const vec3& up) - { - return toRMCV(glm::lookAtLH(eye, center, up)); - } - - // GLM decides whether to call LH or RH based on its - inline mat4 lookAt(const vec3& eye, const vec3& center, const vec3& up) - { - return toRMCV(glm::lookAt(eye, center, up)); - } - - inline mat4 ortho(float left, float right, float bottom, float top, float zNear, float zFar) - { - return toRMCV(glm::ortho(left, right, bottom, top, zNear, zFar)); - } - - inline mat4 perspective(float fovy, float aspect, float zNear, float zFar) - { - return toRMCV(glm::perspective(fovy, aspect, zNear, zFar)); - } - - inline mat4 eulerAngleY(float radians) - { - return toRMCV(glm::eulerAngleY(radians)); - } - - inline mat4 eulerAngleX(float radians) - { - return toRMCV(glm::eulerAngleX(radians)); - } - - inline mat4 eulerAngleXYZ(float radiansX, float radiansY, float radiansZ) - { - return toRMCV(glm::eulerAngleXYZ(radiansX, radiansY, radiansZ)); - } - - inline mat4 mat4_cast(const glm::quat& quat) - { - return toRMCV(glm::mat4_cast(quat)); - } - - inline mat3 mat3_cast(const glm::quat& quat) - { - return toRMCV(glm::mat3_cast(quat)); - } - - inline mat3 make_mat3(const float* rowMajor) - { - mat3 result; - memcpy(&result, rowMajor, sizeof(float) * 9); - return result; - } - - inline mat4 make_mat4(const float* rowMajor) - { - mat4 result; - memcpy(&result, rowMajor, sizeof(float) * 16); - return result; - } - - inline mat3 make_mat3_fromCols(float3 col0, float3 col1, float3 col2) - { - mat3 result; - result[0] = col0; - result[1] = col1; - result[2] = col2; - return result.getTranspose(); - } - - inline bool decompose(mat4 const& modelMatrix, vec3& scale, glm::quat& orientation, vec3& translation, vec3& skew, vec4& perspective) - { - return glm::decompose(toGLM(modelMatrix), scale, orientation, translation, skew, perspective); - } - - inline mat3 diagonal3x3(float3 scale) - { - return toRMCV(glm::diagonal3x3(scale)); - } - - template - bool lex_lt(const matrix& lhs, const matrix& rhs) - { - for (int r = 0; r < R; ++r) - { - for (int c = 0; c < C; ++c) - { - if (lhs[r][c] != rhs[r][c]) - return lhs[r][c] < rhs[r][c]; - } - } - return false; - } -} - -}; - -template -struct ::fmt::formatter> : formatter::RowType> -{ - using MatrixRowType = typename Falcor::rmcv::matrix::RowType; - - template - auto format(const Falcor::rmcv::matrix& matrix, FormatContext& ctx) const - { - auto out = ctx.out(); - for (int r = 0; r < TRowCount; ++r) - { - out = ::fmt::format_to(out, "{}", (r == 0) ? "{" : ", "); - out = formatter::format(matrix.getRow(r), ctx); - } - out = ::fmt::format_to(out, "}}"); - return out; - } -}; diff --git a/Source/Falcor/Utils/Math/MatrixMath.h b/Source/Falcor/Utils/Math/MatrixMath.h new file mode 100644 index 000000000..ae53d5f4a --- /dev/null +++ b/Source/Falcor/Utils/Math/MatrixMath.h @@ -0,0 +1,782 @@ +/*************************************************************************** + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. + # + # Redistribution and use in source and binary forms, with or without + # modification, are permitted provided that the following conditions + # are met: + # * Redistributions of source code must retain the above copyright + # notice, this list of conditions and the following disclaimer. + # * Redistributions in binary form must reproduce the above copyright + # notice, this list of conditions and the following disclaimer in the + # documentation and/or other materials provided with the distribution. + # * Neither the name of NVIDIA CORPORATION nor the names of its + # contributors may be used to endorse or promote products derived + # from this software without specific prior written permission. + # + # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY + # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + **************************************************************************/ + +/** + * Most of this code is derived from the GLM library at https://github.com/g-truc/glm + * + * License: https://github.com/g-truc/glm/blob/master/copying.txt + */ + +#pragma once + +#include "MatrixTypes.h" +#include "Vector.h" +#include "Quaternion.h" + +#include "Core/Assert.h" + +#include + +namespace Falcor +{ +namespace math +{ + +// ---------------------------------------------------------------------------- +// Binary operators (component-wise) +// ---------------------------------------------------------------------------- + +/// Binary * operator +template +[[nodiscard]] matrix operator*(const matrix& lhs, const T& rhs) +{ + matrix result; + for (int r = 0; r < R; ++r) + for (int c = 0; c < C; ++c) + result[r][c] = lhs[r][c] * rhs; + return result; +} + +// ---------------------------------------------------------------------------- +// Multiplication +// ---------------------------------------------------------------------------- + +/// Multiply matrix and matrix. +template +[[nodiscard]] matrix mul(const matrix& lhs, const matrix& rhs) +{ + matrix result; + for (int m = 0; m < M; ++m) + for (int p = 0; p < P; ++p) + result[m][p] = dot(lhs.getRow(m), rhs.getCol(p)); + return result; +} +/// Multiply matrix and vector. Vector is treated as a column vector. +template +[[nodiscard]] vector mul(const matrix& lhs, const vector& rhs) +{ + vector result; + for (int r = 0; r < R; ++r) + result[r] = dot(lhs.getRow(r), rhs); + return result; +} + +/// Multiply vector and matrix. Vector is treated as a row vector. +template +[[nodiscard]] vector mul(const vector& lhs, const matrix& rhs) +{ + vector result; + for (int c = 0; c < C; ++c) + result[c] = dot(lhs, rhs.getCol(c)); + return result; +} + +/// Transform a point by a 4x4 matrix. The point is treated as a column vector with a 1 in the 4th component. +template +[[nodiscard]] vector transformPoint(const matrix& m, const vector& v) +{ + return mul(m, vector(v, T(1))).xyz(); +} + +/// Transform a vector by a 3x3 matrix. +template +[[nodiscard]] vector transformVector(const matrix& m, const vector& v) +{ + return mul(m, v); +} + +/// Transform a vectir by a 4x4 matrix. The vector is treated as a column vector with a 0 in the 4th component. +template +[[nodiscard]] vector transformVector(const matrix& m, const vector& v) +{ + return mul(m, vector(v, T(0))).xyz(); +} + +// ---------------------------------------------------------------------------- +// Functions +// ---------------------------------------------------------------------------- + +/// Tranpose a matrix. +template +matrix transpose(const matrix& m) +{ + matrix result; + for (int r = 0; r < R; ++r) + for (int c = 0; c < C; ++c) + result[c][r] = m[r][c]; + return result; +} + +/// Apply a translation to a 4x4 matrix. +template +matrix translate(const matrix& m, const vector& v) +{ + matrix result(m); + result.setCol(3, m.getCol(0) * v.x + m.getCol(1) * v.y + m.getCol(2) * v.z + m.getCol(3)); + return result; +} + +/// Apply a rotation around an axis to a 4x4 matrix. +template +matrix rotate(const matrix& m, T angle, const vector& axis_) +{ + T a = angle; + T c = cos(a); + T s = sin(a); + + vector axis(normalize(axis_)); + vector temp((T(1) - c) * axis); + + matrix rotate; + rotate[0][0] = c + temp[0] * axis[0]; + rotate[0][1] = temp[1] * axis[0] - s * axis[2]; + rotate[0][2] = temp[2] * axis[0] + s * axis[1]; + + rotate[1][0] = temp[0] * axis[1] + s * axis[2]; + rotate[1][1] = c + temp[1] * axis[1]; + rotate[1][2] = temp[2] * axis[1] - s * axis[0]; + + rotate[2][0] = temp[0] * axis[2] - s * axis[1]; + rotate[2][1] = temp[1] * axis[2] + s * axis[0]; + rotate[2][2] = c + temp[2] * axis[2]; + + matrix result; + result.setCol(0, m.getCol(0) * rotate[0][0] + m.getCol(1) * rotate[1][0] + m.getCol(2) * rotate[2][0]); + result.setCol(1, m.getCol(0) * rotate[0][1] + m.getCol(1) * rotate[1][1] + m.getCol(2) * rotate[2][1]); + result.setCol(2, m.getCol(0) * rotate[0][2] + m.getCol(1) * rotate[1][2] + m.getCol(2) * rotate[2][2]); + result.setCol(3, m.getCol(3)); + + return result; +} + +/// Apply a scale to a 4x4 matrix. +template +matrix scale(const matrix& m, const vector& v) +{ + matrix result; + result.setCol(0, m.getCol(0) * v[0]); + result.setCol(1, m.getCol(1) * v[1]); + result.setCol(2, m.getCol(2) * v[2]); + result.setCol(3, m.getCol(3)); + return result; +} + +/// Compute determinant of a 2x2 matrix. +template +[[nodiscard]] inline T determinant(const matrix& m) +{ + return m[0][0] * m[1][1] - m[1][0] * m[0][1]; +} + +/// Compute determinant of a 3x3 matrix. +template +[[nodiscard]] inline T determinant(const matrix& m) +{ + T a = m[0][0] * (m[1][1] * m[2][2] - m[2][1] * m[1][2]); + T b = m[1][0] * (m[0][1] * m[2][2] - m[2][1] * m[0][2]); + T c = m[2][0] * (m[0][1] * m[1][2] - m[1][1] * m[0][2]); + return a - b + c; +} + +/// Compute determinant of a 4x4 matrix. +template +[[nodiscard]] inline T determinant(const matrix& m) +{ + T subFactor00 = m[2][2] * m[3][3] - m[3][2] * m[2][3]; + T subFactor01 = m[2][1] * m[3][3] - m[3][1] * m[2][3]; + T subFactor02 = m[2][1] * m[3][2] - m[3][1] * m[2][2]; + T subFactor03 = m[2][0] * m[3][3] - m[3][0] * m[2][3]; + T subFactor04 = m[2][0] * m[3][2] - m[3][0] * m[2][2]; + T subFactor05 = m[2][0] * m[3][1] - m[3][0] * m[2][1]; + + vector detCof( + +(m[1][1] * subFactor00 - m[1][2] * subFactor01 + m[1][3] * subFactor02), // + -(m[1][0] * subFactor00 - m[1][2] * subFactor03 + m[1][3] * subFactor04), // + +(m[1][0] * subFactor01 - m[1][1] * subFactor03 + m[1][3] * subFactor05), // + -(m[1][0] * subFactor02 - m[1][1] * subFactor04 + m[1][2] * subFactor05) // + ); + + return m[0][0] * detCof[0] + m[0][1] * detCof[1] + m[0][2] * detCof[2] + m[0][3] * detCof[3]; +} + +/// Compute inverse of a 2x2 matrix. +template +[[nodiscard]] inline matrix inverse(const matrix& m) +{ + T oneOverDet = T(1) / determinant(m); + return matrix{ + +m[1][1] * oneOverDet, -m[0][1] * oneOverDet, // row 0 + -m[1][0] * oneOverDet, +m[0][0] * oneOverDet // row 1 + }; +} + +/// Compute inverse of a 3x3 matrix. +template +[[nodiscard]] inline matrix inverse(const matrix& m) +{ + T oneOverDet = T(1) / determinant(m); + + matrix result; + result[0][0] = +(m[1][1] * m[2][2] - m[1][2] * m[2][1]) * oneOverDet; + result[0][1] = -(m[0][1] * m[2][2] - m[0][2] * m[2][1]) * oneOverDet; + result[0][2] = +(m[0][1] * m[1][2] - m[0][2] * m[1][1]) * oneOverDet; + result[1][0] = -(m[1][0] * m[2][2] - m[1][2] * m[2][0]) * oneOverDet; + result[1][1] = +(m[0][0] * m[2][2] - m[0][2] * m[2][0]) * oneOverDet; + result[1][2] = -(m[0][0] * m[1][2] - m[0][2] * m[1][0]) * oneOverDet; + result[2][0] = +(m[1][0] * m[2][1] - m[1][1] * m[2][0]) * oneOverDet; + result[2][1] = -(m[0][0] * m[2][1] - m[0][1] * m[2][0]) * oneOverDet; + result[2][2] = +(m[0][0] * m[1][1] - m[0][1] * m[1][0]) * oneOverDet; + return result; +} + +/// Compute inverse of a 4x4 matrix. +template +[[nodiscard]] inline matrix inverse(const matrix& m) +{ + T c00 = m[2][2] * m[3][3] - m[2][3] * m[3][2]; + T c02 = m[2][1] * m[3][3] - m[2][3] * m[3][1]; + T c03 = m[2][1] * m[3][2] - m[2][2] * m[3][1]; + + T c04 = m[1][2] * m[3][3] - m[1][3] * m[3][2]; + T c06 = m[1][1] * m[3][3] - m[1][3] * m[3][1]; + T c07 = m[1][1] * m[3][2] - m[1][2] * m[3][1]; + + T c08 = m[1][2] * m[2][3] - m[1][3] * m[2][2]; + T c10 = m[1][1] * m[2][3] - m[1][3] * m[2][1]; + T c11 = m[1][1] * m[2][2] - m[1][2] * m[2][1]; + + T c12 = m[0][2] * m[3][3] - m[0][3] * m[3][2]; + T c14 = m[0][1] * m[3][3] - m[0][3] * m[3][1]; + T c15 = m[0][1] * m[3][2] - m[0][2] * m[3][1]; + + T c16 = m[0][2] * m[2][3] - m[0][3] * m[2][2]; + T c18 = m[0][1] * m[2][3] - m[0][3] * m[2][1]; + T c19 = m[0][1] * m[2][2] - m[0][2] * m[2][1]; + + T c20 = m[0][2] * m[1][3] - m[0][3] * m[1][2]; + T c22 = m[0][1] * m[1][3] - m[0][3] * m[1][1]; + T c23 = m[0][1] * m[1][2] - m[0][2] * m[1][1]; + + vector fac0(c00, c00, c02, c03); + vector fac1(c04, c04, c06, c07); + vector fac2(c08, c08, c10, c11); + vector fac3(c12, c12, c14, c15); + vector fac4(c16, c16, c18, c19); + vector fac5(c20, c20, c22, c23); + + vector vec0(m[0][1], m[0][0], m[0][0], m[0][0]); + vector vec1(m[1][1], m[1][0], m[1][0], m[1][0]); + vector vec2(m[2][1], m[2][0], m[2][0], m[2][0]); + vector vec3(m[3][1], m[3][0], m[3][0], m[3][0]); + + vector inv0(vec1 * fac0 - vec2 * fac1 + vec3 * fac2); + vector inv1(vec0 * fac0 - vec2 * fac3 + vec3 * fac4); + vector inv2(vec0 * fac1 - vec1 * fac3 + vec3 * fac5); + vector inv3(vec0 * fac2 - vec1 * fac4 + vec2 * fac5); + + vector signA(+1, -1, +1, -1); + vector signB(-1, +1, -1, +1); + matrix inverse = matrixFromColumns(inv0 * signA, inv1 * signB, inv2 * signA, inv3 * signB); + + vector row0(inverse[0][0], inverse[0][1], inverse[0][2], inverse[0][3]); + + vector dot0(m.getCol(0) * row0); + T dot1 = (dot0.x + dot0.y) + (dot0.z + dot0.w); + + T oneOverDet = T(1) / dot1; + + return inverse * oneOverDet; +} + +/// Compute the (X * Y * Z) euler angles of a 4x4 matrix. +template +void extractEulerAngleXYZ(const matrix& m, float& angleX, float& angleY, float& angleZ) +{ + T t1 = atan2(m[1][2], m[2][2]); + T c2 = sqrt(m[0][0] * m[0][0] + m[0][1] * m[0][1]); + T t2 = atan2(-m[0][2], c2); + T s1 = sin(t1); + T c1 = cos(t1); + T t3 = atan2(s1 * m[2][0] - c1 * m[1][0], c1 * m[1][1] - s1 * m[2][1]); + angleX = -t1; + angleY = -t2; + angleZ = -t3; +} + +/// Decomposes a model matrix into translation, rotation and scale components. +template +inline bool decompose( + const matrix& modelMatrix, + vector& scale, + quat& orientation, + vector& translation, + vector& skew, + vector& perspective +) +{ + // See https://caff.de/posts/4X4-matrix-decomposition/decomposition.pdf + + const T eps = std::numeric_limits::epsilon(); + + matrix localMatrix(modelMatrix); + + // Abort if zero matrix. + if (abs(localMatrix[3][3]) < eps) + return false; + + // Normalize the matrix. + for (int i = 0; i < 4; ++i) + for (int j = 0; j < 4; ++j) + localMatrix[i][j] /= localMatrix[3][3]; + + // perspectiveMatrix is used to solve for perspective, but it also provides + // an easy way to test for singularity of the upper 3x3 component. + matrix perspectiveMatrix(localMatrix); + perspectiveMatrix[3] = vector(0, 0, 0, 1); + if (abs(determinant(perspectiveMatrix)) < eps) + return false; + + // First, isolate perspective. This is the messiest. + if (abs(localMatrix[3][0]) >= eps || abs(localMatrix[3][1]) >= eps || abs(localMatrix[3][2]) >= eps) + { + // rightHandSide is the right hand side of the equation. + vector rightHandSide = localMatrix[3]; + + // Solve the equation by inverting perspectiveMatrix and multiplying + // rightHandSide by the inverse. + // (This is the easiest way, not necessarily the best.) + matrix inversePerspectiveMatrix = inverse(perspectiveMatrix); + matrix transposedInversePerspectiveMatrix = transpose(inversePerspectiveMatrix); + + perspective = mul(transposedInversePerspectiveMatrix, rightHandSide); + + // Clear the perspective partition. + localMatrix[3] = vector(0, 0, 0, 1); + } + else + { + // No perspective. + perspective = vector(0, 0, 0, 1); + } + + // Next take care of translation (easy). + translation = localMatrix.getCol(3).xyz(); + localMatrix.setRow(3, vector(0, 0, 0, 1)); + + vector row[3]; + + // Now get scale and shear. + for (int i = 0; i < 3; ++i) + for (int j = 0; j < 3; ++j) + row[i][j] = localMatrix[j][i]; + + // Compute X scale factor and normalize first row. + scale.x = length(row[0]); + row[0] = normalize(row[0]); + + // Compute XY shear factor and make 2nd row orthogonal to 1st. + skew.z = dot(row[0], row[1]); + row[1] = row[1] - skew.z * row[0]; + + // Now, compute Y scale and normalize 2nd row. + scale.y = length(row[1]); + row[1] = normalize(row[1]); + skew.z /= scale.y; + + // Compute XZ and YZ shears, orthogonalize 3rd row. + skew.y = dot(row[0], row[2]); + row[2] = row[2] - skew.y * row[0]; + skew.x = dot(row[1], row[2]); + row[2] = row[2] - skew.x * row[1]; + + // Next, get Z scale and normalize 3rd row. + scale.z = length(row[2]); + row[2] = normalize(row[2]); + skew.y /= scale.z; + skew.x /= scale.z; + + // At this point, the matrix (in rows[]) is orthonormal. + // Check for a coordinate system flip. If the determinant + // is -1, then negate the matrix and the scaling factors. + if (dot(row[0], cross(row[1], row[2])) < T(0)) + { + scale *= T(-1); + for (int i = 0; i < 3; i++) + row[i] *= T(-1); + } + + // Now, get the rotations out, as described in the gem. + int i, j, k = 0; + T root, trace = row[0].x + row[1].y + row[2].z; + if (trace > T(0)) + { + root = sqrt(trace + T(1)); + orientation.w = T(0.5) * root; + root = T(0.5) / root; + orientation.x = root * (row[1].z - row[2].y); + orientation.y = root * (row[2].x - row[0].z); + orientation.z = root * (row[0].y - row[1].x); + } // end if > 0 + else + { + static int next[3] = {1, 2, 0}; + i = 0; + if (row[1].y > row[0].x) + i = 1; + if (row[2].z > row[i][i]) + i = 2; + j = next[i]; + k = next[j]; + + root = sqrt(row[i][i] - row[j][j] - row[k][k] + T(1)); + + orientation[i] = T(0.5) * root; + root = T(0.5) / root; + orientation[j] = root * (row[i][j] + row[j][i]); + orientation[k] = root * (row[i][k] + row[k][i]); + orientation.w = root * (row[j][k] - row[k][j]); + } // end if <= 0 + + return true; +} + +// ---------------------------------------------------------------------------- +// Construction +// ---------------------------------------------------------------------------- + +/// Creates a matrix from coefficients in row-major order. +template +[[nodiscard]] inline matrix matrixFromCoefficients(const T* coeffs) +{ + matrix m; + std::memcpy(&m, coeffs, sizeof(T) * R * C); + return m; +} + +/// Creates a matrix from column vectors. +template +[[nodiscard]] inline matrix matrixFromColumns(const vector& col0) +{ + matrix m; + m.setCol(0, col0); + return m; +} + +/// Creates a matrix from column vectors. +template +[[nodiscard]] inline matrix matrixFromColumns(const vector& col0, const vector& col1) +{ + matrix m; + m.setCol(0, col0); + m.setCol(1, col1); + return m; +} + +/// Creates a matrix from column vectors. +template +[[nodiscard]] inline matrix matrixFromColumns(const vector& col0, const vector& col1, const vector& col2) +{ + matrix m; + m.setCol(0, col0); + m.setCol(1, col1); + m.setCol(2, col2); + return m; +} + +/// Creates a matrix from column vectors. +template +[[nodiscard]] inline matrix matrixFromColumns( + const vector& col0, + const vector& col1, + const vector& col2, + const vector& col3 +) +{ + matrix m; + m.setCol(0, col0); + m.setCol(1, col1); + m.setCol(2, col2); + m.setCol(3, col3); + return m; +} + +/// Creates a square matrix from a diagonal vector. +template +[[nodiscard]] inline matrix matrixFromDiagonal(const vector& diag) +{ + matrix m = matrix::zeros(); + for (int i = 0; i < N; i++) + m[i][i] = diag[i]; + return m; +} + +/// Creates a right-handed perspective projection matrix. Depth is mapped to [0, 1]. +template +[[nodiscard]] inline matrix perspective(T fovy, T aspect, T zNear, T zFar) +{ + FALCOR_ASSERT(abs(aspect - std::numeric_limits::epsilon()) > T(0)); + + T tanHalfFovy = tan(fovy / T(2)); + + matrix m = matrix::zeros(); + m[0][0] = T(1) / (aspect * tanHalfFovy); + m[1][1] = T(1) / (tanHalfFovy); + m[2][2] = zFar / (zNear - zFar); + m[3][2] = -T(1); + m[2][3] = -(zFar * zNear) / (zFar - zNear); + return m; +} + +/// Creates a right-handed orthographic projection matrix. Depth is mapped to [0, 1]. +template +[[nodiscard]] inline matrix ortho(T left, T right, T bottom, T top, T zNear, T zFar) +{ + matrix m = matrix::identity(); + m[0][0] = T(2) / (right - left); + m[1][1] = T(2) / (top - bottom); + m[2][2] = -T(1) / (zFar - zNear); + m[0][3] = -(right + left) / (right - left); + m[1][3] = -(top + bottom) / (top - bottom); + m[2][3] = -zNear / (zFar - zNear); + return m; +} + +/// Creates a translation matrix. +template +[[nodiscard]] inline matrix matrixFromTranslation(const vector& v) +{ + return translate(matrix::identity(), v); +} + +/// Creates a rotation matrix from an angle and an axis. +template +[[nodiscard]] inline matrix matrixFromRotation(T angle, const vector& axis) +{ + return rotate(matrix::identity(), angle, axis); +} + +/// Creates a rotation matrix around the X-axis. +template +[[nodiscard]] inline matrix matrixFromRotationX(T angle) +{ + T c = cos(angle); + T s = sin(angle); + + // clang-format off + return matrix{ + T(1), T(0), T(0), T(0), // row 0 + T(0), c, -s, T(0), // row 1 + T(0), s, c, T(0), // row 2 + T(0), T(0), T(0), T(1) // row 3 + }; + // clang-format on +} + +/// Creates a rotation matrix around the Y-axis. +template +[[nodiscard]] inline matrix matrixFromRotationY(T angle) +{ + T c = cos(angle); + T s = sin(angle); + + // clang-format off + return matrix{ + c, T(0), s, T(0), // row 0 + T(0), T(1), T(0), T(0), // row 1 + -s, T(0), c, T(0), // row 2 + T(0), T(0), T(0), T(1) // row 3 + }; + // clang-format on +} + +/// Creates a rotation matrix around the Z-axis. +template +[[nodiscard]] inline matrix matrixFromRotationZ(T angle) +{ + T c = cos(angle); + T s = sin(angle); + + // clang-format off + return matrix{ + c, -s, T(0), T(0), // row 0 + s, c, T(0), T(0), // row 1 + T(0), T(0), T(1), T(0), // row 2 + T(0), T(0), T(0), T(1) // row 3 + }; + // clang-format on +} + +/// Creates a rotation matrix (X * Y * Z). +template +[[nodiscard]] inline matrix matrixFromRotationXYZ(T angleX, T angleY, T angleZ) +{ + T c1 = cos(-angleX); + T c2 = cos(-angleY); + T c3 = cos(-angleZ); + T s1 = sin(-angleX); + T s2 = sin(-angleY); + T s3 = sin(-angleZ); + + matrix m; + m[0][0] = c2 * c3; + m[0][1] = c2 * s3; + m[0][2] = -s2; + m[0][3] = T(0); + + m[1][0] = -c1 * s3 + s1 * s2 * c3; + m[1][1] = c1 * c3 + s1 * s2 * s3; + m[1][2] = s1 * c2; + m[1][3] = T(0); + + m[2][0] = s1 * s3 + c1 * s2 * c3; + m[2][1] = -s1 * c3 + c1 * s2 * s3; + m[2][2] = c1 * c2; + m[2][3] = T(0); + + m[3][0] = T(0); + m[3][1] = T(0); + m[3][2] = T(0); + m[3][3] = T(1); + + return m; +} + +/// Creates a scaling matrix. +template +[[nodiscard]] inline matrix matrixFromScaling(const vector& v) +{ + return scale(matrix::identity(), v); +} + +/** + * Build a look-at matrix. + * If right handed, forward direction is mapped onto -Z axis. + * If left handed, forward direction is mapped onto +Z axis. + * @param eye Eye position + * @param center Center position + * @param up Up vector + * @param handedness Coordinate system handedness. + */ +template +[[nodiscard]] inline matrix matrixFromLookAt( + const vector& eye, + const vector& center, + const vector& up, + Handedness handedness = Handedness::RightHanded +) +{ + vector f(handedness == Handedness::RightHanded ? normalize(eye - center) : normalize(center - eye)); + vector r(normalize(cross(up, f))); + vector u(cross(f, r)); + + matrix result = matrix::identity(); + result[0][0] = r.x; + result[0][1] = r.y; + result[0][2] = r.z; + result[1][0] = u.x; + result[1][1] = u.y; + result[1][2] = u.z; + result[2][0] = f.x; + result[2][1] = f.y; + result[2][2] = f.z; + result[0][3] = -dot(r, eye); + result[1][3] = -dot(u, eye); + result[2][3] = -dot(f, eye); + + return result; +} + +template +[[nodiscard]] inline matrix matrixFromQuat(const quat& q) +{ + matrix m; + T qxx(q.x * q.x); + T qyy(q.y * q.y); + T qzz(q.z * q.z); + T qxz(q.x * q.z); + T qxy(q.x * q.y); + T qyz(q.y * q.z); + T qwx(q.w * q.x); + T qwy(q.w * q.y); + T qwz(q.w * q.z); + + m[0][0] = T(1) - T(2) * (qyy + qzz); + m[0][1] = T(2) * (qxy - qwz); + m[0][2] = T(2) * (qxz + qwy); + + m[1][0] = T(2) * (qxy + qwz); + m[1][1] = T(1) - T(2) * (qxx + qzz); + m[1][2] = T(2) * (qyz - qwx); + + m[2][0] = T(2) * (qxz - qwy); + m[2][1] = T(2) * (qyz + qwx); + m[2][2] = T(1) - T(2) * (qxx + qyy); + + return m; +} + +template +[[nodiscard]] std::string to_string(const matrix& m) +{ + return ::fmt::format("{}", m); +} + +template +bool lex_lt(const matrix& lhs, const matrix& rhs) +{ + for (int r = 0; r < R; ++r) + { + for (int c = 0; c < C; ++c) + { + if (lhs[r][c] != rhs[r][c]) + return lhs[r][c] < rhs[r][c]; + } + } + return false; +} +} // namespace math +} // namespace Falcor + +template +struct fmt::formatter> : formatter::RowType> +{ + using MatrixRowType = typename Falcor::math::matrix::RowType; + + template + auto format(const Falcor::math::matrix& matrix, FormatContext& ctx) const + { + auto out = ctx.out(); + for (int r = 0; r < R; ++r) + { + out = ::fmt::format_to(out, "{}", (r == 0) ? "{" : ", "); + out = formatter::format(matrix.getRow(r), ctx); + } + out = ::fmt::format_to(out, "}}"); + return out; + } +}; diff --git a/Source/Falcor/Utils/Math/MatrixTypes.h b/Source/Falcor/Utils/Math/MatrixTypes.h new file mode 100644 index 000000000..5d689231f --- /dev/null +++ b/Source/Falcor/Utils/Math/MatrixTypes.h @@ -0,0 +1,221 @@ +/*************************************************************************** + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. + # + # Redistribution and use in source and binary forms, with or without + # modification, are permitted provided that the following conditions + # are met: + # * Redistributions of source code must retain the above copyright + # notice, this list of conditions and the following disclaimer. + # * Redistributions in binary form must reproduce the above copyright + # notice, this list of conditions and the following disclaimer in the + # documentation and/or other materials provided with the distribution. + # * Neither the name of NVIDIA CORPORATION nor the names of its + # contributors may be used to endorse or promote products derived + # from this software without specific prior written permission. + # + # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY + # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + # (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 "ScalarTypes.h" +#include "VectorTypes.h" +#include "Core/Assert.h" + +#include + +namespace Falcor +{ +namespace math +{ + +/** + * Matrix type with row-major storage. + * + * The semantics are aligned with Slang: + * - Row major storage + * - Math operators are element-wise (e.g. +, -, *, /) + * - Free standing functions for matrix operations (e.g. mul(), transpose(), etc.) + * + * @tparam T Scalar type + * @tparam RowCount Number of rows (1-4) + * @tparam ColCount Number of columns (1-4) + */ +template +class matrix +{ + static_assert(RowCount >= 1 && RowCount <= 4); + static_assert(ColCount >= 1 && ColCount <= 4); + + // For now, we only support float, but want to have the type T visible + static_assert(std::is_same_v); + +private: + template + friend class matrix; + +public: + using value_type = T; + using RowType = vector; + using ColType = vector; + + static constexpr int getRowCount() { return RowCount; } + static constexpr int getColCount() { return ColCount; } + + matrix() : matrix(Form::Identity) {} + + template + matrix(std::initializer_list v) + { + T* f = &mRows[0][0]; + for (auto it = v.begin(); it != v.end(); ++it, ++f) + *f = static_cast(*it); + } + + matrix(const matrix&) = default; + matrix(matrix&&) noexcept = default; + + /// Construct matrix from another matrix with different dimensions. + /// In HLSL/Slang, destination matrix must be equal or smaller than source matrix. + /// In Falcor, destination matrix can be larger than source matrix (initialized with identity). + template + matrix(const matrix& other) : matrix(Form::Identity) + { + for (int r = 0; r < std::min(RowCount, R); ++r) + { + std::memcpy(&mRows[r], &other.mRows[r], std::min(ColCount, C) * sizeof(T)); + } + } + + matrix& operator=(const matrix&) = default; + matrix& operator=(matrix&&) = default; + + template + matrix& operator=(const matrix& other) + { + for (int r = 0; r < std::min(RowCount, R); ++r) + { + std::memcpy(&mRows[r], &other.mRows[r], std::min(ColCount, C) * sizeof(T)); + } + + return *this; + } + + /// Zero matrix. + [[nodiscard]] static matrix zeros() { return matrix(Form::Zeros); } + + /// Identity matrix. + [[nodiscard]] static matrix identity() { return matrix(Form::Identity); } + + T* data() { return &mRows[0][0]; } + const T* data() const { return &mRows[0][0]; } + + RowType& operator[](int r) + { + FALCOR_ASSERT_LT(r, RowCount); + return mRows[r]; + } + const RowType& operator[](int r) const + { + FALCOR_ASSERT_LT(r, RowCount); + return mRows[r]; + } + + RowType& getRow(int r) + { + FALCOR_ASSERT_LT(r, RowCount); + return mRows[r]; + } + const RowType& getRow(int r) const + { + FALCOR_ASSERT_LT(r, RowCount); + return mRows[r]; + } + + void setRow(int r, const RowType& v) + { + FALCOR_ASSERT_LT(r, RowCount); + mRows[r] = v; + } + + ColType getCol(int col) const + { + FALCOR_ASSERT_LT(col, ColCount); + ColType result; + for (int r = 0; r < RowCount; ++r) + result[r] = mRows[r][col]; + return result; + } + + void setCol(int col, const ColType& v) + { + FALCOR_ASSERT_LT(col, ColCount); + for (int r = 0; r < RowCount; ++r) + mRows[r][col] = v[r]; + } + + bool operator==(const matrix& rhs) const { return std::memcmp(this, &rhs, sizeof(*this)) == 0; } + bool operator!=(const matrix& rhs) const { return !(*this == rhs); } + +private: + enum class Form + { + Undefined, + Zeros, + Identity, + }; + + explicit matrix(Form form) + { + switch (form) + { + case Form::Undefined: +#ifdef _DEBUG + for (int i = 0; i < RowCount; ++i) + mRows[i] = RowType(std::numeric_limits::quiet_NaN()); +#endif + break; + case Form::Zeros: + std::memset(this, 0, sizeof(*this)); + break; + case Form::Identity: + std::memset(this, 0, sizeof(*this)); + for (int i = 0; i < std::min(RowCount, ColCount); ++i) + mRows[i][i] = T(1); + break; + } + } + + RowType mRows[RowCount]; +}; + +using float2x2 = matrix; + +using float3x3 = matrix; + +using float1x4 = matrix; +using float2x4 = matrix; +using float3x4 = matrix; +using float4x4 = matrix; + +} // namespace math + +using float2x2 = math::float2x2; + +using float3x3 = math::float3x3; + +using float1x4 = math::float1x4; +using float2x4 = math::float2x4; +using float3x4 = math::float3x4; +using float4x4 = math::float4x4; + +} // namespace Falcor diff --git a/Source/Falcor/Utils/Math/MatrixUtils.slang b/Source/Falcor/Utils/Math/MatrixUtils.slang index 828072803..ab87cacb7 100644 --- a/Source/Falcor/Utils/Math/MatrixUtils.slang +++ b/Source/Falcor/Utils/Math/MatrixUtils.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -43,10 +43,7 @@ struct xform4x4 { row_major float4x4 xform; - __init(float4x4 m) - { - xform = m; - } + __init(float4x4 m) { xform = m; } } // Affine transform @@ -54,23 +51,18 @@ struct xform3x4 { row_major float3x4 xform; - __init(float3x4 m) - { - xform = m; - } + __init(float3x4 m) { xform = m; } - __init(float4x4 m) - { - xform = float3x4(m[0], m[1], m[2]); - } + __init(float4x4 m) { xform = float3x4(m[0], m[1], m[2]); } } xform3x4 concat(xform3x4 aFromB, xform3x4 bFromC) { xform3x4 aFromC; aFromC.xform = (float3x4)mul( - float4x4(aFromB.xform[0], aFromB.xform[1], aFromB.xform[2], float4(0,0,0,1)), - float4x4(bFromC.xform[0],bFromC.xform[1],bFromC.xform[2],float4(0,0,0,1))); + float4x4(aFromB.xform[0], aFromB.xform[1], aFromB.xform[2], float4(0, 0, 0, 1)), + float4x4(bFromC.xform[0], bFromC.xform[1], bFromC.xform[2], float4(0, 0, 0, 1)) + ); return aFromC; } diff --git a/Source/Falcor/Utils/Math/PackedFormats.h b/Source/Falcor/Utils/Math/PackedFormats.h index 7cd69858e..73cf24da2 100644 --- a/Source/Falcor/Utils/Math/PackedFormats.h +++ b/Source/Falcor/Utils/Math/PackedFormats.h @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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,62 +27,69 @@ **************************************************************************/ #pragma once #include "Vector.h" +#include "FormatConversion.h" #include -/** Host-side utility functions for format conversion. - - The functions defined here should match the corresponding GPU-side - functions, but numerical differences are possible. -*/ +/** + * Host-side utility functions for format conversion. + * + * The functions defined here should match the corresponding GPU-side + * functions, but numerical differences are possible. + */ namespace Falcor { - /** Helper function to reflect the folds of the lower hemisphere - over the diagonals in the octahedral map. - */ - inline float2 oct_wrap(float2 v) - { - return { (1.f - std::abs(v.y)) * (v.x >= 0.f ? 1.f : -1.f), (1.f - std::abs(v.x)) * (v.y >= 0.f ? 1.f : -1.f) }; - } +/** + * Helper function to reflect the folds of the lower hemisphere + * over the diagonals in the octahedral map. + */ +inline float2 oct_wrap(float2 v) +{ + return {(1.f - std::abs(v.y)) * (v.x >= 0.f ? 1.f : -1.f), (1.f - std::abs(v.x)) * (v.y >= 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. - */ - inline 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 = float2(n.x, n.y) * (1.f / (std::abs(n.x) + std::abs(n.y) + std::abs(n.z))); - p = (n.z < 0.f) ? oct_wrap(p) : p; - return p; - } +/** + * 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. + */ +inline 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 = float2(n.x, n.y) * (1.f / (std::abs(n.x) + std::abs(n.y) + std::abs(n.z))); + p = (n.z < 0.f) ? oct_wrap(p) : p; + return p; +} - /** 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. - */ - inline float3 oct_to_ndir_snorm(float2 p) - { - float3 n = float3(p.x, p.y, 1.f - std::abs(p.x) - std::abs(p.y)); - float2 tmp = (n.z < 0.0) ? oct_wrap(float2(n.x, n.y)) : float2(n.x, n.y); - n.x = tmp.x; - n.y = tmp.y; - return normalize(n); - } +/** + * 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. + */ +inline float3 oct_to_ndir_snorm(float2 p) +{ + float3 n = float3(p.x, p.y, 1.f - std::abs(p.x) - std::abs(p.y)); + float2 tmp = (n.z < 0.0) ? oct_wrap(float2(n.x, n.y)) : float2(n.x, n.y); + n.x = tmp.x; + n.y = tmp.y; + return normalize(n); +} - /** Encode a normal packed as 2x 16-bit snorms in the octahedral mapping. - */ - inline uint32_t encodeNormal2x16(float3 normal) - { - float2 octNormal = ndir_to_oct_snorm(normal); - return glm::packSnorm2x16(octNormal); - } +/** + * Encode a normal packed as 2x 16-bit snorms in the octahedral mapping. + */ +inline uint32_t encodeNormal2x16(float3 normal) +{ + float2 octNormal = ndir_to_oct_snorm(normal); + return packSnorm2x16(octNormal); +} - /** Decode a normal packed as 2x 16-bit snorms in the octahedral mapping. - */ - inline float3 decodeNormal2x16(uint32_t packedNormal) - { - float2 octNormal = glm::unpackSnorm2x16(packedNormal); - return oct_to_ndir_snorm(octNormal); - } +/** + * Decode a normal packed as 2x 16-bit snorms in the octahedral mapping. + */ +inline float3 decodeNormal2x16(uint32_t packedNormal) +{ + float2 octNormal = unpackSnorm2x16(packedNormal); + return oct_to_ndir_snorm(octNormal); } +} // namespace Falcor diff --git a/Source/Falcor/Utils/Math/PackedFormats.slang b/Source/Falcor/Utils/Math/PackedFormats.slang index 0df3e9dac..480b77dc1 100644 --- a/Source/Falcor/Utils/Math/PackedFormats.slang +++ b/Source/Falcor/Utils/Math/PackedFormats.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-21, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -29,40 +29,45 @@ import Utils.Math.MathHelpers; import Utils.Math.FormatConversion; import Utils.Color.ColorHelpers; -/** Encode a normal packed as 2x 8-bit snorms in the octahedral mapping. The high 16 bits are unused. -*/ +/** + * Encode a normal packed as 2x 8-bit snorms in the octahedral mapping. The high 16 bits are unused. + */ uint encodeNormal2x8(float3 normal) { float2 octNormal = ndir_to_oct_snorm(normal); return packSnorm2x8(octNormal); } -/** Decode a normal packed as 2x 8-bit snorms in the octahedral mapping. -*/ +/** + * Decode a normal packed as 2x 8-bit snorms in the octahedral mapping. + */ float3 decodeNormal2x8(uint packedNormal) { float2 octNormal = unpackSnorm2x8(packedNormal); return oct_to_ndir_snorm(octNormal); } -/** Encode a normal packed as 2x 16-bit snorms in the octahedral mapping. -*/ +/** + * Encode a normal packed as 2x 16-bit snorms in the octahedral mapping. + */ uint encodeNormal2x16(float3 normal) { float2 octNormal = ndir_to_oct_snorm(normal); return packSnorm2x16(octNormal); } -/** Decode a normal packed as 2x 16-bit snorms in the octahedral mapping. -*/ +/** + * Decode a normal packed as 2x 16-bit snorms in the octahedral mapping. + */ float3 decodeNormal2x16(uint packedNormal) { float2 octNormal = unpackSnorm2x16(packedNormal); return oct_to_ndir_snorm(octNormal); } -/** Encode a normal packed as 3x 16-bit snorms. Note: The high 16 bits of the second dword are unused. -*/ +/** + * Encode a normal packed as 3x 16-bit snorms. Note: The high 16 bits of the second dword are unused. + */ uint2 encodeNormal3x16(float3 normal) { uint2 packedNormal; @@ -71,8 +76,9 @@ uint2 encodeNormal3x16(float3 normal) return packedNormal; } -/** Decode a normal packed as 3x 16-bit snorms. Note: The high 16 bits of the second dword are unused. -*/ +/** + * Decode a normal packed as 3x 16-bit snorms. Note: The high 16 bits of the second dword are unused. + */ float3 decodeNormal3x16(uint2 packedNormal) { float3 normal; @@ -81,21 +87,23 @@ float3 decodeNormal3x16(uint2 packedNormal) return normalize(normal); } -/** Flattens a 3D index into a 1D index in scanline order. - \param[in] idx A 3D index. - \param[in] width The width of the indexed 3D structure. - \param[in] height The height of the indexed 3D structure. -*/ +/** + * Flattens a 3D index into a 1D index in scanline order. + * @param[in] idx A 3D index. + * @param[in] width The width of the indexed 3D structure. + * @param[in] height The height of the indexed 3D structure. + */ uint flatten3D(uint3 idx, uint width, uint height) { return idx.x + width * (idx.y + height * idx.z); } -/** Unflattens a 1D index into a 3D index in scanline order. - \param[in] idx A flattened 3D index. - \param[in] width The width of the indexed 3D structure. - \param[in] height The height of the indexed 3D structure. -*/ +/** + * Unflattens a 1D index into a 3D index in scanline order. + * @param[in] idx A flattened 3D index. + * @param[in] width The width of the indexed 3D structure. + * @param[in] height The height of the indexed 3D structure. + */ uint3 unflatten3D(uint flattenedIdx, uint width, uint height) { uint3 idx = uint3(width, width * height, 0); @@ -106,13 +114,14 @@ uint3 unflatten3D(uint flattenedIdx, uint width, uint height) return idx; } -/** Encode an RGB color into a 32-bit LogLuv HDR format. - The supported luminance range is roughly 10^-6..10^6 in 0.17% steps. - - The log-luminance is encoded with 14 bits and chroma with 9 bits each. - This was empirically more accurate than using 8 bit chroma. - Black (all zeros) is handled exactly. -*/ +/** + * Encode an RGB color into a 32-bit LogLuv HDR format. + * The supported luminance range is roughly 10^-6..10^6 in 0.17% steps. + * + * The log-luminance is encoded with 14 bits and chroma with 9 bits each. + * This was empirically more accurate than using 8 bit chroma. + * Black (all zeros) is handled exactly. + */ uint encodeLogLuvHDR(float3 color) { // Convert RGB to XYZ. @@ -125,7 +134,8 @@ uint encodeLogLuvHDR(float3 color) // Early out if zero luminance to avoid NaN in chroma computation. // Note Le==0 if Y < 9.55e-7. We'll decode that as exactly zero. - if (Le == 0) return 0; + if (Le == 0) + return 0; // Compute chroma (u,v) values by: // x = X / (X + Y + Z) @@ -147,14 +157,16 @@ uint encodeLogLuvHDR(float3 color) return (Le << 18) | (uve.x << 9) | uve.y; } -/** Decode an RGB color stored in a 32-bit LogLuv HDR format. - See encodeLogLuvHDR() for details. -*/ +/** + * Decode an RGB color stored in a 32-bit LogLuv HDR format. + * See encodeLogLuvHDR() for details. + */ float3 decodeLogLuvHDR(uint packedColor) { // Decode luminance Y from encoded log-luminance. uint Le = packedColor >> 18; - if (Le == 0) return float3(0.f); + if (Le == 0) + return float3(0.f); float logY = (float(Le) + 0.5f) / 409.6f - 20.f; float Y = pow(2.f, logY); diff --git a/Source/Falcor/Utils/Math/Quaternion.h b/Source/Falcor/Utils/Math/Quaternion.h new file mode 100644 index 000000000..724b54e95 --- /dev/null +++ b/Source/Falcor/Utils/Math/Quaternion.h @@ -0,0 +1,31 @@ +/*************************************************************************** + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. + # + # Redistribution and use in source and binary forms, with or without + # modification, are permitted provided that the following conditions + # are met: + # * Redistributions of source code must retain the above copyright + # notice, this list of conditions and the following disclaimer. + # * Redistributions in binary form must reproduce the above copyright + # notice, this list of conditions and the following disclaimer in the + # documentation and/or other materials provided with the distribution. + # * Neither the name of NVIDIA CORPORATION nor the names of its + # contributors may be used to endorse or promote products derived + # from this software without specific prior written permission. + # + # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY + # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + # (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 "QuaternionTypes.h" +#include "QuaternionMath.h" diff --git a/Source/Falcor/Utils/Math/Quaternion.slang b/Source/Falcor/Utils/Math/Quaternion.slang index 6d4afefe6..12e8c7fb3 100644 --- a/Source/Falcor/Utils/Math/Quaternion.slang +++ b/Source/Falcor/Utils/Math/Quaternion.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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,9 @@ **************************************************************************/ #include "Utils/Math/MathConstants.slangh" - /************************************************************************************************************** - # All the quaternion code was copied from: https://gist.github.com/mattatz/40a91588d5fb38240403f198a938a593 - **************************************************************************************************************/ +/************************************************************************************************************** + # All the quaternion code was copied from: https://gist.github.com/mattatz/40a91588d5fb38240403f198a938a593 + **************************************************************************************************************/ #define QUATERNION_IDENTITY float4(0, 0, 0, 1) @@ -37,10 +37,7 @@ // http://mathworld.wolfram.com/Quaternion.html float4 qmul(float4 q1, float4 q2) { - return float4( - q2.xyz * q1.w + q1.xyz * q2.w + cross(q1.xyz, q2.xyz), - q1.w * q2.w - dot(q1.xyz, q2.xyz) - ); + return float4(q2.xyz * q1.w + q1.xyz * q2.w + cross(q1.xyz, q2.xyz), q1.w * q2.w - dot(q1.xyz, q2.xyz)); } // Vector rotation with a quaternion @@ -76,10 +73,12 @@ float4 from_to_rotation(float3 v1, float3 v2) tmp = normalize(tmp); q = rotate_angle_axis(M_PI, tmp); } - else if (d > 0.999999) { + else if (d > 0.999999) + { q = QUATERNION_IDENTITY; } - else { + else + { q.xyz = cross(v1, v2); q.w = 1 + d; q = normalize(q); diff --git a/Source/Falcor/Utils/Math/QuaternionMath.h b/Source/Falcor/Utils/Math/QuaternionMath.h new file mode 100644 index 000000000..2adb23635 --- /dev/null +++ b/Source/Falcor/Utils/Math/QuaternionMath.h @@ -0,0 +1,533 @@ +/*************************************************************************** + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. + # + # Redistribution and use in source and binary forms, with or without + # modification, are permitted provided that the following conditions + # are met: + # * Redistributions of source code must retain the above copyright + # notice, this list of conditions and the following disclaimer. + # * Redistributions in binary form must reproduce the above copyright + # notice, this list of conditions and the following disclaimer in the + # documentation and/or other materials provided with the distribution. + # * Neither the name of NVIDIA CORPORATION nor the names of its + # contributors may be used to endorse or promote products derived + # from this software without specific prior written permission. + # + # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY + # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + **************************************************************************/ + +/** + * Most of this code is derived from the GLM library at https://github.com/g-truc/glm + * + * License: https://github.com/g-truc/glm/blob/master/copying.txt + */ + +#pragma once + +#include "QuaternionTypes.h" +#include "ScalarMath.h" +#include "VectorMath.h" +#include "MathConstants.slangh" +#include "Core/Assert.h" + +namespace Falcor +{ +namespace math +{ + +// ---------------------------------------------------------------------------- +// Unary operators (component-wise) +// ---------------------------------------------------------------------------- + +/// Unary plus operator +template +[[nodiscard]] constexpr quat operator+(const quat q) noexcept +{ + return q; +} + +/// Unary minus operator +template +[[nodiscard]] constexpr quat operator-(const quat q) noexcept +{ + return quat{-q.x, -q.y, -q.z, -q.w}; +} + +// ---------------------------------------------------------------------------- +// Binary operators (component-wise) +// ---------------------------------------------------------------------------- + +/// Binary + operator +template +[[nodiscard]] constexpr quat operator+(const quat& lhs, const quat& rhs) noexcept +{ + return quat{lhs.x + rhs.x, lhs.y + rhs.y, lhs.z + rhs.z, lhs.w + rhs.w}; +} + +/// Binary + operator +template +[[nodiscard]] constexpr quat operator+(const quat& lhs, T rhs) noexcept +{ + return quat{lhs.x + rhs, lhs.y + rhs, lhs.z + rhs, lhs.w + rhs}; +} + +/// Binary + operator +template +[[nodiscard]] constexpr quat operator+(T lhs, const quat& rhs) noexcept +{ + return quat{lhs + rhs.x, lhs + rhs.y, lhs + rhs.z, lhs + rhs.w}; +} + +/// Binary - operator +template +[[nodiscard]] constexpr quat operator-(const quat& lhs, const quat& rhs) noexcept +{ + return quat{lhs.x - rhs.x, lhs.y - rhs.y, lhs.z - rhs.z, lhs.w - rhs.w}; +} + +/// Binary - operator +template +[[nodiscard]] constexpr quat operator-(const quat& lhs, T rhs) noexcept +{ + return quat{lhs.x - rhs, lhs.y - rhs, lhs.z - rhs, lhs.w - rhs}; +} + +/// Binary - operator +template +[[nodiscard]] constexpr quat operator-(T lhs, const quat& rhs) noexcept +{ + return quat{lhs - rhs.x, lhs - rhs.y, lhs - rhs.z, lhs - rhs.w}; +} + +/// Binary * operator +template +[[nodiscard]] constexpr quat operator*(const quat& lhs, T rhs) noexcept +{ + return quat{lhs.x * rhs, lhs.y * rhs, lhs.z * rhs, lhs.w * rhs}; +} + +/// Binary * operator +template +[[nodiscard]] constexpr quat operator*(T lhs, const quat& rhs) noexcept +{ + return quat{lhs * rhs.x, lhs * rhs.y, lhs * rhs.z, lhs * rhs.w}; +} + +/// Binary / operator +template +[[nodiscard]] constexpr quat operator/(const quat& lhs, T rhs) noexcept +{ + return quat{lhs.x / rhs, lhs.y / rhs, lhs.z / rhs, lhs.w / rhs}; +} + +/// Binary == operator +template +[[nodiscard]] constexpr vector operator==(const quat& lhs, const quat& rhs) +{ + return bool4{lhs.x == rhs.x, lhs.y == rhs.y, lhs.z == rhs.z, lhs.w == rhs.w}; +} + +/// Binary != operator +template +[[nodiscard]] constexpr vector operator!=(const quat& lhs, const quat& rhs) +{ + return bool4{lhs.x != rhs.x, lhs.y != rhs.y, lhs.z != rhs.z, lhs.w != rhs.w}; +} + +// ---------------------------------------------------------------------------- +// Multiplication +// ---------------------------------------------------------------------------- + +/// Multiply quaternion with another quaternion. +template +[[nodiscard]] constexpr quat mul(const quat& lhs, const quat& rhs) noexcept +{ + return quat{ + lhs.w * rhs.x + lhs.x * rhs.w + lhs.y * rhs.z - lhs.z * rhs.y, // x + lhs.w * rhs.y + lhs.y * rhs.w + lhs.z * rhs.x - lhs.x * rhs.z, // y + lhs.w * rhs.z + lhs.z * rhs.w + lhs.x * rhs.y - lhs.y * rhs.x, // z + lhs.w * rhs.w - lhs.x * rhs.x - lhs.y * rhs.y - lhs.z * rhs.z // w + }; +} + +/// Multiply quaternion and 3 component vector. +template +[[nodiscard]] constexpr vector mul(const quat& q, const vector& v) noexcept +{ + vector qv(q.x, q.y, q.z); + vector uv(cross(qv, v)); + vector uuv(cross(qv, uv)); + return v + ((uv * q.w) + uuv) * T(2); +} + +/// Transform a vector by a quaternion. +template +[[nodiscard]] constexpr vector transformVector(const quat& q, const vector& v) noexcept +{ + return mul(q, v); +} + +// ---------------------------------------------------------------------------- +// Floating point checks +// ---------------------------------------------------------------------------- + +/// isfinite +template +[[nodiscard]] constexpr vector isfinite(const quat& q) +{ + return vector{isfinite(q.x), isfinite(q.y), isfinite(q.z), isfinite(q.w)}; +} + +/// isinf +template +[[nodiscard]] constexpr vector isinf(const quat& q) +{ + return vector{isinf(q.x), isinf(q.y), isinf(q.z), isinf(q.w)}; +} + +/// isnan +template +[[nodiscard]] constexpr vector isnan(const quat& q) +{ + return vector{isnan(q.x), isnan(q.y), isnan(q.z), isnan(q.w)}; +} + +// ---------------------------------------------------------------------------- +// Geometryic functions +// ---------------------------------------------------------------------------- + +/// dot +template +[[nodiscard]] constexpr T dot(const quat& lhs, const quat& rhs) +{ + vector tmp{lhs.w * rhs.w, lhs.x * rhs.x, lhs.y * rhs.y, lhs.z * rhs.z}; + return (tmp.x + tmp.y) + (tmp.z + tmp.w); +} + +/// cross +template +[[nodiscard]] constexpr quat cross(const quat& lhs, const quat& rhs) +{ + return quat( + lhs.w * rhs.x + lhs.x * rhs.w + lhs.y * rhs.z - lhs.z * rhs.y, // x + lhs.w * rhs.y + lhs.y * rhs.w + lhs.z * rhs.x - lhs.x * rhs.z, // y + lhs.w * rhs.z + lhs.z * rhs.w + lhs.x * rhs.y - lhs.y * rhs.x, // z + lhs.w * rhs.w - lhs.x * rhs.x - lhs.y * rhs.y - lhs.z * rhs.z // w + ); +} + +/// length +template +[[nodiscard]] T length(const quat& q) +{ + return sqrt(dot(q, q)); +} + +/// normalize +template +[[nodiscard]] constexpr quat normalize(const quat& q) +{ + T len = length(q); + if (len <= T(0)) + return quat(T(0), T(0), T(0), T(1)); + return q * (T(1) / len); +} + +/// conjugate +template +[[nodiscard]] constexpr quat conjugate(const quat& q) +{ + return quat(-q.x, -q.y, -q.z, q.w); +} + +/// inverse +template +quat inverse(const quat& q) +{ + return conjugate(q) / dot(q, q); +} + +/// Linear interpolation. +template +[[nodiscard]] constexpr quat lerp(const quat& q1, const quat& q2, T t) +{ + return q1 * (T(1) - t) + q2 * t; +} + +/// Spherical linear interpolation. +template +[[nodiscard]] constexpr quat slerp(const quat& q1, const quat& q2_, T t) +{ + quat q2 = q2_; + + T cosTheta = dot(q1, q2); + + // If cosTheta < 0, the interpolation will take the long way around the sphere. + // To fix this, one quat must be negated. + if (cosTheta < T(0)) + { + q2 = -q2; + cosTheta = -cosTheta; + } + + // Perform a linear interpolation when cosTheta is close to 1 to avoid side effect of sin(angle) becoming a zero denominator + if (cosTheta > T(1) - std::numeric_limits::epsilon()) + { + // Linear interpolation + return lerp(q1, q2, t); + } + else + { + // Essential Mathematics, page 467 + T angle = acos(cosTheta); + return (sin((T(1) - t) * angle) * q1 + sin(t * angle) * q2) / sin(angle); + } +} + +// ---------------------------------------------------------------------------- +// Misc +// ---------------------------------------------------------------------------- + +/// Returns pitch value of euler angles expressed in radians. +template +[[nodiscard]] constexpr T pitch(const quat& q) +{ + T y = T(2) * (q.y * q.z + q.w * q.x); + T x = q.w * q.w - q.x * q.x - q.y * q.y + q.z * q.z; + + // Handle sigularity, avoid atan2(0,0) + if (abs(x) < std::numeric_limits::epsilon() && abs(y) < std::numeric_limits::epsilon()) + return T(T(2) * atan2(q.x, q.w)); + + return atan2(y, x); +} + +/// Returns yaw value of euler angles expressed in radians. +template +[[nodiscard]] constexpr T yaw(const quat& q) +{ + return asin(clamp(T(-2) * (q.x * q.z - q.w * q.y), T(-1), T(1))); +} + +/// Returns roll value of euler angles expressed in radians. +template +[[nodiscard]] constexpr T roll(const quat& q) +{ + return atan2(T(2) * (q.x * q.y + q.w * q.z), q.w * q.w + q.x * q.x - q.y * q.y - q.z * q.z); +} + +/// Extract the euler angles in radians from a quaternion (pitch as x, yaw as y, roll as z). +template +[[nodiscard]] constexpr vector eulerAngles(const quat& q) +{ + return vector(pitch(q), yaw(q), roll(q)); +} + +// ---------------------------------------------------------------------------- +// Construction +// ---------------------------------------------------------------------------- + +/** + * Build a quaternion from an angle and a normalized axis. + * @param angle Angle expressed in radians. + * @param axis Axis of the quaternion (must be normalized). + */ +template +[[nodiscard]] inline quat quatFromAngleAxis(T angle, const vector& axis) +{ + T s = sin(angle * T(0.5)); + T c = cos(angle * T(0.5)); + return quat(axis * s, c); +} + +/** + * Compute the rotation between two vectors. + * @param orig Origin vector (must to be normalized). + * @param dest Destination vector (must to be normalized). + */ +template +[[nodiscard]] inline quat quatFromRotationBetweenVectors(const vector& orig, const vector& dest) +{ + T cosTheta = dot(orig, dest); + vector axis; + + if (cosTheta >= T(1) - std::numeric_limits::epsilon()) + { + // orig and dest point in the same direction + return quat::identity(); + } + + if (cosTheta < T(-1) + std::numeric_limits::epsilon()) + { + // special case when vectors in opposite directions : + // there is no "ideal" rotation axis + // So guess one; any will do as long as it's perpendicular to start + // This implementation favors a rotation around the Up axis (Y), + // since it's often what you want to do. + axis = cross(vector(0, 0, 1), orig); + if (dot(axis, axis) < std::numeric_limits::epsilon()) // bad luck, they were parallel, try again! + axis = cross(vector(1, 0, 0), orig); + + axis = normalize(axis); + return quatFromAngleAxis(T(M_PI), axis); + } + + // Implementation from Stan Melax's Game Programming Gems 1 article + axis = cross(orig, dest); + + T s = sqrt((T(1) + cosTheta) * T(2)); + T invs = T(1) / s; + + return quat(axis.x * invs, axis.y * invs, axis.z * invs, s * T(0.5f)); +} + +/** + * Build a quaternion from euler angles (pitch, yaw, roll), in radians. + */ +template +[[nodiscard]] inline quat quatFromEulerAngles(const vector& eulerAngles) +{ + vector c = cos(eulerAngles * T(0.5)); + vector s = sin(eulerAngles * T(0.5)); + + return quat( + s.x * c.y * c.z - c.x * s.y * s.z, // x + c.x * s.y * c.z + s.x * c.y * s.z, // y + c.x * c.y * s.z - s.x * s.y * c.z, // z + c.x * c.y * c.z + s.x * s.y * s.z // w + ); +} + +/** + * Construct a quaternion from a 3x3 rotation matrix. + */ +template +[[nodiscard]] inline quat quatFromMatrix(const matrix& m) +{ + T fourXSquaredMinus1 = m[0][0] - m[1][1] - m[2][2]; + T fourYSquaredMinus1 = m[1][1] - m[0][0] - m[2][2]; + T fourZSquaredMinus1 = m[2][2] - m[0][0] - m[1][1]; + T fourWSquaredMinus1 = m[0][0] + m[1][1] + m[2][2]; + + int biggestIndex = 0; + T fourBiggestSquaredMinus1 = fourWSquaredMinus1; + if (fourXSquaredMinus1 > fourBiggestSquaredMinus1) + { + fourBiggestSquaredMinus1 = fourXSquaredMinus1; + biggestIndex = 1; + } + if (fourYSquaredMinus1 > fourBiggestSquaredMinus1) + { + fourBiggestSquaredMinus1 = fourYSquaredMinus1; + biggestIndex = 2; + } + if (fourZSquaredMinus1 > fourBiggestSquaredMinus1) + { + fourBiggestSquaredMinus1 = fourZSquaredMinus1; + biggestIndex = 3; + } + + T biggestVal = sqrt(fourBiggestSquaredMinus1 + T(1)) * T(0.5); + T mult = T(0.25) / biggestVal; + + switch (biggestIndex) + { + case 0: + return quat((m[2][1] - m[1][2]) * mult, (m[0][2] - m[2][0]) * mult, (m[1][0] - m[0][1]) * mult, biggestVal); + case 1: + return quat(biggestVal, (m[1][0] + m[0][1]) * mult, (m[0][2] + m[2][0]) * mult, (m[2][1] - m[1][2]) * mult); + case 2: + return quat((m[1][0] + m[0][1]) * mult, biggestVal, (m[2][1] + m[1][2]) * mult, (m[0][2] - m[2][0]) * mult); + case 3: + return quat((m[0][2] + m[2][0]) * mult, (m[2][1] + m[1][2]) * mult, biggestVal, (m[1][0] - m[0][1]) * mult); + default: + FALCOR_UNREACHABLE(); + return quat::identity(); + } +} + +/** + * Build a look-at quaternion. + * If right handed, forward direction is mapped onto -Z axis. + * If left handed, forward direction is mapped onto +Z axis. + * @param dir Forward direction (must to be normalized). + * @param up Up vector (must be normalized). + * @param handedness Coordinate system handedness. + */ +template +[[nodiscard]] inline quat quatFromLookAt( + const vector& dir, + const vector& up, + Handedness handedness = Handedness::RightHanded +) +{ + matrix m; + m.setCol(2, handedness == Handedness::RightHanded ? -dir : dir); + vector right = normalize(cross(up, m.getCol(2))); + m.setCol(0, right); + m.setCol(1, cross(m.getCol(2), m.getCol(0))); + + return quatFromMatrix(m); +} + +template +[[nodiscard]] std::string to_string(const quat& q) +{ + return ::fmt::format("{}", q); +} + +} // namespace math +} // namespace Falcor + +template +struct std::equal_to<::Falcor::math::quat> +{ + constexpr bool operator()(const ::Falcor::math::quat& lhs, const ::Falcor::math::quat& rhs) const + { + return lhs.x == rhs.x && lhs.y == rhs.y && lhs.z == rhs.z && lhs.w == rhs.w; + } +}; + +template +struct std::not_equal_to<::Falcor::math::quat> +{ + constexpr bool operator()(const ::Falcor::math::quat& lhs, const ::Falcor::math::quat& rhs) const + { + return lhs.x != rhs.x || lhs.y != rhs.y || lhs.z != rhs.z || lhs.w != rhs.w; + } +}; + +template +struct std::hash<::Falcor::math::quat> +{ + constexpr size_t operator()(const ::Falcor::math::quat& v) const + { + size_t result = 0; + for (size_t i = 0; i < 4; ++i) + result ^= std::hash()(v[i]) + 0x9e3779b9 + (result << 6) + (result >> 2); + return result; + } +}; + +/// Quaternion string formatter. +template +struct fmt::formatter<::Falcor::math::quat> : formatter +{ + template + auto format(const ::Falcor::math::quat& v, FormatContext& ctx) const + { + auto out = ctx.out(); + out = ::fmt::format_to(out, "{{{}, {}, {}, {}}}", v.x, v.y, v.z, v.w); + return out; + } +}; diff --git a/Source/Falcor/Utils/Math/QuaternionTypes.h b/Source/Falcor/Utils/Math/QuaternionTypes.h new file mode 100644 index 000000000..62f506afa --- /dev/null +++ b/Source/Falcor/Utils/Math/QuaternionTypes.h @@ -0,0 +1,81 @@ +/*************************************************************************** + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. + # + # Redistribution and use in source and binary forms, with or without + # modification, are permitted provided that the following conditions + # are met: + # * Redistributions of source code must retain the above copyright + # notice, this list of conditions and the following disclaimer. + # * Redistributions in binary form must reproduce the above copyright + # notice, this list of conditions and the following disclaimer in the + # documentation and/or other materials provided with the distribution. + # * Neither the name of NVIDIA CORPORATION nor the names of its + # contributors may be used to endorse or promote products derived + # from this software without specific prior written permission. + # + # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY + # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + # (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 "ScalarTypes.h" +#include "VectorTypes.h" + +#include + +namespace Falcor +{ +namespace math +{ + +/** + * Quaternion type. + * + * A quaternion is an expression of the form: + * + * q = w + xi + yj + zk + * + * where w, x, y, and z are real numbers and i, j, and k are the imaginary units. + * + * The quaternion is normalized if: + * w^2 + x^2 + y^2 + z^2 = 1 + * + * Quaternions are stored as (x, y, z, w) to make them better for interop with the GPU. + */ +template +struct quat +{ + using value_type = T; + static_assert(std::disjunction_v, std::is_same>, "Invalid quaternion type"); + + T x, y, z, w; + + quat() : x{T(0)}, y{T(0)}, z{T(0)}, w{T(1)} {} + + explicit quat(const vector& xyz, const T& w) : x{xyz.x}, y{xyz.y}, z{xyz.z}, w{w} {} + explicit quat(const T& x, const T& y, const T& z, const T& w) : x{x}, y{y}, z{z}, w{w} {} + + /// Identity quaternion. + [[nodiscard]] static quat identity() { return quat(T(0), T(0), T(0), T(1)); } + + // Accesses + value_type& operator[](size_t i) { return (&x)[i]; } + const value_type& operator[](size_t i) const { return (&x)[i]; } +}; + +using quatf = quat; + +} // namespace math + +using quatf = math::quatf; + +} // namespace Falcor diff --git a/Source/Falcor/Utils/Math/Ray.h b/Source/Falcor/Utils/Math/Ray.h index 168253c68..61a052133 100644 --- a/Source/Falcor/Utils/Math/Ray.h +++ b/Source/Falcor/Utils/Math/Ray.h @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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,29 +30,27 @@ namespace Falcor { - /** Ray type. - This should match the layout of DXR RayDesc. - */ - struct Ray - { - float3 origin; - float tMin; - float3 dir; - float tMax; +/** + * Ray type. + * This should match the layout of DXR RayDesc. + */ +struct Ray +{ + float3 origin; + float tMin; + float3 dir; + float tMax; - Ray() = default; - explicit Ray(float3 origin, float3 dir, float tMin = 0.f, float tMax = std::numeric_limits::max()) - : origin(origin) - , tMin(tMin) - , dir(dir) - , tMax(tMax) - {} - }; + Ray() = default; + explicit Ray(float3 origin, float3 dir, float tMin = 0.f, float tMax = std::numeric_limits::max()) + : origin(origin), tMin(tMin), dir(dir), tMax(tMax) + {} +}; - // These are to ensure that the struct Ray match DXR RayDesc. - static_assert(offsetof(Ray, origin) == 0); - static_assert(offsetof(Ray, tMin) == sizeof(float3)); - static_assert(offsetof(Ray, dir) == offsetof(Ray, tMin) + sizeof(float)); - static_assert(offsetof(Ray, tMax) == offsetof(Ray, dir) + sizeof(float3)); - static_assert(sizeof(Ray) == 32); -} +// These are to ensure that the struct Ray match DXR RayDesc. +static_assert(offsetof(Ray, origin) == 0); +static_assert(offsetof(Ray, tMin) == sizeof(float3)); +static_assert(offsetof(Ray, dir) == offsetof(Ray, tMin) + sizeof(float)); +static_assert(offsetof(Ray, tMax) == offsetof(Ray, dir) + sizeof(float3)); +static_assert(sizeof(Ray) == 32); +} // namespace Falcor diff --git a/Source/Falcor/Utils/Math/Ray.slang b/Source/Falcor/Utils/Math/Ray.slang index 7f83367a6..77dc6c564 100644 --- a/Source/Falcor/Utils/Math/Ray.slang +++ b/Source/Falcor/Utils/Math/Ray.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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,10 @@ **************************************************************************/ #include "Utils/Math/MathConstants.slangh" -/** Ray type. - This has equal layout to DXR RayDesc but adds additional functionality. -*/ +/** + * Ray type. + * This has equal layout to DXR RayDesc but adds additional functionality. + */ struct Ray { float3 origin; @@ -37,8 +38,9 @@ struct Ray float3 dir; float tMax; - /** Initializes a ray. - */ + /** + * Initializes a ray. + */ __init(float3 origin, float3 dir, float tMin = 0.f, float tMax = FLT_MAX) { this.origin = origin; @@ -47,19 +49,15 @@ struct Ray this.tMax = tMax; } - /** Convert to DXR RayDesc. - */ - RayDesc toRayDesc() - { - return { origin, tMin, dir, tMax }; - } + /** + * Convert to DXR RayDesc. + */ + RayDesc toRayDesc() { return { origin, tMin, dir, tMax }; } - /** Evaluate position on the ray. - \param[in] t Ray parameter. - \return Returns evaluated position. - */ - float3 eval(float t) - { - return origin + t * dir; - } + /** + * Evaluate position on the ray. + * @param[in] t Ray parameter. + * @return Returns evaluated position. + */ + float3 eval(float t) { return origin + t * dir; } }; diff --git a/Source/Falcor/Utils/Math/Rectangle.cpp b/Source/Falcor/Utils/Math/Rectangle.cpp index e21d5c139..3cccd5449 100644 --- a/Source/Falcor/Utils/Math/Rectangle.cpp +++ b/Source/Falcor/Utils/Math/Rectangle.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -35,48 +35,53 @@ FALCOR_SCRIPT_BINDING(Rectangle) { using namespace pybind11::literals; - pybind11::class_ uvTile(m, "Rectangle"); + pybind11::class_ rectangle(m, "Rectangle"); - uvTile.def(pybind11::init<>()); - uvTile.def(pybind11::init(), "p"_a); - uvTile.def(pybind11::init(), "pmin"_a, "pmax"_a); + rectangle.def(pybind11::init<>()); + rectangle.def(pybind11::init(), "p"_a); + rectangle.def(pybind11::init(), "min_point"_a, "max_point"_a); - uvTile.def( + rectangle.def( "__repr__", - [](const Rectangle& uvTile) + [](const Rectangle& rectangle) { - return "uvTile(minPoint=" + std::string(pybind11::repr(pybind11::cast(uvTile.minPoint))) + - ", maxPoint=" + std::string(pybind11::repr(pybind11::cast(uvTile.maxPoint))) + ")"; + return "Rectangle(min_point=" + std::string(pybind11::repr(pybind11::cast(rectangle.minPoint))) + + ", max_point=" + std::string(pybind11::repr(pybind11::cast(rectangle.maxPoint))) + ")"; } ); - uvTile.def( + rectangle.def( "__str__", - [](const Rectangle& uvTile) + [](const Rectangle& rectangle) { - return "[" + std::string(pybind11::str(pybind11::cast(uvTile.minPoint))) + ", " + - std::string(pybind11::str(pybind11::cast(uvTile.maxPoint))) + "]"; + return "[" + std::string(pybind11::str(pybind11::cast(rectangle.minPoint))) + ", " + + std::string(pybind11::str(pybind11::cast(rectangle.maxPoint))) + "]"; } ); - uvTile.def_readwrite("minPoint", &Rectangle::minPoint); - uvTile.def_readwrite("maxPoint", &Rectangle::maxPoint); + rectangle.def_readwrite("min_point", &Rectangle::minPoint); + rectangle.def_readwrite("max_point", &Rectangle::maxPoint); - uvTile.def_property_readonly("valid", &Rectangle::valid); - uvTile.def_property_readonly("center", &Rectangle::center); - uvTile.def_property_readonly("extent", &Rectangle::extent); - uvTile.def_property_readonly("area", &Rectangle::area); - uvTile.def_property_readonly("radius", &Rectangle::radius); + rectangle.def_property_readonly("valid", &Rectangle::valid); + rectangle.def_property_readonly("center", &Rectangle::center); + rectangle.def_property_readonly("extent", &Rectangle::extent); + rectangle.def_property_readonly("area", &Rectangle::area); + rectangle.def_property_readonly("radius", &Rectangle::radius); - uvTile.def("invalidate", &Rectangle::invalidate); - uvTile.def("include", pybind11::overload_cast(&Rectangle::include), "p"_a); - uvTile.def("include", pybind11::overload_cast(&Rectangle::include), "b"_a); - uvTile.def("intersection", &Rectangle::intersection); + rectangle.def("invalidate", &Rectangle::invalidate); + rectangle.def("include", pybind11::overload_cast(&Rectangle::include), "p"_a); + rectangle.def("include", pybind11::overload_cast(&Rectangle::include), "b"_a); + rectangle.def("intersection", &Rectangle::intersection); - uvTile.def(pybind11::self == pybind11::self); - uvTile.def(pybind11::self != pybind11::self); - uvTile.def(pybind11::self | pybind11::self); - uvTile.def(pybind11::self |= pybind11::self); - uvTile.def(pybind11::self & pybind11::self); - uvTile.def(pybind11::self &= pybind11::self); + rectangle.def(pybind11::self == pybind11::self); + rectangle.def(pybind11::self != pybind11::self); + rectangle.def(pybind11::self | pybind11::self); + rectangle.def(pybind11::self |= pybind11::self); + rectangle.def(pybind11::self & pybind11::self); + rectangle.def(pybind11::self &= pybind11::self); + + // PYTHONDEPRECATED BEGIN + rectangle.def_readwrite("minPoint", &Rectangle::minPoint); + rectangle.def_readwrite("maxPoint", &Rectangle::maxPoint); + // PYTHONDEPRECATED END } } // namespace Falcor diff --git a/Source/Falcor/Utils/Math/Rectangle.h b/Source/Falcor/Utils/Math/Rectangle.h index a5783bffd..9de73400c 100644 --- a/Source/Falcor/Utils/Math/Rectangle.h +++ b/Source/Falcor/Utils/Math/Rectangle.h @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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,44 +40,36 @@ struct Rectangle float2 minPoint = float2(std::numeric_limits::infinity()); ///< Minimum point. float2 maxPoint = float2(-std::numeric_limits::infinity()); ///< Maximum point. If any minPoint > maxPoint the tile is invalid. - /** Construct bounding tile initialized to +/-inf. - */ + /// Construct bounding tile initialized to +/-inf. Rectangle() = default; - /** Construct bounding tile initialized to single point. - */ + /// Construct bounding tile initialized to single point. Rectangle(const float2& p) : minPoint(p), maxPoint(p) {} - /** Construct bounding tile initialized to min/max point. - */ + /// Construct bounding tile initialized to min/max point. Rectangle(const float2& pmin, const float2& pmax) : minPoint(pmin), maxPoint(pmax) {} - /** Set tile to single point. - */ + /// Set tile to single point. void set(const float2& p) { minPoint = maxPoint = p; } - /** Set the tile corners explicitly. - */ + /// Set the tile corners explicitly. void set(const float2& pmin, const float2& pmax) { minPoint = pmin; maxPoint = pmax; } - /** Invalidates the tile. - */ + /// Invalidates the tile. void invalidate() { minPoint = float2(std::numeric_limits::infinity()); maxPoint = float2(-std::numeric_limits::infinity()); } - /** Returns true if bounding tile is valid (all dimensions zero or larger). - */ + /// Returns true if bounding tile is valid (all dimensions zero or larger). bool valid() const { return maxPoint.x >= minPoint.x && maxPoint.y >= minPoint.y; } - /** Grows the tile to include the point p. - */ + /// Grows the tile to include the point p. Rectangle& include(const float2& p) { minPoint = min(minPoint, p); @@ -85,8 +77,7 @@ struct Rectangle return *this; } - /** Grows the tile to include another tile. - */ + /// Grows the tile to include another tile. Rectangle& include(const Rectangle& b) { minPoint = min(minPoint, b.minPoint); @@ -94,81 +85,76 @@ struct Rectangle return *this; } - /** Make the tile be the intersection between this and another tile. - */ + /// Make the tile be the intersection between this and another tile. Rectangle& intersection(const Rectangle& b) { - minPoint = glm::max(minPoint, b.minPoint); - maxPoint = glm::min(maxPoint, b.maxPoint); + minPoint = max(minPoint, b.minPoint); + maxPoint = min(maxPoint, b.maxPoint); return *this; } - /** Returns true if the two rectangles have any overlap. - */ + /// Returns true if the two rectangles have any overlap. bool overlaps(Rectangle b) { b.intersection(*this); return b.valid() && b.area() > 0.f; } - /** Returns true if the Rectangle `b` is fully contained within this Rectangle. - */ + /// Returns true if the Rectangle `b` is fully contained within this Rectangle. bool contains(const Rectangle& b) { Rectangle temp = *this; return temp.include(b) == *this; } - /** Returns the tile center. - \return Center of the tile if valid, undefined otherwise. - */ + /** + * Returns the tile center. + * @return Center of the tile if valid, undefined otherwise. + */ float2 center() const { return (minPoint + maxPoint) * 0.5f; } - /** Returns the tile extent. - \return Size of the tile if valid, undefined otherwise. - */ + /** + * Returns the tile extent. + * @return Size of the tile if valid, undefined otherwise. + */ float2 extent() const { return maxPoint - minPoint; } - /** Returns the surface area of the tile. - \return Surface area if tile is valid, undefined otherwise. - */ + /** + * Returns the surface area of the tile. + * @return Surface area if tile is valid, undefined otherwise. + */ float area() const { float2 e = extent(); return e.x * e.y; } - /** Returns the radius of the minimal sphere that encloses the tile. - \return Radius of minimal bounding sphere, or undefined if tile is invalid. - */ - float radius() const { return 0.5f * glm::length(extent()); } - - /** Checks whether two bounding tilees are equal. + /** + * Returns the radius of the minimal sphere that encloses the tile. + * @return Radius of minimal bounding sphere, or undefined if tile is invalid. */ - bool operator==(const Rectangle& rhs) const { return minPoint == rhs.minPoint && maxPoint == rhs.maxPoint; } + float radius() const { return 0.5f * length(extent()); } - /** Checks whether two bounding tilees are not equal. - */ - bool operator!=(const Rectangle& rhs) const { return minPoint != rhs.minPoint || maxPoint != rhs.maxPoint; } + /// Checks whether two bounding tilees are equal. + bool operator==(const Rectangle& rhs) const { return all(minPoint == rhs.minPoint) && all(maxPoint == rhs.maxPoint); } - /** Union of two tilees. - */ + /// Checks whether two bounding tilees are not equal. + bool operator!=(const Rectangle& rhs) const { return any(minPoint != rhs.minPoint) || any(maxPoint != rhs.maxPoint); } + + /// Union of two tilees. Rectangle& operator|=(const Rectangle& rhs) { return include(rhs); } - /** Union of two tilees. - */ + /// Union of two tilees. Rectangle operator|(const Rectangle& rhs) const { Rectangle bb = *this; return bb |= rhs; } - /** Intersection of two tilees. - */ + /// Intersection of two tilees. Rectangle& operator&=(const Rectangle& rhs) { return intersection(rhs); } - /** Intersection of two tilees. - */ + /// Intersection of two tilees. Rectangle operator&(const Rectangle& rhs) const { Rectangle bb = *this; diff --git a/Source/Falcor/Utils/Math/ScalarMath.h b/Source/Falcor/Utils/Math/ScalarMath.h new file mode 100644 index 000000000..edcf5b9c7 --- /dev/null +++ b/Source/Falcor/Utils/Math/ScalarMath.h @@ -0,0 +1,276 @@ +/*************************************************************************** + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. + # + # Redistribution and use in source and binary forms, with or without + # modification, are permitted provided that the following conditions + # are met: + # * Redistributions of source code must retain the above copyright + # notice, this list of conditions and the following disclaimer. + # * Redistributions in binary form must reproduce the above copyright + # notice, this list of conditions and the following disclaimer in the + # documentation and/or other materials provided with the distribution. + # * Neither the name of NVIDIA CORPORATION nor the names of its + # contributors may be used to endorse or promote products derived + # from this software without specific prior written permission. + # + # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY + # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + # (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 "ScalarTypes.h" +#include // TODO C++20: Replace with +#include + +namespace Falcor +{ +namespace math +{ + +// ---------------------------------------------------------------------------- +// Boolean reductions +// ---------------------------------------------------------------------------- + +template +[[nodiscard]] constexpr bool any(T x) +{ + return x != T(0); +} + +template +[[nodiscard]] constexpr bool all(T x) +{ + return x != T(0); +} + +// ---------------------------------------------------------------------------- +// Basic functions +// ---------------------------------------------------------------------------- + +template, bool> = false> +[[nodiscard]] constexpr T min(T x, T y) noexcept +{ + return x < y ? x : y; +} + +template, bool> = false> +[[nodiscard]] constexpr T max(T x, T y) noexcept +{ + return x > y ? x : y; +} + +template, bool> = false> +[[nodiscard]] constexpr T clamp(T x, T min_, T max_) noexcept +{ + return max(min_, min(max_, x)); +} + +template, bool> = false> +[[nodiscard]] constexpr T abs(T x) noexcept +{ + return std::abs(x); +} + +template, bool> = false> +[[nodiscard]] constexpr T sign(T x) noexcept +{ + return x < T(0) ? T(-1) : (x > T(0) ? T(1) : T(0)); +} + +// clang-format off + +// ---------------------------------------------------------------------------- +// Floating point checks +// ---------------------------------------------------------------------------- + +template [[nodiscard]] bool isfinite(T x) noexcept; +template<> [[nodiscard]] inline bool isfinite(float x) noexcept { return std::isfinite(x); } +template<> [[nodiscard]] inline bool isfinite(double x) noexcept { return std::isfinite(x); } +template<> [[nodiscard]] inline bool isfinite(float16_t x) noexcept { return x.isFinite(); } + +template [[nodiscard]] bool isinf(T x) noexcept; +template<> [[nodiscard]] inline bool isinf(float x) noexcept { return std::isinf(x); } +template<> [[nodiscard]] inline bool isinf(double x) noexcept { return std::isinf(x); } +template<> [[nodiscard]] inline bool isinf(float16_t x) noexcept { return x.isInf(); } + +template [[nodiscard]] bool isnan(T x) noexcept; +template<> [[nodiscard]] inline bool isnan(float x) noexcept { return std::isnan(x); } +template<> [[nodiscard]] inline bool isnan(double x) noexcept { return std::isnan(x); } +template<> [[nodiscard]] inline bool isnan(float16_t x) noexcept { return x.isNan(); } + +// ---------------------------------------------------------------------------- +// Rounding +// ---------------------------------------------------------------------------- + +template [[nodiscard]] T floor(T x) noexcept; +template<> [[nodiscard]] inline float floor(float x) noexcept { return std::floor(x); } +template<> [[nodiscard]] inline double floor(double x) noexcept { return std::floor(x); } + +template [[nodiscard]] T ceil(T x) noexcept; +template<> [[nodiscard]] inline float ceil(float x) noexcept { return std::ceil(x); } +template<> [[nodiscard]] inline double ceil(double x) noexcept { return std::ceil(x); } + +template [[nodiscard]] T trunc(T x) noexcept; +template<> [[nodiscard]] inline float trunc(float x) noexcept { return std::trunc(x); } +template<> [[nodiscard]] inline double trunc(double x) noexcept { return std::trunc(x); } + +template [[nodiscard]] T round(T x) noexcept; +template<> [[nodiscard]] inline float round(float x) noexcept { return std::round(x); } +template<> [[nodiscard]] inline double round(double x) noexcept { return std::round(x); } + +// ---------------------------------------------------------------------------- +// Exponential +// ---------------------------------------------------------------------------- + +template [[nodiscard]] T pow(T x, T y) noexcept; +template<> [[nodiscard]] inline float pow(float x, float y) noexcept { return std::pow(x, y); } +template<> [[nodiscard]] inline double pow(double x, double y) noexcept { return std::pow(x, y); } + +template [[nodiscard]] T sqrt(T x) noexcept; +template<> [[nodiscard]] inline float sqrt(float x) noexcept { return std::sqrt(x); } +template<> [[nodiscard]] inline double sqrt(double x) noexcept { return std::sqrt(x); } + +template [[nodiscard]] T rsqrt(T x) noexcept; +template<> [[nodiscard]] inline float rsqrt(float x) noexcept { return 1.f / std::sqrt(x); } +template<> [[nodiscard]] inline double rsqrt(double x) noexcept { return 1.0 / std::sqrt(x); } + +template [[nodiscard]] T exp(T x) noexcept; +template<> [[nodiscard]] inline float exp(float x) noexcept { return std::exp(x); } +template<> [[nodiscard]] inline double exp(double x) noexcept { return std::exp(x); } +template<> [[nodiscard]] inline float16_t exp(float16_t x) noexcept { return float16_t(std::exp(float(x))); } + +template [[nodiscard]] T exp2(T x) noexcept; +template<> [[nodiscard]] inline float exp2(float x) noexcept { return std::exp2(x); } +template<> [[nodiscard]] inline double exp2(double x) noexcept { return std::exp2(x); } +template<> [[nodiscard]] inline float16_t exp2(float16_t x) noexcept { return float16_t(std::exp2(float(x))); } + +template [[nodiscard]] T log(T x) noexcept; +template<> [[nodiscard]] inline float log(float x) noexcept { return std::log(x); } +template<> [[nodiscard]] inline double log(double x) noexcept { return std::log(x); } +template<> [[nodiscard]] inline float16_t log(float16_t x) noexcept { return float16_t(std::log(float(x))); } + +template [[nodiscard]] T log2(T x) noexcept; +template<> [[nodiscard]] inline float log2(float x) noexcept { return std::log2(x); } +template<> [[nodiscard]] inline double log2(double x) noexcept { return std::log2(x); } + +template [[nodiscard]] T log10(T x) noexcept; +template<> [[nodiscard]] inline float log10(float x) noexcept { return std::log10(x); } +template<> [[nodiscard]] inline double log10(double x) noexcept { return std::log10(x); } + +// ---------------------------------------------------------------------------- +// Trigonometry +// ---------------------------------------------------------------------------- + +template [[nodiscard]] T radians(T x) noexcept; +template<> [[nodiscard]] inline float radians(float x) noexcept { return x * 0.01745329251994329576923690768489f; } +template<> [[nodiscard]] inline double radians(double x) noexcept { return x * 0.01745329251994329576923690768489; } + +template [[nodiscard]] T degrees(T x) noexcept; +template<> [[nodiscard]] inline float degrees(float x) noexcept { return x * 57.295779513082320876798154814105f; } +template<> [[nodiscard]] inline double degrees(double x) noexcept { return x * 57.295779513082320876798154814105; } + +template [[nodiscard]] T sin(T x) noexcept; +template<> [[nodiscard]] inline float sin(float x) noexcept { return std::sin(x); } +template<> [[nodiscard]] inline double sin(double x) noexcept { return std::sin(x); } + +template [[nodiscard]] T cos(T x) noexcept; +template<> [[nodiscard]] inline float cos(float x) noexcept { return std::cos(x); } +template<> [[nodiscard]] inline double cos(double x) noexcept { return std::cos(x); } + +template [[nodiscard]] T tan(T x) noexcept; +template<> [[nodiscard]] inline float tan(float x) noexcept { return std::tan(x); } +template<> [[nodiscard]] inline double tan(double x) noexcept { return std::tan(x); } + +template [[nodiscard]] T asin(T x) noexcept; +template<> [[nodiscard]] inline float asin(float x) noexcept { return std::asin(x); } +template<> [[nodiscard]] inline double asin(double x) noexcept { return std::asin(x); } + +template [[nodiscard]] T acos(T x) noexcept; +template<> [[nodiscard]] inline float acos(float x) noexcept { return std::acos(x); } +template<> [[nodiscard]] inline double acos(double x) noexcept { return std::acos(x); } + +template [[nodiscard]] T atan(T x) noexcept; +template<> [[nodiscard]] inline float atan(float x) noexcept { return std::atan(x); } +template<> [[nodiscard]] inline double atan(double x) noexcept { return std::atan(x); } + +template [[nodiscard]] T atan2(T y, T x) noexcept; +template<> [[nodiscard]] inline float atan2(float y, float x) noexcept { return std::atan2(y, x); } +template<> [[nodiscard]] inline double atan2(double y, double x) noexcept { return std::atan2(y, x); } + +template [[nodiscard]] T sinh(T x) noexcept; +template<> [[nodiscard]] inline float sinh(float x) noexcept { return std::sinh(x); } +template<> [[nodiscard]] inline double sinh(double x) noexcept { return std::sinh(x); } + +template [[nodiscard]] T cosh(T x) noexcept; +template<> [[nodiscard]] inline float cosh(float x) noexcept { return std::cosh(x); } +template<> [[nodiscard]] inline double cosh(double x) noexcept { return std::cosh(x); } + +template [[nodiscard]] T tanh(T x) noexcept; +template<> [[nodiscard]] inline float tanh(float x) noexcept { return std::tanh(x); } +template<> [[nodiscard]] inline double tanh(double x) noexcept { return std::tanh(x); } + +// ---------------------------------------------------------------------------- +// Misc +// ---------------------------------------------------------------------------- + +template [[nodiscard]] T fmod(T x, T y) noexcept; +template<> [[nodiscard]] inline float fmod(float x, float y) noexcept { return std::fmod(x, y); } +template<> [[nodiscard]] inline double fmod(double x, double y) noexcept { return std::fmod(x, y); } + +template [[nodiscard]] T frac(T x) noexcept; +template<> [[nodiscard]] inline float frac(float x) noexcept { return x - floor(x); } +template<> [[nodiscard]] inline double frac(double x) noexcept { return x - floor(x); } + +template [[nodiscard]] T lerp(T x, T y, T s) noexcept; +template<> [[nodiscard]] inline float lerp(float x, float y, float s) noexcept { return (1.f - s) * x + s * y; } +template<> [[nodiscard]] inline double lerp(double x, double y, double s) noexcept { return (1.0 - s) * x + s * y; } + +template [[nodiscard]] T rcp(T x) noexcept; +template<> [[nodiscard]] inline float rcp(float x) noexcept { return 1.f / x; } +template<> [[nodiscard]] inline double rcp(double x) noexcept { return 1.0 / x; } + +template [[nodiscard]] T saturate(T x) noexcept; +template<> [[nodiscard]] inline float saturate(float x) noexcept { return max(0.f, min(1.f, x)); } +template<> [[nodiscard]] inline double saturate(double x) noexcept { return max(0.0, min(1.0, x)); } + +template [[nodiscard]] T step(T x, T y) noexcept; +template<> [[nodiscard]] inline float step(float x, float y) noexcept { return x >= y ? 1.f : 0.f; } +template<> [[nodiscard]] inline double step(double x, double y) noexcept { return x >= y ? 1.0 : 0.0; } + +// clang-format on + +template, bool> = false> +[[nodiscard]] T smoothstep(T min_, T max_, T x) noexcept +{ + x = saturate((x - min_) / (max_ - min_)); + return x * x * (T(3) - T(2) * x); +} + +// ---------------------------------------------------------------------------- +// Conversion +// ---------------------------------------------------------------------------- + +// clang-format off +[[nodiscard]] inline float f16tof32(uint v) noexcept { return float16ToFloat32(v & 0xffff); } +[[nodiscard]] inline uint f32tof16(float v) noexcept { return float32ToFloat16(v); } + +[[nodiscard]] inline float asfloat(uint32_t i) noexcept { return fstd::bit_cast(i); } +[[nodiscard]] inline float asfloat(int32_t i) noexcept { return fstd::bit_cast(i); } +[[nodiscard]] inline float16_t asfloat16(uint16_t i) noexcept { return fstd::bit_cast(i); } + +[[nodiscard]] inline uint32_t asuint(float f) noexcept { return fstd::bit_cast(f); } +[[nodiscard]] inline int32_t asint(float f) noexcept { return fstd::bit_cast(f); } +[[nodiscard]] inline uint16_t asuint16(float16_t f) noexcept { return fstd::bit_cast(f); } +// clang-format on + +} // namespace math +} // namespace Falcor diff --git a/Source/Falcor/Utils/Math/ScalarTypes.h b/Source/Falcor/Utils/Math/ScalarTypes.h new file mode 100644 index 000000000..f7488368a --- /dev/null +++ b/Source/Falcor/Utils/Math/ScalarTypes.h @@ -0,0 +1,149 @@ +/*************************************************************************** + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. + # + # Redistribution and use in source and binary forms, with or without + # modification, are permitted provided that the following conditions + # are met: + # * Redistributions of source code must retain the above copyright + # notice, this list of conditions and the following disclaimer. + # * Redistributions in binary form must reproduce the above copyright + # notice, this list of conditions and the following disclaimer in the + # documentation and/or other materials provided with the distribution. + # * Neither the name of NVIDIA CORPORATION nor the names of its + # contributors may be used to endorse or promote products derived + # from this software without specific prior written permission. + # + # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY + # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + # (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 "Float16.h" + +#include + +#include +#include + +namespace Falcor +{ +namespace math +{ + +enum class Handedness +{ + RightHanded, + LeftHanded, +}; + +using uint = uint32_t; + +// clang-format off +template [[nodiscard]] std::string to_string(T v); +template<> [[nodiscard]] inline std::string to_string(bool v) { return v ? "1" : "0"; } +template<> [[nodiscard]] inline std::string to_string(int v) { return std::to_string(v); } +template<> [[nodiscard]] inline std::string to_string(uint v) { return std::to_string(v); } +template<> [[nodiscard]] inline std::string to_string(float v) { return std::to_string(v); } +template<> [[nodiscard]] inline std::string to_string(double v) { return std::to_string(v); } +template<> [[nodiscard]] inline std::string to_string(float16_t v) { return std::to_string(float(v)); } +// clang-format on + +// clang-format off +template struct is_bool : ::std::is_same {}; +template struct is_int : ::std::is_same {}; +template struct is_uint : ::std::is_same {}; +template struct is_float : ::std::is_same {}; +template struct is_double : ::std::is_same {}; +template struct is_float16_t : ::std::is_same {}; + +template constexpr bool is_bool_v = is_bool::value; +template constexpr bool is_int_v = is_int::value; +template constexpr bool is_uint_v = is_uint::value; +template constexpr bool is_float_v = is_float::value; +template constexpr bool is_double_v = is_double::value; +template constexpr bool is_float16_t_v = is_float16_t::value; + +template constexpr bool is_arithmetic_v = std::is_arithmetic_v || is_float16_t_v; +template constexpr bool is_floating_point_v = is_float_v || is_double_v || is_float16_t_v; +using std::is_integral_v; +using std::is_signed_v; +using std::is_unsigned_v; +// clang-format on + +template +struct ScalarTraits +{}; + +template<> +struct ScalarTraits +{ + static constexpr const char* name{"bool"}; +}; + +template<> +struct ScalarTraits +{ + static constexpr const char* name{"int"}; +}; + +template<> +struct ScalarTraits +{ + static constexpr const char* name{"uint"}; +}; + +template<> +struct ScalarTraits +{ + static constexpr const char* name{"float"}; +}; + +template<> +struct ScalarTraits +{ + static constexpr const char* name{"double"}; +}; + +template<> +struct ScalarTraits +{ + static constexpr const char* name{"float16_t"}; +}; + +} // namespace math + +using uint = math::uint; +using float16_t = math::float16_t; + +#if FALCOR_MSVC +#pragma warning(push) +#pragma warning(disable : 4455) // disable warning about literal suffixes not starting with an underscore +#endif + +using math::operator""h; + +#if FALCOR_MSVC +#pragma warning(pop) +#endif + +} // namespace Falcor + +// Formatter for the float16_t. +template<> +struct fmt::formatter<::Falcor::math::float16_t> : formatter +{ + template + auto format(::Falcor::math::float16_t value, FormatContext& ctx) const + { + return formatter::format(float(value), ctx); + } +}; diff --git a/Source/Falcor/Utils/Math/ShadingFrame.slang b/Source/Falcor/Utils/Math/ShadingFrame.slang new file mode 100644 index 000000000..d025a6522 --- /dev/null +++ b/Source/Falcor/Utils/Math/ShadingFrame.slang @@ -0,0 +1,150 @@ +/*************************************************************************** + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. + # + # Redistribution and use in source and binary forms, with or without + # modification, are permitted provided that the following conditions + # are met: + # * Redistributions of source code must retain the above copyright + # notice, this list of conditions and the following disclaimer. + # * Redistributions in binary form must reproduce the above copyright + # notice, this list of conditions and the following disclaimer in the + # documentation and/or other materials provided with the distribution. + # * Neither the name of NVIDIA CORPORATION nor the names of its + # contributors may be used to endorse or promote products derived + # from this software without specific prior written permission. + # + # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY + # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + # (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; + +/** + * Shading frame in world space. + * The vectors TBN form an orthonormal basis. + */ +struct ShadingFrame +{ + float3 T; ///< Shading tangent. Normalized. + float3 B; ///< Shading bitangent. Normalized. + float3 N; ///< Shading normal. Normalized. + + /** + * Initialize shading frame from normal and tangent. + * It is assumed the shading frame can be safely orthonormalized. If in doubt, use `createSafe` instead. + * @param[in] Normal in world space. Not normalized. + * @param[in] Target tangent in world space (xyz) and handedness sign (w). Not normalized. + */ + __init(const float3 normalW, const float4 tangentW) + { + this.N = normalize(normalW); + orthonormalize(tangentW); + } + + /** + * Create identity shading frame. + * @return New shading frame. + */ + static ShadingFrame createIdentity() + { + ShadingFrame sf; + sf.T = float3(1.0f, 0.0f, 0.0f); + sf.B = float3(0.0f, 1.0f, 0.0f); + sf.N = float3(0.0f, 0.0f, 1.0f); + return sf; + } + + /** + * Create a shading frame from normal and tangent. + * If the shading frame cannot be safely orthonormalized, a new tangent is invented based on the normal. + * @param[in] normalW Normal in world space. Not normalized. + * @param[in] tangentW Target tangent in world space (xyz) and handedness sign (w). Not normalized. + * @param[out] valid True if a valid tangent space was computed based on the supplied tangent, or false if a tangent space was invented. + * @return New shading frame. + */ + static ShadingFrame createSafe(const float3 normalW, const float4 tangentW, out bool valid) + { + ShadingFrame sf; + sf.N = normalize(normalW); + valid = sf.orthonormalizeSafe(tangentW); + return sf; + } + + /** + * Transform vector from the local shading frame to world space. + * @param[in] v Vector in local space. + * @return Vector in world space. + */ + float3 fromLocal(const float3 v) { return T * v.x + B * v.y + N * v.z; } + + /** + * Transform vector from world space to the local shading frame. + * @param[in] v Vector in world space. + * @return Vector in local space. + */ + float3 toLocal(const float3 v) { return float3(dot(v, T), dot(v, B), dot(v, N)); } + + /** + * Returns sign that gets applied to such that B = cross(N, T) * getHandednessSign(); + * @return +1 for right handed, and -1 for left handed frames. + */ + float getHandednessSign() { return dot(cross(N, T), B) >= 0.f ? 1.f : -1.f; } + + /** + * Orthonormalize the shading frame. + * Vectors T and B are adjusted to make an orthonormal frame where T lies in the same plane as N and the supplied target tangent. + * It is assumed that the tangent is nonzero and not parallel to the normal, and that the sign (w) is +-1.0. + * @param[in] tangentW Target tangent in world space (xyz) and handedness sign (w). Not normalized. + */ + [mutating] + void orthonormalize(const float4 tangentW) + { + T = normalize(tangentW.xyz - N * dot(tangentW.xyz, N)); + B = cross(N, T) * tangentW.w; + } + + /** + * Orthonormalize the shading frame. + * Vectors T and B are adjusted to make an orthonormal frame where T lies in the same plane as N and the supplied target tangent. + * If the shading frame cannot be safely orthonormalized, a new tangent is invented based on the normal. + * @param[in] tangentW Target tangent in world space (xyz) and handedness sign (w). Not normalized. + * @return True if a valid tangent space was computed based on the supplied tangent, or false if a tangent space was invented. + */ + [mutating] + bool orthonormalizeSafe(const float4 tangentW) + { + // Check that tangent space exists and can be safely orthonormalized. + // Otherwise invent a tanget frame based on the normal. + // We check that: + // - Tangent exists, this is indicated by a sign (w) that is +-1.0. + // - It has nonzero length. Zeros can occur due to interpolation or bad assets. + // - It is not parallel to the normal. This can occur due to normal mapping or bad assets. + // - It does not have NaNs. These will propagate and trigger the fallback. + + float NdotT = dot(tangentW.xyz, N); + bool nonParallel = abs(NdotT) < 0.9999f; + bool nonZero = dot(tangentW.xyz, tangentW.xyz) > 0.f; + bool valid = abs(tangentW.w) == 1.0f && nonZero && nonParallel; + + if (valid) + { + T = normalize(tangentW.xyz - N * NdotT); + B = cross(N, T) * tangentW.w; + } + else + { + T = perp_stark(N); + B = cross(N, T); + } + + return valid; + } +}; diff --git a/Source/Falcor/Utils/Math/SphericalHarmonics.slang b/Source/Falcor/Utils/Math/SphericalHarmonics.slang index 82b77478f..bf9fc55be 100644 --- a/Source/Falcor/Utils/Math/SphericalHarmonics.slang +++ b/Source/Falcor/Utils/Math/SphericalHarmonics.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-21, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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,28 @@ **************************************************************************/ import Utils.Math.MathHelpers; - -/** Get sequential index of SH basis function of degree l>=0 and order m in [-l,l]. -*/ +/** + * 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. -*/ +/** + * 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. + // clang-format off switch (idx) { // l = 0 @@ -69,16 +71,18 @@ float eval_SH(uint idx, float3 p) 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 - } + } + // clang-format on 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. -*/ +/** + * 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 index a271fe388..4d91bb286 100644 --- a/Source/Falcor/Utils/Math/Vector.h +++ b/Source/Falcor/Utils/Math/Vector.h @@ -27,83 +27,5 @@ **************************************************************************/ #pragma once -#define GLM_FORCE_CTOR_INIT -#define GLM_ENABLE_EXPERIMENTAL -#define GLM_FORCE_SWIZZLE -#ifdef __clang__ -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wdeprecated-volatile" -#endif -#include -#include -#ifdef __clang__ -#pragma GCC diagnostic pop -#endif -#include -#include - -namespace Falcor -{ - using float2 = glm::vec2; - using float3 = glm::vec3; - using float4 = glm::vec4; - - using uint = glm::uint; - using uint2 = glm::uvec2; - using uint3 = glm::uvec3; - using uint4 = glm::uvec4; - - using int2 = glm::ivec2; - using int3 = glm::ivec3; - using int4 = glm::ivec4; - - using bool2 = glm::bvec2; - using bool3 = glm::bvec3; - using bool4 = glm::bvec4; - - inline std::string to_string(const float2& v) { return "float2(" + std::to_string(v.x) + "," + std::to_string(v.y) + ")"; } - inline std::string to_string(const float3& v) { return "float3(" + std::to_string(v.x) + "," + std::to_string(v.y) + "," + std::to_string(v.z) + ")"; } - inline std::string to_string(const float4& v) { return "float4(" + std::to_string(v.x) + "," + std::to_string(v.y) + "," + std::to_string(v.z) + "," + std::to_string(v.w) + ")"; } - - inline std::string to_string(const uint2& v) { return "uint2(" + std::to_string(v.x) + "," + std::to_string(v.y) + ")"; } - inline std::string to_string(const uint3& v) { return "uint3(" + std::to_string(v.x) + "," + std::to_string(v.y) + "," + std::to_string(v.z) + ")"; } - inline std::string to_string(const uint4& v) { return "uint4(" + std::to_string(v.x) + "," + std::to_string(v.y) + "," + std::to_string(v.z) + "," + std::to_string(v.w) + ")"; } - - inline std::string to_string(const int2& v) { return "int2(" + std::to_string(v.x) + "," + std::to_string(v.y) + ")"; } - inline std::string to_string(const int3& v) { return "int3(" + std::to_string(v.x) + "," + std::to_string(v.y) + "," + std::to_string(v.z) + ")"; } - inline std::string to_string(const int4& v) { return "int4(" + std::to_string(v.x) + "," + std::to_string(v.y) + "," + std::to_string(v.z) + "," + std::to_string(v.w) + ")"; } - - inline std::string to_string(const bool2& v) { return "bool2(" + std::to_string(v.x) + "," + std::to_string(v.y) + ")"; } - inline std::string to_string(const bool3& v) { return "bool3(" + std::to_string(v.x) + "," + std::to_string(v.y) + "," + std::to_string(v.z) + ")"; } - inline std::string to_string(const bool4& v) { return "bool4(" + std::to_string(v.x) + "," + std::to_string(v.y) + "," + std::to_string(v.z) + "," + std::to_string(v.w) + ")"; } -} - -// We must extend the glm namespace, since otherwise std::less won't find the operator -namespace glm -{ - template - bool operator<(const ::glm::vec& lhs, const ::glm::vec& rhs) - { - for (int i = 0; i < TCount; ++i) - if (lhs[i] != rhs[i]) - return lhs[i] < rhs[i]; - return false; - } -} - -template -struct ::fmt::formatter<::glm::vec> : formatter -{ - template - auto format(const ::glm::vec& vec, FormatContext& ctx) const - { - auto out = ctx.out(); - for (int i = 0; i < TCount; ++i) - { - out = ::fmt::format_to(out, "{}", (i == 0) ? "{" : ", "); - out = formatter::format(vec[i], ctx); - } - out = ::fmt::format_to(out, "}}"); - return out; - } -}; +#include "VectorTypes.h" +#include "VectorMath.h" diff --git a/Source/Falcor/Utils/Math/VectorMath.h b/Source/Falcor/Utils/Math/VectorMath.h new file mode 100644 index 000000000..a8635980a --- /dev/null +++ b/Source/Falcor/Utils/Math/VectorMath.h @@ -0,0 +1,1817 @@ +/*************************************************************************** + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. + # + # Redistribution and use in source and binary forms, with or without + # modification, are permitted provided that the following conditions + # are met: + # * Redistributions of source code must retain the above copyright + # notice, this list of conditions and the following disclaimer. + # * Redistributions in binary form must reproduce the above copyright + # notice, this list of conditions and the following disclaimer in the + # documentation and/or other materials provided with the distribution. + # * Neither the name of NVIDIA CORPORATION nor the names of its + # contributors may be used to endorse or promote products derived + # from this software without specific prior written permission. + # + # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY + # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + # (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 "VectorTypes.h" +#include "ScalarMath.h" + +#include + +#include + +namespace Falcor +{ +namespace math +{ + +// ---------------------------------------------------------------------------- +// Boolean reductions +// ---------------------------------------------------------------------------- + +// clang-format off +[[nodiscard]] constexpr bool any(const bool1 v) noexcept { return v.x; } +[[nodiscard]] constexpr bool any(const bool2 v) noexcept { return v.x || v.y; } +[[nodiscard]] constexpr bool any(const bool3 v) noexcept { return v.x || v.y || v.z; } +[[nodiscard]] constexpr bool any(const bool4 v) noexcept { return v.x || v.y || v.z || v.w; } + +[[nodiscard]] constexpr bool all(const bool1 v) noexcept { return v.x; } +[[nodiscard]] constexpr bool all(const bool2 v) noexcept { return v.x && v.y; } +[[nodiscard]] constexpr bool all(const bool3 v) noexcept { return v.x && v.y && v.z; } +[[nodiscard]] constexpr bool all(const bool4 v) noexcept { return v.x && v.y && v.z && v.w; } + +[[nodiscard]] constexpr bool none(const bool1 v) noexcept { return !any(v); } +[[nodiscard]] constexpr bool none(const bool2 v) noexcept { return !any(v); } +[[nodiscard]] constexpr bool none(const bool3 v) noexcept { return !any(v); } +[[nodiscard]] constexpr bool none(const bool4 v) noexcept { return !any(v); } +// clang-format on + +// ---------------------------------------------------------------------------- +// Unary operators +// ---------------------------------------------------------------------------- + +/// Unary plus operator +template, bool> = false> +[[nodiscard]] constexpr auto operator+(const vector v) noexcept +{ + return v; +} + +#if FALCOR_MSVC +#pragma warning(push) +#pragma warning(disable : 4146) // unary minus operator applied to unsigned type, result still unsigned +#endif +/// Unary minus operator +template, bool> = false> +[[nodiscard]] constexpr auto operator-(const vector v) noexcept +{ + if constexpr (N == 1) + return vector{-v.x}; + else if constexpr (N == 2) + return vector{-v.x, -v.y}; + else if constexpr (N == 3) + return vector{-v.x, -v.y, -v.z}; + else + return vector{-v.x, -v.y, -v.z, -v.w}; +} +#if FALCOR_MSVC +#pragma warning(pop) +#endif + +/// Unary not operator +template +[[nodiscard]] constexpr auto operator!(const vector v) noexcept +{ + if constexpr (N == 1) + return bool1{!v.x}; + else if constexpr (N == 2) + return bool2{!v.x, !v.y}; + else if constexpr (N == 3) + return bool3{!v.x, !v.y, !v.z}; + else + return bool4{!v.x, !v.y, !v.z, !v.w}; +} + +/// Unary not operator +template, bool> = false> +[[nodiscard]] constexpr auto operator~(const vector v) noexcept +{ + if constexpr (N == 1) + return vector{~v.x}; + else if constexpr (N == 2) + return vector{~v.x, ~v.y}; + else if constexpr (N == 3) + return vector{~v.x, ~v.y, ~v.z}; + else + return vector{~v.x, ~v.y, ~v.z, ~v.w}; +} + +// ---------------------------------------------------------------------------- +// Binary operators +// ---------------------------------------------------------------------------- + +// clang-format off +/* << = false> +[[nodiscard]] constexpr vector operator{op}(const vector& lhs, const vector& rhs) +{{ + if constexpr (N == 1) + return vector{{lhs.x {op} rhs.x}}; + else if constexpr (N == 2) + return vector{{lhs.x {op} rhs.x, lhs.y {op} rhs.y}}; + else if constexpr (N == 3) + return vector{{lhs.x {op} rhs.x, lhs.y {op} rhs.y, lhs.z {op} rhs.z}}; + else if constexpr (N == 4) + return vector{{lhs.x {op} rhs.x, lhs.y {op} rhs.y, lhs.z {op} rhs.z, lhs.w {op} rhs.w}}; +}} + +/// Binary {op} operator +template = false> +[[nodiscard]] constexpr vector operator{op}(const vector& lhs, T rhs) +{{ + return lhs {op} vector(rhs); +}} + +/// Binary {op} operator +template = false> +[[nodiscard]] constexpr vector operator{op}(T lhs, const vector& rhs) +{{ + return vector(lhs) {op} rhs; +}} +""") + +print_binary_operator("+", "is_arithmetic_v") +print_binary_operator("-", "is_arithmetic_v") +print_binary_operator("*", "is_arithmetic_v") +print_binary_operator("/", "is_arithmetic_v") +print_binary_operator("%", "is_integral_v") +print_binary_operator("<<", "is_integral_v") +print_binary_operator(">>", "is_integral_v") +print_binary_operator("|", "is_integral_v") +print_binary_operator("&", "is_integral_v") +print_binary_operator("^", "is_integral_v") +>>> */ +/// Binary + operator +template, bool> = false> +[[nodiscard]] constexpr vector operator+(const vector& lhs, const vector& rhs) +{ + if constexpr (N == 1) + return vector{lhs.x + rhs.x}; + else if constexpr (N == 2) + return vector{lhs.x + rhs.x, lhs.y + rhs.y}; + else if constexpr (N == 3) + return vector{lhs.x + rhs.x, lhs.y + rhs.y, lhs.z + rhs.z}; + else if constexpr (N == 4) + return vector{lhs.x + rhs.x, lhs.y + rhs.y, lhs.z + rhs.z, lhs.w + rhs.w}; +} + +/// Binary + operator +template, bool> = false> +[[nodiscard]] constexpr vector operator+(const vector& lhs, T rhs) +{ + return lhs + vector(rhs); +} + +/// Binary + operator +template, bool> = false> +[[nodiscard]] constexpr vector operator+(T lhs, const vector& rhs) +{ + return vector(lhs) + rhs; +} + +/// Binary - operator +template, bool> = false> +[[nodiscard]] constexpr vector operator-(const vector& lhs, const vector& rhs) +{ + if constexpr (N == 1) + return vector{lhs.x - rhs.x}; + else if constexpr (N == 2) + return vector{lhs.x - rhs.x, lhs.y - rhs.y}; + else if constexpr (N == 3) + return vector{lhs.x - rhs.x, lhs.y - rhs.y, lhs.z - rhs.z}; + else if constexpr (N == 4) + return vector{lhs.x - rhs.x, lhs.y - rhs.y, lhs.z - rhs.z, lhs.w - rhs.w}; +} + +/// Binary - operator +template, bool> = false> +[[nodiscard]] constexpr vector operator-(const vector& lhs, T rhs) +{ + return lhs - vector(rhs); +} + +/// Binary - operator +template, bool> = false> +[[nodiscard]] constexpr vector operator-(T lhs, const vector& rhs) +{ + return vector(lhs) - rhs; +} + +/// Binary * operator +template, bool> = false> +[[nodiscard]] constexpr vector operator*(const vector& lhs, const vector& rhs) +{ + if constexpr (N == 1) + return vector{lhs.x * rhs.x}; + else if constexpr (N == 2) + return vector{lhs.x * rhs.x, lhs.y * rhs.y}; + else if constexpr (N == 3) + return vector{lhs.x * rhs.x, lhs.y * rhs.y, lhs.z * rhs.z}; + else if constexpr (N == 4) + return vector{lhs.x * rhs.x, lhs.y * rhs.y, lhs.z * rhs.z, lhs.w * rhs.w}; +} + +/// Binary * operator +template, bool> = false> +[[nodiscard]] constexpr vector operator*(const vector& lhs, T rhs) +{ + return lhs * vector(rhs); +} + +/// Binary * operator +template, bool> = false> +[[nodiscard]] constexpr vector operator*(T lhs, const vector& rhs) +{ + return vector(lhs) * rhs; +} + +/// Binary / operator +template, bool> = false> +[[nodiscard]] constexpr vector operator/(const vector& lhs, const vector& rhs) +{ + if constexpr (N == 1) + return vector{lhs.x / rhs.x}; + else if constexpr (N == 2) + return vector{lhs.x / rhs.x, lhs.y / rhs.y}; + else if constexpr (N == 3) + return vector{lhs.x / rhs.x, lhs.y / rhs.y, lhs.z / rhs.z}; + else if constexpr (N == 4) + return vector{lhs.x / rhs.x, lhs.y / rhs.y, lhs.z / rhs.z, lhs.w / rhs.w}; +} + +/// Binary / operator +template, bool> = false> +[[nodiscard]] constexpr vector operator/(const vector& lhs, T rhs) +{ + return lhs / vector(rhs); +} + +/// Binary / operator +template, bool> = false> +[[nodiscard]] constexpr vector operator/(T lhs, const vector& rhs) +{ + return vector(lhs) / rhs; +} + +/// Binary % operator +template, bool> = false> +[[nodiscard]] constexpr vector operator%(const vector& lhs, const vector& rhs) +{ + if constexpr (N == 1) + return vector{lhs.x % rhs.x}; + else if constexpr (N == 2) + return vector{lhs.x % rhs.x, lhs.y % rhs.y}; + else if constexpr (N == 3) + return vector{lhs.x % rhs.x, lhs.y % rhs.y, lhs.z % rhs.z}; + else if constexpr (N == 4) + return vector{lhs.x % rhs.x, lhs.y % rhs.y, lhs.z % rhs.z, lhs.w % rhs.w}; +} + +/// Binary % operator +template, bool> = false> +[[nodiscard]] constexpr vector operator%(const vector& lhs, T rhs) +{ + return lhs % vector(rhs); +} + +/// Binary % operator +template, bool> = false> +[[nodiscard]] constexpr vector operator%(T lhs, const vector& rhs) +{ + return vector(lhs) % rhs; +} + +/// Binary << operator +template, bool> = false> +[[nodiscard]] constexpr vector operator<<(const vector& lhs, const vector& rhs) +{ + if constexpr (N == 1) + return vector{lhs.x << rhs.x}; + else if constexpr (N == 2) + return vector{lhs.x << rhs.x, lhs.y << rhs.y}; + else if constexpr (N == 3) + return vector{lhs.x << rhs.x, lhs.y << rhs.y, lhs.z << rhs.z}; + else if constexpr (N == 4) + return vector{lhs.x << rhs.x, lhs.y << rhs.y, lhs.z << rhs.z, lhs.w << rhs.w}; +} + +/// Binary << operator +template, bool> = false> +[[nodiscard]] constexpr vector operator<<(const vector& lhs, T rhs) +{ + return lhs << vector(rhs); +} + +/// Binary << operator +template, bool> = false> +[[nodiscard]] constexpr vector operator<<(T lhs, const vector& rhs) +{ + return vector(lhs) << rhs; +} + +/// Binary >> operator +template, bool> = false> +[[nodiscard]] constexpr vector operator>>(const vector& lhs, const vector& rhs) +{ + if constexpr (N == 1) + return vector{lhs.x >> rhs.x}; + else if constexpr (N == 2) + return vector{lhs.x >> rhs.x, lhs.y >> rhs.y}; + else if constexpr (N == 3) + return vector{lhs.x >> rhs.x, lhs.y >> rhs.y, lhs.z >> rhs.z}; + else if constexpr (N == 4) + return vector{lhs.x >> rhs.x, lhs.y >> rhs.y, lhs.z >> rhs.z, lhs.w >> rhs.w}; +} + +/// Binary >> operator +template, bool> = false> +[[nodiscard]] constexpr vector operator>>(const vector& lhs, T rhs) +{ + return lhs >> vector(rhs); +} + +/// Binary >> operator +template, bool> = false> +[[nodiscard]] constexpr vector operator>>(T lhs, const vector& rhs) +{ + return vector(lhs) >> rhs; +} + +/// Binary | operator +template, bool> = false> +[[nodiscard]] constexpr vector operator|(const vector& lhs, const vector& rhs) +{ + if constexpr (N == 1) + return vector{lhs.x | rhs.x}; + else if constexpr (N == 2) + return vector{lhs.x | rhs.x, lhs.y | rhs.y}; + else if constexpr (N == 3) + return vector{lhs.x | rhs.x, lhs.y | rhs.y, lhs.z | rhs.z}; + else if constexpr (N == 4) + return vector{lhs.x | rhs.x, lhs.y | rhs.y, lhs.z | rhs.z, lhs.w | rhs.w}; +} + +/// Binary | operator +template, bool> = false> +[[nodiscard]] constexpr vector operator|(const vector& lhs, T rhs) +{ + return lhs | vector(rhs); +} + +/// Binary | operator +template, bool> = false> +[[nodiscard]] constexpr vector operator|(T lhs, const vector& rhs) +{ + return vector(lhs) | rhs; +} + +/// Binary & operator +template, bool> = false> +[[nodiscard]] constexpr vector operator&(const vector& lhs, const vector& rhs) +{ + if constexpr (N == 1) + return vector{lhs.x & rhs.x}; + else if constexpr (N == 2) + return vector{lhs.x & rhs.x, lhs.y & rhs.y}; + else if constexpr (N == 3) + return vector{lhs.x & rhs.x, lhs.y & rhs.y, lhs.z & rhs.z}; + else if constexpr (N == 4) + return vector{lhs.x & rhs.x, lhs.y & rhs.y, lhs.z & rhs.z, lhs.w & rhs.w}; +} + +/// Binary & operator +template, bool> = false> +[[nodiscard]] constexpr vector operator&(const vector& lhs, T rhs) +{ + return lhs & vector(rhs); +} + +/// Binary & operator +template, bool> = false> +[[nodiscard]] constexpr vector operator&(T lhs, const vector& rhs) +{ + return vector(lhs) & rhs; +} + +/// Binary ^ operator +template, bool> = false> +[[nodiscard]] constexpr vector operator^(const vector& lhs, const vector& rhs) +{ + if constexpr (N == 1) + return vector{lhs.x ^ rhs.x}; + else if constexpr (N == 2) + return vector{lhs.x ^ rhs.x, lhs.y ^ rhs.y}; + else if constexpr (N == 3) + return vector{lhs.x ^ rhs.x, lhs.y ^ rhs.y, lhs.z ^ rhs.z}; + else if constexpr (N == 4) + return vector{lhs.x ^ rhs.x, lhs.y ^ rhs.y, lhs.z ^ rhs.z, lhs.w ^ rhs.w}; +} + +/// Binary ^ operator +template, bool> = false> +[[nodiscard]] constexpr vector operator^(const vector& lhs, T rhs) +{ + return lhs ^ vector(rhs); +} + +/// Binary ^ operator +template, bool> = false> +[[nodiscard]] constexpr vector operator^(T lhs, const vector& rhs) +{ + return vector(lhs) ^ rhs; +} + +/* <<>> */ +// clang-format on + +// ---------------------------------------------------------------------------- +// Binary logic operators +// ---------------------------------------------------------------------------- + +// clang-format off +/* << = false> +[[nodiscard]] constexpr auto operator{op}(const vector& lhs, const vector& rhs) +{{ + if constexpr (N == 1) + return bool1{{lhs.x {op} rhs.x}}; + else if constexpr (N == 2) + return bool2{{lhs.x {op} rhs.x, lhs.y {op} rhs.y}}; + else if constexpr (N == 3) + return bool3{{lhs.x {op} rhs.x, lhs.y {op} rhs.y, lhs.z {op} rhs.z}}; + else if constexpr (N == 4) + return bool4{{lhs.x {op} rhs.x, lhs.y {op} rhs.y, lhs.z {op} rhs.z, lhs.w {op} rhs.w}}; +}} + +/// Binary {op} operator +template = false> +[[nodiscard]] constexpr auto operator{op}(const vector& lhs, T rhs) +{{ + return lhs {op} vector(rhs); +}} + +/// Binary {op} operator +template = false> +[[nodiscard]] constexpr auto operator{op}(T lhs, const vector& rhs) +{{ + return vector(lhs) {op} rhs; +}} +""") + +print_binary_operator("||", "is_bool_v") +print_binary_operator("&&", "is_bool_v") +print_binary_operator("==", "true") +print_binary_operator("!=", "true") +print_binary_operator("<", "is_arithmetic_v") +print_binary_operator(">", "is_arithmetic_v") +print_binary_operator("<=", "is_arithmetic_v") +print_binary_operator(">=", "is_arithmetic_v") +>>> */ +/// Binary || operator +template, bool> = false> +[[nodiscard]] constexpr auto operator||(const vector& lhs, const vector& rhs) +{ + if constexpr (N == 1) + return bool1{lhs.x || rhs.x}; + else if constexpr (N == 2) + return bool2{lhs.x || rhs.x, lhs.y || rhs.y}; + else if constexpr (N == 3) + return bool3{lhs.x || rhs.x, lhs.y || rhs.y, lhs.z || rhs.z}; + else if constexpr (N == 4) + return bool4{lhs.x || rhs.x, lhs.y || rhs.y, lhs.z || rhs.z, lhs.w || rhs.w}; +} + +/// Binary || operator +template, bool> = false> +[[nodiscard]] constexpr auto operator||(const vector& lhs, T rhs) +{ + return lhs || vector(rhs); +} + +/// Binary || operator +template, bool> = false> +[[nodiscard]] constexpr auto operator||(T lhs, const vector& rhs) +{ + return vector(lhs) || rhs; +} + +/// Binary && operator +template, bool> = false> +[[nodiscard]] constexpr auto operator&&(const vector& lhs, const vector& rhs) +{ + if constexpr (N == 1) + return bool1{lhs.x && rhs.x}; + else if constexpr (N == 2) + return bool2{lhs.x && rhs.x, lhs.y && rhs.y}; + else if constexpr (N == 3) + return bool3{lhs.x && rhs.x, lhs.y && rhs.y, lhs.z && rhs.z}; + else if constexpr (N == 4) + return bool4{lhs.x && rhs.x, lhs.y && rhs.y, lhs.z && rhs.z, lhs.w && rhs.w}; +} + +/// Binary && operator +template, bool> = false> +[[nodiscard]] constexpr auto operator&&(const vector& lhs, T rhs) +{ + return lhs && vector(rhs); +} + +/// Binary && operator +template, bool> = false> +[[nodiscard]] constexpr auto operator&&(T lhs, const vector& rhs) +{ + return vector(lhs) && rhs; +} + +/// Binary == operator +template = false> +[[nodiscard]] constexpr auto operator==(const vector& lhs, const vector& rhs) +{ + if constexpr (N == 1) + return bool1{lhs.x == rhs.x}; + else if constexpr (N == 2) + return bool2{lhs.x == rhs.x, lhs.y == rhs.y}; + else if constexpr (N == 3) + return bool3{lhs.x == rhs.x, lhs.y == rhs.y, lhs.z == rhs.z}; + else if constexpr (N == 4) + return bool4{lhs.x == rhs.x, lhs.y == rhs.y, lhs.z == rhs.z, lhs.w == rhs.w}; +} + +/// Binary == operator +template = false> +[[nodiscard]] constexpr auto operator==(const vector& lhs, T rhs) +{ + return lhs == vector(rhs); +} + +/// Binary == operator +template = false> +[[nodiscard]] constexpr auto operator==(T lhs, const vector& rhs) +{ + return vector(lhs) == rhs; +} + +/// Binary != operator +template = false> +[[nodiscard]] constexpr auto operator!=(const vector& lhs, const vector& rhs) +{ + if constexpr (N == 1) + return bool1{lhs.x != rhs.x}; + else if constexpr (N == 2) + return bool2{lhs.x != rhs.x, lhs.y != rhs.y}; + else if constexpr (N == 3) + return bool3{lhs.x != rhs.x, lhs.y != rhs.y, lhs.z != rhs.z}; + else if constexpr (N == 4) + return bool4{lhs.x != rhs.x, lhs.y != rhs.y, lhs.z != rhs.z, lhs.w != rhs.w}; +} + +/// Binary != operator +template = false> +[[nodiscard]] constexpr auto operator!=(const vector& lhs, T rhs) +{ + return lhs != vector(rhs); +} + +/// Binary != operator +template = false> +[[nodiscard]] constexpr auto operator!=(T lhs, const vector& rhs) +{ + return vector(lhs) != rhs; +} + +/// Binary < operator +template, bool> = false> +[[nodiscard]] constexpr auto operator<(const vector& lhs, const vector& rhs) +{ + if constexpr (N == 1) + return bool1{lhs.x < rhs.x}; + else if constexpr (N == 2) + return bool2{lhs.x < rhs.x, lhs.y < rhs.y}; + else if constexpr (N == 3) + return bool3{lhs.x < rhs.x, lhs.y < rhs.y, lhs.z < rhs.z}; + else if constexpr (N == 4) + return bool4{lhs.x < rhs.x, lhs.y < rhs.y, lhs.z < rhs.z, lhs.w < rhs.w}; +} + +/// Binary < operator +template, bool> = false> +[[nodiscard]] constexpr auto operator<(const vector& lhs, T rhs) +{ + return lhs < vector(rhs); +} + +/// Binary < operator +template, bool> = false> +[[nodiscard]] constexpr auto operator<(T lhs, const vector& rhs) +{ + return vector(lhs) < rhs; +} + +/// Binary > operator +template, bool> = false> +[[nodiscard]] constexpr auto operator>(const vector& lhs, const vector& rhs) +{ + if constexpr (N == 1) + return bool1{lhs.x > rhs.x}; + else if constexpr (N == 2) + return bool2{lhs.x > rhs.x, lhs.y > rhs.y}; + else if constexpr (N == 3) + return bool3{lhs.x > rhs.x, lhs.y > rhs.y, lhs.z > rhs.z}; + else if constexpr (N == 4) + return bool4{lhs.x > rhs.x, lhs.y > rhs.y, lhs.z > rhs.z, lhs.w > rhs.w}; +} + +/// Binary > operator +template, bool> = false> +[[nodiscard]] constexpr auto operator>(const vector& lhs, T rhs) +{ + return lhs > vector(rhs); +} + +/// Binary > operator +template, bool> = false> +[[nodiscard]] constexpr auto operator>(T lhs, const vector& rhs) +{ + return vector(lhs) > rhs; +} + +/// Binary <= operator +template, bool> = false> +[[nodiscard]] constexpr auto operator<=(const vector& lhs, const vector& rhs) +{ + if constexpr (N == 1) + return bool1{lhs.x <= rhs.x}; + else if constexpr (N == 2) + return bool2{lhs.x <= rhs.x, lhs.y <= rhs.y}; + else if constexpr (N == 3) + return bool3{lhs.x <= rhs.x, lhs.y <= rhs.y, lhs.z <= rhs.z}; + else if constexpr (N == 4) + return bool4{lhs.x <= rhs.x, lhs.y <= rhs.y, lhs.z <= rhs.z, lhs.w <= rhs.w}; +} + +/// Binary <= operator +template, bool> = false> +[[nodiscard]] constexpr auto operator<=(const vector& lhs, T rhs) +{ + return lhs <= vector(rhs); +} + +/// Binary <= operator +template, bool> = false> +[[nodiscard]] constexpr auto operator<=(T lhs, const vector& rhs) +{ + return vector(lhs) <= rhs; +} + +/// Binary >= operator +template, bool> = false> +[[nodiscard]] constexpr auto operator>=(const vector& lhs, const vector& rhs) +{ + if constexpr (N == 1) + return bool1{lhs.x >= rhs.x}; + else if constexpr (N == 2) + return bool2{lhs.x >= rhs.x, lhs.y >= rhs.y}; + else if constexpr (N == 3) + return bool3{lhs.x >= rhs.x, lhs.y >= rhs.y, lhs.z >= rhs.z}; + else if constexpr (N == 4) + return bool4{lhs.x >= rhs.x, lhs.y >= rhs.y, lhs.z >= rhs.z, lhs.w >= rhs.w}; +} + +/// Binary >= operator +template, bool> = false> +[[nodiscard]] constexpr auto operator>=(const vector& lhs, T rhs) +{ + return lhs >= vector(rhs); +} + +/// Binary >= operator +template, bool> = false> +[[nodiscard]] constexpr auto operator>=(T lhs, const vector& rhs) +{ + return vector(lhs) >= rhs; +} + +/* <<>> */ +// clang-format on + +// ---------------------------------------------------------------------------- +// Assignment operators +// ---------------------------------------------------------------------------- + +// clang-format off +/* << = false> +constexpr vector operator{op}(vector& lhs, const vector& rhs) +{{ + lhs.x {op} rhs.x; + if constexpr (N >= 2) + lhs.y {op} rhs.y; + if constexpr (N >= 3) + lhs.z {op} rhs.z; + if constexpr (N >= 4) + lhs.w {op} rhs.w; + return lhs; +}} + +/// {op} assignment operator +template = false> +constexpr vector operator{op}(vector& lhs, T rhs) +{{ + return (lhs {op} vector(rhs)); +}} +""") + +print_assignment_operator("+=", "is_arithmetic_v") +print_assignment_operator("-=", "is_arithmetic_v") +print_assignment_operator("*=", "is_arithmetic_v") +print_assignment_operator("/=", "is_arithmetic_v") +print_assignment_operator("%=", "is_integral_v") +print_assignment_operator("<<=", "is_integral_v") +print_assignment_operator(">>=", "is_integral_v") +print_assignment_operator("|=", "is_integral_v") +print_assignment_operator("&=", "is_integral_v") +print_assignment_operator("^=", "is_integral_v") +>>> */ +/// += assignment operator +template, bool> = false> +constexpr vector operator+=(vector& lhs, const vector& rhs) +{ + lhs.x += rhs.x; + if constexpr (N >= 2) + lhs.y += rhs.y; + if constexpr (N >= 3) + lhs.z += rhs.z; + if constexpr (N >= 4) + lhs.w += rhs.w; + return lhs; +} + +/// += assignment operator +template, bool> = false> +constexpr vector operator+=(vector& lhs, T rhs) +{ + return (lhs += vector(rhs)); +} + +/// -= assignment operator +template, bool> = false> +constexpr vector operator-=(vector& lhs, const vector& rhs) +{ + lhs.x -= rhs.x; + if constexpr (N >= 2) + lhs.y -= rhs.y; + if constexpr (N >= 3) + lhs.z -= rhs.z; + if constexpr (N >= 4) + lhs.w -= rhs.w; + return lhs; +} + +/// -= assignment operator +template, bool> = false> +constexpr vector operator-=(vector& lhs, T rhs) +{ + return (lhs -= vector(rhs)); +} + +/// *= assignment operator +template, bool> = false> +constexpr vector operator*=(vector& lhs, const vector& rhs) +{ + lhs.x *= rhs.x; + if constexpr (N >= 2) + lhs.y *= rhs.y; + if constexpr (N >= 3) + lhs.z *= rhs.z; + if constexpr (N >= 4) + lhs.w *= rhs.w; + return lhs; +} + +/// *= assignment operator +template, bool> = false> +constexpr vector operator*=(vector& lhs, T rhs) +{ + return (lhs *= vector(rhs)); +} + +/// /= assignment operator +template, bool> = false> +constexpr vector operator/=(vector& lhs, const vector& rhs) +{ + lhs.x /= rhs.x; + if constexpr (N >= 2) + lhs.y /= rhs.y; + if constexpr (N >= 3) + lhs.z /= rhs.z; + if constexpr (N >= 4) + lhs.w /= rhs.w; + return lhs; +} + +/// /= assignment operator +template, bool> = false> +constexpr vector operator/=(vector& lhs, T rhs) +{ + return (lhs /= vector(rhs)); +} + +/// %= assignment operator +template, bool> = false> +constexpr vector operator%=(vector& lhs, const vector& rhs) +{ + lhs.x %= rhs.x; + if constexpr (N >= 2) + lhs.y %= rhs.y; + if constexpr (N >= 3) + lhs.z %= rhs.z; + if constexpr (N >= 4) + lhs.w %= rhs.w; + return lhs; +} + +/// %= assignment operator +template, bool> = false> +constexpr vector operator%=(vector& lhs, T rhs) +{ + return (lhs %= vector(rhs)); +} + +/// <<= assignment operator +template, bool> = false> +constexpr vector operator<<=(vector& lhs, const vector& rhs) +{ + lhs.x <<= rhs.x; + if constexpr (N >= 2) + lhs.y <<= rhs.y; + if constexpr (N >= 3) + lhs.z <<= rhs.z; + if constexpr (N >= 4) + lhs.w <<= rhs.w; + return lhs; +} + +/// <<= assignment operator +template, bool> = false> +constexpr vector operator<<=(vector& lhs, T rhs) +{ + return (lhs <<= vector(rhs)); +} + +/// >>= assignment operator +template, bool> = false> +constexpr vector operator>>=(vector& lhs, const vector& rhs) +{ + lhs.x >>= rhs.x; + if constexpr (N >= 2) + lhs.y >>= rhs.y; + if constexpr (N >= 3) + lhs.z >>= rhs.z; + if constexpr (N >= 4) + lhs.w >>= rhs.w; + return lhs; +} + +/// >>= assignment operator +template, bool> = false> +constexpr vector operator>>=(vector& lhs, T rhs) +{ + return (lhs >>= vector(rhs)); +} + +/// |= assignment operator +template, bool> = false> +constexpr vector operator|=(vector& lhs, const vector& rhs) +{ + lhs.x |= rhs.x; + if constexpr (N >= 2) + lhs.y |= rhs.y; + if constexpr (N >= 3) + lhs.z |= rhs.z; + if constexpr (N >= 4) + lhs.w |= rhs.w; + return lhs; +} + +/// |= assignment operator +template, bool> = false> +constexpr vector operator|=(vector& lhs, T rhs) +{ + return (lhs |= vector(rhs)); +} + +/// &= assignment operator +template, bool> = false> +constexpr vector operator&=(vector& lhs, const vector& rhs) +{ + lhs.x &= rhs.x; + if constexpr (N >= 2) + lhs.y &= rhs.y; + if constexpr (N >= 3) + lhs.z &= rhs.z; + if constexpr (N >= 4) + lhs.w &= rhs.w; + return lhs; +} + +/// &= assignment operator +template, bool> = false> +constexpr vector operator&=(vector& lhs, T rhs) +{ + return (lhs &= vector(rhs)); +} + +/// ^= assignment operator +template, bool> = false> +constexpr vector operator^=(vector& lhs, const vector& rhs) +{ + lhs.x ^= rhs.x; + if constexpr (N >= 2) + lhs.y ^= rhs.y; + if constexpr (N >= 3) + lhs.z ^= rhs.z; + if constexpr (N >= 4) + lhs.w ^= rhs.w; + return lhs; +} + +/// ^= assignment operator +template, bool> = false> +constexpr vector operator^=(vector& lhs, T rhs) +{ + return (lhs ^= vector(rhs)); +} + +/* <<>> */ +// clang-format on + +// ---------------------------------------------------------------------------- +// Intrinsics +// ---------------------------------------------------------------------------- + +// clang-format off +/* << = false> +[[nodiscard]] constexpr vector<{return_type}, N> {func}(const vector& {arg}) +{{ + if constexpr (N == 1) + return vector<{return_type}, N>{{{func}({arg}.x)}}; + else if constexpr (N == 2) + return vector<{return_type}, N>{{{func}({arg}.x), {func}({arg}.y)}}; + else if constexpr (N == 3) + return vector<{return_type}, N>{{{func}({arg}.x), {func}({arg}.y), {func}({arg}.z)}}; + else if constexpr (N == 4) + return vector<{return_type}, N>{{{func}({arg}.x), {func}({arg}.y), {func}({arg}.z), {func}({arg}.w)}}; +}} +""") +def print_binary_func(func, enable_if, arg0="x", arg1="y", return_type="T"): + print(f"""/// {func} +template = false> +[[nodiscard]] constexpr vector<{return_type}, N> {func}(const vector& {arg0}, const vector& {arg1}) +{{ + if constexpr (N == 1) + return vector<{return_type}, N>{{{func}({arg0}.x, {arg1}.x)}}; + else if constexpr (N == 2) + return vector<{return_type}, N>{{{func}({arg0}.x, {arg1}.x), {func}({arg0}.y, {arg1}.y)}}; + else if constexpr (N == 3) + return vector<{return_type}, N>{{{func}({arg0}.x, {arg1}.x), {func}({arg0}.y, {arg1}.y), {func}({arg0}.z, {arg1}.z)}}; + else if constexpr (N == 4) + return vector<{return_type}, N>{{{func}({arg0}.x, {arg1}.x), {func}({arg0}.y, {arg1}.y), {func}({arg0}.z, {arg1}.z), {func}({arg0}.w, {arg1}.w)}}; +}} +""") +def print_ternary_func(func, enable_if, arg0="x", arg1="y", arg2="z", return_type="T"): + print(f"""/// {func} +template = false> +[[nodiscard]] constexpr vector<{return_type}, N> {func}(const vector& {arg0}, const vector& {arg1}, const vector& {arg2}) +{{ + if constexpr (N == 1) + return vector<{return_type}, N>{{{func}({arg0}.x, {arg1}.x, {arg2}.x)}}; + else if constexpr (N == 2) + return vector<{return_type}, N>{{{func}({arg0}.x, {arg1}.x, {arg2}.x), {func}({arg0}.y, {arg1}.y, {arg2}.y)}}; + else if constexpr (N == 3) + return vector<{return_type}, N>{{{func}({arg0}.x, {arg1}.x, {arg2}.x), {func}({arg0}.y, {arg1}.y, {arg2}.y), {func}({arg0}.z, {arg1}.z, {arg2}.z)}}; + else if constexpr (N == 4) + return vector<{return_type}, N>{{{func}({arg0}.x, {arg1}.x, {arg2}.x), {func}({arg0}.y, {arg1}.y, {arg2}.y), {func}({arg0}.z, {arg1}.z, {arg2}.z), {func}({arg0}.w, {arg1}.w, {arg2}.w)}}; +}} +""") +print_section("Basic functions") +print_binary_func("min", "is_arithmetic_v") +print_binary_func("max", "is_arithmetic_v") +print_ternary_func("clamp", "is_arithmetic_v", "x", "min_", "max_") +print_unary_func("abs", "is_signed_v") +print_unary_func("sign", "is_signed_v") +print_section("Floating point checks") +print_unary_func("isfinite", "is_floating_point_v", "x", "bool") +print_unary_func("isinf", "is_floating_point_v", "x", "bool") +print_unary_func("isnan", "is_floating_point_v", "x", "bool") +print_section("Rounding") +print_unary_func("floor", "is_floating_point_v") +print_unary_func("ceil", "is_floating_point_v") +print_unary_func("trunc", "is_floating_point_v") +print_unary_func("round", "is_floating_point_v") +print_section("Exponential") +print_binary_func("pow", "is_floating_point_v", "x", "y") +print_unary_func("sqrt", "is_floating_point_v") +print_unary_func("rsqrt", "is_floating_point_v") +print_unary_func("exp", "is_floating_point_v") +print_unary_func("exp2", "is_floating_point_v") +print_unary_func("log", "is_floating_point_v") +print_unary_func("log2", "is_floating_point_v") +print_unary_func("log10", "is_floating_point_v") +print_section("Trigonometry") +print_unary_func("radians", "is_floating_point_v") +print_unary_func("degrees", "is_floating_point_v") +print_unary_func("sin", "is_floating_point_v") +print_unary_func("cos", "is_floating_point_v") +print_unary_func("tan", "is_floating_point_v") +print_unary_func("asin", "is_floating_point_v") +print_unary_func("acos", "is_floating_point_v") +print_unary_func("atan", "is_floating_point_v") +print_binary_func("atan2", "is_floating_point_v", "y", "x") +print_unary_func("sinh", "is_floating_point_v") +print_unary_func("cosh", "is_floating_point_v") +print_unary_func("tanh", "is_floating_point_v") +print_section("Misc") +print_binary_func("fmod", "is_floating_point_v", "x", "y") +print_unary_func("frac", "is_floating_point_v") +print_ternary_func("lerp", "is_floating_point_v", "x", "y", "s") +print_unary_func("rcp", "is_floating_point_v") +print_unary_func("saturate", "is_floating_point_v") +print_ternary_func("smoothstep", "is_floating_point_v", "min_", "max_", "x") +print_binary_func("step", "is_floating_point_v", "x", "y") +>>> */ +// ---------------------------------------------------------------------------- +// Basic functions +// ---------------------------------------------------------------------------- + +/// min +template, bool> = false> +[[nodiscard]] constexpr vector min(const vector& x, const vector& y) +{ + if constexpr (N == 1) + return vector{min(x.x, y.x)}; + else if constexpr (N == 2) + return vector{min(x.x, y.x), min(x.y, y.y)}; + else if constexpr (N == 3) + return vector{min(x.x, y.x), min(x.y, y.y), min(x.z, y.z)}; + else if constexpr (N == 4) + return vector{min(x.x, y.x), min(x.y, y.y), min(x.z, y.z), min(x.w, y.w)}; +} + +/// max +template, bool> = false> +[[nodiscard]] constexpr vector max(const vector& x, const vector& y) +{ + if constexpr (N == 1) + return vector{max(x.x, y.x)}; + else if constexpr (N == 2) + return vector{max(x.x, y.x), max(x.y, y.y)}; + else if constexpr (N == 3) + return vector{max(x.x, y.x), max(x.y, y.y), max(x.z, y.z)}; + else if constexpr (N == 4) + return vector{max(x.x, y.x), max(x.y, y.y), max(x.z, y.z), max(x.w, y.w)}; +} + +/// clamp +template, bool> = false> +[[nodiscard]] constexpr vector clamp(const vector& x, const vector& min_, const vector& max_) +{ + if constexpr (N == 1) + return vector{clamp(x.x, min_.x, max_.x)}; + else if constexpr (N == 2) + return vector{clamp(x.x, min_.x, max_.x), clamp(x.y, min_.y, max_.y)}; + else if constexpr (N == 3) + return vector{clamp(x.x, min_.x, max_.x), clamp(x.y, min_.y, max_.y), clamp(x.z, min_.z, max_.z)}; + else if constexpr (N == 4) + return vector{clamp(x.x, min_.x, max_.x), clamp(x.y, min_.y, max_.y), clamp(x.z, min_.z, max_.z), clamp(x.w, min_.w, max_.w)}; +} + +/// abs +template, bool> = false> +[[nodiscard]] constexpr vector abs(const vector& x) +{ + if constexpr (N == 1) + return vector{abs(x.x)}; + else if constexpr (N == 2) + return vector{abs(x.x), abs(x.y)}; + else if constexpr (N == 3) + return vector{abs(x.x), abs(x.y), abs(x.z)}; + else if constexpr (N == 4) + return vector{abs(x.x), abs(x.y), abs(x.z), abs(x.w)}; +} + +/// sign +template, bool> = false> +[[nodiscard]] constexpr vector sign(const vector& x) +{ + if constexpr (N == 1) + return vector{sign(x.x)}; + else if constexpr (N == 2) + return vector{sign(x.x), sign(x.y)}; + else if constexpr (N == 3) + return vector{sign(x.x), sign(x.y), sign(x.z)}; + else if constexpr (N == 4) + return vector{sign(x.x), sign(x.y), sign(x.z), sign(x.w)}; +} + +// ---------------------------------------------------------------------------- +// Floating point checks +// ---------------------------------------------------------------------------- + +/// isfinite +template, bool> = false> +[[nodiscard]] constexpr vector isfinite(const vector& x) +{ + if constexpr (N == 1) + return vector{isfinite(x.x)}; + else if constexpr (N == 2) + return vector{isfinite(x.x), isfinite(x.y)}; + else if constexpr (N == 3) + return vector{isfinite(x.x), isfinite(x.y), isfinite(x.z)}; + else if constexpr (N == 4) + return vector{isfinite(x.x), isfinite(x.y), isfinite(x.z), isfinite(x.w)}; +} + +/// isinf +template, bool> = false> +[[nodiscard]] constexpr vector isinf(const vector& x) +{ + if constexpr (N == 1) + return vector{isinf(x.x)}; + else if constexpr (N == 2) + return vector{isinf(x.x), isinf(x.y)}; + else if constexpr (N == 3) + return vector{isinf(x.x), isinf(x.y), isinf(x.z)}; + else if constexpr (N == 4) + return vector{isinf(x.x), isinf(x.y), isinf(x.z), isinf(x.w)}; +} + +/// isnan +template, bool> = false> +[[nodiscard]] constexpr vector isnan(const vector& x) +{ + if constexpr (N == 1) + return vector{isnan(x.x)}; + else if constexpr (N == 2) + return vector{isnan(x.x), isnan(x.y)}; + else if constexpr (N == 3) + return vector{isnan(x.x), isnan(x.y), isnan(x.z)}; + else if constexpr (N == 4) + return vector{isnan(x.x), isnan(x.y), isnan(x.z), isnan(x.w)}; +} + +// ---------------------------------------------------------------------------- +// Rounding +// ---------------------------------------------------------------------------- + +/// floor +template, bool> = false> +[[nodiscard]] constexpr vector floor(const vector& x) +{ + if constexpr (N == 1) + return vector{floor(x.x)}; + else if constexpr (N == 2) + return vector{floor(x.x), floor(x.y)}; + else if constexpr (N == 3) + return vector{floor(x.x), floor(x.y), floor(x.z)}; + else if constexpr (N == 4) + return vector{floor(x.x), floor(x.y), floor(x.z), floor(x.w)}; +} + +/// ceil +template, bool> = false> +[[nodiscard]] constexpr vector ceil(const vector& x) +{ + if constexpr (N == 1) + return vector{ceil(x.x)}; + else if constexpr (N == 2) + return vector{ceil(x.x), ceil(x.y)}; + else if constexpr (N == 3) + return vector{ceil(x.x), ceil(x.y), ceil(x.z)}; + else if constexpr (N == 4) + return vector{ceil(x.x), ceil(x.y), ceil(x.z), ceil(x.w)}; +} + +/// trunc +template, bool> = false> +[[nodiscard]] constexpr vector trunc(const vector& x) +{ + if constexpr (N == 1) + return vector{trunc(x.x)}; + else if constexpr (N == 2) + return vector{trunc(x.x), trunc(x.y)}; + else if constexpr (N == 3) + return vector{trunc(x.x), trunc(x.y), trunc(x.z)}; + else if constexpr (N == 4) + return vector{trunc(x.x), trunc(x.y), trunc(x.z), trunc(x.w)}; +} + +/// round +template, bool> = false> +[[nodiscard]] constexpr vector round(const vector& x) +{ + if constexpr (N == 1) + return vector{round(x.x)}; + else if constexpr (N == 2) + return vector{round(x.x), round(x.y)}; + else if constexpr (N == 3) + return vector{round(x.x), round(x.y), round(x.z)}; + else if constexpr (N == 4) + return vector{round(x.x), round(x.y), round(x.z), round(x.w)}; +} + +// ---------------------------------------------------------------------------- +// Exponential +// ---------------------------------------------------------------------------- + +/// pow +template, bool> = false> +[[nodiscard]] constexpr vector pow(const vector& x, const vector& y) +{ + if constexpr (N == 1) + return vector{pow(x.x, y.x)}; + else if constexpr (N == 2) + return vector{pow(x.x, y.x), pow(x.y, y.y)}; + else if constexpr (N == 3) + return vector{pow(x.x, y.x), pow(x.y, y.y), pow(x.z, y.z)}; + else if constexpr (N == 4) + return vector{pow(x.x, y.x), pow(x.y, y.y), pow(x.z, y.z), pow(x.w, y.w)}; +} + +/// sqrt +template, bool> = false> +[[nodiscard]] constexpr vector sqrt(const vector& x) +{ + if constexpr (N == 1) + return vector{sqrt(x.x)}; + else if constexpr (N == 2) + return vector{sqrt(x.x), sqrt(x.y)}; + else if constexpr (N == 3) + return vector{sqrt(x.x), sqrt(x.y), sqrt(x.z)}; + else if constexpr (N == 4) + return vector{sqrt(x.x), sqrt(x.y), sqrt(x.z), sqrt(x.w)}; +} + +/// rsqrt +template, bool> = false> +[[nodiscard]] constexpr vector rsqrt(const vector& x) +{ + if constexpr (N == 1) + return vector{rsqrt(x.x)}; + else if constexpr (N == 2) + return vector{rsqrt(x.x), rsqrt(x.y)}; + else if constexpr (N == 3) + return vector{rsqrt(x.x), rsqrt(x.y), rsqrt(x.z)}; + else if constexpr (N == 4) + return vector{rsqrt(x.x), rsqrt(x.y), rsqrt(x.z), rsqrt(x.w)}; +} + +/// exp +template, bool> = false> +[[nodiscard]] constexpr vector exp(const vector& x) +{ + if constexpr (N == 1) + return vector{exp(x.x)}; + else if constexpr (N == 2) + return vector{exp(x.x), exp(x.y)}; + else if constexpr (N == 3) + return vector{exp(x.x), exp(x.y), exp(x.z)}; + else if constexpr (N == 4) + return vector{exp(x.x), exp(x.y), exp(x.z), exp(x.w)}; +} + +/// exp2 +template, bool> = false> +[[nodiscard]] constexpr vector exp2(const vector& x) +{ + if constexpr (N == 1) + return vector{exp2(x.x)}; + else if constexpr (N == 2) + return vector{exp2(x.x), exp2(x.y)}; + else if constexpr (N == 3) + return vector{exp2(x.x), exp2(x.y), exp2(x.z)}; + else if constexpr (N == 4) + return vector{exp2(x.x), exp2(x.y), exp2(x.z), exp2(x.w)}; +} + +/// log +template, bool> = false> +[[nodiscard]] constexpr vector log(const vector& x) +{ + if constexpr (N == 1) + return vector{log(x.x)}; + else if constexpr (N == 2) + return vector{log(x.x), log(x.y)}; + else if constexpr (N == 3) + return vector{log(x.x), log(x.y), log(x.z)}; + else if constexpr (N == 4) + return vector{log(x.x), log(x.y), log(x.z), log(x.w)}; +} + +/// log2 +template, bool> = false> +[[nodiscard]] constexpr vector log2(const vector& x) +{ + if constexpr (N == 1) + return vector{log2(x.x)}; + else if constexpr (N == 2) + return vector{log2(x.x), log2(x.y)}; + else if constexpr (N == 3) + return vector{log2(x.x), log2(x.y), log2(x.z)}; + else if constexpr (N == 4) + return vector{log2(x.x), log2(x.y), log2(x.z), log2(x.w)}; +} + +/// log10 +template, bool> = false> +[[nodiscard]] constexpr vector log10(const vector& x) +{ + if constexpr (N == 1) + return vector{log10(x.x)}; + else if constexpr (N == 2) + return vector{log10(x.x), log10(x.y)}; + else if constexpr (N == 3) + return vector{log10(x.x), log10(x.y), log10(x.z)}; + else if constexpr (N == 4) + return vector{log10(x.x), log10(x.y), log10(x.z), log10(x.w)}; +} + +// ---------------------------------------------------------------------------- +// Trigonometry +// ---------------------------------------------------------------------------- + +/// radians +template, bool> = false> +[[nodiscard]] constexpr vector radians(const vector& x) +{ + if constexpr (N == 1) + return vector{radians(x.x)}; + else if constexpr (N == 2) + return vector{radians(x.x), radians(x.y)}; + else if constexpr (N == 3) + return vector{radians(x.x), radians(x.y), radians(x.z)}; + else if constexpr (N == 4) + return vector{radians(x.x), radians(x.y), radians(x.z), radians(x.w)}; +} + +/// degrees +template, bool> = false> +[[nodiscard]] constexpr vector degrees(const vector& x) +{ + if constexpr (N == 1) + return vector{degrees(x.x)}; + else if constexpr (N == 2) + return vector{degrees(x.x), degrees(x.y)}; + else if constexpr (N == 3) + return vector{degrees(x.x), degrees(x.y), degrees(x.z)}; + else if constexpr (N == 4) + return vector{degrees(x.x), degrees(x.y), degrees(x.z), degrees(x.w)}; +} + +/// sin +template, bool> = false> +[[nodiscard]] constexpr vector sin(const vector& x) +{ + if constexpr (N == 1) + return vector{sin(x.x)}; + else if constexpr (N == 2) + return vector{sin(x.x), sin(x.y)}; + else if constexpr (N == 3) + return vector{sin(x.x), sin(x.y), sin(x.z)}; + else if constexpr (N == 4) + return vector{sin(x.x), sin(x.y), sin(x.z), sin(x.w)}; +} + +/// cos +template, bool> = false> +[[nodiscard]] constexpr vector cos(const vector& x) +{ + if constexpr (N == 1) + return vector{cos(x.x)}; + else if constexpr (N == 2) + return vector{cos(x.x), cos(x.y)}; + else if constexpr (N == 3) + return vector{cos(x.x), cos(x.y), cos(x.z)}; + else if constexpr (N == 4) + return vector{cos(x.x), cos(x.y), cos(x.z), cos(x.w)}; +} + +/// tan +template, bool> = false> +[[nodiscard]] constexpr vector tan(const vector& x) +{ + if constexpr (N == 1) + return vector{tan(x.x)}; + else if constexpr (N == 2) + return vector{tan(x.x), tan(x.y)}; + else if constexpr (N == 3) + return vector{tan(x.x), tan(x.y), tan(x.z)}; + else if constexpr (N == 4) + return vector{tan(x.x), tan(x.y), tan(x.z), tan(x.w)}; +} + +/// asin +template, bool> = false> +[[nodiscard]] constexpr vector asin(const vector& x) +{ + if constexpr (N == 1) + return vector{asin(x.x)}; + else if constexpr (N == 2) + return vector{asin(x.x), asin(x.y)}; + else if constexpr (N == 3) + return vector{asin(x.x), asin(x.y), asin(x.z)}; + else if constexpr (N == 4) + return vector{asin(x.x), asin(x.y), asin(x.z), asin(x.w)}; +} + +/// acos +template, bool> = false> +[[nodiscard]] constexpr vector acos(const vector& x) +{ + if constexpr (N == 1) + return vector{acos(x.x)}; + else if constexpr (N == 2) + return vector{acos(x.x), acos(x.y)}; + else if constexpr (N == 3) + return vector{acos(x.x), acos(x.y), acos(x.z)}; + else if constexpr (N == 4) + return vector{acos(x.x), acos(x.y), acos(x.z), acos(x.w)}; +} + +/// atan +template, bool> = false> +[[nodiscard]] constexpr vector atan(const vector& x) +{ + if constexpr (N == 1) + return vector{atan(x.x)}; + else if constexpr (N == 2) + return vector{atan(x.x), atan(x.y)}; + else if constexpr (N == 3) + return vector{atan(x.x), atan(x.y), atan(x.z)}; + else if constexpr (N == 4) + return vector{atan(x.x), atan(x.y), atan(x.z), atan(x.w)}; +} + +/// atan2 +template, bool> = false> +[[nodiscard]] constexpr vector atan2(const vector& y, const vector& x) +{ + if constexpr (N == 1) + return vector{atan2(y.x, x.x)}; + else if constexpr (N == 2) + return vector{atan2(y.x, x.x), atan2(y.y, x.y)}; + else if constexpr (N == 3) + return vector{atan2(y.x, x.x), atan2(y.y, x.y), atan2(y.z, x.z)}; + else if constexpr (N == 4) + return vector{atan2(y.x, x.x), atan2(y.y, x.y), atan2(y.z, x.z), atan2(y.w, x.w)}; +} + +/// sinh +template, bool> = false> +[[nodiscard]] constexpr vector sinh(const vector& x) +{ + if constexpr (N == 1) + return vector{sinh(x.x)}; + else if constexpr (N == 2) + return vector{sinh(x.x), sinh(x.y)}; + else if constexpr (N == 3) + return vector{sinh(x.x), sinh(x.y), sinh(x.z)}; + else if constexpr (N == 4) + return vector{sinh(x.x), sinh(x.y), sinh(x.z), sinh(x.w)}; +} + +/// cosh +template, bool> = false> +[[nodiscard]] constexpr vector cosh(const vector& x) +{ + if constexpr (N == 1) + return vector{cosh(x.x)}; + else if constexpr (N == 2) + return vector{cosh(x.x), cosh(x.y)}; + else if constexpr (N == 3) + return vector{cosh(x.x), cosh(x.y), cosh(x.z)}; + else if constexpr (N == 4) + return vector{cosh(x.x), cosh(x.y), cosh(x.z), cosh(x.w)}; +} + +/// tanh +template, bool> = false> +[[nodiscard]] constexpr vector tanh(const vector& x) +{ + if constexpr (N == 1) + return vector{tanh(x.x)}; + else if constexpr (N == 2) + return vector{tanh(x.x), tanh(x.y)}; + else if constexpr (N == 3) + return vector{tanh(x.x), tanh(x.y), tanh(x.z)}; + else if constexpr (N == 4) + return vector{tanh(x.x), tanh(x.y), tanh(x.z), tanh(x.w)}; +} + +// ---------------------------------------------------------------------------- +// Misc +// ---------------------------------------------------------------------------- + +/// fmod +template, bool> = false> +[[nodiscard]] constexpr vector fmod(const vector& x, const vector& y) +{ + if constexpr (N == 1) + return vector{fmod(x.x, y.x)}; + else if constexpr (N == 2) + return vector{fmod(x.x, y.x), fmod(x.y, y.y)}; + else if constexpr (N == 3) + return vector{fmod(x.x, y.x), fmod(x.y, y.y), fmod(x.z, y.z)}; + else if constexpr (N == 4) + return vector{fmod(x.x, y.x), fmod(x.y, y.y), fmod(x.z, y.z), fmod(x.w, y.w)}; +} + +/// frac +template, bool> = false> +[[nodiscard]] constexpr vector frac(const vector& x) +{ + if constexpr (N == 1) + return vector{frac(x.x)}; + else if constexpr (N == 2) + return vector{frac(x.x), frac(x.y)}; + else if constexpr (N == 3) + return vector{frac(x.x), frac(x.y), frac(x.z)}; + else if constexpr (N == 4) + return vector{frac(x.x), frac(x.y), frac(x.z), frac(x.w)}; +} + +/// lerp +template, bool> = false> +[[nodiscard]] constexpr vector lerp(const vector& x, const vector& y, const vector& s) +{ + if constexpr (N == 1) + return vector{lerp(x.x, y.x, s.x)}; + else if constexpr (N == 2) + return vector{lerp(x.x, y.x, s.x), lerp(x.y, y.y, s.y)}; + else if constexpr (N == 3) + return vector{lerp(x.x, y.x, s.x), lerp(x.y, y.y, s.y), lerp(x.z, y.z, s.z)}; + else if constexpr (N == 4) + return vector{lerp(x.x, y.x, s.x), lerp(x.y, y.y, s.y), lerp(x.z, y.z, s.z), lerp(x.w, y.w, s.w)}; +} + +/// rcp +template, bool> = false> +[[nodiscard]] constexpr vector rcp(const vector& x) +{ + if constexpr (N == 1) + return vector{rcp(x.x)}; + else if constexpr (N == 2) + return vector{rcp(x.x), rcp(x.y)}; + else if constexpr (N == 3) + return vector{rcp(x.x), rcp(x.y), rcp(x.z)}; + else if constexpr (N == 4) + return vector{rcp(x.x), rcp(x.y), rcp(x.z), rcp(x.w)}; +} + +/// saturate +template, bool> = false> +[[nodiscard]] constexpr vector saturate(const vector& x) +{ + if constexpr (N == 1) + return vector{saturate(x.x)}; + else if constexpr (N == 2) + return vector{saturate(x.x), saturate(x.y)}; + else if constexpr (N == 3) + return vector{saturate(x.x), saturate(x.y), saturate(x.z)}; + else if constexpr (N == 4) + return vector{saturate(x.x), saturate(x.y), saturate(x.z), saturate(x.w)}; +} + +/// smoothstep +template, bool> = false> +[[nodiscard]] constexpr vector smoothstep(const vector& min_, const vector& max_, const vector& x) +{ + if constexpr (N == 1) + return vector{smoothstep(min_.x, max_.x, x.x)}; + else if constexpr (N == 2) + return vector{smoothstep(min_.x, max_.x, x.x), smoothstep(min_.y, max_.y, x.y)}; + else if constexpr (N == 3) + return vector{smoothstep(min_.x, max_.x, x.x), smoothstep(min_.y, max_.y, x.y), smoothstep(min_.z, max_.z, x.z)}; + else if constexpr (N == 4) + return vector{smoothstep(min_.x, max_.x, x.x), smoothstep(min_.y, max_.y, x.y), smoothstep(min_.z, max_.z, x.z), smoothstep(min_.w, max_.w, x.w)}; +} + +/// step +template, bool> = false> +[[nodiscard]] constexpr vector step(const vector& x, const vector& y) +{ + if constexpr (N == 1) + return vector{step(x.x, y.x)}; + else if constexpr (N == 2) + return vector{step(x.x, y.x), step(x.y, y.y)}; + else if constexpr (N == 3) + return vector{step(x.x, y.x), step(x.y, y.y), step(x.z, y.z)}; + else if constexpr (N == 4) + return vector{step(x.x, y.x), step(x.y, y.y), step(x.z, y.z), step(x.w, y.w)}; +} + +/* <<>> */ +// clang-format on + +// ---------------------------------------------------------------------------- +// Conversion +// ---------------------------------------------------------------------------- + +[[nodiscard]] inline float2 f16tof32(const uint2& value) noexcept +{ + return float2{f16tof32(value.x), f16tof32(value.y)}; +} + +[[nodiscard]] inline float3 f16tof32(const uint3& value) noexcept +{ + return float3{f16tof32(value.x), f16tof32(value.y), f16tof32(value.z)}; +} + +[[nodiscard]] inline float4 f16tof32(const uint4& value) noexcept +{ + return float4{f16tof32(value.x), f16tof32(value.y), f16tof32(value.z), f16tof32(value.w)}; +} + +[[nodiscard]] inline uint2 f32tof16(const float2& value) noexcept +{ + return uint2{f32tof16(value.x), f32tof16(value.y)}; +} + +[[nodiscard]] inline uint3 f32tof16(const float3& value) noexcept +{ + return uint3{f32tof16(value.x), f32tof16(value.y), f32tof16(value.z)}; +} + +[[nodiscard]] inline uint4 f32tof16(const float4& value) noexcept +{ + return uint4{f32tof16(value.x), f32tof16(value.y), f32tof16(value.z), f32tof16(value.w)}; +} + +// TODO(@skallweit) should we have implicit scalar -> vector conversion? +template, bool> = false> +[[nodiscard]] constexpr vector lerp(const vector& a, const vector& b, const T& s) +{ + if constexpr (N == 1) + return vector{lerp(a.x, b.x, s)}; + else if constexpr (N == 2) + return vector{lerp(a.x, b.x, s), lerp(a.y, b.y, s)}; + else if constexpr (N == 3) + return vector{lerp(a.x, b.x, s), lerp(a.y, b.y, s), lerp(a.z, b.z, s)}; + else if constexpr (N == 4) + return vector{lerp(a.x, b.x, s), lerp(a.y, b.y, s), lerp(a.z, b.z, s), lerp(a.w, b.w, s)}; +} + +/// dot +template +[[nodiscard]] constexpr T dot(const vector& lhs, const vector& rhs) +{ + T result = lhs.x * rhs.x; + if constexpr (N >= 2) + result += lhs.y * rhs.y; + if constexpr (N >= 3) + result += lhs.z * rhs.z; + if constexpr (N >= 4) + result += lhs.w * rhs.w; + return result; +} + +/// cross +template +[[nodiscard]] constexpr vector cross(const vector& lhs, const vector& rhs) +{ + return vector(lhs.y * rhs.z - lhs.z * rhs.y, lhs.z * rhs.x - lhs.x * rhs.z, lhs.x * rhs.y - lhs.y * rhs.x); +} + +/// length +template +[[nodiscard]] constexpr T length(const vector& v) +{ + return sqrt(dot(v, v)); +} + +/// normalize +template +[[nodiscard]] constexpr vector normalize(const vector& v) +{ + return v * rsqrt(dot(v, v)); +} + +/// reflect +template +[[nodiscard]] constexpr vector reflect(const vector& v, const vector& n) +{ + return v - T(2) * dot(v, n) * n; +} + +/// Convert vector to string. +template +[[nodiscard]] std::string to_string(const vector& v) +{ + return ::fmt::format("{}", v); +} + +} // namespace math +} // namespace Falcor + +// Specialize std::less to allow using vectors as key in std::map for example. +template +struct std::less<::Falcor::math::vector> +{ + constexpr bool operator()(const ::Falcor::math::vector& lhs, const ::Falcor::math::vector& rhs) const + { + for (int i = 0; i < N; ++i) + if (lhs[i] != rhs[i]) + return lhs[i] < rhs[i]; + return false; + } +}; + +template +struct std::equal_to<::Falcor::math::vector> +{ + constexpr bool operator()(const ::Falcor::math::vector& lhs, const ::Falcor::math::vector& rhs) const + { + for (int i = 0; i < N; ++i) + if (lhs[i] != rhs[i]) + return false; + return true; + } +}; +template +struct std::not_equal_to<::Falcor::math::vector> +{ + constexpr bool operator()(const ::Falcor::math::vector& lhs, const ::Falcor::math::vector& rhs) const + { + for (int i = 0; i < N; ++i) + if (lhs[i] == rhs[i]) + return false; + return true; + } +}; + +template +struct std::hash<::Falcor::math::vector> +{ + constexpr int operator()(const ::Falcor::math::vector& v) const + { + size_t result = 0; + for (int i = 0; i < N; ++i) + result ^= std::hash()(v[i]) + 0x9e3779b9 + (result << 6) + (result >> 2); + return result; + } +}; + +/// Vector string formatter. +template +struct fmt::formatter<::Falcor::math::vector> : formatter +{ + template + auto format(const ::Falcor::math::vector& v, FormatContext& ctx) const + { + auto out = ctx.out(); + for (int i = 0; i < N; ++i) + { + out = ::fmt::format_to(out, "{}", (i == 0) ? "{" : ", "); + out = formatter::format(v[i], ctx); + } + out = ::fmt::format_to(out, "}}"); + return out; + } +}; diff --git a/Source/Falcor/Utils/Math/VectorSwizzle2.inl.h b/Source/Falcor/Utils/Math/VectorSwizzle2.inl.h new file mode 100644 index 000000000..59d613da2 --- /dev/null +++ b/Source/Falcor/Utils/Math/VectorSwizzle2.inl.h @@ -0,0 +1,42 @@ +/*************************************************************************** + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. + # + # Redistribution and use in source and binary forms, with or without + # modification, are permitted provided that the following conditions + # are met: + # * Redistributions of source code must retain the above copyright + # notice, this list of conditions and the following disclaimer. + # * Redistributions in binary form must reproduce the above copyright + # notice, this list of conditions and the following disclaimer in the + # documentation and/or other materials provided with the distribution. + # * Neither the name of NVIDIA CORPORATION nor the names of its + # contributors may be used to endorse or promote products derived + # from this software without specific prior written permission. + # + # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY + # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + **************************************************************************/ + +// clang-format off +/* <<({components}); }}") +>>> */ +[[nodiscard]] constexpr auto xx() const noexcept { return vector(x, x); } +[[nodiscard]] constexpr auto xy() const noexcept { return vector(x, y); } +[[nodiscard]] constexpr auto yx() const noexcept { return vector(y, x); } +[[nodiscard]] constexpr auto yy() const noexcept { return vector(y, y); } +/* <<>> */ +// clang-format on diff --git a/Source/Falcor/Utils/Math/VectorSwizzle3.inl.h b/Source/Falcor/Utils/Math/VectorSwizzle3.inl.h new file mode 100644 index 000000000..e7c8ab53c --- /dev/null +++ b/Source/Falcor/Utils/Math/VectorSwizzle3.inl.h @@ -0,0 +1,75 @@ +/*************************************************************************** + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. + # + # Redistribution and use in source and binary forms, with or without + # modification, are permitted provided that the following conditions + # are met: + # * Redistributions of source code must retain the above copyright + # notice, this list of conditions and the following disclaimer. + # * Redistributions in binary form must reproduce the above copyright + # notice, this list of conditions and the following disclaimer in the + # documentation and/or other materials provided with the distribution. + # * Neither the name of NVIDIA CORPORATION nor the names of its + # contributors may be used to endorse or promote products derived + # from this software without specific prior written permission. + # + # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY + # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + **************************************************************************/ + +// clang-format off +/* <<({components}); }}") +>>> */ +[[nodiscard]] constexpr auto xx() const noexcept { return vector(x, x); } +[[nodiscard]] constexpr auto xy() const noexcept { return vector(x, y); } +[[nodiscard]] constexpr auto xz() const noexcept { return vector(x, z); } +[[nodiscard]] constexpr auto yx() const noexcept { return vector(y, x); } +[[nodiscard]] constexpr auto yy() const noexcept { return vector(y, y); } +[[nodiscard]] constexpr auto yz() const noexcept { return vector(y, z); } +[[nodiscard]] constexpr auto zx() const noexcept { return vector(z, x); } +[[nodiscard]] constexpr auto zy() const noexcept { return vector(z, y); } +[[nodiscard]] constexpr auto zz() const noexcept { return vector(z, z); } +[[nodiscard]] constexpr auto xxx() const noexcept { return vector(x, x, x); } +[[nodiscard]] constexpr auto xxy() const noexcept { return vector(x, x, y); } +[[nodiscard]] constexpr auto xxz() const noexcept { return vector(x, x, z); } +[[nodiscard]] constexpr auto xyx() const noexcept { return vector(x, y, x); } +[[nodiscard]] constexpr auto xyy() const noexcept { return vector(x, y, y); } +[[nodiscard]] constexpr auto xyz() const noexcept { return vector(x, y, z); } +[[nodiscard]] constexpr auto xzx() const noexcept { return vector(x, z, x); } +[[nodiscard]] constexpr auto xzy() const noexcept { return vector(x, z, y); } +[[nodiscard]] constexpr auto xzz() const noexcept { return vector(x, z, z); } +[[nodiscard]] constexpr auto yxx() const noexcept { return vector(y, x, x); } +[[nodiscard]] constexpr auto yxy() const noexcept { return vector(y, x, y); } +[[nodiscard]] constexpr auto yxz() const noexcept { return vector(y, x, z); } +[[nodiscard]] constexpr auto yyx() const noexcept { return vector(y, y, x); } +[[nodiscard]] constexpr auto yyy() const noexcept { return vector(y, y, y); } +[[nodiscard]] constexpr auto yyz() const noexcept { return vector(y, y, z); } +[[nodiscard]] constexpr auto yzx() const noexcept { return vector(y, z, x); } +[[nodiscard]] constexpr auto yzy() const noexcept { return vector(y, z, y); } +[[nodiscard]] constexpr auto yzz() const noexcept { return vector(y, z, z); } +[[nodiscard]] constexpr auto zxx() const noexcept { return vector(z, x, x); } +[[nodiscard]] constexpr auto zxy() const noexcept { return vector(z, x, y); } +[[nodiscard]] constexpr auto zxz() const noexcept { return vector(z, x, z); } +[[nodiscard]] constexpr auto zyx() const noexcept { return vector(z, y, x); } +[[nodiscard]] constexpr auto zyy() const noexcept { return vector(z, y, y); } +[[nodiscard]] constexpr auto zyz() const noexcept { return vector(z, y, z); } +[[nodiscard]] constexpr auto zzx() const noexcept { return vector(z, z, x); } +[[nodiscard]] constexpr auto zzy() const noexcept { return vector(z, z, y); } +[[nodiscard]] constexpr auto zzz() const noexcept { return vector(z, z, z); } +/* <<>> */ +// clang-format on diff --git a/Source/Falcor/Utils/Math/VectorSwizzle4.inl.h b/Source/Falcor/Utils/Math/VectorSwizzle4.inl.h new file mode 100644 index 000000000..11765e803 --- /dev/null +++ b/Source/Falcor/Utils/Math/VectorSwizzle4.inl.h @@ -0,0 +1,375 @@ +/*************************************************************************** + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. + # + # Redistribution and use in source and binary forms, with or without + # modification, are permitted provided that the following conditions + # are met: + # * Redistributions of source code must retain the above copyright + # notice, this list of conditions and the following disclaimer. + # * Redistributions in binary form must reproduce the above copyright + # notice, this list of conditions and the following disclaimer in the + # documentation and/or other materials provided with the distribution. + # * Neither the name of NVIDIA CORPORATION nor the names of its + # contributors may be used to endorse or promote products derived + # from this software without specific prior written permission. + # + # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY + # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + **************************************************************************/ + +// clang-format off +/* <<({components}); }}") +>>> */ +[[nodiscard]] constexpr auto xx() const noexcept { return vector(x, x); } +[[nodiscard]] constexpr auto xy() const noexcept { return vector(x, y); } +[[nodiscard]] constexpr auto xz() const noexcept { return vector(x, z); } +[[nodiscard]] constexpr auto xw() const noexcept { return vector(x, w); } +[[nodiscard]] constexpr auto yx() const noexcept { return vector(y, x); } +[[nodiscard]] constexpr auto yy() const noexcept { return vector(y, y); } +[[nodiscard]] constexpr auto yz() const noexcept { return vector(y, z); } +[[nodiscard]] constexpr auto yw() const noexcept { return vector(y, w); } +[[nodiscard]] constexpr auto zx() const noexcept { return vector(z, x); } +[[nodiscard]] constexpr auto zy() const noexcept { return vector(z, y); } +[[nodiscard]] constexpr auto zz() const noexcept { return vector(z, z); } +[[nodiscard]] constexpr auto zw() const noexcept { return vector(z, w); } +[[nodiscard]] constexpr auto wx() const noexcept { return vector(w, x); } +[[nodiscard]] constexpr auto wy() const noexcept { return vector(w, y); } +[[nodiscard]] constexpr auto wz() const noexcept { return vector(w, z); } +[[nodiscard]] constexpr auto ww() const noexcept { return vector(w, w); } +[[nodiscard]] constexpr auto xxx() const noexcept { return vector(x, x, x); } +[[nodiscard]] constexpr auto xxy() const noexcept { return vector(x, x, y); } +[[nodiscard]] constexpr auto xxz() const noexcept { return vector(x, x, z); } +[[nodiscard]] constexpr auto xxw() const noexcept { return vector(x, x, w); } +[[nodiscard]] constexpr auto xyx() const noexcept { return vector(x, y, x); } +[[nodiscard]] constexpr auto xyy() const noexcept { return vector(x, y, y); } +[[nodiscard]] constexpr auto xyz() const noexcept { return vector(x, y, z); } +[[nodiscard]] constexpr auto xyw() const noexcept { return vector(x, y, w); } +[[nodiscard]] constexpr auto xzx() const noexcept { return vector(x, z, x); } +[[nodiscard]] constexpr auto xzy() const noexcept { return vector(x, z, y); } +[[nodiscard]] constexpr auto xzz() const noexcept { return vector(x, z, z); } +[[nodiscard]] constexpr auto xzw() const noexcept { return vector(x, z, w); } +[[nodiscard]] constexpr auto xwx() const noexcept { return vector(x, w, x); } +[[nodiscard]] constexpr auto xwy() const noexcept { return vector(x, w, y); } +[[nodiscard]] constexpr auto xwz() const noexcept { return vector(x, w, z); } +[[nodiscard]] constexpr auto xww() const noexcept { return vector(x, w, w); } +[[nodiscard]] constexpr auto yxx() const noexcept { return vector(y, x, x); } +[[nodiscard]] constexpr auto yxy() const noexcept { return vector(y, x, y); } +[[nodiscard]] constexpr auto yxz() const noexcept { return vector(y, x, z); } +[[nodiscard]] constexpr auto yxw() const noexcept { return vector(y, x, w); } +[[nodiscard]] constexpr auto yyx() const noexcept { return vector(y, y, x); } +[[nodiscard]] constexpr auto yyy() const noexcept { return vector(y, y, y); } +[[nodiscard]] constexpr auto yyz() const noexcept { return vector(y, y, z); } +[[nodiscard]] constexpr auto yyw() const noexcept { return vector(y, y, w); } +[[nodiscard]] constexpr auto yzx() const noexcept { return vector(y, z, x); } +[[nodiscard]] constexpr auto yzy() const noexcept { return vector(y, z, y); } +[[nodiscard]] constexpr auto yzz() const noexcept { return vector(y, z, z); } +[[nodiscard]] constexpr auto yzw() const noexcept { return vector(y, z, w); } +[[nodiscard]] constexpr auto ywx() const noexcept { return vector(y, w, x); } +[[nodiscard]] constexpr auto ywy() const noexcept { return vector(y, w, y); } +[[nodiscard]] constexpr auto ywz() const noexcept { return vector(y, w, z); } +[[nodiscard]] constexpr auto yww() const noexcept { return vector(y, w, w); } +[[nodiscard]] constexpr auto zxx() const noexcept { return vector(z, x, x); } +[[nodiscard]] constexpr auto zxy() const noexcept { return vector(z, x, y); } +[[nodiscard]] constexpr auto zxz() const noexcept { return vector(z, x, z); } +[[nodiscard]] constexpr auto zxw() const noexcept { return vector(z, x, w); } +[[nodiscard]] constexpr auto zyx() const noexcept { return vector(z, y, x); } +[[nodiscard]] constexpr auto zyy() const noexcept { return vector(z, y, y); } +[[nodiscard]] constexpr auto zyz() const noexcept { return vector(z, y, z); } +[[nodiscard]] constexpr auto zyw() const noexcept { return vector(z, y, w); } +[[nodiscard]] constexpr auto zzx() const noexcept { return vector(z, z, x); } +[[nodiscard]] constexpr auto zzy() const noexcept { return vector(z, z, y); } +[[nodiscard]] constexpr auto zzz() const noexcept { return vector(z, z, z); } +[[nodiscard]] constexpr auto zzw() const noexcept { return vector(z, z, w); } +[[nodiscard]] constexpr auto zwx() const noexcept { return vector(z, w, x); } +[[nodiscard]] constexpr auto zwy() const noexcept { return vector(z, w, y); } +[[nodiscard]] constexpr auto zwz() const noexcept { return vector(z, w, z); } +[[nodiscard]] constexpr auto zww() const noexcept { return vector(z, w, w); } +[[nodiscard]] constexpr auto wxx() const noexcept { return vector(w, x, x); } +[[nodiscard]] constexpr auto wxy() const noexcept { return vector(w, x, y); } +[[nodiscard]] constexpr auto wxz() const noexcept { return vector(w, x, z); } +[[nodiscard]] constexpr auto wxw() const noexcept { return vector(w, x, w); } +[[nodiscard]] constexpr auto wyx() const noexcept { return vector(w, y, x); } +[[nodiscard]] constexpr auto wyy() const noexcept { return vector(w, y, y); } +[[nodiscard]] constexpr auto wyz() const noexcept { return vector(w, y, z); } +[[nodiscard]] constexpr auto wyw() const noexcept { return vector(w, y, w); } +[[nodiscard]] constexpr auto wzx() const noexcept { return vector(w, z, x); } +[[nodiscard]] constexpr auto wzy() const noexcept { return vector(w, z, y); } +[[nodiscard]] constexpr auto wzz() const noexcept { return vector(w, z, z); } +[[nodiscard]] constexpr auto wzw() const noexcept { return vector(w, z, w); } +[[nodiscard]] constexpr auto wwx() const noexcept { return vector(w, w, x); } +[[nodiscard]] constexpr auto wwy() const noexcept { return vector(w, w, y); } +[[nodiscard]] constexpr auto wwz() const noexcept { return vector(w, w, z); } +[[nodiscard]] constexpr auto www() const noexcept { return vector(w, w, w); } +[[nodiscard]] constexpr auto xxxx() const noexcept { return vector(x, x, x, x); } +[[nodiscard]] constexpr auto xxxy() const noexcept { return vector(x, x, x, y); } +[[nodiscard]] constexpr auto xxxz() const noexcept { return vector(x, x, x, z); } +[[nodiscard]] constexpr auto xxxw() const noexcept { return vector(x, x, x, w); } +[[nodiscard]] constexpr auto xxyx() const noexcept { return vector(x, x, y, x); } +[[nodiscard]] constexpr auto xxyy() const noexcept { return vector(x, x, y, y); } +[[nodiscard]] constexpr auto xxyz() const noexcept { return vector(x, x, y, z); } +[[nodiscard]] constexpr auto xxyw() const noexcept { return vector(x, x, y, w); } +[[nodiscard]] constexpr auto xxzx() const noexcept { return vector(x, x, z, x); } +[[nodiscard]] constexpr auto xxzy() const noexcept { return vector(x, x, z, y); } +[[nodiscard]] constexpr auto xxzz() const noexcept { return vector(x, x, z, z); } +[[nodiscard]] constexpr auto xxzw() const noexcept { return vector(x, x, z, w); } +[[nodiscard]] constexpr auto xxwx() const noexcept { return vector(x, x, w, x); } +[[nodiscard]] constexpr auto xxwy() const noexcept { return vector(x, x, w, y); } +[[nodiscard]] constexpr auto xxwz() const noexcept { return vector(x, x, w, z); } +[[nodiscard]] constexpr auto xxww() const noexcept { return vector(x, x, w, w); } +[[nodiscard]] constexpr auto xyxx() const noexcept { return vector(x, y, x, x); } +[[nodiscard]] constexpr auto xyxy() const noexcept { return vector(x, y, x, y); } +[[nodiscard]] constexpr auto xyxz() const noexcept { return vector(x, y, x, z); } +[[nodiscard]] constexpr auto xyxw() const noexcept { return vector(x, y, x, w); } +[[nodiscard]] constexpr auto xyyx() const noexcept { return vector(x, y, y, x); } +[[nodiscard]] constexpr auto xyyy() const noexcept { return vector(x, y, y, y); } +[[nodiscard]] constexpr auto xyyz() const noexcept { return vector(x, y, y, z); } +[[nodiscard]] constexpr auto xyyw() const noexcept { return vector(x, y, y, w); } +[[nodiscard]] constexpr auto xyzx() const noexcept { return vector(x, y, z, x); } +[[nodiscard]] constexpr auto xyzy() const noexcept { return vector(x, y, z, y); } +[[nodiscard]] constexpr auto xyzz() const noexcept { return vector(x, y, z, z); } +[[nodiscard]] constexpr auto xyzw() const noexcept { return vector(x, y, z, w); } +[[nodiscard]] constexpr auto xywx() const noexcept { return vector(x, y, w, x); } +[[nodiscard]] constexpr auto xywy() const noexcept { return vector(x, y, w, y); } +[[nodiscard]] constexpr auto xywz() const noexcept { return vector(x, y, w, z); } +[[nodiscard]] constexpr auto xyww() const noexcept { return vector(x, y, w, w); } +[[nodiscard]] constexpr auto xzxx() const noexcept { return vector(x, z, x, x); } +[[nodiscard]] constexpr auto xzxy() const noexcept { return vector(x, z, x, y); } +[[nodiscard]] constexpr auto xzxz() const noexcept { return vector(x, z, x, z); } +[[nodiscard]] constexpr auto xzxw() const noexcept { return vector(x, z, x, w); } +[[nodiscard]] constexpr auto xzyx() const noexcept { return vector(x, z, y, x); } +[[nodiscard]] constexpr auto xzyy() const noexcept { return vector(x, z, y, y); } +[[nodiscard]] constexpr auto xzyz() const noexcept { return vector(x, z, y, z); } +[[nodiscard]] constexpr auto xzyw() const noexcept { return vector(x, z, y, w); } +[[nodiscard]] constexpr auto xzzx() const noexcept { return vector(x, z, z, x); } +[[nodiscard]] constexpr auto xzzy() const noexcept { return vector(x, z, z, y); } +[[nodiscard]] constexpr auto xzzz() const noexcept { return vector(x, z, z, z); } +[[nodiscard]] constexpr auto xzzw() const noexcept { return vector(x, z, z, w); } +[[nodiscard]] constexpr auto xzwx() const noexcept { return vector(x, z, w, x); } +[[nodiscard]] constexpr auto xzwy() const noexcept { return vector(x, z, w, y); } +[[nodiscard]] constexpr auto xzwz() const noexcept { return vector(x, z, w, z); } +[[nodiscard]] constexpr auto xzww() const noexcept { return vector(x, z, w, w); } +[[nodiscard]] constexpr auto xwxx() const noexcept { return vector(x, w, x, x); } +[[nodiscard]] constexpr auto xwxy() const noexcept { return vector(x, w, x, y); } +[[nodiscard]] constexpr auto xwxz() const noexcept { return vector(x, w, x, z); } +[[nodiscard]] constexpr auto xwxw() const noexcept { return vector(x, w, x, w); } +[[nodiscard]] constexpr auto xwyx() const noexcept { return vector(x, w, y, x); } +[[nodiscard]] constexpr auto xwyy() const noexcept { return vector(x, w, y, y); } +[[nodiscard]] constexpr auto xwyz() const noexcept { return vector(x, w, y, z); } +[[nodiscard]] constexpr auto xwyw() const noexcept { return vector(x, w, y, w); } +[[nodiscard]] constexpr auto xwzx() const noexcept { return vector(x, w, z, x); } +[[nodiscard]] constexpr auto xwzy() const noexcept { return vector(x, w, z, y); } +[[nodiscard]] constexpr auto xwzz() const noexcept { return vector(x, w, z, z); } +[[nodiscard]] constexpr auto xwzw() const noexcept { return vector(x, w, z, w); } +[[nodiscard]] constexpr auto xwwx() const noexcept { return vector(x, w, w, x); } +[[nodiscard]] constexpr auto xwwy() const noexcept { return vector(x, w, w, y); } +[[nodiscard]] constexpr auto xwwz() const noexcept { return vector(x, w, w, z); } +[[nodiscard]] constexpr auto xwww() const noexcept { return vector(x, w, w, w); } +[[nodiscard]] constexpr auto yxxx() const noexcept { return vector(y, x, x, x); } +[[nodiscard]] constexpr auto yxxy() const noexcept { return vector(y, x, x, y); } +[[nodiscard]] constexpr auto yxxz() const noexcept { return vector(y, x, x, z); } +[[nodiscard]] constexpr auto yxxw() const noexcept { return vector(y, x, x, w); } +[[nodiscard]] constexpr auto yxyx() const noexcept { return vector(y, x, y, x); } +[[nodiscard]] constexpr auto yxyy() const noexcept { return vector(y, x, y, y); } +[[nodiscard]] constexpr auto yxyz() const noexcept { return vector(y, x, y, z); } +[[nodiscard]] constexpr auto yxyw() const noexcept { return vector(y, x, y, w); } +[[nodiscard]] constexpr auto yxzx() const noexcept { return vector(y, x, z, x); } +[[nodiscard]] constexpr auto yxzy() const noexcept { return vector(y, x, z, y); } +[[nodiscard]] constexpr auto yxzz() const noexcept { return vector(y, x, z, z); } +[[nodiscard]] constexpr auto yxzw() const noexcept { return vector(y, x, z, w); } +[[nodiscard]] constexpr auto yxwx() const noexcept { return vector(y, x, w, x); } +[[nodiscard]] constexpr auto yxwy() const noexcept { return vector(y, x, w, y); } +[[nodiscard]] constexpr auto yxwz() const noexcept { return vector(y, x, w, z); } +[[nodiscard]] constexpr auto yxww() const noexcept { return vector(y, x, w, w); } +[[nodiscard]] constexpr auto yyxx() const noexcept { return vector(y, y, x, x); } +[[nodiscard]] constexpr auto yyxy() const noexcept { return vector(y, y, x, y); } +[[nodiscard]] constexpr auto yyxz() const noexcept { return vector(y, y, x, z); } +[[nodiscard]] constexpr auto yyxw() const noexcept { return vector(y, y, x, w); } +[[nodiscard]] constexpr auto yyyx() const noexcept { return vector(y, y, y, x); } +[[nodiscard]] constexpr auto yyyy() const noexcept { return vector(y, y, y, y); } +[[nodiscard]] constexpr auto yyyz() const noexcept { return vector(y, y, y, z); } +[[nodiscard]] constexpr auto yyyw() const noexcept { return vector(y, y, y, w); } +[[nodiscard]] constexpr auto yyzx() const noexcept { return vector(y, y, z, x); } +[[nodiscard]] constexpr auto yyzy() const noexcept { return vector(y, y, z, y); } +[[nodiscard]] constexpr auto yyzz() const noexcept { return vector(y, y, z, z); } +[[nodiscard]] constexpr auto yyzw() const noexcept { return vector(y, y, z, w); } +[[nodiscard]] constexpr auto yywx() const noexcept { return vector(y, y, w, x); } +[[nodiscard]] constexpr auto yywy() const noexcept { return vector(y, y, w, y); } +[[nodiscard]] constexpr auto yywz() const noexcept { return vector(y, y, w, z); } +[[nodiscard]] constexpr auto yyww() const noexcept { return vector(y, y, w, w); } +[[nodiscard]] constexpr auto yzxx() const noexcept { return vector(y, z, x, x); } +[[nodiscard]] constexpr auto yzxy() const noexcept { return vector(y, z, x, y); } +[[nodiscard]] constexpr auto yzxz() const noexcept { return vector(y, z, x, z); } +[[nodiscard]] constexpr auto yzxw() const noexcept { return vector(y, z, x, w); } +[[nodiscard]] constexpr auto yzyx() const noexcept { return vector(y, z, y, x); } +[[nodiscard]] constexpr auto yzyy() const noexcept { return vector(y, z, y, y); } +[[nodiscard]] constexpr auto yzyz() const noexcept { return vector(y, z, y, z); } +[[nodiscard]] constexpr auto yzyw() const noexcept { return vector(y, z, y, w); } +[[nodiscard]] constexpr auto yzzx() const noexcept { return vector(y, z, z, x); } +[[nodiscard]] constexpr auto yzzy() const noexcept { return vector(y, z, z, y); } +[[nodiscard]] constexpr auto yzzz() const noexcept { return vector(y, z, z, z); } +[[nodiscard]] constexpr auto yzzw() const noexcept { return vector(y, z, z, w); } +[[nodiscard]] constexpr auto yzwx() const noexcept { return vector(y, z, w, x); } +[[nodiscard]] constexpr auto yzwy() const noexcept { return vector(y, z, w, y); } +[[nodiscard]] constexpr auto yzwz() const noexcept { return vector(y, z, w, z); } +[[nodiscard]] constexpr auto yzww() const noexcept { return vector(y, z, w, w); } +[[nodiscard]] constexpr auto ywxx() const noexcept { return vector(y, w, x, x); } +[[nodiscard]] constexpr auto ywxy() const noexcept { return vector(y, w, x, y); } +[[nodiscard]] constexpr auto ywxz() const noexcept { return vector(y, w, x, z); } +[[nodiscard]] constexpr auto ywxw() const noexcept { return vector(y, w, x, w); } +[[nodiscard]] constexpr auto ywyx() const noexcept { return vector(y, w, y, x); } +[[nodiscard]] constexpr auto ywyy() const noexcept { return vector(y, w, y, y); } +[[nodiscard]] constexpr auto ywyz() const noexcept { return vector(y, w, y, z); } +[[nodiscard]] constexpr auto ywyw() const noexcept { return vector(y, w, y, w); } +[[nodiscard]] constexpr auto ywzx() const noexcept { return vector(y, w, z, x); } +[[nodiscard]] constexpr auto ywzy() const noexcept { return vector(y, w, z, y); } +[[nodiscard]] constexpr auto ywzz() const noexcept { return vector(y, w, z, z); } +[[nodiscard]] constexpr auto ywzw() const noexcept { return vector(y, w, z, w); } +[[nodiscard]] constexpr auto ywwx() const noexcept { return vector(y, w, w, x); } +[[nodiscard]] constexpr auto ywwy() const noexcept { return vector(y, w, w, y); } +[[nodiscard]] constexpr auto ywwz() const noexcept { return vector(y, w, w, z); } +[[nodiscard]] constexpr auto ywww() const noexcept { return vector(y, w, w, w); } +[[nodiscard]] constexpr auto zxxx() const noexcept { return vector(z, x, x, x); } +[[nodiscard]] constexpr auto zxxy() const noexcept { return vector(z, x, x, y); } +[[nodiscard]] constexpr auto zxxz() const noexcept { return vector(z, x, x, z); } +[[nodiscard]] constexpr auto zxxw() const noexcept { return vector(z, x, x, w); } +[[nodiscard]] constexpr auto zxyx() const noexcept { return vector(z, x, y, x); } +[[nodiscard]] constexpr auto zxyy() const noexcept { return vector(z, x, y, y); } +[[nodiscard]] constexpr auto zxyz() const noexcept { return vector(z, x, y, z); } +[[nodiscard]] constexpr auto zxyw() const noexcept { return vector(z, x, y, w); } +[[nodiscard]] constexpr auto zxzx() const noexcept { return vector(z, x, z, x); } +[[nodiscard]] constexpr auto zxzy() const noexcept { return vector(z, x, z, y); } +[[nodiscard]] constexpr auto zxzz() const noexcept { return vector(z, x, z, z); } +[[nodiscard]] constexpr auto zxzw() const noexcept { return vector(z, x, z, w); } +[[nodiscard]] constexpr auto zxwx() const noexcept { return vector(z, x, w, x); } +[[nodiscard]] constexpr auto zxwy() const noexcept { return vector(z, x, w, y); } +[[nodiscard]] constexpr auto zxwz() const noexcept { return vector(z, x, w, z); } +[[nodiscard]] constexpr auto zxww() const noexcept { return vector(z, x, w, w); } +[[nodiscard]] constexpr auto zyxx() const noexcept { return vector(z, y, x, x); } +[[nodiscard]] constexpr auto zyxy() const noexcept { return vector(z, y, x, y); } +[[nodiscard]] constexpr auto zyxz() const noexcept { return vector(z, y, x, z); } +[[nodiscard]] constexpr auto zyxw() const noexcept { return vector(z, y, x, w); } +[[nodiscard]] constexpr auto zyyx() const noexcept { return vector(z, y, y, x); } +[[nodiscard]] constexpr auto zyyy() const noexcept { return vector(z, y, y, y); } +[[nodiscard]] constexpr auto zyyz() const noexcept { return vector(z, y, y, z); } +[[nodiscard]] constexpr auto zyyw() const noexcept { return vector(z, y, y, w); } +[[nodiscard]] constexpr auto zyzx() const noexcept { return vector(z, y, z, x); } +[[nodiscard]] constexpr auto zyzy() const noexcept { return vector(z, y, z, y); } +[[nodiscard]] constexpr auto zyzz() const noexcept { return vector(z, y, z, z); } +[[nodiscard]] constexpr auto zyzw() const noexcept { return vector(z, y, z, w); } +[[nodiscard]] constexpr auto zywx() const noexcept { return vector(z, y, w, x); } +[[nodiscard]] constexpr auto zywy() const noexcept { return vector(z, y, w, y); } +[[nodiscard]] constexpr auto zywz() const noexcept { return vector(z, y, w, z); } +[[nodiscard]] constexpr auto zyww() const noexcept { return vector(z, y, w, w); } +[[nodiscard]] constexpr auto zzxx() const noexcept { return vector(z, z, x, x); } +[[nodiscard]] constexpr auto zzxy() const noexcept { return vector(z, z, x, y); } +[[nodiscard]] constexpr auto zzxz() const noexcept { return vector(z, z, x, z); } +[[nodiscard]] constexpr auto zzxw() const noexcept { return vector(z, z, x, w); } +[[nodiscard]] constexpr auto zzyx() const noexcept { return vector(z, z, y, x); } +[[nodiscard]] constexpr auto zzyy() const noexcept { return vector(z, z, y, y); } +[[nodiscard]] constexpr auto zzyz() const noexcept { return vector(z, z, y, z); } +[[nodiscard]] constexpr auto zzyw() const noexcept { return vector(z, z, y, w); } +[[nodiscard]] constexpr auto zzzx() const noexcept { return vector(z, z, z, x); } +[[nodiscard]] constexpr auto zzzy() const noexcept { return vector(z, z, z, y); } +[[nodiscard]] constexpr auto zzzz() const noexcept { return vector(z, z, z, z); } +[[nodiscard]] constexpr auto zzzw() const noexcept { return vector(z, z, z, w); } +[[nodiscard]] constexpr auto zzwx() const noexcept { return vector(z, z, w, x); } +[[nodiscard]] constexpr auto zzwy() const noexcept { return vector(z, z, w, y); } +[[nodiscard]] constexpr auto zzwz() const noexcept { return vector(z, z, w, z); } +[[nodiscard]] constexpr auto zzww() const noexcept { return vector(z, z, w, w); } +[[nodiscard]] constexpr auto zwxx() const noexcept { return vector(z, w, x, x); } +[[nodiscard]] constexpr auto zwxy() const noexcept { return vector(z, w, x, y); } +[[nodiscard]] constexpr auto zwxz() const noexcept { return vector(z, w, x, z); } +[[nodiscard]] constexpr auto zwxw() const noexcept { return vector(z, w, x, w); } +[[nodiscard]] constexpr auto zwyx() const noexcept { return vector(z, w, y, x); } +[[nodiscard]] constexpr auto zwyy() const noexcept { return vector(z, w, y, y); } +[[nodiscard]] constexpr auto zwyz() const noexcept { return vector(z, w, y, z); } +[[nodiscard]] constexpr auto zwyw() const noexcept { return vector(z, w, y, w); } +[[nodiscard]] constexpr auto zwzx() const noexcept { return vector(z, w, z, x); } +[[nodiscard]] constexpr auto zwzy() const noexcept { return vector(z, w, z, y); } +[[nodiscard]] constexpr auto zwzz() const noexcept { return vector(z, w, z, z); } +[[nodiscard]] constexpr auto zwzw() const noexcept { return vector(z, w, z, w); } +[[nodiscard]] constexpr auto zwwx() const noexcept { return vector(z, w, w, x); } +[[nodiscard]] constexpr auto zwwy() const noexcept { return vector(z, w, w, y); } +[[nodiscard]] constexpr auto zwwz() const noexcept { return vector(z, w, w, z); } +[[nodiscard]] constexpr auto zwww() const noexcept { return vector(z, w, w, w); } +[[nodiscard]] constexpr auto wxxx() const noexcept { return vector(w, x, x, x); } +[[nodiscard]] constexpr auto wxxy() const noexcept { return vector(w, x, x, y); } +[[nodiscard]] constexpr auto wxxz() const noexcept { return vector(w, x, x, z); } +[[nodiscard]] constexpr auto wxxw() const noexcept { return vector(w, x, x, w); } +[[nodiscard]] constexpr auto wxyx() const noexcept { return vector(w, x, y, x); } +[[nodiscard]] constexpr auto wxyy() const noexcept { return vector(w, x, y, y); } +[[nodiscard]] constexpr auto wxyz() const noexcept { return vector(w, x, y, z); } +[[nodiscard]] constexpr auto wxyw() const noexcept { return vector(w, x, y, w); } +[[nodiscard]] constexpr auto wxzx() const noexcept { return vector(w, x, z, x); } +[[nodiscard]] constexpr auto wxzy() const noexcept { return vector(w, x, z, y); } +[[nodiscard]] constexpr auto wxzz() const noexcept { return vector(w, x, z, z); } +[[nodiscard]] constexpr auto wxzw() const noexcept { return vector(w, x, z, w); } +[[nodiscard]] constexpr auto wxwx() const noexcept { return vector(w, x, w, x); } +[[nodiscard]] constexpr auto wxwy() const noexcept { return vector(w, x, w, y); } +[[nodiscard]] constexpr auto wxwz() const noexcept { return vector(w, x, w, z); } +[[nodiscard]] constexpr auto wxww() const noexcept { return vector(w, x, w, w); } +[[nodiscard]] constexpr auto wyxx() const noexcept { return vector(w, y, x, x); } +[[nodiscard]] constexpr auto wyxy() const noexcept { return vector(w, y, x, y); } +[[nodiscard]] constexpr auto wyxz() const noexcept { return vector(w, y, x, z); } +[[nodiscard]] constexpr auto wyxw() const noexcept { return vector(w, y, x, w); } +[[nodiscard]] constexpr auto wyyx() const noexcept { return vector(w, y, y, x); } +[[nodiscard]] constexpr auto wyyy() const noexcept { return vector(w, y, y, y); } +[[nodiscard]] constexpr auto wyyz() const noexcept { return vector(w, y, y, z); } +[[nodiscard]] constexpr auto wyyw() const noexcept { return vector(w, y, y, w); } +[[nodiscard]] constexpr auto wyzx() const noexcept { return vector(w, y, z, x); } +[[nodiscard]] constexpr auto wyzy() const noexcept { return vector(w, y, z, y); } +[[nodiscard]] constexpr auto wyzz() const noexcept { return vector(w, y, z, z); } +[[nodiscard]] constexpr auto wyzw() const noexcept { return vector(w, y, z, w); } +[[nodiscard]] constexpr auto wywx() const noexcept { return vector(w, y, w, x); } +[[nodiscard]] constexpr auto wywy() const noexcept { return vector(w, y, w, y); } +[[nodiscard]] constexpr auto wywz() const noexcept { return vector(w, y, w, z); } +[[nodiscard]] constexpr auto wyww() const noexcept { return vector(w, y, w, w); } +[[nodiscard]] constexpr auto wzxx() const noexcept { return vector(w, z, x, x); } +[[nodiscard]] constexpr auto wzxy() const noexcept { return vector(w, z, x, y); } +[[nodiscard]] constexpr auto wzxz() const noexcept { return vector(w, z, x, z); } +[[nodiscard]] constexpr auto wzxw() const noexcept { return vector(w, z, x, w); } +[[nodiscard]] constexpr auto wzyx() const noexcept { return vector(w, z, y, x); } +[[nodiscard]] constexpr auto wzyy() const noexcept { return vector(w, z, y, y); } +[[nodiscard]] constexpr auto wzyz() const noexcept { return vector(w, z, y, z); } +[[nodiscard]] constexpr auto wzyw() const noexcept { return vector(w, z, y, w); } +[[nodiscard]] constexpr auto wzzx() const noexcept { return vector(w, z, z, x); } +[[nodiscard]] constexpr auto wzzy() const noexcept { return vector(w, z, z, y); } +[[nodiscard]] constexpr auto wzzz() const noexcept { return vector(w, z, z, z); } +[[nodiscard]] constexpr auto wzzw() const noexcept { return vector(w, z, z, w); } +[[nodiscard]] constexpr auto wzwx() const noexcept { return vector(w, z, w, x); } +[[nodiscard]] constexpr auto wzwy() const noexcept { return vector(w, z, w, y); } +[[nodiscard]] constexpr auto wzwz() const noexcept { return vector(w, z, w, z); } +[[nodiscard]] constexpr auto wzww() const noexcept { return vector(w, z, w, w); } +[[nodiscard]] constexpr auto wwxx() const noexcept { return vector(w, w, x, x); } +[[nodiscard]] constexpr auto wwxy() const noexcept { return vector(w, w, x, y); } +[[nodiscard]] constexpr auto wwxz() const noexcept { return vector(w, w, x, z); } +[[nodiscard]] constexpr auto wwxw() const noexcept { return vector(w, w, x, w); } +[[nodiscard]] constexpr auto wwyx() const noexcept { return vector(w, w, y, x); } +[[nodiscard]] constexpr auto wwyy() const noexcept { return vector(w, w, y, y); } +[[nodiscard]] constexpr auto wwyz() const noexcept { return vector(w, w, y, z); } +[[nodiscard]] constexpr auto wwyw() const noexcept { return vector(w, w, y, w); } +[[nodiscard]] constexpr auto wwzx() const noexcept { return vector(w, w, z, x); } +[[nodiscard]] constexpr auto wwzy() const noexcept { return vector(w, w, z, y); } +[[nodiscard]] constexpr auto wwzz() const noexcept { return vector(w, w, z, z); } +[[nodiscard]] constexpr auto wwzw() const noexcept { return vector(w, w, z, w); } +[[nodiscard]] constexpr auto wwwx() const noexcept { return vector(w, w, w, x); } +[[nodiscard]] constexpr auto wwwy() const noexcept { return vector(w, w, w, y); } +[[nodiscard]] constexpr auto wwwz() const noexcept { return vector(w, w, w, z); } +[[nodiscard]] constexpr auto wwww() const noexcept { return vector(w, w, w, w); } +/* <<>> */ +// clang-format on diff --git a/Source/Falcor/Utils/Math/VectorTypes.h b/Source/Falcor/Utils/Math/VectorTypes.h new file mode 100644 index 000000000..bce12df2c --- /dev/null +++ b/Source/Falcor/Utils/Math/VectorTypes.h @@ -0,0 +1,294 @@ +/*************************************************************************** + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. + # + # Redistribution and use in source and binary forms, with or without + # modification, are permitted provided that the following conditions + # are met: + # * Redistributions of source code must retain the above copyright + # notice, this list of conditions and the following disclaimer. + # * Redistributions in binary form must reproduce the above copyright + # notice, this list of conditions and the following disclaimer in the + # documentation and/or other materials provided with the distribution. + # * Neither the name of NVIDIA CORPORATION nor the names of its + # contributors may be used to endorse or promote products derived + # from this software without specific prior written permission. + # + # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY + # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + # (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 "ScalarTypes.h" + +namespace Falcor +{ +namespace math +{ + +// ---------------------------------------------------------------------------- +// Vector types +// ---------------------------------------------------------------------------- + +/** + * Vector type. + * + * The semantics are aligned with Slang: + * - Math operators are element-wise (e.g. +, -, *, /) + * - Free standing functions for vector operations (e.g. dot(), cross(), etc.) + * + * @tparam T Scalar type + * @tparam N Number of elements (1-4) + */ +template +struct vector; + +template +struct vector +{ + static constexpr int dimension = 1; + using value_type = T; + + union + { + T x; + T r; + T s; + }; + // clang-format off + /// Default constructor. + constexpr vector() noexcept = default; + /// Copy constructor. + constexpr vector(const vector& other) noexcept = default; + /// Explicit basic constructor. + explicit constexpr vector(T x) noexcept : x{x} {} + /// Explicit basic constructor (scalar). + template + explicit constexpr vector(U x) noexcept : x{T(x)} {} + // clang-format on + + template + constexpr vector(const vector& other) noexcept : x{T(other.x)} {}; + + [[nodiscard]] constexpr T& operator[](int index) noexcept { return (&(this->x))[index]; } + [[nodiscard]] constexpr const T& operator[](int index) const noexcept { return (&(this->x))[index]; } + + [[nodiscard]] static constexpr int length() noexcept { return dimension; } +}; + +template +struct vector +{ + static constexpr int dimension = 2; + using value_type = T; + + union + { + struct + { + T x, y; + }; + struct + { + T r, g; + }; + struct + { + T s, t; + }; + }; + // clang-format off + /// Default constructor. + constexpr vector() noexcept = default; + /// Copy constructor. + constexpr vector(const vector& other) noexcept = default; + /// Explicit basic constructor. + constexpr vector(T x, T y) noexcept : x{x}, y{y} {} + /// Explicit basic constructor (scalar). + explicit constexpr vector(T scalar) noexcept : x{scalar}, y{scalar} {} + /// Explicit basic constructor (scalar). + template + explicit constexpr vector(U scalar) noexcept : x{T(scalar)}, y{T(scalar)} {} + + template + constexpr vector(X x, Y y) noexcept : x{T(x)}, y{T(y)} {} + // clang-format on + + template + constexpr vector(const vector& other) noexcept : x{T(other.x)}, y{T(other.y)} {}; + + [[nodiscard]] constexpr T& operator[](int index) noexcept { return (&(this->x))[index]; } + [[nodiscard]] constexpr const T& operator[](int index) const noexcept { return (&(this->x))[index]; } + + [[nodiscard]] static constexpr int length() noexcept { return dimension; } + +#include "VectorSwizzle2.inl.h" +}; + +template +struct vector +{ + static constexpr int dimension = 3; + using value_type = T; + + union + { + struct + { + T x, y, z; + }; + struct + { + T r, g, b; + }; + struct + { + T s, t, p; + }; + }; + // clang-format off + /// Default constructor. + constexpr vector() noexcept = default; + /// Copy constructor. + constexpr vector(const vector& other) noexcept = default; + /// Explicit basic constructor. + constexpr vector(T x, T y, T z) noexcept : x{x}, y{y}, z{z} {} + /// Explicit basic constructor (scalar). + explicit constexpr vector(T scalar) noexcept : x{scalar}, y{scalar}, z{scalar} {} + /// Explicit basic constructor (scalar). + template + explicit constexpr vector(U scalar) noexcept : x{T(scalar)}, y{T(scalar)}, z{T(scalar)} {} + + template + constexpr vector(X x, Y y, Z z) noexcept : x{T(x)}, y{T(y)}, z{T(z)} {} + template + constexpr vector(vector xy, Z z) noexcept : x{T(xy.x)}, y{T(xy.y)}, z{T(z)} {} + template + constexpr vector(X x, vector yz) noexcept : x{T(x)}, y{T(yz.x)}, z{T(yz.y)} {} + // clang-format on + + template + constexpr vector(const vector& other) noexcept : x{T(other.x)}, y{T(other.y)}, z{T(other.z)} {}; + + [[nodiscard]] constexpr T& operator[](int index) noexcept { return (&(this->x))[index]; } + [[nodiscard]] constexpr const T& operator[](int index) const noexcept { return (&(this->x))[index]; } + + [[nodiscard]] static constexpr int length() noexcept { return dimension; } + +#include "VectorSwizzle3.inl.h" +}; + +template +struct vector +{ + static constexpr int dimension = 4; + using value_type = T; + + union + { + struct + { + T x, y, z, w; + }; + struct + { + T r, g, b, a; + }; + struct + { + T s, t, p, q; + }; + }; + // clang-format off + /// Default constructor. + constexpr vector() noexcept = default; + /// Copy constructor. + constexpr vector(const vector& other) noexcept = default; + /// Explicit basic constructor. + constexpr vector(T x, T y, T z, T w) noexcept : x{x}, y{y}, z{z}, w{w} {} + /// Explicit basic constructor (scalar). + explicit constexpr vector(T scalar) noexcept : x{scalar}, y{scalar}, z{scalar}, w{scalar} {} + /// Explicit basic constructor (scalar). + template + explicit constexpr vector(U scalar) noexcept : x{T(scalar)}, y{T(scalar)}, z{T(scalar)}, w{T(scalar)} {} + + template + constexpr vector(X x, Y y, Z z, W w) noexcept : x{T(x)}, y{T(y)}, z{T(z)}, w{T(w)} {} + template + constexpr vector(vector xy, Z z, W w) noexcept : x{T(xy.x)}, y{T(xy.y)}, z{T(z)}, w{T(w)} {} + template + constexpr vector(X x, vector yz, W w) noexcept : x{T(x)}, y{T(yz.x)}, z{T(yz.y)}, w{T(w)} {} + template + constexpr vector(X x, Y y, vector zw) noexcept : x{T(x)}, y{T(y)}, z{T(zw.x)}, w{T(zw.y)} {} + template + constexpr vector(vector xy, vector zw) noexcept : x{T(xy.x)}, y{T(xy.y)}, z{T(zw.x)}, w{T(zw.y)} {} + template + constexpr vector(vector xyz, W w) noexcept : x{T(xyz.x)}, y{T(xyz.y)}, z{T(xyz.z)}, w{T(w)} {} + template + constexpr vector(X x, vector yzw) noexcept : x{T(x)}, y{T(yzw.x)}, z{T(yzw.y)}, w{T(yzw.z)} {} + // clang-format on + + template + constexpr vector(const vector& other) noexcept : x{T(other.x)}, y{T(other.y)}, z{T(other.z)}, w{T(other.w)} {}; + + [[nodiscard]] constexpr T& operator[](int index) noexcept { return (&(this->x))[index]; } + [[nodiscard]] constexpr const T& operator[](int index) const noexcept { return (&(this->x))[index]; } + + [[nodiscard]] static constexpr int length() noexcept { return dimension; } + +#include "VectorSwizzle4.inl.h" +}; + +using bool1 = vector; +using bool2 = vector; +using bool3 = vector; +using bool4 = vector; +using int1 = vector; +using int2 = vector; +using int3 = vector; +using int4 = vector; +using uint1 = vector; +using uint2 = vector; +using uint3 = vector; +using uint4 = vector; +using float1 = vector; +using float2 = vector; +using float3 = vector; +using float4 = vector; +using float16_t1 = vector; +using float16_t2 = vector; +using float16_t3 = vector; +using float16_t4 = vector; + +} // namespace math + +using bool1 = math::bool1; +using bool2 = math::bool2; +using bool3 = math::bool3; +using bool4 = math::bool4; +using int1 = math::int1; +using int2 = math::int2; +using int3 = math::int3; +using int4 = math::int4; +using uint1 = math::uint1; +using uint2 = math::uint2; +using uint3 = math::uint3; +using uint4 = math::uint4; +using float1 = math::float1; +using float2 = math::float2; +using float3 = math::float3; +using float4 = math::float4; +using float16_t1 = math::float16_t1; +using float16_t2 = math::float16_t2; +using float16_t3 = math::float16_t3; +using float16_t4 = math::float16_t4; + +} // namespace Falcor diff --git a/Source/Falcor/Utils/NumericRange.h b/Source/Falcor/Utils/NumericRange.h index dee63be87..404ab9feb 100644 --- a/Source/Falcor/Utils/NumericRange.h +++ b/Source/Falcor/Utils/NumericRange.h @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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,47 +32,53 @@ namespace Falcor { - template - class NumericRange final {}; +template +class NumericRange final +{}; - /** Numeric range that can be iterated over. - Should be replaced with C++20 std::views::iota when available. - */ - template - class NumericRange::value>::type> final +/** + * Numeric range that can be iterated over. + * Should be replaced with C++20 std::views::iota when available. + */ +template +class NumericRange::value>::type> final +{ +public: + class Iterator { public: - class Iterator - { - public: - using iterator_category = std::forward_iterator_tag; - using value_type = T; - using difference_type = T; - using pointer = const T*; - using reference = T; + using iterator_category = std::forward_iterator_tag; + using value_type = T; + using difference_type = T; + using pointer = const T*; + using reference = T; - explicit Iterator(const T& value = T(0)) : mValue(value) {} - const Iterator& operator++() { ++mValue; return *this; } - bool operator!=(const Iterator& other) const { return other.mValue != mValue; } - T operator*() const { return mValue; } - private: - T mValue; - }; - - explicit NumericRange(const T& begin, const T& end) - : mBegin(begin) - , mEnd(end) + explicit Iterator(const T& value = T(0)) : mValue(value) {} + const Iterator& operator++() { - if (begin > end) throw ArgumentError("Invalid range"); + ++mValue; + return *this; } - NumericRange() = delete; - NumericRange(const NumericRange&) = delete; - NumericRange(NumericRange&& other) = delete; - - Iterator begin() const { return Iterator(mBegin); } - Iterator end() const { return Iterator(mEnd); } + bool operator!=(const Iterator& other) const { return other.mValue != mValue; } + T operator*() const { return mValue; } private: - T mBegin, mEnd; + T mValue; }; + + explicit NumericRange(const T& begin, const T& end) : mBegin(begin), mEnd(end) + { + if (begin > end) + throw ArgumentError("Invalid range"); + } + NumericRange() = delete; + NumericRange(const NumericRange&) = delete; + NumericRange(NumericRange&& other) = delete; + + Iterator begin() const { return Iterator(mBegin); } + Iterator end() const { return Iterator(mEnd); } + +private: + T mBegin, mEnd; }; +}; // namespace Falcor diff --git a/Source/Falcor/Utils/ObjectID.h b/Source/Falcor/Utils/ObjectID.h index f2501b017..59a2d9de5 100644 --- a/Source/Falcor/Utils/ObjectID.h +++ b/Source/Falcor/Utils/ObjectID.h @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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 "Core/Assert.h" #include -#include #include #include @@ -37,15 +36,16 @@ namespace Falcor { -/** Universal class for strongly typed IDs. Takes an TKindEnum to allow usage for IDs in unrelated - subsystems without polluting a single enum with unrelated kinds. - TODO: Ideally it would also take name of the ID for python binding purposes, but passing - string as a template argument is non-trivial in C++17. - - \param TKindEnum Enum class from which kinds are drawn. Different enum classes are not directly convertible. - \param TKind Kind of the ID. - \param TIntType the underlying numeric type. It is advised that it should be the same for all TKinds in the same enum. - */ +/** + * Universal class for strongly typed IDs. Takes an TKindEnum to allow usage for IDs in unrelated + * subsystems without polluting a single enum with unrelated kinds. + * TODO: Ideally it would also take name of the ID for python binding purposes, but passing + * string as a template argument is non-trivial in C++17. + * + * @param TKindEnum Enum class from which kinds are drawn. Different enum classes are not directly convertible. + * @param TKind Kind of the ID. + * @param TIntType the underlying numeric type. It is advised that it should be the same for all TKinds in the same enum. + */ template class ObjectID { @@ -55,21 +55,20 @@ class ObjectID static constexpr IntType kInvalidID = std::numeric_limits::max(); public: - /** Default construction creates an invalid ID. - TODO: Consider creating uninitialized ID instead, if vectors of IDs become a performance issue. - */ - ObjectID() - : mID(kInvalidID) - {} - - /** Constructs ObjectID from any numeric type. - Checks for validity of the ID with respect to th allowed range. - - \param[in] id Integer ID to initialize from. - */ + /** + * Default construction creates an invalid ID. + * TODO: Consider creating uninitialized ID instead, if vectors of IDs become a performance issue. + */ + ObjectID() : mID(kInvalidID) {} + + /** + * Constructs ObjectID from any numeric type. + * Checks for validity of the ID with respect to th allowed range. + * + * @param[in] id Integer ID to initialize from. + */ template - explicit ObjectID(const T& id, std::enable_if_t, bool> = true) - : mID(IntType(id)) + explicit ObjectID(const T& id, std::enable_if_t, bool> = true) : mID(IntType(id)) { // First we make sure it is positive FALCOR_ASSERT_GE(id, T(0)); @@ -79,107 +78,83 @@ class ObjectID FALCOR_ASSERT_LE(std::make_unsigned_t(id), std::numeric_limits::max()); } - /** Allows converting between different Kinds of the same EnumKind. - This is slightly safer than going straight through numeric ids via get(). - This is mostly used when converting from an "union" ID, that can identify different - objects based on other flags, e.g., kCurveOrMesh that is either Curve or Mesh, - based on the tessellation flags. - NB: Ideally this would be removed, use as sparingly as possible. - - \param[in] other The ObjectID to be converted from. - */ + /** + * Allows converting between different Kinds of the same EnumKind. + * This is slightly safer than going straight through numeric ids via get(). + * This is mostly used when converting from an "union" ID, that can identify different + * objects based on other flags, e.g., kCurveOrMesh that is either Curve or Mesh, + * based on the tessellation flags. + * NB: Ideally this would be removed, use as sparingly as possible. + * + * @param[in] other The ObjectID to be converted from. + */ template - explicit ObjectID( const ObjectID& other ) + explicit ObjectID(const ObjectID& other) { mID = other.get(); } - /** A helper method when convering from an numeric ID in Slang, to the strongly typed CPU ID. - This is separate from the a basic constructor only for the purpose of clearly identifying, - the conversion in the code, as per Joel's "Making Wrong Code Look Wrong" principle. - TODO: Remove once the slang side also has strongly typed IDs. - - \param[in] id Integer ID to initialize from - \return The ObjectID created from a numeric ID - */ + /** + * A helper method when convering from an numeric ID in Slang, to the strongly typed CPU ID. + * This is separate from the a basic constructor only for the purpose of clearly identifying, + * the conversion in the code, as per Joel's "Making Wrong Code Look Wrong" principle. + * TODO: Remove once the slang side also has strongly typed IDs. + * + * @param[in] id Integer ID to initialize from + * @return The ObjectID created from a numeric ID + */ template static ObjectID fromSlang(const T& id, std::enable_if_t, bool> = true) { - return ObjectID{ id }; + return ObjectID{id}; } - /** Provides an invalid ID for comparison purposes. - In the future, most uses would be replaced by either isValid (for comparison), - or by ObjectID(ObjectID::kInvalidID) (for obtaining an invalid ID) + /** + * Provides an invalid ID for comparison purposes. + * In the future, most uses would be replaced by either isValid (for comparison), + * or by ObjectID(ObjectID::kInvalidID) (for obtaining an invalid ID) + * + * @return An invalid ObjectID. + */ + static ObjectID Invalid() { return ObjectID(); } - \return An invalid ObjectID. - */ - static ObjectID Invalid() - { - return ObjectID(); - } + /** + * Returns true when the ID is valid, i.e., get() != kInvalidID + * + * @return True when valid. + */ + bool isValid() const { return mID != kInvalidID; } - /** Returns true when the ID is valid, i.e., get() != kInvalidID + /** + * Return the numeric value of the ID. + * Should be used rather sparingly, e.g., consider allowing objects to be indexed by the strongly + * typed ID, instead of just a number. + * NB: Consider using getters with strongly typed IDs, rather than directly accessing even vectors/buffers. + * + * @return Numeric value of the ID, can be kInvalidID. + */ + IntType get() const { return mID; } - \return True when valid. - */ - bool isValid() const - { - return mID != kInvalidID; - } + /** + * A helped method to convert to numeric ID in Slang. Functionally identical to get(), + * but in the future it should be removed, and the Slang should have a compatible and checked + * strongly typed ID as well. Separated from get() to clearly show all such locations. + * + * @return Numeric value of the ID, can be kInvalidID. + */ + IntType getSlang() const { return get(); } - /** Return the numeric value of the ID. - Should be used rather sparingly, e.g., consider allowing objects to be indexed by the strongly - typed ID, instead of just a number. - NB: Consider using getters with strongly typed IDs, rather than directly accessing even vectors/buffers. - - \return Numeric value of the ID, can be kInvalidID. - */ - IntType get() const - { - return mID; - } + bool operator==(const ObjectID& rhs) const { return mID == rhs.mID; } - /** A helped method to convert to numeric ID in Slang. Functionally identical to get(), - but in the future it should be removed, and the Slang should have a compatible and checked - strongly typed ID as well. Separated from get() to clearly show all such locations. + bool operator!=(const ObjectID& rhs) const { return mID != rhs.mID; } - \return Numeric value of the ID, can be kInvalidID. - */ - IntType getSlang() const - { - return get(); - } + bool operator<=(const ObjectID& rhs) const { return mID <= rhs.mID; } - bool operator==(const ObjectID& rhs) const - { - return mID == rhs.mID; - } + bool operator>=(const ObjectID& rhs) const { return mID >= rhs.mID; } - bool operator!=(const ObjectID& rhs) const - { - return mID != rhs.mID; - } + bool operator<(const ObjectID& rhs) const { return mID < rhs.mID; } - bool operator<=(const ObjectID& rhs) const - { - return mID <= rhs.mID; - } - - bool operator>=(const ObjectID& rhs) const - { - return mID >= rhs.mID; - } - - bool operator<(const ObjectID& rhs) const - { - return mID < rhs.mID; - } - - bool operator>(const ObjectID& rhs) const - { - return mID < rhs.mID; - } + bool operator>(const ObjectID& rhs) const { return mID < rhs.mID; } ObjectID& operator++() { @@ -188,10 +163,8 @@ class ObjectID return *this; } - ObjectID operator++(int) - { - return ObjectID(mID++); - } + ObjectID operator++(int) { return ObjectID(mID++); } + private: IntType mID; }; @@ -202,16 +175,13 @@ inline std::string to_string(const ObjectID& v) return std::to_string(v.get()); } -} +} // namespace Falcor template struct std::hash> { using ObjectID = Falcor::ObjectID; - std::size_t operator()(const ObjectID& id) const noexcept - { - return std::hash{}(id.get()); - } + std::size_t operator()(const ObjectID& id) const noexcept { return std::hash{}(id.get()); } }; template @@ -231,33 +201,3 @@ struct fmt::formatter> return fmt::format_to(ctx.out(), "{0}", id.get()); } }; - -namespace pybind11::detail -{ - template - struct type_caster> - { - using ObjectID = Falcor::ObjectID; - public: - PYBIND11_TYPE_CASTER(ObjectID, const_name("ObjectID")); - - bool load(handle src, bool) - { - PyObject* source = src.ptr(); - PyObject* tmp = PyNumber_Long(source); - if (!tmp) - return false; - - typename ObjectID::IntType idValue = PyLong_AsUnsignedLong(tmp); - Py_DECREF(tmp); - - value = (idValue == ObjectID::kInvalidID) ? ObjectID() : ObjectID(idValue); - return !PyErr_Occurred(); - } - - static handle cast(const ObjectID& src, return_value_policy /* policy */, handle /* parent */) - { - return PyLong_FromUnsignedLong(src.get()); - } - }; -} diff --git a/Source/Falcor/Scene/SceneBuilderAccess.h b/Source/Falcor/Utils/ObjectIDPython.h similarity index 64% rename from Source/Falcor/Scene/SceneBuilderAccess.h rename to Source/Falcor/Utils/ObjectIDPython.h index 36c413960..f3282c6bc 100644 --- a/Source/Falcor/Scene/SceneBuilderAccess.h +++ b/Source/Falcor/Utils/ObjectIDPython.h @@ -27,20 +27,36 @@ **************************************************************************/ #pragma once -#include "Core/Macros.h" -#include "SceneBuilder.h" +#include "ObjectID.h" +#include -namespace Falcor +namespace pybind11::detail { +template +struct type_caster> +{ + using ObjectID = Falcor::ObjectID; + +public: + PYBIND11_TYPE_CASTER(ObjectID, const_name("ObjectID")); + + bool load(handle src, bool) + { + PyObject* source = src.ptr(); + PyObject* tmp = PyNumber_Long(source); + if (!tmp) + return false; -/// This file is a temporary workaround to give access to the curretly active scene builder when loading a `.pyscene` file. -/// The python bindings for creating scene objects like materials, SDFs, volume grids etc. was designed in a way where -/// users can just construct these objects out of thin air. However, the C++ side now needs to pass a GPU device in -/// most of those constructors. In order to not break existing `.pyscene` files, we provide the currently active scene -/// builder (and through that the GPU device) to the python binding wrappers. In the long run, all of these objects should -/// be created through factories, at which point we can remove this file and cleanup every place where it's included. + typename ObjectID::IntType idValue = PyLong_AsUnsignedLong(tmp); + Py_DECREF(tmp); -FALCOR_API void setActivePythonSceneBuilder(SceneBuilder* pSceneBuilder); -FALCOR_API SceneBuilder& getActivePythonSceneBuilder(); + value = (idValue == ObjectID::kInvalidID) ? ObjectID() : ObjectID(idValue); + return !PyErr_Occurred(); + } -} // namespace Falcor + static handle cast(const ObjectID& src, return_value_policy /* policy */, handle /* parent */) + { + return PyLong_FromUnsignedLong(src.get()); + } +}; +} // namespace pybind11::detail diff --git a/Source/Falcor/Utils/PathResolving.cpp b/Source/Falcor/Utils/PathResolving.cpp index 62ee955ac..08092ec69 100644 --- a/Source/Falcor/Utils/PathResolving.cpp +++ b/Source/Falcor/Utils/PathResolving.cpp @@ -38,7 +38,6 @@ bool resolveEnvVariables(std::string& str, EnvVarResolver envResolver, std::stri { FALCOR_ASSERT(envResolver); - std::string::size_type end = 0; std::string::size_type begin = str.find_first_of(beginToken); if (begin == std::string::npos) return true; diff --git a/Source/Falcor/Utils/SDF/SDF2DDraw.slang b/Source/Falcor/Utils/SDF/SDF2DDraw.slang index 3e91eeb99..28fd4ca9e 100644 --- a/Source/Falcor/Utils/SDF/SDF2DDraw.slang +++ b/Source/Falcor/Utils/SDF/SDF2DDraw.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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,13 +30,14 @@ import Utils.SDF.SDF2DPrimitives; import Utils.Math.MathHelpers; -/** Compute a 2x3 matrix to be used for scaling, rotating, and translating an SDF primitive. The scale is applied first, then rotation, and finally translation. - The inverse is returned, which can then be applied directly to the querying point. - \param[in] scale The scale of the transform. - \param[in] rotationAngle Rotation angle of the transform. - \param[in] translation Translation of the transform. - \return Inverse 2x3 transform matrix. -*/ +/** + * Compute a 2x3 matrix to be used for scaling, rotating, and translating an SDF primitive. The scale is applied first, then rotation, and + * finally translation. The inverse is returned, which can then be applied directly to the querying point. + * @param[in] scale The scale of the transform. + * @param[in] rotationAngle Rotation angle of the transform. + * @param[in] translation Translation of the transform. + * @return Inverse 2x3 transform matrix. + */ float2x3 sdfComputeTransform(float2 scale, float rotationAngle, float2 translation) { float ca = cos(rotationAngle); @@ -45,31 +46,33 @@ float2x3 sdfComputeTransform(float2 scale, float rotationAngle, float2 translati return inverse(M); } -/** Applies a scale, rotation, and translation to a point. See sdfComputeTransform() above. - \param[in] p The 2D point to be transformed. - \param[in] scale The scale of the transform. - \param[in] rotationAngle Rotation angle of the transform. - \param[in] translation Translation of the transform. - \return Transformed point. -*/ +/** + * Applies a scale, rotation, and translation to a point. See sdfComputeTransform() above. + * @param[in] p The 2D point to be transformed. + * @param[in] scale The scale of the transform. + * @param[in] rotationAngle Rotation angle of the transform. + * @param[in] translation Translation of the transform. + * @return Transformed point. + */ float2 sdfTransform(float2 p, float2 scale, float rotationAngle, float2 translation) { float2x3 inverseTransform = sdfComputeTransform(scale, rotationAngle, translation); return mul(inverseTransform, float3(p, 1.0f)).xy; } -/** Filler function that may be used by signed distance functions. Returns alpha so you can blend as you wish. - Based on the JCGT article "Antialiased 2D Grid, Marker, and Arrow Shaders" by Rougier. - http://jcgt.org/published/0003/04/01/ - \param[in] signedDistance The signed distance to the primitive. - \param[in] lineWidth Line width. - \param[in] antialiasWidth Antialias with. - \return Alpha. -*/ +/** + * Filler function that may be used by signed distance functions. Returns alpha so you can blend as you wish. + * Based on the JCGT article "Antialiased 2D Grid, Marker, and Arrow Shaders" by Rougier. + * http://jcgt.org/published/0003/04/01/ + * @param[in] signedDistance The signed distance to the primitive. + * @param[in] lineWidth Line width. + * @param[in] antialiasWidth Antialias with. + * @return Alpha. + */ float sdfFilledAlpha(float signedDistance, float lineWidth, float antialiasWidth) { float borderDistance = abs(signedDistance) - antialiasWidth; - float alpha = abs(borderDistance / antialiasWidth); // 0.0 at the outer border and 1.0 close to full fill + float alpha = abs(borderDistance / antialiasWidth); // 0.0 at the outer border and 1.0 close to full fill alpha = smoothstep(0.0f, 1.0f, alpha); if (signedDistance < 0.0f) @@ -86,32 +89,42 @@ float sdfFilledAlpha(float signedDistance, float lineWidth, float antialiasWidth } } -/** Filler function that may be used by signed distance functions. Returns color with alpha. - \param[in] signedDistance The signed distance to the primitive. - \param[in] lineWidth Line width. - \param[in] antialiasWidth Antialias with. - \param[in] fillColor Color of the primitive. - \return Color. -*/ +/** + * Filler function that may be used by signed distance functions. Returns color with alpha. + * @param[in] signedDistance The signed distance to the primitive. + * @param[in] lineWidth Line width. + * @param[in] antialiasWidth Antialias with. + * @param[in] fillColor Color of the primitive. + * @return Color. + */ float4 sdfFilled(float signedDistance, float lineWidth, float antialiasWidth, float4 fillColor) { float alpha = sdfFilledAlpha(signedDistance, lineWidth, antialiasWidth); return float4(fillColor.rgb, alpha * fillColor.a); } -/** Draw function that blends the computed SDF primitive color with the background color. Transforms the primitive prior to evaluation. - \param[in] sdf The SDF primitive. - \param[in] p The 2D point where the SDF is to be evaluated.. - \param[in] scale The scale of the transform. - \param[in] rotationAngle Rotation angle of the transform. - \param[in] translation Translation of the transform. - \param[in] primitiveColor The color of the SDF primitive. - \param[in] backgroundColor Incoming background color. - \param[in] blendRadius Blend radius for antialiasing. - \return Color. -*/ -float4 sdfDraw(const SDF sdf, float2 p, float2 scale, float rotationAngle, float2 translation, - float4 primitiveColor, float4 backgroundColor, float blendRadius = 1.f) +/** + * Draw function that blends the computed SDF primitive color with the background color. Transforms the primitive prior to evaluation. + * @param[in] sdf The SDF primitive. + * @param[in] p The 2D point where the SDF is to be evaluated.. + * @param[in] scale The scale of the transform. + * @param[in] rotationAngle Rotation angle of the transform. + * @param[in] translation Translation of the transform. + * @param[in] primitiveColor The color of the SDF primitive. + * @param[in] backgroundColor Incoming background color. + * @param[in] blendRadius Blend radius for antialiasing. + * @return Color. + */ +float4 sdfDraw( + const SDF sdf, + float2 p, + float2 scale, + float rotationAngle, + float2 translation, + float4 primitiveColor, + float4 backgroundColor, + float blendRadius = 1.f +) { p = sdfTransform(p, scale, rotationAngle, translation); float signedDistance = sdf.eval(p); @@ -119,14 +132,15 @@ float4 sdfDraw(const SDF sdf, float2 p, float2 scale, float rotati return lerp(backgroundColor, color, color.a); } -/** Draw function that blends the computed SDF primitive color with the background color. - \param[in] sdf The SDF primitive. - \param[in] p The 2D point where the SDF is to be evaluated.. - \param[in] primitiveColor The color of the SDF primitive. - \param[in] backgroundColor Incoming background color. - \param[in] blendRadius Blend radius for antialiasing. - \return Color. -*/ +/** + * Draw function that blends the computed SDF primitive color with the background color. + * @param[in] sdf The SDF primitive. + * @param[in] p The 2D point where the SDF is to be evaluated.. + * @param[in] primitiveColor The color of the SDF primitive. + * @param[in] backgroundColor Incoming background color. + * @param[in] blendRadius Blend radius for antialiasing. + * @return Color. + */ float4 sdfDraw(const SDF sdf, float2 p, float4 primitiveColor, float4 backgroundColor, float blendRadius = 1.f) { return sdfDraw(sdf, p, float2(1.0f), 0.0f, float2(0.0f), primitiveColor, backgroundColor, blendRadius); diff --git a/Source/Falcor/Utils/SDF/SDF2DPrimitives.slang b/Source/Falcor/Utils/SDF/SDF2DPrimitives.slang index 11993230c..d567f9828 100644 --- a/Source/Falcor/Utils/SDF/SDF2DPrimitives.slang +++ b/Source/Falcor/Utils/SDF/SDF2DPrimitives.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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,31 +33,35 @@ import Utils.Math.MathHelpers; // However, the code has been bug fixed in several places and extended in some ways + addition of new primitives. // http://jcgt.org/published/0003/04/01/ -/** Signed distance to unit circle located at the origin. -*/ +/** + * Signed distance to unit circle located at the origin. + */ float sdfCircle(float2 p, float radius) { return length(p) - radius; } -/** Signed distance to unit square. -*/ +/** + * Signed distance to unit square. + */ float sdfSquare(float2 p) { return max(abs(p.x), abs(p.y)) - 1.0f / 2.0f; } -/** Signed distance to diamond. -*/ -float sdfDiamond(float2 p) +/** + * Signed distance to diamond. + */ +float sdfDiamond(float2 p) { float x = M_SQRT2 / 2.0f * (p.x - p.y); float y = M_SQRT2 / 2.0f * (p.x + p.y); return max(abs(x), abs(y)) - 1.0f / (2.0f * M_SQRT2); } -/** Signed distance to heart. -*/ +/** + * Signed distance to heart. + */ float sdfHeart(float2 p) { float x = M_SQRT2 / 2.0f * (p.x - p.y); @@ -68,8 +72,9 @@ float sdfHeart(float2 p) return min(min(r1, r2), r3); } -/** Signed distance to chevron. -*/ +/** + * Signed distance to chevron. + */ float sdfChevron(float2 p) { float x = 1.0f / M_SQRT2 * (p.x - p.y); @@ -79,8 +84,9 @@ float sdfChevron(float2 p) return max(r1, -r2); } -/** Signed distance to ring. -*/ +/** + * Signed distance to ring. + */ float sdfRing(float2 p) { float r1 = length(p) - 1.0f / 2.0f; @@ -88,8 +94,9 @@ float sdfRing(float2 p) return max(r1, -r2); } -/** Signed distance to tag. -*/ +/** + * Signed distance to tag. + */ float sdfTag(float2 p) { float r1 = max(abs(p.x) - 1.0f / 2.0f, abs(p.y) - 1.0f / 6.0f); @@ -97,8 +104,9 @@ float sdfTag(float2 p) return max(r1, 0.75f * r2); } -/** Signed distance to cross. -*/ +/** + * Signed distance to cross. + */ float sdfCross(float2 p) { float x = M_SQRT2 / 2.0f * (p.x - p.y); @@ -109,8 +117,9 @@ float sdfCross(float2 p) return max(min(r1, r2), r3) - 1.0f / 2.0f; } -/** Signed distance to asterisk. -*/ +/** + * Signed distance to asterisk. + */ float sdfAsterisk(float2 p) { float x = M_SQRT2 / 2.0f * (p.x - p.y); @@ -122,8 +131,9 @@ float sdfAsterisk(float2 p) return min(min(r1, r2), min(r3, r4)); } -/** Signed distance to infinity. -*/ +/** + * Signed distance to infinity. + */ float sdfInfinity(float2 p) { const float2 c1 = float2(0.2125f, 0.0f); @@ -135,8 +145,9 @@ float sdfInfinity(float2 p) return min(max(r1, -r2), max(r3, -r4)); } -/** Signed distance to pin. -*/ +/** + * Signed distance to pin. + */ float sdfPin(float2 p) { float2 c1 = float2(0.0f, -0.15f); @@ -149,8 +160,9 @@ float sdfPin(float2 p) return max(min(r1, max(max(r2, r3), -p.y)), -r4); } -/** Signed distance to arrow. -*/ +/** + * Signed distance to arrow. + */ float sdfArrow(float2 p) { float r1 = abs(p.x) + abs(p.y) - 1.0f / 2.0f; @@ -159,20 +171,22 @@ float sdfArrow(float2 p) return min(r3, max(0.75f * r1, r2)); } -/** Signed distance to rounded box. -*/ +/** + * Signed distance to rounded box. + */ float sdfRoundedBox(float2 p, float2 halfBoxSides, float radius) { halfBoxSides -= float2(radius); float2 d = abs(p) - halfBoxSides; float outsideDist = length(float2(max(d.x, 0.0f), max(d.y, 0.0f))) - radius; - float insideDist = min(max(d.x, d.y), 0.0f); // Smallest negative distance clamed to 0.0. + float insideDist = min(max(d.x, d.y), 0.0f); // Smallest negative distance clamed to 0.0. return outsideDist + insideDist; } -/** Compute the signed distance from an arbitrary triangle. -*/ -float sdfTriangle(float2 p, float2 p0, float2 p1, float2 p2) // Expects counterclockwise order of vertices. +/** + * Compute the signed distance from an arbitrary triangle. + */ +float sdfTriangle(float2 p, float2 p0, float2 p1, float2 p2) // Expects counterclockwise order of vertices. { // Edge from p0 to p1. float2 N = normalize(p1 - p0); @@ -192,8 +206,9 @@ float sdfTriangle(float2 p, float2 p0, float2 p1, float2 p2) // Expects counte return distance; } -/** Computes the signed distance from a line segment. -*/ +/** + * Computes the signed distance from a line segment. + */ float sdfSegmentDistance(float2 p, float2 p1, float2 p2) { float2 center = (p1 + p2) * 0.5f; @@ -205,8 +220,9 @@ float sdfSegmentDistance(float2 p, float2 p1, float2 p2) return max(dist1, dist2); } -/** Computes the signed distance from rounded line segment. -*/ +/** + * Computes the signed distance from rounded line segment. + */ float sdfRoundedLine(float2 p, float2 p1, float2 p2, float width) { float2 dir = normalize(p2 - p1) * width * 0.5f; @@ -216,8 +232,9 @@ float sdfRoundedLine(float2 p, float2 p1, float2 p2, float width) return min(min(d1, d2), d3); } -/** An arrow than can be used to render (normal) vectors, for example. -*/ +/** + * An arrow than can be used to render (normal) vectors, for example. + */ float sdfVector(float2 p, float2 p1, float2 p2, float width, float arrowHeight) { float2 dir = p2 - p1; @@ -230,8 +247,9 @@ float sdfVector(float2 p, float2 p1, float2 p2, float width, float arrowHeight) return min(d1, d2); } -/** Interface for 2D SDF primitives -*/ +/** + * Interface for 2D SDF primitives + */ interface ISDF2D { float eval(float2 p); @@ -246,67 +264,67 @@ struct SDF2DCircle : ISDF2D struct SDF2DSquare : ISDF2D { - __init() { } + __init() {} float eval(float2 p) { return sdfSquare(p); } }; struct SDF2DDiamond : ISDF2D { - __init() { } + __init() {} float eval(float2 p) { return sdfDiamond(p); } }; struct SDF2DHeart : ISDF2D { - __init() { } + __init() {} float eval(float2 p) { return sdfHeart(p); } }; struct SDF2DChevron : ISDF2D { - __init() { } + __init() {} float eval(float2 p) { return sdfChevron(p); } }; struct SDF2DRing : ISDF2D { - __init() { } + __init() {} float eval(float2 p) { return sdfRing(p); } }; struct SDF2DTag : ISDF2D { - __init() { } + __init() {} float eval(float2 p) { return sdfTag(p); } }; struct SDF2DCross : ISDF2D { - __init() { } + __init() {} float eval(float2 p) { return sdfCross(p); } }; struct SDF2DAsterisk : ISDF2D { - __init() { } + __init() {} float eval(float2 p) { return sdfAsterisk(p); } }; struct SDF2DInfinity : ISDF2D { - __init() { } + __init() {} float eval(float2 p) { return sdfInfinity(p); } }; struct SDF2DPin : ISDF2D { - __init() { } + __init() {} float eval(float2 p) { return sdfPin(p); } }; struct SDF2DArrow : ISDF2D { - __init() { } + __init() {} float eval(float2 p) { return sdfArrow(p); } }; @@ -325,7 +343,7 @@ struct SDF2DRoundedBox : ISDF2D struct SDF2DVector : ISDF2D { float2 startPoint; - float2 endPoint; // Where the arrow is located. + float2 endPoint; // Where the arrow is located. float width; float arrowHeight; __init(float2 startPoint, float2 endPoint, float width, float arrowHeight) diff --git a/Source/Falcor/Utils/SDF/SDF3DShapes.slang b/Source/Falcor/Utils/SDF/SDF3DShapes.slang index 36fc12372..2212d988b 100644 --- a/Source/Falcor/Utils/SDF/SDF3DShapes.slang +++ b/Source/Falcor/Utils/SDF/SDF3DShapes.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-21, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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,18 +28,19 @@ import Utils.Math.IntervalArithmetic; /* Functions for computing the signed distance function (SDF) for several different primitives, -* including sphere, ellipsoid, box, torus, cone, capsule, and bounding box. -* For each primitives, there is a "standard" SDF function, e.g, sdfSphere() that returns -* a scalar distance, but also an a version called sdfIntervalSphere() which is the same -* except all computations are evaluated using interval arithmetic, which means it returns -* an interval (float2). -*/ - -/** Computes signed distance to a sphere centered at the origin. - \param[in] p The point at which the distance is to be computed. - \param[in] r The radius of the sphere. - \return The distance from p to the primitive. -*/ + * including sphere, ellipsoid, box, torus, cone, capsule, and bounding box. + * For each primitives, there is a "standard" SDF function, e.g, sdfSphere() that returns + * a scalar distance, but also an a version called sdfIntervalSphere() which is the same + * except all computations are evaluated using interval arithmetic, which means it returns + * an interval (float2). + */ + +/** + * Computes signed distance to a sphere centered at the origin. + * @param[in] p The point at which the distance is to be computed. + * @param[in] r The radius of the sphere. + * @return The distance from p to the primitive. + */ float sdfSphere(float3 p, float r) { return length(p) - r; @@ -54,11 +55,12 @@ float2 sdfIntervalSphere(float3 pMin, float3 pMax, float r) return ivlSub(ivlLength(xInterval, yInterval, zInterval), r); } -/** Computes signed distance to a ellipsoid centered at the origin. - \param[in] p The point at which the distance is to be computed. - \param[in] r The radii (one per xyz) of the ellipsoid. - \return The distance from p to the primitive. -*/ +/** + * Computes signed distance to a ellipsoid centered at the origin. + * @param[in] p The point at which the distance is to be computed. + * @param[in] r The radii (one per xyz) of the ellipsoid. + * @return The distance from p to the primitive. + */ float sdfEllipsoid(float3 p, float3 r) { float k0 = length(p / r); @@ -78,11 +80,12 @@ float2 sdfIntervalEllipsoid(float3 pMin, float3 pMax, float3 r) return ivlDiv(ivlPosMul(k0, ivlSub(k0, 1.0f)), k1); } -/** Computes signed distance to a box centered at the origin. - \param[in] p The point at which the distance is to be computed. - \param[in] b The halflengths of the box. - \return The distance from p to the primitive. -*/ +/** + * Computes signed distance to a box centered at the origin. + * @param[in] p The point at which the distance is to be computed. + * @param[in] b The halflengths of the box. + * @return The distance from p to the primitive. + */ float sdfBox(float3 p, float3 b) { float3 q = abs(p) - b; @@ -95,15 +98,19 @@ float2 sdfIntervalBox(float3 pMin, float3 pMax, float3 b) float2 yAbsIntervalSubB = ivlSub(ivlAbs(float2(pMin.y, pMax.y)), b.y); float2 zAbsIntervalSubB = ivlSub(ivlAbs(float2(pMin.z, pMax.z)), b.z); - return ivlAdd(ivlLength(ivlMax(xAbsIntervalSubB, 0.0f), ivlMax(yAbsIntervalSubB, 0.0f), ivlMax(zAbsIntervalSubB, 0.0f)), - ivlMin(ivlMax(ivlMax(xAbsIntervalSubB, yAbsIntervalSubB), zAbsIntervalSubB), 0.0f)); + return ivlAdd( + ivlLength(ivlMax(xAbsIntervalSubB, 0.0f), ivlMax(yAbsIntervalSubB, 0.0f), ivlMax(zAbsIntervalSubB, 0.0f)), + ivlMin(ivlMax(ivlMax(xAbsIntervalSubB, yAbsIntervalSubB), zAbsIntervalSubB), 0.0f) + ); } -/** Computes signed distance to a torus centered at the origin. - \param[in] p The point at which the distance is to be computed. - \param[in] r The radius from the origin to the center of the "tube". Note that the tube has zero radius here. This can be added on top using shapeBlobbing. - \return The distance from p to the primitive. -*/ +/** + * Computes signed distance to a torus centered at the origin. + * @param[in] p The point at which the distance is to be computed. + * @param[in] r The radius from the origin to the center of the "tube". Note that the tube has zero radius here. This can be added on top + * using shapeBlobbing. + * @return The distance from p to the primitive. + */ float sdfTorus(float3 p, float r) { return length(float2(length(p.xz) - r, p.y)); @@ -118,12 +125,13 @@ float2 sdfIntervalTorus(float3 pMin, float3 pMax, float r) return ivlLength(ivlSub(ivlLength(xInterval, zInterval), r), yInterval); } -/** Computes signed distance to a cone centered at the origin, with its apex point at (0,h,0) and base in the xz-plane (y=0). - \param[in] p The point at which the distance is to be computed. - \param[in] tan This is tan of the half angle at the apex. This means that the radius at the base is tan * h. - \param[in] h The height of the cone. - \return The distance from p to the primitive. -*/ +/** + * Computes signed distance to a cone centered at the origin, with its apex point at (0,h,0) and base in the xz-plane (y=0). + * @param[in] p The point at which the distance is to be computed. + * @param[in] tan This is tan of the half angle at the apex. This means that the radius at the base is tan * h. + * @param[in] h The height of the cone. + * @return The distance from p to the primitive. + */ float sdfCone(float3 p, float tan, float h) { float2 q = h * float2(tan, -1.0f); @@ -163,11 +171,13 @@ float2 sdfIntervalCone(float3 pMin, float3 pMax, float tan, float h) return ivlMul(ivlSqrt(d), sign(s)); } -/** Computes signed distance to a capsule centered at the origin. Located at (0, -hl, 0) to (0, +hl, 0). - \param[in] p The point at which the distance is to be computed. Note that the capsule has zero radius here. This is added on top using shapeBlobbing. - \param[in] hl The half length of the capsule.. - \return The distance from p to the primitive. -*/ +/** + * Computes signed distance to a capsule centered at the origin. Located at (0, -hl, 0) to (0, +hl, 0). + * @param[in] p The point at which the distance is to be computed. Note that the capsule has zero radius here. This is added on top using + * shapeBlobbing. + * @param[in] hl The half length of the capsule.. + * @return The distance from p to the primitive. + */ float sdfCapsule(float3 p, float hl) { p.y -= clamp(p.y, -hl, hl); @@ -184,19 +194,20 @@ float2 sdfIntervalCapsule(float3 pMin, float3 pMax, float hl) return ivlLength(xInterval, yInterval, zInterval); } -/** Computes signed distance to the edges of a bounding centered. - \param[in] p The point at which the distance is to be computed. - \param[in] halfBoxSides The half box sides. - \param[in] edgeThickness The thickness of the edges. - \return The distance from p to the primitive. -*/ +/** + * Computes signed distance to the edges of a bounding centered. + * @param[in] p The point at which the distance is to be computed. + * @param[in] halfBoxSides The half box sides. + * @param[in] edgeThickness The thickness of the edges. + * @return The distance from p to the primitive. + */ float sdfBoundingBox(float3 point, float3 halfBoxSides, float edgeThickness) { float3 p = abs(point) - halfBoxSides; float3 q = abs(p + edgeThickness) - edgeThickness; - return min(min( - length(max(float3(p.x, q.y, q.z), 0.0f)) + min(max(p.x, max(q.y, q.z)), 0.0f), - length(max(float3(q.x, p.y, q.z), 0.0f)) + min(max(q.x, max(p.y, q.z)), 0.0f)), - length(max(float3(q.x, q.y, p.z), 0.0f)) + min(max(q.x, max(q.y, p.z)), 0.0f)); + return min( + min(length(max(float3(p.x, q.y, q.z), 0.0f)) + min(max(p.x, max(q.y, q.z)), 0.0f), + length(max(float3(q.x, p.y, q.z), 0.0f)) + min(max(q.x, max(p.y, q.z)), 0.0f)), + length(max(float3(q.x, q.y, p.z), 0.0f)) + min(max(q.x, max(q.y, p.z)), 0.0f) + ); } - diff --git a/Source/Falcor/Utils/SDF/SDFOperations.slang b/Source/Falcor/Utils/SDF/SDFOperations.slang index 277239aba..28d03a5bc 100644 --- a/Source/Falcor/Utils/SDF/SDFOperations.slang +++ b/Source/Falcor/Utils/SDF/SDFOperations.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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,8 @@ **************************************************************************/ import Utils.Math.IntervalArithmetic; - /** Blending of two "objects" based on a radius. Equation 17.57 in RTR4, and also in the Graphics Codex by Morgan McGuire. +/** + * Blending of two "objects" based on a radius. Equation 17.57 in RTR4, and also in the Graphics Codex by Morgan McGuire. */ float sdfBlend(float distance1, float distance2, float radius) { @@ -39,13 +40,16 @@ float sdfBlend(float distance1, float distance2, float radius) float2 sdfIntervalBlend(float2 distance1, float2 distance2, float radius) { float2 h = ivlSaturate(ivlAdd(ivlMul(ivlDiv(ivlSub(distance2, distance1), radius), 0.5f), 0.5f)); - float2 d = ivlAdd(ivlMul(ivlAdd(ivlNegate(h), 1.0f), distance2), ivlSub(ivlMul(h, distance1), ivlMul(ivlMul(h, ivlAdd(ivlNegate(h), 1.0f)), radius))); + float2 d = ivlAdd( + ivlMul(ivlAdd(ivlNegate(h), 1.0f), distance2), ivlSub(ivlMul(h, distance1), ivlMul(ivlMul(h, ivlAdd(ivlNegate(h), 1.0f)), radius)) + ); return d; } -/** smin() is used by sdfSmoothUnion() below. - http://iquilezles.org/www/articles/smin/smin.htm -*/ +/** + * smin() is used by sdfSmoothUnion() below. + * http://iquilezles.org/www/articles/smin/smin.htm + */ float smin(float a, float b, float k) { float h = max(k - abs(a - b), 0.0f); @@ -60,9 +64,10 @@ float2 intervalSMin(float2 a, float2 b, float k) return ivlSub(ivlMin(a, b), h); } -/** smax() is used by sdfSmoothIntersection() below. - http://iquilezles.org/www/articles/smin/smin.htm -*/ +/** + * smax() is used by sdfSmoothIntersection() below. + * http://iquilezles.org/www/articles/smin/smin.htm + */ float smax(float a, float b, float k) { float h = max(k - abs(a - b), 0.0f); @@ -77,8 +82,9 @@ float2 intervalSMax(float2 a, float2 b, float k) return ivlAdd(ivlMax(a, b), h); } -/** Union, intersection, and subtraction operators. -*/ +/** + * Union, intersection, and subtraction operators. + */ float sdfUnion(float a, float b) { return min(a, b); @@ -109,43 +115,49 @@ float2 sdfIntervalSubtraction(float2 a, float2 b) return ivlMax(a, ivlNegate(b)); } -/** Smooth union. -*/ +/** + * Smooth union. + */ float sdfSmoothUnion(float a, float b, float k) { return smin(a, b, k); } -/** Smooth intersection. -*/ +/** + * Smooth intersection. + */ float sdfSmoothIntersection(float a, float b, float k) { return smax(a, b, k); } -/** Smooth subtraction. -*/ +/** + * Smooth subtraction. + */ float sdfSmoothSubtraction(float a, float b, float k) { return smax(a, -b, k); } -/** Interval Smooth union. -*/ +/** + * Interval Smooth union. + */ float2 sdfIntervalSmoothUnion(float2 a, float2 b, float k) { return intervalSMin(a, b, k); } -/** Interval Smooth intersection. -*/ +/** + * Interval Smooth intersection. + */ float2 sdfIntervalSmoothIntersection(float2 a, float2 b, float k) { return intervalSMax(a, b, k); } -/** Interval Smooth subtraction. -*/ +/** + * Interval Smooth subtraction. + */ float2 sdfIntervalSmoothSubtraction(float2 a, float2 b, float k) { return intervalSMax(a, ivlNegate(b), k); diff --git a/Source/Falcor/Utils/SampleGenerators/CPUSampleGenerator.h b/Source/Falcor/Utils/SampleGenerators/CPUSampleGenerator.h index ba0ab14f2..408dcbe6e 100644 --- a/Source/Falcor/Utils/SampleGenerators/CPUSampleGenerator.h +++ b/Source/Falcor/Utils/SampleGenerators/CPUSampleGenerator.h @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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,37 @@ **************************************************************************/ #pragma once #include "Core/Macros.h" +#include "Core/Object.h" #include "Utils/Math/Vector.h" -#include namespace Falcor { - /** Two-dimensional sample pattern generator on the CPU. - */ - class FALCOR_API CPUSampleGenerator - { - public: - using SharedPtr = std::shared_ptr; - virtual ~CPUSampleGenerator() = default; +/** + * Two-dimensional sample pattern generator on the CPU. + */ +class FALCOR_API CPUSampleGenerator : public Object +{ +public: + virtual ~CPUSampleGenerator() = default; - /** Return the total number of samples in the sample pattern. - */ - virtual uint32_t getSampleCount() const = 0; + /** + * 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; + /** + * 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 float2 next() = 0; + /** + * Return the next two-dimensional sample. + * @return Sample in the range [-0.5, 0.5) in each dimension. + */ + virtual float2 next() = 0; - protected: - CPUSampleGenerator() = default; - }; -} +protected: + CPUSampleGenerator() = default; +}; +} // namespace Falcor diff --git a/Source/Falcor/Utils/SampleGenerators/DxSamplePattern.cpp b/Source/Falcor/Utils/SampleGenerators/DxSamplePattern.cpp index 98da05561..a728976ec 100644 --- a/Source/Falcor/Utils/SampleGenerators/DxSamplePattern.cpp +++ b/Source/Falcor/Utils/SampleGenerators/DxSamplePattern.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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,18 +30,23 @@ namespace Falcor { - const float2 DxSamplePattern::kPattern[] = { { 1.0f / 16.0f, -3.0f / 16.0f }, - { -1.0f / 16.0f, 3.0f / 16.0f }, - { 5.0f / 16.0f, 1.0f / 16.0f }, - { -3.0f / 16.0f, -5.0f / 16.0f }, - { -5.0f / 16.0f, 5.0f / 16.0f }, - { -7.0f / 16.0f, -1.0f / 16.0f }, - { 3.0f / 16.0f, 7.0f / 16.0f }, - { 7.0f / 16.0f, -7.0f / 16.0f } }; +const float2 DxSamplePattern::kPattern[] = { + // clang-format off + { 1.0f / 16.0f, -3.0f / 16.0f}, + {-1.0f / 16.0f, 3.0f / 16.0f}, + { 5.0f / 16.0f, 1.0f / 16.0f}, + {-3.0f / 16.0f, -5.0f / 16.0f}, + {-5.0f / 16.0f, 5.0f / 16.0f}, + {-7.0f / 16.0f, -1.0f / 16.0f}, + { 3.0f / 16.0f, 7.0f / 16.0f}, + { 7.0f / 16.0f, -7.0f / 16.0f}, + // clang-format on +}; - DxSamplePattern::DxSamplePattern(uint32_t sampleCount) - { - // FIXME: Support other sample counts - if (sampleCount != kSampleCount) logWarning("DxSamplePattern() currently requires sampleCount = 8. Using that number."); - } +DxSamplePattern::DxSamplePattern(uint32_t sampleCount) +{ + // FIXME: Support other sample counts + if (sampleCount != kSampleCount) + logWarning("DxSamplePattern() currently requires sampleCount = 8. Using that number."); } +} // namespace Falcor diff --git a/Source/Falcor/Utils/SampleGenerators/DxSamplePattern.h b/Source/Falcor/Utils/SampleGenerators/DxSamplePattern.h index d71f49786..f1c7416db 100644 --- a/Source/Falcor/Utils/SampleGenerators/DxSamplePattern.h +++ b/Source/Falcor/Utils/SampleGenerators/DxSamplePattern.h @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -29,37 +29,34 @@ #include "CPUSampleGenerator.h" #include "Core/Macros.h" #include "Utils/Math/Vector.h" -#include namespace Falcor { - /** Sample pattern generator for the Direct3D 8x MSAA/SSAA pattern. - */ - class FALCOR_API DxSamplePattern : public CPUSampleGenerator - { - public: - using SharedPtr = std::shared_ptr; - virtual ~DxSamplePattern() = default; +/** + * Sample pattern generator for the Direct3D 8x MSAA/SSAA pattern. + */ +class FALCOR_API DxSamplePattern : public CPUSampleGenerator +{ +public: + /** + * Create DirectX MSAA sample pattern generator. + * @param[in] sampleCount The sample count. This must be 8 currently. + * @return New object, or throws an exception on error. + */ + static ref create(uint32_t sampleCount = 8) { return make_ref(sampleCount); } - /** Create DirectX MSAA sample pattern generator. - \param[in] sampleCount The sample count. This must be 8 currently. - \return New object, or throws an exception on error. - */ - static SharedPtr create(uint32_t sampleCount = 8) { return SharedPtr(new DxSamplePattern(sampleCount)); } + DxSamplePattern(uint32_t sampleCount); + virtual ~DxSamplePattern() = default; - virtual uint32_t getSampleCount() const override { return kSampleCount; } + virtual uint32_t getSampleCount() const override { return kSampleCount; } - virtual void reset(uint32_t startID = 0) override { mCurSample = 0; } + virtual void reset(uint32_t startID = 0) override { mCurSample = 0; } - virtual float2 next() override - { - return kPattern[(mCurSample++) % kSampleCount]; - } - protected: - DxSamplePattern(uint32_t sampleCount); + virtual float2 next() override { return kPattern[(mCurSample++) % kSampleCount]; } - uint32_t mCurSample = 0; - static const uint32_t kSampleCount = 8; - static const float2 kPattern[kSampleCount]; - }; -} +protected: + uint32_t mCurSample = 0; + static constexpr uint32_t kSampleCount = 8; + static const float2 kPattern[kSampleCount]; +}; +} // namespace Falcor diff --git a/Source/Falcor/Utils/SampleGenerators/HaltonSamplePattern.cpp b/Source/Falcor/Utils/SampleGenerators/HaltonSamplePattern.cpp index c3995c532..62da8a4b8 100644 --- a/Source/Falcor/Utils/SampleGenerators/HaltonSamplePattern.cpp +++ b/Source/Falcor/Utils/SampleGenerators/HaltonSamplePattern.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -29,46 +29,47 @@ namespace Falcor { - namespace - { - /** Returns elements of the Halton low-discrepancy sequence. - \param[in] index Index of the queried element, starting from 0. - \param[in] base Base for the digit inversion. Should be the next unused prime number. - */ - float halton(uint32_t index, uint32_t base) - { - // Reversing digit order in the given base in floating point. - float result = 0.0f; - float factor = 1.0f; - - for (; index > 0; index /= base) - { - factor /= base; - result += factor * (index % base); - } - - return result; - } - } +namespace +{ +/** + * Returns elements of the Halton low-discrepancy sequence. + * @param[in] index Index of the queried element, starting from 0. + * @param[in] base Base for the digit inversion. Should be the next unused prime number. + */ +float halton(uint32_t index, uint32_t base) +{ + // Reversing digit order in the given base in floating point. + float result = 0.0f; + float factor = 1.0f; - HaltonSamplePattern::HaltonSamplePattern(uint32_t sampleCount) + for (; index > 0; index /= base) { - mSampleCount = sampleCount; - mCurSample = 0; + factor /= base; + result += factor * (index % base); } - float2 HaltonSamplePattern::next() - { - float2 value = {halton(mCurSample, 2), halton(mCurSample, 3)}; + return result; +} +} // namespace - // Modular increment. - ++mCurSample; - if (mSampleCount != 0) - { - mCurSample = mCurSample % mSampleCount; - } +HaltonSamplePattern::HaltonSamplePattern(uint32_t sampleCount) +{ + mSampleCount = sampleCount; + mCurSample = 0; +} - // Map the result so that [0, 1) maps to [-0.5, 0.5) and 0 maps to the origin. - return glm::fract(value + 0.5f) - 0.5f; +float2 HaltonSamplePattern::next() +{ + float2 value = {halton(mCurSample, 2), halton(mCurSample, 3)}; + + // Modular increment. + ++mCurSample; + if (mSampleCount != 0) + { + mCurSample = mCurSample % mSampleCount; } + + // Map the result so that [0, 1) maps to [-0.5, 0.5) and 0 maps to the origin. + return math::frac(value + 0.5f) - 0.5f; } +} // namespace Falcor diff --git a/Source/Falcor/Utils/SampleGenerators/HaltonSamplePattern.h b/Source/Falcor/Utils/SampleGenerators/HaltonSamplePattern.h index 42b18d7c5..f95484c16 100644 --- a/Source/Falcor/Utils/SampleGenerators/HaltonSamplePattern.h +++ b/Source/Falcor/Utils/SampleGenerators/HaltonSamplePattern.h @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -29,33 +29,30 @@ #include "CPUSampleGenerator.h" #include "Core/Macros.h" #include "Utils/Math/Vector.h" -#include namespace Falcor { - class FALCOR_API HaltonSamplePattern : public CPUSampleGenerator - { - public: - using SharedPtr = std::shared_ptr; - - virtual ~HaltonSamplePattern() = default; - - /** Create Halton sample pattern generator. - \param[in] sampleCount The pattern repeats every 'sampleCount' samples. Zero means no repeating. - \return New object, or throws an exception on error. - */ - static SharedPtr create(uint32_t sampleCount = 0) { return SharedPtr(new HaltonSamplePattern(sampleCount)); } +class FALCOR_API HaltonSamplePattern : public CPUSampleGenerator +{ +public: + /** + * Create Halton sample pattern generator. + * @param[in] sampleCount The pattern repeats every 'sampleCount' samples. Zero means no repeating. + * @return New object, or throws an exception on error. + */ + static ref create(uint32_t sampleCount = 0) { return make_ref(sampleCount); } - virtual uint32_t getSampleCount() const override { return mSampleCount; } + HaltonSamplePattern(uint32_t sampleCount); + virtual ~HaltonSamplePattern() = default; - virtual void reset(uint32_t startID = 0) override { mCurSample = 0; } + virtual uint32_t getSampleCount() const override { return mSampleCount; } - virtual float2 next() override; + virtual void reset(uint32_t startID = 0) override { mCurSample = 0; } - protected: - HaltonSamplePattern(uint32_t sampleCount); + virtual float2 next() override; - uint32_t mCurSample = 0; - uint32_t mSampleCount; - }; -} +protected: + uint32_t mCurSample = 0; + uint32_t mSampleCount; +}; +} // namespace Falcor diff --git a/Source/Falcor/Utils/SampleGenerators/StratifiedSamplePattern.cpp b/Source/Falcor/Utils/SampleGenerators/StratifiedSamplePattern.cpp index 5e26debc6..1830a8361 100644 --- a/Source/Falcor/Utils/SampleGenerators/StratifiedSamplePattern.cpp +++ b/Source/Falcor/Utils/SampleGenerators/StratifiedSamplePattern.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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,58 +32,58 @@ 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); - StratifiedSamplePattern::StratifiedSamplePattern(uint32_t sampleCount) + // 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) { - // 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); + mBinsX++; mBinsY = sampleCount / mBinsX; - while (mBinsX * mBinsY != sampleCount) - { - mBinsX++; - mBinsY = sampleCount / mBinsX; - } - FALCOR_ASSERT(mBinsX * mBinsY == sampleCount); - - // Create permutation table. - mPermutation.resize(sampleCount); - for (uint32_t i = 0; i < sampleCount; i++) mPermutation[i] = i; } + FALCOR_ASSERT(mBinsX * mBinsY == sampleCount); - 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(); - } + // Create permutation table. + mPermutation.resize(sampleCount); + for (uint32_t i = 0; i < sampleCount; i++) + mPermutation[i] = i; +} - float2 StratifiedSamplePattern::next() - { - auto dist = std::uniform_real_distribution(); - auto u = [&]() { return dist(mRng); }; +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(); +} - // Create new permutation at the start of each round of sampling. - if (mCurSample == 0) std::shuffle(mPermutation.begin(), mPermutation.end(), mRng); +float2 StratifiedSamplePattern::next() +{ + auto dist = std::uniform_real_distribution(); + auto u = [&]() { return dist(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(); + // Create new permutation at the start of each round of sampling. + if (mCurSample == 0) + std::shuffle(mPermutation.begin(), mPermutation.end(), mRng); - FALCOR_ASSERT(i < mBinsX && j < mBinsY); - float x = ((float)i + u()) / mBinsX; - float y = ((float)j + u()) / mBinsY; - return float2(x, y) - 0.5f; - } + // 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(); + + FALCOR_ASSERT(i < mBinsX && j < mBinsY); + float x = ((float)i + u()) / mBinsX; + float y = ((float)j + u()) / mBinsY; + return float2(x, y) - 0.5f; } +} // namespace Falcor diff --git a/Source/Falcor/Utils/SampleGenerators/StratifiedSamplePattern.h b/Source/Falcor/Utils/SampleGenerators/StratifiedSamplePattern.h index e916201df..81aebdb7f 100644 --- a/Source/Falcor/Utils/SampleGenerators/StratifiedSamplePattern.h +++ b/Source/Falcor/Utils/SampleGenerators/StratifiedSamplePattern.h @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -29,46 +29,44 @@ #include "CPUSampleGenerator.h" #include "Core/Macros.h" #include "Utils/Math/Vector.h" -#include #include #include namespace Falcor { - /** 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 FALCOR_API StratifiedSamplePattern : public CPUSampleGenerator - { - public: - using SharedPtr = std::shared_ptr; - - virtual ~StratifiedSamplePattern() = default; - - /** Create stratified random sample pattern generator. - \param[in] sampleCount The number of sampling bins to stratify over. - \return New object, or throws an exception on error. - */ - static SharedPtr create(uint32_t sampleCount = 1); +/** + * 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 FALCOR_API StratifiedSamplePattern : public CPUSampleGenerator +{ +public: + /** + * Create stratified random sample pattern generator. + * @param[in] sampleCount The number of sampling bins to stratify over. + * @return New object, or throws an exception on error. + */ + static ref create(uint32_t sampleCount = 1) { return make_ref(sampleCount); } - virtual uint32_t getSampleCount() const override { return mBinsX * mBinsY; } - virtual void reset(uint32_t startID = 0) override; - virtual float2 next() override; + StratifiedSamplePattern(uint32_t sampleCount); + virtual ~StratifiedSamplePattern() = default; - protected: - StratifiedSamplePattern(uint32_t sampleCount); + virtual uint32_t getSampleCount() const override { return mBinsX * mBinsY; } + virtual void reset(uint32_t startID = 0) override; + virtual float2 next() override; - uint32_t mBinsX = 0; - uint32_t mBinsY = 0; - uint32_t mCurSample = 0; - std::mt19937 mRng; - std::vector mPermutation; - }; -} +protected: + uint32_t mBinsX = 0; + uint32_t mBinsY = 0; + uint32_t mCurSample = 0; + std::mt19937 mRng; + std::vector mPermutation; +}; +} // namespace Falcor diff --git a/Source/Falcor/Utils/Sampling/AliasTable.cpp b/Source/Falcor/Utils/Sampling/AliasTable.cpp index 2685b204d..693a8095e 100644 --- a/Source/Falcor/Utils/Sampling/AliasTable.cpp +++ b/Source/Falcor/Utils/Sampling/AliasTable.cpp @@ -30,119 +30,120 @@ namespace Falcor { - AliasTable::SharedPtr AliasTable::create(Device* pDevice, std::vector weights, std::mt19937& rng) - { - return SharedPtr(new AliasTable(pDevice, std::move(weights), rng)); - } - - void AliasTable::setShaderData(const ShaderVar& var) const - { - var["items"] = mpItems; - var["weights"] = mpWeights; - var["count"] = mCount; - var["weightSum"] = (float)mWeightSum; - } +// This builds an alias table via the O(N) algorithm from Vose 1991, "A linear algorithm for generating random +// numbers with a given distribution," IEEE Transactions on Software Engineering 17(9), 972-975. +// +// Basic idea: creating each alias table entry combines one overweighted sample and one underweighted sample +// into one alias table entry plus a residual sample (the overweighted sample minus some of its weight). +// +// By first separating all inputs into 2 temporary buffer (one overweighted set, with weights above the +// average; one underweighted set, with weights below average), we can simply walk through the lists once, +// merging the first elements in each temporary buffer. The residual sample is interted into either the +// overweighted or underweighted set, depending on its residual weight. +// +// The main complexity is dealing with corner cases, thanks to numerical precision issues, where you don't +// have 2 valid entries to combine. By definition, in these corner cases, all remaining unhandled samples +// actually have the average weight (within numerical precision limits) +AliasTable::AliasTable(ref pDevice, std::vector weights, std::mt19937& rng) : mCount((uint32_t)weights.size()) +{ + // Use >= since we reserve 0xFFFFFFFFu as an invalid flag marker during construction. + if (weights.size() >= std::numeric_limits::max()) + throw RuntimeError("Too many entries for alias table."); - // This builds an alias table via the O(N) algorithm from Vose 1991, "A linear algorithm for generating random - // numbers with a given distribution," IEEE Transactions on Software Engineering 17(9), 972-975. - // - // Basic idea: creating each alias table entry combines one overweighted sample and one underweighted sample - // into one alias table entry plus a residual sample (the overweighted sample minus some of its weight). - // - // By first separating all inputs into 2 temporary buffer (one overweighted set, with weights above the - // average; one underweighted set, with weights below average), we can simply walk through the lists once, - // merging the first elements in each temporary buffer. The residual sample is interted into either the - // overweighted or underweighted set, depending on its residual weight. - // - // The main complexity is dealing with corner cases, thanks to numerical precision issues, where you don't - // have 2 valid entries to combine. By definition, in these corner cases, all remaining unhandled samples - // actually have the average weight (within numerical precision limits) - AliasTable::AliasTable(Device* pDevice, std::vector weights, std::mt19937& rng) - : mCount((uint32_t)weights.size()) - { - // Use >= since we reserve 0xFFFFFFFFu as an invalid flag marker during construction. - if (weights.size() >= std::numeric_limits::max()) throw RuntimeError("Too many entries for alias table."); + std::uniform_int_distribution rngDist; - std::uniform_int_distribution rngDist; + mpWeights = Buffer::createStructured( + pDevice, sizeof(float), mCount, Resource::BindFlags::ShaderResource, Buffer::CpuAccess::None, weights.data() + ); - mpWeights = Buffer::createStructured(pDevice, sizeof(float), mCount, Resource::BindFlags::ShaderResource, Buffer::CpuAccess::None, weights.data()); + // Our working set / intermediate buffers (underweight & overweight); initialize to "invalid" + std::vector lowIdx(mCount, 0xFFFFFFFFu); + std::vector highIdx(mCount, 0xFFFFFFFFu); - // Our working set / intermediate buffers (underweight & overweight); initialize to "invalid" - std::vector lowIdx(mCount, 0xFFFFFFFFu); - std::vector highIdx(mCount, 0xFFFFFFFFu); + // Sum element weights, use double to minimize precision issues + mWeightSum = 0.0; + for (float f : weights) + mWeightSum += f; - // Sum element weights, use double to minimize precision issues - mWeightSum = 0.0; - for (float f : weights) mWeightSum += f; + // Find the average weight + float avgWeight = float(mWeightSum / double(mCount)); - // Find the average weight - float avgWeight = float(mWeightSum / double(mCount)); + // Initialize working set. Inset inputs into our lists of above-average or below-average weight elements. + int lowCount = 0; + int highCount = 0; + for (uint32_t i = 0; i < mCount; ++i) + { + if (weights[i] < avgWeight) + lowIdx[lowCount++] = i; + else + highIdx[highCount++] = i; + } - // Initialize working set. Inset inputs into our lists of above-average or below-average weight elements. - int lowCount = 0; - int highCount = 0; - for (uint32_t i = 0; i < mCount; ++i) + // Create alias table entries by merging above- and below-average samples + std::vector items(mCount); + for (uint32_t i = 0; i < mCount; ++i) + { + // Usual case: We have an above-average and below-average sample we can combine into one alias table entry + if ((lowIdx[i] != 0xFFFFFFFFu) && (highIdx[i] != 0xFFFFFFFFu)) { - if (weights[i] < avgWeight) - lowIdx[lowCount++] = i; + // Create an alias table tuple: + items[i] = {weights[lowIdx[i]] / avgWeight, highIdx[i], lowIdx[i], 0}; + + // We've removed some weight from element highIdx[i]; update it's weight, then re-enter it + // on the end of either the above-average or below-average lists. + float updatedWeight = (weights[lowIdx[i]] + weights[highIdx[i]]) - avgWeight; + weights[highIdx[i]] = updatedWeight; + if (updatedWeight < avgWeight) + lowIdx[lowCount++] = highIdx[i]; else - highIdx[highCount++] = i; + highIdx[highCount++] = highIdx[i]; } - // Create alias table entries by merging above- and below-average samples - std::vector items(mCount); - for (uint32_t i = 0; i < mCount; ++i) + // The next two cases can only occur towards the end of table creation, because either: + // (a) all the remaining possible alias table entries have weight *exactly* equal to avgWeight, + // which means these alias table entries only have one input item that is selected + // with 100% probability + // (b) all the remaining alias table entires have *almost* avgWeight, but due to (compounding) + // precision issues throughout the process, they don't have *quite* that value. In this case + // treating these entries as having exactly avgWeight (as in case (a)) is the only right + // thing to do mathematically (other than re-generating the alias table using higher precision + // or trying to reduce catasrophic numerical cancellation in the "updatedWeight" computation above). + else if (highIdx[i] != 0xFFFFFFFFu) { - // Usual case: We have an above-average and below-average sample we can combine into one alias table entry - if ((lowIdx[i] != 0xFFFFFFFFu) && (highIdx[i] != 0xFFFFFFFFu)) - { - // Create an alias table tuple: - items[i] = { weights[lowIdx[i]] / avgWeight, highIdx[i], lowIdx[i], 0 }; - - // We've removed some weight from element highIdx[i]; update it's weight, then re-enter it - // on the end of either the above-average or below-average lists. - float updatedWeight = (weights[lowIdx[i]] + weights[highIdx[i]]) - avgWeight; - weights[highIdx[i]] = updatedWeight; - if (updatedWeight < avgWeight) - lowIdx[lowCount++] = highIdx[i]; - else - highIdx[highCount++] = highIdx[i]; - } - - // The next two cases can only occur towards the end of table creation, because either: - // (a) all the remaining possible alias table entries have weight *exactly* equal to avgWeight, - // which means these alias table entries only have one input item that is selected - // with 100% probability - // (b) all the remaining alias table entires have *almost* avgWeight, but due to (compounding) - // precision issues throughout the process, they don't have *quite* that value. In this case - // treating these entries as having exactly avgWeight (as in case (a)) is the only right - // thing to do mathematically (other than re-generating the alias table using higher precision - // or trying to reduce catasrophic numerical cancellation in the "updatedWeight" computation above). - else if (highIdx[i] != 0xFFFFFFFFu) - { - items[i] = { 1.0f, highIdx[i], highIdx[i], 0 }; - } - else if (lowIdx[i] != 0xFFFFFFFFu) - { - items[i] = { 1.0f, lowIdx[i], lowIdx[i], 0 }; - } + items[i] = {1.0f, highIdx[i], highIdx[i], 0}; + } + else if (lowIdx[i] != 0xFFFFFFFFu) + { + items[i] = {1.0f, lowIdx[i], lowIdx[i], 0}; + } - // If there is neither a highIdx[i] or lowIdx[i] for some array element(s). By construction, - // this cannot occur (without some logic bug above). - else - { - FALCOR_ASSERT(false); // Should not occur - } + // If there is neither a highIdx[i] or lowIdx[i] for some array element(s). By construction, + // this cannot occur (without some logic bug above). + else + { + FALCOR_ASSERT(false); // Should not occur } + } - // TODO: We can simplify the alias table to implicitly store indexB (aka lowIdx[i]), so the AliasTable::Item - // structure would be 1 float + 1 uint32_t, rather than 128 bits. This, of course, would change usage in shaders - // and elsewhere. To do this, here you'd need to sort elements by indexB so that when looking up mpItems[j], - // indexB==j. This works since, by construction, only one element in the table has indexB==j (for any j - // in [0...mCount-1]). Alternatively, during the loop above, you could directly enter elements into the - // correct location in the alias table. + // TODO: We can simplify the alias table to implicitly store indexB (aka lowIdx[i]), so the AliasTable::Item + // structure would be 1 float + 1 uint32_t, rather than 128 bits. This, of course, would change usage in shaders + // and elsewhere. To do this, here you'd need to sort elements by indexB so that when looking up mpItems[j], + // indexB==j. This works since, by construction, only one element in the table has indexB==j (for any j + // in [0...mCount-1]). Alternatively, during the loop above, you could directly enter elements into the + // correct location in the alias table. - // Stash the alias table in our GPU buffer - mpItems = Buffer::createStructured(pDevice, sizeof(AliasTable::Item), mCount, Resource::BindFlags::ShaderResource, Buffer::CpuAccess::None, items.data()); - } + // Stash the alias table in our GPU buffer + mpItems = Buffer::createStructured( + pDevice, sizeof(AliasTable::Item), mCount, Resource::BindFlags::ShaderResource, Buffer::CpuAccess::None, items.data() + ); +} + +void AliasTable::setShaderData(const ShaderVar& var) const +{ + var["items"] = mpItems; + var["weights"] = mpWeights; + var["count"] = mCount; + var["weightSum"] = (float)mWeightSum; } + +} // namespace Falcor diff --git a/Source/Falcor/Utils/Sampling/AliasTable.h b/Source/Falcor/Utils/Sampling/AliasTable.h index 9e61f98f0..f559e2cbe 100644 --- a/Source/Falcor/Utils/Sampling/AliasTable.h +++ b/Source/Falcor/Utils/Sampling/AliasTable.h @@ -34,50 +34,50 @@ namespace Falcor { - /** Implements the alias method for sampling from a discrete probability distribution. - */ - class FALCOR_API AliasTable - { - public: - using SharedPtr = std::shared_ptr; - - /** Create an alias table. - The weights don't need to be normalized to sum up to 1. - \param[in] pDevice GPU device. - \param[in] weights The weights we'd like to sample each entry proportional to. - \param[in] rng The random number generator to use when creating the table. - \returns The alias table. - */ - static SharedPtr create(Device* pDevice, std::vector weights, std::mt19937& rng); - - /** Bind the alias table data to a given shader var. - \param[in] var The shader variable to set the data into. - */ - void setShaderData(const ShaderVar& var) const; - - /** Get the number of weights in the table. - */ - uint32_t getCount() const { return mCount; } +/** + * Implements the alias method for sampling from a discrete probability distribution. + */ +class FALCOR_API AliasTable +{ +public: + /** + * Create an alias table. + * The weights don't need to be normalized to sum up to 1. + * @param[in] pDevice GPU device. + * @param[in] weights The weights we'd like to sample each entry proportional to. + * @param[in] rng The random number generator to use when creating the table. + */ + AliasTable(ref pDevice, std::vector weights, std::mt19937& rng); - /** Get the total sum of all weights in the table. - */ - double getWeightSum() const { return mWeightSum; } + /** + * Bind the alias table data to a given shader var. + * @param[in] var The shader variable to set the data into. + */ + void setShaderData(const ShaderVar& var) const; - private: - AliasTable(Device* pDevice, std::vector weights, std::mt19937& rng); + /** + * Get the number of weights in the table. + */ + uint32_t getCount() const { return mCount; } - // Item structure for the mpItems buffer. - struct Item - { - float threshold; ///< If rand() < threshold, pick indexB (else pick indexA) - uint32_t indexA; ///< The "redirect" index, if uniform sampling would overweight indexB. - uint32_t indexB; ///< The original / permutation index, sampled uniformly in [0...mCount-1] - uint32_t _pad; - }; + /** + * Get the total sum of all weights in the table. + */ + double getWeightSum() const { return mWeightSum; } - uint32_t mCount; ///< Number of items in the alias table. - double mWeightSum; ///< Total weight of all elements used to create the alias table. - Buffer::SharedPtr mpItems; ///< Buffer containing table items. - Buffer::SharedPtr mpWeights; ///< Buffer containing item weights. +private: + // Item structure for the mpItems buffer. + struct Item + { + float threshold; ///< If rand() < threshold, pick indexB (else pick indexA) + uint32_t indexA; ///< The "redirect" index, if uniform sampling would overweight indexB. + uint32_t indexB; ///< The original / permutation index, sampled uniformly in [0...mCount-1] + uint32_t _pad; }; -} + + uint32_t mCount; ///< Number of items in the alias table. + double mWeightSum; ///< Total weight of all elements used to create the alias table. + ref mpItems; ///< Buffer containing table items. + ref mpWeights; ///< Buffer containing item weights. +}; +} // namespace Falcor diff --git a/Source/Falcor/Utils/Sampling/AliasTable.slang b/Source/Falcor/Utils/Sampling/AliasTable.slang index 8a9c95847..f228ec58b 100644 --- a/Source/Falcor/Utils/Sampling/AliasTable.slang +++ b/Source/Falcor/Utils/Sampling/AliasTable.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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,9 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **************************************************************************/ -/** Implements the alias method for sampling from a discrete probability distribution. -*/ +/** + * Implements the alias method for sampling from a discrete probability distribution. + */ struct AliasTable { struct Item @@ -42,38 +43,38 @@ struct AliasTable uint getIndexB() { return indexB; } }; - StructuredBuffer items; ///< List of items used for sampling. - StructuredBuffer weights; ///< List of original weights. - uint count; ///< Total number of weights in the table. - float weightSum; ///< Total sum of all weights in the table. + StructuredBuffer items; ///< List of items used for sampling. + StructuredBuffer weights; ///< List of original weights. + uint count; ///< Total number of weights in the table. + float weightSum; ///< Total sum of all weights in the table. - /** Sample from the table proportional to the weights. - \param[in] index Uniform random index in [0..count). - \param[in] rnd Uniform random number in [0..1). - \return Returns the sampled item index. - */ + /** + * Sample from the table proportional to the weights. + * @param[in] index Uniform random index in [0..count). + * @param[in] rnd Uniform random number in [0..1). + * @return Returns the sampled item index. + */ uint sample(uint index, float rnd) { Item item = items[index]; return rnd >= item.getThreshold() ? item.getIndexA() : item.getIndexB(); } - /** Sample from the table proportional to the weights. - \param[in] rnd Two uniform random number in [0..1). - \return Returns the sampled item index. - */ + /** + * Sample from the table proportional to the weights. + * @param[in] rnd Two uniform random number in [0..1). + * @return Returns the sampled item index. + */ uint sample(float2 rnd) { uint index = min(count - 1, (uint)(rnd.x * count)); return sample(index, rnd.y); } - /** Get the original weight at a given index. - \param[in] index Table index. - \return Returns the original weight. - */ - float getWeight(uint index) - { - return weights[index]; - } + /** + * Get the original weight at a given index. + * @param[in] index Table index. + * @return Returns the original weight. + */ + float getWeight(uint index) { return weights[index]; } }; diff --git a/Source/Falcor/Utils/Sampling/Pseudorandom/LCG.slang b/Source/Falcor/Utils/Sampling/Pseudorandom/LCG.slang index 18641fe3f..8e281e3eb 100644 --- a/Source/Falcor/Utils/Sampling/Pseudorandom/LCG.slang +++ b/Source/Falcor/Utils/Sampling/Pseudorandom/LCG.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-21, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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,27 @@ # 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. -*/ +/** + * 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). -*/ +/** + * Generates the next pseudorandom number in the sequence (32 bits). + */ uint nextRandom(inout LCG rng) { const uint A = 1664525u; @@ -53,9 +55,10 @@ uint nextRandom(inout LCG rng) return rng.state; } -/** Initialize LCG pseudorandom number generator. - \param[in] s0 Initial state (seed). -*/ +/** + * Initialize LCG pseudorandom number generator. + * @param[in] s0 Initial state (seed). + */ LCG createLCG(uint s0) { LCG rng; diff --git a/Source/Falcor/Utils/Sampling/Pseudorandom/SplitMix64.slang b/Source/Falcor/Utils/Sampling/Pseudorandom/SplitMix64.slang index e8d9a7343..fb3799667 100644 --- a/Source/Falcor/Utils/Sampling/Pseudorandom/SplitMix64.slang +++ b/Source/Falcor/Utils/Sampling/Pseudorandom/SplitMix64.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-21, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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,19 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **************************************************************************/ -/** 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. -*/ +/** + * 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 SplitMix64 { @@ -49,8 +50,9 @@ uint64_t asuint64(uint lowbits, uint highbits) return (uint64_t(highbits) << 32) | uint64_t(lowbits); } -/** Generates the next pseudorandom number in the sequence (64 bits). -*/ +/** + * Generates the next pseudorandom number in the sequence (64 bits). + */ uint64_t nextRandom64(inout SplitMix64 rng) { uint64_t z = (rng.state += 0x9E3779B97F4A7C15ull); @@ -59,17 +61,19 @@ uint64_t nextRandom64(inout SplitMix64 rng) return z ^ (z >> 31); } -/** Generates the next pseudorandom number in the sequence (low 32 bits). -*/ +/** + * Generates the next pseudorandom number in the sequence (low 32 bits). + */ uint nextRandom(inout SplitMix64 rng) { return (uint)nextRandom64(rng); } -/** Initialize SplitMix64 pseudorandom number generator. - \param[in] s0 Low bits of initial state (seed). - \param[in] s1 High bits of initial state (seed). -*/ +/** + * 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; diff --git a/Source/Falcor/Utils/Sampling/Pseudorandom/Xorshift32.slang b/Source/Falcor/Utils/Sampling/Pseudorandom/Xorshift32.slang index e7c238405..ef4113216 100644 --- a/Source/Falcor/Utils/Sampling/Pseudorandom/Xorshift32.slang +++ b/Source/Falcor/Utils/Sampling/Pseudorandom/Xorshift32.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-21, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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,27 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **************************************************************************/ -/** Xorshift32 is a tiny pseudorandom generator with reasonably good properties for its size. - It's still not good enough for many use cases due to its small state. - - Prefer pseudorandom generators with a larger state. -*/ +/** + * Xorshift32 is a tiny pseudorandom generator with reasonably good properties for its size. + * It's still not good enough for many use cases due to its small state. + * + * Prefer pseudorandom generators with a larger state. + */ struct Xorshift32 { - /** Initializes a Xorshift32 pseudorandom generator. - \param[in] seed The random seed. - \return the new pseudorandom generator. - */ - __init(uint seed) - { - this.state = seed; - } + /** + * Initializes a Xorshift32 pseudorandom generator. + * @param[in] seed The random seed. + * @return the new pseudorandom generator. + */ + __init(uint seed) { this.state = seed; } - /** Generates the next pseudorandom number in the sequence (32 bits). - This function updates the state. - */ - [mutating] uint next() + /** + * Generates the next pseudorandom number in the sequence (32 bits). + * This function updates the state. + */ + [mutating] + uint next() { state ^= state << 13; state ^= state >> 7; diff --git a/Source/Falcor/Utils/Sampling/Pseudorandom/Xoshiro.slang b/Source/Falcor/Utils/Sampling/Pseudorandom/Xoshiro.slang index 1450c1270..81af6b95e 100644 --- a/Source/Falcor/Utils/Sampling/Pseudorandom/Xoshiro.slang +++ b/Source/Falcor/Utils/Sampling/Pseudorandom/Xoshiro.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-21, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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,17 @@ # 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 -*/ +/** + * 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 { @@ -47,12 +48,13 @@ uint rotl(const uint x, int k) return (x << k) | (x >> (32 - k)); } -/** Generates the next pseudorandom number in the sequence (32 bits). -*/ +/** + * 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; + const uint32_t t = rng.state[1] << 9; rng.state[2] ^= rng.state[0]; rng.state[3] ^= rng.state[1]; @@ -62,12 +64,13 @@ uint nextRandom(inout Xoshiro128StarStar rng) rng.state[2] ^= t; rng.state[3] = rotl(rng.state[3], 11); - return result_starstar; + 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. -*/ +/** + * 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 }; @@ -98,11 +101,12 @@ void jump(inout Xoshiro128StarStar rng) 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). -*/ +/** + * 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; diff --git a/Source/Falcor/Utils/Sampling/SampleGenerator.cpp b/Source/Falcor/Utils/Sampling/SampleGenerator.cpp index 7fb5887b5..8b67cf7f0 100644 --- a/Source/Falcor/Utils/Sampling/SampleGenerator.cpp +++ b/Source/Falcor/Utils/Sampling/SampleGenerator.cpp @@ -29,52 +29,54 @@ namespace Falcor { - static std::map)>> sFactory; - static Gui::DropdownList sGuiDropdownList; +static std::map(ref)>> sFactory; +static Gui::DropdownList sGuiDropdownList; - SampleGenerator::SharedPtr SampleGenerator::create(std::shared_ptr pDevice, uint32_t type) +ref SampleGenerator::create(ref pDevice, uint32_t type) +{ + if (auto it = sFactory.find(type); it != sFactory.end()) { - if (auto it = sFactory.find(type); it != sFactory.end()) - { - return it->second(std::move(pDevice)); - } - else - { - throw ArgumentError("Can't create SampleGenerator. Unknown type"); - } + return it->second(pDevice); } - - Shader::DefineList SampleGenerator::getDefines() const + else { - Shader::DefineList defines; - defines.add("SAMPLE_GENERATOR_TYPE", std::to_string(mType)); - return defines; + throw ArgumentError("Can't create SampleGenerator. Unknown type"); } +} - const Gui::DropdownList& SampleGenerator::getGuiDropdownList() - { - return sGuiDropdownList; - } +Shader::DefineList SampleGenerator::getDefines() const +{ + Shader::DefineList defines; + defines.add("SAMPLE_GENERATOR_TYPE", std::to_string(mType)); + return defines; +} - void SampleGenerator::registerType(uint32_t type, const std::string& name, std::function)> createFunc) - { - sGuiDropdownList.push_back({ type, name }); - sFactory[type] = createFunc; - } +const Gui::DropdownList& SampleGenerator::getGuiDropdownList() +{ + return sGuiDropdownList; +} - void SampleGenerator::registerAll() - { - registerType(SAMPLE_GENERATOR_TINY_UNIFORM, "Tiny uniform (32-bit)", [] (std::shared_ptr pDevice) { return SharedPtr(new SampleGenerator(std::move(pDevice), SAMPLE_GENERATOR_TINY_UNIFORM)); }); - registerType(SAMPLE_GENERATOR_UNIFORM, "Uniform (128-bit)", [] (std::shared_ptr pDevice) { return SharedPtr(new SampleGenerator(std::move(pDevice), SAMPLE_GENERATOR_UNIFORM)); }); - } +void SampleGenerator::registerType(uint32_t type, const std::string& name, std::function(ref)> createFunc) +{ + sGuiDropdownList.push_back({type, name}); + sFactory[type] = createFunc; +} - // Automatically register basic sampler types. - static struct RegisterSampleGenerators - { - RegisterSampleGenerators() - { - SampleGenerator::registerAll(); - } - } - sRegisterSampleGenerators; +void SampleGenerator::registerAll() +{ + registerType( + SAMPLE_GENERATOR_TINY_UNIFORM, "Tiny uniform (32-bit)", + [](ref pDevice) { return ref(new SampleGenerator(pDevice, SAMPLE_GENERATOR_TINY_UNIFORM)); } + ); + registerType( + SAMPLE_GENERATOR_UNIFORM, "Uniform (128-bit)", + [](ref pDevice) { return ref(new SampleGenerator(pDevice, SAMPLE_GENERATOR_UNIFORM)); } + ); } + +// Automatically register basic sampler types. +static struct RegisterSampleGenerators +{ + RegisterSampleGenerators() { SampleGenerator::registerAll(); } +} sRegisterSampleGenerators; +} // namespace Falcor diff --git a/Source/Falcor/Utils/Sampling/SampleGenerator.h b/Source/Falcor/Utils/Sampling/SampleGenerator.h index 1491ec834..62a14503f 100644 --- a/Source/Falcor/Utils/Sampling/SampleGenerator.h +++ b/Source/Falcor/Utils/Sampling/SampleGenerator.h @@ -28,6 +28,7 @@ #pragma once #include "SampleGeneratorType.slangh" #include "Core/Macros.h" +#include "Core/Object.h" #include "Core/API/Shader.h" #include "Core/Program/ShaderVar.h" #include "Utils/UI/Gui.h" @@ -36,79 +37,87 @@ namespace Falcor { - class RenderContext; - - /** 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 FALCOR_API SampleGenerator - { - public: - using SharedPtr = std::shared_ptr; - - virtual ~SampleGenerator() = default; - - /** Factory function for creating a sample generator of the specified type. - \param[in] pDevice GPU device. - \param[in] type The type of sample generator. See SampleGeneratorType.slangh. - \return New object, or throws an exception on error. - */ - static SharedPtr create(std::shared_ptr pDevice, uint32_t type); - - /** Get macro definitions for this sample generator. - \return Macro definitions that must be set on the shader program that uses this sampler. - */ - virtual Shader::DefineList getDefines() const; - - /** Binds the data to a program vars object. - \param[in] pVars ProgramVars of the program to set data into. - */ - virtual void setShaderData(ShaderVar const& var) const {} - - /** Render the sampler's UI. - */ - virtual void renderUI(Gui::Widgets& widget) {} - - /** Begin a frame. - This should be called at the beginning of each frame for samplers that do extra setup for each frame. - \param[in] pRenderContext Render context. - \param[in] frameDim Current frame dimension. - \return Returns true if internal state has changed and setShaderData() should be called before using the sampler. - */ - virtual bool beginFrame(RenderContext* pRenderContext, const uint2& frameDim) { return false; } - - /** End a frame. - This should be called at the end of each frame for samplers that do extra setup for each frame. - \param[in] pRenderContext Render context. - \param[in] pRenderOutput Rendered output. - */ - virtual void endFrame(RenderContext* pRenderContext, const Texture::SharedPtr& pRenderOutput) {} - - /** Returns a GUI dropdown list of all available sample generators. - */ - static const Gui::DropdownList& getGuiDropdownList(); - - /** Register a sample generator type. - \param[in] type The type of sample generator. See SampleGeneratorType.slangh. - \param[in] name Descriptive name used in the UI. - \param[in] createFunc Function to create an instance of the sample generator. - */ - static void registerType(uint32_t type, const std::string& name, std::function)> createFunc); - - protected: - SampleGenerator(std::shared_ptr pDevice, uint32_t type) : mpDevice(std::move(pDevice)), mType(type) {} - - std::shared_ptr mpDevice; - const uint32_t mType; ///< Type of sample generator. See SampleGeneratorType.slangh. - - private: - /** Register all basic sample generator types. - */ - static void registerAll(); - - friend struct RegisterSampleGenerators; - }; -} +class RenderContext; + +/** + * 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 FALCOR_API SampleGenerator : public Object +{ +public: + virtual ~SampleGenerator() = default; + + /** + * Factory function for creating a sample generator of the specified type. + * @param[in] pDevice GPU device. + * @param[in] type The type of sample generator. See SampleGeneratorType.slangh. + * @return New object, or throws an exception on error. + */ + static ref create(ref pDevice, uint32_t type); + + /** + * Get macro definitions for this sample generator. + * @return Macro definitions that must be set on the shader program that uses this sampler. + */ + virtual Shader::DefineList getDefines() const; + + /** + * Binds the data to a program vars object. + * @param[in] pVars ProgramVars of the program to set data into. + */ + virtual void setShaderData(const ShaderVar& var) const {} + + /** + * Render the sampler's UI. + */ + virtual void renderUI(Gui::Widgets& widget) {} + + /** + * Begin a frame. + * This should be called at the beginning of each frame for samplers that do extra setup for each frame. + * @param[in] pRenderContext Render context. + * @param[in] frameDim Current frame dimension. + * @return Returns true if internal state has changed and setShaderData() should be called before using the sampler. + */ + virtual bool beginFrame(RenderContext* pRenderContext, const uint2& frameDim) { return false; } + + /** + * End a frame. + * This should be called at the end of each frame for samplers that do extra setup for each frame. + * @param[in] pRenderContext Render context. + * @param[in] pRenderOutput Rendered output. + */ + virtual void endFrame(RenderContext* pRenderContext, const ref& pRenderOutput) {} + + /** + * Returns a GUI dropdown list of all available sample generators. + */ + static const Gui::DropdownList& getGuiDropdownList(); + + /** + * Register a sample generator type. + * @param[in] type The type of sample generator. See SampleGeneratorType.slangh. + * @param[in] name Descriptive name used in the UI. + * @param[in] createFunc Function to create an instance of the sample generator. + */ + static void registerType(uint32_t type, const std::string& name, std::function(ref)> createFunc); + +protected: + SampleGenerator(ref pDevice, uint32_t type) : mpDevice(pDevice), mType(type) {} + + ref mpDevice; + const uint32_t mType; ///< Type of sample generator. See SampleGeneratorType.slangh. + +private: + /** + * Register all basic sample generator types. + */ + static void registerAll(); + + friend struct RegisterSampleGenerators; +}; +} // namespace Falcor diff --git a/Source/Falcor/Utils/Sampling/SampleGenerator.slang b/Source/Falcor/Utils/Sampling/SampleGenerator.slang index 0d63d5fb9..1612a1284 100644 --- a/Source/Falcor/Utils/Sampling/SampleGenerator.slang +++ b/Source/Falcor/Utils/Sampling/SampleGenerator.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -29,23 +29,24 @@ __exported import Utils.Sampling.SampleGeneratorInterface; -/** 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 'SampleGenerator.Padded' type holds a SampleGenerator plus additional - padding to make the struct a multiple of 16B. -*/ +/** + * 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 'SampleGenerator.Padded' 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; +import Utils.Sampling.TinyUniformSampleGenerator; +typedef TinyUniformSampleGenerator SampleGenerator; #elif defined(SAMPLE_GENERATOR_TYPE) && SAMPLE_GENERATOR_TYPE == SAMPLE_GENERATOR_UNIFORM - import Utils.Sampling.UniformSampleGenerator; - typedef UniformSampleGenerator SampleGenerator; +import Utils.Sampling.UniformSampleGenerator; +typedef UniformSampleGenerator SampleGenerator; #endif diff --git a/Source/Falcor/Utils/Sampling/SampleGeneratorInterface.slang b/Source/Falcor/Utils/Sampling/SampleGeneratorInterface.slang index dc01fef93..2d167ada6 100644 --- a/Source/Falcor/Utils/Sampling/SampleGeneratorInterface.slang +++ b/Source/Falcor/Utils/Sampling/SampleGeneratorInterface.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-21, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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,26 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **************************************************************************/ -/** Slang interface for sample generator implementations. -*/ +/** + * Slang interface for sample generator implementations. + */ interface ISampleGenerator { - /** Returns the next sample value. This function updates the state. - */ - [mutating] uint next(); + /** + * Returns the next sample value. This function updates the state. + */ + [mutating] + uint next(); }; -/** 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. -*/ +/** + * 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 S sg) { diff --git a/Source/Falcor/Utils/Sampling/SampleGeneratorType.slangh b/Source/Falcor/Utils/Sampling/SampleGeneratorType.slangh index 4101cebbd..b5c91d8d2 100644 --- a/Source/Falcor/Utils/Sampling/SampleGeneratorType.slangh +++ b/Source/Falcor/Utils/Sampling/SampleGeneratorType.slangh @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -29,9 +29,9 @@ #include "Utils/HostDeviceShared.slangh" -#define SAMPLE_GENERATOR_TINY_UNIFORM 0 -#define SAMPLE_GENERATOR_UNIFORM 1 +#define SAMPLE_GENERATOR_TINY_UNIFORM 0 +#define SAMPLE_GENERATOR_UNIFORM 1 // Default sampler. -#define SAMPLE_GENERATOR_DEFAULT SAMPLE_GENERATOR_UNIFORM +#define SAMPLE_GENERATOR_DEFAULT SAMPLE_GENERATOR_UNIFORM diff --git a/Source/Falcor/Utils/Sampling/TinyUniformSampleGenerator.slang b/Source/Falcor/Utils/Sampling/TinyUniformSampleGenerator.slang index db4002e0a..5e8a2def7 100644 --- a/Source/Falcor/Utils/Sampling/TinyUniformSampleGenerator.slang +++ b/Source/Falcor/Utils/Sampling/TinyUniformSampleGenerator.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-21, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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,11 +30,12 @@ import Utils.Sampling.Pseudorandom.LCG; import Utils.Math.HashUtils; import Utils.Math.BitTricks; -/** 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. -*/ +/** + * 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. + */ public struct TinyUniformSampleGenerator : ISampleGenerator { struct Padded @@ -43,18 +44,17 @@ public struct TinyUniformSampleGenerator : ISampleGenerator uint3 _pad; }; - /** Initializes the sample generator. - \param[in] seed Seed value. - */ - __init(uint seed) - { - this.rng = createLCG(seed); - } + /** + * Initializes the sample generator. + * @param[in] seed Seed value. + */ + __init(uint seed) { this.rng = createLCG(seed); } - /** Initializes the sample generator for a given pixel and sample number. - \param[in] pixel Pixel id. - \param[in] sampleNumber Sample number. - */ + /** + * Initializes the sample generator for a given pixel and sample number. + * @param[in] pixel Pixel id. + * @param[in] sampleNumber Sample number. + */ __init(uint2 pixel, uint sampleNumber) { // Use block cipher to generate a pseudorandom initial seed. @@ -62,12 +62,11 @@ public struct TinyUniformSampleGenerator : ISampleGenerator this.rng = createLCG(seed); } - /** Returns the next sample value. This function updates the state. - */ - [mutating] uint next() - { - return nextRandom(rng); - } + /** + * 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. + 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 index 2ff76eebb..f0afb15c6 100644 --- a/Source/Falcor/Utils/Sampling/UniformSampleGenerator.slang +++ b/Source/Falcor/Utils/Sampling/UniformSampleGenerator.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-21, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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,13 +30,14 @@ import Utils.Sampling.Pseudorandom.Xoshiro; import Utils.Sampling.Pseudorandom.SplitMix64; import Utils.Math.BitTricks; -/** 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. -*/ +/** + * 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. + */ public struct UniformSampleGenerator : ISampleGenerator { struct Padded @@ -44,10 +45,11 @@ public struct UniformSampleGenerator : ISampleGenerator UniformSampleGenerator internal; } - /** Initializes the sample generator for a given pixel and sample number. - \param[in] pixel Pixel id. - \param[in] sampleNumber Sample number. - */ + /** + * Initializes the sample generator for a given pixel and sample number. + * @param[in] pixel Pixel id. + * @param[in] sampleNumber Sample number. + */ __init(uint2 pixel, uint sampleNumber) { UniformSampleGenerator sampleGenerator; @@ -68,12 +70,11 @@ public struct UniformSampleGenerator : ISampleGenerator this.rng = createXoshiro128StarStar(seed); } - /** Returns the next sample value. This function updates the state. - */ - [mutating] uint next() - { - return nextRandom(rng); - } + /** + * 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 index dfa0c0963..88fcefc39 100644 --- a/Source/Falcor/Utils/Scripting/Console.cpp +++ b/Source/Falcor/Utils/Scripting/Console.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -35,159 +35,169 @@ namespace Falcor { - namespace - { - static const ImVec4 kBackgroundColor = ImVec4(0.f, 0.f, 0.f, 0.8f); - static const uint32_t kLineCount = 20; +namespace +{ +static const ImVec4 kBackgroundColor = ImVec4(0.f, 0.f, 0.f, 0.8f); +static const uint32_t kLineCount = 20; - class ConsoleWindow - { - public: - ConsoleWindow(Gui* pGui) - { - ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(4.f, 4.f)); - ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.f); - ImGui::PushStyleColor(ImGuiCol_WindowBg, kBackgroundColor); - ImGui::PushStyleColor(ImGuiCol_ChildBg, kBackgroundColor); - height = (float)ImGui::GetTextLineHeight() * kLineCount; - ImGui::SetNextWindowSize({ ImGui::GetIO().DisplaySize.x, 0 }, ImGuiCond_Always); - ImGui::SetNextWindowPos({0, 0}, ImGuiCond_Always); - - ImGui::Begin("##Console", nullptr, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize); - ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0.f, 0.f)); - ImGui::PushFont(pGui->getFont("monospace")); - } - - ~ConsoleWindow() - { - ImGui::PopFont(); - ImGui::PopStyleVar(); - ImGui::End(); - ImGui::PopStyleColor(2); - ImGui::PopStyleVar(2); - } - - float height = 0; - }; +class ConsoleWindow +{ +public: + ConsoleWindow(Gui* pGui) + { + ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(4.f, 4.f)); + ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.f); + ImGui::PushStyleColor(ImGuiCol_WindowBg, kBackgroundColor); + ImGui::PushStyleColor(ImGuiCol_ChildBg, kBackgroundColor); + height = (float)ImGui::GetTextLineHeight() * kLineCount; + ImGui::SetNextWindowSize({ImGui::GetIO().DisplaySize.x, 0}, ImGuiCond_Always); + ImGui::SetNextWindowPos({0, 0}, ImGuiCond_Always); + + ImGui::Begin("##Console", nullptr, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize); + ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0.f, 0.f)); + ImGui::PushFont(pGui->getFont("monospace")); } - void Console::clear() + ~ConsoleWindow() { - mLog.clear(); + ImGui::PopFont(); + ImGui::PopStyleVar(); + ImGui::End(); + ImGui::PopStyleColor(2); + ImGui::PopStyleVar(2); } - void Console::render(Gui* pGui, bool& show) - { - // Toggle console with "`" key. - if (ImGui::IsKeyPressed('`')) show = !show; + float height = 0; +}; +} // namespace - // Allow closing console with escape key. - if (show && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Escape))) show = false; +void Console::clear() +{ + mLog.clear(); +} - if (!show) return; +void Console::render(Gui* pGui, bool& show) +{ + // Toggle console with "`" key. + if (ImGui::IsKeyPressed('`')) + show = !show; - ConsoleWindow w(pGui); + // Allow closing console with escape key. + if (show && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Escape))) + show = false; - ImGui::SetNextWindowFocus(); - ImGui::BeginChild("log", {0, w.height - ImGui::GetTextLineHeight() - 5}); - ImGui::PushTextWrapPos(); - ImGui::TextUnformatted(mLog.c_str()); - ImGui::PopTextWrapPos(); - if (mScrollToBottom) - { - ImGui::SetScrollHereY(1.f); - mScrollToBottom = false; - } - ImGui::EndChild(); + if (!show) + return; - ImGui::PushItemWidth(ImGui::GetWindowWidth()); - // Stick focus to console text input. - ImGui::SetKeyboardFocusHere(); - if (ImGui::InputText("##console", mCmdBuffer, std::size(mCmdBuffer), ImGuiInputTextFlags_EnterReturnsTrue | ImGuiInputTextFlags_CallbackHistory | ImGuiInputTextFlags_CallbackCharFilter, &inputTextCallback, this)) - { - enterCommand(); - ImGui::GetIO().KeysDown[(uint32_t)Input::Key::Enter] = false; - } - pGui->setActiveFont(""); - } + ConsoleWindow w(pGui); - bool Console::flush() + ImGui::SetNextWindowFocus(); + ImGui::BeginChild("log", {0, w.height - ImGui::GetTextLineHeight() - 5}); + ImGui::PushTextWrapPos(); + ImGui::TextUnformatted(mLog.c_str()); + ImGui::PopTextWrapPos(); + if (mScrollToBottom) { - if (mCmdPending.empty()) return false; - - if (mCmdPending == "cls") - { - clear(); - return false; - } - - try - { - mLog += Scripting::interpretScript(mCmdPending); - } - catch (const std::exception& e) - { - mLog += std::string(e.what()) + "\n"; - }; + ImGui::SetScrollHereY(1.f); + mScrollToBottom = false; + } + ImGui::EndChild(); + + ImGui::PushItemWidth(ImGui::GetWindowWidth()); + // Stick focus to console text input. + ImGui::SetKeyboardFocusHere(); + if (ImGui::InputText( + "##console", mCmdBuffer, std::size(mCmdBuffer), + ImGuiInputTextFlags_EnterReturnsTrue | ImGuiInputTextFlags_CallbackHistory | ImGuiInputTextFlags_CallbackCharFilter, + &inputTextCallback, this + )) + { + enterCommand(); + ImGui::GetIO().KeysDown[(uint32_t)Input::Key::Enter] = false; + } + pGui->setActiveFont(""); +} - mCmdPending.clear(); +bool Console::flush() +{ + if (mCmdPending.empty()) + return false; - return true; + if (mCmdPending == "cls") + { + clear(); + return false; } - void Console::enterCommand() + try { - auto cmd = std::string(mCmdBuffer); - mCmdBuffer[0] = '\0'; - mLog += cmd + "\n"; - mScrollToBottom = true; + mLog += Scripting::interpretScript(mCmdPending); + } + catch (const std::exception& e) + { + mLog += std::string(e.what()) + "\n"; + }; - // Push command to history. - if (!cmd.empty() && (mHistory.empty() || cmd != mHistory.back())) mHistory.push_back(cmd); - mHistoryIndex = -1; + mCmdPending.clear(); - std::swap(cmd, mCmdPending); - } + return true; +} + +void Console::enterCommand() +{ + auto cmd = std::string(mCmdBuffer); + mCmdBuffer[0] = '\0'; + mLog += cmd + "\n"; + mScrollToBottom = true; + + // Push command to history. + if (!cmd.empty() && (mHistory.empty() || cmd != mHistory.back())) + mHistory.push_back(cmd); + mHistoryIndex = -1; + + std::swap(cmd, mCmdPending); +} - std::optional Console::browseHistory(bool upOrDown) +std::optional Console::browseHistory(bool upOrDown) +{ + if (upOrDown) { - if (upOrDown) + if (mHistoryIndex + 1 < (int32_t)mHistory.size()) { - if (mHistoryIndex + 1 < (int32_t)mHistory.size()) - { - mHistoryIndex++; - return mHistory[mHistory.size() - mHistoryIndex - 1]; - } + mHistoryIndex++; + return mHistory[mHistory.size() - mHistoryIndex - 1]; } - else + } + else + { + if (mHistoryIndex >= 0) { - if (mHistoryIndex >= 0) - { - mHistoryIndex--; - return mHistoryIndex >= 0 ? mHistory[mHistory.size() - mHistoryIndex - 1] : ""; - } + mHistoryIndex--; + return mHistoryIndex >= 0 ? mHistory[mHistory.size() - mHistoryIndex - 1] : ""; } - return {}; } + return {}; +} - int Console::inputTextCallback(ImGuiInputTextCallbackData* data) - { - FALCOR_ASSERT(data->UserData != nullptr); - Console& console = *static_cast(data->UserData); +int Console::inputTextCallback(ImGuiInputTextCallbackData* data) +{ + FALCOR_ASSERT(data->UserData != nullptr); + Console& console = *static_cast(data->UserData); - if (data->EventFlag == ImGuiInputTextFlags_CallbackCharFilter) - { - if (data->EventChar == '`') return 1; - } - if (data->EventFlag == ImGuiInputTextFlags_CallbackHistory) + if (data->EventFlag == ImGuiInputTextFlags_CallbackCharFilter) + { + if (data->EventChar == '`') + return 1; + } + if (data->EventFlag == ImGuiInputTextFlags_CallbackHistory) + { + if (auto cmd = console.browseHistory(data->EventKey == ImGuiKey_UpArrow)) { - if (auto cmd = console.browseHistory(data->EventKey == ImGuiKey_UpArrow)) - { - strncpy(data->Buf, cmd->c_str(), cmd->length() + 1); - data->BufTextLen = data->CursorPos = (int)cmd->length(); - data->BufDirty = true; - } + strncpy(data->Buf, cmd->c_str(), cmd->length() + 1); + data->BufTextLen = data->CursorPos = (int)cmd->length(); + data->BufDirty = true; } - return 0; } + return 0; } +} // namespace Falcor diff --git a/Source/Falcor/Utils/Scripting/Console.h b/Source/Falcor/Utils/Scripting/Console.h index c7a3494a9..e87d42a70 100644 --- a/Source/Falcor/Utils/Scripting/Console.h +++ b/Source/Falcor/Utils/Scripting/Console.h @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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,39 +36,42 @@ struct ImGuiInputTextCallbackData; namespace Falcor { - class Gui; +class Gui; - class FALCOR_API Console - { - public: - /** Clears the console. - */ - void clear(); +class FALCOR_API Console +{ +public: + /** + * Clears the console. + */ + void clear(); - /** Renders the console and handles important keyboard input events: - - The "`" key is used to open/close the console. - - The ESC key is used to close the console if currently open. - - The UP/DOWN keys are used to browse through the history. - \param[in] pGui GUI. - \param[in,out] show Flag to indicate if console is shown. - */ - void render(Gui* pGui, bool& show); + /** + * Renders the console and handles important keyboard input events: + * - The "`" key is used to open/close the console. + * - The ESC key is used to close the console if currently open. + * - The UP/DOWN keys are used to browse through the history. + * @param[in] pGui GUI. + * @param[in,out] show Flag to indicate if console is shown. + */ + void render(Gui* pGui, bool& show); - /** Processes console input. Should be called once at the end of every frame. - \return Returns true if some processing occured. - */ - bool flush(); + /** + * Processes console input. Should be called once at the end of every frame. + * @return Returns true if some processing occured. + */ + bool flush(); - private: - void enterCommand(); - std::optional browseHistory(bool upOrDown); - static int inputTextCallback(ImGuiInputTextCallbackData* data); +private: + void enterCommand(); + std::optional browseHistory(bool upOrDown); + static int inputTextCallback(ImGuiInputTextCallbackData* data); - std::string mLog; - char mCmdBuffer[2048] = {}; - std::string mCmdPending; - std::vector mHistory; - int32_t mHistoryIndex = -1; - bool mScrollToBottom = true; - }; -} + std::string mLog; + char mCmdBuffer[2048] = {}; + std::string mCmdPending; + std::vector mHistory; + int32_t mHistoryIndex = -1; + bool mScrollToBottom = true; +}; +} // namespace Falcor diff --git a/Source/Falcor/Utils/Scripting/Dictionary.h b/Source/Falcor/Utils/Scripting/Dictionary.h index c5c4ea562..430473f52 100644 --- a/Source/Falcor/Utils/Scripting/Dictionary.h +++ b/Source/Falcor/Utils/Scripting/Dictionary.h @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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,121 +34,124 @@ namespace Falcor { - class Dictionary +class Dictionary +{ +public: + using Container = pybind11::dict; + + Dictionary() = default; + Dictionary(const Container& c) : mMap(c) {} + + class Value { public: - using Container = pybind11::dict; - using SharedPtr = std::shared_ptr; - - Dictionary() = default; - Dictionary(const Container& c) : mMap(c) {} + Value(const Container& container, const std::string_view name) : mName(name), mContainer(container){}; + Value(const Container& container = {}) : Value(container, std::string()) {} - class Value + Value& operator=(const Value& rhs) { - public: - Value(const Container& container, const std::string_view name) : mName(name), mContainer(container) {}; - Value(const Container& container = {}) : Value(container, std::string()) {} - - Value& operator=(const Value& rhs) - { - mName = rhs.mName; - mContainer[mName.c_str()] = rhs.mContainer[mName.c_str()]; - return *this; - } - - template - void operator=(const T& t) { mContainer[mName.c_str()] = t; } - - template<> - void operator=(const std::filesystem::path& path) - { - // Convert path to string. Otherwise it's represented as a Python WindowsPath/PosixPath. - *this = path.generic_string(); - } - - template - operator T() const { return mContainer[mName.c_str()].cast(); } - - private: - std::string mName; - const Container& mContainer; - }; - - template - class IteratorT - { - public: - IteratorT(ContainerType* pContainer, const pybind11::detail::dict_iterator& it) : mIt(it), mpContainer(pContainer) {} - - bool operator==(const IteratorT& other) const { return other.mIt == mIt; } - bool operator!=(const IteratorT& other) const { return other.mIt != mIt; } - IteratorT& operator++() { mIt++; return *this; } - IteratorT operator++(int) { ++mIt; return *this; } - - std::pair operator*() - { - std::string key = mIt->first.cast(); - return { key, Value(*mpContainer, key) }; - } - private: - pybind11::detail::dict_iterator mIt; - ContainerType* mpContainer; - }; - - /** Create a new dictionary. - \return A new object, or throws an exception if creation failed. - */ - static SharedPtr create() { return SharedPtr(new Dictionary); } - - using Iterator = IteratorT; - using ConstIterator = IteratorT; - - Value operator[](const std::string_view name) { return Value(mMap, name); } - const Value operator[](const std::string_view name) const { return Value(mMap, name); } + mName = rhs.mName; + mContainer[mName.c_str()] = rhs.mContainer[mName.c_str()]; + return *this; + } template - T get(const std::string_view name, const T& def) const + void operator=(const T& t) { - if (!mMap.contains(name.data())) - return def; - return mMap[name.data()].cast(); + mContainer[mName.c_str()] = t; } - template - std::optional get(const std::string_view name) const + void operator=(const std::filesystem::path& path) { - if (!mMap.contains(name.data())) - return std::optional(); - return std::optional(mMap[name.data()].cast()); + // Convert path to string. Otherwise it's represented as a Python WindowsPath/PosixPath. + *this = path.generic_string(); } - // Avoid forcing std::string creation if Value would be just temporary template - T get(const std::string_view name) const + operator T() const { - return mMap[name.data()].cast(); + return mContainer[mName.c_str()].cast(); } - ConstIterator begin() const { return ConstIterator(&mMap, mMap.begin()); } - ConstIterator end() const { return ConstIterator(&mMap, mMap.end()); } - - Iterator begin() { return Iterator(&mMap, mMap.begin()); } - Iterator end() { return Iterator(&mMap, mMap.end()); } + private: + std::string mName; + const Container& mContainer; + }; - size_t size() const { return mMap.size(); } + template + class IteratorT + { + public: + IteratorT(ContainerType* pContainer, const pybind11::detail::dict_iterator& it) : mIt(it), mpContainer(pContainer) {} - bool keyExists(const std::string& key) const + bool operator==(const IteratorT& other) const { return other.mIt == mIt; } + bool operator!=(const IteratorT& other) const { return other.mIt != mIt; } + IteratorT& operator++() { - return mMap.contains(key.c_str()); + mIt++; + return *this; + } + IteratorT operator++(int) + { + ++mIt; + return *this; } - pybind11::dict toPython() const { return mMap; } - - std::string toString() const + std::pair operator*() { - return pybind11::str(static_cast(mMap)); + std::string key = mIt->first.cast(); + return {key, Value(*mpContainer, key)}; } + private: - Container mMap; + pybind11::detail::dict_iterator mIt; + ContainerType* mpContainer; }; -} + + using Iterator = IteratorT; + using ConstIterator = IteratorT; + + Value operator[](const std::string_view name) { return Value(mMap, name); } + const Value operator[](const std::string_view name) const { return Value(mMap, name); } + + template + T get(const std::string_view name, const T& def) const + { + if (!mMap.contains(name.data())) + return def; + return mMap[name.data()].cast(); + } + + template + std::optional get(const std::string_view name) const + { + if (!mMap.contains(name.data())) + return std::optional(); + return std::optional(mMap[name.data()].cast()); + } + + // Avoid forcing std::string creation if Value would be just temporary + template + T get(const std::string_view name) const + { + return mMap[name.data()].cast(); + } + + ConstIterator begin() const { return ConstIterator(&mMap, mMap.begin()); } + ConstIterator end() const { return ConstIterator(&mMap, mMap.end()); } + + Iterator begin() { return Iterator(&mMap, mMap.begin()); } + Iterator end() { return Iterator(&mMap, mMap.end()); } + + size_t size() const { return mMap.size(); } + + bool keyExists(const std::string& key) const { return mMap.contains(key.c_str()); } + + pybind11::dict toPython() const { return mMap; } + + std::string toString() const { return pybind11::str(static_cast(mMap)); } + +private: + Container mMap; +}; +} // namespace Falcor diff --git a/Source/Falcor/Utils/Scripting/ScriptBindings.cpp b/Source/Falcor/Utils/Scripting/ScriptBindings.cpp index 9409e58f6..b0142a890 100644 --- a/Source/Falcor/Utils/Scripting/ScriptBindings.cpp +++ b/Source/Falcor/Utils/Scripting/ScriptBindings.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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,8 +28,8 @@ #include "ScriptBindings.h" #include "Core/Plugin.h" #include "Utils/Scripting/Scripting.h" -#include "Utils/Math/Float16.h" -#include "Utils/Math/Matrix/Matrix.h" +#include "Utils/Math/Vector.h" +#include "Utils/Math/Matrix.h" #include #include #include @@ -39,272 +39,306 @@ using namespace pybind11::literals; namespace pybind11::detail { - // Casting float16_t <-> float. - template<> - struct type_caster - { - public: - PYBIND11_TYPE_CASTER(Falcor::float16_t, _("float16_t")); - using float_caster = type_caster; +// Casting float16_t <-> float. +template<> +struct type_caster +{ +public: + PYBIND11_TYPE_CASTER(Falcor::float16_t, _("float16_t")); + using float_caster = type_caster; - bool load(handle src, bool convert) + bool load(handle src, bool convert) + { + float_caster caster; + if (caster.load(src, convert)) { - float_caster caster; - if (caster.load(src, convert)) - { - this->value = Falcor::float16_t(float(caster)); - return true; - } - return false; + this->value = Falcor::float16_t(float(caster)); + return true; } + return false; + } - static handle cast(Falcor::float16_t src, return_value_policy policy, handle parent) - { - return float_caster::cast(float(src), policy, parent); - } - }; -} + static handle cast(Falcor::float16_t src, return_value_policy policy, handle parent) + { + return float_caster::cast(float(src), policy, parent); + } +}; +} // namespace pybind11::detail namespace Falcor::ScriptBindings { - namespace +namespace +{ +struct DeferredBinding +{ + std::string name; + RegisterBindingFunc bindingFunc; + bool isRegistered = false; + + DeferredBinding(const std::string& name, RegisterBindingFunc bindingFunc) : name(name), bindingFunc(bindingFunc) {} + + void bind(pybind11::module& m) { - struct DeferredBinding - { - std::string name; - RegisterBindingFunc bindingFunc; - bool isRegistered = false; - - DeferredBinding(const std::string& name, RegisterBindingFunc bindingFunc) - : name(name) - , bindingFunc(bindingFunc) - {} - - void bind(pybind11::module& m) - { - if (!isRegistered) - { - isRegistered = true; - bindingFunc(m); - } - } - }; - - static std::vector& getDeferredBindings() + if (!isRegistered) { - static std::vector deferredBindings; - return deferredBindings; + isRegistered = true; + bindingFunc(m); } + } +}; - static std::string getUniqueDeferredBindingName() - { - static uint32_t id = 0; - return fmt::format("_DeferredBinding{}", id++); - } +static std::vector& getDeferredBindings() +{ + static std::vector deferredBindings; + return deferredBindings; +} - static pybind11::module sModule; - } +static std::string getUniqueDeferredBindingName() +{ + static uint32_t id = 0; + return fmt::format("_DeferredBinding{}", id++); +} - void registerBinding(RegisterBindingFunc f) +static pybind11::module sModule; +} // namespace + +void registerBinding(RegisterBindingFunc f) +{ + if (sModule) { - if (sModule) + try { - try - { - f(sModule); - } - catch (const std::exception& e) - { - PyErr_SetString(PyExc_ImportError, e.what()); - reportError(e.what()); - return; - } + f(sModule); } - else + catch (const std::exception& e) { - registerDeferredBinding(getUniqueDeferredBindingName(), f); + PyErr_SetString(PyExc_ImportError, e.what()); + reportError(e.what()); + return; } } - - void registerDeferredBinding(const std::string& name, RegisterBindingFunc f) + else { - auto& deferredBindings = getDeferredBindings(); - if (std::find_if(deferredBindings.begin(), deferredBindings.end(), [&name](const DeferredBinding& binding) { return binding.name == name; }) != deferredBindings.end()) - { - throw RuntimeError("A script binding with the name '{}' already exists!", name); - } - deferredBindings.emplace_back(name, f); + registerDeferredBinding(getUniqueDeferredBindingName(), f); } +} - void resolveDeferredBinding(const std::string &name, pybind11::module& m) +void registerDeferredBinding(const std::string& name, RegisterBindingFunc f) +{ + auto& deferredBindings = getDeferredBindings(); + if (std::find_if( + deferredBindings.begin(), deferredBindings.end(), [&name](const DeferredBinding& binding) { return binding.name == name; } + ) != deferredBindings.end()) { - auto& deferredBindings = getDeferredBindings(); - auto it = std::find_if(deferredBindings.begin(), deferredBindings.end(), [&name](const DeferredBinding& binding) { return binding.name == name; }); - if (it != deferredBindings.end()) it->bind(m); + throw RuntimeError("A script binding with the name '{}' already exists!", name); } + deferredBindings.emplace_back(name, f); +} +void resolveDeferredBinding(const std::string& name, pybind11::module& m) +{ + auto& deferredBindings = getDeferredBindings(); + auto it = std::find_if( + deferredBindings.begin(), deferredBindings.end(), [&name](const DeferredBinding& binding) { return binding.name == name; } + ); + if (it != deferredBindings.end()) + it->bind(m); +} - template - void addVecType(pybind11::module& m, const std::string name) - { - using ScalarT = typename VecT::value_type; +template +void defineVecType(pybind11::class_& vec) +{ + using ScalarT = typename VecT::value_type; - auto constexpr length = VecT::length(); - static_assert(length >= 2 && length <= 4, "Unsupported number of components"); + auto constexpr length = VecT::length(); + static_assert(length >= 2 && length <= 4, "Unsupported number of components"); - pybind11::class_ vec(m, name.c_str()); + vec.def_readwrite("x", &VecT::x); + vec.def_readwrite("y", &VecT::y); + if constexpr (length >= 3) + vec.def_readwrite("z", &VecT::z); + if constexpr (length >= 4) + vec.def_readwrite("w", &VecT::w); - vec.def_readwrite("x", &VecT::x); - vec.def_readwrite("y", &VecT::y); - if constexpr (length >= 3) vec.def_readwrite("z", &VecT::z); - if constexpr (length >= 4) vec.def_readwrite("w", &VecT::w); + auto initEmpty = []() { return VecT(ScalarT(0)); }; + vec.def(pybind11::init(initEmpty)); - auto initEmpty = []() { return VecT(ScalarT(0)); }; - vec.def(pybind11::init(initEmpty)); + auto initScalar = [](ScalarT c) { return VecT(c); }; + vec.def(pybind11::init(initScalar), "c"_a); - auto initScalar = [](ScalarT c) { return VecT(c); }; - vec.def(pybind11::init(initScalar), "c"_a); + if constexpr (length == 2) + { + auto initVector = [](ScalarT x, ScalarT y) { return VecT(x, y); }; + vec.def(pybind11::init(initVector), "x"_a, "y"_a); + } + else if constexpr (length == 3) + { + auto initVector = [](ScalarT x, ScalarT y, ScalarT z) { return VecT(x, y, z); }; + vec.def(pybind11::init(initVector), "x"_a, "y"_a, "z"_a); + } + else if constexpr (length == 4) + { + auto initVector = [](ScalarT x, ScalarT y, ScalarT z, ScalarT w) { return VecT(x, y, z, w); }; + vec.def(pybind11::init(initVector), "x"_a, "y"_a, "z"_a, "w"_a); + } - if constexpr (length == 2) - { - auto initVector = [](ScalarT x, ScalarT y) { return VecT(x, y); }; - vec.def(pybind11::init(initVector), "x"_a, "y"_a); - } - else if constexpr (length == 3) - { - auto initVector = [](ScalarT x, ScalarT y, ScalarT z) { return VecT(x, y, z); }; - vec.def(pybind11::init(initVector), "x"_a, "y"_a, "z"_a); - } - else if constexpr (length == 4) - { - auto initVector = [](ScalarT x, ScalarT y, ScalarT z, ScalarT w) { return VecT(x, y, z, w); }; - vec.def(pybind11::init(initVector), "x"_a, "y"_a, "z"_a, "w"_a); - } + // Casting float16_t <-> float vectors. + // This allows explicit casts, e.g., float16_t3(c), where c is a float3 in python. + if constexpr (std::is_same::value) + { + using floatN = math::vector; + auto initVector = [](floatN v) { return VecT(v); }; + vec.def(pybind11::init(initVector), "v"_a); + } + else if constexpr (std::is_same::value) + { + using float16_tN = math::vector; + auto initVector = [](float16_tN v) { return VecT(v); }; + vec.def(pybind11::init(initVector), "v"_a); + } - // Casting float16_t <-> float vectors. - // This allows explicit casts, e.g., float16_t3(c), where c is a float3 in python. - if constexpr (std::is_same::value) - { - using floatN = glm::vec; - auto initVector = [](floatN v) { return VecT(v); }; - vec.def(pybind11::init(initVector), "v"_a); - } - else if constexpr (std::is_same::value) - { - using float16_tN = tfloat16_vec; - auto initVector = [](float16_tN v) { return VecT(v); }; - vec.def(pybind11::init(initVector), "v"_a); - } + auto repr = [](const VecT& v) + { + std::string str = + math::ScalarTraits::name + std::to_string(VecT::length()) + "(" + math::to_string(v[0]); + for (int i = 1; i < VecT::length(); i++) + str += ", " + math::to_string(v[i]); + str += ")"; + return str; + }; + vec.def("__repr__", repr); - auto repr = [](const VecT& v) { return Falcor::to_string(v); }; - vec.def("__repr__", repr); - - auto str = [](const VecT& v) { - auto tostr = [](const ScalarT& s) { - if constexpr (std::is_same::value) return to_string(s); - else return std::to_string(s); - }; - std::string vec = "[" + tostr(v[0]); - for (int i = 1; i < VecT::length(); i++) - { - vec += ", " + tostr(v[i]); - } - vec += "]"; - return vec; - }; - vec.def("__str__", str); - - vec.def(pybind11::pickle( - [&] (const VecT &v) { - pybind11::tuple t(length); - for (auto i = 0; i < length; ++i) t[i] = v[i]; - return t; - }, - [&] (pybind11::tuple t) { - if (t.size() != length) throw RuntimeError("Invalid state!"); - VecT v; - for (auto i = 0; i < length; ++i) v[i] = t[i].cast(); - return v; - } - )); - - if constexpr (withOperators) + auto str = [](const VecT& v) + { + std::string str = "[" + math::to_string(v[0]); + for (int i = 1; i < VecT::length(); i++) + str += ", " + math::to_string(v[i]); + str += "]"; + return str; + }; + vec.def("__str__", str); + + vec.def(pybind11::pickle( + [&](const VecT& v) + { + pybind11::tuple t(length); + for (auto i = 0; i < length; ++i) + t[i] = v[i]; + return t; + }, + [&](pybind11::tuple t) { - vec.def(pybind11::self + pybind11::self); - vec.def(pybind11::self += pybind11::self); - vec.def(pybind11::self - pybind11::self); - vec.def(pybind11::self -= pybind11::self); - vec.def(pybind11::self * pybind11::self); - vec.def(pybind11::self *= pybind11::self); - vec.def(pybind11::self / pybind11::self); - vec.def(pybind11::self /= pybind11::self); - vec.def(pybind11::self + ScalarT()); - vec.def(pybind11::self += ScalarT()); - vec.def(pybind11::self - ScalarT()); - vec.def(pybind11::self -= ScalarT()); - vec.def(pybind11::self * ScalarT()); - vec.def(pybind11::self *= ScalarT()); - vec.def(pybind11::self / ScalarT()); - vec.def(pybind11::self /= ScalarT()); + if (t.size() != length) + throw RuntimeError("Invalid state!"); + VecT v; + for (auto i = 0; i < length; ++i) + v[i] = t[i].cast(); + return v; } - } + )); - void initModule(pybind11::module& m) + if constexpr (WithOperators) { - using namespace pybind11::literals; - - // bool2, bool3, bool4 - addVecType(m, "bool2"); - addVecType(m, "bool3"); - addVecType(m, "bool4"); - - // float2, float3, float4 - addVecType(m, "float2"); - addVecType(m, "float3"); - addVecType(m, "float4"); - - // int2, int3, int4 - addVecType(m, "int2"); - addVecType(m, "int3"); - addVecType(m, "int4"); - - // uint2, uint3, uint4 - addVecType(m, "uint2"); - addVecType(m, "uint3"); - addVecType(m, "uint4"); - - // float3x3, float4x4 - // Note: We register these as simple data types without any operations because semantics may change in the future. - pybind11::class_(m, "float3x3"); - pybind11::class_(m, "float4x4"); - - // float16_t types - pybind11::class_(m, "float16_t"); - addVecType(m, "float16_t2"); - addVecType(m, "float16_t3"); - addVecType(m, "float16_t4"); - - // ObjectID - pybind11::class_(m, "ObjectID"); - - // Plugins. - m.def("loadPlugin", [](const std::string& name) { - PluginManager::instance().loadPluginByName(name); - }, "name"_a); - - // Bind all deferred bindings. - for (auto& binding : getDeferredBindings()) - binding.bind(m); - getDeferredBindings().clear(); - - // Retain a handle to the module to add new bindings at runtime. - sModule = m; - - // Register atexit handler to automatically release the module handle on exit. - auto atexit = pybind11::module_::import("atexit"); - atexit.attr("register")(pybind11::cpp_function([]() { - sModule.release(); - })); + vec.def(pybind11::self + pybind11::self); + vec.def(pybind11::self += pybind11::self); + vec.def(pybind11::self - pybind11::self); + vec.def(pybind11::self -= pybind11::self); + vec.def(pybind11::self * pybind11::self); + vec.def(pybind11::self *= pybind11::self); + vec.def(pybind11::self / pybind11::self); + vec.def(pybind11::self /= pybind11::self); + vec.def(pybind11::self + ScalarT()); + vec.def(pybind11::self += ScalarT()); + vec.def(pybind11::self - ScalarT()); + vec.def(pybind11::self -= ScalarT()); + vec.def(pybind11::self * ScalarT()); + vec.def(pybind11::self *= ScalarT()); + vec.def(pybind11::self / ScalarT()); + vec.def(pybind11::self /= ScalarT()); } +} +void initModule(pybind11::module& m) +{ + using namespace pybind11::literals; + + pybind11::class_(m, "float16_t"); + + // Declare all vector types. + // We do this before defining the bindings to allow explicit casts between vector types. + + pybind11::class_ bool2Type(m, "bool2"); + pybind11::class_ bool3Type(m, "bool3"); + pybind11::class_ bool4Type(m, "bool4"); + + pybind11::class_ int2Type(m, "int2"); + pybind11::class_ int3Type(m, "int3"); + pybind11::class_ int4Type(m, "int4"); + + pybind11::class_ uint2Type(m, "uint2"); + pybind11::class_ uint3Type(m, "uint3"); + pybind11::class_ uint4Type(m, "uint4"); + + pybind11::class_ float2Type(m, "float2"); + pybind11::class_ float3Type(m, "float3"); + pybind11::class_ float4Type(m, "float4"); + + pybind11::class_ float16_t2Type(m, "float16_t2"); + pybind11::class_ float16_t3Type(m, "float16_t3"); + pybind11::class_ float16_t4Type(m, "float16_t4"); + + // bool2, bool3, bool4 + defineVecType(bool2Type); + defineVecType(bool3Type); + defineVecType(bool4Type); + + // int2, int3, int4 + defineVecType(int2Type); + defineVecType(int3Type); + defineVecType(int4Type); + + // uint2, uint3, uint4 + defineVecType(uint2Type); + defineVecType(uint3Type); + defineVecType(uint4Type); + + // float2, float3, float4 + defineVecType(float2Type); + defineVecType(float3Type); + defineVecType(float4Type); + + // float16_t2, float16_t3, float16_t4 + defineVecType(float16_t2Type); + defineVecType(float16_t3Type); + defineVecType(float16_t4Type); + + // float3x3, float4x4 + // Note: We register these as simple data types without any operations because semantics may change in the future. + pybind11::class_(m, "float3x3"); + pybind11::class_(m, "float4x4"); + + // ObjectID + pybind11::class_(m, "ObjectID"); + + // Plugins. + m.def( + "load_plugin", [](const std::string& name) { PluginManager::instance().loadPluginByName(name); }, "name"_a + ); + m.def( + "loadPlugin", [](const std::string& name) { PluginManager::instance().loadPluginByName(name); }, "name"_a + ); // PYTHONDEPRECATED + + // Bind all deferred bindings. + for (auto& binding : getDeferredBindings()) + binding.bind(m); + getDeferredBindings().clear(); + + // Retain a handle to the module to add new bindings at runtime. + sModule = m; + + // Register atexit handler to automatically release the module handle on exit. + auto atexit = pybind11::module_::import("atexit"); + atexit.attr("register")(pybind11::cpp_function([]() { sModule.release(); })); } + +} // namespace Falcor::ScriptBindings diff --git a/Source/Falcor/Utils/Scripting/ScriptBindings.h b/Source/Falcor/Utils/Scripting/ScriptBindings.h index 9f1910b51..91598285f 100644 --- a/Source/Falcor/Utils/Scripting/ScriptBindings.h +++ b/Source/Falcor/Utils/Scripting/ScriptBindings.h @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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,242 +27,255 @@ **************************************************************************/ #pragma once #include "Core/Errors.h" +#include "Core/ObjectPython.h" #include -#include // export -#include // export +#include +#include #include #include #include namespace Falcor::ScriptBindings { - /** Callback function to add python bindings to a module. - */ - using RegisterBindingFunc = std::function; - - /** Initialize the binding module. - This is called from the pybind11 module to initialize bindings (see FalcorPython.cpp). - First, this function will go through the list of all deferred bindings and execute them. - Next, it stores a reference to the python module to allow further bindings to be added at runtime. - The reference to the module is automatically released when the module is unloaded. - */ - FALCOR_API void initModule(pybind11::module& m); - - /** Register a script binding function. - The binding function will be called when scripting is initialized. - \param[in] f Function to be called for registering the binding. - */ - FALCOR_API void registerBinding(RegisterBindingFunc f); +/** + * Callback function to add python bindings to a module. + */ +using RegisterBindingFunc = std::function; + +/** + * Initialize the binding module. + * This is called from the pybind11 module to initialize bindings (see FalcorPython.cpp). + * First, this function will go through the list of all deferred bindings and execute them. + * Next, it stores a reference to the python module to allow further bindings to be added at runtime. + * The reference to the module is automatically released when the module is unloaded. + */ +FALCOR_API void initModule(pybind11::module& m); + +/** + * Register a script binding function. + * The binding function will be called when scripting is initialized. + * @param[in] f Function to be called for registering the binding. + */ +FALCOR_API void registerBinding(RegisterBindingFunc f); + +/** + * Register a deferred script binding function. + * This is used to register a script binding function before scripting is initialized. + * The execution of the binding function is deferred until scripting is finally initialized. + * Note: This is called from `registerBinding()` if called before scripting is initialized + * and from the FALCOR_SCRIPT_BINDING macro. + * @param[in] name Name if the binding. + * @param[in] f Function to be called for registering the binding. + */ +FALCOR_API void registerDeferredBinding(const std::string& name, RegisterBindingFunc f); + +/** + * Resolve a deferred script binding by name. + * This immediately executes the deferred binding function registered to the given name + * and can be used to control the order of execution of the binding functions. + * Note: This is used by the FALCOR_SCRIPT_BINDING_DEPENDENCY macro to ensure dependent bindings + * are registered ahead of time. + * @param[in] name Name of the binding to resolve. + * @param[in] m Python module. + */ +FALCOR_API void resolveDeferredBinding(const std::string& name, pybind11::module& m); + +/************************************************************************/ +/* Helpers */ +/************************************************************************/ + +/** + * Adds binary and/or operators to a Python enum. + * This allows the enum to be used as a set of flags instead of just a list of choices. + * @param[in] e Enum to be extended. + */ +template +static void addEnumBinaryOperators(pybind11::enum_& e) +{ + e.def("__and__", [](const T& value1, const T& value2) { return T(int(value1) & int(value2)); }); + e.def("__or__", [](const T& value1, const T& value2) { return T(int(value1) | int(value2)); }); +} - /** Register a deferred script binding function. - This is used to register a script binding function before scripting is initialized. - The execution of the binding function is deferred until scripting is finally initialized. - Note: This is called from `registerBinding()` if called before scripting is initialized - and from the FALCOR_SCRIPT_BINDING macro. - \param[in] name Name if the binding. - \param[in] f Function to be called for registering the binding. - */ - FALCOR_API void registerDeferredBinding(const std::string& name, RegisterBindingFunc f); +/** + * Returns the string representation of a value of a registered type. + * @param[in] value Value to be converted to a string. + * @return Returns the string representation. + */ +template +static std::string repr(const T& value) +{ + return pybind11::repr(pybind11::cast(value)); +} - /** Resolve a deferred script binding by name. - This immediately executes the deferred binding function registered to the given name - and can be used to control the order of execution of the binding functions. - Note: This is used by the FALCOR_SCRIPT_BINDING_DEPENDENCY macro to ensure dependent bindings - are registered ahead of time. - \param[in] name Name of the binding to resolve. - \param[in] m Python module. - */ - FALCOR_API void resolveDeferredBinding(const std::string &name, pybind11::module& m); +/** + * This helper creates script bindings for simple data-only structs. + * + * The structs are made "serializable" to/from python code by adding a __init__ (constructor) + * and a __repr__ implementation. The __init__ function takes kwargs and populates all the + * struct fields that have been registered with the field() method on this helper. + * The __repr__ implementation prints all the fields registered with the field() method. + * + * This helper also makes the python type "pickle-able" by providing a __getstate__ + * and __setstate__ implementation. + * + * Lets assume we have a C++ struct: + * + * struct Example + * { + * int foo; + * std::string bar; + * }; + * + * We can register bindings using: + * + * SerializableStruct example(m, "Example"); + * example.field("foo", &Example::foo); + * example.field("bar", &Example::bar); + * + * In Python, we can then use the constructor like this: + * + * example = Example(foo=123, bar="test") + * + * Also, to serialize the instance into a string we can use repr: + * + * repr(example) + * + * which gives back a string like: Example(foo=123, bar="test") + */ +template +struct SerializableStruct : public pybind11::class_ +{ + using This = SerializableStruct; - /************************************************************************/ - /* Helpers */ - /************************************************************************/ + static_assert(std::is_default_constructible_v && std::is_copy_constructible_v); - /** Adds binary and/or operators to a Python enum. - This allows the enum to be used as a set of flags instead of just a list of choices. - \param[in] e Enum to be extended. - */ - template - static void addEnumBinaryOperators(pybind11::enum_& e) + template + SerializableStruct(pybind11::handle scope, const char* name, const Extra&... extra) + : pybind11::class_(scope, name, extra...) { - e.def("__and__", [](const T& value1, const T& value2) { return T(int(value1) & int(value2)); }); - e.def("__or__", [](const T& value1, const T& value2) { return T(int(value1) | int(value2)); }); + This::info().name = name; + auto initFunc = [](const pybind11::kwargs& args) { return This::init(args); }; + this->def(pybind11::init(initFunc)); + this->def(pybind11::init<>()); + this->def("__repr__", This::repr); + this->def(pybind11::pickle( + [](const T& obj) { return This::getState(obj); }, + [](pybind11::tuple t) + { + T obj; + This::setState(obj, t); + return obj; + } + )); } - /** Returns the string representation of a value of a registered type. - \param[in] value Value to be converted to a string. - \return Returns the string representation. - */ - template - static std::string repr(const T& value) + template + This& field(const char* name, D std::remove_pointer_t::*pm, const Extra&... extra) { - return pybind11::repr(pybind11::cast(value)); - } - - /** This helper creates script bindings for simple data-only structs. - - The structs are made "serializable" to/from python code by adding a __init__ (constructor) - and a __repr__ implementation. The __init__ function takes kwargs and populates all the - struct fields that have been registered with the field() method on this helper. - The __repr__ implementation prints all the fields registered with the field() method. - - This helper also makes the python type "pickle-able" by providing a __getstate__ - and __setstate__ implementation. - - Lets assume we have a C++ struct: - - struct Example - { - int foo; - std::string bar; - }; + this->def_readwrite(name, pm, extra...); - We can register bindings using: + auto getter = [pm](const T& obj) -> pybind11::object { return pybind11::cast(obj.*pm); }; - SerializableStruct example(m, "Example"); - example.field("foo", &Example::foo); - example.field("bar", &Example::bar); + auto setter = [pm](T& obj, pybind11::handle h) { obj.*pm = h.cast(); }; - In Python, we can then use the constructor like this: + std::string nameStr(name); + auto printer = [pm, nameStr](const T& obj) { return nameStr + "=" + std::string(pybind11::repr(pybind11::cast(obj.*pm))); }; - example = Example(foo=123, bar="test") - - Also, to serialize the instance into a string we can use repr: - - repr(example) + auto& info = This::info(); + auto field = Field{getter, setter, printer}; + info.fields.emplace_back(field); + info.fieldByName[name] = field; + return *this; + } - which gives back a string like: Example(foo=123, bar="test") - */ - template - struct SerializableStruct : public pybind11::class_ +private: + static T init(const pybind11::kwargs& args) { - using This = SerializableStruct; - - static_assert(std::is_default_constructible_v && std::is_copy_constructible_v); - - template - SerializableStruct(pybind11::handle scope, const char* name, const Extra&... extra) - : pybind11::class_(scope, name, extra...) - { - This::info().name = name; - auto initFunc = [](const pybind11::kwargs& args) { return This::init(args); }; - this->def(pybind11::init(initFunc)); - this->def(pybind11::init<>()); - this->def("__repr__", This::repr); - this->def(pybind11::pickle( - [] (const T &obj) { return This::getState(obj); }, - [] (pybind11::tuple t) { T obj; This::setState(obj, t); return obj; } - )); - } - - template - This& field(const char* name, D std::remove_pointer_t::* pm, const Extra&... extra) - { - this->def_readwrite(name, pm, extra...); - - auto getter = [pm](const T& obj) -> pybind11::object - { - return pybind11::cast(obj.*pm); - }; - - auto setter = [pm](T& obj, pybind11::handle h) - { - obj.*pm = h.cast(); - }; - - std::string nameStr(name); - auto printer = [pm, nameStr](const T& obj) - { - return nameStr + "=" + std::string(pybind11::repr(pybind11::cast(obj.*pm))); - }; - - auto &info = This::info(); - auto field = Field { getter, setter, printer }; - info.fields.emplace_back(field); - info.fieldByName[name] = field; - return *this; - } + T obj; + const auto& fieldByName = This::info().fieldByName; + for (auto a : args) + fieldByName.at(a.first.cast()).setter(obj, a.second); + return obj; + } - private: - static T init(const pybind11::kwargs& args) + static std::string repr(const T& obj) + { + const auto& info = This::info(); + std::string s = info.name + '('; + bool first = true; + for (const auto& f : info.fields) { - T obj; - const auto& fieldByName = This::info().fieldByName; - for (auto a : args) fieldByName.at(a.first.cast()).setter(obj, a.second); - return obj; - } - - static std::string repr(const T& obj) - { - const auto& info = This::info(); - std::string s = info.name + '('; - bool first = true; - for (const auto f : info.fields) - { - if (!first) s += ", "; - first = false; - s += f.printer(obj); - } - return s + ')'; + if (!first) + s += ", "; + first = false; + s += f.printer(obj); } + return s + ')'; + } - static pybind11::tuple getState(const T &obj) + static pybind11::tuple getState(const T& obj) + { + const auto& fields = This::info().fields; + pybind11::tuple t(fields.size()); + for (size_t i = 0; i < fields.size(); ++i) { - const auto& fields = This::info().fields; - pybind11::tuple t(fields.size()); - for (size_t i = 0; i < fields.size(); ++i) - { - t[i] = fields[i].getter(obj); - } - return t; + t[i] = fields[i].getter(obj); } + return t; + } - static void setState(T &obj, pybind11::tuple t) + static void setState(T& obj, pybind11::tuple t) + { + const auto& fields = This::info().fields; + if (t.size() != fields.size()) + throw RuntimeError("Invalid state!"); + for (size_t i = 0; i < fields.size(); ++i) { - const auto& fields = This::info().fields; - if (t.size() != fields.size()) throw RuntimeError("Invalid state!"); - for (size_t i = 0; i < fields.size(); ++i) - { - fields[i].setter(obj, t[i]); - } + fields[i].setter(obj, t[i]); } + } - struct Field - { - std::function getter; - std::function setter; - std::function printer; - }; - - struct Info - { - std::string name; - std::vector fields; - std::unordered_map fieldByName; - }; + struct Field + { + std::function getter; + std::function setter; + std::function printer; + }; - static Info& info() - { - static Info staticInfo; - return staticInfo; - } + struct Info + { + std::string name; + std::vector fields; + std::unordered_map fieldByName; }; + static Info& info() + { + static Info staticInfo; + return staticInfo; + } +}; + #ifndef _staticlibrary -#define FALCOR_SCRIPT_BINDING(_name) \ - static void ScriptBinding##_name(pybind11::module& m); \ - struct ScriptBindingRegisterer##_name { \ - ScriptBindingRegisterer##_name() \ - { \ - ScriptBindings::registerDeferredBinding(#_name, ScriptBinding##_name); \ - } \ - } gScriptBinding##_name; \ +#define FALCOR_SCRIPT_BINDING(_name) \ + static void ScriptBinding##_name(pybind11::module& m); \ + struct ScriptBindingRegisterer##_name \ + { \ + ScriptBindingRegisterer##_name() \ + { \ + ScriptBindings::registerDeferredBinding(#_name, ScriptBinding##_name); \ + } \ + } gScriptBinding##_name; \ static void ScriptBinding##_name(pybind11::module& m) /* over to the user for the braces */ -#define FALCOR_SCRIPT_BINDING_DEPENDENCY(_name) \ - ScriptBindings::resolveDeferredBinding(#_name, m); +#define FALCOR_SCRIPT_BINDING_DEPENDENCY(_name) ScriptBindings::resolveDeferredBinding(#_name, m); #else -#define FALCOR_SCRIPT_BINDING(_name) static_assert(false, "Using FALCOR_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."); +#define FALCOR_SCRIPT_BINDING(_name) \ + static_assert( \ + false, \ + "Using FALCOR_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 // _staticlibrary -} +} // namespace Falcor::ScriptBindings diff --git a/Source/Falcor/Utils/Scripting/ScriptWriter.h b/Source/Falcor/Utils/Scripting/ScriptWriter.h index 560e17e5e..eb26d93a6 100644 --- a/Source/Falcor/Utils/Scripting/ScriptWriter.h +++ b/Source/Falcor/Utils/Scripting/ScriptWriter.h @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -35,89 +35,81 @@ namespace Falcor { - /** Helper class to write Python script code including: - - calling functions - - calling member functions - - getting/setting properties - - Arguments are automatically converted from C++ types to Python code using `repr()`. - */ - class ScriptWriter +/** + * Helper class to write Python script code including: + * - calling functions + * - calling member functions + * - getting/setting properties + * + * Arguments are automatically converted from C++ types to Python code using `repr()`. + */ +class ScriptWriter +{ +public: + struct VariableName { - public: - struct VariableName - { - std::string name; - explicit VariableName(const std::string& name) : name(name) {} - }; - - static std::string makeFunc(const std::string& func) - { - return func + "()\n"; - } - - template - static std::string getArgString(const T& 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; - } + std::string name; + explicit VariableName(const std::string& name) : name(name) {} + }; - static std::string makeGetProperty(const std::string& var, const std::string& property) - { - return var + "." + property + "\n"; - } + static std::string makeFunc(const std::string& func) { return func + "()\n"; } - template - static std::string makeSetProperty(const std::string& var, const std::string& property, Arg arg) - { - return var + "." + property + " = " + getArgString(arg) + "\n"; - } + template + static std::string getArgString(const T& arg); - static std::string getPathString(std::filesystem::path path, bool stripDataDirs = true) - { - if (stripDataDirs) path = stripDataDirectories(path); - std::string str = path.string(); - std::replace(str.begin(), str.end(), '\\', '/'); - return str; - } - }; + 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 - std::string ScriptWriter::getArgString(const T& arg) + template + static std::string makeMemberFunc(const std::string& var, const std::string& func, Arg first, Args... args) { - return ScriptBindings::repr(arg); + std::string s(var); + s += std::string(".") + makeFunc(func, first, args...); + return s; } - template<> - inline std::string ScriptWriter::getArgString(const Dictionary& dictionary) + static std::string makeGetProperty(const std::string& var, const std::string& property) { return var + "." + property + "\n"; } + + template + static std::string makeSetProperty(const std::string& var, const std::string& property, Arg arg) { - return dictionary.toString(); + return var + "." + property + " = " + getArgString(arg) + "\n"; } - template<> - inline std::string ScriptWriter::getArgString(const ScriptWriter::VariableName& varName) + static std::string getPathString(std::filesystem::path path, bool stripDataDirs = true) { - return varName.name; + if (stripDataDirs) + path = stripDataDirectories(path); + std::string str = path.string(); + std::replace(str.begin(), str.end(), '\\', '/'); + return str; } +}; +template +std::string ScriptWriter::getArgString(const T& arg) +{ + return ScriptBindings::repr(arg); } + +template<> +inline std::string ScriptWriter::getArgString(const Dictionary& dictionary) +{ + return dictionary.toString(); +} + +template<> +inline std::string ScriptWriter::getArgString(const ScriptWriter::VariableName& varName) +{ + return varName.name; +} + +} // namespace Falcor diff --git a/Source/Falcor/Utils/Scripting/Scripting.cpp b/Source/Falcor/Utils/Scripting/Scripting.cpp index f478a69e1..59b04e8d7 100644 --- a/Source/Falcor/Utils/Scripting/Scripting.cpp +++ b/Source/Falcor/Utils/Scripting/Scripting.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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,140 +34,137 @@ namespace Falcor { - const FileDialogFilterVec Scripting::kFileExtensionFilters = { { "py", "Script Files"} }; - bool Scripting::sRunning = false; - std::unique_ptr Scripting::sDefaultContext; +const FileDialogFilterVec Scripting::kFileExtensionFilters = {{"py", "Script Files"}}; +bool Scripting::sRunning = false; +std::unique_ptr Scripting::sDefaultContext; - void Scripting::start() +void Scripting::start() +{ + if (!sRunning) { - if (!sRunning) - { - sRunning = true; -#if FALCOR_WINDOWS - static std::wstring pythonHome = (getRuntimeDirectory() / "pythondist").c_str(); + sRunning = true; + +#ifdef FALCOR_PYTHON_EXECUTABLE + static std::filesystem::path pythonHome{std::filesystem::path{FALCOR_PYTHON_EXECUTABLE}.parent_path()}; #else - static std::wstring pythonHome = string_2_wstring((getRuntimeDirectory() / "pythondist").string()); + static std::filesystem::path pythonHome{getRuntimeDirectory() / "pythondist"}; #endif - Py_SetPythonHome(pythonHome.c_str()); - - try - { - pybind11::initialize_interpreter(); - sDefaultContext.reset(new Context()); - // Extend python search path with the directory containing the falcor python module. - std::string pythonPath = (getRuntimeDirectory() / "python").generic_string(); - Scripting::runScript(fmt::format("import sys; sys.path.append(\"{}\")\n", pythonPath)); - // Set an environment variable to inform the falcor module that it's being loaded from an embedded interpreter. - Scripting::runScript("import os; os.environ[\"FALCOR_EMBEDDED_PYTHON\"] = \"1\""); - - // Import falcor into default scripting context. - Scripting::runScript("from falcor import *"); - } - catch (const std::exception& e) - { - throw RuntimeError("Failed to start the Python interpreter: {}", e.what()); - } - } - } + Py_SetPythonHome(pythonHome.wstring().c_str()); - void Scripting::shutdown() - { - if (sRunning) + try { - sRunning = false; - sDefaultContext.reset(); - pybind11::finalize_interpreter(); + pybind11::initialize_interpreter(); + sDefaultContext.reset(new Context()); + // Extend python search path with the directory containing the falcor python module. + std::string pythonPath = (getRuntimeDirectory() / "python").generic_string(); + Scripting::runScript(fmt::format("import sys; sys.path.append(\"{}\")\n", pythonPath)); + // Set an environment variable to inform the falcor module that it's being loaded from an embedded interpreter. + Scripting::runScript("import os; os.environ[\"FALCOR_EMBEDDED_PYTHON\"] = \"1\""); + + // Import falcor into default scripting context. + Scripting::runScript("from falcor import *"); + } + catch (const std::exception& e) + { + throw RuntimeError("Failed to start the Python interpreter: {}", e.what()); } } +} - Scripting::Context& Scripting::getDefaultContext() - { - FALCOR_ASSERT(sDefaultContext); - return *sDefaultContext; - } - - Scripting::Context Scripting::getCurrentContext() +void Scripting::shutdown() +{ + if (sRunning) { - return Context(pybind11::globals()); + sRunning = false; + sDefaultContext.reset(); + pybind11::finalize_interpreter(); } +} - class RedirectStream - { - public: - RedirectStream(const std::string& stream = "stdout") - : mStream(stream) - { - auto m = pybind11::module::import("sys"); - mOrigStream = m.attr(mStream.c_str()); - mBuffer = pybind11::module::import("io").attr("StringIO")(); - m.attr(mStream.c_str()) = mBuffer; - } +Scripting::Context& Scripting::getDefaultContext() +{ + FALCOR_ASSERT(sDefaultContext); + return *sDefaultContext; +} - ~RedirectStream() - { - pybind11::module::import("sys").attr(mStream.c_str()) = mOrigStream; - } +Scripting::Context Scripting::getCurrentContext() +{ + return Context(pybind11::globals()); +} - operator std::string() const - { - mBuffer.attr("seek")(0); - return pybind11::str(mBuffer.attr("read")()); - } +class RedirectStream +{ +public: + RedirectStream(const std::string& stream = "stdout") : mStream(stream) + { + auto m = pybind11::module::import("sys"); + mOrigStream = m.attr(mStream.c_str()); + mBuffer = pybind11::module::import("io").attr("StringIO")(); + m.attr(mStream.c_str()) = mBuffer; + } - private: - std::string mStream; - pybind11::object mOrigStream; - pybind11::object mBuffer; - }; + ~RedirectStream() { pybind11::module::import("sys").attr(mStream.c_str()) = mOrigStream; } - static Scripting::RunResult runScript(const std::string& script, pybind11::dict& globals, bool captureOutput) + operator std::string() const { - Scripting::RunResult result; + mBuffer.attr("seek")(0); + return pybind11::str(mBuffer.attr("read")()); + } - if (captureOutput) - { - RedirectStream rstdout("stdout"); - RedirectStream rstderr("stderr"); - pybind11::exec(script.c_str(), globals); - result.out = rstdout; - result.err = rstderr; - } - else - { - pybind11::exec(script.c_str(), globals); - } +private: + std::string mStream; + pybind11::object mOrigStream; + pybind11::object mBuffer; +}; - return result; - } +static Scripting::RunResult runScript(const std::string& script, pybind11::dict& globals, bool captureOutput) +{ + Scripting::RunResult result; - Scripting::RunResult Scripting::runScript(const std::string& script, Context& context, bool captureOutput) + if (captureOutput) { - return Falcor::runScript(script, context.mGlobals, captureOutput); + RedirectStream rstdout("stdout"); + RedirectStream rstderr("stderr"); + pybind11::exec(script.c_str(), globals); + result.out = rstdout; + result.err = rstderr; } - - Scripting::RunResult Scripting::runScriptFromFile(const std::filesystem::path& path, Context& context, bool captureOutput) + else { - if (std::filesystem::exists(path)) - { - std::string absFile = std::filesystem::absolute(path).string(); - context.setObject("__file__", absFile); - auto result = Scripting::runScript(readFile(path), context, captureOutput); - context.setObject("__file__", nullptr); // There seems to be no API on pybind11::dict to remove a key. - return result; - } - throw RuntimeError("Failed to run script. Can't find the file '{}'.", path); + pybind11::exec(script.c_str(), globals); } - std::string Scripting::interpretScript(const std::string& script, Context& context) - { - pybind11::module code = pybind11::module::import("code"); - pybind11::object InteractiveInterpreter = code.attr("InteractiveInterpreter"); - auto interpreter = InteractiveInterpreter(context.mGlobals); - auto runsource = interpreter.attr("runsource"); + return result; +} - RedirectStream rstdout("stdout"); - RedirectStream rstderr("stderr"); - runsource(script); - return std::string(rstdout) + std::string(rstderr); +Scripting::RunResult Scripting::runScript(const std::string& script, Context& context, bool captureOutput) +{ + return Falcor::runScript(script, context.mGlobals, captureOutput); +} + +Scripting::RunResult Scripting::runScriptFromFile(const std::filesystem::path& path, Context& context, bool captureOutput) +{ + if (std::filesystem::exists(path)) + { + std::string absFile = std::filesystem::absolute(path).string(); + context.setObject("__file__", absFile); + auto result = Scripting::runScript(readFile(path), context, captureOutput); + context.setObject("__file__", nullptr); // There seems to be no API on pybind11::dict to remove a key. + return result; } + throw RuntimeError("Failed to run script. Can't find the file '{}'.", path); +} + +std::string Scripting::interpretScript(const std::string& script, Context& context) +{ + pybind11::module code = pybind11::module::import("code"); + pybind11::object InteractiveInterpreter = code.attr("InteractiveInterpreter"); + auto interpreter = InteractiveInterpreter(context.mGlobals); + auto runsource = interpreter.attr("runsource"); + + RedirectStream rstdout("stdout"); + RedirectStream rstderr("stderr"); + runsource(script); + return std::string(rstdout) + std::string(rstderr); } +} // namespace Falcor diff --git a/Source/Falcor/Utils/Scripting/Scripting.h b/Source/Falcor/Utils/Scripting/Scripting.h index a2dacaf11..d5fd1130e 100644 --- a/Source/Falcor/Utils/Scripting/Scripting.h +++ b/Source/Falcor/Utils/Scripting/Scripting.h @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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,127 +36,138 @@ namespace Falcor { - class FALCOR_API Scripting +class FALCOR_API Scripting +{ +public: + static const FileDialogFilterVec kFileExtensionFilters; + + /** + * Represents a context for executing scripts. + * Wraps the globals dictionary that is passed to the script on execution. + * The context can be used to pass/retrieve variables to/from the executing script. + */ + class Context { public: - static const FileDialogFilterVec kFileExtensionFilters; + Context(pybind11::dict globals) : mGlobals(globals) {} - /** Represents a context for executing scripts. - Wraps the globals dictionary that is passed to the script on execution. - The context can be used to pass/retrieve variables to/from the executing script. - */ - class Context + Context() { - public: - Context(pybind11::dict globals) : mGlobals(globals) {} + // Copy __builtins__ to our empty globals dictionary. + mGlobals["__builtins__"] = pybind11::globals()["__builtins__"]; + } - Context() - { - // Copy __builtins__ to our empty globals dictionary. - mGlobals["__builtins__"] = pybind11::globals()["__builtins__"]; - } + template + 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; + }; - template - 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; - }; - - template - std::vector> getObjects() + template + std::vector> getObjects() + { + std::vector> v; + for (const auto& l : mGlobals) { - std::vector> v; - for (const auto& l : mGlobals) + try { - try + if (!l.second.is_none()) { - if(!l.second.is_none()) - { - v.push_back(ObjectDesc(l.first.cast(), l.second.cast())); - } + v.push_back(ObjectDesc(l.first.cast(), l.second.cast())); } - catch (const std::exception&) {} } - return v; - } - - template - void setObject(const std::string& name, T obj) - { - mGlobals[name.c_str()] = obj; - } - - template - T getObject(const std::string& name) const - { - return mGlobals[name.c_str()].cast(); - } - - bool containsObject(const std::string& name) const - { - return mGlobals.contains(name.c_str()); + catch (const std::exception&) + {} } + return v; + } - private: - friend class Scripting; - pybind11::dict mGlobals; - }; - - /** Starts the script engine. - This will initialize the Python interpreter and setup the default context. - */ - static void start(); - - /** Shuts the script engine down. - */ - static void shutdown(); - - /** Returns true if the script engine is running. - */ - static bool isRunning() { return sRunning; } - - /** Returns the default context. - */ - static Context& getDefaultContext(); - - /** Returns the context of the currently executing script. - */ - static Context getCurrentContext(); + template + void setObject(const std::string& name, T obj) + { + mGlobals[name.c_str()] = obj; + } - struct RunResult + template + T getObject(const std::string& name) const { - std::string out; - std::string err; - }; + return mGlobals[name.c_str()].cast(); + } - /** Run a script. - \param[in] script Script to run. - \param[in] context Script execution context. - \param[in] captureOutput Enable capturing stdout/stderr and returning it in RunResult. - \return Returns the captured output if enabled. - */ - static RunResult runScript(const std::string& script, Context& context = getDefaultContext(), bool captureOutput = false); - - /** Run a script from a file. - \param[in] path Path of the script to run. - \param[in] context Script execution context. - \param[in] captureOutput Enable capturing stdout/stderr and returning it in RunResult. - \return Returns the captured output if enabled. - */ - static RunResult runScriptFromFile(const std::filesystem::path& path, Context& context = getDefaultContext(), bool captureOutput = false); - - /** Interpret a script and return the evaluated result. - \param[in] script Script to run. - \param[in] context Script execution context. - \return Returns a string representation of the evaluated result of the script. - */ - static std::string interpretScript(const std::string& script, Context& context = getDefaultContext()); + bool containsObject(const std::string& name) const { return mGlobals.contains(name.c_str()); } private: - static bool sRunning; // TODO: REMOVEGLOBAL - static std::unique_ptr sDefaultContext; // TODO: REMOVEGLOBAL + friend class Scripting; + pybind11::dict mGlobals; }; -} + + /** + * Starts the script engine. + * This will initialize the Python interpreter and setup the default context. + */ + static void start(); + + /** + * Shuts the script engine down. + */ + static void shutdown(); + + /** + * Returns true if the script engine is running. + */ + static bool isRunning() { return sRunning; } + + /** + * Returns the default context. + */ + static Context& getDefaultContext(); + + /** + * Returns the context of the currently executing script. + */ + static Context getCurrentContext(); + + struct RunResult + { + std::string out; + std::string err; + }; + + /** + * Run a script. + * @param[in] script Script to run. + * @param[in] context Script execution context. + * @param[in] captureOutput Enable capturing stdout/stderr and returning it in RunResult. + * @return Returns the captured output if enabled. + */ + static RunResult runScript(const std::string& script, Context& context = getDefaultContext(), bool captureOutput = false); + + /** + * Run a script from a file. + * @param[in] path Path of the script to run. + * @param[in] context Script execution context. + * @param[in] captureOutput Enable capturing stdout/stderr and returning it in RunResult. + * @return Returns the captured output if enabled. + */ + static RunResult runScriptFromFile( + const std::filesystem::path& path, + Context& context = getDefaultContext(), + bool captureOutput = false + ); + + /** + * Interpret a script and return the evaluated result. + * @param[in] script Script to run. + * @param[in] context Script execution context. + * @return Returns a string representation of the evaluated result of the script. + */ + static std::string interpretScript(const std::string& script, Context& context = getDefaultContext()); + +private: + static bool sRunning; // TODO: REMOVEGLOBAL + static std::unique_ptr sDefaultContext; // TODO: REMOVEGLOBAL +}; +} // namespace Falcor diff --git a/Source/Falcor/Utils/Scripting/ndarray.cpp b/Source/Falcor/Utils/Scripting/ndarray.cpp new file mode 100644 index 000000000..12160fb0f --- /dev/null +++ b/Source/Falcor/Utils/Scripting/ndarray.cpp @@ -0,0 +1,920 @@ +/*************************************************************************** + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. + # + # Redistribution and use in source and binary forms, with or without + # modification, are permitted provided that the following conditions + # are met: + # * Redistributions of source code must retain the above copyright + # notice, this list of conditions and the following disclaimer. + # * Redistributions in binary form must reproduce the above copyright + # notice, this list of conditions and the following disclaimer in the + # documentation and/or other materials provided with the distribution. + # * Neither the name of NVIDIA CORPORATION nor the names of its + # contributors may be used to endorse or promote products derived + # from this software without specific prior written permission. + # + # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY + # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + # (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 a backport of nanobind's ndarray class to pybind11. +// See https://github.com/wjakob/nanobind/blob/master/docs/ndarray.rst + +#include "ndarray.h" +#include + +namespace pybind11 +{ +namespace detail +{ + +/// Python object representing a `nb_ndarray` (which wraps a DLPack tensor) +struct nb_ndarray +{ + PyObject_HEAD; + ndarray_handle* th; +}; + +template +struct scoped_pymalloc +{ + scoped_pymalloc(size_t size = 1) + { + ptr = (T*)PyMem_Malloc(size * sizeof(T)); + if (!ptr) + pybind11_fail("scoped_pymalloc(): could not allocate " + std::to_string(size) + " bytes of memory!"); + } + ~scoped_pymalloc() { PyMem_Free(ptr); } + T* release() + { + T* temp = ptr; + ptr = nullptr; + return temp; + } + T* get() const { return ptr; } + T& operator[](size_t i) { return ptr[i]; } + T* operator->() { return ptr; } + +private: + T* ptr{nullptr}; +}; + +void nb_ndarray_dealloc(PyObject* self); +int nb_ndarray_getbuffer(PyObject* exporter, Py_buffer* view, int); +void nb_ndarray_releasebuffer(PyObject*, Py_buffer* view); + +static PyType_Slot nb_ndarray_slots[] = { + {Py_tp_dealloc, (void*)nb_ndarray_dealloc}, +#if PY_VERSION_HEX >= 0x03090000 + {Py_bf_getbuffer, (void*)nb_ndarray_getbuffer}, + {Py_bf_releasebuffer, (void*)nb_ndarray_releasebuffer}, +#endif + {0, nullptr}}; + +static PyType_Spec nb_ndarray_spec = { + /* .name = */ "pybind11.nb_ndarray", + /* .basicsize = */ (int)sizeof(nb_ndarray), + /* .itemsize = */ 0, + /* .flags = */ Py_TPFLAGS_DEFAULT, + /* .slots = */ nb_ndarray_slots}; + +struct nb_internals +{ + PyTypeObject* nb_ndarray; + static nb_internals& get() + { + // BACKPORT + // static nb_internals internals{(PyTypeObject*)PyType_FromSpec(&nb_ndarray_spec)}; + static nb_internals internals = []() + { + nb_internals internals_{(PyTypeObject*)PyType_FromSpec(&nb_ndarray_spec)}; +#if PY_VERSION_HEX < 0x03090000 + // Python < 3.9 does not support buffer protocol in stable API. + internals_.nb_ndarray->tp_as_buffer->bf_getbuffer = nb_ndarray_getbuffer; + internals_.nb_ndarray->tp_as_buffer->bf_releasebuffer = nb_ndarray_releasebuffer; +#endif + return internals_; + }(); + return internals; + } +}; + +// ======================================================================== + +struct managed_dltensor +{ + dlpack::dltensor dltensor; + void* manager_ctx; + void (*deleter)(managed_dltensor*); +}; + +struct ndarray_handle +{ + managed_dltensor* ndarray; + std::atomic refcount; + PyObject* owner; + bool free_shape; + bool free_strides; + bool call_deleter; +}; + +void nb_ndarray_dealloc(PyObject* self) +{ + ndarray_dec_ref(((nb_ndarray*)self)->th); + + freefunc tp_free; +#if defined(Py_LIMITED_API) + tp_free = (freefunc)PyType_GetSlot(Py_TYPE(self), Py_tp_free); +#else + tp_free = Py_TYPE(self)->tp_free; +#endif + + tp_free(self); +} + +int nb_ndarray_getbuffer(PyObject* exporter, Py_buffer* view, int) +{ + nb_ndarray* self = (nb_ndarray*)exporter; + + dlpack::dltensor& t = self->th->ndarray->dltensor; + + if (t.device.device_type != device::cpu::value) + { + PyErr_SetString( + PyExc_BufferError, + "Only CPU-allocated ndarrays can be " + "accessed via the buffer protocol!" + ); + return -1; + } + + const char* format = nullptr; + switch ((dlpack::dtype_code)t.dtype.code) + { + case dlpack::dtype_code::Int: + switch (t.dtype.bits) + { + case 8: + format = "b"; + break; + case 16: + format = "h"; + break; + case 32: + format = "i"; + break; + case 64: + format = "q"; + break; + } + break; + + case dlpack::dtype_code::UInt: + switch (t.dtype.bits) + { + case 8: + format = "B"; + break; + case 16: + format = "H"; + break; + case 32: + format = "I"; + break; + case 64: + format = "Q"; + break; + } + break; + + case dlpack::dtype_code::Float: + switch (t.dtype.bits) + { + case 16: + format = "e"; + break; + case 32: + format = "f"; + break; + case 64: + format = "d"; + break; + } + break; + + default: + break; + } + + if (!format || t.dtype.lanes != 1) + { + PyErr_SetString(PyExc_BufferError, "Don't know how to convert DLPack dtype into buffer protocol format!"); + return -1; + } + + view->format = (char*)format; + view->itemsize = t.dtype.bits / 8; + view->buf = (void*)((uintptr_t)t.data + t.byte_offset); + view->obj = exporter; + Py_INCREF(exporter); + + Py_ssize_t len = view->itemsize; + scoped_pymalloc strides(t.ndim), shape(t.ndim); + for (int32_t i = 0; i < t.ndim; ++i) + { + len *= (Py_ssize_t)t.shape[i]; + strides[i] = (Py_ssize_t)t.strides[i] * view->itemsize; + shape[i] = (Py_ssize_t)t.shape[i]; + } + + view->ndim = t.ndim; + view->len = len; + view->readonly = false; + view->suboffsets = nullptr; + view->internal = nullptr; + view->strides = strides.release(); + view->shape = shape.release(); + + return 0; +} + +void nb_ndarray_releasebuffer(PyObject*, Py_buffer* view) +{ + PyMem_Free(view->shape); + PyMem_Free(view->strides); +} + +static PyObject* dlpack_from_buffer_protocol(PyObject* o) +{ + scoped_pymalloc view; + scoped_pymalloc mt; + + if (PyObject_GetBuffer(o, view.get(), PyBUF_RECORDS)) + { + PyErr_Clear(); + return nullptr; + } + + char format = 'B'; + const char* format_str = view->format; + if (format_str) + format = *format_str; + + bool skip_first = format == '@' || format == '='; + + int32_t num = 1; + if (*(uint8_t*)&num == 1) + { + if (format == '<') + skip_first = true; + } + else + { + if (format == '!' || format == '>') + skip_first = true; + } + + if (skip_first && format_str) + format = *++format_str; + + dlpack::dtype dt{}; + bool fail = format_str && format_str[1] != '\0'; + + if (!fail) + { + switch (format) + { + case 'c': + case 'b': + case 'h': + case 'i': + case 'l': + case 'q': + case 'n': + dt.code = (uint8_t)dlpack::dtype_code::Int; + break; + + case 'B': + case 'H': + case 'I': + case 'L': + case 'Q': + case 'N': + dt.code = (uint8_t)dlpack::dtype_code::UInt; + break; + + case 'e': + case 'f': + case 'd': + dt.code = (uint8_t)dlpack::dtype_code::Float; + break; + + default: + fail = true; + } + + dt.lanes = 1; + dt.bits = (uint8_t)(view->itemsize * 8); + } + + if (fail) + { + PyBuffer_Release(view.get()); + return nullptr; + } + + mt->deleter = [](managed_dltensor* mt2) + { + gil_scoped_acquire guard; + Py_buffer* buf = (Py_buffer*)mt2->manager_ctx; + PyBuffer_Release(buf); + PyMem_Free(mt2->dltensor.shape); + PyMem_Free(mt2->dltensor.strides); + PyMem_Free(mt2); + }; + + /* DLPack mandates 256-byte alignment of the 'DLTensor::data' field, but + PyTorch unfortunately ignores the 'byte_offset' value.. :-( */ +#if 0 + uintptr_t value_int = (uintptr_t) view->buf, + value_rounded = (value_int / 256) * 256; +#else + uintptr_t value_int = (uintptr_t)view->buf, value_rounded = value_int; +#endif + + mt->dltensor.data = (void*)value_rounded; + mt->dltensor.device = {device::cpu::value, 0}; + mt->dltensor.ndim = view->ndim; + mt->dltensor.dtype = dt; + mt->dltensor.byte_offset = value_int - value_rounded; + + scoped_pymalloc strides(view->ndim); + scoped_pymalloc shape(view->ndim); + for (size_t i = 0; i < (size_t)view->ndim; ++i) + { + strides[i] = (int64_t)(view->strides[i] / view->itemsize); + shape[i] = (int64_t)view->shape[i]; + } + + mt->manager_ctx = view.release(); + mt->dltensor.shape = shape.release(); + mt->dltensor.strides = strides.release(); + + return PyCapsule_New( + mt.release(), "dltensor", + [](PyObject* o) + { + error_scope scope; // temporarily save any existing errors + managed_dltensor* mt = (managed_dltensor*)PyCapsule_GetPointer(o, "dltensor"); + if (mt) + { + if (mt->deleter) + mt->deleter(mt); + } + else + { + PyErr_Clear(); + } + } + ); +} + +ndarray_handle* ndarray_import(PyObject* o, const ndarray_req* req, bool convert) noexcept +{ + object capsule; + + // If this is not a capsule, try calling o.__dlpack__() + if (!PyCapsule_CheckExact(o)) + { + // BACKPORT + // capsule = steal(PyObject_CallMethod(o, "__dlpack__", nullptr)); + capsule = reinterpret_steal(PyObject_CallMethod(o, "__dlpack__", nullptr)); + + // BACKPORT + // if (!capsule.is_valid()) + if (!capsule) + { + PyErr_Clear(); + PyTypeObject* tp = Py_TYPE(o); + + try + { + // BACKPORT + // const char* module_name = borrow(handle(tp).attr("__module__")).c_str(); + std::string module_name = reinterpret_borrow(handle(tp->tp_dict).attr("__module__")); + + object package; + if (strncmp(module_name.c_str(), "tensorflow.", 11) == 0) + package = module_::import("tensorflow.experimental.dlpack"); + else if (strcmp(module_name.c_str(), "torch") == 0) + package = module_::import("torch.utils.dlpack"); + else if (strncmp(module_name.c_str(), "jaxlib", 6) == 0) + package = module_::import("jax.dlpack"); + + // BACKPORT + // if (package.is_valid()) + if (package) + capsule = package.attr("to_dlpack")(handle(o)); + } + catch (...) + { + // BACKPORT + // capsule.reset(); + capsule.release(); + } + } + + // Try creating a ndarray via the buffer protocol + // BACKPORT + // if (!capsule.is_valid()) + // capsule = steal(dlpack_from_buffer_protocol(o)); + if (!capsule) + capsule = reinterpret_steal(dlpack_from_buffer_protocol(o)); + + // BACKPORT + // if (!capsule.is_valid()) + if (!capsule) + return nullptr; + } + else + { + // BACKPORT + // capsule = borrow(o); + capsule = reinterpret_borrow(o); + } + + // Extract the pointer underlying the capsule + void* ptr = PyCapsule_GetPointer(capsule.ptr(), "dltensor"); + if (!ptr) + { + PyErr_Clear(); + return nullptr; + } + + // Check if the ndarray satisfies the requirements + dlpack::dltensor& t = ((managed_dltensor*)ptr)->dltensor; + + bool pass_dtype = true, pass_device = true, pass_shape = true, pass_order = true; + + if (req->req_dtype) + pass_dtype = t.dtype == req->dtype; + + if (req->req_device) + pass_device = t.device.device_type == req->req_device; + + if (req->req_shape) + { + pass_shape &= req->ndim == (uint32_t)t.ndim; + + if (pass_shape) + { + for (uint32_t i = 0; i < req->ndim; ++i) + { + if (req->shape[i] != (size_t)t.shape[i] && req->shape[i] != pybind11::any) + { + pass_shape = false; + break; + } + } + } + } + + scoped_pymalloc strides(t.ndim); + if ((req->req_order || !t.strides) && t.ndim > 0) + { + size_t accum = 1; + + if (req->req_order == 'C' || !t.strides) + { + for (uint32_t i = (uint32_t)(t.ndim - 1);;) + { + strides[i] = accum; + accum *= t.shape[i]; + if (i == 0) + break; + --i; + } + } + else if (req->req_order == 'F') + { + for (uint32_t i = 0; i < (uint32_t)t.ndim; ++i) + { + strides[i] = accum; + accum *= t.shape[i]; + } + } + else + { + pass_order = false; + } + + if (req->req_order) + { + if (!t.strides) + { + // c-style strides assumed + pass_order = req->req_order == 'C'; + } + else + { + for (uint32_t i = 0; i < (uint32_t)t.ndim; ++i) + { + if (!((strides[i] == t.strides[i]) || (t.shape[i] == 1 && t.strides[i] == 0))) + { + pass_order = false; + break; + } + } + } + } + } + + // Support implicit conversion of 'dtype' and order + if (pass_device && pass_shape && (!pass_dtype || !pass_order) && convert && capsule.ptr() != o) + { + PyTypeObject* tp = Py_TYPE(o); + // BACKPORT + // str module_name_o = borrow(handle(tp).attr("__module__")); + // const char* module_name = module_name_o.c_str(); + std::string module_name_str = reinterpret_borrow(handle(tp->tp_dict).attr("__module__")); + const char* module_name = module_name_str.c_str(); + + char order = 'K'; + if (req->req_order != '\0') + order = req->req_order; + + if (req->dtype.lanes != 1) + return nullptr; + + const char* prefix = nullptr; + char dtype[9]; + switch (req->dtype.code) + { + case (uint8_t)dlpack::dtype_code::Int: + prefix = "int"; + break; + case (uint8_t)dlpack::dtype_code::UInt: + prefix = "uint"; + break; + case (uint8_t)dlpack::dtype_code::Float: + prefix = "float"; + break; + default: + return nullptr; + } + snprintf(dtype, sizeof(dtype), "%s%u", prefix, req->dtype.bits); + + object converted; + try + { + if (strcmp(module_name, "numpy") == 0) + { + converted = handle(o).attr("astype")(dtype, order); + } + else if (strcmp(module_name, "torch") == 0) + { + converted = handle(o).attr("to")(arg("dtype") = module_::import("torch").attr(dtype), arg("copy") = true); + } + else if (strncmp(module_name, "tensorflow.", 11) == 0) + { + converted = module_::import("tensorflow").attr("cast")(handle(o), dtype); + } + else if (strncmp(module_name, "jaxlib", 6) == 0) + { + converted = handle(o).attr("astype")(dtype); + } + } + catch (...) + { + // BACKPORT + // converted.reset(); + converted.release(); + } + + // Potentially try again recursively + // BACKPORT + // if (!converted.is_valid()) + if (!converted) + return nullptr; + else + return ndarray_import(converted.ptr(), req, false); + } + + if (!pass_dtype || !pass_device || !pass_shape || !pass_order) + return nullptr; + + // Create a reference-counted wrapper + scoped_pymalloc result; + result->ndarray = (managed_dltensor*)ptr; + result->refcount = 0; + result->owner = nullptr; + result->free_shape = false; + result->call_deleter = true; + + // Ensure that the strides member is always initialized + if (t.strides) + { + result->free_strides = false; + } + else + { + result->free_strides = true; + t.strides = strides.release(); + } + + // Mark the dltensor capsule as "consumed" + if (PyCapsule_SetName(capsule.ptr(), "used_dltensor") || PyCapsule_SetDestructor(capsule.ptr(), nullptr)) + pybind11_fail( + "pybind11::detail::ndarray_import(): could not mark dltensor " + "capsule as consumed!" + ); + + return result.release(); +} + +dlpack::dltensor* ndarray_inc_ref(ndarray_handle* th) noexcept +{ + if (!th) + return nullptr; + ++th->refcount; + return &th->ndarray->dltensor; +} + +void ndarray_dec_ref(ndarray_handle* th) noexcept +{ + if (!th) + return; + size_t rc_value = th->refcount--; + + if (rc_value == 0) + { + pybind11_fail("ndarray_dec_ref(): reference count became negative!"); + } + else if (rc_value == 1) + { + Py_XDECREF(th->owner); + managed_dltensor* mt = th->ndarray; + if (th->free_shape) + { + PyMem_Free(mt->dltensor.shape); + mt->dltensor.shape = nullptr; + } + if (th->free_strides) + { + PyMem_Free(mt->dltensor.strides); + mt->dltensor.strides = nullptr; + } + if (th->call_deleter) + { + if (mt->deleter) + mt->deleter(mt); + } + else + { + PyMem_Free(mt); + } + PyMem_Free(th); + } +} + +ndarray_handle* ndarray_create( + void* value, + size_t ndim, + const size_t* shape_in, + PyObject* owner, + const int64_t* strides_in, + dlpack::dtype* dtype, + int32_t device_type, + int32_t device_id +) +{ + /* DLPack mandates 256-byte alignment of the 'DLTensor::data' field, but + PyTorch unfortunately ignores the 'byte_offset' value.. :-( */ +#if 0 + uintptr_t value_int = (uintptr_t) value, + value_rounded = (value_int / 256) * 256; +#else + uintptr_t value_int = (uintptr_t)value, value_rounded = value_int; +#endif + + scoped_pymalloc ndarray; + scoped_pymalloc result; + scoped_pymalloc shape(ndim), strides(ndim); + + auto deleter = [](managed_dltensor* mt) + { + gil_scoped_acquire guard; + ndarray_handle* th = (ndarray_handle*)mt->manager_ctx; + ndarray_dec_ref(th); + }; + + for (size_t i = 0; i < ndim; ++i) + shape[i] = (int64_t)shape_in[i]; + + if (ndim > 0) + { + int64_t prod = 1; + for (size_t i = ndim - 1;;) + { + if (strides_in) + { + strides[i] = strides_in[i]; + } + else + { + strides[i] = prod; + prod *= (int64_t)shape_in[i]; + } + if (i == 0) + break; + --i; + } + } + + ndarray->dltensor.data = (void*)value_rounded; + ndarray->dltensor.device.device_type = device_type; + ndarray->dltensor.device.device_id = device_id; + ndarray->dltensor.ndim = (int32_t)ndim; + ndarray->dltensor.dtype = *dtype; + ndarray->dltensor.byte_offset = value_int - value_rounded; + ndarray->dltensor.shape = shape.release(); + ndarray->dltensor.strides = strides.release(); + ndarray->manager_ctx = result.get(); + ndarray->deleter = deleter; + result->ndarray = (managed_dltensor*)ndarray.release(); + result->refcount = 0; + result->owner = owner; + result->free_shape = true; + result->free_strides = true; + result->call_deleter = false; + Py_XINCREF(owner); + return result.release(); +} + +static void ndarray_capsule_destructor(PyObject* o) +{ + error_scope scope; // temporarily save any existing errors + managed_dltensor* mt = (managed_dltensor*)PyCapsule_GetPointer(o, "dltensor"); + + if (mt) + ndarray_dec_ref((ndarray_handle*)mt->manager_ctx); + else + PyErr_Clear(); +} + +PyObject* ndarray_wrap(ndarray_handle* th, int framework, return_value_policy policy) noexcept +{ + if (!th) + return none().release().ptr(); + + bool copy = policy == return_value_policy::copy || policy == return_value_policy::move; + + if ((ndarray_framework)framework == ndarray_framework::numpy) + { + try + { + // BACKPORT + // object o = steal(PyType_GenericAlloc(internals_get().nb_ndarray, 0)); + object o = reinterpret_steal(PyType_GenericAlloc(nb_internals::get().nb_ndarray, 0)); + // if (!o.is_valid()) + if (!o) + return nullptr; + ((nb_ndarray*)o.ptr())->th = th; + ndarray_inc_ref(th); + + return module_::import("numpy").attr("array")(o, arg("copy") = copy).release().ptr(); + } + catch (const std::exception& e) + { + PyErr_Format( + PyExc_RuntimeError, + "pybind11::detail::ndarray_wrap(): could not " + "convert ndarray to NumPy array: %s", + e.what() + ); + return nullptr; + } + } + + object package; + try + { + switch ((ndarray_framework)framework) + { + case ndarray_framework::none: + break; + + case ndarray_framework::pytorch: + package = module_::import("torch.utils.dlpack"); + break; + + case ndarray_framework::tensorflow: + package = module_::import("tensorflow.experimental.dlpack"); + break; + + case ndarray_framework::jax: + package = module_::import("jax.dlpack"); + break; + + default: + pybind11_fail( + "pybind11::detail::ndarray_wrap(): unknown framework " + "specified!" + ); + } + } + catch (const std::exception& e) + { + PyErr_Format( + PyExc_RuntimeError, + "pybind11::detail::ndarray_wrap(): could not import ndarray " + "framework: %s", + e.what() + ); + return nullptr; + } + + // BACKPORT + // object o = steal(PyCapsule_New(th->ndarray, "dltensor", ndarray_capsule_destructor)); + object o = reinterpret_steal(PyCapsule_New(th->ndarray, "dltensor", ndarray_capsule_destructor)); + + ndarray_inc_ref(th); + + // BACKPORT + // if (package.is_valid()) + if (package) + { + try + { + o = package.attr("from_dlpack")(o); + } + catch (const std::exception& e) + { + PyErr_Format( + PyExc_RuntimeError, + "pybind11::detail::ndarray_wrap(): could not " + "import ndarray: %s", + e.what() + ); + return nullptr; + } + } + + if (copy) + { + try + { + o = o.attr("copy")(); + } + catch (std::exception& e) + { + PyErr_Format(PyExc_RuntimeError, "pybind11::detail::ndarray_wrap(): copy failed: %s", e.what()); + return nullptr; + } + } + + return o.release().ptr(); +} + +} // namespace detail +} // namespace pybind11 + +#include "ScriptBindings.h" + +namespace Falcor +{ +FALCOR_SCRIPT_BINDING(ndarray) +{ + m.def( + "inspect_ndarray", + [](pybind11::ndarray<> ndarray) + { + printf("ndarray data pointer : %p\n", ndarray.data()); + printf("ndarray dimension : %zu\n", ndarray.ndim()); + for (size_t i = 0; i < ndarray.ndim(); ++i) + { + printf("ndarray dimension [%zu] : %zu\n", i, ndarray.shape(i)); + printf("ndarray stride [%zu] : %zd\n", i, ndarray.stride(i)); + } + printf( + "Device ID = %u (cpu=%i, cuda=%i)\n", ndarray.device_id(), int(ndarray.device_type() == pybind11::device::cpu::value), + int(ndarray.device_type() == pybind11::device::cuda::value) + ); + printf( + "ndarray dtype: int16=%i, uint32=%i, float32=%i\n", ndarray.dtype() == pybind11::dtype(), + ndarray.dtype() == pybind11::dtype(), ndarray.dtype() == pybind11::dtype() + ); + } + ); +} + +} // namespace Falcor diff --git a/Source/Falcor/Utils/Scripting/ndarray.h b/Source/Falcor/Utils/Scripting/ndarray.h new file mode 100644 index 000000000..aa315fa69 --- /dev/null +++ b/Source/Falcor/Utils/Scripting/ndarray.h @@ -0,0 +1,520 @@ +/*************************************************************************** + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. + # + # Redistribution and use in source and binary forms, with or without + # modification, are permitted provided that the following conditions + # are met: + # * Redistributions of source code must retain the above copyright + # notice, this list of conditions and the following disclaimer. + # * Redistributions in binary form must reproduce the above copyright + # notice, this list of conditions and the following disclaimer in the + # documentation and/or other materials provided with the distribution. + # * Neither the name of NVIDIA CORPORATION nor the names of its + # contributors may be used to endorse or promote products derived + # from this software without specific prior written permission. + # + # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY + # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + # (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 a backport of nanobind's ndarray class to pybind11. +// See https://github.com/wjakob/nanobind/blob/master/docs/ndarray.rst + +/* + nanobind/ndarray.h: functionality to exchange n-dimensional arrays with + other array programming frameworks (NumPy, PyTorch, etc.) + + Copyright (c) 2022 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. + + The API below is based on the DLPack project + (https://github.com/dmlc/dlpack/blob/main/include/dlpack/dlpack.h) +*/ +#pragma once + +#include "Core/Macros.h" +#include + +namespace pybind11 +{ + +// Forward declarations for types in dlpack.h (1) +namespace dlpack +{ +struct dltensor; +struct dtype; +} // namespace dlpack + +namespace detail +{ + +// Forward declarations for types in dlpack.h (2) +struct ndarray_handle; +struct ndarray_req; + +// Try to import a reference-counted ndarray object via DLPack +FALCOR_API ndarray_handle* ndarray_import(PyObject* o, const ndarray_req* req, bool convert) noexcept; + +// Describe a local tensor object using a DLPack capsule +FALCOR_API ndarray_handle* ndarray_create( + void* value, + size_t ndim, + const size_t* shape, + PyObject* owner, + const int64_t* strides, + dlpack::dtype* dtype, + int32_t device, + int32_t device_id +); + +/// Increase the reference count of the given tensor object; returns a pointer +/// to the underlying DLtensor +FALCOR_API dlpack::dltensor* ndarray_inc_ref(ndarray_handle*) noexcept; + +/// Decrease the reference count of the given tensor object +FALCOR_API void ndarray_dec_ref(ndarray_handle*) noexcept; + +/// Wrap a ndarray_handle* into a PyCapsule +FALCOR_API PyObject* ndarray_wrap(ndarray_handle*, int framework, return_value_policy policy) noexcept; + +} // namespace detail +} // namespace pybind11 + +namespace pybind11 +{ + +namespace device +{ +#define NB_DEVICE(enum_name, enum_value) \ + struct enum_name \ + { \ + static constexpr auto name = detail::const_name(#enum_name); \ + static constexpr int32_t value = enum_value; \ + static constexpr bool is_device = true; \ + } +NB_DEVICE(none, 0); +NB_DEVICE(cpu, 1); +NB_DEVICE(cuda, 2); +NB_DEVICE(cuda_host, 3); +NB_DEVICE(opencl, 4); +NB_DEVICE(vulkan, 7); +NB_DEVICE(metal, 8); +NB_DEVICE(rocm, 10); +NB_DEVICE(rocm_host, 11); +NB_DEVICE(cuda_managed, 13); +NB_DEVICE(oneapi, 14); +#undef NB_DEVICE +} // namespace device + +namespace dlpack +{ + +enum class dtype_code : uint8_t +{ + Int = 0, + UInt = 1, + Float = 2, + Bfloat = 4, + Complex = 5 +}; + +struct device +{ + int32_t device_type = 0; + int32_t device_id = 0; +}; + +struct dtype +{ + uint8_t code = 0; + uint8_t bits = 0; + uint16_t lanes = 0; + + bool operator==(const dtype& o) const { return code == o.code && bits == o.bits && lanes == o.lanes; } + bool operator!=(const dtype& o) const { return !operator==(o); } +}; + +struct dltensor +{ + void* data = nullptr; + pybind11::dlpack::device device; + int32_t ndim = 0; + pybind11::dlpack::dtype dtype; + int64_t* shape = nullptr; + int64_t* strides = nullptr; + uint64_t byte_offset = 0; +}; + +} // namespace dlpack + +constexpr size_t any = (size_t)-1; + +template +struct shape +{ + static constexpr size_t size = sizeof...(Is); +}; + +struct c_contig +{}; +struct f_contig +{}; +struct any_contig +{}; +struct numpy +{}; +struct tensorflow +{}; +struct pytorch +{}; +struct jax +{}; + +template +constexpr dlpack::dtype dtype() +{ + static_assert( + std::is_floating_point_v || std::is_integral_v, "pybind11::dtype: T must be a floating point or integer variable!" + ); + + dlpack::dtype result; + + if constexpr (std::is_floating_point_v) + result.code = (uint8_t)dlpack::dtype_code::Float; + else if constexpr (std::is_signed_v) + result.code = (uint8_t)dlpack::dtype_code::Int; + else + result.code = (uint8_t)dlpack::dtype_code::UInt; + + result.bits = sizeof(T) * 8; + result.lanes = 1; + + return result; +} + +namespace detail +{ + +enum class ndarray_framework : int +{ + none, + numpy, + tensorflow, + pytorch, + jax +}; + +struct ndarray_req +{ + dlpack::dtype dtype; + uint32_t ndim = 0; + size_t* shape = nullptr; + bool req_shape = false; + bool req_dtype = false; + char req_order = '\0'; + uint8_t req_device = 0; +}; + +template +struct ndarray_arg +{ + static constexpr size_t size = 0; + static constexpr auto name = descr<0>{}; + static void apply(ndarray_req&) {} +}; + +template +struct ndarray_arg>> +{ + static constexpr size_t size = 0; + + static constexpr auto name = const_name("dtype=float") + const_name(); + + static void apply(ndarray_req& tr) + { + tr.dtype = dtype(); + tr.req_dtype = true; + } +}; + +template +struct ndarray_arg>> +{ + static constexpr size_t size = 0; + + static constexpr auto name = + const_name("dtype=") + const_name>("u", "") + const_name("int") + const_name(); + + static void apply(ndarray_req& tr) + { + tr.dtype = dtype(); + tr.req_dtype = true; + } +}; + +template +struct ndarray_arg> +{ + static constexpr size_t size = sizeof...(Is); + static constexpr auto name = + const_name("shape=(") + concat(const_name(const_name("*"), const_name())...) + const_name(")"); + + static void apply(ndarray_req& tr) + { + size_t i = 0; + ((tr.shape[i++] = Is), ...); + tr.ndim = (uint32_t)sizeof...(Is); + tr.req_shape = true; + } +}; + +template<> +struct ndarray_arg +{ + static constexpr size_t size = 0; + static constexpr auto name = const_name("order='C'"); + static void apply(ndarray_req& tr) { tr.req_order = 'C'; } +}; + +template<> +struct ndarray_arg +{ + static constexpr size_t size = 0; + static constexpr auto name = const_name("order='F'"); + static void apply(ndarray_req& tr) { tr.req_order = 'F'; } +}; + +template<> +struct ndarray_arg +{ + static constexpr size_t size = 0; + static constexpr auto name = const_name("order='*'"); + static void apply(ndarray_req& tr) { tr.req_order = '\0'; } +}; + +template +struct ndarray_arg> +{ + static constexpr size_t size = 0; + static constexpr auto name = const_name("device='") + T::name + const_name("'"); + static void apply(ndarray_req& tr) { tr.req_device = (uint8_t)T::value; } +}; + +template +struct ndarray_info +{ + using scalar_type = void; + using shape_type = void; + constexpr static auto name = const_name("ndarray"); + constexpr static ndarray_framework framework = ndarray_framework::none; +}; + +template +struct ndarray_info : ndarray_info +{ + using scalar_type = std::conditional_t, T, typename ndarray_info::scalar_type>; +}; + +template +struct ndarray_info, Ts...> : ndarray_info +{ + using shape_type = shape; +}; + +template +struct ndarray_info : ndarray_info +{ + constexpr static auto name = const_name("numpy.ndarray"); + constexpr static ndarray_framework framework = ndarray_framework::numpy; +}; + +template +struct ndarray_info : ndarray_info +{ + constexpr static auto name = const_name("torch.Tensor"); + constexpr static ndarray_framework framework = ndarray_framework::pytorch; +}; + +template +struct ndarray_info : ndarray_info +{ + constexpr static auto name = const_name("tensorflow.python.framework.ops.EagerTensor"); + constexpr static ndarray_framework framework = ndarray_framework::tensorflow; +}; + +template +struct ndarray_info : ndarray_info +{ + constexpr static auto name = const_name("jaxlib.xla_extension.DeviceArray"); + constexpr static ndarray_framework framework = ndarray_framework::jax; +}; + +} // namespace detail + +template +class ndarray +{ +public: + using Info = detail::ndarray_info; + using Scalar = typename Info::scalar_type; + + ndarray() = default; + + explicit ndarray(detail::ndarray_handle* handle) : m_handle(handle) + { + if (handle) + m_dltensor = *detail::ndarray_inc_ref(handle); + } + + ndarray( + void* value, + size_t ndim, + const size_t* shape, + handle owner = pybind11::handle(), + const int64_t* strides = nullptr, + dlpack::dtype dtype = pybind11::dtype(), + int32_t device_type = device::cpu::value, + int32_t device_id = 0 + ) + { + m_handle = detail::ndarray_create(value, ndim, shape, owner.ptr(), strides, &dtype, device_type, device_id); + m_dltensor = *detail::ndarray_inc_ref(m_handle); + } + + ~ndarray() { detail::ndarray_dec_ref(m_handle); } + + ndarray(const ndarray& t) : m_handle(t.m_handle), m_dltensor(t.m_dltensor) { detail::ndarray_inc_ref(m_handle); } + + ndarray(ndarray&& t) noexcept : m_handle(t.m_handle), m_dltensor(t.m_dltensor) + { + t.m_handle = nullptr; + t.m_dltensor = dlpack::dltensor(); + } + + ndarray& operator=(ndarray&& t) noexcept + { + detail::ndarray_dec_ref(m_handle); + m_handle = t.m_handle; + m_dltensor = t.m_dltensor; + t.m_handle = nullptr; + t.m_dltensor = dlpack::dltensor(); + return *this; + } + + ndarray& operator=(const ndarray& t) + { + detail::ndarray_inc_ref(t.m_handle); + detail::ndarray_dec_ref(m_handle); + m_handle = t.m_handle; + m_dltensor = t.m_dltensor; + return *this; + } + + dlpack::dtype dtype() const { return m_dltensor.dtype; } + size_t ndim() const { return m_dltensor.ndim; } + size_t shape(size_t i) const { return m_dltensor.shape[i]; } + int64_t stride(size_t i) const { return m_dltensor.strides[i]; } + bool is_valid() const { return m_handle != nullptr; } + int32_t device_type() const { return m_dltensor.device.device_type; } + int32_t device_id() const { return m_dltensor.device.device_id; } + detail::ndarray_handle* handle() const { return m_handle; } + + const Scalar* data() const { return (const Scalar*)((const uint8_t*)m_dltensor.data + m_dltensor.byte_offset); } + + Scalar* data() { return (Scalar*)((uint8_t*)m_dltensor.data + m_dltensor.byte_offset); } + + template + inline auto& operator()(Ts... indices) + { + static_assert( + !std::is_same_v, + "To use pybind11::ndarray::operator(), you must add a scalar type " + "annotation (e.g. 'float') to the ndarray template parameters." + ); + static_assert( + !std::is_same_v, + "To use pybind11::ndarray::operator(), you must add a pybind11::shape<> " + "annotation to the ndarray template parameters." + ); + static_assert(sizeof...(Ts) == Info::shape_type::size, "pybind11::ndarray::operator(): invalid number of arguments"); + + int64_t counter = 0, index = 0; + ((index += int64_t(indices) * m_dltensor.strides[counter++]), ...); + return (Scalar&)*((uint8_t*)m_dltensor.data + m_dltensor.byte_offset + index * sizeof(typename Info::scalar_type)); + } + +private: + detail::ndarray_handle* m_handle = nullptr; + dlpack::dltensor m_dltensor; +}; + +namespace detail +{ + +constexpr descr<0> concat_maybe() +{ + return {}; +} + +template +constexpr descr concat_maybe(const descr& descr) +{ + return descr; +} + +template +constexpr auto concat_maybe(const descr& d, const Args&... args) + -> decltype(std::declval>() + concat_maybe(args...)) +{ + if constexpr (N + sizeof...(Ts) == 0) + return concat_maybe(args...); + else + return d + const_name(", ") + concat_maybe(args...); +} + +template +struct type_caster> +{ + using Value = ndarray; + + PYBIND11_TYPE_CASTER( + ndarray, + Value::Info::name + const_name("[") + concat_maybe(detail::ndarray_arg::name...) + const_name("]") + ); + + // BACKPORT + // bool from_python(handle src, uint8_t flags, cleanup_list*) noexcept + bool load(handle src, bool convert) noexcept + { + constexpr size_t size = (0 + ... + detail::ndarray_arg::size); + size_t shape[size + 1]; + detail::ndarray_req req; + req.shape = shape; + (detail::ndarray_arg::apply(req), ...); + // BACKPORT + // value = ndarray(ndarray_import(src.ptr(), &req, flags & (uint8_t)cast_flags::convert)); + value = ndarray(ndarray_import(src.ptr(), &req, convert)); + return value.is_valid(); + } + + // BACKPORT + // static handle from_cpp(const ndarray& tensor, rv_policy policy, cleanup_list*) noexcept + static handle cast(const ndarray& tensor, return_value_policy policy, handle /* parent */) noexcept + { + return ndarray_wrap(tensor.handle(), int(Value::Info::framework), policy); + } +}; + +} // namespace detail +} // namespace pybind11 diff --git a/Source/Falcor/Utils/Settings.cpp b/Source/Falcor/Utils/Settings.cpp index 2ad8d6bbd..449db0c89 100644 --- a/Source/Falcor/Utils/Settings.cpp +++ b/Source/Falcor/Utils/Settings.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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,9 @@ #include "Utils/Scripting/ScriptBindings.h" +#include +#include + #include namespace Falcor @@ -62,6 +65,13 @@ std::vector toStrings(const nlohmann::json& value) } // namespace +void Settings::addOptions(const pybind11::dict& options) +{ + auto json = pyjson::to_json(options); + merge(getActive().mOptions, json); + updateSearchPaths(json); +} + bool Settings::addOptions(const std::filesystem::path& path) { if (path.extension() == ".json") @@ -82,6 +92,16 @@ bool Settings::addOptionsJSON(const std::filesystem::path& path) return true; } +void Settings::addFilteredAttributes(const pybind11::dict& attributes) +{ + merge(getActive().mFilteredAttributes, pyjson::to_json(attributes)); +} + +void Settings::clearFilteredAttributes() +{ + getActive().mFilteredAttributes.clear(); +} + void Settings::updateSearchPaths(const nlohmann::json& update) { if (update.is_null() || !update.is_object()) diff --git a/Source/Falcor/Utils/Settings.h b/Source/Falcor/Utils/Settings.h index 944fd4fde..0f02e5633 100644 --- a/Source/Falcor/Utils/Settings.h +++ b/Source/Falcor/Utils/Settings.h @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -29,11 +29,8 @@ #include "Core/Macros.h" #include "Core/Assert.h" #include "Core/Platform/SearchDirectories.h" -#include "Utils/Scripting/Dictionary.h" #include -#include -#include #include #include @@ -42,8 +39,14 @@ #include #include +namespace pybind11 +{ +class dict; +} + namespace Falcor { +class Dictionary; class Settings; class SettingsProperties { @@ -92,7 +95,9 @@ class SettingsProperties return true; } - template + // The "gccfix" parameter is used to avoid "explicit specialization in non-namespace scope" in gcc. + // See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=85282 + template struct JsonCaster { static T cast(const nlohmann::json& json) @@ -106,8 +111,8 @@ class SettingsProperties } }; - template<> - struct JsonCaster + template + struct JsonCaster { static bool cast(const nlohmann::json& json) { @@ -117,8 +122,8 @@ class SettingsProperties } }; - template<> - struct JsonCaster + template + struct JsonCaster { static SettingsProperties cast(const nlohmann::json& json) { @@ -129,14 +134,14 @@ class SettingsProperties } }; - template<> - struct JsonCaster + template + struct JsonCaster { static nlohmann::json cast(const nlohmann::json& json) { return json; } }; - template - struct JsonCaster> + template + struct JsonCaster, gccfix> { using ArrayType = std::array; static bool assertHelper(const nlohmann::json& json) { return isType(json); } @@ -311,26 +316,22 @@ class FALCOR_API Settings // Adds to global option list. // It is a nested list of dictionaries - void addOptions(const pybind11::dict& options) - { - auto json = pyjson::to_json(options); - merge(getActive().mOptions, json); - updateSearchPaths(json); - } - void addOptions(const Dictionary& options) { addOptions(options.toPython()); } + void addOptions(const pybind11::dict& options); + /// Add options from a JSON file, returning true on success and false on failure bool addOptions(const std::filesystem::path& path); // Clears the global options to defaults void clearOptions() { getActive().mOptions.clear(); } - /** Attributes don't really belong here. They should be part of the loadScene. - However, if you load scenes from the GUI, you want the attributes to get applied to all the scenes - (think setting attributes to "make all curves tessellate fine"). - - So this, which is effectively an attribute filter (RIF, if you will) need to live in some - reasonably global place. This will be replaced with a more principled solution in the new Scene. - */ + /** + * Attributes don't really belong here. They should be part of the loadScene. + * However, if you load scenes from the GUI, you want the attributes to get applied to all the scenes + * (think setting attributes to "make all curves tessellate fine"). + * + * So this, which is effectively an attribute filter (RIF, if you will) need to live in some + * reasonably global place. This will be replaced with a more principled solution in the new Scene. + */ template std::optional getAttribute(const std::string_view shapeName, const std::string_view attributeName) const @@ -425,11 +426,10 @@ class FALCOR_API Settings // will *NOT* disable motion on everything and then re-enable it for the tiger, // they will only set the "enableMotion = False" on the Tiger, while leaving // everything else to default (which happens to be "False" as well) - void addFilteredAttributes(const pybind11::dict& attributes) { merge(getActive().mFilteredAttributes, pyjson::to_json(attributes)); } - void addFilteredAttributes(const Dictionary& attributes) { addFilteredAttributes(attributes.toPython()); } + void addFilteredAttributes(const pybind11::dict& attributes); // Clears all the attributes to default - void clearFilteredAttributes() { getActive().mFilteredAttributes.clear(); } + void clearFilteredAttributes(); /** * Returns search paths from the given category. diff --git a/Source/Falcor/Utils/SharedCache.h b/Source/Falcor/Utils/SharedCache.h new file mode 100644 index 000000000..5d1b4f648 --- /dev/null +++ b/Source/Falcor/Utils/SharedCache.h @@ -0,0 +1,75 @@ +/*************************************************************************** + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. + # + # Redistribution and use in source and binary forms, with or without + # modification, are permitted provided that the following conditions + # are met: + # * Redistributions of source code must retain the above copyright + # notice, this list of conditions and the following disclaimer. + # * Redistributions in binary form must reproduce the above copyright + # notice, this list of conditions and the following disclaimer in the + # documentation and/or other materials provided with the distribution. + # * Neither the name of NVIDIA CORPORATION nor the names of its + # contributors may be used to endorse or promote products derived + # from this software without specific prior written permission. + # + # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY + # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + # (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 + +namespace Falcor +{ + +/** + * Helper class for managing a shared cache. + * + * This is used in a few places where Falcor used global statics in the past. + * Because Falcor now supports multiple devices, global statics don't work anymore. + * The shared cache is used locally in a file where some resource is shared + * among all instances of a class. The first instance creates the shared + * resource, all subsequent instances can reuse the cached data. If all instances + * are destroyed, the shared resource is automatically released as only the + * instances hold a shared_ptr to the cached item, the cache itself only + * holds a weak_ptr. Using a Key type, we can cache multiple versions of the same + * data, typically used to cache one set for every GPU device instance. + */ +template +struct SharedCache +{ + std::shared_ptr acquire(Key key, const std::function()>& init) + { + std::lock_guard lock(mutex); + auto it = cache.find(key); + if (it != cache.end()) + { + if (auto data = it->second.lock()) + return data; + else + cache.erase(it); + } + + std::shared_ptr data = init(); + cache.emplace(key, data); + return data; + } + + std::mutex mutex; + std::map> cache; +}; + +} // namespace Falcor diff --git a/Source/Falcor/Utils/SlangUtils.slang b/Source/Falcor/Utils/SlangUtils.slang index ef5d5a4fb..114302f23 100644 --- a/Source/Falcor/Utils/SlangUtils.slang +++ b/Source/Falcor/Utils/SlangUtils.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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,11 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **************************************************************************/ -/** Template Max, used to use to ensure we do not have zero sized arrays. - The usage pattern is `float name[ArrayMax.value]` and the array will always be at least 1 -*/ -struct ArrayMax { static const int value = (M < N) ? N : M; } +/** + * Template Max, used to use to ensure we do not have zero sized arrays. + * The usage pattern is `float name[ArrayMax.value]` and the array will always be at least 1 + */ +struct ArrayMax +{ + static const int value = (M < N) ? N : M; +} diff --git a/Source/Falcor/Utils/StringUtils.cpp b/Source/Falcor/Utils/StringUtils.cpp index b4d8c1a02..b7be1c58a 100644 --- a/Source/Falcor/Utils/StringUtils.cpp +++ b/Source/Falcor/Utils/StringUtils.cpp @@ -33,275 +33,314 @@ namespace Falcor { - bool hasPrefix(const std::string& str, const std::string& prefix, bool caseSensitive) +bool hasPrefix(const std::string& str, const std::string& prefix, bool caseSensitive) +{ + if (str.size() >= prefix.size()) { - if(str.size() >= prefix.size()) + if (caseSensitive == false) { - if(caseSensitive == false) - { - std::string s = str; - std::string pfx = prefix; - std::transform(str.begin(), str.end(), s.begin(), ::tolower); - std::transform(prefix.begin(), prefix.end(), pfx.begin(), ::tolower); - return s.compare(0, pfx.length(), pfx) == 0; - } - else - { - return str.compare(0, prefix.length(), prefix) == 0; - } + std::string s = str; + std::string pfx = prefix; + std::transform(str.begin(), str.end(), s.begin(), ::tolower); + std::transform(prefix.begin(), prefix.end(), pfx.begin(), ::tolower); + return s.compare(0, pfx.length(), pfx) == 0; } - return false; - } - - bool hasSuffix(const std::string& str, const std::string& suffix, bool caseSensitive) - { - if(str.size() >= suffix.size()) + else { - std::string s = str.substr(str.length() - suffix.length()); - if(caseSensitive == false) - { - std::string sfx = suffix; - std::transform(s.begin(), s.end(), s.begin(), ::tolower); - std::transform(sfx.begin(), sfx.end(), sfx.begin(), ::tolower); - return (sfx == s); - } - else - { - return (s == suffix); - } + return str.compare(0, prefix.length(), prefix) == 0; } - return false; } + return false; +} - std::vector splitString(const std::string& str, const std::string& delim) +bool hasSuffix(const std::string& str, const std::string& suffix, bool caseSensitive) +{ + if (str.size() >= suffix.size()) { - std::string s; - std::vector vec; - for(char c : str) + std::string s = str.substr(str.length() - suffix.length()); + if (caseSensitive == false) { - if(delim.find(c) != std::string::npos) - { - if(s.length()) - { - vec.push_back(s); - s.clear(); - } - } - else - { - s += c; - } + std::string sfx = suffix; + std::transform(s.begin(), s.end(), s.begin(), ::tolower); + std::transform(sfx.begin(), sfx.end(), sfx.begin(), ::tolower); + return (sfx == s); } - if(s.length()) + else { - vec.push_back(s); + return (s == suffix); } - return vec; } + return false; +} - std::string joinStrings(const std::vector& strings, const std::string& separator) +std::vector splitString(const std::string& str, const std::string& delim) +{ + std::string s; + std::vector vec; + for (char c : str) { - std::string result; - for(auto it = strings.begin(); it != strings.end(); it++) + if (delim.find(c) != std::string::npos) { - result += *it; - - if(it != strings.end() - 1) + if (s.length()) { - result += separator; + vec.push_back(s); + s.clear(); } } - return result; - } - - std::string removeLeadingWhitespace(const std::string& str, const char* whitespace) - { - std::string result(str); - result.erase(0, result.find_first_not_of(whitespace)); - return result; + else + { + s += c; + } } - - std::string removeTrailingWhitespace(const std::string& str, const char* whitespace) + if (s.length()) { - std::string result(str); - result.erase(result.find_last_not_of(whitespace) + 1); - return result; + vec.push_back(s); } + return vec; +} - std::string removeLeadingTrailingWhitespace(const std::string& str, const char* whitespace) +std::string joinStrings(const std::vector& strings, const std::string& separator) +{ + std::string result; + for (auto it = strings.begin(); it != strings.end(); it++) { - return removeTrailingWhitespace(removeLeadingWhitespace(str, whitespace), whitespace); - } + result += *it; - std::string replaceCharacters(const std::string& str, const char* characters, const char replacement) - { - std::string result(str); - size_t pos = result.find_first_of(characters); - while (pos != std::string::npos) + if (it != strings.end() - 1) { - result[pos] = replacement; - pos = result.find_first_of(characters, pos); + result += separator; } - return result; } + return result; +} + +std::string removeLeadingWhitespace(const std::string& str, const char* whitespace) +{ + std::string result(str); + result.erase(0, result.find_first_not_of(whitespace)); + return result; +} + +std::string removeTrailingWhitespace(const std::string& str, const char* whitespace) +{ + std::string result(str); + result.erase(result.find_last_not_of(whitespace) + 1); + return result; +} + +std::string removeLeadingTrailingWhitespace(const std::string& str, const char* whitespace) +{ + return removeTrailingWhitespace(removeLeadingWhitespace(str, whitespace), whitespace); +} - std::string padStringToLength(const std::string& str, size_t length, char padding) +std::string replaceCharacters(const std::string& str, const char* characters, const char replacement) +{ + std::string result(str); + size_t pos = result.find_first_of(characters); + while (pos != std::string::npos) { - std::string result = str; - if (result.length() < length) result.resize(length, padding); - return result; + result[pos] = replacement; + pos = result.find_first_of(characters, pos); } + return result; +} + +std::string padStringToLength(const std::string& str, size_t length, char padding) +{ + std::string result = str; + if (result.length() < length) + result.resize(length, padding); + return result; +} - std::string replaceSubstring(const std::string& input, const std::string& src, const std::string& dst) +std::string replaceSubstring(const std::string& input, const std::string& src, const std::string& dst) +{ + std::string res = input; + size_t offset = res.find(src); + while (offset != std::string::npos) { - std::string res = input; - size_t offset = res.find(src); - while (offset != std::string::npos) - { - res.replace(offset, src.length(), dst); - offset += dst.length(); - offset = res.find(src, offset); - } - return res; + res.replace(offset, src.length(), dst); + offset += dst.length(); + offset = res.find(src, offset); } + return res; +} - bool parseArrayIndex(const std::string& name, std::string& nonArray, uint32_t& index) +std::string decodeURI(const std::string& input) +{ + std::string result; + for (size_t i = 0; i < input.length(); i++) { - size_t dot = name.find_last_of('.'); - size_t bracket = name.find_last_of('['); - - if(bracket != std::string::npos) + if (input[i] == '%') { - // Ignore cases where the last index is an array of struct index (SomeStruct[1].v should be ignored) - if((dot == std::string::npos) || (bracket > dot)) + if (i + 2 < input.length()) { - // We know we have an array index. Make sure it's in range - std::string indexStr = name.substr(bracket + 1); - char* pEndPtr; - index = strtol(indexStr.c_str(), &pEndPtr, 0); - FALCOR_ASSERT(*pEndPtr == ']'); - nonArray = name.substr(0, bracket); - return true; + std::string hex = input.substr(i + 1, 2); + char c = static_cast(strtol(hex.c_str(), nullptr, 16)); + result += c; + i += 2; } } - - return false; + else if (input[i] == '+') + { + result += ' '; + } + else + { + result += input[i]; + } } + return result; +} - void copyStringToBuffer(char* buffer, uint32_t bufferSize, const std::string& s) - { - const uint32_t length = std::min(bufferSize - 1, (uint32_t)s.length()); - s.copy(buffer, length); - buffer[length] = '\0'; - } +bool parseArrayIndex(const std::string& name, std::string& nonArray, uint32_t& index) +{ + size_t dot = name.find_last_of('.'); + size_t bracket = name.find_last_of('['); - std::string formatByteSize(size_t size) + if (bracket != std::string::npos) { - if (size < 1024ull) - return fmt::format("{} B", size); - else if (size < 1048576ull) - return fmt::format("{:.2f} kB", size / 1024.0); - else if (size < 1073741824ull) - return fmt::format("{:.2f} MB", size / 1048576.0); - else if (size < 1099511627776ull) - return fmt::format("{:.2f} GB", size / 1073741824.0); - else - return fmt::format("{:.2f} TB", size / 1099511627776.0); + // Ignore cases where the last index is an array of struct index (SomeStruct[1].v should be ignored) + if ((dot == std::string::npos) || (bracket > dot)) + { + // We know we have an array index. Make sure it's in range + std::string indexStr = name.substr(bracket + 1); + char* pEndPtr; + index = strtol(indexStr.c_str(), &pEndPtr, 0); + FALCOR_ASSERT(*pEndPtr == ']'); + nonArray = name.substr(0, bracket); + return true; + } } - std::string encodeBase64(const void* data, size_t len) - { - // based on https://gist.github.com/tomykaira/f0fd86b6c73063283afe550bc5d77594 - static constexpr char kEncodingTable[] = { - 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', - 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', - 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', - 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', - 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', - 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', - 'w', 'x', 'y', 'z', '0', '1', '2', '3', - '4', '5', '6', '7', '8', '9', '+', '/' - }; + return false; +} - size_t outLen = 4 * ((len + 2) / 3); - std::string out(outLen, '\0'); +void copyStringToBuffer(char* buffer, uint32_t bufferSize, const std::string& s) +{ + const uint32_t length = std::min(bufferSize - 1, (uint32_t)s.length()); + s.copy(buffer, length); + buffer[length] = '\0'; +} - const uint8_t* pIn = reinterpret_cast(data); - auto pOut = out.data(); +std::string formatByteSize(size_t size) +{ + if (size < 1024ull) + return fmt::format("{} B", size); + else if (size < 1048576ull) + return fmt::format("{:.2f} kB", size / 1024.0); + else if (size < 1073741824ull) + return fmt::format("{:.2f} MB", size / 1048576.0); + else if (size < 1099511627776ull) + return fmt::format("{:.2f} GB", size / 1073741824.0); + else + return fmt::format("{:.2f} TB", size / 1099511627776.0); +} - size_t i; - for (i = 0; i + 2 < len; i += 3) +std::string encodeBase64(const void* data, size_t len) +{ + // based on https://gist.github.com/tomykaira/f0fd86b6c73063283afe550bc5d77594 + // clang-format off + static constexpr char kEncodingTable[] = { + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', + 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', + 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', + 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', + 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', + 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', + 'w', 'x', 'y', 'z', '0', '1', '2', '3', + '4', '5', '6', '7', '8', '9', '+', '/' + }; + // clang-format on + + size_t outLen = 4 * ((len + 2) / 3); + std::string out(outLen, '\0'); + + const uint8_t* pIn = reinterpret_cast(data); + auto pOut = out.data(); + + size_t i; + for (i = 0; i + 2 < len; i += 3) + { + *pOut++ = kEncodingTable[(pIn[i] >> 2) & 0x3f]; + *pOut++ = kEncodingTable[((pIn[i] & 0x3) << 4) | ((pIn[i + 1] & 0xf0) >> 4)]; + *pOut++ = kEncodingTable[((pIn[i + 1] & 0xf) << 2) | ((pIn[i + 2] & 0xc0) >> 6)]; + *pOut++ = kEncodingTable[pIn[i + 2] & 0x3f]; + } + if (i < len) + { + *pOut++ = kEncodingTable[(pIn[i] >> 2) & 0x3f]; + if (i == (len - 1)) { - *pOut++ = kEncodingTable[(pIn[i] >> 2) & 0x3f]; - *pOut++ = kEncodingTable[((pIn[i] & 0x3) << 4) | ((pIn[i + 1] & 0xf0) >> 4)]; - *pOut++ = kEncodingTable[((pIn[i + 1] & 0xf) << 2) | ((pIn[i + 2] & 0xc0) >> 6)]; - *pOut++ = kEncodingTable[pIn[i + 2] & 0x3f]; + *pOut++ = kEncodingTable[((pIn[i] & 0x3) << 4)]; + *pOut++ = '='; } - if (i < len) + else { - *pOut++ = kEncodingTable[(pIn[i] >> 2) & 0x3f]; - if (i == (len - 1)) - { - *pOut++ = kEncodingTable[((pIn[i] & 0x3) << 4)]; - *pOut++ = '='; - } - else - { - *pOut++ = kEncodingTable[((pIn[i] & 0x3) << 4) | ((pIn[i + 1] & 0xf0) >> 4)]; - *pOut++ = kEncodingTable[((pIn[i + 1] & 0xf) << 2)]; - } - *pOut++ = '='; + *pOut++ = kEncodingTable[((pIn[i] & 0x3) << 4) | ((pIn[i + 1] & 0xf0) >> 4)]; + *pOut++ = kEncodingTable[((pIn[i + 1] & 0xf) << 2)]; } - - return out; + *pOut++ = '='; } - std::vector decodeBase64(const std::string& in) - { - // based on https://gist.github.com/tomykaira/f0fd86b6c73063283afe550bc5d77594 - static constexpr uint8_t kDecodingTable[] = { - 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 62, 64, 64, 64, 63, - 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 64, 64, 64, 64, 64, 64, - 64, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, - 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 64, 64, 64, 64, 64, - 64, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, - 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64 - }; + return out; +} - size_t inLen = in.size(); - if (inLen == 0) return {}; - if (inLen % 4 != 0) throw ArgumentError("Input data size is not a multiple of 4"); +std::vector decodeBase64(const std::string& in) +{ + // based on https://gist.github.com/tomykaira/f0fd86b6c73063283afe550bc5d77594 + // clang-format off + static constexpr uint8_t kDecodingTable[] = { + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 62, 64, 64, 64, 63, + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 64, 64, 64, 64, 64, 64, + 64, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 64, 64, 64, 64, 64, + 64, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64 + }; + // clang-format on - size_t outLen = inLen / 4 * 3; - if (in[inLen - 1] == '=') outLen--; - if (in[inLen - 2] == '=') outLen--; + size_t inLen = in.size(); + if (inLen == 0) + return {}; + if (inLen % 4 != 0) + throw ArgumentError("Input data size is not a multiple of 4"); - std::vector out(outLen, 0); + size_t outLen = inLen / 4 * 3; + if (in[inLen - 1] == '=') + outLen--; + if (in[inLen - 2] == '=') + outLen--; - for (size_t i = 0, j = 0; i < inLen;) - { - uint32_t a = in[i] == '=' ? 0 & i++ : kDecodingTable[static_cast(in[i++])]; - uint32_t b = in[i] == '=' ? 0 & i++ : kDecodingTable[static_cast(in[i++])]; - uint32_t c = in[i] == '=' ? 0 & i++ : kDecodingTable[static_cast(in[i++])]; - uint32_t d = in[i] == '=' ? 0 & i++ : kDecodingTable[static_cast(in[i++])]; + std::vector out(outLen, 0); - uint32_t triple = (a << 3 * 6) + (b << 2 * 6) + (c << 1 * 6) + (d << 0 * 6); + for (size_t i = 0, j = 0; i < inLen;) + { + uint32_t a = in[i] == '=' ? 0 & i++ : kDecodingTable[static_cast(in[i++])]; + uint32_t b = in[i] == '=' ? 0 & i++ : kDecodingTable[static_cast(in[i++])]; + uint32_t c = in[i] == '=' ? 0 & i++ : kDecodingTable[static_cast(in[i++])]; + uint32_t d = in[i] == '=' ? 0 & i++ : kDecodingTable[static_cast(in[i++])]; - if (j < outLen) out[j++] = (triple >> 2 * 8) & 0xff; - if (j < outLen) out[j++] = (triple >> 1 * 8) & 0xff; - if (j < outLen) out[j++] = (triple >> 0 * 8) & 0xff; - } + uint32_t triple = (a << 3 * 6) + (b << 2 * 6) + (c << 1 * 6) + (d << 0 * 6); - return out; + if (j < outLen) + out[j++] = (triple >> 2 * 8) & 0xff; + if (j < outLen) + out[j++] = (triple >> 1 * 8) & 0xff; + if (j < outLen) + out[j++] = (triple >> 0 * 8) & 0xff; } + + return out; } +} // namespace Falcor diff --git a/Source/Falcor/Utils/StringUtils.h b/Source/Falcor/Utils/StringUtils.h index a84b46332..47608f160 100644 --- a/Source/Falcor/Utils/StringUtils.h +++ b/Source/Falcor/Utils/StringUtils.h @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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,153 +32,177 @@ #include #include #include -#include #include #include namespace Falcor { - /** Check is a string starts with another string - \param[in] str String to check in - \param[in] prefix Prefix to check for - \param[in] caseSensitive Whether comparison should be case-sensitive - \return Returns true if string starts with the specified prefix. - */ - FALCOR_API bool hasPrefix(const std::string& str, const std::string& prefix, bool caseSensitive = true); - - /** Check is a string ends with another string - \param[in] str String to check in - \param[in] suffix Suffix to check for - \param[in] caseSensitive Whether comparison should be case-sensitive - \return Returns true if string ends with the specified suffix - */ - FALCOR_API bool hasSuffix(const std::string& str, const std::string& suffix, bool caseSensitive = true); - - /** Split a string into a vector of strings based on d delimiter - \param[in] str String to split - \param[in] delim Delimiter to split strings by - \return Array of split strings excluding delimiters. - */ - FALCOR_API std::vector splitString(const std::string& str, const std::string& delim); - - /** Join an array of strings separated by another set string - \param[in] strings Array of strings to join. - \param[in] separator String placed between each string to be joined. - \return Joined string. - */ - FALCOR_API std::string joinStrings(const std::vector& strings, const std::string& separator); - - /** Remove leading whitespace. - \param[in] str String to operate on. - \param[in] whitespace Whitespace characters. - \return String with leading whitespace removed. - */ - FALCOR_API std::string removeLeadingWhitespace(const std::string& str, const char* whitespace = " \n\r\t"); - - /** Remove trailing whitespace. - \param[in] str String to operate on. - \param[in] whitespace Whitespace characters. - \return String with trailing whitespace removed. - */ - FALCOR_API std::string removeTrailingWhitespace(const std::string& str, const char* whitespace = " \n\r\t"); - - /** Remove leading and trailing whitespace. - \param[in] str String to operate on. - \param[in] whitespace Whitespace characters. - \return String with leading and trailing whitespace removed. - */ - FALCOR_API std::string removeLeadingTrailingWhitespace(const std::string& str, const char* whitespace = " \n\r\t"); - - /** Replace a set of character. - Example: replaceCharacters("some/path with/whitespace", "/ ", '_') returns "some_path_with_whitespace" - \param[in] str String to operate on. - \param[in] characters Set of characters to replace. - \param[in] replacement Character to use as a replacement. - \return String with characeters replaced. - */ - FALCOR_API std::string replaceCharacters(const std::string& str, const char* characters, const char replacement); - - /** Pad string to minimum length. - */ - FALCOR_API std::string padStringToLength(const std::string& str, size_t length, char padding = ' '); - - /** Replace all occurrences of a substring in a string. The function doesn't change the original string. - \param input The input string - \param src The substring to replace - \param dst The substring to replace Src with - */ - FALCOR_API std::string replaceSubstring(const std::string& input, const std::string& src, const std::string& dst); - - /** Parses a string in the format []. If format is valid, outputs the base name and the array index. - \param[in] name String to parse - \param[out] nonArray Becomes set to the non-array index portion of the string - \param[out] index Becomes set to the index value parsed from the string - \return Whether string was successfully parsed. - */ - FALCOR_API bool parseArrayIndex(const std::string& name, std::string& nonArray, uint32_t& index); - - /** Copy text from a std::string to a char buffer, ensures null termination. - */ - FALCOR_API void copyStringToBuffer(char* buffer, uint32_t bufferSize, const std::string& s); - - /** Converts a size in bytes to a human readable string: - - prints bytes (B) if size < 1000 bytes - - prints kilobytes (KB) if size < 1000 kilobytes - - prints megabytes (MB) if size < 1000 megabytes - - prints gigabytes (GB) if size < 1000 gigabytes - - otherwise prints terabytes (TB) - \param[in] size Size in bytes - \return Returns a human readable string. - */ - FALCOR_API std::string formatByteSize(size_t size); - - /** Convert an ASCII string to lower case. - */ - inline std::string toLowerCase(const std::string& str) - { - std::string s = str; - std::transform(str.begin(), str.end(), s.begin(), ::tolower); - return s; - } - - /** Convert an ASCII string to a UTF-8 wstring - */ - inline std::wstring string_2_wstring(const std::string& s) - { - std::wstring_convert> cvt; - std::wstring ws = cvt.from_bytes(s); - return ws; - } - - /** Convert a UTF-8 wstring to an ASCII string - */ - inline std::string wstring_2_string(const std::wstring& ws) - { - std::wstring_convert> cvt; - std::string s = cvt.to_bytes(ws); - return s; - } - - /** Convert a UTF-32 codepoint to a UTF-8 string - */ - inline std::string utf32ToUtf8(uint32_t codepoint) - { - std::wstring_convert, char32_t> cvt; - return cvt.to_bytes(codepoint); - } - - /** Encode data into base 64 encoding. - */ - FALCOR_API std::string encodeBase64(const void* data, size_t len); - - /** Encode data into base 64 encoding. - */ - inline std::string encodeBase64(const std::vector& in) - { - return encodeBase64(in.data(), in.size()); - } - - /** Decode data from base 64 encoding. - */ - FALCOR_API std::vector decodeBase64(const std::string& in); -}; +/** + * Check is a string starts with another string + * @param[in] str String to check in + * @param[in] prefix Prefix to check for + * @param[in] caseSensitive Whether comparison should be case-sensitive + * @return Returns true if string starts with the specified prefix. + */ +FALCOR_API bool hasPrefix(const std::string& str, const std::string& prefix, bool caseSensitive = true); + +/** + * Check is a string ends with another string + * @param[in] str String to check in + * @param[in] suffix Suffix to check for + * @param[in] caseSensitive Whether comparison should be case-sensitive + * @return Returns true if string ends with the specified suffix + */ +FALCOR_API bool hasSuffix(const std::string& str, const std::string& suffix, bool caseSensitive = true); + +/** + * Split a string into a vector of strings based on d delimiter + * @param[in] str String to split + * @param[in] delim Delimiter to split strings by + * @return Array of split strings excluding delimiters. + */ +FALCOR_API std::vector splitString(const std::string& str, const std::string& delim); + +/** + * Join an array of strings separated by another set string + * @param[in] strings Array of strings to join. + * @param[in] separator String placed between each string to be joined. + * @return Joined string. + */ +FALCOR_API std::string joinStrings(const std::vector& strings, const std::string& separator); + +/** + * Remove leading whitespace. + * @param[in] str String to operate on. + * @param[in] whitespace Whitespace characters. + * @return String with leading whitespace removed. + */ +FALCOR_API std::string removeLeadingWhitespace(const std::string& str, const char* whitespace = " \n\r\t"); + +/** + * Remove trailing whitespace. + * @param[in] str String to operate on. + * @param[in] whitespace Whitespace characters. + * @return String with trailing whitespace removed. + */ +FALCOR_API std::string removeTrailingWhitespace(const std::string& str, const char* whitespace = " \n\r\t"); + +/** + * Remove leading and trailing whitespace. + * @param[in] str String to operate on. + * @param[in] whitespace Whitespace characters. + * @return String with leading and trailing whitespace removed. + */ +FALCOR_API std::string removeLeadingTrailingWhitespace(const std::string& str, const char* whitespace = " \n\r\t"); + +/** + * Replace a set of character. + * Example: replaceCharacters("some/path with/whitespace", "/ ", '_') returns "some_path_with_whitespace" + * @param[in] str String to operate on. + * @param[in] characters Set of characters to replace. + * @param[in] replacement Character to use as a replacement. + * @return String with characeters replaced. + */ +FALCOR_API std::string replaceCharacters(const std::string& str, const char* characters, const char replacement); + +/** + * Pad string to minimum length. + */ +FALCOR_API std::string padStringToLength(const std::string& str, size_t length, char padding = ' '); + +/** + * Replace all occurrences of a substring in a string. The function doesn't change the original string. + * @param input The input string + * @param src The substring to replace + * @param dst The substring to replace Src with + */ +FALCOR_API std::string replaceSubstring(const std::string& input, const std::string& src, const std::string& dst); + +/** + * Decode an URI string. + */ +FALCOR_API std::string decodeURI(const std::string& input); + +/** + * Parses a string in the format []. If format is valid, outputs the base name and the array index. + * @param[in] name String to parse + * @param[out] nonArray Becomes set to the non-array index portion of the string + * @param[out] index Becomes set to the index value parsed from the string + * @return Whether string was successfully parsed. + */ +FALCOR_API bool parseArrayIndex(const std::string& name, std::string& nonArray, uint32_t& index); + +/** + * Copy text from a std::string to a char buffer, ensures null termination. + */ +FALCOR_API void copyStringToBuffer(char* buffer, uint32_t bufferSize, const std::string& s); + +/** + * Converts a size in bytes to a human readable string: + * - prints bytes (B) if size < 1000 bytes + * - prints kilobytes (KB) if size < 1000 kilobytes + * - prints megabytes (MB) if size < 1000 megabytes + * - prints gigabytes (GB) if size < 1000 gigabytes + * - otherwise prints terabytes (TB) + * @param[in] size Size in bytes + * @return Returns a human readable string. + */ +FALCOR_API std::string formatByteSize(size_t size); + +/** + * Convert an ASCII string to lower case. + */ +inline std::string toLowerCase(const std::string& str) +{ + std::string s = str; + std::transform(str.begin(), str.end(), s.begin(), ::tolower); + return s; +} + +/** + * Convert an ASCII string to a UTF-8 wstring + */ +inline std::wstring string_2_wstring(const std::string& s) +{ + std::wstring_convert> cvt; + std::wstring ws = cvt.from_bytes(s); + return ws; +} + +/** + * Convert a UTF-8 wstring to an ASCII string + */ +inline std::string wstring_2_string(const std::wstring& ws) +{ + std::wstring_convert> cvt; + std::string s = cvt.to_bytes(ws); + return s; +} + +/** + * Convert a UTF-32 codepoint to a UTF-8 string + */ +inline std::string utf32ToUtf8(uint32_t codepoint) +{ + std::wstring_convert, char32_t> cvt; + return cvt.to_bytes(codepoint); +} + +/** + * Encode data into base 64 encoding. + */ +FALCOR_API std::string encodeBase64(const void* data, size_t len); + +/** + * Encode data into base 64 encoding. + */ +inline std::string encodeBase64(const std::vector& in) +{ + return encodeBase64(in.data(), in.size()); +} + +/** + * Decode data from base 64 encoding. + */ +FALCOR_API std::vector decodeBase64(const std::string& in); +}; // namespace Falcor diff --git a/Source/Falcor/Utils/TermColor.cpp b/Source/Falcor/Utils/TermColor.cpp index be924ecc7..d267e2e5a 100644 --- a/Source/Falcor/Utils/TermColor.cpp +++ b/Source/Falcor/Utils/TermColor.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -48,52 +48,57 @@ namespace Falcor { #if FALCOR_WINDOWS - /** The Windows console does not have ANSI support by default, - but it can be enabled through SetConsoleMode(). - We use static initialization to do so. - */ - struct EnableVirtualTerminal +/** + * The Windows console does not have ANSI support by default, + * but it can be enabled through SetConsoleMode(). + * We use static initialization to do so. + */ +struct EnableVirtualTerminal +{ + EnableVirtualTerminal() { - EnableVirtualTerminal() + auto enableVirtualTerminal = [](DWORD handle) { - auto enableVirtualTerminal = [] (DWORD handle) - { - HANDLE console = GetStdHandle(handle); - if (console == INVALID_HANDLE_VALUE) return; - DWORD mode; - GetConsoleMode(console, &mode); - mode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING; - SetConsoleMode(console, mode); - }; - enableVirtualTerminal(STD_OUTPUT_HANDLE); - enableVirtualTerminal(STD_ERROR_HANDLE); - } - }; + HANDLE console = GetStdHandle(handle); + if (console == INVALID_HANDLE_VALUE) + return; + DWORD mode; + GetConsoleMode(console, &mode); + mode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING; + SetConsoleMode(console, mode); + }; + enableVirtualTerminal(STD_OUTPUT_HANDLE); + enableVirtualTerminal(STD_ERROR_HANDLE); + } +}; - static EnableVirtualTerminal sEnableVirtualTerminal; +static EnableVirtualTerminal sEnableVirtualTerminal; #endif // FALCOR_WINDOWS - static const std::unordered_map kBeginTag = - { - { TermColor::Gray, "\33[90m" }, - { TermColor::Red, "\33[91m" }, - { TermColor::Green, "\33[92m" }, - { TermColor::Yellow, "\33[93m" }, - { TermColor::Blue, "\33[94m" }, - { TermColor::Magenta, "\33[95m" } - }; +static const std::unordered_map kBeginTag = { + // clang-format off + { TermColor::Gray, "\33[90m" }, + { TermColor::Red, "\33[91m" }, + { TermColor::Green, "\33[92m" }, + { TermColor::Yellow, "\33[93m" }, + { TermColor::Blue, "\33[94m" }, + { TermColor::Magenta, "\33[95m" }, + // clang-format on +}; - static const std::string kEndTag = "\033[0m"; +static const std::string kEndTag = "\033[0m"; - inline bool isTTY(const std::ostream& stream) - { - if (&stream == &std::cout && ISATTY(FILENO(stdout))) return true; - if (&stream == &std::cerr && ISATTY(FILENO(stderr))) return true; - return false; - } +inline bool isTTY(const std::ostream& stream) +{ + if (&stream == &std::cout && ISATTY(FILENO(stdout))) + return true; + if (&stream == &std::cerr && ISATTY(FILENO(stderr))) + return true; + return false; +} - std::string colored(const std::string& str, TermColor color, const std::ostream& stream) - { - return isTTY(stream) ? (kBeginTag.at(color) + str + kEndTag) : str; - } +std::string colored(const std::string& str, TermColor color, const std::ostream& stream) +{ + return isTTY(stream) ? (kBeginTag.at(color) + str + kEndTag) : str; } +} // namespace Falcor diff --git a/Source/Falcor/Utils/TermColor.h b/Source/Falcor/Utils/TermColor.h index cc2b3f295..c6bb6de1d 100644 --- a/Source/Falcor/Utils/TermColor.h +++ b/Source/Falcor/Utils/TermColor.h @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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,21 +31,22 @@ namespace Falcor { - enum class TermColor - { - Gray, - Red, - Green, - Yellow, - Blue, - Magenta - }; +enum class TermColor +{ + Gray, + Red, + Green, + Yellow, + Blue, + Magenta +}; - /** Colorize a string for writing to a terminal. Return original string if stream is not a terminal. - \param[in] str String to colorize - \param[in] color Color - \param[in] stream Output stream - \return Returns string wrapped in color codes if stream is not a terminal, original string otherwise. - */ - std::string colored(const std::string& str, TermColor color, const std::ostream& stream = std::cout); -} +/** + * Colorize a string for writing to a terminal. Return original string if stream is not a terminal. + * @param[in] str String to colorize + * @param[in] color Color + * @param[in] stream Output stream + * @return Returns string wrapped in color codes if stream is not a terminal, original string otherwise. + */ +std::string colored(const std::string& str, TermColor color, const std::ostream& stream = std::cout); +} // namespace Falcor diff --git a/Source/Falcor/Utils/Threading.cpp b/Source/Falcor/Utils/Threading.cpp index 9bbcbe4a6..af8b9debd 100644 --- a/Source/Falcor/Utils/Threading.cpp +++ b/Source/Falcor/Utils/Threading.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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,65 +30,67 @@ namespace Falcor { - namespace - { - struct ThreadingData - { - bool initialized = false; - std::vector threads; - uint32_t current; - } gData; // TODO: REMOVEGLOBAL - } +namespace +{ +struct ThreadingData +{ + bool initialized = false; + std::vector threads; + uint32_t current; +} gData; // TODO: REMOVEGLOBAL +} // namespace - void Threading::start(uint32_t threadCount) - { - if (gData.initialized) return; +void Threading::start(uint32_t threadCount) +{ + if (gData.initialized) + return; - gData.threads.resize(threadCount); - gData.initialized = true; - } + gData.threads.resize(threadCount); + gData.initialized = true; +} - void Threading::shutdown() +void Threading::shutdown() +{ + for (auto& t : gData.threads) { - for (auto& t : gData.threads) - { - if (t.joinable()) t.join(); - } - - gData.initialized = false; + if (t.joinable()) + t.join(); } - Threading::Task Threading::dispatchTask(const std::function& func) - { - FALCOR_ASSERT(gData.initialized); + gData.initialized = false; +} - std::thread& t = gData.threads[gData.current]; - if (t.joinable()) t.join(); - t = std::thread(func); - gData.current = (gData.current + 1) % gData.threads.size(); +Threading::Task Threading::dispatchTask(const std::function& func) +{ + FALCOR_ASSERT(gData.initialized); - return Task(); - } + std::thread& t = gData.threads[gData.current]; + if (t.joinable()) + t.join(); + t = std::thread(func); + gData.current = (gData.current + 1) % gData.threads.size(); - void Threading::finish() - { - for (auto& t : gData.threads) - { - if (t.joinable()) t.join(); - } - } + return Task(); +} - Threading::Task::Task() +void Threading::finish() +{ + for (auto& t : gData.threads) { + if (t.joinable()) + t.join(); } +} - bool Threading::Task::isRunning() - { - FALCOR_UNIMPLEMENTED(); - } +Threading::Task::Task() {} - void Threading::Task::finish() - { - FALCOR_UNIMPLEMENTED(); - } +bool Threading::Task::isRunning() +{ + FALCOR_UNIMPLEMENTED(); +} + +void Threading::Task::finish() +{ + FALCOR_UNIMPLEMENTED(); } +} // namespace Falcor diff --git a/Source/Falcor/Utils/Threading.h b/Source/Falcor/Utils/Threading.h index bd3c77614..121099f65 100644 --- a/Source/Falcor/Utils/Threading.h +++ b/Source/Falcor/Utils/Threading.h @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -35,95 +35,99 @@ namespace Falcor { - class FALCOR_API Threading +class FALCOR_API Threading +{ +public: + const static uint32_t kDefaultThreadCount = 16; + + /** + * Handle to a dispatched task + * + * TODO: Implementation + */ + class Task { public: - const static uint32_t kDefaultThreadCount = 16; - - /** Handle to a dispatched task - - TODO: Implementation - */ - class Task - { - public: - /** Check if task is still executing - */ - bool isRunning(); - - /** Wait for task to finish executing - */ - void finish(); + /// Check if task is still executing + bool isRunning(); - private: - Task(); - friend class Threading; - }; + /// Wait for task to finish executing + void finish(); - /** 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 - */ - static void finish(); + private: + Task(); + friend class Threading; + }; - /** Waits for all currently executing threads to finish and shuts down the thread pool - */ - static void shutdown(); + /** + * 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 + */ + static void finish(); + + /** + * 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); +}; + +/** + * Simple thread barrier class. + * TODO: Once we move to C++20, we should change users of Barrier to use std::barrier instead. + * The only change necessary will be to use std::barrier::arrive_and_wait() in place of Barrier::wait(). + */ +class FALCOR_API Barrier +{ +public: + Barrier(size_t threadCount, std::function completionFunc = nullptr) + : mThreadCount(threadCount), mWaitCount(threadCount), mCompletionFunc(completionFunc) + {} - /** Returns the maximum number of concurrent threads supported by the hardware - */ - static uint32_t getLogicalThreadCount() { return std::thread::hardware_concurrency(); } + Barrier(const Barrier& barrier) = delete; + Barrier& operator=(const Barrier& barrier) = delete; - /** Starts a task on an available thread. - \return Handle to the task - */ - static Task dispatchTask(const std::function& func); - }; - - /** Simple thread barrier class. - TODO: Once we move to C++20, we should change users of Barrier to use std::barrier instead. - The only change necessary will be to use std::barrier::arrive_and_wait() in place of Barrier::wait(). - */ - class FALCOR_API Barrier + void wait() { - public: - Barrier(size_t threadCount, std::function completionFunc = nullptr) - : mThreadCount(threadCount) - , mWaitCount(threadCount) - , mCompletionFunc(completionFunc) - {} + std::unique_lock lock(mMutex); - Barrier(const Barrier& barrier) = delete; - Barrier& operator=(const Barrier& barrier) = delete; + auto generation = mGeneration; - void wait() + if (--mWaitCount == 0) { - std::unique_lock lock(mMutex); - - auto generation = mGeneration; - - if (--mWaitCount == 0) - { - if (mCompletionFunc) mCompletionFunc(); - ++mGeneration; - mWaitCount = mThreadCount; - mCondition.notify_all(); - } - else - { - mCondition.wait(lock, [this, generation] () { return generation != mGeneration; }); - } + if (mCompletionFunc) + mCompletionFunc(); + ++mGeneration; + mWaitCount = mThreadCount; + mCondition.notify_all(); } - - private: - size_t mThreadCount; - size_t mWaitCount; - size_t mGeneration = 0; - std::function mCompletionFunc; - std::mutex mMutex; - std::condition_variable mCondition; - }; -} + else + { + mCondition.wait(lock, [this, generation]() { return generation != mGeneration; }); + } + } + +private: + size_t mThreadCount; + size_t mWaitCount; + size_t mGeneration = 0; + std::function mCompletionFunc; + std::mutex mMutex; + std::condition_variable mCondition; +}; +} // namespace Falcor diff --git a/Source/Falcor/Utils/Timing/Clock.cpp b/Source/Falcor/Utils/Timing/Clock.cpp index c8407606d..211ab7267 100644 --- a/Source/Falcor/Utils/Timing/Clock.cpp +++ b/Source/Falcor/Utils/Timing/Clock.cpp @@ -32,319 +32,323 @@ namespace Falcor { - namespace +namespace +{ +constexpr char kTime[] = "time"; +constexpr char kFrame[] = "frame"; +constexpr char kFramerate[] = "framerate"; +constexpr char kTimeScale[] = "timeScale"; +constexpr char kExitTime[] = "exitTime"; +constexpr char kExitFrame[] = "exitFrame"; +constexpr char kStartTime[] = "startTime"; +constexpr char kEndTime[] = "endTime"; +constexpr char kPause[] = "pause"; +constexpr char kPlay[] = "play"; +constexpr char kStop[] = "stop"; +constexpr char kStep[] = "step"; + +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 = []() { - constexpr char kTime[] = "time"; - constexpr char kFrame[] = "frame"; - constexpr char kFramerate[] = "framerate"; - constexpr char kTimeScale[] = "timeScale"; - constexpr char kExitTime[] = "exitTime"; - constexpr char kExitFrame[] = "exitFrame"; - constexpr char kStartTime[] = "startTime"; - constexpr char kEndTime[] = "endTime"; - constexpr char kPause[] = "pause"; - constexpr char kPlay[] = "play"; - constexpr char kStop[] = "stop"; - constexpr char kStep[] = "step"; - - 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" : std::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, std::numeric_limits::max(), 1u, false, nullptr); - } - else curVal = index; - - return changed ? std::optional(curVal) : std::nullopt; - } + Gui::DropdownList d; + for (auto f : commonFps) + d.push_back({f, f == 0 ? "Disabled" : std::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, std::numeric_limits::max(), 1u, false, nullptr); + } + else + curVal = index; - struct ClockTextures - { - Texture::SharedPtr pPlay; - Texture::SharedPtr pPause; - Texture::SharedPtr pRewind; - Texture::SharedPtr pStop; - Texture::SharedPtr pNextFrame; - Texture::SharedPtr pPrevFrame; - } gClockTextures; // TODO: REMOVEGLOBAL + return changed ? std::optional(curVal) : std::nullopt; +} - constexpr uint64_t kTicksPerSecond = 14400 * (1 << 16); // 14400 is a common multiple of our supported frame-rates. 2^16 gives 64K intra-frame steps +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; - } +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; - } - } +uint64_t frameFromTime(double seconds, uint64_t ticksPerFrame) +{ + return uint64_t(seconds * (double)kTicksPerSecond) / ticksPerFrame; +} +} // namespace - Clock::Clock() { setTime(0); } +Clock::Clock() +{ + setTime(0); +} - Clock& Clock::setFramerate(uint32_t fps) +Clock& Clock::setFramerate(uint32_t fps) +{ + mFramerate = fps; + mTicksPerFrame = 0; + if (fps) { - mFramerate = fps; - mTicksPerFrame = 0; - if(fps) - { - if (kTicksPerSecond % fps) logWarning("Clock::setFramerate() - requested FPS can't be accurately represented. Expect rounding errors"); - mTicksPerFrame = kTicksPerSecond / fps; - } - - if(!mDeferredFrameID && !mDeferredTime) setTime(mTime.now); - return *this; + if (kTicksPerSecond % fps) + logWarning("Clock::setFramerate() - requested FPS can't be accurately represented. Expect rounding errors"); + mTicksPerFrame = kTicksPerSecond / fps; } - Clock& Clock::setExitTime(double seconds) - { - mExitTime = seconds; - mExitFrame = 0; - return *this; - } + if (!mDeferredFrameID && !mDeferredTime) + setTime(mTime.now); + return *this; +} - Clock& Clock::setExitFrame(uint64_t frame) - { - mExitFrame = frame; - mExitTime = 0.0; - return *this; - } +Clock& Clock::setExitTime(double seconds) +{ + mExitTime = seconds; + mExitFrame = 0; + return *this; +} - bool Clock::shouldExit() const - { - return ((mExitTime && getTime() >= mExitTime) || (mExitFrame && getFrame() >= mExitFrame)); - } +Clock& Clock::setExitFrame(uint64_t frame) +{ + mExitFrame = frame; + mExitTime = 0.0; + return *this; +} - Clock& Clock::tick() - { - if (mDeferredFrameID) setFrame(mDeferredFrameID.value()); - else if (mDeferredTime) setTime(mDeferredTime.value()); - else if(!mPaused) step(); - return *this; - } +bool Clock::shouldExit() const +{ + return ((mExitTime && getTime() >= mExitTime) || (mExitFrame && getFrame() >= mExitFrame)); +} - bool Clock::setStartTime(double time) - { - if (time <= 0.) - { - mStartTime = 0.; - return true; - } - if (mEndTime < 0. || time < mEndTime) - { - mStartTime = time; - return true; - } - return false; - } +Clock& Clock::tick() +{ + if (mDeferredFrameID) + setFrame(mDeferredFrameID.value()); + else if (mDeferredTime) + setTime(mDeferredTime.value()); + else if (!mPaused) + step(); + return *this; +} - bool Clock::setEndTime(double time) +bool Clock::setStartTime(double time) +{ + if (time <= 0.) { - if (time < 0. || mStartTime <= 0.f || time > mStartTime) - { - mEndTime = time; - return true; - } - return false; + mStartTime = 0.; + return true; } - - void Clock::updateTimer() + if (mEndTime < 0. || time < mEndTime) { - mTimer.update(); - mRealtime.update(mRealtime.now + mTimer.delta()); + mStartTime = time; + return true; } + return false; +} - void Clock::resetDeferredObjects() +bool Clock::setEndTime(double time) +{ + if (time < 0. || mStartTime <= 0.f || time > mStartTime) { - mDeferredTime = std::nullopt; - mDeferredFrameID = std::nullopt; + mEndTime = time; + return true; } + return false; +} - Clock& Clock::setTime(double seconds, bool deferToNextTick) - { - resetDeferredObjects(); +void Clock::updateTimer() +{ + mTimer.update(); + mRealtime.update(mRealtime.now + mTimer.delta()); +} - seconds = clampTime(seconds); +void Clock::resetDeferredObjects() +{ + mDeferredTime = std::nullopt; + mDeferredFrameID = std::nullopt; +} - if (deferToNextTick) - { - mDeferredTime = seconds; - } - else - { - updateTimer(); - if (mFramerate) - { - mFrames = frameFromTime(seconds, mTicksPerFrame); - seconds = timeFromFrame(mFrames, mTicksPerFrame); - } - else mFrames = 0; - if (mTime.delta < 0) mTime.delta = 0; - - mTime.delta = mTime.now - seconds; - mTime.now = seconds; - } - return *this; - } +Clock& Clock::setTime(double seconds, bool deferToNextTick) +{ + resetDeferredObjects(); - Clock& Clock::setFrame(uint64_t f, bool deferToNextTick) - { - resetDeferredObjects(); + seconds = clampTime(seconds); - if (deferToNextTick) + if (deferToNextTick) + { + mDeferredTime = seconds; + } + else + { + updateTimer(); + if (mFramerate) { - mDeferredFrameID = f; + mFrames = frameFromTime(seconds, mTicksPerFrame); + seconds = timeFromFrame(mFrames, mTicksPerFrame); } else - { - updateTimer(); - mFrames = f; - if (mFramerate) - { - double orgSecs = timeFromFrame(mFrames, mTicksPerFrame); - // TODO: The clamping really should be on ticks, as should everything else - // except when we actually ask for the actual floating time (e.g., for interpolation). - // Otherwise the rounding will be a terrible mess. - double newSecs = clampTime(orgSecs); - if (newSecs != orgSecs) - mFrames = frameFromTime(newSecs, mTicksPerFrame); - - mTime.delta = mTime.now - newSecs; - if (mTime.delta < 0) mTime.delta = 0; - mTime.now = newSecs; - } - } - return *this; - } + mFrames = 0; + if (mTime.delta < 0) + mTime.delta = 0; - Clock& Clock::play() - { - updateTimer(); - mPaused = false; - return *this; + mTime.delta = mTime.now - seconds; + mTime.now = seconds; } + return *this; +} - Clock& Clock::step(int64_t frames) - { - if (frames < 0 && uint64_t(-frames) > mFrames) mFrames = 0; - else mFrames += frames; +Clock& Clock::setFrame(uint64_t f, bool deferToNextTick) +{ + resetDeferredObjects(); - updateTimer(); - double t = isSimulatingFps() ? timeFromFrame(mFrames, mTicksPerFrame) : ((mTimer.delta() * mScale) + mTime.now); - t = clampTime(t); - mTime.update(t); - return *this; + if (deferToNextTick) + { + mDeferredFrameID = f; } - - void Clock::renderUI(Gui::Window& w) + else { - const auto& tex = gClockTextures; - - float time = (float)getTime(); - float scale = (float)getTimeScale(); - if (w.var("Time##Cur", time, 0.f, FLT_MAX, 0.001f, false, "%.3f")) setTime(time); - if (!isSimulatingFps() && w.var("Scale", scale)) setTimeScale(scale); - bool showStep = mPaused && isSimulatingFps(); - - float indent = showStep ? 10.0f : 60.0f; - w.indent(indent); - static const uint2 iconSize = { 25, 25 }; - if (w.imageButton("Rewind", tex.pRewind, iconSize)) setTime(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) setFramerate(fps.value()); - - if (isSimulatingFps()) + updateTimer(); + mFrames = f; + if (mFramerate) { - uint64_t curFrame = getFrame(); - if (w.var("Frame ID", curFrame)) setFrame(curFrame); + double orgSecs = timeFromFrame(mFrames, mTicksPerFrame); + // TODO: The clamping really should be on ticks, as should everything else + // except when we actually ask for the actual floating time (e.g., for interpolation). + // Otherwise the rounding will be a terrible mess. + double newSecs = clampTime(orgSecs); + if (newSecs != orgSecs) + mFrames = frameFromTime(newSecs, mTicksPerFrame); + + mTime.delta = mTime.now - newSecs; + if (mTime.delta < 0) + mTime.delta = 0; + mTime.now = newSecs; } } + return *this; +} - FALCOR_SCRIPT_BINDING(Clock) - { - using namespace pybind11::literals; - - pybind11::class_ clock(m, "Clock"); - - auto setTime = [](Clock* pClock, double t) {pClock->setTime(t, true); }; - clock.def_property(kTime, &Clock::getTime, setTime); - auto setFrame = [](Clock* pClock, uint64_t f) {pClock->setFrame(f, true); }; - clock.def_property(kFrame, &Clock::getFrame, setFrame); - clock.def_property(kFramerate, &Clock::getFramerate, &Clock::setFramerate); - clock.def_property(kTimeScale, &Clock::getTimeScale, &Clock::setTimeScale); - clock.def_property(kStartTime, &Clock::getStartTime, &Clock::setStartTime); - clock.def_property(kEndTime, &Clock::getEndTime, &Clock::setEndTime); - clock.def_property(kExitTime, &Clock::getExitTime, &Clock::setExitTime); - clock.def_property(kExitFrame, &Clock::getExitFrame, &Clock::setExitFrame); - - clock.def(kPause, &Clock::pause); - clock.def(kPlay, &Clock::play); - clock.def(kStop, &Clock::stop); - clock.def(kStep, &Clock::step, "frames"_a = 1); - } +Clock& Clock::play() +{ + updateTimer(); + mPaused = false; + return *this; +} - void Clock::start(Device* pDevice) - { - auto loadTexture = [pDevice](const std::string& tex) - { - auto pTex = Texture::createFromFile(pDevice, "framework/images/" + tex, false, true); - if (!pTex) throw RuntimeError("Failed to load texture"); - return pTex; - }; - gClockTextures.pRewind = loadTexture("rewind.jpg"); - gClockTextures.pPlay = loadTexture("play.jpg"); - gClockTextures.pPause = loadTexture("pause.jpg"); - gClockTextures.pStop = loadTexture("stop.jpg"); - gClockTextures.pNextFrame = loadTexture("next-frame.jpg"); - gClockTextures.pPrevFrame = loadTexture("prev-frame.jpg"); - } +Clock& Clock::step(int64_t frames) +{ + if (frames < 0 && uint64_t(-frames) > mFrames) + mFrames = 0; + else + mFrames += frames; + + updateTimer(); + double t = isSimulatingFps() ? timeFromFrame(mFrames, mTicksPerFrame) : ((mTimer.delta() * mScale) + mTime.now); + t = clampTime(t); + mTime.update(t); + return *this; +} - void Clock::shutdown() +void Clock::renderUI(Gui::Window& w) +{ + const Texture* pRewind = w.loadImage("framework/images/rewind.jpg"); + const Texture* pPlay = w.loadImage("framework/images/play.jpg"); + const Texture* pPause = w.loadImage("framework/images/pause.jpg"); + const Texture* pStop = w.loadImage("framework/images/stop.jpg"); + const Texture* pNextFrame = w.loadImage("framework/images/next-frame.jpg"); + const Texture* pPrevFrame = w.loadImage("framework/images/prev-frame.jpg"); + + float time = (float)getTime(); + float scale = (float)getTimeScale(); + if (w.var("Time##Cur", time, 0.f, FLT_MAX, 0.001f, false, "%.3f")) + setTime(time); + if (!isSimulatingFps() && w.var("Scale", scale)) + setTimeScale(scale); + bool showStep = mPaused && isSimulatingFps(); + + float indent = showStep ? 10.0f : 60.0f; + w.indent(indent); + static const uint2 iconSize = {25, 25}; + if (w.imageButton("Rewind", pRewind, iconSize)) + setTime(0); + if (showStep && w.imageButton("PrevFrame", pPrevFrame, iconSize, true, true)) + step(-1); + if (w.imageButton("Stop", pStop, iconSize, true, true)) + stop(); + auto pTex = mPaused ? pPlay : pPause; + if (w.imageButton("PlayPause", pTex, iconSize, true, true)) + mPaused ? play() : pause(); + if (showStep && w.imageButton("NextFrame", 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) + setFramerate(fps.value()); + + if (isSimulatingFps()) { - gClockTextures = {}; + uint64_t curFrame = getFrame(); + if (w.var("Frame ID", curFrame)) + setFrame(curFrame); } +} - std::string Clock::getScript(const std::string& var) const - { - std::string s; - s += ScriptWriter::makeSetProperty(var, kTime, 0); - s += ScriptWriter::makeSetProperty(var, kFramerate, mFramerate); - if (mExitTime) s += ScriptWriter::makeSetProperty(var, kExitTime, mExitTime); - if (mExitFrame) s += ScriptWriter::makeSetProperty(var, kExitFrame, mExitFrame); - s += std::string("# If ") + kFramerate + " is not zero, you can use the frame property to set the start frame\n"; - s += "# " + ScriptWriter::makeSetProperty(var, kFrame, 0); - if (mPaused) s += ScriptWriter::makeMemberFunc(var, kPause); - return s; - } +FALCOR_SCRIPT_BINDING(Clock) +{ + using namespace pybind11::literals; + + pybind11::class_ clock(m, "Clock"); + + auto setTime = [](Clock* pClock, double t) { pClock->setTime(t, true); }; + clock.def_property(kTime, &Clock::getTime, setTime); + auto setFrame = [](Clock* pClock, uint64_t f) { pClock->setFrame(f, true); }; + clock.def_property(kFrame, &Clock::getFrame, setFrame); + clock.def_property(kFramerate, &Clock::getFramerate, &Clock::setFramerate); + clock.def_property(kTimeScale, &Clock::getTimeScale, &Clock::setTimeScale); + clock.def_property(kStartTime, &Clock::getStartTime, &Clock::setStartTime); + clock.def_property(kEndTime, &Clock::getEndTime, &Clock::setEndTime); + clock.def_property(kExitTime, &Clock::getExitTime, &Clock::setExitTime); + clock.def_property(kExitFrame, &Clock::getExitFrame, &Clock::setExitFrame); + + clock.def(kPause, &Clock::pause); + clock.def(kPlay, &Clock::play); + clock.def(kStop, &Clock::stop); + clock.def(kStep, &Clock::step, "frames"_a = 1); +} + +std::string Clock::getScript(const std::string& var) const +{ + std::string s; + s += ScriptWriter::makeSetProperty(var, kTime, 0); + s += ScriptWriter::makeSetProperty(var, kFramerate, mFramerate); + if (mExitTime) + s += ScriptWriter::makeSetProperty(var, kExitTime, mExitTime); + if (mExitFrame) + s += ScriptWriter::makeSetProperty(var, kExitFrame, mExitFrame); + s += std::string("# If ") + kFramerate + " is not zero, you can use the frame property to set the start frame\n"; + s += "# " + ScriptWriter::makeSetProperty(var, kFrame, 0); + if (mPaused) + s += ScriptWriter::makeMemberFunc(var, kPause); + return s; } +} // namespace Falcor diff --git a/Source/Falcor/Utils/Timing/Clock.h b/Source/Falcor/Utils/Timing/Clock.h index 0b8a579bc..1eb2fdce8 100644 --- a/Source/Falcor/Utils/Timing/Clock.h +++ b/Source/Falcor/Utils/Timing/Clock.h @@ -35,178 +35,212 @@ 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 FALCOR_API Clock +/** + * 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 FALCOR_API Clock +{ +public: + Clock(); + + /** + * 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& setTime(double seconds, bool deferToNextTick = false); + + /** + * Get the time of the last `tick()` call + */ + double getTime() 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 getDelta() 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& setFrame(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 getFrame() 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 getRealTimeDelta() const { return mRealtime.delta; } + + /** + * Set the time at which to terminate the application. + */ + Clock& setExitTime(double seconds); + + /** + * Get the time at which to terminate the application. + */ + double getExitTime() const { return mExitTime; } + + /** + * Set the frame at which to terminate the application. + */ + Clock& setExitFrame(uint64_t frame); + + /** + * Get the frame at which to terminate the application. + */ + uint64_t getExitFrame() const { return mExitFrame; } + + /** + * Check if the application should be terminated. + */ + bool shouldExit() const; + + /** + * Sets the start time of an endless time loop. Needs end time to be set to work correctly. + * Time then loops from Start Time to End Time (jumping back rather than reverting) + */ + bool setStartTime(double startTime); + double getStartTime() const { return mStartTime; } + + /** + * Sets the end time of an endless time loop. Needs start time to be set to work correctly. + * Time then loops from Start Time to End Time (jumping back rather than reverting) + */ + bool setEndTime(double endTime); + double getEndTime() const { return mEndTime; } + + /** + * 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& setFramerate(uint32_t fps); + + /** + * Get the requested FPS value + */ + uint32_t getFramerate() const { return mFramerate; } + + /** + * Pause the clock + */ + Clock& pause() { - public: - Clock(); - - /** Start the system - */ - static void start(Device* pDevice); - - /** 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& setTime(double seconds, bool deferToNextTick = false); - - /** Get the time of the last `tick()` call - */ - double getTime() 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 getDelta() 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& setFrame(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 getFrame() 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 getRealTimeDelta() const { return mRealtime.delta; } - - /** Set the time at which to terminate the application. - */ - Clock& setExitTime(double seconds); - - /** Get the time at which to terminate the application. - */ - double getExitTime() const { return mExitTime; } - - /** Set the frame at which to terminate the application. - */ - Clock& setExitFrame(uint64_t frame); - - /** Get the frame at which to terminate the application. - */ - uint64_t getExitFrame() const { return mExitFrame; } - - /** Check if the application should be terminated. - */ - bool shouldExit() const; - - /** Sets the start time of an endless time loop. Needs end time to be set to work correctly. - Time then loops from Start Time to End Time (jumping back rather than reverting) - */ - bool setStartTime(double startTime); - double getStartTime() const { return mStartTime; } - - /** Sets the end time of an endless time loop. Needs start time to be set to work correctly. - Time then loops from Start Time to End Time (jumping back rather than reverting) - */ - bool setEndTime(double endTime); - double getEndTime() const { return mEndTime; } - - /** 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& setFramerate(uint32_t fps); - - /** Get the requested FPS value - */ - uint32_t getFramerate() const { return mFramerate; } - - /** Pause the clock - */ - Clock& pause() { mPaused = true; return *this; } - - /** Resume the clock - */ - Clock& play(); - - /** Stop the clock (pause + reset) - */ - Clock& stop() { setTime(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& setTimeScale(double scale) { mScale = scale; return *this; } - - /** Get the scale - */ - double getTimeScale() const { return mScale; } - - /** Check if the clock is paused - */ - bool isPaused() const { return mPaused; } - - /** Check if the clock is in real-time mode - */ - bool isSimulatingFps() 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; - if (delta < 0) delta = 0; - 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; - - double mExitTime = 0.0; - uint64_t mExitFrame = 0; - - void updateTimer(); - void resetDeferredObjects(); - double mStartTime = 0; - double mEndTime = -1; - double clampTime(double time) + mPaused = true; + return *this; + } + + /** + * Resume the clock + */ + Clock& play(); + + /** + * Stop the clock (pause + reset) + */ + Clock& stop() + { + setTime(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& setTimeScale(double scale) + { + mScale = scale; + return *this; + } + + /** + * Get the scale + */ + double getTimeScale() const { return mScale; } + + /** + * Check if the clock is paused + */ + bool isPaused() const { return mPaused; } + + /** + * Check if the clock is in real-time mode + */ + bool isSimulatingFps() 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) { - if (time < mStartTime) - return mStartTime; - return (mEndTime < 0 || time < mEndTime) ? time : mStartTime + time - mEndTime; + delta = time - now; + if (delta < 0) + delta = 0; + 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; + + double mExitTime = 0.0; + uint64_t mExitFrame = 0; + + void updateTimer(); + void resetDeferredObjects(); + double mStartTime = 0; + double mEndTime = -1; + double clampTime(double time) + { + if (time < mStartTime) + return mStartTime; + return (mEndTime < 0 || time < mEndTime) ? time : mStartTime + time - mEndTime; + } +}; +} // namespace Falcor diff --git a/Source/Falcor/Utils/Timing/CpuTimer.h b/Source/Falcor/Utils/Timing/CpuTimer.h index 513d66a72..4cab88398 100644 --- a/Source/Falcor/Utils/Timing/CpuTimer.h +++ b/Source/Falcor/Utils/Timing/CpuTimer.h @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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,50 +30,50 @@ namespace Falcor { - /** Provides CPU timer utilities to the application. - */ - class CpuTimer - { - public: - using TimePoint = std::chrono::time_point; +/** + * Provides CPU timer utilities to the application. + */ +class CpuTimer +{ +public: + using TimePoint = std::chrono::time_point; - /** Returns the current time. - */ - static TimePoint getCurrentTimePoint() - { - return std::chrono::high_resolution_clock::now(); - } + /** + * Returns the current time. + */ + static TimePoint getCurrentTimePoint() { return std::chrono::high_resolution_clock::now(); } - /** Update the timer. - \return The TimePoint of the last update() call. This value is meaningless on it's own. Call calcDuration() to get the duration that passed between two TimePoints. - */ - TimePoint update() - { - auto now = getCurrentTimePoint(); - mElapsedTime = now - mCurrentTime; - mCurrentTime = now; - return mCurrentTime; - } + /** + * Update the timer. + * @return The TimePoint of the last update() call. This value is meaningless on it's own. Call calcDuration() to get the duration that + * passed between two TimePoints. + */ + TimePoint update() + { + auto now = getCurrentTimePoint(); + mElapsedTime = now - mCurrentTime; + mCurrentTime = now; + return mCurrentTime; + } - /** Get the time that passed from the last update() call to the one before that. - \return Elapsed time in seconds. - */ - double delta() const - { - return mElapsedTime.count(); - } + /** + * Get the time that passed from the last update() call to the one before that. + * @return Elapsed time in seconds. + */ + double delta() const { return mElapsedTime.count(); } - /** Calculate the duration in milliseconds between 2 time points. - */ - 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 duration.count() * 1.0e-6; - } + /** + * Calculate the duration in milliseconds between 2 time points. + */ + 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 duration.count() * 1.0e-6; + } - private: - TimePoint mCurrentTime; - std::chrono::duration mElapsedTime; - }; -} +private: + TimePoint mCurrentTime; + std::chrono::duration mElapsedTime; +}; +} // namespace Falcor diff --git a/Source/Falcor/Utils/Timing/FrameRate.cpp b/Source/Falcor/Utils/Timing/FrameRate.cpp index 80247d79a..6f510a8f4 100644 --- a/Source/Falcor/Utils/Timing/FrameRate.cpp +++ b/Source/Falcor/Utils/Timing/FrameRate.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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,11 +30,12 @@ namespace Falcor { - std::string FrameRate::getMsg(bool vsyncOn) const - { - double frameTime = getAverageFrameTime(); - std::string msg = fmt::format("{:.1f} FPS ({:.1f} ms/frame)", 1.f / frameTime, frameTime * 1000.0); - if (vsyncOn) msg += std::string(", VSync"); - return msg; - } +std::string FrameRate::getMsg(bool vsyncOn) const +{ + double frameTime = getAverageFrameTime(); + std::string msg = fmt::format("{:.1f} FPS ({:.1f} ms/frame)", 1.f / frameTime, frameTime * 1000.0); + if (vsyncOn) + msg += std::string(", VSync"); + return msg; } +} // namespace Falcor diff --git a/Source/Falcor/Utils/Timing/FrameRate.h b/Source/Falcor/Utils/Timing/FrameRate.h index af108ace5..dd0861851 100644 --- a/Source/Falcor/Utils/Timing/FrameRate.h +++ b/Source/Falcor/Utils/Timing/FrameRate.h @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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,68 +34,76 @@ namespace Falcor { - /** Helper class for calculating framerate. - */ - class FALCOR_API FrameRate +/** + * Helper class for calculating framerate. + */ +class FALCOR_API FrameRate +{ +public: + FrameRate() { - public: - FrameRate() - { - mFrameTimes.resize(kFrameWindow); - reset(); - } + mFrameTimes.resize(kFrameWindow); + reset(); + } - /** 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 reset() - { - mFrameCount = 0; - mClock.setTime(0).tick(); - } + /** + * 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 reset() + { + mFrameCount = 0; + mClock.setTime(0).tick(); + } - /** Tick the timer. - It is assumed that this is called once per frame, since this frequency is assumed when calculating FPS. - */ - void newFrame() - { - mFrameCount++; - mFrameTimes[mFrameCount % kFrameWindow] = mClock.tick().getRealTimeDelta(); - mClock.setTime(0).tick(); - } + /** + * Tick the timer. + * It is assumed that this is called once per frame, since this frequency is assumed when calculating FPS. + */ + void newFrame() + { + mFrameCount++; + mFrameTimes[mFrameCount % kFrameWindow] = mClock.tick().getRealTimeDelta(); + mClock.setTime(0).tick(); + } - /** Get the time in seconds it took to render a frame. - */ - double getAverageFrameTime() const - { - uint64_t frames = std::min(mFrameCount, kFrameWindow); - double time = 0; - for (uint64_t i = 0; i < frames; i++) - time += mFrameTimes[i]; - return time / double(frames); - } + /** + * Get the time in seconds it took to render a frame. + */ + double getAverageFrameTime() const + { + uint64_t frames = std::min(mFrameCount, kFrameWindow); + double time = 0; + for (uint64_t i = 0; i < frames; i++) + time += mFrameTimes[i]; + return time / double(frames); + } - /** Get the time in seconds that it took to render the last frame. - */ - double getLastFrameTime() const - { - return mFrameTimes[mFrameCount % kFrameWindow]; - } + /** + * Get the time in seconds that it took to render the last frame. + */ + double getLastFrameTime() const { return mFrameTimes[mFrameCount % kFrameWindow]; } - /** Get the frame count (= number of times newFrame() has been called). - */ - uint64_t getFrameCount() const { return mFrameCount; } + /** + * Get the frame count (= number of times newFrame() has been called). + */ + uint64_t getFrameCount() const { return mFrameCount; } - /** Get a message with the FPS. - */ - std::string getMsg(bool vsyncOn = false) const; + /** + * Get a message with the FPS. + */ + std::string getMsg(bool vsyncOn = false) const; - private: - Clock mClock; - std::vector mFrameTimes; - uint64_t mFrameCount = 0; - static constexpr uint64_t kFrameWindow = 60; - }; +private: + Clock mClock; + std::vector mFrameTimes; + uint64_t mFrameCount = 0; + static constexpr uint64_t kFrameWindow = 60; +}; - inline std::string to_string(const FrameRate& fr, bool vsyncOn = false) { return fr.getMsg(vsyncOn); } +inline std::string to_string(const FrameRate& fr, bool vsyncOn = false) +{ + return fr.getMsg(vsyncOn); } +} // namespace Falcor diff --git a/Source/Falcor/Utils/Timing/GpuTimer.slang b/Source/Falcor/Utils/Timing/GpuTimer.slang index f6c0b4357..7f9c84dff 100644 --- a/Source/Falcor/Utils/Timing/GpuTimer.slang +++ b/Source/Falcor/Utils/Timing/GpuTimer.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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,36 @@ **************************************************************************/ #include "Utils/NVAPI.slangh" // We need this for accessing the global timer. -/** GPU timer based on NVAPI. - Uses only the lower 32-bits of the global timer. This should be sufficient to do - most measurements but care needs to be taken for handling wrap around. -*/ +/** + * GPU timer based on NVAPI. + * Uses only the lower 32-bits of the global timer. This should be sufficient to do + * most measurements but care needs to be taken for handling wrap around. + */ struct GpuTimer { typedef uint TimePoint; - TimePoint startTime; ///< Time when timer was last started. + TimePoint startTime; ///< Time when timer was last started. - /** Starts the timer. - */ - [mutating] void start() - { - startTime = getCurrentTimePoint(); - } + /** + * Starts the timer. + */ + [mutating] + void start() { startTime = getCurrentTimePoint(); } - /** Returns the elapsed time since the last call to start(). - \return Returns the elapsed time. - */ + /** + * Returns the elapsed time since the last call to start(). + * @return Returns the elapsed time. + */ uint getElapsed() { TimePoint currentTime = getCurrentTimePoint(); return calcDuration(startTime, currentTime); } - /** Get the current time point. - */ + /** + * Get the current time point. + */ static TimePoint getCurrentTimePoint() { #if FALCOR_NVAPI_AVAILABLE @@ -64,13 +66,11 @@ struct GpuTimer #endif } - /** Compute the duration between two time points accounting for at most one overflow. - \param[in] start Start time point - \param[in] end End time point - \return Returns the duration. - */ - static uint calcDuration(TimePoint start, TimePoint end) - { - return end - start; - } + /** + * Compute the duration between two time points accounting for at most one overflow. + * @param[in] start Start time point + * @param[in] end End time point + * @return Returns the duration. + */ + static uint calcDuration(TimePoint start, TimePoint end) { return end - start; } }; diff --git a/Source/Falcor/Utils/Timing/Profiler.cpp b/Source/Falcor/Utils/Timing/Profiler.cpp index 4975a73f1..7d8d48994 100644 --- a/Source/Falcor/Utils/Timing/Profiler.cpp +++ b/Source/Falcor/Utils/Timing/Profiler.cpp @@ -30,428 +30,441 @@ #include "Core/API/GpuTimer.h" #include "Utils/Logger.h" #include "Utils/Scripting/ScriptBindings.h" + #include namespace Falcor { - namespace +namespace +{ +// 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 float kSigma = 0.98f; + +// Size of the event history. The event history is keeping track of event times to allow +// for computing statistics (min, max, mean, stddev) over the recent history. +const size_t kMaxHistorySize = 512; + +pybind11::dict toPython(const Profiler::Stats& stats) +{ + pybind11::dict d; + d["min"] = stats.min; + d["max"] = stats.max; + d["mean"] = stats.mean; + d["std_dev"] = stats.stdDev; + return d; +} + +pybind11::dict toPython(const Profiler::Capture& capture) +{ + pybind11::dict pyCapture; + pybind11::dict pyEvents; + + pyCapture["frame_count"] = capture.getFrameCount(); + pyCapture["events"] = pyEvents; + + for (const auto& lane : capture.getLanes()) { - // 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 float kSigma = 0.98f; - - // Size of the event history. The event history is keeping track of event times to allow - // for computing statistics (min, max, mean, stddev) over the recent history. - const size_t kMaxHistorySize = 512; + pybind11::dict pyLane; + pyLane["name"] = lane.name; + pyLane["stats"] = toPython(lane.stats); + pyLane["records"] = lane.records; + pyEvents[lane.name.c_str()] = pyLane; } - // Profiler::Stats + return pyCapture; +} + +pybind11::dict toPython(const std::vector& events) +{ + pybind11::dict result; - pybind11::dict Profiler::Stats::toPython() const + auto addLane = [&result](std::string name, float value, float average, const Profiler::Stats& stats) { pybind11::dict d; + d["name"] = name; + d["value"] = value; + d["average"] = average; + d["stats"] = toPython(stats); + result[name.c_str()] = d; + }; + + for (const Profiler::Event* pEvent : events) + { + addLane(pEvent->getName() + "/cpu_time", pEvent->getCpuTime(), pEvent->getCpuTimeAverage(), pEvent->computeCpuTimeStats()); + addLane(pEvent->getName() + "/gpu_time", pEvent->getGpuTime(), pEvent->getGpuTimeAverage(), pEvent->computeGpuTimeStats()); + } - d["min"] = min; - d["max"] = max; - d["mean"] = mean; - d["stdDev"] = stdDev; + return result; +} +} // namespace - return d; - } +// Profiler::Stats + +Profiler::Stats Profiler::Stats::compute(const float* data, size_t len) +{ + if (len == 0) + return {}; + + float min = std::numeric_limits::max(); + float max = std::numeric_limits::lowest(); + double sum = 0.0; + double sum2 = 0.0; - Profiler::Stats Profiler::Stats::compute(const float* data, size_t len) + for (size_t i = 0; i < len; ++i) { - if (len == 0) return {}; + float value = data[i]; + min = std::min(min, value); + max = std::max(max, value); + sum += value; + sum2 += value * value; + } - float min = std::numeric_limits::max(); - float max = std::numeric_limits::lowest(); - double sum = 0.0; - double sum2 = 0.0; + double mean = sum / len; + double mean2 = sum2 / len; + double variance = mean2 - mean * mean; + double stdDev = std::sqrt(variance); - for (size_t i = 0; i < len; ++i) - { - float value = data[i]; - min = std::min(min, value); - max = std::max(max, value); - sum += value; - sum2 += value * value; - } + return {min, max, (float)mean, (float)stdDev}; +} - double mean = sum / len; - double mean2 = sum2 / len; - double variance = mean2 - mean * mean; - double stdDev = std::sqrt(variance); +// Profiler::Event - return { min, max, (float)mean, (float)stdDev }; - } +Profiler::Event::Event(const std::string& name) : mName(name), mCpuTimeHistory(kMaxHistorySize, 0.f), mGpuTimeHistory(kMaxHistorySize, 0.f) +{} - // Profiler::Event +Profiler::Stats Profiler::Event::computeCpuTimeStats() const +{ + return Stats::compute(mCpuTimeHistory.data(), mHistorySize); +} - Profiler::Event::Event(const std::string& name) - : mName(name) - , mCpuTimeHistory(kMaxHistorySize, 0.f) - , mGpuTimeHistory(kMaxHistorySize, 0.f) - {} +Profiler::Stats Profiler::Event::computeGpuTimeStats() const +{ + return Stats::compute(mGpuTimeHistory.data(), mHistorySize); +} - Profiler::Stats Profiler::Event::computeCpuTimeStats() const +void Profiler::Event::start(Profiler& profiler, uint32_t frameIndex) +{ + if (++mTriggered > 1) { - return Stats::compute(mCpuTimeHistory.data(), mHistorySize); + logWarning( + "Profiler event '{}' 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.", + mName + ); + return; } - Profiler::Stats Profiler::Event::computeGpuTimeStats() const + auto& frameData = mFrameData[frameIndex % 2]; + + // Update CPU time. + frameData.cpuStartTime = CpuTimer::getCurrentTimePoint(); + + // Update GPU time. + FALCOR_ASSERT(frameData.pActiveTimer == nullptr); + FALCOR_ASSERT(frameData.currentTimer <= frameData.pTimers.size()); + if (frameData.currentTimer == frameData.pTimers.size()) { - return Stats::compute(mGpuTimeHistory.data(), mHistorySize); + ref timer = GpuTimer::create(profiler.mpDevice); + timer->breakStrongReferenceToDevice(); + frameData.pTimers.push_back(timer); } + frameData.pActiveTimer = frameData.pTimers[frameData.currentTimer++].get(); + frameData.pActiveTimer->begin(); + frameData.valid = false; +} - void Profiler::Event::start(Profiler& profiler, uint32_t frameIndex) - { - if (++mTriggered > 1) - { - logWarning("Profiler event '{}' 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.", mName); - return; - } +void Profiler::Event::end(uint32_t frameIndex) +{ + if (--mTriggered != 0) + return; - auto& frameData = mFrameData[frameIndex % 2]; + auto& frameData = mFrameData[frameIndex % 2]; - // Update CPU time. - frameData.cpuStartTime = CpuTimer::getCurrentTimePoint(); + // Update CPU time. + frameData.cpuTotalTime += (float)CpuTimer::calcDuration(frameData.cpuStartTime, CpuTimer::getCurrentTimePoint()); - // Update GPU time. - FALCOR_ASSERT(frameData.pActiveTimer == nullptr); - FALCOR_ASSERT(frameData.currentTimer <= frameData.pTimers.size()); - if (frameData.currentTimer == frameData.pTimers.size()) + // Update GPU time. + FALCOR_ASSERT(frameData.pActiveTimer != nullptr); + frameData.pActiveTimer->end(); + frameData.pActiveTimer = nullptr; + frameData.valid = true; +} + +void Profiler::Event::endFrame(uint32_t frameIndex) +{ + // Resolve GPU timers for the current frame measurements. + // This is necessary before we readback of results next frame. + { + auto& frameData = mFrameData[frameIndex % 2]; + for (auto& pTimer : frameData.pTimers) { - frameData.pTimers.push_back(GpuTimer::create(profiler.mpDevice)); + pTimer->resolve(); } - frameData.pActiveTimer = frameData.pTimers[frameData.currentTimer++].get(); - frameData.pActiveTimer->begin(); - frameData.valid = false; } - void Profiler::Event::end(uint32_t frameIndex) - { - if (--mTriggered != 0) return; + // Update CPU/GPU time from last frame measurement. + auto& frameData = mFrameData[(frameIndex + 1) % 2]; - auto& frameData = mFrameData[frameIndex % 2]; + // Skip update if there are no measurements last frame. + if (!frameData.valid) + return; - // Update CPU time. - frameData.cpuTotalTime += (float)CpuTimer::calcDuration(frameData.cpuStartTime, CpuTimer::getCurrentTimePoint()); + mCpuTime = frameData.cpuTotalTime; + mGpuTime = 0.f; + for (size_t i = 0; i < frameData.currentTimer; ++i) + mGpuTime += (float)frameData.pTimers[i]->getElapsedTime(); + frameData.cpuTotalTime = 0.f; + frameData.currentTimer = 0; - // Update GPU time. - FALCOR_ASSERT(frameData.pActiveTimer != nullptr); - frameData.pActiveTimer->end(); - frameData.pActiveTimer = nullptr; - frameData.valid = true; - } + // Update EMA. + mCpuTimeAverage = mCpuTimeAverage < 0.f ? mCpuTime : (kSigma * mCpuTimeAverage + (1.f - kSigma) * mCpuTime); + mGpuTimeAverage = mGpuTimeAverage < 0.f ? mGpuTime : (kSigma * mGpuTimeAverage + (1.f - kSigma) * mGpuTime); - void Profiler::Event::endFrame(uint32_t frameIndex) - { - // Resolve GPU timers for the current frame measurements. - // This is necessary before we readback of results next frame. - { - auto& frameData = mFrameData[frameIndex % 2]; - for (auto& pTimer : frameData.pTimers) - { - pTimer->resolve(); - } - } + // Update history. + mCpuTimeHistory[mHistoryWriteIndex] = mCpuTime; + mGpuTimeHistory[mHistoryWriteIndex] = mGpuTime; + mHistoryWriteIndex = (mHistoryWriteIndex + 1) % kMaxHistorySize; + mHistorySize = std::min(mHistorySize + 1, kMaxHistorySize); - // Update CPU/GPU time from last frame measurement. - auto& frameData = mFrameData[(frameIndex + 1) % 2]; + mTriggered = 0; +} - // Skip update if there are no measurements last frame. - if (!frameData.valid) return; +// Profiler::Capture - mCpuTime = frameData.cpuTotalTime; - mGpuTime = 0.f; - for (size_t i = 0; i < frameData.currentTimer; ++i) mGpuTime += (float)frameData.pTimers[i]->getElapsedTime(); - frameData.cpuTotalTime = 0.f; - frameData.currentTimer = 0; +std::string Profiler::Capture::toJsonString() const +{ + using namespace pybind11::literals; - // Update EMA. - mCpuTimeAverage = mCpuTimeAverage < 0.f ? mCpuTime : (kSigma * mCpuTimeAverage + (1.f - kSigma) * mCpuTime); - mGpuTimeAverage = mGpuTimeAverage < 0.f ? mGpuTime : (kSigma * mGpuTimeAverage + (1.f - kSigma) * mGpuTime); + // We use pythons JSON encoder to encode the python dictionary to a JSON string. + pybind11::module json = pybind11::module::import("json"); + pybind11::object dumps = json.attr("dumps"); + return pybind11::cast(dumps(toPython(*this), "indent"_a = 2)); +} - // Update history. - mCpuTimeHistory[mHistoryWriteIndex] = mCpuTime; - mGpuTimeHistory[mHistoryWriteIndex] = mGpuTime; - mHistoryWriteIndex = (mHistoryWriteIndex + 1) % kMaxHistorySize; - mHistorySize = std::min(mHistorySize + 1, kMaxHistorySize); +void Profiler::Capture::writeToFile(const std::filesystem::path& path) const +{ + auto json = toJsonString(); + std::ofstream ofs(path); + ofs.write(json.data(), json.size()); +} - mTriggered = 0; - } +Profiler::Capture::Capture(size_t reservedEvents, size_t reservedFrames) : mReservedFrames(reservedFrames) +{ + // Speculativly allocate event record storage. + mLanes.resize(reservedEvents * 2); + for (auto& lane : mLanes) + lane.records.reserve(reservedFrames); +} - // Profiler::Capture +void Profiler::Capture::captureEvents(const std::vector& events) +{ + if (events.empty()) + return; - pybind11::dict Profiler::Capture::toPython() const + // Initialize on first capture. + if (mEvents.empty()) { - pybind11::dict pyCapture; - pybind11::dict pyEvents; - - pyCapture["frameCount"] = mFrameCount; - pyCapture["events"] = pyEvents; - - for (const auto& lane : mLanes) + mEvents = events; + mLanes.resize(mEvents.size() * 2); + for (size_t i = 0; i < mEvents.size(); ++i) { - pybind11::dict pyLane; - pyLane["name"] = lane.name; - pyLane["stats"] = lane.stats.toPython(); - pyLane["records"] = lane.records; - pyEvents[lane.name.c_str()] = pyLane; + auto& pEvent = mEvents[i]; + mLanes[i * 2].name = pEvent->getName() + "/cpu_time"; + mLanes[i * 2].records.reserve(mReservedFrames); + mLanes[i * 2 + 1].name = pEvent->getName() + "/gpu_time"; + mLanes[i * 2 + 1].records.reserve(mReservedFrames); } - - return pyCapture; + return; // Exit as no data is available on first capture. } - std::string Profiler::Capture::toJsonString() const + // Record CPU/GPU timing on subsequent captures. + for (size_t i = 0; i < mEvents.size(); ++i) { - using namespace pybind11::literals; - - // We use pythons JSON encoder to encode the python dictionary to a JSON string. - pybind11::module json = pybind11::module::import("json"); - pybind11::object dumps = json.attr("dumps"); - return pybind11::cast(dumps(toPython(), "indent"_a = 2)); + auto& pEvent = mEvents[i]; + mLanes[i * 2].records.push_back(pEvent->getCpuTime()); + mLanes[i * 2 + 1].records.push_back(pEvent->getGpuTime()); } - void Profiler::Capture::writeToFile(const std::filesystem::path& path) const - { - auto json = toJsonString(); - std::ofstream ofs(path); - ofs.write(json.data(), json.size()); - } + ++mFrameCount; +} - Profiler::Capture::Capture(size_t reservedEvents, size_t reservedFrames) - : mReservedFrames(reservedFrames) - { - // Speculativly allocate event record storage. - mLanes.resize(reservedEvents * 2); - for (auto& lane : mLanes) lane.records.reserve(reservedFrames); - } +void Profiler::Capture::finalize() +{ + FALCOR_ASSERT(!mFinalized); - Profiler::Capture::SharedPtr Profiler::Capture::create(size_t reservedEvents, size_t reservedFrames) + for (auto& lane : mLanes) { - return SharedPtr(new Capture(reservedEvents, reservedFrames)); + lane.stats = Stats::compute(lane.records.data(), lane.records.size()); } - void Profiler::Capture::captureEvents(const std::vector& events) - { - if (events.empty()) return; - - // Initialize on first capture. - if (mEvents.empty()) - { - mEvents = events; - mLanes.resize(mEvents.size() * 2); - for (size_t i = 0; i < mEvents.size(); ++i) - { - auto& pEvent = mEvents[i]; - mLanes[i * 2].name = pEvent->getName() + "/cpuTime"; - mLanes[i * 2].records.reserve(mReservedFrames); - mLanes[i * 2 + 1].name = pEvent->getName() + "/gpuTime"; - mLanes[i * 2 + 1].records.reserve(mReservedFrames); - } - return; // Exit as no data is available on first capture. - } + mFinalized = true; +} - // Record CPU/GPU timing on subsequent captures. - for (size_t i = 0; i < mEvents.size(); ++i) - { - auto& pEvent = mEvents[i]; - mLanes[i * 2].records.push_back(pEvent->getCpuTime()); - mLanes[i * 2 + 1].records.push_back(pEvent->getGpuTime()); - } +// Profiler - ++mFrameCount; - } +Profiler::Profiler(ref pDevice) : mpDevice(pDevice) +{ + mpFence = GpuFence::create(mpDevice); + mpFence->breakStrongReferenceToDevice(); +} - void Profiler::Capture::finalize() +void Profiler::startEvent(RenderContext* pRenderContext, const std::string& name, Flags flags) +{ + if (mEnabled && is_set(flags, Flags::Internal)) { - FALCOR_ASSERT(!mFinalized); - - for (auto& lane : mLanes) + // '/' is used as a "path delimiter", so it cannot be used in the event name. + if (name.find('/') != std::string::npos) { - lane.stats = Stats::compute(lane.records.data(), lane.records.size()); + logWarning("Profiler event names must not contain '/'. Ignoring this profiler event."); + return; } - mFinalized = true; - } - - // Profiler + mCurrentEventName = mCurrentEventName + "/" + name; - Profiler::Profiler(Device* pDevice) - : mpDevice(pDevice) - { - mpFence = GpuFence::create(mpDevice); - } + Event* pEvent = getEvent(mCurrentEventName); + FALCOR_ASSERT(pEvent != nullptr); + if (!mPaused) + pEvent->start(*this, mFrameIndex); - void Profiler::startEvent(RenderContext* pRenderContext, const std::string& name, Flags flags) - { - if (mEnabled && is_set(flags, Flags::Internal)) - { - // '/' is used as a "path delimiter", so it cannot be used in the event name. - if (name.find('/') != std::string::npos) - { - logWarning("Profiler event names must not contain '/'. Ignoring this profiler event."); - return; - } - - mCurrentEventName = mCurrentEventName + "/" + name; - - Event* pEvent = getEvent(mCurrentEventName); - FALCOR_ASSERT(pEvent != nullptr); - if (!mPaused) pEvent->start(*this, mFrameIndex); - - if (std::find(mCurrentFrameEvents.begin(), mCurrentFrameEvents.end(), pEvent) == mCurrentFrameEvents.end()) - { - mCurrentFrameEvents.push_back(pEvent); - } - } - if (is_set(flags, Flags::Pix)) + if (std::find(mCurrentFrameEvents.begin(), mCurrentFrameEvents.end(), pEvent) == mCurrentFrameEvents.end()) { - FALCOR_ASSERT(pRenderContext); - pRenderContext->getLowLevelData()->beginDebugEvent(name.c_str()); + mCurrentFrameEvents.push_back(pEvent); } } - - void Profiler::endEvent(RenderContext* pRenderContext, const std::string& name, Flags flags) + if (is_set(flags, Flags::Pix)) { - if (mEnabled && is_set(flags, Flags::Internal)) - { - // '/' is used as a "path delimiter", so it cannot be used in the event name. - if (name.find('/') != std::string::npos) return; + FALCOR_ASSERT(pRenderContext); + pRenderContext->getLowLevelData()->beginDebugEvent(name.c_str()); + } +} - Event* pEvent = getEvent(mCurrentEventName); - FALCOR_ASSERT(pEvent != nullptr); - if (!mPaused) pEvent->end(mFrameIndex); +void Profiler::endEvent(RenderContext* pRenderContext, const std::string& name, Flags flags) +{ + if (mEnabled && is_set(flags, Flags::Internal)) + { + // '/' is used as a "path delimiter", so it cannot be used in the event name. + if (name.find('/') != std::string::npos) + return; - mCurrentEventName.erase(mCurrentEventName.find_last_of("/")); - } + Event* pEvent = getEvent(mCurrentEventName); + FALCOR_ASSERT(pEvent != nullptr); + if (!mPaused) + pEvent->end(mFrameIndex); - if (is_set(flags, Flags::Pix)) - { - FALCOR_ASSERT(pRenderContext) - pRenderContext->getLowLevelData()->endDebugEvent(); - } + mCurrentEventName.erase(mCurrentEventName.find_last_of("/")); } - Profiler::Event* Profiler::getEvent(const std::string& name) + if (is_set(flags, Flags::Pix)) { - auto event = findEvent(name); - return event ? event : createEvent(name); + FALCOR_ASSERT(pRenderContext) + pRenderContext->getLowLevelData()->endDebugEvent(); } +} - void Profiler::endFrame(RenderContext* pRenderContext) - { - if (mPaused) return; - - // Wait for GPU timings to be available from last frame. - // We use a single fence here instead of one per event, which gets too inefficient. - // TODO: This code should refactored to batch the resolve and readback of timestamps. - if (mFenceValue != uint64_t(-1)) mpFence->syncCpu(); - - for (Event* pEvent : mCurrentFrameEvents) - { - pEvent->endFrame(mFrameIndex); - } - - // Flush and insert signal for synchronization of GPU timings. - pRenderContext->flush(false); - mFenceValue = mpFence->gpuSignal(pRenderContext->getLowLevelData()->getCommandQueue()); +Profiler::Event* Profiler::getEvent(const std::string& name) +{ + auto event = findEvent(name); + return event ? event : createEvent(name); +} - if (mpCapture) mpCapture->captureEvents(mCurrentFrameEvents); +void Profiler::endFrame(RenderContext* pRenderContext) +{ + if (mPaused) + return; - mLastFrameEvents = std::move(mCurrentFrameEvents); - ++mFrameIndex; - } + // Wait for GPU timings to be available from last frame. + // We use a single fence here instead of one per event, which gets too inefficient. + // TODO: This code should refactored to batch the resolve and readback of timestamps. + if (mFenceValue != uint64_t(-1)) + mpFence->syncCpu(); - void Profiler::startCapture(size_t reservedFrames) + for (Event* pEvent : mCurrentFrameEvents) { - setEnabled(true); - mpCapture = Capture::create(mLastFrameEvents.size(), reservedFrames); + pEvent->endFrame(mFrameIndex); } - Profiler::Capture::SharedPtr Profiler::endCapture() - { - Capture::SharedPtr pCapture; - std::swap(pCapture, mpCapture); - if (pCapture) pCapture->finalize(); - return pCapture; - } + // Flush and insert signal for synchronization of GPU timings. + pRenderContext->flush(false); + mFenceValue = mpFence->gpuSignal(pRenderContext->getLowLevelData()->getCommandQueue()); - bool Profiler::isCapturing() const - { - return mpCapture != nullptr; - } + if (mpCapture) + mpCapture->captureEvents(mCurrentFrameEvents); - pybind11::dict Profiler::getPythonEvents() const - { - pybind11::dict result; + mLastFrameEvents = std::move(mCurrentFrameEvents); + ++mFrameIndex; +} - auto addLane = [&result] (std::string name, float value, float average, const Stats& stats) - { - pybind11::dict d; - d["name"] = name; - d["value"] = value; - d["average"] = average; - d["stats"] = stats.toPython(); - result[name.c_str()] = d; - }; - - for (const Profiler::Event* pEvent : getEvents()) - { - addLane(pEvent->getName() + "/cpuTime", pEvent->getCpuTime(), pEvent->getCpuTimeAverage(), pEvent->computeCpuTimeStats()); - addLane(pEvent->getName() + "/gpuTime", pEvent->getGpuTime(), pEvent->getGpuTimeAverage(), pEvent->computeGpuTimeStats()); - } +void Profiler::startCapture(size_t reservedFrames) +{ + setEnabled(true); + mpCapture = std::make_shared(mLastFrameEvents.size(), reservedFrames); +} - return result; - } +std::shared_ptr Profiler::endCapture() +{ + std::shared_ptr pCapture; + std::swap(pCapture, mpCapture); + if (pCapture) + pCapture->finalize(); + return pCapture; +} - Profiler::Event* Profiler::createEvent(const std::string& name) - { - auto pEvent = std::shared_ptr(new Event(name)); - mEvents.emplace(name, pEvent); - return pEvent.get(); - } +bool Profiler::isCapturing() const +{ + return mpCapture != nullptr; +} - Profiler::Event* Profiler::findEvent(const std::string& name) - { - auto event = mEvents.find(name); - return (event == mEvents.end()) ? nullptr : event->second.get(); - } +Profiler::Event* Profiler::createEvent(const std::string& name) +{ + auto pEvent = std::shared_ptr(new Event(name)); + mEvents.emplace(name, pEvent); + return pEvent.get(); +} +Profiler::Event* Profiler::findEvent(const std::string& name) +{ + auto event = mEvents.find(name); + return (event == mEvents.end()) ? nullptr : event->second.get(); +} - ScopedProfilerEvent::ScopedProfilerEvent(RenderContext* pRenderContext, const std::string& name, Profiler::Flags flags) - : mpRenderContext(pRenderContext) - , mName(name) - , mFlags(flags) - { - FALCOR_ASSERT(mpRenderContext); - mpRenderContext->getProfiler()->startEvent(mpRenderContext, mName, mFlags); - } +void Profiler::breakStrongReferenceToDevice() +{ + mpDevice.breakStrongReference(); +} - ScopedProfilerEvent::~ScopedProfilerEvent() - { - mpRenderContext->getProfiler()->endEvent(mpRenderContext, mName, mFlags); - } +ScopedProfilerEvent::ScopedProfilerEvent(RenderContext* pRenderContext, const std::string& name, Profiler::Flags flags) + : mpRenderContext(pRenderContext), mName(name), mFlags(flags) +{ + FALCOR_ASSERT(mpRenderContext); + mpRenderContext->getProfiler()->startEvent(mpRenderContext, mName, mFlags); +} + +ScopedProfilerEvent::~ScopedProfilerEvent() +{ + mpRenderContext->getProfiler()->endEvent(mpRenderContext, mName, mFlags); +} - FALCOR_SCRIPT_BINDING(Profiler) +FALCOR_SCRIPT_BINDING(Profiler) +{ + using namespace pybind11::literals; + + auto endCapture = [](Profiler* pProfiler) { - using namespace pybind11::literals; - - auto endCapture = [] (Profiler* pProfiler) { - std::optional result; - auto pCapture = pProfiler->endCapture(); - if (pCapture) result = pCapture->toPython(); - return result; - }; - - pybind11::class_ profiler(m, "Profiler"); - profiler.def_property("enabled", &Profiler::isEnabled, &Profiler::setEnabled); - profiler.def_property("paused", &Profiler::isPaused, &Profiler::setPaused); - profiler.def_property_readonly("isCapturing", &Profiler::isCapturing); - profiler.def_property_readonly("events", &Profiler::getPythonEvents); - profiler.def("startCapture", &Profiler::startCapture, "reservedFrames"_a = 1000); - profiler.def("endCapture", endCapture); - } + std::optional result; + auto pCapture = pProfiler->endCapture(); + if (pCapture) + result = toPython(*pCapture); + return result; + }; + + pybind11::class_ profiler(m, "Profiler"); + profiler.def_property("enabled", &Profiler::isEnabled, &Profiler::setEnabled); + profiler.def_property("paused", &Profiler::isPaused, &Profiler::setPaused); + profiler.def_property_readonly("is_capturing", &Profiler::isCapturing); + profiler.def_property_readonly("events", [](const Profiler& profiler) { return toPython(profiler.getEvents()); }); + profiler.def("start_capture", &Profiler::startCapture, "reserved_frames"_a = 1000); + profiler.def("end_capture", endCapture); } +} // namespace Falcor diff --git a/Source/Falcor/Utils/Timing/Profiler.h b/Source/Falcor/Utils/Timing/Profiler.h index 6952591b9..b9411be76 100644 --- a/Source/Falcor/Utils/Timing/Profiler.h +++ b/Source/Falcor/Utils/Timing/Profiler.h @@ -29,7 +29,6 @@ #include "CpuTimer.h" #include "Core/Macros.h" #include "Core/API/GpuTimer.h" -#include #include #include #include @@ -38,253 +37,264 @@ namespace Falcor { - class RenderContext; - - /** Container class for CPU/GPU profiling. - This class uses the most accurately available CPU and GPU timers to profile given events. - It automatically creates event hierarchies based on the order and nesting of the calls made. - 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 FALCOR_API Profiler +class RenderContext; + +/** + * Container class for CPU/GPU profiling. + * This class uses the most accurately available CPU and GPU timers to profile given events. + * It automatically creates event hierarchies based on the order and nesting of the calls made. + * 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 FALCOR_API Profiler +{ +public: + enum class Flags { - public: - enum class Flags - { - None = 0x0, - Internal = 0x1, - Pix = 0x2, - - Default = Internal | Pix - }; + None = 0x0, + Internal = 0x1, + Pix = 0x2, - struct Stats - { - float min; - float max; - float mean; - float stdDev; - - pybind11::dict toPython() const; - - static Stats compute(const float* data, size_t len); - }; - - class Event - { - public: - const std::string getName() const { return mName; } - - float getCpuTime() const { return mCpuTime; } - float getGpuTime() const { return mGpuTime; } + Default = Internal | Pix + }; - float getCpuTimeAverage() const { return mCpuTimeAverage; } - float getGpuTimeAverage() const { return mGpuTimeAverage; } + struct Stats + { + float min; + float max; + float mean; + float stdDev; - Stats computeCpuTimeStats() const; - Stats computeGpuTimeStats() const; + static Stats compute(const float* data, size_t len); + }; - private: - Event(const std::string& name); + class Event + { + public: + const std::string getName() const { return mName; } - void start(Profiler& profiler, uint32_t frameIndex); - void end(uint32_t frameIndex); - void endFrame(uint32_t frameIndex); + float getCpuTime() const { return mCpuTime; } + float getGpuTime() const { return mGpuTime; } - std::string mName; ///< Nested event name. + float getCpuTimeAverage() const { return mCpuTimeAverage; } + float getGpuTimeAverage() const { return mGpuTimeAverage; } - float mCpuTime = 0.0; ///< CPU time (previous frame). - float mGpuTime = 0.0; ///< GPU time (previous frame). + Stats computeCpuTimeStats() const; + Stats computeGpuTimeStats() const; - float mCpuTimeAverage = -1.f; ///< Average CPU time (negative value to signify invalid). - float mGpuTimeAverage = -1.f; ///< Average GPU time (negative value to signify invalid). + private: + Event(const std::string& name); - std::vector mCpuTimeHistory; ///< CPU time history (round-robin, used for computing stats). - std::vector mGpuTimeHistory; ///< GPU time history (round-robin, used for computing stats). - size_t mHistoryWriteIndex = 0; ///< History write index. - size_t mHistorySize = 0; ///< History size. + void start(Profiler& profiler, uint32_t frameIndex); + void end(uint32_t frameIndex); + void endFrame(uint32_t frameIndex); - uint32_t mTriggered = 0; ///< Keeping track of nested calls to start(). + std::string mName; ///< Nested event name. - struct FrameData - { - CpuTimer::TimePoint cpuStartTime; ///< Last event CPU start time. - float cpuTotalTime = 0.0; ///< Total accumulated CPU time. + float mCpuTime = 0.0; ///< CPU time (previous frame). + float mGpuTime = 0.0; ///< GPU time (previous frame). - std::vector pTimers; ///< Pool of GPU timers. - size_t currentTimer = 0; ///< Next GPU timer to use from the pool. - GpuTimer *pActiveTimer = nullptr; ///< Currently active GPU timer. + float mCpuTimeAverage = -1.f; ///< Average CPU time (negative value to signify invalid). + float mGpuTimeAverage = -1.f; ///< Average GPU time (negative value to signify invalid). - bool valid = false; ///< True when frame data is valid (after begin/end cycle). - }; - FrameData mFrameData[2]; ///< Double-buffered frame data to avoid GPU flushes. + std::vector mCpuTimeHistory; ///< CPU time history (round-robin, used for computing stats). + std::vector mGpuTimeHistory; ///< GPU time history (round-robin, used for computing stats). + size_t mHistoryWriteIndex = 0; ///< History write index. + size_t mHistorySize = 0; ///< History size. - friend class Profiler; - }; + uint32_t mTriggered = 0; ///< Keeping track of nested calls to start(). - class Capture + struct FrameData { - public: - using SharedPtr = std::shared_ptr; + CpuTimer::TimePoint cpuStartTime; ///< Last event CPU start time. + float cpuTotalTime = 0.0; ///< Total accumulated CPU time. - struct Lane - { - std::string name; - Stats stats; - std::vector records; - }; + std::vector> pTimers; ///< Pool of GPU timers. + size_t currentTimer = 0; ///< Next GPU timer to use from the pool. + GpuTimer* pActiveTimer = nullptr; ///< Currently active GPU timer. - size_t getFrameCount() const { return mFrameCount; } - const std::vector& getLanes() const { return mLanes; } + bool valid = false; ///< True when frame data is valid (after begin/end cycle). + }; + FrameData mFrameData[2]; ///< Double-buffered frame data to avoid GPU flushes. - pybind11::dict toPython() const; + friend class Profiler; + }; - std::string toJsonString() const; - void writeToFile(const std::filesystem::path& path) const; + class Capture + { + public: + struct Lane + { + std::string name; + Stats stats; + std::vector records; + }; - private: - Capture(size_t reservedEvents, size_t reservedFrames); + Capture(size_t reservedEvents, size_t reservedFrames); - static SharedPtr create(size_t reservedEvents, size_t reservedFrames); - void captureEvents(const std::vector& events); - void finalize(); + size_t getFrameCount() const { return mFrameCount; } + const std::vector& getLanes() const { return mLanes; } - size_t mReservedFrames = 0; - size_t mFrameCount = 0; - std::vector mEvents; - std::vector mLanes; - bool mFinalized = false; + std::string toJsonString() const; + void writeToFile(const std::filesystem::path& path) const; - friend class Profiler; - }; + private: + void captureEvents(const std::vector& events); + void finalize(); - /** Constructor. - */ - Profiler(Device* pDevice); - - /** Check if the profiler is enabled. - \return Returns true if the profiler is enabled. - */ - bool isEnabled() const { return mEnabled; } - - /** Enable/disable the profiler. - \param[in] enabled True to enable the profiler. - */ - void setEnabled(bool enabled) { mEnabled = enabled; } - - /** Check if the profiler is paused. - \return Returns true if the profiler is paused. - */ - bool isPaused() const { return mPaused; } - - /** Pause/resume the profiler. - \param[in] paused True to pause the profiler. - */ - void setPaused(bool paused) { mPaused = paused; } - - /** Start profile capture. - \param[in] reservedFrames Number of frames to reserve memory for. - */ - void startCapture(size_t reservedFrames = 1024); - - /** End profile capture. - \return Returns the captured data. - */ - Capture::SharedPtr endCapture(); - - /** Check if the profiler is capturing. - \return Return true if the profiler is capturing. - */ - bool isCapturing() const; - - /** Finish profiling for the entire frame. - Note: Must be called once at the end of each frame. - */ - void endFrame(RenderContext* pRenderContext); - - /** Start profiling a new event and update the events hierarchies. - \param[in] pRenderContext Render context for measuring GPU time. - \param[in] name The event name. - \param[in] flags The event flags. - */ - void startEvent(RenderContext* pRenderContext, const std::string& name, Flags flags = Flags::Default); - - /** Finish profiling a new event and update the events hierarchies. - \param[in] pRenderContext Render context for measuring GPU time. - \param[in] name The event name. - \param[in] flags The event flags. - */ - void endEvent(RenderContext* pRenderContext, const std::string& name, Flags flags = Flags::Default); - - /** Get the event, or create a new one if the event does not yet exist. - This is a public interface to facilitate more complicated construction of event names and finegrained control over the profiled region. - \param[in] name The event name. - \return Returns a pointer to the event. - */ - Event* getEvent(const std::string& name); - - /** Get the profiler events (previous frame). - */ - const std::vector& getEvents() const { return mLastFrameEvents; } - - /** Get the profiler events (previous frame) as a python dictionary. - */ - pybind11::dict getPythonEvents() const; + size_t mReservedFrames = 0; + size_t mFrameCount = 0; + std::vector mEvents; + std::vector mLanes; + bool mFinalized = false; - private: - /** Create a new event. - \param[in] name The event name. - \return Returns the new event. - */ - Event* createEvent(const std::string& name); - - /** Find an event that was previously created. - \param[in] name The event name. - \return Returns the event or nullptr if none was found. - */ - Event* findEvent(const std::string& name); - - Device* mpDevice; - - bool mEnabled = false; - bool mPaused = false; - - std::unordered_map> mEvents; ///< Events by name. - std::vector mCurrentFrameEvents; ///< Events registered for current frame. - std::vector mLastFrameEvents; ///< Events from last frame. - std::string mCurrentEventName; ///< Current nested event name. - uint32_t mCurrentLevel = 0; ///< Current nesting level. - uint32_t mFrameIndex = 0; ///< Current frame index. - - Capture::SharedPtr mpCapture; ///< Currently active capture. - - GpuFence::SharedPtr mpFence; - uint64_t mFenceValue = uint64_t(-1); + friend class Profiler; }; - FALCOR_ENUM_CLASS_OPERATORS(Profiler::Flags); - - /** Helper class for starting and ending profiling events using RAII. - The constructor and destructor call Profiler::StartEvent() and Profiler::EndEvent(). - The FALCOR_PROFILE macro wraps creation of local ProfilerEvent objects when profiling is enabled, - and does nothing when profiling is disabled, so should be used instead of directly creating ProfilerEvent objects. - */ - class FALCOR_API ScopedProfilerEvent - { - public: - ScopedProfilerEvent(RenderContext* pRenderContext, const std::string& name, Profiler::Flags flags = Profiler::Flags::Default); - ~ScopedProfilerEvent(); + /** + * Constructor. + */ + Profiler(ref pDevice); + + /** + * Check if the profiler is enabled. + * @return Returns true if the profiler is enabled. + */ + bool isEnabled() const { return mEnabled; } + + /** + * Enable/disable the profiler. + * @param[in] enabled True to enable the profiler. + */ + void setEnabled(bool enabled) { mEnabled = enabled; } + + /** + * Check if the profiler is paused. + * @return Returns true if the profiler is paused. + */ + bool isPaused() const { return mPaused; } + + /** + * Pause/resume the profiler. + * @param[in] paused True to pause the profiler. + */ + void setPaused(bool paused) { mPaused = paused; } + + /** + * Start profile capture. + * @param[in] reservedFrames Number of frames to reserve memory for. + */ + void startCapture(size_t reservedFrames = 1024); + + /** + * End profile capture. + * @return Returns the captured data. + */ + std::shared_ptr endCapture(); + + /** + * Check if the profiler is capturing. + * @return Return true if the profiler is capturing. + */ + bool isCapturing() const; + + /** + * Finish profiling for the entire frame. + * Note: Must be called once at the end of each frame. + */ + void endFrame(RenderContext* pRenderContext); + + /** + * Start profiling a new event and update the events hierarchies. + * @param[in] pRenderContext Render context for measuring GPU time. + * @param[in] name The event name. + * @param[in] flags The event flags. + */ + void startEvent(RenderContext* pRenderContext, const std::string& name, Flags flags = Flags::Default); + + /** + * Finish profiling a new event and update the events hierarchies. + * @param[in] pRenderContext Render context for measuring GPU time. + * @param[in] name The event name. + * @param[in] flags The event flags. + */ + void endEvent(RenderContext* pRenderContext, const std::string& name, Flags flags = Flags::Default); + + /** + * Get the event, or create a new one if the event does not yet exist. + * This is a public interface to facilitate more complicated construction of event names and finegrained control over the profiled + * region. + * @param[in] name The event name. + * @return Returns a pointer to the event. + */ + Event* getEvent(const std::string& name); + + /** + * Get the profiler events (previous frame). + */ + const std::vector& getEvents() const { return mLastFrameEvents; } + + void breakStrongReferenceToDevice(); + +private: + /** + * Create a new event. + * @param[in] name The event name. + * @return Returns the new event. + */ + Event* createEvent(const std::string& name); + + /** + * Find an event that was previously created. + * @param[in] name The event name. + * @return Returns the event or nullptr if none was found. + */ + Event* findEvent(const std::string& name); + + BreakableReference mpDevice; + + bool mEnabled = false; + bool mPaused = false; + + std::unordered_map> mEvents; ///< Events by name. + std::vector mCurrentFrameEvents; ///< Events registered for current frame. + std::vector mLastFrameEvents; ///< Events from last frame. + std::string mCurrentEventName; ///< Current nested event name. + uint32_t mCurrentLevel = 0; ///< Current nesting level. + uint32_t mFrameIndex = 0; ///< Current frame index. + + std::shared_ptr mpCapture; ///< Currently active capture. + + ref mpFence; + uint64_t mFenceValue = uint64_t(-1); +}; + +FALCOR_ENUM_CLASS_OPERATORS(Profiler::Flags); + +/** + * Helper class for starting and ending profiling events using RAII. + * The constructor and destructor call Profiler::StartEvent() and Profiler::EndEvent(). + * The FALCOR_PROFILE macro wraps creation of local ProfilerEvent objects when profiling is enabled, + * and does nothing when profiling is disabled, so should be used instead of directly creating ProfilerEvent objects. + */ +class FALCOR_API ScopedProfilerEvent +{ +public: + ScopedProfilerEvent(RenderContext* pRenderContext, const std::string& name, Profiler::Flags flags = Profiler::Flags::Default); + ~ScopedProfilerEvent(); - private: - RenderContext* mpRenderContext; - const std::string mName; - Profiler::Flags mFlags; - }; -} +private: + RenderContext* mpRenderContext; + const std::string mName; + Profiler::Flags mFlags; +}; +} // namespace Falcor #if FALCOR_ENABLE_PROFILER -#define FALCOR_PROFILE(_pRenderContext, _name) Falcor::ScopedProfilerEvent _profileEvent##__LINE__(_pRenderContext, _name) -#define FALCOR_PROFILE_CUSTOM(_pRenderContext, _name, _flags) Falcor::ScopedProfilerEvent _profileEvent##__LINE__(_pRenderContext, _name, _flags) +#define FALCOR_PROFILE(_pRenderContext, _name) \ + Falcor::ScopedProfilerEvent FALCOR_CONCAT_STRINGS(_profileEvent, __LINE__)(_pRenderContext, _name) +#define FALCOR_PROFILE_CUSTOM(_pRenderContext, _name, _flags) \ + Falcor::ScopedProfilerEvent FALCOR_CONCAT_STRINGS(_profileEvent, __LINE__)(_pRenderContext, _name, _flags) #else #define FALCOR_PROFILE(_pRenderContext, _name) #define FALCOR_PROFILE_CUSTOM(_pRenderContext, _name, _flags) diff --git a/Source/Falcor/Utils/Timing/ProfilerUI.cpp b/Source/Falcor/Utils/Timing/ProfilerUI.cpp index a234f5604..9b1ec002c 100644 --- a/Source/Falcor/Utils/Timing/ProfilerUI.cpp +++ b/Source/Falcor/Utils/Timing/ProfilerUI.cpp @@ -32,380 +32,418 @@ namespace Falcor { - namespace - { - const char* kGraphModes[(size_t)ProfilerUI::GraphMode::Count] = { "Off", "CPU Time", "GPU Time" }; - - const float kIndentWidth = 16.f; - const float kPadding = 16.f; - const float kBarWidth = 50.f; - const float kBarHeight = 8.f; - const uint32_t kBarColor = 0xffffffff; - const uint32_t kBarMutedColor = 0xff404040; - const float kGraphBarWidth = 2.f; - - static constexpr size_t kColumnCount = 5; - const char* kColumnTitle[kColumnCount] = { "Event", "CPU Time", "CPU %%", "GPU Time", "GPU %%" }; - const float kHeaderSpacing = 5.f; - - const size_t kHistoryCapacity = 256; - - // Colorblind friendly palette. - const std::vector kColorPalette = { - IM_COL32(0x00, 0x49, 0x49, 0xff), - IM_COL32(0x00, 0x92, 0x92, 0xff), - IM_COL32(0xff, 0x6d, 0xb6, 0xff), - IM_COL32(0xff, 0xb6, 0xdb, 0xff), - IM_COL32(0x49, 0x00, 0x92, 0xff), - IM_COL32(0x00, 0x6d, 0xdb, 0xff), - IM_COL32(0xb6, 0x6d, 0xff, 0xff), - IM_COL32(0x6d, 0xb6, 0xff, 0xff), - IM_COL32(0xb6, 0xdb, 0xff, 0xff), - IM_COL32(0x92, 0x00, 0x00, 0xff), - // Yellow-ish colors don't work well with the highlight color. - // IM_COL32(0x92, 0x49, 0x00, 0xff), - // IM_COL32(0xdb, 0x6d, 0x00, 0xff), - IM_COL32(0x24, 0xff, 0x24, 0xff), - IM_COL32(0xff, 0xff, 0x6d, 0xff) - }; - - const uint32_t kHighlightColor = IM_COL32(0xff, 0x7f, 0x00, 0xcf); - - void drawRectFilled(const ImVec2& pos, const ImVec2& size, uint32_t color = 0xffffffff) - { - auto cursorPos = ImGui::GetCursorScreenPos(); - ImGui::GetWindowDrawList()->AddRectFilled(ImVec2(cursorPos.x + pos.x, cursorPos.y + pos.y), ImVec2(cursorPos.x + pos.x + size.x, cursorPos.y + pos.y + size.y), color); - }; +namespace +{ +const char* kGraphModes[(size_t)ProfilerUI::GraphMode::Count] = {"Off", "CPU Time", "GPU Time"}; + +const float kIndentWidth = 16.f; +const float kPadding = 16.f; +const float kBarWidth = 50.f; +const float kBarHeight = 8.f; +const uint32_t kBarColor = 0xffffffff; +const uint32_t kBarMutedColor = 0xff404040; +const float kGraphBarWidth = 2.f; + +static constexpr size_t kColumnCount = 5; +const char* kColumnTitle[kColumnCount] = {"Event", "CPU Time", "CPU %%", "GPU Time", "GPU %%"}; +const float kHeaderSpacing = 5.f; + +const size_t kHistoryCapacity = 256; + +// Colorblind friendly palette. +const std::vector kColorPalette = { + IM_COL32(0x00, 0x49, 0x49, 0xff), + IM_COL32(0x00, 0x92, 0x92, 0xff), + IM_COL32(0xff, 0x6d, 0xb6, 0xff), + IM_COL32(0xff, 0xb6, 0xdb, 0xff), + IM_COL32(0x49, 0x00, 0x92, 0xff), + IM_COL32(0x00, 0x6d, 0xdb, 0xff), + IM_COL32(0xb6, 0x6d, 0xff, 0xff), + IM_COL32(0x6d, 0xb6, 0xff, 0xff), + IM_COL32(0xb6, 0xdb, 0xff, 0xff), + IM_COL32(0x92, 0x00, 0x00, 0xff), + // Yellow-ish colors don't work well with the highlight color. + // IM_COL32(0x92, 0x49, 0x00, 0xff), + // IM_COL32(0xdb, 0x6d, 0x00, 0xff), + IM_COL32(0x24, 0xff, 0x24, 0xff), + IM_COL32(0xff, 0xff, 0x6d, 0xff), +}; + +const uint32_t kHighlightColor = IM_COL32(0xff, 0x7f, 0x00, 0xcf); + +void drawRectFilled(const ImVec2& pos, const ImVec2& size, uint32_t color = 0xffffffff) +{ + auto cursorPos = ImGui::GetCursorScreenPos(); + ImGui::GetWindowDrawList()->AddRectFilled( + ImVec2(cursorPos.x + pos.x, cursorPos.y + pos.y), ImVec2(cursorPos.x + pos.x + size.x, cursorPos.y + pos.y + size.y), color + ); +}; - void drawBar(float fraction, const ImVec2& size, ImU32 color = 0xffffffff, ImU32 background = 0x00000000, bool highlight = false) - { - auto cursorPos = ImGui::GetCursorScreenPos(); - auto height = ImGui::GetTextLineHeightWithSpacing(); - cursorPos.y += 0.5f * (height - size.y); - ImGui::GetWindowDrawList()->AddRectFilled(ImVec2(cursorPos.x, cursorPos.y), ImVec2(cursorPos.x + size.x, cursorPos.y + size.y), background); - ImGui::GetWindowDrawList()->AddRectFilled(ImVec2(cursorPos.x, cursorPos.y), ImVec2(cursorPos.x + fraction * size.x, cursorPos.y + size.y), color); - if (highlight) ImGui::GetWindowDrawList()->AddRect(ImVec2(cursorPos.x, cursorPos.y), ImVec2(cursorPos.x + size.x, cursorPos.y + size.y), kHighlightColor); - } - } +void drawBar(float fraction, const ImVec2& size, ImU32 color = 0xffffffff, ImU32 background = 0x00000000, bool highlight = false) +{ + auto cursorPos = ImGui::GetCursorScreenPos(); + auto height = ImGui::GetTextLineHeightWithSpacing(); + cursorPos.y += 0.5f * (height - size.y); + ImGui::GetWindowDrawList()->AddRectFilled( + ImVec2(cursorPos.x, cursorPos.y), ImVec2(cursorPos.x + size.x, cursorPos.y + size.y), background + ); + ImGui::GetWindowDrawList()->AddRectFilled( + ImVec2(cursorPos.x, cursorPos.y), ImVec2(cursorPos.x + fraction * size.x, cursorPos.y + size.y), color + ); + if (highlight) + ImGui::GetWindowDrawList()->AddRect( + ImVec2(cursorPos.x, cursorPos.y), ImVec2(cursorPos.x + size.x, cursorPos.y + size.y), kHighlightColor + ); +} +} // namespace + +void ProfilerUI::render() +{ + updateEventData(); + updateGraphData(); + + renderOptions(); - void ProfilerUI::render() + // Compute column widths. + float columnWidth[kColumnCount]; + for (size_t i = 0; i < kColumnCount; ++i) + columnWidth[i] = ImGui::CalcTextSize(kColumnTitle[i]).x + kPadding; + for (const auto& eventData : mEventData) { - updateEventData(); - updateGraphData(); + columnWidth[0] = + std::max(columnWidth[0], ImGui::CalcTextSize(eventData.name.c_str()).x + eventData.level * kIndentWidth + kPadding); + } - renderOptions(); + float startY; + float endY; + size_t newHighlightIndex = -1; - // Compute column widths. - float columnWidth[kColumnCount]; - for (size_t i = 0; i < kColumnCount; ++i) columnWidth[i] = ImGui::CalcTextSize(kColumnTitle[i]).x + kPadding; - for (const auto& eventData : mEventData) - { - columnWidth[0] = std::max(columnWidth[0], ImGui::CalcTextSize(eventData.name.c_str()).x + eventData.level * kIndentWidth + kPadding); - } + // Draw table (last column is used for graph). + ImGui::Columns(kColumnCount + 1, "#events", false); + for (size_t col = 0; col < kColumnCount; ++col) + { + ImGui::SetColumnWidth((int)col, columnWidth[col]); + ImGui::TextUnformatted(kColumnTitle[col]); + ImGui::Dummy(ImVec2(0.f, kHeaderSpacing)); - float startY; - float endY; - size_t newHighlightIndex = -1; + if (col == 0) + startY = ImGui::GetCursorPosY(); - // Draw table (last column is used for graph). - ImGui::Columns(kColumnCount + 1, "#events", false); - for (size_t col = 0; col < kColumnCount; ++col) + for (size_t i = 0; i < mEventData.size(); ++i) { - ImGui::SetColumnWidth((int)col, columnWidth[col]); - ImGui::TextUnformatted(kColumnTitle[col]); - ImGui::Dummy(ImVec2(0.f, kHeaderSpacing)); - - if (col == 0) startY = ImGui::GetCursorPosY(); + const auto& eventData = mEventData[i]; + auto pEvent = eventData.pEvent; - for (size_t i = 0; i < mEventData.size(); ++i) + if (col == 0) // Event name { - const auto& eventData = mEventData[i]; - auto pEvent = eventData.pEvent; - - if (col == 0) // Event name - { - float indent = eventData.level * kIndentWidth; - if (indent > 0.f) ImGui::Indent(indent); - auto color = (i == mHighlightIndex) ? ImColor(kHighlightColor) : ImColor(ImGui::GetStyleColorVec4(ImGuiCol_Text)); - ImGui::TextColored(color, "%s", eventData.name.c_str()); - if (indent > 0.f) ImGui::Unindent(indent); - } - else if (col == 1) // CPU time + float indent = eventData.level * kIndentWidth; + if (indent > 0.f) + ImGui::Indent(indent); + auto color = (i == mHighlightIndex) ? ImColor(kHighlightColor) : ImColor(ImGui::GetStyleColorVec4(ImGuiCol_Text)); + ImGui::TextColored(color, "%s", eventData.name.c_str()); + if (indent > 0.f) + ImGui::Unindent(indent); + } + else if (col == 1) // CPU time + { + ImGui::Text("%.2f ms", eventData.cpuTime); + if (ImGui::IsItemHovered()) { - ImGui::Text("%.2f ms", eventData.cpuTime); - if (ImGui::IsItemHovered()) - { - auto stats = eventData.pEvent->computeCpuTimeStats(); - ImGui::BeginTooltip(); - ImGui::Text("%s\nMin: %.2f\nMax: %.2f\nMean: %.2f\nStdDev: %.2f", eventData.name.c_str(), stats.min, stats.max, stats.mean, stats.stdDev); - ImGui::EndTooltip(); - } + auto stats = eventData.pEvent->computeCpuTimeStats(); + ImGui::BeginTooltip(); + ImGui::Text( + "%s\nMin: %.2f\nMax: %.2f\nMean: %.2f\nStdDev: %.2f", eventData.name.c_str(), stats.min, stats.max, stats.mean, + stats.stdDev + ); + ImGui::EndTooltip(); } - else if (col == 2) // CPU % + } + else if (col == 2) // CPU % + { + ImGui::PushID((int)reinterpret_cast(&eventData)); + float fraction = mTotalCpuTime > 0.f ? eventData.cpuTime / mTotalCpuTime : 0.f; + bool isGraphShown = mGraphMode == GraphMode::CpuTime; + bool isHighlighted = isGraphShown && i == mHighlightIndex; + drawBar( + fraction, ImVec2(kBarWidth, kBarHeight), isGraphShown ? eventData.color : kBarColor, + isGraphShown ? eventData.mutedColor : kBarMutedColor, isHighlighted + ); + ImGui::Dummy(ImVec2(kBarWidth, ImGui::GetTextLineHeight())); + if (ImGui::IsItemHovered()) { - ImGui::PushID((int)reinterpret_cast(&eventData)); - float fraction = mTotalCpuTime > 0.f ? eventData.cpuTime / mTotalCpuTime : 0.f; - bool isGraphShown = mGraphMode == GraphMode::CpuTime; - bool isHighlighted = isGraphShown && i == mHighlightIndex; - drawBar(fraction, ImVec2(kBarWidth, kBarHeight), isGraphShown ? eventData.color : kBarColor, isGraphShown ? eventData.mutedColor : kBarMutedColor, isHighlighted); - ImGui::Dummy(ImVec2(kBarWidth, ImGui::GetTextLineHeight())); - if (ImGui::IsItemHovered()) - { - if (isGraphShown) newHighlightIndex = i; - ImGui::BeginTooltip(); - ImGui::Text("%s\n%.1f%%", eventData.name.c_str(), fraction * 100.f); - ImGui::EndTooltip(); - } - ImGui::PopID(); + if (isGraphShown) + newHighlightIndex = i; + ImGui::BeginTooltip(); + ImGui::Text("%s\n%.1f%%", eventData.name.c_str(), fraction * 100.f); + ImGui::EndTooltip(); } - else if (col == 3) // GPU time + ImGui::PopID(); + } + else if (col == 3) // GPU time + { + ImGui::Text("%.2f ms", eventData.gpuTime); + if (ImGui::IsItemHovered()) { - ImGui::Text("%.2f ms", eventData.gpuTime); - if (ImGui::IsItemHovered()) - { - auto stats = eventData.pEvent->computeGpuTimeStats(); - ImGui::BeginTooltip(); - ImGui::Text("%s\nMin: %.2f\nMax: %.2f\nMean: %.2f\nStdDev: %.2f", eventData.name.c_str(), stats.min, stats.max, stats.mean, stats.stdDev); - ImGui::EndTooltip(); - } + auto stats = eventData.pEvent->computeGpuTimeStats(); + ImGui::BeginTooltip(); + ImGui::Text( + "%s\nMin: %.2f\nMax: %.2f\nMean: %.2f\nStdDev: %.2f", eventData.name.c_str(), stats.min, stats.max, stats.mean, + stats.stdDev + ); + ImGui::EndTooltip(); } - else if (col == 4) // GPU % + } + else if (col == 4) // GPU % + { + ImGui::PushID((int)reinterpret_cast(&eventData)); + float fraction = mTotalGpuTime > 0.f ? eventData.gpuTime / mTotalGpuTime : 0.f; + bool isGraphShown = mGraphMode == GraphMode::GpuTime; + bool isHighlighted = isGraphShown && i == mHighlightIndex; + drawBar( + fraction, ImVec2(kBarWidth, kBarHeight), isGraphShown ? eventData.color : kBarColor, + isGraphShown ? eventData.mutedColor : kBarMutedColor, isHighlighted + ); + ImGui::Dummy(ImVec2(kBarWidth, ImGui::GetTextLineHeight())); + if (ImGui::IsItemHovered()) { - ImGui::PushID((int)reinterpret_cast(&eventData)); - float fraction = mTotalGpuTime > 0.f ? eventData.gpuTime / mTotalGpuTime : 0.f; - bool isGraphShown = mGraphMode == GraphMode::GpuTime; - bool isHighlighted = isGraphShown && i == mHighlightIndex; - drawBar(fraction, ImVec2(kBarWidth, kBarHeight), isGraphShown ? eventData.color : kBarColor, isGraphShown ? eventData.mutedColor : kBarMutedColor, isHighlighted); - ImGui::Dummy(ImVec2(kBarWidth, ImGui::GetTextLineHeight())); - if (ImGui::IsItemHovered()) - { - if (isGraphShown) newHighlightIndex = i; - ImGui::BeginTooltip(); - ImGui::Text("%s\n%.1f%%", eventData.name.c_str(), fraction * 100.f); - ImGui::EndTooltip(); - } - ImGui::PopID(); + if (isGraphShown) + newHighlightIndex = i; + ImGui::BeginTooltip(); + ImGui::Text("%s\n%.1f%%", eventData.name.c_str(), fraction * 100.f); + ImGui::EndTooltip(); } + ImGui::PopID(); } - - if (col == 0) endY = ImGui::GetCursorPosY(); - - ImGui::NextColumn(); } - // Set new highlight index if mouse is over one of the bars. - if (newHighlightIndex != -1) mHighlightIndex = newHighlightIndex; + if (col == 0) + endY = ImGui::GetCursorPosY(); - // Draw the graph. - if (mGraphMode != GraphMode::Off) - { - ImGui::Text("Graph"); - ImGui::Dummy(ImVec2(0.f, kHeaderSpacing)); - ImVec2 graphSize(ImGui::GetWindowSize().x - ImGui::GetCursorPosX(), endY - startY); - renderGraph(graphSize, mHighlightIndex, newHighlightIndex); - mHighlightIndex = newHighlightIndex; - } + ImGui::NextColumn(); } - void ProfilerUI::renderOptions() + // Set new highlight index if mouse is over one of the bars. + if (newHighlightIndex != -1) + mHighlightIndex = newHighlightIndex; + + // Draw the graph. + if (mGraphMode != GraphMode::Off) { - bool paused = mpProfiler->isPaused(); - if (ImGui::Checkbox("Pause", &paused)) mpProfiler->setPaused(paused); + ImGui::Text("Graph"); + ImGui::Dummy(ImVec2(0.f, kHeaderSpacing)); + ImVec2 graphSize(ImGui::GetWindowSize().x - ImGui::GetCursorPosX(), endY - startY); + renderGraph(graphSize, mHighlightIndex, newHighlightIndex); + mHighlightIndex = newHighlightIndex; + } +} - ImGui::SameLine(); - ImGui::Checkbox("Average", &mEnableAverage); +void ProfilerUI::renderOptions() +{ + bool paused = mpProfiler->isPaused(); + if (ImGui::Checkbox("Pause", &paused)) + mpProfiler->setPaused(paused); - ImGui::SameLine(); - ImGui::SetNextItemWidth(100.f); - if (ImGui::Combo("Graph", reinterpret_cast(&mGraphMode), kGraphModes, (int)GraphMode::Count)) clearGraphData(); + ImGui::SameLine(); + ImGui::Checkbox("Average", &mEnableAverage); + + ImGui::SameLine(); + ImGui::SetNextItemWidth(100.f); + if (ImGui::Combo("Graph", reinterpret_cast(&mGraphMode), kGraphModes, (int)GraphMode::Count)) + clearGraphData(); - if (mpProfiler->isCapturing()) + if (mpProfiler->isCapturing()) + { + ImGui::SameLine(); + if (ImGui::Button("End Capture")) { - ImGui::SameLine(); - if (ImGui::Button("End Capture")) + auto pCapture = mpProfiler->endCapture(); + FALCOR_ASSERT(pCapture); + FileDialogFilterVec filters{{"json", "JSON"}}; + std::filesystem::path path; + if (saveFileDialog(filters, path)) { - auto pCapture = mpProfiler->endCapture(); - FALCOR_ASSERT(pCapture); - FileDialogFilterVec filters {{ "json", "JSON" }}; - std::filesystem::path path; - if (saveFileDialog(filters, path)) - { - pCapture->writeToFile(path); - } + pCapture->writeToFile(path); } } - else - { - ImGui::SameLine(); - if (ImGui::Button("Start Capture")) mpProfiler->startCapture(); - } - - ImGui::Separator(); } - - void ProfilerUI::renderGraph(const ImVec2& size, size_t highlightIndex, size_t& newHighlightIndex) + else { - ImVec2 mousePos = ImGui::GetMousePos(); - ImVec2 screenPos = ImGui::GetCursorScreenPos(); - mousePos.x -= screenPos.x; - mousePos.y -= screenPos.y; + ImGui::SameLine(); + if (ImGui::Button("Start Capture")) + mpProfiler->startCapture(); + } - float totalMaxGraphValue = 0.f; - for (const auto& eventData : mEventData) totalMaxGraphValue += eventData.level == 0 ? eventData.maxGraphValue : 0.f; + ImGui::Separator(); +} - const float scaleY = size.y / totalMaxGraphValue; +void ProfilerUI::renderGraph(const ImVec2& size, size_t highlightIndex, size_t& newHighlightIndex) +{ + ImVec2 mousePos = ImGui::GetMousePos(); + ImVec2 screenPos = ImGui::GetCursorScreenPos(); + mousePos.x -= screenPos.x; + mousePos.y -= screenPos.y; - float x = 0.f; - float levelY[128]; - std::optional highlightValue; + float totalMaxGraphValue = 0.f; + for (const auto& eventData : mEventData) + totalMaxGraphValue += eventData.level == 0 ? eventData.maxGraphValue : 0.f; - for (size_t k = 0; k < mHistoryLength; ++k) - { - size_t historyIndex = (mHistoryWrite + kHistoryCapacity - k - 1) % kHistoryCapacity; + const float scaleY = size.y / totalMaxGraphValue; - float totalValue = 0.f; - for (const auto& eventData : mEventData) - { - totalValue += eventData.level == 0 ? eventData.graphHistory[historyIndex] : 0.f; - } + float x = 0.f; + float levelY[128]; + std::optional highlightValue; - levelY[0] = size.y - totalValue * scaleY; - float highlightY = 0.f; - float highlightHeight = 0.f; + for (size_t k = 0; k < mHistoryLength; ++k) + { + size_t historyIndex = (mHistoryWrite + kHistoryCapacity - k - 1) % kHistoryCapacity; - for (size_t i = 0; i < mEventData.size(); ++i) - { - const auto& eventData = mEventData[i]; - float value = eventData.graphHistory[historyIndex]; - uint32_t level = eventData.level; + float totalValue = 0.f; + for (const auto& eventData : mEventData) + { + totalValue += eventData.level == 0 ? eventData.graphHistory[historyIndex] : 0.f; + } - float y = levelY[level]; - float height = value * scaleY; - drawRectFilled(ImVec2(x, y), ImVec2(kGraphBarWidth, height), eventData.color); + levelY[0] = size.y - totalValue * scaleY; + float highlightY = 0.f; + float highlightHeight = 0.f; - // Check if mouse is over this bar for tooltip value and highlighting in next frame. - if (mousePos.x >= x && mousePos.x < x + kGraphBarWidth && mousePos.y >= y && mousePos.y < y + height) - { - newHighlightIndex = i; - highlightValue = value / totalValue; - } + for (size_t i = 0; i < mEventData.size(); ++i) + { + const auto& eventData = mEventData[i]; + float value = eventData.graphHistory[historyIndex]; + uint32_t level = eventData.level; - if (highlightIndex == i) - { - highlightY = y; - highlightHeight = height; - } + float y = levelY[level]; + float height = value * scaleY; + drawRectFilled(ImVec2(x, y), ImVec2(kGraphBarWidth, height), eventData.color); - levelY[level + 1] = levelY[level]; - levelY[level] += height; + // Check if mouse is over this bar for tooltip value and highlighting in next frame. + if (mousePos.x >= x && mousePos.x < x + kGraphBarWidth && mousePos.y >= y && mousePos.y < y + height) + { + newHighlightIndex = i; + highlightValue = value / totalValue; } - if (highlightHeight > 0.f) drawRectFilled(ImVec2(x, highlightY), ImVec2(kGraphBarWidth, highlightHeight), kHighlightColor); + if (highlightIndex == i) + { + highlightY = y; + highlightHeight = height; + } - x += kGraphBarWidth; - if (x > size.x) break; + levelY[level + 1] = levelY[level]; + levelY[level] += height; } - ImGui::Dummy(ImVec2(size)); - if (ImGui::IsItemHovered() && highlightValue) - { - FALCOR_ASSERT(newHighlightIndex >= 0 && newHighlightIndex < mEventData.size()); - ImGui::BeginTooltip(); - ImGui::Text("%s\n%.2f%%", mEventData[newHighlightIndex].name.c_str(), *highlightValue * 100.f); - ImGui::EndTooltip(); - } + if (highlightHeight > 0.f) + drawRectFilled(ImVec2(x, highlightY), ImVec2(kGraphBarWidth, highlightHeight), kHighlightColor); + + x += kGraphBarWidth; + if (x > size.x) + break; } - void ProfilerUI::updateEventData() + ImGui::Dummy(ImVec2(size)); + if (ImGui::IsItemHovered() && highlightValue) { - const auto& events = mpProfiler->getEvents(); + FALCOR_ASSERT(newHighlightIndex >= 0 && newHighlightIndex < mEventData.size()); + ImGui::BeginTooltip(); + ImGui::Text("%s\n%.2f%%", mEventData[newHighlightIndex].name.c_str(), *highlightValue * 100.f); + ImGui::EndTooltip(); + } +} + +void ProfilerUI::updateEventData() +{ + const auto& events = mpProfiler->getEvents(); - mEventData.resize(events.size()); - mTotalCpuTime = 0.f; - mTotalGpuTime = 0.f; + mEventData.resize(events.size()); + mTotalCpuTime = 0.f; + mTotalGpuTime = 0.f; - for (size_t i = 0; i < mEventData.size(); ++i) - { - auto& event = mEventData[i]; - auto pEvent = events[i]; + for (size_t i = 0; i < mEventData.size(); ++i) + { + auto& event = mEventData[i]; + auto pEvent = events[i]; - event.pEvent = pEvent; + event.pEvent = pEvent; - // Update name and level. - std::string name = pEvent->getName(); - uint32_t level = std::max((uint32_t)std::count(name.begin(), name.end(), '/'), 1u) - 1; - name = name.substr(name.find_last_of("/") + 1); - event.name = name; - event.level = level; + // Update name and level. + std::string name = pEvent->getName(); + uint32_t level = std::max((uint32_t)std::count(name.begin(), name.end(), '/'), 1u) - 1; + name = name.substr(name.find_last_of("/") + 1); + event.name = name; + event.level = level; - // Use colors from color palette. - event.color = kColorPalette[i % kColorPalette.size()]; - event.mutedColor = (event.color & 0xffffff) | 0x1f000000; + // Use colors from color palette. + event.color = kColorPalette[i % kColorPalette.size()]; + event.mutedColor = (event.color & 0xffffff) | 0x1f000000; - // Get event times. - event.cpuTime = mEnableAverage ? pEvent->getCpuTimeAverage() : pEvent->getCpuTime(); - event.gpuTime = mEnableAverage ? pEvent->getGpuTimeAverage() : pEvent->getGpuTime(); + // Get event times. + event.cpuTime = mEnableAverage ? pEvent->getCpuTimeAverage() : pEvent->getCpuTime(); + event.gpuTime = mEnableAverage ? pEvent->getGpuTimeAverage() : pEvent->getGpuTime(); - // Sum up times. - if (level == 0) - { - mTotalCpuTime += event.cpuTime; - mTotalGpuTime += event.gpuTime; - } + // Sum up times. + if (level == 0) + { + mTotalCpuTime += event.cpuTime; + mTotalGpuTime += event.gpuTime; } } +} - void ProfilerUI::updateGraphData() - { - if (mGraphMode == GraphMode::Off) return; +void ProfilerUI::updateGraphData() +{ + if (mGraphMode == GraphMode::Off) + return; - mTotalGraphValue = 0.f; + mTotalGraphValue = 0.f; - for (auto& event : mEventData) + for (auto& event : mEventData) + { + switch (mGraphMode) { - switch (mGraphMode) - { - case GraphMode::Off: - continue; - case GraphMode::CpuTime: - event.graphValue = event.cpuTime; - break; - case GraphMode::GpuTime: - event.graphValue = event.gpuTime; - break; - case GraphMode::Count: - break; - } - - if (event.level == 0) mTotalGraphValue += event.graphValue; + case GraphMode::Off: + continue; + case GraphMode::CpuTime: + event.graphValue = event.cpuTime; + break; + case GraphMode::GpuTime: + event.graphValue = event.gpuTime; + break; + case GraphMode::Count: + break; + } - event.graphHistory.resize(kHistoryCapacity); - event.graphHistory[mHistoryWrite] = event.graphValue; + if (event.level == 0) + mTotalGraphValue += event.graphValue; - float maxGraphValue = 0.f; - for (size_t j = 0; j < mHistoryLength; ++j) - { - maxGraphValue = std::max(maxGraphValue, event.graphHistory[j]); - } - event.maxGraphValue = maxGraphValue; - } + event.graphHistory.resize(kHistoryCapacity); + event.graphHistory[mHistoryWrite] = event.graphValue; - if (!mpProfiler->isPaused()) + float maxGraphValue = 0.f; + for (size_t j = 0; j < mHistoryLength; ++j) { - mHistoryWrite = (mHistoryWrite + 1) % kHistoryCapacity; - mHistoryLength = std::min(mHistoryLength + 1, kHistoryCapacity); + maxGraphValue = std::max(maxGraphValue, event.graphHistory[j]); } + event.maxGraphValue = maxGraphValue; } - void ProfilerUI::clearGraphData() + if (!mpProfiler->isPaused()) { - mHistoryLength = 0; - mHistoryWrite = 0; + mHistoryWrite = (mHistoryWrite + 1) % kHistoryCapacity; + mHistoryLength = std::min(mHistoryLength + 1, kHistoryCapacity); + } +} - for (auto& event : mEventData) - { - event.graphValue = 0.f; - event.maxGraphValue = 0.f; - } +void ProfilerUI::clearGraphData() +{ + mHistoryLength = 0; + mHistoryWrite = 0; + + for (auto& event : mEventData) + { + event.graphValue = 0.f; + event.maxGraphValue = 0.f; } } +} // namespace Falcor diff --git a/Source/Falcor/Utils/Timing/ProfilerUI.h b/Source/Falcor/Utils/Timing/ProfilerUI.h index f332adc46..16f6f4f2a 100644 --- a/Source/Falcor/Utils/Timing/ProfilerUI.h +++ b/Source/Falcor/Utils/Timing/ProfilerUI.h @@ -36,87 +36,94 @@ struct ImVec2; namespace Falcor { - /** Helper to draw the profiler UI. - This class uses raw calls to ImGui for increased control - (most of it cannot be done through the Falcor wrapper). - */ - class FALCOR_API ProfilerUI +/** + * Helper to draw the profiler UI. + * This class uses raw calls to ImGui for increased control + * (most of it cannot be done through the Falcor wrapper). + */ +class FALCOR_API ProfilerUI +{ +public: + enum class GraphMode : uint32_t + { + Off, + CpuTime, + GpuTime, + Count, + }; + + /** + * Constructor. + */ + ProfilerUI(Profiler* pProfiler) : mpProfiler(pProfiler) {} + + /** + * Render the profiler UI. + * Note: This must be called within a valid ImGui window. + */ + void render(); + +private: + /** + * Render the profiler options. + */ + void renderOptions(); + + /** + * Render the graph. + * @param[in] size Size of the graph in pixels. + * @param[in] highlightIndex Highlighted event index. + * @param[out] newHighlightIndex New highlighted event index (unchanged if mouse not over graph). + */ + void renderGraph(const ImVec2& size, size_t highlightIndex, size_t& newHighlightIndex); + + /** + * Update the internal event data from the current profiler event data. + */ + void updateEventData(); + + /** + * Update the graph data. + * Retrieves the new current value for the graph depending on the current graph mode + * and writes to the graph history. + */ + void updateGraphData(); + + /** + * Clear the graph data. + */ + void clearGraphData(); + + Profiler* mpProfiler; ///< Profiler instance. + + GraphMode mGraphMode = GraphMode::Off; ///< Graph mode. + bool mEnableAverage = true; ///< Use averaged time values (EMA). + + struct EventData { - public: - enum class GraphMode : uint32_t - { - Off, - CpuTime, - GpuTime, - Count, - }; - - /** Constructor. - */ - ProfilerUI(Profiler* pProfiler) : mpProfiler(pProfiler) {} - - /** Render the profiler UI. - Note: This must be called within a valid ImGui window. - */ - void render(); - - private: - - /** Render the profiler options. - */ - void renderOptions(); - - /** Render the graph. - \param[in] size Size of the graph in pixels. - \param[in] highlightIndex Highlighted event index. - \param[out] newHighlightIndex New highlighted event index (unchanged if mouse not over graph). - */ - void renderGraph(const ImVec2& size, size_t highlightIndex, size_t& newHighlightIndex); - - /** Update the internal event data from the current profiler event data. - */ - void updateEventData(); - - /** Update the graph data. - Retrieves the new current value for the graph depending on the current graph mode - and writes to the graph history. - */ - void updateGraphData(); - - /** Clear the graph data. - */ - void clearGraphData(); - - Profiler* mpProfiler; ///< Profiler instance. - - GraphMode mGraphMode = GraphMode::Off; ///< Graph mode. - bool mEnableAverage = true; ///< Use averaged time values (EMA). - - struct EventData - { - Profiler::Event* pEvent; ///< Source profiler event. - std::string name; ///< Short name. - uint32_t level; ///< Event tree level. - uint32_t color; ///< Color (for graph). - uint32_t mutedColor; ///< Muted color (for graph). - float cpuTime; ///< Current CPU time. - float gpuTime; ///< Current GPU time. - - // Graph data. - float graphValue; ///< Current graph value. - float maxGraphValue; ///< Maximum graph value in history. - std::vector graphHistory; ///< Graph value history. - }; - - std::vector mEventData; ///< Preprocessed event data (presisted across frames). - - float mTotalCpuTime = 0.f; ///< Total CPU time of top level events. - float mTotalGpuTime = 0.f; ///< Total GPU time of top level events. - float mTotalGraphValue = 0.f; ///< Total graph value of top level events. - - size_t mHistoryLength = 0; ///< Current length of graph history. - size_t mHistoryWrite = 0; ///< Graph history write index (round-robin). - - size_t mHighlightIndex = -1; ///< Highlighted event index. + Profiler::Event* pEvent; ///< Source profiler event. + std::string name; ///< Short name. + uint32_t level; ///< Event tree level. + uint32_t color; ///< Color (for graph). + uint32_t mutedColor; ///< Muted color (for graph). + float cpuTime; ///< Current CPU time. + float gpuTime; ///< Current GPU time. + + // Graph data. + float graphValue; ///< Current graph value. + float maxGraphValue; ///< Maximum graph value in history. + std::vector graphHistory; ///< Graph value history. }; -} + + std::vector mEventData; ///< Preprocessed event data (presisted across frames). + + float mTotalCpuTime = 0.f; ///< Total CPU time of top level events. + float mTotalGpuTime = 0.f; ///< Total GPU time of top level events. + float mTotalGraphValue = 0.f; ///< Total graph value of top level events. + + size_t mHistoryLength = 0; ///< Current length of graph history. + size_t mHistoryWrite = 0; ///< Graph history write index (round-robin). + + size_t mHighlightIndex = -1; ///< Highlighted event index. +}; +} // namespace Falcor diff --git a/Source/Falcor/Utils/Timing/TimeReport.cpp b/Source/Falcor/Utils/Timing/TimeReport.cpp index 21c5726f2..99eb89adc 100644 --- a/Source/Falcor/Utils/Timing/TimeReport.cpp +++ b/Source/Falcor/Utils/Timing/TimeReport.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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,43 +32,46 @@ namespace Falcor { - TimeReport::TimeReport() - { - reset(); - } +TimeReport::TimeReport() +{ + reset(); +} - void TimeReport::reset() - { - mLastMeasureTime = CpuTimer::getCurrentTimePoint(); - mMeasurements.clear(); - mTotal = 0.0; - } +void TimeReport::reset() +{ + mLastMeasureTime = CpuTimer::getCurrentTimePoint(); + mMeasurements.clear(); + mTotal = 0.0; +} - void TimeReport::resetTimer() - { - mLastMeasureTime = CpuTimer::getCurrentTimePoint(); - mTotal = 0.0; - } +void TimeReport::resetTimer() +{ + mLastMeasureTime = CpuTimer::getCurrentTimePoint(); + mTotal = 0.0; +} - void TimeReport::printToLog() +void TimeReport::printToLog() +{ + for (const auto& [task, duration] : mMeasurements) { - for (const auto& [task, duration] : mMeasurements) - { - logInfo(padStringToLength(task + ":", 25) + " " + std::to_string(duration) + " s" + (mTotal > 0.0 && !mMeasurements.empty() ? ", " + std::to_string(100.0 * duration / mTotal) + "% of total" : "")); - } + logInfo( + padStringToLength(task + ":", 25) + " " + std::to_string(duration) + " s" + + (mTotal > 0.0 && !mMeasurements.empty() ? ", " + std::to_string(100.0 * duration / mTotal) + "% of total" : "") + ); } +} - void TimeReport::measure(const std::string& name) - { - auto currentTime = CpuTimer::getCurrentTimePoint(); - std::chrono::duration duration = currentTime - mLastMeasureTime; - mLastMeasureTime = currentTime; - mMeasurements.push_back({name, duration.count()}); - } +void TimeReport::measure(const std::string& name) +{ + auto currentTime = CpuTimer::getCurrentTimePoint(); + std::chrono::duration duration = currentTime - mLastMeasureTime; + mLastMeasureTime = currentTime; + mMeasurements.push_back({name, duration.count()}); +} - void TimeReport::addTotal(const std::string name) - { - mTotal = std::accumulate(mMeasurements.begin(), mMeasurements.end(), 0.0, [] (double t, auto &&m) { return t + m.second; }); - mMeasurements.push_back({"Total", mTotal}); - } +void TimeReport::addTotal(const std::string name) +{ + mTotal = std::accumulate(mMeasurements.begin(), mMeasurements.end(), 0.0, [](double t, auto&& m) { return t + m.second; }); + mMeasurements.push_back({"Total", mTotal}); } +} // namespace Falcor diff --git a/Source/Falcor/Utils/Timing/TimeReport.h b/Source/Falcor/Utils/Timing/TimeReport.h index 488d02b19..003193ad6 100644 --- a/Source/Falcor/Utils/Timing/TimeReport.h +++ b/Source/Falcor/Utils/Timing/TimeReport.h @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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,40 +34,46 @@ namespace Falcor { - /** Utility class to record a number of timing measurements and print them afterwards. - This is mainly intended for measuring longer running tasks on the CPU. - */ - class FALCOR_API TimeReport - { - public: - TimeReport(); +/** + * Utility class to record a number of timing measurements and print them afterwards. + * This is mainly intended for measuring longer running tasks on the CPU. + */ +class FALCOR_API TimeReport +{ +public: + TimeReport(); - /** Resets the recorded measurements and the internal timer. - */ - void reset(); + /** + * Resets the recorded measurements and the internal timer. + */ + void reset(); - /** Resets the the internal timer but not the recoreded measurements. - */ - void resetTimer(); + /** + * Resets the the internal timer but not the recoreded measurements. + */ + void resetTimer(); - /** Prints the recorded measurements to the logfile. - */ - void printToLog(); + /** + * Prints the recorded measurements to the logfile. + */ + void printToLog(); - /** Records a time measurement. - Measures time since last call to reset() or measure(), whichever happened more recently. - \param[in] name Name of the record. - */ - void measure(const std::string& name); + /** + * Records a time measurement. + * Measures time since last call to reset() or measure(), whichever happened more recently. + * @param[in] name Name of the record. + */ + void measure(const std::string& name); - /** Add a record containing the total of all measurements. - \param[in] name Name of the record. - */ - void addTotal(const std::string name = "Total"); + /** + * Add a record containing the total of all measurements. + * @param[in] name Name of the record. + */ + void addTotal(const std::string name = "Total"); - private: - CpuTimer::TimePoint mLastMeasureTime; - std::vector> mMeasurements; - double mTotal = 0.0; - }; -} +private: + CpuTimer::TimePoint mLastMeasureTime; + std::vector> mMeasurements; + double mTotal = 0.0; +}; +} // namespace Falcor diff --git a/Source/Falcor/Utils/UI/Font.cpp b/Source/Falcor/Utils/UI/Font.cpp index 449d249e0..db31412b1 100644 --- a/Source/Falcor/Utils/UI/Font.cpp +++ b/Source/Falcor/Utils/UI/Font.cpp @@ -32,89 +32,85 @@ namespace Falcor { - Font::Font(Device* pDevice, const std::filesystem::path& path) - { - if (!loadFromFile(pDevice, path)) - throw RuntimeError("Failed to create font resource"); - } - - Font::UniquePtr Font::create(Device* pDevice, const std::filesystem::path& path) - { - return UniquePtr(new Font(pDevice, path)); - } - - static const uint32_t kFontMagicNumber = 0xDEAD0001; +static const uint32_t kFontMagicNumber = 0xDEAD0001; #pragma pack(1) - struct FontFileHeader - { - uint32_t structSize; - uint32_t charDataSize; - uint32_t magicNumber; - uint32_t charCount; - float fontHeight; - float tabWidth; - float letterSpacing; - }; +struct FontFileHeader +{ + uint32_t structSize; + uint32_t charDataSize; + uint32_t magicNumber; + uint32_t charCount; + float fontHeight; + float tabWidth; + float letterSpacing; +}; #pragma pack(1) - struct FontCharData - { - char character; - float topLeftX; - float topLeftY; - float width; - float height; - }; +struct FontCharData +{ + char character; + float topLeftX; + float topLeftY; + float width; + float height; +}; - Font::~Font() = default; +Font::Font(ref pDevice, const std::filesystem::path& path) +{ + if (!loadFromFile(pDevice, path)) + throw RuntimeError("Failed to create font resource"); +} - bool Font::loadFromFile(Device* pDevice, const std::filesystem::path& path) - { - std::filesystem::path texturePath = path; - texturePath.replace_extension(".dds"); - std::filesystem::path dataPath = path; - dataPath.replace_extension(".bin"); +Font::~Font() = default; - if (!std::filesystem::exists(texturePath) || !std::filesystem::exists(dataPath)) - return false; +bool Font::loadFromFile(ref pDevice, const std::filesystem::path& path) +{ + std::filesystem::path texturePath = path; + texturePath.replace_extension(".dds"); + std::filesystem::path dataPath = path; + dataPath.replace_extension(".bin"); - // Load the data - std::ifstream data(dataPath, std::ios::binary); - FontFileHeader header; - // Read the header - data.read((char*)&header, sizeof(header)); - bool valid = (header.structSize == sizeof(header)); - valid = valid && (header.magicNumber == kFontMagicNumber); - valid = valid && (header.charDataSize == sizeof(FontCharData)); - valid = valid && (header.charCount == mCharCount); + if (!std::filesystem::exists(texturePath) || !std::filesystem::exists(dataPath)) + return false; - if (!valid) return false; + // Load the data + std::ifstream data(dataPath, std::ios::binary); + FontFileHeader header; + // Read the header + data.read((char*)&header, sizeof(header)); + bool valid = (header.structSize == sizeof(header)); + valid = valid && (header.magicNumber == kFontMagicNumber); + valid = valid && (header.charDataSize == sizeof(FontCharData)); + valid = valid && (header.charCount == mCharCount); - mTabWidth = header.tabWidth; - mFontHeight = header.fontHeight; + if (!valid) + return false; - mLetterSpacing = 0; - // Load the char data - for(uint32_t i = 0; i < mCharCount; i++) - { - FontCharData charData; - data.read((char*)&charData, sizeof(FontCharData)); - if(charData.character != i + mFirstChar) - { - data.close(); - return false; - } + mTabWidth = header.tabWidth; + mFontHeight = header.fontHeight; - mCharDesc[i].topLeft.x = charData.topLeftX; - mCharDesc[i].topLeft.y = charData.topLeftY; - mCharDesc[i].size.x = charData.width; - mCharDesc[i].size.y = charData.height; - mLetterSpacing = std::max(mLetterSpacing, charData.width); + mLetterSpacing = 0; + // Load the char data + for (uint32_t i = 0; i < mCharCount; i++) + { + FontCharData charData; + data.read((char*)&charData, sizeof(FontCharData)); + if (charData.character != i + mFirstChar) + { + data.close(); + return false; } - // Load the texture - mpTexture = Texture::createFromFile(pDevice, texturePath, false, false); - return mpTexture != nullptr; + mCharDesc[i].topLeft.x = charData.topLeftX; + mCharDesc[i].topLeft.y = charData.topLeftY; + mCharDesc[i].size.x = charData.width; + mCharDesc[i].size.y = charData.height; + mLetterSpacing = std::max(mLetterSpacing, charData.width); } + + // Load the texture + mpTexture = Texture::createFromFile(pDevice, texturePath, false, false); + return mpTexture != nullptr; } +} // namespace Falcor diff --git a/Source/Falcor/Utils/UI/Font.h b/Source/Falcor/Utils/UI/Font.h index 32ca6cc56..77a5db420 100644 --- a/Source/Falcor/Utils/UI/Font.h +++ b/Source/Falcor/Utils/UI/Font.h @@ -36,71 +36,74 @@ namespace Falcor { - /** This class holds data and texture used to render text. - It represents a mono-spaced font. - */ - class Font - { - public: - using UniquePtr = std::unique_ptr; - using UniqueConstPtr = std::unique_ptr; - - ~Font(); +/** + * This class holds data and texture used to render text. + * It represents a mono-spaced font. + */ +class Font +{ +public: + /** + * Constructor. Throws an exception if creation failed. + * @param[in] path File path without extension. + */ + Font(ref pDevice, const std::filesystem::path& path); - /** Create a font object. - \param[in] path File path without extension. - \return New object, or throws an exception if creation failed. - */ - static UniquePtr create(Device* pDevice, const std::filesystem::path& path); + ~Font(); - /** The structs contains information on the location of the character in the texture - */ - struct CharTexCrdDesc - { - float2 topLeft; ///< Non-normalized origin of the character in the texture - float2 size; ///< Size in pixels of the character. This should be used to initialize the texture-coordinate when rendering. - }; + /** + * The structs contains information on the location of the character in the texture + */ + struct CharTexCrdDesc + { + float2 topLeft; ///< Non-normalized origin of the character in the texture + float2 size; ///< Size in pixels of the character. This should be used to initialize the texture-coordinate when rendering. + }; - /** Get the texture containing the characters - */ - Texture::SharedPtr getTexture() const { return mpTexture; } + /** + * Get the texture containing the characters + */ + ref getTexture() const { return mpTexture; } - /** Get the character descriptor - */ - const CharTexCrdDesc& getCharDesc(char c) const - { - FALCOR_ASSERT(c >= mFirstChar && c <= mLastChar); - return mCharDesc[c - mFirstChar]; - } + /** + * Get the character descriptor + */ + const CharTexCrdDesc& getCharDesc(char c) const + { + FALCOR_ASSERT(c >= mFirstChar && c <= mLastChar); + return mCharDesc[c - mFirstChar]; + } - /** Get the height in pixels of the font - */ - float getFontHeight() const { return mFontHeight; } + /** + * Get the height in pixels of the font + */ + float getFontHeight() const { return mFontHeight; } - /** Get the width in pixels of the tab character - */ - float getTabWidth() const { return mTabWidth; } + /** + * Get the width in pixels of the tab character + */ + float getTabWidth() const { return mTabWidth; } - /** Get the spacing in pixels between 2 characters. This is measured as (start-of-char-2) - (start-of-char-1). - */ - float getLettersSpacing() const { return mLetterSpacing; } + /** + * Get the spacing in pixels between 2 characters. This is measured as (start-of-char-2) - (start-of-char-1). + */ + float getLettersSpacing() const { return mLetterSpacing; } - private: - Font(Device* pDevice, const std::filesystem::path& path); - Font(const Font&) = delete; - Font& operator=(const Font&) = delete; +private: + Font(const Font&) = delete; + Font& operator=(const Font&) = delete; - bool loadFromFile(Device* pDevice, const std::filesystem::path& path); + bool loadFromFile(ref pDevice, const std::filesystem::path& path); - static const char mFirstChar = '!'; - static const char mLastChar = '~'; - static const uint32_t mCharCount = mLastChar - mFirstChar + 1; - static const uint32_t mTexWidth = 1024; + static const char mFirstChar = '!'; + static const char mLastChar = '~'; + static const uint32_t mCharCount = mLastChar - mFirstChar + 1; + static const uint32_t mTexWidth = 1024; - Texture::SharedPtr mpTexture; - CharTexCrdDesc mCharDesc[mCharCount]; - float mFontHeight; - float mTabWidth; - float mLetterSpacing; - }; -} + ref mpTexture; + CharTexCrdDesc mCharDesc[mCharCount]; + float mFontHeight; + float mTabWidth; + float mLetterSpacing; +}; +} // namespace Falcor diff --git a/Source/Falcor/Utils/UI/Gui.cpp b/Source/Falcor/Utils/UI/Gui.cpp index d593577f4..ab52054ef 100644 --- a/Source/Falcor/Utils/UI/Gui.cpp +++ b/Source/Falcor/Utils/UI/Gui.cpp @@ -38,1590 +38,1844 @@ #include "Utils/StringUtils.h" #include "Utils/UI/SpectrumUI.h" -#include #include -#pragma warning(disable : 4756) // Overflow in constant arithmetic caused by calculating the setFloat*() functions (when calculating the step and min/max are +/- INF). +#include + +// Overflow in constant arithmetic caused by calculating the setFloat*() functions (when calculating the step and min/max are +/- INF). +#if FALCOR_MSVC +#pragma warning(disable : 4756) +#endif namespace Falcor { - class GuiImpl - { - public: - GuiImpl(std::shared_ptr pDevice, float scaleFactor); - - private: - friend class Gui; - void init(Gui* pGui, float scaleFactor); - void createVao(uint32_t vertexCount, uint32_t indexCount); - void compileFonts(); +class GuiImpl +{ +public: + GuiImpl(ref pDevice, float scaleFactor); - // Helper to create multiple inline text boxes - bool addCheckboxes(const char label[], bool* pData, uint32_t numCheckboxes, bool sameLine); +private: + friend class Gui; + void init(Gui* pGui, float scaleFactor); + void createVao(uint32_t vertexCount, uint32_t indexCount); + void compileFonts(); - struct ComboData - { - uint32_t lastVal = -1; - int32_t currentItem = -1; - }; - std::unordered_map mDropDownValues; + // Helper to create multiple inline text boxes + bool addCheckboxes(const char label[], bool* pData, uint32_t numCheckboxes, bool sameLine); - // 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(); - - std::shared_ptr mpDevice; - 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, uint2 size = { 250, 200 }, uint2 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 float2& size, bool sameLine = false); - void addRect(const float2& size, const float4& color = float4(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[], float3& 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); - void addTextWrapped(const char text[]); - 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[], float3& var, bool sameLine = false); - bool addRgbaColor(const char label[], float4& var, bool sameLine = false); - - void addImage(const char label[], const Texture::SharedPtr& pTex, float2 size = float2(0), bool maintainRatio = true, bool sameLine = false); - bool addImageButton(const char label[], const Texture::SharedPtr& pTex, float2 size, bool maintainRatio = true, bool sameLine = false); - - template - bool addScalarVar(const char label[], T& var, T minVal = std::numeric_limits::lowest(), 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::lowest(), 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::lowest(), 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::lowest(), typename T::value_type maxVal = std::numeric_limits::max(), bool sameLine = false, const char* displayFormat = nullptr); - - template - bool addMatrixVar(const char label[], rmcv::matrix& 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); + struct ComboData + { + uint32_t lastVal = -1; + int32_t currentItem = -1; }; + std::unordered_map mDropDownValues; - GuiImpl::GuiImpl(std::shared_ptr pDevice, float scaleFactor) - : mpDevice(std::move(pDevice)) - , mScaleFactor(scaleFactor) + // This struct is used to cache the mouse events + struct MouseEvents { - ImGui::CreateContext(); - ImGuiIO& io = ImGui::GetIO(); - io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; - io.KeyMap[ImGuiKey_Space] = (uint32_t)Input::Key::Space; - io.KeyMap[ImGuiKey_Apostrophe] = (uint32_t)Input::Key::Apostrophe; - io.KeyMap[ImGuiKey_Comma] = (uint32_t)Input::Key::Comma; - io.KeyMap[ImGuiKey_Minus] = (uint32_t)Input::Key::Minus; - io.KeyMap[ImGuiKey_Period] = (uint32_t)Input::Key::Period; - io.KeyMap[ImGuiKey_Slash] = (uint32_t)Input::Key::Slash; - io.KeyMap[ImGuiKey_0] = (uint32_t)Input::Key::Key0; - io.KeyMap[ImGuiKey_1] = (uint32_t)Input::Key::Key1; - io.KeyMap[ImGuiKey_2] = (uint32_t)Input::Key::Key2; - io.KeyMap[ImGuiKey_3] = (uint32_t)Input::Key::Key3; - io.KeyMap[ImGuiKey_4] = (uint32_t)Input::Key::Key4; - io.KeyMap[ImGuiKey_5] = (uint32_t)Input::Key::Key5; - io.KeyMap[ImGuiKey_6] = (uint32_t)Input::Key::Key6; - io.KeyMap[ImGuiKey_7] = (uint32_t)Input::Key::Key7; - io.KeyMap[ImGuiKey_8] = (uint32_t)Input::Key::Key8; - io.KeyMap[ImGuiKey_9] = (uint32_t)Input::Key::Key9; - io.KeyMap[ImGuiKey_Semicolon] = (uint32_t)Input::Key::Semicolon; - io.KeyMap[ImGuiKey_Equal] = (uint32_t)Input::Key::Equal; - io.KeyMap[ImGuiKey_A] = (uint32_t)Input::Key::A; - io.KeyMap[ImGuiKey_B] = (uint32_t)Input::Key::B; - io.KeyMap[ImGuiKey_C] = (uint32_t)Input::Key::C; - io.KeyMap[ImGuiKey_D] = (uint32_t)Input::Key::D; - io.KeyMap[ImGuiKey_E] = (uint32_t)Input::Key::E; - io.KeyMap[ImGuiKey_F] = (uint32_t)Input::Key::F; - io.KeyMap[ImGuiKey_G] = (uint32_t)Input::Key::G; - io.KeyMap[ImGuiKey_H] = (uint32_t)Input::Key::H; - io.KeyMap[ImGuiKey_I] = (uint32_t)Input::Key::I; - io.KeyMap[ImGuiKey_J] = (uint32_t)Input::Key::J; - io.KeyMap[ImGuiKey_K] = (uint32_t)Input::Key::K; - io.KeyMap[ImGuiKey_L] = (uint32_t)Input::Key::L; - io.KeyMap[ImGuiKey_M] = (uint32_t)Input::Key::M; - io.KeyMap[ImGuiKey_N] = (uint32_t)Input::Key::N; - io.KeyMap[ImGuiKey_O] = (uint32_t)Input::Key::O; - io.KeyMap[ImGuiKey_P] = (uint32_t)Input::Key::P; - io.KeyMap[ImGuiKey_Q] = (uint32_t)Input::Key::Q; - io.KeyMap[ImGuiKey_R] = (uint32_t)Input::Key::R; - io.KeyMap[ImGuiKey_S] = (uint32_t)Input::Key::S; - io.KeyMap[ImGuiKey_T] = (uint32_t)Input::Key::T; - io.KeyMap[ImGuiKey_U] = (uint32_t)Input::Key::U; - io.KeyMap[ImGuiKey_V] = (uint32_t)Input::Key::V; - io.KeyMap[ImGuiKey_W] = (uint32_t)Input::Key::W; - io.KeyMap[ImGuiKey_X] = (uint32_t)Input::Key::X; - io.KeyMap[ImGuiKey_Y] = (uint32_t)Input::Key::Y; - io.KeyMap[ImGuiKey_Z] = (uint32_t)Input::Key::Z; - io.KeyMap[ImGuiKey_LeftBracket] = (uint32_t)Input::Key::LeftBracket; - io.KeyMap[ImGuiKey_Backslash] = (uint32_t)Input::Key::Backslash; - io.KeyMap[ImGuiKey_RightBracket] = (uint32_t)Input::Key::RightBracket; - io.KeyMap[ImGuiKey_GraveAccent] = (uint32_t)Input::Key::GraveAccent; - io.KeyMap[ImGuiKey_Escape] = (uint32_t)Input::Key::Escape; - io.KeyMap[ImGuiKey_Tab] = (uint32_t)Input::Key::Tab; - io.KeyMap[ImGuiKey_Enter] = (uint32_t)Input::Key::Enter; - io.KeyMap[ImGuiKey_Backspace] = (uint32_t)Input::Key::Backspace; - io.KeyMap[ImGuiKey_Insert] = (uint32_t)Input::Key::Insert; - io.KeyMap[ImGuiKey_Delete] = (uint32_t)Input::Key::Del; - io.KeyMap[ImGuiKey_LeftArrow] = (uint32_t)Input::Key::Left; - io.KeyMap[ImGuiKey_RightArrow] = (uint32_t)Input::Key::Right; - io.KeyMap[ImGuiKey_UpArrow] = (uint32_t)Input::Key::Up; - io.KeyMap[ImGuiKey_DownArrow] = (uint32_t)Input::Key::Down; - io.KeyMap[ImGuiKey_PageUp] = (uint32_t)Input::Key::PageUp; - io.KeyMap[ImGuiKey_PageDown] = (uint32_t)Input::Key::PageDown; - io.KeyMap[ImGuiKey_Home] = (uint32_t)Input::Key::Home; - io.KeyMap[ImGuiKey_End] = (uint32_t)Input::Key::End; - io.KeyMap[ImGuiKey_CapsLock] = (uint32_t)Input::Key::CapsLock; - io.KeyMap[ImGuiKey_ScrollLock] = (uint32_t)Input::Key::ScrollLock; - io.KeyMap[ImGuiKey_NumLock] = (uint32_t)Input::Key::NumLock; - io.KeyMap[ImGuiKey_PrintScreen] = (uint32_t)Input::Key::PrintScreen; - io.KeyMap[ImGuiKey_Pause] = (uint32_t)Input::Key::Pause; - io.KeyMap[ImGuiKey_F1] = (uint32_t)Input::Key::F1; - io.KeyMap[ImGuiKey_F2] = (uint32_t)Input::Key::F2; - io.KeyMap[ImGuiKey_F3] = (uint32_t)Input::Key::F3; - io.KeyMap[ImGuiKey_F4] = (uint32_t)Input::Key::F4; - io.KeyMap[ImGuiKey_F5] = (uint32_t)Input::Key::F5; - io.KeyMap[ImGuiKey_F6] = (uint32_t)Input::Key::F6; - io.KeyMap[ImGuiKey_F7] = (uint32_t)Input::Key::F7; - io.KeyMap[ImGuiKey_F8] = (uint32_t)Input::Key::F8; - io.KeyMap[ImGuiKey_F9] = (uint32_t)Input::Key::F9; - io.KeyMap[ImGuiKey_F10] = (uint32_t)Input::Key::F10; - io.KeyMap[ImGuiKey_F11] = (uint32_t)Input::Key::F11; - io.KeyMap[ImGuiKey_F12] = (uint32_t)Input::Key::F12; - io.KeyMap[ImGuiKey_Keypad0] = (uint32_t)Input::Key::Keypad0; - io.KeyMap[ImGuiKey_Keypad1] = (uint32_t)Input::Key::Keypad1; - io.KeyMap[ImGuiKey_Keypad2] = (uint32_t)Input::Key::Keypad2; - io.KeyMap[ImGuiKey_Keypad3] = (uint32_t)Input::Key::Keypad3; - io.KeyMap[ImGuiKey_Keypad4] = (uint32_t)Input::Key::Keypad4; - io.KeyMap[ImGuiKey_Keypad5] = (uint32_t)Input::Key::Keypad5; - io.KeyMap[ImGuiKey_Keypad6] = (uint32_t)Input::Key::Keypad6; - io.KeyMap[ImGuiKey_Keypad7] = (uint32_t)Input::Key::Keypad7; - io.KeyMap[ImGuiKey_Keypad8] = (uint32_t)Input::Key::Keypad8; - io.KeyMap[ImGuiKey_Keypad9] = (uint32_t)Input::Key::Keypad9; - io.KeyMap[ImGuiKey_KeypadDivide] = (uint32_t)Input::Key::KeypadDivide; - io.KeyMap[ImGuiKey_KeypadMultiply] = (uint32_t)Input::Key::KeypadMultiply; - io.KeyMap[ImGuiKey_KeypadSubtract] = (uint32_t)Input::Key::KeypadSubtract; - io.KeyMap[ImGuiKey_KeypadAdd] = (uint32_t)Input::Key::KeypadAdd; - io.KeyMap[ImGuiKey_KeypadEnter] = (uint32_t)Input::Key::KeypadEnter; - io.KeyMap[ImGuiKey_LeftShift] = (uint32_t)Input::Key::LeftShift; - io.KeyMap[ImGuiKey_LeftCtrl] = (uint32_t)Input::Key::LeftControl; - io.KeyMap[ImGuiKey_LeftAlt] = (uint32_t)Input::Key::LeftAlt; - io.KeyMap[ImGuiKey_LeftSuper] = (uint32_t)Input::Key::LeftSuper; - io.KeyMap[ImGuiKey_RightShift] = (uint32_t)Input::Key::RightShift; - io.KeyMap[ImGuiKey_RightCtrl] = (uint32_t)Input::Key::RightControl; - io.KeyMap[ImGuiKey_RightAlt] = (uint32_t)Input::Key::RightAlt; - io.KeyMap[ImGuiKey_RightSuper] = (uint32_t)Input::Key::RightSuper; - io.KeyMap[ImGuiKey_Menu] = (uint32_t)Input::Key::Menu; - 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(mpDevice); - - // Create the program - mpProgram = GraphicsProgram::createFromFile(mpDevice, "Utils/UI/Gui.slang", "vsMain", "psMain"); - mpProgramVars = GraphicsVars::create(mpDevice, mpProgram->getReflector()); - mpPipelineState->setProgram(mpProgram); - - // 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"); - } + bool buttonPressed[3] = {0}; + bool buttonReleased[3] = {0}; + }; - void GuiImpl::createVao(uint32_t vertexCount, uint32_t indexCount) - { - static_assert(sizeof(ImDrawIdx) == sizeof(uint16_t), "ImDrawIdx expected size is a word"); - FALCOR_ASSERT(vertexCount > 0 && indexCount > 0); - uint32_t requiredVbSize = vertexCount * sizeof(ImDrawVert); - uint32_t requiredIbSize = indexCount * sizeof(uint16_t); - bool createVB = true; - bool createIB = true; - - if (mpVao) - { - FALCOR_ASSERT(mpVao->getVertexBuffer(0) && mpVao->getIndexBuffer()); - createVB = mpVao->getVertexBuffer(0)->getSize() < requiredVbSize; - createIB = mpVao->getIndexBuffer()->getSize() < requiredIbSize; + MouseEvents mMouseEvents; + void setIoMouseEvents(); + void resetMouseEvents(); + + ref mpDevice; + ImGuiContext* mpContext; + ref mpVao; + ref mpLayout; + ref mpPipelineState; + uint32_t mGroupStackSize = 0; + + ref mpProgram; + ref mpProgramVars; + ParameterBlockReflection::BindLocation mGuiImageLoc; + float mScaleFactor = 1.0f; + std::unordered_map mFontMap; + ImFont* mpActiveFont = nullptr; + std::map> mLoadedImages; + + 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, + uint2 size = {250, 200}, + uint2 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 float2& size, bool sameLine = false); + void addRect(const float2& size, const float4& color = float4(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[], float3& 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); - if (!createIB && !createVB) - { - return; - } - } + void addText(const char text[], bool sameLine = false); + void addTextWrapped(const char text[]); + 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); - // Need to create a new VAO - std::vector pVB(1); - pVB[0] = createVB ? Buffer::create(mpDevice.get(), requiredVbSize + sizeof(ImDrawVert) * 1000, Buffer::BindFlags::Vertex, Buffer::CpuAccess::Write, nullptr) : mpVao->getVertexBuffer(0); - Buffer::SharedPtr pIB = createIB ? Buffer::create(mpDevice.get(), requiredIbSize, Buffer::BindFlags::Index, Buffer::CpuAccess::Write, nullptr) : mpVao->getIndexBuffer(); - mpVao = Vao::create(Vao::Topology::TriangleList, mpLayout, pVB, pIB, ResourceFormat::R16Uint); - } + bool addRgbColor(const char label[], float3& var, bool sameLine = false); + bool addRgbaColor(const char label[], float4& var, bool sameLine = false); - void GuiImpl::compileFonts() - { - uint8_t* pFontData; - int32_t width, height; + const Texture* loadImage(const std::filesystem::path& path); + void addImage(const char label[], const Texture* pTex, float2 size = float2(0), bool maintainRatio = true, bool sameLine = false); + bool addImageButton(const char label[], const Texture* pTex, float2 size, bool maintainRatio = true, bool sameLine = false); - // Initialize font data - ImGui::GetIO().Fonts->GetTexDataAsAlpha8(&pFontData, &width, &height); - Texture::SharedPtr pTexture = Texture::create2D(mpDevice.get(), width, height, ResourceFormat::R8Unorm, 1, 1, pFontData); - mpProgramVars->setTexture("gFont", pTexture); - } + template + bool addScalarVar( + const char label[], + T& var, + T minVal = std::numeric_limits::lowest(), + 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::lowest(), + 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::lowest(), + 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::lowest(), + typename T::value_type maxVal = std::numeric_limits::max(), + bool sameLine = false, + const char* displayFormat = nullptr + ); + + template + bool addMatrixVar( + const char label[], + math::matrix& 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 + ); +}; + +GuiImpl::GuiImpl(ref pDevice, float scaleFactor) : mpDevice(pDevice), mScaleFactor(scaleFactor) +{ + mpContext = ImGui::CreateContext(); + ImGui::SetCurrentContext(mpContext); + ImGuiIO& io = ImGui::GetIO(); + io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; + io.KeyMap[ImGuiKey_Space] = (uint32_t)Input::Key::Space; + io.KeyMap[ImGuiKey_Apostrophe] = (uint32_t)Input::Key::Apostrophe; + io.KeyMap[ImGuiKey_Comma] = (uint32_t)Input::Key::Comma; + io.KeyMap[ImGuiKey_Minus] = (uint32_t)Input::Key::Minus; + io.KeyMap[ImGuiKey_Period] = (uint32_t)Input::Key::Period; + io.KeyMap[ImGuiKey_Slash] = (uint32_t)Input::Key::Slash; + io.KeyMap[ImGuiKey_0] = (uint32_t)Input::Key::Key0; + io.KeyMap[ImGuiKey_1] = (uint32_t)Input::Key::Key1; + io.KeyMap[ImGuiKey_2] = (uint32_t)Input::Key::Key2; + io.KeyMap[ImGuiKey_3] = (uint32_t)Input::Key::Key3; + io.KeyMap[ImGuiKey_4] = (uint32_t)Input::Key::Key4; + io.KeyMap[ImGuiKey_5] = (uint32_t)Input::Key::Key5; + io.KeyMap[ImGuiKey_6] = (uint32_t)Input::Key::Key6; + io.KeyMap[ImGuiKey_7] = (uint32_t)Input::Key::Key7; + io.KeyMap[ImGuiKey_8] = (uint32_t)Input::Key::Key8; + io.KeyMap[ImGuiKey_9] = (uint32_t)Input::Key::Key9; + io.KeyMap[ImGuiKey_Semicolon] = (uint32_t)Input::Key::Semicolon; + io.KeyMap[ImGuiKey_Equal] = (uint32_t)Input::Key::Equal; + io.KeyMap[ImGuiKey_A] = (uint32_t)Input::Key::A; + io.KeyMap[ImGuiKey_B] = (uint32_t)Input::Key::B; + io.KeyMap[ImGuiKey_C] = (uint32_t)Input::Key::C; + io.KeyMap[ImGuiKey_D] = (uint32_t)Input::Key::D; + io.KeyMap[ImGuiKey_E] = (uint32_t)Input::Key::E; + io.KeyMap[ImGuiKey_F] = (uint32_t)Input::Key::F; + io.KeyMap[ImGuiKey_G] = (uint32_t)Input::Key::G; + io.KeyMap[ImGuiKey_H] = (uint32_t)Input::Key::H; + io.KeyMap[ImGuiKey_I] = (uint32_t)Input::Key::I; + io.KeyMap[ImGuiKey_J] = (uint32_t)Input::Key::J; + io.KeyMap[ImGuiKey_K] = (uint32_t)Input::Key::K; + io.KeyMap[ImGuiKey_L] = (uint32_t)Input::Key::L; + io.KeyMap[ImGuiKey_M] = (uint32_t)Input::Key::M; + io.KeyMap[ImGuiKey_N] = (uint32_t)Input::Key::N; + io.KeyMap[ImGuiKey_O] = (uint32_t)Input::Key::O; + io.KeyMap[ImGuiKey_P] = (uint32_t)Input::Key::P; + io.KeyMap[ImGuiKey_Q] = (uint32_t)Input::Key::Q; + io.KeyMap[ImGuiKey_R] = (uint32_t)Input::Key::R; + io.KeyMap[ImGuiKey_S] = (uint32_t)Input::Key::S; + io.KeyMap[ImGuiKey_T] = (uint32_t)Input::Key::T; + io.KeyMap[ImGuiKey_U] = (uint32_t)Input::Key::U; + io.KeyMap[ImGuiKey_V] = (uint32_t)Input::Key::V; + io.KeyMap[ImGuiKey_W] = (uint32_t)Input::Key::W; + io.KeyMap[ImGuiKey_X] = (uint32_t)Input::Key::X; + io.KeyMap[ImGuiKey_Y] = (uint32_t)Input::Key::Y; + io.KeyMap[ImGuiKey_Z] = (uint32_t)Input::Key::Z; + io.KeyMap[ImGuiKey_LeftBracket] = (uint32_t)Input::Key::LeftBracket; + io.KeyMap[ImGuiKey_Backslash] = (uint32_t)Input::Key::Backslash; + io.KeyMap[ImGuiKey_RightBracket] = (uint32_t)Input::Key::RightBracket; + io.KeyMap[ImGuiKey_GraveAccent] = (uint32_t)Input::Key::GraveAccent; + io.KeyMap[ImGuiKey_Escape] = (uint32_t)Input::Key::Escape; + io.KeyMap[ImGuiKey_Tab] = (uint32_t)Input::Key::Tab; + io.KeyMap[ImGuiKey_Enter] = (uint32_t)Input::Key::Enter; + io.KeyMap[ImGuiKey_Backspace] = (uint32_t)Input::Key::Backspace; + io.KeyMap[ImGuiKey_Insert] = (uint32_t)Input::Key::Insert; + io.KeyMap[ImGuiKey_Delete] = (uint32_t)Input::Key::Del; + io.KeyMap[ImGuiKey_LeftArrow] = (uint32_t)Input::Key::Left; + io.KeyMap[ImGuiKey_RightArrow] = (uint32_t)Input::Key::Right; + io.KeyMap[ImGuiKey_UpArrow] = (uint32_t)Input::Key::Up; + io.KeyMap[ImGuiKey_DownArrow] = (uint32_t)Input::Key::Down; + io.KeyMap[ImGuiKey_PageUp] = (uint32_t)Input::Key::PageUp; + io.KeyMap[ImGuiKey_PageDown] = (uint32_t)Input::Key::PageDown; + io.KeyMap[ImGuiKey_Home] = (uint32_t)Input::Key::Home; + io.KeyMap[ImGuiKey_End] = (uint32_t)Input::Key::End; + io.KeyMap[ImGuiKey_CapsLock] = (uint32_t)Input::Key::CapsLock; + io.KeyMap[ImGuiKey_ScrollLock] = (uint32_t)Input::Key::ScrollLock; + io.KeyMap[ImGuiKey_NumLock] = (uint32_t)Input::Key::NumLock; + io.KeyMap[ImGuiKey_PrintScreen] = (uint32_t)Input::Key::PrintScreen; + io.KeyMap[ImGuiKey_Pause] = (uint32_t)Input::Key::Pause; + io.KeyMap[ImGuiKey_F1] = (uint32_t)Input::Key::F1; + io.KeyMap[ImGuiKey_F2] = (uint32_t)Input::Key::F2; + io.KeyMap[ImGuiKey_F3] = (uint32_t)Input::Key::F3; + io.KeyMap[ImGuiKey_F4] = (uint32_t)Input::Key::F4; + io.KeyMap[ImGuiKey_F5] = (uint32_t)Input::Key::F5; + io.KeyMap[ImGuiKey_F6] = (uint32_t)Input::Key::F6; + io.KeyMap[ImGuiKey_F7] = (uint32_t)Input::Key::F7; + io.KeyMap[ImGuiKey_F8] = (uint32_t)Input::Key::F8; + io.KeyMap[ImGuiKey_F9] = (uint32_t)Input::Key::F9; + io.KeyMap[ImGuiKey_F10] = (uint32_t)Input::Key::F10; + io.KeyMap[ImGuiKey_F11] = (uint32_t)Input::Key::F11; + io.KeyMap[ImGuiKey_F12] = (uint32_t)Input::Key::F12; + io.KeyMap[ImGuiKey_Keypad0] = (uint32_t)Input::Key::Keypad0; + io.KeyMap[ImGuiKey_Keypad1] = (uint32_t)Input::Key::Keypad1; + io.KeyMap[ImGuiKey_Keypad2] = (uint32_t)Input::Key::Keypad2; + io.KeyMap[ImGuiKey_Keypad3] = (uint32_t)Input::Key::Keypad3; + io.KeyMap[ImGuiKey_Keypad4] = (uint32_t)Input::Key::Keypad4; + io.KeyMap[ImGuiKey_Keypad5] = (uint32_t)Input::Key::Keypad5; + io.KeyMap[ImGuiKey_Keypad6] = (uint32_t)Input::Key::Keypad6; + io.KeyMap[ImGuiKey_Keypad7] = (uint32_t)Input::Key::Keypad7; + io.KeyMap[ImGuiKey_Keypad8] = (uint32_t)Input::Key::Keypad8; + io.KeyMap[ImGuiKey_Keypad9] = (uint32_t)Input::Key::Keypad9; + io.KeyMap[ImGuiKey_KeypadDivide] = (uint32_t)Input::Key::KeypadDivide; + io.KeyMap[ImGuiKey_KeypadMultiply] = (uint32_t)Input::Key::KeypadMultiply; + io.KeyMap[ImGuiKey_KeypadSubtract] = (uint32_t)Input::Key::KeypadSubtract; + io.KeyMap[ImGuiKey_KeypadAdd] = (uint32_t)Input::Key::KeypadAdd; + io.KeyMap[ImGuiKey_KeypadEnter] = (uint32_t)Input::Key::KeypadEnter; + io.KeyMap[ImGuiKey_LeftShift] = (uint32_t)Input::Key::LeftShift; + io.KeyMap[ImGuiKey_LeftCtrl] = (uint32_t)Input::Key::LeftControl; + io.KeyMap[ImGuiKey_LeftAlt] = (uint32_t)Input::Key::LeftAlt; + io.KeyMap[ImGuiKey_LeftSuper] = (uint32_t)Input::Key::LeftSuper; + io.KeyMap[ImGuiKey_RightShift] = (uint32_t)Input::Key::RightShift; + io.KeyMap[ImGuiKey_RightCtrl] = (uint32_t)Input::Key::RightControl; + io.KeyMap[ImGuiKey_RightAlt] = (uint32_t)Input::Key::RightAlt; + io.KeyMap[ImGuiKey_RightSuper] = (uint32_t)Input::Key::RightSuper; + io.KeyMap[ImGuiKey_Menu] = (uint32_t)Input::Key::Menu; + 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(mpDevice); + + // Create the program + mpProgram = GraphicsProgram::createFromFile(mpDevice, "Utils/UI/Gui.slang", "vsMain", "psMain"); + mpProgramVars = GraphicsVars::create(mpDevice, mpProgram->getReflector()); + mpPipelineState->setProgram(mpProgram); + + // 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 + ref 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"); + FALCOR_ASSERT(vertexCount > 0 && indexCount > 0); + uint32_t requiredVbSize = vertexCount * sizeof(ImDrawVert); + uint32_t requiredIbSize = indexCount * sizeof(uint16_t); + bool createVB = true; + bool createIB = true; - bool GuiImpl::addCheckboxes(const char label[], bool* pData, uint32_t numCheckboxes, bool sameLine) + if (mpVao) { - bool modified = false; - std::string labelString(std::string("##") + label + '0'); + FALCOR_ASSERT(mpVao->getVertexBuffer(0) && mpVao->getIndexBuffer()); + createVB = mpVao->getVertexBuffer(0)->getSize() < requiredVbSize; + createIB = mpVao->getIndexBuffer()->getSize() < requiredIbSize; - for (uint32_t i = 0; i < numCheckboxes - 1; ++i) + if (!createIB && !createVB) { - labelString[labelString.size() - 1] = '0' + static_cast(i); - modified |= addCheckbox(labelString.c_str(), pData[i], (!i) ? sameLine : true); + return; } + } - addCheckbox(label, pData[numCheckboxes - 1], true); + // Need to create a new VAO + std::vector> pVB(1); + pVB[0] = createVB + ? Buffer::create( + mpDevice, requiredVbSize + sizeof(ImDrawVert) * 1000, Buffer::BindFlags::Vertex, Buffer::CpuAccess::Write, nullptr + ) + : mpVao->getVertexBuffer(0); + ref pIB = createIB ? Buffer::create(mpDevice, requiredIbSize, Buffer::BindFlags::Index, Buffer::CpuAccess::Write, nullptr) + : mpVao->getIndexBuffer(); + mpVao = Vao::create(Vao::Topology::TriangleList, mpLayout, pVB, pIB, ResourceFormat::R16Uint); +} - return modified; - } +void GuiImpl::compileFonts() +{ + uint8_t* pFontData; + int32_t width, height; - void GuiImpl::setIoMouseEvents() + // Initialize font data + ImGui::GetIO().Fonts->GetTexDataAsAlpha8(&pFontData, &width, &height); + ref pTexture = Texture::create2D(mpDevice, 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) { - ImGuiIO& io = ImGui::GetIO(); - memcpy(io.MouseDown, mMouseEvents.buttonPressed, sizeof(mMouseEvents.buttonPressed)); + labelString[labelString.size() - 1] = '0' + static_cast(i); + modified |= addCheckbox(labelString.c_str(), pData[i], (!i) ? sameLine : true); } - void GuiImpl::resetMouseEvents() + 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 (size_t i = 0; i < std::size(mMouseEvents.buttonPressed); i++) { - for (size_t i = 0; i < std::size(mMouseEvents.buttonPressed); i++) + if (mMouseEvents.buttonReleased[i]) { - if (mMouseEvents.buttonReleased[i]) - { - mMouseEvents.buttonPressed[i] = mMouseEvents.buttonReleased[i] = false; - } + mMouseEvents.buttonPressed[i] = mMouseEvents.buttonReleased[i] = false; } } +} - bool GuiImpl::beginMenu(const char* name) - { - return ImGui::BeginMenu(name); - } +bool GuiImpl::beginMenu(const char* name) +{ + return ImGui::BeginMenu(name); +} - void GuiImpl::endMenu() - { - return ImGui::EndMenu(); - } +void GuiImpl::endMenu() +{ + return ImGui::EndMenu(); +} - bool GuiImpl::beginMainMenuBar() - { - bool isOpen = ImGui::BeginMainMenuBar(); - return isOpen; - } +bool GuiImpl::beginMainMenuBar() +{ + bool isOpen = ImGui::BeginMainMenuBar(); + return isOpen; +} - void GuiImpl::endMainMenuBar() - { - ImGui::EndMainMenuBar(); - } +void GuiImpl::endMainMenuBar() +{ + ImGui::EndMainMenuBar(); +} - bool GuiImpl::beginDropDownMenu(const char label[]) - { - return ImGui::BeginMenu(label); - } +bool GuiImpl::beginDropDownMenu(const char label[]) +{ + return ImGui::BeginMenu(label); +} - void GuiImpl::endDropDownMenu() - { - ImGui::EndMenu(); - } +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[], 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::addMenuItem(const char label[], bool& var, const char shortcut[]) +{ + return ImGui::MenuItem(label, shortcut, &var); +} - bool GuiImpl::pushWindow(const char label[], bool& open, uint2 size, uint2 pos, Gui::WindowFlags flags) +bool GuiImpl::pushWindow(const char label[], bool& open, uint2 size, uint2 pos, Gui::WindowFlags flags) +{ + bool allowClose = is_set(flags, Gui::WindowFlags::CloseButton); + if (allowClose) { - bool allowClose = is_set(flags, Gui::WindowFlags::CloseButton); - if (allowClose) + if (!is_set(flags, Gui::WindowFlags::ShowTitleBar)) { - if (!is_set(flags, Gui::WindowFlags::ShowTitleBar)) - { - logWarning("Asking for a close button on window '{}' but the ShowTitleBar flag is not set on the window. The window will not be able to display a close button.", label); - } + logWarning( + "Asking for a close button on window '{}' but the ShowTitleBar flag is not set on the window. The window will not be able " + "to display a close button.", + label + ); } - - ImVec2 fPos(pos.x * mScaleFactor, pos.y * mScaleFactor); - ImVec2 fSize(size.x * mScaleFactor, size.y * mScaleFactor); - 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(); + ImVec2 fPos(pos.x * mScaleFactor, pos.y * mScaleFactor); + ImVec2 fSize(size.x * mScaleFactor, size.y * mScaleFactor); + 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::setCurrentWindowPos(uint32_t x, uint32_t y) - { - ImGui::SetWindowPos({ static_cast(x), static_cast(y) }); - } +void GuiImpl::popWindow() +{ + ImGui::PopFont(); + ImGui::End(); +} - void GuiImpl::setCurrentWindowSize(uint32_t width, uint32_t height) - { - ImGui::SetWindowSize({ static_cast(width), static_cast(height) }); - } +void GuiImpl::setCurrentWindowPos(uint32_t x, uint32_t y) +{ + ImGui::SetWindowPos({static_cast(x), static_cast(y)}); +} - void GuiImpl::beginColumns(uint32_t numColumns) - { - ImGui::Columns(numColumns); - } +void GuiImpl::setCurrentWindowSize(uint32_t width, uint32_t height) +{ + ImGui::SetWindowSize({static_cast(width), static_cast(height)}); +} - void GuiImpl::nextColumn() - { - ImGui::NextColumn(); - } +void GuiImpl::beginColumns(uint32_t numColumns) +{ + ImGui::Columns(numColumns); +} - 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++; - return visible; - } +void GuiImpl::nextColumn() +{ + ImGui::NextColumn(); +} - void GuiImpl::endGroup() - { - FALCOR_ASSERT(mGroupStackSize >= 1); - mGroupStackSize--; - if (mGroupStackSize) ImGui::TreePop(); - } +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++; + return visible; +} - void GuiImpl::indent(float i) - { - ImGui::Indent(i); - } +void GuiImpl::endGroup() +{ + FALCOR_ASSERT(mGroupStackSize >= 1); + mGroupStackSize--; + if (mGroupStackSize) + ImGui::TreePop(); +} - void GuiImpl::addSeparator(uint32_t count) - { - for (uint32_t i = 0; i < count; i++) ImGui::Separator(); - } +void GuiImpl::indent(float i) +{ + ImGui::Indent(i); +} - void GuiImpl::addDummyItem(const char label[], const float2& size, bool sameLine) - { - if (sameLine) ImGui::SameLine(); - ImGui::PushID(label); - ImGui::Dummy({ size.x, size.y }); - ImGui::PopID(); - } +void GuiImpl::addSeparator(uint32_t count) +{ + for (uint32_t i = 0; i < count; i++) + ImGui::Separator(); +} - void GuiImpl::addRect(const float2& size, const float4& color, bool filled, bool sameLine) - { - if (sameLine) ImGui::SameLine(); +void GuiImpl::addDummyItem(const char label[], const float2& size, bool sameLine) +{ + if (sameLine) + ImGui::SameLine(); + ImGui::PushID(label); + ImGui::Dummy({size.x, size.y}); + ImGui::PopID(); +} - 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 }; +void GuiImpl::addRect(const float2& size, const float4& color, bool filled, bool sameLine) +{ + if (sameLine) + ImGui::SameLine(); - if (filled) - { - ImGui::GetWindowDrawList()->AddRectFilled(ImGui::GetCursorScreenPos(), bottomLeft, ImGui::ColorConvertFloat4ToU32(rectColor)); - } - else - { - ImGui::GetWindowDrawList()->AddRect(ImGui::GetCursorScreenPos(), bottomLeft, ImGui::ColorConvertFloat4ToU32(rectColor)); - } - } + 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}; - bool GuiImpl::addDropdown(const char label[], const Gui::DropdownList& values, uint32_t& var, bool sameLine) + if (filled) + { + ImGui::GetWindowDrawList()->AddRectFilled(ImGui::GetCursorScreenPos(), bottomLeft, ImGui::ColorConvertFloat4ToU32(rectColor)); + } + else { - if (values.size() == 0) return false; + ImGui::GetWindowDrawList()->AddRect(ImGui::GetCursorScreenPos(), bottomLeft, ImGui::ColorConvertFloat4ToU32(rectColor)); + } +} - if (sameLine) ImGui::SameLine(); - // Check if we need to update the currentItem - const auto& iter = mDropDownValues.find(label); - int curItem = -1; - if ((iter == mDropDownValues.end()) || (iter->second.lastVal != var)) +bool GuiImpl::addDropdown(const char label[], const Gui::DropdownList& values, uint32_t& var, bool sameLine) +{ + if (values.size() == 0) + return false; + + if (sameLine) + ImGui::SameLine(); + // Check if we need to update the currentItem + const auto& iter = mDropDownValues.find(label); + int curItem = -1; + if ((iter == mDropDownValues.end()) || (iter->second.lastVal != var)) + { + // Search the current val + for (uint32_t i = 0; i < values.size(); i++) { - // Search the current val - for (uint32_t i = 0; i < values.size(); i++) + if (values[i].value == var) { - if (values[i].value == var) - { - curItem = i; - mDropDownValues[label].currentItem = i; - break; - } + 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'; - auto 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) + else { - if (sameLine) ImGui::SameLine(); - return ImGui::Button(label); + curItem = mDropDownValues[label].currentItem; } - bool GuiImpl::addRadioButtons(const Gui::RadioButtonGroup& buttons, uint32_t& activeID) + std::string comboStr; + for (const auto& v : values) { - auto oldValue = activeID; + comboStr += v.label + '\0'; + } + comboStr += '\0'; + auto 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; +} - for (const auto& button : buttons) - { - if (button.sameLine) ImGui::SameLine(); - ImGui::RadioButton(button.label.c_str(), (int*)&activeID, button.buttonID); - } +bool GuiImpl::addButton(const char label[], bool sameLine) +{ + if (sameLine) + ImGui::SameLine(); + return ImGui::Button(label); +} - return oldValue != activeID; - } +bool GuiImpl::addRadioButtons(const Gui::RadioButtonGroup& buttons, uint32_t& activeID) +{ + auto oldValue = activeID; - bool GuiImpl::addDirectionWidget(const char label[], float3& direction) + for (const auto& button : buttons) { - float3 dir = glm::normalize(direction); - bool b = addVecVar(label, dir, -1.f, 1.f, 0.001f, false, "%.3f"); - if (b) direction = glm::normalize(dir); - return b; + if (button.sameLine) + ImGui::SameLine(); + ImGui::RadioButton(button.label.c_str(), (int*)&activeID, button.buttonID); } - bool GuiImpl::addCheckbox(const char label[], bool& var, bool sameLine) - { - if (sameLine) ImGui::SameLine(); - return ImGui::Checkbox(label, &var); - } + return oldValue != activeID; +} - 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; - } +bool GuiImpl::addDirectionWidget(const char label[], float3& direction) +{ + float3 dir = normalize(direction); + bool b = addVecVar(label, dir, -1.f, 1.f, 0.001f, false, "%.3f"); + if (b) + direction = normalize(dir); + return b; +} - template - bool GuiImpl::addBoolVecVar(const char label[], T& var, bool sameLine) +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, &var[0], 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) { - return addCheckboxes(label, glm::value_ptr(var), var.length(), sameLine); + ImGui::SetDragDropPayload(dataLabel, payloadString.data(), payloadString.size() * sizeof(payloadString[0]), ImGuiCond_Once); + ImGui::EndDragDropSource(); } + return b; +} - bool GuiImpl::addDragDropSource(const char label[], const char dataLabel[], const std::string& payloadString) +bool GuiImpl::addDragDropDest(const char dataLabel[], std::string& payloadString) +{ + bool b = false; + if (ImGui::BeginDragDropTarget()) { - if (ImGui::IsItemHovered() && (ImGui::IsMouseClicked(0) || ImGui::IsMouseClicked(1))) ImGui::SetWindowFocus(); - if (!(ImGui::IsWindowFocused())) return false; - bool b = ImGui::BeginDragDropSource(ImGuiDragDropFlags_SourceAllowNullID); + auto dragDropPayload = ImGui::AcceptDragDropPayload(dataLabel); + b = dragDropPayload && dragDropPayload->IsDataType(dataLabel) && (dragDropPayload->Data != nullptr); if (b) { - ImGui::SetDragDropPayload(dataLabel, payloadString.data(), payloadString.size() * sizeof(payloadString[0]), ImGuiCond_Once); - ImGui::EndDragDropSource(); + payloadString.resize(dragDropPayload->DataSize); + std::memcpy(&payloadString.front(), dragDropPayload->Data, dragDropPayload->DataSize); } - return b; + + ImGui::EndDragDropTarget(); } - 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); - } + return b; +} - ImGui::EndDragDropTarget(); - } +void GuiImpl::addText(const char text[], bool sameLine) +{ + if (sameLine) + ImGui::SameLine(); + ImGui::TextUnformatted(text); +} - return b; - } +void GuiImpl::addTextWrapped(const char text[]) +{ + ImGui::TextWrapped("%s", text); +} - void GuiImpl::addText(const char text[], bool sameLine) +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) { - if (sameLine) ImGui::SameLine(); - ImGui::TextUnformatted(text); + const ImGuiInputTextFlags inputTextFlags = ImGuiInputTextFlags_EnterReturnsTrue | ImGuiInputTextFlags_CtrlEnterForNewLine; + return ImGui::InputTextMultiline(label, buf, bufSize, ImVec2(-1.0f, ImGui::GetTextLineHeight() * lineCount), inputTextFlags); } - - void GuiImpl::addTextWrapped(const char text[]) + else { - ImGui::TextWrapped("%s", text); + return ImGui::InputText(label, buf, bufSize, ImGuiInputTextFlags_EnterReturnsTrue); } - 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 (fitWindow) + ImGui::PopItemWidth(); +} - 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); - } +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); - if (fitWindow) ImGui::PopItemWidth(); - } + bool result = addTextbox(label, buf, maxSize, lineCount, flags); + text = std::string(buf); + return result; +} - 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 GuiImpl::addMultiTextbox(const char label[], const std::vector& textLabels, std::vector& textEntries) +{ + static uint32_t sIdOffset = 0; // TODO: REMOVEGLOBAL + bool result = false; - bool result = addTextbox(label, buf, maxSize, lineCount, flags); - text = std::string(buf); - return result; + for (uint32_t i = 0; i < textEntries.size(); ++i) + { + result |= addTextbox(std::string(textLabels[i] + "##" + std::to_string(sIdOffset)).c_str(), textEntries[i]); } - bool GuiImpl::addMultiTextbox(const char label[], const std::vector& textLabels, std::vector& textEntries) + return addButton(label) | result; +} + +void GuiImpl::addTooltip(const char tip[], bool sameLine) +{ + if (sameLine) + ImGui::SameLine(); + ImGui::TextDisabled("(?)"); + if (ImGui::IsItemHovered()) { - static uint32_t sIdOffset = 0; // TODO: REMOVEGLOBAL - bool result = false; + ImGui::BeginTooltip(); + ImGui::PushTextWrapPos(450.0f); + ImGui::TextUnformatted(tip); + ImGui::PopTextWrapPos(); + ImGui::EndTooltip(); + } +} - for (uint32_t i = 0; i < textEntries.size(); ++i) - { - result |= addTextbox(std::string(textLabels[i] + "##" + std::to_string(sIdOffset)).c_str(), textEntries[i]); - } +bool GuiImpl::addRgbColor(const char label[], float3& var, bool sameLine) +{ + if (sameLine) + ImGui::SameLine(); + return ImGui::ColorEdit3(label, &var[0]); +} - return addButton(label) | result; - } +bool GuiImpl::addRgbaColor(const char label[], float4& var, bool sameLine) +{ + if (sameLine) + ImGui::SameLine(); + return ImGui::ColorEdit4(label, &var[0]); +} - void GuiImpl::addTooltip(const char tip[], bool sameLine) +const Texture* GuiImpl::loadImage(const std::filesystem::path& path) +{ + auto it = mLoadedImages.find(path); + if (it != mLoadedImages.end()) + return it->second.get(); + + ref pTex = Texture::createFromFile(mpDevice, path, false, true); + if (!pTex) + throw RuntimeError("Failed to load GUI image from '{}'.", path); + return mLoadedImages.emplace(path, pTex).first->second.get(); +} + +void GuiImpl::addImage(const char label[], const Texture* pTex, float2 size, bool maintainRatio, bool sameLine) +{ + FALCOR_ASSERT(pTex); + if (all(size == float2(0))) { - if (sameLine) ImGui::SameLine(); - ImGui::TextDisabled("(?)"); - if (ImGui::IsItemHovered()) - { - ImGui::BeginTooltip(); - ImGui::PushTextWrapPos(450.0f); - ImGui::TextUnformatted(tip); - ImGui::PopTextWrapPos(); - ImGui::EndTooltip(); - } + ImVec2 windowSize = ImGui::GetWindowSize(); + size = {windowSize.x, windowSize.y}; } - bool GuiImpl::addRgbColor(const char label[], float3& var, bool sameLine) + ImGui::PushID(label); + if (sameLine) + ImGui::SameLine(); + float aspectRatio = maintainRatio ? (static_cast(pTex->getHeight()) / static_cast(pTex->getWidth())) : 1.0f; + ImGui::Image((ImTextureID)(pTex), {size.x, maintainRatio ? size.x * aspectRatio : size.y}); + ImGui::PopID(); +} + +bool GuiImpl::addImageButton(const char label[], const Texture* pTex, float2 size, bool maintainRatio, bool sameLine) +{ + FALCOR_ASSERT(pTex); + if (sameLine) + ImGui::SameLine(); + float aspectRatio = maintainRatio ? (static_cast(pTex->getHeight()) / static_cast(pTex->getWidth())) : 1.0f; + return ImGui::ImageButton((ImTextureID)(pTex), {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 = std::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 constexpr (std::is_same::value) { - if (sameLine) ImGui::SameLine(); - return ImGui::ColorEdit3(label, glm::value_ptr(var)); + return addScalarVarHelper(label, var, ImGuiDataType_S32, minVal, maxVal, step, sameLine, displayFormat); } - - bool GuiImpl::addRgbaColor(const char label[], float4& var, bool sameLine) + else if constexpr (std::is_same::value) { - if (sameLine) ImGui::SameLine(); - return ImGui::ColorEdit4(label, glm::value_ptr(var)); + return addScalarVarHelper(label, var, ImGuiDataType_U32, minVal, maxVal, step, sameLine, displayFormat); } - - void GuiImpl::addImage(const char label[], const Texture::SharedPtr& pTex, float2 size, bool maintainRatio, bool sameLine) + else if constexpr (std::is_same::value) { - FALCOR_ASSERT(pTex); - if (size == float2(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(); + return addScalarVarHelper(label, var, ImGuiDataType_S64, minVal, maxVal, step, sameLine, displayFormat); } - - bool GuiImpl::addImageButton(const char label[], const Texture::SharedPtr& pTex, float2 size, bool maintainRatio, bool sameLine) + else if constexpr (std::is_same::value) { - FALCOR_ASSERT(pTex); - 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 }); + return addScalarVarHelper(label, var, ImGuiDataType_U64, minVal, maxVal, step, sameLine, displayFormat); } - - template - bool addScalarVarHelper(const char label[], T& var, ImGuiDataType_ imguiType, T minVal, T maxVal, float step, bool sameLine, const char* displayFormat) + else if constexpr (std::is_same::value) { - 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; + return addScalarVarHelper(label, var, ImGuiDataType_Float, minVal, maxVal, step, sameLine, displayFormat); } - - template - bool GuiImpl::addScalarVar(const char label[], T& var, T minVal, T maxVal, float step, bool sameLine, const char* displayFormat) + else if constexpr (std::is_same::value) { - if constexpr (std::is_same::value) - { - return addScalarVarHelper(label, var, ImGuiDataType_S32, minVal, maxVal, step, sameLine, displayFormat); - } - else if constexpr (std::is_same::value) - { - return addScalarVarHelper(label, var, ImGuiDataType_U32, minVal, maxVal, step, sameLine, displayFormat); - } - else if constexpr (std::is_same::value) - { - return addScalarVarHelper(label, var, ImGuiDataType_S64, minVal, maxVal, step, sameLine, displayFormat); - } - else if constexpr (std::is_same::value) - { - return addScalarVarHelper(label, var, ImGuiDataType_U64, minVal, maxVal, step, sameLine, displayFormat); - } - else if constexpr (std::is_same::value) - { - return addScalarVarHelper(label, var, ImGuiDataType_Float, minVal, maxVal, step, sameLine, displayFormat); - } - else if constexpr (std::is_same::value) - { - return addScalarVarHelper(label, var, ImGuiDataType_U64, minVal, maxVal, step, sameLine, displayFormat); - } - else if constexpr (std::is_same::value) - { - return addScalarVarHelper(label, var, ImGuiDataType_Double, minVal, maxVal, step, sameLine, displayFormat); - } - else - { - static_assert(!sizeof(T), "Unsupported data type"); - } + return addScalarVarHelper(label, var, ImGuiDataType_U64, minVal, maxVal, step, sameLine, displayFormat); } - - template - bool addScalarSliderHelper(const char label[], T& var, ImGuiDataType_ imguiType, T minVal, T maxVal, bool sameLine, const char* displayFormat) + else if constexpr (std::is_same::value) { - ImGui::PushItemWidth(200); - if (sameLine) ImGui::SameLine(); - bool b = ImGui::SliderScalar(label, imguiType, &var, &minVal, &maxVal, displayFormat); - ImGui::PopItemWidth(); - return b; + return addScalarVarHelper(label, var, ImGuiDataType_Double, minVal, maxVal, step, sameLine, displayFormat); } - - template - bool GuiImpl::addScalarSlider(const char label[], T& var, T minVal, T maxVal, bool sameLine, const char* displayFormat) + else { - if constexpr (std::is_same::value) - { - return addScalarSliderHelper(label, var, ImGuiDataType_S32, minVal, maxVal, sameLine, displayFormat); - } - else if constexpr (std::is_same::value) - { - return addScalarSliderHelper(label, var, ImGuiDataType_U32, minVal, maxVal, sameLine, displayFormat); - } - else if constexpr (std::is_same::value) - { - return addScalarSliderHelper(label, var, ImGuiDataType_S64, minVal, maxVal, sameLine, displayFormat); - } - else if constexpr (std::is_same::value) - { - return addScalarSliderHelper(label, var, ImGuiDataType_U64, minVal, maxVal, sameLine, displayFormat); - } - else if constexpr (std::is_same::value) - { - return addScalarSliderHelper(label, var, ImGuiDataType_Float, minVal, maxVal, sameLine, displayFormat); - } - else if constexpr (std::is_same::value) - { - return addScalarSliderHelper(label, var, ImGuiDataType_Double, minVal, maxVal, sameLine, displayFormat); - } - else - { - static_assert(!sizeof(T), "Unsupported data type"); - } + static_assert(!sizeof(T), "Unsupported data type"); } +} - 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) +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 constexpr (std::is_same::value) { - 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; + return addScalarSliderHelper(label, var, ImGuiDataType_S32, minVal, maxVal, sameLine, displayFormat); } - - 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) + else if constexpr (std::is_same::value) { - if constexpr (std::is_same::value) - { - return addVecVarHelper(label, var, ImGuiDataType_S32, minVal, maxVal, step, sameLine, displayFormat); - } - else if constexpr (std::is_same::value) - { - return addVecVarHelper(label, var, ImGuiDataType_U32, minVal, maxVal, step, sameLine, displayFormat); - } - else if constexpr (std::is_same::value) - { - return addVecVarHelper(label, var, ImGuiDataType_S64, minVal, maxVal, step, sameLine, displayFormat); - } - else if constexpr (std::is_same::value) - { - return addVecVarHelper(label, var, ImGuiDataType_U64, minVal, maxVal, step, sameLine, displayFormat); - } - else if constexpr (std::is_same::value) - { - return addVecVarHelper(label, var, ImGuiDataType_Float, minVal, maxVal, step, sameLine, displayFormat); - } - else if constexpr (std::is_same::value) - { - return addVecVarHelper(label, var, ImGuiDataType_U64, minVal, maxVal, step, sameLine, displayFormat); - } - else - { - static_assert(!sizeof(T), "Unsupported data type"); - } + return addScalarSliderHelper(label, var, ImGuiDataType_U32, minVal, maxVal, sameLine, displayFormat); } - - 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) + else if constexpr (std::is_same::value) { - 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; + return addScalarSliderHelper(label, var, ImGuiDataType_S64, minVal, maxVal, sameLine, displayFormat); } - - template - bool GuiImpl::addVecSlider(const char label[], T& var, typename T::value_type minVal, typename T::value_type maxVal, bool sameLine, const char* displayFormat) + else if constexpr (std::is_same::value) { - if constexpr (std::is_same::value) - { - return addVecSliderHelper(label, var, ImGuiDataType_S32, minVal, maxVal, sameLine, displayFormat); - } - else if constexpr (std::is_same::value) - { - return addVecSliderHelper(label, var, ImGuiDataType_U32, minVal, maxVal, sameLine, displayFormat); - } - else if constexpr (std::is_same::value) - { - return addVecSliderHelper(label, var, ImGuiDataType_S64, minVal, maxVal, sameLine, displayFormat); - } - else if constexpr (std::is_same::value) - { - return addVecSliderHelper(label, var, ImGuiDataType_U64, minVal, maxVal, sameLine, displayFormat); - } - else if constexpr (std::is_same::value) - { - return addVecSliderHelper(label, var, ImGuiDataType_Float, minVal, maxVal, sameLine, displayFormat); - } - else - { - static_assert(!sizeof(T), "Unsupported data type"); - } + return addScalarSliderHelper(label, var, ImGuiDataType_U64, minVal, maxVal, sameLine, displayFormat); } - - template - bool GuiImpl::addMatrixVar(const char label[], rmcv::matrix& var, float minVal, float maxVal, bool sameLine) + else if constexpr (std::is_same::value) { - using MatrixType = rmcv::matrix; - 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.getColCount()); ++i) - { - std::string& stringToDisplay = hiddenLabelString; - hiddenLabelString[hiddenLabelString.size() - 2] = '0' + static_cast(i); - if (i == var.getColCount() - 1) - { - stringToDisplay = labelString; - } + return addScalarSliderHelper(label, var, ImGuiDataType_Float, minVal, maxVal, sameLine, displayFormat); + } + else if constexpr (std::is_same::value) + { + return addScalarSliderHelper(label, var, ImGuiDataType_Double, minVal, maxVal, sameLine, displayFormat); + } + else + { + static_assert(!sizeof(T), "Unsupported data type"); + } +} - // Just to keep the parity with GLM - auto col = var.getCol(i); - b |= addVecVar(stringToDisplay.c_str(), col, minVal, maxVal, 0.001f, sameLine); - var.setCol(i, col); +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, &var[0], var.length(), step, &minVal, &maxVal, displayFormat); + var = clamp(var, T(minVal), T(maxVal)); + ImGui::PopItemWidth(); + return b; +} - 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.getColCount()); - 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; +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 constexpr (std::is_same::value) + { + return addVecVarHelper(label, var, ImGuiDataType_S32, minVal, maxVal, step, sameLine, displayFormat); } - - 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) + else if constexpr (std::is_same::value) { - ImVec2 imSize{ (float)width, (float)height }; - ImGui::PlotLines(label, func, pUserData, (int32_t)sampleCount, sampleOffset, nullptr, yMin, yMax, imSize); + return addVecVarHelper(label, var, ImGuiDataType_U32, minVal, maxVal, step, sameLine, displayFormat); } - - Gui::Gui(std::shared_ptr pDevice, uint32_t width, uint32_t height, float scaleFactor) + else if constexpr (std::is_same::value) + { + return addVecVarHelper(label, var, ImGuiDataType_S64, minVal, maxVal, step, sameLine, displayFormat); + } + else if constexpr (std::is_same::value) + { + return addVecVarHelper(label, var, ImGuiDataType_U64, minVal, maxVal, step, sameLine, displayFormat); + } + else if constexpr (std::is_same::value) { - mpWrapper = std::make_unique(std::move(pDevice), scaleFactor); + return addVecVarHelper(label, var, ImGuiDataType_Float, minVal, maxVal, step, sameLine, displayFormat); + } + else if constexpr (std::is_same::value) + { + return addVecVarHelper(label, var, ImGuiDataType_U64, minVal, maxVal, step, sameLine, displayFormat); + } + else + { + static_assert(!sizeof(T), "Unsupported data type"); + } +} - // Add the default font - addFont("", getRuntimeDirectory() / "data/framework/fonts/trebucbd.ttf"); - addFont("monospace", getRuntimeDirectory() / "data/framework/fonts/consolab.ttf"); - setActiveFont(""); +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, &var[0], var.length(), &minVal, &maxVal, displayFormat); + ImGui::PopItemWidth(); + return b; +} - onWindowResize(width, height); +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 constexpr (std::is_same::value) + { + return addVecSliderHelper(label, var, ImGuiDataType_S32, minVal, maxVal, sameLine, displayFormat); } - - Gui::~Gui() + else if constexpr (std::is_same::value) { - mpWrapper.reset(); - ImGui::DestroyContext(); + return addVecSliderHelper(label, var, ImGuiDataType_U32, minVal, maxVal, sameLine, displayFormat); } - - float4 Gui::pickUniqueColor(const std::string& key) + else if constexpr (std::is_same::value) { - union hashedValue - { - size_t st; - int32_t i32[2]; - }; - hashedValue color; - color.st = std::hash()(key); - - return float4(color.i32[0] % 1000 / 2000.0f, color.i32[1] % 1000 / 2000.0f, (color.i32[0] * color.i32[1]) % 1000 / 2000.0f, 1.0f); + return addVecSliderHelper(label, var, ImGuiDataType_S64, minVal, maxVal, sameLine, displayFormat); } - - void Gui::addFont(const std::string& name, const std::filesystem::path& path) + else if constexpr (std::is_same::value) + { + return addVecSliderHelper(label, var, ImGuiDataType_U64, minVal, maxVal, sameLine, displayFormat); + } + else if constexpr (std::is_same::value) { - float size = 14.0f * mpWrapper->mScaleFactor; - ImFont* pFont = ImGui::GetIO().Fonts->AddFontFromFileTTF(path.string().c_str(), size); - if (!pFont) - throw RuntimeError("Failed to load font from '{}'.", path); - mpWrapper->mFontMap[name] = pFont; - mpWrapper->compileFonts(); + return addVecSliderHelper(label, var, ImGuiDataType_Float, minVal, maxVal, sameLine, displayFormat); } + else + { + static_assert(!sizeof(T), "Unsupported data type"); + } +} + +template +bool GuiImpl::addMatrixVar(const char label[], math::matrix& var, float minVal, float maxVal, bool sameLine) +{ + using MatrixType = math::matrix; + std::string labelString(label); + std::string hiddenLabelString("##"); + hiddenLabelString += labelString + "[0]"; + + ImVec2 topLeft = ImGui::GetCursorScreenPos(); + ImVec2 bottomRight; - void Gui::setActiveFont(const std::string& font) + bool b = false; + + for (uint32_t i = 0; i < static_cast(var.getColCount()); ++i) { - const auto& it = mpWrapper->mFontMap.find(font); - if (it == mpWrapper->mFontMap.end()) + std::string& stringToDisplay = hiddenLabelString; + hiddenLabelString[hiddenLabelString.size() - 2] = '0' + static_cast(i); + if (i == var.getColCount() - 1) { - logWarning("Can't find a font named '{}'.", font); - mpWrapper->mpActiveFont = nullptr; + stringToDisplay = labelString; } - mpWrapper->mpActiveFont = it->second; - } - ImFont* Gui::getFont(std::string f) - { - if (f.size()) return mpWrapper->mFontMap.at(f); - else return mpWrapper->mpActiveFont; + // Just to keep the parity with GLM + auto col = var.getCol(i); + b |= addVecVar(stringToDisplay.c_str(), col, minVal, maxVal, 0.001f, sameLine); + var.setCol(i, col); + + 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.getColCount()); + 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(ref pDevice, uint32_t width, uint32_t height, float scaleFactor) +{ + mpWrapper = std::make_unique(pDevice, scaleFactor); + + // Add the default font + addFont("", getRuntimeDirectory() / "data/framework/fonts/trebucbd.ttf"); + addFont("monospace", getRuntimeDirectory() / "data/framework/fonts/consolab.ttf"); + setActiveFont(""); + + onWindowResize(width, height); +} + +Gui::~Gui() +{ + ImGui::SetCurrentContext(mpWrapper->mpContext); + mpWrapper.reset(); + ImGui::DestroyContext(); +} - void Gui::beginFrame() +float4 Gui::pickUniqueColor(const std::string& key) +{ + union hashedValue { - ImGui::NewFrame(); - } + size_t st; + int32_t i32[2]; + }; + hashedValue color; + color.st = std::hash()(key); + + return float4(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::filesystem::path& path) +{ + float size = 14.0f * mpWrapper->mScaleFactor; + ImFont* pFont = ImGui::GetIO().Fonts->AddFontFromFileTTF(path.string().c_str(), size); + if (!pFont) + throw RuntimeError("Failed to load font from '{}'.", path); + mpWrapper->mFontMap[name] = pFont; + mpWrapper->compileFonts(); +} - void Gui::setGlobalGuiScaling(float scale) +void Gui::setActiveFont(const std::string& font) +{ + const auto& it = mpWrapper->mFontMap.find(font); + if (it == mpWrapper->mFontMap.end()) { - ImGuiIO& io = ImGui::GetIO(); - io.FontGlobalScale = scale; - ImGui::GetStyle().ScaleAllSizes(scale); + logWarning("Can't find a font named '{}'.", font); + mpWrapper->mpActiveFont = nullptr; } + mpWrapper->mpActiveFont = it->second; +} - void Gui::render(RenderContext* pContext, const Fbo::SharedPtr& pFbo, float elapsedTime) - { - while (mpWrapper->mGroupStackSize) mpWrapper->endGroup(); +ImFont* Gui::getFont(std::string f) +{ + if (f.size()) + return mpWrapper->mFontMap.at(f); + else + return mpWrapper->mpActiveFont; +} - // Set the mouse state - mpWrapper->setIoMouseEvents(); +void Gui::beginFrame() +{ + ImGui::SetCurrentContext(mpWrapper->mpContext); + ImGui::NewFrame(); +} - ImGui::Render(); - ImDrawData* pDrawData = ImGui::GetDrawData(); +void Gui::render(RenderContext* pContext, const ref& pFbo, float elapsedTime) +{ + ImGui::SetCurrentContext(mpWrapper->mpContext); - mpWrapper->resetMouseEvents(); + while (mpWrapper->mGroupStackSize) + mpWrapper->endGroup(); - if (pDrawData->CmdListsCount > 0) - { - // Update the VAO - mpWrapper->createVao(pDrawData->TotalVtxCount, pDrawData->TotalIdxCount); - mpWrapper->mpPipelineState->setVao(mpWrapper->mpVao); + // Set the mouse state + mpWrapper->setIoMouseEvents(); - // 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); + ImGui::Render(); + ImDrawData* pDrawData = ImGui::GetDrawData(); - 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++) + mpWrapper->resetMouseEvents(); + + if (pDrawData->CmdListsCount > 0) + { + // 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 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->setSrv(mpWrapper->mGuiImageLoc, reinterpret_cast(pCmd->TextureId)->getSRV()); + mpWrapper->mpProgramVars->getRootVar()["PerFrameCB"]["useGuiImage"] = true; + } + else { - 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->setSrv(mpWrapper->mGuiImageLoc, (mpWrapper->mpImages[reinterpret_cast(pCmd->TextureId) - 1])->getSRV()); - mpWrapper->mpProgramVars["PerFrameCB"]["useGuiImage"] = true; - } - else - { - mpWrapper->mpProgramVars->setSrv(mpWrapper->mGuiImageLoc, nullptr); - 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; + mpWrapper->mpProgramVars->setSrv(mpWrapper->mGuiImageLoc, nullptr); + mpWrapper->mpProgramVars->getRootVar()["PerFrameCB"]["useGuiImage"] = false; } - vtxOffset += pCmdList->VtxBuffer.Size; + 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; + // Prepare for the next frame + ImGuiIO& io = ImGui::GetIO(); + io.DeltaTime = elapsedTime; + mpWrapper->mGroupStackSize = 0; +} + +void Gui::onWindowResize(uint32_t width, uint32_t height) +{ + ImGui::SetCurrentContext(mpWrapper->mpContext); + ImGuiIO& io = ImGui::GetIO(); + io.DisplaySize.x = (float)width; + io.DisplaySize.y = (float)height; + auto var = mpWrapper->mpProgramVars->getRootVar()["PerFrameCB"]; #ifdef FALCOR_FLIP_Y - mpWrapper->mpProgramVars["PerFrameCB"]["scale"] = 2.0f / float2(io.DisplaySize.x, io.DisplaySize.y); - mpWrapper->mpProgramVars["PerFrameCB"]["offset"] = float2(-1.0f); + var["scale"] = 2.0f / float2(io.DisplaySize.x, io.DisplaySize.y); + var["offset"] = float2(-1.0f); #else - mpWrapper->mpProgramVars["PerFrameCB"]["scale"] = 2.0f / float2(io.DisplaySize.x, -io.DisplaySize.y); - mpWrapper->mpProgramVars["PerFrameCB"]["offset"] = float2(-1.0f, 1.0f); + var["scale"] = 2.0f / float2(io.DisplaySize.x, -io.DisplaySize.y); + var["offset"] = float2(-1.0f, 1.0f); #endif - } +} - bool Gui::onMouseEvent(const MouseEvent& event) +bool Gui::onMouseEvent(const MouseEvent& event) +{ + ImGui::SetCurrentContext(mpWrapper->mpContext); + ImGuiIO& io = ImGui::GetIO(); + switch (event.type) { - ImGuiIO& io = ImGui::GetIO(); - switch (event.type) + case MouseEvent::Type::ButtonDown: + case MouseEvent::Type::ButtonUp: + { + bool isDown = event.type == MouseEvent::Type::ButtonDown; + switch (event.button) { - case MouseEvent::Type::ButtonDown: - case MouseEvent::Type::ButtonUp: - { - bool isDown = event.type == MouseEvent::Type::ButtonDown; - switch (event.button) - { - case Input::MouseButton::Left: - mpWrapper->mMouseEvents.buttonPressed[0] = isDown; - break; - case Input::MouseButton::Right: - mpWrapper->mMouseEvents.buttonPressed[1] = isDown; - break; - case Input::MouseButton::Middle: - mpWrapper->mMouseEvents.buttonPressed[2] = isDown; - break; - default: - break; - } - } + case Input::MouseButton::Left: + mpWrapper->mMouseEvents.buttonPressed[0] = isDown; + break; + case Input::MouseButton::Right: + mpWrapper->mMouseEvents.buttonPressed[1] = isDown; break; - case MouseEvent::Type::Move: - io.MousePos.x = event.pos.x * io.DisplaySize.x; - io.MousePos.y = event.pos.y * io.DisplaySize.y; + case Input::MouseButton::Middle: + mpWrapper->mMouseEvents.buttonPressed[2] = isDown; break; - case MouseEvent::Type::Wheel: - io.MouseWheel += event.wheelDelta.y; + default: break; } - - return io.WantCaptureMouse; + } + 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; } - bool Gui::onKeyboardEvent(const KeyboardEvent& event) - { - ImGuiIO& io = ImGui::GetIO(); + return io.WantCaptureMouse; +} - if (event.type == KeyboardEvent::Type::Input) - { - std::string u8str = utf32ToUtf8(event.codepoint); - io.AddInputCharactersUTF8(u8str.c_str()); +bool Gui::onKeyboardEvent(const KeyboardEvent& event) +{ + ImGui::SetCurrentContext(mpWrapper->mpContext); + ImGuiIO& io = ImGui::GetIO(); - // Gui consumes keyboard input - return true; - } - else - { - uint32_t key = (uint32_t)(event.key == Input::Key::KeypadEnter ? Input::Key::Enter : event.key); + if (event.type == KeyboardEvent::Type::Input) + { + std::string u8str = utf32ToUtf8(event.codepoint); + io.AddInputCharactersUTF8(u8str.c_str()); - switch (event.type) - { - case KeyboardEvent::Type::KeyRepeated: - case KeyboardEvent::Type::KeyPressed: - io.KeysDown[key] = true; - break; - case KeyboardEvent::Type::KeyReleased: - io.KeysDown[key] = false; - break; - default: - FALCOR_UNREACHABLE(); - } + // Gui consumes keyboard input + return true; + } + else + { + uint32_t key = (uint32_t)(event.key == Input::Key::KeypadEnter ? Input::Key::Enter : event.key); - io.KeyCtrl = event.hasModifier(Input::Modifier::Ctrl); - io.KeyAlt = event.hasModifier(Input::Modifier::Alt); - io.KeyShift = event.hasModifier(Input::Modifier::Shift); - io.KeySuper = false; - return io.WantCaptureKeyboard; + switch (event.type) + { + case KeyboardEvent::Type::KeyRepeated: + case KeyboardEvent::Type::KeyPressed: + io.KeysDown[key] = true; + break; + case KeyboardEvent::Type::KeyReleased: + io.KeysDown[key] = false; + break; + default: + FALCOR_UNREACHABLE(); } - } - Gui::Group Gui::Widgets::group(const std::string& label, bool beginExpanded) - { - return Group(mpGui, label, beginExpanded); + io.KeyCtrl = event.hasModifier(Input::Modifier::Ctrl); + io.KeyAlt = event.hasModifier(Input::Modifier::Alt); + io.KeyShift = event.hasModifier(Input::Modifier::Shift); + io.KeySuper = false; + return io.WantCaptureKeyboard; } +} - void Gui::Widgets::indent(float i) - { - if (mpGui) mpGui->mpWrapper->indent(i); - } +Gui::Group Gui::Widgets::group(const std::string& label, bool beginExpanded) +{ + return Group(mpGui, label, beginExpanded); +} - void Gui::Widgets::separator(uint32_t count) - { - if (mpGui) mpGui->mpWrapper->addSeparator(count); - } +void Gui::Widgets::indent(float i) +{ + if (mpGui) + mpGui->mpWrapper->indent(i); +} - void Gui::Widgets::dummy(const char label[], const float2& size, bool sameLine) - { - if (mpGui) mpGui->mpWrapper->addDummyItem(label, size, sameLine); - } +void Gui::Widgets::separator(uint32_t count) +{ + if (mpGui) + mpGui->mpWrapper->addSeparator(count); +} - void Gui::Widgets::rect(const float2& size, const float4& color, bool filled, bool sameLine) - { - if (mpGui) mpGui->mpWrapper->addRect(size, color, filled, sameLine); - } +void Gui::Widgets::dummy(const char label[], const float2& size, bool sameLine) +{ + if (mpGui) + mpGui->mpWrapper->addDummyItem(label, size, 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; - } +void Gui::Widgets::rect(const float2& size, const float4& color, bool filled, bool sameLine) +{ + if (mpGui) + mpGui->mpWrapper->addRect(size, color, filled, sameLine); +} - bool Gui::Widgets::button(const char label[], bool sameLine) - { - return mpGui ? mpGui->mpWrapper->addButton(label, sameLine) : false; - } +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::radioButtons(const RadioButtonGroup& buttons, uint32_t& activeID) - { - return mpGui ? mpGui->mpWrapper->addRadioButtons(buttons, activeID) : false; - } +bool Gui::Widgets::button(const char label[], bool sameLine) +{ + return mpGui ? mpGui->mpWrapper->addButton(label, sameLine) : false; +} - bool Gui::Widgets::direction(const char label[], float3& direction) - { - return mpGui ? mpGui->mpWrapper->addDirectionWidget(label, direction) : false; - } +bool Gui::Widgets::radioButtons(const RadioButtonGroup& buttons, uint32_t& activeID) +{ + return mpGui ? mpGui->mpWrapper->addRadioButtons(buttons, activeID) : false; +} - template<> - FALCOR_API bool Gui::Widgets::checkbox(const char label[], bool& var, bool sameLine) - { - return mpGui ? mpGui->mpWrapper->addCheckbox(label, var, sameLine) : false; - } +bool Gui::Widgets::direction(const char label[], float3& direction) +{ + return mpGui ? mpGui->mpWrapper->addDirectionWidget(label, direction) : false; +} - template<> - FALCOR_API bool Gui::Widgets::checkbox(const char label[], int& var, bool sameLine) - { - return mpGui ? mpGui->mpWrapper->addCheckbox(label, var, sameLine) : false; - } +template<> +FALCOR_API bool Gui::Widgets::checkbox(const char label[], bool& 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; - } +template<> +FALCOR_API bool Gui::Widgets::checkbox(const char label[], int& var, bool sameLine) +{ + return mpGui ? mpGui->mpWrapper->addCheckbox(label, var, sameLine) : false; +} -#define add_bool_vec_type(TypeName) template FALCOR_API bool Gui::Widgets::checkbox(const char[], TypeName&, bool) +template +bool Gui::Widgets::checkbox(const char label[], T& var, bool sameLine) +{ + return mpGui ? mpGui->mpWrapper->addBoolVecVar(label, var, sameLine) : false; +} - add_bool_vec_type(bool2); - add_bool_vec_type(bool3); - add_bool_vec_type(bool4); +#define add_bool_vec_type(TypeName) template FALCOR_API bool Gui::Widgets::checkbox(const char[], TypeName&, bool) + +add_bool_vec_type(bool2); +add_bool_vec_type(bool3); +add_bool_vec_type(bool4); #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::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; - } +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; - } +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 FALCOR_API bool Gui::Widgets::var(const char[], TypeName&, TypeName, TypeName, float, bool, const char*) +#define add_scalarVar_type(TypeName) \ + template FALCOR_API 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); - add_scalarVar_type(double); +add_scalarVar_type(int32_t); +add_scalarVar_type(uint32_t); +add_scalarVar_type(uint64_t); +add_scalarVar_type(float); +add_scalarVar_type(double); #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::lowest() / 2, std::numeric_limits::max() / 2); - T upperBound = glm::clamp(maxVal, std::numeric_limits::lowest() / 2, std::numeric_limits::max() / 2); - return mpGui ? mpGui->mpWrapper->addScalarSlider(label, var, lowerBound, upperBound, sameLine, displayFormat) : false; - } +template::value, bool>> +bool Gui::Widgets::slider(const char label[], T& var, T minVal, T maxVal, bool sameLine, const char* displayFormat) +{ + T lowerBound = std::clamp(minVal, std::numeric_limits::lowest() / 2, std::numeric_limits::max() / 2); + T upperBound = std::clamp(maxVal, std::numeric_limits::lowest() / 2, std::numeric_limits::max() / 2); + return mpGui ? mpGui->mpWrapper->addScalarSlider(label, var, lowerBound, upperBound, sameLine, displayFormat) : false; +} -#define add_scalarSlider_type(TypeName) template FALCOR_API bool Gui::Widgets::slider(const char[], TypeName&, TypeName, TypeName, bool, const char*) +#define add_scalarSlider_type(TypeName) \ + template FALCOR_API 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); - add_scalarSlider_type(double); +add_scalarSlider_type(int32_t); +add_scalarSlider_type(uint32_t); +add_scalarSlider_type(uint64_t); +add_scalarSlider_type(float); +add_scalarSlider_type(double); #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; - } +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 FALCOR_API bool Gui::Widgets::var(const char[], TypeName&, typename TypeName::value_type, typename TypeName::value_type, float, bool, const char*) +#define add_vecVar_type(TypeName) \ + template FALCOR_API bool Gui::Widgets::var< \ + TypeName>(const char[], TypeName&, typename TypeName::value_type, typename TypeName::value_type, float, bool, const char*) - add_vecVar_type(int2); - add_vecVar_type(int3); - add_vecVar_type(int4); - add_vecVar_type(uint2); - add_vecVar_type(uint3); - add_vecVar_type(uint4); - add_vecVar_type(float2); - add_vecVar_type(float3); - add_vecVar_type(float4); +add_vecVar_type(int2); +add_vecVar_type(int3); +add_vecVar_type(int4); +add_vecVar_type(uint2); +add_vecVar_type(uint3); +add_vecVar_type(uint4); +add_vecVar_type(float2); +add_vecVar_type(float3); +add_vecVar_type(float4); #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::lowest() / 2, std::numeric_limits::max() / 2); - typename T::value_type upperBound = glm::clamp(maxVal, std::numeric_limits::lowest() / 2, std::numeric_limits::max() / 2); - return mpGui ? mpGui->mpWrapper->addVecSlider(label, var, lowerBound, upperBound, sameLine, displayFormat) : false; - } +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 = math::clamp( + minVal, std::numeric_limits::lowest() / 2, std::numeric_limits::max() / 2 + ); + typename T::value_type upperBound = math::clamp( + maxVal, std::numeric_limits::lowest() / 2, std::numeric_limits::max() / 2 + ); + return mpGui ? mpGui->mpWrapper->addVecSlider(label, var, lowerBound, upperBound, sameLine, displayFormat) : false; +} -#define add_vecSlider_type(TypeName) template FALCOR_API bool Gui::Widgets::slider(const char[], TypeName&, typename TypeName::value_type, typename TypeName::value_type, bool, const char*) +#define add_vecSlider_type(TypeName) \ + template FALCOR_API bool Gui::Widgets::slider< \ + TypeName>(const char[], TypeName&, typename TypeName::value_type, typename TypeName::value_type, bool, const char*) - add_vecSlider_type(int2); - add_vecSlider_type(int3); - add_vecSlider_type(int4); - add_vecSlider_type(uint2); - add_vecSlider_type(uint3); - add_vecSlider_type(uint4); - add_vecSlider_type(float2); - add_vecSlider_type(float3); - add_vecSlider_type(float4); +add_vecSlider_type(int2); +add_vecSlider_type(int3); +add_vecSlider_type(int4); +add_vecSlider_type(uint2); +add_vecSlider_type(uint3); +add_vecSlider_type(uint4); +add_vecSlider_type(float2); +add_vecSlider_type(float3); +add_vecSlider_type(float4); #undef add_vecSlider_type - void Gui::Widgets::text(const std::string& text, bool sameLine) - { - if (mpGui) mpGui->mpWrapper->addText(text.c_str(), sameLine); - } +void Gui::Widgets::text(const std::string& text, bool sameLine) +{ + if (mpGui) + mpGui->mpWrapper->addText(text.c_str(), sameLine); +} - void Gui::Widgets::textWrapped(const std::string& text) - { - if (mpGui) mpGui->mpWrapper->addTextWrapped(text.c_str()); - } +void Gui::Widgets::textWrapped(const std::string& text) +{ + if (mpGui) + mpGui->mpWrapper->addTextWrapped(text.c_str()); +} - 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 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::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; - } +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); - } +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[], float3& var, bool sameLine) - { - return mpGui ? mpGui->mpWrapper->addRgbColor(label, var, sameLine) : false; - } +bool Gui::Widgets::rgbColor(const char label[], float3& var, bool sameLine) +{ + return mpGui ? mpGui->mpWrapper->addRgbColor(label, var, sameLine) : false; +} - bool Gui::Widgets::rgbaColor(const char label[], float4& var, bool sameLine) - { - return mpGui ? mpGui->mpWrapper->addRgbaColor(label, var, sameLine) : false; - } +bool Gui::Widgets::rgbaColor(const char label[], float4& var, bool sameLine) +{ + return mpGui ? mpGui->mpWrapper->addRgbaColor(label, var, sameLine) : false; +} - bool Gui::Widgets::imageButton(const char label[], const Texture::SharedPtr& pTex, float2 size, bool maintainRatio, bool sameLine) - { - return mpGui ? mpGui->mpWrapper->addImageButton(label, pTex, size, maintainRatio, sameLine) : false; - } +const Texture* Gui::Widgets::loadImage(const std::filesystem::path& path) +{ + return mpGui ? mpGui->mpWrapper->loadImage(path) : nullptr; +} - void Gui::Widgets::image(const char label[], const Texture::SharedPtr& pTex, float2 size, bool maintainRatio, bool sameLine) - { - if (mpGui) mpGui->mpWrapper->addImage(label, pTex, size, maintainRatio, sameLine); - } +bool Gui::Widgets::imageButton(const char label[], const Texture* pTex, float2 size, bool maintainRatio, bool sameLine) +{ + return mpGui ? mpGui->mpWrapper->addImageButton(label, pTex, size, maintainRatio, sameLine) : false; +} - 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; - } +void Gui::Widgets::image(const char label[], const Texture* pTex, float2 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 FALCOR_API bool Gui::Widgets::matrix(const char[], TypeName&, float, float, bool) - add_matrix_var(rmcv::mat3); - add_matrix_var(rmcv::mat4); +add_matrix_var(float3x3); +add_matrix_var(float4x4); #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; - } +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() - { - release(); - } +Gui::Menu::Menu(Gui* pGui, const char* name) +{ + if (pGui && pGui->mpWrapper->beginMenu(name)) + mpGui = pGui; +} - void Gui::Menu::release() - { - if (mpGui) mpGui->mpWrapper->endMenu(); - mpGui = nullptr; - } +Gui::Menu::~Menu() +{ + release(); +} - Gui::Menu::Dropdown Gui::Menu::dropdown(const std::string& label) - { - return Dropdown(mpGui, label.c_str()); - } +void Gui::Menu::release() +{ + if (mpGui) + mpGui->mpWrapper->endMenu(); + mpGui = nullptr; +} - bool Gui::Menu::item(const std::string& label) - { - return mpGui && mpGui->mpWrapper->addMenuItem(label.c_str()); - } +Gui::Menu::Dropdown Gui::Menu::dropdown(const std::string& label) +{ + return Dropdown(mpGui, label.c_str()); +} - Gui::Menu::Dropdown::Dropdown(Gui* pGui, const char label[]) - { - if (pGui && pGui->mpWrapper->beginDropDownMenu(label)) mpGui = pGui; - } +bool Gui::Menu::item(const std::string& label) +{ + return mpGui && mpGui->mpWrapper->addMenuItem(label.c_str()); +} - Gui::Menu::Dropdown::~Dropdown() - { - if (mpGui) release(); - } +Gui::Menu::Dropdown::Dropdown(Gui* pGui, const char label[]) +{ + if (pGui && pGui->mpWrapper->beginDropDownMenu(label)) + mpGui = pGui; +} - 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); - } +Gui::Menu::Dropdown::~Dropdown() +{ + if (mpGui) + release(); +} - 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::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); +} - void Gui::Menu::Dropdown::separator() - { - if (mpGui) mpGui->mpWrapper->addSeparator(); - } +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); +} - Gui::Menu Gui::Menu::Dropdown::menu(const char* name) - { - return Menu(mpGui, name); - } +void Gui::Menu::Dropdown::separator() +{ + if (mpGui) + mpGui->mpWrapper->addSeparator(); +} - Gui::Group::Group(Gui* pGui, const std::string& label, bool beginExpanded) - { - if (pGui && pGui->mpWrapper->beginGroup(label, beginExpanded)) mpGui = pGui; - } +Gui::Menu Gui::Menu::Dropdown::menu(const char* name) +{ + return Menu(mpGui, name); +} - bool Gui::Group::open() const - { - return mpGui != nullptr; - } +Gui::Group::Group(Gui* pGui, const std::string& label, bool beginExpanded) +{ + if (pGui && pGui->mpWrapper->beginGroup(label, beginExpanded)) + mpGui = pGui; +} - Gui::Group::~Group() - { - release(); - } +bool Gui::Group::open() const +{ + return mpGui != nullptr; +} - void Gui::Group::release() - { - if (mpGui) mpGui->mpWrapper->endGroup(); mpGui = nullptr; - } +Gui::Group::~Group() +{ + release(); +} - Gui::Window::Window(Gui* pGui, const char* name, uint2 size, uint2 pos, Gui::WindowFlags flags) - { - bool open = true; - if (pGui->mpWrapper->pushWindow(name, open, size, pos, flags)) mpGui = pGui; - } +void Gui::Group::release() +{ + if (mpGui) + mpGui->mpWrapper->endGroup(); + mpGui = nullptr; +} - Gui::Window::Window(Gui* pGui, const char* name, bool& open, uint2 size, uint2 pos, Gui::WindowFlags flags) - { - if (pGui->mpWrapper->pushWindow(name, open, size, pos, flags)) mpGui = pGui; - } +Gui::Window::Window(Gui* pGui, const char* name, uint2 size, uint2 pos, Gui::WindowFlags flags) +{ + bool open = true; + if (pGui->mpWrapper->pushWindow(name, open, size, pos, flags)) + mpGui = pGui; +} - Gui::Window::~Window() - { - release(); - } +Gui::Window::Window(Gui* pGui, const char* name, bool& open, uint2 size, uint2 pos, Gui::WindowFlags flags) +{ + if (pGui->mpWrapper->pushWindow(name, open, size, pos, flags)) + mpGui = pGui; +} - void Gui::Window::release() - { - if (mpGui) mpGui->mpWrapper->popWindow(); - mpGui = nullptr; - } +Gui::Window::~Window() +{ + release(); +} - void Gui::Window::columns(uint32_t numColumns) - { - if (mpGui) mpGui->mpWrapper->beginColumns(numColumns); - } +void Gui::Window::release() +{ + if (mpGui) + mpGui->mpWrapper->popWindow(); + mpGui = nullptr; +} - void Gui::Window::nextColumn() - { - if (mpGui) mpGui->mpWrapper->nextColumn(); - } +void Gui::Window::columns(uint32_t numColumns) +{ + if (mpGui) + mpGui->mpWrapper->beginColumns(numColumns); +} - void Gui::Window::windowPos(uint32_t x, uint32_t y) - { - if (mpGui) mpGui->mpWrapper->setCurrentWindowPos(x, y); - } +void Gui::Window::nextColumn() +{ + if (mpGui) + mpGui->mpWrapper->nextColumn(); +} - void Gui::Window::windowSize(uint32_t width, uint32_t height) - { - if (mpGui) mpGui->mpWrapper->setCurrentWindowSize(width, height); - } +void Gui::Window::windowPos(uint32_t x, uint32_t y) +{ + if (mpGui) + mpGui->mpWrapper->setCurrentWindowPos(x, y); +} - Gui::MainMenu::MainMenu(Gui* pGui) : Gui::Menu() - { - if (pGui->mpWrapper->beginMainMenuBar()) mpGui = pGui; - } +void Gui::Window::windowSize(uint32_t width, uint32_t height) +{ + if (mpGui) + mpGui->mpWrapper->setCurrentWindowSize(width, height); +} - Gui::MainMenu::~MainMenu() - { - release(); - } +Gui::MainMenu::MainMenu(Gui* pGui) : Gui::Menu() +{ + if (pGui->mpWrapper->beginMainMenuBar()) + mpGui = pGui; +} - void Gui::MainMenu::release() - { - if (mpGui) - { - mpGui->mpWrapper->endMainMenuBar(); - mpGui = nullptr; - } - } +Gui::MainMenu::~MainMenu() +{ + release(); +} - template - bool Gui::Widgets::spectrum(const char label[], SampledSpectrum& spectrum) +void Gui::MainMenu::release() +{ + if (mpGui) { - return renderSpectrumUI(*this, spectrum, label); + mpGui->mpWrapper->endMainMenuBar(); + mpGui = nullptr; } +} - template - bool Gui::Widgets::spectrum(const char label[], SampledSpectrum& spectrum, SpectrumUI& spectrumUI) - { - return renderSpectrumUI(*this, spectrum , spectrumUI, label); - } +template +bool Gui::Widgets::spectrum(const char label[], SampledSpectrum& spectrum) +{ + return renderSpectrumUI(*this, spectrum, label); +} - template FALCOR_API bool Gui::Widgets::spectrum(const char label[], SampledSpectrum& spectrum); // No need for a version, since this call does not save the state of the SpectrumUI, and we need a state to switch which of the three curves we edit. +template +bool Gui::Widgets::spectrum(const char label[], SampledSpectrum& spectrum, SpectrumUI& spectrumUI) +{ + return renderSpectrumUI(*this, spectrum, spectrumUI, label); +} - template FALCOR_API bool Gui::Widgets::spectrum(const char label[], SampledSpectrum& spectrum, SpectrumUI& spectrumUI); - template FALCOR_API bool Gui::Widgets::spectrum(const char label[], SampledSpectrum& spectrum, SpectrumUI& spectrumUI); +// No need for a version, since this call does not save the state of the SpectrumUI, and we need a state to switch which of the +// three curves we edit. +template FALCOR_API bool Gui::Widgets::spectrum(const char label[], SampledSpectrum& spectrum); - IDScope::IDScope(const void* id) - { - ImGui::PushID(id); - } +template FALCOR_API bool Gui::Widgets::spectrum(const char label[], SampledSpectrum& spectrum, SpectrumUI& spectrumUI); +template FALCOR_API bool Gui::Widgets::spectrum( + const char label[], + SampledSpectrum& spectrum, + SpectrumUI& spectrumUI +); - IDScope::~IDScope() - { - ImGui::PopID(); - } +IDScope::IDScope(const void* id) +{ + ImGui::PushID(id); +} +IDScope::~IDScope() +{ + ImGui::PopID(); } + +} // namespace Falcor diff --git a/Source/Falcor/Utils/UI/Gui.h b/Source/Falcor/Utils/UI/Gui.h index 5cb3fda7f..05032ca85 100644 --- a/Source/Falcor/Utils/UI/Gui.h +++ b/Source/Falcor/Utils/UI/Gui.h @@ -40,537 +40,673 @@ struct ImFont; namespace Falcor { - class RenderContext; +class RenderContext; - struct MouseEvent; - struct KeyboardEvent; +struct MouseEvent; +struct KeyboardEvent; - class GuiImpl; +class GuiImpl; - template class SpectrumUI; +template +class SpectrumUI; - // Helper to check if a class is a vector - template - struct is_vector : std::false_type {}; +// Helper to check if a class is a vector +template +struct is_vector : std::false_type +{}; - template - struct is_vector> : std::true_type {}; +template +struct is_vector> : std::true_type +{}; - /** A class wrapping the external GUI library - */ - class FALCOR_API Gui +/** + * A class wrapping the external GUI library + */ +class FALCOR_API Gui +{ +public: + using GraphCallback = float (*)(void*, int32_t index); + + /** + * These structs used to initialize dropdowns + */ + struct DropdownValue { - public: - using GraphCallback = float(*)(void*, int32_t index); + uint32_t value; ///< User defined index. Should be unique between different options. + std::string label; ///< Label of the dropdown option. + }; - /** 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; - 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. + }; - 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; - using RadioButtonGroup = std::vector; + enum class TextFlags + { + Empty = 0x0, + FitWindow = 0x1, // Also hides the label + }; - 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 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 + }; - enum class WidgetFlags - { - Empty = 0x0, ///< No flags - SameLine = 0x1, ///< Show a title bar - Inactive = 0x2, ///< Inactive widget, disallow edits - }; + class FALCOR_API Group; - class FALCOR_API Group; + class FALCOR_API Widgets + { + public: + /** + * Begin a new group + * @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); + + /** + * 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 float2& 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 float4 + * @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 float2& size, const float4& color = float4(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[], float3& 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); + + /** + * Static text wrapped to the window + * @param[in] text The string to display + */ + void textWrapped(const std::string& text); + + /** + * 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[], float3& 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[], float4& var, bool sameLine = false); + + /** + * Load an image as a texture. + * Images are cached so subsequent accesses are fast. + * @param[in] path Image path. + * @return Return the texture. + */ + const Texture* loadImage(const std::filesystem::path& path); + + // 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* pTex, float2 size = float2(0), bool maintainRatio = true, bool sameLine = false); + bool imageButton(const char label[], const Texture* pTex, float2 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::lowest(), + T maxVal = std::numeric_limits::max(), + float step = std::is_floating_point_v ? 0.001f : 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::lowest() / 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::lowest(), + typename T::value_type maxVal = std::numeric_limits::max(), + float step = std::is_floating_point_v ? 0.001f : 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::lowest() / 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 + ); + + /** + * Adds a Spectrum user interface. Since there is no SpectrumUI class as input, this call will only disply the spectrum curve and + * its points and final RGB color. Use the spectrum() function below with a SpectrumUI parameters as well if you want to have the + * spectrum UI as well. + * @param[in] label The name of the widget. + * @param[in] spectrum The spectrum to be visualized (and possibly edited). + */ + template + bool spectrum(const char label[], SampledSpectrum& spectrum); + + /** + * Adds a Spectrum user interface. + * @param[in] label The name of the widget. + * @param[in] spectrum The spectrum to be visualized (and possibly edited). + * @param[in] spectrumUI The spectrum UI with modifiable parameters. + */ + template + bool spectrum(const char label[], SampledSpectrum& spectrum, SpectrumUI& spectrumUI); + + Gui* gui() const { return mpGui; } + + protected: + Widgets() = default; + Gui* mpGui = nullptr; + }; - class FALCOR_API Widgets - { - public: - /** Begin a new group - \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); - - /** 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 float2& 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 float4 - \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 float2& size, const float4& color = float4(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[], float3& 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); - - /** Static text wrapped to the window - \param[in] text The string to display - */ - void textWrapped(const std::string& text); - - /** 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[], float3& 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[], float4& 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, float2 size = float2(0), bool maintainRatio = true, bool sameLine = false); - bool imageButton(const char label[], const Texture::SharedPtr& pTex, float2 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::lowest(), T maxVal = std::numeric_limits::max(), - float step = std::is_floating_point_v ? 0.001f : 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::lowest() / 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::lowest(), typename T::value_type maxVal = std::numeric_limits::max(), - float step = std::is_floating_point_v ? 0.001f : 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::lowest() / 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); - - /** Adds a Spectrum user interface. Since there is no SpectrumUI class as input, this call will only disply the spectrum curve and its points and final RGB color. - Use the spectrum() function below with a SpectrumUI parameters as well if you want to have the spectrum UI as well. - \param[in] label The name of the widget. - \param[in] spectrum The spectrum to be visualized (and possibly edited). - */ - template - bool spectrum(const char label[], SampledSpectrum& spectrum); - - /** Adds a Spectrum user interface. - \param[in] label The name of the widget. - \param[in] spectrum The spectrum to be visualized (and possibly edited). - \param[in] spectrumUI The spectrum UI with modifiable parameters. - */ - template - bool spectrum(const char label[], SampledSpectrum& spectrum, SpectrumUI& spectrumUI); - - Gui* gui() const { return mpGui; } - - protected: - Widgets() = default; - Gui* mpGui = nullptr; - }; + class FALCOR_API 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(); - class FALCOR_API Menu + /** + * End the menu of items. + */ + void release(); + + class FALCOR_API Dropdown { 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); + /** + * 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[]); - ~Menu(); + ~Dropdown(); - /** End the menu of items. - */ + /** + * End the drop down menu list of items. + */ void release(); - class FALCOR_API 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 FALCOR_API Group : public Widgets - { - public: - Group() = default; - - /** 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() const; - - /** Bool operator to check if the current group is open or closed. - */ - operator bool() const { return open(); } - - ~Group(); - - /** End a collapsible group block - */ - 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 = ""); - class FALCOR_API 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, uint2 size = { 0, 0 }, uint2 pos = { 0, 0 }, Gui::WindowFlags flags = Gui::WindowFlags::Default); - Window(Gui* pGui, const char* name, bool& open, uint2 size = { 0, 0 }, uint2 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, uint2 size = { 0, 0 }, uint2 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, uint2 size = { 0, 0 }, uint2 pos = { 0, 0 }, Gui::WindowFlags flags = Gui::WindowFlags::Default) : Window(w.gui(), name, open, size, pos, flags) {} - - ~Window(); - - /** End the window. - */ - 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] 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 = ""); - /** Begin a column within the current window - \param[in] numColumns requires number of columns within the window. + /** + * Add a separator between menu items. */ - void columns(uint32_t numColumns); + void separator(); - /** Proceed to the next column within the window. + /** + * Adds a sub-menu within the current dropdown menu. + * @param[in] name the name of the menu */ - 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); + Menu menu(const char* name); + + Gui* mpGui = nullptr; }; - class FALCOR_API MainMenu : public Menu - { - public: - /** Create a new main menu bar. - \param[in] pGui a pointer to the current Gui object - */ - MainMenu(Gui* pGui); + /** + * 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; + }; - ~MainMenu(); + class FALCOR_API Group : public Widgets + { + public: + Group() = default; + + /** + * 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() const; + + /** + * Bool operator to check if the current group is open or closed. + */ + operator bool() const { return open(); } + + ~Group(); + + /** + * End a collapsible group block + */ + void release(); + }; - /** End the main menu bar. - */ - void release(); - }; + class FALCOR_API 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, uint2 size = {0, 0}, uint2 pos = {0, 0}, Gui::WindowFlags flags = Gui::WindowFlags::Default); + Window( + Gui* pGui, + const char* name, + bool& open, + uint2 size = {0, 0}, + uint2 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, + uint2 size = {0, 0}, + uint2 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, + uint2 size = {0, 0}, + uint2 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 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); + }; - /// Constructor. - Gui(std::shared_ptr pDevice, uint32_t width, uint32_t height, float scaleFactor = 1.f); + class FALCOR_API 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(); + }; - ~Gui(); + /// Constructor. + Gui(ref pDevice, uint32_t width, uint32_t height, float scaleFactor = 1.f); - static float4 pickUniqueColor(const std::string& key); + ~Gui(); - /** Add a font - */ - void addFont(const std::string& name, const std::filesystem::path& path); + static float4 pickUniqueColor(const std::string& key); - /** Set the active font - */ - void setActiveFont(const std::string& font); + /** + * Add a font + */ + void addFont(const std::string& name, const std::filesystem::path& path); - ImFont* getFont(std::string f = ""); + /** + * Set the active font + */ + void setActiveFont(const std::string& font); - /** Start a new frame. Must be called at the start of each frame - */ - void beginFrame(); + ImFont* getFont(std::string f = ""); - /** Set global font size scaling - */ - static void setGlobalGuiScaling(float scale); + /** + * Start a new frame. Must be called at the start of each frame + */ + void beginFrame(); - /** Render the GUI - */ - void render(RenderContext* pContext, const Fbo::SharedPtr& pFbo, float elapsedTime); + /** + * Render the GUI + */ + void render(RenderContext* pContext, const ref& pFbo, float elapsedTime); - /** Handle window resize events - */ - void onWindowResize(uint32_t width, uint32_t height); + /** + * Handle window resize events + */ + void onWindowResize(uint32_t width, uint32_t height); - /** Handle mouse events - */ - bool onMouseEvent(const MouseEvent& event); + /** + * Handle mouse events + */ + bool onMouseEvent(const MouseEvent& event); - /** Handle keyboard events - */ - bool onKeyboardEvent(const KeyboardEvent& event); - private: - std::unique_ptr mpWrapper; - }; + /** + * Handle keyboard events + */ + bool onKeyboardEvent(const KeyboardEvent& event); - /** Helper class to create a scope for ImGui IDs using PushID/PopID. - */ - class FALCOR_API IDScope - { - public: - IDScope(const void* id); - ~IDScope(); - }; +private: + std::unique_ptr mpWrapper; +}; - FALCOR_ENUM_CLASS_OPERATORS(Gui::WindowFlags); - FALCOR_ENUM_CLASS_OPERATORS(Gui::TextFlags); -} +/** + * Helper class to create a scope for ImGui IDs using PushID/PopID. + */ +class FALCOR_API IDScope +{ +public: + IDScope(const void* id); + ~IDScope(); +}; + +FALCOR_ENUM_CLASS_OPERATORS(Gui::WindowFlags); +FALCOR_ENUM_CLASS_OPERATORS(Gui::TextFlags); +} // namespace Falcor diff --git a/Source/Falcor/Utils/UI/Gui.slang b/Source/Falcor/Utils/UI/Gui.slang index 9f002ee56..d6e1169b7 100644 --- a/Source/Falcor/Utils/UI/Gui.slang +++ b/Source/Falcor/Utils/UI/Gui.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -56,7 +56,7 @@ VsOut vsMain(VsIn vIn) vOut.color = vIn.color; vOut.texC = vIn.texC; vOut.pos.xy = vIn.pos.xy * scale + offset; - vOut.pos.zw = float2(0,1); + vOut.pos.zw = float2(0, 1); return vOut; } diff --git a/Source/Falcor/Utils/UI/InputState.cpp b/Source/Falcor/Utils/UI/InputState.cpp index acf53dbe3..29dd3555d 100644 --- a/Source/Falcor/Utils/UI/InputState.cpp +++ b/Source/Falcor/Utils/UI/InputState.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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,64 +30,64 @@ namespace Falcor { - bool InputState::isModifierDown(Input::Modifier mod) const - { - return getModifierState(mCurrentKeyState, mod); - } +bool InputState::isModifierDown(Input::Modifier mod) const +{ + return getModifierState(mCurrentKeyState, mod); +} - bool InputState::isModifierPressed(Input::Modifier mod) const - { - return getModifierState(mCurrentKeyState, mod) == true && getModifierState(mPreviousKeyState, mod) == false; - } +bool InputState::isModifierPressed(Input::Modifier mod) const +{ + return getModifierState(mCurrentKeyState, mod) == true && getModifierState(mPreviousKeyState, mod) == false; +} + +bool InputState::isModifierReleased(Input::Modifier mod) const +{ + return getModifierState(mCurrentKeyState, mod) == false && getModifierState(mPreviousKeyState, mod) == true; +} - bool InputState::isModifierReleased(Input::Modifier mod) const +void InputState::onKeyEvent(const KeyboardEvent& keyEvent) +{ + // Update the stored key state. + if (keyEvent.type == KeyboardEvent::Type::KeyPressed || keyEvent.type == KeyboardEvent::Type::KeyReleased) { - return getModifierState(mCurrentKeyState, mod) == false && getModifierState(mPreviousKeyState, mod) == true; + mCurrentKeyState[(size_t)keyEvent.key] = keyEvent.type == KeyboardEvent::Type::KeyPressed; } +} - void InputState::onKeyEvent(const KeyboardEvent& keyEvent) +void InputState::onMouseEvent(const MouseEvent& mouseEvent) +{ + // Update the stored mouse state. + if (mouseEvent.type == MouseEvent::Type::ButtonDown || mouseEvent.type == MouseEvent::Type::ButtonUp) { - // Update the stored key state. - if (keyEvent.type == KeyboardEvent::Type::KeyPressed || keyEvent.type == KeyboardEvent::Type::KeyReleased) - { - mCurrentKeyState[(size_t)keyEvent.key] = keyEvent.type == KeyboardEvent::Type::KeyPressed; - } + mCurrentMouseState[(size_t)mouseEvent.button] = mouseEvent.type == MouseEvent::Type::ButtonDown; } - - void InputState::onMouseEvent(const MouseEvent& mouseEvent) + else if (mouseEvent.type == MouseEvent::Type::Move) { - // Update the stored mouse state. - if (mouseEvent.type == MouseEvent::Type::ButtonDown || mouseEvent.type == MouseEvent::Type::ButtonUp) - { - mCurrentMouseState[(size_t)mouseEvent.button] = mouseEvent.type == MouseEvent::Type::ButtonDown; - } - else if (mouseEvent.type == MouseEvent::Type::Move) - { - mMouseMoving = true; - } + mMouseMoving = true; } +} - void InputState::endFrame() - { - mPreviousKeyState = mCurrentKeyState; - mPreviousMouseState = mCurrentMouseState; +void InputState::endFrame() +{ + mPreviousKeyState = mCurrentKeyState; + mPreviousMouseState = mCurrentMouseState; - mMouseMoving = false; - } + mMouseMoving = false; +} - bool InputState::getModifierState(const KeyStates& states, Input::Modifier mod) const +bool InputState::getModifierState(const KeyStates& states, Input::Modifier mod) const +{ + switch (mod) { - switch (mod) - { - case Input::Modifier::Shift: - return states[(size_t)Input::Key::LeftShift] || states[(size_t)Input::Key::RightShift]; - case Input::Modifier::Ctrl: - return states[(size_t)Input::Key::LeftControl] || states[(size_t)Input::Key::RightControl]; - case Input::Modifier::Alt: - return states[(size_t)Input::Key::LeftAlt] || states[(size_t)Input::Key::RightAlt]; - default: - FALCOR_UNREACHABLE(); - return false; - } + case Input::Modifier::Shift: + return states[(size_t)Input::Key::LeftShift] || states[(size_t)Input::Key::RightShift]; + case Input::Modifier::Ctrl: + return states[(size_t)Input::Key::LeftControl] || states[(size_t)Input::Key::RightControl]; + case Input::Modifier::Alt: + return states[(size_t)Input::Key::LeftAlt] || states[(size_t)Input::Key::RightAlt]; + default: + FALCOR_UNREACHABLE(); + return false; } } +} // namespace Falcor diff --git a/Source/Falcor/Utils/UI/InputState.h b/Source/Falcor/Utils/UI/InputState.h index 99dc038df..54f8bbc0c 100644 --- a/Source/Falcor/Utils/UI/InputState.h +++ b/Source/Falcor/Utils/UI/InputState.h @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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,105 +32,125 @@ namespace Falcor { - /** A class that hold the state of the current inputs such as Keys and mouse events. - The class does not tell Falcor that it has handled the input when the user calls the functions. - It is up to the user to tell Falcor that the input event was handled by returning true in the input event callbacks. - */ - class Sample; - class FALCOR_API InputState +/** + * A class that hold the state of the current inputs such as Keys and mouse events. + * The class does not tell Falcor that it has handled the input when the user calls the functions. + * It is up to the user to tell Falcor that the input event was handled by returning true in the input event callbacks. + */ +class Sample; +class FALCOR_API InputState +{ +public: + bool isMouseMoving() const { return mMouseMoving; } + + /** + * Check if the specified key is currently held down. + * @param[in] key The specified key. + * @return True if currently held down, else false. + */ + bool isKeyDown(Input::Key key) const { return mCurrentKeyState[(size_t)key]; } + + /** + * Check if the specified key was just pressed down. + * @param[in] key The key to check. + * @return True if the specified key was just pressed down, else false. + */ + bool isKeyPressed(Input::Key key) const { return mCurrentKeyState[(size_t)key] == true && mPreviousKeyState[(uint32_t)key] == false; } + + /** + * Check if the specified key was just released up. + * @param[in] key The key to check. + * @return True if the specified key was just released up, else false. + */ + bool isKeyReleased(Input::Key key) const { return mCurrentKeyState[(size_t)key] == false && mPreviousKeyState[(uint32_t)key] == true; } + + /** + * Check if the specified mouse button is currently held down. + * @param[in] mb The specified mouse button. + * @return True if currently held down, else false. + */ + bool isMouseButtonDown(Input::MouseButton mb) const { return mCurrentMouseState[(size_t)mb]; } + + /** + * Check if the specified mouse button was just pressed down. + * @param[in] mb The mouse button to check. + * @return True if the specified mouse button was just pressed down, else false. + */ + bool isMouseButtonClicked(Input::MouseButton mb) const + { + return mCurrentMouseState[(size_t)mb] == true && mPreviousMouseState[(uint32_t)mb] == false; + } + + /** + * Check if the specified mouse button was just released up. + * @param[in] mb The mouse button to check. + * @return True if the specified mouse button was just released up, else false. + */ + bool isMouseButtonReleased(Input::MouseButton mb) const { - public: - bool isMouseMoving() const { return mMouseMoving; } - - /** Check if the specified key is currently held down. - \param[in] key The specified key. - \return True if currently held down, else false. - */ - bool isKeyDown(Input::Key key) const { return mCurrentKeyState[(size_t)key]; } - - /** Check if the specified key was just pressed down. - \param[in] key The key to check. - \return True if the specified key was just pressed down, else false. - */ - bool isKeyPressed(Input::Key key) const { return mCurrentKeyState[(size_t)key] == true && mPreviousKeyState[(uint32_t)key] == false; } - - /** Check if the specified key was just released up. - \param[in] key The key to check. - \return True if the specified key was just released up, else false. - */ - bool isKeyReleased(Input::Key key) const { return mCurrentKeyState[(size_t)key] == false && mPreviousKeyState[(uint32_t)key] == true; } - - /** Check if the specified mouse button is currently held down. - \param[in] mb The specified mouse button. - \return True if currently held down, else false. - */ - bool isMouseButtonDown(Input::MouseButton mb) const { return mCurrentMouseState[(size_t)mb]; } - - /** Check if the specified mouse button was just pressed down. - \param[in] mb The mouse button to check. - \return True if the specified mouse button was just pressed down, else false. - */ - bool isMouseButtonClicked(Input::MouseButton mb) const { return mCurrentMouseState[(size_t)mb] == true && mPreviousMouseState[(uint32_t)mb] == false; } - - /** Check if the specified mouse button was just released up. - \param[in] mb The mouse button to check. - \return True if the specified mouse button was just released up, else false. - */ - bool isMouseButtonReleased(Input::MouseButton mb) const { return mCurrentMouseState[(size_t)mb] == false && mPreviousMouseState[(uint32_t)mb] == true; } - - /** Check if the specified modifier is currently held down. - \param[in] mod The specified modifier. - \return True if currently held down, else false. - */ - bool isModifierDown(Input::Modifier mod) const; - - /** Check if the specified modifier key was just pressed down. - \param[in] mod The key to check. - \return True if the specified modifier key was just pressed down, else false. - */ - bool isModifierPressed(Input::Modifier mod) const; - - /** Check if the specified modifier key was just released up. - \param[in] mod The key to check. - \return True if the specified modifier key was just released up, else false. - */ - bool isModifierReleased(Input::Modifier mod) const; - - private: - static const size_t kKeyCount = (size_t)Input::Key::Count; - static const size_t kMouseButtonCount = (size_t)Input::MouseButton::Count; - using KeyStates = std::bitset; - using MouseState = std::bitset; - - /** Processes the key event and set the internal state. - \param[in] keyEvent The event to register. - */ - void onKeyEvent(const KeyboardEvent& keyEvent); - - /** Processes the mouse event and set the internal state. - \param[in] mouseEvent The event to register. - */ - void onMouseEvent(const MouseEvent& mouseEvent); - - /** Prepare the states for the next frame. - */ - void endFrame(); - - /** Helper function to get the modifier state from the key states. - \param[in] states The key states to fetch from. - \param[in] mod The modifier to check for. - \return True if the modifier state was active, else false. - */ - bool getModifierState(const KeyStates& states, Input::Modifier mod) const; - - // The state (up/down) of the keys and mouse buttons for the current and previous frame, default initialized to zero (up). - KeyStates mCurrentKeyState; - KeyStates mPreviousKeyState; - MouseState mCurrentMouseState; - MouseState mPreviousMouseState; - - bool mMouseMoving = false; - - friend class SampleApp; - }; -} + return mCurrentMouseState[(size_t)mb] == false && mPreviousMouseState[(uint32_t)mb] == true; + } + + /** + * Check if the specified modifier is currently held down. + * @param[in] mod The specified modifier. + * @return True if currently held down, else false. + */ + bool isModifierDown(Input::Modifier mod) const; + + /** + * Check if the specified modifier key was just pressed down. + * @param[in] mod The key to check. + * @return True if the specified modifier key was just pressed down, else false. + */ + bool isModifierPressed(Input::Modifier mod) const; + + /** + * Check if the specified modifier key was just released up. + * @param[in] mod The key to check. + * @return True if the specified modifier key was just released up, else false. + */ + bool isModifierReleased(Input::Modifier mod) const; + +private: + static constexpr size_t kKeyCount = (size_t)Input::Key::Count; + static constexpr size_t kMouseButtonCount = (size_t)Input::MouseButton::Count; + using KeyStates = std::bitset; + using MouseState = std::bitset; + + /** + * Processes the key event and set the internal state. + * @param[in] keyEvent The event to register. + */ + void onKeyEvent(const KeyboardEvent& keyEvent); + + /** + * Processes the mouse event and set the internal state. + * @param[in] mouseEvent The event to register. + */ + void onMouseEvent(const MouseEvent& mouseEvent); + + /** + * Prepare the states for the next frame. + */ + void endFrame(); + + /** + * Helper function to get the modifier state from the key states. + * @param[in] states The key states to fetch from. + * @param[in] mod The modifier to check for. + * @return True if the modifier state was active, else false. + */ + bool getModifierState(const KeyStates& states, Input::Modifier mod) const; + + // The state (up/down) of the keys and mouse buttons for the current and previous frame, default initialized to zero (up). + KeyStates mCurrentKeyState; + KeyStates mPreviousKeyState; + MouseState mCurrentMouseState; + MouseState mPreviousMouseState; + + bool mMouseMoving = false; + + friend class SampleApp; +}; +} // namespace Falcor diff --git a/Source/Falcor/Utils/UI/InputTypes.h b/Source/Falcor/Utils/UI/InputTypes.h index db1a2834f..8c78af8cd 100644 --- a/Source/Falcor/Utils/UI/InputTypes.h +++ b/Source/Falcor/Utils/UI/InputTypes.h @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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,249 +32,256 @@ namespace Falcor { - namespace Input - { - /** Enum for the mouse buttons. Used for checking mouse button state in the InputState class. - */ - enum class MouseButton : uint32_t - { - Left, - Middle, - Right, - Count - }; +namespace Input +{ +/** + * Enum for the mouse buttons. Used for checking mouse button state in the InputState class. + */ +enum class MouseButton : uint32_t +{ + Left, + Middle, + Right, + Count +}; - /** Flags for the different modifiers. - */ - enum class ModifierFlags : uint32_t - { - None = 0, - Shift = 1, - Ctrl = 2, - Alt = 4 - }; - FALCOR_ENUM_CLASS_OPERATORS(ModifierFlags); +/** + * Flags for the different modifiers. + */ +enum class ModifierFlags : uint32_t +{ + None = 0, + Shift = 1, + Ctrl = 2, + Alt = 4 +}; +FALCOR_ENUM_CLASS_OPERATORS(ModifierFlags); - /** Enum for the different modifiers. Used for checking modifier state in the InputState class and for checking modifier status on the KeyboardEvent. - For that to work these needs to have the same values as their flags. - */ - enum class Modifier : uint32_t - { - Shift = (uint32_t)ModifierFlags::Shift, - Ctrl = (uint32_t)ModifierFlags::Ctrl, - Alt = (uint32_t)ModifierFlags::Alt - }; +/** + * Enum for the different modifiers. Used for checking modifier state in the InputState class and for checking modifier status on the + * KeyboardEvent. For that to work these needs to have the same values as their flags. + */ +enum class Modifier : uint32_t +{ + Shift = (uint32_t)ModifierFlags::Shift, + Ctrl = (uint32_t)ModifierFlags::Ctrl, + Alt = (uint32_t)ModifierFlags::Alt +}; - /** Use this enum to find out which key was pressed. Alpha-numeric keys use their uppercase ASCII code, so you can use that as well. - */ - enum class Key : uint32_t - { - // Key codes 0..255 are reserved for ASCII codes. - Space = ' ', - Apostrophe = '\'', - Comma = ',', - Minus = '-', - Period = '.', - Slash = '/', - Key0 = '0', - Key1 = '1', - Key2 = '2', - Key3 = '3', - Key4 = '4', - Key5 = '5', - Key6 = '6', - Key7 = '7', - Key8 = '8', - Key9 = '9', - Semicolon = ';', - Equal = '=', - A = 'A', - B = 'B', - C = 'C', - D = 'D', - E = 'E', - F = 'F', - G = 'G', - H = 'H', - I = 'I', - J = 'J', - K = 'K', - L = 'L', - M = 'M', - N = 'N', - O = 'O', - P = 'P', - Q = 'Q', - R = 'R', - S = 'S', - T = 'T', - U = 'U', - V = 'V', - W = 'W', - X = 'X', - Y = 'Y', - Z = 'Z', - LeftBracket = '[', - Backslash = '\\', - RightBracket = ']', - GraveAccent = '`', +/** + * Use this enum to find out which key was pressed. Alpha-numeric keys use their uppercase ASCII code, so you can use that as well. + */ +enum class Key : uint32_t +{ + // Key codes 0..255 are reserved for ASCII codes. + Space = ' ', + Apostrophe = '\'', + Comma = ',', + Minus = '-', + Period = '.', + Slash = '/', + Key0 = '0', + Key1 = '1', + Key2 = '2', + Key3 = '3', + Key4 = '4', + Key5 = '5', + Key6 = '6', + Key7 = '7', + Key8 = '8', + Key9 = '9', + Semicolon = ';', + Equal = '=', + A = 'A', + B = 'B', + C = 'C', + D = 'D', + E = 'E', + F = 'F', + G = 'G', + H = 'H', + I = 'I', + J = 'J', + K = 'K', + L = 'L', + M = 'M', + N = 'N', + O = 'O', + P = 'P', + Q = 'Q', + R = 'R', + S = 'S', + T = 'T', + U = 'U', + V = 'V', + W = 'W', + X = 'X', + Y = 'Y', + Z = 'Z', + LeftBracket = '[', + Backslash = '\\', + RightBracket = ']', + GraveAccent = '`', - // Special keys start at key code 256. - Escape = 256, - Tab, - Enter, - Backspace, - Insert, - Del, - Right, - Left, - Down, - Up, - PageUp, - PageDown, - Home, - End, - CapsLock, - ScrollLock, - NumLock, - PrintScreen, - Pause, - F1, - F2, - F3, - F4, - F5, - F6, - F7, - F8, - F9, - F10, - F11, - F12, - Keypad0, - Keypad1, - Keypad2, - Keypad3, - Keypad4, - Keypad5, - Keypad6, - Keypad7, - Keypad8, - Keypad9, - KeypadDel, - KeypadDivide, - KeypadMultiply, - KeypadSubtract, - KeypadAdd, - KeypadEnter, - KeypadEqual, - LeftShift, - LeftControl, - LeftAlt, - LeftSuper, // Windows key on windows - RightShift, - RightControl, - RightAlt, - RightSuper, // Windows key on windows - Menu, - Unknown, // Any unknown key code + // Special keys start at key code 256. + Escape = 256, + Tab, + Enter, + Backspace, + Insert, + Del, + Right, + Left, + Down, + Up, + PageUp, + PageDown, + Home, + End, + CapsLock, + ScrollLock, + NumLock, + PrintScreen, + Pause, + F1, + F2, + F3, + F4, + F5, + F6, + F7, + F8, + F9, + F10, + F11, + F12, + Keypad0, + Keypad1, + Keypad2, + Keypad3, + Keypad4, + Keypad5, + Keypad6, + Keypad7, + Keypad8, + Keypad9, + KeypadDel, + KeypadDivide, + KeypadMultiply, + KeypadSubtract, + KeypadAdd, + KeypadEnter, + KeypadEqual, + LeftShift, + LeftControl, + LeftAlt, + LeftSuper, // Windows key on windows + RightShift, + RightControl, + RightAlt, + RightSuper, // Windows key on windows + Menu, + Unknown, // Any unknown key code - Count, - }; - } + Count, +}; +} // namespace Input - /** Abstracts mouse messages - */ - struct MouseEvent +/** + * Abstracts mouse messages + */ +struct MouseEvent +{ + /** + * Message Type + */ + enum class Type { - /** Message Type - */ - enum class Type - { - ButtonDown, ///< Mouse button was pressed - ButtonUp, ///< Mouse button was released - Move, ///< Mouse cursor position changed - Wheel ///< Mouse wheel was scrolled - }; - - Type type; ///< Event Type. - float2 pos; ///< Normalized coordinates x,y in range [0, 1]. (0,0) is the top-left corner of the window. - float2 screenPos; ///< Screen-space coordinates in range [0, clientSize]. (0,0) is the top-left corner of the window. - float2 wheelDelta; ///< If the current event is CMouseEvent#Type#Wheel, the change in wheel scroll. Otherwise zero. - Input::ModifierFlags mods; ///< Keyboard modifier flags. Only valid if the event Type is one the button events - Input::MouseButton button; ///< Which button was active. Only valid if the event Type is ButtonDown or ButtonUp. + ButtonDown, ///< Mouse button was pressed + ButtonUp, ///< Mouse button was released + Move, ///< Mouse cursor position changed + Wheel ///< Mouse wheel was scrolled }; - struct KeyboardEvent + Type type; ///< Event Type. + float2 pos; ///< Normalized coordinates x,y in range [0, 1]. (0,0) is the top-left corner of the window. + float2 screenPos; ///< Screen-space coordinates in range [0, clientSize]. (0,0) is the top-left corner of the window. + float2 wheelDelta; ///< If the current event is CMouseEvent#Type#Wheel, the change in wheel scroll. Otherwise zero. + Input::ModifierFlags mods; ///< Keyboard modifier flags. Only valid if the event Type is one the button events + Input::MouseButton button; ///< Which button was active. Only valid if the event Type is ButtonDown or ButtonUp. +}; + +struct KeyboardEvent +{ + /** + * Keyboard event Type + */ + enum class Type { - /** Keyboard event Type - */ - enum class Type - { - KeyPressed, ///< Key was pressed. - KeyReleased, ///< Key was released. - KeyRepeated, ///< Key is repeatedly down. - Input ///< Character input - }; + KeyPressed, ///< Key was pressed. + KeyReleased, ///< Key was released. + KeyRepeated, ///< Key is repeatedly down. + Input ///< Character input + }; - Type type; ///< The event type - Input::Key key; ///< The last key that was pressed/released - Input::ModifierFlags mods; ///< Keyboard modifier flags - uint32_t codepoint = 0; ///< UTF-32 codepoint from GLFW for Input event types + Type type; ///< The event type + Input::Key key; ///< The last key that was pressed/released + Input::ModifierFlags mods; ///< Keyboard modifier flags + uint32_t codepoint = 0; ///< UTF-32 codepoint from GLFW for Input event types - bool hasModifier(Input::Modifier mod) const { return is_set(mods, (Input::ModifierFlags)mod); } - }; + bool hasModifier(Input::Modifier mod) const { return is_set(mods, (Input::ModifierFlags)mod); } +}; - enum class GamepadButton : uint32_t - { - A, - B, - X, - Y, - LeftBumper, - RightBumper, - Back, - Start, - Guide, - LeftThumb, - RightThumb, - Up, - Right, - Down, - Left, +enum class GamepadButton : uint32_t +{ + A, + B, + X, + Y, + LeftBumper, + RightBumper, + Back, + Start, + Guide, + LeftThumb, + RightThumb, + Up, + Right, + Down, + Left, - Count, - }; + Count, +}; - struct GamepadEvent +struct GamepadEvent +{ + enum class Type { - enum class Type - { - ButtonDown, - ButtonUp, - Connected, - Disconnected, - }; - - Type type; - GamepadButton button; + ButtonDown, + ButtonUp, + Connected, + Disconnected, }; - struct GamepadState - { - float leftX; - float leftY; - float rightX; - float rightY; - float leftTrigger; - float rightTrigger; + Type type; + GamepadButton button; +}; - std::bitset<(size_t)GamepadButton::Count> buttons; +struct GamepadState +{ + float leftX; + float leftY; + float rightX; + float rightY; + float leftTrigger; + float rightTrigger; - bool isButtonDown(GamepadButton button) const - { - uint32_t buttonIndex = (uint32_t) button; - return buttonIndex < (uint32_t)GamepadButton::Count ? buttons[buttonIndex] : false; - } - }; -} + std::bitset<(size_t)GamepadButton::Count> buttons; + + bool isButtonDown(GamepadButton button) const + { + uint32_t buttonIndex = (uint32_t)button; + return buttonIndex < (uint32_t)GamepadButton::Count ? buttons[buttonIndex] : false; + } +}; +} // namespace Falcor diff --git a/Source/Falcor/Utils/UI/PixelZoom.cpp b/Source/Falcor/Utils/UI/PixelZoom.cpp index 09f5b0b2b..d3f01d038 100644 --- a/Source/Falcor/Utils/UI/PixelZoom.cpp +++ b/Source/Falcor/Utils/UI/PixelZoom.cpp @@ -31,97 +31,100 @@ namespace Falcor { - static void clampToEdge(float2& pix, uint32_t width, uint32_t height, uint32_t offset) - { - float2 posOffset = pix + float2(offset, offset); - float2 negOffset = pix - float2(offset, offset); - - //x - if (posOffset.x > width) - { - pix.x = pix.x - (posOffset.x - width); - } - else if (negOffset.x < 0) - { - pix.x = pix.x - negOffset.x; - } +static void clampToEdge(float2& pix, uint32_t width, uint32_t height, uint32_t offset) +{ + float2 posOffset = pix + float2(offset, offset); + float2 negOffset = pix - float2(offset, offset); - //y - if (posOffset.y > height) - { - pix.y = pix.y - (posOffset.y - height); - } - else if (negOffset.y < 0) - { - pix.y = pix.y - negOffset.y; - } + // x + if (posOffset.x > width) + { + pix.x = pix.x - (posOffset.x - width); + } + else if (negOffset.x < 0) + { + pix.x = pix.x - negOffset.x; } - PixelZoom::PixelZoom(std::shared_ptr pDevice, const Fbo* pBackbuffer) - : mpDevice(std::move(pDevice)) + // y + if (posOffset.y > height) { - onResize(pBackbuffer); + pix.y = pix.y - (posOffset.y - height); } + else if (negOffset.y < 0) + { + pix.y = pix.y - negOffset.y; + } +} - void PixelZoom::onResize(const Fbo* pBackbuffer) +PixelZoom::PixelZoom(ref pDevice, const Fbo* pBackbuffer) : mpDevice(pDevice) +{ + onResize(pBackbuffer); +} + +void PixelZoom::onResize(const Fbo* pBackbuffer) +{ + FALCOR_ASSERT(pBackbuffer); + const Fbo::Desc& desc = pBackbuffer->getDesc(); + mpSrcBlitFbo = Fbo::create2D(mpDevice, pBackbuffer->getWidth(), pBackbuffer->getHeight(), desc); + if (mpDstBlitFbo == nullptr) { - FALCOR_ASSERT(pBackbuffer); - const Fbo::Desc& desc = pBackbuffer->getDesc(); - mpSrcBlitFbo = Fbo::create2D(mpDevice.get(), pBackbuffer->getWidth(), pBackbuffer->getHeight(), desc); - if (mpDstBlitFbo == nullptr) - { - mpDstBlitFbo = Fbo::create2D(mpDevice.get(), mDstZoomSize, mDstZoomSize, desc); - } + mpDstBlitFbo = Fbo::create2D(mpDevice, mDstZoomSize, mDstZoomSize, desc); } +} - void PixelZoom::render(RenderContext* pCtx, Fbo* backBuffer) +void PixelZoom::render(RenderContext* pCtx, Fbo* backBuffer) +{ + if (mShouldZoom) { - if (mShouldZoom) - { - //copy backbuffer into src blit fbo - pCtx->copyResource(mpSrcBlitFbo->getColorTexture(0).get(), backBuffer->getColorTexture(0).get()); + // copy backbuffer into src blit fbo + pCtx->copyResource(mpSrcBlitFbo->getColorTexture(0).get(), backBuffer->getColorTexture(0).get()); - //blit src blit fbo into dst blit fbo - uint32_t offset = mSrcZoomSize / 2; - float2 srcPix = float2(mMousePos.x * backBuffer->getWidth(), mMousePos.y * backBuffer->getHeight()); - clampToEdge(srcPix, backBuffer->getWidth(), backBuffer->getHeight(), offset); - float4 srcRect = float4(srcPix.x - offset, srcPix.y - offset, srcPix.x + offset, srcPix.y + offset); - float4 dstRect = float4(0, 0, mDstZoomSize, mDstZoomSize); - pCtx->blit(mpSrcBlitFbo->getColorTexture(0)->getSRV(), mpDstBlitFbo->getColorTexture(0)->getRTV(), srcRect, dstRect, Sampler::Filter::Point); + // blit src blit fbo into dst blit fbo + uint32_t offset = mSrcZoomSize / 2; + float2 srcPix = float2(mMousePos.x * backBuffer->getWidth(), mMousePos.y * backBuffer->getHeight()); + clampToEdge(srcPix, backBuffer->getWidth(), backBuffer->getHeight(), offset); + float4 srcRect = float4(srcPix.x - offset, srcPix.y - offset, srcPix.x + offset, srcPix.y + offset); + float4 dstRect = float4(0, 0, mDstZoomSize, mDstZoomSize); + pCtx->blit( + mpSrcBlitFbo->getColorTexture(0)->getSRV(), mpDstBlitFbo->getColorTexture(0)->getRTV(), srcRect, dstRect, Sampler::Filter::Point + ); - //blit dst blt fbo into back buffer - offset = mDstZoomSize / 2; - clampToEdge(srcPix, backBuffer->getWidth(), backBuffer->getHeight(), offset); - srcRect = dstRect; - dstRect = float4(srcPix.x - offset, srcPix.y - offset, srcPix.x + offset, srcPix.y + offset); - pCtx->blit(mpDstBlitFbo->getColorTexture(0)->getSRV(), backBuffer->getColorTexture(0)->getRTV(), srcRect, dstRect, Sampler::Filter::Point); - } + // blit dst blt fbo into back buffer + offset = mDstZoomSize / 2; + clampToEdge(srcPix, backBuffer->getWidth(), backBuffer->getHeight(), offset); + srcRect = dstRect; + dstRect = float4(srcPix.x - offset, srcPix.y - offset, srcPix.x + offset, srcPix.y + offset); + pCtx->blit( + mpDstBlitFbo->getColorTexture(0)->getSRV(), backBuffer->getColorTexture(0)->getRTV(), srcRect, dstRect, Sampler::Filter::Point + ); } +} - bool PixelZoom::onMouseEvent(const MouseEvent& me) +bool PixelZoom::onMouseEvent(const MouseEvent& me) +{ + if (mShouldZoom) { - if (mShouldZoom) - { - mMousePos = me.pos; - //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 = std::max(mSrcZoomSize + zoomDelta, 3); - return me.type != MouseEvent::Type::Move; // Do not inhibit other passes from receiving mouse movement events. - } - return false; + mMousePos = me.pos; + // 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 = std::max(mSrcZoomSize + zoomDelta, 3); + return me.type != MouseEvent::Type::Move; // Do not inhibit other passes from receiving mouse movement events. } + return false; +} - bool PixelZoom::onKeyboardEvent(const KeyboardEvent& ke) +bool PixelZoom::onKeyboardEvent(const KeyboardEvent& ke) +{ + if (ke.type == KeyboardEvent::Type::KeyPressed || ke.type == KeyboardEvent::Type::KeyReleased) { - if (ke.type == KeyboardEvent::Type::KeyPressed || ke.type == KeyboardEvent::Type::KeyReleased) + if (ke.key == Input::Key::Z) { - if (ke.key == Input::Key::Z) - { - mShouldZoom = (ke.type == KeyboardEvent::Type::KeyPressed); - return true; - } + mShouldZoom = (ke.type == KeyboardEvent::Type::KeyPressed); + return true; } - return false; } - + return false; } + +} // namespace Falcor diff --git a/Source/Falcor/Utils/UI/PixelZoom.h b/Source/Falcor/Utils/UI/PixelZoom.h index e846ae6f8..a9409172d 100644 --- a/Source/Falcor/Utils/UI/PixelZoom.h +++ b/Source/Falcor/Utils/UI/PixelZoom.h @@ -32,48 +32,53 @@ namespace Falcor { - class RenderContext; - struct MouseEvent; - struct KeyboardEvent; +class RenderContext; +struct MouseEvent; +struct KeyboardEvent; - /** Magnifies a region of the screen to assist with inspecting details - */ - class PixelZoom - { - public: - /// Constructor. Throws an exception if creation failed. - PixelZoom(std::shared_ptr pDevice, const Fbo* pBackbuffer); +/** + * Magnifies a region of the screen to assist with inspecting details + */ +class PixelZoom +{ +public: + /// Constructor. Throws an exception if creation failed. + PixelZoom(ref pDevice, const Fbo* pBackbuffer); - /** Does zoom operation if mShouldZoom is true (if ctrl+alt pressed this frame) - \param pCtx Pointer to the render context - \param backbuffer Pointer to the swap chain FBO - */ - void render(RenderContext* pCtx, Fbo* backBuffer); + /** + * Does zoom operation if mShouldZoom is true (if ctrl+alt pressed this frame) + * @param pCtx Pointer to the render context + * @param backbuffer Pointer to the swap chain FBO + */ + void render(RenderContext* pCtx, Fbo* backBuffer); - /** Stores data about mouse needed for zooming - \param me the mouse event - */ - bool onMouseEvent(const MouseEvent& me); + /** + * Stores data about mouse needed for zooming + * @param me the mouse event + */ + bool onMouseEvent(const MouseEvent& me); - /** Checks if it should zoom - \param ke Keyboard event - */ - bool onKeyboardEvent(const KeyboardEvent& ke); + /** + * Checks if it should zoom + * @param ke Keyboard event + */ + bool onKeyboardEvent(const KeyboardEvent& ke); - /** Handle resize events - */ - void onResize(const Fbo* pBackbuffer); + /** + * Handle resize events + */ + void onResize(const Fbo* pBackbuffer); - private: - std::shared_ptr mpDevice; +private: + ref mpDevice; - int32_t mSrcZoomSize = 5; - const uint32_t mDstZoomSize = 200; - const uint32_t mZoomCoefficient = 4; + int32_t mSrcZoomSize = 5; + const uint32_t mDstZoomSize = 200; + const uint32_t mZoomCoefficient = 4; - Fbo::SharedPtr mpSrcBlitFbo; - Fbo::SharedPtr mpDstBlitFbo; - float2 mMousePos = {}; - bool mShouldZoom = false; - }; -} + ref mpSrcBlitFbo; + ref mpDstBlitFbo; + float2 mMousePos = {}; + bool mShouldZoom = false; +}; +} // namespace Falcor diff --git a/Source/Falcor/Utils/UI/SpectrumUI.cpp b/Source/Falcor/Utils/UI/SpectrumUI.cpp index 0fa7cd19c..d8b341089 100644 --- a/Source/Falcor/Utils/UI/SpectrumUI.cpp +++ b/Source/Falcor/Utils/UI/SpectrumUI.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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,450 +33,487 @@ namespace Falcor { - template - SpectrumUI::SpectrumUI() - { - } +template +SpectrumUI::SpectrumUI() +{} - template - SpectrumUI::SpectrumUI(const float2& wavelengthRange, const float2& spectralIntensityRange) +template +SpectrumUI::SpectrumUI(const float2& wavelengthRange, const float2& spectralIntensityRange) +{ + mWavelengthRange = wavelengthRange; + mSpectralIntensityRange = spectralIntensityRange; +} + +// Function for making a ImGui string unique by adding an invisible (##) number (the address of this) to the string. +template +std::string SpectrumUI::makeUnique(const std::string& str) const +{ + return str + "##" + std::to_string(reinterpret_cast(this)); +} + +// Get spectral intensity at a certain wavelength for a spectrum, with curveIndex selecting one of the three components if T=float3. +template +float SpectrumUI::getSpectralIntensity(const float wavelength, const SampledSpectrum* spectrum, const uint32_t curveIndex) const +{ + T spectralIntensity = spectrum->eval(wavelength, mInterpolationType); + float s = 0.0f; + if constexpr (std::is_same_v) { - mWavelengthRange = wavelengthRange; - mSpectralIntensityRange = spectralIntensityRange; + s = spectralIntensity; } - - // Function for making a ImGui string unique by adding an invisible (##) number (the address of this) to the string. - template - std::string SpectrumUI::makeUnique(const std::string& str) const + else if constexpr (std::is_same_v) { - return str + "##" + std::to_string(reinterpret_cast(this)); + s = spectralIntensity[curveIndex % 3]; } - - // Get spectral intensity at a certain wavelength for a spectrum, with curveIndex selecting one of the three components if T=float3. - template - float SpectrumUI::getSpectralIntensity(const float wavelength, const SampledSpectrum* spectrum, const uint32_t curveIndex) const + else { - T spectralIntensity = spectrum->eval(wavelength, mInterpolationType); - float s = 0.0f; - if constexpr (std::is_same_v) - { - s = spectralIntensity; - } - else if constexpr (std::is_same_v) - { - s = spectralIntensity[curveIndex % 3]; - } - else - { - FALCOR_ASSERT(false); - } - return s; + FALCOR_ASSERT(false); } + return s; +} - // Get spectral intensity at a certain index for a spectrum, with curveIndex selecting one of the three components if T=float3. - template - float SpectrumUI::getSpectralIntensity(const uint32_t pointIndex, const SampledSpectrum* spectrum, const uint32_t curveIndex) const +// Get spectral intensity at a certain index for a spectrum, with curveIndex selecting one of the three components if T=float3. +template +float SpectrumUI::getSpectralIntensity(const uint32_t pointIndex, const SampledSpectrum* spectrum, const uint32_t curveIndex) const +{ + T spectralIntensity = spectrum->get(pointIndex); + float s = 0.0f; + if constexpr (std::is_same_v) { - T spectralIntensity = spectrum->get(pointIndex); - float s = 0.0f; - if constexpr (std::is_same_v) - { - s = spectralIntensity; - } - else if constexpr (std::is_same_v) - { - s = spectralIntensity[curveIndex % 3]; - } - else - { - FALCOR_ASSERT(false); - } - return s; + s = spectralIntensity; } - - // Get the number of components of T. - template - uint32_t SpectrumUI::getNumComponents() const + else if constexpr (std::is_same_v) { - if constexpr (std::is_same_v) - { - return 3u; - } - return 1u; + s = spectralIntensity[curveIndex % 3]; } - - // Utility function for rendering text centered horizontally. - template - void SpectrumUI::textHorizontallyCentered(const std::string& text, const float2& pos, const float4& color) + else { - float strWidth = ImGui::CalcTextSize(text.c_str()).x; - ImGui::SetCursorPosX(pos.x - strWidth * 0.5f); - ImGui::SetCursorPosY(pos.y); - ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(color.x, color.y, color.z, color.w)); - ImGui::TextUnformatted(text.c_str()); - ImGui::PopStyleColor(); + FALCOR_ASSERT(false); } + return s; +} - // Utility function for rendering text centered vertically to the left of a certain position. - template - void SpectrumUI::textVerticallyCenteredLeft(const std::string& text, const float2& pos, const float4& color) +// Get the number of components of T. +template +uint32_t SpectrumUI::getNumComponents() const +{ + if constexpr (std::is_same_v) { - ImVec2 strSize = ImGui::CalcTextSize(text.c_str()); - ImGui::SetCursorPosX(pos.x - strSize.x); - ImGui::SetCursorPosY(pos.y - strSize.y * 0.5f); - ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(color.x, color.y, color.z, color.w)); - ImGui::TextUnformatted(text.c_str()); - ImGui::PopStyleColor(); + return 3u; } + return 1u; +} - // Utility function for drawing a line with ImGui. - template - void SpectrumUI::drawLine(ImDrawList* drawList, const float2& canvasPos, const float2& point0, const float2& point1, const float4& color, const float lineWidth) +// Utility function for rendering text centered horizontally. +template +void SpectrumUI::textHorizontallyCentered(const std::string& text, const float2& pos, const float4& color) +{ + float strWidth = ImGui::CalcTextSize(text.c_str()).x; + ImGui::SetCursorPosX(pos.x - strWidth * 0.5f); + ImGui::SetCursorPosY(pos.y); + ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(color.x, color.y, color.z, color.w)); + ImGui::TextUnformatted(text.c_str()); + ImGui::PopStyleColor(); +} + +// Utility function for rendering text centered vertically to the left of a certain position. +template +void SpectrumUI::textVerticallyCenteredLeft(const std::string& text, const float2& pos, const float4& color) +{ + ImVec2 strSize = ImGui::CalcTextSize(text.c_str()); + ImGui::SetCursorPosX(pos.x - strSize.x); + ImGui::SetCursorPosY(pos.y - strSize.y * 0.5f); + ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(color.x, color.y, color.z, color.w)); + ImGui::TextUnformatted(text.c_str()); + ImGui::PopStyleColor(); +} + +// Utility function for drawing a line with ImGui. +template +void SpectrumUI::drawLine( + ImDrawList* drawList, + const float2& canvasPos, + const float2& point0, + const float2& point1, + const float4& color, + const float lineWidth +) +{ + drawList->AddLine( + ImVec2(canvasPos.x + point0.x, canvasPos.y + point0.y), ImVec2(canvasPos.x + point1.x, canvasPos.y + point1.y), + ImColor(color.x, color.y, color.z, color.w), lineWidth + ); +} + +// Utility function for drawing a filled circle with ImGui. +template +void SpectrumUI::drawCircle(ImDrawList* drawList, const float2& canvasPos, const float2& center, const float radius, const float4& color) +{ + drawList->AddCircleFilled(ImVec2(canvasPos.x + center.x, canvasPos.y + center.y), radius, ImColor(color.x, color.y, color.z, color.w)); +} + +// Convert from wavelengh to coordinate inside ImGui. +template +float SpectrumUI::toXCoord(const float wavelength, const float2& xAxisRange) const +{ + const float t = (wavelength - mWavelengthRange.x) / (mWavelengthRange.y - mWavelengthRange.x); // In [0,1]. + return xAxisRange.x + t * (xAxisRange.y - xAxisRange.x); +} + +// Convert from spectral intensity to coordinate inside ImGui. +template +float SpectrumUI::toYCoord(const float spectralIntensity, const float2& yAxisRange) const +{ + const float t = (spectralIntensity - mSpectralIntensityRange.x) / (mSpectralIntensityRange.y - mSpectralIntensityRange.x); // In [0,1]. + return yAxisRange.x + t * (yAxisRange.y - yAxisRange.x); +} + +// Convert from (wavelengh, spectral intensity) to coordinates inside ImGui. +template +float2 SpectrumUI::toCoords(const float wavelength, const float spectralIntensity, const float2& xAxisRange, const float2& yAxisRange) + const +{ + return float2(toXCoord(wavelength, xAxisRange), toYCoord(spectralIntensity, yAxisRange)); +} + +// Convert a certain point (with index) from the Spectrum to coordinates inside ImGui. +template +float2 SpectrumUI::toCoords( + const SampledSpectrum* spectrum, + const int index, + const float2& xAxisRange, + const float2& yAxisRange, + const uint32_t float3Index +) const +{ + const int idx = std::clamp(index, 0, int(spectrum->size() - 1)); + const float deltaW = (spectrum->getWavelengthRange().y - spectrum->getWavelengthRange().x) / (spectrum->size() - 1.0f); + const float wavelength = spectrum->getWavelengthRange().x + idx * deltaW; + + T spectralIntensity = spectrum->get(idx); + if constexpr (std::is_same_v) { - drawList->AddLine(ImVec2(canvasPos.x + point0.x, canvasPos.y + point0.y), ImVec2(canvasPos.x + point1.x, canvasPos.y + point1.y), ImColor(color.x, color.y, color.z, color.w), lineWidth); + return toCoords(wavelength, spectralIntensity, xAxisRange, yAxisRange); } - - // Utility function for drawing a filled circle with ImGui. - template - void SpectrumUI::drawCircle(ImDrawList* drawList, const float2& canvasPos, const float2& center, const float radius, const float4& color) + else if constexpr (std::is_same_v) { - drawList->AddCircleFilled(ImVec2(canvasPos.x + center.x, canvasPos.y + center.y), radius, ImColor(color.x, color.y, color.z, color.w)); + return toCoords(wavelength, spectralIntensity[float3Index], xAxisRange, yAxisRange); } + // Should never happen. + FALCOR_ASSERT(false); +} - // Convert from wavelengh to coordinate inside ImGui. - template - float SpectrumUI::toXCoord(const float wavelength, const float2& xAxisRange) const +// Drawing all three color matching functions. +template +void SpectrumUI::drawColorMatchingFunctions( + ImDrawList* drawList, + const float2& canvasPos, + const float2& canvasSize, + const float2& xAxisRange, + const float2& yAxisRange +) +{ + if (!mDrawColorMatchingFunctions) { - const float t = (wavelength - mWavelengthRange.x) / (mWavelengthRange.y - mWavelengthRange.x); // In [0,1]. - return xAxisRange.x + t * (xAxisRange.y - xAxisRange.x); + return; } - - // Convert from spectral intensity to coordinate inside ImGui. - template - float SpectrumUI::toYCoord(const float spectralIntensity, const float2& yAxisRange) const + const float4 R = float4(0.8f, 0.0f, 0.0f, 1.0f); + const float4 G = float4(0.0f, 0.7f, 0.0f, 1.0f); + const float4 B = float4(0.0f, 0.0f, 1.0f, 1.0f); + const uint32_t loop = uint32_t(canvasSize.x / 4.0f); + const float waveLengthDelta = (mWavelengthRange.y - mWavelengthRange.x) / (loop - 1.0f); + + float4 P, pPrev; + for (uint32_t q = 0; q <= loop; q++) { - const float t = (spectralIntensity - mSpectralIntensityRange.x) / (mSpectralIntensityRange.y - mSpectralIntensityRange.x); // In [0,1]. - return yAxisRange.x + t * (yAxisRange.y - yAxisRange.x); + float wavelength = mWavelengthRange.x + waveLengthDelta * q; + float3 xyzBar = SpectrumUtils::wavelengthToXYZ_CIE1931(wavelength); + float2 tmp = toCoords(wavelength, xyzBar.x, xAxisRange, yAxisRange); + P.w = tmp.x; + P.x = tmp.y; + P.y = toCoords(wavelength, xyzBar.y, xAxisRange, yAxisRange).y; + P.z = toCoords(wavelength, xyzBar.z, xAxisRange, yAxisRange).y; + if (q > 0) + { + drawLine(drawList, canvasPos, float2(pPrev.w, pPrev.x), float2(P.w, P.x), R, 1.0f); + drawLine(drawList, canvasPos, float2(pPrev.w, pPrev.y), float2(P.w, P.y), G, 1.0f); + drawLine(drawList, canvasPos, float2(pPrev.w, pPrev.z), float2(P.w, P.z), B, 1.0f); + } + pPrev = P; } +} - // Convert from (wavelengh, spectral intensity) to coordinates inside ImGui. - template - float2 SpectrumUI::toCoords(const float wavelength, const float spectralIntensity, const float2& xAxisRange, const float2& yAxisRange) const +// Based on "Nice Numbers for Graph Labels" by Paul Heckbert from "Graphics Gems", Academic Press, 1990. +template +float SpectrumUI::generateNiceNumber(const float x) const +{ + float nf; // Nice, rounded fraction. + const int expv = (int)std::floor(std::log10(x)); // Exponent of x. + const float f = x / std::pow(10.0f, float(expv)); // Between 1 and 10. + if (f < 1.5f) { - return float2(toXCoord(wavelength, xAxisRange), toYCoord(spectralIntensity, yAxisRange)); + nf = 1.0f; } - - // Convert a certain point (with index) from the Spectrum to coordinates inside ImGui. - template - float2 SpectrumUI::toCoords(const SampledSpectrum* spectrum, const int index, const float2& xAxisRange, const float2& yAxisRange, const uint32_t float3Index) const + else if (f < 3.0f) { - const int idx = std::clamp(index, 0, int(spectrum->size() - 1)); - const float deltaW = (spectrum->getWavelengthRange().y - spectrum->getWavelengthRange().x) / (spectrum->size() - 1.0f); - const float wavelength = spectrum->getWavelengthRange().x + idx * deltaW; - - T spectralIntensity = spectrum->get(idx); - if constexpr (std::is_same_v) - { - return toCoords(wavelength, spectralIntensity, xAxisRange, yAxisRange); - } - else if constexpr (std::is_same_v) - { - return toCoords(wavelength, spectralIntensity[float3Index], xAxisRange, yAxisRange); - } - FALCOR_ASSERT(false); // Should never happen. + nf = 2.0f; } - - // Drawing all three color matching functions. - template - void SpectrumUI::drawColorMatchingFunctions(ImDrawList* drawList, const float2& canvasPos, const float2& canvasSize, const float2& xAxisRange, const float2& yAxisRange) + else if (f < 7.0f) { - if (!mDrawColorMatchingFunctions) - { - return; - } - const float4 R = float4(0.8f, 0.0f, 0.0f, 1.0f); - const float4 G = float4(0.0f, 0.7f, 0.0f, 1.0f); - const float4 B = float4(0.0f, 0.0f, 1.0f, 1.0f); - const uint32_t loop = uint32_t(canvasSize.x / 4.0f); - const float waveLengthDelta = (mWavelengthRange.y - mWavelengthRange.x) / (loop - 1.0f); - - float4 P, pPrev; - for (uint32_t q = 0; q <= loop; q++) - { - float wavelength = mWavelengthRange.x + waveLengthDelta * q; - float3 xyzBar = SpectrumUtils::wavelengthToXYZ_CIE1931(wavelength); - float2 tmp = toCoords(wavelength, xyzBar.x, xAxisRange, yAxisRange); - P.w = tmp.x; - P.x = tmp.y; - P.y = toCoords(wavelength, xyzBar.y, xAxisRange, yAxisRange).y; - P.z = toCoords(wavelength, xyzBar.z, xAxisRange, yAxisRange).y; - if (q > 0) - { - drawLine(drawList, canvasPos, float2(pPrev.w, pPrev.x), float2(P.w, P.x), R, 1.0f); - drawLine(drawList, canvasPos, float2(pPrev.w, pPrev.y), float2(P.w, P.y), G, 1.0f); - drawLine(drawList, canvasPos, float2(pPrev.w, pPrev.z), float2(P.w, P.z), B, 1.0f); - } - pPrev = P; - } + nf = 5.0f; } - - // Based on "Nice Numbers for Graph Labels" by Paul Heckbert from "Graphics Gems", Academic Press, 1990. - template - float SpectrumUI::generateNiceNumber(const float x) const + else { - float nf; // Nice, rounded fraction. - const int expv = (int)std::floor(std::log10(x)); // Exponent of x. - const float f = x / std::pow(10.0f, float(expv)); // Between 1 and 10. - if (f < 1.5f) - { - nf = 1.0f; - } - else if (f < 3.0f) - { - nf = 2.0f; - } - else if (f < 7.0f) - { - nf = 5.0f; - } - else - { - nf = 10.0f; - } - return nf * std::pow(10.0f, float(expv)); + nf = 10.0f; } + return nf * std::pow(10.0f, float(expv)); +} - - // Draw ticks on the x-axis and text below. - template - void SpectrumUI::drawTextWavelengthsAndTicks(ImDrawList* drawList, const float2& canvasPos, const float2& xAxisRange, const float2& yAxisRange, const float4& textColor, const float4& tickColor, const float4& gridColor) +// Draw ticks on the x-axis and text below. +template +void SpectrumUI::drawTextWavelengthsAndTicks( + ImDrawList* drawList, + const float2& canvasPos, + const float2& xAxisRange, + const float2& yAxisRange, + const float4& textColor, + const float4& tickColor, + const float4& gridColor +) +{ + const float halfTickSize = 4.0f; + auto renderTextAndTick = + [&](ImDrawList* drawList, const float wavelength, const float2& canvasPos, const float2& xAxisRange, const float2& yAxisRange) { - const float halfTickSize = 4.0f; - auto renderTextAndTick = [&](ImDrawList* drawList, const float wavelength, const float2& canvasPos, const float2& xAxisRange, const float2& yAxisRange) + float x = toXCoord(wavelength, xAxisRange); + const auto str = fmt::format("{}", uint32_t(std::round(wavelength))); + textHorizontallyCentered(str, float2(x, yAxisRange.x + 2.0f), textColor); + if (mDrawGridX) { - float x = toXCoord(wavelength, xAxisRange); - const auto str = fmt::format("{}", uint32_t(std::round(wavelength))); - textHorizontallyCentered(str, float2(x, yAxisRange.x + 2.0f), textColor); - if (mDrawGridX) - { - drawLine(drawList, canvasPos, float2(x, yAxisRange.x), float2(x, yAxisRange.y), gridColor, 1.0f); - } - drawLine(drawList, canvasPos, float2(x, yAxisRange.x - halfTickSize), float2(x, yAxisRange.x + halfTickSize), tickColor, 2.0f); - return x; - }; + drawLine(drawList, canvasPos, float2(x, yAxisRange.x), float2(x, yAxisRange.y), gridColor, 1.0f); + } + drawLine(drawList, canvasPos, float2(x, yAxisRange.x - halfTickSize), float2(x, yAxisRange.x + halfTickSize), tickColor, 2.0f); + return x; + }; - float strWidth = ImGui::CalcTextSize("000").x; - float w = xAxisRange.y - xAxisRange.x; - float diff = mWavelengthRange.y - mWavelengthRange.x; + float strWidth = ImGui::CalcTextSize("000").x; + float w = xAxisRange.y - xAxisRange.x; + float diff = mWavelengthRange.y - mWavelengthRange.x; - // Always render the mWavelengthRange.x and mWavelengthRange.y. - float xStart = renderTextAndTick(drawList, mWavelengthRange.x, canvasPos, xAxisRange, yAxisRange); - float xEnd = renderTextAndTick(drawList, mWavelengthRange.y, canvasPos, xAxisRange, yAxisRange); + // Always render the mWavelengthRange.x and mWavelengthRange.y. + float xStart = renderTextAndTick(drawList, mWavelengthRange.x, canvasPos, xAxisRange, yAxisRange); + float xEnd = renderTextAndTick(drawList, mWavelengthRange.y, canvasPos, xAxisRange, yAxisRange); - const uint32_t approxNumTicks = uint32_t(std::floor(w / (2.0f * strWidth))); - if (approxNumTicks <= 1) - { - return; - } - const float approxWidth = diff / (approxNumTicks - 1.0f); - const float delta = generateNiceNumber(approxWidth); - float wavelength = std::floor(mWavelengthRange.x / delta) * delta + delta; - while (wavelength <= mWavelengthRange.y) + const uint32_t approxNumTicks = uint32_t(std::floor(w / (2.0f * strWidth))); + if (approxNumTicks <= 1) + { + return; + } + const float approxWidth = diff / (approxNumTicks - 1.0f); + const float delta = generateNiceNumber(approxWidth); + float wavelength = std::floor(mWavelengthRange.x / delta) * delta + delta; + while (wavelength <= mWavelengthRange.y) + { + float x = toXCoord(wavelength, xAxisRange); + float diffStart = std::abs(x - xStart); + float diffEnd = std::abs(x - xEnd); + if ((diffStart < diffEnd && diffStart > strWidth * 1.5f) || (diffStart >= diffEnd && diffEnd > strWidth * 1.5f)) { - float x = toXCoord(wavelength, xAxisRange); - float diffStart = std::abs(x - xStart); - float diffEnd = std::abs(x - xEnd); - if ((diffStart < diffEnd && diffStart > strWidth * 1.5f) || (diffStart >= diffEnd && diffEnd > strWidth * 1.5f)) - { - renderTextAndTick(drawList, wavelength, canvasPos, xAxisRange, yAxisRange); - } - wavelength += delta; + renderTextAndTick(drawList, wavelength, canvasPos, xAxisRange, yAxisRange); } + wavelength += delta; } +} - // Draw ticks on the y-axis and text to the left. - template - void SpectrumUI::drawTextSpectralIntensityAndTicks(ImDrawList* drawList, const float2& canvasPos, const float2& xAxisRange, const float2& yAxisRange, const float4& textColor, const float4& tickColor, const float4& gridColor) +// Draw ticks on the y-axis and text to the left. +template +void SpectrumUI::drawTextSpectralIntensityAndTicks( + ImDrawList* drawList, + const float2& canvasPos, + const float2& xAxisRange, + const float2& yAxisRange, + const float4& textColor, + const float4& tickColor, + const float4& gridColor +) +{ + const float halfTickSize = 4.0f; + auto renderTextAndTick = [&](ImDrawList* drawList, const float spectralIntensity, const float2& canvasPos, const float2& xAxisRange, + const float2& yAxisRange, const bool first = false) { - const float halfTickSize = 4.0f; - auto renderTextAndTick = [&](ImDrawList* drawList, const float spectralIntensity, const float2& canvasPos, const float2& xAxisRange, const float2& yAxisRange, const bool first = false) + const auto str = fmt::format("{:1.1f}", spectralIntensity); + float y = toYCoord(spectralIntensity, yAxisRange); + textVerticallyCenteredLeft(first ? "0" : str, float2(xAxisRange.x - (first ? 18.0f : 7.0f), y), textColor); + if (mDrawGridY) { - const auto str = fmt::format("{:1.1f}", spectralIntensity); - float y = toYCoord(spectralIntensity, yAxisRange); - textVerticallyCenteredLeft(first ? "0" : str, float2(xAxisRange.x - (first ? 18.0f : 7.0f), y), textColor); - if (mDrawGridY) - { - drawLine(drawList, canvasPos, float2(xAxisRange.x, y), float2(xAxisRange.y, y), gridColor, 1.0f); - } - drawLine(drawList, canvasPos, float2(xAxisRange.x - halfTickSize, y), float2(xAxisRange.x + halfTickSize, y), tickColor, 2.0f); - return y; - }; + drawLine(drawList, canvasPos, float2(xAxisRange.x, y), float2(xAxisRange.y, y), gridColor, 1.0f); + } + drawLine(drawList, canvasPos, float2(xAxisRange.x - halfTickSize, y), float2(xAxisRange.x + halfTickSize, y), tickColor, 2.0f); + return y; + }; - const float strHeight = ImGui::CalcTextSize("0").y; - const float h = yAxisRange.x - yAxisRange.y; // In pixels (x >= y) since y points downwards. - const float diff = mSpectralIntensityRange.y - mSpectralIntensityRange.x; + const float strHeight = ImGui::CalcTextSize("0").y; + const float h = yAxisRange.x - yAxisRange.y; // In pixels (x >= y) since y points downwards. + const float diff = mSpectralIntensityRange.y - mSpectralIntensityRange.x; - // Always render the mSpectralIntensityRange.x and mSpectralIntensityRange.y. - float yStart = renderTextAndTick(drawList, mSpectralIntensityRange.x, canvasPos, xAxisRange, yAxisRange, true); - float yEnd = renderTextAndTick(drawList, mSpectralIntensityRange.y, canvasPos, xAxisRange, yAxisRange); + // Always render the mSpectralIntensityRange.x and mSpectralIntensityRange.y. + float yStart = renderTextAndTick(drawList, mSpectralIntensityRange.x, canvasPos, xAxisRange, yAxisRange, true); + float yEnd = renderTextAndTick(drawList, mSpectralIntensityRange.y, canvasPos, xAxisRange, yAxisRange); - const uint32_t approxNumTicks = uint32_t(h / (2.0f * strHeight)); - if (approxNumTicks <= 1) - { - return; - } - const float approxWidth = diff / (approxNumTicks - 1.0f); - const float delta = generateNiceNumber(approxWidth); - float spectralIntensity = std::floor(mSpectralIntensityRange.x / delta) * delta + delta; - while (spectralIntensity <= mSpectralIntensityRange.y) + const uint32_t approxNumTicks = uint32_t(h / (2.0f * strHeight)); + if (approxNumTicks <= 1) + { + return; + } + const float approxWidth = diff / (approxNumTicks - 1.0f); + const float delta = generateNiceNumber(approxWidth); + float spectralIntensity = std::floor(mSpectralIntensityRange.x / delta) * delta + delta; + while (spectralIntensity <= mSpectralIntensityRange.y) + { + float y = toYCoord(spectralIntensity, yAxisRange); + float diffStart = std::abs(y - yStart); + float diffEnd = std::abs(y - yEnd); + if ((diffStart < diffEnd && diffStart > strHeight * 1.5f) || (diffStart >= diffEnd && diffEnd > strHeight * 1.5f)) { - float y = toYCoord(spectralIntensity, yAxisRange); - float diffStart = std::abs(y - yStart); - float diffEnd = std::abs(y - yEnd); - if ((diffStart < diffEnd && diffStart > strHeight * 1.5f) || (diffStart >= diffEnd && diffEnd > strHeight * 1.5f)) - { - renderTextAndTick(drawList, spectralIntensity, canvasPos, xAxisRange, yAxisRange); - } - spectralIntensity += delta; + renderTextAndTick(drawList, spectralIntensity, canvasPos, xAxisRange, yAxisRange); } + spectralIntensity += delta; } +} - // Draw a spectrum bar where the x-axis is. - template - void SpectrumUI::drawSpectrumBar(ImDrawList* drawList, const float2& canvasPos, const float2& xAxisRange, const float2& yAxisRange, SampledSpectrum* spectrum, const bool multiplyBySpectrum) +// Draw a spectrum bar where the x-axis is. +template +void SpectrumUI::drawSpectrumBar( + ImDrawList* drawList, + const float2& canvasPos, + const float2& xAxisRange, + const float2& yAxisRange, + SampledSpectrum* spectrum, + const bool multiplyBySpectrum +) +{ + const float w = mWavelengthRange.y - mWavelengthRange.x; + if (w == 0.0f) { - const float w = mWavelengthRange.y - mWavelengthRange.x; - if (w == 0.0f) + return; + } + for (float x = xAxisRange.x; x <= xAxisRange.y; x += 1.f) + { + float wavelength = w * (x - xAxisRange.x) / (xAxisRange.y - xAxisRange.x) + mWavelengthRange.x; + float spectralIntensity = getSpectralIntensity(wavelength, spectrum, mEditSpectrumIndex); + float3 color = SpectrumUtils::wavelengthToRGB_Rec709(wavelength) * (multiplyBySpectrum ? spectralIntensity : 1.0f); + drawLine( + drawList, canvasPos, float2(x, yAxisRange.x - 3.0f), float2(x, yAxisRange.x + 3.0f), float4(sRGBToLinear(color), 1.0f), 1.0f + ); + } +} + +// Draw the spectrum curve with lines and circles at the samples with the chosen type of interpolation. +template +void SpectrumUI::drawSpectrumCurve( + ImDrawList* drawList, + const float2& canvasPos, + const float2& canvasSize, + const float2& xAxisRange, + const float2& yAxisRange, + SampledSpectrum* spectrum, + const uint32_t spectrumIndex +) +{ + const float deltaW = (spectrum->getWavelengthRange().y - spectrum->getWavelengthRange().x) / (spectrum->size() - 1.0f); + + // This loop is 1 for T=float and 3 when T=float3. + uint32_t numComponents = getNumComponents(); + float4 lineColor; + for (uint32_t index = 0; index < numComponents; index++) + { + bool lineNeedHighlight = spectrumIndex * numComponents + index == mEditSpectrumIndex; + float lineWidth = lineNeedHighlight ? 3.0f : 2.0f; + if (numComponents == 3) { - return; + const float4 colors[] = {float4(0.8f, 0.0f, 0.0f, 1.0f), float4(0.0f, 0.7f, 0.0f, 1.0f), float4(0.0f, 0.0f, 1.0f, 1.0f)}; + lineColor = colors[index] * (lineNeedHighlight ? 1.0f : 0.7f); } - for (float x = xAxisRange.x; x <= xAxisRange.y; x += 1.f) + else { - float wavelength = w * (x - xAxisRange.x) / (xAxisRange.y - xAxisRange.x) + mWavelengthRange.x; - float spectralIntensity = getSpectralIntensity(wavelength, spectrum, mEditSpectrumIndex); - float3 color = SpectrumUtils::wavelengthToRGB_Rec709(wavelength) * (multiplyBySpectrum ? spectralIntensity : 1.0f); - drawLine(drawList, canvasPos, float2(x, yAxisRange.x - 3.0f), float2(x, yAxisRange.x + 3.0f), float4(sRGBToLinear(color), 1.0f), 1.0f); + lineColor = float4(0.8f, 0.8f, 0.8f, 1.0f) * (lineNeedHighlight ? 1.0f : 0.5f); } - } - - // Draw the spectrum curve with lines and circles at the samples with the chosen type of interpolation. - template - void SpectrumUI::drawSpectrumCurve(ImDrawList* drawList, const float2& canvasPos, const float2& canvasSize, const float2& xAxisRange, const float2& yAxisRange, - SampledSpectrum* spectrum, const uint32_t spectrumIndex) - { - const float deltaW = (spectrum->getWavelengthRange().y - spectrum->getWavelengthRange().x) / (spectrum->size() - 1.0f); - - // This loop is 1 for T=float and 3 when T=float3. - uint32_t numComponents = getNumComponents(); - float4 lineColor; - for (uint32_t index = 0; index < numComponents; index++) + // Draw the curve between two points. + for (uint32_t q = 1; q < spectrum->size(); q++) { - bool lineNeedHighlight = spectrumIndex * numComponents + index == mEditSpectrumIndex; - float lineWidth = lineNeedHighlight ? 3.0f : 2.0f; - if (numComponents == 3) + float2 p0 = toCoords(spectrum, q - 1, xAxisRange, yAxisRange, index); + float2 p1 = toCoords(spectrum, q, xAxisRange, yAxisRange, index); + const uint32_t numLines = 10; + if (mInterpolationType == SpectrumInterpolation::Linear) { - const float4 colors[] = { float4(0.8, 0.0f, 0.0f, 1.0f), float4(0.0, 0.7f, 0.0f, 1.0f), float4(0.0, 0.0f, 1.0f, 1.0f) }; - lineColor = colors[index] * (lineNeedHighlight ? 1.0f : 0.7f); + drawLine(drawList, canvasPos, p0, p1, lineColor, lineWidth); } else { - lineColor = float4(0.8f, 0.8f, 0.8f, 1.0f) * (lineNeedHighlight ? 1.0f : 0.5f); - } - // Draw the curve between two points. - for (uint32_t q = 1; q < spectrum->size(); q++) - { - float2 p0 = toCoords(spectrum, q - 1, xAxisRange, yAxisRange, index); - float2 p1 = toCoords(spectrum, q, xAxisRange, yAxisRange, index); - const uint32_t numLines = 10; - if (mInterpolationType == SpectrumInterpolation::Linear) - { - drawLine(drawList, canvasPos, p0, p1, lineColor, lineWidth); - } - else - { - FALCOR_ASSERT(false); // WIP: Will add more modeas in another MR. - } + // WIP: Will add more modes in another MR. + FALCOR_ASSERT(false); } } - for (uint32_t index = 0; index < getNumComponents(); index++) - { - // Draw the points. - const float radius = 5.0f; - for (uint32_t q = 0; q < spectrum->size(); q++) - { - float wavelength = spectrum->getWavelengthRange().x + q * deltaW; - float spectralIntensity = (q == uint32_t(spectrum->size() - 1)) ? getSpectralIntensity(q, spectrum, index) : getSpectralIntensity(wavelength, spectrum, index); - float2 P = toCoords(wavelength, spectralIntensity, xAxisRange, yAxisRange); - float3 color = SpectrumUtils::wavelengthToRGB_Rec709(wavelength) * (mMultiplyWithSpectralIntensity ? spectralIntensity : 1.0f); - drawCircle(drawList, canvasPos, P, radius, float4(sRGBToLinear(color), 1.0f)); - } + } + for (uint32_t index = 0; index < getNumComponents(); index++) + { + // Draw the points. + const float radius = 5.0f; + for (uint32_t q = 0; q < spectrum->size(); q++) + { + float wavelength = spectrum->getWavelengthRange().x + q * deltaW; + float spectralIntensity = (q == uint32_t(spectrum->size() - 1)) ? getSpectralIntensity(q, spectrum, index) + : getSpectralIntensity(wavelength, spectrum, index); + float2 P = toCoords(wavelength, spectralIntensity, xAxisRange, yAxisRange); + float3 color = SpectrumUtils::wavelengthToRGB_Rec709(wavelength) * (mMultiplyWithSpectralIntensity ? spectralIntensity : 1.0f); + drawCircle(drawList, canvasPos, P, radius, float4(sRGBToLinear(color), 1.0f)); } } +} - // Handle all the mouse interactions: moving the spectrumm points. - template - bool SpectrumUI::handleMouse(const float2& canvasPos, const float2& canvasSize, const float2& xAxisRange, const float2& yAxisRange, ImDrawList* drawList, SampledSpectrum* spectrum, const uint32_t float3Index) +// Handle all the mouse interactions: moving the spectrumm points. +template +bool SpectrumUI::handleMouse( + const float2& canvasPos, + const float2& canvasSize, + const float2& xAxisRange, + const float2& yAxisRange, + ImDrawList* drawList, + SampledSpectrum* spectrum, + const uint32_t float3Index +) +{ + bool changed = false; + if (!ImGui::IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup | ImGuiHoveredFlags_AllowWhenBlockedByActiveItem)) { - bool changed = false; - if (!ImGui::IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup | ImGuiHoveredFlags_AllowWhenBlockedByActiveItem)) - { - return false; - } - float2 mousePosCanvas = float2(ImGui::GetIO().MousePos.x, ImGui::GetIO().MousePos.y) - canvasPos; + return false; + } + float2 mousePosCanvas = float2(ImGui::GetIO().MousePos.x, ImGui::GetIO().MousePos.y) - canvasPos; - const float nearRadius = 30.0f; - if (!mMovePoint) // No point has been clicked. - { - float smallestDist2 = std::numeric_limits::max(); - mPointIndexToBeEdited = 0u; - for (uint32_t q = 0; q < spectrum->size(); q++) // Without caring about whether the user has clicked, search for the point closest to the mouse (within nearRadius). - { - float2 p = toCoords(spectrum, q, xAxisRange, yAxisRange, float3Index); - float2 diff = mousePosCanvas - p; - float dist2 = dot(diff, diff); - if (dist2 < nearRadius * nearRadius && dist2 < smallestDist2) - { - mMovePoint = true; - smallestDist2 = dist2; - mPointIndexToBeEdited = q; - } - } - if (mMovePoint) // If the user hovered or clicked on a point sufficiently close, then draw a circle around the point. + const float nearRadius = 30.0f; + if (!mMovePoint) // No point has been clicked. + { + // Without caring about whether the user has clicked, search for the point closest to the mouse (within nearRadius). + float smallestDist2 = std::numeric_limits::max(); + mPointIndexToBeEdited = 0u; + for (uint32_t q = 0; q < spectrum->size(); q++) + { + float2 p = toCoords(spectrum, q, xAxisRange, yAxisRange, float3Index); + float2 diff = mousePosCanvas - p; + float dist2 = dot(diff, diff); + if (dist2 < nearRadius * nearRadius && dist2 < smallestDist2) { - float2 p = toCoords(spectrum, mPointIndexToBeEdited, xAxisRange, yAxisRange, float3Index); - drawCircle(drawList, canvasPos, p, nearRadius, float4(1.0f, 1.0f, 1.0f, 0.1f)); - const auto str = fmt::format("{:2.4f}", getSpectralIntensity(mPointIndexToBeEdited, spectrum, float3Index)); - ImGui::SetCursorPosX(p.x + 20.0f); - ImGui::SetCursorPosY(p.y - ImGui::CalcTextSize("0").y * 0.5f); - ImGui::PushStyleColor(ImGuiCol_Text, IM_COL32_WHITE); - ImGui::TextUnformatted(str.c_str()); - ImGui::PopStyleColor(); - - mMovePoint = ImGui::GetIO().MouseDown[0]; // Only let the user move the point, if the left mouse is down. + mMovePoint = true; + smallestDist2 = dist2; + mPointIndexToBeEdited = q; } } - if (mMovePoint) // Move the clicked point. - { - - float prevSpectralIntensity = getSpectralIntensity(mPointIndexToBeEdited, spectrum, float3Index); - float spectralIntensity = (yAxisRange.x - mousePosCanvas.y) / (yAxisRange.x - yAxisRange.y) * (mSpectralIntensityRange.y - mSpectralIntensityRange.x) + mSpectralIntensityRange.x; - spectralIntensity = std::max(0.0f, spectralIntensity); - - if constexpr (std::is_same_v) - { - float3 p = spectrum->get(mPointIndexToBeEdited); - p[float3Index] = spectralIntensity; - spectrum->set(mPointIndexToBeEdited, p); - } - else - { - spectrum->set(mPointIndexToBeEdited, spectralIntensity); - } - changed = prevSpectralIntensity != spectralIntensity; - - float2 p = toCoords(spectrum, mPointIndexToBeEdited, xAxisRange, yAxisRange); - drawCircle(drawList, canvasPos, p, nearRadius, float4(1.0f, 1.0f, 1.0f, 0.1f)); // Draw a circle around the point being moved. + // If the user hovered or clicked on a point sufficiently close, then draw a circle around the point. + if (mMovePoint) + { + float2 p = toCoords(spectrum, mPointIndexToBeEdited, xAxisRange, yAxisRange, float3Index); + drawCircle(drawList, canvasPos, p, nearRadius, float4(1.0f, 1.0f, 1.0f, 0.1f)); const auto str = fmt::format("{:2.4f}", getSpectralIntensity(mPointIndexToBeEdited, spectrum, float3Index)); ImGui::SetCursorPosX(p.x + 20.0f); ImGui::SetCursorPosY(p.y - ImGui::CalcTextSize("0").y * 0.5f); @@ -484,134 +521,192 @@ namespace Falcor ImGui::TextUnformatted(str.c_str()); ImGui::PopStyleColor(); - mMovePoint = ImGui::GetIO().MouseDown[0]; // Only let the user continue to move the point, if the left mouse is down. + // Only let the user move the point, if the left mouse is down. + mMovePoint = ImGui::GetIO().MouseDown[0]; } - return changed; } - - // Rendering the spectrum. Note that this function can render several Spectrum on the same drawing area, which can be convenient for debugging purposes & algorithm development. - template - bool SpectrumUI::render(Gui::Widgets& w, const std::string name, std::vector*> spectra, const bool renderOnlySpectrum) + if (mMovePoint) // Move the clicked point. { - if (spectra.size() == 0) + float prevSpectralIntensity = getSpectralIntensity(mPointIndexToBeEdited, spectrum, float3Index); + float spectralIntensity = + (yAxisRange.x - mousePosCanvas.y) / (yAxisRange.x - yAxisRange.y) * (mSpectralIntensityRange.y - mSpectralIntensityRange.x) + + mSpectralIntensityRange.x; + spectralIntensity = std::max(0.0f, spectralIntensity); + + if constexpr (std::is_same_v) { - return false; + float3 p = spectrum->get(mPointIndexToBeEdited); + p[float3Index] = spectralIntensity; + spectrum->set(mPointIndexToBeEdited, p); } - const uint32_t numComponents = getNumComponents(); - Gui::Group mainGroup = w.group(name, true); - if (!mainGroup.open()) + else { - return false; + spectrum->set(mPointIndexToBeEdited, spectralIntensity); } + changed = prevSpectralIntensity != spectralIntensity; - bool changed = false; - if (!renderOnlySpectrum) // Note: cannot combine this if-case with the next, since we do not want group() to be called if renderOnlySpectrum == true. - { - Gui::Group guiGroup = mainGroup.group(makeUnique("UI").c_str(), true); - if (guiGroup.open()) - { - uint32_t idx = uint32_t(mInterpolationType); - guiGroup.dropdown(makeUnique("Interpolation").c_str(), kInterpolationDropdownList, idx); - changed |= (idx != uint32_t(mInterpolationType)); - mInterpolationType = SpectrumInterpolation(idx); - - changed |= guiGroup.checkbox(makeUnique("Draw spectrum bar").c_str(), mDrawSpectrumBar, false); - if (mDrawSpectrumBar) - { - changed |= guiGroup.checkbox(makeUnique("Multiply with spectral intensity").c_str(), mMultiplyWithSpectralIntensity, true); - } - changed |= guiGroup.var(makeUnique("Height").c_str(), mDrawAreaHeight, 64u, 2048u, 1u, false); - if (spectra.size() * numComponents > 1) - { - changed |= guiGroup.var(makeUnique("Index to editable curve").c_str(), mEditSpectrumIndex, 0u, uint32_t(spectra.size() * numComponents - 1), 1u, true); - } - changed |= guiGroup.checkbox(makeUnique("Vertical grid").c_str(), mDrawGridX, false); - changed |= guiGroup.checkbox(makeUnique("Horizontal grid").c_str(), mDrawGridY, true); - changed |= guiGroup.checkbox(makeUnique("Color matching functions").c_str(), mDrawColorMatchingFunctions, true); - changed |= guiGroup.var(makeUnique("Min wavelength").c_str(), mWavelengthRange.x, 0.0f, 800.0f, 5.0f); - changed |= guiGroup.var(makeUnique("Max wavelength").c_str(), mWavelengthRange.y, 0.0f, 800.0f, 5.0f, true); - if (mWavelengthRange.x > mWavelengthRange.y) - { - mWavelengthRange.y = mWavelengthRange.x + 5.0f; - } - changed |= guiGroup.var(makeUnique("Max spectral intensity").c_str(), mSpectralIntensityRange.y, 1.0f, 20.0f, 0.5f); - - guiGroup.release(); - } - } + // Draw a circle around the point being moved. + float2 p = toCoords(spectrum, mPointIndexToBeEdited, xAxisRange, yAxisRange); + drawCircle(drawList, canvasPos, p, nearRadius, float4(1.0f, 1.0f, 1.0f, 0.1f)); - // Draw colored boxes of the spectrum --> RGB for each in spectra and the RGB color in text below. - const float rgbWidth = ImGui::CalcTextSize("00.00, 00.00, 00.00").x; - const float rgbHeight = 20.0f; - const float separation = 5.0f; - ImVec2 p = ImGui::GetCursorScreenPos(); - ImGui::Dummy(ImVec2(0, 20 + ImGui::CalcTextSize("0").y + separation)); - for (uint32_t q = 0; q < uint32_t(spectra.size()); q++) - { - for (uint32_t index = 0; index < numComponents; index++) - { - float3 c = SpectrumUtils::toRGB_D65(*spectra[q], mInterpolationType, index); - const auto str = fmt::format("{:1.2f}, {:1.2f}, {:1.2f}", c.x, c.y, c.z); - c = sRGBToLinear(c); - ImGui::GetWindowDrawList()->AddRectFilled(p, ImVec2(p.x + rgbWidth, p.y + rgbHeight), ImColor(c.r, c.g, c.b, 1.0f)); - ImGui::GetWindowDrawList()->AddText(ImVec2(p.x, p.y + rgbHeight + separation), IM_COL32_WHITE, str.c_str()); - p.x += separation + rgbWidth; - } - } + const auto str = fmt::format("{:2.4f}", getSpectralIntensity(mPointIndexToBeEdited, spectrum, float3Index)); + ImGui::SetCursorPosX(p.x + 20.0f); + ImGui::SetCursorPosY(p.y - ImGui::CalcTextSize("0").y * 0.5f); + ImGui::PushStyleColor(ImGuiCol_Text, IM_COL32_WHITE); + ImGui::TextUnformatted(str.c_str()); + ImGui::PopStyleColor(); - const float marginXLeft = 50.0f; - const float marginXRight = 20.0f; - const float marginYTop = 10.0f; - const float marginYBottom = 30.0f; - const float4 lightGray = float4(0.75f, 0.75f, 0.75f, 1.0f); - const float4 gray = float4(0.5f, 0.5f, 0.5f, 1.0f); + // Only let the user continue to move the point, if the left mouse is down. + mMovePoint = ImGui::GetIO().MouseDown[0]; + } + return changed; +} - const ImVec2 strSize = ImGui::CalcTextSize("000"); +// Rendering the spectrum. Note that this function can render several Spectrum on the same drawing area, which can be convenient for +// debugging purposes & algorithm development. +template +bool SpectrumUI::render(Gui::Widgets& w, const std::string name, std::vector*> spectra, const bool renderOnlySpectrum) +{ + if (spectra.size() == 0) + { + return false; + } + const uint32_t numComponents = getNumComponents(); + Gui::Group mainGroup = w.group(name, true); + if (!mainGroup.open()) + { + return false; + } + bool changed = false; + // Note: cannot combine this if-case with the next, since we do not want group() to be called if renderOnlySpectrum == true. + if (!renderOnlySpectrum) - // This is the main drawing area of the spectrum visualization. - ImGui::BeginChild(makeUnique("Spectrum visualization").c_str(), ImVec2(ImGui::GetWindowContentRegionWidth() - strSize.x * 3 / 4, float(mDrawAreaHeight)), false, ImGuiWindowFlags_NoScrollWithMouse); + { + Gui::Group guiGroup = mainGroup.group(makeUnique("UI").c_str(), true); + if (guiGroup.open()) { - const float2 canvasPos = float2(ImGui::GetCursorScreenPos().x, ImGui::GetCursorScreenPos().y); // ImDrawList API uses screen coordinates. - const float2 canvasSize = float2(ImGui::GetContentRegionAvail().x, ImGui::GetContentRegionAvail().y); - ImDrawList* drawList = ImGui::GetWindowDrawList(); - mEditSpectrumIndex = std::clamp(mEditSpectrumIndex, 0u, uint32_t(spectra.size() * numComponents - 1)); - - ImGui::InvisibleButton(makeUnique("canvas").c_str(), ImVec2(canvasSize.x, canvasSize.y)); // Added so that the ImGUI window cannot be moved by clicking on the SpectrumUI. We want to consume these click by our selves. - - // Draw border. ImGui can do this with third parameter to BeginChild(), but it clears out a thing band where you cannot draw, so better to do it this way. - drawLine(drawList, canvasPos, float2(0.0, 0.0f), float2(canvasSize.x, 0.0f), lightGray, 1.0f); - drawLine(drawList, canvasPos, float2(0.0, canvasSize.y - 1.0f), float2(canvasSize.x, canvasSize.y - 1.0f), lightGray, 1.0f); - drawLine(drawList, canvasPos, float2(0.0, 0.0f), float2(0.0, canvasSize.y - 1.0f), lightGray, 1.0f); - drawLine(drawList, canvasPos, float2(canvasSize.x - 1.0f, 0.0f), float2(canvasSize.x - 1.0f, canvasSize.y - 1.0f), lightGray, 1.0f); - const float2 xAxisRange = float2(marginXLeft, canvasSize.x - marginXRight); - const float2 yAxisRange = float2(canvasSize.y - marginYBottom, marginYTop); + uint32_t idx = uint32_t(mInterpolationType); + guiGroup.dropdown(makeUnique("Interpolation").c_str(), kInterpolationDropdownList, idx); + changed |= (idx != uint32_t(mInterpolationType)); + mInterpolationType = SpectrumInterpolation(idx); + + changed |= guiGroup.checkbox(makeUnique("Draw spectrum bar").c_str(), mDrawSpectrumBar, false); if (mDrawSpectrumBar) { - drawSpectrumBar(drawList, canvasPos, xAxisRange, yAxisRange, spectra[mEditSpectrumIndex / numComponents], mMultiplyWithSpectralIntensity); + changed |= guiGroup.checkbox(makeUnique("Multiply with spectral intensity").c_str(), mMultiplyWithSpectralIntensity, true); } - - drawLine(drawList, canvasPos, float2(xAxisRange.x, yAxisRange.x), float2(xAxisRange.y, yAxisRange.x), gray, 1.0f); // Draw x-axis. - drawLine(drawList, canvasPos, float2(xAxisRange.x, yAxisRange.x), float2(xAxisRange.x, yAxisRange.y), gray, 1.0f); // Draw y-axis. - drawLine(drawList, canvasPos, float2(xAxisRange.y, yAxisRange.x), float2(xAxisRange.y, yAxisRange.y), gray, 1.0f); // Draw y-axis to the right. - - drawColorMatchingFunctions(drawList, canvasPos, canvasSize, xAxisRange, yAxisRange); // Draw the color matching functions xyzbar. - drawTextWavelengthsAndTicks(drawList, canvasPos, xAxisRange, yAxisRange, gray, lightGray, lightGray * 0.5f); // Draw the text wavelengths below the x-axis and the ticks. - drawTextSpectralIntensityAndTicks(drawList, canvasPos, xAxisRange, yAxisRange, gray, lightGray, lightGray * 0.5f); // Draw the ticks on the y-axis and spectral intensity text there. - - // Draw spectrum curves. - for (uint32_t q = 0; q < uint32_t(spectra.size()); q++) + changed |= guiGroup.var(makeUnique("Height").c_str(), mDrawAreaHeight, 64u, 2048u, 1u, false); + if (spectra.size() * numComponents > 1) { - drawSpectrumCurve(drawList, canvasPos, canvasSize, xAxisRange, yAxisRange, spectra[q], q); + changed |= guiGroup.var( + makeUnique("Index to editable curve").c_str(), mEditSpectrumIndex, 0u, uint32_t(spectra.size() * numComponents - 1), 1u, + true + ); } - uint32_t float3Index = numComponents == 1 ? 0 : (mEditSpectrumIndex % 3); - changed |= handleMouse(canvasPos, canvasSize, xAxisRange, yAxisRange, drawList, spectra[mEditSpectrumIndex / numComponents], float3Index); + changed |= guiGroup.checkbox(makeUnique("Vertical grid").c_str(), mDrawGridX, false); + changed |= guiGroup.checkbox(makeUnique("Horizontal grid").c_str(), mDrawGridY, true); + changed |= guiGroup.checkbox(makeUnique("Color matching functions").c_str(), mDrawColorMatchingFunctions, true); + changed |= guiGroup.var(makeUnique("Min wavelength").c_str(), mWavelengthRange.x, 0.0f, 800.0f, 5.0f); + changed |= guiGroup.var(makeUnique("Max wavelength").c_str(), mWavelengthRange.y, 0.0f, 800.0f, 5.0f, true); + if (mWavelengthRange.x > mWavelengthRange.y) + { + mWavelengthRange.y = mWavelengthRange.x + 5.0f; + } + changed |= guiGroup.var(makeUnique("Max spectral intensity").c_str(), mSpectralIntensityRange.y, 1.0f, 20.0f, 0.5f); + + guiGroup.release(); } - ImGui::EndChild(); - mainGroup.release(); - return changed; } - template class SpectrumUI; - template class SpectrumUI; + // Draw colored boxes of the spectrum --> RGB for each in spectra and the RGB color in text below. + const float rgbWidth = ImGui::CalcTextSize("00.00, 00.00, 00.00").x; + const float rgbHeight = 20.0f; + const float separation = 5.0f; + ImVec2 p = ImGui::GetCursorScreenPos(); + ImGui::Dummy(ImVec2(0, 20 + ImGui::CalcTextSize("0").y + separation)); + for (uint32_t q = 0; q < uint32_t(spectra.size()); q++) + { + for (uint32_t index = 0; index < numComponents; index++) + { + float3 c = SpectrumUtils::toRGB_D65(*spectra[q], mInterpolationType, index); + const auto str = fmt::format("{:1.2f}, {:1.2f}, {:1.2f}", c.x, c.y, c.z); + c = sRGBToLinear(c); + ImGui::GetWindowDrawList()->AddRectFilled(p, ImVec2(p.x + rgbWidth, p.y + rgbHeight), ImColor(c.r, c.g, c.b, 1.0f)); + ImGui::GetWindowDrawList()->AddText(ImVec2(p.x, p.y + rgbHeight + separation), IM_COL32_WHITE, str.c_str()); + p.x += separation + rgbWidth; + } + } + + const float marginXLeft = 50.0f; + const float marginXRight = 20.0f; + const float marginYTop = 10.0f; + const float marginYBottom = 30.0f; + const float4 lightGray = float4(0.75f, 0.75f, 0.75f, 1.0f); + const float4 gray = float4(0.5f, 0.5f, 0.5f, 1.0f); + + const ImVec2 strSize = ImGui::CalcTextSize("000"); + + // This is the main drawing area of the spectrum visualization. + ImGui::BeginChild( + makeUnique("Spectrum visualization").c_str(), + ImVec2(ImGui::GetWindowContentRegionWidth() - strSize.x * 3 / 4, float(mDrawAreaHeight)), false, ImGuiWindowFlags_NoScrollWithMouse + ); + { + // ImDrawList API uses screen coordinates. + const float2 canvasPos = float2(ImGui::GetCursorScreenPos().x, ImGui::GetCursorScreenPos().y); + + const float2 canvasSize = float2(ImGui::GetContentRegionAvail().x, ImGui::GetContentRegionAvail().y); + ImDrawList* drawList = ImGui::GetWindowDrawList(); + mEditSpectrumIndex = std::clamp(mEditSpectrumIndex, 0u, uint32_t(spectra.size() * numComponents - 1)); + + // Added so that the ImGUI window cannot be moved by clicking on the SpectrumUI. + // We want to consume these click by our selves. + ImGui::InvisibleButton(makeUnique("canvas").c_str(), ImVec2(canvasSize.x, canvasSize.y)); + + // Draw border. ImGui can do this with third parameter to BeginChild(), but it clears out a thing band where you cannot draw, so + // better to do it this way. + drawLine(drawList, canvasPos, float2(0.0, 0.0f), float2(canvasSize.x, 0.0f), lightGray, 1.0f); + drawLine(drawList, canvasPos, float2(0.0, canvasSize.y - 1.0f), float2(canvasSize.x, canvasSize.y - 1.0f), lightGray, 1.0f); + drawLine(drawList, canvasPos, float2(0.0, 0.0f), float2(0.0, canvasSize.y - 1.0f), lightGray, 1.0f); + drawLine(drawList, canvasPos, float2(canvasSize.x - 1.0f, 0.0f), float2(canvasSize.x - 1.0f, canvasSize.y - 1.0f), lightGray, 1.0f); + const float2 xAxisRange = float2(marginXLeft, canvasSize.x - marginXRight); + const float2 yAxisRange = float2(canvasSize.y - marginYBottom, marginYTop); + if (mDrawSpectrumBar) + { + drawSpectrumBar( + drawList, canvasPos, xAxisRange, yAxisRange, spectra[mEditSpectrumIndex / numComponents], mMultiplyWithSpectralIntensity + ); + } + + // Draw x-axis. + drawLine(drawList, canvasPos, float2(xAxisRange.x, yAxisRange.x), float2(xAxisRange.y, yAxisRange.x), gray, 1.0f); + // Draw y-axis. + drawLine(drawList, canvasPos, float2(xAxisRange.x, yAxisRange.x), float2(xAxisRange.x, yAxisRange.y), gray, 1.0f); + // Draw y-axis to the right. + drawLine(drawList, canvasPos, float2(xAxisRange.y, yAxisRange.x), float2(xAxisRange.y, yAxisRange.y), gray, 1.0f); + + // Draw the color matching functions xyzbar. + drawColorMatchingFunctions(drawList, canvasPos, canvasSize, xAxisRange, yAxisRange); + // Draw the text wavelengths below the x-axis and the ticks. + drawTextWavelengthsAndTicks(drawList, canvasPos, xAxisRange, yAxisRange, gray, lightGray, lightGray * 0.5f); + // Draw the ticks on the y-axis and spectral intensity text there. + drawTextSpectralIntensityAndTicks(drawList, canvasPos, xAxisRange, yAxisRange, gray, lightGray, lightGray * 0.5f); + + // Draw spectrum curves. + for (uint32_t q = 0; q < uint32_t(spectra.size()); q++) + { + drawSpectrumCurve(drawList, canvasPos, canvasSize, xAxisRange, yAxisRange, spectra[q], q); + } + uint32_t float3Index = numComponents == 1 ? 0 : (mEditSpectrumIndex % 3); + changed |= + handleMouse(canvasPos, canvasSize, xAxisRange, yAxisRange, drawList, spectra[mEditSpectrumIndex / numComponents], float3Index); + } + ImGui::EndChild(); + mainGroup.release(); + return changed; } + +template class SpectrumUI; +template class SpectrumUI; +} // namespace Falcor diff --git a/Source/Falcor/Utils/UI/SpectrumUI.h b/Source/Falcor/Utils/UI/SpectrumUI.h index a73847f92..417b18986 100644 --- a/Source/Falcor/Utils/UI/SpectrumUI.h +++ b/Source/Falcor/Utils/UI/SpectrumUI.h @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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,78 +36,148 @@ namespace Falcor { - namespace - { - Gui::DropdownList kInterpolationDropdownList = - { - { (uint32_t)SpectrumInterpolation::Linear, "Linear" }, - }; - } +namespace +{ +Gui::DropdownList kInterpolationDropdownList = { + {(uint32_t)SpectrumInterpolation::Linear, "Linear"}, +}; +} + +/** + * User interface for SampledSpectrum. Implemented using ImGui and Gui::Widgets. Can be called as widget.spectrum(...) or + * renderSpectrumUI(...). + */ +template +class FALCOR_API SpectrumUI +{ +public: + SpectrumUI(); + SpectrumUI(const float2& wavelengthRange, const float2& spectralIntensityRange); + void setWavelengthRange(const float2& range) { mWavelengthRange = range; } + void setSpectralIntensityRange(const float2& range) { mSpectralIntensityRange = range; } + bool render(Gui::Widgets& w, const std::string name, std::vector*> spectra, const bool renderOnlySpectrum = false); + +protected: + std::string makeUnique(const std::string& str) const; + float getSpectralIntensity(const float wavelength, const SampledSpectrum* spectrum, const uint32_t curveIndex) const; + float getSpectralIntensity(const uint32_t pointIndex, const SampledSpectrum* spectrum, const uint32_t curveIndex) const; + uint32_t getNumComponents() const; + void drawLine( + ImDrawList* drawList, + const float2& canvasPos, + const float2& point0, + const float2& point1, + const float4& color, + const float lineWidth = 2.0f + ); + void drawCircle(ImDrawList* drawList, const float2& canvasPos, const float2& center, const float radius, const float4& color); + void textHorizontallyCentered(const std::string& text, const float2& pos, const float4& color); + void textVerticallyCenteredLeft(const std::string& text, const float2& pos, const float4& color); + float toXCoord(const float wavelength, const float2& xAxisRange) const; + float toYCoord(const float spectralIntensity, const float2& yAxisRange) const; + float2 toCoords(const float wavelength, const float spectralIntensity, const float2& xAxisRange, const float2& yAxisRange) const; + float2 toCoords( + const SampledSpectrum* spectrum, + const int index, + const float2& xAxisRange, + const float2& yAxisRange, + const uint32_t float3Index = 0 + ) const; + void drawColorMatchingFunctions( + ImDrawList* drawList, + const float2& canvasPos, + const float2& canvasSize, + const float2& xAxisRange, + const float2& yAxisRange + ); + void drawTextWavelengthsAndTicks( + ImDrawList* drawList, + const float2& canvasPos, + const float2& xAxisRange, + const float2& yAxisRange, + const float4& textColor, + const float4& tickColor, + const float4& gridColor + ); + void drawTextSpectralIntensityAndTicks( + ImDrawList* drawList, + const float2& canvasPos, + const float2& xAxisRange, + const float2& yAxisRange, + const float4& textColor, + const float4& tickColor, + const float4& gridColor + ); + void drawSpectrumBar( + ImDrawList* drawList, + const float2& canvasPos, + const float2& xAxisRange, + const float2& yAxisRange, + SampledSpectrum* spectrum, + const bool multiplyBySpectrum + ); + void drawSpectrumCurve( + ImDrawList* drawList, + const float2& canvasPos, + const float2& canvasSize, + const float2& xAxisRange, + const float2& yAxisRange, + SampledSpectrum* spectrum, + const uint32_t spectrumIndex + ); + bool handleMouse( + const float2& canvasPos, + const float2& canvasSize, + const float2& xAxisRange, + const float2& yAxisRange, + ImDrawList* drawList, + SampledSpectrum* spectrum, + const uint32_t float3Index = 0 + ); + float generateNiceNumber(const float x) const; - /** User interface for SampledSpectrum. Implemented using ImGui and Gui::Widgets. Can be called as widget.spectrum(...) or renderSpectrumUI(...). - */ - template - class FALCOR_API SpectrumUI - { - public: - SpectrumUI(); - SpectrumUI(const float2& wavelengthRange, const float2& spectralIntensityRange); - void setWavelengthRange(const float2& range) { mWavelengthRange = range; } - void setSpectralIntensityRange(const float2& range) { mSpectralIntensityRange = range; } - bool render(Gui::Widgets& w, const std::string name, std::vector*> spectra, const bool renderOnlySpectrum = false); - protected: - std::string makeUnique(const std::string& str) const; - float getSpectralIntensity(const float wavelength, const SampledSpectrum* spectrum, const uint32_t curveIndex) const; - float getSpectralIntensity(const uint32_t pointIndex, const SampledSpectrum* spectrum, const uint32_t curveIndex) const; - uint32_t getNumComponents() const; - void drawLine(ImDrawList* drawList, const float2& canvasPos, const float2& point0, const float2& point1, const float4& color, const float lineWidth = 2.0f); - void drawCircle(ImDrawList* drawList, const float2& canvasPos, const float2& center, const float radius, const float4& color); - void textHorizontallyCentered(const std::string& text, const float2& pos, const float4& color); - void textVerticallyCenteredLeft(const std::string& text, const float2& pos, const float4& color); - float toXCoord(const float wavelength, const float2& xAxisRange) const; - float toYCoord(const float spectralIntensity, const float2& yAxisRange) const; - float2 toCoords(const float wavelength, const float spectralIntensity, const float2& xAxisRange, const float2& yAxisRange) const; - float2 toCoords(const SampledSpectrum* spectrum, const int index, const float2& xAxisRange, const float2& yAxisRange, const uint32_t float3Index = 0) const; - void drawColorMatchingFunctions(ImDrawList* drawList, const float2& canvasPos, const float2& canvasSize, const float2& xAxisRange, const float2& yAxisRange); - void drawTextWavelengthsAndTicks(ImDrawList* drawList, const float2& canvasPos, const float2& xAxisRange, const float2& yAxisRange, const float4& textColor, const float4& tickColor, const float4& gridColor); - void drawTextSpectralIntensityAndTicks(ImDrawList* drawList, const float2& canvasPos, const float2& xAxisRange, const float2& yAxisRange, const float4& textColor, const float4& tickColor, const float4& gridColor); - void drawSpectrumBar(ImDrawList* drawList, const float2& canvasPos, const float2& xAxisRange, const float2& yAxisRange, SampledSpectrum* spectrum, const bool multiplyBySpectrum); - void drawSpectrumCurve(ImDrawList* drawList, const float2& canvasPos, const float2& canvasSize, const float2& xAxisRange, const float2& yAxisRange, SampledSpectrum* spectrum, const uint32_t spectrumIndex); - bool handleMouse(const float2& canvasPos, const float2& canvasSize, const float2& xAxisRange, const float2& yAxisRange, ImDrawList* drawList, SampledSpectrum* spectrum, const uint32_t float3Index = 0); - float generateNiceNumber(const float x) const; - protected: - // UI parameters. - float2 mWavelengthRange = float2(350.0f, 750.0f); - float2 mSpectralIntensityRange = float2(0.0f, 1.0f); - uint32_t mEditSpectrumIndex = 0; - bool mDrawSpectrumBar = true; - bool mMultiplyWithSpectralIntensity = true; - bool mDrawGridX = true; - bool mDrawGridY = true; - uint32_t mDrawAreaHeight = 300u; - bool mDrawColorMatchingFunctions = false; +protected: + // UI parameters. + float2 mWavelengthRange = float2(350.0f, 750.0f); + float2 mSpectralIntensityRange = float2(0.0f, 1.0f); + uint32_t mEditSpectrumIndex = 0; + bool mDrawSpectrumBar = true; + bool mMultiplyWithSpectralIntensity = true; + bool mDrawGridX = true; + bool mDrawGridY = true; + uint32_t mDrawAreaHeight = 300u; + bool mDrawColorMatchingFunctions = false; - bool mMovePoint = false; - uint32_t mPointIndexToBeEdited = 0u; - SpectrumInterpolation mInterpolationType = SpectrumInterpolation::Linear; - }; + bool mMovePoint = false; + uint32_t mPointIndexToBeEdited = 0u; + SpectrumInterpolation mInterpolationType = SpectrumInterpolation::Linear; +}; - template - bool renderSpectrumUI(Gui::Widgets& w, SampledSpectrum& spectrum, const char label[] = "Spectrum UI") - { - SpectrumUI spectrumUI; // Use default parameters. Those will not be saved from frame to frame. - return spectrumUI.render(w, label, { &spectrum }, true); // True in the last parameter means that the UI for changing parameters will not be shown. - } +template +bool renderSpectrumUI(Gui::Widgets& w, SampledSpectrum& spectrum, const char label[] = "Spectrum UI") +{ + // Use default parameters. Those will not be saved from frame to frame. + SpectrumUI spectrumUI; + // True in the last parameter means that the UI for changing parameters will not be shown. + return spectrumUI.render(w, label, {&spectrum}, true); +} - template - bool renderSpectrumUI(Gui::Widgets& w, SampledSpectrum& spectrum, SpectrumUI& spectrumUI, const char label[] = "Spectrum UI") - { - return spectrumUI.render(w, label, { &spectrum }, false); // False in the last parameter means that the UI for changing parameters will be shown. - } +template +bool renderSpectrumUI(Gui::Widgets& w, SampledSpectrum& spectrum, SpectrumUI& spectrumUI, const char label[] = "Spectrum UI") +{ + // False in the last parameter means that the UI for changing parameters will be shown. + return spectrumUI.render(w, label, {&spectrum}, false); +} - template - bool renderSpectrumUI(Gui::Widgets& w, std::vector*>& spectra, SpectrumUI& spectrumUI, const char label[] = "Spectrum UI") - { - return spectrumUI.render(w, label, spectra, false); // False in the last parameter means that the UI for changing parameters will be shown. - } +template +bool renderSpectrumUI( + Gui::Widgets& w, + std::vector*>& spectra, + SpectrumUI& spectrumUI, + const char label[] = "Spectrum UI" +) +{ + // False in the last parameter means that the UI for changing parameters will be shown. + return spectrumUI.render(w, label, spectra, false); } +} // namespace Falcor diff --git a/Source/Falcor/Utils/UI/TextRenderer.3d.slang b/Source/Falcor/Utils/UI/TextRenderer.3d.slang index f400548fa..f9214ef37 100644 --- a/Source/Falcor/Utils/UI/TextRenderer.3d.slang +++ b/Source/Falcor/Utils/UI/TextRenderer.3d.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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,30 +27,30 @@ **************************************************************************/ cbuffer PerFrameCB { - float4x4 gvpTransform; - float3 gFontColor; + float4x4 gvpTransform; + float3 gFontColor; }; Texture2D gFontTex; float4 transform(float2 posS) { - return mul(gvpTransform, float4(posS, 0.5f, 1)); + return mul(gvpTransform, float4(posS, 0.5f, 1)); } -void vsMain(float2 posS : POSITION, inout float2 texC : TEXCOORD, out float4 posSV : SV_POSITION) +void vsMain(float2 posS: POSITION, inout float2 texC: TEXCOORD, out float4 posSV: SV_POSITION) { - posSV = transform(posS); + posSV = transform(posS); } float4 calcColor(float2 texC) { - float4 color = gFontTex.Load(int3(texC, 0)); - color.rgb = gFontColor; - return color; + float4 color = gFontTex.Load(int3(texC, 0)); + color.rgb = gFontColor; + return color; } -float4 psMain(float2 texC : TEXCOORD) : SV_TARGET0 +float4 psMain(float2 texC: TEXCOORD) : SV_TARGET0 { - return calcColor(texC); + return calcColor(texC); } diff --git a/Source/Falcor/Utils/UI/TextRenderer.cpp b/Source/Falcor/Utils/UI/TextRenderer.cpp index 21db7dcdc..361707df7 100644 --- a/Source/Falcor/Utils/UI/TextRenderer.cpp +++ b/Source/Falcor/Utils/UI/TextRenderer.cpp @@ -27,178 +27,152 @@ **************************************************************************/ #include "TextRenderer.h" #include "Core/API/RenderContext.h" -#include "RenderGraph/BasePasses/RasterPass.h" +#include "Core/Pass/RasterPass.h" #include "Utils/UI/Font.h" namespace Falcor { - namespace - { - struct Vertex - { - float2 screenPos; - float2 texCoord; - }; - - const float2 kVertexPos[] = - { - float2(0, 0), - float2(0, 1), - float2(1, 0), +namespace +{ +struct Vertex +{ + float2 screenPos; + float2 texCoord; +}; - float2(1, 0), - float2(0, 1), - float2(1, 1), - }; +const float2 kVertexPos[] = { + float2(0, 0), float2(0, 1), float2(1, 0), - const uint32_t kMaxCharCount = 1000; + float2(1, 0), float2(0, 1), float2(1, 1), +}; - 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); - } +const uint32_t kMaxCharCount = 1000; - struct TextData - { - bool init = false; - TextRenderer::Flags flags = TextRenderer::Flags::Shadowed; - float3 color = float3(1, 1, 1); - Buffer::SharedPtr pVb; - RasterPass::SharedPtr pPass; - Font::UniquePtr pFont; - } gTextData; // TODO: REMOVEGLOBAL - - void setCbData(const Fbo::SharedPtr& pDstFbo) - { - float width = (float)pDstFbo->getWidth(); - float height = (float)pDstFbo->getHeight(); - - // Set the matrix - rmcv::mat4 vpTransform; - vpTransform[0][0] = 2 / width; - vpTransform[1][1] = -2 / height; - vpTransform[0][3] = -1; - vpTransform[1][3] = 1; -#ifdef FALCOR_FLIP_Y - 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; - } +ref createVAO(const ref& pVB) +{ + ref pLayout = VertexLayout::create(); + ref 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); +} - void renderText(RenderContext* pRenderContext, const std::string& text, const Fbo::SharedPtr& pDstFbo, float2 pos) - { - // Make sure we enough space for the next char - FALCOR_ASSERT(text.size() < kMaxCharCount); - setCbData(pDstFbo); - Vertex* verts = (Vertex*)gTextData.pVb->map(Buffer::MapType::WriteDiscard); +} // namespace - float startX = pos.x; - uint32_t vertexCount = 0; // Not the same as text.size(), since some special characters are ignored +TextRenderer::TextRenderer(ref pDevice) : mpDevice(pDevice) +{ + // Create a vertex buffer + const uint32_t vbSize = (uint32_t)(sizeof(Vertex) * kMaxCharCount * std::size(kVertexPos)); + mpVb = Buffer::create(mpDevice, vbSize, Buffer::BindFlags::Vertex, Buffer::CpuAccess::Write, nullptr); + + // Create the RenderState + mpPass = RasterPass::create(mpDevice, "Utils/UI/TextRenderer.3d.slang", "vsMain", "psMain"); + auto& pState = mpPass->getState(); + pState->setVao(createVAO(mpVb)); + + // 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)); + mpFont = std::make_unique(mpDevice, getRuntimeDirectory() / "data/framework/fonts/dejavu-sans-mono-14"); + + // Initialize the buffer + mpPass->getRootVar()["gFontTex"] = mpFont->getTexture(); +} - // 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 < std::size(kVertexPos); i++, vertexCount++) - { - float2 posScale = kVertexPos[i]; - float2 charPos = desc.size * posScale; - charPos += pos; - verts[vertexCount].screenPos = charPos; - verts[vertexCount].texCoord = desc.topLeft + desc.size * kVertexPos[i]; - } - pos.x += gTextData.pFont->getLettersSpacing(); - } - } +TextRenderer::~TextRenderer() = default; - // Submit - gTextData.pVb->unmap(); - gTextData.pPass->getState()->setFbo(pDstFbo); - gTextData.pPass->draw(pRenderContext, vertexCount, 0); - } +void TextRenderer::render(RenderContext* pRenderContext, const std::string& text, const ref& pDstFbo, float2 pos) +{ + if (is_set(mFlags, TextRenderer::Flags::Shadowed)) + { + float3 oldColor = getColor(); + setColor(float3(0)); + renderText(pRenderContext, text, pDstFbo, pos + float2(1)); + setColor(oldColor); } + renderText(pRenderContext, text, pDstFbo, pos); +} - const float3& TextRenderer::getColor() { return gTextData.color; } - void TextRenderer::setColor(const float3& color) { gTextData.color = color; } - TextRenderer::Flags TextRenderer::getFlags() { return gTextData.flags; } - void TextRenderer::setFlags(Flags f) { gTextData.flags = f; } +void TextRenderer::setCbData(const ref& pDstFbo) +{ + float width = (float)pDstFbo->getWidth(); + float height = (float)pDstFbo->getHeight(); + + // Set the matrix + float4x4 vpTransform = float4x4::identity(); + vpTransform[0][0] = 2 / width; + vpTransform[1][1] = -2 / height; + vpTransform[0][3] = -1; + vpTransform[1][3] = 1; +#ifdef FALCOR_FLIP_Y + vpTransform[1][1] *= -1.0f; + vpTransform[3][1] *= -1.0f; +#endif + // Update the program variables + auto var = mpPass->getRootVar()["PerFrameCB"]; + var["gvpTransform"] = vpTransform; + var["gFontColor"] = mColor; +} - void TextRenderer::start(Device* pDevice) - { - if (gTextData.init) return; - - static const std::string kShaderFile("Utils/UI/TextRenderer.3d.slang"); - - // Create a vertex buffer - const uint32_t vbSize = (uint32_t)(sizeof(Vertex)*kMaxCharCount*std::size(kVertexPos)); - gTextData.pVb = Buffer::create(pDevice, vbSize, Buffer::BindFlags::Vertex, Buffer::CpuAccess::Write, nullptr); - - // Create the RenderState - gTextData.pPass = RasterPass::create(pDevice->shared_from_this(), kShaderFile, "vsMain", "psMain"); - 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(pDevice, getRuntimeDirectory() / "data/framework/fonts/dejavu-sans-mono-14"); - - // Initialize the buffer - gTextData.pPass["gFontTex"] = gTextData.pFont->getTexture(); - - gTextData.init = true; - } +void TextRenderer::renderText(RenderContext* pRenderContext, const std::string& text, const ref& pDstFbo, float2 pos) +{ + // Make sure we enough space for the next char + FALCOR_ASSERT(text.size() < kMaxCharCount); + setCbData(pDstFbo); + Vertex* verts = (Vertex*)mpVb->map(Buffer::MapType::WriteDiscard); - void TextRenderer::shutdown() - { - gTextData = {}; - } + float startX = pos.x; + uint32_t vertexCount = 0; // Not the same as text.size(), since some special characters are ignored - void TextRenderer::render(RenderContext* pRenderContext, const std::string& text, const Fbo::SharedPtr& pDstFbo, float2 pos) + // Create the vertex-buffer + for (const auto& c : text) { - if (is_set(gTextData.flags, TextRenderer::Flags::Shadowed)) + if (c == '\n') { - float3 oldColor = getColor(); - setColor(float3(0)); - renderText(pRenderContext, text, pDstFbo, pos + float2(1)); - setColor(oldColor); + pos.y += mpFont->getFontHeight(); + pos.x = startX; + } + else if (c == '\t') + pos.x += mpFont->getTabWidth(); + else if (c == ' ') + pos.x += mpFont->getLettersSpacing(); + else + { + // Regular character + const Font::CharTexCrdDesc& desc = mpFont->getCharDesc(c); + for (uint32_t i = 0; i < std::size(kVertexPos); i++, vertexCount++) + { + float2 posScale = kVertexPos[i]; + float2 charPos = desc.size * posScale; + charPos += pos; + verts[vertexCount].screenPos = charPos; + verts[vertexCount].texCoord = desc.topLeft + desc.size * kVertexPos[i]; + } + pos.x += mpFont->getLettersSpacing(); } - renderText(pRenderContext, text, pDstFbo, pos); } + + // Submit + mpVb->unmap(); + mpPass->getState()->setFbo(pDstFbo); + mpPass->draw(pRenderContext, vertexCount, 0); } + +} // namespace Falcor diff --git a/Source/Falcor/Utils/UI/TextRenderer.h b/Source/Falcor/Utils/UI/TextRenderer.h index d4acd1853..cd8a95158 100644 --- a/Source/Falcor/Utils/UI/TextRenderer.h +++ b/Source/Falcor/Utils/UI/TextRenderer.h @@ -27,61 +27,73 @@ **************************************************************************/ #pragma once #include "Core/Macros.h" +#include "Core/API/fwd.h" +#include "Core/API/Buffer.h" #include "Core/API/FBO.h" +#include "Core/Pass/RasterPass.h" #include "Utils/Math/Vector.h" namespace Falcor { - class RenderContext; +class Font; - /** Class that renders text into the screen. - */ - class FALCOR_API TextRenderer +/** + * Class that renders text into the screen. + */ +class FALCOR_API TextRenderer +{ +public: + enum class Flags { - public: - enum class Flags - { - None = 0x0, - Shadowed = 0x1 - }; + None = 0x0, + Shadowed = 0x1 + }; - /** Initialize the text-renderer - This class is not thread-safe! - */ - static void start(Device* pDevice); + TextRenderer(ref pDevice); + ~TextRenderer(); - /** End batching. This will cause the render queue to flush and display the message to the screen. - */ - static void shutdown(); + /** + * 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 + */ + void render(RenderContext* pRenderContext, const std::string& text, const ref& pDstFbo, float2 pos); - /** 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 - */ - static void render(RenderContext* pRenderContext, const std::string& text, const Fbo::SharedPtr& pDstFbo, float2 pos); + /** + * Returns the color of the text being rendered + * @return current color The text color + */ + const float3& getColor() const { return mColor; } - /** Returns the color of the text being rendered - \return current color The text color - */ - static const float3& getColor(); + /** + * Set the color of the text being rendered + * @param[in] color The text color + */ + void setColor(const float3& color) { mColor = color; } - /** Set the color of the text being rendered - \param[in] color The text color - */ - static void setColor(const float3& color); + /** + * Get the active flags + */ + Flags getFlags() const { return mFlags; } - /** Get the active flags - */ - static Flags getFlags(); + /** + * Set the flags + */ + void setFlags(Flags flags) { mFlags = flags; } - /** Set the flags - */ - static void setFlags(Flags f); - private: - TextRenderer() = default; - }; +private: + void setCbData(const ref& pDstFbo); + void renderText(RenderContext* pRenderContext, const std::string& text, const ref& pDstFbo, float2 pos); + + ref mpDevice; + Flags mFlags = Flags::Shadowed; + float3 mColor = float3(1.f); + ref mpVb; + ref mpPass; + std::unique_ptr mpFont; +}; - FALCOR_ENUM_CLASS_OPERATORS(TextRenderer::Flags); -} +FALCOR_ENUM_CLASS_OPERATORS(TextRenderer::Flags); +} // namespace Falcor diff --git a/Source/Falcor/Utils/Video/VideoEncoder.cpp b/Source/Falcor/Utils/Video/VideoEncoder.cpp deleted file mode 100644 index 3c11bb011..000000000 --- a/Source/Falcor/Utils/Video/VideoEncoder.cpp +++ /dev/null @@ -1,435 +0,0 @@ -/*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. - # - # Redistribution and use in source and binary forms, with or without - # modification, are permitted provided that the following conditions - # are met: - # * Redistributions of source code must retain the above copyright - # notice, this list of conditions and the following disclaimer. - # * Redistributions in binary form must reproduce the above copyright - # notice, this list of conditions and the following disclaimer in the - # documentation and/or other materials provided with the distribution. - # * Neither the name of NVIDIA CORPORATION nor the names of its - # contributors may be used to endorse or promote products derived - # from this software without specific prior written permission. - # - # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY - # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - **************************************************************************/ -#include "VideoEncoder.h" -#include "Utils/Logger.h" -#include "Utils/Scripting/ScriptBindings.h" - -extern "C" -{ -#include -#include -} - -namespace Falcor -{ - namespace - { - AVPixelFormat getPictureFormatFromCodec(AVCodecID codec) - { - 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: - FALCOR_UNREACHABLE(); - return AV_PIX_FMT_NONE; - } - } - - AVPixelFormat getPictureFormatFromFalcorFormat(ResourceFormat format) - { - 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::Codec codec) - { - 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: - FALCOR_UNREACHABLE(); - return AV_CODEC_ID_NONE; - } - } - - static bool error(const std::filesystem::path& path, const std::string& msg) - { - reportError(fmt::format("Error when creating video capture file '{}'.\n{}", path, 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) - { - pCodecCtx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER; - } - - return pCodecCtx; - } - - AVStream* createVideoStream(AVFormatContext* pCtx, uint32_t fps, AVCodecID codecID, const std::filesystem::path& path, AVCodec*& pCodec) - { - // Get the encoder - pCodec = avcodec_find_encoder(codecID); - if (pCodec == nullptr) - { - error(path, std::string("Can't find ") + avcodec_get_name(codecID) + " encoder."); - return nullptr; - } - - // create the video stream - AVStream* pStream = avformat_new_stream(pCtx, nullptr); - if (pStream == nullptr) - { - error(path, "Failed to create video stream."); - return nullptr; - } - 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::filesystem::path& path) - { - AVFrame* pFrame = av_frame_alloc(); - if (pFrame == nullptr) - { - error(path, "Video frame allocation failed."); - return nullptr; - } - - pFrame->format = format; - pFrame->width = width; - pFrame->height = height; - pFrame->pts = 0; - - // Allocate the buffer for the encoded image - if (av_frame_get_buffer(pFrame, 32) < 0) - { - error(path, "Can't allocate destination picture"); - return nullptr; - } - - return pFrame; - } - - bool openVideo(AVCodec* pCodec, AVCodecContext* pCodecCtx, AVFrame*& pFrame, const std::filesystem::path& path) - { - 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(path, "Can't open video codec."); - } - av_dict_free(¶m); - - // create a frame - pFrame = allocateFrame(pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height, path); - if (pFrame == nullptr) - { - return false; - } - return true; - } - } - - VideoEncoder::VideoEncoder(const std::filesystem::path& path) - : mPath(path) - { - } - - VideoEncoder::~VideoEncoder() - { - endCapture(); - } - - VideoEncoder::UniquePtr VideoEncoder::create(const Desc& desc) - { - UniquePtr pVC = UniquePtr(new VideoEncoder(desc.path)); - FALCOR_ASSERT(pVC); - - // Initialize the encoder. This may fail, in which case we return nullptr. - if (pVC->init(desc) == false) - { - pVC = nullptr; - } - 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. -#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(58, 9, 100) - av_register_all(); -#endif - // Create the output context - avformat_alloc_output_context2(&mpOutputContext, nullptr, nullptr, mPath.string().c_str()); - if(mpOutputContext == nullptr) - { - // The sample tries again, while explicitly requesting mpeg format. I chose not to do it, since it might lead to a container with a wrong extension - return error(mPath, "File output format not recognized. Make sure you use a known file extension (avi/mpeg/mp4)"); - } - - // Get the output format of the container - AVOutputFormat* pOutputFormat = mpOutputContext->oformat; - FALCOR_ASSERT((pOutputFormat->flags & AVFMT_NOFILE) == 0); // Problem. We want a file. - - // Create the video codec - AVCodec* pVideoCodec; - mpOutputStream = createVideoStream(mpOutputContext, desc.fps, getCodecID(desc.codec), mPath, pVideoCodec); - if(mpOutputStream == nullptr) - { - return false; - } - - mpCodecContext = createCodecContext(mpOutputContext, desc.width, desc.height, desc.fps, desc.bitrateMbps, desc.gopSize, getCodecID(desc.codec), pVideoCodec); - if(mpCodecContext == nullptr) - { - return false; - } - - // Open the video stream - if(openVideo(pVideoCodec, mpCodecContext, mpFrame, mPath) == false) - { - return false; - } - - // copy the stream parameters to the muxer - if(avcodec_parameters_from_context(mpOutputStream->codecpar, mpCodecContext) < 0) - { - return error(desc.path, "Could not copy the stream parameters\n"); - } - - av_dump_format(mpOutputContext, 0, mPath.string().c_str(), 1); - - // Open the output file - FALCOR_ASSERT((pOutputFormat->flags & AVFMT_NOFILE) == 0); // No output file required. Not sure if/when this happens. - if(avio_open(&mpOutputContext->pb, mPath.string().c_str(), AVIO_FLAG_WRITE) < 0) - { - return error(mPath, "Can't open output file."); - } - - // Write the stream header - if(avformat_write_header(mpOutputContext, nullptr) < 0) - { - return error(mPath, "Can't write file header."); - } - - mFormat = desc.format; - mRowPitch = getFormatBytesPerBlock(desc.format) * desc.width; - if(desc.flipY) - { - mpFlippedImage.reset(new uint8_t[desc.height * mRowPitch]); - } - - FALCOR_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) - { - return error(mPath, "Failed to allocate SWScale context"); - } - return true; - } - - bool flush(AVCodecContext* pCodecContext, AVFormatContext* pOutputContext, AVStream* pOutputStream, const std::filesystem::path& path) - { - while(true) - { - // Allocate a packet - std::unique_ptr> pPacket(av_packet_alloc(), [] (AVPacket* pPacket) { av_packet_free(&pPacket); }); - - int r = avcodec_receive_packet(pCodecContext, pPacket.get()); - if(r == AVERROR(EAGAIN) || r == AVERROR_EOF) - { - return true; - } - else if(r < 0) - { - error(path, "Can't retrieve packet"); - return false; - } - - // rescale output packet timestamp values from codec to stream timebase - av_packet_rescale_ts(pPacket.get(), pCodecContext->time_base, pOutputStream->time_base); - pPacket->stream_index = pOutputStream->index; - r = av_interleaved_write_frame(pOutputContext, pPacket.get()); - if(r < 0) - { - char msg[1024]; - av_make_error_string(msg, 1024, r); - error(path, "Failed when writing encoded frame to file"); - return false; - } - } - } - - void VideoEncoder::endCapture() - { - if(mpOutputContext) - { - // Flush the codex - avcodec_send_frame(mpCodecContext, nullptr); - flush(mpCodecContext, mpOutputContext, mpOutputStream, mPath); - - av_write_trailer(mpOutputContext); - - avio_closep(&mpOutputContext->pb); - avcodec_free_context(&mpCodecContext); - av_frame_free(&mpFrame); - sws_freeContext(mpSwsContext); - avformat_free_context(mpOutputContext); - mpOutputContext = nullptr; - mpOutputStream = nullptr; - } - mpFlippedImage.reset(); - } - - void VideoEncoder::appendFrame(const void* pData) - { - if(mpFlippedImage) - { - // Flip the image - for(int32_t h = 0; h < mpCodecContext->height; h++) - { - const uint8_t* pSrc = (uint8_t*)pData + h * mRowPitch; - uint8_t* pDst = mpFlippedImage.get() + (mpCodecContext->height - 1 - h) * mRowPitch; - memcpy(pDst, pSrc, mRowPitch); - } - - pData = mpFlippedImage.get(); - } - - uint8_t* src[AV_NUM_DATA_POINTERS] = {0}; - int32_t rowPitch[AV_NUM_DATA_POINTERS] = {0}; - src[0] = (uint8_t*)pData; - rowPitch[0] = (int32_t)mRowPitch; - - // Scale and convert the image - sws_scale(mpSwsContext, src, rowPitch, 0, mpCodecContext->height, mpFrame->data, mpFrame->linesize); - - // Encode the frame - int r = avcodec_send_frame(mpCodecContext, mpFrame); - mpFrame->pts++; - if(r == AVERROR(EAGAIN)) - { - if(flush(mpCodecContext, mpOutputContext, mpOutputStream, mPath) == false) - { - return; - } - } - else if(r < 0) - { - error(mPath, "Can't send video frame"); - return; - } - } - - FileDialogFilterVec VideoEncoder::getSupportedContainerForCodec(Codec codec) - { - FileDialogFilterVec filters; - const FileDialogFilter AVI{ "avi", "AVI (Audio Video Interleaved)"}; - const FileDialogFilter MP4{ "mp4", "MP4 (MPEG-4 Part 14)"}; - const FileDialogFilter MKV{ "mkv", "MKV (Matroska)\0*.mkv" }; - - switch(codec) - { - case VideoEncoder::Codec::Raw: - filters.push_back(AVI); - break; - 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::Codec::HEVC: - filters.push_back(MP4); - filters.push_back(MKV); - break; - default: - FALCOR_UNREACHABLE(); - } - return filters; - } - - FALCOR_SCRIPT_BINDING(VideoEncoder) - { - pybind11::enum_ codec(m, "Codec"); - codec.value("Raw", VideoEncoder::Codec::Raw); - codec.value("MPEG4", VideoEncoder::Codec::MPEG4); - codec.value("MPEG2", VideoEncoder::Codec::MPEG2); - codec.value("H264", VideoEncoder::Codec::H264); - codec.value("HEVC", VideoEncoder::Codec::HEVC); - } -} diff --git a/Source/Falcor/Utils/Video/VideoEncoder.h b/Source/Falcor/Utils/Video/VideoEncoder.h deleted file mode 100644 index 69683bdcb..000000000 --- a/Source/Falcor/Utils/Video/VideoEncoder.h +++ /dev/null @@ -1,100 +0,0 @@ -/*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. - # - # Redistribution and use in source and binary forms, with or without - # modification, are permitted provided that the following conditions - # are met: - # * Redistributions of source code must retain the above copyright - # notice, this list of conditions and the following disclaimer. - # * Redistributions in binary form must reproduce the above copyright - # notice, this list of conditions and the following disclaimer in the - # documentation and/or other materials provided with the distribution. - # * Neither the name of NVIDIA CORPORATION nor the names of its - # contributors may be used to endorse or promote products derived - # from this software without specific prior written permission. - # - # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY - # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - # (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/Macros.h" -#include "Core/API/Formats.h" -#include "Core/Platform/OS.h" -#include -#include - -struct AVFormatContext; -struct AVStream; -struct AVFrame; -struct SwsContext; -struct AVCodecContext; - -namespace Falcor -{ - class FALCOR_API VideoEncoder - { - public: - using UniquePtr = std::unique_ptr; - using UniqueConstPtr = std::unique_ptr; - - enum class Codec : int32_t - { - Raw, - H264, - HEVC, - MPEG2, - MPEG4, - }; - - struct Desc - { - uint32_t fps = 60; - uint32_t width = 0; - uint32_t height = 0; - float bitrateMbps = 4; - uint32_t gopSize = 10; - Codec codec = Codec::Raw; - ResourceFormat format = ResourceFormat::BGRA8UnormSrgb; - bool flipY = false; - std::filesystem::path path; - }; - - ~VideoEncoder(); - - /** Create a video encoder. - \param[in] desc Encoder settings. - \return A new encoder object, or returns nullptr on error. For example, due unsupported encoder settings. - */ - static UniquePtr create(const Desc& desc); - - void appendFrame(const void* pData); - void endCapture(); - - static bool isFormatSupported(ResourceFormat format); - static FileDialogFilterVec getSupportedContainerForCodec(Codec codec); - - private: - VideoEncoder(const std::filesystem::path& path); - bool init(const Desc& desc); - - AVFormatContext* mpOutputContext = nullptr; - AVStream* mpOutputStream = nullptr; - AVFrame* mpFrame = nullptr; - SwsContext* mpSwsContext = nullptr; - AVCodecContext* mpCodecContext = nullptr; - - const std::filesystem::path mPath; - ResourceFormat mFormat; - uint32_t mRowPitch = 0; - std::unique_ptr mpFlippedImage; // Used in case the image memory layout if bottom->top - }; -} diff --git a/Source/Falcor/Utils/Video/VideoEncoderUI.cpp b/Source/Falcor/Utils/Video/VideoEncoderUI.cpp deleted file mode 100644 index 5b3cad1b9..000000000 --- a/Source/Falcor/Utils/Video/VideoEncoderUI.cpp +++ /dev/null @@ -1,130 +0,0 @@ -/*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. - # - # Redistribution and use in source and binary forms, with or without - # modification, are permitted provided that the following conditions - # are met: - # * Redistributions of source code must retain the above copyright - # notice, this list of conditions and the following disclaimer. - # * Redistributions in binary form must reproduce the above copyright - # notice, this list of conditions and the following disclaimer in the - # documentation and/or other materials provided with the distribution. - # * Neither the name of NVIDIA CORPORATION nor the names of its - # contributors may be used to endorse or promote products derived - # from this software without specific prior written permission. - # - # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY - # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - **************************************************************************/ -#include "VideoEncoderUI.h" -#include "Core/Assert.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(CallbackStart startCaptureCB, CallbackEnd endCaptureCB) - { - return UniquePtr(new VideoEncoderUI(startCaptureCB, endCaptureCB)); - } - - VideoEncoderUI::VideoEncoderUI(CallbackStart startCaptureCB, CallbackEnd 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; - - { - auto g = w.group("Capture Options", true); - - g.checkbox("Capture UI", mCaptureUI); - g.tooltip("Check this box if you want the GUI recorded"); - - g.checkbox("Reset rendering", mResetOnFirstFrame); - g.tooltip("Check this box if you want the rendering to be reset for the first frame, for example to reset temporal accumulation"); - - g.checkbox("Use Time-Range", mUseTimeRange); - if (mUseTimeRange) - { - auto g = w.group("Time Range", true); - 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)) endCapture(); - } - - void VideoEncoderUI::startCapture() - { - if (!mCapturing) - { - if (saveFileDialog(VideoEncoder::getSupportedContainerForCodec(mCodec), mPath)) - { - FALCOR_ASSERT(mStartCB); - mCapturing = mStartCB(); - } - } - } - - void VideoEncoderUI::endCaptureUI(Gui::Window& w, bool codecOnly) - { - if (mEndCB) - { - if (w.button("End Recording")) - { - endCapture(); - } - } - } - - void VideoEncoderUI::endCapture() - { - if (mCapturing) - { - FALCOR_ASSERT(mEndCB); - mEndCB(); - mCapturing = false; - } - } -} diff --git a/Source/Falcor/Utils/Video/VideoEncoderUI.h b/Source/Falcor/Utils/Video/VideoEncoderUI.h deleted file mode 100644 index 54bd5920e..000000000 --- a/Source/Falcor/Utils/Video/VideoEncoderUI.h +++ /dev/null @@ -1,99 +0,0 @@ -/*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. - # - # Redistribution and use in source and binary forms, with or without - # modification, are permitted provided that the following conditions - # are met: - # * Redistributions of source code must retain the above copyright - # notice, this list of conditions and the following disclaimer. - # * Redistributions in binary form must reproduce the above copyright - # notice, this list of conditions and the following disclaimer in the - # documentation and/or other materials provided with the distribution. - # * Neither the name of NVIDIA CORPORATION nor the names of its - # contributors may be used to endorse or promote products derived - # from this software without specific prior written permission. - # - # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY - # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - # (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 "VideoEncoder.h" -#include "Core/Macros.h" -#include "Utils/UI/Gui.h" -#include -#include -#include - -namespace Falcor -{ - class FALCOR_API VideoEncoderUI - { - public: - using UniquePtr = std::unique_ptr; - using UniqueConstPtr = std::unique_ptr; - using CallbackStart = std::function; - using CallbackEnd = std::function; - - ~VideoEncoderUI() = default; - - /** Create video encoder UI. - \param[in] startCaptureCB Optional function called at start of capture. - \param[in] startCaptureCB Optional function called at end of capture. - \return New object, or throws an exception if creation failed. - */ - static UniquePtr create(CallbackStart startCaptureCB = nullptr, CallbackEnd endCaptureCB = nullptr); - - void render(Gui::Window& w, bool codecOnly = false); - void setCaptureState(bool state); - - 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; } - float getStartTime() const { return mStartTime; } - float getEndTime() const { return mEndTime; } - const std::filesystem::path& getPath() const { return mPath; } - - private: - VideoEncoderUI(CallbackStart startCaptureCB, CallbackEnd endCaptureCB); - - void startCapture(); - void endCapture(); - void startCaptureUI(Gui::Window& w, bool codecOnly); - void endCaptureUI(Gui::Window& w, bool codecOnly); - - bool mCapturing = false; - CallbackStart mStartCB = nullptr; - CallbackEnd mEndCB = nullptr; - - uint32_t mFPS = 60; - VideoEncoder::Codec mCodec = VideoEncoder::Codec::Raw; - - bool mUseTimeRange = false; - bool mCaptureUI = false; - bool mResetOnFirstFrame = false; - float mStartTime = 0; - float mEndTime = 4.f; - - std::filesystem::path mPath; - float mBitrate = 30.f; - uint32_t mGopSize = 10; - }; -} diff --git a/Source/Mogwai/CMakeLists.txt b/Source/Mogwai/CMakeLists.txt index 5cb8d7c69..c0e507fba 100644 --- a/Source/Mogwai/CMakeLists.txt +++ b/Source/Mogwai/CMakeLists.txt @@ -13,8 +13,6 @@ target_sources(Mogwai PRIVATE Extensions/Capture/CaptureTrigger.h Extensions/Capture/FrameCapture.cpp Extensions/Capture/FrameCapture.h - Extensions/Capture/VideoCapture.cpp - Extensions/Capture/VideoCapture.h Extensions/Profiler/TimingCapture.cpp Extensions/Profiler/TimingCapture.h ) diff --git a/Source/Mogwai/Extensions/Capture/CaptureTrigger.cpp b/Source/Mogwai/Extensions/Capture/CaptureTrigger.cpp index 08074cf66..ec9da23a9 100644 --- a/Source/Mogwai/Extensions/Capture/CaptureTrigger.cpp +++ b/Source/Mogwai/Extensions/Capture/CaptureTrigger.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -91,7 +91,7 @@ namespace Mogwai else mGraphRanges.clear(); } - void CaptureTrigger::beginFrame(RenderContext* pRenderContext, const Fbo::SharedPtr& pTargetFbo) + void CaptureTrigger::beginFrame(RenderContext* pRenderContext, const ref& pTargetFbo) { RenderGraph* pGraph = mpRenderer->getActiveGraph(); if (!pGraph) return; @@ -118,7 +118,7 @@ namespace Mogwai } } - void CaptureTrigger::endFrame(RenderContext* pRenderContext, const Fbo::SharedPtr& pTargetFbo) + void CaptureTrigger::endFrame(RenderContext* pRenderContext, const ref& pTargetFbo) { if (!mCurrent.pGraph) return; uint64_t frameId = mpRenderer->getGlobalClock().getFrame(); diff --git a/Source/Mogwai/Extensions/Capture/CaptureTrigger.h b/Source/Mogwai/Extensions/Capture/CaptureTrigger.h index 7fe154f5e..0aa581c95 100644 --- a/Source/Mogwai/Extensions/Capture/CaptureTrigger.h +++ b/Source/Mogwai/Extensions/Capture/CaptureTrigger.h @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -35,8 +35,8 @@ namespace Mogwai public: virtual ~CaptureTrigger() {}; - virtual void beginFrame(RenderContext* pRenderContext, const Fbo::SharedPtr& pTargetFbo) override final; - virtual void endFrame(RenderContext* pRenderContext, const Fbo::SharedPtr& pTargetFbo) override final; + virtual void beginFrame(RenderContext* pRenderContext, const ref& pTargetFbo) override final; + virtual void endFrame(RenderContext* pRenderContext, const ref& pTargetFbo) override final; virtual bool hasWindow() const override { return true; } virtual bool isWindowShown() const override { return mShowUI; } virtual void toggleWindow() override { mShowUI = !mShowUI; } diff --git a/Source/Mogwai/Extensions/Capture/FrameCapture.cpp b/Source/Mogwai/Extensions/Capture/FrameCapture.cpp index 3014ad1dd..03ad77959 100644 --- a/Source/Mogwai/Extensions/Capture/FrameCapture.cpp +++ b/Source/Mogwai/Extensions/Capture/FrameCapture.cpp @@ -161,7 +161,7 @@ namespace Mogwai const std::string outputName = pGraph->getOutputName(outputIndex); const std::string basename = getOutputNamePrefix(outputName) + std::to_string(mpRenderer->getGlobalClock().getFrame()); - const Texture::SharedPtr pOutput = pGraph->getOutput(outputIndex)->asTexture(); + const ref pOutput = pGraph->getOutput(outputIndex)->asTexture(); if (!pOutput) throw RuntimeError("Graph output {} is not a texture", outputName); const ResourceFormat format = pOutput->getFormat(); @@ -187,7 +187,7 @@ namespace Mogwai } // Copy relevant channels into new texture if necessary. - Texture::SharedPtr pTex = pOutput; + ref pTex = pOutput; if (outputChannels == 1 && channels > 1) { // Determine output format. @@ -237,7 +237,7 @@ namespace Mogwai } // Copy color channel into temporary texture. - pTex = Texture::create2D(mpRenderer->getDevice().get(), pOutput->getWidth(), pOutput->getHeight(), outputFormat, 1, 1, nullptr, ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess); + pTex = Texture::create2D(mpRenderer->getDevice(), pOutput->getWidth(), pOutput->getHeight(), outputFormat, 1, 1, nullptr, ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess); mpImageProcessing->copyColorChannel(pRenderContext, pOutput->getSRV(0, 1, 0, 1), pTex->getUAV(), mask); } diff --git a/Source/Mogwai/Extensions/Capture/VideoCapture.cpp b/Source/Mogwai/Extensions/Capture/VideoCapture.cpp deleted file mode 100644 index 7d9990f00..000000000 --- a/Source/Mogwai/Extensions/Capture/VideoCapture.cpp +++ /dev/null @@ -1,224 +0,0 @@ -/*************************************************************************** - # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. - # - # Redistribution and use in source and binary forms, with or without - # modification, are permitted provided that the following conditions - # are met: - # * Redistributions of source code must retain the above copyright - # notice, this list of conditions and the following disclaimer. - # * Redistributions in binary form must reproduce the above copyright - # notice, this list of conditions and the following disclaimer in the - # documentation and/or other materials provided with the distribution. - # * Neither the name of NVIDIA CORPORATION nor the names of its - # contributors may be used to endorse or promote products derived - # from this software without specific prior written permission. - # - # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY - # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - # (INCLUDING 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 "VideoCapture.h" -#include "Utils/Scripting/ScriptWriter.h" - -namespace Mogwai -{ - namespace - { - const std::string kScriptVar = "videoCapture"; - 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 kAddRanges = "addRanges"; - const std::string kPrint = "print"; - const std::string kOutputs = "outputs"; - - Texture::SharedPtr createTextureForBlit(Device* pDevice, const Texture* pSource) - { - FALCOR_ASSERT(pSource->getType() == Texture::Type::Texture2D); - return Texture::create2D(pDevice, pSource->getWidth(), pSource->getHeight(), ResourceFormat::RGBA8UnormSrgb, 1, 1, nullptr, Texture::BindFlags::RenderTarget); - } - } - - MOGWAI_EXTENSION(VideoCapture); - - VideoCapture::VideoCapture(Renderer* pRenderer) : CaptureTrigger(pRenderer, "Video Capture") - { - mpEncoderUI = VideoEncoderUI::create(); - } - - 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::renderBaseUI(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) - { - reportError("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("Error", "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(mpRenderer->getDevice().get(), pTex.get()); - pTex = encoder.pBlitTex; - } - - d.height = pTex->getHeight(); - d.width = pTex->getWidth(); - d.format = pTex->getFormat(); - d.path = getOutputNamePrefix(outputName) + std::to_string(r.first) + "." + std::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::registerScriptBindings(pybind11::module& m) - { - using namespace pybind11::literals; - - CaptureTrigger::registerScriptBindings(m); - - pybind11::class_ videoCapture(m, "VideoCapture"); - - // UI - auto getUI = [](VideoCapture* pVC) { return pVC->mShowUI; }; - auto setUI = [](VideoCapture* pVC, bool show) { pVC->mShowUI = show; }; - videoCapture.def_property(kUI.c_str(), getUI, setUI); - - // Settings - auto getCodec = [](VideoCapture* pVC) {return pVC->mpEncoderUI->getCodec(); }; - auto setCodec = [](VideoCapture* pVC, VideoEncoder::Codec c) {pVC->mpEncoderUI->setCodec(c); return pVC; }; - videoCapture.def_property(kCodec.c_str(), getCodec, setCodec); - - auto getFPS = [](VideoCapture* pVC) {return pVC->mpEncoderUI->getFPS(); }; - auto setFPS = [](VideoCapture* pVC, uint32_t fps) {pVC->mpEncoderUI->setFPS(fps); return pVC; }; - videoCapture.def_property(kFps.c_str(), getFPS, setFPS); - - auto getBitrate = [](VideoCapture* pVC) {return pVC->mpEncoderUI->getBitrate(); }; - auto setBitrate = [](VideoCapture* pVC, float bitrate) {pVC->mpEncoderUI->setBitrate(bitrate); return pVC; }; - videoCapture.def_property(kBitrate.c_str(), getBitrate, setBitrate); - - auto getGopSize = [](VideoCapture* pVC) {return pVC->mpEncoderUI->getGopSize(); }; - auto setGopSize = [](VideoCapture* pVC, uint32_t gop) {pVC->mpEncoderUI->setGopSize(gop); return pVC; }; - videoCapture.def_property(kGopSize.c_str(), getGopSize, setGopSize); - - // Ranges - videoCapture.def(kAddRanges.c_str(), pybind11::overload_cast(&VideoCapture::addRanges), "graph"_a, "ranges"_a); - videoCapture.def(kAddRanges.c_str(), pybind11::overload_cast(&VideoCapture::addRanges), "name"_a, "ranges"_a); - - auto printGraph = [](VideoCapture* pVC, RenderGraph* pGraph) { pybind11::print(pVC->graphRangesStr(pGraph)); }; - videoCapture.def(kPrint.c_str(), printGraph, "graph"_a); - - 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); - }; - videoCapture.def(kPrint.c_str(), printAllGraphs); - } - - std::string VideoCapture::getScriptVar() const - { - return kScriptVar; - } - - std::string VideoCapture::getScript(const std::string& var) const - { - if (mGraphRanges.empty()) return ""; - - std::string s("# Video Capture\n"); - s += CaptureTrigger::getScript(var); - s += ScriptWriter::makeSetProperty(var, kCodec, mpEncoderUI->getCodec()); - s += ScriptWriter::makeSetProperty(var, kFps, mpEncoderUI->getFPS()); - s += ScriptWriter::makeSetProperty(var, kBitrate, mpEncoderUI->getBitrate()); - s += ScriptWriter::makeSetProperty(var, kGopSize, mpEncoderUI->getGopSize()); - - for (const auto& g : mGraphRanges) - { - s += ScriptWriter::makeMemberFunc(var, kAddRanges, g.first->getName(), g.second); - } - return s; - } - - void VideoCapture::addRanges(const RenderGraph* pGraph, const range_vec& ranges) - { - for (auto r : ranges) addRange(pGraph, r.first, r.second); - } - - void VideoCapture::addRanges(const std::string& graphName, const range_vec& ranges) - { - auto pGraph = mpRenderer->getGraph(graphName).get(); - if (!pGraph) throw RuntimeError("Can't find a graph named '{}'", graphName); - this->addRanges(pGraph, ranges); - } - - std::string VideoCapture::graphRangesStr(const RenderGraph* pGraph) - { - const auto& g = mGraphRanges[pGraph]; - std::string s("\t"); - s += kRanges + " = " + ScriptBindings::repr(g); - return s; - } -} - diff --git a/Source/Mogwai/Extensions/Capture/VideoCapture.h b/Source/Mogwai/Extensions/Capture/VideoCapture.h deleted file mode 100644 index 3c7d6207e..000000000 --- a/Source/Mogwai/Extensions/Capture/VideoCapture.h +++ /dev/null @@ -1,65 +0,0 @@ -/*************************************************************************** - # Copyright (c) 2015-21, NVIDIA CORPORATION. All rights reserved. - # - # Redistribution and use in source and binary forms, with or without - # modification, are permitted provided that the following conditions - # are met: - # * Redistributions of source code must retain the above copyright - # notice, this list of conditions and the following disclaimer. - # * Redistributions in binary form must reproduce the above copyright - # notice, this list of conditions and the following disclaimer in the - # documentation and/or other materials provided with the distribution. - # * Neither the name of NVIDIA CORPORATION nor the names of its - # contributors may be used to endorse or promote products derived - # from this software without specific prior written permission. - # - # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY - # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - # (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" -#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 registerScriptBindings(pybind11::module& m) override; - virtual std::string getScriptVar() const override; - virtual std::string getScript(const std::string& var) const override; - virtual void triggerFrame(RenderContext* pCtx, RenderGraph* pGraph, uint64_t frameID) override; - - private: - VideoCapture(Renderer* pRenderer); - - void addRanges(const RenderGraph* pGraph, const range_vec& ranges); - void addRanges(const std::string& graphName, const range_vec& ranges); - std::string graphRangesStr(const RenderGraph* pGraph); - - VideoEncoderUI::UniquePtr mpEncoderUI; - - struct EncodeData - { - std::string output; - VideoEncoder::UniquePtr pEncoder; - Texture::SharedPtr pBlitTex; - }; - std::vector mEncoders; - }; -} diff --git a/Source/Mogwai/Extensions/Profiler/TimingCapture.cpp b/Source/Mogwai/Extensions/Profiler/TimingCapture.cpp index 4d332a8ab..6129db13c 100644 --- a/Source/Mogwai/Extensions/Profiler/TimingCapture.cpp +++ b/Source/Mogwai/Extensions/Profiler/TimingCapture.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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,7 +58,7 @@ namespace Mogwai return kScriptVar; } - void TimingCapture::beginFrame(RenderContext* pRenderContext, const Fbo::SharedPtr& pTargetFbo) + void TimingCapture::beginFrame(RenderContext* pRenderContext, const ref& pTargetFbo) { recordPreviousFrameTime(); } diff --git a/Source/Mogwai/Extensions/Profiler/TimingCapture.h b/Source/Mogwai/Extensions/Profiler/TimingCapture.h index b29f1e575..9e2eed98f 100644 --- a/Source/Mogwai/Extensions/Profiler/TimingCapture.h +++ b/Source/Mogwai/Extensions/Profiler/TimingCapture.h @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -37,7 +37,7 @@ namespace Mogwai virtual ~TimingCapture() = default; static UniquePtr create(Renderer* pRenderer); - virtual void beginFrame(RenderContext* pRenderContext, const Fbo::SharedPtr& pTargetFbo) override; + virtual void beginFrame(RenderContext* pRenderContext, const ref& pTargetFbo) override; virtual void registerScriptBindings(pybind11::module& m) override; virtual std::string getScriptVar() const override; diff --git a/Source/Mogwai/Mogwai.cpp b/Source/Mogwai/Mogwai.cpp index 0c3ac4ed7..80cae6913 100644 --- a/Source/Mogwai/Mogwai.cpp +++ b/Source/Mogwai/Mogwai.cpp @@ -28,9 +28,11 @@ #include "Falcor.h" #include "Mogwai.h" #include "MogwaiSettings.h" +#include "GlobalState.h" #include "Scene/Importer.h" #include "RenderGraph/RenderGraphImportExport.h" #include "RenderGraph/RenderPassStandardFlags.h" +#include "Utils/Scripting/Scripting.h" #include "Utils/Timing/TimeReport.h" #include "Utils/Settings.h" @@ -62,10 +64,12 @@ namespace Mogwai , mOptions(options) , mAppData(kAppDataPath) { + setActivePythonRenderGraphDevice(getDevice()); } Renderer::~Renderer() { + setActivePythonRenderGraphDevice(nullptr); } void Renderer::extend(Extension::CreateFunc func, const std::string& name) @@ -225,13 +229,13 @@ namespace Mogwai bool Renderer::renderDebugWindow(Gui::Widgets& widget, const Gui::DropdownList& dropdown, DebugWindow& data, const uint2& winSize) { // Get the current output, in case `renderOutputUI()` unmarks it - Texture::SharedPtr pTex = std::dynamic_pointer_cast(mGraphs[mActiveGraph].pGraph->getOutput(data.currentOutput)); + ref pTex = mGraphs[mActiveGraph].pGraph->getOutput(data.currentOutput)->asTexture(); std::string label = data.currentOutput + "##" + mGraphs[mActiveGraph].pGraph->getName(); if (!pTex) { reportError("Invalid output resource. Is not a texture."); } uint2 debugSize = (uint2)(float2(winSize) * float2(0.4f, 0.55f)); uint2 debugPos = winSize - debugSize; - debugPos -= 10; + debugPos -= 10u; // Display the dropdown Gui::Window debugWindow(widget.gui(), data.windowName.c_str(), debugSize, debugPos); @@ -241,7 +245,7 @@ namespace Mogwai renderOutputUI(widget, dropdown, data.currentOutput); debugWindow.separator(); - debugWindow.image(label.c_str(), pTex); + debugWindow.image(label.c_str(), pTex.get()); debugWindow.release(); return true; } @@ -257,7 +261,7 @@ namespace Mogwai void Renderer::graphOutputsGui(Gui::Widgets& widget) { - RenderGraph::SharedPtr pGraph = mGraphs[mActiveGraph].pGraph; + ref 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); @@ -364,7 +368,7 @@ namespace Mogwai } } - void Renderer::removeGraph(const RenderGraph::SharedPtr& pGraph) + void Renderer::removeGraph(const ref& pGraph) { for (auto& e : mpExtensions) e->removeGraph(pGraph.get()); size_t i = 0; @@ -382,7 +386,7 @@ namespace Mogwai else reportError("Can't find a graph named '" + graphName + "'. There's nothing to remove."); } - RenderGraph::SharedPtr Renderer::getGraph(const std::string& graphName) const + ref Renderer::getGraph(const std::string& graphName) const { for (const auto& g : mGraphs) { @@ -396,7 +400,7 @@ namespace Mogwai if (mGraphs.size()) removeGraph(mGraphs[mActiveGraph].pGraph); } - std::vector Renderer::getGraphOutputs(const RenderGraph::SharedPtr& pGraph) + std::vector Renderer::getGraphOutputs(const ref& pGraph) { std::vector outputs; for (size_t i = 0; i < pGraph->getOutputCount(); i++) @@ -406,7 +410,7 @@ namespace Mogwai return outputs; } - void Renderer::initGraph(const RenderGraph::SharedPtr& pGraph, GraphData* pData) + void Renderer::initGraph(const ref& pGraph, GraphData* pData) { if (!pData) { @@ -478,7 +482,7 @@ namespace Mogwai } } - void Renderer::addGraph(const RenderGraph::SharedPtr& pGraph) + void Renderer::addGraph(const ref& pGraph) { if (pGraph == nullptr) { @@ -500,7 +504,7 @@ namespace Mogwai initGraph(pGraph, pGraphData); } - void Renderer::setActiveGraph(const RenderGraph::SharedPtr& pGraph) + void Renderer::setActiveGraph(const ref& pGraph) { size_t index = 0; for (; index < mGraphs.size(); ++index) @@ -533,7 +537,7 @@ namespace Mogwai try { TimeReport timeReport; - setScene(SceneBuilder::create(getDevice(), path, getSettings(), buildFlags)->getScene()); + setScene(SceneBuilder(getDevice(), path, getSettings(), buildFlags).getScene()); timeReport.measure("Loading scene (total)"); timeReport.printToLog(); return; @@ -550,7 +554,7 @@ namespace Mogwai setScene(nullptr); } - void Renderer::setScene(const Scene::SharedPtr& pScene) + void Renderer::setScene(const ref& pScene) { mpScene = pScene; @@ -566,9 +570,9 @@ namespace Mogwai Sampler::Desc desc; desc.setFilterMode(Sampler::Filter::Linear, Sampler::Filter::Linear, Sampler::Filter::Linear); desc.setMaxAnisotropy(8); - mpSampler = Sampler::create(getDevice().get(), desc); + mpSampler = Sampler::create(getDevice(), desc); } - mpScene->getMaterialSystem()->setDefaultTextureSampler(mpSampler); + mpScene->getMaterialSystem().setDefaultTextureSampler(mpSampler); } for (auto& g : mGraphs) @@ -579,7 +583,7 @@ namespace Mogwai getGlobalClock().setTime(0); } - Scene::SharedPtr Renderer::getScene() const + ref Renderer::getScene() const { return mpScene; } @@ -634,21 +638,21 @@ namespace Mogwai } // Execute graph. - (*pGraph->getPassesDictionary())[kRenderPassRefreshFlags] = RenderPassRefreshFlags::None; + pGraph->getPassesDictionary()[kRenderPassRefreshFlags] = RenderPassRefreshFlags::None; pGraph->execute(pRenderContext); } - void Renderer::beginFrame(RenderContext* pRenderContext, const Fbo::SharedPtr& pTargetFbo) + void Renderer::beginFrame(RenderContext* pRenderContext, const ref& pTargetFbo) { for (auto& pe : mpExtensions) pe->beginFrame(pRenderContext, pTargetFbo); } - void Renderer::endFrame(RenderContext* pRenderContext, const Fbo::SharedPtr& pTargetFbo) + void Renderer::endFrame(RenderContext* pRenderContext, const ref& pTargetFbo) { for (auto& pe : mpExtensions) pe->endFrame(pRenderContext, pTargetFbo); } - void Renderer::onFrameRender(RenderContext* pRenderContext, const Fbo::SharedPtr& pTargetFbo) + void Renderer::onFrameRender(RenderContext* pRenderContext, const ref& pTargetFbo) { if (!mScriptPath.empty()) { @@ -693,7 +697,7 @@ namespace Mogwai // Blit main graph output to frame buffer. if (mGraphs[mActiveGraph].mainOutput.size()) { - Texture::SharedPtr pOutTex = std::dynamic_pointer_cast(pGraph->getOutput(mGraphs[mActiveGraph].mainOutput)); + ref pOutTex = pGraph->getOutput(mGraphs[mActiveGraph].mainOutput)->asTexture(); FALCOR_ASSERT(pOutTex); pRenderContext->blit(pOutTex->getSRV(), pTargetFbo->getRenderTargetView(0)); } @@ -718,7 +722,7 @@ namespace Mogwai if (mPipedOutput) { - Falcor::Texture::SharedPtr framebufferTexture = pTargetFbo->getColorTexture(0); + Falcor::ref framebufferTexture = pTargetFbo->getColorTexture(0); uint32_t subresource = framebufferTexture->getSubresourceIndex(0, 0); std::vector framebufferData = pRenderContext->readTextureSubresource(framebufferTexture.get(), subresource); fwrite(&framebufferData[0], 4 * pTargetFbo->getWidth() * pTargetFbo->getHeight(), 1, mPipedOutput); @@ -777,7 +781,7 @@ namespace Mogwai for (auto& g : mGraphs) { g.pGraph->onResize(getTargetFbo().get()); - Scene::SharedPtr graphScene = g.pGraph->getScene(); + ref graphScene = g.pGraph->getScene(); if (graphScene) graphScene->setCameraAspectRatio((float)width / (float)height); } if (mpScene) mpScene->setCameraAspectRatio((float)width / (float)height); diff --git a/Source/Mogwai/Mogwai.h b/Source/Mogwai/Mogwai.h index 8415d6b47..3dbaaa2b4 100644 --- a/Source/Mogwai/Mogwai.h +++ b/Source/Mogwai/Mogwai.h @@ -51,8 +51,8 @@ namespace Mogwai using CreateFunc = UniquePtr(*)(Renderer* pRenderer); virtual const std::string& getName() const { return mName; } - virtual void beginFrame(RenderContext* pRenderContext, const Fbo::SharedPtr& pTargetFbo) {}; - virtual void endFrame(RenderContext* pRenderContext, const Fbo::SharedPtr& pTargetFbo) {}; + virtual void beginFrame(RenderContext* pRenderContext, const ref& pTargetFbo) {}; + virtual void endFrame(RenderContext* pRenderContext, const ref& pTargetFbo) {}; virtual bool hasWindow() const { return false; } virtual bool isWindowShown() const { return false; } virtual void toggleWindow() {} @@ -96,7 +96,7 @@ namespace Mogwai void onLoad(RenderContext* pRenderContext) override; void onOptionsChange() override; - void onFrameRender(RenderContext* pRenderContext, const Fbo::SharedPtr& pTargetFbo) override; + void onFrameRender(RenderContext* pRenderContext, const ref& pTargetFbo) override; void onResize(uint32_t width, uint32_t height) override; bool onKeyEvent(const KeyboardEvent& e) override; bool onMouseEvent(const MouseEvent& e) override; @@ -144,7 +144,7 @@ namespace Mogwai struct GraphData { - RenderGraph::SharedPtr pGraph; + ref pGraph; std::string mainOutput; bool showAllOutputs = false; std::vector originalOutputs; @@ -153,26 +153,26 @@ namespace Mogwai Scene::UpdateFlags sceneUpdates = Scene::UpdateFlags::None; }; - Scene::SharedPtr mpScene; + ref mpScene; - void addGraph(const RenderGraph::SharedPtr& pGraph); - void setActiveGraph(const RenderGraph::SharedPtr& pGraph); - void removeGraph(const RenderGraph::SharedPtr& pGraph); + void addGraph(const ref& pGraph); + void setActiveGraph(const ref& pGraph); + void removeGraph(const ref& pGraph); void removeGraph(const std::string& graphName); - RenderGraph::SharedPtr getGraph(const std::string& graphName) const; - void initGraph(const RenderGraph::SharedPtr& pGraph, GraphData* pData); + ref getGraph(const std::string& graphName) const; + void initGraph(const ref& pGraph, GraphData* pData); void removeActiveGraph(); void loadSceneDialog(); void loadScene(std::filesystem::path path, SceneBuilder::Flags buildFlags = SceneBuilder::Flags::Default); void unloadScene(); - void setScene(const Scene::SharedPtr& pScene); - Scene::SharedPtr getScene() const; + void setScene(const ref& pScene); + ref getScene() const; void executeActiveGraph(RenderContext* pRenderContext); - void beginFrame(RenderContext* pRenderContext, const Fbo::SharedPtr& pTargetFbo); - void endFrame(RenderContext* pRenderContext, const Fbo::SharedPtr& pTargetFbo); + void beginFrame(RenderContext* pRenderContext, const ref& pTargetFbo); + void endFrame(RenderContext* pRenderContext, const ref& pTargetFbo); - std::vector getGraphOutputs(const RenderGraph::SharedPtr& pGraph); + std::vector getGraphOutputs(const ref& pGraph); void graphOutputsGui(Gui::Widgets& widget); bool renderDebugWindow(Gui::Widgets& widget, const Gui::DropdownList& dropdown, DebugWindow& data, const uint2& winSize); // Returns false if the window was closed void renderOutputUI(Gui::Widgets& widget, const Gui::DropdownList& dropdown, std::string& selectedOutput); @@ -186,7 +186,7 @@ namespace Mogwai std::vector mGraphs; uint32_t mActiveGraph = 0; - Sampler::SharedPtr mpSampler = nullptr; + ref mpSampler = nullptr; std::filesystem::path mScriptPath; // Editor stuff @@ -196,7 +196,7 @@ namespace Mogwai 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 + static constexpr size_t kInvalidProcessId = -1; // We use this to know that the editor was launching the viewer size_t mEditorProcess = 0; std::filesystem::path mEditorTempPath; std::string mEditorScript; diff --git a/Source/Mogwai/MogwaiScripting.cpp b/Source/Mogwai/MogwaiScripting.cpp index a8c5b74f0..4d96fe138 100644 --- a/Source/Mogwai/MogwaiScripting.cpp +++ b/Source/Mogwai/MogwaiScripting.cpp @@ -29,6 +29,7 @@ #include "Mogwai.h" #include "RenderGraph/RenderGraphIR.h" #include "RenderGraph/RenderGraphImportExport.h" +#include "Utils/Scripting/Scripting.h" #include "Utils/Scripting/ScriptWriter.h" #include "Utils/Settings.h" #include @@ -130,12 +131,12 @@ namespace Mogwai renderer.def(kSaveConfig.c_str(), &Renderer::saveConfig, "path"_a); renderer.def(kAddGraph.c_str(), &Renderer::addGraph, "graph"_a); renderer.def(kSetActiveGraph.c_str(), - [](Renderer* pRenderer, const RenderGraph::SharedPtr& pGraph) + [](Renderer* pRenderer, const ref& pGraph) { pRenderer->setActiveGraph(pGraph); }, "graph"_a); renderer.def(kRemoveGraph.c_str(), pybind11::overload_cast(&Renderer::removeGraph), "name"_a); - renderer.def(kRemoveGraph.c_str(), pybind11::overload_cast(&Renderer::removeGraph), "graph"_a); + renderer.def(kRemoveGraph.c_str(), pybind11::overload_cast&>(&Renderer::removeGraph), "graph"_a); renderer.def(kGetGraph.c_str(), &Renderer::getGraph, "name"_a); auto resizeFrameBuffer = [](Renderer* pRenderer, uint32_t width, uint32_t height) { pRenderer->resizeFrameBuffer(width, height); }; @@ -194,7 +195,6 @@ namespace Mogwai }; Scripting::getDefaultContext().setObject("fc", findExtension("Frame Capture")); // PYTHONDEPRECATED - Scripting::getDefaultContext().setObject("vc", findExtension("Video Capture")); // PYTHONDEPRECATED Scripting::getDefaultContext().setObject("tc", findExtension("Timing Capture")); // PYTHONDEPRECATED renderer.def("getSettings", pybind11::overload_cast<>(&Renderer::getSettings), pybind11::return_value_policy::reference); diff --git a/Source/Mogwai/MogwaiSettings.cpp b/Source/Mogwai/MogwaiSettings.cpp index cce0939cd..c022b0575 100644 --- a/Source/Mogwai/MogwaiSettings.cpp +++ b/Source/Mogwai/MogwaiSettings.cpp @@ -91,7 +91,7 @@ namespace Mogwai { for (uint32_t i = 0; i < count; i++) { - if (screenDims == resolutions[i]) return i; + if (all(screenDims == resolutions[i])) return i; } return kCustomIndex; }; diff --git a/Source/RenderPasses/AccumulatePass/AccumulatePass.cpp b/Source/RenderPasses/AccumulatePass/AccumulatePass.cpp index 73afc840a..4a7558871 100644 --- a/Source/RenderPasses/AccumulatePass/AccumulatePass.cpp +++ b/Source/RenderPasses/AccumulatePass/AccumulatePass.cpp @@ -30,7 +30,7 @@ static void regAccumulatePass(pybind11::module& m) { - pybind11::class_ pass(m, "AccumulatePass"); + pybind11::class_> pass(m, "AccumulatePass"); pass.def_property("enabled", &AccumulatePass::isEnabled, &AccumulatePass::setEnabled); pass.def("reset", &AccumulatePass::reset); @@ -83,13 +83,8 @@ namespace }; } -AccumulatePass::SharedPtr AccumulatePass::create(std::shared_ptr pDevice, const Dictionary& dict) -{ - return SharedPtr(new AccumulatePass(std::move(pDevice), dict)); -} - -AccumulatePass::AccumulatePass(std::shared_ptr pDevice, const Dictionary& dict) - : RenderPass(std::move(pDevice)) +AccumulatePass::AccumulatePass(ref pDevice, const Dictionary& dict) + : RenderPass(pDevice) { // Deserialize pass from dictionary. for (const auto& [key, value] : dict) @@ -188,15 +183,15 @@ void AccumulatePass::execute(RenderContext* pRenderContext, const RenderData& re } // Grab our input/output buffers. - Texture::SharedPtr pSrc = renderData.getTexture(kInputChannel); - Texture::SharedPtr pDst = renderData.getTexture(kOutputChannel); + ref pSrc = renderData.getTexture(kInputChannel); + ref pDst = renderData.getTexture(kOutputChannel); FALCOR_ASSERT(pSrc && pDst); const uint2 resolution = uint2(pSrc->getWidth(), pSrc->getHeight()); const bool resolutionMatch = pDst->getWidth() == resolution.x && pDst->getHeight() == resolution.y; // Reset accumulation when resolution changes. - if (resolution != mFrameDim) + if (any(resolution != mFrameDim)) { mFrameDim = resolution; reset(); @@ -233,7 +228,7 @@ void AccumulatePass::execute(RenderContext* pRenderContext, const RenderData& re } } -void AccumulatePass::accumulate(RenderContext* pRenderContext, const Texture::SharedPtr& pSrc, const Texture::SharedPtr& pDst) +void AccumulatePass::accumulate(RenderContext* pRenderContext, const ref& pSrc, const ref& pDst) { FALCOR_ASSERT(pSrc && pDst); FALCOR_ASSERT(pSrc->getWidth() == mFrameDim.x && pSrc->getHeight() == mFrameDim.y); @@ -270,18 +265,19 @@ void AccumulatePass::accumulate(RenderContext* pRenderContext, const Texture::Sh prepareAccumulation(pRenderContext, mFrameDim.x, mFrameDim.y); // Set shader parameters. - mpVars["PerFrameCB"]["gResolution"] = mFrameDim; - mpVars["PerFrameCB"]["gAccumCount"] = mFrameCount; - mpVars["PerFrameCB"]["gAccumulate"] = mEnabled; - mpVars["PerFrameCB"]["gMovingAverageMode"] = (mMaxFrameCount > 0); - mpVars["gCurFrame"] = pSrc; - mpVars["gOutputFrame"] = pDst; + auto var = mpVars->getRootVar(); + var["PerFrameCB"]["gResolution"] = mFrameDim; + var["PerFrameCB"]["gAccumCount"] = mFrameCount; + var["PerFrameCB"]["gAccumulate"] = mEnabled; + var["PerFrameCB"]["gMovingAverageMode"] = (mMaxFrameCount > 0); + var["gCurFrame"] = pSrc; + var["gOutputFrame"] = pDst; // Bind accumulation buffers. Some of these may be nullptr's. - mpVars["gLastFrameSum"] = mpLastFrameSum; - mpVars["gLastFrameCorr"] = mpLastFrameCorr; - mpVars["gLastFrameSumLo"] = mpLastFrameSumLo; - mpVars["gLastFrameSumHi"] = mpLastFrameSumHi; + var["gLastFrameSum"] = mpLastFrameSum; + var["gLastFrameCorr"] = mpLastFrameCorr; + var["gLastFrameSumLo"] = mpLastFrameSumLo; + var["gLastFrameSumHi"] = mpLastFrameSumHi; // Update the frame count. // The accumulation limit (mMaxFrameCount) has a special value of 0 (no limit) and is not supported in the SingleCompensated mode. @@ -351,7 +347,7 @@ void AccumulatePass::renderUI(Gui::Widgets& widget) } } -void AccumulatePass::setScene(RenderContext* pRenderContext, const Scene::SharedPtr& pScene) +void AccumulatePass::setScene(RenderContext* pRenderContext, const ref& pScene) { mpScene = pScene; @@ -383,7 +379,7 @@ void AccumulatePass::prepareAccumulation(RenderContext* pRenderContext, uint32_t { // 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) + auto prepareBuffer = [&](ref& pBuf, ResourceFormat format, bool bufUsed) { if (!bufUsed) { @@ -393,7 +389,7 @@ void AccumulatePass::prepareAccumulation(RenderContext* pRenderContext, uint32_t // (Re-)create buffer if needed. if (!pBuf || pBuf->getWidth() != width || pBuf->getHeight() != height) { - pBuf = Texture::create2D(mpDevice.get(), width, height, format, 1, 1, nullptr, Resource::BindFlags::ShaderResource | Resource::BindFlags::UnorderedAccess); + pBuf = Texture::create2D(mpDevice, width, height, format, 1, 1, nullptr, Resource::BindFlags::ShaderResource | Resource::BindFlags::UnorderedAccess); FALCOR_ASSERT(pBuf); reset(); } diff --git a/Source/RenderPasses/AccumulatePass/AccumulatePass.h b/Source/RenderPasses/AccumulatePass/AccumulatePass.h index 6abfeb0e1..c74b7d1f2 100644 --- a/Source/RenderPasses/AccumulatePass/AccumulatePass.h +++ b/Source/RenderPasses/AccumulatePass/AccumulatePass.h @@ -27,6 +27,7 @@ **************************************************************************/ #pragma once #include "Falcor.h" +#include "RenderGraph/RenderPass.h" #include "RenderGraph/RenderPassHelpers.h" using namespace Falcor; @@ -45,17 +46,16 @@ class AccumulatePass : public RenderPass public: FALCOR_PLUGIN_CLASS(AccumulatePass, "AccumulatePass", "Temporal accumulation."); - using SharedPtr = std::shared_ptr; + static ref create(ref pDevice, const Dictionary& dict) { return make_ref(pDevice, dict); } + AccumulatePass(ref pDevice, const Dictionary& dict); virtual ~AccumulatePass() = default; - static SharedPtr create(std::shared_ptr pDevice, const Dictionary& dict); - 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 void setScene(RenderContext* pRenderContext, const ref& pScene) override; virtual bool onMouseEvent(const MouseEvent& mouseEvent) override { return false; } virtual bool onKeyEvent(const KeyboardEvent& keyEvent) override { return false; } virtual void onHotReload(HotReloadFlags reloaded) override; @@ -81,23 +81,22 @@ class AccumulatePass : public RenderPass }; protected: - AccumulatePass(std::shared_ptr pDevice, const Dictionary& dict); void prepareAccumulation(RenderContext* pRenderContext, uint32_t width, uint32_t height); - void accumulate(RenderContext* pRenderContext, const Texture::SharedPtr& pSrc, const Texture::SharedPtr& pDst); + void accumulate(RenderContext* pRenderContext, const ref& pSrc, const ref& pDst); // 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; + ref mpScene; ///< The current scene (or nullptr if no scene). + std::map> mpProgram; ///< Accumulation programs, one per mode. + ref mpVars; ///< Program variables. + ref mpState; FormatType mSrcType; ///< Format type of the source that gets accumulated. uint32_t mFrameCount = 0; ///< Number of accumulated frames. This is reset upon changes. uint2 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. + ref mpLastFrameSum; ///< Last frame running sum. Used in Single and SingleKahan mode. + ref mpLastFrameCorr; ///< Last frame running compensation term. Used in SingleKahan mode. + ref mpLastFrameSumLo; ///< Last frame running sum (lo bits). Used in Double mode. + ref mpLastFrameSumHi; ///< Last frame running sum (hi bits). Used in Double mode. // UI variables bool mEnabled = true; ///< True if accumulation is enabled. diff --git a/Source/RenderPasses/BSDFViewer/BSDFViewer.cpp b/Source/RenderPasses/BSDFViewer/BSDFViewer.cpp index 707d9cc04..71fe2c61f 100644 --- a/Source/RenderPasses/BSDFViewer/BSDFViewer.cpp +++ b/Source/RenderPasses/BSDFViewer/BSDFViewer.cpp @@ -63,21 +63,16 @@ void BSDFViewer::registerBindings(pybind11::module& m) mode.value("Slice", BSDFViewerMode::Slice); } -BSDFViewer::SharedPtr BSDFViewer::create(std::shared_ptr pDevice, const Dictionary& dict) -{ - return SharedPtr(new BSDFViewer(std::move(pDevice), dict)); -} - -BSDFViewer::BSDFViewer(std::shared_ptr pDevice, const Dictionary& dict) - : RenderPass(std::move(pDevice)) +BSDFViewer::BSDFViewer(ref pDevice, const Dictionary& dict) + : RenderPass(pDevice) { parseDictionary(dict); // Create a high-quality pseudorandom number generator. mpSampleGenerator = SampleGenerator::create(mpDevice, SAMPLE_GENERATOR_UNIFORM); - mpPixelDebug = PixelDebug::create(mpDevice); - mpFence = GpuFence::create(mpDevice.get()); + mpPixelDebug = std::make_unique(mpDevice); + mpFence = GpuFence::create(mpDevice); } void BSDFViewer::parseDictionary(const Dictionary& dict) @@ -128,7 +123,7 @@ void BSDFViewer::compile(RenderContext* pRenderContext, const CompileData& compi mParams.viewportScale = float2(1.f / extent); } -void BSDFViewer::setScene(RenderContext* pRenderContext, const Scene::SharedPtr& pScene) +void BSDFViewer::setScene(RenderContext* pRenderContext, const ref& pScene) { mpScene = pScene; mpEnvMap = nullptr; @@ -152,7 +147,7 @@ void BSDFViewer::setScene(RenderContext* pRenderContext, const Scene::SharedPtr& // Compile program and bind the scene. mpViewerPass->setVars(nullptr); // Trigger vars creation - mpViewerPass["gScene"] = mpScene->getParameterBlock(); + mpViewerPass->getRootVar()["gScene"] = mpScene->getParameterBlock(); // Setup environment map. mpEnvMap = mpScene->getEnvMap(); @@ -205,7 +200,7 @@ void BSDFViewer::execute(RenderContext* pRenderContext, const RenderData& render else mpViewerPass->removeDefine("SpecularMaskingFunction"); // Setup constants. - mParams.cameraViewportScale = std::tan(glm::radians(mParams.cameraFovY / 2.f)) * mParams.cameraDistance; + mParams.cameraViewportScale = std::tan(math::radians(mParams.cameraFovY / 2.f)) * mParams.cameraDistance; mParams.useEnvMap = mUseEnvMap && mpEnvMap != nullptr; // Set resources. @@ -213,8 +208,8 @@ void BSDFViewer::execute(RenderContext* pRenderContext, const RenderData& render if (!mpPixelDataBuffer) { - mpPixelDataBuffer = Buffer::createStructured(mpDevice.get(), var["pixelData"], 1, ResourceBindFlags::UnorderedAccess, Buffer::CpuAccess::None, nullptr, false); - mpPixelStagingBuffer = Buffer::createStructured(mpDevice.get(), var["pixelData"], 1, ResourceBindFlags::None, Buffer::CpuAccess::Read, nullptr, false); + mpPixelDataBuffer = Buffer::createStructured(mpDevice, var["pixelData"], 1, ResourceBindFlags::UnorderedAccess, Buffer::CpuAccess::None, nullptr, false); + mpPixelStagingBuffer = Buffer::createStructured(mpDevice, var["pixelData"], 1, ResourceBindFlags::None, Buffer::CpuAccess::Read, nullptr, false); } var["params"].setBlob(mParams); @@ -476,7 +471,7 @@ bool BSDFViewer::onMouseEvent(const MouseEvent& mouseEvent) { if (mouseEvent.type == MouseEvent::Type::ButtonDown && mouseEvent.button == Input::MouseButton::Left) { - mParams.selectedPixel = glm::clamp((int2)(mouseEvent.pos * (float2)mParams.frameDim), { 0,0 }, (int2)mParams.frameDim - 1); + mParams.selectedPixel = clamp((int2)(mouseEvent.pos * (float2)mParams.frameDim), { 0,0 }, (int2)mParams.frameDim - 1); } return mpPixelDebug->onMouseEvent(mouseEvent); diff --git a/Source/RenderPasses/BSDFViewer/BSDFViewer.cs.slang b/Source/RenderPasses/BSDFViewer/BSDFViewer.cs.slang index cd2434522..2f3abe0b6 100644 --- a/Source/RenderPasses/BSDFViewer/BSDFViewer.cs.slang +++ b/Source/RenderPasses/BSDFViewer/BSDFViewer.cs.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -167,7 +167,7 @@ struct BSDFViewer SurfaceData data = {}; // Setup Falcor's ShadingData based on the selected scene material and lobes. - data.sd = gScene.materials.prepareShadingData(v, params.materialID, viewDir, lod, params.useNormalMapping); + data.sd = gScene.materials.prepareShadingData(v, params.materialID, viewDir, lod); data.sd.mtl.setActiveLobes(getActiveLobes()); return data; @@ -230,7 +230,8 @@ struct BSDFViewer data.wo = lightDir; // Create BSDF instance. - let mi = gScene.materials.getMaterialInstance(data.sd, lod); + uint hints = !params.useNormalMapping ? (uint)MaterialInstanceHints::DisableNormalMapping : 0; + let mi = gScene.materials.getMaterialInstance(data.sd, lod, hints); data.bsdfProperties = mi.getProperties(data.sd); // Evaluate BSDF at shading point. @@ -239,7 +240,7 @@ struct BSDFViewer // Remove cosine term if it's disabled in the viewer. if (!params.applyNdotL) { - float NdotL = dot(data.sd.N, data.wo); + float NdotL = dot(data.sd.frame.N, data.wo); f = NdotL > 0.f ? f / NdotL : float3(0); } @@ -262,7 +263,7 @@ struct BSDFViewer s.wo = -normalize(params.lightDir); s.weight = mi.eval(sd, s.wo, sg); s.pdf = 1.f; - return dot(sd.N, s.wo) > 0.f; + return dot(sd.frame.N, s.wo) > 0.f; } else { @@ -297,7 +298,8 @@ struct BSDFViewer data = prepareShadingData(v, -rayDir, lod); // Create BSDF instance. - let mi = gScene.materials.getMaterialInstance(data.sd, lod); + uint hints = !params.useNormalMapping ? (uint)MaterialInstanceHints::DisableNormalMapping : 0; + let mi = gScene.materials.getMaterialInstance(data.sd, lod, hints); data.bsdfProperties = mi.getProperties(data.sd); float3 output = 0; @@ -367,9 +369,9 @@ struct BSDFViewer PixelData px = {}; px.texC = data.sd.uv; - px.N = data.sd.N; - px.T = data.sd.T; - px.B = data.sd.B; + px.N = data.sd.frame.N; + px.T = data.sd.frame.T; + px.B = data.sd.frame.B; px.wi = data.sd.V; px.wo = data.wo; px.output = output; diff --git a/Source/RenderPasses/BSDFViewer/BSDFViewer.h b/Source/RenderPasses/BSDFViewer/BSDFViewer.h index 79a19361b..8dd72502a 100644 --- a/Source/RenderPasses/BSDFViewer/BSDFViewer.h +++ b/Source/RenderPasses/BSDFViewer/BSDFViewer.h @@ -27,10 +27,11 @@ **************************************************************************/ #pragma once #include "Falcor.h" -#include "BSDFViewerParams.slang" +#include "RenderGraph/RenderPass.h" #include "Utils/Sampling/SampleGenerator.h" #include "Utils/Debug/PixelDebug.h" #include "Scene/Lights/EnvMap.h" +#include "BSDFViewerParams.slang" using namespace Falcor; @@ -39,48 +40,45 @@ class BSDFViewer : public RenderPass public: FALCOR_PLUGIN_CLASS(BSDFViewer, "BSDFViewer", "BSDF inspection utility."); - using SharedPtr = std::shared_ptr; + static ref create(ref pDevice, const Dictionary& dict) { return make_ref(pDevice, dict); } - /** Create a new object - */ - static SharedPtr create(std::shared_ptr pDevice, const Dictionary& dict); + BSDFViewer(ref pDevice, const Dictionary& dict); 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 renderUI(Gui::Widgets& widget) override; - virtual void setScene(RenderContext* pRenderContext, const Scene::SharedPtr& pScene) override; + virtual void setScene(RenderContext* pRenderContext, const ref& pScene) override; virtual bool onMouseEvent(const MouseEvent& mouseEvent) override; virtual bool onKeyEvent(const KeyboardEvent& keyEvent) override; static void registerBindings(pybind11::module& m); private: - BSDFViewer(std::shared_ptr pDevice, const Dictionary& dict); void parseDictionary(const Dictionary& dict); bool loadEnvMap(const std::filesystem::path& path); void readPixelData(); // Internal state - Scene::SharedPtr mpScene; ///< Loaded scene if any, nullptr otherwise. - EnvMap::SharedPtr mpEnvMap; ///< Environment map if loaded, nullptr otherwise. + ref mpScene; ///< Loaded scene if any, nullptr otherwise. + ref mpEnvMap; ///< Environment map if loaded, nullptr otherwise. bool mUseEnvMap = true; ///< Use environment map if available. BSDFViewerParams mParams; ///< Parameters shared with the shaders. - SampleGenerator::SharedPtr mpSampleGenerator; ///< Random number generator for the integrator. + ref mpSampleGenerator; ///< Random number generator for the integrator. bool mOptionsChanged = false; - GpuFence::SharedPtr mpFence; ///< GPU fence for synchronizing readback. - Buffer::SharedPtr mpPixelDataBuffer; ///< Buffer for data for the selected pixel. - Buffer::SharedPtr mpPixelStagingBuffer; ///< Staging buffer for readback of pixel data. + ref mpFence; ///< GPU fence for synchronizing readback. + ref mpPixelDataBuffer; ///< Buffer for data for the selected pixel. + ref mpPixelStagingBuffer; ///< Staging buffer for readback of pixel data. PixelData mPixelData; ///< Pixel data for the selected pixel (if valid). bool mPixelDataValid = false; bool mPixelDataAvailable = false; - PixelDebug::SharedPtr mpPixelDebug; ///< Utility class for pixel debugging (print in shaders). + std::unique_ptr mpPixelDebug; ///< Utility class for pixel debugging (print in shaders). - ComputePass::SharedPtr mpViewerPass; + ref mpViewerPass; // UI variables Gui::DropdownList mMaterialList; diff --git a/Source/RenderPasses/BlitPass/BlitPass.cpp b/Source/RenderPasses/BlitPass/BlitPass.cpp index 2e0bc9d1d..160d086ea 100644 --- a/Source/RenderPasses/BlitPass/BlitPass.cpp +++ b/Source/RenderPasses/BlitPass/BlitPass.cpp @@ -36,7 +36,7 @@ namespace void regBlitPass(pybind11::module& m) { - pybind11::class_ pass(m, "BlitPass"); + pybind11::class_> pass(m, "BlitPass"); pass.def_property(kFilter, &BlitPass::getFilter, &BlitPass::setFilter); } } @@ -47,6 +47,12 @@ extern "C" FALCOR_API_EXPORT void registerPlugin(Falcor::PluginRegistry& registr ScriptBindings::registerBinding(regBlitPass); } +BlitPass::BlitPass(ref pDevice, const Dictionary& dict) + : RenderPass(pDevice) +{ + parseDictionary(dict); +} + RenderPassReflection BlitPass::reflect(const CompileData& compileData) { RenderPassReflection r; @@ -65,17 +71,6 @@ void BlitPass::parseDictionary(const Dictionary& dict) } } -BlitPass::SharedPtr BlitPass::create(std::shared_ptr pDevice, const Dictionary& dict) -{ - return SharedPtr(new BlitPass(std::move(pDevice), dict)); -} - -BlitPass::BlitPass(std::shared_ptr pDevice, const Dictionary& dict) - : RenderPass(std::move(pDevice)) -{ - parseDictionary(dict); -} - Dictionary BlitPass::getScriptingDictionary() { Dictionary d; diff --git a/Source/RenderPasses/BlitPass/BlitPass.h b/Source/RenderPasses/BlitPass/BlitPass.h index 3a55a9255..cae4e6a38 100644 --- a/Source/RenderPasses/BlitPass/BlitPass.h +++ b/Source/RenderPasses/BlitPass/BlitPass.h @@ -27,6 +27,7 @@ **************************************************************************/ #pragma once #include "Falcor.h" +#include "RenderGraph/RenderPass.h" using namespace Falcor; @@ -39,11 +40,9 @@ class BlitPass : public RenderPass public: FALCOR_PLUGIN_CLASS(BlitPass, "BlitPass", "Blit a texture into a different texture."); - using SharedPtr = std::shared_ptr; + static ref create(ref pDevice, const Dictionary& dict) { return make_ref(pDevice, dict); } - /** Create a new object - */ - static SharedPtr create(std::shared_ptr pDevice, const Dictionary& dict); + BlitPass(ref pDevice, const Dictionary& dict); virtual Dictionary getScriptingDictionary() override; virtual RenderPassReflection reflect(const CompileData& compileData) override; @@ -55,7 +54,6 @@ class BlitPass : public RenderPass void setFilter(Sampler::Filter filter) { mFilter = filter; } private: - BlitPass(std::shared_ptr pDevice, const Dictionary& dict); void parseDictionary(const Dictionary& dict); Sampler::Filter mFilter = Sampler::Filter::Linear; diff --git a/Source/RenderPasses/DLSSPass/DLSSPass.cpp b/Source/RenderPasses/DLSSPass/DLSSPass.cpp index 95e417ddf..b79718f75 100644 --- a/Source/RenderPasses/DLSSPass/DLSSPass.cpp +++ b/Source/RenderPasses/DLSSPass/DLSSPass.cpp @@ -57,7 +57,7 @@ const char kExposure[] = "exposure"; static void registerDLSSPass(pybind11::module& m) { - pybind11::class_ pass(m, "DLSSPass"); + pybind11::class_> pass(m, "DLSSPass"); pybind11::enum_ profile(m, "DLSSProfile"); profile.value("MaxPerf", DLSSPass::Profile::MaxPerf); @@ -75,12 +75,7 @@ extern "C" FALCOR_API_EXPORT void registerPlugin(Falcor::PluginRegistry& registr ScriptBindings::registerBinding(registerDLSSPass); } -DLSSPass::SharedPtr DLSSPass::create(std::shared_ptr pDevice, const Dictionary& dict) -{ - return SharedPtr(new DLSSPass(std::move(pDevice), dict)); -} - -DLSSPass::DLSSPass(std::shared_ptr pDevice, const Dictionary& dict) : RenderPass(std::move(pDevice)) +DLSSPass::DLSSPass(ref pDevice, const Dictionary& dict) : RenderPass(pDevice) { for (const auto& [key, value] : dict) { @@ -105,7 +100,7 @@ DLSSPass::DLSSPass(std::shared_ptr pDevice, const Dictionary& dict) : Re logWarning("Unknown field '{}' in a DLSSPass dictionary.", key); } - mpExposure = Texture::create2D(mpDevice.get(), 1, 1, ResourceFormat::R32Float, 1, 1); + mpExposure = Texture::create2D(mpDevice, 1, 1, ResourceFormat::R32Float, 1, 1); } Dictionary DLSSPass::getScriptingDictionary() @@ -137,7 +132,7 @@ RenderPassReflection DLSSPass::reflect(const CompileData& compileData) return r; } -void DLSSPass::setScene(RenderContext* pRenderContext, const std::shared_ptr& pScene) +void DLSSPass::setScene(RenderContext* pRenderContext, const ref& pScene) { mpScene = pScene; } @@ -193,7 +188,7 @@ void DLSSPass::renderUI(Gui::Widgets& widget) void DLSSPass::initializeDLSS(RenderContext* pRenderContext) { if (!mpNGXWrapper) - mpNGXWrapper.reset(new NGXWrapper(mpDevice.get(), getRuntimeDirectory(), getRuntimeDirectory())); + mpNGXWrapper.reset(new NGXWrapper(mpDevice, getRuntimeDirectory(), getRuntimeDirectory())); Texture* target = nullptr; // Not needed for D3D12 implementation bool depthInverted = false; @@ -215,7 +210,7 @@ void DLSSPass::initializeDLSS(RenderContext* pRenderContext) mDLSSOutputSize = uint2(float2(mInputSize) * float2(mInputSize) / float2(optimalSettings.optimalRenderSize)); mpOutput = Texture::create2D( - mpDevice.get(), mDLSSOutputSize.x, mDLSSOutputSize.y, ResourceFormat::RGBA32Float, 1, 1, nullptr, + mpDevice, mDLSSOutputSize.x, mDLSSOutputSize.y, ResourceFormat::RGBA32Float, 1, 1, nullptr, ResourceBindFlags::UnorderedAccess | ResourceBindFlags::ShaderResource ); @@ -246,7 +241,7 @@ void DLSSPass::executeInternal(RenderContext* pRenderContext, const RenderData& mExposureUpdated = false; } - if (mRecreate || inputSize != mInputSize) + if (mRecreate || any(inputSize != mInputSize)) { mRecreate = false; mInputSize = inputSize; @@ -256,7 +251,7 @@ void DLSSPass::executeInternal(RenderContext* pRenderContext, const RenderData& // If pass output is configured to be fixed to DLSS output, but the sizes don't match, // we'll trigger a graph recompile to update the pass I/O size requirements. // This causes a one frame delay, but unfortunately we don't know the size until after initializeDLSS(). - if (mOutputSizeSelection == RenderPassHelpers::IOSize::Fixed && mPassOutputSize != mDLSSOutputSize) + if (mOutputSizeSelection == RenderPassHelpers::IOSize::Fixed && any(mPassOutputSize != mDLSSOutputSize)) requestRecompile(); } @@ -264,7 +259,7 @@ void DLSSPass::executeInternal(RenderContext* pRenderContext, const RenderData& // Fetch inputs and verify their dimensions. auto getInput = [=](const std::string& name) { - const auto& tex = renderData.getTexture(name); + auto tex = renderData.getTexture(name); if (!tex) throw RuntimeError("DLSSPass: Missing input '{}'", name); if (tex->getWidth() != mInputSize.x || tex->getHeight() != mInputSize.y) @@ -272,19 +267,19 @@ void DLSSPass::executeInternal(RenderContext* pRenderContext, const RenderData& return tex; }; - const auto& color = getInput(kColorInput); - const auto& depth = getInput(kDepthInput); - const auto& motionVectors = getInput(kMotionVectorsInput); + auto color = getInput(kColorInput); + auto depth = getInput(kDepthInput); + auto motionVectors = getInput(kMotionVectorsInput); // Determine if we can write directly to the render pass output. // Otherwise we'll output to an internal buffer and blit to the pass output. FALCOR_ASSERT(mpOutput->getWidth() == mDLSSOutputSize.x && mpOutput->getHeight() == mDLSSOutputSize.y); - bool useInternalBuffer = (mDLSSOutputSize != mPassOutputSize || pOutput->getFormat() != mpOutput->getFormat()); + bool useInternalBuffer = (any(mDLSSOutputSize != mPassOutputSize) || pOutput->getFormat() != mpOutput->getFormat()); - const auto& output = useInternalBuffer ? mpOutput : pOutput; + auto output = useInternalBuffer ? mpOutput : pOutput; // In DLSS X-jitter should go left-to-right, Y-jitter should go top-to-bottom. - // Falcor is using glm::perspective() that gives coordinate system with + // Falcor is using math::perspective() that gives coordinate system with // X from -1 to 1, left-to-right, and Y from -1 to 1, bottom-to-top. // Therefore, we need to flip the Y-jitter only. const auto& camera = mpScene->getCamera(); diff --git a/Source/RenderPasses/DLSSPass/DLSSPass.h b/Source/RenderPasses/DLSSPass/DLSSPass.h index 775c73fce..d295ad529 100644 --- a/Source/RenderPasses/DLSSPass/DLSSPass.h +++ b/Source/RenderPasses/DLSSPass/DLSSPass.h @@ -38,8 +38,6 @@ class DLSSPass : public RenderPass public: FALCOR_PLUGIN_CLASS(DLSSPass, "DLSSPass", "DL antialiasing/upscaling."); - using SharedPtr = std::shared_ptr; - enum class Profile : uint32_t { MaxPerf, @@ -53,17 +51,17 @@ class DLSSPass : public RenderPass Relative, ///< Motion vectors are provided in relative screen space length (pixels divided by screen width/height). }; - static SharedPtr create(std::shared_ptr pDevice, const Dictionary& dict); + static ref create(ref pDevice, const Dictionary& dict) { return make_ref(pDevice, dict); } + + DLSSPass(ref pDevice, const Dictionary& dict); virtual Dictionary getScriptingDictionary() override; virtual RenderPassReflection reflect(const CompileData& compileData) override; - virtual void setScene(RenderContext* pRenderContext, const std::shared_ptr& pScene) override; + virtual void setScene(RenderContext* pRenderContext, const ref& pScene) override; virtual void execute(RenderContext* pRenderContext, const RenderData& renderData) override; virtual void renderUI(Gui::Widgets& widget) override; private: - DLSSPass(std::shared_ptr pDevice, const Dictionary& dict); - void initializeDLSS(RenderContext* pRenderContext); void executeInternal(RenderContext* pRenderContext, const RenderData& renderData); @@ -82,9 +80,9 @@ class DLSSPass : public RenderPass uint2 mPassOutputSize = {}; ///< Pass output size in pixels. If different from DLSS output size, the image gets bilinearly resampled. RenderPassHelpers::IOSize mOutputSizeSelection = RenderPassHelpers::IOSize::Default; ///< Selected output size. - Scene::SharedPtr mpScene; - Texture::SharedPtr mpOutput; ///< Internal output buffer. This is used if format/size conversion upon output is needed. - Texture::SharedPtr mpExposure; ///< Texture of size 1x1 holding exposure value. + ref mpScene; + ref mpOutput; ///< Internal output buffer. This is used if format/size conversion upon output is needed. + ref mpExposure; ///< Texture of size 1x1 holding exposure value. std::unique_ptr mpNGXWrapper; }; diff --git a/Source/RenderPasses/DLSSPass/NGXWrapper.cpp b/Source/RenderPasses/DLSSPass/NGXWrapper.cpp index 5b5208bc9..11bc8926e 100644 --- a/Source/RenderPasses/DLSSPass/NGXWrapper.cpp +++ b/Source/RenderPasses/DLSSPass/NGXWrapper.cpp @@ -44,11 +44,11 @@ #include #include -#define THROW_IF_FAILED(call) \ - { \ - NVSDK_NGX_Result result = call; \ - if (NVSDK_NGX_FAILED(result)) \ - throw RuntimeError(#call " failed with error {}", resultToString(result)); \ +#define THROW_IF_FAILED(call) \ + { \ + NVSDK_NGX_Result result_ = call; \ + if (NVSDK_NGX_FAILED(result_)) \ + throw RuntimeError(#call " failed with error {}", resultToString(result_)); \ } namespace Falcor @@ -88,7 +88,11 @@ VkImageAspectFlags getAspectMaskFromFormat(VkFormat format) } // namespace -NGXWrapper::NGXWrapper(Device* pDevice, const std::filesystem::path& applicationDataPath, const std::filesystem::path& featureSearchPath) +NGXWrapper::NGXWrapper( + ref pDevice, + const std::filesystem::path& applicationDataPath, + const std::filesystem::path& featureSearchPath +) : mpDevice(pDevice) { initializeNGX(applicationDataPath, featureSearchPath); @@ -303,7 +307,7 @@ NGXWrapper::OptimalSettings NGXWrapper::queryOptimalSettings(uint2 displaySize, )); // Depending on what version of DLSS DLL is being used, a sharpness of > 1.f was possible. - settings.sharpness = clamp(settings.sharpness, -1.f, 1.f); + settings.sharpness = math::clamp(settings.sharpness, -1.f, 1.f); return settings; } diff --git a/Source/RenderPasses/DLSSPass/NGXWrapper.h b/Source/RenderPasses/DLSSPass/NGXWrapper.h index d11675252..9ab486e01 100644 --- a/Source/RenderPasses/DLSSPass/NGXWrapper.h +++ b/Source/RenderPasses/DLSSPass/NGXWrapper.h @@ -54,7 +54,7 @@ class NGXWrapper }; /// Constructor. Throws an exception if unable to initialize NGX. - NGXWrapper(Device* pDevice, const std::filesystem::path& applicationDataPath, const std::filesystem::path& featureSearchPath); + NGXWrapper(ref pDevice, const std::filesystem::path& applicationDataPath, const std::filesystem::path& featureSearchPath); ~NGXWrapper(); /// Query optimal DLSS settings for a given resolution and performance/quality profile. @@ -95,7 +95,7 @@ class NGXWrapper void initializeNGX(const std::filesystem::path& applicationDataPath, const std::filesystem::path& featureSearchPath); void shutdownNGX(); - Device* mpDevice = nullptr; + ref mpDevice; bool mInitialized = false; NVSDK_NGX_Parameter* mpParameters = nullptr; diff --git a/Source/RenderPasses/DebugPasses/ColorMapPass/ColorMapPass.cpp b/Source/RenderPasses/DebugPasses/ColorMapPass/ColorMapPass.cpp index 809a8dce4..c091b721b 100644 --- a/Source/RenderPasses/DebugPasses/ColorMapPass/ColorMapPass.cpp +++ b/Source/RenderPasses/DebugPasses/ColorMapPass/ColorMapPass.cpp @@ -62,8 +62,8 @@ void ColorMapPass::registerScriptBindings(pybind11::module& m) colorMap.value("Inferno", ColorMap::Inferno); } -ColorMapPass::ColorMapPass(std::shared_ptr pDevice, const Dictionary& dict) - : RenderPass(std::move(pDevice)) +ColorMapPass::ColorMapPass(ref pDevice, const Dictionary& dict) + : RenderPass(pDevice) { for (const auto& [key, value] : dict) { @@ -75,13 +75,7 @@ ColorMapPass::ColorMapPass(std::shared_ptr pDevice, const Dictionary& di else logWarning("Unknown field '{}' in a ColorMapPass dictionary.", key); } - mpFbo = Fbo::create(mpDevice.get()); -} - -ColorMapPass::SharedPtr ColorMapPass::create(std::shared_ptr pDevice, const Dictionary& dict) -{ - SharedPtr pPass = SharedPtr(new ColorMapPass(std::move(pDevice), dict)); - return pPass; + mpFbo = Fbo::create(mpDevice); } Dictionary ColorMapPass::getScriptingDictionary() @@ -124,8 +118,8 @@ void ColorMapPass::execute(RenderContext* pRenderContext, const RenderData& rend // Smoothly shrink the auto range when the the range of the input shrinks. double alpha = 0.01; - mAutoMinValue = glm::lerp(mAutoMinValue, minValue, alpha); - mAutoMaxValue = glm::lerp(mAutoMaxValue, maxValue, alpha); + mAutoMinValue = math::lerp(mAutoMinValue, minValue, alpha); + mAutoMaxValue = math::lerp(mAutoMaxValue, maxValue, alpha); mMinValue = (float)mAutoMinValue; mMaxValue = (float)mAutoMaxValue; @@ -168,8 +162,9 @@ void ColorMapPass::execute(RenderContext* pRenderContext, const RenderData& rend params.minValue = mMinValue; params.maxValue = mMaxValue; - mpColorMapPass["gTexture"] = inputTexture; - mpColorMapPass["StaticCB"]["gParams"].setBlob(params); + auto var = mpColorMapPass->getRootVar(); + var["gTexture"] = inputTexture; + var["StaticCB"]["gParams"].setBlob(params); mpFbo->attachColorTarget(outputTexture, 0); mpColorMapPass->getState()->setFbo(mpFbo); mpColorMapPass->execute(pRenderContext, mpFbo); @@ -184,14 +179,14 @@ void ColorMapPass::renderUI(Gui::Widgets& widget) widget.var("Max Value", mMaxValue); } -ColorMapPass::AutoRanging::AutoRanging(std::shared_ptr pDevice) +ColorMapPass::AutoRanging::AutoRanging(ref pDevice) { mpParallelReduction = std::make_unique(pDevice); - mpReductionResult = Buffer::create(pDevice.get(), 32, ResourceBindFlags::None, Buffer::CpuAccess::Read); - mpFence = GpuFence::create(pDevice.get()); + mpReductionResult = Buffer::create(pDevice, 32, ResourceBindFlags::None, Buffer::CpuAccess::Read); + mpFence = GpuFence::create(pDevice); } -std::optional> ColorMapPass::AutoRanging::getMinMax(RenderContext* pRenderContext, const Texture::SharedPtr& texture, uint32_t channel) +std::optional> ColorMapPass::AutoRanging::getMinMax(RenderContext* pRenderContext, const ref& texture, uint32_t channel) { FALCOR_ASSERT(pRenderContext); FALCOR_ASSERT(texture); diff --git a/Source/RenderPasses/DebugPasses/ColorMapPass/ColorMapPass.h b/Source/RenderPasses/DebugPasses/ColorMapPass/ColorMapPass.h index bf153890b..f989ff8c1 100644 --- a/Source/RenderPasses/DebugPasses/ColorMapPass/ColorMapPass.h +++ b/Source/RenderPasses/DebugPasses/ColorMapPass/ColorMapPass.h @@ -27,8 +27,9 @@ **************************************************************************/ #pragma once #include "Falcor.h" +#include "Core/Pass/FullScreenPass.h" +#include "RenderGraph/RenderPass.h" #include "Utils/Algorithm/ParallelReduction.h" -#include "RenderGraph/BasePasses/FullScreenPass.h" #include "ColorMapParams.slang" using namespace Falcor; @@ -38,11 +39,9 @@ class ColorMapPass : public RenderPass public: FALCOR_PLUGIN_CLASS(ColorMapPass, "ColorMapPass", "Pass that applies a color map to the input."); - using SharedPtr = std::shared_ptr; + static ref create(ref pDevice, const Dictionary& dict) { return make_ref(pDevice, dict); } - /** Create a new object - */ - static SharedPtr create(std::shared_ptr pDevice, const Dictionary& dict); + ColorMapPass(ref pDevice, const Dictionary& dict); virtual Dictionary getScriptingDictionary() override; virtual RenderPassReflection reflect(const CompileData& compileData) override; @@ -52,29 +51,27 @@ class ColorMapPass : public RenderPass static void registerScriptBindings(pybind11::module& m); private: - ColorMapPass(std::shared_ptr pDevice, const Dictionary& dict); - ColorMap mColorMap = ColorMap::Jet; uint32_t mChannel = 0; bool mAutoRange = true; float mMinValue = 0.f; float mMaxValue = 1.f; - FullScreenPass::SharedPtr mpColorMapPass; - Fbo::SharedPtr mpFbo; + ref mpColorMapPass; + ref mpFbo; bool mRecompile = true; class AutoRanging { public: - AutoRanging(std::shared_ptr pDevice); + AutoRanging(ref pDevice); - std::optional> getMinMax(RenderContext* pRenderContext, const Texture::SharedPtr& texture, uint32_t channel); + std::optional> getMinMax(RenderContext* pRenderContext, const ref& texture, uint32_t channel); private: std::unique_ptr mpParallelReduction; - Buffer::SharedPtr mpReductionResult; - GpuFence::SharedPtr mpFence; + ref mpReductionResult; + ref mpFence; bool mReductionAvailable = false; }; diff --git a/Source/RenderPasses/DebugPasses/ComparisonPass.cpp b/Source/RenderPasses/DebugPasses/ComparisonPass.cpp index 066945749..638b69ddc 100644 --- a/Source/RenderPasses/DebugPasses/ComparisonPass.cpp +++ b/Source/RenderPasses/DebugPasses/ComparisonPass.cpp @@ -26,7 +26,6 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **************************************************************************/ #include "ComparisonPass.h" -#include "Utils/UI/TextRenderer.h" namespace { @@ -40,6 +39,12 @@ namespace const std::string kOutput = "output"; } +ComparisonPass::ComparisonPass(ref pDevice) + : RenderPass(pDevice) +{ + mpTextRenderer = std::make_unique(mpDevice); +} + bool ComparisonPass::parseKeyValuePair(const std::string key, const Dictionary::Value val) { if (key == kSplitLocation) @@ -91,16 +96,17 @@ void ComparisonPass::execute(RenderContext* pRenderContext, const RenderData& re // Get references to our input, output, and temporary accumulation texture pLeftSrcTex = renderData.getTexture(kLeftInput); pRightSrcTex = renderData.getTexture(kRightInput); - pDstFbo = Fbo::create(mpDevice.get(), { renderData.getTexture(kOutput) }); + pDstFbo = Fbo::create(mpDevice, { renderData.getTexture(kOutput) }); // 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["gLeftInput"] = mSwapSides ? pRightSrcTex : pLeftSrcTex; - mpSplitShader["gRightInput"] = mSwapSides ? pLeftSrcTex : pRightSrcTex; + auto var = mpSplitShader->getRootVar(); + var["GlobalCB"]["gSplitLocation"] = int32_t(mSplitLoc * renderData.getDefaultTextureDims().x); + var["GlobalCB"]["gDividerSize"] = mDividerSize; + var["gLeftInput"] = mSwapSides ? pRightSrcTex : pLeftSrcTex; + var["gRightInput"] = mSwapSides ? pLeftSrcTex : pRightSrcTex; // Execute the accumulation shader mpSplitShader->execute(pRenderContext, pDstFbo); @@ -113,12 +119,12 @@ void ComparisonPass::execute(RenderContext* pRenderContext, const RenderData& re // Draw text labeling the right side image std::string rightSide = mSwapSides ? mLeftLabel : mRightLabel; - TextRenderer::render(pRenderContext, rightSide, pDstFbo, float2(screenLocX + 16, screenLocY)); + mpTextRenderer->render(pRenderContext, rightSide, pDstFbo, float2(screenLocX + 16, screenLocY)); // Draw text labeling the left side image std::string leftSide = mSwapSides ? mRightLabel : mLeftLabel; uint32_t leftLength = uint32_t(leftSide.length()) * 9; - TextRenderer::render(pRenderContext, leftSide, pDstFbo, float2(screenLocX - 16 - leftLength, screenLocY)); + mpTextRenderer->render(pRenderContext, leftSide, pDstFbo, float2(screenLocX - 16 - leftLength, screenLocY)); } } diff --git a/Source/RenderPasses/DebugPasses/ComparisonPass.h b/Source/RenderPasses/DebugPasses/ComparisonPass.h index 3731e4c4b..389784f4b 100644 --- a/Source/RenderPasses/DebugPasses/ComparisonPass.h +++ b/Source/RenderPasses/DebugPasses/ComparisonPass.h @@ -27,29 +27,30 @@ **************************************************************************/ #pragma once #include "Falcor.h" -#include "RenderGraph/BasePasses/FullScreenPass.h" +#include "Core/Pass/FullScreenPass.h" +#include "RenderGraph/RenderPass.h" +#include "Utils/UI/TextRenderer.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* pRenderContext, const RenderData& renderData) override; virtual void renderUI(Gui::Widgets& widget) override; protected: - ComparisonPass(std::shared_ptr pDevice) : RenderPass(std::move(pDevice)) {} + ComparisonPass(ref pDevice); virtual void createProgram() = 0; bool parseKeyValuePair(const std::string key, const Dictionary::Value val); - FullScreenPass::SharedPtr mpSplitShader; - Texture::SharedPtr pLeftSrcTex; - Texture::SharedPtr pRightSrcTex; - Fbo::SharedPtr pDstFbo; + ref mpSplitShader; + ref pLeftSrcTex; + ref pRightSrcTex; + ref pDstFbo; + std::unique_ptr mpTextRenderer; // Screen parameters bool mSwapSides = false; // Is the left input on the left side diff --git a/Source/RenderPasses/DebugPasses/InvalidPixelDetectionPass/InvalidPixelDetectionPass.cpp b/Source/RenderPasses/DebugPasses/InvalidPixelDetectionPass/InvalidPixelDetectionPass.cpp index f4c8840b4..e44260482 100644 --- a/Source/RenderPasses/DebugPasses/InvalidPixelDetectionPass/InvalidPixelDetectionPass.cpp +++ b/Source/RenderPasses/DebugPasses/InvalidPixelDetectionPass/InvalidPixelDetectionPass.cpp @@ -34,17 +34,11 @@ namespace const std::string kFormatWarning = "Non-float format can't represent Inf/NaN values. Expect black output."; } -InvalidPixelDetectionPass::InvalidPixelDetectionPass(std::shared_ptr pDevice) - : RenderPass(std::move(pDevice)) +InvalidPixelDetectionPass::InvalidPixelDetectionPass(ref pDevice, const Dictionary& dict) + : RenderPass(pDevice) { mpInvalidPixelDetectPass = FullScreenPass::create(mpDevice, "RenderPasses/DebugPasses/InvalidPixelDetectionPass/InvalidPixelDetection.ps.slang"); - mpFbo = Fbo::create(mpDevice.get()); -} - -InvalidPixelDetectionPass::SharedPtr InvalidPixelDetectionPass::create(std::shared_ptr pDevice, const Dictionary& dict) -{ - SharedPtr pPass = SharedPtr(new InvalidPixelDetectionPass(std::move(pDevice))); - return pPass; + mpFbo = Fbo::create(mpDevice); } RenderPassReflection InvalidPixelDetectionPass::reflect(const CompileData& compileData) @@ -97,7 +91,7 @@ void InvalidPixelDetectionPass::execute(RenderContext* pRenderContext, const Ren } } - mpInvalidPixelDetectPass["gTexture"] = pSrc; + mpInvalidPixelDetectPass->getRootVar()["gTexture"] = pSrc; mpFbo->attachColorTarget(renderData.getTexture(kDst), 0); mpInvalidPixelDetectPass->getState()->setFbo(mpFbo); mpInvalidPixelDetectPass->execute(pRenderContext, mpFbo); diff --git a/Source/RenderPasses/DebugPasses/InvalidPixelDetectionPass/InvalidPixelDetectionPass.h b/Source/RenderPasses/DebugPasses/InvalidPixelDetectionPass/InvalidPixelDetectionPass.h index b69aaa984..735fa87b4 100644 --- a/Source/RenderPasses/DebugPasses/InvalidPixelDetectionPass/InvalidPixelDetectionPass.h +++ b/Source/RenderPasses/DebugPasses/InvalidPixelDetectionPass/InvalidPixelDetectionPass.h @@ -27,7 +27,8 @@ **************************************************************************/ #pragma once #include "Falcor.h" -#include "RenderGraph/BasePasses/FullScreenPass.h" +#include "RenderGraph/RenderPass.h" +#include "Core/Pass/FullScreenPass.h" using namespace Falcor; @@ -36,11 +37,9 @@ class InvalidPixelDetectionPass : public RenderPass public: FALCOR_PLUGIN_CLASS(InvalidPixelDetectionPass, "InvalidPixelDetectionPass", "Pass that marks all NaN pixels red and Inf pixels green in an image."); - using SharedPtr = std::shared_ptr; + static ref create(ref pDevice, const Dictionary& dict) { return make_ref(pDevice, dict); } - /** Create a new object - */ - static SharedPtr create(std::shared_ptr pDevice, const Dictionary& dict); + InvalidPixelDetectionPass(ref pDevice, const Dictionary& dict); virtual RenderPassReflection reflect(const CompileData& compileData) override; virtual void compile(RenderContext* pRenderContext, const CompileData& compileData) override; @@ -48,10 +47,8 @@ class InvalidPixelDetectionPass : public RenderPass virtual void renderUI(Gui::Widgets& widget) override; private: - InvalidPixelDetectionPass(std::shared_ptr pDevice); - - FullScreenPass::SharedPtr mpInvalidPixelDetectPass; - Fbo::SharedPtr mpFbo; + ref mpInvalidPixelDetectPass; + ref mpFbo; ResourceFormat mFormat = ResourceFormat::Unknown; bool mReady = false; }; diff --git a/Source/RenderPasses/DebugPasses/SideBySidePass/SideBySidePass.cpp b/Source/RenderPasses/DebugPasses/SideBySidePass/SideBySidePass.cpp index 776382296..a2c4db24f 100644 --- a/Source/RenderPasses/DebugPasses/SideBySidePass/SideBySidePass.cpp +++ b/Source/RenderPasses/DebugPasses/SideBySidePass/SideBySidePass.cpp @@ -35,24 +35,19 @@ namespace const std::string kSplitShader = "RenderPasses/DebugPasses/SideBySidePass/SideBySide.ps.slang"; } -SideBySidePass::SideBySidePass(std::shared_ptr pDevice) - : ComparisonPass(std::move(pDevice)) +SideBySidePass::SideBySidePass(ref pDevice, const Dictionary& dict) + : ComparisonPass(pDevice) { createProgram(); -} -SideBySidePass::SharedPtr SideBySidePass::create(std::shared_ptr pDevice, const Dictionary& dict) -{ - SharedPtr pPass = SharedPtr(new SideBySidePass(std::move(pDevice))); for (const auto& [key, value] : dict) { - if (key == kImageLeftBound) pPass->mImageLeftBound = value; - else if (!pPass->parseKeyValuePair(key, value)) + if (key == kImageLeftBound) mImageLeftBound = value; + else if (!parseKeyValuePair(key, value)) { logWarning("Unknown field '{}' in a SideBySidePass dictionary.", key); } } - return pPass; } void SideBySidePass::createProgram() @@ -63,7 +58,7 @@ void SideBySidePass::createProgram() void SideBySidePass::execute(RenderContext* pRenderContext, const RenderData& renderData) { - mpSplitShader["GlobalCB"]["gLeftBound"] = mImageLeftBound; + mpSplitShader->getRootVar()["GlobalCB"]["gLeftBound"] = mImageLeftBound; ComparisonPass::execute(pRenderContext, renderData); } diff --git a/Source/RenderPasses/DebugPasses/SideBySidePass/SideBySidePass.h b/Source/RenderPasses/DebugPasses/SideBySidePass/SideBySidePass.h index f1daebe5e..42ac6d400 100644 --- a/Source/RenderPasses/DebugPasses/SideBySidePass/SideBySidePass.h +++ b/Source/RenderPasses/DebugPasses/SideBySidePass/SideBySidePass.h @@ -36,14 +36,14 @@ class SideBySidePass : public ComparisonPass public: FALCOR_PLUGIN_CLASS(SideBySidePass, "SideBySidePass", "Allows the user to compare two inputs side-by-side."); - using SharedPtr = std::shared_ptr; + static ref create(ref pDevice, const Dictionary& dict) { return make_ref(pDevice, dict); } + + SideBySidePass(ref pDevice, const Dictionary& dict); - static SharedPtr create(std::shared_ptr pDevice, const Dictionary& dict); virtual void execute(RenderContext* pRenderContext, const RenderData& renderData) override; virtual void renderUI(Gui::Widgets& widget) override; private: - SideBySidePass(std::shared_ptr pDevice); 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/SplitScreenPass/SplitScreenPass.cpp b/Source/RenderPasses/DebugPasses/SplitScreenPass/SplitScreenPass.cpp index 424883236..d9e3dcb2b 100644 --- a/Source/RenderPasses/DebugPasses/SplitScreenPass/SplitScreenPass.cpp +++ b/Source/RenderPasses/DebugPasses/SplitScreenPass/SplitScreenPass.cpp @@ -57,24 +57,19 @@ namespace const std::string kSplitShader = "RenderPasses/DebugPasses/SplitScreenPass/SplitScreen.ps.slang"; } -SplitScreenPass::SplitScreenPass(std::shared_ptr pDevice) - : ComparisonPass(std::move(pDevice)) +SplitScreenPass::SplitScreenPass(ref pDevice, const Dictionary& dict) + : ComparisonPass(pDevice) { - mpArrowTex = Texture::create2D(mpDevice.get(), 16, 16, ResourceFormat::R8Unorm, 1, Texture::kMaxPossible, kArrowArray); + mpArrowTex = Texture::create2D(mpDevice, 16, 16, ResourceFormat::R8Unorm, 1, Texture::kMaxPossible, kArrowArray); createProgram(); -} -SplitScreenPass::SharedPtr SplitScreenPass::create(std::shared_ptr pDevice, const Dictionary& dict) -{ - SharedPtr pPass = SharedPtr(new SplitScreenPass(std::move(pDevice))); for (const auto& [key, value] : dict) { - if (!pPass->parseKeyValuePair(key, value)) + if (!parseKeyValuePair(key, value)) { logWarning("Unknown field '{}' in a SplitScreenPass dictionary.", key); } } - return pPass; } void SplitScreenPass::createProgram() @@ -85,10 +80,11 @@ void SplitScreenPass::createProgram() void SplitScreenPass::execute(RenderContext* pRenderContext, const RenderData& renderData) { - mpSplitShader["GlobalCB"]["gDividerColor"] = mMouseOverDivider ? kColorSelected : kColorUnselected; - mpSplitShader["GlobalCB"]["gMousePosition"] = mMousePos; - mpSplitShader["GlobalCB"]["gDrawArrows"] = mDrawArrows && mMouseOverDivider; - mpSplitShader["gArrowTex"] = mpArrowTex; + auto var = mpSplitShader->getRootVar(); + var["GlobalCB"]["gDividerColor"] = mMouseOverDivider ? kColorSelected : kColorUnselected; + var["GlobalCB"]["gMousePosition"] = mMousePos; + var["GlobalCB"]["gDrawArrows"] = mDrawArrows && mMouseOverDivider; + var["gArrowTex"] = mpArrowTex; ComparisonPass::execute(pRenderContext, renderData); } @@ -102,7 +98,7 @@ bool SplitScreenPass::onMouseEvent(const MouseEvent& mouseEvent) mMousePos = int2(mouseEvent.screenPos.x, mouseEvent.screenPos.y); // If we're outside the window, stop. - mMousePos = glm::clamp(mMousePos, int2(0, 0), int2(pDstFbo->getWidth() - 1, pDstFbo->getHeight() - 1)); + mMousePos = clamp(mMousePos, int2(0, 0), int2(pDstFbo->getWidth() - 1, pDstFbo->getHeight() - 1)); // Actually process our events if (mMouseOverDivider && mouseEvent.type == MouseEvent::Type::ButtonDown && mouseEvent.button == Input::MouseButton::Left) @@ -129,7 +125,7 @@ bool SplitScreenPass::onMouseEvent(const MouseEvent& mouseEvent) // 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))); + mMouseOverDivider = (std::abs(int32_t(mSplitLoc * pDstFbo->getWidth()) - mMousePos.x) < std::max(6, int32_t(mDividerSize))); return handled; } diff --git a/Source/RenderPasses/DebugPasses/SplitScreenPass/SplitScreenPass.h b/Source/RenderPasses/DebugPasses/SplitScreenPass/SplitScreenPass.h index 23e43bb59..cfe0ff6b9 100644 --- a/Source/RenderPasses/DebugPasses/SplitScreenPass/SplitScreenPass.h +++ b/Source/RenderPasses/DebugPasses/SplitScreenPass/SplitScreenPass.h @@ -37,17 +37,17 @@ class SplitScreenPass : public ComparisonPass public: FALCOR_PLUGIN_CLASS(SplitScreenPass, "SplitScreenPass", "Allows the user to split the screen between two inputs."); - using SharedPtr = std::shared_ptr; + static ref create(ref pDevice, const Dictionary& dict) { return make_ref(pDevice, dict); } + + SplitScreenPass(ref pDevice, const Dictionary& dict); - static SharedPtr create(std::shared_ptr pDevice, const Dictionary& dict); virtual void execute(RenderContext* pRenderContext, const RenderData& renderData) override; virtual bool onMouseEvent(const MouseEvent& mouseEvent) override; virtual void renderUI(Gui::Widgets& widget) override; private: - SplitScreenPass(std::shared_ptr pDevice); virtual void createProgram() override; - Texture::SharedPtr mpArrowTex; // A texture storing a 16x16 grayscale arrow + ref mpArrowTex; // A texture storing a 16x16 grayscale arrow // Mouse parameters bool mMouseOverDivider = false; ///< Is the mouse over the divider? diff --git a/Source/RenderPasses/ErrorMeasurePass/ErrorMeasurePass.cpp b/Source/RenderPasses/ErrorMeasurePass/ErrorMeasurePass.cpp index f2d75e063..fa74bd325 100644 --- a/Source/RenderPasses/ErrorMeasurePass/ErrorMeasurePass.cpp +++ b/Source/RenderPasses/ErrorMeasurePass/ErrorMeasurePass.cpp @@ -79,13 +79,8 @@ const Gui::RadioButtonGroup ErrorMeasurePass::sOutputSelectionButtonsSourceOnly { (uint32_t)OutputId::Source, "Source", true } }; -ErrorMeasurePass::SharedPtr ErrorMeasurePass::create(std::shared_ptr pDevice, const Dictionary& dict) -{ - return SharedPtr(new ErrorMeasurePass(std::move(pDevice), dict)); -} - -ErrorMeasurePass::ErrorMeasurePass(std::shared_ptr pDevice, const Dictionary& dict) - : RenderPass(std::move(pDevice)) +ErrorMeasurePass::ErrorMeasurePass(ref pDevice, const Dictionary& dict) + : RenderPass(pDevice) { for (const auto& [key, value] : dict) { @@ -140,8 +135,8 @@ RenderPassReflection ErrorMeasurePass::reflect(const CompileData& compileData) void ErrorMeasurePass::execute(RenderContext* pRenderContext, const RenderData& renderData) { - Texture::SharedPtr pSourceImageTexture = renderData.getTexture(kInputChannelSourceImage); - Texture::SharedPtr pOutputImageTexture = renderData.getTexture(kOutputChannelImage); + ref pSourceImageTexture = renderData.getTexture(kInputChannelSourceImage); + ref pOutputImageTexture = renderData.getTexture(kOutputChannelImage); // Create the texture for the difference image if this is our first // time through or if the source image resolution has changed. @@ -149,14 +144,14 @@ void ErrorMeasurePass::execute(RenderContext* pRenderContext, const RenderData& if (!mpDifferenceTexture || mpDifferenceTexture->getWidth() != width || mpDifferenceTexture->getHeight() != height) { - mpDifferenceTexture = Texture::create2D(mpDevice.get(), width, height, ResourceFormat::RGBA32Float, 1, 1, nullptr, + mpDifferenceTexture = Texture::create2D(mpDevice, width, height, ResourceFormat::RGBA32Float, 1, 1, nullptr, Resource::BindFlags::ShaderResource | Resource::BindFlags::UnorderedAccess); FALCOR_ASSERT(mpDifferenceTexture); } mMeasurements.valid = false; - Texture::SharedPtr pReference = getReference(renderData); + ref pReference = getReference(renderData); if (!pReference) { // We don't have a reference image, so just copy the source image to the output. @@ -188,20 +183,21 @@ void ErrorMeasurePass::execute(RenderContext* pRenderContext, const RenderData& void ErrorMeasurePass::runDifferencePass(RenderContext* pRenderContext, const RenderData& renderData) { // Bind textures. - Texture::SharedPtr pSourceTexture = renderData.getTexture(kInputChannelSourceImage); - Texture::SharedPtr pWorldPositionTexture = renderData.getTexture(kInputChannelWorldPosition); - mpErrorMeasurerPass["gReference"] = getReference(renderData); - mpErrorMeasurerPass["gSource"] = pSourceTexture; - mpErrorMeasurerPass["gWorldPosition"] = pWorldPositionTexture; - mpErrorMeasurerPass["gResult"] = mpDifferenceTexture; + ref pSourceTexture = renderData.getTexture(kInputChannelSourceImage); + ref pWorldPositionTexture = renderData.getTexture(kInputChannelWorldPosition); + auto var = mpErrorMeasurerPass->getRootVar(); + var["gReference"] = getReference(renderData); + var["gSource"] = pSourceTexture; + var["gWorldPosition"] = pWorldPositionTexture; + var["gResult"] = mpDifferenceTexture; // Set constant buffer parameters. const uint2 resolution = uint2(pSourceTexture->getWidth(), pSourceTexture->getHeight()); - mpErrorMeasurerPass[kConstantBufferName]["gResolution"] = resolution; + var[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; - mpErrorMeasurerPass[kConstantBufferName]["gComputeAverage"] = (uint32_t)mComputeAverage; + var[kConstantBufferName]["gIgnoreBackground"] = (uint32_t)(mIgnoreBackground && pWorldPositionTexture); + var[kConstantBufferName]["gComputeDiffSqr"] = (uint32_t)mComputeSquaredDifference; + var[kConstantBufferName]["gComputeAverage"] = (uint32_t)mComputeAverage; // Run the compute shader. mpErrorMeasurerPass->execute(pRenderContext, resolution.x, resolution.y); @@ -213,7 +209,7 @@ void ErrorMeasurePass::runReductionPasses(RenderContext* pRenderContext, const R mpParallelReduction->execute(pRenderContext, mpDifferenceTexture, ParallelReduction::Type::Sum, &error); const float pixelCountf = static_cast(mpDifferenceTexture->getWidth() * mpDifferenceTexture->getHeight()); - mMeasurements.error = error / pixelCountf; + mMeasurements.error = error.xyz() / pixelCountf; mMeasurements.avgError = (mMeasurements.error.x + mMeasurements.error.y + mMeasurements.error.z) / 3.f; mMeasurements.valid = true; @@ -350,7 +346,7 @@ 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(mpDevice.get(), mReferenceImagePath, false /* no MIPs */, false /* linear color */); + mpReferenceTexture = Texture::createFromFile(mpDevice, mReferenceImagePath, false /* no MIPs */, false /* linear color */); if (!mpReferenceTexture) { reportError(fmt::format("Failed to load texture from '{}'", mReferenceImagePath)); @@ -361,7 +357,7 @@ void ErrorMeasurePass::loadReference() mRunningAvgError = -1.f; // Mark running error values as invalid. } -Texture::SharedPtr ErrorMeasurePass::getReference(const RenderData& renderData) const +ref ErrorMeasurePass::getReference(const RenderData& renderData) const { return mUseLoadedReference ? mpReferenceTexture : renderData.getTexture(kInputChannelReferenceImage); } diff --git a/Source/RenderPasses/ErrorMeasurePass/ErrorMeasurePass.h b/Source/RenderPasses/ErrorMeasurePass/ErrorMeasurePass.h index 86374e72a..bd5cc1c20 100644 --- a/Source/RenderPasses/ErrorMeasurePass/ErrorMeasurePass.h +++ b/Source/RenderPasses/ErrorMeasurePass/ErrorMeasurePass.h @@ -27,6 +27,7 @@ **************************************************************************/ #pragma once #include "Falcor.h" +#include "RenderGraph/RenderPass.h" #include "Utils/Algorithm/ParallelReduction.h" #include @@ -37,8 +38,6 @@ class ErrorMeasurePass : public RenderPass public: FALCOR_PLUGIN_CLASS(ErrorMeasurePass, "ErrorMeasurePass", "Measures error with respect to a reference image."); - using SharedPtr = std::shared_ptr; - enum class OutputId { Source, @@ -47,7 +46,9 @@ class ErrorMeasurePass : public RenderPass Count }; - static SharedPtr create(std::shared_ptr pDevice, const Dictionary& dict); + static ref create(ref pDevice, const Dictionary& dict) { return make_ref(pDevice, dict); } + + ErrorMeasurePass(ref pDevice, const Dictionary& dict); virtual Dictionary getScriptingDictionary() override; virtual RenderPassReflection reflect(const CompileData& compileData) override; @@ -56,19 +57,17 @@ class ErrorMeasurePass : public RenderPass virtual bool onKeyEvent(const KeyboardEvent& keyEvent) override; private: - ErrorMeasurePass(std::shared_ptr pDevice, const Dictionary& dict); - bool init(RenderContext* pRenderContext, const Dictionary& dict); void loadReference(); - Texture::SharedPtr getReference(const RenderData& renderData) const; + ref 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; + ref mpErrorMeasurerPass; std::unique_ptr mpParallelReduction; struct @@ -82,8 +81,8 @@ class ErrorMeasurePass : public RenderPass 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; + ref mpReferenceTexture; + ref mpDifferenceTexture; std::ofstream mMeasurementsFile; diff --git a/Source/RenderPasses/FLIPPass/FLIPPass.cpp b/Source/RenderPasses/FLIPPass/FLIPPass.cpp index 017ae1052..305125951 100644 --- a/Source/RenderPasses/FLIPPass/FLIPPass.cpp +++ b/Source/RenderPasses/FLIPPass/FLIPPass.cpp @@ -76,13 +76,8 @@ void FLIPPass::registerBindings(pybind11::module& m) toneMapper.value("Reinhard", FLIPToneMapperType::Reinhard); } -FLIPPass::SharedPtr FLIPPass::create(std::shared_ptr pDevice, const Dictionary& dict) -{ - return SharedPtr(new FLIPPass(std::move(pDevice), dict)); -} - -FLIPPass::FLIPPass(std::shared_ptr pDevice, const Dictionary& dict) - : RenderPass(std::move(pDevice)) +FLIPPass::FLIPPass(ref pDevice, const Dictionary& dict) + : RenderPass(pDevice) { parseDictionary(dict); @@ -276,43 +271,47 @@ void FLIPPass::execute(RenderContext* pRenderContext, const RenderData& renderDa uint2 outputResolution = uint2(pReferenceImageInput->getWidth(), pReferenceImageInput->getHeight()); if (!mpFLIPErrorMapDisplay || !mpExposureMapDisplay || mpFLIPErrorMapDisplay->getWidth() != outputResolution.x || mpFLIPErrorMapDisplay->getHeight() != outputResolution.y) { - mpFLIPErrorMapDisplay = Texture::create2D(mpDevice.get(), outputResolution.x, outputResolution.y, ResourceFormat::RGBA32Float, 1, 1, nullptr, Resource::BindFlags::UnorderedAccess | Resource::BindFlags::ShaderResource); - mpExposureMapDisplay = Texture::create2D(mpDevice.get(), outputResolution.x, outputResolution.y, ResourceFormat::RGBA32Float, 1, 1, nullptr, Resource::BindFlags::UnorderedAccess | Resource::BindFlags::ShaderResource); + mpFLIPErrorMapDisplay = Texture::create2D(mpDevice, outputResolution.x, outputResolution.y, ResourceFormat::RGBA32Float, 1, 1, nullptr, Resource::BindFlags::UnorderedAccess | Resource::BindFlags::ShaderResource); + mpExposureMapDisplay = Texture::create2D(mpDevice, outputResolution.x, outputResolution.y, ResourceFormat::RGBA32Float, 1, 1, nullptr, Resource::BindFlags::UnorderedAccess | Resource::BindFlags::ShaderResource); } if (!mpLuminance) { - mpLuminance = Buffer::create(mpDevice.get(), outputResolution.x * outputResolution.y * sizeof(float), ResourceBindFlags::UnorderedAccess, Buffer::CpuAccess::None); + mpLuminance = Buffer::create(mpDevice, outputResolution.x * outputResolution.y * sizeof(float), ResourceBindFlags::UnorderedAccess, Buffer::CpuAccess::None); } - // Bind resources. - mpFLIPPass["gTestImage"] = pTestImageInput; - mpFLIPPass["gReferenceImage"] = pReferenceImageInput; - mpFLIPPass["gFLIPErrorMap"] = pErrorMapOutput; - mpFLIPPass["gFLIPErrorMapDisplay"] = mpFLIPErrorMapDisplay; - mpFLIPPass["gExposureMapDisplay"] = mpExposureMapDisplay; - - // Bind CB settings. - auto var = mpFLIPPass["PerFrameCB"]; - var["gIsHDR"] = mIsHDR; - var["gUseMagma"] = mUseMagma; - var["gClampInput"] = mUseMagma; - var["gResolution"] = outputResolution; - var["gMonitorWidthPixels"] = mMonitorWidthPixels; - var["gMonitorWidthMeters"] = mMonitorWidthMeters; - var["gMonitorDistance"] = mMonitorDistanceMeters; - var["gStartExposure"] = mStartExposure; - var["gExposureDelta"] = mExposureDelta; - var["gNumExposures"] = mNumExposures; + { + // Bind resources. + auto rootVar = mpFLIPPass->getRootVar(); + rootVar["gTestImage"] = pTestImageInput; + rootVar["gReferenceImage"] = pReferenceImageInput; + rootVar["gFLIPErrorMap"] = pErrorMapOutput; + rootVar["gFLIPErrorMapDisplay"] = mpFLIPErrorMapDisplay; + rootVar["gExposureMapDisplay"] = mpExposureMapDisplay; + + // Bind CB settings. + auto var = rootVar["PerFrameCB"]; + var["gIsHDR"] = mIsHDR; + var["gUseMagma"] = mUseMagma; + var["gClampInput"] = mUseMagma; + var["gResolution"] = outputResolution; + var["gMonitorWidthPixels"] = mMonitorWidthPixels; + var["gMonitorWidthMeters"] = mMonitorWidthMeters; + var["gMonitorDistance"] = mMonitorDistanceMeters; + var["gStartExposure"] = mStartExposure; + var["gExposureDelta"] = mExposureDelta; + var["gNumExposures"] = mNumExposures; + } // Do we need to compute parameters for HDR-FLIP? if (!mUseCustomExposureParameters && mIsHDR) { // Bind resources. - mpComputeLuminancePass["gInputImage"] = pReferenceImageInput; - mpComputeLuminancePass["gOutputLuminance"] = mpLuminance; + auto rootVar = mpComputeLuminancePass->getRootVar(); + rootVar["gInputImage"] = pReferenceImageInput; + rootVar["gOutputLuminance"] = mpLuminance; // Bind CB settings. - mpComputeLuminancePass["PerFrameCB"]["gResolution"] = outputResolution; + rootVar["PerFrameCB"]["gResolution"] = outputResolution; // Compute luminance of the reference image. mpComputeLuminancePass->execute(pRenderContext, uint3(outputResolution.x, outputResolution.y, 1u)); pRenderContext->flush(true); diff --git a/Source/RenderPasses/FLIPPass/FLIPPass.h b/Source/RenderPasses/FLIPPass/FLIPPass.h index 645bd7399..f448c4be7 100644 --- a/Source/RenderPasses/FLIPPass/FLIPPass.h +++ b/Source/RenderPasses/FLIPPass/FLIPPass.h @@ -27,6 +27,7 @@ **************************************************************************/ #pragma once #include "Falcor.h" +#include "RenderGraph/RenderPass.h" #include "Core/Platform/MonitorInfo.h" #include "Utils/Algorithm/ParallelReduction.h" #include "ToneMappers.slang" @@ -45,14 +46,9 @@ class FLIPPass : public RenderPass "to the errorMapDisplay. The transform is only added before display, however, and will NOT affect the output when it is saved to disk." }); - using SharedPtr = std::shared_ptr; + static ref create(ref pDevice, const Dictionary& dict) { return make_ref(pDevice, dict); } - /** Create a new render pass object. - \param[in] pDevice GPU device. - \param[in] dict Dictionary of serialized parameters. - \return A new object, or an exception is thrown if creation failed. - */ - static SharedPtr create(std::shared_ptr pDevice, const Dictionary& dict); + FLIPPass(ref pDevice, const Dictionary& dict); virtual Dictionary getScriptingDictionary() override; virtual RenderPassReflection reflect(const CompileData& compileData) override; @@ -67,8 +63,6 @@ class FLIPPass : public RenderPass void parseDictionary(const Dictionary& dict); private: - FLIPPass(std::shared_ptr pDevice, const Dictionary& dict); - bool mEnabled = true; ///< Enables FLIP calculation. bool mUseMagma = true; ///< Enable to map FLIP result to magma colormap. @@ -85,12 +79,12 @@ class FLIPPass : public RenderPass float mExposureDelta = 0.0f; ///< Exposure delta used for HDR-FLIP (startExposure + (numExposures - 1) * exposureDelta = stopExposure). uint32_t mNumExposures = 2; ///< Number of exposures used for HDR-FLIP. - Texture::SharedPtr mpFLIPErrorMapDisplay; ///< Internal buffer for temporary display output buffer. - Texture::SharedPtr mpExposureMapDisplay; ///< Internal buffer for the HDR-FLIP exposure map. - Buffer::SharedPtr mpLuminance; ///< Internal buffer for temporary luminance. - ComputePass::SharedPtr mpFLIPPass; ///< Compute pass to calculate FLIP. - ComputePass::SharedPtr mpComputeLuminancePass; ///< Compute pass for computing the luminance of an image. - std::unique_ptr mpParallelReduction; ///< Helper for parallel reduction on the GPU. + ref mpFLIPErrorMapDisplay; ///< Internal buffer for temporary display output buffer. + ref mpExposureMapDisplay; ///< Internal buffer for the HDR-FLIP exposure map. + ref mpLuminance; ///< Internal buffer for temporary luminance. + ref mpFLIPPass; ///< Compute pass to calculate FLIP. + ref mpComputeLuminancePass; ///< Compute pass for computing the luminance of an image. + std::unique_ptr mpParallelReduction; ///< Helper for parallel reduction on the GPU. bool mComputePooledFLIPValues = false; ///< Enable to use parallel reduction to compute FLIP mean/min/max across whole frame. float mAverageFLIP; ///< Average FLIP value across whole frame. diff --git a/Source/RenderPasses/FLIPPass/ToneMappers.slang b/Source/RenderPasses/FLIPPass/ToneMappers.slang index 15f9f6dbc..b686884b6 100644 --- a/Source/RenderPasses/FLIPPass/ToneMappers.slang +++ b/Source/RenderPasses/FLIPPass/ToneMappers.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -100,7 +100,7 @@ float3 toneMap(float3 col, FLIPToneMapperType toneMapper = FLIPToneMapperType::A float3 nom = k0 * colSq + k1 * col + k2; float3 denom = k3 * colSq + k4 * col + k5; - denom = isinf(denom) ? 1.0f : denom; // avoid inf / inf division + denom = select(isinf(denom), 1.0f, denom); // avoid inf / inf division float3 toneMappedCol = nom / denom; diff --git a/Source/RenderPasses/GBuffer/GBuffer/GBuffer.cpp b/Source/RenderPasses/GBuffer/GBuffer/GBuffer.cpp index 89696447f..c065a789d 100644 --- a/Source/RenderPasses/GBuffer/GBuffer/GBuffer.cpp +++ b/Source/RenderPasses/GBuffer/GBuffer/GBuffer.cpp @@ -48,8 +48,8 @@ const ChannelList GBuffer::kGBufferChannels = { "mtlData", "gMaterialData", "Material data (ID, header)", true /* optional */, ResourceFormat::RGBA32Uint }, }; -GBuffer::GBuffer(std::shared_ptr pDevice) - : GBufferBase(std::move(pDevice)) +GBuffer::GBuffer(ref pDevice) + : GBufferBase(pDevice) { FALCOR_ASSERT(kGBufferChannels.size() == 8); // The list of primary GBuffer channels should contain 8 entries, corresponding to the 8 render targets. } diff --git a/Source/RenderPasses/GBuffer/GBuffer/GBuffer.h b/Source/RenderPasses/GBuffer/GBuffer/GBuffer.h index 6859c44a3..b9a9b26c4 100644 --- a/Source/RenderPasses/GBuffer/GBuffer/GBuffer.h +++ b/Source/RenderPasses/GBuffer/GBuffer/GBuffer.h @@ -36,7 +36,7 @@ using namespace Falcor; class GBuffer : public GBufferBase { protected: - GBuffer(std::shared_ptr pDevice); + GBuffer(ref pDevice); // Constants used in derived classes static const ChannelList kGBufferChannels; diff --git a/Source/RenderPasses/GBuffer/GBuffer/GBufferHelpers.slang b/Source/RenderPasses/GBuffer/GBuffer/GBufferHelpers.slang index f510c9825..0e7f2b725 100644 --- a/Source/RenderPasses/GBuffer/GBuffer/GBufferHelpers.slang +++ b/Source/RenderPasses/GBuffer/GBuffer/GBufferHelpers.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -60,10 +60,10 @@ GBufferData prepareGBufferData(const ShadingData sd, const VertexData v, const I // We store the final normal in the G-buffer but pass along the original geometric tangent and its handedness (sign) unmodified. // This is needed for correctly orthonormalizing the tangent frame and computing the bitangent in passes that consume the G-buffer data. - float bitangentSign = sd.getHandednessSign(); + float bitangentSign = sd.frame.getHandednessSign(); gbuf.posW = float4(sd.posW, 1.f); - gbuf.normW = float4(sd.N, 0.f); + gbuf.normW = float4(sd.frame.N, 0.f); gbuf.tangentW = v.tangentW; gbuf.faceNormalW = float4(sd.faceN, 0.f); gbuf.guideNormalW = float4(bsdfProperties.guideNormal, 0.f); diff --git a/Source/RenderPasses/GBuffer/GBuffer/GBufferRT.cpp b/Source/RenderPasses/GBuffer/GBuffer/GBufferRT.cpp index c3827a3af..fd658730b 100644 --- a/Source/RenderPasses/GBuffer/GBuffer/GBufferRT.cpp +++ b/Source/RenderPasses/GBuffer/GBuffer/GBufferRT.cpp @@ -72,9 +72,18 @@ namespace }; }; -GBufferRT::SharedPtr GBufferRT::create(std::shared_ptr pDevice, const Dictionary& dict) +GBufferRT::GBufferRT(ref pDevice, const Dictionary& dict) + : GBuffer(pDevice) { - return SharedPtr(new GBufferRT(std::move(pDevice), dict)); + if (!mpDevice->isFeatureSupported(Device::SupportedFeatures::RaytracingTier1_1)) + { + throw RuntimeError("GBufferRT: Raytracing Tier 1.1 is not supported by the current device"); + } + + parseDictionary(dict); + + // Create random engine + mpSampleGenerator = SampleGenerator::create(mpDevice, SAMPLE_GENERATOR_DEFAULT); } RenderPassReflection GBufferRT::reflect(const CompileData& compileData) @@ -96,7 +105,7 @@ void GBufferRT::execute(RenderContext* pRenderContext, const RenderData& renderD // Update frame dimension based on render pass output. // In this pass all outputs are optional, so we must first find one that exists. - Texture::SharedPtr pOutput; + ref pOutput; auto findOutput = [&](const std::string& name) { auto pTex = renderData.getTexture(name); if (pTex && !pOutput) pOutput = pTex; @@ -179,13 +188,26 @@ Dictionary GBufferRT::getScriptingDictionary() return dict; } -void GBufferRT::setScene(RenderContext* pRenderContext, const Scene::SharedPtr& pScene) +void GBufferRT::setScene(RenderContext* pRenderContext, const ref& pScene) { GBuffer::setScene(pRenderContext, pScene); recreatePrograms(); } +void GBufferRT::parseDictionary(const Dictionary& dict) +{ + GBuffer::parseDictionary(dict); + + for (const auto& [key, value] : dict) + { + if (key == kLODMode) mLODMode = value; + else if (key == kUseTraceRayInline) mUseTraceRayInline = value; + else if (key == kUseDOF) mUseDOF = value; + // TODO: Check for unparsed fields, including those parsed in base classes. + } +} + void GBufferRT::recreatePrograms() { mRaytrace.pProgram = nullptr; @@ -211,7 +233,7 @@ void GBufferRT::executeRaytrace(RenderContext* pRenderContext, const RenderData& desc.setMaxAttributeSize(mpScene->getRaytracingMaxAttributeSize()); desc.setMaxTraceRecursionDepth(kMaxRecursionDepth); - RtBindingTable::SharedPtr sbt = RtBindingTable::create(1, 1, mpScene->getGeometryCount()); + ref sbt = RtBindingTable::create(1, 1, mpScene->getGeometryCount()); sbt->setRayGen(desc.addRayGen("rayGen")); sbt->setMiss(0, desc.addMiss("miss")); sbt->setHitGroup(0, mpScene->getGeometryIDs(Scene::GeometryType::TriangleMesh), desc.addHitGroup("closestHit", "anyHit")); @@ -316,36 +338,9 @@ void GBufferRT::setShaderData(const ShaderVar& var, const RenderData& renderData // Bind output channels as UAV buffers. auto bind = [&](const ChannelDesc& channel) { - Texture::SharedPtr pTex = getOutput(renderData, channel.name); + ref pTex = getOutput(renderData, channel.name); var[channel.texname] = pTex; }; for (const auto& channel : kGBufferChannels) bind(channel); for (const auto& channel : kGBufferExtraChannels) bind(channel); } - -GBufferRT::GBufferRT(std::shared_ptr pDevice, const Dictionary& dict) - : GBuffer(std::move(pDevice)) -{ - if (!mpDevice->isFeatureSupported(Device::SupportedFeatures::RaytracingTier1_1)) - { - throw RuntimeError("GBufferRT: Raytracing Tier 1.1 is not supported by the current device"); - } - - parseDictionary(dict); - - // Create random engine - mpSampleGenerator = SampleGenerator::create(mpDevice, SAMPLE_GENERATOR_DEFAULT); -} - -void GBufferRT::parseDictionary(const Dictionary& dict) -{ - GBuffer::parseDictionary(dict); - - for (const auto& [key, value] : dict) - { - if (key == kLODMode) mLODMode = value; - else if (key == kUseTraceRayInline) mUseTraceRayInline = value; - else if (key == kUseDOF) mUseDOF = value; - // TODO: Check for unparsed fields, including those parsed in base classes. - } -} diff --git a/Source/RenderPasses/GBuffer/GBuffer/GBufferRT.h b/Source/RenderPasses/GBuffer/GBuffer/GBufferRT.h index 1854c32ab..7c987e892 100644 --- a/Source/RenderPasses/GBuffer/GBuffer/GBufferRT.h +++ b/Source/RenderPasses/GBuffer/GBuffer/GBufferRT.h @@ -40,17 +40,19 @@ class GBufferRT : public GBuffer public: FALCOR_PLUGIN_CLASS(GBufferRT, "GBufferRT", "Ray traced G-buffer generation pass."); - using SharedPtr = std::shared_ptr; + static ref create(ref pDevice, const Dictionary& dict) { return make_ref(pDevice, dict); } - static SharedPtr create(std::shared_ptr pDevice, const Dictionary& dict); + GBufferRT(ref pDevice, 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; + void setScene(RenderContext* pRenderContext, const ref& pScene) override; private: + void parseDictionary(const Dictionary& dict) override; + void executeRaytrace(RenderContext* pRenderContext, const RenderData& renderData); void executeCompute(RenderContext* pRenderContext, const RenderData& renderData); @@ -58,12 +60,9 @@ class GBufferRT : public GBuffer void setShaderData(const ShaderVar& var, const RenderData& renderData); void recreatePrograms(); - GBufferRT(std::shared_ptr pDevice, const Dictionary& dict); - void parseDictionary(const Dictionary& dict) override; - // Internal state bool mComputeDOF = false; ///< Flag indicating if depth-of-field is computed for the current frame. - SampleGenerator::SharedPtr mpSampleGenerator; + ref mpSampleGenerator; // UI variables TexLODMode mLODMode = TexLODMode::Mip0; @@ -73,9 +72,9 @@ class GBufferRT : public GBuffer // Ray tracing resources struct { - RtProgram::SharedPtr pProgram; - RtProgramVars::SharedPtr pVars; + ref pProgram; + ref pVars; } mRaytrace; - ComputePass::SharedPtr mpComputePass; + ref mpComputePass; }; diff --git a/Source/RenderPasses/GBuffer/GBuffer/GBufferRT.slang b/Source/RenderPasses/GBuffer/GBuffer/GBufferRT.slang index 3de8fd921..d0e431a2b 100644 --- a/Source/RenderPasses/GBuffer/GBuffer/GBufferRT.slang +++ b/Source/RenderPasses/GBuffer/GBuffer/GBufferRT.slang @@ -233,11 +233,13 @@ struct GBufferRT } // Prepare shading data. - ShadingData sd = gScene.materials.prepareShadingData(v, materialID, -rayDir, lod, evalModifyNormal(hit.getType())); + ShadingData sd = gScene.materials.prepareShadingData(v, materialID, -rayDir, lod); + bool modifyNormal = evalModifyNormal(hit.getType()); + uint hints = !modifyNormal ? (uint)MaterialInstanceHints::DisableNormalMapping : 0; if (kAdjustShadingNormals && (hit.getType() == HitType::Triangle || hit.getType() == HitType::DisplacedTriangle)) { - adjustShadingNormal(sd); + hints |= (uint)MaterialInstanceHints::AdjustShadingNormal; } if (hit.getType() != HitType::None && hit.getType() != HitType::Volume) @@ -256,7 +258,7 @@ struct GBufferRT } // Create material instance and query its properties. - let mi = gScene.materials.getMaterialInstance(sd, lod); + let mi = gScene.materials.getMaterialInstance(sd, lod, hints); let bsdfProperties = mi.getProperties(sd); const GBufferData gbuf = prepareGBufferData(sd, v, mi, bsdfProperties); @@ -307,7 +309,7 @@ struct GBufferRT if (is_valid(gNormalWRoughnessMaterialID)) { float3 normal = bsdfProperties.guideNormal; - if (sd.mtl.getMaterialType() == MaterialType::Hair) normal = sd.T; + if (sd.mtl.getMaterialType() == MaterialType::Hair) normal = sd.frame.T; float2 octNormal = ndir_to_oct_unorm(normal); float roughness = bsdfProperties.roughness; diff --git a/Source/RenderPasses/GBuffer/GBuffer/GBufferRaster.3d.slang b/Source/RenderPasses/GBuffer/GBuffer/GBufferRaster.3d.slang index 0e6f03cfb..c40d01863 100644 --- a/Source/RenderPasses/GBuffer/GBuffer/GBufferRaster.3d.slang +++ b/Source/RenderPasses/GBuffer/GBuffer/GBufferRaster.3d.slang @@ -91,12 +91,13 @@ GBufferPSOut psMain(VSOut vsOut, uint triangleIndex : SV_PrimitiveID, float3 bar const float3 viewDir = normalize(gScene.camera.getPosition() - v.posW); ShadingData sd = gScene.materials.prepareShadingData(v, vsOut.materialID, viewDir, lod); + uint hints = 0; #if ADJUST_SHADING_NORMALS - adjustShadingNormal(sd); + hints |= (uint)MaterialInstanceHints::AdjustShadingNormal; #endif // Create material instance and query its properties. - let mi = gScene.materials.getMaterialInstance(sd, lod); + let mi = gScene.materials.getMaterialInstance(sd, lod, hints); let bsdfProperties = mi.getProperties(sd); const GBufferData gbuf = prepareGBufferData(sd, v, mi, bsdfProperties); diff --git a/Source/RenderPasses/GBuffer/GBuffer/GBufferRaster.cpp b/Source/RenderPasses/GBuffer/GBuffer/GBufferRaster.cpp index dfb9af83c..8ea569741 100644 --- a/Source/RenderPasses/GBuffer/GBuffer/GBufferRaster.cpp +++ b/Source/RenderPasses/GBuffer/GBuffer/GBufferRaster.cpp @@ -55,30 +55,8 @@ namespace const std::string kDepthName = "depth"; } -RenderPassReflection GBufferRaster::reflect(const CompileData& compileData) -{ - RenderPassReflection reflector; - const uint2 sz = RenderPassHelpers::calculateIOSize(mOutputSizeSelection, mFixedOutputSize, compileData.defaultTexDims); - - // Add the required depth output. This always exists. - reflector.addOutput(kDepthName, "Depth buffer").format(ResourceFormat::D32Float).bindFlags(Resource::BindFlags::DepthStencil).texture2D(sz.x, sz.y); - - // 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. - addRenderPassOutputs(reflector, kGBufferChannels, Resource::BindFlags::RenderTarget, sz); - addRenderPassOutputs(reflector, kGBufferExtraChannels, Resource::BindFlags::UnorderedAccess, sz); - reflector.getField(kVBufferName)->format(mVBufferFormat); - - return reflector; -} - -GBufferRaster::SharedPtr GBufferRaster::create(std::shared_ptr pDevice, const Dictionary& dict) -{ - return SharedPtr(new GBufferRaster(std::move(pDevice), dict)); -} - -GBufferRaster::GBufferRaster(std::shared_ptr pDevice, const Dictionary& dict) - : GBuffer(std::move(pDevice)) +GBufferRaster::GBufferRaster(ref pDevice, const Dictionary& dict) + : GBuffer(pDevice) { // Check for required features. if (!mpDevice->isFeatureSupported(Device::SupportedFeatures::Barycentrics)) @@ -99,10 +77,27 @@ GBufferRaster::GBufferRaster(std::shared_ptr pDevice, const Dictionary& // Set depth function DepthStencilState::Desc dsDesc; dsDesc.setDepthFunc(DepthStencilState::Func::Equal).setDepthWriteMask(false); - DepthStencilState::SharedPtr pDsState = DepthStencilState::create(dsDesc); + ref pDsState = DepthStencilState::create(dsDesc); mGBufferPass.pState->setDepthStencilState(pDsState); - mpFbo = Fbo::create(mpDevice.get()); + mpFbo = Fbo::create(mpDevice); +} + +RenderPassReflection GBufferRaster::reflect(const CompileData& compileData) +{ + RenderPassReflection reflector; + const uint2 sz = RenderPassHelpers::calculateIOSize(mOutputSizeSelection, mFixedOutputSize, compileData.defaultTexDims); + + // Add the required depth output. This always exists. + reflector.addOutput(kDepthName, "Depth buffer").format(ResourceFormat::D32Float).bindFlags(Resource::BindFlags::DepthStencil).texture2D(sz.x, sz.y); + + // 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. + addRenderPassOutputs(reflector, kGBufferChannels, Resource::BindFlags::RenderTarget, sz); + addRenderPassOutputs(reflector, kGBufferExtraChannels, Resource::BindFlags::UnorderedAccess, sz); + reflector.getField(kVBufferName)->format(mVBufferFormat); + + return reflector; } void GBufferRaster::compile(RenderContext* pRenderContext, const CompileData& compileData) @@ -110,7 +105,7 @@ void GBufferRaster::compile(RenderContext* pRenderContext, const CompileData& co GBuffer::compile(pRenderContext, compileData); } -void GBufferRaster::setScene(RenderContext* pRenderContext, const Scene::SharedPtr& pScene) +void GBufferRaster::setScene(RenderContext* pRenderContext, const ref& pScene) { GBuffer::setScene(pRenderContext, pScene); @@ -172,7 +167,7 @@ void GBufferRaster::execute(RenderContext* pRenderContext, const RenderData& ren // Bind primary channels as render targets and clear them. for (size_t i = 0; i < kGBufferChannels.size(); ++i) { - Texture::SharedPtr pTex = getOutput(renderData, kGBufferChannels[i].name); + ref pTex = getOutput(renderData, kGBufferChannels[i].name); mpFbo->attachColorTarget(pTex, uint32_t(i)); } pRenderContext->clearFbo(mpFbo.get(), float4(0), 1.f, 0, FboAttachmentType::Color); @@ -218,14 +213,16 @@ void GBufferRaster::execute(RenderContext* pRenderContext, const RenderData& ren if (!mGBufferPass.pVars) mGBufferPass.pVars = GraphicsVars::create(mpDevice, mGBufferPass.pProgram.get()); + auto var = mGBufferPass.pVars->getRootVar(); + // Bind extra channels as UAV buffers. for (const auto& channel : kGBufferExtraChannels) { - Texture::SharedPtr pTex = getOutput(renderData, channel.name); - mGBufferPass.pVars[channel.texname] = pTex; + ref pTex = getOutput(renderData, channel.name); + var[channel.texname] = pTex; } - mGBufferPass.pVars["PerFrameCB"]["gFrameDim"] = mFrameDim; + var["PerFrameCB"]["gFrameDim"] = mFrameDim; mGBufferPass.pState->setFbo(mpFbo); // Sets the viewport // Rasterize the scene. diff --git a/Source/RenderPasses/GBuffer/GBuffer/GBufferRaster.h b/Source/RenderPasses/GBuffer/GBuffer/GBufferRaster.h index 2afd15c71..cddcb14e8 100644 --- a/Source/RenderPasses/GBuffer/GBuffer/GBufferRaster.h +++ b/Source/RenderPasses/GBuffer/GBuffer/GBufferRaster.h @@ -40,34 +40,32 @@ class GBufferRaster : public GBuffer public: FALCOR_PLUGIN_CLASS(GBufferRaster, "GBufferRaster", "Rasterized G-buffer generation pass."); - using SharedPtr = std::shared_ptr; + static ref create(ref pDevice, const Dictionary& dict) { return make_ref(pDevice, dict); } - static SharedPtr create(std::shared_ptr pDevice, const Dictionary& dict); + GBufferRaster(ref pDevice, const Dictionary& dict); RenderPassReflection reflect(const CompileData& compileData) override; void execute(RenderContext* pRenderContext, const RenderData& renderData) override; - void setScene(RenderContext* pRenderContext, const Scene::SharedPtr& pScene) override; + void setScene(RenderContext* pRenderContext, const ref& pScene) override; void onSceneUpdates(RenderContext* pRenderContext, Scene::UpdateFlags sceneUpdates) override; virtual void compile(RenderContext* pRenderContext, const CompileData& compileData) override; private: - GBufferRaster(std::shared_ptr pDevice, const Dictionary& dict); - // Internal state - Fbo::SharedPtr mpFbo; + ref mpFbo; struct { - GraphicsState::SharedPtr pState; - GraphicsProgram::SharedPtr pProgram; - GraphicsVars::SharedPtr pVars; + ref pState; + ref pProgram; + ref pVars; } mDepthPass; // Rasterization resources struct { - GraphicsState::SharedPtr pState; - GraphicsProgram::SharedPtr pProgram; - GraphicsVars::SharedPtr pVars; + ref pState; + ref pProgram; + ref pVars; } mGBufferPass; }; diff --git a/Source/RenderPasses/GBuffer/GBufferBase.cpp b/Source/RenderPasses/GBuffer/GBufferBase.cpp index 64c48624d..3279f8f2c 100644 --- a/Source/RenderPasses/GBuffer/GBufferBase.cpp +++ b/Source/RenderPasses/GBuffer/GBufferBase.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -182,7 +182,7 @@ void GBufferBase::execute(RenderContext* pRenderContext, const RenderData& rende dict[Falcor::kRenderPassGBufferAdjustShadingNormals] = mAdjustShadingNormals; } -void GBufferBase::setScene(RenderContext* pRenderContext, const Scene::SharedPtr& pScene) +void GBufferBase::setScene(RenderContext* pRenderContext, const ref& pScene) { mpScene = pScene; mFrameCount = 0; @@ -200,7 +200,7 @@ void GBufferBase::setScene(RenderContext* pRenderContext, const Scene::SharedPtr } } -static CPUSampleGenerator::SharedPtr createSamplePattern(GBufferBase::SamplePattern type, uint32_t sampleCount) +static ref createSamplePattern(GBufferBase::SamplePattern type, uint32_t sampleCount) { switch (type) { @@ -234,7 +234,7 @@ void GBufferBase::updateSamplePattern() if (mpSampleGenerator) mSampleCount = mpSampleGenerator->getSampleCount(); } -Texture::SharedPtr GBufferBase::getOutput(const RenderData& renderData, const std::string& name) const +ref GBufferBase::getOutput(const RenderData& renderData, const std::string& name) const { // This helper fetches the render pass output with the given name and verifies it has the correct size. FALCOR_ASSERT(mFrameDim.x > 0 && mFrameDim.y > 0); diff --git a/Source/RenderPasses/GBuffer/GBufferBase.h b/Source/RenderPasses/GBuffer/GBufferBase.h index ea4163204..e35904d02 100644 --- a/Source/RenderPasses/GBuffer/GBufferBase.h +++ b/Source/RenderPasses/GBuffer/GBufferBase.h @@ -27,6 +27,7 @@ **************************************************************************/ #pragma once #include "Falcor.h" +#include "RenderGraph/RenderPass.h" #include "RenderGraph/RenderPassHelpers.h" using namespace Falcor; @@ -47,19 +48,19 @@ class GBufferBase : public RenderPass virtual void renderUI(Gui::Widgets& widget) override; virtual void execute(RenderContext* pRenderContext, const RenderData& renderData) override; virtual Dictionary getScriptingDictionary() override; - virtual void setScene(RenderContext* pRenderContext, const Scene::SharedPtr& pScene) override; + virtual void setScene(RenderContext* pRenderContext, const ref& pScene) override; protected: - GBufferBase(std::shared_ptr pDevice) : RenderPass(std::move(pDevice)) {} + GBufferBase(ref pDevice) : RenderPass(pDevice) {} virtual void parseDictionary(const Dictionary& dict); virtual void setCullMode(RasterizerState::CullMode mode) { mCullMode = mode; } void updateFrameDim(const uint2 frameDim); void updateSamplePattern(); - Texture::SharedPtr getOutput(const RenderData& renderData, const std::string& name) const; + ref getOutput(const RenderData& renderData, const std::string& name) const; // Internal state - Scene::SharedPtr mpScene; - CPUSampleGenerator::SharedPtr mpSampleGenerator; ///< Sample generator for camera jitter. + ref mpScene; + ref mpSampleGenerator; ///< Sample generator for camera jitter. uint32_t mFrameCount = 0; ///< Frames rendered since last change of scene. This is used as random seed. uint2 mFrameDim = {}; ///< Current frame dimension in pixels. Note this may be different from the window size. diff --git a/Source/RenderPasses/GBuffer/VBuffer/VBufferRT.cpp b/Source/RenderPasses/GBuffer/VBuffer/VBufferRT.cpp index a10ce7055..86027acdb 100644 --- a/Source/RenderPasses/GBuffer/VBuffer/VBufferRT.cpp +++ b/Source/RenderPasses/GBuffer/VBuffer/VBufferRT.cpp @@ -57,9 +57,18 @@ namespace }; }; -VBufferRT::SharedPtr VBufferRT::create(std::shared_ptr pDevice, const Dictionary& dict) +VBufferRT::VBufferRT(ref pDevice, const Dictionary& dict) + : GBufferBase(pDevice) { - return SharedPtr(new VBufferRT(std::move(pDevice), dict)); + if (!mpDevice->isFeatureSupported(Device::SupportedFeatures::RaytracingTier1_1)) + { + throw RuntimeError("VBufferRT: Raytracing Tier 1.1 is not supported by the current device"); + } + + parseDictionary(dict); + + // Create sample generator + mpSampleGenerator = SampleGenerator::create(mpDevice, SAMPLE_GENERATOR_DEFAULT); } RenderPassReflection VBufferRT::reflect(const CompileData& compileData) @@ -138,13 +147,25 @@ Dictionary VBufferRT::getScriptingDictionary() return dict; } -void VBufferRT::setScene(RenderContext* pRenderContext, const Scene::SharedPtr& pScene) +void VBufferRT::setScene(RenderContext* pRenderContext, const ref& pScene) { GBufferBase::setScene(pRenderContext, pScene); recreatePrograms(); } +void VBufferRT::parseDictionary(const Dictionary& dict) +{ + GBufferBase::parseDictionary(dict); + + for (const auto& [key, value] : dict) + { + if (key == kUseTraceRayInline) mUseTraceRayInline = value; + else if (key == kUseDOF) mUseDOF = value; + // TODO: Check for unparsed fields, including those parsed in base classes. + } +} + void VBufferRT::recreatePrograms() { mRaytrace.pProgram = nullptr; @@ -170,7 +191,7 @@ void VBufferRT::executeRaytrace(RenderContext* pRenderContext, const RenderData& desc.setMaxAttributeSize(mpScene->getRaytracingMaxAttributeSize()); desc.setMaxTraceRecursionDepth(kMaxRecursionDepth); - RtBindingTable::SharedPtr sbt = RtBindingTable::create(1, 1, mpScene->getGeometryCount()); + ref sbt = RtBindingTable::create(1, 1, mpScene->getGeometryCount()); sbt->setRayGen(desc.addRayGen("rayGen")); sbt->setMiss(0, desc.addMiss("miss")); sbt->setHitGroup(0, mpScene->getGeometryIDs(Scene::GeometryType::TriangleMesh), desc.addHitGroup("closestHit", "anyHit")); @@ -270,34 +291,8 @@ void VBufferRT::setShaderData(const ShaderVar& var, const RenderData& renderData // Bind output channels as UAV buffers. auto bind = [&](const ChannelDesc& channel) { - Texture::SharedPtr pTex = getOutput(renderData, channel.name); + ref pTex = getOutput(renderData, channel.name); var[channel.texname] = pTex; }; for (const auto& channel : kVBufferExtraChannels) bind(channel); } - -VBufferRT::VBufferRT(std::shared_ptr pDevice, const Dictionary& dict) - : GBufferBase(std::move(pDevice)) -{ - if (!mpDevice->isFeatureSupported(Device::SupportedFeatures::RaytracingTier1_1)) - { - throw RuntimeError("VBufferRT: Raytracing Tier 1.1 is not supported by the current device"); - } - - parseDictionary(dict); - - // Create sample generator - mpSampleGenerator = SampleGenerator::create(mpDevice, SAMPLE_GENERATOR_DEFAULT); -} - -void VBufferRT::parseDictionary(const Dictionary& dict) -{ - GBufferBase::parseDictionary(dict); - - for (const auto& [key, value] : dict) - { - if (key == kUseTraceRayInline) mUseTraceRayInline = value; - else if (key == kUseDOF) mUseDOF = value; - // TODO: Check for unparsed fields, including those parsed in base classes. - } -} diff --git a/Source/RenderPasses/GBuffer/VBuffer/VBufferRT.h b/Source/RenderPasses/GBuffer/VBuffer/VBufferRT.h index b010c6dbc..5610fb587 100644 --- a/Source/RenderPasses/GBuffer/VBuffer/VBufferRT.h +++ b/Source/RenderPasses/GBuffer/VBuffer/VBufferRT.h @@ -42,17 +42,19 @@ class VBufferRT : public GBufferBase public: FALCOR_PLUGIN_CLASS(VBufferRT, "VBufferRT", "Ray traced V-buffer generation pass."); - using SharedPtr = std::shared_ptr; + static ref create(ref pDevice, const Dictionary& dict) { return make_ref(pDevice, dict); } - static SharedPtr create(std::shared_ptr pDevice, const Dictionary& dict); + VBufferRT(ref pDevice, 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; + void setScene(RenderContext* pRenderContext, const ref& pScene) override; private: + void parseDictionary(const Dictionary& dict) override; + void executeRaytrace(RenderContext* pRenderContext, const RenderData& renderData); void executeCompute(RenderContext* pRenderContext, const RenderData& renderData); @@ -60,12 +62,9 @@ class VBufferRT : public GBufferBase void setShaderData(const ShaderVar& var, const RenderData& renderData); void recreatePrograms(); - VBufferRT(std::shared_ptr pDevice, const Dictionary& dict); - void parseDictionary(const Dictionary& dict) override; - // Internal state bool mComputeDOF = false; ///< Flag indicating if depth-of-field is computed for the current frame. - SampleGenerator::SharedPtr mpSampleGenerator; + ref mpSampleGenerator; // UI variables bool mUseTraceRayInline = false; @@ -73,9 +72,9 @@ class VBufferRT : public GBufferBase struct { - RtProgram::SharedPtr pProgram; - RtProgramVars::SharedPtr pVars; + ref pProgram; + ref pVars; } mRaytrace; - ComputePass::SharedPtr mpComputePass; + ref mpComputePass; }; diff --git a/Source/RenderPasses/GBuffer/VBuffer/VBufferRaster.cpp b/Source/RenderPasses/GBuffer/VBuffer/VBufferRaster.cpp index 917729dea..fa08d7833 100644 --- a/Source/RenderPasses/GBuffer/VBuffer/VBufferRaster.cpp +++ b/Source/RenderPasses/GBuffer/VBuffer/VBufferRaster.cpp @@ -48,28 +48,8 @@ namespace const std::string kDepthName = "depth"; } -RenderPassReflection VBufferRaster::reflect(const CompileData& compileData) -{ - RenderPassReflection reflector; - const uint2 sz = RenderPassHelpers::calculateIOSize(mOutputSizeSelection, mFixedOutputSize, compileData.defaultTexDims); - - // Add the required outputs. These always exist. - reflector.addOutput(kDepthName, "Depth buffer").format(ResourceFormat::D32Float).bindFlags(Resource::BindFlags::DepthStencil).texture2D(sz.x, sz.y); - reflector.addOutput(kVBufferName, kVBufferDesc).bindFlags(Resource::BindFlags::RenderTarget | Resource::BindFlags::UnorderedAccess).format(mVBufferFormat).texture2D(sz.x, sz.y); - - // Add all the other outputs. - addRenderPassOutputs(reflector, kVBufferExtraChannels, Resource::BindFlags::UnorderedAccess, sz); - - return reflector; -} - -VBufferRaster::SharedPtr VBufferRaster::create(std::shared_ptr pDevice, const Dictionary& dict) -{ - return SharedPtr(new VBufferRaster(std::move(pDevice), dict)); -} - -VBufferRaster::VBufferRaster(std::shared_ptr pDevice, const Dictionary& dict) - : GBufferBase(std::move(pDevice)) +VBufferRaster::VBufferRaster(ref pDevice, const Dictionary& dict) + : GBufferBase(pDevice) { // Check for required features. if (!mpDevice->isFeatureSupported(Device::SupportedFeatures::Barycentrics)) @@ -91,10 +71,25 @@ VBufferRaster::VBufferRaster(std::shared_ptr pDevice, const Dictionary& dsDesc.setDepthFunc(DepthStencilState::Func::LessEqual).setDepthWriteMask(true); mRaster.pState->setDepthStencilState(DepthStencilState::create(dsDesc)); - mpFbo = Fbo::create(mpDevice.get()); + mpFbo = Fbo::create(mpDevice); } -void VBufferRaster::setScene(RenderContext* pRenderContext, const Scene::SharedPtr& pScene) +RenderPassReflection VBufferRaster::reflect(const CompileData& compileData) +{ + RenderPassReflection reflector; + const uint2 sz = RenderPassHelpers::calculateIOSize(mOutputSizeSelection, mFixedOutputSize, compileData.defaultTexDims); + + // Add the required outputs. These always exist. + reflector.addOutput(kDepthName, "Depth buffer").format(ResourceFormat::D32Float).bindFlags(Resource::BindFlags::DepthStencil).texture2D(sz.x, sz.y); + reflector.addOutput(kVBufferName, kVBufferDesc).bindFlags(Resource::BindFlags::RenderTarget | Resource::BindFlags::UnorderedAccess).format(mVBufferFormat).texture2D(sz.x, sz.y); + + // Add all the other outputs. + addRenderPassOutputs(reflector, kVBufferExtraChannels, Resource::BindFlags::UnorderedAccess, sz); + + return reflector; +} + +void VBufferRaster::setScene(RenderContext* pRenderContext, const ref& pScene) { GBufferBase::setScene(pRenderContext, pScene); @@ -159,13 +154,15 @@ void VBufferRaster::execute(RenderContext* pRenderContext, const RenderData& ren mpFbo->attachColorTarget(pOutput, 0); mpFbo->attachDepthStencilTarget(pDepth); mRaster.pState->setFbo(mpFbo); // Sets the viewport - mRaster.pVars["PerFrameCB"]["gFrameDim"] = mFrameDim; + + auto var = mRaster.pVars->getRootVar(); + var["PerFrameCB"]["gFrameDim"] = mFrameDim; // Bind extra channels as UAV buffers. for (const auto& channel : kVBufferExtraChannels) { - Texture::SharedPtr pTex = getOutput(renderData, channel.name); - mRaster.pVars[channel.texname] = pTex; + ref pTex = getOutput(renderData, channel.name); + var[channel.texname] = pTex; } // Rasterize the scene. diff --git a/Source/RenderPasses/GBuffer/VBuffer/VBufferRaster.h b/Source/RenderPasses/GBuffer/VBuffer/VBufferRaster.h index 300809234..c23a2f539 100644 --- a/Source/RenderPasses/GBuffer/VBuffer/VBufferRaster.h +++ b/Source/RenderPasses/GBuffer/VBuffer/VBufferRaster.h @@ -41,24 +41,22 @@ class VBufferRaster : public GBufferBase public: FALCOR_PLUGIN_CLASS(VBufferRaster, "VBufferRaster", "Rasterized V-buffer generation pass."); - using SharedPtr = std::shared_ptr; + static ref create(ref pDevice, const Dictionary& dict) { return make_ref(pDevice, dict); } - static SharedPtr create(std::shared_ptr pDevice, const Dictionary& dict); + VBufferRaster(ref pDevice, const Dictionary& dict); RenderPassReflection reflect(const CompileData& compileData) override; - void setScene(RenderContext* pRenderContext, const Scene::SharedPtr& pScene) override; + void setScene(RenderContext* pRenderContext, const ref& pScene) override; void execute(RenderContext* pRenderContext, const RenderData& renderData) override; private: - VBufferRaster(std::shared_ptr pDevice, const Dictionary& dict); - // Internal state - Fbo::SharedPtr mpFbo; + ref mpFbo; struct { - GraphicsState::SharedPtr pState; - GraphicsProgram::SharedPtr pProgram; - GraphicsVars::SharedPtr pVars; + ref pState; + ref pProgram; + ref pVars; } mRaster; }; diff --git a/Source/RenderPasses/ImageLoader/ImageLoader.cpp b/Source/RenderPasses/ImageLoader/ImageLoader.cpp index cbfb162db..22f798a11 100644 --- a/Source/RenderPasses/ImageLoader/ImageLoader.cpp +++ b/Source/RenderPasses/ImageLoader/ImageLoader.cpp @@ -45,23 +45,8 @@ extern "C" FALCOR_API_EXPORT void registerPlugin(Falcor::PluginRegistry& registr registry.registerClass(); } -RenderPassReflection ImageLoader::reflect(const CompileData& compileData) -{ - RenderPassReflection reflector; - uint2 fixedSize = mpTex ? uint2(mpTex->getWidth(), mpTex->getHeight()) : uint2(0); - const uint2 sz = RenderPassHelpers::calculateIOSize(mOutputSizeSelection, fixedSize, compileData.defaultTexDims); - - reflector.addOutput(kDst, "Destination texture").format(mOutputFormat).texture2D(sz.x, sz.y); - return reflector; -} - -ImageLoader::SharedPtr ImageLoader::create(std::shared_ptr pDevice, const Dictionary& dict) -{ - return SharedPtr(new ImageLoader(std::move(pDevice), dict)); -} - -ImageLoader::ImageLoader(std::shared_ptr pDevice, const Dictionary& dict) - : RenderPass(std::move(pDevice)) +ImageLoader::ImageLoader(ref pDevice, const Dictionary& dict) + : RenderPass(pDevice) { for (const auto& [key, value] : dict) { @@ -84,6 +69,16 @@ ImageLoader::ImageLoader(std::shared_ptr pDevice, const Dictionary& dict } } +RenderPassReflection ImageLoader::reflect(const CompileData& compileData) +{ + RenderPassReflection reflector; + uint2 fixedSize = mpTex ? uint2(mpTex->getWidth(), mpTex->getHeight()) : uint2(0); + const uint2 sz = RenderPassHelpers::calculateIOSize(mOutputSizeSelection, fixedSize, compileData.defaultTexDims); + + reflector.addOutput(kDst, "Destination texture").format(mOutputFormat).texture2D(sz.x, sz.y); + return reflector; +} + Dictionary ImageLoader::getScriptingDictionary() { Dictionary dict; @@ -144,7 +139,7 @@ void ImageLoader::renderUI(Gui::Widgets& widget) if (mpTex->getMipCount() > 1) widget.slider("Mip Level", mMipLevel, 0u, mpTex->getMipCount() - 1); if (mpTex->getArraySize() > 1) widget.slider("Array Slice", mArraySlice, 0u, mpTex->getArraySize() - 1); - widget.image(mImagePath.string().c_str(), mpTex, { 320, 320 }); + widget.image(mImagePath.string().c_str(), mpTex.get(), { 320, 320 }); widget.text("Image format: " + to_string(mpTex->getFormat())); widget.text("Image size: (" + std::to_string(mpTex->getWidth()) + ", " + std::to_string(mpTex->getHeight()) + ")"); widget.text("Output format: " + to_string(mOutputFormat)); @@ -180,7 +175,7 @@ bool ImageLoader::loadImage(const std::filesystem::path& path) if (findFileInDataDirectories(mImagePath, fullPath)) { mImagePath = fullPath; - mpTex = Texture::createFromFile(mpDevice.get(), mImagePath, mGenerateMips, mLoadSRGB); + mpTex = Texture::createFromFile(mpDevice, mImagePath, mGenerateMips, mLoadSRGB); return mpTex != nullptr; } else diff --git a/Source/RenderPasses/ImageLoader/ImageLoader.h b/Source/RenderPasses/ImageLoader/ImageLoader.h index 236c344ef..e8d9f81f2 100644 --- a/Source/RenderPasses/ImageLoader/ImageLoader.h +++ b/Source/RenderPasses/ImageLoader/ImageLoader.h @@ -27,6 +27,7 @@ **************************************************************************/ #pragma once #include "Falcor.h" +#include "RenderGraph/RenderPass.h" #include "RenderGraph/RenderPassHelpers.h" using namespace Falcor; @@ -36,11 +37,9 @@ class ImageLoader : public RenderPass public: FALCOR_PLUGIN_CLASS(ImageLoader, "ImageLoader", "Load an image into a texture."); - using SharedPtr = std::shared_ptr; + static ref create(ref pDevice, const Dictionary& dict) { return make_ref(pDevice, dict); } - /** Create a new object - */ - static SharedPtr create(std::shared_ptr pDevice, const Dictionary& dict); + ImageLoader(ref pDevice, const Dictionary& dict); virtual RenderPassReflection reflect(const CompileData& compileData) override; virtual void compile(RenderContext* pRenderContext, const CompileData& compileData) override; @@ -49,15 +48,13 @@ class ImageLoader : public RenderPass virtual Dictionary getScriptingDictionary() override; private: - ImageLoader(std::shared_ptr pDevice, const Dictionary& dict); - bool loadImage(const std::filesystem::path& path); RenderPassHelpers::IOSize mOutputSizeSelection = RenderPassHelpers::IOSize::Default; ///< Selected output size. ResourceFormat mOutputFormat = ResourceFormat::Unknown; ///< Current output resource format. uint2 mOutputSize = {}; ///< Current output size in pixels. - Texture::SharedPtr mpTex; + ref mpTex; std::filesystem::path mImagePath; uint32_t mArraySlice = 0; uint32_t mMipLevel = 0; diff --git a/Source/RenderPasses/MinimalPathTracer/MinimalPathTracer.cpp b/Source/RenderPasses/MinimalPathTracer/MinimalPathTracer.cpp index 791c7b0aa..c3e26319e 100644 --- a/Source/RenderPasses/MinimalPathTracer/MinimalPathTracer.cpp +++ b/Source/RenderPasses/MinimalPathTracer/MinimalPathTracer.cpp @@ -61,13 +61,8 @@ namespace const char kUseImportanceSampling[] = "useImportanceSampling"; } -MinimalPathTracer::SharedPtr MinimalPathTracer::create(std::shared_ptr pDevice, const Dictionary& dict) -{ - return SharedPtr(new MinimalPathTracer(std::move(pDevice), dict)); -} - -MinimalPathTracer::MinimalPathTracer(std::shared_ptr pDevice, const Dictionary& dict) - : RenderPass(std::move(pDevice)) +MinimalPathTracer::MinimalPathTracer(ref pDevice, const Dictionary& dict) + : RenderPass(pDevice) { parseDictionary(dict); @@ -214,7 +209,7 @@ void MinimalPathTracer::renderUI(Gui::Widgets& widget) } } -void MinimalPathTracer::setScene(RenderContext* pRenderContext, const Scene::SharedPtr& pScene) +void MinimalPathTracer::setScene(RenderContext* pRenderContext, const ref& pScene) { // Clear data for previous scene. // After changing scene, the raytracing program should to be recreated. diff --git a/Source/RenderPasses/MinimalPathTracer/MinimalPathTracer.h b/Source/RenderPasses/MinimalPathTracer/MinimalPathTracer.h index acee641d7..9749a4f84 100644 --- a/Source/RenderPasses/MinimalPathTracer/MinimalPathTracer.h +++ b/Source/RenderPasses/MinimalPathTracer/MinimalPathTracer.h @@ -27,6 +27,7 @@ **************************************************************************/ #pragma once #include "Falcor.h" +#include "RenderGraph/RenderPass.h" #include "Utils/Sampling/SampleGenerator.h" using namespace Falcor; @@ -45,26 +46,25 @@ class MinimalPathTracer : public RenderPass public: FALCOR_PLUGIN_CLASS(MinimalPathTracer, "MinimalPathTracer", "Minimal path tracer."); - using SharedPtr = std::shared_ptr; + static ref create(ref pDevice, const Dictionary& dict) { return make_ref(pDevice, dict); } - static SharedPtr create(std::shared_ptr pDevice, const Dictionary& dict); + MinimalPathTracer(ref pDevice, const Dictionary& dict); 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 void setScene(RenderContext* pRenderContext, const ref& pScene) override; virtual bool onMouseEvent(const MouseEvent& mouseEvent) override { return false; } virtual bool onKeyEvent(const KeyboardEvent& keyEvent) override { return false; } private: - MinimalPathTracer(std::shared_ptr pDevice, const Dictionary& dict); void parseDictionary(const Dictionary& dict); void prepareVars(); // Internal state - Scene::SharedPtr mpScene; ///< Current scene. - SampleGenerator::SharedPtr mpSampleGenerator; ///< GPU sample generator. + ref mpScene; ///< Current scene. + ref mpSampleGenerator; ///< GPU sample generator. // Configuration uint mMaxBounces = 3; ///< Max number of indirect bounces (0 = none). @@ -78,8 +78,8 @@ class MinimalPathTracer : public RenderPass // Ray tracing program. struct { - RtProgram::SharedPtr pProgram; - RtBindingTable::SharedPtr pBindingTable; - RtProgramVars::SharedPtr pVars; + ref pProgram; + ref pBindingTable; + ref pVars; } mTracer; }; diff --git a/Source/RenderPasses/MinimalPathTracer/MinimalPathTracer.rt.slang b/Source/RenderPasses/MinimalPathTracer/MinimalPathTracer.rt.slang index bba959645..2722571b8 100644 --- a/Source/RenderPasses/MinimalPathTracer/MinimalPathTracer.rt.slang +++ b/Source/RenderPasses/MinimalPathTracer/MinimalPathTracer.rt.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -250,8 +250,9 @@ float3 evalDirectAnalytic(const ShadingData sd, const IMaterialInstance mi, inou const uint lobeTypes = mi.getLobeTypes(sd); const bool hasReflection = lobeTypes & uint(LobeType::Reflection); const bool hasTransmission = lobeTypes & uint(LobeType::Transmission); - if (dot(ls.dir, sd.N) <= kMinCosTheta && !hasTransmission) return float3(0.f); - if (dot(ls.dir, sd.N) >= -kMinCosTheta && !hasReflection) return float3(0.f); + float NdotL = dot(sd.getOrientedFaceNormal(), ls.dir); + if ((NdotL <= kMinCosTheta && !hasTransmission) || (NdotL >= -kMinCosTheta && !hasReflection)) + return float3(0.f); // Get origin with offset applied in direction of the geometry normal to avoid self-intersection. const float3 origin = computeRayOrigin(sd.posW, dot(sd.faceN, ls.dir) >= 0.f ? sd.faceN : -sd.faceN); @@ -327,7 +328,7 @@ void handleHit(const HitInfo hit, inout ScatterRayData rayData) if (isCurveHit) { // For curves, we set the new origin at the sphere center. - rayOrigin = sd.posW - sd.curveRadius * sd.N; + rayOrigin = sd.posW - sd.curveRadius * sd.frame.N; } else { @@ -398,7 +399,7 @@ float3 tracePath(const uint2 pixel, const uint2 frameDim) if (isCurveHit) { // For curves, we set the new origin at the sphere center. - rayOrigin = sd.posW - sd.curveRadius * sd.N; + rayOrigin = sd.posW - sd.curveRadius * sd.frame.N; } else { diff --git a/Source/RenderPasses/ModulateIllumination/ModulateIllumination.cpp b/Source/RenderPasses/ModulateIllumination/ModulateIllumination.cpp index 7e19b966f..e3621be53 100644 --- a/Source/RenderPasses/ModulateIllumination/ModulateIllumination.cpp +++ b/Source/RenderPasses/ModulateIllumination/ModulateIllumination.cpp @@ -71,13 +71,8 @@ extern "C" FALCOR_API_EXPORT void registerPlugin(Falcor::PluginRegistry& registr registry.registerClass(); } -ModulateIllumination::SharedPtr ModulateIllumination::create(std::shared_ptr pDevice, const Dictionary& dict) -{ - return SharedPtr(new ModulateIllumination(std::move(pDevice), dict)); -} - -ModulateIllumination::ModulateIllumination(std::shared_ptr pDevice, const Dictionary& dict) - : RenderPass(std::move(pDevice)) +ModulateIllumination::ModulateIllumination(ref pDevice, const Dictionary& dict) + : RenderPass(pDevice) { mpModulateIlluminationPass = ComputePass::create(mpDevice, kShaderFile, "main", Program::DefineList(), false); @@ -167,24 +162,25 @@ void ModulateIllumination::execute(RenderContext* pRenderContext, const RenderDa mpModulateIlluminationPass->setVars(nullptr); } - mpModulateIlluminationPass["CB"]["frameDim"] = mFrameDim; + auto var = mpModulateIlluminationPass->getRootVar(); + var["CB"]["frameDim"] = mFrameDim; auto bind = [&](const ChannelDesc& desc) { if (!desc.texname.empty()) { - Texture::SharedPtr pTexture = renderData.getTexture(desc.name); + ref pTexture = renderData.getTexture(desc.name); if (pTexture && (mFrameDim.x != pTexture->getWidth() || mFrameDim.y != pTexture->getHeight())) { logError("Texture {} has dim {}x{}, not compatible with the FrameDim {}x{}.", pTexture->getName(), pTexture->getWidth(), pTexture->getHeight(), mFrameDim.x, mFrameDim.y); } - mpModulateIlluminationPass[desc.texname] = pTexture; + var[desc.texname] = pTexture; } }; for (const auto& channel : kInputChannels) bind(channel); - mpModulateIlluminationPass["gOutput"] = renderData.getTexture(kOutput); + var["gOutput"] = renderData.getTexture(kOutput); mpModulateIlluminationPass->execute(pRenderContext, mFrameDim.x, mFrameDim.y); } diff --git a/Source/RenderPasses/ModulateIllumination/ModulateIllumination.h b/Source/RenderPasses/ModulateIllumination/ModulateIllumination.h index d4ea3d665..44c03f3c9 100644 --- a/Source/RenderPasses/ModulateIllumination/ModulateIllumination.h +++ b/Source/RenderPasses/ModulateIllumination/ModulateIllumination.h @@ -27,6 +27,7 @@ **************************************************************************/ #pragma once #include "Falcor.h" +#include "RenderGraph/RenderPass.h" #include "RenderGraph/RenderPassHelpers.h" using namespace Falcor; @@ -36,9 +37,9 @@ class ModulateIllumination : public RenderPass public: FALCOR_PLUGIN_CLASS(ModulateIllumination, "ModulateIllumination", "Modulate illumination pass."); - using SharedPtr = std::shared_ptr; + static ref create(ref pDevice, const Dictionary& dict) { return make_ref(pDevice, dict); } - static SharedPtr create(std::shared_ptr pDevice, const Dictionary& dict); + ModulateIllumination(ref pDevice, const Dictionary& dict); virtual Dictionary getScriptingDictionary() override; virtual RenderPassReflection reflect(const CompileData& compileData) override; @@ -47,12 +48,10 @@ class ModulateIllumination : public RenderPass virtual void renderUI(Gui::Widgets& widget) override; private: - ModulateIllumination(std::shared_ptr pDevice, const Dictionary& dict); - uint2 mFrameDim = { 0, 0 }; RenderPassHelpers::IOSize mOutputSizeSelection = RenderPassHelpers::IOSize::Default; ///< Selected output size. - ComputePass::SharedPtr mpModulateIlluminationPass; + ref mpModulateIlluminationPass; bool mUseEmission = true; bool mUseDiffuseReflectance = true; diff --git a/Source/RenderPasses/NRDPass/NRDPass.cpp b/Source/RenderPasses/NRDPass/NRDPass.cpp index 1a9307592..913894793 100644 --- a/Source/RenderPasses/NRDPass/NRDPass.cpp +++ b/Source/RenderPasses/NRDPass/NRDPass.cpp @@ -30,7 +30,6 @@ #include "NRDPass.h" #include "RenderPasses/Shared/Denoising/NRDConstants.slang" -#include namespace { @@ -107,13 +106,8 @@ namespace }; } -NRDPass::SharedPtr NRDPass::create(std::shared_ptr pDevice, const Dictionary& dict) -{ - return SharedPtr(new NRDPass(std::move(pDevice), dict)); -} - -NRDPass::NRDPass(std::shared_ptr pDevice, const Dictionary& dict) - : RenderPass(std::move(pDevice)) +NRDPass::NRDPass(ref pDevice, const Dictionary& dict) + : RenderPass(pDevice) { mpDevice->requireD3D12(); @@ -238,7 +232,7 @@ NRDPass::NRDPass(std::shared_ptr pDevice, const Dictionary& dict) } } -Falcor::Dictionary NRDPass::getScriptingDictionary() +Dictionary NRDPass::getScriptingDictionary() { Dictionary dict; @@ -594,7 +588,7 @@ void NRDPass::renderUI(Gui::Widgets& widget) } } -void NRDPass::setScene(RenderContext* pRenderContext, const Scene::SharedPtr& pScene) +void NRDPass::setScene(RenderContext* pRenderContext, const ref& pScene) { mpScene = pScene; } @@ -614,54 +608,54 @@ static void nrdFree(void* userArg, void* memory) free(memory); } -static Falcor::ResourceFormat getFalcorFormat(nrd::Format format) +static ResourceFormat getFalcorFormat(nrd::Format format) { switch (format) { - case nrd::Format::R8_UNORM: return Falcor::ResourceFormat::R8Unorm; - case nrd::Format::R8_SNORM: return Falcor::ResourceFormat::R8Snorm; - case nrd::Format::R8_UINT: return Falcor::ResourceFormat::R8Uint; - case nrd::Format::R8_SINT: return Falcor::ResourceFormat::R8Int; - case nrd::Format::RG8_UNORM: return Falcor::ResourceFormat::RG8Unorm; - case nrd::Format::RG8_SNORM: return Falcor::ResourceFormat::RG8Snorm; - case nrd::Format::RG8_UINT: return Falcor::ResourceFormat::RG8Uint; - case nrd::Format::RG8_SINT: return Falcor::ResourceFormat::RG8Int; - case nrd::Format::RGBA8_UNORM: return Falcor::ResourceFormat::RGBA8Unorm; - case nrd::Format::RGBA8_SNORM: return Falcor::ResourceFormat::RGBA8Snorm; - case nrd::Format::RGBA8_UINT: return Falcor::ResourceFormat::RGBA8Uint; - case nrd::Format::RGBA8_SINT: return Falcor::ResourceFormat::RGBA8Int; - case nrd::Format::RGBA8_SRGB: return Falcor::ResourceFormat::RGBA8UnormSrgb; - case nrd::Format::R16_UNORM: return Falcor::ResourceFormat::R16Unorm; - case nrd::Format::R16_SNORM: return Falcor::ResourceFormat::R16Snorm; - case nrd::Format::R16_UINT: return Falcor::ResourceFormat::R16Uint; - case nrd::Format::R16_SINT: return Falcor::ResourceFormat::R16Int; - case nrd::Format::R16_SFLOAT: return Falcor::ResourceFormat::R16Float; - case nrd::Format::RG16_UNORM: return Falcor::ResourceFormat::RG16Unorm; - case nrd::Format::RG16_SNORM: return Falcor::ResourceFormat::RG16Snorm; - case nrd::Format::RG16_UINT: return Falcor::ResourceFormat::RG16Uint; - case nrd::Format::RG16_SINT: return Falcor::ResourceFormat::RG16Int; - case nrd::Format::RG16_SFLOAT: return Falcor::ResourceFormat::RG16Float; - case nrd::Format::RGBA16_UNORM: return Falcor::ResourceFormat::RGBA16Unorm; - case nrd::Format::RGBA16_SNORM: return Falcor::ResourceFormat::Unknown; // Not defined in Falcor - case nrd::Format::RGBA16_UINT: return Falcor::ResourceFormat::RGBA16Uint; - case nrd::Format::RGBA16_SINT: return Falcor::ResourceFormat::RGBA16Int; - case nrd::Format::RGBA16_SFLOAT: return Falcor::ResourceFormat::RGBA16Float; - case nrd::Format::R32_UINT: return Falcor::ResourceFormat::R32Uint; - case nrd::Format::R32_SINT: return Falcor::ResourceFormat::R32Int; - case nrd::Format::R32_SFLOAT: return Falcor::ResourceFormat::R32Float; - case nrd::Format::RG32_UINT: return Falcor::ResourceFormat::RG32Uint; - case nrd::Format::RG32_SINT: return Falcor::ResourceFormat::RG32Int; - case nrd::Format::RG32_SFLOAT: return Falcor::ResourceFormat::RG32Float; - case nrd::Format::RGB32_UINT: return Falcor::ResourceFormat::RGB32Uint; - case nrd::Format::RGB32_SINT: return Falcor::ResourceFormat::RGB32Int; - case nrd::Format::RGB32_SFLOAT: return Falcor::ResourceFormat::RGB32Float; - case nrd::Format::RGBA32_UINT: return Falcor::ResourceFormat::RGBA32Uint; - case nrd::Format::RGBA32_SINT: return Falcor::ResourceFormat::RGBA32Int; - case nrd::Format::RGBA32_SFLOAT: return Falcor::ResourceFormat::RGBA32Float; - case nrd::Format::R10_G10_B10_A2_UNORM: return Falcor::ResourceFormat::RGB10A2Unorm; - case nrd::Format::R10_G10_B10_A2_UINT: return Falcor::ResourceFormat::RGB10A2Uint; - case nrd::Format::R11_G11_B10_UFLOAT: return Falcor::ResourceFormat::R11G11B10Float; - case nrd::Format::R9_G9_B9_E5_UFLOAT: return Falcor::ResourceFormat::RGB9E5Float; + case nrd::Format::R8_UNORM: return ResourceFormat::R8Unorm; + case nrd::Format::R8_SNORM: return ResourceFormat::R8Snorm; + case nrd::Format::R8_UINT: return ResourceFormat::R8Uint; + case nrd::Format::R8_SINT: return ResourceFormat::R8Int; + case nrd::Format::RG8_UNORM: return ResourceFormat::RG8Unorm; + case nrd::Format::RG8_SNORM: return ResourceFormat::RG8Snorm; + case nrd::Format::RG8_UINT: return ResourceFormat::RG8Uint; + case nrd::Format::RG8_SINT: return ResourceFormat::RG8Int; + case nrd::Format::RGBA8_UNORM: return ResourceFormat::RGBA8Unorm; + case nrd::Format::RGBA8_SNORM: return ResourceFormat::RGBA8Snorm; + case nrd::Format::RGBA8_UINT: return ResourceFormat::RGBA8Uint; + case nrd::Format::RGBA8_SINT: return ResourceFormat::RGBA8Int; + case nrd::Format::RGBA8_SRGB: return ResourceFormat::RGBA8UnormSrgb; + case nrd::Format::R16_UNORM: return ResourceFormat::R16Unorm; + case nrd::Format::R16_SNORM: return ResourceFormat::R16Snorm; + case nrd::Format::R16_UINT: return ResourceFormat::R16Uint; + case nrd::Format::R16_SINT: return ResourceFormat::R16Int; + case nrd::Format::R16_SFLOAT: return ResourceFormat::R16Float; + case nrd::Format::RG16_UNORM: return ResourceFormat::RG16Unorm; + case nrd::Format::RG16_SNORM: return ResourceFormat::RG16Snorm; + case nrd::Format::RG16_UINT: return ResourceFormat::RG16Uint; + case nrd::Format::RG16_SINT: return ResourceFormat::RG16Int; + case nrd::Format::RG16_SFLOAT: return ResourceFormat::RG16Float; + case nrd::Format::RGBA16_UNORM: return ResourceFormat::RGBA16Unorm; + case nrd::Format::RGBA16_SNORM: return ResourceFormat::Unknown; // Not defined in Falcor + case nrd::Format::RGBA16_UINT: return ResourceFormat::RGBA16Uint; + case nrd::Format::RGBA16_SINT: return ResourceFormat::RGBA16Int; + case nrd::Format::RGBA16_SFLOAT: return ResourceFormat::RGBA16Float; + case nrd::Format::R32_UINT: return ResourceFormat::R32Uint; + case nrd::Format::R32_SINT: return ResourceFormat::R32Int; + case nrd::Format::R32_SFLOAT: return ResourceFormat::R32Float; + case nrd::Format::RG32_UINT: return ResourceFormat::RG32Uint; + case nrd::Format::RG32_SINT: return ResourceFormat::RG32Int; + case nrd::Format::RG32_SFLOAT: return ResourceFormat::RG32Float; + case nrd::Format::RGB32_UINT: return ResourceFormat::RGB32Uint; + case nrd::Format::RGB32_SINT: return ResourceFormat::RGB32Int; + case nrd::Format::RGB32_SFLOAT: return ResourceFormat::RGB32Float; + case nrd::Format::RGBA32_UINT: return ResourceFormat::RGBA32Uint; + case nrd::Format::RGBA32_SINT: return ResourceFormat::RGBA32Int; + case nrd::Format::RGBA32_SFLOAT: return ResourceFormat::RGBA32Float; + case nrd::Format::R10_G10_B10_A2_UNORM: return ResourceFormat::RGB10A2Unorm; + case nrd::Format::R10_G10_B10_A2_UINT: return ResourceFormat::RGB10A2Uint; + case nrd::Format::R11_G11_B10_UFLOAT: return ResourceFormat::R11G11B10Float; + case nrd::Format::R9_G9_B9_E5_UFLOAT: return ResourceFormat::RGB9E5Float; default: throw RuntimeError("Unsupported NRD format."); } @@ -684,10 +678,10 @@ static nrd::Method getNrdMethod(NRDPass::DenoisingMethod denoisingMethod) /// Copies into col-major layout, as the NRD library works in column major layout, /// while Falcor uses row-major layout -static void copyMatrix(float* dstMatrix, const rmcv::mat4& srcMatrix) +static void copyMatrix(float* dstMatrix, const float4x4& srcMatrix) { - auto col_major = rmcv::transpose(srcMatrix); - memcpy(dstMatrix, static_cast(col_major.data()), sizeof(rmcv::mat4)); + float4x4 col_major = transpose(srcMatrix); + memcpy(dstMatrix, static_cast(col_major.data()), sizeof(float4x4)); } @@ -730,13 +724,13 @@ void NRDPass::createPipelines() const nrd::DenoiserDesc& denoiserDesc = nrd::GetDenoiserDesc(*mpDenoiser); // Create samplers descriptor layout and set. - Falcor::D3D12DescriptorSetLayout SamplersDescriptorSetLayout; + D3D12DescriptorSetLayout SamplersDescriptorSetLayout; for (uint32_t j = 0; j < denoiserDesc.staticSamplerNum; j++) { SamplersDescriptorSetLayout.addRange(ShaderResourceType::Sampler, denoiserDesc.staticSamplers[j].registerIndex, 1); } - mpSamplersDescriptorSet = Falcor::D3D12DescriptorSet::create(mpDevice.get(), SamplersDescriptorSetLayout, D3D12DescriptorSetBindingUsage::ExplicitBind); + mpSamplersDescriptorSet = D3D12DescriptorSet::create(mpDevice, SamplersDescriptorSetLayout, D3D12DescriptorSetBindingUsage::ExplicitBind); // Set sampler descriptors right away. for (uint32_t j = 0; j < denoiserDesc.staticSamplerNum; j++) @@ -751,7 +745,7 @@ void NRDPass::createPipelines() const nrd::ComputeShader& nrdComputeShader = nrdPipelineDesc.computeShaderDXIL; // Initialize descriptor set. - Falcor::D3D12DescriptorSetLayout CBVSRVUAVdescriptorSetLayout; + D3D12DescriptorSetLayout CBVSRVUAVdescriptorSetLayout; // Add constant buffer to descriptor set. CBVSRVUAVdescriptorSetLayout.addRange(ShaderResourceType::Cbv, denoiserDesc.constantBufferDesc.registerIndex, 1); @@ -770,13 +764,13 @@ void NRDPass::createPipelines() mCBVSRVUAVdescriptorSetLayouts.push_back(CBVSRVUAVdescriptorSetLayout); // Create root signature for the NRD pass. - Falcor::D3D12RootSignature::Desc rootSignatureDesc; + D3D12RootSignature::Desc rootSignatureDesc; rootSignatureDesc.addDescriptorSet(SamplersDescriptorSetLayout); rootSignatureDesc.addDescriptorSet(CBVSRVUAVdescriptorSetLayout); - const Falcor::D3D12RootSignature::Desc& desc = rootSignatureDesc; + const D3D12RootSignature::Desc& desc = rootSignatureDesc; - Falcor::D3D12RootSignature::SharedPtr pRootSig = Falcor::D3D12RootSignature::create(mpDevice.get(), desc); + ref pRootSig = D3D12RootSignature::create(mpDevice, desc); mpRootSignatures.push_back(pRootSig); @@ -791,16 +785,16 @@ void NRDPass::createPipelines() defines.add("NRD_COMPILER_DXC"); defines.add("NRD_USE_OCT_NORMAL_ENCODING", "1"); defines.add("NRD_USE_MATERIAL_ID", "0"); - ComputePass::SharedPtr pPass = ComputePass::create(mpDevice, programDesc, defines); + ref pPass = ComputePass::create(mpDevice, programDesc, defines); - ComputeProgram::SharedPtr pProgram = pPass->getProgram(); - ProgramKernels::SharedConstPtr pProgramKernels = pProgram->getActiveVersion()->getKernels(mpDevice.get(), pPass->getVars().get()); + ref pProgram = pPass->getProgram(); + ref pProgramKernels = pProgram->getActiveVersion()->getKernels(mpDevice.get(), pPass->getVars().get()); ComputeStateObject::Desc csoDesc; csoDesc.setProgramKernels(pProgramKernels); csoDesc.setD3D12RootSignatureOverride(pRootSig); - ComputeStateObject::SharedPtr pCSO = ComputeStateObject::create(mpDevice.get(), csoDesc); + ref pCSO = ComputeStateObject::create(mpDevice, csoDesc); mpPasses.push_back(pPass); mpCachedProgramKernels.push_back(pProgramKernels); @@ -824,28 +818,28 @@ void NRDPass::createResources() for (uint32_t i = 0; i < denoiserDesc.staticSamplerNum; i++) { const nrd::StaticSamplerDesc& nrdStaticsampler = denoiserDesc.staticSamplers[i]; - Falcor::Sampler::Desc samplerDesc; - samplerDesc.setFilterMode(Falcor::Sampler::Filter::Linear, Falcor::Sampler::Filter::Linear, Falcor::Sampler::Filter::Point); + Sampler::Desc samplerDesc; + samplerDesc.setFilterMode(Sampler::Filter::Linear, Sampler::Filter::Linear, Sampler::Filter::Point); if (nrdStaticsampler.sampler == nrd::Sampler::NEAREST_CLAMP || nrdStaticsampler.sampler == nrd::Sampler::LINEAR_CLAMP) { - samplerDesc.setAddressingMode(Falcor::Sampler::AddressMode::Clamp, Falcor::Sampler::AddressMode::Clamp, Falcor::Sampler::AddressMode::Clamp); + samplerDesc.setAddressingMode(Sampler::AddressMode::Clamp, Sampler::AddressMode::Clamp, Sampler::AddressMode::Clamp); } else { - samplerDesc.setAddressingMode(Falcor::Sampler::AddressMode::Mirror, Falcor::Sampler::AddressMode::Mirror, Falcor::Sampler::AddressMode::Mirror); + samplerDesc.setAddressingMode(Sampler::AddressMode::Mirror, Sampler::AddressMode::Mirror, Sampler::AddressMode::Mirror); } if (nrdStaticsampler.sampler == nrd::Sampler::NEAREST_CLAMP || nrdStaticsampler.sampler == nrd::Sampler::NEAREST_MIRRORED_REPEAT) { - samplerDesc.setFilterMode(Falcor::Sampler::Filter::Point, Falcor::Sampler::Filter::Point, Falcor::Sampler::Filter::Point); + samplerDesc.setFilterMode(Sampler::Filter::Point, Sampler::Filter::Point, Sampler::Filter::Point); } else { - samplerDesc.setFilterMode(Falcor::Sampler::Filter::Linear, Falcor::Sampler::Filter::Linear, Falcor::Sampler::Filter::Point); + samplerDesc.setFilterMode(Sampler::Filter::Linear, Sampler::Filter::Linear, Sampler::Filter::Point); } - mpSamplers.push_back(Falcor::Sampler::create(mpDevice.get(), samplerDesc)); + mpSamplers.push_back(Sampler::create(mpDevice, samplerDesc)); } // Texture pool. @@ -859,9 +853,9 @@ void NRDPass::createResources() : denoiserDesc.transientPool[i - denoiserDesc.permanentPoolSize]; // Create texture. - Falcor::ResourceFormat textureFormat = getFalcorFormat(nrdTextureDesc.format); - Falcor::Texture::SharedPtr pTexture = Texture::create2D( - mpDevice.get(), + ResourceFormat textureFormat = getFalcorFormat(nrdTextureDesc.format); + ref pTexture = Texture::create2D( + mpDevice, nrdTextureDesc.width, nrdTextureDesc.height, textureFormat, 1u, nrdTextureDesc.mipNum, nullptr, @@ -875,10 +869,10 @@ void NRDPass::createResources() // Constant buffer. mpConstantBuffer = Buffer::create( - mpDevice.get(), + mpDevice, denoiserDesc.constantBufferDesc.maxDataSize, - Falcor::ResourceBindFlags::Constant, - Falcor::Buffer::CpuAccess::Write, + ResourceBindFlags::Constant, + Buffer::CpuAccess::Write, nullptr); } @@ -896,7 +890,7 @@ void NRDPass::executeInternal(RenderContext* pRenderContext, const RenderData& r // Run classic Falcor compute pass to pack radiance. { FALCOR_PROFILE(pRenderContext, "PackRadiance"); - auto perImageCB = mpPackRadiancePassRelax["PerImageCB"]; + auto perImageCB = mpPackRadiancePassRelax->getRootVar()["PerImageCB"]; perImageCB["gMaxIntensity"] = mMaxIntensity; perImageCB["gDiffuseRadianceHitDist"] = renderData.getTexture(kInputDiffuseRadianceHitDist); @@ -911,7 +905,7 @@ void NRDPass::executeInternal(RenderContext* pRenderContext, const RenderData& r // Run classic Falcor compute pass to pack radiance and hit distance. { FALCOR_PROFILE(pRenderContext, "PackRadianceHitDist"); - auto perImageCB = mpPackRadiancePassRelax["PerImageCB"]; + auto perImageCB = mpPackRadiancePassRelax->getRootVar()["PerImageCB"]; perImageCB["gMaxIntensity"] = mMaxIntensity; perImageCB["gDiffuseRadianceHitDist"] = renderData.getTexture(kInputDiffuseRadianceHitDist); @@ -925,7 +919,7 @@ void NRDPass::executeInternal(RenderContext* pRenderContext, const RenderData& r // Run classic Falcor compute pass to pack radiance and hit distance. { FALCOR_PROFILE(pRenderContext, "PackRadianceHitDist"); - auto perImageCB = mpPackRadiancePassReblur["PerImageCB"]; + auto perImageCB = mpPackRadiancePassReblur->getRootVar()["PerImageCB"]; perImageCB["gHitDistParams"].setBlob(mReblurSettings.hitDistanceParameters); perImageCB["gMaxIntensity"] = mMaxIntensity; @@ -955,8 +949,8 @@ void NRDPass::executeInternal(RenderContext* pRenderContext, const RenderData& r } // Initialize common settings. - rmcv::mat4 viewMatrix = mpScene->getCamera()->getViewMatrix(); - rmcv::mat4 projMatrix = mpScene->getCamera()->getData().projMatNoJitter; + float4x4 viewMatrix = mpScene->getCamera()->getViewMatrix(); + float4x4 projMatrix = mpScene->getCamera()->getData().projMatNoJitter; if (mFrameIndex == 0) { mPrevViewMatrix = viewMatrix; @@ -1008,10 +1002,10 @@ void NRDPass::dispatch(RenderContext* pRenderContext, const RenderData& renderDa mpConstantBuffer->setBlob(dispatchDesc.constantBufferData, 0, dispatchDesc.constantBufferDataSize); // Create descriptor set for the NRD pass. - D3D12DescriptorSet::SharedPtr CBVSRVUAVDescriptorSet = D3D12DescriptorSet::create(mpDevice.get(), mCBVSRVUAVdescriptorSetLayouts[dispatchDesc.pipelineIndex], D3D12DescriptorSetBindingUsage::ExplicitBind); + ref CBVSRVUAVDescriptorSet = D3D12DescriptorSet::create(mpDevice, mCBVSRVUAVdescriptorSetLayouts[dispatchDesc.pipelineIndex], D3D12DescriptorSetBindingUsage::ExplicitBind); // Set CBV. - mpCBV = D3D12ConstantBufferView::create(mpDevice.get(), mpConstantBuffer); + mpCBV = D3D12ConstantBufferView::create(mpDevice, mpConstantBuffer); CBVSRVUAVDescriptorSet->setCbv(0 /* NB: range #0 is CBV range */, denoiserDesc.constantBufferDesc.registerIndex, mpCBV.get()); uint32_t resourceIndex = 0; @@ -1026,7 +1020,7 @@ void NRDPass::dispatch(RenderContext* pRenderContext, const RenderData& renderDa FALCOR_ASSERT(resource.stateNeeded == nrdDescriptorRange.descriptorType); - Falcor::Texture::SharedPtr texture; + ref texture; switch (resource.type) { @@ -1080,22 +1074,22 @@ void NRDPass::dispatch(RenderContext* pRenderContext, const RenderData& renderDa FALCOR_ASSERT(texture); // Set up resource barriers. - Falcor::Resource::State newState = resource.stateNeeded == nrd::DescriptorType::TEXTURE ? Falcor::Resource::State::ShaderResource : Falcor::Resource::State::UnorderedAccess; + Resource::State newState = resource.stateNeeded == nrd::DescriptorType::TEXTURE ? Resource::State::ShaderResource : Resource::State::UnorderedAccess; for (uint16_t mip = 0; mip < resource.mipNum; mip++) { - const Falcor::ResourceViewInfo viewInfo = Falcor::ResourceViewInfo(resource.mipOffset + mip, 1, 0, 1); + const ResourceViewInfo viewInfo = ResourceViewInfo(resource.mipOffset + mip, 1, 0, 1); pRenderContext->resourceBarrier(texture.get(), newState, &viewInfo); } // Set the SRV and UAV descriptors. if (nrdDescriptorRange.descriptorType == nrd::DescriptorType::TEXTURE) { - Falcor::ShaderResourceView::SharedPtr pSRV = texture->getSRV(resource.mipOffset, resource.mipNum, 0, 1); + ref pSRV = texture->getSRV(resource.mipOffset, resource.mipNum, 0, 1); CBVSRVUAVDescriptorSet->setSrv(descriptorRangeIndex + 1 /* NB: range #0 is CBV range */, nrdDescriptorRange.baseRegisterIndex + descriptorOffset, pSRV.get()); } else { - Falcor::UnorderedAccessView::SharedPtr pUAV = texture->getUAV(resource.mipOffset, 0, 1); + ref pUAV = texture->getUAV(resource.mipOffset, 0, 1); CBVSRVUAVDescriptorSet->setUav(descriptorRangeIndex + 1 /* NB: range #0 is CBV range */, nrdDescriptorRange.baseRegisterIndex + descriptorOffset, pUAV.get()); } @@ -1110,9 +1104,9 @@ void NRDPass::dispatch(RenderContext* pRenderContext, const RenderData& renderDa CBVSRVUAVDescriptorSet->bindForCompute(pRenderContext, mpRootSignatures[dispatchDesc.pipelineIndex].get(), 1); // Set pipeline state. - ComputePass::SharedPtr pPass = mpPasses[dispatchDesc.pipelineIndex]; - ComputeProgram::SharedPtr pProgram = pPass->getProgram(); - ProgramKernels::SharedConstPtr pProgramKernels = pProgram->getActiveVersion()->getKernels(mpDevice.get(), pPass->getVars().get()); + ref pPass = mpPasses[dispatchDesc.pipelineIndex]; + ref pProgram = pPass->getProgram(); + ref pProgramKernels = pProgram->getActiveVersion()->getKernels(mpDevice.get(), pPass->getVars().get()); // Check if anything changed. bool newProgram = (pProgramKernels.get() != mpCachedProgramKernels[dispatchDesc.pipelineIndex].get()); @@ -1124,7 +1118,7 @@ void NRDPass::dispatch(RenderContext* pRenderContext, const RenderData& renderDa desc.setProgramKernels(pProgramKernels); desc.setD3D12RootSignatureOverride(mpRootSignatures[dispatchDesc.pipelineIndex]); - ComputeStateObject::SharedPtr pCSO = ComputeStateObject::create(mpDevice.get(), desc); + ref pCSO = ComputeStateObject::create(mpDevice, desc); mpCSOs[dispatchDesc.pipelineIndex] = pCSO; } ID3D12GraphicsCommandList* pCommandList = pRenderContext->getLowLevelData()->getCommandBufferNativeHandle().as(); @@ -1146,7 +1140,7 @@ static void registerNRDPass(pybind11::module& m) profile.value("SpecularDeltaMv", NRDPass::DenoisingMethod::SpecularDeltaMv); } -extern "C" FALCOR_API_EXPORT void registerPlugin(Falcor::PluginRegistry& registry) +extern "C" FALCOR_API_EXPORT void registerPlugin(PluginRegistry& registry) { registry.registerClass(); ScriptBindings::registerBinding(registerNRDPass); diff --git a/Source/RenderPasses/NRDPass/NRDPass.h b/Source/RenderPasses/NRDPass/NRDPass.h index ee71db033..947a51de8 100644 --- a/Source/RenderPasses/NRDPass/NRDPass.h +++ b/Source/RenderPasses/NRDPass/NRDPass.h @@ -42,8 +42,6 @@ class NRDPass : public RenderPass public: FALCOR_PLUGIN_CLASS(NRDPass, "NRD", "NRD denoiser."); - using SharedPtr = std::shared_ptr; - enum class DenoisingMethod : uint32_t { RelaxDiffuseSpecular, @@ -53,19 +51,19 @@ class NRDPass : public RenderPass SpecularDeltaMv }; - static SharedPtr create(std::shared_ptr pDevice, const Dictionary& dict); + static ref create(ref pDevice, const Dictionary& dict) { return make_ref(pDevice, dict); } + + NRDPass(ref pDevice, const Dictionary& dict); 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 renderUI(Gui::Widgets& widget) override; - virtual void setScene(RenderContext* pRenderContext, const Scene::SharedPtr& pScene) override; + virtual void setScene(RenderContext* pRenderContext, const ref& pScene) override; private: - NRDPass(std::shared_ptr pDevice, const Dictionary& dict); - - Scene::SharedPtr mpScene; + ref mpScene; uint2 mScreenSize{}; uint32_t mFrameIndex = 0; RenderPassHelpers::IOSize mOutputSizeSelection = RenderPassHelpers::IOSize::Default; ///< Selected output size. @@ -89,22 +87,22 @@ class NRDPass : public RenderPass nrd::RelaxDiffuseSettings mRelaxDiffuseSettings = {}; nrd::ReblurSettings mReblurSettings = {}; - std::vector mpSamplers; - std::vector mCBVSRVUAVdescriptorSetLayouts; - Falcor::D3D12DescriptorSet::SharedPtr mpSamplersDescriptorSet; - std::vector mpRootSignatures; - std::vector mpPasses; - std::vector mpCachedProgramKernels; - std::vector mpCSOs; - std::vector mpPermanentTextures; - std::vector mpTransientTextures; - Falcor::Buffer::SharedPtr mpConstantBuffer; - Falcor::D3D12ConstantBufferView::SharedPtr mpCBV; - - rmcv::mat4 mPrevViewMatrix; - rmcv::mat4 mPrevProjMatrix; + std::vector> mpSamplers; + std::vector mCBVSRVUAVdescriptorSetLayouts; + ref mpSamplersDescriptorSet; + std::vector> mpRootSignatures; + std::vector> mpPasses; + std::vector> mpCachedProgramKernels; + std::vector> mpCSOs; + std::vector> mpPermanentTextures; + std::vector> mpTransientTextures; + ref mpConstantBuffer; + ref mpCBV; + + float4x4 mPrevViewMatrix; + float4x4 mPrevProjMatrix; // Additional classic Falcor compute pass and resources for packing radiance and hitT for NRD. - ComputePass::SharedPtr mpPackRadiancePassRelax; - ComputePass::SharedPtr mpPackRadiancePassReblur; + ref mpPackRadiancePassRelax; + ref mpPackRadiancePassReblur; }; diff --git a/Source/RenderPasses/OptixDenoiser/CMakeLists.txt b/Source/RenderPasses/OptixDenoiser/CMakeLists.txt index 1120c4637..41540370c 100644 --- a/Source/RenderPasses/OptixDenoiser/CMakeLists.txt +++ b/Source/RenderPasses/OptixDenoiser/CMakeLists.txt @@ -5,15 +5,14 @@ endif() add_plugin(OptixDenoiser) target_sources(OptixDenoiser PRIVATE - BootstrapUtils.cpp ConvertBufToTex.ps.slang ConvertMotionVectorInputs.cs.slang ConvertNormalsToBuf.cs.slang ConvertTexToBuf.cs.slang - CudaUtils.cpp - CudaUtils.h OptixDenoiser.cpp OptixDenoiser.h + OptixUtils.cpp + OptixUtils.h ) target_copy_shaders(OptixDenoiser RenderPasses/OptixDenoiser) diff --git a/Source/RenderPasses/OptixDenoiser/OptixDenoiser.cpp b/Source/RenderPasses/OptixDenoiser/OptixDenoiser.cpp index a20e4c3e7..cf4ecc8e5 100644 --- a/Source/RenderPasses/OptixDenoiser/OptixDenoiser.cpp +++ b/Source/RenderPasses/OptixDenoiser/OptixDenoiser.cpp @@ -26,7 +26,6 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **************************************************************************/ #include "OptixDenoiser.h" -#include "CudaUtils.h" namespace { @@ -52,7 +51,7 @@ namespace static void regOptixDenoiser(pybind11::module& m) { - pybind11::class_ pass(m, "OptixDenoiser"); + pybind11::class_> pass(m, "OptixDenoiser"); pass.def_property(kEnabled, &OptixDenoiser_::getEnabled, &OptixDenoiser_::setEnabled); pybind11::enum_ model(m, "OptixDenoiserModel"); @@ -68,8 +67,8 @@ extern "C" FALCOR_API_EXPORT void registerPlugin(Falcor::PluginRegistry& registr ScriptBindings::registerBinding(regOptixDenoiser); } -OptixDenoiser_::OptixDenoiser_(std::shared_ptr pDevice, const Dictionary& dict) - : RenderPass(std::move(pDevice)) +OptixDenoiser_::OptixDenoiser_(ref pDevice, const Dictionary& dict) + : RenderPass(pDevice) { for (const auto& [key, value] : dict) { @@ -88,12 +87,7 @@ OptixDenoiser_::OptixDenoiser_(std::shared_ptr pDevice, const Dictionary mpConvertNormalsToBuf = ComputePass::create(mpDevice, kConvertNormalsToBufFile, "main"); mpConvertMotionVectors = ComputePass::create(mpDevice, kConvertMotionVecFile, "main"); mpConvertBufToTex = FullScreenPass::create(mpDevice, kConvertBufToTexFile); - mpFbo = Fbo::create(mpDevice.get()); -} - -OptixDenoiser_::SharedPtr OptixDenoiser_::create(std::shared_ptr pDevice, const Dictionary& dict) -{ - return SharedPtr(new OptixDenoiser_(std::move(pDevice), dict)); + mpFbo = Fbo::create(mpDevice); } Dictionary OptixDenoiser_::getScriptingDictionary() @@ -120,7 +114,7 @@ RenderPassReflection OptixDenoiser_::reflect(const CompileData& compileData) return r; } -void OptixDenoiser_::setScene(RenderContext* pRenderContext, const std::shared_ptr& pScene) +void OptixDenoiser_::setScene(RenderContext* pRenderContext, const ref& pScene) { mpScene = pScene; } @@ -170,7 +164,7 @@ void OptixDenoiser_::compile(RenderContext* pRenderContext, const CompileData& c mDenoiser.tileHeight = newSize.y; // Reallocate / reszize our staging buffers for transferring data to and from OptiX / CUDA / DXR - if (newSize != mBufferSize && newSize.x > 0 && newSize.y > 0) + if (any(newSize != mBufferSize) && all(newSize > 0u)) { mBufferSize = newSize; reallocateStagingBuffers(pRenderContext); @@ -249,7 +243,7 @@ void OptixDenoiser_::allocateStagingBuffer(RenderContext* pRenderContext, Intero if (interop.devicePtr) freeSharedDevicePtr((void*)interop.devicePtr); // Create a new DX <-> CUDA shared buffer using the Falcor API to create, then find its CUDA pointer. - interop.buffer = Buffer::createTyped(mpDevice.get(), falcorFormat, + interop.buffer = Buffer::createTyped(mpDevice, falcorFormat, mBufferSize.x * mBufferSize.y, Resource::BindFlags::ShaderResource | Resource::BindFlags::UnorderedAccess | Resource::BindFlags::RenderTarget | Resource::BindFlags::Shared); interop.devicePtr = (CUdeviceptr)exportBufferToCudaDevice(interop.buffer); @@ -301,7 +295,7 @@ void OptixDenoiser_::execute(RenderContext* pRenderContext, const RenderData& re } if (mHasNormalInput && mDenoiser.options.guideNormal) { - convertNormalsToBuf(pRenderContext, renderData.getTexture(kNormalInput), mDenoiser.interop.normal.buffer, mBufferSize, rmcv::transpose(rmcv::inverse(mpScene->getCamera()->getViewMatrix()))); + convertNormalsToBuf(pRenderContext, renderData.getTexture(kNormalInput), mDenoiser.interop.normal.buffer, mBufferSize, transpose(inverse(mpScene->getCamera()->getViewMatrix()))); } if (mHasMotionInput && mDenoiser.modelKind == OptixDenoiserModelKind::OPTIX_DENOISER_MODEL_KIND_TEMPORAL) { @@ -429,7 +423,7 @@ void OptixDenoiser_::renderUI(Gui::Widgets& widget) // Basically a wrapper to handle null Falcor Buffers gracefully, which couldn't // happen in getShareDevicePtr(), due to the bootstrapping that avoids namespace conflicts -void * OptixDenoiser_::exportBufferToCudaDevice(Buffer::SharedPtr &buf) +void * OptixDenoiser_::exportBufferToCudaDevice(ref &buf) { if (buf == nullptr) return nullptr; return getSharedDevicePtr(buf->getSharedApiHandle(), (uint32_t)buf->getSize()); @@ -474,40 +468,40 @@ void OptixDenoiser_::setupDenoiser() mDenoiser.scratchBuffer.getDevicePtr(), mDenoiser.scratchBuffer.getSize()); } -void OptixDenoiser_::convertMotionVectors(RenderContext* pRenderContext, const Texture::SharedPtr& tex, const Buffer::SharedPtr& buf, const uint2& size) +void OptixDenoiser_::convertMotionVectors(RenderContext* pRenderContext, const ref& tex, const ref& buf, const uint2& size) { - auto vars = mpConvertMotionVectors->getVars(); - vars["GlobalCB"]["gStride"] = size.x; - vars["GlobalCB"]["gSize"] = size; - vars["gInTex"] = tex; - vars["gOutBuf"] = buf; + auto var = mpConvertMotionVectors->getRootVar(); + var["GlobalCB"]["gStride"] = size.x; + var["GlobalCB"]["gSize"] = size; + var["gInTex"] = tex; + var["gOutBuf"] = buf; mpConvertMotionVectors->execute(pRenderContext, size.x, size.y); } -void OptixDenoiser_::convertTexToBuf(RenderContext* pRenderContext, const Texture::SharedPtr& tex, const Buffer::SharedPtr& buf, const uint2& size) +void OptixDenoiser_::convertTexToBuf(RenderContext* pRenderContext, const ref& tex, const ref& buf, const uint2& size) { - auto vars = mpConvertTexToBuf->getVars(); - vars["GlobalCB"]["gStride"] = size.x; - vars["gInTex"] = tex; - vars["gOutBuf"] = buf; + auto var = mpConvertTexToBuf->getRootVar(); + var["GlobalCB"]["gStride"] = size.x; + var["gInTex"] = tex; + var["gOutBuf"] = buf; mpConvertTexToBuf->execute(pRenderContext, size.x, size.y); } -void OptixDenoiser_::convertNormalsToBuf(RenderContext* pRenderContext, const Texture::SharedPtr& tex, const Buffer::SharedPtr& buf, const uint2& size, rmcv::mat4 viewIT) +void OptixDenoiser_::convertNormalsToBuf(RenderContext* pRenderContext, const ref& tex, const ref& buf, const uint2& size, float4x4 viewIT) { - auto vars = mpConvertNormalsToBuf->getVars(); - vars["GlobalCB"]["gStride"] = size.x; - vars["GlobalCB"]["gViewIT"] = viewIT; - vars["gInTex"] = tex; - vars["gOutBuf"] = buf; + auto var = mpConvertNormalsToBuf->getRootVar(); + var["GlobalCB"]["gStride"] = size.x; + var["GlobalCB"]["gViewIT"] = viewIT; + var["gInTex"] = tex; + var["gOutBuf"] = buf; mpConvertTexToBuf->execute(pRenderContext, size.x, size.y); } -void OptixDenoiser_::convertBufToTex(RenderContext* pRenderContext, const Buffer::SharedPtr& buf, const Texture::SharedPtr& tex, const uint2& size) +void OptixDenoiser_::convertBufToTex(RenderContext* pRenderContext, const ref& buf, const ref& tex, const uint2& size) { - auto vars = mpConvertBufToTex->getVars(); - vars["GlobalCB"]["gStride"] = size.x; - vars["gInBuf"] = buf; + auto var = mpConvertBufToTex->getRootVar(); + var["GlobalCB"]["gStride"] = size.x; + var["gInBuf"] = buf; mpFbo->attachColorTarget(tex, 0); mpConvertBufToTex->execute(pRenderContext, mpFbo); } diff --git a/Source/RenderPasses/OptixDenoiser/OptixDenoiser.h b/Source/RenderPasses/OptixDenoiser/OptixDenoiser.h index 5fcbece70..6cb279cf8 100644 --- a/Source/RenderPasses/OptixDenoiser/OptixDenoiser.h +++ b/Source/RenderPasses/OptixDenoiser/OptixDenoiser.h @@ -64,9 +64,11 @@ #pragma once #include "Falcor.h" -#include "RenderGraph/BasePasses/FullScreenPass.h" +#include "Core/Pass/FullScreenPass.h" +#include "RenderGraph/RenderPass.h" -#include "CudaUtils.h" +#include "OptixUtils.h" +#include "Utils/CudaUtils.h" using namespace Falcor; @@ -76,25 +78,23 @@ class OptixDenoiser_ : public RenderPass public: FALCOR_PLUGIN_CLASS(OptixDenoiser_, "OptixDenoiser", "Apply the OptiX AI Denoiser."); - using SharedPtr = std::shared_ptr; + static ref create(ref pDevice, const Dictionary& dict) { return make_ref(pDevice, dict); } - static SharedPtr create(std::shared_ptr pDevice, const Dictionary& dict); + OptixDenoiser_(ref pDevice, const Dictionary& dict); 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 renderUI(Gui::Widgets& widget) override; - virtual void setScene(RenderContext* pRenderContext, const std::shared_ptr& pScene) override; + virtual void setScene(RenderContext* pRenderContext, const ref& pScene) override; // Scripting functions bool getEnabled() const { return mEnabled; } void setEnabled(bool enabled) { mEnabled = enabled; } private: - OptixDenoiser_(std::shared_ptr pDevice, const Dictionary& dict); - - Scene::SharedPtr mpScene; + ref mpScene; /** Initializes OptiX & CUDA contexts. Returns true on success (if false, everything else will fail). */ @@ -111,10 +111,10 @@ class OptixDenoiser_ : public RenderPass avoid exposing OptiX interop outside this render pass) so this isn't much slower than a better-designed sharing of GPU memory between DX and OptiX. */ - void convertTexToBuf(RenderContext* pRenderContext, const Texture::SharedPtr& tex, const Buffer::SharedPtr& buf, const uint2& size); - void convertNormalsToBuf(RenderContext* pRenderContext, const Texture::SharedPtr& tex, const Buffer::SharedPtr& buf, const uint2& size, rmcv::mat4 viewIT); - void convertBufToTex(RenderContext* pRenderContext, const Buffer::SharedPtr& buf, const Texture::SharedPtr& tex, const uint2& size); - void convertMotionVectors(RenderContext* pRenderContext, const Texture::SharedPtr& tex, const Buffer::SharedPtr& buf, const uint2& size); + void convertTexToBuf(RenderContext* pRenderContext, const ref& tex, const ref& buf, const uint2& size); + void convertNormalsToBuf(RenderContext* pRenderContext, const ref& tex, const ref& buf, const uint2& size, float4x4 viewIT); + void convertBufToTex(RenderContext* pRenderContext, const ref& buf, const ref& tex, const uint2& size); + void convertMotionVectors(RenderContext* pRenderContext, const ref& tex, const ref& buf, const uint2& size); // Options and parameters for the Falcor render pass bool mEnabled = true; ///< True = using OptiX denoiser, False = pass is a no-op @@ -138,7 +138,7 @@ class OptixDenoiser_ : public RenderPass // Structure to encapsulate DX <-> CUDA interop data for a buffer struct Interop { - Buffer::SharedPtr buffer; // Falcor buffer + ref buffer; // Falcor buffer CUdeviceptr devicePtr = (CUdeviceptr)0; // CUDA pointer to buffer }; @@ -185,11 +185,11 @@ class OptixDenoiser_ : public RenderPass } mDenoiser; // Our shaders for converting buffers on input and output from OptiX - ComputePass::SharedPtr mpConvertTexToBuf; - ComputePass::SharedPtr mpConvertNormalsToBuf; - ComputePass::SharedPtr mpConvertMotionVectors; - FullScreenPass::SharedPtr mpConvertBufToTex; - Fbo::SharedPtr mpFbo; + ref mpConvertTexToBuf; + ref mpConvertNormalsToBuf; + ref mpConvertMotionVectors; + ref mpConvertBufToTex; + ref mpFbo; /** Allocate a DX <-> CUDA staging buffer */ @@ -205,5 +205,5 @@ class OptixDenoiser_ : public RenderPass /** Get a device pointer from a buffer. This wrapper gracefully handles nullptrs (i.e., if buf == nullptr) */ - void* exportBufferToCudaDevice(Buffer::SharedPtr& buf); + void* exportBufferToCudaDevice(ref& buf); }; diff --git a/Source/RenderPasses/OptixDenoiser/CudaUtils.cpp b/Source/RenderPasses/OptixDenoiser/OptixUtils.cpp similarity index 52% rename from Source/RenderPasses/OptixDenoiser/CudaUtils.cpp rename to Source/RenderPasses/OptixDenoiser/OptixUtils.cpp index fb37edebf..c1f9a5d3a 100644 --- a/Source/RenderPasses/OptixDenoiser/CudaUtils.cpp +++ b/Source/RenderPasses/OptixDenoiser/OptixUtils.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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,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 "CudaUtils.h" - +#include "OptixUtils.h" +#include "Utils/CudaUtils.h" #include -#include - -// These live in _BootstrapUtils.cpp since they use Falcor includes / namespace, -// which does not appear to play nice with the CUDA includes / namespace. -extern void reportFatalError(std::string str); -extern void optixLogCallback(unsigned int level, const char* tag, const char* message, void*); // Apparently: this include may only appear in a single source file: #include // Some debug macros -#define CUDA_CHECK(call) \ - { \ - cudaError_t rc = call; \ - if (rc != cudaSuccess) { \ - std::stringstream txt; \ - cudaError_t err = rc; /*cudaGetLastError();*/ \ - txt << "CUDA Error " << cudaGetErrorName(err) \ - << " (" << cudaGetErrorString(err) << ")"; \ - reportFatalError(txt.str()); \ - } \ - } - -#define CUDA_CHECK_NOEXCEPT(call) \ - { \ - call; \ - } - #define OPTIX_CHECK( call ) \ { \ OptixResult res = call; \ @@ -63,29 +40,24 @@ extern void optixLogCallback(unsigned int level, const char* tag, const char* me { \ char buf[1024]; \ sprintf( buf, "Optix call (%s) failed with code %d (line %d)\n", #call, res, __LINE__ ); \ - reportFatalError(std::string(buf)); \ + Falcor::reportFatalError(std::string(buf)); \ } \ } - -#define CUDA_SYNC_CHECK() \ - { \ - cudaDeviceSynchronize(); \ - cudaError_t error = cudaGetLastError(); \ - if( error != cudaSuccess ) \ - { \ - char buf[1024]; \ - sprintf( buf, "error (%s: line %d): %s\n", __FILE__, __LINE__, cudaGetErrorString( error ) ); \ - reportFatalError(std::string(buf)); \ +#define CUDA_CHECK(call) \ + { \ + cudaError_t rc = call; \ + if (rc != cudaSuccess) { \ + std::stringstream txt; \ + cudaError_t err = rc; /*cudaGetLastError();*/ \ + txt << "CUDA Error " << cudaGetErrorName(err) \ + << " (" << cudaGetErrorString(err) << ")"; \ + Falcor::reportFatalError(txt.str()); \ } \ - } - + } -unsigned int initCuda(void) +void optixLogCallback(unsigned int level, const char* tag, const char* message, void*) { - cudaFree(0); - int32_t numDevices; - cudaGetDeviceCount(&numDevices); - return numDevices; + Falcor::logWarning("[Optix][{:2}][{:12}]: {}", level, tag, message); } // This initialization now seems verbose / excessive as CUDA and OptiX initialization @@ -131,84 +103,3 @@ bool initOptix(OptixDeviceContext& optixContext) return true; } - -void CudaBuffer::allocate(size_t size) -{ - if (mpDevicePtr) free(); - mSizeBytes = size; - CUDA_CHECK(cudaMalloc((void**)&mpDevicePtr, mSizeBytes)); -} - -void CudaBuffer::resize(size_t size) -{ - allocate(size); -} - -void CudaBuffer::free(void) -{ - CUDA_CHECK(cudaFree(mpDevicePtr)); - mpDevicePtr = nullptr; - mSizeBytes = 0; -} - -template -bool CudaBuffer::download(T* t, size_t count) -{ - if (!mpDevicePtr) return false; - if (mSizeBytes <= (count * sizeof(T))) return false; - - CUDA_CHECK(cudaMemcpy((void*)t, mpDevicePtr, count * sizeof(T), cudaMemcpyDeviceToHost)); - return true; // might be an error caught by CUDA_CHECK? TODO: process any such error through -} - -template -bool CudaBuffer::upload(const T* t, size_t count) -{ - if (!mpDevicePtr) return false; - if (mSizeBytes <= (count * sizeof(T))) return false; - - CUDA_CHECK(cudaMemcpy(mpDevicePtr, (void*)t, count * sizeof(T), cudaMemcpyHostToDevice)); - return true; // might be an error caught by CUDA_CHECK? TODO: process any such error through -} - -template -void CudaBuffer::allocAndUpload(const std::vector& vt) -{ - allocate(vt.size() * sizeof(T)); - upload((const T*)vt.data(), vt.size()); -} - - -void* getSharedDevicePtr(HANDLE sharedHandle, uint32_t bytes) -{ - // No handle? No pointer! - if (sharedHandle == NULL) return nullptr; - - // Create the descriptor of our shared memory buffer - cudaExternalMemoryHandleDesc externalMemoryHandleDesc; - memset(&externalMemoryHandleDesc, 0, sizeof(externalMemoryHandleDesc)); - externalMemoryHandleDesc.type = cudaExternalMemoryHandleTypeD3D12Resource; - externalMemoryHandleDesc.handle.win32.handle = sharedHandle; - externalMemoryHandleDesc.size = bytes; - externalMemoryHandleDesc.flags = cudaExternalMemoryDedicated; - - // Get a handle to that memory - cudaExternalMemory_t externalMemory; - CUDA_CHECK(cudaImportExternalMemory(&externalMemory, &externalMemoryHandleDesc)); - - // Create a descriptor for our shared buffer pointer - cudaExternalMemoryBufferDesc bufDesc; - memset(&bufDesc, 0, sizeof(bufDesc)); - bufDesc.size = bytes; - - // Actually map the buffer - void* devPtr = nullptr; - CUDA_CHECK(cudaExternalMemoryGetMappedBuffer(&devPtr, externalMemory, &bufDesc)); - return devPtr; -} - -bool freeSharedDevicePtr(void* ptr) -{ - if (!ptr) return false; - return cudaSuccess == cudaFree(ptr); -} diff --git a/Source/RenderPasses/OptixDenoiser/CudaUtils.h b/Source/RenderPasses/OptixDenoiser/OptixUtils.h similarity index 61% rename from Source/RenderPasses/OptixDenoiser/CudaUtils.h rename to Source/RenderPasses/OptixDenoiser/OptixUtils.h index 145eb2fbf..89f2c0ee0 100644 --- a/Source/RenderPasses/OptixDenoiser/CudaUtils.h +++ b/Source/RenderPasses/OptixDenoiser/OptixUtils.h @@ -31,7 +31,6 @@ #include #include -#include // Note: There's some CUDA / Falcor type conflicts. This header includes Falcor-facing functions // for accessing the OptiX denoiser. Including or was a pain @@ -39,42 +38,3 @@ // Initializes OptiX. Returns true on success. bool initOptix(OptixDeviceContext& optixContext); - -// This takes a Windows Handle (e.g., from Falcor::Resource::getSharedApiHandle()) on a resource -// that has been declared "shared" [with Resource::BindFlags::Shared] plus a size of that -// resource in bytes and returns a CUDA device pointer from that resource that can be -// passed into OptiX or CUDA. This pointer is a value returned from -// cudaExternalMemoryGetMappedBuffer(), so should follow its rules (e.g., the docs claim -// you are responsible for calling cudaFree() on this pointer). -void* getSharedDevicePtr(HANDLE sharedHandle, uint32_t bytes); - -// Calls cudaFree() on the provided pointer; -bool freeSharedDevicePtr(void* ptr); - -// A utility class for a GPU/device buffer for use with CUDA. This is essentially stolen -// from Ingo Wald's SIGGRAPH 2019 tutorial code for OptiX 7. -class CudaBuffer -{ -public: - CudaBuffer() {} - - CUdeviceptr getDevicePtr() { return (CUdeviceptr)mpDevicePtr; } - size_t getSize() { return mSizeBytes; } - - void allocate(size_t size); - void resize(size_t size); - void free(); - - template - void allocAndUpload(const std::vector& vt); - - template - bool download(T* t, size_t count); - - template - bool upload(const T* t, size_t count); - -private: - size_t mSizeBytes = 0; - void* mpDevicePtr = nullptr; -}; diff --git a/Source/RenderPasses/PathTracer/GeneratePaths.cs.slang b/Source/RenderPasses/PathTracer/GeneratePaths.cs.slang index d19051c41..1d4f9bd8a 100644 --- a/Source/RenderPasses/PathTracer/GeneratePaths.cs.slang +++ b/Source/RenderPasses/PathTracer/GeneratePaths.cs.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -127,10 +127,11 @@ struct PathGenerator if (hitSurface) { let lod = ExplicitLodTextureSampler(0.f); - ShadingData sd = loadShadingData(hit, cameraRay.origin, cameraRay.dir, true, lod); + ShadingData sd = loadShadingData(hit, cameraRay.origin, cameraRay.dir, lod); // Create material instance and query its properties. - let mi = gScene.materials.getMaterialInstance(sd, lod); + let hints = getMaterialInstanceHints(hit, true /* primary hit */); + let mi = gScene.materials.getMaterialInstance(sd, lod, hints); let bsdfProperties = mi.getProperties(sd); // Check for BSDF lobes that RTXDI can sample. diff --git a/Source/RenderPasses/PathTracer/LoadShadingData.slang b/Source/RenderPasses/PathTracer/LoadShadingData.slang index f7b260e9f..f2ba3f615 100644 --- a/Source/RenderPasses/PathTracer/LoadShadingData.slang +++ b/Source/RenderPasses/PathTracer/LoadShadingData.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -66,11 +66,10 @@ void computeDerivativesAtPrimaryTriangleHit(const TriangleHit hit, uint2 pixel, \param[out] materialID Material ID. \return VertexData struct. */ -VertexData loadVertexData(const HitInfo hit, const float3 rayOrigin, const float3 rayDir, out uint materialID, out bool modifyNormals) +VertexData loadVertexData(const HitInfo hit, const float3 rayOrigin, const float3 rayDir, out uint materialID) { VertexData v = {}; materialID = {}; - modifyNormals = true; #if SCENE_HAS_GEOMETRY_TYPE(GEOMETRY_TYPE_TRIANGLE_MESH) if (hit.getType() == HitType::Triangle) @@ -108,38 +107,34 @@ VertexData loadVertexData(const HitInfo hit, const float3 rayOrigin, const float return v; } -/** Adjust ShadingData - \param[in,out] sd Shading data to update. - \param[in] hit Hit information. - \param[in] isPrimary True if this is the primary hit point. +/** Determine hints to use when creating the material instance. */ -void adjustShadingData(inout ShadingData sd, const HitInfo hit, const bool isPrimary) +uint getMaterialInstanceHints(const HitInfo hit, const bool isPrimary) { + uint hints = 0; if (hit.getType() == HitType::Triangle || hit.getType() == HitType::DisplacedTriangle) { #if GBUFFER_ADJUST_SHADING_NORMALS - if (kAdjustShadingNormals || isPrimary) adjustShadingNormal(sd); + if (kAdjustShadingNormals || isPrimary) hints |= (uint)MaterialInstanceHints::AdjustShadingNormal; #else - if (kAdjustShadingNormals && !isPrimary) adjustShadingNormal(sd); + if (kAdjustShadingNormals && !isPrimary) hints |= (uint)MaterialInstanceHints::AdjustShadingNormal; #endif } + return hints; } /** Setup ShadingData based on loaded vertex/material attributes for a hit point. \param[in] hit Hit information. \param[in] rayOrigin Ray origin. \param[in] rayDir Normalized ray direction. - \param[in] isPrimary True if this is the primary hit point. \param[in] lod Method for computing texture level-of-detail \return ShadingData struct. */ -ShadingData loadShadingData(const HitInfo hit, const float3 rayOrigin, const float3 rayDir, const bool isPrimary, const ITextureSampler lod) +ShadingData loadShadingData(const HitInfo hit, const float3 rayOrigin, const float3 rayDir, const ITextureSampler lod) { uint materialID = {}; - bool modifyNormals = true; - VertexData v = loadVertexData(hit, rayOrigin, rayDir, materialID, modifyNormals); - ShadingData sd = gScene.materials.prepareShadingData(v, materialID, -rayDir, lod, modifyNormals); - adjustShadingData(sd, hit, isPrimary); + VertexData v = loadVertexData(hit, rayOrigin, rayDir, materialID); + ShadingData sd = gScene.materials.prepareShadingData(v, materialID, -rayDir, lod); return sd; } diff --git a/Source/RenderPasses/PathTracer/NRDHelpers.slang b/Source/RenderPasses/PathTracer/NRDHelpers.slang index 8cb9471a3..edbfcceb0 100644 --- a/Source/RenderPasses/PathTracer/NRDHelpers.slang +++ b/Source/RenderPasses/PathTracer/NRDHelpers.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -143,7 +143,7 @@ void setNRDSampleReflectance(NRDBuffers outputNRD, const bool useNRDDemodulation if (path.isDiffusePrimaryHit()) { // Use sum of reflection/transmission albedo as they are denoised together. - const float3 diffuseReflectance = max(kNRDMinReflectance, bsdfProperties.diffuseReflectionAlbedo + bsdfProperties.specularTransmissionAlbedo); + const float3 diffuseReflectance = max(kNRDMinReflectance, bsdfProperties.diffuseReflectionAlbedo + bsdfProperties.diffuseTransmissionAlbedo); outputNRD.sampleReflectance[outSampleIdx] = float4(diffuseReflectance, 1.f); } else if (path.isSpecularPrimaryHit()) diff --git a/Source/RenderPasses/PathTracer/PathTracer.cpp b/Source/RenderPasses/PathTracer/PathTracer.cpp index 096fcffea..e37cb93b8 100644 --- a/Source/RenderPasses/PathTracer/PathTracer.cpp +++ b/Source/RenderPasses/PathTracer/PathTracer.cpp @@ -191,7 +191,7 @@ void PathTracer::registerBindings(pybind11::module& m) misHeuristic.value("PowerTwo", MISHeuristic::PowerTwo); misHeuristic.value("PowerExp", MISHeuristic::PowerExp); - pybind11::class_ pass(m, "PathTracer"); + pybind11::class_> pass(m, "PathTracer"); pass.def_property_readonly("pixelStats", &PathTracer::getPixelStats); pass.def_property("useFixedSeed", @@ -204,13 +204,8 @@ void PathTracer::registerBindings(pybind11::module& m) ); } -PathTracer::SharedPtr PathTracer::create(std::shared_ptr pDevice, const Dictionary& dict) -{ - return SharedPtr(new PathTracer(std::move(pDevice), dict)); -} - -PathTracer::PathTracer(std::shared_ptr pDevice, const Dictionary& dict) - : RenderPass(std::move(pDevice)) +PathTracer::PathTracer(ref pDevice, const Dictionary& dict) + : RenderPass(pDevice) { if (!mpDevice->isShaderModelSupported(Device::ShaderModel::SM6_5)) { @@ -233,8 +228,8 @@ PathTracer::PathTracer(std::shared_ptr pDevice, const Dictionary& dict) // Note: The other programs are lazily created in updatePrograms() because a scene needs to be present when creating them. - mpPixelStats = PixelStats::create(mpDevice); - mpPixelDebug = PixelDebug::create(mpDevice); + mpPixelStats = std::make_unique(mpDevice); + mpPixelDebug = std::make_unique(mpDevice); } void PathTracer::parseDictionary(const Dictionary& dict) @@ -313,7 +308,7 @@ void PathTracer::validateOptions() if (mParams.specularRoughnessThreshold < 0.f || mParams.specularRoughnessThreshold > 1.f) { logWarning("'specularRoughnessThreshold' has invalid value. Clamping to range [0,1]."); - mParams.specularRoughnessThreshold = clamp(mParams.specularRoughnessThreshold, 0.f, 1.f); + mParams.specularRoughnessThreshold = std::clamp(mParams.specularRoughnessThreshold, 0.f, 1.f); } // Static parameters. @@ -350,7 +345,7 @@ void PathTracer::validateOptions() Dictionary PathTracer::getScriptingDictionary() { - if (auto lightBVHSampler = std::dynamic_pointer_cast(mpEmissiveSampler)) + if (auto lightBVHSampler = dynamic_cast(mpEmissiveSampler.get())) { mLightBVHOptions = lightBVHSampler->getOptions(); } @@ -425,13 +420,13 @@ void PathTracer::setFrameDim(const uint2 frameDim) FALCOR_ASSERT(kScreenTileDim.x == (1 << kScreenTileBits.x) && kScreenTileDim.y == (1 << kScreenTileBits.y)); mParams.screenTiles = div_round_up(mParams.frameDim, kScreenTileDim); - if (mParams.frameDim != prevFrameDim || mParams.screenTiles != prevScreenTiles) + if (any(mParams.frameDim != prevFrameDim) || any(mParams.screenTiles != prevScreenTiles)) { mVarsChanged = true; } } -void PathTracer::setScene(RenderContext* pRenderContext, const Scene::SharedPtr& pScene) +void PathTracer::setScene(RenderContext* pRenderContext, const ref& pScene) { mpScene = pScene; mParams.frameCount = 0; @@ -703,7 +698,7 @@ bool PathTracer::onMouseEvent(const MouseEvent& mouseEvent) return mpPixelDebug->onMouseEvent(mouseEvent); } -PathTracer::TracePass::TracePass(std::shared_ptr pDevice, const std::string& name, const std::string& passDefine, const Scene::SharedPtr& pScene, const Program::DefineList& defines, const Program::TypeConformanceList& globalTypeConformances) +PathTracer::TracePass::TracePass(ref pDevice, const std::string& name, const std::string& passDefine, const ref& pScene, const Program::DefineList& defines, const Program::TypeConformanceList& globalTypeConformances) : name(name) , passDefine(passDefine) { @@ -731,11 +726,11 @@ PathTracer::TracePass::TracePass(std::shared_ptr pDevice, const std::str // Specify hit group entry points for every combination of geometry and material type. // The code for each hit group gets specialized for the actual types it's operating on. // First query which material types the scene has. - auto materialTypes = pScene->getMaterialSystem()->getMaterialTypes(); + auto materialTypes = pScene->getMaterialSystem().getMaterialTypes(); for (const auto materialType : materialTypes) { - auto typeConformances = pScene->getMaterialSystem()->getTypeConformances(materialType); + auto typeConformances = pScene->getMaterialSystem().getTypeConformances(materialType); // Add hit groups for triangles. if (auto geometryIDs = pScene->getGeometryIDs(Scene::GeometryType::TriangleMesh, materialType); !geometryIDs.empty()) @@ -769,7 +764,7 @@ PathTracer::TracePass::TracePass(std::shared_ptr pDevice, const std::str pProgram = RtProgram::create(pDevice, desc, defines); } -void PathTracer::TracePass::prepareProgram(std::shared_ptr pDevice, const Program::DefineList& defines) +void PathTracer::TracePass::prepareProgram(ref pDevice, const Program::DefineList& defines) { FALCOR_ASSERT(pProgram != nullptr && pBindingTable != nullptr); pProgram->setDefines(defines); @@ -784,7 +779,7 @@ void PathTracer::updatePrograms() if (mRecompile == false) return; auto defines = mStaticParams.getDefines(*this); - auto globalTypeConformances = mpScene->getMaterialSystem()->getTypeConformances(); + auto globalTypeConformances = mpScene->getMaterialSystem().getTypeConformances(); // Create trace passes lazily. if (!mpTracePass) mpTracePass = std::make_unique(mpDevice, "tracePass", "", mpScene, defines, globalTypeConformances); @@ -821,7 +816,7 @@ void PathTracer::updatePrograms() // Perform program specialization. // Note that we must use set instead of add functions to replace any stale state. - auto prepareProgram = [&](Program::SharedPtr program) + auto prepareProgram = [&](ref program) { program->setDefines(defines); }; @@ -857,7 +852,7 @@ void PathTracer::prepareResources(RenderContext* pRenderContext, const RenderDat if (!mpSampleOffset || mpSampleOffset->getWidth() != mParams.frameDim.x || mpSampleOffset->getHeight() != mParams.frameDim.y) { FALCOR_ASSERT(kScreenTileDim.x * kScreenTileDim.y * kMaxSamplesPerPixel <= (1u << 16)); - mpSampleOffset = Texture::create2D(mpDevice.get(), mParams.frameDim.x, mParams.frameDim.y, ResourceFormat::R16Uint, 1, 1, nullptr, Resource::BindFlags::ShaderResource | Resource::BindFlags::UnorderedAccess); + mpSampleOffset = Texture::create2D(mpDevice, mParams.frameDim.x, mParams.frameDim.y, ResourceFormat::R16Uint, 1, 1, nullptr, Resource::BindFlags::ShaderResource | Resource::BindFlags::UnorderedAccess); mVarsChanged = true; } } @@ -870,24 +865,24 @@ void PathTracer::prepareResources(RenderContext* pRenderContext, const RenderDat { if (!mpSampleColor || mpSampleColor->getElementCount() < sampleCount || mVarsChanged) { - mpSampleColor = Buffer::createStructured(mpDevice.get(), var["sampleColor"], sampleCount, Resource::BindFlags::ShaderResource | Resource::BindFlags::UnorderedAccess, Buffer::CpuAccess::None, nullptr, false); + mpSampleColor = Buffer::createStructured(mpDevice, var["sampleColor"], sampleCount, Resource::BindFlags::ShaderResource | Resource::BindFlags::UnorderedAccess, Buffer::CpuAccess::None, nullptr, false); mVarsChanged = true; } } if (mOutputGuideData && (!mpSampleGuideData || mpSampleGuideData->getElementCount() < sampleCount || mVarsChanged)) { - mpSampleGuideData = Buffer::createStructured(mpDevice.get(), var["sampleGuideData"], sampleCount, Resource::BindFlags::ShaderResource | Resource::BindFlags::UnorderedAccess, Buffer::CpuAccess::None, nullptr, false); + mpSampleGuideData = Buffer::createStructured(mpDevice, var["sampleGuideData"], sampleCount, Resource::BindFlags::ShaderResource | Resource::BindFlags::UnorderedAccess, Buffer::CpuAccess::None, nullptr, false); mVarsChanged = true; } if (mOutputNRDData && (!mpSampleNRDRadiance || mpSampleNRDRadiance->getElementCount() < sampleCount || mVarsChanged)) { - mpSampleNRDRadiance = Buffer::createStructured(mpDevice.get(), var["sampleNRDRadiance"], sampleCount, Resource::BindFlags::ShaderResource | Resource::BindFlags::UnorderedAccess, Buffer::CpuAccess::None, nullptr, false); - mpSampleNRDHitDist = Buffer::createStructured(mpDevice.get(), var["sampleNRDHitDist"], sampleCount, Resource::BindFlags::ShaderResource | Resource::BindFlags::UnorderedAccess, Buffer::CpuAccess::None, nullptr, false); - mpSampleNRDPrimaryHitNeeOnDelta = Buffer::createStructured(mpDevice.get(), var["sampleNRDPrimaryHitNeeOnDelta"], sampleCount, Resource::BindFlags::ShaderResource | Resource::BindFlags::UnorderedAccess, Buffer::CpuAccess::None, nullptr, false); - mpSampleNRDEmission = Buffer::createStructured(mpDevice.get(), var["sampleNRDEmission"], sampleCount, Resource::BindFlags::ShaderResource | Resource::BindFlags::UnorderedAccess, Buffer::CpuAccess::None, nullptr, false); - mpSampleNRDReflectance = Buffer::createStructured(mpDevice.get(), var["sampleNRDReflectance"], sampleCount, Resource::BindFlags::ShaderResource | Resource::BindFlags::UnorderedAccess, Buffer::CpuAccess::None, nullptr, false); + mpSampleNRDRadiance = Buffer::createStructured(mpDevice, var["sampleNRDRadiance"], sampleCount, Resource::BindFlags::ShaderResource | Resource::BindFlags::UnorderedAccess, Buffer::CpuAccess::None, nullptr, false); + mpSampleNRDHitDist = Buffer::createStructured(mpDevice, var["sampleNRDHitDist"], sampleCount, Resource::BindFlags::ShaderResource | Resource::BindFlags::UnorderedAccess, Buffer::CpuAccess::None, nullptr, false); + mpSampleNRDPrimaryHitNeeOnDelta = Buffer::createStructured(mpDevice, var["sampleNRDPrimaryHitNeeOnDelta"], sampleCount, Resource::BindFlags::ShaderResource | Resource::BindFlags::UnorderedAccess, Buffer::CpuAccess::None, nullptr, false); + mpSampleNRDEmission = Buffer::createStructured(mpDevice, var["sampleNRDEmission"], sampleCount, Resource::BindFlags::ShaderResource | Resource::BindFlags::UnorderedAccess, Buffer::CpuAccess::None, nullptr, false); + mpSampleNRDReflectance = Buffer::createStructured(mpDevice, var["sampleNRDReflectance"], sampleCount, Resource::BindFlags::ShaderResource | Resource::BindFlags::UnorderedAccess, Buffer::CpuAccess::None, nullptr, false); mVarsChanged = true; } } @@ -898,7 +893,7 @@ void PathTracer::preparePathTracer(const RenderData& renderData) if (!mpPathTracerBlock || mVarsChanged) { auto reflector = mpReflectTypes->getProgram()->getReflector()->getParameterBlock("pathTracer"); - mpPathTracerBlock = ParameterBlock::create(mpDevice.get(), reflector); + mpPathTracerBlock = ParameterBlock::create(mpDevice, reflector); FALCOR_ASSERT(mpPathTracerBlock); mVarsChanged = true; } @@ -911,7 +906,7 @@ void PathTracer::preparePathTracer(const RenderData& renderData) void PathTracer::resetLighting() { // Retain the options for the emissive sampler. - if (auto lightBVHSampler = std::dynamic_pointer_cast(mpEmissiveSampler)) + if (auto lightBVHSampler = dynamic_cast(mpEmissiveSampler.get())) { mLightBVHOptions = lightBVHSampler->getOptions(); } @@ -959,7 +954,7 @@ bool PathTracer::prepareLighting(RenderContext* pRenderContext) { if (!mpEnvMapSampler) { - mpEnvMapSampler = EnvMapSampler::create(mpDevice, mpScene->getEnvMap()); + mpEnvMapSampler = std::make_unique(mpDevice, mpScene->getEnvMap()); lightingChanged = true; mRecompile = true; } @@ -991,13 +986,13 @@ bool PathTracer::prepareLighting(RenderContext* pRenderContext) switch (mStaticParams.emissiveSampler) { case EmissiveLightSamplerType::Uniform: - mpEmissiveSampler = EmissiveUniformSampler::create(pRenderContext, mpScene); + mpEmissiveSampler = std::make_unique(pRenderContext, mpScene); break; case EmissiveLightSamplerType::LightBVH: - mpEmissiveSampler = LightBVHSampler::create(pRenderContext, mpScene, mLightBVHOptions); + mpEmissiveSampler = std::make_unique(pRenderContext, mpScene, mLightBVHOptions); break; case EmissiveLightSamplerType::Power: - mpEmissiveSampler = EmissivePowerSampler::create(pRenderContext, mpScene); + mpEmissiveSampler = std::make_unique(pRenderContext, mpScene); break; default: throw RuntimeError("Unknown emissive light sampler type"); @@ -1011,7 +1006,7 @@ bool PathTracer::prepareLighting(RenderContext* pRenderContext) if (mpEmissiveSampler) { // Retain the options for the emissive sampler. - if (auto lightBVHSampler = std::dynamic_pointer_cast(mpEmissiveSampler)) + if (auto lightBVHSampler = dynamic_cast(mpEmissiveSampler.get())) { mLightBVHOptions = lightBVHSampler->getOptions(); } @@ -1036,7 +1031,7 @@ void PathTracer::prepareRTXDI(RenderContext* pRenderContext) { if (mStaticParams.useRTXDI) { - if (!mpRTXDI) mpRTXDI = RTXDI::create(mpScene, mRTXDIOptions); + if (!mpRTXDI) mpRTXDI = std::make_unique(mpScene, mRTXDIOptions); // Emit warning if enabled while using spp != 1. if (!mFixedSampleCount || mStaticParams.samplesPerPixel != 1) @@ -1087,14 +1082,14 @@ void PathTracer::setShaderData(const ShaderVar& var, const RenderData& renderDat // Bind runtime data. setNRDData(var["outputNRD"], renderData); - Texture::SharedPtr pViewDir; + ref pViewDir; if (mpScene->getCamera()->getApertureRadius() > 0.f) { pViewDir = renderData.getTexture(kInputViewDir); if (!pViewDir) logWarning("Depth-of-field requires the '{}' input. Expect incorrect rendering.", kInputViewDir); } - Texture::SharedPtr pSampleCount; + ref pSampleCount; if (!mFixedSampleCount) { pSampleCount = renderData.getTexture(kInputSampleCount); @@ -1288,7 +1283,7 @@ void PathTracer::generatePaths(RenderContext* pRenderContext, const RenderData& auto var = mpGeneratePaths->getRootVar()["CB"]["gPathGenerator"]; setShaderData(var, renderData, false); - mpGeneratePaths["gScene"] = mpScene->getParameterBlock(); + mpGeneratePaths->getRootVar()["gScene"] = mpScene->getParameterBlock(); if (mpRTXDI) mpRTXDI->setShaderData(mpGeneratePaths->getRootVar()); diff --git a/Source/RenderPasses/PathTracer/PathTracer.h b/Source/RenderPasses/PathTracer/PathTracer.h index 7c19effe2..3339c5078 100644 --- a/Source/RenderPasses/PathTracer/PathTracer.h +++ b/Source/RenderPasses/PathTracer/PathTracer.h @@ -27,6 +27,7 @@ **************************************************************************/ #pragma once #include "Falcor.h" +#include "RenderGraph/RenderPass.h" #include "RenderGraph/RenderPassHelpers.h" #include "Utils/Debug/PixelDebug.h" #include "Utils/Sampling/SampleGenerator.h" @@ -48,19 +49,19 @@ class PathTracer : public RenderPass public: FALCOR_PLUGIN_CLASS(PathTracer, "PathTracer", "Reference path tracer."); - using SharedPtr = std::shared_ptr; + static ref create(ref pDevice, const Dictionary& dict) { return make_ref(pDevice, dict); } - static SharedPtr create(std::shared_ptr pDevice, const Dictionary& dict); + PathTracer(ref pDevice, const Dictionary& dict); virtual Dictionary getScriptingDictionary() override; virtual RenderPassReflection reflect(const CompileData& compileData) override; - virtual void setScene(RenderContext* pRenderContext, const Scene::SharedPtr& pScene) override; + virtual void setScene(RenderContext* pRenderContext, const ref& pScene) override; virtual void execute(RenderContext* pRenderContext, const RenderData& renderData) override; virtual void renderUI(Gui::Widgets& widget) override; virtual bool onMouseEvent(const MouseEvent& mouseEvent) override; virtual bool onKeyEvent(const KeyboardEvent& keyEvent) override { return false; } - const PixelStats::SharedPtr& getPixelStats() const { return mpPixelStats; } + PixelStats& getPixelStats() { return *mpPixelStats; } static void registerBindings(pybind11::module& m); @@ -69,16 +70,14 @@ class PathTracer : public RenderPass { std::string name; std::string passDefine; - RtProgram::SharedPtr pProgram; - RtBindingTable::SharedPtr pBindingTable; - RtProgramVars::SharedPtr pVars; + ref pProgram; + ref pBindingTable; + ref pVars; - TracePass(std::shared_ptr pDevice, const std::string& name, const std::string& passDefine, const Scene::SharedPtr& pScene, const Program::DefineList& defines, const Program::TypeConformanceList& globalTypeConformances); - void prepareProgram(std::shared_ptr pDevice, const Program::DefineList& defines); + TracePass(ref pDevice, const std::string& name, const std::string& passDefine, const ref& pScene, const Program::DefineList& defines, const Program::TypeConformanceList& globalTypeConformances); + void prepareProgram(ref pDevice, const Program::DefineList& defines); }; - PathTracer(std::shared_ptr pDevice, const Dictionary& dict); - void parseDictionary(const Dictionary& dict); void validateOptions(); void updatePrograms(); @@ -150,15 +149,15 @@ class PathTracer : public RenderPass uint2 mFixedOutputSize = { 512, 512 }; ///< Output size in pixels when 'Fixed' size is selected. // Internal state - Scene::SharedPtr mpScene; ///< The current scene, or nullptr if no scene loaded. - SampleGenerator::SharedPtr mpSampleGenerator; ///< GPU pseudo-random sample generator. - EnvMapSampler::SharedPtr mpEnvMapSampler; ///< Environment map sampler or nullptr if not used. - EmissiveLightSampler::SharedPtr mpEmissiveSampler; ///< Emissive light sampler or nullptr if not used. - RTXDI::SharedPtr mpRTXDI; ///< RTXDI sampler for direct illumination or nullptr if not used. - PixelStats::SharedPtr mpPixelStats; ///< Utility class for collecting pixel stats. - PixelDebug::SharedPtr mpPixelDebug; ///< Utility class for pixel debugging (print in shaders). + ref mpScene; ///< The current scene, or nullptr if no scene loaded. + ref mpSampleGenerator; ///< GPU pseudo-random sample generator. + std::unique_ptr mpEnvMapSampler; ///< Environment map sampler or nullptr if not used. + std::unique_ptr mpEmissiveSampler; ///< Emissive light sampler or nullptr if not used. + std::unique_ptr mpRTXDI; ///< RTXDI sampler for direct illumination or nullptr if not used. + std::unique_ptr mpPixelStats; ///< Utility class for collecting pixel stats. + std::unique_ptr mpPixelDebug; ///< Utility class for pixel debugging (print in shaders). - ParameterBlock::SharedPtr mpPathTracerBlock; ///< Parameter block for the path tracer. + ref mpPathTracerBlock; ///< Parameter block for the path tracer. bool mRecompile = false; ///< Set to true when program specialization has changed. bool mVarsChanged = true; ///< This is set to true whenever the program vars have changed and resources need to be rebound. @@ -169,20 +168,20 @@ class PathTracer : public RenderPass bool mOutputNRDData = false; ///< True if NRD diffuse/specular data should be generated as outputs. bool mOutputNRDAdditionalData = false; ///< True if NRD data from delta and residual paths should be generated as designated outputs rather than being included in specular NRD outputs. - ComputePass::SharedPtr mpGeneratePaths; ///< Fullscreen compute pass generating paths starting at primary hits. - ComputePass::SharedPtr mpResolvePass; ///< Sample resolve pass. - ComputePass::SharedPtr mpReflectTypes; ///< Helper for reflecting structured buffer types. + ref mpGeneratePaths; ///< Fullscreen compute pass generating paths starting at primary hits. + ref mpResolvePass; ///< Sample resolve pass. + ref mpReflectTypes; ///< Helper for reflecting structured buffer types. std::unique_ptr mpTracePass; ///< Main trace pass. std::unique_ptr mpTraceDeltaReflectionPass; ///< Delta reflection trace pass (for NRD). std::unique_ptr mpTraceDeltaTransmissionPass; ///< Delta transmission trace pass (for NRD). - Texture::SharedPtr mpSampleOffset; ///< Output offset into per-sample buffers to where the samples for each pixel are stored (the offset is relative the start of the tile). Only used with non-fixed sample count. - Buffer::SharedPtr mpSampleColor; ///< Compact per-sample color buffer. This is used only if spp > 1. - Buffer::SharedPtr mpSampleGuideData; ///< Compact per-sample denoiser guide data. - Buffer::SharedPtr mpSampleNRDRadiance; ///< Compact per-sample NRD radiance data. - Buffer::SharedPtr mpSampleNRDHitDist; ///< Compact per-sample NRD hit distance data. - Buffer::SharedPtr mpSampleNRDPrimaryHitNeeOnDelta;///< Compact per-sample NEE on delta primary vertices data. - Buffer::SharedPtr mpSampleNRDEmission; ///< Compact per-sample NRD emission data. - Buffer::SharedPtr mpSampleNRDReflectance; ///< Compact per-sample NRD reflectance data. + ref mpSampleOffset; ///< Output offset into per-sample buffers to where the samples for each pixel are stored (the offset is relative the start of the tile). Only used with non-fixed sample count. + ref mpSampleColor; ///< Compact per-sample color buffer. This is used only if spp > 1. + ref mpSampleGuideData; ///< Compact per-sample denoiser guide data. + ref mpSampleNRDRadiance; ///< Compact per-sample NRD radiance data. + ref mpSampleNRDHitDist; ///< Compact per-sample NRD hit distance data. + ref mpSampleNRDPrimaryHitNeeOnDelta;///< Compact per-sample NEE on delta primary vertices data. + ref mpSampleNRDEmission; ///< Compact per-sample NRD emission data. + ref mpSampleNRDReflectance; ///< Compact per-sample NRD reflectance data. }; diff --git a/Source/RenderPasses/PathTracer/PathTracer.slang b/Source/RenderPasses/PathTracer/PathTracer.slang index 361dcacff..fe20a39f0 100644 --- a/Source/RenderPasses/PathTracer/PathTracer.slang +++ b/Source/RenderPasses/PathTracer/PathTracer.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -129,21 +129,21 @@ struct PathTracer { uint index; ///< Vertex index (0 = camera, 1 = primary hit, 2 = secondary hit, etc.). float3 pos; ///< Vertex position. - float3 normal; ///< Shading normal at the vertex (zero if not on a surface). float3 faceNormal; ///< Geometry normal at the vertex (zero if not on a surface). + bool frontFacing; ///< True if path vertex is on the front-facing side (if on a surface). /** Initializes a path vertex. \param[in] index Vertex index. \param[in] pos Vertex position. - \param[in] normal Shading normal. \param[in] faceNormal Geometry normal. + \param[in] frontFacing Front-facing flag. */ - __init(uint index, float3 pos, float3 normal = float3(0.f), float3 faceNormal = float3(0.f)) + __init(uint index, float3 pos, float3 faceNormal = float3(0.f), bool frontFacing = true) { this.index = index; this.pos = pos; - this.normal = normal; this.faceNormal = faceNormal; + this.frontFacing = frontFacing; } /** Get position with offset applied in direction of the geometry normal to avoid self-intersection @@ -155,6 +155,14 @@ struct PathTracer { return computeRayOrigin(pos, dot(faceNormal, rayDir) >= 0 ? faceNormal : -faceNormal); } + + /** Returns the oriented face normal. + \return Face normal flipped to the same side as the view vector. + */ + float3 getOrientedFaceNormal() + { + return frontFacing ? faceNormal : -faceNormal; + } }; /** Set guiding data when background is hit. @@ -429,7 +437,7 @@ struct PathTracer { // For curves tessellated into poly-tubes, we make sure that the origin is on the same side as a scatter ray direction // so there is no self-intersection. - path.origin = sd.posW - sd.N * sd.curveRadius * 2.1f; + path.origin = sd.posW - sd.frame.N * sd.curveRadius * 2.1f; } else { @@ -446,8 +454,8 @@ struct PathTracer } } - // Save the shading normal. This is needed for MIS. - path.normal = sd.N; + // Save the normal used for NEE. This is needed for MIS. + path.normal = sd.getOrientedFaceNormal(); // Mark the path as valid only if it has a non-zero throughput. bool valid = any(path.thp > 0.f); @@ -520,7 +528,7 @@ struct PathTracer /** Generates a light sample on the emissive geometry. \param[in] vertex Path vertex. - \param[in] upperHemisphere True if only upper hemisphere (w.r.t. shading normal) should be considered. + \param[in] upperHemisphere True if only upper hemisphere should be considered. \param[in,out] sg Sample generator. \param[out] ls Struct describing valid samples. \return True if the sample is valid and has nonzero contribution, false otherwise. @@ -531,7 +539,7 @@ struct PathTracer if (!kUseEmissiveLights) return false; TriangleLightSample tls; - if (!emissiveSampler.sampleLight(vertex.pos, vertex.normal, upperHemisphere, sg, tls)) return false; + if (!emissiveSampler.sampleLight(vertex.pos, vertex.getOrientedFaceNormal(), upperHemisphere, sg, tls)) return false; // Setup returned sample. ls.Li = tls.pdf > 0.f ? tls.Le / tls.pdf : float3(0); @@ -635,7 +643,6 @@ struct PathTracer /** 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. - The upper/lower hemisphere is defined as the union of the hemispheres w.r.t. to the shading and face normals. \param[in] vertex Path vertex. \param[in] sampleUpperHemisphere True if the upper hemisphere should be sampled. \param[in] sampleLowerHemisphere True if the lower hemisphere should be sampled. @@ -666,12 +673,9 @@ struct PathTracer if (!valid) return false; // Reject samples in non-requested hemispheres. - float cosTheta = dot(vertex.normal, ls.dir); - // Flip the face normal to point in the same hemisphere as the shading normal. - float3 faceNormal = sign(dot(vertex.normal, vertex.faceNormal)) * vertex.faceNormal; - float cosThetaFace = dot(faceNormal, ls.dir); - if (!sampleUpperHemisphere && (max(cosTheta, cosThetaFace) >= -kMinCosTheta)) return false; - if (!sampleLowerHemisphere && (min(cosTheta, cosThetaFace) <= kMinCosTheta)) return false; + float NdotL = dot(vertex.getOrientedFaceNormal(), ls.dir); + if ((!sampleUpperHemisphere && NdotL >= -kMinCosTheta) || (!sampleLowerHemisphere && NdotL <= kMinCosTheta)) + return false; // Account for light type selection. ls.lightType = lightType; @@ -778,7 +782,7 @@ struct PathTracer let lod = createTextureSampler(path, isPrimaryHit, isTriangleHit); // Load shading data. This is a long latency operation. - ShadingData sd = loadShadingData(path.hit, path.origin, path.dir, isPrimaryHit, lod); + ShadingData sd = loadShadingData(path.hit, path.origin, path.dir, lod); const bool isHairMaterial = kUseHairMaterial && (sd.mtl.getMaterialType() == MaterialType::Hair); // TODO: Decouple geometry from the material. @@ -799,7 +803,8 @@ struct PathTracer logPathVertex(); // Create BSDF instance and query its properties. - const IMaterialInstance mi = gScene.materials.getMaterialInstance(sd, lod); + let hints = getMaterialInstanceHints(path.hit, isPrimaryHit); + const IMaterialInstance mi = gScene.materials.getMaterialInstance(sd, lod, hints); BSDFProperties bsdfProperties = mi.getProperties(sd); // Disable specular lobes if caustics are disabled and path already contains a diffuse vertex. @@ -843,7 +848,7 @@ struct PathTracer TriangleLightHit hit; hit.triangleIndex = gScene.lightCollection.getTriangleIndex(triangleHit.instanceID, triangleHit.primitiveIndex); hit.posW = sd.posW; - hit.normalW = sd.frontFacing ? sd.faceN : -sd.faceN; + hit.normalW = sd.getOrientedFaceNormal(); // Evaluate PDF at the hit, had it been generated with light sampling. // Emissive light samplers have an option to exclusively sample the upper hemisphere. @@ -872,12 +877,12 @@ struct PathTracer if (isCurveHit) { // For curves, we set the new origin at the sphere center. - path.origin = sd.posW - sd.N * sd.curveRadius; + path.origin = sd.posW - sd.frame.N * sd.curveRadius; } else if (isCurvePolyTubeHit) { // For curves tessellated into poly-tubes, we offset the new origin away from the curve center. - path.origin = sd.posW + sd.N * sd.curveRadius * 0.1f; + path.origin = sd.posW + sd.frame.N * sd.curveRadius * 0.1f; } else { @@ -910,7 +915,7 @@ struct PathTracer else { // Setup path vertex. - PathVertex vertex = PathVertex(path.getVertexIndex(), sd.posW, sd.N, sd.faceN); + PathVertex vertex = PathVertex(path.getVertexIndex(), sd.posW, sd.faceN, sd.frontFacing); // Determine if upper/lower hemispheres need to be sampled. bool sampleUpperHemisphere = isCurveHit || isCurvePolyTubeHit || ((lobeTypes & (uint)LobeType::NonDeltaReflection) != 0); @@ -941,9 +946,9 @@ struct PathTracer { // For curves tessellated into poly-tubes, we make sure that the origin is on the same side as light // so there is no self-shadowing (transmission lobe of hair BSDF takes care of that). - if (dot(sd.N, ray.dir) < 0.f) + if (dot(sd.frame.N, ray.dir) < 0.f) { - ray.origin = ray.origin - sd.N * sd.curveRadius * 2.1f; + ray.origin = ray.origin - sd.frame.N * sd.curveRadius * 2.1f; } } diff --git a/Source/RenderPasses/PathTracer/PathTracerNRD.slang b/Source/RenderPasses/PathTracer/PathTracerNRD.slang index 26f61bfd0..948a3778b 100644 --- a/Source/RenderPasses/PathTracer/PathTracerNRD.slang +++ b/Source/RenderPasses/PathTracer/PathTracerNRD.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -56,13 +56,14 @@ extension PathTracer let lod = createTextureSampler(path, isPrimaryHit, isTriangleHit); // Load shading data. This is a long latency operation. - ShadingData sd = loadShadingData(path.hit, path.origin, path.dir, isPrimaryHit, lod); + ShadingData sd = loadShadingData(path.hit, path.origin, path.dir, lod); // Reject false hits in nested dielectrics. if (!handleNestedDielectrics(sd, path)) return; // Create material instance and query its properties. - let mi = gScene.materials.getMaterialInstance(sd, lod); + let hints = getMaterialInstanceHints(path.hit, isPrimaryHit); + let mi = gScene.materials.getMaterialInstance(sd, lod, hints); let bsdfProperties = mi.getProperties(sd); // Query BSDF lobes. @@ -108,7 +109,7 @@ extension PathTracer const float3 reflectance = getMaterialReflectanceForDeltaPaths(materialType, hasDeltaLobes, sd, bsdfProperties); const float primaryHitDist = path.pdf; const float hitDist = float(path.sceneLength) - primaryHitDist; - writeNRDDeltaReflectionGuideBuffers(outputNRD, kUseNRDDemodulation, pixel, reflectance, emission, sd.N, bsdfProperties.roughness, float(path.sceneLength), hitDist); + writeNRDDeltaReflectionGuideBuffers(outputNRD, kUseNRDDemodulation, pixel, reflectance, emission, bsdfProperties.guideNormal, bsdfProperties.roughness, float(path.sceneLength), hitDist); path.terminate(); return; @@ -164,7 +165,7 @@ extension PathTracer const float3 reflectance = getMaterialReflectanceForDeltaPaths(materialType, hasDeltaLobes, sd, bsdfProperties); const float primaryHitDist = path.pdf; const float hitDist = float(path.sceneLength) - primaryHitDist; - writeNRDDeltaReflectionGuideBuffers(outputNRD, kUseNRDDemodulation, pixel, reflectance, emission, sd.N, bsdfProperties.roughness, float(path.sceneLength), hitDist); + writeNRDDeltaReflectionGuideBuffers(outputNRD, kUseNRDDemodulation, pixel, reflectance, emission, bsdfProperties.guideNormal, bsdfProperties.roughness, float(path.sceneLength), hitDist); path.terminate(); return; @@ -192,13 +193,14 @@ extension PathTracer let lod = createTextureSampler(path, isPrimaryHit, isTriangleHit); // Load shading data. This is a long latency operation. - ShadingData sd = loadShadingData(path.hit, path.origin, path.dir, isPrimaryHit, lod); + ShadingData sd = loadShadingData(path.hit, path.origin, path.dir, lod); // Reject false hits in nested dielectrics. if (!handleNestedDielectrics(sd, path)) return; // Create material instance and query its properties. - let mi = gScene.materials.getMaterialInstance(sd, lod); + let hints = getMaterialInstanceHints(path.hit, isPrimaryHit); + let mi = gScene.materials.getMaterialInstance(sd, lod, hints); let bsdfProperties = mi.getProperties(sd); const uint lobeTypes = mi.getLobeTypes(sd); @@ -243,7 +245,7 @@ extension PathTracer if (lastVertex || semiOpaque) { float3 emission = path.L; - writeNRDDeltaTransmissionGuideBuffers(outputNRD, kUseNRDDemodulation, pixel, getMaterialReflectanceForDeltaPaths(materialType, hasDeltaLobes, sd, bsdfProperties), emission, sd.N, bsdfProperties.roughness, float(path.sceneLength), sd.posW); + writeNRDDeltaTransmissionGuideBuffers(outputNRD, kUseNRDDemodulation, pixel, getMaterialReflectanceForDeltaPaths(materialType, hasDeltaLobes, sd, bsdfProperties), emission, bsdfProperties.guideNormal, bsdfProperties.roughness, float(path.sceneLength), sd.posW); path.terminate(); return; @@ -268,7 +270,7 @@ extension PathTracer if (!valid) { float3 emission = path.L; - writeNRDDeltaTransmissionGuideBuffers(outputNRD, kUseNRDDemodulation, pixel, getMaterialReflectanceForDeltaPaths(materialType, hasDeltaLobes, sd, bsdfProperties), emission, sd.N, bsdfProperties.roughness, float(path.sceneLength), sd.posW); + writeNRDDeltaTransmissionGuideBuffers(outputNRD, kUseNRDDemodulation, pixel, getMaterialReflectanceForDeltaPaths(materialType, hasDeltaLobes, sd, bsdfProperties), emission, bsdfProperties.guideNormal, bsdfProperties.roughness, float(path.sceneLength), sd.posW); path.terminate(); return; diff --git a/Source/RenderPasses/PathTracer/ResolvePass.cs.slang b/Source/RenderPasses/PathTracer/ResolvePass.cs.slang index b555be362..670f07898 100644 --- a/Source/RenderPasses/PathTracer/ResolvePass.cs.slang +++ b/Source/RenderPasses/PathTracer/ResolvePass.cs.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -166,7 +166,7 @@ struct ResolvePass break; case NRDPathType::DeltaReflection: deltaReflectionRadiance += demodulatedRadiance; - diffuseRadiance += primaryHitDiffuseReflectance[pixel].rgb > 0.f ? (neeOnDelta / primaryHitDiffuseReflectance[pixel].rgb) : float3(0.f); + diffuseRadiance += select(primaryHitDiffuseReflectance[pixel].rgb > 0.f, neeOnDelta / primaryHitDiffuseReflectance[pixel].rgb, float3(0.f)); break; case NRDPathType::DeltaTransmission: deltaTransmissionRadiance += demodulatedRadiance; diff --git a/Source/RenderPasses/PixelInspectorPass/PixelInspector.cs.slang b/Source/RenderPasses/PixelInspectorPass/PixelInspector.cs.slang index c0fd9c64a..681f7ea39 100644 --- a/Source/RenderPasses/PixelInspectorPass/PixelInspector.cs.slang +++ b/Source/RenderPasses/PixelInspectorPass/PixelInspector.cs.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -104,8 +104,7 @@ bool loadShadingData(const float3 viewDir, const ITextureSampler lod, inout Shad v.coneTexLODValue = 0.f; // Prepare shading data. - // Set 'useNormalMap' parameter to false as normal mapping has already been applied when generating the G-buffer. - sd = gScene.materials.prepareShadingData(v, materialID, viewDir, lod, false); + sd = gScene.materials.prepareShadingData(v, materialID, viewDir, lod); return true; } @@ -134,9 +133,9 @@ void main(uint3 DTid : SV_DispatchThreadID) // Store geometry data. data.posW = sd.posW; - data.normal = sd.N; - data.tangent = sd.T; - data.bitangent = sd.B; + data.normal = sd.frame.N; + data.tangent = sd.frame.T; + data.bitangent = sd.frame.B; data.faceNormal = sd.faceN; data.view = sd.V; data.texCoord = sd.uv; diff --git a/Source/RenderPasses/PixelInspectorPass/PixelInspectorPass.cpp b/Source/RenderPasses/PixelInspectorPass/PixelInspectorPass.cpp index 78974abcf..d48cf68c4 100644 --- a/Source/RenderPasses/PixelInspectorPass/PixelInspectorPass.cpp +++ b/Source/RenderPasses/PixelInspectorPass/PixelInspectorPass.cpp @@ -54,13 +54,8 @@ namespace const char kOutputChannel[] = "gPixelDataBuffer"; } -PixelInspectorPass::SharedPtr PixelInspectorPass::create(std::shared_ptr pDevice, const Dictionary& dict) -{ - return SharedPtr(new PixelInspectorPass(std::move(pDevice))); -} - -PixelInspectorPass::PixelInspectorPass(std::shared_ptr pDevice) - : RenderPass(std::move(pDevice)) +PixelInspectorPass::PixelInspectorPass(ref pDevice, const Dictionary& dict) + : RenderPass(pDevice) { for (auto it : kInputChannels) { @@ -93,11 +88,13 @@ void PixelInspectorPass::execute(RenderContext* pRenderContext, const RenderData if (!mpVars) { mpVars = ComputeVars::create(mpDevice, mpProgram->getReflector()); - mpPixelDataBuffer = Buffer::createStructured(mpDevice.get(), mpProgram.get(), kOutputChannel, 1); + mpPixelDataBuffer = Buffer::createStructured(mpDevice, mpProgram.get(), kOutputChannel, 1); } + auto var = mpVars->getRootVar(); + // Bind the scene. - mpVars["gScene"] = mpScene->getParameterBlock(); + var["gScene"] = mpScene->getParameterBlock(); if (mpScene->getCamera()->getApertureRadius() > 0.f) { @@ -107,37 +104,37 @@ void PixelInspectorPass::execute(RenderContext* pRenderContext, const RenderData const float2 cursorPosition = mUseContinuousPicking ? mCursorPosition : mSelectedCursorPosition; const uint2 resolution = renderData.getDefaultTextureDims(); - mSelectedPixel = glm::min((uint2)(cursorPosition * ((float2)resolution)), resolution - 1u); + mSelectedPixel = min((uint2)(cursorPosition * ((float2)resolution)), resolution - 1u); // Fill in the constant buffer. - mpVars["PerFrameCB"]["gResolution"] = resolution; - mpVars["PerFrameCB"]["gSelectedPixel"] = mSelectedPixel; + var["PerFrameCB"]["gResolution"] = resolution; + var["PerFrameCB"]["gSelectedPixel"] = mSelectedPixel; // Bind all input buffers. for (auto it : kInputChannels) { if (mAvailableInputs[it.name]) { - Texture::SharedPtr pSrc = renderData.getTexture(it.name); - mpVars[it.texname] = pSrc; + ref pSrc = renderData.getTexture(it.name); + var[it.texname] = pSrc; // If the texture has a different resolution, we need to scale the sampling coordinates accordingly. const uint2 srcResolution = uint2(pSrc->getWidth(), pSrc->getHeight()); - const bool needsScaling = mScaleInputsToWindow && srcResolution != resolution; + const bool needsScaling = mScaleInputsToWindow && any(srcResolution != resolution); const uint2 scaledCoord = (uint2)(((float2)(srcResolution * mSelectedPixel)) / ((float2)resolution)); - mpVars["PerFrameCB"][std::string(it.texname) + "Coord"] = needsScaling ? scaledCoord : mSelectedPixel; + var["PerFrameCB"][std::string(it.texname) + "Coord"] = needsScaling ? scaledCoord : mSelectedPixel; - mIsInputInBounds[it.name] = glm::all(glm::lessThanEqual(mSelectedPixel, srcResolution)); + mIsInputInBounds[it.name] = all(mSelectedPixel <= srcResolution); } else { - mpVars->setTexture(it.texname, nullptr); + var[it.texname].setTexture(nullptr); } } // Bind the output buffer. FALCOR_ASSERT(mpPixelDataBuffer); - mpVars[kOutputChannel] = mpPixelDataBuffer; + var[kOutputChannel] = mpPixelDataBuffer; // Run the inspector program. pRenderContext->dispatch(mpState.get(), mpVars.get(), { 1u, 1u, 1u }); @@ -326,7 +323,7 @@ void PixelInspectorPass::renderUI(Gui::Widgets& widget) { auto instanceData = mpScene->getGeometryInstance(pixelData.instanceID); uint32_t matrixID = instanceData.globalMatrixID; - rmcv::mat4 M = mpScene->getAnimationController()->getGlobalMatrices()[matrixID]; + float4x4 M = mpScene->getAnimationController()->getGlobalMatrices()[matrixID]; visGroup.text("Transform:"); visGroup.matrix("##mat", M); @@ -346,7 +343,7 @@ void PixelInspectorPass::renderUI(Gui::Widgets& widget) } } -void PixelInspectorPass::setScene(RenderContext* pRenderContext, const Scene::SharedPtr& pScene) +void PixelInspectorPass::setScene(RenderContext* pRenderContext, const ref& pScene) { mpScene = pScene; mpProgram = nullptr; diff --git a/Source/RenderPasses/PixelInspectorPass/PixelInspectorPass.h b/Source/RenderPasses/PixelInspectorPass/PixelInspectorPass.h index 409361532..485d57674 100644 --- a/Source/RenderPasses/PixelInspectorPass/PixelInspectorPass.h +++ b/Source/RenderPasses/PixelInspectorPass/PixelInspectorPass.h @@ -27,6 +27,7 @@ **************************************************************************/ #pragma once #include "Falcor.h" +#include "RenderGraph/RenderPass.h" using namespace Falcor; @@ -40,28 +41,24 @@ class PixelInspectorPass : public RenderPass "Left-mouse click on a pixel to select it.\n" }); - using SharedPtr = std::shared_ptr; + static ref create(ref pDevice, const Dictionary& dict) { return make_ref(pDevice, dict); } - /** Create a new object - */ - static SharedPtr create(std::shared_ptr pDevice, const Dictionary& dict = {}); + PixelInspectorPass(ref pDevice, const Dictionary& dict); 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 void setScene(RenderContext* pRenderContext, const ref& pScene) override; virtual bool onMouseEvent(const MouseEvent& mouseEvent) override; private: - PixelInspectorPass(std::shared_ptr pDevice); - // Internal state - Scene::SharedPtr mpScene; - ComputeProgram::SharedPtr mpProgram; - ComputeState::SharedPtr mpState; - ComputeVars::SharedPtr mpVars; + ref mpScene; + ref mpProgram; + ref mpState; + ref mpVars; - Buffer::SharedPtr mpPixelDataBuffer; + ref mpPixelDataBuffer; float2 mCursorPosition = float2(0.0f); float2 mSelectedCursorPosition = float2(0.0f); diff --git a/Source/RenderPasses/RTXDIPass/FinalShading.cs.slang b/Source/RenderPasses/RTXDIPass/FinalShading.cs.slang index c1d6d9ead..354e44cf6 100644 --- a/Source/RenderPasses/RTXDIPass/FinalShading.cs.slang +++ b/Source/RenderPasses/RTXDIPass/FinalShading.cs.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -68,7 +68,8 @@ struct FinalShading if (loadShadingData(pixel, frameDim, gScene.camera, vbuffer, lod, sd)) { // Create material instance and query its properties. - let mi = gScene.materials.getMaterialInstance(sd, lod); + let hints = getMaterialInstanceHints(); + let mi = gScene.materials.getMaterialInstance(sd, lod, hints); let bsdfProperties = mi.getProperties(sd); // Get final sample from RTXDI. @@ -113,7 +114,7 @@ struct FinalShading // Demodulate diffuse reflectance (albedo) from diffuse color. diffuseReflectance = bsdfProperties.diffuseReflectionAlbedo; - float3 diffuseFactor = diffuseReflectance <= 0.f ? 0.f : 1.f / diffuseReflectance; + float3 diffuseFactor = select(diffuseReflectance <= 0.f, 0.f, 1.f / diffuseReflectance); diffuseIllumination = diffuse * diffuseFactor; // Demodulate preintegrated specular reflectance from specular color. @@ -121,7 +122,7 @@ struct FinalShading float NdotV = saturate(dot(bsdfProperties.guideNormal, sd.V)); float ggxAlpha = bsdfProperties.roughness * bsdfProperties.roughness; specularReflectance = approxSpecularIntegralGGX(bsdfProperties.specularReflectance, ggxAlpha, NdotV); - float3 specularFactor = specularReflectance <= 0.f ? 0.f : 1.f / specularReflectance; + float3 specularFactor = select(specularReflectance <= 0.f, 0.f, 1.f / specularReflectance); specularIllumination = specular * specularFactor; } else diff --git a/Source/RenderPasses/RTXDIPass/LoadShadingData.slang b/Source/RenderPasses/RTXDIPass/LoadShadingData.slang index 25c7e58ca..f1da39ebc 100644 --- a/Source/RenderPasses/RTXDIPass/LoadShadingData.slang +++ b/Source/RenderPasses/RTXDIPass/LoadShadingData.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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,17 @@ __exported import Scene.HitInfo; import Scene.Material.ShadingUtils; import Utils.Math.MathHelpers; +/** Determine hints to use when creating the material instance. +*/ +uint getMaterialInstanceHints() +{ + uint hints = 0; +#if GBUFFER_ADJUST_SHADING_NORMALS + hints |= (uint)MaterialInstanceHints::AdjustShadingNormal; +#endif + return hints; +} + /** Helper for setting up the ShadingData struct based on loaded data. \param[in] pixel Current pixel coordinates. \param[in] frameDim Frame dimensions in pixel. @@ -58,10 +69,6 @@ bool loadShadingData(const uint2 pixel, const uint2 frameDim, const Camera camer const uint materialID = gScene.getMaterialID(triangleHit.instanceID); sd = gScene.materials.prepareShadingData(v, materialID, -rayDir, lod); - // Adjust shading normals if GBuffer pass has flag enabled. -#if GBUFFER_ADJUST_SHADING_NORMALS - adjustShadingNormal(sd); -#endif valid = true; } diff --git a/Source/RenderPasses/RTXDIPass/PrepareSurfaceData.cs.slang b/Source/RenderPasses/RTXDIPass/PrepareSurfaceData.cs.slang index 2f5a6a5f0..79185d3f0 100644 --- a/Source/RenderPasses/RTXDIPass/PrepareSurfaceData.cs.slang +++ b/Source/RenderPasses/RTXDIPass/PrepareSurfaceData.cs.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -49,7 +49,8 @@ struct PrepareSurfaceData if (isValidSurface) { // Create material instance and query its properties. - let mi = gScene.materials.getMaterialInstance(sd, lod); + let hints = getMaterialInstanceHints(); + let mi = gScene.materials.getMaterialInstance(sd, lod, hints); let bsdfProperties = mi.getProperties(sd); // RTXDI uses a simple material model with only diffuse and specular reflection lobes. diff --git a/Source/RenderPasses/RTXDIPass/RTXDIPass.cpp b/Source/RenderPasses/RTXDIPass/RTXDIPass.cpp index c0cf68d10..3d0556213 100644 --- a/Source/RenderPasses/RTXDIPass/RTXDIPass.cpp +++ b/Source/RenderPasses/RTXDIPass/RTXDIPass.cpp @@ -69,9 +69,19 @@ extern "C" FALCOR_API_EXPORT void registerPlugin(Falcor::PluginRegistry& registr registry.registerClass(); } -RTXDIPass::SharedPtr RTXDIPass::create(std::shared_ptr pDevice, const Dictionary& dict) +RTXDIPass::RTXDIPass(ref pDevice, const Dictionary& dict) + : RenderPass(pDevice) { - return RTXDIPass::SharedPtr(new RTXDIPass(std::move(pDevice), dict)); + parseDictionary(dict); +} + +void RTXDIPass::parseDictionary(const Dictionary& dict) +{ + for (const auto& [key, value] : dict) + { + if (key == kOptions) mOptions = value; + else logWarning("Unknown field '{}' in RTXDIPass dictionary.", key); + } } RenderPassReflection RTXDIPass::reflect(const CompileData& compileData) @@ -123,7 +133,7 @@ void RTXDIPass::execute(RenderContext* pRenderContext, const RenderData& renderD mpRTXDI->endFrame(pRenderContext); } -void RTXDIPass::setScene(RenderContext* pRenderContext, const Scene::SharedPtr& pScene) +void RTXDIPass::setScene(RenderContext* pRenderContext, const ref& pScene) { mpScene = pScene; mpRTXDI = nullptr; @@ -137,28 +147,13 @@ void RTXDIPass::setScene(RenderContext* pRenderContext, const Scene::SharedPtr& logWarning("RTXDIPass: This render pass only supports triangles. Other types of geometry will be ignored."); } - mpRTXDI = RTXDI::create(mpScene, mOptions); + mpRTXDI = std::make_unique(mpScene, mOptions); } } bool RTXDIPass::onMouseEvent(const MouseEvent& mouseEvent) { - return mpRTXDI ? mpRTXDI->getPixelDebug()->onMouseEvent(mouseEvent) : false; -} - -RTXDIPass::RTXDIPass(std::shared_ptr pDevice, const Dictionary& dict) - : RenderPass(std::move(pDevice)) -{ - parseDictionary(dict); -} - -void RTXDIPass::parseDictionary(const Dictionary& dict) -{ - for (const auto& [key, value] : dict) - { - if (key == kOptions) mOptions = value; - else logWarning("Unknown field '{}' in RTXDIPass dictionary.", key); - } + return mpRTXDI ? mpRTXDI->getPixelDebug().onMouseEvent(mouseEvent) : false; } Dictionary RTXDIPass::getScriptingDictionary() @@ -183,7 +178,7 @@ void RTXDIPass::renderUI(Gui::Widgets& widget) } } -void RTXDIPass::prepareSurfaceData(RenderContext* pRenderContext, const Texture::SharedPtr& pVBuffer) +void RTXDIPass::prepareSurfaceData(RenderContext* pRenderContext, const ref& pVBuffer) { FALCOR_ASSERT(mpRTXDI); FALCOR_ASSERT(pVBuffer); @@ -206,18 +201,18 @@ void RTXDIPass::prepareSurfaceData(RenderContext* pRenderContext, const Texture: mpPrepareSurfaceDataPass->addDefine("GBUFFER_ADJUST_SHADING_NORMALS", mGBufferAdjustShadingNormals ? "1" : "0"); - mpPrepareSurfaceDataPass["gScene"] = mpScene->getParameterBlock(); - - auto var = mpPrepareSurfaceDataPass["gPrepareSurfaceData"]; + auto rootVar = mpPrepareSurfaceDataPass->getRootVar(); + rootVar["gScene"] = mpScene->getParameterBlock(); + mpRTXDI->setShaderData(rootVar); + auto var = rootVar["gPrepareSurfaceData"]; var["vbuffer"] = pVBuffer; var["frameDim"] = mFrameDim; - mpRTXDI->setShaderData(mpPrepareSurfaceDataPass->getRootVar()); mpPrepareSurfaceDataPass->execute(pRenderContext, mFrameDim.x, mFrameDim.y); } -void RTXDIPass::finalShading(RenderContext* pRenderContext, const Texture::SharedPtr& pVBuffer, const RenderData& renderData) +void RTXDIPass::finalShading(RenderContext* pRenderContext, const ref& pVBuffer, const RenderData& renderData) { FALCOR_ASSERT(mpRTXDI); FALCOR_ASSERT(pVBuffer); @@ -247,20 +242,19 @@ void RTXDIPass::finalShading(RenderContext* pRenderContext, const Texture::Share // TODO: This should be moved to a more general mechanism using Slang. mpFinalShadingPass->getProgram()->addDefines(getValidResourceDefines(kOutputChannels, renderData)); - mpFinalShadingPass["gScene"] = mpScene->getParameterBlock(); - - auto var = mpFinalShadingPass["gFinalShading"]; + auto rootVar = mpFinalShadingPass->getRootVar(); + rootVar["gScene"] = mpScene->getParameterBlock(); + mpRTXDI->setShaderData(rootVar); + auto var = rootVar["gFinalShading"]; var["vbuffer"] = pVBuffer; var["frameDim"] = mFrameDim; - mpRTXDI->setShaderData(mpFinalShadingPass->getRootVar()); // Bind output channels as UAV buffers. - var = mpFinalShadingPass->getRootVar(); auto bind = [&](const ChannelDesc& channel) { - Texture::SharedPtr pTex = renderData.getTexture(channel.name); - var[channel.texname] = pTex; + ref pTex = renderData.getTexture(channel.name); + rootVar[channel.texname] = pTex; }; for (const auto& channel : kOutputChannels) bind(channel); diff --git a/Source/RenderPasses/RTXDIPass/RTXDIPass.h b/Source/RenderPasses/RTXDIPass/RTXDIPass.h index 86153f62b..050c3080b 100644 --- a/Source/RenderPasses/RTXDIPass/RTXDIPass.h +++ b/Source/RenderPasses/RTXDIPass/RTXDIPass.h @@ -28,6 +28,7 @@ #pragma once #include "Falcor.h" +#include "RenderGraph/RenderPass.h" #include "Rendering/RTXDI/RTXDI.h" using namespace Falcor; @@ -52,38 +53,32 @@ class RTXDIPass : public RenderPass public: FALCOR_PLUGIN_CLASS(RTXDIPass, "RTXDIPass", {"Standalone pass for direct lighting using RTXDI."}) - using SharedPtr = std::shared_ptr; + static ref create(ref pDevice, const Dictionary& dict) { return make_ref(pDevice, dict); } - /** Create a new render pass object. - \param[in] pDevice GPU device. - \param[in] dict Dictionary of serialized parameters. - \return A new object, or an exception is thrown if creation failed. - */ - static SharedPtr create(std::shared_ptr pDevice, const Dictionary& dict); + RTXDIPass(ref pDevice, const Dictionary& dict); 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 renderUI(Gui::Widgets& widget) override; - virtual void setScene(RenderContext* pRenderContext, const Scene::SharedPtr& pScene) override; + virtual void setScene(RenderContext* pRenderContext, const ref& pScene) override; virtual bool onMouseEvent(const MouseEvent& mouseEvent) override; private: - RTXDIPass(std::shared_ptr pDevice, const Dictionary& dict); void parseDictionary(const Dictionary& dict); - void prepareSurfaceData(RenderContext* pRenderContext, const Texture::SharedPtr& pVBuffer); - void finalShading(RenderContext* pRenderContext, const Texture::SharedPtr& pVBuffer, const RenderData& renderData); + void prepareSurfaceData(RenderContext* pRenderContext, const ref& pVBuffer); + void finalShading(RenderContext* pRenderContext, const ref& pVBuffer, const RenderData& renderData); - Scene::SharedPtr mpScene; + ref mpScene; - RTXDI::SharedPtr mpRTXDI; + std::unique_ptr mpRTXDI; RTXDI::Options mOptions; - ComputePass::SharedPtr mpPrepareSurfaceDataPass; - ComputePass::SharedPtr mpFinalShadingPass; + ref mpPrepareSurfaceDataPass; + ref mpFinalShadingPass; uint2 mFrameDim = { 0, 0 }; bool mOptionsChanged = false; diff --git a/Source/RenderPasses/RenderPassTemplate/RenderPassTemplate.cpp b/Source/RenderPasses/RenderPassTemplate/RenderPassTemplate.cpp index 620759118..1f5647415 100644 --- a/Source/RenderPasses/RenderPassTemplate/RenderPassTemplate.cpp +++ b/Source/RenderPasses/RenderPassTemplate/RenderPassTemplate.cpp @@ -32,10 +32,9 @@ extern "C" FALCOR_API_EXPORT void registerPlugin(Falcor::PluginRegistry& registr registry.registerClass(); } -RenderPassTemplate::SharedPtr RenderPassTemplate::create(std::shared_ptr pDevice, const Dictionary& dict) +RenderPassTemplate::RenderPassTemplate(ref pDevice, const Dictionary& dict) + : RenderPass(pDevice) { - SharedPtr pPass = SharedPtr(new RenderPassTemplate(std::move(pDevice))); - return pPass; } Dictionary RenderPassTemplate::getScriptingDictionary() diff --git a/Source/RenderPasses/RenderPassTemplate/RenderPassTemplate.h b/Source/RenderPasses/RenderPassTemplate/RenderPassTemplate.h index c66e4d3f3..69f14929c 100644 --- a/Source/RenderPasses/RenderPassTemplate/RenderPassTemplate.h +++ b/Source/RenderPasses/RenderPassTemplate/RenderPassTemplate.h @@ -27,6 +27,7 @@ **************************************************************************/ #pragma once #include "Falcor.h" +#include "RenderGraph/RenderPass.h" using namespace Falcor; @@ -35,24 +36,18 @@ class RenderPassTemplate : public RenderPass public: FALCOR_PLUGIN_CLASS(RenderPassTemplate, "RenderPassTemplate", "Insert pass description here."); - using SharedPtr = std::shared_ptr; + static ref create(ref pDevice, const Dictionary& dict) { return make_ref(pDevice, dict); } - /** Create a new render pass object. - \param[in] pDevice GPU device. - \param[in] dict Dictionary of serialized parameters. - \return A new object, or an exception is thrown if creation failed. - */ - static SharedPtr create(std::shared_ptr pDevice, const Dictionary& dict); + RenderPassTemplate(ref pDevice, const Dictionary& dict); 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 renderUI(Gui::Widgets& widget) override; - virtual void setScene(RenderContext* pRenderContext, const Scene::SharedPtr& pScene) override {} + virtual void setScene(RenderContext* pRenderContext, const ref& pScene) override {} virtual bool onMouseEvent(const MouseEvent& mouseEvent) override { return false; } virtual bool onKeyEvent(const KeyboardEvent& keyEvent) override { return false; } private: - RenderPassTemplate(std::shared_ptr pDevice) : RenderPass(std::move(pDevice)) {} }; diff --git a/Source/RenderPasses/SDFEditor/Marker2DSet.cpp b/Source/RenderPasses/SDFEditor/Marker2DSet.cpp index 1361b17ed..ffa210194 100644 --- a/Source/RenderPasses/SDFEditor/Marker2DSet.cpp +++ b/Source/RenderPasses/SDFEditor/Marker2DSet.cpp @@ -39,11 +39,6 @@ void Falcor::Marker2DSet::addMarker(const Marker2DDataBlob& newMarker) mDirtyBuffer = true; } -Falcor::Marker2DSet::SharedPtr Falcor::Marker2DSet::create(std::shared_ptr pDevice, uint32_t maxMarkerCount) -{ - return SharedPtr(new Marker2DSet(std::move(pDevice), maxMarkerCount)); -} - void Falcor::Marker2DSet::clear() { mMarkers.clear(); @@ -181,7 +176,7 @@ void Falcor::Marker2DSet::updateBuffer() // Create a new buffer if it does not exist or if the size is too small for the markers. else if (!mpMarkerBuffer || mpMarkerBuffer->getElementCount() < (uint32_t)mMarkers.size()) { - mpMarkerBuffer = Buffer::createStructured(mpDevice.get(), sizeof(Marker2DDataBlob), (uint32_t)mMarkers.size(), ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, mMarkers.data(), false); + mpMarkerBuffer = Buffer::createStructured(mpDevice, sizeof(Marker2DDataBlob), (uint32_t)mMarkers.size(), ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, mMarkers.data(), false); mpMarkerBuffer->setName("Marker2DSet::mpMarkerBuffer"); } // Else update the existing buffer. diff --git a/Source/RenderPasses/SDFEditor/Marker2DSet.h b/Source/RenderPasses/SDFEditor/Marker2DSet.h index de96b5a70..588cf547f 100644 --- a/Source/RenderPasses/SDFEditor/Marker2DSet.h +++ b/Source/RenderPasses/SDFEditor/Marker2DSet.h @@ -39,9 +39,7 @@ namespace Falcor class Marker2DSet { public: - using SharedPtr = std::shared_ptr; - - static SharedPtr create(std::shared_ptr pDevice, uint32_t maxMarkerCount); + Marker2DSet(ref pDevice, uint32_t maxMarkerCount) : mpDevice(pDevice), mMaxMarkerCount(maxMarkerCount) {} /** Resets the marker index to the first position. This will allow the next add-calls to add markers from the beginning again. */ @@ -132,16 +130,13 @@ namespace Falcor /** Get the buffer that holds all markers in this set. */ - Buffer::SharedPtr getBuffer() const { return mpMarkerBuffer; } + ref getBuffer() const { return mpMarkerBuffer; } /** Set shader data. */ void setShaderData(const ShaderVar& var); protected: - - Marker2DSet(std::shared_ptr pDevice, uint32_t maxMarkerCount) : mpDevice(std::move(pDevice)), mMaxMarkerCount(maxMarkerCount) {} - /** Adds a Marker2D object to the buffer. Throws a runtime error when marker count exceeds the maximum marker count. */ void addMarker(const Marker2DDataBlob& newMarker); @@ -151,10 +146,10 @@ namespace Falcor void updateBuffer(); private: - std::shared_ptr mpDevice; + ref mpDevice; uint32_t mMaxMarkerCount; std::vector mMarkers; - Buffer::SharedPtr mpMarkerBuffer; + ref mpMarkerBuffer; bool mDirtyBuffer = false; }; } diff --git a/Source/RenderPasses/SDFEditor/SDFEditor.cpp b/Source/RenderPasses/SDFEditor/SDFEditor.cpp index 44a5cf485..b18c506fc 100644 --- a/Source/RenderPasses/SDFEditor/SDFEditor.cpp +++ b/Source/RenderPasses/SDFEditor/SDFEditor.cpp @@ -28,9 +28,6 @@ #include "SDFEditor.h" #include "RenderGraph/RenderPassHelpers.h" #include "Scene/SDFs/SDF3DPrimitiveFactory.h" -#include -#include -#include namespace { @@ -110,32 +107,26 @@ void SDFEditor::registerBindings(pybind11::module& m) // None at the moment. } -SDFEditor::SharedPtr SDFEditor::create(std::shared_ptr pDevice, const Dictionary& dict) +SDFEditor::SDFEditor(ref pDevice, const Dictionary& dict) + : RenderPass(pDevice) { - SharedPtr pPass = SharedPtr(new SDFEditor(std::move(pDevice), dict)); - return pPass; -} - -SDFEditor::SDFEditor(std::shared_ptr pDevice, const Dictionary& dict) - : RenderPass(std::move(pDevice)) -{ - mpFbo = Fbo::create(mpDevice.get()); + mpFbo = Fbo::create(mpDevice); - mpPickingInfo = Buffer::createStructured(mpDevice.get(), sizeof(SDFPickingInfo), 1, Resource::BindFlags::ShaderResource | Resource::BindFlags::UnorderedAccess, Buffer::CpuAccess::None); - mpPickingInfoReadBack = Buffer::createStructured(mpDevice.get(), sizeof(SDFPickingInfo), 1, Resource::BindFlags::None, Buffer::CpuAccess::Read); - mpReadbackFence = GpuFence::create(mpDevice.get()); + mpPickingInfo = Buffer::createStructured(mpDevice, sizeof(SDFPickingInfo), 1, Resource::BindFlags::ShaderResource | Resource::BindFlags::UnorderedAccess, Buffer::CpuAccess::None); + mpPickingInfoReadBack = Buffer::createStructured(mpDevice, sizeof(SDFPickingInfo), 1, Resource::BindFlags::None, Buffer::CpuAccess::Read); + mpReadbackFence = GpuFence::create(mpDevice); - mUI2D.pMarker2DSet = Marker2DSet::create(mpDevice, 100); - mUI2D.pSelectionWheel = SelectionWheel::create(mUI2D.pMarker2DSet); + mUI2D.pMarker2DSet = std::make_unique(mpDevice, 100); + mUI2D.pSelectionWheel = std::make_unique(*mUI2D.pMarker2DSet); - mpSDFEditingDataBuffer = Buffer::createStructured(mpDevice.get(), sizeof(SDFEditingData), 1); + mpSDFEditingDataBuffer = Buffer::createStructured(mpDevice, sizeof(SDFEditingData), 1); mUI2D.symmetryPlane.normal = float3(1.0f, 0.0f, 0.0f); mUI2D.symmetryPlane.rightVector = float3(0.0f, 0.0f, -1.0f); mUI2D.symmetryPlane.color = float4(1.0f, 0.75f, 0.8f, 0.5f); } -void SDFEditor::setShaderData(const ShaderVar& var, const Texture::SharedPtr& pInputColor, const Texture::SharedPtr& pVBuffer) +void SDFEditor::setShaderData(const ShaderVar& var, const ref& pInputColor, const ref& pVBuffer) { mGPUEditingData.editing = mEditingKeyDown; mGPUEditingData.previewEnabled = mPreviewEnabled; @@ -149,7 +140,7 @@ void SDFEditor::setShaderData(const ShaderVar& var, const Texture::SharedPtr& pI { mGridInstanceCount = mpScene->getSDFGridCount(); // This is safe because the SDF Editor only supports SDFGrid type of SBS for now. std::vector instanceIDs = mpScene->getGeometryInstanceIDsByType(Scene::GeometryType::SDFGrid); - mpGridInstanceIDsBuffer = Buffer::createStructured(mpDevice.get(), sizeof(uint32_t), mGridInstanceCount, ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess, Buffer::CpuAccess::None, instanceIDs.data(), false); + mpGridInstanceIDsBuffer = Buffer::createStructured(mpDevice, sizeof(uint32_t), mGridInstanceCount, ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess, Buffer::CpuAccess::None, instanceIDs.data(), false); } auto rootVar = mpGUIPass->getRootVar(); @@ -173,16 +164,16 @@ void SDFEditor::setShaderData(const ShaderVar& var, const Texture::SharedPtr& pI mUI2D.pMarker2DSet->setShaderData(guiPassVar["markerSet"]); } -void SDFEditor::fetchPreviousVBufferAndZBuffer(RenderContext* pRenderContext, Texture::SharedPtr& pVBuffer, Texture::SharedPtr& pDepth) +void SDFEditor::fetchPreviousVBufferAndZBuffer(RenderContext* pRenderContext, ref& pVBuffer, ref& pDepth) { if (!mpEditingVBuffer || mpEditingVBuffer->getWidth() != pVBuffer->getWidth() || mpEditingVBuffer->getHeight() != pVBuffer->getHeight()) { - mpEditingVBuffer = Texture::create2D(mpDevice.get(), pVBuffer->getWidth(), pVBuffer->getHeight(), pVBuffer->getFormat(), 1, 1); + mpEditingVBuffer = Texture::create2D(mpDevice, pVBuffer->getWidth(), pVBuffer->getHeight(), pVBuffer->getFormat(), 1, 1); } if (!mpEditingLinearZBuffer || mpEditingLinearZBuffer->getWidth() != pDepth->getWidth() || mpEditingLinearZBuffer->getHeight() != pDepth->getHeight()) { - mpEditingLinearZBuffer = Texture::create2D(mpDevice.get(), pDepth->getWidth(), pDepth->getHeight(), ResourceFormat::RG32Float, 1, 1, nullptr, Resource::BindFlags::ShaderResource | Resource::BindFlags::UnorderedAccess); + mpEditingLinearZBuffer = Texture::create2D(mpDevice, pDepth->getWidth(), pDepth->getHeight(), ResourceFormat::RG32Float, 1, 1, nullptr, Resource::BindFlags::ShaderResource | Resource::BindFlags::UnorderedAccess); } pRenderContext->copySubresourceRegion(mpEditingVBuffer.get(), 0, pVBuffer.get(), pVBuffer->getSubresourceIndex(0, 0)); @@ -194,7 +185,7 @@ Dictionary SDFEditor::getScriptingDictionary() return Dictionary(); } -void SDFEditor::setScene(RenderContext* pRenderContext, const Scene::SharedPtr& pScene) +void SDFEditor::setScene(RenderContext* pRenderContext, const ref& pScene) { mpScene = pScene; @@ -235,12 +226,12 @@ void SDFEditor::setScene(RenderContext* pRenderContext, const Scene::SharedPtr& kDefaultTransform); const AnimationController* pAnimationController = mpScene->getAnimationController(); - const rmcv::mat4& transform = pAnimationController->getGlobalMatrices()[instance.globalMatrixID]; + const float4x4& transform = pAnimationController->getGlobalMatrices()[instance.globalMatrixID]; // Update GUI variables mUI2D.bbRenderSettings.selectedInstanceID = mCurrentEdit.instanceID; - mUI2D.gridPlane.position = transform.getCol(3).xyz; - mUI2D.previousGridPlane.position = transform.getCol(3).xyz; + mUI2D.gridPlane.position = transform.getCol(3).xyz(); + mUI2D.previousGridPlane.position = transform.getCol(3).xyz(); updateSymmetryPrimitive(); } @@ -290,32 +281,32 @@ void SDFEditor::updateSymmetryPrimitive() { const AnimationController* pAnimationController = mpScene->getAnimationController(); const GeometryInstanceData& instance = mpScene->getGeometryInstance(mCurrentEdit.instanceID); - rmcv::mat4 invInstanceTransform = rmcv::transpose(pAnimationController->getInvTransposeGlobalMatrices()[instance.globalMatrixID]); + float4x4 invInstanceTransform = transpose(pAnimationController->getInvTransposeGlobalMatrices()[instance.globalMatrixID]); const float3& instanceLocalPrimitivePos = mCurrentEdit.primitive.translation; - float3 instanceLocalPlanePosition = (invInstanceTransform * float4(mUI2D.symmetryPlane.position, 1.0f)).xyz; - float3 instanceLocalPlaneNormal = glm::normalize(invInstanceTransform * float4(mUI2D.symmetryPlane.normal, 0.0f)).xyz; - float3 projInstanceLocalPrimitivePos = (instanceLocalPrimitivePos - glm::dot(instanceLocalPrimitivePos, instanceLocalPlaneNormal) * instanceLocalPlaneNormal); - float3 projInstanceLocalPlanePos = glm::dot(instanceLocalPlanePosition, instanceLocalPlaneNormal) * instanceLocalPlaneNormal; + float3 instanceLocalPlanePosition = transformPoint(invInstanceTransform, mUI2D.symmetryPlane.position); + float3 instanceLocalPlaneNormal = normalize(transformVector(invInstanceTransform, mUI2D.symmetryPlane.normal)); + float3 projInstanceLocalPrimitivePos = (instanceLocalPrimitivePos - dot(instanceLocalPrimitivePos, instanceLocalPlaneNormal) * instanceLocalPlaneNormal); + float3 projInstanceLocalPlanePos = dot(instanceLocalPlanePosition, instanceLocalPlaneNormal) * instanceLocalPlaneNormal; float3 reflectedInstanceLocalPos; - if (glm::length(projInstanceLocalPrimitivePos) > 0.0f) + if (length(projInstanceLocalPrimitivePos) > 0.0f) { - reflectedInstanceLocalPos = glm::reflect(-(instanceLocalPrimitivePos - projInstanceLocalPlanePos), glm::normalize(projInstanceLocalPrimitivePos)); + reflectedInstanceLocalPos = math::reflect(-(instanceLocalPrimitivePos - projInstanceLocalPlanePos), normalize(projInstanceLocalPrimitivePos)); } else { - reflectedInstanceLocalPos = glm::length(instanceLocalPrimitivePos - projInstanceLocalPlanePos) * -instanceLocalPlaneNormal; + reflectedInstanceLocalPos = length(instanceLocalPrimitivePos - projInstanceLocalPlanePos) * -instanceLocalPlaneNormal; } reflectedInstanceLocalPos += projInstanceLocalPlanePos; - rmcv::mat3 symmetricPrimitiveTransform = rmcv::mat3(rmcv::inverse(mCurrentEdit.primitive.invRotationScale)); - symmetricPrimitiveTransform = rmcv::rotate(rmcv::mat4(symmetricPrimitiveTransform), glm::pi(), instanceLocalPlaneNormal); + float3x3 symmetricPrimitiveTransform = float3x3(inverse(mCurrentEdit.primitive.invRotationScale)); + symmetricPrimitiveTransform = math::rotate(float4x4(symmetricPrimitiveTransform), float(M_PI), instanceLocalPlaneNormal); mCurrentEdit.symmetryPrimitive = mCurrentEdit.primitive; mCurrentEdit.symmetryPrimitive.translation = reflectedInstanceLocalPos; - mCurrentEdit.symmetryPrimitive.invRotationScale = rmcv::inverse(rmcv::mat3(symmetricPrimitiveTransform)); + mCurrentEdit.symmetryPrimitive.invRotationScale = inverse(float3x3(symmetricPrimitiveTransform)); } void SDFEditor::setupPrimitiveAndOperation(const float2& center, const float markerSize, SDF3DShapeType editingPrimitive, SDFOperationType editingOperator, const float4& color, const float alpha) @@ -407,7 +398,6 @@ void SDFEditor::rotateGridPlane(const float mouseDiff, const float3& rotationVec const float diagonal = length(float2(mFrameDim)); const float maxAngle = float(M_PI) * 0.05f; float angle; - rmcv::mat4 rotationMatrix; if (fromPreviousMouse) { @@ -420,10 +410,10 @@ void SDFEditor::rotateGridPlane(const float mouseDiff, const float3& rotationVec const float speedFactor = 2.0f * float(M_PI) * 0.5f / diagonal; angle = mouseDiff * speedFactor; } - rotationMatrix = rmcv::rotate(rmcv::mat4(), angle, rotationVector); + float4x4 rotationMatrix = math::rotate(float4x4::identity(), angle, rotationVector); - outNormal = normalize(float3(rotationMatrix * float4(inNormal, 0))); - outRightVector = normalize(float3(rotationMatrix * float4(inRightVector, 0))); + outNormal = normalize(transformVector(rotationMatrix, inNormal)); + outRightVector = normalize(transformVector(rotationMatrix, inRightVector)); } void SDFEditor::translateGridPlane(const float mouseDiff, const float3& translationVector, const float3& inPosition, float3& outPosition) @@ -532,7 +522,7 @@ void SDFEditor::setup2DGUI() static constexpr std::array kOperationTypes = { SDFOperationType::SmoothSubtraction, SDFOperationType::Subtraction, SDFOperationType::Union, SDFOperationType::SmoothUnion }; // Check if the user pressed on the different sectors of the wheel. - if (isMainGUIKeyDown() && !mUI2D.recordStartingMousePos && mUI2D.startMousePosition != mUI2D.currentMousePosition) + if (isMainGUIKeyDown() && !mUI2D.recordStartingMousePos && any(mUI2D.startMousePosition != mUI2D.currentMousePosition)) { uint32_t sectorIndex = UINT32_MAX; if (mUI2D.pSelectionWheel->isMouseOnGroup(mUI2D.currentMousePosition, 0, sectorIndex)) @@ -574,19 +564,19 @@ void SDFEditor::handleActions() const AnimationController* pAnimationController = mpScene->getAnimationController(); const GeometryInstanceData& instance = mpScene->getGeometryInstance(mCurrentEdit.instanceID); - const rmcv::mat4& instanceTransform = pAnimationController->getGlobalMatrices()[instance.globalMatrixID]; + const float4x4& instanceTransform = pAnimationController->getGlobalMatrices()[instance.globalMatrixID]; Ray ray = mpScene->getCamera()->computeRayPinhole(uint2(mUI2D.currentMousePosition), mFrameDim, false); // Rescale grid instance if the grid resolution has changed. float resolutionScalingFactor = mCurrentEdit.pSDFGrid->getResolutionScalingFactor(); if (std::fabs(resolutionScalingFactor - 1.0f) > FLT_EPSILON) { - glm::vec3 scale; - glm::quat rotation; - glm::vec3 translation; - glm::vec3 skew; - glm::vec4 perspective; - rmcv::decompose(instanceTransform, scale, rotation, translation, skew, perspective); + float3 scale; + quatf rotation; + float3 translation; + float3 skew; + float4 perspective; + math::decompose(instanceTransform, scale, rotation, translation, skew, perspective); Transform finalTranform; finalTranform.setTranslation(translation); @@ -605,36 +595,36 @@ void SDFEditor::handleActions() { mInstanceTransformationEdit.scrollTotal = 0.0f; - glm::vec3 scale; - glm::quat rotation; - glm::vec3 translation; - glm::vec3 skew; - glm::vec4 perspective; - rmcv::decompose(instanceTransform, scale, rotation, translation, skew, perspective); + float3 scale; + quatf rotation; + float3 translation; + float3 skew; + float4 perspective; + math::decompose(instanceTransform, scale, rotation, translation, skew, perspective); mInstanceTransformationEdit.startTransform.setTranslation(translation); mInstanceTransformationEdit.startTransform.setRotation(rotation); mInstanceTransformationEdit.startTransform.setScaling(scale); float3 deltaCenter = translation - ray.origin; - float3 planeNormal = -glm::normalize(mpScene->getCamera()->getTarget() - mpScene->getCamera()->getPosition()); + float3 planeNormal = -normalize(mpScene->getCamera()->getTarget() - mpScene->getCamera()->getPosition()); - float startT = glm::dot(deltaCenter, planeNormal) / glm::dot(ray.dir, planeNormal); + float startT = dot(deltaCenter, planeNormal) / dot(ray.dir, planeNormal); mInstanceTransformationEdit.startPlanePos = ray.origin + ray.dir * startT; mInstanceTransformationEdit.startMousePos = mUI2D.currentMousePosition; - float3 arbitraryVectorNotOrthToPlane = glm::abs(planeNormal.z) < glm::epsilon() ? glm::vec3(0.0f, 0.0f, 1.0f) : glm::vec3(1.0f, 0.0f, 0.0f); - mInstanceTransformationEdit.referencePlaneDir = glm::normalize(arbitraryVectorNotOrthToPlane - glm::dot(arbitraryVectorNotOrthToPlane, planeNormal) * planeNormal); + float3 arbitraryVectorNotOrthToPlane = std::abs(planeNormal.z) < FLT_EPSILON ? float3(0.0f, 0.0f, 1.0f) : float3(1.0f, 0.0f, 0.0f); + mInstanceTransformationEdit.referencePlaneDir = normalize(arbitraryVectorNotOrthToPlane - dot(arbitraryVectorNotOrthToPlane, planeNormal) * planeNormal); } // Transform the instance if the mouse was moved or scroll was used. - if ((mUI2D.currentMousePosition != mUI2D.prevMousePosition || mInstanceTransformationEdit.prevScrollTotal != mInstanceTransformationEdit.scrollTotal)) + if ((any(mUI2D.currentMousePosition != mUI2D.prevMousePosition) || mInstanceTransformationEdit.prevScrollTotal != mInstanceTransformationEdit.scrollTotal)) { const float3& startTranslation = mInstanceTransformationEdit.startTransform.getTranslation(); float3 deltaCenter = startTranslation - ray.origin; - float3 planeNormal = -glm::normalize(mpScene->getCamera()->getTarget() - mpScene->getCamera()->getPosition()); + float3 planeNormal = -normalize(mpScene->getCamera()->getTarget() - mpScene->getCamera()->getPosition()); - float t = glm::dot(deltaCenter, planeNormal) / glm::dot(ray.dir, planeNormal); + float t = dot(deltaCenter, planeNormal) / dot(ray.dir, planeNormal); Transform finalTranform = mInstanceTransformationEdit.startTransform; @@ -649,15 +639,15 @@ void SDFEditor::handleActions() { float3 p = ray.origin + ray.dir * t; - float3 startPlaneDir = glm::normalize(mInstanceTransformationEdit.startPlanePos - startTranslation); - float3 currPlaneDir = glm::normalize(p - startTranslation); + float3 startPlaneDir = normalize(mInstanceTransformationEdit.startPlanePos - startTranslation); + float3 currPlaneDir = normalize(p - startTranslation); - float startAngle = glm::atan(glm::dot(planeNormal, glm::cross(startPlaneDir, mInstanceTransformationEdit.referencePlaneDir)), glm::dot(startPlaneDir, mInstanceTransformationEdit.referencePlaneDir)); - float currentAngle = glm::atan(glm::dot(planeNormal, glm::cross(currPlaneDir, mInstanceTransformationEdit.referencePlaneDir)), glm::dot(currPlaneDir, mInstanceTransformationEdit.referencePlaneDir)); + float startAngle = std::atan2(dot(planeNormal, cross(startPlaneDir, mInstanceTransformationEdit.referencePlaneDir)), dot(startPlaneDir, mInstanceTransformationEdit.referencePlaneDir)); + float currentAngle = std::atan2(dot(planeNormal, cross(currPlaneDir, mInstanceTransformationEdit.referencePlaneDir)), dot(currPlaneDir, mInstanceTransformationEdit.referencePlaneDir)); float deltaAngle = startAngle - currentAngle; - float3 localPlaneNormal = (rmcv::inverse(mInstanceTransformationEdit.startTransform.getMatrix()) * float4(planeNormal, 0.0f)).xyz; + float3 localPlaneNormal = normalize(transformVector(inverse(mInstanceTransformationEdit.startTransform.getMatrix()), planeNormal)); - finalTranform.setRotation(glm::rotate(mInstanceTransformationEdit.startTransform.getRotation(), deltaAngle, localPlaneNormal)); + finalTranform.setRotation(mul(mInstanceTransformationEdit.startTransform.getRotation(), math::quatFromAngleAxis(deltaAngle, localPlaneNormal))); } // Handle scaling else if (mInstanceTransformationEdit.state == TransformationState::Scaling) @@ -679,37 +669,37 @@ void SDFEditor::handleActions() { mPrimitiveTransformationEdit.startPrimitive = mCurrentEdit.primitive; - glm::vec3 instanceScale; - glm::quat instanceRotation; - glm::vec3 instanceTranslation; - glm::vec3 dummySkew; - glm::vec4 dummyPerspective; - rmcv::decompose(instanceTransform, instanceScale, instanceRotation, instanceTranslation, dummySkew, dummyPerspective); + float3 instanceScale; + quatf instanceRotation; + float3 instanceTranslation; + float3 dummySkew; + float4 dummyPerspective; + math::decompose(instanceTransform, instanceScale, instanceRotation, instanceTranslation, dummySkew, dummyPerspective); mPrimitiveTransformationEdit.startInstanceTransform.setTranslation(instanceTranslation); mPrimitiveTransformationEdit.startInstanceTransform.setRotation(instanceRotation); mPrimitiveTransformationEdit.startInstanceTransform.setScaling(instanceScale); - glm::vec3 primitiveScale; - glm::quat primitiveRotation; - glm::vec3 dummyTranslation; - rmcv::mat4 primitiveTransform = rmcv::inverse(rmcv::transpose(mCurrentEdit.primitive.invRotationScale)); - rmcv::decompose(primitiveTransform, primitiveScale, primitiveRotation, dummyTranslation, dummySkew, dummyPerspective); + float3 primitiveScale; + quatf primitiveRotation; + float3 dummyTranslation; + float4x4 primitiveTransform = inverse(transpose(mCurrentEdit.primitive.invRotationScale)); + math::decompose(primitiveTransform, primitiveScale, primitiveRotation, dummyTranslation, dummySkew, dummyPerspective); mPrimitiveTransformationEdit.startPrimitiveTransform.setTranslation(mCurrentEdit.primitive.translation); mPrimitiveTransformationEdit.startPrimitiveTransform.setRotation(primitiveRotation); mPrimitiveTransformationEdit.startPrimitiveTransform.setScaling(primitiveScale); - float3 planeOrigin = (mPrimitiveTransformationEdit.startInstanceTransform.getMatrix() * float4(mCurrentEdit.primitive.translation, 1.0f)).xyz; + float3 planeOrigin = transformPoint(mPrimitiveTransformationEdit.startInstanceTransform.getMatrix(), mCurrentEdit.primitive.translation); float3 deltaCenter = planeOrigin - ray.origin; - float3 planeNormal = -glm::normalize(mpScene->getCamera()->getTarget() - mpScene->getCamera()->getPosition()); + float3 planeNormal = -normalize(mpScene->getCamera()->getTarget() - mpScene->getCamera()->getPosition()); - float startT = glm::dot(deltaCenter, planeNormal) / glm::dot(ray.dir, planeNormal); + float startT = dot(deltaCenter, planeNormal) / dot(ray.dir, planeNormal); mPrimitiveTransformationEdit.startPlanePos = ray.origin + ray.dir * startT; mPrimitiveTransformationEdit.startMousePos = mUI2D.currentMousePosition; - float3 arbitraryVectorNotOrthToPlane = glm::abs(planeNormal.z) < glm::epsilon() ? glm::vec3(0.0f, 0.0f, 1.0f) : glm::vec3(1.0f, 0.0f, 0.0f); - mPrimitiveTransformationEdit.referencePlaneDir = glm::normalize(arbitraryVectorNotOrthToPlane - glm::dot(arbitraryVectorNotOrthToPlane, planeNormal) * planeNormal); + float3 arbitraryVectorNotOrthToPlane = std::abs(planeNormal.z) < FLT_EPSILON ? float3(0.0f, 0.0f, 1.0f) : float3(1.0f, 0.0f, 0.0f); + mPrimitiveTransformationEdit.referencePlaneDir = normalize(arbitraryVectorNotOrthToPlane - dot(arbitraryVectorNotOrthToPlane, planeNormal) * planeNormal); } // Update starting position if the state or axis has changed. else if (mPrimitiveTransformationEdit.state != mPrimitiveTransformationEdit.prevState || mPrimitiveTransformationEdit.axis != mPrimitiveTransformationEdit.prevAxis) @@ -721,27 +711,27 @@ void SDFEditor::handleActions() updateSymmetryPrimitive(); } - float3 planeOrigin = (mPrimitiveTransformationEdit.startInstanceTransform.getMatrix() * float4(mCurrentEdit.primitive.translation, 1.0f)).xyz; + float3 planeOrigin = transformPoint(mPrimitiveTransformationEdit.startInstanceTransform.getMatrix(), mCurrentEdit.primitive.translation); float3 deltaCenter = planeOrigin - ray.origin; - float3 planeNormal = -glm::normalize(mpScene->getCamera()->getTarget() - mpScene->getCamera()->getPosition()); + float3 planeNormal = -normalize(mpScene->getCamera()->getTarget() - mpScene->getCamera()->getPosition()); - float startT = glm::dot(deltaCenter, planeNormal) / glm::dot(ray.dir, planeNormal); + float startT = dot(deltaCenter, planeNormal) / dot(ray.dir, planeNormal); mPrimitiveTransformationEdit.startPlanePos = ray.origin + ray.dir * startT; mPrimitiveTransformationEdit.startMousePos = mUI2D.currentMousePosition; - float3 arbitraryVectorNotOrthToPlane = glm::abs(planeNormal.z) < glm::epsilon() ? glm::vec3(0.0f, 0.0f, 1.0f) : glm::vec3(1.0f, 0.0f, 0.0f); - mPrimitiveTransformationEdit.referencePlaneDir = glm::normalize(arbitraryVectorNotOrthToPlane - glm::dot(arbitraryVectorNotOrthToPlane, planeNormal) * planeNormal); + float3 arbitraryVectorNotOrthToPlane = std::abs(planeNormal.z) < FLT_EPSILON ? float3(0.0f, 0.0f, 1.0f) : float3(1.0f, 0.0f, 0.0f); + mPrimitiveTransformationEdit.referencePlaneDir = normalize(arbitraryVectorNotOrthToPlane - dot(arbitraryVectorNotOrthToPlane, planeNormal) * planeNormal); } // Transform the primitive if the mouse has moved. - if (mUI2D.currentMousePosition != mUI2D.prevMousePosition) + if (any(mUI2D.currentMousePosition != mUI2D.prevMousePosition)) { - float3 planeOrigin = (mPrimitiveTransformationEdit.startInstanceTransform.getMatrix() * float4(mPrimitiveTransformationEdit.startPrimitiveTransform.getTranslation(), 1.0f)).xyz; + float3 planeOrigin = transformPoint(mPrimitiveTransformationEdit.startInstanceTransform.getMatrix(), mPrimitiveTransformationEdit.startPrimitiveTransform.getTranslation()); float3 deltaCenter = planeOrigin - ray.origin; - float3 planeNormal = -glm::normalize(mpScene->getCamera()->getTarget() - mpScene->getCamera()->getPosition()); + float3 planeNormal = -normalize(mpScene->getCamera()->getTarget() - mpScene->getCamera()->getPosition()); - float t = glm::dot(deltaCenter, planeNormal) / glm::dot(ray.dir, planeNormal); + float t = dot(deltaCenter, planeNormal) / dot(ray.dir, planeNormal); Transform finalPrimitiveTransform = mPrimitiveTransformationEdit.startPrimitiveTransform; @@ -752,19 +742,19 @@ void SDFEditor::handleActions() float3 startPlaneDir = mPrimitiveTransformationEdit.startPlanePos - planeOrigin; float3 currPlaneDir = p - planeOrigin; - if (!glm::all(glm::equal(startPlaneDir, float3(0.0f))) && !glm::all(glm::equal(currPlaneDir, float3(0.0f)))) + if (!all(startPlaneDir == float3(0.0f)) && !all(currPlaneDir == float3(0.0f))) { - startPlaneDir = glm::normalize(startPlaneDir); - currPlaneDir = glm::normalize(currPlaneDir); + startPlaneDir = normalize(startPlaneDir); + currPlaneDir = normalize(currPlaneDir); - float startAngle = glm::atan(glm::dot(planeNormal, glm::cross(startPlaneDir, mPrimitiveTransformationEdit.referencePlaneDir)), glm::dot(startPlaneDir, mPrimitiveTransformationEdit.referencePlaneDir)); - float currentAngle = glm::atan(glm::dot(planeNormal, glm::cross(currPlaneDir, mPrimitiveTransformationEdit.referencePlaneDir)), glm::dot(currPlaneDir, mPrimitiveTransformationEdit.referencePlaneDir)); + float startAngle = std::atan2(dot(planeNormal, cross(startPlaneDir, mPrimitiveTransformationEdit.referencePlaneDir)), dot(startPlaneDir, mPrimitiveTransformationEdit.referencePlaneDir)); + float currentAngle = std::atan2(dot(planeNormal, cross(currPlaneDir, mPrimitiveTransformationEdit.referencePlaneDir)), dot(currPlaneDir, mPrimitiveTransformationEdit.referencePlaneDir)); float deltaAngle = currentAngle - startAngle; - float3 localPlaneNormal = (rmcv::inverse(mPrimitiveTransformationEdit.startInstanceTransform.getMatrix()) * float4(planeNormal, 0.0f)).xyz; + float3 localPlaneNormal = normalize(transformVector(inverse(mPrimitiveTransformationEdit.startInstanceTransform.getMatrix()), planeNormal)); - finalPrimitiveTransform.setRotation(glm::rotate(mPrimitiveTransformationEdit.startPrimitiveTransform.getRotation(), -deltaAngle, localPlaneNormal)); + finalPrimitiveTransform.setRotation(mul(mPrimitiveTransformationEdit.startPrimitiveTransform.getRotation(), math::quatFromAngleAxis(-deltaAngle, localPlaneNormal))); - mCurrentEdit.primitive.invRotationScale = rmcv::transpose(rmcv::inverse(rmcv::mat3(finalPrimitiveTransform.getMatrix()))); + mCurrentEdit.primitive.invRotationScale = transpose(inverse(float3x3(finalPrimitiveTransform.getMatrix()))); } } else if (mPrimitiveTransformationEdit.state == TransformationState::Scaling) @@ -776,21 +766,21 @@ void SDFEditor::handleActions() scale = std::pow(scale, 100.0f * std::abs(deltaX) / mFrameDim.x); if (mPrimitiveTransformationEdit.axis == SDFEditorAxis::All) { - rmcv::mat3 scaleMatrix(scale); - mCurrentEdit.primitive.invRotationScale = rmcv::transpose(rmcv::inverse(rmcv::mat3(mPrimitiveTransformationEdit.startPrimitiveTransform.getMatrix()) * scaleMatrix)); + float3x3 scaleMatrix = matrixFromDiagonal(float3(scale)); + mCurrentEdit.primitive.invRotationScale = transpose(inverse(mul(float3x3(mPrimitiveTransformationEdit.startPrimitiveTransform.getMatrix()), scaleMatrix))); } else if (mPrimitiveTransformationEdit.axis == SDFEditorAxis::OpSmoothing) { - float finalScaling = glm::clamp(startPrimitive.operationSmoothing * scale, kMinOpSmoothingRadius, kMaxOpSmoothingRadius); + float finalScaling = std::clamp(startPrimitive.operationSmoothing * scale, kMinOpSmoothingRadius, kMaxOpSmoothingRadius); mCurrentEdit.primitive.operationSmoothing = finalScaling; } else { uint32_t axis = uint32_t(mPrimitiveTransformationEdit.axis); - rmcv::mat3 scaleMtx = rmcv::mat3(1.0f); // Identity matrix + float3x3 scaleMtx = float3x3::identity(); scaleMtx[axis][axis] = scale; - mCurrentEdit.primitive.invRotationScale = rmcv::transpose(rmcv::inverse(rmcv::mat3(mPrimitiveTransformationEdit.startPrimitiveTransform.getMatrix()) * scaleMtx)); + mCurrentEdit.primitive.invRotationScale = transpose(inverse(mul(float3x3(mPrimitiveTransformationEdit.startPrimitiveTransform.getMatrix()), scaleMtx))); } } @@ -855,7 +845,7 @@ void SDFEditor::handleUndo() SdfGridID gridID = pair.first; const std::vector& primitiveIDsToRemove = pair.second; - const SDFGrid::SharedPtr& pSDFGrid = mpScene->getSDFGrid(gridID); + const ref& pSDFGrid = mpScene->getSDFGrid(gridID); UndoneSDFEdit undoEdit; undoEdit.gridID = gridID; @@ -877,7 +867,7 @@ void SDFEditor::handleRedo() // Redo - Go through the undone SDF edits and put them back into the mPerformedSDFEdits list. std::unordered_map> primitivesToAddPerSDF; - uint32_t editsToRedoCount = glm::min(primitivesAffectedCount, uint32_t(mUndoneSDFEdits.size())); + uint32_t editsToRedoCount = std::min(primitivesAffectedCount, uint32_t(mUndoneSDFEdits.size())); for (uint32_t i = 0; i < editsToRedoCount; i++) { UndoneSDFEdit undoneEdit = mUndoneSDFEdits.back(); @@ -1051,7 +1041,7 @@ bool SDFEditor::handlePicking(const float2& currentMousePos, float3& localPos) const CameraData& cameraData = mpCamera->getData(); float2 ndc = float2(-1.0f, 1.0f) + float2(2.0f, -2.0f) * (currentMousePos + float2(0.5f, 0.5f)) / float2(mFrameDim); - float3 rayDir = glm::normalize(ndc.x * cameraData.cameraU + ndc.y * cameraData.cameraV + cameraData.cameraW); + float3 rayDir = normalize(ndc.x * cameraData.cameraU + ndc.y * cameraData.cameraV + cameraData.cameraW); float3 iSectPosition; if (mUI2D.gridPlane.active) // Grid is on, so we pick on the grid. @@ -1070,8 +1060,8 @@ bool SDFEditor::handlePicking(const float2& currentMousePos, float3& localPos) const GeometryInstanceData& instance = mpScene->getGeometryInstance(mCurrentEdit.instanceID); const AnimationController* pAnimationController = mpScene->getAnimationController(); - const rmcv::mat4& invTransposeInstanceTransform = pAnimationController->getInvTransposeGlobalMatrices()[instance.globalMatrixID]; - localPos = (rmcv::transpose(invTransposeInstanceTransform) * float4(iSectPosition, 1.0f)).xyz; + const float4x4& invTransposeInstanceTransform = pAnimationController->getInvTransposeGlobalMatrices()[instance.globalMatrixID]; + localPos = transformPoint(transpose(invTransposeInstanceTransform), iSectPosition); return true; } @@ -1087,8 +1077,8 @@ void SDFEditor::execute(RenderContext* pRenderContext, const RenderData& renderD mpFbo->attachColorTarget(pOutput, 0); // Make a copy of the vBuffer and the linear z-buffer before editing. - Texture::SharedPtr pVBuffer = renderData.getTexture(kInputVBuffer); - Texture::SharedPtr pDepth = renderData.getTexture(kInputDepth); + ref pVBuffer = renderData.getTexture(kInputVBuffer); + ref pDepth = renderData.getTexture(kInputDepth); if (!mEditingKeyDown) { fetchPreviousVBufferAndZBuffer(pRenderContext, pVBuffer, pDepth); diff --git a/Source/RenderPasses/SDFEditor/SDFEditor.h b/Source/RenderPasses/SDFEditor/SDFEditor.h index 10f22b3f6..3c0048f9a 100644 --- a/Source/RenderPasses/SDFEditor/SDFEditor.h +++ b/Source/RenderPasses/SDFEditor/SDFEditor.h @@ -27,10 +27,11 @@ **************************************************************************/ #pragma once #include "Falcor.h" +#include "Core/Pass/FullScreenPass.h" +#include "RenderGraph/RenderPass.h" #include "SDFEditorTypes.slang" #include "Marker2DSet.h" #include "SelectionWheel.h" -#include "RenderGraph/BasePasses/FullScreenPass.h" using namespace Falcor; @@ -39,21 +40,16 @@ class SDFEditor : public RenderPass public: FALCOR_PLUGIN_CLASS(SDFEditor, "SDFEditor", "Signed distance function (SDF) editor"); - using SharedPtr = std::shared_ptr; + static ref create(ref pDevice, const Dictionary& dict) { return make_ref(pDevice, dict); } - /** Create a new render pass object. - \param[in] pDevice GPU device. - \param[in] dict Dictionary of serialized parameters. - \return A new object, or an exception is thrown if creation failed. - */ - static SharedPtr create(std::shared_ptr pDevice, const Dictionary& dict); + SDFEditor(ref pDevice, const Dictionary& dict); 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 renderUI(RenderContext* pRenderContext, Gui::Widgets& widget) override; - virtual void setScene(RenderContext* pRenderContext, const Scene::SharedPtr& pScene) override; + virtual void setScene(RenderContext* pRenderContext, const ref& pScene) override; virtual bool onMouseEvent(const MouseEvent& mouseEvent) override; virtual bool onKeyEvent(const KeyboardEvent& keyEvent) override; @@ -96,8 +92,8 @@ class SDFEditor : public RenderPass CpuTimer::TimePoint timeOfReleaseMainGUIKey; bool fadeAwayGUI = false; bool drawCurrentModes = true; - Marker2DSet::SharedPtr pMarker2DSet; - SelectionWheel::SharedPtr pSelectionWheel; + std::unique_ptr pMarker2DSet; + std::unique_ptr pSelectionWheel; float currentBlobbing = 0.0f; SDF3DShapeType currentEditingShape = SDF3DShapeType::Sphere; SDFOperationType currentEditingOperator = SDFOperationType::Union; @@ -113,7 +109,7 @@ class SDFEditor : public RenderPass { uint32_t instanceID; SdfGridID gridID; - SDFGrid::SharedPtr pSDFGrid; + ref pSDFGrid; SDF3DPrimitive primitive; SDF3DPrimitive symmetryPrimitive; uint32_t primitiveID = UINT32_MAX; @@ -133,10 +129,8 @@ class SDFEditor : public RenderPass }; private: - SDFEditor(std::shared_ptr pDevice, const Dictionary& dict); - - void setShaderData(const ShaderVar& var, const Texture::SharedPtr& pInputColor, const Texture::SharedPtr& pVBuffer); - void fetchPreviousVBufferAndZBuffer(RenderContext* pRenderContext, Texture::SharedPtr& pVBuffer, Texture::SharedPtr& pDepth); + void setShaderData(const ShaderVar& var, const ref& pInputColor, const ref& pVBuffer); + void fetchPreviousVBufferAndZBuffer(RenderContext* pRenderContext, ref& pVBuffer, ref& pDepth); // 2D GUI functions. void setup2DGUI(); @@ -171,13 +165,13 @@ class SDFEditor : public RenderPass void bakePrimitives(); private: - Scene::SharedPtr mpScene = nullptr; ///< The current scene. - Camera::SharedPtr mpCamera = nullptr; ///< The camera. - FullScreenPass::SharedPtr mpGUIPass = nullptr; ///< A full screen pass drawing the 2D GUI. - Fbo::SharedPtr mpFbo = nullptr; ///< Frame buffer object. - Texture::SharedPtr mpEditingVBuffer = nullptr; ///< A copy of the VBuffer used while moving/adding a primitive. - Texture::SharedPtr mpEditingLinearZBuffer = nullptr; ///< A copy of the linear Z buffer used while moving/adding a primitive. - Buffer::SharedPtr mpSDFEditingDataBuffer; ///< A buffer that contain current Edit data for GUI visualization. + ref mpScene; ///< The current scene. + ref mpCamera; ///< The camera. + ref mpGUIPass; ///< A full screen pass drawing the 2D GUI. + ref mpFbo; ///< Frame buffer object. + ref mpEditingVBuffer; ///< A copy of the VBuffer used while moving/adding a primitive. + ref mpEditingLinearZBuffer; ///< A copy of the linear Z buffer used while moving/adding a primitive. + ref mpSDFEditingDataBuffer; ///< A buffer that contain current Edit data for GUI visualization. struct { @@ -220,14 +214,14 @@ class SDFEditor : public RenderPass uint2 mFrameDim = { 0, 0 }; UI2D mUI2D; - Buffer::SharedPtr mpPickingInfo; ///< Buffer for reading back picking info from the GPU. - Buffer::SharedPtr mpPickingInfoReadBack; ///< Staging buffer for reading back picking info from the GPU. - GpuFence::SharedPtr mpReadbackFence; ///< GPU fence for synchronizing picking info readback. + ref mpPickingInfo; ///< Buffer for reading back picking info from the GPU. + ref mpPickingInfoReadBack; ///< Staging buffer for reading back picking info from the GPU. + ref mpReadbackFence; ///< GPU fence for synchronizing picking info readback. SDFPickingInfo mPickingInfo; SDFEditingData mGPUEditingData; - Buffer::SharedPtr mpGridInstanceIDsBuffer; + ref mpGridInstanceIDsBuffer; uint32_t mGridInstanceCount = 0; uint32_t mNonBakedPrimitiveCount = 0; diff --git a/Source/RenderPasses/SDFEditor/SelectionWheel.cpp b/Source/RenderPasses/SDFEditor/SelectionWheel.cpp index ba5489f1f..8af8379a2 100644 --- a/Source/RenderPasses/SDFEditor/SelectionWheel.cpp +++ b/Source/RenderPasses/SDFEditor/SelectionWheel.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -29,11 +29,6 @@ using namespace Falcor; -Falcor::SelectionWheel::SharedPtr Falcor::SelectionWheel::create(Marker2DSet::SharedPtr pMarker2DSet) -{ - return SharedPtr(new SelectionWheel(pMarker2DSet)); -} - void Falcor::SelectionWheel::update(const float2& mousePos, const Desc& description) { const float kPI = (float)M_PI; @@ -153,7 +148,7 @@ float2 Falcor::SelectionWheel::getCenterPositionOfSector(uint32_t groupIndex, ui float rotation = getRotationOfSector(groupIndex, sectorIndex); float sectorAngle = getAngleOfSectorInGroup(groupIndex); float angle = rotation + sectorAngle * 0.5f; - float2 dir(glm::cos(angle), glm::sin(angle)); + float2 dir(std::cos(angle), std::sin(angle)); return mDescription.position + dir * (mDescription.minRadius + mDescription.maxRadius) * 0.5f; } @@ -183,7 +178,7 @@ float Falcor::SelectionWheel::getGroupAngle() void Falcor::SelectionWheel::computeMouseAngleAndDirLength(const float2& mousePos, float& mouseAngle, float& dirLength) { float2 dir = mousePos - mDescription.position; - dirLength = glm::length(dir); + dirLength = length(dir); mouseAngle = std::atan2(dir.y, dir.x); if (mouseAngle < 0.f) mouseAngle = (float)M_PI * 2.f + mouseAngle; @@ -202,5 +197,5 @@ void Falcor::SelectionWheel::addCircleSector(float rotation, float angle, const { constexpr float kStartOffset = (float)M_PI / 2.f; float rotaion = kStartOffset - rotation - angle*0.5f - (marginOnBothSides ? 0.f : margin*0.5f); - mpMarker2DSet->addCircleSector(mDescription.position, rotaion, angle - std::fabs(marginOnBothSides ? 2.f * margin : margin), mDescription.minRadius, mDescription.maxRadius, color, borderColor, excludeBorderFlags); + mMarker2DSet.addCircleSector(mDescription.position, rotaion, angle - std::fabs(marginOnBothSides ? 2.f * margin : margin), mDescription.minRadius, mDescription.maxRadius, color, borderColor, excludeBorderFlags); } diff --git a/Source/RenderPasses/SDFEditor/SelectionWheel.h b/Source/RenderPasses/SDFEditor/SelectionWheel.h index 60fe72cd7..1f9c5a4c1 100644 --- a/Source/RenderPasses/SDFEditor/SelectionWheel.h +++ b/Source/RenderPasses/SDFEditor/SelectionWheel.h @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -35,8 +35,7 @@ namespace Falcor class SelectionWheel { public: - using SharedPtr = std::shared_ptr; - static const uint32_t kInvalidIndex = -1; + static constexpr uint32_t kInvalidIndex = -1; struct Desc { @@ -50,7 +49,7 @@ namespace Falcor float borderWidth; ///< Thickness of the border in pixels. }; - static SharedPtr create(Marker2DSet::SharedPtr pMarker2DSet); + SelectionWheel(Marker2DSet& marker2DSet) : mMarker2DSet(marker2DSet) {} void update(const float2& mousePos, const Desc& description); @@ -64,8 +63,6 @@ namespace Falcor float getGroupAngle(); private: - SelectionWheel(Marker2DSet::SharedPtr pMarker2DSet) : mpMarker2DSet(pMarker2DSet) {} - void computeMouseAngleAndDirLength(const float2& mousePos, float& mouseAngle, float& dirLength); void computeGroupAndSectorIndexFromAngle(float mouseAngle, uint32_t& groupIndex, uint32_t& sectorIndex); @@ -81,6 +78,6 @@ namespace Falcor private: Desc mDescription; - Marker2DSet::SharedPtr mpMarker2DSet; + Marker2DSet& mMarker2DSet; }; } diff --git a/Source/RenderPasses/SVGFPass/SVGFPass.cpp b/Source/RenderPasses/SVGFPass/SVGFPass.cpp index 846ead130..99794d2e7 100644 --- a/Source/RenderPasses/SVGFPass/SVGFPass.cpp +++ b/Source/RenderPasses/SVGFPass/SVGFPass.cpp @@ -78,13 +78,8 @@ extern "C" FALCOR_API_EXPORT void registerPlugin(Falcor::PluginRegistry& registr registry.registerClass(); } -SVGFPass::SharedPtr SVGFPass::create(std::shared_ptr pDevice, const Dictionary& dict) -{ - return SharedPtr(new SVGFPass(std::move(pDevice), dict)); -} - -SVGFPass::SVGFPass(std::shared_ptr pDevice, const Dictionary& dict) - : RenderPass(std::move(pDevice)) +SVGFPass::SVGFPass(ref pDevice, const Dictionary& dict) + : RenderPass(pDevice) { for (const auto& [key, value] : dict) { @@ -172,16 +167,16 @@ void SVGFPass::compile(RenderContext* pRenderContext, const CompileData& compile void SVGFPass::execute(RenderContext* pRenderContext, const RenderData& renderData) { - Texture::SharedPtr pAlbedoTexture = renderData.getTexture(kInputBufferAlbedo); - Texture::SharedPtr pColorTexture = renderData.getTexture(kInputBufferColor); - Texture::SharedPtr pEmissionTexture = renderData.getTexture(kInputBufferEmission); - Texture::SharedPtr pWorldPositionTexture = renderData.getTexture(kInputBufferWorldPosition); - Texture::SharedPtr pWorldNormalTexture = renderData.getTexture(kInputBufferWorldNormal); - Texture::SharedPtr pPosNormalFwidthTexture = renderData.getTexture(kInputBufferPosNormalFwidth); - Texture::SharedPtr pLinearZTexture = renderData.getTexture(kInputBufferLinearZ); - Texture::SharedPtr pMotionVectorTexture = renderData.getTexture(kInputBufferMotionVector); + ref pAlbedoTexture = renderData.getTexture(kInputBufferAlbedo); + ref pColorTexture = renderData.getTexture(kInputBufferColor); + ref pEmissionTexture = renderData.getTexture(kInputBufferEmission); + ref pWorldPositionTexture = renderData.getTexture(kInputBufferWorldPosition); + ref pWorldNormalTexture = renderData.getTexture(kInputBufferWorldNormal); + ref pPosNormalFwidthTexture = renderData.getTexture(kInputBufferPosNormalFwidth); + ref pLinearZTexture = renderData.getTexture(kInputBufferLinearZ); + ref pMotionVectorTexture = renderData.getTexture(kInputBufferMotionVector); - Texture::SharedPtr pOutputTexture = renderData.getTexture(kOutputBufferFilteredImage); + ref pOutputTexture = renderData.getTexture(kOutputBufferFilteredImage); FALCOR_ASSERT(mpFilteredIlluminationFbo && mpFilteredIlluminationFbo->getWidth() == pAlbedoTexture->getWidth() && @@ -203,7 +198,7 @@ void SVGFPass::execute(RenderContext* pRenderContext, const RenderData& renderDa // 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 = + ref pPrevLinearZAndNormalTexture = renderData.getTexture(kInternalBufferPreviousLinearZAndNormal); computeReprojection(pRenderContext, pAlbedoTexture, pColorTexture, pEmissionTexture, pMotionVectorTexture, pPosNormalFwidthTexture, @@ -221,7 +216,7 @@ void SVGFPass::execute(RenderContext* pRenderContext, const RenderData& renderDa computeAtrousDecomposition(pRenderContext, pAlbedoTexture); // Compute albedo * filtered illumination and add emission back in. - auto perImageCB = mpFinalModulate["PerImageCB"]; + auto perImageCB = mpFinalModulate->getRootVar()["PerImageCB"]; perImageCB["gAlbedo"] = pAlbedoTexture; perImageCB["gEmission"] = pEmissionTexture; perImageCB["gIllumination"] = mpPingPongFbo[0]->getColorTexture(0); @@ -251,26 +246,26 @@ void SVGFPass::allocateFbos(uint2 dim, RenderContext* pRenderContext) 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(mpDevice.get(), dim.x, dim.y, desc); - mpPrevReprojFbo = Fbo::create2D(mpDevice.get(), dim.x, dim.y, desc); + mpCurReprojFbo = Fbo::create2D(mpDevice, dim.x, dim.y, desc); + mpPrevReprojFbo = Fbo::create2D(mpDevice, 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(mpDevice.get(), dim.x, dim.y, desc); + mpLinearZAndNormalFbo = Fbo::create2D(mpDevice, 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(mpDevice.get(), dim.x, dim.y, desc); - mpPingPongFbo[1] = Fbo::create2D(mpDevice.get(), dim.x, dim.y, desc); - mpFilteredPastFbo = Fbo::create2D(mpDevice.get(), dim.x, dim.y, desc); - mpFilteredIlluminationFbo = Fbo::create2D(mpDevice.get(), dim.x, dim.y, desc); - mpFinalFbo = Fbo::create2D(mpDevice.get(), dim.x, dim.y, desc); + mpPingPongFbo[0] = Fbo::create2D(mpDevice, dim.x, dim.y, desc); + mpPingPongFbo[1] = Fbo::create2D(mpDevice, dim.x, dim.y, desc); + mpFilteredPastFbo = Fbo::create2D(mpDevice, dim.x, dim.y, desc); + mpFilteredIlluminationFbo = Fbo::create2D(mpDevice, dim.x, dim.y, desc); + mpFinalFbo = Fbo::create2D(mpDevice, dim.x, dim.y, desc); } mBuffersNeedClear = true; @@ -296,23 +291,23 @@ void SVGFPass::clearBuffers(RenderContext* pRenderContext, const RenderData& ren // (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) +void SVGFPass::computeLinearZAndNormal(RenderContext* pRenderContext, ref pLinearZTexture, + ref pWorldNormalTexture) { - auto perImageCB = mpPackLinearZAndNormal["PerImageCB"]; + auto perImageCB = mpPackLinearZAndNormal->getRootVar()["PerImageCB"]; perImageCB["gLinearZ"] = pLinearZTexture; perImageCB["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) +void SVGFPass::computeReprojection(RenderContext* pRenderContext, ref pAlbedoTexture, + ref pColorTexture, ref pEmissionTexture, + ref pMotionVectorTexture, + ref pPositionNormalFwidthTexture, + ref pPrevLinearZTexture) { - auto perImageCB = mpReprojection["PerImageCB"]; + auto perImageCB = mpReprojection->getRootVar()["PerImageCB"]; // Setup textures for our reprojection shader pass perImageCB["gMotion"] = pMotionVectorTexture; @@ -335,7 +330,7 @@ void SVGFPass::computeReprojection(RenderContext* pRenderContext, Texture::Share void SVGFPass::computeFilteredMoments(RenderContext* pRenderContext) { - auto perImageCB = mpFilterMoments["PerImageCB"]; + auto perImageCB = mpFilterMoments->getRootVar()["PerImageCB"]; perImageCB["gIllumination"] = mpCurReprojFbo->getColorTexture(0); perImageCB["gHistoryLength"] = mpCurReprojFbo->getColorTexture(2); @@ -348,9 +343,9 @@ void SVGFPass::computeFilteredMoments(RenderContext* pRenderContext) mpFilterMoments->execute(pRenderContext, mpPingPongFbo[0]); } -void SVGFPass::computeAtrousDecomposition(RenderContext* pRenderContext, Texture::SharedPtr pAlbedoTexture) +void SVGFPass::computeAtrousDecomposition(RenderContext* pRenderContext, ref pAlbedoTexture) { - auto perImageCB = mpAtrous["PerImageCB"]; + auto perImageCB = mpAtrous->getRootVar()["PerImageCB"]; perImageCB["gAlbedo"] = pAlbedoTexture; perImageCB["gHistoryLength"] = mpCurReprojFbo->getColorTexture(2); @@ -361,7 +356,7 @@ void SVGFPass::computeAtrousDecomposition(RenderContext* pRenderContext, Texture for (int i = 0; i < mFilterIterations; i++) { - Fbo::SharedPtr curTargetFbo = mpPingPongFbo[1]; + ref curTargetFbo = mpPingPongFbo[1]; perImageCB["gIllumination"] = mpPingPongFbo[0]->getColorTexture(0); perImageCB["gStepSize"] = 1 << i; diff --git a/Source/RenderPasses/SVGFPass/SVGFPass.h b/Source/RenderPasses/SVGFPass/SVGFPass.h index 059cb3bb2..e002f6f2d 100644 --- a/Source/RenderPasses/SVGFPass/SVGFPass.h +++ b/Source/RenderPasses/SVGFPass/SVGFPass.h @@ -27,7 +27,8 @@ **************************************************************************/ #pragma once #include "Falcor.h" -#include "RenderGraph/BasePasses/FullScreenPass.h" +#include "RenderGraph/RenderPass.h" +#include "Core/Pass/FullScreenPass.h" using namespace Falcor; @@ -36,9 +37,9 @@ class SVGFPass : public RenderPass public: FALCOR_PLUGIN_CLASS(SVGFPass, "SVGFPass", "SVGF denoising pass."); - using SharedPtr = std::shared_ptr; + static ref create(ref pDevice, const Dictionary& dict) { return make_ref(pDevice, dict); } - static SharedPtr create(std::shared_ptr pDevice, const Dictionary& dict); + SVGFPass(ref pDevice, const Dictionary& dict); virtual Dictionary getScriptingDictionary() override; virtual RenderPassReflection reflect(const CompileData& compileData) override; @@ -47,21 +48,19 @@ class SVGFPass : public RenderPass virtual void renderUI(Gui::Widgets& widget) override; private: - SVGFPass(std::shared_ptr pDevice, const Dictionary& dict); - bool init(const Dictionary& dict); void allocateFbos(uint2 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 computeLinearZAndNormal(RenderContext* pRenderContext, ref pLinearZTexture, + ref pWorldNormalTexture); + void computeReprojection(RenderContext* pRenderContext, ref pAlbedoTexture, + ref pColorTexture, ref pEmissionTexture, + ref pMotionVectorTexture, + ref pPositionNormalFwidthTexture, + ref pPrevLinearZAndNormalTexture); void computeFilteredMoments(RenderContext* pRenderContext); - void computeAtrousDecomposition(RenderContext* pRenderContext, Texture::SharedPtr pAlbedoTexture); + void computeAtrousDecomposition(RenderContext* pRenderContext, ref pAlbedoTexture); bool mBuffersNeedClear = false; @@ -76,18 +75,18 @@ class SVGFPass : public RenderPass float mMomentsAlpha = 0.2f; // SVGF passes - FullScreenPass::SharedPtr mpPackLinearZAndNormal; - FullScreenPass::SharedPtr mpReprojection; - FullScreenPass::SharedPtr mpFilterMoments; - FullScreenPass::SharedPtr mpAtrous; - FullScreenPass::SharedPtr mpFinalModulate; + ref mpPackLinearZAndNormal; + ref mpReprojection; + ref mpFilterMoments; + ref mpAtrous; + ref 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; + ref mpPingPongFbo[2]; + ref mpLinearZAndNormalFbo; + ref mpFilteredPastFbo; + ref mpCurReprojFbo; + ref mpPrevReprojFbo; + ref mpFilteredIlluminationFbo; + ref mpFinalFbo; }; diff --git a/Source/RenderPasses/SceneDebugger/SceneDebugger.cpp b/Source/RenderPasses/SceneDebugger/SceneDebugger.cpp index 5fc2854e3..997b9da74 100644 --- a/Source/RenderPasses/SceneDebugger/SceneDebugger.cpp +++ b/Source/RenderPasses/SceneDebugger/SceneDebugger.cpp @@ -136,7 +136,7 @@ namespace mode.value("Roughness", SceneDebuggerMode::Roughness); mode.value("FlatShaded", SceneDebuggerMode::FlatShaded); - pybind11::class_ pass(m, "SceneDebugger"); + pybind11::class_> pass(m, "SceneDebugger"); pass.def_property(kMode, &SceneDebugger::getMode, &SceneDebugger::setMode); } } @@ -147,13 +147,8 @@ extern "C" FALCOR_API_EXPORT void registerPlugin(Falcor::PluginRegistry& registr Falcor::ScriptBindings::registerBinding(registerBindings); } -SceneDebugger::SharedPtr SceneDebugger::create(std::shared_ptr pDevice, const Dictionary& dict) -{ - return SharedPtr(new SceneDebugger(std::move(pDevice), dict)); -} - -SceneDebugger::SceneDebugger(std::shared_ptr pDevice, const Dictionary& dict) - : RenderPass(std::move(pDevice)) +SceneDebugger::SceneDebugger(ref pDevice, const Dictionary& dict) + : RenderPass(pDevice) { if (!mpDevice->isFeatureSupported(Device::SupportedFeatures::RaytracingTier1_1)) { @@ -168,7 +163,7 @@ SceneDebugger::SceneDebugger(std::shared_ptr pDevice, const Dictionary& else logWarning("Unknown field '{}' in a SceneDebugger dictionary.", key); } - mpFence = GpuFence::create(mpDevice.get()); + mpFence = GpuFence::create(mpDevice); } Dictionary SceneDebugger::getScriptingDictionary() @@ -192,7 +187,7 @@ void SceneDebugger::compile(RenderContext* pRenderContext, const CompileData& co mParams.frameDim = compileData.defaultTexDims; } -void SceneDebugger::setScene(RenderContext* pRenderContext, const Scene::SharedPtr& pScene) +void SceneDebugger::setScene(RenderContext* pRenderContext, const ref& pScene) { mpScene = pScene; mpMeshToBlasID = nullptr; @@ -212,7 +207,7 @@ void SceneDebugger::setScene(RenderContext* pRenderContext, const Scene::SharedP auto blasIDs = mpScene->getMeshBlasIDs(); if (!blasIDs.empty()) { - mpMeshToBlasID = Buffer::createStructured(mpDevice.get(), sizeof(uint32_t), (uint32_t)blasIDs.size(), ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, blasIDs.data(), false); + mpMeshToBlasID = Buffer::createStructured(mpDevice, sizeof(uint32_t), (uint32_t)blasIDs.size(), ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, blasIDs.data(), false); } // Create instance metadata. @@ -222,8 +217,8 @@ void SceneDebugger::setScene(RenderContext* pRenderContext, const Scene::SharedP auto var = mpDebugPass->getRootVar()["CB"]["gSceneDebugger"]; if (!mpPixelData) { - mpPixelData = Buffer::createStructured(mpDevice.get(), var["pixelData"], 1, ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess, Buffer::CpuAccess::None, nullptr, false); - mpPixelDataStaging = Buffer::createStructured(mpDevice.get(), var["pixelData"], 1, ResourceBindFlags::None, Buffer::CpuAccess::Read, nullptr, false); + mpPixelData = Buffer::createStructured(mpDevice, var["pixelData"], 1, ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess, Buffer::CpuAccess::None, nullptr, false); + mpPixelDataStaging = Buffer::createStructured(mpDevice, var["pixelData"], 1, ResourceBindFlags::None, Buffer::CpuAccess::Read, nullptr, false); } var["pixelData"] = mpPixelData; var["meshToBlasID"] = mpMeshToBlasID; @@ -357,11 +352,13 @@ void SceneDebugger::renderPixelDataUI(Gui::Widgets& widget) // Print the list of scene graph nodes affecting this mesh instance. std::vector nodes; - NodeID nodeID{ instance.globalMatrixID }; - while (nodeID != NodeID::Invalid()) { - nodes.push_back(nodeID); - nodeID = mpScene->getParentNodeID(nodeID); + NodeID nodeID{ instance.globalMatrixID }; + while (nodeID != NodeID::Invalid()) + { + nodes.push_back(nodeID); + nodeID = mpScene->getParentNodeID(nodeID); + } } FALCOR_ASSERT(!nodes.empty()); @@ -370,7 +367,7 @@ void SceneDebugger::renderPixelDataUI(Gui::Widgets& widget) for (auto it = nodes.rbegin(); it != nodes.rend(); it++) { auto nodeID = *it; - rmcv::mat4 mat = localMatrices[nodeID.get()]; + float4x4 mat = localMatrices[nodeID.get()]; if (auto nodeGroup = widget.group("ID " + to_string(nodeID)); nodeGroup.open()) { g.matrix("", mat); @@ -416,7 +413,7 @@ void SceneDebugger::renderPixelDataUI(Gui::Widgets& widget) // Show SDF grid details. if (auto g = widget.group("SDF grid info"); g.open()) { - const SDFGrid::SharedPtr& pSDFGrid = mpScene->getSDFGrid(SdfGridID{ data.geometryID }); + const ref& pSDFGrid = mpScene->getSDFGrid(SdfGridID{ data.geometryID }); std::string text; text += fmt::format("gridWidth: {}\n", pSDFGrid->getGridWidth()); g.text(text); @@ -483,7 +480,7 @@ bool SceneDebugger::onMouseEvent(const MouseEvent& mouseEvent) if (mouseEvent.type == MouseEvent::Type::ButtonDown && mouseEvent.button == Input::MouseButton::Left) { float2 cursorPos = mouseEvent.pos * (float2)mParams.frameDim; - mParams.selectedPixel = (uint2)glm::clamp(cursorPos, float2(0.f), float2(mParams.frameDim.x - 1, mParams.frameDim.y - 1)); + mParams.selectedPixel = (uint2)clamp(cursorPos, float2(0.f), float2(mParams.frameDim.x - 1, mParams.frameDim.y - 1)); } return false; @@ -523,5 +520,5 @@ void SceneDebugger::initInstanceInfo() } // Create GPU buffer. - mpInstanceInfo = Buffer::createStructured(mpDevice.get(), sizeof(InstanceInfo), (uint32_t)instanceInfo.size(), ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, instanceInfo.data(), false); + mpInstanceInfo = Buffer::createStructured(mpDevice, sizeof(InstanceInfo), (uint32_t)instanceInfo.size(), ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, instanceInfo.data(), false); } diff --git a/Source/RenderPasses/SceneDebugger/SceneDebugger.cs.slang b/Source/RenderPasses/SceneDebugger/SceneDebugger.cs.slang index 785e080f9..a85fa7c33 100644 --- a/Source/RenderPasses/SceneDebugger/SceneDebugger.cs.slang +++ b/Source/RenderPasses/SceneDebugger/SceneDebugger.cs.slang @@ -198,9 +198,9 @@ struct SceneDebugger // ShadingData d.posW = sd.posW; d.V = sd.V; - d.N = sd.N; - d.T = sd.T; - d.B = sd.B; + d.N = sd.frame.N; + d.T = sd.frame.T; + d.B = sd.frame.B; d.uv = sd.uv; d.faceN = sd.faceN; d.tangentW = sd.tangentW; @@ -246,11 +246,11 @@ struct SceneDebugger case SceneDebuggerMode::FaceNormal: return remapVector(sd.faceN); case SceneDebuggerMode::ShadingNormal: - return remapVector(sd.N); + return remapVector(sd.frame.N); case SceneDebuggerMode::ShadingTangent: - return remapVector(sd.T); + return remapVector(sd.frame.T); case SceneDebuggerMode::ShadingBitangent: - return remapVector(sd.B); + return remapVector(sd.frame.B); case SceneDebuggerMode::FrontFacingFlag: { float v = 0.75f * luminance(abs(sd.faceN)) + 0.25f; @@ -259,7 +259,7 @@ struct SceneDebugger case SceneDebuggerMode::BackfacingShadingNormal: { float v = 0.75f * luminance(abs(sd.faceN)) + 0.25f; - bool backFacing = dot(sd.N, sd.V) <= 0.f; + bool backFacing = dot(sd.frame.N, sd.V) <= 0.f; return backFacing ? float3(z, z, 0) : float3(v, v, v); } case SceneDebuggerMode::TexCoords: diff --git a/Source/RenderPasses/SceneDebugger/SceneDebugger.h b/Source/RenderPasses/SceneDebugger/SceneDebugger.h index 31d5b6fb3..f4a19ecda 100644 --- a/Source/RenderPasses/SceneDebugger/SceneDebugger.h +++ b/Source/RenderPasses/SceneDebugger/SceneDebugger.h @@ -27,6 +27,7 @@ **************************************************************************/ #pragma once #include "Falcor.h" +#include "RenderGraph/RenderPass.h" #include "Scene/HitInfoType.slang" #include "SharedTypes.slang" @@ -41,14 +42,14 @@ class SceneDebugger : public RenderPass public: FALCOR_PLUGIN_CLASS(SceneDebugger, "SceneDebugger", "Scene debugger for identifying asset issues."); - using SharedPtr = std::shared_ptr; + static ref create(ref pDevice, const Dictionary& dict) { return make_ref(pDevice, dict); } - static SharedPtr create(std::shared_ptr pDevice, const Dictionary& dict); + SceneDebugger(ref pDevice, const Dictionary& dict); Dictionary getScriptingDictionary() override; RenderPassReflection reflect(const CompileData& compileData) override; void compile(RenderContext* pRenderContext, const CompileData& compileData) override; - void setScene(RenderContext* pRenderContext, const Scene::SharedPtr& pScene) override; + void setScene(RenderContext* pRenderContext, const ref& pScene) override; void execute(RenderContext* pRenderContext, const RenderData& renderData) override; void renderUI(Gui::Widgets& widget) override; bool onMouseEvent(const MouseEvent& mouseEvent) override; @@ -59,18 +60,17 @@ class SceneDebugger : public RenderPass void setMode(SceneDebuggerMode mode) { mParams.mode = (uint32_t)mode; } private: - SceneDebugger(std::shared_ptr pDevice, const Dictionary& dict); void renderPixelDataUI(Gui::Widgets& widget); void initInstanceInfo(); // Internal state - Scene::SharedPtr mpScene; + ref mpScene; SceneDebuggerParams mParams; - ComputePass::SharedPtr mpDebugPass; - GpuFence::SharedPtr mpFence; - Buffer::SharedPtr mpPixelData; ///< Buffer for recording pixel data at the selected pixel. - Buffer::SharedPtr mpPixelDataStaging; ///< Readback buffer. - Buffer::SharedPtr mpMeshToBlasID; - Buffer::SharedPtr mpInstanceInfo; + ref mpDebugPass; + ref mpFence; + ref mpPixelData; ///< Buffer for recording pixel data at the selected pixel. + ref mpPixelDataStaging; ///< Readback buffer. + ref mpMeshToBlasID; + ref mpInstanceInfo; bool mPixelDataAvailable = false; }; diff --git a/Source/RenderPasses/SceneDebugger/SharedTypes.slang b/Source/RenderPasses/SceneDebugger/SharedTypes.slang index aad1660ab..163cef696 100644 --- a/Source/RenderPasses/SceneDebugger/SharedTypes.slang +++ b/Source/RenderPasses/SceneDebugger/SharedTypes.slang @@ -71,7 +71,7 @@ struct SceneDebuggerParams struct PixelData { - static const uint kInvalidID = 0xffffffff; + static constexpr uint kInvalidID = 0xffffffff; // Geometry uint hitType; diff --git a/Source/RenderPasses/SimplePostFX/SimplePostFX.cpp b/Source/RenderPasses/SimplePostFX/SimplePostFX.cpp index f3e183ba7..facdf15d9 100644 --- a/Source/RenderPasses/SimplePostFX/SimplePostFX.cpp +++ b/Source/RenderPasses/SimplePostFX/SimplePostFX.cpp @@ -56,7 +56,7 @@ namespace static void regSimplePostFX(pybind11::module& m) { - pybind11::class_ pass(m, "SimplePostFX"); + pybind11::class_> pass(m, "SimplePostFX"); pass.def_property(kEnabled, &SimplePostFX::getEnabled, &SimplePostFX::setEnabled); pass.def_property(kWipe, &SimplePostFX::getWipe, &SimplePostFX::setWipe); pass.def_property(kBloomAmount, &SimplePostFX::getBloomAmount, &SimplePostFX::setBloomAmount); @@ -80,36 +80,8 @@ extern "C" FALCOR_API_EXPORT void registerPlugin(Falcor::PluginRegistry& registr ScriptBindings::registerBinding(regSimplePostFX); } -SimplePostFX::SharedPtr SimplePostFX::create(std::shared_ptr pDevice, const Dictionary& dict) -{ - return SharedPtr(new SimplePostFX(std::move(pDevice), dict)); -} - -Dictionary SimplePostFX::getScriptingDictionary() -{ - Dictionary dict; - dict[kEnabled] = getEnabled(); - dict[kOutputSize] = mOutputSizeSelection; - if (mOutputSizeSelection == RenderPassHelpers::IOSize::Fixed) dict[kFixedOutputSize] = mFixedOutputSize; - dict[kWipe] = getWipe(); - dict[kBloomAmount] = getBloomAmount(); - dict[kStarAmount] = getStarAmount(); - dict[kStarAngle] = getStarAngle(); - dict[kVignetteAmount] = getVignetteAmount(); - dict[kChromaticAberrationAmount] = getChromaticAberrationAmount(); - dict[kBarrelDistortAmount] = getBarrelDistortAmount(); - dict[kSaturationCurve] = getSaturationCurve(); - dict[kColorOffset] = getColorOffset(); - dict[kColorScale] = getColorScale(); - dict[kColorPower] = getColorPower(); - dict[kColorOffsetScalar] = getColorOffsetScalar(); - dict[kColorScaleScalar] = getColorScaleScalar(); - dict[kColorPowerScalar] = getColorPowerScalar(); - return dict; -} - -SimplePostFX::SimplePostFX(std::shared_ptr pDevice, const Dictionary& dict) - : RenderPass(std::move(pDevice)) +SimplePostFX::SimplePostFX(ref pDevice, const Dictionary& dict) + : RenderPass(pDevice) { // Deserialize pass from dictionary. for (const auto& [key, value] : dict) @@ -137,7 +109,7 @@ SimplePostFX::SimplePostFX(std::shared_ptr pDevice, const Dictionary& di Sampler::Desc samplerDesc; samplerDesc.setFilterMode(Sampler::Filter::Linear, Sampler::Filter::Linear, Sampler::Filter::Point); samplerDesc.setAddressingMode(Sampler::AddressMode::Border, Sampler::AddressMode::Border, Sampler::AddressMode::Border); - mpLinearSampler = Sampler::create(mpDevice.get(), samplerDesc); + mpLinearSampler = Sampler::create(mpDevice, samplerDesc); Program::DefineList defines; mpDownsamplePass = ComputePass::create(mpDevice, kShaderFile, "downsample", defines); @@ -145,6 +117,29 @@ SimplePostFX::SimplePostFX(std::shared_ptr pDevice, const Dictionary& di mpPostFXPass = ComputePass::create(mpDevice, kShaderFile, "runPostFX", defines); } +Dictionary SimplePostFX::getScriptingDictionary() +{ + Dictionary dict; + dict[kEnabled] = getEnabled(); + dict[kOutputSize] = mOutputSizeSelection; + if (mOutputSizeSelection == RenderPassHelpers::IOSize::Fixed) dict[kFixedOutputSize] = mFixedOutputSize; + dict[kWipe] = getWipe(); + dict[kBloomAmount] = getBloomAmount(); + dict[kStarAmount] = getStarAmount(); + dict[kStarAngle] = getStarAngle(); + dict[kVignetteAmount] = getVignetteAmount(); + dict[kChromaticAberrationAmount] = getChromaticAberrationAmount(); + dict[kBarrelDistortAmount] = getBarrelDistortAmount(); + dict[kSaturationCurve] = getSaturationCurve(); + dict[kColorOffset] = getColorOffset(); + dict[kColorScale] = getColorScale(); + dict[kColorPower] = getColorPower(); + dict[kColorOffsetScalar] = getColorOffsetScalar(); + dict[kColorScaleScalar] = getColorScaleScalar(); + dict[kColorPowerScalar] = getColorPowerScalar(); + return dict; +} + RenderPassReflection SimplePostFX::reflect(const CompileData& compileData) { RenderPassReflection reflector; @@ -174,10 +169,10 @@ void SimplePostFX::execute(RenderContext* pRenderContext, const RenderData& rend getBloomAmount() == 0.f && getChromaticAberrationAmount() == 0.f && getBarrelDistortAmount() == 0.f && - getSaturationCurve() == float3(1.f) && - getColorOffset() == float3(0.5f) && - getColorScale() == float3(0.5f) && - getColorPower() == float3(0.5f) && + all(getSaturationCurve() == float3(1.f)) && + all(getColorOffset() == float3(0.5f)) && + all(getColorScale() == float3(0.5f)) && + all(getColorPower() == float3(0.5f)) && getColorOffsetScalar() == 0.f && getColorScaleScalar() == 0.f && getColorPowerScalar() == 0.f @@ -191,51 +186,60 @@ void SimplePostFX::execute(RenderContext* pRenderContext, const RenderData& rend preparePostFX(pRenderContext, resolution.x, resolution.y); if (getBloomAmount() > 0.f) { - mpDownsamplePass["gLinearSampler"] = mpLinearSampler; - for (int level = 0; level < kNumLevels; ++level) + // Downsampling { - uint2 res = { std::max(1u , resolution.x >> (level + 1)), std::max(1u , resolution.y >> (level + 1)) }; - float2 invres = float2(1.f / res.x, 1.f / res.y); - mpDownsamplePass["PerFrameCB"]["gResolution"] = res; - mpDownsamplePass["PerFrameCB"]["gInvRes"] = invres; - mpDownsamplePass["gSrc"] = level ? mpPyramid[level] : pSrc; - mpDownsamplePass["gDst"] = mpPyramid[level + 1]; - mpDownsamplePass->execute(pRenderContext, uint3(res, 1)); + auto var = mpDownsamplePass->getRootVar(); + var["gLinearSampler"] = mpLinearSampler; + for (int level = 0; level < kNumLevels; ++level) + { + uint2 res = { std::max(1u , resolution.x >> (level + 1)), std::max(1u , resolution.y >> (level + 1)) }; + float2 invres = float2(1.f / res.x, 1.f / res.y); + var["PerFrameCB"]["gResolution"] = res; + var["PerFrameCB"]["gInvRes"] = invres; + var["gSrc"] = level ? mpPyramid[level] : pSrc; + var["gDst"] = mpPyramid[level + 1]; + mpDownsamplePass->execute(pRenderContext, uint3(res, 1)); + } } - mpUpsamplePass["gLinearSampler"] = mpLinearSampler; - mpUpsamplePass["PerFrameCB"]["gBloomAmount"] = getBloomAmount(); - mpUpsamplePass["gSrc"] = pSrc; - for (int level = kNumLevels - 1; level >= 0; --level) + // Upsampling { - uint2 res = { std::max(1u , resolution.x >> level), std::max(1u , resolution.y >> level) }; - float2 invres = float2(1.f / res.x, 1.f / res.y); - mpUpsamplePass["PerFrameCB"]["gResolution"] = res; - mpUpsamplePass["PerFrameCB"]["gInvRes"] = invres; - bool wantStar = level == 1 || level == 2; - mpUpsamplePass["PerFrameCB"]["gStar"] = (wantStar) ? getStarAmount() : 0.f; - if (wantStar) { - float ang = getStarAngle(); - mpUpsamplePass["PerFrameCB"]["gStarDir1"] = float2(std::sin(ang), std::cos(ang)) * invres * 2.f; - ang += float(M_PI) / 3.f; - mpUpsamplePass["PerFrameCB"]["gStarDir2"] = float2(std::sin(ang), std::cos(ang)) * invres * 2.f; - ang += float(M_PI) / 3.f; - mpUpsamplePass["PerFrameCB"]["gStarDir3"] = float2(std::sin(ang), std::cos(ang)) * invres * 2.f; + auto var = mpUpsamplePass->getRootVar(); + var["gLinearSampler"] = mpLinearSampler; + var["PerFrameCB"]["gBloomAmount"] = getBloomAmount(); + var["gSrc"] = pSrc; + for (int level = kNumLevels - 1; level >= 0; --level) + { + uint2 res = { std::max(1u , resolution.x >> level), std::max(1u , resolution.y >> level) }; + float2 invres = float2(1.f / res.x, 1.f / res.y); + var["PerFrameCB"]["gResolution"] = res; + var["PerFrameCB"]["gInvRes"] = invres; + bool wantStar = level == 1 || level == 2; + var["PerFrameCB"]["gStar"] = (wantStar) ? getStarAmount() : 0.f; + if (wantStar) { + float ang = getStarAngle(); + var["PerFrameCB"]["gStarDir1"] = float2(std::sin(ang), std::cos(ang)) * invres * 2.f; + ang += float(M_PI) / 3.f; + var["PerFrameCB"]["gStarDir2"] = float2(std::sin(ang), std::cos(ang)) * invres * 2.f; + ang += float(M_PI) / 3.f; + var["PerFrameCB"]["gStarDir3"] = float2(std::sin(ang), std::cos(ang)) * invres * 2.f; + } + var["gBloomed"] = mpPyramid[level + 1]; + var["gDst"] = mpPyramid[level]; + var["PerFrameCB"]["gInPlace"] = level > 0; // for most levels, we update the pyramid in place. for the last step, we read from the original source since we did not compute it in the downsample passes. + mpUpsamplePass->execute(pRenderContext, uint3(res, 1)); } - mpUpsamplePass["gBloomed"] = mpPyramid[level + 1]; - mpUpsamplePass["gDst"] = mpPyramid[level]; - mpUpsamplePass["PerFrameCB"]["gInPlace"] = level > 0; // for most levels, we update the pyramid in place. for the last step, we read from the original source since we did not compute it in the downsample passes. - mpUpsamplePass->execute(pRenderContext, uint3(res, 1)); } } { - mpPostFXPass["PerFrameCB"]["gResolution"] = resolution; - mpPostFXPass["PerFrameCB"]["gInvRes"] = float2(1.f / resolution.x, 1.f / resolution.y); - mpPostFXPass["PerFrameCB"]["gVignetteAmount"] = getVignetteAmount(); - mpPostFXPass["PerFrameCB"]["gChromaticAberrationAmount"] = getChromaticAberrationAmount() * (1.f / 64.f); + auto var = mpPostFXPass->getRootVar(); + var["PerFrameCB"]["gResolution"] = resolution; + var["PerFrameCB"]["gInvRes"] = float2(1.f / resolution.x, 1.f / resolution.y); + var["PerFrameCB"]["gVignetteAmount"] = getVignetteAmount(); + var["PerFrameCB"]["gChromaticAberrationAmount"] = getChromaticAberrationAmount() * (1.f / 64.f); float barrel = getBarrelDistortAmount() * 0.125f; - mpPostFXPass["PerFrameCB"]["gBarrelDistort"] = float2(1.f / (1.f + 4.f * barrel), barrel); // scale factor chosen to keep the corners of a 16:9 viewport fixed + var["PerFrameCB"]["gBarrelDistort"] = float2(1.f / (1.f + 4.f * barrel), barrel); // scale factor chosen to keep the corners of a 16:9 viewport fixed float3 satcurve = getSaturationCurve(); // fit a quadratic thru the 3 points satcurve.y -= satcurve.x; @@ -243,15 +247,15 @@ void SimplePostFX::execute(RenderContext* pRenderContext, const RenderData& rend float A = 2.f * satcurve.z - 4.f * satcurve.y; float B = satcurve.z - A; float C = satcurve.x; - mpPostFXPass["PerFrameCB"]["gSaturationCurve"] = float3(A, B, C); - mpPostFXPass["PerFrameCB"]["gColorOffset"] = getColorOffset() + getColorOffsetScalar() - 0.5f; - mpPostFXPass["PerFrameCB"]["gColorScale"] = getColorScale() * std::exp2(1.f + 2.f * getColorScaleScalar()); - mpPostFXPass["PerFrameCB"]["gColorPower"] = exp2(3.f * (0.5f - getColorPower() - getColorPowerScalar())); - mpPostFXPass["PerFrameCB"]["gWipe"] = mWipe * resolution.x; - mpPostFXPass["gBloomed"] = getBloomAmount() > 0.f ? mpPyramid[0] : pSrc; - mpPostFXPass["gSrc"] = pSrc; - mpPostFXPass["gDst"] = pDst; - mpPostFXPass["gLinearSampler"] = mpLinearSampler; + var["PerFrameCB"]["gSaturationCurve"] = float3(A, B, C); + var["PerFrameCB"]["gColorOffset"] = getColorOffset() + getColorOffsetScalar() - 0.5f; + var["PerFrameCB"]["gColorScale"] = getColorScale() * std::exp2(1.f + 2.f * getColorScaleScalar()); + var["PerFrameCB"]["gColorPower"] = exp2(3.f * (0.5f - getColorPower() - getColorPowerScalar())); + var["PerFrameCB"]["gWipe"] = mWipe * resolution.x; + var["gBloomed"] = getBloomAmount() > 0.f ? mpPyramid[0] : pSrc; + var["gSrc"] = pSrc; + var["gDst"] = pDst; + var["gLinearSampler"] = mpLinearSampler; mpPostFXPass->execute(pRenderContext, uint3(resolution, 1)); } } @@ -260,7 +264,7 @@ void SimplePostFX::preparePostFX(RenderContext* pRenderContext, uint32_t width, { for (int res = 0; res < kNumLevels + 1; ++res) { - Texture::SharedPtr& pBuf = mpPyramid[res]; + ref& pBuf = mpPyramid[res]; if (getBloomAmount() <= 0.f) { pBuf = nullptr; @@ -271,7 +275,7 @@ void SimplePostFX::preparePostFX(RenderContext* pRenderContext, uint32_t width, uint32_t h = std::max(1u, height >> res); if (!pBuf || pBuf->getWidth() != w || pBuf->getHeight() != h) { - pBuf = Texture::create2D(mpDevice.get(), w, h, ResourceFormat::RGBA16Float, 1, 1, nullptr, Resource::BindFlags::ShaderResource | Resource::BindFlags::UnorderedAccess); + pBuf = Texture::create2D(mpDevice, w, h, ResourceFormat::RGBA16Float, 1, 1, nullptr, Resource::BindFlags::ShaderResource | Resource::BindFlags::UnorderedAccess); FALCOR_ASSERT(pBuf); } } diff --git a/Source/RenderPasses/SimplePostFX/SimplePostFX.h b/Source/RenderPasses/SimplePostFX/SimplePostFX.h index 758eb71bb..a08c67989 100644 --- a/Source/RenderPasses/SimplePostFX/SimplePostFX.h +++ b/Source/RenderPasses/SimplePostFX/SimplePostFX.h @@ -37,21 +37,16 @@ class SimplePostFX : public RenderPass public: FALCOR_PLUGIN_CLASS(SimplePostFX, "SimplePostFX", "Simple set of post effects."); - using SharedPtr = std::shared_ptr; + static ref create(ref pDevice, const Dictionary& dict) { return make_ref(pDevice, dict); } - /** Create a new render pass object. - \param[in] pDevice GPU device. - \param[in] dict Dictionary of serialized parameters. - \return A new object, or an exception is thrown if creation failed. - */ - static SharedPtr create(std::shared_ptr pDevice, const Dictionary& dict); + SimplePostFX(ref pDevice, const Dictionary& dict); 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 renderUI(Gui::Widgets& widget) override; - virtual void setScene(RenderContext* pRenderContext, const Scene::SharedPtr& pScene) override {} + virtual void setScene(RenderContext* pRenderContext, const ref& pScene) override {} bool getEnabled() const { return mEnabled; } float getWipe() const { return mWipe; } @@ -86,8 +81,6 @@ class SimplePostFX : public RenderPass void setColorPowerScalar(float v) { mColorPowerScalar = v; } private: - SimplePostFX(std::shared_ptr pDevice, const Dictionary& dict); - void preparePostFX(RenderContext* pRenderContext, uint32_t width, uint32_t height); const static int kNumLevels = 8; @@ -95,12 +88,12 @@ class SimplePostFX : public RenderPass RenderPassHelpers::IOSize mOutputSizeSelection = RenderPassHelpers::IOSize::Default; ///< Selected output size. uint2 mFixedOutputSize = { 512, 512 }; ///< Output size in pixels when 'Fixed' size is selected. - ComputePass::SharedPtr mpDownsamplePass; - ComputePass::SharedPtr mpUpsamplePass; - ComputePass::SharedPtr mpPostFXPass; + ref mpDownsamplePass; + ref mpUpsamplePass; + ref mpPostFXPass; - Texture::SharedPtr mpPyramid[kNumLevels + 1]; ///< Image pyramid, fine to coarse, full res down in steps of 4x (16x area). - Sampler::SharedPtr mpLinearSampler; + ref mpPyramid[kNumLevels + 1]; ///< Image pyramid, fine to coarse, full res down in steps of 4x (16x area). + ref mpLinearSampler; float mWipe = 0.f; ///< Wipe across to see the effect without fx. 0<=all effect, 1>= disabled. bool mEnabled = true; ///< Enable the entire pass. diff --git a/Source/RenderPasses/TAA/TAA.cpp b/Source/RenderPasses/TAA/TAA.cpp index 6cc85b87a..a1dec3b7b 100644 --- a/Source/RenderPasses/TAA/TAA.cpp +++ b/Source/RenderPasses/TAA/TAA.cpp @@ -42,7 +42,7 @@ namespace static void regTAA(pybind11::module& m) { - pybind11::class_ pass(m, "TAA"); + pybind11::class_> pass(m, "TAA"); pass.def_property("alpha", &TAA::getAlpha, &TAA::setAlpha); pass.def_property("sigma", &TAA::getColorBoxSigma, &TAA::setColorBoxSigma); pass.def_property("antiFlicker", &TAA::getAntiFlicker, &TAA::setAntiFlicker); @@ -54,27 +54,22 @@ extern "C" FALCOR_API_EXPORT void registerPlugin(Falcor::PluginRegistry& registr ScriptBindings::registerBinding(regTAA); } -TAA::TAA(std::shared_ptr pDevice) - : RenderPass(std::move(pDevice)) +TAA::TAA(ref pDevice, const Dictionary& dict) + : RenderPass(pDevice) { mpPass = FullScreenPass::create(mpDevice, kShaderFilename); - mpFbo = Fbo::create(mpDevice.get()); + mpFbo = Fbo::create(mpDevice); Sampler::Desc samplerDesc; samplerDesc.setFilterMode(Sampler::Filter::Linear, Sampler::Filter::Linear, Sampler::Filter::Linear); - mpLinearSampler = Sampler::create(mpDevice.get(), samplerDesc); -} + mpLinearSampler = Sampler::create(mpDevice, samplerDesc); -TAA::SharedPtr TAA::create(std::shared_ptr pDevice, const Dictionary& dict) -{ - SharedPtr pTAA = SharedPtr(new TAA(std::move(pDevice))); for (const auto& [key, value] : dict) { - if (key == kAlpha) pTAA->mControls.alpha = value; - else if (key == kColorBoxSigma) pTAA->mControls.colorBoxSigma = value; - else if (key == kAntiFlicker) pTAA->mControls.antiFlicker = value; + if (key == kAlpha) mControls.alpha = value; + else if (key == kColorBoxSigma) mControls.colorBoxSigma = value; + else if (key == kAntiFlicker) mControls.antiFlicker = value; else logWarning("Unknown field '{}' in a TemporalAA dictionary.", key); } - return pTAA; } Dictionary TAA::getScriptingDictionary() @@ -108,13 +103,14 @@ void TAA::execute(RenderContext* pRenderContext, const RenderData& renderData) FALCOR_ASSERT((pColorIn->getHeight() == mpPrevColor->getHeight()) && (pColorIn->getHeight() == pMotionVec->getHeight())); FALCOR_ASSERT(pColorIn->getSampleCount() == 1 && mpPrevColor->getSampleCount() == 1 && pMotionVec->getSampleCount() == 1); - mpPass["PerFrameCB"]["gAlpha"] = mControls.alpha; - mpPass["PerFrameCB"]["gColorBoxSigma"] = mControls.colorBoxSigma; - mpPass["PerFrameCB"]["gAntiFlicker"] = mControls.antiFlicker; - mpPass["gTexColor"] = pColorIn; - mpPass["gTexMotionVec"] = pMotionVec; - mpPass["gTexPrevColor"] = mpPrevColor; - mpPass["gSampler"] = mpLinearSampler; + auto var = mpPass->getRootVar(); + var["PerFrameCB"]["gAlpha"] = mControls.alpha; + var["PerFrameCB"]["gColorBoxSigma"] = mControls.colorBoxSigma; + var["PerFrameCB"]["gAntiFlicker"] = mControls.antiFlicker; + var["gTexColor"] = pColorIn; + var["gTexMotionVec"] = pMotionVec; + var["gTexPrevColor"] = mpPrevColor; + var["gSampler"] = mpLinearSampler; mpPass->execute(pRenderContext, mpFbo); pRenderContext->blit(pColorOut->getSRV(), mpPrevColor->getRTV()); @@ -129,7 +125,7 @@ void TAA::allocatePrevColor(const Texture* pColorOut) allocate = allocate || (mpPrevColor->getFormat() != pColorOut->getFormat()); FALCOR_ASSERT(pColorOut->getSampleCount() == 1); - if (allocate) mpPrevColor = Texture::create2D(mpDevice.get(), pColorOut->getWidth(), pColorOut->getHeight(), pColorOut->getFormat(), 1, 1, nullptr, Resource::BindFlags::RenderTarget | Resource::BindFlags::ShaderResource); + if (allocate) mpPrevColor = Texture::create2D(mpDevice, pColorOut->getWidth(), pColorOut->getHeight(), pColorOut->getFormat(), 1, 1, nullptr, Resource::BindFlags::RenderTarget | Resource::BindFlags::ShaderResource); } void TAA::renderUI(Gui::Widgets& widget) diff --git a/Source/RenderPasses/TAA/TAA.h b/Source/RenderPasses/TAA/TAA.h index 0c2f9a7fb..abbb93d20 100644 --- a/Source/RenderPasses/TAA/TAA.h +++ b/Source/RenderPasses/TAA/TAA.h @@ -27,7 +27,8 @@ **************************************************************************/ #pragma once #include "Falcor.h" -#include "RenderGraph/BasePasses/FullScreenPass.h" +#include "Core/Pass/FullScreenPass.h" +#include "RenderGraph/RenderPass.h" using namespace Falcor; @@ -38,9 +39,9 @@ class TAA : public RenderPass public: FALCOR_PLUGIN_CLASS(TAA, "TAA", "Temporal Anti-Aliasing."); - using SharedPtr = std::shared_ptr; + static ref create(ref pDevice, const Dictionary& dict) { return make_ref(pDevice, dict); } - static SharedPtr create(std::shared_ptr pDevice, const Dictionary& dict); + TAA(ref pDevice, const Dictionary& dict); virtual Dictionary getScriptingDictionary() override; virtual RenderPassReflection reflect(const CompileData& compileData) override; @@ -55,12 +56,11 @@ class TAA : public RenderPass bool getAntiFlicker() { return mControls.antiFlicker; } private: - TAA(std::shared_ptr pDevice); void allocatePrevColor(const Texture* pColorOut); - FullScreenPass::SharedPtr mpPass; - Fbo::SharedPtr mpFbo; - Sampler::SharedPtr mpLinearSampler; + ref mpPass; + ref mpFbo; + ref mpLinearSampler; struct { @@ -69,5 +69,5 @@ class TAA : public RenderPass bool antiFlicker = true; } mControls; - Texture::SharedPtr mpPrevColor; + ref mpPrevColor; }; diff --git a/Source/RenderPasses/TestPasses/CMakeLists.txt b/Source/RenderPasses/TestPasses/CMakeLists.txt index 3ccdf73d8..a2d6cc48f 100644 --- a/Source/RenderPasses/TestPasses/CMakeLists.txt +++ b/Source/RenderPasses/TestPasses/CMakeLists.txt @@ -2,6 +2,9 @@ add_plugin(TestPasses) target_sources(TestPasses PRIVATE TestPasses.cpp + TestPyTorchPass.cpp + TestPyTorchPass.h + TestPyTorchPass.cs.slang TestRtProgram.cpp TestRtProgram.h TestRtProgram.rt.slang diff --git a/Source/RenderPasses/TestPasses/TestPasses.cpp b/Source/RenderPasses/TestPasses/TestPasses.cpp index 5e2028e57..3424fc811 100644 --- a/Source/RenderPasses/TestPasses/TestPasses.cpp +++ b/Source/RenderPasses/TestPasses/TestPasses.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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,13 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **************************************************************************/ #include "TestRtProgram.h" +#include "TestPyTorchPass.h" extern "C" FALCOR_API_EXPORT void registerPlugin(Falcor::PluginRegistry& registry) { registry.registerClass(); + registry.registerClass(); + ScriptBindings::registerBinding(TestRtProgram::registerScriptBindings); + ScriptBindings::registerBinding(TestPyTorchPass::registerScriptBindings); } diff --git a/Source/RenderPasses/TestPasses/TestPyTorchPass.cpp b/Source/RenderPasses/TestPasses/TestPyTorchPass.cpp new file mode 100644 index 000000000..2e34769f7 --- /dev/null +++ b/Source/RenderPasses/TestPasses/TestPyTorchPass.cpp @@ -0,0 +1,243 @@ +/*************************************************************************** + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. + # + # Redistribution and use in source and binary forms, with or without + # modification, are permitted provided that the following conditions + # are met: + # * Redistributions of source code must retain the above copyright + # notice, this list of conditions and the following disclaimer. + # * Redistributions in binary form must reproduce the above copyright + # notice, this list of conditions and the following disclaimer in the + # documentation and/or other materials provided with the distribution. + # * Neither the name of NVIDIA CORPORATION nor the names of its + # contributors may be used to endorse or promote products derived + # from this software without specific prior written permission. + # + # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY + # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + **************************************************************************/ +#include "TestPyTorchPass.h" + +namespace +{ + const char kShaderFilename[] = "RenderPasses/TestPasses/TestPyTorchPass.cs.slang"; +} + +void TestPyTorchPass::registerScriptBindings(pybind11::module& m) +{ + pybind11::class_> pass(m, "TestPyTorchPass"); + + pass.def("generateData", &TestPyTorchPass::generateData); + pass.def("verifyData", &TestPyTorchPass::verifyData); +} + +TestPyTorchPass::TestPyTorchPass(ref pDevice, const Dictionary& dict) + : RenderPass(pDevice) +{ + { + Program::Desc desc; + desc.addShaderLibrary(kShaderFilename).csEntry("writeBuffer"); + mpWritePass = ComputePass::create(mpDevice, desc); + } + { + Program::Desc desc; + desc.addShaderLibrary(kShaderFilename).csEntry("readBuffer"); + mpReadPass = ComputePass::create(mpDevice, desc); + } + + mpFence = GpuFence::create(mpDevice); + mpCounterBuffer = Buffer::create(mpDevice, sizeof(uint32_t)); + mpCounterStagingBuffer = Buffer::create(mpDevice, sizeof(uint32_t), ResourceBindFlags::None, Buffer::CpuAccess::Read, nullptr); + +#if FALCOR_HAS_CUDA + // Initialize CUDA. + // This does not seem to be necessary in order to access shared buffer handles. + // Is it because the python script has created the device prior to calling functions here? + initCuda(); +#endif +} + +TestPyTorchPass::~TestPyTorchPass() +{ +#if FALCOR_HAS_CUDA + mSharedWriteBuffer.free(); + mSharedReadBuffer.free(); +#endif +} + +Dictionary TestPyTorchPass::getScriptingDictionary() +{ + Dictionary dict; + return dict; +} + +RenderPassReflection TestPyTorchPass::reflect(const CompileData& compileData) +{ + RenderPassReflection reflector; + return reflector; +} + +TestPyTorchPass::PyTorchTensor TestPyTorchPass::generateData(const uint3 dim, const uint32_t offset) +{ +#if FALCOR_HAS_CUDA + // We create a tensor and return to PyTorch. Falcor retains ownership of the memory. + // The Pytorch side is free to access the tensor up until the next call to this function. + // The caller is responsible for synchronizing the access or copying the data into its own memory. + + RenderContext* pRenderContext = mpDevice->getRenderContext(); + + const size_t elemCount = (size_t)dim.x * dim.y * dim.z; + const size_t byteSize = elemCount * sizeof(float); + checkInvariant(byteSize <= std::numeric_limits::max(), "Buffer is too large."); + + if (mpBuffer == nullptr || mpBuffer->getElementCount() < elemCount) + { + // Create data buffer and CUDA shared buffer for async PyTorch access. + // Pytorch can access the data in the shared buffer while we generate new data into the data buffer. + // It is fine to recreate the buffers here without syncing as the caller is responsible for synchronization. + logInfo("Reallocating buffers to size {} bytes", byteSize); + mpBuffer = Buffer::createStructured(mpDevice, sizeof(float), elemCount, ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess, Buffer::CpuAccess::None, nullptr, false); + mSharedWriteBuffer = createInteropBuffer(mpDevice, byteSize); + } + + auto var = mpWritePass->getRootVar(); + var["bufferUav"] = mpBuffer; + var["CB"]["dim"] = dim; + var["CB"]["offset"] = offset; + + logInfo("Generating data on {}x{}x{} grid", dim.x, dim.y, dim.z); + mpWritePass->execute(pRenderContext, dim); + + // Copy data to shared CUDA buffer. + pRenderContext->copyResource(mSharedWriteBuffer.buffer.get(), mpBuffer.get()); + + // Flush and wait on fence for synchronization. + pRenderContext->flush(false); + mpFence->gpuSignal(pRenderContext->getLowLevelData()->getCommandQueue()); + mpFence->syncCpu(); + + // Construct PyTorch tensor from CUDA buffer. + const size_t shape[3] = { dim.x, dim.y, dim.z }; + const pybind11::dlpack::dtype dtype = pybind11::dtype(); + int32_t deviceType = pybind11::device::cuda::value; + int32_t deviceId = 0; // TODO: Consistent enumeration of GPU device IDs. + + TestPyTorchPass::PyTorchTensor tensor = TestPyTorchPass::PyTorchTensor((void*)mSharedWriteBuffer.devicePtr, 3, shape, pybind11::handle() /* owner */, nullptr /* strides */, dtype, deviceType, deviceId); + return tensor; +#else + throw RuntimeError("CUDA is not available."); +#endif +} + +bool TestPyTorchPass::verifyData(const uint3 dim, const uint32_t offset, TestPyTorchPass::PyTorchTensor data) +{ +#if FALCOR_HAS_CUDA + // Pytorch owns the memory for the tensor that is passed in. + // We copy it into a shared CUDA/DX buffer and run a compute pass to verify its contents. + // The caller is responsible for synchronizing so that the tensor is not modified while accessed here. + + RenderContext* pRenderContext = mpDevice->getRenderContext(); + + // Verify that the data is a valid Torch tensor. + if (!data.is_valid() || + data.dtype() != pybind11::dtype() || + data.device_type() != pybind11::device::cuda::value) + { + logWarning("Expected CUDA float tensor"); + return false; + } + + if (data.ndim() != 3 || + data.shape(0) != dim.x || + data.shape(1) != dim.y || + data.shape(2) != dim.z) + { + logWarning("Unexpected tensor dimensions (dim {}, expected dim {})", uint3(data.shape(0), data.shape(1), data.shape(2)), dim); + return false; + } + + // Note: For dim == 1, the stride is undefined as we always multiply it by + // the index 0. Different versions of pytorch seem to define the stride + // differently. Here, we replace any undefined stride with zeros to make + // the check work for different pytorch versions. + const uint3 stride = { + dim[0] > 1 ? data.stride(0) : 0, + dim[1] > 1 ? data.stride(1) : 0, + dim[2] > 1 ? data.stride(2) : 0 + }; + const uint3 expectedStride = { + dim[0] > 1 ? dim[1] * dim[2] : 0, + dim[1] > 1 ? dim[2] : 0, + dim[2] > 1 ? 1 : 0 + }; + if (any(stride != expectedStride)) + { + logWarning("Unexpected tensor layout (stride {}, expected stride {})", stride, expectedStride); + return false; + } + + // Create shared CUDA/DX buffer for accessing the data. + const size_t elemCount = (size_t)dim.x * dim.y * dim.z; + const size_t byteSize = elemCount * sizeof(float); + checkInvariant(byteSize <= std::numeric_limits::max(), "Buffer is too large."); + + if (mSharedReadBuffer.buffer == nullptr || mSharedReadBuffer.buffer->getSize() < byteSize) + { + // Note it is ok to free the buffer here without syncing as the buffer is not in use between iterations. + // We sync below after copying data into it and after the compute pass has finished, we don't access it anymore. + logInfo("Reallocating shared CUDA/DX buffer to size {} bytes", byteSize); + mSharedReadBuffer.free(); + mSharedReadBuffer = createInteropBuffer(mpDevice, byteSize); + } + + // Copy to shared CUDA/DX buffer for access from compute pass. + CUdeviceptr srcPtr = (CUdeviceptr)data.data(); + cudaCopyDeviceToDevice((void*)mSharedReadBuffer.devicePtr, (void*)srcPtr, byteSize); + + // The sync is required for the data to be visible in the DX buffer after the copy. + syncCudaDevice(); + + pRenderContext->clearUAV(mpCounterBuffer->getUAV().get(), uint4(0)); + + // Run compute pass to count number of elements in the tensor that has the expected value. + auto var = mpReadPass->getRootVar(); + var["bufferSrv"] = mSharedReadBuffer.buffer; + var["counter"] = mpCounterBuffer; + var["CB"]["dim"] = dim; + var["CB"]["offset"] = offset; + + logInfo("Reading [{}, {}, {}] tensor", dim.x, dim.y, dim.z); + mpReadPass->execute(pRenderContext, dim); + + // Copy counter to staging buffer for readback. + pRenderContext->copyResource(mpCounterStagingBuffer.get(), mpCounterBuffer.get()); + + // Flush and wait on fence for synchronization. + pRenderContext->flush(false); + mpFence->gpuSignal(pRenderContext->getLowLevelData()->getCommandQueue()); + mpFence->syncCpu(); + + const uint32_t counter = *reinterpret_cast(mpCounterStagingBuffer->map(Buffer::MapType::Read)); + mpCounterStagingBuffer->unmap(); + FALCOR_ASSERT(counter <= elemCount); + + if (counter != elemCount) + { + logWarning("Unexpected tensor data ({} out of {} values correct)", counter, elemCount); + return false; + } + + return true; +#else + throw RuntimeError("CUDA is not available."); +#endif +} diff --git a/Source/RenderPasses/TestPasses/TestPyTorchPass.cs.slang b/Source/RenderPasses/TestPasses/TestPyTorchPass.cs.slang new file mode 100644 index 000000000..1cea9fc0d --- /dev/null +++ b/Source/RenderPasses/TestPasses/TestPyTorchPass.cs.slang @@ -0,0 +1,63 @@ +/*************************************************************************** + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. + # + # Redistribution and use in source and binary forms, with or without + # modification, are permitted provided that the following conditions + # are met: + # * Redistributions of source code must retain the above copyright + # notice, this list of conditions and the following disclaimer. + # * Redistributions in binary form must reproduce the above copyright + # notice, this list of conditions and the following disclaimer in the + # documentation and/or other materials provided with the distribution. + # * Neither the name of NVIDIA CORPORATION nor the names of its + # contributors may be used to endorse or promote products derived + # from this software without specific prior written permission. + # + # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY + # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + **************************************************************************/ +StructuredBuffer bufferSrv; +RWStructuredBuffer bufferUav; +RWByteAddressBuffer counter; + +cbuffer CB +{ + uint3 dim; + uint offset; +} + +uint getIndex(uint3 threadID) +{ + return (threadID.x * dim.y + threadID.y) * dim.z + threadID.z; +} + +[numthreads(16, 16, 1)] +void writeBuffer(uint3 threadID : SV_DispatchThreadID) +{ + if (any(threadID >= dim)) return; + + uint idx = getIndex(threadID); + bufferUav[idx] = (float)(offset + idx); +} + +[numthreads(1, 16, 16)] +void readBuffer(uint3 threadID : SV_DispatchThreadID) +{ + if (any(threadID >= dim)) return; + + uint idx = getIndex(threadID); + float expectedValue = (float)(offset + idx); + float value = bufferSrv[idx]; + + if (value == expectedValue) + counter.InterlockedAdd(0, 1); +} diff --git a/Source/RenderPasses/TestPasses/TestPyTorchPass.h b/Source/RenderPasses/TestPasses/TestPyTorchPass.h new file mode 100644 index 000000000..512aefa5b --- /dev/null +++ b/Source/RenderPasses/TestPasses/TestPyTorchPass.h @@ -0,0 +1,75 @@ +/*************************************************************************** + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. + # + # Redistribution and use in source and binary forms, with or without + # modification, are permitted provided that the following conditions + # are met: + # * Redistributions of source code must retain the above copyright + # notice, this list of conditions and the following disclaimer. + # * Redistributions in binary form must reproduce the above copyright + # notice, this list of conditions and the following disclaimer in the + # documentation and/or other materials provided with the distribution. + # * Neither the name of NVIDIA CORPORATION nor the names of its + # contributors may be used to endorse or promote products derived + # from this software without specific prior written permission. + # + # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY + # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + # (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 "RenderGraph/RenderPass.h" +#include "Utils/Scripting/ndarray.h" +#if FALCOR_HAS_CUDA +#include "Utils/CudaUtils.h" +#endif + +using namespace Falcor; + +class TestPyTorchPass : public RenderPass +{ +public: + FALCOR_PLUGIN_CLASS(TestPyTorchPass, "TestPyTorchPass", "Test pass for PyTorch tensor interop."); + + using PyTorchTensor = pybind11::ndarray; + + static ref create(ref pDevice, const Dictionary& dict) { return make_ref(pDevice, dict); } + + TestPyTorchPass(ref pDevice, const Dictionary& dict); + virtual ~TestPyTorchPass(); + + 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 ref& pScene) override {} + virtual void execute(RenderContext* pRenderContext, const RenderData& renderData) override {} + virtual void renderUI(Gui::Widgets& widget) override {} + virtual bool onMouseEvent(const MouseEvent& mouseEvent) override { return false; } + virtual bool onKeyEvent(const KeyboardEvent& keyEvent) override { return false; } + + static void registerScriptBindings(pybind11::module& m); + + PyTorchTensor generateData(const uint3 dim, const uint32_t offset); + bool verifyData(const uint3 dim, const uint32_t offset, PyTorchTensor data); + +private: + ref mpBuffer; ///< GPU buffer for generated data. + ref mpCounterBuffer; + ref mpCounterStagingBuffer; +#if FALCOR_HAS_CUDA + InteropBuffer mSharedWriteBuffer; ///< Shared CUDA/DX buffer for passing data from Falcor to PyTorch asynchronously. + InteropBuffer mSharedReadBuffer; ///< Shared CUDA/DX buffer for passing data from PyTorch to Falcor asynchronously. +#endif + ref mpWritePass; + ref mpReadPass; + ref mpFence; +}; diff --git a/Source/RenderPasses/TestPasses/TestRtProgram.cpp b/Source/RenderPasses/TestPasses/TestRtProgram.cpp index 88b884da4..7d982efcd 100644 --- a/Source/RenderPasses/TestPasses/TestRtProgram.cpp +++ b/Source/RenderPasses/TestPasses/TestRtProgram.cpp @@ -45,19 +45,14 @@ namespace void TestRtProgram::registerScriptBindings(pybind11::module& m) { - pybind11::class_ pass(m, "TestRtProgram"); + pybind11::class_> pass(m, "TestRtProgram"); pass.def("addCustomPrimitive", &TestRtProgram::addCustomPrimitive); pass.def("removeCustomPrimitive", &TestRtProgram::removeCustomPrimitive); pass.def("moveCustomPrimitive", &TestRtProgram::moveCustomPrimitive); } -TestRtProgram::SharedPtr TestRtProgram::create(std::shared_ptr pDevice, const Dictionary& dict) -{ - return SharedPtr(new TestRtProgram(std::move(pDevice), dict)); -} - -TestRtProgram::TestRtProgram(std::shared_ptr pDevice, const Dictionary& dict) - : RenderPass(std::move(pDevice)) +TestRtProgram::TestRtProgram(ref pDevice, const Dictionary& dict) + : RenderPass(pDevice) { for (const auto& [key, value] : dict) { @@ -81,7 +76,7 @@ RenderPassReflection TestRtProgram::reflect(const CompileData& compileData) return reflector; } -void TestRtProgram::setScene(RenderContext* pRenderContext, const Scene::SharedPtr& pScene) +void TestRtProgram::setScene(RenderContext* pRenderContext, const ref& pScene) { mpScene = pScene; @@ -110,7 +105,7 @@ void TestRtProgram::sceneChanged() desc.setMaxPayloadSize(kMaxPayloadSizeBytes); desc.setMaxAttributeSize(kMaxAttributeSizeBytes); - RtBindingTable::SharedPtr sbt; + ref sbt; if (mMode == 0) { diff --git a/Source/RenderPasses/TestPasses/TestRtProgram.h b/Source/RenderPasses/TestPasses/TestRtProgram.h index 16c559003..5e9fa951f 100644 --- a/Source/RenderPasses/TestPasses/TestRtProgram.h +++ b/Source/RenderPasses/TestPasses/TestRtProgram.h @@ -27,6 +27,7 @@ **************************************************************************/ #pragma once #include "Falcor.h" +#include "RenderGraph/RenderPass.h" using namespace Falcor; @@ -35,19 +36,14 @@ class TestRtProgram : public RenderPass public: FALCOR_PLUGIN_CLASS(TestRtProgram, "TestRtProgram", "Test pass for RtProgram."); - using SharedPtr = std::shared_ptr; + static ref create(ref pDevice, const Dictionary& dict) { return make_ref(pDevice, dict); } - /** Create a new render pass object. - \param[in] pDevice GPU device. - \param[in] dict Dictionary of serialized parameters. - \return A new object, or an exception is thrown if creation failed. - */ - static SharedPtr create(std::shared_ptr pDevice, const Dictionary& dict); + TestRtProgram(ref pDevice, const Dictionary& dict); 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 setScene(RenderContext* pRenderContext, const ref& pScene) override; virtual void execute(RenderContext* pRenderContext, const RenderData& renderData) override; virtual void renderUI(Gui::Widgets& widget) override; virtual bool onMouseEvent(const MouseEvent& mouseEvent) override { return false; } @@ -56,15 +52,13 @@ class TestRtProgram : public RenderPass static void registerScriptBindings(pybind11::module& m); private: - TestRtProgram(std::shared_ptr pDevice, const Dictionary& dict); - void sceneChanged(); void addCustomPrimitive(); void removeCustomPrimitive(uint32_t index); void moveCustomPrimitive(); // Internal state - Scene::SharedPtr mpScene; + ref mpScene; uint32_t mMode = 0; uint32_t mSelectedIdx = 0; @@ -74,7 +68,7 @@ class TestRtProgram : public RenderPass struct { - RtProgram::SharedPtr pProgram; - RtProgramVars::SharedPtr pVars; + ref pProgram; + ref pVars; } mRT; }; diff --git a/Source/RenderPasses/ToneMapper/ToneMapper.cpp b/Source/RenderPasses/ToneMapper/ToneMapper.cpp index 68c94091a..553e29c2d 100644 --- a/Source/RenderPasses/ToneMapper/ToneMapper.cpp +++ b/Source/RenderPasses/ToneMapper/ToneMapper.cpp @@ -98,7 +98,19 @@ namespace static void regToneMapper(pybind11::module& m) { - pybind11::class_ pass(m, "ToneMapper"); + pybind11::enum_ op(m, "ToneMapOp"); + op.value("Linear", ToneMapper::Operator::Linear); + op.value("Reinhard", ToneMapper::Operator::Reinhard); + op.value("ReinhardModified", ToneMapper::Operator::ReinhardModified); + op.value("HejiHableAlu", ToneMapper::Operator::HejiHableAlu); + op.value("HableUc2", ToneMapper::Operator::HableUc2); + op.value("Aces", ToneMapper::Operator::Aces); + + pybind11::enum_ exposureMode(m, "ExposureMode"); + exposureMode.value("AperturePriority", ToneMapper::ExposureMode::AperturePriority); + exposureMode.value("ShutterPriority", ToneMapper::ExposureMode::ShutterPriority); + + pybind11::class_> pass(m, "ToneMapper"); pass.def_property(kExposureCompensation, &ToneMapper::getExposureCompensation, &ToneMapper::setExposureCompensation); pass.def_property(kAutoExposure, &ToneMapper::getAutoExposure, &ToneMapper::setAutoExposure); pass.def_property(kExposureValue, &ToneMapper::getExposureValue, &ToneMapper::setExposureValue); @@ -112,18 +124,6 @@ static void regToneMapper(pybind11::module& m) pass.def_property(kFNumber, &ToneMapper::getFNumber, &ToneMapper::setFNumber); pass.def_property(kShutter, &ToneMapper::getShutter, &ToneMapper::setShutter); pass.def_property(kExposureMode, &ToneMapper::getExposureMode, &ToneMapper::setExposureMode); - - pybind11::enum_ op(m, "ToneMapOp"); - op.value("Linear", ToneMapper::Operator::Linear); - op.value("Reinhard", ToneMapper::Operator::Reinhard); - op.value("ReinhardModified", ToneMapper::Operator::ReinhardModified); - op.value("HejiHableAlu", ToneMapper::Operator::HejiHableAlu); - op.value("HableUc2", ToneMapper::Operator::HableUc2); - op.value("Aces", ToneMapper::Operator::Aces); - - pybind11::enum_ exposureMode(m, "ExposureMode"); - exposureMode.value("AperturePriority", ToneMapper::ExposureMode::AperturePriority); - exposureMode.value("ShutterPriority", ToneMapper::ExposureMode::ShutterPriority); } extern "C" FALCOR_API_EXPORT void registerPlugin(Falcor::PluginRegistry& registry) @@ -132,13 +132,8 @@ extern "C" FALCOR_API_EXPORT void registerPlugin(Falcor::PluginRegistry& registr ScriptBindings::registerBinding(regToneMapper); } -ToneMapper::SharedPtr ToneMapper::create(std::shared_ptr pDevice, const Dictionary& dict) -{ - return ToneMapper::SharedPtr(new ToneMapper(std::move(pDevice), dict)); -} - -ToneMapper::ToneMapper(std::shared_ptr pDevice, const Dictionary& dict) - : RenderPass(std::move(pDevice)) +ToneMapper::ToneMapper(ref pDevice, const Dictionary& dict) + : RenderPass(pDevice) { parseDictionary(dict); @@ -149,9 +144,9 @@ ToneMapper::ToneMapper(std::shared_ptr pDevice, const Dictionary& dict) Sampler::Desc samplerDesc; samplerDesc.setFilterMode(Sampler::Filter::Point, Sampler::Filter::Point, Sampler::Filter::Point); - mpPointSampler = Sampler::create(mpDevice.get(), samplerDesc); + mpPointSampler = Sampler::create(mpDevice, samplerDesc); samplerDesc.setFilterMode(Sampler::Filter::Linear, Sampler::Filter::Linear, Sampler::Filter::Point); - mpLinearSampler = Sampler::create(mpDevice.get(), samplerDesc); + mpLinearSampler = Sampler::create(mpDevice, samplerDesc); } void ToneMapper::parseDictionary(const Dictionary& dict) @@ -227,7 +222,7 @@ void ToneMapper::execute(RenderContext* pRenderContext, const RenderData& render logWarning("ToneMapper pass I/O has different dimensions. The image will be resampled."); } - Fbo::SharedPtr pFbo = Fbo::create(mpDevice.get()); + ref pFbo = Fbo::create(mpDevice); pFbo->attachColorTarget(pDst, 0); // Run luminance pass if auto exposure is enabled @@ -235,8 +230,9 @@ void ToneMapper::execute(RenderContext* pRenderContext, const RenderData& render { createLuminanceFbo(pSrc); - mpLuminancePass["gColorTex"] = pSrc; - mpLuminancePass["gColorSampler"] = mpLinearSampler; + auto var = mpLuminancePass->getRootVar(); + var["gColorTex"] = pSrc; + var["gColorSampler"] = mpLinearSampler; mpLuminancePass->execute(pRenderContext, mpLuminanceFbo); mpLuminanceFbo->getColorTexture(0)->generateMips(pRenderContext); @@ -258,24 +254,25 @@ void ToneMapper::execute(RenderContext* pRenderContext, const RenderData& render ToneMapperParams params; params.whiteScale = mWhiteScale; params.whiteMaxLuminance = mWhiteMaxLuminance; - params.colorTransform = static_cast>(mColorTransform); + params.colorTransform = float3x4(mColorTransform); mpToneMapPass->getRootVar()["PerImageCB"]["gParams"].setBlob(¶ms, sizeof(params)); mUpdateToneMapPass = false; } - mpToneMapPass["gColorTex"] = pSrc; - mpToneMapPass["gColorSampler"] = mpPointSampler; + auto var = mpToneMapPass->getRootVar(); + var["gColorTex"] = pSrc; + var["gColorSampler"] = mpPointSampler; if (mAutoExposure) { - mpToneMapPass["gLuminanceTexSampler"] = mpLinearSampler; - mpToneMapPass["gLuminanceTex"] = mpLuminanceFbo->getColorTexture(0); + var["gLuminanceTexSampler"] = mpLinearSampler; + var["gLuminanceTex"] = mpLuminanceFbo->getColorTexture(0); } mpToneMapPass->execute(pRenderContext, pFbo); } -void ToneMapper::createLuminanceFbo(const Texture::SharedPtr& pSrc) +void ToneMapper::createLuminanceFbo(const ref& pSrc) { bool createFbo = mpLuminanceFbo == nullptr; ResourceFormat srcFormat = pSrc->getFormat(); @@ -297,7 +294,7 @@ void ToneMapper::createLuminanceFbo(const Texture::SharedPtr& pSrc) { Fbo::Desc desc; desc.setColorTarget(0, luminanceFormat); - mpLuminanceFbo = Fbo::create2D(mpDevice.get(), requiredWidth, requiredHeight, desc, 1, Fbo::kAttachEntireMipLevel); + mpLuminanceFbo = Fbo::create2D(mpDevice, requiredWidth, requiredHeight, desc, 1, Fbo::kAttachEntireMipLevel); } } @@ -392,7 +389,7 @@ void ToneMapper::renderUI(Gui::Widgets& widget) } } -void ToneMapper::setScene(RenderContext* pRenderContext, const std::shared_ptr& pScene) +void ToneMapper::setScene(RenderContext* pRenderContext, const ref& pScene) { if (pScene && mUseSceneMetadata) { @@ -406,7 +403,7 @@ void ToneMapper::setScene(RenderContext* pRenderContext, const std::shared_ptr(); + mWhiteBalanceTransform = mWhiteBalance ? calculateWhiteBalanceTransformRGB_Rec709(mWhitePoint) : float3x3::identity(); // Calculate source illuminant, i.e. the color that transforms to a pure white (1, 1, 1) output at the current color settings. - mSourceWhite = rmcv::inverse(mWhiteBalanceTransform) * float3(1, 1, 1); + mSourceWhite = mul(inverse(mWhiteBalanceTransform), float3(1, 1, 1)); } void ToneMapper::updateColorTransform() diff --git a/Source/RenderPasses/ToneMapper/ToneMapper.h b/Source/RenderPasses/ToneMapper/ToneMapper.h index 1012e6646..f29caf757 100644 --- a/Source/RenderPasses/ToneMapper/ToneMapper.h +++ b/Source/RenderPasses/ToneMapper/ToneMapper.h @@ -30,7 +30,7 @@ #include "ToneMapperParams.slang" #include "RenderGraph/RenderPass.h" #include "RenderGraph/RenderPassHelpers.h" -#include "RenderGraph/BasePasses/FullScreenPass.h" +#include "Core/Pass/FullScreenPass.h" using namespace Falcor; @@ -41,7 +41,6 @@ class ToneMapper : public RenderPass "Tone-map a color-buffer. The resulting buffer is always in the [0, 1] range. The pass supports auto-exposure and eye-adaptation." }); - using SharedPtr = std::shared_ptr; using Operator = ToneMapperOperator; enum class ExposureMode @@ -50,13 +49,15 @@ class ToneMapper : public RenderPass ShutterPriority, // Keep shutter constant when modifying EV }; - static SharedPtr create(std::shared_ptr pDevice, const Dictionary& dict); + static ref create(ref pDevice, const Dictionary& dict) { return make_ref(pDevice, dict); } + + ToneMapper(ref pDevice, const Dictionary& dict); 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 std::shared_ptr& pScene) override; + virtual void setScene(RenderContext* pRenderContext, const ref& pScene) override; // Scripting functions void setExposureCompensation(float exposureCompensation); @@ -88,23 +89,22 @@ class ToneMapper : public RenderPass ExposureMode getExposureMode() const { return mExposureMode; } private: - ToneMapper(std::shared_ptr pDevice, const Dictionary& dict); void parseDictionary(const Dictionary& dict); void createToneMapPass(); void createLuminancePass(); - void createLuminanceFbo(const Texture::SharedPtr& pSrc); + void createLuminanceFbo(const ref& pSrc); void updateWhiteBalanceTransform(); void updateColorTransform(); void updateExposureValue(); - FullScreenPass::SharedPtr mpToneMapPass; - FullScreenPass::SharedPtr mpLuminancePass; - Fbo::SharedPtr mpLuminanceFbo; - Sampler::SharedPtr mpPointSampler; - Sampler::SharedPtr mpLinearSampler; + ref mpToneMapPass; + ref mpLuminancePass; + ref mpLuminanceFbo; + ref mpPointSampler; + ref mpLinearSampler; RenderPassHelpers::IOSize mOutputSizeSelection = RenderPassHelpers::IOSize::Default; ///< Selected output size. ResourceFormat mOutputFormat = ResourceFormat::Unknown; ///< Output format (uses default when set to ResourceFormat::Unknown). @@ -129,9 +129,9 @@ class ToneMapper : public RenderPass float mWhiteScale = 11.2f; ///< Parameter used in Uc2Hable operator. // Pre-computed fields based on above settings - rmcv::mat3 mWhiteBalanceTransform; ///< Color balance transform in RGB space. + float3x3 mWhiteBalanceTransform; ///< Color balance transform in RGB space. float3 mSourceWhite; ///< Source illuminant in RGB (the white point to which the image is transformed to conform to). - rmcv::mat3 mColorTransform; ///< Final color transform with exposure value baked in. + float3x3 mColorTransform; ///< Final color transform with exposure value baked in. bool mRecreateToneMapPass = true; bool mUpdateToneMapPass = true; diff --git a/Source/RenderPasses/Utils/CMakeLists.txt b/Source/RenderPasses/Utils/CMakeLists.txt index 7806a4275..b962b4a89 100644 --- a/Source/RenderPasses/Utils/CMakeLists.txt +++ b/Source/RenderPasses/Utils/CMakeLists.txt @@ -8,6 +8,10 @@ target_sources(Utils PRIVATE Composite/Composite.h Composite/CompositeMode.slangh + CrossFade/CrossFade.cpp + CrossFade/CrossFade.cs.slang + CrossFade/CrossFade.h + GaussianBlur/GaussianBlur.cpp GaussianBlur/GaussianBlur.h GaussianBlur/GaussianBlur.ps.slang diff --git a/Source/RenderPasses/Utils/Composite/Composite.cpp b/Source/RenderPasses/Utils/Composite/Composite.cpp index d53aab011..328a57509 100644 --- a/Source/RenderPasses/Utils/Composite/Composite.cpp +++ b/Source/RenderPasses/Utils/Composite/Composite.cpp @@ -48,13 +48,8 @@ namespace }; } -Composite::SharedPtr Composite::create(std::shared_ptr pDevice, const Dictionary& dict) -{ - return SharedPtr(new Composite(std::move(pDevice), dict)); -} - -Composite::Composite(std::shared_ptr pDevice, const Dictionary& dict) - : RenderPass(std::move(pDevice)) +Composite::Composite(ref pDevice, const Dictionary& dict) + : RenderPass(pDevice) { // Parse dictionary. for (const auto& [key, value] : dict) @@ -107,14 +102,14 @@ void Composite::execute(RenderContext* pRenderContext, const RenderData& renderD } // Bind resources. - auto var = mCompositePass["CB"]; - var["frameDim"] = mFrameDim; - var["scaleA"] = mScaleA; - var["scaleB"] = mScaleB; - - mCompositePass["A"] = renderData.getTexture(kInputA); // Can be nullptr - mCompositePass["B"] = renderData.getTexture(kInputB); // Can be nullptr - mCompositePass["output"] = pOutput; + auto var = mCompositePass->getRootVar(); + var["CB"]["frameDim"] = mFrameDim; + var["CB"]["scaleA"] = mScaleA; + var["CB"]["scaleB"] = mScaleB; + var["A"] = renderData.getTexture(kInputA); // Can be nullptr + + var["B"] = renderData.getTexture(kInputB); // Can be nullptr + var["output"] = pOutput; mCompositePass->execute(pRenderContext, mFrameDim.x, mFrameDim.y); } diff --git a/Source/RenderPasses/Utils/Composite/Composite.h b/Source/RenderPasses/Utils/Composite/Composite.h index f9b897898..b6eb10d8b 100644 --- a/Source/RenderPasses/Utils/Composite/Composite.h +++ b/Source/RenderPasses/Utils/Composite/Composite.h @@ -27,6 +27,7 @@ **************************************************************************/ #pragma once #include "Falcor.h" +#include "RenderGraph/RenderPass.h" using namespace Falcor; @@ -42,8 +43,6 @@ class Composite : public RenderPass public: FALCOR_PLUGIN_CLASS(Composite, "Composite", "Composite pass."); - using SharedPtr = std::shared_ptr; - /** Composite modes. */ enum class Mode @@ -52,7 +51,9 @@ class Composite : public RenderPass Multiply, }; - static SharedPtr create(std::shared_ptr pDevice, const Dictionary& dict); + static ref create(ref pDevice, const Dictionary& dict) { return make_ref(pDevice, dict); } + + Composite(ref pDevice, const Dictionary& dict); virtual Dictionary getScriptingDictionary() override; virtual RenderPassReflection reflect(const CompileData& compileData) override; @@ -63,8 +64,6 @@ class Composite : public RenderPass static void registerBindings(pybind11::module& m); private: - Composite(std::shared_ptr pDevice, const Dictionary& dict); - Program::DefineList getDefines() const; uint2 mFrameDim = { 0, 0 }; @@ -73,5 +72,5 @@ class Composite : public RenderPass float mScaleB = 1.f; ResourceFormat mOutputFormat = ResourceFormat::RGBA32Float; - ComputePass::SharedPtr mCompositePass; + ref mCompositePass; }; diff --git a/Source/RenderPasses/Utils/CrossFade/CrossFade.cpp b/Source/RenderPasses/Utils/CrossFade/CrossFade.cpp new file mode 100644 index 000000000..affa9ea9d --- /dev/null +++ b/Source/RenderPasses/Utils/CrossFade/CrossFade.cpp @@ -0,0 +1,173 @@ +/*************************************************************************** + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. + # + # Redistribution and use in source and binary forms, with or without + # modification, are permitted provided that the following conditions + # are met: + # * Redistributions of source code must retain the above copyright + # notice, this list of conditions and the following disclaimer. + # * Redistributions in binary form must reproduce the above copyright + # notice, this list of conditions and the following disclaimer in the + # documentation and/or other materials provided with the distribution. + # * Neither the name of NVIDIA CORPORATION nor the names of its + # contributors may be used to endorse or promote products derived + # from this software without specific prior written permission. + # + # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY + # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + **************************************************************************/ +#include "CrossFade.h" +#include "RenderGraph/RenderPassStandardFlags.h" + +namespace +{ + const std::string kShaderFile("RenderPasses/Utils/CrossFade/CrossFade.cs.slang"); + + const std::string kInputA = "A"; + const std::string kInputB = "B"; + const std::string kOutput = "out"; + + const std::string kOutputFormat = "outputFormat"; + const std::string kEnableAutoFade = "enableAutoFade"; + const std::string kWaitFrameCount = "waitFrameCount"; + const std::string kFadeFrameCount = "fadeFrameCount"; + const std::string kFadeFactor = "fadeFactor"; +} + +CrossFade::CrossFade(ref pDevice, const Dictionary& dict) + : RenderPass(pDevice) +{ + // Parse dictionary. + for (const auto& [key, value] : dict) + { + if (key == kOutputFormat) mOutputFormat = value; + else if (key == kEnableAutoFade) mEnableAutoFade = value; + else if (key == kWaitFrameCount) mWaitFrameCount = value; + else if (key == kFadeFrameCount) mFadeFrameCount = value; + else if (key == kFadeFactor) mFadeFactor = value; + else logWarning("Unknown field '{}' in CrossFade pass dictionary.", key); + } + + // Create resources. + mpFadePass = ComputePass::create(mpDevice, kShaderFile, "main"); +} + +Dictionary CrossFade::getScriptingDictionary() +{ + Dictionary dict; + if (mOutputFormat != ResourceFormat::Unknown) dict[kOutputFormat] = mOutputFormat; + dict[kEnableAutoFade] = mEnableAutoFade; + dict[kWaitFrameCount] = mWaitFrameCount; + dict[kFadeFrameCount] = mFadeFrameCount; + dict[kFadeFactor] = mFadeFactor; + return dict; +} + +RenderPassReflection CrossFade::reflect(const CompileData& compileData) +{ + RenderPassReflection reflector; + reflector.addInput(kInputA, "Input A").bindFlags(ResourceBindFlags::ShaderResource).flags(RenderPassReflection::Field::Flags::Optional); + reflector.addInput(kInputB, "Input B").bindFlags(ResourceBindFlags::ShaderResource).flags(RenderPassReflection::Field::Flags::Optional); + reflector.addOutput(kOutput, "Output").bindFlags(ResourceBindFlags::UnorderedAccess).format(mOutputFormat); + return reflector; +} + +void CrossFade::setScene(RenderContext* pRenderContext, const ref& pScene) +{ + mpScene = pScene; +} + +void CrossFade::compile(RenderContext* pRenderContext, const CompileData& compileData) +{ + mFrameDim = compileData.defaultTexDims; +} + +void CrossFade::execute(RenderContext* pRenderContext, const RenderData& renderData) +{ + bool shouldReset = false; + + // Query refresh flags passed down from the application and other passes. + auto& dict = renderData.getDictionary(); + auto refreshFlags = dict.getValue(kRenderPassRefreshFlags, RenderPassRefreshFlags::None); + + // If any refresh flag is set, we reset frame accumulation. + if (refreshFlags != RenderPassRefreshFlags::None) shouldReset = true; + + // 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) + { + shouldReset = true; + } + 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) shouldReset = true; + } + if (is_set(sceneUpdates, Scene::UpdateFlags::SDFGeometryChanged)) + { + shouldReset = true; + } + } + + if (shouldReset) + { + mMixFrame = 0; + } + else + { + mMixFrame++; + } + + float mix = mEnableAutoFade ? + math::clamp((float(mMixFrame) - mWaitFrameCount) / mFadeFrameCount, 0.f, 1.f) : + math::clamp(mFadeFactor, 0.f, 1.f); + + mScaleA = 1.f - mix; + mScaleB = mix; + + // Prepare program. + const auto& pOutput = renderData.getTexture(kOutput); + FALCOR_ASSERT(pOutput); + mOutputFormat = pOutput->getFormat(); + checkInvariant(!isIntegerFormat(mOutputFormat), "Output cannot be an integer format."); + + // Bind resources. + auto var = mpFadePass->getRootVar(); + var["CB"]["frameDim"] = mFrameDim; + var["CB"]["scaleA"] = mScaleA; + var["CB"]["scaleB"] = mScaleB; + + var["A"] = renderData.getTexture(kInputA); // Can be nullptr + var["B"] = renderData.getTexture(kInputB); // Can be nullptr + var["output"] = pOutput; + mpFadePass->execute(pRenderContext, mFrameDim.x, mFrameDim.y); +} + +void CrossFade::renderUI(Gui::Widgets& widget) +{ + widget.text("This pass fades between inputs A and B"); + widget.checkbox("Enable Auto Fade", mEnableAutoFade); + if (mEnableAutoFade) + { + widget.var("Wait Frame Count", mWaitFrameCount); + widget.var("Fade Frame Count", mFadeFrameCount); + } + else + { + widget.var("Fade Factor", mFadeFactor, 0.f, 1.f); + } +} diff --git a/Source/RenderPasses/Utils/CrossFade/CrossFade.cs.slang b/Source/RenderPasses/Utils/CrossFade/CrossFade.cs.slang new file mode 100644 index 000000000..401b3394f --- /dev/null +++ b/Source/RenderPasses/Utils/CrossFade/CrossFade.cs.slang @@ -0,0 +1,51 @@ +/*************************************************************************** + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. + # + # Redistribution and use in source and binary forms, with or without + # modification, are permitted provided that the following conditions + # are met: + # * Redistributions of source code must retain the above copyright + # notice, this list of conditions and the following disclaimer. + # * Redistributions in binary form must reproduce the above copyright + # notice, this list of conditions and the following disclaimer in the + # documentation and/or other materials provided with the distribution. + # * Neither the name of NVIDIA CORPORATION nor the names of its + # contributors may be used to endorse or promote products derived + # from this software without specific prior written permission. + # + # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY + # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + # (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 CB +{ + uint2 frameDim; + float scaleA; + float scaleB; +} + +// Inputs +Texture2D A; +Texture2D B; + +// Output +RWTexture2D output; + +[numthreads(16, 16, 1)] +void main(uint3 dispatchThreadId : SV_DispatchThreadID) +{ + const uint2 pixel = dispatchThreadId.xy; + if (any(pixel >= frameDim)) return; + + float4 result = (scaleA * A[pixel]) + (scaleB * B[pixel]); + + output[pixel] = result; +} diff --git a/Source/RenderPasses/Utils/CrossFade/CrossFade.h b/Source/RenderPasses/Utils/CrossFade/CrossFade.h new file mode 100644 index 000000000..f23554baa --- /dev/null +++ b/Source/RenderPasses/Utils/CrossFade/CrossFade.h @@ -0,0 +1,66 @@ +/*************************************************************************** + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. + # + # Redistribution and use in source and binary forms, with or without + # modification, are permitted provided that the following conditions + # are met: + # * Redistributions of source code must retain the above copyright + # notice, this list of conditions and the following disclaimer. + # * Redistributions in binary form must reproduce the above copyright + # notice, this list of conditions and the following disclaimer in the + # documentation and/or other materials provided with the distribution. + # * Neither the name of NVIDIA CORPORATION nor the names of its + # contributors may be used to endorse or promote products derived + # from this software without specific prior written permission. + # + # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY + # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + # (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 "RenderGraph/RenderPass.h" + +using namespace Falcor; + +/** Simple pass for time-dependent fading between two buffers. +*/ +class CrossFade : public RenderPass +{ +public: + FALCOR_PLUGIN_CLASS(CrossFade, "CrossFade", "CrossFade pass."); + + static ref create(ref pDevice, const Dictionary& dict) { return make_ref(pDevice, dict); } + + CrossFade(ref pDevice, const Dictionary& dict); + + virtual Dictionary getScriptingDictionary() override; + virtual RenderPassReflection reflect(const CompileData& compileData) override; + virtual void setScene(RenderContext* pRenderContext, const ref& pScene) override; + virtual void compile(RenderContext* pRenderContext, const CompileData& compileData) override; + virtual void execute(RenderContext* pRenderContext, const RenderData& renderData) override; + virtual void renderUI(Gui::Widgets& widget) override; + +private: + uint2 mFrameDim = { 0, 0 }; + float mScaleA = 1.f; + float mScaleB = 1.f; + ResourceFormat mOutputFormat = ResourceFormat::RGBA32Float; + + ref mpFadePass; + + ref mpScene; + uint32_t mMixFrame = 0; + bool mEnableAutoFade = true; + uint32_t mWaitFrameCount = 10; + uint32_t mFadeFrameCount = 100; + float mFadeFactor = 0.5f; ///< Fixed fade factor (t) used when auto-fade is disabled. The output is: (1-t)*A + t*B. +}; diff --git a/Source/RenderPasses/Utils/GaussianBlur/GaussianBlur.cpp b/Source/RenderPasses/Utils/GaussianBlur/GaussianBlur.cpp index 38fbb1827..b1f6bce72 100644 --- a/Source/RenderPasses/Utils/GaussianBlur/GaussianBlur.cpp +++ b/Source/RenderPasses/Utils/GaussianBlur/GaussianBlur.cpp @@ -41,30 +41,25 @@ namespace void GaussianBlur::registerBindings(pybind11::module& m) { - pybind11::class_ pass(m, "GaussianBlur"); + pybind11::class_> pass(m, "GaussianBlur"); pass.def_property(kKernelWidth, &GaussianBlur::getKernelWidth, &GaussianBlur::setKernelWidth); pass.def_property(kSigma, &GaussianBlur::getSigma, &GaussianBlur::setSigma); } -GaussianBlur::GaussianBlur(std::shared_ptr pDevice) - : RenderPass(std::move(pDevice)) +GaussianBlur::GaussianBlur(ref pDevice, const Dictionary& dict) + : RenderPass(pDevice) { - mpFbo = Fbo::create(mpDevice.get()); + mpFbo = Fbo::create(mpDevice); 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(mpDevice.get(), samplerDesc); -} + mpSampler = Sampler::create(mpDevice, samplerDesc); -GaussianBlur::SharedPtr GaussianBlur::create(std::shared_ptr pDevice, const Dictionary& dict) -{ - SharedPtr pBlur = SharedPtr(new GaussianBlur(std::move(pDevice))); for (const auto& [key, value] : dict) { - if (key == kKernelWidth) pBlur->mKernelWidth = value; - else if (key == kSigma) pBlur->mSigma = value; + if (key == kKernelWidth) mKernelWidth = value; + else if (key == kSigma) mSigma = value; else logWarning("Unknown field '{}' in a GaussianBlur dictionary.", key); } - return pBlur; } Dictionary GaussianBlur::getScriptingDictionary() @@ -136,13 +131,19 @@ void GaussianBlur::execute(RenderContext* pRenderContext, const RenderData& rend createTmpFbo(pSrc.get()); // Horizontal pass - mpHorizontalBlur["gSampler"] = mpSampler; - mpHorizontalBlur["gSrcTex"] = pSrc; - mpHorizontalBlur->execute(pRenderContext, mpTmpFbo); + { + auto var = mpHorizontalBlur->getRootVar(); + var["gSampler"] = mpSampler; + var["gSrcTex"] = pSrc; + mpHorizontalBlur->execute(pRenderContext, mpTmpFbo); + } // Vertical pass - mpVerticalBlur["gSrcTex"] = mpTmpFbo->getColorTexture(0); - mpVerticalBlur->execute(pRenderContext, mpFbo); + { + auto var = mpVerticalBlur->getRootVar(); + var["gSrcTex"] = mpTmpFbo->getColorTexture(0); + mpVerticalBlur->execute(pRenderContext, mpFbo); + } } void GaussianBlur::createTmpFbo(const Texture* pSrc) @@ -162,7 +163,7 @@ void GaussianBlur::createTmpFbo(const Texture* pSrc) { Fbo::Desc fboDesc; fboDesc.setColorTarget(0, srcFormat); - mpTmpFbo = Fbo::create2D(mpDevice.get(), pSrc->getWidth(), pSrc->getHeight(), fboDesc, pSrc->getArraySize()); + mpTmpFbo = Fbo::create2D(mpDevice, pSrc->getWidth(), pSrc->getHeight(), fboDesc, pSrc->getArraySize()); } } @@ -205,7 +206,7 @@ void GaussianBlur::updateKernel() sum += (i == 0) ? weights[i] : 2 * weights[i]; } - Buffer::SharedPtr pBuf = Buffer::createTyped(mpDevice.get(), mKernelWidth, Resource::BindFlags::ShaderResource); + ref pBuf = Buffer::createTyped(mpDevice, mKernelWidth, Resource::BindFlags::ShaderResource); for (uint32_t i = 0; i <= center; i++) { @@ -214,5 +215,5 @@ void GaussianBlur::updateKernel() pBuf->setElement(center - i, w); } - mpHorizontalBlur["weights"] = pBuf; + mpHorizontalBlur->getRootVar()["weights"] = pBuf; } diff --git a/Source/RenderPasses/Utils/GaussianBlur/GaussianBlur.h b/Source/RenderPasses/Utils/GaussianBlur/GaussianBlur.h index c6fe3f831..c1eb3e2d7 100644 --- a/Source/RenderPasses/Utils/GaussianBlur/GaussianBlur.h +++ b/Source/RenderPasses/Utils/GaussianBlur/GaussianBlur.h @@ -27,7 +27,8 @@ **************************************************************************/ #pragma once #include "Falcor.h" -#include "RenderGraph/BasePasses/FullScreenPass.h" +#include "Core/Pass/FullScreenPass.h" +#include "RenderGraph/RenderPass.h" using namespace Falcor; @@ -36,9 +37,9 @@ class GaussianBlur : public RenderPass public: FALCOR_PLUGIN_CLASS(GaussianBlur, "GaussianBlur", "Gaussian blur."); - using SharedPtr = std::shared_ptr; + static ref create(ref pDevice, const Dictionary& dict) { return make_ref(pDevice, dict); } - static SharedPtr create(std::shared_ptr pDevice, const Dictionary& dict); + GaussianBlur(ref pDevice, const Dictionary& dict); virtual Dictionary getScriptingDictionary() override; virtual RenderPassReflection reflect(const CompileData& compileData) override; @@ -54,16 +55,15 @@ class GaussianBlur : public RenderPass static void registerBindings(pybind11::module& m); private: - GaussianBlur(std::shared_ptr pDevice); 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; + ref mpHorizontalBlur; + ref mpVerticalBlur; + ref mpFbo; + ref mpTmpFbo; + ref mpSampler; }; diff --git a/Source/RenderPasses/Utils/Utils.cpp b/Source/RenderPasses/Utils/Utils.cpp index f189b8486..84ecdca84 100644 --- a/Source/RenderPasses/Utils/Utils.cpp +++ b/Source/RenderPasses/Utils/Utils.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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,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 "CrossFade/CrossFade.h" #include "Composite/Composite.h" #include "GaussianBlur/GaussianBlur.h" extern "C" FALCOR_API_EXPORT void registerPlugin(Falcor::PluginRegistry& registry) { + registry.registerClass(); + registry.registerClass(); ScriptBindings::registerBinding(Composite::registerBindings); diff --git a/Source/RenderPasses/WhittedRayTracer/WhittedRayTracer.cpp b/Source/RenderPasses/WhittedRayTracer/WhittedRayTracer.cpp index 0610dece3..c18ddb005 100644 --- a/Source/RenderPasses/WhittedRayTracer/WhittedRayTracer.cpp +++ b/Source/RenderPasses/WhittedRayTracer/WhittedRayTracer.cpp @@ -92,7 +92,7 @@ extern "C" FALCOR_API_EXPORT void registerPlugin(Falcor::PluginRegistry& registr void WhittedRayTracer::registerBindings(pybind11::module& m) { - pybind11::class_ pass(m, "WhittedRayTracer"); + pybind11::class_> pass(m, "WhittedRayTracer"); pybind11::enum_ rayConeMode(m, "RayConeMode"); rayConeMode.value("Combo", RayConeMode::Combo); @@ -104,13 +104,8 @@ void WhittedRayTracer::registerBindings(pybind11::module& m) rayConeFilterMode.value("AnisotropicWhenRefraction", RayFootprintFilterMode::AnisotropicWhenRefraction); } -WhittedRayTracer::SharedPtr WhittedRayTracer::create(std::shared_ptr pDevice, const Dictionary& dict) -{ - return SharedPtr(new WhittedRayTracer(std::move(pDevice), dict)); -} - -WhittedRayTracer::WhittedRayTracer(std::shared_ptr pDevice, const Dictionary& dict) - : RenderPass(std::move(pDevice)) +WhittedRayTracer::WhittedRayTracer(ref pDevice, const Dictionary& dict) + : RenderPass(pDevice) { // Parse dictionary. for (const auto& [key, value] : dict) @@ -225,19 +220,19 @@ void WhittedRayTracer::renderUI(Gui::Widgets& widget) dirty |= widget.var("Max bounces", mMaxBounces, 0u, 10u); widget.tooltip("Maximum path length for indirect illumination.\n0 = direct only\n1 = one indirect bounce etc.", true); - uint32_t modeIndex = static_cast(mTexLODMode); - if (widget.dropdown("Texture LOD mode", kTexLODModeList, modeIndex)) + uint32_t texLODModeIndex = static_cast(mTexLODMode); + if (widget.dropdown("Texture LOD mode", kTexLODModeList, texLODModeIndex)) { - setTexLODMode(TexLODMode(modeIndex)); + setTexLODMode(TexLODMode(texLODModeIndex)); dirty = true; } widget.tooltip("The texture level-of-detail mode to use."); if (mTexLODMode == TexLODMode::RayCones) { - uint32_t modeIndex = static_cast(mRayConeMode); - if (widget.dropdown("Ray cone mode", kRayConeModeList, modeIndex)) + uint32_t rayConeModeIndex = static_cast(mRayConeMode); + if (widget.dropdown("Ray cone mode", kRayConeModeList, rayConeModeIndex)) { - setRayConeMode(RayConeMode(modeIndex)); + setRayConeMode(RayConeMode(rayConeModeIndex)); dirty = true; } widget.tooltip("The variant of ray cones to use."); @@ -277,7 +272,7 @@ void WhittedRayTracer::renderUI(Gui::Widgets& widget) } } -void WhittedRayTracer::setScene(RenderContext* pRenderContext, const Scene::SharedPtr& pScene) +void WhittedRayTracer::setScene(RenderContext* pRenderContext, const ref& pScene) { // Clear data for previous scene. // After changing scene, the raytracing program should to be recreated. diff --git a/Source/RenderPasses/WhittedRayTracer/WhittedRayTracer.h b/Source/RenderPasses/WhittedRayTracer/WhittedRayTracer.h index 8ffead137..36414de39 100644 --- a/Source/RenderPasses/WhittedRayTracer/WhittedRayTracer.h +++ b/Source/RenderPasses/WhittedRayTracer/WhittedRayTracer.h @@ -27,6 +27,7 @@ **************************************************************************/ #pragma once #include "Falcor.h" +#include "RenderGraph/RenderPass.h" #include "RenderGraph/RenderPassHelpers.h" #include "Utils/Sampling/SampleGenerator.h" #include "Rendering/Materials/TexLODTypes.slang" // Using the enum with Mip0, RayCones, etc @@ -37,21 +38,25 @@ using namespace Falcor; /** Whitted ray tracer. This pass implements the simplest possible Whitted ray tracer. + + The render pass serves as an example and testbed for texture LOD. + The scene materials are overridden to add ideal specular reflection + and refraction components. Unbiased rendering should not be expected. */ class WhittedRayTracer : public RenderPass { public: FALCOR_PLUGIN_CLASS(WhittedRayTracer, "WhittedRayTracer", "Simple Whitted ray tracer."); - using SharedPtr = std::shared_ptr; + static ref create(ref pDevice, const Dictionary& dict) { return make_ref(pDevice, dict); } - static SharedPtr create(std::shared_ptr pDevice, const Dictionary& dict); + WhittedRayTracer(ref pDevice, const Dictionary& dict); 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 void setScene(RenderContext* pRenderContext, const ref& pScene) override; virtual bool onMouseEvent(const MouseEvent& mouseEvent) override { return false; } virtual bool onKeyEvent(const KeyboardEvent& keyEvent) override { return false; } @@ -70,14 +75,12 @@ class WhittedRayTracer : public RenderPass static void registerBindings(pybind11::module& m); private: - WhittedRayTracer(std::shared_ptr pDevice, const Dictionary& dict); - void prepareVars(); void setStaticParams(RtProgram* pProgram) const; // Internal state - Scene::SharedPtr mpScene; ///< Current scene. - SampleGenerator::SharedPtr mpSampleGenerator; ///< GPU sample generator. + ref mpScene; ///< Current scene. + ref mpSampleGenerator; ///< GPU sample generator. uint mMaxBounces = 3; ///< Max number of indirect bounces (0 = none). TexLODMode mTexLODMode = TexLODMode::Mip0; ///< Which texture LOD mode to use. @@ -94,8 +97,8 @@ class WhittedRayTracer : public RenderPass // Ray tracing program. struct { - RtProgram::SharedPtr pProgram; - RtBindingTable::SharedPtr pBindingTable; - RtProgramVars::SharedPtr pVars; + ref pProgram; + ref pBindingTable; + ref pVars; } mTracer; }; diff --git a/Source/RenderPasses/WhittedRayTracer/WhittedRayTracer.rt.slang b/Source/RenderPasses/WhittedRayTracer/WhittedRayTracer.rt.slang index cf393d3e4..3013829a2 100644 --- a/Source/RenderPasses/WhittedRayTracer/WhittedRayTracer.rt.slang +++ b/Source/RenderPasses/WhittedRayTracer/WhittedRayTracer.rt.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -109,6 +109,14 @@ float roughnessToSpread(float roughness) return 2.0f * sqrt(sigma2); } +/** Returns the shading normal flipped for backfacing hits on double-sided materials. + This reproduces the legacy behavior before the conventions were updated. +*/ +float3 getFlippedShadingNormal(const ShadingData sd) +{ + return !sd.frontFacing && sd.mtl.isDoubleSided() ? -sd.frame.N : sd.frame.N; +} + /** Payload for scatter ray (128B when ray cones are used, 164B when ray diffs are used). */ struct ScatterRayData @@ -181,8 +189,7 @@ ShadingData loadShadingData(const uint2 pixel, const float3 posW, const float3 r v.coneTexLODValue = 0.f; // Prepare shading data. - // Set 'useNormalMap' parameter to false as normal mapping has already been applied when generating the G-buffer. - return gScene.materials.prepareShadingData(v, materialID, -rayDir, lod, false); + return gScene.materials.prepareShadingData(v, materialID, -rayDir, lod); } /** Helper for computing relatice index of refraction (eta) at a hit. @@ -253,7 +260,7 @@ float3 evalDirectAnalytic(const ShadingData sd, const IMaterialInstance mi, cons bool valid = sampleLight(rayOrigin, gScene.getLight(lightIndex), sg, ls); // Reject sample if lower hemisphere. - if (valid && dot(ls.dir, sd.N) > kMinCosTheta) + if (valid && dot(ls.dir, getFlippedShadingNormal(sd)) > kMinCosTheta) { // Test visibility by tracing a shadow ray. bool V = traceShadowRay(rayOrigin, ls.dir, ls.distance); @@ -433,7 +440,7 @@ void scatterClosestHit(inout ScatterRayData rayData : SV_RayPayload, BuiltInTria if (eta != 1.0f) { - if (!refractWithTIR(rayDir, sd.N, eta, refractedRayDir)) + if (!refractWithTIR(rayDir, getFlippedShadingNormal(sd), eta, refractedRayDir)) { tir = true; } @@ -460,7 +467,7 @@ void scatterClosestHit(inout ScatterRayData rayData : SV_RayPayload, BuiltInTria } else // Refraction { - if (!refractRayCone(rayData.rayCone, rayOrg, rayDir, sd.posW, sd.N, surfaceSpreadAngle, eta, refractedRayDir)) + if (!refractRayCone(rayData.rayCone, rayOrg, rayDir, sd.posW, getFlippedShadingNormal(sd), surfaceSpreadAngle, eta, refractedRayDir)) { rayData.rayCone = rayData.rayCone.addToSpreadAngle(surfaceSpreadAngle); tir = true; @@ -504,9 +511,9 @@ void scatterClosestHit(inout ScatterRayData rayData : SV_RayPayload, BuiltInTria if (kUseFresnelAsBRDF) { - rayData.thp *= 1.0f - evalFresnelSchlick(bsdfProperties.specularReflectance, 1.0f, max(0.00001f, dot(sd.N, -rayDir))); + rayData.thp *= 1.0f - evalFresnelSchlick(bsdfProperties.specularReflectance, 1.0f, max(0.00001f, dot(getFlippedShadingNormal(sd), -rayDir))); // There's convinient 'evalFresnelDielectric' we could use here, but it's somewhat darker (worse for development) - //rayData.thp *= 1.0f - evalFresnelDielectric(eta, dot(sd.N, -rayDir)); + //rayData.thp *= 1.0f - evalFresnelDielectric(eta, dot(getFlippedShadingNormal(sd), -rayDir)); } else { @@ -628,7 +635,7 @@ void rayGen() prepareVerticesForRayDiffs(rayDir, vertices, worldMat, worldInvTransposeMat, barycentrics, edge1, edge2, normals, unnormalizedN, txcoords); computeRayDirectionDifferentials(nonNormalizedRayDir, gScene.camera.data.cameraU, gScene.camera.data.cameraV, launchDim, dDdx, dDdy); RayDiff rd = RayDiff(float3(0.f), float3(0.f), dDdx, dDdy); // Init ray diff. dOdx = 0 , dOdy = 0, and the directions are from above. - rayDiff = rd.propagate(worldPos.xyz, rayDir, hitT, sd.N); // Propagate the ray differential to the current hit point. + rayDiff = rd.propagate(worldPos.xyz, rayDir, hitT, getFlippedShadingNormal(sd)); // Propagate the ray differential to the current hit point. computeBarycentricDifferentials(rayDiff, rayDir, edge1, edge2, sd.faceN, dBarydx, dBarydy); computeNormalDifferentials(rayDiff, unnormalizedN, dBarydx, dBarydy, normals, dNdx, dNdy); float3 right = normalize(gScene.camera.computeNonNormalizedRayDirPinhole(launchIndex + uint2(1, 0), launchDim)) - rayDir; @@ -639,7 +646,7 @@ void rayGen() { float curvature = gScene.computeCurvatureIsotropicFirstHit(triangleHit.instanceID, triangleHit.primitiveIndex, rayDir); float rayConeWidth = hitT * gScreenSpacePixelSpreadAngle; - surfaceSpreadAngle = computeSpreadAngleFromCurvatureIso(curvature, hitT * gScreenSpacePixelSpreadAngle, rayDir, sd.N); + surfaceSpreadAngle = computeSpreadAngleFromCurvatureIso(curvature, hitT * gScreenSpacePixelSpreadAngle, rayDir, getFlippedShadingNormal(sd)); } #if USE_ROUGHNESS_TO_VARIANCE @@ -664,7 +671,7 @@ void rayGen() else // Refraction { float3 hitPoint = rayOrg + rayDir * hitT; - if (!refractRayCone(rayData.rayCone, rayOrg, rayDir, hitPoint, sd.N, surfaceSpreadAngle, eta, refractedRayDir)) + if (!refractRayCone(rayData.rayCone, rayOrg, rayDir, hitPoint, getFlippedShadingNormal(sd), surfaceSpreadAngle, eta, refractedRayDir)) { rayData.rayCone = rayData.rayCone.addToSpreadAngle(surfaceSpreadAngle); tir = true; @@ -717,7 +724,7 @@ void rayGen() } else // Mip0. { - if (!refractWithTIR(rayDir, sd.N, eta, refractedRayDir)) + if (!refractWithTIR(rayDir, getFlippedShadingNormal(sd), eta, refractedRayDir)) { tir = true; } @@ -726,7 +733,7 @@ void rayGen() // Generate scatter ray. if (eta == 1.0f || tir) { - generateReflectionRay(sd, bsdfProperties, rayOriginAtHitPoint, rayDir, sd.N, tir, rayData); + generateReflectionRay(sd, bsdfProperties, rayOriginAtHitPoint, rayDir, getFlippedShadingNormal(sd), tir, rayData); } else { @@ -735,10 +742,10 @@ void rayGen() if (kUseFresnelAsBRDF) { - rayData.thp *= 1.0f - evalFresnelSchlick(bsdfProperties.specularReflectance, 1.0f, max(0.00001f, dot(sd.N, -rayDir))); + rayData.thp *= 1.0f - evalFresnelSchlick(bsdfProperties.specularReflectance, 1.0f, max(0.00001f, dot(getFlippedShadingNormal(sd), -rayDir))); // There's convenient 'evalFresnelDielectric' we could use here, but it's somewhat darker (worse for development) - //rayData.thp *= 1.0f - evalFresnelDielectric(eta, dot(sd.N, -rayDir)); + //rayData.thp *= 1.0f - evalFresnelDielectric(eta, dot(getFlippedShadingNormal(sd), -rayDir)); } else { diff --git a/Source/Samples/CudaInterop/CudaInterop.cpp b/Source/Samples/CudaInterop/CudaInterop.cpp index cada63ec0..d67d47e24 100644 --- a/Source/Samples/CudaInterop/CudaInterop.cpp +++ b/Source/Samples/CudaInterop/CudaInterop.cpp @@ -44,15 +44,14 @@ void CudaInterop::onLoad(RenderContext* pRenderContext) throw RuntimeError("CUDA driver API initialization failed."); // Create our input and output textures - mpInputTex = Texture::createFromFile(getDevice().get(), kTextureFilename, false, false, ResourceBindFlags::Shared); + mpInputTex = Texture::createFromFile(getDevice(), kTextureFilename, false, false, ResourceBindFlags::Shared); if (!mpInputTex) throw RuntimeError("Failed to load texture '{}'", kTextureFilename); mWidth = mpInputTex->getWidth(); mHeight = mpInputTex->getHeight(); mpOutputTex = Texture::create2D( - getDevice().get(), mWidth, mHeight, mpInputTex->getFormat(), 1, 1, nullptr, - ResourceBindFlags::Shared | ResourceBindFlags::ShaderResource + getDevice(), 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 @@ -69,7 +68,7 @@ void CudaInterop::onLoad(RenderContext* pRenderContext) throw RuntimeError("Output texture to surface mapping failed"); } -void CudaInterop::onFrameRender(RenderContext* pRenderContext, const Fbo::SharedPtr& pTargetFbo) +void CudaInterop::onFrameRender(RenderContext* pRenderContext, const ref& pTargetFbo) { const Falcor::float4 clearColor(0.38f, 0.52f, 0.10f, 1); pRenderContext->clearFbo(pTargetFbo.get(), clearColor, 1.0f, 0, FboAttachmentType::All); diff --git a/Source/Samples/CudaInterop/CudaInterop.h b/Source/Samples/CudaInterop/CudaInterop.h index 4f7a97a8a..2361684e7 100644 --- a/Source/Samples/CudaInterop/CudaInterop.h +++ b/Source/Samples/CudaInterop/CudaInterop.h @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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,13 +40,13 @@ class CudaInterop : public SampleApp ~CudaInterop(); void onLoad(RenderContext* pRenderContext) override; - void onFrameRender(RenderContext* pRenderContext, const Fbo::SharedPtr& pTargetFbo) override; + void onFrameRender(RenderContext* pRenderContext, const ref& pTargetFbo) override; private: uint32_t mWidth; uint32_t mHeight; - Texture::SharedPtr mpInputTex; - Texture::SharedPtr mpOutputTex; + ref mpInputTex; + ref mpOutputTex; cudaSurfaceObject_t mInputSurf; cudaSurfaceObject_t mOutputSurf; }; diff --git a/Source/Samples/CudaInterop/FalcorCUDA.cpp b/Source/Samples/CudaInterop/FalcorCUDA.cpp index 1bfed668f..6901b49b0 100644 --- a/Source/Samples/CudaInterop/FalcorCUDA.cpp +++ b/Source/Samples/CudaInterop/FalcorCUDA.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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,8 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **************************************************************************/ #include "FalcorCUDA.h" -#include -#include -#include #include "Core/API/Device.h" +#include #define CU_CHECK_SUCCESS(x) \ do \ @@ -59,64 +57,10 @@ using namespace Falcor; namespace { - -class WindowsSecurityAttributes -{ -protected: - SECURITY_ATTRIBUTES mWinSecurityAttributes; - PSECURITY_DESCRIPTOR mWinPSecurityDescriptor; - -public: - WindowsSecurityAttributes() - { - mWinPSecurityDescriptor = (PSECURITY_DESCRIPTOR)calloc(1, SECURITY_DESCRIPTOR_MIN_LENGTH + 2 * sizeof(void**)); - FALCOR_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() - { - 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 namespace FalcorCUDA @@ -153,9 +97,9 @@ bool initCUDA() return true; } -bool importTextureToMipmappedArray(Falcor::Texture::SharedPtr pTex, cudaMipmappedArray_t& mipmappedArray, uint32_t cudaUsageFlags) +bool importTextureToMipmappedArray(Falcor::ref pTex, cudaMipmappedArray_t& mipmappedArray, uint32_t cudaUsageFlags) { - HANDLE sharedHandle = pTex->getSharedApiHandle(); + SharedResourceApiHandle sharedHandle = pTex->getSharedApiHandle(); if (sharedHandle == NULL) { reportError("FalcorCUDA::importTextureToMipmappedArray - texture shared handle creation failed"); @@ -192,7 +136,7 @@ bool importTextureToMipmappedArray(Falcor::Texture::SharedPtr pTex, cudaMipmappe return true; } -cudaSurfaceObject_t mapTextureToSurface(Texture::SharedPtr pTex, uint32_t cudaUsageFlags) +cudaSurfaceObject_t mapTextureToSurface(ref pTex, uint32_t cudaUsageFlags) { // Create a mipmapped array from the texture cudaMipmappedArray_t mipmap; diff --git a/Source/Samples/CudaInterop/FalcorCUDA.h b/Source/Samples/CudaInterop/FalcorCUDA.h index a08c07c23..8469a8092 100644 --- a/Source/Samples/CudaInterop/FalcorCUDA.h +++ b/Source/Samples/CudaInterop/FalcorCUDA.h @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -47,7 +47,7 @@ bool initCUDA(); * @param usageFlags The requested flags to be bound to the mipmapped array * @return True if successful, false otherwise */ -bool importTextureToMipmappedArray(Falcor::Texture::SharedPtr pTex, cudaMipmappedArray_t& mipmappedArray, uint32_t cudaUsageFlags); +bool importTextureToMipmappedArray(Falcor::ref pTex, cudaMipmappedArray_t& mipmappedArray, uint32_t cudaUsageFlags); /** * Maps a texture to a surface object which can be read and written within a CUDA kernel. @@ -58,6 +58,6 @@ bool importTextureToMipmappedArray(Falcor::Texture::SharedPtr pTex, cudaMipmappe * surface object * @return The surface object that the input texture is bound to */ -cudaSurfaceObject_t mapTextureToSurface(Falcor::Texture::SharedPtr pTex, uint32_t usageFlags); +cudaSurfaceObject_t mapTextureToSurface(Falcor::ref pTex, uint32_t usageFlags); }; // namespace FalcorCUDA diff --git a/Source/Samples/HelloDXR/HelloDXR.cpp b/Source/Samples/HelloDXR/HelloDXR.cpp index 1613e98e9..a6c32d095 100644 --- a/Source/Samples/HelloDXR/HelloDXR.cpp +++ b/Source/Samples/HelloDXR/HelloDXR.cpp @@ -59,25 +59,28 @@ void HelloDXR::onResize(uint32_t width, uint32_t height) } mpRtOut = Texture::create2D( - getDevice().get(), width, height, ResourceFormat::RGBA16Float, 1, 1, nullptr, + getDevice(), width, height, ResourceFormat::RGBA16Float, 1, 1, nullptr, Resource::BindFlags::UnorderedAccess | Resource::BindFlags::ShaderResource ); } -void HelloDXR::onFrameRender(RenderContext* pRenderContext, const Fbo::SharedPtr& pTargetFbo) +void HelloDXR::onFrameRender(RenderContext* pRenderContext, const ref& pTargetFbo) { pRenderContext->clearFbo(pTargetFbo.get(), kClearColor, 1.0f, 0, FboAttachmentType::All); if (mpScene) { - mpScene->update(pRenderContext, getGlobalClock().getTime()); + Scene::UpdateFlags updates = mpScene->update(pRenderContext, getGlobalClock().getTime()); + if (is_set(updates, Scene::UpdateFlags::GeometryChanged)) + throw RuntimeError("This sample does not support scene geometry changes."); + if (mRayTrace) - renderRT(pRenderContext, pTargetFbo.get()); + renderRT(pRenderContext, pTargetFbo); else - mpRasterPass->renderScene(pRenderContext, pTargetFbo); + renderRaster(pRenderContext, pTargetFbo); } - TextRenderer::render(pRenderContext, getFrameRate().getMsg(), pTargetFbo, {20, 20}); + getTextRenderer().render(pRenderContext, getFrameRate().getMsg(), pTargetFbo, {20, 20}); } void HelloDXR::onGuiRender(Gui* pGui) @@ -145,7 +148,7 @@ void HelloDXR::loadScene(const std::filesystem::path& path, const Fbo* pTargetFb rasterProgDesc.addShaderLibrary("Samples/HelloDXR/HelloDXR.3d.slang").vsEntry("vsMain").psEntry("psMain"); rasterProgDesc.addTypeConformances(typeConformances); - mpRasterPass = RasterScenePass::create(getDevice(), mpScene, rasterProgDesc, defines); + mpRasterPass = RasterPass::create(getDevice(), rasterProgDesc, defines); // We'll now create a raytracing program. To do that we need to setup two things: // - A program description (RtProgram::Desc). This holds all shader entry points, compiler flags, macro defintions, @@ -168,7 +171,7 @@ void HelloDXR::loadScene(const std::filesystem::path& path, const Fbo* pTargetFb rtProgDesc.setMaxPayloadSize(24); // The largest ray payload struct (PrimaryRayData) is 24 bytes. The payload size // should be set as small as possible for maximum performance. - RtBindingTable::SharedPtr sbt = RtBindingTable::create(2, 2, mpScene->getGeometryCount()); + ref sbt = RtBindingTable::create(2, 2, mpScene->getGeometryCount()); sbt->setRayGen(rtProgDesc.addRayGen("rayGen")); sbt->setMiss(0, rtProgDesc.addMiss("primaryMiss")); sbt->setMiss(1, rtProgDesc.addMiss("shadowMiss")); @@ -183,27 +186,31 @@ void HelloDXR::loadScene(const std::filesystem::path& path, const Fbo* pTargetFb void HelloDXR::setPerFrameVars(const Fbo* pTargetFbo) { - auto cb = mpRtVars["PerFrameCB"]; - cb["invView"] = rmcv::inverse(mpCamera->getViewMatrix()); - cb["viewportDims"] = float2(pTargetFbo->getWidth(), pTargetFbo->getHeight()); + auto var = mpRtVars->getRootVar(); + var["PerFrameCB"]["invView"] = inverse(mpCamera->getViewMatrix()); + var["PerFrameCB"]["viewportDims"] = float2(pTargetFbo->getWidth(), pTargetFbo->getHeight()); float fovY = focalLengthToFovY(mpCamera->getFocalLength(), Camera::kDefaultFrameHeight); - cb["tanHalfFovY"] = std::tan(fovY * 0.5f); - cb["sampleIndex"] = mSampleIndex++; - cb["useDOF"] = mUseDOF; - mpRtVars["gOutput"] = mpRtOut; + var["PerFrameCB"]["tanHalfFovY"] = std::tan(fovY * 0.5f); + var["PerFrameCB"]["sampleIndex"] = mSampleIndex++; + var["PerFrameCB"]["useDOF"] = mUseDOF; + var["gOutput"] = mpRtOut; } -void HelloDXR::renderRT(RenderContext* pRenderContext, const Fbo* pTargetFbo) +void HelloDXR::renderRaster(RenderContext* pRenderContext, const ref& pTargetFbo) { - FALCOR_PROFILE(pRenderContext, "renderRT"); + FALCOR_ASSERT(mpScene); + FALCOR_PROFILE(pRenderContext, "renderRaster"); + + mpRasterPass->getState()->setFbo(pTargetFbo); + mpScene->rasterize(pRenderContext, mpRasterPass->getState().get(), mpRasterPass->getVars().get()); +} +void HelloDXR::renderRT(RenderContext* pRenderContext, const ref& pTargetFbo) +{ FALCOR_ASSERT(mpScene); - if (is_set(mpScene->getUpdates(), Scene::UpdateFlags::GeometryChanged)) - { - throw RuntimeError("This sample does not support scene geometry changes."); - } + FALCOR_PROFILE(pRenderContext, "renderRT"); - setPerFrameVars(pTargetFbo); + setPerFrameVars(pTargetFbo.get()); pRenderContext->clearUAV(mpRtOut->getUAV().get(), kClearColor); mpScene->raytrace(pRenderContext, mpRaytraceProgram.get(), mpRtVars, uint3(pTargetFbo->getWidth(), pTargetFbo->getHeight(), 1)); diff --git a/Source/Samples/HelloDXR/HelloDXR.h b/Source/Samples/HelloDXR/HelloDXR.h index 15a9a6319..1e1cf3397 100644 --- a/Source/Samples/HelloDXR/HelloDXR.h +++ b/Source/Samples/HelloDXR/HelloDXR.h @@ -28,7 +28,7 @@ #pragma once #include "Falcor.h" #include "Core/SampleApp.h" -#include "RenderGraph/BasePasses/RasterScenePass.h" +#include "Core/Pass/RasterPass.h" using namespace Falcor; @@ -40,7 +40,7 @@ class HelloDXR : public SampleApp void onLoad(RenderContext* pRenderContext) override; void onResize(uint32_t width, uint32_t height) override; - void onFrameRender(RenderContext* pRenderContext, const Fbo::SharedPtr& pTargetFbo) override; + void onFrameRender(RenderContext* pRenderContext, const ref& pTargetFbo) override; void onGuiRender(Gui* pGui) override; bool onKeyEvent(const KeyboardEvent& keyEvent) override; bool onMouseEvent(const MouseEvent& mouseEvent) override; @@ -48,18 +48,20 @@ class HelloDXR : public SampleApp private: void loadScene(const std::filesystem::path& path, const Fbo* pTargetFbo); void setPerFrameVars(const Fbo* pTargetFbo); - void renderRT(RenderContext* pRenderContext, const Fbo* pTargetFbo); + void renderRaster(RenderContext* pRenderContext, const ref& pTargetFbo); + void renderRT(RenderContext* pRenderContext, const ref& pTargetFbo); - RasterScenePass::SharedPtr mpRasterPass; - Scene::SharedPtr mpScene; + ref mpScene; + ref mpCamera; - RtProgram::SharedPtr mpRaytraceProgram = nullptr; - Camera::SharedPtr mpCamera; + ref mpRasterPass; + + ref mpRaytraceProgram; + ref mpRtVars; + ref mpRtOut; bool mRayTrace = true; bool mUseDOF = false; - RtProgramVars::SharedPtr mpRtVars; - Texture::SharedPtr mpRtOut; uint32_t mSampleIndex = 0xdeadbeef; }; diff --git a/Source/Samples/SampleAppTemplate/SampleAppTemplate.cpp b/Source/Samples/SampleAppTemplate/SampleAppTemplate.cpp index d221b9fc1..ff906e434 100644 --- a/Source/Samples/SampleAppTemplate/SampleAppTemplate.cpp +++ b/Source/Samples/SampleAppTemplate/SampleAppTemplate.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -59,7 +59,7 @@ void SampleAppTemplate::onResize(uint32_t width, uint32_t height) // } -void SampleAppTemplate::onFrameRender(RenderContext* pRenderContext, const Fbo::SharedPtr& pTargetFbo) +void SampleAppTemplate::onFrameRender(RenderContext* pRenderContext, const ref& pTargetFbo) { const float4 clearColor(0.38f, 0.52f, 0.10f, 1); pRenderContext->clearFbo(pTargetFbo.get(), clearColor, 1.0f, 0, FboAttachmentType::All); diff --git a/Source/Samples/SampleAppTemplate/SampleAppTemplate.h b/Source/Samples/SampleAppTemplate/SampleAppTemplate.h index 43f213c50..11968b2bb 100644 --- a/Source/Samples/SampleAppTemplate/SampleAppTemplate.h +++ b/Source/Samples/SampleAppTemplate/SampleAppTemplate.h @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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,7 +40,7 @@ class SampleAppTemplate : public SampleApp void onLoad(RenderContext* pRenderContext) override; void onShutdown() override; void onResize(uint32_t width, uint32_t height) override; - void onFrameRender(RenderContext* pRenderContext, const Fbo::SharedPtr& pTargetFbo) override; + void onFrameRender(RenderContext* pRenderContext, const ref& pTargetFbo) override; void onGuiRender(Gui* pGui) override; bool onKeyEvent(const KeyboardEvent& keyEvent) override; bool onMouseEvent(const MouseEvent& mouseEvent) override; diff --git a/Source/Samples/ShaderToy/ShaderToy.cpp b/Source/Samples/ShaderToy/ShaderToy.cpp index 4aa42db11..fd1f16051 100644 --- a/Source/Samples/ShaderToy/ShaderToy.cpp +++ b/Source/Samples/ShaderToy/ShaderToy.cpp @@ -49,7 +49,7 @@ void ShaderToy::onLoad(RenderContext* pRenderContext) // Texture sampler Sampler::Desc samplerDesc; samplerDesc.setFilterMode(Sampler::Filter::Linear, Sampler::Filter::Linear, Sampler::Filter::Linear).setMaxAnisotropy(8); - mpLinearSampler = Sampler::create(getDevice().get(), samplerDesc); + mpLinearSampler = Sampler::create(getDevice(), samplerDesc); // Load shaders mpMainPass = FullScreenPass::create(getDevice(), "Samples/ShaderToy/Toy.ps.slang"); @@ -60,13 +60,14 @@ void ShaderToy::onResize(uint32_t width, uint32_t height) mAspectRatio = (float(width) / float(height)); } -void ShaderToy::onFrameRender(RenderContext* pRenderContext, const Fbo::SharedPtr& pTargetFbo) +void ShaderToy::onFrameRender(RenderContext* pRenderContext, const ref& pTargetFbo) { // iResolution float width = (float)pTargetFbo->getWidth(); float height = (float)pTargetFbo->getHeight(); - mpMainPass["ToyCB"]["iResolution"] = float2(width, height); - mpMainPass["ToyCB"]["iGlobalTime"] = (float)getGlobalClock().getTime(); + auto var = mpMainPass->getRootVar()["ToyCB"]; + var["iResolution"] = float2(width, height); + var["iGlobalTime"] = (float)getGlobalClock().getTime(); // run final pass mpMainPass->execute(pRenderContext, pTargetFbo); diff --git a/Source/Samples/ShaderToy/ShaderToy.h b/Source/Samples/ShaderToy/ShaderToy.h index 293cc2129..ca8cafa7e 100644 --- a/Source/Samples/ShaderToy/ShaderToy.h +++ b/Source/Samples/ShaderToy/ShaderToy.h @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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,7 @@ #pragma once #include "Falcor.h" #include "Core/SampleApp.h" -#include "RenderGraph/BasePasses/FullScreenPass.h" +#include "Core/Pass/FullScreenPass.h" using namespace Falcor; @@ -40,13 +40,13 @@ class ShaderToy : public SampleApp void onLoad(RenderContext* pRenderContext) override; void onResize(uint32_t width, uint32_t height) override; - void onFrameRender(RenderContext* pRenderContext, const Fbo::SharedPtr& pTargetFbo) override; + void onFrameRender(RenderContext* pRenderContext, const ref& pTargetFbo) override; private: - Sampler::SharedPtr mpLinearSampler; + ref mpLinearSampler; float mAspectRatio = 0; - RasterizerState::SharedPtr mpNoCullRastState; - DepthStencilState::SharedPtr mpNoDepthDS; - BlendState::SharedPtr mpOpaqueBS; - FullScreenPass::SharedPtr mpMainPass; + ref mpNoCullRastState; + ref mpNoDepthDS; + ref mpOpaqueBS; + ref mpMainPass; }; diff --git a/Source/Samples/Visualization2D/Visualization2D.cpp b/Source/Samples/Visualization2D/Visualization2D.cpp index ebb6d6a6d..5060a417a 100644 --- a/Source/Samples/Visualization2D/Visualization2D.cpp +++ b/Source/Samples/Visualization2D/Visualization2D.cpp @@ -47,24 +47,25 @@ void Visualization2D::onLoad(RenderContext* pRenderContext) createRenderPass(); } -void Visualization2D::onFrameRender(RenderContext* pRenderContext, const Fbo::SharedPtr& pTargetFbo) +void Visualization2D::onFrameRender(RenderContext* pRenderContext, const ref& pTargetFbo) { float width = (float)pTargetFbo->getWidth(); float height = (float)pTargetFbo->getHeight(); - mpMainPass["Visual2DCB"]["iResolution"] = float2(width, height); - mpMainPass["Visual2DCB"]["iGlobalTime"] = (float)getGlobalClock().getTime(); - mpMainPass["Visual2DCB"]["iMousePosition"] = mMousePosition; + auto var = mpMainPass->getRootVar(); + var["Visual2DCB"]["iResolution"] = float2(width, height); + var["Visual2DCB"]["iGlobalTime"] = (float)getGlobalClock().getTime(); + var["Visual2DCB"]["iMousePosition"] = mMousePosition; switch (mSelectedScene) { case Scene::MarkerDemo: break; case Scene::VoxelNormals: - mpMainPass["VoxelNormalsCB"]["iShowNormalField"] = mVoxelNormalsGUI.showNormalField; - mpMainPass["VoxelNormalsCB"]["iShowBoxes"] = mVoxelNormalsGUI.showBoxes; - mpMainPass["VoxelNormalsCB"]["iShowBoxDiagonals"] = mVoxelNormalsGUI.showBoxDiagonals; - mpMainPass["VoxelNormalsCB"]["iShowBorderLines"] = mVoxelNormalsGUI.showBorderLines; - mpMainPass["VoxelNormalsCB"]["iShowBoxAroundPoint"] = mVoxelNormalsGUI.showBoxAroundPoint; + var["VoxelNormalsCB"]["iShowNormalField"] = mVoxelNormalsGUI.showNormalField; + var["VoxelNormalsCB"]["iShowBoxes"] = mVoxelNormalsGUI.showBoxes; + var["VoxelNormalsCB"]["iShowBoxDiagonals"] = mVoxelNormalsGUI.showBoxDiagonals; + var["VoxelNormalsCB"]["iShowBorderLines"] = mVoxelNormalsGUI.showBorderLines; + var["VoxelNormalsCB"]["iShowBoxAroundPoint"] = mVoxelNormalsGUI.showBoxAroundPoint; break; default: break; diff --git a/Source/Samples/Visualization2D/Visualization2D.h b/Source/Samples/Visualization2D/Visualization2D.h index 3d72564d5..354ba5b64 100644 --- a/Source/Samples/Visualization2D/Visualization2D.h +++ b/Source/Samples/Visualization2D/Visualization2D.h @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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,7 @@ #pragma once #include "Falcor.h" #include "Core/SampleApp.h" -#include "RenderGraph/BasePasses/FullScreenPass.h" +#include "Core/Pass/FullScreenPass.h" using namespace Falcor; @@ -48,7 +48,7 @@ class Visualization2D : public SampleApp ~Visualization2D(); void onLoad(RenderContext* pRenderContext) override; - void onFrameRender(RenderContext* pRenderContext, const Fbo::SharedPtr& pTargetFbo) override; + void onFrameRender(RenderContext* pRenderContext, const ref& pTargetFbo) override; void onGuiRender(Gui* pGui) override; bool onKeyEvent(const KeyboardEvent& keyEvent) override; bool onMouseEvent(const MouseEvent& mouseEvent) override; @@ -64,7 +64,7 @@ class Visualization2D : public SampleApp void createRenderPass(); private: - FullScreenPass::SharedPtr mpMainPass; + ref mpMainPass; bool mLeftButtonDown = false; float2 mMousePosition = float2(0.2f, 0.1f); diff --git a/Source/Tools/FalcorTest/CMakeLists.txt b/Source/Tools/FalcorTest/CMakeLists.txt index 351fd6770..dddfbe331 100644 --- a/Source/Tools/FalcorTest/CMakeLists.txt +++ b/Source/Tools/FalcorTest/CMakeLists.txt @@ -12,24 +12,25 @@ target_sources(FalcorTest PRIVATE Tests/Core/ConstantBufferTests.cs.slang Tests/Core/DDSReadTests.cpp Tests/Core/DDSReadTests.cs.slang - Tests/Core/ResourceAliasing.cpp - Tests/Core/ResourceAliasing.cs.slang Tests/Core/LargeBuffer.cpp Tests/Core/LargeBuffer.cs.slang + Tests/Core/ObjectTests.cpp Tests/Core/ParamBlockCB.cpp Tests/Core/ParamBlockCB.cs.slang Tests/Core/ParamBlockDefinition.slang Tests/Core/ParamBlockReflection.cs.slang Tests/Core/PluginTests.cpp + Tests/Core/ResourceAliasing.cpp + Tests/Core/ResourceAliasing.cs.slang Tests/Core/RootBufferParamBlockTests.cpp Tests/Core/RootBufferParamBlockTests.cs.slang Tests/Core/RootBufferStructTests.cpp Tests/Core/RootBufferStructTests.cs.slang Tests/Core/RootBufferTests.cpp Tests/Core/RootBufferTests.cs.slang + Tests/Core/TextureLoadTests.cs.slang Tests/Core/TextureTests.cpp Tests/Core/TextureTests.cs.slang - Tests/Core/TextureLoadTests.cs.slang Tests/DebugPasses/InvalidPixelDetectionTests.cpp @@ -116,6 +117,7 @@ target_sources(FalcorTest PRIVATE Tests/Utils/Debug/WarpProfilerTests.cs.slang Tests/Utils/Image/BitmapTests.cpp + Tests/Utils/Image/TextureManagerTests.cpp Tests/Utils/AABBTests.cpp Tests/Utils/AABBTests.cs.slang @@ -144,6 +146,7 @@ target_sources(FalcorTest PRIVATE Tests/Utils/ParallelReductionTests.cpp Tests/Utils/PathResolvingTests.cpp Tests/Utils/PrefixSumTests.cpp + Tests/Utils/QuaternionTests.cpp Tests/Utils/RectangleTests.cpp Tests/Utils/SettingsTests.cpp Tests/Utils/StringUtilsTests.cpp diff --git a/Source/Tools/FalcorTest/FalcorTest.cpp b/Source/Tools/FalcorTest/FalcorTest.cpp index 59a48bbd0..d80bf0461 100644 --- a/Source/Tools/FalcorTest/FalcorTest.cpp +++ b/Source/Tools/FalcorTest/FalcorTest.cpp @@ -41,7 +41,7 @@ FalcorTest::FalcorTest(const SampleAppConfig& config, const Options& options) : FalcorTest::~FalcorTest() {} -void FalcorTest::onFrameRender(RenderContext* pRenderContext, const Fbo::SharedPtr& pTargetFbo) +void FalcorTest::onFrameRender(RenderContext* pRenderContext, const ref& pTargetFbo) { int returnCode = runTests(getDevice(), getTargetFbo().get(), mOptions.categoryFlags, mOptions.filter, mOptions.xmlReportPath, mOptions.repeat); diff --git a/Source/Tools/FalcorTest/FalcorTest.h b/Source/Tools/FalcorTest/FalcorTest.h index dde849a76..0586c29e6 100644 --- a/Source/Tools/FalcorTest/FalcorTest.h +++ b/Source/Tools/FalcorTest/FalcorTest.h @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -46,7 +46,7 @@ class FalcorTest : public SampleApp FalcorTest(const SampleAppConfig& config, const Options& options); ~FalcorTest(); - void onFrameRender(RenderContext* pRenderContext, const Fbo::SharedPtr& pTargetFbo) override; + void onFrameRender(RenderContext* pRenderContext, const ref& pTargetFbo) override; private: Options mOptions; diff --git a/Source/Tools/FalcorTest/Tests/Core/BufferAccessTests.cpp b/Source/Tools/FalcorTest/Tests/Core/BufferAccessTests.cpp index 4f6c04f7f..06590883b 100644 --- a/Source/Tools/FalcorTest/Tests/Core/BufferAccessTests.cpp +++ b/Source/Tools/FalcorTest/Tests/Core/BufferAccessTests.cpp @@ -35,7 +35,7 @@ const uint32_t elems = 256; /** Create buffer with the given CPU access and elements initialized to 0,1,2,... */ -Buffer::SharedPtr createTestBuffer(Device* pDevice, Buffer::CpuAccess cpuAccess, bool initialize = true) +ref createTestBuffer(ref pDevice, Buffer::CpuAccess cpuAccess, bool initialize = true) { std::vector initData(elems); for (uint32_t i = 0; i < elems; i++) @@ -50,7 +50,7 @@ Buffer::SharedPtr createTestBuffer(Device* pDevice, Buffer::CpuAccess cpuAccess, */ void testBufferReadback(GPUUnitTestContext& ctx, Buffer::CpuAccess cpuAccess) { - auto pBuf = createTestBuffer(ctx.getDevice().get(), cpuAccess); + auto pBuf = createTestBuffer(ctx.getDevice(), cpuAccess); // Run program that copies the buffer elements into result buffer. ctx.createProgram("Tests/Core/BufferAccessTests.cs.slang", "readback", Program::DefineList(), Shader::CompilerFlags::None); @@ -72,7 +72,7 @@ void testBufferReadback(GPUUnitTestContext& ctx, Buffer::CpuAccess cpuAccess) */ GPU_TEST(CopyBufferCpuAccessWrite) { - Device* pDevice = ctx.getDevice().get(); + ref pDevice = ctx.getDevice(); auto pBuf = createTestBuffer(pDevice, Buffer::CpuAccess::Write); @@ -99,7 +99,7 @@ GPU_TEST(CopyBufferCpuAccessWrite) */ GPU_TEST(SetBlobBufferCpuAccessWrite, "Disabled due to issue with SRV/UAVs for resources on the upload heap (#638)") { - Device* pDevice = ctx.getDevice().get(); + ref pDevice = ctx.getDevice(); auto pBuf = createTestBuffer(pDevice, Buffer::CpuAccess::Write, false); diff --git a/Source/Tools/FalcorTest/Tests/Core/BufferTests.cpp b/Source/Tools/FalcorTest/Tests/Core/BufferTests.cpp index 705702786..cda38016c 100644 --- a/Source/Tools/FalcorTest/Tests/Core/BufferTests.cpp +++ b/Source/Tools/FalcorTest/Tests/Core/BufferTests.cpp @@ -46,7 +46,7 @@ void testBuffer(GPUUnitTestContext& ctx, uint32_t numElems, uint32_t index = 0, { static_assert(type == Type::ByteAddressBuffer || type == Type::TypedBuffer || type == Type::StructuredBuffer); - Device* pDevice = ctx.getDevice().get(); + ref pDevice = ctx.getDevice(); numElems = div_round_up(numElems, 256u) * 256u; // Make sure we run full thread groups. @@ -65,7 +65,7 @@ void testBuffer(GPUUnitTestContext& ctx, uint32_t numElems, uint32_t index = 0, ctx.createProgram("Tests/Core/BufferTests.cs.slang", "clearBuffer", defines); // Create test buffer. - Buffer::SharedPtr pBuffer; + ref pBuffer; if constexpr (type == Type::ByteAddressBuffer) pBuffer = Buffer::create(pDevice, numElems * sizeof(uint32_t), ResourceBindFlags::UnorderedAccess, Buffer::CpuAccess::None); else if constexpr (type == Type::TypedBuffer) @@ -166,7 +166,7 @@ GPU_TEST(BufferUpdate) const uint4 a = {1, 2, 3, 4}; const uint4 b = {5, 6, 7, 8}; - auto pBuffer = Buffer::create(ctx.getDevice().get(), 16); + auto pBuffer = Buffer::create(ctx.getDevice(), 16); pBuffer->setBlob(&a, 0, 16); diff --git a/Source/Tools/FalcorTest/Tests/Core/DDSReadTests.cpp b/Source/Tools/FalcorTest/Tests/Core/DDSReadTests.cpp index 9dea6eab6..12e808571 100644 --- a/Source/Tools/FalcorTest/Tests/Core/DDSReadTests.cpp +++ b/Source/Tools/FalcorTest/Tests/Core/DDSReadTests.cpp @@ -41,12 +41,12 @@ namespace { void testDDS(GPUUnitTestContext& ctx, const std::string& testName, ResourceFormat fmt, bool expectLoadFailure) { - Device* pDevice = ctx.getDevice().get(); + ref pDevice = ctx.getDevice(); // Read input DDS file std::filesystem::path ddsPath = getRuntimeDirectory() / fmt::format("data/tests/{}.dds", testName); - Texture::SharedPtr pDDSTex; + ref pDDSTex; // Note that we can always specify loadAsSrgb=false, even when fmt is sRGB, because // the flag is a no-op if the format encoded in the DDS file specifies a nonlinear format. try @@ -73,7 +73,7 @@ void testDDS(GPUUnitTestContext& ctx, const std::string& testName, ResourceForma // Read reference image. If no reference image exists, the test will fail, and a reference image will be output. std::filesystem::path refPath = getRuntimeDirectory() / fmt::format("data/tests/{}-ref.png", testName); - Texture::SharedPtr pPngTex; + ref pPngTex; if (std::filesystem::exists(refPath)) { pPngTex = Texture::createFromFile(pDevice, refPath, false, false); @@ -83,7 +83,7 @@ void testDDS(GPUUnitTestContext& ctx, const std::string& testName, ResourceForma EXPECT_EQ(pDDSTex->getFormat(), fmt); // Create uncompressed destination texture - Texture::SharedPtr pSrcTex = pDDSTex; + ref pSrcTex = pDDSTex; ResourceFormat destFormat = ResourceFormat::RGBA32Float; auto pDst = Texture::create2D( pDevice, pDDSTex->getWidth(), pDDSTex->getHeight(), destFormat, 1, 1, nullptr, @@ -117,15 +117,15 @@ void testDDS(GPUUnitTestContext& ctx, const std::string& testName, ResourceForma // Create texture from difference data const uint8_t* diff = ctx.mapBuffer("difference"); EXPECT(diff != nullptr); - Texture::SharedPtr pDiffTex(Texture::create2D(pDevice, dstDim.x, dstDim.y, ResourceFormat::RGBA32Float, 1, 1, diff)); + ref pDiffTex(Texture::create2D(pDevice, dstDim.x, dstDim.y, ResourceFormat::RGBA32Float, 1, 1, diff)); ctx.unmapBuffer("difference"); // Analyze difference texture - TextureAnalyzer::SharedPtr analyzer(TextureAnalyzer::create(ctx.getDevice())); + TextureAnalyzer analyzer(pDevice); auto pResultBuffer = Buffer::create( pDevice, TextureAnalyzer::getResultSize(), ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess ); - analyzer->analyze(ctx.getRenderContext(), pDiffTex, 0, 0, pResultBuffer); + analyzer.analyze(ctx.getRenderContext(), pDiffTex, 0, 0, pResultBuffer); const TextureAnalyzer::Result* result = static_cast(pResultBuffer->map(Buffer::MapType::Read)); // Expect difference image to be uniform 0. diff --git a/Source/Tools/FalcorTest/Tests/Core/LargeBuffer.cpp b/Source/Tools/FalcorTest/Tests/Core/LargeBuffer.cpp index 670b45ae8..36a7489d8 100644 --- a/Source/Tools/FalcorTest/Tests/Core/LargeBuffer.cpp +++ b/Source/Tools/FalcorTest/Tests/Core/LargeBuffer.cpp @@ -38,7 +38,7 @@ std::mt19937 r; */ void testCopyRegion(GPUUnitTestContext& ctx, size_t bufferSize) { - Device* pDevice = ctx.getDevice().get(); + ref pDevice = ctx.getDevice(); std::vector data(256); const size_t testSize = data.size() * sizeof(data[0]); @@ -93,7 +93,7 @@ void testCopyRegion(GPUUnitTestContext& ctx, size_t bufferSize) */ void testReadRaw(GPUUnitTestContext& ctx, bool useRootDesc, size_t bufferSize) { - Device* pDevice = ctx.getDevice().get(); + ref pDevice = ctx.getDevice(); Shader::DefineList defines; defines.add("USE_ROOT_DESC", useRootDesc ? "1" : "0"); @@ -148,7 +148,7 @@ void testReadRaw(GPUUnitTestContext& ctx, bool useRootDesc, size_t bufferSize) */ void testReadStructured(GPUUnitTestContext& ctx, bool useRootDesc, size_t bufferSize) { - Device* pDevice = ctx.getDevice().get(); + ref pDevice = ctx.getDevice(); Shader::DefineList defines; defines.add("USE_ROOT_DESC", useRootDesc ? "1" : "0"); @@ -205,7 +205,7 @@ void testReadStructured(GPUUnitTestContext& ctx, bool useRootDesc, size_t buffer */ void testReadStructuredUint(GPUUnitTestContext& ctx, bool useRootDesc, size_t bufferSize) { - Device* pDevice = ctx.getDevice().get(); + ref pDevice = ctx.getDevice(); Shader::DefineList defines; defines.add("USE_ROOT_DESC", useRootDesc ? "1" : "0"); diff --git a/Source/Tools/FalcorTest/Tests/Core/ObjectTests.cpp b/Source/Tools/FalcorTest/Tests/Core/ObjectTests.cpp new file mode 100644 index 000000000..fff59fd4e --- /dev/null +++ b/Source/Tools/FalcorTest/Tests/Core/ObjectTests.cpp @@ -0,0 +1,172 @@ +/*************************************************************************** + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. + # + # Redistribution and use in source and binary forms, with or without + # modification, are permitted provided that the following conditions + # are met: + # * Redistributions of source code must retain the above copyright + # notice, this list of conditions and the following disclaimer. + # * Redistributions in binary form must reproduce the above copyright + # notice, this list of conditions and the following disclaimer in the + # documentation and/or other materials provided with the distribution. + # * Neither the name of NVIDIA CORPORATION nor the names of its + # contributors may be used to endorse or promote products derived + # from this software without specific prior written permission. + # + # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY + # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + # (INCLUDING 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 "Core/Object.h" + +namespace Falcor +{ + +class DummyObject : public Object +{ +public: + DummyObject() { getCount()++; } + ~DummyObject() { getCount()--; } + + static uint32_t& getCount() + { + static uint32_t sCount = 0; + return sCount; + } +}; + +CPU_TEST(Object_ref) +{ + ASSERT_EQ(DummyObject::getCount(), 0); + + ref r1; + ref r2; + + EXPECT_TRUE(r1 == r1); + EXPECT_TRUE(r1 == r2); + EXPECT_TRUE(r1 == nullptr); + EXPECT_FALSE(r1 != r1); + EXPECT_FALSE(r1 != r2); + EXPECT_FALSE(r1 != nullptr); + EXPECT_FALSE(bool(r1)); + EXPECT(r1.get() == nullptr); + + r1 = make_ref(); + EXPECT_EQ(DummyObject::getCount(), 1); + EXPECT_EQ(r1->refCount(), 1); + + EXPECT_TRUE(r1 == r1); + EXPECT_FALSE(r1 == r2); + EXPECT_FALSE(r1 == nullptr); + EXPECT_FALSE(r1 != r1); + EXPECT_TRUE(r1 != r2); + EXPECT_TRUE(r1 != nullptr); + EXPECT_TRUE(bool(r1)); + EXPECT(r1.get() != nullptr); + + r2 = r1; + EXPECT_EQ(DummyObject::getCount(), 1); + EXPECT_EQ(r1->refCount(), 2); + EXPECT_TRUE(r1 == r2); + EXPECT_FALSE(r1 != r2); + + r2 = nullptr; + EXPECT_EQ(DummyObject::getCount(), 1); + EXPECT_EQ(r1->refCount(), 1); + + r1 = nullptr; + EXPECT_EQ(DummyObject::getCount(), 0); +} + +class DummyBuffer; + +class DummyDevice : public Object +{ +public: + ref buffer; + + DummyDevice() { getCount()++; } + ~DummyDevice() { getCount()--; } + + static uint32_t& getCount() + { + static uint32_t sCount = 0; + return sCount; + } +}; + +class DummyBuffer : public Object +{ +public: + BreakableReference device; + + DummyBuffer(ref device) : device(std::move(device)) { getCount()++; } + ~DummyBuffer() { getCount()--; } + + static uint32_t& getCount() + { + static uint32_t sCount = 0; + return sCount; + } +}; + +CPU_TEST(Object_BreakableReference) +{ + ASSERT_EQ(DummyDevice::getCount(), 0); + ASSERT_EQ(DummyBuffer::getCount(), 0); + + { + ref device = make_ref(); + + // Create a buffer that has a reference to the device -> cyclic reference + device->buffer = make_ref(device); + + EXPECT_EQ(DummyDevice::getCount(), 1); + EXPECT_EQ(DummyBuffer::getCount(), 1); + + DummyBuffer* bufferPtr = device->buffer.get(); + + // Release the device + device = nullptr; + + // Device is not released as there is still a reference from the buffer + EXPECT_EQ(DummyDevice::getCount(), 1); + EXPECT_EQ(DummyBuffer::getCount(), 1); + + // Break the cycle + bufferPtr->device.breakStrongReference(); + + EXPECT_EQ(DummyDevice::getCount(), 0); + EXPECT_EQ(DummyBuffer::getCount(), 0); + } + + { + ref device = make_ref(); + + // Create a buffer that has a reference to the device -> cyclic reference + device->buffer = make_ref(device); + // Immediately break the cycle + device->buffer->device.breakStrongReference(); + + EXPECT_EQ(DummyDevice::getCount(), 1); + EXPECT_EQ(DummyBuffer::getCount(), 1); + + // Release the device + device = nullptr; + + // Device is released as there is no strong reference from the buffer + EXPECT_EQ(DummyDevice::getCount(), 0); + EXPECT_EQ(DummyBuffer::getCount(), 0); + } +} + +} // namespace Falcor diff --git a/Source/Tools/FalcorTest/Tests/Core/ParamBlockCB.cpp b/Source/Tools/FalcorTest/Tests/Core/ParamBlockCB.cpp index d9ffdf07e..c6c2e39da 100644 --- a/Source/Tools/FalcorTest/Tests/Core/ParamBlockCB.cpp +++ b/Source/Tools/FalcorTest/Tests/Core/ParamBlockCB.cpp @@ -33,14 +33,14 @@ namespace Falcor */ GPU_TEST(ParamBlockCB) { - Device* pDevice = ctx.getDevice().get(); + ref pDevice = ctx.getDevice(); ctx.createProgram("Tests/Core/ParamBlockCB.cs.slang", "main", Program::DefineList(), Shader::CompilerFlags::None); ctx.allocateStructuredBuffer("result", 1); auto pBlockReflection = ctx.getProgram()->getReflector()->getParameterBlock("gParamBlock"); auto pParamBlock = ParameterBlock::create(pDevice, pBlockReflection); - pParamBlock["a"] = 42.1f; + pParamBlock->getRootVar()["a"] = 42.1f; ctx["gParamBlock"] = pParamBlock; ctx.runProgram(1, 1, 1); diff --git a/Source/Tools/FalcorTest/Tests/Core/ResourceAliasing.cpp b/Source/Tools/FalcorTest/Tests/Core/ResourceAliasing.cpp index 487ddfaba..bd66ccd1f 100644 --- a/Source/Tools/FalcorTest/Tests/Core/ResourceAliasing.cpp +++ b/Source/Tools/FalcorTest/Tests/Core/ResourceAliasing.cpp @@ -29,9 +29,9 @@ namespace Falcor { -GPU_TEST(BufferAliasingRead) +GPU_TEST(BufferAliasing_Read) { - Device* pDevice = ctx.getDevice().get(); + ref pDevice = ctx.getDevice(); const size_t N = 32; @@ -43,11 +43,12 @@ GPU_TEST(BufferAliasingRead) ); ctx.createProgram("Tests/Core/ResourceAliasing.cs.slang", "testRead", Program::DefineList(), Shader::CompilerFlags::None); - ctx.allocateStructuredBuffer("result", N * 2); + ctx.allocateStructuredBuffer("result", N * 3); // Bind buffer to two separate vars to test resource aliasing. ctx["bufA1"] = pBuffer; ctx["bufA2"] = pBuffer; + ctx["bufA3"] = pBuffer; ctx.runProgram(N, 1, 1); @@ -56,17 +57,18 @@ GPU_TEST(BufferAliasingRead) { EXPECT_EQ(result[i], (float)i) << "i = " << i; EXPECT_EQ(result[i + N], (float)i) << "i = " << i; + EXPECT_EQ(result[i + 2 * N], (float)i) << "i = " << i; } ctx.unmapBuffer("result"); } -GPU_TEST(BufferAliasingReadWrite) +GPU_TEST(BufferAliasing_ReadWrite) { - Device* pDevice = ctx.getDevice().get(); + ref pDevice = ctx.getDevice(); const size_t N = 32; - std::vector initData(N * 2); + std::vector initData(N * 3); for (size_t i = 0; i < initData.size(); i++) initData[i] = (float)i; auto pBuffer = Buffer::create( @@ -79,6 +81,7 @@ GPU_TEST(BufferAliasingReadWrite) // Bind buffer to two separate vars to test resource aliasing. ctx["bufB1"] = pBuffer; ctx["bufB2"] = pBuffer; + ctx["bufB3"] = pBuffer; ctx.runProgram(N, 1, 1); @@ -87,7 +90,41 @@ GPU_TEST(BufferAliasingReadWrite) { EXPECT_EQ(result[i], (float)(N - i)) << "i = " << i; EXPECT_EQ(result[i + N], (float)(N - i)) << "i = " << i; + EXPECT_EQ(result[i + 2 * N], (float)(N - i)) << "i = " << i; } pBuffer->unmap(); } + +GPU_TEST(BufferAliasing_StructRead, "Disabled because version fails") +{ + ref pDevice = ctx.getDevice(); + + const size_t N = 32; + + std::vector initData(N); + for (size_t i = 0; i < initData.size(); i++) + initData[i] = (float)i; + auto pBuffer = Buffer::createStructured( + pDevice, initData.size() * sizeof(float), 1, Resource::BindFlags::ShaderResource, Buffer::CpuAccess::None, initData.data(), false + ); + + ctx.createProgram("Tests/Core/ResourceAliasing.cs.slang", "testStructRead", Program::DefineList(), Shader::CompilerFlags::None); + ctx.allocateStructuredBuffer("result", N * 3); + + // Bind buffer to three separate vars to test resource aliasing. + ctx["bufStruct1"] = pBuffer; + ctx["bufStruct2"] = pBuffer; + ctx["bufStruct3"] = pBuffer; + + ctx.runProgram(N, 1, 1); + + const float* result = ctx.mapBuffer("result"); + for (size_t i = 0; i < N; i++) + { + EXPECT_EQ(result[i], (float)i) << "i = " << i; + EXPECT_EQ(result[i + N], (float)i) << "i = " << i; + EXPECT_EQ(result[i + 2 * N], (float)i) << "i = " << i; + } + ctx.unmapBuffer("result"); +} } // namespace Falcor diff --git a/Source/Tools/FalcorTest/Tests/Core/ResourceAliasing.cs.slang b/Source/Tools/FalcorTest/Tests/Core/ResourceAliasing.cs.slang index 8d7e7ca9d..bdcfd3c26 100644 --- a/Source/Tools/FalcorTest/Tests/Core/ResourceAliasing.cs.slang +++ b/Source/Tools/FalcorTest/Tests/Core/ResourceAliasing.cs.slang @@ -27,13 +27,25 @@ **************************************************************************/ RWStructuredBuffer result; -// These descriptors alias the same buffer. +struct S +{ + float d[8]; +}; + +// These descriptors alias the same raw buffer. StructuredBuffer bufA1; StructuredBuffer bufA2; +StructuredBuffer bufA3; -// These descriptors alias the same buffer. +// These descriptors alias the same raw buffer. RWStructuredBuffer bufB1; RWStructuredBuffer bufB2; +RWStructuredBuffer bufB3; + +// These descriptors alias the same structured buffer. +StructuredBuffer bufStruct1; +StructuredBuffer bufStruct2; +StructuredBuffer bufStruct3; [numthreads(32, 1, 1)] void testRead(uint3 threadId: SV_DispatchThreadID) @@ -41,6 +53,7 @@ void testRead(uint3 threadId: SV_DispatchThreadID) uint i = threadId.x; result[i] = bufA1[i]; result[i + 32] = bufA2[i / 4][i % 4]; + result[i + 64] = bufA3[i / 8].d[i % 8]; } [numthreads(32, 1, 1)] @@ -49,4 +62,14 @@ void testReadWrite(uint3 threadId: SV_DispatchThreadID) uint i = threadId.x; bufB1[i] = (float)(32 - i); bufB2[i / 4 + 8][i % 4] = (float)(32 - i); + bufB3[i / 8 + 8].d[i % 8] = (float)(32 - i); +} + +[numthreads(32, 1, 1)] +void testStructRead(uint3 threadId: SV_DispatchThreadID) +{ + uint i = threadId.x; + result[i] = asfloat(bufStruct1[i]); + result[i + 32] = asfloat(bufStruct2[i / 4][i % 4]); + result[i + 64] = bufStruct3[i / 8].d[i % 8]; } diff --git a/Source/Tools/FalcorTest/Tests/Core/RootBufferParamBlockTests.cpp b/Source/Tools/FalcorTest/Tests/Core/RootBufferParamBlockTests.cpp index 0d1037631..4c9fe6842 100644 --- a/Source/Tools/FalcorTest/Tests/Core/RootBufferParamBlockTests.cpp +++ b/Source/Tools/FalcorTest/Tests/Core/RootBufferParamBlockTests.cpp @@ -26,7 +26,7 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **************************************************************************/ #include "Testing/UnitTest.h" -#include "RenderGraph/BasePasses/ComputePass.h" +#include "Core/Pass/ComputePass.h" #include namespace Falcor @@ -45,9 +45,9 @@ auto dist = std::uniform_int_distribution(0, 100); void testRootBuffer(GPUUnitTestContext& ctx, const std::string& shaderModel, bool useUav) { - Device* pDevice = ctx.getDevice().get(); + ref pDevice = ctx.getDevice(); - auto r = [&]() -> uint32_t { return dist(rng); }; + auto nextRandom = [&]() -> uint32_t { return dist(rng); }; Program::DefineList defines = {{"USE_UAV", useUav ? "1" : "0"}}; Shader::CompilerFlags compilerFlags = Shader::CompilerFlags::None; @@ -65,7 +65,7 @@ void testRootBuffer(GPUUnitTestContext& ctx, const std::string& shaderModel, boo // Bind non-root resources to the parameter block. auto block = pParamBlock->getRootVar(); - float c0 = (float)r(); + float c0 = (float)nextRandom(); block["c0"] = c0; std::vector bufA[2]; @@ -73,7 +73,7 @@ void testRootBuffer(GPUUnitTestContext& ctx, const std::string& shaderModel, boo { bufA[j].resize(kNumElems); for (uint32_t i = 0; i < kNumElems; i++) - bufA[j][i] = r(); + bufA[j][i] = nextRandom(); block["bufA"][j] = Buffer::create( pDevice, kNumElems * sizeof(uint32_t), Resource::BindFlags::ShaderResource, Buffer::CpuAccess::None, bufA[j].data() ); @@ -83,7 +83,7 @@ void testRootBuffer(GPUUnitTestContext& ctx, const std::string& shaderModel, boo { bufB[j].resize(kNumElems); for (uint32_t i = 0; i < kNumElems; i++) - bufB[j][i] = (float)r(); + bufB[j][i] = (float)nextRandom(); block["bufB"][j] = Buffer::createTyped(pDevice, kNumElems, Resource::BindFlags::UnorderedAccess, Buffer::CpuAccess::None, bufB[j].data()); } @@ -92,7 +92,7 @@ void testRootBuffer(GPUUnitTestContext& ctx, const std::string& shaderModel, boo { bufC[j].resize(kNumElems); for (uint32_t i = 0; i < kNumElems; i++) - bufC[j][i] = r(); + bufC[j][i] = nextRandom(); block["bufC"][j] = Buffer::createTyped(pDevice, kNumElems, Resource::BindFlags::ShaderResource, Buffer::CpuAccess::None, bufC[j].data()); } @@ -101,7 +101,7 @@ void testRootBuffer(GPUUnitTestContext& ctx, const std::string& shaderModel, boo std::vector testBuffer(kNumElems); { for (uint32_t i = 0; i < kNumElems; i++) - testBuffer[i] = r(); + testBuffer[i] = nextRandom(); auto pTestBuffer = Buffer::create( pDevice, kNumElems * sizeof(uint32_t), useUav ? ResourceBindFlags::UnorderedAccess : ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, testBuffer.data() @@ -109,7 +109,7 @@ void testRootBuffer(GPUUnitTestContext& ctx, const std::string& shaderModel, boo bool ret = pParamBlock->setBuffer(kRootBufferName, pTestBuffer); EXPECT(ret); - Buffer::SharedPtr pBoundBuffer = pParamBlock->getBuffer(kRootBufferName); + ref pBoundBuffer = pParamBlock->getBuffer(kRootBufferName); EXPECT_EQ(pBoundBuffer, pTestBuffer); } @@ -125,7 +125,7 @@ void testRootBuffer(GPUUnitTestContext& ctx, const std::string& shaderModel, boo { globalBufA.resize(kNumElems); for (uint32_t i = 0; i < kNumElems; i++) - globalBufA[i] = r(); + globalBufA[i] = nextRandom(); var["globalBufA"] = Buffer::createTyped( pDevice, kNumElems, Resource::BindFlags::ShaderResource, Buffer::CpuAccess::None, globalBufA.data() ); @@ -133,7 +133,7 @@ void testRootBuffer(GPUUnitTestContext& ctx, const std::string& shaderModel, boo std::vector globalTestBuffer(kNumElems); { for (uint32_t i = 0; i < kNumElems; i++) - globalTestBuffer[i] = r(); + globalTestBuffer[i] = nextRandom(); var[kGlobalRootBufferName] = Buffer::create( pDevice, kNumElems * sizeof(uint32_t), useUav ? ResourceBindFlags::UnorderedAccess : ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, globalTestBuffer.data() diff --git a/Source/Tools/FalcorTest/Tests/Core/RootBufferStructTests.cpp b/Source/Tools/FalcorTest/Tests/Core/RootBufferStructTests.cpp index 24a56b619..21a781159 100644 --- a/Source/Tools/FalcorTest/Tests/Core/RootBufferStructTests.cpp +++ b/Source/Tools/FalcorTest/Tests/Core/RootBufferStructTests.cpp @@ -40,9 +40,9 @@ auto dist = std::uniform_int_distribution(0, 100); void testRootBufferInStruct(GPUUnitTestContext& ctx, const std::string& shaderModel, bool useUav) { - Device* pDevice = ctx.getDevice().get(); + ref pDevice = ctx.getDevice(); - auto r = [&]() -> uint32_t { return dist(rng); }; + auto nextRandom = [&]() -> uint32_t { return dist(rng); }; Program::DefineList defines = {{"USE_UAV", useUav ? "1" : "0"}}; Shader::CompilerFlags compilerFlags = Shader::CompilerFlags::None; @@ -56,14 +56,14 @@ void testRootBufferInStruct(GPUUnitTestContext& ctx, const std::string& shaderMo std::vector buf(kNumElems); { for (uint32_t i = 0; i < kNumElems; i++) - buf[i] = r(); + buf[i] = nextRandom(); data["buf"] = Buffer::createTyped(pDevice, kNumElems, ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, buf.data()); } std::vector rwBuf(kNumElems); { for (uint32_t i = 0; i < kNumElems; i++) - rwBuf[i] = r(); + rwBuf[i] = nextRandom(); data["rwBuf"] = Buffer::createTyped(pDevice, kNumElems, ResourceBindFlags::UnorderedAccess, Buffer::CpuAccess::None, rwBuf.data()); } @@ -72,7 +72,7 @@ void testRootBufferInStruct(GPUUnitTestContext& ctx, const std::string& shaderMo std::vector rootBuf(kNumElems); { for (uint32_t i = 0; i < kNumElems; i++) - rootBuf[i] = r(); + rootBuf[i] = nextRandom(); auto pRootBuffer = Buffer::createStructured( pDevice, data[kRootBufferName], kNumElems, useUav ? ResourceBindFlags::UnorderedAccess : ResourceBindFlags::ShaderResource, @@ -81,7 +81,7 @@ void testRootBufferInStruct(GPUUnitTestContext& ctx, const std::string& shaderMo data[kRootBufferName] = pRootBuffer; - Buffer::SharedPtr pBoundBuffer = data[kRootBufferName]; + ref pBoundBuffer = data[kRootBufferName]; EXPECT_EQ(pBoundBuffer, pRootBuffer); } diff --git a/Source/Tools/FalcorTest/Tests/Core/RootBufferTests.cpp b/Source/Tools/FalcorTest/Tests/Core/RootBufferTests.cpp index 9e3a326cc..bfbb38603 100644 --- a/Source/Tools/FalcorTest/Tests/Core/RootBufferTests.cpp +++ b/Source/Tools/FalcorTest/Tests/Core/RootBufferTests.cpp @@ -49,9 +49,9 @@ struct S void testRootBuffer(GPUUnitTestContext& ctx, const std::string& shaderModel, bool useUav) { - Device* pDevice = ctx.getDevice().get(); + ref pDevice = ctx.getDevice(); - auto r = [&]() -> uint32_t { return dist(rng); }; + auto nextRandom = [&]() -> uint32_t { return dist(rng); }; Program::DefineList defines = {{"USE_UAV", useUav ? "1" : "0"}}; Shader::CompilerFlags compilerFlags = Shader::CompilerFlags::None; @@ -67,7 +67,7 @@ void testRootBuffer(GPUUnitTestContext& ctx, const std::string& shaderModel, boo std::vector rawBuffer(kNumElems); { for (uint32_t i = 0; i < kNumElems; i++) - rawBuffer[i] = r(); + rawBuffer[i] = nextRandom(); var["rawBuffer"] = Buffer::create( pDevice, kNumElems * sizeof(uint32_t), ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, rawBuffer.data() ); @@ -76,7 +76,7 @@ void testRootBuffer(GPUUnitTestContext& ctx, const std::string& shaderModel, boo std::vector structBuffer(kNumElems); { for (uint32_t i = 0; i < kNumElems; i++) - structBuffer[i] = {r() + 0.5f, r()}; + structBuffer[i] = {nextRandom() + 0.5f, nextRandom()}; var["structBuffer"] = Buffer::createStructured( pDevice, var["structBuffer"], kNumElems, ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, structBuffer.data() ); @@ -85,7 +85,7 @@ void testRootBuffer(GPUUnitTestContext& ctx, const std::string& shaderModel, boo std::vector typedBufferUint(kNumElems); { for (uint32_t i = 0; i < kNumElems; i++) - typedBufferUint[i] = r(); + typedBufferUint[i] = nextRandom(); var["typedBufferUint"] = Buffer::createTyped( pDevice, kNumElems, ResourceBindFlags::UnorderedAccess, Buffer::CpuAccess::None, typedBufferUint.data() ); @@ -94,7 +94,7 @@ void testRootBuffer(GPUUnitTestContext& ctx, const std::string& shaderModel, boo std::vector typedBufferFloat4(kNumElems); { for (uint32_t i = 0; i < kNumElems; i++) - typedBufferFloat4[i] = {r() * 0.25f, r() * 0.5f, r() * 0.75f, r()}; + typedBufferFloat4[i] = {nextRandom() * 0.25f, nextRandom() * 0.5f, nextRandom() * 0.75f, float(nextRandom())}; var["typedBufferFloat4"] = Buffer::createTyped( pDevice, kNumElems, ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, typedBufferFloat4.data() ); @@ -104,14 +104,14 @@ void testRootBuffer(GPUUnitTestContext& ctx, const std::string& shaderModel, boo std::vector testBuffer(kNumElems); { for (uint32_t i = 0; i < kNumElems; i++) - testBuffer[i] = r(); + testBuffer[i] = nextRandom(); auto pTestBuffer = Buffer::create( pDevice, kNumElems * sizeof(uint32_t), useUav ? ResourceBindFlags::UnorderedAccess : ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, testBuffer.data() ); var[kRootBufferName] = pTestBuffer; - Buffer::SharedPtr pBoundBuffer = var[kRootBufferName]; + ref pBoundBuffer = var[kRootBufferName]; EXPECT_EQ(pBoundBuffer, pTestBuffer); } @@ -140,11 +140,11 @@ void testRootBuffer(GPUUnitTestContext& ctx, const std::string& shaderModel, boo // Change the binding of other resources to test that the root buffer stays correctly bound. for (uint32_t i = 0; i < kNumElems; i++) - rawBuffer[i] = r(); + rawBuffer[i] = nextRandom(); var["rawBuffer"] = Buffer::create(pDevice, kNumElems * sizeof(uint32_t), ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, rawBuffer.data()); for (uint32_t i = 0; i < kNumElems; i++) - typedBufferFloat4[i] = {r() * 0.25f, r() * 0.5f, r() * 0.75f, r()}; + typedBufferFloat4[i] = {nextRandom() * 0.25f, nextRandom() * 0.5f, nextRandom() * 0.75f, float(nextRandom())}; var["typedBufferFloat4"] = Buffer::createTyped( pDevice, kNumElems, ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, typedBufferFloat4.data() ); @@ -156,14 +156,14 @@ void testRootBuffer(GPUUnitTestContext& ctx, const std::string& shaderModel, boo // Test binding a new root buffer. { for (uint32_t i = 0; i < kNumElems; i++) - testBuffer[i] = r(); + testBuffer[i] = nextRandom(); auto pTestBuffer = Buffer::create( pDevice, kNumElems * sizeof(uint32_t), useUav ? ResourceBindFlags::UnorderedAccess : ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, testBuffer.data() ); var[kRootBufferName] = pTestBuffer; - Buffer::SharedPtr pBoundBuffer = var[kRootBufferName]; + ref pBoundBuffer = var[kRootBufferName]; EXPECT_EQ(pBoundBuffer, pTestBuffer); } diff --git a/Source/Tools/FalcorTest/Tests/Core/TextureLoadTests.cs.slang b/Source/Tools/FalcorTest/Tests/Core/TextureLoadTests.cs.slang index 2a96ec9a6..96567ccef 100644 --- a/Source/Tools/FalcorTest/Tests/Core/TextureLoadTests.cs.slang +++ b/Source/Tools/FalcorTest/Tests/Core/TextureLoadTests.cs.slang @@ -27,12 +27,12 @@ **************************************************************************/ RWStructuredBuffer result; -Texture2D texUnorm; +Texture2D texUnorm; Texture2D texUnormAsUint; Texture2D texUint; [numthreads(256, 1, 1)] -void main(uint3 threadId: SV_DispatchThreadID) +void testLoadFormat(uint3 threadId: SV_DispatchThreadID) { float4 f = texUnorm[threadId.xy]; uint4 u = texUnormAsUint[threadId.xy]; @@ -40,3 +40,11 @@ void main(uint3 threadId: SV_DispatchThreadID) result[threadId.x] = { asuint(f.x), u.x, (uint)(f.x * 255.f), v.x }; } + +[numthreads(1, 1, 1)] +void testLoadMips(uint3 threadId: SV_DispatchThreadID) +{ + result[0] = (uint4)(texUnorm.Load(int3(0, 0, 0)) * 255.f); + result[1] = (uint4)(texUnorm.Load(int3(0, 0, 1)) * 255.f); + result[2] = (uint4)(texUnorm.Load(int3(0, 0, 2)) * 255.f); +} diff --git a/Source/Tools/FalcorTest/Tests/Core/TextureTests.cpp b/Source/Tools/FalcorTest/Tests/Core/TextureTests.cpp index fceb9a399..a7726aa0b 100644 --- a/Source/Tools/FalcorTest/Tests/Core/TextureTests.cpp +++ b/Source/Tools/FalcorTest/Tests/Core/TextureTests.cpp @@ -34,7 +34,7 @@ namespace Falcor */ GPU_TEST(RWTexture3D) { - Device* pDevice = ctx.getDevice().get(); + ref pDevice = ctx.getDevice(); auto pTex = Texture::create3D( pDevice, 16, 16, 16, ResourceFormat::R32Uint, 1, nullptr, ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess @@ -61,7 +61,8 @@ GPU_TEST(RWTexture3D) { for (uint32_t x = 0; x < 16; x++) { - EXPECT_EQ(result[i], x * y * z + 577) << "i = " << i++; + EXPECT_EQ(result[i], x * y * z + 577) << "i = " << i; + ++i; } } } @@ -72,7 +73,7 @@ GPU_TEST(RWTexture3D) */ GPU_TEST(TextureMinMaxMip) { - Device* pDevice = ctx.getDevice().get(); + ref pDevice = ctx.getDevice(); // Generate test texture. const uint32_t texWidth = 16; @@ -178,7 +179,7 @@ GPU_TEST(TextureMinMaxMip) // and explicitly as an 8-bit integer format (RGBA8Uint). GPU_TEST(Texture_Load8Bit) { - Device* pDevice = ctx.getDevice().get(); + ref pDevice = ctx.getDevice(); // Create test data. uint8_t data[1024]; @@ -199,7 +200,7 @@ GPU_TEST(Texture_Load8Bit) auto texUint = Texture::create2D(pDevice, 256, 1, ResourceFormat::RGBA8Uint, 1, 1, data); EXPECT(texUint != nullptr); - ctx.createProgram("Tests/Core/TextureLoadTests.cs.slang", "main"); + ctx.createProgram("Tests/Core/TextureLoadTests.cs.slang", "testLoadFormat"); ctx.allocateStructuredBuffer("result", 256); ctx["texUnorm"] = texUnorm; ctx["texUnormAsUint"] = texUnorm; @@ -222,4 +223,44 @@ GPU_TEST(Texture_Load8Bit) ctx.unmapBuffer("result"); } + +GPU_TEST(Texture2D_LoadMips) +{ + ref pDevice = ctx.getDevice(); + + std::filesystem::path paths[] = { + getRuntimeDirectory() / "data/tests/tiny_mip0.png", + getRuntimeDirectory() / "data/tests/tiny_mip1.png", + getRuntimeDirectory() / "data/tests/tiny_mip2.png", + }; + + auto tex = Texture::createMippedFromFiles(pDevice, paths, false); + ASSERT(tex != nullptr); + + EXPECT_EQ(tex->getMipCount(), 3); + + ctx.createProgram("Tests/Core/TextureLoadTests.cs.slang", "testLoadMips"); + ctx.allocateStructuredBuffer("result", 3); + ctx["texUnorm"] = tex; + ctx.runProgram(1, 1, 1); + + const uint4* result = ctx.mapBuffer("result"); + + EXPECT_EQ(result[0].x, 255); + EXPECT_EQ(result[0].y, 0); + EXPECT_EQ(result[0].z, 0); + EXPECT_EQ(result[0].w, 255); + + EXPECT_EQ(result[1].x, 0); + EXPECT_EQ(result[1].y, 255); + EXPECT_EQ(result[1].z, 0); + EXPECT_EQ(result[1].w, 255); + + EXPECT_EQ(result[2].x, 0); + EXPECT_EQ(result[2].y, 0); + EXPECT_EQ(result[2].z, 255); + EXPECT_EQ(result[2].w, 255); + + ctx.unmapBuffer("result"); +} } // namespace Falcor diff --git a/Source/Tools/FalcorTest/Tests/DebugPasses/InvalidPixelDetectionTests.cpp b/Source/Tools/FalcorTest/Tests/DebugPasses/InvalidPixelDetectionTests.cpp index 499853b24..f60013355 100644 --- a/Source/Tools/FalcorTest/Tests/DebugPasses/InvalidPixelDetectionTests.cpp +++ b/Source/Tools/FalcorTest/Tests/DebugPasses/InvalidPixelDetectionTests.cpp @@ -32,7 +32,7 @@ namespace Falcor { GPU_TEST(InvalidPixelDetectionPass) { - Device* pDevice = ctx.getDevice().get(); + ref pDevice = ctx.getDevice(); float pInitData[8] = { std::numeric_limits::quiet_NaN(), @@ -47,9 +47,9 @@ GPU_TEST(InvalidPixelDetectionPass) RenderContext* pRenderContext = ctx.getRenderContext(); Fbo* pTargetFbo = ctx.getTargetFbo(); - Texture::SharedPtr pInput = Texture::create2D(pDevice, 2, 4, ResourceFormat::R32Float, 1, Resource::kMaxPossible, pInitData); - RenderGraph::SharedPtr pGraph = RenderGraph::create(ctx.getDevice(), "Invalid Pixel Detection"); - RenderPass::SharedPtr pPass = RenderPass::create("InvalidPixelDetectionPass", ctx.getDevice()); + ref pInput = Texture::create2D(pDevice, 2, 4, ResourceFormat::R32Float, 1, Resource::kMaxPossible, pInitData); + ref pGraph = RenderGraph::create(ctx.getDevice(), "Invalid Pixel Detection"); + ref pPass = RenderPass::create("InvalidPixelDetectionPass", ctx.getDevice()); if (!pPass) throw RuntimeError("Could not create render pass 'InvalidPixelDetectionPass'"); pGraph->addPass(pPass, "InvalidPixelDetectionPass"); @@ -57,7 +57,7 @@ GPU_TEST(InvalidPixelDetectionPass) pGraph->markOutput("InvalidPixelDetectionPass.dst"); pGraph->onResize(pTargetFbo); pGraph->execute(pRenderContext); - Resource::SharedPtr pOutput = pGraph->getOutput("InvalidPixelDetectionPass.dst"); + ref pOutput = pGraph->getOutput("InvalidPixelDetectionPass.dst"); std::vector color = pRenderContext->readTextureSubresource(pOutput->asTexture().get(), 0); uint32_t* output = (uint32_t*)color.data(); diff --git a/Source/Tools/FalcorTest/Tests/Rendering/Materials/BSDFIntegratorTests.cpp b/Source/Tools/FalcorTest/Tests/Rendering/Materials/BSDFIntegratorTests.cpp index 28cfade43..546ed0006 100644 --- a/Source/Tools/FalcorTest/Tests/Rendering/Materials/BSDFIntegratorTests.cpp +++ b/Source/Tools/FalcorTest/Tests/Rendering/Materials/BSDFIntegratorTests.cpp @@ -47,7 +47,7 @@ const float kMaxL2 = 1e-6f; GPU_TEST(BSDFIntegrator) { // Create material. - StandardMaterial::SharedPtr pMaterial = StandardMaterial::create(ctx.getDevice(), "testMaterial"); + ref pMaterial = StandardMaterial::create(ctx.getDevice(), "testMaterial"); pMaterial->setBaseColor(float4(0.3f, 0.8f, 0.9f, 1.f)); pMaterial->setMetallic(0.f); pMaterial->setRoughness(1.f); @@ -55,10 +55,10 @@ GPU_TEST(BSDFIntegrator) // Create and update scene containing the material. Scene::SceneData sceneData; - sceneData.pMaterials = MaterialSystem::create(ctx.getDevice()); + sceneData.pMaterials = std::make_unique(ctx.getDevice()); MaterialID materialID = sceneData.pMaterials->addMaterial(pMaterial); - Scene::SharedPtr pScene = Scene::create(ctx.getDevice(), std::move(sceneData)); + ref pScene = Scene::create(ctx.getDevice(), std::move(sceneData)); auto updateFlags = pScene->update(ctx.getRenderContext(), 0.0); // Create BSDF integrator utility. @@ -72,7 +72,7 @@ GPU_TEST(BSDFIntegrator) for (size_t i = 0; i < cosThetas.size(); i++) { float3 e = results[i] - kExpectedResults[i]; - float l2 = std::sqrt(glm::dot(e, e)); + float l2 = std::sqrt(dot(e, e)); EXPECT_LE(l2, kMaxL2) << " result=" << to_string(results[i]) << " expected=" << to_string(kExpectedResults[i]) << " cosTheta=" << cosThetas[i]; } diff --git a/Source/Tools/FalcorTest/Tests/Rendering/Materials/MicrofacetTests.cpp b/Source/Tools/FalcorTest/Tests/Rendering/Materials/MicrofacetTests.cpp index 3495f2234..3e7bd30c5 100644 --- a/Source/Tools/FalcorTest/Tests/Rendering/Materials/MicrofacetTests.cpp +++ b/Source/Tools/FalcorTest/Tests/Rendering/Materials/MicrofacetTests.cpp @@ -92,7 +92,7 @@ struct SamplingTestSpec void setupSamplingTest(GPUUnitTestContext& ctx, const SamplingTestSpec& spec, const std::string& csEntry) { - Device* pDevice = ctx.getDevice().get(); + ref pDevice = ctx.getDevice(); uint32_t testCount = spec.visibleNormals ? (uint32_t)spec.incidentAngles.size() : 1; @@ -126,7 +126,7 @@ void setupSamplingTest(GPUUnitTestContext& ctx, const SamplingTestSpec& spec, co std::vector> tabulateHistogram(GPUUnitTestContext& ctx, const SamplingTestSpec& spec) { - Device* pDevice = ctx.getDevice().get(); + ref pDevice = ctx.getDevice(); uint32_t testCount = spec.visibleNormals ? (uint32_t)spec.incidentAngles.size() : 1; uint32_t binCount = spec.phiBinCount * spec.cosThetaBinCount; @@ -159,7 +159,7 @@ std::vector> tabulateHistogram(GPUUnitTestContext& ctx, cons std::vector> tabulatePdf(GPUUnitTestContext& ctx, const SamplingTestSpec& spec) { - Device* pDevice = ctx.getDevice().get(); + ref pDevice = ctx.getDevice(); uint32_t testCount = spec.visibleNormals ? (uint32_t)spec.incidentAngles.size() : 1; uint32_t binCount = spec.phiBinCount * spec.cosThetaBinCount; diff --git a/Source/Tools/FalcorTest/Tests/Rendering/Materials/RGLAcquisitionTests.cpp b/Source/Tools/FalcorTest/Tests/Rendering/Materials/RGLAcquisitionTests.cpp index 3da44b935..ecefd8133 100644 --- a/Source/Tools/FalcorTest/Tests/Rendering/Materials/RGLAcquisitionTests.cpp +++ b/Source/Tools/FalcorTest/Tests/Rendering/Materials/RGLAcquisitionTests.cpp @@ -35,7 +35,7 @@ namespace Falcor GPU_TEST(RGLAcquisition) { // Create material. - StandardMaterial::SharedPtr pMaterial = StandardMaterial::create(ctx.getDevice(), "testMaterial"); + ref pMaterial = StandardMaterial::create(ctx.getDevice(), "testMaterial"); pMaterial->setBaseColor(float4(0.3f, 0.8f, 0.9f, 1.f)); pMaterial->setMetallic(0.f); pMaterial->setRoughness(1.f); @@ -43,10 +43,10 @@ GPU_TEST(RGLAcquisition) // Create and update scene containing the material. Scene::SceneData sceneData; - sceneData.pMaterials = MaterialSystem::create(ctx.getDevice()); + sceneData.pMaterials = std::make_unique(ctx.getDevice()); MaterialID materialID = sceneData.pMaterials->addMaterial(pMaterial); - Scene::SharedPtr pScene = Scene::create(ctx.getDevice(), std::move(sceneData)); + ref pScene = Scene::create(ctx.getDevice(), std::move(sceneData)); auto updateFlags = pScene->update(ctx.getRenderContext(), 0.0); // Create acquisition class. diff --git a/Source/Tools/FalcorTest/Tests/Sampling/AliasTableTests.cpp b/Source/Tools/FalcorTest/Tests/Sampling/AliasTableTests.cpp index c111551db..67395e1d9 100644 --- a/Source/Tools/FalcorTest/Tests/Sampling/AliasTableTests.cpp +++ b/Source/Tools/FalcorTest/Tests/Sampling/AliasTableTests.cpp @@ -38,7 +38,7 @@ namespace { void testAliasTable(GPUUnitTestContext& ctx, uint32_t N, std::vector specificWeights = {}) { - Device* pDevice = ctx.getDevice().get(); + ref pDevice = ctx.getDevice(); std::mt19937 rng; std::uniform_real_distribution uniform; @@ -56,16 +56,15 @@ void testAliasTable(GPUUnitTestContext& ctx, uint32_t N, std::vector spec } // Create alias table. - auto aliasTable = AliasTable::create(pDevice, weights, rng); - EXPECT(aliasTable != nullptr); + AliasTable aliasTable(pDevice, weights, rng); // Compute weight sum. double weightSum = 0.0; for (const auto& weight : weights) weightSum += weight; - EXPECT_EQ(aliasTable->getCount(), weights.size()); - EXPECT_EQ(aliasTable->getWeightSum(), weightSum); + EXPECT_EQ(aliasTable.getCount(), weights.size()); + EXPECT_EQ(aliasTable.getWeightSum(), weightSum); // Test sampling the alias table. { @@ -81,7 +80,7 @@ void testAliasTable(GPUUnitTestContext& ctx, uint32_t N, std::vector spec ctx.createProgram("Tests/Sampling/AliasTableTests.cs.slang", "testAliasTableSample"); ctx.allocateStructuredBuffer("sampleResult", resultCount); ctx.allocateStructuredBuffer("random", randomCount, random.data()); - aliasTable->setShaderData(ctx["CB"]["aliasTable"]); + aliasTable.setShaderData(ctx["CB"]["aliasTable"]); ctx["CB"]["resultCount"] = resultCount; ctx.runProgram(resultCount); @@ -127,7 +126,7 @@ void testAliasTable(GPUUnitTestContext& ctx, uint32_t N, std::vector spec // Setup and run GPU test. ctx.createProgram("Tests/Sampling/AliasTableTests.cs.slang", "testAliasTableWeight"); ctx.allocateStructuredBuffer("weightResult", resultCount); - aliasTable->setShaderData(ctx["CB"]["aliasTable"]); + aliasTable.setShaderData(ctx["CB"]["aliasTable"]); ctx["CB"]["resultCount"] = resultCount; ctx.runProgram(resultCount); diff --git a/Source/Tools/FalcorTest/Tests/Sampling/PseudorandomTests.cpp b/Source/Tools/FalcorTest/Tests/Sampling/PseudorandomTests.cpp index fb2f2e0b5..0b11ebf7b 100644 --- a/Source/Tools/FalcorTest/Tests/Sampling/PseudorandomTests.cpp +++ b/Source/Tools/FalcorTest/Tests/Sampling/PseudorandomTests.cpp @@ -104,7 +104,7 @@ const char kShaderFile[] = "Tests/Sampling/PseudorandomTests.cs.slang"; const uint32_t kInstances = 256; const uint32_t kDimensions = 64; -Buffer::SharedPtr createSeed(Device* pDevice, size_t elements, std::vector& seed) +ref createSeed(ref pDevice, size_t elements, std::vector& seed) { // Initialize buffer of random seed data. seed.resize(elements); @@ -113,7 +113,7 @@ Buffer::SharedPtr createSeed(Device* pDevice, size_t elements, std::vector pSeedBuf = Buffer::create(pDevice, seed.size() * sizeof(seed[0]), Resource::BindFlags::ShaderResource, Buffer::CpuAccess::None, seed.data()); FALCOR_ASSERT(pSeedBuf); return pSeedBuf; @@ -126,7 +126,7 @@ GPU_TEST(XoshiroPRNG) { // Create random seed (128 bits per instance). std::vector seed; - auto pSeedBuf = createSeed(ctx.getDevice().get(), kInstances * 4, seed); + auto pSeedBuf = createSeed(ctx.getDevice(), kInstances * 4, seed); // Setup and run GPU test. ctx.createProgram(kShaderFile, "testXoshiro"); @@ -160,7 +160,7 @@ GPU_TEST(SplitMixPRNG) { // Create random seed (64 bits per instance). std::vector seed; - auto pSeedBuf = createSeed(ctx.getDevice().get(), kInstances * 2, seed); + auto pSeedBuf = createSeed(ctx.getDevice(), 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); @@ -191,7 +191,7 @@ GPU_TEST(LCGPRNG) { // Create random seed (32 bits per instance). std::vector seed; - auto pSeedBuf = createSeed(ctx.getDevice().get(), kInstances, seed); + auto pSeedBuf = createSeed(ctx.getDevice(), kInstances, seed); // Setup and run GPU test. ctx.createProgram(kShaderFile, "testLCG"); diff --git a/Source/Tools/FalcorTest/Tests/Sampling/SampleGeneratorTests.cpp b/Source/Tools/FalcorTest/Tests/Sampling/SampleGeneratorTests.cpp index 55e52c1b7..d86c7e57b 100644 --- a/Source/Tools/FalcorTest/Tests/Sampling/SampleGeneratorTests.cpp +++ b/Source/Tools/FalcorTest/Tests/Sampling/SampleGeneratorTests.cpp @@ -73,14 +73,14 @@ double correlation(const float* elems, const size_t numElems, const size_t strid void testSampleGenerator(GPUUnitTestContext& ctx, uint32_t type, const double meanError, const double corrThreshold, bool testInstances) { // Create sample generator. - SampleGenerator::SharedPtr pSampleGenerator = SampleGenerator::create(ctx.getDevice(), type); + ref pSampleGenerator = SampleGenerator::create(ctx.getDevice(), type); // Setup GPU test. // We defer the creation of the vars until after shader specialization. auto defines = pSampleGenerator->getDefines(); ctx.createProgram(kShaderFile, "test", defines, Shader::CompilerFlags::None, "6_2"); - pSampleGenerator->beginFrame(ctx.getRenderContext(), kDispatchDim.xy); + pSampleGenerator->beginFrame(ctx.getRenderContext(), uint2(kDispatchDim.x, kDispatchDim.y)); pSampleGenerator->setShaderData(ctx.vars().getRootVar()); const size_t numSamples = kDispatchDim.x * kDispatchDim.y * kDispatchDim.z * kDimensions; diff --git a/Source/Tools/FalcorTest/Tests/Scene/EnvMapTests.cpp b/Source/Tools/FalcorTest/Tests/Scene/EnvMapTests.cpp index 755091425..478bc3ec7 100644 --- a/Source/Tools/FalcorTest/Tests/Scene/EnvMapTests.cpp +++ b/Source/Tools/FalcorTest/Tests/Scene/EnvMapTests.cpp @@ -42,19 +42,16 @@ GPU_TEST(EnvMap) // 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. - EnvMap::SharedPtr pEnvMap = EnvMap::createFromFile(ctx.getDevice(), kEnvMapFile); + ref pEnvMap = EnvMap::createFromFile(ctx.getDevice(), kEnvMapFile); EXPECT_NE(pEnvMap, nullptr); if (pEnvMap == nullptr) return; - EnvMapSampler::SharedPtr pEnvMapSampler = EnvMapSampler::create(ctx.getDevice(), pEnvMap); - EXPECT_NE(pEnvMapSampler, nullptr); - if (pEnvMapSampler == nullptr) - return; + EnvMapSampler envMapSampler(ctx.getDevice(), pEnvMap); // Check that the importance map exists and is a square power-of-two // texture with a full mip map hierarchy. - auto pImportanceMap = pEnvMapSampler->getImportanceMap(); + auto pImportanceMap = envMapSampler.getImportanceMap(); EXPECT_NE(pImportanceMap, nullptr); if (pImportanceMap == nullptr) return; diff --git a/Source/Tools/FalcorTest/Tests/Scene/Material/BSDFTests.cpp b/Source/Tools/FalcorTest/Tests/Scene/Material/BSDFTests.cpp index d7bc61fea..68d528b24 100644 --- a/Source/Tools/FalcorTest/Tests/Scene/Material/BSDFTests.cpp +++ b/Source/Tools/FalcorTest/Tests/Scene/Material/BSDFTests.cpp @@ -106,7 +106,7 @@ void setupSamplingTest(GPUUnitTestContext& ctx, const SamplingTestSpec& spec, co FALCOR_ASSERT(!spec.bsdfConfigs.empty()); FALCOR_ASSERT(spec.sampleCount > spec.threadSampleCount && spec.sampleCount % spec.threadSampleCount == 0); - Device* pDevice = ctx.getDevice().get(); + ref pDevice = ctx.getDevice(); uint32_t testCount = (uint32_t)spec.bsdfConfigs.size(); uint32_t binCount = spec.phiBinCount * spec.cosThetaBinCount; @@ -140,7 +140,7 @@ void setupSamplingTest(GPUUnitTestContext& ctx, const SamplingTestSpec& spec, co std::vector> tabulateHistogram(GPUUnitTestContext& ctx, const SamplingTestSpec& spec) { - Device* pDevice = ctx.getDevice().get(); + ref pDevice = ctx.getDevice(); uint32_t testCount = (uint32_t)spec.bsdfConfigs.size(); uint32_t binCount = spec.phiBinCount * spec.cosThetaBinCount; @@ -173,7 +173,7 @@ std::vector> tabulateHistogram(GPUUnitTestContext& ctx, cons std::vector> tabulatePdf(GPUUnitTestContext& ctx, const SamplingTestSpec& spec) { - Device* pDevice = ctx.getDevice().get(); + ref pDevice = ctx.getDevice(); uint32_t testCount = (uint32_t)spec.bsdfConfigs.size(); uint32_t binCount = spec.phiBinCount * spec.cosThetaBinCount; @@ -206,7 +206,7 @@ std::vector, std::vector>> tabulateWeightA const SamplingTestSpec& spec ) { - Device* pDevice = ctx.getDevice().get(); + ref pDevice = ctx.getDevice(); uint32_t testCount = (uint32_t)spec.bsdfConfigs.size(); uint32_t binCount = spec.phiBinCount * spec.cosThetaBinCount; @@ -314,130 +314,6 @@ void testSampling(GPUUnitTestContext& ctx, const SamplingTestSpec& spec) } } // namespace -/// The OLD, pre-refactor BSDFs (from BxDF.slang), to be deleted - -GPU_TEST(TestBsdf_DiffuseReflectionLambert) -{ - const float3 perp = normalize(float3(0.f, 0.f, 1.f)); - const float3 oblique = normalize(float3(0.5f, 0.f, 0.5f)); - const float3 grazing = normalize(float3(0.f, 1.f, 0.01f)); - - testSampling( - ctx, {"Rendering.Materials.BxDF", - "DiffuseReflectionLambert", - "bsdf.albedo = float3(1.f);", - { - {"perp", perp, {0.f, 0.f, 0.f, 0.f}}, - {"oblique", oblique, {0.f, 0.f, 0.f, 0.f}}, - {"grazing", grazing, {0.f, 0.f, 0.f, 0.f}}, - }} - ); -} - -GPU_TEST(TestBsdf_DiffuseReflectionDisney) -{ - const float3 perp = normalize(float3(0.f, 0.f, 1.f)); - const float3 oblique = normalize(float3(0.5f, 0.f, 0.5f)); - const float3 grazing = normalize(float3(0.f, 1.f, 0.01f)); - - testSampling( - ctx, {"Rendering.Materials.BxDF", - "DiffuseReflectionDisney", - "bsdf.albedo = float3(1.f); bsdf.roughness = 0.5f;", - { - {"perp", perp, {0.f, 0.f, 0.f, 0.f}}, - {"oblique", oblique, {0.f, 0.f, 0.f, 0.f}}, - {"grazing", grazing, {0.f, 0.f, 0.f, 0.f}}, - }} - ); -} - -GPU_TEST(TestBsdf_DiffuseReflectionFrostbite) -{ - const float3 perp = normalize(float3(0.f, 0.f, 1.f)); - const float3 oblique = normalize(float3(0.5f, 0.f, 0.5f)); - const float3 grazing = normalize(float3(0.f, 1.f, 0.01f)); - - testSampling( - ctx, {"Rendering.Materials.BxDF", - "DiffuseReflectionFrostbite", - "bsdf.albedo = float3(1.f); bsdf.roughness = 0.5f;", - { - {"perp", perp, {0.f, 0.f, 0.f, 0.f}}, - {"oblique", oblique, {0.f, 0.f, 0.f, 0.f}}, - {"grazing", grazing, {0.f, 0.f, 0.f, 0.f}}, - }} - ); -} - -GPU_TEST(TestBsdf_DiffuseTransmissionLambert) -{ - const float3 perp = normalize(float3(0.f, 0.f, 1.f)); - const float3 oblique = normalize(float3(0.5f, 0.f, 0.5f)); - const float3 grazing = normalize(float3(0.f, 1.f, 0.01f)); - - testSampling( - ctx, {"Rendering.Materials.BxDF", - "DiffuseTransmissionLambert", - "bsdf.albedo = float3(1.f);", - { - {"perp", perp, {0.f, 0.f, 0.f, 0.f}}, - {"oblique", oblique, {0.f, 0.f, 0.f, 0.f}}, - {"grazing", grazing, {0.f, 0.f, 0.f, 0.f}}, - }} - ); -} - -GPU_TEST(TestBsdf_SpecularReflectionMicrofacet) -{ - const float3 perp = normalize(float3(0.f, 0.f, 1.f)); - const float3 oblique = normalize(float3(0.5f, 0.f, 0.5f)); - const float3 grazing = normalize(float3(0.f, 1.f, 0.01f)); - - testSampling( - ctx, {"Rendering.Materials.BxDF", - "SpecularReflectionMicrofacet", - "bsdf.albedo = float3(1.f); bsdf.activeLobes = 0xff; bsdf.alpha = params.x;", - { - {"smooth_perp", perp, {0.05f, 0.f, 0.f, 0.f}}, - {"smooth_oblique", oblique, {0.05f, 0.f, 0.f, 0.f}}, - {"smooth_grazing", grazing, {0.05f, 0.f, 0.f, 0.f}}, - {"rough_perp", perp, {0.5f, 0.f, 0.f, 0.f}}, - {"rough_oblique", oblique, {0.5f, 0.f, 0.f, 0.f}}, - {"rough_grazing", grazing, {0.5f, 0.f, 0.f, 0.f}}, - }} - ); -} - -GPU_TEST(TestBsdf_SpecularReflectionTransmissionMicrofacet, "Disabled, not passing") -{ - const float3 perp = normalize(float3(0.f, 0.f, 1.f)); - const float3 oblique = normalize(float3(0.5f, 0.f, 0.5f)); - const float3 grazing = normalize(float3(0.f, 1.f, 0.01f)); - - testSampling( - ctx, {"Rendering.Materials.BxDF", - "SpecularReflectionTransmissionMicrofacet", - "bsdf.activeLobes = 0xff; bsdf.transmissionAlbedo = float3(1.f); bsdf.alpha = params.x; bsdf.eta = params.y;", - { - {"to_glass_smooth_perp", perp, {0.05f, 0.67f, 0.f, 0.f}}, - {"to_glass_smooth_oblique", oblique, {0.05f, 0.67f, 0.f, 0.f}}, - {"to_glass_smooth_grazing", grazing, {0.05f, 0.67f, 0.f, 0.f}}, - {"to_glass_rough_perp", perp, {0.5f, 0.67f, 0.f, 0.f}}, - {"to_glass_rough_oblique", oblique, {0.5f, 0.67f, 0.f, 0.f}}, - {"to_glass_rough_grazing", grazing, {0.5f, 0.67f, 0.f, 0.f}}, - {"from_glass_smooth_perp", perp, {0.05f, 1.5f, 0.f, 0.f}}, - {"from_glass_smooth_oblique", oblique, {0.05f, 1.5f, 0.f, 0.f}}, - {"from_glass_smooth_grazing", grazing, {0.05f, 1.5f, 0.f, 0.f}}, - {"from_glass_rough_perp", perp, {0.5f, 1.5f, 0.f, 0.f}}, - {"from_glass_rough_oblique", oblique, {0.5f, 1.5f, 0.f, 0.f}}, - {"from_glass_rough_grazing", grazing, {0.5f, 1.5f, 0.f, 0.f}}, - }} - ); -} - -/// The NEW, post-refactor BSDFs - GPU_TEST(TestBsdf_DisneyDiffuseBRDF) { const float3 perp = normalize(float3(0.f, 0.f, 1.f)); @@ -567,7 +443,7 @@ GPU_TEST(TestBsdf_SpecularMicrofacetBRDF) ); } -GPU_TEST(TestBsdf_SpecularMicrofacetBTDF, "Disabled, not passing") +GPU_TEST(TestBsdf_SpecularMicrofacetBSDF, "Disabled, not passing") { const float3 perp = normalize(float3(0.f, 0.f, 1.f)); const float3 oblique = normalize(float3(0.5f, 0.f, 0.5f)); @@ -575,7 +451,7 @@ GPU_TEST(TestBsdf_SpecularMicrofacetBTDF, "Disabled, not passing") testSampling( ctx, {"Rendering.Materials.BSDFs.SpecularMicrofacet", - "SpecularMicrofacetBTDF", + "SpecularMicrofacetBSDF", "bsdf.activeLobes = 0xff; bsdf.transmissionAlbedo = float3(1.f); bsdf.alpha = params.x; bsdf.eta = params.y;", { {"to_glass_smooth_perp", perp, {0.05f, 0.67f, 0.f, 0.f}}, diff --git a/Source/Tools/FalcorTest/Tests/Scene/Material/HairChiang16Tests.cpp b/Source/Tools/FalcorTest/Tests/Scene/Material/HairChiang16Tests.cpp index a5110e161..67d113f0a 100644 --- a/Source/Tools/FalcorTest/Tests/Scene/Material/HairChiang16Tests.cpp +++ b/Source/Tools/FalcorTest/Tests/Scene/Material/HairChiang16Tests.cpp @@ -52,7 +52,7 @@ void testWhiteFurnace(GPUUnitTestContext& ctx, const std::string& funcName, cons } // Create sample generator. - SampleGenerator::SharedPtr pSampleGenerator = SampleGenerator::create(ctx.getDevice(), SAMPLE_GENERATOR_UNIFORM); + ref pSampleGenerator = SampleGenerator::create(ctx.getDevice(), SAMPLE_GENERATOR_UNIFORM); // Setup GPU test. auto defines = pSampleGenerator->getDefines(); @@ -107,7 +107,7 @@ GPU_TEST(HairChiang16_PbrtReference) } // Create sample generator. - SampleGenerator::SharedPtr pSampleGenerator = SampleGenerator::create(ctx.getDevice(), SAMPLE_GENERATOR_UNIFORM); + ref pSampleGenerator = SampleGenerator::create(ctx.getDevice(), SAMPLE_GENERATOR_UNIFORM); // Setup GPU test. auto defines = pSampleGenerator->getDefines(); @@ -165,7 +165,7 @@ GPU_TEST(HairChiang16_ImportanceSamplingWeights) } // Create sample generator. - SampleGenerator::SharedPtr pSampleGenerator = SampleGenerator::create(ctx.getDevice(), SAMPLE_GENERATOR_UNIFORM); + ref pSampleGenerator = SampleGenerator::create(ctx.getDevice(), SAMPLE_GENERATOR_UNIFORM); // Setup GPU test. auto defines = pSampleGenerator->getDefines(); @@ -215,7 +215,7 @@ GPU_TEST(HairChiang16_SamplingConsistency) } // Create sample generator. - SampleGenerator::SharedPtr pSampleGenerator = SampleGenerator::create(ctx.getDevice(), SAMPLE_GENERATOR_UNIFORM); + ref pSampleGenerator = SampleGenerator::create(ctx.getDevice(), SAMPLE_GENERATOR_UNIFORM); // Setup GPU test. auto defines = pSampleGenerator->getDefines(); diff --git a/Source/Tools/FalcorTest/Tests/Scene/Material/MERLFileTests.cpp b/Source/Tools/FalcorTest/Tests/Scene/Material/MERLFileTests.cpp index 43918a6a6..921490906 100644 --- a/Source/Tools/FalcorTest/Tests/Scene/Material/MERLFileTests.cpp +++ b/Source/Tools/FalcorTest/Tests/Scene/Material/MERLFileTests.cpp @@ -50,7 +50,7 @@ GPU_TEST(MERLFile) const float3 expected = float3(0.5f); auto lut = merlFile.prepareAlbedoLUT(ctx.getDevice()); - EXPECT_EQ(lut.size(), (size_t)MERLMaterialData::kAlbedoLUTSize); + EXPECT_EQ(lut.size(), MERLMaterialData::kAlbedoLUTSize); for (auto v : lut) { EXPECT_EQ(v.x, expected.x); diff --git a/Source/Tools/FalcorTest/Tests/Slang/CastFloat16.cpp b/Source/Tools/FalcorTest/Tests/Slang/CastFloat16.cpp index 88878013e..563c9ec91 100644 --- a/Source/Tools/FalcorTest/Tests/Slang/CastFloat16.cpp +++ b/Source/Tools/FalcorTest/Tests/Slang/CastFloat16.cpp @@ -40,7 +40,7 @@ std::uniform_real_distribution u; GPU_TEST(CastFloat16) { - Device* pDevice = ctx.getDevice().get(); + ref pDevice = ctx.getDevice(); ctx.createProgram("Tests/Slang/CastFloat16.cs.slang", "testCastFloat16", Program::DefineList(), Shader::CompilerFlags::None, "6_5"); ctx.allocateStructuredBuffer("result", kNumElems); diff --git a/Source/Tools/FalcorTest/Tests/Slang/Float16Tests.cpp b/Source/Tools/FalcorTest/Tests/Slang/Float16Tests.cpp index 1c52470ef..f68380c4e 100644 --- a/Source/Tools/FalcorTest/Tests/Slang/Float16Tests.cpp +++ b/Source/Tools/FalcorTest/Tests/Slang/Float16Tests.cpp @@ -44,7 +44,7 @@ std::uniform_real_distribution u; void test(GPUUnitTestContext& ctx, const std::string& shaderModel, bool useUav) { - Device* pDevice = ctx.getDevice().get(); + ref pDevice = ctx.getDevice(); Program::DefineList defines = {{"USE_UAV", useUav ? "1" : "0"}}; diff --git a/Source/Tools/FalcorTest/Tests/Slang/Float64Tests.cpp b/Source/Tools/FalcorTest/Tests/Slang/Float64Tests.cpp index 92091001d..b766e8c09 100644 --- a/Source/Tools/FalcorTest/Tests/Slang/Float64Tests.cpp +++ b/Source/Tools/FalcorTest/Tests/Slang/Float64Tests.cpp @@ -45,7 +45,7 @@ std::uniform_real_distribution u; void test(GPUUnitTestContext& ctx, const std::string& shaderModel, bool useUav) { - Device* pDevice = ctx.getDevice().get(); + ref pDevice = ctx.getDevice(); Program::DefineList defines = {{"USE_UAV", useUav ? "1" : "0"}}; diff --git a/Source/Tools/FalcorTest/Tests/Slang/InheritanceTests.cpp b/Source/Tools/FalcorTest/Tests/Slang/InheritanceTests.cpp index e8bc324b2..5950e00cf 100644 --- a/Source/Tools/FalcorTest/Tests/Slang/InheritanceTests.cpp +++ b/Source/Tools/FalcorTest/Tests/Slang/InheritanceTests.cpp @@ -61,7 +61,7 @@ std::uniform_int_distribution ui; GPU_TEST(Inheritance_ManualCreate) { - Device* pDevice = ctx.getDevice().get(); + ref pDevice = ctx.getDevice(); Program::DefineList defines; defines.add("NUM_TESTS", std::to_string(kNumTests)); @@ -113,7 +113,7 @@ GPU_TEST(Inheritance_ManualCreate) GPU_TEST(Inheritance_ConformanceCreate) { - Device* pDevice = ctx.getDevice().get(); + ref pDevice = ctx.getDevice(); Program::DefineList defines; defines.add("NUM_TESTS", std::to_string(kNumTests)); diff --git a/Source/Tools/FalcorTest/Tests/Slang/Int64Tests.cpp b/Source/Tools/FalcorTest/Tests/Slang/Int64Tests.cpp index f9f392ca2..0b120f831 100644 --- a/Source/Tools/FalcorTest/Tests/Slang/Int64Tests.cpp +++ b/Source/Tools/FalcorTest/Tests/Slang/Int64Tests.cpp @@ -44,7 +44,7 @@ std::mt19937 r; void test(GPUUnitTestContext& ctx, const std::string& shaderModel, bool useUav) { - Device* pDevice = ctx.getDevice().get(); + ref pDevice = ctx.getDevice(); Program::DefineList defines = {{"USE_UAV", useUav ? "1" : "0"}}; diff --git a/Source/Tools/FalcorTest/Tests/Slang/ShaderString.cpp b/Source/Tools/FalcorTest/Tests/Slang/ShaderString.cpp index e2d287602..07b0a2f79 100644 --- a/Source/Tools/FalcorTest/Tests/Slang/ShaderString.cpp +++ b/Source/Tools/FalcorTest/Tests/Slang/ShaderString.cpp @@ -68,7 +68,7 @@ const uint32_t kSize = 32; GPU_TEST(ShaderStringInline) { - Device* pDevice = ctx.getDevice().get(); + ref pDevice = ctx.getDevice(); // Create program with generated code placed inline in the same translation // unit as the entry point. diff --git a/Source/Tools/FalcorTest/Tests/Slang/SlangInheritance.cpp b/Source/Tools/FalcorTest/Tests/Slang/SlangInheritance.cpp index 6508b09bf..2db2b0dca 100644 --- a/Source/Tools/FalcorTest/Tests/Slang/SlangInheritance.cpp +++ b/Source/Tools/FalcorTest/Tests/Slang/SlangInheritance.cpp @@ -78,7 +78,7 @@ GPU_TEST(SlangStructInheritanceReflection, "Not working yet") GPU_TEST(SlangStructInheritanceLayout) { - Device* pDevice = ctx.getDevice().get(); + ref pDevice = ctx.getDevice(); ctx.createProgram("Tests/Slang/SlangInheritance.cs.slang", "main", Program::DefineList(), Shader::CompilerFlags::None, "6_5"); ShaderVar var = ctx.vars().getRootVar(); diff --git a/Source/Tools/FalcorTest/Tests/Slang/SlangMutatingTests.cpp b/Source/Tools/FalcorTest/Tests/Slang/SlangMutatingTests.cpp index e989fe12a..d57d8414d 100644 --- a/Source/Tools/FalcorTest/Tests/Slang/SlangMutatingTests.cpp +++ b/Source/Tools/FalcorTest/Tests/Slang/SlangMutatingTests.cpp @@ -31,7 +31,7 @@ namespace Falcor { GPU_TEST(SlangMutating) { - Device* pDevice = ctx.getDevice().get(); + ref pDevice = ctx.getDevice(); ctx.createProgram("Tests/Slang/SlangMutatingTests.cs.slang", "main", Program::DefineList(), Shader::CompilerFlags::None, "6_3"); ctx.allocateStructuredBuffer("result", 1); diff --git a/Source/Tools/FalcorTest/Tests/Slang/SlangReinterpretCast.cpp b/Source/Tools/FalcorTest/Tests/Slang/SlangReinterpretCast.cpp index 6fb6a37fe..4d9f7281c 100644 --- a/Source/Tools/FalcorTest/Tests/Slang/SlangReinterpretCast.cpp +++ b/Source/Tools/FalcorTest/Tests/Slang/SlangReinterpretCast.cpp @@ -46,7 +46,7 @@ const uint32_t kElems = 128; GPU_TEST(SlangReinterpretCast) { - Device* pDevice = ctx.getDevice().get(); + ref pDevice = ctx.getDevice(); ctx.createProgram("Tests/Slang/SlangReinterpretCast.cs.slang", "main", Program::DefineList(), Shader::CompilerFlags::None, "6_5"); ctx.allocateStructuredBuffer("resultA", kElems); diff --git a/Source/Tools/FalcorTest/Tests/Slang/StructuredBufferMatrix.cpp b/Source/Tools/FalcorTest/Tests/Slang/StructuredBufferMatrix.cpp index 8b460dcaa..4f00fd234 100644 --- a/Source/Tools/FalcorTest/Tests/Slang/StructuredBufferMatrix.cpp +++ b/Source/Tools/FalcorTest/Tests/Slang/StructuredBufferMatrix.cpp @@ -34,7 +34,7 @@ namespace { void runTest2(GPUUnitTestContext& ctx, Program::DefineList defines) { - Device* pDevice = ctx.getDevice().get(); + ref pDevice = ctx.getDevice(); ctx.createProgram( "Tests/Slang/StructuredBufferMatrix.cs.slang", "testStructuredBufferMatrixLoad2", defines, Shader::CompilerFlags::DumpIntermediates, @@ -70,7 +70,7 @@ void runTest2(GPUUnitTestContext& ctx, Program::DefineList defines) GPU_TEST(StructuredBufferMatrixLoad1) { - Device* pDevice = ctx.getDevice().get(); + ref pDevice = ctx.getDevice(); ctx.createProgram( "Tests/Slang/StructuredBufferMatrix.cs.slang", "testStructuredBufferMatrixLoad1", Program::DefineList(), diff --git a/Source/Tools/FalcorTest/Tests/Slang/TemplatedLoad.cpp b/Source/Tools/FalcorTest/Tests/Slang/TemplatedLoad.cpp index 828826226..0bdccdcc2 100644 --- a/Source/Tools/FalcorTest/Tests/Slang/TemplatedLoad.cpp +++ b/Source/Tools/FalcorTest/Tests/Slang/TemplatedLoad.cpp @@ -46,7 +46,7 @@ std::vector generateData(const size_t n) void test(GPUUnitTestContext& ctx, const std::string& entryPoint, const size_t n) { - Device* pDevice = ctx.getDevice().get(); + ref pDevice = ctx.getDevice(); std::vector elems = generateData(n); diff --git a/Source/Tools/FalcorTest/Tests/Slang/UnboundedDescriptorArray.cpp b/Source/Tools/FalcorTest/Tests/Slang/UnboundedDescriptorArray.cpp index eb79c532a..cf5129693 100644 --- a/Source/Tools/FalcorTest/Tests/Slang/UnboundedDescriptorArray.cpp +++ b/Source/Tools/FalcorTest/Tests/Slang/UnboundedDescriptorArray.cpp @@ -31,7 +31,7 @@ namespace Falcor { GPU_TEST(UnboundedDescriptorArray, "Unbounded arrays are not yet supported") { - Device* pDevice = ctx.getDevice().get(); + ref pDevice = ctx.getDevice(); const uint32_t kTexCount = 4; diff --git a/Source/Tools/FalcorTest/Tests/Slang/WaveOps.cpp b/Source/Tools/FalcorTest/Tests/Slang/WaveOps.cpp index fa71e16c0..bf5a922fd 100644 --- a/Source/Tools/FalcorTest/Tests/Slang/WaveOps.cpp +++ b/Source/Tools/FalcorTest/Tests/Slang/WaveOps.cpp @@ -112,7 +112,7 @@ std::vector computeMinMaxResult(const std::vector& data, uint32_t void testWaveMinMax(GPUUnitTestContext& ctx, bool conditional) { - Device* pDevice = ctx.getDevice().get(); + ref pDevice = ctx.getDevice(); Program::DefineList defines = {{"CONDITIONAL", conditional ? "1" : "0"}}; ctx.createProgram(kShaderFilename, "testWaveMinMax", defines, Shader::CompilerFlags::None, "6_0"); @@ -158,7 +158,7 @@ void testWaveMinMax(GPUUnitTestContext& ctx, bool conditional) uint32_t queryLaneCount(GPUUnitTestContext& ctx) { - Device* pDevice = ctx.getDevice().get(); + ref pDevice = ctx.getDevice(); ctx.createProgram(kShaderFilename, "testWaveGetLaneCount", Program::DefineList(), Shader::CompilerFlags::None, "6_0"); @@ -186,7 +186,7 @@ GPU_TEST(WaveGetLaneCount) // WaveMatch intrinsic is available only on D3D12. GPU_TEST_D3D12(WaveMatch) { - Device* pDevice = ctx.getDevice().get(); + ref pDevice = ctx.getDevice(); ctx.createProgram(kShaderFilename, "testWaveMatch", Program::DefineList(), Shader::CompilerFlags::None, "6_5"); ctx.allocateStructuredBuffer("result", kNumElems); @@ -242,7 +242,7 @@ GPU_TEST(WaveMaxSimpleFloat, "Disabled due to compiler issues") // Input: -15,-14, ..., -3, -2, -1, ..., 16 // Output: -2, -2, ..., -2, -2, -1, ..., 16 - Device* pDevice = ctx.getDevice().get(); + ref pDevice = ctx.getDevice(); if (uint32_t laneCount = queryLaneCount(ctx); laneCount != 32) throw SkippingTestException("Test assumes warp size 32"); @@ -277,7 +277,7 @@ GPU_TEST(WaveMaxSimpleInt) // Input: -15,-14, ..., -3, -2, -1, ..., 16 // Output: -2, -2, ..., -2, -2, -1, ..., 16 - Device* pDevice = ctx.getDevice().get(); + ref pDevice = ctx.getDevice(); if (uint32_t laneCount = queryLaneCount(ctx); laneCount != 32) throw SkippingTestException("Test assumes warp size 32"); diff --git a/Source/Tools/FalcorTest/Tests/Utils/BitTricksTests.cpp b/Source/Tools/FalcorTest/Tests/Utils/BitTricksTests.cpp index 15c3a793a..6884fc3fa 100644 --- a/Source/Tools/FalcorTest/Tests/Utils/BitTricksTests.cpp +++ b/Source/Tools/FalcorTest/Tests/Utils/BitTricksTests.cpp @@ -48,7 +48,7 @@ uint32_t referenceBitInterleave(uint32_t x, uint32_t y, uint32_t m) GPU_TEST(BitInterleave) { - Device* pDevice = ctx.getDevice().get(); + ref pDevice = ctx.getDevice(); const uint32_t tests = 5; const uint32_t n = 1 << 16; @@ -63,7 +63,7 @@ GPU_TEST(BitInterleave) for (auto& it : testData) it = r(); - Buffer::SharedPtr pTestDataBuffer = + ref pTestDataBuffer = Buffer::create(pDevice, n * sizeof(uint32_t), Resource::BindFlags::ShaderResource, Buffer::CpuAccess::None, testData.data()); // Setup and run GPU test. diff --git a/Source/Tools/FalcorTest/Tests/Utils/BitonicSortTests.cpp b/Source/Tools/FalcorTest/Tests/Utils/BitonicSortTests.cpp index 3a7b4f35c..e0f7677d6 100644 --- a/Source/Tools/FalcorTest/Tests/Utils/BitonicSortTests.cpp +++ b/Source/Tools/FalcorTest/Tests/Utils/BitonicSortTests.cpp @@ -47,7 +47,7 @@ void bitonicSortRef(std::vector& data, const uint32_t chunkSize) void testGpuSort(GPUUnitTestContext& ctx, BitonicSort& bitonicSort, const uint32_t n, const uint32_t chunkSize) { - Device* pDevice = ctx.getDevice().get(); + ref pDevice = ctx.getDevice(); // Create a buffer of random data to use as test data. std::vector testData(n); @@ -55,7 +55,7 @@ void testGpuSort(GPUUnitTestContext& ctx, BitonicSort& bitonicSort, const uint32 for (auto& it : testData) it = r(); - Buffer::SharedPtr pTestDataBuffer = + ref pTestDataBuffer = Buffer::create(pDevice, n * sizeof(uint32_t), Resource::BindFlags::UnorderedAccess, Buffer::CpuAccess::None, testData.data()); // Execute sort on the GPU. diff --git a/Source/Tools/FalcorTest/Tests/Utils/BufferAllocatorTests.cpp b/Source/Tools/FalcorTest/Tests/Utils/BufferAllocatorTests.cpp index 2037cc0b5..2fd00a763 100644 --- a/Source/Tools/FalcorTest/Tests/Utils/BufferAllocatorTests.cpp +++ b/Source/Tools/FalcorTest/Tests/Utils/BufferAllocatorTests.cpp @@ -111,7 +111,7 @@ GPU_TEST(BufferAllocatorNoAlign) // Validate GPU buffer. auto validateGpuBuffer = [&]() { - Buffer::SharedPtr pBuffer = buf.getGPUBuffer(ctx.getDevice().get()); + ref pBuffer = buf.getGPUBuffer(ctx.getDevice()); EXPECT(!pBuffer->isStructured()); EXPECT(!pBuffer->isTyped()); @@ -196,7 +196,7 @@ GPU_TEST(BufferAllocatorAlign) } // Get the GPU buffer. Make sure it's the expected size. - Buffer::SharedPtr pBuffer = buf.getGPUBuffer(ctx.getDevice().get()); + ref pBuffer = buf.getGPUBuffer(ctx.getDevice()); EXPECT(!pBuffer->isStructured()); EXPECT(!pBuffer->isTyped()); @@ -231,7 +231,7 @@ GPU_TEST(BufferAllocatorStructNoAlign) // Validate GPU buffer. { - Buffer::SharedPtr pBuffer = buf.getGPUBuffer(ctx.getDevice().get()); + ref pBuffer = buf.getGPUBuffer(ctx.getDevice()); EXPECT(pBuffer->isStructured()); EXPECT(!pBuffer->isTyped()); @@ -300,7 +300,7 @@ GPU_TEST(BufferAllocatorStructAlign) // Validate GPU buffer. { - Buffer::SharedPtr pBuffer = buf.getGPUBuffer(ctx.getDevice().get()); + ref pBuffer = buf.getGPUBuffer(ctx.getDevice()); EXPECT(pBuffer->isStructured()); EXPECT(!pBuffer->isTyped()); diff --git a/Source/Tools/FalcorTest/Tests/Utils/Color/SpectrumUtilsTests.cpp b/Source/Tools/FalcorTest/Tests/Utils/Color/SpectrumUtilsTests.cpp index 16beb35dc..c845e3293 100644 --- a/Source/Tools/FalcorTest/Tests/Utils/Color/SpectrumUtilsTests.cpp +++ b/Source/Tools/FalcorTest/Tests/Utils/Color/SpectrumUtilsTests.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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 @@ GPU_TEST(WavelengthToXYZ) EXPECT_GE(res.z, 0.f); float3 e = ref - res; - maxSqrError = glm::max(maxSqrError, e * e); + maxSqrError = max(maxSqrError, e * e); } ctx.unmapBuffer("result"); diff --git a/Source/Tools/FalcorTest/Tests/Utils/ColorUtilsTests.cpp b/Source/Tools/FalcorTest/Tests/Utils/ColorUtilsTests.cpp index a8b21da3c..981e1b6ed 100644 --- a/Source/Tools/FalcorTest/Tests/Utils/ColorUtilsTests.cpp +++ b/Source/Tools/FalcorTest/Tests/Utils/ColorUtilsTests.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -52,8 +52,8 @@ CPU_TEST(ColorTransforms) auto dist = std::uniform_real_distribution(); auto u = [&]() -> float { return dist(rng); }; - const rmcv::mat3 LMS_CAT02 = kColorTransform_LMStoXYZ_CAT02 * kColorTransform_XYZtoLMS_CAT02; - const rmcv::mat3 LMS_Bradford = kColorTransform_LMStoXYZ_Bradford * kColorTransform_XYZtoLMS_Bradford; + const float3x3 LMS_CAT02 = mul(kColorTransform_LMStoXYZ_CAT02, kColorTransform_XYZtoLMS_CAT02); + const float3x3 LMS_Bradford = mul(kColorTransform_LMStoXYZ_Bradford, kColorTransform_XYZtoLMS_Bradford); // Run test code that transforms random colors between different spaces. for (uint32_t i = 0; i < n; i++) @@ -65,11 +65,11 @@ CPU_TEST(ColorTransforms) EXPECT_LE(maxAbsDiff(res1, c), kMaxError); // Test XYZ<->LMS using the CAT02 transform. - float3 res2 = LMS_CAT02 * c; + float3 res2 = mul(LMS_CAT02, c); EXPECT_LE(maxAbsDiff(res2, c), kMaxError); // Test XYZ<->LMS using the Bradford transform - float3 res3 = LMS_Bradford * c; + float3 res3 = mul(LMS_Bradford, c); EXPECT_LE(maxAbsDiff(res3, c), kMaxError); } } @@ -79,7 +79,7 @@ CPU_TEST(WhiteBalance) const float3 white = {1, 1, 1}; // The white point should be 6500K. Verify that we get pure white back. - float3 wbWhite = calculateWhiteBalanceTransformRGB_Rec709(6500.f) * white; + float3 wbWhite = mul(calculateWhiteBalanceTransformRGB_Rec709(6500.f), white); EXPECT_LE(maxAbsDiff(wbWhite, white), kMaxError); // Test white balance transform at a few different color temperatures. @@ -89,9 +89,9 @@ CPU_TEST(WhiteBalance) // - Cloudy (7000K) => yellowish tint (r > g > b) // - Sunny (5500K) => blueish tint (r < g < b) // - Indoor (3000K) => stronger bluish tint (r < g < b) - float3 wbCloudy = calculateWhiteBalanceTransformRGB_Rec709(7000.f) * white; - float3 wbSunny = calculateWhiteBalanceTransformRGB_Rec709(5500.f) * white; - float3 wbIndoor = calculateWhiteBalanceTransformRGB_Rec709(3000.f) * white; + float3 wbCloudy = mul(calculateWhiteBalanceTransformRGB_Rec709(7000.f), white); + float3 wbSunny = mul(calculateWhiteBalanceTransformRGB_Rec709(5500.f), white); + float3 wbIndoor = mul(calculateWhiteBalanceTransformRGB_Rec709(3000.f), white); EXPECT_GE(wbCloudy.r, wbCloudy.g); EXPECT_GE(wbCloudy.g, wbCloudy.b); diff --git a/Source/Tools/FalcorTest/Tests/Utils/Debug/WarpProfilerTests.cpp b/Source/Tools/FalcorTest/Tests/Utils/Debug/WarpProfilerTests.cpp index 28f811a41..30a15ad5a 100644 --- a/Source/Tools/FalcorTest/Tests/Utils/Debug/WarpProfilerTests.cpp +++ b/Source/Tools/FalcorTest/Tests/Utils/Debug/WarpProfilerTests.cpp @@ -32,7 +32,7 @@ namespace Falcor { GPU_TEST_D3D12(WarpProfiler) { - WarpProfiler profiler(ctx.getDevice().get(), 4); + WarpProfiler profiler(ctx.getDevice(), 4); Program::Desc desc; desc.addShaderLibrary("Tests/Utils/Debug/WarpProfilerTests.cs.slang").csEntry("main"); diff --git a/Source/Tools/FalcorTest/Tests/Utils/Float16TypesTests.cpp b/Source/Tools/FalcorTest/Tests/Utils/Float16TypesTests.cpp index 1f246aef3..fca5b9d6b 100644 --- a/Source/Tools/FalcorTest/Tests/Utils/Float16TypesTests.cpp +++ b/Source/Tools/FalcorTest/Tests/Utils/Float16TypesTests.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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 "Testing/UnitTest.h" -#include "Utils/Math/Float16.h" +#include "Utils/Math/ScalarMath.h" #include // TODO C++20: Replace with #include @@ -36,11 +36,11 @@ namespace { std::mt19937 rng; -template +template void testVector(CPUUnitTestContext& ctx) { - using floatN = glm::vec; - using float16_tN = tfloat16_vec; + using floatN = ::Falcor::math::vector; + using float16_tN = ::Falcor::math::vector; std::uniform_real_distribution dist(-65504.f, 65504.f); // Numerical range of float16 auto u = [&]() { return dist(rng); }; @@ -77,16 +77,16 @@ CPU_TEST(Float16Vector) EXPECT_EQ(sizeof(float16_t4), 8); // Test direct element access. - float16_t2 a = float16_t2(1.f, 2.f); + float16_t2 a = float16_t2(1.h, 2.h); EXPECT_EQ((float)a.x, 1.f); EXPECT_EQ((float)a.y, 2.f); - float16_t3 b = float16_t3(1.f, 2.f, 3.f); + float16_t3 b = float16_t3(1.h, 2.h, 3.h); EXPECT_EQ((float)b.x, 1.f); EXPECT_EQ((float)b.y, 2.f); EXPECT_EQ((float)b.z, 3.f); - float16_t4 c = float16_t4(1.f, 2.f, 3.f, 4.f); + float16_t4 c = float16_t4(1.h, 2.h, 3.h, 4.h); EXPECT_EQ((float)c.x, 1.f); EXPECT_EQ((float)c.y, 2.f); EXPECT_EQ((float)c.z, 3.f); diff --git a/Source/Tools/FalcorTest/Tests/Utils/GeometryHelpersTests.cpp b/Source/Tools/FalcorTest/Tests/Utils/GeometryHelpersTests.cpp index 436d0b0b8..d0f849188 100644 --- a/Source/Tools/FalcorTest/Tests/Utils/GeometryHelpersTests.cpp +++ b/Source/Tools/FalcorTest/Tests/Utils/GeometryHelpersTests.cpp @@ -28,7 +28,6 @@ #include "Testing/UnitTest.h" #include "Utils/HostDeviceShared.slangh" #include "Utils/Math/Common.h" -#include #include #include @@ -81,11 +80,11 @@ struct BBoxTestCase void runBBoxTestComputeShader(GPUUnitTestContext& ctx, const BBoxTestCase* testCases, int nTests, const char* entrypoint) { - Device* pDevice = ctx.getDevice().get(); + ref pDevice = ctx.getDevice(); - Buffer::SharedPtr pOriginBuffer = Buffer::createTyped(pDevice, nTests); - Buffer::SharedPtr pAABBMinBuffer = Buffer::createTyped(pDevice, nTests); - Buffer::SharedPtr pAABBMaxBuffer = Buffer::createTyped(pDevice, nTests); + ref pOriginBuffer = Buffer::createTyped(pDevice, nTests); + ref pAABBMinBuffer = Buffer::createTyped(pDevice, nTests); + ref pAABBMaxBuffer = Buffer::createTyped(pDevice, nTests); for (int i = 0; i < nTests; ++i) { @@ -220,7 +219,7 @@ GPU_TEST_D3D12(ComputeRayOrigin) std::mt19937 rng; auto dist = std::uniform_real_distribution(-1.f, 1.f); - auto r = [&]() -> float { return dist(rng); }; + auto nextRandom = [&]() -> float { return dist(rng); }; // Create random test data. std::vector testPositions(nTests); @@ -228,8 +227,8 @@ GPU_TEST_D3D12(ComputeRayOrigin) for (uint32_t i = 0; i < nTests; i++) { float scale = std::pow(10.f, (float)i / nTests * 60.f - 30.f); // 1e-30..1e30 - testPositions[i] = float3(r(), r(), r()) * scale; - testNormals[i] = glm::normalize(float3(r(), r(), r())); + testPositions[i] = float3(nextRandom(), nextRandom(), nextRandom()) * scale; + testNormals[i] = normalize(float3(nextRandom(), nextRandom(), nextRandom())); } // Setup and run GPU test. @@ -275,7 +274,7 @@ GPU_TEST(BoxSubtendedConeAngleAverageRandoms) GPU_TEST(SphereSubtendedAngle) { - Device* pDevice = ctx.getDevice().get(); + ref pDevice = ctx.getDevice(); // Generate test data... struct TestCase @@ -296,7 +295,7 @@ GPU_TEST(SphereSubtendedAngle) }; int nTests = sizeof(testCases) / sizeof(testCases[0]); - Buffer::SharedPtr pTestCaseBuffer = Buffer::createTyped(pDevice, nTests); + ref pTestCaseBuffer = Buffer::createTyped(pDevice, nTests); for (int i = 0; i < nTests; ++i) { @@ -341,7 +340,7 @@ GPU_TEST(SphereSubtendedAngle) GPU_TEST(ComputeClippedTriangleArea2D) { - Device* pDevice = ctx.getDevice().get(); + ref pDevice = ctx.getDevice(); struct TestCase { @@ -551,12 +550,12 @@ GPU_TEST(ComputeClippedTriangleArea2D) for (int j = 0; j < m; j++) { float v = (j + 0.5f) / m; - float y = lerp(b.minPos.y, b.maxPos.y, v); + float y = math::lerp(b.minPos.y, b.maxPos.y, v); for (int k = 0; k < m; k++) { float u = (k + 0.5f) / m; - float x = lerp(b.minPos.x, b.maxPos.x, u); + float x = math::lerp(b.minPos.x, b.maxPos.x, u); if (t.isInside(float2(x, y))) hits++; diff --git a/Source/Tools/FalcorTest/Tests/Utils/HashUtilsTests.cpp b/Source/Tools/FalcorTest/Tests/Utils/HashUtilsTests.cpp index 75ae8af74..8de681259 100644 --- a/Source/Tools/FalcorTest/Tests/Utils/HashUtilsTests.cpp +++ b/Source/Tools/FalcorTest/Tests/Utils/HashUtilsTests.cpp @@ -53,10 +53,10 @@ uint32_t jenkinsHash(uint32_t a) GPU_TEST(JenkinsHash_CompareToCPU) { - Device* pDevice = ctx.getDevice().get(); + ref pDevice = ctx.getDevice(); // Allocate results buffer (64k dwords). - Buffer::SharedPtr pResultBuffer = Buffer::createTyped(pDevice, 1 << 16, ResourceBindFlags::UnorderedAccess); + ref pResultBuffer = Buffer::createTyped(pDevice, 1 << 16, ResourceBindFlags::UnorderedAccess); ctx.getRenderContext()->clearUAV(pResultBuffer->getUAV().get(), uint4(0)); // Setup and run GPU test. @@ -98,10 +98,10 @@ GPU_TEST(JenkinsHash_PerfectHashGPU) GPU_TEST(JenkinsHash_PerfectHashGPU, "Disabled for performance reasons") #endif { - Device* pDevice = ctx.getDevice().get(); + ref pDevice = ctx.getDevice(); // Allocate results buffer (2^27 dwords). - Buffer::SharedPtr pResultBuffer = Buffer::createTyped(pDevice, 1 << 27, ResourceBindFlags::UnorderedAccess); + ref pResultBuffer = Buffer::createTyped(pDevice, 1 << 27, ResourceBindFlags::UnorderedAccess); ctx.getRenderContext()->clearUAV(pResultBuffer->getUAV().get(), uint4(0)); // Setup and run GPU test. diff --git a/Source/plugins/importers/USDImporter/Subdivision.h b/Source/Tools/FalcorTest/Tests/Utils/Image/TextureManagerTests.cpp similarity index 69% rename from Source/plugins/importers/USDImporter/Subdivision.h rename to Source/Tools/FalcorTest/Tests/Utils/Image/TextureManagerTests.cpp index 7791c1fc9..d5476395b 100644 --- a/Source/plugins/importers/USDImporter/Subdivision.h +++ b/Source/Tools/FalcorTest/Tests/Utils/Image/TextureManagerTests.cpp @@ -25,28 +25,30 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **************************************************************************/ -#include "USDHelpers.h" -#include "Utils.h" - -BEGIN_DISABLE_USD_WARNINGS -#include -#include -#include -#include -END_DISABLE_USD_WARNINGS +#include "Testing/UnitTest.h" +#include "Utils/Image/TextureManager.h" namespace Falcor { +GPU_TEST(TextureManager_LoadMips) +{ + ref pDevice = ctx.getDevice(); + + TextureManager textureManager(pDevice, 10); + + std::filesystem::path path = getRuntimeDirectory() / "data/tests/tiny_.png"; + + auto handle = textureManager.loadTexture(path, false, false, ResourceBindFlags::ShaderResource, false); + EXPECT(handle.isValid()); + EXPECT(!handle.isUdim()); + + auto tex = textureManager.getTexture(handle); + ASSERT(tex != nullptr); -bool refine(const pxr::UsdGeomMesh& geomMesh, - const pxr::HdMeshTopology& topology, - const uint32_t& maxLevel, - const pxr::VtVec3fArray& basePoints, - const pxr::VtVec2fArray& baseUVs, - const TfToken uvFreq, - pxr::HdMeshTopology& refinedTopology, - pxr::VtVec3fArray& refinedPoints, - pxr::VtVec3fArray& refinedNormals, - pxr::VtVec2fArray& refinedUVs, - std::unique_ptr& meshUtil); + EXPECT_EQ(tex->getWidth(), 4); + EXPECT_EQ(tex->getHeight(), 4); + EXPECT_EQ(tex->getDepth(), 1); + EXPECT_EQ(tex->getMipCount(), 3); + EXPECT_EQ(tex->getArraySize(), 1); } +} // namespace Falcor diff --git a/Source/Tools/FalcorTest/Tests/Utils/ImageProcessing.cpp b/Source/Tools/FalcorTest/Tests/Utils/ImageProcessing.cpp index 7e5dc206c..efda80987 100644 --- a/Source/Tools/FalcorTest/Tests/Utils/ImageProcessing.cpp +++ b/Source/Tools/FalcorTest/Tests/Utils/ImageProcessing.cpp @@ -27,7 +27,6 @@ **************************************************************************/ #include "Testing/UnitTest.h" #include "Utils/Image/ImageProcessing.h" -#include "Utils/Math/Float16.h" namespace Falcor { @@ -55,7 +54,7 @@ void testCopyColorChannel( ResourceFormat dstFormat ) { - Device* pDevice = ctx.getDevice().get(); + ref pDevice = ctx.getDevice(); const auto srcChannels = getFormatChannelCount(srcFormat); const auto dstChannels = getFormatChannelCount(dstFormat); diff --git a/Source/Tools/FalcorTest/Tests/Utils/IntersectionHelpersTests.cpp b/Source/Tools/FalcorTest/Tests/Utils/IntersectionHelpersTests.cpp index dcc79682d..cd3479baf 100644 --- a/Source/Tools/FalcorTest/Tests/Utils/IntersectionHelpersTests.cpp +++ b/Source/Tools/FalcorTest/Tests/Utils/IntersectionHelpersTests.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -80,7 +80,7 @@ float3 getRayOrigin(RayOriginLocation loc, float radius, float3 center, float3 h { x = rOut(); y = rOut(); - z = (glm::dot(normal, hit) - normal.x * x - normal.y * y) / normal.z; + z = (dot(normal, hit) - normal.x * x - normal.y * y) / normal.z; return float3(x, y, z); } @@ -89,7 +89,7 @@ float3 getRayOrigin(RayOriginLocation loc, float radius, float3 center, float3 h x = rOut(); y = rOut(); z = rOut(); - } while (x * x + y * y + z * z <= radius * radius || glm::dot(normal, float3(x, y, z) - shiftedHit) >= 0); + } while (x * x + y * y + z * z <= radius * radius || dot(normal, float3(x, y, z) - shiftedHit) >= 0); break; case RayOriginLocation::Inside: do @@ -108,24 +108,24 @@ float3 getRayOrigin(RayOriginLocation loc, float radius, float3 center, float3 h float3 getRayDir(bool hasIntersection, float3 origin, float3 hit, bool normalized) { - if (hit == origin) + if (all(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; + return (normalized) ? normalize(dir) : dir; } float3 dir = hit - origin; if (hasIntersection) { - return (normalized) ? glm::normalize(dir) : dir; + return (normalized) ? normalize(dir) : dir; } else { - return (normalized) ? glm::normalize(-dir) : -dir; + return (normalized) ? normalize(-dir) : -dir; } } } // namespace @@ -145,7 +145,7 @@ GPU_TEST_D3D12(RaySphereIntersection) for (int32_t i = 0; i < 12; i++) { testSphereCenters[i] = float3(r(), r(), r()); - testSphereRadii[i] = abs(r()); + testSphereRadii[i] = std::abs(r()); refIsects[i] = getHitPoint(testSphereRadii[i], testSphereCenters[i]); switch (i) { @@ -208,11 +208,11 @@ GPU_TEST_D3D12(RaySphereIntersection) 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)) + EXPECT(std::abs(isectLoc[i].x - refIsects[i].x) <= eps * (std::abs(isectLoc[i].x) + std::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)) + EXPECT(std::abs(isectLoc[i].y - refIsects[i].y) <= eps * (std::abs(isectLoc[i].y) + std::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)) + EXPECT(std::abs(isectLoc[i].z - refIsects[i].z) <= eps * (std::abs(isectLoc[i].z) + std::abs(refIsects[i].z) + 1.0f)) << "RaySphereTestCase" << i << ", expected " << refIsects[i].z << ", got " << isectLoc[i].z; } diff --git a/Source/Tools/FalcorTest/Tests/Utils/MathHelpersTests.cpp b/Source/Tools/FalcorTest/Tests/Utils/MathHelpersTests.cpp index 43b9e6025..23e761699 100644 --- a/Source/Tools/FalcorTest/Tests/Utils/MathHelpersTests.cpp +++ b/Source/Tools/FalcorTest/Tests/Utils/MathHelpersTests.cpp @@ -26,6 +26,7 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **************************************************************************/ #include "Testing/UnitTest.h" +#include "Utils/Math/ScalarMath.h" #include #include #include @@ -87,7 +88,7 @@ GPU_TEST(MathHelpers_ErrorFunction) for (int32_t i = 0; i < n; ++i) { float t = i / (float)(n - 1); - float x = lerp(-5, 5, t); + float x = math::lerp(-5, 5, t); input[i] = x; ref[i] = std::erf(x); } @@ -117,7 +118,7 @@ GPU_TEST(MathHelpers_InverseErrorFunction) for (int32_t i = 0; i < n; ++i) { float t = i / (float)(n - 1); - input[i] = lerp(-1, 1, t); + input[i] = math::lerp(-1, 1, t); } ctx.allocateStructuredBuffer("result", n); diff --git a/Source/Tools/FalcorTest/Tests/Utils/MatrixTests.cpp b/Source/Tools/FalcorTest/Tests/Utils/MatrixTests.cpp index 1dbd8f492..83cfb75da 100644 --- a/Source/Tools/FalcorTest/Tests/Utils/MatrixTests.cpp +++ b/Source/Tools/FalcorTest/Tests/Utils/MatrixTests.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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 "Testing/UnitTest.h" -#include "Utils/Math/Matrix/Matrix.h" +#include "Utils/Math/Matrix.h" #include #include @@ -34,9 +34,762 @@ namespace Falcor { +template +bool almostEqual(math::vector a, math::vector b, T epsilon = T(1e-5)) +{ + return all(abs(a - b) < math::vector(epsilon)); +} + +template +bool almostEqual(math::quat a, math::quat b, T epsilon = T(1e-5)) +{ + return all(math::vector(b.x - a.x, b.y - a.y, b.z - a.z, b.w - a.w) < math::vector(epsilon)); +} + +#define EXPECT_ALMOST_EQ(a, b) EXPECT_TRUE(almostEqual(a, b)) << fmt::format("{} != {}", a, b) + +CPU_TEST(Matrix_Constructor) +{ + // Default constructor + { + float4x4 m; + EXPECT_EQ(m[0], float4(1, 0, 0, 0)); + EXPECT_EQ(m[1], float4(0, 1, 0, 0)); + EXPECT_EQ(m[2], float4(0, 0, 1, 0)); + EXPECT_EQ(m[3], float4(0, 0, 0, 1)); + } + + // Initializer list constructor + { + float4x4 m({1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}); + EXPECT_EQ(m[0], float4(1, 2, 3, 4)); + EXPECT_EQ(m[1], float4(5, 6, 7, 8)); + EXPECT_EQ(m[2], float4(9, 10, 11, 12)); + EXPECT_EQ(m[3], float4(13, 14, 15, 16)); + } + + // Identity + { + float4x4 m = float4x4::identity(); + EXPECT_EQ(m[0], float4(1, 0, 0, 0)); + EXPECT_EQ(m[1], float4(0, 1, 0, 0)); + EXPECT_EQ(m[2], float4(0, 0, 1, 0)); + EXPECT_EQ(m[3], float4(0, 0, 0, 1)); + } + + // Zeros + { + float4x4 m = float4x4::zeros(); + EXPECT_EQ(m[0], float4(0, 0, 0, 0)); + EXPECT_EQ(m[1], float4(0, 0, 0, 0)); + EXPECT_EQ(m[2], float4(0, 0, 0, 0)); + EXPECT_EQ(m[3], float4(0, 0, 0, 0)); + } +} + +CPU_TEST(Matrix_multilply) +{ + // Scalar multiplication + { + float4x4 m({1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}); + float4x4 m2 = m * 2.f; + EXPECT_EQ(m2[0], float4(2, 4, 6, 8)); + EXPECT_EQ(m2[1], float4(10, 12, 14, 16)); + EXPECT_EQ(m2[2], float4(18, 20, 22, 24)); + EXPECT_EQ(m2[3], float4(26, 28, 30, 32)); + } + + // Matrix/matrix multiplication + { + float4x4 m1({1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}); + float4x4 m2({-1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16}); + float4x4 m3 = mul(m1, m2); + EXPECT_EQ(m3[0], float4(-90, -100, -110, -120)); + EXPECT_EQ(m3[1], float4(-202, -228, -254, -280)); + EXPECT_EQ(m3[2], float4(-314, -356, -398, -440)); + EXPECT_EQ(m3[3], float4(-426, -484, -542, -600)); + } + + // Matrix/vector multiplication + { + float4x4 m({1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}); + float4 v(1, 2, 3, 4); + float4 v2 = mul(m, v); + EXPECT_EQ(v2, float4(30, 70, 110, 150)); + } + + // Vector/matrix multiplication + { + float4x4 m({1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}); + float4 v(1, 2, 3, 4); + float4 v2 = mul(v, m); + EXPECT_EQ(v2, float4(90, 100, 110, 120)); + } +} + +CPU_TEST(Matrix_transformPoint) +{ + float4x4 m({1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}); + float3 v(1, 2, 3); + float3 v2 = transformPoint(m, v); + EXPECT_EQ(v2, float3(18, 46, 74)); +} + +CPU_TEST(Matrix_transformVector) +{ + // 3x3 + { + float3x3 m({1, 2, 3, 4, 5, 6, 7, 8, 9}); + float3 v(1, 2, 3); + float3 v2 = transformVector(m, v); + EXPECT_EQ(v2, float3(14, 32, 50)); + } + + // 4x4 + { + float4x4 m({1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}); + float3 v(1, 2, 3); + float3 v2 = transformVector(m, v); + EXPECT_EQ(v2, float3(14, 38, 62)); + } +} + +CPU_TEST(Matrix_transpose) +{ + // 3x3 + { + float3x3 m({1, 2, 3, 4, 5, 6, 7, 8, 9}); + float3x3 m2 = transpose(m); + EXPECT_EQ(m2[0], float3(1, 4, 7)); + EXPECT_EQ(m2[1], float3(2, 5, 8)); + EXPECT_EQ(m2[2], float3(3, 6, 9)); + } + + // 4x4 + { + float4x4 m({1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}); + float4x4 m2 = transpose(m); + EXPECT_EQ(m2[0], float4(1, 5, 9, 13)); + EXPECT_EQ(m2[1], float4(2, 6, 10, 14)); + EXPECT_EQ(m2[2], float4(3, 7, 11, 15)); + EXPECT_EQ(m2[3], float4(4, 8, 12, 16)); + } +} + +CPU_TEST(Matrix_translate) +{ + float4x4 m({ + 1, 0, 0, 10, // + 0, -1, 0, 20, // + 0, 0, 1, 30, // + 0, 0, 0, 1 // + }); + float4x4 m2 = translate(m, float3(1, 2, 3)); + EXPECT_ALMOST_EQ(m2[0], float4(1, 0, 0, 11)); + EXPECT_ALMOST_EQ(m2[1], float4(0, -1, 0, 18)); + EXPECT_ALMOST_EQ(m2[2], float4(0, 0, 1, 33)); + EXPECT_ALMOST_EQ(m2[3], float4(0, 0, 0, 1)); +} + +CPU_TEST(Matrix_rotate) +{ + float4x4 m({ + 1, 0, 0, 10, // + 0, -1, 0, 20, // + 0, 0, 1, 30, // + 0, 0, 0, 1 // + }); + float4x4 m2 = rotate(m, math::radians(90.f), float3(0, 1, 0)); + EXPECT_ALMOST_EQ(m2[0], float4(0, 0, 1, 10)); + EXPECT_ALMOST_EQ(m2[1], float4(0, -1, 0, 20)); + EXPECT_ALMOST_EQ(m2[2], float4(-1, 0, 0, 30)); + EXPECT_ALMOST_EQ(m2[3], float4(0, 0, 0, 1)); +} + +CPU_TEST(Matrix_scale) +{ + float4x4 m({ + 1, 0, 0, 10, // + 0, -1, 0, 20, // + 0, 0, 1, 30, // + 0, 0, 0, 1 // + }); + float4x4 m2 = scale(m, float3(2, 3, 4)); + EXPECT_ALMOST_EQ(m2[0], float4(2, 0, 0, 10)); + EXPECT_ALMOST_EQ(m2[1], float4(0, -3, 0, 20)); + EXPECT_ALMOST_EQ(m2[2], float4(0, 0, 4, 30)); + EXPECT_ALMOST_EQ(m2[3], float4(0, 0, 0, 1)); +} + +CPU_TEST(Matrix_determinant) +{ + // 2x2 + { + float2x2 m1 = float2x2({1, 2, 1, 2}); + EXPECT_EQ(math::determinant(m1), 0); + float2x2 m2 = float2x2({1, 2, 3, 4}); + EXPECT_EQ(math::determinant(m2), -2); + } + + // 3x3 + { + float3x3 m1 = float3x3({1, 2, 3, 4, 5, 6, 7, 8, 9}); + EXPECT_EQ(math::determinant(m1), 0); + float3x3 m2 = float3x3({1, 2, 3, 6, 5, 4, 8, 7, 9}); + EXPECT_EQ(math::determinant(m2), -21); + } + + // 4x4 + { + float4x4 m1 = float4x4({1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}); + EXPECT_EQ(math::determinant(m1), 0); + float4x4 m2 = float4x4({1, 2, 3, 4, 8, 7, 6, 5, 9, 10, 12, 11, 15, 16, 13, 14}); + EXPECT_EQ(math::determinant(m2), 72); + } +} + +CPU_TEST(Matrix_inverse) +{ + // 2x2 + { + float2x2 m = inverse(float2x2({1, 2, 3, 4})); + EXPECT_ALMOST_EQ(m[0], float2(-2, 1)); + EXPECT_ALMOST_EQ(m[1], float2(1.5f, -0.5f)); + } + + // 3x3 + { + float3x3 m = inverse(float3x3({1, 2, 3, 6, 5, 4, 8, 7, 9})); + EXPECT_ALMOST_EQ(m[0], float3(-0.809523f, -0.142857f, 0.333333f)); + EXPECT_ALMOST_EQ(m[1], float3(1.047619f, 0.714285f, -0.666666f)); + EXPECT_ALMOST_EQ(m[2], float3(-0.095238f, -0.428571f, 0.333333f)); + } + + // 4x4 + { + float4x4 m = inverse(float4x4({1, 2, 3, 4, 8, 7, 6, 5, 9, 10, 12, 11, 15, 16, 13, 14})); + EXPECT_ALMOST_EQ(m[0], float4(1.125f, 1.25f, -0.5f, -0.375f)); + EXPECT_ALMOST_EQ(m[1], float4(-1.652777f, -1.527777f, 0.5f, 0.625f)); + EXPECT_ALMOST_EQ(m[2], float4(-0.625f, -0.25f, 0.5f, -0.125f)); + EXPECT_ALMOST_EQ(m[3], float4(1.263888f, 0.638888f, -0.5f, -0.125f)); + } +} + +CPU_TEST(Matrix_extractEulerAngleXYZ) +{ + { + // float4x4 m = math::matrixFromRotationXYZ(math::radians(45.f), math::radians(45.f), math::radians(45.f)); + float4x4 m = float4x4({ + 0.5f, -0.5f, 0.707107f, 0.f, // + 0.853553f, 0.146446f, -0.5f, 0.f, // + 0.146446f, 0.853553f, 0.5f, 0.f, // + 0.f, 0.f, 0.f, 1.f // + }); + float3 angles; + math::extractEulerAngleXYZ(m, angles.x, angles.y, angles.z); + EXPECT_ALMOST_EQ(angles, math::radians(float3(45.f, 45.f, 45.f))); + } + + { + // float4x4 m = math::matrixFromRotationXYZ(math::radians(20.f), math::radians(40.f), math::radians(60.f)); + float4x4 m = float4x4({ + 0.383022f, -0.663414f, 0.642787f, 0.f, // + 0.923720f, 0.279453f, -0.262002f, 0.f, // + -0.005813f, 0.694109f, 0.719846f, 0.f, // + 0.f, 0.f, 0.f, 1.f // + }); + float3 angles; + math::extractEulerAngleXYZ(m, angles.x, angles.y, angles.z); + EXPECT_ALMOST_EQ(angles, math::radians(float3(20.f, 40.f, 60.f))); + } +} + +CPU_TEST(Matrix_decompose) +{ + const auto testDecompose = [&](float4x4 m, float3 expectedScale, quatf expectedOrientation, float3 expectedTranslation, + float3 expectedSkew, float4 expectedPerspective, bool expectedResult = true) + { + float3 scale; + quatf orientation; + float3 translation; + float3 skew; + float4 perspective; + bool result = math::decompose(m, scale, orientation, translation, skew, perspective); + if (expectedResult) + { + EXPECT_ALMOST_EQ(scale, expectedScale); + EXPECT_ALMOST_EQ(orientation, expectedOrientation); + EXPECT_ALMOST_EQ(translation, expectedTranslation); + EXPECT_ALMOST_EQ(skew, expectedSkew); + EXPECT_ALMOST_EQ(perspective, expectedPerspective); + } + EXPECT(result == expectedResult); + }; + + // Zero matrix + testDecompose( + float4x4::zeros(), // matrix + float3(), // scale + quatf(), // orientation + float3(), // translation + float3(), // skew + float4(), // perspective + false // result + ); + + // Identity matrix + testDecompose( + float4x4::identity(), // matrix + float3(1.f, 1.f, 1.f), // scale + quatf::identity(), // orientation + float3(0.f, 0.f, 0.f), // translation + float3(0.f, 0.f, 0.f), // skew + float4(0.f, 0.f, 0.f, 1.f) // perspective + ); + + // Scale only + testDecompose( + float4x4({ + 2.f, 0.f, 0.f, 0.f, // row 0 + 0.f, 3.f, 0.f, 0.f, // row 1 + 0.f, 0.f, 4.f, 0.f, // row 2 + 0.f, 0.f, 0.f, 1.f // row 3 + }), + float3(2.f, 3.f, 4.f), // scale + quatf::identity(), // orientation + float3(0.f, 0.f, 0.f), // translation + float3(0.f, 0.f, 0.f), // skew + float4(0.f, 0.f, 0.f, 1.f) // perspective + ); + + // Orientation only + // float4x4 m = math::matrixFromRotationX(math::radians(45.f)); + testDecompose( + float4x4({ + 1.f, 0.f, 0.f, 0.f, // row 0 + 0.f, 0.707107f, -0.707107f, 0.f, // row 1 + 0.f, 0.707107f, 0.707107f, 0.f, // row 2 + 0.f, 0.f, 0.f, 1.f // row 3 + }), + float3(1.f, 1.f, 1.f), // scale + quatf(0.382683f, 0.f, 0.f, 0.92388f), // orientation + float3(0.f, 0.f, 0.f), // translation + float3(0.f, 0.f, 0.f), // skew + float4(0.f, 0.f, 0.f, 1.f) // perspective + ); + + // Translation only + testDecompose( + float4x4({ + 1.f, 0.f, 0.f, 1.f, // row 0 + 0.f, 1.f, 0.f, 2.f, // row 1 + 0.f, 0.f, 1.f, 3.f, // row 2 + 0.f, 0.f, 0.f, 1.f // row 3 + }), + float3(1.f, 1.f, 1.f), // scale + quatf::identity(), // orientation + float3(1.f, 2.f, 3.f), // translation + float3(0.f, 0.f, 0.f), // skew + float4(0.f, 0.f, 0.f, 1.f) // perspective + ); + + // Skew only + testDecompose( + float4x4({ + 1.f, 2.f, 3.f, 0.f, // row 0 + 0.f, 1.f, 4.f, 0.f, // row 1 + 0.f, 0.f, 1.f, 0.f, // row 2 + 0.f, 0.f, 0.f, 1.f // row 3 + }), + float3(1.f, 1.f, 1.f), // scale + quatf::identity(), // orientation + float3(0.f, 0.f, 0.f), // translation + float3(4.f, 3.f, 2.f), // skew + float4(0.f, 0.f, 0.f, 1.f) // perspective + ); + + // Perspective only + testDecompose( + float4x4({ + 1.f, 0.f, 0.f, 0.f, // row 0 + 0.f, 1.f, 0.f, 0.f, // row 1 + 0.f, 0.f, 1.f, 0.f, // row 2 + 0.1f, 0.2f, 0.3f, 1.f // row 3 + }), + float3(1.f, 1.f, 1.f), // scale + quatf::identity(), // orientation + float3(0.f, 0.f, 0.f), // translation + float3(0.f, 0.f, 0.f), // skew + float4(0.1f, 0.2f, 0.3f, 1.f) // perspective + ); + + // Affine transform + float4x4 m = float4x4::identity(); + m = mul(math::matrixFromScaling(float3(2.f, 3.f, 4.f)), m); + m = mul(math::matrixFromRotationX(math::radians(45.f)), m); + m = mul(math::matrixFromTranslation(float3(1.f, 2.f, 3.f)), m); + testDecompose( + float4x4({ + 2.f, 0.f, 0.f, 1.f, // row 0 + 0.f, 2.12132f, -2.82843f, 2.f, // row 1 + 0.f, 2.12132f, 2.82843f, 3.f, // row 2 + 0.f, 0.f, 0.f, 1.f // row 3 + }), + float3(2.f, 3.f, 4.f), // scale + quatf(0.382683f, 0.f, 0.f, 0.92388f), // orientation + float3(1.f, 2.f, 3.f), // translation + float3(0.f, 0.f, 0.f), // skew + float4(0.f, 0.f, 0.f, 1.f) // perspective + ); +} + +CPU_TEST(Matrix_matrixFromCoefficients) +{ + // 3x3 + { + const float values[9] = {1, 2, 3, 4, 5, 6, 7, 8, 9}; + float3x3 m = math::matrixFromCoefficients(values); + EXPECT_EQ(m[0], float3(1, 2, 3)); + EXPECT_EQ(m[1], float3(4, 5, 6)); + EXPECT_EQ(m[2], float3(7, 8, 9)); + } + + // 4x4 + { + const float values[16] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}; + float4x4 m = math::matrixFromCoefficients(values); + EXPECT_EQ(m[0], float4(1, 2, 3, 4)); + EXPECT_EQ(m[1], float4(5, 6, 7, 8)); + EXPECT_EQ(m[2], float4(9, 10, 11, 12)); + EXPECT_EQ(m[3], float4(13, 14, 15, 16)); + } +} + +CPU_TEST(Matrix_matrixFromColumns) +{ + // 2x4 + { + math::matrix m = math::matrixFromColumns(float2(1, 2), float2(3, 4), float2(5, 6), float2(7, 8)); + EXPECT_EQ(m[0], float4(1, 3, 5, 7)); + EXPECT_EQ(m[1], float4(2, 4, 6, 8)); + } + + // 4x2 + { + math::matrix m = math::matrixFromColumns(float4(1, 2, 3, 4), float4(5, 6, 7, 8)); + EXPECT_EQ(m[0], float2(1, 5)); + EXPECT_EQ(m[1], float2(2, 6)); + EXPECT_EQ(m[2], float2(3, 7)); + EXPECT_EQ(m[3], float2(4, 8)); + } + + // 4x4 + { + float4x4 m = math::matrixFromColumns(float4(1, 2, 3, 4), float4(5, 6, 7, 8), float4(9, 10, 11, 12), float4(13, 14, 15, 16)); + EXPECT_EQ(m[0], float4(1, 5, 9, 13)); + EXPECT_EQ(m[1], float4(2, 6, 10, 14)); + EXPECT_EQ(m[2], float4(3, 7, 11, 15)); + EXPECT_EQ(m[3], float4(4, 8, 12, 16)); + } +} + +CPU_TEST(Matrix_matrixFromDiagonal) +{ + // 3x3 + { + float3x3 m = math::matrixFromDiagonal(float3(1, 2, 3)); + EXPECT_EQ(m[0], float3(1, 0, 0)); + EXPECT_EQ(m[1], float3(0, 2, 0)); + EXPECT_EQ(m[2], float3(0, 0, 3)); + } + + // 4x4 + { + float4x4 m = math::matrixFromDiagonal(float4(1, 2, 3, 4)); + EXPECT_EQ(m[0], float4(1, 0, 0, 0)); + EXPECT_EQ(m[1], float4(0, 2, 0, 0)); + EXPECT_EQ(m[2], float4(0, 0, 3, 0)); + EXPECT_EQ(m[3], float4(0, 0, 0, 4)); + } +} + +CPU_TEST(Matrix_perspective) +{ + float4x4 m = math::perspective(math::radians(45.f), 2.f, 0.1f, 1000.f); + EXPECT_ALMOST_EQ(m[0], float4(1.207107f, 0, 0, 0)); + EXPECT_ALMOST_EQ(m[1], float4(0, 2.414213f, 0, 0)); + EXPECT_ALMOST_EQ(m[2], float4(0, 0, -1.0001f, -0.1f)); + EXPECT_ALMOST_EQ(m[3], float4(0, 0, -1.f, 0)); +} + +CPU_TEST(Matrix_ortho) +{ + float4x4 m = math::ortho(-10.f, 10.f, -10.f, 10.f, 0.1f, 1000.f); + EXPECT_ALMOST_EQ(m[0], float4(0.1f, 0, 0, 0)); + EXPECT_ALMOST_EQ(m[1], float4(0, 0.1f, 0, 0)); + EXPECT_ALMOST_EQ(m[2], float4(0, 0, -0.001f, -0.0001f)); + EXPECT_ALMOST_EQ(m[3], float4(0, 0, 0, 1.0f)); +} + +CPU_TEST(Matrix_matrixFromTranslation) +{ + float4x4 m = math::matrixFromTranslation(float3(1, 2, 3)); + EXPECT_EQ(m[0], float4(1, 0, 0, 1)); + EXPECT_EQ(m[1], float4(0, 1, 0, 2)); + EXPECT_EQ(m[2], float4(0, 0, 1, 3)); + EXPECT_EQ(m[3], float4(0, 0, 0, 1)); +} + +CPU_TEST(Matrix_matrixFromRotation) +{ + // Rotation around X-axis by 90 degrees + { + float4x4 m = math::matrixFromRotation(math::radians(90.f), float3(1, 0, 0)); + EXPECT_ALMOST_EQ(m[0], float4(1, 0, 0, 0)); + EXPECT_ALMOST_EQ(m[1], float4(0, 0, -1, 0)); + EXPECT_ALMOST_EQ(m[2], float4(0, 1, 0, 0)); + EXPECT_ALMOST_EQ(m[3], float4(0, 0, 0, 1)); + } + + // Rotation around X-axis by -45 degrees + { + float4x4 m = math::matrixFromRotation(math::radians(-45.f), float3(1, 0, 0)); + EXPECT_ALMOST_EQ(m[0], float4(1, 0, 0, 0)); + EXPECT_ALMOST_EQ(m[1], float4(0, 0.707106f, 0.707106f, 0)); + EXPECT_ALMOST_EQ(m[2], float4(0, -0.707106f, 0.707106f, 0)); + EXPECT_ALMOST_EQ(m[3], float4(0, 0, 0, 1)); + } + + // Rotation around Y-axis by 90 degrees + { + float4x4 m = math::matrixFromRotation(math::radians(90.f), float3(0, 1, 0)); + EXPECT_ALMOST_EQ(m[0], float4(0, 0, 1, 0)); + EXPECT_ALMOST_EQ(m[1], float4(0, 1, 0, 0)); + EXPECT_ALMOST_EQ(m[2], float4(-1, 0, 0, 0)); + EXPECT_ALMOST_EQ(m[3], float4(0, 0, 0, 1)); + } + + // Rotation around Y-axis by -45 degrees + { + float4x4 m = math::matrixFromRotation(math::radians(-45.f), float3(0, 1, 0)); + EXPECT_ALMOST_EQ(m[0], float4(0.707106f, 0, -0.707106f, 0)); + EXPECT_ALMOST_EQ(m[1], float4(0, 1, 0, 0)); + EXPECT_ALMOST_EQ(m[2], float4(0.707106f, 0, 0.707106f, 0)); + EXPECT_ALMOST_EQ(m[3], float4(0, 0, 0, 1)); + } + + // Rotation around Z-axis by 90 degrees + { + float4x4 m = math::matrixFromRotation(math::radians(90.f), float3(0, 0, 1)); + EXPECT_ALMOST_EQ(m[0], float4(0, -1, 0, 0)); + EXPECT_ALMOST_EQ(m[1], float4(1, 0, 0, 0)); + EXPECT_ALMOST_EQ(m[2], float4(0, 0, 1, 0)); + EXPECT_ALMOST_EQ(m[3], float4(0, 0, 0, 1)); + } + + // Rotation around Z-axis by -45 degrees + { + float4x4 m = math::matrixFromRotation(math::radians(-45.f), float3(0, 0, 1)); + EXPECT_ALMOST_EQ(m[0], float4(0.707106f, 0.707106f, 0, 0)); + EXPECT_ALMOST_EQ(m[1], float4(-0.707106f, 0.707106f, 0, 0)); + EXPECT_ALMOST_EQ(m[2], float4(0, 0, 1, 0)); + EXPECT_ALMOST_EQ(m[3], float4(0, 0, 0, 1)); + } + + // Rotation around oblique axis + { + float4x4 m = math::matrixFromRotation(math::radians(60.f), normalize(float3(1, 1, 1))); + EXPECT_ALMOST_EQ(m[0], float4(0.666666f, -0.333333f, 0.666666f, 0.f)); + EXPECT_ALMOST_EQ(m[1], float4(0.666666f, 0.666666f, -0.333333f, 0.f)); + EXPECT_ALMOST_EQ(m[2], float4(-0.333333f, 0.666666f, 0.666666f, 0.f)); + EXPECT_ALMOST_EQ(m[3], float4(0, 0, 0, 1)); + } +} + +CPU_TEST(Matrix_matrixFromRotationXYZ) +{ + // Rotation around X-axis by 90 degrees + { + float4x4 m = math::matrixFromRotationX(math::radians(90.f)); + EXPECT_ALMOST_EQ(m[0], float4(1, 0, 0, 0)); + EXPECT_ALMOST_EQ(m[1], float4(0, 0, -1, 0)); + EXPECT_ALMOST_EQ(m[2], float4(0, 1, 0, 0)); + EXPECT_ALMOST_EQ(m[3], float4(0, 0, 0, 1)); + } + + // Rotation around X-axis by 90 degrees + { + float4x4 m = math::matrixFromRotationXYZ(math::radians(90.f), 0.f, 0.f); + EXPECT_ALMOST_EQ(m[0], float4(1, 0, 0, 0)); + EXPECT_ALMOST_EQ(m[1], float4(0, 0, -1, 0)); + EXPECT_ALMOST_EQ(m[2], float4(0, 1, 0, 0)); + EXPECT_ALMOST_EQ(m[3], float4(0, 0, 0, 1)); + } + + // Rotation around X-axis by -45 degrees + { + float4x4 m = math::matrixFromRotationX(math::radians(-45.f)); + EXPECT_ALMOST_EQ(m[0], float4(1, 0, 0, 0)); + EXPECT_ALMOST_EQ(m[1], float4(0, 0.707106f, 0.707106f, 0)); + EXPECT_ALMOST_EQ(m[2], float4(0, -0.707106f, 0.707106f, 0)); + EXPECT_ALMOST_EQ(m[3], float4(0, 0, 0, 1)); + } + + // Rotation around X-axis by -45 degrees + { + float4x4 m = math::matrixFromRotationXYZ(math::radians(-45.f), 0.f, 0.f); + EXPECT_ALMOST_EQ(m[0], float4(1, 0, 0, 0)); + EXPECT_ALMOST_EQ(m[1], float4(0, 0.707106f, 0.707106f, 0)); + EXPECT_ALMOST_EQ(m[2], float4(0, -0.707106f, 0.707106f, 0)); + EXPECT_ALMOST_EQ(m[3], float4(0, 0, 0, 1)); + } + + // Rotation around Y-axis by 90 degrees + { + float4x4 m = math::matrixFromRotationY(math::radians(90.f)); + EXPECT_ALMOST_EQ(m[0], float4(0, 0, 1, 0)); + EXPECT_ALMOST_EQ(m[1], float4(0, 1, 0, 0)); + EXPECT_ALMOST_EQ(m[2], float4(-1, 0, 0, 0)); + EXPECT_ALMOST_EQ(m[3], float4(0, 0, 0, 1)); + } + + // Rotation around Y-axis by 90 degrees + { + float4x4 m = math::matrixFromRotationXYZ(0.f, math::radians(90.f), 0.f); + EXPECT_ALMOST_EQ(m[0], float4(0, 0, 1, 0)); + EXPECT_ALMOST_EQ(m[1], float4(0, 1, 0, 0)); + EXPECT_ALMOST_EQ(m[2], float4(-1, 0, 0, 0)); + EXPECT_ALMOST_EQ(m[3], float4(0, 0, 0, 1)); + } + + // Rotation around Y-axis by -45 degrees + { + float4x4 m = math::matrixFromRotationY(math::radians(-45.f)); + EXPECT_ALMOST_EQ(m[0], float4(0.707106f, 0, -0.707106f, 0)); + EXPECT_ALMOST_EQ(m[1], float4(0, 1, 0, 0)); + EXPECT_ALMOST_EQ(m[2], float4(0.707106f, 0, 0.707106f, 0)); + EXPECT_ALMOST_EQ(m[3], float4(0, 0, 0, 1)); + } + + // Rotation around Y-axis by -45 degrees + { + float4x4 m = math::matrixFromRotationXYZ(0.f, math::radians(-45.f), 0.f); + EXPECT_ALMOST_EQ(m[0], float4(0.707106f, 0, -0.707106f, 0)); + EXPECT_ALMOST_EQ(m[1], float4(0, 1, 0, 0)); + EXPECT_ALMOST_EQ(m[2], float4(0.707106f, 0, 0.707106f, 0)); + EXPECT_ALMOST_EQ(m[3], float4(0, 0, 0, 1)); + } + + // Rotation around Z-axis by 90 degrees + { + float4x4 m = math::matrixFromRotationZ(math::radians(90.f)); + EXPECT_ALMOST_EQ(m[0], float4(0, -1, 0, 0)); + EXPECT_ALMOST_EQ(m[1], float4(1, 0, 0, 0)); + EXPECT_ALMOST_EQ(m[2], float4(0, 0, 1, 0)); + EXPECT_ALMOST_EQ(m[3], float4(0, 0, 0, 1)); + } + + // Rotation around Z-axis by 90 degrees + { + float4x4 m = math::matrixFromRotationXYZ(0.f, 0.f, math::radians(90.f)); + EXPECT_ALMOST_EQ(m[0], float4(0, -1, 0, 0)); + EXPECT_ALMOST_EQ(m[1], float4(1, 0, 0, 0)); + EXPECT_ALMOST_EQ(m[2], float4(0, 0, 1, 0)); + EXPECT_ALMOST_EQ(m[3], float4(0, 0, 0, 1)); + } + + // Rotation around Z-axis by -45 degrees + { + float4x4 m = math::matrixFromRotationZ(math::radians(-45.f)); + EXPECT_ALMOST_EQ(m[0], float4(0.707106f, 0.707106f, 0, 0)); + EXPECT_ALMOST_EQ(m[1], float4(-0.707106f, 0.707106f, 0, 0)); + EXPECT_ALMOST_EQ(m[2], float4(0, 0, 1, 0)); + EXPECT_ALMOST_EQ(m[3], float4(0, 0, 0, 1)); + } + + // Rotation around Z-axis by -45 degrees + { + float4x4 m = math::matrixFromRotationXYZ(0.f, 0.f, math::radians(-45.f)); + EXPECT_ALMOST_EQ(m[0], float4(0.707106f, 0.707106f, 0, 0)); + EXPECT_ALMOST_EQ(m[1], float4(-0.707106f, 0.707106f, 0, 0)); + EXPECT_ALMOST_EQ(m[2], float4(0, 0, 1, 0)); + EXPECT_ALMOST_EQ(m[3], float4(0, 0, 0, 1)); + } + + // Rotation around XYZ by 45 degrees + { + float4x4 m = math::matrixFromRotationXYZ(math::radians(45.f), math::radians(45.f), math::radians(45.f)); + EXPECT_ALMOST_EQ(m[0], float4(0.5f, -0.5f, 0.707107f, 0.f)); + EXPECT_ALMOST_EQ(m[1], float4(0.853553f, 0.146446f, -0.5f, 0.f)); + EXPECT_ALMOST_EQ(m[2], float4(0.146446f, 0.853553f, 0.5f, 0.f)); + EXPECT_ALMOST_EQ(m[3], float4(0, 0, 0, 1)); + } + + // Rotation around XYZ by 20, 40, 60 degrees + { + float4x4 m = math::matrixFromRotationXYZ(math::radians(20.f), math::radians(40.f), math::radians(60.f)); + EXPECT_ALMOST_EQ(m[0], float4(0.383022f, -0.663414f, 0.642787f, 0)); + EXPECT_ALMOST_EQ(m[1], float4(0.923720f, 0.279453f, -0.262002f, 0)); + EXPECT_ALMOST_EQ(m[2], float4(-0.005813f, 0.694109f, 0.719846f, 0)); + EXPECT_ALMOST_EQ(m[3], float4(0, 0, 0, 1)); + } +} + +CPU_TEST(Matrix_matrixFromScaling) +{ + float4x4 m = math::matrixFromScaling(float3(2.f, 3.f, 4.f)); + EXPECT_ALMOST_EQ(m[0], float4(2, 0, 0, 0)); + EXPECT_ALMOST_EQ(m[1], float4(0, 3, 0, 0)); + EXPECT_ALMOST_EQ(m[2], float4(0, 0, 4, 0)); + EXPECT_ALMOST_EQ(m[3], float4(0, 0, 0, 1)); +} + +CPU_TEST(Matrix_matrixFromLookAt) +{ + // Right handed + { + float4x4 m = math::matrixFromLookAt(float3(10, 5, 0), float3(0, -5, 0), float3(0, 1, 0), math::Handedness::RightHanded); + EXPECT_ALMOST_EQ(m[0], float4(0, 0, -1, 0)); + EXPECT_ALMOST_EQ(m[1], float4(-0.707107f, 0.707107f, 0, 3.535535f)); + EXPECT_ALMOST_EQ(m[2], float4(0.707107f, 0.707107f, 0, -10.606603f)); + EXPECT_ALMOST_EQ(m[3], float4(0, 0, 0, 1)); + } + + // Left handed + { + float4x4 m = math::matrixFromLookAt(float3(10, 5, 0), float3(0, -5, 0), float3(0, 1, 0), math::Handedness::LeftHanded); + EXPECT_ALMOST_EQ(m[0], float4(0, 0, 1, 0)); + EXPECT_ALMOST_EQ(m[1], float4(-0.707107f, 0.707107f, 0, 3.535535f)); + EXPECT_ALMOST_EQ(m[2], float4(-0.707107f, -0.707107f, 0, 10.606603f)); + EXPECT_ALMOST_EQ(m[3], float4(0, 0, 0, 1)); + } +} + +CPU_TEST(Matrix_matrixFromQuat) +{ + // Identity quaternion + { + float4x4 m = math::matrixFromQuat(quatf::identity()); + EXPECT_ALMOST_EQ(m[0], float4(1, 0, 0, 0)); + EXPECT_ALMOST_EQ(m[1], float4(0, 1, 0, 0)); + EXPECT_ALMOST_EQ(m[2], float4(0, 0, 1, 0)); + EXPECT_ALMOST_EQ(m[3], float4(0, 0, 0, 1)); + } + + // Rotation around oblique axis + { + quatf q = math::quatFromAngleAxis(math::radians(60.f), normalize(float3(1, 1, 1))); + float4x4 m = math::matrixFromQuat(q); + EXPECT_ALMOST_EQ(m[0], float4(0.666666f, -0.333333f, 0.666666f, 0.f)); + EXPECT_ALMOST_EQ(m[1], float4(0.666666f, 0.666666f, -0.333333f, 0.f)); + EXPECT_ALMOST_EQ(m[2], float4(-0.333333f, 0.666666f, 0.666666f, 0.f)); + EXPECT_ALMOST_EQ(m[3], float4(0, 0, 0, 1)); + } +} + CPU_TEST(Matrix_FloatFormatter) { - rmcv::mat3x3 test0({1.1f, 1.2f, 1.3f, 2.1f, 2.2f, 2.3f, 3.1f, 3.2f, 3.3f}); + float3x3 test0({1.1f, 1.2f, 1.3f, 2.1f, 2.2f, 2.3f, 3.1f, 3.2f, 3.3f}); EXPECT_EQ(fmt::format("{}", test0), "{{1.1, 1.2, 1.3}, {2.1, 2.2, 2.3}, {3.1, 3.2, 3.3}}"); EXPECT_EQ( diff --git a/Source/Tools/FalcorTest/Tests/Utils/ParallelReductionTests.cpp b/Source/Tools/FalcorTest/Tests/Utils/ParallelReductionTests.cpp index 2c699a191..b6b11f250 100644 --- a/Source/Tools/FalcorTest/Tests/Utils/ParallelReductionTests.cpp +++ b/Source/Tools/FalcorTest/Tests/Utils/ParallelReductionTests.cpp @@ -27,7 +27,6 @@ **************************************************************************/ #include "Testing/UnitTest.h" #include "Utils/Algorithm/ParallelReduction.h" -#include #include namespace Falcor @@ -77,16 +76,6 @@ struct snorm_t 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) @@ -99,7 +88,7 @@ void store(void* ptr, size_t idx, float& val) template void testReduction(GPUUnitTestContext& ctx, ParallelReduction& reduction, ResourceFormat format, uint32_t width, uint32_t height) { - Device* pDevice = ctx.getDevice().get(); + ref pDevice = ctx.getDevice(); // Create random test data. const uint32_t channels = getFormatChannelCount(format); @@ -138,7 +127,7 @@ void testReduction(GPUUnitTestContext& ctx, ParallelReduction& reduction, Resour { value = u() * 200.f - 100.f; if (sz == 2) - store(ptr, i, value); + store(ptr, i, value); else if (sz == 4) store(ptr, i, value); else @@ -199,14 +188,13 @@ void testReduction(GPUUnitTestContext& ctx, ParallelReduction& reduction, Resour } // Create a texture with test data. - Texture::SharedPtr pTexture = Texture::create2D(pDevice, width, height, format, 1, 1, pInitData.get()); + ref pTexture = Texture::create2D(pDevice, width, height, format, 1, 1, pInitData.get()); // Test Sum operation. { // Allocate buffer for the result on the GPU. DataType nullValue = {}; - Buffer::SharedPtr pResultBuffer = - Buffer::create(pDevice, 16, ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, &nullValue); + ref pResultBuffer = Buffer::create(pDevice, 16, ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, &nullValue); // Perform reduction operation. DataType result; @@ -250,8 +238,7 @@ void testReduction(GPUUnitTestContext& ctx, ParallelReduction& reduction, Resour { // Allocate buffer for the result on the GPU. DataType nullValues[2] = {{}, {}}; - Buffer::SharedPtr pResultBuffer = - Buffer::create(pDevice, 32, ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, nullValues); + ref pResultBuffer = Buffer::create(pDevice, 32, ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, nullValues); // Perform reduction operation. DataType result[2]; diff --git a/Source/Tools/FalcorTest/Tests/Utils/PrefixSumTests.cpp b/Source/Tools/FalcorTest/Tests/Utils/PrefixSumTests.cpp index 584622f21..59933ff76 100644 --- a/Source/Tools/FalcorTest/Tests/Utils/PrefixSumTests.cpp +++ b/Source/Tools/FalcorTest/Tests/Utils/PrefixSumTests.cpp @@ -48,7 +48,7 @@ uint32_t prefixSumRef(std::vector& elems) void testPrefixSum(GPUUnitTestContext& ctx, PrefixSum& prefixSum, uint32_t numElems) { - Device* pDevice = ctx.getDevice().get(); + ref pDevice = ctx.getDevice(); // Create a buffer of random data to use as test data. // We make sure the total sum fits in 32 bits. @@ -59,13 +59,13 @@ void testPrefixSum(GPUUnitTestContext& ctx, PrefixSum& prefixSum, uint32_t numEl for (auto& it : testData) it = r() % maxVal; - Buffer::SharedPtr pTestDataBuffer = Buffer::create( + ref pTestDataBuffer = Buffer::create( pDevice, numElems * sizeof(uint32_t), Resource::BindFlags::UnorderedAccess, Buffer::CpuAccess::None, testData.data() ); // Allocate buffer for the total sum on the GPU. uint32_t nullValue = 0; - Buffer::SharedPtr pSumBuffer = Buffer::create(pDevice, 4, ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, &nullValue); + ref pSumBuffer = Buffer::create(pDevice, 4, ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, &nullValue); // Execute prefix sum on the GPU. uint32_t sum = 0; diff --git a/Source/Tools/FalcorTest/Tests/Utils/QuaternionTests.cpp b/Source/Tools/FalcorTest/Tests/Utils/QuaternionTests.cpp new file mode 100644 index 000000000..de788cf9b --- /dev/null +++ b/Source/Tools/FalcorTest/Tests/Utils/QuaternionTests.cpp @@ -0,0 +1,531 @@ +/*************************************************************************** + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. + # + # Redistribution and use in source and binary forms, with or without + # modification, are permitted provided that the following conditions + # are met: + # * Redistributions of source code must retain the above copyright + # notice, this list of conditions and the following disclaimer. + # * Redistributions in binary form must reproduce the above copyright + # notice, this list of conditions and the following disclaimer in the + # documentation and/or other materials provided with the distribution. + # * Neither the name of NVIDIA CORPORATION nor the names of its + # contributors may be used to endorse or promote products derived + # from this software without specific prior written permission. + # + # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY + # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + # (INCLUDING 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/Math/Quaternion.h" + +namespace Falcor +{ + +template +bool almostEqual(T a, T b, T epsilon = T(1e-5)) +{ + return std::abs(a - b) < epsilon; +} + +template +bool almostEqual(math::vector a, math::vector b, T epsilon = T(1e-5)) +{ + return all(abs(a - b) < math::vector(epsilon)); +} + +template +bool almostEqual(math::quat a, math::quat b, T epsilon = T(1e-5)) +{ + return all(math::vector(b.x - a.x, b.y - a.y, b.z - a.z, b.w - a.w) < math::vector(epsilon)); +} + +template +bool almostEqualOrientation(math::quat a, math::quat b, T epsilon = T(1e-5)) +{ + if (dot(a, b) < T(0)) + b = -b; + return almostEqual(a, b, epsilon); +} + +#define EXPECT_ALMOST_EQ(a, b) EXPECT_TRUE(almostEqual(a, b)) << fmt::format("{} != {}", a, b) +#define EXPECT_ALMOST_EQ_EPS(a, b, eps) EXPECT_TRUE(almostEqual(a, b, eps)) << fmt::format("{} != {}", a, b) +#define EXPECT_ALMOST_EQ_ORIENTATION(a, b) EXPECT_TRUE(almostEqualOrientation(a, b)) << fmt::format("{} != {}", a, b) + +CPU_TEST(Quaternion_Constructor) +{ + // Default constructor + { + quatf q; + EXPECT_EQ(q.x, 0.f); + EXPECT_EQ(q.y, 0.f); + EXPECT_EQ(q.z, 0.f); + EXPECT_EQ(q.w, 1.f); + } + + // Constructor with 4 floats + { + quatf q(1.f, 2.f, 3.f, 4.f); + EXPECT_EQ(q.x, 1.f); + EXPECT_EQ(q.y, 2.f); + EXPECT_EQ(q.z, 3.f); + EXPECT_EQ(q.w, 4.f); + } + + // Constructor with vector + scalar + { + quatf q(float3(1.f, 2.f, 3.f), 4.f); + EXPECT_EQ(q.x, 1.f); + EXPECT_EQ(q.y, 2.f); + EXPECT_EQ(q.z, 3.f); + EXPECT_EQ(q.w, 4.f); + } + + // Identity + { + quatf q = quatf::identity(); + EXPECT_EQ(q.x, 0.f); + EXPECT_EQ(q.y, 0.f); + EXPECT_EQ(q.z, 0.f); + EXPECT_EQ(q.w, 1.f); + } +} + +CPU_TEST(Quaternion_Access) +{ + { + quatf q(1.f, 2.f, 3.f, 4.f); + EXPECT_EQ(q[0], 1.f); + EXPECT_EQ(q[1], 2.f); + EXPECT_EQ(q[2], 3.f); + EXPECT_EQ(q[3], 4.f); + } +} + +CPU_TEST(Quaternion_Operator) +{ + // Unary + operator + { + quatf q1(1.f, 2.f, 3.f, 4.f); + quatf q2 = +q1; + EXPECT_EQ(q2, quatf(1.f, 2.f, 3.f, 4.f)); + } + + // Unary - operator + { + quatf q1(1.f, 2.f, 3.f, 4.f); + quatf q2 = -q1; + EXPECT_EQ(q2, quatf(-1.f, -2.f, -3.f, -4.f)); + } + + // Binary + operator + { + quatf q1(1.f, 2.f, 3.f, 4.f); + quatf q2(2.f, 3.f, 4.f, 5.f); + quatf q3 = q1 + q2; + EXPECT_EQ(q3, quatf(3.f, 5.f, 7.f, 9.f)); + quatf q4 = q1 + 2.f; + EXPECT_EQ(q4, quatf(3.f, 4.f, 5.f, 6.f)); + quatf q5 = 2.f + q1; + EXPECT_EQ(q5, quatf(3.f, 4.f, 5.f, 6.f)); + } + + // Binary - operator + { + quatf q1(1.f, 2.f, 3.f, 4.f); + quatf q2(2.f, 3.f, 4.f, 5.f); + quatf q3 = q1 - q2; + EXPECT_EQ(q3, quatf(-1.f, -1.f, -1.f, -1.f)); + quatf q4 = q1 - 2.f; + EXPECT_EQ(q4, quatf(-1.f, 0.f, 1.f, 2.f)); + quatf q5 = 2.f - q1; + EXPECT_EQ(q5, quatf(1.f, 0.f, -1.f, -2.f)); + } + + // Binary * operator + { + quatf q1(1.f, 2.f, 3.f, 4.f); + quatf q2 = q1 * 2.f; + EXPECT_EQ(q2, quatf(2.f, 4.f, 6.f, 8.f)); + quatf q3 = 3.f * q1; + EXPECT_EQ(q3, quatf(3.f, 6.f, 9.f, 12.f)); + } + + // Binary / operator + { + quatf q1(1.f, 2.f, 3.f, 4.f); + quatf q2 = q1 / 2.f; + EXPECT_EQ(q2, quatf(0.5f, 1.f, 1.5f, 2.f)); + } + + // Binary == operator + { + quatf q1(1.f, 2.f, 3.f, 4.f); + quatf q2(1.f, 2.f, 3.f, 4.f); + EXPECT_EQ(q1 == q2, bool4(true, true, true, true)); + quatf q3(1.f, 2.f, 3.f, 5.f); + EXPECT_EQ(q1 == q3, bool4(true, true, true, false)); + } + + // Binary != operator + { + quatf q1(1.f, 2.f, 3.f, 4.f); + quatf q2(1.f, 2.f, 3.f, 4.f); + EXPECT_EQ(q1 != q2, bool4(false, false, false, false)); + quatf q3(1.f, 2.f, 3.f, 5.f); + EXPECT_EQ(q1 != q3, bool4(false, false, false, true)); + } +} + +CPU_TEST(Quaternion_Multiply) +{ + // Quaternion / quaternion multiplication + { + quatf q1(1.f, 2.f, 3.f, 4.f); + quatf q2(2.f, 3.f, 4.f, 5.f); + quatf q3 = mul(q1, q2); + EXPECT_EQ(q3, quatf(12.f, 24.f, 30.f, 0.f)); + } + + // Quaternion / vector multiplication + { + quatf q1(2.f, 3.f, 4.f, 5.f); + float3 v1(2.f, 3.f, 4.f); + float3 v2 = mul(q1, v1); + EXPECT_EQ(v2, float3(2.f, 3.f, 4.f)); + } +} + +CPU_TEST(Quaternion_FloatChecks) +{ + // isfinite + { + quatf q1(0.f, 0.f, 0.f, 0.f); + EXPECT_EQ(isfinite(q1), bool4(true, true, true, true)); + quatf q2(std::numeric_limits::infinity(), 0.f, 0.f, std::numeric_limits::infinity()); + EXPECT_EQ(isfinite(q2), bool4(false, true, true, false)); + } + // isinf + { + quatf q1(0.f, 0.f, 0.f, 0.f); + EXPECT_EQ(isinf(q1), bool4(false, false, false, false)); + quatf q2(std::numeric_limits::infinity(), 0.f, 0.f, std::numeric_limits::infinity()); + EXPECT_EQ(isinf(q2), bool4(true, false, false, true)); + } + // isnan + { + quatf q1(0.f, 0.f, 0.f, 0.f); + EXPECT_EQ(isnan(q1), bool4(false, false, false, false)); + quatf q2(std::numeric_limits::quiet_NaN(), 0.f, 0.f, std::numeric_limits::quiet_NaN()); + EXPECT_EQ(isnan(q2), bool4(true, false, false, true)); + } +} + +CPU_TEST(Quaternion_Functions) +{ + // dot product + { + quatf q1(1.f, 2.f, 3.f, 4.f); + quatf q2(2.f, 3.f, 4.f, 5.f); + float d = dot(q1, q2); + EXPECT_EQ(d, 40.f); + } + + // cross product + { + quatf q1(1.f, 2.f, 3.f, 4.f); + quatf q2(2.f, 3.f, 4.f, 5.f); + quatf q3 = cross(q1, q2); + EXPECT_EQ(q3, quatf(12.f, 24.f, 30.f, 0.f)); + } + + // length + { + quatf q1(1.f, 2.f, 3.f, 4.f); + float l = length(q1); + EXPECT_EQ(l, sqrtf(30.f)); + } + + // normalize + { + quatf q1(1.f, 2.f, 3.f, 4.f); + quatf q2 = normalize(q1); + EXPECT_EQ(q2, q1 * (1.f / sqrtf(30.f))); + } + + // conjugate + { + quatf q1(1.f, 2.f, 3.f, 4.f); + quatf q2 = conjugate(q1); + EXPECT_EQ(q2, quatf(-1.f, -2.f, -3.f, 4.f)); + } + + // inverse + { + quatf q1(1.f, 2.f, 3.f, 4.f); + quatf q2 = inverse(q1); + EXPECT_EQ(q2, quatf(-1.f / 30.f, -2.f / 30.f, -3.f / 30.f, 4.f / 30.f)); + } + + // lerp + { + quatf q1(1.f, 2.f, 3.f, 4.f); + quatf q2(2.f, 3.f, 4.f, 5.f); + quatf q3 = lerp(q1, q2, 0.5f); + EXPECT_EQ(q3, quatf(1.5f, 2.5f, 3.5f, 4.5f)); + } +} + +CPU_TEST(Quaternion_slerp) +{ + auto test = [&](float angle, float t, float expectedAngle) + { + quatf from = quatf::identity(); + quatf to = math::quatFromAngleAxis(math::radians(angle), float3(0, 1, 0)); + quatf expected = math::quatFromAngleAxis(math::radians(expectedAngle), float3(0, 1, 0)); + + quatf q1 = slerp(from, to, t); + EXPECT_ALMOST_EQ_ORIENTATION(q1, expected); + + quatf q2 = slerp(to, from, 1.f - t); + EXPECT_ALMOST_EQ_ORIENTATION(q2, expected); + }; + + // Basic + test(+160, 0.375f, +60); + test(-160, 0.375f, -60); + + // Shorting + test(+320, 0.375f, -15); // Mathematically, should be +120 + test(-320, 0.375f, +15); // Mathematically, should be -120 + + // Lengthening short way + test(320, 1.5f, -60); // Mathematically, should be 480 (ie -240) + + // Lengthening + test(+70, 3, +210); + test(-70, 3, -210); + + // Edge case that often causes NaNs + test(0, .5f, 0); + + // This edge case is ill-defined for "intuitive" slerp and can't be tested. + // test(180, .25f, 45); + + // Conversely, this edge case is well-defined for "intuitive" slerp. + // For mathematical slerp, the axis is ill-defined and can take many values. + test(360, .25f, 0); +} + +CPU_TEST(Quaternion_Euler) +{ + // pitch 90 degrees + { + // quatf q = math::quatFromAngleAxis(math::radians(90.f), float3(1.f, 0.f, 0.f)); + quatf q = quatf(std::sqrt(0.5f), 0.f, 0.f, std::sqrt(0.5f)); + EXPECT_ALMOST_EQ(pitch(q), math::radians(90.f)); + } + + // pitch -60 degrees + { + // quatf q = math::quatFromAngleAxis(math::radians(-60.f), float3(1.f, 0.f, 0.f)); + quatf q = quatf(-0.5f, 0.f, 0.f, std::sqrt(0.75f)); + EXPECT_ALMOST_EQ(pitch(q), math::radians(-60.f)); + } + + // yaw 90 degrees + { + // quatf q = math::quatFromAngleAxis(math::radians(90.f), float3(0.f, 1.f, 0.f)); + quatf q = quatf(0.f, std::sqrt(0.5f), 0.f, std::sqrt(0.5f)); + EXPECT_ALMOST_EQ_EPS(yaw(q), math::radians(90.f), 1e-3f); + } + + // yaw -60 degrees + { + // quatf q = math::quatFromAngleAxis(math::radians(-60.f), float3(0.f, 1.f, 0.f)); + quatf q = quatf(0.f, -0.5f, 0.f, std::sqrt(0.75f)); + EXPECT_ALMOST_EQ(yaw(q), math::radians(-60.f)); + } + + // roll 90 degrees + { + // quatf q = math::quatFromAngleAxis(math::radians(90.f), float3(0.f, 0.f, 1.f)); + quatf q = quatf(0.f, 0.f, std::sqrt(0.5f), std::sqrt(0.5f)); + EXPECT_ALMOST_EQ(roll(q), math::radians(90.f)); + } + + // roll -60 degrees + { + // quatf q = math::quatFromAngleAxis(math::radians(-60.f), float3(0.f, 0.f, 1.f)); + quatf q = quatf(0.f, 0.f, -0.5f, std::sqrt(0.75f)); + EXPECT_ALMOST_EQ(roll(q), math::radians(-60.f)); + } + + // eulerAngles + { + // quatf q1 = math::quatFromAngleAxis(math::radians(-60.f), float3(1.f, 0.f, 0.f)); + quatf q1 = quatf(-0.5f, 0.f, 0.f, std::sqrt(0.75f)); + EXPECT_ALMOST_EQ(eulerAngles(q1), float3(math::radians(-60.f), 0.f, 0.f)); + + // quatf q2 = math::quatFromAngleAxis(math::radians(-60.f), float3(0.f, 1.f, 0.f)); + quatf q2 = quatf(0.f, -0.5f, 0.f, std::sqrt(0.75f)); + EXPECT_ALMOST_EQ(eulerAngles(q2), float3(0.f, math::radians(-60.f), 0.f)); + + // quatf q3 = math::quatFromAngleAxis(math::radians(-60.f), float3(0.f, 0.f, 1.f)); + quatf q3 = quatf(0.f, 0.f, -0.5f, std::sqrt(0.75f)); + EXPECT_ALMOST_EQ(eulerAngles(q3), float3(0.f, 0.f, math::radians(-60.f))); + + // quatf q4 = mul(mul(q3, q2), q1); + quatf q4 = quatf(-0.591506362f, -0.158493653f, -0.591506362f, 0.524519026f); + EXPECT_ALMOST_EQ(eulerAngles(q4), float3(math::radians(-60.f), math::radians(-60.f), math::radians(-60.f))); + } +} + +CPU_TEST(Quaternion_quatFromAngleAxis) +{ + // 90 degrees X-axis + { + quatf q1 = math::quatFromAngleAxis(math::radians(90.f), float3(1.f, 0.f, 0.f)); + quatf q2 = quatf(std::sqrt(0.5f), 0.f, 0.f, std::sqrt(0.5f)); + EXPECT_ALMOST_EQ(q1, q2); + } + + // -60 degrees X-axis + { + quatf q1 = math::quatFromAngleAxis(math::radians(-60.f), float3(1.f, 0.f, 0.f)); + quatf q2 = quatf(-0.5f, 0.f, 0.f, std::sqrt(0.75f)); + EXPECT_ALMOST_EQ(q1, q2); + } + + // 90 degrees Y-axis + { + quatf q1 = math::quatFromAngleAxis(math::radians(90.f), float3(0.f, 1.f, 0.f)); + quatf q2 = quatf(0.f, std::sqrt(0.5f), 0.f, std::sqrt(0.5f)); + EXPECT_ALMOST_EQ(q1, q2); + } + + // -60 degrees Y-axis + { + quatf q1 = math::quatFromAngleAxis(math::radians(-60.f), float3(0.f, 1.f, 0.f)); + quatf q2 = quatf(0.f, -0.5f, 0.f, std::sqrt(0.75f)); + EXPECT_ALMOST_EQ(q1, q2); + } + + // 90 degrees Z-axis + { + quatf q1 = math::quatFromAngleAxis(math::radians(90.f), float3(0.f, 0.f, 1.f)); + quatf q2 = quatf(0.f, 0.f, std::sqrt(0.5f), std::sqrt(0.5f)); + EXPECT_ALMOST_EQ(q1, q2); + } + + // -60 degrees Z-axis + { + quatf q1 = math::quatFromAngleAxis(math::radians(-60.f), float3(0.f, 0.f, 1.f)); + quatf q2 = quatf(0.f, 0.f, -0.5f, std::sqrt(0.75f)); + EXPECT_ALMOST_EQ(q1, q2); + } +} + +CPU_TEST(Quaternion_quatFromRotationBetweenVectors) +{ + auto test = [&](float3 v1, float3 axis, float angle) + { + quatf q1 = math::quatFromAngleAxis(math::radians(angle), axis); + float3 v2 = transformVector(q1, v1); + quatf q2 = math::quatFromRotationBetweenVectors(v1, v2); + float3 v3 = transformVector(q2, v1); + EXPECT_ALMOST_EQ(v2, v3); + }; + + test(float3(0.f, 1.f, 0.f), float3(1.f, 0.f, 0.f), 90); + test(float3(0.f, 0.f, 1.f), float3(1.f, 0.f, 0.f), -135); + + test(float3(1.f, 0.f, 0.f), float3(0.f, 1.f, 0.f), 90); + test(float3(0.f, 0.f, 1.f), float3(0.f, 1.f, 0.f), -135); + + test(float3(1.f, 0.f, 0.f), float3(0.f, 0.f, 1.f), 90); + test(float3(0.f, 1.f, 0.f), float3(0.f, 0.f, 1.f), -135); + + test(float3(1.f, 0.f, 0.f), normalize(float3(1.f, 1.f, 1.f)), 45); + test(float3(0.f, 1.f, 0.f), normalize(float3(1.f, 1.f, 1.f)), 90); + test(float3(0.f, 0.f, 1.f), normalize(float3(1.f, 1.f, 1.f)), 135); +} + +CPU_TEST(Quaternion_quatFromEulerAngles) +{ + // 90 degrees X-axis + { + quatf q1 = math::quatFromEulerAngles(float3(math::radians(90.f), 0.f, 0.f)); + quatf q2 = quatf(std::sqrt(0.5f), 0.f, 0.f, std::sqrt(0.5f)); + EXPECT_ALMOST_EQ(q1, q2); + } + + // -60 degrees X-axis + { + quatf q1 = math::quatFromEulerAngles(float3(math::radians(-60.f), 0.f, 0.f)); + quatf q2 = quatf(-0.5f, 0.f, 0.f, std::sqrt(0.75f)); + EXPECT_ALMOST_EQ(q1, q2); + } + + // 90 degrees Y-axis + { + quatf q1 = math::quatFromEulerAngles(float3(0.f, math::radians(90.f), 0.f)); + quatf q2 = quatf(0.f, std::sqrt(0.5f), 0.f, std::sqrt(0.5f)); + EXPECT_ALMOST_EQ(q1, q2); + } + + // -60 degrees Y-axis + { + quatf q1 = math::quatFromEulerAngles(float3(0.f, math::radians(-60.f), 0.f)); + quatf q2 = quatf(0.f, -0.5f, 0.f, std::sqrt(0.75f)); + EXPECT_ALMOST_EQ(q1, q2); + } + + // 90 degrees Z-axis + { + quatf q1 = math::quatFromEulerAngles(float3(0.f, 0.f, math::radians(90.f))); + quatf q2 = quatf(0.f, 0.f, std::sqrt(0.5f), std::sqrt(0.5f)); + EXPECT_ALMOST_EQ(q1, q2); + } + + // -60 degrees Z-axis + { + quatf q1 = math::quatFromEulerAngles(float3(0.f, 0.f, math::radians(-60.f))); + quatf q2 = quatf(0.f, 0.f, -0.5f, std::sqrt(0.75f)); + EXPECT_ALMOST_EQ(q1, q2); + } +} + +CPU_TEST(Quaternion_quatFromMatrix) +{ + auto test = [&](int x, int y, int z) + { + float3 eulerAngles = radians(float3(x, y, z)); + quatf q1 = math::quatFromEulerAngles(eulerAngles); + float3x3 m = math::matrixFromQuat(q1); + quatf q2 = math::quatFromMatrix(m); + EXPECT_ALMOST_EQ(q1, q2); + EXPECT_ALMOST_EQ_EPS(eulerAngles, math::eulerAngles(q2), 1e-3f); + }; + + test(0, 0, 0); + test(90, 0, 0); + test(0, 90, 0); + test(0, 0, 90); + test(-45, 0, 0); + test(0, -45, 0); + test(0, 0, -45); + test(10, 20, 30); + test(-30, -20, -10); +} + +} // namespace Falcor diff --git a/Source/Tools/FalcorTest/Tests/Utils/SettingsTests.cpp b/Source/Tools/FalcorTest/Tests/Utils/SettingsTests.cpp index c3d23709e..03229fde7 100644 --- a/Source/Tools/FalcorTest/Tests/Utils/SettingsTests.cpp +++ b/Source/Tools/FalcorTest/Tests/Utils/SettingsTests.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -177,7 +177,7 @@ CPU_TEST(Settings_OptionsTypes) { options.get("string", int(3)); } - catch (Falcor::SettingsProperties::TypeError err) + catch (const Falcor::SettingsProperties::TypeError&) { wrongTypeString = true; } @@ -188,7 +188,7 @@ CPU_TEST(Settings_OptionsTypes) { options.get("int", std::string("test")); } - catch (Falcor::SettingsProperties::TypeError err) + catch (const Falcor::SettingsProperties::TypeError&) { wrongTypeInt = true; } @@ -199,7 +199,7 @@ CPU_TEST(Settings_OptionsTypes) { options.get("int[2]", float(0.f)); } - catch (Falcor::SettingsProperties::TypeError err) + catch (const Falcor::SettingsProperties::TypeError&) { wrongTypeArray = true; } @@ -208,12 +208,15 @@ CPU_TEST(Settings_OptionsTypes) CPU_TEST(Settings_OptionsOverride) { - pybind11::dict pyDict; - pyDict["mogwai"] = pybind11::dict(); - pyDict["mogwai"]["value"] = 17; - Settings settings; - settings.addOptions(pyDict); + + { + pybind11::dict pyDict; + pyDict["mogwai"] = pybind11::dict(); + pyDict["mogwai"]["value"] = 17; + settings.addOptions(pyDict); + } + SettingsProperties options = settings.getOptions(); EXPECT_EQ(options.get("mogwai:value", 0), 17); diff --git a/Source/Tools/FalcorTest/Tests/Utils/StringUtilsTests.cpp b/Source/Tools/FalcorTest/Tests/Utils/StringUtilsTests.cpp index 6db0ba8ab..79f66883e 100644 --- a/Source/Tools/FalcorTest/Tests/Utils/StringUtilsTests.cpp +++ b/Source/Tools/FalcorTest/Tests/Utils/StringUtilsTests.cpp @@ -101,4 +101,12 @@ CPU_TEST(FormatByteSize) EXPECT_EQ(formatByteSize(10 * TB), "10.00 TB"); } +CPU_TEST(DecodeURI) +{ + EXPECT_EQ(decodeURI("test"), "test"); + EXPECT_EQ(decodeURI("hello%20world"), "hello world"); + EXPECT_EQ(decodeURI("hello%20world%21"), "hello world!"); + EXPECT_EQ(decodeURI("%22hello+world%22"), "\"hello world\""); +} + } // namespace Falcor diff --git a/Source/Tools/FalcorTest/Tests/Utils/TextureAnalyzerTests.cpp b/Source/Tools/FalcorTest/Tests/Utils/TextureAnalyzerTests.cpp index fff6df278..e3b9b0e3c 100644 --- a/Source/Tools/FalcorTest/Tests/Utils/TextureAnalyzerTests.cpp +++ b/Source/Tools/FalcorTest/Tests/Utils/TextureAnalyzerTests.cpp @@ -122,13 +122,12 @@ const TextureAnalyzer::Result kExpectedResult[] = { GPU_TEST(TextureAnalyzer) { - Device* pDevice = ctx.getDevice().get(); + ref pDevice = ctx.getDevice(); - TextureAnalyzer::SharedPtr pTextureAnalyzer = TextureAnalyzer::create(ctx.getDevice()); - EXPECT(pTextureAnalyzer != nullptr); + TextureAnalyzer textureAnalyzer(pDevice); // Load test textures. - std::vector textures(kNumTests); + std::vector> textures(kNumTests); for (size_t i = 0; i < kNumTests; i++) { std::string fn = "tests/texture" + std::to_string(i + 1) + (i < kNumPNGs ? ".png" : ".exr"); @@ -144,10 +143,10 @@ GPU_TEST(TextureAnalyzer) for (size_t i = 0; i < kNumTests; i++) { - pTextureAnalyzer->analyze(ctx.getRenderContext(), textures[i], 0, 0, pResult, i * kResultSize); + textureAnalyzer.analyze(ctx.getRenderContext(), textures[i], 0, 0, pResult, i * kResultSize); } - auto verify = [&ctx](Buffer::SharedPtr pResult) + auto verify = [&ctx](ref pResult) { // Verify results. const TextureAnalyzer::Result* result = static_cast(pResult->map(Buffer::MapType::Read)); @@ -187,7 +186,7 @@ GPU_TEST(TextureAnalyzer) // Test the array version of the interface. ctx.getRenderContext()->clearUAV(pResult->getUAV().get(), uint4(0xbabababa)); - pTextureAnalyzer->analyze(ctx.getRenderContext(), textures, pResult); + textureAnalyzer.analyze(ctx.getRenderContext(), textures, pResult); verify(pResult); } diff --git a/Source/Tools/FalcorTest/Tests/Utils/VectorTests.cpp b/Source/Tools/FalcorTest/Tests/Utils/VectorTests.cpp index 85201d387..d3f958473 100644 --- a/Source/Tools/FalcorTest/Tests/Utils/VectorTests.cpp +++ b/Source/Tools/FalcorTest/Tests/Utils/VectorTests.cpp @@ -66,12 +66,12 @@ CPU_TEST(Vector_Comparison) std::random_device rd; std::mt19937 g(rd()); std::shuffle(vec.begin(), vec.end(), g); - std::sort(vec.begin(), vec.end()); + std::sort(vec.begin(), vec.end(), std::less{}); for (size_t i = 0; i < vec.size(); ++i) { for (size_t j = i + 1; j < vec.size(); ++j) { - EXPECT_LT(vec[i], vec[j]); + EXPECT(std::less{}(vec[i], vec[j])); } } } diff --git a/Source/Tools/ImageCompare/ImageCompare.cpp b/Source/Tools/ImageCompare/ImageCompare.cpp index 288c46ce5..b6b85fdce 100644 --- a/Source/Tools/ImageCompare/ImageCompare.cpp +++ b/Source/Tools/ImageCompare/ImageCompare.cpp @@ -61,16 +61,16 @@ T clamp(T x, T lo, T hi) class Image { public: - using SharedPtr = std::shared_ptr; + Image(uint32_t width, uint32_t height) : mWidth(width), mHeight(height), mData(std::make_unique(width * height * 4)) {} uint32_t getWidth() const { return mWidth; } uint32_t getHeight() const { return mHeight; } const float* getData() const { return mData.get(); } float* getData() { return mData.get(); } - static SharedPtr create(uint32_t width, uint32_t height) { return SharedPtr(new Image(width, height)); } + static std::shared_ptr create(uint32_t width, uint32_t height) { return std::make_shared(width, height); } - static SharedPtr loadFromFile(const std::filesystem::path& path) + static std::shared_ptr loadFromFile(const std::filesystem::path& path) { FREE_IMAGE_FORMAT fifFormat = FIF_UNKNOWN; @@ -180,8 +180,6 @@ class Image uint32_t mWidth; uint32_t mHeight; std::unique_ptr mData; - - Image(uint32_t width, uint32_t height) : mWidth(width), mHeight(height), mData(std::make_unique(width * height * 4)) {} }; struct MSE @@ -262,7 +260,7 @@ static const std::vector errorMetrics = { {"mape", "Mean Absolute Percentage Error", compare}, }; -static Image::SharedPtr generateHeatMap(uint32_t width, uint32_t height, const float* errorMap) +static std::shared_ptr generateHeatMap(uint32_t width, uint32_t height, const float* errorMap) { auto writeColor = [](float t, float* dst) { @@ -312,7 +310,7 @@ static bool compareImages( catch (const std::runtime_error& e) { std::cerr << "Cannot load image from '" << path.string() << "' (Error: " << e.what() << ")." << std::endl; - return Image::SharedPtr(); + return std::shared_ptr{}; } }; diff --git a/Source/Tools/RenderGraphEditor/RenderGraphEditor.cpp b/Source/Tools/RenderGraphEditor/RenderGraphEditor.cpp index acdbce671..4e06675f7 100644 --- a/Source/Tools/RenderGraphEditor/RenderGraphEditor.cpp +++ b/Source/Tools/RenderGraphEditor/RenderGraphEditor.cpp @@ -28,6 +28,7 @@ #include "RenderGraphEditor.h" #include "RenderGraph/RenderGraph.h" #include "RenderGraph/RenderGraphImportExport.h" +#include "GlobalState.h" #include #include @@ -36,6 +37,13 @@ #include #include +#if FALCOR_WINDOWS +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#include +#endif + FALCOR_EXPORT_D3D12_AGILITY_SDK namespace @@ -48,6 +56,8 @@ const std::string kDefaultPassIcon = "framework/images/pass-icon.png"; RenderGraphEditor::RenderGraphEditor(const SampleAppConfig& config, const Options& options) : SampleApp(config), mOptions(options), mCurrentGraphIndex(0) { + setActivePythonRenderGraphDevice(getDevice()); + mNextGraphString.resize(255, 0); mCurrentGraphOutput = ""; mGraphOutputEditString = mCurrentGraphOutput; @@ -61,11 +71,13 @@ RenderGraphEditor::~RenderGraphEditor() terminateProcess(mViewerProcess); mViewerProcess = 0; } + + setActivePythonRenderGraphDevice(nullptr); } void RenderGraphEditor::onLoad(RenderContext* pRenderContext) { - mpDefaultIconTex = Texture::createFromFile(getDevice().get(), kDefaultPassIcon, false, false); + mpDefaultIconTex = Texture::createFromFile(getDevice(), kDefaultPassIcon, false, false); if (!mpDefaultIconTex) throw RuntimeError("Failed to load icon"); @@ -225,7 +237,7 @@ void RenderGraphEditor::onGuiRender(Gui* pGui) { const auto& [type, info] = renderPasses[i]; passWindow.rect({148.0f, 64.0f}, pGui->pickUniqueColor(type), false); - passWindow.image(("RenderPass##" + std::to_string(i)).c_str(), mpDefaultIconTex, {148.0f, 44.0f}); + passWindow.image(("RenderPass##" + std::to_string(i)).c_str(), mpDefaultIconTex.get(), {148.0f, 44.0f}); passWindow.dragDropSource(type.c_str(), "RenderPassType", type); passWindow.text(type); passWindow.tooltip(info.desc, true); @@ -424,7 +436,7 @@ void RenderGraphEditor::loadGraphsFromFile(const std::filesystem::path& path, co FALCOR_ASSERT(!path.empty()); // 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, path); @@ -470,7 +482,7 @@ void RenderGraphEditor::createNewGraph(const std::string& renderGraphName) { std::string graphName = renderGraphName; auto nameToIndexIt = mGraphNamesToIndex.find(graphName); - RenderGraph::SharedPtr newGraph = RenderGraph::create(getDevice()); + ref newGraph = RenderGraph::create(getDevice()); std::string tempGraphName = graphName; while (mGraphNamesToIndex.find(tempGraphName) != mGraphNamesToIndex.end()) @@ -491,7 +503,7 @@ void RenderGraphEditor::createNewGraph(const std::string& renderGraphName) mOpenGraphNames.push_back(nextGraphID); } -void RenderGraphEditor::onFrameRender(RenderContext* pRenderContext, const Fbo::SharedPtr& pTargetFbo) +void RenderGraphEditor::onFrameRender(RenderContext* pRenderContext, const ref& pTargetFbo) { const float4 clearColor(0.25, 0.25, 0.25, 1); pRenderContext->clearFbo(pTargetFbo.get(), clearColor, 1.0f, 0, FboAttachmentType::All); diff --git a/Source/Tools/RenderGraphEditor/RenderGraphEditor.h b/Source/Tools/RenderGraphEditor/RenderGraphEditor.h index 3a65480a8..957f889fb 100644 --- a/Source/Tools/RenderGraphEditor/RenderGraphEditor.h +++ b/Source/Tools/RenderGraphEditor/RenderGraphEditor.h @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -47,7 +47,7 @@ class RenderGraphEditor : public SampleApp ~RenderGraphEditor(); void onLoad(RenderContext* pRenderContext) override; - void onFrameRender(RenderContext* pRenderContext, const Fbo::SharedPtr& pTargetFbo) override; + void onFrameRender(RenderContext* pRenderContext, const ref& pTargetFbo) override; void onResize(uint32_t width, uint32_t height) override; void onGuiRender(Gui* pGui) override; void onDroppedFile(const std::filesystem::path& path) override; @@ -61,7 +61,7 @@ class RenderGraphEditor : public SampleApp Options mOptions; - std::vector mpGraphs; + std::vector> mpGraphs; std::vector mRenderGraphUIs; std::unordered_map mGraphNamesToIndex; size_t mCurrentGraphIndex; @@ -71,7 +71,7 @@ class RenderGraphEditor : public SampleApp std::string mCurrentGraphOutput; std::string mGraphOutputEditString; std::filesystem::path mUpdateFilePath; - Texture::SharedPtr mpDefaultIconTex; + ref mpDefaultIconTex; Gui::DropdownList mOpenGraphNames; bool mShowCreateGraphWindow = false; diff --git a/Source/plugins/importers/AssimpImporter/AssimpImporter.cpp b/Source/plugins/importers/AssimpImporter/AssimpImporter.cpp index 6d46e28c3..5b2ea2c84 100644 --- a/Source/plugins/importers/AssimpImporter/AssimpImporter.cpp +++ b/Source/plugins/importers/AssimpImporter/AssimpImporter.cpp @@ -42,7 +42,9 @@ #include #include #include -#include +#include + +#include #include #include @@ -54,8 +56,8 @@ namespace { // Global camera animation interpolation and warping configuration. // Assimp does not provide enough information to determine this from data. -static const Animation::InterpolationMode kCameraInterpolationMode = Animation::InterpolationMode::Linear; -static const bool kCameraEnableWarping = true; +const Animation::InterpolationMode kCameraInterpolationMode = Animation::InterpolationMode::Linear; +const bool kCameraEnableWarping = true; using BoneMeshMap = std::map>; using MeshInstanceList = std::vector>; @@ -67,7 +69,7 @@ using MeshInstanceList = std::vector>; */ float convertSpecPowerToRoughness(float specPower) { - return clamp(sqrt(2.0f / (specPower + 2.0f)), 0.f, 1.f); + return std::clamp(std::sqrt(2.0f / (specPower + 2.0f)), 0.f, 1.f); } enum class ImportMode @@ -77,10 +79,10 @@ enum class ImportMode GLTF2, }; -rmcv::mat4 aiCast(const aiMatrix4x4& aiMat) +float4x4 aiCast(const aiMatrix4x4& aiMat) { - rmcv::mat4 m{aiMat.a1, aiMat.a2, aiMat.a3, aiMat.a4, aiMat.b1, aiMat.b2, aiMat.b3, aiMat.b4, - aiMat.c1, aiMat.c2, aiMat.c3, aiMat.c4, aiMat.d1, aiMat.d2, aiMat.d3, aiMat.d4}; + float4x4 m{aiMat.a1, aiMat.a2, aiMat.a3, aiMat.a4, aiMat.b1, aiMat.b2, aiMat.b3, aiMat.b4, + aiMat.c1, aiMat.c2, aiMat.c3, aiMat.c4, aiMat.d1, aiMat.d2, aiMat.d3, aiMat.d4}; return m; } @@ -95,9 +97,9 @@ float3 aiCast(const aiVector3D& val) return float3(val.x, val.y, val.z); } -glm::quat aiCast(const aiQuaternion& q) +quatf aiCast(const aiQuaternion& q) { - return glm::quat(q.w, q.x, q.y, q.z); + return quatf(q.x, q.y, q.z, q.w); } /** @@ -149,9 +151,9 @@ class ImporterData std::filesystem::path path; const aiScene* pScene; SceneBuilder& builder; - std::map materialMap; + std::map> materialMap; std::map meshMap; // Assimp mesh index to Falcor mesh ID - std::map localToBindPoseMatrices; + std::map localToBindPoseMatrices; NodeID getFalcorNodeID(const aiNode* pNode) const { return mAiToFalcorNodeID.at(pNode); } @@ -240,11 +242,11 @@ void createAnimation(ImporterData& data, const aiAnimation* pAiAnim, ImportMode aiNodeAnim* pAiNode = pAiAnim->mChannels[i]; resetNegativeKeyframeTimes(pAiNode); - std::vector animations; - for (uint32_t i = 0; i < data.getNodeInstanceCount(pAiNode->mNodeName.C_Str()); i++) + std::vector> animations; + for (uint32_t j = 0; j < data.getNodeInstanceCount(pAiNode->mNodeName.C_Str()); j++) { - Animation::SharedPtr pAnimation = Animation::create( - std::string(pAiNode->mNodeName.C_Str()) + "." + std::to_string(i), data.getFalcorNodeID(pAiNode->mNodeName.C_Str(), i), + ref pAnimation = Animation::create( + std::string(pAiNode->mNodeName.C_Str()) + "." + std::to_string(j), data.getFalcorNodeID(pAiNode->mNodeName.C_Str(), j), durationInSeconds ); animations.push_back(pAnimation); @@ -285,12 +287,43 @@ void createAnimation(ImporterData& data, const aiAnimation* pAiAnim, ImportMode } } +/** + * The current version of AssImp (5.2.5) has a bug where it creates an invalid + * scene graph for animated cameras. The scene graph might look like this: + * + * - Root + * - Camera_$AssimpFbx$_Translation + * - Camera_$AssimpFbx$_Rotation + * - Camera_$AssimpFbx$_PostRotation + * - Camera_$AssimpFbx$_Scaling + * - Camera + * + * The animation is attached to the "Camera" leaf-node, but the extra scene + * nodes above are not set to identity. This leads to incorrect camera + * animation. To fix this, we simply set all the inner nodes to identity. + */ +void fixFbxCameraAnimation(ImporterData& data, NodeID cameraNodeID) +{ + SceneBuilder::Node& cameraNode = data.builder.getNode(cameraNodeID); + + NodeID nodeID = cameraNode.parent; + while (nodeID.isValid()) + { + SceneBuilder::Node& node = data.builder.getNode(nodeID); + if (hasPrefix(node.name, cameraNode.name + "_$AssimpFbx$_")) + node.transform = float4x4::identity(); + else + break; + nodeID = node.parent; + } +} + void createCameras(ImporterData& data, ImportMode importMode) { for (uint i = 0; i < data.pScene->mNumCameras; i++) { const aiCamera* pAiCamera = data.pScene->mCameras[i]; - Camera::SharedPtr pCamera = Camera::create(); + ref pCamera = Camera::create(); pCamera->setName(pAiCamera->mName.C_Str()); pCamera->setPosition(aiCast(pAiCamera->mPosition)); pCamera->setUpVector(aiCast(pAiCamera->mUp)); @@ -309,19 +342,21 @@ void createCameras(ImporterData& data, ImportMode importMode) if (nodeID != NodeID::Invalid()) { - SceneBuilder::Node n; - n.name = "Camera.BaseMatrix"; - n.parent = nodeID; - n.transform = pCamera->getViewMatrix(); - // GLTF2 already uses -Z view direction convention in Assimp, FBX does not + // Create a local transform node for the camera + // In GLTF2, the local transform is actually incorrect (contains world space position) + // so we use identity transform instead. + SceneBuilder::Node node; + node.name = pCamera->getName() + ".LocalTransform"; + node.parent = nodeID; if (importMode != ImportMode::GLTF2) - n.transform.setCol(2, -n.transform.getCol(2)); - nodeID = data.builder.addNode(n); - pCamera->setNodeID(nodeID); + node.transform = pCamera->getViewMatrix(); + NodeID localNodeID = data.builder.addNode(node); + pCamera->setNodeID(localNodeID); if (data.builder.isNodeAnimated(nodeID)) { pCamera->setHasAnimation(true); data.builder.setNodeInterpolationMode(nodeID, kCameraInterpolationMode, kCameraEnableWarping); + fixFbxCameraAnimation(data, nodeID); } } @@ -329,7 +364,7 @@ void createCameras(ImporterData& data, ImportMode importMode) } } -void addLightCommon(const Light::SharedPtr& pLight, const rmcv::mat4& baseMatrix, ImporterData& data, const aiLight* pAiLight) +void addLightCommon(const ref& pLight, const float4x4& baseMatrix, ImporterData& data, const aiLight* pAiLight) { FALCOR_ASSERT(pAiLight->mColorDiffuse == pAiLight->mColorSpecular); pLight->setIntensity(aiCast(pAiLight->mColorSpecular)); @@ -351,17 +386,17 @@ void addLightCommon(const Light::SharedPtr& pLight, const rmcv::mat4& baseMatrix void createDirLight(ImporterData& data, const aiLight* pAiLight) { - DirectionalLight::SharedPtr pLight = DirectionalLight::create(pAiLight->mName.C_Str()); + ref pLight = DirectionalLight::create(pAiLight->mName.C_Str()); float3 direction = normalize(aiCast(pAiLight->mDirection)); pLight->setWorldDirection(direction); - rmcv::mat4 base; + float4x4 base = float4x4::identity(); base.setCol(2, float4(-direction, 0)); addLightCommon(pLight, base, data, pAiLight); } void createPointLight(ImporterData& data, const aiLight* pAiLight) { - PointLight::SharedPtr pLight = PointLight::create(pAiLight->mName.C_Str()); + ref pLight = PointLight::create(pAiLight->mName.C_Str()); float3 position = aiCast(pAiLight->mPosition); float3 direction = aiCast(pAiLight->mDirection); float3 up = aiCast(pAiLight->mUp); @@ -376,12 +411,12 @@ void createPointLight(ImporterData& data, const aiLight* pAiLight) pLight->setPenumbraAngle(pAiLight->mAngleOuterCone - pAiLight->mAngleInnerCone); float3 right = cross(direction, up); - rmcv::mat4 base; - // Set it as rows here, really? - base.setCol(0, float4(right, 0)); - base.setCol(1, float4(up, 0)); - base.setCol(2, float4(-direction, 0)); - base.setCol(3, float4(position, 1)); + float4x4 base = matrixFromColumns( + float4(right, 0), // col 0 + float4(up, 0), // col 1 + float4(-direction, 0), // col 2 + float4(position, 1) // col 3 + ); addLightCommon(pLight, base, data, pAiLight); } @@ -440,7 +475,7 @@ void createTangentList( float3 B = float3(pAiBitangent[i].x, pAiBitangent[i].y, pAiBitangent[i].z); float3 N = float3(pAiNormal[i].x, pAiNormal[i].y, pAiNormal[i].z); float sign = dot(cross(N, T), B) >= 0.f ? 1.f : -1.f; - tangents[i] = float4(glm::normalize(T), sign); + tangents[i] = float4(normalize(T), sign); } } @@ -678,9 +713,9 @@ void dumpSceneGraphHierarchy(ImporterData& data, const std::filesystem::path& pa dotfile.close(); } -rmcv::mat4 getLocalToBindPoseMatrix(ImporterData& data, const std::string& name) +float4x4 getLocalToBindPoseMatrix(ImporterData& data, const std::string& name) { - return isBone(data, name) ? data.localToBindPoseMatrices[name] : rmcv::identity(); + return isBone(data, name) ? data.localToBindPoseMatrices[name] : float4x4::identity(); } void parseNode(ImporterData& data, const aiNode* pCurrent, bool hasBoneAncestor) @@ -743,7 +778,7 @@ void loadTextures( ImporterData& data, const aiMaterial* pAiMaterial, const std::filesystem::path& searchPath, - const Material::SharedPtr& pMaterial, + const ref& pMaterial, ImportMode importMode ) { @@ -759,6 +794,9 @@ void loadTextures( aiString aiPath; pAiMaterial->GetTexture(source.aiType, source.aiIndex, &aiPath); std::string path(aiPath.data); + // In GLTF2, the path is encoded as a URI + if (importMode == ImportMode::GLTF2) + path = decodeURI(path); // Assets may contain windows native paths, replace '\' with '/' to make compatible on Linux. std::replace(path.begin(), path.end(), '\\', '/'); if (path.empty()) @@ -773,7 +811,7 @@ void loadTextures( } } -Material::SharedPtr createMaterial( +ref createMaterial( ImporterData& data, const aiMaterial* pAiMaterial, const std::filesystem::path& searchPath, @@ -806,7 +844,7 @@ Material::SharedPtr createMaterial( } // Create an instance of the standard material. All materials are assumed to be of this type. - StandardMaterial::SharedPtr pMaterial = StandardMaterial::create(data.builder.getDevice(), nameStr, shadingModel); + ref pMaterial = StandardMaterial::create(data.builder.getDevice(), nameStr, shadingModel); // Load textures. Note that loading is affected by the current shading model. loadTextures(data, pAiMaterial, searchPath, pMaterial, importMode); @@ -877,7 +915,7 @@ Material::SharedPtr createMaterial( // Handle GLTF2 PBR materials if (importMode == ImportMode::GLTF2) { - if (pAiMaterial->Get(AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_BASE_COLOR_FACTOR, color) == AI_SUCCESS) + if (pAiMaterial->Get(AI_MATKEY_BASE_COLOR, color) == AI_SUCCESS) { float4 baseColor = float4(color.r, color.g, color.b, pMaterial->getBaseColor().a); pMaterial->setBaseColor(baseColor); @@ -886,11 +924,11 @@ Material::SharedPtr createMaterial( float4 specularParams = pMaterial->getSpecularParams(); float metallic; - if (pAiMaterial->Get(AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLIC_FACTOR, metallic) == AI_SUCCESS) + if (pAiMaterial->Get(AI_MATKEY_METALLIC_FACTOR, metallic) == AI_SUCCESS) specularParams.b = metallic; float roughness; - if (pAiMaterial->Get(AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_ROUGHNESS_FACTOR, roughness) == AI_SUCCESS) + if (pAiMaterial->Get(AI_MATKEY_ROUGHNESS_FACTOR, roughness) == AI_SUCCESS) specularParams.g = roughness; pMaterial->setSpecularParams(specularParams); @@ -990,6 +1028,66 @@ void validateScene(ImporterData& data) validateBones(data); } +void dumpAssimpData(ImporterData& data) +{ + std::string out; + + out += "Scene graph hierarchy:\n"; + + std::function dumpNode = [&dumpNode, &data, &out](const aiNode* pNode, int indent) + { + std::string indentStr(indent, ' '); + std::string name = pNode->mName.C_Str(); + std::string type = getNodeType(data, pNode); + + out += fmt::format("{}name: {}\n", indentStr, pNode->mName.C_Str()); + out += fmt::format("{}transform: {}\n", indentStr, aiCast(pNode->mTransformation)); + + for (uint32_t i = 0; i < pNode->mNumChildren; i++) + dumpNode(pNode->mChildren[i], indent + 4); + }; + dumpNode(data.pScene->mRootNode, 0); + + out += "Animations:\n"; + + const auto dumpAnimation = [&out](const aiAnimation* pAnim) + { + out += fmt::format(" name: {}\n", pAnim->mName.C_Str()); + out += fmt::format(" duration: {}\n", pAnim->mDuration); + out += fmt::format(" ticks per second: {}\n", pAnim->mTicksPerSecond); + out += fmt::format(" channels: {}\n", pAnim->mNumChannels); + for (uint32_t i = 0; i < pAnim->mNumChannels; i++) + { + const aiNodeAnim* pChannel = pAnim->mChannels[i]; + out += fmt::format(" channel[{}]:\n", i); + out += fmt::format(" node name: {}\n", pChannel->mNodeName.C_Str()); + out += fmt::format(" position keys: {}\n", pChannel->mNumPositionKeys); + out += fmt::format(" rotation keys: {}\n", pChannel->mNumRotationKeys); + out += fmt::format(" scaling keys: {}\n", pChannel->mNumScalingKeys); + + for (uint32_t j = 0; j < pChannel->mNumPositionKeys; j++) + out += fmt::format( + " position key[{}]: time {}, value {}\n", j, pChannel->mPositionKeys[j].mTime, + aiCast(pChannel->mPositionKeys[j].mValue) + ); + for (uint32_t j = 0; j < pChannel->mNumRotationKeys; j++) + out += fmt::format( + " rotation key[{}]: time {}, value {}\n", j, pChannel->mRotationKeys[j].mTime, + aiCast(pChannel->mRotationKeys[j].mValue) + ); + for (uint32_t j = 0; j < pChannel->mNumScalingKeys; j++) + out += fmt::format( + " scaling key[{}]: time {}, value {}\n", j, pChannel->mScalingKeys[j].mTime, + aiCast(pChannel->mScalingKeys[j].mValue) + ); + } + }; + for (uint32_t i = 0; i < data.pScene->mNumAnimations; i++) + dumpAnimation(data.pScene->mAnimations[i]); + + logInfo(out); +} + } // namespace std::unique_ptr AssimpImporter::create() @@ -997,7 +1095,7 @@ std::unique_ptr AssimpImporter::create() return std::make_unique(); } -void AssimpImporter::importScene(const std::filesystem::path& path, SceneBuilder& builder, const Dictionary& dict) +void AssimpImporter::importScene(const std::filesystem::path& path, SceneBuilder& builder, const pybind11::dict& dict) { if (!path.is_absolute()) throw ImporterError(path, "Expected absolute path."); @@ -1048,6 +1146,8 @@ void AssimpImporter::importScene(const std::filesystem::path& path, SceneBuilder if (hasExtension(path, "gltf") || hasExtension(path, "glb")) importMode = ImportMode::GLTF2; + // dumpAssimpData(data); + createAllMaterials(data, searchPath, importMode); timeReport.measure("Creating materials"); diff --git a/Source/plugins/importers/AssimpImporter/AssimpImporter.h b/Source/plugins/importers/AssimpImporter/AssimpImporter.h index 2d9a276cf..3b7ef95db 100644 --- a/Source/plugins/importers/AssimpImporter/AssimpImporter.h +++ b/Source/plugins/importers/AssimpImporter/AssimpImporter.h @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -50,7 +50,7 @@ class AssimpImporter : public Importer static std::unique_ptr create(); - void importScene(const std::filesystem::path& path, SceneBuilder& builder, const Dictionary& dict) override; + void importScene(const std::filesystem::path& path, SceneBuilder& builder, const pybind11::dict& dict) override; }; } // namespace Falcor diff --git a/Source/plugins/importers/PBRTImporter/Builder.cpp b/Source/plugins/importers/PBRTImporter/Builder.cpp index f336461a3..f1eb8f58a 100644 --- a/Source/plugins/importers/PBRTImporter/Builder.cpp +++ b/Source/plugins/importers/PBRTImporter/Builder.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -173,8 +173,8 @@ std::string BasicScene::toString() const auto printNamedEntities = [&](const std::string_view name, auto entities) { str += fmt::format("{}=[\n", name); - for (const auto& [name, entity] : entities) - str += fmt::format("{}={}\n", name, entity.toString()); + for (const auto& [entityName, entity] : entities) + str += fmt::format("{}={}\n", entityName, entity.toString()); str += "]\n"; }; @@ -227,12 +227,12 @@ void BasicSceneBuilder::onColorSpace(const std::string& name, FileLoc loc) void BasicSceneBuilder::onIdentity(FileLoc loc) { - mGraphicsState.forActiveTransforms([](auto t) { return rmcv::identity(); }); + mGraphicsState.forActiveTransforms([](auto t) { return float4x4::identity(); }); } void BasicSceneBuilder::onTranslate(Float dx, Float dy, Float dz, FileLoc loc) { - mGraphicsState.forActiveTransforms([=](auto t) { return t * rmcv::translate(float3(dx, dy, dz)); }); + mGraphicsState.forActiveTransforms([=](auto t) { return mul(t, math::matrixFromTranslation(float3(dx, dy, dz))); }); } void BasicSceneBuilder::onCoordinateSystem(const std::string& name, FileLoc loc) @@ -335,7 +335,7 @@ void BasicSceneBuilder::onWorldBegin(FileLoc loc) // Reset graphics state. for (uint32_t i = 0; i < kMaxTransforms; ++i) { - mGraphicsState.ctm[i] = rmcv::identity(); + mGraphicsState.ctm[i] = float4x4::identity(); } mGraphicsState.activeTransformBits = kAllTransformsBits; mNamedCoordinateSystems["world"] = mGraphicsState.ctm; @@ -483,8 +483,8 @@ void BasicSceneBuilder::onOption(const std::string& name, const std::string& val void BasicSceneBuilder::onTransform(Float tr[16], FileLoc loc) { // The PBRT file-format has matrices for row-vectors (v.M), so needs transpose - rmcv::mat4 m = rmcv::transpose( - rmcv::mat4({tr[0], tr[1], tr[2], tr[3], tr[4], tr[5], tr[6], tr[7], tr[8], tr[9], tr[10], tr[11], tr[12], tr[13], tr[14], tr[15]}) + float4x4 m = transpose( + float4x4({tr[0], tr[1], tr[2], tr[3], tr[4], tr[5], tr[6], tr[7], tr[8], tr[9], tr[10], tr[11], tr[12], tr[13], tr[14], tr[15]}) ); mGraphicsState.forActiveTransforms([=](auto t) { return m; }); @@ -493,27 +493,27 @@ void BasicSceneBuilder::onTransform(Float tr[16], FileLoc loc) void BasicSceneBuilder::onConcatTransform(Float tr[16], FileLoc loc) { // The PBRT file-format has matrices for row-vectors (v.M), so needs transpose - rmcv::mat4 m = rmcv::transpose( - rmcv::mat4({tr[0], tr[1], tr[2], tr[3], tr[4], tr[5], tr[6], tr[7], tr[8], tr[9], tr[10], tr[11], tr[12], tr[13], tr[14], tr[15]}) + float4x4 m = transpose( + float4x4({tr[0], tr[1], tr[2], tr[3], tr[4], tr[5], tr[6], tr[7], tr[8], tr[9], tr[10], tr[11], tr[12], tr[13], tr[14], tr[15]}) ); - mGraphicsState.forActiveTransforms([=](auto t) { return t * m; }); + mGraphicsState.forActiveTransforms([=](auto t) { return mul(t, m); }); } void BasicSceneBuilder::onRotate(Float angle, Float dx, Float dy, Float dz, FileLoc loc) { - mGraphicsState.forActiveTransforms([=](auto t) { return t * rmcv::rotate(glm::radians(angle), float3(dx, dy, dz)); }); + mGraphicsState.forActiveTransforms([=](auto t) { return mul(t, math::matrixFromRotation(math::radians(angle), float3(dx, dy, dz))); }); } void BasicSceneBuilder::onScale(Float sx, Float sy, Float sz, FileLoc loc) { - mGraphicsState.forActiveTransforms([=](auto t) { return t * rmcv::scale(float3(sx, sy, sz)); }); + mGraphicsState.forActiveTransforms([=](auto t) { return mul(t, math::matrixFromScaling(float3(sx, sy, sz))); }); } void BasicSceneBuilder::onLookAt(Float ex, Float ey, Float ez, Float lx, Float ly, Float lz, Float ux, Float uy, Float uz, FileLoc loc) { - auto lookAt = rmcv::lookAtLH(float3(ex, ey, ez), float3(lx, ly, lz), float3(ux, uy, uz)); - mGraphicsState.forActiveTransforms([=](auto t) { return t * lookAt; }); + auto lookAt = math::matrixFromLookAt(float3(ex, ey, ez), float3(lx, ly, lz), float3(ux, uy, uz), math::Handedness::LeftHanded); + mGraphicsState.forActiveTransforms([=](auto t) { return mul(t, lookAt); }); } void BasicSceneBuilder::onActiveTransformAll(FileLoc loc) diff --git a/Source/plugins/importers/PBRTImporter/Builder.h b/Source/plugins/importers/PBRTImporter/Builder.h index 96de6f058..632e8004e 100644 --- a/Source/plugins/importers/PBRTImporter/Builder.h +++ b/Source/plugins/importers/PBRTImporter/Builder.h @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -37,8 +37,6 @@ #include "Core/Assert.h" #include "Utils/Math/Matrix.h" -#include - #include #include #include @@ -83,18 +81,16 @@ struct MaterialSceneEntity : public SceneEntity struct TransformedSceneEntity : public SceneEntity { TransformedSceneEntity() = default; - TransformedSceneEntity(const std::string& name, ParameterDictionary params, FileLoc loc, const rmcv::mat4& transform) + TransformedSceneEntity(const std::string& name, ParameterDictionary params, FileLoc loc, const float4x4& transform) : SceneEntity(name, params, loc), transform(transform) {} std::string toString() const { - return fmt::format( - "TransformedSceneEntity(name='{}', params={}, transform={})", name, params.toString(), rmcv::to_string(transform) - ); + return fmt::format("TransformedSceneEntity(name='{}', params={}, transform={})", name, params.toString(), to_string(transform)); } - rmcv::mat4 transform; + float4x4 transform = float4x4::identity(); }; struct CameraSceneEntity : public TransformedSceneEntity @@ -104,7 +100,7 @@ struct CameraSceneEntity : public TransformedSceneEntity const std::string& name, ParameterDictionary params, FileLoc loc, - const rmcv::mat4& transform, + const float4x4& transform, const std::string& medium ) : TransformedSceneEntity(name, params, loc, transform), medium(medium) @@ -113,8 +109,7 @@ struct CameraSceneEntity : public TransformedSceneEntity std::string toString() const { return fmt::format( - "CameraSceneEntity(name='{}', params={}, transform={}, medium='{}')", name, params.toString(), rmcv::to_string(transform), - medium + "CameraSceneEntity(name='{}', params={}, transform={}, medium='{}')", name, params.toString(), to_string(transform), medium ); } @@ -124,20 +119,14 @@ struct CameraSceneEntity : public TransformedSceneEntity struct LightSceneEntity : public TransformedSceneEntity { LightSceneEntity() = default; - LightSceneEntity( - const std::string& name, - ParameterDictionary params, - FileLoc loc, - const rmcv::mat4& transform, - const std::string& medium - ) + LightSceneEntity(const std::string& name, ParameterDictionary params, FileLoc loc, const float4x4& transform, const std::string& medium) : TransformedSceneEntity(name, params, loc, transform), medium(medium) {} std::string toString() const { return fmt::format( - "LightSceneEntity(name='{}', params={}, transform={}, medium='{}')", name, params.toString(), rmcv::to_string(transform), medium + "LightSceneEntity(name='{}', params={}, transform={}, medium='{}')", name, params.toString(), to_string(transform), medium ); } @@ -147,26 +136,26 @@ struct LightSceneEntity : public TransformedSceneEntity struct MediumSceneEntity : public TransformedSceneEntity { MediumSceneEntity() = default; - MediumSceneEntity(const std::string& name, ParameterDictionary params, FileLoc loc, const rmcv::mat4& transform) + MediumSceneEntity(const std::string& name, ParameterDictionary params, FileLoc loc, const float4x4& transform) : TransformedSceneEntity(name, params, loc, transform) {} std::string toString() const { - return fmt::format("MediumSceneEntity(name='{}', params={}, transform={})", name, params.toString(), rmcv::to_string(transform)); + return fmt::format("MediumSceneEntity(name='{}', params={}, transform={})", name, params.toString(), to_string(transform)); } }; struct TextureSceneEntity : public TransformedSceneEntity { TextureSceneEntity() = default; - TextureSceneEntity(const std::string& name, ParameterDictionary params, FileLoc loc, const rmcv::mat4& transform) + TextureSceneEntity(const std::string& name, ParameterDictionary params, FileLoc loc, const float4x4& transform) : TransformedSceneEntity(name, params, loc, transform) {} std::string toString() const { - return fmt::format("TextureSceneEntity(name='{}', params={}, transform={})", name, params.toString(), rmcv::to_string(transform)); + return fmt::format("TextureSceneEntity(name='{}', params={}, transform={})", name, params.toString(), to_string(transform)); } }; @@ -177,7 +166,7 @@ struct ShapeSceneEntity : public TransformedSceneEntity const std::string& name, ParameterDictionary params, FileLoc loc, - const rmcv::mat4& transform, + const float4x4& transform, bool reverseOrientation, MaterialRef materialRef, int lightIndex, @@ -197,7 +186,7 @@ struct ShapeSceneEntity : public TransformedSceneEntity return fmt::format( "ShapeSceneEntity(name='{}', params={}, transform={}, reverseOrientation={}, " "materialRef={}. lightIndex={}, insideMedium='{}', outsideMedium='{}')", - name, params.toString(), rmcv::to_string(transform), reverseOrientation, to_string(materialRef), lightIndex, insideMedium, + name, params.toString(), to_string(transform), reverseOrientation, to_string(materialRef), lightIndex, insideMedium, outsideMedium ); } @@ -223,13 +212,13 @@ struct InstanceDefinitionSceneEntity struct InstanceSceneEntity { InstanceSceneEntity() = default; - InstanceSceneEntity(const std::string& name, FileLoc loc, const rmcv::mat4& transform) : name(name), loc(loc), transform(transform) {} + InstanceSceneEntity(const std::string& name, FileLoc loc, const float4x4& transform) : name(name), loc(loc), transform(transform) {} - std::string toString() const { return fmt::format("InstanceSceneEntity(name='{}', transform='{}')", name, rmcv::to_string(transform)); } + std::string toString() const { return fmt::format("InstanceSceneEntity(name='{}', transform='{}')", name, to_string(transform)); } std::string name; FileLoc loc; - rmcv::mat4 transform; + float4x4 transform = float4x4::identity(); }; class BasicScene @@ -305,16 +294,10 @@ class BasicScene constexpr uint32_t kMaxTransforms = 2; -using Transform = rmcv::mat4; +using Transform = float4x4; struct TransformSet { - TransformSet() - { - for (uint32_t i = 0; i < kMaxTransforms; ++i) - t[i] = rmcv::identity(); - } - Transform& operator[](uint32_t i) { FALCOR_ASSERT(i < kMaxTransforms); @@ -332,7 +315,7 @@ struct TransformSet TransformSet tInv; for (uint32_t i = 0; i < kMaxTransforms; ++i) { - tInv.t[i] = rmcv::inverse(ts.t[i]); + tInv.t[i] = inverse(ts.t[i]); } return tInv; } @@ -348,7 +331,7 @@ struct TransformSet } private: - Transform t[kMaxTransforms]; + Transform t[kMaxTransforms] = {float4x4::identity(), float4x4::identity()}; }; class BasicSceneBuilder : public ParserTarget @@ -399,7 +382,7 @@ class BasicSceneBuilder : public ParserTarget void onEndOfFiles() override; private: - rmcv::mat4 getTransform() const { return mGraphicsState.ctm[0]; } + float4x4 getTransform() const { return mGraphicsState.ctm[0]; } static constexpr int kStartTransformBits = 1 << 0; static constexpr int kEndTransformBits = 1 << 1; diff --git a/Source/plugins/importers/PBRTImporter/EnvMapConverter.h b/Source/plugins/importers/PBRTImporter/EnvMapConverter.h index baa3d3582..54e86bf44 100644 --- a/Source/plugins/importers/PBRTImporter/EnvMapConverter.h +++ b/Source/plugins/importers/PBRTImporter/EnvMapConverter.h @@ -30,7 +30,7 @@ #include "Core/API/Formats.h" #include "Core/API/Sampler.h" #include "Core/API/RenderContext.h" -#include "RenderGraph/BasePasses/ComputePass.h" +#include "Core/Pass/ComputePass.h" namespace Falcor::pbrt { @@ -41,14 +41,14 @@ namespace Falcor::pbrt class EnvMapConverter { public: - EnvMapConverter(std::shared_ptr pDevice) : mpDevice(std::move(pDevice)) + EnvMapConverter(ref pDevice) : mpDevice(pDevice) { mpComputePass = ComputePass::create(mpDevice, "plugins/importers/PBRTImporter/EnvMapConverter.cs.slang"); Sampler::Desc desc; desc.setFilterMode(Sampler::Filter::Linear, Sampler::Filter::Linear, Sampler::Filter::Linear); desc.setAddressingMode(Sampler::AddressMode::Clamp, Sampler::AddressMode::Clamp, Sampler::AddressMode::Clamp); - mpSampler = Sampler::create(mpDevice.get(), desc); + mpSampler = Sampler::create(mpDevice, desc); } /** @@ -58,15 +58,15 @@ class EnvMapConverter * @param[in] pSrcTexture Source texture with envmap in equal-area octahedral mapping. * @return Texture with envmap in lat-long mapping. */ - Texture::SharedPtr convertEqualAreaOctToLatLong(RenderContext* pRenderContext, const Texture::SharedPtr& pSrcTexture) const + ref convertEqualAreaOctToLatLong(RenderContext* pRenderContext, const ref& pSrcTexture) const { FALCOR_ASSERT(pSrcTexture); FALCOR_ASSERT(pSrcTexture->getWidth() == pSrcTexture->getHeight()); uint2 dstDim{pSrcTexture->getWidth() * 2, pSrcTexture->getHeight()}; - Texture::SharedPtr pDstTexture = Texture::create2D( - mpDevice.get(), dstDim.x, dstDim.y, ResourceFormat::RGBA32Float, 1, 1, nullptr, + ref pDstTexture = Texture::create2D( + mpDevice, dstDim.x, dstDim.y, ResourceFormat::RGBA32Float, 1, 1, nullptr, Resource::BindFlags::ShaderResource | Resource::BindFlags::UnorderedAccess ); @@ -81,9 +81,9 @@ class EnvMapConverter } private: - std::shared_ptr mpDevice; - ComputePass::SharedPtr mpComputePass; - Sampler::SharedPtr mpSampler; + ref mpDevice; + ref mpComputePass; + ref mpSampler; }; } // namespace Falcor::pbrt diff --git a/Source/plugins/importers/PBRTImporter/LoopSubdivide.cpp b/Source/plugins/importers/PBRTImporter/LoopSubdivide.cpp index 7f1dcf34c..edce40e54 100644 --- a/Source/plugins/importers/PBRTImporter/LoopSubdivide.cpp +++ b/Source/plugins/importers/PBRTImporter/LoopSubdivide.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -178,11 +178,11 @@ LoopSubdivideResult loopSubdivide(uint32_t levels, fstd::span posi std::vector faces; // Allocate vertices and faces. - std::unique_ptr verts = std::make_unique(positions.size()); + std::unique_ptr vertexBuffer = std::make_unique(positions.size()); for (size_t i = 0; i < positions.size(); ++i) { - verts[i] = SDVertex(positions[i]); - vertices.push_back(&verts[i]); + vertexBuffer[i] = SDVertex(positions[i]); + vertices.push_back(&vertexBuffer[i]); } size_t faceCount = indices.size() / 3; std::unique_ptr fs = std::make_unique(faceCount); @@ -192,15 +192,17 @@ LoopSubdivideResult loopSubdivide(uint32_t levels, fstd::span posi } // Set face to vertex pointers. - const uint32_t* vp = indices.data(); - for (size_t i = 0; i < faceCount; ++i, vp += 3) { - SDFace* f = faces[i]; - for (uint32_t j = 0; j < 3; ++j) + const uint32_t* vp = indices.data(); + for (size_t i = 0; i < faceCount; ++i, vp += 3) { - SDVertex* v = vertices[vp[j]]; - f->v[j] = v; - v->startFace = f; + SDFace* f = faces[i]; + for (uint32_t j = 0; j < 3; ++j) + { + SDVertex* v = vertices[vp[j]]; + f->v[j] = v; + v->startFace = f; + } } } @@ -494,7 +496,7 @@ static float3 weightOneRing(SDVertex* vert, float beta) return p; } -void SDVertex::oneRing(float3* p) +void SDVertex::oneRing(float3* p_) { if (!boundary) { @@ -502,7 +504,7 @@ void SDVertex::oneRing(float3* p) SDFace* face = startFace; do { - *p++ = face->nextVert(this)->p; + *p_++ = face->nextVert(this)->p; face = face->nextFace(this); } while (face != startFace); } @@ -515,10 +517,10 @@ void SDVertex::oneRing(float3* p) { face = f2; } - *p++ = face->nextVert(this)->p; + *p_++ = face->nextVert(this)->p; do { - *p++ = face->prevVert(this)->p; + *p_++ = face->prevVert(this)->p; face = face->prevFace(this); } while (face != nullptr); } diff --git a/Source/plugins/importers/PBRTImporter/PBRTImporter.cpp b/Source/plugins/importers/PBRTImporter/PBRTImporter.cpp index ce4d3811b..696371b8a 100644 --- a/Source/plugins/importers/PBRTImporter/PBRTImporter.cpp +++ b/Source/plugins/importers/PBRTImporter/PBRTImporter.cpp @@ -85,20 +85,22 @@ #include "Scene/Material/PBRT/PBRTDiffuseTransmissionMaterial.h" #include "Scene/Curves/CurveTessellation.h" +#include + #include namespace Falcor { namespace pbrt { -const rmcv::mat4 kYtoZ = { +const float4x4 kYtoZ = { 1.f, 0.f, 0.f, 0.f, // 0.f, 0.f, 1.f, 0.f, // 0.f, 1.f, 0.f, 0.f, // 0.f, 0.f, 0.f, 1.f, // }; -const rmcv::mat4 kInvertZ = { +const float4x4 kInvertZ = { 1.f, 0.f, 0.f, 0.f, // 0.f, 1.f, 0.f, 0.f, // 0.f, 0.f, -1.f, 0.f, // @@ -110,8 +112,8 @@ const rmcv::mat4 kInvertZ = { */ struct Camera { - Falcor::Camera::SharedPtr pCamera; - rmcv::mat4 transform; + Falcor::ref pCamera; + float4x4 transform = float4x4::identity(); }; /** @@ -119,8 +121,8 @@ struct Camera */ struct Light { - Falcor::Light::SharedPtr pLight; - Falcor::EnvMap::SharedPtr pEnvMap; + Falcor::ref pLight; + Falcor::ref pEnvMap; }; /** @@ -130,8 +132,8 @@ struct Light */ struct FloatTexture { - std::variant texture; - rmcv::mat4 transform = rmcv::identity(); + std::variant> texture; + float4x4 transform = float4x4::identity(); bool isConstant() const { return std::holds_alternative(texture); } float getConstant() const @@ -149,8 +151,8 @@ struct FloatTexture struct SpectrumTexture { SpectrumType spectrumType = SpectrumType::Albedo; - std::variant texture; - rmcv::mat4 transform = rmcv::identity(); + std::variant> texture; + float4x4 transform = float4x4::identity(); bool isConstant() const { return std::holds_alternative(texture); } Spectrum getConstant() const @@ -168,9 +170,9 @@ struct Medium */ struct Shape { - Falcor::TriangleMesh::SharedPtr pTriangleMesh; - rmcv::mat4 transform; - Falcor::Material::SharedPtr pMaterial; + Falcor::ref pTriangleMesh; + float4x4 transform = float4x4::identity(); + Falcor::ref pMaterial; }; /** @@ -181,14 +183,14 @@ struct Shape */ struct CurveAggregate { - using Key = std::tuple; + using Key = std::tuple; struct KeyHash { std::size_t operator()(const Key& key) const { return fnvHashArray64(&key, sizeof(key)); } }; - rmcv::mat4 transform; - Falcor::Material::SharedPtr pMaterial; + float4x4 transform = float4x4::identity(); + Falcor::ref pMaterial; uint32_t splitDepth; std::vector strands; ///< Contains the number of points in each strand. @@ -198,8 +200,8 @@ struct CurveAggregate struct InstanceDefinition { - std::vector> meshes; // List of meshID + transform - std::vector> curves; // List of curveID + transfrom + std::vector> meshes; // List of meshID + transform + std::vector> curves; // List of curveID + transfrom }; struct BuilderContext @@ -212,10 +214,10 @@ struct BuilderContext std::map media; - std::map namedMaterials; - std::vector materials; + std::map> namedMaterials; + std::vector> materials; - Falcor::Material::SharedPtr pDefaultMaterial; + Falcor::ref pDefaultMaterial; std::unordered_map curveAggregates; @@ -225,9 +227,9 @@ struct BuilderContext bool usePBRTMaterials = false; - Falcor::Material::SharedPtr getMaterial(const MaterialRef& materialRef) + Falcor::ref getMaterial(const MaterialRef& materialRef) { - Falcor::Material::SharedPtr pMaterial; + Falcor::ref pMaterial; if (const uint32_t* pIndex = std::get_if(&materialRef)) { @@ -359,12 +361,12 @@ SpectrumTexture getSpectrumTexture( void assignSpectrumTexture( const SpectrumTexture& spectrumTexture, std::function constantSetter, - std::function textureSetter + std::function)> textureSetter ) { if (const auto* pSpectrum = std::get_if(&spectrumTexture.texture)) constantSetter(spectrumToRGB(*pSpectrum, spectrumTexture.spectrumType)); - else if (const auto* pTexture = std::get_if(&spectrumTexture.texture)) + else if (const auto* pTexture = std::get_if>(&spectrumTexture.texture)) textureSetter(*pTexture); } @@ -601,7 +603,7 @@ Camera createCamera(BuilderContext& ctx, const CameraSceneEntity& entity) auto pCamera = Falcor::Camera::create("Camera"); pCamera->setApertureRadius(lensradius); pCamera->setFocalDistance(focaldistance); - float focalLength = fovYToFocalLength(glm::radians(fov), 24.f); + float focalLength = fovYToFocalLength(math::radians(fov), 24.f); pCamera->setFocalLength(focalLength); camera.pCamera = pCamera; @@ -683,7 +685,7 @@ Light createLight(BuilderContext& ctx, const LightSceneEntity& entity) if (illuminance > 0.f) intensity *= illuminance; - float3 direction = normalize(rmcv::mat3(entity.transform) * (to - from)); + float3 direction = normalize(transformVector(entity.transform, to - from)); auto pDirectionalLight = Falcor::DirectionalLight::create("DirectionalLight"); pDirectionalLight->setIntensity(intensity); @@ -708,8 +710,7 @@ Light createLight(BuilderContext& ctx, const LightSceneEntity& entity) // Falcor doesn't have constant infinite emitter. // We create a one pixel env map for now. float4 data{spectrumToRGB(L[0], SpectrumType::Illuminant), 0.f}; - auto pTexture = - Texture::create2D(ctx.builder.getDevice().get(), 1, 1, ResourceFormat::RGBA32Float, 1, Texture::kMaxPossible, &data); + auto pTexture = Texture::create2D(ctx.builder.getDevice(), 1, 1, ResourceFormat::RGBA32Float, 1, Texture::kMaxPossible, &data); auto pEnvMap = EnvMap::create(ctx.builder.getDevice(), pTexture); light.pEnvMap = pEnvMap; @@ -717,7 +718,7 @@ Light createLight(BuilderContext& ctx, const LightSceneEntity& entity) else if (!filename.empty()) { auto path = ctx.resolver(filename); - auto pOctTexture = Falcor::Texture::createFromFile(ctx.builder.getDevice().get(), path, false, false); + auto pOctTexture = Falcor::Texture::createFromFile(ctx.builder.getDevice(), path, false, false); // TODO: Use equal-area octahedral parametrization when env map supports it. logWarning( entity.loc, @@ -729,8 +730,8 @@ Light createLight(BuilderContext& ctx, const LightSceneEntity& entity) pEnvMap->setIntensity(scale); float3 rotation; - rmcv::extractEulerAngleXYZ(entity.transform, rotation.x, rotation.y, rotation.z); - pEnvMap->setRotation(glm::degrees(rotation)); + math::extractEulerAngleXYZ(entity.transform, rotation.x, rotation.y, rotation.z); + pEnvMap->setRotation(math::degrees(rotation)); light.pEnvMap = pEnvMap; } @@ -753,7 +754,7 @@ FloatTexture createFloatTexture(BuilderContext& ctx, const TextureSceneEntity& e FloatTexture floatTexture; floatTexture.transform = entity.transform; - if (floatTexture.transform != rmcv::identity()) + if (floatTexture.transform != float4x4::identity()) logWarning(entity.loc, "Texture transforms are currently not supported and ignored."); if (type == "constant") @@ -815,7 +816,7 @@ FloatTexture createFloatTexture(BuilderContext& ctx, const TextureSceneEntity& e } bool sRGB = encoding == "sRGB"; - floatTexture.texture = Falcor::Texture::createFromFile(ctx.builder.getDevice().get(), path, generateMips, sRGB); + floatTexture.texture = Falcor::Texture::createFromFile(ctx.builder.getDevice(), path, generateMips, sRGB); } else if (type == "checkerboard") { @@ -870,7 +871,7 @@ SpectrumTexture createSpectrumTexture(BuilderContext& ctx, const TextureSceneEnt spectrumTexture.spectrumType = SpectrumType::Albedo; spectrumTexture.transform = entity.transform; - if (spectrumTexture.transform != rmcv::identity()) + if (spectrumTexture.transform != float4x4::identity()) logWarning(entity.loc, "Texture transforms are currently not supported and ignored."); if (type == "constant") @@ -932,7 +933,7 @@ SpectrumTexture createSpectrumTexture(BuilderContext& ctx, const TextureSceneEnt } bool sRGB = encoding == "sRGB"; - spectrumTexture.texture = Falcor::Texture::createFromFile(ctx.builder.getDevice().get(), path, generateMips, sRGB); + spectrumTexture.texture = Falcor::Texture::createFromFile(ctx.builder.getDevice(), path, generateMips, sRGB); } else if (type == "checkerboard") { @@ -966,14 +967,14 @@ SpectrumTexture createSpectrumTexture(BuilderContext& ctx, const TextureSceneEnt return spectrumTexture; } -Falcor::Material::SharedPtr createMaterial(BuilderContext& ctx, const MaterialSceneEntity& entity, bool isAreaLight = false) +Falcor::ref createMaterial(BuilderContext& ctx, const MaterialSceneEntity& entity, bool isAreaLight = false) { const auto& type = entity.type; const auto& params = entity.params; auto warnUnsupported = [&]() { warnUnsupportedType(entity.loc, "Material", type); }; - Falcor::Material::SharedPtr pMaterial; + Falcor::ref pMaterial; if (type == "" || type == "none") { @@ -997,7 +998,7 @@ Falcor::Material::SharedPtr createMaterial(BuilderContext& ctx, const MaterialSc auto pPBRTMaterial = PBRTDiffuseMaterial::create(ctx.builder.getDevice(), entity.name); assignSpectrumTexture( reflectance, [&](float3 rgb) { pPBRTMaterial->setBaseColor(float4(rgb, 1.f)); }, - [&](Falcor::Texture::SharedPtr pTexture) { pPBRTMaterial->setBaseColorTexture(pTexture); } + [&](Falcor::ref pTexture) { pPBRTMaterial->setBaseColorTexture(pTexture); } ); pPBRTMaterial->setDoubleSided(true); pMaterial = pPBRTMaterial; @@ -1009,7 +1010,7 @@ Falcor::Material::SharedPtr createMaterial(BuilderContext& ctx, const MaterialSc pStandardMaterial->setRoughness(1.f); assignSpectrumTexture( reflectance, [&](float3 rgb) { pStandardMaterial->setBaseColor(float4(rgb, 1.f)); }, - [&](Falcor::Texture::SharedPtr pTexture) { pStandardMaterial->setBaseColorTexture(pTexture); } + [&](Falcor::ref pTexture) { pStandardMaterial->setBaseColorTexture(pTexture); } ); pStandardMaterial->setDoubleSided(true); pMaterial = pStandardMaterial; @@ -1034,7 +1035,7 @@ Falcor::Material::SharedPtr createMaterial(BuilderContext& ctx, const MaterialSc pPBRTMaterial->setRoughness(roughness); assignSpectrumTexture( reflectance, [&](float3 rgb) { pPBRTMaterial->setBaseColor(float4(rgb, 1.f)); }, - [&](Falcor::Texture::SharedPtr pTexture) { pPBRTMaterial->setBaseColorTexture(pTexture); } + [&](Falcor::ref pTexture) { pPBRTMaterial->setBaseColorTexture(pTexture); } ); pPBRTMaterial->setDoubleSided(true); pMaterial = pPBRTMaterial; @@ -1048,7 +1049,7 @@ Falcor::Material::SharedPtr createMaterial(BuilderContext& ctx, const MaterialSc pStandardMaterial->setRoughness(std::sqrt(roughness)); assignSpectrumTexture( reflectance, [&](float3 rgb) { pStandardMaterial->setBaseColor(float4(rgb, 1.f)); }, - [&](Falcor::Texture::SharedPtr pTexture) { pStandardMaterial->setBaseColorTexture(pTexture); } + [&](Falcor::ref pTexture) { pStandardMaterial->setBaseColorTexture(pTexture); } ); pStandardMaterial->setDoubleSided(true); pMaterial = pStandardMaterial; @@ -1196,11 +1197,11 @@ Falcor::Material::SharedPtr createMaterial(BuilderContext& ctx, const MaterialSc auto pPBRTMaterial = PBRTDiffuseTransmissionMaterial::create(ctx.builder.getDevice(), entity.name); assignSpectrumTexture( reflectance, [&](float3 rgb) { pPBRTMaterial->setBaseColor(float4(rgb, 1.f)); }, - [&](Falcor::Texture::SharedPtr pTexture) { pPBRTMaterial->setBaseColorTexture(pTexture); } + [&](Falcor::ref pTexture) { pPBRTMaterial->setBaseColorTexture(pTexture); } ); assignSpectrumTexture( transmittance, [&](float3 rgb) { pPBRTMaterial->setTransmissionColor(rgb); }, - [&](Falcor::Texture::SharedPtr pTexture) { pPBRTMaterial->setTransmissionTexture(pTexture); } + [&](Falcor::ref pTexture) { pPBRTMaterial->setTransmissionTexture(pTexture); } ); pMaterial = pPBRTMaterial; } @@ -1212,11 +1213,11 @@ Falcor::Material::SharedPtr createMaterial(BuilderContext& ctx, const MaterialSc pStandardMaterial->setDiffuseTransmission(0.5f); assignSpectrumTexture( reflectance, [&](float3 rgb) { pStandardMaterial->setBaseColor(float4(rgb, 1.f)); }, - [&](Falcor::Texture::SharedPtr pTexture) { pStandardMaterial->setBaseColorTexture(pTexture); } + [&](Falcor::ref pTexture) { pStandardMaterial->setBaseColorTexture(pTexture); } ); assignSpectrumTexture( transmittance, [&](float3 rgb) { pStandardMaterial->setTransmissionColor(rgb); }, - [&](Falcor::Texture::SharedPtr pTexture) { pStandardMaterial->setTransmissionTexture(pTexture); } + [&](Falcor::ref pTexture) { pStandardMaterial->setTransmissionTexture(pTexture); } ); pStandardMaterial->setDoubleSided(true); pMaterial = pStandardMaterial; @@ -1243,7 +1244,7 @@ Falcor::Material::SharedPtr createMaterial(BuilderContext& ctx, const MaterialSc float beta_n = getFloatTextureConstantOnly(ctx, params, "beta_n", 0.3f); float alpha = getFloatTextureConstantOnly(ctx, params, "alpha", 2.f); - HairMaterial::SharedPtr pHairMaterial = HairMaterial::create(ctx.builder.getDevice(), entity.name); + ref pHairMaterial = HairMaterial::create(ctx.builder.getDevice(), entity.name); float3 baseColor = HairMaterial::colorFromSigmaA(HairMaterial::sigmaAFromConcentration(0.5f, 0.2f), beta_n); pHairMaterial->setBaseColor(float4(baseColor, 1.f)); pHairMaterial->setSpecularParams(float4(beta_m, beta_n, alpha, 0.f)); @@ -1261,7 +1262,7 @@ Falcor::Material::SharedPtr createMaterial(BuilderContext& ctx, const MaterialSc assignSpectrumTexture( *sigma_a, [&](float3 constant) { pHairMaterial->setBaseColor(float4(HairMaterial::colorFromSigmaA(constant, beta_n), 1.f)); }, - [&](Falcor::Texture::SharedPtr pTexture) + [&](Falcor::ref pTexture) { logWarning(entity.loc, "Non-constant 'sigma_a' is currently not supported. Using default color instead."); } ); } @@ -1274,16 +1275,15 @@ Falcor::Material::SharedPtr createMaterial(BuilderContext& ctx, const MaterialSc assignSpectrumTexture( *reflectance, [&](float3 constant) { pHairMaterial->setBaseColor(float4(constant, 1.f)); }, - [&](Falcor::Texture::SharedPtr pTexture) { pHairMaterial->setBaseColorTexture(pTexture); } + [&](Falcor::ref pTexture) { pHairMaterial->setBaseColorTexture(pTexture); } ); } else if (eumelanin || pheomelanin) { float eumelaninValue = getFloatTextureConstantOnly(ctx, params, "eumelanin", 0.3f); float pheomelaninValue = getFloatTextureConstantOnly(ctx, params, "pheomelanin", 0.3f); - float3 baseColor = - HairMaterial::colorFromSigmaA(HairMaterial::sigmaAFromConcentration(eumelaninValue, pheomelaninValue), beta_n); - pHairMaterial->setBaseColor(float4(baseColor, 1.f)); + float3 color = HairMaterial::colorFromSigmaA(HairMaterial::sigmaAFromConcentration(eumelaninValue, pheomelaninValue), beta_n); + pHairMaterial->setBaseColor(float4(color, 1.f)); } pMaterial = pHairMaterial; @@ -1322,7 +1322,7 @@ Falcor::Material::SharedPtr createMaterial(BuilderContext& ctx, const MaterialSc auto normalmap = params.getString("normalmap", ""); if (!normalmap.empty()) { - auto pNormalMap = Texture::createFromFile(ctx.builder.getDevice().get(), ctx.resolver(normalmap), true, false); + auto pNormalMap = Texture::createFromFile(ctx.builder.getDevice(), ctx.resolver(normalmap), true, false); pMaterial->setTexture(Material::TextureSlot::Normal, pNormalMap); } } @@ -1363,7 +1363,7 @@ Medium createMedium(BuilderContext& ctx, const MediumSceneEntity& entity) return medium; } -void createAreaLight(BuilderContext& ctx, const SceneEntity& entity, const Falcor::Material::SharedPtr& pMaterial) +void createAreaLight(BuilderContext& ctx, const SceneEntity& entity, const Falcor::ref& pMaterial) { auto warnUnsupported = [&]() { warnUnsupportedType(entity.loc, "Area light", entity.name); }; @@ -1377,7 +1377,7 @@ void createAreaLight(BuilderContext& ctx, const SceneEntity& entity, const Falco auto L = getSpectrumAsRGB(ctx, params, "L", float3(1.f), SpectrumType::Illuminant); auto scale = params.getFloat("scale", 1.f); - if (auto pStandardMaterial = std::dynamic_pointer_cast(pMaterial)) + if (auto pStandardMaterial = dynamic_ref_cast(pMaterial)) { pStandardMaterial->setEmissiveColor(L); pStandardMaterial->setEmissiveFactor(1.f); @@ -1433,7 +1433,7 @@ Shape createShape(BuilderContext& ctx, const ShapeSceneEntity& entity) shape.pTriangleMesh = Falcor::TriangleMesh::createDisk(radius); shape.pTriangleMesh->setName("disk"); - rmcv::mat4 transform = rmcv::translate(float3(0.f, 0.f, height)) * kYtoZ; + float4x4 transform = mul(math::matrixFromTranslation(float3(0.f, 0.f, height)), kYtoZ); shape.pTriangleMesh->applyTransform(transform); } else if (type == "bilinearmesh") @@ -1459,9 +1459,9 @@ Shape createShape(BuilderContext& ctx, const ShapeSceneEntity& entity) if (basis != "bspline") logWarning(entity.loc, "Basis '{}' is not supported. Using 'bspline' basis instead.", basis); - auto type = params.getString("type", "flat"); - if (type != "cylinder") - logWarning(entity.loc, "Curve type '{}' is not supported. Using 'cylinder' type instead.", type); + auto curveType = params.getString("type", "flat"); + if (curveType != "cylinder") + logWarning(entity.loc, "Curve type '{}' is not supported. Using 'cylinder' type instead.", curveType); auto P = params.getPoint3Array("P"); @@ -1488,7 +1488,7 @@ Shape createShape(BuilderContext& ctx, const ShapeSceneEntity& entity) { float t = float(i) / pointCount; aggregate.points[offset + i] = P[i]; - aggregate.widths[offset + i] = lerp(width0, width1, t); + aggregate.widths[offset + i] = math::lerp(width0, width1, t); } } else if (type == "trianglemesh") @@ -1601,7 +1601,7 @@ Shape createShape(BuilderContext& ctx, const ShapeSceneEntity& entity) auto& vertex = vertexList[i]; vertex.position = result.positions[i]; vertex.normal = result.normals[i]; - vertex.texCoord = float3(0.f); + vertex.texCoord = float2(0.f); } shape.pTriangleMesh = Falcor::TriangleMesh::create(vertexList, result.indices); @@ -1664,7 +1664,7 @@ std::variant createCurveGeometry(BuilderContext { auto result = CurveTessellation::convertToLinearSweptSphere( curveAggregate.strands.size(), curveAggregate.strands.data(), curveAggregate.points.data(), curveAggregate.widths.data(), - nullptr, 1, subdivPerSegment, 1, 1, 1.f, rmcv::mat4() + nullptr, 1, subdivPerSegment, 1, 1, 1.f, float4x4::identity() ); Falcor::SceneBuilder::Curve curve; @@ -1777,7 +1777,7 @@ void buildScene(BuilderContext& ctx) auto camera = createCamera(ctx, ctx.scene.getCamera()); if (camera.pCamera) { - auto nodeID = ctx.builder.addNode({"camera", camera.transform * kInvertZ}); + auto nodeID = ctx.builder.addNode({"camera", mul(camera.transform, kInvertZ)}); camera.pCamera->setNodeID(nodeID); ctx.builder.addCamera(camera.pCamera); } @@ -1859,7 +1859,7 @@ void buildScene(BuilderContext& ctx) // Instantiate meshes. for (const auto& [meshID, transform] : instanceDefinition.meshes) { - auto nodeID = ctx.builder.addNode({"instance", instanceTransform * transform}); + auto nodeID = ctx.builder.addNode({"instance", mul(instanceTransform, transform)}); ctx.builder.addMeshInstance(nodeID, meshID); } } @@ -1872,7 +1872,7 @@ std::unique_ptr PBRTImporter::create() return std::make_unique(); } -void PBRTImporter::importScene(const std::filesystem::path& path, SceneBuilder& builder, const Dictionary& dict) +void PBRTImporter::importScene(const std::filesystem::path& path, SceneBuilder& builder, const pybind11::dict& dict) { if (!path.is_absolute()) throw ImporterError(path, "Expected absolute path."); diff --git a/Source/plugins/importers/PBRTImporter/PBRTImporter.h b/Source/plugins/importers/PBRTImporter/PBRTImporter.h index 31df50dc4..517791e5e 100644 --- a/Source/plugins/importers/PBRTImporter/PBRTImporter.h +++ b/Source/plugins/importers/PBRTImporter/PBRTImporter.h @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -43,7 +43,7 @@ class PBRTImporter : public Importer static std::unique_ptr create(); - void importScene(const std::filesystem::path& path, SceneBuilder& builder, const Dictionary& dict) override; + void importScene(const std::filesystem::path& path, SceneBuilder& builder, const pybind11::dict& dict) override; }; } // namespace Falcor diff --git a/Source/plugins/importers/PythonImporter/PythonImporter.cpp b/Source/plugins/importers/PythonImporter/PythonImporter.cpp index b1984409e..be318e3b0 100644 --- a/Source/plugins/importers/PythonImporter/PythonImporter.cpp +++ b/Source/plugins/importers/PythonImporter/PythonImporter.cpp @@ -27,7 +27,7 @@ **************************************************************************/ #include "PythonImporter.h" #include "Scene/Importer.h" -#include "Scene/SceneBuilderAccess.h" +#include "GlobalState.h" #include "Utils/Scripting/Scripting.h" #include #include @@ -72,8 +72,8 @@ static std::vector sImportDirectories; class ScopedImport { public: - ScopedImport(const std::filesystem::path& path, Settings& settings) - : mScopedSettings(settings), mPath(path), mDirectory(path.parent_path()) + ScopedImport(const std::filesystem::path& path, SceneBuilder& builder) + : mScopedSettings(builder.getSettings()), mPath(path), mDirectory(path.parent_path()) { FALCOR_ASSERT(path.is_absolute()); sImportPaths.emplace(mPath); @@ -81,6 +81,9 @@ class ScopedImport // Add directory to search directories (add it to the front to make it highest priority). addDataDirectory(mDirectory, true); + + // Set global scene builder as workaround to support old Python API. + setActivePythonSceneBuilder(&builder); } ~ScopedImport() { @@ -95,6 +98,12 @@ class ScopedImport { removeDataDirectory(mDirectory); } + + // Unset global scene builder. + if (sImportDirectories.size() == 0) + { + setActivePythonSceneBuilder(nullptr); + } } private: @@ -115,7 +124,7 @@ std::unique_ptr PythonImporter::create() return std::make_unique(); } -void PythonImporter::importScene(const std::filesystem::path& path, SceneBuilder& builder, const Dictionary& dict) +void PythonImporter::importScene(const std::filesystem::path& path, SceneBuilder& builder, const pybind11::dict& dict) { if (!path.is_absolute()) throw ImporterError(path, "Expected absolute path."); @@ -132,7 +141,7 @@ void PythonImporter::importScene(const std::filesystem::path& path, SceneBuilder // Keep track of this import and add script directory to data search directories. // We use RAII here to make sure the scope is properly removed when throwing an exception. - ScopedImport scopedImport(path, builder.getSettings()); + ScopedImport scopedImport(path, builder); // Execute script. try @@ -140,9 +149,7 @@ void PythonImporter::importScene(const std::filesystem::path& path, SceneBuilder Scripting::Context context; context.setObject("sceneBuilder", &builder); Scripting::runScript("from falcor import *", context); - setActivePythonSceneBuilder(&builder); Scripting::runScriptFromFile(path, context); - setActivePythonSceneBuilder(nullptr); } catch (const std::exception& e) { diff --git a/Source/plugins/importers/PythonImporter/PythonImporter.h b/Source/plugins/importers/PythonImporter/PythonImporter.h index ef95aa546..c287dd245 100644 --- a/Source/plugins/importers/PythonImporter/PythonImporter.h +++ b/Source/plugins/importers/PythonImporter/PythonImporter.h @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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,7 +40,7 @@ class PythonImporter : public Importer static std::unique_ptr create(); - void importScene(const std::filesystem::path& path, SceneBuilder& builder, const Dictionary& dict) override; + void importScene(const std::filesystem::path& path, SceneBuilder& builder, const pybind11::dict& dict) override; }; } // namespace Falcor diff --git a/Source/plugins/importers/USDImporter/CMakeLists.txt b/Source/plugins/importers/USDImporter/CMakeLists.txt index d496dae14..2b3e5cbde 100644 --- a/Source/plugins/importers/USDImporter/CMakeLists.txt +++ b/Source/plugins/importers/USDImporter/CMakeLists.txt @@ -9,13 +9,14 @@ target_sources(USDImporter PRIVATE CreateSpecularTransmissionTexture.cs.slang ImporterContext.cpp ImporterContext.h + IndexedVector.h PackBaseColorAlpha.cs.slang PreviewSurfaceConverter.cpp PreviewSurfaceConverter.h SampleTexture.slang StandardMaterialSpec.h - Subdivision.cpp - Subdivision.h + Tessellation.cpp + Tessellation.h USDHelpers.h USDImporter.cpp USDImporter.h diff --git a/Source/plugins/importers/USDImporter/ImporterContext.cpp b/Source/plugins/importers/USDImporter/ImporterContext.cpp index 3183c57a1..fbe5eddaa 100644 --- a/Source/plugins/importers/USDImporter/ImporterContext.cpp +++ b/Source/plugins/importers/USDImporter/ImporterContext.cpp @@ -34,9 +34,11 @@ #include "Scene/Material/HairMaterial.h" #include "Scene/Material/StandardMaterial.h" #include "Utils/Settings.h" -#include "Subdivision.h" +#include "Tessellation.h" -#include +#include + +#include #include #include @@ -60,12 +62,6 @@ BEGIN_DISABLE_USD_WARNINGS #include #include #include -#include -#include -#include -#include -#include -#include #include #include #include @@ -142,7 +138,7 @@ namespace Falcor // Shuffle triangle data into GeomSubset order. // N specifies the number of data values per face; 1 corresponds to uniform, 3 corresponds to faceVarying template - void remapGeomSubsetData(T& inData, const VtIntArray& faceMap, VtIntArray subsetOffset, const VtIntArray& primitiveParams) + void remapGeomSubsetData(T& inData, const VtIntArray& faceMap, VtIntArray subsetOffset, const VtIntArray& coarseFaceIndices) { // Note that subsetOffset is passed by value; we modify the local copy as part of bookkeeping FALCOR_ASSERT(N == 1 || N == 3); @@ -151,8 +147,7 @@ namespace Falcor for (size_t i = 0; i < inData.size() / N; ++i) { - int coarseFaceParam = primitiveParams[i]; - int faceIdx = HdMeshUtil::DecodeFaceIndexFromCoarseFaceParam(coarseFaceParam); + int faceIdx = coarseFaceIndices[i]; int geomSubset = faceMap[faceIdx]; // Check for unassigned faces to ensure we don't fall over in the face of invalid input if (geomSubset >= 0) @@ -169,42 +164,16 @@ namespace Falcor } template - void remapGeomSubsetData(T& inData, AttributeFrequency freq, const VtIntArray& faceMap, VtIntArray& subsetOffset, const VtIntArray& primitiveParams) + void remapGeomSubsetData(T& inData, AttributeFrequency freq, const VtIntArray& faceMap, VtIntArray& subsetOffset, const VtIntArray& coarseFaceIndices) { // Reshuffle data into subset order, if necessary (i.e., if the count depends on the number of faces) if (freq == AttributeFrequency::FaceVarying) { - remapGeomSubsetData<3>(inData, faceMap, subsetOffset, primitiveParams); - } - else if (freq == AttributeFrequency::Uniform) - { - remapGeomSubsetData<1>(inData, faceMap, subsetOffset, primitiveParams); - } - } - - // Generate triangulated versions of any data items whose count depends on the number of faces. - // Safe to call for any AttributeFrequency, as non-face-varying data will be passed through unchanged. - template - void convertTriangulatedFaceData(T& inData, AttributeFrequency freq, HdType hdType, size_t faceCount, const VtIntArray& primitiveParams, HdMeshUtil& meshUtil) - { - // If the number of data items depends on the number of faces, generate triangulated versions. - // Otherwise, don't modify inData. - if (freq == AttributeFrequency::FaceVarying) - { - VtValue triangulatedData; - meshUtil.ComputeTriangulatedFaceVaryingPrimvar((const void*)inData.cdata(), (int)inData.size(), hdType, &triangulatedData); - inData = triangulatedData.Get(); + remapGeomSubsetData<3>(inData, faceMap, subsetOffset, coarseFaceIndices); } else if (freq == AttributeFrequency::Uniform) { - T triangulatedData(faceCount); - for (size_t i = 0; i < faceCount; ++i) - { - int coarseFaceParam = primitiveParams[i]; - int coarseFaceIdx = HdMeshUtil::DecodeFaceIndexFromCoarseFaceParam(coarseFaceParam); - triangulatedData[i] = inData[coarseFaceIdx]; - } - inData = std::move(triangulatedData); + remapGeomSubsetData<1>(inData, faceMap, subsetOffset, coarseFaceIndices); } } @@ -232,7 +201,7 @@ namespace Falcor */ bool getTexCoordPrimvar(const UsdGeomGprim& prim, UsdGeomPrimvar& primvar) { - std::vector primvars = prim.GetPrimvars(); + std::vector primvars = UsdGeomPrimvarsAPI(prim).GetPrimvars(); std::vector texCoordPrimvars; // Texture coordinate primvar naming is in general based on an implicit @@ -277,79 +246,67 @@ namespace Falcor UsdGeomPrimvarsAPI primvarApi(usdMesh); + UsdMeshData baseMesh; // Gather mesh metadata - TfToken scheme = getAttribute(usdMesh.GetSubdivisionSchemeAttr(), UsdGeomTokens->catmullClark); - TfToken orient = getAttribute(usdMesh.GetOrientationAttr(), UsdGeomTokens->rightHanded); + baseMesh.topology.scheme = getAttribute(usdMesh.GetSubdivisionSchemeAttr(), UsdGeomTokens->catmullClark); + baseMesh.topology.orient = getAttribute(usdMesh.GetOrientationAttr(), UsdGeomTokens->rightHanded); UsdAttribute pointsAttr = usdMesh.GetPointsAttr(); - if (!pointsAttr) + if (!pointsAttr || !pointsAttr.Get(&baseMesh.points, timeCode)) { logWarning("Mesh '{}' does not specify vertices. Ignoring.", meshName); return false; } - VtVec3fArray usdPoints; - pointsAttr.Get(&usdPoints, timeCode); - UsdAttribute faceCountsAttr = usdMesh.GetFaceVertexCountsAttr(); - if (!faceCountsAttr) + if (!faceCountsAttr || !faceCountsAttr.Get(&baseMesh.topology.faceCounts, timeCode)) { logWarning("Mesh '{}' has no faces. Ignoring.", meshName); return false; } - VtIntArray usdFaceCounts; - faceCountsAttr.Get(&usdFaceCounts, timeCode); UsdAttribute faceIndicesAttr = usdMesh.GetFaceVertexIndicesAttr(); - if (!faceIndicesAttr) + if (!faceIndicesAttr || !faceIndicesAttr.Get(&baseMesh.topology.faceIndices, timeCode)) { logWarning("Mesh '{}' does not specify face indices. Ignoring.", meshName); return false; } - VtIntArray usdFaceIndices; - faceIndicesAttr.Get(&usdFaceIndices, timeCode); - - VtIntArray usdHoleIndices; UsdAttribute holeIndicesAttr = usdMesh.GetHoleIndicesAttr(); if (holeIndicesAttr) { - holeIndicesAttr.Get(&usdHoleIndices, timeCode); + holeIndicesAttr.Get(&baseMesh.topology.holeIndices, timeCode); } - // Construct a HdMeshUtil to perform triangulation for us - HdMeshTopology topology(scheme, orient, usdFaceCounts, usdFaceIndices, usdHoleIndices); - - VtVec3iArray triangleIndices; - VtIntArray primitiveParams; - VtVec3fArray usdNormals; - VtVec2fArray usdUVs; - std::unique_ptr meshUtil = nullptr; - + // By default, don't perform any refinement. int level = 0; - if (scheme != UsdGeomTokens->none) + if (baseMesh.topology.scheme != UsdGeomTokens->none) { - level = 2; + // If a scheme is specified, default to the global refinement level. + level = ctx.defaultRefinementLevel; - // If specific value is given as an override, use it. + // Apply any overrides unless explicitly disabled via the refinementEnableOverride attribute. + bool applyOverride = true; UsdAttribute overrideAttrib = usdMesh.GetPrim().GetAttribute(TfToken("refinementEnableOverride")); if (overrideAttrib) { - bool overrideRefine = false; - overrideAttrib.Get(&overrideRefine); - if (overrideRefine) + overrideAttrib.Get(&applyOverride); + } + if (applyOverride) + { + // Refinement level override is enabled; use value specified, if any. + UsdAttribute refinementAttrib = usdMesh.GetPrim().GetAttribute(TfToken("refinementLevel")); + if (refinementAttrib) { - UsdAttribute refinementAttrib = usdMesh.GetPrim().GetAttribute(TfToken("refinementLevel")); - if (refinementAttrib) - { - refinementAttrib.Get(&level); - } + refinementAttrib.Get(&level); } } - // FIXME: Get override value, if any, from attribute store + // Get override from builder settings, if any. + level = ctx.builder.getSettings().getAttribute(meshName, "refinementLevel", level); } // Get texture coordinates, if any. + baseMesh.uvInterp = UsdGeomTokens->none; UsdGeomPrimvar uvPrimvar; TfToken uvInterp = UsdGeomTokens->none; if (getTexCoordPrimvar(usdMesh, uvPrimvar)) @@ -358,7 +315,8 @@ namespace Falcor geomOut.texCrdsInterp = convertInterpolation(uvInterp); // Get flattened version of the texture coordinates - uvPrimvar.ComputeFlattened(&usdUVs, timeCode); + uvPrimvar.ComputeFlattened(&baseMesh.uvs, timeCode); + baseMesh.uvInterp = uvPrimvar.GetInterpolation(); } // Get skinning data. @@ -372,74 +330,32 @@ namespace Falcor level = 0; } - bool refined = false; - if (level > 0) + // If both normals and primvars:normals are specified, the latter has precedence. + baseMesh.normalInterp = UsdGeomTokens->none; + UsdGeomPrimvar normalsPrimvar(primvarApi.GetPrimvar(TfToken("primvars:normals"))); + if (normalsPrimvar.HasValue()) { - UsdGeomPrimvar normalsPrimvar(primvarApi.GetPrimvar(TfToken("primvars:normals"))); - if (normalsPrimvar.HasValue() || usdMesh.GetNormalsAttr().IsAuthored()) - { - logWarning("Ignoring authored normals on subdivided mesh '{}'.", usdMesh.GetPath().GetString()); - } - - VtVec3fArray refinedPoints; - VtVec3fArray refinedNormals; - VtVec2fArray refinedUVs; - refined = refine(usdMesh, topology, level, usdPoints, usdUVs, uvInterp, topology, refinedPoints, refinedNormals, refinedUVs, meshUtil); - if (refined) - { - usdPoints = std::move(refinedPoints); - usdNormals = std::move(refinedNormals); - usdUVs = std::move(refinedUVs); - - meshUtil->ComputeTriangleIndices(&triangleIndices, &primitiveParams); - - // Normals are guaranteed to be per-vertex at this point. - geomOut.normalInterp = AttributeFrequency::Vertex; - geomOut.numReferencedPoints = usdPoints.size(); - } + // Get flattened version of the normals + normalsPrimvar.ComputeFlattened(&baseMesh.normals, timeCode); + baseMesh.normalInterp = normalsPrimvar.GetInterpolation(); } - - if (!refined) + else if (usdMesh.GetNormalsAttr().IsAuthored()) { - // Not a (valid) subdiv mesh. - meshUtil = std::make_unique(&topology, usdMesh.GetPath()); - - meshUtil->ComputeTriangleIndices(&triangleIndices, &primitiveParams); + // Normals specified via the attribute cannot be indexed, so there is no need to flatten them. + usdMesh.GetNormalsAttr().Get(&baseMesh.normals, timeCode); + baseMesh.normalInterp = usdMesh.GetNormalsInterpolation(); + } - Hd_VertexAdjacency adjacency; - adjacency.BuildAdjacencyTable(&topology); - geomOut.numReferencedPoints = adjacency.GetNumPoints(); + VtIntArray coarseFaceIndices; + UsdMeshData tessellatedMesh = tessellate(usdMesh, baseMesh, level, coarseFaceIndices); + if (tessellatedMesh.points.size() == 0) return false; - // If both normals and primvars:normals are specified, the latter has precedence. - UsdGeomPrimvar normalsPrimvar(primvarApi.GetPrimvar(TfToken("primvars:normals"))); - if (normalsPrimvar.HasValue()) - { - // Get flattened version of the normals - normalsPrimvar.ComputeFlattened(&usdNormals, timeCode); - geomOut.normalInterp = convertInterpolation(normalsPrimvar.GetInterpolation()); - } - else if (usdMesh.GetNormalsAttr().IsAuthored()) - { - // Normals specified via the attribute cannot be indexed, so there is no need to flatten them. - usdMesh.GetNormalsAttr().Get(&usdNormals, timeCode); - geomOut.normalInterp = convertInterpolation(usdMesh.GetNormalsInterpolation()); - } - else - { - // No authored normals. Generate faceted normals for this non-suddivided mesh. - usdNormals = Hd_FlatNormals::ComputeFlatNormals(&topology, usdPoints.cdata()); - geomOut.normalInterp = AttributeFrequency::Uniform; - } + VtVec3iArray triangleIndices = tessellatedMesh.topology.getTriangleIndices(); - // Generate triangulated normals, if necessary - convertTriangulatedFaceData(usdNormals, geomOut.normalInterp, HdType::HdTypeFloatVec3, triangleIndices.size(), primitiveParams, *meshUtil); - } - - if (usdUVs.size() > 0) - { - // Generate triangulated UVs, if necessary - convertTriangulatedFaceData(usdUVs, geomOut.texCrdsInterp, HdType::HdTypeFloatVec2, triangleIndices.size(), primitiveParams, *meshUtil); - } + // Normals are guaranteed to be per-vertex at this point. + geomOut.normalInterp = convertInterpolation(tessellatedMesh.normalInterp); + geomOut.numReferencedPoints = tessellatedMesh.points.size(); + geomOut.texCrdsInterp = convertInterpolation(tessellatedMesh.uvInterp); // Load skinning data // Mesh must have skinning data, and a corresponding skeleton. @@ -469,8 +385,13 @@ namespace Falcor jointIndicesPrimvar.Get(&jointIndices, UsdTimeCode::EarliestTime()); jointWeightsPrimvar.Get(&jointWeights, UsdTimeCode::EarliestTime()); + if (tessellatedMesh.points.size() != jointIndices.size() / elementSize) + { + logError("Base meash has {} points, tesss mesh has {} points, joint indices total {}, element size {}", baseMesh.points.size(), tessellatedMesh.points.size(), jointIndices.size(), elementSize); + } + // Should be a set of bone influences per point when interpolation is "vertex" - FALCOR_ASSERT(usdPoints.size() == jointIndices.size() / elementSize); + FALCOR_ASSERT(tessellatedMesh.points.size() == jointIndices.size() / elementSize); // Get the skeleton bound to this mesh and remap indices const auto& skelIdx = skelMapIt->second; @@ -478,9 +399,9 @@ namespace Falcor remapMeshJointIndices(ctx, subskel, usdMesh.GetPrim(), jointIndices); // Load per vertex bone influences - geomOut.jointIndices.resize(usdPoints.size(), uint4(NodeID::kInvalidID)); - geomOut.jointWeights.resize(usdPoints.size(), float4(0.0f)); - for (uint32_t i = 0; i < usdPoints.size(); i++) + geomOut.jointIndices.resize(tessellatedMesh.points.size(), uint4(NodeID::kInvalidID)); + geomOut.jointWeights.resize(tessellatedMesh.points.size(), float4(0.0f)); + for (uint32_t i = 0; i < tessellatedMesh.points.size(); i++) { uint4& indices = geomOut.jointIndices[i]; float4& weights = geomOut.jointWeights[i]; @@ -511,7 +432,6 @@ namespace Falcor } // We now have triangulated points and normals, as well as texture coordinates, if specified. - std::vector geomSubsets = UsdGeomSubset::GetAllGeomSubsets(usdMesh); uint32_t subsetCount = (uint32_t)geomSubsets.size(); @@ -532,7 +452,7 @@ namespace Falcor // one subset, either explicitly, or to the implied "catch-all" subset. // Construct mapping from face idx to subset idx - VtIntArray faceMap(topology.GetNumFaces(), -1); + VtIntArray faceMap(tessellatedMesh.topology.getNumFaces(), -1); for (uint32_t i = 0; i < subsetCount; ++i) { const UsdGeomSubset& geomSubset = geomSubsets[i]; @@ -554,7 +474,7 @@ namespace Falcor } // Unmapped faces are assigned to a separate catchall subset - VtIntArray unassignedIndices = UsdGeomSubset::GetUnassignedIndices(geomSubsets, topology.GetNumFaces()); + VtIntArray unassignedIndices = UsdGeomSubset::GetUnassignedIndices(geomSubsets, tessellatedMesh.topology.getNumFaces()); if (unassignedIndices.size() > 0) { for (int idx : unassignedIndices) @@ -567,9 +487,8 @@ namespace Falcor // Compute the number of triangle faces per subset VtIntArray trianglesPerSubset(subsetCount, 0); - for (int coarseFaceParam : primitiveParams) + for (int faceIdx : coarseFaceIndices) { - int faceIdx = HdMeshUtil::DecodeFaceIndexFromCoarseFaceParam(coarseFaceParam); int geomSubset = faceMap[faceIdx]; if (geomSubset >= 0) { @@ -585,13 +504,13 @@ namespace Falcor } // Reshuffle face indices into subset order - remapGeomSubsetData(triangleIndices, AttributeFrequency::Uniform, faceMap, subsetOffset, primitiveParams); + remapGeomSubsetData(triangleIndices, AttributeFrequency::Uniform, faceMap, subsetOffset, coarseFaceIndices); // Reshuffle normals - remapGeomSubsetData(usdNormals, geomOut.normalInterp, faceMap, subsetOffset, primitiveParams); + remapGeomSubsetData(tessellatedMesh.normals, geomOut.normalInterp, faceMap, subsetOffset, coarseFaceIndices); // Reshuffle texcoords - remapGeomSubsetData(usdUVs, geomOut.texCrdsInterp, faceMap, subsetOffset, primitiveParams); + remapGeomSubsetData(tessellatedMesh.uvs, geomOut.texCrdsInterp, faceMap, subsetOffset, coarseFaceIndices); // Count the number of non-degenerate subsets subsetCount = (uint32_t)std::count_if(trianglesPerSubset.begin(), trianglesPerSubset.end(), [](int val) {return val > 0; }); @@ -630,19 +549,19 @@ namespace Falcor // Copy points static_assert(sizeof(GfVec3f) == sizeof(float3)); - geomOut.points.resize(usdPoints.size()); - std::memcpy(geomOut.points.data(), usdPoints.data(), usdPoints.size() * sizeof(GfVec3f)); + geomOut.points.resize(tessellatedMesh.points.size()); + std::memcpy(geomOut.points.data(), tessellatedMesh.points.data(), tessellatedMesh.points.size() * sizeof(GfVec3f)); // Copy indices geomOut.triangulatedIndices.resize(3 * triangleIndices.size()); std::memcpy(geomOut.triangulatedIndices.data(), triangleIndices.data(), geomOut.triangulatedIndices.size() * sizeof(uint32_t)); // Copy normals - if (usdNormals.size() > 0) + if (tessellatedMesh.normals.size() > 0) { static_assert(sizeof(GfVec3f) == sizeof(float3)); - geomOut.normals.resize(usdNormals.size()); - std::memcpy(geomOut.normals.data(), usdNormals.cdata(), usdNormals.size() * sizeof(GfVec3f)); + geomOut.normals.resize(tessellatedMesh.normals.size()); + std::memcpy(geomOut.normals.data(), tessellatedMesh.normals.cdata(), tessellatedMesh.normals.size() * sizeof(GfVec3f)); } else { @@ -651,11 +570,11 @@ namespace Falcor } // Copy texture coordinates - if (usdUVs.size() > 0) + if (tessellatedMesh.uvs.size() > 0) { static_assert(sizeof(GfVec2f) == sizeof(float2)); - geomOut.texCrds.resize(usdUVs.size()); - std::memcpy(geomOut.texCrds.data(), usdUVs.cdata(), geomOut.texCrds.size() * sizeof(GfVec2f)); + geomOut.texCrds.resize(tessellatedMesh.uvs.size()); + std::memcpy(geomOut.texCrds.data(), tessellatedMesh.uvs.cdata(), tessellatedMesh.uvs.size() * sizeof(GfVec2f)); } return true; } @@ -670,38 +589,42 @@ namespace Falcor TfToken curveType = getAttribute(usdCurve.GetTypeAttr(), UsdGeomTokens->cubic); TfToken wrap = getAttribute(usdCurve.GetWrapAttr(), UsdGeomTokens->nonperiodic); + VtVec3fArray usdExtent; UsdAttribute extentAttr = usdCurve.GetExtentAttr(); - if (!extentAttr) + if (!extentAttr || !extentAttr.Get(&usdExtent, timeCode)) { logWarning("Curve '{}' has no AABB. Ignoring.", curveName); return false; } - VtVec3fArray usdExtent; - extentAttr.Get(&usdExtent, timeCode); UsdAttribute pointsAttr = usdCurve.GetPointsAttr(); - if (!pointsAttr) + if (!pointsAttr || !pointsAttr.Get(&usdPoints, timeCode)) { logWarning("Curve '{}' does not specify control points. Ignoring.", curveName); return false; } - pointsAttr.Get(&usdPoints, timeCode); UsdAttribute curveVertexCountsAttr = usdCurve.GetCurveVertexCountsAttr(); - if (!curveVertexCountsAttr) + if (!curveVertexCountsAttr || !curveVertexCountsAttr.Get(&usdCurveVertexCounts, timeCode)) { logWarning("Curve '{}' has no vertices. Ignoring.", curveName); return false; } - curveVertexCountsAttr.Get(&usdCurveVertexCounts, timeCode); UsdAttribute widthsAttr = usdCurve.GetWidthsAttr(); - if (!widthsAttr) + if (!widthsAttr || !widthsAttr.Get(&usdCurveWidths, timeCode)) { - logWarning("Curve '{}' has no width attribute. Ignoring.", curveName); - return false; + logDebug("Curve '{}' has no width attribute. Falling back to default width of 1.", curveName); + size_t vertexCount = std::accumulate(usdCurveVertexCounts.begin(), usdCurveVertexCounts.end(), 0); + usdCurveWidths.clear(); + usdCurveWidths.resize(vertexCount, + [](float* begin, float* end) + { + for (;begin != end; ++begin) + *begin = 1.f; + } + ); } - widthsAttr.Get(&usdCurveWidths, timeCode); // Get curve texture coordinates, if any. UsdGeomPrimvar uvPrimvar; @@ -746,7 +669,7 @@ namespace Falcor // Convert to linear swept sphere segments. CurveTessellation::SweptSphereResult result = CurveTessellation::convertToLinearSweptSphere(strandCount, reinterpret_cast(usdCurveVertexCounts.data()), (float3*)usdPoints.data(), usdCurveWidths.data(), pUsdUVs, 1, - subdivPerSegment, keepOneEveryXStrands, keepOneEveryXVerticesPerStrand, widthScale, rmcv::identity()); + subdivPerSegment, keepOneEveryXStrands, keepOneEveryXVerticesPerStrand, widthScale, float4x4::identity()); // Copy data. geomOut.id = curveName; @@ -900,7 +823,7 @@ namespace Falcor sbCurve.radius.pData = curveData.radius.data(); sbCurve.texCrds.pData = (curveData.texCrds.size() > 0 ? curveData.texCrds.data() : nullptr); - Material::SharedPtr pMaterial = ctx.resolveMaterial(curvePrim, curveData.material, sbCurve.name); + ref pMaterial = ctx.resolveMaterial(curvePrim, curveData.material, sbCurve.name); // It is possible the material was found in the scene and is assigned to other non-curve geometry. // We'll issue a warning if there is a material type mismatch. @@ -925,28 +848,6 @@ namespace Falcor return true; } - void verifyMeshGeomData(const MeshGeomData& geomData, const std::filesystem::path& path, const std::string& primName) - { - // Basic sanity checks - if (geomData.normals.size() > 0) - { - size_t expectedNormals = computeElementCount(geomData.normalInterp, geomData.triangulatedIndices.size() / 3, geomData.numReferencedPoints); - if (expectedNormals != geomData.normals.size()) - { - throw ImporterError(path, "Conversion error on USD prim '{}'. Expected {} normals, created {}.", primName, expectedNormals, geomData.normals.size()); - } - } - - if (geomData.texCrds.size() > 0) - { - size_t expectedTexcoords = computeElementCount(geomData.texCrdsInterp, geomData.triangulatedIndices.size() / 3, geomData.points.size()); - if (expectedTexcoords != geomData.texCrds.size()) - { - throw ImporterError(path, "Conversion error on USD prim '{}'. Expected {} texcoords, created {}.", primName, expectedTexcoords, geomData.texCrds.size()); - } - } - } - bool processMesh(Mesh& mesh, ImporterContext& ctx) { FALCOR_ASSERT(mesh.prim.IsA() || mesh.prim.IsA()); @@ -969,8 +870,6 @@ namespace Falcor return false; } - verifyMeshGeomData(geomData, ctx.path, primName); - // Finally, create a SceneBuilder::Mesh for each geomSubset in the MeshGeomData. mesh.processedMeshes.reserve(geomData.geomSubsets.size()); if (isTimeSampled(UsdGeomPointBased(mesh.prim))) @@ -1040,7 +939,7 @@ namespace Falcor if (!(mesh.processedMeshes[i].staticData.size() == indices.size())) { - throw ImporterError(ctx.path, "Keyframe {} for mesh '{}' does not match vertex count of original mesh.", sampleIdx, mesh.prim.GetName().GetString()); + throw ImporterError(ctx.stagePath, "Keyframe {} for mesh '{}' does not match vertex count of original mesh.", sampleIdx, mesh.prim.GetName().GetString()); } // Fill vertex data @@ -1125,8 +1024,6 @@ namespace Falcor return false; } - verifyMeshGeomData(geomData, ctx.path, primName); - // Finally, create a SceneBuilder::Mesh for the (single) geomSubset in the MeshGeomData. FALCOR_ASSERT(geomData.geomSubsets.size() == 1); @@ -1188,8 +1085,7 @@ namespace Falcor void addMeshesToSceneBuilder(ImporterContext& ctx, TimeReport& timeReport) { // Process collected mesh tasks. - NumericRange meshRange(0, ctx.meshTasks.size()); - std::for_each(std::execution::par, meshRange.begin(), meshRange.end(), + tbb::parallel_for(0, ctx.meshTasks.size(), [&](size_t i) { FALCOR_ASSERT(ctx.meshTasks[i].sampleIdx == 0); @@ -1224,8 +1120,7 @@ namespace Falcor } // Process time-sampled mesh keyframes - NumericRange keyframeRange(0, ctx.meshKeyframeTasks.size()); - std::for_each(std::execution::par, keyframeRange.begin(), keyframeRange.end(), + tbb::parallel_for(0, ctx.meshKeyframeTasks.size(), [&](size_t i) { auto& task = ctx.meshKeyframeTasks[i]; @@ -1255,7 +1150,7 @@ namespace Falcor void addInstancesToSceneBuilder(ImporterContext& ctx, TimeReport& timeReport) { // Helper function to add all submeshes associated with the given UsdGeomMesh to SceneBuilder - auto addSubmeshes = [&](const UsdPrim& meshPrim, const std::string& name, const rmcv::mat4& xform, const rmcv::mat4& bindXform, NodeID parentId) + auto addSubmeshes = [&](const UsdPrim& meshPrim, const std::string& name, const float4x4& xform, const float4x4& bindXform, NodeID parentId) { auto subNodeId = ctx.builder.addNode(makeNode(name, xform, bindXform, parentId)); FALCOR_ASSERT(meshPrim.IsA()); @@ -1269,15 +1164,15 @@ namespace Falcor // Add individual instances of prepocessed geometry to scene builder. Each mesh may contain more than one submesh, each corresponding to a UsdGeomSubset. for (const auto& instance : ctx.geomInstances) { - addSubmeshes(instance.prim, instance.name, rmcv::mat4(1.f), instance.bindTransform, instance.parentID); + addSubmeshes(instance.prim, instance.name, float4x4::identity(), instance.bindTransform, instance.parentID); } // Add instances of prototypes to scene builder. Because SceneBuilder only supports instanced meshes, and not // general instancing, we effectively replicate each Prototype's subgraph. We could in theory collapse the subgraph // if all of the transformations are static, but time-sampled transformations require us to use a more general approach. - for (const auto& inst : ctx.prototypeInstances) + for (const auto& instance : ctx.prototypeInstances) { - std::vector> protoInstanceStack = { std::make_pair(inst, inst.parentID) }; + std::vector> protoInstanceStack = { std::make_pair(instance, instance.parentID) }; while (!protoInstanceStack.empty()) { PrototypeInstance protoInstance = protoInstanceStack.back().first; @@ -1285,13 +1180,13 @@ namespace Falcor protoInstanceStack.pop_back(); // Add root node for this prototype instance, parented appropriately. - NodeID rootNodeID = ctx.builder.addNode(makeNode(protoInstance.name, protoInstance.xform, rmcv::mat4(1.f), parentID)); + NodeID rootNodeID = ctx.builder.addNode(makeNode(protoInstance.name, protoInstance.xform, float4x4::identity(), parentID)); // If there are keyframes, create an animation from them targeting the root node we just created. // This could be more cleanly addressed if a Falcor::Animation could target more than one node. if (protoInstance.keyframes.size() > 0) { - Animation::SharedPtr pAnimation = Animation::create(protoInstance.name, rootNodeID, protoInstance.keyframes.back().time); + ref pAnimation = Animation::create(protoInstance.name, rootNodeID, protoInstance.keyframes.back().time); for (const auto& keyframe : protoInstance.keyframes) { pAnimation->addKeyframe(keyframe); @@ -1328,7 +1223,7 @@ namespace Falcor // if the prototype is instantiated multiple times. std::string animationName = protoGeom.nodes[animation.targetNodeID.get()].name; NodeID targetNodeID{ animation.targetNodeID.get() + protoRootID.get() }; - Animation::SharedPtr pAnimation = Animation::create(animationName, targetNodeID, animation.keyframes.back().time); + ref pAnimation = Animation::create(animationName, targetNodeID, animation.keyframes.back().time); for (const auto& keyframe : animation.keyframes) { pAnimation->addKeyframe(keyframe); @@ -1342,11 +1237,11 @@ namespace Falcor std::string instName = protoInstance.name + "/" + inst.name; if (inst.prim.IsA()) { - addSubmeshes(inst.prim, instName, inst.xform, rmcv::mat4(1.f), NodeID{ inst.parentID.get() + protoRootID.get() }); + addSubmeshes(inst.prim, instName, inst.xform, float4x4::identity(), NodeID{ inst.parentID.get() + protoRootID.get() }); } else if (inst.prim.IsA()) { - auto nodeId = ctx.builder.addNode(makeNode(instName, inst.xform, rmcv::mat4(1.f), NodeID{ inst.parentID.get() + protoRootID.get() })); + auto nodeId = ctx.builder.addNode(makeNode(instName, inst.xform, float4x4::identity(), NodeID{ inst.parentID.get() + protoRootID.get() })); const auto& curve = ctx.getCurve(inst.prim); if (curve.tessellationMode == CurveTessellationMode::LinearSweptSphere) @@ -1379,8 +1274,7 @@ namespace Falcor void addCurvesToSceneBuilder(ImporterContext& ctx, TimeReport& timeReport) { // Process collected curves. - NumericRange range(0, ctx.curves.size()); - std::for_each(std::execution::par, range.begin(), range.end(), + tbb::parallel_for(0, ctx.curves.size(), [&](size_t i) { processCurve(ctx.curves[i], ctx); } ); @@ -1408,7 +1302,7 @@ namespace Falcor // Add instances to scene builder. for (const auto& instance : ctx.curveInstances) { - auto nodeId = ctx.builder.addNode(makeNode(instance.name, instance.xform, rmcv::mat4(1.f), instance.parentID)); + auto nodeId = ctx.builder.addNode(makeNode(instance.name, instance.xform, float4x4::identity(), instance.parentID)); const auto& curve = ctx.getCurve(instance.prim); if (curve.tessellationMode == CurveTessellationMode::LinearSweptSphere) @@ -1424,18 +1318,19 @@ namespace Falcor timeReport.measure("Create curve instances"); } - float3 getLightIntensity(const UsdLuxLight& light) + template + float3 getLightIntensity(const UsdLuxLightType& light, const UsdPrim& prim) { - float exposure = getAttribute(light.GetExposureAttr(), 0.f); - float intens = getAttribute(light.GetIntensityAttr(), 1.f); + float exposure = getAuthoredAttribute(light.GetExposureAttr(), prim.GetAttribute(TfToken("exposure")), 0.f); + float intens = getAuthoredAttribute(light.GetIntensityAttr(), prim.GetAttribute(TfToken("intensity")), 1.f); GfVec3f blackbodyRGB(1.f, 1.f, 1.f); - if (getAttribute(light.GetEnableColorTemperatureAttr(), false)) + if (getAuthoredAttribute(light.GetEnableColorTemperatureAttr(), prim.GetAttribute(TfToken("enableColorTemperature")), false)) { - float temperature = getAttribute(light.GetColorTemperatureAttr(), 6500.f); + float temperature = getAuthoredAttribute(light.GetColorTemperatureAttr(), prim.GetAttribute(TfToken("colorTemperature")), 6500.f); blackbodyRGB = UsdLuxBlackbodyTemperatureAsRgb(temperature); } - GfVec3f color = getAttribute(light.GetColorAttr(), GfVec3f(1.f, 1.f, 1.f)); - return std::pow(2.f, exposure) * intens * toGlm(blackbodyRGB) * toGlm(color); + GfVec3f color = getAuthoredAttribute(light.GetColorAttr(), prim.GetAttribute(TfToken("color")), GfVec3f(1.f, 1.f, 1.f)); + return std::exp2(exposure) * intens * toFalcor(blackbodyRGB) * toFalcor(color); } Falcor::Animation::Keyframe createKeyframe(const UsdGeomXformCommonAPI& xformAPI, double timeCode, double timeCodesPerSecond) @@ -1449,8 +1344,8 @@ namespace Falcor UsdGeomXformCommonAPI::RotationOrder rotOrder; xformAPI.GetXformVectors(&trans, &rot, &scale, &pivot, &rotOrder, timeCode); - keyframe.translation = toGlm(trans); - keyframe.scaling = toGlm(scale); + keyframe.translation = toFalcor(trans); + keyframe.scaling = toFalcor(scale); keyframe.time = timeCode / timeCodesPerSecond; if (pivot != GfVec3f(0.f, 0.f, 0.f)) @@ -1458,29 +1353,29 @@ namespace Falcor logWarning("Ignoring non-zero pivot extracted from '{}'.", xformAPI.GetPath().GetString()); } - glm::quat qx = glm::angleAxis(glm::radians(rot[0]), glm::vec3(1.f, 0.f, 0.f)); - glm::quat qy = glm::angleAxis(glm::radians(rot[1]), glm::vec3(0.f, 1.f, 0.f)); - glm::quat qz = glm::angleAxis(glm::radians(rot[2]), glm::vec3(0.f, 0.f, 1.f)); + quatf qx = math::quatFromAngleAxis(math::radians(rot[0]), float3(1.f, 0.f, 0.f)); + quatf qy = math::quatFromAngleAxis(math::radians(rot[1]), float3(0.f, 1.f, 0.f)); + quatf qz = math::quatFromAngleAxis(math::radians(rot[2]), float3(0.f, 0.f, 1.f)); switch (rotOrder) { case UsdGeomXformCommonAPI::RotationOrder::RotationOrderXYZ: - keyframe.rotation = qz * qy * qx; + keyframe.rotation = mul(mul(qz, qy), qx); break; case UsdGeomXformCommonAPI::RotationOrder::RotationOrderXZY: - keyframe.rotation = qy * qz * qx; + keyframe.rotation = mul(mul(qy, qz), qx); break; case UsdGeomXformCommonAPI::RotationOrder::RotationOrderYXZ: - keyframe.rotation = qz * qx * qy; + keyframe.rotation = mul(mul(qz, qx), qy); break; case UsdGeomXformCommonAPI::RotationOrder::RotationOrderYZX: - keyframe.rotation = qx * qz * qy; + keyframe.rotation = mul(mul(qx, qz), qy); break; case UsdGeomXformCommonAPI::RotationOrder::RotationOrderZXY: - keyframe.rotation = qy * qx * qz; + keyframe.rotation = mul(mul(qy, qx), qz); break; case UsdGeomXformCommonAPI::RotationOrder::RotationOrderZYX: - keyframe.rotation = qx * qy * qz; + keyframe.rotation = mul(mul(qx, qy), qz); break; default: FALCOR_UNREACHABLE(); @@ -1518,49 +1413,21 @@ namespace Falcor void ImporterContext::createEnvMap(const UsdPrim& lightPrim) { - const UsdAttribute texAttribute = UsdLuxDomeLight(lightPrim).GetTextureFileAttr(); - if (!texAttribute) - { - logWarning("No texture attribute specified for dome light '{}'. Ignoring.", lightPrim.GetPath().GetString()); - return; - } - - SdfAssetPath path; - texAttribute.Get(&path, UsdTimeCode::EarliestTime()); - UsdLuxDomeLight domeLight(lightPrim); - float exposure = 0.f; - const UsdAttribute exposureAttr = domeLight.GetExposureAttr(); - if (exposureAttr) - { - exposureAttr.Get(&exposure, UsdTimeCode::EarliestTime()); - } - - float intens = 1.f; - const UsdAttribute intensAttr = domeLight.GetIntensityAttr(); - if (intensAttr) - { - intensAttr.Get(&intens, UsdTimeCode::EarliestTime()); - } - - float3 color = { 1.f, 1.f, 1.f }; - const UsdAttribute colorAttr = domeLight.GetColorAttr(); - if (colorAttr) - { - GfVec3f vec; - colorAttr.Get(&vec, UsdTimeCode::EarliestTime()); - color = float3(vec[0], vec[1], vec[2]); - } + SdfAssetPath path = getAuthoredAttribute(domeLight.GetTextureFileAttr(), lightPrim.GetAttribute(TfToken("texture:file")), SdfAssetPath()); std::string envMapPath(path.GetResolvedPath()); - if (envMapPath.empty()) { logError("Failed to resolve environment map path '{}' for light '{}'.", path.GetAssetPath(), lightPrim.GetPath().GetString()); return; } - EnvMap::SharedPtr pEnvMap = EnvMap::createFromFile(builder.getDevice(), envMapPath); + float exposure = getAuthoredAttribute(domeLight.GetExposureAttr(), lightPrim.GetAttribute(TfToken("exposure")), 0.f); + float intensity = getAuthoredAttribute(domeLight.GetIntensityAttr(), lightPrim.GetAttribute(TfToken("intensity")), 1.f); + GfVec3f color = getAuthoredAttribute(domeLight.GetColorAttr(), lightPrim.GetAttribute(TfToken("color")), GfVec3f(1.f, 1.f, 1.f)); + + ref pEnvMap = EnvMap::createFromFile(builder.getDevice(), envMapPath); if (pEnvMap == nullptr) { @@ -1568,23 +1435,23 @@ namespace Falcor return; } - pEnvMap->setIntensity(std::pow(2.f, exposure) * intens); - pEnvMap->setTint(color); + pEnvMap->setIntensity(std::pow(2.f, exposure) * intensity); + pEnvMap->setTint(toFalcor(color)); // The local-to-world transform includes a 90-degree rotation about X to account for the difference // between USD dome light orientation (up=+Z) and Falcor env map orientation (up=+Y), as well as // a 90-degree around Y in world space to account for differences if longitudinal mapping. // FIXME: This assumes a static local to world xform. Should support rigid body animation - rmcv::mat4 xform = rmcv::eulerAngleY(glm::radians(90.f)) * getLocalToWorldXform(UsdGeomXformable(lightPrim)) * rmcv::eulerAngleX(glm::radians(90.f)); + float4x4 xform = mul(mul(math::matrixFromRotationY(math::radians(90.f)), getLocalToWorldXform(UsdGeomXformable(lightPrim))), math::matrixFromRotationX(math::radians(90.f))); float3 rotation; // Extract rotation from the computed transform - rmcv::extractEulerAngleXYZ(xform, rotation.x, rotation.y, rotation.z); - pEnvMap->setRotation(glm::degrees(rotation)); + math::extractEulerAngleXYZ(xform, rotation.x, rotation.y, rotation.z); + pEnvMap->setRotation(math::degrees(rotation)); builder.setEnvMap(pEnvMap); } - void ImporterContext::addLight(const UsdPrim& lightPrim, Light::SharedPtr pLight, NodeID parentId) + void ImporterContext::addLight(const UsdPrim& lightPrim, ref pLight, NodeID parentId) { NodeID nodeId = builder.addNode(makeNode(lightPrim.GetName(), parentId)); pLight->setNodeID(nodeId); @@ -1596,10 +1463,10 @@ namespace Falcor { UsdLuxDistantLight distantLight(lightPrim); - float3 intensity = getLightIntensity(distantLight); - float angle = getAttribute(distantLight.GetAngleAttr(), 0.f); + float3 intensity = getLightIntensity(distantLight, lightPrim); + float angle = getAuthoredAttribute(distantLight.GetAngleAttr(), lightPrim.GetAttribute(TfToken("angle")), 0.f); - DistantLight::SharedPtr pLight = DistantLight::create(lightPrim.GetName()); + ref pLight = DistantLight::create(lightPrim.GetName()); pLight->setIntensity(intensity); pLight->setWorldDirection(float3(0.f, 0.f, -1.f)); pLight->setAngle(float(0.5 * angle * M_PI / 180)); @@ -1611,12 +1478,12 @@ namespace Falcor { UsdLuxRectLight rectLight(lightPrim); - float3 intensity = getLightIntensity(rectLight); + float3 intensity = getLightIntensity(rectLight, lightPrim); - float width = getAttribute(rectLight.GetWidthAttr(), 1.f); - float height = getAttribute(rectLight.GetHeightAttr(), 1.f); + float width = getAuthoredAttribute(rectLight.GetWidthAttr(), lightPrim.GetAttribute(TfToken("width")), 1.f); + float height = getAuthoredAttribute(rectLight.GetHeightAttr(), lightPrim.GetAttribute(TfToken("height")), 1.f); - AnalyticAreaLight::SharedPtr pLight = RectLight::create(lightPrim.GetName()); + ref pLight = RectLight::create(lightPrim.GetName()); pLight->setIntensity(intensity); // Scale width and height to account for the fact that Falcor's 'unit' rect light extends // from (-1,-1) to (1,1), vs. USD's (-0.5,-0.5) to (0.5,0.5). @@ -1630,11 +1497,11 @@ namespace Falcor { UsdLuxSphereLight sphereLight(lightPrim); - float3 intensity = getLightIntensity(sphereLight); + float3 intensity = getLightIntensity(sphereLight, lightPrim); - float radius = getAttribute(sphereLight.GetRadiusAttr(), 0.5f); + float radius = getAuthoredAttribute(sphereLight.GetRadiusAttr(), lightPrim.GetAttribute(TfToken("radius")), 0.5f); - AnalyticAreaLight::SharedPtr pLight = SphereLight::create(lightPrim.GetName()); + ref pLight = SphereLight::create(lightPrim.GetName()); pLight->setIntensity(intensity); pLight->setScaling(float3(radius, radius, radius)); @@ -1645,11 +1512,11 @@ namespace Falcor { UsdLuxDiskLight diskLight(lightPrim); - float3 intensity = getLightIntensity(diskLight); + float3 intensity = getLightIntensity(diskLight, lightPrim); - float radius = getAttribute(diskLight.GetRadiusAttr(), 0.5f); + float radius = getAuthoredAttribute(diskLight.GetRadiusAttr(), lightPrim.GetAttribute(TfToken("radius")), 0.5f); - AnalyticAreaLight::SharedPtr pLight = DiscLight::create(lightPrim.GetName()); + ref pLight = DiscLight::create(lightPrim.GetName()); pLight->setIntensity(intensity); // Flip Z to emit along -Z axis, flip X to preserve handedness. Equivalent to a scale by (radius, radius, 1), followed by a 180 degree rotation around Y. @@ -1663,9 +1530,9 @@ namespace Falcor { UsdLuxDiskLight diskLight(lightPrim); - float3 intensity = getLightIntensity(diskLight); + float3 intensity = getLightIntensity(diskLight, lightPrim); - float radius = getAttribute(diskLight.GetRadiusAttr(), 0.5f); + float radius = getAuthoredAttribute(diskLight.GetRadiusAttr(), lightPrim.GetAttribute(TfToken("radius")), 0.5f); // Replace disc analytic light with mesh and bind IES material. VtArray points; @@ -1718,7 +1585,7 @@ namespace Falcor localDict[materialName] = materialName; addMesh(mesh.GetPrim()); - addGeomInstance(lightPrim.GetName(), mesh.GetPrim(), float4x4(), float4x4()); + addGeomInstance(lightPrim.GetName(), mesh.GetPrim(), float4x4::identity(), float4x4::identity()); } bool ImporterContext::createCamera(const UsdPrim& cameraPrim) @@ -1726,18 +1593,18 @@ namespace Falcor const UsdGeomCamera camera(cameraPrim); GfCamera cam = camera.GetCamera(0.f); - Camera::SharedPtr pCamera = Camera::create(cameraPrim.GetName()); + ref pCamera = Camera::create(cameraPrim.GetName()); NodeID nodeId = builder.addNode(makeNode(cameraPrim.GetName(), nodeStack.back())); pCamera->setNodeID(nodeId); pCamera->setHasAnimation(builder.isNodeAnimated(nodeId)); float focalDistance = std::max(1.f, cam.GetFocusDistance()); - rmcv::mat4 view(toRMCV(cam.GetTransform())); + float4x4 view(toFalcor(cam.GetTransform())); - float3 pos = view * float4(0.f, 0.f, 0.f, 1.f); - float3 target = view * float4(0.f, 0.f, -focalDistance, 1.f); - float3 up = view * float4(0.f, 1.f, 0.f, 0.f); + float3 pos = transformPoint(view, float3(0.f)); + float3 target = transformPoint(view, float3(0.f, 0.f, -focalDistance)); + float3 up = transformVector(view, float3(0.f, 1.f, 0.f)); pCamera->setPosition(pos); pCamera->setTarget(target); @@ -1829,11 +1696,11 @@ namespace Falcor for (uint32_t j = 0; j < matrices.size(); ++j) { const auto& matrix = matrices[j]; - rmcv::mat4 glmMat = toRMCV(matrix); + float4x4 glmMat = toFalcor(matrix); Animation::Keyframe keyframe; float3 skew; float4 persp; - rmcv::decompose(glmMat, keyframe.scaling, keyframe.rotation, keyframe.translation, skew, persp); + math::decompose(glmMat, keyframe.scaling, keyframe.rotation, keyframe.translation, skew, persp); keyframe.time = time / timeCodesPerSecond; keyframes[j].push_back(keyframe); } @@ -1888,8 +1755,7 @@ namespace Falcor break; } - NumericRange range(0, indexData.size()); - std::for_each(std::execution::par, range.begin(), range.end(), + tbb::parallel_for(0, indexData.size(), [&](size_t j) { isSameTopology |= (indexData[j] == refIndexData[j]); @@ -1899,7 +1765,7 @@ namespace Falcor } if (!isSameTopology) { - throw ImporterError(path, "The topology/indexing of curves changes across keyframes. Only dynamic vertex positions are supported."); + throw ImporterError(stagePath, "The topology/indexing of curves changes across keyframes. Only dynamic vertex positions are supported."); } cachedCurve.indexData.resize(refIndexData.size()); @@ -1924,8 +1790,8 @@ namespace Falcor cachedCurves.push_back(cachedCurve); } - ImporterContext::ImporterContext(const std::filesystem::path& path, UsdStageRefPtr pStage, SceneBuilder& builder, const Dictionary& dict, TimeReport& timeReport, bool useInstanceProxies /*= false*/) - : path(path) + ImporterContext::ImporterContext(const std::filesystem::path& stagePath, UsdStageRefPtr pStage, SceneBuilder& builder, const pybind11::dict& dict, TimeReport& timeReport, bool useInstanceProxies /*= false*/) + : stagePath(stagePath) , pStage(pStage) , dict(dict) , timeReport(timeReport) @@ -1936,9 +1802,9 @@ namespace Falcor } - Material::SharedPtr ImporterContext::resolveMaterial(const UsdPrim& prim, const UsdShadeMaterial& material, const std::string& primName) + ref ImporterContext::resolveMaterial(const UsdPrim& prim, const UsdShadeMaterial& material, const std::string& primName) { - Material::SharedPtr pMaterial; + ref pMaterial; if (material) { @@ -1953,9 +1819,9 @@ namespace Falcor return pMaterial; } - Material::SharedPtr ImporterContext::getDefaultMaterial(const UsdPrim& prim) + ref ImporterContext::getDefaultMaterial(const UsdPrim& prim) { - Material::SharedPtr pMaterial; + ref pMaterial; float3 defaultColor; if (prim.IsA()) @@ -1968,7 +1834,7 @@ namespace Falcor } else { - throw ImporterError(path, "Conversion error on USD prim '{}'. Cannot create default material for non-mesh, non-curve prim.", prim.GetPath().GetString()); + throw ImporterError(stagePath, "Conversion error on USD prim '{}'. Cannot create default material for non-mesh, non-curve prim.", prim.GetPath().GetString()); } // If there is a displayColor attribute associated with this prim, use it as the default color. @@ -2003,7 +1869,7 @@ namespace Falcor { return it->second; } - StandardMaterial::SharedPtr pDefaultMaterial = StandardMaterial::create(builder.getDevice(), "default-mesh-" + std::to_string(defaultMaterialMap.size())); + ref pDefaultMaterial = StandardMaterial::create(builder.getDevice(), "default-mesh-" + std::to_string(defaultMaterialMap.size())); pDefaultMaterial->setBaseColor(float4(defaultColor, 1.f)); pDefaultMaterial->setRoughness(0.3f); pDefaultMaterial->setMetallic(0.f); @@ -2019,7 +1885,7 @@ namespace Falcor { return it->second; } - HairMaterial::SharedPtr pDefaultCurveMaterial = HairMaterial::create(builder.getDevice(), "default-curve-" + std::to_string(defaultCurveMaterialMap.size())); + ref pDefaultCurveMaterial = HairMaterial::create(builder.getDevice(), "default-curve-" + std::to_string(defaultCurveMaterialMap.size())); pDefaultCurveMaterial->setBaseColor(float4(defaultColor, 1.f)); pDefaultCurveMaterial->setSpecularParams(float4(kDefaultCurveLongitudinalRoughness, kDefaultCurveAzimuthalRoughness, kDefaultScaleAngleDegree, 0.f)); pDefaultCurveMaterial->setIndexOfRefraction(kDefaultCurveIOR); @@ -2113,7 +1979,7 @@ namespace Falcor if (prim.IsInstance()) { - const UsdPrim protoPrim(prim.GetMaster()); + const UsdPrim protoPrim(prim.GetPrototype()); if (!protoPrim.IsValid()) { @@ -2136,7 +2002,7 @@ namespace Falcor { logDebug("Adding mesh '{}'.", primName); addMesh(prim); - proto.addGeomInstance(primName, prim, rmcv::mat4(1.f), rmcv::mat4(1.f)); + proto.addGeomInstance(primName, prim, float4x4::identity(), float4x4::identity()); } else if (prim.IsA() || prim.IsA() || @@ -2148,9 +2014,9 @@ namespace Falcor { logDebug("Adding BasisCurves '{}'.", primName); addCurve(prim); - proto.addGeomInstance(primName, prim, rmcv::mat4(1.f), rmcv::mat4(1.f)); + proto.addGeomInstance(primName, prim, float4x4::identity(), float4x4::identity()); } - else if (prim.IsA()) + else if (prim.IsA() || prim.IsA()) { logWarning("Ignoring light '{}' encountered in prototype prim.", primName); it.PruneChildren(); @@ -2199,7 +2065,7 @@ namespace Falcor return prototypeGeomMap.find(protoPrim) != prototypeGeomMap.end(); } - void ImporterContext::addGeomInstance(const std::string& name, const UsdPrim& prim, const rmcv::mat4& xform, const rmcv::mat4& bindXform) + void ImporterContext::addGeomInstance(const std::string& name, const UsdPrim& prim, const float4x4& xform, const float4x4& bindXform) { geomInstances.push_back(GeomInstance{ name, prim, xform, bindXform, nodeStack.back()}); } @@ -2221,8 +2087,9 @@ namespace Falcor logError("Error occurred gathering prototypes for point instancer '{}'.", primName); } + VtIntArray protoIndices; UsdAttribute protoIndicesAttr(instancer.GetProtoIndicesAttr()); - if (!protoIndicesAttr.IsDefined()) + if (!protoIndicesAttr.IsDefined() || !protoIndicesAttr.Get(&protoIndices, UsdTimeCode::EarliestTime())) { logError("Point instancer '{}' has no prototype indices. Ignoring prim.", primName); return; @@ -2261,9 +2128,6 @@ namespace Falcor } } - VtIntArray protoIndices; - protoIndicesAttr.Get(&protoIndices, UsdTimeCode::EarliestTime()); - std::vector> keyframes; VtMatrix4dArray instXforms; @@ -2303,7 +2167,7 @@ namespace Falcor } else { - protoInst.xform = toRMCV(instXforms[i]); + protoInst.xform = toFalcor(instXforms[i]); } if (proto) @@ -2352,11 +2216,13 @@ namespace Falcor void ImporterContext::createSkeleton(const UsdPrim& prim) { + Usd_PrimFlagsPredicate predicate; + UsdSkelRoot skelRoot(prim); - skelCache.Populate(skelRoot); + skelCache.Populate(skelRoot, predicate); std::vector bindings; - skelCache.ComputeSkelBindings(skelRoot, &bindings); + skelCache.ComputeSkelBindings(skelRoot, &bindings, predicate); if (bindings.empty()) { @@ -2390,8 +2256,8 @@ namespace Falcor // Record helpful data about each mesh affected by this Skeleton for (const auto& skinningQuery : binding.GetSkinningTargets()) { - UsdPrim prim = skinningQuery.GetPrim(); - if (prim.IsA()) + UsdPrim skinningPrim = skinningQuery.GetPrim(); + if (skinningPrim.IsA()) { // A mesh's joint indices may only refer to a subset of the skeleton. // Use USD's JointMapper to reliably determine if we need to remap, @@ -2415,19 +2281,19 @@ namespace Falcor { meshToSkeletonJoints[i] = skelJointToIndex.at(meshJoints[i]); } - subskeleton.skinnedMeshes.emplace(prim, std::move(meshToSkeletonJoints)); + subskeleton.skinnedMeshes.emplace(skinningPrim, std::move(meshToSkeletonJoints)); } else { // Joints do not need remapping - subskeleton.skinnedMeshes.emplace(prim, std::vector()); + subskeleton.skinnedMeshes.emplace(skinningPrim, std::vector()); } - meshSkelMap.emplace(prim, std::make_pair(skeletons.size() - 1, skelData.subskeletons.size() - 1)); + meshSkelMap.emplace(skinningPrim, std::make_pair(skeletons.size() - 1, skelData.subskeletons.size() - 1)); } else { - logWarning("Cannot apply skinning to non-mesh prim '{}' of type {}.", prim.GetName().GetString(), prim.GetTypeName().GetString()); + logWarning("Cannot apply skinning to non-mesh prim '{}' of type {}.", skinningPrim.GetName().GetString(), skinningPrim.GetTypeName().GetString()); } } @@ -2444,8 +2310,8 @@ namespace Falcor { SceneBuilder::Node n; n.name = joints[i].GetString(); - n.transform = toRMCV(restTransform[i]); - n.localToBindPose = rmcv::inverse(toRMCV(bindTransform[i])); + n.transform = toFalcor(restTransform[i]); + n.localToBindPose = inverse(toFalcor(bindTransform[i])); n.parent = skelTopo.IsRoot(i) ? NodeID::Invalid() : NodeID{ skelTopo.GetParent(i) }; subskeleton.bones.push_back(n); } @@ -2473,9 +2339,9 @@ namespace Falcor { Animation::Keyframe keyframe; keyframe.time = t / timeCodesPerSecond; - keyframe.translation = toGlm(trans[i]); - keyframe.rotation = glm::quat(rot[i].GetReal(), toGlm(rot[i].GetImaginary())); - keyframe.scaling = toGlm(scales[i]); + keyframe.translation = toFalcor(trans[i]); + keyframe.rotation = quatf(toFalcor(rot[i].GetImaginary()), rot[i].GetReal()); + keyframe.scaling = toFalcor(scales[i]); subskeleton.animations[i]->addKeyframe(keyframe); } @@ -2483,10 +2349,10 @@ namespace Falcor } } - rmcv::mat4 ImporterContext::getLocalToWorldXform(const UsdGeomXformable& prim, UsdTimeCode timeCode) + float4x4 ImporterContext::getLocalToWorldXform(const UsdGeomXformable& prim, UsdTimeCode timeCode) { - rmcv::mat4 localToWorld = toRMCV(prim.ComputeLocalToWorldTransform(timeCode)); - return rootXform * localToWorld; + float4x4 localToWorld = toFalcor(prim.ComputeLocalToWorldTransform(timeCode)); + return mul(rootXform, localToWorld); } void ImporterContext::pushNode(const UsdGeomXformable& prim) @@ -2500,7 +2366,7 @@ namespace Falcor { // The node stack should at least contain the root node. FALCOR_ASSERT(nodeStack.size() > 0); - rmcv::mat4 localTransform; + float4x4 localTransform; bool resets = getLocalTransform(prim, localTransform); SceneBuilder::Node node; node.name = prim.GetPath().GetString(); @@ -2511,7 +2377,7 @@ namespace Falcor nodeStack.push_back(nodeID); } - rmcv::mat4 ImporterContext::getGeomBindTransform(const UsdPrim& usdPrim) const + float4x4 ImporterContext::getGeomBindTransform(const UsdPrim& usdPrim) const { GfMatrix4d bindXform(1.0); UsdGeomPrimvar geomBindTransform(UsdGeomPrimvarsAPI(usdPrim).GetPrimvar(UsdSkelTokens->primvarsSkelGeomBindTransform)); @@ -2520,7 +2386,7 @@ namespace Falcor geomBindTransform.Get(&bindXform, UsdTimeCode::EarliestTime()); } - return toRMCV(bindXform); + return toFalcor(bindXform); } void ImporterContext::pushNodeStack(NodeID rootNode) @@ -2536,12 +2402,12 @@ namespace Falcor size_t origDepth = nodeStackStartDepth.back(); if (origDepth != nodeStack.size()) { - throw ImporterError(path, "USDImporter transform stack error detected."); + throw ImporterError(stagePath, "USDImporter transform stack error detected."); } nodeStackStartDepth.pop_back(); } - void ImporterContext::setRootXform(const rmcv::mat4& xform) + void ImporterContext::setRootXform(const float4x4& xform) { if (rootXformNodeId != NodeID::Invalid()) { @@ -2563,9 +2429,9 @@ namespace Falcor pushNodeStack(rootXformNodeId); } - void ImporterContext::addCurveInstance(const std::string& name, const UsdPrim& curvePrim, const rmcv::mat4& xform, NodeID parentID) + void ImporterContext::addCurveInstance(const std::string& name, const UsdPrim& curvePrim, const float4x4& xform, NodeID parentID) { - curveInstances.push_back(GeomInstance{ name, curvePrim, xform, rmcv::mat4(1.f), parentID }); + curveInstances.push_back(GeomInstance{ name, curvePrim, xform, float4x4::identity(), parentID }); } void ImporterContext::finalize() diff --git a/Source/plugins/importers/USDImporter/ImporterContext.h b/Source/plugins/importers/USDImporter/ImporterContext.h index b626cf8f1..6ac96f6cd 100644 --- a/Source/plugins/importers/USDImporter/ImporterContext.h +++ b/Source/plugins/importers/USDImporter/ImporterContext.h @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -37,7 +37,6 @@ #include "Utils/Math/Vector.h" #include "Utils/Math/Matrix.h" #include "Utils/Timing/TimeReport.h" -#include "Utils/Scripting/Dictionary.h" BEGIN_DISABLE_USD_WARNINGS #include @@ -48,9 +47,8 @@ BEGIN_DISABLE_USD_WARNINGS #include END_DISABLE_USD_WARNINGS -#include +#include -#include #include #include #include @@ -73,8 +71,8 @@ namespace Falcor { std::string name; ///< Instance name. UsdPrim prim; ///< Reference to mesh prim. - rmcv::mat4 xform; ///< Instance world transform - rmcv::mat4 bindTransform; ///< If mesh is skinned, its bind-time transform. + float4x4 xform; ///< Instance world transform + float4x4 bindTransform; ///< If mesh is skinned, its bind-time transform. NodeID parentID; ///< SceneBuilder node ID of parent }; @@ -85,7 +83,7 @@ namespace Falcor std::string name; ///< Instance name. UsdPrim protoPrim; ///< Reference to prototype prim. NodeID parentID{ NodeID::kInvalidID }; ///< SceneBuilder parent node id. - rmcv::mat4 xform = rmcv::mat4(1.f); ///< Instance transformation. + float4x4 xform = float4x4::identity(); ///< Instance transformation. std::vector keyframes; ///< Keyframes for animated instance transformation, if any. }; @@ -123,7 +121,7 @@ namespace Falcor */ struct Curve { - static const uint32_t kInvalidID = std::numeric_limits::max(); + static constexpr uint32_t kInvalidID = std::numeric_limits::max(); UsdPrim curvePrim; ///< Curve prim. CurveTessellationMode tessellationMode; ///< Curve tessellation mode. @@ -161,13 +159,13 @@ namespace Falcor { SceneBuilder::Node node; node.name = prim.GetPath().GetString(); - node.transform = rmcv::mat4(1.f); + node.transform = float4x4::identity(); node.parent = NodeID::Invalid(); nodes.push_back(node); nodeStack.push_back(NodeID{ 0 }); } - void addGeomInstance(const std::string& name, UsdPrim prim, const rmcv::mat4& xform, const rmcv::mat4& bindXform) + void addGeomInstance(const std::string& name, UsdPrim prim, const float4x4& xform, const float4x4& bindXform) { geomInstances.push_back(GeomInstance{name, prim, xform, bindXform, nodeStack.back()}); } @@ -187,7 +185,7 @@ namespace Falcor { // The node stack should at least contain the root node. FALCOR_ASSERT(nodeStack.size() > 0); - rmcv::mat4 localTransform; + float4x4 localTransform; bool resets = getLocalTransform(prim, localTransform); SceneBuilder::Node node; @@ -222,7 +220,7 @@ namespace Falcor // Skeleton Data std::vector bones; ///< Local transforms of each bone - std::vector animations; ///< Animations per bone + std::vector> animations; ///< Animations per bone NodeID::IntType nodeOffset{ NodeID::kInvalidID };///< Where this skeleton's nodes start in the SceneBuilder }; @@ -237,15 +235,15 @@ namespace Falcor // Importer data and helper functions struct ImporterContext { - ImporterContext(const std::filesystem::path& path, UsdStageRefPtr pStage, SceneBuilder& builder, const Dictionary& dict, TimeReport& timeReport, bool useInstanceProxies = false); + ImporterContext(const std::filesystem::path& stagePath, UsdStageRefPtr pStage, SceneBuilder& builder, const pybind11::dict& dict, TimeReport& timeReport, bool useInstanceProxies = false); // Get pointer to default material for the given prim, based on its type, creating it if it doesn't already exist. // Thread-safe. - Material::SharedPtr getDefaultMaterial(const UsdPrim& prim); + ref getDefaultMaterial(const UsdPrim& prim); // Return a pointer to the material to use for the given UsdShadeMaterial. Thread-safe. - Material::SharedPtr resolveMaterial(const UsdPrim& prim, const UsdShadeMaterial& material, const std::string& primName); + ref resolveMaterial(const UsdPrim& prim, const UsdShadeMaterial& material, const std::string& primName); // Return the UsdShadeMaterial bound to the given prim template @@ -257,7 +255,7 @@ namespace Falcor // Meshes void addMesh(const UsdPrim& prim); const Mesh& getMesh(const UsdPrim& meshPrim) { return meshes[geomMap.at(meshPrim)]; } - void addGeomInstance(const std::string& name, const UsdPrim& prim, const rmcv::mat4& xform, const rmcv::mat4& bindxform); + void addGeomInstance(const std::string& name, const UsdPrim& prim, const float4x4& xform, const float4x4& bindxform); // Prototypes void createPrototype(const UsdPrim& rootPrim); @@ -275,12 +273,12 @@ namespace Falcor } return curves[curveMap.at(curvePrim)]; } - void addCurveInstance(const std::string& name, const UsdPrim& curvePrim, const rmcv::mat4& xform, NodeID parentID); + void addCurveInstance(const std::string& name, const UsdPrim& curvePrim, const float4x4& xform, NodeID parentID); void addCachedCurve(Curve& curve); // Add USD Objects void createEnvMap(const UsdPrim& lightPrim); - void addLight(const UsdPrim& lightPrim, Light::SharedPtr pLight, NodeID parentId); + void addLight(const UsdPrim& lightPrim, ref pLight, NodeID parentId); void createDistantLight(const UsdPrim& lightPrim); void createRectLight(const UsdPrim& lightPrim); void createSphereLight(const UsdPrim& lightPrim); @@ -307,13 +305,13 @@ namespace Falcor This should only be called once during importer initialization and only when the nodeStack is empty. */ - void setRootXform(const rmcv::mat4& xform); - rmcv::mat4 getLocalToWorldXform(const UsdGeomXformable& prim, UsdTimeCode time = UsdTimeCode::EarliestTime()); + void setRootXform(const float4x4& xform); + float4x4 getLocalToWorldXform(const UsdGeomXformable& prim, UsdTimeCode time = UsdTimeCode::EarliestTime()); size_t getNodeStackDepth() const { return nodeStack.size(); } void pushNode(const UsdGeomXformable& prim); void popNode() { nodeStack.pop_back(); } NodeID getRootNodeID() const { return nodeStack[nodeStackStartDepth.back()]; } - rmcv::mat4 getGeomBindTransform(const UsdPrim& usdPrim) const; + float4x4 getGeomBindTransform(const UsdPrim& usdPrim) const; /** Start a new node stack. @@ -337,9 +335,9 @@ namespace Falcor */ void finalize(); - std::filesystem::path path; ///< Path of the USD stage being imported. + std::filesystem::path stagePath; ///< Path of the USD stage being imported. UsdStageRefPtr pStage; ///< USD stage being imported. - const Dictionary& dict; ///< Input map from material path to material short name. + const pybind11::dict& dict; ///< Input map from material path to material short name. std::map localDict; ///< Local input map from material path to TimeReport& timeReport; ///< Timer object to use when importing. SceneBuilder& builder; ///< Scene builder for this import session. @@ -347,13 +345,14 @@ namespace Falcor std::vector nodeStackStartDepth; ///< Stack depth at time of new node stack creation float metersPerUnit = .01f; ///< Meters per unit scene distance. double timeCodesPerSecond = 24.0; ///< Time code unit scaling for time-sampled data. USD default is 24. - rmcv::mat4 rootXform; ///< Pseudoroot xform, correcting for world unit scaling and up vector orientation. + int defaultRefinementLevel = 0; ///< Default subdiv refinement level. + float4x4 rootXform = float4x4::identity(); ///< Pseudoroot xform, correcting for world unit scaling and up vector orientation. NodeID rootXformNodeId{ NodeID::kInvalidID }; ///< Get the node ID containing the scene root transform in the builder's scene graph bool useInstanceProxies = false; ///< If true, traverse instances as if they were non-instances (debugging feature). - std::unordered_map materialMap; ///< Created material instances, indexed by material instance name. - std::unordered_map defaultMaterialMap; ///< Default materials, indexed by base color. - std::unordered_map defaultCurveMaterialMap; ///< Default curve materials, indexed by absorption coefficient. + std::unordered_map> materialMap; ///< Created material instances, indexed by material instance name. + std::unordered_map, Float3Hash> defaultMaterialMap; ///< Default materials, indexed by base color. + std::unordered_map, Float3Hash> defaultCurveMaterialMap; ///< Default curve materials, indexed by absorption coefficient. std::mutex materialMutex; ///< Mutex to protect access to material maps. std::vector meshes; ///< List of meshes. diff --git a/Source/Falcor/RenderGraph/BasePasses/RasterScenePass.cpp b/Source/plugins/importers/USDImporter/IndexedVector.h similarity index 50% rename from Source/Falcor/RenderGraph/BasePasses/RasterScenePass.cpp rename to Source/plugins/importers/USDImporter/IndexedVector.h index 3bab5a258..d0968f4fc 100644 --- a/Source/Falcor/RenderGraph/BasePasses/RasterScenePass.cpp +++ b/Source/plugins/importers/USDImporter/IndexedVector.h @@ -25,47 +25,73 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **************************************************************************/ -#include "RasterScenePass.h" -#include "Scene/Scene.h" +#pragma once + +#include +#include namespace Falcor { - RasterScenePass::RasterScenePass(std::shared_ptr pDevice, const Scene::SharedPtr& pScene, const Program::Desc& progDesc, const Program::DefineList& programDefines) - : BaseGraphicsPass(std::move(pDevice), progDesc, programDefines), mpScene(pScene) - { - FALCOR_ASSERT(pScene); - } - RasterScenePass::SharedPtr RasterScenePass::create(std::shared_ptr pDevice, const Scene::SharedPtr& pScene, const Program::Desc& progDesc, const Program::DefineList& programDefines) - { - if (pScene == nullptr) throw ArgumentError("Can't create a RasterScenePass object without a scene"); - - Program::DefineList dl = programDefines; - dl.add(pScene->getSceneDefines()); - - return SharedPtr(new RasterScenePass(std::move(pDevice), pScene, progDesc, dl)); - } - - RasterScenePass::SharedPtr RasterScenePass::create(std::shared_ptr pDevice, const Scene::SharedPtr& pScene, const std::filesystem::path& path, const std::string& vsEntry, const std::string& psEntry, const Program::DefineList& programDefines) +/** + * @brief Class to convert vector of possibly-duplicate items to a vector of indices into a set of unique data items. + * + * @tparam T Underlying type + * @tparam I Index value type + * @tparam H Hash object on type T, used to determine data item equivalence + */ +template +class IndexedVector +{ +public: + /** + * @brief Append data item. + * @param[in] v Data item to append + */ + void append(const T& v) { - Program::Desc d; - d.addShaderLibrary(path).vsEntry(vsEntry).psEntry(psEntry); - return create(std::move(pDevice), pScene, d, programDefines); + uint32_t idx; + append(v, idx); } - void RasterScenePass::renderScene(RenderContext* pRenderContext, const Fbo::SharedPtr& pDstFbo) + /** + * @brief Append data item. + * + * @param[in] v Data item to append + * @param[out] idx Index of the unique item corresponding to v + * @return True if @p v was newly inserted into the set of unique data item + */ + bool append(const T& v, uint32_t& outIdx) { - mpState->setFbo(pDstFbo); - mpScene->rasterize(pRenderContext, mpState.get(), mpVars.get()); + bool insertedNew = false; + auto iter = mIndexMap.find(v); + if (iter == mIndexMap.end()) + { + iter = mIndexMap.insert(std::make_pair(v, I(mValues.size()))).first; + outIdx = mValues.size(); + mValues.push_back(v); + insertedNew = true; + } + else + { + outIdx = iter->second; + } + mIndices.push_back(iter->second); + return insertedNew; } + /** + * @brief Get the set of unique data items. + */ + const pxr::VtArray& getValues() const { return mValues; } - bool RasterScenePass::onMouseEvent(const MouseEvent& mouseEvent) - { - return mpScene->onMouseEvent(mouseEvent); - } + /** + * @brief Get the ordered list of item indices. + */ + const pxr::VtArray& getIndices() const { return mIndices; } - bool RasterScenePass::onKeyEvent(const KeyboardEvent& keyEvent) - { - return mpScene->onKeyEvent(keyEvent); - } +private: + std::unordered_map mIndexMap; + pxr::VtArray mValues; + pxr::VtArray mIndices; +}; } diff --git a/Source/plugins/importers/USDImporter/PreviewSurfaceConverter.cpp b/Source/plugins/importers/USDImporter/PreviewSurfaceConverter.cpp index e20bafb98..d4eaf2b49 100644 --- a/Source/plugins/importers/USDImporter/PreviewSurfaceConverter.cpp +++ b/Source/plugins/importers/USDImporter/PreviewSurfaceConverter.cpp @@ -34,7 +34,6 @@ BEGIN_DISABLE_USD_WARNINGS #include #include -#include #include #include END_DISABLE_USD_WARNINGS @@ -124,7 +123,7 @@ SdfLayerHandle getLayerHandle(const UsdAttribute& attr, const UsdTimeCode& time) } } // namespace -StandardMaterial::SharedPtr PreviewSurfaceConverter::getCachedMaterial(const UsdShadeShader& shader) +ref PreviewSurfaceConverter::getCachedMaterial(const UsdShadeShader& shader) { std::unique_lock lock(mCacheMutex); @@ -158,7 +157,7 @@ StandardMaterial::SharedPtr PreviewSurfaceConverter::getCachedMaterial(const Usd return nullptr; } -StandardMaterial::SharedPtr PreviewSurfaceConverter::getCachedMaterial(const StandardMaterialSpec& spec) +ref PreviewSurfaceConverter::getCachedMaterial(const StandardMaterialSpec& spec) { std::unique_lock lock(mCacheMutex); @@ -192,7 +191,7 @@ StandardMaterial::SharedPtr PreviewSurfaceConverter::getCachedMaterial(const Sta return nullptr; } -void PreviewSurfaceConverter::cacheMaterial(const UsdShadeShader& shader, StandardMaterial::SharedPtr pMaterial) +void PreviewSurfaceConverter::cacheMaterial(const UsdShadeShader& shader, ref pMaterial) { { std::unique_lock lock(mCacheMutex); @@ -207,7 +206,7 @@ void PreviewSurfaceConverter::cacheMaterial(const UsdShadeShader& shader, Standa mPrimCacheUpdated.notify_all(); } -void PreviewSurfaceConverter::cacheMaterial(const StandardMaterialSpec& spec, StandardMaterial::SharedPtr pMaterial) +void PreviewSurfaceConverter::cacheMaterial(const StandardMaterialSpec& spec, ref pMaterial) { { std::unique_lock lock(mCacheMutex); @@ -303,7 +302,7 @@ StandardMaterialSpec::ConvertedInput PreviewSurfaceConverter::convertTexture( UsdShadeShader shader(prim); TfToken id = getAttribute(prim.GetAttribute(UsdShadeTokens->infoId), TfToken()); - if (id != UsdImagingTokens->UsdUVTexture) + if (id != TfToken("UsdUVTexture")) { logWarning("Expected UsdUVTexture node '{}' is not a UsdUVTexture.", prim.GetPath().GetString()); return ret; @@ -318,7 +317,7 @@ StandardMaterialSpec::ConvertedInput PreviewSurfaceConverter::convertTexture( if (shader.GetInput(biasToken)) { float4 value; - if (!getFloat4Value(input, value) || value != float4(0.f, 0.f, 0.f, 0.f)) + if (!getFloat4Value(input, value) || any(value != float4(0.f, 0.f, 0.f, 0.f))) { logWarning( "UsdUvTexture node '{}' specifies a non-zero bias, which is not supported.", prim.GetPath().GetString() @@ -326,8 +325,7 @@ StandardMaterialSpec::ConvertedInput PreviewSurfaceConverter::convertTexture( } } - UsdShadeInput scaleInput = shader.GetInput(scaleToken); - if (scaleInput) + if (UsdShadeInput scaleInput = shader.GetInput(scaleToken); scaleInput) { if (!getFloat4Value(scaleInput, ret.textureScale)) { @@ -335,7 +333,7 @@ StandardMaterialSpec::ConvertedInput PreviewSurfaceConverter::convertTexture( "UsdUvTexture node '{}' specifies value scale of an unsupported type.", prim.GetPath().GetString() ); } - else if (!scaleSupported && glm::any(glm::notEqual(ret.textureScale, float4(1.f, 1.f, 1.f, 1.f)))) + else if (!scaleSupported && any(ret.textureScale != float4(1.f, 1.f, 1.f, 1.f))) { logWarning( "UsdUvTexture node '{}' specifies a value scale, which is not supported.", prim.GetPath().GetString() @@ -352,20 +350,16 @@ StandardMaterialSpec::ConvertedInput PreviewSurfaceConverter::convertTexture( id = getAttribute(source.GetPrim().GetAttribute(UsdShadeTokens->infoId), TfToken()); if (id == transform2dToken) { - UsdPrim prim(source.GetPrim()); - - UsdShadeInput scaleInput = source.GetInput(scaleToken); - if (scaleInput) + if (UsdShadeInput scaleInput = source.GetInput(scaleToken); scaleInput) { - Falcor::float2 scaleVec; + float2 scaleVec; if (getFloat2Value(scaleInput, scaleVec)) { ret.texTransform.setScaling(float3(scaleVec.x, scaleVec.y, 1.f)); } } - UsdShadeInput rotateInput = source.GetInput(rotationToken); - if (rotateInput) + if (UsdShadeInput rotateInput = source.GetInput(rotationToken); rotateInput) { float degrees; if (rotateInput.Get(°rees, UsdTimeCode::EarliestTime())) @@ -374,16 +368,15 @@ StandardMaterialSpec::ConvertedInput PreviewSurfaceConverter::convertTexture( } } - UsdShadeInput translateInput = source.GetInput(translationToken); - if (translateInput) + if (UsdShadeInput translateInput = source.GetInput(translationToken); translateInput) { - Falcor::float2 translateVec; + float2 translateVec; if (getFloat2Value(translateInput, translateVec)) { ret.texTransform.setTranslation(float3(translateVec.x, translateVec.y, 0.f)); } } - ret.texTransform.setCompositionOrder(Falcor::Transform::CompositionOrder::ScaleRotateTranslate); + ret.texTransform.setCompositionOrder(Transform::CompositionOrder::ScaleRotateTranslate); } else if (id != float2PrimvarReaderToken) { @@ -454,13 +447,13 @@ StandardMaterialSpec::ConvertedInput PreviewSurfaceConverter::convertTexture( if (fileInput) { SdfAssetPath path; - UsdPrim prim(fileInput.GetPrim()); - UsdAttribute fileAttrib = prim.GetAttribute(TfToken("inputs:file")); + UsdPrim filePrim(fileInput.GetPrim()); + UsdAttribute fileAttrib = filePrim.GetAttribute(TfToken("inputs:file")); - TfToken colorSpace = fileAttrib.GetColorSpace(); - if (colorSpace == sRGBToken) + TfToken fileColorSpace = fileAttrib.GetColorSpace(); + if (fileColorSpace == sRGBToken) ret.loadSRGB = true; - else if (colorSpace == rawToken) + else if (fileColorSpace == rawToken) ret.loadSRGB = false; fileInput.Get(&path, UsdTimeCode::EarliestTime()); @@ -540,8 +533,8 @@ StandardMaterialSpec::ConvertedInput PreviewSurfaceConverter::convertColor( return ret; } -PreviewSurfaceConverter::PreviewSurfaceConverter(std::shared_ptr pDevice) - : mpDevice(std::move(pDevice)) +PreviewSurfaceConverter::PreviewSurfaceConverter(ref pDevice) + : mpDevice(pDevice) { mpSpecTransPass = ComputePass::create(mpDevice, kSpecTransShaderFile, kSpecTransShaderEntry); @@ -555,13 +548,13 @@ PreviewSurfaceConverter::PreviewSurfaceConverter(std::shared_ptr pDevice Sampler::AddressMode::Clamp, Sampler::AddressMode::Clamp, Sampler::AddressMode::Clamp ); - mpSampler = Sampler::create(mpDevice.get(), samplerDesc); + mpSampler = Sampler::create(mpDevice, samplerDesc); } // Convert textured opacity to textured specular transparency. -Texture::SharedPtr PreviewSurfaceConverter::createSpecularTransmissionTexture( +ref PreviewSurfaceConverter::createSpecularTransmissionTexture( StandardMaterialSpec::ConvertedInput& opacity, - Texture::SharedPtr opacityTexture, + ref opacityTexture, RenderContext* pRenderContext ) { @@ -579,15 +572,16 @@ Texture::SharedPtr PreviewSurfaceConverter::createSpecularTransmissionTexture( } uint2 resolution(opacityTexture->getWidth(), opacityTexture->getHeight()); - Texture::SharedPtr pTexture = Texture::create2D( - mpDevice.get(), resolution.x, resolution.y, ResourceFormat::RGBA8Unorm, 1, Texture::kMaxPossible, nullptr, + ref pTexture = Texture::create2D( + mpDevice, resolution.x, resolution.y, ResourceFormat::RGBA8Unorm, 1, Texture::kMaxPossible, nullptr, Resource::BindFlags::ShaderResource | Resource::BindFlags::UnorderedAccess | Resource::BindFlags::RenderTarget ); - mpSpecTransPass["CB"]["outDim"] = resolution; - mpSpecTransPass["CB"]["opacityChannel"] = getChannelIndex(opacity.channels); - mpSpecTransPass["opacityTexture"] = opacityTexture; - mpSpecTransPass["outputTexture"] = pTexture; + auto var = mpSpecTransPass->getRootVar(); + var["CB"]["outDim"] = resolution; + var["CB"]["opacityChannel"] = getChannelIndex(opacity.channels); + var["opacityTexture"] = opacityTexture; + var["outputTexture"] = pTexture; mpSpecTransPass->execute(pRenderContext, resolution.x, resolution.y); @@ -600,11 +594,11 @@ Texture::SharedPtr PreviewSurfaceConverter::createSpecularTransmissionTexture( * Combine base color and alpha, one or both of which may be textured, into a single texture. * If both are textured, they may be of different resolutions. */ -Texture::SharedPtr PreviewSurfaceConverter::packBaseColorAlpha( +ref PreviewSurfaceConverter::packBaseColorAlpha( StandardMaterialSpec::ConvertedInput& baseColor, - Texture::SharedPtr baseColorTexture, + ref baseColorTexture, StandardMaterialSpec::ConvertedInput& opacity, - Texture::SharedPtr opacityTexture, + ref opacityTexture, RenderContext* pRenderContext ) { @@ -627,26 +621,27 @@ Texture::SharedPtr PreviewSurfaceConverter::packBaseColorAlpha( // sRGB format textures can't be bound as UAVs. Render to an RGBA32Float intermediate texture, and then blit to // RGBA8UnormSrgb to preserve precision near zero. - Texture::SharedPtr pTexture = Texture::create2D( - mpDevice.get(), resolution.x, resolution.y, ResourceFormat::RGBA32Float, 1, 1, nullptr, + ref pTexture = Texture::create2D( + mpDevice, resolution.x, resolution.y, ResourceFormat::RGBA32Float, 1, 1, nullptr, Resource::BindFlags::ShaderResource | Resource::BindFlags::UnorderedAccess ); - mpPackAlphaPass["CB"]["opacityChannel"] = opacityTexture ? getChannelIndex(opacity.channels) : -1; - mpPackAlphaPass["CB"]["opacityValue"] = opacity.uniformValue.r; - mpPackAlphaPass["CB"]["baseColorTextureValid"] = baseColorTexture ? 1 : 0; - mpPackAlphaPass["CB"]["baseColorValue"] = baseColor.uniformValue; - mpPackAlphaPass["CB"]["outDim"] = resolution; - mpPackAlphaPass["opacityTexture"] = opacityTexture; - mpPackAlphaPass["baseColorTexture"] = baseColorTexture; - mpPackAlphaPass["sampler"] = mpSampler; - mpPackAlphaPass["outputTexture"] = pTexture; + auto var = mpPackAlphaPass->getRootVar(); + var["CB"]["opacityChannel"] = opacityTexture ? getChannelIndex(opacity.channels) : -1; + var["CB"]["opacityValue"] = opacity.uniformValue.r; + var["CB"]["baseColorTextureValid"] = baseColorTexture ? 1 : 0; + var["CB"]["baseColorValue"] = baseColor.uniformValue; + var["CB"]["outDim"] = resolution; + var["opacityTexture"] = opacityTexture; + var["baseColorTexture"] = baseColorTexture; + var["sampler"] = mpSampler; + var["outputTexture"] = pTexture; mpPackAlphaPass->execute(pRenderContext, resolution.x, resolution.y); // Create the output mipmapped sRGB texture - Texture::SharedPtr pFinal = Texture::create2D( - mpDevice.get(), resolution.x, resolution.y, ResourceFormat::RGBA8UnormSrgb, 1, Texture::kMaxPossible, nullptr, + ref pFinal = Texture::create2D( + mpDevice, resolution.x, resolution.y, ResourceFormat::RGBA8UnormSrgb, 1, Texture::kMaxPossible, nullptr, Resource::BindFlags::ShaderResource | Resource::BindFlags::RenderTarget ); @@ -660,11 +655,11 @@ Texture::SharedPtr PreviewSurfaceConverter::packBaseColorAlpha( // Combine roughness and metallic parameters, one or both of which are textured, into a specular/ORM texture. // If both are textured, they may be of different resolutions. -Texture::SharedPtr PreviewSurfaceConverter::createSpecularTexture( +ref PreviewSurfaceConverter::createSpecularTexture( StandardMaterialSpec::ConvertedInput& roughness, - Texture::SharedPtr roughnessTexture, + ref roughnessTexture, StandardMaterialSpec::ConvertedInput& metallic, - Texture::SharedPtr metallicTexture, + ref metallicTexture, RenderContext* pRenderContext ) { @@ -690,20 +685,21 @@ Texture::SharedPtr PreviewSurfaceConverter::createSpecularTexture( ); uint2 resolution(width, height); - Texture::SharedPtr pTexture = Texture::create2D( - mpDevice.get(), width, height, ResourceFormat::RGBA8Unorm, 1, Texture::kMaxPossible, nullptr, + ref pTexture = Texture::create2D( + mpDevice, width, height, ResourceFormat::RGBA8Unorm, 1, Texture::kMaxPossible, nullptr, Resource::BindFlags::ShaderResource | Resource::BindFlags::UnorderedAccess | Resource::BindFlags::RenderTarget ); - mpSpecularPass["CB"]["roughnessChannel"] = roughnessTexture ? getChannelIndex(roughness.channels) : -1; - mpSpecularPass["CB"]["roughnessValue"] = roughness.uniformValue.r; - mpSpecularPass["CB"]["metallicChannel"] = metallicTexture ? getChannelIndex(metallic.channels) : -1; - mpSpecularPass["CB"]["metallicValue"] = metallic.uniformValue.r; - mpSpecularPass["CB"]["outDim"] = resolution; - mpSpecularPass["metallicTexture"] = metallicTexture; - mpSpecularPass["roughnessTexture"] = roughnessTexture; - mpSpecularPass["sampler"] = mpSampler; - mpSpecularPass["outputTexture"] = pTexture; + auto var = mpSpecularPass->getRootVar(); + var["CB"]["roughnessChannel"] = roughnessTexture ? getChannelIndex(roughness.channels) : -1; + var["CB"]["roughnessValue"] = roughness.uniformValue.r; + var["CB"]["metallicChannel"] = metallicTexture ? getChannelIndex(metallic.channels) : -1; + var["CB"]["metallicValue"] = metallic.uniformValue.r; + var["CB"]["outDim"] = resolution; + var["metallicTexture"] = metallicTexture; + var["roughnessTexture"] = roughnessTexture; + var["sampler"] = mpSampler; + var["outputTexture"] = pTexture; mpSpecularPass->execute(pRenderContext, resolution.x, resolution.y); @@ -712,7 +708,7 @@ Texture::SharedPtr PreviewSurfaceConverter::createSpecularTexture( return pTexture; } -Texture::SharedPtr PreviewSurfaceConverter::loadTexture(const StandardMaterialSpec::ConvertedInput& ci) +ref PreviewSurfaceConverter::loadTexture(const StandardMaterialSpec::ConvertedInput& ci) { if (ci.texturePath.empty()) { @@ -724,7 +720,7 @@ Texture::SharedPtr PreviewSurfaceConverter::loadTexture(const StandardMaterialSp // slow but can run in parallel, from texture creation, which must be single-threaded, // as done below. std::scoped_lock lock(mMutex); - return ImageIO::loadTextureFromDDS(mpDevice.get(), ci.texturePath, ci.loadSRGB); + return ImageIO::loadTextureFromDDS(mpDevice, ci.texturePath, ci.loadSRGB); } else { @@ -741,7 +737,7 @@ Texture::SharedPtr PreviewSurfaceConverter::loadTexture(const StandardMaterialSp { std::scoped_lock lock(mMutex); return Texture::create2D( - mpDevice.get(), pBitmap->getWidth(), pBitmap->getHeight(), format, 1, Texture::kMaxPossible, pBitmap->getData() + mpDevice, pBitmap->getWidth(), pBitmap->getHeight(), format, 1, Texture::kMaxPossible, pBitmap->getData() ); } } @@ -912,7 +908,7 @@ StandardMaterialSpec PreviewSurfaceConverter::createSpec(const std::string& name return spec; } -Material::SharedPtr PreviewSurfaceConverter::convert( +ref PreviewSurfaceConverter::convert( const UsdShadeMaterial& material, const std::string& primName, RenderContext* pRenderContext @@ -952,7 +948,7 @@ Material::SharedPtr PreviewSurfaceConverter::convert( } TfToken id = getAttribute(shader.GetPrim().GetAttribute(UsdShadeTokens->infoId), TfToken()); - if (id != UsdImagingTokens->UsdPreviewSurface) + if (id != TfToken("UsdPreviewSurface")) { logDebug( "Material '{}' has a surface output node of type '{}', not UsdPreviewSurface.", primName, id.GetString() @@ -964,7 +960,7 @@ Material::SharedPtr PreviewSurfaceConverter::convert( // Is there a valid cached instance for this material prim? If so, simply return it. // Note that this call will block if another thread is concurrently converting the same material. - StandardMaterial::SharedPtr pMaterial = getCachedMaterial(shader); + ref pMaterial = getCachedMaterial(shader); if (pMaterial) { return pMaterial; @@ -996,20 +992,20 @@ Material::SharedPtr PreviewSurfaceConverter::convert( // For now, force all materials to be double-sided. pMaterial->setDoubleSided(true); - Texture::SharedPtr baseColorTexture = loadTexture(spec.baseColor); - Texture::SharedPtr opacityTexture = loadTexture(spec.opacity); - Texture::SharedPtr roughnessTexture = loadTexture(spec.roughness); - Texture::SharedPtr metallicTexture = loadTexture(spec.metallic); - Texture::SharedPtr normalTexture = loadTexture(spec.normal); - Texture::SharedPtr emissionTexture = loadTexture(spec.emission); - Texture::SharedPtr displacementTexture = loadTexture(spec.disp); + ref baseColorTexture = loadTexture(spec.baseColor); + ref opacityTexture = loadTexture(spec.opacity); + ref roughnessTexture = loadTexture(spec.roughness); + ref metallicTexture = loadTexture(spec.metallic); + ref normalTexture = loadTexture(spec.normal); + ref emissionTexture = loadTexture(spec.emission); + ref displacementTexture = loadTexture(spec.disp); pMaterial->setIndexOfRefraction(spec.ior); // If there is either a roughness or metallic texture, convert texture(s) and constant (if any) to an ORM texture. if (metallicTexture || roughnessTexture) { - Texture::SharedPtr pSpecularTex = + ref pSpecularTex = createSpecularTexture(spec.roughness, roughnessTexture, spec.metallic, metallicTexture, pRenderContext); pMaterial->setSpecularTexture(pSpecularTex); } @@ -1032,7 +1028,7 @@ Material::SharedPtr PreviewSurfaceConverter::convert( } else { - spec.baseColor.uniformValue = float4(spec.baseColor.uniformValue.rgb, spec.opacity.uniformValue.r); + spec.baseColor.uniformValue = float4(spec.baseColor.uniformValue.xyz(), spec.opacity.uniformValue.r); } pMaterial->setAlphaThreshold(spec.opacityThreshold); } @@ -1045,7 +1041,7 @@ Material::SharedPtr PreviewSurfaceConverter::convert( "UsdPreviewSurface '{}' has texture-mapped opacity. Converting to textured specular transmission.", shader.GetPath().GetString() ); - Texture::SharedPtr transmissionTexture = + ref transmissionTexture = createSpecularTransmissionTexture(spec.opacity, opacityTexture, pRenderContext); pMaterial->setTransmissionTexture(transmissionTexture); pMaterial->setSpecularTransmission(1.f); @@ -1083,10 +1079,10 @@ Material::SharedPtr PreviewSurfaceConverter::convert( } else { - pMaterial->setEmissiveColor(spec.emission.uniformValue); + pMaterial->setEmissiveColor(spec.emission.uniformValue.xyz()); } - if (spec.emission.textureScale != glm::float4(1.f, 1.f, 1.f, 1.f)) + if (any(spec.emission.textureScale != float4(1.f, 1.f, 1.f, 1.f))) { if (spec.emission.textureScale.x != spec.emission.textureScale.y || spec.emission.textureScale.x != spec.emission.textureScale.z || @@ -1106,13 +1102,13 @@ Material::SharedPtr PreviewSurfaceConverter::convert( pMaterial->setDisplacementMap(displacementTexture); } - if (spec.texTransform.getMatrix() != Falcor::rmcv::identity()) + if (spec.texTransform.getMatrix() != float4x4::identity()) { // Compute the inverse of the texture transform, as Falcor assumes // the transform applies to the texture, rather than the texture coordinates, // as UsdPreviewSurface does. FALCOR_ASSERT( - spec.texTransform.getCompositionOrder() == Falcor::Transform::CompositionOrder::ScaleRotateTranslate + spec.texTransform.getCompositionOrder() == Transform::CompositionOrder::ScaleRotateTranslate ); Transform inv; inv.setTranslation(-spec.texTransform.getTranslation()); @@ -1120,7 +1116,7 @@ Material::SharedPtr PreviewSurfaceConverter::convert( inv.setScaling(float3(1.f / scale.x, 1.f / scale.y, 1.f)); float3 rot = spec.texTransform.getRotationEuler(); inv.setRotationEuler(-rot); - inv.setCompositionOrder(Falcor::Transform::CompositionOrder::TranslateRotateScale); + inv.setCompositionOrder(Transform::CompositionOrder::TranslateRotateScale); pMaterial->setTextureTransform(inv); } diff --git a/Source/plugins/importers/USDImporter/PreviewSurfaceConverter.h b/Source/plugins/importers/USDImporter/PreviewSurfaceConverter.h index 423a84ade..96ad085af 100644 --- a/Source/plugins/importers/USDImporter/PreviewSurfaceConverter.h +++ b/Source/plugins/importers/USDImporter/PreviewSurfaceConverter.h @@ -30,7 +30,7 @@ #include "USDHelpers.h" #include "Core/API/Texture.h" #include "Core/API/Sampler.h" -#include "RenderGraph/BasePasses/ComputePass.h" +#include "Core/Pass/ComputePass.h" #include "Scene/Material/Material.h" #include "Scene/Material/StandardMaterial.h" #include "StandardMaterialSpec.h" @@ -56,7 +56,7 @@ class PreviewSurfaceConverter /** * Create a new converter, compiling required compute shaders */ - PreviewSurfaceConverter(std::shared_ptr pDevice); + PreviewSurfaceConverter(ref pDevice); /** * Create a Falcor material from a USD material containing a UsdPreviewSurface shader. @@ -64,7 +64,7 @@ class PreviewSurfaceConverter * \param primName Name of the primitive to which the material is bound (for warning message reporting only). * \return A Falcor material instance representing the UsdPreviewSurface specified in the given material, if any, nullptr otherwise. */ - Material::SharedPtr convert( + ref convert( const pxr::UsdShadeMaterial& material, const std::string& primName, RenderContext* pRenderContext @@ -72,25 +72,25 @@ class PreviewSurfaceConverter private: StandardMaterialSpec createSpec(const std::string& name, const UsdShadeShader& shader) const; - Texture::SharedPtr loadTexture(const StandardMaterialSpec::ConvertedInput& ci); + ref loadTexture(const StandardMaterialSpec::ConvertedInput& ci); - Texture::SharedPtr createSpecularTransmissionTexture( + ref createSpecularTransmissionTexture( StandardMaterialSpec::ConvertedInput& opacity, - Texture::SharedPtr opacityTexture, + ref opacityTexture, RenderContext* pRenderContext ); - Texture::SharedPtr packBaseColorAlpha( + ref packBaseColorAlpha( StandardMaterialSpec::ConvertedInput& baseColor, - Texture::SharedPtr baseColorTexture, + ref baseColorTexture, StandardMaterialSpec::ConvertedInput& opacity, - Texture::SharedPtr opacityTexture, + ref opacityTexture, RenderContext* pRenderContext ); - Texture::SharedPtr createSpecularTexture( + ref createSpecularTexture( StandardMaterialSpec::ConvertedInput& roughness, - Texture::SharedPtr roughnessTexture, + ref roughnessTexture, StandardMaterialSpec::ConvertedInput& metallic, - Texture::SharedPtr metallicTexture, + ref metallicTexture, RenderContext* pRenderContext ); @@ -108,27 +108,27 @@ class PreviewSurfaceConverter bool scaleSupported = false ) const; - StandardMaterial::SharedPtr getCachedMaterial(const pxr::UsdShadeShader& shader); - StandardMaterial::SharedPtr getCachedMaterial(const StandardMaterialSpec& spec); + ref getCachedMaterial(const pxr::UsdShadeShader& shader); + ref getCachedMaterial(const StandardMaterialSpec& spec); - void cacheMaterial(const UsdShadeShader& shader, StandardMaterial::SharedPtr pMaterial); - void cacheMaterial(const StandardMaterialSpec& spec, StandardMaterial::SharedPtr pMaterial); + void cacheMaterial(const UsdShadeShader& shader, ref pMaterial); + void cacheMaterial(const StandardMaterialSpec& spec, ref pMaterial); - std::shared_ptr mpDevice; + ref mpDevice; - ComputePass::SharedPtr mpSpecTransPass; ///< Pass to convert opacity to transparency - ComputePass::SharedPtr mpPackAlphaPass; ///< Pass to convert separate RGB and A to RGBA - ComputePass::SharedPtr mpSpecularPass; ///< Pass to create ORM texture + ref mpSpecTransPass; ///< Pass to convert opacity to transparency + ref mpPackAlphaPass; ///< Pass to convert separate RGB and A to RGBA + ref mpSpecularPass; ///< Pass to create ORM texture - Sampler::SharedPtr mpSampler; ///< Bilinear clamp sampler + ref mpSampler; ///< Bilinear clamp sampler ///< Map from UsdPreviewSurface-defining UsdShadeShader to Falcor material instance. An entry with a null instance ///< indicates in-progress conversion. - std::unordered_map mPrimMaterialCache; + std::unordered_map, UsdObjHash> mPrimMaterialCache; ///< Map from StandardMaterialSpec to Falcor material instance. An entry with a null instance indicates in-progress ///< conversion. - std::unordered_map mSpecMaterialCache; + std::unordered_map, SpecHash> mSpecMaterialCache; std::mutex mMutex; ///< Mutex to ensure serial invocation of calls that are not thread safe (e.g., texture creation ///< and compute program execution). diff --git a/Source/plugins/importers/USDImporter/StandardMaterialSpec.h b/Source/plugins/importers/USDImporter/StandardMaterialSpec.h index 5df7f7460..3ad6dc2d8 100644 --- a/Source/plugins/importers/USDImporter/StandardMaterialSpec.h +++ b/Source/plugins/importers/USDImporter/StandardMaterialSpec.h @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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,8 +31,6 @@ #include "Scene/Material/Material.h" #include "Scene/Material/StandardMaterial.h" #include "Utils/Logger.h" -#define GLM_ENABLE_EXPERIMENTAL -#include "glm/gtx/hash.hpp" #include @@ -75,12 +73,12 @@ struct StandardMaterialSpec bool operator==(const TextureTransform& other) const { - return scale == other.scale && translate == other.translate && rotate == other.rotate; + return all(scale == other.scale) && all(translate == other.translate) && rotate == other.rotate; } bool operator!=(const TextureTransform& other) const { - return scale != other.scale || translate != other.translate || rotate != other.rotate; + return any(scale != other.scale) || any(translate != other.translate) || rotate != other.rotate; } }; @@ -94,8 +92,8 @@ struct StandardMaterialSpec bool operator==(const ConvertedInput& o) const { - return texturePath == o.texturePath && texTransform == o.texTransform && textureScale == o.textureScale && - channels == o.channels && uniformValue == o.uniformValue && loadSRGB == o.loadSRGB; + return texturePath == o.texturePath && texTransform == o.texTransform && all(textureScale == o.textureScale) && + channels == o.channels && all(uniformValue == o.uniformValue) && loadSRGB == o.loadSRGB; } float4 uniformValue = float4(0.f, 0.f, 0.f, 0.f); ///< Uniform value, may only hold a single valid component for @@ -123,8 +121,8 @@ struct StandardMaterialSpec if (!input.isTextured()) return; const Falcor::Transform& newTransform = input.texTransform; - if (newTransform.getMatrix() != Falcor::rmcv::identity() && - texTransform.getMatrix() == Falcor::rmcv::identity()) + if (newTransform.getMatrix() != Falcor::float4x4::identity() && + texTransform.getMatrix() == Falcor::float4x4::identity()) { texTransform = newTransform; } diff --git a/Source/plugins/importers/USDImporter/Subdivision.cpp b/Source/plugins/importers/USDImporter/Subdivision.cpp deleted file mode 100644 index 48e9d324f..000000000 --- a/Source/plugins/importers/USDImporter/Subdivision.cpp +++ /dev/null @@ -1,296 +0,0 @@ -/*************************************************************************** - # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. - # - # Redistribution and use in source and binary forms, with or without - # modification, are permitted provided that the following conditions - # are met: - # * Redistributions of source code must retain the above copyright - # notice, this list of conditions and the following disclaimer. - # * Redistributions in binary form must reproduce the above copyright - # notice, this list of conditions and the following disclaimer in the - # documentation and/or other materials provided with the distribution. - # * Neither the name of NVIDIA CORPORATION nor the names of its - # contributors may be used to endorse or promote products derived - # from this software without specific prior written permission. - # - # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY - # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - **************************************************************************/ -#include "Subdivision.h" -#include "Core/Assert.h" -#include "Utils/Logger.h" - -#include - -#include -#include - -#include -#include -#include -#include - - -using namespace pxr; -using namespace OpenSubdiv; - -namespace Falcor -{ - -namespace -{ -struct SubdivVec3f -{ - GfVec3f v; - void Clear(void* = nullptr) - { - v = {0.f, 0.f, 0.f}; - } - - void AddWithWeight(const SubdivVec3f& o, float weight) - { - v += o.v * weight; - } -}; - -struct SubdivVec2f -{ - GfVec2f v; - void Clear(void* = nullptr) - { - v = {0.f, 0.f}; - } - - void AddWithWeight(const SubdivVec2f& o, float weight) - { - v += o.v * weight; - } -}; -struct GfVec2fHash -{ - size_t operator()(const GfVec2f& v) const - { - // Simple hash function that multiplies the integer interpretation of each component by a prime and xors the results - return (*reinterpret_cast(&v[0]) * 7727ULL) ^ (*reinterpret_cast(&v[1]) * 5521ULL); - } -}; - -Sdc::Options::FVarLinearInterpolation getFaceVaryingLinearInterpolation(const UsdGeomMesh& geomMesh) -{ - TfToken interp = getAttribute(geomMesh.GetFaceVaryingLinearInterpolationAttr(), UsdGeomTokens->cornersPlus1); - - if (interp == UsdGeomTokens->none) return Sdc::Options::FVarLinearInterpolation::FVAR_LINEAR_NONE; - else if (interp == UsdGeomTokens->cornersOnly) return Sdc::Options::FVarLinearInterpolation::FVAR_LINEAR_CORNERS_ONLY; - else if (interp == UsdGeomTokens->cornersPlus1) return Sdc::Options::FVarLinearInterpolation::FVAR_LINEAR_CORNERS_PLUS1; - else if (interp == UsdGeomTokens->cornersPlus2) return Sdc::Options::FVarLinearInterpolation::FVAR_LINEAR_CORNERS_PLUS2; - else if (interp == UsdGeomTokens->boundaries) return Sdc::Options::FVarLinearInterpolation::FVAR_LINEAR_BOUNDARIES; - else if (interp == UsdGeomTokens->all) return Sdc::Options::FVarLinearInterpolation::FVAR_LINEAR_ALL; - - logWarning("Unsupported face varying linear interpolation mode '{}' on '{}'.", interp.GetString(), geomMesh.GetPath().GetString()); - return Sdc::Options::FVarLinearInterpolation::FVAR_LINEAR_CORNERS_PLUS1; -} - -Sdc::Options::VtxBoundaryInterpolation getVertexBoundaryInterpolation(const UsdGeomMesh& geomMesh) -{ - TfToken interp = getAttribute(geomMesh.GetInterpolateBoundaryAttr(), UsdGeomTokens->edgeAndCorner); - - if (interp == UsdGeomTokens->none) return Sdc::Options::VtxBoundaryInterpolation::VTX_BOUNDARY_NONE; - else if (interp == UsdGeomTokens->edgeOnly) return Sdc::Options::VtxBoundaryInterpolation::VTX_BOUNDARY_EDGE_ONLY; - else if (interp == UsdGeomTokens->edgeAndCorner) return Sdc::Options::VtxBoundaryInterpolation::VTX_BOUNDARY_EDGE_AND_CORNER; - - logWarning("Unsupported vertex boundary interpolation mode '{}' on '{}'.", interp.GetString(), geomMesh.GetPath().GetString()); - return Sdc::Options::VtxBoundaryInterpolation::VTX_BOUNDARY_EDGE_AND_CORNER; -} -} // anonymous namespace - -bool refine(const UsdGeomMesh& geomMesh, - const HdMeshTopology& topology, - const uint32_t& maxLevel, - const VtVec3fArray& basePoints, - const VtVec2fArray& baseUVs, - const TfToken uvFreq, - HdMeshTopology& refinedTopology, - VtVec3fArray& refinedPoints, - VtVec3fArray& refinedNormals, - VtVec2fArray& refinedUVs, - std::unique_ptr& meshUtil) -{ - if (maxLevel == 0 || basePoints.size() == 0 || topology.GetNumPoints() == 0 || topology.GetNumFaces() == 0) - { - return false; - } - - uint32_t uvInterpolationMode = 0; - - if (uvFreq == UsdGeomTokens->faceVarying) uvInterpolationMode = Far::StencilTableFactory::INTERPOLATE_FACE_VARYING; - else if (uvFreq == UsdGeomTokens->varying) uvInterpolationMode = Far::StencilTableFactory::INTERPOLATE_VARYING; - else if (uvFreq == UsdGeomTokens->vertex) uvInterpolationMode = Far::StencilTableFactory::INTERPOLATE_VERTEX; - else - { - logWarning("Unsupported texture coordinate frequency: {}", uvFreq.GetString()); - return false; - } - - OpenSubdiv::Sdc::SchemeType scheme = Sdc::SCHEME_CATMARK; - TfToken usdScheme = topology.GetScheme(); - - if (usdScheme == UsdGeomTokens->catmullClark) - { - scheme = Sdc::SCHEME_CATMARK; - } - else if (usdScheme == UsdGeomTokens->loop) - { - // Ensure that the input consists solely of triangles. - VtIntArray courseIndices = topology.GetFaceVertexCounts(); - auto it = std::find_if(courseIndices.begin(), courseIndices.end(), [](int i) { return i != 3; }); - if (it != courseIndices.end()) - { - logWarning("Cannot apply Loop subdivision to non-triangular base mesh '{}'.", geomMesh.GetPath().GetString()); - return false; - } - scheme = Sdc::SCHEME_LOOP; - } - else if (usdScheme == UsdGeomTokens->bilinear) - { - scheme = Sdc::SCHEME_BILINEAR; - } - else if (usdScheme == UsdGeomTokens->none) - { - return false; - } - else - { - logWarning("Unknown subdivision scheme: '{}'.", usdScheme.GetString()); - return false; - } - - OpenSubdiv::Sdc::Options options; - options.SetVtxBoundaryInterpolation(getVertexBoundaryInterpolation(geomMesh)); - options.SetFVarLinearInterpolation(getFaceVaryingLinearInterpolation(geomMesh)); - Far::TopologyRefinerFactory::Options refinerOptions(scheme, options); - - Far::TopologyDescriptor desc; - desc.numVertices = topology.GetNumPoints(); - desc.numFaces = topology.GetNumFaces(); - desc.numVertsPerFace = topology.GetFaceVertexCounts().data(); - desc.vertIndicesPerFace = topology.GetFaceVertexIndices().data(); - - Far::TopologyDescriptor::FVarChannel channel; - std::vector uvIndices; - VtVec2fArray indexedUVs; - - if (baseUVs.size() > 0 && uvFreq == UsdGeomTokens->faceVarying) - { - // Construct unique set of UVs and indices - std::unordered_map indexMap; - for (auto& uv : baseUVs) - { - auto iter = indexMap.find(uv); - if (iter == indexMap.end()) - { - iter = indexMap.insert(std::make_pair(uv, indexedUVs.size())).first; - indexedUVs.push_back(uv); - } - uvIndices.push_back(iter->second); - } - channel.numValues = indexedUVs.size(); - channel.valueIndices = uvIndices.data(); - desc.numFVarChannels = 1; - desc.fvarChannels = &channel; - } - - std::unique_ptr refiner(Far::TopologyRefinerFactory::Create(desc, refinerOptions)); - refiner->RefineUniform(Far::TopologyRefiner::UniformOptions(maxLevel)); - - // Construct a new HdMeshTopogy from the bottom-most refined level. - uint32_t refinementLevel = refiner->GetMaxLevel(); - const Far::TopologyLevel bottomTopology = refiner->GetLevel(refinementLevel); - uint32_t bottomFaceCount = bottomTopology.GetNumFaces(); - uint32_t bottomFaceVertexCount = bottomTopology.GetNumFaceVertices(); - uint32_t bottomFaceVaryingCount = bottomTopology.GetNumFVarValues(0); - - // Construct per-face vertex and indices arrays, used to construct refined HdMeshTopology. - VtIntArray faceVertexCounts = VtIntArray(bottomFaceCount); - VtIntArray faceVertexIndices = VtIntArray(bottomFaceVertexCount); - - uint32_t faceIdx = 0; - for (uint32_t f = 0; f < bottomFaceCount; ++f) - { - Far::ConstIndexArray faceIndices = bottomTopology.GetFaceVertices(f); - faceVertexCounts[f] = faceIndices.size(); - for (int i = 0; i < faceVertexCounts[f]; ++i) - { - faceVertexIndices[faceIdx++] = faceIndices[i]; - } - } - - if (faceIdx != bottomFaceVertexCount) - { - logError("Face vertex count mismatch while refining '{}'", geomMesh.GetPath().GetString()); - return false; - } - - // Construct an HdMeshTopology for the refined mesh. - refinedTopology = HdMeshTopology(topology.GetScheme(), topology.GetOrientation(), faceVertexCounts, faceVertexIndices, refinementLevel); - - // Construct an HdMeshUtil for the refined topology. - meshUtil = std::make_unique(&refinedTopology, geomMesh.GetPath()); - - // Build point interpolation stencil table. - Far::StencilTableFactory::Options stencilOptions; - stencilOptions.interpolationMode = Far::StencilTableFactory::INTERPOLATE_VERTEX; - stencilOptions.generateIntermediateLevels = false; - stencilOptions.generateOffsets = false; - - std::unique_ptr stencilTable(Far::StencilTableFactory::Create(*refiner, stencilOptions)); - - // Compute refined vertex positions. - refinedPoints.resize(stencilTable->GetNumStencils()); - stencilTable->UpdateValues(reinterpret_cast(basePoints.data()), reinterpret_cast(refinedPoints.data())); - - logDebug("After refinement, {} faces, {} vertex indices, {} face varying values, {} points", bottomFaceCount, bottomFaceVertexCount, bottomFaceVaryingCount, refinedPoints.size()); - - // Compute refined normals. Note that we ignore any authored normals, as per the USD spec. - Hd_VertexAdjacency adjacency; - adjacency.BuildAdjacencyTable(&refinedTopology); - refinedNormals = Hd_SmoothNormals::ComputeSmoothNormals(&adjacency, (int)refinedPoints.size(), refinedPoints.cdata()); - - // Compute refined texcoords, if required. - if (baseUVs.size() > 0) - { - // Create a new stencil table, if required. - if (uvInterpolationMode != stencilOptions.interpolationMode) - { - stencilOptions.interpolationMode = uvInterpolationMode; - stencilTable.reset(Far::StencilTableFactory::Create(*refiner, stencilOptions)); - } - - refinedUVs.resize(stencilTable->GetNumStencils()); - const GfVec2f* uvData = (uvFreq == UsdGeomTokens->faceVarying ? indexedUVs.cdata() : baseUVs.cdata()); - stencilTable->UpdateValues(reinterpret_cast(uvData), reinterpret_cast(refinedUVs.data())); - - if (uvFreq == UsdGeomTokens->faceVarying) - { - // Texcoord are face varying. Create flattened array of face-varying UVs, to match Falcor convention. - VtVec2fArray tmpUVs = std::move(refinedUVs); - refinedUVs.clear(); - for (uint32_t f = 0; f < bottomFaceCount; ++f) - { - const Far::ConstIndexArray uvs = bottomTopology.GetFaceFVarValues(f, 0); - std::for_each(uvs.begin(), uvs.end(), [&](const auto& idx){refinedUVs.push_back(tmpUVs[idx]);}); - } - } - } - - return true; -} -} diff --git a/Source/plugins/importers/USDImporter/Tessellation.cpp b/Source/plugins/importers/USDImporter/Tessellation.cpp new file mode 100644 index 000000000..631a42e64 --- /dev/null +++ b/Source/plugins/importers/USDImporter/Tessellation.cpp @@ -0,0 +1,537 @@ +/*************************************************************************** + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. + # + # Redistribution and use in source and binary forms, with or without + # modification, are permitted provided that the following conditions + # are met: + # * Redistributions of source code must retain the above copyright + # notice, this list of conditions and the following disclaimer. + # * Redistributions in binary form must reproduce the above copyright + # notice, this list of conditions and the following disclaimer in the + # documentation and/or other materials provided with the distribution. + # * Neither the name of NVIDIA CORPORATION nor the names of its + # contributors may be used to endorse or promote products derived + # from this software without specific prior written permission. + # + # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY + # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + **************************************************************************/ +#include "Tessellation.h" +#include "Core/Assert.h" +#include "Utils/Logger.h" +#include "Utils/Math/FNVHash.h" +#include "IndexedVector.h" + +#include +#include +#include +#include + +#include +#include +#include + +using namespace pxr; +using namespace OpenSubdiv; + +namespace Falcor +{ + +namespace +{ + +struct GfVec2fHash +{ + size_t operator()(const GfVec2f& v) const + { + FNVHash64 hash; + hash.insert(&v, sizeof(v)); + return hash.get(); + } +}; + +struct GfVec3fHash +{ + size_t operator()(const GfVec3f& v) const + { + FNVHash64 hash; + hash.insert(&v, sizeof(v)); + return hash.get(); + } +}; + +/** + * @brief Mesh construction helper class. + * + * Aggregates per-face (facet set) indices/positions/normals/uvs into indexed mesh data, sharing position data as possible. + * Note that normals are assumed to be per-vertex. + */ +class MeshIndexer +{ +public: + MeshIndexer(const TfToken& uvInterp) + : mUVInterp(uvInterp) + { + } + + // Add a face's worth of facets to the mesh. Assumes that the facets are triangles. + void addFacets(const std::vector& indices, const std::vector& positions, const std::vector& normals, const std::vector& uvs) + { + FALCOR_ASSERT((indices.size() % 3) == 0); + + // Ensure attribute interp == "none" iff attribute data size == 0 + FALCOR_ASSERT((mUVInterp == UsdGeomTokens->none) == (uvs.size() == 0)); + + const size_t positionCount = positions.size() / 3; + + // Table to map from given vertex index to mesh vertex index + std::vector indexMap; + + for (int i = 0, j = 0; i < positionCount; ++i, j += 3) + { + GfVec3f pos(positions[j+0], positions[j+1], positions[j+2]); + uint32_t idx; + if (mPositionSet.append(pos, idx)) + { + // If this was the first time this position was seen, also push normals, and uvs if they are per-vertex. + mNormals.push_back(GfVec3f(normals[j+0], normals[j+1], normals[j+2])); + + if (mUVInterp == UsdGeomTokens->vertex || mUVInterp == UsdGeomTokens->varying) + { + mUVs.push_back(GfVec2f(uvs[2*i+0], uvs[2*i+1])); + } + } + indexMap.push_back(idx); + } + + for (const auto& idx : indices) + { + mIndices.push_back(indexMap[idx]); + + // If uv attributes are face varying, append to the current index. + if (mUVInterp == UsdGeomTokens->faceVarying) + { + mUVs.push_back(GfVec2f(uvs[2*idx+0], uvs[2*idx+1])); + } + } + + if (mUVInterp == UsdGeomTokens->uniform) + { + mUVs.push_back(GfVec2f(uvs[0], uvs[1])); + } + } + + VtIntArray getIndices() { return mIndices; } + VtVec3fArray getPositions() { return mPositions.size() > 0 ? mPositions : mPositionSet.getValues(); } + VtVec2fArray getUVs() { return mUVs; } + VtVec3fArray getNormals() { return mNormals; } + + TfToken getUVInterp() const { return mUVInterp; } + +private: + TfToken mUVInterp; + IndexedVector mPositionSet; + VtIntArray mIndices; + VtVec3fArray mPositions; + VtVec3fArray mNormals; + VtVec2fArray mUVs; +}; + +Sdc::Options::FVarLinearInterpolation getFaceVaryingLinearInterpolation(const UsdGeomMesh& geomMesh) +{ + TfToken interp = getAttribute(geomMesh.GetFaceVaryingLinearInterpolationAttr(), UsdGeomTokens->cornersPlus1); + + if (interp == UsdGeomTokens->none) return Sdc::Options::FVarLinearInterpolation::FVAR_LINEAR_NONE; + else if (interp == UsdGeomTokens->cornersOnly) return Sdc::Options::FVarLinearInterpolation::FVAR_LINEAR_CORNERS_ONLY; + else if (interp == UsdGeomTokens->cornersPlus1) return Sdc::Options::FVarLinearInterpolation::FVAR_LINEAR_CORNERS_PLUS1; + else if (interp == UsdGeomTokens->cornersPlus2) return Sdc::Options::FVarLinearInterpolation::FVAR_LINEAR_CORNERS_PLUS2; + else if (interp == UsdGeomTokens->boundaries) return Sdc::Options::FVarLinearInterpolation::FVAR_LINEAR_BOUNDARIES; + else if (interp == UsdGeomTokens->all) return Sdc::Options::FVarLinearInterpolation::FVAR_LINEAR_ALL; + + logWarning("Unsupported face varying linear interpolation mode '{}' on '{}'.", interp.GetString(), geomMesh.GetPath().GetString()); + return Sdc::Options::FVarLinearInterpolation::FVAR_LINEAR_CORNERS_PLUS1; +} + +Sdc::Options::VtxBoundaryInterpolation getVertexBoundaryInterpolation(const UsdGeomMesh& geomMesh) +{ + TfToken interp = getAttribute(geomMesh.GetInterpolateBoundaryAttr(), UsdGeomTokens->edgeAndCorner); + + if (interp == UsdGeomTokens->none) return Sdc::Options::VtxBoundaryInterpolation::VTX_BOUNDARY_NONE; + else if (interp == UsdGeomTokens->edgeOnly) return Sdc::Options::VtxBoundaryInterpolation::VTX_BOUNDARY_EDGE_ONLY; + else if (interp == UsdGeomTokens->edgeAndCorner) return Sdc::Options::VtxBoundaryInterpolation::VTX_BOUNDARY_EDGE_AND_CORNER; + + logWarning("Unsupported vertex boundary interpolation mode '{}' on '{}'.", interp.GetString(), geomMesh.GetPath().GetString()); + return Sdc::Options::VtxBoundaryInterpolation::VTX_BOUNDARY_EDGE_AND_CORNER; +} + +/** + * Triangulate a mesh without applying any refinement. + * + * This is both faster than using the general-case tessellation path, and preserves ordering of vertices so that other constructs that + * rely on same (e.g., joint indices) will remain compatible post-triangulation. + * + * We use simple fan triangulation, which is only guaranteed to produce correct results on convex faces. + * Note that this is the same basic approach used by USD's HdMeshUtil::ComputeTriangleIndices(). + */ +UsdMeshData triangulate(const pxr::UsdGeomMesh& geomMesh, const UsdMeshData& baseMesh, pxr::VtIntArray& coarseFaceIndices) +{ + const std::string& meshName = geomMesh.GetPath().GetString(); + const auto& faceIndices = baseMesh.topology.faceIndices; + const auto& faceCounts = baseMesh.topology.faceCounts; + const size_t faceCount = faceCounts.size(); + + const bool leftHanded = baseMesh.topology.orient != UsdGeomTokens->rightHanded; + + VtVec3fArray outNormals; + if (baseMesh.normalInterp == UsdGeomTokens->vertex || baseMesh.normalInterp == UsdGeomTokens->varying) + { + outNormals = baseMesh.normals; + } + + // If there are no input normals, then as per the USD spec, we generate uniform face ("flat") normals. + bool generateNormals = baseMesh.normals.size() == 0; + + VtVec2fArray outUVs; + if (baseMesh.uvInterp == UsdGeomTokens->vertex || baseMesh.uvInterp == UsdGeomTokens->varying) + { + outUVs = baseMesh.uvs; + } + + // Sort the hole indices so that determining if a given face is a hole is a constant-time operation. + VtIntArray sortedHoleIndices = baseMesh.topology.holeIndices; + std::sort(sortedHoleIndices.begin(), sortedHoleIndices.end()); + const uint32_t holeFaceCount = sortedHoleIndices.size(); + + VtIntArray outFaceIndices; + + int next[2] = {1, 2}; + if (leftHanded) std::swap(next[0], next[1]); + + // Triangulate each face, f + // vertIdx is the offset into faceIndices to the first vertex index for the current face + for (uint32_t f = 0, vertIdx = 0, holeIdx = 0; f < faceCount; vertIdx += faceCounts[f], ++f) + { + uint32_t vertexCount = faceCounts[f]; + + if (holeIdx < holeFaceCount && sortedHoleIndices[holeIdx] == f) + { + // This face is a hole face; skip it. + ++holeIdx; + continue; + } + else if (vertexCount < 3) + { + // Skip degenerate faces + continue; + } + + GfVec3f flatNormal = {}; + if (generateNormals) + { + // Generate a uniform (per-original-face) normal to use for each triangle we generate. + const GfVec3f& v0 = baseMesh.points[faceIndices[vertIdx]]; + for (uint32_t j = 0; j < vertexCount - 2; ++j) + { + const GfVec3f& v1 = baseMesh.points[faceIndices[vertIdx + j + next[0]]]; + const GfVec3f& v2 = baseMesh.points[faceIndices[vertIdx + j + next[1]]]; + flatNormal += GfCross(v1 - v0, v2 - v0); + } + GfNormalize(&flatNormal); + } + + for (uint32_t v = 0; v < vertexCount - 2; ++v) + { + // Append a triplet of face indices, thereby adding a new triangle to the output. + // As we do so, append copies of any uniform or face-varying attributes to their respective + // outputs, since they are not indexed. + // In the case of vertex and varying attributes, we can simply re-use the base mesh values, since + // they are indexed along with the corresponding vertex. + outFaceIndices.push_back(faceIndices[vertIdx]); + outFaceIndices.push_back(faceIndices[vertIdx + v + next[0]]); + outFaceIndices.push_back(faceIndices[vertIdx + v + next[1]]); + + if (baseMesh.normalInterp == UsdGeomTokens->faceVarying) + { + outNormals.push_back(baseMesh.normals[vertIdx]); + outNormals.push_back(baseMesh.normals[vertIdx + v + next[0]]); + outNormals.push_back(baseMesh.normals[vertIdx + v + next[1]]); + } + + if (baseMesh.uvInterp == UsdGeomTokens->faceVarying) + { + outUVs.push_back(baseMesh.uvs[vertIdx]); + outUVs.push_back(baseMesh.uvs[vertIdx + v + next[0]]); + outUVs.push_back(baseMesh.uvs[vertIdx + v + next[1]]); + } + + if (generateNormals) + { + // Append the generated flat normal to the output + outNormals.push_back(flatNormal); + } + else if (baseMesh.normalInterp == UsdGeomTokens->uniform) + { + // Copy the appropriate input uniform normal to the output + outNormals.push_back(baseMesh.normals[f]); + } + + if (baseMesh.uvInterp == UsdGeomTokens->uniform) + { + // Copy the appropriate input uniform uv to the output + outUVs.push_back(baseMesh.uvs[f]); + } + + coarseFaceIndices.push_back(f); + } + } + + UsdMeshData tessellatedMesh; + tessellatedMesh.topology.scheme = UsdGeomTokens->none; + tessellatedMesh.topology.orient = baseMesh.topology.orient; + tessellatedMesh.topology.faceIndices = std::move(outFaceIndices); + tessellatedMesh.topology.faceCounts = VtIntArray(tessellatedMesh.topology.faceIndices.size() / 3, 3); + tessellatedMesh.normalInterp = generateNormals ? UsdGeomTokens->uniform : baseMesh.normalInterp; + tessellatedMesh.uvInterp = baseMesh.uvInterp; + tessellatedMesh.points = baseMesh.points; + tessellatedMesh.normals = std::move(outNormals); + tessellatedMesh.uvs = std::move(outUVs); + return tessellatedMesh; +} +} // anonymous namespace + +/** + * Tessellate a UsdGeomMesh, generating uv and normal attributes as required. + * + * If refinement (subdivision) is to be applied, OpenSubdiv is used to evalute the surface and related attributes. + * If no refinement is to be applied, we use a simple vertex-order-perserving triangulation scheme instead. + */ +UsdMeshData tessellate(const pxr::UsdGeomMesh& geomMesh, + const UsdMeshData& baseMesh, + uint32_t maxRefinementLevel, + pxr::VtIntArray& coarseFaceIndices) +{ + if (baseMesh.points.size() == 0 || baseMesh.topology.getNumFaces() == 0 || baseMesh.topology.faceIndices.size() == 0) + { + return UsdMeshData(); + } + + if (baseMesh.topology.scheme == UsdGeomTokens->none || maxRefinementLevel == 0) + { + return triangulate(geomMesh, baseMesh, coarseFaceIndices); + } + + typedef Bfr::RefinerSurfaceFactory<> SurfaceFactory; + typedef Bfr::Surface Surface; + typedef Bfr::SurfaceFactoryMeshAdapter::FVarID FVarID; + + OpenSubdiv::Sdc::SchemeType scheme = Sdc::SCHEME_CATMARK; + TfToken usdScheme = baseMesh.topology.scheme; + + uint32_t tessellationRate = maxRefinementLevel + 1; + + if (usdScheme == UsdGeomTokens->catmullClark) + { + scheme = Sdc::SCHEME_CATMARK; + } + else if (usdScheme == UsdGeomTokens->loop) + { + // Ensure that the input consists solely of triangles. + auto it = std::find_if(baseMesh.topology.faceIndices.begin(), baseMesh.topology.faceIndices.end(), [](int i) { return i != 3; }); + if (it != baseMesh.topology.faceIndices.end()) + { + logWarning("Cannot apply Loop subdivision to non-triangular mesh '{}'. Unrefined mesh will be used.", geomMesh.GetPath().GetString()); + return triangulate(geomMesh, baseMesh, coarseFaceIndices); + } + scheme = Sdc::SCHEME_LOOP; + } + else if (usdScheme == UsdGeomTokens->bilinear) + { + scheme = Sdc::SCHEME_BILINEAR; + } + else + { + logWarning("Unknown subdivision scheme: '{}' on mesh '{}'. Unrefined mesh will be used.", usdScheme.GetString(), geomMesh.GetPath().GetString()); + return triangulate(geomMesh, baseMesh, coarseFaceIndices); + } + + // Generate per-vertex normals on refined meshes, as per the USD spec. + TfToken normalInterp = UsdGeomTokens->vertex; + + if (baseMesh.normals.size() > 0) + { + logWarning("Ignoring authored normals on subdivided mesh '{}'.", geomMesh.GetPath().GetString()); + } + + OpenSubdiv::Sdc::Options options; + options.SetVtxBoundaryInterpolation(getVertexBoundaryInterpolation(geomMesh)); + options.SetFVarLinearInterpolation(getFaceVaryingLinearInterpolation(geomMesh)); + Far::TopologyRefinerFactory::Options refinerOptions(scheme, options); + + Far::TopologyDescriptor::FVarChannel channels; + + Far::TopologyDescriptor desc = {}; + desc.numVertices = baseMesh.topology.faceIndices.size(); + desc.numFaces = baseMesh.topology.getNumFaces(); + desc.numVertsPerFace = (const int*)baseMesh.topology.faceCounts.data(); + desc.vertIndicesPerFace = (Far::Index*)baseMesh.topology.faceIndices.data(); + desc.fvarChannels = &channels; // Channels will be initialized below if necessary. + + std::vector uvIndices; + VtVec2fArray indexedUVs; + + float const* uvData = (float*)baseMesh.uvs.data(); + TfToken uvInterp = uvData != nullptr ? baseMesh.uvInterp : UsdGeomTokens->none; + + IndexedVector indexedUVSet; + + if (baseMesh.uvs.size() > 0 && uvInterp == UsdGeomTokens->faceVarying) + { + // OpenSubdiv expects indexed face-varying UVs, whereas ours are unindexed. + // Construct unique set of UVs and indices. + for (auto& uv : baseMesh.uvs) + { + indexedUVSet.append(uv); + } + uvData = (float *)indexedUVSet.getValues().data(); + channels.numValues = indexedUVSet.getValues().size(); + channels.valueIndices = indexedUVSet.getIndices().data(); + ++desc.numFVarChannels; + } + + std::unique_ptr refiner(Far::TopologyRefinerFactory::Create(desc, refinerOptions)); + + SurfaceFactory::Options surfaceOptions; + SurfaceFactory surfaceFactory(*refiner, surfaceOptions); + + Surface vertexSurface; + Surface varyingSurface; + Surface fvarSurface; + + std::vector facePatchPoints; + std::vector outCoords; + std::vector outPos; + std::vector outDu, outDv; + std::vector outNormals; + std::vector outUV; + std::vector outFacets; + + Bfr::Tessellation::Options tessOptions; + // Facet size 3 => triangulate + tessOptions.SetFacetSize(3); + + bool leftHanded = baseMesh.topology.orient == UsdGeomTokens->leftHanded; + + // Note that normals on refined meshes are always per-vertex, and generated as part of + // the subdivision process, as per the USD spec. As such, any authored normals are ignored. + // Further, we do not need to handle e.g., face-varying or uniform normals here. + + MeshIndexer meshIndexer(uvInterp); + + const uint32_t faceCount = surfaceFactory.GetNumFaces(); + + coarseFaceIndices.clear(); + + bool createVaryingSurf = false; + + Surface* uvSurface = nullptr; + if (uvInterp == UsdGeomTokens->faceVarying) uvSurface = &fvarSurface; + else if (uvInterp == UsdGeomTokens->vertex) uvSurface = &vertexSurface; + else if (uvInterp == UsdGeomTokens->varying) + { + uvSurface = &varyingSurface; + createVaryingSurf = true; + } + + // Face-varying UVs always have ID of 0. It's safe to set this, and to create the face-varying surface, + // even if UVs aren't provided. + FVarID fvarID = 0; + + for (uint32_t f = 0; f < faceCount; ++f) + { + surfaceFactory.InitSurfaces(f, &vertexSurface, &fvarSurface, &fvarID, desc.numFVarChannels, createVaryingSurf ? &varyingSurface : nullptr); + if (!vertexSurface.IsValid()) continue; + // Fall back to using vertex surface if the uv surface is invalid for whatever reason. + if (uvSurface && !uvSurface->IsValid()) uvSurface = &vertexSurface; + + Bfr::Tessellation tessPattern(vertexSurface.GetParameterization(), tessellationRate, tessOptions); + + const int outCoordCount = tessPattern.GetNumCoords(); + + outCoords.resize(outCoordCount * 2); + tessPattern.GetCoords(outCoords.data()); + + outNormals.resize(outCoordCount * 3); + outPos.resize(outCoordCount * 3); + outDu.resize(outCoordCount * 3); + outDv.resize(outCoordCount * 3); + + if (uvSurface) + { + const int pointSize = 2; + facePatchPoints.resize(uvSurface->GetNumPatchPoints() * pointSize); + outUV.resize(outCoordCount * pointSize); + uvSurface->PreparePatchPoints(uvData, pointSize, facePatchPoints.data(), pointSize); + for (int i = 0, j = 0; i < outCoordCount; ++i, j += pointSize) + { + uvSurface->Evaluate(&outCoords[i * 2], facePatchPoints.data(), pointSize, &outUV[j]); + } + } + else if (uvInterp == UsdGeomTokens->uniform) + { + outUV.resize(2); + outUV[0] = baseMesh.uvs[f][0]; + outUV[1] = baseMesh.uvs[f][1]; + } + + const int pointSize = 3; + facePatchPoints.resize(vertexSurface.GetNumPatchPoints() * pointSize); + vertexSurface.PreparePatchPoints((float*)baseMesh.points.data(), pointSize, facePatchPoints.data(), pointSize); + { + float3 du; + float3 dv; + + // Compute positions and normals + for (int i = 0, j = 0; i < outCoordCount; ++i, j += pointSize) + { + vertexSurface.Evaluate(&outCoords[i * 2], facePatchPoints.data(), pointSize, &outPos[j], reinterpret_cast(&du), reinterpret_cast(&dv)); + // Use the partials to construct a normal vector. + float3 normal = normalize(cross(du, dv)); + if (leftHanded) normal = -normal; + outNormals[j+0] = normal.x; + outNormals[j+1] = normal.y; + outNormals[j+2] = normal.z; + } + } + + const int facetCount = tessPattern.GetNumFacets(); + outFacets.resize(facetCount * 3); + tessPattern.GetFacets(outFacets.data()); + + meshIndexer.addFacets(outFacets, outPos, outNormals, outUV); + + // Append the index of each facet's originating coarse face, f. + for (int i = 0; i < facetCount; ++i) coarseFaceIndices.push_back(f); + } + + UsdMeshData tessellatedMesh; + tessellatedMesh.topology.scheme = UsdGeomTokens->none; + tessellatedMesh.topology.orient = baseMesh.topology.orient; + tessellatedMesh.topology.faceIndices = meshIndexer.getIndices(); + tessellatedMesh.topology.faceCounts = VtIntArray(tessellatedMesh.topology.faceIndices.size() / 3, 3); + tessellatedMesh.normalInterp = UsdGeomTokens->vertex; + tessellatedMesh.uvInterp = meshIndexer.getUVInterp(); + tessellatedMesh.points = meshIndexer.getPositions(); + tessellatedMesh.normals = meshIndexer.getNormals(); + tessellatedMesh.uvs = meshIndexer.getUVs(); + return tessellatedMesh; +} +} diff --git a/Source/plugins/importers/USDImporter/Tessellation.h b/Source/plugins/importers/USDImporter/Tessellation.h new file mode 100644 index 000000000..d602fcd50 --- /dev/null +++ b/Source/plugins/importers/USDImporter/Tessellation.h @@ -0,0 +1,100 @@ +/*************************************************************************** + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. + # + # Redistribution and use in source and binary forms, with or without + # modification, are permitted provided that the following conditions + # are met: + # * Redistributions of source code must retain the above copyright + # notice, this list of conditions and the following disclaimer. + # * Redistributions in binary form must reproduce the above copyright + # notice, this list of conditions and the following disclaimer in the + # documentation and/or other materials provided with the distribution. + # * Neither the name of NVIDIA CORPORATION nor the names of its + # contributors may be used to endorse or promote products derived + # from this software without specific prior written permission. + # + # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY + # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + **************************************************************************/ +#include "USDHelpers.h" +#include "Utils.h" +#include "Core/Assert.h" + +BEGIN_DISABLE_USD_WARNINGS +#include +END_DISABLE_USD_WARNINGS + +namespace Falcor +{ + +/** + * Mesh topology information. + */ +struct MeshTopology +{ + MeshTopology() { } + MeshTopology(pxr::TfToken scheme, pxr::TfToken orient, VtIntArray& faceCounts, VtIntArray& faceIndices) + : scheme(scheme) + , orient(orient) + , faceCounts(faceCounts) + , faceIndices(faceIndices) + { + + } + pxr::TfToken scheme; ///< Subdivision scheme, "none", "catmullClark", etc. + pxr::TfToken orient; ///< Orientation, nominally "leftHanded" or "rightHanded" + pxr::VtIntArray faceCounts; ///< Per-face number of vertices. + pxr::VtIntArray faceIndices; ///< Per-face-vertex indices. + pxr::VtIntArray holeIndices; ///< Indices of hole faces (sorted, per USD spec). + + uint32_t getNumFaces() const { return faceCounts.size(); } + + VtVec3iArray getTriangleIndices() const + { + FALCOR_ASSERT((faceIndices.size() % 3) == 0); + + VtVec3iArray ret; + for (uint32_t i = 0; i < faceIndices.size(); i += 3) + { + ret.push_back(GfVec3i(faceIndices[i+0], faceIndices[i+1], faceIndices[i+2])); + } + return ret; + } +}; + +/** + * A Basic mesh, as represented using USD datatypes. + */ +struct UsdMeshData +{ + MeshTopology topology; ///< Topology + pxr::VtVec3fArray points; ///< Vertex positions + pxr::VtVec3fArray normals; ///< Shading normals + pxr::VtVec2fArray uvs; ///< Texture coordinates + pxr::TfToken normalInterp; ///< Normal interpolation mode (none, vertex, varying, faceVarying) + pxr::TfToken uvInterp; ///< Texture coordinate interpolatoin mode (none, vertex, varying, faceVarying) +}; + +/** + * @brief Tessellate a UsdMeshData into triangles + * + * @param[in] geomMesh UsdGeomMesh being tessellated; used to extract subdivision and tessellation attributes. + * @param[in] baseMesh Base mesh to tessellate. + * @param[in] maxRefinementLevel Maximum subdivision refinement level. Zero indicates no subdivision. + * @param[out] coarseFaceIndices Index of base face from which each output triangle derives. + * @return UsdMeshData containing tessellated results; points will be zero-length on failure. + */ +UsdMeshData tessellate(const pxr::UsdGeomMesh& geomMesh, + const UsdMeshData& baseMesh, + uint32_t maxRefinementLevel, + pxr::VtIntArray& coarseFaceIndices); +} diff --git a/Source/plugins/importers/USDImporter/USDHelpers.h b/Source/plugins/importers/USDImporter/USDHelpers.h index 31a10f0ec..577051965 100644 --- a/Source/plugins/importers/USDImporter/USDHelpers.h +++ b/Source/plugins/importers/USDImporter/USDHelpers.h @@ -27,7 +27,7 @@ **************************************************************************/ #pragma once -#ifdef _MSC_VER +#if defined(_MSC_VER) #define BEGIN_DISABLE_USD_WARNINGS \ __pragma( warning(push) ) \ __pragma( warning(disable : 4003) ) /* Not enough macro arguments */ \ @@ -37,6 +37,19 @@ __pragma( warning(disable : 5033) ) /* 'register' storage class specifier deprecated */ #define END_DISABLE_USD_WARNINGS \ __pragma( warning(pop) ) +#elif defined(__clang__) +#define BEGIN_DISABLE_USD_WARNINGS \ + __pragma( clang diagnostic push ) \ + __pragma( clang diagnostic ignored "-Wignored-attributes" ) +#define END_DISABLE_USD_WARNINGS \ + __pragma( clang diagnostic pop ) +#elif defined(__GNUC__) +#define BEGIN_DISABLE_USD_WARNINGS \ + _Pragma("GCC diagnostic push") \ + _Pragma("GCC diagnostic ignored \"-Wignored-attributes\"") + _Pragma("GCC diagnostic ignored \"-Wparentheses\"") +#define END_DISABLE_USD_WARNINGS \ + _Pragma("GCC diagnostic pop") #else #define BEGIN_DISABLE_USD_WARNINGS #define END_DISABLE_USD_WARNINGS diff --git a/Source/plugins/importers/USDImporter/USDImporter.cpp b/Source/plugins/importers/USDImporter/USDImporter.cpp index 57c4e05ea..0be186877 100644 --- a/Source/plugins/importers/USDImporter/USDImporter.cpp +++ b/Source/plugins/importers/USDImporter/USDImporter.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, 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,11 +33,12 @@ #include "Utils/Settings.h" #include "Scene/Importer.h" -#include +#include BEGIN_DISABLE_USD_WARNINGS #include #include +#include #include #include #include @@ -100,8 +101,7 @@ namespace Falcor if (prim.IsInstance() && !ctx.useInstanceProxies) { if (!checkPrim(prim, ctx.builder.getSettings())) continue; - - const UsdPrim protoPrim(prim.GetMaster()); + const UsdPrim protoPrim(prim.GetPrototype()); if (protoPrim.IsValid()) { @@ -131,8 +131,8 @@ namespace Falcor logDebug("Adding mesh '{}'.", primName); ctx.addMesh(prim); - rmcv::mat4 bindXform = ctx.getGeomBindTransform(prim); - ctx.addGeomInstance(primName, prim, rmcv::mat4(1.f), bindXform); + float4x4 bindXform = ctx.getGeomBindTransform(prim); + ctx.addGeomInstance(primName, prim, float4x4::identity(), bindXform); } else if (prim.IsA()) { @@ -143,7 +143,7 @@ namespace Falcor // TODO: Add support for curve instancing // Now we assume each curve has only one instance. - ctx.addCurveInstance(primName, prim, rmcv::mat4(1.f), ctx.nodeStack.back()); + ctx.addCurveInstance(primName, prim, float4x4::identity(), ctx.nodeStack.back()); } else if (prim.IsA()) { @@ -249,24 +249,26 @@ namespace Falcor } // Convert entries in the USD renderSettings dictionary to their Falcor equivalents, if any. - Scene::Metadata createMetadata(UsdStageRefPtr& pStage) + void setMetadata(UsdStageRefPtr& pStage, ImporterContext& ctx) { VtDictionary customLayerDict; if (!pStage->GetMetadata(TfToken("customLayerData"), &customLayerDict)) { // Not custom layer metadata - return {}; + return; } if (customLayerDict.find("renderSettings") == customLayerDict.end()) { // No render settings dictionary - return {}; + return; } // Found an OV custom rendering dictionary. Convert relevant parameters to Falcor equivalents. // If a param value isn't explicitly set in the renderSettings dictionary, use the default OV/Create value. + std::optional refinementLevel; + VtDictionary renderDict = customLayerDict["renderSettings"].Get(); Scene::Metadata meta; @@ -279,6 +281,7 @@ namespace Falcor meta.maxSpecularBounces = getMetadata(renderDict, "rtx:pathtracing:maxSpecularAndTransmissionBounces", (uint32_t)6); meta.maxTransmissionBounces = getMetadata(renderDict, "rtx:pathtracing:maxSpecularAndTransmissionBounces", (uint32_t)6); meta.maxVolumeBounces = getMetadata(renderDict, "rtx:pathtracing:maxVolumeBounces", (uint32_t)4); + refinementLevel = getMetadata(renderDict, "rtx:hydra:refinementLevel", (uint32_t)0); // Falcor's "0 bounce" includes GBuffer output (i.e, primary visibility), while Create's does not. // Further, each Falcor bounce includes NEE, while Create's path tracer's NEE requires an extra bounce. @@ -293,7 +296,9 @@ namespace Falcor meta.maxTransmissionBounces = std::max(meta.maxDiffuseBounces.value(), std::max(2U, meta.maxTransmissionBounces.value()) - 2U); meta.maxVolumeBounces = std::max(meta.maxDiffuseBounces.value(), std::max(2U, meta.maxVolumeBounces.value()) - 2U); - return meta; + ctx.defaultRefinementLevel = refinementLevel.value(); + + ctx.builder.setMetadata(meta); } std::unique_ptr USDImporter::create() @@ -301,7 +306,7 @@ namespace Falcor return std::make_unique(); } - void USDImporter::importScene(const std::filesystem::path& path, SceneBuilder& builder, const Dictionary& dict) + void USDImporter::importScene(const std::filesystem::path& path, SceneBuilder& builder, const pybind11::dict& dict) { if (!path.is_absolute()) throw ImporterError(path, "Expected absolute path."); @@ -314,7 +319,9 @@ namespace Falcor // Remove the diagnostic delegate from the TfDiagnosticMgr upon return. ScopeGuard removeDiagDelegate{ [&]() { TfDiagnosticMgr::GetInstance().RemoveDelegate(&diagnosticDelegate); } }; - ArGetResolver().ConfigureResolverForAsset(path.string()); + // Create resolver. + auto resolverContext = ArGetResolver().CreateDefaultContextForAsset(path.string()); + ArResolverContextBinder binder(resolverContext); UsdStageRefPtr pStage = UsdStage::Open(path.string()); if (!pStage) @@ -349,8 +356,7 @@ namespace Falcor ctx.builder.setCameraSpeed(0.025f * stageDiagonal * ctx.metersPerUnit); } - Scene::Metadata metadata = createMetadata(pStage); - ctx.builder.setMetadata(metadata); + setMetadata(pStage, ctx); timeReport.measure("Load scene settings"); @@ -359,11 +365,11 @@ namespace Falcor { // Create prototypes for all prototype prims. ctx.pushNodeStack(); - std::vector prototypes(pStage->GetMasters()); - for (const UsdPrim& rootPrim : prototypes) + std::vector prototypes(pStage->GetPrototypes()); + for (const UsdPrim& prim : prototypes) { - if (!checkPrim(rootPrim, ctx.builder.getSettings())) continue; - ctx.createPrototype(rootPrim); + if (!checkPrim(prim, ctx.builder.getSettings())) continue; + ctx.createPrototype(prim); } ctx.popNodeStack(); FALCOR_ASSERT(ctx.getNodeStackDepth() == 0); @@ -371,10 +377,10 @@ namespace Falcor // Initialize stage-to-Falcor transformation based on specified stage up and unit scaling which // accounts for any differences in scene units and 'up' orientation between the stage and Falcor - rmcv::mat4 rootXform = rmcv::scale(float3(ctx.metersPerUnit)); + float4x4 rootXform = math::matrixFromScaling(float3(ctx.metersPerUnit)); if (UsdGeomGetStageUpAxis(pStage) == UsdGeomTokens->z) { - rootXform = rmcv::eulerAngleX(glm::radians(-90.0f)) * rootXform; + rootXform = mul(math::matrixFromRotationX(math::radians(-90.0f)), rootXform); } else { @@ -398,11 +404,11 @@ namespace Falcor { // No camera specified; attempt to create a reasonable default float3 viewDir = normalize(float3(-1.f, -1.f, -1.f)); - float3 target = toGlm(stageCenter * ctx.metersPerUnit); + float3 target = toFalcor(stageCenter * ctx.metersPerUnit); float3 pos = target - (viewDir * stageDiagonal * 1.5f * ctx.metersPerUnit); float3 up(0.f, 1.f, 0.f); - Camera::SharedPtr pCamera = Camera::create("Default"); + ref pCamera = Camera::create("Default"); pCamera->setPosition(pos); pCamera->setTarget(target); pCamera->setUpVector(up); diff --git a/Source/plugins/importers/USDImporter/USDImporter.h b/Source/plugins/importers/USDImporter/USDImporter.h index a8721a0af..6b41dd522 100644 --- a/Source/plugins/importers/USDImporter/USDImporter.h +++ b/Source/plugins/importers/USDImporter/USDImporter.h @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -39,6 +39,6 @@ namespace Falcor static std::unique_ptr create(); - void importScene(const std::filesystem::path& path, SceneBuilder& builder, const Dictionary& dict) override; + void importScene(const std::filesystem::path& path, SceneBuilder& builder, const pybind11::dict& dict) override; }; } diff --git a/Source/plugins/importers/USDImporter/Utils.h b/Source/plugins/importers/USDImporter/Utils.h index 60057289c..7905e3103 100644 --- a/Source/plugins/importers/USDImporter/Utils.h +++ b/Source/plugins/importers/USDImporter/Utils.h @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -70,28 +70,28 @@ namespace Falcor } }; - inline float3 toGlm(const GfVec3f& v) + inline float3 toFalcor(const GfVec3f& v) { return float3(v[0], v[1], v[2]); } - inline float3 toGlm(const GfVec3d& v) + inline float3 toFalcor(const GfVec3d& v) { return float3(v[0], v[1], v[2]); } - inline float3 toGlm(const GfVec3h& v) + inline float3 toFalcor(const GfVec3h& v) { return float3(v[0], v[1], v[2]); } - inline rmcv::mat4 toRMCV(const GfMatrix4d& m) + inline float4x4 toFalcor(const GfMatrix4d& m) { // USD uses row-major matrices and row vectors, which are pre-multiplied (v * M) with a matrix to perform a transformation. - // Falcor RMCV uses row-major matrices and column vectors, which are post-multiplied (M * v). + // Falcor uses row-major matrices and column vectors, which are post-multiplied (M * v). // As such, we transpose USD matrices upon import. // (Same as for GLM, except we have to write the values actually transposed, rather than using the col-major layout of the constructor) - return rmcv::mat4({ + return float4x4({ m[0][0], m[1][0], m[2][0], m[3][0], m[0][1], m[1][1], m[2][1], m[3][1], m[0][2], m[1][2], m[2][2], m[3][2], @@ -101,20 +101,20 @@ namespace Falcor inline SceneBuilder::Node makeNode(const std::string& name, NodeID parentId = NodeID::Invalid()) { - return SceneBuilder::Node{name, rmcv::mat4(1.f), rmcv::mat4(1.f), rmcv::mat4(1.f), parentId}; + return SceneBuilder::Node{name, float4x4::identity(), float4x4::identity(), float4x4::identity(), parentId}; } - inline SceneBuilder::Node makeNode(const std::string& name, const rmcv::mat4& xform, const rmcv::mat4& bindTransform, NodeID parentId = NodeID::Invalid()) + inline SceneBuilder::Node makeNode(const std::string& name, const float4x4& xform, const float4x4& bindTransform, NodeID parentId = NodeID::Invalid()) { - return SceneBuilder::Node{name, xform, bindTransform, rmcv::mat4(1.f), parentId}; + return SceneBuilder::Node{name, xform, bindTransform, float4x4::identity(), parentId}; } - inline bool getLocalTransform(const UsdGeomXformable& xformable, rmcv::mat4& xform) + inline bool getLocalTransform(const UsdGeomXformable& xformable, float4x4& xform) { bool resets = false; GfMatrix4d transform; xformable.GetLocalTransformation(&transform, &resets, UsdTimeCode::EarliestTime()); - xform = toRMCV(transform); + xform = toFalcor(transform); return resets; } @@ -131,6 +131,25 @@ namespace Falcor return val; } + // Helper function that returns the main attribute if it is authored (non-default), + // otherwise it tries to retrieve the fallback attribute and should that fail, the default value. + // This is used to provide compatibility, e.g. when the default disk light radius changed from "radius" to "inputs:radius" + template + inline T getAuthoredAttribute(const UsdAttribute& mainAttrib, const UsdAttribute& fallbackAttrib, const T& def) + { + T val = def; + if (mainAttrib && mainAttrib.IsAuthored()) + { + mainAttrib.Get(&val, UsdTimeCode::EarliestTime()); + } + else if(fallbackAttrib) + { + fallbackAttrib.Get(&val, UsdTimeCode::EarliestTime()); + } + return val; + } + + using AttributeFrequency = SceneBuilder::Mesh::AttributeFrequency; inline size_t computeElementCount(AttributeFrequency freq, size_t faceCount, size_t vertexCount) diff --git a/build_scripts/deploycommon.bat b/build_scripts/deploycommon.bat index c87d498d4..e0e93204b 100644 --- a/build_scripts/deploycommon.bat +++ b/build_scripts/deploycommon.bat @@ -21,11 +21,11 @@ if %IsDebug% EQU 0 ( robocopy %ExtDir%\deps\bin\ %OutDir% /E /r:0 >nul ) else ( robocopy %ExtDir%\deps\debug\bin\ %OutDir% /E /r:0 >nul - robocopy %ExtDir%\deps\bin\ %OutDir% assimp-vc142-mt.* /r:0 >nul + robocopy %ExtDir%\deps\bin\ %OutDir% assimp-vc143-mt.* /r:0 >nul rem Needed for OpenVDB (debug version links to release version of Half_2.5) robocopy %ExtDir%\deps\bin\ %OutDir% Half-2_5.* /r:0 >nul ) -robocopy %ExtDir%\python\ %OutDir% python37.dll /r:0 >nul +robocopy %ExtDir%\python\ %OutDir% python*.dll /r:0 >nul robocopy %ExtDir%\python %OutDir%\pythondist /E /r:0 >nul robocopy %ExtDir%\slang\bin\windows-x64\%SlangDir% %OutDir% *.dll /r:0 >nul robocopy %ExtDir%\pix\bin\x64 %OutDir% WinPixEventRuntime.dll /r:0 >nul diff --git a/build_scripts/deploycommon.sh b/build_scripts/deploycommon.sh index b41816230..c0f24fc8d 100644 --- a/build_scripts/deploycommon.sh +++ b/build_scripts/deploycommon.sh @@ -6,52 +6,68 @@ # $4 -> Slang build configuration # $5 -> DLSS directory -ExtDir=$1/external/packman/ -OutDir=$2 +EXT_DIR=$1/external/packman/ +OUT_DIR=$2 -IsDebug=false +IS_DEBUG=false if [ "$3" = "Debug" ]; then - IsDebug=true + IS_DEBUG=true fi -SlangDir=$4 +SLANG_DIR=$4 # Copy externals -if [ "${IsDebug}" = false ]; then - cp -frp ${ExtDir}/deps/lib/*.so* ${OutDir} +if [ "${IS_DEBUG}" = false ]; then + cp -frp ${EXT_DIR}/deps/lib/*.so* ${OUT_DIR} else - cp -frp ${ExtDir}/deps/debug/lib/*.so* ${OutDir} - cp -fp ${ExtDir}/deps/lib/libassimp.so* ${OutDir} - cp -fp ${ExtDir}/deps/lib/libtbb.so* ${OutDir} + cp -frp ${EXT_DIR}/deps/debug/lib/*.so* ${OUT_DIR} fi -cp -fp ${ExtDir}/python/lib/libpython*.so* ${OutDir} -mkdir -p ${OutDir}/pythondist -cp -frp ${ExtDir}/python/* ${OutDir}/pythondist +cp -fp ${EXT_DIR}/python/lib/libpython*.so* ${OUT_DIR} +mkdir -p ${OUT_DIR}/pythondist +cp -frp ${EXT_DIR}/python/* ${OUT_DIR}/pythondist # Copy slang -cp -f ${ExtDir}/slang/bin/linux-x64/${SlangDir}/lib*.so ${OutDir} +cp -f ${EXT_DIR}/slang/bin/linux-x64/${SLANG_DIR}/lib*.so ${OUT_DIR} + +# Copy CUDA +CUDA_DIR=${EXT_DIR}/cuda +if [ -d ${CUDA_DIR} ]; then + cp -fp ${CUDA_DIR}/lib64/libcudart.so* ${OUT_DIR} + cp -fp ${CUDA_DIR}/lib64/libnvrtc.so* ${OUT_DIR} + cp -fp ${CUDA_DIR}/lib64/libcublas.so* ${OUT_DIR} + cp -fp ${CUDA_DIR}/lib64/libcurand.so* ${OUT_DIR} +fi # Copy RTXDI SDK shaders -RtxdiSDKDir=${ExtDir}/rtxdi/rtxdi-sdk/include/rtxdi -RtxdiSDKTargetDir=${OutDir}/shaders/rtxdi -if [ -d ${RtxdiSDKDir} ]; then - mkdir -p ${RtxdiSDKTargetDir} - cp ${RtxdiSDKDir}/ResamplingFunctions.hlsli ${RtxdiSDKTargetDir} - cp ${RtxdiSDKDir}/Reservoir.hlsli ${RtxdiSDKTargetDir} - cp ${RtxdiSDKDir}/RtxdiHelpers.hlsli ${RtxdiSDKTargetDir} - cp ${RtxdiSDKDir}/RtxdiMath.hlsli ${RtxdiSDKTargetDir} - cp ${RtxdiSDKDir}/RtxdiParameters.h ${RtxdiSDKTargetDir} - cp ${RtxdiSDKDir}/RtxdiTypes.h ${RtxdiSDKTargetDir} +RTXDI_DIR=${EXT_DIR}/rtxdi/rtxdi-sdk/include/rtxdi +RTXDI_TARGET_DIR=${OUT_DIR}/shaders/rtxdi +if [ -d ${RTXDI_DIR} ]; then + mkdir -p ${RTXDI_TARGET_DIR} + cp ${RTXDI_DIR}/ResamplingFunctions.hlsli ${RTXDI_TARGET_DIR} + cp ${RTXDI_DIR}/Reservoir.hlsli ${RTXDI_TARGET_DIR} + cp ${RTXDI_DIR}/RtxdiHelpers.hlsli ${RTXDI_TARGET_DIR} + cp ${RTXDI_DIR}/RtxdiMath.hlsli ${RTXDI_TARGET_DIR} + cp ${RTXDI_DIR}/RtxdiParameters.h ${RTXDI_TARGET_DIR} + cp ${RTXDI_DIR}/RtxdiTypes.h ${RTXDI_TARGET_DIR} fi # Copy NanoVDB -NanoVDBDir=${ExtDir}/nanovdb -NanoVDBTargetDir=${OutDir}/shaders/nanovdb -if [ -d ${NanoVDBDir} ]; then - mkdir -p ${NanoVDBTargetDir} - cp ${NanoVDBDir}/include/nanovdb/PNanoVDB.h ${NanoVDBTargetDir} +NANOVDB_DIR=${EXT_DIR}/nanovdb +NANOVDB_TARGET_DIR=${OUT_DIR}/shaders/nanovdb +if [ -d ${NANOVDB_DIR} ]; then + mkdir -p ${NANOVDB_TARGET_DIR} + cp ${NANOVDB_DIR}/include/nanovdb/PNanoVDB.h ${NANOVDB_TARGET_DIR} +fi + +# Copy USD +if [ "${IS_DEBUG}" = false ]; then + cp -fp ${EXT_DIR}/nv-usd-release/lib/libusd_ms.so ${OUT_DIR} + cp -frp ${EXT_DIR}/nv-usd-release/lib/usd ${OUT_DIR}/usd +else + cp -fp ${EXT_DIR}/nv-usd-debug/lib/libusd_ms.so ${OUT_DIR} + cp -frp ${EXT_DIR}/nv-usd-debug/lib/usd ${OUT_DIR}/usd fi # Copy NVTT -cp ${ExtDir}/nvtt/libcudart.so.11.0 ${OutDir} -cp ${ExtDir}/nvtt/libnvtt.so ${OutDir} +cp ${EXT_DIR}/nvtt/libcudart.so.11.0 ${OUT_DIR} +cp ${EXT_DIR}/nvtt/libnvtt.so ${OUT_DIR} diff --git a/build_scripts/falcor__init__.py b/build_scripts/falcor__init__.py new file mode 100644 index 000000000..6db661e52 --- /dev/null +++ b/build_scripts/falcor__init__.py @@ -0,0 +1,11 @@ +import sys as _sys +import os as _os + +if _os.name == "nt" and _sys.version_info >= (3, 8): + falcor_dir = _os.path.abspath(_os.path.join(_os.path.dirname(__file__), "../..")) + _os.add_dll_directory(falcor_dir) + del falcor_dir + +del _sys, _os + +from .falcor_ext import * diff --git a/build_scripts/generate_stubs.py b/build_scripts/generate_stubs.py index 667b9b179..2eecd1861 100644 --- a/build_scripts/generate_stubs.py +++ b/build_scripts/generate_stubs.py @@ -1,366 +1,6 @@ -# This script is heavily inspired by mitsuba3: -# https://github.com/mitsuba-renderer/mitsuba3/blob/master/resources/generate_stub_files.py - -# Copyright (c) 2017 Wenzel Jakob , All rights reserved. - -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: - -# 1. Redistributions of source code must retain the above copyright notice, this -# list of conditions and the following disclaimer. - -# 2. Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. - -# 3. Neither the name of the copyright holder nor the names of its contributors -# may be used to endorse or promote products derived from this software -# without specific prior written permission. - -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -# You are under no obligation whatsoever to provide any bug fixes, patches, or -# upgrades to the features, functionality or performance of the source code -# ("Enhancements") to anyone; however, if you choose to make your Enhancements -# available either publicly, or directly to the author of this software, without -# imposing a separate written license agreement for such Enhancements, then you -# hereby grant the following license: a non-exclusive, royalty-free perpetual -# license to install, use, modify, prepare derivative works, incorporate into -# other computer software, distribute, and sublicense such enhancements or -# derivative works thereof, in binary and source code form. - -""" -Usage: generate_stubs.py {package_dir} -This script generates stub files for Python type information for the `falcor` -module. It writes all the objects (classes, methods, functions, enums, etc.) -it finds to the `package_dir` folder. The stub files contain both the signatures -and the docstrings of the objects. -""" - -import os import sys -import json -import logging -import inspect -import re -import time - -# ------------------------------------------------------------------------------ - -buffer = "" - - -def w(s): - global buffer - buffer += f"{s}\n" - - -# ------------------------------------------------------------------------------ - - -def process_type_hint(s) -> str: - sub = s - type_hints = [] - offset = 0 - while True: - match = re.search(r"[a-zA-Z]+: ", sub) - if match is None: - return s - i = match.start() + len(match.group()) - match_next = re.search(r"[a-zA-Z]+: ", sub[i:]) - if match_next is None: - j = sub.index(")") - else: - j = i + match_next.start() - 2 - - type_hints.append((offset + i, offset + j, sub[i:j])) - - if match_next is None: - break - - offset = offset + j - sub = s[offset:] - - offset = 0 - result = "" - for t in type_hints: - result += s[offset : t[0] - 2] - offset = t[1] - # if the type hint is valid, then add it as well - if not ("::" in t[2]): - result += f": {t[2]}" - result += s[offset:] - - # Check is return type hint is not valid - if "::" in result[result.index(" -> ") :]: - result = result[: result.index(" -> ")] - - return result - - -# ------------------------------------------------------------------------------ - - -def process_properties(name, p, indent=0): - indent = " " * indent - - if not p is None: - w(f"{indent}{name} = ...") - if not p.__doc__ is None: - doc = p.__doc__.splitlines() - if len(doc) == 1: - w(f'{indent}"{doc[0]}"') - elif len(doc) > 1: - w(f'{indent}"""') - for l in doc: - w(f"{indent}{l}") - w(f'{indent}"""') - - -# ------------------------------------------------------------------------------ - - -def process_enums(name, e, indent=0): - indent = " " * indent - - if not e is None: - w(f"{indent}{name} = {int(e)}") - - if not e.__doc__ is None: - doc = e.__doc__.splitlines() - w(f'{indent}"""') - for l in doc: - if l.startswith(f" {name}"): - w(f"{indent}{l}") - w(f'{indent}"""') - - -# ------------------------------------------------------------------------------ - - -def process_class(m, c): - methods = [] - py_methods = [] - properties = [] - enums = [] - - for k in dir(c): - # Skip private attributes - if k.startswith("_"): - continue - if k.endswith("_"): - continue - - v = getattr(c, k) - if type(v).__name__ == "instancemethod": - methods.append((k, v)) - elif type(v).__name__ == "function" and v.__code__.co_varnames[0] == "self": - py_methods.append((k, v)) - elif type(v).__name__ == "property": - properties.append((k, v)) - elif str(v).endswith(k): - enums.append((k, v)) - - base = c.__bases__[0] - base_module = base.__module__ - base_name = base.__qualname__ - has_base = not ( - base_module == "builtins" - or base_name == "object" - or base_name == "pybind11_object" - ) - - base_name = base_module + "." + base_name - base_name = base_name.replace(m.__name__ + ".", "") - - w(f'class {c.__name__}{"(" + base_name + ")" if has_base else ""}:') - if c.__doc__ is not None: - doc = c.__doc__.splitlines() - if len(doc) > 0: - if doc[0].strip() == "": - doc = doc[1:] - if c.__doc__: - w(f' """') - for l in doc: - w(f" {l}") - w(f' """') - w(f"") - - process_function(m, c.__init__, indent=4) - process_function(m, c.__call__, indent=4) - - if len(properties) > 0: - for k, v in properties: - process_properties(k, v, indent=4) - w(f"") - - if len(enums) > 0: - for k, v in sorted(enums, key=lambda item: int(item[1])): - process_enums(k, v, indent=4) - w(f"") - - for k, v in methods: - process_function(m, v, indent=4) - - # for k, v in py_methods: - # process_py_function(k, v, indent=4) - w(f" ...") - w("") - - -# ------------------------------------------------------------------------------ - - -def process_function(m, f, indent=0): - indent = " " * indent - if f is None or f.__doc__ is None: - return - - overloads = [] - for l in f.__doc__.splitlines(): - if ") -> " in l: - l = process_type_hint(l) - l = l.replace(m.__name__ + ".", "") - overloads.append((l, [])) - else: - if len(overloads) > 0: - overloads[-1][1].append(l) - - for l, doc in overloads: - has_doc = len(doc) > 1 - - # Overload? - if l[1] == ".": - w(f"{indent}@overload") - w(f"{indent}def {l[3:]}:{'' if has_doc else ' ...'}") - else: - w(f"{indent}def {l}:{'' if has_doc else ' ...'}") - - if len(doc) > 1: # first line is always empty - w(f'{indent} """') - for l in doc[1:]: - w(f"{indent} {l}") - w(f'{indent} """') - w(f"{indent} ...") - w(f"") - - w(f"") - - -# ------------------------------------------------------------------------------ - - -def process_py_function(name, obj, indent=0): - indent = " " * indent - if obj is None: - return - - has_doc = obj.__doc__ is not None - - signature = str(inspect.signature(obj)) - signature = signature.replace("'", "") - - # Fix parameters that have enums as default values - enum_match = re.search(r"\=<", signature) - while enum_match is not None: - begin = enum_match.start() - end = begin + signature[begin:].index(">") - - new_default_value = signature[begin + 2 : end] - new_default_value = new_default_value[: new_default_value.index(":")] - - signature = signature[: begin + 1] + new_default_value + signature[end + 1 :] - enum_match = re.search(r"\=<", signature[begin]) - - w(f"{indent}def {name}{signature}:{'' if has_doc else ' ...'}") - - if has_doc: - doc = obj.__doc__.splitlines() - if len(doc) > 0: # first line is always empty - w(f'{indent} """') - for l in doc: - w(f"{indent} {l.strip()}") - w(f'{indent} """') - w(f"{indent} ...") - w(f"") - - -# ------------------------------------------------------------------------------ - - -def process_builtin_type(type, name): - w(f"class {name}: ...") - w(f"") - - -# ------------------------------------------------------------------------------ - - -def process_module(m): - global buffer - - module_name = m.__name__ - logging.info(f"Processing module '{module_name}' ...") - - submodules = [] - buffer = "" - - w(f"# This file is auto-generated by {os.path.split(__file__)[1]}") - w(f"import os") - w(f"from typing import Any, List, Optional, overload") - w(f"") - - for k in dir(m): - v = getattr(m, k) - - if inspect.isclass(v): - logging.debug(f"Found class '{k}'") - if hasattr(v, "__module__") and not (v.__module__.startswith(module_name)): - if v in [bool, int, float]: - process_builtin_type(v, k) - continue - process_class(m, v) - elif type(v).__name__ in ["method", "function"]: - logging.debug(f"Found python function '{k}'") - # process_py_function(k, v) - elif type(v).__name__ == "builtin_function_or_method": - logging.debug(f"Found function '{k}'") - process_function(m, v) - elif type(v) in [str, bool, int, float]: - logging.debug(f"Found property '{k}'") - if k.startswith("_"): - continue - process_properties(k, v) - # elif type(v).__bases__[0].__name__ == "module" or type(v).__name__ == "module": - elif type(v).__name__ == "module": - logging.debug(f"Found module '{k}'") - - w("") - w(f"from . import {v.__name__[len(module_name) + 1:]}") - w("") - - submodules.append(v) - - if module_name != "falcor": - w("") - w(f"from {'.' * (module_name.count('.') + 2)} import falcor") - w("") - pass - - return buffer, submodules - - -# ------------------------------------------------------------------------------ +from pybind11_stubgen import main if __name__ == "__main__": if len(sys.argv) < 2: @@ -368,39 +8,4 @@ def process_module(m): "One argument expected: the falcor python library directory." ) package_dir = sys.argv[1] - - start = time.time() - - logging.basicConfig(level=logging.INFO, format="%(levelname)s: %(message)s") - logging.info(f"Generating python stub files in '{package_dir}'") - - import falcor - - logging.debug("Loading plugins ...") - plugins = json.load( - open(os.path.join(package_dir, "../plugins/plugins.json"), "r") - ) - for plugin in plugins: - logging.debug(f"Loading plugin '{plugin}'") - falcor.loadPlugin(plugin) - - # Process modules. - modules = [falcor] - processed_modules = set() - while len(modules) > 0: - m = modules[0] - modules = modules[1:] - if m in processed_modules: - continue - - buffer, submodules = process_module(m) - - module_dir = os.path.join(package_dir, m.__name__.replace(".", os.path.sep)) - os.makedirs(module_dir, exist_ok=True) - open(os.path.join(module_dir, "__init__.pyi"), "w").write(buffer) - - modules += submodules - processed_modules.add(m) - - elapsed = time.time() - start - logging.debug("Done ({:.2f} ms)".format(elapsed * 1000)) + main(["-o", package_dir, "--ignore-invalid=all", "--skip-signature-downgrade", "--no-setup-py", "--root-module-suffix=", "falcor"]) diff --git a/build_scripts/pybind11_stubgen.py b/build_scripts/pybind11_stubgen.py new file mode 100644 index 000000000..955782e94 --- /dev/null +++ b/build_scripts/pybind11_stubgen.py @@ -0,0 +1,1224 @@ +""" +Source: https://github.com/sizmailov/pybind11-stubgen + +Copyright (c) 2019 Sergei Izmailov , All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +You are under no obligation whatsoever to provide any bug fixes, patches, or +upgrades to the features, functionality or performance of the source code +("Enhancements") to anyone; however, if you choose to make your Enhancements +available either publicly, or directly to the author of this software, without +imposing a separate written license agreement for such Enhancements, then you +hereby grant the following license: a non-exclusive, royalty-free perpetual +license to install, use, modify, prepare derivative works, incorporate into +other computer software, distribute, and sublicense such enhancements or +derivative works thereof, in binary and source code form. +""" + +import ast +import importlib +import inspect +import itertools +import logging +import os +import re +import sys +import warnings +from argparse import ArgumentParser +from typing import Any, Callable, Dict, List, Optional, Set + +logger = logging.getLogger(__name__) + +_visited_objects = [] + +# A list of function docstring pre-processing hooks +function_docstring_preprocessing_hooks: List[Callable[[str], str]] = [] + + +def _find_str_end(s, start): + for i in range(start + 1, len(s)): + c = s[i] + if c == "\\": # skip escaped chars + continue + if c == s[start]: + return i + return -1 + + +def _is_balanced(s): + closing = {"(": ")", "{": "}", "[": "]"} + + stack = [] + i = 0 + while i < len(s): + c = s[i] + if c in "\"'": + # TODO: handle triple-quoted strings too + i = _find_str_end(s, i) + if i < 0: + return False + if c in closing: + stack.append(closing[c]) + elif stack and stack[-1] == c: + stack.pop() + i += 1 + + return len(stack) == 0 + + +class DirectoryWalkerGuard(object): + def __init__(self, dirname): + self.dirname = dirname + self.origin = os.getcwd() + + def __enter__(self): + if not os.path.exists(self.dirname): + os.makedirs(self.dirname) + + assert os.path.isdir(self.dirname) + os.chdir(self.dirname) + + def __exit__(self, exc_type, exc_val, exc_tb): + os.chdir(self.origin) + + +_default_pybind11_repr_re = re.compile( + r"(<(?P\w+(\.\w+)*) object at 0x[0-9a-fA-F]+>)|" + r"(<(?P\w+(.\w+)*): -?\d+>)" +) + + +def replace_default_pybind11_repr(line): + default_reprs = [] + + def replacement(m): + if m.group("class"): + default_reprs.append(m.group(0)) + return "..." + return m.group("enum") + + return default_reprs, _default_pybind11_repr_re.sub(replacement, line) + + +class FunctionSignature(object): + # When True don't raise an error when invalid signatures/defaultargs are + # encountered (yes, global variables, blame me) + ignore_invalid_signature = False + ignore_invalid_defaultarg = False + + signature_downgrade = True + + # Number of invalid default values found so far + n_invalid_default_values = 0 + + # Number of invalid signatures found so far + n_invalid_signatures = 0 + + @classmethod + def n_fatal_errors(cls): + return ( + 0 if cls.ignore_invalid_defaultarg else cls.n_invalid_default_values + ) + (0 if cls.ignore_invalid_signature else cls.n_invalid_signatures) + + def __init__(self, name, args="*args, **kwargs", rtype="None", validate=True): + self.name = name + self.args = args + self.rtype = rtype + + if validate: + invalid_defaults, self.args = replace_default_pybind11_repr(self.args) + if invalid_defaults: + FunctionSignature.n_invalid_default_values += 1 + lvl = ( + logging.WARNING + if FunctionSignature.ignore_invalid_defaultarg + else logging.ERROR + ) + logger.log( + lvl, "Default argument value(s) replaced with ellipses (...):" + ) + for invalid_default in invalid_defaults: + logger.log(lvl, " {}".format(invalid_default)) + + function_def_str = "def {sig.name}({sig.args}) -> {sig.rtype}: ...".format( + sig=self + ) + try: + ast.parse(function_def_str) + except SyntaxError as e: + FunctionSignature.n_invalid_signatures += 1 + if FunctionSignature.signature_downgrade: + self.name = name + self.args = "*args, **kwargs" + self.rtype = "typing.Any" + lvl = ( + logging.WARNING + if FunctionSignature.ignore_invalid_signature + else logging.ERROR + ) + logger.log( + lvl, + "Generated stubs signature is degraded to `(*args, **kwargs) -> typing.Any` for", + ) + else: + lvl = logging.WARNING + logger.warning("Ignoring invalid signature:") + logger.log(lvl, function_def_str) + logger.log(lvl, " " * (e.offset - 1) + "^-- Invalid syntax") + + def __eq__(self, other): + return isinstance(other, FunctionSignature) and ( + self.name, + self.args, + self.rtype, + ) == (other.name, other.args, other.rtype) + + def __hash__(self): + return hash((self.name, self.args, self.rtype)) + + def split_arguments(self): + if len(self.args.strip()) == 0: + return [] + + prev_stop = 0 + brackets = 0 + splitted_args = [] + + for i, c in enumerate(self.args): + if c == "[": + brackets += 1 + elif c == "]": + brackets -= 1 + assert brackets >= 0 + elif c == "," and brackets == 0: + splitted_args.append(self.args[prev_stop:i]) + prev_stop = i + 1 + + splitted_args.append(self.args[prev_stop:]) + assert brackets == 0 + return splitted_args + + @staticmethod + def argument_type(arg): + return arg.split(":")[-1].strip() + + def get_all_involved_types(self): + types = [] + for t in [self.rtype] + self.split_arguments(): + types.extend( + [ + m[0] + for m in re.findall( + r"([a-zA-Z_][a-zA-Z0-9_]*(\.[a-zA-Z_][a-zA-Z0-9_]*)*)", + self.argument_type(t), + ) + ] + ) + return types + + +class PropertySignature(object): + NONE = 0 + READ_ONLY = 1 + WRITE_ONLY = 2 + READ_WRITE = READ_ONLY | WRITE_ONLY + + def __init__(self, rtype, setter_args, access_type): + self.rtype = rtype + self.setter_args = setter_args + self.access_type = access_type + + @property + def setter_arg_type(self): + return FunctionSignature.argument_type( + FunctionSignature("name", self.setter_args).split_arguments()[1] + ) + + +# If true numpy.ndarray[int32[3,3]] will be reduced to numpy.ndarray +BARE_NUPMY_NDARRAY = False + + +def replace_numpy_array(match_obj): + if BARE_NUPMY_NDARRAY: + return "numpy.ndarray" + numpy_type = match_obj.group("type") + # pybind always append size of data type + if numpy_type in [ + "int8", + "int16", + "int32", + "int64", + "float16", + "float32", + "float64", + "complex32", + "complex64", + "longcomplex", + ]: + numpy_type = "numpy." + numpy_type + + shape = match_obj.group("shape") + if shape: + shape = ", _Shape[{}]".format(shape) + else: + shape = "" + result = r"numpy.ndarray[{type}{shape}]".format(type=numpy_type, shape=shape) + return result + + +def replace_typing_types(match): + # pybind used to have iterator/iterable in place of Iterator/Iterable + name = match.group("type") + capitalized = name[0].capitalize() + name[1:] + return "typing." + capitalized + +def remove_trailing_brackets(match): + return match.group("type") + +class StubsGenerator(object): + INDENT = " " * 4 + + GLOBAL_CLASSNAME_REPLACEMENTS = { + re.compile( + r"numpy.ndarray\[(?P[^\[\]]+)(\[(?P[^\[\]]+)\])?(?P[^][]*)\]" + ): replace_numpy_array, + re.compile( + r"(?Callable|Dict|[Ii]terator|[Ii]terable|List" + r"|Optional|Set|Tuple|Union|ItemsView|KeysView|ValuesView)(?!\w)" + ): replace_typing_types, + # Falcor: Replace trailing brackets in tensor type annotations + re.compile( + r"(?Pndarray|Tensor)\[\]" + ): remove_trailing_brackets, + } + + def parse(self): + raise NotImplementedError + + def to_lines(self): # type: () -> List[str] + raise NotImplementedError + + @staticmethod + def _indent(line): # type: (str) -> str + return StubsGenerator.INDENT + line + + @staticmethod + def indent(lines): # type: (str) -> str + lines = lines.split("\n") + lines = [StubsGenerator._indent(l) if l else l for l in lines] + return "\n".join(lines) + + @staticmethod + def is_valid_module(module_name): # type: (str) -> bool + import importlib.util + + try: + return importlib.import_module(module_name) is not None + except ModuleNotFoundError: + return False + + @staticmethod + def fully_qualified_name(klass): + module_name = klass.__module__ if hasattr(klass, "__module__") else None + class_name = getattr(klass, "__qualname__", klass.__name__) + + if module_name == "builtins": + return class_name + else: + return "{module}.{klass}".format(module=module_name, klass=class_name) + + @staticmethod + def apply_classname_replacements(s): # type: (str) -> Any + for k, v in StubsGenerator.GLOBAL_CLASSNAME_REPLACEMENTS.items(): + s = k.sub(v, s) + return s + + @staticmethod + def function_signatures_from_docstring( + name, func, module_name + ): # type: (str, Any, str) -> List[FunctionSignature] + try: + signature_regex = ( + r"(\s*(?P\d+).)" + r"?\s*{name}\s*\((?P{balanced_parentheses})\)" + r"\s*->\s*" + r"(?P[^\(\)]+)\s*".format(name=name, balanced_parentheses=".*") + ) + docstring = func.__doc__ + + for hook in function_docstring_preprocessing_hooks: + docstring = hook(docstring) + + signatures = [] + for line in docstring.split("\n"): + m = re.match(signature_regex, line) + if m: + args = m.group("args") + rtype = m.group("rtype") + + # Falcor: Apply replacements early before doing ast.parse() in FunctionSignature + args = StubsGenerator.apply_classname_replacements(args) + rtype = StubsGenerator.apply_classname_replacements(rtype) + + if _is_balanced(args): + signatures.append(FunctionSignature(name, args, rtype)) + + # strip module name if provided + if module_name: + for sig in signatures: + regex = r"{}\.(\w+)".format(module_name.replace(".", r"\.")) + sig.args = re.sub(regex, r"\g<1>", sig.args) + sig.rtype = re.sub(regex, r"\g<1>", sig.rtype) + + # Falcor: Replacements already applied above + # for sig in signatures: + # sig.args = StubsGenerator.apply_classname_replacements(sig.args) + # sig.rtype = StubsGenerator.apply_classname_replacements(sig.rtype) + + return sorted(list(set(signatures)), key=lambda fs: fs.args) + except AttributeError: + return [] + + @staticmethod + def property_signature_from_docstring( + prop, module_name + ): # type: (Any, str)-> PropertySignature + + getter_rtype = "None" + setter_args = "None" + access_type = PropertySignature.NONE + + strip_module_name = module_name is not None + + if hasattr(prop, "fget") and prop.fget is not None: + access_type |= PropertySignature.READ_ONLY + if hasattr(prop.fget, "__doc__") and prop.fget.__doc__ is not None: + for line in prop.fget.__doc__.split("\n"): + if strip_module_name: + line = line.replace(module_name + ".", "") + m = re.match( + r"\s*(\w*)\((?P[^()]*)\)\s*->\s*(?P[^()]+)\s*", + line, + ) + if m: + getter_rtype = m.group("rtype") + break + + if hasattr(prop, "fset") and prop.fset is not None: + access_type |= PropertySignature.WRITE_ONLY + if hasattr(prop.fset, "__doc__") and prop.fset.__doc__ is not None: + for line in prop.fset.__doc__.split("\n"): + if strip_module_name: + line = line.replace(module_name + ".", "") + m = re.match( + r"\s*(\w*)\((?P[^()]*)\)\s*->\s*(?P[^()]+)\s*", + line, + ) + if m: + args = m.group("args") + # replace first argument with self + setter_args = ",".join(["self"] + args.split(",")[1:]) + break + getter_rtype = StubsGenerator.apply_classname_replacements(getter_rtype) + setter_args = StubsGenerator.apply_classname_replacements(setter_args) + return PropertySignature(getter_rtype, setter_args, access_type) + + @staticmethod + def remove_signatures(docstring): # type: (str) ->str + + if docstring is None: + return "" + + for hook in function_docstring_preprocessing_hooks: + docstring = hook(docstring) + + signature_regex = ( + r"(\s*(?P\d+).\s*)" + r"?{name}\s*\((?P.*)\)\s*(->\s*(?P[^\(\)]+)\s*)?".format( + name=r"\w+" + ) + ) + + lines = docstring.split("\n\n") + lines = filter(lambda line: line != "Overloaded function.", lines) + + return "\n\n".join( + filter(lambda line: not re.match(signature_regex, line), lines) + ) + + @staticmethod + def sanitize_docstring(docstring): # type: (str) ->str + docstring = StubsGenerator.remove_signatures(docstring) + docstring = docstring.rstrip("\n") + + if docstring and re.match(r"^\s*$", docstring): + docstring = "" + + return docstring + + @staticmethod + def format_docstring(docstring): + docstring = inspect.cleandoc("\n" + docstring) + return StubsGenerator.indent('"""\n{}\n"""'.format(docstring.strip("\n"))) + + +class AttributeStubsGenerator(StubsGenerator): + def __init__(self, name, attribute): # type: (str, Any)-> None + self.name = name + self.attr = attribute + + def parse(self): + if self in _visited_objects: + return + _visited_objects.append(self) + + def is_safe_to_use_repr(self, value): + if value is None or isinstance(value, (int, str)): + return True + if isinstance(value, (float, complex)): + try: + eval(repr(value)) + return True + except (SyntaxError, NameError): + return False + if isinstance(value, (list, tuple, set)): + for x in value: + if not self.is_safe_to_use_repr(x): + return False + return True + if isinstance(value, dict): + for k, v in value.items(): + if not self.is_safe_to_use_repr(k) or not self.is_safe_to_use_repr(v): + return False + return True + return False + + def to_lines(self): # type: () -> List[str] + if self.is_safe_to_use_repr(self.attr): + return ["{name} = {repr}".format(name=self.name, repr=repr(self.attr))] + + # special case for modules + # https://github.com/sizmailov/pybind11-stubgen/issues/43 + if type(self.attr) is type(os) and hasattr(self.attr, "__name__"): + return ["{name} = {repr}".format(name=self.name, repr=self.attr.__name__)] + + # special case for PyCapsule + # https://github.com/sizmailov/pybind11-stubgen/issues/86 + attr_type = type(self.attr) + if attr_type.__name__ == "PyCapsule" and attr_type.__module__ == "builtins": + return ["{name}: typing.Any # PyCapsule()".format(name=self.name)] + + value_lines = repr(self.attr).split("\n") + typename = self.fully_qualified_name(type(self.attr)) + + if len(value_lines) == 1: + value = value_lines[0] + # remove random address from + value = re.sub(r" at 0x[0-9a-fA-F]+>", ">", value) + if value == "<{typename} object>".format(typename=typename): + value_comment = "" + else: + value_comment = " # value = {value}".format(value=value) + return [ + "{name}: {typename}{value_comment}".format( + name=self.name, typename=typename, value_comment=value_comment + ) + ] + else: + return ( + [ + "{name}: {typename} # value = ".format( + name=self.name, typename=typename + ) + ] + + ['"""'] + + [l.replace('"""', r"\"\"\"") for l in value_lines] + + ['"""'] + ) + + def get_involved_modules_names(self): # type: () -> Set[str] + attr_type = type(self.attr) + if attr_type is type(os): + return {self.attr.__name__} + if attr_type.__name__ == "PyCapsule" and attr_type.__module__ == "builtins": + # PyCapsule rendered as typing.Any + return {"typing"} + return {self.attr.__class__.__module__} + + +class FreeFunctionStubsGenerator(StubsGenerator): + def __init__(self, name, free_function, module_name): + self.name = name + self.member = free_function + self.module_name = module_name + self.signatures = [] # type: List[FunctionSignature] + + def parse(self): + self.signatures = self.function_signatures_from_docstring( + self.name, self.member, self.module_name + ) + + def to_lines(self): # type: () -> List[str] + result = [] + docstring = self.sanitize_docstring(self.member.__doc__) + if not docstring and not ( + self.name.startswith("__") and self.name.endswith("__") + ): + logger.debug( + "Docstring is empty for '%s'" % self.fully_qualified_name(self.member) + ) + for sig in self.signatures: + if len(self.signatures) > 1: + result.append("@typing.overload") + result.append( + "def {name}({args}) -> {rtype}:".format( + name=sig.name, args=sig.args, rtype=sig.rtype + ) + ) + if docstring: + result.append(self.format_docstring(docstring)) + docstring = None # don't print docstring for other overloads + else: + result.append(self.indent("pass")) + + return result + + def get_involved_modules_names(self): # type: () -> Set[str] + involved_modules_names = set() + for s in self.signatures: # type: FunctionSignature + for t in s.get_all_involved_types(): # type: str + try: + module_name = t[: t.rindex(".")] + if self.is_valid_module(module_name): + involved_modules_names.add(module_name) + except ValueError: + pass + return involved_modules_names + + +class ClassMemberStubsGenerator(FreeFunctionStubsGenerator): + def __init__(self, name, free_function, module_name): + super(ClassMemberStubsGenerator, self).__init__( + name, free_function, module_name + ) + + def to_lines(self): # type: () -> List[str] + result = [] + docstring = self.sanitize_docstring(self.member.__doc__) + if not docstring and not ( + self.name.startswith("__") and self.name.endswith("__") + ): + logger.debug( + "Docstring is empty for '%s'" % self.fully_qualified_name(self.member) + ) + for sig in self.signatures: + args = sig.args + sargs = args.strip() + if not sargs.startswith("self"): + if sargs.startswith("cls"): + result.append("@classmethod") + # remove type of cls + args = ",".join(["cls"] + sig.split_arguments()[1:]) + else: + result.append("@staticmethod") + else: + # remove type of self + args = ",".join(["self"] + sig.split_arguments()[1:]) + if len(self.signatures) > 1: + result.append("@typing.overload") + + result.append( + "def {name}({args}) -> {rtype}: {ellipsis}".format( + name=sig.name, + args=args, + rtype=sig.rtype, + ellipsis="" if docstring else "...", + ) + ) + if docstring: + result.append(self.format_docstring(docstring)) + docstring = None # don't print docstring for other overloads + return result + + +class PropertyStubsGenerator(StubsGenerator): + def __init__(self, name, prop, module_name): + self.name = name + self.prop = prop + self.module_name = module_name + self.signature = None # type: PropertySignature + + def parse(self): + self.signature = self.property_signature_from_docstring( + self.prop, self.module_name + ) + + def to_lines(self): # type: () -> List[str] + + docstring = self.sanitize_docstring(self.prop.__doc__) + docstring_prop = "\n\n".join( + [docstring, ":type: {rtype}".format(rtype=self.signature.rtype)] + ) + + result = [ + "@property", + "def {field_name}(self) -> {rtype}:".format( + field_name=self.name, rtype=self.signature.rtype + ), + self.format_docstring(docstring_prop), + ] + + if self.signature.setter_args != "None": + result.append("@{field_name}.setter".format(field_name=self.name)) + result.append( + "def {field_name}({args}) -> None:".format( + field_name=self.name, args=self.signature.setter_args + ) + ) + if docstring: + result.append(self.format_docstring(docstring)) + else: + result.append(self.indent("pass")) + + return result + + +class ClassStubsGenerator(StubsGenerator): + ATTRIBUTES_BLACKLIST = ( + "__class__", + "__module__", + "__qualname__", + "__dict__", + "__weakref__", + "__annotations__", + ) + PYBIND11_ATTRIBUTES_BLACKLIST = ("__entries",) + METHODS_BLACKLIST = ("__dir__", "__sizeof__") + BASE_CLASS_BLACKLIST = ("pybind11_object", "object") + CLASS_NAME_BLACKLIST = ("pybind11_type",) + + def __init__( + self, + klass, + attributes_blacklist=ATTRIBUTES_BLACKLIST, + pybind11_attributes_blacklist=PYBIND11_ATTRIBUTES_BLACKLIST, + base_class_blacklist=BASE_CLASS_BLACKLIST, + methods_blacklist=METHODS_BLACKLIST, + class_name_blacklist=CLASS_NAME_BLACKLIST, + ): + self.klass = klass + assert inspect.isclass(klass) + assert klass.__name__.isidentifier() + + self.doc_string = None # type: Optional[str] + + self.classes = [] # type: List[ClassStubsGenerator] + self.fields = [] # type: List[AttributeStubsGenerator] + self.properties = [] # type: List[PropertyStubsGenerator] + self.methods = [] # type: List[ClassMemberStubsGenerator] + self.alias = [] + + self.base_classes = [] + self.involved_modules_names = set() # Set[str] + + self.attributes_blacklist = attributes_blacklist + self.pybind11_attributes_blacklist = pybind11_attributes_blacklist + self.base_class_blacklist = base_class_blacklist + self.methods_blacklist = methods_blacklist + self.class_name_blacklist = class_name_blacklist + + def get_involved_modules_names(self): + return self.involved_modules_names + + def parse(self): + if self.klass in _visited_objects: + return + _visited_objects.append(self.klass) + + bases = inspect.getmro(self.klass)[1:] + + def is_base_member(name, member): + for base in bases: + if hasattr(base, name) and getattr(base, name) is member: + return True + return False + + is_pybind11 = any(base.__name__ == "pybind11_object" for base in bases) + + for name, member in inspect.getmembers(self.klass): + # check if attribute is in __dict__ (fast path) before slower search in base classes + if name not in self.klass.__dict__ and is_base_member(name, member): + continue + if name.startswith("__pybind11_module"): + continue + if (inspect.isroutine(member) or inspect.isclass(member)) and name != member.__name__: + self.alias.append(AliasStubsGenerator(name, member)) + elif inspect.isroutine(member): + self.methods.append( + ClassMemberStubsGenerator(name, member, self.klass.__module__) + ) + elif name != "__class__" and inspect.isclass(member): + if ( + member.__name__ not in self.class_name_blacklist + and member.__name__.isidentifier() + ): + self.classes.append(ClassStubsGenerator(member)) + elif isinstance(member, property): + self.properties.append( + PropertyStubsGenerator(name, member, self.klass.__module__) + ) + elif name == "__doc__": + self.doc_string = member + elif not ( + name in self.attributes_blacklist + or (is_pybind11 and name in self.pybind11_attributes_blacklist) + ): + self.fields.append(AttributeStubsGenerator(name, member)) + # logger.warning("Unknown member %s type : `%s` " % (name, str(type(member)))) + + for x in itertools.chain( + self.classes, self.methods, self.properties, self.fields + , + self.alias): + x.parse() + + for B in bases: + if ( + B.__name__ != self.klass.__name__ + and B.__name__ not in self.base_class_blacklist + ): + self.base_classes.append(B) + self.involved_modules_names.add(B.__module__) + + for f in self.methods: # type: ClassMemberStubsGenerator + self.involved_modules_names |= f.get_involved_modules_names() + + for attr in self.fields: + self.involved_modules_names |= attr.get_involved_modules_names() + + def to_lines(self): # type: () -> List[str] + def strip_current_module_name(obj, module_name): + regex = r"{}\.(\w+)".format(module_name.replace(".", r"\.")) + return re.sub(regex, r"\g<1>", obj) + + base_classes_list = [ + strip_current_module_name( + self.fully_qualified_name(b), self.klass.__module__ + ) + for b in self.base_classes + ] + result = [ + "class {class_name}({base_classes_list}):{doc_string}".format( + class_name=self.klass.__name__, + base_classes_list=", ".join(base_classes_list), + doc_string="\n" + self.format_docstring(self.doc_string) + if self.doc_string + else "", + ), + ] + for cl in self.classes: + result.extend(map(self.indent, cl.to_lines())) + + for f in self.methods: + if f.name not in self.methods_blacklist: + result.extend(map(self.indent, f.to_lines())) + + for p in self.properties: + result.extend(map(self.indent, p.to_lines())) + + for p in self.fields: + result.extend(map(self.indent, p.to_lines())) + + result.append(self.indent("pass")) + return result + + +class AliasStubsGenerator(StubsGenerator): + + def __init__(self, alias_name, origin): + self.alias_name = alias_name + self.origin = origin + + def parse(self): + pass + + def to_lines(self): # type: () -> List[str] + return [ + "{alias} = {origin}".format( + alias=self.alias_name, + origin=self.fully_qualified_name(self.origin) + ) + ] + + def get_involved_modules_names(self): # type: () -> Set[str] + if inspect.ismodule(self.origin): + return {self.origin.__name__} + elif inspect.isroutine(self.origin) or inspect.isclass(self.origin): + return {self.origin.__module__.__name__} + else: + return set() + + +class ModuleStubsGenerator(StubsGenerator): + CLASS_NAME_BLACKLIST = ClassStubsGenerator.CLASS_NAME_BLACKLIST + ATTRIBUTES_BLACKLIST = ( + "__file__", + "__loader__", + "__name__", + "__package__", + "__spec__", + "__path__", + "__cached__", + "__builtins__", + ) + + def __init__( + self, + module_or_module_name, + attributes_blacklist=ATTRIBUTES_BLACKLIST, + class_name_blacklist=CLASS_NAME_BLACKLIST, + ): + if isinstance(module_or_module_name, str): + self.module = importlib.import_module(module_or_module_name) + else: + self.module = module_or_module_name + assert inspect.ismodule(self.module) + + self.doc_string = None # type: Optional[str] + self.classes = [] # type: List[ClassStubsGenerator] + self.free_functions = [] # type: List[FreeFunctionStubsGenerator] + self.submodules = [] # type: List[ModuleStubsGenerator] + self.imported_modules = [] # type: List[str] + self.imported_classes = {} # type: Dict[str, type] + self.attributes = [] # type: List[AttributeStubsGenerator] + self.alias = [] + self.stub_suffix = "" + self.write_setup_py = False + + self.attributes_blacklist = attributes_blacklist + self.class_name_blacklist = class_name_blacklist + + def parse(self): + if self.module in _visited_objects: + return + _visited_objects.append(self.module) + logger.debug("Parsing '%s' module" % self.module.__name__) + for name, member in inspect.getmembers(self.module): + if (inspect.isfunction(member) or inspect.isclass(member)) and name != member.__name__: + self.alias.append(AliasStubsGenerator(name, member)) + elif inspect.ismodule(member): + m = ModuleStubsGenerator(member) + if m.module.__name__.split(".")[:-1] == self.module.__name__.split("."): + self.submodules.append(m) + else: + self.imported_modules += [m.module.__name__] + logger.debug( + "Skip '%s' module while parsing '%s' " + % (m.module.__name__, self.module.__name__) + ) + elif inspect.isbuiltin(member) or inspect.isfunction(member): + self.free_functions.append( + FreeFunctionStubsGenerator(name, member, self.module.__name__) + ) + elif inspect.isclass(member): + if member.__module__ == self.module.__name__: + if ( + member.__name__ not in self.class_name_blacklist + and member.__name__.isidentifier() + ): + self.classes.append(ClassStubsGenerator(member)) + else: + self.imported_classes[name] = member + elif name == "__doc__": + self.doc_string = member + elif name not in self.attributes_blacklist: + self.attributes.append(AttributeStubsGenerator(name, member)) + + for x in itertools.chain( + self.submodules, self.classes, self.free_functions, self.attributes + ): + x.parse() + + def class_ordering( + a, b + ): # type: (ClassStubsGenerator, ClassStubsGenerator) -> int + if a.klass is b.klass: + return 0 + if issubclass(a.klass, b.klass): + return -1 + if issubclass(b.klass, a.klass): + return 1 + return 0 + + # reorder classes so base classes would be printed before derived + # print([ k.klass.__name__ for k in self.classes ]) + for i in range(len(self.classes)): + for j in range(i + 1, len(self.classes)): + if class_ordering(self.classes[i], self.classes[j]) < 0: + t = self.classes[i] + self.classes[i] = self.classes[j] + self.classes[j] = t + # print( [ k.klass.__name__ for k in self.classes ] ) + + def get_involved_modules_names(self): + result = set(self.imported_modules) + + for attr in self.attributes: + result |= attr.get_involved_modules_names() + + for C in self.classes: # type: ClassStubsGenerator + result |= C.get_involved_modules_names() + + for f in self.free_functions: # type: FreeFunctionStubsGenerator + result |= f.get_involved_modules_names() + + return set(result) - {"builtins", "typing", self.module.__name__} + + def to_lines(self): # type: () -> List[str] + + result = [] + + if self.doc_string: + result += ['"""' + self.doc_string.replace('"""', r"\"\"\"") + '"""'] + + if sys.version_info[:2] >= (3, 7): + result += ["from __future__ import annotations"] + + result += ["import {}".format(self.module.__name__)] + + # import everything from typing + result += ["import typing"] + + for name, class_ in self.imported_classes.items(): + class_name = getattr(class_, "__qualname__", class_.__name__) + if name == class_name: + suffix = "" + else: + suffix = " as {}".format(name) + result += [ + "from {} import {}{}".format(class_.__module__, class_name, suffix) + ] + + # import used packages + used_modules = sorted(self.get_involved_modules_names()) + if used_modules: + # result.append("if TYPE_CHECKING:") + # result.extend(map(self.indent, map(lambda m: "import {}".format(m), used_modules))) + result.extend(map(lambda mod: "import {}".format(mod), used_modules)) + + if "numpy" in used_modules and not BARE_NUPMY_NDARRAY: + result += ["_Shape = typing.Tuple[int, ...]"] + + # add space between imports and rest of module + result += [""] + + globals_ = {} + exec("from {} import *".format(self.module.__name__), globals_) + all_ = set(member for member in globals_.keys() if member.isidentifier()) - { + "__builtins__" + } + result.append( + "__all__ = [\n " + + ",\n ".join(map(lambda s: '"%s"' % s, sorted(all_))) + + "\n]\n\n" + ) + + for x in itertools.chain(self.classes, self.free_functions, self.attributes, + self.alias): + result.extend(x.to_lines()) + result.append("") # Newline at EOF + return result + + @property + def short_name(self): + return self.module.__name__.split(".")[-1] + + def write(self): + with DirectoryWalkerGuard(self.short_name + self.stub_suffix): + with open("__init__.pyi", "w", encoding="utf-8") as init_pyi: + init_pyi.write("\n".join(self.to_lines())) + for m in self.submodules: + m.write() + + if self.write_setup_py: + with open("setup.py", "w", encoding="utf-8") as setuppy: + setuppy.write( + """from setuptools import setup +import os + + +def find_stubs(package): + stubs = [] + for root, dirs, files in os.walk(package): + for file in files: + path = os.path.join(root, file).replace(package + os.sep, '', 1) + stubs.append(path) + return dict(package=stubs) + + +setup( + name='{package_name}-stubs', + maintainer="{package_name} Developers", + maintainer_email="example@python.org", + description="PEP 561 type stubs for {package_name}", + version='1.0', + packages=['{package_name}-stubs'], + # PEP 561 requires these + install_requires=['{package_name}'], + package_data=find_stubs('{package_name}-stubs'), +)""".format( + package_name=self.short_name + ) + ) + + +def main(args=None): + parser = ArgumentParser( + prog="pybind11-stubgen", description="Generates stubs for specified modules" + ) + parser.add_argument( + "-o", + "--output-dir", + help="the root directory for output stubs", + default="./stubs", + ) + parser.add_argument( + "--root-module-suffix", + type=str, + default="-stubs", + dest="root_module_suffix", + help="optional suffix to disambiguate from the original package", + ) + parser.add_argument( + "--root_module_suffix", + type=str, + default=None, + dest="root_module_suffix_deprecated", + help="Deprecated. Use `--root-module-suffix`", + ) + parser.add_argument("--no-setup-py", action="store_true") + parser.add_argument( + "--non-stop", action="store_true", help="Deprecated. Use `--ignore-invalid=all`" + ) + parser.add_argument( + "--ignore-invalid", + nargs="+", + choices=["signature", "defaultarg", "all"], + default=[], + help="Ignore invalid specified python expressions in docstrings", + ) + parser.add_argument( + "--skip-signature-downgrade", + action="store_true", + help="Do not downgrade invalid function signatures to func(*args, **kwargs)", + ) + parser.add_argument( + "--bare-numpy-ndarray", + action="store_true", + default=False, + help="Render `numpy.ndarray` without (non-standardized) bracket-enclosed type and shape info", + ) + parser.add_argument( + "module_names", nargs="+", metavar="MODULE_NAME", type=str, help="modules names" + ) + parser.add_argument("--log-level", default="INFO", help="Set output log level") + + sys_args = parser.parse_args(args or sys.argv[1:]) + + if sys_args.non_stop: + sys_args.ignore_invalid = ["all"] + warnings.warn( + "`--non-stop` is deprecated in favor of `--ignore-invalid=all`", + FutureWarning, + ) + + if sys_args.bare_numpy_ndarray: + global BARE_NUPMY_NDARRAY + BARE_NUPMY_NDARRAY = True + + if "all" in sys_args.ignore_invalid: + FunctionSignature.ignore_invalid_signature = True + FunctionSignature.ignore_invalid_defaultarg = True + else: + if "signature" in sys_args.ignore_invalid: + FunctionSignature.ignore_invalid_signature = True + if "defaultarg" in sys_args.ignore_invalid: + FunctionSignature.ignore_invalid_defaultarg = True + + if sys_args.skip_signature_downgrade: + FunctionSignature.signature_downgrade = False + + if sys_args.root_module_suffix_deprecated is not None: + sys_args.root_module_suffix = sys_args.root_module_suffix_deprecated + warnings.warn( + "`--root_module_suffix` is deprecated in favor of `--root-module-suffix`", + FutureWarning, + ) + + stderr_handler = logging.StreamHandler(sys.stderr) + handlers = [stderr_handler] + + logging.basicConfig( + level=logging.getLevelName(sys_args.log_level), + format="[%(asctime)s] {%(filename)s:%(lineno)d} %(levelname)s - %(message)s", + handlers=handlers, + ) + + with DirectoryWalkerGuard(sys_args.output_dir): + for _module_name in sys_args.module_names: + _module = ModuleStubsGenerator(_module_name) + _module.parse() + if FunctionSignature.n_fatal_errors() == 0: + _module.stub_suffix = sys_args.root_module_suffix + _module.write_setup_py = not sys_args.no_setup_py + _dir = _module_name.split(".")[:-1] or ["."] + _dir = os.path.join(*_dir) + with DirectoryWalkerGuard(_dir): + _module.write() + + if FunctionSignature.n_invalid_signatures > 0: + logger.info("Useful link: Avoiding C++ types in docstrings:") + logger.info( + " https://pybind11.readthedocs.io/en/latest/advanced/misc.html" + "#avoiding-cpp-types-in-docstrings" + ) + + if FunctionSignature.n_invalid_default_values > 0: + logger.info("Useful link: Default argument representation:") + logger.info( + " https://pybind11.readthedocs.io/en/latest/advanced/functions.html" + "#default-arguments-revisited" + ) + + if FunctionSignature.n_fatal_errors() > 0: + exit(1) + + +if __name__ == "__main__": + main() diff --git a/data/tests/tiny_mip0.png b/data/tests/tiny_mip0.png new file mode 100644 index 0000000000000000000000000000000000000000..5b5c761c9608eabdcf01ba49317f0057897ab655 GIT binary patch literal 127 zcmeAS@N?(olHy`uVBq!ia0vp^EFjFm1SHiab7}%9#^NA%Cx&(BWL|<~(j9#r85lP9 zbN@+X1@buyJR*x382Ao@Fyrz36)8YL5lqA6vTxkG R%LFLT;OXk;vd$@?2>>=|9fSY? literal 0 HcmV?d00001 diff --git a/data/tests/tiny_mip1.png b/data/tests/tiny_mip1.png new file mode 100644 index 0000000000000000000000000000000000000000..84dce3ef5cca528722a1b47dd8477207f64817eb GIT binary patch literal 125 zcmeAS@N?(olHy`uVBq!ia0vp^Od!kwBL7~QRScvUi-X*q7}lMWc?smOq&xaLGB9lH z=l+w(3gmMZctipf@f`+X#^d=bQh - - + @@ -15,24 +14,23 @@ - + - + - - + + - - + @@ -47,10 +45,13 @@ - + + + - + + @@ -61,10 +62,4 @@ - - - - - - diff --git a/docs/development/coding-conventions.md b/docs/development/coding-conventions.md index 833db7e60..b6c97b709 100644 --- a/docs/development/coding-conventions.md +++ b/docs/development/coding-conventions.md @@ -172,14 +172,51 @@ The following table outlines the naming prefixing and casing used: | local variables | `camelCase` | | macros | `UPPER_SNAKE_CASE` | -In addition `p` prefix is used for pointers. This applies to all kinds of variables, e.g. `mpSomePtr`, `spSomePointer` etc. -The prefix `p` is not strictly required in self-contained new code. +In the past a `p` prefix was used for pointers. This applies to all kinds of variables, e.g. `mpSomePtr`, `spSomePointer` etc. +The prefix `p` should not be used for self-contained new code, but might still be used to conform to old code. ### Variables - Names should in general be descriptive of what a variable/parameter/etc. represents. - The shorter the scope of a variable, the less important it is for it to have a verbose and descriptive name. Single-letter names are okay for extremely short-lived variables and loop counters, so long as their role is clear in context. +### Name shadowing + +- The compiler is configured to error on name shadowing to prevent unexpected behavior and make name replacement more robust. +- It's good practice to introduce local scopes for reusing the same variable name: + +```c++ +void foo() +{ + { + Program::Desc desc; + ... + mProgramA = Program::create(desc); + } + + { + Program::Desc desc; + ... + mProgramB = Program::create(desc); + } +} +``` + +- If function argument names are shadowing struct members, use a trailing underscore (`_`) to avoid shadowing: + +```c++ +struct Data +{ + int foo; + int bar; + void set(int foo_, int bar_) + { + foo = foo_; + bar = bar_; + } +} +``` + ### Macros - The use of macros should be minimized. @@ -217,6 +254,7 @@ The prefix `p` is not strictly required in self-contained new code. - Use `std::unique_ptr` unless the ownership of an object is truly shared, it is the more efficient smart pointer. - Use the `override` specifier on all overridden virtual methods. It helps catch bugs. - Avoid using `friend` unless absolutely needed. +- Use `static constexpr` for class/struct scoped constant values. Previous to C++17, `static const` members have to be initialized out-of-class, with C++17 they can be initialized inline using either `inline static const` or `static constexpr` (which is implicitly `inline`). C++ has an exception to allow `static const` integral types to be initialized in the declaration, but this will result in no global symbol for the constant (unless also defined out-of-class), which in turn leads to hard to understand behavior when referencing such a constant in a different library for example. Some more information can be found here: https://fekir.info/post/linker-error-with-static-member-variable ### Rules for C++ structs diff --git a/docs/development/unit-testing.md b/docs/development/unit-testing.md index 34137cf68..a0baad2ad 100644 --- a/docs/development/unit-testing.md +++ b/docs/development/unit-testing.md @@ -27,26 +27,41 @@ Run the batch file `tests/run_unit_tests.bat` with appropriate settings. ``` $ ./run_unit_tests.bat --help -usage: run_unit_tests.py [-h] [-c CONFIG] [-e ENVIRONMENT] [-f FILTER] - [-x XML_REPORT] [-r REPEAT] [--skip-build] +usage: run_unit_tests.py [-h] [--environment ENVIRONMENT] [--config CONFIG] [--list-configs] -Utility for running unit tests. +Frontend for running unit tests. This script helps to run the falcor unit +tests for different build configurations. optional arguments: - -h, --help show this help message and exit - -c CONFIG, --config CONFIG - Build configuration - -e ENVIRONMENT, --environment ENVIRONMENT - Environment - -f FILTER, --filter FILTER - Regular expression for filtering tests to run - -x XML_REPORT, --xml-report XML_REPORT - XML report output file - -r REPEAT, --repeat REPEAT - Number of times to repeat the test. - --skip-build Skip building project before running tests - --list-configs List available build configurations. + -h, --help Show this help message and exit + --environment ENVIRONMENT + Environment (default: environment/default.json) + --config CONFIG Build configuration (default: windows-ninja-msvc- + Release) + --list-configs List available build configurations + +Additional arguments consumed by FalcorTest.exe: + + FalcorTest {OPTIONS} + + Falcor unit tests. + + OPTIONS: + + -h, --help Display this help menu. + -c[all,cpu,gpu], + --category=[all,cpu,gpu] Test categories to run (default: all). + -d[d3d12|vulkan], + --device-type=[d3d12|vulkan] Graphics device type. + --list-gpus List available GPUs + --gpu=[index] Select specific GPU to use + -f[filter], --filter=[filter] Regular expression for filtering tests + to run. + -x[path], --xml-report=[path] XML report output file. + -r[N], --repeat=[N] Number of times to repeat the test. + --enable-debug-layer Enable debug layer (enabled by default + in Debug build). ``` ### From Visual Studio or the Command Line @@ -61,8 +76,15 @@ Run the executable `build//bin/[Debug|Release]/FalcorTest.exe` OPTIONS: -h, --help Display this help menu. + -c[all,cpu,gpu], + --category=[all,cpu,gpu] Test categories to run (default: all). + -d[d3d12|vulkan], + --device-type=[d3d12|vulkan] Graphics device type. + --list-gpus List available GPUs + --gpu=[index] Select specific GPU to use -f[filter], --filter=[filter] Regular expression for filtering tests to run. + -x[path], --xml-report=[path] XML report output file. -r[N], --repeat=[N] Number of times to repeat the test. --enable-debug-layer Enable debug layer (enabled by default in Debug build). diff --git a/external/CMakeLists.txt b/external/CMakeLists.txt index 7e7e9db36..1001eb956 100644 --- a/external/CMakeLists.txt +++ b/external/CMakeLists.txt @@ -37,10 +37,6 @@ set(GLFW_BUILD_TESTS OFF) set(GLFW_BUILD_DOCS OFF) add_subdirectory(glfw) -# glm -message(STATUS "Configure glm") -add_subdirectory(glm) - # imgui add_library(imgui INTERFACE) target_include_directories(imgui INTERFACE imgui) @@ -87,7 +83,7 @@ set(PACKMAN_DIR ${CMAKE_CURRENT_SOURCE_DIR}/packman) # that are generated by vcpkg. Building in vcpkg allows to make sure to have # compatible inter-dependencies. The main libraries provided are: # -# ffmpeg, OpenEXR, OpenVDB, assimp, FreeImage, pugixml, hdf5, lz4, zlib +# OpenEXR, OpenVDB, assimp, FreeImage, pugixml, hdf5, lz4, zlib # # vcpkg does not currently have the ability to generate a set of imported # targets for use in CMake (it offers to use FindXXX scripts, but this @@ -96,26 +92,6 @@ set(PACKMAN_DIR ${CMAKE_CURRENT_SOURCE_DIR}/packman) set(FALCOR_DEPS_DIR ${PACKMAN_DIR}/deps) -# ffmpeg -# Note: Using an INTERFACE target to simplify linking against all the various libraries in ffmpeg -if(FALCOR_WINDOWS) - add_library(ffmpeg INTERFACE) - target_include_directories(ffmpeg INTERFACE ${FALCOR_DEPS_DIR}/include) - target_link_directories(ffmpeg INTERFACE - $<$:${FALCOR_DEPS_DIR}/lib> - $<$:${FALCOR_DEPS_DIR}/debug/lib> - ) - target_link_libraries(ffmpeg INTERFACE "avcodec.lib;avutil.lib;avformat.lib;swscale.lib") -elseif(FALCOR_LINUX) - add_library(ffmpeg INTERFACE) - target_include_directories(ffmpeg INTERFACE ${FALCOR_DEPS_DIR}/include) - target_link_directories(ffmpeg INTERFACE - $<$:${FALCOR_DEPS_DIR}/lib> - $<$:${FALCOR_DEPS_DIR}/debug/lib> - ) - target_link_libraries(ffmpeg INTERFACE avcodec.so avutil.so avformat.so swscale.so swresample.so) -endif() - # OpenEXR # Note: Using an INTERFACE target to simplify linking against all the various libraries in OpenEXR if(FALCOR_WINDOWS) @@ -126,8 +102,8 @@ if(FALCOR_WINDOWS) $<$:${FALCOR_DEPS_DIR}/debug/lib> ) target_link_libraries(OpenEXR INTERFACE - $<$:Imath-2_5.lib Half-2_5.lib IlmThread-2_5.lib IlmImf-2_5.lib Iex-2_5.lib> - $<$:Imath-2_5_d.lib Half-2_5_d.lib IlmThread-2_5_d.lib IlmImf-2_5_d.lib Iex-2_5_d.lib> + $<$:OpenEXR-3_1.lib Imath-3_1.lib IlmThread-3_1.lib Iex-3_1.lib> + $<$:OpenEXR-3_1_d.lib Imath-3_1_d.lib IlmThread-3_1_d.lib Iex-3_1_d.lib> ) elseif(FALCOR_LINUX) add_library(OpenEXR INTERFACE) @@ -137,8 +113,8 @@ elseif(FALCOR_LINUX) $<$:${FALCOR_DEPS_DIR}/debug/lib> ) target_link_libraries(OpenEXR INTERFACE - $<$:Imath-2_5.so Half-2_5.so IlmThread-2_5.so IlmImf-2_5.so Iex-2_5.so> - $<$:Imath-2_5_d.so Half-2_5_d.so IlmThread-2_5_d.so IlmImf-2_5_d.so Iex-2_5_d.so> + $<$:OpenEXR-3_1.so Imath-3_1.so IlmThread-3_1.so Iex-3_1.so> + $<$:OpenEXR-3_1_d.so Imath-3_1_d.so IlmThread-3_1_d.so Iex-3_1_d.so> ) endif() @@ -148,21 +124,42 @@ if(FALCOR_WINDOWS) set_target_properties(tbb PROPERTIES INTERFACE_INCLUDE_DIRECTORIES ${FALCOR_DEPS_DIR}/include IMPORTED_IMPLIB_RELEASE ${FALCOR_DEPS_DIR}/lib/tbb.lib - IMPORTED_LOCATION_DEBUG ${FALCOR_DEPS_DIR}/bin/tbb.dll + IMPORTED_LOCATION_RELEASE ${FALCOR_DEPS_DIR}/bin/tbb.dll IMPORTED_IMPLIB_DEBUG ${FALCOR_DEPS_DIR}/debug/lib/tbb_debug.lib IMPORTED_LOCATION_DEBUG ${FALCOR_DEPS_DIR}/debug/bin/tbb_debug.dll - INTERFACE_COMPILE_DEFINITIONS_RELEASE "TBB_USE_DEBUG=0;__TBB_NO_IMPLICIT_LINKAGE=1" - INTERFACE_COMPILE_DEFINITIONS_DEBUG "TBB_USE_DEBUG=1;__TBB_NO_IMPLICIT_LINKAGE=1" + ) + target_compile_definitions(tbb INTERFACE + $<$:TBB_USE_DEBUG=0;__TBB_NO_IMPLICIT_LINKAGE=1> + $<$:TBB_USE_DEBUG=1;__TBB_NO_IMPLICIT_LINKAGE=1> ) elseif(FALCOR_LINUX) - # add_library(tbb INTERFACE) add_library(tbb SHARED IMPORTED GLOBAL) set_target_properties(tbb PROPERTIES INTERFACE_INCLUDE_DIRECTORIES ${FALCOR_DEPS_DIR}/include IMPORTED_LOCATION_RELEASE ${FALCOR_DEPS_DIR}/lib/libtbb.so - IMPORTED_LOCATION_DEBUG ${FALCOR_DEPS_DIR}/debug/lib/libtbb.so # this is a copy of the release library - INTERFACE_COMPILE_DEFINITIONS_RELEASE "TBB_USE_DEBUG=0" - INTERFACE_COMPILE_DEFINITIONS_DEBUG "TBB_USE_DEBUG=1" + IMPORTED_LOCATION_DEBUG ${FALCOR_DEPS_DIR}/debug/lib/libtbb_debug.so + ) + target_compile_definitions(tbb INTERFACE + $<$:TBB_USE_DEBUG=0> + $<$:TBB_USE_DEBUG=1> + ) +endif() + +# opensubdiv +# Note: only static libs are supported under windows. Use static for linux as well for symmetry. +if(FALCOR_WINDOWS) + add_library(opensubdiv STATIC IMPORTED GLOBAL) + set_target_properties(opensubdiv PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES ${FALCOR_DEPS_DIR}/include + IMPORTED_LOCATION_RELEASE ${FALCOR_DEPS_DIR}/lib/osdCPU.lib + IMPORTED_LOCATION_DEBUG ${FALCOR_DEPS_DIR}/debug/lib/osdCPU.lib + ) +elseif(FALCOR_LINUX) + add_library(opensubdiv STATIC IMPORTED GLOBAL) + set_target_properties(opensubdiv PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES ${FALCOR_DEPS_DIR}/include + IMPORTED_LOCATION_RELEASE ${FALCOR_DEPS_DIR}/lib/libosdCPU.a + IMPORTED_LOCATION_DEBUG ${FALCOR_DEPS_DIR}/debug/lib/libosdCPU.a ) endif() @@ -175,7 +172,7 @@ if(FALCOR_WINDOWS) IMPORTED_LOCATION_RELEASE ${FALCOR_DEPS_DIR}/bin/openvdb.dll IMPORTED_IMPLIB_DEBUG ${FALCOR_DEPS_DIR}/debug/lib/openvdb.lib IMPORTED_LOCATION_DEBUG ${FALCOR_DEPS_DIR}/debug/bin/openvdb.dll - INTERFACE_LINK_LIBRARIES tbb + INTERFACE_LINK_LIBRARIES "tbb;blosc" ) elseif(FALCOR_LINUX) add_library(OpenVDB SHARED IMPORTED GLOBAL) @@ -183,7 +180,7 @@ elseif(FALCOR_LINUX) INTERFACE_INCLUDE_DIRECTORIES ${FALCOR_DEPS_DIR}/include IMPORTED_LOCATION_RELEASE ${FALCOR_DEPS_DIR}/lib/libopenvdb.so IMPORTED_LOCATION_DEBUG ${FALCOR_DEPS_DIR}/debug/lib/libopenvdb.so - INTERFACE_LINK_LIBRARIES tbb + INTERFACE_LINK_LIBRARIES "tbb;blosc" ) endif() @@ -193,8 +190,8 @@ if(FALCOR_WINDOWS) add_library(assimp SHARED IMPORTED GLOBAL) set_target_properties(assimp PROPERTIES INTERFACE_INCLUDE_DIRECTORIES ${FALCOR_DEPS_DIR}/include - IMPORTED_IMPLIB ${FALCOR_DEPS_DIR}/lib/assimp-vc142-mt.lib - IMPORTED_LOCATION ${FALCOR_DEPS_DIR}/bin/assimp-vc142-mt.dll + IMPORTED_IMPLIB ${FALCOR_DEPS_DIR}/lib/assimp-vc143-mt.lib + IMPORTED_LOCATION ${FALCOR_DEPS_DIR}/bin/assimp-vc143-mt.dll ) elseif(FALCOR_LINUX) add_library(assimp SHARED IMPORTED GLOBAL) @@ -369,51 +366,38 @@ target_include_directories(nanovdb INTERFACE ${NANOVDB_DIR}/include) set(NV_USD_RELEASE_DIR ${PACKMAN_DIR}/nv-usd-release) set(NV_USD_DEBUG_DIR ${PACKMAN_DIR}/nv-usd-debug) if((EXISTS ${NV_USD_RELEASE_DIR}) AND (EXISTS ${NV_USD_DEBUG_DIR})) - add_library(nv-usd INTERFACE) - target_include_directories(nv-usd INTERFACE - $<$:${NV_USD_RELEASE_DIR}/include> - $<$:${NV_USD_DEBUG_DIR}/include> - ) - target_link_directories(nv-usd INTERFACE - $<$:${NV_USD_RELEASE_DIR}/lib ${NV_USD_RELEASE_DIR}/plugin/usd> - $<$:${NV_USD_DEBUG_DIR}/lib ${NV_USD_DEBUG_DIR}/plugin/usd> - ) - target_link_libraries(nv-usd INTERFACE - usd.lib sdf.lib usdGeom.lib usdImaging.lib;usdRender.lib usdSkel.lib vt.lib ar.lib work.lib hd.lib arch.lib tf.lib usdLux.lib usdShade.lib plug.lib usdMdl.lib gf.lib - # boost_python - $<$:boost_python37-vc141-mt-x64-1_68.lib> - $<$:boost_python37-vc141-mt-gd-x64-1_68.lib> - ) - target_compile_definitions(nv-usd INTERFACE - _SILENCE_CXX17_ITERATOR_BASE_CLASS_DEPRECATION_WARNING - $<$:TBB_USE_DEBUG=0> - $<$:TBB_USE_DEBUG=1> - - ) + if(FALCOR_WINDOWS) + add_library(nv-usd INTERFACE) + target_include_directories(nv-usd INTERFACE + $<$:${NV_USD_RELEASE_DIR}/include ${NV_USD_RELEASE_DIR}/include/boost-1_76> + $<$:${NV_USD_DEBUG_DIR}/include ${NV_USD_DEBUG_DIR}/include/boost-1_76> + ) + target_link_directories(nv-usd INTERFACE + $<$:${NV_USD_RELEASE_DIR}/lib ${NV_USD_RELEASE_DIR}/plugin/usd> + $<$:${NV_USD_DEBUG_DIR}/lib ${NV_USD_DEBUG_DIR}/plugin/usd> + ) + target_link_libraries(nv-usd INTERFACE + usd_ms.lib + ) + target_compile_definitions(nv-usd INTERFACE + _SILENCE_CXX17_ITERATOR_BASE_CLASS_DEPRECATION_WARNING + $<$:TBB_USE_DEBUG=0> + $<$:TBB_USE_DEBUG=1> + ) + elseif(FALCOR_LINUX) + add_library(nv-usd SHARED IMPORTED GLOBAL) + set_target_properties(nv-usd PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES "${NV_USD_RELEASE_DIR}/include;${NV_USD_RELEASE_DIR}/include/boost" + IMPORTED_LOCATION_RELEASE ${NV_USD_RELEASE_DIR}/lib/libusd_ms.so + IMPORTED_LOCATION_DEBUG ${NV_USD_DEBUG_DIR}/lib/libusd_ms.so + INTERFACE_LINK_LIBRARIES "tbb" + ) + endif() set(FALCOR_HAS_NV_USD ON PARENT_SCOPE) else() set(FALCOR_HAS_NV_USD OFF PARENT_SCOPE) endif() -# opensubdiv -set(OPENSUBDIV_RELEASE_DIR ${PACKMAN_DIR}/opensubdiv-release) -set(OPENSUBDIV_DEBUG_DIR ${PACKMAN_DIR}/opensubdiv-debug) -if((EXISTS ${OPENSUBDIV_RELEASE_DIR}) AND (EXISTS ${OPENSUBDIV_DEBUG_DIR})) - add_library(opensubdiv INTERFACE) - target_include_directories(opensubdiv INTERFACE - $<$:${OPENSUBDIV_RELEASE_DIR}/include> - $<$:${OPENSUBDIV_DEBUG_DIR}/include> - ) - target_link_directories(opensubdiv INTERFACE - $<$:${OPENSUBDIV_RELEASE_DIR}/lib ${OPENSUBDIV_RELEASE_DIR}/lib> - $<$:${OPENSUBDIV_DEBUG_DIR}/lib ${OPENSUBDIV_DEBUG_DIR}/lib> - ) - target_link_libraries(opensubdiv INTERFACE osdCPU.lib osdGPU.lib) - set(FALCOR_HAS_OPENSUBDIV ON PARENT_SCOPE) -else() - set(FALCOR_HAS_OPENSUBDIV OFF PARENT_SCOPE) -endif() - # mdl-sdk set(MDL_SDK_DIR ${PACKMAN_DIR}/mdl-sdk) if(EXISTS ${MDL_SDK_DIR}) diff --git a/external/glm b/external/glm deleted file mode 160000 index bf71a8349..000000000 --- a/external/glm +++ /dev/null @@ -1 +0,0 @@ -Subproject commit bf71a834948186f4097caa076cd2663c69a10e1e diff --git a/external/imgui_addons/imguinodegrapheditor/imguinodegrapheditor.cpp b/external/imgui_addons/imguinodegrapheditor/imguinodegrapheditor.cpp index e0eca72c8..4497ba37a 100644 --- a/external/imgui_addons/imguinodegrapheditor/imguinodegrapheditor.cpp +++ b/external/imgui_addons/imguinodegrapheditor/imguinodegrapheditor.cpp @@ -37,6 +37,16 @@ # define snprintf _snprintf #endif //(defined(_MSC_VER) && !defined(snprintf)) +#ifdef _MSC_VER +# pragma warning (push) +# pragma warning (disable: 4456) // declaration of 'xx' hides previous local declaration +# pragma warning (disable: 4458) // declaration of 'xx' hides class member +# pragma warning (disable: 4706) // assignment within conditional expression +#endif + +#ifdef __GNUC__ +# pragma GCC diagnostic ignored "-Wmisleading-indentation" +#endif namespace ImGui { @@ -3140,5 +3150,6 @@ namespace ImGui { #endif //IMGUINODEGRAPHEDITOR_NOTESTDEMO - - +#ifdef _MSC_VER +# pragma warning (pop) +#endif diff --git a/scripts/python/test_pytorch.py b/scripts/python/test_pytorch.py new file mode 100644 index 000000000..87f2aa250 --- /dev/null +++ b/scripts/python/test_pytorch.py @@ -0,0 +1,81 @@ +import torch +import numpy as np +import falcor +from falcor import * +from time import time +import random + +def createTensor(dim, offset, device): + data = torch.zeros(dim, dtype=torch.float32, device=torch.device('cpu')) + for k in range(dim[0]): + for j in range(dim[1]): + for i in range(dim[2]): + idx = (k * dim[1] + j) * dim[2] + i + data[k][j][i] = idx + offset + return data.to(device) + +def testTensorToFalcor(device, test_pass, iterations=10): + print('Testing passing tensors to Falcor') + for offset in range(iterations): + dim = random.sample(range(1, 32), 3) + data = createTensor(dim, offset, device) + #torch.cuda.synchronize(device) # Does not seem to be necessary + + res = test_pass.verifyData(uint3(dim[0],dim[1],dim[2]), offset, data) + if not res: + raise RuntimeError(f'Test {offset} to pass tensor to Falcor failed') + +def testTensorFromFalcor(test_pass, iterations=10): + print('Testing passing tensors from Falcor') + for offset in range(iterations): + dim = random.sample(range(1, 32), 3) + data = test_pass.generateData(uint3(dim[0],dim[1],dim[2]), offset) + + # Check the returned tensor + if not isinstance(data, torch.Tensor): + raise RuntimeError('Expected torch.Tensor object') + if not data.is_cuda or data.dtype != torch.float32: + raise RuntimeError('Expected CUDA float tensor') + if list(data.size()) != dim: + raise RuntimeError(f'Unexpected tensor dimensions (dim {list(data.size())}, expected dim {dim})') + + d = data.to("cpu") + count = 0 + for k in range(dim[0]): + for j in range(dim[1]): + for i in range(dim[2]): + idx = (k * dim[1] + j) * dim[2] + i + if d[k][j][i] == float(idx + offset): + count += 1 + + elemCount = dim[0] * dim[1] * dim[2] + if count != elemCount: + raise RuntimeError(f'Unexpected tensor data ({counter} out of {elemCount} values correct)", , elemCount)') + +def main(): + random.seed(1) + + # Create torch CUDA device + print('Creating CUDA device') + if not torch.cuda.is_available(): + raise RuntimeError('CUDA not available') + device = torch.device("cuda:0") + print(device) + torch.set_default_tensor_type(torch.cuda.FloatTensor) + + # Create testbed + testbed = falcor.Testbed() + render_graph = testbed.createRenderGraph("TestPybind") + test_pass = render_graph.createPass("test_pybind_pass", "TestPyTorchPass", {}) + testbed.renderGraph = render_graph + + # Test passing tensors to Falcor + testTensorToFalcor(device, test_pass, 100) + + # Test passing tensors from Falcor + testTensorFromFalcor(test_pass, 100) + + print('SUCCESS!') + +if __name__ == '__main__': + main() diff --git a/setup.sh b/setup.sh index aeb8c2a82..3796cac4d 100644 --- a/setup.sh +++ b/setup.sh @@ -39,8 +39,4 @@ fi echo "Patching NVTT package ..." cp -fp ${BASE_DIR}/external/packman/nvtt/libnvtt.so.30106 ${BASE_DIR}/external/packman/nvtt/libnvtt.so -# HACK: Copy libtbb.so from release to debug so we can have a proper import target. -echo "Patching falcor_deps package ..." -cp -fp ${BASE_DIR}/external/packman/deps/lib/libtbb.so* ${BASE_DIR}/external/packman/deps/debug/lib - exit 0 diff --git a/setup_vs2019.bat b/setup_vs2019.bat deleted file mode 100644 index 2c6c7bff3..000000000 --- a/setup_vs2019.bat +++ /dev/null @@ -1,36 +0,0 @@ -: This script sets up a Visual Studio 2019 solution. - -@echo off -setlocal - -set PRESET_SUFFIX="" - -if "%~1"=="ci" ( - set PRESET_SUFFIX="-ci" -) - -: Fetch dependencies. -call %~dp0\setup.bat - -: Configuration. -set PRESET=windows-vs2019 -set TOOLSET=host=x86 -set CMAKE_EXE=%~dp0\tools\.packman\cmake\bin\cmake.exe -set CUSTOM_CUDA_DIR=%~dp0\external\packman\cuda - -: Check if custom CUDA directory contains a valid CUDA SDK. -: Adjust toolset string to use the custom CUDA toolkit. -if exist %CUSTOM_CUDA_DIR%\bin\nvcc.exe ( - set TOOLSET=%TOOLSET%,cuda="%CUSTOM_CUDA_DIR%" -) - -: Configure solution by running cmake. -echo Configuring Visual Studio solution ... -%CMAKE_EXE% --preset %PRESET% -T %TOOLSET% -if errorlevel 1 ( - echo Failed to configure solution! - exit /b 1 -) - -: Success. -exit /b 0 diff --git a/tests/image_tests/renderpasses/graphs/CrossFadePass.py b/tests/image_tests/renderpasses/graphs/CrossFadePass.py new file mode 100644 index 000000000..b7fa0e4f8 --- /dev/null +++ b/tests/image_tests/renderpasses/graphs/CrossFadePass.py @@ -0,0 +1,18 @@ +from falcor import * + +def render_graph_CrossFadePass(): + g = RenderGraph("CrossFade Pass") + CrossFade = createPass("CrossFade") + g.addPass(CrossFade, "CrossFade") + ImageLoaderA = createPass("ImageLoader", {'filename': 'Cubemaps/Sorsele3/posz.jpg', 'mips': False, 'srgb': True, 'arrayIndex': 0, 'mipLevel': 0}) + g.addPass(ImageLoaderA, "ImageLoaderA") + ImageLoaderB = createPass("ImageLoader", {'filename': 'smoke-puff.png', 'mips': False, 'srgb': True, 'arrayIndex': 0, 'mipLevel': 0}) + g.addPass(ImageLoaderB, "ImageLoaderB") + g.addEdge("ImageLoaderA.dst", "CrossFade.A") + g.addEdge("ImageLoaderB.dst", "CrossFade.B") + g.markOutput("CrossFade.out") + return g + +CrossFadePass = render_graph_CrossFadePass() +try: m.addGraph(CrossFadePass) +except NameError: None diff --git a/tests/image_tests/renderpasses/test_CrossFadePass.py b/tests/image_tests/renderpasses/test_CrossFadePass.py new file mode 100644 index 000000000..ba5c2722e --- /dev/null +++ b/tests/image_tests/renderpasses/test_CrossFadePass.py @@ -0,0 +1,16 @@ +IMAGE_TEST = { + "device_types": ["d3d12", "vulkan"] +} + +import sys +sys.path.append('..') +from helpers import render_frames +from graphs.CrossFadePass import CrossFadePass as g +from falcor import * + +m.addGraph(g) + +# default +render_frames(m, 'default', frames=[10,50]) + +exit() diff --git a/tests/image_tests/renderpasses/test_PathTracerMaterials.py b/tests/image_tests/renderpasses/test_PathTracerMaterials.py index b9c56e440..1a40d3c4e 100644 --- a/tests/image_tests/renderpasses/test_PathTracerMaterials.py +++ b/tests/image_tests/renderpasses/test_PathTracerMaterials.py @@ -14,6 +14,10 @@ m.loadScene('TestScenes/Materials/Materials.pyscene') render_frames(m, 'types', frames=[1,256]) +# Test for light leaks +m.loadScene('TestScenes/Materials/LightLeaks.pyscene') +render_frames(m, 'leaks', frames=[1,256]) + # Test alpha testing m.loadScene('TestScenes/AlphaTest/AlphaTest.pyscene') render_frames(m, 'alpha', frames=[1,64]) diff --git a/tests/image_tests/renderscripts/test_PathTracerNRD.py b/tests/image_tests/renderscripts/test_PathTracerNRD.py index c5153e26c..8bf9cfe6c 100644 --- a/tests/image_tests/renderscripts/test_PathTracerNRD.py +++ b/tests/image_tests/renderscripts/test_PathTracerNRD.py @@ -1,5 +1,5 @@ IMAGE_TEST = { - 'tolerance': 1e-6 + 'tolerance': 2e-6 } # NOTE: diff --git a/tests/python_tests/core/__init__.py b/tests/python_tests/core/__init__.py new file mode 100644 index 000000000..93aaa9edb --- /dev/null +++ b/tests/python_tests/core/__init__.py @@ -0,0 +1 @@ +# do not remove diff --git a/tests/python_tests/core/test_device.py b/tests/python_tests/core/test_device.py new file mode 100644 index 000000000..c38e36016 --- /dev/null +++ b/tests/python_tests/core/test_device.py @@ -0,0 +1,26 @@ +import os +import unittest +import falcor + +if os.name == 'nt': + DEVICE_TYPES=[falcor.DeviceType.D3D12, falcor.DeviceType.Vulkan] +else: + DEVICE_TYPES=[falcor.DeviceType.Vulkan] + +class TestDevice(unittest.TestCase): + + def test_create(self): + for device_type in DEVICE_TYPES: + with self.subTest(): + print(f"device_type={device_type}") + + device = falcor.Device(type=device_type) + self.assertTrue(device is not None) + + print(f"max_compute_dispatch_thread_groups={device.limits.max_compute_dispatch_thread_groups}") + print(f"max_shader_visible_samplers={device.limits.max_shader_visible_samplers}") + + del device + +if __name__ == '__main__': + unittest.main() diff --git a/tests/python_tests/test_dummy.py b/tests/python_tests/test_dummy.py new file mode 100644 index 000000000..544aba0e9 --- /dev/null +++ b/tests/python_tests/test_dummy.py @@ -0,0 +1,12 @@ +import unittest + +class TestDummy(unittest.TestCase): + def test_upper(self): + self.assertEqual("foo".upper(), "FOO") + + def test_isupper(self): + self.assertTrue("FOO".isupper()) + self.assertFalse("Foo".isupper()) + +if __name__ == '__main__': + unittest.main() diff --git a/tests/run_python_tests.bat b/tests/run_python_tests.bat new file mode 100644 index 000000000..bfcb15cfe --- /dev/null +++ b/tests/run_python_tests.bat @@ -0,0 +1,9 @@ +@echo off + +set pwd=%~dp0 +set project_dir=%pwd%..\ +set python=%project_dir%tools\.packman\python\python.exe + +if not exist %python% call %project_dir%setup.bat + +call %python% %pwd%testing/run_python_tests.py %* diff --git a/tests/run_python_tests.sh b/tests/run_python_tests.sh new file mode 100644 index 000000000..0da6ed8e1 --- /dev/null +++ b/tests/run_python_tests.sh @@ -0,0 +1,12 @@ +#!/bin/sh + +export pwd=`pwd` +export project_dir=$pwd/.. +export python_dir=$project_dir/tools/.packman/python +export python=$python_dir/bin/python3 + +if [ ! -f "$python" ]; then + $project_dir/setup.sh +fi + +env LD_LIBRARY_PATH="$python_dir/lib" $python $pwd/testing/run_python_tests.py $@ diff --git a/tests/testing/core/config.py b/tests/testing/core/config.py index c96ae4b8c..f21002435 100644 --- a/tests/testing/core/config.py +++ b/tests/testing/core/config.py @@ -24,22 +24,12 @@ # Suffix to use for error images. ERROR_IMAGE_SUFFIX = '.error.png' +PYTHON_TESTS_DIR = "tests/python_tests" + # Build configurations. BUILD_CONFIGS = { # Temporary build configurations combining a CMake preset and build type. # These should be replaced by parsing CMakePresets.json in the future. - 'windows-vs2019-Release': { - 'build_dir': 'build/windows-vs2019/bin/Release' - }, - 'windows-vs2019-Debug': { - 'build_dir': 'build/windows-vs2019/bin/Debug' - }, - 'windows-vs2019-ci-Release': { - 'build_dir': 'build/windows-vs2019-ci/bin/Release' - }, - 'windows-vs2019-ci-Debug': { - 'build_dir': 'build/windows-vs2019-ci/bin/Debug' - }, 'windows-vs2022-Release': { 'build_dir': 'build/windows-vs2022/bin/Release' }, @@ -64,35 +54,51 @@ 'windows-ninja-msvc-ci-Debug': { 'build_dir': 'build/windows-ninja-msvc-ci/bin/Debug' }, - 'linux-ninja-clang-Release': { - 'build_dir': 'build/linux-ninja-clang/bin/Release' + 'linux-clang-Release': { + 'build_dir': 'build/linux-clang/bin/Release' + }, + 'linux-clang-Debug': { + 'build_dir': 'build/linux-clang/bin/Debug' + }, + 'linux-clang-ci-Release': { + 'build_dir': 'build/linux-clang-ci/bin/Release' + }, + 'linux-clang-ci-Debug': { + 'build_dir': 'build/linux-clang-ci/bin/Debug' + }, + 'linux-gcc-Release': { + 'build_dir': 'build/linux-gcc/bin/Release' }, - 'linux-ninja-clang-Debug': { - 'build_dir': 'build/linux-ninja-clang/bin/Debug' + 'linux-gcc-Debug': { + 'build_dir': 'build/linux-gcc/bin/Debug' }, - 'linux-ninja-clang-ci-Release': { - 'build_dir': 'build/linux-ninja-clang-ci/bin/Release' + 'linux-gcc-ci-Release': { + 'build_dir': 'build/linux-gcc-ci/bin/Release' }, - 'linux-ninja-clang-ci-Debug': { - 'build_dir': 'build/linux-ninja-clang-ci/bin/Debug' + 'linux-gcc-ci-Debug': { + 'build_dir': 'build/linux-gcc-ci/bin/Debug' }, } if os.name == 'nt': # Executables. CMAKE_EXE = "tools/.packman/cmake/bin/cmake.exe" + FALCOR_LIB = 'Falcor.dll' FALCOR_TEST_EXE = 'FalcorTest.exe' MOGWAI_EXE = 'Mogwai.exe' IMAGE_COMPARE_EXE = 'ImageCompare.exe' + PYTHON_EXE = "pythondist/python.exe" SUPPORTED_DEVICE_TYPES = ["d3d12", "vulkan"] elif os.name == 'posix': # Executables. CMAKE_EXE = "tools/.packman/cmake/bin/cmake" + FALCOR_LIB = 'libFalcor.so' FALCOR_TEST_EXE = 'FalcorTest' MOGWAI_EXE = 'Mogwai' IMAGE_COMPARE_EXE = 'ImageCompare' + PYTHON_EXE = "pythondist/python" SUPPORTED_DEVICE_TYPES = ["vulkan"] diff --git a/tests/testing/core/environment.py b/tests/testing/core/environment.py index 1675072cd..e7bf29393 100644 --- a/tests/testing/core/environment.py +++ b/tests/testing/core/environment.py @@ -24,6 +24,30 @@ def validate_json(data, schema, full_name=None): elif prop_schema.get('optional', False) == False: raise TypeError(f'Property "{name}" does not exist') + +def find_most_recent_build_config(): + ''' + Find the build config most recently built by checking all + possible build directories and finding the most recently + changed Falcor library. + ''' + project_dir = Path(__file__).parents[3].resolve() + + best_config_name = None + best_config_time = None + + for config_name, config_values in config.BUILD_CONFIGS.items(): + build_dir = project_dir / config_values["build_dir"] + falcor_lib = build_dir / config.FALCOR_LIB + if not os.path.exists(falcor_lib): + continue + stat = os.stat(falcor_lib) + if not best_config_name or stat.st_mtime >= best_config_time: + best_config_name = config_name + best_config_time = stat.st_mtime + + return best_config_name + class Environment: ''' Holds a bunch of variables necessary to run the testing infrastructure. @@ -62,7 +86,7 @@ def __init__(self, json_file, build_config): # Validate build configuration. if not build_config in config.BUILD_CONFIGS.keys(): - raise Exception(f'Invalid build configuration "{build_config}". Choose from: {", ".join(config.BUILD_CONFIGS.keys())}.') + raise Exception(f'Invalid build configuration "{build_config}".') # Setup environment variables. self.name = env['name'] @@ -76,6 +100,7 @@ def __init__(self, json_file, build_config): self.image_tests_result_dir = env['image_tests']['result_dir'] self.image_tests_ref_dir = env['image_tests']['ref_dir'] self.image_tests_remote_ref_dir = env['image_tests'].get('remote_ref_dir', None) + self.python_tests_dir = self.project_dir / config.PYTHON_TESTS_DIR self.build_config = build_config self.branch = helpers.get_git_head_branch(self.project_dir) @@ -84,6 +109,7 @@ def __init__(self, json_file, build_config): self.falcor_test_exe = self.build_dir / config.FALCOR_TEST_EXE self.mogwai_exe = self.build_dir / config.MOGWAI_EXE self.image_compare_exe = self.build_dir / config.IMAGE_COMPARE_EXE + self.python_exe = self.build_dir / config.PYTHON_EXE def resolve_image_dir(self, image_dir, branch, build_id): ''' diff --git a/tests/testing/libs/xmlrunner/LICENSE.txt b/tests/testing/libs/xmlrunner/LICENSE.txt new file mode 100644 index 000000000..870a799f8 --- /dev/null +++ b/tests/testing/libs/xmlrunner/LICENSE.txt @@ -0,0 +1,26 @@ +Copyright (c) 2008-2013, Daniel Fernandes Martins +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +The views and conclusions contained in the software and documentation are those +of the authors and should not be interpreted as representing official policies, +either expressed or implied, of the FreeBSD Project. \ No newline at end of file diff --git a/tests/testing/libs/xmlrunner/__init__.py b/tests/testing/libs/xmlrunner/__init__.py new file mode 100644 index 000000000..d244bb63c --- /dev/null +++ b/tests/testing/libs/xmlrunner/__init__.py @@ -0,0 +1,13 @@ +# -*- coding: utf-8 -*- + +""" +This module provides the XMLTestRunner class, which is heavily based on the +default TextTestRunner. +""" + +# Allow version to be detected at runtime. +from .version import __version__ + +from .runner import XMLTestRunner + +__all__ = ('__version__', 'XMLTestRunner') diff --git a/tests/testing/libs/xmlrunner/__main__.py b/tests/testing/libs/xmlrunner/__main__.py new file mode 100644 index 000000000..1f90374a2 --- /dev/null +++ b/tests/testing/libs/xmlrunner/__main__.py @@ -0,0 +1,19 @@ +"""Main entry point""" + +import sys +from .runner import XMLTestProgram + +if sys.argv[0].endswith("__main__.py"): + import os.path + # We change sys.argv[0] to make help message more useful + # use executable without path, unquoted + # (it's just a hint anyway) + # (if you have spaces in your executable you get what you deserve!) + executable = os.path.basename(sys.executable) + sys.argv[0] = executable + " -m xmlrunner" + del os + +__unittest = True + + +XMLTestProgram(module=None) diff --git a/tests/testing/libs/xmlrunner/builder.py b/tests/testing/libs/xmlrunner/builder.py new file mode 100644 index 000000000..3eff61d6b --- /dev/null +++ b/tests/testing/libs/xmlrunner/builder.py @@ -0,0 +1,247 @@ +import re +import sys +import datetime +import time + +from xml.dom.minidom import Document + + +__all__ = ('TestXMLBuilder', 'TestXMLContext') + + +# see issue #74, the encoding name needs to be one of +# http://www.iana.org/assignments/character-sets/character-sets.xhtml +UTF8 = 'UTF-8' + +# Workaround for Python bug #5166 +# http://bugs.python.org/issue5166 + +_char_tail = '' + +if sys.maxunicode > 0x10000: + _char_tail = (u'%s-%s') % ( + chr(0x10000), + chr(min(sys.maxunicode, 0x10FFFF)) + ) + +_nontext_sub = re.compile( + r'[^\x09\x0A\x0D\x20-\uD7FF\uE000-\uFFFD%s]' % _char_tail, + re.U +).sub + + +def replace_nontext(text, replacement=u'\uFFFD'): + return _nontext_sub(replacement, text) + + +class TestXMLContext(object): + """A XML report file have a distinct hierarchy. The outermost element is + 'testsuites', which contains one or more 'testsuite' elements. The role of + these elements is to give the proper context to 'testcase' elements. + + These contexts have a few things in common: they all have some sort of + counters (i.e. how many testcases are inside that context, how many failed, + and so on), they all have a 'time' attribute indicating how long it took + for their testcases to run, etc. + + The purpose of this class is to abstract the job of composing this + hierarchy while keeping track of counters and how long it took for a + context to be processed. + """ + + # Allowed keys for self.counters + _allowed_counters = ('tests', 'errors', 'failures', 'skipped',) + + def __init__(self, xml_doc, parent_context=None): + """Creates a new instance of a root or nested context (depending whether + `parent_context` is provided or not). + """ + self.xml_doc = xml_doc + self.parent = parent_context + self._start_time_m = 0 + self._stop_time_m = 0 + self._stop_time = 0 + self.counters = {} + + def element_tag(self): + """Returns the name of the tag represented by this context. + """ + return self.element.tagName + + def begin(self, tag, name): + """Begins the creation of this context in the XML document by creating + an empty tag . + """ + self.element = self.xml_doc.createElement(tag) + self.element.setAttribute('name', replace_nontext(name)) + self._start_time = time.monotonic() + + def end(self): + """Closes this context (started with a call to `begin`) and creates an + attribute for each counter and another for the elapsed time. + """ + # time.monotonic is reliable for measuring differences, not affected by NTP + self._stop_time_m = time.monotonic() + # time.time is used for reference point + self._stop_time = time.time() + self.element.setAttribute('time', self.elapsed_time()) + self.element.setAttribute('timestamp', self.timestamp()) + self._set_result_counters() + return self.element + + def _set_result_counters(self): + """Sets an attribute in this context's tag for each counter considering + what's valid for each tag name. + """ + tag = self.element_tag() + + for counter_name in TestXMLContext._allowed_counters: + valid_counter_for_element = False + + if counter_name == 'skipped': + valid_counter_for_element = ( + tag == 'testsuite' + ) + else: + valid_counter_for_element = ( + tag in ('testsuites', 'testsuite') + ) + + if valid_counter_for_element: + value = str( + self.counters.get(counter_name, 0) + ) + self.element.setAttribute(counter_name, value) + + def increment_counter(self, counter_name): + """Increments a counter named by `counter_name`, which can be any one + defined in `_allowed_counters`. + """ + if counter_name in TestXMLContext._allowed_counters: + self.counters[counter_name] = \ + self.counters.get(counter_name, 0) + 1 + + def elapsed_time(self): + """Returns the time the context took to run between the calls to + `begin()` and `end()`, in seconds. + """ + return format(self._stop_time_m - self._start_time_m, '.3f') + + def timestamp(self): + """Returns the time the context ended as ISO-8601-formatted timestamp. + """ + return datetime.datetime.fromtimestamp(self._stop_time).replace(microsecond=0).isoformat() + + +class TestXMLBuilder(object): + """This class encapsulates most rules needed to create a XML test report + behind a simple interface. + """ + + def __init__(self): + """Creates a new instance. + """ + self._xml_doc = Document() + self._current_context = None + + def current_context(self): + """Returns the current context. + """ + return self._current_context + + def begin_context(self, tag, name): + """Begins a new context in the XML test report, which usually is defined + by one on the tags 'testsuites', 'testsuite', or 'testcase'. + """ + context = TestXMLContext(self._xml_doc, self._current_context) + context.begin(tag, name) + + self._current_context = context + + def context_tag(self): + """Returns the tag represented by the current context. + """ + return self._current_context.element_tag() + + def _create_cdata_section(self, content): + """Returns a new CDATA section containing the string defined in + `content`. + """ + filtered_content = replace_nontext(content) + return self._xml_doc.createCDATASection(filtered_content) + + def append_cdata_section(self, tag, content): + """Appends a tag in the format CDATA into the tag represented + by the current context. Returns the created tag. + """ + element = self._xml_doc.createElement(tag) + + pos = content.find(']]>') + while pos >= 0: + tmp = content[0:pos+2] + element.appendChild(self._create_cdata_section(tmp)) + content = content[pos+2:] + pos = content.find(']]>') + + element.appendChild(self._create_cdata_section(content)) + + self._append_child(element) + return element + + def append(self, tag, content, **kwargs): + """Apends a tag in the format CDATA + into the tag represented by the current context. Returns the created + tag. + """ + element = self._xml_doc.createElement(tag) + + for key, value in kwargs.items(): + filtered_value = replace_nontext(str(value)) + element.setAttribute(key, filtered_value) + + if content: + element.appendChild(self._create_cdata_section(content)) + + self._append_child(element) + return element + + def _append_child(self, element): + """Appends a tag object represented by `element` into the tag + represented by the current context. + """ + if self._current_context: + self._current_context.element.appendChild(element) + else: + self._xml_doc.appendChild(element) + + def increment_counter(self, counter_name): + """Increments a counter in the current context and their parents. + """ + context = self._current_context + + while context: + context.increment_counter(counter_name) + context = context.parent + + def end_context(self): + """Ends the current context and sets the current context as being the + previous one (if it exists). Also, when a context ends, its tag is + appended in the proper place inside the document. + """ + if not self._current_context: + return False + + element = self._current_context.end() + + self._current_context = self._current_context.parent + self._append_child(element) + + return True + + def finish(self): + """Ends all open contexts and returns a pretty printed version of the + generated XML document. + """ + while self.end_context(): + pass + return self._xml_doc.toprettyxml(indent='\t', encoding=UTF8) diff --git a/tests/testing/libs/xmlrunner/result.py b/tests/testing/libs/xmlrunner/result.py new file mode 100644 index 000000000..dd90b8230 --- /dev/null +++ b/tests/testing/libs/xmlrunner/result.py @@ -0,0 +1,677 @@ + +import inspect +import io +import os +import sys +import datetime +import traceback +import re +from os import path +from io import StringIO + +# use direct import to bypass freezegun +from time import time + +from .unittest import TestResult, TextTestResult, failfast + + +# Matches invalid XML1.0 unicode characters, like control characters: +# http://www.w3.org/TR/2006/REC-xml-20060816/#charsets +# http://stackoverflow.com/questions/1707890/fast-way-to-filter-illegal-xml-unicode-chars-in-python + +_illegal_unichrs = [ + (0x00, 0x08), (0x0B, 0x0C), (0x0E, 0x1F), + (0x7F, 0x84), (0x86, 0x9F), + (0xFDD0, 0xFDDF), (0xFFFE, 0xFFFF), +] +if sys.maxunicode >= 0x10000: # not narrow build + _illegal_unichrs.extend([ + (0x1FFFE, 0x1FFFF), (0x2FFFE, 0x2FFFF), + (0x3FFFE, 0x3FFFF), (0x4FFFE, 0x4FFFF), + (0x5FFFE, 0x5FFFF), (0x6FFFE, 0x6FFFF), + (0x7FFFE, 0x7FFFF), (0x8FFFE, 0x8FFFF), + (0x9FFFE, 0x9FFFF), (0xAFFFE, 0xAFFFF), + (0xBFFFE, 0xBFFFF), (0xCFFFE, 0xCFFFF), + (0xDFFFE, 0xDFFFF), (0xEFFFE, 0xEFFFF), + (0xFFFFE, 0xFFFFF), (0x10FFFE, 0x10FFFF), + ]) + +_illegal_ranges = [ + "%s-%s" % (chr(low), chr(high)) + for (low, high) in _illegal_unichrs +] + +INVALID_XML_1_0_UNICODE_RE = re.compile(u'[%s]' % u''.join(_illegal_ranges)) + + +STDOUT_LINE = '\nStdout:\n%s' +STDERR_LINE = '\nStderr:\n%s' + + +def safe_unicode(data, encoding='utf8'): + """Return a unicode string containing only valid XML characters. + + encoding - if data is a byte string it is first decoded to unicode + using this encoding. + """ + data = str(data) + return INVALID_XML_1_0_UNICODE_RE.sub('', data) + + +def testcase_name(test_method): + testcase = type(test_method) + + # Ignore module name if it is '__main__' + module = testcase.__module__ + '.' + if module == '__main__.': + module = '' + result = module + testcase.__name__ + return result + + +def resolve_filename(filename): + # Try to make filename relative to current directory. + try: + rel_filename = os.path.relpath(filename) + except ValueError: + return filename + # if not inside folder, keep as-is + return filename if rel_filename.startswith('../') else rel_filename + + +class _DuplicateWriter(io.TextIOBase): + """ + Duplicate output from the first handle to the second handle + + The second handle is expected to be a StringIO and not to block. + """ + + def __init__(self, first, second): + super(_DuplicateWriter, self).__init__() + self._first = first + self._second = second + + def flush(self): + self._first.flush() + self._second.flush() + + def writable(self): + return True + + def getvalue(self): + return self._second.getvalue() + + def writelines(self, lines): + self._first.writelines(lines) + self._second.writelines(lines) + + def write(self, b): + if isinstance(self._first, io.TextIOBase): + wrote = self._first.write(b) + + if wrote is not None: + # expected to always succeed to write + self._second.write(b[:wrote]) + + return wrote + else: + # file-like object that doesn't return wrote bytes. + self._first.write(b) + self._second.write(b) + return len(b) + + +class _TestInfo(object): + """ + This class keeps useful information about the execution of a + test method. + """ + + # Possible test outcomes + (SUCCESS, FAILURE, ERROR, SKIP) = range(4) + + OUTCOME_ELEMENTS = { + SUCCESS: None, + FAILURE: 'failure', + ERROR: 'error', + SKIP: 'skipped', + } + + def __init__(self, test_result, test_method, outcome=SUCCESS, err=None, subTest=None, filename=None, lineno=None, doc=None): + self.test_result = test_result + self.outcome = outcome + self.elapsed_time = 0 + self.timestamp = datetime.datetime.min.replace(microsecond=0).isoformat() + if err is not None: + if self.outcome != _TestInfo.SKIP: + self.test_exception_name = safe_unicode(err[0].__name__) + self.test_exception_message = safe_unicode(err[1]) + else: + self.test_exception_message = safe_unicode(err) + + self.stdout = test_result._stdout_data + self.stderr = test_result._stderr_data + + self.test_description = self.test_result.getDescription(test_method) + self.test_exception_info = ( + '' if outcome in (self.SUCCESS, self.SKIP) + else self.test_result._exc_info_to_string( + err, test_method) + ) + + self.test_name = testcase_name(test_method) + self.test_id = test_method.id() + + if subTest: + self.test_id = subTest.id() + self.test_description = self.test_result.getDescription(subTest) + + self.filename = filename + self.lineno = lineno + self.doc = doc + + def id(self): + return self.test_id + + def test_finished(self): + """Save info that can only be calculated once a test has run. + """ + self.elapsed_time = \ + self.test_result.stop_time - self.test_result.start_time + timestamp = datetime.datetime.fromtimestamp(self.test_result.stop_time) + self.timestamp = timestamp.replace(microsecond=0).isoformat() + + def get_error_info(self): + """ + Return a text representation of an exception thrown by a test + method. + """ + return self.test_exception_info + + +class _XMLTestResult(TextTestResult): + """ + A test result class that can express test results in a XML report. + + Used by XMLTestRunner. + """ + def __init__(self, stream=sys.stderr, descriptions=1, verbosity=1, + elapsed_times=True, properties=None, infoclass=None): + TextTestResult.__init__(self, stream, descriptions, verbosity) + self._stdout_data = None + self._stderr_data = None + self._stdout_capture = StringIO() + self.__stdout_saved = None + self._stderr_capture = StringIO() + self.__stderr_saved = None + self.successes = [] + self.callback = None + self.elapsed_times = elapsed_times + self.properties = properties # junit testsuite properties + self.filename = None + self.lineno = None + self.doc = None + if infoclass is None: + self.infoclass = _TestInfo + else: + self.infoclass = infoclass + + def _prepare_callback(self, test_info, target_list, verbose_str, + short_str): + """ + Appends a `infoclass` to the given target list and sets a callback + method to be called by stopTest method. + """ + test_info.filename = self.filename + test_info.lineno = self.lineno + test_info.doc = self.doc + target_list.append(test_info) + + def callback(): + """Prints the test method outcome to the stream, as well as + the elapsed time. + """ + + test_info.test_finished() + + # Ignore the elapsed times for a more reliable unit testing + if not self.elapsed_times: + self.start_time = self.stop_time = 0 + + if self.showAll: + self.stream.writeln( + '%s (%.3fs)' % (verbose_str, test_info.elapsed_time) + ) + elif self.dots: + self.stream.write(short_str) + + self.stream.flush() + + self.callback = callback + + def startTest(self, test): + """ + Called before execute each test method. + """ + self.start_time = time() + TestResult.startTest(self, test) + + try: + if getattr(test, '_dt_test', None) is not None: + # doctest.DocTestCase + self.filename = test._dt_test.filename + self.lineno = test._dt_test.lineno + else: + # regular unittest.TestCase? + test_method = getattr(test, test._testMethodName) + test_class = type(test) + # Note: inspect can get confused with decorators, so use class. + self.filename = inspect.getsourcefile(test_class) + # Handle partial and partialmethod objects. + test_method = getattr(test_method, 'func', test_method) + _, self.lineno = inspect.getsourcelines(test_method) + + self.doc = test_method.__doc__ + except (AttributeError, IOError, TypeError): + # issue #188, #189, #195 + # some frameworks can make test method opaque. + pass + + if self.showAll: + self.stream.write(' ' + self.getDescription(test)) + self.stream.write(" ... ") + self.stream.flush() + + def _setupStdout(self): + """ + Capture stdout / stderr by replacing sys.stdout / sys.stderr + """ + super(_XMLTestResult, self)._setupStdout() + self.__stdout_saved = sys.stdout + sys.stdout = _DuplicateWriter(sys.stdout, self._stdout_capture) + self.__stderr_saved = sys.stderr + sys.stderr = _DuplicateWriter(sys.stderr, self._stderr_capture) + + def _restoreStdout(self): + """ + Stop capturing stdout / stderr and recover sys.stdout / sys.stderr + """ + if self.__stdout_saved: + sys.stdout = self.__stdout_saved + self.__stdout_saved = None + if self.__stderr_saved: + sys.stderr = self.__stderr_saved + self.__stderr_saved = None + self._stdout_capture.seek(0) + self._stdout_capture.truncate() + self._stderr_capture.seek(0) + self._stderr_capture.truncate() + super(_XMLTestResult, self)._restoreStdout() + + def _save_output_data(self): + self._stdout_data = self._stdout_capture.getvalue() + self._stderr_data = self._stderr_capture.getvalue() + + def stopTest(self, test): + """ + Called after execute each test method. + """ + self._save_output_data() + # self._stdout_data = sys.stdout.getvalue() + # self._stderr_data = sys.stderr.getvalue() + + TextTestResult.stopTest(self, test) + self.stop_time = time() + + if self.callback and callable(self.callback): + self.callback() + self.callback = None + + def addSuccess(self, test): + """ + Called when a test executes successfully. + """ + self._save_output_data() + self._prepare_callback( + self.infoclass(self, test), self.successes, 'ok', '.' + ) + + @failfast + def addFailure(self, test, err): + """ + Called when a test method fails. + """ + self._save_output_data() + testinfo = self.infoclass( + self, test, self.infoclass.FAILURE, err) + self.failures.append(( + testinfo, + self._exc_info_to_string(err, test) + )) + self._prepare_callback(testinfo, [], 'FAIL', 'F') + + @failfast + def addError(self, test, err): + """ + Called when a test method raises an error. + """ + self._save_output_data() + testinfo = self.infoclass( + self, test, self.infoclass.ERROR, err) + self.errors.append(( + testinfo, + self._exc_info_to_string(err, test) + )) + self._prepare_callback(testinfo, [], 'ERROR', 'E') + + def addSubTest(self, testcase, test, err): + """ + Called when a subTest method raises an error. + """ + if err is not None: + + errorText = None + errorValue = None + errorList = None + if issubclass(err[0], test.failureException): + errorText = 'FAIL' + errorValue = self.infoclass.FAILURE + errorList = self.failures + + else: + errorText = 'ERROR' + errorValue = self.infoclass.ERROR + errorList = self.errors + + self._save_output_data() + + testinfo = self.infoclass( + self, testcase, errorValue, err, subTest=test) + errorList.append(( + testinfo, + self._exc_info_to_string(err, testcase) + )) + self._prepare_callback(testinfo, [], errorText, errorText[0]) + + def addSkip(self, test, reason): + """ + Called when a test method was skipped. + """ + self._save_output_data() + testinfo = self.infoclass( + self, test, self.infoclass.SKIP, reason) + testinfo.test_exception_name = 'skip' + testinfo.test_exception_message = reason + self.skipped.append((testinfo, reason)) + self._prepare_callback(testinfo, [], 'skip', 's') + + def addExpectedFailure(self, test, err): + """ + Missing in xmlrunner, copy-pasted from xmlrunner addError. + """ + self._save_output_data() + + testinfo = self.infoclass(self, test, self.infoclass.SKIP, err) + testinfo.test_exception_name = 'XFAIL' + testinfo.test_exception_message = 'expected failure: {}'.format(testinfo.test_exception_message) + + self.expectedFailures.append((testinfo, self._exc_info_to_string(err, test))) + self._prepare_callback(testinfo, [], 'expected failure', 'x') + + @failfast + def addUnexpectedSuccess(self, test): + """ + Missing in xmlrunner, copy-pasted from xmlrunner addSuccess. + """ + self._save_output_data() + + testinfo = self.infoclass(self, test) # do not set outcome here because it will need exception + testinfo.outcome = self.infoclass.ERROR + # But since we want to have error outcome, we need to provide additional fields: + testinfo.test_exception_name = 'UnexpectedSuccess' + testinfo.test_exception_message = ('Unexpected success: This test was marked as expected failure but passed, ' + 'please review it') + + self.unexpectedSuccesses.append((testinfo, 'unexpected success')) + self._prepare_callback(testinfo, [], 'unexpected success', 'u') + + def printErrorList(self, flavour, errors): + """ + Writes information about the FAIL or ERROR to the stream. + """ + for test_info, dummy in errors: + self.stream.writeln(self.separator1) + self.stream.writeln( + '%s [%.3fs]: %s' % (flavour, test_info.elapsed_time, + test_info.test_description) + ) + self.stream.writeln(self.separator2) + self.stream.writeln('%s' % test_info.get_error_info()) + self.stream.flush() + + def _get_info_by_testcase(self): + """ + Organizes test results by TestCase module. This information is + used during the report generation, where a XML report will be created + for each TestCase. + """ + tests_by_testcase = {} + + for tests in (self.successes, self.failures, self.errors, + self.skipped, self.expectedFailures, self.unexpectedSuccesses): + for test_info in tests: + if isinstance(test_info, tuple): + # This is a skipped, error or a failure test case + test_info = test_info[0] + testcase_name = test_info.test_name + if testcase_name not in tests_by_testcase: + tests_by_testcase[testcase_name] = [] + tests_by_testcase[testcase_name].append(test_info) + + return tests_by_testcase + + def _report_testsuite_properties(xml_testsuite, xml_document, properties): + if properties: + xml_properties = xml_document.createElement('properties') + xml_testsuite.appendChild(xml_properties) + for key, value in properties.items(): + prop = xml_document.createElement('property') + prop.setAttribute('name', str(key)) + prop.setAttribute('value', str(value)) + xml_properties.appendChild(prop) + + _report_testsuite_properties = staticmethod(_report_testsuite_properties) + + def _report_testsuite(suite_name, tests, xml_document, parentElement, + properties): + """ + Appends the testsuite section to the XML document. + """ + testsuite = xml_document.createElement('testsuite') + parentElement.appendChild(testsuite) + module_name = suite_name.rpartition('.')[0] + file_name = module_name.replace('.', '/') + '.py' + + testsuite.setAttribute('name', suite_name) + testsuite.setAttribute('tests', str(len(tests))) + testsuite.setAttribute('file', file_name) + + testsuite.setAttribute( + 'time', '%.3f' % sum(map(lambda e: e.elapsed_time, tests)) + ) + if tests: + testsuite.setAttribute( + 'timestamp', max(map(lambda e: e.timestamp, tests)) + ) + failures = filter(lambda e: e.outcome == e.FAILURE, tests) + testsuite.setAttribute('failures', str(len(list(failures)))) + + errors = filter(lambda e: e.outcome == e.ERROR, tests) + testsuite.setAttribute('errors', str(len(list(errors)))) + + skips = filter(lambda e: e.outcome == _TestInfo.SKIP, tests) + testsuite.setAttribute('skipped', str(len(list(skips)))) + + _XMLTestResult._report_testsuite_properties( + testsuite, xml_document, properties) + + for test in tests: + _XMLTestResult._report_testcase(test, testsuite, xml_document) + + return testsuite + + _report_testsuite = staticmethod(_report_testsuite) + + def _test_method_name(test_id): + """ + Returns the test method name. + """ + # Trick subtest referencing objects + subtest_parts = test_id.split(' ') + test_method_name = subtest_parts[0].split('.')[-1] + subtest_method_name = [test_method_name] + subtest_parts[1:] + return ' '.join(subtest_method_name) + + _test_method_name = staticmethod(_test_method_name) + + def _createCDATAsections(xmldoc, node, text): + text = safe_unicode(text) + pos = text.find(']]>') + while pos >= 0: + tmp = text[0:pos+2] + cdata = xmldoc.createCDATASection(tmp) + node.appendChild(cdata) + text = text[pos+2:] + pos = text.find(']]>') + cdata = xmldoc.createCDATASection(text) + node.appendChild(cdata) + + _createCDATAsections = staticmethod(_createCDATAsections) + + def _report_testcase(test_result, xml_testsuite, xml_document): + """ + Appends a testcase section to the XML document. + """ + testcase = xml_document.createElement('testcase') + xml_testsuite.appendChild(testcase) + + class_name = re.sub(r'^__main__.', '', test_result.id()) + + # Trick subtest referencing objects + class_name = class_name.split(' ')[0].rpartition('.')[0] + + testcase.setAttribute('classname', class_name) + testcase.setAttribute( + 'name', _XMLTestResult._test_method_name(test_result.test_id) + ) + testcase.setAttribute('time', '%.3f' % test_result.elapsed_time) + testcase.setAttribute('timestamp', test_result.timestamp) + + if test_result.filename is not None: + # Try to make filename relative to current directory. + filename = resolve_filename(test_result.filename) + testcase.setAttribute('file', filename) + + if test_result.lineno is not None: + testcase.setAttribute('line', str(test_result.lineno)) + + if test_result.doc is not None: + comment = str(test_result.doc) + # The use of '--' is forbidden in XML comments + comment = comment.replace('--', '--') + testcase.appendChild(xml_document.createComment(safe_unicode(comment))) + + result_elem_name = test_result.OUTCOME_ELEMENTS[test_result.outcome] + + if result_elem_name is not None: + result_elem = xml_document.createElement(result_elem_name) + testcase.appendChild(result_elem) + + result_elem.setAttribute( + 'type', + test_result.test_exception_name + ) + result_elem.setAttribute( + 'message', + test_result.test_exception_message + ) + if test_result.get_error_info(): + error_info = safe_unicode(test_result.get_error_info()) + _XMLTestResult._createCDATAsections( + xml_document, result_elem, error_info) + + if test_result.stdout: + systemout = xml_document.createElement('system-out') + testcase.appendChild(systemout) + _XMLTestResult._createCDATAsections( + xml_document, systemout, test_result.stdout) + + if test_result.stderr: + systemout = xml_document.createElement('system-err') + testcase.appendChild(systemout) + _XMLTestResult._createCDATAsections( + xml_document, systemout, test_result.stderr) + + _report_testcase = staticmethod(_report_testcase) + + def generate_reports(self, test_runner): + """ + Generates the XML reports to a given XMLTestRunner object. + """ + from xml.dom.minidom import Document + all_results = self._get_info_by_testcase() + + outputHandledAsString = \ + isinstance(test_runner.output, str) + + if (outputHandledAsString and not os.path.exists(test_runner.output)): + os.makedirs(test_runner.output) + + if not outputHandledAsString: + doc = Document() + testsuite = doc.createElement('testsuites') + doc.appendChild(testsuite) + parentElement = testsuite + + for suite, tests in all_results.items(): + if outputHandledAsString: + doc = Document() + parentElement = doc + + suite_name = suite + if test_runner.outsuffix: + # not checking with 'is not None', empty means no suffix. + # suite_name = '%s-%s' % (suite, test_runner.outsuffix) + # FALCOR: Removed the suffix from the test name + pass + + # Build the XML file + testsuite = _XMLTestResult._report_testsuite( + suite_name, tests, doc, parentElement, self.properties + ) + + if outputHandledAsString: + xml_content = doc.toprettyxml( + indent='\t', + encoding=test_runner.encoding + ) + filename = path.join( + test_runner.output, + 'TEST-%s.xml' % suite_name) + with open(filename, 'wb') as report_file: + report_file.write(xml_content) + + if self.showAll: + self.stream.writeln('Generated XML report: {}'.format(filename)) + + if not outputHandledAsString: + # Assume that test_runner.output is a stream + xml_content = doc.toprettyxml( + indent='\t', + encoding=test_runner.encoding + ) + test_runner.output.write(xml_content) + + def _exc_info_to_string(self, err, test): + """Converts a sys.exc_info()-style tuple of values into a string.""" + return super(_XMLTestResult, self)._exc_info_to_string(err, test) diff --git a/tests/testing/libs/xmlrunner/runner.py b/tests/testing/libs/xmlrunner/runner.py new file mode 100644 index 000000000..6b6588b7e --- /dev/null +++ b/tests/testing/libs/xmlrunner/runner.py @@ -0,0 +1,192 @@ + +import argparse +import sys +import time + +from .unittest import TextTestRunner, TestProgram +from .result import _XMLTestResult + +# see issue #74, the encoding name needs to be one of +# http://www.iana.org/assignments/character-sets/character-sets.xhtml +UTF8 = 'UTF-8' + + +class XMLTestRunner(TextTestRunner): + """ + A test runner class that outputs the results in JUnit like XML files. + """ + + def __init__(self, output='.', outsuffix=None, + elapsed_times=True, encoding=UTF8, + resultclass=None, + **kwargs): + super(XMLTestRunner, self).__init__(**kwargs) + self.output = output + self.encoding = encoding + # None means default timestamped suffix + # '' (empty) means no suffix + if outsuffix is None: + outsuffix = time.strftime("%Y%m%d%H%M%S") + self.outsuffix = outsuffix + self.elapsed_times = elapsed_times + if resultclass is None: + self.resultclass = _XMLTestResult + else: + self.resultclass = resultclass + + def _make_result(self): + """ + Creates a TestResult object which will be used to store + information about the executed tests. + """ + # override in subclasses if necessary. + return self.resultclass( + self.stream, self.descriptions, self.verbosity, self.elapsed_times + ) + + def run(self, test): + """ + Runs the given test case or test suite. + """ + try: + # Prepare the test execution + result = self._make_result() + result.failfast = self.failfast + result.buffer = self.buffer + if hasattr(test, 'properties'): + # junit testsuite properties + result.properties = test.properties + + # Print a nice header + self.stream.writeln() + self.stream.writeln('Running tests...') + self.stream.writeln(result.separator2) + + # Execute tests + start_time = time.monotonic() + test(result) + stop_time = time.monotonic() + time_taken = stop_time - start_time + + # Print results + result.printErrors() + self.stream.writeln(result.separator2) + run = result.testsRun + self.stream.writeln("Ran %d test%s in %.3fs" % ( + run, run != 1 and "s" or "", time_taken) + ) + self.stream.writeln() + + # other metrics + expectedFails = len(result.expectedFailures) + unexpectedSuccesses = len(result.unexpectedSuccesses) + skipped = len(result.skipped) + + # Error traces + infos = [] + if not result.wasSuccessful(): + self.stream.write("FAILED") + failed, errored = map(len, (result.failures, result.errors)) + if failed: + infos.append("failures={0}".format(failed)) + if errored: + infos.append("errors={0}".format(errored)) + else: + self.stream.write("OK") + + if skipped: + infos.append("skipped={0}".format(skipped)) + if expectedFails: + infos.append("expected failures={0}".format(expectedFails)) + if unexpectedSuccesses: + infos.append("unexpected successes={0}".format( + unexpectedSuccesses)) + + if infos: + self.stream.writeln(" ({0})".format(", ".join(infos))) + else: + self.stream.write("\n") + + # Generate reports + self.stream.writeln() + self.stream.writeln('Generating XML reports...') + result.generate_reports(self) + finally: + pass + + return result + + +class XMLTestProgram(TestProgram): + + def __init__(self, *args, **kwargs): + kwargs.setdefault('testRunner', XMLTestRunner) + self.warnings = None # python2 fix + self._parseKnownArgs(kwargs) + super(XMLTestProgram, self).__init__(*args, **kwargs) + + def _parseKnownArgs(self, kwargs): + argv = kwargs.get('argv') + if argv is None: + argv = sys.argv + + # python2 argparse fix + parser = argparse.ArgumentParser(prog='xmlrunner') + group = parser.add_mutually_exclusive_group() + group.add_argument( + '-o', '--output', metavar='DIR', + help='Directory for storing XML reports (\'.\' default)') + group.add_argument( + '--output-file', metavar='FILENAME', + help='Filename for storing XML report') + parser.add_argument( + '--outsuffix', metavar='STRING', + help='Output suffix (timestamp is default)') + namespace, argv = parser.parse_known_args(argv) + self.output = namespace.output + self.output_file = namespace.output_file + self.outsuffix = namespace.outsuffix + kwargs['argv'] = argv + + def _initArgParsers(self): + # this code path is only called in python3 (optparse vs argparse) + super(XMLTestProgram, self)._initArgParsers() + + for parser in (self._main_parser, self._discovery_parser): + group = parser.add_mutually_exclusive_group() + group.add_argument( + '-o', '--output', metavar='DIR', nargs=1, + help='Directory for storing XML reports (\'.\' default)') + group.add_argument( + '--output-file', metavar='FILENAME', nargs=1, + help='Filename for storing XML report') + group.add_argument( + '--outsuffix', metavar='STRING', nargs=1, + help='Output suffix (timestamp is default)') + + def runTests(self): + kwargs = dict( + verbosity=self.verbosity, + failfast=self.failfast, + buffer=self.buffer, + warnings=self.warnings, + ) + if sys.version_info[:2] > (3, 4): + kwargs.update(tb_locals=self.tb_locals) + + output_file = None + try: + if self.output_file is not None: + output_file = open(self.output_file, 'wb') + kwargs.update(output=output_file) + elif self.output is not None: + kwargs.update(output=self.output) + + if self.outsuffix is not None: + kwargs.update(outsuffix=self.outsuffix) + + self.testRunner = self.testRunner(**kwargs) + super(XMLTestProgram, self).runTests() + finally: + if output_file is not None: + output_file.close() diff --git a/tests/testing/libs/xmlrunner/unittest.py b/tests/testing/libs/xmlrunner/unittest.py new file mode 100644 index 000000000..a1720486b --- /dev/null +++ b/tests/testing/libs/xmlrunner/unittest.py @@ -0,0 +1,15 @@ + +from __future__ import absolute_import + +import sys +# pylint: disable-msg=W0611 +import unittest +from unittest import TextTestRunner +from unittest import TestResult, TextTestResult +from unittest.result import failfast +from unittest.main import TestProgram + + +__all__ = ( + 'unittest', 'TextTestRunner', 'TestResult', 'TextTestResult', + 'TestProgram', 'failfast') diff --git a/tests/testing/libs/xmlrunner/version.py b/tests/testing/libs/xmlrunner/version.py new file mode 100644 index 000000000..bb42b1443 --- /dev/null +++ b/tests/testing/libs/xmlrunner/version.py @@ -0,0 +1,2 @@ + +__version__ = '3.2.0' diff --git a/tests/testing/run_image_tests.py b/tests/testing/run_image_tests.py index 11ad5f110..677341796 100644 --- a/tests/testing/run_image_tests.py +++ b/tests/testing/run_image_tests.py @@ -1,5 +1,5 @@ ''' -Script for running image tests. +Frontend for running image tests. ''' import os @@ -20,9 +20,8 @@ import threading from xml.etree import ElementTree as ET -from build_falcor import build_falcor - from core import Environment, helpers, config +from core.environment import find_most_recent_build_config from core.termcolor import colored print_mutex = multiprocessing.Lock() @@ -571,22 +570,23 @@ def pull_refs(remote_ref_dir, ref_dir): return success def main(): + default_config = find_most_recent_build_config() default_processes_count = min(config.DEFAULT_PROCESS_COUNT, multiprocessing.cpu_count()) - parser = argparse.ArgumentParser(description="Utility for running image tests.") - parser.add_argument('-c', '--config', type=str, action='store', help=f'Build configuration') - parser.add_argument('--tolerance', type=float, action='store', help='Override tolerance to be at least this value.', default=config.DEFAULT_TOLERANCE) - parser.add_argument('--parallel', type=int, action='store', help='Set the number of Mogwai processes to be used in parallel', default=default_processes_count) - parser.add_argument('-e', '--environment', type=str, action='store', help='Environment', default=config.DEFAULT_ENVIRONMENT) + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument('--environment', type=str, action='store', help=f'Environment (default: {config.DEFAULT_ENVIRONMENT})', default=config.DEFAULT_ENVIRONMENT) + parser.add_argument('--config', type=str, action='store', help=f'Build configuration (default: {default_config})', default=default_config) + parser.add_argument('--list-configs', action='store_true', help='List available build configurations.') + parser.add_argument('-l', '--list', action='store_true', help='List available tests') parser.add_argument('-t', '--tags', type=str, action='store', help='Comma separated list of tags for filtering tests to run', default='default') parser.add_argument('-f', '--filter', type=str, action='store', help='Regular expression for filtering tests to run') parser.add_argument('-x', '--xml-report', type=str, action='store', help='XML report output file') parser.add_argument('-b', '--ref-branch', help='Reference branch to compare against (defaults to master branch)', default='master') parser.add_argument('--compare-only', action='store_true', help='Compare previous results against references without generating new images') + parser.add_argument('--tolerance', type=float, action='store', help='Override tolerance to be at least this value.', default=config.DEFAULT_TOLERANCE) + parser.add_argument('--parallel', type=int, action='store', help='Set the number of Mogwai processes to be used in parallel', default=default_processes_count) parser.add_argument('--gen-refs', action='store_true', help='Generate reference images instead of running tests') - parser.add_argument('--skip-build', action='store_true', help='Skip building project before running') - parser.add_argument('--list-configs', action='store_true', help='List available build configurations.') additional_group = parser.add_argument_group('extended arguments ', 'Additional options used for testing pipelines on TeamCity.') additional_group.add_argument('--pull-refs', action='store_true', help='Pull reference images from remote before running tests') @@ -595,32 +595,30 @@ def main(): args = parser.parse_args() - # The number of processes on Windows is 61, which should be enough for anything, so just hard coding it capped here - args.parallel = min(args.parallel, 61) - - process_controller = ProcessController(args.parallel) + # Try to load environment. + env = None + try: + env = Environment(args.environment, args.config) + except Exception as e: + env_error = e # List build configurations. if args.list_configs: print('Available build configurations:\n' + '\n'.join(config.BUILD_CONFIGS.keys())) sys.exit(0) - # Load environment. - try: - env = Environment(args.environment, args.config) - except Exception as e: - print(e) + # Abort if environment is missing. + if env == None: + print(f"\nFailed to load environment: {env_error}") sys.exit(1) - # Build before running tests. - if not (args.skip_build or args.list): - if not build_falcor(env): - print('Failed to build') - sys.exit(1) - # Collect tests to run. tests = collect_tests(env.image_tests_dir, args.filter, args.tags) + # The number of processes on Windows is 61, which should be enough for anything, so just hard coding it capped here + args.parallel = min(args.parallel, 61) + process_controller = ProcessController(args.parallel) + if args.list: # List available tests. list_tests(tests) diff --git a/tests/testing/run_python_tests.py b/tests/testing/run_python_tests.py new file mode 100644 index 000000000..ec3b159d1 --- /dev/null +++ b/tests/testing/run_python_tests.py @@ -0,0 +1,120 @@ +""" +Frontend for running python unit tests. +""" + +import os +import sys +import argparse +import subprocess +from pathlib import Path + +from core import Environment, config +from core.environment import find_most_recent_build_config + + +def run_python_tests(env: Environment, xml_report, args): + """ + Run python tests. + """ + cmd_args = [str(env.python_exe)] + if xml_report: + cmd_args += ["-m", "xmlrunner", "--output-file", str(xml_report)] + else: + cmd_args += ["-m", "unittest"] + cmd_args += ["discover", str(env.python_tests_dir)] + args + + environ = os.environ.copy() + PYTHONPATH = environ.get("PYTHONPATH", "") + PATH = environ.get("PATH", "") + LD_LIBRARY_PATH = environ.get("LD_LIBRARY_PATH", "") + SEP = ";" if os.name == 'nt' else ":" + + # Extend PYTHONPATH with library directory for loading xmlrunner + PYTHONPATH = str(Path(__file__).parent / "libs") + SEP + PYTHONPATH + # Extend PYTHONPATH & PATH & LD_LIBRARY_PATH (linux) with build directory for loading falcor + PYTHONPATH = str(env.build_dir / "python") + SEP + PYTHONPATH + PATH = str(env.build_dir) + SEP + PATH + LD_LIBRARY_PATH = str(env.build_dir) + SEP + LD_LIBRARY_PATH + + environ["PYTHONPATH"] = PYTHONPATH + environ["PATH"] = PATH + environ["LD_LIBRARY_PATH"] = LD_LIBRARY_PATH + + p = subprocess.Popen(cmd_args, env=environ) + try: + p.communicate(timeout=600) + except subprocess.TimeoutExpired: + p.kill() + print("\n\nProcess killed due to timeout") + + return p.returncode == 0 + + +def main(): + default_config = find_most_recent_build_config() + + parser = argparse.ArgumentParser(description=__doc__, add_help=False) + parser.add_argument( + "-h", "--help", action="store_true", help="Show this help message and exit" + ) + parser.add_argument( + "--environment", + type=str, + action="store", + help=f"Environment (default: {config.DEFAULT_ENVIRONMENT})", + default=config.DEFAULT_ENVIRONMENT, + ) + parser.add_argument( + "--config", + type=str, + action="store", + help=f"Build configuration (default: {default_config})", + default=default_config, + ) + parser.add_argument( + "--list-configs", + action="store_true", + help="List available build configurations", + ) + parser.add_argument( + "--xml-report", type=str, action="store", help="XML report output file" + ) + args, passthrough_args = parser.parse_known_args() + + # Try to load environment. + env = None + try: + env = Environment(args.environment, args.config) + except Exception as e: + env_error = e + + # Print help. + if args.help: + parser.print_help() + if env: + print(f"\nAdditional arguments consumed by Python's unit test runner:\n") + subprocess.call([str(env.python_exe), "-m", "unittest", "-h"]) + else: + print(f"\nFailed to load environment: {env_error}") + sys.exit(0) + + # List build configurations. + if args.list_configs: + print( + "Available build configurations:\n" + "\n".join(config.BUILD_CONFIGS.keys()) + ) + sys.exit(0) + + # Abort if environment is missing. + if env == None: + print(f"\nFailed to load environment: {env_error}") + sys.exit(1) + + # Run tests. + success = run_python_tests(env, args.xml_report, passthrough_args) + + sys.exit(0 if success else 1) + + +if __name__ == "__main__": + main() diff --git a/tests/testing/run_unit_tests.py b/tests/testing/run_unit_tests.py index 10d55dae7..bac1bf91e 100644 --- a/tests/testing/run_unit_tests.py +++ b/tests/testing/run_unit_tests.py @@ -1,6 +1,7 @@ ''' -Script for running unit tests. -Most of the work is delegated to FalcorTest. +Frontend for running unit tests. + +This script helps to run the falcor unit tests for different build configurations. ''' import sys @@ -8,25 +9,13 @@ import subprocess from core import Environment, config +from core.environment import find_most_recent_build_config -from build_falcor import build_falcor - -def run_unit_tests(env, category, device_type, filter_regex, xml_report, repeat_count): +def run_unit_tests(env: Environment, args): ''' Run unit tests by running FalcorTest. - The optional filter_regex is used to select specific tests to run. ''' - args = [str(env.falcor_test_exe)] - if category: - args += ['--category', str(category)] - if device_type: - args += ['--device-type', str(device_type)] - if filter_regex: - args += ['--filter', str(filter_regex)] - if xml_report: - args += ['--xml-report', str(xml_report)] - if repeat_count: - args += ['--repeat', str(repeat_count)] + args = [str(env.falcor_test_exe)] + args p = subprocess.Popen(args) try: @@ -38,38 +27,44 @@ def run_unit_tests(env, category, device_type, filter_regex, xml_report, repeat_ return p.returncode == 0 def main(): - parser = argparse.ArgumentParser(description='Utility for running unit tests.') - parser.add_argument('-c', '--config', type=str, action='store', help=f'Build configuration') - parser.add_argument('-e', '--environment', type=str, action='store', help='Environment', default=config.DEFAULT_ENVIRONMENT) - parser.add_argument('--category', type=str, action='store', help='Test categories to run (default: all).') - parser.add_argument('-d', '--device-type', type=str, action='store', help='Graphics device type.') - parser.add_argument('-f', '--filter', type=str, action='store', help='Regular expression for filtering tests to run') - parser.add_argument('-x', '--xml-report', type=str, action='store', help='XML report output file') - parser.add_argument('-r', '--repeat', type=int, action='store', help='Number of times to repeat the test.') - parser.add_argument('--skip-build', action='store_true', help='Skip building project before running tests') - parser.add_argument('--list-configs', action='store_true', help='List available build configurations.') - args = parser.parse_args() + default_config = find_most_recent_build_config() + + parser = argparse.ArgumentParser(description=__doc__, add_help=False) + parser.add_argument('-h', '--help', action='store_true', help='Show this help message and exit') + parser.add_argument('--environment', type=str, action='store', help=f'Environment (default: {config.DEFAULT_ENVIRONMENT})', default=config.DEFAULT_ENVIRONMENT) + parser.add_argument('--config', type=str, action='store', help=f'Build configuration (default: {default_config})', default=default_config) + parser.add_argument('--list-configs', action='store_true', help='List available build configurations') + args, passthrough_args = parser.parse_known_args() + + # Try to load environment. + env = None + try: + env = Environment(args.environment, args.config) + except Exception as e: + env_error = e + + # Print help. + if args.help: + parser.print_help() + if env: + print(f"\nAdditional arguments consumed by {config.FALCOR_TEST_EXE}:\n") + subprocess.call([str(env.falcor_test_exe), '-h']) + else: + print(f"\nFailed to load environment: {env_error}") + sys.exit(0) # List build configurations. if args.list_configs: print('Available build configurations:\n' + '\n'.join(config.BUILD_CONFIGS.keys())) sys.exit(0) - # Load environment. - try: - env = Environment(args.environment, args.config) - except Exception as e: - print(e) + # Abort if environment is missing. + if env == None: + print(f"\nFailed to load environment: {env_error}") sys.exit(1) - # Build before running tests. - if not args.skip_build: - if not build_falcor(env): - print('Failed to build') - sys.exit(1) - # Run tests. - success = run_unit_tests(env, args.category, args.device_type, args.filter, args.xml_report, args.repeat) + success = run_unit_tests(env, passthrough_args) sys.exit(0 if success else 1) diff --git a/tools/format_code.sh b/tools/format_code.sh index 86d140ca8..06c716381 100644 --- a/tools/format_code.sh +++ b/tools/format_code.sh @@ -1,10 +1,11 @@ #!/bin/sh -export pwd=`pwd` -export project_dir=$pwd/.. -export python_dir=$project_dir/tools/.packman/python -export python=$python_dir/bin/python3 -export clang_format=$project_dir/tools/.packman/clang-format/clang-format.exe +script=$(readlink -f "$0") +pwd=$(dirname "$script") +project_dir=$pwd/.. +python_dir=$project_dir/tools/.packman/python +python=$python_dir/bin/python3 +clang_format=$project_dir/tools/.packman/clang-format/clang-format if [ ! -f "$python" ] || [ ! -f "$clang_format" ]; then $project_dir/setup.sh diff --git a/tools/pymacro.bat b/tools/pymacro.bat new file mode 100644 index 000000000..37fb010c0 --- /dev/null +++ b/tools/pymacro.bat @@ -0,0 +1,9 @@ +@echo off + +set pwd=%~dp0 +set project_dir=%pwd%..\ +set python=%project_dir%tools\.packman\python\python.exe + +if not exist %python% call %project_dir%setup.bat + +call %python% %pwd%/pymacro.py %* diff --git a/tools/pymacro.py b/tools/pymacro.py new file mode 100644 index 000000000..2b5064ff5 --- /dev/null +++ b/tools/pymacro.py @@ -0,0 +1,117 @@ +#!/usr/bin/env python + +""" +Python macro tool. + +Expands macros in C++ code using the python interpreter. +Example: + +/* <<>> */ +const int table[] = { + 0, 1, 2, 3, 4, 5, 6, 7 +}; +/* <<>> */ + +""" + +import sys +import re +import argparse +from pathlib import Path +from enum import Enum +from io import StringIO + + +class Capturing(list): + def __enter__(self): + self._stdout = sys.stdout + sys.stdout = self._stringio = StringIO() + return self + + def __exit__(self, *args): + self.extend(self._stringio.getvalue().splitlines()) + del self._stringio # free up some memory + sys.stdout = self._stdout + + +HEADER_START_RE = re.compile(r"^\s*\/\*\s*<<>>\s*\*\/\s*$") +FOOTER_RE = re.compile(r"^\s*\/\*\s+<<>>\s*\*\/\s*$") + + +class State(Enum): + IDLE = 1 + HEADER = 2 + CONTENT = 3 + + +def process_file(path: Path, dry_run: bool): + + state = State.IDLE + script_lines = [] + + lines_in = open(path).readlines() + lines_out = [] + + for line in lines_in: + if state == State.IDLE: + lines_out.append(line) + m = HEADER_START_RE.match(line) + if m: + script_lines = [] + state = State.HEADER + elif state == State.HEADER: + lines_out.append(line) + m = HEADER_END_RE.match(line) + if m: + state = State.CONTENT + script = "".join(script_lines) + c = compile(script, "", "exec") + with Capturing() as output: + eval(c) + lines_out += [l + "\n" for l in output] + else: + script_lines.append(line) + elif state == State.CONTENT: + m = FOOTER_RE.match(line) + if m: + lines_out.append(line) + state = State.IDLE + + if lines_out != lines_in: + if dry_run: + print("".join(lines_out)) + else: + open(path, "w").writelines(lines_out) + + +def run(args): + for file in args.files: + process_file(Path(file), dry_run=args.dry_run) + return 0 + + +def main(): + parser = argparse.ArgumentParser( + description=__doc__, formatter_class=argparse.RawTextHelpFormatter + ) + parser.add_argument( + "-d", + "--dry-run", + action="store_true", + default=False, + help="Run without writing files", + ) + parser.add_argument("files", metavar="file", nargs="+") + + args = parser.parse_args() + + return run(args) + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/tools/remove_hungarian_notation.py b/tools/remove_hungarian_notation.py new file mode 100644 index 000000000..34f5b7c14 --- /dev/null +++ b/tools/remove_hungarian_notation.py @@ -0,0 +1,45 @@ +import os +import re +import argparse + + +def remove_hungarian_notation(filename): + with open(filename, "r") as f: + contents = f.read() + + pattern = r"([^a-zA-Z0-9_\"'])([msg]?[p][A-Z][a-zA-Z0-9_]*)" + + def remove_hungarian(match): + var = match.group(2) + if var[0] == "p": + return match.group(1) + var[1].lower() + var[2:] + if var[0] in "msg": + return match.group(1) + var[0] + var[2:] + return match.group(0) + + new_contents = re.sub(pattern, remove_hungarian, contents) + + with open(filename, "w") as f: + f.write(new_contents) + + +def process_directory(path): + # recursively process all files in the directory and its subdirectories + for root, dirs, files in os.walk(path): + for file in files: + # only process C/C++ source files + if file.endswith(".cpp") or file.endswith(".h"): + filename = os.path.join(root, file) + remove_hungarian_notation(filename) + + +if __name__ == "__main__": + parser = argparse.ArgumentParser( + description="Remove Hungarian notation from C/C++ identifiers." + ) + parser.add_argument( + "path", metavar="path", type=str, help="the path to the directory to process" + ) + + args = parser.parse_args() + process_directory(path=args.path)