From 3f30b44b7a9d09210c172f362f81f6848730d2bd Mon Sep 17 00:00:00 2001 From: skallweitNV <64953474+skallweitNV@users.noreply.github.com> Date: Mon, 3 Jul 2023 10:56:06 +0200 Subject: [PATCH] Falcor 6.0 (#366) --- CMakeLists.txt | 6 +- Source/Falcor/CMakeLists.txt | 50 +- Source/Falcor/Core/API/Aftermath.cpp | 467 +++++++++++++++ Source/Falcor/Core/API/Aftermath.h | 99 +++ Source/Falcor/Core/API/BlendState.h | 1 + Source/Falcor/Core/API/BlitContext.cpp | 2 +- Source/Falcor/Core/API/Buffer.h | 1 + Source/Falcor/Core/API/Common.h | 21 +- Source/Falcor/Core/API/ComputeStateObject.h | 1 + Source/Falcor/Core/API/CopyContext.cpp | 10 + Source/Falcor/Core/API/CopyContext.h | 5 + Source/Falcor/Core/API/DepthStencilState.h | 1 + Source/Falcor/Core/API/Device.cpp | 42 +- Source/Falcor/Core/API/Device.h | 24 +- Source/Falcor/Core/API/FBO.h | 1 + Source/Falcor/Core/API/FencedPool.h | 1 + Source/Falcor/Core/API/Formats.cpp | 8 +- Source/Falcor/Core/API/Formats.h | 24 + Source/Falcor/Core/API/GFXAPI.cpp | 25 +- Source/Falcor/Core/API/GFXHelpers.cpp | 6 + Source/Falcor/Core/API/GpuFence.h | 1 + Source/Falcor/Core/API/GpuMemoryHeap.h | 1 + Source/Falcor/Core/API/GpuTimer.cpp | 2 + Source/Falcor/Core/API/GpuTimer.h | 1 + Source/Falcor/Core/API/GraphicsStateObject.h | 1 + .../Falcor/Core/API/LowLevelContextData.cpp | 2 +- Source/Falcor/Core/API/ParameterBlock.h | 1 + Source/Falcor/Core/API/QueryHeap.h | 1 + Source/Falcor/Core/API/RasterizerState.cpp | 5 - Source/Falcor/Core/API/RasterizerState.h | 15 + Source/Falcor/Core/API/Resource.h | 1 + Source/Falcor/Core/API/ResourceViews.h | 1 + .../Falcor/Core/API/RtAccelerationStructure.h | 1 + ...RtAccelerationStructurePostBuildInfoPool.h | 1 + Source/Falcor/Core/API/RtStateObject.cpp | 12 +- Source/Falcor/Core/API/RtStateObject.h | 1 + Source/Falcor/Core/API/Sampler.cpp | 11 - Source/Falcor/Core/API/Sampler.h | 36 ++ Source/Falcor/Core/API/Shader.cpp | 92 --- Source/Falcor/Core/API/Shader.h | 318 ---------- Source/Falcor/Core/API/ShaderType.h | 96 +++ .../Core/API/Shared/D3D12ConstantBufferView.h | 1 + .../Core/API/Shared/D3D12DescriptorHeap.h | 1 + .../Core/API/Shared/D3D12DescriptorPool.h | 1 + .../Core/API/Shared/D3D12DescriptorSet.h | 3 +- .../API/Shared/D3D12DescriptorSetLayout.h | 2 +- .../Core/API/Shared/D3D12RootSignature.h | 1 + Source/Falcor/Core/API/Swapchain.h | 1 + Source/Falcor/Core/API/Texture.cpp | 2 +- Source/Falcor/Core/API/Texture.h | 1 + Source/Falcor/Core/API/VAO.h | 1 + Source/Falcor/Core/API/VertexLayout.h | 2 + Source/Falcor/Core/Enum.h | 188 ++++++ Source/Falcor/Core/ErrorHandling.cpp | 6 + Source/Falcor/Core/Object.cpp | 71 ++- Source/Falcor/Core/Object.h | 67 ++- Source/Falcor/Core/Pass/BaseGraphicsPass.cpp | 3 +- Source/Falcor/Core/Pass/BaseGraphicsPass.h | 3 +- Source/Falcor/Core/Pass/ComputePass.cpp | 7 +- Source/Falcor/Core/Pass/ComputePass.h | 7 +- Source/Falcor/Core/Pass/FullScreenPass.cpp | 13 +- Source/Falcor/Core/Pass/FullScreenPass.h | 6 +- Source/Falcor/Core/Pass/RasterPass.cpp | 6 +- Source/Falcor/Core/Pass/RasterPass.h | 10 +- Source/Falcor/Core/Program/ComputeProgram.cpp | 2 +- Source/Falcor/Core/Program/ComputeProgram.h | 2 +- Source/Falcor/Core/Program/DefineList.h | 86 +++ .../Falcor/Core/Program/GraphicsProgram.cpp | 4 +- Source/Falcor/Core/Program/GraphicsProgram.h | 4 +- Source/Falcor/Core/Program/Program.cpp | 4 +- Source/Falcor/Core/Program/Program.h | 129 +++- Source/Falcor/Core/Program/ProgramManager.cpp | 62 +- Source/Falcor/Core/Program/ProgramManager.h | 12 +- .../Falcor/Core/Program/ProgramReflection.cpp | 3 +- .../Falcor/Core/Program/ProgramReflection.h | 4 + Source/Falcor/Core/Program/ProgramVersion.cpp | 20 +- Source/Falcor/Core/Program/ProgramVersion.h | 102 +++- Source/Falcor/Core/Program/RtBindingTable.h | 1 + Source/Falcor/Core/Program/RtProgram.h | 22 +- Source/Falcor/Core/Program/ShaderVar.cpp | 13 +- Source/Falcor/Core/Program/ShaderVar.h | 9 + Source/Falcor/Core/SampleApp.cpp | 27 +- Source/Falcor/Core/SampleApp.h | 6 +- Source/Falcor/Core/State/ComputeState.h | 1 + Source/Falcor/Core/State/GraphicsState.h | 1 + Source/Falcor/Core/Testbed.cpp | 52 +- Source/Falcor/Core/Testbed.h | 19 +- Source/Falcor/Core/Window.h | 1 + Source/Falcor/RenderGraph/RenderGraph.cpp | 185 ++---- Source/Falcor/RenderGraph/RenderGraph.h | 20 +- Source/Falcor/RenderGraph/RenderGraphIR.cpp | 16 +- Source/Falcor/RenderGraph/RenderGraphIR.h | 6 +- .../RenderGraph/RenderGraphImportExport.cpp | 2 +- Source/Falcor/RenderGraph/RenderGraphUI.cpp | 2 +- Source/Falcor/RenderGraph/RenderPass.cpp | 4 +- Source/Falcor/RenderGraph/RenderPass.h | 21 +- .../Falcor/RenderGraph/RenderPassHelpers.cpp | 10 - Source/Falcor/RenderGraph/RenderPassHelpers.h | 26 +- Source/Falcor/RenderPasses/ResolvePass.h | 3 +- .../Rendering/Lights/EmissiveLightSampler.cpp | 11 +- .../Rendering/Lights/EmissiveLightSampler.h | 4 +- .../Lights/EmissiveLightSampler.slang | 2 + .../Lights/EmissiveLightSamplerType.slangh | 8 + .../Lights/EmissiveUniformSampler.cpp | 11 - .../Rendering/Lights/EmissiveUniformSampler.h | 6 + .../Rendering/Lights/LightBVHBuilder.cpp | 34 +- .../Falcor/Rendering/Lights/LightBVHBuilder.h | 25 + .../Rendering/Lights/LightBVHSampler.cpp | 34 +- .../Falcor/Rendering/Lights/LightBVHSampler.h | 14 +- .../Rendering/Lights/LightBVHSampler.slang | 10 + .../LightBVHSamplerSharedDefinitions.slang | 7 + .../Rendering/Materials/BSDFs/BeerBTDF.slang | 3 +- .../Materials/BSDFs/DielectricPlateBSDF.slang | 5 + .../Materials/BSDFs/DiffuseSpecularBRDF.slang | 5 + .../Rendering/Materials/BSDFs/SheenBSDF.slang | 3 +- .../Materials/BSDFs/SimpleBTDF.slang | 3 +- .../Rendering/Materials/ClothMaterial.slang | 2 +- .../Rendering/Materials/HairMaterial.slang | 5 +- .../Rendering/Materials/LayeredBSDF.slang | 5 + .../PBRT/PBRTCoatedConductorMaterial.slang | 2 +- .../PBRT/PBRTCoatedDiffuseMaterial.slang | 2 +- .../PBRT/PBRTConductorMaterialInstance.slang | 5 + .../PBRT/PBRTDielectricMaterial.slang | 3 +- .../PBRT/PBRTDielectricMaterialInstance.slang | 5 + .../Materials/StandardMaterial.slang | 5 +- .../Rendering/Materials/TexLODTypes.slang | 15 + Source/Falcor/Rendering/RTXDI/RTXDI.cpp | 83 +-- Source/Falcor/Rendering/RTXDI/RTXDI.h | 58 +- .../RTXDI/RTXDIApplicationBridge.slangh | 2 + .../Rendering/Volumes/GridVolumeSampler.cpp | 44 +- .../Rendering/Volumes/GridVolumeSampler.h | 12 +- .../Volumes/GridVolumeSamplerParams.slang | 13 + .../Volumes/HomogeneousVolumeSampler.slang | 2 + .../Rendering/Volumes/PhaseFunction.slang | 10 +- Source/Falcor/Scene/Animation/Animatable.h | 1 + .../Scene/Animation/AnimatedVertexCache.cpp | 6 +- Source/Falcor/Scene/Animation/Animation.h | 1 + Source/Falcor/Scene/Camera/Camera.h | 1 + .../Displacement/DisplacementMapping.slang | 3 + Source/Falcor/Scene/HitInfo.cpp | 4 +- Source/Falcor/Scene/HitInfo.h | 4 +- Source/Falcor/Scene/Importer.cpp | 7 +- Source/Falcor/Scene/Importer.h | 12 +- Source/Falcor/Scene/Intersection.slang | 6 + Source/Falcor/Scene/Lights/EnvMap.h | 2 + Source/Falcor/Scene/Lights/Light.h | 1 + .../Falcor/Scene/Lights/LightCollection.cpp | 2 +- Source/Falcor/Scene/Lights/LightCollection.h | 1 + Source/Falcor/Scene/Lights/LightProfile.h | 1 + .../Falcor/Scene/Material/BasicMaterial.cpp | 11 +- Source/Falcor/Scene/Material/BasicMaterial.h | 9 +- .../Scene/Material/BasicMaterialData.slang | 4 +- Source/Falcor/Scene/Material/ClothMaterial.h | 1 + Source/Falcor/Scene/Material/HairMaterial.h | 1 + Source/Falcor/Scene/Material/MERLMaterial.h | 3 +- .../Scene/Material/MERLMaterialData.slang | 2 +- .../Falcor/Scene/Material/MERLMixMaterial.h | 3 +- .../Scene/Material/MERLMixMaterialData.slang | 2 +- Source/Falcor/Scene/Material/Material.cpp | 18 +- Source/Falcor/Scene/Material/Material.h | 19 +- .../Falcor/Scene/Material/MaterialData.slang | 26 +- .../Falcor/Scene/Material/MaterialSystem.cpp | 47 +- Source/Falcor/Scene/Material/MaterialSystem.h | 17 +- .../Scene/Material/MaterialSystem.slang | 19 +- .../PBRT/PBRTCoatedConductorMaterial.h | 1 + .../Material/PBRT/PBRTCoatedDiffuseMaterial.h | 1 + .../Material/PBRT/PBRTConductorMaterial.h | 1 + .../Material/PBRT/PBRTDielectricMaterial.h | 1 + .../Scene/Material/PBRT/PBRTDiffuseMaterial.h | 1 + .../PBRT/PBRTDiffuseTransmissionMaterial.h | 1 + Source/Falcor/Scene/Material/RGLMaterial.h | 3 +- .../Scene/Material/RGLMaterialData.slang | 2 +- .../Scene/Material/StandardMaterial.cpp | 2 +- .../Falcor/Scene/Material/StandardMaterial.h | 1 + .../Scene/Material/TextureSampler.slang | 59 ++ .../NormalizedDenseSDFGrid/NDSDFGrid.slang | 3 + Source/Falcor/Scene/SDFs/SDFGrid.h | 3 + .../Scene/SDFs/SparseBrickSet/SDFSBS.cpp | 18 +- .../Scene/SDFs/SparseBrickSet/SDFSBS.slang | 3 + .../Scene/SDFs/SparseVoxelOctree/SDFSVO.cpp | 6 +- .../Scene/SDFs/SparseVoxelOctree/SDFSVO.slang | 3 + .../SparseVoxelOctree/SDFSVOHashTable.slang | 4 + Source/Falcor/Scene/Scene.cpp | 47 +- Source/Falcor/Scene/Scene.h | 10 +- Source/Falcor/Scene/SceneBuilder.cpp | 31 +- Source/Falcor/Scene/SceneBuilder.h | 14 + Source/Falcor/Scene/TriangleMesh.h | 1 + Source/Falcor/Scene/Volume/Grid.cpp | 1 + Source/Falcor/Scene/Volume/Grid.h | 1 + Source/Falcor/Scene/Volume/GridConverter.h | 5 +- Source/Falcor/Scene/Volume/GridVolume.cpp | 17 +- Source/Falcor/Scene/Volume/GridVolume.h | 21 + Source/Falcor/Testing/UnitTest.cpp | 562 ++++++++++++------ Source/Falcor/Testing/UnitTest.h | 331 +++++++---- Source/Falcor/Utils/Algorithm/BitonicSort.cpp | 2 +- .../Utils/Algorithm/ParallelReduction.cpp | 8 +- Source/Falcor/Utils/Algorithm/PrefixSum.cpp | 2 +- .../Utils/Geometry/IntersectionHelpers.slang | 5 + Source/Falcor/Utils/HostDeviceShared.slangh | 4 + Source/Falcor/Utils/Image/Bitmap.cpp | 41 +- Source/Falcor/Utils/Logger.cpp | 121 +++- Source/Falcor/Utils/Logger.h | 37 +- Source/Falcor/Utils/Math/MatrixMath.h | 2 +- Source/Falcor/Utils/Math/QuaternionMath.h | 6 +- Source/Falcor/Utils/Math/ScalarTypes.h | 4 +- Source/Falcor/Utils/Math/VectorMath.h | 4 +- Source/Falcor/Utils/Properties.cpp | 494 +++++++++++++++ Source/Falcor/Utils/Properties.h | 473 +++++++++++++++ .../SampleGenerators/CPUSampleGenerator.h | 1 + .../Falcor/Utils/Sampling/SampleGenerator.cpp | 4 +- .../Falcor/Utils/Sampling/SampleGenerator.h | 5 +- .../Sampling/SampleGeneratorInterface.slang | 11 + .../{Dictionary.h => PythonDictionary.h} | 17 +- .../Falcor/Utils/Scripting/ScriptBindings.cpp | 4 + .../Falcor/Utils/Scripting/ScriptBindings.h | 150 +---- Source/Falcor/Utils/Scripting/ScriptWriter.h | 12 +- Source/Falcor/Utils/Scripting/Scripting.h | 7 + Source/Falcor/Utils/Settings.cpp | 15 + Source/Falcor/Utils/Settings.h | 4 +- Source/Falcor/Utils/StringFormatters.h | 10 +- Source/Falcor/Utils/UI/Gui.h | 33 + .../AccumulatePass/AccumulatePass.cpp | 70 +-- .../AccumulatePass/AccumulatePass.h | 21 +- Source/RenderPasses/BSDFViewer/BSDFViewer.cpp | 45 +- .../BSDFViewer/BSDFViewer.cs.slang | 3 + Source/RenderPasses/BSDFViewer/BSDFViewer.h | 10 +- .../BSDFViewer/BSDFViewerParams.slang | 6 + Source/RenderPasses/BlitPass/BlitPass.cpp | 34 +- Source/RenderPasses/BlitPass/BlitPass.h | 8 +- Source/RenderPasses/DLSSPass/DLSSPass.cpp | 52 +- Source/RenderPasses/DLSSPass/DLSSPass.h | 26 +- .../ColorMapPass/ColorMapParams.slang | 10 + .../DebugPasses/ColorMapPass/ColorMapPass.cpp | 47 +- .../DebugPasses/ColorMapPass/ColorMapPass.h | 8 +- .../DebugPasses/ComparisonPass.cpp | 16 +- .../RenderPasses/DebugPasses/ComparisonPass.h | 4 +- .../RenderPasses/DebugPasses/DebugPasses.cpp | 2 - .../InvalidPixelDetectionPass.cpp | 2 +- .../InvalidPixelDetectionPass.h | 4 +- .../SideBySidePass/SideBySidePass.cpp | 6 +- .../SideBySidePass/SideBySidePass.h | 4 +- .../SplitScreenPass/SplitScreenPass.cpp | 6 +- .../SplitScreenPass/SplitScreenPass.h | 4 +- .../ErrorMeasurePass/ErrorMeasurePass.cpp | 39 +- .../ErrorMeasurePass/ErrorMeasurePass.h | 16 +- Source/RenderPasses/FLIPPass/FLIPPass.cpp | 70 +-- Source/RenderPasses/FLIPPass/FLIPPass.h | 10 +- .../RenderPasses/FLIPPass/ToneMappers.slang | 7 + .../RenderPasses/GBuffer/GBuffer/GBuffer.cpp | 5 +- .../GBuffer/GBuffer/GBufferRT.cpp | 39 +- .../RenderPasses/GBuffer/GBuffer/GBufferRT.h | 10 +- .../GBuffer/GBuffer/GBufferRaster.cpp | 4 +- .../GBuffer/GBuffer/GBufferRaster.h | 4 +- Source/RenderPasses/GBuffer/GBufferBase.cpp | 64 +- Source/RenderPasses/GBuffer/GBufferBase.h | 13 +- .../GBuffer/VBuffer/VBufferRT.cpp | 28 +- .../RenderPasses/GBuffer/VBuffer/VBufferRT.h | 10 +- .../GBuffer/VBuffer/VBufferRaster.cpp | 4 +- .../GBuffer/VBuffer/VBufferRaster.h | 4 +- .../RenderPasses/ImageLoader/ImageLoader.cpp | 28 +- Source/RenderPasses/ImageLoader/ImageLoader.h | 6 +- .../MinimalPathTracer/MinimalPathTracer.cpp | 22 +- .../MinimalPathTracer/MinimalPathTracer.h | 8 +- .../ModulateIllumination.cpp | 42 +- .../ModulateIllumination.h | 6 +- Source/RenderPasses/NRDPass/NRDPass.cpp | 147 +++-- Source/RenderPasses/NRDPass/NRDPass.h | 19 +- .../OptixDenoiser/OptixDenoiser.cpp | 34 +- .../OptixDenoiser/OptixDenoiser.h | 7 +- Source/RenderPasses/OptixDenoiser/README.txt | 3 +- Source/RenderPasses/PathTracer/Params.slang | 13 + Source/RenderPasses/PathTracer/PathTracer.cpp | 138 ++--- Source/RenderPasses/PathTracer/PathTracer.h | 16 +- .../RenderPasses/PathTracer/PathTracer.slang | 3 + .../PixelInspectorPass/PixelInspectorPass.cpp | 4 +- .../PixelInspectorPass/PixelInspectorPass.h | 4 +- Source/RenderPasses/RTXDIPass/RTXDIPass.cpp | 18 +- Source/RenderPasses/RTXDIPass/RTXDIPass.h | 8 +- .../RenderPassTemplate/RenderPassTemplate.cpp | 6 +- .../RenderPassTemplate/RenderPassTemplate.h | 6 +- .../RenderPasses/SDFEditor/GUIPass.ps.slang | 4 + Source/RenderPasses/SDFEditor/SDFEditor.cpp | 6 +- Source/RenderPasses/SDFEditor/SDFEditor.h | 6 +- Source/RenderPasses/SVGFPass/SVGFPass.cpp | 28 +- Source/RenderPasses/SVGFPass/SVGFPass.h | 7 +- .../SceneDebugger/SceneDebugger.cpp | 72 +-- .../SceneDebugger/SceneDebugger.h | 6 +- .../SceneDebugger/SharedTypes.slang | 24 + .../SimplePostFX/SimplePostFX.cpp | 50 +- .../RenderPasses/SimplePostFX/SimplePostFX.h | 6 +- Source/RenderPasses/TAA/TAA.cpp | 18 +- Source/RenderPasses/TAA/TAA.h | 6 +- .../TestPasses/TestPyTorchPass.cpp | 7 +- .../RenderPasses/TestPasses/TestPyTorchPass.h | 6 +- .../RenderPasses/TestPasses/TestRtProgram.cpp | 16 +- .../RenderPasses/TestPasses/TestRtProgram.h | 6 +- Source/RenderPasses/ToneMapper/ToneMapper.cpp | 100 ++-- Source/RenderPasses/ToneMapper/ToneMapper.h | 16 +- .../ToneMapper/ToneMapperParams.slang | 10 + .../Utils/Composite/Composite.cpp | 35 +- .../RenderPasses/Utils/Composite/Composite.h | 18 +- .../Utils/CrossFade/CrossFade.cpp | 22 +- .../RenderPasses/Utils/CrossFade/CrossFade.h | 6 +- .../Utils/GaussianBlur/GaussianBlur.cpp | 18 +- .../Utils/GaussianBlur/GaussianBlur.h | 6 +- Source/RenderPasses/Utils/Utils.cpp | 1 - .../WhittedRayTracer/WhittedRayTracer.cpp | 75 +-- .../WhittedRayTracer/WhittedRayTracer.h | 6 +- .../WhittedRayTracerTypes.slang | 7 + Source/Samples/CMakeLists.txt | 1 + Source/Samples/MultiSampling/CMakeLists.txt | 11 + .../MultiSampling/MultiSampling.3d.slang} | 29 +- .../Samples/MultiSampling/MultiSampling.cpp | 99 +++ .../MultiSampling/MultiSampling.h} | 21 +- Source/Tools/FalcorTest/CMakeLists.txt | 6 +- Source/Tools/FalcorTest/FalcorTest.cpp | 115 ++-- .../FalcorTest/Tests/Core/AftermathTests.cpp} | 39 +- .../Tests/Core/AftermathTests.cs.slang | 45 ++ .../Tools/FalcorTest/Tests/Core/BlitTests.cpp | 7 +- .../Tests/Core/BufferAccessTests.cpp | 10 +- .../FalcorTest/Tests/Core/BufferTests.cpp | 9 +- .../Tests/Core/ConstantBufferTests.cpp | 10 +- .../Tools/FalcorTest/Tests/Core/CoreTests.cpp | 56 ++ .../FalcorTest/Tests/Core/DDSReadTests.cpp | 19 +- .../Tools/FalcorTest/Tests/Core/EnumTests.cpp | 149 +++++ .../FalcorTest/Tests/Core/LargeBuffer.cpp | 29 +- .../FalcorTest/Tests/Core/ObjectTests.cpp | 3 + .../FalcorTest/Tests/Core/ParamBlockCB.cpp | 6 +- .../Tests/Core/ResourceAliasing.cpp | 12 +- .../Tests/Core/RootBufferParamBlockTests.cpp | 7 +- .../Tests/Core/RootBufferStructTests.cpp | 7 +- .../FalcorTest/Tests/Core/RootBufferTests.cpp | 7 +- .../FalcorTest/Tests/Core/TextureTests.cpp | 11 +- .../InvalidPixelDetectionTests.cpp | 7 +- .../Tests/Platform/LockFileTests.cpp | 10 +- .../Rendering/Materials/MicrofacetTests.cpp | 34 +- .../Materials/MicrofacetTests.cs.slang | 2 +- .../Tests/Sampling/AliasTableTests.cpp | 6 +- .../Tests/Sampling/LowDiscrepancyTests.cpp | 3 +- .../Tests/Sampling/PseudorandomTests.cpp | 108 ++-- .../Tests/Sampling/SampleGeneratorTests.cpp | 19 +- .../FalcorTest/Tests/Scene/EnvMapTests.cpp | 2 +- .../Tests/Scene/Material/BSDFTests.cpp | 2 +- .../Scene/Material/HairChiang16Tests.cpp | 25 +- .../Tests/Scene/Material/MERLFileTests.cpp | 2 +- .../FalcorTest/Tests/Slang/CastFloat16.cpp | 5 +- .../FalcorTest/Tests/Slang/Float16Tests.cpp | 7 +- .../FalcorTest/Tests/Slang/Float64Tests.cpp | 7 +- .../Tests/Slang/InheritanceTests.cpp | 22 +- .../FalcorTest/Tests/Slang/Int64Tests.cpp | 7 +- .../FalcorTest/Tests/Slang/NestedStructs.cpp | 4 +- .../FalcorTest/Tests/Slang/ShaderModel.cpp | 7 +- .../FalcorTest/Tests/Slang/ShaderString.cpp | 28 +- .../Tests/Slang/SlangInheritance.cpp | 9 +- .../Tests/Slang/SlangMutatingTests.cpp | 5 +- .../Tests/Slang/SlangReinterpretCast.cpp | 8 +- .../FalcorTest/Tests/Slang/SlangTests.cpp | 20 +- .../Tests/Slang/StructuredBufferMatrix.cpp | 21 +- .../FalcorTest/Tests/Slang/TemplatedLoad.cpp | 5 +- .../FalcorTest/Tests/Slang/TraceRayFlags.cpp | 7 +- .../FalcorTest/Tests/Slang/TraceRayInline.cpp | 4 +- .../Tests/Slang/UnboundedDescriptorArray.cpp | 5 +- .../Tools/FalcorTest/Tests/Slang/WaveOps.cpp | 30 +- .../FalcorTest/Tests/Utils/AABBTests.cpp | 3 +- .../FalcorTest/Tests/Utils/BitTricksTests.cpp | 3 +- .../Tests/Utils/BitonicSortTests.cpp | 4 +- .../Tests/Utils/Color/SpectrumUtilsTests.cpp | 3 +- .../Tests/Utils/Debug/WarpProfilerTests.cpp | 2 +- .../Tests/Utils/GeometryHelpersTests.cpp | 35 +- .../FalcorTest/Tests/Utils/HalfUtilsTests.cpp | 19 +- .../Tests/Utils/IntersectionHelpersTests.cpp | 8 +- .../Tests/Utils/MathHelpersTests.cpp | 12 +- .../Tests/Utils/PackedFormatsTests.cpp | 2 +- .../Tests/Utils/PropertiesTests.cpp | 480 +++++++++++++++ .../AssimpImporter/AssimpImporter.cpp | 53 +- .../importers/AssimpImporter/AssimpImporter.h | 8 + .../plugins/importers/PBRTImporter/Parser.cpp | 1 + .../PythonImporter/PythonImporter.cpp | 61 +- .../importers/PythonImporter/PythonImporter.h | 10 + .../importers/USDImporter/ImporterContext.cpp | 1 + .../importers/USDImporter/ImporterContext.h | 2 + .../importers/USDImporter/USDImporter.cpp | 2 + Source/plugins/importers/USDImporter/Utils.h | 11 + build_scripts/deploycommon.bat | 9 + build_scripts/deploycommon.sh | 14 + dependencies.xml | 6 +- external/CMakeLists.txt | 22 + external/fmt | 2 +- external/include/BS_thread_pool_light.hpp | 327 ++++++++++ scripts/BSDFViewer.py | 4 +- scripts/MinimalPathTracer.py | 4 +- scripts/PathTracer.py | 4 +- scripts/PathTracerNRD.py | 14 +- scripts/RTXDI.py | 2 +- scripts/sdf-editor/SDFEditor.py | 6 +- .../renderpasses/graphs/ColorMapPass.py | 2 +- .../renderpasses/graphs/CompositePass.py | 4 +- .../renderpasses/graphs/CrossFadePass.py | 4 +- tests/image_tests/renderpasses/graphs/DLSS.py | 4 +- .../renderpasses/graphs/FLIPPass.py | 4 +- .../renderpasses/graphs/GaussianBlur.py | 2 +- .../renderpasses/graphs/HalfRes.py | 8 +- .../image_tests/renderpasses/graphs/MVecRT.py | 2 +- .../renderpasses/graphs/MVecRaster.py | 2 +- .../renderpasses/graphs/MinimalPathTracer.py | 2 +- .../graphs/ModulateIllumination.py | 6 +- .../renderpasses/graphs/PathTracer.py | 4 +- .../renderpasses/graphs/PathTracerAdaptive.py | 8 +- .../graphs/PathTracerDielectrics.py | 4 +- .../graphs/PathTracerMaterials.py | 4 +- .../graphs/SDFEditorRenderGraphV2.py | 2 +- tests/image_tests/renderpasses/graphs/SVGF.py | 2 +- .../renderpasses/graphs/SideBySide.py | 4 +- .../renderpasses/graphs/SimplePostFX.py | 4 +- .../renderpasses/graphs/SplitScreen.py | 4 +- tests/image_tests/renderpasses/graphs/TAA.py | 2 +- .../renderpasses/graphs/ToneMapping.py | 4 +- .../renderpasses/graphs/WhittedRayTracer.py | 6 +- .../renderpasses/test_ColorMapPass.py | 7 +- .../renderpasses/test_CompositePass.py | 7 +- .../image_tests/renderpasses/test_FLIPPass.py | 5 +- .../renderpasses/test_GBufferRTTexGrads.py | 10 +- .../renderpasses/test_GBufferRasterAlpha.py | 10 +- .../test_PathTracerDielectrics.py | 2 +- .../renderpasses/test_PathTracerMaterials.py | 8 +- .../renderpasses/test_PathTracerReload.py | 4 +- .../renderpasses/test_TextureLOD.py | 14 +- .../renderpasses/test_ToneMapping.py | 5 +- .../renderpasses/test_VBufferRasterAlpha.py | 10 +- .../renderscripts/test_BSDFViewer.py | 2 +- .../renderscripts/test_MinimalPathTracer.py | 2 +- .../scene/graphs/GBufferRTCullBack.py | 2 +- .../scene/graphs/GBufferRasterCullBack.py | 2 +- tests/image_tests/scene/graphs/PathTracer.py | 4 +- tests/image_tests/scene/scenes/smoke.pyscene | 2 +- .../scene/test_AnimationBehavior.py | 2 +- tests/image_tests/scene/test_Displacement.py | 2 +- tests/image_tests/scene/test_RtProgram.py | 6 +- .../image_tests/scene/test_TriangleWinding.py | 6 +- .../scene/test_USDPreviewSurface.py | 4 +- tests/python_tests/core/test_device.py | 7 +- tests/python_tests/test_dummy.py | 6 + tests/run_python_tests.bat | 11 +- tests/run_python_tests.sh | 12 +- tests/testing/run_python_tests.py | 4 +- tools/fix_render_script.py | 138 +++++ tools/run_clang_format.py | 2 + 447 files changed, 7136 insertions(+), 3353 deletions(-) create mode 100644 Source/Falcor/Core/API/Aftermath.cpp create mode 100644 Source/Falcor/Core/API/Aftermath.h delete mode 100644 Source/Falcor/Core/API/Shader.cpp delete mode 100644 Source/Falcor/Core/API/Shader.h create mode 100644 Source/Falcor/Core/API/ShaderType.h create mode 100644 Source/Falcor/Core/Enum.h create mode 100644 Source/Falcor/Core/Program/DefineList.h create mode 100644 Source/Falcor/Utils/Properties.cpp create mode 100644 Source/Falcor/Utils/Properties.h rename Source/Falcor/Utils/Scripting/{Dictionary.h => PythonDictionary.h} (90%) create mode 100644 Source/Samples/MultiSampling/CMakeLists.txt rename Source/{Falcor/Rendering/Materials/TexLODTypes.cpp => Samples/MultiSampling/MultiSampling.3d.slang} (82%) create mode 100644 Source/Samples/MultiSampling/MultiSampling.cpp rename Source/{Tools/FalcorTest/FalcorTest.h => Samples/MultiSampling/MultiSampling.h} (83%) rename Source/{Falcor/Core/API/Common.cpp => Tools/FalcorTest/Tests/Core/AftermathTests.cpp} (69%) create mode 100644 Source/Tools/FalcorTest/Tests/Core/AftermathTests.cs.slang create mode 100644 Source/Tools/FalcorTest/Tests/Core/CoreTests.cpp create mode 100644 Source/Tools/FalcorTest/Tests/Core/EnumTests.cpp create mode 100644 Source/Tools/FalcorTest/Tests/Utils/PropertiesTests.cpp create mode 100644 external/include/BS_thread_pool_light.hpp create mode 100644 tools/fix_render_script.py diff --git a/CMakeLists.txt b/CMakeLists.txt index 6d9b9ed1f..2a0c25ddc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -27,6 +27,9 @@ set(FALCOR_USE_SYSTEM_PYTHON OFF CACHE BOOL "Use system Python distribution") set(FALCOR_ENABLE_USD ON CACHE BOOL "Enable USD") +# Enable/disable Address Sanitizer. +set(FALCOR_ENABLE_ASAN OFF CACHE BOOL "Enable Address Sanitizer") + # Header validation. # If enabled, additional targets are generated to validate that headers are self sufficient. set(FALCOR_VALIDATE_HEADERS OFF CACHE BOOL "Enable header validation") @@ -200,6 +203,7 @@ add_subdirectory(external) message(STATUS "Feature flags:") message(STATUS "FALCOR_HAS_D3D12: ${FALCOR_HAS_D3D12}") message(STATUS "FALCOR_HAS_VULKAN: ${FALCOR_HAS_VULKAN}") +message(STATUS "FALCOR_HAS_AFTERMATH: ${FALCOR_HAS_AFTERMATH}") message(STATUS "FALCOR_HAS_NVAPI: ${FALCOR_HAS_NVAPI}") message(STATUS "FALCOR_HAS_PIX: ${FALCOR_HAS_PIX}") message(STATUS "FALCOR_HAS_CUDA: ${FALCOR_HAS_CUDA}") @@ -451,7 +455,7 @@ if(plugin_targets) endif() # Generate settings.toml file. -file(GENERATE OUTPUT ${FALCOR_OUTPUT_DIRECTORY}/settings.json CONTENT "{ \"standardsearchpath\" : { \"media\" : \"\${FALCOR_MEDIA_FOLDERS}\"}}") +file(GENERATE OUTPUT ${FALCOR_OUTPUT_DIRECTORY}/settings.json CONTENT "{ \"standardsearchpath\" : { \"media\" : \"\${FALCOR_MEDIA_FOLDERS}\", \"mdl\" : \"\${FALCOR_MDL_PATHS}\" }}") # Make Mogwai and FalcorPython depend on all plugins. if(plugin_targets) diff --git a/Source/Falcor/CMakeLists.txt b/Source/Falcor/CMakeLists.txt index 2ca962dd0..797c196a0 100644 --- a/Source/Falcor/CMakeLists.txt +++ b/Source/Falcor/CMakeLists.txt @@ -9,6 +9,7 @@ target_sources(Falcor PRIVATE GlobalState.h Core/Assert.h + Core/Enum.h Core/ErrorHandling.cpp Core/ErrorHandling.h Core/Errors.cpp @@ -31,6 +32,8 @@ target_sources(Falcor PRIVATE Core/Window.cpp Core/Window.h + Core/API/Aftermath.h + Core/API/Aftermath.cpp Core/API/BlendState.cpp Core/API/BlendState.h Core/API/BlitContext.cpp @@ -38,7 +41,6 @@ target_sources(Falcor PRIVATE Core/API/BlitReduction.3d.slang Core/API/Buffer.cpp Core/API/Buffer.h - Core/API/Common.cpp Core/API/Common.h Core/API/ComputeContext.cpp Core/API/ComputeContext.h @@ -94,10 +96,9 @@ target_sources(Falcor PRIVATE Core/API/RtStateObject.h Core/API/Sampler.cpp Core/API/Sampler.h - Core/API/Shader.cpp - Core/API/Shader.h Core/API/ShaderResourceType.h Core/API/ShaderTable.h + Core/API/ShaderType.h Core/API/Swapchain.cpp Core/API/Swapchain.h Core/API/Texture.cpp @@ -133,6 +134,7 @@ target_sources(Falcor PRIVATE Core/Program/ComputeProgram.cpp Core/Program/ComputeProgram.h + Core/Program/DefineList.h Core/Program/GraphicsProgram.cpp Core/Program/GraphicsProgram.h Core/Program/Program.cpp @@ -247,7 +249,6 @@ target_sources(Falcor PRIVATE Rendering/Materials/StandardMaterialInstance.slang Rendering/Materials/StandardMaterial.slang Rendering/Materials/TexLODHelpers.slang - Rendering/Materials/TexLODTypes.cpp Rendering/Materials/TexLODTypes.slang Rendering/Materials/PBRT/PBRTDiffuseMaterial.slang @@ -534,6 +535,8 @@ target_sources(Falcor PRIVATE Utils/ObjectIDPython.h Utils/PathResolving.cpp Utils/PathResolving.h + Utils/Properties.cpp + Utils/Properties.h Utils/Settings.cpp Utils/Settings.h Utils/SharedCache.h @@ -666,9 +669,9 @@ 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/PythonDictionary.h Utils/Scripting/ScriptBindings.cpp Utils/Scripting/ScriptBindings.h Utils/Scripting/Scripting.cpp @@ -830,16 +833,43 @@ target_compile_options(Falcor -Wno-literal-suffix -Wno-class-memaccess -Wno-strict-aliasing + -Wno-maybe-uninitialized + -Wno-stringop-truncation > PRIVATE $<$:/bigobj> # big object files ) +if(FALCOR_ENABLE_ASAN) + target_compile_options(Falcor + PUBLIC + $<$: + /fsanitize=address + > + $<$,$>: + -fsanitize=address + > + ) + target_link_options(Falcor + PUBLIC + $<$,$>: + -fsanitize=address + > + ) + target_compile_definitions(Falcor + PUBLIC + $<$: + _DISABLE_VECTOR_ANNOTATION + _DISABLE_STRING_ANNOTATION + > + ) +endif() + if(FALCOR_PRECOMPILED_HEADERS) -target_precompile_headers(Falcor - PRIVATE - Falcor.h -) + target_precompile_headers(Falcor + PRIVATE + Falcor.h + ) endif() target_link_options(Falcor @@ -865,6 +895,7 @@ target_compile_definitions(Falcor # Falcor feature flags. FALCOR_HAS_D3D12=$ FALCOR_HAS_VULKAN=$ + FALCOR_HAS_AFTERMATH=$ FALCOR_HAS_NVAPI=$ FALCOR_HAS_CUDA=$ FALCOR_HAS_D3D12_AGILITY_SDK=$ @@ -902,6 +933,7 @@ target_link_libraries(Falcor glfw mikktspace nvtt $<$:d3d12> $<$:agility-sdk> + $<$:aftermath> $<$:nvapi> # Windows system libraries. $<$:shcore.lib> diff --git a/Source/Falcor/Core/API/Aftermath.cpp b/Source/Falcor/Core/API/Aftermath.cpp new file mode 100644 index 000000000..8d41694b5 --- /dev/null +++ b/Source/Falcor/Core/API/Aftermath.cpp @@ -0,0 +1,467 @@ +/*************************************************************************** + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. + # + # Redistribution and use in source and binary forms, with or without + # modification, are permitted provided that the following conditions + # are met: + # * Redistributions of source code must retain the above copyright + # notice, this list of conditions and the following disclaimer. + # * Redistributions in binary form must reproduce the above copyright + # notice, this list of conditions and the following disclaimer in the + # documentation and/or other materials provided with the distribution. + # * Neither the name of NVIDIA CORPORATION nor the names of its + # contributors may be used to endorse or promote products derived + # from this software without specific prior written permission. + # + # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY + # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + **************************************************************************/ +#if FALCOR_HAS_AFTERMATH + +#include "Aftermath.h" +#include "Device.h" +#include "LowLevelContextData.h" +#include "NativeHandle.h" +#include "NativeHandleTraits.h" +#include "Core/Macros.h" +#include "Core/Version.h" +#include "Utils/Logger.h" + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +// Note: This is based on the Aftermath sample code. + +// Helper for comparing GFSDK_Aftermath_ShaderDebugInfoIdentifier. +inline bool operator<(const GFSDK_Aftermath_ShaderDebugInfoIdentifier& lhs, const GFSDK_Aftermath_ShaderDebugInfoIdentifier& rhs) +{ + if (lhs.id[0] == rhs.id[0]) + { + return lhs.id[1] < rhs.id[1]; + } + return lhs.id[0] < rhs.id[0]; +} + +namespace Falcor +{ + +static std::string getResultString(GFSDK_Aftermath_Result result) +{ + switch (result) + { + case GFSDK_Aftermath_Result_FAIL_DriverVersionNotSupported: + return "Unsupported driver version - requires an NVIDIA R495 display driver or newer."; + case GFSDK_Aftermath_Result_FAIL_D3dDllInterceptionNotSupported: + return "Aftermath is incompatible with D3D API interception, such as PIX or Nsight Graphics."; + case GFSDK_Aftermath_Result_FAIL_D3DDebugLayerNotCompatible: + return "Aftermath is incompatible with the D3D debug layer."; + default: + return fmt::format("Aftermath error {:#x}", int32_t(result)); + } +} + +// Helper macro for checking Nsight Aftermath results and throwing exception +// in case of a failure. +#define AFTERMATH_CHECK_ERROR(FC) \ + do \ + { \ + GFSDK_Aftermath_Result _result = FC; \ + if (!GFSDK_Aftermath_SUCCEED(_result)) \ + throw RuntimeError(getResultString(_result)); \ + } while (0) + +static std::mutex sMutex; +static bool sInitialized = false; +static std::map> sShaderDebugInfo; + +static void shaderDebugInfoLookupCallback( + const GFSDK_Aftermath_ShaderDebugInfoIdentifier* pIdentifier, + PFN_GFSDK_Aftermath_SetData setShaderDebugInfo, + void* pUserData +); +static void shaderLookupCallback( + const GFSDK_Aftermath_ShaderBinaryHash* pShaderHash, + PFN_GFSDK_Aftermath_SetData setShaderBinary, + void* pUserData +); +static void shaderSourceDebugInfoLookupCallback( + const GFSDK_Aftermath_ShaderDebugName* pShaderDebugName, + PFN_GFSDK_Aftermath_SetData setShaderBinary, + void* pUserData +); + +/// Helper for writing a GPU crash dump to a file. +static void writeGpuCrashDumpToFile(const void* pGpuCrashDump, const uint32_t gpuCrashDumpSize) +{ + // Create a GPU crash dump decoder object for the GPU crash dump. + GFSDK_Aftermath_GpuCrashDump_Decoder decoder = {}; + AFTERMATH_CHECK_ERROR(GFSDK_Aftermath_GpuCrashDump_CreateDecoder(GFSDK_Aftermath_Version_API, pGpuCrashDump, gpuCrashDumpSize, &decoder) + ); + + // Use the decoder object to read basic information, like application + // name, PID, etc. from the GPU crash dump. + GFSDK_Aftermath_GpuCrashDump_BaseInfo baseInfo = {}; + AFTERMATH_CHECK_ERROR(GFSDK_Aftermath_GpuCrashDump_GetBaseInfo(decoder, &baseInfo)); + + // Use the decoder object to query the application name that was set + // in the GPU crash dump description. + uint32_t applicationNameLength = 0; + AFTERMATH_CHECK_ERROR(GFSDK_Aftermath_GpuCrashDump_GetDescriptionSize( + decoder, GFSDK_Aftermath_GpuCrashDumpDescriptionKey_ApplicationName, &applicationNameLength + )); + + std::vector applicationName(applicationNameLength, '\0'); + + AFTERMATH_CHECK_ERROR(GFSDK_Aftermath_GpuCrashDump_GetDescription( + decoder, GFSDK_Aftermath_GpuCrashDumpDescriptionKey_ApplicationName, uint32_t(applicationName.size()), applicationName.data() + )); + + // Create a unique file name for writing the crash dump data to a file. + // Note: due to an Nsight Aftermath bug (will be fixed in an upcoming + // driver release) we may see redundant crash dumps. As a workaround, + // attach a unique count to each generated file name. + static int count = 0; + const std::string baseFileName = + (getRuntimeDirectory() / fmt::format("{}-{}-{}", applicationName.data(), baseInfo.pid, ++count)).string(); + + // Write the crash dump data to a file using the .nv-gpudmp extension + // registered with Nsight Graphics. + const std::string crashDumpFileName = baseFileName + ".nv-gpudmp"; + std::ofstream dumpFile(crashDumpFileName, std::ios::out | std::ios::binary); + if (dumpFile) + { + dumpFile.write(reinterpret_cast(pGpuCrashDump), gpuCrashDumpSize); + dumpFile.close(); + } + + // Decode the crash dump to a JSON string. + // Step 1: Generate the JSON and get the size. + uint32_t jsonSize = 0; + AFTERMATH_CHECK_ERROR(GFSDK_Aftermath_GpuCrashDump_GenerateJSON( + decoder, GFSDK_Aftermath_GpuCrashDumpDecoderFlags_ALL_INFO, GFSDK_Aftermath_GpuCrashDumpFormatterFlags_NONE, + shaderDebugInfoLookupCallback, shaderLookupCallback, shaderSourceDebugInfoLookupCallback, nullptr, &jsonSize + )); + // Step 2: Allocate a buffer and fetch the generated JSON. + std::vector json(jsonSize); + AFTERMATH_CHECK_ERROR(GFSDK_Aftermath_GpuCrashDump_GetJSON(decoder, uint32_t(json.size()), json.data())); + + // Write the crash dump data as JSON to a file. + const std::string jsonFileName = crashDumpFileName + ".json"; + std::ofstream jsonFile(jsonFileName, std::ios::out | std::ios::binary); + if (jsonFile) + { + // Write the JSON to the file (excluding string termination) + jsonFile.write(json.data(), json.size() - 1); + jsonFile.close(); + } + + // Destroy the GPU crash dump decoder object. + AFTERMATH_CHECK_ERROR(GFSDK_Aftermath_GpuCrashDump_DestroyDecoder(decoder)); +} + +// Helper for writing shader debug information to a file +static void writeShaderDebugInformationToFile( + GFSDK_Aftermath_ShaderDebugInfoIdentifier identifier, + const void* pShaderDebugInfo, + const uint32_t shaderDebugInfoSize +) +{ + // Create a unique file name. + const std::string path = fmt::format("shader-{:x}{:x}.nvdbg", identifier.id[0], identifier.id[1]); + + std::ofstream f(path, std::ios::out | std::ios::binary); + if (f) + { + f.write((const char*)pShaderDebugInfo, shaderDebugInfoSize); + } +} + +// Handler for GPU crash dump callbacks from Nsight Aftermath +static void gpuCrashDumpCallback(const void* pGpuCrashDump, const uint32_t gpuCrashDumpSize, void* pUserData) +{ + std::lock_guard lock(sMutex); + + // Write to file for later in-depth analysis with Nsight Graphics. + writeGpuCrashDumpToFile(pGpuCrashDump, gpuCrashDumpSize); +} + +// Handler for shader debug information callbacks +static void shaderDebugInfoCallback(const void* pShaderDebugInfo, const uint32_t shaderDebugInfoSize, void* pUserData) +{ + std::lock_guard lock(sMutex); + + // Get shader debug information identifier + GFSDK_Aftermath_ShaderDebugInfoIdentifier identifier = {}; + AFTERMATH_CHECK_ERROR( + GFSDK_Aftermath_GetShaderDebugInfoIdentifier(GFSDK_Aftermath_Version_API, pShaderDebugInfo, shaderDebugInfoSize, &identifier) + ); + + // Store information for decoding of GPU crash dumps with shader address mapping + // from within the application. + std::vector data((uint8_t*)pShaderDebugInfo, (uint8_t*)pShaderDebugInfo + shaderDebugInfoSize); + sShaderDebugInfo[identifier].swap(data); + + // Write to file for later in-depth analysis of crash dumps with Nsight Graphics + writeShaderDebugInformationToFile(identifier, pShaderDebugInfo, shaderDebugInfoSize); +} + +// Handler for GPU crash dump description callbacks +static void crashDumpDescriptionCallback(PFN_GFSDK_Aftermath_AddGpuCrashDumpDescription addDescription, void* pUserData) +{ + addDescription(GFSDK_Aftermath_GpuCrashDumpDescriptionKey_ApplicationName, "Falcor"); + addDescription(GFSDK_Aftermath_GpuCrashDumpDescriptionKey_ApplicationVersion, getLongVersionString().c_str()); + // addDescription(GFSDK_Aftermath_GpuCrashDumpDescriptionKey_UserDefined, "user defined string 1"); + // addDescription(GFSDK_Aftermath_GpuCrashDumpDescriptionKey_UserDefined + 1, "used defined string 2"); +} + +// Handler for app-managed marker resolve callbacks +static void resolveMarkerCallback(const void* pMarker, void* pUserData, void** resolvedMarkerData, uint32_t* markerSize) {} + +// Handler for shader source debug info lookup callbacks. +// This is used by the JSON decoder for mapping shader instruction addresses to +// HLSL source lines, if the shaders used by the application were compiled with +// separate debug info data files. +static void shaderDebugInfoLookupCallback( + const GFSDK_Aftermath_ShaderDebugInfoIdentifier* pIdentifier, + PFN_GFSDK_Aftermath_SetData setShaderDebugInfo, + void* pUserData +) +{ + // Search the list of shader debug information blobs received earlier. + auto it = sShaderDebugInfo.find(*pIdentifier); + if (it == sShaderDebugInfo.end()) + { + // Early exit, nothing found. No need to call setShaderDebugInfo. + return; + } + + // Let the GPU crash dump decoder know about the shader debug information + // that was found. + setShaderDebugInfo(it->second.data(), uint32_t(it->second.size())); +} + +// Handler for shader lookup callbacks. +// This is used by the JSON decoder for mapping shader instruction +// addresses to DXIL lines or HLSL source lines. +// NOTE: If the application loads stripped shader binaries (-Qstrip_debug), +// Aftermath will require access to both the stripped and the not stripped +// shader binaries. +static void shaderLookupCallback( + const GFSDK_Aftermath_ShaderBinaryHash* pShaderHash, + PFN_GFSDK_Aftermath_SetData setShaderBinary, + void* pUserData +) +{ + // Find shader binary data for the shader hash in the shader database. + std::vector shaderBinary; + // if (!m_shaderDatabase.FindShaderBinary(shaderHash, shaderBinary)) + { + // Early exit, nothing found. No need to call setShaderBinary. + return; + } + + // Let the GPU crash dump decoder know about the shader data + // that was found. + setShaderBinary(shaderBinary.data(), uint32_t(shaderBinary.size())); +} + +// Handler for shader source debug info lookup callbacks. +// This is used by the JSON decoder for mapping shader instruction addresses to +// HLSL source lines, if the shaders used by the application were compiled with +// separate debug info data files. +static void shaderSourceDebugInfoLookupCallback( + const GFSDK_Aftermath_ShaderDebugName* pShaderDebugName, + PFN_GFSDK_Aftermath_SetData setShaderBinary, + void* pUserData +) +{ + // Find source debug info for the shader DebugName in the shader database. + std::vector sourceDebugInfo; + // if (!m_shaderDatabase.FindSourceShaderDebugData(shaderDebugName, sourceDebugInfo)) + { + // Early exit, nothing found. No need to call setShaderBinary. + return; + } + + // Let the GPU crash dump decoder know about the shader debug data that was + // found. + setShaderBinary(sourceDebugInfo.data(), uint32_t(sourceDebugInfo.size())); +} + +void enableAftermath() +{ + std::lock_guard lock(sMutex); + + if (sInitialized) + return; + + // Enable GPU crash dumps and set up the callbacks for crash dump notifications, + // shader debug information notifications, and providing additional crash + // dump description data.Only the crash dump callback is mandatory. The other two + // callbacks are optional and can be omitted, by passing nullptr, if the corresponding + // functionality is not used. + // The DeferDebugInfoCallbacks flag enables caching of shader debug information data + // in memory. If the flag is set, ShaderDebugInfoCallback will be called only + // in the event of a crash, right before GpuCrashDumpCallback. If the flag is not set, + // ShaderDebugInfoCallback will be called for every shader that is compiled. + AFTERMATH_CHECK_ERROR(GFSDK_Aftermath_EnableGpuCrashDumps( + // API version + GFSDK_Aftermath_Version_API, + // Device flags + GFSDK_Aftermath_GpuCrashDumpWatchedApiFlags_DX | GFSDK_Aftermath_GpuCrashDumpWatchedApiFlags_Vulkan, + // Let the Nsight Aftermath library cache shader debug information + GFSDK_Aftermath_GpuCrashDumpFeatureFlags_DeferDebugInfoCallbacks, + // Callbacks + gpuCrashDumpCallback, shaderDebugInfoCallback, crashDumpDescriptionCallback, + // Do not resolve markers for now (they are embedded with string data) + nullptr /* resolveMarkerCallback */, + // User data + nullptr + )); + + sInitialized = true; +} + +void disableAftermath() +{ + std::lock_guard lock(sMutex); + + if (sInitialized) + { + GFSDK_Aftermath_DisableGpuCrashDumps(); + sInitialized = false; + } +} + +bool waitForAftermathDumps(int timeout) +{ + if (!sInitialized) + return true; + + // DXGI_ERROR error notification is asynchronous to the NVIDIA display + // driver's GPU crash handling. Give the Nsight Aftermath GPU crash dump + // thread some time to do its work before terminating the process. + auto tdrTerminationTimeout = std::chrono::seconds(timeout); + auto tStart = std::chrono::steady_clock::now(); + auto tElapsed = std::chrono::milliseconds::zero(); + + GFSDK_Aftermath_CrashDump_Status status = GFSDK_Aftermath_CrashDump_Status_Unknown; + AFTERMATH_CHECK_ERROR(GFSDK_Aftermath_GetCrashDumpStatus(&status)); + + while (status != GFSDK_Aftermath_CrashDump_Status_CollectingDataFailed && status != GFSDK_Aftermath_CrashDump_Status_Finished && + tElapsed < tdrTerminationTimeout) + { + // Sleep 50ms and poll the status again until timeout or Aftermath finished processing the crash dump. + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + AFTERMATH_CHECK_ERROR(GFSDK_Aftermath_GetCrashDumpStatus(&status)); + + auto tEnd = std::chrono::steady_clock::now(); + tElapsed = std::chrono::duration_cast(tEnd - tStart); + } + + return status == GFSDK_Aftermath_CrashDump_Status_Finished; +} + +AftermathContext::AftermathContext(Device* pDevice) : mpDevice(pDevice) {} + +AftermathContext::~AftermathContext() {} + +bool AftermathContext::initialize(AftermathFlags flags) +{ + if (mInitialized) + return true; + + flags |= AftermathFlags::EnableMarkers; + flags |= AftermathFlags::EnableResourceTracking; + flags |= AftermathFlags::CallStackCapturing; + // flags |= AftermathFlags::GenerateShaderDebugInfo; + // flags |= AftermathFlags::EnableShaderErrorReporting; + + switch (mpDevice->getType()) + { +#if FALCOR_HAS_D3D12 + case Device::Type::D3D12: + { + ID3D12Device* pD3D12Device = mpDevice->getNativeHandle(0).as(); + GFSDK_Aftermath_Result result = GFSDK_Aftermath_DX12_Initialize(GFSDK_Aftermath_Version_API, flags, pD3D12Device); + if (!GFSDK_Aftermath_SUCCEED(result)) + { + logWarning("Aftermath failed to initialize on D3D12 device: {}", getResultString(result)); + return false; + } + } + break; +#endif +#if FALCOR_HAS_VULKAN + case Device::Type::Vulkan: + logWarning("Aftermath on Vulkan only supports basic GPU crash dumps."); + return false; +#endif + default: + logWarning("Aftermath is not supported on this device."); + return false; + } + + mInitialized = true; + return true; +} + +void AftermathContext::addMarker(const LowLevelContextData* pLowLevelContextData, std::string_view name) +{ + if (!mInitialized) + return; + +#if FALCOR_HAS_D3D12 + if (mpDevice->getType() == Device::Type::D3D12) + { + auto& context = reinterpret_cast(mContextHandle); + ID3D12GraphicsCommandList* pCommandList = pLowLevelContextData->getCommandBufferNativeHandle().as(); + GFSDK_Aftermath_Result result; + + if (pCommandList != mpLastCommandList) + { + // TODO should we call + // GFSDK_Aftermath_ReleaseContextHandle(context); + mpLastCommandList = pCommandList; + result = GFSDK_Aftermath_DX12_CreateContextHandle(pCommandList, &context); + if (!GFSDK_Aftermath_SUCCEED(result)) + { + logWarning("Aftermath failed to create context handle: {}", getResultString(result)); + mInitialized = false; + return; + } + } + + result = GFSDK_Aftermath_SetEventMarker(context, name.data(), name.size()); + if (!GFSDK_Aftermath_SUCCEED(result)) + { + logWarning("Aftermath failed to set event marker: {}", getResultString(result)); + mInitialized = false; + return; + } + } +#endif +} + +} // namespace Falcor + +#endif // FALCOR_HAS_AFTERMATH diff --git a/Source/Falcor/Core/API/Aftermath.h b/Source/Falcor/Core/API/Aftermath.h new file mode 100644 index 000000000..0ff0a9152 --- /dev/null +++ b/Source/Falcor/Core/API/Aftermath.h @@ -0,0 +1,99 @@ +/*************************************************************************** + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. + # + # Redistribution and use in source and binary forms, with or without + # modification, are permitted provided that the following conditions + # are met: + # * Redistributions of source code must retain the above copyright + # notice, this list of conditions and the following disclaimer. + # * Redistributions in binary form must reproduce the above copyright + # notice, this list of conditions and the following disclaimer in the + # documentation and/or other materials provided with the distribution. + # * Neither the name of NVIDIA CORPORATION nor the names of its + # contributors may be used to endorse or promote products derived + # from this software without specific prior written permission. + # + # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY + # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + **************************************************************************/ + +/** + * This file defines a wrapper of NSight Aftermath SDK and is used within + * the Device class to add Aftermath support. + * + * Aftermath generates a GPU crash dump when the application crashes. This can + * be very useful for debugging GPU crashes. + */ + +#pragma once + +#if FALCOR_HAS_AFTERMATH + +#include "Core/Macros.h" + +#include + +namespace Falcor +{ + +class Device; +class LowLevelContextData; + +/// Aftermath feature flags. +/// See section on GFSDK_Aftermath_FeatureFlags in GFSDK_Aftermath.h for details. +/// Note: For using EnableMarkers, the Aftermath Monitor must be running on the +/// host machine. +enum AftermathFlags +{ + Minimum = 0x00000000, + EnableMarkers = 0x00000001, + EnableResourceTracking = 0x00000002, + CallStackCapturing = 0x40000000, + GenerateShaderDebugInfo = 0x00000008, + EnableShaderErrorReporting = 0x00000010, + + Defaults = EnableMarkers | EnableResourceTracking | CallStackCapturing | GenerateShaderDebugInfo | EnableShaderErrorReporting, +}; +FALCOR_ENUM_CLASS_OPERATORS(AftermathFlags); + +/// Aftermath per-device context. +class AftermathContext +{ +public: + AftermathContext(Device* pDevice); + ~AftermathContext(); + + /// Initialize Aftermath on the device. + bool initialize(AftermathFlags flags = AftermathFlags::Defaults); + + /// Add a marker to the command list. + void addMarker(const LowLevelContextData* pLowLevelContextData, std::string_view name); + +private: + Device* mpDevice; + bool mInitialized = false; + void* mpLastCommandList = nullptr; + int32_t mContextHandle = 0; +}; + +/// Enable GPU crash dump tracking. +void enableAftermath(); + +/// Disable GPU crash dump tracking. +void disableAftermath(); + +/// Wait for GPU crash dumps to be generated. Timeout is in seconds. +bool waitForAftermathDumps(int timeout = 5); + +} // namespace Falcor + +#endif // FALCOR_HAS_AFTERMATH diff --git a/Source/Falcor/Core/API/BlendState.h b/Source/Falcor/Core/API/BlendState.h index 3a6051283..db1d60b13 100644 --- a/Source/Falcor/Core/API/BlendState.h +++ b/Source/Falcor/Core/API/BlendState.h @@ -39,6 +39,7 @@ namespace Falcor */ class FALCOR_API BlendState : public Object { + FALCOR_OBJECT(BlendState) public: /** * Defines how to combine the blend inputs diff --git a/Source/Falcor/Core/API/BlitContext.cpp b/Source/Falcor/Core/API/BlitContext.cpp index 1dc062463..431d9b260 100644 --- a/Source/Falcor/Core/API/BlitContext.cpp +++ b/Source/Falcor/Core/API/BlitContext.cpp @@ -38,7 +38,7 @@ BlitContext::BlitContext(Device* pDevice) FALCOR_ASSERT(pDevice); // Init the blit data. - Program::DefineList defines = { + DefineList defines = { {"SAMPLE_COUNT", "1"}, {"COMPLEX_BLIT", "0"}, {"SRC_INT", "0"}, diff --git a/Source/Falcor/Core/API/Buffer.h b/Source/Falcor/Core/API/Buffer.h index 4a5fe09b6..e454062e0 100644 --- a/Source/Falcor/Core/API/Buffer.h +++ b/Source/Falcor/Core/API/Buffer.h @@ -115,6 +115,7 @@ CASE(float3, ResourceFormat::RGB32Float); */ class FALCOR_API Buffer : public Resource { + FALCOR_OBJECT(Buffer) public: /** * Buffer access flags. diff --git a/Source/Falcor/Core/API/Common.h b/Source/Falcor/Core/API/Common.h index a9dc60f9e..dd6a7a1c6 100644 --- a/Source/Falcor/Core/API/Common.h +++ b/Source/Falcor/Core/API/Common.h @@ -27,6 +27,8 @@ **************************************************************************/ #pragma once +#include "Core/Enum.h" + namespace Falcor { enum class ComparisonFunc @@ -41,4 +43,21 @@ enum class ComparisonFunc Greater, ///< Passes if source is greater than to the destination GreaterEqual, ///< Passes if source is greater than or equal to the destination }; -} + +FALCOR_ENUM_INFO( + ComparisonFunc, + { + {ComparisonFunc::Disabled, "Disabled"}, + {ComparisonFunc::Never, "Never"}, + {ComparisonFunc::Always, "Always"}, + {ComparisonFunc::Less, "Less"}, + {ComparisonFunc::Equal, "Equal"}, + {ComparisonFunc::NotEqual, "NotEqual"}, + {ComparisonFunc::LessEqual, "LessEqual"}, + {ComparisonFunc::Greater, "Greater"}, + {ComparisonFunc::GreaterEqual, "GreaterEqual"}, + } +); +FALCOR_ENUM_REGISTER(ComparisonFunc); + +} // namespace Falcor diff --git a/Source/Falcor/Core/API/ComputeStateObject.h b/Source/Falcor/Core/API/ComputeStateObject.h index 8e127c20a..695aa8667 100644 --- a/Source/Falcor/Core/API/ComputeStateObject.h +++ b/Source/Falcor/Core/API/ComputeStateObject.h @@ -42,6 +42,7 @@ class D3D12RootSignature; class FALCOR_API ComputeStateObject : public Object { + FALCOR_OBJECT(ComputeStateObject) public: class FALCOR_API Desc { diff --git a/Source/Falcor/Core/API/CopyContext.cpp b/Source/Falcor/Core/API/CopyContext.cpp index b61ab5799..cc02322cd 100644 --- a/Source/Falcor/Core/API/CopyContext.cpp +++ b/Source/Falcor/Core/API/CopyContext.cpp @@ -32,6 +32,7 @@ #include "GpuFence.h" #include "GFXHelpers.h" #include "NativeHandleTraits.h" +#include "Aftermath.h" #if FALCOR_HAS_D3D12 #include "Shared/D3D12DescriptorPool.h" #include "Shared/D3D12DescriptorData.h" @@ -532,4 +533,13 @@ void CopyContext::copySubresourceRegion( ); mCommandsPending = true; } + +void CopyContext::addAftermathMarker(std::string_view name) +{ +#if FALCOR_HAS_AFTERMATH + if (AftermathContext* pAftermathContext = mpDevice->getAftermathContext()) + pAftermathContext->addMarker(mpLowLevelData.get(), name); +#endif +}; + } // namespace Falcor diff --git a/Source/Falcor/Core/API/CopyContext.h b/Source/Falcor/Core/API/CopyContext.h index fad4640ef..c21ea43f7 100644 --- a/Source/Falcor/Core/API/CopyContext.h +++ b/Source/Falcor/Core/API/CopyContext.h @@ -187,6 +187,11 @@ class FALCOR_API CopyContext */ void unbindCustomGPUDescriptorPool(); + /** + * Add an aftermath marker to the command list. + */ + void addAftermathMarker(std::string_view name); + protected: bool textureBarrier(const Texture* pTexture, Resource::State newState); bool bufferBarrier(const Buffer* pBuffer, Resource::State newState); diff --git a/Source/Falcor/Core/API/DepthStencilState.h b/Source/Falcor/Core/API/DepthStencilState.h index d4d92ec35..678e4d3f0 100644 --- a/Source/Falcor/Core/API/DepthStencilState.h +++ b/Source/Falcor/Core/API/DepthStencilState.h @@ -38,6 +38,7 @@ namespace Falcor */ class FALCOR_API DepthStencilState : public Object { + FALCOR_OBJECT(DepthStencilState) public: /** * Used for stencil control. diff --git a/Source/Falcor/Core/API/Device.cpp b/Source/Falcor/Core/API/Device.cpp index adf26bd53..3c1790d27 100644 --- a/Source/Falcor/Core/API/Device.cpp +++ b/Source/Falcor/Core/API/Device.cpp @@ -30,6 +30,7 @@ #include "GFXHelpers.h" #include "GFXAPI.h" #include "NativeHandleTraits.h" +#include "Aftermath.h" #include "Core/Macros.h" #include "Core/Assert.h" #include "Core/Errors.h" @@ -381,6 +382,17 @@ inline Device::ShaderModel querySupportedShaderModel(gfx::IDevice* pDevice) Device::Device(const Desc& desc) : mDesc(desc) { + if (mDesc.enableAftermath) + { +#if FALCOR_HAS_AFTERMATH + // Aftermath is incompatible with debug layers, so lets disable them. + mDesc.enableDebugLayer = false; + enableAftermath(); +#else + logWarning("Falcor was compiled without Aftermath support. Aftermath is disabled"); +#endif + } + // Create a global slang session passed to GFX and used for compiling programs in ProgramManager. slang::createGlobalSession(mSlangGlobalSession.writeRef()); @@ -472,11 +484,20 @@ Device::Device(const Desc& desc) : mDesc(desc) } const auto& deviceInfo = mGfxDevice->getDeviceInfo(); - logInfo("Created GPU device '{}' using '{}' API.", deviceInfo.adapterName, deviceInfo.apiName); - + mInfo.adapterName = deviceInfo.adapterName; + mInfo.apiName = deviceInfo.apiName; + logInfo("Created GPU device '{}' using '{}' API.", mInfo.adapterName, mInfo.apiName); mLimits = queryLimits(mGfxDevice); mSupportedFeatures = querySupportedFeatures(mGfxDevice); +#if FALCOR_HAS_AFTERMATH + if (mDesc.enableAftermath) + { + mpAftermathContext = std::make_unique(this); + mpAftermathContext->initialize(); + } +#endif + #if FALCOR_NVAPI_AVAILABLE // Explicitly check for SER support via NVAPI. // Slang currently relies on NVAPI to implement the SER API but cannot check it's availibility @@ -499,6 +520,11 @@ Device::Device(const Desc& desc) : mDesc(desc) mSupportedFeatures |= SupportedFeatures::RaytracingReordering; } #endif + if (getType() == Type::Vulkan) + { + // Vulkan always supports SER. + mSupportedFeatures |= SupportedFeatures::ShaderExecutionReorderingAPI; + } mSupportedShaderModel = querySupportedShaderModel(mGfxDevice); mGpuTimestampFrequency = 1000.0 / (double)mGfxDevice->getDeviceInfo().timestampFrequency; @@ -826,6 +852,7 @@ void Device::endFrame() mpFrameFence->syncCpu(mpFrameFence->getCpuValue() - kInFlightFrameCount); // Switch to next transient resource heap. + getCurrentTransientResourceHeap()->finish(); mCurrentTransientResourceHeapIndex = (mCurrentTransientResourceHeapIndex + 1) % kInFlightFrameCount; mpRenderContext->getLowLevelData()->closeCommandBuffer(); getCurrentTransientResourceHeap()->synchronizeAndReset(); @@ -881,22 +908,27 @@ FALCOR_SCRIPT_BINDING(Device) deviceType.value("D3D12", Device::Type::D3D12); deviceType.value("Vulkan", Device::Type::Vulkan); + pybind11::class_ info(device, "Info"); + info.def_readonly("adapter_name", &Device::Info::adapterName); + info.def_readonly("api_name", &Device::Info::apiName); + 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::Type type, uint32_t gpu, bool enable_debug_layer, bool enable_aftermath) { Device::Desc desc; desc.type = type; desc.gpu = gpu; desc.enableDebugLayer = enable_debug_layer; + desc.enableAftermath = enable_aftermath; return make_ref(desc); } ), - "type"_a = Device::Type::Default, "gpu"_a = 0, "enable_debug_layer"_a = false + "type"_a = Device::Type::Default, "gpu"_a = 0, "enable_debug_layer"_a = false, "enable_aftermath"_a = false ); device.def( "create_buffer", @@ -921,6 +953,8 @@ FALCOR_SCRIPT_BINDING(Device) ); device.def_property_readonly("profiler", &Device::getProfiler); + device.def_property_readonly("type", &Device::getType); + device.def_property_readonly("info", &Device::getInfo); device.def_property_readonly("limits", &Device::getLimits); device.def_property_readonly("render_context", &Device::getRenderContext); } diff --git a/Source/Falcor/Core/API/Device.h b/Source/Falcor/Core/API/Device.h index cd2c1ff6c..c7d4f1382 100644 --- a/Source/Falcor/Core/API/Device.h +++ b/Source/Falcor/Core/API/Device.h @@ -62,9 +62,11 @@ class D3D12DescriptorPool; class PipelineCreationAPIDispatcher; class ProgramManager; class Profiler; +class AftermathContext; class FALCOR_API Device : public Object { + FALCOR_OBJECT(Device) public: /** * Maximum number of in-flight frames. @@ -93,6 +95,9 @@ class FALCOR_API Device : public Object /// Enable the debug layer. The default for release build is false, for debug build it's true. bool enableDebugLayer = FALCOR_DEFAULT_ENABLE_DEBUG_LAYER; + /// Enable NVIDIA NSight Aftermath GPU crash dump. + bool enableAftermath = false; + /// The maximum number of entries allowable in the shader cache. A value of 0 indicates no limit. uint32_t maxShaderCacheEntryCount = 1000; @@ -105,6 +110,12 @@ class FALCOR_API Device : public Object #endif }; + struct Info + { + std::string adapterName; + std::string apiName; + }; + struct Limits { uint3 maxComputeDispatchThreadGroups; @@ -125,7 +136,7 @@ class FALCOR_API Device : public Object ConservativeRasterizationTier3 = 0x80, ///< On D3D12, conservative rasterization tier 3 is supported. RasterizerOrderedViews = 0x100, ///< On D3D12, rasterizer ordered views (ROVs) are supported. WaveOperations = 0x200, - ShaderExecutionReorderingAPI = 0x400, ///< On D3D12, this means SER API is available (in the future this will be part of the shader model). + ShaderExecutionReorderingAPI = 0x400, ///< On D3D12 and Vulkan, this means SER API is available (in the future this will be part of the shader model). RaytracingReordering = 0x800, ///< On D3D12, this means SER is supported on the hardware. // clang-format on @@ -215,6 +226,10 @@ class FALCOR_API Device : public Object */ const ref& getDefaultSampler() const { return mpDefaultSampler; } +#if FALCOR_HAS_AFTERMATH + AftermathContext* getAftermathContext() const { return mpAftermathContext.get(); } +#endif + #if FALCOR_HAS_D3D12 const ref& getD3D12CpuDescriptorPool() const { @@ -234,6 +249,8 @@ class FALCOR_API Device : public Object double getGpuTimestampFrequency() const { return mGpuTimestampFrequency; } // ms/tick + const Info& getInfo() const { return mInfo; } + /** * Get the device limits. */ @@ -323,10 +340,15 @@ class FALCOR_API Device : public Object std::unique_ptr mpRenderContext; double mGpuTimestampFrequency; + Info mInfo; Limits mLimits; SupportedFeatures mSupportedFeatures = SupportedFeatures::None; ShaderModel mSupportedShaderModel = ShaderModel::Unknown; +#if FALCOR_HAS_AFTERMATH + std::unique_ptr mpAftermathContext; +#endif + #if FALCOR_NVAPI_AVAILABLE std::unique_ptr mpAPIDispatcher; #endif diff --git a/Source/Falcor/Core/API/FBO.h b/Source/Falcor/Core/API/FBO.h index b8aa2a9e0..ad332c2d4 100644 --- a/Source/Falcor/Core/API/FBO.h +++ b/Source/Falcor/Core/API/FBO.h @@ -44,6 +44,7 @@ namespace Falcor */ class FALCOR_API Fbo : public Object { + FALCOR_OBJECT(Fbo) public: class FALCOR_API Desc { diff --git a/Source/Falcor/Core/API/FencedPool.h b/Source/Falcor/Core/API/FencedPool.h index af1d90577..44cca0340 100644 --- a/Source/Falcor/Core/API/FencedPool.h +++ b/Source/Falcor/Core/API/FencedPool.h @@ -38,6 +38,7 @@ namespace Falcor template class FALCOR_API FencedPool : public Object { + FALCOR_OBJECT(FencedPool) public: using NewObjectFuncType = ObjectType (*)(void*); diff --git a/Source/Falcor/Core/API/Formats.cpp b/Source/Falcor/Core/API/Formats.cpp index 6c9a43b04..33dad7ba9 100644 --- a/Source/Falcor/Core/API/Formats.cpp +++ b/Source/Falcor/Core/API/Formats.cpp @@ -52,6 +52,7 @@ const FormatDesc kFormatDesc[] = { {ResourceFormat::RGB10A2Unorm, "RGB10A2Unorm", 4, 4, FormatType::Unorm, {false, false, false,}, {1, 1}, {10, 10, 10, 2 }}, {ResourceFormat::RGB10A2Uint, "RGB10A2Uint", 4, 4, FormatType::Uint, {false, false, false,}, {1, 1}, {10, 10, 10, 2 }}, {ResourceFormat::RGBA16Unorm, "RGBA16Unorm", 8, 4, FormatType::Unorm, {false, false, false,}, {1, 1}, {16, 16, 16, 16}}, + {ResourceFormat::RGBA16Snorm, "RGBA16Snorm", 8, 4, FormatType::Snorm, {false, false, false,}, {1, 1}, {16, 16, 16, 16}}, {ResourceFormat::RGBA8UnormSrgb, "RGBA8UnormSrgb", 4, 4, FormatType::UnormSrgb, {false, false, false,}, {1, 1}, {8, 8, 8, 8 }}, // Format Name, BytesPerBlock ChannelCount Type {bDepth, bStencil, bCompressed}, {CompressionRatio.Width, CompressionRatio.Height} {ResourceFormat::R16Float, "R16Float", 2, 1, FormatType::Float, {false, false, false,}, {1, 1}, {16, 0, 0, 0 }}, @@ -79,11 +80,12 @@ const FormatDesc kFormatDesc[] = { {ResourceFormat::RGB32Int, "RGB32Int", 12, 3, FormatType::Sint, {false, false, false,}, {1, 1}, {32, 32, 32, 0 }}, {ResourceFormat::RGB32Uint, "RGB32Uint", 12, 3, FormatType::Uint, {false, false, false,}, {1, 1}, {32, 32, 32, 0 }}, {ResourceFormat::RGBA8Int, "RGBA8Int", 4, 4, FormatType::Sint, {false, false, false,}, {1, 1}, {8, 8, 8, 8 }}, - {ResourceFormat::RGBA8Uint, "RGBA8Uint", 4, 4, FormatType::Uint, {false, false, false, }, {1, 1}, {8, 8, 8, 8 }}, + {ResourceFormat::RGBA8Uint, "RGBA8Uint", 4, 4, FormatType::Uint, {false, false, false,}, {1, 1}, {8, 8, 8, 8 }}, {ResourceFormat::RGBA16Int, "RGBA16Int", 8, 4, FormatType::Sint, {false, false, false,}, {1, 1}, {16, 16, 16, 16}}, {ResourceFormat::RGBA16Uint, "RGBA16Uint", 8, 4, FormatType::Uint, {false, false, false,}, {1, 1}, {16, 16, 16, 16}}, {ResourceFormat::RGBA32Int, "RGBA32Int", 16, 4, FormatType::Sint, {false, false, false,}, {1, 1}, {32, 32, 32, 32}}, {ResourceFormat::RGBA32Uint, "RGBA32Uint", 16, 4, FormatType::Uint, {false, false, false,}, {1, 1}, {32, 32, 32, 32}}, + {ResourceFormat::BGRA4Unorm, "BGRA4Unorm", 2, 4, FormatType::Unorm, {false, false, false,}, {1, 1}, {4, 4, 4, 4 }}, {ResourceFormat::BGRA8Unorm, "BGRA8Unorm", 4, 4, FormatType::Unorm, {false, false, false,}, {1, 1}, {8, 8, 8, 8 }}, {ResourceFormat::BGRA8UnormSrgb, "BGRA8UnormSrgb", 4, 4, FormatType::UnormSrgb, {false, false, false,}, {1, 1}, {8, 8, 8, 8 }}, {ResourceFormat::BGRX8Unorm, "BGRX8Unorm", 4, 4, FormatType::Unorm, {false, false, false,}, {1, 1}, {8, 8, 8, 8 }}, @@ -91,6 +93,7 @@ const FormatDesc kFormatDesc[] = { // Format Name, BytesPerBlock ChannelCount Type {bDepth, bStencil, bCompressed}, {CompressionRatio.Width, CompressionRatio.Height} {ResourceFormat::R5G6B5Unorm, "R5G6B5Unorm", 2, 3, FormatType::Unorm, {false, false, false,}, {1, 1}, {5, 6, 5, 0 }}, {ResourceFormat::D32Float, "D32Float", 4, 1, FormatType::Float, {true, false, false,}, {1, 1}, {32, 0, 0, 0 }}, + {ResourceFormat::D32FloatS8Uint, "D32FloatS8Uint", 4, 1, FormatType::Float, {true, true, false,}, {1, 1}, {32, 0, 0, 0 }}, {ResourceFormat::D16Unorm, "D16Unorm", 2, 1, FormatType::Unorm, {true, false, false,}, {1, 1}, {16, 0, 0, 0 }}, {ResourceFormat::BC1Unorm, "BC1Unorm", 8, 3, FormatType::Unorm, {false, false, true, }, {4, 4}, {64, 0, 0, 0 }}, {ResourceFormat::BC1UnormSrgb, "BC1UnormSrgb", 8, 3, FormatType::UnormSrgb, {false, false, true, }, {4, 4}, {64, 0, 0, 0 }}, @@ -170,6 +173,7 @@ const NativeFormatDesc kNativeFormatDescs[] = { {ResourceFormat::RGB10A2Unorm, DXGI_FORMAT_R10G10B10A2_UNORM, VK_FORMAT_A2B10G10R10_UNORM_PACK32}, {ResourceFormat::RGB10A2Uint, DXGI_FORMAT_R10G10B10A2_UINT, VK_FORMAT_A2B10G10R10_UINT_PACK32}, {ResourceFormat::RGBA16Unorm, DXGI_FORMAT_R16G16B16A16_UNORM, VK_FORMAT_R16G16B16A16_UNORM}, + {ResourceFormat::RGBA16Snorm, DXGI_FORMAT_R16G16B16A16_SNORM, VK_FORMAT_R16G16B16A16_SNORM}, {ResourceFormat::RGBA8UnormSrgb, DXGI_FORMAT_R8G8B8A8_UNORM_SRGB, VK_FORMAT_R8G8B8A8_SRGB}, {ResourceFormat::R16Float, DXGI_FORMAT_R16_FLOAT, VK_FORMAT_R16_SFLOAT}, {ResourceFormat::RG16Float, DXGI_FORMAT_R16G16_FLOAT, VK_FORMAT_R16G16_SFLOAT}, @@ -200,12 +204,14 @@ const NativeFormatDesc kNativeFormatDescs[] = { {ResourceFormat::RGBA16Uint, DXGI_FORMAT_R16G16B16A16_UINT, VK_FORMAT_R16G16B16A16_UINT}, {ResourceFormat::RGBA32Int, DXGI_FORMAT_R32G32B32A32_SINT, VK_FORMAT_R32G32B32A32_SINT}, {ResourceFormat::RGBA32Uint, DXGI_FORMAT_R32G32B32A32_UINT, VK_FORMAT_R32G32B32A32_UINT}, + {ResourceFormat::BGRA4Unorm, DXGI_FORMAT_B4G4R4A4_UNORM, VK_FORMAT_UNDEFINED}, {ResourceFormat::BGRA8Unorm, DXGI_FORMAT_B8G8R8A8_UNORM, VK_FORMAT_B8G8R8A8_UNORM}, {ResourceFormat::BGRA8UnormSrgb, DXGI_FORMAT_B8G8R8A8_UNORM_SRGB, VK_FORMAT_B8G8R8A8_SRGB}, {ResourceFormat::BGRX8Unorm, DXGI_FORMAT_B8G8R8X8_UNORM, VK_FORMAT_B8G8R8A8_UNORM}, {ResourceFormat::BGRX8UnormSrgb, DXGI_FORMAT_B8G8R8X8_UNORM_SRGB, VK_FORMAT_B8G8R8A8_SRGB}, {ResourceFormat::R5G6B5Unorm, DXGI_FORMAT_B5G6R5_UNORM, VK_FORMAT_B5G6R5_UNORM_PACK16}, {ResourceFormat::D32Float, DXGI_FORMAT_D32_FLOAT, VK_FORMAT_D32_SFLOAT}, + {ResourceFormat::D32FloatS8Uint, DXGI_FORMAT_D32_FLOAT_S8X24_UINT, VK_FORMAT_D32_SFLOAT_S8_UINT}, {ResourceFormat::D16Unorm, DXGI_FORMAT_D16_UNORM, VK_FORMAT_D16_UNORM}, {ResourceFormat::BC1Unorm, DXGI_FORMAT_BC1_UNORM, VK_FORMAT_BC1_RGB_UNORM_BLOCK}, {ResourceFormat::BC1UnormSrgb, DXGI_FORMAT_BC1_UNORM_SRGB, VK_FORMAT_BC1_RGB_SRGB_BLOCK}, diff --git a/Source/Falcor/Core/API/Formats.h b/Source/Falcor/Core/API/Formats.h index 28c24d486..756c7feb4 100644 --- a/Source/Falcor/Core/API/Formats.h +++ b/Source/Falcor/Core/API/Formats.h @@ -28,7 +28,9 @@ #pragma once #include "Core/Assert.h" #include "Core/Macros.h" +#include "Core/Enum.h" #include +#include #include namespace Falcor @@ -98,6 +100,7 @@ enum class ResourceFormat : uint32_t RGB10A2Unorm, RGB10A2Uint, RGBA16Unorm, + RGBA16Snorm, RGBA8UnormSrgb, R16Float, RG16Float, @@ -129,6 +132,7 @@ enum class ResourceFormat : uint32_t RGBA32Int, RGBA32Uint, + BGRA4Unorm, BGRA8Unorm, BGRA8UnormSrgb, @@ -138,6 +142,7 @@ enum class ResourceFormat : uint32_t // Depth-stencil D32Float, + D32FloatS8Uint, D16Unorm, // Compressed formats @@ -488,5 +493,24 @@ inline const std::string to_string(ResourceBindFlags flags) return s; } +// Manually define the struct that FALCOR_ENUM_INFO(ResourceFormat) would generate so we +// can use the existing table of resource formats. +struct ResourceFormat_info +{ + static fstd::span> items() + { + auto createItems = []() + { + std::vector> items((size_t)ResourceFormat::Count); + for (size_t i = 0; i < (size_t)ResourceFormat::Count; ++i) + items[i] = std::make_pair(ResourceFormat(i), to_string(ResourceFormat(i))); + return items; + }; + static std::vector> items = createItems(); + return items; + } +}; +FALCOR_ENUM_REGISTER(ResourceFormat); + /*! @} */ } // namespace Falcor diff --git a/Source/Falcor/Core/API/GFXAPI.cpp b/Source/Falcor/Core/API/GFXAPI.cpp index 88d0aabc1..10fa8488e 100644 --- a/Source/Falcor/Core/API/GFXAPI.cpp +++ b/Source/Falcor/Core/API/GFXAPI.cpp @@ -28,10 +28,33 @@ #include "GFXAPI.h" #include "Core/ErrorHandling.h" +#if FALCOR_HAS_D3D12 +#include "dxgi.h" +#endif + namespace Falcor { void gfxReportError(const char* msg, gfx::Result result) { - reportFatalError(fmt::format("{}\nResult: {}", msg, result)); + const char* resultStr = nullptr; +#if FALCOR_HAS_D3D12 + switch (result) + { + case DXGI_ERROR_DEVICE_REMOVED: + resultStr = "DXGI_ERROR_DEVICE_REMOVED"; + break; + case DXGI_ERROR_DEVICE_HUNG: + resultStr = "DXGI_ERROR_DEVICE_HUNG"; + break; + case DXGI_ERROR_DEVICE_RESET: + resultStr = "DXGI_ERROR_DEVICE_RESET"; + break; + } +#endif + + if (resultStr) + reportFatalError(fmt::format("GFX ERROR: {}\nResult: {} ({})", msg, result, resultStr)); + else + reportFatalError(fmt::format("GFX ERROR: {}\nResult: {}", msg, result)); } } // namespace Falcor diff --git a/Source/Falcor/Core/API/GFXHelpers.cpp b/Source/Falcor/Core/API/GFXHelpers.cpp index 199340fa4..7c63c1e22 100644 --- a/Source/Falcor/Core/API/GFXHelpers.cpp +++ b/Source/Falcor/Core/API/GFXHelpers.cpp @@ -64,6 +64,8 @@ gfx::Format getGFXFormat(ResourceFormat format) return gfx::Format::BC7_UNORM; case ResourceFormat::BC7UnormSrgb: return gfx::Format::BC7_UNORM_SRGB; + case ResourceFormat::BGRA4Unorm: + return gfx::Format::B4G4R4A4_UNORM; case ResourceFormat::BGRA8Unorm: return gfx::Format::B8G8R8A8_UNORM; case ResourceFormat::BGRA8UnormSrgb: @@ -76,6 +78,8 @@ gfx::Format getGFXFormat(ResourceFormat format) return gfx::Format::D16_UNORM; case ResourceFormat::D32Float: return gfx::Format::D32_FLOAT; + case ResourceFormat::D32FloatS8Uint: + return gfx::Format::D32_FLOAT_S8_UINT; case ResourceFormat::R11G11B10Float: return gfx::Format::R11G11B10_FLOAT; case ResourceFormat::R16Float: @@ -150,6 +154,8 @@ gfx::Format getGFXFormat(ResourceFormat format) return gfx::Format::R16G16B16A16_UINT; case ResourceFormat::RGBA16Unorm: return gfx::Format::R16G16B16A16_UNORM; + case ResourceFormat::RGBA16Snorm: + return gfx::Format::R16G16B16A16_SNORM; case ResourceFormat::RGBA32Float: return gfx::Format::R32G32B32A32_FLOAT; case ResourceFormat::RGBA32Int: diff --git a/Source/Falcor/Core/API/GpuFence.h b/Source/Falcor/Core/API/GpuFence.h index 4c00df7be..5378375f9 100644 --- a/Source/Falcor/Core/API/GpuFence.h +++ b/Source/Falcor/Core/API/GpuFence.h @@ -45,6 +45,7 @@ using CommandQueueHandle = gfx::ICommandQueue*; */ class FALCOR_API GpuFence : public Object { + FALCOR_OBJECT(GpuFence) public: ~GpuFence(); diff --git a/Source/Falcor/Core/API/GpuMemoryHeap.h b/Source/Falcor/Core/API/GpuMemoryHeap.h index 1bbce3998..c03b3b32b 100644 --- a/Source/Falcor/Core/API/GpuMemoryHeap.h +++ b/Source/Falcor/Core/API/GpuMemoryHeap.h @@ -38,6 +38,7 @@ namespace Falcor { class FALCOR_API GpuMemoryHeap : public Object { + FALCOR_OBJECT(GpuMemoryHeap) public: enum class Type { diff --git a/Source/Falcor/Core/API/GpuTimer.cpp b/Source/Falcor/Core/API/GpuTimer.cpp index f94758511..bd3a74671 100644 --- a/Source/Falcor/Core/API/GpuTimer.cpp +++ b/Source/Falcor/Core/API/GpuTimer.cpp @@ -50,7 +50,9 @@ GpuTimer::GpuTimer(ref pDevice) : mpDevice(pDevice) FALCOR_ASSERT(mpDevice); mpResolveBuffer = Buffer::create(mpDevice, sizeof(uint64_t) * 2, Buffer::BindFlags::None, Buffer::CpuAccess::None, nullptr); + mpResolveBuffer->breakStrongReferenceToDevice(); mpResolveStagingBuffer = Buffer::create(mpDevice, sizeof(uint64_t) * 2, Buffer::BindFlags::None, Buffer::CpuAccess::Read, nullptr); + mpResolveStagingBuffer->breakStrongReferenceToDevice(); // Create timestamp query heap upon first use. mStart = mpDevice->getTimestampQueryHeap()->allocate(); diff --git a/Source/Falcor/Core/API/GpuTimer.h b/Source/Falcor/Core/API/GpuTimer.h index a71873afe..f86af10db 100644 --- a/Source/Falcor/Core/API/GpuTimer.h +++ b/Source/Falcor/Core/API/GpuTimer.h @@ -40,6 +40,7 @@ namespace Falcor */ class FALCOR_API GpuTimer : public Object { + FALCOR_OBJECT(GpuTimer) public: /** * Create a new timer object. diff --git a/Source/Falcor/Core/API/GraphicsStateObject.h b/Source/Falcor/Core/API/GraphicsStateObject.h index 77e988c32..aafb6be83 100644 --- a/Source/Falcor/Core/API/GraphicsStateObject.h +++ b/Source/Falcor/Core/API/GraphicsStateObject.h @@ -40,6 +40,7 @@ namespace Falcor { class FALCOR_API GraphicsStateObject : public Object { + FALCOR_OBJECT(GraphicsStateObject) public: static constexpr uint32_t kSampleMaskAll = -1; diff --git a/Source/Falcor/Core/API/LowLevelContextData.cpp b/Source/Falcor/Core/API/LowLevelContextData.cpp index 0407c8311..13bf64d00 100644 --- a/Source/Falcor/Core/API/LowLevelContextData.cpp +++ b/Source/Falcor/Core/API/LowLevelContextData.cpp @@ -91,7 +91,7 @@ void LowLevelContextData::closeCommandBuffer() void LowLevelContextData::openCommandBuffer() { mIsCommandBufferOpen = true; - mGfxCommandBuffer = mpDevice->getCurrentTransientResourceHeap()->createCommandBuffer(); + FALCOR_GFX_CALL(mpDevice->getCurrentTransientResourceHeap()->createCommandBuffer(mGfxCommandBuffer.writeRef())); mpCommandBuffer = mGfxCommandBuffer.get(); } diff --git a/Source/Falcor/Core/API/ParameterBlock.h b/Source/Falcor/Core/API/ParameterBlock.h index 0c7f7481e..ea1cd3131 100644 --- a/Source/Falcor/Core/API/ParameterBlock.h +++ b/Source/Falcor/Core/API/ParameterBlock.h @@ -56,6 +56,7 @@ class CopyContext; */ class FALCOR_API ParameterBlock : public Object { + FALCOR_OBJECT(ParameterBlock) public: ~ParameterBlock(); diff --git a/Source/Falcor/Core/API/QueryHeap.h b/Source/Falcor/Core/API/QueryHeap.h index 3fd0ec6a0..84641c272 100644 --- a/Source/Falcor/Core/API/QueryHeap.h +++ b/Source/Falcor/Core/API/QueryHeap.h @@ -37,6 +37,7 @@ namespace Falcor { class FALCOR_API QueryHeap : public Object { + FALCOR_OBJECT(QueryHeap) public: enum class Type { diff --git a/Source/Falcor/Core/API/RasterizerState.cpp b/Source/Falcor/Core/API/RasterizerState.cpp index d600e479b..00b72f0b9 100644 --- a/Source/Falcor/Core/API/RasterizerState.cpp +++ b/Source/Falcor/Core/API/RasterizerState.cpp @@ -42,10 +42,5 @@ RasterizerState::~RasterizerState() = default; FALCOR_SCRIPT_BINDING(RasterizerState) { pybind11::class_>(m, "RasterizerState"); - - pybind11::enum_ cullMode(m, "CullMode"); - cullMode.value("CullBack", RasterizerState::CullMode::Back); - cullMode.value("CullFront", RasterizerState::CullMode::Front); - cullMode.value("CullNone", RasterizerState::CullMode::None); } } // namespace Falcor diff --git a/Source/Falcor/Core/API/RasterizerState.h b/Source/Falcor/Core/API/RasterizerState.h index 588363315..0ca9572a4 100644 --- a/Source/Falcor/Core/API/RasterizerState.h +++ b/Source/Falcor/Core/API/RasterizerState.h @@ -29,6 +29,8 @@ #include "Handles.h" #include "Core/Macros.h" #include "Core/Object.h" +#include "Core/Enum.h" +#include namespace Falcor { @@ -37,6 +39,7 @@ namespace Falcor */ class FALCOR_API RasterizerState : public Object { + FALCOR_OBJECT(RasterizerState) public: /** * Cull mode @@ -48,6 +51,15 @@ class FALCOR_API RasterizerState : public Object Back, ///< Cull back-facing primitives }; + FALCOR_ENUM_INFO( + CullMode, + { + {CullMode::None, "None"}, + {CullMode::Front, "Front"}, + {CullMode::Back, "Back"}, + } + ); + /** * Polygon fill mode */ @@ -226,4 +238,7 @@ class FALCOR_API RasterizerState : public Object RasterizerState(const Desc& Desc) : mDesc(Desc) {} Desc mDesc; }; + +FALCOR_ENUM_REGISTER(RasterizerState::CullMode); + } // namespace Falcor diff --git a/Source/Falcor/Core/API/Resource.h b/Source/Falcor/Core/API/Resource.h index 890c68936..4c509cb33 100644 --- a/Source/Falcor/Core/API/Resource.h +++ b/Source/Falcor/Core/API/Resource.h @@ -46,6 +46,7 @@ struct ResourceViewInfo; class FALCOR_API Resource : public Object { + FALCOR_OBJECT(Resource) public: using BindFlags = ResourceBindFlags; diff --git a/Source/Falcor/Core/API/ResourceViews.h b/Source/Falcor/Core/API/ResourceViews.h index 02ec45743..0a12f1c03 100644 --- a/Source/Falcor/Core/API/ResourceViews.h +++ b/Source/Falcor/Core/API/ResourceViews.h @@ -74,6 +74,7 @@ struct FALCOR_API ResourceViewInfo */ class FALCOR_API ResourceView : public Object { + FALCOR_OBJECT(ResourceView) public: using Dimension = ReflectionResourceType::Dimensions; static const uint32_t kMaxPossible = -1; diff --git a/Source/Falcor/Core/API/RtAccelerationStructure.h b/Source/Falcor/Core/API/RtAccelerationStructure.h index 749baa51f..db2a5c3a0 100644 --- a/Source/Falcor/Core/API/RtAccelerationStructure.h +++ b/Source/Falcor/Core/API/RtAccelerationStructure.h @@ -180,6 +180,7 @@ struct RtAccelerationStructureBuildInputs */ class FALCOR_API RtAccelerationStructure : public Object { + FALCOR_OBJECT(RtAccelerationStructure) public: class FALCOR_API Desc { diff --git a/Source/Falcor/Core/API/RtAccelerationStructurePostBuildInfoPool.h b/Source/Falcor/Core/API/RtAccelerationStructurePostBuildInfoPool.h index 233ed7b8a..7f817b0dc 100644 --- a/Source/Falcor/Core/API/RtAccelerationStructurePostBuildInfoPool.h +++ b/Source/Falcor/Core/API/RtAccelerationStructurePostBuildInfoPool.h @@ -42,6 +42,7 @@ enum class RtAccelerationStructurePostBuildInfoQueryType class FALCOR_API RtAccelerationStructurePostBuildInfoPool : public Object { + FALCOR_OBJECT(RtAccelerationStructurePostBuildInfoPool) public: struct Desc { diff --git a/Source/Falcor/Core/API/RtStateObject.cpp b/Source/Falcor/Core/API/RtStateObject.cpp index 8e4a39026..5300ba127 100644 --- a/Source/Falcor/Core/API/RtStateObject.cpp +++ b/Source/Falcor/Core/API/RtStateObject.cpp @@ -48,14 +48,14 @@ RtStateObject::RtStateObject(ref pDevice, const Desc& desc) : mpDevice(p { if (pEntryPointGroup->getType() == EntryPointGroupKernels::Type::RtHitGroup) { - const Shader* pIntersection = pEntryPointGroup->getShader(ShaderType::Intersection); - const Shader* pAhs = pEntryPointGroup->getShader(ShaderType::AnyHit); - const Shader* pChs = pEntryPointGroup->getShader(ShaderType::ClosestHit); + const EntryPointKernel* pIntersection = pEntryPointGroup->getKernel(ShaderType::Intersection); + const EntryPointKernel* pAhs = pEntryPointGroup->getKernel(ShaderType::AnyHit); + const EntryPointKernel* pChs = pEntryPointGroup->getKernel(ShaderType::ClosestHit); gfx::HitGroupDesc hitgroupDesc = {}; - hitgroupDesc.anyHitEntryPoint = pAhs ? pAhs->getEntryPoint().c_str() : nullptr; - hitgroupDesc.closestHitEntryPoint = pChs ? pChs->getEntryPoint().c_str() : nullptr; - hitgroupDesc.intersectionEntryPoint = pIntersection ? pIntersection->getEntryPoint().c_str() : nullptr; + hitgroupDesc.anyHitEntryPoint = pAhs ? pAhs->getEntryPointName().c_str() : nullptr; + hitgroupDesc.closestHitEntryPoint = pChs ? pChs->getEntryPointName().c_str() : nullptr; + hitgroupDesc.intersectionEntryPoint = pIntersection ? pIntersection->getEntryPointName().c_str() : nullptr; hitgroupDesc.hitGroupName = pEntryPointGroup->getExportName().c_str(); hitGroups.push_back(hitgroupDesc); } diff --git a/Source/Falcor/Core/API/RtStateObject.h b/Source/Falcor/Core/API/RtStateObject.h index 242f40548..c446c8431 100644 --- a/Source/Falcor/Core/API/RtStateObject.h +++ b/Source/Falcor/Core/API/RtStateObject.h @@ -39,6 +39,7 @@ namespace Falcor { class FALCOR_API RtStateObject : public Object { + FALCOR_OBJECT(RtStateObject) public: struct Desc { diff --git a/Source/Falcor/Core/API/Sampler.cpp b/Source/Falcor/Core/API/Sampler.cpp index f71f8bcbd..142f7e542 100644 --- a/Source/Falcor/Core/API/Sampler.cpp +++ b/Source/Falcor/Core/API/Sampler.cpp @@ -153,16 +153,5 @@ void Sampler::breakStrongReferenceToDevice() FALCOR_SCRIPT_BINDING(Sampler) { pybind11::class_>(m, "Sampler"); - - pybind11::enum_ filter(m, "SamplerFilter"); - filter.value("Linear", Sampler::Filter::Linear); - filter.value("Point", Sampler::Filter::Point); - - pybind11::enum_ addressMode(m, "AddressMode"); - addressMode.value("Wrap", Sampler::AddressMode::Wrap); - addressMode.value("Mirror", Sampler::AddressMode::Mirror); - addressMode.value("Clamp", Sampler::AddressMode::Clamp); - addressMode.value("Border", Sampler::AddressMode::Border); - addressMode.value("MirrorOnce", Sampler::AddressMode::MirrorOnce); } } // namespace Falcor diff --git a/Source/Falcor/Core/API/Sampler.h b/Source/Falcor/Core/API/Sampler.h index be279771c..ef2d4ed54 100644 --- a/Source/Falcor/Core/API/Sampler.h +++ b/Source/Falcor/Core/API/Sampler.h @@ -32,6 +32,7 @@ #include "NativeHandle.h" #include "Core/Macros.h" #include "Core/Object.h" +#include "Core/Enum.h" #include "Utils/Math/Vector.h" namespace Falcor @@ -41,6 +42,7 @@ namespace Falcor */ class FALCOR_API Sampler : public Object { + FALCOR_OBJECT(Sampler) public: /** * Filter mode @@ -51,6 +53,14 @@ class FALCOR_API Sampler : public Object Linear, }; + FALCOR_ENUM_INFO( + Filter, + { + {Filter::Point, "Point"}, + {Filter::Linear, "Linear"}, + } + ); + /** * Addressing mode in case the texture coordinates are out of [0, 1] range */ @@ -63,6 +73,17 @@ class FALCOR_API Sampler : public Object MirrorOnce ///< Same as Mirror, but mirrors only once around 0 }; + FALCOR_ENUM_INFO( + AddressMode, + { + {AddressMode::Wrap, "Wrap"}, + {AddressMode::Mirror, "Mirror"}, + {AddressMode::Clamp, "Clamp"}, + {AddressMode::Border, "Border"}, + {AddressMode::MirrorOnce, "MirrorOnce"}, + } + ); + /** * Reduction mode */ @@ -74,6 +95,16 @@ class FALCOR_API Sampler : public Object Max, }; + FALCOR_ENUM_INFO( + ReductionMode, + { + {ReductionMode::Standard, "Standard"}, + {ReductionMode::Comparison, "Comparison"}, + {ReductionMode::Min, "Min"}, + {ReductionMode::Max, "Max"}, + } + ); + /** * Comparison mode for the sampler. */ @@ -295,4 +326,9 @@ class FALCOR_API Sampler : public Object friend class Device; }; + +FALCOR_ENUM_REGISTER(Sampler::Filter); +FALCOR_ENUM_REGISTER(Sampler::AddressMode); +FALCOR_ENUM_REGISTER(Sampler::ReductionMode); + } // namespace Falcor diff --git a/Source/Falcor/Core/API/Shader.cpp b/Source/Falcor/Core/API/Shader.cpp deleted file mode 100644 index 3d39cd105..000000000 --- a/Source/Falcor/Core/API/Shader.cpp +++ /dev/null @@ -1,92 +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 "Shader.h" -#include "Device.h" -#include "GFXAPI.h" - -namespace Falcor -{ -struct ShaderData -{ - Shader::Blob pBlob; - Slang::ComPtr pLinkedSlangEntryPoint; - ISlangBlob* getBlob() - { - if (!pBlob) - { - Slang::ComPtr pSlangBlob; - Slang::ComPtr pDiagnostics; - - if (SLANG_FAILED(pLinkedSlangEntryPoint->getEntryPointCode(0, 0, pSlangBlob.writeRef(), pDiagnostics.writeRef()))) - { - throw RuntimeError(std::string("Shader compilation failed. \n") + (const char*)pDiagnostics->getBufferPointer()); - } - pBlob = Shader::Blob(pSlangBlob.get()); - } - return pBlob.get(); - } -}; - -Shader::Shader(ShaderType type) : mType(type) -{ - mpPrivateData = std::make_unique(); -} - -Shader::~Shader() {} - -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 - // are known, which is right before a draw/dispatch command is issued, and this is done - // internally within GFX. - // The `Shader` implementation here serves as a helper utility for application code that - // uses raw graphics API to get shader kernel code from an ordinary slang source. - // Since most users/render-passes do not need to get shader kernel code, we defer - // the call to slang's `getEntryPointCode` function until it is actually needed. - // to avoid redundant shader compiler invocation. - mpPrivateData->pBlob = nullptr; - mpPrivateData->pLinkedSlangEntryPoint = slangEntryPoint; - return slangEntryPoint != nullptr; -} - -Shader::BlobData Shader::getBlobData() const -{ - auto blob = mpPrivateData->getBlob(); - - BlobData result; - result.data = blob->getBufferPointer(); - result.size = blob->getBufferSize(); - return result; -} -} // namespace Falcor diff --git a/Source/Falcor/Core/API/Shader.h b/Source/Falcor/Core/API/Shader.h deleted file mode 100644 index 39a74a10a..000000000 --- a/Source/Falcor/Core/API/Shader.h +++ /dev/null @@ -1,318 +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 "Handles.h" -#include "Core/Macros.h" -#include "Core/Assert.h" -#include "Core/Object.h" - -#include -#include -#include -#include -#include // std::nullptr_t - -struct ISlangBlob; - -namespace Falcor -{ -/** - * Falcor shader types - */ -enum class ShaderType -{ - Vertex, ///< Vertex shader - Pixel, ///< Pixel shader - Geometry, ///< Geometry shader - Hull, ///< Hull shader (AKA Tessellation control shader) - Domain, ///< Domain shader (AKA Tessellation evaluation shader) - Compute, ///< Compute shader - - RayGeneration, ///< Ray generation shader - Intersection, ///< Intersection shader - AnyHit, ///< Any hit shader - ClosestHit, ///< Closest hit shader - Miss, ///< Miss shader - Callable, ///< Callable shader - Count ///< Shader Type count -}; - -/** - * Converts ShaderType enum elements to a string. - * @param[in] type Type to convert to string - * @return Shader type as a string - */ -inline const std::string to_string(ShaderType type) -{ - switch (type) - { - case ShaderType::Vertex: - return "vertex"; - case ShaderType::Pixel: - return "pixel"; - case ShaderType::Hull: - return "hull"; - case ShaderType::Domain: - return "domain"; - case ShaderType::Geometry: - return "geometry"; - case ShaderType::Compute: - return "compute"; - case ShaderType::RayGeneration: - return "raygeneration"; - case ShaderType::Intersection: - return "intersection"; - case ShaderType::AnyHit: - return "anyhit"; - case ShaderType::ClosestHit: - return "closesthit"; - case ShaderType::Miss: - return "miss"; - case ShaderType::Callable: - return "callable"; - default: - FALCOR_UNREACHABLE(); - return ""; - } -} - -/** - * Forward declaration of backend implementation-specific Shader data. - */ -struct ShaderData; - -/** - * Low-level shader object - * This class abstracts the API's shader creation and management - */ -class FALCOR_API Shader : public Object -{ -public: - typedef Slang::ComPtr Blob; - - enum class CompilerFlags - { - None = 0x0, - TreatWarningsAsErrors = 0x1, - DumpIntermediates = 0x2, - FloatingPointModeFast = 0x4, - FloatingPointModePrecise = 0x8, - GenerateDebugInfo = 0x10, - MatrixLayoutColumnMajor = 0x20, // Falcor is using row-major, use this only to compile stand-alone external shaders. - }; - - struct BlobData - { - const void* data; - size_t size; - }; - - class DefineList : public std::map - { - public: - /** - * Adds a macro definition. If the macro already exists, it will be replaced. - * @param[in] name The name of macro. - * @param[in] value Optional. The value of the macro. - * @return The updated list of macro definitions. - */ - DefineList& add(const std::string& name, const std::string& val = "") - { - (*this)[name] = val; - return *this; - } - - /** - * Removes a macro definition. If the macro doesn't exist, the call will be silently ignored. - * @param[in] name The name of macro. - * @return The updated list of macro definitions. - */ - DefineList& remove(const std::string& name) - { - (*this).erase(name); - return *this; - } - - /** - * Add a define list to the current list - */ - DefineList& add(const DefineList& dl) - { - for (const auto& p : dl) - add(p.first, p.second); - return *this; - } - - /** - * Remove a define list from the current list - */ - DefineList& remove(const DefineList& dl) - { - for (const auto& p : dl) - remove(p.first); - return *this; - } - - DefineList() = default; - DefineList(std::initializer_list> il) : std::map(il) {} - }; - - /** - * Representing a shader implementation of an interface. - * When linked into a `ProgramVersion`, the specialized shader will contain - * the implementation of the specified type in a dynamic dispatch function. - */ - struct TypeConformance - { - std::string mTypeName; - std::string mInterfaceName; - TypeConformance() = default; - TypeConformance(const std::string& typeName, const std::string& interfaceName) : mTypeName(typeName), mInterfaceName(interfaceName) - {} - bool operator<(const TypeConformance& other) const - { - return mTypeName < other.mTypeName || (mTypeName == other.mTypeName && mInterfaceName < other.mInterfaceName); - } - bool operator==(const TypeConformance& other) const - { - return mTypeName == other.mTypeName && mInterfaceName == other.mInterfaceName; - } - struct HashFunction - { - size_t operator()(const TypeConformance& conformance) const - { - size_t hash = std::hash()(conformance.mTypeName); - hash = hash ^ std::hash()(conformance.mInterfaceName); - return hash; - } - }; - }; - - class TypeConformanceList : public std::map - { - public: - /** - * Adds a type conformance. If the type conformance exists, it will be replaced. - * @param[in] typeName The name of the implementation type. - * @param[in] interfaceName The name of the interface type. - * @param[in] id Optional. The id representing the implementation type for this interface. If it is -1, Slang will automatically - * assign a unique Id for the type. - * @return The updated list of type conformances. - */ - TypeConformanceList& add(const std::string& typeName, const std::string& interfaceName, uint32_t id = -1) - { - (*this)[TypeConformance(typeName, interfaceName)] = id; - return *this; - } - - /** - * Removes a type conformance. If the type conformance doesn't exist, the call will be silently ignored. - * @param[in] typeName The name of the implementation type. - * @param[in] interfaceName The name of the interface type. - * @return The updated list of type conformances. - */ - TypeConformanceList& remove(const std::string& typeName, const std::string& interfaceName) - { - (*this).erase(TypeConformance(typeName, interfaceName)); - return *this; - } - - /** - * Add a type conformance list to the current list - */ - TypeConformanceList& add(const TypeConformanceList& cl) - { - for (const auto& p : cl) - add(p.first.mTypeName, p.first.mInterfaceName, p.second); - return *this; - } - - /** - * Remove a type conformance list from the current list - */ - TypeConformanceList& remove(const TypeConformanceList& cl) - { - for (const auto& p : cl) - remove(p.first.mTypeName, p.first.mInterfaceName); - return *this; - } - - TypeConformanceList() = default; - TypeConformanceList(std::initializer_list> il) : std::map(il) - {} - }; - - /** - * Create a shader object - * @param[in] linkedSlangEntryPoint The Slang IComponentType that defines the shader entry point. - * @param[in] type The Type of the 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 ref create( - Slang::ComPtr linkedSlangEntryPoint, - ShaderType type, - const std::string& entryPointName, - CompilerFlags flags, - std::string& log - ) - { - ref pShader = ref(new Shader(type)); - pShader->mEntryPointName = entryPointName; - return pShader->init(linkedSlangEntryPoint, entryPointName, flags, log) ? pShader : nullptr; - } - - virtual ~Shader(); - - /** - * Get the shader Type - */ - ShaderType getType() const { return mType; } - - /** - * Get the name of the entry point. - */ - const std::string& getEntryPoint() const { return mEntryPointName; } - - BlobData getBlobData() const; - -protected: - // API handle depends on the shader Type, so it stored be stored as part of the private data - bool init( - Slang::ComPtr linkedSlangEntryPoint, - const std::string& entryPointName, - CompilerFlags flags, - std::string& log - ); - Shader(ShaderType Type); - ShaderType mType; - std::string mEntryPointName; - std::unique_ptr mpPrivateData; -}; -FALCOR_ENUM_CLASS_OPERATORS(Shader::CompilerFlags); -} // namespace Falcor diff --git a/Source/Falcor/Core/API/ShaderType.h b/Source/Falcor/Core/API/ShaderType.h new file mode 100644 index 000000000..2dba23103 --- /dev/null +++ b/Source/Falcor/Core/API/ShaderType.h @@ -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. + **************************************************************************/ +#pragma once +#include "Core/Macros.h" +#include "Core/Assert.h" + +#include + +namespace Falcor +{ +/** + * Falcor shader types + */ +enum class ShaderType +{ + Vertex, ///< Vertex shader + Pixel, ///< Pixel shader + Geometry, ///< Geometry shader + Hull, ///< Hull shader (AKA Tessellation control shader) + Domain, ///< Domain shader (AKA Tessellation evaluation shader) + Compute, ///< Compute shader + + RayGeneration, ///< Ray generation shader + Intersection, ///< Intersection shader + AnyHit, ///< Any hit shader + ClosestHit, ///< Closest hit shader + Miss, ///< Miss shader + Callable, ///< Callable shader + Count ///< Shader Type count +}; + +/** + * Converts ShaderType enum elements to a string. + * @param[in] type Type to convert to string + * @return Shader type as a string + */ +inline const std::string to_string(ShaderType type) +{ + switch (type) + { + case ShaderType::Vertex: + return "vertex"; + case ShaderType::Pixel: + return "pixel"; + case ShaderType::Hull: + return "hull"; + case ShaderType::Domain: + return "domain"; + case ShaderType::Geometry: + return "geometry"; + case ShaderType::Compute: + return "compute"; + case ShaderType::RayGeneration: + return "raygeneration"; + case ShaderType::Intersection: + return "intersection"; + case ShaderType::AnyHit: + return "anyhit"; + case ShaderType::ClosestHit: + return "closesthit"; + case ShaderType::Miss: + return "miss"; + case ShaderType::Callable: + return "callable"; + default: + FALCOR_UNREACHABLE(); + return ""; + } +} + +} // namespace Falcor diff --git a/Source/Falcor/Core/API/Shared/D3D12ConstantBufferView.h b/Source/Falcor/Core/API/Shared/D3D12ConstantBufferView.h index 264b3ecdb..22bbfdecd 100644 --- a/Source/Falcor/Core/API/Shared/D3D12ConstantBufferView.h +++ b/Source/Falcor/Core/API/Shared/D3D12ConstantBufferView.h @@ -40,6 +40,7 @@ namespace Falcor // that wish to use the raw D3D12DescriptorSet API. class FALCOR_API D3D12ConstantBufferView : public Object { + FALCOR_OBJECT(D3D12ConstantBufferView) public: static ref create(ref pDevice, ref pBuffer); static ref create(ref pDevice); diff --git a/Source/Falcor/Core/API/Shared/D3D12DescriptorHeap.h b/Source/Falcor/Core/API/Shared/D3D12DescriptorHeap.h index 64e39d665..0b3ff7ceb 100644 --- a/Source/Falcor/Core/API/Shared/D3D12DescriptorHeap.h +++ b/Source/Falcor/Core/API/Shared/D3D12DescriptorHeap.h @@ -40,6 +40,7 @@ namespace Falcor { class D3D12DescriptorHeap : public Object { + FALCOR_OBJECT(D3D12DescriptorHeap) public: using ApiHandle = ID3D12DescriptorHeapPtr; using CpuHandle = D3D12_CPU_DESCRIPTOR_HANDLE; diff --git a/Source/Falcor/Core/API/Shared/D3D12DescriptorPool.h b/Source/Falcor/Core/API/Shared/D3D12DescriptorPool.h index c9b157b55..0ce0f6c7c 100644 --- a/Source/Falcor/Core/API/Shared/D3D12DescriptorPool.h +++ b/Source/Falcor/Core/API/Shared/D3D12DescriptorPool.h @@ -41,6 +41,7 @@ struct DescriptorSetApiData; class FALCOR_API D3D12DescriptorPool : public Object { + FALCOR_OBJECT(D3D12DescriptorPool) public: using ApiHandle = ID3D12DescriptorHeapPtr; using CpuHandle = D3D12_CPU_DESCRIPTOR_HANDLE; diff --git a/Source/Falcor/Core/API/Shared/D3D12DescriptorSet.h b/Source/Falcor/Core/API/Shared/D3D12DescriptorSet.h index c1522a37a..1af656a80 100644 --- a/Source/Falcor/Core/API/Shared/D3D12DescriptorSet.h +++ b/Source/Falcor/Core/API/Shared/D3D12DescriptorSet.h @@ -31,7 +31,7 @@ #include "Core/Macros.h" #include "Core/Object.h" #include "Core/API/fwd.h" -#include "Core/API/Shader.h" +#include "Core/API/ShaderResourceType.h" #include #include @@ -54,6 +54,7 @@ enum class D3D12DescriptorSetBindingUsage class FALCOR_API D3D12DescriptorSet : public Object { + FALCOR_OBJECT(D3D12DescriptorSet) public: using Type = ShaderResourceType; using CpuHandle = D3D12DescriptorPool::CpuHandle; diff --git a/Source/Falcor/Core/API/Shared/D3D12DescriptorSetLayout.h b/Source/Falcor/Core/API/Shared/D3D12DescriptorSetLayout.h index 00a1e2403..795b8f838 100644 --- a/Source/Falcor/Core/API/Shared/D3D12DescriptorSetLayout.h +++ b/Source/Falcor/Core/API/Shared/D3D12DescriptorSetLayout.h @@ -28,7 +28,7 @@ #pragma once #include "Core/Macros.h" -#include "Core/API/Shader.h" +#include "Core/API/ShaderType.h" #include "Core/API/ShaderResourceType.h" #include diff --git a/Source/Falcor/Core/API/Shared/D3D12RootSignature.h b/Source/Falcor/Core/API/Shared/D3D12RootSignature.h index 86cdeaefe..8ca0fba19 100644 --- a/Source/Falcor/Core/API/Shared/D3D12RootSignature.h +++ b/Source/Falcor/Core/API/Shared/D3D12RootSignature.h @@ -57,6 +57,7 @@ class CopyContext; */ class FALCOR_API D3D12RootSignature : public Object { + FALCOR_OBJECT(D3D12RootSignature) public: using ApiHandle = ID3D12RootSignaturePtr; using DescType = ShaderResourceType; diff --git a/Source/Falcor/Core/API/Swapchain.h b/Source/Falcor/Core/API/Swapchain.h index 09c6698d7..5f44597ab 100644 --- a/Source/Falcor/Core/API/Swapchain.h +++ b/Source/Falcor/Core/API/Swapchain.h @@ -37,6 +37,7 @@ namespace Falcor { class FALCOR_API Swapchain : public Object { + FALCOR_OBJECT(Swapchain) public: struct Desc { diff --git a/Source/Falcor/Core/API/Texture.cpp b/Source/Falcor/Core/API/Texture.cpp index 176c2809d..a0cac0f59 100644 --- a/Source/Falcor/Core/API/Texture.cpp +++ b/Source/Falcor/Core/API/Texture.cpp @@ -750,7 +750,7 @@ void Texture::apiInit(const void* pData, bool autoGenMips) FALCOR_ASSERT(desc.numMipLevels > 0 && desc.size.depth > 0 && desc.arraySize > 0 && desc.sampleDesc.numSamples > 0); // create resource - mGfxTextureResource = mpDevice->getGfxDevice()->createTextureResource(desc, nullptr); + FALCOR_GFX_CALL(mpDevice->getGfxDevice()->createTextureResource(desc, nullptr, mGfxTextureResource.writeRef())); FALCOR_ASSERT(mGfxTextureResource); // upload init data through texture class diff --git a/Source/Falcor/Core/API/Texture.h b/Source/Falcor/Core/API/Texture.h index 6291464f6..2bb1452f4 100644 --- a/Source/Falcor/Core/API/Texture.h +++ b/Source/Falcor/Core/API/Texture.h @@ -46,6 +46,7 @@ class RenderContext; */ class FALCOR_API Texture : public Resource { + FALCOR_OBJECT(Texture) public: ~Texture(); diff --git a/Source/Falcor/Core/API/VAO.h b/Source/Falcor/Core/API/VAO.h index c653eea96..252de2f10 100644 --- a/Source/Falcor/Core/API/VAO.h +++ b/Source/Falcor/Core/API/VAO.h @@ -42,6 +42,7 @@ namespace Falcor */ class FALCOR_API Vao : public Object { + FALCOR_OBJECT(Vao) public: ~Vao() = default; diff --git a/Source/Falcor/Core/API/VertexLayout.h b/Source/Falcor/Core/API/VertexLayout.h index eff13ab87..223df6b48 100644 --- a/Source/Falcor/Core/API/VertexLayout.h +++ b/Source/Falcor/Core/API/VertexLayout.h @@ -38,6 +38,7 @@ namespace Falcor */ class FALCOR_API VertexBufferLayout : public Object { + FALCOR_OBJECT(VertexBufferLayout) public: enum class InputClass { @@ -155,6 +156,7 @@ class FALCOR_API VertexBufferLayout : public Object */ class VertexLayout : public Object { + FALCOR_OBJECT(VertexLayout) public: /** * Create a new vertex layout object. diff --git a/Source/Falcor/Core/Enum.h b/Source/Falcor/Core/Enum.h new file mode 100644 index 000000000..959594e01 --- /dev/null +++ b/Source/Falcor/Core/Enum.h @@ -0,0 +1,188 @@ +/*************************************************************************** + # 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 "Errors.h" +#include "Utils/Logger.h" +#include +#include +#include +#include +#include +#include +#include + +namespace Falcor +{ +// Helper using ADL to find EnumInfo in other namespaces. +template +using EnumInfo = decltype(falcorFindEnumInfoADL(std::declval())); + +template +struct has_enum_info : std::false_type +{}; + +template +struct has_enum_info::items)>> : std::true_type +{}; + +template +inline constexpr bool has_enum_info_v = has_enum_info::value; + +/** + * Convert an enum value to a string. + * Throws if the enum value is not found in the registered enum information. + */ +template, bool> = true> +inline const std::string& enumToString(T value) +{ + const auto& items = EnumInfo::items(); + auto it = std::find_if(items.begin(), items.end(), [value](const auto& item) { return item.first == value; }); + if (it == items.end()) + throw RuntimeError("Invalid enum value {}", int(value)); + return it->second; +} + +/** + * Convert a string to an enum value. + * Throws if the string is not found in the registered enum information. + */ +template, bool> = true> +inline T stringToEnum(std::string_view name) +{ + const auto& items = EnumInfo::items(); + auto it = std::find_if(items.begin(), items.end(), [name](const auto& item) { return item.second == name; }); + if (it == items.end()) + throw RuntimeError("Invalid enum name '{}'", name); + return it->first; +} + +/** + * Check if an enum has a value with the given name. + */ +template, bool> = true> +inline bool enumHasValue(std::string_view name) +{ + const auto& items = EnumInfo::items(); + auto it = std::find_if(items.begin(), items.end(), [name](const auto& item) { return item.second == name; }); + return it != items.end(); +} + +/** + * Convert an flags enum value to a list of strings. + * Throws if any of the flags are not found in the registered enum information. + */ +template, bool> = true> +inline std::vector flagsToStringList(T flags) +{ + std::vector list; + const auto& items = EnumInfo::items(); + for (const auto& item : items) + { + if (is_set(flags, item.first)) + { + list.push_back(item.second); + flip_bit(flags, item.first); + } + } + if (flags != T(0)) + throw RuntimeError("Invalid enum flags value {}", int(flags)); + return list; +} + +/** + * Convert a list of strings to a flags enum value. + * Throws if any of the strings are not found in the registered enum information. + */ +template, bool> = true> +inline T stringListToFlags(const std::vector& list) +{ + T flags = T(0); + for (const auto& name : list) + flags |= stringToEnum(name); + return flags; +} + +} // namespace Falcor + +/** + * Define enum information. This is expected to be used as follows: + * + * enum class Foo { A, B, C }; + * FALCOR_ENUM_INFO(Foo, { + * { Foo::A, "A" }, + * { Foo::B, "B" }, + * { Foo::C, "C" }, + * }) + */ +#define FALCOR_ENUM_INFO(T, ...) \ + struct T##_info \ + { \ + static fstd::span> items() \ + { \ + static std::pair items[] = __VA_ARGS__; \ + return {std::begin(items), std::end(items)}; \ + } \ + }; + +/** + * Register enum information to be used with helper functions. + * This needs to be placed outside of any structs but within the + * namespace of the enum: + * + * namespace ns + * { + * struct Bar + * { + * enum class Foo { A, B, C }; + * FALCOR_ENUM_INFO(Foo, ...) + * }; + * + * FALCOR_ENUM_REGISTER(Bar::Foo) + * } // namespace ns + * + * Registered enums can be converted to/from strings using: + * - enumToString(Enum value) + * - stringToEnum(std::string_view name) + */ +#define FALCOR_ENUM_REGISTER(T) \ + constexpr T##_info falcorFindEnumInfoADL [[maybe_unused]] (T) noexcept \ + { \ + return T##_info{}; \ + } + +/// Enum formatter. +template +struct fmt::formatter, char>> : formatter +{ + template + auto format(const T& e, FormatContext& ctx) + { + return formatter::format(Falcor::enumToString(e), ctx); + } +}; diff --git a/Source/Falcor/Core/ErrorHandling.cpp b/Source/Falcor/Core/ErrorHandling.cpp index ec657a65d..16d725d5a 100644 --- a/Source/Falcor/Core/ErrorHandling.cpp +++ b/Source/Falcor/Core/ErrorHandling.cpp @@ -28,6 +28,7 @@ #include "ErrorHandling.h" #include "Platform/OS.h" #include "Utils/Logger.h" +#include "API/Aftermath.h" #include namespace Falcor @@ -126,6 +127,11 @@ void reportErrorAndAllowRetry(const std::string& msg) if (showStackTrace) extendedMsg += "\n\nStacktrace:\n" + getStackTrace(3); +#if FALCOR_HAS_AFTERMATH + if (!waitForAftermathDumps()) + extendedMsg += "\n\nAftermath GPU crash dump generation failed.\n\n"; +#endif + logFatal(extendedMsg); if (sShowMessageBoxOnError) diff --git a/Source/Falcor/Core/Object.cpp b/Source/Falcor/Core/Object.cpp index e4ee8dbdf..4c8420ce7 100644 --- a/Source/Falcor/Core/Object.cpp +++ b/Source/Falcor/Core/Object.cpp @@ -29,18 +29,26 @@ #include "Assert.h" #include "ErrorHandling.h" #include +#include namespace Falcor { -#if FALCOR_ENABLE_REF_TRACKING +#if FALCOR_ENABLE_OBJECT_TRACKING static std::mutex sTrackedObjectsMutex; static std::set sTrackedObjects; #endif void Object::incRef() const { - ++mRefCount; + uint32_t refCount = mRefCount.fetch_add(1); +#if FALCOR_ENABLE_OBJECT_TRACKING + if (refCount == 0) + { + std::lock_guard lock(sTrackedObjectsMutex); + sTrackedObjects.insert(this); + } +#endif } void Object::decRef(bool dealloc) const noexcept @@ -50,19 +58,43 @@ void Object::decRef(bool dealloc) const noexcept { reportFatalError("Internal error: Object reference count < 0!"); } - else if (refCount == 1 && dealloc) + else if (refCount == 1) { -#if FALCOR_ENABLE_REF_TRACKING - if (mEnableRefTracking) +#if FALCOR_ENABLE_OBJECT_TRACKING { std::lock_guard lock(sTrackedObjectsMutex); sTrackedObjects.erase(this); } #endif - delete this; + if (dealloc) + delete this; } } +#if FALCOR_ENABLE_OBJECT_TRACKING + +void Object::dumpAliveObjects() +{ + std::lock_guard lock(sTrackedObjectsMutex); + logInfo("Alive objects:"); + for (const Object* object : sTrackedObjects) + object->dumpRefs(); +} + +void Object::dumpRefs() const +{ + logInfo("Object (class={} address={}) has {} reference(s)", getClassName(), fmt::ptr(this), refCount()); +#if FALCOR_ENABLE_REF_TRACKING + std::lock_guard lock(mRefTrackerMutex); + for (const auto& it : mRefTrackers) + { + logInfo("ref={} count={}\n{}\n", it.first, it.second.count, it.second.origin); + } +#endif +} + +#endif // FALCOR_ENABLE_OBJECT_TRACKING + #if FALCOR_ENABLE_REF_TRACKING void Object::incRef(uint64_t refId) const @@ -102,32 +134,7 @@ void Object::decRef(uint64_t refId, bool dealloc) const noexcept 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(); + mEnableRefTracking = enable; } #endif // FALCOR_ENABLE_REF_TRACKING diff --git a/Source/Falcor/Core/Object.h b/Source/Falcor/Core/Object.h index 68a776816..c53751c66 100644 --- a/Source/Falcor/Core/Object.h +++ b/Source/Falcor/Core/Object.h @@ -29,23 +29,32 @@ #include "Core/Macros.h" -#include +#include #include #include -#include #include +/** + * Enable/disable object lifetime tracking. + * When enabled, each object that derives from Object will have its + * lifetime tracked. This is useful for debugging memory leaks. + */ +#define FALCOR_ENABLE_OBJECT_TRACKING 0 + /** * Enable/disable reference tracking. - * When enabled, the reference count of each object that has tracking - * enabled using setEnableRefTracking() is tracked. Each time the reference + * When enabled, all references to object that have reference tracking + * enabled using setEnableRefTracking() are 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 +#if !FALCOR_ENABLE_OBJECT_TRACKING +#error "FALCOR_ENABLE_REF_TRACKING requires FALCOR_ENABLE_OBJECT_TRACKING" +#endif #include #include #endif @@ -103,6 +112,10 @@ class FALCOR_API Object /// Destructor. virtual ~Object() = default; + /// Return the name of the class. + /// Note: This reports the actual class name if FALCOR_OBJECT() is used. + virtual const char* getClassName() const { return "Object"; } + /// Return the current reference count. int refCount() const { return mRefCount; }; @@ -112,18 +125,20 @@ class FALCOR_API Object /// Decrease the reference count of the object and possibly deallocate it. void decRef(bool dealloc = true) const noexcept; +#if FALCOR_ENABLE_OBJECT_TRACKING + /// Dump all objects that are currently alive. + static void dumpAliveObjects(); + + /// Dump references of this object. + void dumpRefs() const; +#endif + #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: @@ -150,6 +165,14 @@ static uint64_t nextRefId() } #endif +/// Macro to declare the object class name. +#define FALCOR_OBJECT(class_) \ +public: \ + const char* getClassName() const override \ + { \ + return #class_; \ + } + /** * @brief Reference counting helper. * @@ -395,8 +418,6 @@ class ref #endif } - friend std::ostream& operator<<(std::ostream& os, const ref& r) { return os << r.mPtr; } - private: inline void incRef(const Object* object) { @@ -481,8 +502,6 @@ class BreakableReference void breakStrongReference() { mStrongRef.reset(); } - friend std::ostream& operator<<(std::ostream& os, const BreakableReference& r) { return os << r.get(); } - private: ref mStrongRef; T* mWeakRef = nullptr; @@ -490,6 +509,26 @@ class BreakableReference } // namespace Falcor +template +struct fmt::formatter> : formatter +{ + template + auto format(const Falcor::ref& ref, FormatContext& ctx) const + { + return formatter::format(ref.get(), ctx); + } +}; + +template +struct fmt::formatter> : formatter +{ + template + auto format(const Falcor::BreakableReference& ref, FormatContext& ctx) const + { + return formatter::format(ref.get(), ctx); + } +}; + namespace std { template diff --git a/Source/Falcor/Core/Pass/BaseGraphicsPass.cpp b/Source/Falcor/Core/Pass/BaseGraphicsPass.cpp index ef3fe7967..f8837a7c9 100644 --- a/Source/Falcor/Core/Pass/BaseGraphicsPass.cpp +++ b/Source/Falcor/Core/Pass/BaseGraphicsPass.cpp @@ -29,8 +29,7 @@ namespace Falcor { -BaseGraphicsPass::BaseGraphicsPass(ref pDevice, const Program::Desc& progDesc, const Program::DefineList& programDefines) - : mpDevice(pDevice) +BaseGraphicsPass::BaseGraphicsPass(ref pDevice, const Program::Desc& progDesc, const DefineList& programDefines) : mpDevice(pDevice) { auto pProg = GraphicsProgram::create(mpDevice, progDesc, programDefines); pProg->breakStrongReferenceToDevice(); diff --git a/Source/Falcor/Core/Pass/BaseGraphicsPass.h b/Source/Falcor/Core/Pass/BaseGraphicsPass.h index 804410603..a38bf6384 100644 --- a/Source/Falcor/Core/Pass/BaseGraphicsPass.h +++ b/Source/Falcor/Core/Pass/BaseGraphicsPass.h @@ -39,6 +39,7 @@ namespace Falcor { class FALCOR_API BaseGraphicsPass : public Object { + FALCOR_OBJECT(BaseGraphicsPass) public: virtual ~BaseGraphicsPass() = default; @@ -86,7 +87,7 @@ class FALCOR_API BaseGraphicsPass : public Object * @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); + BaseGraphicsPass(ref pDevice, const Program::Desc& progDesc, const DefineList& programDefines); BreakableReference mpDevice; ref mpVars; diff --git a/Source/Falcor/Core/Pass/ComputePass.cpp b/Source/Falcor/Core/Pass/ComputePass.cpp index 933c67499..7aab07848 100644 --- a/Source/Falcor/Core/Pass/ComputePass.cpp +++ b/Source/Falcor/Core/Pass/ComputePass.cpp @@ -31,8 +31,7 @@ namespace Falcor { -ComputePass::ComputePass(ref pDevice, const Program::Desc& desc, const Program::DefineList& defines, bool createVars) - : mpDevice(pDevice) +ComputePass::ComputePass(ref pDevice, const Program::Desc& desc, const DefineList& defines, bool createVars) : mpDevice(pDevice) { auto pProg = ComputeProgram::create(mpDevice, desc, defines); mpState = ComputeState::create(mpDevice); @@ -46,7 +45,7 @@ ref ComputePass::create( ref pDevice, const std::filesystem::path& path, const std::string& csEntry, - const Program::DefineList& defines, + const DefineList& defines, bool createVars ) { @@ -55,7 +54,7 @@ ref ComputePass::create( return create(pDevice, desc, defines, createVars); } -ref ComputePass::create(ref pDevice, const Program::Desc& desc, const Program::DefineList& defines, bool createVars) +ref ComputePass::create(ref pDevice, const Program::Desc& desc, const DefineList& defines, bool createVars) { return ref(new ComputePass(pDevice, desc, defines, createVars)); } diff --git a/Source/Falcor/Core/Pass/ComputePass.h b/Source/Falcor/Core/Pass/ComputePass.h index ef25eb185..42819eeda 100644 --- a/Source/Falcor/Core/Pass/ComputePass.h +++ b/Source/Falcor/Core/Pass/ComputePass.h @@ -40,6 +40,7 @@ namespace Falcor { class FALCOR_API ComputePass : public Object { + FALCOR_OBJECT(ComputePass) public: /** * Create a new compute pass from file. @@ -54,7 +55,7 @@ class FALCOR_API ComputePass : public Object ref pDevice, const std::filesystem::path& path, const std::string& csEntry = "main", - const Program::DefineList& defines = Program::DefineList(), + const DefineList& defines = DefineList(), bool createVars = true ); @@ -69,7 +70,7 @@ class FALCOR_API ComputePass : public Object static ref create( ref pDevice, const Program::Desc& desc, - const Program::DefineList& defines = Program::DefineList(), + const DefineList& defines = DefineList(), bool createVars = true ); @@ -142,7 +143,7 @@ class FALCOR_API ComputePass : public Object uint3 getThreadGroupSize() const { return mpState->getProgram()->getReflector()->getThreadGroupSize(); } protected: - ComputePass(ref pDevice, const Program::Desc& desc, const Program::DefineList& defines, bool createVars); + ComputePass(ref pDevice, const Program::Desc& desc, const DefineList& defines, bool createVars); ref mpDevice; ref mpVars; diff --git a/Source/Falcor/Core/Pass/FullScreenPass.cpp b/Source/Falcor/Core/Pass/FullScreenPass.cpp index 327f29b07..fe45d5cf9 100644 --- a/Source/Falcor/Core/Pass/FullScreenPass.cpp +++ b/Source/Falcor/Core/Pass/FullScreenPass.cpp @@ -72,7 +72,7 @@ struct FullScreenPass::SharedData static SharedCache sSharedCache; -FullScreenPass::FullScreenPass(ref pDevice, const Program::Desc& progDesc, const Program::DefineList& programDefines) +FullScreenPass::FullScreenPass(ref pDevice, const Program::Desc& progDesc, const DefineList& programDefines) : BaseGraphicsPass(pDevice, progDesc, programDefines) { // Get shared VB and VAO. @@ -88,15 +88,10 @@ FullScreenPass::FullScreenPass(ref pDevice, const Program::Desc& progDes FullScreenPass::~FullScreenPass() = default; -ref FullScreenPass::create( - ref pDevice, - const Program::Desc& desc, - const Program::DefineList& defines, - uint32_t viewportMask -) +ref FullScreenPass::create(ref pDevice, const Program::Desc& desc, const DefineList& defines, uint32_t viewportMask) { Program::Desc d = desc; - Program::DefineList defs = defines; + DefineList defs = defines; std::string gs; if (viewportMask) @@ -114,7 +109,7 @@ ref FullScreenPass::create( ref FullScreenPass::create( ref pDevice, const std::filesystem::path& path, - const Program::DefineList& defines, + const DefineList& defines, uint32_t viewportMask ) { diff --git a/Source/Falcor/Core/Pass/FullScreenPass.h b/Source/Falcor/Core/Pass/FullScreenPass.h index 25eb4e751..e2250798c 100644 --- a/Source/Falcor/Core/Pass/FullScreenPass.h +++ b/Source/Falcor/Core/Pass/FullScreenPass.h @@ -52,7 +52,7 @@ class FALCOR_API FullScreenPass : public BaseGraphicsPass static ref create( ref pDevice, const std::filesystem::path& path, - const Program::DefineList& defines = Program::DefineList(), + const DefineList& defines = DefineList(), uint32_t viewportMask = 0 ); @@ -67,7 +67,7 @@ class FALCOR_API FullScreenPass : public BaseGraphicsPass static ref create( ref pDevice, const Program::Desc& desc, - const Program::DefineList& defines = Program::DefineList(), + const DefineList& defines = DefineList(), uint32_t viewportMask = 0 ); @@ -81,7 +81,7 @@ class FALCOR_API FullScreenPass : public BaseGraphicsPass virtual void execute(RenderContext* pRenderContext, const ref& pFbo, bool autoSetVpSc = true) const; protected: - FullScreenPass(ref pDevice, const Program::Desc& progDesc, const Program::DefineList& programDefines); + FullScreenPass(ref pDevice, const Program::Desc& progDesc, const DefineList& programDefines); private: std::shared_ptr mpSharedData; diff --git a/Source/Falcor/Core/Pass/RasterPass.cpp b/Source/Falcor/Core/Pass/RasterPass.cpp index 35538ac65..272a6f96a 100644 --- a/Source/Falcor/Core/Pass/RasterPass.cpp +++ b/Source/Falcor/Core/Pass/RasterPass.cpp @@ -30,7 +30,7 @@ namespace Falcor { -ref RasterPass::create(ref pDevice, const Program::Desc& desc, const Program::DefineList& defines) +ref RasterPass::create(ref pDevice, const Program::Desc& desc, const DefineList& defines) { return ref(new RasterPass(pDevice, desc, defines)); } @@ -40,7 +40,7 @@ ref RasterPass::create( const std::filesystem::path& path, const std::string& vsEntry, const std::string& psEntry, - const Program::DefineList& defines + const DefineList& defines ) { Program::Desc desc; @@ -48,7 +48,7 @@ ref RasterPass::create( return create(pDevice, desc, defines); } -RasterPass::RasterPass(ref pDevice, const Program::Desc& progDesc, const Program::DefineList& programDefines) +RasterPass::RasterPass(ref pDevice, const Program::Desc& progDesc, const DefineList& programDefines) : BaseGraphicsPass(pDevice, progDesc, programDefines) {} diff --git a/Source/Falcor/Core/Pass/RasterPass.h b/Source/Falcor/Core/Pass/RasterPass.h index 6e3c2795f..3401775a1 100644 --- a/Source/Falcor/Core/Pass/RasterPass.h +++ b/Source/Falcor/Core/Pass/RasterPass.h @@ -53,7 +53,7 @@ class FALCOR_API RasterPass : public BaseGraphicsPass const std::filesystem::path& path, const std::string& vsEntry, const std::string& psEntry, - const Program::DefineList& defines = Program::DefineList() + const DefineList& defines = DefineList() ); /** @@ -64,11 +64,7 @@ class FALCOR_API RasterPass : public BaseGraphicsPass * 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() - ); + static ref create(ref pDevice, const Program::Desc& desc, const DefineList& defines = DefineList()); /** * Ordered draw call. @@ -86,6 +82,6 @@ class FALCOR_API RasterPass : public BaseGraphicsPass 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); + RasterPass(ref pDevice, const Program::Desc& progDesc, const DefineList& programDefines); }; } // namespace Falcor diff --git a/Source/Falcor/Core/Program/ComputeProgram.cpp b/Source/Falcor/Core/Program/ComputeProgram.cpp index 6774d003b..8b57b203f 100644 --- a/Source/Falcor/Core/Program/ComputeProgram.cpp +++ b/Source/Falcor/Core/Program/ComputeProgram.cpp @@ -39,7 +39,7 @@ ref ComputeProgram::createFromFile( const std::filesystem::path& path, const std::string& csEntry, const DefineList& programDefines, - Shader::CompilerFlags flags, + CompilerFlags flags, const std::string& shaderModel ) { diff --git a/Source/Falcor/Core/Program/ComputeProgram.h b/Source/Falcor/Core/Program/ComputeProgram.h index 632939257..f260158a1 100644 --- a/Source/Falcor/Core/Program/ComputeProgram.h +++ b/Source/Falcor/Core/Program/ComputeProgram.h @@ -62,7 +62,7 @@ class FALCOR_API ComputeProgram : public Program const std::filesystem::path& path, const std::string& csEntry, const DefineList& programDefines = DefineList(), - Shader::CompilerFlags flags = Shader::CompilerFlags::None, + CompilerFlags flags = CompilerFlags::None, const std::string& shaderModel = "" ); diff --git a/Source/Falcor/Core/Program/DefineList.h b/Source/Falcor/Core/Program/DefineList.h new file mode 100644 index 000000000..5ff74b1c3 --- /dev/null +++ b/Source/Falcor/Core/Program/DefineList.h @@ -0,0 +1,86 @@ +/*************************************************************************** + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. + # + # Redistribution and use in source and binary forms, with or without + # modification, are permitted provided that the following conditions + # are met: + # * Redistributions of source code must retain the above copyright + # notice, this list of conditions and the following disclaimer. + # * Redistributions in binary form must reproduce the above copyright + # notice, this list of conditions and the following disclaimer in the + # documentation and/or other materials provided with the distribution. + # * Neither the name of NVIDIA CORPORATION nor the names of its + # contributors may be used to endorse or promote products derived + # from this software without specific prior written permission. + # + # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY + # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + # (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 + +namespace Falcor +{ + +class DefineList : public std::map +{ +public: + /** + * Adds a macro definition. If the macro already exists, it will be replaced. + * @param[in] name The name of macro. + * @param[in] value Optional. The value of the macro. + * @return The updated list of macro definitions. + */ + DefineList& add(const std::string& name, const std::string& val = "") + { + (*this)[name] = val; + return *this; + } + + /** + * Removes a macro definition. If the macro doesn't exist, the call will be silently ignored. + * @param[in] name The name of macro. + * @return The updated list of macro definitions. + */ + DefineList& remove(const std::string& name) + { + (*this).erase(name); + return *this; + } + + /** + * Add a define list to the current list + */ + DefineList& add(const DefineList& dl) + { + for (const auto& p : dl) + add(p.first, p.second); + return *this; + } + + /** + * Remove a define list from the current list + */ + DefineList& remove(const DefineList& dl) + { + for (const auto& p : dl) + remove(p.first); + return *this; + } + + DefineList() = default; + DefineList(std::initializer_list> il) : std::map(il) {} +}; +} // namespace Falcor diff --git a/Source/Falcor/Core/Program/GraphicsProgram.cpp b/Source/Falcor/Core/Program/GraphicsProgram.cpp index c12c62be8..2e980f6b2 100644 --- a/Source/Falcor/Core/Program/GraphicsProgram.cpp +++ b/Source/Falcor/Core/Program/GraphicsProgram.cpp @@ -33,7 +33,7 @@ namespace Falcor { -ref GraphicsProgram::create(ref pDevice, const Desc& desc, const Program::DefineList& programDefines) +ref GraphicsProgram::create(ref pDevice, const Desc& desc, const DefineList& programDefines) { return ref(new GraphicsProgram(pDevice, desc, programDefines)); } @@ -51,7 +51,7 @@ ref GraphicsProgram::createFromFile( return create(pDevice, d, programDefines); } -GraphicsProgram::GraphicsProgram(ref pDevice, const Desc& desc, const Program::DefineList& programDefines) +GraphicsProgram::GraphicsProgram(ref pDevice, const Desc& desc, const DefineList& programDefines) : Program(pDevice, desc, programDefines) {} diff --git a/Source/Falcor/Core/Program/GraphicsProgram.h b/Source/Falcor/Core/Program/GraphicsProgram.h index 1934c1ed0..95839311b 100644 --- a/Source/Falcor/Core/Program/GraphicsProgram.h +++ b/Source/Falcor/Core/Program/GraphicsProgram.h @@ -51,7 +51,7 @@ class FALCOR_API GraphicsProgram : public Program * stages. * @return A new object, or an exception is thrown if creation failed. */ - static ref create(ref pDevice, const Desc& desc, const Program::DefineList& programDefines = DefineList()); + static ref create(ref pDevice, const Desc& desc, const DefineList& programDefines = DefineList()); /** * Create a new graphics program from file. @@ -73,6 +73,6 @@ class FALCOR_API GraphicsProgram : public Program ); private: - GraphicsProgram(ref pDevice, const Desc& desc, const Program::DefineList& programDefines); + GraphicsProgram(ref pDevice, const Desc& desc, const DefineList& programDefines); }; } // namespace Falcor diff --git a/Source/Falcor/Core/Program/Program.cpp b/Source/Falcor/Core/Program/Program.cpp index 1971e5b24..cd3dc66f6 100644 --- a/Source/Falcor/Core/Program/Program.cpp +++ b/Source/Falcor/Core/Program/Program.cpp @@ -337,7 +337,7 @@ bool Program::setDefines(const DefineList& dl) bool Program::addTypeConformance(const std::string& typeName, const std::string interfaceType, uint32_t id) { - Shader::TypeConformance conformance = Shader::TypeConformance(typeName, interfaceType); + TypeConformance conformance = TypeConformance(typeName, interfaceType); if (mTypeConformanceList.find(conformance) == mTypeConformanceList.end()) { markDirty(); @@ -349,7 +349,7 @@ bool Program::addTypeConformance(const std::string& typeName, const std::string bool Program::removeTypeConformance(const std::string& typeName, const std::string interfaceType) { - Shader::TypeConformance conformance = Shader::TypeConformance(typeName, interfaceType); + TypeConformance conformance = TypeConformance(typeName, interfaceType); if (mTypeConformanceList.find(conformance) != mTypeConformanceList.end()) { markDirty(); diff --git a/Source/Falcor/Core/Program/Program.h b/Source/Falcor/Core/Program/Program.h index 331252835..0a8590902 100644 --- a/Source/Falcor/Core/Program/Program.h +++ b/Source/Falcor/Core/Program/Program.h @@ -27,10 +27,11 @@ **************************************************************************/ #pragma once #include "ProgramVersion.h" +#include "DefineList.h" #include "Core/Macros.h" #include "Core/Object.h" #include "Core/API/fwd.h" -#include "Core/API/Shader.h" +#include "Core/API/ShaderType.h" #include #include #include @@ -48,10 +49,105 @@ namespace Falcor */ class FALCOR_API Program : public Object { + FALCOR_OBJECT(Program) public: - using DefineList = Shader::DefineList; using ArgumentList = std::vector; - using TypeConformanceList = Shader::TypeConformanceList; + + enum class CompilerFlags + { + None = 0x0, + TreatWarningsAsErrors = 0x1, + DumpIntermediates = 0x2, + FloatingPointModeFast = 0x4, + FloatingPointModePrecise = 0x8, + GenerateDebugInfo = 0x10, + MatrixLayoutColumnMajor = 0x20, // Falcor is using row-major, use this only to compile stand-alone external shaders. + }; + + /** + * Representing a shader implementation of an interface. + * When linked into a `ProgramVersion`, the specialized shader will contain + * the implementation of the specified type in a dynamic dispatch function. + */ + struct TypeConformance + { + std::string mTypeName; + std::string mInterfaceName; + TypeConformance() = default; + TypeConformance(const std::string& typeName, const std::string& interfaceName) : mTypeName(typeName), mInterfaceName(interfaceName) + {} + bool operator<(const TypeConformance& other) const + { + return mTypeName < other.mTypeName || (mTypeName == other.mTypeName && mInterfaceName < other.mInterfaceName); + } + bool operator==(const TypeConformance& other) const + { + return mTypeName == other.mTypeName && mInterfaceName == other.mInterfaceName; + } + struct HashFunction + { + size_t operator()(const TypeConformance& conformance) const + { + size_t hash = std::hash()(conformance.mTypeName); + hash = hash ^ std::hash()(conformance.mInterfaceName); + return hash; + } + }; + }; + + class TypeConformanceList : public std::map + { + public: + /** + * Adds a type conformance. If the type conformance exists, it will be replaced. + * @param[in] typeName The name of the implementation type. + * @param[in] interfaceName The name of the interface type. + * @param[in] id Optional. The id representing the implementation type for this interface. If it is -1, Slang will automatically + * assign a unique Id for the type. + * @return The updated list of type conformances. + */ + TypeConformanceList& add(const std::string& typeName, const std::string& interfaceName, uint32_t id = -1) + { + (*this)[TypeConformance(typeName, interfaceName)] = id; + return *this; + } + + /** + * Removes a type conformance. If the type conformance doesn't exist, the call will be silently ignored. + * @param[in] typeName The name of the implementation type. + * @param[in] interfaceName The name of the interface type. + * @return The updated list of type conformances. + */ + TypeConformanceList& remove(const std::string& typeName, const std::string& interfaceName) + { + (*this).erase(TypeConformance(typeName, interfaceName)); + return *this; + } + + /** + * Add a type conformance list to the current list + */ + TypeConformanceList& add(const TypeConformanceList& cl) + { + for (const auto& p : cl) + add(p.first.mTypeName, p.first.mInterfaceName, p.second); + return *this; + } + + /** + * Remove a type conformance list from the current list + */ + TypeConformanceList& remove(const TypeConformanceList& cl) + { + for (const auto& p : cl) + remove(p.first.mTypeName, p.first.mInterfaceName); + return *this; + } + + TypeConformanceList() = default; + TypeConformanceList(std::initializer_list> il) : std::map(il) + {} + }; /** * Shader module stored as a string or file. @@ -182,25 +278,6 @@ class FALCOR_API Program : public Object return *this; } - /** - * Enable/disable treat-warnings-as-error compilation flag. - */ - Desc& warningsAsErrors(bool enable) - { - enable ? mShaderFlags |= Shader::CompilerFlags::TreatWarningsAsErrors - : mShaderFlags &= ~(Shader::CompilerFlags::TreatWarningsAsErrors); - return *this; - } - - /** - * Enable/disable pre-processed shader dump. - */ - Desc& dumpIntermediates(bool enable) - { - enable ? mShaderFlags |= Shader::CompilerFlags::DumpIntermediates : mShaderFlags &= ~(Shader::CompilerFlags::DumpIntermediates); - return *this; - } - /** * Set the shader model string. * This should be `6_0`, `6_1`, `6_2`, `6_3`, `6_4`, or `6_5`. The default is `6_3`. @@ -210,12 +287,12 @@ class FALCOR_API Program : public Object /** * Get the compiler flags. */ - Shader::CompilerFlags getCompilerFlags() const { return mShaderFlags; } + CompilerFlags getCompilerFlags() const { return mShaderFlags; } /** * Set the compiler flags. Replaces any previously set flags. */ - Desc& setCompilerFlags(Shader::CompilerFlags flags) + Desc& setCompilerFlags(CompilerFlags flags) { mShaderFlags = flags; return *this; @@ -285,7 +362,7 @@ class FALCOR_API Program : public Object int32_t mActiveSource = -1; ///< Current source index. int32_t mActiveGroup = -1; ///< Current entry point index. - Shader::CompilerFlags mShaderFlags = Shader::CompilerFlags::None; + CompilerFlags mShaderFlags = CompilerFlags::None; ArgumentList mCompilerArguments; std::string mShaderModel = "6_3"; std::string mLanguagePrelude; @@ -434,4 +511,6 @@ class FALCOR_API Program : public Object void reset(); }; +FALCOR_ENUM_CLASS_OPERATORS(Program::CompilerFlags); + } // namespace Falcor diff --git a/Source/Falcor/Core/Program/ProgramManager.cpp b/Source/Falcor/Core/Program/ProgramManager.cpp index d8633e376..67e274479 100644 --- a/Source/Falcor/Core/Program/ProgramManager.cpp +++ b/Source/Falcor/Core/Program/ProgramManager.cpp @@ -426,19 +426,18 @@ ref ProgramManager::createProgramKernels( ref pReflector; doSlangReflection(programVersion, pSpecializedSlangProgram, pLinkedEntryPoints, pReflector, log); - // Create Shader objects for each entry point and cache them here. - std::vector> allShaders; + // Create kernel objects for each entry point and cache them here. + std::vector> allKernels; for (uint32_t i = 0; i < allEntryPointCount; i++) { auto pLinkedEntryPoint = pLinkedEntryPoints[i]; auto entryPointDesc = program.mDesc.mEntryPoints[i]; - ref shader = - Shader::create(pLinkedEntryPoint, entryPointDesc.stage, entryPointDesc.exportName, program.mDesc.getCompilerFlags(), log); - if (!shader) + ref kernel = EntryPointKernel::create(pLinkedEntryPoint, entryPointDesc.stage, entryPointDesc.exportName); + if (!kernel) return nullptr; - allShaders.push_back(std::move(shader)); + allKernels.push_back(std::move(kernel)); } // In order to construct the `ProgramKernels` we need to extract @@ -459,13 +458,13 @@ ref ProgramManager::createProgramKernels( // code for its constituent entry points, using the "linked" // version of the entry-point group. // - std::vector> shaders; + std::vector> kernels; for (auto entryPointIndex : entryPointGroupDesc.entryPoints) { - shaders.push_back(allShaders[entryPointIndex]); + kernels.push_back(allKernels[entryPointIndex]); } auto pGroupReflector = pReflector->getEntryPointGroup(gg); - auto pEntryPointGroupKernels = createEntryPointGroupKernels(shaders, pGroupReflector); + auto pEntryPointGroupKernels = createEntryPointGroupKernels(kernels, pGroupReflector); entryPointGroups.push_back(pEntryPointGroupKernels); } @@ -486,22 +485,22 @@ ref ProgramManager::createProgramKernels( } ref ProgramManager::createEntryPointGroupKernels( - const std::vector>& shaders, + const std::vector>& kernels, const ref& pReflector ) const { - FALCOR_ASSERT(shaders.size() != 0); + FALCOR_ASSERT(kernels.size() != 0); - switch (shaders[0]->getType()) + switch (kernels[0]->getType()) { case ShaderType::Vertex: case ShaderType::Pixel: case ShaderType::Geometry: case ShaderType::Hull: case ShaderType::Domain: - return EntryPointGroupKernels::create(EntryPointGroupKernels::Type::Rasterization, shaders, shaders[0]->getEntryPoint()); + return EntryPointGroupKernels::create(EntryPointGroupKernels::Type::Rasterization, kernels, kernels[0]->getEntryPointName()); case ShaderType::Compute: - return EntryPointGroupKernels::create(EntryPointGroupKernels::Type::Compute, shaders, shaders[0]->getEntryPoint()); + return EntryPointGroupKernels::create(EntryPointGroupKernels::Type::Compute, kernels, kernels[0]->getEntryPointName()); case ShaderType::AnyHit: case ShaderType::ClosestHit: case ShaderType::Intersection: @@ -512,12 +511,12 @@ ref ProgramManager::createEntryPointGroupKernels( throw RuntimeError("Local root signatures are not supported for raytracing entry points."); } std::string exportName = fmt::format("HitGroup{}", mHitGroupID++); - return EntryPointGroupKernels::create(EntryPointGroupKernels::Type::RtHitGroup, shaders, exportName); + return EntryPointGroupKernels::create(EntryPointGroupKernels::Type::RtHitGroup, kernels, exportName); } case ShaderType::RayGeneration: case ShaderType::Miss: case ShaderType::Callable: - return EntryPointGroupKernels::create(EntryPointGroupKernels::Type::RtSingleShader, shaders, shaders[0]->getEntryPoint()); + return EntryPointGroupKernels::create(EntryPointGroupKernels::Type::RtSingleShader, kernels, kernels[0]->getEntryPointName()); } FALCOR_UNREACHABLE(); @@ -551,13 +550,13 @@ bool ProgramManager::reloadAllPrograms(bool forceReload) return hasReloaded; } -void ProgramManager::addGlobalDefines(const Program::DefineList& defineList) +void ProgramManager::addGlobalDefines(const DefineList& defineList) { mGlobalDefineList.add(defineList); reloadAllPrograms(true); } -void ProgramManager::removeGlobalDefines(const Program::DefineList& defineList) +void ProgramManager::removeGlobalDefines(const DefineList& defineList) { mGlobalDefineList.remove(defineList); reloadAllPrograms(true); @@ -612,19 +611,16 @@ SlangCompileRequest* ProgramManager::createSlangCompileRequest(const Program& pr targetDesc.profile = pSlangGlobalSession->findProfile(getSlangProfileString(program.mDesc.mShaderModel).c_str()); if (targetDesc.profile == SLANG_PROFILE_UNKNOWN) - { - reportError("Can't find Slang profile for shader model " + program.mDesc.mShaderModel); - return nullptr; - } + throw RuntimeError("Can't find Slang profile for shader model {}", program.mDesc.mShaderModel); // Get compiler flags and adjust with forced flags. - Shader::CompilerFlags compilerFlags = program.mDesc.getCompilerFlags(); + Program::CompilerFlags compilerFlags = program.mDesc.getCompilerFlags(); compilerFlags &= ~mForcedCompilerFlags.disabled; compilerFlags |= mForcedCompilerFlags.enabled; // Set floating point mode. If no shader compiler flags for this were set, we use Slang's default mode. - bool flagFast = is_set(compilerFlags, Shader::CompilerFlags::FloatingPointModeFast); - bool flagPrecise = is_set(compilerFlags, Shader::CompilerFlags::FloatingPointModePrecise); + bool flagFast = is_set(compilerFlags, Program::CompilerFlags::FloatingPointModeFast); + bool flagPrecise = is_set(compilerFlags, Program::CompilerFlags::FloatingPointModePrecise); if (flagFast && flagPrecise) { logWarning( @@ -651,11 +647,11 @@ SlangCompileRequest* ProgramManager::createSlangCompileRequest(const Program& pr { case Device::Type::D3D12: targetDesc.format = SLANG_DXIL; - targetMacroName = "FALCOR_D3D"; + targetMacroName = "FALCOR_D3D12"; break; case Device::Type::Vulkan: targetDesc.format = SLANG_SPIRV; - targetMacroName = "FALCOR_VK"; + targetMacroName = "FALCOR_VULKAN"; break; default: FALCOR_UNREACHABLE(); @@ -689,7 +685,7 @@ SlangCompileRequest* ProgramManager::createSlangCompileRequest(const Program& pr // to allow it to compute correct reflection information. Slang then invokes the downstream compiler. // Column major option can be useful when compiling external shader sources that don't depend // on anything Falcor. - bool useColumnMajor = is_set(compilerFlags, Shader::CompilerFlags::MatrixLayoutColumnMajor); + bool useColumnMajor = is_set(compilerFlags, Program::CompilerFlags::MatrixLayoutColumnMajor); sessionDesc.defaultMatrixLayoutMode = useColumnMajor ? SLANG_MATRIX_LAYOUT_COLUMN_MAJOR : SLANG_MATRIX_LAYOUT_ROW_MAJOR; Slang::ComPtr pSlangSession; @@ -706,8 +702,7 @@ SlangCompileRequest* ProgramManager::createSlangCompileRequest(const Program& pr } else { - reportError("Language prelude set for unsupported target " + std::string(targetMacroName)); - return nullptr; + throw RuntimeError("Language prelude set for unsupported target {}", targetMacroName); } } @@ -720,11 +715,11 @@ SlangCompileRequest* ProgramManager::createSlangCompileRequest(const Program& pr spOverrideDiagnosticSeverity(pSlangRequest, 30081, SLANG_SEVERITY_DISABLED); // implicit conversion // Enable/disable intermediates dump - bool dumpIR = is_set(program.mDesc.getCompilerFlags(), Shader::CompilerFlags::DumpIntermediates); + bool dumpIR = is_set(program.mDesc.getCompilerFlags(), Program::CompilerFlags::DumpIntermediates); spSetDumpIntermediates(pSlangRequest, dumpIR); // Set debug level - if (mGenerateDebugInfo || is_set(program.mDesc.getCompilerFlags(), Shader::CompilerFlags::GenerateDebugInfo)) + if (mGenerateDebugInfo || is_set(program.mDesc.getCompilerFlags(), Program::CompilerFlags::GenerateDebugInfo)) spSetDebugInfoLevel(pSlangRequest, SLANG_DEBUG_INFO_LEVEL_STANDARD); // Configure any flags for the Slang compilation step @@ -785,9 +780,8 @@ SlangCompileRequest* ProgramManager::createSlangCompileRequest(const Program& pr std::filesystem::path fullPath; if (!findFileInShaderDirectories(path, fullPath)) { - reportError("Can't find file " + path.string()); spDestroyCompileRequest(pSlangRequest); - return nullptr; + throw RuntimeError("Can't find shader file {}", path); } spAddTranslationUnitSourceFile(pSlangRequest, translationUnitIndex, fullPath.string().c_str()); } diff --git a/Source/Falcor/Core/Program/ProgramManager.h b/Source/Falcor/Core/Program/ProgramManager.h index 87443e391..3b7c58441 100644 --- a/Source/Falcor/Core/Program/ProgramManager.h +++ b/Source/Falcor/Core/Program/ProgramManager.h @@ -45,8 +45,8 @@ class ProgramManager */ struct ForcedCompilerFlags { - Shader::CompilerFlags enabled = Shader::CompilerFlags::None; ///< Compiler flags forcefully enabled on all shaders - Shader::CompilerFlags disabled = Shader::CompilerFlags::None; ///< Compiler flags forcefully enabled on all shaders + Program::CompilerFlags enabled = Program::CompilerFlags::None; ///< Compiler flags forcefully enabled on all shaders + Program::CompilerFlags disabled = Program::CompilerFlags::None; ///< Compiler flags forcefully enabled on all shaders }; struct CompilationStats @@ -73,7 +73,7 @@ class ProgramManager ) const; ref createEntryPointGroupKernels( - const std::vector>& shaders, + const std::vector>& kernels, const ref& pReflector ) const; @@ -88,13 +88,13 @@ class ProgramManager * Add a list of defines applied to all programs. * @param[in] defineList List of macro definitions. */ - void addGlobalDefines(const Program::DefineList& defineList); + void addGlobalDefines(const DefineList& defineList); /** * Remove a list of defines applied to all programs. * @param[in] defineList List of macro definitions. */ - void removeGlobalDefines(const Program::DefineList& defineList); + void removeGlobalDefines(const DefineList& defineList); /** * Enable/disable global generation of shader debug info. @@ -133,7 +133,7 @@ class ProgramManager std::vector mLoadedPrograms; mutable CompilationStats mCompilationStats; - Program::DefineList mGlobalDefineList; + DefineList mGlobalDefineList; bool mGenerateDebugInfo = false; ForcedCompilerFlags mForcedCompilerFlags; diff --git a/Source/Falcor/Core/Program/ProgramReflection.cpp b/Source/Falcor/Core/Program/ProgramReflection.cpp index 57c1e3618..0f10f2255 100644 --- a/Source/Falcor/Core/Program/ProgramReflection.cpp +++ b/Source/Falcor/Core/Program/ProgramReflection.cpp @@ -62,8 +62,7 @@ TypedShaderVarOffset TypedShaderVarOffset::operator[](const std::string& name) c } } - reportError(fmt::format("No member named '{}' found.", name)); - return TypedShaderVarOffset(); + throw RuntimeError("No member named '{}' found.", name); } TypedShaderVarOffset TypedShaderVarOffset::operator[](const char* name) const diff --git a/Source/Falcor/Core/Program/ProgramReflection.h b/Source/Falcor/Core/Program/ProgramReflection.h index 1efbedf5f..52e6851c2 100644 --- a/Source/Falcor/Core/Program/ProgramReflection.h +++ b/Source/Falcor/Core/Program/ProgramReflection.h @@ -582,6 +582,7 @@ struct TypedShaderVarOffset : ShaderVarOffset */ class FALCOR_API ReflectionType : public Object { + FALCOR_OBJECT(ReflectionType) public: virtual ~ReflectionType() = default; @@ -1205,6 +1206,7 @@ class FALCOR_API ReflectionInterfaceType : public ReflectionType */ class FALCOR_API ReflectionVar : public Object { + FALCOR_OBJECT(ReflectionVar) public: /** * Create a new object @@ -1249,6 +1251,7 @@ class ProgramReflection; */ class FALCOR_API ParameterBlockReflection : public Object { + FALCOR_OBJECT(ParameterBlockReflection) public: static constexpr uint32_t kInvalidIndex = 0xffffffff; @@ -1490,6 +1493,7 @@ typedef EntryPointGroupReflection EntryPointBaseReflection; */ class FALCOR_API ProgramReflection : public Object { + FALCOR_OBJECT(ProgramReflection) public: /** * Data structured describing a shader input/output variable. Used mostly to communicate VS inputs and PS outputs diff --git a/Source/Falcor/Core/Program/ProgramVersion.cpp b/Source/Falcor/Core/Program/ProgramVersion.cpp index 9e6405a33..e1e2db0b8 100644 --- a/Source/Falcor/Core/Program/ProgramVersion.cpp +++ b/Source/Falcor/Core/Program/ProgramVersion.cpp @@ -46,23 +46,23 @@ namespace Falcor ref EntryPointGroupKernels::create( EntryPointGroupKernels::Type type, - const EntryPointGroupKernels::Shaders& shaders, + const std::vector>& kernels, const std::string& exportName ) { - return ref(new EntryPointGroupKernels(type, shaders, exportName)); + return ref(new EntryPointGroupKernels(type, kernels, exportName)); } -EntryPointGroupKernels::EntryPointGroupKernels(Type type, const Shaders& shaders, const std::string& exportName) - : mType(type), mShaders(shaders), mExportName(exportName) +EntryPointGroupKernels::EntryPointGroupKernels(Type type, const std::vector>& kernels, const std::string& exportName) + : mType(type), mKernels(kernels), mExportName(exportName) {} -const Shader* EntryPointGroupKernels::getShader(ShaderType type) const +const EntryPointKernel* EntryPointGroupKernels::getKernel(ShaderType type) const { - for (auto& pShader : mShaders) + for (auto& pKernel : mKernels) { - if (pShader->getType() == type) - return pShader.get(); + if (pKernel->getType() == type) + return pKernel.get(); } return nullptr; } @@ -152,11 +152,11 @@ ref ProgramKernels::create( return pProgram; } -const Shader* ProgramKernels::getShader(ShaderType type) const +const EntryPointKernel* ProgramKernels::getKernel(ShaderType type) const { for (auto& pEntryPointGroup : mUniqueEntryPointGroups) { - if (auto pShader = pEntryPointGroup->getShader(type)) + if (auto pShader = pEntryPointGroup->getKernel(type)) return pShader; } return nullptr; diff --git a/Source/Falcor/Core/Program/ProgramVersion.h b/Source/Falcor/Core/Program/ProgramVersion.h index 30aba922a..354fba2e2 100644 --- a/Source/Falcor/Core/Program/ProgramVersion.h +++ b/Source/Falcor/Core/Program/ProgramVersion.h @@ -27,10 +27,11 @@ **************************************************************************/ #pragma once #include "ProgramReflection.h" +#include "DefineList.h" #include "Core/Macros.h" #include "Core/Object.h" #include "Core/API/fwd.h" -#include "Core/API/Shader.h" +#include "Core/API/ShaderType.h" #include "Core/API/Handles.h" #include #include @@ -45,11 +46,88 @@ class FALCOR_API Program; class FALCOR_API ProgramVars; class FALCOR_API ProgramVersion; +/** + * Represents a single program entry point and its associated kernel code. + * + * 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 + * are known, which is right before a draw/dispatch command is issued, and this is done + * internally within GFX. + * The `EntryPointKernel` implementation here serves as a helper utility for application code that + * uses raw graphics API to get shader kernel code from an ordinary slang source. + * Since most users/render-passes do not need to get shader kernel code, we defer + * the call to slang's `getEntryPointCode` function until it is actually needed. + * to avoid redundant shader compiler invocation. + */ +class FALCOR_API EntryPointKernel : public Object +{ + FALCOR_OBJECT(EntryPointKernel) +public: + struct BlobData + { + const void* data; + size_t size; + }; + + /** + * Create a shader object + * @param[in] linkedSlangEntryPoint The Slang IComponentType that defines the shader entry point. + * @param[in] type The Type of the shader + * @return If success, a new shader object, otherwise nullptr + */ + static ref create( + Slang::ComPtr linkedSlangEntryPoint, + ShaderType type, + const std::string& entryPointName + ) + { + return ref(new EntryPointKernel(linkedSlangEntryPoint, type, entryPointName)); + } + + /** + * Get the shader Type + */ + ShaderType getType() const { return mType; } + + /** + * Get the name of the entry point. + */ + const std::string& getEntryPointName() const { return mEntryPointName; } + + BlobData getBlobData() const + { + if (!mpBlob) + { + Slang::ComPtr pDiagnostics; + if (SLANG_FAILED(mLinkedSlangEntryPoint->getEntryPointCode(0, 0, mpBlob.writeRef(), pDiagnostics.writeRef()))) + { + throw RuntimeError(std::string("Shader compilation failed. \n") + (const char*)pDiagnostics->getBufferPointer()); + } + } + + BlobData result; + result.data = mpBlob->getBufferPointer(); + result.size = mpBlob->getBufferSize(); + return result; + } + +protected: + EntryPointKernel(Slang::ComPtr linkedSlangEntryPoint, ShaderType type, const std::string& entryPointName) + : mLinkedSlangEntryPoint(linkedSlangEntryPoint), mType(type), mEntryPointName(entryPointName) + {} + + Slang::ComPtr mLinkedSlangEntryPoint; + ShaderType mType; + std::string mEntryPointName; + mutable Slang::ComPtr mpBlob; +}; + /** * A collection of one or more entry points in a program kernels object. */ class FALCOR_API EntryPointGroupKernels : public Object { + FALCOR_OBJECT(EntryPointGroupKernels) public: /** * Types of entry point groups. @@ -62,25 +140,27 @@ class FALCOR_API EntryPointGroupKernels : public Object RtHitGroup, ///< A ray tracing "hit group" }; - using Shaders = std::vector>; - - static ref create(Type type, const Shaders& shaders, const std::string& exportName); + static ref create( + Type type, + const std::vector>& kernels, + const std::string& exportName + ); virtual ~EntryPointGroupKernels() = default; Type getType() const { return mType; } - const Shader* getShader(ShaderType type) const; - const Shader* getShaderByIndex(int32_t index) const { return mShaders[index].get(); } + const EntryPointKernel* getKernel(ShaderType type) const; + const EntryPointKernel* getKernelByIndex(size_t index) const { return mKernels[index].get(); } const std::string& getExportName() const { return mExportName; } protected: - EntryPointGroupKernels(Type type, const Shaders& shaders, const std::string& exportName); + EntryPointGroupKernels(Type type, const std::vector>& shaders, const std::string& exportName); EntryPointGroupKernels() = default; EntryPointGroupKernels(const EntryPointGroupKernels&) = delete; EntryPointGroupKernels& operator=(const EntryPointGroupKernels&) = delete; Type mType; - Shaders mShaders; + std::vector> mKernels; std::string mExportName; }; @@ -90,6 +170,7 @@ class FALCOR_API EntryPointGroupKernels : public Object */ class FALCOR_API ProgramKernels : public Object { + FALCOR_OBJECT(ProgramKernels) public: typedef std::vector> UniqueEntryPointGroups; @@ -121,7 +202,7 @@ class FALCOR_API ProgramKernels : public Object /** * Get an attached shader object, or nullptr if no shader is attached to the slot. */ - const Shader* getShader(ShaderType type) const; + const EntryPointKernel* getKernel(ShaderType type) const; /** * Get the program name @@ -162,9 +243,8 @@ class FALCOR_API ProgramKernels : public Object class ProgramVersion : public Object { + FALCOR_OBJECT(ProgramVersion) public: - using DefineList = Shader::DefineList; - /** * Get the program that this version was created from */ diff --git a/Source/Falcor/Core/Program/RtBindingTable.h b/Source/Falcor/Core/Program/RtBindingTable.h index 7e3507832..4832e6ded 100644 --- a/Source/Falcor/Core/Program/RtBindingTable.h +++ b/Source/Falcor/Core/Program/RtBindingTable.h @@ -47,6 +47,7 @@ namespace Falcor */ class FALCOR_API RtBindingTable : public Object { + FALCOR_OBJECT(RtBindingTable) public: using ShaderID = RtProgram::ShaderID; diff --git a/Source/Falcor/Core/Program/RtProgram.h b/Source/Falcor/Core/Program/RtProgram.h index 3ae921e90..526e6389a 100644 --- a/Source/Falcor/Core/Program/RtProgram.h +++ b/Source/Falcor/Core/Program/RtProgram.h @@ -268,24 +268,6 @@ class FALCOR_API RtProgram : public Program return *this; } - /** - * Enable/disable treat-warnings-as-error compilation flag. - */ - Desc& warningsAsErrors(bool enable) - { - mBaseDesc.warningsAsErrors(enable); - return *this; - } - - /** - * Enable/disable pre-processed shader dump. - */ - Desc& dumpIntermediates(bool enable) - { - mBaseDesc.dumpIntermediates(enable); - return *this; - } - /** * Set the shader model. The default is SM 6.5 for DXR Tier 1.1 support. */ @@ -298,12 +280,12 @@ class FALCOR_API RtProgram : public Program /** * Get the compiler flags. */ - Shader::CompilerFlags getCompilerFlags() const { return mBaseDesc.getCompilerFlags(); } + CompilerFlags getCompilerFlags() const { return mBaseDesc.getCompilerFlags(); } /** * Set the compiler flags. Replaces any previously set flags. */ - Desc& setCompilerFlags(Shader::CompilerFlags flags) + Desc& setCompilerFlags(CompilerFlags flags) { mBaseDesc.setCompilerFlags(flags); return *this; diff --git a/Source/Falcor/Core/Program/ShaderVar.cpp b/Source/Falcor/Core/Program/ShaderVar.cpp index b04b8a5ba..8933c0170 100644 --- a/Source/Falcor/Core/Program/ShaderVar.cpp +++ b/Source/Falcor/Core/Program/ShaderVar.cpp @@ -114,11 +114,16 @@ ShaderVar ShaderVar::operator[](const std::string& name) const auto result = findMember(name); if (!result.isValid() && isValid()) { - reportError("No member named '" + name + "' found.\n"); + throw ArgumentError("No member named '{}' found.", name); } return result; } +ShaderVar ShaderVar::operator[](std::string_view name) const +{ + return (*this)[std::string(name)]; +} + ShaderVar ShaderVar::operator[](const char* name) const { // #SHADER_VAR we can use std::string_view to do lookups into the map @@ -191,8 +196,7 @@ ShaderVar ShaderVar::operator[](size_t index) const } } - reportError("No element or member found at index " + std::to_string(index)); - return ShaderVar(); + throw ArgumentError("No element or member found at index {}", index); } ShaderVar ShaderVar::operator[](const TypedShaderVarOffset& offset) const @@ -287,8 +291,7 @@ ShaderVar ShaderVar::operator[](const UniformShaderVarOffset& loc) const } } - reportError("no member at offset"); - return ShaderVar(); + throw ArgumentError("No element or member found at offset {}", byteOffset); } bool ShaderVar::isValid() const diff --git a/Source/Falcor/Core/Program/ShaderVar.h b/Source/Falcor/Core/Program/ShaderVar.h index c60e379b8..850fd3ffa 100644 --- a/Source/Falcor/Core/Program/ShaderVar.h +++ b/Source/Falcor/Core/Program/ShaderVar.h @@ -135,6 +135,15 @@ struct FALCOR_API ShaderVar */ ShaderVar operator[](const char* name) const; + /** + * Get a shader variable pointer to a sub-field. + * + * This shader variable must point to a value of `struct` type, with a field matching the given `name`. + * If this shader variable points at a constant buffer or parameter block, then the lookup will proceed in the contents of that block. + * Otherwise an error is logged and an invalid `ShaderVar` is returned. + */ + ShaderVar operator[](std::string_view name) const; + /** * Get a shader variable pointer to a sub-field. * diff --git a/Source/Falcor/Core/SampleApp.cpp b/Source/Falcor/Core/SampleApp.cpp index e1345230c..64fb700a7 100644 --- a/Source/Falcor/Core/SampleApp.cpp +++ b/Source/Falcor/Core/SampleApp.cpp @@ -54,8 +54,6 @@ SampleApp::SampleApp(const SampleAppConfig& config) OSServices::start(); Threading::start(); - mpSettings.reset(new Settings); - mShowUI = config.showUI; mVsyncOn = config.windowDesc.enableVSync; mClock.setTimeScale(config.timeScale); @@ -96,17 +94,13 @@ SampleApp::SampleApp(const SampleAppConfig& config) uint2 fboSize = mpWindow ? mpWindow->getClientAreaSize() : uint2(config.windowDesc.width, config.windowDesc.height); mpTargetFBO = Fbo::create2D(mpDevice, fboSize.x, fboSize.y, config.colorFormat, config.depthFormat); - // Load settings.toml files - getSettings().addOptions(getRuntimeDirectory() / "settings.json"); - if (!getHomeDirectory().empty()) - getSettings().addOptions(getHomeDirectory() / ".falcor" / "settings.json"); // Populate the data search paths from the config file, only adding those that aren't in already auto searchDirectories = getSettings().getSearchDirectories("media"); for (auto& it : searchDirectories.get()) addDataDirectory(it); // Set global shader defines - Program::DefineList globalDefines = { + DefineList globalDefines = { {"FALCOR_NVAPI_AVAILABLE", (FALCOR_NVAPI_AVAILABLE && mpDevice->getType() == Device::Type::D3D12) ? "1" : "0"}, #if FALCOR_NVAPI_AVAILABLE {"NV_SHADER_EXTN_SLOT", "u999"}, @@ -118,7 +112,7 @@ SampleApp::SampleApp(const SampleAppConfig& config) if (config.shaderPreciseFloat) { mpDevice->getProgramManager()->setForcedCompilerFlags( - {Shader::CompilerFlags::FloatingPointModePrecise, Shader::CompilerFlags::FloatingPointModeFast} + {Program::CompilerFlags::FloatingPointModePrecise, Program::CompilerFlags::FloatingPointModeFast} ); } @@ -136,9 +130,6 @@ SampleApp::~SampleApp() mpDevice->flushAndSync(); - // contains Python dictionaries, needs to be terminated before Scripting::shutdown() - mpSettings.reset(); - Threading::shutdown(); Scripting::shutdown(); PluginManager::instance().releaseAllPlugins(); @@ -149,8 +140,8 @@ SampleApp::~SampleApp() mpSwapchain.reset(); mpWindow.reset(); -#if FALCOR_ENABLE_REF_TRACKING - Object::dumpAllRefs(); +#if FALCOR_ENABLE_OBJECT_TRACKING + Object::dumpAliveObjects(); #endif mpDevice.reset(); #ifdef _DEBUG @@ -161,6 +152,16 @@ SampleApp::~SampleApp() Logger::shutdown(); } +const Settings& SampleApp::getSettings() const +{ + return Settings::getGlobalSettings(); +} + +Settings& SampleApp::getSettings() +{ + return Settings::getGlobalSettings(); +} + int SampleApp::run() { try diff --git a/Source/Falcor/Core/SampleApp.h b/Source/Falcor/Core/SampleApp.h index c8f6f0c70..f6f81e844 100644 --- a/Source/Falcor/Core/SampleApp.h +++ b/Source/Falcor/Core/SampleApp.h @@ -169,13 +169,13 @@ class FALCOR_API SampleApp : public Window::ICallbacks /** * Get the Settings object for Options and Attributes. */ - const Settings& getSettings() const { return *mpSettings; } + const Settings& getSettings() const; /** * Get the Settings object for Options and Attributes, accessible for writing. * Should only be done by input-parsers, whatever they might be. */ - Settings& getSettings() { return *mpSettings; } + Settings& getSettings(); /** * Get the GPU device for this application. @@ -332,8 +332,6 @@ class FALCOR_API SampleApp : public Window::ICallbacks FrameRate mFrameRate; Clock mClock; - std::unique_ptr mpSettings; - bool mShouldTerminate = false; ///< True if application should terminate. bool mRendererPaused = false; ///< True if rendering is paused. bool mVsyncOn = false; diff --git a/Source/Falcor/Core/State/ComputeState.h b/Source/Falcor/Core/State/ComputeState.h index 03297bdfe..ceffb9e35 100644 --- a/Source/Falcor/Core/State/ComputeState.h +++ b/Source/Falcor/Core/State/ComputeState.h @@ -44,6 +44,7 @@ class ComputeVars; */ class FALCOR_API ComputeState : public Object { + FALCOR_OBJECT(ComputeState) public: ~ComputeState() = default; diff --git a/Source/Falcor/Core/State/GraphicsState.h b/Source/Falcor/Core/State/GraphicsState.h index 64435db95..c3b7568ef 100644 --- a/Source/Falcor/Core/State/GraphicsState.h +++ b/Source/Falcor/Core/State/GraphicsState.h @@ -51,6 +51,7 @@ class GraphicsVars; */ class FALCOR_API GraphicsState : public Object { + FALCOR_OBJECT(GraphicsState) public: virtual ~GraphicsState(); diff --git a/Source/Falcor/Core/Testbed.cpp b/Source/Falcor/Core/Testbed.cpp index eb5082f9a..ffba7e3b9 100644 --- a/Source/Falcor/Core/Testbed.cpp +++ b/Source/Falcor/Core/Testbed.cpp @@ -50,17 +50,6 @@ Testbed::~Testbed() internalShutdown(); } -/// 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 - -ref Testbed::create(const Options& options) -{ - ref pTestbed = make_ref(options); - spTestbeds.push_back(pTestbed); - return pTestbed; -} - void Testbed::run() { mShouldInterrupt = false; @@ -154,9 +143,17 @@ void Testbed::resizeFrameBuffer(uint32_t width, uint32_t height) } } -void Testbed::loadScene(const std::filesystem::path& path) +void Testbed::loadScene(const std::filesystem::path& path, SceneBuilder::Flags buildFlags) { - mpScene = Scene::create(mpDevice, path); + mpScene = SceneBuilder(mpDevice, path, Settings(), buildFlags).getScene(); + + if (mpRenderGraph) + mpRenderGraph->setScene(mpScene); +} + +void Testbed::loadSceneFromString(const std::string& scene, const std::string extension, SceneBuilder::Flags buildFlags) +{ + mpScene = SceneBuilder(mpDevice, scene.data(), scene.length(), extension, Settings(), buildFlags).getScene(); if (mpRenderGraph) mpRenderGraph->setScene(mpScene); @@ -268,6 +265,11 @@ void Testbed::internalInit(const Options& options) OSServices::start(); Threading::start(); + // Populate the data search paths from the config file, only adding those that aren't in already + auto searchDirectories = Settings::getGlobalSettings().getSearchDirectories("media"); + for (auto& it : searchDirectories.get()) + addDataDirectory(it); + // Create the device. mpDevice = make_ref(options.deviceDesc); @@ -291,7 +293,7 @@ void Testbed::internalInit(const Options& options) mpTargetFBO = Fbo::create2D(mpDevice, fboSize.x, fboSize.y, options.colorFormat, options.depthFormat); // Set global shader defines. - Program::DefineList globalDefines = { + DefineList globalDefines = { {"FALCOR_NVAPI_AVAILABLE", (FALCOR_NVAPI_AVAILABLE && mpDevice->getType() == Device::Type::D3D12) ? "1" : "0"}, #if FALCOR_NVAPI_AVAILABLE {"NV_SHADER_EXTN_SLOT", "u999"}, @@ -434,7 +436,7 @@ void Testbed::renderUI() mpGui->render(pRenderContext, mpTargetFBO, (float)mFrameRate.getLastFrameTime()); } -void Testbed::captureOutput(std::string filename, uint32_t outputIndex) +void Testbed::captureOutput(const std::filesystem::path& path, uint32_t outputIndex) { if (!mpImageProcessing) mpImageProcessing = std::make_unique(mpDevice); @@ -565,7 +567,7 @@ void Testbed::captureOutput(std::string filename, uint32_t outputIndex) if (mask == TextureChannelFlags::RGBA) flags |= Bitmap::ExportFlags::ExportAlpha; - pTex->captureToFile(0, 0, filename, fileformat, flags, false /* async */); + pTex->captureToFile(0, 0, path, fileformat, flags, false /* async */); } } @@ -576,6 +578,7 @@ FALCOR_SCRIPT_BINDING(Testbed) FALCOR_SCRIPT_BINDING_DEPENDENCY(Clock) FALCOR_SCRIPT_BINDING_DEPENDENCY(Profiler) FALCOR_SCRIPT_BINDING_DEPENDENCY(Scene) + FALCOR_SCRIPT_BINDING_DEPENDENCY(SceneBuilder) using namespace pybind11::literals; @@ -583,7 +586,8 @@ FALCOR_SCRIPT_BINDING(Testbed) testbed.def( pybind11::init( - [](uint32_t width, uint32_t height, bool create_window, Device::Type device_type, uint32_t gpu) + [](uint32_t width, uint32_t height, bool create_window, Device::Type device_type, uint32_t gpu, bool enable_debug_layers, + bool enable_aftermath) { Testbed::Options options; options.windowDesc.width = width; @@ -591,15 +595,22 @@ FALCOR_SCRIPT_BINDING(Testbed) options.createWindow = create_window; options.deviceDesc.type = device_type; options.deviceDesc.gpu = gpu; + options.deviceDesc.enableDebugLayer = enable_debug_layers; + options.deviceDesc.enableAftermath = enable_aftermath; return Testbed::create(options); } ), - "width"_a = 1920, "height"_a = 1080, "create_window"_a = false, "device_type"_a = Device::Type::Default, "gpu"_a = 0 + "width"_a = 1920, "height"_a = 1080, "create_window"_a = false, "device_type"_a = Device::Type::Default, "gpu"_a = 0, + "enable_debug_layers"_a = false, "enable_aftermath"_a = false ); testbed.def("run", &Testbed::run); testbed.def("frame", &Testbed::frame); testbed.def("resize_frame_buffer", &Testbed::resizeFrameBuffer, "width"_a, "height"_a); - testbed.def("load_scene", &Testbed::loadScene, "path"_a); + testbed.def("load_scene", &Testbed::loadScene, "path"_a, "build_flags"_a = SceneBuilder::Flags::Default); + testbed.def( + "load_scene_from_string", &Testbed::loadSceneFromString, "scene"_a, "extension"_a = "pyscene", + "build_flags"_a = SceneBuilder::Flags::Default + ); 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 @@ -609,6 +620,7 @@ FALCOR_SCRIPT_BINDING(Testbed) testbed.def_property_readonly("scene", &Testbed::getScene); testbed.def_property_readonly("clock", &Testbed::getClock); // PYTHONDEPRECATED testbed.def_property("render_graph", &Testbed::getRenderGraph, &Testbed::setRenderGraph); + testbed.def_property("show_ui", &Testbed::getShowUI, &Testbed::setShowUI); // PYTHONDEPRECATED BEGIN testbed.def( @@ -627,7 +639,7 @@ FALCOR_SCRIPT_BINDING(Testbed) "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("loadScene", &Testbed::loadScene, "path"_a, "build_flags"_a = SceneBuilder::Flags::Default); 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 diff --git a/Source/Falcor/Core/Testbed.h b/Source/Falcor/Core/Testbed.h index c332cb5df..78d02f0bf 100644 --- a/Source/Falcor/Core/Testbed.h +++ b/Source/Falcor/Core/Testbed.h @@ -33,6 +33,7 @@ #include "Core/API/Formats.h" #include "RenderGraph/RenderGraph.h" #include "Scene/Scene.h" +#include "Scene/SceneBuilder.h" #include "Utils/Image/ImageProcessing.h" #include "Utils/Timing/FrameRate.h" #include "Utils/Timing/Clock.h" @@ -48,6 +49,7 @@ class ProfilerUI; /// This is the main Falcor application available through the Python API. class Testbed : public Object, private Window::ICallbacks { + FALCOR_OBJECT(Testbed) public: struct Options { @@ -61,11 +63,11 @@ class Testbed : public Object, private Window::ICallbacks ResourceFormat depthFormat = ResourceFormat::D32Float; ///< Depth buffer format of the frame buffer. }; + static ref create(const Options& options) { return make_ref(options); } + Testbed(const Options& options = Options()); virtual ~Testbed(); - static ref create(const Options& options); - const ref& getDevice() const { return mpDevice; } /// Run the main loop. @@ -82,7 +84,13 @@ class Testbed : public Object, private Window::ICallbacks /// Resize the main frame buffer. void resizeFrameBuffer(uint32_t width, uint32_t height); - void loadScene(const std::filesystem::path& path); + void loadScene(const std::filesystem::path& path, SceneBuilder::Flags buildFlags = SceneBuilder::Flags::Default); + + void loadSceneFromString( + const std::string& sceneStr, + const std::string extension = "pyscene", + SceneBuilder::Flags buildFlags = SceneBuilder::Flags::Default + ); ref getScene() const; Clock& getClock(); @@ -93,7 +101,10 @@ class Testbed : public Object, private Window::ICallbacks void setRenderGraph(const ref& graph); const ref& getRenderGraph() const; - void captureOutput(std::string filename, uint32_t outputIndex = 0); + void captureOutput(const std::filesystem::path& path, uint32_t outputIndex = 0); + + bool getShowUI() const { return mUI.showUI; } + void setShowUI() { mUI.showUI = true; } private: // Implementation of Window::ICallbacks diff --git a/Source/Falcor/Core/Window.h b/Source/Falcor/Core/Window.h index acbb0310c..1540b6b39 100644 --- a/Source/Falcor/Core/Window.h +++ b/Source/Falcor/Core/Window.h @@ -45,6 +45,7 @@ struct GamepadState; class FALCOR_API Window : public Object { + FALCOR_OBJECT(Window) public: using ApiHandle = WindowHandle; diff --git a/Source/Falcor/RenderGraph/RenderGraph.cpp b/Source/Falcor/RenderGraph/RenderGraph.cpp index 429c3a212..7940ed35a 100644 --- a/Source/Falcor/RenderGraph/RenderGraph.cpp +++ b/Source/Falcor/RenderGraph/RenderGraph.cpp @@ -98,9 +98,9 @@ void RenderGraph::setScene(const ref& pScene) mRecompile = true; } -ref RenderGraph::createPass(const std::string& passName, const std::string& passType, const Dictionary& dict) +ref RenderGraph::createPass(const std::string& passName, const std::string& passType, const Properties& props) { - ref pPass = RenderPass::create(passType, mpDevice, dict); + ref pPass = RenderPass::create(passType, mpDevice, props); if (pPass) addPass(pPass, passName); return pPass; @@ -108,18 +108,11 @@ ref RenderGraph::createPass(const std::string& passName, const std:: uint32_t RenderGraph::addPass(const ref& 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; - } + checkArgument(pPass != nullptr, "Added pass must not be null."); + checkArgument(getPassIndex(passName) == kInvalidIndex, "Pass name '{}' already exists.", passName); + + uint32_t passIndex = mpGraph->addNode(); + mNameToIndex[passName] = passIndex; pPass->mPassChangedCB = [this]() { mRecompile = true; }; pPass->mName = passName; @@ -134,11 +127,7 @@ uint32_t RenderGraph::addPass(const ref& pPass, const std::string& p 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; - } + checkArgument(index != kInvalidIndex, "Can't remove render pass '{}'. Pass doesn't exist.", 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 @@ -161,36 +150,17 @@ void RenderGraph::removePass(const std::string& name) mRecompile = true; } -void RenderGraph::applyPassSettings(const std::string& passName, const Dictionary& dict) +void RenderGraph::updatePass(const std::string& passName, const Properties& props) { uint32_t index = getPassIndex(passName); const auto pPassIt = mNodeData.find(index); - if (pPassIt == mNodeData.end()) - { - logError("Error in RenderGraph::updatePass(). Unable to find pass " + passName); - return; - } - auto pPass = pPassIt->second.pPass; - - pPass->applySettings(dict); -} - -void RenderGraph::updatePass(const std::string& passName, const Dictionary& dict) -{ - 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; - } + checkArgument(pPassIt != mNodeData.end(), "Can't update render pass '{}'. Pass doesn't exist.", passName); // 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); + auto pPass = RenderPass::create(passTypeName, mpDevice, props); pPassIt->second.pPass = pPass; pPass->mPassChangedCB = [this]() { mRecompile = true; }; pPass->mName = pOldPass->getName(); @@ -203,12 +173,9 @@ void RenderGraph::updatePass(const std::string& passName, const Dictionary& dict const ref& RenderGraph::getPass(const std::string& name) const { uint32_t index = getPassIndex(name); - if (index == kInvalidIndex) - { - static const ref pNull; - reportError("RenderGraph::getRenderPass() - can't find a pass named '" + name + "'"); - return pNull; - } + + checkArgument(index != kInvalidIndex, "Can't find render pass '{}'.", name); + return mNodeData.at(index).pPass; } @@ -251,28 +218,22 @@ static str_pair parseFieldName(const std::string& fullname) RenderPass* RenderGraph::getRenderPassAndNamePair( const bool input, const std::string& fullname, - const std::string& errorPrefix, std::pair& nameAndField ) const { nameAndField = parseFieldName(fullname); RenderPass* pPass = getPass(nameAndField.first).get(); - if (!pPass) - { - reportError(errorPrefix + " - can't find render pass named '" + nameAndField.first + "'"); - return nullptr; - } + if (pPass == nullptr) + throw ArgumentError("Can't find render pass '{}'.", nameAndField.first); 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; - } + throw ArgumentError("Can't find field named '{}' in render pass '{}'.", nameAndField.second, nameAndField.first); + return pPass; } @@ -289,22 +250,17 @@ 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); + getRenderPassAndNamePair(false, src, srcPair); + getRenderPassAndNamePair(true, dst, dstPair); newEdge.srcField = srcPair.second; newEdge.dstField = dstPair.second; - 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" + throw ArgumentError( + "Can't add from '{}' to '{}'. 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", + src, dst ); - return kInvalidIndex; - } uint32_t srcIndex = mNameToIndex[srcPair.first]; uint32_t dstIndex = mNameToIndex[dstPair.first]; @@ -321,24 +277,19 @@ uint32_t RenderGraph::addEdge(const std::string& src, const std::string& dst) if (edgeData.dstField == newEdge.dstField) { - reportError( - "RenderGraph::addEdge() - destination '" + dst + - "' is already initialized. Please remove the existing connection before trying to add an edge" + throw ArgumentError( + "Edge destination '{}' is already initialized. Please remove the existing connection before trying to add a new edge.", + dst ); - return kInvalidIndex; } } } // 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" + throw ArgumentError( + "Can't add the edge from '{}' to '{}'. The edge will create a cycle in the graph which is not allowed.", src, dst ); - return kInvalidIndex; - } uint32_t e = mpGraph->addEdge(srcIndex, dstIndex); mEdgeData[e] = newEdge; @@ -349,14 +300,8 @@ uint32_t RenderGraph::addEdge(const std::string& src, const std::string& dst) 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); - - if (pSrc == nullptr || pDst == nullptr) - { - reportError("Unable to remove edge. Input or output node not found."); - return; - } + const RenderPass* pSrc = getRenderPassAndNamePair(false, src, srcPair); + const RenderPass* pDst = getRenderPassAndNamePair(true, dst, dstPair); uint32_t srcIndex = mNameToIndex[srcPair.first]; @@ -378,11 +323,8 @@ void RenderGraph::removeEdge(const std::string& src, const std::string& dst) void RenderGraph::removeEdge(uint32_t edgeID) { - if (mEdgeData.find(edgeID) == mEdgeData.end()) - { - reportError("Can't remove edge with index " + std::to_string(edgeID) + ". The edge doesn't exist"); - return; - } + checkArgument(mEdgeData.find(edgeID) != mEdgeData.end(), "Can't remove edge with index {}. The edge doesn't exist.", edgeID); + mEdgeData.erase(edgeID); mpGraph->removeEdge(edgeID); mRecompile = true; @@ -479,10 +421,7 @@ void RenderGraph::execute(RenderContext* pRenderContext) { std::string log; if (!compile(pRenderContext, log)) - { - reportError("Failed to compile RenderGraph\n" + log + "Ignoring RenderGraph::execute() call"); - return; - } + throw RuntimeError("Failed to compile render graph:\n{}", log); FALCOR_ASSERT(mpExe); RenderGraphExe::Context c{ @@ -564,9 +503,7 @@ void RenderGraph::update(const ref& pGraph) 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; + RenderPass* pPass = getRenderPassAndNamePair(true, name, strPair); if (pResource) { @@ -576,12 +513,7 @@ void RenderGraph::setInput(const std::string& name, const ref& pResour { 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; + throw ArgumentError("Trying to remove an external resource named '{}' but the resource wasn't registered before.", name); } mCompilerDeps.externalResources.erase(name); } @@ -592,8 +524,7 @@ void RenderGraph::setInput(const std::string& name, const ref& pResour void RenderGraph::markOutput(const std::string& name, TextureChannelFlags mask) { - if (mask == TextureChannelFlags::None) - throw RuntimeError("RenderGraph::markOutput() mask must be non-empty"); + checkArgument(mask != TextureChannelFlags::None, "Mask must be non-empty"); // Recursive call to handle '*' wildcard. if (name == "*") @@ -605,9 +536,7 @@ void RenderGraph::markOutput(const std::string& name, TextureChannelFlags mask) } str_pair strPair; - const auto& pPass = getRenderPassAndNamePair(false, name, "RenderGraph::markOutput()", strPair); - if (pPass == nullptr) - return; + getRenderPassAndNamePair(false, name, strPair); GraphOut newOut; newOut.field = strPair.second; @@ -632,9 +561,7 @@ void RenderGraph::markOutput(const std::string& name, TextureChannelFlags mask) void RenderGraph::unmarkOutput(const std::string& name) { str_pair strPair; - const auto& pPass = getRenderPassAndNamePair(false, name, "RenderGraph::unmarkOutput()", strPair); - if (pPass == nullptr) - return; + getRenderPassAndNamePair(false, name, strPair); GraphOut removeMe; removeMe.field = strPair.second; @@ -651,9 +578,8 @@ void RenderGraph::unmarkOutput(const std::string& name) 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; + getRenderPassAndNamePair(false, name, strPair); + uint32_t passIndex = getPassIndex(strPair.first); GraphOut thisOutput = {passIndex, strPair.second}; return isGraphOutput(thisOutput); @@ -662,24 +588,16 @@ bool RenderGraph::isGraphOutput(const std::string& name) const ref RenderGraph::getOutput(const std::string& name) { if (mRecompile) - { - reportError("RenderGraph::getOutput() - can't fetch an output resource because the graph wasn't successfuly compiled yet"); - return nullptr; - } + throw RuntimeError("Can't fetch the output '{}'. The graph wasn't successfuly compiled yet.", name); str_pair strPair; - RenderPass* pPass = getRenderPassAndNamePair(false, name, "RenderGraph::getOutput()", strPair); - if (!pPass) - return nullptr; + getRenderPassAndNamePair(false, name, strPair); 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; - } + throw RuntimeError("Can't fetch the output '{}'. The resource is wasn't marked as an output.", name); return mpExe->getResource(name); } @@ -777,10 +695,11 @@ FALCOR_SCRIPT_BINDING(RenderGraph) 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(); }); + renderPass.def_property_readonly("properties", [](RenderPass& self) { return self.getProperties().toPython(); }); + renderPass.def("set_properties", [](RenderPass& self, pybind11::dict dict) { self.setProperties(Properties(dict)); }); // PYTHONDEPRECATED BEGIN - renderPass.def("getDictionary", [](RenderPass& pass) { return pass.getScriptingDictionary().toPython(); }); + renderPass.def("getDictionary", [](RenderPass& pass) { return pass.getProperties().toPython(); }); // PYTHONDEPRECATED END // RenderGraph @@ -790,12 +709,12 @@ FALCOR_SCRIPT_BINDING(RenderGraph) 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)); }, + { return graph.createPass(pass_name, pass_type, Properties(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)); }, + "update_pass", [](RenderGraph& graph, const std::string& name, pybind11::dict dict) { graph.updatePass(name, Properties(dict)); }, "name"_a, "dict"_a ); renderGraph.def("add_edge", &RenderGraph::addEdge, "src"_a, "dst"_a); @@ -821,13 +740,13 @@ FALCOR_SCRIPT_BINDING(RenderGraph) renderGraph.def( "createPass", [](RenderGraph& graph, const std::string& passName, const std::string& passType, pybind11::dict dict = {}) - { return graph.createPass(passName, passType, Dictionary(dict)); }, + { return graph.createPass(passName, passType, Properties(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)); }, + "updatePass", [](RenderGraph& graph, const std::string& passName, pybind11::dict d) { graph.updatePass(passName, Properties(d)); }, "name"_a, "dict"_a ); renderGraph.def("addEdge", &RenderGraph::addEdge, "src"_a, "dst"_a); @@ -843,7 +762,7 @@ FALCOR_SCRIPT_BINDING(RenderGraph) // RenderPassLibrary const auto& globalCreateRenderPass = [](const std::string& type, pybind11::dict d = {}) { - auto pPass = RenderPass::create(type, accessActivePythonRenderGraphDevice(), Dictionary(d)); + auto pPass = RenderPass::create(type, accessActivePythonRenderGraphDevice(), Properties(d)); if (!pPass) throw RuntimeError("Can't create a render pass of type '{}'. Make sure the required plugin library was loaded.", type); return pPass; diff --git a/Source/Falcor/RenderGraph/RenderGraph.h b/Source/Falcor/RenderGraph/RenderGraph.h index 5205003a7..10b1db8a9 100644 --- a/Source/Falcor/RenderGraph/RenderGraph.h +++ b/Source/Falcor/RenderGraph/RenderGraph.h @@ -51,6 +51,7 @@ namespace Falcor */ class FALCOR_API RenderGraph : public Object { + FALCOR_OBJECT(RenderGraph) public: static const FileDialogFilterVec kFileExtensionFilters; static constexpr uint32_t kInvalidIndex = -1; @@ -89,10 +90,10 @@ class FALCOR_API RenderGraph : public Object * 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. + * @param[in] props Render pass properties. * @return The new render pass. */ - ref createPass(const std::string& passName, const std::string& passType, const Dictionary& dict); + ref createPass(const std::string& passName, const std::string& passType, const Properties& props); /** * Add a render pass. The name has to be unique, otherwise the call will be ignored. @@ -114,12 +115,7 @@ class FALCOR_API RenderGraph : public Object /** * 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); + void updatePass(const std::string& passName, const Properties& props); /** * Insert an edge from a render pass' output to a different render pass input. @@ -341,12 +337,8 @@ class FALCOR_API RenderGraph : public Object 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; + RenderPass* getRenderPassAndNamePair(const bool input, const std::string& fullname, std::pair& nameAndField) + const; uint32_t getEdge(const std::string& src, const std::string& dst); diff --git a/Source/Falcor/RenderGraph/RenderGraphIR.cpp b/Source/Falcor/RenderGraph/RenderGraphIR.cpp index 331991663..8466358ea 100644 --- a/Source/Falcor/RenderGraph/RenderGraphIR.cpp +++ b/Source/Falcor/RenderGraph/RenderGraphIR.cpp @@ -38,7 +38,9 @@ RenderGraphIR::RenderGraphIR(const std::string& name, bool newGraph) : mName(nam { if (newGraph) { - mIR += "from falcor import *\n\n"; + mIR += "from pathlib import WindowsPath, PosixPath\n"; + mIR += "from falcor import *\n"; + mIR += "\n"; mIR += "def " + getFuncName(mName) + "():\n"; mIndentation = " "; mGraphPrefix += mIndentation; @@ -47,14 +49,14 @@ RenderGraphIR::RenderGraphIR(const std::string& name, bool newGraph) : mName(nam mGraphPrefix += "g."; } -void RenderGraphIR::createPass(const std::string& passClass, const std::string& passName, const Dictionary& dictionary) +void RenderGraphIR::createPass(const std::string& passClass, const std::string& passName, const Properties& props) { - mIR += mGraphPrefix + ScriptWriter::makeFunc("create_pass", passName, passClass, dictionary); + mIR += mGraphPrefix + ScriptWriter::makeFunc("create_pass", passName, passClass, props.toPython()); } -void RenderGraphIR::updatePass(const std::string& passName, const Dictionary& dictionary) +void RenderGraphIR::updatePass(const std::string& passName, const Properties& props) { - mIR += mGraphPrefix + ScriptWriter::makeFunc("update_pass", passName, dictionary); + mIR += mGraphPrefix + ScriptWriter::makeFunc("update_pass", passName, props.toPython()); } void RenderGraphIR::removePass(const std::string& passName) @@ -92,7 +94,9 @@ void RenderGraphIR::unmarkOutput(const std::string& name) std::string RenderGraphIR::getFuncName(const std::string& graphName) { - return "render_graph_" + graphName; + std::string name = "render_graph_" + graphName; + name = replaceCharacters(name, " /\\", '_'); + return name; } } // namespace Falcor diff --git a/Source/Falcor/RenderGraph/RenderGraphIR.h b/Source/Falcor/RenderGraph/RenderGraphIR.h index e745707bb..3e71743d7 100644 --- a/Source/Falcor/RenderGraph/RenderGraphIR.h +++ b/Source/Falcor/RenderGraph/RenderGraphIR.h @@ -28,7 +28,7 @@ #pragma once #include "Core/Macros.h" #include "Core/API/Formats.h" -#include "Utils/Scripting/Dictionary.h" +#include "Utils/Properties.h" #include #include @@ -41,8 +41,8 @@ class FALCOR_API RenderGraphIR public: RenderGraphIR(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 createPass(const std::string& passClass, const std::string& passName, const Properties& props = Properties()); + void updatePass(const std::string& passName, const Properties& props); 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); diff --git a/Source/Falcor/RenderGraph/RenderGraphImportExport.cpp b/Source/Falcor/RenderGraph/RenderGraphImportExport.cpp index a37e0e4fb..089c2e806 100644 --- a/Source/Falcor/RenderGraph/RenderGraphImportExport.cpp +++ b/Source/Falcor/RenderGraph/RenderGraphImportExport.cpp @@ -136,7 +136,7 @@ std::string RenderGraphExporter::getIR(const ref& pGraph) for (const auto& node : pGraph->mNodeData) { const auto& nodeData = node.second; - ir.createPass(nodeData.pPass->getType(), nodeData.name, nodeData.pPass->getScriptingDictionary()); + ir.createPass(nodeData.pPass->getType(), nodeData.name, nodeData.pPass->getProperties()); } // Add the edges diff --git a/Source/Falcor/RenderGraph/RenderGraphUI.cpp b/Source/Falcor/RenderGraph/RenderGraphUI.cpp index fc6c08dcd..5dc95f254 100644 --- a/Source/Falcor/RenderGraph/RenderGraphUI.cpp +++ b/Source/Falcor/RenderGraph/RenderGraphUI.cpp @@ -1013,7 +1013,7 @@ void RenderGraphUI::renderUI(RenderContext* pRenderContext, Gui* pGui) // TODO -- only call this with data change if (ImGui::IsWindowFocused()) { - mpIr->updatePass(renderUIName, pPass->getScriptingDictionary()); + mpIr->updatePass(renderUIName, pPass->getProperties()); } mShouldUpdate = true; } diff --git a/Source/Falcor/RenderGraph/RenderPass.cpp b/Source/Falcor/RenderGraph/RenderPass.cpp index 5bfebe00b..734d1350d 100644 --- a/Source/Falcor/RenderGraph/RenderPass.cpp +++ b/Source/Falcor/RenderGraph/RenderPass.cpp @@ -50,12 +50,12 @@ ref RenderData::getTexture(const std::string_view name) const return pResource ? pResource->asTexture() : nullptr; } -ref RenderPass::create(std::string_view type, ref pDevice, const Dictionary& dict, PluginManager& pm) +ref RenderPass::create(std::string_view type, ref pDevice, const Properties& props, 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, pDevice, dict); + return pm.createClass(type, pDevice, props); } } // namespace Falcor diff --git a/Source/Falcor/RenderGraph/RenderPass.h b/Source/Falcor/RenderGraph/RenderPass.h index 4f94a1ef5..9c0efd144 100644 --- a/Source/Falcor/RenderGraph/RenderPass.h +++ b/Source/Falcor/RenderGraph/RenderPass.h @@ -34,8 +34,8 @@ #include "Core/API/Resource.h" #include "Core/API/Texture.h" #include "Scene/Scene.h" +#include "Utils/Properties.h" #include "Utils/InternalDictionary.h" -#include "Utils/Scripting/Dictionary.h" #include "Utils/Scripting/ScriptBindings.h" #include "Utils/UI/Gui.h" #include @@ -118,8 +118,9 @@ class FALCOR_API RenderData */ class FALCOR_API RenderPass : public Object { + FALCOR_OBJECT(RenderPass) public: - using PluginCreate = std::function(ref pDevice, const Dictionary& dict)>; + using PluginCreate = std::function(ref pDevice, const Properties& props)>; struct PluginInfo { std::string desc; ///< Brief textual description of what the render pass does. @@ -143,7 +144,7 @@ class FALCOR_API RenderPass : public Object static ref create( std::string_view type, ref pDevice, - const Dictionary& dict = {}, + const Properties& props = {}, PluginManager& pm = PluginManager::instance() ); @@ -178,9 +179,14 @@ class FALCOR_API RenderPass : public Object virtual void execute(RenderContext* pRenderContext, const RenderData& renderData) = 0; /** - * Get a dictionary that can be used to reconstruct the object + * Set the render pass properties. */ - virtual Dictionary getScriptingDictionary() { return {}; } + virtual void setProperties(const Properties& props) {} + + /** + * Get the render pass properties. + */ + virtual Properties getProperties() const { return {}; } /** * Render the pass's UI @@ -228,11 +234,6 @@ class FALCOR_API RenderPass : public Object */ 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 */ diff --git a/Source/Falcor/RenderGraph/RenderPassHelpers.cpp b/Source/Falcor/RenderGraph/RenderPassHelpers.cpp index ab786d61f..ce8eba8db 100644 --- a/Source/Falcor/RenderGraph/RenderPassHelpers.cpp +++ b/Source/Falcor/RenderGraph/RenderPassHelpers.cpp @@ -50,14 +50,4 @@ uint2 RenderPassHelpers::calculateIOSize(const IOSize selection, const uint2 fix 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); -} } // namespace Falcor diff --git a/Source/Falcor/RenderGraph/RenderPassHelpers.h b/Source/Falcor/RenderGraph/RenderPassHelpers.h index b5ff5a1b8..1817a479b 100644 --- a/Source/Falcor/RenderGraph/RenderPassHelpers.h +++ b/Source/Falcor/RenderGraph/RenderPassHelpers.h @@ -29,6 +29,7 @@ #include "RenderPass.h" #include "RenderPassReflection.h" #include "Core/Macros.h" +#include "Core/Enum.h" #include "Core/API/Formats.h" #include "Core/API/RenderContext.h" #include "Core/Program/Program.h" @@ -54,14 +55,17 @@ struct FALCOR_API RenderPassHelpers 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"}, - }; + FALCOR_ENUM_INFO( + IOSize, + { + {IOSize::Default, "Default"}, + {IOSize::Fixed, "Fixed"}, + {IOSize::Full, "Full"}, + {IOSize::Half, "Half"}, + {IOSize::Quarter, "Quarter"}, + {IOSize::Double, "Double"}, + } + ); /** * Helper for calculating desired I/O size in pixels based on selected mode. @@ -69,6 +73,8 @@ struct FALCOR_API RenderPassHelpers static uint2 calculateIOSize(const IOSize selection, const uint2 fixedSize, const uint2 windowSize); }; +FALCOR_ENUM_REGISTER(RenderPassHelpers::IOSize); + // TODO: Move below out of the global scope, e.g. into RenderPassHelpers struct. // TODO: Update render passes to use addRenderPass*() helpers. @@ -98,13 +104,13 @@ using ChannelList = std::vector; * @param[in] prefix Prefix used for defines. * @return Returns a list of defines to add to the progrem. */ -inline Program::DefineList getValidResourceDefines( +inline DefineList getValidResourceDefines( const ChannelList& channels, const RenderData& renderData, const std::string& prefix = "is_valid_" ) { - Program::DefineList defines; + DefineList defines; for (const auto& desc : channels) { diff --git a/Source/Falcor/RenderPasses/ResolvePass.h b/Source/Falcor/RenderPasses/ResolvePass.h index b66352ed7..4885fd9bd 100644 --- a/Source/Falcor/RenderPasses/ResolvePass.h +++ b/Source/Falcor/RenderPasses/ResolvePass.h @@ -34,7 +34,6 @@ namespace Falcor { class RenderContext; -class Dictionary; class RenderData; class FALCOR_API ResolvePass : public RenderPass @@ -44,7 +43,7 @@ class FALCOR_API ResolvePass : public RenderPass // 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); } + static ref create(ref pDevice, const Properties& props = {}) { return make_ref(pDevice); } ResolvePass(ref pDevice); diff --git a/Source/Falcor/Rendering/Lights/EmissiveLightSampler.cpp b/Source/Falcor/Rendering/Lights/EmissiveLightSampler.cpp index 6df8ff4fe..ab1a5caf4 100644 --- a/Source/Falcor/Rendering/Lights/EmissiveLightSampler.cpp +++ b/Source/Falcor/Rendering/Lights/EmissiveLightSampler.cpp @@ -31,17 +31,8 @@ namespace Falcor { - Program::DefineList EmissiveLightSampler::getDefines() const + DefineList EmissiveLightSampler::getDefines() const { return {{ "_EMISSIVE_LIGHT_SAMPLER_TYPE", std::to_string((uint32_t)mType) }}; } - - FALCOR_SCRIPT_BINDING(EmissiveLightSampler) - { - pybind11::enum_ type(m, "EmissiveLightSamplerType"); - type.value("Uniform", EmissiveLightSamplerType::Uniform); - type.value("LightBVH", EmissiveLightSamplerType::LightBVH); - type.value("Power", EmissiveLightSamplerType::Power); - type.value("Null", EmissiveLightSamplerType::Null); - } } diff --git a/Source/Falcor/Rendering/Lights/EmissiveLightSampler.h b/Source/Falcor/Rendering/Lights/EmissiveLightSampler.h index a623a7c1b..c75af5d77 100644 --- a/Source/Falcor/Rendering/Lights/EmissiveLightSampler.h +++ b/Source/Falcor/Rendering/Lights/EmissiveLightSampler.h @@ -28,7 +28,7 @@ #pragma once #include "EmissiveLightSamplerType.slangh" #include "Core/Macros.h" -#include "Core/Program/Program.h" +#include "Core/Program/DefineList.h" #include "Scene/Scene.h" namespace Falcor @@ -55,7 +55,7 @@ namespace Falcor /** Return a list of shader defines to use this light sampler. * \return Returns a list of shader defines. */ - virtual Program::DefineList getDefines() const; + virtual DefineList getDefines() const; /** Bind the light sampler data to a given shader var */ diff --git a/Source/Falcor/Rendering/Lights/EmissiveLightSampler.slang b/Source/Falcor/Rendering/Lights/EmissiveLightSampler.slang index a89bc5cd7..5cd16132c 100644 --- a/Source/Falcor/Rendering/Lights/EmissiveLightSampler.slang +++ b/Source/Falcor/Rendering/Lights/EmissiveLightSampler.slang @@ -65,6 +65,8 @@ __exported import Utils.Sampling.SampleGeneratorInterface; { bool sampleLight(const float3 posW, const float3 normalW, const bool upperHemisphere, inout S sg, out TriangleLightSample ls) { + ls = {}; + return false; } diff --git a/Source/Falcor/Rendering/Lights/EmissiveLightSamplerType.slangh b/Source/Falcor/Rendering/Lights/EmissiveLightSamplerType.slangh index 2531d11d7..3af57720c 100644 --- a/Source/Falcor/Rendering/Lights/EmissiveLightSamplerType.slangh +++ b/Source/Falcor/Rendering/Lights/EmissiveLightSamplerType.slangh @@ -42,6 +42,14 @@ enum class EmissiveLightSamplerType : uint32_t Null = 0xff, }; +FALCOR_ENUM_INFO(EmissiveLightSamplerType, { + { EmissiveLightSamplerType::Uniform, "Uniform" }, + { EmissiveLightSamplerType::LightBVH, "LightBVH" }, + { EmissiveLightSamplerType::Power, "Power" }, + { EmissiveLightSamplerType::Null, "Null" }, +}); +FALCOR_ENUM_REGISTER(EmissiveLightSamplerType); + // For shader specialization in EmissiveLightSampler.slang we can't use the enums. // TODO: Find a way to remove this workaround. #define EMISSIVE_LIGHT_SAMPLER_UNIFORM 0 diff --git a/Source/Falcor/Rendering/Lights/EmissiveUniformSampler.cpp b/Source/Falcor/Rendering/Lights/EmissiveUniformSampler.cpp index 50ebdaa59..da03339fe 100644 --- a/Source/Falcor/Rendering/Lights/EmissiveUniformSampler.cpp +++ b/Source/Falcor/Rendering/Lights/EmissiveUniformSampler.cpp @@ -26,7 +26,6 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **************************************************************************/ #include "EmissiveUniformSampler.h" -#include "Utils/Scripting/ScriptBindings.h" namespace Falcor { @@ -37,14 +36,4 @@ namespace Falcor // Make sure the light collection is created. mpScene->getLightCollection(pRenderContext); } - - FALCOR_SCRIPT_BINDING(EmissiveUniformSampler) - { - // TODO use a nested class in the bindings when supported. - ScriptBindings::SerializableStruct options(m, "EmissiveUniformSamplerOptions"); -#define field(f_) field(#f_, &EmissiveUniformSampler::Options::f_) - // TODO - //options.field(usePreintegration); -#undef field - } } diff --git a/Source/Falcor/Rendering/Lights/EmissiveUniformSampler.h b/Source/Falcor/Rendering/Lights/EmissiveUniformSampler.h index 2e15624db..388e90ed1 100644 --- a/Source/Falcor/Rendering/Lights/EmissiveUniformSampler.h +++ b/Source/Falcor/Rendering/Lights/EmissiveUniformSampler.h @@ -28,6 +28,7 @@ #pragma once #include "EmissiveLightSampler.h" #include "Core/Macros.h" +#include "Utils/Properties.h" #include "Scene/Lights/LightCollection.h" namespace Falcor @@ -46,6 +47,11 @@ namespace Falcor { // TODO //bool usePreintegration = true; ///< Use pre-integrated flux per triangle to guide BVH build/sampling. Only relevant if mUseBVHTree == true. + + template + void serialize(Archive& ar) + { + } }; /** Creates a EmissiveUniformSampler for a given scene. diff --git a/Source/Falcor/Rendering/Lights/LightBVHBuilder.cpp b/Source/Falcor/Rendering/Lights/LightBVHBuilder.cpp index 2f1931e92..d7acf8bf8 100644 --- a/Source/Falcor/Rendering/Lights/LightBVHBuilder.cpp +++ b/Source/Falcor/Rendering/Lights/LightBVHBuilder.cpp @@ -30,7 +30,6 @@ #include "Core/Errors.h" #include "Utils/Logger.h" #include "Utils/Timing/Profiler.h" -#include "Utils/Scripting/ScriptBindings.h" #include "Utils/Math/MathConstants.slangh" #include @@ -217,13 +216,6 @@ namespace const float3 dims = max(float3(epsilon), bb.extent()); return dims.x * dims.y * dims.z; } - - const Gui::DropdownList kSplitHeuristicList = - { - { (uint32_t)LightBVHBuilder::SplitHeuristic::Equal, "Equal" }, - { (uint32_t)LightBVHBuilder::SplitHeuristic::BinnedSAH, "Binned SAH" }, - { (uint32_t)LightBVHBuilder::SplitHeuristic::BinnedSAOH, "Binned SAOH" } - }; } namespace Falcor @@ -329,7 +321,7 @@ namespace Falcor optionsChanged |= widget.checkbox("Allow refitting", options.allowRefitting); optionsChanged |= widget.var("Max triangle count per leaf", options.maxTriangleCountPerLeaf, 1u, kMaxLeafTriangleCount); - optionsChanged |= widget.dropdown("Split heuristic", kSplitHeuristicList, (uint32_t&)options.splitHeuristicSelection); + optionsChanged |= widget.dropdown("Split heuristic", options.splitHeuristicSelection); if (auto splitGroup = widget.group("Split Options", true)) { @@ -888,28 +880,4 @@ namespace Falcor throw RuntimeError("Unsupported SplitHeuristic: {}", static_cast(heuristic)); } } - - FALCOR_SCRIPT_BINDING(LightBVHBuilder) - { - pybind11::enum_ splitHeuristic(m, "SplitHeuristic"); - splitHeuristic.value("Equal", LightBVHBuilder::SplitHeuristic::Equal); - splitHeuristic.value("BinnedSAH", LightBVHBuilder::SplitHeuristic::BinnedSAH); - splitHeuristic.value("BinnedSAOH", LightBVHBuilder::SplitHeuristic::BinnedSAOH); - - // TODO use a nested class in the bindings when supported. - ScriptBindings::SerializableStruct options(m, "LightBVHBuilderOptions"); -#define field(f_) field(#f_, &LightBVHBuilder::Options::f_) - options.field(splitHeuristicSelection); - options.field(maxTriangleCountPerLeaf); - options.field(binCount); - options.field(volumeEpsilon); - options.field(splitAlongLargest); - options.field(useVolumeOverSA); - options.field(useLeafCreationCost); - options.field(createLeavesASAP); - options.field(allowRefitting); - options.field(usePreintegration); - options.field(useLightingCones); -#undef field - } } diff --git a/Source/Falcor/Rendering/Lights/LightBVHBuilder.h b/Source/Falcor/Rendering/Lights/LightBVHBuilder.h index 220274023..9a98f518b 100644 --- a/Source/Falcor/Rendering/Lights/LightBVHBuilder.h +++ b/Source/Falcor/Rendering/Lights/LightBVHBuilder.h @@ -28,6 +28,7 @@ #pragma once #include "LightBVH.h" #include "Core/Macros.h" +#include "Core/Enum.h" #include "Utils/Math/AABB.h" #include "Utils/Math/Vector.h" #include "Utils/UI/Gui.h" @@ -55,6 +56,12 @@ namespace Falcor BinnedSAOH = 2u, ///< Split the input according to SAOH (Estévez Conty et al, 2018); the input is binned for speeding up the SAOH computation. }; + FALCOR_ENUM_INFO(SplitHeuristic, { + { SplitHeuristic::Equal, "Equal" }, + { SplitHeuristic::BinnedSAH, "BinnedSAH" }, + { SplitHeuristic::BinnedSAOH, "BinnedSAOH" }, + }); + /** Light BVH builder configuration options. Note if you change options, please update FALCOR_SCRIPT_BINDING in LightBVHBuilder.cpp */ @@ -71,6 +78,22 @@ namespace Falcor bool allowRefitting = true; ///< Rather than always rebuilding the BVH from scratch, keep the hierarchy but update the bounds and lighting cones. bool usePreintegration = true; ///< Use pre-integration for culling out emissive triangles and use their flux when computing the splits. Only valid when using the BinnedSAOH split heuristic. bool useLightingCones = true; ///< Use lighting cones when computing the splits. Only valid when using the BinnedSAOH split heuristic. + + template + void serialize(Archive& ar) + { + ar("splitHeuristicSelection", splitHeuristicSelection); + ar("maxTriangleCountPerLeaf", maxTriangleCountPerLeaf); + ar("binCount", binCount); + ar("volumeEpsilon", volumeEpsilon); + ar("splitAlongLargest", splitAlongLargest); + ar("useVolumeOverSA", useVolumeOverSA); + ar("useLeafCreationCost", useLeafCreationCost); + ar("createLeavesASAP", createLeavesASAP); + ar("allowRefitting", allowRefitting); + ar("usePreintegration", usePreintegration); + ar("useLightingCones", useLightingCones); + } }; /** Constructor. @@ -179,4 +202,6 @@ namespace Falcor // Configuration Options mOptions; }; + + FALCOR_ENUM_REGISTER(LightBVHBuilder::SplitHeuristic); } diff --git a/Source/Falcor/Rendering/Lights/LightBVHSampler.cpp b/Source/Falcor/Rendering/Lights/LightBVHSampler.cpp index 5ddd01f02..cddd7a2b0 100644 --- a/Source/Falcor/Rendering/Lights/LightBVHSampler.cpp +++ b/Source/Falcor/Rendering/Lights/LightBVHSampler.cpp @@ -29,22 +29,11 @@ #include "Core/Assert.h" #include "Core/Errors.h" #include "Utils/Timing/Profiler.h" -#include "Utils/Scripting/ScriptBindings.h" #include #include namespace Falcor { - namespace - { - const Gui::DropdownList kSolidAngleBoundList = - { - { (uint32_t)SolidAngleBoundMethod::Sphere, "Sphere" }, - { (uint32_t)SolidAngleBoundMethod::BoxToCenter, "Cone around center dir" }, - { (uint32_t)SolidAngleBoundMethod::BoxToAverage, "Cone around average dir" }, - }; - } - bool LightBVHSampler::update(RenderContext* pRenderContext) { FALCOR_PROFILE(pRenderContext, "LightBVHSampler::update"); @@ -75,7 +64,7 @@ namespace Falcor return samplerChanged; } - Program::DefineList LightBVHSampler::getDefines() const + DefineList LightBVHSampler::getDefines() const { // Call the base class first. auto defines = EmissiveLightSampler::getDefines(); @@ -121,7 +110,7 @@ namespace Falcor optionsChanged |= traversalGroup.checkbox("Disable node flux", mOptions.disableNodeFlux); optionsChanged |= traversalGroup.checkbox("Use triangle uniform sampling", mOptions.useUniformTriangleSampling); - if (traversalGroup.dropdown("Solid Angle Bound", kSolidAngleBoundList, (uint32_t&)mOptions.solidAngleBoundMethod)) + if (traversalGroup.dropdown("Solid Angle Bound", mOptions.solidAngleBoundMethod)) { mNeedsRebuild = optionsChanged = true; } @@ -148,23 +137,4 @@ namespace Falcor mpBVHBuilder = std::make_unique(mOptions.buildOptions); mpBVH = std::make_unique(pScene->getDevice(), pScene->getLightCollection(pRenderContext)); } - - FALCOR_SCRIPT_BINDING(LightBVHSampler) - { - pybind11::enum_ solidAngleBoundMethod(m, "SolidAngleBoundMethod"); - solidAngleBoundMethod.value("BoxToAverage", SolidAngleBoundMethod::BoxToAverage); - solidAngleBoundMethod.value("BoxToCenter", SolidAngleBoundMethod::BoxToCenter); - solidAngleBoundMethod.value("Sphere", SolidAngleBoundMethod::Sphere); - - // TODO use a nested class in the bindings when supported. - ScriptBindings::SerializableStruct options(m, "LightBVHSamplerOptions"); -#define field(f_) field(#f_, &LightBVHSampler::Options::f_) - options.field(buildOptions); - options.field(useBoundingCone); - options.field(useLightingCone); - options.field(disableNodeFlux); - options.field(useUniformTriangleSampling); - options.field(solidAngleBoundMethod); -#undef field - } } diff --git a/Source/Falcor/Rendering/Lights/LightBVHSampler.h b/Source/Falcor/Rendering/Lights/LightBVHSampler.h index b555fde47..0e2fc47d9 100644 --- a/Source/Falcor/Rendering/Lights/LightBVHSampler.h +++ b/Source/Falcor/Rendering/Lights/LightBVHSampler.h @@ -31,6 +31,7 @@ #include "LightBVHBuilder.h" #include "LightBVHSamplerSharedDefinitions.slang" #include "Core/Macros.h" +#include "Utils/Properties.h" #include "Utils/Math/AABB.h" #include "Scene/Lights/LightCollection.h" #include @@ -69,6 +70,17 @@ namespace Falcor // Note: Empty constructor needed for clang due to the use of the nested struct constructor in the parent constructor. Options() {} + + template + void serialize(Archive& ar) + { + ar("buildOptions", buildOptions); + ar("useBoundingCone", useBoundingCone); + ar("useLightingCone", useLightingCone); + ar("disableNodeFlux", disableNodeFlux); + ar("useUniformTriangleSampling", useUniformTriangleSampling); + ar("solidAngleBoundMethod", solidAngleBoundMethod); + } }; /** Creates a LightBVHSampler for a given scene. @@ -88,7 +100,7 @@ namespace Falcor /** Return a list of shader defines to use this light sampler. * \return Returns a list of shader defines. */ - virtual Program::DefineList getDefines() const override; + virtual DefineList getDefines() const override; /** Bind the light sampler data to a given shader variable. \param[in] var Shader variable. diff --git a/Source/Falcor/Rendering/Lights/LightBVHSampler.slang b/Source/Falcor/Rendering/Lights/LightBVHSampler.slang index 7e39a8eec..cdf8fcf32 100644 --- a/Source/Falcor/Rendering/Lights/LightBVHSampler.slang +++ b/Source/Falcor/Rendering/Lights/LightBVHSampler.slang @@ -74,6 +74,8 @@ struct LightBVHSampler : IEmissiveLightSampler */ bool sampleLight(const float3 posW, const float3 normalW, const bool upperHemisphere, inout S sg, out TriangleLightSample ls) { + ls = {}; + if (gScene.lightCollection.isEmpty()) return false; // Stochastically traverse the light BVH to pick a triangle. @@ -178,6 +180,8 @@ struct LightBVHSampler : IEmissiveLightSampler */ float boundCosineTerm(const float3 posW, const float3 normalW, const float3 center, const float3 extent, out float cosThetaCone) { + cosThetaCone = {}; + float3 coneDir; float sinThetaCone = 0.f; @@ -370,6 +374,9 @@ struct LightBVHSampler : IEmissiveLightSampler */ bool pickTriangle(const float3 posW, const float3 normalW, const bool upperHemisphere, const uint nodeIndex, const float u, out float pdf, out uint triangleIndex) { + pdf = {}; + triangleIndex = {}; + const LeafNode node = _lightBVH.getLeafNode(nodeIndex); if (kUseUniformTriangleSampling) @@ -422,6 +429,9 @@ struct LightBVHSampler : IEmissiveLightSampler */ bool sampleLightViaBVH(const float3 posW, const float3 normalW, const bool upperHemisphere, float u, out float pdf, out uint triangleIndex) { + pdf = {}; + triangleIndex = {}; + // Traverse BVH to select a leaf node with N triangles based on estimated probabilities during traversal. float leafPdf; uint leafNodeIndex; diff --git a/Source/Falcor/Rendering/Lights/LightBVHSamplerSharedDefinitions.slang b/Source/Falcor/Rendering/Lights/LightBVHSamplerSharedDefinitions.slang index 1108f0983..15c1d66d8 100644 --- a/Source/Falcor/Rendering/Lights/LightBVHSamplerSharedDefinitions.slang +++ b/Source/Falcor/Rendering/Lights/LightBVHSamplerSharedDefinitions.slang @@ -37,4 +37,11 @@ enum class SolidAngleBoundMethod : uint32_t Sphere, }; +FALCOR_ENUM_INFO(SolidAngleBoundMethod, { + { SolidAngleBoundMethod::BoxToCenter, "BoxToCenter" }, + { SolidAngleBoundMethod::BoxToAverage, "BoxToAverage" }, + { SolidAngleBoundMethod::Sphere, "Sphere" }, +}); +FALCOR_ENUM_REGISTER(SolidAngleBoundMethod); + END_NAMESPACE_FALCOR diff --git a/Source/Falcor/Rendering/Materials/BSDFs/BeerBTDF.slang b/Source/Falcor/Rendering/Materials/BSDFs/BeerBTDF.slang index 29bc7e786..54f168c51 100644 --- a/Source/Falcor/Rendering/Materials/BSDFs/BeerBTDF.slang +++ b/Source/Falcor/Rendering/Materials/BSDFs/BeerBTDF.slang @@ -49,7 +49,8 @@ struct BeerBTDF : IBSDF // Default initialization to avoid divergence at returns. wo = {}; weight = {}; - pdf = 0.f; + pdf = {}; + lobeType = {}; if (wi.z > kMinCosTheta) return false; diff --git a/Source/Falcor/Rendering/Materials/BSDFs/DielectricPlateBSDF.slang b/Source/Falcor/Rendering/Materials/BSDFs/DielectricPlateBSDF.slang index 085620f7f..b4bdb7c7e 100644 --- a/Source/Falcor/Rendering/Materials/BSDFs/DielectricPlateBSDF.slang +++ b/Source/Falcor/Rendering/Materials/BSDFs/DielectricPlateBSDF.slang @@ -74,6 +74,11 @@ struct DielectricPlateBSDF : IBSDF bool sample(float3 wiLocal, out float3 wo, out float pdf, out float3 weight, out uint lobeType, inout S sg) { + wo = {}; + pdf = {}; + weight = {}; + lobeType = {}; + float relEta = eta; const float etaRefract = 1.005f; diff --git a/Source/Falcor/Rendering/Materials/BSDFs/DiffuseSpecularBRDF.slang b/Source/Falcor/Rendering/Materials/BSDFs/DiffuseSpecularBRDF.slang index 66c29b5be..b4f4104c7 100644 --- a/Source/Falcor/Rendering/Materials/BSDFs/DiffuseSpecularBRDF.slang +++ b/Source/Falcor/Rendering/Materials/BSDFs/DiffuseSpecularBRDF.slang @@ -66,6 +66,11 @@ struct DiffuseSpecularBRDF : IBSDF bool sample(const float3 wi, out float3 wo, out float pdf, out float3 weight, out uint lobeType, inout S sg) { + wo = {}; + pdf = {}; + weight = {}; + lobeType = {}; + float diffuseWeight = max(0.f, luminance(diffuse)); float specularWeight = max(0.001f, luminance(specular)); float norm = 1.f / (diffuseWeight + specularWeight); diff --git a/Source/Falcor/Rendering/Materials/BSDFs/SheenBSDF.slang b/Source/Falcor/Rendering/Materials/BSDFs/SheenBSDF.slang index eb9e64006..bb45e1661 100644 --- a/Source/Falcor/Rendering/Materials/BSDFs/SheenBSDF.slang +++ b/Source/Falcor/Rendering/Materials/BSDFs/SheenBSDF.slang @@ -101,7 +101,8 @@ struct SheenBSDF : IBSDF // Default initialization to avoid divergence at returns. wo = {}; weight = {}; - pdf = 0.f; + pdf = {}; + lobeType = {}; float3 albedoT = evalAlbedo(wi, LobeType::All).transmission; diff --git a/Source/Falcor/Rendering/Materials/BSDFs/SimpleBTDF.slang b/Source/Falcor/Rendering/Materials/BSDFs/SimpleBTDF.slang index 18e4f2579..893bb1e4e 100644 --- a/Source/Falcor/Rendering/Materials/BSDFs/SimpleBTDF.slang +++ b/Source/Falcor/Rendering/Materials/BSDFs/SimpleBTDF.slang @@ -44,7 +44,8 @@ struct SimpleBTDF : IBSDF // Default initialization to avoid divergence at returns. wo = {}; weight = {}; - pdf = 0.f; + pdf = {}; + lobeType = {}; if (wi.z > kMinCosTheta) return false; diff --git a/Source/Falcor/Rendering/Materials/ClothMaterial.slang b/Source/Falcor/Rendering/Materials/ClothMaterial.slang index 2ae8ef85e..fd3332477 100644 --- a/Source/Falcor/Rendering/Materials/ClothMaterial.slang +++ b/Source/Falcor/Rendering/Materials/ClothMaterial.slang @@ -54,7 +54,7 @@ struct ClothMaterial : MaterialBase, IMaterial // Calculate the specular reflectance for dielectrics from the IoR, as in the Disney BSDF [Burley 2015]. // UE4 uses 0.08 multiplied by a default specular value of 0.5, hence F0=0.04 as default. The default IoR=1.5 gives the same result. - float f = (data.IoR - 1.f) / (data.IoR + 1.f); + float f = (header.getIoR() - 1.f) / (header.getIoR() + 1.f); float F0 = f * f; brdf.diffuseColor = baseColor; diff --git a/Source/Falcor/Rendering/Materials/HairMaterial.slang b/Source/Falcor/Rendering/Materials/HairMaterial.slang index 9b2950081..ea35c3d19 100644 --- a/Source/Falcor/Rendering/Materials/HairMaterial.slang +++ b/Source/Falcor/Rendering/Materials/HairMaterial.slang @@ -44,8 +44,9 @@ struct HairMaterial : MaterialBase, IMaterial HairChiang16Data d = {}; - d.IoR = data.IoR; - d.eta = sd.frontFacing ? (sd.IoR / data.IoR) : (data.IoR / sd.IoR); + float16_t IoR = header.getIoR(); + d.IoR = IoR; + d.eta = sd.frontFacing ? (sd.IoR / IoR) : (IoR / sd.IoR); // Sample base color. const float3 baseColor = ms.sampleTexture(data.texBaseColor, s, sd.uv, data.baseColor, lod).rgb; diff --git a/Source/Falcor/Rendering/Materials/LayeredBSDF.slang b/Source/Falcor/Rendering/Materials/LayeredBSDF.slang index 97d12bd60..c5f8a8056 100644 --- a/Source/Falcor/Rendering/Materials/LayeredBSDF.slang +++ b/Source/Falcor/Rendering/Materials/LayeredBSDF.slang @@ -167,6 +167,11 @@ struct LayeredBSDF : IBSDF bool sample(const float3 wi, out float3 wo, out float pdfOut, out float3 weightOut, out uint lobeType, inout S sg) { + wo = {}; + pdfOut = {}; + weightOut = {}; + lobeType = {}; + bool enterTop = wi.z > 0.0f; BSDFSample bs; diff --git a/Source/Falcor/Rendering/Materials/PBRT/PBRTCoatedConductorMaterial.slang b/Source/Falcor/Rendering/Materials/PBRT/PBRTCoatedConductorMaterial.slang index 8f6a28338..ecf9f4439 100644 --- a/Source/Falcor/Rendering/Materials/PBRT/PBRTCoatedConductorMaterial.slang +++ b/Source/Falcor/Rendering/Materials/PBRT/PBRTCoatedConductorMaterial.slang @@ -46,7 +46,7 @@ struct PBRTCoatedConductorMaterial : MaterialBase, IMaterial // RG: Interface X&Y roughness // BA: Conductor X&Y roughness const float4 roughnesses = ms.sampleTexture(data.texSpecular, s, sd.uv, data.specular, lod).rgba; - float eta = 1.0f / data.IoR; + float eta = 1.0f / header.getIoR(); // Compute final shading frame. ShadingFrame sf = sd.frame; diff --git a/Source/Falcor/Rendering/Materials/PBRT/PBRTCoatedDiffuseMaterial.slang b/Source/Falcor/Rendering/Materials/PBRT/PBRTCoatedDiffuseMaterial.slang index 3fa3d9039..6aef083db 100644 --- a/Source/Falcor/Rendering/Materials/PBRT/PBRTCoatedDiffuseMaterial.slang +++ b/Source/Falcor/Rendering/Materials/PBRT/PBRTCoatedDiffuseMaterial.slang @@ -43,7 +43,7 @@ struct PBRTCoatedDiffuseMaterial : MaterialBase, IMaterial const float3 baseColor = ms.sampleTexture(data.texBaseColor, s, sd.uv, data.baseColor, lod).rgb; // RG: Interface X&Y roughness const float2 roughness = ms.sampleTexture(data.texSpecular, s, sd.uv, data.specular, lod).rg; - float eta = 1.0f / data.IoR; + float eta = 1.0f / header.getIoR(); // Compute final shading frame. ShadingFrame sf = sd.frame; diff --git a/Source/Falcor/Rendering/Materials/PBRT/PBRTConductorMaterialInstance.slang b/Source/Falcor/Rendering/Materials/PBRT/PBRTConductorMaterialInstance.slang index 5e13e5fef..bb3189e9d 100644 --- a/Source/Falcor/Rendering/Materials/PBRT/PBRTConductorMaterialInstance.slang +++ b/Source/Falcor/Rendering/Materials/PBRT/PBRTConductorMaterialInstance.slang @@ -48,6 +48,11 @@ struct PBRTConductorBSDF : IBSDF bool sample(float3 wiLocal, out float3 wo, out float pdf, out float3 weight, out uint lobeType, inout S sg) { + wo = {}; + pdf = {}; + weight = {}; + lobeType = {}; + if (wiLocal.z < 0.0f) return false; if (D.isSingular()) diff --git a/Source/Falcor/Rendering/Materials/PBRT/PBRTDielectricMaterial.slang b/Source/Falcor/Rendering/Materials/PBRT/PBRTDielectricMaterial.slang index 58d8c22f5..fb223bdeb 100644 --- a/Source/Falcor/Rendering/Materials/PBRT/PBRTDielectricMaterial.slang +++ b/Source/Falcor/Rendering/Materials/PBRT/PBRTDielectricMaterial.slang @@ -41,7 +41,8 @@ struct PBRTDielectricMaterial : MaterialBase, IMaterial SamplerState s = ms.getTextureSampler(header.getDefaultTextureSamplerID()); const float2 roughness = ms.sampleTexture(data.texSpecular, s, sd.uv, data.specular, lod).rg; - float eta = !sd.frontFacing ? 1.0f / data.IoR : data.IoR; + float16_t IoR = header.getIoR(); + float eta = !sd.frontFacing ? 1.0f / IoR : IoR; // Compute final shading frame. // Do not flip the shading normal for backfacing hits. The BSDF handles transmission from both sides. diff --git a/Source/Falcor/Rendering/Materials/PBRT/PBRTDielectricMaterialInstance.slang b/Source/Falcor/Rendering/Materials/PBRT/PBRTDielectricMaterialInstance.slang index e097e8a96..3301784a2 100644 --- a/Source/Falcor/Rendering/Materials/PBRT/PBRTDielectricMaterialInstance.slang +++ b/Source/Falcor/Rendering/Materials/PBRT/PBRTDielectricMaterialInstance.slang @@ -54,6 +54,11 @@ struct PBRTDielectricBSDF : IBSDF bool sample(float3 wiLocal, out float3 wo, out float pdf, out float3 weight, out uint lobeType, inout S sg) { + wo = {}; + pdf = {}; + weight = {}; + lobeType = {}; + float relEta = 1.0 / eta; if (eta == 1 || D.isSingular()) diff --git a/Source/Falcor/Rendering/Materials/StandardMaterial.slang b/Source/Falcor/Rendering/Materials/StandardMaterial.slang index 84b7b9d38..e5972aa9c 100644 --- a/Source/Falcor/Rendering/Materials/StandardMaterial.slang +++ b/Source/Falcor/Rendering/Materials/StandardMaterial.slang @@ -53,7 +53,8 @@ struct StandardMaterial : MaterialBase, IMaterial StandardBSDFData d = {}; - d.eta = sd.frontFacing ? (sd.IoR / data.IoR) : (data.IoR / sd.IoR); + float16_t IoR = header.getIoR(); + d.eta = sd.frontFacing ? (sd.IoR / IoR) : (IoR / sd.IoR); d.diffuseTransmission = data.diffuseTransmission; d.specularTransmission = data.specularTransmission; if (d.diffuseTransmission > 0.f || d.specularTransmission > 0.f) @@ -63,7 +64,7 @@ struct StandardMaterial : MaterialBase, IMaterial // Calculate the specular reflectance for dielectrics from the IoR, as in the Disney BSDF [Burley 2015]. // UE4 uses 0.08 multiplied by a default specular value of 0.5, hence F0=0.04 as default. The default IoR=1.5 gives the same result. - float f = (data.IoR - 1.f) / (data.IoR + 1.f); + float f = (IoR - 1.f) / (IoR + 1.f); float F0 = f * f; // Sample base color. diff --git a/Source/Falcor/Rendering/Materials/TexLODTypes.slang b/Source/Falcor/Rendering/Materials/TexLODTypes.slang index 87888ce9c..7510507fc 100644 --- a/Source/Falcor/Rendering/Materials/TexLODTypes.slang +++ b/Source/Falcor/Rendering/Materials/TexLODTypes.slang @@ -42,8 +42,17 @@ enum class TexLODMode : uint32_t Mip0 = 0, RayCones = 1, RayDiffs = 2, + Stochastic = 3, }; +FALCOR_ENUM_INFO(TexLODMode, { + { TexLODMode::Mip0, "Mip0" }, + { TexLODMode::RayCones, "RayCones" }, + { TexLODMode::RayDiffs, "RayDiffs" }, + { TexLODMode::Stochastic, "Stochastic" }, +}); +FALCOR_ENUM_REGISTER(TexLODMode); + /** This enum is shared between CPU/GPU. Both Combo and Unified avoid computing surface spread from the G-buffer. Combo uses ray differentials to compute the spread at the first hit and then average edge curvature for all hits after that. @@ -55,4 +64,10 @@ enum class RayConeMode : uint32_t Unified = 1, }; +FALCOR_ENUM_INFO(RayConeMode, { + { RayConeMode::Combo, "Combo" }, + { RayConeMode::Unified, "Unified" }, +}); +FALCOR_ENUM_REGISTER(RayConeMode); + END_NAMESPACE_FALCOR diff --git a/Source/Falcor/Rendering/RTXDI/RTXDI.cpp b/Source/Falcor/Rendering/RTXDI/RTXDI.cpp index ccde10abf..eaf50bc6c 100644 --- a/Source/Falcor/Rendering/RTXDI/RTXDI.cpp +++ b/Source/Falcor/Rendering/RTXDI/RTXDI.cpp @@ -31,7 +31,6 @@ #include "Utils/Logger.h" #include "Utils/Math/Common.h" #include "Utils/Timing/Profiler.h" -#include "Utils/Scripting/ScriptBindings.h" #include // TODO C++20: Replace with namespace Falcor @@ -72,22 +71,6 @@ namespace Falcor const uint32_t kMinMaxHistoryLength = 0; const uint32_t kMaxMaxHistoryLength = 50; - Gui::DropdownList kModeList = - { - { (uint32_t)RTXDI::Mode::NoResampling, "No resampling" }, - { (uint32_t)RTXDI::Mode::SpatialResampling, "Spatial resampling only" }, - { (uint32_t)RTXDI::Mode::TemporalResampling, "Temporal resampling only" }, - { (uint32_t)RTXDI::Mode::SpatiotemporalResampling, "Spatiotemporal resampling" }, - }; - - Gui::DropdownList kBiasCorrectionList = - { - { (uint32_t)RTXDI::BiasCorrection::Off, "Off" }, - { (uint32_t)RTXDI::BiasCorrection::Basic, "Basic" }, - { (uint32_t)RTXDI::BiasCorrection::Pairwise, "Pairwise" }, - { (uint32_t)RTXDI::BiasCorrection::RayTraced, "RayTraced" }, - }; - template void validateRange(T& value, T minValue, T maxValue, const char* name) { @@ -161,9 +144,9 @@ namespace Falcor mOptions = newOptions; } - Program::DefineList RTXDI::getDefines() const + DefineList RTXDI::getDefines() const { - Program::DefineList defines; + DefineList defines; #if FALCOR_HAS_RTXDI defines.add("RTXDI_INSTALLED", "1"); #else @@ -671,7 +654,7 @@ namespace Falcor // Helper for creating compute passes. auto createComputePass = [&](const std::string& file, const std::string& entryPoint) { - Shader::DefineList defines = mpScene->getSceneDefines(); + DefineList defines = mpScene->getSceneDefines(); defines.add("RTXDI_INSTALLED", "1"); Program::Desc desc; @@ -822,7 +805,13 @@ namespace Falcor bool useTemporalResampling = (mOptions.mode == Mode::TemporalResampling || mOptions.mode == Mode::SpatiotemporalResampling); bool useSpatialResampling = (mOptions.mode == Mode::SpatialResampling || mOptions.mode == Mode::SpatiotemporalResampling); - changed |= widget.dropdown("Mode", kModeList, reinterpret_cast(options.mode)); + changed |= widget.dropdown("Mode", options.mode); + widget.tooltip("Mode.\n\n" + "NoResampling: No resampling (Talbot RIS from EGSR 2005 \"Importance Resampling for Global Illumination\").\n" + "SpatialResampling: Spatial resampling only.\n" + "TemporalResampling: Temporal resampling only.\n" + "SpatiotemporalResampling: Spatiotemporal resampling." + ); if (auto group = widget.group("Light presampling", false)) { @@ -868,10 +857,11 @@ namespace Falcor { if (auto group = widget.group("Resampling", false)) { - changed |= group.dropdown("Bias correction", kBiasCorrectionList, reinterpret_cast(options.biasCorrection)); + changed |= group.dropdown("Bias correction", options.biasCorrection); group.tooltip("Bias correction mode.\n\n" "Off: Use (1/M) normalization, which is very biased but also very fast.\n" "Basic: Use MIS-like normalization but assume that every sample is visible.\n" + "Pairwise: Use pairwise MIS normalization. Assumes every sample is visibile.\n" "RayTraced: Use MIS-like normalization with visibility rays. Unbiased.\n"); changed |= group.var("Depth threshold", options.depthThreshold, 0.0f, 1.0f, 0.001f); @@ -933,53 +923,4 @@ namespace Falcor return changed; } - - FALCOR_SCRIPT_BINDING(RTXDI) - { - pybind11::enum_ mode(m, "RTXDIMode"); - mode.value("NoResampling", RTXDI::Mode::NoResampling); - mode.value("SpatialResampling", RTXDI::Mode::SpatialResampling); - mode.value("TemporalResampling", RTXDI::Mode::TemporalResampling); - mode.value("SpatiotemporalResampling", RTXDI::Mode::SpatiotemporalResampling); - - pybind11::enum_ biasCorrection(m, "RTXDIBiasCorrection"); - biasCorrection.value("Off", RTXDI::BiasCorrection::Off); - biasCorrection.value("Basic", RTXDI::BiasCorrection::Basic); - biasCorrection.value("Pairwise", RTXDI::BiasCorrection::Pairwise); - biasCorrection.value("RayTraced", RTXDI::BiasCorrection::RayTraced); - - ScriptBindings::SerializableStruct options(m, "RTXDIOptions"); -#define field(f_) field(#f_, &RTXDI::Options::f_) - options.field(mode); - - options.field(presampledTileCount); - options.field(presampledTileSize); - options.field(storeCompactLightInfo); - - options.field(localLightCandidateCount); - options.field(infiniteLightCandidateCount); - options.field(envLightCandidateCount); - options.field(brdfCandidateCount); - options.field(brdfCutoff); - options.field(testCandidateVisibility); - - options.field(biasCorrection); - options.field(depthThreshold); - options.field(normalThreshold); - - options.field(samplingRadius); - options.field(spatialSampleCount); - options.field(spatialIterations); - - options.field(maxHistoryLength); - options.field(boilingFilterStrength); - - options.field(rayEpsilon); - options.field(useEmissiveTextures); - - options.field(enableVisibilityShortcut); - options.field(enablePermutationSampling); -#undef field - } - } diff --git a/Source/Falcor/Rendering/RTXDI/RTXDI.h b/Source/Falcor/Rendering/RTXDI/RTXDI.h index 0b8f65d0e..3d4c7074a 100644 --- a/Source/Falcor/Rendering/RTXDI/RTXDI.h +++ b/Source/Falcor/Rendering/RTXDI/RTXDI.h @@ -27,6 +27,8 @@ **************************************************************************/ #pragma once #include "Core/Macros.h" +#include "Core/Enum.h" +#include "Utils/Properties.h" #include "Utils/Debug/PixelDebug.h" #include "Scene/Scene.h" #include @@ -94,16 +96,30 @@ namespace Falcor SpatiotemporalResampling = 4, ///< Spatiotemporal resampling. }; + FALCOR_ENUM_INFO(Mode, { + { Mode::NoResampling, "NoResampling" }, + { Mode::SpatialResampling, "SpatialResampling" }, + { Mode::TemporalResampling, "TemporalResampling" }, + { Mode::SpatiotemporalResampling, "SpatiotemporalResampling" }, + }); + /** Bias correction modes. */ enum class BiasCorrection { Off = 0, ///< Use (1/M) normalization, which is very biased but also very fast. Basic = 1, ///< Use MIS-like normalization but assume that every sample is visible. - Pairwise = 2, ///< Use pairwise MIS normalization. Assumes every sample is visible. + Pairwise = 2, ///< Use pairwise MIS normalization. Assumes every sample is visible. RayTraced = 3, ///< Use MIS-like normalization with visibility rays. Unbiased. }; + FALCOR_ENUM_INFO(BiasCorrection, { + { BiasCorrection::Off, "Off" }, + { BiasCorrection::Basic, "Basic" }, + { BiasCorrection::Pairwise, "Pairwise" }, + { BiasCorrection::RayTraced, "RayTraced" }, + }); + /** Configuration options, with generally reasonable defaults. */ struct Options @@ -155,6 +171,41 @@ namespace Falcor // Note: Empty constructor needed for clang due to the use of the nested struct constructor in the parent constructor. Options() {} + + template + void serialize(Archive& ar) + { + ar("mode", mode); + + ar("presampledTileCount", presampledTileCount); + ar("presampledTileSize", presampledTileSize); + ar("storeCompactLightInfo", storeCompactLightInfo); + + ar("localLightCandidateCount", localLightCandidateCount); + ar("infiniteLightCandidateCount", infiniteLightCandidateCount); + ar("envLightCandidateCount", envLightCandidateCount); + ar("brdfCandidateCount", brdfCandidateCount); + ar("brdfCutoff", brdfCutoff); + ar("testCandidateVisibility", testCandidateVisibility); + + ar("biasCorrection", biasCorrection); + ar("depthThreshold", depthThreshold); + ar("normalThreshold", normalThreshold); + + ar("samplingRadius", samplingRadius); + ar("spatialSampleCount", spatialSampleCount); + ar("spatialIterations", spatialIterations); + + ar("maxHistoryLength", maxHistoryLength); + ar("boilingFilterStrength", boilingFilterStrength); + + ar("rayEpsilon", rayEpsilon); + + ar("useEmissiveTextures", useEmissiveTextures); + + ar("enableVisibilityShortcut", enableVisibilityShortcut); + ar("enablePermutationSampling", enablePermutationSampling); + } }; static_assert(std::is_trivially_copyable() , "Options needs to be trivially copyable"); @@ -188,7 +239,7 @@ namespace Falcor /** Get a list of shader defines for using the RTXDI sampler. \return List of shader defines. */ - Program::DefineList getDefines() const; + DefineList getDefines() const; /** Bind the RTXDI sampler to a given shader var. Note: RTXDI is always bound to the global "gRTXDI" variable, so we expect a root shader variable here. @@ -342,4 +393,7 @@ namespace Falcor #endif // FALCOR_HAS_RTXDI }; + + FALCOR_ENUM_REGISTER(RTXDI::Mode); + FALCOR_ENUM_REGISTER(RTXDI::BiasCorrection); } diff --git a/Source/Falcor/Rendering/RTXDI/RTXDIApplicationBridge.slangh b/Source/Falcor/Rendering/RTXDI/RTXDIApplicationBridge.slangh index d72712940..4edbc681d 100644 --- a/Source/Falcor/Rendering/RTXDI/RTXDIApplicationBridge.slangh +++ b/Source/Falcor/Rendering/RTXDI/RTXDIApplicationBridge.slangh @@ -357,6 +357,8 @@ float3 tangentToWorld(RAB_Surface surface, float3 h) */ bool RAB_GetSurfaceBrdfSample(RAB_Surface surface, inout RAB_RandomSamplerState rng, out float3 dir) { + dir = {}; + float3 rand; rand.x = RAB_GetNextRandom(rng); rand.y = RAB_GetNextRandom(rng); diff --git a/Source/Falcor/Rendering/Volumes/GridVolumeSampler.cpp b/Source/Falcor/Rendering/Volumes/GridVolumeSampler.cpp index 97cda2689..4b7f0af78 100644 --- a/Source/Falcor/Rendering/Volumes/GridVolumeSampler.cpp +++ b/Source/Falcor/Rendering/Volumes/GridVolumeSampler.cpp @@ -27,25 +27,9 @@ **************************************************************************/ #include "GridVolumeSampler.h" #include "Core/Assert.h" -#include "Utils/Scripting/ScriptBindings.h" namespace Falcor { - namespace - { - const Gui::DropdownList kTransmittanceEstimatorList = - { - { (uint32_t)TransmittanceEstimator::DeltaTracking, "Delta Tracking (Global Majorant)" }, - { (uint32_t)TransmittanceEstimator::RatioTracking, "Ratio Tracking (Global Majorant)" }, - { (uint32_t)TransmittanceEstimator::RatioTrackingLocalMajorant, "Ratio Tracking (Local Majorants)" }, - }; - const Gui::DropdownList kDistanceSamplerList = - { - { (uint32_t)DistanceSampler::DeltaTracking, "Delta Tracking (Global Majorant)" }, - { (uint32_t)DistanceSampler::DeltaTrackingLocalMajorant, "Delta Tracking (Local Majorants)" }, - }; - } - GridVolumeSampler::GridVolumeSampler(RenderContext* pRenderContext, ref pScene, const Options& options) : mpScene(pScene) , mOptions(options) @@ -53,9 +37,9 @@ namespace Falcor FALCOR_ASSERT(pScene); } - Program::DefineList GridVolumeSampler::getDefines() const + DefineList GridVolumeSampler::getDefines() const { - Program::DefineList defines; + DefineList defines; defines.add("GRID_VOLUME_SAMPLER_USE_BRICKEDGRID", std::to_string((uint32_t)mOptions.useBrickedGrid)); defines.add("GRID_VOLUME_SAMPLER_TRANSMITTANCE_ESTIMATOR", std::to_string((uint32_t)mOptions.transmittanceEstimator)); defines.add("GRID_VOLUME_SAMPLER_DISTANCE_SAMPLER", std::to_string((uint32_t)mOptions.distanceSampler)); @@ -80,13 +64,13 @@ namespace Falcor } dirty = true; } - if (widget.dropdown("Transmittance Estimator", kTransmittanceEstimatorList, reinterpret_cast(mOptions.transmittanceEstimator))) + if (widget.dropdown("Transmittance Estimator", mOptions.transmittanceEstimator)) { // Enable bricked grid if the chosen mode requires it. if (requiresBrickedGrid(mOptions.transmittanceEstimator)) mOptions.useBrickedGrid = true; dirty = true; } - if (widget.dropdown("Distance Sampler", kDistanceSamplerList, reinterpret_cast(mOptions.distanceSampler))) + if (widget.dropdown("Distance Sampler", mOptions.distanceSampler)) { // Enable bricked grid if the chosen mode requires it. if (requiresBrickedGrid(mOptions.distanceSampler)) mOptions.useBrickedGrid = true; @@ -95,24 +79,4 @@ namespace Falcor return dirty; } - - FALCOR_SCRIPT_BINDING(GridVolumeSampler) - { - pybind11::enum_ transmittanceEstimator(m, "TransmittanceEstimator"); - transmittanceEstimator.value("DeltaTracking", TransmittanceEstimator::DeltaTracking); - transmittanceEstimator.value("RatioTracking", TransmittanceEstimator::RatioTracking); - transmittanceEstimator.value("RatioTrackingLocalMajorant", TransmittanceEstimator::RatioTrackingLocalMajorant); - - pybind11::enum_ distanceSampler(m, "DistanceSampler"); - distanceSampler.value("DeltaTracking", DistanceSampler::DeltaTracking); - distanceSampler.value("DeltaTrackingLocalMajorant", DistanceSampler::DeltaTrackingLocalMajorant); - - // TODO use a nested class in the bindings when supported. - ScriptBindings::SerializableStruct options(m, "GridVolumeSamplerOptions"); -#define field(f_) field(#f_, &GridVolumeSampler::Options::f_) - options.field(transmittanceEstimator); - options.field(distanceSampler); - options.field(useBrickedGrid); -#undef field - } } diff --git a/Source/Falcor/Rendering/Volumes/GridVolumeSampler.h b/Source/Falcor/Rendering/Volumes/GridVolumeSampler.h index c73264ff0..2da719371 100644 --- a/Source/Falcor/Rendering/Volumes/GridVolumeSampler.h +++ b/Source/Falcor/Rendering/Volumes/GridVolumeSampler.h @@ -28,6 +28,8 @@ #pragma once #include "GridVolumeSamplerParams.slang" #include "Core/Macros.h" +#include "Core/Program/DefineList.h" +#include "Utils/Properties.h" #include "Utils/UI/Gui.h" #include "Scene/Scene.h" @@ -52,6 +54,14 @@ namespace Falcor // Note: Empty constructor needed for clang due to the use of the nested struct constructor in the parent constructor. Options() {} + + template + void serialize(Archive& ar) + { + ar("transmittanceEstimator", transmittanceEstimator); + ar("distanceSampler", distanceSampler); + ar("useBrickedGrid", useBrickedGrid); + } }; /** Create a new object. @@ -65,7 +75,7 @@ namespace Falcor /** Get a list of shader defines for using the grid volume sampler. \return Returns a list of defines. */ - Program::DefineList getDefines() const; + DefineList getDefines() const; /** Bind the grid volume sampler to a given shader variable. \param[in] var Shader variable. diff --git a/Source/Falcor/Rendering/Volumes/GridVolumeSamplerParams.slang b/Source/Falcor/Rendering/Volumes/GridVolumeSamplerParams.slang index cd134d16d..fc116e6a2 100644 --- a/Source/Falcor/Rendering/Volumes/GridVolumeSamplerParams.slang +++ b/Source/Falcor/Rendering/Volumes/GridVolumeSamplerParams.slang @@ -39,12 +39,25 @@ enum class TransmittanceEstimator RatioTrackingLocalMajorant, // BrickedGrid only. }; +FALCOR_ENUM_INFO(TransmittanceEstimator, { + { TransmittanceEstimator::DeltaTracking, "DeltaTracking" }, + { TransmittanceEstimator::RatioTracking, "RatioTracking" }, + { TransmittanceEstimator::RatioTrackingLocalMajorant, "RatioTrackingLocalMajorant" }, +}); +FALCOR_ENUM_REGISTER(TransmittanceEstimator); + enum class DistanceSampler { DeltaTracking, DeltaTrackingLocalMajorant, // BrickedGrid only. }; +FALCOR_ENUM_INFO(DistanceSampler, { + { DistanceSampler::DeltaTracking, "DeltaTracking" }, + { DistanceSampler::DeltaTrackingLocalMajorant, "DeltaTrackingLocalMajorant" }, +}); +FALCOR_ENUM_REGISTER(DistanceSampler); + #ifdef HOST_CODE inline bool requiresBrickedGrid(DistanceSampler d) { return d == DistanceSampler::DeltaTrackingLocalMajorant; } inline bool requiresBrickedGrid(TransmittanceEstimator t) { return t == TransmittanceEstimator::RatioTrackingLocalMajorant; } diff --git a/Source/Falcor/Rendering/Volumes/HomogeneousVolumeSampler.slang b/Source/Falcor/Rendering/Volumes/HomogeneousVolumeSampler.slang index b7ab8dd11..38d37b3db 100644 --- a/Source/Falcor/Rendering/Volumes/HomogeneousVolumeSampler.slang +++ b/Source/Falcor/Rendering/Volumes/HomogeneousVolumeSampler.slang @@ -65,6 +65,8 @@ struct HomogeneousVolumeSampler */ static bool sampleDistance(const float3 sigmaA, const float3 sigmaS, const float3 thp, inout S sg, out DistanceSample ds) { + ds = {}; + if (all(sigmaS == 0.f)) return false; const float3 sigmaT = sigmaA + sigmaS; diff --git a/Source/Falcor/Rendering/Volumes/PhaseFunction.slang b/Source/Falcor/Rendering/Volumes/PhaseFunction.slang index 501c83e25..78cbc9d5a 100644 --- a/Source/Falcor/Rendering/Volumes/PhaseFunction.slang +++ b/Source/Falcor/Rendering/Volumes/PhaseFunction.slang @@ -197,16 +197,16 @@ struct DualHenyeyGreensteinPhaseFunction : IPhaseFunction cosTheta = (1.f + meancosineHero * meancosineHero - sqr * sqr) / (2.f * meancosineHero); } - float selectionweight; - float3 weight; + float selectionWeight = {}; + weight = {}; if (u.z <= avgW0) { - selectionweight = avgW0; + selectionWeight = avgW0; weight = w0; } else { - selectionweight = (1.0 - avgW0); + selectionWeight = (1.0 - avgW0); weight = 1.f - w0; } @@ -220,7 +220,7 @@ struct DualHenyeyGreensteinPhaseFunction : IPhaseFunction wo = normalize(w.x * T + w.y * B + w.z * dir); pdf = evalHenyeyGreenstein(cosTheta, meancosineHero); - weight = (evalHenyeyGreenstein(cosTheta, meancosineSelect) * weight / evalHenyeyGreenstein(dot(wi, wo), meancosineHero)) / selectionweight; + weight = (evalHenyeyGreenstein(cosTheta, meancosineSelect) * weight / evalHenyeyGreenstein(dot(wi, wo), meancosineHero)) / selectionWeight; return true; } diff --git a/Source/Falcor/Scene/Animation/Animatable.h b/Source/Falcor/Scene/Animation/Animatable.h index 97d185a2d..70799e8c2 100644 --- a/Source/Falcor/Scene/Animation/Animatable.h +++ b/Source/Falcor/Scene/Animation/Animatable.h @@ -38,6 +38,7 @@ namespace Falcor */ class FALCOR_API Animatable : public Object { + FALCOR_OBJECT(Animatable) public: virtual ~Animatable() {} diff --git a/Source/Falcor/Scene/Animation/AnimatedVertexCache.cpp b/Source/Falcor/Scene/Animation/AnimatedVertexCache.cpp index 019f7278b..7e448d084 100644 --- a/Source/Falcor/Scene/Animation/AnimatedVertexCache.cpp +++ b/Source/Falcor/Scene/Animation/AnimatedVertexCache.cpp @@ -477,7 +477,7 @@ namespace Falcor { FALCOR_ASSERT(!mCachedMeshes.empty()); - Program::DefineList defines; + DefineList defines; defines.add("MESH_KEYFRAME_COUNT", std::to_string(mMeshKeyframeCount)); mpMeshVertexUpdatePass = ComputePass::create(mpDevice, "Scene/Animation/UpdateMeshVertices.slang", "main", defines); @@ -495,7 +495,7 @@ namespace Falcor { FALCOR_ASSERT(mCurveLSSCount > 0); - Program::DefineList defines; + DefineList defines; defines.add("CURVE_KEYFRAME_COUNT", std::to_string(mCurveKeyframeTimes.size())); mpCurveVertexUpdatePass = ComputePass::create(mpDevice, kUpdateCurveVerticesFilename, "main", defines); @@ -520,7 +520,7 @@ namespace Falcor { FALCOR_ASSERT(mCurvePolyTubeCount > 0); - Program::DefineList defines; + DefineList defines; defines.add("CURVE_KEYFRAME_COUNT", std::to_string(mCurveKeyframeTimes.size())); mpCurvePolyTubeVertexUpdatePass = ComputePass::create(mpDevice, kUpdateCurvePolyTubeVerticesFilename, "main", defines); diff --git a/Source/Falcor/Scene/Animation/Animation.h b/Source/Falcor/Scene/Animation/Animation.h index dd281e919..44ec4bc4a 100644 --- a/Source/Falcor/Scene/Animation/Animation.h +++ b/Source/Falcor/Scene/Animation/Animation.h @@ -43,6 +43,7 @@ namespace Falcor class FALCOR_API Animation : public Object { + FALCOR_OBJECT(Animation) public: enum class InterpolationMode { diff --git a/Source/Falcor/Scene/Camera/Camera.h b/Source/Falcor/Scene/Camera/Camera.h index bb59cb8b7..2615b72ec 100644 --- a/Source/Falcor/Scene/Camera/Camera.h +++ b/Source/Falcor/Scene/Camera/Camera.h @@ -45,6 +45,7 @@ namespace Falcor */ class FALCOR_API Camera : public Animatable { + FALCOR_OBJECT(Camera) public: // Default dimensions of full frame cameras and 35mm film static constexpr float kDefaultFrameHeight = 24.f; diff --git a/Source/Falcor/Scene/Displacement/DisplacementMapping.slang b/Source/Falcor/Scene/Displacement/DisplacementMapping.slang index bf20edd42..76b9e009a 100644 --- a/Source/Falcor/Scene/Displacement/DisplacementMapping.slang +++ b/Source/Falcor/Scene/Displacement/DisplacementMapping.slang @@ -415,6 +415,9 @@ void rayBilinearPatchIntersectionTest( bool traceHeightMapEstimated(const DisplacementData displacementData, float3 startPoint, float3 endPoint, out float intersectedT, out float intersectedHeight) { + intersectedT = {}; + intersectedHeight = {}; + const float entryHeightData = displacementData.readValue(startPoint.xy); #if DISPLACEMENT_TWO_SIDED == 0 if (startPoint.z < entryHeightData) diff --git a/Source/Falcor/Scene/HitInfo.cpp b/Source/Falcor/Scene/HitInfo.cpp index 97813e381..c5964ab75 100644 --- a/Source/Falcor/Scene/HitInfo.cpp +++ b/Source/Falcor/Scene/HitInfo.cpp @@ -105,10 +105,10 @@ namespace Falcor } - Shader::DefineList HitInfo::getDefines() const + DefineList HitInfo::getDefines() const { FALCOR_ASSERT((mTypeBits + mInstanceIDBits) <= 32 && mPrimitiveIndexBits <= 32); - Shader::DefineList defines; + DefineList defines; defines.add("HIT_INFO_DEFINES", "1"); defines.add("HIT_INFO_USE_COMPRESSION", mUseCompression ? "1" : "0"); defines.add("HIT_INFO_TYPE_BITS", std::to_string(mTypeBits)); diff --git a/Source/Falcor/Scene/HitInfo.h b/Source/Falcor/Scene/HitInfo.h index 690214956..12d84f366 100644 --- a/Source/Falcor/Scene/HitInfo.h +++ b/Source/Falcor/Scene/HitInfo.h @@ -28,7 +28,7 @@ #pragma once #include "Core/Macros.h" #include "Core/API/Formats.h" -#include "Core/API/Shader.h" +#include "Core/Program/DefineList.h" namespace Falcor { @@ -56,7 +56,7 @@ namespace Falcor /** Returns defines needed packing/unpacking a HitInfo struct. */ - Shader::DefineList getDefines() const; + DefineList getDefines() const; /** Returns the resource format required for encoding packed hit information. */ diff --git a/Source/Falcor/Scene/Importer.cpp b/Source/Falcor/Scene/Importer.cpp index 0ea328891..855d62048 100644 --- a/Source/Falcor/Scene/Importer.cpp +++ b/Source/Falcor/Scene/Importer.cpp @@ -30,7 +30,7 @@ namespace Falcor { - std::unique_ptr Importer::create(std::string extension, const PluginManager& pm) + std::unique_ptr Importer::create(std::string_view extension, const PluginManager& pm) { for (const auto& [type, info] : pm.getInfos()) if (std::find(info.extensions.begin(), info.extensions.end(), extension) != info.extensions.end()) @@ -46,6 +46,11 @@ namespace Falcor return extensions; } + void Importer::importSceneFromMemory(const void* buffer, size_t byteSize, std::string_view extension, SceneBuilder& builder, const pybind11::dict& dict) + { + throw RuntimeError("Not implemented."); + } + FALCOR_SCRIPT_BINDING(Importer) { pybind11::register_exception(m, "ImporterError"); diff --git a/Source/Falcor/Scene/Importer.h b/Source/Falcor/Scene/Importer.h index 2ff82f85b..4b79e201d 100644 --- a/Source/Falcor/Scene/Importer.h +++ b/Source/Falcor/Scene/Importer.h @@ -105,6 +105,16 @@ namespace Falcor */ virtual void importScene(const std::filesystem::path& path, SceneBuilder& builder, const pybind11::dict& dict) = 0; + /** Import a scene from memory. + \param[in] buffer Memory buffer. + \param[in] byteSize Size in bytes of memory buffer. + \param[in] extension File extension for the format the scene is stored in. + \param[in] builder Scene builder. + \param[in] dict Optional dictionary. + Throws an ImporterError if something went wrong. + */ + virtual void importSceneFromMemory(const void* buffer, size_t byteSize, std::string_view extension, SceneBuilder& builder, const pybind11::dict& dict); + // Importer factory /** Create an importer for a file of an asset with the given file extension. @@ -112,7 +122,7 @@ namespace Falcor \param pm Plugin manager. \return Returns an instance of the importer or nullptr if no compatible importer was found. */ - static std::unique_ptr create(std::string extension, const PluginManager& pm = PluginManager::instance()); + static std::unique_ptr create(std::string_view extension, const PluginManager& pm = PluginManager::instance()); /** Return a list of supported file extensions by the current set of loaded importer plugins. */ diff --git a/Source/Falcor/Scene/Intersection.slang b/Source/Falcor/Scene/Intersection.slang index 5283952b3..a11a26033 100644 --- a/Source/Falcor/Scene/Intersection.slang +++ b/Source/Falcor/Scene/Intersection.slang @@ -55,6 +55,9 @@ struct DisplacedTriangleMeshIntersector */ static bool intersect(const Ray ray, const GeometryInstanceID instanceID, const uint primitiveIndex, out Attribs attribs, out float t) { + attribs = {}; + t = {}; + const uint materialID = gScene.getMaterialID(instanceID); const uint3 indices = gScene.getIndices(instanceID, primitiveIndex); const StaticVertexData vertices[3] = { gScene.getVertex(indices[0]), gScene.getVertex(indices[1]), gScene.getVertex(indices[2]) }; @@ -156,6 +159,9 @@ struct SDFGridIntersector */ static bool intersect(const Ray ray, const GeometryInstanceID instanceID, const uint primitiveID, out SDFGridHitData hitData, out float t) { + hitData ={}; + t = {}; + const GeometryInstanceData instance = gScene.getGeometryInstance(instanceID); SDFGrid sdfGrid; gScene.getSDFGrid(instanceID, sdfGrid); diff --git a/Source/Falcor/Scene/Lights/EnvMap.h b/Source/Falcor/Scene/Lights/EnvMap.h index 4369ee8dd..76113b489 100644 --- a/Source/Falcor/Scene/Lights/EnvMap.h +++ b/Source/Falcor/Scene/Lights/EnvMap.h @@ -45,6 +45,7 @@ namespace Falcor */ class FALCOR_API EnvMap : public Object { + FALCOR_OBJECT(EnvMap) public: virtual ~EnvMap() = default; @@ -138,6 +139,7 @@ namespace Falcor Changes mChanges = Changes::None; + friend class Scene; friend class SceneCache; }; diff --git a/Source/Falcor/Scene/Lights/Light.h b/Source/Falcor/Scene/Lights/Light.h index 6e1575f5d..acf2790ed 100644 --- a/Source/Falcor/Scene/Lights/Light.h +++ b/Source/Falcor/Scene/Lights/Light.h @@ -44,6 +44,7 @@ namespace Falcor */ class FALCOR_API Light : public Animatable { + FALCOR_OBJECT(Light) public: virtual ~Light() = default; diff --git a/Source/Falcor/Scene/Lights/LightCollection.cpp b/Source/Falcor/Scene/Lights/LightCollection.cpp index f33c6f4ab..685f1f08a 100644 --- a/Source/Falcor/Scene/Lights/LightCollection.cpp +++ b/Source/Falcor/Scene/Lights/LightCollection.cpp @@ -62,7 +62,7 @@ namespace Falcor initIntegrator(pRenderContext, *mpScene); // Create programs for building/updating the mesh lights. - Shader::DefineList defines = mpScene->getSceneDefines(); + 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); diff --git a/Source/Falcor/Scene/Lights/LightCollection.h b/Source/Falcor/Scene/Lights/LightCollection.h index c43e93691..5dc2bb8f3 100644 --- a/Source/Falcor/Scene/Lights/LightCollection.h +++ b/Source/Falcor/Scene/Lights/LightCollection.h @@ -56,6 +56,7 @@ namespace Falcor */ class FALCOR_API LightCollection : public Object { + FALCOR_OBJECT(LightCollection) public: enum class UpdateFlags : uint32_t { diff --git a/Source/Falcor/Scene/Lights/LightProfile.h b/Source/Falcor/Scene/Lights/LightProfile.h index 86b990366..d5a76d36b 100644 --- a/Source/Falcor/Scene/Lights/LightProfile.h +++ b/Source/Falcor/Scene/Lights/LightProfile.h @@ -43,6 +43,7 @@ namespace Falcor class FALCOR_API LightProfile : public Object { + FALCOR_OBJECT(LightProfile) public: static ref createFromIesProfile(ref pDevice, const std::filesystem::path& filename, bool normalize); diff --git a/Source/Falcor/Scene/Material/BasicMaterial.cpp b/Source/Falcor/Scene/Material/BasicMaterial.cpp index cd0794b31..fa108a5b2 100644 --- a/Source/Falcor/Scene/Material/BasicMaterial.cpp +++ b/Source/Falcor/Scene/Material/BasicMaterial.cpp @@ -53,6 +53,7 @@ namespace Falcor : Material(pDevice, name, type) { mHeader.setIsBasicMaterial(true); + mHeader.setIoR(1.5h); // Setup common texture slots. mTextureSlotInfo[(uint32_t)TextureSlot::Displacement] = { "displacement", TextureChannelFlags::RGB, false }; @@ -259,15 +260,6 @@ namespace Falcor } } - void BasicMaterial::setIndexOfRefraction(float IoR) - { - if (mData.IoR != (float16_t)IoR) - { - mData.IoR = (float16_t)IoR; - markUpdates(UpdateFlags::DataChanged); - } - } - void BasicMaterial::setDefaultTextureSampler(const ref& pSampler) { if (pSampler != mpDefaultSampler) @@ -599,7 +591,6 @@ namespace Falcor compare_vec_field(specular); compare_vec_field(emissive); compare_field(emissiveFactor); - compare_field(IoR); compare_field(diffuseTransmission); compare_field(specularTransmission); compare_vec_field(transmission); diff --git a/Source/Falcor/Scene/Material/BasicMaterial.h b/Source/Falcor/Scene/Material/BasicMaterial.h index a27dd4f9e..4110cc78f 100644 --- a/Source/Falcor/Scene/Material/BasicMaterial.h +++ b/Source/Falcor/Scene/Material/BasicMaterial.h @@ -43,6 +43,7 @@ namespace Falcor */ class FALCOR_API BasicMaterial : public Material { + FALCOR_OBJECT(BasicMaterial) public: /** Render the UI. \return True if the material was modified. @@ -236,14 +237,6 @@ namespace Falcor */ NormalMapType getNormalMapType() const { return mData.getNormalMapType(); } - /** Set the index of refraction. - */ - void setIndexOfRefraction(float IoR); - - /** Get the index of refraction. - */ - float getIndexOfRefraction() const { return (float)mData.IoR; } - /** Returns the material data struct. */ const BasicMaterialData& getData() const { return mData; } diff --git a/Source/Falcor/Scene/Material/BasicMaterialData.slang b/Source/Falcor/Scene/Material/BasicMaterialData.slang index 72a0190e4..40dcb1769 100644 --- a/Source/Falcor/Scene/Material/BasicMaterialData.slang +++ b/Source/Falcor/Scene/Material/BasicMaterialData.slang @@ -47,7 +47,7 @@ BEGIN_NAMESPACE_FALCOR */ struct BasicMaterialData { - // MaterialHeader (8B) is stored just before this struct in memory. + // MaterialHeader (16B) is stored just before this struct in memory. 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). @@ -60,7 +60,7 @@ struct BasicMaterialData float16_t diffuseTransmission = 0.h; ///< Diffuse transmission. Range [0,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_t _pad0 = 0.h; 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]. diff --git a/Source/Falcor/Scene/Material/ClothMaterial.h b/Source/Falcor/Scene/Material/ClothMaterial.h index bd9219c3d..56111df99 100644 --- a/Source/Falcor/Scene/Material/ClothMaterial.h +++ b/Source/Falcor/Scene/Material/ClothMaterial.h @@ -49,6 +49,7 @@ namespace Falcor */ class FALCOR_API ClothMaterial : public BasicMaterial { + FALCOR_OBJECT(ClothMaterial) public: static ref create(ref pDevice, const std::string& name) { return make_ref(pDevice, name); }; diff --git a/Source/Falcor/Scene/Material/HairMaterial.h b/Source/Falcor/Scene/Material/HairMaterial.h index 3106c2a63..31f6bd71d 100644 --- a/Source/Falcor/Scene/Material/HairMaterial.h +++ b/Source/Falcor/Scene/Material/HairMaterial.h @@ -47,6 +47,7 @@ namespace Falcor */ class FALCOR_API HairMaterial : public BasicMaterial { + FALCOR_OBJECT(HairMaterial) public: static ref create(ref pDevice, const std::string& name) { return make_ref(pDevice, name); }; diff --git a/Source/Falcor/Scene/Material/MERLMaterial.h b/Source/Falcor/Scene/Material/MERLMaterial.h index dc8d1b3cf..36a2ac98b 100644 --- a/Source/Falcor/Scene/Material/MERLMaterial.h +++ b/Source/Falcor/Scene/Material/MERLMaterial.h @@ -42,6 +42,7 @@ namespace Falcor */ class FALCOR_API MERLMaterial : public Material { + FALCOR_OBJECT(MERLMaterial) public: static ref create(ref pDevice, const std::string& name, const std::filesystem::path& path) { return make_ref(pDevice, name, path); } @@ -55,7 +56,7 @@ namespace Falcor Program::ShaderModuleList getShaderModules() const override; Program::TypeConformanceList getTypeConformances() const override; - int getBufferCount() const override { return 1; } + size_t getMaxBufferCount() const override { return 1; } protected: void init(const MERLFile& merlFile); diff --git a/Source/Falcor/Scene/Material/MERLMaterialData.slang b/Source/Falcor/Scene/Material/MERLMaterialData.slang index 80641b4af..00bbcfb28 100644 --- a/Source/Falcor/Scene/Material/MERLMaterialData.slang +++ b/Source/Falcor/Scene/Material/MERLMaterialData.slang @@ -42,7 +42,7 @@ BEGIN_NAMESPACE_FALCOR */ struct MERLMaterialData { - // MaterialHeader (8B) is stored just before this struct in memory. + // MaterialHeader (16B) is stored just before this struct in memory. uint bufferID = 0; ///< Buffer ID in material system where BRDF data is stored. uint samplerID = 0; ///< Texture sampler ID for LUT sampler. DiffuseSpecularData extraData = {}; ///< Parameters for a best fit BRDF approximation. diff --git a/Source/Falcor/Scene/Material/MERLMixMaterial.h b/Source/Falcor/Scene/Material/MERLMixMaterial.h index 3ce0dc058..4c50e3e7b 100644 --- a/Source/Falcor/Scene/Material/MERLMixMaterial.h +++ b/Source/Falcor/Scene/Material/MERLMixMaterial.h @@ -45,6 +45,7 @@ namespace Falcor */ class FALCOR_API MERLMixMaterial : public Material { + FALCOR_OBJECT(MERLMixMaterial) public: static ref create(ref pDevice, const std::string& name, const std::vector& paths) { return make_ref(pDevice, name, paths); } @@ -56,7 +57,7 @@ namespace Falcor MaterialDataBlob getDataBlob() const override { return prepareDataBlob(mData); } Program::ShaderModuleList getShaderModules() const override; Program::TypeConformanceList getTypeConformances() const override; - int getBufferCount() const override { return 1; } + size_t getMaxBufferCount() const override { return 1; } bool setTexture(const TextureSlot slot, const ref& pTexture) override; void setDefaultTextureSampler(const ref& pSampler) override; diff --git a/Source/Falcor/Scene/Material/MERLMixMaterialData.slang b/Source/Falcor/Scene/Material/MERLMixMaterialData.slang index dd1a3f56e..ead7c6537 100644 --- a/Source/Falcor/Scene/Material/MERLMixMaterialData.slang +++ b/Source/Falcor/Scene/Material/MERLMixMaterialData.slang @@ -44,7 +44,7 @@ BEGIN_NAMESPACE_FALCOR */ struct MERLMixMaterialData { - // MaterialHeader (8B) is stored just before this struct in memory. + // MaterialHeader (16B) is stored just before this struct in memory. uint flags = 0; ///< Material flags and packed sampler IDs. See accessors below. uint brdfCount = 0; ///< Number of loaded BRDFs. uint byteStride = 0; ///< Stride in bytes between each BRDF in the data buffer. diff --git a/Source/Falcor/Scene/Material/Material.cpp b/Source/Falcor/Scene/Material/Material.cpp index 2f28dfea9..a07252a1a 100644 --- a/Source/Falcor/Scene/Material/Material.cpp +++ b/Source/Falcor/Scene/Material/Material.cpp @@ -38,8 +38,8 @@ namespace Falcor namespace { static_assert(sizeof(TextureHandle) == 4); - static_assert(sizeof(MaterialHeader) == 8); - static_assert(sizeof(MaterialPayload) == 120); + static_assert(sizeof(MaterialHeader) == 16); + static_assert(sizeof(MaterialPayload) == 112); static_assert(sizeof(MaterialDataBlob) == 128); static_assert(static_cast(MaterialType::BuiltinCount) <= (1u << MaterialHeader::kMaterialTypeBits), "MaterialType count exceeds the maximum"); static_assert(static_cast(AlphaMode::Count) <= (1u << MaterialHeader::kAlphaModeBits), "AlphaMode bit count exceeds the maximum"); @@ -47,6 +47,8 @@ namespace Falcor static_assert(static_cast(TextureHandle::Mode::Count) <= (1u << TextureHandle::kModeBits), "TextureHandle::Mode bit count exceeds the maximum"); static_assert(MaterialHeader::kTotalHeaderBitsX <= 32, "MaterialHeader bit count x exceeds the maximum"); static_assert(MaterialHeader::kTotalHeaderBitsY <= 32, "MaterialHeader bit count y exceeds the maximum"); + static_assert(MaterialHeader::kTotalHeaderBitsZ <= 32, "MaterialHeader bit count z exceeds the maximum"); + static_assert(MaterialHeader::kTotalHeaderBitsW <= 32, "MaterialHeader bit count w exceeds the maximum"); static_assert(MaterialHeader::kAlphaThresholdBits == 16, "MaterialHeader alpha threshold bit count must be 16"); } @@ -61,8 +63,9 @@ namespace Falcor { mHeader.setMaterialType(type); mHeader.setAlphaMode(AlphaMode::Opaque); - mHeader.setAlphaThreshold(float16_t(0.5f)); + mHeader.setAlphaThreshold(0.5h); mHeader.setActiveLobes(static_cast(LobeType::All)); + mHeader.setIoR(1.h); } bool Material::renderUI(Gui::Widgets& widget) @@ -145,6 +148,15 @@ namespace Falcor } } + void Material::setIndexOfRefraction(float IoR) + { + if (mHeader.getIoR() != (float16_t)IoR) + { + mHeader.setIoR((float16_t)IoR); + markUpdates(UpdateFlags::DataChanged); + } + } + const Material::TextureSlotInfo& Material::getTextureSlotInfo(const TextureSlot slot) const { FALCOR_ASSERT((size_t)slot < mTextureSlotInfo.size()); diff --git a/Source/Falcor/Scene/Material/Material.h b/Source/Falcor/Scene/Material/Material.h index 6eba105cd..4aee9123b 100644 --- a/Source/Falcor/Scene/Material/Material.h +++ b/Source/Falcor/Scene/Material/Material.h @@ -54,6 +54,7 @@ namespace Falcor */ class FALCOR_API Material : public Object { + FALCOR_OBJECT(Material) public: /** Flags indicating if and what was updated in the material. */ @@ -195,6 +196,14 @@ namespace Falcor */ virtual uint32_t getNestedPriority() const { return mHeader.getNestedPriority(); } + /** Set the index of refraction. + */ + virtual void setIndexOfRefraction(float IoR); + + /** Get the index of refraction. + */ + virtual float getIndexOfRefraction() const { return (float)mHeader.getIoR(); } + /** Get information about a texture slot. \param[in] slot The texture slot. \return Info about the slot. If the slot doesn't exist isEnabled() returns false. @@ -290,11 +299,11 @@ namespace Falcor /** Get shader defines for the material. The defines must be set on any program using the material. */ - virtual Program::DefineList getDefines() const { return {}; } + virtual DefineList getDefines() const { return {}; } /** Get the number of buffers used by this material. */ - virtual int getBufferCount() const { return 0; } + virtual size_t getMaxBufferCount() const { return 0; } /** Returns the maximum number of textures this material will use. By default we use the number of texture slots. The reason for this is that, @@ -303,6 +312,10 @@ namespace Falcor */ virtual size_t getMaxTextureCount() const { return (size_t)Material::TextureSlot::Count; } + /** Get the number of 3D textures used by this material. + */ + virtual size_t getMaxTexture3DCount() const { return 0; } + // 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 @@ -312,7 +325,7 @@ namespace Falcor Used to set `anyValueSize` on `IMaterialInstance` above the default (128B), for exceptionally large materials. Large material instances can have a singificant performance impact. */ - virtual size_t getMaterialInstanceByteSize() { return 128; } + virtual size_t getMaterialInstanceByteSize() const { return 128; } protected: Material(ref pDevice, const std::string& name, MaterialType type); diff --git a/Source/Falcor/Scene/Material/MaterialData.slang b/Source/Falcor/Scene/Material/MaterialData.slang index 28c20e10b..7c8b1f22f 100644 --- a/Source/Falcor/Scene/Material/MaterialData.slang +++ b/Source/Falcor/Scene/Material/MaterialData.slang @@ -36,11 +36,11 @@ __exported import MaterialTypes; BEGIN_NAMESPACE_FALCOR -/** This is a host/device structure for material header data for all material types (8B). +/** This is a host/device structure for material header data for all material types (16B). */ struct MaterialHeader { - uint2 packedData = {}; + uint4 packedData = {}; static constexpr uint kMaterialTypeBits = 16; static constexpr uint kNestedPriorityBits = 4; @@ -48,6 +48,7 @@ struct MaterialHeader static constexpr uint kSamplerIDBits = 8; static constexpr uint kAlphaModeBits = 1; static constexpr uint kAlphaThresholdBits = 16; // Using float16_t format + static constexpr uint kIoRBits = 16; // Using float16_t format // packedData.x bit layout static constexpr uint kMaterialTypeOffset = 0; @@ -69,6 +70,13 @@ struct MaterialHeader static constexpr uint kTotalHeaderBitsY = kDeltaSpecularFlagOffset + 1; + // packedData.z bit layout + static constexpr uint kIoROffset = 0; + + static constexpr uint kTotalHeaderBitsZ = kIoROffset + kIoRBits; + + // packedData.w bit layout + static constexpr uint kTotalHeaderBitsW = 0; /** Set material type. */ @@ -169,18 +177,24 @@ struct MaterialHeader */ bool isDeltaSpecular() CONST_FUNCTION { return packedData.y & (1u << kDeltaSpecularFlagOffset); } + /// Set index of refraction. + SETTER_DECL void setIoR(float16_t ior) { packedData.z = PACK_BITS_UNSAFE(kIoRBits, kIoROffset, packedData.z, (uint)asuint16(ior)); } + + /// Get index of refraction. + float16_t getIoR() CONST_FUNCTION { return asfloat16((uint16_t)EXTRACT_BITS(kIoRBits, kIoROffset, packedData.z)); } + #ifdef HOST_CODE friend bool operator==(const MaterialHeader& lhs, const MaterialHeader& rhs); friend bool operator!=(const MaterialHeader& lhs, const MaterialHeader& rhs) { return !(lhs == rhs); } #endif }; -/** This is a host/device structure for material payload data (120B). +/** This is a host/device structure for material payload data (112B). The format of the data depends on the material type. */ struct MaterialPayload { - uint data[30]; + uint data[28]; }; /** This is a host/device structure representing a blob of material data (128B). @@ -191,8 +205,8 @@ struct MaterialPayload */ struct MaterialDataBlob { - MaterialHeader header; // 8B - MaterialPayload payload; // 120B + MaterialHeader header; // 16B + MaterialPayload payload; // 112B }; END_NAMESPACE_FALCOR diff --git a/Source/Falcor/Scene/Material/MaterialSystem.cpp b/Source/Falcor/Scene/Material/MaterialSystem.cpp index 569e4ddfb..39ad6f61e 100644 --- a/Source/Falcor/Scene/Material/MaterialSystem.cpp +++ b/Source/Falcor/Scene/Material/MaterialSystem.cpp @@ -42,6 +42,7 @@ namespace Falcor const std::string kMaterialSamplersName = "materialSamplers"; const std::string kMaterialTexturesName = "materialTextures"; const std::string kMaterialBuffersName = "materialBuffers"; + const std::string kMaterialTextures3DName = "materialTextures3D"; const size_t kMaxSamplerCount = 1ull << MaterialHeader::kSamplerIDBits; const size_t kMaxTextureCount = 1ull << TextureHandle::kTextureIDBits; @@ -187,6 +188,25 @@ namespace Falcor mBuffersChanged = true; } + uint32_t MaterialSystem::addTexture3D(const ref& pTexture) + { + FALCOR_ASSERT(pTexture && pTexture->getType() == Texture::Type::Texture3D && pTexture->getSampleCount() == 1); + + // Reuse previously added texture. + if (auto it = std::find_if(mTextures3D.begin(), mTextures3D.end(), [&](auto pOther) { return pTexture == pOther; }); it != mTextures3D.end()) + return (uint32_t)std::distance(mTextures3D.begin(), it); + + if (mTextures3D.size() >= mTexture3DDescCount) + throw RuntimeError("Too many 3D textures"); + + const uint32_t textureID = static_cast(mTextures3D.size()); + + mTextures3D.push_back(pTexture); + mTextures3DChanged = true; + + return textureID; + } + MaterialID MaterialSystem::addMaterial(const ref& pMaterial) { checkArgument(pMaterial != nullptr, "'pMaterial' is missing"); @@ -407,6 +427,8 @@ namespace Falcor for (uint32_t materialID = 0; materialID < (uint32_t)mMaterials.size(); ++materialID) { auto& pMaterial = mMaterials[materialID]; + if (pMaterial->mpDevice != mpDevice) + throw RuntimeError("Material '{}' was created with a different device than the MaterialSystem.", pMaterial->getName()); mMaterialsUpdateFlags[materialID] = pMaterial->update(this); } @@ -471,6 +493,17 @@ namespace Falcor mBuffersChanged = false; } + // Update 3D textures. + if (forceUpdate || mTextures3DChanged) + { + auto var = blockVar[kMaterialTextures3DName]; + for (size_t i = 0; i < mTextures3D.size(); i++) + { + var[i] = mTextures3D[i]; + } + mTextures3DChanged = false; + } + // Update shader modules and type conformances. // This is done by iterating over all materials to query their properties. @@ -502,6 +535,7 @@ namespace Falcor { mTextureDescCount = 0; mBufferDescCount = 0; + mTexture3DDescCount = 0; mMaterialCountByType.resize(getMaterialTypeCount()); std::fill(mMaterialCountByType.begin(), mMaterialCountByType.end(), 0); @@ -512,9 +546,9 @@ namespace Falcor { // Update descriptor counts. These counts will be reported by getDefines(). // TODO: Remove this when unbounded descriptor arrays are supported (#1321). - // TODO: Rename getBufferCount() -> getMaxBufferCount() mTextureDescCount += pMaterial->getMaxTextureCount(); - mBufferDescCount += pMaterial->getBufferCount(); + mBufferDescCount += pMaterial->getMaxBufferCount(); + mTexture3DDescCount += pMaterial->getMaxTexture3DCount(); // Update material type info. size_t index = (size_t)pMaterial->getType(); @@ -553,7 +587,7 @@ namespace Falcor return s; } - Shader::DefineList MaterialSystem::getDefines() const + DefineList MaterialSystem::getDefines() const { checkInvariant(!mMaterialsChanged, "Materials have changed. Call update() first."); @@ -561,10 +595,11 @@ namespace Falcor for (auto& it : mMaterials) materialInstanceByteSize = std::max(materialInstanceByteSize, it->getMaterialInstanceByteSize()); - Shader::DefineList defines; + DefineList defines; defines.add("MATERIAL_SYSTEM_SAMPLER_DESC_COUNT", std::to_string(kMaxSamplerCount)); defines.add("MATERIAL_SYSTEM_TEXTURE_DESC_COUNT", std::to_string(mTextureDescCount)); defines.add("MATERIAL_SYSTEM_BUFFER_DESC_COUNT", std::to_string(mBufferDescCount)); + defines.add("MATERIAL_SYSTEM_TEXTURE_3D_DESC_COUNT", std::to_string(mTexture3DDescCount)); defines.add("MATERIAL_SYSTEM_UDIM_INDIRECTION_ENABLED", mpTextureManager->getUdimIndirectionCount() > 0 ? "1" : "0"); defines.add("MATERIAL_SYSTEM_HAS_SPEC_GLOSS_MATERIALS", mHasSpecGlossStandardMaterial ? "1" : "0"); defines.add("FALCOR_MATERIAL_INSTANCE_SIZE", std::to_string(materialInstanceByteSize)); @@ -573,7 +608,7 @@ namespace Falcor // We ensure that two materials cannot set the same define to mismatching values. for (const auto& material : mMaterials) { - Shader::DefineList materialDefines = material->getDefines(); + DefineList materialDefines = material->getDefines(); for (const auto& [name, value] : materialDefines) { if (auto it = defines.find(name); it != defines.end()) @@ -629,7 +664,7 @@ namespace Falcor void MaterialSystem::createParameterBlock() { // Create parameter block. - Program::DefineList defines = getDefines(); + DefineList defines = getDefines(); defines.add("MATERIAL_SYSTEM_PARAMETER_BLOCK"); auto pPass = ComputePass::create(mpDevice, kShaderFilename, "main", defines); auto pReflector = pPass->getProgram()->getReflector()->getParameterBlock("gMaterialsBlock"); diff --git a/Source/Falcor/Scene/Material/MaterialSystem.h b/Source/Falcor/Scene/Material/MaterialSystem.h index af00d7f9b..d8a81889f 100644 --- a/Source/Falcor/Scene/Material/MaterialSystem.h +++ b/Source/Falcor/Scene/Material/MaterialSystem.h @@ -29,10 +29,10 @@ #include "Material.h" #include "Core/Macros.h" #include "Core/API/fwd.h" -#include "Core/API/Shader.h" #include "Core/API/ParameterBlock.h" #include "Core/API/Buffer.h" #include "Core/API/Sampler.h" +#include "Core/Program/DefineList.h" #include "Core/Program/Program.h" #include "Utils/Image/TextureManager.h" #include "Utils/UI/Gui.h" @@ -86,7 +86,7 @@ namespace Falcor These need to be set before binding the material system parameter block. \return List of shader defines. */ - Shader::DefineList getDefines() const; + DefineList getDefines() const; /** Get type conformances for all material types used. These need to be set on a program before using the material system in shaders @@ -146,6 +146,16 @@ namespace Falcor */ uint32_t getBufferCount() const { return (uint32_t)mBuffers.size(); } + /** Add a 3D texture resource to be managed. + \param[in] pTexture The texture. + \return The ID of the texture. + */ + uint32_t addTexture3D(const ref& pTexture); + + /** Get the total number of 3D textures. + */ + uint32_t getTexture3DCount() const { return (uint32_t)mTextures3D.size(); } + /** Add a material. If an identical material already exists, the material is not added and the existing ID returned. \param[in] pMaterial The material. @@ -234,12 +244,14 @@ namespace Falcor // Metadata size_t mTextureDescCount = 0; ///< Number of texture descriptors in GPU descriptor array. This variable is for book-keeping until unbounded descriptor arrays are supported (see #1321). size_t mBufferDescCount = 0; ///< Number of buffer descriptors in GPU descriptor array. This variable is for book-keeping until unbounded descriptor arrays are supported (see #1321). + size_t mTexture3DDescCount = 0; ///< Number of 3D texture descriptors in GPU descriptor array. This variable is for book-keeping until unbounded descriptor arrays are supported (see #1321). std::vector mMaterialCountByType; ///< Number of materials of each type, indexed by MaterialType. std::set mMaterialTypes; ///< Set of all material types used. bool mHasSpecGlossStandardMaterial = false; ///< True if standard materials using the SpecGloss shading model exist. bool mSamplersChanged = false; ///< Flag indicating if samplers were added/removed since last update. bool mBuffersChanged = false; ///< Flag indicating if buffers were added/removed since last update. + bool mTextures3DChanged = false; ///< Flag indicating if 3D textures were added/removed since last update. bool mMaterialsChanged = false; ///< Flag indicating if materials were added/removed since last update. Per-material updates are tracked by each material's update flags. Material::UpdateFlags mMaterialUpdates = Material::UpdateFlags::None; ///< Material updates across all materials since last update. @@ -251,6 +263,7 @@ namespace Falcor 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. + std::vector> mTextures3D; ///< 3D textures 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/MaterialSystem.slang b/Source/Falcor/Scene/Material/MaterialSystem.slang index bf1a76f9b..e01c91969 100644 --- a/Source/Falcor/Scene/Material/MaterialSystem.slang +++ b/Source/Falcor/Scene/Material/MaterialSystem.slang @@ -54,6 +54,8 @@ struct MaterialSystem /// Buffer resources for all materials. TODO: Make this an unbounded array (see #1321). ByteAddressBuffer materialBuffers[ArrayMax<1, MATERIAL_SYSTEM_BUFFER_DESC_COUNT>.value]; + Texture3D materialTextures3D[ArrayMax<1, MATERIAL_SYSTEM_TEXTURE_3D_DESC_COUNT>.value]; + /** * When UDIMs are used, this array contains indirection from -1001 to materialTextures. @@ -116,7 +118,7 @@ struct MaterialSystem Currently there is a bug in glslang/spv-out that generates invalid SPIRV code for functions that returns a SamplerState. We workaround this issue by always inlining this function in our generated glsl code. */ -#ifdef FALCOR_VK +#ifdef FALCOR_VULKAN [ForceInline] #endif SamplerState getTextureSampler(const uint samplerID) @@ -129,7 +131,7 @@ struct MaterialSystem Currently there is a bug in glslang/spv-out that generates invalid SPIRV code for functions that returns a SamplerState. We workaround this issue by always inlining this function in our generated glsl code. */ -#ifdef FALCOR_VK +#ifdef FALCOR_VULKAN [ForceInline] #endif SamplerState getDefaultTextureSampler(const uint materialID) @@ -145,6 +147,11 @@ struct MaterialSystem return materialBuffers[bufferID]; } + Texture3D getTexture3D(const uint textureID) + { + return materialTextures3D[textureID]; + } + /** Check if a material has displacement. \param[in] materialID Material ID \return Returns true if material has displacement. @@ -204,13 +211,7 @@ struct MaterialSystem */ float evalIoR(const uint materialID) { - // TODO: Move IoR field into MaterialHeader so it's available for all material types. - if (isBasicMaterial(materialID)) - { - BasicMaterialData md = getBasicMaterialData(materialID); - return md.IoR; - } - return 1.f; + return getMaterialHeader(materialID).getIoR(); } /** Resolves TextureHandle from possibly-UDIM to standard TextureHandle diff --git a/Source/Falcor/Scene/Material/PBRT/PBRTCoatedConductorMaterial.h b/Source/Falcor/Scene/Material/PBRT/PBRTCoatedConductorMaterial.h index 9c6359687..2a7f48792 100644 --- a/Source/Falcor/Scene/Material/PBRT/PBRTCoatedConductorMaterial.h +++ b/Source/Falcor/Scene/Material/PBRT/PBRTCoatedConductorMaterial.h @@ -57,6 +57,7 @@ namespace Falcor */ class FALCOR_API PBRTCoatedConductorMaterial : public BasicMaterial { + FALCOR_OBJECT(PBRTCoatedConductorMaterial) public: static ref create(ref pDevice, const std::string& name) { return make_ref(pDevice, name); } diff --git a/Source/Falcor/Scene/Material/PBRT/PBRTCoatedDiffuseMaterial.h b/Source/Falcor/Scene/Material/PBRT/PBRTCoatedDiffuseMaterial.h index 6e01cfff7..4995e804d 100644 --- a/Source/Falcor/Scene/Material/PBRT/PBRTCoatedDiffuseMaterial.h +++ b/Source/Falcor/Scene/Material/PBRT/PBRTCoatedDiffuseMaterial.h @@ -53,6 +53,7 @@ namespace Falcor */ class FALCOR_API PBRTCoatedDiffuseMaterial : public BasicMaterial { + FALCOR_OBJECT(PBRTCoatedDiffuseMaterial) public: static ref create(ref pDevice, const std::string& name) { return make_ref(pDevice, name); } diff --git a/Source/Falcor/Scene/Material/PBRT/PBRTConductorMaterial.h b/Source/Falcor/Scene/Material/PBRT/PBRTConductorMaterial.h index 7a2cf6064..a080ccd8e 100644 --- a/Source/Falcor/Scene/Material/PBRT/PBRTConductorMaterial.h +++ b/Source/Falcor/Scene/Material/PBRT/PBRTConductorMaterial.h @@ -57,6 +57,7 @@ namespace Falcor */ class FALCOR_API PBRTConductorMaterial : public BasicMaterial { + FALCOR_OBJECT(PBRTConductorMaterial) public: static ref create(ref pDevice, const std::string& name) { return make_ref(pDevice, name); } diff --git a/Source/Falcor/Scene/Material/PBRT/PBRTDielectricMaterial.h b/Source/Falcor/Scene/Material/PBRT/PBRTDielectricMaterial.h index cd032892e..ff876a028 100644 --- a/Source/Falcor/Scene/Material/PBRT/PBRTDielectricMaterial.h +++ b/Source/Falcor/Scene/Material/PBRT/PBRTDielectricMaterial.h @@ -51,6 +51,7 @@ namespace Falcor */ class FALCOR_API PBRTDielectricMaterial : public BasicMaterial { + FALCOR_OBJECT(PBRTDielectricMaterial) public: static ref create(ref pDevice, const std::string& name) { return make_ref(pDevice, name); } diff --git a/Source/Falcor/Scene/Material/PBRT/PBRTDiffuseMaterial.h b/Source/Falcor/Scene/Material/PBRT/PBRTDiffuseMaterial.h index 073dab890..fc5431ed8 100644 --- a/Source/Falcor/Scene/Material/PBRT/PBRTDiffuseMaterial.h +++ b/Source/Falcor/Scene/Material/PBRT/PBRTDiffuseMaterial.h @@ -47,6 +47,7 @@ namespace Falcor */ class FALCOR_API PBRTDiffuseMaterial : public BasicMaterial { + FALCOR_OBJECT(PBRTDiffuseMaterial) public: static ref create(ref pDevice, const std::string& name) { return make_ref(pDevice, name); } diff --git a/Source/Falcor/Scene/Material/PBRT/PBRTDiffuseTransmissionMaterial.h b/Source/Falcor/Scene/Material/PBRT/PBRTDiffuseTransmissionMaterial.h index a42726668..970390e27 100644 --- a/Source/Falcor/Scene/Material/PBRT/PBRTDiffuseTransmissionMaterial.h +++ b/Source/Falcor/Scene/Material/PBRT/PBRTDiffuseTransmissionMaterial.h @@ -51,6 +51,7 @@ namespace Falcor */ class FALCOR_API PBRTDiffuseTransmissionMaterial : public BasicMaterial { + FALCOR_OBJECT(PBRTDiffuseTransmissionMaterial) public: static ref create(ref pDevice, const std::string& name) { return make_ref(pDevice, name); } diff --git a/Source/Falcor/Scene/Material/RGLMaterial.h b/Source/Falcor/Scene/Material/RGLMaterial.h index 6fcbbdff2..655b2ef7b 100644 --- a/Source/Falcor/Scene/Material/RGLMaterial.h +++ b/Source/Falcor/Scene/Material/RGLMaterial.h @@ -41,6 +41,7 @@ namespace Falcor */ class FALCOR_API RGLMaterial : public Material { + FALCOR_OBJECT(RGLMaterial) public: static ref create(ref pDevice, const std::string& name, const std::filesystem::path& path) { return make_ref(pDevice, name, path); } @@ -53,7 +54,7 @@ namespace Falcor Program::ShaderModuleList getShaderModules() const override; Program::TypeConformanceList getTypeConformances() const override; - virtual int getBufferCount() const override { return 12; } + virtual size_t getMaxBufferCount() const override { return 12; } bool loadBRDF(const std::filesystem::path& path); diff --git a/Source/Falcor/Scene/Material/RGLMaterialData.slang b/Source/Falcor/Scene/Material/RGLMaterialData.slang index d871e1761..ce99fda51 100644 --- a/Source/Falcor/Scene/Material/RGLMaterialData.slang +++ b/Source/Falcor/Scene/Material/RGLMaterialData.slang @@ -40,7 +40,7 @@ BEGIN_NAMESPACE_FALCOR */ struct RGLMaterialData { - // MaterialHeader (8B) is stored just before this struct in memory. + // MaterialHeader (16B) is stored just before this struct in memory. uint phiSize = 0; ///< Number of angular sampling points. uint thetaSize = 0; uint2 sigmaSize = uint2(0); ///< Size of sigma/NDF lookup tables. These are 2D. diff --git a/Source/Falcor/Scene/Material/StandardMaterial.cpp b/Source/Falcor/Scene/Material/StandardMaterial.cpp index 6db745021..c171f8396 100644 --- a/Source/Falcor/Scene/Material/StandardMaterial.cpp +++ b/Source/Falcor/Scene/Material/StandardMaterial.cpp @@ -41,9 +41,9 @@ namespace Falcor : BasicMaterial(pDevice, name, MaterialType::Standard) { setShadingModel(shadingModel); - bool specGloss = getShadingModel() == ShadingModel::SpecGloss; // Setup additional texture slots. + bool specGloss = getShadingModel() == ShadingModel::SpecGloss; mTextureSlotInfo[(uint32_t)TextureSlot::BaseColor] = { specGloss ? "diffuse" : "baseColor", TextureChannelFlags::RGBA, true }; mTextureSlotInfo[(uint32_t)TextureSlot::Specular] = specGloss ? TextureSlotInfo{ "specular", TextureChannelFlags::RGBA, true } : TextureSlotInfo{ "spec", TextureChannelFlags::Green | TextureChannelFlags::Blue, false }; mTextureSlotInfo[(uint32_t)TextureSlot::Normal] = { "normal", TextureChannelFlags::RGB, false }; diff --git a/Source/Falcor/Scene/Material/StandardMaterial.h b/Source/Falcor/Scene/Material/StandardMaterial.h index 0821cace6..724353c5f 100644 --- a/Source/Falcor/Scene/Material/StandardMaterial.h +++ b/Source/Falcor/Scene/Material/StandardMaterial.h @@ -67,6 +67,7 @@ namespace Falcor */ class FALCOR_API StandardMaterial : public BasicMaterial { + FALCOR_OBJECT(StandardMaterial) public: static ref create(ref pDevice, const std::string& name = "", ShadingModel shadingModel = ShadingModel::MetalRough) { diff --git a/Source/Falcor/Scene/Material/TextureSampler.slang b/Source/Falcor/Scene/Material/TextureSampler.slang index 18113d75b..7b2aadbc4 100644 --- a/Source/Falcor/Scene/Material/TextureSampler.slang +++ b/Source/Falcor/Scene/Material/TextureSampler.slang @@ -29,6 +29,7 @@ /** Interface for texture sampling techniques. Types implementing this interface support sampling using different LOD computation techniques */ +[anyValueSize(32)] interface ITextureSampler { /** Sample from a 2D texture using the level of detail computed by this method @@ -160,3 +161,61 @@ struct ExplicitGradientTextureSampler : ITextureSampler return t.SampleGrad(s, uv, gradX, gradY); } }; + +/** Texture sampling using filtered importance sampling + The filters applied are controlled by the UV and LOD jitter values passed at init time. +*/ +struct StochasticTextureSampler : ITextureSampler +{ + float2 gradX; ///< Gradient of texture coordinate in the screen-space X direction + float2 gradY; ///< Gradient of texture coordiante in teh screen-space Y direction + float2 uvJitter; ///< Stochastic uv jitter + float lodJitter; ///< Stochastic lod jitter + float maxAnisotropy; ///< Max anisotropy (ratio between major and minor ellipse axes) + + __init( + float2 gradX, float2 gradY, float2 uvJitter, float lodJitter, float maxAnisotropy=64) + { + this.gradX = gradX; + this.gradY = gradY; + this.uvJitter = uvJitter; + this.lodJitter = lodJitter; + this.maxAnisotropy = maxAnisotropy; + } + + float4 sampleTexture(Texture2D t, SamplerState s, float2 uv) + { + uint2 dim; + t.GetDimensions(dim.x, dim.y); + let dudx = dim.x * gradX.x; + let dvdx = dim.y * gradX.y; + let dudy = dim.x * gradY.x; + let dvdy = dim.y * gradY.y; + + // Find min and max ellipse axis + float2 maxAxis = float2(dudy, dvdy); + float2 minAxis = float2(dudx, dvdx); + if (dot(minAxis, minAxis) > dot(maxAxis, maxAxis)) + { + minAxis = float2(dudy, dvdy); + maxAxis = float2(dudx, dvdx); + } + + float minAxisLength = length(minAxis); + float maxAxisLength = length(maxAxis); + + if (minAxisLength > 0 && (minAxisLength * maxAnisotropy) < maxAxisLength) + { + let scale = maxAxisLength / (minAxisLength * maxAnisotropy); + minAxisLength *= scale; + } + + // No need to clamp to min and max lod levels, as HW should do it for free. + let lod = log2(minAxisLength) + lodJitter; + + uv = uv * dim + uvJitter; + uv = (floor(uv) + 0.5) / dim; + + return t.SampleLevel(s, uv, lod); + } +}; diff --git a/Source/Falcor/Scene/SDFs/NormalizedDenseSDFGrid/NDSDFGrid.slang b/Source/Falcor/Scene/SDFs/NormalizedDenseSDFGrid/NDSDFGrid.slang index 2b4d25852..6de4e0692 100644 --- a/Source/Falcor/Scene/SDFs/NormalizedDenseSDFGrid/NDSDFGrid.slang +++ b/Source/Falcor/Scene/SDFs/NormalizedDenseSDFGrid/NDSDFGrid.slang @@ -128,6 +128,9 @@ struct NDSDFGrid : SDFGridBase, ISDFGrid bool intersectSDF(const float3 rayOrigin, const float3 rayDir, const float tMin, const float tMax, const uint primitiveID, out float t, out SDFGridHitData hitData) { + t = {}; + hitData = {}; + // Add 0.5f to origin so that it is in [0, 1] instead of [-0.5, 0.5]. float3 rayOrigLocal = rayOrigin + 0.5f; diff --git a/Source/Falcor/Scene/SDFs/SDFGrid.h b/Source/Falcor/Scene/SDFs/SDFGrid.h index bc45c141e..1eb37bdf0 100644 --- a/Source/Falcor/Scene/SDFs/SDFGrid.h +++ b/Source/Falcor/Scene/SDFs/SDFGrid.h @@ -69,6 +69,7 @@ namespace Falcor */ class FALCOR_API SDFGrid : public Object { + FALCOR_OBJECT(SDFGrid) public: enum class Type { @@ -259,6 +260,8 @@ namespace Falcor ref mpSDFGridTexture; ///< A texture on the GPU holding the value representation. ref mpEvaluatePrimitivesPass; + + friend class Scene; }; FALCOR_ENUM_CLASS_OPERATORS(SDFGrid::UpdateFlags); diff --git a/Source/Falcor/Scene/SDFs/SparseBrickSet/SDFSBS.cpp b/Source/Falcor/Scene/SDFs/SparseBrickSet/SDFSBS.cpp index 3e30b9b40..2afef195c 100644 --- a/Source/Falcor/Scene/SDFs/SparseBrickSet/SDFSBS.cpp +++ b/Source/Falcor/Scene/SDFs/SparseBrickSet/SDFSBS.cpp @@ -185,7 +185,7 @@ namespace Falcor // For brick widths smaller than 8 brick validation will be performed using group shared memory. // For larger brick widths, brick validation will be performed using a global atomic. - Program::DefineList defines; + DefineList defines; defines.add("GROUP_BRICK_CREATION", mBrickWidth <= 8u ? "1" : "0"); defines.add("GROUP_WIDTH", std::to_string(std::min(mBrickWidth, 8u))); @@ -424,7 +424,7 @@ namespace Falcor Program::Desc desc; desc.addShaderLibrary(kCreateChunksFromPrimitivesShaderName).csEntry("rootEntryPoint"); - Program::DefineList defines; + DefineList defines; defines.add("CHUNK_WIDTH", std::to_string(kChunkWidth)); defines.add("BRICK_WIDTH", std::to_string(mBrickWidth)); @@ -436,7 +436,7 @@ namespace Falcor Program::Desc desc; desc.addShaderLibrary(kCreateChunksFromPrimitivesShaderName).csEntry("subdivideEntryPoint"); - Program::DefineList defines; + DefineList defines; defines.add("CHUNK_WIDTH", std::to_string(kChunkWidth)); defines.add("BRICK_WIDTH", std::to_string(mBrickWidth)); @@ -448,7 +448,7 @@ namespace Falcor Program::Desc desc; desc.addShaderLibrary(kPruneEmptyBricksShaderName).csEntry("coarsePrune"); - Program::DefineList defines; + DefineList defines; defines.add("BRICK_WIDTH", std::to_string(mBrickWidth)); mpCoarselyPruneEmptyBricks = ComputePass::create(mpDevice, desc, defines); @@ -459,7 +459,7 @@ namespace Falcor Program::Desc desc; desc.addShaderLibrary(kPruneEmptyBricksShaderName).csEntry("finePrune"); - Program::DefineList defines; + DefineList defines; defines.add("BRICK_WIDTH", std::to_string(mBrickWidth)); mpFinelyPruneEmptyBricks = ComputePass::create(mpDevice, desc, defines); @@ -470,7 +470,7 @@ namespace Falcor Program::Desc desc; desc.addShaderLibrary(kCreateBricksFromChunksShaderName).csEntry("main"); - Program::DefineList defines; + DefineList defines; defines.add("CHUNK_WIDTH", std::to_string(kChunkWidth)); defines.add("BRICK_WIDTH", std::to_string(mBrickWidth)); defines.add("COMPRESS_BRICKS", mCompressed ? "1" : "0"); @@ -845,7 +845,7 @@ namespace Falcor Program::Desc desc; desc.addShaderLibrary(kExpandSDFieldShaderName).csEntry("main"); - Program::DefineList defines; + DefineList defines; defines.add("GROUP_WIDTH", "8"); mpExpandSDFieldPass = ComputePass::create(mpDevice, desc, defines); } @@ -901,7 +901,7 @@ namespace Falcor // For brick widths smaller than 8 interval computation will be performed using group shared memory. // For larger brick widths, interval computation will be performed by letting each thread (brick) iterate over all its children. - Program::DefineList defines; + DefineList defines; defines.add("GROUP_BRICK_CREATION", mBrickWidth <= 8u ? "1" : "0"); defines.add("GROUP_WIDTH", std::to_string(std::min(mBrickWidth, 8u))); defines.add("BRICK_WIDTH", std::to_string(mBrickWidth)); @@ -914,7 +914,7 @@ namespace Falcor Program::Desc desc; desc.addShaderLibrary(kComputeIntervalSDFieldFromGridShaderName).csEntry("chunkGather"); - Program::DefineList defines; + DefineList defines; defines.add("GROUP_BRICK_CREATION", "0"); defines.add("GROUP_WIDTH", std::to_string(8u)); defines.add("BRICK_WIDTH", std::to_string(mBrickWidth)); diff --git a/Source/Falcor/Scene/SDFs/SparseBrickSet/SDFSBS.slang b/Source/Falcor/Scene/SDFs/SparseBrickSet/SDFSBS.slang index 6279cf776..f9f29d710 100644 --- a/Source/Falcor/Scene/SDFs/SparseBrickSet/SDFSBS.slang +++ b/Source/Falcor/Scene/SDFs/SparseBrickSet/SDFSBS.slang @@ -151,6 +151,9 @@ struct SDFSBS : SDFGridBase, ISDFGrid bool intersectSDF(const float3 rayOrigin, const float3 rayDir, const float tMin, const float tMax, const uint primitiveID, out float t, out SDFGridHitData hitData) { + t = {}; + hitData = {}; + // Normalize ray direction. float dirLength = length(rayDir); float inverseDirLength = 1.0f / dirLength; diff --git a/Source/Falcor/Scene/SDFs/SparseVoxelOctree/SDFSVO.cpp b/Source/Falcor/Scene/SDFs/SparseVoxelOctree/SDFSVO.cpp index f11b3c644..f60046aec 100644 --- a/Source/Falcor/Scene/SDFs/SparseVoxelOctree/SDFSVO.cpp +++ b/Source/Falcor/Scene/SDFs/SparseVoxelOctree/SDFSVO.cpp @@ -178,7 +178,7 @@ namespace Falcor { Program::Desc desc; desc.addShaderLibrary(kSDFSVOBuildLevelFromTextureShaderName).csEntry("main").setShaderModel("6_5"); - mpBuildFinestLevelFromDistanceTexturePass = ComputePass::create(mpDevice, desc, Program::DefineList({ {"FINEST_LEVEL_PASS", "1"} })); + mpBuildFinestLevelFromDistanceTexturePass = ComputePass::create(mpDevice, desc, DefineList({ {"FINEST_LEVEL_PASS", "1"} })); } // Create voxels for the bottom level. @@ -201,7 +201,7 @@ namespace Falcor { Program::Desc desc; desc.addShaderLibrary(kSDFSVOBuildLevelFromTextureShaderName).csEntry("main").setShaderModel("6_5"); - mpBuildLevelFromDistanceTexturePass = ComputePass::create(mpDevice, desc, Program::DefineList({ {"FINEST_LEVEL_PASS", "0"} })); + mpBuildLevelFromDistanceTexturePass = ComputePass::create(mpDevice, desc, DefineList({ {"FINEST_LEVEL_PASS", "0"} })); } // Allocate a buffer that will hold the voxel count for each level except the bottom level (as we already obtained that previously). @@ -252,7 +252,7 @@ namespace Falcor if (!mpSortLocationCodesPass) { - Program::DefineList definesList; + DefineList definesList; definesList.add("GROUP_SIZE", std::to_string(groupSize)); definesList.add("BUFFER_SIZE", std::to_string(hashTableCapacity)); definesList.add("LOCAL_BMS", std::to_string(kSDFSVOLocationCodeLocalBMS)); diff --git a/Source/Falcor/Scene/SDFs/SparseVoxelOctree/SDFSVO.slang b/Source/Falcor/Scene/SDFs/SparseVoxelOctree/SDFSVO.slang index 3a162b43b..b593dfa60 100644 --- a/Source/Falcor/Scene/SDFs/SparseVoxelOctree/SDFSVO.slang +++ b/Source/Falcor/Scene/SDFs/SparseVoxelOctree/SDFSVO.slang @@ -66,6 +66,9 @@ struct SDFSVO : SDFGridBase, ISDFGrid bool intersectSDF(const float3 rayOrigin, const float3 rayDir, const float tMin, const float tMax, const uint primitiveID, out float t, out SDFGridHitData hitData) { + t = {}; + hitData = {}; + // rayOrigin will be in [-0.5, 0.5], offset it so that it is in [1, 2]. float3 o = rayOrigin + 1.5f; diff --git a/Source/Falcor/Scene/SDFs/SparseVoxelOctree/SDFSVOHashTable.slang b/Source/Falcor/Scene/SDFs/SparseVoxelOctree/SDFSVOHashTable.slang index 2aebf8506..51c28c90a 100644 --- a/Source/Falcor/Scene/SDFs/SparseVoxelOctree/SDFSVOHashTable.slang +++ b/Source/Falcor/Scene/SDFs/SparseVoxelOctree/SDFSVOHashTable.slang @@ -169,6 +169,10 @@ struct SDFSVOHashTable */ bool lookup(uint2 locationCode, out uint svoOffset, out uint validMask, out uint2 packedValues) { + svoOffset = {}; + validMask = {}; + packedValues = {}; + uint hash = hashLocationCode(locationCode); uint slot = (hash & (capacity - 1)); diff --git a/Source/Falcor/Scene/Scene.cpp b/Source/Falcor/Scene/Scene.cpp index c8c0a9954..25d03e6a0 100644 --- a/Source/Falcor/Scene/Scene.cpp +++ b/Source/Falcor/Scene/Scene.cpp @@ -256,7 +256,7 @@ namespace Falcor void Scene::updateSceneDefines() { - Shader::DefineList defines; + DefineList defines; // The following defines are currently static and do not change at runtime. defines.add("SCENE_GRID_COUNT", std::to_string(mGrids.size())); @@ -277,14 +277,14 @@ namespace Falcor mSceneDefines = defines; } - Shader::DefineList Scene::getSceneDefines() const + DefineList Scene::getSceneDefines() const { return mSceneDefines; } - Shader::DefineList Scene::getSceneSDFGridDefines() const + DefineList Scene::getSceneSDFGridDefines() const { - Shader::DefineList defines; + DefineList defines; // Setup static defines for enum values. defines.add("SCENE_SDF_GRID_IMPLEMENTATION_NDSDF", std::to_string((uint32_t)SDFGrid::Type::NormalizedDenseGrid)); @@ -1102,6 +1102,8 @@ namespace Falcor for (uint32_t sdfGridID = 0; sdfGridID < mSDFGrids.size(); ++sdfGridID) { ref& pSDFGrid = mSDFGrids[sdfGridID]; + if (pSDFGrid->mpDevice != mpDevice) + throw RuntimeError("SDFGrid '{}' was created with a different device than the Scene", pSDFGrid->getName()); SDFGrid::UpdateFlags sdfGridUpdateFlags = pSDFGrid->update(pRenderContext); if (is_set(sdfGridUpdateFlags, SDFGrid::UpdateFlags::AABBsChanged)) @@ -1610,6 +1612,8 @@ namespace Falcor // Update animations and get combined updates. for (const auto& pGridVolume : mGridVolumes) { + if (pGridVolume->mpDevice != mpDevice) + throw RuntimeError("GridVolume '{}' was created with a different device than the Scene.", pGridVolume->getName()); updateAnimatable(*pGridVolume, *mpAnimationController, forceUpdate); combinedUpdates |= pGridVolume->getUpdates(); } @@ -1667,6 +1671,8 @@ namespace Falcor if (mpEnvMap) { + if (mpEnvMap->mpDevice != mpDevice) + throw RuntimeError("EnvMap was created with a different device than the Scene."); auto envMapChanges = mpEnvMap->beginFrame(); if (envMapChanges != EnvMap::Changes::None || mEnvMapChanged || forceUpdate) { @@ -4099,14 +4105,31 @@ namespace Falcor 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 + pybind11::class_ renderSettings(m, "SceneRenderSettings"); + renderSettings.def_readwrite("useEnvLight", &Scene::RenderSettings::useEnvLight); + renderSettings.def_readwrite("useAnalyticLights", &Scene::RenderSettings::useAnalyticLights); + renderSettings.def_readwrite("useEmissiveLights", &Scene::RenderSettings::useEmissiveLights); + renderSettings.def_readwrite("useGridVolumes", &Scene::RenderSettings::useGridVolumes); + renderSettings.def_readwrite("diffuseAlbedoMultiplier", &Scene::RenderSettings::diffuseAlbedoMultiplier); + renderSettings.def(pybind11::init<>([](bool useEnvLight, bool useAnalyticLights, bool useEmissiveLights, bool useGridVolumes, float diffuseAlbedoMultiplier) { + Scene::RenderSettings settings; + settings.useEnvLight = useEnvLight; + settings.useAnalyticLights = useAnalyticLights; + settings.useEmissiveLights = useEmissiveLights; + settings.useGridVolumes = useGridVolumes; + settings.diffuseAlbedoMultiplier = diffuseAlbedoMultiplier; + return settings; + }), "useEnvLight"_a = true, "useAnalyticLights"_a = true, "useEmissiveLights"_a = true, "useGridVolumes"_a = true, "diffuseAlbedoMultiplier"_a = 1.f); + renderSettings.def("__repr__", [](const Scene::RenderSettings& self) { + return fmt::format( + "SceneRenderSettings(useEnvLight={}, useAnalyticLights={}, useEmissiveLights={}, useGridVolumes={}, diffuseAlbedoMultiplier={})", + self.useEnvLight ? "True" : "False", + self.useAnalyticLights ? "True" : "False", + self.useEmissiveLights ? "True" : "False", + self.useGridVolumes ? "True" : "False", + self.diffuseAlbedoMultiplier + ); + }); // Scene pybind11::class_> scene(m, "Scene"); diff --git a/Source/Falcor/Scene/Scene.h b/Source/Falcor/Scene/Scene.h index 587509a94..6e36d01db 100644 --- a/Source/Falcor/Scene/Scene.h +++ b/Source/Falcor/Scene/Scene.h @@ -108,6 +108,7 @@ namespace Falcor */ class FALCOR_API Scene : public Object { + FALCOR_OBJECT(Scene) public: using GeometryType = ::Falcor::GeometryType; using GeometryTypeFlags = ::Falcor::GeometryTypeFlags; @@ -451,6 +452,7 @@ namespace Falcor /** Create scene from file. \param[in] pDevice GPU device. \param[in] path Import the scene from this file path. + \param[in] settings Optional settings. \return Scene object, or throws an ImporterError if import went wrong. */ static ref create(ref pDevice, const std::filesystem::path& path, const Settings& settings = Settings()); @@ -472,7 +474,7 @@ namespace Falcor The user is responsible to check for this and update all programs that access the scene. \return List of shader defines. */ - Shader::DefineList getSceneDefines() const; + DefineList getSceneDefines() const; /** Get type conformances. These type conformances must be set on all programs that access the scene. @@ -1109,7 +1111,7 @@ namespace Falcor void createMeshUVTiles(const std::vector& meshDesc, const std::vector& indexData, const std::vector& staticData); void updateSceneDefines(); - Shader::DefineList getSceneSDFGridDefines() const; + DefineList getSceneSDFGridDefines() const; /** Set the SDF grid config if this scene contains any SDF grid geometry. */ @@ -1309,8 +1311,8 @@ namespace Falcor Metadata mMetadata; ///< Importer-provided metadata. RenderSettings mRenderSettings; ///< Render settings. RenderSettings mPrevRenderSettings; - Shader::DefineList mSceneDefines; ///< Current list of defines that need to be set on any program accessing the scene. - Shader::DefineList mPrevSceneDefines; ///< List of defines for the previous frame. + DefineList mSceneDefines; ///< Current list of defines that need to be set on any program accessing the scene. + DefineList mPrevSceneDefines; ///< List of defines for the previous frame. Program::TypeConformanceList mTypeConformances; ///< Current list of type conformances that need to be set on any program accessing the scene. UpdateCallback mUpdateCallback; ///< Scene update callback. diff --git a/Source/Falcor/Scene/SceneBuilder.cpp b/Source/Falcor/Scene/SceneBuilder.cpp index 2fc50c805..0783f8238 100644 --- a/Source/Falcor/Scene/SceneBuilder.cpp +++ b/Source/Falcor/Scene/SceneBuilder.cpp @@ -210,8 +210,8 @@ namespace Falcor mSceneData.pMaterials = std::make_unique(mpDevice); } - SceneBuilder::SceneBuilder(ref pDevice, const std::filesystem::path& path, const Settings& settings, Flags buildFlags) - : SceneBuilder(pDevice, settings, buildFlags) + SceneBuilder::SceneBuilder(ref pDevice, const std::filesystem::path& path, const Settings& settings, Flags flags) + : SceneBuilder(pDevice, settings, flags) { std::filesystem::path fullPath; if (!findFileInDataDirectories(path, fullPath)) @@ -220,11 +220,11 @@ namespace Falcor } // Compute scene cache key based on absolute scene path and build flags. - mSceneCacheKey = computeSceneCacheKey(fullPath, buildFlags); + mSceneCacheKey = computeSceneCacheKey(fullPath, flags); // Determine if scene cache should be written after import. - bool useCache = is_set(buildFlags, Flags::UseCache); - bool rebuildCache = is_set(buildFlags, Flags::RebuildCache); + bool useCache = is_set(flags, Flags::UseCache); + bool rebuildCache = is_set(flags, Flags::RebuildCache); mWriteSceneCache = useCache || rebuildCache; // Try to load scene cache if supported, available and requested. @@ -244,6 +244,12 @@ namespace Falcor import(path); } + SceneBuilder::SceneBuilder(ref pDevice, const void* buffer, size_t byteSize, std::string_view extension, const Settings& settings, Flags flags) + : SceneBuilder(pDevice, settings, flags) + { + importFromMemory(buffer, byteSize, extension); + } + SceneBuilder::~SceneBuilder() {} void SceneBuilder::import(const std::filesystem::path& path, const pybind11::dict& dict) @@ -266,6 +272,21 @@ namespace Falcor } } + void SceneBuilder::importFromMemory(const void* buffer, size_t byteSize, std::string_view extension, const pybind11::dict& dict) + { + logInfo("Importing scene from memory"); + + mSceneData.path = ""; + if (auto importer = Importer::create(extension)) + { + importer->importSceneFromMemory(buffer, byteSize, extension, *this, dict); + } + else + { + throw ImporterError("", "Unknown file extension."); + } + } + ref SceneBuilder::getScene() { if (mpScene) return mpScene; diff --git a/Source/Falcor/Scene/SceneBuilder.h b/Source/Falcor/Scene/SceneBuilder.h index 4d1e336d3..d42eea906 100644 --- a/Source/Falcor/Scene/SceneBuilder.h +++ b/Source/Falcor/Scene/SceneBuilder.h @@ -333,6 +333,11 @@ namespace Falcor */ SceneBuilder(ref pDevice, const std::filesystem::path& path, const Settings& settings, Flags flags = Flags::Default); + /** Create a new builder and import a scene/model from memory. + Throws an ImporterError if importing went wrong. + */ + SceneBuilder(ref pDevice, const void* buffer, size_t byteSize, std::string_view extension, const Settings& settings, Flags flags = Flags::Default); + ~SceneBuilder(); /** Import a scene/model file @@ -341,6 +346,15 @@ namespace Falcor */ void import(const std::filesystem::path& path, const pybind11::dict& dict = pybind11::dict()); + /** Import a scene/model file from memory. + \param[in] buffer Memory buffer. + \param[in] byteSize Size in bytes of memory buffer. + \param[in] extension File extension for the format the scene is stored in. + \param[in] dict Optional dictionary. + Throws an ImporterError if something went wrong. + */ + void importFromMemory(const void* buffer, size_t byteSize, std::string_view extension, 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 */ diff --git a/Source/Falcor/Scene/TriangleMesh.h b/Source/Falcor/Scene/TriangleMesh.h index 2b9acb665..2d55c4052 100644 --- a/Source/Falcor/Scene/TriangleMesh.h +++ b/Source/Falcor/Scene/TriangleMesh.h @@ -44,6 +44,7 @@ namespace Falcor */ class FALCOR_API TriangleMesh : public Object { + FALCOR_OBJECT(TriangleMesh) public: struct Vertex { diff --git a/Source/Falcor/Scene/Volume/Grid.cpp b/Source/Falcor/Scene/Volume/Grid.cpp index edd529a95..fa9e06085 100644 --- a/Source/Falcor/Scene/Volume/Grid.cpp +++ b/Source/Falcor/Scene/Volume/Grid.cpp @@ -36,6 +36,7 @@ #include "Utils/Math/Vector.h" #include "Utils/Math/Matrix.h" #include "GlobalState.h" +#include "Utils/PathResolving.h" #ifdef _MSC_VER #pragma warning(push) diff --git a/Source/Falcor/Scene/Volume/Grid.h b/Source/Falcor/Scene/Volume/Grid.h index bcf0f1427..b4532aa8c 100644 --- a/Source/Falcor/Scene/Volume/Grid.h +++ b/Source/Falcor/Scene/Volume/Grid.h @@ -58,6 +58,7 @@ namespace Falcor */ class FALCOR_API Grid : public Object { + FALCOR_OBJECT(Grid) public: /** Create a sphere voxel grid. \param[in] pDevice GPU device. diff --git a/Source/Falcor/Scene/Volume/GridConverter.h b/Source/Falcor/Scene/Volume/GridConverter.h index fd9220910..48948f85f 100644 --- a/Source/Falcor/Scene/Volume/GridConverter.h +++ b/Source/Falcor/Scene/Volume/GridConverter.h @@ -289,13 +289,14 @@ namespace Falcor auto range = NumericRange(0, mLeafDim[0].z); std::for_each(std::execution::par, range.begin(), range.end(), [&](int z) { convertSlice(z); }); for (int mip = 1; mip < 4; ++mip) computeMip(mip); - double dt = CpuTimer::calcDuration(t0, CpuTimer::getCurrentTimePoint()); - logInfo("converted in {}ms: mNonEmptyCount {} vs max {}", dt, mNonEmptyCount, getAtlasMaxBrick()); BrickedGrid bricks; bricks.range = Texture::create3D(pDevice, mLeafDim[0].x, mLeafDim[0].y, mLeafDim[0].z, ResourceFormat::RG16Float, 4, mRangeData.data(), ResourceBindFlags::ShaderResource, false); bricks.indirection = Texture::create3D(pDevice, mLeafDim[0].x, mLeafDim[0].y, mLeafDim[0].z, ResourceFormat::RGBA8Uint, 1, mPtrData.data(), ResourceBindFlags::ShaderResource, false); bricks.atlas = Texture::create3D(pDevice, getAtlasSizePixels().x, getAtlasSizePixels().y, getAtlasSizePixels().z, getAtlasFormat(), 1, mAtlasData.data(), ResourceBindFlags::ShaderResource, false); + + double dt = CpuTimer::calcDuration(t0, CpuTimer::getCurrentTimePoint()); + logDebug("Converted '{}' in {:.4}ms: mNonEmptyCount {} vs max {}", mpFloatGrid->gridName(), dt, mNonEmptyCount.load(), getAtlasMaxBrick()); return bricks; } } diff --git a/Source/Falcor/Scene/Volume/GridVolume.cpp b/Source/Falcor/Scene/Volume/GridVolume.cpp index 5f53e4365..dd25d1840 100644 --- a/Source/Falcor/Scene/Volume/GridVolume.cpp +++ b/Source/Falcor/Scene/Volume/GridVolume.cpp @@ -125,14 +125,21 @@ namespace Falcor return grid != nullptr; } - uint32_t GridVolume::loadGridSequence(GridSlot slot, const std::vector& paths, const std::string& gridname, bool keepEmpty) + GridVolume::GridSequence GridVolume::createGridSequence(ref pDevice, const std::vector& paths, const std::string& gridname, bool keepEmpty) { GridSequence grids; for (const auto& path : paths) { - auto grid = Grid::createFromFile(mpDevice, path, gridname); + auto grid = Grid::createFromFile(pDevice, path, gridname); if (keepEmpty || grid) grids.push_back(grid); } + + return grids; + } + + uint32_t GridVolume::loadGridSequence(GridSlot slot, const std::vector& paths, const std::string& gridname, bool keepEmpty) + { + GridVolume::GridSequence grids = GridVolume::createGridSequence(mpDevice, paths, gridname, keepEmpty); setGridSequence(slot, grids); return (uint32_t)grids.size(); } @@ -240,10 +247,9 @@ namespace Falcor void GridVolume::updatePlayback(double currentTime) { - uint32_t frameCount = getGridFrameCount(); - if (mPlaybackEnabled && frameCount > 0) + if (mPlaybackEnabled && mGridFrameCount > 0) { - uint32_t frameIndex = (uint32_t)std::floor(std::max(0.0, currentTime) * mFrameRate) % frameCount; + uint32_t frameIndex = (mStartFrame + (uint32_t)std::floor(std::max(0.0, currentTime) * mFrameRate)) % mGridFrameCount; setGridFrame(frameIndex); } } @@ -381,6 +387,7 @@ namespace Falcor volume.def_property("gridFrame", &GridVolume::getGridFrame, &GridVolume::setGridFrame); volume.def_property_readonly("gridFrameCount", &GridVolume::getGridFrameCount); volume.def_property("frameRate", &GridVolume::getFrameRate, &GridVolume::setFrameRate); + volume.def_property("startFrame", &GridVolume::getStartFrame, &GridVolume::setStartFrame); volume.def_property("playbackEnabled", &GridVolume::isPlaybackEnabled, &GridVolume::setPlaybackEnabled); volume.def_property("densityGrid", &GridVolume::getDensityGrid, &GridVolume::setDensityGrid); volume.def_property("densityScale", &GridVolume::getDensityScale, &GridVolume::setDensityScale); diff --git a/Source/Falcor/Scene/Volume/GridVolume.h b/Source/Falcor/Scene/Volume/GridVolume.h index e4d1489b1..da9d9e531 100644 --- a/Source/Falcor/Scene/Volume/GridVolume.h +++ b/Source/Falcor/Scene/Volume/GridVolume.h @@ -38,6 +38,7 @@ #include #include #include +#include namespace Falcor { @@ -49,6 +50,7 @@ namespace Falcor */ class FALCOR_API GridVolume : public Animatable { + FALCOR_OBJECT(GridVolume) public: using GridSequence = std::vector>; @@ -115,6 +117,15 @@ namespace Falcor */ bool loadGrid(GridSlot slot, const std::filesystem::path& path, const std::string& gridname); + /** Create a GridSequence from a list of files. + \param[in] pDevice GPU device + \param[in] paths File paths of the grids. Can also include a full path or relative path from a data directory. + \param[in] gridname Name of the grid to load. + \param[in] keepEmpty Add empty (nullptr) grids to the sequence if one cannot be loaded from the file. + \return Returns the resulting GridSequence + */ + static GridSequence createGridSequence(ref pDevice, const std::vector& paths, const std::string& gridname, bool keepEmpty = true); + /** Load a sequence of grids from files to a grid slot. Note: This will replace any existing grid sequence for that slot. \param[in] slot Grid slot. @@ -177,6 +188,14 @@ namespace Falcor */ double getFrameRate() const { return mFrameRate; } + /** Set the grid playback start frame. + */ + void setStartFrame(uint32_t frame) { mStartFrame = mGridFrameCount > 0 ? std::clamp(frame, (uint32_t)0, mGridFrameCount - 1) : 0; } + + /** Get the grid playback start frame + */ + uint32_t getStartFrame() const { return mStartFrame; } + /** Enable/disable grid playback. */ void setPlaybackEnabled(bool enabled); @@ -276,11 +295,13 @@ namespace Falcor uint32_t mGridFrame = 0; uint32_t mGridFrameCount = 1; double mFrameRate = 30.f; + uint32_t mStartFrame = 0; bool mPlaybackEnabled = false; AABB mBounds; GridVolumeData mData; mutable UpdateFlags mUpdates = UpdateFlags::None; + friend class Scene; friend class SceneCache; }; diff --git a/Source/Falcor/Testing/UnitTest.cpp b/Source/Falcor/Testing/UnitTest.cpp index ac9bb6377..493f63d35 100644 --- a/Source/Falcor/Testing/UnitTest.cpp +++ b/Source/Falcor/Testing/UnitTest.cpp @@ -26,59 +26,41 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **************************************************************************/ #include "UnitTest.h" +#include "Core/Version.h" +#include "Core/Platform/OS.h" #include "Core/API/Device.h" #include "Core/API/RenderContext.h" +#include "Core/Program/ProgramManager.h" +#include "Utils/Scripting/Scripting.h" +#include "Utils/Threading.h" #include "Utils/StringUtils.h" #include "Utils/TermColor.h" #include "Utils/Logger.h" #include "Utils/Math/Common.h" #include "Utils/Math/Vector.h" + #include #include +#include +#include + #include #include #include -#include - -#include +#include namespace Falcor { -namespace -{ -struct Test +namespace unittest { - std::string getTitle() const - { - std::string tag; - if (cpuFunc) - { - tag = "CPU"; - } - else - { - tag = "GPU"; - switch (deviceType) - { - case Device::Type::D3D12: - tag += " D3D12"; - break; - case Device::Type::Vulkan: - tag += " Vulkan"; - break; - } - } - - return fmt::format("{}/{} ({})", path.filename(), name, tag); - } +struct TestDesc +{ std::filesystem::path path; std::string name; - std::string skipMessage; + unittest::Options options; CPUTestFunc cpuFunc; GPUTestFunc gpuFunc; - UnitTestDeviceFlags supportedDevices; - Device::Type deviceType; }; struct TestResult @@ -96,46 +78,80 @@ struct TestResult uint64_t elapsedMS = 0; }; -static std::vector& getTestRegistry() +static std::vector& getTestRegistry() { - static std::vector registry; + static std::vector registry; return registry; } -} // end anonymous namespace +class DevicePool +{ +public: + DevicePool(Device::Desc defaultDesc) : mDefaultDesc(defaultDesc) {} -const char* plural(size_t count, const char* suffix) + ref acquireDevice(Device::Type deviceType) + { + std::lock_guard lock(mMutex); + auto& devices = mDevices[deviceType]; + if (devices.empty()) + { + Device::Desc desc = mDefaultDesc; + desc.type = deviceType; + ref device = make_ref(desc); + + // Set global shader defines + DefineList globalDefines = { + {"FALCOR_NVAPI_AVAILABLE", (FALCOR_NVAPI_AVAILABLE && device->getType() == Device::Type::D3D12) ? "1" : "0"}, +#if FALCOR_NVAPI_AVAILABLE + {"NV_SHADER_EXTN_SLOT", "u999"}, +#endif + }; + device->getProgramManager()->addGlobalDefines(globalDefines); + + return device; + } + auto device = devices.back(); + devices.pop_back(); + return device; + } + + void releaseDevice(ref&& device) + { + std::lock_guard lock(mMutex); + mDevices[device->getType()].push_back(device); + } + +private: + Device::Desc mDefaultDesc; + std::mutex mMutex; + std::map>> mDevices; +}; + +inline const char* plural(size_t count, const char* suffix) { if (count == 1) return ""; return suffix; } -void registerCPUTest(const std::filesystem::path& path, const std::string& name, const std::string& skipMessage, CPUTestFunc func) +void registerCPUTest(std::filesystem::path path, std::string name, unittest::Options options, CPUTestFunc func) { - Test test; - test.path = path; - test.name = name; - test.skipMessage = skipMessage; - test.cpuFunc = std::move(func); - getTestRegistry().push_back(test); + TestDesc desc; + desc.path = std::move(path); + desc.name = std::move(name); + desc.options = std::move(options); + desc.cpuFunc = std::move(func); + getTestRegistry().push_back(desc); } -void registerGPUTest( - const std::filesystem::path& path, - const std::string& name, - const std::string& skipMessage, - GPUTestFunc func, - UnitTestDeviceFlags supportedDevices -) +void registerGPUTest(std::filesystem::path path, std::string name, unittest::Options options, GPUTestFunc func) { - Test test; - test.path = path; - test.name = name; - test.skipMessage = skipMessage; - test.gpuFunc = std::move(func); - test.supportedDevices = supportedDevices; - getTestRegistry().push_back(test); + TestDesc desc; + desc.path = std::move(path); + desc.name = std::move(name); + desc.options = std::move(options); + desc.gpuFunc = std::move(func); + getTestRegistry().push_back(desc); } /// Prints the UnitTest report line, making sure it is always printed to the console once. @@ -146,8 +162,14 @@ void reportLine(const std::string_view format, Args&&... args) bool willLogPrint = is_set(Logger::getOutputs(), Logger::OutputFlags::Console) || (is_set(Logger::getOutputs(), Logger::OutputFlags::DebugWindow) && isDebuggerPresent()); + static std::mutex mutex; + std::lock_guard lock(mutex); + if (!willLogPrint) + { std::cout << report << std::endl; + std::flush(std::cout); + } logInfo(report); } @@ -161,55 +183,60 @@ inline void writeXmlReport(const std::filesystem::path& path, const std::vector< pugi::xml_document doc; pugi::xml_node testsuitesNode = doc.append_child("testsuites"); + testsuitesNode.append_attribute("name").set_value("Unit Tests"); - pugi::xml_node testsuiteNode = testsuitesNode.append_child("testsuite"); - testsuiteNode.append_attribute("name").set_value("Unit Tests"); + // Split reports into suites. + std::map>> reportBySuite; + for (const auto& item : report) + reportBySuite[item.first.suiteName].push_back(item); - for (const auto& [test, result] : report) + for (const auto& [suiteName, suiteReport] : reportBySuite) { - pugi::xml_node testcaseNode = testsuiteNode.append_child("testcase"); - testcaseNode.append_attribute("name").set_value(test.getTitle().c_str()); - testcaseNode.append_attribute("time").set_value(result.elapsedMS / 1000.0); + pugi::xml_node testsuiteNode = testsuitesNode.append_child("testsuite"); + testsuiteNode.append_attribute("name").set_value(suiteName.c_str()); - switch (result.status) + for (const auto& [test, result] : suiteReport) { - case TestResult::Status::Passed: - break; - case TestResult::Status::Skipped: - testcaseNode.append_child("skipped"); + pugi::xml_node testcaseNode = testsuiteNode.append_child("testcase"); + testcaseNode.append_attribute("name").set_value(test.name.c_str()); + testcaseNode.append_attribute("time").set_value(result.elapsedMS / 1000.0); + + switch (result.status) + { + case TestResult::Status::Passed: + break; + case TestResult::Status::Skipped: + testcaseNode.append_child("skipped"); + break; + case TestResult::Status::Failed: + default: + { + std::string message = joinStrings(result.messages, "\n"); + testcaseNode.append_child("failure").append_attribute("message").set_value(message.c_str()); + } break; - case TestResult::Status::Failed: - default: - { - std::string message = joinStrings(result.messages, "\n"); - testcaseNode.append_child("failure").append_attribute("message").set_value(message.c_str()); - } - break; + } } } doc.save_file(path.native().c_str()); } -inline TestResult runTest(const Test& test, ref pDevice, Fbo* pTargetFbo) +inline TestResult runTest(const Test& test, DevicePool& devicePool) { if (!test.skipMessage.empty()) return {TestResult::Status::Skipped, {test.skipMessage}}; - if (test.gpuFunc) - { - if (pDevice->getType() == Device::Type::D3D12 && !is_set(test.supportedDevices, UnitTestDeviceFlags::D3D12)) - return {TestResult::Status::Skipped, {"Not supported on D3D12."}}; - if (pDevice->getType() == Device::Type::Vulkan && !is_set(test.supportedDevices, UnitTestDeviceFlags::Vulkan)) - return {TestResult::Status::Skipped, {"Not supported on Vulkan."}}; - } - TestResult result{TestResult::Status::Passed}; - auto startTime = std::chrono::steady_clock::now(); + ref pDevice; + if (test.gpuFunc) + pDevice = devicePool.acquireDevice(test.deviceType); CPUUnitTestContext cpuCtx; - GPUUnitTestContext gpuCtx(pDevice, pTargetFbo); + GPUUnitTestContext gpuCtx(pDevice); + + auto startTime = std::chrono::steady_clock::now(); try { @@ -256,20 +283,17 @@ inline TestResult runTest(const Test& test, ref pDevice, Fbo* pTargetFbo result.elapsedMS = std::chrono::duration_cast(endTime - startTime).count(); // Release GPU resources. - if (test.gpuFunc) + if (pDevice) + { + pDevice->endFrame(); pDevice->flushAndSync(); + devicePool.releaseDevice(std::move(pDevice)); + } return result; } -int32_t runTests( - ref pDevice, - Fbo* pTargetFbo, - UnitTestCategoryFlags categoryFlags, - const std::string& testFilter, - const std::filesystem::path& xmlReportPath, - uint32_t repeatCount -) +inline int32_t runTestsParallel(const RunOptions& options) { // Abort on Ctrl-C. std::atomic abort{false}; @@ -281,65 +305,143 @@ int32_t runTests( } ); - size_t totalTestCount = 0; - std::map> tests; - std::vector> report; - std::map> failedTests; + auto startTime = std::chrono::steady_clock::now(); + + DevicePool devicePool(options.deviceDesc); + + // Gather tests. + std::vector tests = enumerateTests(); + tests = filterTests(tests, options.testSuiteFilter, options.testCaseFilter, options.tagFilter, options.deviceDesc.type); + + std::vector results(tests.size()); + + BS::thread_pool_light threadPool(options.parallel); - // Filter tests. - std::regex testFilterRegex(testFilter, std::regex::icase | std::regex::basic); - for (auto& it : getTestRegistry()) + reportLine("[==========] Running {} test{}.", tests.size(), plural(tests.size(), "s")); + + for (size_t testIndex = 0; testIndex < tests.size(); ++testIndex) { - if (it.cpuFunc && !is_set(categoryFlags, UnitTestCategoryFlags::CPU)) - continue; - if (it.gpuFunc && !is_set(categoryFlags, UnitTestCategoryFlags::GPU)) - continue; + threadPool.push_task( + [&abort, &tests, &results, &devicePool, testIndex]() + { + if (abort) + return; - it.deviceType = pDevice->getType(); + const Test& test = tests[testIndex]; + TestResult& result = results[testIndex]; + std::string repeats; - if (std::regex_search(it.getTitle(), testFilterRegex)) - { - totalTestCount++; - tests[it.path.filename().string()].push_back(it); - } + reportLine("[ RUN ] {}:{}{}", test.suiteName, test.name, repeats); + + result = runTest(test, devicePool); + + std::string statusTag; + switch (result.status) + { + case TestResult::Status::Passed: + statusTag = "[ OK ]"; + break; + case TestResult::Status::Failed: + statusTag = "[ FAILED ]"; + break; + case TestResult::Status::Skipped: + statusTag = "[ SKIPPED ]"; + break; + } + if (!result.extraMessage.empty()) + reportLine("{}", result.extraMessage); + reportLine("{} {}:{}{} ({} ms)", statusTag, test.suiteName, test.name, repeats, result.elapsedMS); + } + ); } - // Sort tests by name. - for (auto& it : tests) + threadPool.wait_for_tasks(); + + if (abort) { - std::sort(it.second.begin(), it.second.end(), [](const Test& a, const Test& b) { return a.name < b.name; }); + reportLine("[ ABORTED ]"); + return 1; } + auto endTime = std::chrono::steady_clock::now(); + uint64_t totalMS = std::chrono::duration_cast(endTime - startTime).count(); + + int32_t failureCount = 0; + for (const auto& result : results) + failureCount += result.status == TestResult::Status::Failed ? 1 : 0; + + reportLine("[==========] {} test{} ran. ({} ms total)", tests.size(), plural(tests.size(), "s"), totalMS); + reportLine("[ PASSED ] {} test{}.", tests.size() - failureCount, plural(tests.size() - failureCount, "s")); + if (failureCount > 0) + { + reportLine("[ FAILED ] {} test{}, listed below.", failureCount, plural(failureCount, "s")); + for (size_t i = 0; i < tests.size(); ++i) + if (results[i].status == TestResult::Status::Failed) + reportLine("[ FAILED ] {}:{}", tests[i].suiteName, tests[i].name); + reportLine(""); + reportLine("{} FAILED TEST{}", failureCount, plural(failureCount, "S")); + } + + return failureCount; +} + +inline int32_t runTestsSerial(const RunOptions& options) +{ + // Abort on Ctrl-C. + std::atomic abort{false}; + setKeyboardInterruptHandler( + [&abort]() + { + reportLine("\nDetected Ctrl-C, aborting ...\n"); + abort = true; + } + ); + + auto startTime = std::chrono::steady_clock::now(); + + DevicePool devicePool(options.deviceDesc); + + // Gather tests. + std::vector tests = enumerateTests(); + tests = filterTests(tests, options.testSuiteFilter, options.testCaseFilter, options.tagFilter, options.deviceDesc.type); + + // Split tests into suites. + std::map> suites; + for (const auto& test : tests) + suites[test.suiteName].push_back(test); + + std::map> failedTests; + std::vector> report; + + size_t suiteCount = suites.size(); + size_t testCount = tests.size(); int32_t failureCount = 0; - uint64_t totalMS = 0; reportLine( - "[==========] Running {} test{} from {} test suite{}.", totalTestCount, plural(totalTestCount, "s"), tests.size(), - plural(tests.size(), "s") + "[==========] Running {} test{} from {} test suite{}.", testCount, plural(testCount, "s"), suiteCount, plural(suiteCount, "s") ); - for (auto suiteIt = tests.begin(); suiteIt != tests.end(); ++suiteIt) + for (const auto& [suiteName, suiteTests] : suites) { if (abort) break; - const std::string& suiteName = suiteIt->first; - reportLine("[----------] {} test{} from {}", suiteIt->second.size(), plural(suiteIt->second.size(), "s"), suiteName); + reportLine("[----------] {} test{} from {}", suiteTests.size(), plural(suiteTests.size(), "s"), suiteName); uint64_t suiteMS = 0; - for (auto& test : suiteIt->second) + for (const auto& test : suiteTests) { if (abort) break; bool success = true; - for (uint32_t repeatIndex = 0; repeatIndex < repeatCount; ++repeatIndex) + for (uint32_t repeatIndex = 0; repeatIndex < options.repeat; ++repeatIndex) { if (abort) break; std::string repeats; - if (repeatCount > 1) - repeats = fmt::format("[{}/{}]", repeatIndex + 1, repeatCount); + if (options.repeat > 1) + repeats = fmt::format("[{}/{}]", repeatIndex + 1, options.repeat); reportLine("[ RUN ] {}:{}{}", suiteName, test.name, repeats); - TestResult result = runTest(test, pDevice, pTargetFbo); + TestResult result = runTest(test, devicePool); report.emplace_back(test, result); std::string statusTag; @@ -367,12 +469,8 @@ int32_t runTests( } } } - reportLine( - "[----------] {} test{} from {} ({} ms total)", suiteIt->second.size(), plural(suiteIt->second.size(), "s"), suiteIt->first, - suiteMS - ); + reportLine("[----------] {} test{} from {} ({} ms total)", suiteTests.size(), plural(suiteTests.size(), "s"), suiteName, suiteMS); reportLine(""); - totalMS += suiteMS; } if (abort) @@ -381,14 +479,17 @@ int32_t runTests( return 1; } - if (!xmlReportPath.empty()) - writeXmlReport(xmlReportPath, report); + auto endTime = std::chrono::steady_clock::now(); + uint64_t totalMS = std::chrono::duration_cast(endTime - startTime).count(); + + if (!options.xmlReportPath.empty()) + writeXmlReport(options.xmlReportPath, report); reportLine( - "[==========] {} test{} from {} test suite{} ran. ({} ms total)", totalTestCount, plural(totalTestCount, "s"), tests.size(), - plural(tests.size(), "s"), totalMS + "[==========] {} test{} from {} test suite{} ran. ({} ms total)", testCount, plural(testCount, "s"), suiteCount, + plural(suiteCount, "s"), totalMS ); - reportLine("[ PASSED ] {} test{}.", totalTestCount - failureCount, plural(totalTestCount - failureCount, "s")); + reportLine("[ PASSED ] {} test{}.", testCount - failureCount, plural(testCount - failureCount, "s")); if (failureCount > 0) { reportLine("[ FAILED ] {} test{}, listed below.", failureCount, plural(failureCount, "s")); @@ -402,6 +503,139 @@ int32_t runTests( return failureCount; } +int32_t runTests(const RunOptions& options) +{ + // Disable logging to console, we don't want to clutter the test runner output with log messages. + Logger::setOutputs(Logger::OutputFlags::File | Logger::OutputFlags::DebugWindow); + + logInfo("Falcor {}", getLongVersionString()); + + OSServices::start(); + Threading::start(); + Scripting::start(); + + int32_t failureCount = options.parallel > 1 ? runTestsParallel(options) : runTestsSerial(options); + + Scripting::shutdown(); + Threading::shutdown(); + OSServices::stop(); + + return failureCount; +} + +std::vector enumerateTests() +{ + std::vector tests; + + for (auto& desc : getTestRegistry()) + { + Test test; + + test.suiteName = desc.path.filename().string(); + test.name = desc.name; + test.tags = desc.options.tags; + test.skipMessage = desc.options.skipMessage; + test.deviceType = Device::Type::Default; + test.cpuFunc = desc.cpuFunc; + test.gpuFunc = desc.gpuFunc; + + if (test.cpuFunc) + { + tests.push_back(test); + } + else if (test.gpuFunc) + { +#if FALCOR_HAS_D3D12 + if (desc.options.deviceTypes.empty() || desc.options.deviceTypes.count(Device::Type::D3D12)) + { + test.deviceType = Device::Type::D3D12; + test.name = fmt::format("{} (D3D12)", desc.name); + tests.push_back(test); + } +#endif +#if FALCOR_HAS_VULKAN + if (desc.options.deviceTypes.empty() || desc.options.deviceTypes.count(Device::Type::Vulkan)) + { + test.deviceType = Device::Type::Vulkan; + test.name = fmt::format("{} (Vulkan)", desc.name); + tests.push_back(test); + } +#endif + } + } + + // Sort by suite name first, followed by test name. + std::sort( + tests.begin(), tests.end(), + [](const Test& a, const Test& b) + { + if (a.suiteName == b.suiteName) + return a.name < b.name; + return a.suiteName < b.suiteName; + } + ); + + return tests; +} + +std::vector filterTests( + std::vector tests, + std::string testSuiteFilter, + std::string testCaseFilter, + std::string tagFilter, + Device::Type deviceType +) +{ + std::vector filtered; + + std::regex suiteFilterRegex(testSuiteFilter, std::regex::icase | std::regex::basic); + std::regex testFilterRegex(testCaseFilter, std::regex::icase | std::regex::basic); + + std::set includeTags; + std::set excludeTags; + for (const auto& token : splitString(tagFilter, ",")) + { + if (token.empty()) + continue; + if (token[0] == '-' || token[0] == '!' || token[0] == '~') + excludeTags.insert(token.substr(1)); + else if (token[0] == '+') + includeTags.insert(token.substr(1)); + else + includeTags.insert(token); + } + + auto matchTags = + [](const std::set& tags, const std::set& includeTags, const std::set& excludeTags) + { + bool include = includeTags.empty(); + bool exclude = false; + + for (const auto& tag : tags) + { + include |= includeTags.count(tag) == 1; + exclude |= excludeTags.count(tag) == 1; + } + + return include && !exclude; + }; + + for (auto&& test : tests) + { + if (!testSuiteFilter.empty() && !std::regex_search(test.suiteName, suiteFilterRegex)) + continue; + if (!testCaseFilter.empty() && !std::regex_search(test.name, testFilterRegex)) + continue; + if (!matchTags(test.tags, includeTags, excludeTags)) + continue; + if (deviceType != Device::Type::Default && test.deviceType != deviceType) + continue; + filtered.push_back(test); + } + + return filtered; +} + /////////////////////////////////////////////////////////////////////////// void UnitTestContext::reportFailure(const std::string& message) @@ -417,8 +651,8 @@ void UnitTestContext::reportFailure(const std::string& message) void GPUUnitTestContext::createProgram( const std::filesystem::path& path, const std::string& entry, - const Program::DefineList& programDefines, - Shader::CompilerFlags flags, + const DefineList& programDefines, + Program::CompilerFlags flags, const std::string& shaderModel, bool createShaderVars ) @@ -433,7 +667,7 @@ void GPUUnitTestContext::createProgram( createVars(); } -void GPUUnitTestContext::createProgram(const Program::Desc& desc, const Program::DefineList& programDefines, bool createShaderVars) +void GPUUnitTestContext::createProgram(const Program::Desc& desc, const DefineList& programDefines, bool createShaderVars) { // Create program. mpProgram = ComputeProgram::create(mpDevice, desc, programDefines); @@ -461,16 +695,16 @@ 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, mpProgram.get(), name, nElements); - FALCOR_ASSERT(mStructuredBuffers[name].pBuffer); + mStructuredBuffers[name] = Buffer::createStructured(mpDevice, mpProgram.get(), name, nElements); if (pInitData) { - size_t expectedDataSize = mStructuredBuffers[name].pBuffer->getStructSize() * mStructuredBuffers[name].pBuffer->getElementCount(); + ref buffer = mStructuredBuffers[name]; + size_t expectedDataSize = buffer->getStructSize() * buffer->getElementCount(); if (initDataSize == 0) initDataSize = expectedDataSize; else if (initDataSize != expectedDataSize) throw ErrorRunningTestException("StructuredBuffer '" + name + "' initial data size mismatch"); - mStructuredBuffers[name].pBuffer->setBlob(pInitData, 0, initDataSize); + buffer->setBlob(pInitData, 0, initDataSize); } } @@ -479,7 +713,7 @@ void GPUUnitTestContext::runProgram(const uint3& dimensions) checkInvariant(mpVars != nullptr, "Program vars not created"); for (const auto& buffer : mStructuredBuffers) { - mpVars->setBuffer(buffer.first, buffer.second.pBuffer); + mpVars->setBuffer(buffer.first, buffer.second); } uint3 groups = div_round_up(dimensions, mThreadGroupSize); @@ -493,27 +727,7 @@ void GPUUnitTestContext::runProgram(const uint3& dimensions) mpDevice->getRenderContext()->dispatch(mpState.get(), mpVars.get(), groups); } -void GPUUnitTestContext::unmapBuffer(const char* bufferName) -{ - FALCOR_ASSERT(mStructuredBuffers.find(bufferName) != mStructuredBuffers.end()); - if (!mStructuredBuffers[bufferName].mapped) - throw ErrorRunningTestException(std::string(bufferName) + ": buffer not mapped"); - mStructuredBuffers[bufferName].pBuffer->unmap(); - mStructuredBuffers[bufferName].mapped = false; -} - -const void* GPUUnitTestContext::mapRawRead(const char* bufferName) -{ - FALCOR_ASSERT(mStructuredBuffers.find(bufferName) != mStructuredBuffers.end()); - if (mStructuredBuffers.find(bufferName) == mStructuredBuffers.end()) - { - throw ErrorRunningTestException(std::string(bufferName) + ": couldn't find buffer to map"); - } - if (mStructuredBuffers[bufferName].mapped) - throw ErrorRunningTestException(std::string(bufferName) + ": buffer already mapped"); - mStructuredBuffers[bufferName].mapped = true; - return mStructuredBuffers[bufferName].pBuffer->map(Buffer::MapType::Read); -} +} // namespace unittest /** * Simple tests of the testing framework. How meta. @@ -557,7 +771,7 @@ GPU_TEST(TestGPUTest) ctx["TestCB"]["scale"] = 2.f; ctx.runProgram(); - const float* s = ctx.mapBuffer("result"); + std::vector s = ctx.readBuffer("result"); // s[i] == 2*i EXPECT(s[1] == 2); EXPECT_EQ(s[1], 2); @@ -566,7 +780,21 @@ GPU_TEST(TestGPUTest) EXPECT_LE(s[4], 8); EXPECT_GT(s[5], 5); EXPECT_GE(s[6], 11); +} + +CPU_TEST(TestSkip1, "skipped") +{ + EXPECT(false); +} + +CPU_TEST(TestSkip2, SKIP("skipped")) +{ + EXPECT(false); +} - ctx.unmapBuffer("result"); +CPU_TEST(TestTags, TAGS("tag1", "tag2", "tag3")) +{ + EXPECT(true); } + } // namespace Falcor diff --git a/Source/Falcor/Testing/UnitTest.h b/Source/Falcor/Testing/UnitTest.h index d59758b0a..95360db10 100644 --- a/Source/Falcor/Testing/UnitTest.h +++ b/Source/Falcor/Testing/UnitTest.h @@ -41,6 +41,7 @@ #include #include #include +#include #include #include #include @@ -54,11 +55,10 @@ namespace Falcor { - class RenderContext; -class CPUUnitTestContext; -class GPUUnitTestContext; +namespace unittest +{ static constexpr int kMaxTestFailures = 25; @@ -85,53 +85,57 @@ class SkippingTestException : public std::runtime_error SkippingTestException(const std::string& what) : std::runtime_error(what.c_str()) {} }; -using CPUTestFunc = std::function; -using GPUTestFunc = std::function; - -enum class UnitTestCategoryFlags +struct RunOptions { - None = 0x0, - CPU = 0x1, - GPU = 0x2, - All = CPU | GPU, + Device::Desc deviceDesc; + std::string testSuiteFilter; + std::string testCaseFilter; + std::string tagFilter; + std::filesystem::path xmlReportPath; + uint32_t parallel = 1; + uint32_t repeat = 1; }; -FALCOR_ENUM_CLASS_OPERATORS(UnitTestCategoryFlags); +FALCOR_API int32_t runTests(const RunOptions& options); + +class CPUUnitTestContext; +class GPUUnitTestContext; -enum class UnitTestDeviceFlags +using CPUTestFunc = std::function; +using GPUTestFunc = std::function; + +struct Test { - D3D12 = 0x1, - Vulkan = 0x2, - All = D3D12 | Vulkan, + std::string suiteName; + std::string name; + std::set tags; + std::string skipMessage; + Device::Type deviceType; + + CPUTestFunc cpuFunc; + GPUTestFunc gpuFunc; }; -FALCOR_ENUM_CLASS_OPERATORS(UnitTestDeviceFlags); +/// Enumerate all tests. +FALCOR_API std::vector enumerateTests(); -FALCOR_API void registerCPUTest( - const std::filesystem::path& path, - const std::string& name, - const std::string& skipMessage, - CPUTestFunc func -); -FALCOR_API void registerGPUTest( - const std::filesystem::path& path, - const std::string& name, - const std::string& skipMessage, - GPUTestFunc func, - UnitTestDeviceFlags supportedDevices -); -FALCOR_API int32_t runTests( - ref pDevice, - Fbo* pTargetFbo, - UnitTestCategoryFlags categoryFlags, - const std::string& testFilterRegexp, - const std::filesystem::path& xmlReportPath, - uint32_t repeatCount = 1 +/// Filter tests by suite and case name. +FALCOR_API std::vector filterTests( + std::vector tests, + std::string testSuiteFilter, + std::string testCaseFilter, + std::string tagFilter, + Device::Type deviceType ); class FALCOR_API UnitTestContext { public: + /** + * Skip the current test at runtime. + */ + void skip(const char* message) { throw SkippingTestException(message); } + /** * reportFailure is called with an error message to report a failing * test. Normally it's only used by the EXPECT_EQ (etc.) macros, @@ -153,7 +157,7 @@ class FALCOR_API CPUUnitTestContext : public UnitTestContext class FALCOR_API GPUUnitTestContext : public UnitTestContext { public: - GPUUnitTestContext(ref pDevice, Fbo* pTargetFbo) : mpDevice(pDevice), mpTargetFbo(pTargetFbo) {} + GPUUnitTestContext(ref pDevice) : mpDevice(pDevice) {} /** * createProgram creates a compute program from the source code at the @@ -164,8 +168,8 @@ class FALCOR_API GPUUnitTestContext : public UnitTestContext void createProgram( const std::filesystem::path& path, const std::string& csEntry = "main", - const Program::DefineList& programDefines = Program::DefineList(), - Shader::CompilerFlags flags = Shader::CompilerFlags::None, + const DefineList& programDefines = DefineList(), + Program::CompilerFlags flags = Program::CompilerFlags::None, const std::string& shaderModel = "", bool createShaderVars = true ); @@ -173,11 +177,7 @@ class FALCOR_API GPUUnitTestContext : public UnitTestContext /** * Create compute program based on program desc and defines. */ - void createProgram( - const Program::Desc& desc, - const Program::DefineList& programDefines = Program::DefineList(), - bool createShaderVars = true - ); + void createProgram(const Program::Desc& desc, const DefineList& programDefines = DefineList(), bool createShaderVars = true); /** * (Re-)create the shader variables. Call this if vars were not @@ -219,6 +219,24 @@ class FALCOR_API GPUUnitTestContext : public UnitTestContext */ void allocateStructuredBuffer(const std::string& name, uint32_t nElements, const void* pInitData = nullptr, size_t initDataSize = 0); + /** + * Read the contents of a structured buffer into a vector. + */ + template + std::vector readBuffer(const char* bufferName) + { + FALCOR_ASSERT(mStructuredBuffers.find(bufferName) != mStructuredBuffers.end()); + auto it = mStructuredBuffers.find(bufferName); + if (it == mStructuredBuffers.end()) + throw ErrorRunningTestException(std::string(bufferName) + ": couldn't find buffer to map"); + ref buffer = it->second; + const T* data = reinterpret_cast(buffer->map(Buffer::MapType::Read)); + size_t size = buffer->getSize() / sizeof(T); + std::vector result(data, data + size); + buffer->unmap(); + return result; + } + /** * runProgram runs the compute program that was specified in * |createProgram|, where the total number of threads that runs is @@ -234,23 +252,6 @@ class FALCOR_API GPUUnitTestContext : public UnitTestContext */ void runProgram(uint32_t width = 1, uint32_t height = 1, uint32_t depth = 1) { runProgram(uint3(width, height, depth)); } - /** - * mapBuffer returns a pointer to the named structured buffer. - * Returns nullptr if no such buffer exists. SFINAE is used to - * require that a the requested pointer is const. - */ - template - T* mapBuffer(const char* bufferName, typename std::enable_if::value>::type* = 0) - { - return reinterpret_cast(mapRawRead(bufferName)); - } - - /** - * unmapBuffer unmaps a buffer after it's been used after a call to - * |mapBuffer()|. - */ - void unmapBuffer(const char* bufferName); - /** * Returns the current Falcor render device. */ @@ -261,37 +262,95 @@ class FALCOR_API GPUUnitTestContext : public UnitTestContext */ RenderContext* getRenderContext() const { return mpDevice->getRenderContext(); } - /** - * Returns the current FBO. - */ - Fbo* getTargetFbo() const { return mpTargetFbo; } - /** * Returns the program. */ ComputeProgram* getProgram() const { return mpProgram.get(); } private: - const void* mapRawRead(const char* bufferName); - // Internal state ref mpDevice; - Fbo* mpTargetFbo; ref mpState; ref mpProgram; ref mpVars; uint3 mThreadGroupSize = {0, 0, 0}; - struct ParameterBuffer + std::map> mStructuredBuffers; +}; + +struct Tags +{ + Tags(std::string tag) { tags.push_back(std::move(tag)); } + Tags(std::initializer_list tags_) { - ref pBuffer; - bool mapped = false; - }; - std::map mStructuredBuffers; + for (const char* tag : tags_) + tags.push_back(tag); + } + + std::vector tags; }; -namespace unittest +struct Skip { + Skip(std::string msg_) : msg(std::move(msg_)) {} + + std::string msg; +}; + +struct DeviceTypes +{ + DeviceTypes(Device::Type deviceType) { deviceTypes.insert(deviceType); } + DeviceTypes(std::initializer_list deviceTypes_) + { + for (Device::Type type : deviceTypes_) + deviceTypes.insert(type); + } + + std::set deviceTypes; +}; + +struct Options +{ + std::set tags; + std::string skipMessage; + std::set deviceTypes; +}; + +inline void applyArg(Options& options, Tags&& arg) +{ + options.tags.insert(arg.tags.begin(), arg.tags.end()); +} + +inline void applyArg(Options& options, Skip&& arg) +{ + options.skipMessage = std::move(arg.msg); +} + +inline void applyArg(Options& options, DeviceTypes&& arg) +{ + options.deviceTypes.insert(arg.deviceTypes.begin(), arg.deviceTypes.end()); +} + +inline void applyArg(Options& options, Device::Type deviceType) +{ + options.deviceTypes.insert(deviceType); +} + +template +inline void applyArg(Options& options, const char (&skipMsg)[N]) +{ + options.skipMessage = std::string(skipMsg, N - 1); +} + +template +void applyArgs(Options& options, Args&&... args) +{ + (applyArg(options, std::forward(args)), ...); +} + +FALCOR_API void registerCPUTest(std::filesystem::path path, std::string name, unittest::Options options, CPUTestFunc func); +FALCOR_API void registerGPUTest(std::filesystem::path path, std::string name, unittest::Options options, GPUTestFunc func); + /** * StreamSink is a utility class used by the testing framework that either * captures values printed via C++'s operator<< (as with regular @@ -462,57 +521,95 @@ inline std::optional createBinaryMessage(std::string_view lhsStr, s * Start of user-facing API */ +using UnitTestContext = unittest::UnitTestContext; +using CPUUnitTestContext = unittest::CPUUnitTestContext; +using GPUUnitTestContext = unittest::GPUUnitTestContext; + /** - * Macro to define a CPU unit test. The optional skip message will - * disable the test from running without leading to a failure. - * The macro defines an instance of the |CPUUnitTestRegisterer| class, - * which in turn registers the test with the test framework when its - * constructor executes at program startup time. Next, it starts the - * definition of the testing function, up to the point at which - * the user should supply an open brace and start writing code. + * Macro to define a CPU unit test. The optional arguments include: + * + * - SKIP(msg): Skip the test with the given message (expands to unittest::Skip). + * - TAGS(...): A list of tags to associate with the test (expands to unittest::Tags). + * + * Some examples: + * + * CPU_TEST(Test1) {} // Test is always run + * CPU_TEST(Test2, SKIP("Not implemented")) {} // Test is skipped + * CPU_TEST(Test3, TAGS("tag1", "tag2")) {} // Test is run and tagged with "tag1" and "tag2" + * + * For convenience, and for backwards compatibility, a string can be used as an + * optional argument to skip the test: + * + * CPU_TEST(Test4, "Not implemented") {} // Test is skipped (same as above) + * + * Note: All CPU tests are implicitly tagged with "cpu". */ -#define CPU_TEST(name, ...) \ - static void CPUUnitTest##name(CPUUnitTestContext& ctx); \ - struct CPUUnitTestRegisterer##name \ - { \ - CPUUnitTestRegisterer##name() \ - { \ - std::filesystem::path path = __FILE__; \ - const char* skipMessage = "" __VA_ARGS__; \ - registerCPUTest(path, #name, skipMessage, CPUUnitTest##name); \ - } \ - } RegisterCPUTest##name; \ +#define CPU_TEST(name, ...) \ + static void CPUUnitTest##name(CPUUnitTestContext& ctx); \ + struct CPUUnitTestRegisterer##name \ + { \ + CPUUnitTestRegisterer##name() \ + { \ + std::filesystem::path path = __FILE__; \ + unittest::Options options; \ + applyArgs(options, ##__VA_ARGS__); \ + options.tags.insert("cpu"); \ + unittest::registerCPUTest(path, #name, options, CPUUnitTest##name); \ + } \ + } RegisterCPUTest##name; \ static void CPUUnitTest##name(CPUUnitTestContext& ctx) /* over to the user for the braces */ /** - * Macro to define a GPU unit test. The optional skip message will - * disable the test from running without leading to a failure. - * The macro works in the same ways as CPU_TEST(). + * Macro to define a GPU unit test. The optional arguments include: + * + * - SKIP(msg): Skip the test with the given message (expands to unittest::Skip). + * - TAGS(...): A list of tags to associate with the test (expands to unittest::Tags). + * - DEVICE_TYPES(...): A list of device types to run the test on (expands to unittest::DeviceTypes). + * + * Some examples: + * + * GPU_TEST(Test1) {} // Test is always run + * GPU_TEST(Test2, SKIP("Not implemented")) {} // Test is skipped + * GPU_TEST(Test3, TAGS("tag1", "tag2")) {} // Test is run and tagged with "tag1" and "tag2" + * GPU_TEST(Test4, DEVICE_TYPES(Device::Type::D3D12)) {} // Test is only run on D3D12 + * + * For convenience, and for backwards compatibility, a string can be used as an + * optional argument to skip the test: + * + * GPU_TEST(Test5, "Not implemented") {} // Test is skipped (same as above) + * + * Also, Device::Type values can be used as optional arguments to specify a + * device type to run the test on: + * + * GPU_TEST(Test6, Device::Type::D3D12) {} // Test is only run on D3D12 (same as above) + * + * Note: All GPU tests are implicitly tagged with "gpu". */ -#define GPU_TEST_INTERNAL(name, flags, ...) \ - static void GPUUnitTest##name(GPUUnitTestContext& ctx); \ - struct GPUUnitTestRegisterer##name \ - { \ - GPUUnitTestRegisterer##name() \ - { \ - std::filesystem::path path = __FILE__; \ - const char* skipMessage = "" __VA_ARGS__; \ - registerGPUTest(path, #name, skipMessage, GPUUnitTest##name, flags); \ - } \ - } RegisterGPUTest##name; \ +#define GPU_TEST(name, ...) \ + static void GPUUnitTest##name(GPUUnitTestContext& ctx); \ + struct GPUUnitTestRegisterer##name \ + { \ + GPUUnitTestRegisterer##name() \ + { \ + std::filesystem::path path = __FILE__; \ + unittest::Options options; \ + applyArgs(options, ##__VA_ARGS__); \ + options.tags.insert("gpu"); \ + unittest::registerGPUTest(path, #name, options, GPUUnitTest##name); \ + } \ + } RegisterGPUTest##name; \ static void GPUUnitTest##name(GPUUnitTestContext& ctx) /* over to the user for the braces */ -#define GPU_TEST(name, ...) GPU_TEST_INTERNAL(name, UnitTestDeviceFlags::All, __VA_ARGS__) +// clang-format off -/** - * Define GPU_TEST_D3D12 macro that defines a GPU unit test only supported on D3D12. - */ -#define GPU_TEST_D3D12(name, ...) GPU_TEST_INTERNAL(name, UnitTestDeviceFlags::D3D12, __VA_ARGS__) +/// Used as an argument of CPU_TEST/GPU_TEST to tag a test with a set of strings. +#define TAGS(...) ::Falcor::unittest::Tags{__VA_ARGS__} +/// Used as an argument of CPU_TEST/GPU_TEST to mark a test to be skipped. +#define SKIP(msg) ::Falcor::unittest::Skip{msg} +/// Used as an argument of GPU_TEST to mark a test to only run for certain devices. +#define DEVICE_TYPES(...) ::Falcor::unittest::DeviceTypes{__VA_ARGS__} -/** - * Define GPU_TEST_VULKAN macro that defines a GPU unit test only supported on Vulkan. - */ -#define GPU_TEST_VULKAN(name, ...) GPU_TEST_INTERNAL(name, UnitTestDeviceFlags::Vulkan, __VA_ARGS__) +// clang-format on /** * Macro definitions for the GPU unit testing framework. Note that they diff --git a/Source/Falcor/Utils/Algorithm/BitonicSort.cpp b/Source/Falcor/Utils/Algorithm/BitonicSort.cpp index 0990d3b4a..b7e894f31 100644 --- a/Source/Falcor/Utils/Algorithm/BitonicSort.cpp +++ b/Source/Falcor/Utils/Algorithm/BitonicSort.cpp @@ -43,7 +43,7 @@ BitonicSort::BitonicSort(ref pDevice) : mpDevice(pDevice) mSort.pState = ComputeState::create(mpDevice); // Create shaders - Program::DefineList defines; + 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); diff --git a/Source/Falcor/Utils/Algorithm/ParallelReduction.cpp b/Source/Falcor/Utils/Algorithm/ParallelReduction.cpp index 8820b42db..fec334ece 100644 --- a/Source/Falcor/Utils/Algorithm/ParallelReduction.cpp +++ b/Source/Falcor/Utils/Algorithm/ParallelReduction.cpp @@ -40,9 +40,9 @@ 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); + DefineList defines = {{"REDUCTION_TYPE", "1"}, {"FORMAT_CHANNELS", "1"}, {"FORMAT_TYPE", "1"}}; + mpInitialProgram = ComputeProgram::createFromFile(mpDevice, kShaderFile, "initialPass", defines); + mpFinalProgram = ComputeProgram::createFromFile(mpDevice, kShaderFile, "finalPass", defines); mpVars = ComputeVars::create(mpDevice, mpInitialProgram.get()); // Check assumptions on thread group sizes. The initial pass is a 2D dispatch, the final pass a 1D. @@ -150,7 +150,7 @@ void ParallelReduction::execute( const uint32_t channelCount = getFormatChannelCount(pInput->getFormat()); FALCOR_ASSERT(channelCount >= 1 && channelCount <= 4); - Program::DefineList defines; + 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)); diff --git a/Source/Falcor/Utils/Algorithm/PrefixSum.cpp b/Source/Falcor/Utils/Algorithm/PrefixSum.cpp index d2a2d861d..4754ae66e 100644 --- a/Source/Falcor/Utils/Algorithm/PrefixSum.cpp +++ b/Source/Falcor/Utils/Algorithm/PrefixSum.cpp @@ -42,7 +42,7 @@ const uint32_t kGroupSize = 1024; PrefixSum::PrefixSum(ref pDevice) : mpDevice(pDevice) { // Create shaders and state. - Program::DefineList defines = {{"GROUP_SIZE", std::to_string(kGroupSize)}}; + 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); diff --git a/Source/Falcor/Utils/Geometry/IntersectionHelpers.slang b/Source/Falcor/Utils/Geometry/IntersectionHelpers.slang index edc3cf81d..906b13f84 100644 --- a/Source/Falcor/Utils/Geometry/IntersectionHelpers.slang +++ b/Source/Falcor/Utils/Geometry/IntersectionHelpers.slang @@ -42,6 +42,8 @@ import Utils.Math.MathHelpers; */ bool intersectRaySphere(const float3 rayOrigin, const float3 rayDir, const float3 sphereCenter, const float sphereRadius, out float t) { + t = {}; + // Implementation is taken from Chapter 7 of Ray-Tracing Gems float3 f = rayOrigin - sphereCenter; float a = dot(rayDir, rayDir); @@ -106,6 +108,9 @@ bool intersectRayAABB(const float3 rayOrigin, const float3 rayDir, const float3 */ bool intersectRayTriangle(const float3 rayOrigin, const float3 rayDir, const float3 vertices[3], out float3 barycentrics, out float t) { + barycentrics = {}; + t = {}; + const float3 absDir = abs(rayDir); uint axis = 0; if (absDir.y > absDir.x && absDir.y > absDir.z) diff --git a/Source/Falcor/Utils/HostDeviceShared.slangh b/Source/Falcor/Utils/HostDeviceShared.slangh index 0d9355607..d258fb980 100644 --- a/Source/Falcor/Utils/HostDeviceShared.slangh +++ b/Source/Falcor/Utils/HostDeviceShared.slangh @@ -55,6 +55,7 @@ #define CONST_FUNCTION const #define STD_NAMESPACE std:: +#include "Core/Enum.h" #include "Utils/Math/ScalarMath.h" #include "Utils/Math/Vector.h" #include "Utils/Math/Matrix.h" @@ -85,4 +86,7 @@ namespace Falcor #define CONST_FUNCTION #define STD_NAMESPACE +#define FALCOR_ENUM_INFO(T, ...) +#define FALCOR_ENUM_REGISTER(T) + #endif // HOST_CODE diff --git a/Source/Falcor/Utils/Image/Bitmap.cpp b/Source/Falcor/Utils/Image/Bitmap.cpp index d6b3be791..b74d89638 100644 --- a/Source/Falcor/Utils/Image/Bitmap.cpp +++ b/Source/Falcor/Utils/Image/Bitmap.cpp @@ -405,8 +405,7 @@ Bitmap::FileFormat Bitmap::getFormatFromFileExtension(const std::string& ext) if (kExtensions[i] == ext) return Bitmap::FileFormat(i); } - reportError("Can't find a matching format for file extension '" + ext + "'"); - return Bitmap::FileFormat(-1); + throw ArgumentError("Can't find a matching format for file extension '{}'.", ext); } FileDialogFilterVec Bitmap::getFileDialogFilters(ResourceFormat format) @@ -479,22 +478,13 @@ void Bitmap::saveImage( ) { if (pData == nullptr) - { - reportError("Bitmap::saveImage provided no data to save."); - return; - } + throw ArgumentError("Provided data must not be nullptr."); if (is_set(exportFlags, ExportFlags::Uncompressed) && is_set(exportFlags, ExportFlags::Lossy)) - { - reportError("Bitmap::saveImage incompatible flags: lossy cannot be combined with uncompressed."); - return; - } + throw ArgumentError("Incompatible flags: lossy cannot be combined with uncompressed."); if (fileFormat == FileFormat::DdsFile) - { - reportError("Bitmap::saveImage cannot save DDS files. Use ImageIO instead."); - return; - } + throw ArgumentError("Cannot save DDS files. Use ImageIO instead."); int flags = 0; FIBITMAP* pImage = nullptr; @@ -530,8 +520,7 @@ void Bitmap::saveImage( } else if (bytesPerPixel != 16 && bytesPerPixel != 12) { - reportError("Bitmap::saveImage supports only 32-bit/channel RGB/RGBA or 16-bit RGBA images as PFM/EXR files."); - return; + throw ArgumentError("Only support for 32-bit/channel RGB/RGBA or 16-bit RGBA images as PFM/EXR files."); } const bool exportAlpha = is_set(exportFlags, ExportFlags::ExportAlpha); @@ -539,22 +528,13 @@ void Bitmap::saveImage( if (fileFormat == Bitmap::FileFormat::PfmFile) { if (is_set(exportFlags, ExportFlags::Lossy)) - { - reportError("Bitmap::saveImage: PFM does not support lossy compression mode."); - return; - } + throw ArgumentError("PFM does not support lossy compression mode."); if (exportAlpha) - { - reportError("Bitmap::saveImage: PFM does not support alpha channel."); - return; - } + throw ArgumentError("PFM does not support alpha channel."); } if (exportAlpha && bytesPerPixel != 16) - { - reportError("Bitmap::saveImage requesting to export alpha-channel to EXR file, but the resource doesn't have an alpha-channel"); - return; - } + throw ArgumentError("Requesting to export alpha-channel to EXR file, but the resource doesn't have an alpha-channel"); // Upload the image manually and flip it vertically bool scanlineCopy = exportAlpha ? bytesPerPixel == 16 : bytesPerPixel == 12; @@ -663,9 +643,8 @@ void Bitmap::saveImage( } if (!FreeImage_Save(toFreeImageFormat(fileFormat), pImage, path.string().c_str(), flags)) - { - reportError("Bitmap::saveImage: FreeImage failed to save image"); - } + throw RuntimeError("FreeImage failed to save image"); + FreeImage_Unload(pImage); } } // namespace Falcor diff --git a/Source/Falcor/Utils/Logger.cpp b/Source/Falcor/Utils/Logger.cpp index 67ce7c4e3..755f5b378 100644 --- a/Source/Falcor/Utils/Logger.cpp +++ b/Source/Falcor/Utils/Logger.cpp @@ -28,12 +28,17 @@ #include "Logger.h" #include "Core/Assert.h" #include "Core/Platform/OS.h" +#include "Utils/Scripting/ScriptBindings.h" #include +#include +#include +#include namespace Falcor { namespace { +std::mutex sMutex; Logger::Level sVerbosity = Logger::Level::Info; Logger::OutputFlags sOutputs = Logger::OutputFlags::Console | Logger::OutputFlags::File | Logger::OutputFlags::DebugWindow; std::filesystem::path sLogFilePath; @@ -99,7 +104,7 @@ void Logger::shutdown() #endif } -const char* getLogLevelString(Logger::Level level) +inline const char* getLogLevelString(Logger::Level level) { switch (level) { @@ -119,13 +124,43 @@ const char* getLogLevelString(Logger::Level level) } } -void Logger::log(Level level, const std::string_view msg) +class MessageDeduplicator { +public: + static MessageDeduplicator& instance() + { + static MessageDeduplicator sInstance; + return sInstance; + } + + bool isDuplicate(std::string_view msg) + { + std::lock_guard lock(mMutex); + auto it = mStrings.find(msg); + if (it != mStrings.end()) + return true; + mStrings.insert(std::string(msg)); + return false; + } + +private: + MessageDeduplicator() = default; + + std::mutex mMutex; + std::set> mStrings; +}; + +void Logger::log(Level level, const std::string_view msg, Frequency frequency) +{ + std::lock_guard lock(sMutex); #if FALCOR_ENABLE_LOGGER if (level <= sVerbosity) { std::string s = fmt::format("{} {}\n", getLogLevelString(level), msg); + if (frequency == Frequency::Once && MessageDeduplicator::instance().isDuplicate(s)) + return; + // Write to console. if (is_set(sOutputs, OutputFlags::Console)) { @@ -149,43 +184,89 @@ void Logger::log(Level level, const std::string_view msg) #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) { + std::lock_guard lock(sMutex); sVerbosity = level; } + Logger::Level Logger::getVerbosity() { + std::lock_guard lock(sMutex); return sVerbosity; } void Logger::setOutputs(OutputFlags outputs) { + std::lock_guard lock(sMutex); sOutputs = outputs; } + Logger::OutputFlags Logger::getOutputs() { + std::lock_guard lock(sMutex); return sOutputs; } -const std::filesystem::path& Logger::getLogFilePath() +void Logger::setLogFilePath(const std::filesystem::path& path) +{ + std::lock_guard lock(sMutex); +#if FALCOR_ENABLE_LOGGER + if (sLogFile) + { + fclose(sLogFile); + sLogFile = nullptr; + sInitialized = false; + } + sLogFilePath = path; +#else + return false; +#endif +} + +std::filesystem::path Logger::getLogFilePath() { + std::lock_guard lock(sMutex); return sLogFilePath; } + +FALCOR_SCRIPT_BINDING(Logger) +{ + using namespace pybind11::literals; + + pybind11::class_ logger(m, "Logger"); + + pybind11::enum_ level(logger, "Level"); + level.value("Disabled", Logger::Level::Disabled); + level.value("Fatal", Logger::Level::Fatal); + level.value("Error", Logger::Level::Error); + level.value("Warning", Logger::Level::Warning); + level.value("Info", Logger::Level::Info); + level.value("Debug", Logger::Level::Debug); + + pybind11::enum_ outputFlags(logger, "OutputFlags"); + outputFlags.value("None_", Logger::OutputFlags::None); + outputFlags.value("Console", Logger::OutputFlags::Console); + outputFlags.value("File", Logger::OutputFlags::File); + outputFlags.value("DebugWindow", Logger::OutputFlags::DebugWindow); + + logger.def_property_static( + "verbosity", [](pybind11::object) { return Logger::getVerbosity(); }, + [](pybind11::object, Logger::Level verbosity) { Logger::setVerbosity(verbosity); } + ); + logger.def_property_static( + "outputs", [](pybind11::object) { return Logger::getOutputs(); }, + [](pybind11::object, Logger::OutputFlags outputs) { Logger::setOutputs(outputs); } + ); + logger.def_property_static( + "log_file_path", [](pybind11::object) { return Logger::getLogFilePath(); }, + [](pybind11::object, std::filesystem::path path) { Logger::setLogFilePath(path); } + ); + + logger.def_static( + "log", [](Logger::Level level, const std::string_view msg) { Logger::log(level, msg, Logger::Frequency::Always); }, "level"_a, + "msg"_a + ); +} + } // namespace Falcor diff --git a/Source/Falcor/Utils/Logger.h b/Source/Falcor/Utils/Logger.h index 5a76f27a6..d6814f46a 100644 --- a/Source/Falcor/Utils/Logger.h +++ b/Source/Falcor/Utils/Logger.h @@ -55,9 +55,16 @@ class FALCOR_API Logger Count, ///< Keep this last. }; + enum class Frequency + { + Always, ///< Reports the message always + Once, ///< Reports the message only first time the exact string appears + }; + /// Log output. enum class OutputFlags { + None = 0x0, ///< No output. Console = 0x2, ///< Output to console (stdout/stderr). File = 0x1, ///< Output to log file. DebugWindow = 0x4, ///< Output to debug window (if debugger is attached). @@ -94,17 +101,15 @@ class FALCOR_API Logger /** * 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); + static void 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(); + static std::filesystem::path getLogFilePath(); /** * Check if the logger is enabled. @@ -116,7 +121,7 @@ class FALCOR_API Logger * @param[in] level Log level. * @param[in] msg Log message. */ - static void log(Level level, const std::string_view msg); + static void log(Level level, const std::string_view msg, Frequency frequency = Frequency::Always); private: Logger() = delete; @@ -161,6 +166,17 @@ inline void logWarning(fmt::format_string format, Args&&... args) Logger::log(Logger::Level::Warning, fmt::format(format, std::forward(args)...)); } +inline void logWarningOnce(const std::string_view msg) +{ + Logger::log(Logger::Level::Warning, msg, Logger::Frequency::Once); +} + +template +inline void logWarningOnce(fmt::format_string format, Args&&... args) +{ + Logger::log(Logger::Level::Warning, fmt::format(format, std::forward(args)...), Logger::Frequency::Once); +} + inline void logError(const std::string_view msg) { Logger::log(Logger::Level::Error, msg); @@ -172,6 +188,17 @@ inline void logError(fmt::format_string format, Args&&... args) Logger::log(Logger::Level::Error, fmt::format(format, std::forward(args)...)); } +inline void logErrorOnce(const std::string_view msg) +{ + Logger::log(Logger::Level::Error, msg, Logger::Frequency::Once); +} + +template +inline void logErrorOnce(fmt::format_string format, Args&&... args) +{ + Logger::log(Logger::Level::Error, fmt::format(format, std::forward(args)...), Logger::Frequency::Once); +} + inline void logFatal(const std::string_view msg) { Logger::log(Logger::Level::Fatal, msg); diff --git a/Source/Falcor/Utils/Math/MatrixMath.h b/Source/Falcor/Utils/Math/MatrixMath.h index ae53d5f4a..4c4444a5d 100644 --- a/Source/Falcor/Utils/Math/MatrixMath.h +++ b/Source/Falcor/Utils/Math/MatrixMath.h @@ -776,7 +776,7 @@ struct fmt::formatter> : formatter::format(matrix.getRow(r), ctx); } - out = ::fmt::format_to(out, "}}"); + out = fmt::format_to(out, "}}"); return out; } }; diff --git a/Source/Falcor/Utils/Math/QuaternionMath.h b/Source/Falcor/Utils/Math/QuaternionMath.h index 2adb23635..8296f5537 100644 --- a/Source/Falcor/Utils/Math/QuaternionMath.h +++ b/Source/Falcor/Utils/Math/QuaternionMath.h @@ -521,13 +521,13 @@ struct std::hash<::Falcor::math::quat> /// Quaternion string formatter. template -struct fmt::formatter<::Falcor::math::quat> : formatter +struct fmt::formatter> : formatter { template - auto format(const ::Falcor::math::quat& v, FormatContext& ctx) const + 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); + out = fmt::format_to(out, "{{{}, {}, {}, {}}}", v.x, v.y, v.z, v.w); return out; } }; diff --git a/Source/Falcor/Utils/Math/ScalarTypes.h b/Source/Falcor/Utils/Math/ScalarTypes.h index f7488368a..f8d2d1247 100644 --- a/Source/Falcor/Utils/Math/ScalarTypes.h +++ b/Source/Falcor/Utils/Math/ScalarTypes.h @@ -139,10 +139,10 @@ using math::operator""h; // Formatter for the float16_t. template<> -struct fmt::formatter<::Falcor::math::float16_t> : formatter +struct fmt::formatter : formatter { template - auto format(::Falcor::math::float16_t value, FormatContext& ctx) const + auto format(Falcor::math::float16_t value, FormatContext& ctx) const { return formatter::format(float(value), ctx); } diff --git a/Source/Falcor/Utils/Math/VectorMath.h b/Source/Falcor/Utils/Math/VectorMath.h index a8635980a..6588b64e1 100644 --- a/Source/Falcor/Utils/Math/VectorMath.h +++ b/Source/Falcor/Utils/Math/VectorMath.h @@ -1800,10 +1800,10 @@ struct std::hash<::Falcor::math::vector> /// Vector string formatter. template -struct fmt::formatter<::Falcor::math::vector> : formatter +struct fmt::formatter> : formatter { template - auto format(const ::Falcor::math::vector& v, FormatContext& ctx) const + auto format(const Falcor::math::vector& v, FormatContext& ctx) const { auto out = ctx.out(); for (int i = 0; i < N; ++i) diff --git a/Source/Falcor/Utils/Properties.cpp b/Source/Falcor/Utils/Properties.cpp new file mode 100644 index 000000000..afd4ebd1e --- /dev/null +++ b/Source/Falcor/Utils/Properties.cpp @@ -0,0 +1,494 @@ +/*************************************************************************** + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. + # + # Redistribution and use in source and binary forms, with or without + # modification, are permitted provided that the following conditions + # are met: + # * Redistributions of source code must retain the above copyright + # notice, this list of conditions and the following disclaimer. + # * Redistributions in binary form must reproduce the above copyright + # notice, this list of conditions and the following disclaimer in the + # documentation and/or other materials provided with the distribution. + # * Neither the name of NVIDIA CORPORATION nor the names of its + # contributors may be used to endorse or promote products derived + # from this software without specific prior written permission. + # + # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY + # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + **************************************************************************/ +#include "Properties.h" + +#include +#include +#include + +namespace Falcor +{ +using json = Properties::json; + +namespace +{ +template +json vecToJson(const VecT& vec) +{ + auto constexpr length = VecT::length(); + auto j = json::array(); + for (size_t i = 0; i < length; ++i) + j.push_back(vec[i]); + return j; +} + +template +json valueToJson(const T& value) +{ + if constexpr (std::is_same_v) + { + return value.toJson(); + } + else if constexpr (std::is_same_v || std::is_same_v || std::is_same_v) + { + return vecToJson(value); + } + else if constexpr (std::is_same_v || std::is_same_v || std::is_same_v) + { + return vecToJson(value); + } + else if constexpr (std::is_same_v || std::is_same_v || std::is_same_v) + { + return vecToJson(value); + } + else + { + return json(value); + } +} + +template +VecT vecFromJson(const json& json, std::string_view name) +{ + auto constexpr length = VecT::length(); + if (!json.is_array()) + throw RuntimeError("Property '{}' is not an array.", name); + if (json.size() != length) + throw RuntimeError("Property '{}' has an invalid number of elements.", name); + VecT result; + for (size_t i = 0; i < length; ++i) + json[i].get_to(result[i]); + return result; +} + +template +T valueFromJson(const json& json, std::string_view name) +{ + if constexpr (std::is_same_v) + { + if (!json.is_boolean()) + throw RuntimeError("Property '{}' is not a boolean.", name); + return static_cast(json); + } + else if constexpr (std::is_integral_v && std::is_signed_v) + { + if (!json.is_number_integer()) + throw RuntimeError("Property '{}' is not an integer.", name); + return static_cast(json); + } + else if constexpr (std::is_integral_v && !std::is_signed_v) + { + // if (!json.is_number_intenger() !json.is_number_unsigned()) throw RuntimeError("Property '{}' is not an unsigned integer.", name); + return static_cast(json); + } + else if constexpr (std::is_floating_point_v) + { + // Allow integers to be converted to floating point + if (!json.is_number_float() && !json.is_number_integer()) + throw RuntimeError("Property '{}' is not a floating point value or integer.", name); + return static_cast(json); + } + else if constexpr (std::is_same_v) + { + if (!json.is_string()) + throw RuntimeError("Property '{}' is not a string.", name); + return static_cast(json); + } + else if constexpr (std::is_same_v) + { + if (!json.is_string()) + throw RuntimeError("Property '{}' is not a string/path.", name); + return static_cast(json); + } + else if constexpr (std::is_same_v) + { + if (!json.is_object()) + throw RuntimeError("Property '{}' is not an object.", name); + return Properties(json); + } + else if constexpr (std::is_same_v || std::is_same_v || std::is_same_v) + { + return vecFromJson(json, name); + } + else if constexpr (std::is_same_v || std::is_same_v || std::is_same_v) + { + return vecFromJson(json, name); + } + else if constexpr (std::is_same_v || std::is_same_v || std::is_same_v) + { + return vecFromJson(json, name); + } +} + +json pythonToJson(const pybind11::handle& obj) +{ + if (obj.ptr() == nullptr || obj.is_none()) + { + return nullptr; + } + if (pybind11::isinstance(obj)) + { + return obj.cast(); + } + if (pybind11::isinstance(obj)) + { + try + { + json::number_integer_t s = obj.cast(); + if (pybind11::int_(s).equal(obj)) + { + return s; + } + } + catch (...) + {} + try + { + json::number_unsigned_t u = obj.cast(); + if (pybind11::int_(u).equal(obj)) + { + return u; + } + } + catch (...) + {} + throw std::runtime_error( + "pythonToJson received an integer out of range for both json::number_integer_t and json::number_unsigned_t type: " + + pybind11::repr(obj).cast() + ); + } + if (pybind11::isinstance(obj)) + { + return obj.cast(); + } + // if (pybind11::isinstance(obj)) + // { + // pybind11::module base64 = pybind11::module::import("base64"); + // return base64.attr("b64encode")(obj).attr("decode")("utf-8").cast(); + // } + if (pybind11::isinstance(obj)) + { + return obj.cast(); + } + if (pybind11::isinstance(obj) || pybind11::isinstance(obj)) + { + auto out = json::array(); + for (const pybind11::handle value : obj) + { + out.push_back(pythonToJson(value)); + } + return out; + } + if (pybind11::isinstance(obj)) + { + auto out = json::object(); + for (const pybind11::handle key : obj) + { + out[pybind11::str(key).cast()] = pythonToJson(obj[key]); + } + return out; + } + // isinstance doesn't work for all PathLike so we also check the type name. + const char* tp_name = obj.ptr()->ob_type->tp_name; + if (pybind11::isinstance(obj) || std::strcmp(tp_name, "WindowsPath") == 0 || + std::strcmp(tp_name, "PosixPath") == 0) + { + return obj.cast().string(); + } +#define VEC_TO_JSON(T) \ + if (pybind11::isinstance(obj)) \ + { \ + return vecToJson(obj.cast()); \ + } + VEC_TO_JSON(int2) + VEC_TO_JSON(int3) + VEC_TO_JSON(int4) + VEC_TO_JSON(uint2) + VEC_TO_JSON(uint3) + VEC_TO_JSON(uint4) + VEC_TO_JSON(float2) + VEC_TO_JSON(float3) + VEC_TO_JSON(float4) + throw std::runtime_error("pythonToJson not implemented for this type of object: " + pybind11::repr(obj).cast()); +} + +pybind11::object jsonToPython(const json& j) +{ + if (j.is_null()) + { + return pybind11::none(); + } + else if (j.is_boolean()) + { + return pybind11::bool_(j.get()); + } + else if (j.is_number_unsigned()) + { + return pybind11::int_(j.get()); + } + else if (j.is_number_integer()) + { + return pybind11::int_(j.get()); + } + else if (j.is_number_float()) + { + return pybind11::float_(j.get()); + } + else if (j.is_string()) + { + return pybind11::str(j.get()); + } + else if (j.is_array()) + { + pybind11::list obj(j.size()); + for (std::size_t i = 0; i < j.size(); i++) + { + obj[i] = jsonToPython(j[i]); + } + return std::move(obj); + } + else // Object + { + pybind11::dict obj; + for (json::const_iterator it = j.cbegin(); it != j.cend(); ++it) + { + obj[pybind11::str(it.key())] = jsonToPython(it.value()); + } + return std::move(obj); + } +} +} // namespace + +Properties::Properties() +{ + mJson = std::make_unique(json::object()); +} + +Properties::Properties(const json& j) +{ + mJson = std::make_unique(j); +} + +Properties::Properties(json&& j) +{ + mJson = std::make_unique(std::move(j)); +} + +Properties::Properties(const pybind11::dict& d) +{ + mJson = std::make_unique(pythonToJson(d)); +} + +Properties::Properties(const Properties& other) +{ + mJson = std::make_unique(*other.mJson); +} + +Properties::Properties(Properties&& other) +{ + mJson = std::move(other.mJson); +} + +Properties::~Properties() {} + +Properties& Properties::operator=(const Properties& other) +{ + mJson = std::make_unique(*other.mJson); + return *this; +} + +Properties& Properties::operator=(Properties&& other) +{ + mJson = std::move(other.mJson); + return *this; +} + +json Properties::toJson() const +{ + return *mJson; +} + +pybind11::dict Properties::toPython() const +{ + return jsonToPython(*mJson); +} + +std::string Properties::dump(int indent) const +{ + return mJson->dump(indent); +} + +bool Properties::empty() const +{ + return mJson->empty(); +} + +bool Properties::has(std::string_view name) const +{ + return mJson->find(name) != mJson->end(); +} + +template +void Properties::setInternal(std::string_view name, const T& value) +{ + (*mJson)[name] = valueToJson(value); +} + +template +bool Properties::getInternal(std::string_view name, T& value) const +{ + if (auto it = mJson->find(name); it != mJson->end()) + { + value = valueFromJson(*it, name); + return true; + } + else + return false; +} + +bool Properties::operator==(const Properties& rhs) const +{ + return *mJson == *rhs.mJson; +} + +bool Properties::operator!=(const Properties& rhs) const +{ + return !(*this == rhs); +} + +// ------------------------------------------------------------------ +// Iterator +// ------------------------------------------------------------------ + +struct Properties::Iterator::Impl +{ + Properties& properties; + json::iterator it; +}; + +Properties::Iterator::Iterator(std::unique_ptr impl) : mImpl(std::move(impl)) {} +Properties::Iterator::~Iterator() {} + +bool Properties::Iterator::operator==(const Iterator& other) const +{ + return mImpl->it == other.mImpl->it; +} + +bool Properties::Iterator::operator!=(const Iterator& other) const +{ + return mImpl->it != other.mImpl->it; +} + +Properties::Iterator& Properties::Iterator::operator++() +{ + mImpl->it++; + return *this; +} + +std::pair Properties::Iterator::operator*() +{ + return std::make_pair(mImpl->it.key(), Value(mImpl->properties, mImpl->it.key())); +} + +struct Properties::ConstIterator::Impl +{ + const Properties& properties; + json::const_iterator it; +}; + +Properties::ConstIterator::ConstIterator(std::unique_ptr impl) : mImpl(std::move(impl)) {} +Properties::ConstIterator::~ConstIterator() {} + +bool Properties::ConstIterator::operator==(const ConstIterator& other) const +{ + return mImpl->it == other.mImpl->it; +} + +bool Properties::ConstIterator::operator!=(const ConstIterator& other) const +{ + return mImpl->it != other.mImpl->it; +} + +Properties::ConstIterator& Properties::ConstIterator::operator++() +{ + mImpl->it++; + return *this; +} + +std::pair Properties::ConstIterator::operator*() +{ + return std::make_pair(mImpl->it.key(), ConstValue(mImpl->properties, mImpl->it.key())); +} + +Properties::Iterator Properties::begin() +{ + return Iterator(std::make_unique(Iterator::Impl{*this, mJson->begin()})); +} + +Properties::Iterator Properties::end() +{ + return Iterator(std::make_unique(Iterator::Impl{*this, mJson->end()})); +} + +Properties::ConstIterator Properties::begin() const +{ + return ConstIterator(std::make_unique(ConstIterator::Impl{*this, mJson->begin()})); +} + +Properties::ConstIterator Properties::end() const +{ + return ConstIterator(std::make_unique(ConstIterator::Impl{*this, mJson->end()})); +} + +#define EXPORT_PROPERTY_ACCESSOR(T) \ + template FALCOR_API void Properties::setInternal(std::string_view, const T&); \ + template FALCOR_API bool Properties::getInternal(std::string_view, T&) const; + +EXPORT_PROPERTY_ACCESSOR(bool) +EXPORT_PROPERTY_ACCESSOR(int32_t) +EXPORT_PROPERTY_ACCESSOR(int64_t) +EXPORT_PROPERTY_ACCESSOR(uint32_t) +EXPORT_PROPERTY_ACCESSOR(uint64_t) +EXPORT_PROPERTY_ACCESSOR(float) +EXPORT_PROPERTY_ACCESSOR(double) +EXPORT_PROPERTY_ACCESSOR(std::string) +EXPORT_PROPERTY_ACCESSOR(std::filesystem::path) +EXPORT_PROPERTY_ACCESSOR(int2) +EXPORT_PROPERTY_ACCESSOR(int3) +EXPORT_PROPERTY_ACCESSOR(int4) +EXPORT_PROPERTY_ACCESSOR(uint2) +EXPORT_PROPERTY_ACCESSOR(uint3) +EXPORT_PROPERTY_ACCESSOR(uint4) +EXPORT_PROPERTY_ACCESSOR(float2) +EXPORT_PROPERTY_ACCESSOR(float3) +EXPORT_PROPERTY_ACCESSOR(float4) +EXPORT_PROPERTY_ACCESSOR(Properties) + +#undef EXPORT_PROPERTY_ACCESSOR + +} // namespace Falcor diff --git a/Source/Falcor/Utils/Properties.h b/Source/Falcor/Utils/Properties.h new file mode 100644 index 000000000..8d9ab784c --- /dev/null +++ b/Source/Falcor/Utils/Properties.h @@ -0,0 +1,473 @@ +/*************************************************************************** + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. + # + # Redistribution and use in source and binary forms, with or without + # modification, are permitted provided that the following conditions + # are met: + # * Redistributions of source code must retain the above copyright + # notice, this list of conditions and the following disclaimer. + # * Redistributions in binary form must reproduce the above copyright + # notice, this list of conditions and the following disclaimer in the + # documentation and/or other materials provided with the distribution. + # * Neither the name of NVIDIA CORPORATION nor the names of its + # contributors may be used to endorse or promote products derived + # from this software without specific prior written permission. + # + # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY + # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + # (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/Assert.h" +#include "Core/Enum.h" +#include "Utils/Math/VectorTypes.h" + +// Do not include the full "nlohmann/json.hpp" header here, it will increase compile time considerably. +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace Falcor +{ + +class Properties; + +namespace detail +{ + +/// Primary template handles all types not supporting the operation. +template class, typename = std::void_t<>> +struct detect : std::false_type +{}; + +/// Specialization recognizes/validates only types supporting the archetype. +template class Op> +struct detect>> : std::true_type +{}; + +class DummyArchive +{}; + +template +using serialize_t = decltype(&T::template serialize); + +/// Check if the type T has a void serialize(Archive&) member function template. +template +inline constexpr bool has_serialize_v = detect::value; + +} // namespace detail + +template +Properties serializeToProperties(const T& value); + +template +T deserializeFromProperties(const Properties& props); + +/** + * A class for storing properties. + * + * Properties are stored as a JSON object. The JSON object is ordered, so the order of properties is preserved. + * Using JSON as a backing storage, properties can easily be serialized to/from files. + * This class also supports conversion to/from python dictionaries, making it easy to specify properties from python. + * + * For usage patterns, look at the unit tests. + */ +class FALCOR_API Properties +{ +public: + using json = nlohmann::ordered_json; + + Properties(); + Properties(const json& j); + Properties(json&& j); + Properties(const pybind11::dict& d); + + Properties(const Properties& other); + Properties(Properties&& other); + + ~Properties(); + + Properties& operator=(const Properties& other); + Properties& operator=(Properties&& other); + + /// Converts the properties to a JSON object. + json toJson() const; + + /// Converts the properties to a python dictionary. + pybind11::dict toPython() const; + + /// Dumps the properties to a string. + std::string dump(int indent = -1) const; + + /// Check if the properties are empty. + bool empty() const; + + /// Check if a property exists. + bool has(std::string_view name) const; + + /// Set a property. + template + void set(std::string_view name, const T& value) + { + if constexpr (has_enum_info_v) + { + setInternal(name, enumToString(value)); + } + else if constexpr (detail::has_serialize_v) + { + setInternal(name, serializeToProperties(value)); + } + else + { + setInternal(name, value); + } + } + + /// Get a property. + /// Throws if property does not exist or has the wrong type. + template + T get(std::string_view name) const + { + if constexpr (has_enum_info_v) + { + std::string value; + if (!getInternal(name, value)) + throw RuntimeError("Property '{}' does not exist.", name); + return stringToEnum(value); + } + else if constexpr (detail::has_serialize_v) + { + Properties props; + if (!getInternal(name, props)) + throw RuntimeError("Property '{}' does not exist.", name); + return deserializeFromProperties(props); + } + else + { + T value; + if (!getInternal(name, value)) + throw RuntimeError("Property '{}' does not exist.", name); + return value; + } + } + + /// Get a property. + /// Returns the default value if the property does not exist. + /// Throws if the property exists but has the wrong type. + template + T get(std::string_view name, const T& def) const + { + if constexpr (has_enum_info_v) + { + std::string value; + if (!getInternal(name, value)) + return def; + return stringToEnum(value); + } + else if constexpr (detail::has_serialize_v) + { + Properties props; + if (!getInternal(name, props)) + return def; + return deserializeFromProperties(props); + } + else + { + T value; + if (!getInternal(name, value)) + return def; + return value; + } + } + + /// Get a property. + /// Stores the value to the passed reference and returns true if it exists. + /// Returns false otherwise. + /// Throws if the property exists but has the wrong type. + template + bool getTo(std::string_view name, T& value) const + { + if constexpr (has_enum_info_v) + { + std::string enumString; + bool result = getInternal(name, enumString); + if (result) + value = stringToEnum(enumString); + return result; + } + else if constexpr (detail::has_serialize_v) + { + Properties props; + bool result = getInternal(name, props); + if (result) + value = deserializeFromProperties(props); + return result; + } + else + { + return getInternal(name, value); + } + } + + /// Get a property. + /// Returns empty optional if the property does not exist. + /// Throws if the property exists but has the wrong type. + template + std::optional getOpt(std::string_view name) const + { + if constexpr (has_enum_info_v) + { + std::string enumString; + bool result = getInternal(name, enumString); + return result ? std::make_optional(stringToEnum(enumString)) : std::nullopt; + } + else if constexpr (detail::has_serialize_v) + { + Properties props; + bool result = getInternal(name, props); + return result ? std::make_optional(deserializeFromProperties(props)) : std::nullopt; + } + else + { + T value; + bool result = getInternal(name, value); + return result ? std::make_optional(value) : std::nullopt; + } + } + + /// Convenience overload for handling C strings. + void set(std::string_view name, const char* value) { set(name, std::string(value)); } + + // -------------------------------------------------------------------- + // Comparison + // -------------------------------------------------------------------- + + bool operator==(const Properties& rhs) const; + bool operator!=(const Properties& rhs) const; + + // -------------------------------------------------------------------- + // Accessors and iterators + // -------------------------------------------------------------------- + + /// Value accessor. + class Value + { + public: + template + void operator=(const T& value) const + { + mProperties.set(mName, value); + } + + template + operator T() const + { + return mProperties.get(mName); + } + + private: + Value(Properties& properties, std::string_view name) : mProperties(properties), mName(name) {} + Properties& mProperties; + std::string mName; + + friend class Properties; + }; + + /// Constant value accessor. + class ConstValue + { + public: + template + operator T() const + { + return mProperties.get(mName); + } + + private: + ConstValue(const Properties& properties, std::string_view name) : mProperties(properties), mName(name) {} + const Properties& mProperties; + std::string mName; + + friend class Properties; + }; + + /// Iterator. + class FALCOR_API Iterator + { + public: + bool operator==(const Iterator& other) const; + bool operator!=(const Iterator& other) const; + Iterator& operator++(); + std::pair operator*(); + ~Iterator(); + + private: + struct Impl; + Iterator(std::unique_ptr impl); + std::unique_ptr mImpl; + friend class Properties; + }; + + /// Constant iterator. + class FALCOR_API ConstIterator + { + public: + bool operator==(const ConstIterator& other) const; + bool operator!=(const ConstIterator& other) const; + ConstIterator& operator++(); + std::pair operator*(); + ~ConstIterator(); + + private: + struct Impl; + ConstIterator(std::unique_ptr impl); + std::unique_ptr mImpl; + friend class Properties; + }; + + Value operator[](std::string_view name) { return Value(*this, name); } + const ConstValue operator[](std::string_view name) const { return ConstValue(*this, name); } + + Iterator begin(); + Iterator end(); + + ConstIterator begin() const; + ConstIterator end() const; + +private: + template + void setInternal(std::string_view name, const T& value); + + template + bool getInternal(std::string_view name, T& value) const; + + std::unique_ptr mJson; +}; + +#define EXTERN_PROPERTY_ACCESSOR(T) \ + extern template FALCOR_API void Properties::setInternal(std::string_view, const T&); \ + extern template FALCOR_API bool Properties::getInternal(std::string_view, T&) const; + +EXTERN_PROPERTY_ACCESSOR(bool) +EXTERN_PROPERTY_ACCESSOR(int32_t) +EXTERN_PROPERTY_ACCESSOR(int64_t) +EXTERN_PROPERTY_ACCESSOR(uint32_t) +EXTERN_PROPERTY_ACCESSOR(uint64_t) +EXTERN_PROPERTY_ACCESSOR(float) +EXTERN_PROPERTY_ACCESSOR(double) +EXTERN_PROPERTY_ACCESSOR(std::string) +EXTERN_PROPERTY_ACCESSOR(std::filesystem::path) +EXTERN_PROPERTY_ACCESSOR(int2) +EXTERN_PROPERTY_ACCESSOR(int3) +EXTERN_PROPERTY_ACCESSOR(int4) +EXTERN_PROPERTY_ACCESSOR(uint2) +EXTERN_PROPERTY_ACCESSOR(uint3) +EXTERN_PROPERTY_ACCESSOR(uint4) +EXTERN_PROPERTY_ACCESSOR(float2) +EXTERN_PROPERTY_ACCESSOR(float3) +EXTERN_PROPERTY_ACCESSOR(float4) +EXTERN_PROPERTY_ACCESSOR(Properties) + +#undef EXTERN_PROPERTY_ACCESSOR + +/** + * Helper class for serializing objects into Properties. + */ +class PropertiesWriter +{ +public: + template + void operator()(std::string_view name, const T& value) + { + if constexpr (detail::has_serialize_v) + mProperties.set(name, PropertiesWriter::write(value)); + else + mProperties.set(name, value); + } + + template + static Properties write(const T& value) + { + PropertiesWriter writer; + const_cast(value).serialize(writer); + return writer.mProperties; + } + +private: + PropertiesWriter() = default; + Properties mProperties; +}; + +/** + * Helper class for deserializing objects from Properties. + */ +class PropertiesReader +{ +public: + template + void operator()(std::string_view name, T& value) + { + if constexpr (detail::has_serialize_v) + { + Properties props; + if (mProperties.getTo(name, props)) + value = PropertiesReader::read(props); + } + else + mProperties.getTo(name, value); + } + + template + static T read(const Properties& props) + { + T value; + PropertiesReader reader(props); + value.serialize(reader); + return value; + } + +private: + PropertiesReader(const Properties& props) : mProperties(props) {} + const Properties& mProperties; +}; + +template +Properties serializeToProperties(const T& value) +{ + return PropertiesWriter::write(value); +} + +template +T deserializeFromProperties(const Properties& props) +{ + return PropertiesReader::read(props); +} + +} // namespace Falcor + +template<> +struct fmt::formatter : formatter +{ + template + auto format(const Falcor::Properties& props, FormatContext& ctx) const + { + return formatter::format(props.dump(), ctx); + } +}; diff --git a/Source/Falcor/Utils/SampleGenerators/CPUSampleGenerator.h b/Source/Falcor/Utils/SampleGenerators/CPUSampleGenerator.h index 408dcbe6e..013f5e025 100644 --- a/Source/Falcor/Utils/SampleGenerators/CPUSampleGenerator.h +++ b/Source/Falcor/Utils/SampleGenerators/CPUSampleGenerator.h @@ -37,6 +37,7 @@ namespace Falcor */ class FALCOR_API CPUSampleGenerator : public Object { + FALCOR_OBJECT(CPUSampleGenerator) public: virtual ~CPUSampleGenerator() = default; diff --git a/Source/Falcor/Utils/Sampling/SampleGenerator.cpp b/Source/Falcor/Utils/Sampling/SampleGenerator.cpp index 8b67cf7f0..3a11a69e7 100644 --- a/Source/Falcor/Utils/Sampling/SampleGenerator.cpp +++ b/Source/Falcor/Utils/Sampling/SampleGenerator.cpp @@ -44,9 +44,9 @@ ref SampleGenerator::create(ref pDevice, uint32_t type) } } -Shader::DefineList SampleGenerator::getDefines() const +DefineList SampleGenerator::getDefines() const { - Shader::DefineList defines; + DefineList defines; defines.add("SAMPLE_GENERATOR_TYPE", std::to_string(mType)); return defines; } diff --git a/Source/Falcor/Utils/Sampling/SampleGenerator.h b/Source/Falcor/Utils/Sampling/SampleGenerator.h index 62a14503f..96ecffa40 100644 --- a/Source/Falcor/Utils/Sampling/SampleGenerator.h +++ b/Source/Falcor/Utils/Sampling/SampleGenerator.h @@ -29,7 +29,7 @@ #include "SampleGeneratorType.slangh" #include "Core/Macros.h" #include "Core/Object.h" -#include "Core/API/Shader.h" +#include "Core/Program/DefineList.h" #include "Core/Program/ShaderVar.h" #include "Utils/UI/Gui.h" #include @@ -48,6 +48,7 @@ class RenderContext; */ class FALCOR_API SampleGenerator : public Object { + FALCOR_OBJECT(SampleGenerator) public: virtual ~SampleGenerator() = default; @@ -63,7 +64,7 @@ class FALCOR_API SampleGenerator : public Object * 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; + virtual DefineList getDefines() const; /** * Binds the data to a program vars object. diff --git a/Source/Falcor/Utils/Sampling/SampleGeneratorInterface.slang b/Source/Falcor/Utils/Sampling/SampleGeneratorInterface.slang index 2d167ada6..781124395 100644 --- a/Source/Falcor/Utils/Sampling/SampleGeneratorInterface.slang +++ b/Source/Falcor/Utils/Sampling/SampleGeneratorInterface.slang @@ -73,3 +73,14 @@ float3 sampleNext3D(inout S sg) sample.z = sampleNext1D(sg); return sample; } + +float4 sampleNext4D(inout S sg) +{ + float4 sample; + // Don't use the float4 initializer to ensure consistent order of evaluation. + sample.x = sampleNext1D(sg); + sample.y = sampleNext1D(sg); + sample.z = sampleNext1D(sg); + sample.w = sampleNext1D(sg); + return sample; +} diff --git a/Source/Falcor/Utils/Scripting/Dictionary.h b/Source/Falcor/Utils/Scripting/PythonDictionary.h similarity index 90% rename from Source/Falcor/Utils/Scripting/Dictionary.h rename to Source/Falcor/Utils/Scripting/PythonDictionary.h index 430473f52..2fb48d041 100644 --- a/Source/Falcor/Utils/Scripting/Dictionary.h +++ b/Source/Falcor/Utils/Scripting/PythonDictionary.h @@ -26,6 +26,7 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **************************************************************************/ #pragma once +#include "Core/Enum.h" #include #include #include @@ -34,13 +35,13 @@ namespace Falcor { -class Dictionary +class PythonDictionary { public: using Container = pybind11::dict; - Dictionary() = default; - Dictionary(const Container& c) : mMap(c) {} + PythonDictionary() = default; + PythonDictionary(const Container& c) : mMap(c) {} class Value { @@ -58,7 +59,10 @@ class Dictionary template void operator=(const T& t) { - mContainer[mName.c_str()] = t; + if constexpr (has_enum_info_v) + mContainer[mName.c_str()] = enumToString(t); + else + mContainer[mName.c_str()] = t; } void operator=(const std::filesystem::path& path) @@ -70,7 +74,10 @@ class Dictionary template operator T() const { - return mContainer[mName.c_str()].cast(); + if constexpr (has_enum_info_v) + return stringToEnum(mContainer[mName.c_str()].cast()); + else + return mContainer[mName.c_str()].cast(); } private: diff --git a/Source/Falcor/Utils/Scripting/ScriptBindings.cpp b/Source/Falcor/Utils/Scripting/ScriptBindings.cpp index b0142a890..0fcd74c29 100644 --- a/Source/Falcor/Utils/Scripting/ScriptBindings.cpp +++ b/Source/Falcor/Utils/Scripting/ScriptBindings.cpp @@ -328,6 +328,10 @@ void initModule(pybind11::module& m) "loadPlugin", [](const std::string& name) { PluginManager::instance().loadPluginByName(name); }, "name"_a ); // PYTHONDEPRECATED +#if FALCOR_ENABLE_OBJECT_TRACKING + m.def("dump_alive_objects", []() { Object::dumpAliveObjects(); }); +#endif + // Bind all deferred bindings. for (auto& binding : getDeferredBindings()) binding.bind(m); diff --git a/Source/Falcor/Utils/Scripting/ScriptBindings.h b/Source/Falcor/Utils/Scripting/ScriptBindings.h index 91598285f..6c3339182 100644 --- a/Source/Falcor/Utils/Scripting/ScriptBindings.h +++ b/Source/Falcor/Utils/Scripting/ScriptBindings.h @@ -27,6 +27,7 @@ **************************************************************************/ #pragma once #include "Core/Errors.h" +#include "Core/Enum.h" #include "Core/ObjectPython.h" #include #include @@ -107,155 +108,6 @@ static std::string repr(const T& value) 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; - * }; - * - * 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; - - 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; - } - -private: - static T init(const pybind11::kwargs& args) - { - 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 + ')'; - } - - 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) - { - t[i] = fields[i].getter(obj); - } - return 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) - { - 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; - }; - - static Info& info() - { - static Info staticInfo; - return staticInfo; - } -}; - #ifndef _staticlibrary #define FALCOR_SCRIPT_BINDING(_name) \ static void ScriptBinding##_name(pybind11::module& m); \ diff --git a/Source/Falcor/Utils/Scripting/ScriptWriter.h b/Source/Falcor/Utils/Scripting/ScriptWriter.h index eb26d93a6..a8af6f370 100644 --- a/Source/Falcor/Utils/Scripting/ScriptWriter.h +++ b/Source/Falcor/Utils/Scripting/ScriptWriter.h @@ -27,7 +27,7 @@ **************************************************************************/ #pragma once #include "ScriptBindings.h" -#include "Dictionary.h" +#include "PythonDictionary.h" #include "Core/Platform/OS.h" #include #include @@ -101,9 +101,15 @@ std::string ScriptWriter::getArgString(const T& arg) } template<> -inline std::string ScriptWriter::getArgString(const Dictionary& dictionary) +inline std::string ScriptWriter::getArgString(const PythonDictionary& dict) { - return dictionary.toString(); + return dict.toString(); +} + +template<> +inline std::string ScriptWriter::getArgString(const pybind11::dict& dict) +{ + return pybind11::str(static_cast(dict)); } template<> diff --git a/Source/Falcor/Utils/Scripting/Scripting.h b/Source/Falcor/Utils/Scripting/Scripting.h index d5fd1130e..bea738616 100644 --- a/Source/Falcor/Utils/Scripting/Scripting.h +++ b/Source/Falcor/Utils/Scripting/Scripting.h @@ -57,6 +57,13 @@ class FALCOR_API Scripting mGlobals["__builtins__"] = pybind11::globals()["__builtins__"]; } + ~Context() + { + // We need to manually cleanup the globals dictionary to avoid keeping references to objects. + for (const auto& it : mGlobals) + mGlobals[it.first] = nullptr; + } + template struct ObjectDesc { diff --git a/Source/Falcor/Utils/Settings.cpp b/Source/Falcor/Utils/Settings.cpp index 449db0c89..c4ba50243 100644 --- a/Source/Falcor/Utils/Settings.cpp +++ b/Source/Falcor/Utils/Settings.cpp @@ -65,6 +65,21 @@ std::vector toStrings(const nlohmann::json& value) } // namespace +Settings& Settings::getGlobalSettings() +{ + static Settings globalSettings = []() + { + Settings settings; + // Load settings from runtime directory first. + settings.addOptions(getRuntimeDirectory() / "settings.json"); + // Override with user settings. + if (!getHomeDirectory().empty()) + settings.addOptions(getHomeDirectory() / ".falcor" / "settings.json"); + return settings; + }(); + return globalSettings; +} + void Settings::addOptions(const pybind11::dict& options) { auto json = pyjson::to_json(options); diff --git a/Source/Falcor/Utils/Settings.h b/Source/Falcor/Utils/Settings.h index 0f02e5633..56cf8dc5a 100644 --- a/Source/Falcor/Utils/Settings.h +++ b/Source/Falcor/Utils/Settings.h @@ -46,7 +46,6 @@ class dict; namespace Falcor { -class Dictionary; class Settings; class SettingsProperties { @@ -298,6 +297,9 @@ class SettingsProperties class FALCOR_API Settings { public: + /// Get the global settings instance. + static Settings& getGlobalSettings(); + Settings() : mData(1) {} SettingsProperties getOptions() const { return SettingsProperties(&getActive().mOptions); } diff --git a/Source/Falcor/Utils/StringFormatters.h b/Source/Falcor/Utils/StringFormatters.h index ebea9bc2e..b74671096 100644 --- a/Source/Falcor/Utils/StringFormatters.h +++ b/Source/Falcor/Utils/StringFormatters.h @@ -31,10 +31,8 @@ #include #include -namespace fmt -{ template<> -struct formatter : formatter +struct fmt::formatter : formatter { template auto format(const std::filesystem::path& p, FormatContext& ctx) @@ -44,18 +42,16 @@ struct formatter : formatter }; template -struct formatter> : fmt::formatter +struct fmt::formatter> : formatter { template auto format(const std::optional& opt, FormatContext& ctx) { if (opt) { - fmt::formatter::format(*opt, ctx); + formatter::format(*opt, ctx); return ctx.out(); } return fmt::format_to(ctx.out(), "nullopt"); } }; - -} // namespace fmt diff --git a/Source/Falcor/Utils/UI/Gui.h b/Source/Falcor/Utils/UI/Gui.h index 05032ca85..7929f72cb 100644 --- a/Source/Falcor/Utils/UI/Gui.h +++ b/Source/Falcor/Utils/UI/Gui.h @@ -27,10 +27,12 @@ **************************************************************************/ #pragma once #include "Core/Macros.h" +#include "Core/Enum.h" #include "Core/API/Texture.h" #include "Core/API/FBO.h" #include "Utils/Math/Vector.h" #include "Utils/Color/SampledSpectrum.h" +#include #include #include #include @@ -164,6 +166,37 @@ class FALCOR_API Gui */ bool dropdown(const char label[], const DropdownList& values, uint32_t& var, bool sameLine = false); + /** + * Adds a dropdown menu for an enum setup with FALCOR_ENUM_INFO. + * @param[in] label The name of the dropdown menu. + * @param[in] var A reference to a user variable that will be updated directly when a dropdown option 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 + */ + template, bool> = true> + bool dropdown(const char label[], T& var, bool sameLine = false) + { + bool changed = false; + if (sameLine) + ImGui::SameLine(); + const std::string& currentValue = enumToString(var); + if (ImGui::BeginCombo(label, currentValue.c_str())) + { + const auto& items = EnumInfo::items(); + for (const auto& item : items) + { + bool selected = var == item.first; + if (ImGui::Selectable(item.second.c_str(), &selected)) + { + var = item.first; + changed = true; + } + } + ImGui::EndCombo(); + } + return changed; + } + /** * Button. Will return true if the button was pressed * @param[in] label Text to display on the button diff --git a/Source/RenderPasses/AccumulatePass/AccumulatePass.cpp b/Source/RenderPasses/AccumulatePass/AccumulatePass.cpp index 4a7558871..413ed1a7f 100644 --- a/Source/RenderPasses/AccumulatePass/AccumulatePass.cpp +++ b/Source/RenderPasses/AccumulatePass/AccumulatePass.cpp @@ -33,16 +33,6 @@ static void regAccumulatePass(pybind11::module& m) pybind11::class_> pass(m, "AccumulatePass"); pass.def_property("enabled", &AccumulatePass::isEnabled, &AccumulatePass::setEnabled); pass.def("reset", &AccumulatePass::reset); - - pybind11::enum_ precision(m, "AccumulatePrecision"); - precision.value("Double", AccumulatePass::Precision::Double); - precision.value("Single", AccumulatePass::Precision::Single); - precision.value("SingleCompensated", AccumulatePass::Precision::SingleCompensated); - - pybind11::enum_ overflowMode(m, "AccumulateOverflowMode"); - overflowMode.value("Stop", AccumulatePass::OverflowMode::Stop); - overflowMode.value("Reset", AccumulatePass::OverflowMode::Reset); - overflowMode.value("EMA", AccumulatePass::OverflowMode::EMA); } extern "C" FALCOR_API_EXPORT void registerPlugin(Falcor::PluginRegistry& registry) @@ -67,27 +57,13 @@ namespace const char kPrecisionMode[] = "precisionMode"; const char kMaxFrameCount[] = "maxFrameCount"; const char kOverflowMode[] = "overflowMode"; - - const Gui::DropdownList kModeSelectorList = - { - { (uint32_t)AccumulatePass::Precision::Double, "Double precision" }, - { (uint32_t)AccumulatePass::Precision::Single, "Single precision" }, - { (uint32_t)AccumulatePass::Precision::SingleCompensated, "Single precision (compensated)" }, - }; - - const Gui::DropdownList kOverflowModeList = - { - { (uint32_t)AccumulatePass::OverflowMode::Stop, "Stop" }, - { (uint32_t)AccumulatePass::OverflowMode::Reset, "Reset" }, - { (uint32_t)AccumulatePass::OverflowMode::EMA, "EMA" }, - }; } -AccumulatePass::AccumulatePass(ref pDevice, const Dictionary& dict) +AccumulatePass::AccumulatePass(ref pDevice, const Properties& props) : RenderPass(pDevice) { // Deserialize pass from dictionary. - for (const auto& [key, value] : dict) + for (const auto& [key, value] : props) { if (key == kEnabled) mEnabled = value; else if (key == kOutputFormat) mOutputFormat = value; @@ -97,30 +73,30 @@ AccumulatePass::AccumulatePass(ref pDevice, const Dictionary& dict) else if (key == kPrecisionMode) mPrecisionMode = value; else if (key == kMaxFrameCount) mMaxFrameCount = value; else if (key == kOverflowMode) mOverflowMode = value; - else logWarning("Unknown field '{}' in AccumulatePass dictionary.", key); + else logWarning("Unknown property '{}' in AccumulatePass properties.", key); } - if (dict.keyExists("enableAccumulation")) + if (props.has("enableAccumulation")) { logWarning("'enableAccumulation' is deprecated. Use 'enabled' instead."); - if (!dict.keyExists(kEnabled)) mEnabled = dict["enableAccumulation"]; + if (!props.has(kEnabled)) mEnabled = props["enableAccumulation"]; } mpState = ComputeState::create(mpDevice); } -Dictionary AccumulatePass::getScriptingDictionary() +Properties AccumulatePass::getProperties() const { - Dictionary dict; - dict[kEnabled] = mEnabled; - if (mOutputFormat != ResourceFormat::Unknown) dict[kOutputFormat] = mOutputFormat; - dict[kOutputSize] = mOutputSizeSelection; - if (mOutputSizeSelection == RenderPassHelpers::IOSize::Fixed) dict[kFixedOutputSize] = mFixedOutputSize; - dict[kAutoReset] = mAutoReset; - dict[kPrecisionMode] = mPrecisionMode; - dict[kMaxFrameCount] = mMaxFrameCount; - dict[kOverflowMode] = mOverflowMode; - return dict; + Properties props; + props[kEnabled] = mEnabled; + if (mOutputFormat != ResourceFormat::Unknown) props[kOutputFormat] = mOutputFormat; + props[kOutputSize] = mOutputSizeSelection; + if (mOutputSizeSelection == RenderPassHelpers::IOSize::Fixed) props[kFixedOutputSize] = mFixedOutputSize; + props[kAutoReset] = mAutoReset; + props[kPrecisionMode] = mPrecisionMode; + props[kMaxFrameCount] = mMaxFrameCount; + props[kOverflowMode] = mOverflowMode; + return props; } RenderPassReflection AccumulatePass::reflect(const CompileData& compileData) @@ -238,7 +214,7 @@ void AccumulatePass::accumulate(RenderContext* pRenderContext, const refgetReflector()); mSrcType = srcType; @@ -298,7 +274,7 @@ void AccumulatePass::renderUI(Gui::Widgets& widget) { // Controls for output size. // When output size requirements change, we'll trigger a graph recompile to update the render pass I/O sizes. - if (widget.dropdown("Output size", RenderPassHelpers::kIOSizeList, (uint32_t&)mOutputSizeSelection)) requestRecompile(); + if (widget.dropdown("Output size", mOutputSizeSelection)) requestRecompile(); if (mOutputSizeSelection == RenderPassHelpers::IOSize::Fixed) { if (widget.var("Size in pixels", mFixedOutputSize, 32u, 16384u)) requestRecompile(); @@ -313,7 +289,7 @@ void AccumulatePass::renderUI(Gui::Widgets& widget) widget.checkbox("Auto Reset", mAutoReset); widget.tooltip("Reset accumulation automatically upon scene changes and refresh flags."); - if (widget.dropdown("Mode", kModeSelectorList, (uint32_t&)mPrecisionMode)) + if (widget.dropdown("Mode", mPrecisionMode)) { // Reset accumulation when mode changes. reset(); @@ -330,7 +306,7 @@ void AccumulatePass::renderUI(Gui::Widgets& widget) } widget.tooltip("Maximum number of frames to accumulate before triggering overflow. 0 means infinite accumulation."); - if (widget.dropdown("Overflow Mode", kOverflowModeList, reinterpret_cast(mOverflowMode))) + if (widget.dropdown("Overflow Mode", mOverflowMode)) { reset(); } diff --git a/Source/RenderPasses/AccumulatePass/AccumulatePass.h b/Source/RenderPasses/AccumulatePass/AccumulatePass.h index c74b7d1f2..0d348b348 100644 --- a/Source/RenderPasses/AccumulatePass/AccumulatePass.h +++ b/Source/RenderPasses/AccumulatePass/AccumulatePass.h @@ -46,12 +46,12 @@ class AccumulatePass : public RenderPass public: FALCOR_PLUGIN_CLASS(AccumulatePass, "AccumulatePass", "Temporal accumulation."); - static ref create(ref pDevice, const Dictionary& dict) { return make_ref(pDevice, dict); } + static ref create(ref pDevice, const Properties& props) { return make_ref(pDevice, props); } - AccumulatePass(ref pDevice, const Dictionary& dict); + AccumulatePass(ref pDevice, const Properties& props); virtual ~AccumulatePass() = default; - virtual Dictionary getScriptingDictionary() override; + virtual Properties getProperties() const override; virtual RenderPassReflection reflect(const CompileData& compileData) override; virtual void execute(RenderContext* pRenderContext, const RenderData& renderData) override; virtual void renderUI(Gui::Widgets& widget) override; @@ -73,6 +73,12 @@ class AccumulatePass : public RenderPass SingleCompensated, ///< Compensated summation (Kahan summation) in single precision. }; + FALCOR_ENUM_INFO(Precision, { + { Precision::Double, "Double" }, + { Precision::Single, "Single" }, + { Precision::SingleCompensated, "SingleCompensated" }, + }); + enum class OverflowMode : uint32_t { Stop, ///< Stop accumulation and retain accumulated image. @@ -80,6 +86,12 @@ class AccumulatePass : public RenderPass EMA, ///< Switch to exponential moving average accumulation. }; + FALCOR_ENUM_INFO(OverflowMode, { + { OverflowMode::Stop, "Stop" }, + { OverflowMode::Reset, "Reset" }, + { OverflowMode::EMA, "EMA" }, + }); + protected: void prepareAccumulation(RenderContext* pRenderContext, uint32_t width, uint32_t height); void accumulate(RenderContext* pRenderContext, const ref& pSrc, const ref& pDst); @@ -109,3 +121,6 @@ class AccumulatePass : public RenderPass RenderPassHelpers::IOSize mOutputSizeSelection = RenderPassHelpers::IOSize::Default; ///< Selected output size. uint2 mFixedOutputSize = { 512, 512 }; ///< Output size in pixels when 'Fixed' size is selected. }; + +FALCOR_ENUM_REGISTER(AccumulatePass::Precision); +FALCOR_ENUM_REGISTER(AccumulatePass::OverflowMode); diff --git a/Source/RenderPasses/BSDFViewer/BSDFViewer.cpp b/Source/RenderPasses/BSDFViewer/BSDFViewer.cpp index 71fe2c61f..2d669c9d2 100644 --- a/Source/RenderPasses/BSDFViewer/BSDFViewer.cpp +++ b/Source/RenderPasses/BSDFViewer/BSDFViewer.cpp @@ -32,7 +32,6 @@ extern "C" FALCOR_API_EXPORT void registerPlugin(Falcor::PluginRegistry& registry) { registry.registerClass(); - ScriptBindings::registerBinding(BSDFViewer::registerBindings); } namespace @@ -47,26 +46,12 @@ namespace const char kUseEnvMap[] = "useEnvMap"; const char kTexCoords[] = "texCoords"; const char kOutputAlbedo[] = "outputAlbedo"; - - // UI elements. - Gui::DropdownList kViewerModeList = - { - { (uint32_t)BSDFViewerMode::Material, "Material" }, - { (uint32_t)BSDFViewerMode::Slice, "Slice" }, - }; -} - -void BSDFViewer::registerBindings(pybind11::module& m) -{ - pybind11::enum_ mode(m, "BSDFViewerMode"); - mode.value("Material", BSDFViewerMode::Material); - mode.value("Slice", BSDFViewerMode::Slice); } -BSDFViewer::BSDFViewer(ref pDevice, const Dictionary& dict) +BSDFViewer::BSDFViewer(ref pDevice, const Properties& props) : RenderPass(pDevice) { - parseDictionary(dict); + parseProperties(props); // Create a high-quality pseudorandom number generator. mpSampleGenerator = SampleGenerator::create(mpDevice, SAMPLE_GENERATOR_UNIFORM); @@ -75,9 +60,9 @@ BSDFViewer::BSDFViewer(ref pDevice, const Dictionary& dict) mpFence = GpuFence::create(mpDevice); } -void BSDFViewer::parseDictionary(const Dictionary& dict) +void BSDFViewer::parseProperties(const Properties& props) { - for (const auto& [key, value] : dict) + for (const auto& [key, value] : props) { if (key == kMaterialID) mParams.materialID = value; else if (key == kViewerMode) mParams.viewerMode = value; @@ -88,19 +73,19 @@ void BSDFViewer::parseDictionary(const Dictionary& dict) mParams.texCoords = value; } else if (key == kOutputAlbedo) mParams.outputAlbedo = value; - else logWarning("Unknown field '{}' in BSDFViewer dictionary.", key); + else logWarning("Unknown property '{}' in BSDFViewer properties.", key); } } -Dictionary BSDFViewer::getScriptingDictionary() +Properties BSDFViewer::getProperties() const { - Dictionary d; - d[kMaterialID] = mParams.materialID; - d[kViewerMode] = mParams.viewerMode; - d[kUseEnvMap] = mUseEnvMap; - if (mParams.useFixedTexCoords) d[kTexCoords] = mParams.texCoords; - if (mParams.outputAlbedo != 0) d[kOutputAlbedo] = mParams.outputAlbedo; - return d; + Properties props; + props[kMaterialID] = mParams.materialID; + props[kViewerMode] = mParams.viewerMode; + props[kUseEnvMap] = mUseEnvMap; + if (mParams.useFixedTexCoords) props[kTexCoords] = mParams.texCoords; + if (mParams.outputAlbedo != 0) props[kOutputAlbedo] = mParams.outputAlbedo; + return props; } RenderPassReflection BSDFViewer::reflect(const CompileData& compileData) @@ -139,7 +124,7 @@ void BSDFViewer::setScene(RenderContext* pRenderContext, const ref& pScen desc.addShaderLibrary(kFileViewerPass).csEntry("main"); desc.addTypeConformances(mpScene->getTypeConformances()); - Program::DefineList defines; + DefineList defines; defines.add(mpSampleGenerator->getDefines()); defines.add(mpScene->getSceneDefines()); @@ -264,7 +249,7 @@ void BSDFViewer::renderUI(Gui::Widgets& widget) bool dirty = false; - dirty |= widget.dropdown("Mode", kViewerModeList, (uint32_t&)mParams.viewerMode); + dirty |= widget.dropdown("Mode", mParams.viewerMode); switch (mParams.viewerMode) { diff --git a/Source/RenderPasses/BSDFViewer/BSDFViewer.cs.slang b/Source/RenderPasses/BSDFViewer/BSDFViewer.cs.slang index 2f3abe0b6..f49a6d3c6 100644 --- a/Source/RenderPasses/BSDFViewer/BSDFViewer.cs.slang +++ b/Source/RenderPasses/BSDFViewer/BSDFViewer.cs.slang @@ -111,6 +111,9 @@ struct BSDFViewer */ bool calculateSphereGeometry(float2 uv, out VertexData v, out float3 rayDir) { + v = {}; + rayDir = {}; + const float2 ndc = float2(2.f * uv.x - 1.f, -2.f * uv.y + 1.f); if (params.orthographicCamera) diff --git a/Source/RenderPasses/BSDFViewer/BSDFViewer.h b/Source/RenderPasses/BSDFViewer/BSDFViewer.h index 8dd72502a..caaf33c14 100644 --- a/Source/RenderPasses/BSDFViewer/BSDFViewer.h +++ b/Source/RenderPasses/BSDFViewer/BSDFViewer.h @@ -40,11 +40,11 @@ class BSDFViewer : public RenderPass public: FALCOR_PLUGIN_CLASS(BSDFViewer, "BSDFViewer", "BSDF inspection utility."); - static ref create(ref pDevice, const Dictionary& dict) { return make_ref(pDevice, dict); } + static ref create(ref pDevice, const Properties& props) { return make_ref(pDevice, props); } - BSDFViewer(ref pDevice, const Dictionary& dict); + BSDFViewer(ref pDevice, const Properties& props); - virtual Dictionary getScriptingDictionary() override; + virtual Properties getProperties() const 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; @@ -53,10 +53,8 @@ class BSDFViewer : public RenderPass virtual bool onMouseEvent(const MouseEvent& mouseEvent) override; virtual bool onKeyEvent(const KeyboardEvent& keyEvent) override; - static void registerBindings(pybind11::module& m); - private: - void parseDictionary(const Dictionary& dict); + void parseProperties(const Properties& props); bool loadEnvMap(const std::filesystem::path& path); void readPixelData(); diff --git a/Source/RenderPasses/BSDFViewer/BSDFViewerParams.slang b/Source/RenderPasses/BSDFViewer/BSDFViewerParams.slang index de7cfa758..50f344c98 100644 --- a/Source/RenderPasses/BSDFViewer/BSDFViewerParams.slang +++ b/Source/RenderPasses/BSDFViewer/BSDFViewerParams.slang @@ -36,6 +36,12 @@ enum class BSDFViewerMode : uint32_t Slice, ///< BSDF slice viewer. }; +FALCOR_ENUM_INFO(BSDFViewerMode, { + { BSDFViewerMode::Material, "Material" }, + { BSDFViewerMode::Slice, "Slice" }, +}); +FALCOR_ENUM_REGISTER(BSDFViewerMode); + enum class AlbedoSelection : uint32_t { ShowAlbedo = 0x1, diff --git a/Source/RenderPasses/BlitPass/BlitPass.cpp b/Source/RenderPasses/BlitPass/BlitPass.cpp index 160d086ea..482f6b050 100644 --- a/Source/RenderPasses/BlitPass/BlitPass.cpp +++ b/Source/RenderPasses/BlitPass/BlitPass.cpp @@ -37,7 +37,10 @@ namespace void regBlitPass(pybind11::module& m) { pybind11::class_> pass(m, "BlitPass"); - pass.def_property(kFilter, &BlitPass::getFilter, &BlitPass::setFilter); + pass.def_property("filter", + [](const BlitPass& self) { return enumToString(self.getFilter()); }, + [](BlitPass& self, const std::string& value) {self.setFilter(stringToEnum(value)); } + ); } } @@ -47,10 +50,10 @@ extern "C" FALCOR_API_EXPORT void registerPlugin(Falcor::PluginRegistry& registr ScriptBindings::registerBinding(regBlitPass); } -BlitPass::BlitPass(ref pDevice, const Dictionary& dict) +BlitPass::BlitPass(ref pDevice, const Properties& props) : RenderPass(pDevice) { - parseDictionary(dict); + parseProperties(props); } RenderPassReflection BlitPass::reflect(const CompileData& compileData) @@ -61,22 +64,22 @@ RenderPassReflection BlitPass::reflect(const CompileData& compileData) return r; } -void BlitPass::parseDictionary(const Dictionary& dict) +void BlitPass::parseProperties(const Properties& props) { - for (const auto& [key, value] : dict) + for (const auto& [key, value] : props) { if (key == kFilter) setFilter(value); if (key == kOutputFormat) mOutputFormat = value; - else logWarning("Unknown field '{}' in a BlitPass dictionary.", key); + else logWarning("Unknown property '{}' in a BlitPass properties.", key); } } -Dictionary BlitPass::getScriptingDictionary() +Properties BlitPass::getProperties() const { - Dictionary d; - d[kFilter] = mFilter; - if (mOutputFormat != ResourceFormat::Unknown) d[kOutputFormat] = mOutputFormat; - return d; + Properties props; + props[kFilter] = mFilter; + if (mOutputFormat != ResourceFormat::Unknown) props[kOutputFormat] = mOutputFormat; + return props; } void BlitPass::execute(RenderContext* pRenderContext, const RenderData& renderData) @@ -96,12 +99,5 @@ void BlitPass::execute(RenderContext* pRenderContext, const RenderData& renderDa void BlitPass::renderUI(Gui::Widgets& widget) { - static const Gui::DropdownList kFilterList = - { - { (uint32_t)Sampler::Filter::Linear, "Linear" }, - { (uint32_t)Sampler::Filter::Point, "Point" }, - }; - uint32_t f = (uint32_t)mFilter; - - if (widget.dropdown("Filter", kFilterList, f)) setFilter((Sampler::Filter)f); + if (auto filter = mFilter; widget.dropdown("Filter", filter)) setFilter(filter); } diff --git a/Source/RenderPasses/BlitPass/BlitPass.h b/Source/RenderPasses/BlitPass/BlitPass.h index cae4e6a38..827660252 100644 --- a/Source/RenderPasses/BlitPass/BlitPass.h +++ b/Source/RenderPasses/BlitPass/BlitPass.h @@ -40,11 +40,11 @@ class BlitPass : public RenderPass public: FALCOR_PLUGIN_CLASS(BlitPass, "BlitPass", "Blit a texture into a different texture."); - static ref create(ref pDevice, const Dictionary& dict) { return make_ref(pDevice, dict); } + static ref create(ref pDevice, const Properties& props) { return make_ref(pDevice, props); } - BlitPass(ref pDevice, const Dictionary& dict); + BlitPass(ref pDevice, const Properties& props); - virtual Dictionary getScriptingDictionary() override; + virtual Properties getProperties() const override; virtual RenderPassReflection reflect(const CompileData& compileData) override; virtual void execute(RenderContext* pRenderContext, const RenderData& renderData) override; virtual void renderUI(Gui::Widgets& widget) override; @@ -54,7 +54,7 @@ class BlitPass : public RenderPass void setFilter(Sampler::Filter filter) { mFilter = filter; } private: - void parseDictionary(const Dictionary& dict); + void parseProperties(const Properties& props); Sampler::Filter mFilter = Sampler::Filter::Linear; ResourceFormat mOutputFormat = ResourceFormat::Unknown; diff --git a/Source/RenderPasses/DLSSPass/DLSSPass.cpp b/Source/RenderPasses/DLSSPass/DLSSPass.cpp index b79718f75..d5489f89d 100644 --- a/Source/RenderPasses/DLSSPass/DLSSPass.cpp +++ b/Source/RenderPasses/DLSSPass/DLSSPass.cpp @@ -29,17 +29,6 @@ namespace { -const Gui::DropdownList kProfileChoices = { - {(uint32_t)DLSSPass::Profile::MaxPerf, "Max Performance"}, - {(uint32_t)DLSSPass::Profile::Balanced, "Balanced"}, - {(uint32_t)DLSSPass::Profile::MaxQuality, "Max Quality"}, -}; - -const Gui::DropdownList kMotionVectorScaleChoices = { - {(uint32_t)DLSSPass::MotionVectorScale::Absolute, "Absolute"}, - {(uint32_t)DLSSPass::MotionVectorScale::Relative, "Relative"}, -}; - const char kColorInput[] = "color"; const char kDepthInput[] = "depth"; const char kMotionVectorsInput[] = "mvec"; @@ -58,15 +47,6 @@ const char kExposure[] = "exposure"; static void registerDLSSPass(pybind11::module& m) { pybind11::class_> pass(m, "DLSSPass"); - - pybind11::enum_ profile(m, "DLSSProfile"); - profile.value("MaxPerf", DLSSPass::Profile::MaxPerf); - profile.value("Balanced", DLSSPass::Profile::Balanced); - profile.value("MaxQuality", DLSSPass::Profile::MaxQuality); - - pybind11::enum_ motionVectorScale(m, "DLSSMotionVectorScale"); - motionVectorScale.value("Absolute", DLSSPass::MotionVectorScale::Absolute); - motionVectorScale.value("Relative", DLSSPass::MotionVectorScale::Relative); } extern "C" FALCOR_API_EXPORT void registerPlugin(Falcor::PluginRegistry& registry) @@ -75,9 +55,9 @@ extern "C" FALCOR_API_EXPORT void registerPlugin(Falcor::PluginRegistry& registr ScriptBindings::registerBinding(registerDLSSPass); } -DLSSPass::DLSSPass(ref pDevice, const Dictionary& dict) : RenderPass(pDevice) +DLSSPass::DLSSPass(ref pDevice, const Properties& props) : RenderPass(pDevice) { - for (const auto& [key, value] : dict) + for (const auto& [key, value] : props) { if (key == kEnabled) mEnabled = value; @@ -97,23 +77,23 @@ DLSSPass::DLSSPass(ref pDevice, const Dictionary& dict) : RenderPass(pDe mExposureUpdated = true; } else - logWarning("Unknown field '{}' in a DLSSPass dictionary.", key); + logWarning("Unknown property '{}' in a DLSSPass properties.", key); } mpExposure = Texture::create2D(mpDevice, 1, 1, ResourceFormat::R32Float, 1, 1); } -Dictionary DLSSPass::getScriptingDictionary() +Properties DLSSPass::getProperties() const { - Dictionary d; - d[kEnabled] = mEnabled; - d[kOutputSize] = mOutputSizeSelection; - d[kProfile] = mProfile; - d[kMotionVectorScale] = mMotionVectorScale; - d[kIsHDR] = mIsHDR; - d[kSharpness] = mSharpness; - d[kExposure] = mExposure; - return d; + Properties props; + props[kEnabled] = mEnabled; + props[kOutputSize] = mOutputSizeSelection; + props[kProfile] = mProfile; + props[kMotionVectorScale] = mMotionVectorScale; + props[kIsHDR] = mIsHDR; + props[kSharpness] = mSharpness; + props[kExposure] = mExposure; + return props; } RenderPassReflection DLSSPass::reflect(const CompileData& compileData) @@ -150,7 +130,7 @@ void DLSSPass::renderUI(Gui::Widgets& widget) // Controls for output size. // When output size requirements change, we'll trigger a graph recompile to update the render pass I/O sizes. - if (widget.dropdown("Output size", RenderPassHelpers::kIOSizeList, (uint32_t&)mOutputSizeSelection)) + if (widget.dropdown("Output size", mOutputSizeSelection)) requestRecompile(); widget.tooltip( "Specifies the pass output size.\n" @@ -162,9 +142,9 @@ void DLSSPass::renderUI(Gui::Widgets& widget) if (mEnabled) { - mRecreate |= widget.dropdown("Profile", kProfileChoices, reinterpret_cast(mProfile)); + mRecreate |= widget.dropdown("Profile", mProfile); - widget.dropdown("Motion vector scale", kMotionVectorScaleChoices, reinterpret_cast(mMotionVectorScale)); + widget.dropdown("Motion vector scale", mMotionVectorScale); widget.tooltip( "Absolute: Motion vectors are provided in absolute screen space length (pixels)\n" "Relative: Motion vectors are provided in relative screen space length (pixels divided by screen width/height)." diff --git a/Source/RenderPasses/DLSSPass/DLSSPass.h b/Source/RenderPasses/DLSSPass/DLSSPass.h index d295ad529..0ca306414 100644 --- a/Source/RenderPasses/DLSSPass/DLSSPass.h +++ b/Source/RenderPasses/DLSSPass/DLSSPass.h @@ -45,17 +45,34 @@ class DLSSPass : public RenderPass MaxQuality, }; + FALCOR_ENUM_INFO( + Profile, + { + {Profile::MaxPerf, "MaxPerf"}, + {Profile::Balanced, "Balanced"}, + {Profile::MaxQuality, "MaxQuality"}, + } + ); + enum class MotionVectorScale : uint32_t { Absolute, ///< Motion vectors are provided in absolute screen space length (pixels). Relative, ///< Motion vectors are provided in relative screen space length (pixels divided by screen width/height). }; - static ref create(ref pDevice, const Dictionary& dict) { return make_ref(pDevice, dict); } + FALCOR_ENUM_INFO( + MotionVectorScale, + { + {MotionVectorScale::Absolute, "Absolute"}, + {MotionVectorScale::Relative, "Relative"}, + } + ); + + static ref create(ref pDevice, const Properties& props) { return make_ref(pDevice, props); } - DLSSPass(ref pDevice, const Dictionary& dict); + DLSSPass(ref pDevice, const Properties& props); - virtual Dictionary getScriptingDictionary() override; + virtual Properties getProperties() const override; virtual RenderPassReflection reflect(const CompileData& compileData) override; virtual void setScene(RenderContext* pRenderContext, const ref& pScene) override; virtual void execute(RenderContext* pRenderContext, const RenderData& renderData) override; @@ -86,3 +103,6 @@ class DLSSPass : public RenderPass std::unique_ptr mpNGXWrapper; }; + +FALCOR_ENUM_REGISTER(DLSSPass::Profile); +FALCOR_ENUM_REGISTER(DLSSPass::MotionVectorScale); diff --git a/Source/RenderPasses/DebugPasses/ColorMapPass/ColorMapParams.slang b/Source/RenderPasses/DebugPasses/ColorMapPass/ColorMapParams.slang index f23d7d6d1..4d113048a 100644 --- a/Source/RenderPasses/DebugPasses/ColorMapPass/ColorMapParams.slang +++ b/Source/RenderPasses/DebugPasses/ColorMapPass/ColorMapParams.slang @@ -39,6 +39,16 @@ enum class ColorMap : uint32_t Inferno, }; +FALCOR_ENUM_INFO(ColorMap, { + { ColorMap::Grey, "Grey" }, + { ColorMap::Jet, "Jet" }, + { ColorMap::Viridis, "Viridis" }, + { ColorMap::Plasma, "Plasma" }, + { ColorMap::Magma, "Magma" }, + { ColorMap::Inferno, "Inferno" }, +}); +FALCOR_ENUM_REGISTER(ColorMap); + struct ColorMapParams { float minValue = 0.f; diff --git a/Source/RenderPasses/DebugPasses/ColorMapPass/ColorMapPass.cpp b/Source/RenderPasses/DebugPasses/ColorMapPass/ColorMapPass.cpp index c091b721b..c291b8964 100644 --- a/Source/RenderPasses/DebugPasses/ColorMapPass/ColorMapPass.cpp +++ b/Source/RenderPasses/DebugPasses/ColorMapPass/ColorMapPass.cpp @@ -31,16 +31,6 @@ namespace { const std::string kShaderFile = "RenderPasses/DebugPasses/ColorMapPass/ColorMapPass.ps.slang"; - const Gui::DropdownList kColorMapList = - { - { (uint32_t)ColorMap::Grey, "Grey" }, - { (uint32_t)ColorMap::Jet, "Jet" }, - { (uint32_t)ColorMap::Viridis, "Viridis" }, - { (uint32_t)ColorMap::Plasma, "Plasma" }, - { (uint32_t)ColorMap::Magma, "Magma" }, - { (uint32_t)ColorMap::Inferno, "Inferno" }, - }; - const std::string kInput = "input"; const std::string kOutput = "output"; @@ -51,42 +41,31 @@ namespace const std::string kMaxValue = "maxValue"; } -void ColorMapPass::registerScriptBindings(pybind11::module& m) -{ - pybind11::enum_ colorMap(m, "ColorMap"); - colorMap.value("Grey", ColorMap::Grey); - colorMap.value("Jet", ColorMap::Jet); - colorMap.value("Viridis", ColorMap::Viridis); - colorMap.value("Plasma", ColorMap::Plasma); - colorMap.value("Magma", ColorMap::Magma); - colorMap.value("Inferno", ColorMap::Inferno); -} - -ColorMapPass::ColorMapPass(ref pDevice, const Dictionary& dict) +ColorMapPass::ColorMapPass(ref pDevice, const Properties& props) : RenderPass(pDevice) { - for (const auto& [key, value] : dict) + for (const auto& [key, value] : props) { if (key == kColorMap) mColorMap = value; else if (key == kChannel) mChannel = value; else if (key == kAutoRange) mAutoRange = value; else if (key == kMinValue) mMinValue = value; else if (key == kMaxValue) mMaxValue = value; - else logWarning("Unknown field '{}' in a ColorMapPass dictionary.", key); + else logWarning("Unknown property '{}' in a ColorMapPass properties.", key); } mpFbo = Fbo::create(mpDevice); } -Dictionary ColorMapPass::getScriptingDictionary() +Properties ColorMapPass::getProperties() const { - Dictionary d; - d[kColorMap] = mColorMap; - d[kChannel] = mChannel; - d[kAutoRange] = mAutoRange; - d[kMinValue] = mMinValue; - d[kMaxValue] = mMaxValue; - return d; + Properties props; + props[kColorMap] = mColorMap; + props[kChannel] = mChannel; + props[kAutoRange] = mAutoRange; + props[kMinValue] = mMinValue; + props[kMaxValue] = mMaxValue; + return props; } RenderPassReflection ColorMapPass::reflect(const CompileData& compileData) @@ -135,7 +114,7 @@ void ColorMapPass::execute(RenderContext* pRenderContext, const RenderData& rend mpAutoRanging.reset(); } - Program::DefineList defines; + DefineList defines; defines.add("_COLOR_MAP", std::to_string((uint32_t)mColorMap)); defines.add("_CHANNEL", std::to_string(mChannel)); @@ -172,7 +151,7 @@ void ColorMapPass::execute(RenderContext* pRenderContext, const RenderData& rend void ColorMapPass::renderUI(Gui::Widgets& widget) { - mRecompile |= widget.dropdown("Color Map", kColorMapList, *reinterpret_cast(&mColorMap)); + mRecompile |= widget.dropdown("Color Map", mColorMap); mRecompile |= widget.var("Channel", mChannel, 0u, 3u); widget.checkbox("Auto Range", mAutoRange); widget.var("Min Value", mMinValue); diff --git a/Source/RenderPasses/DebugPasses/ColorMapPass/ColorMapPass.h b/Source/RenderPasses/DebugPasses/ColorMapPass/ColorMapPass.h index f989ff8c1..2dfe969fc 100644 --- a/Source/RenderPasses/DebugPasses/ColorMapPass/ColorMapPass.h +++ b/Source/RenderPasses/DebugPasses/ColorMapPass/ColorMapPass.h @@ -39,17 +39,15 @@ class ColorMapPass : public RenderPass public: FALCOR_PLUGIN_CLASS(ColorMapPass, "ColorMapPass", "Pass that applies a color map to the input."); - static ref create(ref pDevice, const Dictionary& dict) { return make_ref(pDevice, dict); } + static ref create(ref pDevice, const Properties& props) { return make_ref(pDevice, props); } - ColorMapPass(ref pDevice, const Dictionary& dict); + ColorMapPass(ref pDevice, const Properties& props); - virtual Dictionary getScriptingDictionary() override; + virtual Properties getProperties() const override; virtual RenderPassReflection reflect(const CompileData& compileData) override; virtual void execute(RenderContext* pRenderContext, const RenderData& renderData) override; virtual void renderUI(Gui::Widgets& widget) override; - static void registerScriptBindings(pybind11::module& m); - private: ColorMap mColorMap = ColorMap::Jet; uint32_t mChannel = 0; diff --git a/Source/RenderPasses/DebugPasses/ComparisonPass.cpp b/Source/RenderPasses/DebugPasses/ComparisonPass.cpp index 638b69ddc..b69f48424 100644 --- a/Source/RenderPasses/DebugPasses/ComparisonPass.cpp +++ b/Source/RenderPasses/DebugPasses/ComparisonPass.cpp @@ -45,7 +45,7 @@ ComparisonPass::ComparisonPass(ref pDevice) mpTextRenderer = std::make_unique(mpDevice); } -bool ComparisonPass::parseKeyValuePair(const std::string key, const Dictionary::Value val) +bool ComparisonPass::parseKeyValuePair(const std::string key, const Properties::ConstValue& val) { if (key == kSplitLocation) { @@ -72,14 +72,14 @@ bool ComparisonPass::parseKeyValuePair(const std::string key, const Dictionary:: else return false; } -Dictionary ComparisonPass::getScriptingDictionary() +Properties ComparisonPass::getProperties() const { - Dictionary dict; - dict[kSplitLocation] = mSplitLoc; - dict[kShowTextLabels] = mShowLabels; - dict[kLeftLabel] = mLeftLabel; - dict[kRightLabel] = mRightLabel; - return dict; + Properties props; + props[kSplitLocation] = mSplitLoc; + props[kShowTextLabels] = mShowLabels; + props[kLeftLabel] = mLeftLabel; + props[kRightLabel] = mRightLabel; + return props; } RenderPassReflection ComparisonPass::reflect(const CompileData& compileData) diff --git a/Source/RenderPasses/DebugPasses/ComparisonPass.h b/Source/RenderPasses/DebugPasses/ComparisonPass.h index 389784f4b..5f5beefb9 100644 --- a/Source/RenderPasses/DebugPasses/ComparisonPass.h +++ b/Source/RenderPasses/DebugPasses/ComparisonPass.h @@ -36,7 +36,7 @@ using namespace Falcor; class ComparisonPass : public RenderPass { public: - virtual Dictionary getScriptingDictionary() override; + virtual Properties getProperties() const override; virtual RenderPassReflection reflect(const CompileData& compileData) override; virtual void execute(RenderContext* pRenderContext, const RenderData& renderData) override; virtual void renderUI(Gui::Widgets& widget) override; @@ -44,7 +44,7 @@ class ComparisonPass : public RenderPass protected: ComparisonPass(ref pDevice); virtual void createProgram() = 0; - bool parseKeyValuePair(const std::string key, const Dictionary::Value val); + bool parseKeyValuePair(const std::string key, const Properties::ConstValue& val); ref mpSplitShader; ref pLeftSrcTex; diff --git a/Source/RenderPasses/DebugPasses/DebugPasses.cpp b/Source/RenderPasses/DebugPasses/DebugPasses.cpp index d182b111b..ece1ba0af 100644 --- a/Source/RenderPasses/DebugPasses/DebugPasses.cpp +++ b/Source/RenderPasses/DebugPasses/DebugPasses.cpp @@ -36,6 +36,4 @@ extern "C" FALCOR_API_EXPORT void registerPlugin(Falcor::PluginRegistry& registr registry.registerClass(); registry.registerClass(); registry.registerClass(); - - ScriptBindings::registerBinding(ColorMapPass::registerScriptBindings); } diff --git a/Source/RenderPasses/DebugPasses/InvalidPixelDetectionPass/InvalidPixelDetectionPass.cpp b/Source/RenderPasses/DebugPasses/InvalidPixelDetectionPass/InvalidPixelDetectionPass.cpp index e44260482..a44411afc 100644 --- a/Source/RenderPasses/DebugPasses/InvalidPixelDetectionPass/InvalidPixelDetectionPass.cpp +++ b/Source/RenderPasses/DebugPasses/InvalidPixelDetectionPass/InvalidPixelDetectionPass.cpp @@ -34,7 +34,7 @@ namespace const std::string kFormatWarning = "Non-float format can't represent Inf/NaN values. Expect black output."; } -InvalidPixelDetectionPass::InvalidPixelDetectionPass(ref pDevice, const Dictionary& dict) +InvalidPixelDetectionPass::InvalidPixelDetectionPass(ref pDevice, const Properties& props) : RenderPass(pDevice) { mpInvalidPixelDetectPass = FullScreenPass::create(mpDevice, "RenderPasses/DebugPasses/InvalidPixelDetectionPass/InvalidPixelDetection.ps.slang"); diff --git a/Source/RenderPasses/DebugPasses/InvalidPixelDetectionPass/InvalidPixelDetectionPass.h b/Source/RenderPasses/DebugPasses/InvalidPixelDetectionPass/InvalidPixelDetectionPass.h index 735fa87b4..b2136da14 100644 --- a/Source/RenderPasses/DebugPasses/InvalidPixelDetectionPass/InvalidPixelDetectionPass.h +++ b/Source/RenderPasses/DebugPasses/InvalidPixelDetectionPass/InvalidPixelDetectionPass.h @@ -37,9 +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."); - static ref create(ref pDevice, const Dictionary& dict) { return make_ref(pDevice, dict); } + static ref create(ref pDevice, const Properties& props) { return make_ref(pDevice, props); } - InvalidPixelDetectionPass(ref pDevice, const Dictionary& dict); + InvalidPixelDetectionPass(ref pDevice, const Properties& props); virtual RenderPassReflection reflect(const CompileData& compileData) override; virtual void compile(RenderContext* pRenderContext, const CompileData& compileData) override; diff --git a/Source/RenderPasses/DebugPasses/SideBySidePass/SideBySidePass.cpp b/Source/RenderPasses/DebugPasses/SideBySidePass/SideBySidePass.cpp index a2c4db24f..e9c53d572 100644 --- a/Source/RenderPasses/DebugPasses/SideBySidePass/SideBySidePass.cpp +++ b/Source/RenderPasses/DebugPasses/SideBySidePass/SideBySidePass.cpp @@ -35,17 +35,17 @@ namespace const std::string kSplitShader = "RenderPasses/DebugPasses/SideBySidePass/SideBySide.ps.slang"; } -SideBySidePass::SideBySidePass(ref pDevice, const Dictionary& dict) +SideBySidePass::SideBySidePass(ref pDevice, const Properties& props) : ComparisonPass(pDevice) { createProgram(); - for (const auto& [key, value] : dict) + for (const auto& [key, value] : props) { if (key == kImageLeftBound) mImageLeftBound = value; else if (!parseKeyValuePair(key, value)) { - logWarning("Unknown field '{}' in a SideBySidePass dictionary.", key); + logWarning("Unknown property '{}' in a SideBySidePass properties.", key); } } } diff --git a/Source/RenderPasses/DebugPasses/SideBySidePass/SideBySidePass.h b/Source/RenderPasses/DebugPasses/SideBySidePass/SideBySidePass.h index 42ac6d400..06ebc131d 100644 --- a/Source/RenderPasses/DebugPasses/SideBySidePass/SideBySidePass.h +++ b/Source/RenderPasses/DebugPasses/SideBySidePass/SideBySidePass.h @@ -36,9 +36,9 @@ class SideBySidePass : public ComparisonPass public: FALCOR_PLUGIN_CLASS(SideBySidePass, "SideBySidePass", "Allows the user to compare two inputs side-by-side."); - static ref create(ref pDevice, const Dictionary& dict) { return make_ref(pDevice, dict); } + static ref create(ref pDevice, const Properties& props) { return make_ref(pDevice, props); } - SideBySidePass(ref pDevice, const Dictionary& dict); + SideBySidePass(ref pDevice, const Properties& props); virtual void execute(RenderContext* pRenderContext, const RenderData& renderData) override; virtual void renderUI(Gui::Widgets& widget) override; diff --git a/Source/RenderPasses/DebugPasses/SplitScreenPass/SplitScreenPass.cpp b/Source/RenderPasses/DebugPasses/SplitScreenPass/SplitScreenPass.cpp index d9e3dcb2b..a0ebc74cc 100644 --- a/Source/RenderPasses/DebugPasses/SplitScreenPass/SplitScreenPass.cpp +++ b/Source/RenderPasses/DebugPasses/SplitScreenPass/SplitScreenPass.cpp @@ -57,17 +57,17 @@ namespace const std::string kSplitShader = "RenderPasses/DebugPasses/SplitScreenPass/SplitScreen.ps.slang"; } -SplitScreenPass::SplitScreenPass(ref pDevice, const Dictionary& dict) +SplitScreenPass::SplitScreenPass(ref pDevice, const Properties& props) : ComparisonPass(pDevice) { mpArrowTex = Texture::create2D(mpDevice, 16, 16, ResourceFormat::R8Unorm, 1, Texture::kMaxPossible, kArrowArray); createProgram(); - for (const auto& [key, value] : dict) + for (const auto& [key, value] : props) { if (!parseKeyValuePair(key, value)) { - logWarning("Unknown field '{}' in a SplitScreenPass dictionary.", key); + logWarning("Unknown property '{}' in a SplitScreenPass properties.", key); } } } diff --git a/Source/RenderPasses/DebugPasses/SplitScreenPass/SplitScreenPass.h b/Source/RenderPasses/DebugPasses/SplitScreenPass/SplitScreenPass.h index cfe0ff6b9..a3333dade 100644 --- a/Source/RenderPasses/DebugPasses/SplitScreenPass/SplitScreenPass.h +++ b/Source/RenderPasses/DebugPasses/SplitScreenPass/SplitScreenPass.h @@ -37,9 +37,9 @@ class SplitScreenPass : public ComparisonPass public: FALCOR_PLUGIN_CLASS(SplitScreenPass, "SplitScreenPass", "Allows the user to split the screen between two inputs."); - static ref create(ref pDevice, const Dictionary& dict) { return make_ref(pDevice, dict); } + static ref create(ref pDevice, const Properties& props) { return make_ref(pDevice, props); } - SplitScreenPass(ref pDevice, const Dictionary& dict); + SplitScreenPass(ref pDevice, const Properties& props); virtual void execute(RenderContext* pRenderContext, const RenderData& renderData) override; virtual bool onMouseEvent(const MouseEvent& mouseEvent) override; diff --git a/Source/RenderPasses/ErrorMeasurePass/ErrorMeasurePass.cpp b/Source/RenderPasses/ErrorMeasurePass/ErrorMeasurePass.cpp index fa74bd325..06608834f 100644 --- a/Source/RenderPasses/ErrorMeasurePass/ErrorMeasurePass.cpp +++ b/Source/RenderPasses/ErrorMeasurePass/ErrorMeasurePass.cpp @@ -53,18 +53,9 @@ namespace const std::string kSelectedOutputId = "SelectedOutputId"; } -static void regErrorMeasurePass(pybind11::module& m) -{ - pybind11::enum_ op(m, "OutputId"); - op.value("Source", ErrorMeasurePass::OutputId::Source); - op.value("Reference", ErrorMeasurePass::OutputId::Reference); - op.value("Difference", ErrorMeasurePass::OutputId::Difference); -} - extern "C" FALCOR_API_EXPORT void registerPlugin(Falcor::PluginRegistry& registry) { registry.registerClass(); - ScriptBindings::registerBinding(regErrorMeasurePass); } const Gui::RadioButtonGroup ErrorMeasurePass::sOutputSelectionButtons = @@ -79,10 +70,10 @@ const Gui::RadioButtonGroup ErrorMeasurePass::sOutputSelectionButtonsSourceOnly { (uint32_t)OutputId::Source, "Source", true } }; -ErrorMeasurePass::ErrorMeasurePass(ref pDevice, const Dictionary& dict) +ErrorMeasurePass::ErrorMeasurePass(ref pDevice, const Properties& props) : RenderPass(pDevice) { - for (const auto& [key, value] : dict) + for (const auto& [key, value] : props) { if (key == kReferenceImagePath) mReferenceImagePath = value.operator std::filesystem::path(); else if (key == kMeasurementsFilePath) mMeasurementsFilePath = value.operator std::filesystem::path(); @@ -95,7 +86,7 @@ ErrorMeasurePass::ErrorMeasurePass(ref pDevice, const Dictionary& dict) else if (key == kSelectedOutputId) mSelectedOutputId = value; else { - logWarning("Unknown field '{}' in ErrorMeasurePass dictionary.", key); + logWarning("Unknown property '{}' in ErrorMeasurePass properties.", key); } } @@ -107,19 +98,19 @@ ErrorMeasurePass::ErrorMeasurePass(ref pDevice, const Dictionary& dict) mpErrorMeasurerPass = ComputePass::create(mpDevice, kErrorComputationShaderFile); } -Dictionary ErrorMeasurePass::getScriptingDictionary() +Properties ErrorMeasurePass::getProperties() const { - Dictionary dict; - dict[kReferenceImagePath] = mReferenceImagePath; - dict[kMeasurementsFilePath] = mMeasurementsFilePath; - dict[kIgnoreBackground] = mIgnoreBackground; - dict[kComputeSquaredDifference] = mComputeSquaredDifference; - dict[kComputeAverage] = mComputeAverage; - dict[kUseLoadedReference] = mUseLoadedReference; - dict[kReportRunningError] = mReportRunningError; - dict[kRunningErrorSigma] = mRunningErrorSigma; - dict[kSelectedOutputId] = mSelectedOutputId; - return dict; + Properties props; + props[kReferenceImagePath] = mReferenceImagePath; + props[kMeasurementsFilePath] = mMeasurementsFilePath; + props[kIgnoreBackground] = mIgnoreBackground; + props[kComputeSquaredDifference] = mComputeSquaredDifference; + props[kComputeAverage] = mComputeAverage; + props[kUseLoadedReference] = mUseLoadedReference; + props[kReportRunningError] = mReportRunningError; + props[kRunningErrorSigma] = mRunningErrorSigma; + props[kSelectedOutputId] = mSelectedOutputId; + return props; } RenderPassReflection ErrorMeasurePass::reflect(const CompileData& compileData) diff --git a/Source/RenderPasses/ErrorMeasurePass/ErrorMeasurePass.h b/Source/RenderPasses/ErrorMeasurePass/ErrorMeasurePass.h index bd5cc1c20..f17b68ca2 100644 --- a/Source/RenderPasses/ErrorMeasurePass/ErrorMeasurePass.h +++ b/Source/RenderPasses/ErrorMeasurePass/ErrorMeasurePass.h @@ -46,19 +46,23 @@ class ErrorMeasurePass : public RenderPass Count }; - static ref create(ref pDevice, const Dictionary& dict) { return make_ref(pDevice, dict); } + FALCOR_ENUM_INFO(OutputId, { + { OutputId::Source, "Source" }, + { OutputId::Reference, "Reference" }, + { OutputId::Difference, "Difference" }, + }); - ErrorMeasurePass(ref pDevice, const Dictionary& dict); + static ref create(ref pDevice, const Properties& props) { return make_ref(pDevice, props); } - virtual Dictionary getScriptingDictionary() override; + ErrorMeasurePass(ref pDevice, const Properties& props); + + virtual Properties getProperties() const override; virtual RenderPassReflection reflect(const CompileData& compileData) override; virtual void execute(RenderContext* pRenderContext, const RenderData& renderData) override; virtual void renderUI(Gui::Widgets& widget) override; virtual bool onKeyEvent(const KeyboardEvent& keyEvent) override; private: - bool init(RenderContext* pRenderContext, const Dictionary& dict); - void loadReference(); ref getReference(const RenderData& renderData) const; void openMeasurementsFile(); @@ -102,3 +106,5 @@ class ErrorMeasurePass : public RenderPass static const Gui::RadioButtonGroup sOutputSelectionButtons; static const Gui::RadioButtonGroup sOutputSelectionButtonsSourceOnly; }; + +FALCOR_ENUM_REGISTER(ErrorMeasurePass::OutputId); diff --git a/Source/RenderPasses/FLIPPass/FLIPPass.cpp b/Source/RenderPasses/FLIPPass/FLIPPass.cpp index 305125951..14fe92616 100644 --- a/Source/RenderPasses/FLIPPass/FLIPPass.cpp +++ b/Source/RenderPasses/FLIPPass/FLIPPass.cpp @@ -39,13 +39,6 @@ namespace const char kErrorMapDisplayOutput[] = "errorMapDisplay"; // Low-precision FLIP error map - use for display / analysis (not computations). const char kExposureMapDisplayOutput[] = "exposureMapDisplay"; // Low-precision HDR-FLIP exposure map. - const Gui::DropdownList kToneMapperList = - { - { (uint32_t)FLIPToneMapperType::ACES, "ACES" }, - { (uint32_t)FLIPToneMapperType::Hable, "Hable"}, - { (uint32_t)FLIPToneMapperType::Reinhard, "Reinhard"}, - }; - const char kEnabled[] = "enabled"; const char kIsHDR[] = "isHDR"; const char kToneMapper[] = "toneMapper"; @@ -65,27 +58,18 @@ namespace extern "C" FALCOR_API_EXPORT void registerPlugin(Falcor::PluginRegistry& registry) { registry.registerClass(); - ScriptBindings::registerBinding(FLIPPass::registerBindings); -} - -void FLIPPass::registerBindings(pybind11::module& m) -{ - pybind11::enum_ toneMapper(m, "FLIPToneMapperType"); - toneMapper.value("ACES", FLIPToneMapperType::ACES); - toneMapper.value("Hable", FLIPToneMapperType::Hable); - toneMapper.value("Reinhard", FLIPToneMapperType::Reinhard); } -FLIPPass::FLIPPass(ref pDevice, const Dictionary& dict) +FLIPPass::FLIPPass(ref pDevice, const Properties& props) : RenderPass(pDevice) { - parseDictionary(dict); + parseProperties(props); - Program::DefineList defines; + DefineList defines; defines.add("TONE_MAPPER", std::to_string((uint32_t)mToneMapper)); mpFLIPPass = ComputePass::create(mpDevice, kFLIPShaderFile, "main", defines); - mpComputeLuminancePass = ComputePass::create(mpDevice, kComputeLuminanceShaderFile, "computeLuminance", Program::DefineList()); + mpComputeLuminancePass = ComputePass::create(mpDevice, kComputeLuminanceShaderFile, "computeLuminance", DefineList()); // Create parallel reduction helper. mpParallelReduction = std::make_unique(mpDevice); @@ -108,34 +92,34 @@ FLIPPass::FLIPPass(ref pDevice, const Dictionary& dict) } } -Dictionary FLIPPass::getScriptingDictionary() +Properties FLIPPass::getProperties() const { - Dictionary d; - d[kEnabled] = mEnabled; - d[kUseMagma] = mUseMagma; - d[kClampInput] = mClampInput; - d[kIsHDR] = mIsHDR; - d[kToneMapper] = mToneMapper; - d[kUseCustomExposureParameters] = mUseCustomExposureParameters; - d[kStartExposure] = mStartExposure; - d[kStopExposure] = mStopExposure; - d[kNumExposures] = mNumExposures; - d[kMonitorWidthPixels] = mMonitorWidthPixels; - d[kMonitorWidthMeters] = mMonitorWidthMeters; - d[kMonitorDistance] = mMonitorDistanceMeters; - d[kComputePooledFLIPValues] = mComputePooledFLIPValues; - d[kUseRealMonitorInfo] = mUseRealMonitorInfo; - return d; + Properties props; + props[kEnabled] = mEnabled; + props[kUseMagma] = mUseMagma; + props[kClampInput] = mClampInput; + props[kIsHDR] = mIsHDR; + props[kToneMapper] = mToneMapper; + props[kUseCustomExposureParameters] = mUseCustomExposureParameters; + props[kStartExposure] = mStartExposure; + props[kStopExposure] = mStopExposure; + props[kNumExposures] = mNumExposures; + props[kMonitorWidthPixels] = mMonitorWidthPixels; + props[kMonitorWidthMeters] = mMonitorWidthMeters; + props[kMonitorDistance] = mMonitorDistanceMeters; + props[kComputePooledFLIPValues] = mComputePooledFLIPValues; + props[kUseRealMonitorInfo] = mUseRealMonitorInfo; + return props; } -void FLIPPass::parseDictionary(const Dictionary& dict) +void FLIPPass::parseProperties(const Properties& props) { // Read settings. - for (const auto& [key, value] : dict) + for (const auto& [key, value] : props) { if (key == kEnabled) mEnabled = value; else if (key == kIsHDR) mIsHDR = value; - else if (key == kToneMapper) mToneMapper = FLIPToneMapperType(value); + else if (key == kToneMapper) mToneMapper = value; else if (key == kUseCustomExposureParameters) mUseCustomExposureParameters = value; else if (key == kStartExposure) mStartExposure = value; else if (key == kStopExposure) mStopExposure = value; @@ -147,7 +131,7 @@ void FLIPPass::parseDictionary(const Dictionary& dict) else if (key == kMonitorDistance) mMonitorDistanceMeters = value; else if (key == kComputePooledFLIPValues) mComputePooledFLIPValues = value; else if (key == kUseRealMonitorInfo) mUseRealMonitorInfo = value; - else logWarning("Unknown field '{}' in a FLIPPass dictionary.", key); + else logWarning("Unknown property '{}' in a FLIPPass properties.", key); } } @@ -170,7 +154,7 @@ void FLIPPass::updatePrograms() return; } - Program::DefineList defines; + DefineList defines; defines.add("TONE_MAPPER", std::to_string((uint32_t)mToneMapper)); mpFLIPPass->getProgram()->addDefines(defines); @@ -360,7 +344,7 @@ void FLIPPass::renderUI(Gui::Widgets& widget) if (mIsHDR) { - dirty |= widget.dropdown("Tone mapper", kToneMapperList, reinterpret_cast(mToneMapper)); + dirty |= widget.dropdown("Tone mapper", mToneMapper); widget.tooltip("The tone mapper assumed by HDR-FLIP."); dirty |= widget.checkbox("Use custom exposure parameters", mUseCustomExposureParameters); widget.tooltip("Check to manually choose start and stop exposure as well as number of exposures used for HDR-FLIP."); diff --git a/Source/RenderPasses/FLIPPass/FLIPPass.h b/Source/RenderPasses/FLIPPass/FLIPPass.h index f448c4be7..4c7f9dc94 100644 --- a/Source/RenderPasses/FLIPPass/FLIPPass.h +++ b/Source/RenderPasses/FLIPPass/FLIPPass.h @@ -46,21 +46,19 @@ 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." }); - static ref create(ref pDevice, const Dictionary& dict) { return make_ref(pDevice, dict); } + static ref create(ref pDevice, const Properties& props) { return make_ref(pDevice, props); } - FLIPPass(ref pDevice, const Dictionary& dict); + FLIPPass(ref pDevice, const Properties& props); - virtual Dictionary getScriptingDictionary() override; + virtual Properties getProperties() const override; virtual RenderPassReflection reflect(const CompileData& compileData) override; virtual void execute(RenderContext* pRenderContext, const RenderData& renderData) override; virtual void renderUI(Gui::Widgets& widget) override; - static void registerBindings(pybind11::module& m); - protected: void updatePrograms(); void computeExposureParameters(const float Ymedian, const float Ymax); - void parseDictionary(const Dictionary& dict); + void parseProperties(const Properties& props); private: bool mEnabled = true; ///< Enables FLIP calculation. diff --git a/Source/RenderPasses/FLIPPass/ToneMappers.slang b/Source/RenderPasses/FLIPPass/ToneMappers.slang index b686884b6..c7e0c22a9 100644 --- a/Source/RenderPasses/FLIPPass/ToneMappers.slang +++ b/Source/RenderPasses/FLIPPass/ToneMappers.slang @@ -44,6 +44,13 @@ enum class FLIPToneMapperType : uint32_t Reinhard = 2, }; +FALCOR_ENUM_INFO(FLIPToneMapperType, { + { FLIPToneMapperType::ACES, "ACES" }, + { FLIPToneMapperType::Hable, "Hable" }, + { FLIPToneMapperType::Reinhard, "Reinhard" }, +}); +FALCOR_ENUM_REGISTER(FLIPToneMapperType); + #ifndef HOST_CODE float3 toneMap(float3 col, FLIPToneMapperType toneMapper = FLIPToneMapperType::ACES) { diff --git a/Source/RenderPasses/GBuffer/GBuffer/GBuffer.cpp b/Source/RenderPasses/GBuffer/GBuffer/GBuffer.cpp index c065a789d..6a6f06bd5 100644 --- a/Source/RenderPasses/GBuffer/GBuffer/GBuffer.cpp +++ b/Source/RenderPasses/GBuffer/GBuffer/GBuffer.cpp @@ -30,7 +30,8 @@ namespace Falcor { // Update 'mtlData' channel format if size changes. - static_assert(sizeof(MaterialHeader) == 8); + // Note: Currently, we only store the first 8 bytes of the material header. + static_assert(sizeof(MaterialHeader) == 16); } // List of primary GBuffer channels. These correspond to the render targets @@ -45,7 +46,7 @@ const ChannelList GBuffer::kGBufferChannels = { "texC", "gTexC", "Texture coordinate", true /* optional */, ResourceFormat::RG32Float }, { "texGrads", "gTexGrads", "Texture gradients (ddx, ddy)", true /* optional */, ResourceFormat::RGBA16Float }, { "mvec", "gMotionVector", "Motion vector", true /* optional */, ResourceFormat::RG32Float }, - { "mtlData", "gMaterialData", "Material data (ID, header)", true /* optional */, ResourceFormat::RGBA32Uint }, + { "mtlData", "gMaterialData", "Material data (ID, header.x, header.y, lobes)", true /* optional */, ResourceFormat::RGBA32Uint }, }; GBuffer::GBuffer(ref pDevice) diff --git a/Source/RenderPasses/GBuffer/GBuffer/GBufferRT.cpp b/Source/RenderPasses/GBuffer/GBuffer/GBufferRT.cpp index fd658730b..95f72af8a 100644 --- a/Source/RenderPasses/GBuffer/GBuffer/GBufferRT.cpp +++ b/Source/RenderPasses/GBuffer/GBuffer/GBufferRT.cpp @@ -45,13 +45,6 @@ namespace // Scripting options const std::string kLODMode = "texLOD"; - const Falcor::Gui::DropdownList kLODModeList = - { - { (uint32_t)TexLODMode::Mip0, "Mip0" }, - { (uint32_t)TexLODMode::RayDiffs, "Ray Diffs" }, - { (uint32_t)TexLODMode::RayCones, "Ray Cones" }, - }; - // Additional output channels. const std::string kVBufferName = "vbuffer"; const ChannelList kGBufferExtraChannels = @@ -72,7 +65,7 @@ namespace }; }; -GBufferRT::GBufferRT(ref pDevice, const Dictionary& dict) +GBufferRT::GBufferRT(ref pDevice, const Properties& props) : GBuffer(pDevice) { if (!mpDevice->isFeatureSupported(Device::SupportedFeatures::RaytracingTier1_1)) @@ -80,7 +73,7 @@ GBufferRT::GBufferRT(ref pDevice, const Dictionary& dict) throw RuntimeError("GBufferRT: Raytracing Tier 1.1 is not supported by the current device"); } - parseDictionary(dict); + parseProperties(props); // Create random engine mpSampleGenerator = SampleGenerator::create(mpDevice, SAMPLE_GENERATOR_DEFAULT); @@ -161,7 +154,7 @@ void GBufferRT::renderUI(Gui::Widgets& widget) GBuffer::renderUI(widget); // Ray tracing specific options. - if (widget.dropdown("LOD Mode", kLODModeList, reinterpret_cast(mLODMode))) + if (widget.dropdown("LOD Mode", mLODMode)) { mOptionsChanged = true; } @@ -179,13 +172,13 @@ void GBufferRT::renderUI(Gui::Widgets& widget) widget.tooltip("This option enables stochastic depth-of-field when the camera's aperture radius is nonzero. Disable it to force the use of a pinhole camera.", true); } -Dictionary GBufferRT::getScriptingDictionary() +Properties GBufferRT::getProperties() const { - Dictionary dict = GBuffer::getScriptingDictionary(); - dict[kLODMode] = mLODMode; - dict[kUseTraceRayInline] = mUseTraceRayInline; - dict[kUseDOF] = mUseDOF; - return dict; + Properties props = GBuffer::getProperties(); + props[kLODMode] = mLODMode; + props[kUseTraceRayInline] = mUseTraceRayInline; + props[kUseDOF] = mUseDOF; + return props; } void GBufferRT::setScene(RenderContext* pRenderContext, const ref& pScene) @@ -195,11 +188,11 @@ void GBufferRT::setScene(RenderContext* pRenderContext, const ref& pScene recreatePrograms(); } -void GBufferRT::parseDictionary(const Dictionary& dict) +void GBufferRT::parseProperties(const Properties& props) { - GBuffer::parseDictionary(dict); + GBuffer::parseProperties(props); - for (const auto& [key, value] : dict) + for (const auto& [key, value] : props) { if (key == kLODMode) mLODMode = value; else if (key == kUseTraceRayInline) mUseTraceRayInline = value; @@ -219,7 +212,7 @@ void GBufferRT::executeRaytrace(RenderContext* pRenderContext, const RenderData& { if (!mRaytrace.pProgram || !mRaytrace.pVars) { - Program::DefineList defines; + DefineList defines; defines.add(mpScene->getSceneDefines()); defines.add(mpSampleGenerator->getDefines()); defines.add(getShaderDefines(renderData)); @@ -285,7 +278,7 @@ void GBufferRT::executeCompute(RenderContext* pRenderContext, const RenderData& desc.addShaderLibrary(kProgramComputeFile).csEntry("main").setShaderModel("6_5"); desc.addTypeConformances(mpScene->getTypeConformances()); - Program::DefineList defines; + DefineList defines; defines.add(mpScene->getSceneDefines()); defines.add(mpSampleGenerator->getDefines()); defines.add(getShaderDefines(renderData)); @@ -306,9 +299,9 @@ void GBufferRT::executeCompute(RenderContext* pRenderContext, const RenderData& mpComputePass->execute(pRenderContext, uint3(mFrameDim, 1)); } -Program::DefineList GBufferRT::getShaderDefines(const RenderData& renderData) const +DefineList GBufferRT::getShaderDefines(const RenderData& renderData) const { - Program::DefineList defines; + DefineList defines; defines.add("COMPUTE_DEPTH_OF_FIELD", mComputeDOF ? "1" : "0"); defines.add("USE_ALPHA_TEST", mUseAlphaTest ? "1" : "0"); defines.add("LOD_MODE", std::to_string((uint32_t)mLODMode)); diff --git a/Source/RenderPasses/GBuffer/GBuffer/GBufferRT.h b/Source/RenderPasses/GBuffer/GBuffer/GBufferRT.h index 7c987e892..335e02b56 100644 --- a/Source/RenderPasses/GBuffer/GBuffer/GBufferRT.h +++ b/Source/RenderPasses/GBuffer/GBuffer/GBufferRT.h @@ -40,23 +40,23 @@ class GBufferRT : public GBuffer public: FALCOR_PLUGIN_CLASS(GBufferRT, "GBufferRT", "Ray traced G-buffer generation pass."); - static ref create(ref pDevice, const Dictionary& dict) { return make_ref(pDevice, dict); } + static ref create(ref pDevice, const Properties& props) { return make_ref(pDevice, props); } - GBufferRT(ref pDevice, const Dictionary& dict); + GBufferRT(ref pDevice, const Properties& props); RenderPassReflection reflect(const CompileData& compileData) override; void execute(RenderContext* pRenderContext, const RenderData& renderData) override; void renderUI(Gui::Widgets& widget) override; - Dictionary getScriptingDictionary() override; + Properties getProperties() const override; void setScene(RenderContext* pRenderContext, const ref& pScene) override; private: - void parseDictionary(const Dictionary& dict) override; + void parseProperties(const Properties& props) override; void executeRaytrace(RenderContext* pRenderContext, const RenderData& renderData); void executeCompute(RenderContext* pRenderContext, const RenderData& renderData); - Program::DefineList getShaderDefines(const RenderData& renderData) const; + DefineList getShaderDefines(const RenderData& renderData) const; void setShaderData(const ShaderVar& var, const RenderData& renderData); void recreatePrograms(); diff --git a/Source/RenderPasses/GBuffer/GBuffer/GBufferRaster.cpp b/Source/RenderPasses/GBuffer/GBuffer/GBufferRaster.cpp index 8ea569741..42e59b9a1 100644 --- a/Source/RenderPasses/GBuffer/GBuffer/GBufferRaster.cpp +++ b/Source/RenderPasses/GBuffer/GBuffer/GBufferRaster.cpp @@ -55,7 +55,7 @@ namespace const std::string kDepthName = "depth"; } -GBufferRaster::GBufferRaster(ref pDevice, const Dictionary& dict) +GBufferRaster::GBufferRaster(ref pDevice, const Properties& props) : GBuffer(pDevice) { // Check for required features. @@ -68,7 +68,7 @@ GBufferRaster::GBufferRaster(ref pDevice, const Dictionary& dict) throw RuntimeError("GBufferRaster: Rasterizer ordered views (ROVs) are not supported by the current device"); } - parseDictionary(dict); + parseProperties(props); // Initialize graphics state mDepthPass.pState = GraphicsState::create(mpDevice); diff --git a/Source/RenderPasses/GBuffer/GBuffer/GBufferRaster.h b/Source/RenderPasses/GBuffer/GBuffer/GBufferRaster.h index cddcb14e8..22400157d 100644 --- a/Source/RenderPasses/GBuffer/GBuffer/GBufferRaster.h +++ b/Source/RenderPasses/GBuffer/GBuffer/GBufferRaster.h @@ -40,9 +40,9 @@ class GBufferRaster : public GBuffer public: FALCOR_PLUGIN_CLASS(GBufferRaster, "GBufferRaster", "Rasterized G-buffer generation pass."); - static ref create(ref pDevice, const Dictionary& dict) { return make_ref(pDevice, dict); } + static ref create(ref pDevice, const Properties& props) { return make_ref(pDevice, props); } - GBufferRaster(ref pDevice, const Dictionary& dict); + GBufferRaster(ref pDevice, const Properties& props); RenderPassReflection reflect(const CompileData& compileData) override; void execute(RenderContext* pRenderContext, const RenderData& renderData) override; diff --git a/Source/RenderPasses/GBuffer/GBufferBase.cpp b/Source/RenderPasses/GBuffer/GBufferBase.cpp index 3279f8f2c..439c205e8 100644 --- a/Source/RenderPasses/GBuffer/GBufferBase.cpp +++ b/Source/RenderPasses/GBuffer/GBufferBase.cpp @@ -35,23 +35,12 @@ #include "Utils/SampleGenerators/HaltonSamplePattern.h" #include "Utils/SampleGenerators/StratifiedSamplePattern.h" -static void registerBindings(pybind11::module& m) -{ - pybind11::enum_ samplePattern(m, "SamplePattern"); - samplePattern.value("Center", GBufferBase::SamplePattern::Center); - samplePattern.value("DirectX", GBufferBase::SamplePattern::DirectX); - samplePattern.value("Halton", GBufferBase::SamplePattern::Halton); - samplePattern.value("Stratified", GBufferBase::SamplePattern::Stratified); -} - extern "C" FALCOR_API_EXPORT void registerPlugin(Falcor::PluginRegistry& registry) { registry.registerClass(); registry.registerClass(); registry.registerClass(); registry.registerClass(); - - Falcor::ScriptBindings::registerBinding(registerBindings); } namespace @@ -66,27 +55,11 @@ namespace const char kAdjustShadingNormals[] = "adjustShadingNormals"; const char kForceCullMode[] = "forceCullMode"; const char kCullMode[] = "cull"; - - // UI variables. - const Gui::DropdownList kSamplePatternList = - { - { (uint32_t)GBufferBase::SamplePattern::Center, "Center" }, - { (uint32_t)GBufferBase::SamplePattern::DirectX, "DirectX" }, - { (uint32_t)GBufferBase::SamplePattern::Halton, "Halton" }, - { (uint32_t)GBufferBase::SamplePattern::Stratified, "Stratified" }, - }; - - const Gui::DropdownList kCullModeList = - { - { (uint32_t)RasterizerState::CullMode::None, "None" }, - { (uint32_t)RasterizerState::CullMode::Back, "Back" }, - { (uint32_t)RasterizerState::CullMode::Front, "Front" }, - }; } -void GBufferBase::parseDictionary(const Dictionary& dict) +void GBufferBase::parseProperties(const Properties& props) { - for (const auto& [key, value] : dict) + for (const auto& [key, value] : props) { if (key == kOutputSize) mOutputSizeSelection = value; else if (key == kFixedOutputSize) mFixedOutputSize = value; @@ -100,35 +73,35 @@ void GBufferBase::parseDictionary(const Dictionary& dict) } // Handle deprecated "disableAlphaTest" value. - if (dict.keyExists(kDisableAlphaTest) && !dict.keyExists(kUseAlphaTest)) mUseAlphaTest = !dict[kDisableAlphaTest]; + if (props.has(kDisableAlphaTest) && !props.has(kUseAlphaTest)) mUseAlphaTest = !props[kDisableAlphaTest]; } -Dictionary GBufferBase::getScriptingDictionary() +Properties GBufferBase::getProperties() const { - Dictionary dict; - dict[kOutputSize] = mOutputSizeSelection; - if (mOutputSizeSelection == RenderPassHelpers::IOSize::Fixed) dict[kFixedOutputSize] = mFixedOutputSize; - dict[kSamplePattern] = mSamplePattern; - dict[kSampleCount] = mSampleCount; - dict[kUseAlphaTest] = mUseAlphaTest; - dict[kAdjustShadingNormals] = mAdjustShadingNormals; - dict[kForceCullMode] = mForceCullMode; - dict[kCullMode] = mCullMode; - return dict; + Properties props; + props[kOutputSize] = mOutputSizeSelection; + if (mOutputSizeSelection == RenderPassHelpers::IOSize::Fixed) props[kFixedOutputSize] = mFixedOutputSize; + props[kSamplePattern] = mSamplePattern; + props[kSampleCount] = mSampleCount; + props[kUseAlphaTest] = mUseAlphaTest; + props[kAdjustShadingNormals] = mAdjustShadingNormals; + props[kForceCullMode] = mForceCullMode; + props[kCullMode] = mCullMode; + return props; } void GBufferBase::renderUI(Gui::Widgets& widget) { // Controls for output size. // When output size requirements change, we'll trigger a graph recompile to update the render pass I/O sizes. - if (widget.dropdown("Output size", RenderPassHelpers::kIOSizeList, (uint32_t&)mOutputSizeSelection)) requestRecompile(); + if (widget.dropdown("Output size", mOutputSizeSelection)) requestRecompile(); if (mOutputSizeSelection == RenderPassHelpers::IOSize::Fixed) { if (widget.var("Size in pixels", mFixedOutputSize, 32u, 16384u)) requestRecompile(); } // Sample pattern controls. - bool updatePattern = widget.dropdown("Sample pattern", kSamplePatternList, (uint32_t&)mSamplePattern); + bool updatePattern = widget.dropdown("Sample pattern", mSamplePattern); widget.tooltip("Selects sample pattern for anti-aliasing over multiple frames.\n\n" "The camera jitter is set at the start of each frame based on the chosen pattern. All render passes should see the same jitter.\n" "'Center' disables anti-aliasing by always sampling at the center of the pixel.", true); @@ -157,10 +130,9 @@ void GBufferBase::renderUI(Gui::Widgets& widget) if (mForceCullMode) { - uint32_t cullMode = (uint32_t)mCullMode; - if (widget.dropdown("Cull mode", kCullModeList, cullMode)) + if (auto cullMode = mCullMode; widget.dropdown("Cull mode", cullMode)) { - setCullMode((RasterizerState::CullMode)cullMode); + setCullMode(cullMode); mOptionsChanged = true; } } diff --git a/Source/RenderPasses/GBuffer/GBufferBase.h b/Source/RenderPasses/GBuffer/GBufferBase.h index e35904d02..db2f43bfc 100644 --- a/Source/RenderPasses/GBuffer/GBufferBase.h +++ b/Source/RenderPasses/GBuffer/GBufferBase.h @@ -45,14 +45,21 @@ class GBufferBase : public RenderPass Stratified, }; + FALCOR_ENUM_INFO(SamplePattern, { + { SamplePattern::Center, "Center" }, + { SamplePattern::DirectX, "DirectX" }, + { SamplePattern::Halton, "Halton" }, + { SamplePattern::Stratified, "Stratified" }, + }); + virtual void renderUI(Gui::Widgets& widget) override; virtual void execute(RenderContext* pRenderContext, const RenderData& renderData) override; - virtual Dictionary getScriptingDictionary() override; + virtual Properties getProperties() const override; virtual void setScene(RenderContext* pRenderContext, const ref& pScene) override; protected: GBufferBase(ref pDevice) : RenderPass(pDevice) {} - virtual void parseDictionary(const Dictionary& dict); + virtual void parseProperties(const Properties& props); virtual void setCullMode(RasterizerState::CullMode mode) { mCullMode = mode; } void updateFrameDim(const uint2 frameDim); void updateSamplePattern(); @@ -79,3 +86,5 @@ class GBufferBase : public RenderPass bool mOptionsChanged = false; ///< Indicates whether any options that affect the output have changed since last frame. }; + +FALCOR_ENUM_REGISTER(GBufferBase::SamplePattern); diff --git a/Source/RenderPasses/GBuffer/VBuffer/VBufferRT.cpp b/Source/RenderPasses/GBuffer/VBuffer/VBufferRT.cpp index 86027acdb..b5dcf1905 100644 --- a/Source/RenderPasses/GBuffer/VBuffer/VBufferRT.cpp +++ b/Source/RenderPasses/GBuffer/VBuffer/VBufferRT.cpp @@ -57,7 +57,7 @@ namespace }; }; -VBufferRT::VBufferRT(ref pDevice, const Dictionary& dict) +VBufferRT::VBufferRT(ref pDevice, const Properties& props) : GBufferBase(pDevice) { if (!mpDevice->isFeatureSupported(Device::SupportedFeatures::RaytracingTier1_1)) @@ -65,7 +65,7 @@ VBufferRT::VBufferRT(ref pDevice, const Dictionary& dict) throw RuntimeError("VBufferRT: Raytracing Tier 1.1 is not supported by the current device"); } - parseDictionary(dict); + parseProperties(props); // Create sample generator mpSampleGenerator = SampleGenerator::create(mpDevice, SAMPLE_GENERATOR_DEFAULT); @@ -138,13 +138,13 @@ void VBufferRT::renderUI(Gui::Widgets& widget) widget.tooltip("This option enables stochastic depth-of-field when the camera's aperture radius is nonzero. Disable it to force the use of a pinhole camera.", true); } -Dictionary VBufferRT::getScriptingDictionary() +Properties VBufferRT::getProperties() const { - Dictionary dict = GBufferBase::getScriptingDictionary(); - dict[kUseTraceRayInline] = mUseTraceRayInline; - dict[kUseDOF] = mUseDOF; + Properties props = GBufferBase::getProperties(); + props[kUseTraceRayInline] = mUseTraceRayInline; + props[kUseDOF] = mUseDOF; - return dict; + return props; } void VBufferRT::setScene(RenderContext* pRenderContext, const ref& pScene) @@ -154,11 +154,11 @@ void VBufferRT::setScene(RenderContext* pRenderContext, const ref& pScene recreatePrograms(); } -void VBufferRT::parseDictionary(const Dictionary& dict) +void VBufferRT::parseProperties(const Properties& props) { - GBufferBase::parseDictionary(dict); + GBufferBase::parseProperties(props); - for (const auto& [key, value] : dict) + for (const auto& [key, value] : props) { if (key == kUseTraceRayInline) mUseTraceRayInline = value; else if (key == kUseDOF) mUseDOF = value; @@ -177,7 +177,7 @@ void VBufferRT::executeRaytrace(RenderContext* pRenderContext, const RenderData& { if (!mRaytrace.pProgram || !mRaytrace.pVars) { - Program::DefineList defines; + DefineList defines; defines.add(mpScene->getSceneDefines()); defines.add(mpSampleGenerator->getDefines()); defines.add(getShaderDefines(renderData)); @@ -241,7 +241,7 @@ void VBufferRT::executeCompute(RenderContext* pRenderContext, const RenderData& desc.addShaderLibrary(kProgramComputeFile).csEntry("main").setShaderModel("6_5"); desc.addTypeConformances(mpScene->getTypeConformances()); - Program::DefineList defines; + DefineList defines; defines.add(mpScene->getSceneDefines()); defines.add(mpSampleGenerator->getDefines()); defines.add(getShaderDefines(renderData)); @@ -262,9 +262,9 @@ void VBufferRT::executeCompute(RenderContext* pRenderContext, const RenderData& mpComputePass->execute(pRenderContext, uint3(mFrameDim, 1)); } -Program::DefineList VBufferRT::getShaderDefines(const RenderData& renderData) const +DefineList VBufferRT::getShaderDefines(const RenderData& renderData) const { - Program::DefineList defines; + DefineList defines; defines.add("COMPUTE_DEPTH_OF_FIELD", mComputeDOF ? "1" : "0"); defines.add("USE_ALPHA_TEST", mUseAlphaTest ? "1" : "0"); diff --git a/Source/RenderPasses/GBuffer/VBuffer/VBufferRT.h b/Source/RenderPasses/GBuffer/VBuffer/VBufferRT.h index 5610fb587..5824dca17 100644 --- a/Source/RenderPasses/GBuffer/VBuffer/VBufferRT.h +++ b/Source/RenderPasses/GBuffer/VBuffer/VBufferRT.h @@ -42,23 +42,23 @@ class VBufferRT : public GBufferBase public: FALCOR_PLUGIN_CLASS(VBufferRT, "VBufferRT", "Ray traced V-buffer generation pass."); - static ref create(ref pDevice, const Dictionary& dict) { return make_ref(pDevice, dict); } + static ref create(ref pDevice, const Properties& props) { return make_ref(pDevice, props); } - VBufferRT(ref pDevice, const Dictionary& dict); + VBufferRT(ref pDevice, const Properties& props); RenderPassReflection reflect(const CompileData& compileData) override; void execute(RenderContext* pRenderContext, const RenderData& renderData) override; void renderUI(Gui::Widgets& widget) override; - Dictionary getScriptingDictionary() override; + Properties getProperties() const override; void setScene(RenderContext* pRenderContext, const ref& pScene) override; private: - void parseDictionary(const Dictionary& dict) override; + void parseProperties(const Properties& props) override; void executeRaytrace(RenderContext* pRenderContext, const RenderData& renderData); void executeCompute(RenderContext* pRenderContext, const RenderData& renderData); - Program::DefineList getShaderDefines(const RenderData& renderData) const; + DefineList getShaderDefines(const RenderData& renderData) const; void setShaderData(const ShaderVar& var, const RenderData& renderData); void recreatePrograms(); diff --git a/Source/RenderPasses/GBuffer/VBuffer/VBufferRaster.cpp b/Source/RenderPasses/GBuffer/VBuffer/VBufferRaster.cpp index fa08d7833..d3936362d 100644 --- a/Source/RenderPasses/GBuffer/VBuffer/VBufferRaster.cpp +++ b/Source/RenderPasses/GBuffer/VBuffer/VBufferRaster.cpp @@ -48,7 +48,7 @@ namespace const std::string kDepthName = "depth"; } -VBufferRaster::VBufferRaster(ref pDevice, const Dictionary& dict) +VBufferRaster::VBufferRaster(ref pDevice, const Properties& props) : GBufferBase(pDevice) { // Check for required features. @@ -61,7 +61,7 @@ VBufferRaster::VBufferRaster(ref pDevice, const Dictionary& dict) throw RuntimeError("VBufferRaster: Rasterizer ordered views (ROVs) are not supported by the current device"); } - parseDictionary(dict); + parseProperties(props); // Initialize graphics state mRaster.pState = GraphicsState::create(mpDevice); diff --git a/Source/RenderPasses/GBuffer/VBuffer/VBufferRaster.h b/Source/RenderPasses/GBuffer/VBuffer/VBufferRaster.h index c23a2f539..c65b5d3f8 100644 --- a/Source/RenderPasses/GBuffer/VBuffer/VBufferRaster.h +++ b/Source/RenderPasses/GBuffer/VBuffer/VBufferRaster.h @@ -41,9 +41,9 @@ class VBufferRaster : public GBufferBase public: FALCOR_PLUGIN_CLASS(VBufferRaster, "VBufferRaster", "Rasterized V-buffer generation pass."); - static ref create(ref pDevice, const Dictionary& dict) { return make_ref(pDevice, dict); } + static ref create(ref pDevice, const Properties& props) { return make_ref(pDevice, props); } - VBufferRaster(ref pDevice, const Dictionary& dict); + VBufferRaster(ref pDevice, const Properties& props); RenderPassReflection reflect(const CompileData& compileData) override; void setScene(RenderContext* pRenderContext, const ref& pScene) override; diff --git a/Source/RenderPasses/ImageLoader/ImageLoader.cpp b/Source/RenderPasses/ImageLoader/ImageLoader.cpp index 22f798a11..8039209fe 100644 --- a/Source/RenderPasses/ImageLoader/ImageLoader.cpp +++ b/Source/RenderPasses/ImageLoader/ImageLoader.cpp @@ -45,10 +45,10 @@ extern "C" FALCOR_API_EXPORT void registerPlugin(Falcor::PluginRegistry& registr registry.registerClass(); } -ImageLoader::ImageLoader(ref pDevice, const Dictionary& dict) +ImageLoader::ImageLoader(ref pDevice, const Properties& props) : RenderPass(pDevice) { - for (const auto& [key, value] : dict) + for (const auto& [key, value] : props) { if (key == kOutputSize) mOutputSizeSelection = value; else if (key == kOutputFormat) mOutputFormat = value; @@ -57,7 +57,7 @@ ImageLoader::ImageLoader(ref pDevice, const Dictionary& dict) else if (key == kMips) mGenerateMips = value; else if (key == kArraySlice) mArraySlice = value; else if (key == kMipLevel) mMipLevel = value; - else logWarning("Unknown field '{}' in a ImageLoader dictionary.", key); + else logWarning("Unknown property '{}' in a ImageLoader properties.", key); } if (!mImagePath.empty()) @@ -79,17 +79,17 @@ RenderPassReflection ImageLoader::reflect(const CompileData& compileData) return reflector; } -Dictionary ImageLoader::getScriptingDictionary() +Properties ImageLoader::getProperties() const { - Dictionary dict; - dict[kOutputSize] = mOutputSizeSelection; - if (mOutputFormat != ResourceFormat::Unknown) dict[kOutputFormat] = mOutputFormat; - dict[kImage] = stripDataDirectories(mImagePath); - dict[kMips] = mGenerateMips; - dict[kSrgb] = mLoadSRGB; - dict[kArraySlice] = mArraySlice; - dict[kMipLevel] = mMipLevel; - return dict; + Properties props; + props[kOutputSize] = mOutputSizeSelection; + if (mOutputFormat != ResourceFormat::Unknown) props[kOutputFormat] = mOutputFormat; + props[kImage] = stripDataDirectories(mImagePath); + props[kMips] = mGenerateMips; + props[kSrgb] = mLoadSRGB; + props[kArraySlice] = mArraySlice; + props[kMipLevel] = mMipLevel; + return props; } void ImageLoader::compile(RenderContext* pRenderContext, const CompileData& compileData) @@ -118,7 +118,7 @@ void ImageLoader::execute(RenderContext* pRenderContext, const RenderData& rende void ImageLoader::renderUI(Gui::Widgets& widget) { // When output size requirements change, we'll trigger a graph recompile to update the render pass I/O sizes. - if (widget.dropdown("Output size", RenderPassHelpers::kIOSizeList, (uint32_t&)mOutputSizeSelection)) requestRecompile(); + if (widget.dropdown("Output size", mOutputSizeSelection)) requestRecompile(); widget.tooltip("Specifies the pass output size.\n" "'Default' means that the output is sized based on requirements of connected passes.\n" "'Fixed' means the output is always at the image's native size.\n" diff --git a/Source/RenderPasses/ImageLoader/ImageLoader.h b/Source/RenderPasses/ImageLoader/ImageLoader.h index e8d9f81f2..c7f2d5c18 100644 --- a/Source/RenderPasses/ImageLoader/ImageLoader.h +++ b/Source/RenderPasses/ImageLoader/ImageLoader.h @@ -37,15 +37,15 @@ class ImageLoader : public RenderPass public: FALCOR_PLUGIN_CLASS(ImageLoader, "ImageLoader", "Load an image into a texture."); - static ref create(ref pDevice, const Dictionary& dict) { return make_ref(pDevice, dict); } + static ref create(ref pDevice, const Properties& props) { return make_ref(pDevice, props); } - ImageLoader(ref pDevice, const Dictionary& dict); + ImageLoader(ref pDevice, const Properties& props); 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 Dictionary getScriptingDictionary() override; + virtual Properties getProperties() const override; private: bool loadImage(const std::filesystem::path& path); diff --git a/Source/RenderPasses/MinimalPathTracer/MinimalPathTracer.cpp b/Source/RenderPasses/MinimalPathTracer/MinimalPathTracer.cpp index c3e26319e..3936cb585 100644 --- a/Source/RenderPasses/MinimalPathTracer/MinimalPathTracer.cpp +++ b/Source/RenderPasses/MinimalPathTracer/MinimalPathTracer.cpp @@ -61,34 +61,34 @@ namespace const char kUseImportanceSampling[] = "useImportanceSampling"; } -MinimalPathTracer::MinimalPathTracer(ref pDevice, const Dictionary& dict) +MinimalPathTracer::MinimalPathTracer(ref pDevice, const Properties& props) : RenderPass(pDevice) { - parseDictionary(dict); + parseProperties(props); // Create a sample generator. mpSampleGenerator = SampleGenerator::create(mpDevice, SAMPLE_GENERATOR_UNIFORM); FALCOR_ASSERT(mpSampleGenerator); } -void MinimalPathTracer::parseDictionary(const Dictionary& dict) +void MinimalPathTracer::parseProperties(const Properties& props) { - for (const auto& [key, value] : dict) + for (const auto& [key, value] : props) { if (key == kMaxBounces) mMaxBounces = value; else if (key == kComputeDirect) mComputeDirect = value; else if (key == kUseImportanceSampling) mUseImportanceSampling = value; - else logWarning("Unknown field '{}' in MinimalPathTracer dictionary.", key); + else logWarning("Unknown property '{}' in MinimalPathTracer properties.", key); } } -Dictionary MinimalPathTracer::getScriptingDictionary() +Properties MinimalPathTracer::getProperties() const { - Dictionary d; - d[kMaxBounces] = mMaxBounces; - d[kComputeDirect] = mComputeDirect; - d[kUseImportanceSampling] = mUseImportanceSampling; - return d; + Properties props; + props[kMaxBounces] = mMaxBounces; + props[kComputeDirect] = mComputeDirect; + props[kUseImportanceSampling] = mUseImportanceSampling; + return props; } RenderPassReflection MinimalPathTracer::reflect(const CompileData& compileData) diff --git a/Source/RenderPasses/MinimalPathTracer/MinimalPathTracer.h b/Source/RenderPasses/MinimalPathTracer/MinimalPathTracer.h index 9749a4f84..9bd146a1f 100644 --- a/Source/RenderPasses/MinimalPathTracer/MinimalPathTracer.h +++ b/Source/RenderPasses/MinimalPathTracer/MinimalPathTracer.h @@ -46,11 +46,11 @@ class MinimalPathTracer : public RenderPass public: FALCOR_PLUGIN_CLASS(MinimalPathTracer, "MinimalPathTracer", "Minimal path tracer."); - static ref create(ref pDevice, const Dictionary& dict) { return make_ref(pDevice, dict); } + static ref create(ref pDevice, const Properties& props) { return make_ref(pDevice, props); } - MinimalPathTracer(ref pDevice, const Dictionary& dict); + MinimalPathTracer(ref pDevice, const Properties& props); - virtual Dictionary getScriptingDictionary() override; + virtual Properties getProperties() const override; virtual RenderPassReflection reflect(const CompileData& compileData) override; virtual void execute(RenderContext* pRenderContext, const RenderData& renderData) override; virtual void renderUI(Gui::Widgets& widget) override; @@ -59,7 +59,7 @@ class MinimalPathTracer : public RenderPass virtual bool onKeyEvent(const KeyboardEvent& keyEvent) override { return false; } private: - void parseDictionary(const Dictionary& dict); + void parseProperties(const Properties& props); void prepareVars(); // Internal state diff --git a/Source/RenderPasses/ModulateIllumination/ModulateIllumination.cpp b/Source/RenderPasses/ModulateIllumination/ModulateIllumination.cpp index e3621be53..7729b1b39 100644 --- a/Source/RenderPasses/ModulateIllumination/ModulateIllumination.cpp +++ b/Source/RenderPasses/ModulateIllumination/ModulateIllumination.cpp @@ -71,13 +71,13 @@ extern "C" FALCOR_API_EXPORT void registerPlugin(Falcor::PluginRegistry& registr registry.registerClass(); } -ModulateIllumination::ModulateIllumination(ref pDevice, const Dictionary& dict) +ModulateIllumination::ModulateIllumination(ref pDevice, const Properties& props) : RenderPass(pDevice) { - mpModulateIlluminationPass = ComputePass::create(mpDevice, kShaderFile, "main", Program::DefineList(), false); + mpModulateIlluminationPass = ComputePass::create(mpDevice, kShaderFile, "main", DefineList(), false); // Deserialize pass from dictionary. - for (const auto& [key, value] : dict) + for (const auto& [key, value] : props) { if (key == kUseEmission) mUseEmission = value; else if (key == kUseDiffuseReflectance) mUseDiffuseReflectance = value; @@ -94,28 +94,28 @@ ModulateIllumination::ModulateIllumination(ref pDevice, const Dictionary else if (key == kOutputSize) mOutputSizeSelection = value; else { - logWarning("Unknown field '{}' in ModulateIllumination dictionary.", key); + logWarning("Unknown property '{}' in ModulateIllumination properties.", key); } } } -Falcor::Dictionary ModulateIllumination::getScriptingDictionary() +Falcor::Properties ModulateIllumination::getProperties() const { - Dictionary dict; - dict[kUseEmission] = mUseEmission; - dict[kUseDiffuseReflectance] = mUseDiffuseReflectance; - dict[kUseDiffuseRadiance] = mUseDiffuseRadiance; - dict[kUseSpecularReflectance] = mUseSpecularReflectance; - dict[kUseSpecularRadiance] = mUseSpecularRadiance; - dict[kUseDeltaReflectionEmission] = mUseDeltaReflectionEmission; - dict[kUseDeltaReflectionReflectance] = mUseDeltaReflectionReflectance; - dict[kUseDeltaReflectionRadiance] = mUseDeltaReflectionRadiance; - dict[kUseDeltaTransmissionEmission] = mUseDeltaTransmissionEmission; - dict[kUseDeltaTransmissionReflectance] = mUseDeltaTransmissionReflectance; - dict[kUseDeltaTransmissionRadiance] = mUseDeltaTransmissionRadiance; - dict[kUseResidualRadiance] = mUseResidualRadiance; - dict[kOutputSize] = mOutputSizeSelection; - return dict; + Properties props; + props[kUseEmission] = mUseEmission; + props[kUseDiffuseReflectance] = mUseDiffuseReflectance; + props[kUseDiffuseRadiance] = mUseDiffuseRadiance; + props[kUseSpecularReflectance] = mUseSpecularReflectance; + props[kUseSpecularRadiance] = mUseSpecularRadiance; + props[kUseDeltaReflectionEmission] = mUseDeltaReflectionEmission; + props[kUseDeltaReflectionReflectance] = mUseDeltaReflectionReflectance; + props[kUseDeltaReflectionRadiance] = mUseDeltaReflectionRadiance; + props[kUseDeltaTransmissionEmission] = mUseDeltaTransmissionEmission; + props[kUseDeltaTransmissionReflectance] = mUseDeltaTransmissionReflectance; + props[kUseDeltaTransmissionRadiance] = mUseDeltaTransmissionRadiance; + props[kUseResidualRadiance] = mUseResidualRadiance; + props[kOutputSize] = mOutputSizeSelection; + return props; } RenderPassReflection ModulateIllumination::reflect(const CompileData& compileData) @@ -141,7 +141,7 @@ void ModulateIllumination::execute(RenderContext* pRenderContext, const RenderDa // For optional I/O resources, set 'is_valid_' defines to inform the program of which ones it can access. // TODO: This should be moved to a more general mechanism using Slang. - Program::DefineList defineList = getValidResourceDefines(kInputChannels, renderData); + DefineList defineList = getValidResourceDefines(kInputChannels, renderData); // Override defines. if (!mUseEmission) defineList["is_valid_gEmission"] = "0"; diff --git a/Source/RenderPasses/ModulateIllumination/ModulateIllumination.h b/Source/RenderPasses/ModulateIllumination/ModulateIllumination.h index 44c03f3c9..80817c640 100644 --- a/Source/RenderPasses/ModulateIllumination/ModulateIllumination.h +++ b/Source/RenderPasses/ModulateIllumination/ModulateIllumination.h @@ -37,11 +37,11 @@ class ModulateIllumination : public RenderPass public: FALCOR_PLUGIN_CLASS(ModulateIllumination, "ModulateIllumination", "Modulate illumination pass."); - static ref create(ref pDevice, const Dictionary& dict) { return make_ref(pDevice, dict); } + static ref create(ref pDevice, const Properties& props) { return make_ref(pDevice, props); } - ModulateIllumination(ref pDevice, const Dictionary& dict); + ModulateIllumination(ref pDevice, const Properties& props); - virtual Dictionary getScriptingDictionary() override; + virtual Properties getProperties() const 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; diff --git a/Source/RenderPasses/NRDPass/NRDPass.cpp b/Source/RenderPasses/NRDPass/NRDPass.cpp index 913894793..3df92dd62 100644 --- a/Source/RenderPasses/NRDPass/NRDPass.cpp +++ b/Source/RenderPasses/NRDPass/NRDPass.cpp @@ -106,18 +106,18 @@ namespace }; } -NRDPass::NRDPass(ref pDevice, const Dictionary& dict) +NRDPass::NRDPass(ref pDevice, const Properties& props) : RenderPass(pDevice) { mpDevice->requireD3D12(); - Program::DefineList definesRelax; + DefineList definesRelax; definesRelax.add("NRD_USE_OCT_NORMAL_ENCODING", "1"); definesRelax.add("NRD_USE_MATERIAL_ID", "0"); definesRelax.add("NRD_METHOD", "0"); // NRD_METHOD_RELAX_DIFFUSE_SPECULAR mpPackRadiancePassRelax = ComputePass::create(mpDevice, kShaderPackRadiance, "main", definesRelax); - Program::DefineList definesReblur; + DefineList definesReblur; definesReblur.add("NRD_USE_OCT_NORMAL_ENCODING", "1"); definesReblur.add("NRD_USE_MATERIAL_ID", "0"); definesReblur.add("NRD_METHOD", "1"); // NRD_METHOD_REBLUR_DIFFUSE_SPECULAR @@ -149,7 +149,7 @@ NRDPass::NRDPass(ref pDevice, const Dictionary& dict) mRelaxDiffuseSettings.depthThreshold = 0.02f; // Deserialize pass from dictionary. - for (const auto& [key, value] : dict) + for (const auto& [key, value] : props) { if (key == kEnabled) mEnabled = value; else if (key == kMethod) mDenoisingMethod = value; @@ -198,7 +198,7 @@ NRDPass::NRDPass(ref pDevice, const Dictionary& dict) else if (key == kEnableMaterialTestForSpecular) mRelaxDiffuseSpecularSettings.enableMaterialTestForSpecular = value; else { - logWarning("Unknown field '{}' in NRD dictionary.", key); + logWarning("Unknown property '{}' in NRD properties.", key); } } else if (mDenoisingMethod == DenoisingMethod::RelaxDiffuse) @@ -222,88 +222,88 @@ NRDPass::NRDPass(ref pDevice, const Dictionary& dict) else if (key == kEnableMaterialTestForDiffuse) mRelaxDiffuseSettings.enableMaterialTest = value; else { - logWarning("Unknown field '{}' in NRD dictionary.", key); + logWarning("Unknown property '{}' in NRD properties.", key); } } else { - logWarning("Unknown field '{}' in NRD dictionary.", key); + logWarning("Unknown property '{}' in NRD properties.", key); } } } -Dictionary NRDPass::getScriptingDictionary() +Properties NRDPass::getProperties() const { - Dictionary dict; + Properties props; - dict[kEnabled] = mEnabled; - dict[kMethod] = mDenoisingMethod; - dict[kOutputSize] = mOutputSizeSelection; + props[kEnabled] = mEnabled; + props[kMethod] = mDenoisingMethod; + props[kOutputSize] = mOutputSizeSelection; // Common settings. - dict[kWorldSpaceMotion] = mWorldSpaceMotion; - dict[kDisocclusionThreshold] = mDisocclusionThreshold; + props[kWorldSpaceMotion] = mWorldSpaceMotion; + props[kDisocclusionThreshold] = mDisocclusionThreshold; // Pack radiance settings. - dict[kMaxIntensity] = mMaxIntensity; + props[kMaxIntensity] = mMaxIntensity; // ReLAX diffuse/specular settings. if (mDenoisingMethod == DenoisingMethod::RelaxDiffuseSpecular || mDenoisingMethod == DenoisingMethod::ReblurDiffuseSpecular) { - dict[kDiffusePrepassBlurRadius] = mRelaxDiffuseSpecularSettings.diffusePrepassBlurRadius; - dict[kSpecularPrepassBlurRadius] = mRelaxDiffuseSpecularSettings.specularPrepassBlurRadius; - dict[kDiffuseMaxAccumulatedFrameNum] = mRelaxDiffuseSpecularSettings.diffuseMaxAccumulatedFrameNum; - dict[kSpecularMaxAccumulatedFrameNum] = mRelaxDiffuseSpecularSettings.specularMaxAccumulatedFrameNum; - dict[kDiffuseMaxFastAccumulatedFrameNum] = mRelaxDiffuseSpecularSettings.diffuseMaxFastAccumulatedFrameNum; - dict[kSpecularMaxFastAccumulatedFrameNum] = mRelaxDiffuseSpecularSettings.specularMaxFastAccumulatedFrameNum; - dict[kDiffusePhiLuminance] = mRelaxDiffuseSpecularSettings.diffusePhiLuminance; - dict[kSpecularPhiLuminance] = mRelaxDiffuseSpecularSettings.specularPhiLuminance; - dict[kDiffuseLobeAngleFraction] = mRelaxDiffuseSpecularSettings.diffuseLobeAngleFraction; - dict[kSpecularLobeAngleFraction] = mRelaxDiffuseSpecularSettings.specularLobeAngleFraction; - dict[kRoughnessFraction] = mRelaxDiffuseSpecularSettings.roughnessFraction; - dict[kDiffuseHistoryRejectionNormalThreshold] = mRelaxDiffuseSpecularSettings.diffuseHistoryRejectionNormalThreshold; - dict[kSpecularVarianceBoost] = mRelaxDiffuseSpecularSettings.specularVarianceBoost; - dict[kSpecularLobeAngleSlack] = mRelaxDiffuseSpecularSettings.specularLobeAngleSlack; - dict[kDisocclusionFixEdgeStoppingNormalPower] = mRelaxDiffuseSpecularSettings.disocclusionFixEdgeStoppingNormalPower; - dict[kDisocclusionFixMaxRadius] = mRelaxDiffuseSpecularSettings.disocclusionFixMaxRadius; - dict[kDisocclusionFixNumFramesToFix] = mRelaxDiffuseSpecularSettings.disocclusionFixNumFramesToFix; - dict[kHistoryClampingColorBoxSigmaScale] = mRelaxDiffuseSpecularSettings.historyClampingColorBoxSigmaScale; - dict[kSpatialVarianceEstimationHistoryThreshold] = mRelaxDiffuseSpecularSettings.spatialVarianceEstimationHistoryThreshold; - dict[kAtrousIterationNum] = mRelaxDiffuseSpecularSettings.atrousIterationNum; - dict[kMinLuminanceWeight] = mRelaxDiffuseSpecularSettings.minLuminanceWeight; - dict[kDepthThreshold] = mRelaxDiffuseSpecularSettings.depthThreshold; - dict[kLuminanceEdgeStoppingRelaxation] = mRelaxDiffuseSpecularSettings.luminanceEdgeStoppingRelaxation; - dict[kNormalEdgeStoppingRelaxation] = mRelaxDiffuseSpecularSettings.normalEdgeStoppingRelaxation; - dict[kRoughnessEdgeStoppingRelaxation] = mRelaxDiffuseSpecularSettings.roughnessEdgeStoppingRelaxation; - dict[kEnableAntiFirefly] = mRelaxDiffuseSpecularSettings.enableAntiFirefly; - dict[kEnableReprojectionTestSkippingWithoutMotion] = mRelaxDiffuseSpecularSettings.enableReprojectionTestSkippingWithoutMotion; - dict[kEnableSpecularVirtualHistoryClamping] = mRelaxDiffuseSpecularSettings.enableSpecularVirtualHistoryClamping; - dict[kEnableRoughnessEdgeStopping] = mRelaxDiffuseSpecularSettings.enableRoughnessEdgeStopping; - dict[kEnableMaterialTestForDiffuse] = mRelaxDiffuseSpecularSettings.enableMaterialTestForDiffuse; - dict[kEnableMaterialTestForSpecular] = mRelaxDiffuseSpecularSettings.enableMaterialTestForSpecular; + props[kDiffusePrepassBlurRadius] = mRelaxDiffuseSpecularSettings.diffusePrepassBlurRadius; + props[kSpecularPrepassBlurRadius] = mRelaxDiffuseSpecularSettings.specularPrepassBlurRadius; + props[kDiffuseMaxAccumulatedFrameNum] = mRelaxDiffuseSpecularSettings.diffuseMaxAccumulatedFrameNum; + props[kSpecularMaxAccumulatedFrameNum] = mRelaxDiffuseSpecularSettings.specularMaxAccumulatedFrameNum; + props[kDiffuseMaxFastAccumulatedFrameNum] = mRelaxDiffuseSpecularSettings.diffuseMaxFastAccumulatedFrameNum; + props[kSpecularMaxFastAccumulatedFrameNum] = mRelaxDiffuseSpecularSettings.specularMaxFastAccumulatedFrameNum; + props[kDiffusePhiLuminance] = mRelaxDiffuseSpecularSettings.diffusePhiLuminance; + props[kSpecularPhiLuminance] = mRelaxDiffuseSpecularSettings.specularPhiLuminance; + props[kDiffuseLobeAngleFraction] = mRelaxDiffuseSpecularSettings.diffuseLobeAngleFraction; + props[kSpecularLobeAngleFraction] = mRelaxDiffuseSpecularSettings.specularLobeAngleFraction; + props[kRoughnessFraction] = mRelaxDiffuseSpecularSettings.roughnessFraction; + props[kDiffuseHistoryRejectionNormalThreshold] = mRelaxDiffuseSpecularSettings.diffuseHistoryRejectionNormalThreshold; + props[kSpecularVarianceBoost] = mRelaxDiffuseSpecularSettings.specularVarianceBoost; + props[kSpecularLobeAngleSlack] = mRelaxDiffuseSpecularSettings.specularLobeAngleSlack; + props[kDisocclusionFixEdgeStoppingNormalPower] = mRelaxDiffuseSpecularSettings.disocclusionFixEdgeStoppingNormalPower; + props[kDisocclusionFixMaxRadius] = mRelaxDiffuseSpecularSettings.disocclusionFixMaxRadius; + props[kDisocclusionFixNumFramesToFix] = mRelaxDiffuseSpecularSettings.disocclusionFixNumFramesToFix; + props[kHistoryClampingColorBoxSigmaScale] = mRelaxDiffuseSpecularSettings.historyClampingColorBoxSigmaScale; + props[kSpatialVarianceEstimationHistoryThreshold] = mRelaxDiffuseSpecularSettings.spatialVarianceEstimationHistoryThreshold; + props[kAtrousIterationNum] = mRelaxDiffuseSpecularSettings.atrousIterationNum; + props[kMinLuminanceWeight] = mRelaxDiffuseSpecularSettings.minLuminanceWeight; + props[kDepthThreshold] = mRelaxDiffuseSpecularSettings.depthThreshold; + props[kLuminanceEdgeStoppingRelaxation] = mRelaxDiffuseSpecularSettings.luminanceEdgeStoppingRelaxation; + props[kNormalEdgeStoppingRelaxation] = mRelaxDiffuseSpecularSettings.normalEdgeStoppingRelaxation; + props[kRoughnessEdgeStoppingRelaxation] = mRelaxDiffuseSpecularSettings.roughnessEdgeStoppingRelaxation; + props[kEnableAntiFirefly] = mRelaxDiffuseSpecularSettings.enableAntiFirefly; + props[kEnableReprojectionTestSkippingWithoutMotion] = mRelaxDiffuseSpecularSettings.enableReprojectionTestSkippingWithoutMotion; + props[kEnableSpecularVirtualHistoryClamping] = mRelaxDiffuseSpecularSettings.enableSpecularVirtualHistoryClamping; + props[kEnableRoughnessEdgeStopping] = mRelaxDiffuseSpecularSettings.enableRoughnessEdgeStopping; + props[kEnableMaterialTestForDiffuse] = mRelaxDiffuseSpecularSettings.enableMaterialTestForDiffuse; + props[kEnableMaterialTestForSpecular] = mRelaxDiffuseSpecularSettings.enableMaterialTestForSpecular; } else if (mDenoisingMethod == DenoisingMethod::RelaxDiffuse) { - dict[kDiffusePrepassBlurRadius] = mRelaxDiffuseSettings.prepassBlurRadius; - dict[kDiffuseMaxAccumulatedFrameNum] = mRelaxDiffuseSettings.diffuseMaxAccumulatedFrameNum; - dict[kDiffuseMaxFastAccumulatedFrameNum] = mRelaxDiffuseSettings.diffuseMaxFastAccumulatedFrameNum; - dict[kDiffusePhiLuminance] = mRelaxDiffuseSettings.diffusePhiLuminance; - dict[kDiffuseLobeAngleFraction] = mRelaxDiffuseSettings.diffuseLobeAngleFraction; - dict[kDiffuseHistoryRejectionNormalThreshold] = mRelaxDiffuseSettings.diffuseHistoryRejectionNormalThreshold; - dict[kDisocclusionFixEdgeStoppingNormalPower] = mRelaxDiffuseSettings.disocclusionFixEdgeStoppingNormalPower; - dict[kDisocclusionFixMaxRadius] = mRelaxDiffuseSettings.disocclusionFixMaxRadius; - dict[kDisocclusionFixNumFramesToFix] = mRelaxDiffuseSettings.disocclusionFixNumFramesToFix; - dict[kHistoryClampingColorBoxSigmaScale] = mRelaxDiffuseSettings.historyClampingColorBoxSigmaScale; - dict[kSpatialVarianceEstimationHistoryThreshold] = mRelaxDiffuseSettings.spatialVarianceEstimationHistoryThreshold; - dict[kAtrousIterationNum] = mRelaxDiffuseSettings.atrousIterationNum; - dict[kMinLuminanceWeight] = mRelaxDiffuseSettings.minLuminanceWeight; - dict[kDepthThreshold] = mRelaxDiffuseSettings.depthThreshold; - dict[kEnableAntiFirefly] = mRelaxDiffuseSettings.enableAntiFirefly; - dict[kEnableReprojectionTestSkippingWithoutMotion] = mRelaxDiffuseSettings.enableReprojectionTestSkippingWithoutMotion; - dict[kEnableMaterialTestForDiffuse] = mRelaxDiffuseSettings.enableMaterialTest; + props[kDiffusePrepassBlurRadius] = mRelaxDiffuseSettings.prepassBlurRadius; + props[kDiffuseMaxAccumulatedFrameNum] = mRelaxDiffuseSettings.diffuseMaxAccumulatedFrameNum; + props[kDiffuseMaxFastAccumulatedFrameNum] = mRelaxDiffuseSettings.diffuseMaxFastAccumulatedFrameNum; + props[kDiffusePhiLuminance] = mRelaxDiffuseSettings.diffusePhiLuminance; + props[kDiffuseLobeAngleFraction] = mRelaxDiffuseSettings.diffuseLobeAngleFraction; + props[kDiffuseHistoryRejectionNormalThreshold] = mRelaxDiffuseSettings.diffuseHistoryRejectionNormalThreshold; + props[kDisocclusionFixEdgeStoppingNormalPower] = mRelaxDiffuseSettings.disocclusionFixEdgeStoppingNormalPower; + props[kDisocclusionFixMaxRadius] = mRelaxDiffuseSettings.disocclusionFixMaxRadius; + props[kDisocclusionFixNumFramesToFix] = mRelaxDiffuseSettings.disocclusionFixNumFramesToFix; + props[kHistoryClampingColorBoxSigmaScale] = mRelaxDiffuseSettings.historyClampingColorBoxSigmaScale; + props[kSpatialVarianceEstimationHistoryThreshold] = mRelaxDiffuseSettings.spatialVarianceEstimationHistoryThreshold; + props[kAtrousIterationNum] = mRelaxDiffuseSettings.atrousIterationNum; + props[kMinLuminanceWeight] = mRelaxDiffuseSettings.minLuminanceWeight; + props[kDepthThreshold] = mRelaxDiffuseSettings.depthThreshold; + props[kEnableAntiFirefly] = mRelaxDiffuseSettings.enableAntiFirefly; + props[kEnableReprojectionTestSkippingWithoutMotion] = mRelaxDiffuseSettings.enableReprojectionTestSkippingWithoutMotion; + props[kEnableMaterialTestForDiffuse] = mRelaxDiffuseSettings.enableMaterialTest; } - return dict; + return props; } RenderPassReflection NRDPass::reflect(const CompileData& compileData) @@ -668,8 +668,8 @@ static nrd::Method getNrdMethod(NRDPass::DenoisingMethod denoisingMethod) case NRDPass::DenoisingMethod::RelaxDiffuseSpecular: return nrd::Method::RELAX_DIFFUSE_SPECULAR; case NRDPass::DenoisingMethod::RelaxDiffuse: return nrd::Method::RELAX_DIFFUSE; case NRDPass::DenoisingMethod::ReblurDiffuseSpecular: return nrd::Method::REBLUR_DIFFUSE_SPECULAR; - case NRDPass::DenoisingMethod::SpecularReflectionMv: return nrd::Method::SPECULAR_REFLECTION_MV; - case NRDPass::DenoisingMethod::SpecularDeltaMv: return nrd::Method::SPECULAR_DELTA_MV; + case NRDPass::DenoisingMethod::SpecularReflectionMv: return nrd::Method::SPECULAR_REFLECTION_MV; + case NRDPass::DenoisingMethod::SpecularDeltaMv: return nrd::Method::SPECULAR_DELTA_MV; default: FALCOR_UNREACHABLE(); return nrd::Method::RELAX_DIFFUSE_SPECULAR; @@ -780,8 +780,8 @@ void NRDPass::createPipelines() Program::Desc programDesc; programDesc.addShaderLibrary(shaderFileName).csEntry(nrdPipelineDesc.shaderEntryPointName); - programDesc.setCompilerFlags(Shader::CompilerFlags::MatrixLayoutColumnMajor); - Program::DefineList defines; + programDesc.setCompilerFlags(Program::CompilerFlags::MatrixLayoutColumnMajor); + DefineList defines; defines.add("NRD_COMPILER_DXC"); defines.add("NRD_USE_OCT_NORMAL_ENCODING", "1"); defines.add("NRD_USE_MATERIAL_ID", "0"); @@ -1130,18 +1130,7 @@ void NRDPass::dispatch(RenderContext* pRenderContext, const RenderData& renderDa pCommandList->Dispatch(dispatchDesc.gridWidth, dispatchDesc.gridHeight, 1); } -static void registerNRDPass(pybind11::module& m) -{ - pybind11::enum_ profile(m, "NRDMethod"); - profile.value("RelaxDiffuseSpecular", NRDPass::DenoisingMethod::RelaxDiffuseSpecular); - profile.value("RelaxDiffuse", NRDPass::DenoisingMethod::RelaxDiffuse); - profile.value("ReblurDiffuseSpecular", NRDPass::DenoisingMethod::ReblurDiffuseSpecular); - profile.value("SpecularReflectionMv", NRDPass::DenoisingMethod::SpecularReflectionMv); - profile.value("SpecularDeltaMv", NRDPass::DenoisingMethod::SpecularDeltaMv); -} - 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 947a51de8..0b17db58c 100644 --- a/Source/RenderPasses/NRDPass/NRDPass.h +++ b/Source/RenderPasses/NRDPass/NRDPass.h @@ -27,7 +27,8 @@ **************************************************************************/ #pragma once -#include +#include "Falcor.h" +#include "Core/Enum.h" #include "Core/API/Shared/D3D12DescriptorSet.h" #include "Core/API/Shared/D3D12RootSignature.h" #include "Core/API/Shared/D3D12ConstantBufferView.h" @@ -51,11 +52,19 @@ class NRDPass : public RenderPass SpecularDeltaMv }; - static ref create(ref pDevice, const Dictionary& dict) { return make_ref(pDevice, dict); } + FALCOR_ENUM_INFO(DenoisingMethod, { + { DenoisingMethod::RelaxDiffuseSpecular, "RelaxDiffuseSpecular" }, + { DenoisingMethod::RelaxDiffuse, "RelaxDiffuse" }, + { DenoisingMethod::ReblurDiffuseSpecular, "ReblurDiffuseSpecular" }, + { DenoisingMethod::SpecularReflectionMv, "SpecularReflectionMv" }, + { DenoisingMethod::SpecularDeltaMv, "SpecularDeltaMv" }, + }); - NRDPass(ref pDevice, const Dictionary& dict); + static ref create(ref pDevice, const Properties& props) { return make_ref(pDevice, props); } - virtual Dictionary getScriptingDictionary() override; + NRDPass(ref pDevice, const Properties& props); + + virtual Properties getProperties() const 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; @@ -106,3 +115,5 @@ class NRDPass : public RenderPass ref mpPackRadiancePassRelax; ref mpPackRadiancePassReblur; }; + +FALCOR_ENUM_REGISTER(NRDPass::DenoisingMethod); diff --git a/Source/RenderPasses/OptixDenoiser/OptixDenoiser.cpp b/Source/RenderPasses/OptixDenoiser/OptixDenoiser.cpp index cf4ecc8e5..47cbdf03e 100644 --- a/Source/RenderPasses/OptixDenoiser/OptixDenoiser.cpp +++ b/Source/RenderPasses/OptixDenoiser/OptixDenoiser.cpp @@ -27,6 +27,14 @@ **************************************************************************/ #include "OptixDenoiser.h" +FALCOR_ENUM_INFO(OptixDenoiserModelKind, { + { OptixDenoiserModelKind::OPTIX_DENOISER_MODEL_KIND_LDR, "LDR" }, + { OptixDenoiserModelKind::OPTIX_DENOISER_MODEL_KIND_HDR, "HDR" }, + { OptixDenoiserModelKind::OPTIX_DENOISER_MODEL_KIND_AOV, "AOV" }, + { OptixDenoiserModelKind::OPTIX_DENOISER_MODEL_KIND_TEMPORAL, "Temporal" }, +}); +FALCOR_ENUM_REGISTER(OptixDenoiserModelKind); + namespace { // Names for pass input and output textures @@ -53,12 +61,6 @@ static void regOptixDenoiser(pybind11::module& m) { pybind11::class_> pass(m, "OptixDenoiser"); pass.def_property(kEnabled, &OptixDenoiser_::getEnabled, &OptixDenoiser_::setEnabled); - - pybind11::enum_ model(m, "OptixDenoiserModel"); - model.value("LDR", OptixDenoiserModelKind::OPTIX_DENOISER_MODEL_KIND_LDR); - model.value("HDR", OptixDenoiserModelKind::OPTIX_DENOISER_MODEL_KIND_HDR); - model.value("AOV", OptixDenoiserModelKind::OPTIX_DENOISER_MODEL_KIND_AOV); - model.value("Temporal", OptixDenoiserModelKind::OPTIX_DENOISER_MODEL_KIND_TEMPORAL); } extern "C" FALCOR_API_EXPORT void registerPlugin(Falcor::PluginRegistry& registry) @@ -67,10 +69,10 @@ extern "C" FALCOR_API_EXPORT void registerPlugin(Falcor::PluginRegistry& registr ScriptBindings::registerBinding(regOptixDenoiser); } -OptixDenoiser_::OptixDenoiser_(ref pDevice, const Dictionary& dict) +OptixDenoiser_::OptixDenoiser_(ref pDevice, const Properties& props) : RenderPass(pDevice) { - for (const auto& [key, value] : dict) + for (const auto& [key, value] : props) { if (key == kEnabled) mEnabled = value; else if (key == kModel) @@ -80,7 +82,7 @@ OptixDenoiser_::OptixDenoiser_(ref pDevice, const Dictionary& dict) } else if (key == kBlend) mDenoiser.params.blendFactor = value; else if (key == kDenoiseAlpha) mDenoiser.params.denoiseAlpha = (value ? 1u : 0u); - else logWarning("Unknown field '{}' in a OptixDenoiser dictionary.", key); + else logWarning("Unknown property '{}' in a OptixDenoiser properties.", key); } mpConvertTexToBuf = ComputePass::create(mpDevice, kConvertTexToBufFile, "main"); @@ -90,16 +92,16 @@ OptixDenoiser_::OptixDenoiser_(ref pDevice, const Dictionary& dict) mpFbo = Fbo::create(mpDevice); } -Dictionary OptixDenoiser_::getScriptingDictionary() +Properties OptixDenoiser_::getProperties() const { - Dictionary d; + Properties props; - d[kEnabled] = mEnabled; - d[kBlend] = mDenoiser.params.blendFactor; - d[kModel] = mDenoiser.modelKind; - d[kDenoiseAlpha] = bool(mDenoiser.params.denoiseAlpha > 0); + props[kEnabled] = mEnabled; + props[kBlend] = mDenoiser.params.blendFactor; + props[kModel] = mDenoiser.modelKind; + props[kDenoiseAlpha] = bool(mDenoiser.params.denoiseAlpha > 0); - return d; + return props; } RenderPassReflection OptixDenoiser_::reflect(const CompileData& compileData) diff --git a/Source/RenderPasses/OptixDenoiser/OptixDenoiser.h b/Source/RenderPasses/OptixDenoiser/OptixDenoiser.h index 6cb279cf8..96e390b9a 100644 --- a/Source/RenderPasses/OptixDenoiser/OptixDenoiser.h +++ b/Source/RenderPasses/OptixDenoiser/OptixDenoiser.h @@ -64,6 +64,7 @@ #pragma once #include "Falcor.h" +#include "Core/Enum.h" #include "Core/Pass/FullScreenPass.h" #include "RenderGraph/RenderPass.h" @@ -78,11 +79,11 @@ class OptixDenoiser_ : public RenderPass public: FALCOR_PLUGIN_CLASS(OptixDenoiser_, "OptixDenoiser", "Apply the OptiX AI Denoiser."); - static ref create(ref pDevice, const Dictionary& dict) { return make_ref(pDevice, dict); } + static ref create(ref pDevice, const Properties& props) { return make_ref(pDevice, props); } - OptixDenoiser_(ref pDevice, const Dictionary& dict); + OptixDenoiser_(ref pDevice, const Properties& props); - virtual Dictionary getScriptingDictionary() override; + virtual Properties getProperties() const 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; diff --git a/Source/RenderPasses/OptixDenoiser/README.txt b/Source/RenderPasses/OptixDenoiser/README.txt index 5cd88e051..06d8a742c 100644 --- a/Source/RenderPasses/OptixDenoiser/README.txt +++ b/Source/RenderPasses/OptixDenoiser/README.txt @@ -42,8 +42,7 @@ By default, when connecting the OptiX denoiser to a render graph, it is: alpha is irrelevant. (d) Uses either the HDR or Temporal OptiX denoiser (depending if motion vectors are provided) - - Can be overridden in the Python file via 'model' : OptixDenoiserModel.LDR, OptixDenoiserModel.HDR, - or OptixDenoiserModel.Temporal + - Can be overridden in the Python file via 'model' : 'LDR', 'HDR', 'AOV' or 'Temporal' (e) Using the most guides possible. - If you pass in noisy color only, no guides are used. diff --git a/Source/RenderPasses/PathTracer/Params.slang b/Source/RenderPasses/PathTracer/Params.slang index dcf2509c3..43a241388 100644 --- a/Source/RenderPasses/PathTracer/Params.slang +++ b/Source/RenderPasses/PathTracer/Params.slang @@ -38,6 +38,12 @@ enum class ColorFormat : uint32_t LogLuvHDR = 1, }; +FALCOR_ENUM_INFO(ColorFormat, { + { ColorFormat::RGBA32F, "RGBA32F" }, + { ColorFormat::LogLuvHDR, "LogLuvHDR" }, +}); +FALCOR_ENUM_REGISTER(ColorFormat); + enum class MISHeuristic : uint32_t { Balance = 0, ///< Balance heuristic. @@ -45,6 +51,13 @@ enum class MISHeuristic : uint32_t PowerExp = 2, ///< Power heuristic (variable exponent). }; +FALCOR_ENUM_INFO(MISHeuristic, { + { MISHeuristic::Balance, "Balance" }, + { MISHeuristic::PowerTwo, "PowerTwo" }, + { MISHeuristic::PowerExp, "PowerExp" }, +}); +FALCOR_ENUM_REGISTER(MISHeuristic); + // Define tile sizes in pixels. // The frame is divided into tiles stored in scanline order, with pixels in tiles enumerated in Morton order. static const uint2 kScreenTileDim = { 16, 16 }; ///< Screen-tile dimension in pixels. diff --git a/Source/RenderPasses/PathTracer/PathTracer.cpp b/Source/RenderPasses/PathTracer/PathTracer.cpp index e37cb93b8..d8e9cce66 100644 --- a/Source/RenderPasses/PathTracer/PathTracer.cpp +++ b/Source/RenderPasses/PathTracer/PathTracer.cpp @@ -111,33 +111,6 @@ namespace { kOutputNRDResidualRadianceHitDist, "", "Output residual color (linear) and hit distance", true /* optional */, ResourceFormat::RGBA32Float }, }; - // UI variables. - const Gui::DropdownList kColorFormatList = - { - { (uint32_t)ColorFormat::RGBA32F, "RGBA32F (128bpp)" }, - { (uint32_t)ColorFormat::LogLuvHDR, "LogLuvHDR (32bpp)" }, - }; - - const Gui::DropdownList kMISHeuristicList = - { - { (uint32_t)MISHeuristic::Balance, "Balance heuristic" }, - { (uint32_t)MISHeuristic::PowerTwo, "Power heuristic (exp=2)" }, - { (uint32_t)MISHeuristic::PowerExp, "Power heuristic" }, - }; - - const Gui::DropdownList kEmissiveSamplerList = - { - { (uint32_t)EmissiveLightSamplerType::Uniform, "Uniform" }, - { (uint32_t)EmissiveLightSamplerType::LightBVH, "LightBVH" }, - { (uint32_t)EmissiveLightSamplerType::Power, "Power" }, - }; - - const Gui::DropdownList kLODModeList = - { - { (uint32_t)TexLODMode::Mip0, "Mip0" }, - { (uint32_t)TexLODMode::RayDiffs, "Ray Diffs" } - }; - // Scripting options. const std::string kSamplesPerPixel = "samplesPerPixel"; const std::string kMaxSurfaceBounces = "maxSurfaceBounces"; @@ -182,15 +155,6 @@ extern "C" FALCOR_API_EXPORT void registerPlugin(Falcor::PluginRegistry& registr void PathTracer::registerBindings(pybind11::module& m) { - pybind11::enum_ colorFormat(m, "ColorFormat"); - colorFormat.value("RGBA32F", ColorFormat::RGBA32F); - colorFormat.value("LogLuvHDR", ColorFormat::LogLuvHDR); - - pybind11::enum_ misHeuristic(m, "MISHeuristic"); - misHeuristic.value("Balance", MISHeuristic::Balance); - misHeuristic.value("PowerTwo", MISHeuristic::PowerTwo); - misHeuristic.value("PowerExp", MISHeuristic::PowerExp); - pybind11::class_> pass(m, "PathTracer"); pass.def_property_readonly("pixelStats", &PathTracer::getPixelStats); @@ -204,7 +168,7 @@ void PathTracer::registerBindings(pybind11::module& m) ); } -PathTracer::PathTracer(ref pDevice, const Dictionary& dict) +PathTracer::PathTracer(ref pDevice, const Properties& props) : RenderPass(pDevice) { if (!mpDevice->isShaderModelSupported(Device::ShaderModel::SM6_5)) @@ -216,7 +180,7 @@ PathTracer::PathTracer(ref pDevice, const Dictionary& dict) throw RuntimeError("PathTracer: Raytracing Tier 1.1 is not supported by the current device"); } - parseDictionary(dict); + parseProperties(props); validateOptions(); // Create sample generator. @@ -232,9 +196,9 @@ PathTracer::PathTracer(ref pDevice, const Dictionary& dict) mpPixelDebug = std::make_unique(mpDevice); } -void PathTracer::parseDictionary(const Dictionary& dict) +void PathTracer::parseProperties(const Properties& props) { - for (const auto& [key, value] : dict) + for (const auto& [key, value] : props) { // Rendering parameters if (key == kSamplesPerPixel) mStaticParams.samplesPerPixel = value; @@ -275,15 +239,15 @@ void PathTracer::parseDictionary(const Dictionary& dict) else if (key == kFixedOutputSize) mFixedOutputSize = value; else if (key == kColorFormat) mStaticParams.colorFormat = value; - else logWarning("Unknown field '{}' in PathTracer dictionary.", key); + else logWarning("Unknown property '{}' in PathTracer properties.", key); } - if (dict.keyExists(kMaxSurfaceBounces)) + if (props.has(kMaxSurfaceBounces)) { // Initialize bounce counts to 'maxSurfaceBounces' if they weren't explicitly set. - if (!dict.keyExists(kMaxDiffuseBounces)) mStaticParams.maxDiffuseBounces = mStaticParams.maxSurfaceBounces; - if (!dict.keyExists(kMaxSpecularBounces)) mStaticParams.maxSpecularBounces = mStaticParams.maxSurfaceBounces; - if (!dict.keyExists(kMaxTransmissionBounces)) mStaticParams.maxTransmissionBounces = mStaticParams.maxSurfaceBounces; + if (!props.has(kMaxDiffuseBounces)) mStaticParams.maxDiffuseBounces = mStaticParams.maxSurfaceBounces; + if (!props.has(kMaxSpecularBounces)) mStaticParams.maxSpecularBounces = mStaticParams.maxSurfaceBounces; + if (!props.has(kMaxTransmissionBounces)) mStaticParams.maxTransmissionBounces = mStaticParams.maxSurfaceBounces; } else { @@ -297,7 +261,7 @@ void PathTracer::parseDictionary(const Dictionary& dict) mStaticParams.maxSurfaceBounces < mStaticParams.maxTransmissionBounces; // Show a warning if maxSurfaceBounces will be adjusted in validateOptions(). - if (dict.keyExists(kMaxSurfaceBounces) && maxSurfaceBouncesNeedsAdjustment) + if (props.has(kMaxSurfaceBounces) && maxSurfaceBouncesNeedsAdjustment) { logWarning("'{}' is set lower than '{}', '{}' or '{}' and will be increased.", kMaxSurfaceBounces, kMaxDiffuseBounces, kMaxSpecularBounces, kMaxTransmissionBounces); } @@ -343,55 +307,55 @@ void PathTracer::validateOptions() } } -Dictionary PathTracer::getScriptingDictionary() +Properties PathTracer::getProperties() const { if (auto lightBVHSampler = dynamic_cast(mpEmissiveSampler.get())) { mLightBVHOptions = lightBVHSampler->getOptions(); } - Dictionary d; + Properties props; // Rendering parameters - d[kSamplesPerPixel] = mStaticParams.samplesPerPixel; - d[kMaxSurfaceBounces] = mStaticParams.maxSurfaceBounces; - d[kMaxDiffuseBounces] = mStaticParams.maxDiffuseBounces; - d[kMaxSpecularBounces] = mStaticParams.maxSpecularBounces; - d[kMaxTransmissionBounces] = mStaticParams.maxTransmissionBounces; + props[kSamplesPerPixel] = mStaticParams.samplesPerPixel; + props[kMaxSurfaceBounces] = mStaticParams.maxSurfaceBounces; + props[kMaxDiffuseBounces] = mStaticParams.maxDiffuseBounces; + props[kMaxSpecularBounces] = mStaticParams.maxSpecularBounces; + props[kMaxTransmissionBounces] = mStaticParams.maxTransmissionBounces; // Sampling parameters - d[kSampleGenerator] = mStaticParams.sampleGenerator; - if (mParams.useFixedSeed) d[kFixedSeed] = mParams.fixedSeed; - d[kUseBSDFSampling] = mStaticParams.useBSDFSampling; - d[kUseRussianRoulette] = mStaticParams.useRussianRoulette; - d[kUseNEE] = mStaticParams.useNEE; - d[kUseMIS] = mStaticParams.useMIS; - d[kMISHeuristic] = mStaticParams.misHeuristic; - d[kMISPowerExponent] = mStaticParams.misPowerExponent; - d[kEmissiveSampler] = mStaticParams.emissiveSampler; - if (mStaticParams.emissiveSampler == EmissiveLightSamplerType::LightBVH) d[kLightBVHOptions] = mLightBVHOptions; - d[kUseRTXDI] = mStaticParams.useRTXDI; - d[kRTXDIOptions] = mRTXDIOptions; + props[kSampleGenerator] = mStaticParams.sampleGenerator; + if (mParams.useFixedSeed) props[kFixedSeed] = mParams.fixedSeed; + props[kUseBSDFSampling] = mStaticParams.useBSDFSampling; + props[kUseRussianRoulette] = mStaticParams.useRussianRoulette; + props[kUseNEE] = mStaticParams.useNEE; + props[kUseMIS] = mStaticParams.useMIS; + props[kMISHeuristic] = mStaticParams.misHeuristic; + props[kMISPowerExponent] = mStaticParams.misPowerExponent; + props[kEmissiveSampler] = mStaticParams.emissiveSampler; + if (mStaticParams.emissiveSampler == EmissiveLightSamplerType::LightBVH) props[kLightBVHOptions] = mLightBVHOptions; + props[kUseRTXDI] = mStaticParams.useRTXDI; + props[kRTXDIOptions] = mRTXDIOptions; // Material parameters - d[kUseAlphaTest] = mStaticParams.useAlphaTest; - d[kAdjustShadingNormals] = mStaticParams.adjustShadingNormals; - d[kMaxNestedMaterials] = mStaticParams.maxNestedMaterials; - d[kUseLightsInDielectricVolumes] = mStaticParams.useLightsInDielectricVolumes; - d[kDisableCaustics] = mStaticParams.disableCaustics; - d[kSpecularRoughnessThreshold] = mParams.specularRoughnessThreshold; - d[kPrimaryLodMode] = mStaticParams.primaryLodMode; - d[kLODBias] = mParams.lodBias; + props[kUseAlphaTest] = mStaticParams.useAlphaTest; + props[kAdjustShadingNormals] = mStaticParams.adjustShadingNormals; + props[kMaxNestedMaterials] = mStaticParams.maxNestedMaterials; + props[kUseLightsInDielectricVolumes] = mStaticParams.useLightsInDielectricVolumes; + props[kDisableCaustics] = mStaticParams.disableCaustics; + props[kSpecularRoughnessThreshold] = mParams.specularRoughnessThreshold; + props[kPrimaryLodMode] = mStaticParams.primaryLodMode; + props[kLODBias] = mParams.lodBias; // Denoising parameters - d[kUseNRDDemodulation] = mStaticParams.useNRDDemodulation; + props[kUseNRDDemodulation] = mStaticParams.useNRDDemodulation; // Output parameters - d[kOutputSize] = mOutputSizeSelection; - if (mOutputSizeSelection == RenderPassHelpers::IOSize::Fixed) d[kFixedOutputSize] = mFixedOutputSize; - d[kColorFormat] = mStaticParams.colorFormat; + props[kOutputSize] = mOutputSizeSelection; + if (mOutputSizeSelection == RenderPassHelpers::IOSize::Fixed) props[kFixedOutputSize] = mFixedOutputSize; + props[kColorFormat] = mStaticParams.colorFormat; - return d; + return props; } RenderPassReflection PathTracer::reflect(const CompileData& compileData) @@ -577,7 +541,7 @@ bool PathTracer::renderRenderingUI(Gui::Widgets& widget) if (mStaticParams.useMIS) { - dirty |= widget.dropdown("MIS heuristic", kMISHeuristicList, reinterpret_cast(mStaticParams.misHeuristic)); + dirty |= widget.dropdown("MIS heuristic", mStaticParams.misHeuristic); if (mStaticParams.misHeuristic == MISHeuristic::PowerExp) { @@ -589,7 +553,7 @@ bool PathTracer::renderRenderingUI(Gui::Widgets& widget) { if (auto group = widget.group("Emissive sampler")) { - if (widget.dropdown("Emissive sampler", kEmissiveSamplerList, (uint32_t&)mStaticParams.emissiveSampler)) + if (widget.dropdown("Emissive sampler", mStaticParams.emissiveSampler)) { resetLighting(); dirty = true; @@ -631,7 +595,7 @@ bool PathTracer::renderRenderingUI(Gui::Widgets& widget) runtimeDirty |= widget.var("Specular roughness threshold", mParams.specularRoughnessThreshold, 0.f, 1.f); widget.tooltip("Specular reflection events are only classified as specular if the material's roughness value is equal or smaller than this threshold. Otherwise they are classified diffuse."); - dirty |= widget.dropdown("Primary LOD Mode", kLODModeList, reinterpret_cast(mStaticParams.primaryLodMode)); + dirty |= widget.dropdown("Primary LOD Mode", mStaticParams.primaryLodMode); widget.tooltip("Texture LOD mode at primary hit"); runtimeDirty |= widget.var("TexLOD bias", mParams.lodBias, -16.f, 16.f, 0.01f); @@ -650,13 +614,13 @@ bool PathTracer::renderRenderingUI(Gui::Widgets& widget) // Controls for output size. // When output size requirements change, we'll trigger a graph recompile to update the render pass I/O sizes. - if (widget.dropdown("Output size", RenderPassHelpers::kIOSizeList, (uint32_t&)mOutputSizeSelection)) requestRecompile(); + if (widget.dropdown("Output size", mOutputSizeSelection)) requestRecompile(); if (mOutputSizeSelection == RenderPassHelpers::IOSize::Fixed) { if (widget.var("Size in pixels", mFixedOutputSize, 32u, 16384u)) requestRecompile(); } - dirty |= widget.dropdown("Color format", kColorFormatList, (uint32_t&)mStaticParams.colorFormat); + dirty |= widget.dropdown("Color format", mStaticParams.colorFormat); widget.tooltip("Selects the color format used for internal per-sample color and denoiser buffers"); } @@ -698,7 +662,7 @@ bool PathTracer::onMouseEvent(const MouseEvent& mouseEvent) return mpPixelDebug->onMouseEvent(mouseEvent); } -PathTracer::TracePass::TracePass(ref pDevice, const std::string& name, const std::string& passDefine, const ref& 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 DefineList& defines, const Program::TypeConformanceList& globalTypeConformances) : name(name) , passDefine(passDefine) { @@ -764,7 +728,7 @@ PathTracer::TracePass::TracePass(ref pDevice, const std::string& name, c pProgram = RtProgram::create(pDevice, desc, defines); } -void PathTracer::TracePass::prepareProgram(ref pDevice, const Program::DefineList& defines) +void PathTracer::TracePass::prepareProgram(ref pDevice, const DefineList& defines) { FALCOR_ASSERT(pProgram != nullptr && pBindingTable != nullptr); pProgram->setDefines(defines); @@ -1371,9 +1335,9 @@ void PathTracer::resolvePass(RenderContext* pRenderContext, const RenderData& re mpResolvePass->execute(pRenderContext, { mParams.frameDim, 1u }); } -Program::DefineList PathTracer::StaticParams::getDefines(const PathTracer& owner) const +DefineList PathTracer::StaticParams::getDefines(const PathTracer& owner) const { - Program::DefineList defines; + DefineList defines; // Path tracer configuration. defines.add("SAMPLES_PER_PIXEL", (owner.mFixedSampleCount ? std::to_string(samplesPerPixel) : "0")); // 0 indicates a variable sample count diff --git a/Source/RenderPasses/PathTracer/PathTracer.h b/Source/RenderPasses/PathTracer/PathTracer.h index 3339c5078..a42a0878b 100644 --- a/Source/RenderPasses/PathTracer/PathTracer.h +++ b/Source/RenderPasses/PathTracer/PathTracer.h @@ -49,11 +49,11 @@ class PathTracer : public RenderPass public: FALCOR_PLUGIN_CLASS(PathTracer, "PathTracer", "Reference path tracer."); - static ref create(ref pDevice, const Dictionary& dict) { return make_ref(pDevice, dict); } + static ref create(ref pDevice, const Properties& props) { return make_ref(pDevice, props); } - PathTracer(ref pDevice, const Dictionary& dict); + PathTracer(ref pDevice, const Properties& props); - virtual Dictionary getScriptingDictionary() override; + virtual Properties getProperties() const override; virtual RenderPassReflection reflect(const CompileData& compileData) override; virtual void setScene(RenderContext* pRenderContext, const ref& pScene) override; virtual void execute(RenderContext* pRenderContext, const RenderData& renderData) override; @@ -74,11 +74,11 @@ class PathTracer : public RenderPass ref pBindingTable; ref pVars; - 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); + TracePass(ref pDevice, const std::string& name, const std::string& passDefine, const ref& pScene, const DefineList& defines, const Program::TypeConformanceList& globalTypeConformances); + void prepareProgram(ref pDevice, const DefineList& defines); }; - void parseDictionary(const Dictionary& dict); + void parseProperties(const Properties& props); void validateOptions(); void updatePrograms(); void setFrameDim(const uint2 frameDim); @@ -135,13 +135,13 @@ class PathTracer : public RenderPass // Denoising parameters bool useNRDDemodulation = true; ///< Global switch for NRD demodulation. - Program::DefineList getDefines(const PathTracer& owner) const; + DefineList getDefines(const PathTracer& owner) const; }; // Configuration PathTracerParams mParams; ///< Runtime path tracer parameters. StaticParams mStaticParams; ///< Static parameters. These are set as compile-time constants in the shaders. - LightBVHSampler::Options mLightBVHOptions; ///< Current options for the light BVH sampler. + mutable LightBVHSampler::Options mLightBVHOptions; ///< Current options for the light BVH sampler. RTXDI::Options mRTXDIOptions; ///< Current options for the RTXDI sampler. bool mEnabled = true; ///< Switch to enable/disable the path tracer. When disabled the pass outputs are cleared. diff --git a/Source/RenderPasses/PathTracer/PathTracer.slang b/Source/RenderPasses/PathTracer/PathTracer.slang index fe20a39f0..b1871d0b5 100644 --- a/Source/RenderPasses/PathTracer/PathTracer.slang +++ b/Source/RenderPasses/PathTracer/PathTracer.slang @@ -637,6 +637,9 @@ struct PathTracer u -= p[lightType]; } + lightType = {}; + pdf = {}; + return false; } diff --git a/Source/RenderPasses/PixelInspectorPass/PixelInspectorPass.cpp b/Source/RenderPasses/PixelInspectorPass/PixelInspectorPass.cpp index d48cf68c4..5d4f22630 100644 --- a/Source/RenderPasses/PixelInspectorPass/PixelInspectorPass.cpp +++ b/Source/RenderPasses/PixelInspectorPass/PixelInspectorPass.cpp @@ -54,7 +54,7 @@ namespace const char kOutputChannel[] = "gPixelDataBuffer"; } -PixelInspectorPass::PixelInspectorPass(ref pDevice, const Dictionary& dict) +PixelInspectorPass::PixelInspectorPass(ref pDevice, const Properties& props) : RenderPass(pDevice) { for (auto it : kInputChannels) @@ -356,7 +356,7 @@ void PixelInspectorPass::setScene(RenderContext* pRenderContext, const refgetShaderModules()); desc.addShaderLibrary(kShaderFile).csEntry("main"); desc.addTypeConformances(mpScene->getTypeConformances()); - desc.setCompilerFlags(Shader::CompilerFlags::TreatWarningsAsErrors); + desc.setCompilerFlags(Program::CompilerFlags::TreatWarningsAsErrors); mpProgram = ComputeProgram::create(mpDevice, desc, mpScene->getSceneDefines()); mpState->setProgram(mpProgram); diff --git a/Source/RenderPasses/PixelInspectorPass/PixelInspectorPass.h b/Source/RenderPasses/PixelInspectorPass/PixelInspectorPass.h index 485d57674..a9dbbd25c 100644 --- a/Source/RenderPasses/PixelInspectorPass/PixelInspectorPass.h +++ b/Source/RenderPasses/PixelInspectorPass/PixelInspectorPass.h @@ -41,9 +41,9 @@ class PixelInspectorPass : public RenderPass "Left-mouse click on a pixel to select it.\n" }); - static ref create(ref pDevice, const Dictionary& dict) { return make_ref(pDevice, dict); } + static ref create(ref pDevice, const Properties& props) { return make_ref(pDevice, props); } - PixelInspectorPass(ref pDevice, const Dictionary& dict); + PixelInspectorPass(ref pDevice, const Properties& props); virtual RenderPassReflection reflect(const CompileData& compileData) override; virtual void execute(RenderContext* pRenderContext, const RenderData& renderData) override; diff --git a/Source/RenderPasses/RTXDIPass/RTXDIPass.cpp b/Source/RenderPasses/RTXDIPass/RTXDIPass.cpp index 3d0556213..75f584e8e 100644 --- a/Source/RenderPasses/RTXDIPass/RTXDIPass.cpp +++ b/Source/RenderPasses/RTXDIPass/RTXDIPass.cpp @@ -69,18 +69,18 @@ extern "C" FALCOR_API_EXPORT void registerPlugin(Falcor::PluginRegistry& registr registry.registerClass(); } -RTXDIPass::RTXDIPass(ref pDevice, const Dictionary& dict) +RTXDIPass::RTXDIPass(ref pDevice, const Properties& props) : RenderPass(pDevice) { - parseDictionary(dict); + parseProperties(props); } -void RTXDIPass::parseDictionary(const Dictionary& dict) +void RTXDIPass::parseProperties(const Properties& props) { - for (const auto& [key, value] : dict) + for (const auto& [key, value] : props) { if (key == kOptions) mOptions = value; - else logWarning("Unknown field '{}' in RTXDIPass dictionary.", key); + else logWarning("Unknown property '{}' in RTXDIPass properties.", key); } } @@ -156,11 +156,11 @@ bool RTXDIPass::onMouseEvent(const MouseEvent& mouseEvent) return mpRTXDI ? mpRTXDI->getPixelDebug().onMouseEvent(mouseEvent) : false; } -Dictionary RTXDIPass::getScriptingDictionary() +Properties RTXDIPass::getProperties() const { - Dictionary d; - d[kOptions] = mOptions; - return d; + Properties props; + props[kOptions] = mOptions; + return props; } void RTXDIPass::compile(RenderContext* pRenderContext, const CompileData& compileData) diff --git a/Source/RenderPasses/RTXDIPass/RTXDIPass.h b/Source/RenderPasses/RTXDIPass/RTXDIPass.h index 050c3080b..8cfe0dede 100644 --- a/Source/RenderPasses/RTXDIPass/RTXDIPass.h +++ b/Source/RenderPasses/RTXDIPass/RTXDIPass.h @@ -53,11 +53,11 @@ class RTXDIPass : public RenderPass public: FALCOR_PLUGIN_CLASS(RTXDIPass, "RTXDIPass", {"Standalone pass for direct lighting using RTXDI."}) - static ref create(ref pDevice, const Dictionary& dict) { return make_ref(pDevice, dict); } + static ref create(ref pDevice, const Properties& props) { return make_ref(pDevice, props); } - RTXDIPass(ref pDevice, const Dictionary& dict); + RTXDIPass(ref pDevice, const Properties& props); - virtual Dictionary getScriptingDictionary() override; + virtual Properties getProperties() const 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; @@ -67,7 +67,7 @@ class RTXDIPass : public RenderPass virtual bool onMouseEvent(const MouseEvent& mouseEvent) override; private: - void parseDictionary(const Dictionary& dict); + void parseProperties(const Properties& props); void prepareSurfaceData(RenderContext* pRenderContext, const ref& pVBuffer); void finalShading(RenderContext* pRenderContext, const ref& pVBuffer, const RenderData& renderData); diff --git a/Source/RenderPasses/RenderPassTemplate/RenderPassTemplate.cpp b/Source/RenderPasses/RenderPassTemplate/RenderPassTemplate.cpp index 1f5647415..422521d93 100644 --- a/Source/RenderPasses/RenderPassTemplate/RenderPassTemplate.cpp +++ b/Source/RenderPasses/RenderPassTemplate/RenderPassTemplate.cpp @@ -32,14 +32,14 @@ extern "C" FALCOR_API_EXPORT void registerPlugin(Falcor::PluginRegistry& registr registry.registerClass(); } -RenderPassTemplate::RenderPassTemplate(ref pDevice, const Dictionary& dict) +RenderPassTemplate::RenderPassTemplate(ref pDevice, const Properties& props) : RenderPass(pDevice) { } -Dictionary RenderPassTemplate::getScriptingDictionary() +Properties RenderPassTemplate::getProperties() const { - return Dictionary(); + return {}; } RenderPassReflection RenderPassTemplate::reflect(const CompileData& compileData) diff --git a/Source/RenderPasses/RenderPassTemplate/RenderPassTemplate.h b/Source/RenderPasses/RenderPassTemplate/RenderPassTemplate.h index 69f14929c..2dc956b9f 100644 --- a/Source/RenderPasses/RenderPassTemplate/RenderPassTemplate.h +++ b/Source/RenderPasses/RenderPassTemplate/RenderPassTemplate.h @@ -36,11 +36,11 @@ class RenderPassTemplate : public RenderPass public: FALCOR_PLUGIN_CLASS(RenderPassTemplate, "RenderPassTemplate", "Insert pass description here."); - static ref create(ref pDevice, const Dictionary& dict) { return make_ref(pDevice, dict); } + static ref create(ref pDevice, const Properties& props) { return make_ref(pDevice, props); } - RenderPassTemplate(ref pDevice, const Dictionary& dict); + RenderPassTemplate(ref pDevice, const Properties& props); - virtual Dictionary getScriptingDictionary() override; + virtual Properties getProperties() const 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; diff --git a/Source/RenderPasses/SDFEditor/GUIPass.ps.slang b/Source/RenderPasses/SDFEditor/GUIPass.ps.slang index afb0e325f..9ae0fa61c 100644 --- a/Source/RenderPasses/SDFEditor/GUIPass.ps.slang +++ b/Source/RenderPasses/SDFEditor/GUIPass.ps.slang @@ -62,6 +62,8 @@ ParameterBlock gGUIPass; bool iBoundingBox(in float3 ro, in float3 rd, in float3 halfExtent, in float e, out float t) { + t = {}; + float2 boxT; if (!intersectRayAABB(ro, rd, -halfExtent, halfExtent, boxT)) { @@ -141,6 +143,8 @@ float4 traceSDFBoundingBox(Ray cameraRay, uint instanceID, float distanceToHitPo bool sdfIntersectPrimitive(float3 rayOrigin, float3 rayDir, float3 bbCenter, float3 bbExtent, SDF3DPrimitive primitive, out float t) { + t = {}; + float ilen = 1.f / length(rayDir); rayDir *= ilen; diff --git a/Source/RenderPasses/SDFEditor/SDFEditor.cpp b/Source/RenderPasses/SDFEditor/SDFEditor.cpp index b18c506fc..11d2aed26 100644 --- a/Source/RenderPasses/SDFEditor/SDFEditor.cpp +++ b/Source/RenderPasses/SDFEditor/SDFEditor.cpp @@ -107,7 +107,7 @@ void SDFEditor::registerBindings(pybind11::module& m) // None at the moment. } -SDFEditor::SDFEditor(ref pDevice, const Dictionary& dict) +SDFEditor::SDFEditor(ref pDevice, const Properties& props) : RenderPass(pDevice) { mpFbo = Fbo::create(mpDevice); @@ -180,9 +180,9 @@ void SDFEditor::fetchPreviousVBufferAndZBuffer(RenderContext* pRenderContext, re pRenderContext->copySubresourceRegion(mpEditingLinearZBuffer.get(), 0, pDepth.get(), pDepth->getSubresourceIndex(0, 0)); } -Dictionary SDFEditor::getScriptingDictionary() +Properties SDFEditor::getProperties() const { - return Dictionary(); + return {}; } void SDFEditor::setScene(RenderContext* pRenderContext, const ref& pScene) diff --git a/Source/RenderPasses/SDFEditor/SDFEditor.h b/Source/RenderPasses/SDFEditor/SDFEditor.h index 3c0048f9a..0de00f4d6 100644 --- a/Source/RenderPasses/SDFEditor/SDFEditor.h +++ b/Source/RenderPasses/SDFEditor/SDFEditor.h @@ -40,11 +40,11 @@ class SDFEditor : public RenderPass public: FALCOR_PLUGIN_CLASS(SDFEditor, "SDFEditor", "Signed distance function (SDF) editor"); - static ref create(ref pDevice, const Dictionary& dict) { return make_ref(pDevice, dict); } + static ref create(ref pDevice, const Properties& props) { return make_ref(pDevice, props); } - SDFEditor(ref pDevice, const Dictionary& dict); + SDFEditor(ref pDevice, const Properties& props); - virtual Dictionary getScriptingDictionary() override; + virtual Properties getProperties() const 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; diff --git a/Source/RenderPasses/SVGFPass/SVGFPass.cpp b/Source/RenderPasses/SVGFPass/SVGFPass.cpp index 99794d2e7..12c0cbc4b 100644 --- a/Source/RenderPasses/SVGFPass/SVGFPass.cpp +++ b/Source/RenderPasses/SVGFPass/SVGFPass.cpp @@ -78,10 +78,10 @@ extern "C" FALCOR_API_EXPORT void registerPlugin(Falcor::PluginRegistry& registr registry.registerClass(); } -SVGFPass::SVGFPass(ref pDevice, const Dictionary& dict) +SVGFPass::SVGFPass(ref pDevice, const Properties& props) : RenderPass(pDevice) { - for (const auto& [key, value] : dict) + for (const auto& [key, value] : props) { if (key == kEnabled) mFilterEnabled = value; else if (key == kIterations) mFilterIterations = value; @@ -91,7 +91,7 @@ SVGFPass::SVGFPass(ref pDevice, const Dictionary& dict) else if (key == kPhiNormal) mPhiNormal = value; else if (key == kAlpha) mAlpha = value; else if (key == kMomentsAlpha) mMomentsAlpha = value; - else logWarning("Unknown field '{}' in SVGFPass dictionary.", key); + else logWarning("Unknown property '{}' in SVGFPass properties.", key); } mpPackLinearZAndNormal = FullScreenPass::create(mpDevice, kPackLinearZAndNormalShader); @@ -102,18 +102,18 @@ SVGFPass::SVGFPass(ref pDevice, const Dictionary& dict) FALCOR_ASSERT(mpPackLinearZAndNormal && mpReprojection && mpAtrous && mpFilterMoments && mpFinalModulate); } -Dictionary SVGFPass::getScriptingDictionary() +Properties SVGFPass::getProperties() const { - Dictionary dict; - dict[kEnabled] = mFilterEnabled; - dict[kIterations] = mFilterIterations; - dict[kFeedbackTap] = mFeedbackTap; - dict[kVarianceEpsilon] = mVarainceEpsilon; - dict[kPhiColor] = mPhiColor; - dict[kPhiNormal] = mPhiNormal; - dict[kAlpha] = mAlpha; - dict[kMomentsAlpha] = mMomentsAlpha; - return dict; + Properties props; + props[kEnabled] = mFilterEnabled; + props[kIterations] = mFilterIterations; + props[kFeedbackTap] = mFeedbackTap; + props[kVarianceEpsilon] = mVarainceEpsilon; + props[kPhiColor] = mPhiColor; + props[kPhiNormal] = mPhiNormal; + props[kAlpha] = mAlpha; + props[kMomentsAlpha] = mMomentsAlpha; + return props; } /* diff --git a/Source/RenderPasses/SVGFPass/SVGFPass.h b/Source/RenderPasses/SVGFPass/SVGFPass.h index e002f6f2d..e979ee080 100644 --- a/Source/RenderPasses/SVGFPass/SVGFPass.h +++ b/Source/RenderPasses/SVGFPass/SVGFPass.h @@ -37,18 +37,17 @@ class SVGFPass : public RenderPass public: FALCOR_PLUGIN_CLASS(SVGFPass, "SVGFPass", "SVGF denoising pass."); - static ref create(ref pDevice, const Dictionary& dict) { return make_ref(pDevice, dict); } + static ref create(ref pDevice, const Properties& props) { return make_ref(pDevice, props); } - SVGFPass(ref pDevice, const Dictionary& dict); + SVGFPass(ref pDevice, const Properties& props); - virtual Dictionary getScriptingDictionary() override; + virtual Properties getProperties() const override; virtual RenderPassReflection reflect(const CompileData& compileData) override; virtual void execute(RenderContext* pRenderContext, const RenderData& renderData) override; virtual void compile(RenderContext* pRenderContext, const CompileData& compileData) override; virtual void renderUI(Gui::Widgets& widget) override; private: - bool init(const Dictionary& dict); void allocateFbos(uint2 dim, RenderContext* pRenderContext); void clearBuffers(RenderContext* pRenderContext, const RenderData& renderData); diff --git a/Source/RenderPasses/SceneDebugger/SceneDebugger.cpp b/Source/RenderPasses/SceneDebugger/SceneDebugger.cpp index 997b9da74..2ba1cfd55 100644 --- a/Source/RenderPasses/SceneDebugger/SceneDebugger.cpp +++ b/Source/RenderPasses/SceneDebugger/SceneDebugger.cpp @@ -34,31 +34,6 @@ namespace const std::string kOutput = "output"; - // UI elements - const Gui::DropdownList kModeList = - { - // Geometry - { (uint32_t)SceneDebuggerMode::HitType, "Hit type" }, - { (uint32_t)SceneDebuggerMode::InstanceID, "Instance ID" }, - { (uint32_t)SceneDebuggerMode::MaterialID, "Material ID" }, - { (uint32_t)SceneDebuggerMode::PrimitiveID, "Primitive ID" }, - { (uint32_t)SceneDebuggerMode::GeometryID, "Geometry ID" }, - { (uint32_t)SceneDebuggerMode::BlasID, "BLAS ID" }, - { (uint32_t)SceneDebuggerMode::InstancedGeometry, "Instanced geometry" }, - // Shading data - { (uint32_t)SceneDebuggerMode::FaceNormal, "Face normal" }, - { (uint32_t)SceneDebuggerMode::ShadingNormal, "Shading normal" }, - { (uint32_t)SceneDebuggerMode::ShadingTangent, "Shading tangent" }, - { (uint32_t)SceneDebuggerMode::ShadingBitangent, "Shading bitangent" }, - { (uint32_t)SceneDebuggerMode::FrontFacingFlag, "Front-facing flag" }, - { (uint32_t)SceneDebuggerMode::BackfacingShadingNormal, "Back-facing shading normal" }, - { (uint32_t)SceneDebuggerMode::TexCoords, "Texture coordinates" }, - // Material properties - { (uint32_t)SceneDebuggerMode::GuideNormal, "Guide normal" }, - { (uint32_t)SceneDebuggerMode::Roughness, "Roughness" }, - { (uint32_t)SceneDebuggerMode::FlatShaded, "Flat shaded" }, - }; - std::string getModeDesc(SceneDebuggerMode mode) { switch (mode) @@ -114,30 +89,11 @@ namespace void registerBindings(pybind11::module& m) { - pybind11::enum_ mode(m, "SceneDebuggerMode"); - // Geometry - mode.value("HitType", SceneDebuggerMode::HitType); - mode.value("InstanceID", SceneDebuggerMode::InstanceID); - mode.value("MaterialID", SceneDebuggerMode::MaterialID); - mode.value("PrimitiveID", SceneDebuggerMode::PrimitiveID); - mode.value("GeometryID", SceneDebuggerMode::GeometryID); - mode.value("BlasID", SceneDebuggerMode::BlasID); - mode.value("InstancedGeometry", SceneDebuggerMode::InstancedGeometry); - // Shading data - mode.value("FaceNormal", SceneDebuggerMode::FaceNormal); - mode.value("ShadingNormal", SceneDebuggerMode::ShadingNormal); - mode.value("ShadingTangent", SceneDebuggerMode::ShadingTangent); - mode.value("ShadingBitangent", SceneDebuggerMode::ShadingBitangent); - mode.value("FrontFacingFlag", SceneDebuggerMode::FrontFacingFlag); - mode.value("BackfacingShadingNormal", SceneDebuggerMode::BackfacingShadingNormal); - mode.value("TexCoords", SceneDebuggerMode::TexCoords); - // Material properties - mode.value("GuideNormal", SceneDebuggerMode::GuideNormal); - mode.value("Roughness", SceneDebuggerMode::Roughness); - mode.value("FlatShaded", SceneDebuggerMode::FlatShaded); - pybind11::class_> pass(m, "SceneDebugger"); - pass.def_property(kMode, &SceneDebugger::getMode, &SceneDebugger::setMode); + pass.def_property(kMode, + [](const SceneDebugger& self) { return enumToString(self.getMode()); }, + [](SceneDebugger& self, const std::string& value) {self.setMode(stringToEnum(value)); } + ); } } @@ -147,7 +103,7 @@ extern "C" FALCOR_API_EXPORT void registerPlugin(Falcor::PluginRegistry& registr Falcor::ScriptBindings::registerBinding(registerBindings); } -SceneDebugger::SceneDebugger(ref pDevice, const Dictionary& dict) +SceneDebugger::SceneDebugger(ref pDevice, const Properties& props) : RenderPass(pDevice) { if (!mpDevice->isFeatureSupported(Device::SupportedFeatures::RaytracingTier1_1)) @@ -156,22 +112,22 @@ SceneDebugger::SceneDebugger(ref pDevice, const Dictionary& dict) } // Parse dictionary. - for (const auto& [key, value] : dict) + for (const auto& [key, value] : props) { - if (key == kMode) mParams.mode = (uint32_t)value; + if (key == kMode) mParams.mode = (uint32_t)value.operator SceneDebuggerMode(); else if (key == kShowVolumes) mParams.showVolumes = value; - else logWarning("Unknown field '{}' in a SceneDebugger dictionary.", key); + else logWarning("Unknown property '{}' in a SceneDebugger properties.", key); } mpFence = GpuFence::create(mpDevice); } -Dictionary SceneDebugger::getScriptingDictionary() +Properties SceneDebugger::getProperties() const { - Dictionary d; - d[kMode] = SceneDebuggerMode(mParams.mode); - d[kShowVolumes] = mParams.showVolumes; - return d; + Properties props; + props[kMode] = SceneDebuggerMode(mParams.mode); + props[kShowVolumes] = mParams.showVolumes; + return props; } RenderPassReflection SceneDebugger::reflect(const CompileData& compileData) @@ -257,7 +213,7 @@ void SceneDebugger::execute(RenderContext* pRenderContext, const RenderData& ren void SceneDebugger::renderUI(Gui::Widgets& widget) { - widget.dropdown("Mode", kModeList, mParams.mode); + widget.dropdown("Mode", reinterpret_cast(mParams.mode)); widget.tooltip("Selects visualization mode"); widget.checkbox("Clamp to [0,1]", mParams.clamp); diff --git a/Source/RenderPasses/SceneDebugger/SceneDebugger.h b/Source/RenderPasses/SceneDebugger/SceneDebugger.h index f4a19ecda..7e680536d 100644 --- a/Source/RenderPasses/SceneDebugger/SceneDebugger.h +++ b/Source/RenderPasses/SceneDebugger/SceneDebugger.h @@ -42,11 +42,11 @@ class SceneDebugger : public RenderPass public: FALCOR_PLUGIN_CLASS(SceneDebugger, "SceneDebugger", "Scene debugger for identifying asset issues."); - static ref create(ref pDevice, const Dictionary& dict) { return make_ref(pDevice, dict); } + static ref create(ref pDevice, const Properties& props) { return make_ref(pDevice, props); } - SceneDebugger(ref pDevice, const Dictionary& dict); + SceneDebugger(ref pDevice, const Properties& props); - Dictionary getScriptingDictionary() override; + Properties getProperties() const override; RenderPassReflection reflect(const CompileData& compileData) override; void compile(RenderContext* pRenderContext, const CompileData& compileData) override; void setScene(RenderContext* pRenderContext, const ref& pScene) override; diff --git a/Source/RenderPasses/SceneDebugger/SharedTypes.slang b/Source/RenderPasses/SceneDebugger/SharedTypes.slang index 163cef696..a25ac4518 100644 --- a/Source/RenderPasses/SceneDebugger/SharedTypes.slang +++ b/Source/RenderPasses/SceneDebugger/SharedTypes.slang @@ -54,6 +54,30 @@ enum class SceneDebuggerMode : uint32_t FlatShaded, }; +FALCOR_ENUM_INFO(SceneDebuggerMode, { + // Geometry + { SceneDebuggerMode::HitType, "HitType" }, + { SceneDebuggerMode::InstanceID, "InstanceID" }, + { SceneDebuggerMode::MaterialID, "MaterialID" }, + { SceneDebuggerMode::GeometryID, "GeometryID" }, + { SceneDebuggerMode::BlasID, "BlasID" }, + { SceneDebuggerMode::InstancedGeometry, "InstancedGeometry" }, + { SceneDebuggerMode::PrimitiveID, "PrimitiveID" }, + // Shading data + { SceneDebuggerMode::FaceNormal, "FaceNormal" }, + { SceneDebuggerMode::ShadingNormal, "ShadingNormal" }, + { SceneDebuggerMode::ShadingTangent, "ShadingTangent" }, + { SceneDebuggerMode::ShadingBitangent, "ShadingBitangent" }, + { SceneDebuggerMode::FrontFacingFlag, "FrontFacingFlag" }, + { SceneDebuggerMode::BackfacingShadingNormal, "BackfacingShadingNormal" }, + { SceneDebuggerMode::TexCoords, "TexCoords" }, + // Material properties + { SceneDebuggerMode::GuideNormal, "GuideNormal" }, + { SceneDebuggerMode::Roughness, "Roughness" }, + { SceneDebuggerMode::FlatShaded, "FlatShaded" }, +}); +FALCOR_ENUM_REGISTER(SceneDebuggerMode); + struct SceneDebuggerParams { uint mode = (uint)SceneDebuggerMode::FaceNormal; ///< Current visualization mode. See SceneDebuggerMode. diff --git a/Source/RenderPasses/SimplePostFX/SimplePostFX.cpp b/Source/RenderPasses/SimplePostFX/SimplePostFX.cpp index facdf15d9..f31680b5b 100644 --- a/Source/RenderPasses/SimplePostFX/SimplePostFX.cpp +++ b/Source/RenderPasses/SimplePostFX/SimplePostFX.cpp @@ -80,11 +80,11 @@ extern "C" FALCOR_API_EXPORT void registerPlugin(Falcor::PluginRegistry& registr ScriptBindings::registerBinding(regSimplePostFX); } -SimplePostFX::SimplePostFX(ref pDevice, const Dictionary& dict) +SimplePostFX::SimplePostFX(ref pDevice, const Properties& props) : RenderPass(pDevice) { // Deserialize pass from dictionary. - for (const auto& [key, value] : dict) + for (const auto& [key, value] : props) { if (key == kEnabled) setEnabled(value); else if (key == kOutputSize) mOutputSizeSelection = value; @@ -103,7 +103,7 @@ SimplePostFX::SimplePostFX(ref pDevice, const Dictionary& dict) else if (key == kColorOffsetScalar) setColorOffsetScalar(value); else if (key == kColorScaleScalar) setColorScaleScalar(value); else if (key == kColorPowerScalar) setColorPowerScalar(value); - else logWarning("Unknown field '{}' in SimplePostFX dictionary.", key); + else logWarning("Unknown property '{}' in SimplePostFX properties.", key); } Sampler::Desc samplerDesc; @@ -111,33 +111,33 @@ SimplePostFX::SimplePostFX(ref pDevice, const Dictionary& dict) samplerDesc.setAddressingMode(Sampler::AddressMode::Border, Sampler::AddressMode::Border, Sampler::AddressMode::Border); mpLinearSampler = Sampler::create(mpDevice, samplerDesc); - Program::DefineList defines; + DefineList defines; mpDownsamplePass = ComputePass::create(mpDevice, kShaderFile, "downsample", defines); mpUpsamplePass = ComputePass::create(mpDevice, kShaderFile, "upsample", defines); mpPostFXPass = ComputePass::create(mpDevice, kShaderFile, "runPostFX", defines); } -Dictionary SimplePostFX::getScriptingDictionary() +Properties SimplePostFX::getProperties() const { - 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; + Properties props; + props[kEnabled] = getEnabled(); + props[kOutputSize] = mOutputSizeSelection; + if (mOutputSizeSelection == RenderPassHelpers::IOSize::Fixed) props[kFixedOutputSize] = mFixedOutputSize; + props[kWipe] = getWipe(); + props[kBloomAmount] = getBloomAmount(); + props[kStarAmount] = getStarAmount(); + props[kStarAngle] = getStarAngle(); + props[kVignetteAmount] = getVignetteAmount(); + props[kChromaticAberrationAmount] = getChromaticAberrationAmount(); + props[kBarrelDistortAmount] = getBarrelDistortAmount(); + props[kSaturationCurve] = getSaturationCurve(); + props[kColorOffset] = getColorOffset(); + props[kColorScale] = getColorScale(); + props[kColorPower] = getColorPower(); + props[kColorOffsetScalar] = getColorOffsetScalar(); + props[kColorScaleScalar] = getColorScaleScalar(); + props[kColorPowerScalar] = getColorPowerScalar(); + return props; } RenderPassReflection SimplePostFX::reflect(const CompileData& compileData) @@ -286,7 +286,7 @@ void SimplePostFX::renderUI(Gui::Widgets& widget) { // Controls for output size. // When output size requirements change, we'll trigger a graph recompile to update the render pass I/O sizes. - if (widget.dropdown("Output size", RenderPassHelpers::kIOSizeList, (uint32_t&)mOutputSizeSelection)) requestRecompile(); + if (widget.dropdown("Output size", mOutputSizeSelection)) requestRecompile(); if (mOutputSizeSelection == RenderPassHelpers::IOSize::Fixed) { if (widget.var("Size in pixels", mFixedOutputSize, 32u, 16384u)) requestRecompile(); diff --git a/Source/RenderPasses/SimplePostFX/SimplePostFX.h b/Source/RenderPasses/SimplePostFX/SimplePostFX.h index a08c67989..56db23657 100644 --- a/Source/RenderPasses/SimplePostFX/SimplePostFX.h +++ b/Source/RenderPasses/SimplePostFX/SimplePostFX.h @@ -37,11 +37,11 @@ class SimplePostFX : public RenderPass public: FALCOR_PLUGIN_CLASS(SimplePostFX, "SimplePostFX", "Simple set of post effects."); - static ref create(ref pDevice, const Dictionary& dict) { return make_ref(pDevice, dict); } + static ref create(ref pDevice, const Properties& props) { return make_ref(pDevice, props); } - SimplePostFX(ref pDevice, const Dictionary& dict); + SimplePostFX(ref pDevice, const Properties& props); - virtual Dictionary getScriptingDictionary() override; + virtual Properties getProperties() const 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; diff --git a/Source/RenderPasses/TAA/TAA.cpp b/Source/RenderPasses/TAA/TAA.cpp index a1dec3b7b..6d48e22af 100644 --- a/Source/RenderPasses/TAA/TAA.cpp +++ b/Source/RenderPasses/TAA/TAA.cpp @@ -54,7 +54,7 @@ extern "C" FALCOR_API_EXPORT void registerPlugin(Falcor::PluginRegistry& registr ScriptBindings::registerBinding(regTAA); } -TAA::TAA(ref pDevice, const Dictionary& dict) +TAA::TAA(ref pDevice, const Properties& props) : RenderPass(pDevice) { mpPass = FullScreenPass::create(mpDevice, kShaderFilename); @@ -63,22 +63,22 @@ TAA::TAA(ref pDevice, const Dictionary& dict) samplerDesc.setFilterMode(Sampler::Filter::Linear, Sampler::Filter::Linear, Sampler::Filter::Linear); mpLinearSampler = Sampler::create(mpDevice, samplerDesc); - for (const auto& [key, value] : dict) + for (const auto& [key, value] : props) { 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); + else logWarning("Unknown property '{}' in a TemporalAA properties.", key); } } -Dictionary TAA::getScriptingDictionary() +Properties TAA::getProperties() const { - Dictionary dict; - dict[kAlpha] = mControls.alpha; - dict[kColorBoxSigma] = mControls.colorBoxSigma; - dict[kAntiFlicker] = mControls.antiFlicker; - return dict; + Properties props; + props[kAlpha] = mControls.alpha; + props[kColorBoxSigma] = mControls.colorBoxSigma; + props[kAntiFlicker] = mControls.antiFlicker; + return props; } RenderPassReflection TAA::reflect(const CompileData& compileData) diff --git a/Source/RenderPasses/TAA/TAA.h b/Source/RenderPasses/TAA/TAA.h index abbb93d20..5b94b11e9 100644 --- a/Source/RenderPasses/TAA/TAA.h +++ b/Source/RenderPasses/TAA/TAA.h @@ -39,11 +39,11 @@ class TAA : public RenderPass public: FALCOR_PLUGIN_CLASS(TAA, "TAA", "Temporal Anti-Aliasing."); - static ref create(ref pDevice, const Dictionary& dict) { return make_ref(pDevice, dict); } + static ref create(ref pDevice, const Properties& props) { return make_ref(pDevice, props); } - TAA(ref pDevice, const Dictionary& dict); + TAA(ref pDevice, const Properties& props); - virtual Dictionary getScriptingDictionary() override; + virtual Properties getProperties() const override; virtual RenderPassReflection reflect(const CompileData& compileData) override; virtual void execute(RenderContext* pRenderContext, const RenderData& renderData) override; virtual void renderUI(Gui::Widgets& widget) override; diff --git a/Source/RenderPasses/TestPasses/TestPyTorchPass.cpp b/Source/RenderPasses/TestPasses/TestPyTorchPass.cpp index 2e34769f7..9bd8ab4d3 100644 --- a/Source/RenderPasses/TestPasses/TestPyTorchPass.cpp +++ b/Source/RenderPasses/TestPasses/TestPyTorchPass.cpp @@ -40,7 +40,7 @@ void TestPyTorchPass::registerScriptBindings(pybind11::module& m) pass.def("verifyData", &TestPyTorchPass::verifyData); } -TestPyTorchPass::TestPyTorchPass(ref pDevice, const Dictionary& dict) +TestPyTorchPass::TestPyTorchPass(ref pDevice, const Properties& props) : RenderPass(pDevice) { { @@ -74,10 +74,9 @@ TestPyTorchPass::~TestPyTorchPass() #endif } -Dictionary TestPyTorchPass::getScriptingDictionary() +Properties TestPyTorchPass::getProperties() const { - Dictionary dict; - return dict; + return {}; } RenderPassReflection TestPyTorchPass::reflect(const CompileData& compileData) diff --git a/Source/RenderPasses/TestPasses/TestPyTorchPass.h b/Source/RenderPasses/TestPasses/TestPyTorchPass.h index 512aefa5b..b2fa8dc58 100644 --- a/Source/RenderPasses/TestPasses/TestPyTorchPass.h +++ b/Source/RenderPasses/TestPasses/TestPyTorchPass.h @@ -42,12 +42,12 @@ class TestPyTorchPass : public RenderPass using PyTorchTensor = pybind11::ndarray; - static ref create(ref pDevice, const Dictionary& dict) { return make_ref(pDevice, dict); } + static ref create(ref pDevice, const Properties& props) { return make_ref(pDevice, props); } - TestPyTorchPass(ref pDevice, const Dictionary& dict); + TestPyTorchPass(ref pDevice, const Properties& props); virtual ~TestPyTorchPass(); - virtual Dictionary getScriptingDictionary() override; + virtual Properties getProperties() const 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 {} diff --git a/Source/RenderPasses/TestPasses/TestRtProgram.cpp b/Source/RenderPasses/TestPasses/TestRtProgram.cpp index 7d982efcd..1f9401e97 100644 --- a/Source/RenderPasses/TestPasses/TestRtProgram.cpp +++ b/Source/RenderPasses/TestPasses/TestRtProgram.cpp @@ -51,22 +51,22 @@ void TestRtProgram::registerScriptBindings(pybind11::module& m) pass.def("moveCustomPrimitive", &TestRtProgram::moveCustomPrimitive); } -TestRtProgram::TestRtProgram(ref pDevice, const Dictionary& dict) +TestRtProgram::TestRtProgram(ref pDevice, const Properties& props) : RenderPass(pDevice) { - for (const auto& [key, value] : dict) + for (const auto& [key, value] : props) { if (key == kMode) mMode = value; - else logWarning("Unknown field '{}' in TestRtProgram dictionary.", key); + else logWarning("Unknown property '{}' in TestRtProgram properties.", key); } if (mMode > 1) throw RuntimeError("mode has to be 0 or 1"); } -Dictionary TestRtProgram::getScriptingDictionary() +Properties TestRtProgram::getProperties() const { - Dictionary dict; - dict[kMode] = mMode; - return dict; + Properties props; + props[kMode] = mMode; + return props; } RenderPassReflection TestRtProgram::reflect(const CompileData& compileData) @@ -205,7 +205,7 @@ void TestRtProgram::sceneChanged() sbt->setMiss(0, desc.addMiss("miss0")); sbt->setMiss(1, desc.addMiss("miss1")); - Program::DefineList defines = mpScene->getSceneDefines(); + DefineList defines = mpScene->getSceneDefines(); defines.add("MODE", std::to_string(mMode)); // Create program and vars. diff --git a/Source/RenderPasses/TestPasses/TestRtProgram.h b/Source/RenderPasses/TestPasses/TestRtProgram.h index 5e9fa951f..01c581bdd 100644 --- a/Source/RenderPasses/TestPasses/TestRtProgram.h +++ b/Source/RenderPasses/TestPasses/TestRtProgram.h @@ -36,11 +36,11 @@ class TestRtProgram : public RenderPass public: FALCOR_PLUGIN_CLASS(TestRtProgram, "TestRtProgram", "Test pass for RtProgram."); - static ref create(ref pDevice, const Dictionary& dict) { return make_ref(pDevice, dict); } + static ref create(ref pDevice, const Properties& props) { return make_ref(pDevice, props); } - TestRtProgram(ref pDevice, const Dictionary& dict); + TestRtProgram(ref pDevice, const Properties& props); - virtual Dictionary getScriptingDictionary() override; + virtual Properties getProperties() const 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; diff --git a/Source/RenderPasses/ToneMapper/ToneMapper.cpp b/Source/RenderPasses/ToneMapper/ToneMapper.cpp index 553e29c2d..8fb136ce6 100644 --- a/Source/RenderPasses/ToneMapper/ToneMapper.cpp +++ b/Source/RenderPasses/ToneMapper/ToneMapper.cpp @@ -31,22 +31,6 @@ namespace { - const Gui::DropdownList kOperatorList = - { - { (uint32_t)ToneMapper::Operator::Linear, "Linear" }, - { (uint32_t)ToneMapper::Operator::Reinhard, "Reinhard" }, - { (uint32_t)ToneMapper::Operator::ReinhardModified, "Modified Reinhard" }, - { (uint32_t)ToneMapper::Operator::HejiHableAlu, "Heji's approximation" }, - { (uint32_t)ToneMapper::Operator::HableUc2, "Uncharted 2" }, - { (uint32_t)ToneMapper::Operator::Aces, "ACES" }, - }; - - const Gui::DropdownList kExposureModeList = - { - { (uint32_t)ToneMapper::ExposureMode::AperturePriority, "Aperture priority" }, - { (uint32_t)ToneMapper::ExposureMode::ShutterPriority, "Shutter priority" }, - }; - const char kSrc[] = "src"; const char kDst[] = "dst"; @@ -98,18 +82,6 @@ namespace static void regToneMapper(pybind11::module& m) { - 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); @@ -117,13 +89,19 @@ static void regToneMapper(pybind11::module& m) pass.def_property(kFilmSpeed, &ToneMapper::getFilmSpeed, &ToneMapper::setFilmSpeed); pass.def_property(kWhiteBalance, &ToneMapper::getWhiteBalance, &ToneMapper::setWhiteBalance); pass.def_property(kWhitePoint, &ToneMapper::getWhitePoint, &ToneMapper::setWhitePoint); - pass.def_property(kOperator, &ToneMapper::getOperator, &ToneMapper::setOperator); + pass.def_property(kOperator, + [](const ToneMapper& self) { return enumToString(self.getOperator()); }, + [](ToneMapper& self, const std::string& value) {self.setOperator(stringToEnum(value)); } + ); pass.def_property(kClamp, &ToneMapper::getClamp, &ToneMapper::setClamp); pass.def_property(kWhiteMaxLuminance, &ToneMapper::getWhiteMaxLuminance, &ToneMapper::setWhiteMaxLuminance); pass.def_property(kWhiteScale, &ToneMapper::getWhiteScale, &ToneMapper::setWhiteScale); pass.def_property(kFNumber, &ToneMapper::getFNumber, &ToneMapper::setFNumber); pass.def_property(kShutter, &ToneMapper::getShutter, &ToneMapper::setShutter); - pass.def_property(kExposureMode, &ToneMapper::getExposureMode, &ToneMapper::setExposureMode); + pass.def_property(kExposureMode, + [](const ToneMapper& self) { return enumToString(self.getExposureMode()); }, + [](ToneMapper& self, const std::string& value) {self.setExposureMode(stringToEnum(value)); } + ); } extern "C" FALCOR_API_EXPORT void registerPlugin(Falcor::PluginRegistry& registry) @@ -132,10 +110,10 @@ extern "C" FALCOR_API_EXPORT void registerPlugin(Falcor::PluginRegistry& registr ScriptBindings::registerBinding(regToneMapper); } -ToneMapper::ToneMapper(ref pDevice, const Dictionary& dict) +ToneMapper::ToneMapper(ref pDevice, const Properties& props) : RenderPass(pDevice) { - parseDictionary(dict); + parseProperties(props); createLuminancePass(); createToneMapPass(); @@ -149,9 +127,9 @@ ToneMapper::ToneMapper(ref pDevice, const Dictionary& dict) mpLinearSampler = Sampler::create(mpDevice, samplerDesc); } -void ToneMapper::parseDictionary(const Dictionary& dict) +void ToneMapper::parseProperties(const Properties& props) { - for (const auto& [key, value] : dict) + for (const auto& [key, value] : props) { if (key == kOutputSize) mOutputSizeSelection = value; else if (key == kOutputFormat) mOutputFormat = value; @@ -169,30 +147,30 @@ void ToneMapper::parseDictionary(const Dictionary& dict) else if (key == kFNumber) setFNumber(value); else if (key == kShutter) setShutter(value); else if (key == kExposureMode) setExposureMode(value); - else logWarning("Unknown field '{}' in a ToneMapping dictionary.", key); + else logWarning("Unknown property '{}' in a ToneMapping properties.", key); } } -Dictionary ToneMapper::getScriptingDictionary() +Properties ToneMapper::getProperties() const { - Dictionary d; - d[kOutputSize] = mOutputSizeSelection; - if (mOutputSizeSelection == RenderPassHelpers::IOSize::Fixed) d[kFixedOutputSize] = mFixedOutputSize; - if (mOutputFormat != ResourceFormat::Unknown) d[kOutputFormat] = mOutputFormat; - d[kUseSceneMetadata] = mUseSceneMetadata; - d[kExposureCompensation] = mExposureCompensation; - d[kAutoExposure] = mAutoExposure; - d[kFilmSpeed] = mFilmSpeed; - d[kWhiteBalance] = mWhiteBalance; - d[kWhitePoint] = mWhitePoint; - d[kOperator] = mOperator; - d[kClamp] = mClamp; - d[kWhiteMaxLuminance] = mWhiteMaxLuminance; - d[kWhiteScale] = mWhiteScale; - d[kFNumber] = mFNumber; - d[kShutter] = mShutter; - d[kExposureMode] = mExposureMode; - return d; + Properties props; + props[kOutputSize] = mOutputSizeSelection; + if (mOutputSizeSelection == RenderPassHelpers::IOSize::Fixed) props[kFixedOutputSize] = mFixedOutputSize; + if (mOutputFormat != ResourceFormat::Unknown) props[kOutputFormat] = mOutputFormat; + props[kUseSceneMetadata] = mUseSceneMetadata; + props[kExposureCompensation] = mExposureCompensation; + props[kAutoExposure] = mAutoExposure; + props[kFilmSpeed] = mFilmSpeed; + props[kWhiteBalance] = mWhiteBalance; + props[kWhitePoint] = mWhitePoint; + props[kOperator] = mOperator; + props[kClamp] = mClamp; + props[kWhiteMaxLuminance] = mWhiteMaxLuminance; + props[kWhiteScale] = mWhiteScale; + props[kFNumber] = mFNumber; + props[kShutter] = mShutter; + props[kExposureMode] = mExposureMode; + return props; } RenderPassReflection ToneMapper::reflect(const CompileData& compileData) @@ -308,7 +286,7 @@ void ToneMapper::renderUI(Gui::Widgets& widget) { // Controls for output size. // When output size requirements change, we'll trigger a graph recompile to update the render pass I/O sizes. - if (widget.dropdown("Output size", RenderPassHelpers::kIOSizeList, (uint32_t&)mOutputSizeSelection)) requestRecompile(); + if (widget.dropdown("Output size", mOutputSizeSelection)) requestRecompile(); if (mOutputSizeSelection == RenderPassHelpers::IOSize::Fixed) { if (widget.var("Size in pixels", mFixedOutputSize, 32u, 16384u)) requestRecompile(); @@ -322,10 +300,9 @@ void ToneMapper::renderUI(Gui::Widgets& widget) if (!mAutoExposure) { - uint32_t emIndex = static_cast(mExposureMode); - if (exposureGroup.dropdown("Exposure mode", kExposureModeList, emIndex)) + if (auto exposureMode = mExposureMode; exposureGroup.dropdown("Exposure mode", exposureMode)) { - setExposureMode(ExposureMode(emIndex)); + setExposureMode(exposureMode); } if (exposureGroup.var("Exposure Value (EV)", mExposureValue, kExposureValueMin, kExposureValueMax, 0.1f, false, "%.1f")) @@ -370,10 +347,9 @@ void ToneMapper::renderUI(Gui::Widgets& widget) if (auto tonemappingGroup = widget.group("Tonemapping", true)) { - uint32_t opIndex = static_cast(mOperator); - if (tonemappingGroup.dropdown("Operator", kOperatorList, opIndex)) + if (auto op = mOperator; tonemappingGroup.dropdown("Operator", op)) { - setOperator(Operator(opIndex)); + setOperator(op); } if (mOperator == Operator::ReinhardModified) @@ -513,7 +489,7 @@ void ToneMapper::createLuminancePass() void ToneMapper::createToneMapPass() { - Program::DefineList defines; + DefineList defines; defines.add("_TONE_MAPPER_OPERATOR", std::to_string(static_cast(mOperator))); if (mAutoExposure) defines.add("_TONE_MAPPER_AUTO_EXPOSURE"); if (mClamp) defines.add("_TONE_MAPPER_CLAMP"); diff --git a/Source/RenderPasses/ToneMapper/ToneMapper.h b/Source/RenderPasses/ToneMapper/ToneMapper.h index f29caf757..a5ec5f4e0 100644 --- a/Source/RenderPasses/ToneMapper/ToneMapper.h +++ b/Source/RenderPasses/ToneMapper/ToneMapper.h @@ -28,6 +28,7 @@ #pragma once #include "Falcor.h" #include "ToneMapperParams.slang" +#include "Core/Enum.h" #include "RenderGraph/RenderPass.h" #include "RenderGraph/RenderPassHelpers.h" #include "Core/Pass/FullScreenPass.h" @@ -49,11 +50,16 @@ class ToneMapper : public RenderPass ShutterPriority, // Keep shutter constant when modifying EV }; - static ref create(ref pDevice, const Dictionary& dict) { return make_ref(pDevice, dict); } + FALCOR_ENUM_INFO(ExposureMode, { + { ExposureMode::AperturePriority, "AperturePriority" }, + { ExposureMode::ShutterPriority, "ShutterPriority" }, + }); + + static ref create(ref pDevice, const Properties& props) { return make_ref(pDevice, props); } - ToneMapper(ref pDevice, const Dictionary& dict); + ToneMapper(ref pDevice, const Properties& props); - virtual Dictionary getScriptingDictionary() override; + virtual Properties getProperties() const override; virtual RenderPassReflection reflect(const CompileData& compileData) override; virtual void execute(RenderContext* pRenderContext, const RenderData& renderData) override; virtual void renderUI(Gui::Widgets& widget) override; @@ -89,7 +95,7 @@ class ToneMapper : public RenderPass ExposureMode getExposureMode() const { return mExposureMode; } private: - void parseDictionary(const Dictionary& dict); + void parseProperties(const Properties& props); void createToneMapPass(); void createLuminancePass(); @@ -138,3 +144,5 @@ class ToneMapper : public RenderPass ExposureMode mExposureMode = ExposureMode::AperturePriority; }; + +FALCOR_ENUM_REGISTER(ToneMapper::ExposureMode); diff --git a/Source/RenderPasses/ToneMapper/ToneMapperParams.slang b/Source/RenderPasses/ToneMapper/ToneMapperParams.slang index 18765369a..a2376f415 100644 --- a/Source/RenderPasses/ToneMapper/ToneMapperParams.slang +++ b/Source/RenderPasses/ToneMapper/ToneMapperParams.slang @@ -40,6 +40,16 @@ enum class ToneMapperOperator : uint32_t Aces, ///< Aces Filmic Tone-Mapping }; +FALCOR_ENUM_INFO(ToneMapperOperator, { + { ToneMapperOperator::Linear, "Linear" }, + { ToneMapperOperator::Reinhard, "Reinhard" }, + { ToneMapperOperator::ReinhardModified, "ReinhardModified" }, + { ToneMapperOperator::HejiHableAlu, "HejiHableAlu" }, + { ToneMapperOperator::HableUc2, "HableUc2" }, + { ToneMapperOperator::Aces, "Aces" }, +}); +FALCOR_ENUM_REGISTER(ToneMapperOperator); + /** Tone mapper parameters shared between host and device. Make sure struct layout follows the HLSL packing rules as it is uploaded as a memory blob. Do not use bool's as they are 1 byte in Visual Studio, 4 bytes in HLSL. diff --git a/Source/RenderPasses/Utils/Composite/Composite.cpp b/Source/RenderPasses/Utils/Composite/Composite.cpp index 328a57509..5c0fe690a 100644 --- a/Source/RenderPasses/Utils/Composite/Composite.cpp +++ b/Source/RenderPasses/Utils/Composite/Composite.cpp @@ -48,31 +48,31 @@ namespace }; } -Composite::Composite(ref pDevice, const Dictionary& dict) +Composite::Composite(ref pDevice, const Properties& props) : RenderPass(pDevice) { // Parse dictionary. - for (const auto& [key, value] : dict) + for (const auto& [key, value] : props) { if (key == kMode) mMode = value; else if (key == kScaleA) mScaleA = value; else if (key == kScaleB) mScaleB = value; else if (key == kOutputFormat) mOutputFormat = value; - else logWarning("Unknown field '{}' in Composite pass dictionary.", key); + else logWarning("Unknown property '{}' in Composite pass properties.", key); } // Create resources. - mCompositePass = ComputePass::create(mpDevice, kShaderFile, "main", Program::DefineList(), false); + mCompositePass = ComputePass::create(mpDevice, kShaderFile, "main", DefineList(), false); } -Dictionary Composite::getScriptingDictionary() +Properties Composite::getProperties() const { - Dictionary dict; - dict[kMode] = mMode; - dict[kScaleA] = mScaleA; - dict[kScaleB] = mScaleB; - if (mOutputFormat != ResourceFormat::Unknown) dict[kOutputFormat] = mOutputFormat; - return dict; + Properties props; + props[kMode] = mMode; + props[kScaleA] = mScaleA; + props[kScaleB] = mScaleB; + if (mOutputFormat != ResourceFormat::Unknown) props[kOutputFormat] = mOutputFormat; + return props; } RenderPassReflection Composite::reflect(const CompileData& compileData) @@ -116,12 +116,12 @@ void Composite::execute(RenderContext* pRenderContext, const RenderData& renderD void Composite::renderUI(Gui::Widgets& widget) { widget.text("This pass scales and composites inputs A and B together"); - widget.dropdown("Mode", kModeList, reinterpret_cast(mMode)); + widget.dropdown("Mode", mMode); widget.var("Scale A", mScaleA); widget.var("Scale B", mScaleB); } -Program::DefineList Composite::getDefines() const +DefineList Composite::getDefines() const { uint32_t compositeMode = 0; switch (mMode) @@ -152,16 +152,9 @@ Program::DefineList Composite::getDefines() const break; } - Program::DefineList defines; + DefineList defines; defines.add("COMPOSITE_MODE", std::to_string(compositeMode)); defines.add("OUTPUT_FORMAT", std::to_string(outputFormat)); return defines; } - -void Composite::registerBindings(pybind11::module& m) -{ - pybind11::enum_ compositeMode(m, "CompositeMode"); - compositeMode.value("Add", Composite::Mode::Add); - compositeMode.value("Multiply", Composite::Mode::Multiply); -} diff --git a/Source/RenderPasses/Utils/Composite/Composite.h b/Source/RenderPasses/Utils/Composite/Composite.h index b6eb10d8b..c20b9edf8 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 "Core/Enum.h" #include "RenderGraph/RenderPass.h" using namespace Falcor; @@ -51,20 +52,23 @@ class Composite : public RenderPass Multiply, }; - static ref create(ref pDevice, const Dictionary& dict) { return make_ref(pDevice, dict); } + FALCOR_ENUM_INFO(Mode, { + { Mode::Add, "Add" }, + { Mode::Multiply, "Multiply" }, + }); - Composite(ref pDevice, const Dictionary& dict); + static ref create(ref pDevice, const Properties& props) { return make_ref(pDevice, props); } - virtual Dictionary getScriptingDictionary() override; + Composite(ref pDevice, const Properties& props); + + virtual Properties getProperties() const 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; - static void registerBindings(pybind11::module& m); - private: - Program::DefineList getDefines() const; + DefineList getDefines() const; uint2 mFrameDim = { 0, 0 }; Mode mMode = Mode::Add; @@ -74,3 +78,5 @@ class Composite : public RenderPass ref mCompositePass; }; + +FALCOR_ENUM_REGISTER(Composite::Mode); diff --git a/Source/RenderPasses/Utils/CrossFade/CrossFade.cpp b/Source/RenderPasses/Utils/CrossFade/CrossFade.cpp index affa9ea9d..49b33b25a 100644 --- a/Source/RenderPasses/Utils/CrossFade/CrossFade.cpp +++ b/Source/RenderPasses/Utils/CrossFade/CrossFade.cpp @@ -43,33 +43,33 @@ namespace const std::string kFadeFactor = "fadeFactor"; } -CrossFade::CrossFade(ref pDevice, const Dictionary& dict) +CrossFade::CrossFade(ref pDevice, const Properties& props) : RenderPass(pDevice) { // Parse dictionary. - for (const auto& [key, value] : dict) + for (const auto& [key, value] : props) { 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); + else logWarning("Unknown property '{}' in CrossFade pass properties.", key); } // Create resources. mpFadePass = ComputePass::create(mpDevice, kShaderFile, "main"); } -Dictionary CrossFade::getScriptingDictionary() +Properties CrossFade::getProperties() const { - Dictionary dict; - if (mOutputFormat != ResourceFormat::Unknown) dict[kOutputFormat] = mOutputFormat; - dict[kEnableAutoFade] = mEnableAutoFade; - dict[kWaitFrameCount] = mWaitFrameCount; - dict[kFadeFrameCount] = mFadeFrameCount; - dict[kFadeFactor] = mFadeFactor; - return dict; + Properties props; + if (mOutputFormat != ResourceFormat::Unknown) props[kOutputFormat] = mOutputFormat; + props[kEnableAutoFade] = mEnableAutoFade; + props[kWaitFrameCount] = mWaitFrameCount; + props[kFadeFrameCount] = mFadeFrameCount; + props[kFadeFactor] = mFadeFactor; + return props; } RenderPassReflection CrossFade::reflect(const CompileData& compileData) diff --git a/Source/RenderPasses/Utils/CrossFade/CrossFade.h b/Source/RenderPasses/Utils/CrossFade/CrossFade.h index f23554baa..b0ad694ac 100644 --- a/Source/RenderPasses/Utils/CrossFade/CrossFade.h +++ b/Source/RenderPasses/Utils/CrossFade/CrossFade.h @@ -38,11 +38,11 @@ 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); } + static ref create(ref pDevice, const Properties& props) { return make_ref(pDevice, props); } - CrossFade(ref pDevice, const Dictionary& dict); + CrossFade(ref pDevice, const Properties& props); - virtual Dictionary getScriptingDictionary() override; + virtual Properties getProperties() const 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; diff --git a/Source/RenderPasses/Utils/GaussianBlur/GaussianBlur.cpp b/Source/RenderPasses/Utils/GaussianBlur/GaussianBlur.cpp index b1f6bce72..ba6422264 100644 --- a/Source/RenderPasses/Utils/GaussianBlur/GaussianBlur.cpp +++ b/Source/RenderPasses/Utils/GaussianBlur/GaussianBlur.cpp @@ -46,7 +46,7 @@ void GaussianBlur::registerBindings(pybind11::module& m) pass.def_property(kSigma, &GaussianBlur::getSigma, &GaussianBlur::setSigma); } -GaussianBlur::GaussianBlur(ref pDevice, const Dictionary& dict) +GaussianBlur::GaussianBlur(ref pDevice, const Properties& props) : RenderPass(pDevice) { mpFbo = Fbo::create(mpDevice); @@ -54,20 +54,20 @@ GaussianBlur::GaussianBlur(ref pDevice, const Dictionary& dict) 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, samplerDesc); - for (const auto& [key, value] : dict) + for (const auto& [key, value] : props) { if (key == kKernelWidth) mKernelWidth = value; else if (key == kSigma) mSigma = value; - else logWarning("Unknown field '{}' in a GaussianBlur dictionary.", key); + else logWarning("Unknown property '{}' in a GaussianBlur properties.", key); } } -Dictionary GaussianBlur::getScriptingDictionary() +Properties GaussianBlur::getProperties() const { - Dictionary dict; - dict[kKernelWidth] = mKernelWidth; - dict[kSigma] = mSigma; - return dict; + Properties props; + props[kKernelWidth] = mKernelWidth; + props[kSigma] = mSigma; + return props; } RenderPassReflection GaussianBlur::reflect(const CompileData& compileData) @@ -107,7 +107,7 @@ void GaussianBlur::compile(RenderContext* pRenderContext, const CompileData& com if (!mReady) throw RuntimeError("GaussianBlur: Missing incoming reflection information"); uint32_t arraySize = compileData.connectedResources.getField(kSrc)->getArraySize(); - Program::DefineList defines; + DefineList defines; defines.add("_KERNEL_WIDTH", std::to_string(mKernelWidth)); if (arraySize > 1) defines.add("_USE_TEX2D_ARRAY"); diff --git a/Source/RenderPasses/Utils/GaussianBlur/GaussianBlur.h b/Source/RenderPasses/Utils/GaussianBlur/GaussianBlur.h index c1eb3e2d7..30c7f955a 100644 --- a/Source/RenderPasses/Utils/GaussianBlur/GaussianBlur.h +++ b/Source/RenderPasses/Utils/GaussianBlur/GaussianBlur.h @@ -37,11 +37,11 @@ class GaussianBlur : public RenderPass public: FALCOR_PLUGIN_CLASS(GaussianBlur, "GaussianBlur", "Gaussian blur."); - static ref create(ref pDevice, const Dictionary& dict) { return make_ref(pDevice, dict); } + static ref create(ref pDevice, const Properties& props) { return make_ref(pDevice, props); } - GaussianBlur(ref pDevice, const Dictionary& dict); + GaussianBlur(ref pDevice, const Properties& props); - virtual Dictionary getScriptingDictionary() override; + virtual Properties getProperties() const 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; diff --git a/Source/RenderPasses/Utils/Utils.cpp b/Source/RenderPasses/Utils/Utils.cpp index 84ecdca84..3c4b6a5a5 100644 --- a/Source/RenderPasses/Utils/Utils.cpp +++ b/Source/RenderPasses/Utils/Utils.cpp @@ -34,7 +34,6 @@ extern "C" FALCOR_API_EXPORT void registerPlugin(Falcor::PluginRegistry& registr registry.registerClass(); registry.registerClass(); - ScriptBindings::registerBinding(Composite::registerBindings); registry.registerClass(); ScriptBindings::registerBinding(GaussianBlur::registerBindings); diff --git a/Source/RenderPasses/WhittedRayTracer/WhittedRayTracer.cpp b/Source/RenderPasses/WhittedRayTracer/WhittedRayTracer.cpp index c18ddb005..c67728005 100644 --- a/Source/RenderPasses/WhittedRayTracer/WhittedRayTracer.cpp +++ b/Source/RenderPasses/WhittedRayTracer/WhittedRayTracer.cpp @@ -40,26 +40,6 @@ namespace const char kRayDiffFilterMode[] = "rayDiffFilterMode"; const char kUseRoughnessToVariance[] = "useRoughnessToVariance"; - const Gui::DropdownList kTexLODModeList = - { - { uint32_t(TexLODMode::Mip0), "Mip0" }, - { uint32_t(TexLODMode::RayCones), "Ray cones" }, - { uint32_t(TexLODMode::RayDiffs), "Ray diffs" }, - }; - - const Gui::DropdownList kRayFootprintFilterModeList = - { - { uint32_t(RayFootprintFilterMode::Isotropic), "Isotropic" }, - { uint32_t(RayFootprintFilterMode::Anisotropic), "Anisotropic" }, - { uint32_t(RayFootprintFilterMode::AnisotropicWhenRefraction), "Anisotropic only for refraction" }, - }; - - const Gui::DropdownList kRayConeModeList = - { - { uint32_t(RayConeMode::Combo), "Combo" }, - { uint32_t(RayConeMode::Unified), "Unified" }, - }; - // Ray tracing settings that affect the traversal stack size. // These should be set as small as possible. const uint32_t kMaxPayloadSizeBytes = 164; @@ -93,22 +73,13 @@ extern "C" FALCOR_API_EXPORT void registerPlugin(Falcor::PluginRegistry& registr void WhittedRayTracer::registerBindings(pybind11::module& m) { pybind11::class_> pass(m, "WhittedRayTracer"); - - pybind11::enum_ rayConeMode(m, "RayConeMode"); - rayConeMode.value("Combo", RayConeMode::Combo); - rayConeMode.value("Unified", RayConeMode::Unified); - - pybind11::enum_ rayConeFilterMode(m, "RayFootprintFilterMode"); - rayConeFilterMode.value("Isotropic", RayFootprintFilterMode::Isotropic); - rayConeFilterMode.value("Anisotropic", RayFootprintFilterMode::Anisotropic); - rayConeFilterMode.value("AnisotropicWhenRefraction", RayFootprintFilterMode::AnisotropicWhenRefraction); } -WhittedRayTracer::WhittedRayTracer(ref pDevice, const Dictionary& dict) +WhittedRayTracer::WhittedRayTracer(ref pDevice, const Properties& props) : RenderPass(pDevice) { // Parse dictionary. - for (const auto& [key, value] : dict) + for (const auto& [key, value] : props) { if (key == kMaxBounces) mMaxBounces = (uint32_t)value; else if (key == kTexLODMode) mTexLODMode = value; @@ -116,7 +87,7 @@ WhittedRayTracer::WhittedRayTracer(ref pDevice, const Dictionary& dict) else if (key == kRayConeFilterMode) mRayConeFilterMode = value; else if (key == kRayDiffFilterMode) mRayDiffFilterMode = value; else if (key == kUseRoughnessToVariance) mUseRoughnessToVariance = value; - else logWarning("Unknown field '{}' in a WhittedRayTracer dictionary.", key); + else logWarning("Unknown property '{}' in a WhittedRayTracer properties.", key); } // Create a sample generator. @@ -124,16 +95,16 @@ WhittedRayTracer::WhittedRayTracer(ref pDevice, const Dictionary& dict) FALCOR_ASSERT(mpSampleGenerator); } -Dictionary WhittedRayTracer::getScriptingDictionary() +Properties WhittedRayTracer::getProperties() const { - Dictionary d; - d[kMaxBounces] = mMaxBounces; - d[kTexLODMode] = mTexLODMode; - d[kRayConeMode] = mRayConeMode; - d[kRayConeFilterMode] = mRayConeFilterMode; - d[kRayDiffFilterMode] = mRayDiffFilterMode; - d[kUseRoughnessToVariance] = mUseRoughnessToVariance; - return d; + Properties props; + props[kMaxBounces] = mMaxBounces; + props[kTexLODMode] = mTexLODMode; + props[kRayConeMode] = mRayConeMode; + props[kRayConeFilterMode] = mRayConeFilterMode; + props[kRayDiffFilterMode] = mRayDiffFilterMode; + props[kUseRoughnessToVariance] = mUseRoughnessToVariance; + return props; } RenderPassReflection WhittedRayTracer::reflect(const CompileData& compileData) @@ -220,27 +191,24 @@ 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 texLODModeIndex = static_cast(mTexLODMode); - if (widget.dropdown("Texture LOD mode", kTexLODModeList, texLODModeIndex)) + if (auto mode = mTexLODMode; widget.dropdown("Texture LOD mode", mode)) { - setTexLODMode(TexLODMode(texLODModeIndex)); + setTexLODMode(mode); dirty = true; } widget.tooltip("The texture level-of-detail mode to use."); if (mTexLODMode == TexLODMode::RayCones) { - uint32_t rayConeModeIndex = static_cast(mRayConeMode); - if (widget.dropdown("Ray cone mode", kRayConeModeList, rayConeModeIndex)) + if (auto mode = mRayConeMode; widget.dropdown("Ray cone mode", mode)) { - setRayConeMode(RayConeMode(rayConeModeIndex)); + setRayConeMode(mode); dirty = true; } widget.tooltip("The variant of ray cones to use."); - uint32_t rayConeFilterModeIndex = static_cast(mRayConeFilterMode); - if (widget.dropdown("Ray cone filter mode", kRayFootprintFilterModeList, rayConeFilterModeIndex)) + if (auto mode = mRayConeFilterMode; widget.dropdown("Ray cone filter mode", mode)) { - setRayConeFilterMode(RayFootprintFilterMode(rayConeFilterModeIndex)); + setRayConeFilterMode(mode); dirty = true; } widget.tooltip("What type of ray cone filter method to use beyond the first hit"); @@ -254,10 +222,9 @@ void WhittedRayTracer::renderUI(Gui::Widgets& widget) if (mTexLODMode == TexLODMode::RayDiffs) { - uint32_t rayDiffFilterModeIndex = static_cast(mRayDiffFilterMode); - if (widget.dropdown("Ray diff filter mode", kRayFootprintFilterModeList, rayDiffFilterModeIndex)) + if (auto mode = mRayDiffFilterMode; widget.dropdown("Ray diff filter mode", mode)) { - setRayDiffFilterMode(RayFootprintFilterMode(rayDiffFilterModeIndex)); + setRayDiffFilterMode(mode); dirty = true; } widget.tooltip("What type of ray diff filter method to use beyond the first hit"); @@ -330,7 +297,7 @@ void WhittedRayTracer::prepareVars() void WhittedRayTracer::setStaticParams(RtProgram* pProgram) const { - Program::DefineList defines; + DefineList defines; defines.add("MAX_BOUNCES", std::to_string(mMaxBounces)); defines.add("TEX_LOD_MODE", std::to_string(static_cast(mTexLODMode))); defines.add("RAY_CONE_MODE", std::to_string(static_cast(mRayConeMode))); diff --git a/Source/RenderPasses/WhittedRayTracer/WhittedRayTracer.h b/Source/RenderPasses/WhittedRayTracer/WhittedRayTracer.h index 36414de39..41862b0ec 100644 --- a/Source/RenderPasses/WhittedRayTracer/WhittedRayTracer.h +++ b/Source/RenderPasses/WhittedRayTracer/WhittedRayTracer.h @@ -48,11 +48,11 @@ class WhittedRayTracer : public RenderPass public: FALCOR_PLUGIN_CLASS(WhittedRayTracer, "WhittedRayTracer", "Simple Whitted ray tracer."); - static ref create(ref pDevice, const Dictionary& dict) { return make_ref(pDevice, dict); } + static ref create(ref pDevice, const Properties& props) { return make_ref(pDevice, props); } - WhittedRayTracer(ref pDevice, const Dictionary& dict); + WhittedRayTracer(ref pDevice, const Properties& props); - virtual Dictionary getScriptingDictionary() override; + virtual Properties getProperties() const override; virtual RenderPassReflection reflect(const CompileData& compileData) override; virtual void execute(RenderContext* pRenderContext, const RenderData& renderData) override; virtual void renderUI(Gui::Widgets& widget) override; diff --git a/Source/RenderPasses/WhittedRayTracer/WhittedRayTracerTypes.slang b/Source/RenderPasses/WhittedRayTracer/WhittedRayTracerTypes.slang index 209739f0e..61472bbe4 100644 --- a/Source/RenderPasses/WhittedRayTracer/WhittedRayTracerTypes.slang +++ b/Source/RenderPasses/WhittedRayTracer/WhittedRayTracerTypes.slang @@ -44,4 +44,11 @@ enum class RayFootprintFilterMode : uint32_t AnisotropicWhenRefraction = 2 }; +FALCOR_ENUM_INFO(RayFootprintFilterMode, { + { RayFootprintFilterMode::Isotropic, "Isotropic" }, + { RayFootprintFilterMode::Anisotropic, "Anisotropic" }, + { RayFootprintFilterMode::AnisotropicWhenRefraction, "AnisotropicWhenRefraction" }, +}); +FALCOR_ENUM_REGISTER(RayFootprintFilterMode); + END_NAMESPACE_FALCOR diff --git a/Source/Samples/CMakeLists.txt b/Source/Samples/CMakeLists.txt index 36bf5847a..79114e460 100644 --- a/Source/Samples/CMakeLists.txt +++ b/Source/Samples/CMakeLists.txt @@ -1,5 +1,6 @@ add_subdirectory(CudaInterop) add_subdirectory(HelloDXR) +add_subdirectory(MultiSampling) add_subdirectory(SampleAppTemplate) add_subdirectory(ShaderToy) add_subdirectory(Visualization2D) diff --git a/Source/Samples/MultiSampling/CMakeLists.txt b/Source/Samples/MultiSampling/CMakeLists.txt new file mode 100644 index 000000000..2c53fe92c --- /dev/null +++ b/Source/Samples/MultiSampling/CMakeLists.txt @@ -0,0 +1,11 @@ +add_falcor_executable(MultiSampling) + +target_sources(MultiSampling PRIVATE + MultiSampling.cpp + MultiSampling.h + MultiSampling.3d.slang +) + +target_copy_shaders(MultiSampling Samples/MultiSampling) + +target_source_group(MultiSampling "Samples") diff --git a/Source/Falcor/Rendering/Materials/TexLODTypes.cpp b/Source/Samples/MultiSampling/MultiSampling.3d.slang similarity index 82% rename from Source/Falcor/Rendering/Materials/TexLODTypes.cpp rename to Source/Samples/MultiSampling/MultiSampling.3d.slang index 3469be87e..3b7f8be95 100644 --- a/Source/Falcor/Rendering/Materials/TexLODTypes.cpp +++ b/Source/Samples/MultiSampling/MultiSampling.3d.slang @@ -25,16 +25,25 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **************************************************************************/ -#include "TexLODTypes.slang" -#include "Utils/Scripting/ScriptBindings.h" -namespace Falcor +struct VSIn { - FALCOR_SCRIPT_BINDING(TexLODTypes) - { - pybind11::enum_ texLODMode(m, "TexLODMode"); - texLODMode.value("Mip0", TexLODMode::Mip0); - texLODMode.value("RayCones", TexLODMode::RayCones); - texLODMode.value("RayDiffs", TexLODMode::RayDiffs); - } + float2 pos : POSITION; +}; + +struct VSOut +{ + float4 pos : SV_POSITION; +}; + +VSOut vsMain(VSIn vIn) +{ + VSOut vOut; + vOut.pos = float4(vIn.pos, 0.f, 1.f); + return vOut; +} + +float4 psMain(VSOut vsOut, uint triangleIndex: SV_PrimitiveID) : SV_TARGET +{ + return float4(0.5f); } diff --git a/Source/Samples/MultiSampling/MultiSampling.cpp b/Source/Samples/MultiSampling/MultiSampling.cpp new file mode 100644 index 000000000..eca2c76e4 --- /dev/null +++ b/Source/Samples/MultiSampling/MultiSampling.cpp @@ -0,0 +1,99 @@ +/*************************************************************************** + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. + # + # Redistribution and use in source and binary forms, with or without + # modification, are permitted provided that the following conditions + # are met: + # * Redistributions of source code must retain the above copyright + # notice, this list of conditions and the following disclaimer. + # * Redistributions in binary form must reproduce the above copyright + # notice, this list of conditions and the following disclaimer in the + # documentation and/or other materials provided with the distribution. + # * Neither the name of NVIDIA CORPORATION nor the names of its + # contributors may be used to endorse or promote products derived + # from this software without specific prior written permission. + # + # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY + # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + **************************************************************************/ +#include "MultiSampling.h" + +namespace +{ +const uint32_t kTriangleCount = 16; +const uint32_t kSampleCount = 8; +} // namespace + +MultiSampling::MultiSampling(const SampleAppConfig& config) : SampleApp(config) {} + +MultiSampling::~MultiSampling() {} + +void MultiSampling::onLoad(RenderContext* pRenderContext) +{ + // Load program + mpRasterPass = RasterPass::create(getDevice(), "Samples/MultiSampling/MultiSampling.3d.slang", "vsMain", "psMain"); + + // Create disk triangles + float2 vertices[kTriangleCount * 3]; + for (uint32_t i = 0; i < kTriangleCount; ++i) + { + float theta0 = float(i) / kTriangleCount * M_2PI; + float theta1 = float(i + 1) / kTriangleCount * M_2PI; + vertices[i * 3 + 0] = float2(0, 0); + vertices[i * 3 + 1] = float2(cos(theta0), sin(theta0)) * 0.75f; + vertices[i * 3 + 2] = float2(cos(theta1), sin(theta1)) * 0.75f; + } + auto vertexBuffer = Buffer::createTyped( + getDevice(), kTriangleCount * 3, ResourceBindFlags::ShaderResource | ResourceBindFlags::Vertex, Buffer::CpuAccess::None, vertices + ); + + // Create vertex layout + auto bufferLayout = VertexBufferLayout::create(); + bufferLayout->addElement("POSITION", 0, ResourceFormat::RG32Float, 1, 0); + auto layout = VertexLayout::create(); + layout->addBufferLayout(0, bufferLayout); + + // Create VAO + mpVao = Vao::create(Vao::Topology::TriangleList, layout, {vertexBuffer}); + + // Create FBO + mpFbo = Fbo::create(getDevice()); + ref tex = Texture::create2DMS( + getDevice(), 128, 128, ResourceFormat::RGBA32Float, kSampleCount, 1, + Resource::BindFlags::ShaderResource | Resource::BindFlags::RenderTarget + ); + mpFbo->attachColorTarget(tex, 0); +} + +void MultiSampling::onFrameRender(RenderContext* pRenderContext, const ref& pTargetFbo) +{ + pRenderContext->clearFbo(mpFbo.get(), float4(0.f), 0.f, 0); + + mpRasterPass->getState()->setFbo(mpFbo); + mpRasterPass->getState()->setVao(mpVao); + mpRasterPass->draw(pRenderContext, kTriangleCount * 3, 0); + + pRenderContext->blit(mpFbo->getColorTexture(0)->getSRV(), pTargetFbo->getRenderTargetView(0)); +} + +int main(int argc, char** argv) +{ + SampleAppConfig config; + config.windowDesc.width = 1024; + config.windowDesc.height = 1024; + config.windowDesc.resizableWindow = true; + config.windowDesc.enableVSync = true; + config.windowDesc.title = "Falcor multi-sampling example"; + + MultiSampling multiSample(config); + return multiSample.run(); +} diff --git a/Source/Tools/FalcorTest/FalcorTest.h b/Source/Samples/MultiSampling/MultiSampling.h similarity index 83% rename from Source/Tools/FalcorTest/FalcorTest.h rename to Source/Samples/MultiSampling/MultiSampling.h index 0586c29e6..f00e85433 100644 --- a/Source/Tools/FalcorTest/FalcorTest.h +++ b/Source/Samples/MultiSampling/MultiSampling.h @@ -28,26 +28,21 @@ #pragma once #include "Falcor.h" #include "Core/SampleApp.h" -#include "Testing/UnitTest.h" +#include "Core/Pass/RasterPass.h" using namespace Falcor; -class FalcorTest : public SampleApp +class MultiSampling : public SampleApp { public: - struct Options - { - UnitTestCategoryFlags categoryFlags = UnitTestCategoryFlags::All; - std::string filter; - std::filesystem::path xmlReportPath; - uint32_t repeat = 1; - }; - - FalcorTest(const SampleAppConfig& config, const Options& options); - ~FalcorTest(); + MultiSampling(const SampleAppConfig& config); + ~MultiSampling(); + void onLoad(RenderContext* pRenderContext) override; void onFrameRender(RenderContext* pRenderContext, const ref& pTargetFbo) override; private: - Options mOptions; + ref mpRasterPass; + ref mpVao; + ref mpFbo; }; diff --git a/Source/Tools/FalcorTest/CMakeLists.txt b/Source/Tools/FalcorTest/CMakeLists.txt index dddfbe331..6891b7412 100644 --- a/Source/Tools/FalcorTest/CMakeLists.txt +++ b/Source/Tools/FalcorTest/CMakeLists.txt @@ -2,16 +2,19 @@ add_falcor_executable(FalcorTest) target_sources(FalcorTest PRIVATE FalcorTest.cpp - FalcorTest.h + Tests/Core/AftermathTests.cpp + Tests/Core/AftermathTests.cs.slang Tests/Core/BufferAccessTests.cpp Tests/Core/BufferAccessTests.cs.slang Tests/Core/BufferTests.cpp Tests/Core/BufferTests.cs.slang Tests/Core/ConstantBufferTests.cpp Tests/Core/ConstantBufferTests.cs.slang + Tests/Core/CoreTests.cpp Tests/Core/DDSReadTests.cpp Tests/Core/DDSReadTests.cs.slang + Tests/Core/EnumTests.cpp Tests/Core/LargeBuffer.cpp Tests/Core/LargeBuffer.cs.slang Tests/Core/ObjectTests.cpp @@ -146,6 +149,7 @@ target_sources(FalcorTest PRIVATE Tests/Utils/ParallelReductionTests.cpp Tests/Utils/PathResolvingTests.cpp Tests/Utils/PrefixSumTests.cpp + Tests/Utils/PropertiesTests.cpp Tests/Utils/QuaternionTests.cpp Tests/Utils/RectangleTests.cpp Tests/Utils/SettingsTests.cpp diff --git a/Source/Tools/FalcorTest/FalcorTest.cpp b/Source/Tools/FalcorTest/FalcorTest.cpp index d80bf0461..6094f3b4b 100644 --- a/Source/Tools/FalcorTest/FalcorTest.cpp +++ b/Source/Tools/FalcorTest/FalcorTest.cpp @@ -25,7 +25,6 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **************************************************************************/ -#include "FalcorTest.h" #include "Utils/StringUtils.h" #include "Testing/UnitTest.h" @@ -35,32 +34,30 @@ #include #include -FALCOR_EXPORT_D3D12_AGILITY_SDK - -FalcorTest::FalcorTest(const SampleAppConfig& config, const Options& options) : SampleApp(config), mOptions(options) {} +using namespace Falcor; -FalcorTest::~FalcorTest() {} - -void FalcorTest::onFrameRender(RenderContext* pRenderContext, const ref& pTargetFbo) -{ - int returnCode = - runTests(getDevice(), getTargetFbo().get(), mOptions.categoryFlags, mOptions.filter, mOptions.xmlReportPath, mOptions.repeat); - shutdown(returnCode); -} +FALCOR_EXPORT_D3D12_AGILITY_SDK int main(int argc, char** argv) { args::ArgumentParser parser("Falcor unit tests."); parser.helpParams.programName = "FalcorTest"; args::HelpFlag helpFlag(parser, "help", "Display this help menu.", {'h', "help"}); - args::ValueFlag categoryFlag(parser, "all,cpu,gpu", "Test categories to run (default: all).", {'c', "category"}); + args::ValueFlag parallelFlag(parser, "N", "EXPERIMENTAL: Number of worker threads (default: 1).", {'p', "parallel"}); args::ValueFlag deviceTypeFlag(parser, "d3d12|vulkan", "Graphics device type.", {'d', "device-type"}); args::Flag listGPUsFlag(parser, "", "List available GPUs", {"list-gpus"}); args::ValueFlag gpuFlag(parser, "index", "Select specific GPU to use", {"gpu"}); - args::ValueFlag filterFlag(parser, "filter", "Regular expression for filtering tests to run.", {'f', "filter"}); + args::Flag listTestSuites(parser, "", "List test suites", {"list-test-suites"}); + args::Flag listTestCases(parser, "", "List test cases", {"list-test-cases"}); + args::Flag listTags(parser, "", "List tags", {"list-tags"}); + args::ValueFlag testSuiteFilterFlag(parser, "regex", "Filter test suites to run.", {'s', "test-suite"}); + args::ValueFlag testCaseFilterFlag(parser, "regex", "Filter test cases to run.", {'f', "test-case"}); + args::ValueFlag tagFilterFlag(parser, "tags", "Filter test cases by tags.", {'t', "tags"}); args::ValueFlag xmlReportFlag(parser, "path", "XML report output file.", {'x', "xml-report"}); args::ValueFlag repeatFlag(parser, "N", "Number of times to repeat the test.", {'r', "repeat"}); args::Flag enableDebugLayerFlag(parser, "", "Enable debug layer (enabled by default in Debug build).", {"enable-debug-layer"}); + args::Flag enableAftermathFlag(parser, "", "Enable Aftermath GPU crash dump.", {"enable-aftermath"}); + args::CompletionFlag completionFlag(parser, {"complete"}); try @@ -90,15 +87,14 @@ int main(int argc, char** argv) return 1; } - SampleAppConfig config; - config.headless = true; + unittest::RunOptions options; if (deviceTypeFlag) { if (args::get(deviceTypeFlag) == "d3d12") - config.deviceDesc.type = Device::Type::D3D12; + options.deviceDesc.type = Device::Type::D3D12; else if (args::get(deviceTypeFlag) == "vulkan") - config.deviceDesc.type = Device::Type::Vulkan; + options.deviceDesc.type = Device::Type::Vulkan; else { std::cerr << "Invalid device type, use 'd3d12' or 'vulkan'" << std::endl; @@ -107,47 +103,70 @@ int main(int argc, char** argv) } if (listGPUsFlag) { - const auto gpus = Device::getGPUs(config.deviceDesc.type); + const auto gpus = Device::getGPUs(options.deviceDesc.type); for (size_t i = 0; i < gpus.size(); ++i) fmt::print("GPU {}: {}\n", i, gpus[i].name); return 0; } + if (gpuFlag) - config.deviceDesc.gpu = args::get(gpuFlag); + options.deviceDesc.gpu = args::get(gpuFlag); if (enableDebugLayerFlag) - config.deviceDesc.enableDebugLayer = true; - - FalcorTest::Options options; + options.deviceDesc.enableDebugLayer = true; + if (enableAftermathFlag) + options.deviceDesc.enableAftermath = true; - if (categoryFlag) - { - options.categoryFlags = UnitTestCategoryFlags::None; - std::vector tokens = splitString(args::get(categoryFlag), ","); - for (const auto& token : tokens) - { - if (token == "all") - options.categoryFlags |= UnitTestCategoryFlags::All; - else if (token == "cpu") - options.categoryFlags |= UnitTestCategoryFlags::CPU; - else if (token == "gpu") - options.categoryFlags |= UnitTestCategoryFlags::GPU; - else - { - std::cerr << "Invalid test category '" << token << "'" << std::endl; - return 1; - } - } - } - if (filterFlag) - options.filter = args::get(filterFlag); + if (testSuiteFilterFlag) + options.testSuiteFilter = args::get(testSuiteFilterFlag); + if (testCaseFilterFlag) + options.testCaseFilter = args::get(testCaseFilterFlag); + if (tagFilterFlag) + options.tagFilter = args::get(tagFilterFlag); if (xmlReportFlag) options.xmlReportPath = args::get(xmlReportFlag); + if (parallelFlag) + options.parallel = args::get(parallelFlag); if (repeatFlag) options.repeat = args::get(repeatFlag); - // Disable logging to console, we don't want to clutter the test runner output with log messages. - Logger::setOutputs(Logger::OutputFlags::File | Logger::OutputFlags::DebugWindow); + if (listTestSuites || listTestCases || listTags) + { + std::vector tests = unittest::enumerateTests(); + tests = unittest::filterTests(tests, options.testSuiteFilter, options.testCaseFilter, options.tagFilter, options.deviceDesc.type); - FalcorTest falcorTest(config, options); - return falcorTest.run(); + if (listTestSuites) + { + std::set suites; + for (const auto& test : tests) + suites.insert(test.suiteName); + for (const auto& suite : suites) + fmt::print("{}\n", suite); + return 0; + } + if (listTestCases) + { + for (const auto& test : tests) + fmt::print("{}:{}\n", test.suiteName, test.name); + return 0; + } + if (listTags) + { + std::set tags; + for (const auto& test : tests) + for (const auto& tag : test.tags) + tags.insert(tag); + for (const auto& tag : tags) + fmt::print("{}\n", tag); + return 0; + } + } + + try + { + return unittest::runTests(options); + } + catch (const std::exception& e) + { + reportFatalError("FalcorTest crashed unexpectedly...\n" + std::string(e.what()), false); + } } diff --git a/Source/Falcor/Core/API/Common.cpp b/Source/Tools/FalcorTest/Tests/Core/AftermathTests.cpp similarity index 69% rename from Source/Falcor/Core/API/Common.cpp rename to Source/Tools/FalcorTest/Tests/Core/AftermathTests.cpp index 31776a112..14366661b 100644 --- a/Source/Falcor/Core/API/Common.cpp +++ b/Source/Tools/FalcorTest/Tests/Core/AftermathTests.cpp @@ -25,22 +25,35 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **************************************************************************/ -#include "Common.h" -#include "Utils/Scripting/ScriptBindings.h" +#include "Testing/UnitTest.h" namespace Falcor { -FALCOR_SCRIPT_BINDING(ComparisonFunc) +#if FALCOR_HAS_AFTERMATH +GPU_TEST(AftermathCatchTDR) { - pybind11::enum_ comparisonFunc(m, "ComparisonFunc"); - comparisonFunc.value("Disabled", ComparisonFunc::Disabled); - comparisonFunc.value("LessEqual", ComparisonFunc::LessEqual); - comparisonFunc.value("GreaterEqual", ComparisonFunc::GreaterEqual); - comparisonFunc.value("Less", ComparisonFunc::Less); - comparisonFunc.value("Greater", ComparisonFunc::Greater); - comparisonFunc.value("Equal", ComparisonFunc::Equal); - comparisonFunc.value("NotEqual", ComparisonFunc::NotEqual); - comparisonFunc.value("Always", ComparisonFunc::Always); - comparisonFunc.value("Never", ComparisonFunc::Never); + ref pDevice = ctx.getDevice(); + + if (!pDevice->getAftermathContext()) + ctx.skip("Aftermath is not enabled"); + + ctx.createProgram("Tests/Core/AftermathTests.cs.slang", "main"); + + std::vector data(1024, 1); + ctx.allocateStructuredBuffer("result", 1024, data.data()); + + ctx.getRenderContext()->addAftermathMarker("before"); + + // This should force a TDR (timeout detection & recovery). + ctx.runProgram(32 * 1024, 1024); + + ctx.getRenderContext()->addAftermathMarker("after"); + + pDevice->flushAndSync(); + + // At this point we have lost the device, so submitting another dispatch should terminate the application. + ctx.runProgram(1024, 1024); } +#endif + } // namespace Falcor diff --git a/Source/Tools/FalcorTest/Tests/Core/AftermathTests.cs.slang b/Source/Tools/FalcorTest/Tests/Core/AftermathTests.cs.slang new file mode 100644 index 000000000..bce85001d --- /dev/null +++ b/Source/Tools/FalcorTest/Tests/Core/AftermathTests.cs.slang @@ -0,0 +1,45 @@ +/*************************************************************************** + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. + # + # Redistribution and use in source and binary forms, with or without + # modification, are permitted provided that the following conditions + # are met: + # * Redistributions of source code must retain the above copyright + # notice, this list of conditions and the following disclaimer. + # * Redistributions in binary form must reproduce the above copyright + # notice, this list of conditions and the following disclaimer in the + # documentation and/or other materials provided with the distribution. + # * Neither the name of NVIDIA CORPORATION nor the names of its + # contributors may be used to endorse or promote products derived + # from this software without specific prior written permission. + # + # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY + # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + **************************************************************************/ +RWStructuredBuffer result; + +[numthreads(32, 32, 1)] +void main(uint3 threadId: SV_DispatchThreadID) +{ + uint idx = threadId.x % 1024; + + uint count = 0xffffffff; + + while (count-- > 0) + { + while (result[idx] != 0) + { + result[idx] += 1; + } + result[idx] = 1; + } +} diff --git a/Source/Tools/FalcorTest/Tests/Core/BlitTests.cpp b/Source/Tools/FalcorTest/Tests/Core/BlitTests.cpp index be8eac891..512d56d64 100644 --- a/Source/Tools/FalcorTest/Tests/Core/BlitTests.cpp +++ b/Source/Tools/FalcorTest/Tests/Core/BlitTests.cpp @@ -97,14 +97,14 @@ void testBlit(GPUUnitTestContext& ctx, const uint2 srcDim, const uint32_t scale) ctx.getRenderContext()->blit(pSrc->getSRV(), pDst->getRTV()); // Run program to copy resulting texels into readback buffer. - Program::DefineList defines = {{"FLOAT_FORMAT", std::is_same_v ? "1" : "0"}}; - ctx.createProgram("Tests/Core/BlitTests.cs.slang", "readback", defines, Shader::CompilerFlags::None); + DefineList defines = {{"FLOAT_FORMAT", std::is_same_v ? "1" : "0"}}; + ctx.createProgram("Tests/Core/BlitTests.cs.slang", "readback", defines); ctx.allocateStructuredBuffer("result", dstElemes); ctx["tex"] = pDst; ctx["CB"]["sz"] = dstDim; ctx.runProgram(dstDim.x, dstDim.y, 1); - const T* result = ctx.mapBuffer("result"); + std::vector result = ctx.readBuffer("result"); for (uint32_t i = 0; i < dstElemes; i++) { if constexpr (std::is_same_v) @@ -116,7 +116,6 @@ void testBlit(GPUUnitTestContext& ctx, const uint2 srcDim, const uint32_t scale) EXPECT_EQ(result[i], dstData[i]) << "i = " << i; } } - ctx.unmapBuffer("result"); } } // namespace diff --git a/Source/Tools/FalcorTest/Tests/Core/BufferAccessTests.cpp b/Source/Tools/FalcorTest/Tests/Core/BufferAccessTests.cpp index 06590883b..045f02f9f 100644 --- a/Source/Tools/FalcorTest/Tests/Core/BufferAccessTests.cpp +++ b/Source/Tools/FalcorTest/Tests/Core/BufferAccessTests.cpp @@ -53,17 +53,16 @@ void testBufferReadback(GPUUnitTestContext& ctx, Buffer::CpuAccess 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); + ctx.createProgram("Tests/Core/BufferAccessTests.cs.slang", "readback"); ctx.allocateStructuredBuffer("result", elems); ctx["buffer"] = pBuf; ctx.runProgram(elems, 1, 1); - const uint32_t* result = ctx.mapBuffer("result"); + std::vector result = ctx.readBuffer("result"); for (uint32_t i = 0; i < elems; i++) { EXPECT_EQ(result[i], i) << "i = " << i; } - ctx.unmapBuffer("result"); } } // namespace @@ -110,17 +109,16 @@ GPU_TEST(SetBlobBufferCpuAccessWrite, "Disabled due to issue with SRV/UAVs for r pBuf->setBlob(initData.data(), 0, elems * sizeof(uint32_t)); // Run program that copies the buffer elements into result buffer. - ctx.createProgram("Tests/Core/BufferAccessTests.cs.slang", "readback", Program::DefineList(), Shader::CompilerFlags::None); + ctx.createProgram("Tests/Core/BufferAccessTests.cs.slang", "readback"); ctx.allocateStructuredBuffer("result", elems); ctx["buffer"] = pBuf; ctx.runProgram(elems, 1, 1); - const uint32_t* result = ctx.mapBuffer("result"); + std::vector result = ctx.readBuffer("result"); for (uint32_t i = 0; i < elems; i++) { EXPECT_EQ(result[i], i) << "i = " << i; } - ctx.unmapBuffer("result"); } /** Test that GPU reads from buffer created without CPU access works. diff --git a/Source/Tools/FalcorTest/Tests/Core/BufferTests.cpp b/Source/Tools/FalcorTest/Tests/Core/BufferTests.cpp index cda38016c..27a5355bf 100644 --- a/Source/Tools/FalcorTest/Tests/Core/BufferTests.cpp +++ b/Source/Tools/FalcorTest/Tests/Core/BufferTests.cpp @@ -61,7 +61,7 @@ void testBuffer(GPUUnitTestContext& ctx, uint32_t numElems, uint32_t index = 0, } // Create clear program. - Program::DefineList defines = {{"TYPE", std::to_string((uint32_t)type)}}; + DefineList defines = {{"TYPE", std::to_string((uint32_t)type)}}; ctx.createProgram("Tests/Core/BufferTests.cs.slang", "clearBuffer", defines); // Create test buffer. @@ -106,7 +106,7 @@ void testBuffer(GPUUnitTestContext& ctx, uint32_t numElems, uint32_t index = 0, ctx.runProgram(numElems, 1, 1); // Verify results. - const uint32_t* pResult = ctx.mapBuffer("result"); + std::vector result = ctx.readBuffer("result"); for (uint32_t i = 0; i < numElems; i++) { // Each RMW pass adds i+1 to the element at index i. @@ -114,11 +114,10 @@ void testBuffer(GPUUnitTestContext& ctx, uint32_t numElems, uint32_t index = 0, uint32_t expected = (i + 1) * kIterations * 2; if (i >= index && i < index + blob.size()) expected = blob[i - index] + (i + 1) * kIterations; - uint32_t result = pResult[i]; + uint32_t actual = result[i]; - EXPECT_EQ(result, expected) << "i = " << i << " (numElems = " << numElems << " index = " << index << " count = " << count << ")"; + EXPECT_EQ(actual, expected) << "i = " << i << " (numElems = " << numElems << " index = " << index << " count = " << count << ")"; } - ctx.unmapBuffer("result"); } } // namespace diff --git a/Source/Tools/FalcorTest/Tests/Core/ConstantBufferTests.cpp b/Source/Tools/FalcorTest/Tests/Core/ConstantBufferTests.cpp index 0565f63e4..6f4bf4fb5 100644 --- a/Source/Tools/FalcorTest/Tests/Core/ConstantBufferTests.cpp +++ b/Source/Tools/FalcorTest/Tests/Core/ConstantBufferTests.cpp @@ -33,35 +33,33 @@ namespace Falcor */ GPU_TEST(BuiltinConstantBuffer1) { - ctx.createProgram("Tests/Core/ConstantBufferTests.cs.slang", "testCbuffer1", Program::DefineList(), Shader::CompilerFlags::None); + ctx.createProgram("Tests/Core/ConstantBufferTests.cs.slang", "testCbuffer1"); ctx.allocateStructuredBuffer("result", 3); ctx["CB"]["params1"]["a"] = 1; ctx["CB"]["params1"]["b"] = 3; ctx["CB"]["params1"]["c"] = 5.5f; ctx.runProgram(1, 1, 1); - const float* result = ctx.mapBuffer("result"); + std::vector result = ctx.readBuffer("result"); EXPECT_EQ(result[0], 1); EXPECT_EQ(result[1], 3); EXPECT_EQ(result[2], 5.5f); - ctx.unmapBuffer("result"); } /** GPU test for builtin constant buffer using ConstantBuffer<> syntax. */ GPU_TEST(BuiltinConstantBuffer2) { - ctx.createProgram("Tests/Core/ConstantBufferTests.cs.slang", "testCbuffer2", Program::DefineList(), Shader::CompilerFlags::None); + ctx.createProgram("Tests/Core/ConstantBufferTests.cs.slang", "testCbuffer2"); ctx.allocateStructuredBuffer("result", 3); ctx["params2"]["a"] = 1; ctx["params2"]["b"] = 3; ctx["params2"]["c"] = 5.5f; ctx.runProgram(1, 1, 1); - const float* result = ctx.mapBuffer("result"); + std::vector result = ctx.readBuffer("result"); EXPECT_EQ(result[0], 1); EXPECT_EQ(result[1], 3); EXPECT_EQ(result[2], 5.5f); - ctx.unmapBuffer("result"); } } // namespace Falcor diff --git a/Source/Tools/FalcorTest/Tests/Core/CoreTests.cpp b/Source/Tools/FalcorTest/Tests/Core/CoreTests.cpp new file mode 100644 index 000000000..14a72855e --- /dev/null +++ b/Source/Tools/FalcorTest/Tests/Core/CoreTests.cpp @@ -0,0 +1,56 @@ +/*************************************************************************** + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. + # + # Redistribution and use in source and binary forms, with or without + # modification, are permitted provided that the following conditions + # are met: + # * Redistributions of source code must retain the above copyright + # notice, this list of conditions and the following disclaimer. + # * Redistributions in binary form must reproduce the above copyright + # notice, this list of conditions and the following disclaimer in the + # documentation and/or other materials provided with the distribution. + # * Neither the name of NVIDIA CORPORATION nor the names of its + # contributors may be used to endorse or promote products derived + # from this software without specific prior written permission. + # + # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY + # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + **************************************************************************/ +#include "Testing/UnitTest.h" + +namespace Falcor +{ +GPU_TEST(TransientHeapRecycling) +{ + ref pDevice = ctx.getDevice(); + RenderContext* pRenderContext = pDevice->getRenderContext(); + + size_t M = 1024 * 1024 * 1024; + std::vector cpuBuf(M, 0); + ref A = Buffer::create(pDevice, M, ResourceBindFlags::None, Buffer::CpuAccess::Read, cpuBuf.data()); + ref B = Buffer::create(pDevice, 4, ResourceBindFlags::None, Buffer::CpuAccess::None); + + // Progress through N frames (and transient heaps), ending up using the + // same transient heap as is used for uploading the data to buffer A. + // Before the fix, this leads to a validation error as the buffer for + // uplading to buffer A is still in flight. + for (uint32_t i = 0; i < Device::kInFlightFrameCount; ++i) + pDevice->endFrame(); + + // The following commands will trigger a TDR even if the validation error + // is missed. + pRenderContext->copyBufferRegion(B.get(), 0, A.get(), 0, 4); + pRenderContext->flush(true); + A->map(Buffer::MapType::Read); + A->unmap(); +} +} // namespace Falcor diff --git a/Source/Tools/FalcorTest/Tests/Core/DDSReadTests.cpp b/Source/Tools/FalcorTest/Tests/Core/DDSReadTests.cpp index 12e808571..2f65b3b93 100644 --- a/Source/Tools/FalcorTest/Tests/Core/DDSReadTests.cpp +++ b/Source/Tools/FalcorTest/Tests/Core/DDSReadTests.cpp @@ -96,14 +96,14 @@ void testDDS(GPUUnitTestContext& ctx, const std::string& testName, ResourceForma if (pPngTex) { // Create program to compare decompressed image with reference image - ctx.createProgram("Tests/Core/DDSReadTests.cs.slang", "diff", Program::DefineList(), Shader::CompilerFlags::None); + ctx.createProgram("Tests/Core/DDSReadTests.cs.slang", "diff"); ctx.allocateStructuredBuffer("difference", 4 * sizeof(float) * pDst->getWidth() * pDst->getHeight()); ctx["ref"] = pPngTex; // Reference } else { // Create program to copy decompressed image so that we can save it as the reference - ctx.createProgram("Tests/Core/DDSReadTests.cs.slang", "readback", Program::DefineList(), Shader::CompilerFlags::None); + ctx.createProgram("Tests/Core/DDSReadTests.cs.slang", "readback"); ctx.allocateStructuredBuffer("result", 4 * sizeof(uint32_t) * pDst->getWidth() * pDst->getHeight()); } @@ -115,10 +115,8 @@ void testDDS(GPUUnitTestContext& ctx, const std::string& testName, ResourceForma if (pPngTex) { // Create texture from difference data - const uint8_t* diff = ctx.mapBuffer("difference"); - EXPECT(diff != nullptr); - ref pDiffTex(Texture::create2D(pDevice, dstDim.x, dstDim.y, ResourceFormat::RGBA32Float, 1, 1, diff)); - ctx.unmapBuffer("difference"); + std::vector diff = ctx.readBuffer("difference"); + ref pDiffTex(Texture::create2D(pDevice, dstDim.x, dstDim.y, ResourceFormat::RGBA32Float, 1, 1, diff.data())); // Analyze difference texture TextureAnalyzer analyzer(pDevice); @@ -142,15 +140,12 @@ void testDDS(GPUUnitTestContext& ctx, const std::string& testName, ResourceForma else { // Save newly-created reference image - const uint8_t* result = ctx.mapBuffer("result"); - EXPECT(result != nullptr); - - Bitmap::UniqueConstPtr resultBitmap(Bitmap::create(dstDim.x, dstDim.y, ResourceFormat::RGBA8Unorm, (const uint8_t*)result)); + std::vector result = ctx.readBuffer("result"); + Bitmap::UniqueConstPtr resultBitmap(Bitmap::create(dstDim.x, dstDim.y, ResourceFormat::RGBA8Unorm, result.data())); Bitmap::saveImage( refPath, dstDim.x, dstDim.y, Bitmap::FileFormat::PngFile, Bitmap::ExportFlags::Uncompressed | Bitmap::ExportFlags::ExportAlpha, - ResourceFormat::RGBA8Unorm, false, (void*)result + ResourceFormat::RGBA8Unorm, false, result.data() ); - ctx.unmapBuffer("result"); } } } // namespace diff --git a/Source/Tools/FalcorTest/Tests/Core/EnumTests.cpp b/Source/Tools/FalcorTest/Tests/Core/EnumTests.cpp new file mode 100644 index 000000000..2b72f1234 --- /dev/null +++ b/Source/Tools/FalcorTest/Tests/Core/EnumTests.cpp @@ -0,0 +1,149 @@ + +#include "Testing/UnitTest.h" +#include "Core/Enum.h" + +enum class TestEnum +{ + A, + B, + C, +}; + +FALCOR_ENUM_INFO( + TestEnum, + { + {TestEnum::A, "A"}, + {TestEnum::B, "B"}, + {TestEnum::C, "C"}, + } +); +FALCOR_ENUM_REGISTER(TestEnum); + +enum class TestFlags +{ + A = 1 << 0, + B = 1 << 1, + C = 1 << 2, +}; +FALCOR_ENUM_CLASS_OPERATORS(TestFlags); + +FALCOR_ENUM_INFO( + TestFlags, + { + {TestFlags::A, "A"}, + {TestFlags::B, "B"}, + {TestFlags::C, "C"}, + } +); +FALCOR_ENUM_REGISTER(TestFlags); + +namespace Falcor +{ +struct TestStruct +{ + enum class TestEnum + { + X, + Y, + }; + + FALCOR_ENUM_INFO( + TestEnum, + { + {TestEnum::X, "X"}, + {TestEnum::Y, "Y"}, + } + ); +}; + +FALCOR_ENUM_REGISTER(TestStruct::TestEnum); +} // namespace Falcor + +namespace Falcor +{ +static_assert(has_enum_info::value == false); +static_assert(has_enum_info_v == false); +static_assert(has_enum_info<::TestEnum>::value == true); +static_assert(has_enum_info_v<::TestEnum> == true); +static_assert(has_enum_info::value == true); +static_assert(has_enum_info_v == true); + +CPU_TEST(EnumInfo) +{ + EXPECT_TRUE(enumHasValue("A")); + EXPECT_TRUE(enumHasValue("B")); + EXPECT_TRUE(enumHasValue("C")); + EXPECT_FALSE(enumHasValue("D")); + + EXPECT(stringToEnum("A") == TestEnum::A); + EXPECT(stringToEnum("B") == TestEnum::B); + EXPECT(stringToEnum("C") == TestEnum::C); + + // Converting unregistered values throws. + try + { + enumToString(TestEnum(-1)); + EXPECT(false); + } + catch (const RuntimeError&) + { + EXPECT(true); + } + + // Converting unregistered strings throws. + try + { + stringToEnum("D"); + EXPECT(false); + } + catch (const RuntimeError&) + { + EXPECT(true); + } + + EXPECT_TRUE(enumHasValue("X")); + EXPECT_TRUE(enumHasValue("Y")); + EXPECT_FALSE(enumHasValue("Z")); + + // Test enum nested in namespace and struct. + EXPECT(enumToString(TestStruct::TestEnum::X) == "X"); + EXPECT(enumToString(TestStruct::TestEnum::Y) == "Y"); + EXPECT(stringToEnum("X") == TestStruct::TestEnum::X); + EXPECT(stringToEnum("Y") == TestStruct::TestEnum::Y); + + // Test flags. + EXPECT(flagsToStringList(TestFlags{0}) == std::vector({})); + EXPECT(flagsToStringList(TestFlags::A) == std::vector({"A"})); + EXPECT(flagsToStringList(TestFlags::B) == std::vector({"B"})); + EXPECT(flagsToStringList(TestFlags::A | TestFlags::B) == std::vector({"A", "B"})); + EXPECT(flagsToStringList(TestFlags::A | TestFlags::B | TestFlags::C) == std::vector({"A", "B", "C"})); + + // Converting unregistered values throws. + try + { + flagsToStringList(TestFlags(-1)); + EXPECT(false); + } + catch (const RuntimeError&) + { + EXPECT(true); + } + + EXPECT(stringListToFlags({}) == TestFlags{0}); + EXPECT(stringListToFlags({"A"}) == TestFlags::A); + EXPECT(stringListToFlags({"B"}) == TestFlags::B); + EXPECT(stringListToFlags({"A", "B"}) == (TestFlags::A | TestFlags::B)); + EXPECT(stringListToFlags({"A", "B", "C"}) == (TestFlags::A | TestFlags::B | TestFlags::C)); + + // Converting unregistered strings throws. + try + { + stringListToFlags({"D"}); + EXPECT(false); + } + catch (const RuntimeError&) + { + EXPECT(true); + } +} +} // namespace Falcor diff --git a/Source/Tools/FalcorTest/Tests/Core/LargeBuffer.cpp b/Source/Tools/FalcorTest/Tests/Core/LargeBuffer.cpp index 36a7489d8..ee7eb094b 100644 --- a/Source/Tools/FalcorTest/Tests/Core/LargeBuffer.cpp +++ b/Source/Tools/FalcorTest/Tests/Core/LargeBuffer.cpp @@ -95,7 +95,7 @@ void testReadRaw(GPUUnitTestContext& ctx, bool useRootDesc, size_t bufferSize) { ref pDevice = ctx.getDevice(); - Shader::DefineList defines; + DefineList defines; defines.add("USE_ROOT_DESC", useRootDesc ? "1" : "0"); size_t elemCount = bufferSize / sizeof(uint32_t); @@ -128,7 +128,7 @@ void testReadRaw(GPUUnitTestContext& ctx, bool useRootDesc, size_t bufferSize) } // Run compute program to read from the large buffer. - ctx.createProgram("Tests/Core/LargeBuffer.cs.slang", "testReadRaw", defines, Shader::CompilerFlags::None); + ctx.createProgram("Tests/Core/LargeBuffer.cs.slang", "testReadRaw", defines); ctx.allocateStructuredBuffer("result", 256); auto var = ctx.vars().getRootVar(); var["buffer"] = pBuffer; @@ -136,12 +136,11 @@ void testReadRaw(GPUUnitTestContext& ctx, bool useRootDesc, size_t bufferSize) ctx.runProgram(256, 1, 1); // Check the result. - const uint32_t* result = ctx.mapBuffer("result"); + std::vector result = ctx.readBuffer("result"); for (size_t i = 0; i < data.size(); i++) { EXPECT_EQ(result[i], data[i]) << "i = " << i; } - ctx.unmapBuffer("result"); } /** Test reading from the end of a large structured buffer (stride 16B). @@ -150,7 +149,7 @@ void testReadStructured(GPUUnitTestContext& ctx, bool useRootDesc, size_t buffer { ref pDevice = ctx.getDevice(); - Shader::DefineList defines; + DefineList defines; defines.add("USE_ROOT_DESC", useRootDesc ? "1" : "0"); size_t elemCount = bufferSize / sizeof(uint4); @@ -185,7 +184,7 @@ void testReadStructured(GPUUnitTestContext& ctx, bool useRootDesc, size_t buffer } // Run compute program to read from the large buffer. - ctx.createProgram("Tests/Core/LargeBuffer.cs.slang", "testReadStructured", defines, Shader::CompilerFlags::None); + ctx.createProgram("Tests/Core/LargeBuffer.cs.slang", "testReadStructured", defines); ctx.allocateStructuredBuffer("result", 256); auto var = ctx.vars().getRootVar(); var["structuredBuffer"] = pBuffer; @@ -193,12 +192,11 @@ void testReadStructured(GPUUnitTestContext& ctx, bool useRootDesc, size_t buffer ctx.runProgram(256, 1, 1); // Check the result. - const uint32_t* result = ctx.mapBuffer("result"); + std::vector result = ctx.readBuffer("result"); for (size_t i = 0; i < data.size(); i++) { EXPECT_EQ(result[i], data[i].x) << "i = " << i; } - ctx.unmapBuffer("result"); } /** Test reading from the end of a large structured buffer (stride 4B). @@ -207,7 +205,7 @@ void testReadStructuredUint(GPUUnitTestContext& ctx, bool useRootDesc, size_t bu { ref pDevice = ctx.getDevice(); - Shader::DefineList defines; + DefineList defines; defines.add("USE_ROOT_DESC", useRootDesc ? "1" : "0"); size_t elemCount = bufferSize / sizeof(uint32_t); @@ -242,7 +240,7 @@ void testReadStructuredUint(GPUUnitTestContext& ctx, bool useRootDesc, size_t bu } // Run compute program to read from the large buffer. - ctx.createProgram("Tests/Core/LargeBuffer.cs.slang", "testReadStructuredUint", defines, Shader::CompilerFlags::None); + ctx.createProgram("Tests/Core/LargeBuffer.cs.slang", "testReadStructuredUint", defines); ctx.allocateStructuredBuffer("result", 256); auto var = ctx.vars().getRootVar(); var["structuredBufferUint"] = pBuffer; @@ -250,12 +248,11 @@ void testReadStructuredUint(GPUUnitTestContext& ctx, bool useRootDesc, size_t bu ctx.runProgram(256, 1, 1); // Check the result. - const uint32_t* result = ctx.mapBuffer("result"); + std::vector result = ctx.readBuffer("result"); for (size_t i = 0; i < data.size(); i++) { EXPECT_EQ(result[i], data[i]) << "i = " << i; } - ctx.unmapBuffer("result"); } } // namespace @@ -291,13 +288,13 @@ GPU_TEST(LargeBufferReadRawRoot1) } // Enabled for D3D12 only since Vulkan doesn't support buffers larger than 2^32-1. -GPU_TEST_D3D12(LargeBufferReadRawRoot2) +GPU_TEST(LargeBufferReadRawRoot2, Device::Type::D3D12) { testReadRaw(ctx, true, 4ull << 30); // 4GB } // Enabled for D3D12 only since Vulkan doesn't support buffers larger than 2^32-1. -GPU_TEST_D3D12(LargeBufferReadRawRoot3, "Disabled due to 4GB buffer limit") +GPU_TEST(LargeBufferReadRawRoot3, Device::Type::D3D12, "Disabled due to 4GB buffer limit") { testReadRaw(ctx, true, 5ull << 30); // 5GB } @@ -314,13 +311,13 @@ GPU_TEST(LargeBufferReadStructuredRoot1) } // Enabled for D3D12 only since Vulkan doesn't support buffers larger than 2^32-1. -GPU_TEST_D3D12(LargeBufferReadStructuredRoot2) +GPU_TEST(LargeBufferReadStructuredRoot2, Device::Type::D3D12) { testReadStructured(ctx, true, 4ull << 30); // 4GB } // Enabled for D3D12 only since Vulkan doesn't support buffers larger than 2^32-1. -GPU_TEST_D3D12(LargeBufferReadStructuredRoot3, "Disabled due to 4GB buffer limit") +GPU_TEST(LargeBufferReadStructuredRoot3, Device::Type::D3D12, "Disabled due to 4GB buffer limit") { testReadStructured(ctx, true, 5ull << 30); // 5GB } diff --git a/Source/Tools/FalcorTest/Tests/Core/ObjectTests.cpp b/Source/Tools/FalcorTest/Tests/Core/ObjectTests.cpp index fff59fd4e..18999b5c2 100644 --- a/Source/Tools/FalcorTest/Tests/Core/ObjectTests.cpp +++ b/Source/Tools/FalcorTest/Tests/Core/ObjectTests.cpp @@ -33,6 +33,7 @@ namespace Falcor class DummyObject : public Object { + FALCOR_OBJECT(DummyObject) public: DummyObject() { getCount()++; } ~DummyObject() { getCount()--; } @@ -91,6 +92,7 @@ class DummyBuffer; class DummyDevice : public Object { + FALCOR_OBJECT(DummyDevice) public: ref buffer; @@ -106,6 +108,7 @@ class DummyDevice : public Object class DummyBuffer : public Object { + FALCOR_OBJECT(DummyBuffer) public: BreakableReference device; diff --git a/Source/Tools/FalcorTest/Tests/Core/ParamBlockCB.cpp b/Source/Tools/FalcorTest/Tests/Core/ParamBlockCB.cpp index c6c2e39da..dbbde4bf6 100644 --- a/Source/Tools/FalcorTest/Tests/Core/ParamBlockCB.cpp +++ b/Source/Tools/FalcorTest/Tests/Core/ParamBlockCB.cpp @@ -35,7 +35,7 @@ GPU_TEST(ParamBlockCB) { ref pDevice = ctx.getDevice(); - ctx.createProgram("Tests/Core/ParamBlockCB.cs.slang", "main", Program::DefineList(), Shader::CompilerFlags::None); + ctx.createProgram("Tests/Core/ParamBlockCB.cs.slang", "main"); ctx.allocateStructuredBuffer("result", 1); auto pBlockReflection = ctx.getProgram()->getReflector()->getParameterBlock("gParamBlock"); @@ -45,8 +45,6 @@ GPU_TEST(ParamBlockCB) ctx["gParamBlock"] = pParamBlock; ctx.runProgram(1, 1, 1); - const float* result = ctx.mapBuffer("result"); - EXPECT_EQ(result[0], 42.1f); - ctx.unmapBuffer("result"); + std::vector result = ctx.readBuffer("result"); } } // namespace Falcor diff --git a/Source/Tools/FalcorTest/Tests/Core/ResourceAliasing.cpp b/Source/Tools/FalcorTest/Tests/Core/ResourceAliasing.cpp index bd66ccd1f..6f11e06fa 100644 --- a/Source/Tools/FalcorTest/Tests/Core/ResourceAliasing.cpp +++ b/Source/Tools/FalcorTest/Tests/Core/ResourceAliasing.cpp @@ -42,7 +42,7 @@ GPU_TEST(BufferAliasing_Read) pDevice, initData.size() * sizeof(float), Resource::BindFlags::ShaderResource, Buffer::CpuAccess::None, initData.data() ); - ctx.createProgram("Tests/Core/ResourceAliasing.cs.slang", "testRead", Program::DefineList(), Shader::CompilerFlags::None); + ctx.createProgram("Tests/Core/ResourceAliasing.cs.slang", "testRead"); ctx.allocateStructuredBuffer("result", N * 3); // Bind buffer to two separate vars to test resource aliasing. @@ -52,14 +52,13 @@ GPU_TEST(BufferAliasing_Read) ctx.runProgram(N, 1, 1); - const float* result = ctx.mapBuffer("result"); + std::vector result = ctx.readBuffer("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"); } GPU_TEST(BufferAliasing_ReadWrite) @@ -76,7 +75,7 @@ GPU_TEST(BufferAliasing_ReadWrite) Buffer::CpuAccess::None, initData.data() ); - ctx.createProgram("Tests/Core/ResourceAliasing.cs.slang", "testReadWrite", Program::DefineList(), Shader::CompilerFlags::None); + ctx.createProgram("Tests/Core/ResourceAliasing.cs.slang", "testReadWrite"); // Bind buffer to two separate vars to test resource aliasing. ctx["bufB1"] = pBuffer; @@ -108,7 +107,7 @@ GPU_TEST(BufferAliasing_StructRead, "Disabled because version fails") 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.createProgram("Tests/Core/ResourceAliasing.cs.slang", "testStructRead"); ctx.allocateStructuredBuffer("result", N * 3); // Bind buffer to three separate vars to test resource aliasing. @@ -118,13 +117,12 @@ GPU_TEST(BufferAliasing_StructRead, "Disabled because version fails") ctx.runProgram(N, 1, 1); - const float* result = ctx.mapBuffer("result"); + std::vector result = ctx.readBuffer("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/RootBufferParamBlockTests.cpp b/Source/Tools/FalcorTest/Tests/Core/RootBufferParamBlockTests.cpp index 4c9fe6842..5a2270026 100644 --- a/Source/Tools/FalcorTest/Tests/Core/RootBufferParamBlockTests.cpp +++ b/Source/Tools/FalcorTest/Tests/Core/RootBufferParamBlockTests.cpp @@ -49,8 +49,8 @@ void testRootBuffer(GPUUnitTestContext& ctx, const std::string& shaderModel, boo auto nextRandom = [&]() -> uint32_t { return dist(rng); }; - Program::DefineList defines = {{"USE_UAV", useUav ? "1" : "0"}}; - Shader::CompilerFlags compilerFlags = Shader::CompilerFlags::None; + DefineList defines = {{"USE_UAV", useUav ? "1" : "0"}}; + Program::CompilerFlags compilerFlags = Program::CompilerFlags::None; // Create parameter block based on reflection of a dummy program. // This is to ensure that the register index/space here do not match those of the final program. @@ -143,7 +143,7 @@ void testRootBuffer(GPUUnitTestContext& ctx, const std::string& shaderModel, boo // Test that reading from all the resources in the block works. ctx.runProgram(kNumElems, 1, 1); - const float* result = ctx.mapBuffer("result"); + std::vector result = ctx.readBuffer("result"); for (uint32_t i = 0; i < kNumElems; i++) { float r = 0.f; @@ -162,7 +162,6 @@ void testRootBuffer(GPUUnitTestContext& ctx, const std::string& shaderModel, boo r += globalTestBuffer[i] * 12; EXPECT_EQ(result[i], r) << "i = " << i; } - ctx.unmapBuffer("result"); } } // namespace diff --git a/Source/Tools/FalcorTest/Tests/Core/RootBufferStructTests.cpp b/Source/Tools/FalcorTest/Tests/Core/RootBufferStructTests.cpp index 21a781159..e7615e5e4 100644 --- a/Source/Tools/FalcorTest/Tests/Core/RootBufferStructTests.cpp +++ b/Source/Tools/FalcorTest/Tests/Core/RootBufferStructTests.cpp @@ -44,8 +44,8 @@ void testRootBufferInStruct(GPUUnitTestContext& ctx, const std::string& shaderMo auto nextRandom = [&]() -> uint32_t { return dist(rng); }; - Program::DefineList defines = {{"USE_UAV", useUav ? "1" : "0"}}; - Shader::CompilerFlags compilerFlags = Shader::CompilerFlags::None; + DefineList defines = {{"USE_UAV", useUav ? "1" : "0"}}; + Program::CompilerFlags compilerFlags = Program::CompilerFlags::None; ctx.createProgram("Tests/Core/RootBufferStructTests.cs.slang", "main", defines, compilerFlags, shaderModel); ctx.allocateStructuredBuffer("result", kNumElems); @@ -88,7 +88,7 @@ void testRootBufferInStruct(GPUUnitTestContext& ctx, const std::string& shaderMo // Run the program to test that we can access the buffer. ctx.runProgram(kNumElems, 1, 1); - const uint32_t* result = ctx.mapBuffer("result"); + std::vector result = ctx.readBuffer("result"); for (uint32_t i = 0; i < kNumElems; i++) { uint32_t r = 0; @@ -97,7 +97,6 @@ void testRootBufferInStruct(GPUUnitTestContext& ctx, const std::string& shaderMo r += rootBuf[i] * 3; EXPECT_EQ(result[i], r) << "i = " << i; } - ctx.unmapBuffer("result"); } } // namespace diff --git a/Source/Tools/FalcorTest/Tests/Core/RootBufferTests.cpp b/Source/Tools/FalcorTest/Tests/Core/RootBufferTests.cpp index bfbb38603..24a9ac28f 100644 --- a/Source/Tools/FalcorTest/Tests/Core/RootBufferTests.cpp +++ b/Source/Tools/FalcorTest/Tests/Core/RootBufferTests.cpp @@ -53,8 +53,8 @@ void testRootBuffer(GPUUnitTestContext& ctx, const std::string& shaderModel, boo auto nextRandom = [&]() -> uint32_t { return dist(rng); }; - Program::DefineList defines = {{"USE_UAV", useUav ? "1" : "0"}}; - Shader::CompilerFlags compilerFlags = Shader::CompilerFlags::None; + DefineList defines = {{"USE_UAV", useUav ? "1" : "0"}}; + Program::CompilerFlags compilerFlags = Program::CompilerFlags::None; ctx.createProgram("Tests/Core/RootBufferTests.cs.slang", "main", defines, compilerFlags, shaderModel); ctx.allocateStructuredBuffer("result", kNumElems); @@ -117,7 +117,7 @@ void testRootBuffer(GPUUnitTestContext& ctx, const std::string& shaderModel, boo auto verifyResults = [&](auto str) { - const float* result = ctx.mapBuffer("result"); + std::vector result = ctx.readBuffer("result"); for (uint32_t i = 0; i < kNumElems; i++) { float r = 0.f; @@ -131,7 +131,6 @@ void testRootBuffer(GPUUnitTestContext& ctx, const std::string& shaderModel, boo r += testBuffer[i] * 6; EXPECT_EQ(result[i], r) << "i = " << i << " (" << str << ")"; } - ctx.unmapBuffer("result"); }; // Run the program to test that we can access the buffer. diff --git a/Source/Tools/FalcorTest/Tests/Core/TextureTests.cpp b/Source/Tools/FalcorTest/Tests/Core/TextureTests.cpp index a7726aa0b..c4f6ee139 100644 --- a/Source/Tools/FalcorTest/Tests/Core/TextureTests.cpp +++ b/Source/Tools/FalcorTest/Tests/Core/TextureTests.cpp @@ -53,7 +53,7 @@ GPU_TEST(RWTexture3D) ctx.runProgram(16, 16, 16); // Verify result. - const uint32_t* result = ctx.mapBuffer("result"); + std::vector result = ctx.readBuffer("result"); size_t i = 0; for (uint32_t z = 0; z < 16; z++) { @@ -66,7 +66,6 @@ GPU_TEST(RWTexture3D) } } } - ctx.unmapBuffer("result"); } /** GPU test for creating a min/max MIP pyramid. @@ -207,7 +206,7 @@ GPU_TEST(Texture_Load8Bit) ctx["texUint"] = texUint; ctx.runProgram(256); - const uint4* result = ctx.mapBuffer("result"); + std::vector result = ctx.readBuffer("result"); for (uint32_t i = 0; i < 256; i++) { @@ -220,8 +219,6 @@ GPU_TEST(Texture_Load8Bit) EXPECT_EQ(result[i].z, i); EXPECT_EQ(result[i].w, i); } - - ctx.unmapBuffer("result"); } GPU_TEST(Texture2D_LoadMips) @@ -244,7 +241,7 @@ GPU_TEST(Texture2D_LoadMips) ctx["texUnorm"] = tex; ctx.runProgram(1, 1, 1); - const uint4* result = ctx.mapBuffer("result"); + std::vector result = ctx.readBuffer("result"); EXPECT_EQ(result[0].x, 255); EXPECT_EQ(result[0].y, 0); @@ -260,7 +257,5 @@ GPU_TEST(Texture2D_LoadMips) 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 f60013355..3380ff457 100644 --- a/Source/Tools/FalcorTest/Tests/DebugPasses/InvalidPixelDetectionTests.cpp +++ b/Source/Tools/FalcorTest/Tests/DebugPasses/InvalidPixelDetectionTests.cpp @@ -25,6 +25,7 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **************************************************************************/ +#include "Core/Plugin.h" #include "Testing/UnitTest.h" #include "RenderGraph/RenderGraph.h" @@ -32,6 +33,8 @@ namespace Falcor { GPU_TEST(InvalidPixelDetectionPass) { + PluginManager::instance().loadPluginByName("DebugPasses"); + ref pDevice = ctx.getDevice(); float pInitData[8] = { @@ -46,7 +49,7 @@ GPU_TEST(InvalidPixelDetectionPass) }; RenderContext* pRenderContext = ctx.getRenderContext(); - Fbo* pTargetFbo = ctx.getTargetFbo(); + ref pTargetFbo = Fbo::create2D(pDevice, 2, 4, ResourceFormat::BGRA8UnormSrgb); 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()); @@ -55,7 +58,7 @@ GPU_TEST(InvalidPixelDetectionPass) pGraph->addPass(pPass, "InvalidPixelDetectionPass"); pGraph->setInput("InvalidPixelDetectionPass.src", pInput); pGraph->markOutput("InvalidPixelDetectionPass.dst"); - pGraph->onResize(pTargetFbo); + pGraph->onResize(pTargetFbo.get()); pGraph->execute(pRenderContext); ref pOutput = pGraph->getOutput("InvalidPixelDetectionPass.dst"); std::vector color = pRenderContext->readTextureSubresource(pOutput->asTexture().get(), 0); diff --git a/Source/Tools/FalcorTest/Tests/Platform/LockFileTests.cpp b/Source/Tools/FalcorTest/Tests/Platform/LockFileTests.cpp index 36bcdfd20..5032548e5 100644 --- a/Source/Tools/FalcorTest/Tests/Platform/LockFileTests.cpp +++ b/Source/Tools/FalcorTest/Tests/Platform/LockFileTests.cpp @@ -48,7 +48,7 @@ CPU_TEST(LockFile_Closed) CPU_TEST(LockFile_OpenClose) { - const std::filesystem::path path = "test_lock_file"; + const std::filesystem::path path = "test_lock_file_1"; { LockFile file; @@ -74,7 +74,7 @@ CPU_TEST(LockFile_OpenClose) CPU_TEST(LockFile_ExclusiveLock) { - static const std::filesystem::path path = "test_lock_file"; + static const std::filesystem::path path = "test_lock_file_2"; static std::atomic lockCounter; static std::atomic unlockCounter; @@ -130,7 +130,7 @@ CPU_TEST(LockFile_ExclusiveLock) // Make sure none of the threads were able to acquire the lock yet. std::this_thread::sleep_for(std::chrono::milliseconds(5)); - EXPECT_EQ(lockCounter, 0); + EXPECT_EQ(lockCounter.load(), 0); // Release the lock from the main thread. This will allow all the other // threads to acquire the lock, one after the other. @@ -158,8 +158,8 @@ CPU_TEST(LockFile_ExclusiveLock) } // Ensure all threads did manage to acquire the lock. - EXPECT_EQ(lockCounter, tasks.size()); - EXPECT_EQ(unlockCounter, tasks.size()); + EXPECT_EQ(lockCounter.load(), tasks.size()); + EXPECT_EQ(unlockCounter.load(), tasks.size()); // Check that we can now acquire the lock in non-blocking mode. EXPECT_TRUE(lockFile2.tryLock(LockFile::LockType::Exclusive)); diff --git a/Source/Tools/FalcorTest/Tests/Rendering/Materials/MicrofacetTests.cpp b/Source/Tools/FalcorTest/Tests/Rendering/Materials/MicrofacetTests.cpp index 3e7bd30c5..ee844e9cc 100644 --- a/Source/Tools/FalcorTest/Tests/Rendering/Materials/MicrofacetTests.cpp +++ b/Source/Tools/FalcorTest/Tests/Rendering/Materials/MicrofacetTests.cpp @@ -96,7 +96,7 @@ void setupSamplingTest(GPUUnitTestContext& ctx, const SamplingTestSpec& spec, co uint32_t testCount = spec.visibleNormals ? (uint32_t)spec.incidentAngles.size() : 1; - Program::DefineList defines; + DefineList defines; defines.add("TEST_NDF_TYPE", spec.ndf); ctx.createProgram(kShaderFile, csEntry, defines); @@ -281,7 +281,7 @@ GPU_TEST(MicrofacetSigmaIntegration) // by Dupuy et al. 2016, Eq. 18. for (uint32_t t = 0; t < kNdfs.size(); ++t) { - Program::DefineList defines; + DefineList defines; defines.add("TEST_NDF_TYPE", kNdfs[t]); ctx.createProgram(kShaderFile, "sigmaIntegration", defines); @@ -298,8 +298,8 @@ GPU_TEST(MicrofacetSigmaIntegration) ctx.allocateStructuredBuffer("result2", N); ctx.runProgram(N, 1, 1); - const float* result1 = ctx.mapBuffer("result1"); - const float* result2 = ctx.mapBuffer("result2"); + std::vector result1 = ctx.readBuffer("result1"); + std::vector result2 = ctx.readBuffer("result2"); for (uint32_t i = 0; i < N; ++i) { float sigmaRef = result1[i]; @@ -307,8 +307,6 @@ GPU_TEST(MicrofacetSigmaIntegration) float diff = std::abs(sigmaRef - sigmaEval); EXPECT_LT(diff, 5e-3f); } - ctx.unmapBuffer("result1"); - ctx.unmapBuffer("result2"); } } @@ -320,7 +318,7 @@ GPU_TEST(MicrofacetSigmaLambdaConsistency) // by Dupuy et al. 2016, Eq. 18. for (uint32_t t = 0; t < kNdfs.size(); ++t) { - Program::DefineList defines; + DefineList defines; defines.add("TEST_NDF_TYPE", kNdfs[t]); ctx.createProgram(kShaderFile, "sigmaLambdaConsistency", defines); @@ -337,8 +335,8 @@ GPU_TEST(MicrofacetSigmaLambdaConsistency) ctx.allocateStructuredBuffer("result2", N); ctx.runProgram(N, 1, 1); - const float* result1 = ctx.mapBuffer("result1"); - const float* result2 = ctx.mapBuffer("result2"); + std::vector result1 = ctx.readBuffer("result1"); + std::vector result2 = ctx.readBuffer("result2"); for (uint32_t i = 0; i < N; ++i) { float mu = -1 + 2 * float(i) / (N - 1); @@ -357,8 +355,6 @@ GPU_TEST(MicrofacetSigmaLambdaConsistency) float diff = std::abs(sigma - rhs); EXPECT_LT(diff, 1e-3f); } - ctx.unmapBuffer("result1"); - ctx.unmapBuffer("result2"); } } @@ -369,7 +365,7 @@ GPU_TEST(MicrofacetLambdaNonsymmetry) // by Dupuy et al. 2016, Eq. 18 and 19. for (uint32_t t = 0; t < kNdfs.size(); ++t) { - Program::DefineList defines; + DefineList defines; defines.add("TEST_NDF_TYPE", kNdfs[t]); ctx.createProgram(kShaderFile, "lambdaNonsymmetry", defines); @@ -386,8 +382,8 @@ GPU_TEST(MicrofacetLambdaNonsymmetry) ctx.allocateStructuredBuffer("result2", N); ctx.runProgram(N, 1, 1); - const float* result1 = ctx.mapBuffer("result1"); - const float* result2 = ctx.mapBuffer("result2"); + std::vector result1 = ctx.readBuffer("result1"); + std::vector result2 = ctx.readBuffer("result2"); for (uint32_t i = 0; i < N; ++i) { float LambdaPos = result1[i]; @@ -397,8 +393,6 @@ GPU_TEST(MicrofacetLambdaNonsymmetry) float diff = std::abs(lhs - rhs); EXPECT_LT(diff, 1e-3f); } - ctx.unmapBuffer("result1"); - ctx.unmapBuffer("result2"); } } @@ -407,7 +401,7 @@ GPU_TEST(MicrofacetG1Symmetry) // Test the symmetry of the Smith bistatic shadowing function G1. for (uint32_t t = 0; t < kNdfs.size(); ++t) { - Program::DefineList defines; + DefineList defines; defines.add("TEST_NDF_TYPE", kNdfs[t]); ctx.createProgram(kShaderFile, "g1Symmetry", defines); @@ -424,8 +418,8 @@ GPU_TEST(MicrofacetG1Symmetry) ctx.allocateStructuredBuffer("result2", N); ctx.runProgram(N, 1, 1); - const float* result1 = ctx.mapBuffer("result1"); - const float* result2 = ctx.mapBuffer("result2"); + std::vector result1 = ctx.readBuffer("result1"); + std::vector result2 = ctx.readBuffer("result2"); for (uint32_t i = 0; i < N; ++i) { float g1Pos = result1[i]; @@ -433,8 +427,6 @@ GPU_TEST(MicrofacetG1Symmetry) float diff = std::abs(g1Pos - g1Neg); EXPECT_LT(diff, 1e-3f); } - ctx.unmapBuffer("result1"); - ctx.unmapBuffer("result2"); } } diff --git a/Source/Tools/FalcorTest/Tests/Rendering/Materials/MicrofacetTests.cs.slang b/Source/Tools/FalcorTest/Tests/Rendering/Materials/MicrofacetTests.cs.slang index 54a8f9c0e..f170b14c3 100644 --- a/Source/Tools/FalcorTest/Tests/Rendering/Materials/MicrofacetTests.cs.slang +++ b/Source/Tools/FalcorTest/Tests/Rendering/Materials/MicrofacetTests.cs.slang @@ -179,7 +179,7 @@ void sigmaIntegration(uint3 threadId: SV_DispatchThreadID) float pdf; float3 wm = sample_cosine_hemisphere_concentric(sampleNext2D(sg), pdf); double integrand = max(0.f, dot(-wi, wm)) * mf.evalD(wm); - sum += integrand / pdf; + sum += integrand / (double)pdf; } sum /= N; result1[threadId.x] = (float)sum; diff --git a/Source/Tools/FalcorTest/Tests/Sampling/AliasTableTests.cpp b/Source/Tools/FalcorTest/Tests/Sampling/AliasTableTests.cpp index 67395e1d9..614ca854e 100644 --- a/Source/Tools/FalcorTest/Tests/Sampling/AliasTableTests.cpp +++ b/Source/Tools/FalcorTest/Tests/Sampling/AliasTableTests.cpp @@ -86,14 +86,13 @@ void testAliasTable(GPUUnitTestContext& ctx, uint32_t N, std::vector spec // Build histogram. std::vector histogram(N, 0); - const uint32_t* result = ctx.mapBuffer("sampleResult"); + std::vector result = ctx.readBuffer("sampleResult"); for (uint32_t i = 0; i < resultCount; ++i) { uint32_t item = result[i]; EXPECT(item >= 0u && item < N); histogram[item]++; } - ctx.unmapBuffer("sampleResult"); // Verify histogram using a chi-square test. std::vector expFrequencies(N); @@ -131,12 +130,11 @@ void testAliasTable(GPUUnitTestContext& ctx, uint32_t N, std::vector spec ctx.runProgram(resultCount); // Verify weights. - const float* weightResult = ctx.mapBuffer("weightResult"); + std::vector weightResult = ctx.readBuffer("weightResult"); for (uint32_t i = 0; i < resultCount; ++i) { EXPECT_EQ(weightResult[i], weights[i]); } - ctx.unmapBuffer("weightResult"); } } } // namespace diff --git a/Source/Tools/FalcorTest/Tests/Sampling/LowDiscrepancyTests.cpp b/Source/Tools/FalcorTest/Tests/Sampling/LowDiscrepancyTests.cpp index dd7fff79c..18a08f979 100644 --- a/Source/Tools/FalcorTest/Tests/Sampling/LowDiscrepancyTests.cpp +++ b/Source/Tools/FalcorTest/Tests/Sampling/LowDiscrepancyTests.cpp @@ -38,12 +38,11 @@ GPU_TEST(RadicalInverse) ctx["TestCB"]["resultSize"] = 4; ctx.runProgram(); - const float* s = ctx.mapBuffer("result"); + std::vector s = ctx.readBuffer("result"); EXPECT_EQ(s[0], 0.f); EXPECT_EQ(s[1], 0.5f); EXPECT_EQ(s[2], 0.25f); EXPECT_EQ(s[3], 0.75f); - ctx.unmapBuffer("result"); } } // namespace Falcor diff --git a/Source/Tools/FalcorTest/Tests/Sampling/PseudorandomTests.cpp b/Source/Tools/FalcorTest/Tests/Sampling/PseudorandomTests.cpp index 0b11ebf7b..e717e41e9 100644 --- a/Source/Tools/FalcorTest/Tests/Sampling/PseudorandomTests.cpp +++ b/Source/Tools/FalcorTest/Tests/Sampling/PseudorandomTests.cpp @@ -39,62 +39,60 @@ namespace Falcor { // Reference implementation of the xoshiro128** algorithm. // See http://xoshiro.di.unimi.it/xoshiro128starstar.c -namespace xoshiro128starstar +struct xoshiro128starstar { -static inline uint32_t rotl(const uint32_t x, int k) -{ - return (x << k) | (x >> (32 - k)); -} + static inline uint32_t rotl(const uint32_t x, int k) { return (x << k) | (x >> (32 - k)); } -static uint32_t s[4]; + uint32_t s[4]; -uint32_t next() -{ - const uint32_t result_starstar = rotl(s[0] * 5, 7) * 9; + uint32_t next() + { + const uint32_t result_starstar = rotl(s[0] * 5, 7) * 9; - const uint32_t t = s[1] << 9; + const uint32_t t = s[1] << 9; - s[2] ^= s[0]; - s[3] ^= s[1]; - s[1] ^= s[2]; - s[0] ^= s[3]; + s[2] ^= s[0]; + s[3] ^= s[1]; + s[1] ^= s[2]; + s[0] ^= s[3]; - s[2] ^= t; + s[2] ^= t; - s[3] = rotl(s[3], 11); + s[3] = rotl(s[3], 11); - return result_starstar; -} -} // namespace xoshiro128starstar + return result_starstar; + } +}; // Reference implementation of the SplitMix64 algorithm. // See http://xoshiro.di.unimi.it/splitmix64.c -namespace splitmix64 +struct splitmix64 { -static uint64_t x; + uint64_t x; -uint64_t next() -{ - uint64_t z = (x += 0x9e3779b97f4a7c15); - z = (z ^ (z >> 30)) * 0xbf58476d1ce4e5b9; - z = (z ^ (z >> 27)) * 0x94d049bb133111eb; - return z ^ (z >> 31); -} -}; // namespace splitmix64 + uint64_t next() + { + uint64_t z = (x += 0x9e3779b97f4a7c15); + z = (z ^ (z >> 30)) * 0xbf58476d1ce4e5b9; + z = (z ^ (z >> 27)) * 0x94d049bb133111eb; + return z ^ (z >> 31); + } +}; // Reference implementation of the LCG from Numerical Recipes. // See https://en.wikipedia.org/wiki/Linear_congruential_generator -namespace lcg +struct lcg { -static uint32_t state; -uint32_t next() -{ - const uint32_t a = 1664525; - const uint32_t c = 1013904223; - state = a * state + c; - return state; -} -} // namespace lcg + uint32_t state; + + uint32_t next() + { + const uint32_t a = 1664525; + const uint32_t c = 1013904223; + state = a * state + c; + return state; + } +}; // Shared test utils. namespace @@ -135,23 +133,23 @@ GPU_TEST(XoshiroPRNG) ctx.runProgram(kInstances); // Compare result against reference implementation. - const uint32_t* result = ctx.mapBuffer("result"); + std::vector result = ctx.readBuffer("result"); for (uint32_t i = 0; i < kInstances; i++) { + xoshiro128starstar rng; // Set seed. - xoshiro128starstar::s[0] = seed[i * 4 + 0]; - xoshiro128starstar::s[1] = seed[i * 4 + 1]; - xoshiro128starstar::s[2] = seed[i * 4 + 2]; - xoshiro128starstar::s[3] = seed[i * 4 + 3]; + rng.s[0] = seed[i * 4 + 0]; + rng.s[1] = seed[i * 4 + 1]; + rng.s[2] = seed[i * 4 + 2]; + rng.s[3] = seed[i * 4 + 3]; for (uint32_t j = 0; j < kDimensions; j++) { - const uint32_t ref = xoshiro128starstar::next(); + const uint32_t ref = rng.next(); const uint32_t res = result[j * kInstances + i]; EXPECT_EQ(ref, res) << "instance = " << i << " dimension = " << j; } } - ctx.unmapBuffer("result"); } /** GPU test for SplitMix64 pseudorandom number generator. @@ -163,26 +161,26 @@ GPU_TEST(SplitMixPRNG) 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); + ctx.createProgram(kShaderFile, "testSplitMix"); ctx.allocateStructuredBuffer("result64", kInstances * kDimensions); ctx["seed"] = pSeedBuf; ctx.runProgram(kInstances); // Compare result against reference implementation. - const uint64_t* result = ctx.mapBuffer("result64"); + std::vector result = ctx.readBuffer("result64"); for (uint32_t i = 0; i < kInstances; i++) { + splitmix64 rng; // Set seed. - splitmix64::x = (uint64_t(seed[i * 2 + 1]) << 32) | uint64_t(seed[i * 2 + 0]); + rng.x = (uint64_t(seed[i * 2 + 1]) << 32) | uint64_t(seed[i * 2 + 0]); for (uint32_t j = 0; j < kDimensions; j++) { - const uint64_t ref = splitmix64::next(); + const uint64_t ref = rng.next(); const uint64_t res = result[j * kInstances + i]; EXPECT_EQ(ref, res) << "instance = " << i << " dimension = " << j; } } - ctx.unmapBuffer("result64"); } /** GPU test for LCG pseudorandom number generator. @@ -200,19 +198,19 @@ GPU_TEST(LCGPRNG) ctx.runProgram(kInstances); // Compare result against reference implementation. - const uint32_t* result = ctx.mapBuffer("result"); + std::vector result = ctx.readBuffer("result"); for (uint32_t i = 0; i < kInstances; i++) { + lcg rng; // Set seed. - lcg::state = seed[i]; + rng.state = seed[i]; for (uint32_t j = 0; j < kDimensions; j++) { - const uint32_t ref = lcg::next(); + const uint32_t ref = rng.next(); const uint32_t res = result[j * kInstances + i]; EXPECT_EQ(ref, res) << "instance = " << i << " dimension = " << j; } } - ctx.unmapBuffer("result"); } } // namespace Falcor diff --git a/Source/Tools/FalcorTest/Tests/Sampling/SampleGeneratorTests.cpp b/Source/Tools/FalcorTest/Tests/Sampling/SampleGeneratorTests.cpp index d86c7e57b..18f12cfa2 100644 --- a/Source/Tools/FalcorTest/Tests/Sampling/SampleGeneratorTests.cpp +++ b/Source/Tools/FalcorTest/Tests/Sampling/SampleGeneratorTests.cpp @@ -56,8 +56,8 @@ double correlation(const float* elems, const size_t numElems, const size_t strid size_t n = 0; for (size_t i = 0; i + stride < numElems; i++) { - float x = elems[i]; - float y = elems[i + stride]; + double x = elems[i]; + double y = elems[i + stride]; sum_x += x; sum_y += y; sum_xx += x * x; @@ -78,7 +78,7 @@ void testSampleGenerator(GPUUnitTestContext& ctx, uint32_t type, const double me // 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"); + ctx.createProgram(kShaderFile, "test", defines, Program::CompilerFlags::None, "6_2"); pSampleGenerator->beginFrame(ctx.getRenderContext(), uint2(kDispatchDim.x, kDispatchDim.y)); pSampleGenerator->setShaderData(ctx.vars().getRootVar()); @@ -92,24 +92,29 @@ void testSampleGenerator(GPUUnitTestContext& ctx, uint32_t type, const double me ctx.runProgram(kDispatchDim); // Readback results. - const float* result = ctx.mapBuffer("result"); + std::vector result = ctx.readBuffer("result"); // Check that all samples are in the [0,1) range, // and that their mean is roughly 0.5. + float min = std::numeric_limits::max(); + float max = std::numeric_limits::lowest(); double mean = 0.0; for (size_t i = 0; i < numSamples; i++) { float u = result[i]; + min = std::min(min, u); + max = std::max(max, u); mean += u; - EXPECT(u >= 0.f && u < 1.f) << u; } mean /= numSamples; + EXPECT_GE(min, 0.f); + EXPECT_LT(max, 1.f); EXPECT_GE(mean, 0.5 - meanError); EXPECT_LE(mean, 0.5 + meanError); // Check correlation between adjacent samples along different dimensions in the sample set. // This is not really a robust statistical test, but it should detect if something is fundamentally wrong. - auto corr = [&](size_t stride) -> double { return std::abs(correlation(result, numSamples, stride)); }; + auto corr = [&](size_t stride) -> double { return std::abs(correlation(result.data(), result.size(), stride)); }; // Test nearby dimensions. for (size_t i = 1; i <= 8; i++) @@ -139,8 +144,6 @@ void testSampleGenerator(GPUUnitTestContext& ctx, uint32_t type, const double me EXPECT_LE(corr(i * instanceStride), corrThreshold) << "i = " << i; } } - - ctx.unmapBuffer("result"); } } // namespace diff --git a/Source/Tools/FalcorTest/Tests/Scene/EnvMapTests.cpp b/Source/Tools/FalcorTest/Tests/Scene/EnvMapTests.cpp index 478bc3ec7..0eb1fde90 100644 --- a/Source/Tools/FalcorTest/Tests/Scene/EnvMapTests.cpp +++ b/Source/Tools/FalcorTest/Tests/Scene/EnvMapTests.cpp @@ -34,7 +34,7 @@ namespace Falcor namespace { // This file is located in the media/ directory fetched by packman. -const char kEnvMapFile[] = "LightProbes/20050806-03_hd.hdr"; +const char kEnvMapFile[] = "test_scenes/envmaps/20050806-03_hd.hdr"; } // namespace GPU_TEST(EnvMap) diff --git a/Source/Tools/FalcorTest/Tests/Scene/Material/BSDFTests.cpp b/Source/Tools/FalcorTest/Tests/Scene/Material/BSDFTests.cpp index 68d528b24..0e5959035 100644 --- a/Source/Tools/FalcorTest/Tests/Scene/Material/BSDFTests.cpp +++ b/Source/Tools/FalcorTest/Tests/Scene/Material/BSDFTests.cpp @@ -111,7 +111,7 @@ void setupSamplingTest(GPUUnitTestContext& ctx, const SamplingTestSpec& spec, co uint32_t testCount = (uint32_t)spec.bsdfConfigs.size(); uint32_t binCount = spec.phiBinCount * spec.cosThetaBinCount; - Program::DefineList defines; + DefineList defines; defines.add("TEST_BSDF_IMPORT", spec.bsdfImport); defines.add("TEST_BSDF", spec.bsdf); defines.add("TEST_BSDF_INIT", spec.bsdfInit); diff --git a/Source/Tools/FalcorTest/Tests/Scene/Material/HairChiang16Tests.cpp b/Source/Tools/FalcorTest/Tests/Scene/Material/HairChiang16Tests.cpp index 67d113f0a..a1a57a2b9 100644 --- a/Source/Tools/FalcorTest/Tests/Scene/Material/HairChiang16Tests.cpp +++ b/Source/Tools/FalcorTest/Tests/Scene/Material/HairChiang16Tests.cpp @@ -56,7 +56,7 @@ void testWhiteFurnace(GPUUnitTestContext& ctx, const std::string& funcName, cons // Setup GPU test. auto defines = pSampleGenerator->getDefines(); - ctx.createProgram(kShaderFile, funcName, defines, Shader::CompilerFlags::None, "6_2"); + ctx.createProgram(kShaderFile, funcName, defines, Program::CompilerFlags::None, "6_2"); pSampleGenerator->setShaderData(ctx.vars().getRootVar()); @@ -67,12 +67,11 @@ void testWhiteFurnace(GPUUnitTestContext& ctx, const std::string& funcName, cons ctx.runProgram(testCount); - const float* result = ctx.mapBuffer("result"); + std::vector result = ctx.readBuffer("result"); for (uint32_t i = 0; i < testCount; i++) { EXPECT_LE(std::abs(result[i] - 1.f), threshold) << "WhiteFurnaceTestCase" << i << ", expected " << 1 << ", got " << result[i]; } - ctx.unmapBuffer("result"); } } // namespace @@ -85,10 +84,7 @@ GPU_TEST(HairChiang16_PbrtReference) std::filesystem::path path = getRuntimeDirectory() / "data/tests/pbrt_hair_bsdf.dat"; fin.open(path, std::ios::in | std::ios::binary); - if (!fin.is_open()) - { - throw ErrorRunningTestException("Cannot find reference data file 'pbrt_hair_bsdf.dat'."); - } + ASSERT(fin.is_open()); std::vector buf(testCount * 17); fin.read((char*)buf.data(), buf.size() * sizeof(float)); @@ -111,7 +107,7 @@ GPU_TEST(HairChiang16_PbrtReference) // Setup GPU test. auto defines = pSampleGenerator->getDefines(); - ctx.createProgram(kShaderFile, "testPbrtReference", defines, Shader::CompilerFlags::None, "6_2"); + ctx.createProgram(kShaderFile, "testPbrtReference", defines, Program::CompilerFlags::None, "6_2"); ctx.allocateStructuredBuffer("gBetaM", testCount, buf.data()); ctx.allocateStructuredBuffer("gBetaN", testCount, buf.data() + testCount); @@ -126,7 +122,7 @@ GPU_TEST(HairChiang16_PbrtReference) ctx.runProgram(testCount); - const float3* result = ctx.mapBuffer("gResultOurs"); + std::vector result = ctx.readBuffer("gResultOurs"); for (uint32_t i = 0; i < testCount; i++) { for (uint32_t c = 0; c < 3; c++) @@ -136,7 +132,6 @@ GPU_TEST(HairChiang16_PbrtReference) << result[i][c]; } } - ctx.unmapBuffer("gResultOurs"); } GPU_TEST(HairChiang16_WhiteFurnaceUniform) @@ -169,7 +164,7 @@ GPU_TEST(HairChiang16_ImportanceSamplingWeights) // Setup GPU test. auto defines = pSampleGenerator->getDefines(); - ctx.createProgram(kShaderFile, "testImportanceSamplingWeights", defines, Shader::CompilerFlags::None, "6_2"); + ctx.createProgram(kShaderFile, "testImportanceSamplingWeights", defines, Program::CompilerFlags::None, "6_2"); pSampleGenerator->setShaderData(ctx.vars().getRootVar()); @@ -180,7 +175,7 @@ GPU_TEST(HairChiang16_ImportanceSamplingWeights) ctx.runProgram(testCount, sampleCount); - const float* result = ctx.mapBuffer("result"); + std::vector result = ctx.readBuffer("result"); for (uint32_t i = 0; i < testCount; i++) { for (uint32_t j = 0; j < sampleCount; j++) @@ -196,7 +191,6 @@ GPU_TEST(HairChiang16_ImportanceSamplingWeights) << "ImportanceSamplingWeightsTestCase(" << i << ", " << j << "), expected " << 1 << ", got " << result[idx]; } } - ctx.unmapBuffer("result"); } GPU_TEST(HairChiang16_SamplingConsistency) @@ -219,7 +213,7 @@ GPU_TEST(HairChiang16_SamplingConsistency) // Setup GPU test. auto defines = pSampleGenerator->getDefines(); - ctx.createProgram(kShaderFile, "testSamplingConsistency", defines, Shader::CompilerFlags::None, "6_2"); + ctx.createProgram(kShaderFile, "testSamplingConsistency", defines, Program::CompilerFlags::None, "6_2"); pSampleGenerator->setShaderData(ctx.vars().getRootVar()); @@ -230,12 +224,11 @@ GPU_TEST(HairChiang16_SamplingConsistency) ctx.runProgram(testCount); - const float* result = ctx.mapBuffer("result"); + std::vector result = ctx.readBuffer("result"); for (uint32_t i = 0; i < testCount; i++) { EXPECT_LE(result[i], 0.05f) << "SamplingConsistencyTestCase" << i << ", expected " << 0 << ", got " << result[i]; } - ctx.unmapBuffer("result"); } } // namespace Falcor diff --git a/Source/Tools/FalcorTest/Tests/Scene/Material/MERLFileTests.cpp b/Source/Tools/FalcorTest/Tests/Scene/Material/MERLFileTests.cpp index 921490906..eee349773 100644 --- a/Source/Tools/FalcorTest/Tests/Scene/Material/MERLFileTests.cpp +++ b/Source/Tools/FalcorTest/Tests/Scene/Material/MERLFileTests.cpp @@ -33,7 +33,7 @@ namespace Falcor { GPU_TEST(MERLFile) { - const std::filesystem::path path = "TestScenes/Materials/Data/gray-lambert.binary"; + const std::filesystem::path path = "test_scenes/materials/data/gray-lambert.binary"; std::filesystem::path fullPath; ASSERT(findFileInDataDirectories(path, fullPath)); diff --git a/Source/Tools/FalcorTest/Tests/Slang/CastFloat16.cpp b/Source/Tools/FalcorTest/Tests/Slang/CastFloat16.cpp index 563c9ec91..72930629f 100644 --- a/Source/Tools/FalcorTest/Tests/Slang/CastFloat16.cpp +++ b/Source/Tools/FalcorTest/Tests/Slang/CastFloat16.cpp @@ -42,7 +42,7 @@ GPU_TEST(CastFloat16) { ref pDevice = ctx.getDevice(); - ctx.createProgram("Tests/Slang/CastFloat16.cs.slang", "testCastFloat16", Program::DefineList(), Shader::CompilerFlags::None, "6_5"); + ctx.createProgram("Tests/Slang/CastFloat16.cs.slang", "testCastFloat16", DefineList(), Program::CompilerFlags::None, "6_5"); ctx.allocateStructuredBuffer("result", kNumElems); std::vector elems(kNumElems * 2); @@ -56,7 +56,7 @@ GPU_TEST(CastFloat16) ctx.runProgram(kNumElems, 1, 1); // Verify results. - const uint32_t* result = ctx.mapBuffer("result"); + std::vector result = ctx.readBuffer("result"); for (uint32_t i = 0; i < kNumElems; i++) { uint16_t ix = elems[2 * i]; @@ -64,6 +64,5 @@ GPU_TEST(CastFloat16) uint32_t expected = (uint32_t(iy) << 16) | ix; EXPECT_EQ(result[i], expected) << "i = " << i; } - ctx.unmapBuffer("result"); } } // namespace Falcor diff --git a/Source/Tools/FalcorTest/Tests/Slang/Float16Tests.cpp b/Source/Tools/FalcorTest/Tests/Slang/Float16Tests.cpp index f68380c4e..913f56c33 100644 --- a/Source/Tools/FalcorTest/Tests/Slang/Float16Tests.cpp +++ b/Source/Tools/FalcorTest/Tests/Slang/Float16Tests.cpp @@ -46,9 +46,9 @@ void test(GPUUnitTestContext& ctx, const std::string& shaderModel, bool useUav) { ref pDevice = ctx.getDevice(); - Program::DefineList defines = {{"USE_UAV", useUav ? "1" : "0"}}; + DefineList defines = {{"USE_UAV", useUav ? "1" : "0"}}; - ctx.createProgram("Tests/Slang/Float16Tests.cs.slang", "testFloat16", defines, Shader::CompilerFlags::None, shaderModel); + ctx.createProgram("Tests/Slang/Float16Tests.cs.slang", "testFloat16", defines, Program::CompilerFlags::None, shaderModel); ctx.allocateStructuredBuffer("result", kNumElems); std::vector elems(kNumElems); @@ -64,12 +64,11 @@ void test(GPUUnitTestContext& ctx, const std::string& shaderModel, bool useUav) ctx.runProgram(kNumElems, 1, 1); // Verify results. - const uint16_t* result = ctx.mapBuffer("result"); + std::vector result = ctx.readBuffer("result"); for (uint32_t i = 0; i < kNumElems; i++) { EXPECT_EQ(result[i], elems[i]) << "i = " << i << " shaderModel=" << shaderModel; } - ctx.unmapBuffer("result"); } } // namespace diff --git a/Source/Tools/FalcorTest/Tests/Slang/Float64Tests.cpp b/Source/Tools/FalcorTest/Tests/Slang/Float64Tests.cpp index b766e8c09..b10509b8a 100644 --- a/Source/Tools/FalcorTest/Tests/Slang/Float64Tests.cpp +++ b/Source/Tools/FalcorTest/Tests/Slang/Float64Tests.cpp @@ -47,9 +47,9 @@ void test(GPUUnitTestContext& ctx, const std::string& shaderModel, bool useUav) { ref pDevice = ctx.getDevice(); - Program::DefineList defines = {{"USE_UAV", useUav ? "1" : "0"}}; + DefineList defines = {{"USE_UAV", useUav ? "1" : "0"}}; - ctx.createProgram("Tests/Slang/Float64Tests.cs.slang", "testFloat64", defines, Shader::CompilerFlags::None, shaderModel); + ctx.createProgram("Tests/Slang/Float64Tests.cs.slang", "testFloat64", defines, Program::CompilerFlags::None, shaderModel); ctx.allocateStructuredBuffer("result", kNumElems); std::vector elems(kNumElems); @@ -65,12 +65,11 @@ void test(GPUUnitTestContext& ctx, const std::string& shaderModel, bool useUav) ctx.runProgram(kNumElems, 1, 1); // Verify results. - const uint64_t* result = ctx.mapBuffer("result"); + std::vector result = ctx.readBuffer("result"); for (uint32_t i = 0; i < kNumElems; i++) { EXPECT_EQ(result[i], elems[i]) << "i = " << i << " shaderModel=" << shaderModel; } - ctx.unmapBuffer("result"); } } // namespace diff --git a/Source/Tools/FalcorTest/Tests/Slang/InheritanceTests.cpp b/Source/Tools/FalcorTest/Tests/Slang/InheritanceTests.cpp index 5950e00cf..b1c963036 100644 --- a/Source/Tools/FalcorTest/Tests/Slang/InheritanceTests.cpp +++ b/Source/Tools/FalcorTest/Tests/Slang/InheritanceTests.cpp @@ -63,9 +63,9 @@ GPU_TEST(Inheritance_ManualCreate) { ref pDevice = ctx.getDevice(); - Program::DefineList defines; + DefineList defines; defines.add("NUM_TESTS", std::to_string(kNumTests)); - ctx.createProgram("Tests/Slang/InheritanceTests.cs.slang", "testInheritanceManual", defines, Shader::CompilerFlags::None, "6_5"); + ctx.createProgram("Tests/Slang/InheritanceTests.cs.slang", "testInheritanceManual", defines, Program::CompilerFlags::None, "6_5"); ctx.allocateStructuredBuffer("resultsInt", kNumTests); ctx.allocateStructuredBuffer("resultsFloat", kNumTests); @@ -99,23 +99,21 @@ GPU_TEST(Inheritance_ManualCreate) ctx.runProgram(kNumTests, 1, 1); // Verify results. - const int* resultsInt = ctx.mapBuffer("resultsInt"); - const float2* resultsFloat = ctx.mapBuffer("resultsFloat"); + std::vector resultsInt = ctx.readBuffer("resultsInt"); + std::vector resultsFloat = ctx.readBuffer("resultsFloat"); for (uint32_t i = 0; i < kNumTests; i++) { const auto expected = getCpuResult(testType[i], testValue[i], data[i]); EXPECT_EQ(resultsInt[i], expected.first) << "i = " << i; EXPECT_EQ(resultsFloat[i], expected.second) << "i = " << i; } - ctx.unmapBuffer("resultsInt"); - ctx.unmapBuffer("resultsFloat"); } GPU_TEST(Inheritance_ConformanceCreate) { ref pDevice = ctx.getDevice(); - Program::DefineList defines; + DefineList defines; defines.add("NUM_TESTS", std::to_string(kNumTests)); Program::Desc desc; desc.addShaderLibrary("Tests/Slang/InheritanceTests.cs.slang"); @@ -164,25 +162,23 @@ GPU_TEST(Inheritance_ConformanceCreate) ctx.runProgram(kNumTests, 1, 1); // Verify results. - const int* resultsInt = ctx.mapBuffer("resultsInt"); - const float2* resultsFloat = ctx.mapBuffer("resultsFloat"); + std::vector resultsInt = ctx.readBuffer("resultsInt"); + std::vector resultsFloat = ctx.readBuffer("resultsFloat"); for (uint32_t i = 0; i < kNumTests; i++) { const auto expected = getCpuResult(testType[i], testValue[i], data[i]); EXPECT_EQ(resultsInt[i], expected.first) << "i = " << i; EXPECT_EQ(resultsFloat[i], expected.second) << "i = " << i; } - ctx.unmapBuffer("resultsInt"); - ctx.unmapBuffer("resultsFloat"); } /// This correctly and reliably fails, but there is no way to automatically test it. // GPU_TEST(Inheritance_CheckInvalid) // { -// Program::DefineList defines; +// DefineList defines; // defines.add("NUM_TESTS", std::to_string(kNumTests)); // defines.add("COMPILE_WITH_ERROR", "1"); -// ctx.createProgram("Tests/Slang/InheritanceTests.cs.slang", "testInheritance", defines, Shader::CompilerFlags::None, "6_5"); +// ctx.createProgram("Tests/Slang/InheritanceTests.cs.slang", "testInheritance", defines, Program::CompilerFlags::None, "6_5"); // } } // namespace Falcor diff --git a/Source/Tools/FalcorTest/Tests/Slang/Int64Tests.cpp b/Source/Tools/FalcorTest/Tests/Slang/Int64Tests.cpp index 0b120f831..23c529cea 100644 --- a/Source/Tools/FalcorTest/Tests/Slang/Int64Tests.cpp +++ b/Source/Tools/FalcorTest/Tests/Slang/Int64Tests.cpp @@ -46,9 +46,9 @@ void test(GPUUnitTestContext& ctx, const std::string& shaderModel, bool useUav) { ref pDevice = ctx.getDevice(); - Program::DefineList defines = {{"USE_UAV", useUav ? "1" : "0"}}; + DefineList defines = {{"USE_UAV", useUav ? "1" : "0"}}; - ctx.createProgram("Tests/Slang/Int64Tests.cs.slang", "testInt64", defines, Shader::CompilerFlags::None, shaderModel); + ctx.createProgram("Tests/Slang/Int64Tests.cs.slang", "testInt64", defines, Program::CompilerFlags::None, shaderModel); ctx.allocateStructuredBuffer("result", kNumElems * 2); std::vector elems(kNumElems); @@ -64,7 +64,7 @@ void test(GPUUnitTestContext& ctx, const std::string& shaderModel, bool useUav) ctx.runProgram(kNumElems, 1, 1); // Verify results. - const uint32_t* result = ctx.mapBuffer("result"); + std::vector result = ctx.readBuffer("result"); for (uint32_t i = 0; i < kNumElems; i++) { uint32_t lo = result[2 * i]; @@ -72,7 +72,6 @@ void test(GPUUnitTestContext& ctx, const std::string& shaderModel, bool useUav) uint64_t res = ((uint64_t)hi << 32) | lo; EXPECT_EQ(res, elems[i]) << "i = " << i << " shaderModel=" << shaderModel; } - ctx.unmapBuffer("result"); } } // namespace diff --git a/Source/Tools/FalcorTest/Tests/Slang/NestedStructs.cpp b/Source/Tools/FalcorTest/Tests/Slang/NestedStructs.cpp index 6f2712abe..c0ef76bde 100644 --- a/Source/Tools/FalcorTest/Tests/Slang/NestedStructs.cpp +++ b/Source/Tools/FalcorTest/Tests/Slang/NestedStructs.cpp @@ -60,7 +60,7 @@ GPU_TEST(NestedStructs) ctx.runProgram(); - const uint32_t* result = ctx.mapBuffer("result"); + std::vector result = ctx.readBuffer("result"); EXPECT_EQ(result[0], asuint(1.1f)); EXPECT_EQ(result[1], 17); @@ -89,7 +89,5 @@ GPU_TEST(NestedStructs) EXPECT_EQ(result[24], asuint(1.65f)); EXPECT_EQ(result[25], 7); EXPECT_EQ(result[26], 3); - - ctx.unmapBuffer("result"); } } // namespace Falcor diff --git a/Source/Tools/FalcorTest/Tests/Slang/ShaderModel.cpp b/Source/Tools/FalcorTest/Tests/Slang/ShaderModel.cpp index 48d31fb41..889d85d88 100644 --- a/Source/Tools/FalcorTest/Tests/Slang/ShaderModel.cpp +++ b/Source/Tools/FalcorTest/Tests/Slang/ShaderModel.cpp @@ -35,16 +35,15 @@ const uint32_t kNumElems = 256; void test(GPUUnitTestContext& ctx, const std::string& shaderModel) { - ctx.createProgram("Tests/Slang/ShaderModel.cs.slang", "main", Program::DefineList(), Shader::CompilerFlags::None, shaderModel); + ctx.createProgram("Tests/Slang/ShaderModel.cs.slang", "main", DefineList(), Program::CompilerFlags::None, shaderModel); ctx.allocateStructuredBuffer("result", kNumElems); ctx.runProgram(kNumElems, 1, 1); - const uint32_t* result = ctx.mapBuffer("result"); + std::vector result = ctx.readBuffer("result"); for (uint32_t i = 0; i < kNumElems; i++) { EXPECT_EQ(result[i], 3 * i); } - ctx.unmapBuffer("result"); } } // namespace @@ -79,7 +78,7 @@ GPU_TEST(ShaderModel6_5) } #if FALCOR_HAS_D3D12_AGILITY_SDK -GPU_TEST_D3D12(ShaderModel6_6) +GPU_TEST(ShaderModel6_6, Device::Type::D3D12) { test(ctx, "6_6"); } diff --git a/Source/Tools/FalcorTest/Tests/Slang/ShaderString.cpp b/Source/Tools/FalcorTest/Tests/Slang/ShaderString.cpp index 07b0a2f79..8c0ea95a9 100644 --- a/Source/Tools/FalcorTest/Tests/Slang/ShaderString.cpp +++ b/Source/Tools/FalcorTest/Tests/Slang/ShaderString.cpp @@ -76,7 +76,7 @@ GPU_TEST(ShaderStringInline) desc.addShaderLibrary("Tests/Slang/ShaderStringInline.cs.slang").csEntry("main"); desc.addShaderString(kShaderModuleA, "ModuleA", "", false); - ctx.createProgram(desc, Program::DefineList()); + ctx.createProgram(desc, DefineList()); ctx.allocateStructuredBuffer("result", kSize); // Create and bind test data. @@ -95,12 +95,11 @@ GPU_TEST(ShaderStringInline) // Run program and validate results. ctx.runProgram(kSize, 1, 1); - const uint32_t* result = ctx.mapBuffer("result"); + std::vector result = ctx.readBuffer("result"); for (uint32_t i = 0; i < kSize; i++) { EXPECT_EQ(result[i], values[i] * 991); } - ctx.unmapBuffer("result"); } GPU_TEST(ShaderStringModule) @@ -111,18 +110,17 @@ GPU_TEST(ShaderStringModule) desc.addShaderString(kShaderModuleD, "GeneratedModule", "Tests/Slang/GeneratedModule.slang", true); desc.addShaderLibrary("Tests/Slang/ShaderStringModule.cs.slang").csEntry("main"); - ctx.createProgram(desc, Program::DefineList()); + ctx.createProgram(desc, DefineList()); ctx.allocateStructuredBuffer("result", kSize); // Run program and validate results. ctx.runProgram(kSize, 1, 1); - const uint32_t* result = ctx.mapBuffer("result"); + std::vector result = ctx.readBuffer("result"); for (uint32_t i = 0; i < kSize; i++) { EXPECT_EQ(result[i], i * 997); } - ctx.unmapBuffer("result"); } GPU_TEST(ShaderStringImport) @@ -133,18 +131,17 @@ GPU_TEST(ShaderStringImport) desc.addShaderLibrary("Tests/Slang/ShaderStringImport.cs.slang").csEntry("main"); desc.addShaderString(kShaderModuleC, "ModuleC", "", false); - ctx.createProgram(desc, Program::DefineList()); + ctx.createProgram(desc, DefineList()); ctx.allocateStructuredBuffer("result", kSize); // Run program and validate results. ctx.runProgram(kSize, 1, 1); - const uint32_t* result = ctx.mapBuffer("result"); + std::vector result = ctx.readBuffer("result"); for (uint32_t i = 0; i < kSize; i++) { EXPECT_EQ(result[i], i * 993); } - ctx.unmapBuffer("result"); } GPU_TEST(ShaderStringImportDuplicate, "Duplicate import not working") @@ -162,12 +159,11 @@ GPU_TEST(ShaderStringImportDuplicate, "Duplicate import not working") // Run program and validate results. ctx.runProgram(kSize, 1, 1); - const uint32_t* result = ctx.mapBuffer("result"); + std::vector result = ctx.readBuffer("result"); for (uint32_t i = 0; i < kSize; i++) { EXPECT_EQ(result[i], i * 993); } - ctx.unmapBuffer("result"); } GPU_TEST(ShaderStringImported) @@ -178,18 +174,17 @@ GPU_TEST(ShaderStringImported) desc.addShaderString(kShaderModuleD, "GeneratedModule", "Tests/Slang/GeneratedModule.slang", true); desc.addShaderLibrary("Tests/Slang/ShaderStringImported.cs.slang").csEntry("main"); - ctx.createProgram(desc, Program::DefineList()); + ctx.createProgram(desc, DefineList()); ctx.allocateStructuredBuffer("result", kSize); // Run program and validate results. ctx.runProgram(kSize, 1, 1); - const uint32_t* result = ctx.mapBuffer("result"); + std::vector result = ctx.readBuffer("result"); for (uint32_t i = 0; i < kSize; i++) { EXPECT_EQ(result[i], i * 997); } - ctx.unmapBuffer("result"); } GPU_TEST(ShaderStringDynamicObject) @@ -206,7 +201,7 @@ GPU_TEST(ShaderStringDynamicObject) Program::TypeConformanceList typeConformances = Program::TypeConformanceList{{{"DynamicType", "IDynamicType"}, typeID}}; desc.addTypeConformances(typeConformances); - ctx.createProgram(desc, Program::DefineList()); + ctx.createProgram(desc, DefineList()); ctx.allocateStructuredBuffer("result", kSize); auto var = ctx.vars().getRootVar(); @@ -215,11 +210,10 @@ GPU_TEST(ShaderStringDynamicObject) // Run program and validate results. ctx.runProgram(kSize, 1, 1); - const uint32_t* result = ctx.mapBuffer("result"); + std::vector result = ctx.readBuffer("result"); for (uint32_t i = 0; i < kSize; i++) { EXPECT_EQ(result[i], i * 997); } - ctx.unmapBuffer("result"); } } // namespace Falcor diff --git a/Source/Tools/FalcorTest/Tests/Slang/SlangInheritance.cpp b/Source/Tools/FalcorTest/Tests/Slang/SlangInheritance.cpp index 2db2b0dca..70baad254 100644 --- a/Source/Tools/FalcorTest/Tests/Slang/SlangInheritance.cpp +++ b/Source/Tools/FalcorTest/Tests/Slang/SlangInheritance.cpp @@ -32,7 +32,7 @@ namespace Falcor { GPU_TEST(SlangStructInheritanceReflection, "Not working yet") { - ctx.createProgram("Tests/Slang/SlangInheritance.cs.slang", "main", Program::DefineList(), Shader::CompilerFlags::None, "6_5"); + ctx.createProgram("Tests/Slang/SlangInheritance.cs.slang", "main", DefineList(), Program::CompilerFlags::None, "6_5"); // Reflection of struct A. auto typeA = ctx.getProgram()->getReflector()->findType("A"); @@ -80,7 +80,7 @@ GPU_TEST(SlangStructInheritanceLayout) { ref pDevice = ctx.getDevice(); - ctx.createProgram("Tests/Slang/SlangInheritance.cs.slang", "main", Program::DefineList(), Shader::CompilerFlags::None, "6_5"); + ctx.createProgram("Tests/Slang/SlangInheritance.cs.slang", "main", DefineList(), Program::CompilerFlags::None, "6_5"); ShaderVar var = ctx.vars().getRootVar(); // TODO: Use built-in buffer when reflection of struct inheritance works (see #1306). @@ -110,7 +110,7 @@ GPU_TEST(SlangStructInheritanceLayout) EXPECT_EQ(offsetof(B, scalar), 0); EXPECT_EQ(offsetof(B, vector), 4); - // const uint32_t* result = ctx.mapBuffer("result"); + // std::vector result = ctx.readBuffer("result"); const uint32_t* result = (const uint32_t*)pResult->map(Buffer::MapType::Read); // Check struct fields read back from the GPU. @@ -119,8 +119,5 @@ GPU_TEST(SlangStructInheritanceLayout) EXPECT_EQ(result[1], initData[1]); EXPECT_EQ(result[2], initData[2]); EXPECT_EQ(result[3], initData[3]); - - // ctx.unmapBuffer("result"); - pResult->unmap(); } } // namespace Falcor diff --git a/Source/Tools/FalcorTest/Tests/Slang/SlangMutatingTests.cpp b/Source/Tools/FalcorTest/Tests/Slang/SlangMutatingTests.cpp index d57d8414d..3436140f7 100644 --- a/Source/Tools/FalcorTest/Tests/Slang/SlangMutatingTests.cpp +++ b/Source/Tools/FalcorTest/Tests/Slang/SlangMutatingTests.cpp @@ -33,7 +33,7 @@ GPU_TEST(SlangMutating) { ref pDevice = ctx.getDevice(); - ctx.createProgram("Tests/Slang/SlangMutatingTests.cs.slang", "main", Program::DefineList(), Shader::CompilerFlags::None, "6_3"); + ctx.createProgram("Tests/Slang/SlangMutatingTests.cs.slang", "main", DefineList(), Program::CompilerFlags::None, "6_3"); ctx.allocateStructuredBuffer("result", 1); ShaderVar var = ctx.vars().getRootVar(); @@ -42,8 +42,7 @@ GPU_TEST(SlangMutating) ctx.runProgram(); - const uint32_t* result = ctx.mapBuffer("result"); + std::vector result = ctx.readBuffer("result"); EXPECT_EQ(result[0], 33); - ctx.unmapBuffer("result"); } } // namespace Falcor diff --git a/Source/Tools/FalcorTest/Tests/Slang/SlangReinterpretCast.cpp b/Source/Tools/FalcorTest/Tests/Slang/SlangReinterpretCast.cpp index 4d9f7281c..42e5d5ce3 100644 --- a/Source/Tools/FalcorTest/Tests/Slang/SlangReinterpretCast.cpp +++ b/Source/Tools/FalcorTest/Tests/Slang/SlangReinterpretCast.cpp @@ -48,7 +48,7 @@ GPU_TEST(SlangReinterpretCast) { ref pDevice = ctx.getDevice(); - ctx.createProgram("Tests/Slang/SlangReinterpretCast.cs.slang", "main", Program::DefineList(), Shader::CompilerFlags::None, "6_5"); + ctx.createProgram("Tests/Slang/SlangReinterpretCast.cs.slang", "main", DefineList(), Program::CompilerFlags::None, "6_5"); ctx.allocateStructuredBuffer("resultA", kElems); ctx.allocateStructuredBuffer("resultB", kElems); ctx.allocateStructuredBuffer("resultC", kElems); @@ -81,7 +81,7 @@ GPU_TEST(SlangReinterpretCast) ctx.runProgram(kElems); // Verify final result matches our input. - const A* result = ctx.mapBuffer("resultA"); + std::vector result = ctx.readBuffer("resultA"); for (size_t i = 0; i < data.size(); i++) { EXPECT_EQ(result[i].a, data[i].a); @@ -101,18 +101,16 @@ GPU_TEST(SlangReinterpretCast) EXPECT_EQ((float)result[i].j.x, (float)data[i].j.x); EXPECT_EQ((float)result[i].j.y, (float)data[i].j.y); } - ctx.unmapBuffer("resultA"); // Verify the intermediate results. We'll just do a binary comparison for simplicity. auto verify = [&](const char* bufferName) { - const uint32_t* result = ctx.mapBuffer(bufferName); + std::vector result = ctx.readBuffer(bufferName); const uint32_t* rawData = reinterpret_cast(data.data()); for (size_t i = 0; i < data.size() * sizeof(data[0]) / 4; i++) { EXPECT_EQ(result[i], rawData[i]) << "i = " << i << " buffer " << bufferName; } - ctx.unmapBuffer(bufferName); }; verify("resultA"); diff --git a/Source/Tools/FalcorTest/Tests/Slang/SlangTests.cpp b/Source/Tools/FalcorTest/Tests/Slang/SlangTests.cpp index 6690226b5..f289d040d 100644 --- a/Source/Tools/FalcorTest/Tests/Slang/SlangTests.cpp +++ b/Source/Tools/FalcorTest/Tests/Slang/SlangTests.cpp @@ -41,12 +41,12 @@ namespace { void testEnum(GPUUnitTestContext& ctx, const std::string& shaderModel) { - ctx.createProgram("Tests/Slang/SlangTests.cs.slang", "testEnum", Program::DefineList(), Shader::CompilerFlags::None, shaderModel); + ctx.createProgram("Tests/Slang/SlangTests.cs.slang", "testEnum", DefineList(), Program::CompilerFlags::None, shaderModel); ctx.allocateStructuredBuffer("result", 12); ctx.runProgram(1, 1, 1); // Verify results. - const uint32_t* result = ctx.mapBuffer("result"); + std::vector result = ctx.readBuffer("result"); EXPECT_EQ(result[0], (uint32_t)Type1::A); EXPECT_EQ(result[1], (uint32_t)Type1::B); @@ -62,8 +62,6 @@ void testEnum(GPUUnitTestContext& ctx, const std::string& shaderModel) EXPECT_EQ(result[9], (uint32_t)Type3::B); EXPECT_EQ(result[10], (uint32_t)Type3::C); EXPECT_EQ(result[11], (uint32_t)Type3::D); - - ctx.unmapBuffer("result"); } uint64_t asuint64(double a) @@ -95,12 +93,12 @@ GPU_TEST(SlangScalarTypes) { const uint32_t maxTests = 100; - ctx.createProgram("Tests/Slang/SlangTests.cs.slang", "testScalarTypes", Program::DefineList(), Shader::CompilerFlags::None, "6_2"); + ctx.createProgram("Tests/Slang/SlangTests.cs.slang", "testScalarTypes", DefineList(), Program::CompilerFlags::None, "6_2"); ctx.allocateStructuredBuffer("result", maxTests); ctx.runProgram(1, 1, 1); // Verify results. - const uint32_t* result = ctx.mapBuffer("result"); + std::vector result = ctx.readBuffer("result"); int i = 0; @@ -155,7 +153,6 @@ GPU_TEST(SlangScalarTypes) EXPECT_EQ(result[i], 0x12345678); i++; - ctx.unmapBuffer("result"); FALCOR_ASSERT(i < maxTests); } @@ -169,13 +166,13 @@ GPU_TEST(SlangDefaultInitializers) auto test = [&](const std::string& shaderModel) { ctx.createProgram( - "Tests/Slang/SlangTests.cs.slang", "testDefaultInitializers", Program::DefineList(), Shader::CompilerFlags::None, shaderModel + "Tests/Slang/SlangTests.cs.slang", "testDefaultInitializers", DefineList(), Program::CompilerFlags::None, shaderModel ); ctx.allocateStructuredBuffer("result", maxTests, initData.data(), initData.size() * sizeof(initData[0])); ctx.runProgram(1, 1, 1); // Verify results. - const uint32_t* result = ctx.mapBuffer("result"); + std::vector result = ctx.readBuffer("result"); for (uint32_t i = 0; i < maxTests; i++) { uint32_t expected = i < usedTests ? 0 : -1; @@ -184,7 +181,6 @@ GPU_TEST(SlangDefaultInitializers) EXPECT_EQ(result[i], expected) << "i = " << i << " (sm" << shaderModel << ")"; } - ctx.unmapBuffer("result"); }; // Test the default shader model, followed by specific models. @@ -213,13 +209,11 @@ GPU_TEST(SlangHashedStrings) EXPECT_EQ(hashedStrings[2].string, "Test String 2"); EXPECT_EQ(hashedStrings[3].string, "Test String 3"); - const uint32_t* result = ctx.mapBuffer("result"); + std::vector result = ctx.readBuffer("result"); for (size_t i = 0; i < 4; ++i) { EXPECT_EQ(result[i], hashedStrings[i].hash); } - - ctx.unmapBuffer("result"); } } // namespace Falcor diff --git a/Source/Tools/FalcorTest/Tests/Slang/StructuredBufferMatrix.cpp b/Source/Tools/FalcorTest/Tests/Slang/StructuredBufferMatrix.cpp index 4f00fd234..c77f4bfe5 100644 --- a/Source/Tools/FalcorTest/Tests/Slang/StructuredBufferMatrix.cpp +++ b/Source/Tools/FalcorTest/Tests/Slang/StructuredBufferMatrix.cpp @@ -32,13 +32,13 @@ namespace Falcor { namespace { -void runTest2(GPUUnitTestContext& ctx, Program::DefineList defines) +void runTest2(GPUUnitTestContext& ctx, DefineList defines) { ref pDevice = ctx.getDevice(); ctx.createProgram( - "Tests/Slang/StructuredBufferMatrix.cs.slang", "testStructuredBufferMatrixLoad2", defines, Shader::CompilerFlags::DumpIntermediates, - "6_5" + "Tests/Slang/StructuredBufferMatrix.cs.slang", "testStructuredBufferMatrixLoad2", defines, + Program::CompilerFlags::DumpIntermediates, "6_5" ); ctx.allocateStructuredBuffer("result", 16); @@ -59,12 +59,11 @@ void runTest2(GPUUnitTestContext& ctx, Program::DefineList defines) ctx.runProgram(1, 1, 1); // Verify results. - const float* result = ctx.mapBuffer("result"); + std::vector result = ctx.readBuffer("result"); for (size_t i = 0; i < 16; i++) { EXPECT_EQ(result[i], (float)i + 0.75f) << "i = " << i; } - ctx.unmapBuffer("result"); } } // namespace @@ -73,8 +72,7 @@ GPU_TEST(StructuredBufferMatrixLoad1) ref pDevice = ctx.getDevice(); ctx.createProgram( - "Tests/Slang/StructuredBufferMatrix.cs.slang", "testStructuredBufferMatrixLoad1", Program::DefineList(), - Shader::CompilerFlags::None, "6_5" + "Tests/Slang/StructuredBufferMatrix.cs.slang", "testStructuredBufferMatrixLoad1", DefineList(), Program::CompilerFlags::None, "6_5" ); ctx.allocateStructuredBuffer("result", 32); @@ -97,31 +95,30 @@ GPU_TEST(StructuredBufferMatrixLoad1) ctx.runProgram(1, 1, 1); // Verify results. - const float* result = ctx.mapBuffer("result"); + std::vector result = ctx.readBuffer("result"); for (size_t i = 0; i < 32; i++) { EXPECT_EQ(result[i], (float)i + 0.5f) << "i = " << i; } - ctx.unmapBuffer("result"); } GPU_TEST(StructuredBufferMatrixLoad2_1) { - Program::DefineList defines = {{"LAYOUT", "1"}}; + DefineList defines = {{"LAYOUT", "1"}}; runTest2(ctx, defines); } // TODO: Enable when https://github.com/microsoft/DirectXShaderCompiler/issues/4492 has been resolved. GPU_TEST(StructuredBufferMatrixLoad2_2, "Disabled due to compiler bug") { - Program::DefineList defines = {{"LAYOUT", "2"}}; + DefineList defines = {{"LAYOUT", "2"}}; runTest2(ctx, defines); } // TODO: Enable when https://github.com/microsoft/DirectXShaderCompiler/issues/4492 has been resolved. GPU_TEST(StructuredBufferMatrixLoad2_3, "Disabled due to compiler bug") { - Program::DefineList defines = {{"LAYOUT", "3"}}; + DefineList defines = {{"LAYOUT", "3"}}; runTest2(ctx, defines); } } // namespace Falcor diff --git a/Source/Tools/FalcorTest/Tests/Slang/TemplatedLoad.cpp b/Source/Tools/FalcorTest/Tests/Slang/TemplatedLoad.cpp index 0bdccdcc2..81557b015 100644 --- a/Source/Tools/FalcorTest/Tests/Slang/TemplatedLoad.cpp +++ b/Source/Tools/FalcorTest/Tests/Slang/TemplatedLoad.cpp @@ -50,7 +50,7 @@ void test(GPUUnitTestContext& ctx, const std::string& entryPoint, const size_t n std::vector elems = generateData(n); - ctx.createProgram("Tests/Slang/TemplatedLoad.cs.slang", entryPoint, Program::DefineList(), Shader::CompilerFlags::None, "6_5"); + ctx.createProgram("Tests/Slang/TemplatedLoad.cs.slang", entryPoint, DefineList(), Program::CompilerFlags::None, "6_5"); ctx.allocateStructuredBuffer("result", (uint32_t)elems.size()); auto var = ctx.vars().getRootVar(); @@ -60,12 +60,11 @@ void test(GPUUnitTestContext& ctx, const std::string& entryPoint, const size_t n ctx.runProgram(1, 1, 1); // Verify results. - const uint16_t* result = ctx.mapBuffer("result"); + std::vector result = ctx.readBuffer("result"); for (size_t i = 0; i < elems.size(); i++) { EXPECT_EQ(result[i], elems[i]) << "i = " << i; } - ctx.unmapBuffer("result"); } } // namespace diff --git a/Source/Tools/FalcorTest/Tests/Slang/TraceRayFlags.cpp b/Source/Tools/FalcorTest/Tests/Slang/TraceRayFlags.cpp index 0b1dcfd33..0b640e826 100644 --- a/Source/Tools/FalcorTest/Tests/Slang/TraceRayFlags.cpp +++ b/Source/Tools/FalcorTest/Tests/Slang/TraceRayFlags.cpp @@ -45,7 +45,7 @@ void testRayFlags(GPUUnitTestContext& ctx, bool useDXR_1_1) (uint32_t)RayFlags::CullNonOpaque, }; - Program::DefineList defines; + DefineList defines; std::string shaderModel = "6_3"; if (useDXR_1_1) @@ -56,16 +56,15 @@ void testRayFlags(GPUUnitTestContext& ctx, bool useDXR_1_1) shaderModel = "6_5"; } - ctx.createProgram("Tests/Slang/TraceRayFlags.cs.slang", "testRayFlags", defines, Shader::CompilerFlags::None, shaderModel); + ctx.createProgram("Tests/Slang/TraceRayFlags.cs.slang", "testRayFlags", defines, Program::CompilerFlags::None, shaderModel); ctx.allocateStructuredBuffer("result", (uint32_t)expected.size()); ctx.runProgram(1, 1, 1); - const uint32_t* result = ctx.mapBuffer("result"); + std::vector result = ctx.readBuffer("result"); for (size_t i = 0; i < expected.size(); ++i) { EXPECT_EQ(result[i], expected[i]); } - ctx.unmapBuffer("result"); } } // namespace diff --git a/Source/Tools/FalcorTest/Tests/Slang/TraceRayInline.cpp b/Source/Tools/FalcorTest/Tests/Slang/TraceRayInline.cpp index 7adae6cf2..fea680166 100644 --- a/Source/Tools/FalcorTest/Tests/Slang/TraceRayInline.cpp +++ b/Source/Tools/FalcorTest/Tests/Slang/TraceRayInline.cpp @@ -32,8 +32,6 @@ namespace Falcor GPU_TEST(testTraceRayInlineAPI) { // We don't actually run the program, just make sure it compiles. - ctx.createProgram( - "Tests/Slang/TraceRayInline.cs.slang", "testTraceRayInlineAPI", Program::DefineList(), Shader::CompilerFlags::None, "6_5" - ); + ctx.createProgram("Tests/Slang/TraceRayInline.cs.slang", "testTraceRayInlineAPI", DefineList(), Program::CompilerFlags::None, "6_5"); } } // namespace Falcor diff --git a/Source/Tools/FalcorTest/Tests/Slang/UnboundedDescriptorArray.cpp b/Source/Tools/FalcorTest/Tests/Slang/UnboundedDescriptorArray.cpp index cf5129693..b0b970bfd 100644 --- a/Source/Tools/FalcorTest/Tests/Slang/UnboundedDescriptorArray.cpp +++ b/Source/Tools/FalcorTest/Tests/Slang/UnboundedDescriptorArray.cpp @@ -35,7 +35,7 @@ GPU_TEST(UnboundedDescriptorArray, "Unbounded arrays are not yet supported") const uint32_t kTexCount = 4; - ctx.createProgram("Tests/Slang/UnboundedDescriptorArray.cs.slang", "main", Program::DefineList(), Shader::CompilerFlags::None, "6_5"); + ctx.createProgram("Tests/Slang/UnboundedDescriptorArray.cs.slang", "main", DefineList(), Program::CompilerFlags::None, "6_5"); ctx.allocateStructuredBuffer("result", kTexCount); auto var = ctx.vars().getRootVar()["resources"]; @@ -47,12 +47,11 @@ GPU_TEST(UnboundedDescriptorArray, "Unbounded arrays are not yet supported") ctx.runProgram(kTexCount, 1, 1); - const float* result = ctx.mapBuffer("result"); + std::vector result = ctx.readBuffer("result"); for (size_t i = 0; i < kTexCount; i++) { float expected = (float)(i + 1); EXPECT_EQ(result[i], expected) << "i = " << i; } - ctx.unmapBuffer("result"); } } // namespace Falcor diff --git a/Source/Tools/FalcorTest/Tests/Slang/WaveOps.cpp b/Source/Tools/FalcorTest/Tests/Slang/WaveOps.cpp index bf5a922fd..0ef6dd08c 100644 --- a/Source/Tools/FalcorTest/Tests/Slang/WaveOps.cpp +++ b/Source/Tools/FalcorTest/Tests/Slang/WaveOps.cpp @@ -114,8 +114,8 @@ void testWaveMinMax(GPUUnitTestContext& ctx, bool conditional) { ref pDevice = ctx.getDevice(); - Program::DefineList defines = {{"CONDITIONAL", conditional ? "1" : "0"}}; - ctx.createProgram(kShaderFilename, "testWaveMinMax", defines, Shader::CompilerFlags::None, "6_0"); + DefineList defines = {{"CONDITIONAL", conditional ? "1" : "0"}}; + ctx.createProgram(kShaderFilename, "testWaveMinMax", defines, Program::CompilerFlags::None, "6_0"); ctx.allocateStructuredBuffer("result", kNumElems * 2); auto var = ctx.vars().getRootVar(); @@ -147,20 +147,19 @@ void testWaveMinMax(GPUUnitTestContext& ctx, bool conditional) std::vector expectedResult = computeMinMaxResult(testData, laneCount, conditional); FALCOR_ASSERT(expectedResult.size() == testData.size() * 2); - const float4* result = ctx.mapBuffer("result"); + std::vector result = ctx.readBuffer("result"); for (size_t i = 0; i < testData.size(); i++) { EXPECT_EQ(result[2 * i + 0].x, expectedResult[2 * i + 0]) << "WaveActiveMin (i = " << i << ")"; EXPECT_EQ(result[2 * i + 1].x, expectedResult[2 * i + 1]) << "WaveActiveMax (i = " << i << ")"; } - ctx.unmapBuffer("result"); } uint32_t queryLaneCount(GPUUnitTestContext& ctx) { ref pDevice = ctx.getDevice(); - ctx.createProgram(kShaderFilename, "testWaveGetLaneCount", Program::DefineList(), Shader::CompilerFlags::None, "6_0"); + ctx.createProgram(kShaderFilename, "testWaveGetLaneCount", DefineList(), Program::CompilerFlags::None, "6_0"); auto var = ctx.vars().getRootVar(); uint32_t zero = 0; @@ -184,11 +183,11 @@ GPU_TEST(WaveGetLaneCount) } // WaveMatch intrinsic is available only on D3D12. -GPU_TEST_D3D12(WaveMatch) +GPU_TEST(WaveMatch, Device::Type::D3D12) { ref pDevice = ctx.getDevice(); - ctx.createProgram(kShaderFilename, "testWaveMatch", Program::DefineList(), Shader::CompilerFlags::None, "6_5"); + ctx.createProgram(kShaderFilename, "testWaveMatch", DefineList(), Program::CompilerFlags::None, "6_5"); ctx.allocateStructuredBuffer("result", kNumElems); auto var = ctx.vars().getRootVar(); @@ -213,7 +212,7 @@ GPU_TEST_D3D12(WaveMatch) std::vector expectedResult = computeMatchResult(matchData, laneCount); FALCOR_ASSERT(expectedResult.size() == matchData.size()); - const uint4* result = ctx.mapBuffer("result"); + std::vector result = ctx.readBuffer("result"); for (size_t i = 0; i < matchData.size(); i++) { EXPECT_EQ(result[i].x, expectedResult[i].x) << "i = " << i; @@ -221,7 +220,6 @@ GPU_TEST_D3D12(WaveMatch) EXPECT_EQ(result[i].z, expectedResult[i].z) << "i = " << i; EXPECT_EQ(result[i].w, expectedResult[i].w) << "i = " << i; } - ctx.unmapBuffer("result"); } GPU_TEST(WaveMinMax) @@ -245,13 +243,13 @@ GPU_TEST(WaveMaxSimpleFloat, "Disabled due to compiler issues") ref pDevice = ctx.getDevice(); if (uint32_t laneCount = queryLaneCount(ctx); laneCount != 32) - throw SkippingTestException("Test assumes warp size 32"); + ctx.skip("Test assumes warp size 32"); std::vector testData(32); for (size_t i = 0; i < testData.size(); i++) testData[i] = (float)i - 15; - ctx.createProgram(kShaderFilename, "testWaveMaxSimpleFloat", Program::DefineList(), Shader::CompilerFlags::None, "6_0"); + ctx.createProgram(kShaderFilename, "testWaveMaxSimpleFloat", DefineList(), Program::CompilerFlags::None, "6_0"); ctx.allocateStructuredBuffer("result", 32); auto var = ctx.vars().getRootVar(); @@ -260,13 +258,12 @@ GPU_TEST(WaveMaxSimpleFloat, "Disabled due to compiler issues") ctx.runProgram(32, 1, 1); // Verify result. - const float4* result = ctx.mapBuffer("result"); + std::vector result = ctx.readBuffer("result"); for (size_t i = 0; i < 32; i++) { float expected = testData[i] <= -2.f ? -2.f : testData[i]; EXPECT_EQ(result[i].x, expected) << "i = " << i; } - ctx.unmapBuffer("result"); } GPU_TEST(WaveMaxSimpleInt) @@ -280,13 +277,13 @@ GPU_TEST(WaveMaxSimpleInt) ref pDevice = ctx.getDevice(); if (uint32_t laneCount = queryLaneCount(ctx); laneCount != 32) - throw SkippingTestException("Test assumes warp size 32"); + ctx.skip("Test assumes warp size 32"); std::vector testData(32); for (size_t i = 0; i < testData.size(); i++) testData[i] = (int)i - 15; - ctx.createProgram(kShaderFilename, "testWaveMaxSimpleInt", Program::DefineList(), Shader::CompilerFlags::None, "6_0"); + ctx.createProgram(kShaderFilename, "testWaveMaxSimpleInt", DefineList(), Program::CompilerFlags::None, "6_0"); ctx.allocateStructuredBuffer("result", 32); auto var = ctx.vars().getRootVar(); @@ -295,12 +292,11 @@ GPU_TEST(WaveMaxSimpleInt) ctx.runProgram(32, 1, 1); // Verify result. - const int4* result = ctx.mapBuffer("result"); + std::vector result = ctx.readBuffer("result"); for (size_t i = 0; i < 32; i++) { int expected = testData[i] <= -2 ? -2 : testData[i]; EXPECT_EQ(result[i].x, expected) << "i = " << i; } - ctx.unmapBuffer("result"); } } // namespace Falcor diff --git a/Source/Tools/FalcorTest/Tests/Utils/AABBTests.cpp b/Source/Tools/FalcorTest/Tests/Utils/AABBTests.cpp index 47924100e..90fc94c36 100644 --- a/Source/Tools/FalcorTest/Tests/Utils/AABBTests.cpp +++ b/Source/Tools/FalcorTest/Tests/Utils/AABBTests.cpp @@ -52,7 +52,7 @@ GPU_TEST(AABB) ctx.runProgram(); // Verify results. - const float3* result = ctx.mapBuffer("result"); + std::vector result = ctx.readBuffer("result"); size_t i = 0; // Test 0 @@ -211,6 +211,5 @@ GPU_TEST(AABB) ++i; FALCOR_ASSERT(i <= resultSize); - ctx.unmapBuffer("result"); } } // namespace Falcor diff --git a/Source/Tools/FalcorTest/Tests/Utils/BitTricksTests.cpp b/Source/Tools/FalcorTest/Tests/Utils/BitTricksTests.cpp index 6884fc3fa..2f286a7a5 100644 --- a/Source/Tools/FalcorTest/Tests/Utils/BitTricksTests.cpp +++ b/Source/Tools/FalcorTest/Tests/Utils/BitTricksTests.cpp @@ -73,7 +73,7 @@ GPU_TEST(BitInterleave) ctx.runProgram(n); // Verify results. - const uint32_t* result = ctx.mapBuffer("result"); + std::vector result = ctx.readBuffer("result"); for (uint32_t i = 0; i < n; i++) { const uint32_t bits = testData[i]; @@ -88,6 +88,5 @@ GPU_TEST(BitInterleave) EXPECT_EQ(result[tests * i + 3], (bits & 0x000f000f)); EXPECT_EQ(result[tests * i + 4], (bits & 0x0f0f0f0f)); } - ctx.unmapBuffer("result"); } } // namespace Falcor diff --git a/Source/Tools/FalcorTest/Tests/Utils/BitonicSortTests.cpp b/Source/Tools/FalcorTest/Tests/Utils/BitonicSortTests.cpp index e0f7677d6..e1d7a5e07 100644 --- a/Source/Tools/FalcorTest/Tests/Utils/BitonicSortTests.cpp +++ b/Source/Tools/FalcorTest/Tests/Utils/BitonicSortTests.cpp @@ -78,9 +78,9 @@ void testGpuSort(GPUUnitTestContext& ctx, BitonicSort& bitonicSort, const uint32 } // namespace #if FALCOR_NVAPI_AVAILABLE -GPU_TEST_D3D12(BitonicSort) +GPU_TEST(BitonicSort, Device::Type::D3D12) #else -GPU_TEST_D3D12(BitonicSort, "Requires NVAPI") +GPU_TEST(BitonicSort, Device::Type::D3D12, "Requires NVAPI") #endif { // Create utility class for sorting. diff --git a/Source/Tools/FalcorTest/Tests/Utils/Color/SpectrumUtilsTests.cpp b/Source/Tools/FalcorTest/Tests/Utils/Color/SpectrumUtilsTests.cpp index c845e3293..e119e948e 100644 --- a/Source/Tools/FalcorTest/Tests/Utils/Color/SpectrumUtilsTests.cpp +++ b/Source/Tools/FalcorTest/Tests/Utils/Color/SpectrumUtilsTests.cpp @@ -61,7 +61,7 @@ GPU_TEST(WavelengthToXYZ) // Verify results. float3 maxSqrError = {}; - const float3* result = ctx.mapBuffer("result"); + std::vector result = ctx.readBuffer("result"); for (uint32_t i = 0; i < n; i++) { float lambda = wavelengths[i]; @@ -75,7 +75,6 @@ GPU_TEST(WavelengthToXYZ) float3 e = ref - res; maxSqrError = max(maxSqrError, e * e); } - ctx.unmapBuffer("result"); EXPECT_LE(maxSqrError.x, 2.0e-4f); EXPECT_LE(maxSqrError.y, 6.6e-5f); diff --git a/Source/Tools/FalcorTest/Tests/Utils/Debug/WarpProfilerTests.cpp b/Source/Tools/FalcorTest/Tests/Utils/Debug/WarpProfilerTests.cpp index 30a15ad5a..5e8ed73b3 100644 --- a/Source/Tools/FalcorTest/Tests/Utils/Debug/WarpProfilerTests.cpp +++ b/Source/Tools/FalcorTest/Tests/Utils/Debug/WarpProfilerTests.cpp @@ -30,7 +30,7 @@ namespace Falcor { -GPU_TEST_D3D12(WarpProfiler) +GPU_TEST(WarpProfiler, Device::Type::D3D12) { WarpProfiler profiler(ctx.getDevice(), 4); diff --git a/Source/Tools/FalcorTest/Tests/Utils/GeometryHelpersTests.cpp b/Source/Tools/FalcorTest/Tests/Utils/GeometryHelpersTests.cpp index d0f849188..c0b4cfab4 100644 --- a/Source/Tools/FalcorTest/Tests/Utils/GeometryHelpersTests.cpp +++ b/Source/Tools/FalcorTest/Tests/Utils/GeometryHelpersTests.cpp @@ -120,8 +120,8 @@ void testKnownBBoxes(GPUUnitTestContext& ctx, const char* entrypoint) runBBoxTestComputeShader(ctx, testCases, nTests, entrypoint); - const float* sinTheta = ctx.mapBuffer("sinTheta"); - const float* cosTheta = ctx.mapBuffer("cosTheta"); + std::vector sinTheta = ctx.readBuffer("sinTheta"); + std::vector cosTheta = ctx.readBuffer("cosTheta"); for (int i = 0; i < nTests; ++i) { @@ -141,9 +141,6 @@ void testKnownBBoxes(GPUUnitTestContext& ctx, const char* entrypoint) << "BBoxTestCase " << i << ", expected cos(theta) = " << std::cos(testCases[i].angle) << ", got " << cosTheta[i]; } } - - ctx.unmapBuffer("cosTheta"); - ctx.unmapBuffer("sinTheta"); } void testRandomBBoxes(GPUUnitTestContext& ctx, const char* entrypoint) @@ -168,9 +165,9 @@ void testRandomBBoxes(GPUUnitTestContext& ctx, const char* entrypoint) runBBoxTestComputeShader(ctx, testCases.data(), static_cast(testCases.size()), entrypoint); - const float* sinTheta = ctx.mapBuffer("sinTheta"); - const float* cosTheta = ctx.mapBuffer("cosTheta"); - const float3* coneDir = ctx.mapBuffer("coneDir"); + std::vector sinTheta = ctx.readBuffer("sinTheta"); + std::vector cosTheta = ctx.readBuffer("cosTheta"); + std::vector coneDir = ctx.readBuffer("coneDir"); for (size_t i = 0; i < testCases.size(); ++i) { @@ -205,15 +202,12 @@ void testRandomBBoxes(GPUUnitTestContext& ctx, const char* entrypoint) EXPECT_LT(minCosTheta, (cosTheta[i] > 0 ? (1.01f * cosTheta[i]) : (0.99f * cosTheta[i]))); } } - - ctx.unmapBuffer("cosTheta"); - ctx.unmapBuffer("sinTheta"); } } // namespace -// This test is currently disabled for Vulkan as the float precision is not exact (Shader::CompilerFlags::FloatingPointModePrecise not +// This test is currently disabled for Vulkan as the float precision is not exact (Program::CompilerFlags::FloatingPointModePrecise not // supported?). -GPU_TEST_D3D12(ComputeRayOrigin) +GPU_TEST(ComputeRayOrigin, Device::Type::D3D12) { const uint32_t nTests = 1 << 16; @@ -232,7 +226,7 @@ GPU_TEST_D3D12(ComputeRayOrigin) } // Setup and run GPU test. - ctx.createProgram(kShaderFilename, "testComputeRayOrigin", Program::DefineList(), Shader::CompilerFlags::FloatingPointModePrecise); + ctx.createProgram(kShaderFilename, "testComputeRayOrigin", DefineList(), Program::CompilerFlags::FloatingPointModePrecise); ctx.allocateStructuredBuffer("result", nTests); ctx.allocateStructuredBuffer("pos", nTests, testPositions.data(), testPositions.size() * sizeof(float3)); ctx.allocateStructuredBuffer("normal", nTests, testNormals.data(), testNormals.size() * sizeof(float3)); @@ -240,13 +234,12 @@ GPU_TEST_D3D12(ComputeRayOrigin) ctx.runProgram(nTests); // Verify results. - const float3* result = ctx.mapBuffer("result"); + std::vector result = ctx.readBuffer("result"); for (uint32_t i = 0; i < nTests; i++) { float3 ref = offset_ray(testPositions[i], testNormals[i]); EXPECT_EQ(result[i], ref) << "i = " << i; } - ctx.unmapBuffer("result"); } GPU_TEST(BoxSubtendedConeAngleCenter) @@ -310,8 +303,8 @@ GPU_TEST(SphereSubtendedAngle) ctx.allocateStructuredBuffer("cosTheta", nTests); ctx.runProgram(nTests); - const float* sinTheta = ctx.mapBuffer("sinTheta"); - const float* cosTheta = ctx.mapBuffer("cosTheta"); + std::vector sinTheta = ctx.readBuffer("sinTheta"); + std::vector cosTheta = ctx.readBuffer("cosTheta"); for (int i = 0; i < nTests; ++i) { @@ -333,9 +326,6 @@ GPU_TEST(SphereSubtendedAngle) << ", " << tc.origin.y << ", " << tc.origin.z << "), radius " << tc.radius; } } - - ctx.unmapBuffer("cosTheta"); - ctx.unmapBuffer("sinTheta"); } GPU_TEST(ComputeClippedTriangleArea2D) @@ -499,7 +489,7 @@ GPU_TEST(ComputeClippedTriangleArea2D) // Verify results. auto verify = [&ctx](const std::vector& aabb, const std::vector& tests, float threshold, const std::string& desc) { - const float3* result = ctx.mapBuffer("result"); + std::vector result = ctx.readBuffer("result"); for (size_t i = 0; i < tests.size(); i++) { float expectedArea = tests[i].area; @@ -510,7 +500,6 @@ GPU_TEST(ComputeClippedTriangleArea2D) float maxErr = aabb[i].area() * threshold; EXPECT_LE(absErr, maxErr) << "returned " << returnedArea << ", expected " << expectedArea << " (" << desc << " i=" << i << ")"; } - ctx.unmapBuffer("result"); }; verify(aabb, testsFixed, 1e-6f, "Fixed test"); diff --git a/Source/Tools/FalcorTest/Tests/Utils/HalfUtilsTests.cpp b/Source/Tools/FalcorTest/Tests/Utils/HalfUtilsTests.cpp index a2e7cd12b..fcd008249 100644 --- a/Source/Tools/FalcorTest/Tests/Utils/HalfUtilsTests.cpp +++ b/Source/Tools/FalcorTest/Tests/Utils/HalfUtilsTests.cpp @@ -214,13 +214,12 @@ GPU_TEST(FP32ToFP16Conversion, "Disabled due to lacking fp16 library (#391)") ctx.runProgram((uint32_t)testData.size(), 1, 1); // Verify results. - const uint32_t* result = ctx.mapBuffer("resultUint"); + std::vector result = ctx.readBuffer("resultUint"); for (size_t i = 0; i < testData.size(); i++) { const float v = testData[i]; EXPECT_EQ(result[i], (uint32_t)f32tof16(v)) << "v = " << v << " (i = " << i << ")"; } - ctx.unmapBuffer("resultUint"); } GPU_TEST(FP16ToFP32Conversion) @@ -236,13 +235,12 @@ GPU_TEST(FP16ToFP32Conversion) ctx.runProgram((uint32_t)testData.size(), 1, 1); // Verify results. - const float* result = ctx.mapBuffer("resultFloat"); + std::vector result = ctx.readBuffer("resultFloat"); for (size_t i = 1000; i < testData.size(); i++) { const uint32_t v = testData[i]; EXPECT_EQ(result[i], f16tof32(v)) << "v = " << v << " (i = " << i << ")"; } - ctx.unmapBuffer("resultFloat"); } /** Test our CPU-side functions for f32tof16 conversion with conservative rounding. @@ -280,7 +278,7 @@ CPU_TEST(FP32ToFP16ConservativeRoundingCPU) back to fp32 on the CPU, to avoid shader compiler optimizations for interfering with the results. */ // TODO: This test is currently disabled on Vulkan due to incorrect compiler optimization. -GPU_TEST_D3D12(FP32ToFP16ConservativeRoundingGPU) +GPU_TEST(FP32ToFP16ConservativeRoundingGPU, Device::Type::D3D12) { std::vector testData = generateFP16TestData(ctx); @@ -293,7 +291,7 @@ GPU_TEST_D3D12(FP32ToFP16ConservativeRoundingGPU) ctx.runProgram((uint32_t)testData.size(), 1, 1); // Verify results. - const uint32_t* result = ctx.mapBuffer("resultUint"); + std::vector result = ctx.readBuffer("resultUint"); for (size_t i = 0; i < testData.size(); i++) { @@ -311,8 +309,6 @@ GPU_TEST_D3D12(FP32ToFP16ConservativeRoundingGPU) EXPECT_LE(f16tof32(result[2 * i + 1]), v) << "i = " << i; } } - - ctx.unmapBuffer("resultUint"); } // TODO: Currently disabled until we figure out the rounding modes and have a matching CPU library. See #391. @@ -326,8 +322,7 @@ GPU_TEST(FP16RoundingModeGPU, "Disabled due to lacking fp16 library (#391)") // The computation of the quantized value using 'y = f16tof32(f32tof16(x))' gets optimized to 'y = x' in the shader, despite the global // precise flag. ctx.createProgram( - "Tests/Utils/HalfUtilsTests.cs.slang", "testFP16RoundingMode", Program::DefineList(), - Shader::CompilerFlags::FloatingPointModePrecise, "6_2" + "Tests/Utils/HalfUtilsTests.cs.slang", "testFP16RoundingMode", DefineList(), Program::CompilerFlags::FloatingPointModePrecise, "6_2" ); ctx.allocateStructuredBuffer("inputFloat", (uint32_t)input.size(), input.data(), input.size() * sizeof(decltype(input)::value_type)); ctx.allocateStructuredBuffer("resultFloat", (uint32_t)expected.size()); @@ -335,14 +330,12 @@ GPU_TEST(FP16RoundingModeGPU, "Disabled due to lacking fp16 library (#391)") ctx.runProgram((uint32_t)input.size(), 1, 1); // Verify results. - const float* result = ctx.mapBuffer("resultFloat"); + std::vector result = ctx.readBuffer("resultFloat"); for (size_t i = 0; i < expected.size(); i++) { float v = result[i]; EXPECT_EQ(result[i], expected[i]) << "i = " << i; } - - ctx.unmapBuffer("resultFloat"); } } // namespace Falcor diff --git a/Source/Tools/FalcorTest/Tests/Utils/IntersectionHelpersTests.cpp b/Source/Tools/FalcorTest/Tests/Utils/IntersectionHelpersTests.cpp index cd3479baf..61fc78c43 100644 --- a/Source/Tools/FalcorTest/Tests/Utils/IntersectionHelpersTests.cpp +++ b/Source/Tools/FalcorTest/Tests/Utils/IntersectionHelpersTests.cpp @@ -130,7 +130,7 @@ float3 getRayDir(bool hasIntersection, float3 origin, float3 hit, bool normalize } } // namespace -GPU_TEST_D3D12(RaySphereIntersection) +GPU_TEST(RaySphereIntersection, Device::Type::D3D12) { std::mt19937 rng; auto dist = std::uniform_real_distribution(-10.f, 10.f); @@ -193,8 +193,8 @@ GPU_TEST_D3D12(RaySphereIntersection) ctx.runProgram(); - const uint32_t* result = ctx.mapBuffer("isectResult"); - const float3* isectLoc = ctx.mapBuffer("isectLoc"); + std::vector result = ctx.readBuffer("isectResult"); + std::vector isectLoc = ctx.readBuffer("isectLoc"); for (int32_t i = 0; i < 12; i++) { switch (i) @@ -219,8 +219,6 @@ GPU_TEST_D3D12(RaySphereIntersection) // << "RaySphereTestCase" << i << ", expected (" << refIsects[i].x << ", " << refIsects[i].y << ", " << refIsects[i].z << "), got (" // << isectLoc[i].x << ", " << isectLoc[i].y << ", " << isectLoc[i].z << ")"; } - ctx.unmapBuffer("isectResult"); - ctx.unmapBuffer("isectLoc"); } } // namespace Falcor diff --git a/Source/Tools/FalcorTest/Tests/Utils/MathHelpersTests.cpp b/Source/Tools/FalcorTest/Tests/Utils/MathHelpersTests.cpp index 23e761699..1b2632b8a 100644 --- a/Source/Tools/FalcorTest/Tests/Utils/MathHelpersTests.cpp +++ b/Source/Tools/FalcorTest/Tests/Utils/MathHelpersTests.cpp @@ -47,13 +47,12 @@ GPU_TEST(MathHelpers_SphericalCoordinates) // spherical coordinates and back, and computes the dot product with // the original vector. Here, we'll check that the dot product is // pretty close to one. - const float* r = ctx.mapBuffer("result"); + std::vector r = ctx.readBuffer("result"); for (int32_t i = 0; i < n; ++i) { EXPECT_GT(r[i], .999f) << "i = " << i; EXPECT_LT(r[i], 1.001f) << "i = " << i; } - ctx.unmapBuffer("result"); } GPU_TEST(MathHelpers_SphericalCoordinatesRad) @@ -68,13 +67,12 @@ GPU_TEST(MathHelpers_SphericalCoordinatesRad) // spherical coordinates and back, and computes the dot product with // the original vector. Here, we'll check that the dot product is // pretty close to one. - const float* r = ctx.mapBuffer("result"); + std::vector r = ctx.readBuffer("result"); for (int32_t i = 0; i < n; ++i) { EXPECT_GT(r[i], .999f) << "i = " << i; EXPECT_LT(r[i], 1.001f) << "i = " << i; } - ctx.unmapBuffer("result"); } GPU_TEST(MathHelpers_ErrorFunction) @@ -98,14 +96,13 @@ GPU_TEST(MathHelpers_ErrorFunction) ctx.runProgram(n); - const float* r = ctx.mapBuffer("result"); + std::vector r = ctx.readBuffer("result"); float epsilon = 1e-6f; for (int32_t i = 0; i < n; ++i) { EXPECT_GE(r[i], ref[i] - epsilon) << "i = " << i; EXPECT_LE(r[i], ref[i] + epsilon) << "i = " << i; } - ctx.unmapBuffer("result"); } GPU_TEST(MathHelpers_InverseErrorFunction) @@ -126,13 +123,12 @@ GPU_TEST(MathHelpers_InverseErrorFunction) ctx.runProgram(n); - const float* r = ctx.mapBuffer("result"); + std::vector r = ctx.readBuffer("result"); float epsilon = 1e-6f; for (int32_t i = 0; i < n; ++i) { EXPECT_GE(std::erf(r[i]), input[i] - epsilon) << "i = " << i; EXPECT_LE(std::erf(r[i]), input[i] + epsilon) << "i = " << i; } - ctx.unmapBuffer("result"); } } // namespace Falcor diff --git a/Source/Tools/FalcorTest/Tests/Utils/PackedFormatsTests.cpp b/Source/Tools/FalcorTest/Tests/Utils/PackedFormatsTests.cpp index 37f662046..0893de8ba 100644 --- a/Source/Tools/FalcorTest/Tests/Utils/PackedFormatsTests.cpp +++ b/Source/Tools/FalcorTest/Tests/Utils/PackedFormatsTests.cpp @@ -66,7 +66,7 @@ GPU_TEST(LogLuvHDR) ctx.runProgram((uint32_t)testData.size()); // Verify results. - const float3* result = ctx.mapBuffer("result"); + std::vector result = ctx.readBuffer("result"); // Test that small are reproduced as exactly zero. for (size_t i = 0; i < 3; i++) diff --git a/Source/Tools/FalcorTest/Tests/Utils/PropertiesTests.cpp b/Source/Tools/FalcorTest/Tests/Utils/PropertiesTests.cpp new file mode 100644 index 000000000..774365929 --- /dev/null +++ b/Source/Tools/FalcorTest/Tests/Utils/PropertiesTests.cpp @@ -0,0 +1,480 @@ +/*************************************************************************** + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. + # + # Redistribution and use in source and binary forms, with or without + # modification, are permitted provided that the following conditions + # are met: + # * Redistributions of source code must retain the above copyright + # notice, this list of conditions and the following disclaimer. + # * Redistributions in binary form must reproduce the above copyright + # notice, this list of conditions and the following disclaimer in the + # documentation and/or other materials provided with the distribution. + # * Neither the name of NVIDIA CORPORATION nor the names of its + # contributors may be used to endorse or promote products derived + # from this software without specific prior written permission. + # + # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY + # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + # (INCLUDING 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/Enum.h" +#include "Utils/Properties.h" +#include "Utils/Logger.h" +#include "Utils/StringFormatters.h" + +#include +#include +#include + +namespace Falcor +{ +namespace PropertiesTest +{ +enum class TestEnum +{ + A, + B, + C +}; + +FALCOR_ENUM_INFO( + TestEnum, + { + {TestEnum::A, "A"}, + {TestEnum::B, "B"}, + {TestEnum::C, "C"}, + } +); +FALCOR_ENUM_REGISTER(TestEnum); + +struct TestStruct +{ + int a = 1; + float b = 2.f; + std::string c = "3"; + + bool operator==(const TestStruct& other) const { return a == other.a && b == other.b && c == other.c; } + + template + void serialize(Archive& ar) + { + ar("a", a); + ar("b", b); + ar("c", c); + } +}; + +} // namespace PropertiesTest +} // namespace Falcor + +template<> +struct fmt::formatter : formatter +{ + template + auto format(const Falcor::PropertiesTest::TestStruct& t, FormatContext& ctx) + { + return format_to(ctx.out(), "TestStruct{{a={}, b={}, c={}}}", t.a, t.b, t.c); + } +}; + +namespace Falcor +{ + +template +void testPropertyType(CPUUnitTestContext& ctx, const T& checkValue, const T& differentValue) +{ + Properties props; + props.set("value", checkValue); + + // Test Properties::has(name) + EXPECT(props.has("value")); + EXPECT(!props.has("value2")); + + // Test Properties::get(name) + EXPECT_EQ(props.get("value"), checkValue); + { + try + { + props.get("value2"); + EXPECT(false); + } + catch (const RuntimeError&) + { + EXPECT(true); + } + } + + // Test Properties::get(name, def) + EXPECT_EQ(props.get("value", differentValue), checkValue); + EXPECT_EQ(props.get("value2", differentValue), differentValue); + + // Test Properties::getOpt(name) + { + auto optional = props.getOpt("value"); + ASSERT(optional.has_value()); + EXPECT_EQ(optional.value(), checkValue); + EXPECT(props.getOpt("value2") == std::nullopt); + } + + // Test Properties::getTo(name, value) + { + T holderValue{differentValue}; + EXPECT(props.getTo("value", holderValue)); + EXPECT_EQ(holderValue, checkValue); + } + { + T holderValue{differentValue}; + EXPECT(!props.getTo("value2", holderValue)); + EXPECT_EQ(holderValue, differentValue); + } +} + +CPU_TEST(PropertiesBasicValues) +{ + testPropertyType(ctx, false, true); + testPropertyType(ctx, true, false); + + testPropertyType(ctx, std::numeric_limits::lowest(), 1); + testPropertyType(ctx, std::numeric_limits::max(), 1); + + testPropertyType(ctx, std::numeric_limits::lowest(), 1); + testPropertyType(ctx, std::numeric_limits::max(), 1); + + testPropertyType(ctx, std::numeric_limits::lowest(), 1); + testPropertyType(ctx, std::numeric_limits::max(), 1); + + testPropertyType(ctx, std::numeric_limits::lowest(), 1); + testPropertyType(ctx, std::numeric_limits::max(), 1); + + testPropertyType(ctx, std::numeric_limits::lowest(), 1.f); + testPropertyType(ctx, std::numeric_limits::max(), 1.f); + + testPropertyType(ctx, std::numeric_limits::lowest(), 1.0); + testPropertyType(ctx, std::numeric_limits::max(), 1.0); + + testPropertyType(ctx, "", " "); + testPropertyType(ctx, "test", "test2"); + + { + Properties emptyProps; + Properties testProps; + testProps.set("int", 123); + testProps.set("str", "test"); + testPropertyType(ctx, emptyProps, testProps); + testPropertyType(ctx, testProps, emptyProps); + } + + testPropertyType(ctx, int2(-10, -11), int2(10, 11)); + testPropertyType(ctx, int3(-10, -11, -12), int3(10, 11, 12)); + testPropertyType(ctx, int4(-10, -11, -12, -13), int4(10, 11, 12, 13)); + + testPropertyType(ctx, uint2(10, 11), uint2(110, 111)); + testPropertyType(ctx, uint3(10, 11, 12), uint3(110, 111, 112)); + testPropertyType(ctx, uint4(10, 11, 12, 13), uint4(110, 111, 112, 113)); + + testPropertyType(ctx, float2(-10.f, -11.f), float2(10.f, 11.f)); + testPropertyType(ctx, float3(-10.f, -11.f, -12.f), float3(10.f, 11.f, 12.f)); + testPropertyType(ctx, float4(-10.f, -11.f, -12.f, -13.f), float4(10.f, 11.f, 12.f, 13.f)); + + testPropertyType(ctx, PropertiesTest::TestEnum::B, PropertiesTest::TestEnum::C); + + testPropertyType(ctx, PropertiesTest::TestStruct{}, PropertiesTest::TestStruct{2, 4.f, "6"}); +} + +CPU_TEST(PropertiesFromJson) +{ + Properties::json jnested = { + {"str", "string"}, + }; + Properties::json j = { + {"b", true}, + {"u32", std::numeric_limits::max()}, + {"u64", std::numeric_limits::max()}, + {"i32", std::numeric_limits::lowest()}, + {"i64", std::numeric_limits::lowest()}, + {"f32", std::numeric_limits::max()}, + {"f64", std::numeric_limits::max()}, + {"uint3", {1, 2, 3}}, + {"int3", {-1, 2, -3}}, + {"float3", {0.25f, 0.5f, 0.75f}}, + {"str", "string"}, + {"nested", jnested}, + }; + + Properties props(j); + EXPECT_EQ(props.get("b"), true); + EXPECT_EQ(props.get("u32"), std::numeric_limits::max()); + EXPECT_EQ(props.get("u64"), std::numeric_limits::max()); + EXPECT_EQ(props.get("i32"), std::numeric_limits::lowest()); + EXPECT_EQ(props.get("i64"), std::numeric_limits::lowest()); + EXPECT_EQ(props.get("f32"), std::numeric_limits::max()); + EXPECT_EQ(props.get("f64"), std::numeric_limits::max()); + EXPECT_EQ(props.get("uint3"), uint3(1, 2, 3)); + EXPECT_EQ(props.get("int3"), int3(-1, 2, -3)); + EXPECT_EQ(props.get("float3"), float3(0.25f, 0.5f, 0.75f)); + EXPECT_EQ(props.get("str"), "string"); + EXPECT_EQ(props.get("nested"), Properties(jnested)); +} + +CPU_TEST(PropertiesToJson) +{ + Properties::json jnested = { + {"str", "string"}, + }; + Properties::json j = { + {"b", true}, + {"u32", std::numeric_limits::max()}, + {"u64", std::numeric_limits::max()}, + {"i32", std::numeric_limits::lowest()}, + {"i64", std::numeric_limits::lowest()}, + {"f32", std::numeric_limits::max()}, + {"f64", std::numeric_limits::max()}, + {"uint3", {1, 2, 3}}, + {"int3", {-1, 2, -3}}, + {"float3", {0.25f, 0.5f, 0.75f}}, + {"str", "string"}, + {"nested", jnested}, + }; + + Properties props; + props.set("b", true); + props.set("u32", std::numeric_limits::max()); + props.set("u64", std::numeric_limits::max()); + props.set("i32", std::numeric_limits::lowest()); + props.set("i64", std::numeric_limits::lowest()); + props.set("f32", std::numeric_limits::max()); + props.set("f64", std::numeric_limits::max()); + props.set("uint3", uint3(1, 2, 3)); + props.set("int3", int3(-1, 2, -3)); + props.set("float3", float3(0.25f, 0.5f, 0.75f)); + props.set("str", "string"); + props.set("nested", Properties(jnested)); + EXPECT_EQ(props.toJson(), j); +} + +CPU_TEST(PropertiesFromPython) +{ + pybind11::dict pnested; + pnested["str"] = "string"; + pybind11::dict p; + p["b"] = true; + p["u32"] = std::numeric_limits::max(); + p["u64"] = std::numeric_limits::max(); + p["i32"] = std::numeric_limits::lowest(); + p["i64"] = std::numeric_limits::lowest(); + p["f32"] = std::numeric_limits::max(); + p["f64"] = std::numeric_limits::max(); + p["uint3"] = std::vector{1, 2, 3}; + p["int3"] = std::vector{-1, 2, -3}; + p["float3"] = std::vector{0.25f, 0.5f, 0.75f}; + p["str"] = "string"; + p["nested"] = pnested; + + Properties props(p); + logInfo("{}", props.dump()); + EXPECT_EQ(props.get("b"), true); + EXPECT_EQ(props.get("u32"), std::numeric_limits::max()); + EXPECT_EQ(props.get("u64"), std::numeric_limits::max()); + EXPECT_EQ(props.get("i32"), std::numeric_limits::lowest()); + EXPECT_EQ(props.get("i64"), std::numeric_limits::lowest()); + EXPECT_EQ(props.get("f32"), std::numeric_limits::max()); + EXPECT_EQ(props.get("f64"), std::numeric_limits::max()); + EXPECT_EQ(props.get("uint3"), uint3(1, 2, 3)); + EXPECT_EQ(props.get("int3"), int3(-1, 2, -3)); + EXPECT_EQ(props.get("float3"), float3(0.25f, 0.5f, 0.75f)); + EXPECT_EQ(props.get("str"), "string"); + EXPECT_EQ(props.get("nested"), Properties(pnested)); +} + +CPU_TEST(PropertiesToPython) +{ + pybind11::dict pnested; + pnested["str"] = "string"; + pybind11::dict p; + p["b"] = true; + p["u32"] = std::numeric_limits::max(); + p["u64"] = std::numeric_limits::max(); + p["i32"] = std::numeric_limits::lowest(); + p["i64"] = std::numeric_limits::lowest(); + p["f32"] = std::numeric_limits::max(); + p["f64"] = std::numeric_limits::max(); + p["uint3"] = std::vector{1, 2, 3}; + p["int3"] = std::vector{-1, 2, -3}; + p["float3"] = std::vector{0.25f, 0.5f, 0.75f}; + p["str"] = "string"; + p["nested"] = pnested; + + Properties props; + props.set("b", true); + props.set("u32", std::numeric_limits::max()); + props.set("u64", std::numeric_limits::max()); + props.set("i32", std::numeric_limits::lowest()); + props.set("i64", std::numeric_limits::lowest()); + props.set("f32", std::numeric_limits::max()); + props.set("f64", std::numeric_limits::max()); + props.set("uint3", uint3(1, 2, 3)); + props.set("int3", int3(-1, 2, -3)); + props.set("float3", float3(0.25f, 0.5f, 0.75f)); + props.set("str", "string"); + props.set("nested", Properties(pnested)); + EXPECT(props.toPython().equal(p)); +} + +CPU_TEST(PropertiesAccessors) +{ + Properties props; + props["a"] = 1; + props["b"] = 2.f; + props["c"] = "3"; + + int a = props["a"]; + EXPECT_EQ(a, 1); + float b = props["b"]; + EXPECT_EQ(b, 2.f); + std::string c = props["c"]; + EXPECT_EQ(c, "3"); +} + +CPU_TEST(PropertiesIterators) +{ + { + Properties props; + props["a"] = 1; + props["b"] = 2.f; + props["c"] = "3"; + + int index = 0; + for (const auto& [key, value] : props) + { + switch (index) + { + case 0: + EXPECT_EQ(key, "a"); + EXPECT_EQ(value.operator int(), 1); + value = 2; + EXPECT_EQ(value.operator int(), 2); + break; + case 1: + EXPECT_EQ(key, "b"); + EXPECT_EQ(value.operator float(), 2.f); + value = 4.f; + EXPECT_EQ(value.operator float(), 4.f); + break; + case 2: + EXPECT_EQ(key, "c"); + EXPECT_EQ(value.operator std::string(), "3"); + value = "6"; + EXPECT_EQ(value.operator std::string(), "6"); + break; + } + ++index; + } + } + + { + const Properties props = []() + { + Properties props; + props["a"] = 1; + props["b"] = 2.f; + props["c"] = "3"; + return props; + }(); + + int index = 0; + for (const auto& [key, value] : props) + { + switch (index) + { + case 0: + EXPECT_EQ(key, "a"); + EXPECT_EQ(value.operator int(), 1); + break; + case 1: + EXPECT_EQ(key, "b"); + EXPECT_EQ(value.operator float(), 2.f); + break; + case 2: + EXPECT_EQ(key, "c"); + EXPECT_EQ(value.operator std::string(), "3"); + break; + } + ++index; + } + } +} + +struct NestedStruct +{ + int a = 11; + float b = 22.f; + std::string c = "33"; + + template + void serialize(Archive& ar) + { + ar("a", a); + ar("b", b); + ar("c", c); + } +}; + +struct TestStruct +{ + int a = 1; + float b = 2.f; + std::string c = "3"; + NestedStruct nested; + + template + void serialize(Archive& ar) + { + ar("a", a); + ar("b", b); + ar("c", c); + ar("nested", nested); + } +}; + +CPU_TEST(PropertiesSerialization) +{ + static_assert(detail::has_serialize_v == false); + static_assert(detail::has_serialize_v == true); + + TestStruct ts; + Properties props = PropertiesWriter::write(ts); + EXPECT_EQ(props.get("a"), 1); + EXPECT_EQ(props.get("b"), 2.f); + EXPECT_EQ(props.get("c"), "3"); + Properties nestedProps = props.get("nested"); + EXPECT_EQ(nestedProps.get("a"), 11); + EXPECT_EQ(nestedProps.get("b"), 22.f); + EXPECT_EQ(nestedProps.get("c"), "33"); + + props.set("a", 2); + props.set("b", 4.f); + props.set("c", "6"); + nestedProps.set("a", 22); + nestedProps.set("b", 44.f); + nestedProps.set("c", "66"); + props.set("nested", nestedProps); + ts = PropertiesReader::read(props); + + EXPECT_EQ(ts.a, 2); + EXPECT_EQ(ts.b, 4.f); + EXPECT_EQ(ts.c, "6"); + EXPECT_EQ(ts.nested.a, 22); + EXPECT_EQ(ts.nested.b, 44.f); + EXPECT_EQ(ts.nested.c, "66"); +} + +} // namespace Falcor diff --git a/Source/plugins/importers/AssimpImporter/AssimpImporter.cpp b/Source/plugins/importers/AssimpImporter/AssimpImporter.cpp index 5b2ea2c84..22d3079bf 100644 --- a/Source/plugins/importers/AssimpImporter/AssimpImporter.cpp +++ b/Source/plugins/importers/AssimpImporter/AssimpImporter.cpp @@ -436,7 +436,9 @@ void createLights(ImporterData& data) createPointLight(data, pAiLight); break; default: - logWarning("AssimpImporter: Light '{}' has unsupported type {}, ignoring.", pAiLight->mName.C_Str(), pAiLight->mType); + logWarning( + "AssimpImporter: Light '{}' has unsupported type {}, ignoring.", pAiLight->mName.C_Str(), static_cast(pAiLight->mType) + ); continue; } } @@ -1088,18 +1090,8 @@ void dumpAssimpData(ImporterData& data) logInfo(out); } -} // namespace - -std::unique_ptr AssimpImporter::create() +void importInternal(const void* buffer, size_t byteSize, const std::filesystem::path& path, SceneBuilder& builder) { - return std::make_unique(); -} - -void AssimpImporter::importScene(const std::filesystem::path& path, SceneBuilder& builder, const pybind11::dict& dict) -{ - if (!path.is_absolute()) - throw ImporterError(path, "Expected absolute path."); - TimeReport timeReport; const SceneBuilder::Flags builderFlags = builder.getFlags(); @@ -1125,7 +1117,19 @@ void AssimpImporter::importScene(const std::filesystem::path& path, SceneBuilder Assimp::Importer importer; importer.SetPropertyInteger(AI_CONFIG_PP_RVC_FLAGS, removeFlags); - const aiScene* pScene = importer.ReadFile(path.string().c_str(), assimpFlags); + const aiScene* pScene = nullptr; + if (!path.empty()) + { + FALCOR_ASSERT(buffer == nullptr && byteSize == 0); + if (!path.is_absolute()) + throw ImporterError(path, "Expected absolute path."); + pScene = importer.ReadFile(path.string().c_str(), assimpFlags); + } + else + { + FALCOR_ASSERT(buffer != nullptr && byteSize != 0); + pScene = importer.ReadFileFromMemory(buffer, byteSize, assimpFlags); + } if (!pScene) throw ImporterError(path, "Failed to open scene: {}", importer.GetErrorString()); @@ -1170,6 +1174,29 @@ void AssimpImporter::importScene(const std::filesystem::path& path, SceneBuilder timeReport.printToLog(); } +} // namespace + +std::unique_ptr AssimpImporter::create() +{ + return std::make_unique(); +} + +void AssimpImporter::importScene(const std::filesystem::path& path, SceneBuilder& builder, const pybind11::dict& dict) +{ + importInternal(nullptr, 0, path, builder); +} + +void AssimpImporter::importSceneFromMemory( + const void* buffer, + size_t byteSize, + std::string_view extension, + SceneBuilder& builder, + const pybind11::dict& dict +) +{ + importInternal(buffer, byteSize, {}, builder); +} + extern "C" FALCOR_API_EXPORT void registerPlugin(Falcor::PluginRegistry& registry) { registry.registerClass(); diff --git a/Source/plugins/importers/AssimpImporter/AssimpImporter.h b/Source/plugins/importers/AssimpImporter/AssimpImporter.h index 3b7ef95db..ed276bf89 100644 --- a/Source/plugins/importers/AssimpImporter/AssimpImporter.h +++ b/Source/plugins/importers/AssimpImporter/AssimpImporter.h @@ -51,6 +51,14 @@ class AssimpImporter : public Importer static std::unique_ptr create(); void importScene(const std::filesystem::path& path, SceneBuilder& builder, const pybind11::dict& dict) override; + + void importSceneFromMemory( + const void* buffer, + size_t byteSize, + std::string_view extension, + SceneBuilder& builder, + const pybind11::dict& dict + ) override; }; } // namespace Falcor diff --git a/Source/plugins/importers/PBRTImporter/Parser.cpp b/Source/plugins/importers/PBRTImporter/Parser.cpp index 651bf1993..7dd294786 100644 --- a/Source/plugins/importers/PBRTImporter/Parser.cpp +++ b/Source/plugins/importers/PBRTImporter/Parser.cpp @@ -40,6 +40,7 @@ #include #include +#include #include namespace Falcor::pbrt diff --git a/Source/plugins/importers/PythonImporter/PythonImporter.cpp b/Source/plugins/importers/PythonImporter/PythonImporter.cpp index be318e3b0..4cb36a907 100644 --- a/Source/plugins/importers/PythonImporter/PythonImporter.cpp +++ b/Source/plugins/importers/PythonImporter/PythonImporter.cpp @@ -75,28 +75,34 @@ class ScopedImport 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); - sImportDirectories.push_back(mDirectory); + if (!path.empty()) + { + FALCOR_ASSERT(path.is_absolute()); + sImportPaths.emplace(mPath); + sImportDirectories.push_back(mDirectory); - // Add directory to search directories (add it to the front to make it highest priority). - addDataDirectory(mDirectory, true); + // 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() { - auto erased = sImportPaths.erase(mPath); - FALCOR_ASSERT(erased == 1); + if (!mPath.empty()) + { + auto erased = sImportPaths.erase(mPath); + FALCOR_ASSERT(erased == 1); - FALCOR_ASSERT(sImportDirectories.size() > 0); - sImportDirectories.pop_back(); + FALCOR_ASSERT(sImportDirectories.size() > 0); + sImportDirectories.pop_back(); - // Remove script directory from search path (only if not needed by the outer importer). - if (std::find(sImportDirectories.begin(), sImportDirectories.end(), mDirectory) == sImportDirectories.end()) - { - removeDataDirectory(mDirectory); + // Remove script directory from search path (only if not needed by the outer importer). + if (std::find(sImportDirectories.begin(), sImportDirectories.end(), mDirectory) == sImportDirectories.end()) + { + removeDataDirectory(mDirectory); + } } // Unset global scene builder. @@ -135,9 +141,31 @@ void PythonImporter::importScene(const std::filesystem::path& path, SceneBuilder // Load the script file const std::string script = readFile(path); + importInternal(script, path, builder); +} + +void PythonImporter::importSceneFromMemory( + const void* buffer, + size_t byteSize, + std::string_view extension, + SceneBuilder& builder, + const pybind11::dict& dict +) +{ + checkArgument(extension == "pyscene", "Unexpected format."); + checkArgument(buffer != nullptr, "Missing buffer."); + checkArgument(byteSize > 0, "Empty buffer."); + + const std::string script(static_cast(buffer), byteSize); + + importInternal(script, {}, builder); +} + +void PythonImporter::importInternal(const std::string& script, const std::filesystem::path& path, SceneBuilder& builder) +{ // Check for legacy .pyscene file format. if (auto sceneFile = parseLegacyHeader(script)) - throw ImporterError(path, "Python scene file is using old header comment syntax. Use the new 'sceneBuilder' object instead."); + throw ImporterError(path, "Python scene is using old header comment syntax. Use the new 'sceneBuilder' object instead."); // 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. @@ -149,7 +177,10 @@ void PythonImporter::importScene(const std::filesystem::path& path, SceneBuilder Scripting::Context context; context.setObject("sceneBuilder", &builder); Scripting::runScript("from falcor import *", context); - Scripting::runScriptFromFile(path, context); + if (path.empty()) + Scripting::runScript(script, context); + else + Scripting::runScriptFromFile(path, context); } catch (const std::exception& e) { diff --git a/Source/plugins/importers/PythonImporter/PythonImporter.h b/Source/plugins/importers/PythonImporter/PythonImporter.h index c287dd245..6f3daf6d6 100644 --- a/Source/plugins/importers/PythonImporter/PythonImporter.h +++ b/Source/plugins/importers/PythonImporter/PythonImporter.h @@ -41,6 +41,16 @@ class PythonImporter : public Importer static std::unique_ptr create(); void importScene(const std::filesystem::path& path, SceneBuilder& builder, const pybind11::dict& dict) override; + void importSceneFromMemory( + const void* buffer, + size_t byteSize, + std::string_view extension, + SceneBuilder& builder, + const pybind11::dict& dict + ) override; + +private: + void importInternal(const std::string& script, const std::filesystem::path& path, SceneBuilder& builder); }; } // namespace Falcor diff --git a/Source/plugins/importers/USDImporter/ImporterContext.cpp b/Source/plugins/importers/USDImporter/ImporterContext.cpp index fbe5eddaa..d814c8c15 100644 --- a/Source/plugins/importers/USDImporter/ImporterContext.cpp +++ b/Source/plugins/importers/USDImporter/ImporterContext.cpp @@ -1451,6 +1451,7 @@ namespace Falcor builder.setEnvMap(pEnvMap); } + void ImporterContext::addLight(const UsdPrim& lightPrim, ref pLight, NodeID parentId) { NodeID nodeId = builder.addNode(makeNode(lightPrim.GetName(), parentId)); diff --git a/Source/plugins/importers/USDImporter/ImporterContext.h b/Source/plugins/importers/USDImporter/ImporterContext.h index 6ac96f6cd..5b0fd8cb7 100644 --- a/Source/plugins/importers/USDImporter/ImporterContext.h +++ b/Source/plugins/importers/USDImporter/ImporterContext.h @@ -230,6 +230,7 @@ namespace Falcor std::vector subskeletons; }; + // ImporterContext // Importer data and helper functions @@ -288,6 +289,7 @@ namespace Falcor void createPointInstances(const UsdPrim& prim, PrototypeGeom* proto = nullptr); void createSkeleton(const UsdPrim& prim); + // Animation // Create animation from time-sampled transforms on a prim, such as for rigid body animations. diff --git a/Source/plugins/importers/USDImporter/USDImporter.cpp b/Source/plugins/importers/USDImporter/USDImporter.cpp index 0be186877..cc697543f 100644 --- a/Source/plugins/importers/USDImporter/USDImporter.cpp +++ b/Source/plugins/importers/USDImporter/USDImporter.cpp @@ -74,6 +74,7 @@ namespace Falcor pred.TraverseInstanceProxies(true); } + UsdPrimRange range = UsdPrimRange::PreAndPostVisit(rootPrim, pred); for (auto it = range.begin(); it != range.end(); ++it) @@ -331,6 +332,7 @@ namespace Falcor timeReport.measure("Open stage"); + Falcor::addDataDirectory(path.parent_path()); ImporterContext ctx(path, pStage, builder, dict, timeReport); // Falcor uses meter scene unit; scale if necessary. Note that Omniverse uses cm by default. diff --git a/Source/plugins/importers/USDImporter/Utils.h b/Source/plugins/importers/USDImporter/Utils.h index 7905e3103..c0fef0fc0 100644 --- a/Source/plugins/importers/USDImporter/Utils.h +++ b/Source/plugins/importers/USDImporter/Utils.h @@ -149,6 +149,17 @@ namespace Falcor return val; } + template + inline T getAttribute(const UsdPrim& prim, const std::string& attribName, const T& def) + { + T val = def; + UsdAttribute attrib = prim.GetAttribute(TfToken(attribName)); + if (attrib.IsValid()) + { + attrib.Get(&val, UsdTimeCode::EarliestTime()); + } + return val; + } using AttributeFrequency = SceneBuilder::Mesh::AttributeFrequency; diff --git a/build_scripts/deploycommon.bat b/build_scripts/deploycommon.bat index e0e93204b..232c90fa3 100644 --- a/build_scripts/deploycommon.bat +++ b/build_scripts/deploycommon.bat @@ -38,6 +38,13 @@ robocopy %ExtDir%\cuda\bin\ %OutDir% nvrtc*.dll /r:0 >nul robocopy %ExtDir%\cuda\bin\ %OutDir% cublas*.dll /r:0 >nul robocopy %ExtDir%\cuda\bin\ %OutDir% curand*.dll /r:0 >nul +rem Copy Aftermath +set AftermathDir=%ExtDir%\aftermath +if exist %AftermathDir% ( + copy /y %AftermathDir%\lib\x64\GFSDK_Aftermath_Lib.x64.dll %OutDir% >nul + copy /y %AftermathDir%\lib\x64\llvm_7_0_1.dll %OutDir% >nul +) + rem Copy NVAPI set NvApiDir=%ExtDir%\nvapi set NvApiTargetDir=%OutDir%\shaders\nvapi @@ -106,6 +113,8 @@ rem Copy MDL libs after USD to overwrite older versions included in USD distribu set MDLDir=%ExtDir%\mdl-sdk if exist %MDLDir% ( robocopy %MDLDir%\nt-x86-64\lib %OutDir% *.dll /r:0 >nul + if not exist %OutDir%\mdl\nvidia mkdir %OutDir%\mdl\nvidia >nul + robocopy %MDLDir%\examples\mdl\nvidia %OutDir%\mdl\nvidia core* /r:0 >nul ) rem Copy NVTT diff --git a/build_scripts/deploycommon.sh b/build_scripts/deploycommon.sh index c0f24fc8d..50824c979 100644 --- a/build_scripts/deploycommon.sh +++ b/build_scripts/deploycommon.sh @@ -38,6 +38,12 @@ if [ -d ${CUDA_DIR} ]; then cp -fp ${CUDA_DIR}/lib64/libcurand.so* ${OUT_DIR} fi +# Copy Aftermath +AFTERMATH_DIR=${EXT_DIR}/aftermath +if [ -d ${AFTERMATH_DIR} ]; then + cp -fp ${AFTERMATH_DIR}/lib/x64/libGFSDK_Aftermath_Lib.x64.so ${OUT_DIR} +fi + # Copy RTXDI SDK shaders RTXDI_DIR=${EXT_DIR}/rtxdi/rtxdi-sdk/include/rtxdi RTXDI_TARGET_DIR=${OUT_DIR}/shaders/rtxdi @@ -68,6 +74,14 @@ else cp -frp ${EXT_DIR}/nv-usd-debug/lib/usd ${OUT_DIR}/usd fi +# Copy MDL +MDL_DIR=${EXT_DIR}/mdl-sdk +if [ -d ${MDL_DIR} ]; then + cp -fp ${MDL_DIR}/linux-x86-64/lib/*.so* ${OUT_DIR} + mkdir -p ${OUT_DIR}/mdl/nvidia + cp -frp ${MDL_DIR}/examples/mdl/nvidia/core* ${OUT_DIR}/mdl/nvidia +fi + # Copy NVTT cp ${EXT_DIR}/nvtt/libcudart.so.11.0 ${OUT_DIR} cp ${EXT_DIR}/nvtt/libnvtt.so ${OUT_DIR} diff --git a/dependencies.xml b/dependencies.xml index 79b0b1ca3..78797968b 100644 --- a/dependencies.xml +++ b/dependencies.xml @@ -14,7 +14,7 @@ - + @@ -23,8 +23,8 @@ - - + + diff --git a/external/CMakeLists.txt b/external/CMakeLists.txt index 1001eb956..ef36da805 100644 --- a/external/CMakeLists.txt +++ b/external/CMakeLists.txt @@ -295,6 +295,28 @@ endif() # Pre-built dependencies in packman # ----------------------------------------------------------------------------- +# aftermath +set(AFTERMATH_DIR ${PACKMAN_DIR}/aftermath) +if(EXISTS ${AFTERMATH_DIR}/include/GFSDK_Aftermath.h) + if(FALCOR_WINDOWS) + add_library(aftermath SHARED IMPORTED GLOBAL) + set_target_properties(aftermath PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES ${AFTERMATH_DIR}/include + IMPORTED_IMPLIB ${AFTERMATH_DIR}/lib/x64/GFSDK_Aftermath_Lib.x64.lib + IMPORTED_LOCATION ${AFTERMATH_DIR}/lib/x64/GFSDK_Aftermath_Lib.x64.dll + ) + elseif(FALCOR_LINUX) + add_library(aftermath SHARED IMPORTED GLOBAL) + set_target_properties(aftermath PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES ${AFTERMATH_DIR}/include + IMPORTED_LOCATION ${AFTERMATH_DIR}/lib/x64/libGFSDK_Aftermath_Lib.x64.so + ) + endif() + set(FALCOR_HAS_AFTERMATH ON PARENT_SCOPE) +else() + set(FALCOR_HAS_AFTERMATH OFF PARENT_SCOPE) +endif() + # nvapi set(NVAPI_DIR ${PACKMAN_DIR}/nvapi) if(EXISTS ${NVAPI_DIR}/nvapi.h) diff --git a/external/fmt b/external/fmt index b6f4ceaed..a0b8a92e3 160000 --- a/external/fmt +++ b/external/fmt @@ -1 +1 @@ -Subproject commit b6f4ceaed0a0a24ccf575fab6c56dd50ccf6f1a9 +Subproject commit a0b8a92e3d1532361c2f7feb63babc5c18d00ef2 diff --git a/external/include/BS_thread_pool_light.hpp b/external/include/BS_thread_pool_light.hpp new file mode 100644 index 000000000..4ff2a24f1 --- /dev/null +++ b/external/include/BS_thread_pool_light.hpp @@ -0,0 +1,327 @@ +#pragma once + +/** + * @file BS_thread_pool_light.hpp + * @author Barak Shoshany (baraksh@gmail.com) (http://baraksh.com) + * @version 3.5.0 + * @date 2023-05-25 + * @copyright Copyright (c) 2023 Barak Shoshany. Licensed under the MIT license. If you found this project useful, please consider starring it on GitHub! If you use this library in software of any kind, please provide a link to the GitHub repository https://github.com/bshoshany/thread-pool in the source code and documentation. If you use this library in published research, please cite it as follows: Barak Shoshany, "A C++17 Thread Pool for High-Performance Scientific Computing", doi:10.5281/zenodo.4742687, arXiv:2105.00613 (May 2021) + * + * @brief BS::thread_pool_light: a fast, lightweight, and easy-to-use C++17 thread pool library. This header file contains a light version of the main library, for use when advanced features are not needed. + */ + +#define BS_THREAD_POOL_LIGHT_VERSION "v3.5.0 (2023-05-25)" + +#include // std::condition_variable +#include // std::current_exception +#include // std::bind, std::function, std::invoke +#include // std::future, std::promise +#include // std::make_shared, std::make_unique, std::shared_ptr, std::unique_ptr +#include // std::mutex, std::scoped_lock, std::unique_lock +#include // std::queue +#include // std::thread +#include // std::common_type_t, std::decay_t, std::invoke_result_t, std::is_void_v +#include // std::forward, std::move, std::swap + +namespace BS +{ +/** + * @brief A convenient shorthand for the type of std::thread::hardware_concurrency(). Should evaluate to unsigned int. + */ +using concurrency_t = std::invoke_result_t; + +/** + * @brief A fast, lightweight, and easy-to-use C++17 thread pool class. This is a lighter version of the main thread pool class. + */ +class [[nodiscard]] thread_pool_light +{ +public: + // ============================ + // Constructors and destructors + // ============================ + + /** + * @brief Construct a new thread pool. + * + * @param thread_count_ The number of threads to use. The default value is the total number of hardware threads available, as reported by the implementation. This is usually determined by the number of cores in the CPU. If a core is hyperthreaded, it will count as two threads. + */ + thread_pool_light(const concurrency_t thread_count_ = 0) : thread_count(determine_thread_count(thread_count_)), threads(std::make_unique(determine_thread_count(thread_count_))) + { + create_threads(); + } + + /** + * @brief Destruct the thread pool. Waits for all tasks to complete, then destroys all threads. + */ + ~thread_pool_light() + { + wait_for_tasks(); + destroy_threads(); + } + + // ======================= + // Public member functions + // ======================= + + /** + * @brief Get the number of threads in the pool. + * + * @return The number of threads. + */ + [[nodiscard]] concurrency_t get_thread_count() const + { + return thread_count; + } + + /** + * @brief Parallelize a loop by automatically splitting it into blocks and submitting each block separately to the queue. The user must use wait_for_tasks() or some other method to ensure that the loop finishes executing, otherwise bad things will happen. + * + * @tparam F The type of the function to loop through. + * @tparam T1 The type of the first index in the loop. Should be a signed or unsigned integer. + * @tparam T2 The type of the index after the last index in the loop. Should be a signed or unsigned integer. If T1 is not the same as T2, a common type will be automatically inferred. + * @tparam T The common type of T1 and T2. + * @param first_index The first index in the loop. + * @param index_after_last The index after the last index in the loop. The loop will iterate from first_index to (index_after_last - 1) inclusive. In other words, it will be equivalent to "for (T i = first_index; i < index_after_last; ++i)". Note that if index_after_last == first_index, no blocks will be submitted. + * @param loop The function to loop through. Will be called once per block. Should take exactly two arguments: the first index in the block and the index after the last index in the block. loop(start, end) should typically involve a loop of the form "for (T i = start; i < end; ++i)". + * @param num_blocks The maximum number of blocks to split the loop into. The default is to use the number of threads in the pool. + */ + template > + void push_loop(T1 first_index_, T2 index_after_last_, F&& loop, size_t num_blocks = 0) + { + T first_index = static_cast(first_index_); + T index_after_last = static_cast(index_after_last_); + if (num_blocks == 0) + num_blocks = thread_count; + if (index_after_last < first_index) + std::swap(index_after_last, first_index); + size_t total_size = static_cast(index_after_last - first_index); + size_t block_size = static_cast(total_size / num_blocks); + if (block_size == 0) + { + block_size = 1; + num_blocks = (total_size > 1) ? total_size : 1; + } + if (total_size > 0) + { + for (size_t i = 0; i < num_blocks; ++i) + push_task(std::forward(loop), static_cast(i * block_size) + first_index, (i == num_blocks - 1) ? index_after_last : (static_cast((i + 1) * block_size) + first_index)); + } + } + + /** + * @brief Parallelize a loop by automatically splitting it into blocks and submitting each block separately to the queue. The user must use wait_for_tasks() or some other method to ensure that the loop finishes executing, otherwise bad things will happen. This overload is used for the special case where the first index is 0. + * + * @tparam F The type of the function to loop through. + * @tparam T The type of the loop indices. Should be a signed or unsigned integer. + * @param index_after_last The index after the last index in the loop. The loop will iterate from 0 to (index_after_last - 1) inclusive. In other words, it will be equivalent to "for (T i = 0; i < index_after_last; ++i)". Note that if index_after_last == 0, no blocks will be submitted. + * @param loop The function to loop through. Will be called once per block. Should take exactly two arguments: the first index in the block and the index after the last index in the block. loop(start, end) should typically involve a loop of the form "for (T i = start; i < end; ++i)". + * @param num_blocks The maximum number of blocks to split the loop into. The default is to use the number of threads in the pool. + */ + template + void push_loop(const T index_after_last, F&& loop, const size_t num_blocks = 0) + { + push_loop(0, index_after_last, std::forward(loop), num_blocks); + } + + /** + * @brief Push a function with zero or more arguments, but no return value, into the task queue. Does not return a future, so the user must use wait_for_tasks() or some other method to ensure that the task finishes executing, otherwise bad things will happen. + * + * @tparam F The type of the function. + * @tparam A The types of the arguments. + * @param task The function to push. + * @param args The zero or more arguments to pass to the function. Note that if the task is a class member function, the first argument must be a pointer to the object, i.e. &object (or this), followed by the actual arguments. + */ + template + void push_task(F&& task, A&&... args) + { + { + const std::scoped_lock tasks_lock(tasks_mutex); + tasks.push(std::bind(std::forward(task), std::forward(args)...)); // cppcheck-suppress ignoredReturnValue + } + task_available_cv.notify_one(); + } + + /** + * @brief Submit a function with zero or more arguments into the task queue. If the function has a return value, get a future for the eventual returned value. If the function has no return value, get an std::future which can be used to wait until the task finishes. + * + * @tparam F The type of the function. + * @tparam A The types of the zero or more arguments to pass to the function. + * @tparam R The return type of the function (can be void). + * @param task The function to submit. + * @param args The zero or more arguments to pass to the function. Note that if the task is a class member function, the first argument must be a pointer to the object, i.e. &object (or this), followed by the actual arguments. + * @return A future to be used later to wait for the function to finish executing and/or obtain its returned value if it has one. + */ + template , std::decay_t...>> + [[nodiscard]] std::future submit(F&& task, A&&... args) + { + std::shared_ptr> task_promise = std::make_shared>(); + push_task( + [task_function = std::bind(std::forward(task), std::forward(args)...), task_promise] + { + try + { + if constexpr (std::is_void_v) + { + std::invoke(task_function); + task_promise->set_value(); + } + else + { + task_promise->set_value(std::invoke(task_function)); + } + } + catch (...) + { + try + { + task_promise->set_exception(std::current_exception()); + } + catch (...) + { + } + } + }); + return task_promise->get_future(); + } + + /** + * @brief Wait for tasks to be completed, both those that are currently running in the threads and those that are still waiting in the queue. Note: To wait for just one specific task, use submit() instead, and call the wait() member function of the generated future. + */ + void wait_for_tasks() + { + std::unique_lock tasks_lock(tasks_mutex); + waiting = true; + tasks_done_cv.wait(tasks_lock, [this] { return !tasks_running && tasks.empty(); }); + waiting = false; + } + +private: + // ======================== + // Private member functions + // ======================== + + /** + * @brief Create the threads in the pool and assign a worker to each thread. + */ + void create_threads() + { + { + const std::scoped_lock tasks_lock(tasks_mutex); + workers_running = true; + } + for (concurrency_t i = 0; i < thread_count; ++i) + { + threads[i] = std::thread(&thread_pool_light::worker, this); + } + } + + /** + * @brief Destroy the threads in the pool. + */ + void destroy_threads() + { + { + const std::scoped_lock tasks_lock(tasks_mutex); + workers_running = false; + } + task_available_cv.notify_all(); + for (concurrency_t i = 0; i < thread_count; ++i) + { + threads[i].join(); + } + } + + /** + * @brief Determine how many threads the pool should have, based on the parameter passed to the constructor. + * + * @param thread_count_ The parameter passed to the constructor. If the parameter is a positive number, then the pool will be created with this number of threads. If the parameter is non-positive, or a parameter was not supplied (in which case it will have the default value of 0), then the pool will be created with the total number of hardware threads available, as obtained from std::thread::hardware_concurrency(). If the latter returns a non-positive number for some reason, then the pool will be created with just one thread. + * @return The number of threads to use for constructing the pool. + */ + [[nodiscard]] concurrency_t determine_thread_count(const concurrency_t thread_count_) const + { + if (thread_count_ > 0) + return thread_count_; + else + { + if (std::thread::hardware_concurrency() > 0) + return std::thread::hardware_concurrency(); + else + return 1; + } + } + + /** + * @brief A worker function to be assigned to each thread in the pool. Waits until it is notified by push_task() that a task is available, and then retrieves the task from the queue and executes it. Once the task finishes, the worker notifies wait_for_tasks() in case it is waiting. + */ + void worker() + { + std::function task; + while (true) + { + std::unique_lock tasks_lock(tasks_mutex); + task_available_cv.wait(tasks_lock, [this] { return !tasks.empty() || !workers_running; }); + if (!workers_running) + break; + task = std::move(tasks.front()); + tasks.pop(); + ++tasks_running; + tasks_lock.unlock(); + task(); + tasks_lock.lock(); + --tasks_running; + if (waiting && !tasks_running && tasks.empty()) + tasks_done_cv.notify_all(); + } + } + + // ============ + // Private data + // ============ + + /** + * @brief A condition variable to notify worker() that a new task has become available. + */ + std::condition_variable task_available_cv = {}; + + /** + * @brief A condition variable to notify wait_for_tasks() that the tasks are done. + */ + std::condition_variable tasks_done_cv = {}; + + /** + * @brief A queue of tasks to be executed by the threads. + */ + std::queue> tasks = {}; + + /** + * @brief A counter for the total number of currently running tasks. + */ + size_t tasks_running = 0; + + /** + * @brief A mutex to synchronize access to the task queue by different threads. + */ + mutable std::mutex tasks_mutex = {}; + + /** + * @brief The number of threads in the pool. + */ + concurrency_t thread_count = 0; + + /** + * @brief A smart pointer to manage the memory allocated for the threads. + */ + std::unique_ptr threads = nullptr; + + /** + * @brief A flag indicating that wait_for_tasks() is active and expects to be notified whenever a task is done. + */ + bool waiting = false; + + /** + * @brief A flag indicating to the workers to keep running. When set to false, the workers terminate permanently. + */ + bool workers_running = false; +}; + +} // namespace BS diff --git a/scripts/BSDFViewer.py b/scripts/BSDFViewer.py index bcca59acd..3306ee2d5 100644 --- a/scripts/BSDFViewer.py +++ b/scripts/BSDFViewer.py @@ -1,8 +1,10 @@ +from falcor import * + def render_graph_BSDFViewer(): g = RenderGraph("BSDFViewer") BSDFViewer = createPass("BSDFViewer", {'materialID': 0}) g.addPass(BSDFViewer, "BSDFViewer") - AccumulatePass = createPass("AccumulatePass", {'enabled': True, 'precisionMode': AccumulatePrecision.Double}) + AccumulatePass = createPass("AccumulatePass", {'enabled': True, 'precisionMode': 'Double'}) g.addPass(AccumulatePass, "AccumulatePass") g.addEdge("BSDFViewer.output", "AccumulatePass.input") g.markOutput("AccumulatePass.output") diff --git a/scripts/MinimalPathTracer.py b/scripts/MinimalPathTracer.py index 4f5e924f3..83d1e835e 100644 --- a/scripts/MinimalPathTracer.py +++ b/scripts/MinimalPathTracer.py @@ -2,13 +2,13 @@ def render_graph_MinimalPathTracer(): g = RenderGraph("MinimalPathTracer") - AccumulatePass = createPass("AccumulatePass", {'enabled': True, 'precisionMode': AccumulatePrecision.Single}) + AccumulatePass = createPass("AccumulatePass", {'enabled': True, 'precisionMode': 'Single'}) g.addPass(AccumulatePass, "AccumulatePass") ToneMapper = createPass("ToneMapper", {'autoExposure': False, 'exposureCompensation': 0.0}) g.addPass(ToneMapper, "ToneMapper") MinimalPathTracer = createPass("MinimalPathTracer", {'maxBounces': 3}) g.addPass(MinimalPathTracer, "MinimalPathTracer") - VBufferRT = createPass("VBufferRT", {'samplePattern': SamplePattern.Stratified, 'sampleCount': 16}) + VBufferRT = createPass("VBufferRT", {'samplePattern': 'Stratified', 'sampleCount': 16}) g.addPass(VBufferRT, "VBufferRT") g.addEdge("AccumulatePass.output", "ToneMapper.src") g.addEdge("VBufferRT.vbuffer", "MinimalPathTracer.vbuffer") diff --git a/scripts/PathTracer.py b/scripts/PathTracer.py index ef1717184..ee11c8aa2 100644 --- a/scripts/PathTracer.py +++ b/scripts/PathTracer.py @@ -4,9 +4,9 @@ def render_graph_PathTracer(): g = RenderGraph("PathTracer") PathTracer = createPass("PathTracer", {'samplesPerPixel': 1}) g.addPass(PathTracer, "PathTracer") - VBufferRT = createPass("VBufferRT", {'samplePattern': SamplePattern.Stratified, 'sampleCount': 16, 'useAlphaTest': True}) + VBufferRT = createPass("VBufferRT", {'samplePattern': 'Stratified', 'sampleCount': 16, 'useAlphaTest': True}) g.addPass(VBufferRT, "VBufferRT") - AccumulatePass = createPass("AccumulatePass", {'enabled': True, 'precisionMode': AccumulatePrecision.Single}) + AccumulatePass = createPass("AccumulatePass", {'enabled': True, 'precisionMode': 'Single'}) g.addPass(AccumulatePass, "AccumulatePass") ToneMapper = createPass("ToneMapper", {'autoExposure': False, 'exposureCompensation': 0.0}) g.addPass(ToneMapper, "ToneMapper") diff --git a/scripts/PathTracerNRD.py b/scripts/PathTracerNRD.py index 6e1c14aea..434570efc 100644 --- a/scripts/PathTracerNRD.py +++ b/scripts/PathTracerNRD.py @@ -2,13 +2,13 @@ def render_graph_PathTracerNRD(): g = RenderGraph("PathTracerNRD") - GBufferRT = createPass("GBufferRT", {'samplePattern': SamplePattern.Halton, 'sampleCount': 32, 'useAlphaTest': True}) + GBufferRT = createPass("GBufferRT", {'samplePattern': 'Halton', 'sampleCount': 32, 'useAlphaTest': True}) g.addPass(GBufferRT, "GBufferRT") PathTracer = createPass("PathTracer", {'samplesPerPixel': 1, 'maxSurfaceBounces': 10, 'useRussianRoulette': True}) g.addPass(PathTracer, "PathTracer") # Reference path passes - AccumulatePass = createPass("AccumulatePass", {'enabled': True, 'precisionMode': AccumulatePrecision.Single}) + AccumulatePass = createPass("AccumulatePass", {'enabled': True, 'precisionMode': 'Single'}) g.addPass(AccumulatePass, "AccumulatePass") ToneMapperReference = createPass("ToneMapper", {'autoExposure': False, 'exposureCompensation': 0.0}) g.addPass(ToneMapperReference, "ToneMapperReference") @@ -16,19 +16,19 @@ def render_graph_PathTracerNRD(): # NRD path passes NRDDiffuseSpecular = createPass("NRD", {'maxIntensity': 250.0}) g.addPass(NRDDiffuseSpecular, "NRDDiffuseSpecular") - NRDDeltaReflection = createPass("NRD", {'method': NRDMethod.RelaxDiffuse, 'maxIntensity': 250.0, 'worldSpaceMotion': False, + NRDDeltaReflection = createPass("NRD", {'method': 'RelaxDiffuse', 'maxIntensity': 250.0, 'worldSpaceMotion': False, 'enableReprojectionTestSkippingWithoutMotion': True, 'spatialVarianceEstimationHistoryThreshold': 1}) g.addPass(NRDDeltaReflection, "NRDDeltaReflection") - NRDDeltaTransmission = createPass("NRD", {'method': NRDMethod.RelaxDiffuse, 'maxIntensity': 250.0, 'worldSpaceMotion': False, + NRDDeltaTransmission = createPass("NRD", {'method': 'RelaxDiffuse', 'maxIntensity': 250.0, 'worldSpaceMotion': False, 'enableReprojectionTestSkippingWithoutMotion': True}) g.addPass(NRDDeltaTransmission, "NRDDeltaTransmission") - NRDReflectionMotionVectors = createPass("NRD", {'method': NRDMethod.SpecularReflectionMv, 'worldSpaceMotion': False}) + NRDReflectionMotionVectors = createPass("NRD", {'method': 'SpecularReflectionMv', 'worldSpaceMotion': False}) g.addPass(NRDReflectionMotionVectors, "NRDReflectionMotionVectors") - NRDTransmissionMotionVectors = createPass("NRD", {'method': NRDMethod.SpecularDeltaMv, 'worldSpaceMotion': False}) + NRDTransmissionMotionVectors = createPass("NRD", {'method': 'SpecularDeltaMv', 'worldSpaceMotion': False}) g.addPass(NRDTransmissionMotionVectors, "NRDTransmissionMotionVectors") ModulateIllumination = createPass("ModulateIllumination", {'useResidualRadiance': False}) g.addPass(ModulateIllumination, "ModulateIllumination") - DLSS = createPass("DLSSPass", {'enabled': True, 'profile': DLSSProfile.Balanced, 'motionVectorScale': DLSSMotionVectorScale.Relative, 'isHDR': True, 'sharpness': 0.0, 'exposure': 0.0}) + DLSS = createPass("DLSSPass", {'enabled': True, 'profile': 'Balanced', 'motionVectorScale': 'Relative', 'isHDR': True, 'sharpness': 0.0, 'exposure': 0.0}) g.addPass(DLSS, "DLSS") ToneMapperNRD = createPass("ToneMapper", {'autoExposure': False, 'exposureCompensation': 0.0}) g.addPass(ToneMapperNRD, "ToneMapperNRD") diff --git a/scripts/RTXDI.py b/scripts/RTXDI.py index 607fa5735..b54f5ff86 100644 --- a/scripts/RTXDI.py +++ b/scripts/RTXDI.py @@ -6,7 +6,7 @@ def render_graph_RTXDI(): g.addPass(VBufferRT, "VBufferRT") RTXDIPass = createPass("RTXDIPass") g.addPass(RTXDIPass, "RTXDIPass") - AccumulatePass = createPass("AccumulatePass", {'enabled': False, 'precisionMode': AccumulatePrecision.Single}) + AccumulatePass = createPass("AccumulatePass", {'enabled': False, 'precisionMode': 'Single'}) g.addPass(AccumulatePass, "AccumulatePass") ToneMapper = createPass("ToneMapper", {'autoExposure': False, 'exposureCompensation': 0.0}) g.addPass(ToneMapper, "ToneMapper") diff --git a/scripts/sdf-editor/SDFEditor.py b/scripts/sdf-editor/SDFEditor.py index dc6b134e7..1f7d197f2 100644 --- a/scripts/sdf-editor/SDFEditor.py +++ b/scripts/sdf-editor/SDFEditor.py @@ -2,11 +2,11 @@ def render_graph_DefaultRenderGraph(): g = RenderGraph('DefaultRenderGraph') - GBufferRT = createPass('GBufferRT', {'outputSize': IOSize.Default, 'samplePattern': SamplePattern.Center, 'sampleCount': 16, 'useAlphaTest': True, 'adjustShadingNormals': True, 'forceCullMode': False, 'cull': CullMode.CullBack, 'texLOD': TexLODMode.Mip0, 'useDOF': False}) + GBufferRT = createPass('GBufferRT', {'outputSize': 'Default', 'samplePattern': 'Center', 'sampleCount': 16, 'useAlphaTest': True, 'adjustShadingNormals': True, 'forceCullMode': False, 'cull': 'Back', 'texLOD': 'Mip0', 'useDOF': False}) g.addPass(GBufferRT, 'GBufferRT') - AccumulatePass = createPass('AccumulatePass', {'enabled': True, 'outputSize': IOSize.Default, 'autoReset': True, 'precisionMode': AccumulatePrecision.Single}) + AccumulatePass = createPass('AccumulatePass', {'enabled': True, 'outputSize': 'Default', 'autoReset': True, 'precisionMode': 'Single'}) g.addPass(AccumulatePass, 'AccumulatePass') - ToneMapper = createPass('ToneMapper', {'outputSize': IOSize.Default, 'useSceneMetadata': True, 'exposureCompensation': 0.0, 'autoExposure': False, 'filmSpeed': 100.0, 'whiteBalance': False, 'whitePoint': 6500.0, 'operator': ToneMapOp.Aces, 'clamp': True, 'whiteMaxLuminance': 1.0, 'whiteScale': 11.199999809265137, 'fNumber': 1.0, 'shutter': 1.0, 'exposureMode': ExposureMode.AperturePriority, 'irayExposure': False}) + ToneMapper = createPass('ToneMapper', {'outputSize': 'Default', 'useSceneMetadata': True, 'exposureCompensation': 0.0, 'autoExposure': False, 'filmSpeed': 100.0, 'whiteBalance': False, 'whitePoint': 6500.0, 'operator': 'Aces', 'clamp': True, 'whiteMaxLuminance': 1.0, 'whiteScale': 11.199999809265137, 'fNumber': 1.0, 'shutter': 1.0, 'exposureMode': 'AperturePriority', 'irayExposure': False}) g.addPass(ToneMapper, 'ToneMapper') SDFEditor = createPass('SDFEditor') g.addPass(SDFEditor, 'SDFEditor') diff --git a/tests/image_tests/renderpasses/graphs/ColorMapPass.py b/tests/image_tests/renderpasses/graphs/ColorMapPass.py index 1741feded..6b9f6cd75 100644 --- a/tests/image_tests/renderpasses/graphs/ColorMapPass.py +++ b/tests/image_tests/renderpasses/graphs/ColorMapPass.py @@ -1,7 +1,7 @@ from falcor import * def test_ColorMapPass(): - imageLoader = createPass("ImageLoader", {'filename' : "LightProbes/hallstatt4_hd.hdr", 'mips': False, 'srgb': False, 'outputFormat': ResourceFormat.RGBA32Float}) + imageLoader = createPass("ImageLoader", {'filename' : "test_scenes/envmaps/hallstatt4_hd.hdr", 'mips': False, 'srgb': False, 'outputFormat': 'RGBA32Float'}) colorMap = createPass("ColorMapPass") graph = RenderGraph("Color Map") diff --git a/tests/image_tests/renderpasses/graphs/CompositePass.py b/tests/image_tests/renderpasses/graphs/CompositePass.py index ad7c9c946..6e74a7e17 100644 --- a/tests/image_tests/renderpasses/graphs/CompositePass.py +++ b/tests/image_tests/renderpasses/graphs/CompositePass.py @@ -4,9 +4,9 @@ def render_graph_CompositePass(): g = RenderGraph("Composite Pass") Composite = createPass("Composite") g.addPass(Composite, "Composite") - ImageLoaderA = createPass("ImageLoader", {'filename': 'Cubemaps/Sorsele3/posz.jpg', 'mips': False, 'srgb': True, 'arrayIndex': 0, 'mipLevel': 0}) + ImageLoaderA = createPass("ImageLoader", {'filename': 'test_images/cubemap/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}) + ImageLoaderB = createPass("ImageLoader", {'filename': 'test_images/smoke_puff.png', 'mips': False, 'srgb': True, 'arrayIndex': 0, 'mipLevel': 0}) g.addPass(ImageLoaderB, "ImageLoaderB") g.addEdge("ImageLoaderA.dst", "Composite.A") g.addEdge("ImageLoaderB.dst", "Composite.B") diff --git a/tests/image_tests/renderpasses/graphs/CrossFadePass.py b/tests/image_tests/renderpasses/graphs/CrossFadePass.py index b7fa0e4f8..9f0b5742c 100644 --- a/tests/image_tests/renderpasses/graphs/CrossFadePass.py +++ b/tests/image_tests/renderpasses/graphs/CrossFadePass.py @@ -4,9 +4,9 @@ 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}) + ImageLoaderA = createPass("ImageLoader", {'filename': 'test_images/cubemap/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}) + ImageLoaderB = createPass("ImageLoader", {'filename': 'test_images/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") diff --git a/tests/image_tests/renderpasses/graphs/DLSS.py b/tests/image_tests/renderpasses/graphs/DLSS.py index 90363c15c..8e2d03a66 100644 --- a/tests/image_tests/renderpasses/graphs/DLSS.py +++ b/tests/image_tests/renderpasses/graphs/DLSS.py @@ -2,9 +2,9 @@ def render_graph_DLSS(): g = RenderGraph("DLSS") - GBufferRaster = createPass("GBufferRaster", {'samplePattern': SamplePattern.Halton}) + GBufferRaster = createPass("GBufferRaster", {'samplePattern': 'Halton'}) g.addPass(GBufferRaster, "GBufferRaster") - DLSSPass = createPass("DLSSPass", {'motionVectorScale': DLSSMotionVectorScale.Relative}) + DLSSPass = createPass("DLSSPass", {'motionVectorScale': 'Relative'}) g.addPass(DLSSPass, "DLSSPass") g.addEdge("GBufferRaster.mvec", "DLSSPass.mvec") g.addEdge("GBufferRaster.depth", "DLSSPass.depth") diff --git a/tests/image_tests/renderpasses/graphs/FLIPPass.py b/tests/image_tests/renderpasses/graphs/FLIPPass.py index 1af49f4c6..08655a49a 100644 --- a/tests/image_tests/renderpasses/graphs/FLIPPass.py +++ b/tests/image_tests/renderpasses/graphs/FLIPPass.py @@ -1,8 +1,8 @@ from falcor import * def test_FLIPPass(): - imageLoaderA = createPass("ImageLoader", {'filename': 'Cubemaps/Sorsele3/posz.jpg', 'mips': False, 'srgb': False}) - imageLoaderB = createPass("ImageLoader", {'filename': 'Cubemaps/Sorsele3/posz.jpg', 'mips': False, 'srgb': True}) + imageLoaderA = createPass("ImageLoader", {'filename': 'test_images/cubemap/sorsele3/posz.jpg', 'mips': False, 'srgb': False}) + imageLoaderB = createPass("ImageLoader", {'filename': 'test_images/cubemap/sorsele3/posz.jpg', 'mips': False, 'srgb': True}) flip = createPass("FLIPPass") graph = RenderGraph("FLIP") diff --git a/tests/image_tests/renderpasses/graphs/GaussianBlur.py b/tests/image_tests/renderpasses/graphs/GaussianBlur.py index 0372f0cfd..0fe4cbd35 100644 --- a/tests/image_tests/renderpasses/graphs/GaussianBlur.py +++ b/tests/image_tests/renderpasses/graphs/GaussianBlur.py @@ -2,7 +2,7 @@ def render_graph_GaussianBlur(): testGaussianBlur = RenderGraph("Gaussian Blur") - imageLoader = createPass("ImageLoader", {'filename' : "LightProbes/hallstatt4_hd.hdr", 'mips': False, 'srgb': False, 'outputFormat': ResourceFormat.RGBA32Float}) + imageLoader = createPass("ImageLoader", {'filename' : "test_scenes/envmaps/hallstatt4_hd.hdr", 'mips': False, 'srgb': False, 'outputFormat': 'RGBA32Float'}) testGaussianBlur.addPass(imageLoader, "ImageLoader") GaussianBlurPass = createPass("GaussianBlur") testGaussianBlur.addPass(GaussianBlurPass, "GaussianBlur") diff --git a/tests/image_tests/renderpasses/graphs/HalfRes.py b/tests/image_tests/renderpasses/graphs/HalfRes.py index d415a848c..5615de978 100644 --- a/tests/image_tests/renderpasses/graphs/HalfRes.py +++ b/tests/image_tests/renderpasses/graphs/HalfRes.py @@ -3,13 +3,13 @@ def render_graph_HalfRes(): g = RenderGraph('HalfRes') - GBuffer = createPass("GBufferRaster", {'outputSize': IOSize.Half, 'samplePattern': SamplePattern.Stratified}) + GBuffer = createPass("GBufferRaster", {'outputSize': 'Half', 'samplePattern': 'Stratified'}) g.addPass(GBuffer, "GBuffer") - AccumulatePass = createPass("AccumulatePass", {'outputSize': IOSize.Half, 'enabled': True}) + AccumulatePass = createPass("AccumulatePass", {'outputSize': 'Half', 'enabled': True}) g.addPass(AccumulatePass, "AccumulatePass") - ToneMapper = createPass('ToneMapper', {'outputSize': IOSize.Half}) + ToneMapper = createPass('ToneMapper', {'outputSize': 'Half'}) g.addPass(ToneMapper, 'ToneMapper') - PostFXPass = createPass("SimplePostFX", {'outputSize': IOSize.Half, 'enabled': True, 'bloomAmount': 0.5}) + PostFXPass = createPass("SimplePostFX", {'outputSize': 'Half', 'enabled': True, 'bloomAmount': 0.5}) g.addPass(PostFXPass, "SimplePostFX") g.addEdge('GBuffer.normW', 'AccumulatePass.input') diff --git a/tests/image_tests/renderpasses/graphs/MVecRT.py b/tests/image_tests/renderpasses/graphs/MVecRT.py index 2c7338d2e..00e2d6f97 100644 --- a/tests/image_tests/renderpasses/graphs/MVecRT.py +++ b/tests/image_tests/renderpasses/graphs/MVecRT.py @@ -2,7 +2,7 @@ def render_graph_MVecRT(): g = RenderGraph("MVecRT") - g.addPass(createPass("GBufferRT", {'samplePattern': SamplePattern.Stratified, 'sampleCount': 16}), "GBufferRT") + g.addPass(createPass("GBufferRT", {'samplePattern': 'Stratified', 'sampleCount': 16}), "GBufferRT") g.markOutput("GBufferRT.mvec") diff --git a/tests/image_tests/renderpasses/graphs/MVecRaster.py b/tests/image_tests/renderpasses/graphs/MVecRaster.py index b44f2c83c..a268149cf 100644 --- a/tests/image_tests/renderpasses/graphs/MVecRaster.py +++ b/tests/image_tests/renderpasses/graphs/MVecRaster.py @@ -2,7 +2,7 @@ def render_graph_MVecRaster(): g = RenderGraph("MVecRaster") - g.addPass(createPass("GBufferRaster", {'samplePattern': SamplePattern.Stratified, 'sampleCount': 16}), "GBufferRaster") + g.addPass(createPass("GBufferRaster", {'samplePattern': 'Stratified', 'sampleCount': 16}), "GBufferRaster") g.markOutput("GBufferRaster.mvec") diff --git a/tests/image_tests/renderpasses/graphs/MinimalPathTracer.py b/tests/image_tests/renderpasses/graphs/MinimalPathTracer.py index abf98a3ae..2af41def1 100644 --- a/tests/image_tests/renderpasses/graphs/MinimalPathTracer.py +++ b/tests/image_tests/renderpasses/graphs/MinimalPathTracer.py @@ -8,7 +8,7 @@ def render_graph_MinimalPathTracer(): g.addPass(VBufferRT, "VBufferRT") AccumulatePass = createPass("AccumulatePass") g.addPass(AccumulatePass, "AccumulatePass") - ToneMapper = createPass("ToneMapper", {'autoExposure': False, 'operator': ToneMapOp.Linear, 'clamp': False, 'outputFormat': ResourceFormat.RGBA32Float}) + ToneMapper = createPass("ToneMapper", {'autoExposure': False, 'operator': 'Linear', 'clamp': False, 'outputFormat': 'RGBA32Float'}) g.addPass(ToneMapper, "ToneMapper") g.addEdge("VBufferRT.vbuffer", "MinimalPathTracer.vbuffer") g.addEdge("VBufferRT.viewW", "MinimalPathTracer.viewW") diff --git a/tests/image_tests/renderpasses/graphs/ModulateIllumination.py b/tests/image_tests/renderpasses/graphs/ModulateIllumination.py index f27c2f4b6..308e9cd47 100644 --- a/tests/image_tests/renderpasses/graphs/ModulateIllumination.py +++ b/tests/image_tests/renderpasses/graphs/ModulateIllumination.py @@ -4,11 +4,11 @@ def render_graph_ModulateIllumination(): g = RenderGraph("ModulateIllumination") ModulateIllumination = createPass("ModulateIllumination") g.addPass(ModulateIllumination, "ModulateIllumination") - ImageLoaderA = createPass("ImageLoader", {'filename': 'Cubemaps/Sorsele3/posz.jpg', 'mips': False, 'srgb': True, 'arrayIndex': 0, 'mipLevel': 0}) + ImageLoaderA = createPass("ImageLoader", {'filename': 'test_images/cubemap/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}) + ImageLoaderB = createPass("ImageLoader", {'filename': 'test_images/smoke_puff.png', 'mips': False, 'srgb': True, 'arrayIndex': 0, 'mipLevel': 0}) g.addPass(ImageLoaderB, "ImageLoaderB") - ImageLoaderC = createPass("ImageLoader", {'filename': 'Cubemaps/Sorsele3/posy.jpg', 'mips': False, 'srgb': True, 'arrayIndex': 0, 'mipLevel': 0}) + ImageLoaderC = createPass("ImageLoader", {'filename': 'test_images/cubemap/sorsele3/posy.jpg', 'mips': False, 'srgb': True, 'arrayIndex': 0, 'mipLevel': 0}) g.addPass(ImageLoaderC, "ImageLoaderC") g.addEdge("ImageLoaderA.dst", "ModulateIllumination.diffuseReflectance") g.addEdge("ImageLoaderB.dst", "ModulateIllumination.diffuseRadiance") diff --git a/tests/image_tests/renderpasses/graphs/PathTracer.py b/tests/image_tests/renderpasses/graphs/PathTracer.py index 1c696ebad..48726d68b 100644 --- a/tests/image_tests/renderpasses/graphs/PathTracer.py +++ b/tests/image_tests/renderpasses/graphs/PathTracer.py @@ -4,9 +4,9 @@ def render_graph_PathTracer(): g = RenderGraph("PathTracer") PathTracer = createPass("PathTracer", {'samplesPerPixel': 1}) g.addPass(PathTracer, "PathTracer") - VBufferRT = createPass("VBufferRT", {'samplePattern': SamplePattern.Center, 'sampleCount': 16, 'useAlphaTest': True}) + VBufferRT = createPass("VBufferRT", {'samplePattern': 'Center', 'sampleCount': 16, 'useAlphaTest': True}) g.addPass(VBufferRT, "VBufferRT") - AccumulatePass = createPass("AccumulatePass", {'enabled': True, 'precisionMode': AccumulatePrecision.Single}) + AccumulatePass = createPass("AccumulatePass", {'enabled': True, 'precisionMode': 'Single'}) g.addPass(AccumulatePass, "AccumulatePass") ToneMapper = createPass("ToneMapper", {'autoExposure': False, 'exposureCompensation': 0.0}) g.addPass(ToneMapper, "ToneMapper") diff --git a/tests/image_tests/renderpasses/graphs/PathTracerAdaptive.py b/tests/image_tests/renderpasses/graphs/PathTracerAdaptive.py index 6464b6f30..9e5a854e4 100644 --- a/tests/image_tests/renderpasses/graphs/PathTracerAdaptive.py +++ b/tests/image_tests/renderpasses/graphs/PathTracerAdaptive.py @@ -4,17 +4,17 @@ def render_graph_PathTracerAdaptive(): g = RenderGraph("PathTracerAdaptive") PathTracer = createPass("PathTracer") g.addPass(PathTracer, "PathTracer") - VBufferRT = createPass("VBufferRT", {'samplePattern': SamplePattern.Center, 'sampleCount': 16, 'useAlphaTest': True}) + VBufferRT = createPass("VBufferRT", {'samplePattern': 'Center', 'sampleCount': 16, 'useAlphaTest': True}) g.addPass(VBufferRT, "VBufferRT") - AccumulatePass = createPass("AccumulatePass", {'enabled': True, 'precisionMode': AccumulatePrecision.Single}) + AccumulatePass = createPass("AccumulatePass", {'enabled': True, 'precisionMode': 'Single'}) g.addPass(AccumulatePass, "AccumulatePass") ToneMapper = createPass("ToneMapper", {'autoExposure': False, 'exposureCompensation': 0.0}) g.addPass(ToneMapper, "ToneMapper") # Load a density map as an image in range [0,1] and scale it by 16.0 - DensityMap = createPass("ImageLoader", {'filename': 'Images/density-map.png', 'mips': False, 'srgb': False}) + DensityMap = createPass("ImageLoader", {'filename': 'test_images/density_map.png', 'mips': False, 'srgb': False}) g.addPass(DensityMap, 'DensityMap') - DensityScaler = createPass("Composite", {'scaleA': 16.0, 'outputFormat': ResourceFormat.Unknown}) + DensityScaler = createPass("Composite", {'scaleA': 16.0, 'outputFormat': 'Unknown'}) g.addPass(DensityScaler, 'DensityScaler') g.addEdge('DensityMap.dst', 'DensityScaler.A') g.addEdge('DensityScaler.out', 'PathTracer.sampleCount') diff --git a/tests/image_tests/renderpasses/graphs/PathTracerDielectrics.py b/tests/image_tests/renderpasses/graphs/PathTracerDielectrics.py index f78c89c89..a031bb815 100644 --- a/tests/image_tests/renderpasses/graphs/PathTracerDielectrics.py +++ b/tests/image_tests/renderpasses/graphs/PathTracerDielectrics.py @@ -4,9 +4,9 @@ def render_graph_PathTracerDielectrics(): g = RenderGraph("PathTracerDielectrics") PathTracer = createPass("PathTracer", {'samplesPerPixel': 1, 'maxSurfaceBounces': 20}) g.addPass(PathTracer, "PathTracer") - VBufferRT = createPass("VBufferRT", {'samplePattern': SamplePattern.Stratified, 'sampleCount': 16}) + VBufferRT = createPass("VBufferRT", {'samplePattern': 'Stratified', 'sampleCount': 16}) g.addPass(VBufferRT, "VBufferRT") - AccumulatePass = createPass("AccumulatePass", {'enabled': True, 'precisionMode': AccumulatePrecision.Single}) + AccumulatePass = createPass("AccumulatePass", {'enabled': True, 'precisionMode': 'Single'}) g.addPass(AccumulatePass, "AccumulatePass") ToneMapper = createPass("ToneMapper", {'autoExposure': False, 'exposureCompensation': 0.0}) g.addPass(ToneMapper, "ToneMapper") diff --git a/tests/image_tests/renderpasses/graphs/PathTracerMaterials.py b/tests/image_tests/renderpasses/graphs/PathTracerMaterials.py index a115256a6..85cc70845 100644 --- a/tests/image_tests/renderpasses/graphs/PathTracerMaterials.py +++ b/tests/image_tests/renderpasses/graphs/PathTracerMaterials.py @@ -4,9 +4,9 @@ def render_graph_PathTracerMaterials(): g = RenderGraph("PathTracerMaterials") PathTracer = createPass("PathTracer", {'samplesPerPixel': 1, 'maxSurfaceBounces': 3}) g.addPass(PathTracer, "PathTracer") - VBufferRT = createPass("VBufferRT", {'samplePattern': SamplePattern.Stratified, 'sampleCount': 16}) + VBufferRT = createPass("VBufferRT", {'samplePattern': 'Stratified', 'sampleCount': 16}) g.addPass(VBufferRT, "VBufferRT") - AccumulatePass = createPass("AccumulatePass", {'enabled': True, 'precisionMode': AccumulatePrecision.Single}) + AccumulatePass = createPass("AccumulatePass", {'enabled': True, 'precisionMode': 'Single'}) g.addPass(AccumulatePass, "AccumulatePass") ToneMapper = createPass("ToneMapper", {'autoExposure': False, 'exposureCompensation': 0.0}) g.addPass(ToneMapper, "ToneMapper") diff --git a/tests/image_tests/renderpasses/graphs/SDFEditorRenderGraphV2.py b/tests/image_tests/renderpasses/graphs/SDFEditorRenderGraphV2.py index b04440b72..981a7cb24 100644 --- a/tests/image_tests/renderpasses/graphs/SDFEditorRenderGraphV2.py +++ b/tests/image_tests/renderpasses/graphs/SDFEditorRenderGraphV2.py @@ -4,7 +4,7 @@ def render_graph_DefaultRenderGraph(): g = RenderGraph('DefaultRenderGraph') GBufferRT = createPass('GBufferRT') g.addPass(GBufferRT, 'GBufferRT') - AccumulatePass = createPass("AccumulatePass", {'enabled': True, 'precisionMode': AccumulatePrecision.Single}) + AccumulatePass = createPass("AccumulatePass", {'enabled': True, 'precisionMode': 'Single'}) g.addPass(AccumulatePass, 'AccumulatePass') ToneMapper = createPass("ToneMapper", {'autoExposure': False, 'exposureCompensation': 0.0}) g.addPass(ToneMapper, 'ToneMapper') diff --git a/tests/image_tests/renderpasses/graphs/SVGF.py b/tests/image_tests/renderpasses/graphs/SVGF.py index c84eaf8a6..9971ec4f8 100644 --- a/tests/image_tests/renderpasses/graphs/SVGF.py +++ b/tests/image_tests/renderpasses/graphs/SVGF.py @@ -4,7 +4,7 @@ def render_graph_SVGF(): g = RenderGraph("SVGF") SVGFPass = createPass("SVGFPass", {'Enabled': True, 'Iterations': 4, 'FeedbackTap': 1, 'VarianceEpsilon': 9.999999747378752e-05, 'PhiColor': 10.0, 'PhiNormal': 128.0, 'Alpha': 0.05000000074505806, 'MomentsAlpha': 0.20000000298023224}) g.addPass(SVGFPass, "SVGFPass") - GBufferRaster = createPass("GBufferRaster", {'cull': CullMode.CullBack}) + GBufferRaster = createPass("GBufferRaster", {'cull': 'Back'}) g.addPass(GBufferRaster, "GBufferRaster") PathTracer = createPass("PathTracer") g.addPass(PathTracer, "PathTracer") diff --git a/tests/image_tests/renderpasses/graphs/SideBySide.py b/tests/image_tests/renderpasses/graphs/SideBySide.py index 10a78ed4e..95b58e983 100644 --- a/tests/image_tests/renderpasses/graphs/SideBySide.py +++ b/tests/image_tests/renderpasses/graphs/SideBySide.py @@ -1,8 +1,8 @@ from falcor import * def test_SideBySide(): - imageLoaderA = createPass("ImageLoader", {'filename': 'Cubemaps/Sorsele3/posz.jpg', 'mips': False, 'srgb': False}) - imageLoaderB = createPass("ImageLoader", {'filename': 'Cubemaps/Sorsele3/posz.jpg', 'mips': False, 'srgb': True}) + imageLoaderA = createPass("ImageLoader", {'filename': 'test_images/cubemap/sorsele3/posz.jpg', 'mips': False, 'srgb': False}) + imageLoaderB = createPass("ImageLoader", {'filename': 'test_images/cubemap/sorsele3/posz.jpg', 'mips': False, 'srgb': True}) sideComparison = createPass("SideBySidePass") graph = RenderGraph("Side by Side") diff --git a/tests/image_tests/renderpasses/graphs/SimplePostFX.py b/tests/image_tests/renderpasses/graphs/SimplePostFX.py index 660ca4c6a..24191c1a9 100644 --- a/tests/image_tests/renderpasses/graphs/SimplePostFX.py +++ b/tests/image_tests/renderpasses/graphs/SimplePostFX.py @@ -2,7 +2,7 @@ def render_graph_SimplePostFX(): testSimplePostFX = RenderGraph("SimplePostFX") - ImageLoader = createPass("ImageLoader", {'filename' : "LightProbes/20060807_wells6_hd.hdr", 'mips': False, 'srgb': False}) + ImageLoader = createPass("ImageLoader", {'filename' : "test_scenes/envmaps/20060807_wells6_hd.hdr", 'mips': False, 'srgb': False}) testSimplePostFX.addPass(ImageLoader, "ImageLoader") PostFXPass = createPass("SimplePostFX", {'bloomAmount': 0.5, @@ -20,7 +20,7 @@ def render_graph_SimplePostFX(): 'enabled': True, 'wipe': 0.33}) testSimplePostFX.addPass(PostFXPass, "SimplePostFX") - BlitPass = createPass("BlitPass", {'filter': SamplerFilter.Linear}) + BlitPass = createPass("BlitPass", {'filter': 'Linear'}) testSimplePostFX.addPass(BlitPass, "BlitPass") testSimplePostFX.addEdge("ImageLoader.dst", "SimplePostFX.src") testSimplePostFX.addEdge("SimplePostFX.dst", "BlitPass.src") diff --git a/tests/image_tests/renderpasses/graphs/SplitScreen.py b/tests/image_tests/renderpasses/graphs/SplitScreen.py index fce6146a8..d9d081653 100644 --- a/tests/image_tests/renderpasses/graphs/SplitScreen.py +++ b/tests/image_tests/renderpasses/graphs/SplitScreen.py @@ -1,8 +1,8 @@ from falcor import * def test_SplitScreen(): - imageLoaderA = createPass("ImageLoader", {'filename': 'Cubemaps/Sorsele3/posz.jpg', 'mips': False, 'srgb': False}) - imageLoaderB = createPass("ImageLoader", {'filename': 'Cubemaps/Sorsele3/posz.jpg', 'mips': False, 'srgb': True}) + imageLoaderA = createPass("ImageLoader", {'filename': 'test_images/cubemap/sorsele3/posz.jpg', 'mips': False, 'srgb': False}) + imageLoaderB = createPass("ImageLoader", {'filename': 'test_images/cubemap/sorsele3/posz.jpg', 'mips': False, 'srgb': True}) splitScreen = createPass("SplitScreenPass") graph = RenderGraph("Split Screen Graph") diff --git a/tests/image_tests/renderpasses/graphs/TAA.py b/tests/image_tests/renderpasses/graphs/TAA.py index 6a23f616e..e75193021 100644 --- a/tests/image_tests/renderpasses/graphs/TAA.py +++ b/tests/image_tests/renderpasses/graphs/TAA.py @@ -2,7 +2,7 @@ def render_graph_TAA(): testTAA = RenderGraph("TAA") - GBufferRaster = createPass("GBufferRaster", {"samplePattern": SamplePattern.Halton}) + GBufferRaster = createPass("GBufferRaster", {"samplePattern": 'Halton'}) testTAA.addPass(GBufferRaster, "GBufferRaster") TAAPass = createPass("TAA") testTAA.addPass(TAAPass, "TAA") diff --git a/tests/image_tests/renderpasses/graphs/ToneMapping.py b/tests/image_tests/renderpasses/graphs/ToneMapping.py index fd08498c2..fb0a5d453 100644 --- a/tests/image_tests/renderpasses/graphs/ToneMapping.py +++ b/tests/image_tests/renderpasses/graphs/ToneMapping.py @@ -2,11 +2,11 @@ def render_graph_ToneMapping(): testToneMapping = RenderGraph("ToneMapper") - ImageLoader = createPass("ImageLoader", {'filename' : "LightProbes/hallstatt4_hd.hdr", 'mips': False, 'srgb': True}) + ImageLoader = createPass("ImageLoader", {'filename' : "test_scenes/envmaps/hallstatt4_hd.hdr", 'mips': False, 'srgb': True}) testToneMapping.addPass(ImageLoader, "ImageLoader") ToneMapping = createPass("ToneMapper") testToneMapping.addPass(ToneMapping, "ToneMapping") - BlitPass = createPass("BlitPass", {'filter': SamplerFilter.Linear}) + BlitPass = createPass("BlitPass", {'filter': 'Linear'}) testToneMapping.addPass(BlitPass, "BlitPass") testToneMapping.addEdge("ImageLoader.dst", "ToneMapping.src") testToneMapping.addEdge("ToneMapping.dst", "BlitPass.src") diff --git a/tests/image_tests/renderpasses/graphs/WhittedRayTracer.py b/tests/image_tests/renderpasses/graphs/WhittedRayTracer.py index 849912cd1..1c4c24df2 100644 --- a/tests/image_tests/renderpasses/graphs/WhittedRayTracer.py +++ b/tests/image_tests/renderpasses/graphs/WhittedRayTracer.py @@ -2,11 +2,11 @@ def render_graph_WhittedRayTracer(): g = RenderGraph("WhittedRayTracer") - WhittedRayTracer = createPass("WhittedRayTracer", {'maxBounces': 7, 'texLODMode': TexLODMode.RayCones, 'rayConeMode': RayConeMode.Unified, 'rayConeFilterMode': RayFootprintFilterMode.AnisotropicWhenRefraction, 'useRoughnessToVariance': False}) + WhittedRayTracer = createPass("WhittedRayTracer", {'maxBounces': 7, 'texLODMode': 'RayCones', 'rayConeMode': 'Unified', 'rayConeFilterMode': 'AnisotropicWhenRefraction', 'useRoughnessToVariance': False}) g.addPass(WhittedRayTracer, "WhittedRayTracer") - GBufferRT = createPass("GBufferRT", {'samplePattern': SamplePattern.Center, 'sampleCount': 1}) + GBufferRT = createPass("GBufferRT", {'samplePattern': 'Center', 'sampleCount': 1}) g.addPass(GBufferRT, "GBufferRT") - ToneMapper = createPass("ToneMapper", {'autoExposure': False, 'exposureValue': 1.0, 'exposureCompensation': 2.2, 'operator': ToneMapOp.Linear}) + ToneMapper = createPass("ToneMapper", {'autoExposure': False, 'exposureValue': 1.0, 'exposureCompensation': 2.2, 'operator': 'Linear'}) g.addPass(ToneMapper, "ToneMapper") g.addEdge("WhittedRayTracer.color", "ToneMapper.src") g.addEdge("GBufferRT.posW", "WhittedRayTracer.posW") diff --git a/tests/image_tests/renderpasses/test_ColorMapPass.py b/tests/image_tests/renderpasses/test_ColorMapPass.py index 3916d1f79..8c2e32b02 100644 --- a/tests/image_tests/renderpasses/test_ColorMapPass.py +++ b/tests/image_tests/renderpasses/test_ColorMapPass.py @@ -14,9 +14,10 @@ render_frames(m, 'default') # colorMap -for colorMap in [ColorMap.Grey, ColorMap.Jet, ColorMap.Viridis, ColorMap.Plasma, ColorMap.Magma, ColorMap.Inferno]: +for colorMap in ['Grey', 'Jet', 'Viridis', 'Plasma', 'Magma', 'Inferno']: g.updatePass('ColorMap', {'colorMap': colorMap}) - render_frames(m, 'colorMap.' + str(colorMap)) + # TODO: Remove "ColorMap." from name. + render_frames(m, 'colorMap.ColorMap.' + colorMap) # channel for channel in [0, 1, 2, 3]: @@ -25,7 +26,7 @@ # minValue, maxValue for (minValue, maxValue) in [(0, 1), (1, 0), (0.25, 0.75)]: - g.updatePass('ColorMap', {'colorMap': ColorMap.Jet, 'minValue': minValue, 'maxValue': maxValue}) + g.updatePass('ColorMap', {'colorMap': 'Jet', 'minValue': minValue, 'maxValue': maxValue}) render_frames(m, 'minValue.' + str(minValue) + '.maxValue.' + str(maxValue)) # autoRange diff --git a/tests/image_tests/renderpasses/test_CompositePass.py b/tests/image_tests/renderpasses/test_CompositePass.py index aa650a9d8..e2bc5b6f4 100644 --- a/tests/image_tests/renderpasses/test_CompositePass.py +++ b/tests/image_tests/renderpasses/test_CompositePass.py @@ -14,13 +14,14 @@ render_frames(m, 'default') # modes -for mode in [CompositeMode.Add, CompositeMode.Multiply]: +for mode in ['Add', 'Multiply']: g.updatePass('Composite', {'mode': mode}) - render_frames(m, 'mode.' + str(mode)) + # TODO: Remove "CompositeMode." from name. + render_frames(m, 'mode.CompositeMode.' + mode) # scaleA, scaleB for scaleA, scaleB in [(0.5, 1.5), (1.0, 1.0), (1.5, 0.5)]: - g.updatePass('Composite', {'mode': CompositeMode.Add, 'scaleA': scaleA, 'scaleB': scaleB}) + g.updatePass('Composite', {'mode': 'Add', 'scaleA': scaleA, 'scaleB': scaleB}) render_frames(m, 'scaleA.' + str(scaleA) + '.scaleB.' + str(scaleB)) exit() diff --git a/tests/image_tests/renderpasses/test_FLIPPass.py b/tests/image_tests/renderpasses/test_FLIPPass.py index 192a16e85..4c6bb753e 100644 --- a/tests/image_tests/renderpasses/test_FLIPPass.py +++ b/tests/image_tests/renderpasses/test_FLIPPass.py @@ -20,8 +20,9 @@ render_frames(m, 'isHDR.' + str(isHDR)) # toneMapper -for toneMapper in [FLIPToneMapperType.ACES, FLIPToneMapperType.Hable, FLIPToneMapperType.Reinhard]: +for toneMapper in ['ACES', 'Hable', 'Reinhard']: g.updatePass('FLIP', {'isHDR': True, 'toneMapper': toneMapper}) - render_frames(m, 'toneMapper.' + str(toneMapper)) + # TODO: Remove "FLIPToneMapperType." from name. + render_frames(m, 'toneMapper.FLIPToneMapperType.' + toneMapper) exit() diff --git a/tests/image_tests/renderpasses/test_GBufferRTTexGrads.py b/tests/image_tests/renderpasses/test_GBufferRTTexGrads.py index e1e103ffc..d8a3a0586 100644 --- a/tests/image_tests/renderpasses/test_GBufferRTTexGrads.py +++ b/tests/image_tests/renderpasses/test_GBufferRTTexGrads.py @@ -6,18 +6,20 @@ from falcor import * m.addGraph(g) -m.loadScene('TestScenes/texLOD/spheres_cube.pyscene') +m.loadScene('test_scenes/tex_lod/spheres_cube.pyscene') -texLODModes = [TexLODMode.Mip0, TexLODMode.RayCones, TexLODMode.RayDiffs] +texLODModes = ['Mip0', 'RayCones', 'RayDiffs'] # texGrads for mode in texLODModes: g.updatePass('GBufferRT', {'texLOD': mode, "useTraceRayInline": False}) - render_frames(m, 'texGrads.' + str(mode)) + # TODO: Remove "TexLODMode." from name. + render_frames(m, 'texGrads.TexLODMode.' + mode) # texGrads trace ray inline for mode in texLODModes: g.updatePass('GBufferRT', {'texLOD': mode, "useTraceRayInline": True}) - render_frames(m, 'texGrads-inline.' + str(mode)) + # TODO: Remove "TexLODMode." from name. + render_frames(m, 'texGrads-inline.TexLODMode.' + mode) exit() diff --git a/tests/image_tests/renderpasses/test_GBufferRasterAlpha.py b/tests/image_tests/renderpasses/test_GBufferRasterAlpha.py index 0053fb14d..3fd0a641b 100644 --- a/tests/image_tests/renderpasses/test_GBufferRasterAlpha.py +++ b/tests/image_tests/renderpasses/test_GBufferRasterAlpha.py @@ -5,25 +5,25 @@ from falcor import * m.addGraph(g) -m.loadScene('TestScenes/AlphaTest/AlphaTest.pyscene') +m.loadScene('test_scenes/alpha_test/alpha_test.pyscene') # default render_frames(m, 'default', frames=[1]) # force cull back -g.updatePass('GBufferRaster', {'forceCullMode': True, 'cull': CullMode.CullBack}) +g.updatePass('GBufferRaster', {'forceCullMode': True, 'cull': 'Back'}) render_frames(m, 'cullback', frames=[1]) # force cull front -g.updatePass('GBufferRaster', {'forceCullMode': True, 'cull': CullMode.CullFront}) +g.updatePass('GBufferRaster', {'forceCullMode': True, 'cull': 'Front'}) render_frames(m, 'cullfront', frames=[1]) # force cull none -g.updatePass('GBufferRaster', {'forceCullMode': True, 'cull': CullMode.CullNone}) +g.updatePass('GBufferRaster', {'forceCullMode': True, 'cull': 'None'}) render_frames(m, 'cullnone', frames=[1]) # disable alpha -g.updatePass('GBufferRaster', {'forceCullMode': True, 'cull': CullMode.CullNone, 'useAlphaTest': False}) +g.updatePass('GBufferRaster', {'forceCullMode': True, 'cull': 'None', 'useAlphaTest': False}) render_frames(m, 'alphaoff', frames=[1]) exit() diff --git a/tests/image_tests/renderpasses/test_PathTracerDielectrics.py b/tests/image_tests/renderpasses/test_PathTracerDielectrics.py index ba5428ad5..41ba01d4f 100644 --- a/tests/image_tests/renderpasses/test_PathTracerDielectrics.py +++ b/tests/image_tests/renderpasses/test_PathTracerDielectrics.py @@ -5,7 +5,7 @@ from falcor import * m.addGraph(g) -m.loadScene('TestScenes/NestedDielectrics.pyscene') +m.loadScene('test_scenes/nested_dielectrics.pyscene') # default render_frames(m, 'default', frames=[1,256]) diff --git a/tests/image_tests/renderpasses/test_PathTracerMaterials.py b/tests/image_tests/renderpasses/test_PathTracerMaterials.py index 1a40d3c4e..54aa21403 100644 --- a/tests/image_tests/renderpasses/test_PathTracerMaterials.py +++ b/tests/image_tests/renderpasses/test_PathTracerMaterials.py @@ -7,19 +7,19 @@ m.addGraph(g) # Test variations of the standard material -m.loadScene('TestScenes/MaterialTest.pyscene') +m.loadScene('test_scenes/material_test.pyscene') render_frames(m, 'default', frames=[1,256]) # Test different material types -m.loadScene('TestScenes/Materials/Materials.pyscene') +m.loadScene('test_scenes/materials/materials.pyscene') render_frames(m, 'types', frames=[1,256]) # Test for light leaks -m.loadScene('TestScenes/Materials/LightLeaks.pyscene') +m.loadScene('test_scenes/materials/light_leaks.pyscene') render_frames(m, 'leaks', frames=[1,256]) # Test alpha testing -m.loadScene('TestScenes/AlphaTest/AlphaTest.pyscene') +m.loadScene('test_scenes/alpha_test/alpha_test.pyscene') render_frames(m, 'alpha', frames=[1,64]) # Test disabling alpha testing on secondary hits diff --git a/tests/image_tests/renderpasses/test_PathTracerReload.py b/tests/image_tests/renderpasses/test_PathTracerReload.py index 7fa1e03ec..aad4708f0 100644 --- a/tests/image_tests/renderpasses/test_PathTracerReload.py +++ b/tests/image_tests/renderpasses/test_PathTracerReload.py @@ -7,11 +7,11 @@ m.addGraph(g) # default -m.loadScene('TestScenes/CornellBox.pyscene') +m.loadScene('test_scenes/cornell_box.pyscene') render_frames(m, 'default', frames=[1]) # load other scene -m.loadScene('TestScenes/NestedDielectrics.pyscene') +m.loadScene('test_scenes/nested_dielectrics.pyscene') render_frames(m, 'nested', frames=[1]) exit() diff --git a/tests/image_tests/renderpasses/test_TextureLOD.py b/tests/image_tests/renderpasses/test_TextureLOD.py index 03f51c5b3..be6f1938f 100644 --- a/tests/image_tests/renderpasses/test_TextureLOD.py +++ b/tests/image_tests/renderpasses/test_TextureLOD.py @@ -6,19 +6,21 @@ from falcor import * m.addGraph(g) -m.loadScene('TestScenes/texLOD/spheres_cube.pyscene') +m.loadScene('test_scenes/tex_lod/spheres_cube.pyscene') # default render_frames(m, 'default') # texLODMode -for mode in [TexLODMode.Mip0, TexLODMode.RayCones, TexLODMode.RayDiffs]: +for mode in ['Mip0', 'RayCones', 'RayDiffs']: g.updatePass('WhittedRayTracer', {'maxBounces': 7, 'texLODMode': mode}) - render_frames(m, 'texLODMode.' + str(mode)) + # TODO: Remove "TexLODMode." from name. + render_frames(m, 'texLODMode.TexLODMode.' + mode) # rayConeFilterMode -for mode in [RayFootprintFilterMode.Isotropic, RayFootprintFilterMode.Anisotropic, RayFootprintFilterMode.AnisotropicWhenRefraction]: - g.updatePass('WhittedRayTracer', {'maxBounces': 7, 'texLODMode': TexLODMode.RayCones, 'rayConeFilterMode': mode}) - render_frames(m, 'rayConeFilterMode.' + str(mode)) +for mode in ['Isotropic', 'Anisotropic', 'AnisotropicWhenRefraction']: + g.updatePass('WhittedRayTracer', {'maxBounces': 7, 'texLODMode': 'RayCones', 'rayConeFilterMode': mode}) + # TODO: Remove "RayFootprintFilterMode." from name. + render_frames(m, 'rayConeFilterMode.RayFootprintFilterMode.' + mode) exit() diff --git a/tests/image_tests/renderpasses/test_ToneMapping.py b/tests/image_tests/renderpasses/test_ToneMapping.py index bc2a24057..82f6dedef 100644 --- a/tests/image_tests/renderpasses/test_ToneMapping.py +++ b/tests/image_tests/renderpasses/test_ToneMapping.py @@ -14,9 +14,10 @@ render_frames(m, 'default') # operator -for operator in [ToneMapOp.Linear, ToneMapOp.Reinhard, ToneMapOp.ReinhardModified, ToneMapOp.HejiHableAlu, ToneMapOp.HableUc2, ToneMapOp.Aces]: +for operator in ['Linear', 'Reinhard', 'ReinhardModified', 'HejiHableAlu', 'HableUc2', 'Aces']: g.updatePass('ToneMapping', {'operator': operator}) - render_frames(m, 'operator.' + str(operator)) + # TODO: Remove "ToneMapOp." from name. + render_frames(m, 'operator.ToneMapOp.' + operator) # autoExposure for b in [False, True]: diff --git a/tests/image_tests/renderpasses/test_VBufferRasterAlpha.py b/tests/image_tests/renderpasses/test_VBufferRasterAlpha.py index 30b4feced..e084c54c5 100644 --- a/tests/image_tests/renderpasses/test_VBufferRasterAlpha.py +++ b/tests/image_tests/renderpasses/test_VBufferRasterAlpha.py @@ -5,25 +5,25 @@ from falcor import * m.addGraph(g) -m.loadScene('TestScenes/AlphaTest/AlphaTest.pyscene') +m.loadScene('test_scenes/alpha_test/alpha_test.pyscene') # default render_frames(m, 'default', frames=[1]) # force cull back -g.updatePass('VBufferRaster', {'forceCullMode': True, 'cull': CullMode.CullBack}) +g.updatePass('VBufferRaster', {'forceCullMode': True, 'cull': 'Back'}) render_frames(m, 'cullback', frames=[1]) # force cull front -g.updatePass('VBufferRaster', {'forceCullMode': True, 'cull': CullMode.CullFront}) +g.updatePass('VBufferRaster', {'forceCullMode': True, 'cull': 'Front'}) render_frames(m, 'cullfront', frames=[1]) # force cull none -g.updatePass('VBufferRaster', {'forceCullMode': True, 'cull': CullMode.CullNone}) +g.updatePass('VBufferRaster', {'forceCullMode': True, 'cull': 'None'}) render_frames(m, 'cullnone', frames=[1]) # disable alpha -g.updatePass('VBufferRaster', {'forceCullMode': True, 'cull': CullMode.CullNone, 'useAlphaTest': False}) +g.updatePass('VBufferRaster', {'forceCullMode': True, 'cull': 'None', 'useAlphaTest': False}) render_frames(m, 'alphaoff', frames=[1]) exit() diff --git a/tests/image_tests/renderscripts/test_BSDFViewer.py b/tests/image_tests/renderscripts/test_BSDFViewer.py index ce2a3ab64..27a84acba 100644 --- a/tests/image_tests/renderscripts/test_BSDFViewer.py +++ b/tests/image_tests/renderscripts/test_BSDFViewer.py @@ -10,7 +10,7 @@ render_frames(m, 'arcade', frames=[16]) # materials -m.loadScene('TestScenes/MaterialTest.pyscene') +m.loadScene('test_scenes/material_test.pyscene') render_frames(m, 'materials', frames=[16]) exit() diff --git a/tests/image_tests/renderscripts/test_MinimalPathTracer.py b/tests/image_tests/renderscripts/test_MinimalPathTracer.py index a3b3e174d..f5fe840dd 100644 --- a/tests/image_tests/renderscripts/test_MinimalPathTracer.py +++ b/tests/image_tests/renderscripts/test_MinimalPathTracer.py @@ -13,7 +13,7 @@ render_frames(m, 'arcade', frames=[64]) # materials -m.loadScene('TestScenes/MaterialTest.pyscene') +m.loadScene('test_scenes/material_test.pyscene') render_frames(m, 'materials', frames=[64]) exit() diff --git a/tests/image_tests/scene/graphs/GBufferRTCullBack.py b/tests/image_tests/scene/graphs/GBufferRTCullBack.py index c91653c59..9eaf006ba 100644 --- a/tests/image_tests/scene/graphs/GBufferRTCullBack.py +++ b/tests/image_tests/scene/graphs/GBufferRTCullBack.py @@ -2,7 +2,7 @@ def render_graph_GBufferRTCullBack(): g = RenderGraph('GBufferRTCullBack') - GBufferRT = createPass('GBufferRT', {'samplePattern': SamplePattern.Center, 'forceCullMode': True, 'cull': CullMode.CullBack}) + GBufferRT = createPass('GBufferRT', {'samplePattern': 'Center', 'forceCullMode': True, 'cull': 'Back'}) g.addPass(GBufferRT, 'GBufferRT') g.markOutput('GBufferRT.faceNormalW') return g diff --git a/tests/image_tests/scene/graphs/GBufferRasterCullBack.py b/tests/image_tests/scene/graphs/GBufferRasterCullBack.py index 94a38c02e..8d700d49d 100644 --- a/tests/image_tests/scene/graphs/GBufferRasterCullBack.py +++ b/tests/image_tests/scene/graphs/GBufferRasterCullBack.py @@ -2,7 +2,7 @@ def render_graph_GBufferRasterCullBack(): g = RenderGraph('GBufferRasterCullBack') - GBufferRaster = createPass('GBufferRaster', {'samplePattern': SamplePattern.Center, 'forceCullMode': True, 'cull': CullMode.CullBack}) + GBufferRaster = createPass('GBufferRaster', {'samplePattern': 'Center', 'forceCullMode': True, 'cull': 'Back'}) g.addPass(GBufferRaster, 'GBufferRaster') g.markOutput('GBufferRaster.faceNormalW') return g diff --git a/tests/image_tests/scene/graphs/PathTracer.py b/tests/image_tests/scene/graphs/PathTracer.py index 459170ad3..4f2fbbffb 100644 --- a/tests/image_tests/scene/graphs/PathTracer.py +++ b/tests/image_tests/scene/graphs/PathTracer.py @@ -4,9 +4,9 @@ def render_graph_PathTracer(): g = RenderGraph("PathTracer") PathTracer = createPass("PathTracer", {'samplesPerPixel': 1, 'maxSurfaceBounces': 10}) g.addPass(PathTracer, "PathTracer") - VBufferRT = createPass("VBufferRT", {'samplePattern': SamplePattern.Stratified, 'sampleCount': 16}) + VBufferRT = createPass("VBufferRT", {'samplePattern': 'Stratified', 'sampleCount': 16}) g.addPass(VBufferRT, "VBufferRT") - AccumulatePass = createPass("AccumulatePass", {'enabled': True, 'precisionMode': AccumulatePrecision.Single}) + AccumulatePass = createPass("AccumulatePass", {'enabled': True, 'precisionMode': 'Single'}) g.addPass(AccumulatePass, "AccumulatePass") ToneMapper = createPass("ToneMapper", {'autoExposure': False, 'exposureCompensation': 0.0}) g.addPass(ToneMapper, "ToneMapper") diff --git a/tests/image_tests/scene/scenes/smoke.pyscene b/tests/image_tests/scene/scenes/smoke.pyscene index 82a045045..adbfc4eaf 100644 --- a/tests/image_tests/scene/scenes/smoke.pyscene +++ b/tests/image_tests/scene/scenes/smoke.pyscene @@ -1,7 +1,7 @@ # Create volumes smokeVolume = GridVolume('smoke') -smokeVolume.loadGrid(GridVolume.GridSlot.Density, 'TestScenes/smoke.vdb', 'density') +smokeVolume.loadGrid(GridVolume.GridSlot.Density, 'test_scenes/volumes/smoke.vdb', 'density') smokeVolume.densityScale = 0.5 smokeVolume.albedo = float3(0.5, 0.5, 0.5) sceneBuilder.addGridVolume(smokeVolume) diff --git a/tests/image_tests/scene/test_AnimationBehavior.py b/tests/image_tests/scene/test_AnimationBehavior.py index 28e48e660..446a32b42 100644 --- a/tests/image_tests/scene/test_AnimationBehavior.py +++ b/tests/image_tests/scene/test_AnimationBehavior.py @@ -5,7 +5,7 @@ from falcor import * m.addGraph(g) -m.loadScene("TestScenes/AnimatedCubes/AnimatedCubes.pyscene") +m.loadScene("test_scenes/animated_cubes/animated_cubes.pyscene") # preInfinityBehavior (behavior before first keyframe) render_frames(m, "preInfinity", frames=[1,90,180,270,350]) diff --git a/tests/image_tests/scene/test_Displacement.py b/tests/image_tests/scene/test_Displacement.py index be325e041..dd7ea36f8 100644 --- a/tests/image_tests/scene/test_Displacement.py +++ b/tests/image_tests/scene/test_Displacement.py @@ -6,7 +6,7 @@ from falcor import * m.addGraph(g) -m.loadScene('TestScenes/CornellBoxDisplaced.pyscene') +m.loadScene('test_scenes/cornell_box_displaced.pyscene') # default render_frames(m, 'default', frames=[64]) diff --git a/tests/image_tests/scene/test_RtProgram.py b/tests/image_tests/scene/test_RtProgram.py index ae2bb5725..4731ddc09 100644 --- a/tests/image_tests/scene/test_RtProgram.py +++ b/tests/image_tests/scene/test_RtProgram.py @@ -8,17 +8,17 @@ m.addGraph(g) # default: triangles and custom primitives -m.loadScene('TestScenes/GeometryTypes.pyscene') +m.loadScene('test_scenes/geometry_types.pyscene') render_frames(m, 'default', frames=[1]) # two_curves: triangles, curves, and custom primitives -m.loadScene('CurveTest/two_curves.pyscene') +m.loadScene('test_scenes/curves/two_curves.pyscene') render_frames(m, 'two_curves', frames=[1]) g.updatePass('TestRtProgram', {'mode': 1}) # test for dynamic dispatch -m.loadScene('TestScenes/AlphaTest/AlphaTest.pyscene') +m.loadScene('test_scenes/alpha_test/alpha_test.pyscene') render_frames(m, 'types', frames=[1]) exit() diff --git a/tests/image_tests/scene/test_TriangleWinding.py b/tests/image_tests/scene/test_TriangleWinding.py index 64ec33208..1d51c60e8 100644 --- a/tests/image_tests/scene/test_TriangleWinding.py +++ b/tests/image_tests/scene/test_TriangleWinding.py @@ -8,14 +8,14 @@ from falcor import * # Load test scene that has mixed triangle winding in object/world space -m.loadScene('TestScenes/WindingTest.pyscene') +m.loadScene('test_scenes/winding_test.pyscene') m.addGraph(SceneDebuggerGraph) -SceneDebuggerGraph.getPass('SceneDebugger').mode = SceneDebuggerMode.FrontFacingFlag +SceneDebuggerGraph.getPass('SceneDebugger').mode = 'FrontFacingFlag' render_frames(m, 'frontfacing', frames=[2]) -SceneDebuggerGraph.getPass('SceneDebugger').mode = SceneDebuggerMode.FaceNormal +SceneDebuggerGraph.getPass('SceneDebugger').mode = 'FaceNormal' render_frames(m, 'facenormal', frames=[2]) diff --git a/tests/image_tests/scene/test_USDPreviewSurface.py b/tests/image_tests/scene/test_USDPreviewSurface.py index 5d621d416..bb741cd1d 100644 --- a/tests/image_tests/scene/test_USDPreviewSurface.py +++ b/tests/image_tests/scene/test_USDPreviewSurface.py @@ -8,11 +8,11 @@ m.addGraph(g) # Knob -m.loadScene('TestScenes/MoriKnob/knob.usd') +m.loadScene('test_scenes/mori_knob/knob.usd') render_frames(m, 'Knob', frames=[1,64]) # KnobMaterials -m.loadScene('TestScenes/MoriKnob/KnobMaterials.usda') +m.loadScene('test_scenes/mori_knob/knob_materials.usda') render_frames(m, 'KnobMaterials', frames=[1,64]) exit() diff --git a/tests/python_tests/core/test_device.py b/tests/python_tests/core/test_device.py index c38e36016..759017790 100644 --- a/tests/python_tests/core/test_device.py +++ b/tests/python_tests/core/test_device.py @@ -17,8 +17,11 @@ def test_create(self): 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}") + print(f"info.adapter_name={device.info.adapter_name}") + print(f"info.api_name={device.info.api_name}") + + print(f"limits.max_compute_dispatch_thread_groups={device.limits.max_compute_dispatch_thread_groups}") + print(f"limits.max_shader_visible_samplers={device.limits.max_shader_visible_samplers}") del device diff --git a/tests/python_tests/test_dummy.py b/tests/python_tests/test_dummy.py index 544aba0e9..32f8984f9 100644 --- a/tests/python_tests/test_dummy.py +++ b/tests/python_tests/test_dummy.py @@ -1,4 +1,5 @@ import unittest +import numpy as np class TestDummy(unittest.TestCase): def test_upper(self): @@ -8,5 +9,10 @@ def test_isupper(self): self.assertTrue("FOO".isupper()) self.assertFalse("Foo".isupper()) + def test_numpy(self): + x = np.ones((2,2)) + x = 2 * x + self.assertEqual(x[0,0], 2.0) + if __name__ == '__main__': unittest.main() diff --git a/tests/run_python_tests.bat b/tests/run_python_tests.bat index bfcb15cfe..3bce99fa5 100644 --- a/tests/run_python_tests.bat +++ b/tests/run_python_tests.bat @@ -1,9 +1,8 @@ @echo off -set pwd=%~dp0 -set project_dir=%pwd%..\ -set python=%project_dir%tools\.packman\python\python.exe +if "%CONDA_PYTHON_EXE%"=="" ( + echo Python tests require conda environment to run. + exit /b 1 +) -if not exist %python% call %project_dir%setup.bat - -call %python% %pwd%testing/run_python_tests.py %* +python %~dp0testing/run_python_tests.py %* diff --git a/tests/run_python_tests.sh b/tests/run_python_tests.sh index 0da6ed8e1..9049f1825 100644 --- a/tests/run_python_tests.sh +++ b/tests/run_python_tests.sh @@ -1,12 +1,10 @@ #!/bin/sh -export pwd=`pwd` -export project_dir=$pwd/.. -export python_dir=$project_dir/tools/.packman/python -export python=$python_dir/bin/python3 +DIR="$( dirname -- "$BASH_SOURCE"; )"; -if [ ! -f "$python" ]; then - $project_dir/setup.sh +if [ -z "${CONDA_PYTHON_EXE}" ]; then + echo "Python tests require conda environment to run." + exit 1 fi -env LD_LIBRARY_PATH="$python_dir/lib" $python $pwd/testing/run_python_tests.py $@ +python ${DIR}/testing/run_python_tests.py $@ diff --git a/tests/testing/run_python_tests.py b/tests/testing/run_python_tests.py index ec3b159d1..4cc1d2413 100644 --- a/tests/testing/run_python_tests.py +++ b/tests/testing/run_python_tests.py @@ -16,7 +16,7 @@ def run_python_tests(env: Environment, xml_report, args): """ Run python tests. """ - cmd_args = [str(env.python_exe)] + cmd_args = ["python"] if xml_report: cmd_args += ["-m", "xmlrunner", "--output-file", str(xml_report)] else: @@ -93,7 +93,7 @@ def main(): 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"]) + subprocess.call(["python", "-m", "unittest", "-h"]) else: print(f"\nFailed to load environment: {env_error}") sys.exit(0) diff --git a/tools/fix_render_script.py b/tools/fix_render_script.py new file mode 100644 index 000000000..a5723c030 --- /dev/null +++ b/tools/fix_render_script.py @@ -0,0 +1,138 @@ +import re +import glob +import argparse + +# List of Python enum types that are replaced with strings. +# The pattern EnumName.Value is replaced with 'Value'. +ENUMS = [ + "CompositeMode", + "ToneMapOp", + "ExposureMode", + "SceneDebuggerMode", + "TexLODMode", + "RayConeMode", + "RayFootprintFilterMode", + "ColorFormat", + "MISHeuristic", + "SchedulingMode", + "EmissiveLightSamplerType", + "OptixDenoiserModel", + "NRDMethod", + "SamplePattern", + "FLIPToneMapperType", + "OutputId", + "DLSSProfile", + "DLSSMotionVectorScale", + "ColorMap", + "BSDFViewerMode", + "AccumulatePrecision", + "AccumulateOverflowMode", + "TransformNormalPassOp", + "AdaptiveSamplerMode", + "AdaptiveSamplerAnimMode", + "AdaptiveSamplerClampMode", + "DenoiserModel", + "ExportPassFormat", + "ExportPassOp", + "ExportPassFreq", + "IOSize", + "SamplerFilter", + "AddressMode", + "ComparisonFunc", + "SplitHeuristic", + "SolidAngleBoundMethod", + "RTXDIMode", + "RTXDIBiasCorrection", + "TransmittanceEstimator", + "DistanceSampler", + "ResourceFormat", +] + +# Dictionary of Python enum values that are replaced with strings. +# This is used to convert enums that have different values in the Python bindings and the strings. +ENUM_MAP = { + "CullMode.CullNone": "None", + "CullMode.CullFront": "Front", + "CullMode.CullBack": "Back", +} + +SERIALIZABLE_STRUCTS = [ + "SplitSampleGeneratorOptions", + "EmissiveUniformSamplerOptions", + "LightBVHBuilderOptions", + "LightBVHSamplerOptions", + "RTXDIOptions", + "GridVolumeSamplerOptions", + "ScreenSpaceReSTIROptions", + "PathTracerParams", +] + +def is_render_script(text: str): + return "from falcor import *" in text + + +def update_enums(text): + for e in ENUMS: + r = re.compile(rf"([^'\"]){e}\.(\w+)") + if r.findall(text) != []: + print(f"Replacing '{e}' enum with strings") + text = r.sub(r"\1'\2'", text) + for k, v in ENUM_MAP.items(): + r = re.compile(rf"([^'\"]){k}") + if r.findall(text) != []: + print(f"Replacing '{k}' enum value with string") + text = r.sub(rf"\1'{v}'", text) + return text + + +RE_ARG = re.compile(r"([a-zA-Z0-9]+)=([a-zA-Z0-9\.\"']+)") + +def update_serializable_structs(text): + for s in SERIALIZABLE_STRUCTS: + r = re.compile(rf"{s}\(([^)]*)\)") + for m in r.finditer(text): + args = m[1] + args = re.sub(r"([a-zA-Z0-9]+)=", r"'\1': ", args) + args = "{" + args + "}" + text = text.replace(m[0], args) + + return text + + +def run(args): + files = list(glob.glob(args.path, recursive=True)) + + for file in files: + text = open(file, "r").read() + if not is_render_script(text): + if not args.force: + print(f"Skipping '{file}' which does not seem to be a render script.") + continue + print(f"Checking file '{file}' ...") + original = text + text = update_enums(text) + text = update_serializable_structs(text) + if not args.dry_run and text != original: + print(f"Writing file '{file}'") + open(file, "w").write(text) + + +parser = argparse.ArgumentParser(description="Utility for fixing render scripts") +parser.add_argument("path", type=str, help="Glob pattern for searching files") +parser.add_argument( + "-f", + "--force", + action="store_true", + default=False, + help="Force updating files even if not detected as render script", +) +parser.add_argument( + "-d", + "--dry-run", + action="store_true", + default=False, + help="Run without writing files", +) + +args = parser.parse_args() +run(args) diff --git a/tools/run_clang_format.py b/tools/run_clang_format.py index b967ad52a..e08fb125c 100644 --- a/tools/run_clang_format.py +++ b/tools/run_clang_format.py @@ -451,6 +451,8 @@ def main(): njobs = args.j if njobs == 0: njobs = multiprocessing.cpu_count() + 1 + # On windows we run into issues when using more than 64 processes + njobs = min(60, njobs) njobs = min(len(files), njobs) if njobs == 1: