diff options
Diffstat (limited to 'source/blender/compositor')
142 files changed, 5620 insertions, 825 deletions
diff --git a/source/blender/compositor/CMakeLists.txt b/source/blender/compositor/CMakeLists.txt index 55e349423bb..f49a9694ab3 100644 --- a/source/blender/compositor/CMakeLists.txt +++ b/source/blender/compositor/CMakeLists.txt @@ -1,676 +1,682 @@ # SPDX-License-Identifier: GPL-2.0-or-later # Copyright 2011 Blender Foundation. All rights reserved. -set(INC - . - intern - nodes - operations - ../blenkernel - ../blenlib - ../blentranslation - ../depsgraph - ../imbuf - ../makesdna - ../makesrna - ../nodes - ../windowmanager - ../nodes/composite - ../nodes/intern - ../render - ../render/intern - ../../../extern/clew/include - ../../../intern/atomic - ../../../intern/clog - ../../../intern/guardedalloc - - # dna_type_offsets.h - ${CMAKE_CURRENT_BINARY_DIR}/../makesdna/intern - # RNA_prototypes.h - ${CMAKE_BINARY_DIR}/source/blender/makesrna -) - -set(INC_SYS - -) - -set(SRC - COM_compositor.h - COM_defines.h - - intern/COM_BufferArea.h - intern/COM_BufferOperation.cc - intern/COM_BufferOperation.h - intern/COM_BufferRange.h - intern/COM_BuffersIterator.h - intern/COM_CPUDevice.cc - intern/COM_CPUDevice.h - intern/COM_ChunkOrder.cc - intern/COM_ChunkOrder.h - intern/COM_ChunkOrderHotspot.cc - intern/COM_ChunkOrderHotspot.h - intern/COM_CompositorContext.cc - intern/COM_CompositorContext.h - intern/COM_ConstantFolder.cc - intern/COM_ConstantFolder.h - intern/COM_Converter.cc - intern/COM_Converter.h - intern/COM_Debug.cc - intern/COM_Debug.h - intern/COM_Device.cc - intern/COM_Device.h - intern/COM_Enums.cc - intern/COM_Enums.h - intern/COM_ExecutionGroup.cc - intern/COM_ExecutionGroup.h - intern/COM_ExecutionModel.cc - intern/COM_ExecutionModel.h - intern/COM_ExecutionSystem.cc - intern/COM_ExecutionSystem.h - intern/COM_FullFrameExecutionModel.cc - intern/COM_FullFrameExecutionModel.h - intern/COM_MemoryBuffer.cc - intern/COM_MemoryBuffer.h - intern/COM_MemoryProxy.cc - intern/COM_MemoryProxy.h - intern/COM_MetaData.cc - intern/COM_MetaData.h - intern/COM_MultiThreadedOperation.cc - intern/COM_MultiThreadedOperation.h - intern/COM_MultiThreadedRowOperation.cc - intern/COM_MultiThreadedRowOperation.h - intern/COM_Node.cc - intern/COM_Node.h - intern/COM_NodeConverter.cc - intern/COM_NodeConverter.h - intern/COM_NodeGraph.cc - intern/COM_NodeGraph.h - intern/COM_NodeOperation.cc - intern/COM_NodeOperation.h - intern/COM_NodeOperationBuilder.cc - intern/COM_NodeOperationBuilder.h - intern/COM_OpenCLDevice.cc - intern/COM_OpenCLDevice.h - intern/COM_SharedOperationBuffers.cc - intern/COM_SharedOperationBuffers.h - intern/COM_SingleThreadedOperation.cc - intern/COM_SingleThreadedOperation.h - intern/COM_TiledExecutionModel.cc - intern/COM_TiledExecutionModel.h - intern/COM_WorkPackage.cc - intern/COM_WorkPackage.h - intern/COM_WorkScheduler.cc - intern/COM_WorkScheduler.h - intern/COM_compositor.cc - - operations/COM_QualityStepHelper.cc - operations/COM_QualityStepHelper.h - - # Internal nodes - nodes/COM_SocketProxyNode.cc - nodes/COM_SocketProxyNode.h - - # input nodes - nodes/COM_BokehImageNode.cc - nodes/COM_BokehImageNode.h - nodes/COM_ColorNode.cc - nodes/COM_ColorNode.h - nodes/COM_ImageNode.cc - nodes/COM_ImageNode.h - nodes/COM_MaskNode.cc - nodes/COM_MaskNode.h - nodes/COM_MovieClipNode.cc - nodes/COM_MovieClipNode.h - nodes/COM_OutputFileNode.cc - nodes/COM_OutputFileNode.h - nodes/COM_RenderLayersNode.cc - nodes/COM_RenderLayersNode.h - nodes/COM_SceneTimeNode.cc - nodes/COM_SceneTimeNode.h - nodes/COM_SwitchNode.cc - nodes/COM_SwitchNode.h - nodes/COM_SwitchViewNode.cc - nodes/COM_SwitchViewNode.h - nodes/COM_TextureNode.cc - nodes/COM_TextureNode.h - nodes/COM_TimeNode.cc - nodes/COM_TimeNode.h - nodes/COM_ValueNode.cc - nodes/COM_ValueNode.h - - # output nodes - nodes/COM_CompositorNode.cc - nodes/COM_CompositorNode.h - nodes/COM_SplitViewerNode.cc - nodes/COM_SplitViewerNode.h - nodes/COM_ViewLevelsNode.cc - nodes/COM_ViewLevelsNode.h - nodes/COM_ViewerNode.cc - nodes/COM_ViewerNode.h - operations/COM_CalculateMeanOperation.cc - operations/COM_CalculateMeanOperation.h - operations/COM_CalculateStandardDeviationOperation.cc - operations/COM_CalculateStandardDeviationOperation.h - - # distort nodes - nodes/COM_FlipNode.cc - nodes/COM_FlipNode.h - nodes/COM_RotateNode.cc - nodes/COM_RotateNode.h - nodes/COM_ScaleNode.cc - nodes/COM_ScaleNode.h - nodes/COM_TranslateNode.cc - nodes/COM_TranslateNode.h - - nodes/COM_DisplaceNode.cc - nodes/COM_DisplaceNode.h - nodes/COM_MapUVNode.cc - nodes/COM_MapUVNode.h - - nodes/COM_ChannelMatteNode.cc - nodes/COM_ChannelMatteNode.h - nodes/COM_ChromaMatteNode.cc - nodes/COM_ChromaMatteNode.h - nodes/COM_ColorMatteNode.cc - nodes/COM_ColorMatteNode.h - nodes/COM_DifferenceMatteNode.cc - nodes/COM_DifferenceMatteNode.h - nodes/COM_DistanceMatteNode.cc - nodes/COM_DistanceMatteNode.h - nodes/COM_LensDistortionNode.cc - nodes/COM_LensDistortionNode.h - nodes/COM_LuminanceMatteNode.cc - nodes/COM_LuminanceMatteNode.h - - nodes/COM_GlareNode.cc - nodes/COM_GlareNode.h - - nodes/COM_SunBeamsNode.cc - nodes/COM_SunBeamsNode.h - operations/COM_SunBeamsOperation.cc - operations/COM_SunBeamsOperation.h - - nodes/COM_CryptomatteNode.cc - nodes/COM_CryptomatteNode.h - operations/COM_CryptomatteOperation.cc - operations/COM_CryptomatteOperation.h - - nodes/COM_CornerPinNode.cc - nodes/COM_CornerPinNode.h - nodes/COM_PlaneTrackDeformNode.cc - nodes/COM_PlaneTrackDeformNode.h - - nodes/COM_CropNode.cc - nodes/COM_CropNode.h - operations/COM_CropOperation.cc - operations/COM_CropOperation.h - - nodes/COM_DefocusNode.cc - nodes/COM_DefocusNode.h - nodes/COM_MovieDistortionNode.cc - nodes/COM_MovieDistortionNode.h - nodes/COM_Stabilize2dNode.cc - nodes/COM_Stabilize2dNode.h - nodes/COM_TransformNode.cc - nodes/COM_TransformNode.h - - # color nodes - nodes/COM_AlphaOverNode.cc - nodes/COM_AlphaOverNode.h - nodes/COM_BrightnessNode.cc - nodes/COM_BrightnessNode.h - nodes/COM_ColorBalanceNode.cc - nodes/COM_ColorBalanceNode.h - nodes/COM_ColorCorrectionNode.cc - nodes/COM_ColorCorrectionNode.h - nodes/COM_ColorCurveNode.cc - nodes/COM_ColorCurveNode.h - nodes/COM_ColorExposureNode.cc - nodes/COM_ColorExposureNode.h - nodes/COM_ColorRampNode.cc - nodes/COM_ColorRampNode.h - nodes/COM_ColorToBWNode.cc - nodes/COM_ColorToBWNode.h - nodes/COM_ConvertAlphaNode.cc - nodes/COM_ConvertAlphaNode.h - nodes/COM_ConvertColorSpaceNode.cc - nodes/COM_ConvertColorSpaceNode.h - nodes/COM_GammaNode.cc - nodes/COM_GammaNode.h - nodes/COM_HueSaturationValueCorrectNode.cc - nodes/COM_HueSaturationValueCorrectNode.h - nodes/COM_HueSaturationValueNode.cc - nodes/COM_HueSaturationValueNode.h - nodes/COM_InvertNode.cc - nodes/COM_InvertNode.h - nodes/COM_MixNode.cc - nodes/COM_MixNode.h - nodes/COM_SetAlphaNode.cc - nodes/COM_SetAlphaNode.h - nodes/COM_TonemapNode.cc - nodes/COM_TonemapNode.h - nodes/COM_VectorCurveNode.cc - nodes/COM_VectorCurveNode.h - nodes/COM_ZCombineNode.cc - nodes/COM_ZCombineNode.h - operations/COM_TonemapOperation.cc - operations/COM_TonemapOperation.h - - # converter nodes - nodes/COM_CombineColorNode.cc - nodes/COM_CombineColorNode.h - nodes/COM_CombineColorNodeLegacy.cc - nodes/COM_CombineColorNodeLegacy.h - nodes/COM_CombineXYZNode.cc - nodes/COM_CombineXYZNode.h - nodes/COM_IDMaskNode.cc - nodes/COM_IDMaskNode.h - nodes/COM_SeparateColorNode.cc - nodes/COM_SeparateColorNode.h - nodes/COM_SeparateColorNodeLegacy.cc - nodes/COM_SeparateColorNodeLegacy.h - nodes/COM_SeparateXYZNode.cc - nodes/COM_SeparateXYZNode.h - - nodes/COM_MapRangeNode.cc - nodes/COM_MapRangeNode.h - nodes/COM_MapValueNode.cc - nodes/COM_MapValueNode.h - nodes/COM_MathNode.cc - nodes/COM_MathNode.h - nodes/COM_NormalNode.cc - nodes/COM_NormalNode.h - nodes/COM_NormalizeNode.cc - nodes/COM_NormalizeNode.h - - operations/COM_NormalizeOperation.cc - operations/COM_NormalizeOperation.h - - nodes/COM_PixelateNode.cc - nodes/COM_PixelateNode.h - operations/COM_PixelateOperation.cc - operations/COM_PixelateOperation.h - - # Filter nodes - nodes/COM_BilateralBlurNode.cc - nodes/COM_BilateralBlurNode.h - operations/COM_BilateralBlurOperation.cc - operations/COM_BilateralBlurOperation.h - nodes/COM_VectorBlurNode.cc - nodes/COM_VectorBlurNode.h - operations/COM_VectorBlurOperation.cc - operations/COM_VectorBlurOperation.h - nodes/COM_AntiAliasingNode.cc - nodes/COM_AntiAliasingNode.h - nodes/COM_BlurNode.cc - nodes/COM_BlurNode.h - nodes/COM_BokehBlurNode.cc - nodes/COM_BokehBlurNode.h - nodes/COM_DenoiseNode.cc - nodes/COM_DenoiseNode.h - nodes/COM_DespeckleNode.cc - nodes/COM_DespeckleNode.h - nodes/COM_DilateErodeNode.cc - nodes/COM_DilateErodeNode.h - nodes/COM_DirectionalBlurNode.cc - nodes/COM_DirectionalBlurNode.h - nodes/COM_FilterNode.cc - nodes/COM_FilterNode.h - nodes/COM_InpaintNode.cc - nodes/COM_InpaintNode.h - nodes/COM_PosterizeNode.cc - nodes/COM_PosterizeNode.h - - operations/COM_BlurBaseOperation.cc - operations/COM_BlurBaseOperation.h - operations/COM_BokehBlurOperation.cc - operations/COM_BokehBlurOperation.h - operations/COM_DirectionalBlurOperation.cc - operations/COM_DirectionalBlurOperation.h - operations/COM_FastGaussianBlurOperation.cc - operations/COM_FastGaussianBlurOperation.h - operations/COM_GammaCorrectOperation.cc - operations/COM_GammaCorrectOperation.h - operations/COM_GaussianAlphaBlurBaseOperation.cc - operations/COM_GaussianAlphaBlurBaseOperation.h - operations/COM_GaussianAlphaXBlurOperation.cc - operations/COM_GaussianAlphaXBlurOperation.h - operations/COM_GaussianAlphaYBlurOperation.cc - operations/COM_GaussianAlphaYBlurOperation.h - operations/COM_GaussianBlurBaseOperation.cc - operations/COM_GaussianBlurBaseOperation.h - operations/COM_GaussianBokehBlurOperation.cc - operations/COM_GaussianBokehBlurOperation.h - operations/COM_GaussianXBlurOperation.cc - operations/COM_GaussianXBlurOperation.h - operations/COM_GaussianYBlurOperation.cc - operations/COM_GaussianYBlurOperation.h - operations/COM_MovieClipAttributeOperation.cc - operations/COM_MovieClipAttributeOperation.h - operations/COM_MovieDistortionOperation.cc - operations/COM_MovieDistortionOperation.h - operations/COM_PosterizeOperation.cc - operations/COM_PosterizeOperation.h - operations/COM_SMAAOperation.cc - operations/COM_SMAAOperation.h - operations/COM_VariableSizeBokehBlurOperation.cc - operations/COM_VariableSizeBokehBlurOperation.h - - # Matte nodes - nodes/COM_BoxMaskNode.cc - nodes/COM_BoxMaskNode.h - nodes/COM_ColorSpillNode.cc - nodes/COM_ColorSpillNode.h - nodes/COM_DoubleEdgeMaskNode.cc - nodes/COM_DoubleEdgeMaskNode.h - nodes/COM_EllipseMaskNode.cc - nodes/COM_EllipseMaskNode.h - - operations/COM_DoubleEdgeMaskOperation.cc - operations/COM_DoubleEdgeMaskOperation.h - - - nodes/COM_KeyingScreenNode.cc - nodes/COM_KeyingScreenNode.h - operations/COM_KeyingScreenOperation.cc - operations/COM_KeyingScreenOperation.h - - nodes/COM_TrackPositionNode.cc - nodes/COM_TrackPositionNode.h - operations/COM_TrackPositionOperation.cc - operations/COM_TrackPositionOperation.h - - nodes/COM_KeyingNode.cc - nodes/COM_KeyingNode.h - operations/COM_KeyingBlurOperation.cc - operations/COM_KeyingBlurOperation.h - operations/COM_KeyingClipOperation.cc - operations/COM_KeyingClipOperation.h - operations/COM_KeyingDespillOperation.cc - operations/COM_KeyingDespillOperation.h - operations/COM_KeyingOperation.cc - operations/COM_KeyingOperation.h - - operations/COM_ColorSpillOperation.cc - operations/COM_ColorSpillOperation.h - operations/COM_RenderLayersProg.cc - operations/COM_RenderLayersProg.h - - operations/COM_BokehImageOperation.cc - operations/COM_BokehImageOperation.h - operations/COM_ImageOperation.cc - operations/COM_ImageOperation.h - operations/COM_MultilayerImageOperation.cc - operations/COM_MultilayerImageOperation.h - operations/COM_TextureOperation.cc - operations/COM_TextureOperation.h - - - operations/COM_SocketProxyOperation.cc - operations/COM_SocketProxyOperation.h - - operations/COM_CompositorOperation.cc - operations/COM_CompositorOperation.h - operations/COM_ConvertDepthToRadiusOperation.cc - operations/COM_ConvertDepthToRadiusOperation.h - operations/COM_OutputFileMultiViewOperation.cc - operations/COM_OutputFileMultiViewOperation.h - operations/COM_OutputFileOperation.cc - operations/COM_OutputFileOperation.h - operations/COM_PreviewOperation.cc - operations/COM_PreviewOperation.h - operations/COM_SplitOperation.cc - operations/COM_SplitOperation.h - operations/COM_ViewerOperation.cc - operations/COM_ViewerOperation.h - operations/COM_ZCombineOperation.cc - operations/COM_ZCombineOperation.h - - operations/COM_ChangeHSVOperation.cc - operations/COM_ChangeHSVOperation.h - operations/COM_ChannelMatteOperation.cc - operations/COM_ChannelMatteOperation.h - operations/COM_ChromaMatteOperation.cc - operations/COM_ChromaMatteOperation.h - operations/COM_ColorCurveOperation.cc - operations/COM_ColorCurveOperation.h - operations/COM_ColorExposureOperation.cc - operations/COM_ColorExposureOperation.h - operations/COM_ColorMatteOperation.cc - operations/COM_ColorMatteOperation.h - operations/COM_ColorRampOperation.cc - operations/COM_ColorRampOperation.h - operations/COM_CurveBaseOperation.cc - operations/COM_CurveBaseOperation.h - operations/COM_DifferenceMatteOperation.cc - operations/COM_DifferenceMatteOperation.h - operations/COM_DistanceRGBMatteOperation.cc - operations/COM_DistanceRGBMatteOperation.h - operations/COM_DistanceYCCMatteOperation.cc - operations/COM_DistanceYCCMatteOperation.h - operations/COM_HueSaturationValueCorrectOperation.cc - operations/COM_HueSaturationValueCorrectOperation.h - operations/COM_LuminanceMatteOperation.cc - operations/COM_LuminanceMatteOperation.h - operations/COM_VectorCurveOperation.cc - operations/COM_VectorCurveOperation.h - - operations/COM_BrightnessOperation.cc - operations/COM_BrightnessOperation.h - operations/COM_ColorCorrectionOperation.cc - operations/COM_ColorCorrectionOperation.h - operations/COM_ConstantOperation.cc - operations/COM_ConstantOperation.h - operations/COM_GammaOperation.cc - operations/COM_GammaOperation.h - operations/COM_MixOperation.cc - operations/COM_MixOperation.h - operations/COM_ReadBufferOperation.cc - operations/COM_ReadBufferOperation.h - operations/COM_SetColorOperation.cc - operations/COM_SetColorOperation.h - operations/COM_SetValueOperation.cc - operations/COM_SetValueOperation.h - operations/COM_SetVectorOperation.cc - operations/COM_SetVectorOperation.h - operations/COM_WriteBufferOperation.cc - operations/COM_WriteBufferOperation.h - - operations/COM_MathBaseOperation.cc - operations/COM_MathBaseOperation.h - - operations/COM_AlphaOverKeyOperation.cc - operations/COM_AlphaOverKeyOperation.h - operations/COM_AlphaOverMixedOperation.cc - operations/COM_AlphaOverMixedOperation.h - operations/COM_AlphaOverPremultiplyOperation.cc - operations/COM_AlphaOverPremultiplyOperation.h - - operations/COM_ColorBalanceASCCDLOperation.cc - operations/COM_ColorBalanceASCCDLOperation.h - operations/COM_ColorBalanceLGGOperation.cc - operations/COM_ColorBalanceLGGOperation.h - operations/COM_InvertOperation.cc - operations/COM_InvertOperation.h - operations/COM_MapRangeOperation.cc - operations/COM_MapRangeOperation.h - operations/COM_MapValueOperation.cc - operations/COM_MapValueOperation.h - operations/COM_SetAlphaMultiplyOperation.cc - operations/COM_SetAlphaMultiplyOperation.h - operations/COM_SetAlphaReplaceOperation.cc - operations/COM_SetAlphaReplaceOperation.h - - # Distort operation - operations/COM_DisplaceOperation.cc - operations/COM_DisplaceOperation.h - operations/COM_DisplaceSimpleOperation.cc - operations/COM_DisplaceSimpleOperation.h - operations/COM_FlipOperation.cc - operations/COM_FlipOperation.h - operations/COM_MapUVOperation.cc - operations/COM_MapUVOperation.h - operations/COM_PlaneCornerPinOperation.cc - operations/COM_PlaneCornerPinOperation.h - operations/COM_PlaneDistortCommonOperation.cc - operations/COM_PlaneDistortCommonOperation.h - operations/COM_PlaneTrackOperation.cc - operations/COM_PlaneTrackOperation.h - operations/COM_ProjectorLensDistortionOperation.cc - operations/COM_ProjectorLensDistortionOperation.h - operations/COM_RotateOperation.cc - operations/COM_RotateOperation.h - operations/COM_ScaleOperation.cc - operations/COM_ScaleOperation.h - operations/COM_ScreenLensDistortionOperation.cc - operations/COM_ScreenLensDistortionOperation.h - operations/COM_TransformOperation.cc - operations/COM_TransformOperation.h - operations/COM_TranslateOperation.cc - operations/COM_TranslateOperation.h - operations/COM_WrapOperation.cc - operations/COM_WrapOperation.h - - # Filter operations - operations/COM_ConvolutionEdgeFilterOperation.cc - operations/COM_ConvolutionEdgeFilterOperation.h - operations/COM_ConvolutionFilterOperation.cc - operations/COM_ConvolutionFilterOperation.h - operations/COM_DenoiseOperation.cc - operations/COM_DenoiseOperation.h - operations/COM_DespeckleOperation.cc - operations/COM_DespeckleOperation.h - operations/COM_DilateErodeOperation.cc - operations/COM_DilateErodeOperation.h - operations/COM_GlareBaseOperation.cc - operations/COM_GlareBaseOperation.h - operations/COM_GlareFogGlowOperation.cc - operations/COM_GlareFogGlowOperation.h - operations/COM_GlareGhostOperation.cc - operations/COM_GlareGhostOperation.h - operations/COM_GlareSimpleStarOperation.cc - operations/COM_GlareSimpleStarOperation.h - operations/COM_GlareStreaksOperation.cc - operations/COM_GlareStreaksOperation.h - operations/COM_GlareThresholdOperation.cc - operations/COM_GlareThresholdOperation.h - operations/COM_InpaintOperation.cc - operations/COM_InpaintOperation.h - operations/COM_SetSamplerOperation.cc - operations/COM_SetSamplerOperation.h - - - # Convert operations - operations/COM_ConvertOperation.cc - operations/COM_ConvertOperation.h - operations/COM_IDMaskOperation.cc - operations/COM_IDMaskOperation.h - - operations/COM_ConvertColorSpaceOperation.cc - operations/COM_ConvertColorSpaceOperation.h - operations/COM_DotproductOperation.cc - operations/COM_DotproductOperation.h - - # Matte operation - operations/COM_BoxMaskOperation.cc - operations/COM_BoxMaskOperation.h - operations/COM_EllipseMaskOperation.cc - operations/COM_EllipseMaskOperation.h - - operations/COM_ConvertColorProfileOperation.cc - operations/COM_ConvertColorProfileOperation.h - operations/COM_MovieClipOperation.cc - operations/COM_MovieClipOperation.h - - operations/COM_AntiAliasOperation.cc - operations/COM_AntiAliasOperation.h - - operations/COM_MaskOperation.cc - operations/COM_MaskOperation.h -) - -set(LIB - bf_blenkernel - bf_blenlib - extern_clew -) - -list(APPEND INC - ${CMAKE_CURRENT_BINARY_DIR}/operations -) - -data_to_c( - ${CMAKE_CURRENT_SOURCE_DIR}/operations/COM_OpenCLKernels.cl - ${CMAKE_CURRENT_BINARY_DIR}/operations/COM_OpenCLKernels.cl.h - SRC -) - -add_definitions(-DCL_USE_DEPRECATED_OPENCL_1_1_APIS) - -set(GENSRC_DIR ${CMAKE_CURRENT_BINARY_DIR}/operations) -set(GENSRC ${GENSRC_DIR}/COM_SMAAAreaTexture.h) -add_custom_command( - OUTPUT ${GENSRC} - COMMAND ${CMAKE_COMMAND} -E make_directory ${GENSRC_DIR} - COMMAND "$<TARGET_FILE:smaa_areatex>" ${GENSRC} - DEPENDS smaa_areatex -) -add_custom_target(smaa_areatex_header - SOURCES ${GENSRC} -) -list(APPEND SRC - ${GENSRC} -) -unset(GENSRC) -unset(GENSRC_DIR) - -if(WITH_OPENIMAGEDENOISE) - add_definitions(-DWITH_OPENIMAGEDENOISE) - add_definitions(-DOIDN_STATIC_LIB) - list(APPEND INC_SYS - ${OPENIMAGEDENOISE_INCLUDE_DIRS} - ${TBB_INCLUDE_DIRS} +add_subdirectory(realtime_compositor) + +if(WITH_COMPOSITOR_CPU) + set(INC + . + intern + nodes + operations + ../blenkernel + ../blenlib + ../blentranslation + ../depsgraph + ../imbuf + ../makesdna + ../makesrna + ../nodes + ../windowmanager + ../nodes/composite + ../nodes/intern + ../render + ../render/intern + ../../../extern/clew/include + ../../../intern/atomic + ../../../intern/clog + ../../../intern/guardedalloc + + # dna_type_offsets.h + ${CMAKE_CURRENT_BINARY_DIR}/../makesdna/intern + # RNA_prototypes.h + ${CMAKE_BINARY_DIR}/source/blender/makesrna ) - list(APPEND LIB - ${OPENIMAGEDENOISE_LIBRARIES} - ${TBB_LIBRARIES} + + set(INC_SYS + ) -endif() -blender_add_lib(bf_compositor "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") + set(SRC + COM_compositor.h + COM_defines.h + + intern/COM_BufferArea.h + intern/COM_BufferOperation.cc + intern/COM_BufferOperation.h + intern/COM_BufferRange.h + intern/COM_BuffersIterator.h + intern/COM_CPUDevice.cc + intern/COM_CPUDevice.h + intern/COM_ChunkOrder.cc + intern/COM_ChunkOrder.h + intern/COM_ChunkOrderHotspot.cc + intern/COM_ChunkOrderHotspot.h + intern/COM_CompositorContext.cc + intern/COM_CompositorContext.h + intern/COM_ConstantFolder.cc + intern/COM_ConstantFolder.h + intern/COM_Converter.cc + intern/COM_Converter.h + intern/COM_Debug.cc + intern/COM_Debug.h + intern/COM_Device.cc + intern/COM_Device.h + intern/COM_Enums.cc + intern/COM_Enums.h + intern/COM_ExecutionGroup.cc + intern/COM_ExecutionGroup.h + intern/COM_ExecutionModel.cc + intern/COM_ExecutionModel.h + intern/COM_ExecutionSystem.cc + intern/COM_ExecutionSystem.h + intern/COM_FullFrameExecutionModel.cc + intern/COM_FullFrameExecutionModel.h + intern/COM_MemoryBuffer.cc + intern/COM_MemoryBuffer.h + intern/COM_MemoryProxy.cc + intern/COM_MemoryProxy.h + intern/COM_MetaData.cc + intern/COM_MetaData.h + intern/COM_MultiThreadedOperation.cc + intern/COM_MultiThreadedOperation.h + intern/COM_MultiThreadedRowOperation.cc + intern/COM_MultiThreadedRowOperation.h + intern/COM_Node.cc + intern/COM_Node.h + intern/COM_NodeConverter.cc + intern/COM_NodeConverter.h + intern/COM_NodeGraph.cc + intern/COM_NodeGraph.h + intern/COM_NodeOperation.cc + intern/COM_NodeOperation.h + intern/COM_NodeOperationBuilder.cc + intern/COM_NodeOperationBuilder.h + intern/COM_OpenCLDevice.cc + intern/COM_OpenCLDevice.h + intern/COM_SharedOperationBuffers.cc + intern/COM_SharedOperationBuffers.h + intern/COM_SingleThreadedOperation.cc + intern/COM_SingleThreadedOperation.h + intern/COM_TiledExecutionModel.cc + intern/COM_TiledExecutionModel.h + intern/COM_WorkPackage.cc + intern/COM_WorkPackage.h + intern/COM_WorkScheduler.cc + intern/COM_WorkScheduler.h + intern/COM_compositor.cc + + operations/COM_QualityStepHelper.cc + operations/COM_QualityStepHelper.h + + # Internal nodes + nodes/COM_SocketProxyNode.cc + nodes/COM_SocketProxyNode.h + + # input nodes + nodes/COM_BokehImageNode.cc + nodes/COM_BokehImageNode.h + nodes/COM_ColorNode.cc + nodes/COM_ColorNode.h + nodes/COM_ImageNode.cc + nodes/COM_ImageNode.h + nodes/COM_MaskNode.cc + nodes/COM_MaskNode.h + nodes/COM_MovieClipNode.cc + nodes/COM_MovieClipNode.h + nodes/COM_OutputFileNode.cc + nodes/COM_OutputFileNode.h + nodes/COM_RenderLayersNode.cc + nodes/COM_RenderLayersNode.h + nodes/COM_SceneTimeNode.cc + nodes/COM_SceneTimeNode.h + nodes/COM_SwitchNode.cc + nodes/COM_SwitchNode.h + nodes/COM_SwitchViewNode.cc + nodes/COM_SwitchViewNode.h + nodes/COM_TextureNode.cc + nodes/COM_TextureNode.h + nodes/COM_TimeNode.cc + nodes/COM_TimeNode.h + nodes/COM_ValueNode.cc + nodes/COM_ValueNode.h + + # output nodes + nodes/COM_CompositorNode.cc + nodes/COM_CompositorNode.h + nodes/COM_SplitViewerNode.cc + nodes/COM_SplitViewerNode.h + nodes/COM_ViewLevelsNode.cc + nodes/COM_ViewLevelsNode.h + nodes/COM_ViewerNode.cc + nodes/COM_ViewerNode.h + operations/COM_CalculateMeanOperation.cc + operations/COM_CalculateMeanOperation.h + operations/COM_CalculateStandardDeviationOperation.cc + operations/COM_CalculateStandardDeviationOperation.h + + # distort nodes + nodes/COM_FlipNode.cc + nodes/COM_FlipNode.h + nodes/COM_RotateNode.cc + nodes/COM_RotateNode.h + nodes/COM_ScaleNode.cc + nodes/COM_ScaleNode.h + nodes/COM_TranslateNode.cc + nodes/COM_TranslateNode.h + + nodes/COM_DisplaceNode.cc + nodes/COM_DisplaceNode.h + nodes/COM_MapUVNode.cc + nodes/COM_MapUVNode.h + + nodes/COM_ChannelMatteNode.cc + nodes/COM_ChannelMatteNode.h + nodes/COM_ChromaMatteNode.cc + nodes/COM_ChromaMatteNode.h + nodes/COM_ColorMatteNode.cc + nodes/COM_ColorMatteNode.h + nodes/COM_DifferenceMatteNode.cc + nodes/COM_DifferenceMatteNode.h + nodes/COM_DistanceMatteNode.cc + nodes/COM_DistanceMatteNode.h + nodes/COM_LensDistortionNode.cc + nodes/COM_LensDistortionNode.h + nodes/COM_LuminanceMatteNode.cc + nodes/COM_LuminanceMatteNode.h + + nodes/COM_GlareNode.cc + nodes/COM_GlareNode.h + + nodes/COM_SunBeamsNode.cc + nodes/COM_SunBeamsNode.h + operations/COM_SunBeamsOperation.cc + operations/COM_SunBeamsOperation.h + + nodes/COM_CryptomatteNode.cc + nodes/COM_CryptomatteNode.h + operations/COM_CryptomatteOperation.cc + operations/COM_CryptomatteOperation.h + + nodes/COM_CornerPinNode.cc + nodes/COM_CornerPinNode.h + nodes/COM_PlaneTrackDeformNode.cc + nodes/COM_PlaneTrackDeformNode.h + + nodes/COM_CropNode.cc + nodes/COM_CropNode.h + operations/COM_CropOperation.cc + operations/COM_CropOperation.h + + nodes/COM_DefocusNode.cc + nodes/COM_DefocusNode.h + nodes/COM_MovieDistortionNode.cc + nodes/COM_MovieDistortionNode.h + nodes/COM_Stabilize2dNode.cc + nodes/COM_Stabilize2dNode.h + nodes/COM_TransformNode.cc + nodes/COM_TransformNode.h + + # color nodes + nodes/COM_AlphaOverNode.cc + nodes/COM_AlphaOverNode.h + nodes/COM_BrightnessNode.cc + nodes/COM_BrightnessNode.h + nodes/COM_ColorBalanceNode.cc + nodes/COM_ColorBalanceNode.h + nodes/COM_ColorCorrectionNode.cc + nodes/COM_ColorCorrectionNode.h + nodes/COM_ColorCurveNode.cc + nodes/COM_ColorCurveNode.h + nodes/COM_ColorExposureNode.cc + nodes/COM_ColorExposureNode.h + nodes/COM_ColorRampNode.cc + nodes/COM_ColorRampNode.h + nodes/COM_ColorToBWNode.cc + nodes/COM_ColorToBWNode.h + nodes/COM_ConvertAlphaNode.cc + nodes/COM_ConvertAlphaNode.h + nodes/COM_ConvertColorSpaceNode.cc + nodes/COM_ConvertColorSpaceNode.h + nodes/COM_GammaNode.cc + nodes/COM_GammaNode.h + nodes/COM_HueSaturationValueCorrectNode.cc + nodes/COM_HueSaturationValueCorrectNode.h + nodes/COM_HueSaturationValueNode.cc + nodes/COM_HueSaturationValueNode.h + nodes/COM_InvertNode.cc + nodes/COM_InvertNode.h + nodes/COM_MixNode.cc + nodes/COM_MixNode.h + nodes/COM_SetAlphaNode.cc + nodes/COM_SetAlphaNode.h + nodes/COM_TonemapNode.cc + nodes/COM_TonemapNode.h + nodes/COM_VectorCurveNode.cc + nodes/COM_VectorCurveNode.h + nodes/COM_ZCombineNode.cc + nodes/COM_ZCombineNode.h + operations/COM_TonemapOperation.cc + operations/COM_TonemapOperation.h + + # converter nodes + nodes/COM_CombineColorNode.cc + nodes/COM_CombineColorNode.h + nodes/COM_CombineColorNodeLegacy.cc + nodes/COM_CombineColorNodeLegacy.h + nodes/COM_CombineXYZNode.cc + nodes/COM_CombineXYZNode.h + nodes/COM_IDMaskNode.cc + nodes/COM_IDMaskNode.h + nodes/COM_SeparateColorNode.cc + nodes/COM_SeparateColorNode.h + nodes/COM_SeparateColorNodeLegacy.cc + nodes/COM_SeparateColorNodeLegacy.h + nodes/COM_SeparateXYZNode.cc + nodes/COM_SeparateXYZNode.h + + nodes/COM_MapRangeNode.cc + nodes/COM_MapRangeNode.h + nodes/COM_MapValueNode.cc + nodes/COM_MapValueNode.h + nodes/COM_MathNode.cc + nodes/COM_MathNode.h + nodes/COM_NormalNode.cc + nodes/COM_NormalNode.h + nodes/COM_NormalizeNode.cc + nodes/COM_NormalizeNode.h + + operations/COM_NormalizeOperation.cc + operations/COM_NormalizeOperation.h + + nodes/COM_PixelateNode.cc + nodes/COM_PixelateNode.h + operations/COM_PixelateOperation.cc + operations/COM_PixelateOperation.h + + # Filter nodes + nodes/COM_BilateralBlurNode.cc + nodes/COM_BilateralBlurNode.h + operations/COM_BilateralBlurOperation.cc + operations/COM_BilateralBlurOperation.h + nodes/COM_VectorBlurNode.cc + nodes/COM_VectorBlurNode.h + operations/COM_VectorBlurOperation.cc + operations/COM_VectorBlurOperation.h + nodes/COM_AntiAliasingNode.cc + nodes/COM_AntiAliasingNode.h + nodes/COM_BlurNode.cc + nodes/COM_BlurNode.h + nodes/COM_BokehBlurNode.cc + nodes/COM_BokehBlurNode.h + nodes/COM_DenoiseNode.cc + nodes/COM_DenoiseNode.h + nodes/COM_DespeckleNode.cc + nodes/COM_DespeckleNode.h + nodes/COM_DilateErodeNode.cc + nodes/COM_DilateErodeNode.h + nodes/COM_DirectionalBlurNode.cc + nodes/COM_DirectionalBlurNode.h + nodes/COM_FilterNode.cc + nodes/COM_FilterNode.h + nodes/COM_InpaintNode.cc + nodes/COM_InpaintNode.h + nodes/COM_PosterizeNode.cc + nodes/COM_PosterizeNode.h + + operations/COM_BlurBaseOperation.cc + operations/COM_BlurBaseOperation.h + operations/COM_BokehBlurOperation.cc + operations/COM_BokehBlurOperation.h + operations/COM_DirectionalBlurOperation.cc + operations/COM_DirectionalBlurOperation.h + operations/COM_FastGaussianBlurOperation.cc + operations/COM_FastGaussianBlurOperation.h + operations/COM_GammaCorrectOperation.cc + operations/COM_GammaCorrectOperation.h + operations/COM_GaussianAlphaBlurBaseOperation.cc + operations/COM_GaussianAlphaBlurBaseOperation.h + operations/COM_GaussianAlphaXBlurOperation.cc + operations/COM_GaussianAlphaXBlurOperation.h + operations/COM_GaussianAlphaYBlurOperation.cc + operations/COM_GaussianAlphaYBlurOperation.h + operations/COM_GaussianBlurBaseOperation.cc + operations/COM_GaussianBlurBaseOperation.h + operations/COM_GaussianBokehBlurOperation.cc + operations/COM_GaussianBokehBlurOperation.h + operations/COM_GaussianXBlurOperation.cc + operations/COM_GaussianXBlurOperation.h + operations/COM_GaussianYBlurOperation.cc + operations/COM_GaussianYBlurOperation.h + operations/COM_MovieClipAttributeOperation.cc + operations/COM_MovieClipAttributeOperation.h + operations/COM_MovieDistortionOperation.cc + operations/COM_MovieDistortionOperation.h + operations/COM_PosterizeOperation.cc + operations/COM_PosterizeOperation.h + operations/COM_SMAAOperation.cc + operations/COM_SMAAOperation.h + operations/COM_VariableSizeBokehBlurOperation.cc + operations/COM_VariableSizeBokehBlurOperation.h + + # Matte nodes + nodes/COM_BoxMaskNode.cc + nodes/COM_BoxMaskNode.h + nodes/COM_ColorSpillNode.cc + nodes/COM_ColorSpillNode.h + nodes/COM_DoubleEdgeMaskNode.cc + nodes/COM_DoubleEdgeMaskNode.h + nodes/COM_EllipseMaskNode.cc + nodes/COM_EllipseMaskNode.h + + operations/COM_DoubleEdgeMaskOperation.cc + operations/COM_DoubleEdgeMaskOperation.h + + + nodes/COM_KeyingScreenNode.cc + nodes/COM_KeyingScreenNode.h + operations/COM_KeyingScreenOperation.cc + operations/COM_KeyingScreenOperation.h + + nodes/COM_TrackPositionNode.cc + nodes/COM_TrackPositionNode.h + operations/COM_TrackPositionOperation.cc + operations/COM_TrackPositionOperation.h + + nodes/COM_KeyingNode.cc + nodes/COM_KeyingNode.h + operations/COM_KeyingBlurOperation.cc + operations/COM_KeyingBlurOperation.h + operations/COM_KeyingClipOperation.cc + operations/COM_KeyingClipOperation.h + operations/COM_KeyingDespillOperation.cc + operations/COM_KeyingDespillOperation.h + operations/COM_KeyingOperation.cc + operations/COM_KeyingOperation.h + + operations/COM_ColorSpillOperation.cc + operations/COM_ColorSpillOperation.h + operations/COM_RenderLayersProg.cc + operations/COM_RenderLayersProg.h + + operations/COM_BokehImageOperation.cc + operations/COM_BokehImageOperation.h + operations/COM_ImageOperation.cc + operations/COM_ImageOperation.h + operations/COM_MultilayerImageOperation.cc + operations/COM_MultilayerImageOperation.h + operations/COM_TextureOperation.cc + operations/COM_TextureOperation.h + + + operations/COM_SocketProxyOperation.cc + operations/COM_SocketProxyOperation.h + + operations/COM_CompositorOperation.cc + operations/COM_CompositorOperation.h + operations/COM_ConvertDepthToRadiusOperation.cc + operations/COM_ConvertDepthToRadiusOperation.h + operations/COM_OutputFileMultiViewOperation.cc + operations/COM_OutputFileMultiViewOperation.h + operations/COM_OutputFileOperation.cc + operations/COM_OutputFileOperation.h + operations/COM_PreviewOperation.cc + operations/COM_PreviewOperation.h + operations/COM_SplitOperation.cc + operations/COM_SplitOperation.h + operations/COM_ViewerOperation.cc + operations/COM_ViewerOperation.h + operations/COM_ZCombineOperation.cc + operations/COM_ZCombineOperation.h + + operations/COM_ChangeHSVOperation.cc + operations/COM_ChangeHSVOperation.h + operations/COM_ChannelMatteOperation.cc + operations/COM_ChannelMatteOperation.h + operations/COM_ChromaMatteOperation.cc + operations/COM_ChromaMatteOperation.h + operations/COM_ColorCurveOperation.cc + operations/COM_ColorCurveOperation.h + operations/COM_ColorExposureOperation.cc + operations/COM_ColorExposureOperation.h + operations/COM_ColorMatteOperation.cc + operations/COM_ColorMatteOperation.h + operations/COM_ColorRampOperation.cc + operations/COM_ColorRampOperation.h + operations/COM_CurveBaseOperation.cc + operations/COM_CurveBaseOperation.h + operations/COM_DifferenceMatteOperation.cc + operations/COM_DifferenceMatteOperation.h + operations/COM_DistanceRGBMatteOperation.cc + operations/COM_DistanceRGBMatteOperation.h + operations/COM_DistanceYCCMatteOperation.cc + operations/COM_DistanceYCCMatteOperation.h + operations/COM_HueSaturationValueCorrectOperation.cc + operations/COM_HueSaturationValueCorrectOperation.h + operations/COM_LuminanceMatteOperation.cc + operations/COM_LuminanceMatteOperation.h + operations/COM_VectorCurveOperation.cc + operations/COM_VectorCurveOperation.h + + operations/COM_BrightnessOperation.cc + operations/COM_BrightnessOperation.h + operations/COM_ColorCorrectionOperation.cc + operations/COM_ColorCorrectionOperation.h + operations/COM_ConstantOperation.cc + operations/COM_ConstantOperation.h + operations/COM_GammaOperation.cc + operations/COM_GammaOperation.h + operations/COM_MixOperation.cc + operations/COM_MixOperation.h + operations/COM_ReadBufferOperation.cc + operations/COM_ReadBufferOperation.h + operations/COM_SetColorOperation.cc + operations/COM_SetColorOperation.h + operations/COM_SetValueOperation.cc + operations/COM_SetValueOperation.h + operations/COM_SetVectorOperation.cc + operations/COM_SetVectorOperation.h + operations/COM_WriteBufferOperation.cc + operations/COM_WriteBufferOperation.h + + operations/COM_MathBaseOperation.cc + operations/COM_MathBaseOperation.h + + operations/COM_AlphaOverKeyOperation.cc + operations/COM_AlphaOverKeyOperation.h + operations/COM_AlphaOverMixedOperation.cc + operations/COM_AlphaOverMixedOperation.h + operations/COM_AlphaOverPremultiplyOperation.cc + operations/COM_AlphaOverPremultiplyOperation.h + + operations/COM_ColorBalanceASCCDLOperation.cc + operations/COM_ColorBalanceASCCDLOperation.h + operations/COM_ColorBalanceLGGOperation.cc + operations/COM_ColorBalanceLGGOperation.h + operations/COM_InvertOperation.cc + operations/COM_InvertOperation.h + operations/COM_MapRangeOperation.cc + operations/COM_MapRangeOperation.h + operations/COM_MapValueOperation.cc + operations/COM_MapValueOperation.h + operations/COM_SetAlphaMultiplyOperation.cc + operations/COM_SetAlphaMultiplyOperation.h + operations/COM_SetAlphaReplaceOperation.cc + operations/COM_SetAlphaReplaceOperation.h + + # Distort operation + operations/COM_DisplaceOperation.cc + operations/COM_DisplaceOperation.h + operations/COM_DisplaceSimpleOperation.cc + operations/COM_DisplaceSimpleOperation.h + operations/COM_FlipOperation.cc + operations/COM_FlipOperation.h + operations/COM_MapUVOperation.cc + operations/COM_MapUVOperation.h + operations/COM_PlaneCornerPinOperation.cc + operations/COM_PlaneCornerPinOperation.h + operations/COM_PlaneDistortCommonOperation.cc + operations/COM_PlaneDistortCommonOperation.h + operations/COM_PlaneTrackOperation.cc + operations/COM_PlaneTrackOperation.h + operations/COM_ProjectorLensDistortionOperation.cc + operations/COM_ProjectorLensDistortionOperation.h + operations/COM_RotateOperation.cc + operations/COM_RotateOperation.h + operations/COM_ScaleOperation.cc + operations/COM_ScaleOperation.h + operations/COM_ScreenLensDistortionOperation.cc + operations/COM_ScreenLensDistortionOperation.h + operations/COM_TransformOperation.cc + operations/COM_TransformOperation.h + operations/COM_TranslateOperation.cc + operations/COM_TranslateOperation.h + operations/COM_WrapOperation.cc + operations/COM_WrapOperation.h + + # Filter operations + operations/COM_ConvolutionEdgeFilterOperation.cc + operations/COM_ConvolutionEdgeFilterOperation.h + operations/COM_ConvolutionFilterOperation.cc + operations/COM_ConvolutionFilterOperation.h + operations/COM_DenoiseOperation.cc + operations/COM_DenoiseOperation.h + operations/COM_DespeckleOperation.cc + operations/COM_DespeckleOperation.h + operations/COM_DilateErodeOperation.cc + operations/COM_DilateErodeOperation.h + operations/COM_GlareBaseOperation.cc + operations/COM_GlareBaseOperation.h + operations/COM_GlareFogGlowOperation.cc + operations/COM_GlareFogGlowOperation.h + operations/COM_GlareGhostOperation.cc + operations/COM_GlareGhostOperation.h + operations/COM_GlareSimpleStarOperation.cc + operations/COM_GlareSimpleStarOperation.h + operations/COM_GlareStreaksOperation.cc + operations/COM_GlareStreaksOperation.h + operations/COM_GlareThresholdOperation.cc + operations/COM_GlareThresholdOperation.h + operations/COM_InpaintOperation.cc + operations/COM_InpaintOperation.h + operations/COM_SetSamplerOperation.cc + operations/COM_SetSamplerOperation.h + + + # Convert operations + operations/COM_ConvertOperation.cc + operations/COM_ConvertOperation.h + operations/COM_IDMaskOperation.cc + operations/COM_IDMaskOperation.h + + operations/COM_ConvertColorSpaceOperation.cc + operations/COM_ConvertColorSpaceOperation.h + operations/COM_DotproductOperation.cc + operations/COM_DotproductOperation.h + + # Matte operation + operations/COM_BoxMaskOperation.cc + operations/COM_BoxMaskOperation.h + operations/COM_EllipseMaskOperation.cc + operations/COM_EllipseMaskOperation.h + + operations/COM_ConvertColorProfileOperation.cc + operations/COM_ConvertColorProfileOperation.h + operations/COM_MovieClipOperation.cc + operations/COM_MovieClipOperation.h + + operations/COM_AntiAliasOperation.cc + operations/COM_AntiAliasOperation.h + + operations/COM_MaskOperation.cc + operations/COM_MaskOperation.h + ) -if(WITH_UNITY_BUILD) - set_target_properties(bf_compositor PROPERTIES UNITY_BUILD ON) - set_target_properties(bf_compositor PROPERTIES UNITY_BUILD_BATCH_SIZE 10) -endif() + set(LIB + bf_blenkernel + bf_blenlib + extern_clew + ) -if(COMMAND target_precompile_headers) - target_precompile_headers(bf_compositor PRIVATE COM_precomp.h) -endif() + list(APPEND INC + ${CMAKE_CURRENT_BINARY_DIR}/operations + ) -if(CXX_WARN_NO_SUGGEST_OVERRIDE) - target_compile_options(bf_compositor PRIVATE "-Wsuggest-override") -endif() + data_to_c( + ${CMAKE_CURRENT_SOURCE_DIR}/operations/COM_OpenCLKernels.cl + ${CMAKE_CURRENT_BINARY_DIR}/operations/COM_OpenCLKernels.cl.h + SRC + ) -add_dependencies(bf_compositor smaa_areatex_header) + add_definitions(-DCL_USE_DEPRECATED_OPENCL_1_1_APIS) -if(WITH_GTESTS) - set(TEST_SRC - tests/COM_BufferArea_test.cc - tests/COM_BufferRange_test.cc - tests/COM_BuffersIterator_test.cc - tests/COM_NodeOperation_test.cc + set(GENSRC_DIR ${CMAKE_CURRENT_BINARY_DIR}/operations) + set(GENSRC ${GENSRC_DIR}/COM_SMAAAreaTexture.h) + add_custom_command( + OUTPUT ${GENSRC} + COMMAND ${CMAKE_COMMAND} -E make_directory ${GENSRC_DIR} + COMMAND "$<TARGET_FILE:smaa_areatex>" ${GENSRC} + DEPENDS smaa_areatex ) - set(TEST_INC + add_custom_target(smaa_areatex_header + SOURCES ${GENSRC} ) - set(TEST_LIB - bf_compositor + list(APPEND SRC + ${GENSRC} ) - include(GTestTesting) - blender_add_test_lib(bf_compositor_tests "${TEST_SRC}" "${INC};${TEST_INC}" "${INC_SYS}" "${LIB};${TEST_LIB}") -endif() + unset(GENSRC) + unset(GENSRC_DIR) + + if(WITH_OPENIMAGEDENOISE) + add_definitions(-DWITH_OPENIMAGEDENOISE) + add_definitions(-DOIDN_STATIC_LIB) + list(APPEND INC_SYS + ${OPENIMAGEDENOISE_INCLUDE_DIRS} + ${TBB_INCLUDE_DIRS} + ) + list(APPEND LIB + ${OPENIMAGEDENOISE_LIBRARIES} + ${TBB_LIBRARIES} + ) + endif() + + blender_add_lib(bf_compositor "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") + + if(WITH_UNITY_BUILD) + set_target_properties(bf_compositor PROPERTIES UNITY_BUILD ON) + set_target_properties(bf_compositor PROPERTIES UNITY_BUILD_BATCH_SIZE 10) + endif() + + if(COMMAND target_precompile_headers) + target_precompile_headers(bf_compositor PRIVATE COM_precomp.h) + endif() + + if(CXX_WARN_NO_SUGGEST_OVERRIDE) + target_compile_options(bf_compositor PRIVATE "-Wsuggest-override") + endif() + + add_dependencies(bf_compositor smaa_areatex_header) + + if(WITH_GTESTS) + set(TEST_SRC + tests/COM_BufferArea_test.cc + tests/COM_BufferRange_test.cc + tests/COM_BuffersIterator_test.cc + tests/COM_NodeOperation_test.cc + ) + set(TEST_INC + ) + set(TEST_LIB + bf_compositor + ) + include(GTestTesting) + blender_add_test_lib(bf_compositor_tests "${TEST_SRC}" "${INC};${TEST_INC}" "${INC_SYS}" "${LIB};${TEST_LIB}") + endif() + + # Needed so we can use dna_type_offsets.h for defaults initialization. + add_dependencies(bf_compositor bf_dna) + # RNA_prototypes.h + add_dependencies(bf_compositor bf_rna) -# Needed so we can use dna_type_offsets.h for defaults initialization. -add_dependencies(bf_compositor bf_dna) -# RNA_prototypes.h -add_dependencies(bf_compositor bf_rna) +# End WITH_COMPOSITOR_CPU. +endif() diff --git a/source/blender/compositor/COM_compositor.h b/source/blender/compositor/COM_compositor.h index 0fdd7647f8d..fd53460f854 100644 --- a/source/blender/compositor/COM_compositor.h +++ b/source/blender/compositor/COM_compositor.h @@ -12,8 +12,8 @@ extern "C" { /* Keep ascii art. */ /* clang-format off */ + /** - * * \defgroup Model The data model of the compositor * \ingroup compositor * \defgroup Memory The memory management stuff diff --git a/source/blender/compositor/intern/COM_Converter.h b/source/blender/compositor/intern/COM_Converter.h index e0164b9162d..eba61e07e1a 100644 --- a/source/blender/compositor/intern/COM_Converter.h +++ b/source/blender/compositor/intern/COM_Converter.h @@ -37,8 +37,8 @@ Node *COM_convert_bnode(bNode *b_node); bool COM_bnode_is_fast_node(const bNode &b_node); /** - * \brief This function will add a datetype conversion rule when the to-socket does not support the - * from-socket actual data type. + * \brief This function will add a date-type conversion rule when the to-socket does not support + * the from-socket actual data type. */ NodeOperation *COM_convert_data_type(const NodeOperationOutput &from, const NodeOperationInput &to); diff --git a/source/blender/compositor/intern/COM_Node.h b/source/blender/compositor/intern/COM_Node.h index ae910c28342..364f9439366 100644 --- a/source/blender/compositor/intern/COM_Node.h +++ b/source/blender/compositor/intern/COM_Node.h @@ -31,7 +31,7 @@ class Node { /** * \brief stores the reference to the SDNA bNode struct */ - bNode *editor_node_; + const bNode *editor_node_; /** * \brief Is this node part of the active group @@ -61,7 +61,7 @@ class Node { /** * \brief get the reference to the SDNA bNode struct */ - bNode *get_bnode() const + const bNode *get_bnode() const { return editor_node_; } diff --git a/source/blender/compositor/intern/COM_NodeConverter.h b/source/blender/compositor/intern/COM_NodeConverter.h index a90531bad0e..ceaf04f11a0 100644 --- a/source/blender/compositor/intern/COM_NodeConverter.h +++ b/source/blender/compositor/intern/COM_NodeConverter.h @@ -90,7 +90,7 @@ class NodeConverter { /** * When a node has no valid data - * \note missing image / group pointer, or missing renderlayer from EXR + * \note missing image / group pointer, or missing render-layer from EXR. */ NodeOperation *set_invalid_output(NodeOutput *output); diff --git a/source/blender/compositor/nodes/COM_AlphaOverNode.cc b/source/blender/compositor/nodes/COM_AlphaOverNode.cc index 5b0f7a1d0e6..8f9f6f07395 100644 --- a/source/blender/compositor/nodes/COM_AlphaOverNode.cc +++ b/source/blender/compositor/nodes/COM_AlphaOverNode.cc @@ -14,10 +14,10 @@ void AlphaOverNode::convert_to_operations(NodeConverter &converter, { NodeInput *color1Socket = this->get_input_socket(1); NodeInput *color2Socket = this->get_input_socket(2); - bNode *editor_node = this->get_bnode(); + const bNode *editor_node = this->get_bnode(); MixBaseOperation *convert_prog; - NodeTwoFloats *ntf = (NodeTwoFloats *)editor_node->storage; + const NodeTwoFloats *ntf = (const NodeTwoFloats *)editor_node->storage; if (ntf->x != 0.0f) { AlphaOverMixedOperation *mix_operation = new AlphaOverMixedOperation(); mix_operation->setX(ntf->x); diff --git a/source/blender/compositor/nodes/COM_AntiAliasingNode.cc b/source/blender/compositor/nodes/COM_AntiAliasingNode.cc index e201720c53b..eb71b70d0bb 100644 --- a/source/blender/compositor/nodes/COM_AntiAliasingNode.cc +++ b/source/blender/compositor/nodes/COM_AntiAliasingNode.cc @@ -9,8 +9,8 @@ namespace blender::compositor { void AntiAliasingNode::convert_to_operations(NodeConverter &converter, const CompositorContext & /*context*/) const { - bNode *node = this->get_bnode(); - NodeAntiAliasingData *data = (NodeAntiAliasingData *)node->storage; + const bNode *node = this->get_bnode(); + const NodeAntiAliasingData *data = (const NodeAntiAliasingData *)node->storage; /* Edge Detection (First Pass) */ SMAAEdgeDetectionOperation *operation1 = nullptr; diff --git a/source/blender/compositor/nodes/COM_BlurNode.cc b/source/blender/compositor/nodes/COM_BlurNode.cc index a8148d7abd7..9377cfa783c 100644 --- a/source/blender/compositor/nodes/COM_BlurNode.cc +++ b/source/blender/compositor/nodes/COM_BlurNode.cc @@ -22,8 +22,8 @@ BlurNode::BlurNode(bNode *editor_node) : Node(editor_node) void BlurNode::convert_to_operations(NodeConverter &converter, const CompositorContext &context) const { - bNode *editor_node = this->get_bnode(); - NodeBlurData *data = (NodeBlurData *)editor_node->storage; + const bNode *editor_node = this->get_bnode(); + const NodeBlurData *data = (const NodeBlurData *)editor_node->storage; NodeInput *input_size_socket = this->get_input_socket(1); bool connected_size_socket = input_size_socket->is_linked(); diff --git a/source/blender/compositor/nodes/COM_BokehBlurNode.cc b/source/blender/compositor/nodes/COM_BokehBlurNode.cc index 31258ddb56a..ebdc82b0d19 100644 --- a/source/blender/compositor/nodes/COM_BokehBlurNode.cc +++ b/source/blender/compositor/nodes/COM_BokehBlurNode.cc @@ -15,7 +15,7 @@ BokehBlurNode::BokehBlurNode(bNode *editor_node) : Node(editor_node) void BokehBlurNode::convert_to_operations(NodeConverter &converter, const CompositorContext &context) const { - bNode *b_node = this->get_bnode(); + const bNode *b_node = this->get_bnode(); NodeInput *input_size_socket = this->get_input_socket(2); diff --git a/source/blender/compositor/nodes/COM_BokehImageNode.cc b/source/blender/compositor/nodes/COM_BokehImageNode.cc index 6bc56aa5184..f25ac47fef9 100644 --- a/source/blender/compositor/nodes/COM_BokehImageNode.cc +++ b/source/blender/compositor/nodes/COM_BokehImageNode.cc @@ -15,7 +15,7 @@ void BokehImageNode::convert_to_operations(NodeConverter &converter, const CompositorContext & /*context*/) const { BokehImageOperation *operation = new BokehImageOperation(); - operation->set_data((NodeBokehImage *)this->get_bnode()->storage); + operation->set_data((const NodeBokehImage *)this->get_bnode()->storage); converter.add_operation(operation); converter.map_output_socket(get_output_socket(0), operation->get_output_socket(0)); diff --git a/source/blender/compositor/nodes/COM_BoxMaskNode.cc b/source/blender/compositor/nodes/COM_BoxMaskNode.cc index 41717804dba..c1a1b72d063 100644 --- a/source/blender/compositor/nodes/COM_BoxMaskNode.cc +++ b/source/blender/compositor/nodes/COM_BoxMaskNode.cc @@ -22,7 +22,7 @@ void BoxMaskNode::convert_to_operations(NodeConverter &converter, BoxMaskOperation *operation; operation = new BoxMaskOperation(); - operation->set_data((NodeBoxMask *)this->get_bnode()->storage); + operation->set_data((const NodeBoxMask *)this->get_bnode()->storage); operation->set_mask_type(this->get_bnode()->custom1); converter.add_operation(operation); diff --git a/source/blender/compositor/nodes/COM_BrightnessNode.cc b/source/blender/compositor/nodes/COM_BrightnessNode.cc index 88efd541fe9..a7877ca9378 100644 --- a/source/blender/compositor/nodes/COM_BrightnessNode.cc +++ b/source/blender/compositor/nodes/COM_BrightnessNode.cc @@ -14,7 +14,7 @@ BrightnessNode::BrightnessNode(bNode *editor_node) : Node(editor_node) void BrightnessNode::convert_to_operations(NodeConverter &converter, const CompositorContext & /*context*/) const { - bNode *bnode = this->get_bnode(); + const bNode *bnode = this->get_bnode(); BrightnessOperation *operation = new BrightnessOperation(); operation->set_use_premultiply((bnode->custom1 & 1) != 0); converter.add_operation(operation); diff --git a/source/blender/compositor/nodes/COM_ChannelMatteNode.cc b/source/blender/compositor/nodes/COM_ChannelMatteNode.cc index 81fc638970f..6a0a28cd171 100644 --- a/source/blender/compositor/nodes/COM_ChannelMatteNode.cc +++ b/source/blender/compositor/nodes/COM_ChannelMatteNode.cc @@ -16,14 +16,14 @@ ChannelMatteNode::ChannelMatteNode(bNode *editor_node) : Node(editor_node) void ChannelMatteNode::convert_to_operations(NodeConverter &converter, const CompositorContext & /*context*/) const { - bNode *node = this->get_bnode(); + const bNode *node = this->get_bnode(); NodeInput *input_socket_image = this->get_input_socket(0); NodeOutput *output_socket_image = this->get_output_socket(0); NodeOutput *output_socket_matte = this->get_output_socket(1); NodeOperation *convert = nullptr, *inv_convert = nullptr; - /* colorspace */ + /* color-space */ switch (node->custom1) { case CMP_NODE_CHANNEL_MATTE_CS_RGB: break; diff --git a/source/blender/compositor/nodes/COM_ChromaMatteNode.cc b/source/blender/compositor/nodes/COM_ChromaMatteNode.cc index cbc5a06ce11..35bf6210da5 100644 --- a/source/blender/compositor/nodes/COM_ChromaMatteNode.cc +++ b/source/blender/compositor/nodes/COM_ChromaMatteNode.cc @@ -16,7 +16,7 @@ ChromaMatteNode::ChromaMatteNode(bNode *editor_node) : Node(editor_node) void ChromaMatteNode::convert_to_operations(NodeConverter &converter, const CompositorContext & /*context*/) const { - bNode *editorsnode = get_bnode(); + const bNode *editorsnode = get_bnode(); NodeInput *input_socket_image = this->get_input_socket(0); NodeInput *input_socket_key = this->get_input_socket(1); diff --git a/source/blender/compositor/nodes/COM_ColorBalanceNode.cc b/source/blender/compositor/nodes/COM_ColorBalanceNode.cc index cb009ecc634..f3f5aa4e49b 100644 --- a/source/blender/compositor/nodes/COM_ColorBalanceNode.cc +++ b/source/blender/compositor/nodes/COM_ColorBalanceNode.cc @@ -15,7 +15,7 @@ ColorBalanceNode::ColorBalanceNode(bNode *editor_node) : Node(editor_node) void ColorBalanceNode::convert_to_operations(NodeConverter &converter, const CompositorContext & /*context*/) const { - bNode *node = this->get_bnode(); + const bNode *node = this->get_bnode(); NodeColorBalance *n = (NodeColorBalance *)node->storage; NodeInput *input_socket = this->get_input_socket(0); diff --git a/source/blender/compositor/nodes/COM_ColorCorrectionNode.cc b/source/blender/compositor/nodes/COM_ColorCorrectionNode.cc index 2497f1d57bc..3e021296fee 100644 --- a/source/blender/compositor/nodes/COM_ColorCorrectionNode.cc +++ b/source/blender/compositor/nodes/COM_ColorCorrectionNode.cc @@ -14,7 +14,7 @@ ColorCorrectionNode::ColorCorrectionNode(bNode *editor_node) : Node(editor_node) void ColorCorrectionNode::convert_to_operations(NodeConverter &converter, const CompositorContext & /*context*/) const { - bNode *editor_node = get_bnode(); + const bNode *editor_node = get_bnode(); ColorCorrectionOperation *operation = new ColorCorrectionOperation(); operation->set_data((NodeColorCorrection *)editor_node->storage); diff --git a/source/blender/compositor/nodes/COM_ColorCurveNode.cc b/source/blender/compositor/nodes/COM_ColorCurveNode.cc index ba81d375846..5275bfcab84 100644 --- a/source/blender/compositor/nodes/COM_ColorCurveNode.cc +++ b/source/blender/compositor/nodes/COM_ColorCurveNode.cc @@ -16,7 +16,7 @@ void ColorCurveNode::convert_to_operations(NodeConverter &converter, { if (this->get_input_socket(2)->is_linked() || this->get_input_socket(3)->is_linked()) { ColorCurveOperation *operation = new ColorCurveOperation(); - operation->set_curve_mapping((CurveMapping *)this->get_bnode()->storage); + operation->set_curve_mapping((const CurveMapping *)this->get_bnode()->storage); converter.add_operation(operation); converter.map_input_socket(get_input_socket(0), operation->get_input_socket(0)); @@ -33,7 +33,7 @@ void ColorCurveNode::convert_to_operations(NodeConverter &converter, operation->set_black_level(col); this->get_input_socket(3)->get_editor_value_color(col); operation->set_white_level(col); - operation->set_curve_mapping((CurveMapping *)this->get_bnode()->storage); + operation->set_curve_mapping((const CurveMapping *)this->get_bnode()->storage); converter.add_operation(operation); converter.map_input_socket(get_input_socket(0), operation->get_input_socket(0)); diff --git a/source/blender/compositor/nodes/COM_ColorMatteNode.cc b/source/blender/compositor/nodes/COM_ColorMatteNode.cc index f8fccd53a91..163ca4c7573 100644 --- a/source/blender/compositor/nodes/COM_ColorMatteNode.cc +++ b/source/blender/compositor/nodes/COM_ColorMatteNode.cc @@ -16,7 +16,7 @@ ColorMatteNode::ColorMatteNode(bNode *editor_node) : Node(editor_node) void ColorMatteNode::convert_to_operations(NodeConverter &converter, const CompositorContext & /*context*/) const { - bNode *editorsnode = get_bnode(); + const bNode *editorsnode = get_bnode(); NodeInput *input_socket_image = this->get_input_socket(0); NodeInput *input_socket_key = this->get_input_socket(1); diff --git a/source/blender/compositor/nodes/COM_ColorRampNode.cc b/source/blender/compositor/nodes/COM_ColorRampNode.cc index fd91678800a..c2569a27fe6 100644 --- a/source/blender/compositor/nodes/COM_ColorRampNode.cc +++ b/source/blender/compositor/nodes/COM_ColorRampNode.cc @@ -18,7 +18,7 @@ void ColorRampNode::convert_to_operations(NodeConverter &converter, NodeInput *input_socket = this->get_input_socket(0); NodeOutput *output_socket = this->get_output_socket(0); NodeOutput *output_socket_alpha = this->get_output_socket(1); - bNode *editor_node = this->get_bnode(); + const bNode *editor_node = this->get_bnode(); ColorRampOperation *operation = new ColorRampOperation(); operation->set_color_band((ColorBand *)editor_node->storage); diff --git a/source/blender/compositor/nodes/COM_ColorSpillNode.cc b/source/blender/compositor/nodes/COM_ColorSpillNode.cc index 118879ebdcc..448c3db6c46 100644 --- a/source/blender/compositor/nodes/COM_ColorSpillNode.cc +++ b/source/blender/compositor/nodes/COM_ColorSpillNode.cc @@ -14,7 +14,7 @@ ColorSpillNode::ColorSpillNode(bNode *editor_node) : Node(editor_node) void ColorSpillNode::convert_to_operations(NodeConverter &converter, const CompositorContext & /*context*/) const { - bNode *editorsnode = get_bnode(); + const bNode *editorsnode = get_bnode(); NodeInput *input_socket_image = this->get_input_socket(0); NodeInput *input_socket_fac = this->get_input_socket(1); diff --git a/source/blender/compositor/nodes/COM_CombineColorNode.cc b/source/blender/compositor/nodes/COM_CombineColorNode.cc index ca2c59478fd..36ff24cb9c3 100644 --- a/source/blender/compositor/nodes/COM_CombineColorNode.cc +++ b/source/blender/compositor/nodes/COM_CombineColorNode.cc @@ -40,7 +40,7 @@ void CombineColorNode::convert_to_operations(NodeConverter &converter, converter.map_input_socket(input_bsocket, operation->get_input_socket(2)); converter.map_input_socket(input_asocket, operation->get_input_socket(3)); - bNode *editor_node = this->get_bnode(); + const bNode *editor_node = this->get_bnode(); NodeCMPCombSepColor *storage = (NodeCMPCombSepColor *)editor_node->storage; NodeOperation *color_conv = nullptr; diff --git a/source/blender/compositor/nodes/COM_CombineColorNodeLegacy.cc b/source/blender/compositor/nodes/COM_CombineColorNodeLegacy.cc index d5ba379bfc2..8ab84715179 100644 --- a/source/blender/compositor/nodes/COM_CombineColorNodeLegacy.cc +++ b/source/blender/compositor/nodes/COM_CombineColorNodeLegacy.cc @@ -65,7 +65,7 @@ NodeOperation *CombineHSVANode::get_color_converter(const CompositorContext & /* NodeOperation *CombineYCCANode::get_color_converter(const CompositorContext & /*context*/) const { ConvertYCCToRGBOperation *operation = new ConvertYCCToRGBOperation(); - bNode *editor_node = this->get_bnode(); + const bNode *editor_node = this->get_bnode(); operation->set_mode(editor_node->custom1); return operation; } diff --git a/source/blender/compositor/nodes/COM_CompositorNode.cc b/source/blender/compositor/nodes/COM_CompositorNode.cc index d9009e313dd..444dcbd5c6c 100644 --- a/source/blender/compositor/nodes/COM_CompositorNode.cc +++ b/source/blender/compositor/nodes/COM_CompositorNode.cc @@ -14,7 +14,7 @@ CompositorNode::CompositorNode(bNode *editor_node) : Node(editor_node) void CompositorNode::convert_to_operations(NodeConverter &converter, const CompositorContext &context) const { - bNode *editor_node = this->get_bnode(); + const bNode *editor_node = this->get_bnode(); bool is_active = (editor_node->flag & NODE_DO_OUTPUT_RECALC) || context.is_rendering(); bool ignore_alpha = (editor_node->custom2 & CMP_NODE_OUTPUT_IGNORE_ALPHA) != 0; diff --git a/source/blender/compositor/nodes/COM_ConvertAlphaNode.cc b/source/blender/compositor/nodes/COM_ConvertAlphaNode.cc index 690be1c4551..98fae0a47bf 100644 --- a/source/blender/compositor/nodes/COM_ConvertAlphaNode.cc +++ b/source/blender/compositor/nodes/COM_ConvertAlphaNode.cc @@ -10,7 +10,7 @@ void ConvertAlphaNode::convert_to_operations(NodeConverter &converter, const CompositorContext & /*context*/) const { NodeOperation *operation = nullptr; - bNode *node = this->get_bnode(); + const bNode *node = this->get_bnode(); /* value hardcoded in rna_nodetree.c */ if (node->custom1 == 1) { diff --git a/source/blender/compositor/nodes/COM_ConvertColorSpaceNode.cc b/source/blender/compositor/nodes/COM_ConvertColorSpaceNode.cc index 219b3acbd04..7d557de66e4 100644 --- a/source/blender/compositor/nodes/COM_ConvertColorSpaceNode.cc +++ b/source/blender/compositor/nodes/COM_ConvertColorSpaceNode.cc @@ -27,7 +27,7 @@ ConvertColorSpaceNode::ConvertColorSpaceNode(bNode *editorNode) : Node(editorNod void ConvertColorSpaceNode::convert_to_operations(NodeConverter &converter, const CompositorContext &UNUSED(context)) const { - bNode *b_node = get_bnode(); + const bNode *b_node = get_bnode(); NodeInput *inputSocketImage = this->get_input_socket(0); NodeOutput *outputSocketImage = this->get_output_socket(0); @@ -50,7 +50,7 @@ void ConvertColorSpaceNode::convert_to_operations(NodeConverter &converter, bool ConvertColorSpaceNode::performs_conversion(NodeConvertColorSpace &settings) const { - bNode *b_node = get_bnode(); + const bNode *b_node = get_bnode(); if (IMB_colormanagement_space_name_is_data(settings.from_color_space)) { CLOG_INFO(&LOG, diff --git a/source/blender/compositor/nodes/COM_CropNode.cc b/source/blender/compositor/nodes/COM_CropNode.cc index fd07b028a01..849fb80a8a8 100644 --- a/source/blender/compositor/nodes/COM_CropNode.cc +++ b/source/blender/compositor/nodes/COM_CropNode.cc @@ -14,7 +14,7 @@ CropNode::CropNode(bNode *editor_node) : Node(editor_node) void CropNode::convert_to_operations(NodeConverter &converter, const CompositorContext & /*context*/) const { - bNode *node = get_bnode(); + const bNode *node = get_bnode(); NodeTwoXYs *crop_settings = (NodeTwoXYs *)node->storage; bool relative = (bool)node->custom2; bool crop_image = (bool)node->custom1; diff --git a/source/blender/compositor/nodes/COM_CryptomatteNode.cc b/source/blender/compositor/nodes/COM_CryptomatteNode.cc index b1cae5bfc24..42d699af01b 100644 --- a/source/blender/compositor/nodes/COM_CryptomatteNode.cc +++ b/source/blender/compositor/nodes/COM_CryptomatteNode.cc @@ -23,8 +23,9 @@ void CryptomatteBaseNode::convert_to_operations(NodeConverter &converter, { NodeOutput *output_image_socket = this->get_output_socket(0); - bNode *node = this->get_bnode(); - NodeCryptomatte *cryptomatte_settings = static_cast<NodeCryptomatte *>(node->storage); + const bNode *node = this->get_bnode(); + const NodeCryptomatte *cryptomatte_settings = static_cast<const NodeCryptomatte *>( + node->storage); CryptomatteOperation *cryptomatte_operation = create_cryptomatte_operation( converter, context, *node, cryptomatte_settings); diff --git a/source/blender/compositor/nodes/COM_DefocusNode.cc b/source/blender/compositor/nodes/COM_DefocusNode.cc index 26bb79a5846..40ccf0fad15 100644 --- a/source/blender/compositor/nodes/COM_DefocusNode.cc +++ b/source/blender/compositor/nodes/COM_DefocusNode.cc @@ -19,8 +19,8 @@ DefocusNode::DefocusNode(bNode *editor_node) : Node(editor_node) void DefocusNode::convert_to_operations(NodeConverter &converter, const CompositorContext &context) const { - bNode *node = this->get_bnode(); - NodeDefocus *data = (NodeDefocus *)node->storage; + const bNode *node = this->get_bnode(); + const NodeDefocus *data = (const NodeDefocus *)node->storage; Scene *scene = node->id ? (Scene *)node->id : context.get_scene(); Object *camob = scene ? scene->camera : nullptr; diff --git a/source/blender/compositor/nodes/COM_DenoiseNode.cc b/source/blender/compositor/nodes/COM_DenoiseNode.cc index 301cb123359..81f45da475d 100644 --- a/source/blender/compositor/nodes/COM_DenoiseNode.cc +++ b/source/blender/compositor/nodes/COM_DenoiseNode.cc @@ -19,8 +19,8 @@ void DenoiseNode::convert_to_operations(NodeConverter &converter, return; } - bNode *node = this->get_bnode(); - NodeDenoise *denoise = (NodeDenoise *)node->storage; + const bNode *node = this->get_bnode(); + const NodeDenoise *denoise = (const NodeDenoise *)node->storage; DenoiseOperation *operation = new DenoiseOperation(); converter.add_operation(operation); diff --git a/source/blender/compositor/nodes/COM_DespeckleNode.cc b/source/blender/compositor/nodes/COM_DespeckleNode.cc index cbdff384eda..c2601435f34 100644 --- a/source/blender/compositor/nodes/COM_DespeckleNode.cc +++ b/source/blender/compositor/nodes/COM_DespeckleNode.cc @@ -14,7 +14,7 @@ DespeckleNode::DespeckleNode(bNode *editor_node) : Node(editor_node) void DespeckleNode::convert_to_operations(NodeConverter &converter, const CompositorContext & /*context*/) const { - bNode *editor_node = this->get_bnode(); + const bNode *editor_node = this->get_bnode(); NodeInput *input_socket = this->get_input_socket(0); NodeInput *input_image_socket = this->get_input_socket(1); NodeOutput *output_socket = this->get_output_socket(0); diff --git a/source/blender/compositor/nodes/COM_DifferenceMatteNode.cc b/source/blender/compositor/nodes/COM_DifferenceMatteNode.cc index 7c5d1f69fb1..7ec90cb33d8 100644 --- a/source/blender/compositor/nodes/COM_DifferenceMatteNode.cc +++ b/source/blender/compositor/nodes/COM_DifferenceMatteNode.cc @@ -19,7 +19,7 @@ void DifferenceMatteNode::convert_to_operations(NodeConverter &converter, NodeInput *input_socket2 = this->get_input_socket(1); NodeOutput *output_socket_image = this->get_output_socket(0); NodeOutput *output_socket_matte = this->get_output_socket(1); - bNode *editor_node = this->get_bnode(); + const bNode *editor_node = this->get_bnode(); DifferenceMatteOperation *operation_set = new DifferenceMatteOperation(); operation_set->set_settings((NodeChroma *)editor_node->storage); diff --git a/source/blender/compositor/nodes/COM_DilateErodeNode.cc b/source/blender/compositor/nodes/COM_DilateErodeNode.cc index ee3ad8f4639..1b8c9d3b0a0 100644 --- a/source/blender/compositor/nodes/COM_DilateErodeNode.cc +++ b/source/blender/compositor/nodes/COM_DilateErodeNode.cc @@ -27,9 +27,8 @@ DilateErodeNode::DilateErodeNode(bNode *editor_node) : Node(editor_node) void DilateErodeNode::convert_to_operations(NodeConverter &converter, const CompositorContext &context) const { - - bNode *editor_node = this->get_bnode(); - if (editor_node->custom1 == CMP_NODE_DILATEERODE_DISTANCE_THRESH) { + const bNode *editor_node = this->get_bnode(); + if (editor_node->custom1 == CMP_NODE_DILATE_ERODE_DISTANCE_THRESHOLD) { DilateErodeThresholdOperation *operation = new DilateErodeThresholdOperation(); operation->set_distance(editor_node->custom2); operation->set_inset(editor_node->custom3); @@ -48,7 +47,7 @@ void DilateErodeNode::convert_to_operations(NodeConverter &converter, converter.map_output_socket(get_output_socket(0), operation->get_output_socket(0)); } } - else if (editor_node->custom1 == CMP_NODE_DILATEERODE_DISTANCE) { + else if (editor_node->custom1 == CMP_NODE_DILATE_ERODE_DISTANCE) { if (editor_node->custom2 > 0) { DilateDistanceOperation *operation = new DilateDistanceOperation(); operation->set_distance(editor_node->custom2); @@ -66,7 +65,7 @@ void DilateErodeNode::convert_to_operations(NodeConverter &converter, converter.map_output_socket(get_output_socket(0), operation->get_output_socket(0)); } } - else if (editor_node->custom1 == CMP_NODE_DILATEERODE_DISTANCE_FEATHER) { + else if (editor_node->custom1 == CMP_NODE_DILATE_ERODE_DISTANCE_FEATHER) { /* this uses a modified gaussian blur function otherwise its far too slow */ eCompositorQuality quality = context.get_quality(); diff --git a/source/blender/compositor/nodes/COM_DirectionalBlurNode.cc b/source/blender/compositor/nodes/COM_DirectionalBlurNode.cc index b9477cc6783..52df671674b 100644 --- a/source/blender/compositor/nodes/COM_DirectionalBlurNode.cc +++ b/source/blender/compositor/nodes/COM_DirectionalBlurNode.cc @@ -14,7 +14,7 @@ DirectionalBlurNode::DirectionalBlurNode(bNode *editor_node) : Node(editor_node) void DirectionalBlurNode::convert_to_operations(NodeConverter &converter, const CompositorContext &context) const { - NodeDBlurData *data = (NodeDBlurData *)this->get_bnode()->storage; + const NodeDBlurData *data = (const NodeDBlurData *)this->get_bnode()->storage; DirectionalBlurOperation *operation = new DirectionalBlurOperation(); operation->set_quality(context.get_quality()); operation->set_data(data); diff --git a/source/blender/compositor/nodes/COM_DistanceMatteNode.cc b/source/blender/compositor/nodes/COM_DistanceMatteNode.cc index ea0327d3d46..249fc48ab67 100644 --- a/source/blender/compositor/nodes/COM_DistanceMatteNode.cc +++ b/source/blender/compositor/nodes/COM_DistanceMatteNode.cc @@ -16,8 +16,8 @@ DistanceMatteNode::DistanceMatteNode(bNode *editor_node) : Node(editor_node) void DistanceMatteNode::convert_to_operations(NodeConverter &converter, const CompositorContext & /*context*/) const { - bNode *editorsnode = get_bnode(); - NodeChroma *storage = (NodeChroma *)editorsnode->storage; + const bNode *editorsnode = this->get_bnode(); + const NodeChroma *storage = (const NodeChroma *)editorsnode->storage; NodeInput *input_socket_image = this->get_input_socket(0); NodeInput *input_socket_key = this->get_input_socket(1); diff --git a/source/blender/compositor/nodes/COM_DoubleEdgeMaskNode.cc b/source/blender/compositor/nodes/COM_DoubleEdgeMaskNode.cc index eb4510a104c..7bd14d7a8d4 100644 --- a/source/blender/compositor/nodes/COM_DoubleEdgeMaskNode.cc +++ b/source/blender/compositor/nodes/COM_DoubleEdgeMaskNode.cc @@ -15,7 +15,7 @@ void DoubleEdgeMaskNode::convert_to_operations(NodeConverter &converter, const CompositorContext & /*context*/) const { DoubleEdgeMaskOperation *operation; - bNode *bnode = this->get_bnode(); + const bNode *bnode = this->get_bnode(); operation = new DoubleEdgeMaskOperation(); operation->set_adjecent_only(bnode->custom1); diff --git a/source/blender/compositor/nodes/COM_FilterNode.cc b/source/blender/compositor/nodes/COM_FilterNode.cc index f2efa8caefd..dce08b4cf2c 100644 --- a/source/blender/compositor/nodes/COM_FilterNode.cc +++ b/source/blender/compositor/nodes/COM_FilterNode.cc @@ -21,7 +21,7 @@ void FilterNode::convert_to_operations(NodeConverter &converter, ConvolutionFilterOperation *operation = nullptr; switch (this->get_bnode()->custom1) { - case CMP_FILT_SOFT: + case CMP_NODE_FILTER_SOFT: operation = new ConvolutionFilterOperation(); operation->set3x3Filter(1 / 16.0f, 2 / 16.0f, @@ -33,11 +33,11 @@ void FilterNode::convert_to_operations(NodeConverter &converter, 2 / 16.0f, 1 / 16.0f); break; - case CMP_FILT_SHARP_BOX: + case CMP_NODE_FILTER_SHARP_BOX: operation = new ConvolutionFilterOperation(); operation->set3x3Filter(-1, -1, -1, -1, 9, -1, -1, -1, -1); break; - case CMP_FILT_LAPLACE: + case CMP_NODE_FILTER_LAPLACE: operation = new ConvolutionEdgeFilterOperation(); operation->set3x3Filter(-1 / 8.0f, -1 / 8.0f, @@ -49,23 +49,23 @@ void FilterNode::convert_to_operations(NodeConverter &converter, -1 / 8.0f, -1 / 8.0f); break; - case CMP_FILT_SOBEL: + case CMP_NODE_FILTER_SOBEL: operation = new ConvolutionEdgeFilterOperation(); operation->set3x3Filter(1, 2, 1, 0, 0, 0, -1, -2, -1); break; - case CMP_FILT_PREWITT: + case CMP_NODE_FILTER_PREWITT: operation = new ConvolutionEdgeFilterOperation(); operation->set3x3Filter(1, 1, 1, 0, 0, 0, -1, -1, -1); break; - case CMP_FILT_KIRSCH: + case CMP_NODE_FILTER_KIRSCH: operation = new ConvolutionEdgeFilterOperation(); operation->set3x3Filter(5, 5, 5, -3, -3, -3, -2, -2, -2); break; - case CMP_FILT_SHADOW: + case CMP_NODE_FILTER_SHADOW: operation = new ConvolutionFilterOperation(); operation->set3x3Filter(1, 2, 1, 0, 1, 0, -1, -2, -1); break; - case CMP_FILT_SHARP_DIAMOND: + case CMP_NODE_FILTER_SHARP_DIAMOND: operation = new ConvolutionFilterOperation(); operation->set3x3Filter(0, -1, 0, -1, 5, -1, 0, -1, 0); break; diff --git a/source/blender/compositor/nodes/COM_GlareNode.cc b/source/blender/compositor/nodes/COM_GlareNode.cc index 0d7be486b9b..d80e6f9543f 100644 --- a/source/blender/compositor/nodes/COM_GlareNode.cc +++ b/source/blender/compositor/nodes/COM_GlareNode.cc @@ -20,8 +20,8 @@ GlareNode::GlareNode(bNode *editor_node) : Node(editor_node) void GlareNode::convert_to_operations(NodeConverter &converter, const CompositorContext & /*context*/) const { - bNode *node = this->get_bnode(); - NodeGlare *glare = (NodeGlare *)node->storage; + const bNode *node = this->get_bnode(); + const NodeGlare *glare = (const NodeGlare *)node->storage; GlareBaseOperation *glareoperation = nullptr; switch (glare->type) { diff --git a/source/blender/compositor/nodes/COM_HueSaturationValueCorrectNode.cc b/source/blender/compositor/nodes/COM_HueSaturationValueCorrectNode.cc index 8ae415b9c28..163cddb6f46 100644 --- a/source/blender/compositor/nodes/COM_HueSaturationValueCorrectNode.cc +++ b/source/blender/compositor/nodes/COM_HueSaturationValueCorrectNode.cc @@ -21,7 +21,7 @@ void HueSaturationValueCorrectNode::convert_to_operations( NodeInput *value_socket = this->get_input_socket(0); NodeInput *color_socket = this->get_input_socket(1); NodeOutput *output_socket = this->get_output_socket(0); - bNode *editorsnode = get_bnode(); + const bNode *editorsnode = get_bnode(); CurveMapping *storage = (CurveMapping *)editorsnode->storage; ConvertRGBToHSVOperation *rgbToHSV = new ConvertRGBToHSVOperation(); diff --git a/source/blender/compositor/nodes/COM_IDMaskNode.cc b/source/blender/compositor/nodes/COM_IDMaskNode.cc index f9c284b916c..a1e08bee0cb 100644 --- a/source/blender/compositor/nodes/COM_IDMaskNode.cc +++ b/source/blender/compositor/nodes/COM_IDMaskNode.cc @@ -14,7 +14,7 @@ IDMaskNode::IDMaskNode(bNode *editor_node) : Node(editor_node) void IDMaskNode::convert_to_operations(NodeConverter &converter, const CompositorContext & /*context*/) const { - bNode *bnode = this->get_bnode(); + const bNode *bnode = this->get_bnode(); IDMaskOperation *operation; operation = new IDMaskOperation(); diff --git a/source/blender/compositor/nodes/COM_ImageNode.cc b/source/blender/compositor/nodes/COM_ImageNode.cc index 35031888d5c..a7cc6bf39df 100644 --- a/source/blender/compositor/nodes/COM_ImageNode.cc +++ b/source/blender/compositor/nodes/COM_ImageNode.cc @@ -55,7 +55,7 @@ void ImageNode::convert_to_operations(NodeConverter &converter, { /** Image output */ NodeOutput *output_image = this->get_output_socket(0); - bNode *editor_node = this->get_bnode(); + const bNode *editor_node = this->get_bnode(); Image *image = (Image *)editor_node->id; ImageUser *imageuser = (ImageUser *)editor_node->storage; int framenumber = context.get_framenumber(); diff --git a/source/blender/compositor/nodes/COM_InpaintNode.cc b/source/blender/compositor/nodes/COM_InpaintNode.cc index 1b899163e4d..dd80380387f 100644 --- a/source/blender/compositor/nodes/COM_InpaintNode.cc +++ b/source/blender/compositor/nodes/COM_InpaintNode.cc @@ -15,7 +15,7 @@ void InpaintNode::convert_to_operations(NodeConverter &converter, const CompositorContext & /*context*/) const { - bNode *editor_node = this->get_bnode(); + const bNode *editor_node = this->get_bnode(); /* if (editor_node->custom1 == CMP_NODE_INPAINT_SIMPLE) { */ if (true) { diff --git a/source/blender/compositor/nodes/COM_InvertNode.cc b/source/blender/compositor/nodes/COM_InvertNode.cc index 002136e5c2a..ce5c4f48f9e 100644 --- a/source/blender/compositor/nodes/COM_InvertNode.cc +++ b/source/blender/compositor/nodes/COM_InvertNode.cc @@ -16,7 +16,7 @@ void InvertNode::convert_to_operations(NodeConverter &converter, const CompositorContext & /*context*/) const { InvertOperation *operation = new InvertOperation(); - bNode *node = this->get_bnode(); + const bNode *node = this->get_bnode(); operation->set_color(node->custom1 & CMP_CHAN_RGB); operation->set_alpha(node->custom1 & CMP_CHAN_A); converter.add_operation(operation); diff --git a/source/blender/compositor/nodes/COM_KeyingNode.cc b/source/blender/compositor/nodes/COM_KeyingNode.cc index 10e6b5597f2..a9138f026f7 100644 --- a/source/blender/compositor/nodes/COM_KeyingNode.cc +++ b/source/blender/compositor/nodes/COM_KeyingNode.cc @@ -205,8 +205,8 @@ NodeOperationOutput *KeyingNode::setup_clip(NodeConverter &converter, void KeyingNode::convert_to_operations(NodeConverter &converter, const CompositorContext &context) const { - bNode *editor_node = this->get_bnode(); - NodeKeyingData *keying_data = (NodeKeyingData *)editor_node->storage; + const bNode *editor_node = this->get_bnode(); + const NodeKeyingData *keying_data = (const NodeKeyingData *)editor_node->storage; NodeInput *input_image = this->get_input_socket(0); NodeInput *input_screen = this->get_input_socket(1); diff --git a/source/blender/compositor/nodes/COM_KeyingScreenNode.cc b/source/blender/compositor/nodes/COM_KeyingScreenNode.cc index 1e159e11966..7470d49bc1b 100644 --- a/source/blender/compositor/nodes/COM_KeyingScreenNode.cc +++ b/source/blender/compositor/nodes/COM_KeyingScreenNode.cc @@ -14,7 +14,7 @@ KeyingScreenNode::KeyingScreenNode(bNode *editor_node) : Node(editor_node) void KeyingScreenNode::convert_to_operations(NodeConverter &converter, const CompositorContext &context) const { - bNode *editor_node = this->get_bnode(); + const bNode *editor_node = this->get_bnode(); MovieClip *clip = (MovieClip *)editor_node->id; NodeKeyingScreenData *keyingscreen_data = (NodeKeyingScreenData *)editor_node->storage; diff --git a/source/blender/compositor/nodes/COM_LensDistortionNode.cc b/source/blender/compositor/nodes/COM_LensDistortionNode.cc index 3fe1125382e..5ed97c614ed 100644 --- a/source/blender/compositor/nodes/COM_LensDistortionNode.cc +++ b/source/blender/compositor/nodes/COM_LensDistortionNode.cc @@ -15,7 +15,7 @@ LensDistortionNode::LensDistortionNode(bNode *editor_node) : Node(editor_node) void LensDistortionNode::convert_to_operations(NodeConverter &converter, const CompositorContext & /*context*/) const { - bNode *editor_node = this->get_bnode(); + const bNode *editor_node = this->get_bnode(); NodeLensDist *data = (NodeLensDist *)editor_node->storage; if (data->proj) { ProjectorLensDistortionOperation *operation = new ProjectorLensDistortionOperation(); diff --git a/source/blender/compositor/nodes/COM_LuminanceMatteNode.cc b/source/blender/compositor/nodes/COM_LuminanceMatteNode.cc index 00ae679581c..06e8eb58c4d 100644 --- a/source/blender/compositor/nodes/COM_LuminanceMatteNode.cc +++ b/source/blender/compositor/nodes/COM_LuminanceMatteNode.cc @@ -15,7 +15,7 @@ LuminanceMatteNode::LuminanceMatteNode(bNode *editor_node) : Node(editor_node) void LuminanceMatteNode::convert_to_operations(NodeConverter &converter, const CompositorContext & /*context*/) const { - bNode *editorsnode = get_bnode(); + const bNode *editorsnode = get_bnode(); NodeInput *input_socket = this->get_input_socket(0); NodeOutput *output_socket_image = this->get_output_socket(0); NodeOutput *output_socket_matte = this->get_output_socket(1); diff --git a/source/blender/compositor/nodes/COM_MapUVNode.cc b/source/blender/compositor/nodes/COM_MapUVNode.cc index 54a24a94ad1..ed9bff657f3 100644 --- a/source/blender/compositor/nodes/COM_MapUVNode.cc +++ b/source/blender/compositor/nodes/COM_MapUVNode.cc @@ -14,7 +14,7 @@ MapUVNode::MapUVNode(bNode *editor_node) : Node(editor_node) void MapUVNode::convert_to_operations(NodeConverter &converter, const CompositorContext & /*context*/) const { - bNode *node = this->get_bnode(); + const bNode *node = this->get_bnode(); MapUVOperation *operation = new MapUVOperation(); operation->set_alpha((float)node->custom1); diff --git a/source/blender/compositor/nodes/COM_MapValueNode.cc b/source/blender/compositor/nodes/COM_MapValueNode.cc index be7289e61b1..8b3c3cc3ec6 100644 --- a/source/blender/compositor/nodes/COM_MapValueNode.cc +++ b/source/blender/compositor/nodes/COM_MapValueNode.cc @@ -15,7 +15,7 @@ MapValueNode::MapValueNode(bNode *editor_node) : Node(editor_node) void MapValueNode::convert_to_operations(NodeConverter &converter, const CompositorContext & /*context*/) const { - TexMapping *storage = (TexMapping *)this->get_bnode()->storage; + const TexMapping *storage = (const TexMapping *)this->get_bnode()->storage; NodeInput *color_socket = this->get_input_socket(0); NodeOutput *value_socket = this->get_output_socket(0); diff --git a/source/blender/compositor/nodes/COM_MaskNode.cc b/source/blender/compositor/nodes/COM_MaskNode.cc index 01f98416a9e..f92e307328d 100644 --- a/source/blender/compositor/nodes/COM_MaskNode.cc +++ b/source/blender/compositor/nodes/COM_MaskNode.cc @@ -19,8 +19,8 @@ void MaskNode::convert_to_operations(NodeConverter &converter, NodeOutput *output_mask = this->get_output_socket(0); - bNode *editor_node = this->get_bnode(); - NodeMask *data = (NodeMask *)editor_node->storage; + const bNode *editor_node = this->get_bnode(); + const NodeMask *data = (const NodeMask *)editor_node->storage; Mask *mask = (Mask *)editor_node->id; /* Always connect the output image. */ diff --git a/source/blender/compositor/nodes/COM_MixNode.cc b/source/blender/compositor/nodes/COM_MixNode.cc index a44dfc6caa6..e9179d7063c 100644 --- a/source/blender/compositor/nodes/COM_MixNode.cc +++ b/source/blender/compositor/nodes/COM_MixNode.cc @@ -21,7 +21,7 @@ void MixNode::convert_to_operations(NodeConverter &converter, NodeInput *color1Socket = this->get_input_socket(1); NodeInput *color2Socket = this->get_input_socket(2); NodeOutput *output_socket = this->get_output_socket(0); - bNode *editor_node = this->get_bnode(); + const bNode *editor_node = this->get_bnode(); bool use_alpha_premultiply = (this->get_bnode()->custom2 & 1) != 0; bool use_clamp = (this->get_bnode()->custom2 & 2) != 0; diff --git a/source/blender/compositor/nodes/COM_MovieClipNode.cc b/source/blender/compositor/nodes/COM_MovieClipNode.cc index d3f522fd8cd..e870cd7e0da 100644 --- a/source/blender/compositor/nodes/COM_MovieClipNode.cc +++ b/source/blender/compositor/nodes/COM_MovieClipNode.cc @@ -29,7 +29,7 @@ void MovieClipNode::convert_to_operations(NodeConverter &converter, NodeOutput *scale_movie_clip = this->get_output_socket(4); NodeOutput *angle_movie_clip = this->get_output_socket(5); - bNode *editor_node = this->get_bnode(); + const bNode *editor_node = this->get_bnode(); MovieClip *movie_clip = (MovieClip *)editor_node->id; MovieClipUser *movie_clip_user = (MovieClipUser *)editor_node->storage; bool cache_frame = !context.is_rendering(); diff --git a/source/blender/compositor/nodes/COM_MovieDistortionNode.cc b/source/blender/compositor/nodes/COM_MovieDistortionNode.cc index 41ecfc24a8a..7c6e19c03dd 100644 --- a/source/blender/compositor/nodes/COM_MovieDistortionNode.cc +++ b/source/blender/compositor/nodes/COM_MovieDistortionNode.cc @@ -15,7 +15,7 @@ MovieDistortionNode::MovieDistortionNode(bNode *editor_node) : Node(editor_node) void MovieDistortionNode::convert_to_operations(NodeConverter &converter, const CompositorContext &context) const { - bNode *bnode = this->get_bnode(); + const bNode *bnode = this->get_bnode(); MovieClip *clip = (MovieClip *)bnode->id; NodeInput *input_socket = this->get_input_socket(0); diff --git a/source/blender/compositor/nodes/COM_OutputFileNode.cc b/source/blender/compositor/nodes/COM_OutputFileNode.cc index f69511d88e6..c83bcf42efd 100644 --- a/source/blender/compositor/nodes/COM_OutputFileNode.cc +++ b/source/blender/compositor/nodes/COM_OutputFileNode.cc @@ -37,6 +37,10 @@ void OutputFileNode::map_input_sockets(NodeConverter &converter, void OutputFileNode::add_preview_to_first_linked_input(NodeConverter &converter) const { + if (get_input_sockets().is_empty()) { + return; + } + NodeInput *first_socket = this->get_input_socket(0); if (first_socket->is_linked()) { converter.add_node_input_preview(first_socket); @@ -46,7 +50,7 @@ void OutputFileNode::add_preview_to_first_linked_input(NodeConverter &converter) void OutputFileNode::convert_to_operations(NodeConverter &converter, const CompositorContext &context) const { - NodeImageMultiFile *storage = (NodeImageMultiFile *)this->get_bnode()->storage; + const NodeImageMultiFile *storage = (const NodeImageMultiFile *)this->get_bnode()->storage; const bool is_multiview = (context.get_render_data()->scemode & R_MULTIVIEW) != 0; add_preview_to_first_linked_input(converter); @@ -95,8 +99,8 @@ void OutputFileNode::convert_to_operations(NodeConverter &converter, if (input->is_linked()) { NodeImageMultiFileSocket *sockdata = (NodeImageMultiFileSocket *)input->get_bnode_socket()->storage; - ImageFormatData *format = (sockdata->use_node_format ? &storage->format : - &sockdata->format); + const ImageFormatData *format = (sockdata->use_node_format ? &storage->format : + &sockdata->format); char path[FILE_MAX]; /* combine file path for the input */ diff --git a/source/blender/compositor/nodes/COM_PlaneTrackDeformNode.cc b/source/blender/compositor/nodes/COM_PlaneTrackDeformNode.cc index a2037812677..308fd81a12d 100644 --- a/source/blender/compositor/nodes/COM_PlaneTrackDeformNode.cc +++ b/source/blender/compositor/nodes/COM_PlaneTrackDeformNode.cc @@ -15,7 +15,7 @@ PlaneTrackDeformNode::PlaneTrackDeformNode(bNode *editor_node) : Node(editor_nod void PlaneTrackDeformNode::convert_to_operations(NodeConverter &converter, const CompositorContext &context) const { - bNode *editor_node = this->get_bnode(); + const bNode *editor_node = this->get_bnode(); MovieClip *clip = (MovieClip *)editor_node->id; NodePlaneTrackDeformData *data = (NodePlaneTrackDeformData *)editor_node->storage; diff --git a/source/blender/compositor/nodes/COM_ScaleNode.cc b/source/blender/compositor/nodes/COM_ScaleNode.cc index 6eea61b1568..1d613a030d7 100644 --- a/source/blender/compositor/nodes/COM_ScaleNode.cc +++ b/source/blender/compositor/nodes/COM_ScaleNode.cc @@ -17,7 +17,7 @@ ScaleNode::ScaleNode(bNode *editor_node) : Node(editor_node) void ScaleNode::convert_to_operations(NodeConverter &converter, const CompositorContext &context) const { - bNode *bnode = this->get_bnode(); + const bNode *bnode = this->get_bnode(); NodeInput *input_socket = this->get_input_socket(0); NodeInput *input_xsocket = this->get_input_socket(1); @@ -25,7 +25,7 @@ void ScaleNode::convert_to_operations(NodeConverter &converter, NodeOutput *output_socket = this->get_output_socket(0); switch (bnode->custom1) { - case CMP_SCALE_RELATIVE: { + case CMP_NODE_SCALE_RELATIVE: { ScaleRelativeOperation *operation = new ScaleRelativeOperation(); converter.add_operation(operation); @@ -39,7 +39,7 @@ void ScaleNode::convert_to_operations(NodeConverter &converter, break; } - case CMP_SCALE_SCENEPERCENT: { + case CMP_NODE_SCALE_RENDER_PERCENT: { SetValueOperation *scale_factor_operation = new SetValueOperation(); scale_factor_operation->set_value(context.get_render_percentage_as_factor()); converter.add_operation(scale_factor_operation); @@ -59,13 +59,14 @@ void ScaleNode::convert_to_operations(NodeConverter &converter, break; } - case CMP_SCALE_RENDERPERCENT: { + case CMP_NODE_SCALE_RENDER_SIZE: { const RenderData *rd = context.get_render_data(); const float render_size_factor = context.get_render_percentage_as_factor(); ScaleFixedSizeOperation *operation = new ScaleFixedSizeOperation(); /* framing options */ - operation->set_is_aspect((bnode->custom2 & CMP_SCALE_RENDERSIZE_FRAME_ASPECT) != 0); - operation->set_is_crop((bnode->custom2 & CMP_SCALE_RENDERSIZE_FRAME_CROP) != 0); + operation->set_is_aspect( + ELEM(bnode->custom2, CMP_NODE_SCALE_RENDER_SIZE_FIT, CMP_NODE_SCALE_RENDER_SIZE_CROP)); + operation->set_is_crop(bnode->custom2 == CMP_NODE_SCALE_RENDER_SIZE_CROP); operation->set_offset(bnode->custom3, bnode->custom4); operation->set_new_width(rd->xsch * render_size_factor); operation->set_new_height(rd->ysch * render_size_factor); @@ -79,7 +80,7 @@ void ScaleNode::convert_to_operations(NodeConverter &converter, break; } - case CMP_SCALE_ABSOLUTE: { + case CMP_NODE_SCALE_ABSOLUTE: { /* TODO: what is the use of this one.... perhaps some issues when the ui was updated... */ ScaleAbsoluteOperation *operation = new ScaleAbsoluteOperation(); converter.add_operation(operation); diff --git a/source/blender/compositor/nodes/COM_SeparateColorNode.cc b/source/blender/compositor/nodes/COM_SeparateColorNode.cc index a956c02ed42..28ebbb35e9a 100644 --- a/source/blender/compositor/nodes/COM_SeparateColorNode.cc +++ b/source/blender/compositor/nodes/COM_SeparateColorNode.cc @@ -20,8 +20,8 @@ void SeparateColorNode::convert_to_operations(NodeConverter &converter, NodeOutput *output_bsocket = this->get_output_socket(2); NodeOutput *output_asocket = this->get_output_socket(3); - bNode *editor_node = this->get_bnode(); - NodeCMPCombSepColor *storage = (NodeCMPCombSepColor *)editor_node->storage; + const bNode *editor_node = this->get_bnode(); + const NodeCMPCombSepColor *storage = (const NodeCMPCombSepColor *)editor_node->storage; NodeOperation *color_conv = nullptr; switch (storage->mode) { diff --git a/source/blender/compositor/nodes/COM_SeparateColorNodeLegacy.cc b/source/blender/compositor/nodes/COM_SeparateColorNodeLegacy.cc index c3728bc152f..11d8ca31d35 100644 --- a/source/blender/compositor/nodes/COM_SeparateColorNodeLegacy.cc +++ b/source/blender/compositor/nodes/COM_SeparateColorNodeLegacy.cc @@ -97,7 +97,7 @@ NodeOperation *SeparateHSVANode::get_color_converter(const CompositorContext & / NodeOperation *SeparateYCCANode::get_color_converter(const CompositorContext & /*context*/) const { ConvertRGBToYCCOperation *operation = new ConvertRGBToYCCOperation(); - bNode *editor_node = this->get_bnode(); + const bNode *editor_node = this->get_bnode(); operation->set_mode(editor_node->custom1); return operation; } diff --git a/source/blender/compositor/nodes/COM_SplitViewerNode.cc b/source/blender/compositor/nodes/COM_SplitViewerNode.cc index d02bc6e773d..0f21bd6e50d 100644 --- a/source/blender/compositor/nodes/COM_SplitViewerNode.cc +++ b/source/blender/compositor/nodes/COM_SplitViewerNode.cc @@ -16,7 +16,7 @@ SplitViewerNode::SplitViewerNode(bNode *editor_node) : Node(editor_node) void SplitViewerNode::convert_to_operations(NodeConverter &converter, const CompositorContext &context) const { - bNode *editor_node = this->get_bnode(); + const bNode *editor_node = this->get_bnode(); bool do_output = (editor_node->flag & NODE_DO_OUTPUT_RECALC || context.is_rendering()) && (editor_node->flag & NODE_DO_OUTPUT); diff --git a/source/blender/compositor/nodes/COM_Stabilize2dNode.cc b/source/blender/compositor/nodes/COM_Stabilize2dNode.cc index bcb1cf2accc..360e9e0bce3 100644 --- a/source/blender/compositor/nodes/COM_Stabilize2dNode.cc +++ b/source/blender/compositor/nodes/COM_Stabilize2dNode.cc @@ -18,7 +18,7 @@ Stabilize2dNode::Stabilize2dNode(bNode *editor_node) : Node(editor_node) void Stabilize2dNode::convert_to_operations(NodeConverter &converter, const CompositorContext &context) const { - bNode *editor_node = this->get_bnode(); + const bNode *editor_node = this->get_bnode(); NodeInput *image_input = this->get_input_socket(0); MovieClip *clip = (MovieClip *)editor_node->id; bool invert = (editor_node->custom2 & CMP_NODEFLAG_STABILIZE_INVERSE) != 0; diff --git a/source/blender/compositor/nodes/COM_SunBeamsNode.cc b/source/blender/compositor/nodes/COM_SunBeamsNode.cc index 197098f2ff4..c33d9d0faf5 100644 --- a/source/blender/compositor/nodes/COM_SunBeamsNode.cc +++ b/source/blender/compositor/nodes/COM_SunBeamsNode.cc @@ -16,7 +16,7 @@ void SunBeamsNode::convert_to_operations(NodeConverter &converter, { NodeInput *input_socket = this->get_input_socket(0); NodeOutput *output_socket = this->get_output_socket(0); - NodeSunBeams *data = (NodeSunBeams *)get_bnode()->storage; + const NodeSunBeams *data = (const NodeSunBeams *)get_bnode()->storage; SunBeamsOperation *operation = new SunBeamsOperation(); operation->set_data(*data); diff --git a/source/blender/compositor/nodes/COM_SwitchViewNode.cc b/source/blender/compositor/nodes/COM_SwitchViewNode.cc index ed9e311f1b4..9507b35b4ca 100644 --- a/source/blender/compositor/nodes/COM_SwitchViewNode.cc +++ b/source/blender/compositor/nodes/COM_SwitchViewNode.cc @@ -15,7 +15,7 @@ void SwitchViewNode::convert_to_operations(NodeConverter &converter, { NodeOperationOutput *result; const char *view_name = context.get_view_name(); - bNode *bnode = this->get_bnode(); + const bNode *bnode = this->get_bnode(); /* get the internal index of the socket with a matching name */ int nr = BLI_findstringindex(&bnode->inputs, view_name, offsetof(bNodeSocket, name)); diff --git a/source/blender/compositor/nodes/COM_TextureNode.cc b/source/blender/compositor/nodes/COM_TextureNode.cc index be5f7b90e11..44d4a41fcec 100644 --- a/source/blender/compositor/nodes/COM_TextureNode.cc +++ b/source/blender/compositor/nodes/COM_TextureNode.cc @@ -14,7 +14,7 @@ TextureNode::TextureNode(bNode *editor_node) : Node(editor_node) void TextureNode::convert_to_operations(NodeConverter &converter, const CompositorContext &context) const { - bNode *editor_node = this->get_bnode(); + const bNode *editor_node = this->get_bnode(); Tex *texture = (Tex *)editor_node->id; TextureOperation *operation = new TextureOperation(); bool scene_color_manage = !STREQ(context.get_scene()->display_settings.display_device, "None"); diff --git a/source/blender/compositor/nodes/COM_TimeNode.cc b/source/blender/compositor/nodes/COM_TimeNode.cc index b3dbd74b795..4f4f6f7bf8a 100644 --- a/source/blender/compositor/nodes/COM_TimeNode.cc +++ b/source/blender/compositor/nodes/COM_TimeNode.cc @@ -18,7 +18,7 @@ void TimeNode::convert_to_operations(NodeConverter &converter, const CompositorContext &context) const { SetValueOperation *operation = new SetValueOperation(); - bNode *node = this->get_bnode(); + const bNode *node = this->get_bnode(); /* stack order output: fac */ float fac = 0.0f; diff --git a/source/blender/compositor/nodes/COM_TonemapNode.cc b/source/blender/compositor/nodes/COM_TonemapNode.cc index d20b9698163..f08798db19c 100644 --- a/source/blender/compositor/nodes/COM_TonemapNode.cc +++ b/source/blender/compositor/nodes/COM_TonemapNode.cc @@ -14,7 +14,7 @@ TonemapNode::TonemapNode(bNode *editor_node) : Node(editor_node) void TonemapNode::convert_to_operations(NodeConverter &converter, const CompositorContext & /*context*/) const { - NodeTonemap *data = (NodeTonemap *)this->get_bnode()->storage; + const NodeTonemap *data = (const NodeTonemap *)this->get_bnode()->storage; TonemapOperation *operation = data->type == 1 ? new PhotoreceptorTonemapOperation() : new TonemapOperation(); diff --git a/source/blender/compositor/nodes/COM_TrackPositionNode.cc b/source/blender/compositor/nodes/COM_TrackPositionNode.cc index 1143ce81c80..da12f72b451 100644 --- a/source/blender/compositor/nodes/COM_TrackPositionNode.cc +++ b/source/blender/compositor/nodes/COM_TrackPositionNode.cc @@ -40,7 +40,7 @@ static TrackPositionOperation *create_motion_operation(NodeConverter &converter, void TrackPositionNode::convert_to_operations(NodeConverter &converter, const CompositorContext &context) const { - bNode *editor_node = this->get_bnode(); + const bNode *editor_node = this->get_bnode(); MovieClip *clip = (MovieClip *)editor_node->id; NodeTrackPosData *trackpos_data = (NodeTrackPosData *)editor_node->storage; diff --git a/source/blender/compositor/nodes/COM_TranslateNode.cc b/source/blender/compositor/nodes/COM_TranslateNode.cc index 50c5b4c0d2c..8c53d773ad2 100644 --- a/source/blender/compositor/nodes/COM_TranslateNode.cc +++ b/source/blender/compositor/nodes/COM_TranslateNode.cc @@ -17,8 +17,8 @@ TranslateNode::TranslateNode(bNode *editor_node) : Node(editor_node) void TranslateNode::convert_to_operations(NodeConverter &converter, const CompositorContext &context) const { - bNode *bnode = this->get_bnode(); - NodeTranslateData *data = (NodeTranslateData *)bnode->storage; + const bNode *bnode = this->get_bnode(); + const NodeTranslateData *data = (const NodeTranslateData *)bnode->storage; NodeInput *input_socket = this->get_input_socket(0); NodeInput *input_xsocket = this->get_input_socket(1); diff --git a/source/blender/compositor/nodes/COM_VectorBlurNode.cc b/source/blender/compositor/nodes/COM_VectorBlurNode.cc index f8a2a90ca81..90b8d5a2012 100644 --- a/source/blender/compositor/nodes/COM_VectorBlurNode.cc +++ b/source/blender/compositor/nodes/COM_VectorBlurNode.cc @@ -14,8 +14,8 @@ VectorBlurNode::VectorBlurNode(bNode *editor_node) : Node(editor_node) void VectorBlurNode::convert_to_operations(NodeConverter &converter, const CompositorContext &context) const { - bNode *node = this->get_bnode(); - NodeBlurData *vector_blur_settings = (NodeBlurData *)node->storage; + const bNode *node = this->get_bnode(); + const NodeBlurData *vector_blur_settings = (const NodeBlurData *)node->storage; VectorBlurOperation *operation = new VectorBlurOperation(); operation->set_vector_blur_settings(vector_blur_settings); diff --git a/source/blender/compositor/nodes/COM_VectorCurveNode.cc b/source/blender/compositor/nodes/COM_VectorCurveNode.cc index c0871ab21e1..d42d2831bc9 100644 --- a/source/blender/compositor/nodes/COM_VectorCurveNode.cc +++ b/source/blender/compositor/nodes/COM_VectorCurveNode.cc @@ -15,7 +15,7 @@ void VectorCurveNode::convert_to_operations(NodeConverter &converter, const CompositorContext & /*context*/) const { VectorCurveOperation *operation = new VectorCurveOperation(); - operation->set_curve_mapping((CurveMapping *)this->get_bnode()->storage); + operation->set_curve_mapping((const CurveMapping *)this->get_bnode()->storage); converter.add_operation(operation); converter.map_input_socket(get_input_socket(0), operation->get_input_socket(0)); diff --git a/source/blender/compositor/nodes/COM_ViewerNode.cc b/source/blender/compositor/nodes/COM_ViewerNode.cc index ebef331c62f..47056bcd8c4 100644 --- a/source/blender/compositor/nodes/COM_ViewerNode.cc +++ b/source/blender/compositor/nodes/COM_ViewerNode.cc @@ -15,7 +15,7 @@ ViewerNode::ViewerNode(bNode *editor_node) : Node(editor_node) void ViewerNode::convert_to_operations(NodeConverter &converter, const CompositorContext &context) const { - bNode *editor_node = this->get_bnode(); + const bNode *editor_node = this->get_bnode(); bool do_output = (editor_node->flag & NODE_DO_OUTPUT_RECALC || context.is_rendering()) && (editor_node->flag & NODE_DO_OUTPUT); bool ignore_alpha = (editor_node->custom2 & CMP_NODE_OUTPUT_IGNORE_ALPHA) != 0; diff --git a/source/blender/compositor/operations/COM_BokehImageOperation.h b/source/blender/compositor/operations/COM_BokehImageOperation.h index 751f1ad8d8e..28506ba36b5 100644 --- a/source/blender/compositor/operations/COM_BokehImageOperation.h +++ b/source/blender/compositor/operations/COM_BokehImageOperation.h @@ -39,7 +39,7 @@ class BokehImageOperation : public MultiThreadedOperation { /** * \brief Settings of the bokeh image */ - NodeBokehImage *data_; + const NodeBokehImage *data_; /** * \brief precalculate center of the image @@ -119,7 +119,7 @@ class BokehImageOperation : public MultiThreadedOperation { * \brief set the node data * \param data: */ - void set_data(NodeBokehImage *data) + void set_data(const NodeBokehImage *data) { data_ = data; } diff --git a/source/blender/compositor/operations/COM_BoxMaskOperation.h b/source/blender/compositor/operations/COM_BoxMaskOperation.h index 9f18d110f3c..7e976cab6b6 100644 --- a/source/blender/compositor/operations/COM_BoxMaskOperation.h +++ b/source/blender/compositor/operations/COM_BoxMaskOperation.h @@ -22,7 +22,7 @@ class BoxMaskOperation : public MultiThreadedOperation { float aspect_ratio_; int mask_type_; - NodeBoxMask *data_; + const NodeBoxMask *data_; public: BoxMaskOperation(); @@ -42,7 +42,7 @@ class BoxMaskOperation : public MultiThreadedOperation { */ void deinit_execution() override; - void set_data(NodeBoxMask *data) + void set_data(const NodeBoxMask *data) { data_ = data; } diff --git a/source/blender/compositor/operations/COM_CompositorOperation.cc b/source/blender/compositor/operations/COM_CompositorOperation.cc index e8b61ff229e..c0cc3fa1ba4 100644 --- a/source/blender/compositor/operations/COM_CompositorOperation.cc +++ b/source/blender/compositor/operations/COM_CompositorOperation.cc @@ -5,6 +5,7 @@ #include "BKE_global.h" #include "BKE_image.h" +#include "BKE_scene.h" #include "RE_pipeline.h" @@ -164,8 +165,8 @@ void CompositorOperation::execute_region(rcti *rect, unsigned int /*tile_number* * Full frame */ - int full_width = rd->xsch * rd->size / 100; - int full_height = rd->ysch * rd->size / 100; + int full_width, full_height; + BKE_render_resolution(rd, false, &full_width, &full_height); dx = rd->border.xmin * full_width - (full_width - this->get_width()) / 2.0f; dy = rd->border.ymin * full_height - (full_height - this->get_height()) / 2.0f; @@ -214,8 +215,8 @@ void CompositorOperation::update_memory_buffer_partial(MemoryBuffer *UNUSED(outp void CompositorOperation::determine_canvas(const rcti &UNUSED(preferred_area), rcti &r_area) { - int width = rd_->xsch * rd_->size / 100; - int height = rd_->ysch * rd_->size / 100; + int width, height; + BKE_render_resolution(rd_, false, &width, &height); /* Check actual render resolution with cropping it may differ with cropped border.rendering * Fix for T31777 Border Crop gives black (easy). */ diff --git a/source/blender/compositor/operations/COM_CurveBaseOperation.cc b/source/blender/compositor/operations/COM_CurveBaseOperation.cc index e92a2f08ed4..e7cbf0d28d5 100644 --- a/source/blender/compositor/operations/COM_CurveBaseOperation.cc +++ b/source/blender/compositor/operations/COM_CurveBaseOperation.cc @@ -33,7 +33,7 @@ void CurveBaseOperation::deinit_execution() } } -void CurveBaseOperation::set_curve_mapping(CurveMapping *mapping) +void CurveBaseOperation::set_curve_mapping(const CurveMapping *mapping) { /* duplicate the curve to avoid glitches while drawing, see bug T32374. */ if (curve_mapping_) { diff --git a/source/blender/compositor/operations/COM_CurveBaseOperation.h b/source/blender/compositor/operations/COM_CurveBaseOperation.h index d30c64e3282..4a745c34f24 100644 --- a/source/blender/compositor/operations/COM_CurveBaseOperation.h +++ b/source/blender/compositor/operations/COM_CurveBaseOperation.h @@ -26,7 +26,7 @@ class CurveBaseOperation : public MultiThreadedOperation { void init_execution() override; void deinit_execution() override; - void set_curve_mapping(CurveMapping *mapping); + void set_curve_mapping(const CurveMapping *mapping); }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_DenoiseOperation.cc b/source/blender/compositor/operations/COM_DenoiseOperation.cc index 1a199ba2eab..3f32eced0f8 100644 --- a/source/blender/compositor/operations/COM_DenoiseOperation.cc +++ b/source/blender/compositor/operations/COM_DenoiseOperation.cc @@ -163,7 +163,7 @@ void DenoiseOperation::deinit_execution() SingleThreadedOperation::deinit_execution(); } -static bool are_guiding_passes_noise_free(NodeDenoise *settings) +static bool are_guiding_passes_noise_free(const NodeDenoise *settings) { switch (settings->prefilter) { case CMP_NODE_DENOISE_PREFILTER_NONE: @@ -201,7 +201,7 @@ void DenoiseOperation::generate_denoise(MemoryBuffer *output, MemoryBuffer *input_color, MemoryBuffer *input_normal, MemoryBuffer *input_albedo, - NodeDenoise *settings) + const NodeDenoise *settings) { BLI_assert(input_color->get_buffer()); if (!input_color->get_buffer()) { diff --git a/source/blender/compositor/operations/COM_DenoiseOperation.h b/source/blender/compositor/operations/COM_DenoiseOperation.h index 2b170bb9bb7..709b8843b93 100644 --- a/source/blender/compositor/operations/COM_DenoiseOperation.h +++ b/source/blender/compositor/operations/COM_DenoiseOperation.h @@ -37,7 +37,7 @@ class DenoiseOperation : public DenoiseBaseOperation { /** * \brief settings of the denoise node. */ - NodeDenoise *settings_; + const NodeDenoise *settings_; public: DenoiseOperation(); @@ -51,7 +51,7 @@ class DenoiseOperation : public DenoiseBaseOperation { */ void deinit_execution() override; - void set_denoise_settings(NodeDenoise *settings) + void set_denoise_settings(const NodeDenoise *settings) { settings_ = settings; } @@ -66,7 +66,7 @@ class DenoiseOperation : public DenoiseBaseOperation { MemoryBuffer *input_color, MemoryBuffer *input_normal, MemoryBuffer *input_albedo, - NodeDenoise *settings); + const NodeDenoise *settings); MemoryBuffer *create_memory_buffer(rcti *rect) override; }; diff --git a/source/blender/compositor/operations/COM_DirectionalBlurOperation.h b/source/blender/compositor/operations/COM_DirectionalBlurOperation.h index e209bac2305..4e0b072dec9 100644 --- a/source/blender/compositor/operations/COM_DirectionalBlurOperation.h +++ b/source/blender/compositor/operations/COM_DirectionalBlurOperation.h @@ -11,7 +11,7 @@ namespace blender::compositor { class DirectionalBlurOperation : public MultiThreadedOperation, public QualityStepHelper { private: SocketReader *input_program_; - NodeDBlurData *data_; + const NodeDBlurData *data_; float center_x_pix_, center_y_pix_; float tx_, ty_; @@ -39,7 +39,7 @@ class DirectionalBlurOperation : public MultiThreadedOperation, public QualitySt ReadBufferOperation *read_operation, rcti *output) override; - void set_data(NodeDBlurData *data) + void set_data(const NodeDBlurData *data) { data_ = data; } diff --git a/source/blender/compositor/operations/COM_DistanceRGBMatteOperation.h b/source/blender/compositor/operations/COM_DistanceRGBMatteOperation.h index 2cb21be43ad..7b1a38618fd 100644 --- a/source/blender/compositor/operations/COM_DistanceRGBMatteOperation.h +++ b/source/blender/compositor/operations/COM_DistanceRGBMatteOperation.h @@ -13,7 +13,7 @@ namespace blender::compositor { */ class DistanceRGBMatteOperation : public MultiThreadedOperation { protected: - NodeChroma *settings_; + const NodeChroma *settings_; SocketReader *input_image_program_; SocketReader *input_key_program_; @@ -33,7 +33,7 @@ class DistanceRGBMatteOperation : public MultiThreadedOperation { void init_execution() override; void deinit_execution() override; - void set_settings(NodeChroma *node_chroma) + void set_settings(const NodeChroma *node_chroma) { settings_ = node_chroma; } diff --git a/source/blender/compositor/operations/COM_GlareBaseOperation.h b/source/blender/compositor/operations/COM_GlareBaseOperation.h index 4750086a119..0a2c5507a76 100644 --- a/source/blender/compositor/operations/COM_GlareBaseOperation.h +++ b/source/blender/compositor/operations/COM_GlareBaseOperation.h @@ -32,7 +32,7 @@ class GlareBaseOperation : public SingleThreadedOperation { /** * \brief settings of the glare node. */ - NodeGlare *settings_; + const NodeGlare *settings_; bool is_output_rendered_; @@ -47,7 +47,7 @@ class GlareBaseOperation : public SingleThreadedOperation { */ void deinit_execution() override; - void set_glare_settings(NodeGlare *settings) + void set_glare_settings(const NodeGlare *settings) { settings_ = settings; } @@ -64,7 +64,9 @@ class GlareBaseOperation : public SingleThreadedOperation { protected: GlareBaseOperation(); - virtual void generate_glare(float *data, MemoryBuffer *input_tile, NodeGlare *settings) = 0; + virtual void generate_glare(float *data, + MemoryBuffer *input_tile, + const NodeGlare *settings) = 0; MemoryBuffer *create_memory_buffer(rcti *rect) override; }; diff --git a/source/blender/compositor/operations/COM_GlareFogGlowOperation.cc b/source/blender/compositor/operations/COM_GlareFogGlowOperation.cc index 8b52123b2f0..ade3f11a8b3 100644 --- a/source/blender/compositor/operations/COM_GlareFogGlowOperation.cc +++ b/source/blender/compositor/operations/COM_GlareFogGlowOperation.cc @@ -391,7 +391,7 @@ static void convolve(float *dst, MemoryBuffer *in1, MemoryBuffer *in2) void GlareFogGlowOperation::generate_glare(float *data, MemoryBuffer *input_tile, - NodeGlare *settings) + const NodeGlare *settings) { int x, y; float scale, u, v, r, w, d; diff --git a/source/blender/compositor/operations/COM_GlareFogGlowOperation.h b/source/blender/compositor/operations/COM_GlareFogGlowOperation.h index 5e19f2ab2aa..2a74aeef048 100644 --- a/source/blender/compositor/operations/COM_GlareFogGlowOperation.h +++ b/source/blender/compositor/operations/COM_GlareFogGlowOperation.h @@ -16,7 +16,7 @@ class GlareFogGlowOperation : public GlareBaseOperation { } protected: - void generate_glare(float *data, MemoryBuffer *input_tile, NodeGlare *settings) override; + void generate_glare(float *data, MemoryBuffer *input_tile, const NodeGlare *settings) override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_GlareGhostOperation.cc b/source/blender/compositor/operations/COM_GlareGhostOperation.cc index e863f74d98a..13b7af2329e 100644 --- a/source/blender/compositor/operations/COM_GlareGhostOperation.cc +++ b/source/blender/compositor/operations/COM_GlareGhostOperation.cc @@ -20,7 +20,7 @@ static float smooth_mask(float x, float y) void GlareGhostOperation::generate_glare(float *data, MemoryBuffer *input_tile, - NodeGlare *settings) + const NodeGlare *settings) { const int qt = 1 << settings->quality; const float s1 = 4.0f / (float)qt, s2 = 2.0f * s1; diff --git a/source/blender/compositor/operations/COM_GlareGhostOperation.h b/source/blender/compositor/operations/COM_GlareGhostOperation.h index 644c2975676..db79358034a 100644 --- a/source/blender/compositor/operations/COM_GlareGhostOperation.h +++ b/source/blender/compositor/operations/COM_GlareGhostOperation.h @@ -16,7 +16,7 @@ class GlareGhostOperation : public GlareBaseOperation { } protected: - void generate_glare(float *data, MemoryBuffer *input_tile, NodeGlare *settings) override; + void generate_glare(float *data, MemoryBuffer *input_tile, const NodeGlare *settings) override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_GlareSimpleStarOperation.cc b/source/blender/compositor/operations/COM_GlareSimpleStarOperation.cc index a656eb3c706..69182b56bee 100644 --- a/source/blender/compositor/operations/COM_GlareSimpleStarOperation.cc +++ b/source/blender/compositor/operations/COM_GlareSimpleStarOperation.cc @@ -7,7 +7,7 @@ namespace blender::compositor { void GlareSimpleStarOperation::generate_glare(float *data, MemoryBuffer *input_tile, - NodeGlare *settings) + const NodeGlare *settings) { int i, x, y, ym, yp, xm, xp; float c[4] = {0, 0, 0, 0}, tc[4] = {0, 0, 0, 0}; diff --git a/source/blender/compositor/operations/COM_GlareSimpleStarOperation.h b/source/blender/compositor/operations/COM_GlareSimpleStarOperation.h index 3c2d2fe2d0f..470af780eb2 100644 --- a/source/blender/compositor/operations/COM_GlareSimpleStarOperation.h +++ b/source/blender/compositor/operations/COM_GlareSimpleStarOperation.h @@ -16,7 +16,7 @@ class GlareSimpleStarOperation : public GlareBaseOperation { } protected: - void generate_glare(float *data, MemoryBuffer *input_tile, NodeGlare *settings) override; + void generate_glare(float *data, MemoryBuffer *input_tile, const NodeGlare *settings) override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_GlareStreaksOperation.cc b/source/blender/compositor/operations/COM_GlareStreaksOperation.cc index 233eb8caf38..e4f06eb0e50 100644 --- a/source/blender/compositor/operations/COM_GlareStreaksOperation.cc +++ b/source/blender/compositor/operations/COM_GlareStreaksOperation.cc @@ -7,7 +7,7 @@ namespace blender::compositor { void GlareStreaksOperation::generate_glare(float *data, MemoryBuffer *input_tile, - NodeGlare *settings) + const NodeGlare *settings) { int x, y, n; unsigned int nump = 0; diff --git a/source/blender/compositor/operations/COM_GlareStreaksOperation.h b/source/blender/compositor/operations/COM_GlareStreaksOperation.h index 09ef30c339d..56afe3d8462 100644 --- a/source/blender/compositor/operations/COM_GlareStreaksOperation.h +++ b/source/blender/compositor/operations/COM_GlareStreaksOperation.h @@ -16,7 +16,7 @@ class GlareStreaksOperation : public GlareBaseOperation { } protected: - void generate_glare(float *data, MemoryBuffer *input_tile, NodeGlare *settings) override; + void generate_glare(float *data, MemoryBuffer *input_tile, const NodeGlare *settings) override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_GlareThresholdOperation.h b/source/blender/compositor/operations/COM_GlareThresholdOperation.h index 90870e3cca9..7930e32eda7 100644 --- a/source/blender/compositor/operations/COM_GlareThresholdOperation.h +++ b/source/blender/compositor/operations/COM_GlareThresholdOperation.h @@ -18,7 +18,7 @@ class GlareThresholdOperation : public MultiThreadedOperation { /** * \brief settings of the glare node. */ - NodeGlare *settings_; + const NodeGlare *settings_; public: GlareThresholdOperation(); @@ -38,7 +38,7 @@ class GlareThresholdOperation : public MultiThreadedOperation { */ void deinit_execution() override; - void set_glare_settings(NodeGlare *settings) + void set_glare_settings(const NodeGlare *settings) { settings_ = settings; } diff --git a/source/blender/compositor/operations/COM_MapValueOperation.cc b/source/blender/compositor/operations/COM_MapValueOperation.cc index 1353e0391a3..f55d394baa8 100644 --- a/source/blender/compositor/operations/COM_MapValueOperation.cc +++ b/source/blender/compositor/operations/COM_MapValueOperation.cc @@ -25,7 +25,7 @@ void MapValueOperation::execute_pixel_sampled(float output[4], { float src[4]; input_operation_->read_sampled(src, x, y, sampler); - TexMapping *texmap = settings_; + const TexMapping *texmap = settings_; float value = (src[0] + texmap->loc[0]) * texmap->size[0]; if (texmap->flag & TEXMAP_CLIP_MIN) { if (value < texmap->min[0]) { @@ -52,7 +52,7 @@ void MapValueOperation::update_memory_buffer_partial(MemoryBuffer *output, { for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) { const float input = *it.in(0); - TexMapping *texmap = settings_; + const TexMapping *texmap = settings_; float value = (input + texmap->loc[0]) * texmap->size[0]; if (texmap->flag & TEXMAP_CLIP_MIN) { if (value < texmap->min[0]) { diff --git a/source/blender/compositor/operations/COM_MapValueOperation.h b/source/blender/compositor/operations/COM_MapValueOperation.h index 4c384e52c95..5c1f425c5bd 100644 --- a/source/blender/compositor/operations/COM_MapValueOperation.h +++ b/source/blender/compositor/operations/COM_MapValueOperation.h @@ -18,7 +18,7 @@ class MapValueOperation : public MultiThreadedOperation { * Cached reference to the input_program */ SocketReader *input_operation_; - TexMapping *settings_; + const TexMapping *settings_; public: /** @@ -44,7 +44,7 @@ class MapValueOperation : public MultiThreadedOperation { /** * \brief set the TexMapping settings */ - void set_settings(TexMapping *settings) + void set_settings(const TexMapping *settings) { settings_ = settings; } diff --git a/source/blender/compositor/operations/COM_MovieClipOperation.cc b/source/blender/compositor/operations/COM_MovieClipOperation.cc index d7cf41cf422..b62d972e807 100644 --- a/source/blender/compositor/operations/COM_MovieClipOperation.cc +++ b/source/blender/compositor/operations/COM_MovieClipOperation.cc @@ -74,7 +74,7 @@ void MovieClipBaseOperation::execute_pixel_sampled(float output[4], zero_v4(output); } else if (ibuf->rect == nullptr && ibuf->rect_float == nullptr) { - /* Happens for multilayer exr, i.e. */ + /* Happens for multi-layer EXR, i.e. */ zero_v4(output); } else { diff --git a/source/blender/compositor/operations/COM_OutputFileMultiViewOperation.cc b/source/blender/compositor/operations/COM_OutputFileMultiViewOperation.cc index 341541b4cdd..760ed94f882 100644 --- a/source/blender/compositor/operations/COM_OutputFileMultiViewOperation.cc +++ b/source/blender/compositor/operations/COM_OutputFileMultiViewOperation.cc @@ -21,7 +21,7 @@ OutputOpenExrSingleLayerMultiViewOperation::OutputOpenExrSingleLayerMultiViewOpe const RenderData *rd, const bNodeTree *tree, DataType datatype, - ImageFormatData *format, + const ImageFormatData *format, const char *path, const char *view_name, const bool save_as_render) @@ -243,7 +243,7 @@ OutputStereoOperation::OutputStereoOperation(const Scene *scene, const RenderData *rd, const bNodeTree *tree, DataType datatype, - ImageFormatData *format, + const ImageFormatData *format, const char *path, const char *name, const char *view_name, diff --git a/source/blender/compositor/operations/COM_OutputFileMultiViewOperation.h b/source/blender/compositor/operations/COM_OutputFileMultiViewOperation.h index 69f4011d340..e36999e5cf1 100644 --- a/source/blender/compositor/operations/COM_OutputFileMultiViewOperation.h +++ b/source/blender/compositor/operations/COM_OutputFileMultiViewOperation.h @@ -22,7 +22,7 @@ class OutputOpenExrSingleLayerMultiViewOperation : public OutputSingleLayerOpera const RenderData *rd, const bNodeTree *tree, DataType datatype, - ImageFormatData *format, + const ImageFormatData *format, const char *path, const char *view_name, bool save_as_render); @@ -57,7 +57,7 @@ class OutputStereoOperation : public OutputSingleLayerOperation { const RenderData *rd, const bNodeTree *tree, DataType datatype, - struct ImageFormatData *format, + const struct ImageFormatData *format, const char *path, const char *name, const char *view_name, diff --git a/source/blender/compositor/operations/COM_OutputFileOperation.cc b/source/blender/compositor/operations/COM_OutputFileOperation.cc index 49de275c256..1d22f3e8cd2 100644 --- a/source/blender/compositor/operations/COM_OutputFileOperation.cc +++ b/source/blender/compositor/operations/COM_OutputFileOperation.cc @@ -204,7 +204,7 @@ OutputSingleLayerOperation::OutputSingleLayerOperation(const Scene *scene, const RenderData *rd, const bNodeTree *tree, DataType datatype, - ImageFormatData *format, + const ImageFormatData *format, const char *path, const char *view_name, const bool save_as_render) diff --git a/source/blender/compositor/operations/COM_OutputFileOperation.h b/source/blender/compositor/operations/COM_OutputFileOperation.h index 875defe00e9..df1d68838d9 100644 --- a/source/blender/compositor/operations/COM_OutputFileOperation.h +++ b/source/blender/compositor/operations/COM_OutputFileOperation.h @@ -35,7 +35,7 @@ class OutputSingleLayerOperation : public MultiThreadedOperation { const RenderData *rd, const bNodeTree *tree, DataType datatype, - ImageFormatData *format, + const ImageFormatData *format, const char *path, const char *view_name, bool save_as_render); diff --git a/source/blender/compositor/operations/COM_PlaneDistortCommonOperation.cc b/source/blender/compositor/operations/COM_PlaneDistortCommonOperation.cc index 8bf8e339697..9c19f55e04f 100644 --- a/source/blender/compositor/operations/COM_PlaneDistortCommonOperation.cc +++ b/source/blender/compositor/operations/COM_PlaneDistortCommonOperation.cc @@ -43,6 +43,11 @@ BLI_INLINE void warp_coord(float x, float y, float matrix[3][3], float uv[2], fl uv[0] = vec[0] / vec[2]; uv[1] = vec[1] / vec[2]; + /* Offset so that pixel center corresponds to a (0.5, 0.5), which helps keeping transformed + * image sharp. */ + uv[0] += 0.5f; + uv[1] += 0.5f; + deriv[0][0] = (matrix[0][0] - matrix[0][2] * uv[0]) / vec[2]; deriv[1][0] = (matrix[0][1] - matrix[0][2] * uv[1]) / vec[2]; deriv[0][1] = (matrix[1][0] - matrix[1][2] * uv[0]) / vec[2]; @@ -66,9 +71,18 @@ void PlaneDistortWarpImageOperation::calculate_corners(const float corners[4][2] const NodeOperation *image = get_input_operation(0); const int width = image->get_width(); const int height = image->get_height(); + + MotionSample *sample_data = &samples_[sample]; + + /* If the image which is to be warped empty assume unit transform and don't attempt to calculate + * actual homography (otherwise homography solver will attempt to deal with singularity). */ + if (width == 0 || height == 0) { + unit_m3(sample_data->perspective_matrix); + return; + } + float frame_corners[4][2] = { {0.0f, 0.0f}, {(float)width, 0.0f}, {(float)width, (float)height}, {0.0f, (float)height}}; - MotionSample *sample_data = &samples_[sample]; BKE_tracking_homography_between_two_quads( sample_data->frame_space_corners, frame_corners, sample_data->perspective_matrix); } diff --git a/source/blender/compositor/operations/COM_RenderLayersProg.cc b/source/blender/compositor/operations/COM_RenderLayersProg.cc index 522086658f8..40f2187b27b 100644 --- a/source/blender/compositor/operations/COM_RenderLayersProg.cc +++ b/source/blender/compositor/operations/COM_RenderLayersProg.cc @@ -109,8 +109,8 @@ void RenderLayersProg::execute_pixel_sampled(float output[4], /* see comment in execute_region describing coordinate mapping, * here it simply goes other way around */ - int full_width = rd->xsch * rd->size / 100; - int full_height = rd->ysch * rd->size / 100; + int full_width, full_height; + BKE_render_resolution(rd, false, &full_width, &full_height); dx = rd->border.xmin * full_width - (full_width - this->get_width()) / 2.0f; dy = rd->border.ymin * full_height - (full_height - this->get_height()) / 2.0f; diff --git a/source/blender/compositor/operations/COM_SMAAOperation.cc b/source/blender/compositor/operations/COM_SMAAOperation.cc index 11e51e81ef0..261426b31e2 100644 --- a/source/blender/compositor/operations/COM_SMAAOperation.cc +++ b/source/blender/compositor/operations/COM_SMAAOperation.cc @@ -12,7 +12,7 @@ extern "C" { namespace blender::compositor { /* - * An implementation of Enhanced Subpixel Morphological Antialiasing (SMAA) + * An implementation of Enhanced Sub-pixel Morphological Anti-aliasing (SMAA) * * The algorithm was proposed by: * Jorge Jimenez, Jose I. Echevarria, Tiago Sousa, Diego Gutierrez diff --git a/source/blender/compositor/operations/COM_ScaleOperation.cc b/source/blender/compositor/operations/COM_ScaleOperation.cc index 1957c5eb5fc..2a2aff31893 100644 --- a/source/blender/compositor/operations/COM_ScaleOperation.cc +++ b/source/blender/compositor/operations/COM_ScaleOperation.cc @@ -7,7 +7,7 @@ namespace blender::compositor { #define USE_FORCE_BILINEAR -/* XXX(campbell): ignore input and use default from old compositor, +/* XXX(@campbellbarton): ignore input and use default from old compositor, * could become an option like the transform node. * * NOTE: use bilinear because bicubic makes fuzzy even when not scaling at all (1:1) diff --git a/source/blender/compositor/operations/COM_TextureOperation.cc b/source/blender/compositor/operations/COM_TextureOperation.cc index 8b27c3aded4..c1c8db2ae3f 100644 --- a/source/blender/compositor/operations/COM_TextureOperation.cc +++ b/source/blender/compositor/operations/COM_TextureOperation.cc @@ -6,6 +6,7 @@ #include "BKE_image.h" #include "BKE_node.h" +#include "BKE_scene.h" #include "NOD_texture.h" @@ -59,8 +60,8 @@ void TextureBaseOperation::determine_canvas(const rcti &preferred_area, rcti &r_ { r_area = preferred_area; if (BLI_rcti_is_empty(&preferred_area)) { - int width = rd_->xsch * rd_->size / 100; - int height = rd_->ysch * rd_->size / 100; + int width, height; + BKE_render_resolution(rd_, false, &width, &height); r_area.xmax = preferred_area.xmin + width; r_area.ymax = preferred_area.ymin + height; } diff --git a/source/blender/compositor/operations/COM_TonemapOperation.cc b/source/blender/compositor/operations/COM_TonemapOperation.cc index fa40cd36f4c..714625e483d 100644 --- a/source/blender/compositor/operations/COM_TonemapOperation.cc +++ b/source/blender/compositor/operations/COM_TonemapOperation.cc @@ -46,7 +46,7 @@ void TonemapOperation::execute_pixel(float output[4], int x, int y, void *data) void PhotoreceptorTonemapOperation::execute_pixel(float output[4], int x, int y, void *data) { AvgLogLum *avg = (AvgLogLum *)data; - NodeTonemap *ntm = data_; + const NodeTonemap *ntm = data_; const float f = expf(-data_->f); const float m = (ntm->m > 0.0f) ? ntm->m : (0.3f + 0.7f * powf(avg->auto_key, 1.4f)); @@ -233,7 +233,7 @@ void PhotoreceptorTonemapOperation::update_memory_buffer_partial(MemoryBuffer *o Span<MemoryBuffer *> inputs) { AvgLogLum *avg = cached_instance_; - NodeTonemap *ntm = data_; + const NodeTonemap *ntm = data_; const float f = expf(-data_->f); const float m = (ntm->m > 0.0f) ? ntm->m : (0.3f + 0.7f * powf(avg->auto_key, 1.4f)); const float ic = 1.0f - ntm->c; diff --git a/source/blender/compositor/operations/COM_TonemapOperation.h b/source/blender/compositor/operations/COM_TonemapOperation.h index 7868aa140dc..8071470b3f4 100644 --- a/source/blender/compositor/operations/COM_TonemapOperation.h +++ b/source/blender/compositor/operations/COM_TonemapOperation.h @@ -34,7 +34,7 @@ class TonemapOperation : public MultiThreadedOperation { /** * \brief settings of the Tonemap */ - NodeTonemap *data_; + const NodeTonemap *data_; /** * \brief temporarily cache of the execution storage @@ -62,7 +62,7 @@ class TonemapOperation : public MultiThreadedOperation { */ void deinit_execution() override; - void set_data(NodeTonemap *data) + void set_data(const NodeTonemap *data) { data_ = data; } diff --git a/source/blender/compositor/operations/COM_VectorBlurOperation.h b/source/blender/compositor/operations/COM_VectorBlurOperation.h index 9c83c0645c2..0c4d215b6d3 100644 --- a/source/blender/compositor/operations/COM_VectorBlurOperation.h +++ b/source/blender/compositor/operations/COM_VectorBlurOperation.h @@ -25,7 +25,7 @@ class VectorBlurOperation : public NodeOperation, public QualityStepHelper { /** * \brief settings of the glare node. */ - NodeBlurData *settings_; + const NodeBlurData *settings_; float *cached_instance_; @@ -49,7 +49,7 @@ class VectorBlurOperation : public NodeOperation, public QualityStepHelper { void *initialize_tile_data(rcti *rect) override; - void set_vector_blur_settings(NodeBlurData *settings) + void set_vector_blur_settings(const NodeBlurData *settings) { settings_ = settings; } diff --git a/source/blender/compositor/operations/COM_ViewerOperation.cc b/source/blender/compositor/operations/COM_ViewerOperation.cc index 58392b8a638..3bd5fa4ad14 100644 --- a/source/blender/compositor/operations/COM_ViewerOperation.cc +++ b/source/blender/compositor/operations/COM_ViewerOperation.cc @@ -104,8 +104,8 @@ void ViewerOperation::execute_region(rcti *rect, unsigned int /*tile_number*/) void ViewerOperation::determine_canvas(const rcti &preferred_area, rcti &r_area) { - const int scene_render_width = rd_->xsch * rd_->size / 100; - const int scene_render_height = rd_->ysch * rd_->size / 100; + int scene_render_width, scene_render_height; + BKE_render_resolution(rd_, false, &scene_render_width, &scene_render_height); rcti local_preferred = preferred_area; local_preferred.xmax = local_preferred.xmin + scene_render_width; @@ -156,7 +156,7 @@ void ViewerOperation::init_image() ibuf->y = display_height_; /* zero size can happen if no image buffers exist to define a sensible resolution */ if (ibuf->x > 0 && ibuf->y > 0) { - imb_addrectfloatImBuf(ibuf); + imb_addrectfloatImBuf(ibuf, 4); } ibuf->userflags |= IB_DISPLAY_BUFFER_INVALID; diff --git a/source/blender/compositor/realtime_compositor/CMakeLists.txt b/source/blender/compositor/realtime_compositor/CMakeLists.txt new file mode 100644 index 00000000000..1f1333332f5 --- /dev/null +++ b/source/blender/compositor/realtime_compositor/CMakeLists.txt @@ -0,0 +1,66 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +set(INC + . + ../../blenkernel + ../../blenlib + ../../gpu + ../../imbuf + ../../makesdna + ../../makesrna + ../../nodes + ../../gpu/intern + ../../../../intern/guardedalloc +) + + +set(SRC + intern/compile_state.cc + intern/context.cc + intern/conversion_operation.cc + intern/domain.cc + intern/evaluator.cc + intern/input_single_value_operation.cc + intern/node_operation.cc + intern/operation.cc + intern/realize_on_domain_operation.cc + intern/reduce_to_single_value_operation.cc + intern/result.cc + intern/scheduler.cc + intern/shader_node.cc + intern/shader_operation.cc + intern/simple_operation.cc + intern/static_shader_manager.cc + intern/texture_pool.cc + intern/utilities.cc + + COM_compile_state.hh + COM_context.hh + COM_conversion_operation.hh + COM_domain.hh + COM_evaluator.hh + COM_input_descriptor.hh + COM_input_single_value_operation.hh + COM_node_operation.hh + COM_operation.hh + COM_realize_on_domain_operation.hh + COM_reduce_to_single_value_operation.hh + COM_result.hh + COM_scheduler.hh + COM_shader_node.hh + COM_shader_operation.hh + COM_simple_operation.hh + COM_static_shader_manager.hh + COM_texture_pool.hh + COM_utilities.hh +) + +set(LIB + bf_gpu + bf_nodes + bf_imbuf + bf_blenlib + bf_blenkernel +) + +blender_add_lib(bf_realtime_compositor "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") diff --git a/source/blender/compositor/realtime_compositor/COM_compile_state.hh b/source/blender/compositor/realtime_compositor/COM_compile_state.hh new file mode 100644 index 00000000000..924919bbef6 --- /dev/null +++ b/source/blender/compositor/realtime_compositor/COM_compile_state.hh @@ -0,0 +1,170 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#include "BLI_map.hh" + +#include "NOD_derived_node_tree.hh" + +#include "COM_domain.hh" +#include "COM_node_operation.hh" +#include "COM_scheduler.hh" +#include "COM_shader_operation.hh" + +namespace blender::realtime_compositor { + +using namespace nodes::derived_node_tree_types; + +/* ------------------------------------------------------------------------------------------------ + * Compile State + * + * The compile state is a utility class used to track the state of compilation when compiling the + * node tree. In particular, it tracks two important pieces of information, each of which is + * described in one of the following sections. + * + * First, it stores a mapping between all nodes and the operations they were compiled into. The + * mapping are stored independently depending on the type of the operation in the node_operations_ + * and shader_operations_ maps. So those two maps are mutually exclusive. The compiler should call + * the map_node_to_node_operation and map_node_to_shader_operation methods to populate those maps + * as soon as it compiles a node or multiple nodes into an operation. Those maps are used to + * retrieve the results of outputs linked to the inputs of operations. For more details, see the + * get_result_from_output_socket method. For the node tree shown below, nodes 1, 2, and 6 are + * mapped to their compiled operations in the node_operation_ map. While nodes 3 and 4 are both + * mapped to the first shader operation, and node 5 is mapped to the second shader operation in the + * shader_operations_ map. + * + * Shader Operation 1 Shader Operation 2 + * +-----------------------------------+ +------------------+ + * .------------. | .------------. .------------. | | .------------. | .------------. + * | Node 1 | | | Node 3 | | Node 4 | | | | Node 5 | | | Node 6 | + * | |----|--| |--| |---|-----|--| |--|--| | + * | | .-|--| | | | | .--|--| | | | | + * '------------' | | '------------' '------------' | | | '------------' | '------------' + * | +-----------------------------------+ | +------------------+ + * .------------. | | + * | Node 2 | | | + * | |--'----------------------------------------' + * | | + * '------------' + * + * Second, it stores the shader compile unit as well as its domain. One should first go over the + * discussion in COM_evaluator.hh for a high level description of the mechanism of the compile + * unit. The one important detail in this class is the should_compile_shader_compile_unit method, + * which implements the criteria of whether the compile unit should be compiled given the node + * currently being processed as an argument. Those criteria are described as follows. If the + * compile unit is empty as is the case when processing nodes 1, 2, and 3, then it plainly + * shouldn't be compiled. If the given node is not a shader node, then it can't be added to the + * compile unit and the unit is considered complete and should be compiled, as is the case when + * processing node 6. If the computed domain of the given node is not compatible with the domain of + * the compiled unit, then it can't be added to the unit and the unit is considered complete and + * should be compiled, as is the case when processing node 5, more on this in the next section. + * Otherwise, the given node is compatible with the compile unit and can be added to it, so the + * unit shouldn't be compiled just yet, as is the case when processing node 4. + * + * Special attention should be given to the aforementioned domain compatibility criterion. One + * should first go over the discussion in COM_domain.hh for more information on domains. When a + * compile unit gets eventually compiled to a shader operation, that operation will have a certain + * operation domain, and any node that gets added to the compile unit should itself have a computed + * node domain that is compatible with that operation domain, otherwise, had the node been compiled + * into its own operation separately, the result would have been be different. For instance, + * consider the above node tree where node 1 outputs a 100x100 result, node 2 outputs a 50x50 + * result, the first input in node 3 has the highest domain priority, and the second input in node + * 5 has the highest domain priority. In this case, shader operation 1 will output a 100x100 + * result, and shader operation 2 will output a 50x50 result, because that's the computed operation + * domain for each of them. So node 6 will get a 50x50 result. Now consider the same node tree, but + * where all three nodes 3, 4, and 5 were compiled into a single shader operation as shown the node + * tree below. In that case, shader operation 1 will output a 100x100 result, because that's its + * computed operation domain. So node 6 will get a 100x100 result. As can be seen, the final result + * is different even though the node tree is the same. That's why the compiler can decide to + * compile the compile unit early even though further nodes can still be technically added to it. + * + * Shader Operation 1 + * +------------------------------------------------------+ + * .------------. | .------------. .------------. .------------. | .------------. + * | Node 1 | | | Node 3 | | Node 4 | | Node 5 | | | Node 6 | + * | |----|--| |--| |------| |--|--| | + * | | .-|--| | | | .---| | | | | + * '------------' | | '------------' '------------' | '------------' | '------------' + * | +----------------------------------|-------------------+ + * .------------. | | + * | Node 2 | | | + * | |--'------------------------------------' + * | | + * '------------' + * + * To check for the domain compatibility between the compile unit and the node being processed, + * the domain of the compile unit is assumed to be the domain of the first node whose computed + * domain is not an identity domain. Identity domains corresponds to single value results, so those + * are always compatible with any domain. The domain of the compile unit is computed and set in + * the add_node_to_shader_compile_unit method. When processing a node, the computed domain of node + * is compared to the compile unit domain in the should_compile_shader_compile_unit method, noting + * that identity domains are always compatible. Node domains are computed in the + * compute_shader_node_domain method, which is analogous to Operation::compute_domain for nodes + * that are not yet compiled. */ +class CompileState { + private: + /* A reference to the node execution schedule that is being compiled. */ + const Schedule &schedule_; + /* Those two maps associate each node with the operation it was compiled into. Each node is + * either compiled into a node operation and added to node_operations, or compiled into a shader + * operation and added to shader_operations. Those maps are used to retrieve the results of + * outputs linked to the inputs of operations. See the get_result_from_output_socket method for + * more information. */ + Map<DNode, NodeOperation *> node_operations_; + Map<DNode, ShaderOperation *> shader_operations_; + /* A contiguous subset of the node execution schedule that contains the group of nodes that will + * be compiled together into a Shader Operation. See the discussion in COM_evaluator.hh for + * more information. */ + ShaderCompileUnit shader_compile_unit_; + /* The domain of the shader compile unit. */ + Domain shader_compile_unit_domain_ = Domain::identity(); + + public: + /* Construct a compile state from the node execution schedule being compiled. */ + CompileState(const Schedule &schedule); + + /* Get a reference to the node execution schedule being compiled. */ + const Schedule &get_schedule(); + + /* Add an association between the given node and the give node operation that the node was + * compiled into in the node_operations_ map. */ + void map_node_to_node_operation(DNode node, NodeOperation *operation); + + /* Add an association between the given node and the give shader operation that the node was + * compiled into in the shader_operations_ map. */ + void map_node_to_shader_operation(DNode node, ShaderOperation *operation); + + /* Returns a reference to the result of the operation corresponding to the given output that the + * given output's node was compiled to. */ + Result &get_result_from_output_socket(DOutputSocket output); + + /* Add the given node to the compile unit. And if the domain of the compile unit is not yet + * determined or was determined to be an identity domain, update it to the computed domain for + * the give node. */ + void add_node_to_shader_compile_unit(DNode node); + + /* Get a reference to the shader compile unit. */ + ShaderCompileUnit &get_shader_compile_unit(); + + /* Clear the compile unit. This should be called once the compile unit is compiled to ready it to + * track the next potential compile unit. */ + void reset_shader_compile_unit(); + + /* Determines if the compile unit should be compiled based on a number of criteria give the node + * currently being processed. Those criteria are as follows: + * - If compile unit is empty, then it can't and shouldn't be compiled. + * - If the given node is not a shader node, then it can't be added to the compile unit + * and the unit is considered complete and should be compiled. + * - If the computed domain of the given node is not compatible with the domain of the compile + * unit, then it can't be added to it and the unit is considered complete and should be + * compiled. */ + bool should_compile_shader_compile_unit(DNode node); + + private: + /* Compute the node domain of the given shader node. This is analogous to the + * Operation::compute_domain method, except it is computed from the node itself as opposed to a + * compiled operation. See the discussion in COM_domain.hh for more information. */ + Domain compute_shader_node_domain(DNode node); +}; + +} // namespace blender::realtime_compositor diff --git a/source/blender/compositor/realtime_compositor/COM_context.hh b/source/blender/compositor/realtime_compositor/COM_context.hh new file mode 100644 index 00000000000..b5c8cea641f --- /dev/null +++ b/source/blender/compositor/realtime_compositor/COM_context.hh @@ -0,0 +1,72 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#include "BLI_math_vec_types.hh" +#include "BLI_string_ref.hh" + +#include "DNA_scene_types.h" + +#include "GPU_texture.h" + +#include "COM_static_shader_manager.hh" +#include "COM_texture_pool.hh" + +namespace blender::realtime_compositor { + +/* ------------------------------------------------------------------------------------------------ + * Context + * + * A Context is an abstract class that is implemented by the caller of the evaluator to provide the + * necessary data and functionalities for the correct operation of the evaluator. This includes + * providing input data like render passes and the active scene, as well as references to the data + * where the output of the evaluator will be written. The class also provides a reference to the + * texture pool which should be implemented by the caller and provided during construction. + * Finally, the class have an instance of a static shader manager for convenient shader + * acquisition. */ +class Context { + private: + /* A texture pool that can be used to allocate textures for the compositor efficiently. */ + TexturePool &texture_pool_; + /* A static shader manager that can be used to acquire shaders for the compositor efficiently. */ + StaticShaderManager shader_manager_; + + public: + Context(TexturePool &texture_pool); + + /* Get the active compositing scene. */ + virtual const Scene *get_scene() const = 0; + + /* Get the dimensions of the output. */ + virtual int2 get_output_size() = 0; + + /* Get the texture representing the output where the result of the compositor should be + * written. This should be called by output nodes to get their target texture. */ + virtual GPUTexture *get_output_texture() = 0; + + /* Get the texture where the given render pass is stored. This should be called by the Render + * Layer node to populate its outputs. */ + virtual GPUTexture *get_input_texture(int view_layer, eScenePassType pass_type) = 0; + + /* Get the name of the view currently being rendered. */ + virtual StringRef get_view_name() = 0; + + /* Set an info message. This is called by the compositor evaluator to inform or warn the user + * about something, typically an error. The implementation should display the message in an + * appropriate place, which can be directly in the UI or just logged to the output stream. */ + virtual void set_info_message(StringRef message) const = 0; + + /* Get the current frame number of the active scene. */ + int get_frame_number() const; + + /* Get the current time in seconds of the active scene. */ + float get_time() const; + + /* Get a reference to the texture pool of this context. */ + TexturePool &texture_pool(); + + /* Get a reference to the static shader manager of this context. */ + StaticShaderManager &shader_manager(); +}; + +} // namespace blender::realtime_compositor diff --git a/source/blender/compositor/realtime_compositor/COM_conversion_operation.hh b/source/blender/compositor/realtime_compositor/COM_conversion_operation.hh new file mode 100644 index 00000000000..310333aea5a --- /dev/null +++ b/source/blender/compositor/realtime_compositor/COM_conversion_operation.hh @@ -0,0 +1,158 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#include "GPU_shader.h" + +#include "COM_context.hh" +#include "COM_input_descriptor.hh" +#include "COM_result.hh" +#include "COM_simple_operation.hh" + +namespace blender::realtime_compositor { + +/* -------------------------------------------------------------------- */ +/** \name Conversion Operation + * + * A simple operation that converts a result from a certain type to another. See the derived + * classes for more details. + * \{ */ + +class ConversionOperation : public SimpleOperation { + public: + using SimpleOperation::SimpleOperation; + + /* If the input result is a single value, execute_single is called. Otherwise, the shader + * provided by get_conversion_shader is dispatched. */ + void execute() override; + + /* Determine if a conversion operation is needed for the input with the given result and + * descriptor. If it is not needed, return a null pointer. If it is needed, return an instance of + * the appropriate conversion operation. */ + static SimpleOperation *construct_if_needed(Context &context, + const Result &input_result, + const InputDescriptor &input_descriptor); + + protected: + /* Convert the input single value result to the output single value result. */ + virtual void execute_single(const Result &input, Result &output) = 0; + + /* Get the shader the will be used for conversion. */ + virtual GPUShader *get_conversion_shader() const = 0; + + /** \} */ + +}; // namespace blender::realtime_compositorclassConversionOperation:publicSimpleOperation + +/* -------------------------------------------------------------------- */ +/** \name Convert Float to Vector Operation + * + * Takes a float result and outputs a vector result. All three components of the output are filled + * with the input float. + * \{ */ + +class ConvertFloatToVectorOperation : public ConversionOperation { + public: + ConvertFloatToVectorOperation(Context &context); + + void execute_single(const Result &input, Result &output) override; + + GPUShader *get_conversion_shader() const override; +}; + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Convert Float to Color Operation + * + * Takes a float result and outputs a color result. All three color channels of the output are + * filled with the input float and the alpha channel is set to 1. + * \{ */ + +class ConvertFloatToColorOperation : public ConversionOperation { + public: + ConvertFloatToColorOperation(Context &context); + + void execute_single(const Result &input, Result &output) override; + + GPUShader *get_conversion_shader() const override; +}; + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Convert Color to Float Operation + * + * Takes a color result and outputs a float result. The output is the average of the three color + * channels, the alpha channel is ignored. + * \{ */ + +class ConvertColorToFloatOperation : public ConversionOperation { + public: + ConvertColorToFloatOperation(Context &context); + + void execute_single(const Result &input, Result &output) override; + + GPUShader *get_conversion_shader() const override; +}; + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Convert Color to Vector Operation + * + * Takes a color result and outputs a vector result. The output is a copy of the three color + * channels to the three vector components. + * \{ */ + +class ConvertColorToVectorOperation : public ConversionOperation { + public: + ConvertColorToVectorOperation(Context &context); + + void execute_single(const Result &input, Result &output) override; + + GPUShader *get_conversion_shader() const override; +}; + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Convert Vector to Float Operation + * + * Takes a vector result and outputs a float result. The output is the average of the three + * components. + * \{ */ + +/* + * + * */ +class ConvertVectorToFloatOperation : public ConversionOperation { + public: + ConvertVectorToFloatOperation(Context &context); + + void execute_single(const Result &input, Result &output) override; + + GPUShader *get_conversion_shader() const override; +}; + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Convert Vector to Color Operation + * + * Takes a vector result and outputs a color result. The output is a copy of the three vector + * components to the three color channels with the alpha channel set to 1. + * \{ */ + +class ConvertVectorToColorOperation : public ConversionOperation { + public: + ConvertVectorToColorOperation(Context &context); + + void execute_single(const Result &input, Result &output) override; + + GPUShader *get_conversion_shader() const override; +}; + +/** \} */ + +} // namespace blender::realtime_compositor diff --git a/source/blender/compositor/realtime_compositor/COM_domain.hh b/source/blender/compositor/realtime_compositor/COM_domain.hh new file mode 100644 index 00000000000..54d712f7578 --- /dev/null +++ b/source/blender/compositor/realtime_compositor/COM_domain.hh @@ -0,0 +1,166 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#include <cstdint> + +#include "BLI_float3x3.hh" +#include "BLI_math_vec_types.hh" + +namespace blender::realtime_compositor { + +/* Possible interpolations to use when realizing an input result of some domain on another domain. + * See the RealizationOptions struct for more information. */ +enum class Interpolation : uint8_t { + Nearest, + Bilinear, + Bicubic, +}; + +/* ------------------------------------------------------------------------------------------------ + * Realization Options + * + * The options that describe how an input result prefer to be realized on some other domain. This + * is used by the Realize On Domain Operation to identify the appropriate method of realization. + * See the Domain class for more information. */ +struct RealizationOptions { + /* The interpolation method that should be used when performing realization. Since realizing a + * result involves projecting it on a different domain, which in turn, involves sampling the + * result at arbitrary locations, the interpolation identifies the method used for computing the + * value at those arbitrary locations. */ + Interpolation interpolation = Interpolation::Nearest; + /* If true, the result will be repeated infinitely along the horizontal axis when realizing the + * result. If false, regions outside of bounds of the result along the horizontal axis will be + * filled with zeros. */ + bool repeat_x = false; + /* If true, the result will be repeated infinitely along the vertical axis when realizing the + * result. If false, regions outside of bounds of the result along the vertical axis will be + * filled with zeros. */ + bool repeat_y = false; +}; + +/* ------------------------------------------------------------------------------------------------ + * Domain + * + * The compositor is designed in such a way as to allow compositing in an infinite virtual + * compositing space. Consequently, any result of an operation is not only represented by its image + * output, but also by its transformation in that virtual space. The transformation of the result + * together with the dimension of its image is stored and represented by a Domain. In the figure + * below, two results of different domains are illustrated on the virtual compositing space. One of + * the results is centered in space with an image dimension of 800px × 600px, and the other result + * is scaled down and translated such that it lies in the upper right quadrant of the space with an + * image dimension of 800px × 400px. The position of the domain is in pixel space, and the domain + * is considered centered if it has an identity transformation. Note that both results have the + * same resolution, but occupy different areas of the virtual compositing space. + * + * y + * ^ + * 800px x 600px | + * .---------------------|---------------------. + * | | 800px x 600px | + * | | .-------------. | + * | | | | | + * | | '-------------' | + * ------|---------------------|---------------------|------> x + * | | | + * | | | + * | | | + * | | | + * '---------------------|---------------------' + * | + * + * By default, results have domains of identity transformations, that is, they are centered in + * space, but a transformation operation like the rotate, translate, or transform operations will + * adjust the transformation to make the result reside somewhere different in space. The domain of + * a single value result is irrelevant and always set to an identity domain. + * + * An operation is typically only concerned about a subset of the virtual compositing space, this + * subset is represented by a domain which is called the Operation Domain. It follows that before + * the operation itself is executed, inputs will typically be realized on the operation domain to + * be in the same domain and have the same dimension as that of the operation domain. This process + * is called Domain Realization and is implemented using an operation called the Realize On Domain + * Operation. Realization involves projecting the result onto the target domain, copying the area + * of the result that intersects the target domain, and filling the rest with zeros or repetitions + * of the result depending on the realization options that can be set by the user. Consequently, + * operations can generally expect their inputs to have the same dimension and can operate on them + * directly and transparently. For instance, if an operation takes both results illustrated in + * the figure above, and the operation has an operation domain that matches the bigger domain, the + * result with the bigger domain will not need to be realized because it already has a domain that + * matches that of the operation domain, but the result with the smaller domain will have to be + * realized into a new result that has the same domain as the domain of the bigger result. Assuming + * no repetition, the output of the realization will be an all zeros image with dimension 800px × + * 600px with a small scaled version of the smaller result copied into the upper right quadrant of + * the image. The following figure illustrates the realization process on a different set of + * results + * + * Realized Result + * +-------------+ +-------------+ + * | Operation | | | + * | Domain | | Zeros | + * | | ----> | | + * +-----|-----+ | |-----+ | + * | | C | | | C | | + * | +-----|-------+ +-----'-------+ + * | Domain Of | + * | Input | + * +-----------+ + * + * An operation can operate in an arbitrary operation domain, but in most cases, the operation + * domain is inferred from the inputs of the operation. In particular, one of the inputs is said to + * be the Domain Input of the operation and the operation domain is inferred from its domain. It + * follows that this particular input will not need realization, because it already has the correct + * domain. The domain input selection mechanism is as follows. Each of the inputs are assigned a + * value by the developer called the Domain Priority, the domain input is then chosen as the + * non-single value input with the highest domain priority, zero being the highest priority. See + * Operation::compute_domain for more information. + * + * The aforementioned logic for operation domain computation is only a default that works for most + * cases, but an operation can override the compute_domain method to implement a different logic. + * For instance, output nodes have an operation domain the same size as the viewport and with an + * identity transformation, their operation domain doesn't depend on the inputs at all. + * + * For instance, a filter operation has two inputs, a factor and a color, the latter of which is + * assigned a domain priority of 0 and the former is assigned a domain priority of 1. If the color + * input is not a single value input, then the color input is considered to be the domain input of + * the operation and the operation domain is computed to be the same domain as the color input, + * because it has the highest priority. It follows that if the factor input has a different domain + * than the computed domain of the operation, it will be projected and realized on it to have the + * same domain as described above. On the other hand, if the color input is a single value input, + * then the factor input is considered to be the domain input and the operation domain will be the + * same as the domain of the factor input, because it has the second highest domain priority. + * Finally, if both inputs are single value inputs, the operation domain will be an identity domain + * and is irrelevant, because the output will be a domain-less single value. */ +class Domain { + public: + /* The size of the domain in pixels. */ + int2 size; + /* The 2D transformation of the domain defining its translation in pixels, rotation, and scale in + * the virtual compositing space. */ + float3x3 transformation; + /* The options that describe how this domain prefer to be realized on some other domain. See the + * RealizationOptions struct for more information. */ + RealizationOptions realization_options; + + public: + /* A size only constructor that sets the transformation to identity. */ + Domain(int2 size); + + Domain(int2 size, float3x3 transformation); + + /* Transform the domain by the given transformation. This effectively pre-multiply the given + * transformation by the current transformation of the domain. */ + void transform(const float3x3 &input_transformation); + + /* Returns a domain of size 1x1 and an identity transformation. */ + static Domain identity(); +}; + +/* Compare the size and transformation of the domain. The realization_options are not compared + * because they only describe the method of realization on another domain, which is not technically + * a property of the domain itself. */ +bool operator==(const Domain &a, const Domain &b); + +/* Inverse of the above equality operator. */ +bool operator!=(const Domain &a, const Domain &b); + +} // namespace blender::realtime_compositor diff --git a/source/blender/compositor/realtime_compositor/COM_evaluator.hh b/source/blender/compositor/realtime_compositor/COM_evaluator.hh new file mode 100644 index 00000000000..258a2a038c4 --- /dev/null +++ b/source/blender/compositor/realtime_compositor/COM_evaluator.hh @@ -0,0 +1,170 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#include <memory> + +#include "BLI_vector.hh" + +#include "DNA_node_types.h" + +#include "NOD_derived_node_tree.hh" + +#include "COM_compile_state.hh" +#include "COM_context.hh" +#include "COM_node_operation.hh" +#include "COM_operation.hh" +#include "COM_shader_operation.hh" + +namespace blender::realtime_compositor { + +using namespace nodes::derived_node_tree_types; + +/* ------------------------------------------------------------------------------------------------ + * Evaluator + * + * The evaluator is the main class of the compositor and the entry point of its execution. The + * evaluator compiles the compositor node tree and evaluates it to compute its output. It is + * constructed from a compositor node tree and a compositor context. Upon calling the evaluate + * method, the evaluator will check if the node tree is already compiled into an operations stream, + * and if it is, it will go over it and evaluate the operations in order. It is then the + * responsibility of the caller to call the reset method when the node tree changes to invalidate + * the operations stream. A reset is also required if the resources used by the node tree change, + * for instances, when the dimensions of an image used by the node tree changes. This is necessary + * because the evaluator compiles the node tree into an operations stream that is specifically + * optimized for the structure of the resources used by the node tree. + * + * Otherwise, if the node tree is not yet compiled, the evaluator will compile it into an + * operations stream, evaluating the operations in the process. It should be noted that operations + * are evaluated as soon as they are compiled, as opposed to compiling the whole operations stream + * and then evaluating it in a separate step. This is important because, as mentioned before, the + * operations stream is optimized specifically for the structure of the resources used by the node + * tree, which is only known after the operations are evaluated. In other words, the evaluator uses + * the evaluated results of previously compiled operations to compile the operations that follow + * them in an optimized manner. + * + * Compilation starts by computing an optimized node execution schedule by calling the + * compute_schedule function, see the discussion in COM_scheduler.hh for more details. For the node + * tree shown below, the execution schedule is denoted by the node numbers. The compiler then goes + * over the execution schedule in order and compiles each node into either a Node Operation or a + * Shader Operation, depending on the node type, see the is_shader_node function. A Shader + * operation is constructed from a group of nodes forming a contiguous subset of the node execution + * schedule. For instance, in the node tree shown below, nodes 3 and 4 are compiled together into a + * shader operation and node 5 is compiled into its own shader operation, both of which are + * contiguous subsets of the node execution schedule. This process is described in details in the + * following section. + * + * Shader Operation 1 Shader Operation 2 + * +-----------------------------------+ +------------------+ + * .------------. | .------------. .------------. | | .------------. | .------------. + * | Node 1 | | | Node 3 | | Node 4 | | | | Node 5 | | | Node 6 | + * | |----|--| |--| |---|-----|--| |--|--| | + * | | .-|--| | | | | .--|--| | | | | + * '------------' | | '------------' '------------' | | | '------------' | '------------' + * | +-----------------------------------+ | +------------------+ + * .------------. | | + * | Node 2 | | | + * | |--'----------------------------------------' + * | | + * '------------' + * + * For non shader nodes, the compilation process is straight forward, the compiler instantiates a + * node operation from the node, map its inputs to the results of the outputs they are linked to, + * and evaluates the operations. However, for shader nodes, since a group of nodes can be compiled + * together into a shader operation, the compilation process is a bit involved. The compiler uses + * an instance of the Compile State class to keep track of the compilation process. The compiler + * state stores the so called "shader compile unit", which is the current group of nodes that will + * eventually be compiled together into a shader operation. While going over the schedule, the + * compiler adds the shader nodes to the compile unit until it decides that the compile unit is + * complete and should be compiled. This is typically decided when the current node is not + * compatible with the compile unit and can't be added to it, only then it compiles the compile + * unit into a shader operation and resets it to ready it to track the next potential group of + * nodes that will form a shader operation. This decision is made based on various criteria in the + * should_compile_shader_compile_unit function. See the discussion in COM_compile_state.hh for more + * details of those criteria, but perhaps the most evident of which is whether the node is actually + * a shader node, if it isn't, then it evidently can't be added to the compile unit and the compile + * unit is should be compiled. + * + * For the node tree above, the compilation process is as follows. The compiler goes over the node + * execution schedule in order considering each node. Nodes 1 and 2 are not shader node so they are + * compiled into node operations and added to the operations stream. The current compile unit is + * empty, so it is not compiled. Node 3 is a shader node, and since the compile unit is currently + * empty, it is unconditionally added to it. Node 4 is a shader node, it was decided---for the sake + * of the demonstration---that it is compatible with the compile unit and can be added to it. Node + * 5 is a shader node, but it was decided---for the sake of the demonstration---that it is not + * compatible with the compile unit, so the compile unit is considered complete and is compiled + * first, adding the first shader operation to the operations stream and resetting the compile + * unit. Node 5 is then added to the now empty compile unit similar to node 3. Node 6 is not a + * shader node, so the compile unit is considered complete and is compiled first, adding the first + * shader operation to the operations stream and resetting the compile unit. Finally, node 6 is + * compiled into a node operation similar to nodes 1 and 2 and added to the operations stream. */ +class Evaluator { + private: + /* A reference to the compositor context. */ + Context &context_; + /* A reference to the compositor node tree. */ + bNodeTree &node_tree_; + std::unique_ptr<DerivedNodeTree> derived_node_tree_; + /* The compiled operations stream. This contains ordered pointers to the operations that were + * compiled. This is initialized when the node tree is compiled and freed when the evaluator + * resets. The is_compiled_ member indicates whether the operation stream can be used or needs to + * be compiled first. Note that the operations stream can be empty even when compiled, this can + * happen when the node tree is empty or invalid for instance. */ + Vector<std::unique_ptr<Operation>> operations_stream_; + /* True if the node tree is already compiled into an operations stream that can be evaluated + * directly. False if the node tree is not compiled yet and needs to be compiled. */ + bool is_compiled_ = false; + + public: + /* Construct an evaluator from a compositor node tree and a context. */ + Evaluator(Context &context, bNodeTree &node_tree); + + /* Evaluate the compositor node tree. If the node tree is already compiled into an operations + * stream, that stream will be evaluated directly. Otherwise, the node tree will be compiled and + * evaluated. */ + void evaluate(); + + /* Invalidate the operations stream that was compiled for the node tree. This should be called + * when the node tree changes or the structure of any of the resources used by it changes. By + * structure, we mean things like the dimensions of the used images, while changes to their + * contents do not necessitate a reset. */ + void reset(); + + private: + /* Check if the compositor node tree is valid by checking if it has: + * - Cyclic links. + * - Undefined nodes or sockets. + * - Unsupported nodes. + * If the node tree is valid, true is returned. Otherwise, false is returned, and an appropriate + * error message is set by calling the context's set_info_message method. */ + bool validate_node_tree(); + + /* Compile the node tree into an operations stream and evaluate it. */ + void compile_and_evaluate(); + + /* Compile the given node into a node operation, map each input to the result of the output + * linked to it, update the compile state, add the newly created operation to the operations + * stream, and evaluate the operation. */ + void compile_and_evaluate_node(DNode node, CompileState &compile_state); + + /* Map each input of the node operation to the result of the output linked to it. Unlinked inputs + * are mapped to the result of a newly created Input Single Value Operation, which is added to + * the operations stream and evaluated. Since this method might add operations to the operations + * stream, the actual node operation should only be added to the stream once this method is + * called. */ + void map_node_operation_inputs_to_their_results(DNode node, + NodeOperation *operation, + CompileState &compile_state); + + /* Compile the shader compile unit into a shader operation, map each input of the operation to + * the result of the output linked to it, update the compile state, add the newly created + * operation to the operations stream, evaluate the operation, and finally reset the shader + * compile unit. */ + void compile_and_evaluate_shader_compile_unit(CompileState &compile_state); + + /* Map each input of the shader operation to the result of the output linked to it. */ + void map_shader_operation_inputs_to_their_results(ShaderOperation *operation, + CompileState &compile_state); +}; + +} // namespace blender::realtime_compositor diff --git a/source/blender/compositor/realtime_compositor/COM_input_descriptor.hh b/source/blender/compositor/realtime_compositor/COM_input_descriptor.hh new file mode 100644 index 00000000000..215a92ab3b4 --- /dev/null +++ b/source/blender/compositor/realtime_compositor/COM_input_descriptor.hh @@ -0,0 +1,34 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#include "COM_result.hh" + +namespace blender::realtime_compositor { + +/* ------------------------------------------------------------------------------------------------ + * Input Descriptor + * + * A class that describes an input of an operation. */ +class InputDescriptor { + public: + /* The type of input. This may be different that the type of result that the operation will + * receive for the input, in which case, an implicit conversion operation will be added as an + * input processor to convert it to the required type. */ + ResultType type; + /* If true, then the input does not need to be realized on the domain of the operation before its + * execution. See the discussion in COM_domain.hh for more information. */ + bool skip_realization = false; + /* The priority of the input for determining the operation domain. The non-single value input + * with the highest priority will be used to infer the operation domain, the highest priority + * being zero. See the discussion in COM_domain.hh for more information. */ + int domain_priority = 0; + /* If true, the input expects a single value, and if a non-single value is provided, a default + * single value will be used instead, see the get_<type>_value_default methods in the Result + * class. It follows that this also implies skip_realization, because we don't need to realize a + * result that will be discarded anyways. If false, the input can work with both single and + * non-single values. */ + bool expects_single_value = false; +}; + +} // namespace blender::realtime_compositor diff --git a/source/blender/compositor/realtime_compositor/COM_input_single_value_operation.hh b/source/blender/compositor/realtime_compositor/COM_input_single_value_operation.hh new file mode 100644 index 00000000000..bbcd336ee11 --- /dev/null +++ b/source/blender/compositor/realtime_compositor/COM_input_single_value_operation.hh @@ -0,0 +1,46 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#include "BLI_string_ref.hh" + +#include "NOD_derived_node_tree.hh" + +#include "COM_context.hh" +#include "COM_operation.hh" +#include "COM_result.hh" + +namespace blender::realtime_compositor { + +using namespace nodes::derived_node_tree_types; + +/* ------------------------------------------------------------------------------------------------ + * Input Single Value Operation + * + * An input single value operation is an operation that outputs a single value result whose value + * is the value of an unlinked input socket. This is typically used to initialize the values of + * unlinked node input sockets. */ +class InputSingleValueOperation : public Operation { + private: + /* The identifier of the output. */ + static const StringRef output_identifier_; + /* The input socket whose value will be computed as the operation's result. */ + DInputSocket input_socket_; + + public: + InputSingleValueOperation(Context &context, DInputSocket input_socket); + + /* Allocate a single value result and set its value to the default value of the input socket. */ + void execute() override; + + /* Get a reference to the output result of the operation, this essentially calls the super + * get_result with the output identifier of the operation. */ + Result &get_result(); + + private: + /* Populate the result of the operation, this essentially calls the super populate_result method + * with the output identifier of the operation. */ + void populate_result(Result result); +}; + +} // namespace blender::realtime_compositor diff --git a/source/blender/compositor/realtime_compositor/COM_node_operation.hh b/source/blender/compositor/realtime_compositor/COM_node_operation.hh new file mode 100644 index 00000000000..94bc4dfd39d --- /dev/null +++ b/source/blender/compositor/realtime_compositor/COM_node_operation.hh @@ -0,0 +1,56 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#include "BLI_string_ref.hh" + +#include "DNA_node_types.h" + +#include "NOD_derived_node_tree.hh" + +#include "COM_context.hh" +#include "COM_operation.hh" +#include "COM_scheduler.hh" + +namespace blender::realtime_compositor { + +using namespace nodes::derived_node_tree_types; + +/* ------------------------------------------------------------------------------------------------ + * Node Operation + * + * A node operation is a subclass of operation that nodes should implement and instantiate in the + * get_compositor_operation function of bNodeType, passing the inputs given to that function to the + * constructor. This class essentially just implements a default constructor that populates output + * results for all outputs of the node as well as input descriptors for all inputs of the nodes + * based on their socket declaration. The class also provides some utility methods for easier + * implementation of nodes. */ +class NodeOperation : public Operation { + private: + /* The node that this operation represents. */ + DNode node_; + + public: + /* Populate the output results based on the node outputs and populate the input descriptors based + * on the node inputs. */ + NodeOperation(Context &context, DNode node); + + /* Compute and set the initial reference counts of all the results of the operation. The + * reference counts of the results are the number of operations that use those results, which is + * computed as the number of inputs whose node is part of the schedule and is linked to the + * output corresponding to each result. The node execution schedule is given as an input. */ + void compute_results_reference_counts(const Schedule &schedule); + + protected: + /* Returns a reference to the derived node that this operation represents. */ + const DNode &node() const; + + /* Returns a reference to the node that this operation represents. */ + const bNode &bnode() const; + + /* Returns true if the output identified by the given identifier is needed and should be + * computed, otherwise returns false. */ + bool should_compute_output(StringRef identifier); +}; + +} // namespace blender::realtime_compositor diff --git a/source/blender/compositor/realtime_compositor/COM_operation.hh b/source/blender/compositor/realtime_compositor/COM_operation.hh new file mode 100644 index 00000000000..38518c00c04 --- /dev/null +++ b/source/blender/compositor/realtime_compositor/COM_operation.hh @@ -0,0 +1,175 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#include <memory> +#include <string> + +#include "BLI_map.hh" +#include "BLI_string_ref.hh" +#include "BLI_vector.hh" + +#include "COM_context.hh" +#include "COM_domain.hh" +#include "COM_input_descriptor.hh" +#include "COM_result.hh" +#include "COM_static_shader_manager.hh" +#include "COM_texture_pool.hh" + +namespace blender::realtime_compositor { + +class SimpleOperation; + +/* A type representing a vector of simple operations that store the input processors for a + * particular input. */ +using ProcessorsVector = Vector<std::unique_ptr<SimpleOperation>>; + +/* ------------------------------------------------------------------------------------------------ + * Operation + * + * The operation is the basic unit of the compositor. The evaluator compiles the compositor node + * tree into an ordered stream of operations which are then executed in order during evaluation. + * The operation class can be sub-classed to implement a new operation. Operations have a number of + * inputs and outputs that are declared during construction and are identified by string + * identifiers. Inputs are declared by calling declare_input_descriptor providing an appropriate + * descriptor. Those inputs are mapped to the results computed by other operations whose outputs + * are linked to the inputs. Such mappings are established by the compiler during compilation by + * calling the map_input_to_result method. Outputs are populated by calling the populate_result + * method, providing a result of an appropriate type. Upon execution, the operation allocates a + * result for each of its outputs and computes their value based on its inputs and options. + * + * Each input may have one or more input processors, which are simple operations that process the + * inputs before the operation is executed, see the discussion in COM_simple_operation.hh for more + * information. And thus the effective input of the operation is the result of the last input + * processor if one exists. Input processors are added and evaluated by calling the + * add_and_evaluate_input_processors method, which provides a default implementation that does + * things like implicit conversion, domain realization, and more. This default implementation can, + * however, be overridden, extended, or removed. Once the input processors are added and evaluated + * for the first time, they are stored in the operation and future evaluations can evaluate them + * directly without having to add them again. + * + * The operation is evaluated by calling the evaluate method, which first adds the input processors + * if they weren't added already and evaluates them, then it resets the results of the operation, + * then it calls the execute method of the operation, and finally it releases the results mapped to + * the inputs to declare that they are no longer needed. */ +class Operation { + private: + /* A reference to the compositor context. This member references the same object in all + * operations but is included in the class for convenience. */ + Context &context_; + /* A mapping between each output of the operation identified by its identifier and the result for + * that output. A result for each output of the operation should be constructed and added to the + * map during operation construction by calling the populate_result method. The results should be + * allocated and their contents should be computed in the execute method. */ + Map<std::string, Result> results_; + /* A mapping between each input of the operation identified by its identifier and its input + * descriptor. Those descriptors should be declared during operation construction by calling the + * declare_input_descriptor method. */ + Map<std::string, InputDescriptor> input_descriptors_; + /* A mapping between each input of the operation identified by its identifier and a pointer to + * the computed result providing its data. The mapped result is either one that was computed by + * another operation or one that was internally computed in the operation by the last input + * processor for that input. It is the responsibility of the evaluator to map the inputs to their + * linked results before evaluating the operation by calling the map_input_to_result method. */ + Map<StringRef, Result *> results_mapped_to_inputs_; + /* A mapping between each input of the operation identified by its identifier and an ordered list + * of simple operations to process that input. This is initialized the first time the input + * processors are evaluated by calling the add_and_evaluate_input_processors method. Further + * evaluations will evaluate the processors directly without the need to add them again. The + * input_processors_added_ member indicates whether the processors were already added and can be + * evaluated directly or need to be added and evaluated. */ + Map<StringRef, ProcessorsVector> input_processors_; + /* True if the input processors were already added and can be evaluated directly. False if the + * input processors are not yet added and needs to be added. */ + bool input_processors_added_ = false; + + public: + Operation(Context &context); + + virtual ~Operation(); + + /* Evaluate the operation by: + * 1. Evaluating the input processors. + * 2. Resetting the results of the operation. + * 3. Calling the execute method of the operation. + * 4. Releasing the results mapped to the inputs. */ + void evaluate(); + + /* Get a reference to the output result identified by the given identifier. */ + Result &get_result(StringRef identifier); + + /* Map the input identified by the given identifier to the result providing its data. See + * results_mapped_to_inputs_ for more details. This should be called by the evaluator to + * establish links between different operations. */ + void map_input_to_result(StringRef identifier, Result *result); + + protected: + /* Compute the operation domain of this operation. By default, this implements a default logic + * that infers the operation domain from the inputs, which may be overridden for a different + * logic. See the discussion in COM_domain.hh for the inference logic and more information. */ + virtual Domain compute_domain(); + + /* Add and evaluate any needed input processors, which essentially just involves calling the + * add_and_evaluate_input_processor method with the needed processors. This is called before + * executing the operation to prepare its inputs. The class defines a default implementation + * which adds typically needed processors, but derived classes can override the method to have + * a different implementation, extend the implementation, or remove it entirely. */ + virtual void add_and_evaluate_input_processors(); + + /* Given the identifier of an input of the operation and a processor operation: + * - Add the given processor to the list of input processors for the input. + * - Map the input of the processor to be the result of the last input processor or the result + * mapped to the input if no previous processors exists. + * - Switch the result mapped to the input to be the output result of the processor. + * - Evaluate the processor. */ + void add_and_evaluate_input_processor(StringRef identifier, SimpleOperation *processor); + + /* This method should allocate the operation results, execute the operation, and compute the + * output results. */ + virtual void execute() = 0; + + /* Get a reference to the result connected to the input identified by the given identifier. */ + Result &get_input(StringRef identifier) const; + + /* Switch the result mapped to the input identified by the given identifier with the given + * result. */ + void switch_result_mapped_to_input(StringRef identifier, Result *result); + + /* Add the given result to the results_ map identified by the given output identifier. This + * should be called during operation construction for all outputs. The provided result shouldn't + * be allocated or initialized, this will happen later during execution. */ + void populate_result(StringRef identifier, Result result); + + /* Declare the descriptor of the input identified by the given identifier to be the given + * descriptor. Adds the given descriptor to the input_descriptors_ map identified by the given + * input identifier. This should be called during operation constructor for all inputs. */ + void declare_input_descriptor(StringRef identifier, InputDescriptor descriptor); + + /* Get a reference to the descriptor of the input identified by the given identified. */ + InputDescriptor &get_input_descriptor(StringRef identifier); + + /* Returns a reference to the compositor context. */ + Context &context(); + + /* Returns a reference to the texture pool of the compositor context. */ + TexturePool &texture_pool() const; + + /* Returns a reference to the shader manager of the compositor context. */ + StaticShaderManager &shader_manager() const; + + private: + /* Evaluate the input processors. If the input processors were already added they will be + * evaluated directly. Otherwise, the input processors will be added and evaluated. */ + void evaluate_input_processors(); + + /* Resets the results of the operation. See the reset method in the Result class for more + * information. */ + void reset_results(); + + /* Release the results that are mapped to the inputs of the operation. This is called after the + * evaluation of the operation to declare that the results are no longer needed by this + * operation. */ + void release_inputs(); +}; + +} // namespace blender::realtime_compositor diff --git a/source/blender/compositor/realtime_compositor/COM_realize_on_domain_operation.hh b/source/blender/compositor/realtime_compositor/COM_realize_on_domain_operation.hh new file mode 100644 index 00000000000..5a842e16008 --- /dev/null +++ b/source/blender/compositor/realtime_compositor/COM_realize_on_domain_operation.hh @@ -0,0 +1,49 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#include "GPU_shader.h" + +#include "COM_context.hh" +#include "COM_domain.hh" +#include "COM_input_descriptor.hh" +#include "COM_result.hh" +#include "COM_simple_operation.hh" + +namespace blender::realtime_compositor { + +/* ------------------------------------------------------------------------------------------------ + * Realize On Domain Operation + * + * A simple operation that projects the input on a certain target domain, copies the area of the + * input that intersects the target domain, and fill the rest with zeros or repetitions of the + * input depending on the realization options of the target domain. See the discussion in + * COM_domain.hh for more information. */ +class RealizeOnDomainOperation : public SimpleOperation { + private: + /* The target domain to realize the input on. */ + Domain domain_; + + public: + RealizeOnDomainOperation(Context &context, Domain domain, ResultType type); + + void execute() override; + + /* Determine if a realize on domain operation is needed for the input with the given result and + * descriptor in an operation with the given operation domain. If it is not needed, return a null + * pointer. If it is needed, return an instance of the operation. */ + static SimpleOperation *construct_if_needed(Context &context, + const Result &input_result, + const InputDescriptor &input_descriptor, + const Domain &operation_domain); + + protected: + /* The operation domain is just the target domain. */ + Domain compute_domain() override; + + private: + /* Get the realization shader of the appropriate type. */ + GPUShader *get_realization_shader(); +}; + +} // namespace blender::realtime_compositor diff --git a/source/blender/compositor/realtime_compositor/COM_reduce_to_single_value_operation.hh b/source/blender/compositor/realtime_compositor/COM_reduce_to_single_value_operation.hh new file mode 100644 index 00000000000..2f5a82c79b7 --- /dev/null +++ b/source/blender/compositor/realtime_compositor/COM_reduce_to_single_value_operation.hh @@ -0,0 +1,31 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#include "COM_context.hh" +#include "COM_result.hh" +#include "COM_simple_operation.hh" + +namespace blender::realtime_compositor { + +/* ------------------------------------------------------------------------------------------------ + * Reduce To Single Value Operation + * + * A simple operation that reduces its input result into a single value output result. The input is + * assumed to be a texture result of size 1x1, that is, a texture composed of a single pixel, the + * value of which shall serve as the single value of the output result. */ +class ReduceToSingleValueOperation : public SimpleOperation { + public: + ReduceToSingleValueOperation(Context &context, ResultType type); + + /* Download the input pixel from the GPU texture and set its value to the value of the allocated + * single value output result. */ + void execute() override; + + /* Determine if a reduce to single value operation is needed for the input with the + * given result. If it is not needed, return a null pointer. If it is needed, return an instance + * of the operation. */ + static SimpleOperation *construct_if_needed(Context &context, const Result &input_result); +}; + +} // namespace blender::realtime_compositor diff --git a/source/blender/compositor/realtime_compositor/COM_result.hh b/source/blender/compositor/realtime_compositor/COM_result.hh new file mode 100644 index 00000000000..a16d68bb92d --- /dev/null +++ b/source/blender/compositor/realtime_compositor/COM_result.hh @@ -0,0 +1,234 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#include "BLI_float3x3.hh" +#include "BLI_math_vec_types.hh" + +#include "GPU_shader.h" +#include "GPU_texture.h" + +#include "COM_domain.hh" +#include "COM_texture_pool.hh" + +namespace blender::realtime_compositor { + +/* Possible data types that operations can operate on. They either represent the base type of the + * result texture or a single value result. */ +enum class ResultType : uint8_t { + Float, + Vector, + Color, +}; + +/* ------------------------------------------------------------------------------------------------ + * Result + * + * A result represents the computed value of an output of an operation. A result can either + * represent an image or a single value. A result is typed, and can be of type color, vector, or + * float. Single value results are stored in 1x1 textures to make them easily accessible in + * shaders. But the same value is also stored in the value union member of the result for any + * host-side processing. The texture of the result is allocated from the texture pool referenced by + * the result. + * + * Results are reference counted and their textures are released once their reference count reaches + * zero. After constructing a result, the set_initial_reference_count method is called to declare + * the number of operations that needs this result. Once each operation that needs the result no + * longer needs it, the release method is called and the reference count is decremented, until it + * reaches zero, where the result's texture is then released. Since results are eventually + * decremented to zero by the end of every evaluation, the reference count is restored before every + * evaluation to its initial reference count by calling the reset method, which is why a separate + * member initial_reference_count_ is stored to keep track of the initial value. + * + * A result not only represents an image, but also the area it occupies in the virtual compositing + * space. This area is called the Domain of the result, see the discussion in COM_domain.hh for + * more information. + * + * A result can be a proxy result that merely wraps another master result, in which case, it shares + * its values and delegates all reference counting to it. While a proxy result shares the value of + * the master result, it can have a different domain. Consequently, transformation operations are + * implemented using proxy results, where their results are proxy results of their inputs but with + * their domains transformed based on their options. Moreover, proxy results can also be used as + * the results of identity operations, that is, operations that do nothing to their inputs in + * certain configurations. In which case, the proxy result is left as is with no extra + * transformation on its domain whatsoever. Proxy results can be created by calling the + * pass_through method, see that method for more details. */ +class Result { + private: + /* The base type of the texture or the type of the single value. */ + ResultType type_; + /* If true, the result is a single value, otherwise, the result is a texture. */ + bool is_single_value_; + /* A GPU texture storing the result data. This will be a 1x1 texture if the result is a single + * value, the value of which will be identical to that of the value member. See class description + * for more information. */ + GPUTexture *texture_ = nullptr; + /* The texture pool used to allocate the texture of the result, this should be initialized during + * construction. */ + TexturePool *texture_pool_ = nullptr; + /* The number of operations that currently needs this result. At the time when the result is + * computed, this member will have a value that matches initial_reference_count_. Once each + * operation that needs the result no longer needs it, the release method is called and the + * reference count is decremented, until it reaches zero, where the result's texture is then + * released. If this result have a master result, then this reference count is irrelevant and + * shadowed by the reference count of the master result. */ + int reference_count_; + /* The number of operations that reference and use this result at the time when it was initially + * computed. Since reference_count_ is decremented and always becomes zero at the end of the + * evaluation, this member is used to reset the reference count of the results for later + * evaluations by calling the reset method. This member is also used to determine if this result + * should be computed by calling the should_compute method. */ + int initial_reference_count_; + /* If the result is a single value, this member stores the value of the result, the value of + * which will be identical to that stored in the texture member. The active union member depends + * on the type of the result. This member is uninitialized and should not be used if the result + * is a texture. */ + union { + float float_value_; + float3 vector_value_; + float4 color_value_; + }; + /* The domain of the result. This only matters if the result was a texture. See the discussion in + * COM_domain.hh for more information. */ + Domain domain_ = Domain::identity(); + /* If not nullptr, then this result wraps and shares the value of another master result. In this + * case, calls to texture-related methods like increment_reference_count and release should + * operate on the master result as opposed to this result. This member is typically set upon + * calling the pass_through method, which sets this result to be the master of a target result. + * See that method for more information. */ + Result *master_ = nullptr; + + public: + /* Construct a result of the given type with the given texture pool that will be used to allocate + * and release the result's texture. */ + Result(ResultType type, TexturePool &texture_pool); + + /* Declare the result to be a texture result, allocate a texture of an appropriate type with + * the size of the given domain from the result's texture pool, and set the domain of the result + * to the given domain. */ + void allocate_texture(Domain domain); + + /* Declare the result to be a single value result, allocate a texture of an appropriate + * type with size 1x1 from the result's texture pool, and set the domain to be an identity + * domain. See class description for more information. */ + void allocate_single_value(); + + /* Allocate a single value result and set its value to zero. This is called for results whose + * value can't be computed and are considered invalid. */ + void allocate_invalid(); + + /* Bind the texture of the result to the texture image unit with the given name in the currently + * bound given shader. This also inserts a memory barrier for texture fetches to ensure any prior + * writes to the texture are reflected before reading from it. */ + void bind_as_texture(GPUShader *shader, const char *texture_name) const; + + /* Bind the texture of the result to the image unit with the given name in the currently bound + * given shader. */ + void bind_as_image(GPUShader *shader, const char *image_name) const; + + /* Unbind the texture which was previously bound using bind_as_texture. */ + void unbind_as_texture() const; + + /* Unbind the texture which was previously bound using bind_as_image. */ + void unbind_as_image() const; + + /* Pass this result through to a target result, in which case, the target result becomes a proxy + * result with this result as its master result. This is done by making the target result a copy + * of this result, essentially having identical values between the two and consequently sharing + * the underlying texture. An exception is the initial reference count, whose value is retained + * and not copied, because it is a property of the original result and is needed for correctly + * resetting the result before the next evaluation. Additionally, this result is set to be the + * master of the target result, by setting the master member of the target. Finally, the + * reference count of the result is incremented by the reference count of the target result. See + * the discussion above for more information. */ + void pass_through(Result &target); + + /* Transform the result by the given transformation. This effectively pre-multiply the given + * transformation by the current transformation of the domain of the result. */ + void transform(const float3x3 &transformation); + + /* Get a reference to the realization options of this result. See the RealizationOptions struct + * for more information. */ + RealizationOptions &get_realization_options(); + + /* If the result is a single value result of type float, return its float value. Otherwise, an + * uninitialized value is returned. */ + float get_float_value() const; + + /* If the result is a single value result of type vector, return its vector value. Otherwise, an + * uninitialized value is returned. */ + float3 get_vector_value() const; + + /* If the result is a single value result of type color, return its color value. Otherwise, an + * uninitialized value is returned. */ + float4 get_color_value() const; + + /* Same as get_float_value but returns a default value if the result is not a single value. */ + float get_float_value_default(float default_value) const; + + /* Same as get_vector_value but returns a default value if the result is not a single value. */ + float3 get_vector_value_default(const float3 &default_value) const; + + /* Same as get_color_value but returns a default value if the result is not a single value. */ + float4 get_color_value_default(const float4 &default_value) const; + + /* If the result is a single value result of type float, set its float value and upload it to the + * texture. Otherwise, an undefined behavior is invoked. */ + void set_float_value(float value); + + /* If the result is a single value result of type vector, set its vector value and upload it to + * the texture. Otherwise, an undefined behavior is invoked. */ + void set_vector_value(const float3 &value); + + /* If the result is a single value result of type color, set its color value and upload it to the + * texture. Otherwise, an undefined behavior is invoked. */ + void set_color_value(const float4 &value); + + /* Set the value of initial_reference_count_, see that member for more details. This should be + * called after constructing the result to declare the number of operations that needs it. */ + void set_initial_reference_count(int count); + + /* Reset the result to prepare it for a new evaluation. This should be called before evaluating + * the operation that computes this result. First, set the value of reference_count_ to the value + * of initial_reference_count_ since reference_count_ may have already been decremented to zero + * in a previous evaluation. Second, set master_ to nullptr because the result may have been + * turned into a proxy result in a previous evaluation. Other fields don't need to be reset + * because they are runtime and overwritten during evaluation. */ + void reset(); + + /* Increment the reference count of the result by the given count. If this result have a master + * result, the reference count of the master result is incremented instead. */ + void increment_reference_count(int count = 1); + + /* Decrement the reference count of the result and release the its texture back into the texture + * pool if the reference count reaches zero. This should be called when an operation that used + * this result no longer needs it. If this result have a master result, the master result is + * released instead. */ + void release(); + + /* Returns true if this result should be computed and false otherwise. The result should be + * computed if its reference count is not zero, that is, its result is used by at least one + * operation. */ + bool should_compute(); + + /* Returns the type of the result. */ + ResultType type() const; + + /* Returns true if the result is a texture and false of it is a single value. */ + bool is_texture() const; + + /* Returns true if the result is a single value and false of it is a texture. */ + bool is_single_value() const; + + /* Returns the allocated GPU texture of the result. */ + GPUTexture *texture() const; + + /* Returns the reference count of the result. If this result have a master result, then the + * reference count of the master result is returned instead. */ + int reference_count() const; + + /* Returns a reference to the domain of the result. See the Domain class. */ + const Domain &domain() const; +}; + +} // namespace blender::realtime_compositor diff --git a/source/blender/compositor/realtime_compositor/COM_scheduler.hh b/source/blender/compositor/realtime_compositor/COM_scheduler.hh new file mode 100644 index 00000000000..4f778b32145 --- /dev/null +++ b/source/blender/compositor/realtime_compositor/COM_scheduler.hh @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#include "BLI_vector_set.hh" + +#include "NOD_derived_node_tree.hh" + +namespace blender::realtime_compositor { + +using namespace nodes::derived_node_tree_types; + +/* A type representing the ordered set of nodes defining the schedule of node execution. */ +using Schedule = VectorSet<DNode>; + +/* Computes the execution schedule of the node tree. This is essentially a post-order depth first + * traversal of the node tree from the output node to the leaf input nodes, with informed order of + * traversal of dependencies based on a heuristic estimation of the number of needed buffers. */ +Schedule compute_schedule(DerivedNodeTree &tree); + +} // namespace blender::realtime_compositor diff --git a/source/blender/compositor/realtime_compositor/COM_shader_node.hh b/source/blender/compositor/realtime_compositor/COM_shader_node.hh new file mode 100644 index 00000000000..50337935d03 --- /dev/null +++ b/source/blender/compositor/realtime_compositor/COM_shader_node.hh @@ -0,0 +1,87 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#include "BLI_string_ref.hh" +#include "BLI_vector.hh" + +#include "DNA_node_types.h" + +#include "GPU_material.h" + +#include "NOD_derived_node_tree.hh" + +namespace blender::realtime_compositor { + +using namespace nodes::derived_node_tree_types; + +/* ------------------------------------------------------------------------------------------------ + * Shader Node + * + * A shader node encapsulates a compositor node tree that is capable of being used together with + * other shader nodes to construct a Shader Operation using the GPU material compiler. A GPU node + * stack for each of the node inputs and outputs is stored and populated during construction in + * order to represent the node as a GPU node inside the GPU material graph, see GPU_material.h for + * more information. Derived classes should implement the compile method to add the node and link + * it to the GPU material given to the method. The compiler is expected to initialize the input + * links of the node before invoking the compile method. See the discussion in + * COM_shader_operation.hh for more information. */ +class ShaderNode { + private: + /* The node that this operation represents. */ + DNode node_; + /* The GPU node stacks of the inputs of the node. Those are populated during construction in the + * populate_inputs method. The links of the inputs are initialized by the GPU material compiler + * prior to calling the compile method. There is an extra stack at the end to mark the end of the + * array, as this is what the GPU module functions expect. */ + Vector<GPUNodeStack> inputs_; + /* The GPU node stacks of the outputs of the node. Those are populated during construction in the + * populate_outputs method. There is an extra stack at the end to mark the end of the array, as + * this is what the GPU module functions expect. */ + Vector<GPUNodeStack> outputs_; + + public: + /* Construct the node by populating both its inputs and outputs. */ + ShaderNode(DNode node); + + virtual ~ShaderNode() = default; + + /* Compile the node by adding the appropriate GPU material graph nodes and linking the + * appropriate resources. */ + virtual void compile(GPUMaterial *material) = 0; + + /* Returns a contiguous array containing the GPU node stacks of each input. */ + GPUNodeStack *get_inputs_array(); + + /* Returns a contiguous array containing the GPU node stacks of each output. */ + GPUNodeStack *get_outputs_array(); + + /* Returns the GPU node stack of the input with the given identifier. */ + GPUNodeStack &get_input(StringRef identifier); + + /* Returns the GPU node stack of the output with the given identifier. */ + GPUNodeStack &get_output(StringRef identifier); + + /* Returns the GPU node link of the input with the given identifier, if the input is not linked, + * a uniform link carrying the value of the input will be created a returned. It is expected that + * the caller will use the returned link in a GPU material, otherwise, the link may not be + * properly freed. */ + GPUNodeLink *get_input_link(StringRef identifier); + + protected: + /* Returns a reference to the derived node that this operation represents. */ + const DNode &node() const; + + /* Returns a reference to the node this operations represents. */ + const bNode &bnode() const; + + private: + /* Populate the inputs of the node. The input link is set to nullptr and is expected to be + * initialized by the GPU material compiler before calling the compile method. */ + void populate_inputs(); + /* Populate the outputs of the node. The output link is set to nullptr and is expected to be + * initialized by the compile method. */ + void populate_outputs(); +}; + +} // namespace blender::realtime_compositor diff --git a/source/blender/compositor/realtime_compositor/COM_shader_operation.hh b/source/blender/compositor/realtime_compositor/COM_shader_operation.hh new file mode 100644 index 00000000000..d03e52ac8f2 --- /dev/null +++ b/source/blender/compositor/realtime_compositor/COM_shader_operation.hh @@ -0,0 +1,242 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#include <memory> + +#include "BLI_map.hh" +#include "BLI_string_ref.hh" +#include "BLI_vector_set.hh" + +#include "GPU_material.h" +#include "GPU_shader.h" + +#include "gpu_shader_create_info.hh" + +#include "NOD_derived_node_tree.hh" + +#include "COM_context.hh" +#include "COM_operation.hh" +#include "COM_scheduler.hh" + +namespace blender::realtime_compositor { + +using namespace nodes::derived_node_tree_types; + +/* A type representing a contiguous subset of the node execution schedule that will be compiled + * into a Shader Operation. */ +using ShaderCompileUnit = VectorSet<DNode>; + +/* ------------------------------------------------------------------------------------------------ + * Shader Operation + * + * An operation that evaluates a shader compiled from a contiguous subset of the node execution + * schedule using the GPU material compiler, see GPU_material.h for more information. The subset + * of the node execution schedule is called a shader compile unit, see the discussion in + * COM_compile_state.hh for more information. + * + * Consider the following node graph with a node execution schedule denoted by the number on each + * node. The compiler may decide to compile a subset of the execution schedule into a shader + * operation, in this case, the nodes from 3 to 5 were compiled together into a shader operation. + * This subset is called the shader compile unit. See the discussion in COM_evaluator.hh for more + * information on the compilation process. Each of the nodes inside the compile unit implements a + * Shader Node which is instantiated, stored in shader_nodes_, and used during compilation. See the + * discussion in COM_shader_node.hh for more information. Links that are internal to the shader + * operation are established between the input and outputs of the shader nodes, for instance, the + * links between nodes 3 and 4 as well as those between nodes 4 and 5. However, links that cross + * the boundary of the shader operation needs special handling. + * + * Shader Operation + * +------------------------------------------------------+ + * .------------. | .------------. .------------. .------------. | .------------. + * | Node 1 | | | Node 3 | | Node 4 | | Node 5 | | | Node 6 | + * | |----|--| |--| |------| |--|--| | + * | | .-|--| | | | .---| | | | | + * '------------' | | '------------' '------------' | '------------' | '------------' + * | +----------------------------------|-------------------+ + * .------------. | | + * | Node 2 | | | + * | |--'------------------------------------' + * | | + * '------------' + * + * Links from nodes that are not part of the shader operation to nodes that are part of the shader + * operation are considered inputs of the operation itself and are declared as such. For instance, + * the link from node 1 to node 3 is declared as an input to the operation, and the same applies + * for the links from node 2 to nodes 3 and 5. Note, however, that only one input is declared for + * each distinct output socket, so both links from node 2 share the same input of the operation. + * An input to the operation is declared for a distinct output socket as follows: + * + * - A texture is added to the shader, which will be bound to the result of the output socket + * during evaluation. + * - A GPU attribute is added to the GPU material for that output socket and is linked to the GPU + * input stack of the inputs linked to the output socket. + * - Code is emitted to initialize the values of the attributes by sampling the textures + * corresponding to each of the inputs. + * - The newly added attribute is mapped to the output socket in output_to_material_attribute_map_ + * to share that same attributes for all inputs linked to the same output socket. + * + * Links from nodes that are part of the shader operation to nodes that are not part of the shader + * operation are considered outputs of the operation itself and are declared as such. For instance, + * the link from node 5 to node 6 is declared as an output to the operation. An output to the + * operation is declared for an output socket as follows: + * + * - An image is added in the shader where the output value will be written. + * - A storer GPU material node that stores the value of the output is added and linked to the GPU + * output stack of the output. The storer will store the value in the image identified by the + * index of the output given to the storer. + * - The storer functions are generated dynamically to map each index with its appropriate image. + * + * The GPU material code generator source is used to construct a compute shader that is then + * dispatched during operation evaluation after binding the inputs, outputs, and any necessary + * resources. */ +class ShaderOperation : public Operation { + private: + /* The compile unit that will be compiled into this shader operation. */ + ShaderCompileUnit compile_unit_; + /* The GPU material backing the operation. This is created and compiled during construction and + * freed during destruction. */ + GPUMaterial *material_; + /* A map that associates each node in the compile unit with an instance of its shader node. */ + Map<DNode, std::unique_ptr<ShaderNode>> shader_nodes_; + /* A map that associates the identifier of each input of the operation with the output socket it + * is linked to. This is needed to help the compiler establish links between operations. */ + Map<std::string, DOutputSocket> inputs_to_linked_outputs_map_; + /* A map that associates the output socket that provides the result of an output of the operation + * with the identifier of that output. This is needed to help the compiler establish links + * between operations. */ + Map<DOutputSocket, std::string> output_sockets_to_output_identifiers_map_; + /* A map that associates the output socket of a node that is not part of the shader operation to + * the attribute that was created for it. This is used to share the same attribute with all + * inputs that are linked to the same output socket. */ + Map<DOutputSocket, GPUNodeLink *> output_to_material_attribute_map_; + + public: + /* Construct and compile a GPU material from the given shader compile unit by calling + * GPU_material_from_callbacks with the appropriate callbacks. */ + ShaderOperation(Context &context, ShaderCompileUnit &compile_unit); + + /* Free the GPU material. */ + ~ShaderOperation(); + + /* Allocate the output results, bind the shader and all its needed resources, then dispatch the + * shader. */ + void execute() override; + + /* Get the identifier of the operation output corresponding to the given output socket. This is + * called by the compiler to identify the operation output that provides the result for an input + * by providing the output socket that the input is linked to. See + * output_sockets_to_output_identifiers_map_ for more information. */ + StringRef get_output_identifier_from_output_socket(DOutputSocket output_socket); + + /* Get a reference to the inputs to linked outputs map of the operation. This is called by the + * compiler to identify the output that each input of the operation is linked to for correct + * input mapping. See inputs_to_linked_outputs_map_ for more information. */ + Map<std::string, DOutputSocket> &get_inputs_to_linked_outputs_map(); + + /* Compute and set the initial reference counts of all the results of the operation. The + * reference counts of the results are the number of operations that use those results, which is + * computed as the number of inputs whose node is part of the schedule and is linked to the + * output corresponding to each of the results of the operation. The node execution schedule is + * given as an input. */ + void compute_results_reference_counts(const Schedule &schedule); + + private: + /* Bind the uniform buffer of the GPU material as well as any color band textures needed by the + * GPU material. The compiled shader of the material is given as an argument and assumed to be + * bound. */ + void bind_material_resources(GPUShader *shader); + + /* Bind the input results of the operation to the appropriate textures in the GPU material. The + * attributes stored in output_to_material_attribute_map_ have names that match the texture + * samplers in the shader as well as the identifiers of the operation inputs that they correspond + * to. The compiled shader of the material is given as an argument and assumed to be bound. */ + void bind_inputs(GPUShader *shader); + + /* Bind the output results of the operation to the appropriate images in the GPU material. The + * name of the images in the shader match the identifier of their corresponding outputs. The + * compiled shader of the material is given as an argument and assumed to be bound. */ + void bind_outputs(GPUShader *shader); + + /* A static callback method of interface ConstructGPUMaterialFn that is passed to + * GPU_material_from_callbacks to construct the GPU material graph. The thunk parameter will be a + * pointer to the instance of ShaderOperation that is being compiled. The method goes over the + * compile unit and does the following for each node: + * + * - Instantiate a ShaderNode from the node and add it to shader_nodes_. + * - Link the inputs of the node if needed. The inputs are either linked to other nodes in the + * GPU material graph or are exposed as inputs to the shader operation itself if they are + * linked to nodes that are not part of the shader operation. + * - Call the compile method of the shader node to actually add and link the GPU material graph + * nodes. + * - If any of the outputs of the node are linked to nodes that are not part of the shader + * operation, they are exposed as outputs to the shader operation itself. */ + static void construct_material(void *thunk, GPUMaterial *material); + + /* Link the inputs of the node if needed. Unlinked inputs are ignored as they will be linked by + * the node compile method. If the input is linked to a node that is not part of the shader + * operation, the input will be exposed as an input to the shader operation and linked to it. + * While if the input is linked to a node that is part of the shader operation, then it is linked + * to that node in the GPU material node graph. */ + void link_node_inputs(DNode node, GPUMaterial *material); + + /* Given the input socket of a node that is part of the shader operation which is linked to the + * given output socket of a node that is also part of the shader operation, just link the output + * link of the GPU node stack of the output socket to the input link of the GPU node stack of the + * input socket. This essentially establishes the needed links in the GPU material node graph. */ + void link_node_input_internal(DInputSocket input_socket, DOutputSocket output_socket); + + /* Given the input socket of a node that is part of the shader operation which is linked to the + * given output socket of a node that is not part of the shader operation, declare a new + * operation input and link it to the input link of the GPU node stack of the input socket. An + * operation input is only declared if no input was already declared for that same output socket + * before. */ + void link_node_input_external(DInputSocket input_socket, + DOutputSocket output_socket, + GPUMaterial *material); + + /* Given the input socket of a node that is part of the shader operation which is linked to the + * given output socket of a node that is not part of the shader operation, declare a new input to + * the operation that is represented in the GPU material by a newly created GPU attribute. It is + * assumed that no operation input was declared for this same output socket before. In the + * generate_code_for_inputs method, a texture will be added in the shader for each of the + * declared inputs, having the same name as the attribute. Additionally, code will be emitted to + * initialize the attributes by sampling their corresponding textures. */ + void declare_operation_input(DInputSocket input_socket, + DOutputSocket output_socket, + GPUMaterial *material); + + /* Populate the output results of the shader operation for output sockets of the given node that + * are linked to nodes outside of the shader operation. */ + void populate_results_for_node(DNode node, GPUMaterial *material); + + /* Given the output socket of a node that is part of the shader operation which is linked to an + * input socket of a node that is not part of the shader operation, declare a new output to the + * operation and link it to an output storer passing in the index of the output. In the + * generate_code_for_outputs method, an image will be added in the shader for each of the + * declared outputs. Additionally, code will be emitted to define the storer functions that store + * the value in the appropriate image identified by the given index. */ + void populate_operation_result(DOutputSocket output_socket, GPUMaterial *material); + + /* A static callback method of interface GPUCodegenCallbackFn that is passed to + * GPU_material_from_callbacks to create the shader create info of the GPU material. The thunk + * parameter will be a pointer to the instance of ShaderOperation that is being compiled. + * + * This method first generates the necessary code to load the inputs and store the outputs. Then, + * it creates a compute shader from the generated sources. Finally, it adds the necessary GPU + * resources to the shader. */ + static void generate_code(void *thunk, GPUMaterial *material, GPUCodegenOutput *code_generator); + + /* Add an image in the shader for each of the declared outputs. Additionally, emit code to define + * the storer functions that store the given value in the appropriate image identified by the + * given index. */ + void generate_code_for_outputs(gpu::shader::ShaderCreateInfo &shader_create_info); + + /* Add a texture will in the shader for each of the declared inputs/attributes in the operation, + * having the same name as the attribute. Additionally, emit code to initialize the attributes by + * sampling their corresponding textures. */ + void generate_code_for_inputs(GPUMaterial *material, + gpu::shader::ShaderCreateInfo &shader_create_info); +}; + +} // namespace blender::realtime_compositor diff --git a/source/blender/compositor/realtime_compositor/COM_simple_operation.hh b/source/blender/compositor/realtime_compositor/COM_simple_operation.hh new file mode 100644 index 00000000000..0061986ce42 --- /dev/null +++ b/source/blender/compositor/realtime_compositor/COM_simple_operation.hh @@ -0,0 +1,64 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#include "BLI_string_ref.hh" + +#include "COM_operation.hh" +#include "COM_result.hh" + +namespace blender::realtime_compositor { + +/* ------------------------------------------------------------------------------------------------ + * Simple Operation + * + * A simple operation is an operation that takes exactly one input and computes exactly one output. + * Moreover, the output is guaranteed to only have a single user, that is, its reference count will + * be one. Such operations can be attached to the inputs of operations to pre-process the inputs to + * prepare them before the operation is executed. */ +class SimpleOperation : public Operation { + private: + /* The identifier of the output. This is constant for all operations. */ + static const StringRef output_identifier_; + /* The identifier of the input. This is constant for all operations. */ + static const StringRef input_identifier_; + + public: + using Operation::Operation; + + /* Get a reference to the output result of the operation, this essentially calls the super + * get_result method with the output identifier of the operation. */ + Result &get_result(); + + /* Map the input of the operation to the given result, this essentially calls the super + * map_input_to_result method with the input identifier of the operation. */ + void map_input_to_result(Result *result); + + protected: + /* Simple operations don't need input processors, so override with an empty implementation. */ + void add_and_evaluate_input_processors() override; + + /* Get a reference to the input result of the operation, this essentially calls the super + * get_result method with the input identifier of the operation. */ + Result &get_input(); + + /* Switch the result mapped to the input with the given result, this essentially calls the super + * switch_result_mapped_to_input method with the input identifier of the operation. */ + void switch_result_mapped_to_input(Result *result); + + /* Populate the result of the operation, this essentially calls the super populate_result method + * with the output identifier of the operation and sets the initial reference count of the result + * to 1, since the result of an operation is guaranteed to have a single user. */ + void populate_result(Result result); + + /* Declare the descriptor of the input of the operation to be the given descriptor, this + * essentially calls the super declare_input_descriptor method with the input identifier of the + * operation. */ + void declare_input_descriptor(InputDescriptor descriptor); + + /* Get a reference to the descriptor of the input, this essentially calls the super + * get_input_descriptor method with the input identifier of the operation. */ + InputDescriptor &get_input_descriptor(); +}; + +} // namespace blender::realtime_compositor diff --git a/source/blender/compositor/realtime_compositor/COM_static_shader_manager.hh b/source/blender/compositor/realtime_compositor/COM_static_shader_manager.hh new file mode 100644 index 00000000000..161a80862a0 --- /dev/null +++ b/source/blender/compositor/realtime_compositor/COM_static_shader_manager.hh @@ -0,0 +1,33 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#include "BLI_map.hh" +#include "BLI_string_ref.hh" + +#include "GPU_shader.h" + +namespace blender::realtime_compositor { + +/* ------------------------------------------------------------------------------------------------- + * Static Shader Manager + * + * A static shader manager is a map of shaders identified by their info name that can be acquired + * and reused throughout the evaluation of the compositor and are only freed when the shader + * manager is destroyed. Once a shader is acquired for the first time, it will be cached in the + * manager to be potentially acquired later if needed without the shader creation overhead. */ +class StaticShaderManager { + private: + /* The set of shaders identified by their info name that are currently available in the manager + * to be acquired. */ + Map<StringRef, GPUShader *> shaders_; + + public: + ~StaticShaderManager(); + + /* Check if there is an available shader with the given info name in the manager, if such shader + * exists, return it, otherwise, return a newly created shader and add it to the manager. */ + GPUShader *get(const char *info_name); +}; + +} // namespace blender::realtime_compositor diff --git a/source/blender/compositor/realtime_compositor/COM_texture_pool.hh b/source/blender/compositor/realtime_compositor/COM_texture_pool.hh new file mode 100644 index 00000000000..cc6641d288f --- /dev/null +++ b/source/blender/compositor/realtime_compositor/COM_texture_pool.hh @@ -0,0 +1,86 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#include <cstdint> + +#include "BLI_map.hh" +#include "BLI_math_vec_types.hh" +#include "BLI_vector.hh" + +#include "GPU_texture.h" + +namespace blender::realtime_compositor { + +/* ------------------------------------------------------------------------------------------------ + * Texture Pool Key + * + * A key used to identify a texture specification in a texture pool. Defines a hash and an equality + * operator for use in a hash map. */ +class TexturePoolKey { + public: + int2 size; + eGPUTextureFormat format; + + /* Construct a key from the given texture size and format. */ + TexturePoolKey(int2 size, eGPUTextureFormat format); + + /* Construct a key from the size and format of the given texture. */ + TexturePoolKey(const GPUTexture *texture); + + uint64_t hash() const; +}; + +bool operator==(const TexturePoolKey &a, const TexturePoolKey &b); + +/* ------------------------------------------------------------------------------------------------ + * Texture Pool + * + * A texture pool allows the allocation and reuse of textures throughout the execution of the + * compositor to avoid memory fragmentation and texture allocation overheads. The texture pool + * delegates the actual texture allocation to an allocate_texture method that should be implemented + * by the caller of the compositor evaluator, allowing a more agnostic and flexible execution that + * can be controlled by the caller. If the compositor is expected to execute frequently, like on + * every redraw, then the allocation method should use a persistent texture pool to allow + * cross-evaluation texture pooling, for instance, by using the DRWTexturePool. But if the + * evaluator is expected to execute infrequently, the allocated textures can just be freed when the + * evaluator is done, that is, when the pool is destructed. */ +class TexturePool { + private: + /* The set of textures in the pool that are available to acquire for each distinct texture + * specification. */ + Map<TexturePoolKey, Vector<GPUTexture *>> textures_; + + public: + /* Check if there is an available texture with the given specification in the pool, if such + * texture exists, return it, otherwise, return a newly allocated texture. Expect the texture to + * be uncleared and possibly contains garbage data. */ + GPUTexture *acquire(int2 size, eGPUTextureFormat format); + + /* Shorthand for acquire with GPU_RGBA16F format. */ + GPUTexture *acquire_color(int2 size); + + /* Shorthand for acquire with GPU_RGBA16F format. Identical to acquire_color because vectors + * are stored in RGBA textures, due to the limited support for RGB textures. */ + GPUTexture *acquire_vector(int2 size); + + /* Shorthand for acquire with GPU_R16F format. */ + GPUTexture *acquire_float(int2 size); + + /* Put the texture back into the pool, potentially to be acquired later by another user. Expects + * the texture to be one that was acquired using the same texture pool. */ + void release(GPUTexture *texture); + + /* Reset the texture pool by clearing all available textures without freeing the textures. If the + * textures will no longer be needed, they should be freed in the destructor. This should be + * called after the compositor is done evaluating. */ + void reset(); + + private: + /* Returns a newly allocated texture with the given specification. This method should be + * implemented by the caller of the compositor evaluator. See the class description for more + * information. */ + virtual GPUTexture *allocate_texture(int2 size, eGPUTextureFormat format) = 0; +}; + +} // namespace blender::realtime_compositor diff --git a/source/blender/compositor/realtime_compositor/COM_utilities.hh b/source/blender/compositor/realtime_compositor/COM_utilities.hh new file mode 100644 index 00000000000..efd1bc2b6b0 --- /dev/null +++ b/source/blender/compositor/realtime_compositor/COM_utilities.hh @@ -0,0 +1,71 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#include "BLI_function_ref.hh" +#include "BLI_math_vec_types.hh" + +#include "NOD_derived_node_tree.hh" + +#include "GPU_shader.h" + +#include "COM_input_descriptor.hh" +#include "COM_result.hh" + +namespace blender::realtime_compositor { + +using namespace nodes::derived_node_tree_types; + +/** + * Get the origin socket of the given node input. If the input is not linked, the socket itself is + * returned. If the input is linked, the socket that is linked to it is returned, which could + * either be an input or an output. An input socket is returned when the given input is connected + * to an unlinked input of a group input node. + */ +DSocket get_input_origin_socket(DInputSocket input); + +/** + * Get the output socket linked to the given node input. If the input is not linked to an output, + * a null output is returned. + */ +DOutputSocket get_output_linked_to_input(DInputSocket input); + +/** Get the result type that corresponds to the type of the given socket. */ +ResultType get_node_socket_result_type(const bNodeSocket *socket); + +/** + * Returns true if any of the nodes linked to the given output satisfies the given condition, + * and false otherwise. + */ +bool is_output_linked_to_node_conditioned(DOutputSocket output, + FunctionRef<bool(DNode)> condition); + +/** Returns the number of inputs linked to the given output that satisfy the given condition. */ +int number_of_inputs_linked_to_output_conditioned(DOutputSocket output, + FunctionRef<bool(DInputSocket)> condition); + +/** A node is a shader node if it defines a method to get a shader node operation. */ +bool is_shader_node(DNode node); + +/** + * Returns true if the given node is supported, that is, have an implementation. + * Returns false otherwise. + */ +bool is_node_supported(DNode node); + +/** Get the input descriptor of the given input socket. */ +InputDescriptor input_descriptor_from_input_socket(const bNodeSocket *socket); + +/** + * Dispatch the given compute shader in a 2D compute space such that the number of threads in both + * dimensions is as small as possible but at least covers the entirety of threads_range assuming + * the shader has a local group size given by local_size. That means that the number of threads + * might be a bit larger than threads_range, so shaders has to put that into consideration. A + * default local size of 16x16 is assumed, which is the optimal local size for many image + * processing shaders. + */ +void compute_dispatch_threads_at_least(GPUShader *shader, + int2 threads_range, + int2 local_size = int2(16)); + +} // namespace blender::realtime_compositor diff --git a/source/blender/compositor/realtime_compositor/intern/compile_state.cc b/source/blender/compositor/realtime_compositor/intern/compile_state.cc new file mode 100644 index 00000000000..5fa2fc9d544 --- /dev/null +++ b/source/blender/compositor/realtime_compositor/intern/compile_state.cc @@ -0,0 +1,168 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include <limits> + +#include "BLI_math_vec_types.hh" + +#include "DNA_node_types.h" + +#include "NOD_derived_node_tree.hh" + +#include "COM_compile_state.hh" +#include "COM_domain.hh" +#include "COM_input_descriptor.hh" +#include "COM_node_operation.hh" +#include "COM_result.hh" +#include "COM_scheduler.hh" +#include "COM_shader_operation.hh" +#include "COM_utilities.hh" + +namespace blender::realtime_compositor { + +using namespace nodes::derived_node_tree_types; + +CompileState::CompileState(const Schedule &schedule) : schedule_(schedule) +{ +} + +const Schedule &CompileState::get_schedule() +{ + return schedule_; +} + +void CompileState::map_node_to_node_operation(DNode node, NodeOperation *operations) +{ + return node_operations_.add_new(node, operations); +} + +void CompileState::map_node_to_shader_operation(DNode node, ShaderOperation *operations) +{ + return shader_operations_.add_new(node, operations); +} + +Result &CompileState::get_result_from_output_socket(DOutputSocket output) +{ + /* The output belongs to a node that was compiled into a standard node operation, so return a + * reference to the result from that operation using the output identifier. */ + if (node_operations_.contains(output.node())) { + NodeOperation *operation = node_operations_.lookup(output.node()); + return operation->get_result(output->identifier); + } + + /* Otherwise, the output belongs to a node that was compiled into a shader operation, so + * retrieve the internal identifier of that output and return a reference to the result from + * that operation using the retrieved identifier. */ + ShaderOperation *operation = shader_operations_.lookup(output.node()); + return operation->get_result(operation->get_output_identifier_from_output_socket(output)); +} + +void CompileState::add_node_to_shader_compile_unit(DNode node) +{ + shader_compile_unit_.add_new(node); + + /* If the domain of the shader compile unit is not yet determined or was determined to be + * an identity domain, update it to be the computed domain of the node. */ + if (shader_compile_unit_domain_ == Domain::identity()) { + shader_compile_unit_domain_ = compute_shader_node_domain(node); + } +} + +ShaderCompileUnit &CompileState::get_shader_compile_unit() +{ + return shader_compile_unit_; +} + +void CompileState::reset_shader_compile_unit() +{ + return shader_compile_unit_.clear(); +} + +bool CompileState::should_compile_shader_compile_unit(DNode node) +{ + /* If the shader compile unit is empty, then it can't be compiled yet. */ + if (shader_compile_unit_.is_empty()) { + return false; + } + + /* If the node is not a shader node, then it can't be added to the shader compile unit and the + * shader compile unit is considered complete and should be compiled. */ + if (!is_shader_node(node)) { + return true; + } + + /* If the computed domain of the node doesn't matches the domain of the shader compile unit, then + * it can't be added to the shader compile unit and the shader compile unit is considered + * complete and should be compiled. Identity domains are an exception as they are always + * compatible because they represents single values. */ + if (shader_compile_unit_domain_ != Domain::identity() && + shader_compile_unit_domain_ != compute_shader_node_domain(node)) { + return true; + } + + /* Otherwise, the node is compatible and can be added to the compile unit and it shouldn't be + * compiled just yet. */ + return false; +} + +Domain CompileState::compute_shader_node_domain(DNode node) +{ + /* Default to an identity domain in case no domain input was found, most likely because all + * inputs are single values. */ + Domain node_domain = Domain::identity(); + int current_domain_priority = std::numeric_limits<int>::max(); + + /* Go over the inputs and find the domain of the non single value input with the highest domain + * priority. */ + for (const bNodeSocket *input : node->input_sockets()) { + const DInputSocket dinput{node.context(), input}; + + /* Get the output linked to the input. If it is null, that means the input is unlinked, so skip + * it. */ + const DOutputSocket output = get_output_linked_to_input(dinput); + if (!output) { + continue; + } + + const InputDescriptor input_descriptor = input_descriptor_from_input_socket(input); + + /* If the output belongs to a node that is part of the shader compile unit, then the domain of + * the input is the domain of the compile unit itself. */ + if (shader_compile_unit_.contains(output.node())) { + /* Single value inputs can't be domain inputs. */ + if (shader_compile_unit_domain_.size == int2(1)) { + continue; + } + + /* Notice that the lower the domain priority value is, the higher the priority is, hence the + * less than comparison. */ + if (input_descriptor.domain_priority < current_domain_priority) { + node_domain = shader_compile_unit_domain_; + current_domain_priority = input_descriptor.domain_priority; + } + continue; + } + + const Result &result = get_result_from_output_socket(output); + + /* A single value input can't be a domain input. */ + if (result.is_single_value() || input_descriptor.expects_single_value) { + continue; + } + + /* An input that skips realization can't be a domain input. */ + if (input_descriptor.skip_realization) { + continue; + } + + /* Notice that the lower the domain priority value is, the higher the priority is, hence the + * less than comparison. */ + if (input_descriptor.domain_priority < current_domain_priority) { + node_domain = result.domain(); + current_domain_priority = input_descriptor.domain_priority; + } + } + + return node_domain; +} + +} // namespace blender::realtime_compositor diff --git a/source/blender/compositor/realtime_compositor/intern/context.cc b/source/blender/compositor/realtime_compositor/intern/context.cc new file mode 100644 index 00000000000..64ac29af3d1 --- /dev/null +++ b/source/blender/compositor/realtime_compositor/intern/context.cc @@ -0,0 +1,36 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "COM_context.hh" +#include "COM_static_shader_manager.hh" +#include "COM_texture_pool.hh" + +namespace blender::realtime_compositor { + +Context::Context(TexturePool &texture_pool) : texture_pool_(texture_pool) +{ +} + +int Context::get_frame_number() const +{ + return get_scene()->r.cfra; +} + +float Context::get_time() const +{ + const float frame_number = static_cast<float>(get_frame_number()); + const float frame_rate = static_cast<float>(get_scene()->r.frs_sec) / + static_cast<float>(get_scene()->r.frs_sec_base); + return frame_number / frame_rate; +} + +TexturePool &Context::texture_pool() +{ + return texture_pool_; +} + +StaticShaderManager &Context::shader_manager() +{ + return shader_manager_; +} + +} // namespace blender::realtime_compositor diff --git a/source/blender/compositor/realtime_compositor/intern/conversion_operation.cc b/source/blender/compositor/realtime_compositor/intern/conversion_operation.cc new file mode 100644 index 00000000000..3743b9bba87 --- /dev/null +++ b/source/blender/compositor/realtime_compositor/intern/conversion_operation.cc @@ -0,0 +1,239 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "BLI_math_vec_types.hh" + +#include "GPU_shader.h" + +#include "COM_context.hh" +#include "COM_conversion_operation.hh" +#include "COM_input_descriptor.hh" +#include "COM_result.hh" +#include "COM_utilities.hh" + +namespace blender::realtime_compositor { + +/* -------------------------------------------------------------------- */ +/** \name Conversion Operation + * \{ */ + +void ConversionOperation::execute() +{ + Result &result = get_result(); + const Result &input = get_input(); + + if (input.is_single_value()) { + result.allocate_single_value(); + execute_single(input, result); + return; + } + + result.allocate_texture(input.domain()); + + GPUShader *shader = get_conversion_shader(); + GPU_shader_bind(shader); + + input.bind_as_texture(shader, "input_tx"); + result.bind_as_image(shader, "output_img"); + + compute_dispatch_threads_at_least(shader, input.domain().size); + + input.unbind_as_texture(); + result.unbind_as_image(); + GPU_shader_unbind(); +} + +SimpleOperation *ConversionOperation::construct_if_needed(Context &context, + const Result &input_result, + const InputDescriptor &input_descriptor) +{ + ResultType result_type = input_result.type(); + ResultType expected_type = input_descriptor.type; + + /* If the result type differs from the expected type, return an instance of an appropriate + * conversion operation. Otherwise, return a null pointer. */ + + if (result_type == ResultType::Float && expected_type == ResultType::Vector) { + return new ConvertFloatToVectorOperation(context); + } + + if (result_type == ResultType::Float && expected_type == ResultType::Color) { + return new ConvertFloatToColorOperation(context); + } + + if (result_type == ResultType::Color && expected_type == ResultType::Float) { + return new ConvertColorToFloatOperation(context); + } + + if (result_type == ResultType::Color && expected_type == ResultType::Vector) { + return new ConvertColorToVectorOperation(context); + } + + if (result_type == ResultType::Vector && expected_type == ResultType::Float) { + return new ConvertVectorToFloatOperation(context); + } + + if (result_type == ResultType::Vector && expected_type == ResultType::Color) { + return new ConvertVectorToColorOperation(context); + } + + return nullptr; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Convert Float to Vector Operation + * \{ */ + +ConvertFloatToVectorOperation::ConvertFloatToVectorOperation(Context &context) + : ConversionOperation(context) +{ + InputDescriptor input_descriptor; + input_descriptor.type = ResultType::Float; + declare_input_descriptor(input_descriptor); + populate_result(Result(ResultType::Vector, texture_pool())); +} + +void ConvertFloatToVectorOperation::execute_single(const Result &input, Result &output) +{ + output.set_vector_value(float3(input.get_float_value())); +} + +GPUShader *ConvertFloatToVectorOperation::get_conversion_shader() const +{ + return shader_manager().get("compositor_convert_float_to_vector"); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Convert Float to Color Operation + * \{ */ + +ConvertFloatToColorOperation::ConvertFloatToColorOperation(Context &context) + : ConversionOperation(context) +{ + InputDescriptor input_descriptor; + input_descriptor.type = ResultType::Float; + declare_input_descriptor(input_descriptor); + populate_result(Result(ResultType::Color, texture_pool())); +} + +void ConvertFloatToColorOperation::execute_single(const Result &input, Result &output) +{ + float4 color = float4(input.get_float_value()); + color[3] = 1.0f; + output.set_color_value(color); +} + +GPUShader *ConvertFloatToColorOperation::get_conversion_shader() const +{ + return shader_manager().get("compositor_convert_float_to_color"); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Convert Color to Float Operation + * \{ */ + +ConvertColorToFloatOperation::ConvertColorToFloatOperation(Context &context) + : ConversionOperation(context) +{ + InputDescriptor input_descriptor; + input_descriptor.type = ResultType::Color; + declare_input_descriptor(input_descriptor); + populate_result(Result(ResultType::Float, texture_pool())); +} + +void ConvertColorToFloatOperation::execute_single(const Result &input, Result &output) +{ + float4 color = input.get_color_value(); + output.set_float_value((color[0] + color[1] + color[2]) / 3.0f); +} + +GPUShader *ConvertColorToFloatOperation::get_conversion_shader() const +{ + return shader_manager().get("compositor_convert_color_to_float"); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Convert Color to Vector Operation + * \{ */ + +ConvertColorToVectorOperation::ConvertColorToVectorOperation(Context &context) + : ConversionOperation(context) +{ + InputDescriptor input_descriptor; + input_descriptor.type = ResultType::Color; + declare_input_descriptor(input_descriptor); + populate_result(Result(ResultType::Vector, texture_pool())); +} + +void ConvertColorToVectorOperation::execute_single(const Result &input, Result &output) +{ + float4 color = input.get_color_value(); + output.set_vector_value(float3(color)); +} + +GPUShader *ConvertColorToVectorOperation::get_conversion_shader() const +{ + return shader_manager().get("compositor_convert_color_to_vector"); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Convert Vector to Float Operation + * \{ */ + +ConvertVectorToFloatOperation::ConvertVectorToFloatOperation(Context &context) + : ConversionOperation(context) +{ + InputDescriptor input_descriptor; + input_descriptor.type = ResultType::Vector; + declare_input_descriptor(input_descriptor); + populate_result(Result(ResultType::Float, texture_pool())); +} + +void ConvertVectorToFloatOperation::execute_single(const Result &input, Result &output) +{ + float3 vector = input.get_vector_value(); + output.set_float_value((vector[0] + vector[1] + vector[2]) / 3.0f); +} + +GPUShader *ConvertVectorToFloatOperation::get_conversion_shader() const +{ + return shader_manager().get("compositor_convert_vector_to_float"); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Convert Vector to Color Operation + * \{ */ + +ConvertVectorToColorOperation::ConvertVectorToColorOperation(Context &context) + : ConversionOperation(context) +{ + InputDescriptor input_descriptor; + input_descriptor.type = ResultType::Vector; + declare_input_descriptor(input_descriptor); + populate_result(Result(ResultType::Color, texture_pool())); +} + +void ConvertVectorToColorOperation::execute_single(const Result &input, Result &output) +{ + output.set_color_value(float4(input.get_vector_value(), 1.0f)); +} + +GPUShader *ConvertVectorToColorOperation::get_conversion_shader() const +{ + return shader_manager().get("compositor_convert_vector_to_color"); +} + +/** \} */ + +} // namespace blender::realtime_compositor diff --git a/source/blender/compositor/realtime_compositor/intern/domain.cc b/source/blender/compositor/realtime_compositor/intern/domain.cc new file mode 100644 index 00000000000..31b297c212e --- /dev/null +++ b/source/blender/compositor/realtime_compositor/intern/domain.cc @@ -0,0 +1,38 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "BLI_float3x3.hh" +#include "BLI_math_vec_types.hh" + +#include "COM_domain.hh" + +namespace blender::realtime_compositor { + +Domain::Domain(int2 size) : size(size), transformation(float3x3::identity()) +{ +} + +Domain::Domain(int2 size, float3x3 transformation) : size(size), transformation(transformation) +{ +} + +void Domain::transform(const float3x3 &input_transformation) +{ + transformation = input_transformation * transformation; +} + +Domain Domain::identity() +{ + return Domain(int2(1), float3x3::identity()); +} + +bool operator==(const Domain &a, const Domain &b) +{ + return a.size == b.size && a.transformation == b.transformation; +} + +bool operator!=(const Domain &a, const Domain &b) +{ + return !(a == b); +} + +} // namespace blender::realtime_compositor diff --git a/source/blender/compositor/realtime_compositor/intern/evaluator.cc b/source/blender/compositor/realtime_compositor/intern/evaluator.cc new file mode 100644 index 00000000000..48457bec199 --- /dev/null +++ b/source/blender/compositor/realtime_compositor/intern/evaluator.cc @@ -0,0 +1,170 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include <string> + +#include "DNA_node_types.h" + +#include "NOD_derived_node_tree.hh" + +#include "COM_compile_state.hh" +#include "COM_context.hh" +#include "COM_evaluator.hh" +#include "COM_input_single_value_operation.hh" +#include "COM_node_operation.hh" +#include "COM_operation.hh" +#include "COM_result.hh" +#include "COM_scheduler.hh" +#include "COM_shader_operation.hh" +#include "COM_utilities.hh" + +namespace blender::realtime_compositor { + +using namespace nodes::derived_node_tree_types; + +Evaluator::Evaluator(Context &context, bNodeTree &node_tree) + : context_(context), node_tree_(node_tree) +{ +} + +void Evaluator::evaluate() +{ + context_.texture_pool().reset(); + + if (!is_compiled_) { + compile_and_evaluate(); + is_compiled_ = true; + return; + } + + for (const std::unique_ptr<Operation> &operation : operations_stream_) { + operation->evaluate(); + } +} + +void Evaluator::reset() +{ + operations_stream_.clear(); + derived_node_tree_.reset(); + + is_compiled_ = false; +} + +bool Evaluator::validate_node_tree() +{ + if (derived_node_tree_->has_link_cycles()) { + context_.set_info_message("Compositor node tree has cyclic links!"); + return false; + } + + if (derived_node_tree_->has_undefined_nodes_or_sockets()) { + context_.set_info_message("Compositor node tree has undefined nodes or sockets!"); + return false; + } + + return true; +} + +void Evaluator::compile_and_evaluate() +{ + derived_node_tree_ = std::make_unique<DerivedNodeTree>(node_tree_); + + if (!validate_node_tree()) { + return; + } + + const Schedule schedule = compute_schedule(*derived_node_tree_); + + CompileState compile_state(schedule); + + for (const DNode &node : schedule) { + if (compile_state.should_compile_shader_compile_unit(node)) { + compile_and_evaluate_shader_compile_unit(compile_state); + } + + if (is_shader_node(node)) { + compile_state.add_node_to_shader_compile_unit(node); + } + else { + compile_and_evaluate_node(node, compile_state); + } + } +} + +void Evaluator::compile_and_evaluate_node(DNode node, CompileState &compile_state) +{ + NodeOperation *operation = node->typeinfo->get_compositor_operation(context_, node); + + compile_state.map_node_to_node_operation(node, operation); + + map_node_operation_inputs_to_their_results(node, operation, compile_state); + + /* This has to be done after input mapping because the method may add Input Single Value + * Operations to the operations stream, which needs to be evaluated before the operation itself + * is evaluated. */ + operations_stream_.append(std::unique_ptr<Operation>(operation)); + + operation->compute_results_reference_counts(compile_state.get_schedule()); + + operation->evaluate(); +} + +void Evaluator::map_node_operation_inputs_to_their_results(DNode node, + NodeOperation *operation, + CompileState &compile_state) +{ + for (const bNodeSocket *input : node->input_sockets()) { + const DInputSocket dinput{node.context(), input}; + + DSocket dorigin = get_input_origin_socket(dinput); + + /* The origin socket is an output, which means the input is linked. So map the input to the + * result we get from the output. */ + if (dorigin->is_output()) { + Result &result = compile_state.get_result_from_output_socket(DOutputSocket(dorigin)); + operation->map_input_to_result(input->identifier, &result); + continue; + } + + /* Otherwise, the origin socket is an input, which either means the input is unlinked and the + * origin is the input socket itself or the input is connected to an unlinked input of a group + * input node and the origin is the input of the group input node. So map the input to the + * result of a newly created Input Single Value Operation. */ + auto *input_operation = new InputSingleValueOperation(context_, DInputSocket(dorigin)); + operation->map_input_to_result(input->identifier, &input_operation->get_result()); + + operations_stream_.append(std::unique_ptr<InputSingleValueOperation>(input_operation)); + + input_operation->evaluate(); + } +} + +void Evaluator::compile_and_evaluate_shader_compile_unit(CompileState &compile_state) +{ + ShaderCompileUnit &compile_unit = compile_state.get_shader_compile_unit(); + ShaderOperation *operation = new ShaderOperation(context_, compile_unit); + + for (DNode node : compile_unit) { + compile_state.map_node_to_shader_operation(node, operation); + } + + map_shader_operation_inputs_to_their_results(operation, compile_state); + + operations_stream_.append(std::unique_ptr<Operation>(operation)); + + operation->compute_results_reference_counts(compile_state.get_schedule()); + + operation->evaluate(); + + compile_state.reset_shader_compile_unit(); +} + +void Evaluator::map_shader_operation_inputs_to_their_results(ShaderOperation *operation, + CompileState &compile_state) +{ + for (const auto &item : operation->get_inputs_to_linked_outputs_map().items()) { + Result &result = compile_state.get_result_from_output_socket(item.value); + operation->map_input_to_result(item.key, &result); + } +} + +} // namespace blender::realtime_compositor diff --git a/source/blender/compositor/realtime_compositor/intern/input_single_value_operation.cc b/source/blender/compositor/realtime_compositor/intern/input_single_value_operation.cc new file mode 100644 index 00000000000..b3cc86b5f79 --- /dev/null +++ b/source/blender/compositor/realtime_compositor/intern/input_single_value_operation.cc @@ -0,0 +1,59 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "BLI_math_vec_types.hh" + +#include "COM_input_single_value_operation.hh" +#include "COM_operation.hh" +#include "COM_result.hh" +#include "COM_utilities.hh" + +namespace blender::realtime_compositor { + +const StringRef InputSingleValueOperation::output_identifier_ = StringRef("Output"); + +InputSingleValueOperation::InputSingleValueOperation(Context &context, DInputSocket input_socket) + : Operation(context), input_socket_(input_socket) +{ + const ResultType result_type = get_node_socket_result_type(input_socket_.bsocket()); + Result result = Result(result_type, texture_pool()); + + /* The result of an input single value operation is guaranteed to have a single user. */ + result.set_initial_reference_count(1); + + populate_result(result); +} + +void InputSingleValueOperation::execute() +{ + /* Allocate a single value for the result. */ + Result &result = get_result(); + result.allocate_single_value(); + + const bNodeSocket *bsocket = input_socket_.bsocket(); + + /* Set the value of the result to the default value of the input socket. */ + switch (result.type()) { + case ResultType::Float: + result.set_float_value(bsocket->default_value_typed<bNodeSocketValueFloat>()->value); + break; + case ResultType::Vector: + result.set_vector_value( + float3(bsocket->default_value_typed<bNodeSocketValueVector>()->value)); + break; + case ResultType::Color: + result.set_color_value(float4(bsocket->default_value_typed<bNodeSocketValueRGBA>()->value)); + break; + } +} + +Result &InputSingleValueOperation::get_result() +{ + return Operation::get_result(output_identifier_); +} + +void InputSingleValueOperation::populate_result(Result result) +{ + Operation::populate_result(output_identifier_, result); +} + +} // namespace blender::realtime_compositor diff --git a/source/blender/compositor/realtime_compositor/intern/node_operation.cc b/source/blender/compositor/realtime_compositor/intern/node_operation.cc new file mode 100644 index 00000000000..1c20c967ddb --- /dev/null +++ b/source/blender/compositor/realtime_compositor/intern/node_operation.cc @@ -0,0 +1,67 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include <memory> + +#include "BLI_map.hh" +#include "BLI_string_ref.hh" +#include "BLI_vector.hh" + +#include "DNA_node_types.h" + +#include "NOD_derived_node_tree.hh" +#include "NOD_node_declaration.hh" + +#include "COM_context.hh" +#include "COM_input_descriptor.hh" +#include "COM_node_operation.hh" +#include "COM_operation.hh" +#include "COM_result.hh" +#include "COM_scheduler.hh" +#include "COM_utilities.hh" + +namespace blender::realtime_compositor { + +using namespace nodes::derived_node_tree_types; + +NodeOperation::NodeOperation(Context &context, DNode node) : Operation(context), node_(node) +{ + for (const bNodeSocket *output : node->output_sockets()) { + const ResultType result_type = get_node_socket_result_type(output); + const Result result = Result(result_type, texture_pool()); + populate_result(output->identifier, result); + } + + for (const bNodeSocket *input : node->input_sockets()) { + const InputDescriptor input_descriptor = input_descriptor_from_input_socket(input); + declare_input_descriptor(input->identifier, input_descriptor); + } +} + +void NodeOperation::compute_results_reference_counts(const Schedule &schedule) +{ + for (const bNodeSocket *output : this->node()->output_sockets()) { + const DOutputSocket doutput{node().context(), output}; + + const int reference_count = number_of_inputs_linked_to_output_conditioned( + doutput, [&](DInputSocket input) { return schedule.contains(input.node()); }); + + get_result(doutput->identifier).set_initial_reference_count(reference_count); + } +} + +const DNode &NodeOperation::node() const +{ + return node_; +} + +const bNode &NodeOperation::bnode() const +{ + return *node_; +} + +bool NodeOperation::should_compute_output(StringRef identifier) +{ + return get_result(identifier).should_compute(); +} + +} // namespace blender::realtime_compositor diff --git a/source/blender/compositor/realtime_compositor/intern/operation.cc b/source/blender/compositor/realtime_compositor/intern/operation.cc new file mode 100644 index 00000000000..832196cc5ef --- /dev/null +++ b/source/blender/compositor/realtime_compositor/intern/operation.cc @@ -0,0 +1,206 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include <limits> +#include <memory> + +#include "BLI_map.hh" +#include "BLI_string_ref.hh" +#include "BLI_vector.hh" + +#include "COM_context.hh" +#include "COM_conversion_operation.hh" +#include "COM_domain.hh" +#include "COM_input_descriptor.hh" +#include "COM_operation.hh" +#include "COM_realize_on_domain_operation.hh" +#include "COM_reduce_to_single_value_operation.hh" +#include "COM_result.hh" +#include "COM_simple_operation.hh" +#include "COM_static_shader_manager.hh" +#include "COM_texture_pool.hh" + +namespace blender::realtime_compositor { + +Operation::Operation(Context &context) : context_(context) +{ +} + +Operation::~Operation() = default; + +void Operation::evaluate() +{ + evaluate_input_processors(); + + reset_results(); + + execute(); + + release_inputs(); +} + +Result &Operation::get_result(StringRef identifier) +{ + return results_.lookup(identifier); +} + +void Operation::map_input_to_result(StringRef identifier, Result *result) +{ + results_mapped_to_inputs_.add_new(identifier, result); +} + +Domain Operation::compute_domain() +{ + /* Default to an identity domain in case no domain input was found, most likely because all + * inputs are single values. */ + Domain operation_domain = Domain::identity(); + int current_domain_priority = std::numeric_limits<int>::max(); + + /* Go over the inputs and find the domain of the non single value input with the highest domain + * priority. */ + for (StringRef identifier : input_descriptors_.keys()) { + const Result &result = get_input(identifier); + const InputDescriptor &descriptor = get_input_descriptor(identifier); + + /* A single value input can't be a domain input. */ + if (result.is_single_value() || descriptor.expects_single_value) { + continue; + } + + /* An input that skips realization can't be a domain input. */ + if (descriptor.skip_realization) { + continue; + } + + /* Notice that the lower the domain priority value is, the higher the priority is, hence the + * less than comparison. */ + if (descriptor.domain_priority < current_domain_priority) { + operation_domain = result.domain(); + current_domain_priority = descriptor.domain_priority; + } + } + + return operation_domain; +} + +void Operation::add_and_evaluate_input_processors() +{ + /* Each input processor type is added to all inputs entirely before the next type. This is done + * because the construction of the input processors may depend on the result of previous input + * processors for all inputs. For instance, the realize on domain input processor considers the + * value of all inputs, so previous input processors for all inputs needs to be added and + * evaluated first. */ + + for (const StringRef &identifier : results_mapped_to_inputs_.keys()) { + SimpleOperation *single_value = ReduceToSingleValueOperation::construct_if_needed( + context(), get_input(identifier)); + add_and_evaluate_input_processor(identifier, single_value); + } + + for (const StringRef &identifier : results_mapped_to_inputs_.keys()) { + SimpleOperation *conversion = ConversionOperation::construct_if_needed( + context(), get_input(identifier), get_input_descriptor(identifier)); + add_and_evaluate_input_processor(identifier, conversion); + } + + for (const StringRef &identifier : results_mapped_to_inputs_.keys()) { + SimpleOperation *realize_on_domain = RealizeOnDomainOperation::construct_if_needed( + context(), get_input(identifier), get_input_descriptor(identifier), compute_domain()); + add_and_evaluate_input_processor(identifier, realize_on_domain); + } +} + +void Operation::add_and_evaluate_input_processor(StringRef identifier, SimpleOperation *processor) +{ + /* Allow null inputs to facilitate construct_if_needed pattern of addition. For instance, see the + * implementation of the add_and_evaluate_input_processors method. */ + if (!processor) { + return; + } + + ProcessorsVector &processors = input_processors_.lookup_or_add_default(identifier); + + /* Get the result that should serve as the input for the processor. This is either the result + * mapped to the input or the result of the last processor depending on whether this is the first + * processor or not. */ + Result &result = processors.is_empty() ? get_input(identifier) : processors.last()->get_result(); + + /* Map the input result of the processor and add it to the processors vector. */ + processor->map_input_to_result(&result); + processors.append(std::unique_ptr<SimpleOperation>(processor)); + + /* Switch the result mapped to the input to be the output result of the processor. */ + switch_result_mapped_to_input(identifier, &processor->get_result()); + + processor->evaluate(); +} + +Result &Operation::get_input(StringRef identifier) const +{ + return *results_mapped_to_inputs_.lookup(identifier); +} + +void Operation::switch_result_mapped_to_input(StringRef identifier, Result *result) +{ + results_mapped_to_inputs_.lookup(identifier) = result; +} + +void Operation::populate_result(StringRef identifier, Result result) +{ + results_.add_new(identifier, result); +} + +void Operation::declare_input_descriptor(StringRef identifier, InputDescriptor descriptor) +{ + input_descriptors_.add_new(identifier, descriptor); +} + +InputDescriptor &Operation::get_input_descriptor(StringRef identifier) +{ + return input_descriptors_.lookup(identifier); +} + +Context &Operation::context() +{ + return context_; +} + +TexturePool &Operation::texture_pool() const +{ + return context_.texture_pool(); +} + +StaticShaderManager &Operation::shader_manager() const +{ + return context_.shader_manager(); +} + +void Operation::evaluate_input_processors() +{ + if (!input_processors_added_) { + add_and_evaluate_input_processors(); + input_processors_added_ = true; + return; + } + + for (const ProcessorsVector &processors : input_processors_.values()) { + for (const std::unique_ptr<SimpleOperation> &processor : processors) { + processor->evaluate(); + } + } +} + +void Operation::reset_results() +{ + for (Result &result : results_.values()) { + result.reset(); + } +} + +void Operation::release_inputs() +{ + for (Result *result : results_mapped_to_inputs_.values()) { + result->release(); + } +} + +} // namespace blender::realtime_compositor diff --git a/source/blender/compositor/realtime_compositor/intern/realize_on_domain_operation.cc b/source/blender/compositor/realtime_compositor/intern/realize_on_domain_operation.cc new file mode 100644 index 00000000000..817293c0fa6 --- /dev/null +++ b/source/blender/compositor/realtime_compositor/intern/realize_on_domain_operation.cc @@ -0,0 +1,130 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "BLI_float3x3.hh" +#include "BLI_math_vec_types.hh" +#include "BLI_utildefines.h" + +#include "GPU_shader.h" +#include "GPU_texture.h" + +#include "COM_context.hh" +#include "COM_domain.hh" +#include "COM_input_descriptor.hh" +#include "COM_realize_on_domain_operation.hh" +#include "COM_result.hh" +#include "COM_utilities.hh" + +namespace blender::realtime_compositor { + +RealizeOnDomainOperation::RealizeOnDomainOperation(Context &context, + Domain domain, + ResultType type) + : SimpleOperation(context), domain_(domain) +{ + InputDescriptor input_descriptor; + input_descriptor.type = type; + declare_input_descriptor(input_descriptor); + populate_result(Result(type, texture_pool())); +} + +void RealizeOnDomainOperation::execute() +{ + Result &input = get_input(); + Result &result = get_result(); + + result.allocate_texture(domain_); + + GPUShader *shader = get_realization_shader(); + GPU_shader_bind(shader); + + /* Transform the input space into the domain space. */ + const float3x3 local_transformation = input.domain().transformation * + domain_.transformation.inverted(); + + /* Set the origin of the transformation to be the center of the domain. */ + const float3x3 transformation = float3x3::from_origin_transformation( + local_transformation, float2(domain_.size) / 2.0f); + + /* Invert the transformation because the shader transforms the domain coordinates instead of the + * input image itself and thus expect the inverse. */ + const float3x3 inverse_transformation = transformation.inverted(); + + GPU_shader_uniform_mat3_as_mat4(shader, "inverse_transformation", inverse_transformation.ptr()); + + /* The texture sampler should use bilinear interpolation for both the bilinear and bicubic + * cases, as the logic used by the bicubic realization shader expects textures to use bilinear + * interpolation. */ + const bool use_bilinear = ELEM(input.get_realization_options().interpolation, + Interpolation::Bilinear, + Interpolation::Bicubic); + GPU_texture_filter_mode(input.texture(), use_bilinear); + + /* Make out-of-bound texture access return zero by clamping to border color. And make texture + * wrap appropriately if the input repeats. */ + const bool repeats = input.get_realization_options().repeat_x || + input.get_realization_options().repeat_y; + GPU_texture_wrap_mode(input.texture(), repeats, false); + + input.bind_as_texture(shader, "input_tx"); + result.bind_as_image(shader, "domain_img"); + + compute_dispatch_threads_at_least(shader, domain_.size); + + input.unbind_as_texture(); + result.unbind_as_image(); + GPU_shader_unbind(); +} + +GPUShader *RealizeOnDomainOperation::get_realization_shader() +{ + switch (get_result().type()) { + case ResultType::Color: + return shader_manager().get("compositor_realize_on_domain_color"); + case ResultType::Vector: + return shader_manager().get("compositor_realize_on_domain_vector"); + case ResultType::Float: + return shader_manager().get("compositor_realize_on_domain_float"); + } + + BLI_assert_unreachable(); + return nullptr; +} + +Domain RealizeOnDomainOperation::compute_domain() +{ + return domain_; +} + +SimpleOperation *RealizeOnDomainOperation::construct_if_needed( + Context &context, + const Result &input_result, + const InputDescriptor &input_descriptor, + const Domain &operation_domain) +{ + /* This input wants to skip realization, the operation is not needed. */ + if (input_descriptor.skip_realization) { + return nullptr; + } + + /* The input expects a single value and if no single value is provided, it will be ignored and a + * default value will be used, so no need to realize it and the operation is not needed. */ + if (input_descriptor.expects_single_value) { + return nullptr; + } + + /* Input result is a single value and does not need realization, the operation is not needed. */ + if (input_result.is_single_value()) { + return nullptr; + } + + /* The input have an identical domain to the operation domain, so no need to realize it and the + * operation is not needed. */ + if (input_result.domain() == operation_domain) { + return nullptr; + } + + /* Otherwise, realization is needed. */ + return new RealizeOnDomainOperation(context, operation_domain, input_descriptor.type); +} + +} // namespace blender::realtime_compositor diff --git a/source/blender/compositor/realtime_compositor/intern/reduce_to_single_value_operation.cc b/source/blender/compositor/realtime_compositor/intern/reduce_to_single_value_operation.cc new file mode 100644 index 00000000000..acc9b4ab7d6 --- /dev/null +++ b/source/blender/compositor/realtime_compositor/intern/reduce_to_single_value_operation.cc @@ -0,0 +1,67 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "GPU_state.h" +#include "GPU_texture.h" + +#include "MEM_guardedalloc.h" + +#include "COM_context.hh" +#include "COM_input_descriptor.hh" +#include "COM_reduce_to_single_value_operation.hh" +#include "COM_result.hh" + +namespace blender::realtime_compositor { + +ReduceToSingleValueOperation::ReduceToSingleValueOperation(Context &context, ResultType type) + : SimpleOperation(context) +{ + InputDescriptor input_descriptor; + input_descriptor.type = type; + declare_input_descriptor(input_descriptor); + populate_result(Result(type, texture_pool())); +} + +void ReduceToSingleValueOperation::execute() +{ + /* Make sure any prior writes to the texture are reflected before downloading it. */ + GPU_memory_barrier(GPU_BARRIER_TEXTURE_UPDATE); + + const Result &input = get_input(); + float *pixel = static_cast<float *>(GPU_texture_read(input.texture(), GPU_DATA_FLOAT, 0)); + + Result &result = get_result(); + result.allocate_single_value(); + switch (result.type()) { + case ResultType::Color: + result.set_color_value(pixel); + break; + case ResultType::Vector: + result.set_vector_value(pixel); + break; + case ResultType::Float: + result.set_float_value(*pixel); + break; + } + + MEM_freeN(pixel); +} + +SimpleOperation *ReduceToSingleValueOperation::construct_if_needed(Context &context, + const Result &input_result) +{ + /* Input result is already a single value, the operation is not needed. */ + if (input_result.is_single_value()) { + return nullptr; + } + + /* The input is a full sized texture and can't be reduced to a single value, the operation is not + * needed. */ + if (input_result.domain().size != int2(1)) { + return nullptr; + } + + /* The input is a texture of a single pixel and can be reduced to a single value. */ + return new ReduceToSingleValueOperation(context, input_result.type()); +} + +} // namespace blender::realtime_compositor diff --git a/source/blender/compositor/realtime_compositor/intern/result.cc b/source/blender/compositor/realtime_compositor/intern/result.cc new file mode 100644 index 00000000000..8059367d211 --- /dev/null +++ b/source/blender/compositor/realtime_compositor/intern/result.cc @@ -0,0 +1,257 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "BLI_float3x3.hh" +#include "BLI_math_vec_types.hh" + +#include "GPU_shader.h" +#include "GPU_state.h" +#include "GPU_texture.h" + +#include "COM_domain.hh" +#include "COM_result.hh" +#include "COM_texture_pool.hh" + +namespace blender::realtime_compositor { + +Result::Result(ResultType type, TexturePool &texture_pool) + : type_(type), texture_pool_(&texture_pool) +{ +} + +void Result::allocate_texture(Domain domain) +{ + is_single_value_ = false; + switch (type_) { + case ResultType::Float: + texture_ = texture_pool_->acquire_float(domain.size); + break; + case ResultType::Vector: + texture_ = texture_pool_->acquire_vector(domain.size); + break; + case ResultType::Color: + texture_ = texture_pool_->acquire_color(domain.size); + break; + } + domain_ = domain; +} + +void Result::allocate_single_value() +{ + is_single_value_ = true; + /* Single values are stored in 1x1 textures as well as the single value members. */ + const int2 texture_size{1, 1}; + switch (type_) { + case ResultType::Float: + texture_ = texture_pool_->acquire_float(texture_size); + break; + case ResultType::Vector: + texture_ = texture_pool_->acquire_vector(texture_size); + break; + case ResultType::Color: + texture_ = texture_pool_->acquire_color(texture_size); + break; + } + domain_ = Domain::identity(); +} + +void Result::allocate_invalid() +{ + allocate_single_value(); + switch (type_) { + case ResultType::Float: + set_float_value(0.0f); + break; + case ResultType::Vector: + set_vector_value(float3(0.0f)); + break; + case ResultType::Color: + set_color_value(float4(0.0f)); + break; + } +} + +void Result::bind_as_texture(GPUShader *shader, const char *texture_name) const +{ + /* Make sure any prior writes to the texture are reflected before reading from it. */ + GPU_memory_barrier(GPU_BARRIER_TEXTURE_FETCH); + + const int texture_image_unit = GPU_shader_get_texture_binding(shader, texture_name); + GPU_texture_bind(texture_, texture_image_unit); +} + +void Result::bind_as_image(GPUShader *shader, const char *image_name) const +{ + const int image_unit = GPU_shader_get_texture_binding(shader, image_name); + GPU_texture_image_bind(texture_, image_unit); +} + +void Result::unbind_as_texture() const +{ + GPU_texture_unbind(texture_); +} + +void Result::unbind_as_image() const +{ + GPU_texture_image_unbind(texture_); +} + +void Result::pass_through(Result &target) +{ + /* Increment the reference count of the master by the original reference count of the target. */ + increment_reference_count(target.reference_count()); + + /* Make the target an exact copy of this result, but keep the initial reference count, as this is + * a property of the original result and is needed for correctly resetting the result before the + * next evaluation. */ + const int initial_reference_count = target.initial_reference_count_; + target = *this; + target.initial_reference_count_ = initial_reference_count; + + target.master_ = this; +} + +void Result::transform(const float3x3 &transformation) +{ + domain_.transform(transformation); +} + +RealizationOptions &Result::get_realization_options() +{ + return domain_.realization_options; +} + +float Result::get_float_value() const +{ + return float_value_; +} + +float3 Result::get_vector_value() const +{ + return vector_value_; +} + +float4 Result::get_color_value() const +{ + return color_value_; +} + +float Result::get_float_value_default(float default_value) const +{ + if (is_single_value()) { + return get_float_value(); + } + return default_value; +} + +float3 Result::get_vector_value_default(const float3 &default_value) const +{ + if (is_single_value()) { + return get_vector_value(); + } + return default_value; +} + +float4 Result::get_color_value_default(const float4 &default_value) const +{ + if (is_single_value()) { + return get_color_value(); + } + return default_value; +} + +void Result::set_float_value(float value) +{ + float_value_ = value; + GPU_texture_update(texture_, GPU_DATA_FLOAT, &float_value_); +} + +void Result::set_vector_value(const float3 &value) +{ + vector_value_ = value; + GPU_texture_update(texture_, GPU_DATA_FLOAT, vector_value_); +} + +void Result::set_color_value(const float4 &value) +{ + color_value_ = value; + GPU_texture_update(texture_, GPU_DATA_FLOAT, color_value_); +} + +void Result::set_initial_reference_count(int count) +{ + initial_reference_count_ = count; +} + +void Result::reset() +{ + master_ = nullptr; + reference_count_ = initial_reference_count_; +} + +void Result::increment_reference_count(int count) +{ + /* If there is a master result, increment its reference count instead. */ + if (master_) { + master_->increment_reference_count(count); + return; + } + + reference_count_ += count; +} + +void Result::release() +{ + /* If there is a master result, release it instead. */ + if (master_) { + master_->release(); + return; + } + + /* Decrement the reference count, and if it reaches zero, release the texture back into the + * texture pool. */ + reference_count_--; + if (reference_count_ == 0) { + texture_pool_->release(texture_); + } +} + +bool Result::should_compute() +{ + return initial_reference_count_ != 0; +} + +ResultType Result::type() const +{ + return type_; +} + +bool Result::is_texture() const +{ + return !is_single_value_; +} + +bool Result::is_single_value() const +{ + return is_single_value_; +} + +GPUTexture *Result::texture() const +{ + return texture_; +} + +int Result::reference_count() const +{ + /* If there is a master result, return its reference count instead. */ + if (master_) { + return master_->reference_count(); + } + return reference_count_; +} + +const Domain &Result::domain() const +{ + return domain_; +} + +} // namespace blender::realtime_compositor diff --git a/source/blender/compositor/realtime_compositor/intern/scheduler.cc b/source/blender/compositor/realtime_compositor/intern/scheduler.cc new file mode 100644 index 00000000000..ac5cc55a73f --- /dev/null +++ b/source/blender/compositor/realtime_compositor/intern/scheduler.cc @@ -0,0 +1,314 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "BLI_map.hh" +#include "BLI_set.hh" +#include "BLI_stack.hh" +#include "BLI_vector.hh" +#include "BLI_vector_set.hh" + +#include "NOD_derived_node_tree.hh" + +#include "BKE_node_runtime.hh" + +#include "COM_scheduler.hh" +#include "COM_utilities.hh" + +namespace blender::realtime_compositor { + +using namespace nodes::derived_node_tree_types; + +/* Compute the output node whose result should be computed. The output node is the node marked as + * NODE_DO_OUTPUT. If multiple types of output nodes are marked, then the preference will be + * CMP_NODE_COMPOSITE > CMP_NODE_VIEWER > CMP_NODE_SPLITVIEWER. If no output node exists, a null + * node will be returned. */ +static DNode compute_output_node(DerivedNodeTree &tree) +{ + const bNodeTree &root_tree = tree.root_context().btree(); + + for (const bNode *node : root_tree.nodes_by_type("CompositorNodeComposite")) { + if (node->flag & NODE_DO_OUTPUT) { + return DNode(&tree.root_context(), node); + } + } + + for (const bNode *node : root_tree.nodes_by_type("CompositorNodeViewer")) { + if (node->flag & NODE_DO_OUTPUT) { + return DNode(&tree.root_context(), node); + } + } + + for (const bNode *node : root_tree.nodes_by_type("CompositorNodeSplitViewer")) { + if (node->flag & NODE_DO_OUTPUT) { + return DNode(&tree.root_context(), node); + } + } + + /* No output node found, return a null node. */ + return DNode(); +} + +/* A type representing a mapping that associates each node with a heuristic estimation of the + * number of intermediate buffers needed to compute it and all of its dependencies. See the + * compute_number_of_needed_buffers function for more information. */ +using NeededBuffers = Map<DNode, int>; + +/* Compute a heuristic estimation of the number of intermediate buffers needed to compute each node + * and all of its dependencies for all nodes that the given node depends on. The output is a map + * that maps each node with the number of intermediate buffers needed to compute it and all of its + * dependencies. + * + * Consider a node that takes n number of buffers as an input from a number of node dependencies, + * which we shall call the input nodes. The node also computes and outputs m number of buffers. + * In order for the node to compute its output, a number of intermediate buffers will be needed. + * Since the node takes n buffers and outputs m buffers, then the number of buffers directly + * needed by the node is (n + m). But each of the input buffers are computed by a node that, in + * turn, needs a number of buffers to compute its output. So the total number of buffers needed + * to compute the output of the node is max(n + m, d) where d is the number of buffers needed by + * the input node that needs the largest number of buffers. We only consider the input node that + * needs the largest number of buffers, because those buffers can be reused by any input node + * that needs a lesser number of buffers. + * + * Shader nodes, however, are a special case because links between two shader nodes inside the same + * shader operation don't pass a buffer, but a single value in the compiled shader. So for shader + * nodes, only inputs and outputs linked to nodes that are not shader nodes should be considered. + * Note that this might not actually be true, because the compiler may decide to split a shader + * operation into multiples ones that will pass buffers, but this is not something that can be + * known at scheduling-time. See the discussion in COM_compile_state.hh, COM_evaluator.hh, and + * COM_shader_operation.hh for more information. In the node tree shown below, node 4 will have + * exactly the same number of needed buffers by node 3, because its inputs and outputs are all + * internally linked in the shader operation. + * + * Shader Operation + * +------------------------------------------------------+ + * .------------. | .------------. .------------. .------------. | .------------. + * | Node 1 | | | Node 3 | | Node 4 | | Node 5 | | | Node 6 | + * | |----|--| |--| |------| |--|--| | + * | | .-|--| | | | .---| | | | | + * '------------' | | '------------' '------------' | '------------' | '------------' + * | +----------------------------------|-------------------+ + * .------------. | | + * | Node 2 | | | + * | |--'------------------------------------' + * | | + * '------------' + * + * Note that the computed output is not guaranteed to be accurate, and will not be in most cases. + * The computation is merely a heuristic estimation that works well in most cases. This is due to a + * number of reasons: + * - The node tree is actually a graph that allows output sharing, which is not something that was + * taken into consideration in this implementation because it is difficult to correctly consider. + * - Each node may allocate any number of internal buffers, which is not taken into account in this + * implementation because it rarely affects the output and is done by very few nodes. + * - The compiler may decide to compiler the schedule differently depending on runtime information + * which we can merely speculate at scheduling-time as described above. */ +static NeededBuffers compute_number_of_needed_buffers(DNode output_node) +{ + NeededBuffers needed_buffers; + + /* A stack of nodes used to traverse the node tree starting from the output node. */ + Stack<DNode> node_stack = {output_node}; + + /* Traverse the node tree in a post order depth first manner and compute the number of needed + * buffers for each node. Post order traversal guarantee that all the node dependencies of each + * node are computed before it. This is done by pushing all the uncomputed node dependencies to + * the node stack first and only popping and computing the node when all its node dependencies + * were computed. */ + while (!node_stack.is_empty()) { + /* Do not pop the node immediately, as it may turn out that we can't compute its number of + * needed buffers just yet because its dependencies weren't computed, it will be popped later + * when needed. */ + DNode &node = node_stack.peek(); + + /* Go over the node dependencies connected to the inputs of the node and push them to the node + * stack if they were not computed already. */ + Set<DNode> pushed_nodes; + for (const bNodeSocket *input : node->input_sockets()) { + const DInputSocket dinput{node.context(), input}; + + /* Get the output linked to the input. If it is null, that means the input is unlinked and + * has no dependency node. */ + const DOutputSocket doutput = get_output_linked_to_input(dinput); + if (!doutput) { + continue; + } + + /* The node dependency was already computed or pushed before, so skip it. */ + if (needed_buffers.contains(doutput.node()) || pushed_nodes.contains(doutput.node())) { + continue; + } + + /* The output node needs to be computed, push the node dependency to the node stack and + * indicate that it was pushed. */ + node_stack.push(doutput.node()); + pushed_nodes.add_new(doutput.node()); + } + + /* If any of the node dependencies were pushed, that means that not all of them were computed + * and consequently we can't compute the number of needed buffers for this node just yet. */ + if (!pushed_nodes.is_empty()) { + continue; + } + + /* We don't need to store the result of the pop because we already peeked at it before. */ + node_stack.pop(); + + /* Compute the number of buffers that the node takes as an input as well as the number of + * buffers needed to compute the most demanding of the node dependencies. */ + int number_of_input_buffers = 0; + int buffers_needed_by_dependencies = 0; + for (const bNodeSocket *input : node->input_sockets()) { + const DInputSocket dinput{node.context(), input}; + + /* Get the output linked to the input. If it is null, that means the input is unlinked. + * Unlinked inputs do not take a buffer, so skip those inputs. */ + const DOutputSocket doutput = get_output_linked_to_input(dinput); + if (!doutput) { + continue; + } + + /* Since this input is linked, if the link is not between two shader nodes, it means that the + * node takes a buffer through this input and so we increment the number of input buffers. */ + if (!is_shader_node(node) || !is_shader_node(doutput.node())) { + number_of_input_buffers++; + } + + /* If the number of buffers needed by the node dependency is more than the total number of + * buffers needed by the dependencies, then update the latter to be the former. This is + * computing the "d" in the aforementioned equation "max(n + m, d)". */ + const int buffers_needed_by_dependency = needed_buffers.lookup(doutput.node()); + if (buffers_needed_by_dependency > buffers_needed_by_dependencies) { + buffers_needed_by_dependencies = buffers_needed_by_dependency; + } + } + + /* Compute the number of buffers that will be computed/output by this node. */ + int number_of_output_buffers = 0; + for (const bNodeSocket *output : node->output_sockets()) { + const DOutputSocket doutput{node.context(), output}; + + /* The output is not linked, it outputs no buffer. */ + if (!output->is_logically_linked()) { + continue; + } + + /* If any of the links is not between two shader nodes, it means that the node outputs + * a buffer through this output and so we increment the number of output buffers. */ + if (!is_output_linked_to_node_conditioned(doutput, is_shader_node) || + !is_shader_node(node)) { + number_of_output_buffers++; + } + } + + /* Compute the heuristic estimation of the number of needed intermediate buffers to compute + * this node and all of its dependencies. This is computing the aforementioned equation + * "max(n + m, d)". */ + const int total_buffers = MAX2(number_of_input_buffers + number_of_output_buffers, + buffers_needed_by_dependencies); + needed_buffers.add(node, total_buffers); + } + + return needed_buffers; +} + +/* There are multiple different possible orders of evaluating a node graph, each of which needs + * to allocate a number of intermediate buffers to store its intermediate results. It follows + * that we need to find the evaluation order which uses the least amount of intermediate buffers. + * For instance, consider a node that takes two input buffers A and B. Each of those buffers is + * computed through a number of nodes constituting a sub-graph whose root is the node that + * outputs that buffer. Suppose the number of intermediate buffers needed to compute A and B are + * N(A) and N(B) respectively and N(A) > N(B). Then evaluating the sub-graph computing A would be + * a better option than that of B, because had B was computed first, its outputs will need to be + * stored in extra buffers in addition to the buffers needed by A. The number of buffers needed by + * each node is estimated as described in the compute_number_of_needed_buffers function. + * + * This is a heuristic generalization of the Sethi–Ullman algorithm, a generalization that + * doesn't always guarantee an optimal evaluation order, as the optimal evaluation order is very + * difficult to compute, however, this method works well in most cases. Moreover it assumes that + * all buffers will have roughly the same size, which may not always be the case. */ +Schedule compute_schedule(DerivedNodeTree &tree) +{ + Schedule schedule; + + /* Compute the output node whose result should be computed. */ + const DNode output_node = compute_output_node(tree); + + /* No output node, the node tree has no effect, return an empty schedule. */ + if (!output_node) { + return schedule; + } + + /* Compute the number of buffers needed by each node connected to the output. */ + const NeededBuffers needed_buffers = compute_number_of_needed_buffers(output_node); + + /* A stack of nodes used to traverse the node tree starting from the output node. */ + Stack<DNode> node_stack = {output_node}; + + /* Traverse the node tree in a post order depth first manner, scheduling the nodes in an order + * informed by the number of buffers needed by each node. Post order traversal guarantee that all + * the node dependencies of each node are scheduled before it. This is done by pushing all the + * unscheduled node dependencies to the node stack first and only popping and scheduling the node + * when all its node dependencies were scheduled. */ + while (!node_stack.is_empty()) { + /* Do not pop the node immediately, as it may turn out that we can't schedule it just yet + * because its dependencies weren't scheduled, it will be popped later when needed. */ + DNode &node = node_stack.peek(); + + /* Compute the nodes directly connected to the node inputs sorted by their needed buffers such + * that the node with the lowest number of needed buffers comes first. Note that we actually + * want the node with the highest number of needed buffers to be schedule first, but since + * those are pushed to the traversal stack, we need to push them in reverse order. */ + Vector<DNode> sorted_dependency_nodes; + for (const bNodeSocket *input : node->input_sockets()) { + const DInputSocket dinput{node.context(), input}; + + /* Get the output linked to the input. If it is null, that means the input is unlinked and + * has no dependency node, so skip it. */ + const DOutputSocket doutput = get_output_linked_to_input(dinput); + if (!doutput) { + continue; + } + + /* The dependency node was added before, so skip it. The number of dependency nodes is very + * small, typically less than 3, so a linear search is okay. */ + if (sorted_dependency_nodes.contains(doutput.node())) { + continue; + } + + /* The dependency node was already schedule, so skip it. */ + if (schedule.contains(doutput.node())) { + continue; + } + + /* Sort in ascending order on insertion, the number of dependency nodes is very small, + * typically less than 3, so insertion sort is okay. */ + int insertion_position = 0; + for (int i = 0; i < sorted_dependency_nodes.size(); i++) { + if (needed_buffers.lookup(doutput.node()) > + needed_buffers.lookup(sorted_dependency_nodes[i])) { + insertion_position++; + } + else { + break; + } + } + sorted_dependency_nodes.insert(insertion_position, doutput.node()); + } + + /* Push the sorted dependency nodes to the node stack in order. */ + for (const DNode &dependency_node : sorted_dependency_nodes) { + node_stack.push(dependency_node); + } + + /* If there are no sorted dependency nodes, that means they were all already scheduled or that + * none exists in the first place, so we can pop and schedule the node now. */ + if (sorted_dependency_nodes.is_empty()) { + /* The node might have already been scheduled, so we don't use add_new here and simply don't + * add it if it was already scheduled. */ + schedule.add(node_stack.pop()); + } + } + + return schedule; +} + +} // namespace blender::realtime_compositor diff --git a/source/blender/compositor/realtime_compositor/intern/shader_node.cc b/source/blender/compositor/realtime_compositor/intern/shader_node.cc new file mode 100644 index 00000000000..96dd50790c3 --- /dev/null +++ b/source/blender/compositor/realtime_compositor/intern/shader_node.cc @@ -0,0 +1,157 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "BLI_assert.h" +#include "BLI_math_vector.h" +#include "BLI_string_ref.hh" + +#include "DNA_node_types.h" + +#include "NOD_derived_node_tree.hh" + +#include "GPU_material.h" + +#include "COM_shader_node.hh" +#include "COM_utilities.hh" + +namespace blender::realtime_compositor { + +using namespace nodes::derived_node_tree_types; + +ShaderNode::ShaderNode(DNode node) : node_(node) +{ + populate_inputs(); + populate_outputs(); +} + +GPUNodeStack *ShaderNode::get_inputs_array() +{ + return inputs_.data(); +} + +GPUNodeStack *ShaderNode::get_outputs_array() +{ + return outputs_.data(); +} + +GPUNodeStack &ShaderNode::get_input(StringRef identifier) +{ + return inputs_[node_.input_by_identifier(identifier)->index()]; +} + +GPUNodeStack &ShaderNode::get_output(StringRef identifier) +{ + return outputs_[node_.output_by_identifier(identifier)->index()]; +} + +GPUNodeLink *ShaderNode::get_input_link(StringRef identifier) +{ + GPUNodeStack &input = get_input(identifier); + if (input.link) { + return input.link; + } + return GPU_uniform(input.vec); +} + +const DNode &ShaderNode::node() const +{ + return node_; +} + +const bNode &ShaderNode::bnode() const +{ + return *node_; +} + +static eGPUType gpu_type_from_socket_type(eNodeSocketDatatype type) +{ + switch (type) { + case SOCK_FLOAT: + return GPU_FLOAT; + case SOCK_VECTOR: + return GPU_VEC3; + case SOCK_RGBA: + return GPU_VEC4; + default: + BLI_assert_unreachable(); + return GPU_NONE; + } +} + +static void gpu_stack_vector_from_socket(float *vector, const bNodeSocket *socket) +{ + switch (socket->type) { + case SOCK_FLOAT: + vector[0] = socket->default_value_typed<bNodeSocketValueFloat>()->value; + return; + case SOCK_VECTOR: + copy_v3_v3(vector, socket->default_value_typed<bNodeSocketValueVector>()->value); + return; + case SOCK_RGBA: + copy_v4_v4(vector, socket->default_value_typed<bNodeSocketValueRGBA>()->value); + return; + default: + BLI_assert_unreachable(); + } +} + +static void populate_gpu_node_stack(DSocket socket, GPUNodeStack &stack) +{ + /* Make sure this stack is not marked as the end of the stack array. */ + stack.end = false; + /* This will be initialized later by the GPU material compiler or the compile method. */ + stack.link = nullptr; + + stack.sockettype = socket->type; + stack.type = gpu_type_from_socket_type((eNodeSocketDatatype)socket->type); + + if (socket->is_input()) { + const DInputSocket input(socket); + + DSocket origin = get_input_origin_socket(input); + + /* The input is linked if the origin socket is an output socket. Had it been an input socket, + * then it is an unlinked input of a group input node. */ + stack.hasinput = origin->is_output(); + + /* Get the socket value from the origin if it is an input, because then it would either be an + * unlinked input or an unlinked input of a group input node that the socket is linked to, + * otherwise, get the value from the socket itself. */ + if (origin->is_input()) { + gpu_stack_vector_from_socket(stack.vec, origin.bsocket()); + } + else { + gpu_stack_vector_from_socket(stack.vec, socket.bsocket()); + } + } + else { + stack.hasoutput = socket->is_logically_linked(); + } +} + +void ShaderNode::populate_inputs() +{ + /* Reserve a stack for each input in addition to an extra stack at the end to mark the end of the + * array, as this is what the GPU module functions expect. */ + const int num_input_sockets = node_->input_sockets().size(); + inputs_.resize(num_input_sockets + 1); + inputs_.last().end = true; + + for (int i = 0; i < num_input_sockets; i++) { + populate_gpu_node_stack(node_.input(i), inputs_[i]); + } +} + +void ShaderNode::populate_outputs() +{ + /* Reserve a stack for each output in addition to an extra stack at the end to mark the end of + * the array, as this is what the GPU module functions expect. */ + const int num_output_sockets = node_->output_sockets().size(); + outputs_.resize(num_output_sockets + 1); + outputs_.last().end = true; + + for (int i = 0; i < num_output_sockets; i++) { + populate_gpu_node_stack(node_.output(i), outputs_[i]); + } +} + +} // namespace blender::realtime_compositor diff --git a/source/blender/compositor/realtime_compositor/intern/shader_operation.cc b/source/blender/compositor/realtime_compositor/intern/shader_operation.cc new file mode 100644 index 00000000000..8e52baf63ec --- /dev/null +++ b/source/blender/compositor/realtime_compositor/intern/shader_operation.cc @@ -0,0 +1,526 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include <memory> +#include <string> + +#include "BLI_listbase.h" +#include "BLI_map.hh" +#include "BLI_string_ref.hh" +#include "BLI_utildefines.h" + +#include "DNA_customdata_types.h" + +#include "GPU_material.h" +#include "GPU_shader.h" +#include "GPU_texture.h" +#include "GPU_uniform_buffer.h" + +#include "gpu_shader_create_info.hh" + +#include "NOD_derived_node_tree.hh" +#include "NOD_node_declaration.hh" + +#include "COM_context.hh" +#include "COM_operation.hh" +#include "COM_result.hh" +#include "COM_scheduler.hh" +#include "COM_shader_node.hh" +#include "COM_shader_operation.hh" +#include "COM_utilities.hh" + +namespace blender::realtime_compositor { + +using namespace nodes::derived_node_tree_types; + +ShaderOperation::ShaderOperation(Context &context, ShaderCompileUnit &compile_unit) + : Operation(context), compile_unit_(compile_unit) +{ + material_ = GPU_material_from_callbacks(&construct_material, &generate_code, this); + GPU_material_status_set(material_, GPU_MAT_QUEUED); + GPU_material_compile(material_); +} + +ShaderOperation::~ShaderOperation() +{ + GPU_material_free_single(material_); +} + +void ShaderOperation::execute() +{ + const Domain domain = compute_domain(); + for (StringRef identifier : output_sockets_to_output_identifiers_map_.values()) { + Result &result = get_result(identifier); + result.allocate_texture(domain); + } + + GPUShader *shader = GPU_material_get_shader(material_); + GPU_shader_bind(shader); + + bind_material_resources(shader); + bind_inputs(shader); + bind_outputs(shader); + + compute_dispatch_threads_at_least(shader, domain.size); + + GPU_texture_unbind_all(); + GPU_texture_image_unbind_all(); + GPU_uniformbuf_unbind_all(); + GPU_shader_unbind(); +} + +StringRef ShaderOperation::get_output_identifier_from_output_socket(DOutputSocket output_socket) +{ + return output_sockets_to_output_identifiers_map_.lookup(output_socket); +} + +Map<std::string, DOutputSocket> &ShaderOperation::get_inputs_to_linked_outputs_map() +{ + return inputs_to_linked_outputs_map_; +} + +void ShaderOperation::compute_results_reference_counts(const Schedule &schedule) +{ + for (const auto &item : output_sockets_to_output_identifiers_map_.items()) { + const int reference_count = number_of_inputs_linked_to_output_conditioned( + item.key, [&](DInputSocket input) { return schedule.contains(input.node()); }); + + get_result(item.value).set_initial_reference_count(reference_count); + } +} + +void ShaderOperation::bind_material_resources(GPUShader *shader) +{ + /* Bind the uniform buffer of the material if it exists. It may not exist if the GPU material has + * no uniforms. */ + GPUUniformBuf *ubo = GPU_material_uniform_buffer_get(material_); + if (ubo) { + GPU_uniformbuf_bind(ubo, GPU_shader_get_uniform_block_binding(shader, GPU_UBO_BLOCK_NAME)); + } + + /* Bind color band textures needed by curve and ramp nodes. */ + ListBase textures = GPU_material_textures(material_); + LISTBASE_FOREACH (GPUMaterialTexture *, texture, &textures) { + if (texture->colorband) { + const int texture_image_unit = GPU_shader_get_texture_binding(shader, texture->sampler_name); + GPU_texture_bind(*texture->colorband, texture_image_unit); + } + } +} + +void ShaderOperation::bind_inputs(GPUShader *shader) +{ + /* Attributes represents the inputs of the operation and their names match those of the inputs of + * the operation as well as the corresponding texture samples in the shader. */ + ListBase attributes = GPU_material_attributes(material_); + LISTBASE_FOREACH (GPUMaterialAttribute *, attribute, &attributes) { + get_input(attribute->name).bind_as_texture(shader, attribute->name); + } +} + +void ShaderOperation::bind_outputs(GPUShader *shader) +{ + for (StringRefNull output_identifier : output_sockets_to_output_identifiers_map_.values()) { + get_result(output_identifier).bind_as_image(shader, output_identifier.c_str()); + } +} + +void ShaderOperation::construct_material(void *thunk, GPUMaterial *material) +{ + ShaderOperation *operation = static_cast<ShaderOperation *>(thunk); + for (DNode node : operation->compile_unit_) { + ShaderNode *shader_node = node->typeinfo->get_compositor_shader_node(node); + operation->shader_nodes_.add_new(node, std::unique_ptr<ShaderNode>(shader_node)); + + operation->link_node_inputs(node, material); + + shader_node->compile(material); + + operation->populate_results_for_node(node, material); + } +} + +void ShaderOperation::link_node_inputs(DNode node, GPUMaterial *material) +{ + for (const bNodeSocket *input : node->input_sockets()) { + const DInputSocket dinput{node.context(), input}; + + /* Get the output linked to the input. If it is null, that means the input is unlinked. + * Unlinked inputs are linked by the node compile method, so skip this here. */ + const DOutputSocket doutput = get_output_linked_to_input(dinput); + if (!doutput) { + continue; + } + + /* If the origin node is part of the shader operation, then the link is internal to the GPU + * material graph and is linked appropriately. */ + if (compile_unit_.contains(doutput.node())) { + link_node_input_internal(dinput, doutput); + continue; + } + + /* Otherwise, the origin node is not part of the shader operation, then the link is external to + * the GPU material graph and an input to the shader operation must be declared and linked to + * the node input. */ + link_node_input_external(dinput, doutput, material); + } +} + +void ShaderOperation::link_node_input_internal(DInputSocket input_socket, + DOutputSocket output_socket) +{ + ShaderNode &output_node = *shader_nodes_.lookup(output_socket.node()); + GPUNodeStack &output_stack = output_node.get_output(output_socket->identifier); + + ShaderNode &input_node = *shader_nodes_.lookup(input_socket.node()); + GPUNodeStack &input_stack = input_node.get_input(input_socket->identifier); + + input_stack.link = output_stack.link; +} + +void ShaderOperation::link_node_input_external(DInputSocket input_socket, + DOutputSocket output_socket, + GPUMaterial *material) +{ + + ShaderNode &node = *shader_nodes_.lookup(input_socket.node()); + GPUNodeStack &stack = node.get_input(input_socket->identifier); + + /* An input was already declared for that same output socket, so no need to declare it again. */ + if (!output_to_material_attribute_map_.contains(output_socket)) { + declare_operation_input(input_socket, output_socket, material); + } + + /* Link the attribute representing the shader operation input corresponding to the given output + * socket. */ + stack.link = output_to_material_attribute_map_.lookup(output_socket); +} + +static const char *get_set_function_name(ResultType type) +{ + switch (type) { + case ResultType::Float: + return "set_value"; + case ResultType::Vector: + return "set_rgb"; + case ResultType::Color: + return "set_rgba"; + } + + BLI_assert_unreachable(); + return nullptr; +} + +void ShaderOperation::declare_operation_input(DInputSocket input_socket, + DOutputSocket output_socket, + GPUMaterial *material) +{ + const int input_index = output_to_material_attribute_map_.size(); + std::string input_identifier = "input" + std::to_string(input_index); + + /* Declare the input descriptor for this input and prefer to declare its type to be the same as + * the type of the output socket because doing type conversion in the shader is much cheaper. */ + InputDescriptor input_descriptor = input_descriptor_from_input_socket(input_socket.bsocket()); + input_descriptor.type = get_node_socket_result_type(output_socket.bsocket()); + declare_input_descriptor(input_identifier, input_descriptor); + + /* Add a new GPU attribute representing an input to the GPU material. Instead of using the + * attribute directly, we link it to an appropriate set function and use its output link instead. + * This is needed because the `gputype` member of the attribute is only initialized if it is + * linked to a GPU node. */ + GPUNodeLink *attribute_link; + GPU_link(material, + get_set_function_name(input_descriptor.type), + GPU_attribute(material, CD_AUTO_FROM_NAME, input_identifier.c_str()), + &attribute_link); + + /* Map the output socket to the attribute that was created for it. */ + output_to_material_attribute_map_.add(output_socket, attribute_link); + + /* Map the identifier of the operation input to the output socket it is linked to. */ + inputs_to_linked_outputs_map_.add_new(input_identifier, output_socket); +} + +void ShaderOperation::populate_results_for_node(DNode node, GPUMaterial *material) +{ + for (const bNodeSocket *output : node->output_sockets()) { + const DOutputSocket doutput{node.context(), output}; + + /* If any of the nodes linked to the output are not part of the shader operation, then an + * output result needs to be populated for it. */ + const bool need_to_populate_result = is_output_linked_to_node_conditioned( + doutput, [&](DNode node) { return !compile_unit_.contains(node); }); + + if (need_to_populate_result) { + populate_operation_result(doutput, material); + } + } +} + +static const char *get_store_function_name(ResultType type) +{ + switch (type) { + case ResultType::Float: + return "node_compositor_store_output_float"; + case ResultType::Vector: + return "node_compositor_store_output_vector"; + case ResultType::Color: + return "node_compositor_store_output_color"; + } + + BLI_assert_unreachable(); + return nullptr; +} + +void ShaderOperation::populate_operation_result(DOutputSocket output_socket, GPUMaterial *material) +{ + const unsigned int output_id = output_sockets_to_output_identifiers_map_.size(); + std::string output_identifier = "output" + std::to_string(output_id); + + const ResultType result_type = get_node_socket_result_type(output_socket.bsocket()); + const Result result = Result(result_type, texture_pool()); + populate_result(output_identifier, result); + + /* Map the output socket to the identifier of the newly populated result. */ + output_sockets_to_output_identifiers_map_.add_new(output_socket, output_identifier); + + ShaderNode &node = *shader_nodes_.lookup(output_socket.node()); + GPUNodeLink *output_link = node.get_output(output_socket->identifier).link; + + /* Link the output node stack to an output storer storing in the appropriate result. The result + * is identified by its index in the operation and the index is encoded as a float to be passed + * to the GPU function. Additionally, create an output link from the storer node to declare as an + * output to the GPU material. This storer output link is a dummy link in the sense that its + * value is ignored since it is already written in the output, but it is used to track nodes that + * contribute to the output of the compositor node tree. */ + GPUNodeLink *storer_output_link; + GPUNodeLink *id_link = GPU_constant((float *)&output_id); + const char *store_function_name = get_store_function_name(result_type); + GPU_link(material, store_function_name, id_link, output_link, &storer_output_link); + + /* Declare the output link of the storer node as an output of the GPU material to help the GPU + * code generator to track the nodes that contribute to the output of the shader. */ + GPU_material_add_output_link_composite(material, storer_output_link); +} + +using namespace gpu::shader; + +void ShaderOperation::generate_code(void *thunk, + GPUMaterial *material, + GPUCodegenOutput *code_generator_output) +{ + ShaderOperation *operation = static_cast<ShaderOperation *>(thunk); + ShaderCreateInfo &shader_create_info = *reinterpret_cast<ShaderCreateInfo *>( + code_generator_output->create_info); + + shader_create_info.local_group_size(16, 16); + + /* The resources are added without explicit locations, so make sure it is done by the + * shader creator. */ + shader_create_info.auto_resource_location(true); + + /* Add implementation for implicit conversion operations inserted by the code generator. This + * file should include the functions [float|vec3|vec4]_from_[float|vec3|vec4]. */ + shader_create_info.typedef_source("gpu_shader_compositor_type_conversion.glsl"); + + /* The source shader is a compute shader with a main function that calls the dynamically + * generated evaluate function. The evaluate function includes the serialized GPU material graph + * preceded by code that initialized the inputs of the operation. Additionally, the storer + * functions that writes the outputs are defined outside the evaluate function. */ + shader_create_info.compute_source("gpu_shader_compositor_main.glsl"); + + /* The main function is emitted in the shader before the evaluate function, so the evaluate + * function needs to be forward declared here. */ + shader_create_info.typedef_source_generated += "void evaluate();\n"; + + operation->generate_code_for_outputs(shader_create_info); + + shader_create_info.compute_source_generated += "void evaluate()\n{\n"; + + operation->generate_code_for_inputs(material, shader_create_info); + + shader_create_info.compute_source_generated += code_generator_output->composite; + + shader_create_info.compute_source_generated += "}\n"; +} + +static eGPUTextureFormat texture_format_from_result_type(ResultType type) +{ + switch (type) { + case ResultType::Float: + return GPU_R16F; + case ResultType::Vector: + return GPU_RGBA16F; + case ResultType::Color: + return GPU_RGBA16F; + } + + BLI_assert_unreachable(); + return GPU_RGBA16F; +} + +/* Texture storers in the shader always take a vec4 as an argument, so encode each type in a vec4 + * appropriately. */ +static const char *glsl_store_expression_from_result_type(ResultType type) +{ + switch (type) { + case ResultType::Float: + return "vec4(value)"; + case ResultType::Vector: + return "vec4(vector, 0.0)"; + case ResultType::Color: + return "color"; + } + + BLI_assert_unreachable(); + return nullptr; +} + +void ShaderOperation::generate_code_for_outputs(ShaderCreateInfo &shader_create_info) +{ + const std::string store_float_function_header = "void store_float(const uint id, float value)"; + const std::string store_vector_function_header = "void store_vector(const uint id, vec3 vector)"; + const std::string store_color_function_header = "void store_color(const uint id, vec4 color)"; + + /* The store functions are used by the node_compositor_store_output_[float|vector|color] + * functions but are only defined later as part of the compute source, so they need to be forward + * declared. */ + shader_create_info.typedef_source_generated += store_float_function_header + ";\n"; + shader_create_info.typedef_source_generated += store_vector_function_header + ";\n"; + shader_create_info.typedef_source_generated += store_color_function_header + ";\n"; + + /* Each of the store functions is essentially a single switch case on the given ID, so start by + * opening the function with a curly bracket followed by opening a switch statement in each of + * the functions. */ + std::stringstream store_float_function; + std::stringstream store_vector_function; + std::stringstream store_color_function; + const std::string store_function_start = "\n{\n switch (id) {\n"; + store_float_function << store_float_function_header << store_function_start; + store_vector_function << store_vector_function_header << store_function_start; + store_color_function << store_color_function_header << store_function_start; + + for (StringRefNull output_identifier : output_sockets_to_output_identifiers_map_.values()) { + const Result &result = get_result(output_identifier); + + /* Add a write-only image for this output where its values will be written. */ + shader_create_info.image(0, + texture_format_from_result_type(result.type()), + Qualifier::WRITE, + ImageType::FLOAT_2D, + output_identifier, + Frequency::BATCH); + + /* Add a case for the index of this output followed by a break statement. */ + std::stringstream case_code; + const std::string store_expression = glsl_store_expression_from_result_type(result.type()); + const std::string texel = ", ivec2(gl_GlobalInvocationID.xy), "; + case_code << " case " << StringRef(output_identifier).drop_known_prefix("output") << ":\n" + << " imageStore(" << output_identifier << texel << store_expression << ");\n" + << " break;\n"; + + /* Only add the case to the function with the matching type. */ + switch (result.type()) { + case ResultType::Float: + store_float_function << case_code.str(); + break; + case ResultType::Vector: + store_vector_function << case_code.str(); + break; + case ResultType::Color: + store_color_function << case_code.str(); + break; + } + } + + /* Close the previously opened switch statement as well as the function itself. */ + const std::string store_function_end = " }\n}\n\n"; + store_float_function << store_function_end; + store_vector_function << store_function_end; + store_color_function << store_function_end; + + shader_create_info.compute_source_generated += store_float_function.str() + + store_vector_function.str() + + store_color_function.str(); +} + +static const char *glsl_type_from_result_type(ResultType type) +{ + switch (type) { + case ResultType::Float: + return "float"; + case ResultType::Vector: + return "vec3"; + case ResultType::Color: + return "vec4"; + } + + BLI_assert_unreachable(); + return nullptr; +} + +/* Texture loaders in the shader always return a vec4, so a swizzle is needed to retrieve the + * actual value for each type. */ +static const char *glsl_swizzle_from_result_type(ResultType type) +{ + switch (type) { + case ResultType::Float: + return "x"; + case ResultType::Vector: + return "xyz"; + case ResultType::Color: + return "rgba"; + } + + BLI_assert_unreachable(); + return nullptr; +} + +void ShaderOperation::generate_code_for_inputs(GPUMaterial *material, + ShaderCreateInfo &shader_create_info) +{ + /* The attributes of the GPU material represents the inputs of the operation. */ + ListBase attributes = GPU_material_attributes(material); + + if (BLI_listbase_is_empty(&attributes)) { + return; + } + + /* Add a texture sampler for each of the inputs with the same name as the attribute. */ + LISTBASE_FOREACH (GPUMaterialAttribute *, attribute, &attributes) { + shader_create_info.sampler(0, ImageType::FLOAT_2D, attribute->name, Frequency::BATCH); + } + + /* Declare a struct called var_attrs that includes an appropriately typed member for each of the + * inputs. The names of the members should be the letter v followed by the ID of the attribute + * corresponding to the input. Such names are expected by the code generator. */ + std::stringstream declare_attributes; + declare_attributes << "struct {\n"; + LISTBASE_FOREACH (GPUMaterialAttribute *, attribute, &attributes) { + const InputDescriptor &input_descriptor = get_input_descriptor(attribute->name); + const std::string type = glsl_type_from_result_type(input_descriptor.type); + declare_attributes << " " << type << " v" << attribute->id << ";\n"; + } + declare_attributes << "} var_attrs;\n\n"; + + shader_create_info.compute_source_generated += declare_attributes.str(); + + /* The texture loader utilities are needed to sample the input textures and initialize the + * attributes. */ + shader_create_info.typedef_source("gpu_shader_compositor_texture_utilities.glsl"); + + /* Initialize each member of the previously declared struct by loading its corresponding texture + * with an appropriate swizzle for its type. */ + std::stringstream initialize_attributes; + LISTBASE_FOREACH (GPUMaterialAttribute *, attribute, &attributes) { + const InputDescriptor &input_descriptor = get_input_descriptor(attribute->name); + const std::string swizzle = glsl_swizzle_from_result_type(input_descriptor.type); + initialize_attributes << "var_attrs.v" << attribute->id << " = " + << "texture_load(" << attribute->name + << ", ivec2(gl_GlobalInvocationID.xy))." << swizzle << ";\n"; + } + initialize_attributes << "\n"; + + shader_create_info.compute_source_generated += initialize_attributes.str(); +} + +} // namespace blender::realtime_compositor diff --git a/source/blender/compositor/realtime_compositor/intern/simple_operation.cc b/source/blender/compositor/realtime_compositor/intern/simple_operation.cc new file mode 100644 index 00000000000..d55a20e5c54 --- /dev/null +++ b/source/blender/compositor/realtime_compositor/intern/simple_operation.cc @@ -0,0 +1,55 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "COM_input_descriptor.hh" +#include "COM_operation.hh" +#include "COM_result.hh" +#include "COM_simple_operation.hh" + +namespace blender::realtime_compositor { + +const StringRef SimpleOperation::input_identifier_ = StringRef("Input"); +const StringRef SimpleOperation::output_identifier_ = StringRef("Output"); + +Result &SimpleOperation::get_result() +{ + return Operation::get_result(output_identifier_); +} + +void SimpleOperation::map_input_to_result(Result *result) +{ + Operation::map_input_to_result(input_identifier_, result); +} + +void SimpleOperation::add_and_evaluate_input_processors() +{ +} + +Result &SimpleOperation::get_input() +{ + return Operation::get_input(input_identifier_); +} + +void SimpleOperation::switch_result_mapped_to_input(Result *result) +{ + Operation::switch_result_mapped_to_input(input_identifier_, result); +} + +void SimpleOperation::populate_result(Result result) +{ + Operation::populate_result(output_identifier_, result); + + /* The result of a simple operation is guaranteed to have a single user. */ + get_result().set_initial_reference_count(1); +} + +void SimpleOperation::declare_input_descriptor(InputDescriptor descriptor) +{ + Operation::declare_input_descriptor(input_identifier_, descriptor); +} + +InputDescriptor &SimpleOperation::get_input_descriptor() +{ + return Operation::get_input_descriptor(input_identifier_); +} + +} // namespace blender::realtime_compositor diff --git a/source/blender/compositor/realtime_compositor/intern/static_shader_manager.cc b/source/blender/compositor/realtime_compositor/intern/static_shader_manager.cc new file mode 100644 index 00000000000..c9c8a056f87 --- /dev/null +++ b/source/blender/compositor/realtime_compositor/intern/static_shader_manager.cc @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "GPU_shader.h" + +#include "COM_static_shader_manager.hh" + +namespace blender::realtime_compositor { + +StaticShaderManager::~StaticShaderManager() +{ + for (GPUShader *shader : shaders_.values()) { + GPU_shader_free(shader); + } +} + +GPUShader *StaticShaderManager::get(const char *info_name) +{ + /* If a shader with the same info name already exists in the manager, return it, otherwise, + * create a new shader from the info name and return it. */ + return shaders_.lookup_or_add_cb( + info_name, [info_name]() { return GPU_shader_create_from_info_name(info_name); }); +} + +} // namespace blender::realtime_compositor diff --git a/source/blender/compositor/realtime_compositor/intern/texture_pool.cc b/source/blender/compositor/realtime_compositor/intern/texture_pool.cc new file mode 100644 index 00000000000..6bf2041e6ba --- /dev/null +++ b/source/blender/compositor/realtime_compositor/intern/texture_pool.cc @@ -0,0 +1,88 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include <cstdint> + +#include "BLI_hash.hh" +#include "BLI_map.hh" +#include "BLI_math_vec_types.hh" +#include "BLI_vector.hh" + +#include "GPU_texture.h" + +#include "COM_texture_pool.hh" + +namespace blender::realtime_compositor { + +/* -------------------------------------------------------------------- */ +/** \name Texture Pool Key + * \{ */ + +TexturePoolKey::TexturePoolKey(int2 size, eGPUTextureFormat format) : size(size), format(format) +{ +} + +TexturePoolKey::TexturePoolKey(const GPUTexture *texture) +{ + size = int2(GPU_texture_width(texture), GPU_texture_height(texture)); + format = GPU_texture_format(texture); +} + +uint64_t TexturePoolKey::hash() const +{ + return get_default_hash_3(size.x, size.y, format); +} + +bool operator==(const TexturePoolKey &a, const TexturePoolKey &b) +{ + return a.size == b.size && a.format == b.format; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Texture Pool + * \{ */ + +GPUTexture *TexturePool::acquire(int2 size, eGPUTextureFormat format) +{ + /* Check if there is an available texture with the required specification, and if one exists, + * return it. */ + const TexturePoolKey key = TexturePoolKey(size, format); + Vector<GPUTexture *> &available_textures = textures_.lookup_or_add_default(key); + if (!available_textures.is_empty()) { + return available_textures.pop_last(); + } + + /* Otherwise, allocate a new texture. */ + return allocate_texture(size, format); +} + +GPUTexture *TexturePool::acquire_color(int2 size) +{ + return acquire(size, GPU_RGBA16F); +} + +GPUTexture *TexturePool::acquire_vector(int2 size) +{ + /* Vectors are stored in RGBA textures because RGB textures have limited support. */ + return acquire(size, GPU_RGBA16F); +} + +GPUTexture *TexturePool::acquire_float(int2 size) +{ + return acquire(size, GPU_R16F); +} + +void TexturePool::release(GPUTexture *texture) +{ + textures_.lookup(TexturePoolKey(texture)).append(texture); +} + +void TexturePool::reset() +{ + textures_.clear(); +} + +/** \} */ + +} // namespace blender::realtime_compositor diff --git a/source/blender/compositor/realtime_compositor/intern/utilities.cc b/source/blender/compositor/realtime_compositor/intern/utilities.cc new file mode 100644 index 00000000000..1a5823b8441 --- /dev/null +++ b/source/blender/compositor/realtime_compositor/intern/utilities.cc @@ -0,0 +1,134 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "BLI_assert.h" +#include "BLI_function_ref.hh" +#include "BLI_math_vec_types.hh" +#include "BLI_math_vector.hh" +#include "BLI_utildefines.h" + +#include "DNA_node_types.h" + +#include "NOD_derived_node_tree.hh" +#include "NOD_node_declaration.hh" + +#include "GPU_compute.h" +#include "GPU_shader.h" + +#include "COM_operation.hh" +#include "COM_result.hh" +#include "COM_utilities.hh" + +namespace blender::realtime_compositor { + +using namespace nodes::derived_node_tree_types; +using TargetSocketPathInfo = DOutputSocket::TargetSocketPathInfo; + +DSocket get_input_origin_socket(DInputSocket input) +{ + /* The input is unlinked. Return the socket itself. */ + if (!input->is_logically_linked()) { + return input; + } + + /* Only a single origin socket is guaranteed to exist. */ + DSocket socket; + input.foreach_origin_socket([&](const DSocket origin) { socket = origin; }); + return socket; +} + +DOutputSocket get_output_linked_to_input(DInputSocket input) +{ + /* Get the origin socket of this input, which will be an output socket if the input is linked + * to an output. */ + const DSocket origin = get_input_origin_socket(input); + + /* If the origin socket is an input, that means the input is unlinked, so return a null output + * socket. */ + if (origin->is_input()) { + return DOutputSocket(); + } + + /* Now that we know the origin is an output, return a derived output from it. */ + return DOutputSocket(origin); +} + +ResultType get_node_socket_result_type(const bNodeSocket *socket) +{ + switch (socket->type) { + case SOCK_FLOAT: + return ResultType::Float; + case SOCK_VECTOR: + return ResultType::Vector; + case SOCK_RGBA: + return ResultType::Color; + default: + BLI_assert_unreachable(); + return ResultType::Float; + } +} + +bool is_output_linked_to_node_conditioned(DOutputSocket output, FunctionRef<bool(DNode)> condition) +{ + bool condition_satisfied = false; + output.foreach_target_socket( + [&](DInputSocket target, const TargetSocketPathInfo &UNUSED(path_info)) { + if (condition(target.node())) { + condition_satisfied = true; + return; + } + }); + return condition_satisfied; +} + +int number_of_inputs_linked_to_output_conditioned(DOutputSocket output, + FunctionRef<bool(DInputSocket)> condition) +{ + int count = 0; + output.foreach_target_socket( + [&](DInputSocket target, const TargetSocketPathInfo &UNUSED(path_info)) { + if (condition(target)) { + count++; + } + }); + return count; +} + +bool is_shader_node(DNode node) +{ + return node->typeinfo->get_compositor_shader_node; +} + +bool is_node_supported(DNode node) +{ + return node->typeinfo->get_compositor_operation || node->typeinfo->get_compositor_shader_node; +} + +InputDescriptor input_descriptor_from_input_socket(const bNodeSocket *socket) +{ + using namespace nodes; + InputDescriptor input_descriptor; + input_descriptor.type = get_node_socket_result_type(socket); + const NodeDeclaration *node_declaration = socket->owner_node().declaration(); + /* Not every node have a declaration, in which case, we assume the default values for the rest of + * the properties. */ + if (!node_declaration) { + return input_descriptor; + } + const SocketDeclarationPtr &socket_declaration = node_declaration->inputs()[socket->index()]; + input_descriptor.domain_priority = socket_declaration->compositor_domain_priority(); + input_descriptor.skip_realization = socket_declaration->compositor_skip_realization(); + input_descriptor.expects_single_value = socket_declaration->compositor_expects_single_value(); + return input_descriptor; +} + +void compute_dispatch_threads_at_least(GPUShader *shader, int2 threads_range, int2 local_size) +{ + /* If the threads range is divisible by the local size, dispatch the number of needed groups, + * which is their division. If it is not divisible, then dispatch an extra group to cover the + * remaining invocations, which means the actual threads range of the dispatch will be a bit + * larger than the given one. */ + const int2 groups_to_dispatch = math::divide_ceil(threads_range, local_size); + GPU_compute_dispatch(shader, groups_to_dispatch.x, groups_to_dispatch.y, 1); +} + +} // namespace blender::realtime_compositor |