Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHans Goudey <h.goudey@me.com>2021-08-23 22:10:39 +0300
committerHans Goudey <h.goudey@me.com>2021-08-23 22:10:39 +0300
commitb3639670ee001cc77480cdc8db54ce517d992e3b (patch)
tree6fd63d86a18915aac313c1e85853a809ed0df516
parentea3ee04fa8ba4613007bfbed2206b9af9d3aaddd (diff)
parentbe1891e895c012b36ca574a90b5d90fc1433152e (diff)
Merge branch 'master' into temp-geometry-nodes-fields-prototype
-rw-r--r--CMakeLists.txt3
-rw-r--r--build_files/cmake/Modules/FindZstd.cmake66
-rw-r--r--build_files/cmake/platform/platform_apple.cmake3
-rw-r--r--build_files/cmake/platform/platform_unix.cmake1
-rw-r--r--build_files/cmake/platform/platform_win32.cmake3
-rw-r--r--build_files/config/pipeline_config.yaml4
-rw-r--r--doc/python_api/requirements.txt12
-rw-r--r--intern/cycles/blender/addon/properties.py2
-rw-r--r--intern/cycles/blender/blender_object.cpp3
-rw-r--r--intern/cycles/blender/blender_util.h2
-rw-r--r--intern/cycles/kernel/closure/bsdf_principled_diffuse.h6
-rw-r--r--intern/cycles/render/alembic.cpp70
-rw-r--r--intern/cycles/render/alembic.h30
-rw-r--r--intern/cycles/render/alembic_read.cpp16
-rw-r--r--intern/ghost/GHOST_IEvent.h6
-rw-r--r--intern/ghost/intern/GHOST_SystemWin32.cpp2
-rw-r--r--release/scripts/startup/bl_ui/space_userpref.py1
-rw-r--r--source/blender/blenfont/BLF_api.h49
-rw-r--r--source/blender/blenfont/intern/blf.c93
-rw-r--r--source/blender/blenfont/intern/blf_default.c8
-rw-r--r--source/blender/blenfont/intern/blf_font.c772
-rw-r--r--source/blender/blenfont/intern/blf_glyph.c50
-rw-r--r--source/blender/blenfont/intern/blf_internal.h43
-rw-r--r--source/blender/blenfont/intern/blf_internal_types.h9
-rw-r--r--source/blender/blenkernel/BKE_cachefile.h5
-rw-r--r--source/blender/blenkernel/BKE_node.h17
-rw-r--r--source/blender/blenkernel/intern/bvhutils.cc3
-rw-r--r--source/blender/blenkernel/intern/cachefile.c8
-rw-r--r--source/blender/blenkernel/intern/gpencil_geom.cc28
-rw-r--r--source/blender/blenkernel/intern/lib_id.c4
-rw-r--r--source/blender/blenkernel/intern/simulation.cc4
-rw-r--r--source/blender/blenkernel/intern/text.c4
-rw-r--r--source/blender/blenlib/BLI_fileops.h21
-rw-r--r--source/blender/blenlib/BLI_filereader.h81
-rw-r--r--source/blender/blenlib/BLI_math_base.h2
-rw-r--r--source/blender/blenlib/BLI_string_utf8.h4
-rw-r--r--source/blender/blenlib/CMakeLists.txt7
-rw-r--r--source/blender/blenlib/intern/fileops.c243
-rw-r--r--source/blender/blenlib/intern/filereader_file.c80
-rw-r--r--source/blender/blenlib/intern/filereader_gzip.c108
-rw-r--r--source/blender/blenlib/intern/filereader_memory.c145
-rw-r--r--source/blender/blenlib/intern/filereader_zstd.c335
-rw-r--r--source/blender/blenlib/intern/freetypefont.c2
-rw-r--r--source/blender/blenlib/intern/math_base_inline.c8
-rw-r--r--source/blender/blenlib/intern/string_utf8.c6
-rw-r--r--source/blender/blenlib/tests/BLI_string_utf8_test.cc4
-rw-r--r--source/blender/blenloader/BLO_undofile.h14
-rw-r--r--source/blender/blenloader/CMakeLists.txt2
-rw-r--r--source/blender/blenloader/intern/readfile.c458
-rw-r--r--source/blender/blenloader/intern/readfile.h32
-rw-r--r--source/blender/blenloader/intern/undofile.c95
-rw-r--r--source/blender/blenloader/intern/versioning_250.c3
-rw-r--r--source/blender/blenloader/intern/versioning_legacy.c3
-rw-r--r--source/blender/blenloader/intern/writefile.c298
-rw-r--r--source/blender/compositor/CMakeLists.txt6
-rw-r--r--source/blender/compositor/COM_defines.h4
-rw-r--r--source/blender/compositor/intern/COM_CompositorContext.h6
-rw-r--r--source/blender/compositor/intern/COM_MemoryBuffer.cc69
-rw-r--r--source/blender/compositor/intern/COM_MemoryBuffer.h99
-rw-r--r--source/blender/compositor/intern/COM_NodeGraph.cc9
-rw-r--r--source/blender/compositor/intern/COM_NodeGraph.h4
-rw-r--r--source/blender/compositor/intern/COM_NodeOperation.cc6
-rw-r--r--source/blender/compositor/intern/COM_NodeOperation.h11
-rw-r--r--source/blender/compositor/nodes/COM_CryptomatteNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_RotateNode.cc25
-rw-r--r--source/blender/compositor/nodes/COM_Stabilize2dNode.cc100
-rw-r--r--source/blender/compositor/nodes/COM_TransformNode.cc61
-rw-r--r--source/blender/compositor/operations/COM_BlurBaseOperation.cc95
-rw-r--r--source/blender/compositor/operations/COM_BlurBaseOperation.h22
-rw-r--r--source/blender/compositor/operations/COM_BokehBlurOperation.cc178
-rw-r--r--source/blender/compositor/operations/COM_BokehBlurOperation.h12
-rw-r--r--source/blender/compositor/operations/COM_ChannelMatteOperation.cc34
-rw-r--r--source/blender/compositor/operations/COM_ChannelMatteOperation.h8
-rw-r--r--source/blender/compositor/operations/COM_ChromaMatteOperation.cc55
-rw-r--r--source/blender/compositor/operations/COM_ChromaMatteOperation.h8
-rw-r--r--source/blender/compositor/operations/COM_ColorMatteOperation.cc37
-rw-r--r--source/blender/compositor/operations/COM_ColorMatteOperation.h8
-rw-r--r--source/blender/compositor/operations/COM_ColorRampOperation.cc10
-rw-r--r--source/blender/compositor/operations/COM_ColorRampOperation.h8
-rw-r--r--source/blender/compositor/operations/COM_ColorSpillOperation.cc33
-rw-r--r--source/blender/compositor/operations/COM_ColorSpillOperation.h8
-rw-r--r--source/blender/compositor/operations/COM_ConvertOperation.cc164
-rw-r--r--source/blender/compositor/operations/COM_ConvertOperation.h68
-rw-r--r--source/blender/compositor/operations/COM_CropOperation.cc45
-rw-r--r--source/blender/compositor/operations/COM_CropOperation.h13
-rw-r--r--source/blender/compositor/operations/COM_CryptomatteOperation.cc30
-rw-r--r--source/blender/compositor/operations/COM_CryptomatteOperation.h8
-rw-r--r--source/blender/compositor/operations/COM_DifferenceMatteOperation.cc41
-rw-r--r--source/blender/compositor/operations/COM_DifferenceMatteOperation.h8
-rw-r--r--source/blender/compositor/operations/COM_DisplaceOperation.cc95
-rw-r--r--source/blender/compositor/operations/COM_DisplaceOperation.h22
-rw-r--r--source/blender/compositor/operations/COM_DisplaceSimpleOperation.cc52
-rw-r--r--source/blender/compositor/operations/COM_DisplaceSimpleOperation.h9
-rw-r--r--source/blender/compositor/operations/COM_DistanceRGBMatteOperation.cc42
-rw-r--r--source/blender/compositor/operations/COM_DistanceRGBMatteOperation.h10
-rw-r--r--source/blender/compositor/operations/COM_DistanceYCCMatteOperation.cc2
-rw-r--r--source/blender/compositor/operations/COM_DistanceYCCMatteOperation.h2
-rw-r--r--source/blender/compositor/operations/COM_FastGaussianBlurOperation.cc106
-rw-r--r--source/blender/compositor/operations/COM_FastGaussianBlurOperation.h21
-rw-r--r--source/blender/compositor/operations/COM_FlipOperation.cc38
-rw-r--r--source/blender/compositor/operations/COM_FlipOperation.h9
-rw-r--r--source/blender/compositor/operations/COM_GammaCorrectOperation.cc57
-rw-r--r--source/blender/compositor/operations/COM_GammaCorrectOperation.h14
-rw-r--r--source/blender/compositor/operations/COM_GaussianAlphaBlurBaseOperation.cc168
-rw-r--r--source/blender/compositor/operations/COM_GaussianAlphaBlurBaseOperation.h62
-rw-r--r--source/blender/compositor/operations/COM_GaussianAlphaXBlurOperation.cc13
-rw-r--r--source/blender/compositor/operations/COM_GaussianAlphaXBlurOperation.h23
-rw-r--r--source/blender/compositor/operations/COM_GaussianAlphaYBlurOperation.cc15
-rw-r--r--source/blender/compositor/operations/COM_GaussianAlphaYBlurOperation.h23
-rw-r--r--source/blender/compositor/operations/COM_GaussianBlurBaseOperation.cc154
-rw-r--r--source/blender/compositor/operations/COM_GaussianBlurBaseOperation.h50
-rw-r--r--source/blender/compositor/operations/COM_GaussianBokehBlurOperation.cc213
-rw-r--r--source/blender/compositor/operations/COM_GaussianBokehBlurOperation.h18
-rw-r--r--source/blender/compositor/operations/COM_GaussianXBlurOperation.cc15
-rw-r--r--source/blender/compositor/operations/COM_GaussianXBlurOperation.h11
-rw-r--r--source/blender/compositor/operations/COM_GaussianYBlurOperation.cc13
-rw-r--r--source/blender/compositor/operations/COM_GaussianYBlurOperation.h11
-rw-r--r--source/blender/compositor/operations/COM_KeyingBlurOperation.cc63
-rw-r--r--source/blender/compositor/operations/COM_KeyingBlurOperation.h11
-rw-r--r--source/blender/compositor/operations/COM_KeyingClipOperation.cc85
-rw-r--r--source/blender/compositor/operations/COM_KeyingClipOperation.h11
-rw-r--r--source/blender/compositor/operations/COM_KeyingDespillOperation.cc29
-rw-r--r--source/blender/compositor/operations/COM_KeyingDespillOperation.h8
-rw-r--r--source/blender/compositor/operations/COM_KeyingOperation.cc45
-rw-r--r--source/blender/compositor/operations/COM_KeyingOperation.h8
-rw-r--r--source/blender/compositor/operations/COM_KeyingScreenOperation.cc95
-rw-r--r--source/blender/compositor/operations/COM_KeyingScreenOperation.h12
-rw-r--r--source/blender/compositor/operations/COM_LuminanceMatteOperation.cc36
-rw-r--r--source/blender/compositor/operations/COM_LuminanceMatteOperation.h8
-rw-r--r--source/blender/compositor/operations/COM_MapUVOperation.cc97
-rw-r--r--source/blender/compositor/operations/COM_MapUVOperation.h21
-rw-r--r--source/blender/compositor/operations/COM_MathBaseOperation.cc322
-rw-r--r--source/blender/compositor/operations/COM_MathBaseOperation.h154
-rw-r--r--source/blender/compositor/operations/COM_MovieDistortionOperation.cc47
-rw-r--r--source/blender/compositor/operations/COM_MovieDistortionOperation.h9
-rw-r--r--source/blender/compositor/operations/COM_PlaneCornerPinOperation.cc99
-rw-r--r--source/blender/compositor/operations/COM_PlaneCornerPinOperation.h7
-rw-r--r--source/blender/compositor/operations/COM_PlaneDistortCommonOperation.cc115
-rw-r--r--source/blender/compositor/operations/COM_PlaneDistortCommonOperation.h16
-rw-r--r--source/blender/compositor/operations/COM_PlaneTrackOperation.cc26
-rw-r--r--source/blender/compositor/operations/COM_PlaneTrackOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_ProjectorLensDistortionOperation.cc54
-rw-r--r--source/blender/compositor/operations/COM_ProjectorLensDistortionOperation.h11
-rw-r--r--source/blender/compositor/operations/COM_RotateOperation.cc86
-rw-r--r--source/blender/compositor/operations/COM_RotateOperation.h37
-rw-r--r--source/blender/compositor/operations/COM_ScaleOperation.cc13
-rw-r--r--source/blender/compositor/operations/COM_ScaleOperation.h9
-rw-r--r--source/blender/compositor/operations/COM_ScreenLensDistortionOperation.cc189
-rw-r--r--source/blender/compositor/operations/COM_ScreenLensDistortionOperation.h13
-rw-r--r--source/blender/compositor/operations/COM_SetAlphaMultiplyOperation.cc12
-rw-r--r--source/blender/compositor/operations/COM_SetAlphaMultiplyOperation.h8
-rw-r--r--source/blender/compositor/operations/COM_SetAlphaReplaceOperation.cc13
-rw-r--r--source/blender/compositor/operations/COM_SetAlphaReplaceOperation.h8
-rw-r--r--source/blender/compositor/operations/COM_TransformOperation.cc156
-rw-r--r--source/blender/compositor/operations/COM_TransformOperation.h87
-rw-r--r--source/blender/compositor/operations/COM_VariableSizeBokehBlurOperation.cc161
-rw-r--r--source/blender/compositor/operations/COM_VariableSizeBokehBlurOperation.h17
-rw-r--r--source/blender/draw/engines/eevee/eevee_lookdev.c2
-rw-r--r--source/blender/draw/engines/image/image_engine.c4
-rw-r--r--source/blender/draw/engines/overlay/overlay_extra.c5
-rw-r--r--source/blender/draw/intern/draw_cache_extract.h178
-rw-r--r--source/blender/draw/intern/draw_cache_extract_mesh.cc53
-rw-r--r--source/blender/draw/intern/draw_cache_extract_mesh_render_data.c53
-rw-r--r--source/blender/draw/intern/draw_cache_impl_mesh.c689
-rw-r--r--source/blender/draw/intern/mesh_extractors/extract_mesh.c6
-rw-r--r--source/blender/draw/intern/mesh_extractors/extract_mesh.h8
-rw-r--r--source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_edituv.cc8
-rw-r--r--source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_fdots.cc2
-rw-r--r--source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines.cc12
-rw-r--r--source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines_adjacency.cc2
-rw-r--r--source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines_paint_mask.cc2
-rw-r--r--source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_points.cc2
-rw-r--r--source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_tris.cc22
-rw-r--r--source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_edge_fac.cc2
-rw-r--r--source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_edit_data.cc2
-rw-r--r--source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_edituv_data.cc2
-rw-r--r--source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_edituv_stretch_angle.cc2
-rw-r--r--source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_edituv_stretch_area.cc2
-rw-r--r--source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_fdots_edituv_data.cc2
-rw-r--r--source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_fdots_nor.cc4
-rw-r--r--source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_fdots_pos.cc2
-rw-r--r--source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_fdots_uv.cc2
-rw-r--r--source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_lnor.cc4
-rw-r--r--source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_mesh_analysis.cc2
-rw-r--r--source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_orco.cc2
-rw-r--r--source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_pos_nor.cc4
-rw-r--r--source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_sculpt_data.cc2
-rw-r--r--source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_select_idx.cc8
-rw-r--r--source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_skin_roots.cc2
-rw-r--r--source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_tan.cc4
-rw-r--r--source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_uv.cc2
-rw-r--r--source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_vcol.cc2
-rw-r--r--source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_weights.cc2
-rw-r--r--source/blender/editors/curve/editfont.c4
-rw-r--r--source/blender/editors/gpencil/gpencil_interpolate.c4
-rw-r--r--source/blender/editors/gpencil/gpencil_primitive.c4
-rw-r--r--source/blender/editors/include/UI_resources.h2
-rw-r--r--source/blender/editors/interface/interface_handlers.c12
-rw-r--r--source/blender/editors/interface/interface_templates.c11
-rw-r--r--source/blender/editors/interface/resources.c2
-rw-r--r--source/blender/editors/mesh/editmesh_tools.c2
-rw-r--r--source/blender/editors/physics/particle_edit.c2
-rw-r--r--source/blender/editors/render/render_update.c32
-rw-r--r--source/blender/editors/space_buttons/buttons_ops.c12
-rw-r--r--source/blender/editors/space_console/console_ops.c6
-rw-r--r--source/blender/editors/space_image/image_ops.c2
-rw-r--r--source/blender/editors/space_image/image_sequence.c2
-rw-r--r--source/blender/editors/space_info/info_ops.c2
-rw-r--r--source/blender/editors/space_node/node_draw.cc4
-rw-r--r--source/blender/editors/space_sequencer/sequencer_add.c10
-rw-r--r--source/blender/editors/space_sequencer/sequencer_edit.c2
-rw-r--r--source/blender/editors/space_text/text_ops.c2
-rw-r--r--source/blender/editors/space_view3d/view3d_gizmo_ruler.c4
-rw-r--r--source/blender/editors/transform/transform_convert.c13
-rw-r--r--source/blender/editors/transform/transform_convert.h4
-rw-r--r--source/blender/editors/transform/transform_convert_action.c20
-rw-r--r--source/blender/editors/transform/transform_convert_armature.c4
-rw-r--r--source/blender/editors/transform/transform_convert_object.c2
-rw-r--r--source/blender/editors/transform/transform_snap_animation.c4
-rw-r--r--source/blender/editors/util/numinput.c2
-rw-r--r--source/blender/functions/CMakeLists.txt7
-rw-r--r--source/blender/functions/FN_generic_virtual_array.hh2
-rw-r--r--source/blender/functions/FN_multi_function_network.hh536
-rw-r--r--source/blender/functions/FN_multi_function_network_evaluation.hh62
-rw-r--r--source/blender/functions/FN_multi_function_network_optimization.hh29
-rw-r--r--source/blender/functions/FN_multi_function_signature.hh15
-rw-r--r--source/blender/functions/intern/multi_function_network.cc330
-rw-r--r--source/blender/functions/intern/multi_function_network_evaluation.cc1083
-rw-r--r--source/blender/functions/intern/multi_function_network_optimization.cc501
-rw-r--r--source/blender/functions/tests/FN_multi_function_network_test.cc280
-rw-r--r--source/blender/functions/tests/FN_multi_function_test.cc92
-rw-r--r--source/blender/functions/tests/FN_multi_function_test_common.hh174
-rw-r--r--source/blender/io/collada/collada_internal.cpp2
-rw-r--r--source/blender/makesdna/DNA_cachefile_defaults.h2
-rw-r--r--source/blender/makesdna/DNA_cachefile_types.h10
-rw-r--r--source/blender/makesrna/RNA_access.h9
-rw-r--r--source/blender/makesrna/intern/rna_access.c25
-rw-r--r--source/blender/makesrna/intern/rna_cachefile.c17
-rw-r--r--source/blender/makesrna/intern/rna_main_api.c2
-rw-r--r--source/blender/modifiers/intern/MOD_nodes.cc5
-rw-r--r--source/blender/modifiers/intern/MOD_nodes_evaluator.cc2
-rw-r--r--source/blender/modifiers/intern/MOD_nodes_evaluator.hh6
-rw-r--r--source/blender/modifiers/intern/MOD_subsurf.c9
-rw-r--r--source/blender/nodes/CMakeLists.txt6
-rw-r--r--source/blender/nodes/NOD_multi_function.hh130
-rw-r--r--source/blender/nodes/NOD_node_tree_multi_function.hh390
-rw-r--r--source/blender/nodes/composite/node_composite_tree.c2
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_idMask.c2
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_math.c2
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_premulkey.c2
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_sepcombHSVA.c4
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_sepcombRGBA.c4
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_sepcombYCCA.c4
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_sepcombYUVA.c4
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_setalpha.c2
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_switchview.c2
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_valToRgb.c4
-rw-r--r--source/blender/nodes/function/node_function_util.hh2
-rw-r--r--source/blender/nodes/function/nodes/node_fn_boolean_math.cc21
-rw-r--r--source/blender/nodes/function/nodes/node_fn_float_compare.cc27
-rw-r--r--source/blender/nodes/function/nodes/node_fn_float_to_int.cc21
-rw-r--r--source/blender/nodes/function/nodes/node_fn_input_string.cc12
-rw-r--r--source/blender/nodes/function/nodes/node_fn_input_vector.cc10
-rw-r--r--source/blender/nodes/function/nodes/node_fn_random_float.cc9
-rw-r--r--source/blender/nodes/geometry/node_geometry_tree.cc2
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_switch.cc2
-rw-r--r--source/blender/nodes/intern/node_geometry_exec.cc1
-rw-r--r--source/blender/nodes/intern/node_multi_function.cc (renamed from source/blender/nodes/NOD_type_callbacks.hh)32
-rw-r--r--source/blender/nodes/intern/node_socket.cc1
-rw-r--r--source/blender/nodes/intern/node_tree_multi_function.cc409
-rw-r--r--source/blender/nodes/intern/type_callbacks.cc60
-rw-r--r--source/blender/nodes/shader/node_shader_tree.c2
-rw-r--r--source/blender/nodes/shader/node_shader_util.h2
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_blackbody.c2
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_clamp.cc8
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_curves.cc14
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_map_range.cc10
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_math.cc69
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_mixRgb.cc6
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_sepcombHSV.c4
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_sepcombRGB.cc12
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_sepcombXYZ.cc12
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_shaderToRgb.c2
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_squeeze.c2
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_valToRgb.cc11
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_value.cc6
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_vector_math.cc28
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_vector_rotate.cc37
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_wavelength.c2
-rw-r--r--source/blender/nodes/texture/node_texture_tree.c2
-rw-r--r--source/blender/nodes/texture/nodes/node_texture_distance.c2
-rw-r--r--source/blender/nodes/texture/nodes/node_texture_math.c2
-rw-r--r--source/blender/nodes/texture/nodes/node_texture_valToNor.c2
-rw-r--r--source/blender/nodes/texture/nodes/node_texture_valToRgb.c4
-rw-r--r--source/blender/python/intern/bpy_app_icons.c6
-rw-r--r--source/blender/sequencer/intern/image_cache.c32
-rw-r--r--source/blender/sequencer/intern/strip_edit.c2
-rw-r--r--source/blender/windowmanager/CMakeLists.txt4
-rw-r--r--source/blender/windowmanager/intern/wm_event_system.c4
-rw-r--r--source/blender/windowmanager/intern/wm_files.c93
-rw-r--r--source/blender/windowmanager/intern/wm_operators.c2
301 files changed, 7791 insertions, 6277 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 2868324bf46..47712f0ac1e 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1598,6 +1598,9 @@ elseif(CMAKE_C_COMPILER_ID MATCHES "Clang")
ADD_CHECK_C_COMPILER_FLAG(C_WARNINGS C_WARN_UNUSED_PARAMETER -Wunused-parameter)
ADD_CHECK_CXX_COMPILER_FLAG(CXX_WARNINGS CXX_WARN_ALL -Wall)
+ # Using C++20 features while having C++17 as the project language isn't allowed by MSVC.
+ ADD_CHECK_CXX_COMPILER_FLAG(CXX_WARNINGS CXX_CXX20_DESIGNATOR -Wc++20-designator)
+
ADD_CHECK_CXX_COMPILER_FLAG(CXX_WARNINGS CXX_WARN_NO_AUTOLOGICAL_COMPARE -Wno-tautological-compare)
ADD_CHECK_CXX_COMPILER_FLAG(CXX_WARNINGS CXX_WARN_NO_UNKNOWN_PRAGMAS -Wno-unknown-pragmas)
ADD_CHECK_CXX_COMPILER_FLAG(CXX_WARNINGS CXX_WARN_NO_CHAR_SUBSCRIPTS -Wno-char-subscripts)
diff --git a/build_files/cmake/Modules/FindZstd.cmake b/build_files/cmake/Modules/FindZstd.cmake
new file mode 100644
index 00000000000..84606d01f44
--- /dev/null
+++ b/build_files/cmake/Modules/FindZstd.cmake
@@ -0,0 +1,66 @@
+# - Find Zstd library
+# Find the native Zstd includes and library
+# This module defines
+# ZSTD_INCLUDE_DIRS, where to find zstd.h, Set when
+# ZSTD_INCLUDE_DIR is found.
+# ZSTD_LIBRARIES, libraries to link against to use Zstd.
+# ZSTD_ROOT_DIR, The base directory to search for Zstd.
+# This can also be an environment variable.
+# ZSTD_FOUND, If false, do not try to use Zstd.
+#
+# also defined, but not for general use are
+# ZSTD_LIBRARY, where to find the Zstd library.
+
+#=============================================================================
+# Copyright 2019 Blender Foundation.
+#
+# Distributed under the OSI-approved BSD License (the "License");
+# see accompanying file Copyright.txt for details.
+#
+# This software is distributed WITHOUT ANY WARRANTY; without even the
+# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+# See the License for more information.
+#=============================================================================
+
+# If ZSTD_ROOT_DIR was defined in the environment, use it.
+IF(NOT ZSTD_ROOT_DIR AND NOT $ENV{ZSTD_ROOT_DIR} STREQUAL "")
+ SET(ZSTD_ROOT_DIR $ENV{ZSTD_ROOT_DIR})
+ENDIF()
+
+SET(_zstd_SEARCH_DIRS
+ ${ZSTD_ROOT_DIR}
+)
+
+FIND_PATH(ZSTD_INCLUDE_DIR
+ NAMES
+ zstd.h
+ HINTS
+ ${_zstd_SEARCH_DIRS}
+ PATH_SUFFIXES
+ include
+)
+
+FIND_LIBRARY(ZSTD_LIBRARY
+ NAMES
+ zstd
+ HINTS
+ ${_zstd_SEARCH_DIRS}
+ PATH_SUFFIXES
+ lib64 lib
+ )
+
+# handle the QUIETLY and REQUIRED arguments and set ZSTD_FOUND to TRUE if
+# all listed variables are TRUE
+INCLUDE(FindPackageHandleStandardArgs)
+FIND_PACKAGE_HANDLE_STANDARD_ARGS(Zstd DEFAULT_MSG
+ ZSTD_LIBRARY ZSTD_INCLUDE_DIR)
+
+IF(ZSTD_FOUND)
+ SET(ZSTD_LIBRARIES ${ZSTD_LIBRARY})
+ SET(ZSTD_INCLUDE_DIRS ${ZSTD_INCLUDE_DIR})
+ENDIF()
+
+MARK_AS_ADVANCED(
+ ZSTD_INCLUDE_DIR
+ ZSTD_LIBRARY
+)
diff --git a/build_files/cmake/platform/platform_apple.cmake b/build_files/cmake/platform/platform_apple.cmake
index dceafb236de..2bfdc84ec2a 100644
--- a/build_files/cmake/platform/platform_apple.cmake
+++ b/build_files/cmake/platform/platform_apple.cmake
@@ -441,6 +441,9 @@ if(WITH_HARU)
endif()
endif()
+set(ZSTD_ROOT_DIR ${LIBDIR}/zstd)
+find_package(Zstd REQUIRED)
+
if(EXISTS ${LIBDIR})
without_system_libs_end()
endif()
diff --git a/build_files/cmake/platform/platform_unix.cmake b/build_files/cmake/platform/platform_unix.cmake
index 7f62399ac4f..fc0c37e4c8b 100644
--- a/build_files/cmake/platform/platform_unix.cmake
+++ b/build_files/cmake/platform/platform_unix.cmake
@@ -99,6 +99,7 @@ endif()
find_package_wrapper(JPEG REQUIRED)
find_package_wrapper(PNG REQUIRED)
find_package_wrapper(ZLIB REQUIRED)
+find_package_wrapper(Zstd REQUIRED)
find_package_wrapper(Freetype REQUIRED)
if(WITH_PYTHON)
diff --git a/build_files/cmake/platform/platform_win32.cmake b/build_files/cmake/platform/platform_win32.cmake
index 3773aaaffed..d44ef691d1b 100644
--- a/build_files/cmake/platform/platform_win32.cmake
+++ b/build_files/cmake/platform/platform_win32.cmake
@@ -873,3 +873,6 @@ if(WITH_HARU)
set(WITH_HARU OFF)
endif()
endif()
+
+set(ZSTD_INCLUDE_DIRS ${LIBDIR}/zstd/include)
+set(ZSTD_LIBRARIES ${LIBDIR}/zstd/lib/zstd_static.lib)
diff --git a/build_files/config/pipeline_config.yaml b/build_files/config/pipeline_config.yaml
index 611df59caec..5d1a24a30f1 100644
--- a/build_files/config/pipeline_config.yaml
+++ b/build_files/config/pipeline_config.yaml
@@ -51,9 +51,9 @@ buildbot:
gcc:
version: '9.0.0'
cuda10:
- version: '10.1.0'
+ version: '10.1.243'
cuda11:
- version: '11.4.0'
+ version: '11.4.1'
optix:
version: '7.1.0'
cmake:
diff --git a/doc/python_api/requirements.txt b/doc/python_api/requirements.txt
index a854d2a267b..b5a9d15bf7b 100644
--- a/doc/python_api/requirements.txt
+++ b/doc/python_api/requirements.txt
@@ -1,13 +1,13 @@
-sphinx==3.5.4
+sphinx==4.1.1
# Sphinx dependencies that are important
-Jinja2==2.11.3
-Pygments==2.9.0
-docutils==0.16
+Jinja2==3.0.1
+Pygments==2.10.0
+docutils==0.17.1
snowballstemmer==2.1.0
babel==2.9.1
-requests==2.25.1
+requests==2.26.0
# Only needed to match the theme used for the official documentation.
# Without this theme, the default theme will be used.
-sphinx_rtd_theme==0.5.2
+sphinx_rtd_theme==1.0.0rc1
diff --git a/intern/cycles/blender/addon/properties.py b/intern/cycles/blender/addon/properties.py
index ac0aca57028..0c3af3fabeb 100644
--- a/intern/cycles/blender/addon/properties.py
+++ b/intern/cycles/blender/addon/properties.py
@@ -408,7 +408,7 @@ class CyclesRenderSettings(bpy.types.PropertyGroup):
adaptive_threshold: FloatProperty(
name="Adaptive Sampling Threshold",
- description="Noise level step to stop sampling at, lower values reduce noise the cost of render time. Zero for automatic setting based on number of AA samples",
+ description="Noise level step to stop sampling at, lower values reduce noise at the cost of render time. Zero for automatic setting based on number of AA samples",
min=0.0, max=1.0,
default=0.0,
precision=4,
diff --git a/intern/cycles/blender/blender_object.cpp b/intern/cycles/blender/blender_object.cpp
index 2dbebac4cc3..a7eae421b55 100644
--- a/intern/cycles/blender/blender_object.cpp
+++ b/intern/cycles/blender/blender_object.cpp
@@ -523,6 +523,9 @@ void BlenderSync::sync_procedural(BL::Object &b_ob,
procedural->set_scale(cache_file.scale());
+ procedural->set_use_prefetch(cache_file.use_prefetch());
+ procedural->set_prefetch_cache_size(cache_file.prefetch_cache_size());
+
/* create or update existing AlembicObjects */
ustring object_path = ustring(b_mesh_cache.object_path());
diff --git a/intern/cycles/blender/blender_util.h b/intern/cycles/blender/blender_util.h
index 3cf75b338dc..f6824f31b7b 100644
--- a/intern/cycles/blender/blender_util.h
+++ b/intern/cycles/blender/blender_util.h
@@ -424,7 +424,7 @@ static inline void set_enum(PointerRNA &ptr, const char *name, const string &ide
static inline string get_string(PointerRNA &ptr, const char *name)
{
char cstrbuf[1024];
- char *cstr = RNA_string_get_alloc(&ptr, name, cstrbuf, sizeof(cstrbuf));
+ char *cstr = RNA_string_get_alloc(&ptr, name, cstrbuf, sizeof(cstrbuf), NULL);
string str(cstr);
if (cstr != cstrbuf)
MEM_freeN(cstr);
diff --git a/intern/cycles/kernel/closure/bsdf_principled_diffuse.h b/intern/cycles/kernel/closure/bsdf_principled_diffuse.h
index 43646aaeb5b..d5d012068ff 100644
--- a/intern/cycles/kernel/closure/bsdf_principled_diffuse.h
+++ b/intern/cycles/kernel/closure/bsdf_principled_diffuse.h
@@ -36,10 +36,10 @@ static_assert(sizeof(ShaderClosure) >= sizeof(PrincipledDiffuseBsdf),
ccl_device float3 calculate_principled_diffuse_brdf(
const PrincipledDiffuseBsdf *bsdf, float3 N, float3 V, float3 L, float3 H, float *pdf)
{
- float NdotL = max(dot(N, L), 0.0f);
- float NdotV = max(dot(N, V), 0.0f);
+ float NdotL = dot(N, L);
+ float NdotV = dot(N, V);
- if (NdotL < 0 || NdotV < 0) {
+ if (NdotL <= 0 || NdotV <= 0) {
*pdf = 0.0f;
return make_float3(0.0f, 0.0f, 0.0f);
}
diff --git a/intern/cycles/render/alembic.cpp b/intern/cycles/render/alembic.cpp
index 81f47256739..69bc0712674 100644
--- a/intern/cycles/render/alembic.cpp
+++ b/intern/cycles/render/alembic.cpp
@@ -25,6 +25,7 @@
#include "render/shader.h"
#include "util/util_foreach.h"
+#include "util/util_logging.h"
#include "util/util_progress.h"
#include "util/util_transform.h"
#include "util/util_vector.h"
@@ -211,6 +212,35 @@ void CachedData::set_time_sampling(TimeSampling time_sampling)
}
}
+size_t CachedData::memory_used() const
+{
+ size_t mem_used = 0;
+
+ mem_used += curve_first_key.memory_used();
+ mem_used += curve_keys.memory_used();
+ mem_used += curve_radius.memory_used();
+ mem_used += curve_shader.memory_used();
+ mem_used += num_ngons.memory_used();
+ mem_used += shader.memory_used();
+ mem_used += subd_creases_edge.memory_used();
+ mem_used += subd_creases_weight.memory_used();
+ mem_used += subd_face_corners.memory_used();
+ mem_used += subd_num_corners.memory_used();
+ mem_used += subd_ptex_offset.memory_used();
+ mem_used += subd_smooth.memory_used();
+ mem_used += subd_start_corner.memory_used();
+ mem_used += transforms.memory_used();
+ mem_used += triangles.memory_used();
+ mem_used += uv_loops.memory_used();
+ mem_used += vertices.memory_used();
+
+ for (const CachedAttribute &attr : attributes) {
+ mem_used += attr.data.memory_used();
+ }
+
+ return mem_used;
+}
+
static M44d convert_yup_zup(const M44d &mtx, float scale_mult)
{
V3d scale, shear, rotation, translation;
@@ -706,6 +736,9 @@ NODE_DEFINE(AlembicProcedural)
SOCKET_NODE_ARRAY(objects, "Objects", AlembicObject::get_node_type());
+ SOCKET_BOOLEAN(use_prefetch, "Use Prefetch", true);
+ SOCKET_INT(prefetch_cache_size, "Prefetch Cache Size", 4096);
+
return type;
}
@@ -823,6 +856,30 @@ void AlembicProcedural::generate(Scene *scene, Progress &progress)
}
}
+ if (use_prefetch_is_modified()) {
+ if (!use_prefetch) {
+ for (Node *node : objects) {
+ AlembicObject *object = static_cast<AlembicObject *>(node);
+ object->clear_cache();
+ }
+ }
+ }
+
+ if (prefetch_cache_size_is_modified()) {
+ /* Check whether the current memory usage fits in the new requested size,
+ * abort the render if it is any higher. */
+ size_t memory_used = 0ul;
+ for (Node *node : objects) {
+ AlembicObject *object = static_cast<AlembicObject *>(node);
+ memory_used += object->get_cached_data().memory_used();
+ }
+
+ if (memory_used > get_prefetch_cache_size_in_bytes()) {
+ progress.set_error("Error: Alembic Procedural memory limit reached");
+ return;
+ }
+ }
+
build_caches(progress);
foreach (Node *node, objects) {
@@ -1300,6 +1357,8 @@ void AlembicProcedural::walk_hierarchy(
void AlembicProcedural::build_caches(Progress &progress)
{
+ size_t memory_used = 0;
+
for (Node *node : objects) {
AlembicObject *object = static_cast<AlembicObject *>(node);
@@ -1353,7 +1412,18 @@ void AlembicProcedural::build_caches(Progress &progress)
if (scale_is_modified() || object->get_cached_data().transforms.size() == 0) {
object->setup_transform_cache(object->get_cached_data(), scale);
}
+
+ memory_used += object->get_cached_data().memory_used();
+
+ if (use_prefetch) {
+ if (memory_used > get_prefetch_cache_size_in_bytes()) {
+ progress.set_error("Error: Alembic Procedural memory limit reached");
+ return;
+ }
+ }
}
+
+ VLOG(1) << "AlembicProcedural memory usage : " << string_human_readable_size(memory_used);
}
CCL_NAMESPACE_END
diff --git a/intern/cycles/render/alembic.h b/intern/cycles/render/alembic.h
index 9c58af720f6..8e166a5ab04 100644
--- a/intern/cycles/render/alembic.h
+++ b/intern/cycles/render/alembic.h
@@ -272,6 +272,21 @@ template<typename T> class DataStore {
node->set(*socket, value);
}
+ size_t memory_used() const
+ {
+ if constexpr (is_array<T>::value) {
+ size_t mem_used = 0;
+
+ for (const T &array : data) {
+ mem_used += array.size() * sizeof(array[0]);
+ }
+
+ return mem_used;
+ }
+
+ return data.size() * sizeof(T);
+ }
+
private:
const TimeIndexPair &get_index_for_time(double time) const
{
@@ -332,6 +347,8 @@ struct CachedData {
void invalidate_last_loaded_time(bool attributes_only = false);
void set_time_sampling(Alembic::AbcCoreAbstract::TimeSampling time_sampling);
+
+ size_t memory_used() const;
};
/* Representation of an Alembic object for the AlembicProcedural.
@@ -482,6 +499,13 @@ class AlembicProcedural : public Procedural {
* software. */
NODE_SOCKET_API(float, scale)
+ /* Cache controls */
+ NODE_SOCKET_API(bool, use_prefetch)
+
+ /* Memory limit for the cache, if the data does not fit within this limit, rendering is aborted.
+ */
+ NODE_SOCKET_API(int, prefetch_cache_size)
+
AlembicProcedural();
~AlembicProcedural();
@@ -531,6 +555,12 @@ class AlembicProcedural : public Procedural {
void read_subd(AlembicObject *abc_object, Alembic::AbcGeom::Abc::chrono_t frame_time);
void build_caches(Progress &progress);
+
+ size_t get_prefetch_cache_size_in_bytes() const
+ {
+ /* prefetch_cache_size is in megabytes, so convert to bytes. */
+ return static_cast<size_t>(prefetch_cache_size) * 1024 * 1024;
+ }
};
CCL_NAMESPACE_END
diff --git a/intern/cycles/render/alembic_read.cpp b/intern/cycles/render/alembic_read.cpp
index c53ec668938..b105af63b44 100644
--- a/intern/cycles/render/alembic_read.cpp
+++ b/intern/cycles/render/alembic_read.cpp
@@ -44,9 +44,19 @@ static set<chrono_t> get_relevant_sample_times(AlembicProcedural *proc,
return result;
}
- // load the data for the entire animation
- const double start_frame = static_cast<double>(proc->get_start_frame());
- const double end_frame = static_cast<double>(proc->get_end_frame());
+ double start_frame;
+ double end_frame;
+
+ if (proc->get_use_prefetch()) {
+ // load the data for the entire animation
+ start_frame = static_cast<double>(proc->get_start_frame());
+ end_frame = static_cast<double>(proc->get_end_frame());
+ }
+ else {
+ // load the data for the current frame
+ start_frame = static_cast<double>(proc->get_frame());
+ end_frame = start_frame;
+ }
const double frame_rate = static_cast<double>(proc->get_frame_rate());
const double start_time = start_frame / frame_rate;
diff --git a/intern/ghost/GHOST_IEvent.h b/intern/ghost/GHOST_IEvent.h
index bcccd536ebd..239eea21088 100644
--- a/intern/ghost/GHOST_IEvent.h
+++ b/intern/ghost/GHOST_IEvent.h
@@ -32,10 +32,10 @@ class GHOST_IWindow;
/**
* Interface class for events received from GHOST.
* You should not need to inherit this class. The system will pass these events
- * to the GHOST_IEventConsumer::processEvent() method of event consumers.<br>
- * Use the getType() method to retrieve the type of event and the getData()
+ * to the #GHOST_IEventConsumer::processEvent() method of event consumers.<br>
+ * Use the #getType() method to retrieve the type of event and the #getData()
* method to get the event data out. Using the event type you can cast the
- * event data to the correct event dat structure.
+ * event data to the correct event data structure.
* \see GHOST_IEventConsumer#processEvent
* \see GHOST_TEventType
*/
diff --git a/intern/ghost/intern/GHOST_SystemWin32.cpp b/intern/ghost/intern/GHOST_SystemWin32.cpp
index 347067eae50..f44107ee000 100644
--- a/intern/ghost/intern/GHOST_SystemWin32.cpp
+++ b/intern/ghost/intern/GHOST_SystemWin32.cpp
@@ -1741,7 +1741,7 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam,
case WM_MOUSELEAVE: {
window->m_mousePresent = false;
if (window->getTabletData().Active == GHOST_kTabletModeNone) {
- processCursorEvent(window);
+ event = processCursorEvent(window);
}
GHOST_Wintab *wt = window->getWintab();
if (wt) {
diff --git a/release/scripts/startup/bl_ui/space_userpref.py b/release/scripts/startup/bl_ui/space_userpref.py
index 003f2f223ea..708701c4804 100644
--- a/release/scripts/startup/bl_ui/space_userpref.py
+++ b/release/scripts/startup/bl_ui/space_userpref.py
@@ -997,7 +997,6 @@ class USERPREF_PT_theme_text_style(ThemePanel, CenterAlignMixIn, Panel):
flow = layout.grid_flow(row_major=False, columns=0, even_columns=True, even_rows=False, align=True)
col = flow.column()
- col.row().prop(font_style, "font_kerning_style", expand=True)
col.prop(font_style, "points")
col = flow.column(align=True)
diff --git a/source/blender/blenfont/BLF_api.h b/source/blender/blenfont/BLF_api.h
index eb3f9805240..4de7e704a7e 100644
--- a/source/blender/blenfont/BLF_api.h
+++ b/source/blender/blenfont/BLF_api.h
@@ -98,13 +98,13 @@ void BLF_batch_draw_flush(void);
void BLF_batch_draw_end(void);
/* Draw the string using the current font. */
-void BLF_draw_ex(int fontid, const char *str, size_t len, struct ResultBLF *r_info)
+void BLF_draw_ex(int fontid, const char *str, size_t str_len, struct ResultBLF *r_info)
ATTR_NONNULL(2);
-void BLF_draw(int fontid, const char *str, size_t len) ATTR_NONNULL(2);
-void BLF_draw_ascii_ex(int fontid, const char *str, size_t len, struct ResultBLF *r_info)
+void BLF_draw(int fontid, const char *str, size_t str_len) ATTR_NONNULL(2);
+void BLF_draw_ascii_ex(int fontid, const char *str, size_t str_len, struct ResultBLF *r_info)
ATTR_NONNULL(2);
-void BLF_draw_ascii(int fontid, const char *str, size_t len) ATTR_NONNULL(2);
-int BLF_draw_mono(int fontid, const char *str, size_t len, int cwidth) ATTR_NONNULL(2);
+void BLF_draw_ascii(int fontid, const char *str, size_t str_len) ATTR_NONNULL(2);
+int BLF_draw_mono(int fontid, const char *str, size_t str_len, int cwidth) ATTR_NONNULL(2);
typedef bool (*BLF_GlyphBoundsFn)(const char *str,
const size_t str_step_ofs,
@@ -116,43 +116,45 @@ typedef bool (*BLF_GlyphBoundsFn)(const char *str,
void BLF_boundbox_foreach_glyph_ex(int fontid,
const char *str,
- size_t len,
+ size_t str_len,
BLF_GlyphBoundsFn user_fn,
void *user_data,
struct ResultBLF *r_info) ATTR_NONNULL(2);
void BLF_boundbox_foreach_glyph(int fontid,
const char *str,
- size_t len,
+ size_t str_len,
BLF_GlyphBoundsFn user_fn,
void *user_data) ATTR_NONNULL(2);
/* Get the string byte offset that fits within a given width */
-size_t BLF_width_to_strlen(int fontid, const char *str, size_t len, float width, float *r_width)
- ATTR_NONNULL(2);
+size_t BLF_width_to_strlen(
+ int fontid, const char *str, size_t str_len, float width, float *r_width) ATTR_NONNULL(2);
/* Same as BLF_width_to_strlen but search from the string end */
-size_t BLF_width_to_rstrlen(int fontid, const char *str, size_t len, float width, float *r_width)
- ATTR_NONNULL(2);
+size_t BLF_width_to_rstrlen(
+ int fontid, const char *str, size_t str_len, float width, float *r_width) ATTR_NONNULL(2);
/* This function return the bounding box of the string
* and are not multiplied by the aspect.
*/
void BLF_boundbox_ex(int fontid,
const char *str,
- size_t len,
+ size_t str_len,
struct rctf *box,
struct ResultBLF *r_info) ATTR_NONNULL(2);
-void BLF_boundbox(int fontid, const char *str, size_t len, struct rctf *box) ATTR_NONNULL();
+void BLF_boundbox(int fontid, const char *str, size_t str_len, struct rctf *box) ATTR_NONNULL();
/* The next both function return the width and height
* of the string, using the current font and both value
* are multiplied by the aspect of the font.
*/
-float BLF_width_ex(int fontid, const char *str, size_t len, struct ResultBLF *r_info)
+float BLF_width_ex(int fontid, const char *str, size_t str_len, struct ResultBLF *r_info)
ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(2);
-float BLF_width(int fontid, const char *str, size_t len) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
-float BLF_height_ex(int fontid, const char *str, size_t len, struct ResultBLF *r_info)
+float BLF_width(int fontid, const char *str, size_t str_len) ATTR_WARN_UNUSED_RESULT
+ ATTR_NONNULL();
+float BLF_height_ex(int fontid, const char *str, size_t str_len, struct ResultBLF *r_info)
ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(2);
-float BLF_height(int fontid, const char *str, size_t len) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
+float BLF_height(int fontid, const char *str, size_t str_len) ATTR_WARN_UNUSED_RESULT
+ ATTR_NONNULL();
/* Return dimensions of the font without any sample text. */
int BLF_height_max(int fontid) ATTR_WARN_UNUSED_RESULT;
@@ -163,8 +165,8 @@ float BLF_ascender(int fontid) ATTR_WARN_UNUSED_RESULT;
/* The following function return the width and height of the string, but
* just in one call, so avoid extra freetype2 stuff.
*/
-void BLF_width_and_height(int fontid, const char *str, size_t len, float *r_width, float *r_height)
- ATTR_NONNULL();
+void BLF_width_and_height(
+ int fontid, const char *str, size_t str_len, float *r_width, float *r_height) ATTR_NONNULL();
/* For fixed width fonts only, returns the width of a
* character.
@@ -221,9 +223,9 @@ void BLF_buffer_col(int fontid, const float rgba[4]) ATTR_NONNULL(2);
/* Draw the string into the buffer, this function draw in both buffer,
* float and unsigned char _BUT_ it's not necessary set both buffer, NULL is valid here.
*/
-void BLF_draw_buffer_ex(int fontid, const char *str, size_t len, struct ResultBLF *r_info)
+void BLF_draw_buffer_ex(int fontid, const char *str, size_t str_len, struct ResultBLF *r_info)
ATTR_NONNULL(2);
-void BLF_draw_buffer(int fontid, const char *str, size_t len) ATTR_NONNULL(2);
+void BLF_draw_buffer(int fontid, const char *str, size_t str_len) ATTR_NONNULL(2);
/* Add a path to the font dir paths. */
void BLF_dir_add(const char *path) ATTR_NONNULL();
@@ -254,8 +256,9 @@ void BLF_default_dpi(int dpi);
void BLF_default_set(int fontid);
int BLF_default(void); /* get default font ID so we can pass it to other functions */
/* Draw the string using the default font, size and dpi. */
-void BLF_draw_default(float x, float y, float z, const char *str, size_t len) ATTR_NONNULL();
-void BLF_draw_default_ascii(float x, float y, float z, const char *str, size_t len) ATTR_NONNULL();
+void BLF_draw_default(float x, float y, float z, const char *str, size_t str_len) ATTR_NONNULL();
+void BLF_draw_default_ascii(float x, float y, float z, const char *str, size_t str_len)
+ ATTR_NONNULL();
/* Set size and DPI, and return default font ID. */
int BLF_set_default(void);
diff --git a/source/blender/blenfont/intern/blf.c b/source/blender/blenfont/intern/blf.c
index 9168e7aa19c..86d67c80fd4 100644
--- a/source/blender/blenfont/intern/blf.c
+++ b/source/blender/blenfont/intern/blf.c
@@ -108,7 +108,6 @@ void BLF_cache_clear(void)
FontBLF *font = global_font[i];
if (font) {
blf_glyph_cache_clear(font);
- blf_kerning_cache_clear(font);
}
}
}
@@ -522,7 +521,7 @@ static void blf_draw_gl__end(FontBLF *font)
}
}
-void BLF_draw_ex(int fontid, const char *str, size_t len, struct ResultBLF *r_info)
+void BLF_draw_ex(int fontid, const char *str, const size_t str_len, struct ResultBLF *r_info)
{
FontBLF *font = blf_get(fontid);
@@ -531,27 +530,27 @@ void BLF_draw_ex(int fontid, const char *str, size_t len, struct ResultBLF *r_in
if (font) {
blf_draw_gl__start(font);
if (font->flags & BLF_WORD_WRAP) {
- blf_font_draw__wrap(font, str, len, r_info);
+ blf_font_draw__wrap(font, str, str_len, r_info);
}
else {
- blf_font_draw(font, str, len, r_info);
+ blf_font_draw(font, str, str_len, r_info);
}
blf_draw_gl__end(font);
}
}
-void BLF_draw(int fontid, const char *str, size_t len)
+void BLF_draw(int fontid, const char *str, const size_t str_len)
{
- if (len == 0 || str[0] == '\0') {
+ if (str_len == 0 || str[0] == '\0') {
return;
}
/* Avoid bgl usage to corrupt BLF drawing. */
GPU_bgl_end();
- BLF_draw_ex(fontid, str, len, NULL);
+ BLF_draw_ex(fontid, str, str_len, NULL);
}
-void BLF_draw_ascii_ex(int fontid, const char *str, size_t len, struct ResultBLF *r_info)
+void BLF_draw_ascii_ex(int fontid, const char *str, const size_t str_len, struct ResultBLF *r_info)
{
FontBLF *font = blf_get(fontid);
@@ -561,27 +560,27 @@ void BLF_draw_ascii_ex(int fontid, const char *str, size_t len, struct ResultBLF
blf_draw_gl__start(font);
if (font->flags & BLF_WORD_WRAP) {
/* Use non-ASCII draw function for word-wrap. */
- blf_font_draw__wrap(font, str, len, r_info);
+ blf_font_draw__wrap(font, str, str_len, r_info);
}
else {
- blf_font_draw_ascii(font, str, len, r_info);
+ blf_font_draw_ascii(font, str, str_len, r_info);
}
blf_draw_gl__end(font);
}
}
-void BLF_draw_ascii(int fontid, const char *str, size_t len)
+void BLF_draw_ascii(int fontid, const char *str, const size_t str_len)
{
- if (len == 0 || str[0] == '\0') {
+ if (str_len == 0 || str[0] == '\0') {
return;
}
- BLF_draw_ascii_ex(fontid, str, len, NULL);
+ BLF_draw_ascii_ex(fontid, str, str_len, NULL);
}
-int BLF_draw_mono(int fontid, const char *str, size_t len, int cwidth)
+int BLF_draw_mono(int fontid, const char *str, const size_t str_len, int cwidth)
{
- if (len == 0 || str[0] == '\0') {
+ if (str_len == 0 || str[0] == '\0') {
return 0;
}
@@ -590,7 +589,7 @@ int BLF_draw_mono(int fontid, const char *str, size_t len, int cwidth)
if (font) {
blf_draw_gl__start(font);
- columns = blf_font_draw_mono(font, str, len, cwidth);
+ columns = blf_font_draw_mono(font, str, str_len, cwidth);
blf_draw_gl__end(font);
}
@@ -607,7 +606,7 @@ int BLF_draw_mono(int fontid, const char *str, size_t len, int cwidth)
*/
void BLF_boundbox_foreach_glyph_ex(int fontid,
const char *str,
- size_t len,
+ size_t str_len,
BLF_GlyphBoundsFn user_fn,
void *user_data,
struct ResultBLF *r_info)
@@ -622,25 +621,26 @@ void BLF_boundbox_foreach_glyph_ex(int fontid,
BLI_assert(0);
}
else {
- blf_font_boundbox_foreach_glyph(font, str, len, user_fn, user_data, r_info);
+ blf_font_boundbox_foreach_glyph(font, str, str_len, user_fn, user_data, r_info);
}
}
}
void BLF_boundbox_foreach_glyph(
- int fontid, const char *str, size_t len, BLF_GlyphBoundsFn user_fn, void *user_data)
+ int fontid, const char *str, const size_t str_len, BLF_GlyphBoundsFn user_fn, void *user_data)
{
- BLF_boundbox_foreach_glyph_ex(fontid, str, len, user_fn, user_data, NULL);
+ BLF_boundbox_foreach_glyph_ex(fontid, str, str_len, user_fn, user_data, NULL);
}
-size_t BLF_width_to_strlen(int fontid, const char *str, size_t len, float width, float *r_width)
+size_t BLF_width_to_strlen(
+ int fontid, const char *str, const size_t str_len, float width, float *r_width)
{
FontBLF *font = blf_get(fontid);
if (font) {
const float xa = (font->flags & BLF_ASPECT) ? font->aspect[0] : 1.0f;
size_t ret;
- ret = blf_font_width_to_strlen(font, str, len, width / xa, r_width);
+ ret = blf_font_width_to_strlen(font, str, str_len, width / xa, r_width);
if (r_width) {
*r_width *= xa;
}
@@ -653,14 +653,15 @@ size_t BLF_width_to_strlen(int fontid, const char *str, size_t len, float width,
return 0;
}
-size_t BLF_width_to_rstrlen(int fontid, const char *str, size_t len, float width, float *r_width)
+size_t BLF_width_to_rstrlen(
+ int fontid, const char *str, const size_t str_len, float width, float *r_width)
{
FontBLF *font = blf_get(fontid);
if (font) {
const float xa = (font->flags & BLF_ASPECT) ? font->aspect[0] : 1.0f;
size_t ret;
- ret = blf_font_width_to_rstrlen(font, str, len, width / xa, r_width);
+ ret = blf_font_width_to_rstrlen(font, str, str_len, width / xa, r_width);
if (r_width) {
*r_width *= xa;
}
@@ -674,7 +675,7 @@ size_t BLF_width_to_rstrlen(int fontid, const char *str, size_t len, float width
}
void BLF_boundbox_ex(
- int fontid, const char *str, size_t len, rctf *r_box, struct ResultBLF *r_info)
+ int fontid, const char *str, const size_t str_len, rctf *r_box, struct ResultBLF *r_info)
{
FontBLF *font = blf_get(fontid);
@@ -682,47 +683,48 @@ void BLF_boundbox_ex(
if (font) {
if (font->flags & BLF_WORD_WRAP) {
- blf_font_boundbox__wrap(font, str, len, r_box, r_info);
+ blf_font_boundbox__wrap(font, str, str_len, r_box, r_info);
}
else {
- blf_font_boundbox(font, str, len, r_box, r_info);
+ blf_font_boundbox(font, str, str_len, r_box, r_info);
}
}
}
-void BLF_boundbox(int fontid, const char *str, size_t len, rctf *r_box)
+void BLF_boundbox(int fontid, const char *str, const size_t str_len, rctf *r_box)
{
- BLF_boundbox_ex(fontid, str, len, r_box, NULL);
+ BLF_boundbox_ex(fontid, str, str_len, r_box, NULL);
}
-void BLF_width_and_height(int fontid, const char *str, size_t len, float *r_width, float *r_height)
+void BLF_width_and_height(
+ int fontid, const char *str, const size_t str_len, float *r_width, float *r_height)
{
FontBLF *font = blf_get(fontid);
if (font) {
- blf_font_width_and_height(font, str, len, r_width, r_height, NULL);
+ blf_font_width_and_height(font, str, str_len, r_width, r_height, NULL);
}
else {
*r_width = *r_height = 0.0f;
}
}
-float BLF_width_ex(int fontid, const char *str, size_t len, struct ResultBLF *r_info)
+float BLF_width_ex(int fontid, const char *str, const size_t str_len, struct ResultBLF *r_info)
{
FontBLF *font = blf_get(fontid);
BLF_RESULT_CHECK_INIT(r_info);
if (font) {
- return blf_font_width(font, str, len, r_info);
+ return blf_font_width(font, str, str_len, r_info);
}
return 0.0f;
}
-float BLF_width(int fontid, const char *str, size_t len)
+float BLF_width(int fontid, const char *str, const size_t str_len)
{
- return BLF_width_ex(fontid, str, len, NULL);
+ return BLF_width_ex(fontid, str, str_len, NULL);
}
float BLF_fixed_width(int fontid)
@@ -736,22 +738,22 @@ float BLF_fixed_width(int fontid)
return 0.0f;
}
-float BLF_height_ex(int fontid, const char *str, size_t len, struct ResultBLF *r_info)
+float BLF_height_ex(int fontid, const char *str, const size_t str_len, struct ResultBLF *r_info)
{
FontBLF *font = blf_get(fontid);
BLF_RESULT_CHECK_INIT(r_info);
if (font) {
- return blf_font_height(font, str, len, r_info);
+ return blf_font_height(font, str, str_len, r_info);
}
return 0.0f;
}
-float BLF_height(int fontid, const char *str, size_t len)
+float BLF_height(int fontid, const char *str, const size_t str_len)
{
- return BLF_height_ex(fontid, str, len, NULL);
+ return BLF_height_ex(fontid, str, str_len, NULL);
}
int BLF_height_max(int fontid)
@@ -895,24 +897,27 @@ void blf_draw_buffer__end(void)
{
}
-void BLF_draw_buffer_ex(int fontid, const char *str, size_t len, struct ResultBLF *r_info)
+void BLF_draw_buffer_ex(int fontid,
+ const char *str,
+ const size_t str_len,
+ struct ResultBLF *r_info)
{
FontBLF *font = blf_get(fontid);
if (font && (font->buf_info.fbuf || font->buf_info.cbuf)) {
blf_draw_buffer__start(font);
if (font->flags & BLF_WORD_WRAP) {
- blf_font_draw_buffer__wrap(font, str, len, r_info);
+ blf_font_draw_buffer__wrap(font, str, str_len, r_info);
}
else {
- blf_font_draw_buffer(font, str, len, r_info);
+ blf_font_draw_buffer(font, str, str_len, r_info);
}
blf_draw_buffer__end();
}
}
-void BLF_draw_buffer(int fontid, const char *str, size_t len)
+void BLF_draw_buffer(int fontid, const char *str, const size_t str_len)
{
- BLF_draw_buffer_ex(fontid, str, len, NULL);
+ BLF_draw_buffer_ex(fontid, str, str_len, NULL);
}
char *BLF_display_name_from_file(const char *filename)
diff --git a/source/blender/blenfont/intern/blf_default.c b/source/blender/blenfont/intern/blf_default.c
index 7bbc865128d..1b458e8aaef 100644
--- a/source/blender/blenfont/intern/blf_default.c
+++ b/source/blender/blenfont/intern/blf_default.c
@@ -68,23 +68,23 @@ int BLF_set_default(void)
return global_font_default;
}
-void BLF_draw_default(float x, float y, float z, const char *str, size_t len)
+void BLF_draw_default(float x, float y, float z, const char *str, const size_t str_len)
{
ASSERT_DEFAULT_SET;
const uiStyle *style = UI_style_get();
BLF_size(global_font_default, style->widgetlabel.points, global_font_dpi);
BLF_position(global_font_default, x, y, z);
- BLF_draw(global_font_default, str, len);
+ BLF_draw(global_font_default, str, str_len);
}
/* same as above but call 'BLF_draw_ascii' */
-void BLF_draw_default_ascii(float x, float y, float z, const char *str, size_t len)
+void BLF_draw_default_ascii(float x, float y, float z, const char *str, const size_t str_len)
{
ASSERT_DEFAULT_SET;
const uiStyle *style = UI_style_get();
BLF_size(global_font_default, style->widgetlabel.points, global_font_dpi);
BLF_position(global_font_default, x, y, z);
- BLF_draw_ascii(global_font_default, str, len); /* XXX, use real length */
+ BLF_draw_ascii(global_font_default, str, str_len); /* XXX, use real length */
}
diff --git a/source/blender/blenfont/intern/blf_font.c b/source/blender/blenfont/intern/blf_font.c
index 00d3cfb09eb..af68e1563d1 100644
--- a/source/blender/blenfont/intern/blf_font.c
+++ b/source/blender/blenfont/intern/blf_font.c
@@ -72,6 +72,38 @@ static SpinLock ft_lib_mutex;
static SpinLock blf_glyph_cache_mutex;
/* -------------------------------------------------------------------- */
+/** \name FreeType Utilities (Internal)
+ * \{ */
+
+/**
+ * Convert a FreeType 26.6 value representing an unscaled design size to pixels.
+ * This is an exact copy of the scaling done inside FT_Get_Kerning when called
+ * with #FT_KERNING_DEFAULT, including arbitrary resizing for small fonts.
+ */
+static int blf_unscaled_F26Dot6_to_pixels(FontBLF *font, FT_Pos value)
+{
+ /* Scale value by font size using integer-optimized multiplication. */
+ FT_Long scaled = FT_MulFix(value, font->face->size->metrics.x_scale);
+
+ /* FreeType states that this '25' has been determined heuristically. */
+ if (font->face->size->metrics.x_ppem < 25) {
+ scaled = FT_MulDiv(scaled, font->face->size->metrics.x_ppem, 25);
+ }
+
+ /* Copies of internal FreeType macros needed here. */
+#define FT_PIX_FLOOR(x) ((x) & ~63)
+#define FT_PIX_ROUND(x) FT_PIX_FLOOR((x) + 32)
+
+ /* Round to even 64ths, then divide by 64. */
+ return (int)FT_PIX_ROUND(scaled) >> 6;
+
+#undef FT_PIX_FLOOR
+#undef FT_PIX_ROUND
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
/** \name Glyph Batching
* \{ */
@@ -257,67 +289,8 @@ static void blf_batch_draw_end(void)
/** \} */
/* -------------------------------------------------------------------- */
-
-int blf_font_init(void)
-{
- memset(&g_batch, 0, sizeof(g_batch));
- BLI_spin_init(&ft_lib_mutex);
- BLI_spin_init(&blf_glyph_cache_mutex);
- return FT_Init_FreeType(&ft_lib);
-}
-
-void blf_font_exit(void)
-{
- FT_Done_FreeType(ft_lib);
- BLI_spin_end(&ft_lib_mutex);
- BLI_spin_end(&blf_glyph_cache_mutex);
- blf_batch_draw_exit();
-}
-
-void blf_font_size(FontBLF *font, unsigned int size, unsigned int dpi)
-{
- GlyphCacheBLF *gc;
- FT_Error err;
-
- blf_glyph_cache_acquire(font);
-
- gc = blf_glyph_cache_find(font, size, dpi);
- if (gc) {
- /* Optimization: do not call FT_Set_Char_Size if size did not change. */
- if (font->size == size && font->dpi == dpi) {
- blf_glyph_cache_release(font);
- return;
- }
- }
-
- err = FT_Set_Char_Size(font->face, 0, ((FT_F26Dot6)(size)) * 64, dpi, dpi);
- if (err) {
- /* FIXME: here we can go through the fixed size and choice a close one */
- printf("The current font don't support the size, %u and dpi, %u\n", size, dpi);
-
- blf_glyph_cache_release(font);
- return;
- }
-
- font->size = size;
- font->dpi = dpi;
-
- if (!gc) {
- blf_glyph_cache_new(font);
- }
- blf_glyph_cache_release(font);
-}
-
-static void blf_font_ensure_ascii_kerning(FontBLF *font, GlyphCacheBLF *gc)
-{
- if (font->kerning_cache || !FT_HAS_KERNING(font->face)) {
- return;
- }
- font->kerning_cache = blf_kerning_cache_find(font);
- if (!font->kerning_cache) {
- font->kerning_cache = blf_kerning_cache_new(font, gc);
- }
-}
+/** \name Glyph Stepping Utilities (Internal)
+ * \{ */
/* Fast path for runs of ASCII characters. Given that common UTF-8
* input will consist of an overwhelming majority of ASCII
@@ -355,26 +328,44 @@ BLI_INLINE void blf_kerning_step_fast(FontBLF *font,
const uint c,
int *pen_x_p)
{
- /* `blf_font_ensure_ascii_kerning(font, gc);` must be called before this function. */
- BLI_assert(font->kerning_cache != NULL || !FT_HAS_KERNING(font->face));
+ if (!FT_HAS_KERNING(font->face) || g_prev == NULL) {
+ return;
+ }
- if (g_prev != NULL && FT_HAS_KERNING(font->face)) {
- if ((c_prev < KERNING_CACHE_TABLE_SIZE) && (c < GLYPH_ASCII_TABLE_SIZE)) {
- *pen_x_p += font->kerning_cache->ascii_table[c][c_prev];
- }
- else {
- FT_Vector delta;
- if (FT_Get_Kerning(font->face, g_prev->idx, g->idx, FT_KERNING_DEFAULT, &delta) == 0) {
- *pen_x_p += (int)delta.x >> 6;
- }
- }
+ FT_Vector delta = {KERNING_ENTRY_UNSET};
+
+ /* Get unscaled kerning value from our cache if ASCII. */
+ if ((c_prev < KERNING_CACHE_TABLE_SIZE) && (c < GLYPH_ASCII_TABLE_SIZE)) {
+ delta.x = font->kerning_cache->ascii_table[c][c_prev];
+ }
+
+ /* If not ASCII or not found in cache, ask FreeType for kerning. */
+ if (UNLIKELY(delta.x == KERNING_ENTRY_UNSET)) {
+ /* Note that this function sets delta values to zero on any error. */
+ FT_Get_Kerning(font->face, g_prev->idx, g->idx, FT_KERNING_UNSCALED, &delta);
+ }
+
+ /* If ASCII we save this value to our cache for quicker access next time. */
+ if ((c_prev < KERNING_CACHE_TABLE_SIZE) && (c < GLYPH_ASCII_TABLE_SIZE)) {
+ font->kerning_cache->ascii_table[c][c_prev] = (int)delta.x;
+ }
+
+ if (delta.x != 0) {
+ /* Convert unscaled design units to pixels and move pen. */
+ *pen_x_p += blf_unscaled_F26Dot6_to_pixels(font, delta.x);
}
}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Text Drawing: GPU
+ * \{ */
+
static void blf_font_draw_ex(FontBLF *font,
GlyphCacheBLF *gc,
const char *str,
- size_t len,
+ const size_t str_len,
struct ResultBLF *r_info,
int pen_y)
{
@@ -383,16 +374,14 @@ static void blf_font_draw_ex(FontBLF *font,
int pen_x = 0;
size_t i = 0;
- if (len == 0) {
+ if (str_len == 0) {
/* early output, don't do any IMM OpenGL. */
return;
}
- blf_font_ensure_ascii_kerning(font, gc);
-
blf_batch_draw_begin(font);
- while ((i < len) && str[i]) {
+ while ((i < str_len) && str[i]) {
g = blf_utf8_next_fast(font, gc, str, &i, &c);
if (UNLIKELY(c == BLI_UTF8_ERR)) {
@@ -418,16 +407,16 @@ static void blf_font_draw_ex(FontBLF *font,
r_info->width = pen_x;
}
}
-void blf_font_draw(FontBLF *font, const char *str, size_t len, struct ResultBLF *r_info)
+void blf_font_draw(FontBLF *font, const char *str, const size_t str_len, struct ResultBLF *r_info)
{
GlyphCacheBLF *gc = blf_glyph_cache_acquire(font);
- blf_font_draw_ex(font, gc, str, len, r_info, 0);
+ blf_font_draw_ex(font, gc, str, str_len, r_info, 0);
blf_glyph_cache_release(font);
}
/* faster version of blf_font_draw, ascii only for view dimensions */
static void blf_font_draw_ascii_ex(
- FontBLF *font, const char *str, size_t len, struct ResultBLF *r_info, int pen_y)
+ FontBLF *font, const char *str, size_t str_len, struct ResultBLF *r_info, int pen_y)
{
unsigned int c, c_prev = BLI_UTF8_ERR;
GlyphBLF *g, *g_prev = NULL;
@@ -435,11 +424,9 @@ static void blf_font_draw_ascii_ex(
GlyphCacheBLF *gc = blf_glyph_cache_acquire(font);
- blf_font_ensure_ascii_kerning(font, gc);
-
blf_batch_draw_begin(font);
- while ((c = *(str++)) && len--) {
+ while ((c = *(str++)) && str_len--) {
BLI_assert(c < GLYPH_ASCII_TABLE_SIZE);
g = gc->glyph_ascii_table[c];
if (UNLIKELY(g == NULL)) {
@@ -469,13 +456,16 @@ static void blf_font_draw_ascii_ex(
blf_glyph_cache_release(font);
}
-void blf_font_draw_ascii(FontBLF *font, const char *str, size_t len, struct ResultBLF *r_info)
+void blf_font_draw_ascii(FontBLF *font,
+ const char *str,
+ const size_t str_len,
+ struct ResultBLF *r_info)
{
- blf_font_draw_ascii_ex(font, str, len, r_info, 0);
+ blf_font_draw_ascii_ex(font, str, str_len, r_info, 0);
}
/* use fixed column width, but an utf8 character may occupy multiple columns */
-int blf_font_draw_mono(FontBLF *font, const char *str, size_t len, int cwidth)
+int blf_font_draw_mono(FontBLF *font, const char *str, const size_t str_len, int cwidth)
{
unsigned int c;
GlyphBLF *g;
@@ -487,7 +477,7 @@ int blf_font_draw_mono(FontBLF *font, const char *str, size_t len, int cwidth)
blf_batch_draw_begin(font);
- while ((i < len) && str[i]) {
+ while ((i < str_len) && str[i]) {
g = blf_utf8_next_fast(font, gc, str, &i, &c);
if (UNLIKELY(c == BLI_UTF8_ERR)) {
@@ -515,11 +505,17 @@ int blf_font_draw_mono(FontBLF *font, const char *str, size_t len, int cwidth)
return columns;
}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Text Drawgin: Buffer
+ * \{ */
+
/* Sanity checks are done by BLF_draw_buffer() */
static void blf_font_draw_buffer_ex(FontBLF *font,
GlyphCacheBLF *gc,
const char *str,
- size_t len,
+ const size_t str_len,
struct ResultBLF *r_info,
int pen_y)
{
@@ -536,11 +532,9 @@ static void blf_font_draw_buffer_ex(FontBLF *font,
int chx, chy;
int y, x;
- blf_font_ensure_ascii_kerning(font, gc);
-
/* another buffer specific call for color conversion */
- while ((i < len) && str[i]) {
+ while ((i < str_len) && str[i]) {
g = blf_utf8_next_fast(font, gc, str, &i, &c);
if (UNLIKELY(c == BLI_UTF8_ERR)) {
@@ -655,13 +649,26 @@ static void blf_font_draw_buffer_ex(FontBLF *font,
}
}
-void blf_font_draw_buffer(FontBLF *font, const char *str, size_t len, struct ResultBLF *r_info)
+void blf_font_draw_buffer(FontBLF *font,
+ const char *str,
+ const size_t str_len,
+ struct ResultBLF *r_info)
{
GlyphCacheBLF *gc = blf_glyph_cache_acquire(font);
- blf_font_draw_buffer_ex(font, gc, str, len, r_info, 0);
+ blf_font_draw_buffer_ex(font, gc, str, str_len, r_info, 0);
blf_glyph_cache_release(font);
}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Text Evaluation: Width to Sting Length
+ *
+ * Use to implement exported functions:
+ * - #BLF_width_to_strlen
+ * - #BLF_width_to_rstrlen
+ * \{ */
+
static bool blf_font_width_to_strlen_glyph_process(FontBLF *font,
const uint c_prev,
const uint c,
@@ -684,7 +691,7 @@ static bool blf_font_width_to_strlen_glyph_process(FontBLF *font,
}
size_t blf_font_width_to_strlen(
- FontBLF *font, const char *str, size_t len, float width, float *r_width)
+ FontBLF *font, const char *str, const size_t str_len, float width, float *r_width)
{
unsigned int c, c_prev = BLI_UTF8_ERR;
GlyphBLF *g, *g_prev;
@@ -694,9 +701,7 @@ size_t blf_font_width_to_strlen(
GlyphCacheBLF *gc = blf_glyph_cache_acquire(font);
const int width_i = (int)width;
- blf_font_ensure_ascii_kerning(font, gc);
-
- for (i_prev = i = 0, width_new = pen_x = 0, g_prev = NULL, c_prev = 0; (i < len) && str[i];
+ for (i_prev = i = 0, width_new = pen_x = 0, g_prev = NULL, c_prev = 0; (i < str_len) && str[i];
i_prev = i, width_new = pen_x, c_prev = c, g_prev = g) {
g = blf_utf8_next_fast(font, gc, str, &i, &c);
@@ -714,7 +719,7 @@ size_t blf_font_width_to_strlen(
}
size_t blf_font_width_to_rstrlen(
- FontBLF *font, const char *str, size_t len, float width, float *r_width)
+ FontBLF *font, const char *str, const size_t str_len, float width, float *r_width)
{
unsigned int c, c_prev = BLI_UTF8_ERR;
GlyphBLF *g, *g_prev;
@@ -725,9 +730,7 @@ size_t blf_font_width_to_rstrlen(
GlyphCacheBLF *gc = blf_glyph_cache_acquire(font);
const int width_i = (int)width;
- blf_font_ensure_ascii_kerning(font, gc);
-
- i = BLI_strnlen(str, len);
+ i = BLI_strnlen(str, str_len);
s = BLI_str_find_prev_char_utf8(str, &str[i]);
i = (size_t)((s != NULL) ? s - str : 0);
s_prev = BLI_str_find_prev_char_utf8(str, s);
@@ -759,10 +762,16 @@ size_t blf_font_width_to_rstrlen(
return i;
}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Text Evaluation: Glyph Bound Box with Callback
+ * \{ */
+
static void blf_font_boundbox_ex(FontBLF *font,
GlyphCacheBLF *gc,
const char *str,
- size_t len,
+ const size_t str_len,
rctf *box,
struct ResultBLF *r_info,
int pen_y)
@@ -778,9 +787,7 @@ static void blf_font_boundbox_ex(FontBLF *font,
box->ymin = 32000.0f;
box->ymax = -32000.0f;
- blf_font_ensure_ascii_kerning(font, gc);
-
- while ((i < len) && str[i]) {
+ while ((i < str_len) && str[i]) {
g = blf_utf8_next_fast(font, gc, str, &i, &c);
if (UNLIKELY(c == BLI_UTF8_ERR)) {
@@ -828,15 +835,178 @@ static void blf_font_boundbox_ex(FontBLF *font,
}
}
void blf_font_boundbox(
- FontBLF *font, const char *str, size_t len, rctf *r_box, struct ResultBLF *r_info)
+ FontBLF *font, const char *str, const size_t str_len, rctf *r_box, struct ResultBLF *r_info)
+{
+ GlyphCacheBLF *gc = blf_glyph_cache_acquire(font);
+ blf_font_boundbox_ex(font, gc, str, str_len, r_box, r_info, 0);
+ blf_glyph_cache_release(font);
+}
+
+void blf_font_width_and_height(FontBLF *font,
+ const char *str,
+ const size_t str_len,
+ float *r_width,
+ float *r_height,
+ struct ResultBLF *r_info)
+{
+ float xa, ya;
+ rctf box;
+
+ if (font->flags & BLF_ASPECT) {
+ xa = font->aspect[0];
+ ya = font->aspect[1];
+ }
+ else {
+ xa = 1.0f;
+ ya = 1.0f;
+ }
+
+ if (font->flags & BLF_WORD_WRAP) {
+ blf_font_boundbox__wrap(font, str, str_len, &box, r_info);
+ }
+ else {
+ blf_font_boundbox(font, str, str_len, &box, r_info);
+ }
+ *r_width = (BLI_rctf_size_x(&box) * xa);
+ *r_height = (BLI_rctf_size_y(&box) * ya);
+}
+
+float blf_font_width(FontBLF *font,
+ const char *str,
+ const size_t str_len,
+ struct ResultBLF *r_info)
+{
+ float xa;
+ rctf box;
+
+ if (font->flags & BLF_ASPECT) {
+ xa = font->aspect[0];
+ }
+ else {
+ xa = 1.0f;
+ }
+
+ if (font->flags & BLF_WORD_WRAP) {
+ blf_font_boundbox__wrap(font, str, str_len, &box, r_info);
+ }
+ else {
+ blf_font_boundbox(font, str, str_len, &box, r_info);
+ }
+ return BLI_rctf_size_x(&box) * xa;
+}
+
+float blf_font_height(FontBLF *font,
+ const char *str,
+ const size_t str_len,
+ struct ResultBLF *r_info)
+{
+ float ya;
+ rctf box;
+
+ if (font->flags & BLF_ASPECT) {
+ ya = font->aspect[1];
+ }
+ else {
+ ya = 1.0f;
+ }
+
+ if (font->flags & BLF_WORD_WRAP) {
+ blf_font_boundbox__wrap(font, str, str_len, &box, r_info);
+ }
+ else {
+ blf_font_boundbox(font, str, str_len, &box, r_info);
+ }
+ return BLI_rctf_size_y(&box) * ya;
+}
+
+float blf_font_fixed_width(FontBLF *font)
{
+ const unsigned int c = ' ';
+
GlyphCacheBLF *gc = blf_glyph_cache_acquire(font);
- blf_font_boundbox_ex(font, gc, str, len, r_box, r_info, 0);
+ GlyphBLF *g = blf_glyph_search(gc, c);
+ if (!g) {
+ g = blf_glyph_add(font, gc, FT_Get_Char_Index(font->face, c), c);
+
+ /* if we don't find the glyph. */
+ if (!g) {
+ blf_glyph_cache_release(font);
+ return 0.0f;
+ }
+ }
+
blf_glyph_cache_release(font);
+ return g->advance;
}
+static void blf_font_boundbox_foreach_glyph_ex(FontBLF *font,
+ GlyphCacheBLF *gc,
+ const char *str,
+ const size_t str_len,
+ BLF_GlyphBoundsFn user_fn,
+ void *user_data,
+ struct ResultBLF *r_info,
+ int pen_y)
+{
+ unsigned int c, c_prev = BLI_UTF8_ERR;
+ GlyphBLF *g, *g_prev = NULL;
+ int pen_x = 0;
+ size_t i = 0, i_curr;
+ rcti gbox;
+
+ if (str_len == 0) {
+ /* early output. */
+ return;
+ }
+
+ while ((i < str_len) && str[i]) {
+ i_curr = i;
+ g = blf_utf8_next_fast(font, gc, str, &i, &c);
+
+ if (UNLIKELY(c == BLI_UTF8_ERR)) {
+ break;
+ }
+ if (UNLIKELY(g == NULL)) {
+ continue;
+ }
+ blf_kerning_step_fast(font, g_prev, g, c_prev, c, &pen_x);
+
+ gbox.xmin = pen_x;
+ gbox.xmax = gbox.xmin + MIN2(g->advance_i, g->dims[0]);
+ gbox.ymin = pen_y;
+ gbox.ymax = gbox.ymin - g->dims[1];
+
+ pen_x += g->advance_i;
+
+ if (user_fn(str, i_curr, &gbox, g->advance_i, &g->box, g->pos, user_data) == false) {
+ break;
+ }
+
+ g_prev = g;
+ c_prev = c;
+ }
+
+ if (r_info) {
+ r_info->lines = 1;
+ r_info->width = pen_x;
+ }
+}
+void blf_font_boundbox_foreach_glyph(FontBLF *font,
+ const char *str,
+ const size_t str_len,
+ BLF_GlyphBoundsFn user_fn,
+ void *user_data,
+ struct ResultBLF *r_info)
+{
+ GlyphCacheBLF *gc = blf_glyph_cache_acquire(font);
+ blf_font_boundbox_foreach_glyph_ex(font, gc, str, str_len, user_fn, user_data, r_info, 0);
+ blf_glyph_cache_release(font);
+}
+
+/** \} */
+
/* -------------------------------------------------------------------- */
-/** \name Word-Wrap Support
+/** \name Text Evaluation: Word-Wrap with Callback
* \{ */
/**
@@ -850,12 +1020,12 @@ void blf_font_boundbox(
*/
static void blf_font_wrap_apply(FontBLF *font,
const char *str,
- size_t len,
+ const size_t str_len,
struct ResultBLF *r_info,
void (*callback)(FontBLF *font,
GlyphCacheBLF *gc,
const char *str,
- size_t len,
+ const size_t str_len,
int pen_y,
void *userdata),
void *userdata)
@@ -869,15 +1039,13 @@ static void blf_font_wrap_apply(FontBLF *font,
GlyphCacheBLF *gc = blf_glyph_cache_acquire(font);
- blf_font_ensure_ascii_kerning(font, gc);
-
struct WordWrapVars {
int wrap_width;
size_t start, last[2];
} wrap = {font->wrap_width != -1 ? font->wrap_width : INT_MAX, 0, {0, 0}};
- // printf("%s wrapping (%d, %d) `%s`:\n", __func__, len, strlen(str), str);
- while ((i < len) && str[i]) {
+ // printf("%s wrapping (%d, %d) `%s`:\n", __func__, str_len, strlen(str), str);
+ while ((i < str_len) && str[i]) {
/* wrap vars */
size_t i_curr = i;
@@ -905,7 +1073,7 @@ static void blf_font_wrap_apply(FontBLF *font,
if (UNLIKELY((pen_x_next >= wrap.wrap_width) && (wrap.start != wrap.last[0]))) {
do_draw = true;
}
- else if (UNLIKELY(((i < len) && str[i]) == 0)) {
+ else if (UNLIKELY(((i < str_len) && str[i]) == 0)) {
/* need check here for trailing newline, else we draw it */
wrap.last[0] = i + ((g->c != '\n') ? 1 : 0);
wrap.last[1] = i;
@@ -956,268 +1124,179 @@ static void blf_font_wrap_apply(FontBLF *font,
static void blf_font_draw__wrap_cb(FontBLF *font,
GlyphCacheBLF *gc,
const char *str,
- size_t len,
+ const size_t str_len,
int pen_y,
void *UNUSED(userdata))
{
- blf_font_draw_ex(font, gc, str, len, NULL, pen_y);
+ blf_font_draw_ex(font, gc, str, str_len, NULL, pen_y);
}
-void blf_font_draw__wrap(FontBLF *font, const char *str, size_t len, struct ResultBLF *r_info)
+void blf_font_draw__wrap(FontBLF *font,
+ const char *str,
+ const size_t str_len,
+ struct ResultBLF *r_info)
{
- blf_font_wrap_apply(font, str, len, r_info, blf_font_draw__wrap_cb, NULL);
+ blf_font_wrap_apply(font, str, str_len, r_info, blf_font_draw__wrap_cb, NULL);
}
/* blf_font_boundbox__wrap */
-static void blf_font_boundbox_wrap_cb(
- FontBLF *font, GlyphCacheBLF *gc, const char *str, size_t len, int pen_y, void *userdata)
+static void blf_font_boundbox_wrap_cb(FontBLF *font,
+ GlyphCacheBLF *gc,
+ const char *str,
+ const size_t str_len,
+ int pen_y,
+ void *userdata)
{
rctf *box = userdata;
rctf box_single;
- blf_font_boundbox_ex(font, gc, str, len, &box_single, NULL, pen_y);
+ blf_font_boundbox_ex(font, gc, str, str_len, &box_single, NULL, pen_y);
BLI_rctf_union(box, &box_single);
}
void blf_font_boundbox__wrap(
- FontBLF *font, const char *str, size_t len, rctf *box, struct ResultBLF *r_info)
+ FontBLF *font, const char *str, const size_t str_len, rctf *box, struct ResultBLF *r_info)
{
box->xmin = 32000.0f;
box->xmax = -32000.0f;
box->ymin = 32000.0f;
box->ymax = -32000.0f;
- blf_font_wrap_apply(font, str, len, r_info, blf_font_boundbox_wrap_cb, box);
+ blf_font_wrap_apply(font, str, str_len, r_info, blf_font_boundbox_wrap_cb, box);
}
/* blf_font_draw_buffer__wrap */
static void blf_font_draw_buffer__wrap_cb(FontBLF *font,
GlyphCacheBLF *gc,
const char *str,
- size_t len,
+ const size_t str_len,
int pen_y,
void *UNUSED(userdata))
{
- blf_font_draw_buffer_ex(font, gc, str, len, NULL, pen_y);
+ blf_font_draw_buffer_ex(font, gc, str, str_len, NULL, pen_y);
}
void blf_font_draw_buffer__wrap(FontBLF *font,
const char *str,
- size_t len,
+ const size_t str_len,
struct ResultBLF *r_info)
{
- blf_font_wrap_apply(font, str, len, r_info, blf_font_draw_buffer__wrap_cb, NULL);
+ blf_font_wrap_apply(font, str, str_len, r_info, blf_font_draw_buffer__wrap_cb, NULL);
}
/** \} */
-void blf_font_width_and_height(FontBLF *font,
- const char *str,
- size_t len,
- float *r_width,
- float *r_height,
- struct ResultBLF *r_info)
+/* -------------------------------------------------------------------- */
+/** \name Text Evaluation: Count Missing Characters
+ * \{ */
+
+int blf_font_count_missing_chars(FontBLF *font,
+ const char *str,
+ const size_t str_len,
+ int *r_tot_chars)
{
- float xa, ya;
- rctf box;
+ int missing = 0;
+ size_t i = 0;
- if (font->flags & BLF_ASPECT) {
- xa = font->aspect[0];
- ya = font->aspect[1];
- }
- else {
- xa = 1.0f;
- ya = 1.0f;
- }
+ *r_tot_chars = 0;
+ while (i < str_len) {
+ unsigned int c;
- if (font->flags & BLF_WORD_WRAP) {
- blf_font_boundbox__wrap(font, str, len, &box, r_info);
- }
- else {
- blf_font_boundbox(font, str, len, &box, r_info);
+ if ((c = str[i]) < GLYPH_ASCII_TABLE_SIZE) {
+ i++;
+ }
+ else if ((c = BLI_str_utf8_as_unicode_step(str, &i)) != BLI_UTF8_ERR) {
+ if (FT_Get_Char_Index((font)->face, c) == 0) {
+ missing++;
+ }
+ }
+ (*r_tot_chars)++;
}
- *r_width = (BLI_rctf_size_x(&box) * xa);
- *r_height = (BLI_rctf_size_y(&box) * ya);
+ return missing;
}
-float blf_font_width(FontBLF *font, const char *str, size_t len, struct ResultBLF *r_info)
-{
- float xa;
- rctf box;
-
- if (font->flags & BLF_ASPECT) {
- xa = font->aspect[0];
- }
- else {
- xa = 1.0f;
- }
+/** \} */
- if (font->flags & BLF_WORD_WRAP) {
- blf_font_boundbox__wrap(font, str, len, &box, r_info);
- }
- else {
- blf_font_boundbox(font, str, len, &box, r_info);
- }
- return BLI_rctf_size_x(&box) * xa;
-}
+/* -------------------------------------------------------------------- */
+/** \name Font Query: Attributes
+ * \{ */
-float blf_font_height(FontBLF *font, const char *str, size_t len, struct ResultBLF *r_info)
+int blf_font_height_max(FontBLF *font)
{
- float ya;
- rctf box;
+ int height_max;
- if (font->flags & BLF_ASPECT) {
- ya = font->aspect[1];
- }
- else {
- ya = 1.0f;
- }
+ GlyphCacheBLF *gc = blf_glyph_cache_acquire(font);
+ height_max = gc->glyph_height_max;
- if (font->flags & BLF_WORD_WRAP) {
- blf_font_boundbox__wrap(font, str, len, &box, r_info);
- }
- else {
- blf_font_boundbox(font, str, len, &box, r_info);
- }
- return BLI_rctf_size_y(&box) * ya;
+ blf_glyph_cache_release(font);
+ return height_max;
}
-float blf_font_fixed_width(FontBLF *font)
+int blf_font_width_max(FontBLF *font)
{
- const unsigned int c = ' ';
+ int width_max;
GlyphCacheBLF *gc = blf_glyph_cache_acquire(font);
- GlyphBLF *g = blf_glyph_search(gc, c);
- if (!g) {
- g = blf_glyph_add(font, gc, FT_Get_Char_Index(font->face, c), c);
-
- /* if we don't find the glyph. */
- if (!g) {
- blf_glyph_cache_release(font);
- return 0.0f;
- }
- }
+ width_max = gc->glyph_width_max;
blf_glyph_cache_release(font);
- return g->advance;
+ return width_max;
}
-/* -------------------------------------------------------------------- */
-/** \name Glyph Bound Box with Callback
- * \{ */
-
-static void blf_font_boundbox_foreach_glyph_ex(FontBLF *font,
- GlyphCacheBLF *gc,
- const char *str,
- size_t len,
- BLF_GlyphBoundsFn user_fn,
- void *user_data,
- struct ResultBLF *r_info,
- int pen_y)
+float blf_font_descender(FontBLF *font)
{
- unsigned int c, c_prev = BLI_UTF8_ERR;
- GlyphBLF *g, *g_prev = NULL;
- int pen_x = 0;
- size_t i = 0, i_curr;
- rcti gbox;
-
- if (len == 0) {
- /* early output. */
- return;
- }
-
- blf_font_ensure_ascii_kerning(font, gc);
-
- while ((i < len) && str[i]) {
- i_curr = i;
- g = blf_utf8_next_fast(font, gc, str, &i, &c);
-
- if (UNLIKELY(c == BLI_UTF8_ERR)) {
- break;
- }
- if (UNLIKELY(g == NULL)) {
- continue;
- }
- blf_kerning_step_fast(font, g_prev, g, c_prev, c, &pen_x);
+ float descender;
- gbox.xmin = pen_x;
- gbox.xmax = gbox.xmin + MIN2(g->advance_i, g->dims[0]);
- gbox.ymin = pen_y;
- gbox.ymax = gbox.ymin - g->dims[1];
+ GlyphCacheBLF *gc = blf_glyph_cache_acquire(font);
+ descender = gc->descender;
- pen_x += g->advance_i;
+ blf_glyph_cache_release(font);
+ return descender;
+}
- if (user_fn(str, i_curr, &gbox, g->advance_i, &g->box, g->pos, user_data) == false) {
- break;
- }
+float blf_font_ascender(FontBLF *font)
+{
+ float ascender;
- g_prev = g;
- c_prev = c;
- }
+ GlyphCacheBLF *gc = blf_glyph_cache_acquire(font);
+ ascender = gc->ascender;
- if (r_info) {
- r_info->lines = 1;
- r_info->width = pen_x;
- }
+ blf_glyph_cache_release(font);
+ return ascender;
}
-void blf_font_boundbox_foreach_glyph(FontBLF *font,
- const char *str,
- size_t len,
- BLF_GlyphBoundsFn user_fn,
- void *user_data,
- struct ResultBLF *r_info)
+
+char *blf_display_name(FontBLF *font)
{
- GlyphCacheBLF *gc = blf_glyph_cache_acquire(font);
- blf_font_boundbox_foreach_glyph_ex(font, gc, str, len, user_fn, user_data, r_info, 0);
- blf_glyph_cache_release(font);
+ if (!font->face->family_name) {
+ return NULL;
+ }
+ return BLI_sprintfN("%s %s", font->face->family_name, font->face->style_name);
}
/** \} */
-int blf_font_count_missing_chars(FontBLF *font,
- const char *str,
- const size_t len,
- int *r_tot_chars)
-{
- int missing = 0;
- size_t i = 0;
-
- *r_tot_chars = 0;
- while (i < len) {
- unsigned int c;
+/* -------------------------------------------------------------------- */
+/** \name Font Subsystem Init/Exit
+ * \{ */
- if ((c = str[i]) < GLYPH_ASCII_TABLE_SIZE) {
- i++;
- }
- else if ((c = BLI_str_utf8_as_unicode_step(str, &i)) != BLI_UTF8_ERR) {
- if (FT_Get_Char_Index((font)->face, c) == 0) {
- missing++;
- }
- }
- (*r_tot_chars)++;
- }
- return missing;
+int blf_font_init(void)
+{
+ memset(&g_batch, 0, sizeof(g_batch));
+ BLI_spin_init(&ft_lib_mutex);
+ BLI_spin_init(&blf_glyph_cache_mutex);
+ return FT_Init_FreeType(&ft_lib);
}
-void blf_font_free(FontBLF *font)
+void blf_font_exit(void)
{
- BLI_spin_lock(&blf_glyph_cache_mutex);
- GlyphCacheBLF *gc;
-
- while ((gc = BLI_pophead(&font->cache))) {
- blf_glyph_cache_free(gc);
- }
-
- blf_kerning_cache_clear(font);
+ FT_Done_FreeType(ft_lib);
+ BLI_spin_end(&ft_lib_mutex);
+ BLI_spin_end(&blf_glyph_cache_mutex);
+ blf_batch_draw_exit();
+}
- FT_Done_Face(font->face);
- if (font->filename) {
- MEM_freeN(font->filename);
- }
- if (font->name) {
- MEM_freeN(font->name);
- }
- MEM_freeN(font);
+/** \} */
- BLI_spin_unlock(&blf_glyph_cache_mutex);
-}
+/* -------------------------------------------------------------------- */
+/** \name Font New/Free
+ * \{ */
static void blf_font_fill(FontBLF *font)
{
@@ -1246,7 +1325,6 @@ static void blf_font_fill(FontBLF *font)
font->dpi = 0;
font->size = 0;
BLI_listbase_clear(&font->cache);
- BLI_listbase_clear(&font->kerning_caches);
font->kerning_cache = NULL;
#if BLF_BLUR_ENABLE
font->blur = 0;
@@ -1307,6 +1385,17 @@ FontBLF *blf_font_new(const char *name, const char *filename)
font->name = BLI_strdup(name);
font->filename = BLI_strdup(filename);
blf_font_fill(font);
+
+ if (FT_HAS_KERNING(font->face)) {
+ /* Create kerning cache table and fill with value indicating "unset". */
+ font->kerning_cache = MEM_mallocN(sizeof(KerningCacheBLF), __func__);
+ for (uint i = 0; i < KERNING_CACHE_TABLE_SIZE; i++) {
+ for (uint j = 0; j < KERNING_CACHE_TABLE_SIZE; j++) {
+ font->kerning_cache->ascii_table[i][j] = KERNING_ENTRY_UNSET;
+ }
+ }
+ }
+
return font;
}
@@ -1346,54 +1435,61 @@ FontBLF *blf_font_new_from_mem(const char *name, const unsigned char *mem, int m
return font;
}
-int blf_font_height_max(FontBLF *font)
+void blf_font_free(FontBLF *font)
{
- int height_max;
-
- GlyphCacheBLF *gc = blf_glyph_cache_acquire(font);
- height_max = gc->glyph_height_max;
+ BLI_spin_lock(&blf_glyph_cache_mutex);
+ GlyphCacheBLF *gc;
- blf_glyph_cache_release(font);
- return height_max;
-}
+ while ((gc = BLI_pophead(&font->cache))) {
+ blf_glyph_cache_free(gc);
+ }
-int blf_font_width_max(FontBLF *font)
-{
- int width_max;
+ if (font->kerning_cache) {
+ MEM_freeN(font->kerning_cache);
+ }
- GlyphCacheBLF *gc = blf_glyph_cache_acquire(font);
- width_max = gc->glyph_width_max;
+ FT_Done_Face(font->face);
+ if (font->filename) {
+ MEM_freeN(font->filename);
+ }
+ if (font->name) {
+ MEM_freeN(font->name);
+ }
+ MEM_freeN(font);
- blf_glyph_cache_release(font);
- return width_max;
+ BLI_spin_unlock(&blf_glyph_cache_mutex);
}
-float blf_font_descender(FontBLF *font)
-{
- float descender;
-
- GlyphCacheBLF *gc = blf_glyph_cache_acquire(font);
- descender = gc->descender;
+/** \} */
- blf_glyph_cache_release(font);
- return descender;
-}
+/* -------------------------------------------------------------------- */
+/** \name Font Configure
+ * \{ */
-float blf_font_ascender(FontBLF *font)
+void blf_font_size(FontBLF *font, unsigned int size, unsigned int dpi)
{
- float ascender;
+ blf_glyph_cache_acquire(font);
- GlyphCacheBLF *gc = blf_glyph_cache_acquire(font);
- ascender = gc->ascender;
+ GlyphCacheBLF *gc = blf_glyph_cache_find(font, size, dpi);
+ if (gc && (font->size == size && font->dpi == dpi)) {
+ /* Optimization: do not call FT_Set_Char_Size if size did not change. */
+ }
+ else {
+ const FT_Error err = FT_Set_Char_Size(font->face, 0, ((FT_F26Dot6)(size)) * 64, dpi, dpi);
+ if (err) {
+ /* FIXME: here we can go through the fixed size and choice a close one */
+ printf("The current font don't support the size, %u and dpi, %u\n", size, dpi);
+ }
+ else {
+ font->size = size;
+ font->dpi = dpi;
+ if (gc == NULL) {
+ blf_glyph_cache_new(font);
+ }
+ }
+ }
blf_glyph_cache_release(font);
- return ascender;
}
-char *blf_display_name(FontBLF *font)
-{
- if (!font->face->family_name) {
- return NULL;
- }
- return BLI_sprintfN("%s %s", font->face->family_name, font->face->style_name);
-}
+/** \} */
diff --git a/source/blender/blenfont/intern/blf_glyph.c b/source/blender/blenfont/intern/blf_glyph.c
index 5fb69251466..6cdf5fc5996 100644
--- a/source/blender/blenfont/intern/blf_glyph.c
+++ b/source/blender/blenfont/intern/blf_glyph.c
@@ -55,56 +55,6 @@
#include "BLI_math_vector.h"
#include "BLI_strict_flags.h"
-KerningCacheBLF *blf_kerning_cache_find(FontBLF *font)
-{
- return (KerningCacheBLF *)font->kerning_caches.first;
-}
-
-/* Create a new glyph cache for the current kerning mode. */
-KerningCacheBLF *blf_kerning_cache_new(FontBLF *font, GlyphCacheBLF *gc)
-{
- KerningCacheBLF *kc = MEM_mallocN(sizeof(KerningCacheBLF), __func__);
- kc->next = NULL;
- kc->prev = NULL;
-
- GlyphBLF *g_table[KERNING_CACHE_TABLE_SIZE];
- for (uint i = 0; i < KERNING_CACHE_TABLE_SIZE; i++) {
- GlyphBLF *g = blf_glyph_search(gc, i);
- if (UNLIKELY(g == NULL)) {
- FT_UInt glyph_index = FT_Get_Char_Index(font->face, i);
- g = blf_glyph_add(font, gc, glyph_index, i);
- }
- g_table[i] = g;
- }
-
- memset(kc->ascii_table, 0, sizeof(kc->ascii_table));
- for (uint i = 0; i < KERNING_CACHE_TABLE_SIZE; i++) {
- GlyphBLF *g = g_table[i];
- if (g == NULL) {
- continue;
- }
- for (uint j = 0; j < KERNING_CACHE_TABLE_SIZE; j++) {
- GlyphBLF *g_prev = g_table[j];
- if (g_prev == NULL) {
- continue;
- }
- FT_Vector delta;
- if (FT_Get_Kerning(font->face, g_prev->idx, g->idx, FT_KERNING_DEFAULT, &delta) == 0) {
- kc->ascii_table[i][j] = (int)delta.x >> 6;
- }
- }
- }
-
- BLI_addhead(&font->kerning_caches, kc);
- return kc;
-}
-
-void blf_kerning_cache_clear(FontBLF *font)
-{
- font->kerning_cache = NULL;
- BLI_freelistN(&font->kerning_caches);
-}
-
GlyphCacheBLF *blf_glyph_cache_find(FontBLF *font, unsigned int size, unsigned int dpi)
{
GlyphCacheBLF *p;
diff --git a/source/blender/blenfont/intern/blf_internal.h b/source/blender/blenfont/intern/blf_internal.h
index 35a6d019eac..6fd5e8b7503 100644
--- a/source/blender/blenfont/intern/blf_internal.h
+++ b/source/blender/blenfont/intern/blf_internal.h
@@ -53,46 +53,55 @@ struct FontBLF *blf_font_new_from_mem(const char *name, const unsigned char *mem
void blf_font_attach_from_mem(struct FontBLF *font, const unsigned char *mem, int mem_size);
void blf_font_size(struct FontBLF *font, unsigned int size, unsigned int dpi);
-void blf_font_draw(struct FontBLF *font, const char *str, size_t len, struct ResultBLF *r_info);
+void blf_font_draw(struct FontBLF *font,
+ const char *str,
+ size_t str_len,
+ struct ResultBLF *r_info);
void blf_font_draw__wrap(struct FontBLF *font,
const char *str,
- size_t len,
+ size_t str_len,
struct ResultBLF *r_info);
void blf_font_draw_ascii(struct FontBLF *font,
const char *str,
- size_t len,
+ size_t str_len,
struct ResultBLF *r_info);
-int blf_font_draw_mono(struct FontBLF *font, const char *str, size_t len, int cwidth);
+int blf_font_draw_mono(struct FontBLF *font, const char *str, size_t str_len, int cwidth);
void blf_font_draw_buffer(struct FontBLF *font,
const char *str,
- size_t len,
+ size_t str_len,
struct ResultBLF *r_info);
void blf_font_draw_buffer__wrap(struct FontBLF *font,
const char *str,
- size_t len,
+ size_t str_len,
struct ResultBLF *r_info);
size_t blf_font_width_to_strlen(
- struct FontBLF *font, const char *str, size_t len, float width, float *r_width);
+ struct FontBLF *font, const char *str, size_t str_len, float width, float *r_width);
size_t blf_font_width_to_rstrlen(
- struct FontBLF *font, const char *str, size_t len, float width, float *r_width);
+ struct FontBLF *font, const char *str, size_t str_len, float width, float *r_width);
void blf_font_boundbox(struct FontBLF *font,
const char *str,
- size_t len,
+ size_t str_len,
struct rctf *r_box,
struct ResultBLF *r_info);
void blf_font_boundbox__wrap(struct FontBLF *font,
const char *str,
- size_t len,
+ size_t str_len,
struct rctf *r_box,
struct ResultBLF *r_info);
void blf_font_width_and_height(struct FontBLF *font,
const char *str,
- size_t len,
+ size_t str_len,
float *r_width,
float *r_height,
struct ResultBLF *r_info);
-float blf_font_width(struct FontBLF *font, const char *str, size_t len, struct ResultBLF *r_info);
-float blf_font_height(struct FontBLF *font, const char *str, size_t len, struct ResultBLF *r_info);
+float blf_font_width(struct FontBLF *font,
+ const char *str,
+ size_t str_len,
+ struct ResultBLF *r_info);
+float blf_font_height(struct FontBLF *font,
+ const char *str,
+ size_t str_len,
+ struct ResultBLF *r_info);
float blf_font_fixed_width(struct FontBLF *font);
int blf_font_height_max(struct FontBLF *font);
int blf_font_width_max(struct FontBLF *font);
@@ -103,7 +112,7 @@ char *blf_display_name(struct FontBLF *font);
void blf_font_boundbox_foreach_glyph(struct FontBLF *font,
const char *str,
- size_t len,
+ size_t str_len,
bool (*user_fn)(const char *str,
const size_t str_step_ofs,
const struct rcti *glyph_step_bounds,
@@ -116,15 +125,11 @@ void blf_font_boundbox_foreach_glyph(struct FontBLF *font,
int blf_font_count_missing_chars(struct FontBLF *font,
const char *str,
- const size_t len,
+ const size_t str_len,
int *r_tot_chars);
void blf_font_free(struct FontBLF *font);
-struct KerningCacheBLF *blf_kerning_cache_find(struct FontBLF *font);
-struct KerningCacheBLF *blf_kerning_cache_new(struct FontBLF *font, struct GlyphCacheBLF *gc);
-void blf_kerning_cache_clear(struct FontBLF *font);
-
struct GlyphCacheBLF *blf_glyph_cache_find(struct FontBLF *font,
unsigned int size,
unsigned int dpi);
diff --git a/source/blender/blenfont/intern/blf_internal_types.h b/source/blender/blenfont/intern/blf_internal_types.h
index caa10b2b125..38d7d7b6e21 100644
--- a/source/blender/blenfont/intern/blf_internal_types.h
+++ b/source/blender/blenfont/intern/blf_internal_types.h
@@ -34,6 +34,9 @@
/* Number of characters in KerningCacheBLF.table. */
#define KERNING_CACHE_TABLE_SIZE 128
+/* A value in the kerning cache that indicates it is not yet set. */
+#define KERNING_ENTRY_UNSET INT_MAX
+
typedef struct BatchBLF {
struct FontBLF *font; /* can only batch glyph from the same font */
struct GPUBatch *batch;
@@ -50,7 +53,6 @@ typedef struct BatchBLF {
extern BatchBLF g_batch;
typedef struct KerningCacheBLF {
- struct KerningCacheBLF *next, *prev;
/**
* Cache a ascii glyph pairs. Only store the x offset we are interested in,
* instead of the full #FT_Vector since it's not used for drawing at the moment.
@@ -223,10 +225,7 @@ typedef struct FontBLF {
*/
ListBase cache;
- /* list of kerning cache for this font. */
- ListBase kerning_caches;
-
- /* current kerning cache for this font and kerning mode. */
+ /* Cache of unscaled kerning values. Will be NULL if font does not have kerning. */
KerningCacheBLF *kerning_cache;
/* freetype2 lib handle. */
diff --git a/source/blender/blenkernel/BKE_cachefile.h b/source/blender/blenkernel/BKE_cachefile.h
index 58d876b184b..836597f95da 100644
--- a/source/blender/blenkernel/BKE_cachefile.h
+++ b/source/blender/blenkernel/BKE_cachefile.h
@@ -61,11 +61,6 @@ void BKE_cachefile_reader_open(struct CacheFile *cache_file,
const char *object_path);
void BKE_cachefile_reader_free(struct CacheFile *cache_file, struct CacheReader **reader);
-/* Determine whether the CacheFile should use a render engine procedural. If so, data is not read
- * from the file and bouding boxes are used to represent the objects in the Scene. Render engines
- * will receive the bounding box as a placeholder but can instead load the data directly if they
- * support it.
- */
bool BKE_cache_file_uses_render_procedural(const struct CacheFile *cache_file,
struct Scene *scene,
const int dag_eval_mode);
diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h
index 1f7aa4dd26f..c7864e8909d 100644
--- a/source/blender/blenkernel/BKE_node.h
+++ b/source/blender/blenkernel/BKE_node.h
@@ -111,8 +111,7 @@ typedef struct bNodeSocketTemplate {
#ifdef __cplusplus
namespace blender {
namespace nodes {
-class SocketMFNetworkBuilder;
-class NodeMFNetworkBuilder;
+class NodeMultiFunctionBuilder;
class GeoNodeExecParams;
} // namespace nodes
namespace fn {
@@ -121,18 +120,16 @@ class MFDataType;
} // namespace fn
} // namespace blender
-using NodeExpandInMFNetworkFunction = void (*)(blender::nodes::NodeMFNetworkBuilder &builder);
+using NodeMultiFunctionBuildFunction = void (*)(blender::nodes::NodeMultiFunctionBuilder &builder);
using NodeGeometryExecFunction = void (*)(blender::nodes::GeoNodeExecParams params);
using SocketGetCPPTypeFunction = const blender::fn::CPPType *(*)();
using SocketGetCPPValueFunction = void (*)(const struct bNodeSocket &socket, void *r_value);
using SocketGetGeometryNodesCPPTypeFunction = const blender::fn::CPPType *(*)();
using SocketGetGeometryNodesCPPValueFunction = void (*)(const struct bNodeSocket &socket,
void *r_value);
-using SocketExpandInMFNetworkFunction = void (*)(blender::nodes::SocketMFNetworkBuilder &builder);
#else
-typedef void *NodeExpandInMFNetworkFunction;
-typedef void *SocketExpandInMFNetworkFunction;
+typedef void *NodeMultiFunctionBuildFunction;
typedef void *NodeGeometryExecFunction;
typedef void *SocketGetCPPTypeFunction;
typedef void *SocketGetGeometryNodesCPPTypeFunction;
@@ -196,8 +193,6 @@ typedef struct bNodeSocketType {
/* Callback to free the socket type. */
void (*free_self)(struct bNodeSocketType *stype);
- /* Expands the socket into a multi-function node that outputs the socket value. */
- SocketExpandInMFNetworkFunction expand_in_mf_network;
/* Return the CPPType of this socket. */
SocketGetCPPTypeFunction get_base_cpp_type;
/* Get the value of this socket in a generic way. */
@@ -332,8 +327,8 @@ typedef struct bNodeType {
/* gpu */
NodeGPUExecFunction gpu_fn;
- /* Expands the bNode into nodes in a multi-function network, which will be evaluated later on. */
- NodeExpandInMFNetworkFunction expand_in_mf_network;
+ /* Build a multi-function for this node. */
+ NodeMultiFunctionBuildFunction build_multi_function;
/* Execute a geometry node. */
NodeGeometryExecFunction geometry_node_execute;
@@ -351,7 +346,7 @@ typedef struct bNodeType {
#define NODE_CLASS_OP_FILTER 5
#define NODE_CLASS_GROUP 6
// #define NODE_CLASS_FILE 7
-#define NODE_CLASS_CONVERTOR 8
+#define NODE_CLASS_CONVERTER 8
#define NODE_CLASS_MATTE 9
#define NODE_CLASS_DISTORT 10
// #define NODE_CLASS_OP_DYNAMIC 11 /* deprecated */
diff --git a/source/blender/blenkernel/intern/bvhutils.cc b/source/blender/blenkernel/intern/bvhutils.cc
index 164f921c7ac..707201207d9 100644
--- a/source/blender/blenkernel/intern/bvhutils.cc
+++ b/source/blender/blenkernel/intern/bvhutils.cc
@@ -1254,11 +1254,10 @@ BVHTree *bvhtree_from_editmesh_looptri_ex(BVHTreeFromEditMesh *data,
bool in_cache = bvhcache_find(
bvh_cache_p, bvh_cache_type, &tree, &lock_started, mesh_eval_mutex);
BVHCache *bvh_cache = *bvh_cache_p;
- bvhtree_balance(tree, true);
-
if (in_cache == false) {
tree = bvhtree_from_editmesh_looptri_create_tree(
epsilon, tree_type, axis, em, looptri_mask, looptri_num_active);
+ bvhtree_balance(tree, true);
/* Save on cache for later use */
// printf("BVHTree built and saved on cache\n");
diff --git a/source/blender/blenkernel/intern/cachefile.c b/source/blender/blenkernel/intern/cachefile.c
index 4a60564b4a1..87b1584d422 100644
--- a/source/blender/blenkernel/intern/cachefile.c
+++ b/source/blender/blenkernel/intern/cachefile.c
@@ -368,7 +368,7 @@ void BKE_cachefile_eval(Main *bmain, Depsgraph *depsgraph, CacheFile *cache_file
#endif
if (DEG_is_active(depsgraph)) {
- /* Flush object paths back to original datablock for UI. */
+ /* Flush object paths back to original data-block for UI. */
CacheFile *cache_file_orig = (CacheFile *)DEG_get_original_id(&cache_file->id);
BLI_freelistN(&cache_file_orig->object_paths);
BLI_duplicatelist(&cache_file_orig->object_paths, &cache_file->object_paths);
@@ -411,6 +411,12 @@ float BKE_cachefile_time_offset(const CacheFile *cache_file, const float time, c
return cache_file->is_sequence ? frame : frame / fps - time_offset;
}
+/**
+ * Determine whether the #CacheFile should use a render engine procedural. If so, data is not read
+ * from the file and bounding boxes are used to represent the objects in the Scene.
+ * Render engines will receive the bounding box as a placeholder but can instead
+ * load the data directly if they support it.
+ */
bool BKE_cache_file_uses_render_procedural(const CacheFile *cache_file,
Scene *scene,
const int dag_eval_mode)
diff --git a/source/blender/blenkernel/intern/gpencil_geom.cc b/source/blender/blenkernel/intern/gpencil_geom.cc
index 0f218d6166c..5bca20ecd44 100644
--- a/source/blender/blenkernel/intern/gpencil_geom.cc
+++ b/source/blender/blenkernel/intern/gpencil_geom.cc
@@ -620,7 +620,10 @@ bool BKE_gpencil_stroke_trim_points(bGPDstroke *gps, const int index_from, const
}
if (new_count == 1) {
- BKE_gpencil_free_stroke_weights(gps);
+ if (gps->dvert) {
+ BKE_gpencil_free_stroke_weights(gps);
+ MEM_freeN(gps->dvert);
+ }
MEM_freeN(gps->points);
gps->points = nullptr;
gps->dvert = nullptr;
@@ -628,27 +631,24 @@ bool BKE_gpencil_stroke_trim_points(bGPDstroke *gps, const int index_from, const
return false;
}
- new_pt = (bGPDspoint *)MEM_callocN(sizeof(bGPDspoint) * new_count, "gp_stroke_points_trimmed");
-
- for (int i = 0; i < new_count; i++) {
- memcpy(&new_pt[i], &pt[i + index_from], sizeof(bGPDspoint));
- }
+ new_pt = (bGPDspoint *)MEM_mallocN(sizeof(bGPDspoint) * new_count, "gp_stroke_points_trimmed");
+ memcpy(new_pt, &pt[index_from], sizeof(bGPDspoint) * new_count);
if (gps->dvert) {
- new_dv = (MDeformVert *)MEM_callocN(sizeof(MDeformVert) * new_count,
+ new_dv = (MDeformVert *)MEM_mallocN(sizeof(MDeformVert) * new_count,
"gp_stroke_dverts_trimmed");
for (int i = 0; i < new_count; i++) {
dv = &gps->dvert[i + index_from];
new_dv[i].flag = dv->flag;
new_dv[i].totweight = dv->totweight;
- new_dv[i].dw = (MDeformWeight *)MEM_callocN(sizeof(MDeformWeight) * dv->totweight,
+ new_dv[i].dw = (MDeformWeight *)MEM_mallocN(sizeof(MDeformWeight) * dv->totweight,
"gp_stroke_dverts_dw_trimmed");
for (int j = 0; j < dv->totweight; j++) {
new_dv[i].dw[j].weight = dv->dw[j].weight;
new_dv[i].dw[j].def_nr = dv->dw[j].def_nr;
}
- BKE_defvert_clear(dv);
}
+ BKE_gpencil_free_stroke_weights(gps);
MEM_freeN(gps->dvert);
gps->dvert = new_dv;
}
@@ -692,25 +692,21 @@ bool BKE_gpencil_stroke_split(bGPdata *gpd,
gpf, gps, gps->mat_nr, new_count, gps->thickness);
new_pt = new_gps->points; /* Allocated from above. */
-
- for (int i = 0; i < new_count; i++) {
- memcpy(&new_pt[i], &pt[i + before_index], sizeof(bGPDspoint));
- }
+ memcpy(new_pt, &pt[before_index], sizeof(bGPDspoint) * new_count);
if (gps->dvert) {
- new_dv = (MDeformVert *)MEM_callocN(sizeof(MDeformVert) * new_count,
+ new_dv = (MDeformVert *)MEM_mallocN(sizeof(MDeformVert) * new_count,
"gp_stroke_dverts_remaining(MDeformVert)");
for (int i = 0; i < new_count; i++) {
dv = &gps->dvert[i + before_index];
new_dv[i].flag = dv->flag;
new_dv[i].totweight = dv->totweight;
- new_dv[i].dw = (MDeformWeight *)MEM_callocN(sizeof(MDeformWeight) * dv->totweight,
+ new_dv[i].dw = (MDeformWeight *)MEM_mallocN(sizeof(MDeformWeight) * dv->totweight,
"gp_stroke_dverts_dw_remaining(MDeformWeight)");
for (int j = 0; j < dv->totweight; j++) {
new_dv[i].dw[j].weight = dv->dw[j].weight;
new_dv[i].dw[j].def_nr = dv->dw[j].def_nr;
}
- BKE_defvert_clear(dv);
}
new_gps->dvert = new_dv;
}
diff --git a/source/blender/blenkernel/intern/lib_id.c b/source/blender/blenkernel/intern/lib_id.c
index 0f880d16358..495d5d79eb9 100644
--- a/source/blender/blenkernel/intern/lib_id.c
+++ b/source/blender/blenkernel/intern/lib_id.c
@@ -1481,7 +1481,7 @@ static bool id_name_final_build(char *name, char *base_name, size_t base_name_le
/* Code above may have generated invalid utf-8 string, due to raw truncation.
* Ensure we get a valid one now. */
- base_name_len -= (size_t)BLI_utf8_invalid_strip(base_name, base_name_len);
+ base_name_len -= (size_t)BLI_str_utf8_invalid_strip(base_name, base_name_len);
/* Also truncate orig name, and start the whole check again. */
name[base_name_len] = '\0';
@@ -1731,7 +1731,7 @@ bool BKE_id_new_name_validate(ListBase *lb, ID *id, const char *tname, const boo
else {
/* disallow non utf8 chars,
* the interface checks for this but new ID's based on file names don't */
- BLI_utf8_invalid_strip(name, strlen(name));
+ BLI_str_utf8_invalid_strip(name, strlen(name));
}
ID *id_sorting_hint = NULL;
diff --git a/source/blender/blenkernel/intern/simulation.cc b/source/blender/blenkernel/intern/simulation.cc
index 5aac29c19a7..4c97ccdf8b1 100644
--- a/source/blender/blenkernel/intern/simulation.cc
+++ b/source/blender/blenkernel/intern/simulation.cc
@@ -49,14 +49,10 @@
#include "BKE_simulation.h"
#include "NOD_geometry.h"
-#include "NOD_node_tree_multi_function.hh"
#include "BLI_map.hh"
#include "BLT_translation.h"
-#include "FN_multi_function_network_evaluation.hh"
-#include "FN_multi_function_network_optimization.hh"
-
#include "DEG_depsgraph.h"
#include "DEG_depsgraph_query.h"
diff --git a/source/blender/blenkernel/intern/text.c b/source/blender/blenkernel/intern/text.c
index 275cf0d4c38..06137f5d110 100644
--- a/source/blender/blenkernel/intern/text.c
+++ b/source/blender/blenkernel/intern/text.c
@@ -308,7 +308,7 @@ int txt_extended_ascii_as_utf8(char **str)
int added = 0;
while ((*str)[i]) {
- if ((bad_char = BLI_utf8_invalid_byte(*str + i, length - i)) == -1) {
+ if ((bad_char = BLI_str_utf8_invalid_byte(*str + i, length - i)) == -1) {
break;
}
@@ -322,7 +322,7 @@ int txt_extended_ascii_as_utf8(char **str)
i = 0;
while ((*str)[i]) {
- if ((bad_char = BLI_utf8_invalid_byte((*str) + i, length - i)) == -1) {
+ if ((bad_char = BLI_str_utf8_invalid_byte((*str) + i, length - i)) == -1) {
memcpy(newstr + mi, (*str) + i, length - i + 1);
break;
}
diff --git a/source/blender/blenlib/BLI_fileops.h b/source/blender/blenlib/BLI_fileops.h
index 7cfecc798a7..906a56ce909 100644
--- a/source/blender/blenlib/BLI_fileops.h
+++ b/source/blender/blenlib/BLI_fileops.h
@@ -154,18 +154,17 @@ bool BLI_file_is_writable(const char *file) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL
bool BLI_file_touch(const char *file) ATTR_NONNULL();
bool BLI_file_alias_target(const char *filepath, char *r_targetpath) ATTR_WARN_UNUSED_RESULT;
-#if 0 /* UNUSED */
-int BLI_file_gzip(const char *from, const char *to) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
-#endif
-char *BLI_file_ungzip_to_mem(const char *from_file, int *r_size) ATTR_WARN_UNUSED_RESULT
- ATTR_NONNULL();
-size_t BLI_gzip_mem_to_file_at_pos(void *buf,
- size_t len,
- FILE *file,
- size_t gz_stream_offset,
- int compression_level) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
-size_t BLI_ungzip_file_to_mem_at_pos(void *buf, size_t len, FILE *file, size_t gz_stream_offset)
+bool BLI_file_magic_is_gzip(const char header[4]);
+
+size_t BLI_file_zstd_from_mem_at_pos(void *buf,
+ size_t len,
+ FILE *file,
+ size_t file_offset,
+ int compression_level) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
+size_t BLI_file_unzstd_to_mem_at_pos(void *buf, size_t len, FILE *file, size_t file_offset)
ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
+bool BLI_file_magic_is_zstd(const char header[4]);
+
size_t BLI_file_descriptor_size(int file) ATTR_WARN_UNUSED_RESULT;
size_t BLI_file_size(const char *path) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
diff --git a/source/blender/blenlib/BLI_filereader.h b/source/blender/blenlib/BLI_filereader.h
new file mode 100644
index 00000000000..8d1fa3d1596
--- /dev/null
+++ b/source/blender/blenlib/BLI_filereader.h
@@ -0,0 +1,81 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
+ * All rights reserved.
+ */
+
+/** \file
+ * \ingroup bli
+ * \brief Wrapper for reading from various sources (e.g. raw files, compressed files, memory...).
+ */
+
+#pragma once
+
+#ifdef WIN32
+# include "BLI_winstuff.h"
+#else
+# include <sys/types.h>
+#endif
+
+#include "BLI_compiler_attrs.h"
+#include "BLI_utildefines.h"
+
+#if defined(_MSC_VER) || defined(__APPLE__) || defined(__HAIKU__) || defined(__NetBSD__)
+typedef int64_t off64_t;
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct FileReader;
+
+typedef ssize_t (*FileReaderReadFn)(struct FileReader *reader, void *buffer, size_t size);
+typedef off64_t (*FileReaderSeekFn)(struct FileReader *reader, off64_t offset, int whence);
+typedef void (*FileReaderCloseFn)(struct FileReader *reader);
+
+/* General structure for all FileReaders, implementations add custom fields at the end. */
+typedef struct FileReader {
+ FileReaderReadFn read;
+ FileReaderSeekFn seek;
+ FileReaderCloseFn close;
+
+ off64_t offset;
+} FileReader;
+
+/* Functions for opening the various types of FileReader.
+ * They either succeed and return a valid FileReader, or fail and return NULL.
+ *
+ * If a FileReader is created, it has to be cleaned up and freed by calling
+ * its close() function unless another FileReader has taken ownership - for example,
+ * Zstd and Gzip take over the base FileReader and will clean it up when their clean() is called.
+ */
+
+/* Create FileReader from raw file descriptor. */
+FileReader *BLI_filereader_new_file(int filedes) ATTR_WARN_UNUSED_RESULT;
+/* Create FileReader from raw file descriptor using memory-mapped IO. */
+FileReader *BLI_filereader_new_mmap(int filedes) ATTR_WARN_UNUSED_RESULT;
+/* Create FileReader from a region of memory. */
+FileReader *BLI_filereader_new_memory(const void *data, size_t len) ATTR_WARN_UNUSED_RESULT
+ ATTR_NONNULL();
+/* Create FileReader from applying Zstd decompression on an underlying file. */
+FileReader *BLI_filereader_new_zstd(FileReader *base) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
+/* Create FileReader from applying Gzip decompression on an underlying file. */
+FileReader *BLI_filereader_new_gzip(FileReader *base) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/source/blender/blenlib/BLI_math_base.h b/source/blender/blenlib/BLI_math_base.h
index e877503e835..dbdd28766a5 100644
--- a/source/blender/blenlib/BLI_math_base.h
+++ b/source/blender/blenlib/BLI_math_base.h
@@ -196,6 +196,8 @@ MINLINE unsigned int log2_ceil_u(unsigned int x);
MINLINE int divide_round_i(int a, int b);
MINLINE int mod_i(int i, int n);
+MINLINE float round_to_even(float f);
+
MINLINE signed char round_fl_to_char(float a);
MINLINE unsigned char round_fl_to_uchar(float a);
MINLINE short round_fl_to_short(float a);
diff --git a/source/blender/blenlib/BLI_string_utf8.h b/source/blender/blenlib/BLI_string_utf8.h
index 65d9d7863c3..e1d7e2c58f7 100644
--- a/source/blender/blenlib/BLI_string_utf8.h
+++ b/source/blender/blenlib/BLI_string_utf8.h
@@ -31,8 +31,8 @@ char *BLI_strncpy_utf8(char *__restrict dst, const char *__restrict src, size_t
ATTR_NONNULL();
size_t BLI_strncpy_utf8_rlen(char *__restrict dst, const char *__restrict src, size_t maxncpy)
ATTR_NONNULL();
-ptrdiff_t BLI_utf8_invalid_byte(const char *str, size_t length) ATTR_NONNULL();
-int BLI_utf8_invalid_strip(char *str, size_t length) ATTR_NONNULL();
+ptrdiff_t BLI_str_utf8_invalid_byte(const char *str, size_t length) ATTR_NONNULL();
+int BLI_str_utf8_invalid_strip(char *str, size_t length) ATTR_NONNULL();
/* warning, can return -1 on bad chars */
int BLI_str_utf8_size(const char *p) ATTR_NONNULL();
diff --git a/source/blender/blenlib/CMakeLists.txt b/source/blender/blenlib/CMakeLists.txt
index b6603dce378..f98d15ad08b 100644
--- a/source/blender/blenlib/CMakeLists.txt
+++ b/source/blender/blenlib/CMakeLists.txt
@@ -31,6 +31,7 @@ set(INC
set(INC_SYS
${ZLIB_INCLUDE_DIRS}
+ ${ZSTD_INCLUDE_DIRS}
${FREETYPE_INCLUDE_DIRS}
${GMP_INCLUDE_DIRS}
)
@@ -75,6 +76,10 @@ set(SRC
intern/endian_switch.c
intern/expr_pylike_eval.c
intern/fileops.c
+ intern/filereader_file.c
+ intern/filereader_gzip.c
+ intern/filereader_memory.c
+ intern/filereader_zstd.c
intern/fnmatch.c
intern/freetypefont.c
intern/gsqueue.c
@@ -194,6 +199,7 @@ set(SRC
BLI_enumerable_thread_specific.hh
BLI_expr_pylike_eval.h
BLI_fileops.h
+ BLI_filereader.h
BLI_fileops_types.h
BLI_float2.hh
BLI_float3.hh
@@ -323,6 +329,7 @@ set(LIB
${FREETYPE_LIBRARY}
${ZLIB_LIBRARIES}
+ ${ZSTD_LIBRARIES}
)
if(WITH_MEM_VALGRIND)
diff --git a/source/blender/blenlib/intern/fileops.c b/source/blender/blenlib/intern/fileops.c
index ac034d2b5cd..532a29b8147 100644
--- a/source/blender/blenlib/intern/fileops.c
+++ b/source/blender/blenlib/intern/fileops.c
@@ -31,6 +31,7 @@
#include <errno.h>
#include "zlib.h"
+#include "zstd.h"
#ifdef WIN32
# include "BLI_fileops_types.h"
@@ -61,200 +62,124 @@
#include "BLI_sys_types.h" /* for intptr_t support */
#include "BLI_utildefines.h"
-#if 0 /* UNUSED */
-/* gzip the file in from and write it to "to".
- * return -1 if zlib fails, -2 if the originating file does not exist
- * NOTE: will remove the "from" file
- */
-int BLI_file_gzip(const char *from, const char *to)
+size_t BLI_file_zstd_from_mem_at_pos(
+ void *buf, size_t len, FILE *file, size_t file_offset, int compression_level)
{
- char buffer[10240];
- int file;
- int readsize = 0;
- int rval = 0, err;
- gzFile gzfile;
+ fseek(file, file_offset, SEEK_SET);
- /* level 1 is very close to 3 (the default) in terms of file size,
- * but about twice as fast, best use for speedy saving - campbell */
- gzfile = BLI_gzopen(to, "wb1");
- if (gzfile == NULL) {
- return -1;
- }
- file = BLI_open(from, O_BINARY | O_RDONLY, 0);
- if (file == -1) {
- return -2;
- }
+ ZSTD_CCtx *ctx = ZSTD_createCCtx();
+ ZSTD_CCtx_setParameter(ctx, ZSTD_c_compressionLevel, compression_level);
+
+ ZSTD_inBuffer input = {buf, len, 0};
- while (1) {
- readsize = read(file, buffer, sizeof(buffer));
+ size_t out_len = ZSTD_CStreamOutSize();
+ void *out_buf = MEM_mallocN(out_len, __func__);
+ size_t total_written = 0;
- if (readsize < 0) {
- rval = -2; /* error happened in reading */
- fprintf(stderr, "Error reading file %s: %s.\n", from, strerror(errno));
+ /* Compress block and write it out until the input has been consumed. */
+ while (input.pos < input.size) {
+ ZSTD_outBuffer output = {out_buf, out_len, 0};
+ size_t ret = ZSTD_compressStream2(ctx, &output, &input, ZSTD_e_continue);
+ if (ZSTD_isError(ret)) {
break;
}
- else if (readsize == 0) {
- break; /* done reading */
+ if (fwrite(out_buf, 1, output.pos, file) != output.pos) {
+ break;
}
+ total_written += output.pos;
+ }
- if (gzwrite(gzfile, buffer, readsize) <= 0) {
- rval = -1; /* error happened in writing */
- fprintf(stderr, "Error writing gz file %s: %s.\n", to, gzerror(gzfile, &err));
+ /* Finalize the Zstd frame. */
+ size_t ret = 1;
+ while (ret != 0) {
+ ZSTD_outBuffer output = {out_buf, out_len, 0};
+ ret = ZSTD_compressStream2(ctx, &output, &input, ZSTD_e_end);
+ if (ZSTD_isError(ret)) {
+ break;
+ }
+ if (fwrite(out_buf, 1, output.pos, file) != output.pos) {
break;
}
+ total_written += output.pos;
}
- gzclose(gzfile);
- close(file);
+ MEM_freeN(out_buf);
+ ZSTD_freeCCtx(ctx);
- return rval;
+ return ZSTD_isError(ret) ? 0 : total_written;
}
-#endif
-/* gzip the file in from_file and write it to memory to_mem, at most size bytes.
- * return the unzipped size
- */
-char *BLI_file_ungzip_to_mem(const char *from_file, int *r_size)
+size_t BLI_file_unzstd_to_mem_at_pos(void *buf, size_t len, FILE *file, size_t file_offset)
{
- gzFile gzfile;
- int readsize, size, alloc_size = 0;
- char *mem = NULL;
- const int chunk_size = 512 * 1024;
+ fseek(file, file_offset, SEEK_SET);
- size = 0;
+ ZSTD_DCtx *ctx = ZSTD_createDCtx();
- gzfile = BLI_gzopen(from_file, "rb");
- for (;;) {
- if (mem == NULL) {
- mem = MEM_callocN(chunk_size, "BLI_ungzip_to_mem");
- alloc_size = chunk_size;
- }
- else {
- mem = MEM_reallocN(mem, size + chunk_size);
- alloc_size += chunk_size;
- }
+ size_t in_len = ZSTD_DStreamInSize();
+ void *in_buf = MEM_mallocN(in_len, __func__);
+ ZSTD_inBuffer input = {in_buf, in_len, 0};
- readsize = gzread(gzfile, mem + size, chunk_size);
- if (readsize > 0) {
- size += readsize;
- }
- else {
+ ZSTD_outBuffer output = {buf, len, 0};
+
+ size_t ret = 0;
+ /* Read and decompress chunks of input data until we have enough output. */
+ while (output.pos < output.size && !ZSTD_isError(ret)) {
+ input.size = fread(in_buf, 1, in_len, file);
+ if (input.size == 0) {
break;
}
- }
- gzclose(gzfile);
+ /* Consume input data until we run out or have enough output. */
+ input.pos = 0;
+ while (input.pos < input.size && output.pos < output.size) {
+ ret = ZSTD_decompressStream(ctx, &output, &input);
- if (size == 0) {
- MEM_freeN(mem);
- mem = NULL;
- }
- else if (alloc_size != size) {
- mem = MEM_reallocN(mem, size);
+ if (ZSTD_isError(ret)) {
+ break;
+ }
+ }
}
- *r_size = size;
+ MEM_freeN(in_buf);
+ ZSTD_freeDCtx(ctx);
- return mem;
+ return ZSTD_isError(ret) ? 0 : output.pos;
}
-#define CHUNK (256 * 1024)
-
-/* gzip byte array from memory and write it to file at certain position.
- * return size of gzip stream.
- */
-size_t BLI_gzip_mem_to_file_at_pos(
- void *buf, size_t len, FILE *file, size_t gz_stream_offset, int compression_level)
+bool BLI_file_magic_is_gzip(const char header[4])
{
- int ret, flush;
- unsigned have;
- z_stream strm;
- unsigned char out[CHUNK];
-
- BLI_fseek(file, gz_stream_offset, 0);
-
- strm.zalloc = Z_NULL;
- strm.zfree = Z_NULL;
- strm.opaque = Z_NULL;
- ret = deflateInit(&strm, compression_level);
- if (ret != Z_OK) {
- return 0;
- }
-
- strm.avail_in = len;
- strm.next_in = (Bytef *)buf;
- flush = Z_FINISH;
-
- do {
- strm.avail_out = CHUNK;
- strm.next_out = out;
- ret = deflate(&strm, flush);
- if (ret == Z_STREAM_ERROR) {
- return 0;
- }
- have = CHUNK - strm.avail_out;
- if (fwrite(out, 1, have, file) != have || ferror(file)) {
- deflateEnd(&strm);
- return 0;
- }
- } while (strm.avail_out == 0);
-
- if (strm.avail_in != 0 || ret != Z_STREAM_END) {
- return 0;
- }
-
- deflateEnd(&strm);
- return (size_t)strm.total_out;
+ /* GZIP itself starts with the magic bytes 0x1f 0x8b.
+ * The third byte indicates the compression method, which is 0x08 for DEFLATE. */
+ return header[0] == 0x1f && header[1] == 0x8b && header[2] == 0x08;
}
-/* read and decompress gzip stream from file at certain position to buffer.
- * return size of decompressed data.
- */
-size_t BLI_ungzip_file_to_mem_at_pos(void *buf, size_t len, FILE *file, size_t gz_stream_offset)
+bool BLI_file_magic_is_zstd(const char header[4])
{
- int ret;
- z_stream strm;
- size_t chunk = 256 * 1024;
- unsigned char in[CHUNK];
-
- BLI_fseek(file, gz_stream_offset, 0);
-
- strm.zalloc = Z_NULL;
- strm.zfree = Z_NULL;
- strm.opaque = Z_NULL;
- strm.avail_in = 0;
- strm.next_in = Z_NULL;
- ret = inflateInit(&strm);
- if (ret != Z_OK) {
- return 0;
+ /* ZSTD files consist of concatenated frames, each either a Zstd frame or a skippable frame.
+ * Both types of frames start with a magic number: 0xFD2FB528 for Zstd frames and 0x184D2A5*
+ * for skippable frames, with the * being anything from 0 to F.
+ *
+ * To check whether a file is Zstd-compressed, we just check whether the first frame matches
+ * either. Seeking through the file until a Zstd frame is found would make things more
+ * complicated and the probability of a false positive is rather low anyways.
+ *
+ * Note that LZ4 uses a compatible format, so even though its compressed frames have a
+ * different magic number, a valid LZ4 file might also start with a skippable frame matching
+ * the second check here.
+ *
+ * For more details, see https://github.com/facebook/zstd/blob/dev/doc/zstd_compression_format.md
+ */
+
+ uint32_t magic = *((uint32_t *)header);
+ if (magic == 0xFD2FB528) {
+ return true;
}
-
- do {
- strm.avail_in = fread(in, 1, chunk, file);
- strm.next_in = in;
- if (ferror(file)) {
- inflateEnd(&strm);
- return 0;
- }
-
- do {
- strm.avail_out = len;
- strm.next_out = (Bytef *)buf + strm.total_out;
-
- ret = inflate(&strm, Z_NO_FLUSH);
- if (ret == Z_STREAM_ERROR) {
- return 0;
- }
- } while (strm.avail_out == 0);
-
- } while (ret != Z_STREAM_END);
-
- inflateEnd(&strm);
- return (size_t)strm.total_out;
+ if ((magic >> 4) == 0x184D2A5) {
+ return true;
+ }
+ return false;
}
-#undef CHUNK
-
/**
* Returns true if the file with the specified name can be written.
* This implementation uses access(2), which makes the check according
diff --git a/source/blender/blenlib/intern/filereader_file.c b/source/blender/blenlib/intern/filereader_file.c
new file mode 100644
index 00000000000..3a833871e27
--- /dev/null
+++ b/source/blender/blenlib/intern/filereader_file.c
@@ -0,0 +1,80 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2004-2021 Blender Foundation
+ * All rights reserved.
+ */
+
+/** \file
+ * \ingroup bli
+ */
+
+#ifndef WIN32
+# include <unistd.h> /* for read close */
+#else
+# include "BLI_winstuff.h"
+# include "winsock2.h"
+# include <io.h> /* for open close read */
+#endif
+
+#include "BLI_blenlib.h"
+#include "BLI_filereader.h"
+
+#include "MEM_guardedalloc.h"
+
+typedef struct {
+ FileReader reader;
+
+ int filedes;
+} RawFileReader;
+
+static ssize_t file_read(FileReader *reader, void *buffer, size_t size)
+{
+ RawFileReader *rawfile = (RawFileReader *)reader;
+ ssize_t readsize = read(rawfile->filedes, buffer, size);
+
+ if (readsize >= 0) {
+ rawfile->reader.offset += readsize;
+ }
+
+ return readsize;
+}
+
+static off64_t file_seek(FileReader *reader, off64_t offset, int whence)
+{
+ RawFileReader *rawfile = (RawFileReader *)reader;
+ rawfile->reader.offset = BLI_lseek(rawfile->filedes, offset, whence);
+ return rawfile->reader.offset;
+}
+
+static void file_close(FileReader *reader)
+{
+ RawFileReader *rawfile = (RawFileReader *)reader;
+ close(rawfile->filedes);
+ MEM_freeN(rawfile);
+}
+
+FileReader *BLI_filereader_new_file(int filedes)
+{
+ RawFileReader *rawfile = MEM_callocN(sizeof(RawFileReader), __func__);
+
+ rawfile->filedes = filedes;
+
+ rawfile->reader.read = file_read;
+ rawfile->reader.seek = file_seek;
+ rawfile->reader.close = file_close;
+
+ return (FileReader *)rawfile;
+}
diff --git a/source/blender/blenlib/intern/filereader_gzip.c b/source/blender/blenlib/intern/filereader_gzip.c
new file mode 100644
index 00000000000..72eb153a8b9
--- /dev/null
+++ b/source/blender/blenlib/intern/filereader_gzip.c
@@ -0,0 +1,108 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2004-2021 Blender Foundation
+ * All rights reserved.
+ */
+
+/** \file
+ * \ingroup bli
+ */
+
+#include <zlib.h>
+
+#include "BLI_blenlib.h"
+#include "BLI_filereader.h"
+
+#include "MEM_guardedalloc.h"
+
+typedef struct {
+ FileReader reader;
+
+ FileReader *base;
+
+ z_stream strm;
+
+ void *in_buf;
+ size_t in_size;
+} GzipReader;
+
+static ssize_t gzip_read(FileReader *reader, void *buffer, size_t size)
+{
+ GzipReader *gzip = (GzipReader *)reader;
+
+ gzip->strm.avail_out = size;
+ gzip->strm.next_out = buffer;
+
+ while (gzip->strm.avail_out > 0) {
+ if (gzip->strm.avail_in == 0) {
+ /* Ran out of buffered input data, read some more. */
+ size_t readsize = gzip->base->read(gzip->base, gzip->in_buf, gzip->in_size);
+
+ if (readsize > 0) {
+ /* We got some data, so mark the buffer as refilled. */
+ gzip->strm.avail_in = readsize;
+ gzip->strm.next_in = gzip->in_buf;
+ }
+ else {
+ /* The underlying file is EOF, so return as much as we can. */
+ break;
+ }
+ }
+
+ int ret = inflate(&gzip->strm, Z_NO_FLUSH);
+
+ if (ret != Z_OK && ret != Z_BUF_ERROR) {
+ break;
+ }
+ }
+
+ ssize_t read_len = size - gzip->strm.avail_out;
+ gzip->reader.offset += read_len;
+ return read_len;
+}
+
+static void gzip_close(FileReader *reader)
+{
+ GzipReader *gzip = (GzipReader *)reader;
+
+ if (inflateEnd(&gzip->strm) != Z_OK) {
+ printf("close gzip stream error\n");
+ }
+ MEM_freeN((void *)gzip->in_buf);
+
+ gzip->base->close(gzip->base);
+ MEM_freeN(gzip);
+}
+
+FileReader *BLI_filereader_new_gzip(FileReader *base)
+{
+ GzipReader *gzip = MEM_callocN(sizeof(GzipReader), __func__);
+ gzip->base = base;
+
+ if (inflateInit2(&gzip->strm, 16 + MAX_WBITS) != Z_OK) {
+ MEM_freeN(gzip);
+ return NULL;
+ }
+
+ gzip->in_size = 256 * 2014;
+ gzip->in_buf = MEM_mallocN(gzip->in_size, "gzip in buf");
+
+ gzip->reader.read = gzip_read;
+ gzip->reader.seek = NULL;
+ gzip->reader.close = gzip_close;
+
+ return (FileReader *)gzip;
+}
diff --git a/source/blender/blenlib/intern/filereader_memory.c b/source/blender/blenlib/intern/filereader_memory.c
new file mode 100644
index 00000000000..150fed7d1cc
--- /dev/null
+++ b/source/blender/blenlib/intern/filereader_memory.c
@@ -0,0 +1,145 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2004-2021 Blender Foundation
+ * All rights reserved.
+ */
+
+/** \file
+ * \ingroup bli
+ */
+
+#include <string.h>
+
+#include "BLI_blenlib.h"
+#include "BLI_filereader.h"
+#include "BLI_mmap.h"
+
+#include "MEM_guardedalloc.h"
+
+/* This file implements both memory-backed and memory-mapped-file-backed reading. */
+typedef struct {
+ FileReader reader;
+
+ const char *data;
+ BLI_mmap_file *mmap;
+ size_t length;
+} MemoryReader;
+
+static ssize_t memory_read_raw(FileReader *reader, void *buffer, size_t size)
+{
+ MemoryReader *mem = (MemoryReader *)reader;
+
+ /* Don't read more bytes than there are available in the buffer. */
+ size_t readsize = MIN2(size, (size_t)(mem->length - mem->reader.offset));
+
+ memcpy(buffer, mem->data + mem->reader.offset, readsize);
+ mem->reader.offset += readsize;
+
+ return readsize;
+}
+
+static off64_t memory_seek(FileReader *reader, off64_t offset, int whence)
+{
+ MemoryReader *mem = (MemoryReader *)reader;
+
+ off64_t new_pos;
+ if (whence == SEEK_CUR) {
+ new_pos = mem->reader.offset + offset;
+ }
+ else if (whence == SEEK_SET) {
+ new_pos = offset;
+ }
+ else if (whence == SEEK_END) {
+ new_pos = mem->length + offset;
+ }
+ else {
+ return -1;
+ }
+
+ if (new_pos < 0 || new_pos > mem->length) {
+ return -1;
+ }
+
+ mem->reader.offset = new_pos;
+ return mem->reader.offset;
+}
+
+static void memory_close_raw(FileReader *reader)
+{
+ MEM_freeN(reader);
+}
+
+FileReader *BLI_filereader_new_memory(const void *data, size_t len)
+{
+ MemoryReader *mem = MEM_callocN(sizeof(MemoryReader), __func__);
+
+ mem->data = (const char *)data;
+ mem->length = len;
+
+ mem->reader.read = memory_read_raw;
+ mem->reader.seek = memory_seek;
+ mem->reader.close = memory_close_raw;
+
+ return (FileReader *)mem;
+}
+
+/* Memory-mapped file reading.
+ * By using `mmap()`, we can map a file so that it can be treated like normal memory,
+ * meaning that we can just read from it with `memcpy()` etc.
+ * This avoids system call overhead and can significantly speed up file loading.
+ */
+
+static ssize_t memory_read_mmap(FileReader *reader, void *buffer, size_t size)
+{
+ MemoryReader *mem = (MemoryReader *)reader;
+
+ /* Don't read more bytes than there are available in the buffer. */
+ size_t readsize = MIN2(size, (size_t)(mem->length - mem->reader.offset));
+
+ if (!BLI_mmap_read(mem->mmap, buffer, mem->reader.offset, readsize)) {
+ return 0;
+ }
+
+ mem->reader.offset += readsize;
+
+ return readsize;
+}
+
+static void memory_close_mmap(FileReader *reader)
+{
+ MemoryReader *mem = (MemoryReader *)reader;
+ BLI_mmap_free(mem->mmap);
+ MEM_freeN(mem);
+}
+
+FileReader *BLI_filereader_new_mmap(int filedes)
+{
+ BLI_mmap_file *mmap = BLI_mmap_open(filedes);
+ if (mmap == NULL) {
+ return NULL;
+ }
+
+ MemoryReader *mem = MEM_callocN(sizeof(MemoryReader), __func__);
+
+ mem->mmap = mmap;
+ mem->length = BLI_lseek(filedes, 0, SEEK_END);
+
+ mem->reader.read = memory_read_mmap;
+ mem->reader.seek = memory_seek;
+ mem->reader.close = memory_close_mmap;
+
+ return (FileReader *)mem;
+}
diff --git a/source/blender/blenlib/intern/filereader_zstd.c b/source/blender/blenlib/intern/filereader_zstd.c
new file mode 100644
index 00000000000..785a40cd1a1
--- /dev/null
+++ b/source/blender/blenlib/intern/filereader_zstd.c
@@ -0,0 +1,335 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2021 Blender Foundation
+ * All rights reserved.
+ */
+
+/** \file
+ * \ingroup bli
+ */
+
+#include <string.h>
+#include <zstd.h>
+
+#include "BLI_blenlib.h"
+#include "BLI_endian_switch.h"
+#include "BLI_filereader.h"
+#include "BLI_math_base.h"
+
+#include "MEM_guardedalloc.h"
+
+typedef struct {
+ FileReader reader;
+
+ FileReader *base;
+
+ ZSTD_DCtx *ctx;
+ ZSTD_inBuffer in_buf;
+ size_t in_buf_max_size;
+
+ struct {
+ int num_frames;
+ size_t *compressed_ofs;
+ size_t *uncompressed_ofs;
+
+ char *cached_content;
+ int cached_frame;
+ } seek;
+} ZstdReader;
+
+static bool zstd_read_u32(FileReader *base, uint32_t *val)
+{
+ if (base->read(base, val, sizeof(uint32_t)) != sizeof(uint32_t)) {
+ return false;
+ }
+#ifdef __BIG_ENDIAN__
+ BLI_endian_switch_uint32(val);
+#endif
+ return true;
+}
+
+static bool zstd_read_seek_table(ZstdReader *zstd)
+{
+ FileReader *base = zstd->base;
+
+ /* The seek table frame is at the end of the file, so seek there
+ * and verify that there is enough data. */
+ if (base->seek(base, -4, SEEK_END) < 13) {
+ return false;
+ }
+ uint32_t magic;
+ if (!zstd_read_u32(base, &magic) || magic != 0x8F92EAB1) {
+ return false;
+ }
+
+ uint8_t flags;
+ if (base->seek(base, -5, SEEK_END) < 0 || base->read(base, &flags, 1) != 1) {
+ return false;
+ }
+ /* Bit 7 indicates checksums. Bits 5 and 6 must be zero. */
+ bool has_checksums = (flags & 0x80);
+ if (flags & 0x60) {
+ return false;
+ }
+
+ uint32_t num_frames;
+ if (base->seek(base, -9, SEEK_END) < 0 || !zstd_read_u32(base, &num_frames)) {
+ return false;
+ }
+
+ /* Each frame has either 2 or 3 uint32_t, and after that we have
+ * num_frames, flags and magic for another 9 bytes. */
+ uint32_t expected_frame_length = num_frames * (has_checksums ? 12 : 8) + 9;
+ /* The frame starts with another magic number and its length, but these
+ * two fields are not included when counting length. */
+ off64_t frame_start_ofs = 8 + expected_frame_length;
+ /* Sanity check: Before the start of the seek table frame,
+ * there must be num_frames frames, each of which at least 8 bytes long. */
+ off64_t seek_frame_start = base->seek(base, -frame_start_ofs, SEEK_END);
+ if (seek_frame_start < num_frames * 8) {
+ return false;
+ }
+
+ if (!zstd_read_u32(base, &magic) || magic != 0x184D2A5E) {
+ return false;
+ }
+
+ uint32_t frame_length;
+ if (!zstd_read_u32(base, &frame_length) || frame_length != expected_frame_length) {
+ return false;
+ }
+
+ zstd->seek.num_frames = num_frames;
+ zstd->seek.compressed_ofs = MEM_malloc_arrayN(num_frames + 1, sizeof(size_t), __func__);
+ zstd->seek.uncompressed_ofs = MEM_malloc_arrayN(num_frames + 1, sizeof(size_t), __func__);
+
+ size_t compressed_ofs = 0;
+ size_t uncompressed_ofs = 0;
+ for (int i = 0; i < num_frames; i++) {
+ uint32_t compressed_size, uncompressed_size;
+ if (!zstd_read_u32(base, &compressed_size) || !zstd_read_u32(base, &uncompressed_size)) {
+ break;
+ }
+ if (has_checksums && base->seek(base, 4, SEEK_CUR) < 0) {
+ break;
+ }
+ zstd->seek.compressed_ofs[i] = compressed_ofs;
+ zstd->seek.uncompressed_ofs[i] = uncompressed_ofs;
+ compressed_ofs += compressed_size;
+ uncompressed_ofs += uncompressed_size;
+ }
+ zstd->seek.compressed_ofs[num_frames] = compressed_ofs;
+ zstd->seek.uncompressed_ofs[num_frames] = uncompressed_ofs;
+
+ /* Seek to the end of the previous frame for the following BHead frame detection. */
+ if (seek_frame_start != compressed_ofs || base->seek(base, seek_frame_start, SEEK_SET) < 0) {
+ MEM_freeN(zstd->seek.compressed_ofs);
+ MEM_freeN(zstd->seek.uncompressed_ofs);
+ memset(&zstd->seek, 0, sizeof(zstd->seek));
+ return false;
+ }
+
+ zstd->seek.cached_frame = -1;
+
+ return true;
+}
+
+/* Find out which frame contains the given position in the uncompressed stream.
+ * Basically just bisection. */
+static int zstd_frame_from_pos(ZstdReader *zstd, size_t pos)
+{
+ int low = 0, high = zstd->seek.num_frames;
+
+ if (pos >= zstd->seek.uncompressed_ofs[zstd->seek.num_frames]) {
+ return -1;
+ }
+
+ while (low + 1 < high) {
+ int mid = low + ((high - low) >> 1);
+ if (zstd->seek.uncompressed_ofs[mid] <= pos) {
+ low = mid;
+ }
+ else {
+ high = mid;
+ }
+ }
+
+ return low;
+}
+
+/* Ensure that the currently loaded frame is the correct one. */
+static const char *zstd_ensure_cache(ZstdReader *zstd, int frame)
+{
+ if (zstd->seek.cached_frame == frame) {
+ /* Cached frame matches, so just return it. */
+ return zstd->seek.cached_content;
+ }
+
+ /* Cached frame doesn't match, so discard it and cache the wanted one onstead. */
+ MEM_SAFE_FREE(zstd->seek.cached_content);
+
+ size_t compressed_size = zstd->seek.compressed_ofs[frame + 1] - zstd->seek.compressed_ofs[frame];
+ size_t uncompressed_size = zstd->seek.uncompressed_ofs[frame + 1] -
+ zstd->seek.uncompressed_ofs[frame];
+
+ char *uncompressed_data = MEM_mallocN(uncompressed_size, __func__);
+ char *compressed_data = MEM_mallocN(compressed_size, __func__);
+ if (zstd->base->seek(zstd->base, zstd->seek.compressed_ofs[frame], SEEK_SET) < 0 ||
+ zstd->base->read(zstd->base, compressed_data, compressed_size) < compressed_size) {
+ MEM_freeN(compressed_data);
+ MEM_freeN(uncompressed_data);
+ return NULL;
+ }
+
+ size_t res = ZSTD_decompressDCtx(
+ zstd->ctx, uncompressed_data, uncompressed_size, compressed_data, compressed_size);
+ MEM_freeN(compressed_data);
+ if (ZSTD_isError(res) || res < uncompressed_size) {
+ MEM_freeN(uncompressed_data);
+ return NULL;
+ }
+
+ zstd->seek.cached_frame = frame;
+ zstd->seek.cached_content = uncompressed_data;
+ return uncompressed_data;
+}
+
+static ssize_t zstd_read_seekable(FileReader *reader, void *buffer, size_t size)
+{
+ ZstdReader *zstd = (ZstdReader *)reader;
+
+ size_t end_offset = zstd->reader.offset + size, read_len = 0;
+ while (zstd->reader.offset < end_offset) {
+ int frame = zstd_frame_from_pos(zstd, zstd->reader.offset);
+ if (frame < 0) {
+ /* EOF is reached, so return as much as we can. */
+ break;
+ }
+
+ const char *framedata = zstd_ensure_cache(zstd, frame);
+ if (framedata == NULL) {
+ /* Error while reading the frame, so return as much as we can. */
+ break;
+ }
+
+ size_t frame_end_offset = min_zz(zstd->seek.uncompressed_ofs[frame + 1], end_offset);
+ size_t frame_read_len = frame_end_offset - zstd->reader.offset;
+
+ size_t offset_in_frame = zstd->reader.offset - zstd->seek.uncompressed_ofs[frame];
+ memcpy((char *)buffer + read_len, framedata + offset_in_frame, frame_read_len);
+ read_len += frame_read_len;
+ zstd->reader.offset = frame_end_offset;
+ }
+
+ return read_len;
+}
+
+static off64_t zstd_seek(FileReader *reader, off64_t offset, int whence)
+{
+ ZstdReader *zstd = (ZstdReader *)reader;
+ off64_t new_pos;
+ if (whence == SEEK_SET) {
+ new_pos = offset;
+ }
+ else if (whence == SEEK_END) {
+ new_pos = zstd->seek.uncompressed_ofs[zstd->seek.num_frames] + offset;
+ }
+ else {
+ new_pos = zstd->reader.offset + offset;
+ }
+
+ if (new_pos < 0 || new_pos > zstd->seek.uncompressed_ofs[zstd->seek.num_frames]) {
+ return -1;
+ }
+ zstd->reader.offset = new_pos;
+ return zstd->reader.offset;
+}
+
+static ssize_t zstd_read(FileReader *reader, void *buffer, size_t size)
+{
+ ZstdReader *zstd = (ZstdReader *)reader;
+ ZSTD_outBuffer output = {buffer, size, 0};
+
+ while (output.pos < output.size) {
+ if (zstd->in_buf.pos == zstd->in_buf.size) {
+ /* Ran out of buffered input data, read some more. */
+ zstd->in_buf.pos = 0;
+ ssize_t readsize = zstd->base->read(
+ zstd->base, (char *)zstd->in_buf.src, zstd->in_buf_max_size);
+
+ if (readsize > 0) {
+ /* We got some data, so mark the buffer as refilled. */
+ zstd->in_buf.size = readsize;
+ }
+ else {
+ /* The underlying file is EOF, so return as much as we can. */
+ break;
+ }
+ }
+
+ if (ZSTD_isError(ZSTD_decompressStream(zstd->ctx, &output, &zstd->in_buf))) {
+ break;
+ }
+ }
+
+ zstd->reader.offset += output.pos;
+ return output.pos;
+}
+
+static void zstd_close(FileReader *reader)
+{
+ ZstdReader *zstd = (ZstdReader *)reader;
+
+ ZSTD_freeDCtx(zstd->ctx);
+ if (zstd->reader.seek) {
+ MEM_freeN(zstd->seek.uncompressed_ofs);
+ MEM_freeN(zstd->seek.compressed_ofs);
+ MEM_freeN(zstd->seek.cached_content);
+ }
+ else {
+ MEM_freeN((void *)zstd->in_buf.src);
+ }
+
+ zstd->base->close(zstd->base);
+ MEM_freeN(zstd);
+}
+
+FileReader *BLI_filereader_new_zstd(FileReader *base)
+{
+ ZstdReader *zstd = MEM_callocN(sizeof(ZstdReader), __func__);
+
+ zstd->ctx = ZSTD_createDCtx();
+ zstd->base = base;
+
+ if (zstd_read_seek_table(zstd)) {
+ zstd->reader.read = zstd_read_seekable;
+ zstd->reader.seek = zstd_seek;
+ }
+ else {
+ zstd->reader.read = zstd_read;
+ zstd->reader.seek = NULL;
+
+ zstd->in_buf_max_size = ZSTD_DStreamInSize();
+ zstd->in_buf.src = MEM_mallocN(zstd->in_buf_max_size, "zstd in buf");
+ zstd->in_buf.size = zstd->in_buf_max_size;
+ /* This signals that the buffer has run out,
+ * which will make the read function refill it on the first call. */
+ zstd->in_buf.pos = zstd->in_buf_max_size;
+ }
+ zstd->reader.close = zstd_close;
+
+ return (FileReader *)zstd;
+}
diff --git a/source/blender/blenlib/intern/freetypefont.c b/source/blender/blenlib/intern/freetypefont.c
index a8b50b66f5f..e1e3aa273b5 100644
--- a/source/blender/blenlib/intern/freetypefont.c
+++ b/source/blender/blenlib/intern/freetypefont.c
@@ -302,7 +302,7 @@ static VFontData *objfnt_to_ftvfontdata(PackedFile *pf)
/* Get the name. */
if (face->family_name) {
BLI_snprintf(vfd->name, sizeof(vfd->name), "%s %s", face->family_name, face->style_name);
- BLI_utf8_invalid_strip(vfd->name, strlen(vfd->name));
+ BLI_str_utf8_invalid_strip(vfd->name, strlen(vfd->name));
}
/* Select a character map. */
diff --git a/source/blender/blenlib/intern/math_base_inline.c b/source/blender/blenlib/intern/math_base_inline.c
index a80c495ecf3..983fd3b6543 100644
--- a/source/blender/blenlib/intern/math_base_inline.c
+++ b/source/blender/blenlib/intern/math_base_inline.c
@@ -363,6 +363,14 @@ MINLINE signed char round_db_to_char_clamp(double a){
#undef _round_clamp_fl_impl
#undef _round_clamp_db_impl
+/**
+ * Round to closest even number, halfway cases are rounded away from zero.
+ */
+MINLINE float round_to_even(float f)
+{
+ return roundf(f * 0.5f) * 2.0f;
+}
+
/* integer division that rounds 0.5 up, particularly useful for color blending
* with integers, to avoid gradual darkening when rounding down */
MINLINE int divide_round_i(int a, int b)
diff --git a/source/blender/blenlib/intern/string_utf8.c b/source/blender/blenlib/intern/string_utf8.c
index 19ff8764259..5710bd6b150 100644
--- a/source/blender/blenlib/intern/string_utf8.c
+++ b/source/blender/blenlib/intern/string_utf8.c
@@ -70,7 +70,7 @@ static const size_t utf8_skip_data[256] = {
*
* \return the offset of the first invalid byte.
*/
-ptrdiff_t BLI_utf8_invalid_byte(const char *str, size_t length)
+ptrdiff_t BLI_str_utf8_invalid_byte(const char *str, size_t length)
{
const unsigned char *p, *perr, *pend = (const unsigned char *)str + length;
unsigned char c;
@@ -200,14 +200,14 @@ utf8_error:
*
* \return number of stripped bytes.
*/
-int BLI_utf8_invalid_strip(char *str, size_t length)
+int BLI_str_utf8_invalid_strip(char *str, size_t length)
{
ptrdiff_t bad_char;
int tot = 0;
BLI_assert(str[length] == '\0');
- while ((bad_char = BLI_utf8_invalid_byte(str, length)) != -1) {
+ while ((bad_char = BLI_str_utf8_invalid_byte(str, length)) != -1) {
str += bad_char;
length -= (size_t)(bad_char + 1);
diff --git a/source/blender/blenlib/tests/BLI_string_utf8_test.cc b/source/blender/blenlib/tests/BLI_string_utf8_test.cc
index 9ddc372e6d1..13f5cb7f284 100644
--- a/source/blender/blenlib/tests/BLI_string_utf8_test.cc
+++ b/source/blender/blenlib/tests/BLI_string_utf8_test.cc
@@ -266,7 +266,7 @@ static const char *utf8_invalid_tests[][3] = {
};
/* clang-format on */
-/* BLI_utf8_invalid_strip (and indirectly, BLI_utf8_invalid_byte). */
+/* BLI_str_utf8_invalid_strip (and indirectly, BLI_str_utf8_invalid_byte). */
TEST(string, Utf8InvalidBytes)
{
for (int i = 0; utf8_invalid_tests[i][0] != nullptr; i++) {
@@ -277,7 +277,7 @@ TEST(string, Utf8InvalidBytes)
char buff[80];
memcpy(buff, tst, sizeof(buff));
- const int num_errors_found = BLI_utf8_invalid_strip(buff, sizeof(buff) - 1);
+ const int num_errors_found = BLI_str_utf8_invalid_strip(buff, sizeof(buff) - 1);
printf("[%02d] -> [%02d] \"%s\" -> \"%s\"\n", num_errors, num_errors_found, tst, buff);
EXPECT_EQ(num_errors_found, num_errors);
diff --git a/source/blender/blenloader/BLO_undofile.h b/source/blender/blenloader/BLO_undofile.h
index fc41a6e832f..4e240e2462b 100644
--- a/source/blender/blenloader/BLO_undofile.h
+++ b/source/blender/blenloader/BLO_undofile.h
@@ -24,6 +24,8 @@
* \ingroup blenloader
*/
+#include "BLI_filereader.h"
+
struct GHash;
struct Scene;
@@ -65,6 +67,16 @@ typedef struct MemFileUndoData {
size_t undo_size;
} MemFileUndoData;
+/* FileReader-compatible wrapper for reading MemFiles */
+typedef struct {
+ FileReader reader;
+
+ MemFile *memfile;
+ int undo_direction;
+
+ bool memchunk_identical;
+} UndoReader;
+
/* actually only used writefile.c */
void BLO_memfile_write_init(MemFileWriteData *mem_data,
@@ -84,3 +96,5 @@ extern struct Main *BLO_memfile_main_get(struct MemFile *memfile,
struct Main *bmain,
struct Scene **r_scene);
extern bool BLO_memfile_write_file(struct MemFile *memfile, const char *filename);
+
+FileReader *BLO_memfile_new_filereader(MemFile *memfile, int undo_direction);
diff --git a/source/blender/blenloader/CMakeLists.txt b/source/blender/blenloader/CMakeLists.txt
index f5baf0dcb83..89631588ed0 100644
--- a/source/blender/blenloader/CMakeLists.txt
+++ b/source/blender/blenloader/CMakeLists.txt
@@ -42,7 +42,7 @@ set(INC
)
set(INC_SYS
- ${ZLIB_INCLUDE_DIRS}
+ ${ZSTD_INCLUDE_DIRS}
)
set(SRC
diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c
index e48c305fc4b..49c3497f996 100644
--- a/source/blender/blenloader/intern/readfile.c
+++ b/source/blender/blenloader/intern/readfile.c
@@ -21,8 +21,6 @@
* \ingroup blenloader
*/
-#include "zlib.h"
-
#include <ctype.h> /* for isdigit. */
#include <fcntl.h> /* for open flags (O_BINARY, O_RDONLY). */
#include <limits.h>
@@ -71,7 +69,6 @@
#include "BLI_math.h"
#include "BLI_memarena.h"
#include "BLI_mempool.h"
-#include "BLI_mmap.h"
#include "BLI_threads.h"
#include "PIL_time.h"
@@ -788,7 +785,7 @@ static BHeadN *get_bhead(FileData *fd)
*/
if (fd->flags & FD_FLAGS_FILE_POINTSIZE_IS_4) {
bhead4.code = DATA;
- readsize = fd->read(fd, &bhead4, sizeof(bhead4), NULL);
+ readsize = fd->file->read(fd->file, &bhead4, sizeof(bhead4));
if (readsize == sizeof(bhead4) || bhead4.code == ENDB) {
if (fd->flags & FD_FLAGS_SWITCH_ENDIAN) {
@@ -811,7 +808,7 @@ static BHeadN *get_bhead(FileData *fd)
}
else {
bhead8.code = DATA;
- readsize = fd->read(fd, &bhead8, sizeof(bhead8), NULL);
+ readsize = fd->file->read(fd->file, &bhead8, sizeof(bhead8));
if (readsize == sizeof(bhead8) || bhead8.code == ENDB) {
if (fd->flags & FD_FLAGS_SWITCH_ENDIAN) {
@@ -845,22 +842,22 @@ static BHeadN *get_bhead(FileData *fd)
/* pass */
}
#ifdef USE_BHEAD_READ_ON_DEMAND
- else if (fd->seek != NULL && BHEAD_USE_READ_ON_DEMAND(&bhead)) {
+ else if (fd->file->seek != NULL && BHEAD_USE_READ_ON_DEMAND(&bhead)) {
/* Delay reading bhead content. */
new_bhead = MEM_mallocN(sizeof(BHeadN), "new_bhead");
if (new_bhead) {
new_bhead->next = new_bhead->prev = NULL;
- new_bhead->file_offset = fd->file_offset;
+ new_bhead->file_offset = fd->file->offset;
new_bhead->has_data = false;
new_bhead->is_memchunk_identical = false;
new_bhead->bhead = bhead;
- off64_t seek_new = fd->seek(fd, bhead.len, SEEK_CUR);
+ off64_t seek_new = fd->file->seek(fd->file, bhead.len, SEEK_CUR);
if (seek_new == -1) {
fd->is_eof = true;
MEM_freeN(new_bhead);
new_bhead = NULL;
}
- BLI_assert(fd->file_offset == seek_new);
+ BLI_assert(fd->file->offset == seek_new);
}
else {
fd->is_eof = true;
@@ -878,14 +875,17 @@ static BHeadN *get_bhead(FileData *fd)
new_bhead->is_memchunk_identical = false;
new_bhead->bhead = bhead;
- readsize = fd->read(
- fd, new_bhead + 1, (size_t)bhead.len, &new_bhead->is_memchunk_identical);
+ readsize = fd->file->read(fd->file, new_bhead + 1, (size_t)bhead.len);
- if (readsize != (ssize_t)bhead.len) {
+ if (readsize != bhead.len) {
fd->is_eof = true;
MEM_freeN(new_bhead);
new_bhead = NULL;
}
+
+ if (fd->flags & FD_FLAGS_IS_MEMFILE) {
+ new_bhead->is_memchunk_identical = ((UndoReader *)fd->file)->memchunk_identical;
+ }
}
else {
fd->is_eof = true;
@@ -964,17 +964,19 @@ static bool blo_bhead_read_data(FileData *fd, BHead *thisblock, void *buf)
bool success = true;
BHeadN *new_bhead = BHEADN_FROM_BHEAD(thisblock);
BLI_assert(new_bhead->has_data == false && new_bhead->file_offset != 0);
- off64_t offset_backup = fd->file_offset;
- if (UNLIKELY(fd->seek(fd, new_bhead->file_offset, SEEK_SET) == -1)) {
+ off64_t offset_backup = fd->file->offset;
+ if (UNLIKELY(fd->file->seek(fd->file, new_bhead->file_offset, SEEK_SET) == -1)) {
success = false;
}
else {
- if (fd->read(fd, buf, (size_t)new_bhead->bhead.len, &new_bhead->is_memchunk_identical) !=
- (ssize_t)new_bhead->bhead.len) {
+ if (fd->file->read(fd->file, buf, (size_t)new_bhead->bhead.len) != new_bhead->bhead.len) {
success = false;
}
+ if (fd->flags & FD_FLAGS_IS_MEMFILE) {
+ new_bhead->is_memchunk_identical = ((UndoReader *)fd->file)->memchunk_identical;
+ }
}
- if (fd->seek(fd, offset_backup, SEEK_SET) == -1) {
+ if (fd->file->seek(fd->file, offset_backup, SEEK_SET) == -1) {
success = false;
}
return success;
@@ -1017,7 +1019,7 @@ static void decode_blender_header(FileData *fd)
ssize_t readsize;
/* read in the header data */
- readsize = fd->read(fd, header, sizeof(header), NULL);
+ readsize = fd->file->read(fd->file, header, sizeof(header));
if (readsize == sizeof(header) && STREQLEN(header, "BLENDER", 7) && ELEM(header[7], '_', '-') &&
ELEM(header[8], 'v', 'V') &&
@@ -1147,210 +1149,12 @@ static int *read_file_thumbnail(FileData *fd)
/** \} */
-/* -------------------------------------------------------------------- */
-/** \name File Data API
- * \{ */
-
-/* Regular file reading. */
-
-static ssize_t fd_read_data_from_file(FileData *filedata,
- void *buffer,
- size_t size,
- bool *UNUSED(r_is_memchunck_identical))
-{
- ssize_t readsize = read(filedata->filedes, buffer, size);
-
- if (readsize < 0) {
- readsize = EOF;
- }
- else {
- filedata->file_offset += readsize;
- }
-
- return readsize;
-}
-
-static off64_t fd_seek_data_from_file(FileData *filedata, off64_t offset, int whence)
-{
- filedata->file_offset = BLI_lseek(filedata->filedes, offset, whence);
- return filedata->file_offset;
-}
-
-/* GZip file reading. */
-
-static ssize_t fd_read_gzip_from_file(FileData *filedata,
- void *buffer,
- size_t size,
- bool *UNUSED(r_is_memchunck_identical))
-{
- BLI_assert(size <= INT_MAX);
-
- ssize_t readsize = gzread(filedata->gzfiledes, buffer, (uint)size);
-
- if (readsize < 0) {
- readsize = EOF;
- }
- else {
- filedata->file_offset += readsize;
- }
-
- return readsize;
-}
-
-/* Memory reading. */
-
-static ssize_t fd_read_from_memory(FileData *filedata,
- void *buffer,
- size_t size,
- bool *UNUSED(r_is_memchunck_identical))
-{
- /* don't read more bytes than there are available in the buffer */
- ssize_t readsize = (ssize_t)MIN2(size, filedata->buffersize - (size_t)filedata->file_offset);
-
- memcpy(buffer, filedata->buffer + filedata->file_offset, (size_t)readsize);
- filedata->file_offset += readsize;
-
- return readsize;
-}
-
-/* Memory-mapped file reading.
- * By using mmap(), we can map a file so that it can be treated like normal memory,
- * meaning that we can just read from it with memcpy() etc.
- * This avoids system call overhead and can significantly speed up file loading.
- */
-
-static ssize_t fd_read_from_mmap(FileData *filedata,
- void *buffer,
- size_t size,
- bool *UNUSED(r_is_memchunck_identical))
-{
- /* don't read more bytes than there are available in the buffer */
- size_t readsize = MIN2(size, (size_t)(filedata->buffersize - filedata->file_offset));
-
- if (!BLI_mmap_read(filedata->mmap_file, buffer, filedata->file_offset, readsize)) {
- return 0;
- }
-
- filedata->file_offset += readsize;
-
- return readsize;
-}
-
-static off64_t fd_seek_from_mmap(FileData *filedata, off64_t offset, int whence)
-{
- off64_t new_pos;
- if (whence == SEEK_CUR) {
- new_pos = filedata->file_offset + offset;
- }
- else if (whence == SEEK_SET) {
- new_pos = offset;
- }
- else if (whence == SEEK_END) {
- new_pos = filedata->buffersize + offset;
- }
- else {
- return -1;
- }
-
- if (new_pos < 0 || new_pos > filedata->buffersize) {
- return -1;
- }
-
- filedata->file_offset = new_pos;
- return filedata->file_offset;
-}
-
-/* MemFile reading. */
-
-static ssize_t fd_read_from_memfile(FileData *filedata,
- void *buffer,
- size_t size,
- bool *r_is_memchunck_identical)
-{
- static size_t seek = SIZE_MAX; /* the current position */
- static size_t offset = 0; /* size of previous chunks */
- static MemFileChunk *chunk = NULL;
- size_t chunkoffset, readsize, totread;
-
- if (r_is_memchunck_identical != NULL) {
- *r_is_memchunck_identical = true;
- }
-
- if (size == 0) {
- return 0;
- }
-
- if (seek != (size_t)filedata->file_offset) {
- chunk = filedata->memfile->chunks.first;
- seek = 0;
-
- while (chunk) {
- if (seek + chunk->size > (size_t)filedata->file_offset) {
- break;
- }
- seek += chunk->size;
- chunk = chunk->next;
- }
- offset = seek;
- seek = (size_t)filedata->file_offset;
- }
-
- if (chunk) {
- totread = 0;
-
- do {
- /* first check if it's on the end if current chunk */
- if (seek - offset == chunk->size) {
- offset += chunk->size;
- chunk = chunk->next;
- }
-
- /* debug, should never happen */
- if (chunk == NULL) {
- CLOG_ERROR(&LOG, "Illegal read, got a NULL chunk");
- return 0;
- }
-
- chunkoffset = seek - offset;
- readsize = size - totread;
-
- /* data can be spread over multiple chunks, so clamp size
- * to within this chunk, and then it will read further in
- * the next chunk */
- if (chunkoffset + readsize > chunk->size) {
- readsize = chunk->size - chunkoffset;
- }
-
- memcpy(POINTER_OFFSET(buffer, totread), chunk->buf + chunkoffset, readsize);
- totread += readsize;
- filedata->file_offset += readsize;
- seek += readsize;
- if (r_is_memchunck_identical != NULL) {
- /* `is_identical` of current chunk represents whether it changed compared to previous undo
- * step. this is fine in redo case, but not in undo case, where we need an extra flag
- * defined when saving the next (future) step after the one we want to restore, as we are
- * supposed to 'come from' that future undo step, and not the one before current one. */
- *r_is_memchunck_identical &= filedata->undo_direction == STEP_REDO ?
- chunk->is_identical :
- chunk->is_identical_future;
- }
- } while (totread < size);
-
- return (ssize_t)totread;
- }
-
- return 0;
-}
-
static FileData *filedata_new(BlendFileReadReport *reports)
{
BLI_assert(reports != NULL);
FileData *fd = MEM_callocN(sizeof(FileData), "FileData");
- fd->filedes = -1;
- fd->gzfiledes = NULL;
-
fd->memsdna = DNA_sdna_current_get();
fd->datamap = oldnewmap_new();
@@ -1387,78 +1191,66 @@ static FileData *blo_decode_and_check(FileData *fd, ReportList *reports)
static FileData *blo_filedata_from_file_descriptor(const char *filepath,
BlendFileReadReport *reports,
- int file)
+ int filedes)
{
- FileDataReadFn *read_fn = NULL;
- FileDataSeekFn *seek_fn = NULL; /* Optional. */
- size_t buffersize = 0;
- BLI_mmap_file *mmap_file = NULL;
-
- gzFile gzfile = (gzFile)Z_NULL;
-
char header[7];
+ FileReader *rawfile = BLI_filereader_new_file(filedes);
+ FileReader *file = NULL;
- /* Regular file. */
errno = 0;
- if (read(file, header, sizeof(header)) != sizeof(header)) {
+ /* If opening the file failed or we can't read the header, give up. */
+ if (rawfile == NULL || rawfile->read(rawfile, header, sizeof(header)) != sizeof(header)) {
BKE_reportf(reports->reports,
RPT_WARNING,
"Unable to read '%s': %s",
filepath,
errno ? strerror(errno) : TIP_("insufficient content"));
+ if (rawfile) {
+ rawfile->close(rawfile);
+ }
+ else {
+ close(filedes);
+ }
return NULL;
}
- /* Regular file. */
- if (memcmp(header, "BLENDER", sizeof(header)) == 0) {
- read_fn = fd_read_data_from_file;
- seek_fn = fd_seek_data_from_file;
+ /* Rewind the file after reading the header. */
+ rawfile->seek(rawfile, 0, SEEK_SET);
- mmap_file = BLI_mmap_open(file);
- if (mmap_file != NULL) {
- read_fn = fd_read_from_mmap;
- seek_fn = fd_seek_from_mmap;
- buffersize = BLI_lseek(file, 0, SEEK_END);
+ /* Check if we have a regular file. */
+ if (memcmp(header, "BLENDER", sizeof(header)) == 0) {
+ /* Try opening the file with memory-mapped IO. */
+ file = BLI_filereader_new_mmap(filedes);
+ if (file == NULL) {
+ /* mmap failed, so just keep using rawfile. */
+ file = rawfile;
+ rawfile = NULL;
}
}
-
- BLI_lseek(file, 0, SEEK_SET);
-
- /* Gzip file. */
- errno = 0;
- if ((read_fn == NULL) &&
- /* Check header magic. */
- (header[0] == 0x1f && header[1] == 0x8b)) {
- gzfile = BLI_gzopen(filepath, "rb");
- if (gzfile == (gzFile)Z_NULL) {
- BKE_reportf(reports->reports,
- RPT_WARNING,
- "Unable to open '%s': %s",
- filepath,
- errno ? strerror(errno) : TIP_("unknown error reading file"));
- return NULL;
+ else if (BLI_file_magic_is_gzip(header)) {
+ file = BLI_filereader_new_gzip(rawfile);
+ if (file != NULL) {
+ rawfile = NULL; /* The Gzip FileReader takes ownership of `rawfile`. */
+ }
+ }
+ else if (BLI_file_magic_is_zstd(header)) {
+ file = BLI_filereader_new_zstd(rawfile);
+ if (file != NULL) {
+ rawfile = NULL; /* The Zstd FileReader takes ownership of `rawfile`. */
}
-
- /* 'seek_fn' is too slow for gzip, don't set it. */
- read_fn = fd_read_gzip_from_file;
- /* Caller must close. */
- file = -1;
}
- if (read_fn == NULL) {
+ /* Clean up `rawfile` if it wasn't taken over. */
+ if (rawfile != NULL) {
+ rawfile->close(rawfile);
+ }
+ if (file == NULL) {
BKE_reportf(reports->reports, RPT_WARNING, "Unrecognized file format '%s'", filepath);
return NULL;
}
FileData *fd = filedata_new(reports);
-
- fd->filedes = file;
- fd->gzfiledes = gzfile;
-
- fd->read = read_fn;
- fd->seek = seek_fn;
- fd->mmap_file = mmap_file;
- fd->buffersize = buffersize;
+ fd->file = file;
return fd;
}
@@ -1475,11 +1267,7 @@ static FileData *blo_filedata_from_file_open(const char *filepath, BlendFileRead
errno ? strerror(errno) : TIP_("unknown error reading file"));
return NULL;
}
- FileData *fd = blo_filedata_from_file_descriptor(filepath, reports, file);
- if ((fd == NULL) || (fd->filedes == -1)) {
- close(file);
- }
- return fd;
+ return blo_filedata_from_file_descriptor(filepath, reports, file);
}
/* cannot be called with relative paths anymore! */
@@ -1513,50 +1301,6 @@ static FileData *blo_filedata_from_file_minimal(const char *filepath)
return NULL;
}
-static ssize_t fd_read_gzip_from_memory(FileData *filedata,
- void *buffer,
- size_t size,
- bool *UNUSED(r_is_memchunck_identical))
-{
- int err;
-
- filedata->strm.next_out = (Bytef *)buffer;
- filedata->strm.avail_out = (uint)size;
-
- /* Inflate another chunk. */
- err = inflate(&filedata->strm, Z_SYNC_FLUSH);
-
- if (err == Z_STREAM_END) {
- return 0;
- }
- if (err != Z_OK) {
- CLOG_ERROR(&LOG, "ZLib error (code %d)", err);
- return 0;
- }
-
- filedata->file_offset += size;
-
- return (ssize_t)size;
-}
-
-static int fd_read_gzip_from_memory_init(FileData *fd)
-{
-
- fd->strm.next_in = (Bytef *)fd->buffer;
- fd->strm.avail_in = fd->buffersize;
- fd->strm.total_out = 0;
- fd->strm.zalloc = Z_NULL;
- fd->strm.zfree = Z_NULL;
-
- if (inflateInit2(&fd->strm, (16 + MAX_WBITS)) != Z_OK) {
- return 0;
- }
-
- fd->read = fd_read_gzip_from_memory;
-
- return 1;
-}
-
FileData *blo_filedata_from_memory(const void *mem, int memsize, BlendFileReadReport *reports)
{
if (!mem || memsize < SIZEOFBLENDERHEADER) {
@@ -1565,24 +1309,24 @@ FileData *blo_filedata_from_memory(const void *mem, int memsize, BlendFileReadRe
return NULL;
}
- FileData *fd = filedata_new(reports);
- const char *cp = mem;
-
- fd->buffer = mem;
- fd->buffersize = memsize;
+ FileReader *mem_file = BLI_filereader_new_memory(mem, memsize);
+ FileReader *file = mem_file;
- /* test if gzip */
- if (cp[0] == 0x1f && cp[1] == 0x8b) {
- if (0 == fd_read_gzip_from_memory_init(fd)) {
- blo_filedata_free(fd);
- return NULL;
- }
+ if (BLI_file_magic_is_gzip(mem)) {
+ file = BLI_filereader_new_gzip(mem_file);
}
- else {
- fd->read = fd_read_from_memory;
+ else if (BLI_file_magic_is_zstd(mem)) {
+ file = BLI_filereader_new_zstd(mem_file);
}
- fd->flags |= FD_FLAGS_NOT_MY_BUFFER;
+ if (file == NULL) {
+ /* Compression initialization failed. */
+ mem_file->close(mem_file);
+ return NULL;
+ }
+
+ FileData *fd = filedata_new(reports);
+ fd->file = file;
return blo_decode_and_check(fd, reports->reports);
}
@@ -1597,11 +1341,9 @@ FileData *blo_filedata_from_memfile(MemFile *memfile,
}
FileData *fd = filedata_new(reports);
- fd->memfile = memfile;
+ fd->file = BLO_memfile_new_filereader(memfile, params->undo_direction);
fd->undo_direction = params->undo_direction;
-
- fd->read = fd_read_from_memfile;
- fd->flags |= FD_FLAGS_NOT_MY_BUFFER;
+ fd->flags |= FD_FLAGS_IS_MEMFILE;
return blo_decode_and_check(fd, reports->reports);
}
@@ -1609,30 +1351,7 @@ FileData *blo_filedata_from_memfile(MemFile *memfile,
void blo_filedata_free(FileData *fd)
{
if (fd) {
- if (fd->filedes != -1) {
- close(fd->filedes);
- }
-
- if (fd->gzfiledes != NULL) {
- gzclose(fd->gzfiledes);
- }
-
- if (fd->strm.next_in) {
- int err = inflateEnd(&fd->strm);
- if (err != Z_OK) {
- CLOG_ERROR(&LOG, "Close gzip stream error (code %d)", err);
- }
- }
-
- if (fd->buffer && !(fd->flags & FD_FLAGS_NOT_MY_BUFFER)) {
- MEM_freeN((void *)fd->buffer);
- fd->buffer = NULL;
- }
-
- if (fd->mmap_file) {
- BLI_mmap_free(fd->mmap_file);
- fd->mmap_file = NULL;
- }
+ fd->file->close(fd->file);
/* Free all BHeadN data blocks */
#ifndef NDEBUG
@@ -1640,7 +1359,7 @@ void blo_filedata_free(FileData *fd)
#else
/* Sanity check we're not keeping memory we don't need. */
LISTBASE_FOREACH_MUTABLE (BHeadN *, new_bhead, &fd->bhead_list) {
- if (fd->seek != NULL && BHEAD_USE_READ_ON_DEMAND(&new_bhead->bhead)) {
+ if (fd->file->seek != NULL && BHEAD_USE_READ_ON_DEMAND(&new_bhead->bhead)) {
BLI_assert(new_bhead->has_data == 0);
}
MEM_freeN(new_bhead);
@@ -2096,7 +1815,7 @@ static void blo_cache_storage_entry_clear_in_old(ID *UNUSED(id),
void blo_cache_storage_init(FileData *fd, Main *bmain)
{
- if (fd->memfile != NULL) {
+ if (fd->flags & FD_FLAGS_IS_MEMFILE) {
BLI_assert(fd->cache_storage == NULL);
fd->cache_storage = MEM_mallocN(sizeof(*fd->cache_storage), __func__);
fd->cache_storage->memarena = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__);
@@ -2261,7 +1980,7 @@ static void *read_struct(FileData *fd, BHead *bh, const char *blockname)
* undo since DNA must match. */
static const void *peek_struct_undo(FileData *fd, BHead *bhead)
{
- BLI_assert(fd->memfile != NULL);
+ BLI_assert(fd->flags & FD_FLAGS_IS_MEMFILE);
UNUSED_VARS_NDEBUG(fd);
return (bhead->len) ? (const void *)(bhead + 1) : NULL;
}
@@ -3679,7 +3398,7 @@ static BHead *read_libblock(FileData *fd,
* When datablocks are changed but still exist, we restore them at the old
* address and inherit recalc flags for the dependency graph. */
ID *id_old = NULL;
- if (fd->memfile != NULL) {
+ if (fd->flags & FD_FLAGS_IS_MEMFILE) {
if (read_libblock_undo_restore(fd, main, bhead, tag, &id_old)) {
if (r_id) {
*r_id = id_old;
@@ -3980,13 +3699,14 @@ static void lib_link_all(FileData *fd, Main *bmain)
continue;
}
- if (fd->memfile != NULL && GS(id->name) == ID_WM) {
+ if ((fd->flags & FD_FLAGS_IS_MEMFILE) && GS(id->name) == ID_WM) {
/* No load UI for undo memfiles.
* Only WM currently, SCR needs it still (see below), and so does WS? */
continue;
}
- if (fd->memfile != NULL && do_partial_undo && (id->tag & LIB_TAG_UNDO_OLD_ID_REUSED) != 0) {
+ if ((fd->flags & FD_FLAGS_IS_MEMFILE) && do_partial_undo &&
+ (id->tag & LIB_TAG_UNDO_OLD_ID_REUSED) != 0) {
/* This ID has been re-used from 'old' bmain. Since it was therefore unchanged across
* current undo step, and old IDs re-use their old memory address, we do not need to liblink
* it at all. */
@@ -4165,7 +3885,7 @@ BlendFileData *blo_read_file_internal(FileData *fd, const char *filepath)
BlendFileData *bfd;
ListBase mainlist = {NULL, NULL};
- if (fd->memfile != NULL) {
+ if (fd->flags & FD_FLAGS_IS_MEMFILE) {
CLOG_INFO(&LOG_UNDO, 2, "UNDO: read step");
}
@@ -4256,7 +3976,7 @@ BlendFileData *blo_read_file_internal(FileData *fd, const char *filepath)
}
/* do before read_libraries, but skip undo case */
- if (fd->memfile == NULL) {
+ if ((fd->flags & FD_FLAGS_IS_MEMFILE) == 0) {
if ((fd->skip_flags & BLO_READ_SKIP_DATA) == 0) {
do_versions(fd, NULL, bfd->main);
}
@@ -4278,7 +3998,7 @@ BlendFileData *blo_read_file_internal(FileData *fd, const char *filepath)
fd->reports->duration.libraries = PIL_check_seconds_timer() - fd->reports->duration.libraries;
/* Skip in undo case. */
- if (fd->memfile == NULL) {
+ if ((fd->flags & FD_FLAGS_IS_MEMFILE) == 0) {
/* Note that we can't recompute user-counts at this point in undo case, we play too much with
* IDs from different memory realms, and Main database is not in a fully valid state yet.
*/
@@ -4311,7 +4031,7 @@ BlendFileData *blo_read_file_internal(FileData *fd, const char *filepath)
/* Now that all our data-blocks are loaded,
* we can re-generate overrides from their references. */
- if (fd->memfile == NULL) {
+ if ((fd->flags & FD_FLAGS_IS_MEMFILE) == 0) {
/* Do not apply in undo case! */
fd->reports->duration.lib_overrides = PIL_check_seconds_timer();
@@ -4391,7 +4111,7 @@ static void sort_bhead_old_map(FileData *fd)
static BHead *find_previous_lib(FileData *fd, BHead *bhead)
{
/* Skip library data-blocks in undo, see comment in read_libblock. */
- if (fd->memfile) {
+ if (fd->flags & FD_FLAGS_IS_MEMFILE) {
return NULL;
}
@@ -5850,7 +5570,7 @@ void BLO_read_pointer_array(BlendDataReader *reader, void **ptr_p)
bool BLO_read_data_is_undo(BlendDataReader *reader)
{
- return reader->fd->memfile != NULL;
+ return (reader->fd->flags & FD_FLAGS_IS_MEMFILE);
}
void BLO_read_data_globmap_add(BlendDataReader *reader, void *oldaddr, void *newaddr)
@@ -5870,7 +5590,7 @@ BlendFileReadReport *BLO_read_data_reports(BlendDataReader *reader)
bool BLO_read_lib_is_undo(BlendLibReader *reader)
{
- return reader->fd->memfile != NULL;
+ return (reader->fd->flags & FD_FLAGS_IS_MEMFILE);
}
Main *BLO_read_lib_get_main(BlendLibReader *reader)
diff --git a/source/blender/blenloader/intern/readfile.h b/source/blender/blenloader/intern/readfile.h
index b04043f9641..beeed8e45ae 100644
--- a/source/blender/blenloader/intern/readfile.h
+++ b/source/blender/blenloader/intern/readfile.h
@@ -28,10 +28,10 @@
# include "BLI_winstuff.h"
#endif
+#include "BLI_filereader.h"
#include "DNA_sdna_types.h"
#include "DNA_space_types.h"
#include "DNA_windowmanager_types.h" /* for ReportType */
-#include "zlib.h"
struct BLI_mmap_file;
struct BLOCacheStorage;
@@ -50,7 +50,7 @@ enum eFileDataFlag {
FD_FLAGS_FILE_POINTSIZE_IS_4 = 1 << 1,
FD_FLAGS_POINTSIZE_DIFFERS = 1 << 2,
FD_FLAGS_FILE_OK = 1 << 3,
- FD_FLAGS_NOT_MY_BUFFER = 1 << 4,
+ FD_FLAGS_IS_MEMFILE = 1 << 4,
/* XXX Unused in practice (checked once but never set). */
FD_FLAGS_NOT_MY_LIBMAP = 1 << 5,
};
@@ -60,44 +60,18 @@ enum eFileDataFlag {
# pragma GCC poison off_t
#endif
-#if defined(_MSC_VER) || defined(__APPLE__) || defined(__HAIKU__) || defined(__NetBSD__)
-typedef int64_t off64_t;
-#endif
-
-typedef ssize_t(FileDataReadFn)(struct FileData *filedata,
- void *buffer,
- size_t size,
- bool *r_is_memchunk_identical);
-typedef off64_t(FileDataSeekFn)(struct FileData *filedata, off64_t offset, int whence);
-
typedef struct FileData {
/** Linked list of BHeadN's. */
ListBase bhead_list;
enum eFileDataFlag flags;
bool is_eof;
- size_t buffersize;
- off64_t file_offset;
- FileDataReadFn *read;
- FileDataSeekFn *seek;
+ FileReader *file;
- /** Regular file reading. */
- int filedes;
-
- /** Variables needed for reading from memory / stream / memory-mapped files. */
- const char *buffer;
- struct BLI_mmap_file *mmap_file;
- /** Variables needed for reading from memfile (undo). */
- struct MemFile *memfile;
/** Whether we are undoing (< 0) or redoing (> 0), used to choose which 'unchanged' flag to use
* to detect unchanged data from memfile. */
int undo_direction; /* eUndoStepDir */
- /** Variables needed for reading from file. */
- gzFile gzfiledes;
- /** Gzip stream for memory decompression. */
- z_stream strm;
-
/** Now only in use for library appending. */
char relabase[FILE_MAX];
diff --git a/source/blender/blenloader/intern/undofile.c b/source/blender/blenloader/intern/undofile.c
index 2eeeac2e8d7..62072cf7df5 100644
--- a/source/blender/blenloader/intern/undofile.c
+++ b/source/blender/blenloader/intern/undofile.c
@@ -48,6 +48,7 @@
#include "BKE_lib_id.h"
#include "BKE_main.h"
+#include "BKE_undo_system.h"
/* keep last */
#include "BLI_strict_flags.h"
@@ -273,3 +274,97 @@ bool BLO_memfile_write_file(struct MemFile *memfile, const char *filename)
}
return true;
}
+
+static ssize_t undo_read(FileReader *reader, void *buffer, size_t size)
+{
+ UndoReader *undo = (UndoReader *)reader;
+
+ static size_t seek = SIZE_MAX; /* The current position. */
+ static size_t offset = 0; /* Size of previous chunks. */
+ static MemFileChunk *chunk = NULL;
+ size_t chunkoffset, readsize, totread;
+
+ undo->memchunk_identical = true;
+
+ if (size == 0) {
+ return 0;
+ }
+
+ if (seek != (size_t)undo->reader.offset) {
+ chunk = undo->memfile->chunks.first;
+ seek = 0;
+
+ while (chunk) {
+ if (seek + chunk->size > (size_t)undo->reader.offset) {
+ break;
+ }
+ seek += chunk->size;
+ chunk = chunk->next;
+ }
+ offset = seek;
+ seek = (size_t)undo->reader.offset;
+ }
+
+ if (chunk) {
+ totread = 0;
+
+ do {
+ /* First check if it's on the end if current chunk. */
+ if (seek - offset == chunk->size) {
+ offset += chunk->size;
+ chunk = chunk->next;
+ }
+
+ /* Debug, should never happen. */
+ if (chunk == NULL) {
+ printf("illegal read, chunk zero\n");
+ return 0;
+ }
+
+ chunkoffset = seek - offset;
+ readsize = size - totread;
+
+ /* Data can be spread over multiple chunks, so clamp size
+ * to within this chunk, and then it will read further in
+ * the next chunk. */
+ if (chunkoffset + readsize > chunk->size) {
+ readsize = chunk->size - chunkoffset;
+ }
+
+ memcpy(POINTER_OFFSET(buffer, totread), chunk->buf + chunkoffset, readsize);
+ totread += readsize;
+ undo->reader.offset += (off64_t)readsize;
+ seek += readsize;
+
+ /* `is_identical` of current chunk represents whether it changed compared to previous undo
+ * step. this is fine in redo case, but not in undo case, where we need an extra flag
+ * defined when saving the next (future) step after the one we want to restore, as we are
+ * supposed to 'come from' that future undo step, and not the one before current one. */
+ undo->memchunk_identical &= undo->undo_direction == STEP_REDO ? chunk->is_identical :
+ chunk->is_identical_future;
+ } while (totread < size);
+
+ return (ssize_t)totread;
+ }
+
+ return 0;
+}
+
+static void undo_close(FileReader *reader)
+{
+ MEM_freeN(reader);
+}
+
+FileReader *BLO_memfile_new_filereader(MemFile *memfile, int undo_direction)
+{
+ UndoReader *undo = MEM_callocN(sizeof(UndoReader), __func__);
+
+ undo->memfile = memfile;
+ undo->undo_direction = undo_direction;
+
+ undo->reader.read = undo_read;
+ undo->reader.seek = NULL;
+ undo->reader.close = undo_close;
+
+ return (FileReader *)undo;
+}
diff --git a/source/blender/blenloader/intern/versioning_250.c b/source/blender/blenloader/intern/versioning_250.c
index e56c1995363..436645c2241 100644
--- a/source/blender/blenloader/intern/versioning_250.c
+++ b/source/blender/blenloader/intern/versioning_250.c
@@ -23,8 +23,7 @@
#else
# include "BLI_winstuff.h"
# include "winsock2.h"
-# include <io.h> /* for open close read */
-# include <zlib.h> /* odd include order-issue */
+# include <io.h> /* for open close read */
#endif
/* allow readfile to use deprecated functionality */
diff --git a/source/blender/blenloader/intern/versioning_legacy.c b/source/blender/blenloader/intern/versioning_legacy.c
index 81371e1c1ed..6ba27b6ee9e 100644
--- a/source/blender/blenloader/intern/versioning_legacy.c
+++ b/source/blender/blenloader/intern/versioning_legacy.c
@@ -28,8 +28,7 @@
#else
# include "BLI_winstuff.h"
# include "winsock2.h"
-# include <io.h> /* for open close read */
-# include <zlib.h> /* odd include order-issue */
+# include <io.h> /* for open close read */
#endif
/* allow readfile to use deprecated functionality */
diff --git a/source/blender/blenloader/intern/writefile.c b/source/blender/blenloader/intern/writefile.c
index 99246603e9a..90d58514eb5 100644
--- a/source/blender/blenloader/intern/writefile.c
+++ b/source/blender/blenloader/intern/writefile.c
@@ -83,7 +83,6 @@
# include "BLI_winstuff.h"
# include "winsock2.h"
# include <io.h>
-# include <zlib.h> /* odd include order-issue */
#else
# include <unistd.h> /* FreeBSD, for write() and close(). */
#endif
@@ -101,7 +100,12 @@
#include "BLI_bitmap.h"
#include "BLI_blenlib.h"
#include "BLI_endian_defines.h"
+#include "BLI_endian_switch.h"
+#include "BLI_link_utils.h"
+#include "BLI_linklist.h"
+#include "BLI_math_base.h"
#include "BLI_mempool.h"
+#include "BLI_threads.h"
#include "MEM_guardedalloc.h" /* MEM_freeN */
#include "BKE_blender_version.h"
@@ -129,14 +133,21 @@
#include <errno.h>
+#include <zstd.h>
+
/* Make preferences read-only. */
#define U (*((const UserDef *)&U))
/* ********* my write, buffered writing with minimum size chunks ************ */
/* Use optimal allocation since blocks of this size are kept in memory for undo. */
-#define MYWRITE_BUFFER_SIZE (MEM_SIZE_OPTIMAL(1 << 17)) /* 128kb */
-#define MYWRITE_MAX_CHUNK (MEM_SIZE_OPTIMAL(1 << 15)) /* ~32kb */
+#define MEM_BUFFER_SIZE (MEM_SIZE_OPTIMAL(1 << 17)) /* 128kb */
+#define MEM_CHUNK_SIZE (MEM_SIZE_OPTIMAL(1 << 15)) /* ~32kb */
+
+#define ZSTD_BUFFER_SIZE (1 << 21) /* 2mb */
+#define ZSTD_CHUNK_SIZE (1 << 20) /* 1mb */
+
+#define ZSTD_COMPRESSION_LEVEL 3
/** Use if we want to store how many bytes have been written to the file. */
// #define USE_WRITE_DATA_LEN
@@ -147,9 +158,16 @@
typedef enum {
WW_WRAP_NONE = 1,
- WW_WRAP_ZLIB,
+ WW_WRAP_ZSTD,
} eWriteWrapType;
+typedef struct ZstdFrame {
+ struct ZstdFrame *next, *prev;
+
+ uint32_t compressed_size;
+ uint32_t uncompressed_size;
+} ZstdFrame;
+
typedef struct WriteWrap WriteWrap;
struct WriteWrap {
/* callbacks */
@@ -161,15 +179,23 @@ struct WriteWrap {
bool use_buf;
/* internal */
- union {
- int file_handle;
- gzFile gz_handle;
- } _user_data;
+ int file_handle;
+ struct {
+ ListBase threadpool;
+ ListBase tasks;
+ ThreadMutex mutex;
+ ThreadCondition condition;
+ int next_frame;
+ int num_frames;
+
+ int level;
+ ListBase frames;
+
+ bool write_error;
+ } zstd;
};
/* none */
-#define FILE_HANDLE(ww) (ww)->_user_data.file_handle
-
static bool ww_open_none(WriteWrap *ww, const char *filepath)
{
int file;
@@ -177,7 +203,7 @@ static bool ww_open_none(WriteWrap *ww, const char *filepath)
file = BLI_open(filepath, O_BINARY + O_WRONLY + O_CREAT + O_TRUNC, 0666);
if (file != -1) {
- FILE_HANDLE(ww) = file;
+ ww->file_handle = file;
return true;
}
@@ -185,39 +211,170 @@ static bool ww_open_none(WriteWrap *ww, const char *filepath)
}
static bool ww_close_none(WriteWrap *ww)
{
- return (close(FILE_HANDLE(ww)) != -1);
+ return (close(ww->file_handle) != -1);
}
static size_t ww_write_none(WriteWrap *ww, const char *buf, size_t buf_len)
{
- return write(FILE_HANDLE(ww), buf, buf_len);
+ return write(ww->file_handle, buf, buf_len);
}
-#undef FILE_HANDLE
-/* zlib */
-#define FILE_HANDLE(ww) (ww)->_user_data.gz_handle
+/* zstd */
+
+typedef struct {
+ struct ZstdWriteBlockTask *next, *prev;
+ void *data;
+ size_t size;
+ int frame_number;
+ WriteWrap *ww;
+} ZstdWriteBlockTask;
-static bool ww_open_zlib(WriteWrap *ww, const char *filepath)
+static void *zstd_write_task(void *userdata)
{
- gzFile file;
+ ZstdWriteBlockTask *task = userdata;
+ WriteWrap *ww = task->ww;
- file = BLI_gzopen(filepath, "wb1");
+ size_t out_buf_len = ZSTD_compressBound(task->size);
+ void *out_buf = MEM_mallocN(out_buf_len, "Zstd out buffer");
+ size_t out_size = ZSTD_compress(
+ out_buf, out_buf_len, task->data, task->size, ZSTD_COMPRESSION_LEVEL);
- if (file != Z_NULL) {
- FILE_HANDLE(ww) = file;
- return true;
+ MEM_freeN(task->data);
+
+ BLI_mutex_lock(&ww->zstd.mutex);
+
+ while (ww->zstd.next_frame != task->frame_number) {
+ BLI_condition_wait(&ww->zstd.condition, &ww->zstd.mutex);
}
- return false;
+ if (ZSTD_isError(out_size)) {
+ ww->zstd.write_error = true;
+ }
+ else {
+ if (ww_write_none(ww, out_buf, out_size) == out_size) {
+ ZstdFrame *frameinfo = MEM_mallocN(sizeof(ZstdFrame), "zstd frameinfo");
+ frameinfo->uncompressed_size = task->size;
+ frameinfo->compressed_size = out_size;
+ BLI_addtail(&ww->zstd.frames, frameinfo);
+ }
+ else {
+ ww->zstd.write_error = true;
+ }
+ }
+
+ ww->zstd.next_frame++;
+
+ BLI_mutex_unlock(&ww->zstd.mutex);
+ BLI_condition_notify_all(&ww->zstd.condition);
+
+ MEM_freeN(out_buf);
+ return NULL;
+}
+
+static bool ww_open_zstd(WriteWrap *ww, const char *filepath)
+{
+ if (!ww_open_none(ww, filepath)) {
+ return false;
+ }
+
+ /* Leave one thread open for the main writing logic, unless we only have one HW thread. */
+ int num_threads = max_ii(1, BLI_system_thread_count() - 1);
+ BLI_threadpool_init(&ww->zstd.threadpool, zstd_write_task, num_threads);
+ BLI_mutex_init(&ww->zstd.mutex);
+ BLI_condition_init(&ww->zstd.condition);
+
+ return true;
}
-static bool ww_close_zlib(WriteWrap *ww)
+
+static void zstd_write_u32_le(WriteWrap *ww, uint32_t val)
+{
+#ifdef __BIG_ENDIAN__
+ BLI_endian_switch_uint32(&val);
+#endif
+ ww_write_none(ww, (char *)&val, sizeof(uint32_t));
+}
+
+/* In order to implement efficient seeking when reading the .blend, we append
+ * a skippable frame that encodes information about the other frames present
+ * in the file.
+ * The format here follows the upstream spec for seekable files:
+ * https://github.com/facebook/zstd/blob/master/contrib/seekable_format/zstd_seekable_compression_format.md
+ * If this information is not present in a file (e.g. if it was compressed
+ * with external tools), it can still be opened in Blender, but seeking will
+ * not be supported, so more memory might be needed. */
+static void zstd_write_seekable_frames(WriteWrap *ww)
+{
+ /* Write seek table header (magic number and frame size). */
+ zstd_write_u32_le(ww, 0x184D2A5E);
+
+ /* The actual frame number might not match ww->zstd.num_frames if there was a write error. */
+ const uint32_t num_frames = BLI_listbase_count(&ww->zstd.frames);
+ /* Each frame consists of two u32, so 8 bytes each.
+ * After the frames, a footer containing two u32 and one byte (9 bytes total) is written. */
+ const uint32_t frame_size = num_frames * 8 + 9;
+ zstd_write_u32_le(ww, frame_size);
+
+ /* Write seek table entries. */
+ LISTBASE_FOREACH (ZstdFrame *, frame, &ww->zstd.frames) {
+ zstd_write_u32_le(ww, frame->compressed_size);
+ zstd_write_u32_le(ww, frame->uncompressed_size);
+ }
+
+ /* Write seek table footer (number of frames, option flags and second magic number). */
+ zstd_write_u32_le(ww, num_frames);
+ const char flags = 0; /* We don't store checksums for each frame. */
+ ww_write_none(ww, &flags, 1);
+ zstd_write_u32_le(ww, 0x8F92EAB1);
+}
+
+static bool ww_close_zstd(WriteWrap *ww)
{
- return (gzclose(FILE_HANDLE(ww)) == Z_OK);
+ BLI_threadpool_end(&ww->zstd.threadpool);
+ BLI_freelistN(&ww->zstd.tasks);
+
+ BLI_mutex_end(&ww->zstd.mutex);
+ BLI_condition_end(&ww->zstd.condition);
+
+ zstd_write_seekable_frames(ww);
+ BLI_freelistN(&ww->zstd.frames);
+
+ return ww_close_none(ww) && !ww->zstd.write_error;
}
-static size_t ww_write_zlib(WriteWrap *ww, const char *buf, size_t buf_len)
+
+static size_t ww_write_zstd(WriteWrap *ww, const char *buf, size_t buf_len)
{
- return gzwrite(FILE_HANDLE(ww), buf, buf_len);
+ if (ww->zstd.write_error) {
+ return 0;
+ }
+
+ ZstdWriteBlockTask *task = MEM_mallocN(sizeof(ZstdWriteBlockTask), __func__);
+ task->data = MEM_mallocN(buf_len, __func__);
+ memcpy(task->data, buf, buf_len);
+ task->size = buf_len;
+ task->frame_number = ww->zstd.num_frames++;
+ task->ww = ww;
+
+ BLI_mutex_lock(&ww->zstd.mutex);
+ BLI_addtail(&ww->zstd.tasks, task);
+
+ /* If there's a free worker thread, just push the block into that thread.
+ * Otherwise, we wait for the earliest thread to finish.
+ * We look up the earliest thread while holding the mutex, but release it
+ * before joining the thread to prevent a deadlock. */
+ ZstdWriteBlockTask *first_task = ww->zstd.tasks.first;
+ BLI_mutex_unlock(&ww->zstd.mutex);
+ if (!BLI_available_threads(&ww->zstd.threadpool)) {
+ BLI_threadpool_remove(&ww->zstd.threadpool, first_task);
+
+ /* If the task list was empty before we pushed our task, there should
+ * always be a free thread. */
+ BLI_assert(first_task != task);
+ BLI_remlink(&ww->zstd.tasks, first_task);
+ MEM_freeN(first_task);
+ }
+ BLI_threadpool_insert(&ww->zstd.threadpool, task);
+
+ return buf_len;
}
-#undef FILE_HANDLE
/* --- end compression types --- */
@@ -226,11 +383,11 @@ static void ww_handle_init(eWriteWrapType ww_type, WriteWrap *r_ww)
memset(r_ww, 0, sizeof(*r_ww));
switch (ww_type) {
- case WW_WRAP_ZLIB: {
- r_ww->open = ww_open_zlib;
- r_ww->close = ww_close_zlib;
- r_ww->write = ww_write_zlib;
- r_ww->use_buf = false;
+ case WW_WRAP_ZSTD: {
+ r_ww->open = ww_open_zstd;
+ r_ww->close = ww_close_zstd;
+ r_ww->write = ww_write_zstd;
+ r_ww->use_buf = true;
break;
}
default: {
@@ -252,10 +409,17 @@ static void ww_handle_init(eWriteWrapType ww_type, WriteWrap *r_ww)
typedef struct {
const struct SDNA *sdna;
- /** Use for file and memory writing (fixed size of #MYWRITE_BUFFER_SIZE). */
- uchar *buf;
- /** Number of bytes used in #WriteData.buf (flushed when exceeded). */
- size_t buf_used_len;
+ struct {
+ /** Use for file and memory writing (size stored in max_size). */
+ uchar *buf;
+ /** Number of bytes used in #WriteData.buf (flushed when exceeded). */
+ size_t used_len;
+
+ /** Maximum size of the buffer. */
+ size_t max_size;
+ /** Threshold above which writes get their own chunk. */
+ size_t chunk_size;
+ } buffer;
#ifdef USE_WRITE_DATA_LEN
/** Total number of bytes written. */
@@ -271,7 +435,7 @@ typedef struct {
bool use_memfile;
/**
- * Wrap writing, so we can use zlib or
+ * Wrap writing, so we can use zstd or
* other compression types later, see: G_FILE_COMPRESS
* Will be NULL for UNDO.
*/
@@ -291,7 +455,15 @@ static WriteData *writedata_new(WriteWrap *ww)
wd->ww = ww;
if ((ww == NULL) || (ww->use_buf)) {
- wd->buf = MEM_mallocN(MYWRITE_BUFFER_SIZE, "wd->buf");
+ if (ww == NULL) {
+ wd->buffer.max_size = MEM_BUFFER_SIZE;
+ wd->buffer.chunk_size = MEM_CHUNK_SIZE;
+ }
+ else {
+ wd->buffer.max_size = ZSTD_BUFFER_SIZE;
+ wd->buffer.chunk_size = ZSTD_CHUNK_SIZE;
+ }
+ wd->buffer.buf = MEM_mallocN(wd->buffer.max_size, "wd->buffer.buf");
}
return wd;
@@ -325,8 +497,8 @@ static void writedata_do_write(WriteData *wd, const void *mem, size_t memlen)
static void writedata_free(WriteData *wd)
{
- if (wd->buf) {
- MEM_freeN(wd->buf);
+ if (wd->buffer.buf) {
+ MEM_freeN(wd->buffer.buf);
}
MEM_freeN(wd);
}
@@ -343,9 +515,9 @@ static void writedata_free(WriteData *wd)
*/
static void mywrite_flush(WriteData *wd)
{
- if (wd->buf_used_len != 0) {
- writedata_do_write(wd, wd->buf, wd->buf_used_len);
- wd->buf_used_len = 0;
+ if (wd->buffer.used_len != 0) {
+ writedata_do_write(wd, wd->buffer.buf, wd->buffer.used_len);
+ wd->buffer.used_len = 0;
}
}
@@ -369,20 +541,20 @@ static void mywrite(WriteData *wd, const void *adr, size_t len)
wd->write_len += len;
#endif
- if (wd->buf == NULL) {
+ if (wd->buffer.buf == NULL) {
writedata_do_write(wd, adr, len);
}
else {
/* if we have a single big chunk, write existing data in
* buffer and write out big chunk in smaller pieces */
- if (len > MYWRITE_MAX_CHUNK) {
- if (wd->buf_used_len != 0) {
- writedata_do_write(wd, wd->buf, wd->buf_used_len);
- wd->buf_used_len = 0;
+ if (len > wd->buffer.chunk_size) {
+ if (wd->buffer.used_len != 0) {
+ writedata_do_write(wd, wd->buffer.buf, wd->buffer.used_len);
+ wd->buffer.used_len = 0;
}
do {
- size_t writelen = MIN2(len, MYWRITE_MAX_CHUNK);
+ size_t writelen = MIN2(len, wd->buffer.chunk_size);
writedata_do_write(wd, adr, writelen);
adr = (const char *)adr + writelen;
len -= writelen;
@@ -392,14 +564,14 @@ static void mywrite(WriteData *wd, const void *adr, size_t len)
}
/* if data would overflow buffer, write out the buffer */
- if (len + wd->buf_used_len > MYWRITE_BUFFER_SIZE - 1) {
- writedata_do_write(wd, wd->buf, wd->buf_used_len);
- wd->buf_used_len = 0;
+ if (len + wd->buffer.used_len > wd->buffer.max_size - 1) {
+ writedata_do_write(wd, wd->buffer.buf, wd->buffer.used_len);
+ wd->buffer.used_len = 0;
}
/* append data at end of buffer */
- memcpy(&wd->buf[wd->buf_used_len], adr, len);
- wd->buf_used_len += len;
+ memcpy(&wd->buffer.buf[wd->buffer.used_len], adr, len);
+ wd->buffer.used_len += len;
}
}
@@ -430,9 +602,9 @@ static WriteData *mywrite_begin(WriteWrap *ww, MemFile *compare, MemFile *curren
*/
static bool mywrite_end(WriteData *wd)
{
- if (wd->buf_used_len != 0) {
- writedata_do_write(wd, wd->buf, wd->buf_used_len);
- wd->buf_used_len = 0;
+ if (wd->buffer.used_len != 0) {
+ writedata_do_write(wd, wd->buffer.buf, wd->buffer.used_len);
+ wd->buffer.used_len = 0;
}
if (wd->use_memfile) {
@@ -1150,7 +1322,6 @@ bool BLO_write_file(Main *mainvar,
ReportList *reports)
{
char tempname[FILE_MAX + 1];
- eWriteWrapType ww_type;
WriteWrap ww;
eBLO_WritePathRemap remap_mode = params->remap_mode;
@@ -1172,14 +1343,7 @@ bool BLO_write_file(Main *mainvar,
/* open temporary file, so we preserve the original in case we crash */
BLI_snprintf(tempname, sizeof(tempname), "%s@", filepath);
- if (write_flags & G_FILE_COMPRESS) {
- ww_type = WW_WRAP_ZLIB;
- }
- else {
- ww_type = WW_WRAP_NONE;
- }
-
- ww_handle_init(ww_type, &ww);
+ ww_handle_init((write_flags & G_FILE_COMPRESS) ? WW_WRAP_ZSTD : WW_WRAP_NONE, &ww);
if (ww.open(&ww, tempname) == false) {
BKE_reportf(
diff --git a/source/blender/compositor/CMakeLists.txt b/source/blender/compositor/CMakeLists.txt
index ee287c65fe9..8ddcf11602a 100644
--- a/source/blender/compositor/CMakeLists.txt
+++ b/source/blender/compositor/CMakeLists.txt
@@ -328,10 +328,14 @@ set(SRC
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
@@ -515,6 +519,8 @@ set(SRC
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
diff --git a/source/blender/compositor/COM_defines.h b/source/blender/compositor/COM_defines.h
index 40a1e0da2a8..e270eeb3386 100644
--- a/source/blender/compositor/COM_defines.h
+++ b/source/blender/compositor/COM_defines.h
@@ -33,6 +33,8 @@ enum class eExecutionModel {
FullFrame
};
+enum class eDimension { X, Y };
+
/**
* \brief possible data types for sockets
* \ingroup Model
@@ -119,6 +121,8 @@ constexpr float COM_PREVIEW_SIZE = 140.f;
constexpr float COM_RULE_OF_THIRDS_DIVIDER = 100.0f;
constexpr float COM_BLUR_BOKEH_PIXELS = 512;
+constexpr rcti COM_SINGLE_ELEM_AREA = {0, 1, 0, 1};
+
constexpr IndexRange XRange(const rcti &area)
{
return IndexRange(area.xmin, area.xmax - area.xmin);
diff --git a/source/blender/compositor/intern/COM_CompositorContext.h b/source/blender/compositor/intern/COM_CompositorContext.h
index 403ec62e359..c6e83f93777 100644
--- a/source/blender/compositor/intern/COM_CompositorContext.h
+++ b/source/blender/compositor/intern/COM_CompositorContext.h
@@ -239,6 +239,12 @@ class CompositorContext {
this->m_hasActiveOpenCLDevices = hasAvtiveOpenCLDevices;
}
+ /** Whether it has a view with a specific name and not the default one. */
+ bool has_explicit_view() const
+ {
+ return m_viewName && m_viewName[0] != '\0';
+ }
+
/**
* \brief get the active rendering view
*/
diff --git a/source/blender/compositor/intern/COM_MemoryBuffer.cc b/source/blender/compositor/intern/COM_MemoryBuffer.cc
index 6b954072a9a..1fbf502fea6 100644
--- a/source/blender/compositor/intern/COM_MemoryBuffer.cc
+++ b/source/blender/compositor/intern/COM_MemoryBuffer.cc
@@ -245,7 +245,9 @@ void MemoryBuffer::copy_from(const MemoryBuffer *src,
void MemoryBuffer::copy_from(const uchar *src, const rcti &area)
{
- copy_from(src, area, 0, this->get_num_channels(), this->get_num_channels(), 0);
+ const int elem_stride = this->get_num_channels();
+ const int row_stride = elem_stride * getWidth();
+ copy_from(src, area, 0, this->get_num_channels(), elem_stride, row_stride, 0);
}
void MemoryBuffer::copy_from(const uchar *src,
@@ -253,10 +255,18 @@ void MemoryBuffer::copy_from(const uchar *src,
const int channel_offset,
const int elem_size,
const int elem_stride,
+ const int row_stride,
const int to_channel_offset)
{
- copy_from(
- src, area, channel_offset, elem_size, elem_stride, area.xmin, area.ymin, to_channel_offset);
+ copy_from(src,
+ area,
+ channel_offset,
+ elem_size,
+ elem_stride,
+ row_stride,
+ area.xmin,
+ area.ymin,
+ to_channel_offset);
}
void MemoryBuffer::copy_from(const uchar *src,
@@ -264,6 +274,7 @@ void MemoryBuffer::copy_from(const uchar *src,
const int channel_offset,
const int elem_size,
const int elem_stride,
+ const int row_stride,
const int to_x,
const int to_y,
const int to_channel_offset)
@@ -273,10 +284,9 @@ void MemoryBuffer::copy_from(const uchar *src,
const int width = BLI_rcti_size_x(&area);
const int height = BLI_rcti_size_y(&area);
- const int src_row_stride = width * elem_stride;
- const uchar *const src_start = src + area.ymin * src_row_stride + channel_offset;
+ const uchar *const src_start = src + area.ymin * row_stride + channel_offset;
for (int y = 0; y < height; y++) {
- const uchar *from_elem = src_start + y * src_row_stride;
+ const uchar *from_elem = src_start + y * row_stride + area.xmin * elem_stride;
float *to_elem = &this->get_value(to_x, to_y + y, to_channel_offset);
const float *row_end = to_elem + width * this->elem_stride;
while (to_elem < row_end) {
@@ -346,7 +356,16 @@ void MemoryBuffer::copy_from(const ImBuf *src,
else if (src->rect) {
const uchar *uc_buf = (uchar *)src->rect;
const int elem_stride = src->channels;
- copy_from(uc_buf, area, channel_offset, elem_size, elem_stride, to_x, to_y, to_channel_offset);
+ const int row_stride = elem_stride * src->x;
+ copy_from(uc_buf,
+ area,
+ channel_offset,
+ elem_size,
+ elem_stride,
+ row_stride,
+ to_x,
+ to_y,
+ to_channel_offset);
if (ensure_linear_space) {
colorspace_to_scene_linear(this, area, src->rect_colorspace);
}
@@ -405,12 +424,48 @@ void MemoryBuffer::addPixel(int x, int y, const float color[4])
}
}
+static void read_ewa_elem(void *userdata, int x, int y, float result[4])
+{
+ const MemoryBuffer *buffer = static_cast<const MemoryBuffer *>(userdata);
+ buffer->read_elem_checked(x, y, result);
+}
+
+void MemoryBuffer::read_elem_filtered(
+ const float x, const float y, float dx[2], float dy[2], float *out) const
+{
+ BLI_assert(this->m_datatype == DataType::Color);
+
+ const float deriv[2][2] = {{dx[0], dx[1]}, {dy[0], dy[1]}};
+
+ float inv_width = 1.0f / (float)this->getWidth(), inv_height = 1.0f / (float)this->getHeight();
+ /* TODO(sergey): Render pipeline uses normalized coordinates and derivatives,
+ * but compositor uses pixel space. For now let's just divide the values and
+ * switch compositor to normalized space for EWA later.
+ */
+ float uv_normal[2] = {get_relative_x(x) * inv_width, get_relative_y(y) * inv_height};
+ float du_normal[2] = {deriv[0][0] * inv_width, deriv[0][1] * inv_height};
+ float dv_normal[2] = {deriv[1][0] * inv_width, deriv[1][1] * inv_height};
+
+ BLI_ewa_filter(this->getWidth(),
+ this->getHeight(),
+ false,
+ true,
+ uv_normal,
+ du_normal,
+ dv_normal,
+ read_ewa_elem,
+ const_cast<MemoryBuffer *>(this),
+ out);
+}
+
+/* TODO(manzanilla): to be removed with tiled implementation. */
static void read_ewa_pixel_sampled(void *userdata, int x, int y, float result[4])
{
MemoryBuffer *buffer = (MemoryBuffer *)userdata;
buffer->read(result, x, y);
}
+/* TODO(manzanilla): to be removed with tiled implementation. */
void MemoryBuffer::readEWA(float *result, const float uv[2], const float derivatives[2][2])
{
if (m_is_a_single_elem) {
diff --git a/source/blender/compositor/intern/COM_MemoryBuffer.h b/source/blender/compositor/intern/COM_MemoryBuffer.h
index 310e87b6a4b..f3e15c2a495 100644
--- a/source/blender/compositor/intern/COM_MemoryBuffer.h
+++ b/source/blender/compositor/intern/COM_MemoryBuffer.h
@@ -191,23 +191,96 @@ class MemoryBuffer {
void read_elem(int x, int y, float *out) const
{
- memcpy(out, get_elem(x, y), m_num_channels * sizeof(float));
+ memcpy(out, get_elem(x, y), get_elem_bytes_len());
+ }
+
+ void read_elem_checked(int x, int y, float *out) const
+ {
+ if (x < m_rect.xmin || x >= m_rect.xmax || y < m_rect.ymin || y >= m_rect.ymax) {
+ clear_elem(out);
+ }
+ else {
+ read_elem(x, y, out);
+ }
+ }
+
+ void read_elem_checked(float x, float y, float *out) const
+ {
+ if (x < m_rect.xmin || x >= m_rect.xmax || y < m_rect.ymin || y >= m_rect.ymax) {
+ clear_elem(out);
+ }
+ else {
+ read_elem(x, y, out);
+ }
+ }
+
+ void read_elem_bilinear(float x, float y, float *out) const
+ {
+ /* Only clear past +/-1 borders to be able to smooth edges. */
+ if (x <= m_rect.xmin - 1.0f || x >= m_rect.xmax || y <= m_rect.ymin - 1.0f ||
+ y >= m_rect.ymax) {
+ clear_elem(out);
+ return;
+ }
+
+ if (m_is_a_single_elem) {
+ if (x >= m_rect.xmin && x < m_rect.xmax - 1.0f && y >= m_rect.ymin &&
+ y < m_rect.ymax - 1.0f) {
+ memcpy(out, m_buffer, get_elem_bytes_len());
+ return;
+ }
+
+ /* Do sampling at borders to smooth edges. */
+ const float last_x = getWidth() - 1.0f;
+ const float rel_x = get_relative_x(x);
+ float single_x = 0.0f;
+ if (rel_x < 0.0f) {
+ single_x = rel_x;
+ }
+ else if (rel_x > last_x) {
+ single_x = rel_x - last_x;
+ }
+
+ const float last_y = getHeight() - 1.0f;
+ const float rel_y = get_relative_y(y);
+ float single_y = 0.0f;
+ if (rel_y < 0.0f) {
+ single_y = rel_y;
+ }
+ else if (rel_y > last_y) {
+ single_y = rel_y - last_y;
+ }
+
+ BLI_bilinear_interpolation_fl(m_buffer, out, 1, 1, m_num_channels, single_x, single_y);
+ return;
+ }
+
+ BLI_bilinear_interpolation_fl(m_buffer,
+ out,
+ getWidth(),
+ getHeight(),
+ m_num_channels,
+ get_relative_x(x),
+ get_relative_y(y));
}
void read_elem_sampled(float x, float y, PixelSampler sampler, float *out) const
{
switch (sampler) {
case PixelSampler::Nearest:
- this->read_elem(x, y, out);
+ read_elem_checked(x, y, out);
break;
case PixelSampler::Bilinear:
case PixelSampler::Bicubic:
/* No bicubic. Current implementation produces fuzzy results. */
- this->readBilinear(out, x, y);
+ read_elem_bilinear(x, y, out);
break;
}
}
+ void read_elem_filtered(
+ const float x, const float y, float dx[2], float dy[2], float *out) const;
+
/**
* Get channel value at given coordinates.
*/
@@ -403,6 +476,8 @@ class MemoryBuffer {
y = y + m_rect.ymin;
}
+ /* TODO(manzanilla): to be removed with tiled implementation. For applying #MemoryBufferExtend
+ * use #wrap_pixel. */
inline void read(float *result,
int x,
int y,
@@ -425,6 +500,7 @@ class MemoryBuffer {
}
}
+ /* TODO(manzanilla): to be removed with tiled implementation. */
inline void readNoCheck(float *result,
int x,
int y,
@@ -507,12 +583,14 @@ class MemoryBuffer {
int channel_offset,
int elem_size,
int elem_stride,
+ int row_stride,
int to_channel_offset);
void copy_from(const uchar *src,
const rcti &area,
int channel_offset,
int elem_size,
int elem_stride,
+ int row_stride,
int to_x,
int to_y,
int to_channel_offset);
@@ -582,6 +660,21 @@ class MemoryBuffer {
return get_memory_width() * get_memory_height();
}
+ void clear_elem(float *out) const
+ {
+ memset(out, 0, this->m_num_channels * sizeof(float));
+ }
+
+ template<typename T> T get_relative_x(T x) const
+ {
+ return x - m_rect.xmin;
+ }
+
+ template<typename T> T get_relative_y(T y) const
+ {
+ return y - m_rect.ymin;
+ }
+
void copy_single_elem_from(const MemoryBuffer *src,
int channel_offset,
int elem_size,
diff --git a/source/blender/compositor/intern/COM_NodeGraph.cc b/source/blender/compositor/intern/COM_NodeGraph.cc
index 205fbcc0440..1872fbcf656 100644
--- a/source/blender/compositor/intern/COM_NodeGraph.cc
+++ b/source/blender/compositor/intern/COM_NodeGraph.cc
@@ -248,7 +248,9 @@ void NodeGraph::add_proxies_group_inputs(bNode *b_node, bNode *b_node_io)
}
}
-void NodeGraph::add_proxies_group_outputs(bNode *b_node, bNode *b_node_io, bool use_buffer)
+void NodeGraph::add_proxies_group_outputs(const CompositorContext &context,
+ bNode *b_node,
+ bNode *b_node_io)
{
bNodeTree *b_group_tree = (bNodeTree *)b_node->id;
BLI_assert(b_group_tree); /* should have been checked in advance */
@@ -261,7 +263,8 @@ void NodeGraph::add_proxies_group_outputs(bNode *b_node, bNode *b_node_io, bool
b_sock_io = b_sock_io->next) {
bNodeSocket *b_sock_group = find_b_node_output(b_node, b_sock_io->identifier);
if (b_sock_group) {
- if (use_buffer) {
+ if (context.isGroupnodeBufferEnabled() &&
+ context.get_execution_model() == eExecutionModel::Tiled) {
SocketBufferNode *buffer = new SocketBufferNode(b_node_io, b_sock_io, b_sock_group);
add_node(buffer, b_group_tree, key, is_active_group);
}
@@ -297,7 +300,7 @@ void NodeGraph::add_proxies_group(const CompositorContext &context,
}
if (b_node_io->type == NODE_GROUP_OUTPUT && (b_node_io->flag & NODE_DO_OUTPUT)) {
- add_proxies_group_outputs(b_node, b_node_io, context.isGroupnodeBufferEnabled());
+ add_proxies_group_outputs(context, b_node, b_node_io);
}
}
diff --git a/source/blender/compositor/intern/COM_NodeGraph.h b/source/blender/compositor/intern/COM_NodeGraph.h
index 7fa01593e1e..dfcc6c2fcf9 100644
--- a/source/blender/compositor/intern/COM_NodeGraph.h
+++ b/source/blender/compositor/intern/COM_NodeGraph.h
@@ -107,7 +107,9 @@ class NodeGraph {
bool is_active_group);
void add_proxies_group_inputs(bNode *b_node, bNode *b_node_io);
- void add_proxies_group_outputs(bNode *b_node, bNode *b_node_io, bool use_buffer);
+ void add_proxies_group_outputs(const CompositorContext &context,
+ bNode *b_node,
+ bNode *b_node_io);
void add_proxies_group(const CompositorContext &context, bNode *b_node, bNodeInstanceKey key);
void add_proxies_reroute(bNodeTree *b_ntree,
diff --git a/source/blender/compositor/intern/COM_NodeOperation.cc b/source/blender/compositor/intern/COM_NodeOperation.cc
index 575e8446abe..1b87cdf72fb 100644
--- a/source/blender/compositor/intern/COM_NodeOperation.cc
+++ b/source/blender/compositor/intern/COM_NodeOperation.cc
@@ -82,8 +82,12 @@ void NodeOperation::determineResolution(unsigned int resolution[2],
input.determineResolution(resolution, preferredResolution);
used_resolution_index = m_resolutionInputSocketIndex;
}
- unsigned int temp2[2] = {resolution[0], resolution[1]};
+ if (modify_determined_resolution_fn_) {
+ modify_determined_resolution_fn_(resolution);
+ }
+
+ unsigned int temp2[2] = {resolution[0], resolution[1]};
unsigned int temp[2];
for (unsigned int index = 0; index < m_inputs.size(); index++) {
if (index == used_resolution_index) {
diff --git a/source/blender/compositor/intern/COM_NodeOperation.h b/source/blender/compositor/intern/COM_NodeOperation.h
index 934007d25ce..b402dc7f174 100644
--- a/source/blender/compositor/intern/COM_NodeOperation.h
+++ b/source/blender/compositor/intern/COM_NodeOperation.h
@@ -287,6 +287,8 @@ class NodeOperation {
*/
unsigned int m_resolutionInputSocketIndex;
+ std::function<void(unsigned int resolution[2])> modify_determined_resolution_fn_;
+
/**
* \brief mutex reference for very special node initializations
* \note only use when you really know what you are doing.
@@ -518,6 +520,15 @@ class NodeOperation {
void setResolutionInputSocketIndex(unsigned int index);
/**
+ * Set a custom function to modify determined resolution from main input just before setting it
+ * as preferred resolution for the other inputs.
+ */
+ void set_determined_resolution_modifier(std::function<void(unsigned int resolution[2])> fn)
+ {
+ modify_determined_resolution_fn_ = fn;
+ }
+
+ /**
* \brief get the render priority of this node.
* \note only applicable for output operations like ViewerOperation
* \return eCompositorPriority
diff --git a/source/blender/compositor/nodes/COM_CryptomatteNode.cc b/source/blender/compositor/nodes/COM_CryptomatteNode.cc
index 4032a655633..5835f051ce3 100644
--- a/source/blender/compositor/nodes/COM_CryptomatteNode.cc
+++ b/source/blender/compositor/nodes/COM_CryptomatteNode.cc
@@ -124,6 +124,10 @@ void CryptomatteNode::input_operations_from_render_source(
RenderLayer *render_layer = RE_GetRenderLayer(render_result, view_layer->name);
if (render_layer) {
LISTBASE_FOREACH (RenderPass *, render_pass, &render_layer->passes) {
+ if (context.has_explicit_view() && !STREQ(render_pass->view, context.getViewName())) {
+ continue;
+ }
+
const std::string combined_name = combined_layer_pass_name(render_layer, render_pass);
if (blender::StringRef(combined_name).startswith(prefix)) {
RenderLayersProg *op = new RenderLayersProg(
diff --git a/source/blender/compositor/nodes/COM_RotateNode.cc b/source/blender/compositor/nodes/COM_RotateNode.cc
index af5baa733dc..c2fd8ed5594 100644
--- a/source/blender/compositor/nodes/COM_RotateNode.cc
+++ b/source/blender/compositor/nodes/COM_RotateNode.cc
@@ -30,20 +30,31 @@ RotateNode::RotateNode(bNode *editorNode) : Node(editorNode)
}
void RotateNode::convertToOperations(NodeConverter &converter,
- const CompositorContext & /*context*/) const
+ const CompositorContext &context) const
{
NodeInput *inputSocket = this->getInputSocket(0);
NodeInput *inputDegreeSocket = this->getInputSocket(1);
NodeOutput *outputSocket = this->getOutputSocket(0);
RotateOperation *operation = new RotateOperation();
- SetSamplerOperation *sampler = new SetSamplerOperation();
- sampler->setSampler((PixelSampler)this->getbNode()->custom1);
-
- converter.addOperation(sampler);
converter.addOperation(operation);
- converter.addLink(sampler->getOutputSocket(), operation->getInputSocket(0));
- converter.mapInputSocket(inputSocket, sampler->getInputSocket(0));
+ PixelSampler sampler = (PixelSampler)this->getbNode()->custom1;
+ switch (context.get_execution_model()) {
+ case eExecutionModel::Tiled: {
+ SetSamplerOperation *sampler_op = new SetSamplerOperation();
+ sampler_op->setSampler(sampler);
+ converter.addOperation(sampler_op);
+ converter.addLink(sampler_op->getOutputSocket(), operation->getInputSocket(0));
+ converter.mapInputSocket(inputSocket, sampler_op->getInputSocket(0));
+ break;
+ }
+ case eExecutionModel::FullFrame: {
+ operation->set_sampler(sampler);
+ converter.mapInputSocket(inputSocket, operation->getInputSocket(0));
+ break;
+ }
+ }
+
converter.mapInputSocket(inputDegreeSocket, operation->getInputSocket(1));
converter.mapOutputSocket(outputSocket, operation->getOutputSocket(0));
}
diff --git a/source/blender/compositor/nodes/COM_Stabilize2dNode.cc b/source/blender/compositor/nodes/COM_Stabilize2dNode.cc
index 0262f653d1a..7b2388bebca 100644
--- a/source/blender/compositor/nodes/COM_Stabilize2dNode.cc
+++ b/source/blender/compositor/nodes/COM_Stabilize2dNode.cc
@@ -22,6 +22,7 @@
#include "COM_RotateOperation.h"
#include "COM_ScaleOperation.h"
#include "COM_SetSamplerOperation.h"
+#include "COM_TransformOperation.h"
#include "COM_TranslateOperation.h"
#include "BKE_tracking.h"
@@ -42,18 +43,12 @@ void Stabilize2dNode::convertToOperations(NodeConverter &converter,
NodeInput *imageInput = this->getInputSocket(0);
MovieClip *clip = (MovieClip *)editorNode->id;
bool invert = (editorNode->custom2 & CMP_NODEFLAG_STABILIZE_INVERSE) != 0;
+ const PixelSampler sampler = (PixelSampler)editorNode->custom1;
- ScaleRelativeOperation *scaleOperation = new ScaleRelativeOperation();
- scaleOperation->setSampler((PixelSampler)editorNode->custom1);
- RotateOperation *rotateOperation = new RotateOperation();
- rotateOperation->setDoDegree2RadConversion(false);
- TranslateOperation *translateOperation = new TranslateOperation();
MovieClipAttributeOperation *scaleAttribute = new MovieClipAttributeOperation();
MovieClipAttributeOperation *angleAttribute = new MovieClipAttributeOperation();
MovieClipAttributeOperation *xAttribute = new MovieClipAttributeOperation();
MovieClipAttributeOperation *yAttribute = new MovieClipAttributeOperation();
- SetSamplerOperation *psoperation = new SetSamplerOperation();
- psoperation->setSampler((PixelSampler)editorNode->custom1);
scaleAttribute->setAttribute(MCA_SCALE);
scaleAttribute->setFramenumber(context.getFramenumber());
@@ -79,38 +74,67 @@ void Stabilize2dNode::convertToOperations(NodeConverter &converter,
converter.addOperation(angleAttribute);
converter.addOperation(xAttribute);
converter.addOperation(yAttribute);
- converter.addOperation(scaleOperation);
- converter.addOperation(translateOperation);
- converter.addOperation(rotateOperation);
- converter.addOperation(psoperation);
- converter.addLink(scaleAttribute->getOutputSocket(), scaleOperation->getInputSocket(1));
- converter.addLink(scaleAttribute->getOutputSocket(), scaleOperation->getInputSocket(2));
-
- converter.addLink(angleAttribute->getOutputSocket(), rotateOperation->getInputSocket(1));
-
- converter.addLink(xAttribute->getOutputSocket(), translateOperation->getInputSocket(1));
- converter.addLink(yAttribute->getOutputSocket(), translateOperation->getInputSocket(2));
-
- converter.mapOutputSocket(getOutputSocket(), psoperation->getOutputSocket());
-
- if (invert) {
- // Translate -> Rotate -> Scale.
- converter.mapInputSocket(imageInput, translateOperation->getInputSocket(0));
-
- converter.addLink(translateOperation->getOutputSocket(), rotateOperation->getInputSocket(0));
- converter.addLink(rotateOperation->getOutputSocket(), scaleOperation->getInputSocket(0));
-
- converter.addLink(scaleOperation->getOutputSocket(), psoperation->getInputSocket(0));
- }
- else {
- // Scale -> Rotate -> Translate.
- converter.mapInputSocket(imageInput, scaleOperation->getInputSocket(0));
-
- converter.addLink(scaleOperation->getOutputSocket(), rotateOperation->getInputSocket(0));
- converter.addLink(rotateOperation->getOutputSocket(), translateOperation->getInputSocket(0));
-
- converter.addLink(translateOperation->getOutputSocket(), psoperation->getInputSocket(0));
+ switch (context.get_execution_model()) {
+ case eExecutionModel::Tiled: {
+ ScaleRelativeOperation *scaleOperation = new ScaleRelativeOperation();
+ scaleOperation->setSampler(sampler);
+ RotateOperation *rotateOperation = new RotateOperation();
+ rotateOperation->setDoDegree2RadConversion(false);
+ TranslateOperation *translateOperation = new TranslateOperation();
+ SetSamplerOperation *psoperation = new SetSamplerOperation();
+ psoperation->setSampler(sampler);
+
+ converter.addOperation(scaleOperation);
+ converter.addOperation(translateOperation);
+ converter.addOperation(rotateOperation);
+ converter.addOperation(psoperation);
+
+ converter.addLink(scaleAttribute->getOutputSocket(), scaleOperation->getInputSocket(1));
+ converter.addLink(scaleAttribute->getOutputSocket(), scaleOperation->getInputSocket(2));
+
+ converter.addLink(angleAttribute->getOutputSocket(), rotateOperation->getInputSocket(1));
+
+ converter.addLink(xAttribute->getOutputSocket(), translateOperation->getInputSocket(1));
+ converter.addLink(yAttribute->getOutputSocket(), translateOperation->getInputSocket(2));
+
+ converter.mapOutputSocket(getOutputSocket(), psoperation->getOutputSocket());
+
+ if (invert) {
+ // Translate -> Rotate -> Scale.
+ converter.mapInputSocket(imageInput, translateOperation->getInputSocket(0));
+
+ converter.addLink(translateOperation->getOutputSocket(),
+ rotateOperation->getInputSocket(0));
+ converter.addLink(rotateOperation->getOutputSocket(), scaleOperation->getInputSocket(0));
+
+ converter.addLink(scaleOperation->getOutputSocket(), psoperation->getInputSocket(0));
+ }
+ else {
+ // Scale -> Rotate -> Translate.
+ converter.mapInputSocket(imageInput, scaleOperation->getInputSocket(0));
+
+ converter.addLink(scaleOperation->getOutputSocket(), rotateOperation->getInputSocket(0));
+ converter.addLink(rotateOperation->getOutputSocket(),
+ translateOperation->getInputSocket(0));
+
+ converter.addLink(translateOperation->getOutputSocket(), psoperation->getInputSocket(0));
+ }
+ break;
+ }
+ case eExecutionModel::FullFrame: {
+ TransformOperation *transform_op = new TransformOperation();
+ transform_op->set_sampler(sampler);
+ transform_op->set_convert_rotate_degree_to_rad(false);
+ transform_op->set_invert(invert);
+ converter.addOperation(transform_op);
+ converter.mapInputSocket(imageInput, transform_op->getInputSocket(0));
+ converter.addLink(xAttribute->getOutputSocket(), transform_op->getInputSocket(1));
+ converter.addLink(yAttribute->getOutputSocket(), transform_op->getInputSocket(2));
+ converter.addLink(angleAttribute->getOutputSocket(), transform_op->getInputSocket(3));
+ converter.addLink(scaleAttribute->getOutputSocket(), transform_op->getInputSocket(4));
+ converter.mapOutputSocket(getOutputSocket(), transform_op->getOutputSocket());
+ }
}
}
diff --git a/source/blender/compositor/nodes/COM_TransformNode.cc b/source/blender/compositor/nodes/COM_TransformNode.cc
index e1deaf616a4..d2fb7b54633 100644
--- a/source/blender/compositor/nodes/COM_TransformNode.cc
+++ b/source/blender/compositor/nodes/COM_TransformNode.cc
@@ -22,6 +22,7 @@
#include "COM_ScaleOperation.h"
#include "COM_SetSamplerOperation.h"
#include "COM_SetValueOperation.h"
+#include "COM_TransformOperation.h"
#include "COM_TranslateOperation.h"
namespace blender::compositor {
@@ -32,7 +33,7 @@ TransformNode::TransformNode(bNode *editorNode) : Node(editorNode)
}
void TransformNode::convertToOperations(NodeConverter &converter,
- const CompositorContext & /*context*/) const
+ const CompositorContext &context) const
{
NodeInput *imageInput = this->getInputSocket(0);
NodeInput *xInput = this->getInputSocket(1);
@@ -40,33 +41,51 @@ void TransformNode::convertToOperations(NodeConverter &converter,
NodeInput *angleInput = this->getInputSocket(3);
NodeInput *scaleInput = this->getInputSocket(4);
- ScaleRelativeOperation *scaleOperation = new ScaleRelativeOperation();
- converter.addOperation(scaleOperation);
+ switch (context.get_execution_model()) {
+ case eExecutionModel::Tiled: {
+ ScaleRelativeOperation *scaleOperation = new ScaleRelativeOperation();
+ converter.addOperation(scaleOperation);
- RotateOperation *rotateOperation = new RotateOperation();
- rotateOperation->setDoDegree2RadConversion(false);
- converter.addOperation(rotateOperation);
+ RotateOperation *rotateOperation = new RotateOperation();
+ rotateOperation->setDoDegree2RadConversion(false);
+ converter.addOperation(rotateOperation);
- TranslateOperation *translateOperation = new TranslateOperation();
- converter.addOperation(translateOperation);
+ TranslateOperation *translateOperation = new TranslateOperation();
+ converter.addOperation(translateOperation);
- SetSamplerOperation *sampler = new SetSamplerOperation();
- sampler->setSampler((PixelSampler)this->getbNode()->custom1);
- converter.addOperation(sampler);
+ SetSamplerOperation *sampler = new SetSamplerOperation();
+ sampler->setSampler((PixelSampler)this->getbNode()->custom1);
+ converter.addOperation(sampler);
- converter.mapInputSocket(imageInput, sampler->getInputSocket(0));
- converter.addLink(sampler->getOutputSocket(), scaleOperation->getInputSocket(0));
- converter.mapInputSocket(scaleInput, scaleOperation->getInputSocket(1));
- converter.mapInputSocket(scaleInput, scaleOperation->getInputSocket(2)); // xscale = yscale
+ converter.mapInputSocket(imageInput, sampler->getInputSocket(0));
+ converter.addLink(sampler->getOutputSocket(), scaleOperation->getInputSocket(0));
+ converter.mapInputSocket(scaleInput, scaleOperation->getInputSocket(1));
+ converter.mapInputSocket(scaleInput, scaleOperation->getInputSocket(2)); // xscale = yscale
- converter.addLink(scaleOperation->getOutputSocket(), rotateOperation->getInputSocket(0));
- converter.mapInputSocket(angleInput, rotateOperation->getInputSocket(1));
+ converter.addLink(scaleOperation->getOutputSocket(), rotateOperation->getInputSocket(0));
+ converter.mapInputSocket(angleInput, rotateOperation->getInputSocket(1));
- converter.addLink(rotateOperation->getOutputSocket(), translateOperation->getInputSocket(0));
- converter.mapInputSocket(xInput, translateOperation->getInputSocket(1));
- converter.mapInputSocket(yInput, translateOperation->getInputSocket(2));
+ converter.addLink(rotateOperation->getOutputSocket(), translateOperation->getInputSocket(0));
+ converter.mapInputSocket(xInput, translateOperation->getInputSocket(1));
+ converter.mapInputSocket(yInput, translateOperation->getInputSocket(2));
- converter.mapOutputSocket(getOutputSocket(), translateOperation->getOutputSocket());
+ converter.mapOutputSocket(getOutputSocket(), translateOperation->getOutputSocket());
+ break;
+ }
+ case eExecutionModel::FullFrame: {
+ TransformOperation *op = new TransformOperation();
+ op->set_sampler((PixelSampler)this->getbNode()->custom1);
+ converter.addOperation(op);
+
+ converter.mapInputSocket(imageInput, op->getInputSocket(0));
+ converter.mapInputSocket(xInput, op->getInputSocket(1));
+ converter.mapInputSocket(yInput, op->getInputSocket(2));
+ converter.mapInputSocket(angleInput, op->getInputSocket(3));
+ converter.mapInputSocket(scaleInput, op->getInputSocket(4));
+ converter.mapOutputSocket(getOutputSocket(), op->getOutputSocket());
+ break;
+ }
+ }
}
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_BlurBaseOperation.cc b/source/blender/compositor/operations/COM_BlurBaseOperation.cc
index 8b73624ca79..280a6026ebe 100644
--- a/source/blender/compositor/operations/COM_BlurBaseOperation.cc
+++ b/source/blender/compositor/operations/COM_BlurBaseOperation.cc
@@ -17,6 +17,8 @@
*/
#include "COM_BlurBaseOperation.h"
+#include "COM_ConstantOperation.h"
+
#include "BLI_math.h"
#include "MEM_guardedalloc.h"
@@ -36,11 +38,15 @@ BlurBaseOperation::BlurBaseOperation(DataType data_type)
this->m_size = 1.0f;
this->m_sizeavailable = false;
this->m_extend_bounds = false;
+ use_variable_size_ = false;
}
-void BlurBaseOperation::initExecution()
+
+void BlurBaseOperation::init_data()
{
- this->m_inputProgram = this->getInputSocketReader(0);
- this->m_inputSize = this->getInputSocketReader(1);
+ if (execution_model_ == eExecutionModel::FullFrame) {
+ updateSize();
+ }
+
this->m_data.image_in_width = this->getWidth();
this->m_data.image_in_height = this->getHeight();
if (this->m_data.relative) {
@@ -61,6 +67,12 @@ void BlurBaseOperation::initExecution()
this->m_data.sizex = round_fl_to_int(this->m_data.percentx * 0.01f * sizex);
this->m_data.sizey = round_fl_to_int(this->m_data.percenty * 0.01f * sizey);
}
+}
+
+void BlurBaseOperation::initExecution()
+{
+ this->m_inputProgram = this->getInputSocketReader(0);
+ this->m_inputSize = this->getInputSocketReader(1);
QualityStepHelper::initExecution(COM_QH_MULTIPLY);
}
@@ -165,23 +177,82 @@ void BlurBaseOperation::setData(const NodeBlurData *data)
memcpy(&m_data, data, sizeof(NodeBlurData));
}
+int BlurBaseOperation::get_blur_size(eDimension dim) const
+{
+ switch (dim) {
+ case eDimension::X:
+ return m_data.sizex;
+ case eDimension::Y:
+ return m_data.sizey;
+ }
+ return -1;
+}
+
void BlurBaseOperation::updateSize()
{
- if (!this->m_sizeavailable) {
- float result[4];
- this->getInputSocketReader(1)->readSampled(result, 0, 0, PixelSampler::Nearest);
- this->m_size = result[0];
- this->m_sizeavailable = true;
+ if (this->m_sizeavailable || use_variable_size_) {
+ return;
}
+
+ switch (execution_model_) {
+ case eExecutionModel::Tiled: {
+ float result[4];
+ this->getInputSocketReader(1)->readSampled(result, 0, 0, PixelSampler::Nearest);
+ this->m_size = result[0];
+ break;
+ }
+ case eExecutionModel::FullFrame: {
+ NodeOperation *size_input = get_input_operation(SIZE_INPUT_INDEX);
+ if (size_input->get_flags().is_constant_operation) {
+ m_size = *static_cast<ConstantOperation *>(size_input)->get_constant_elem();
+ } /* Else use default. */
+ break;
+ }
+ }
+ this->m_sizeavailable = true;
}
void BlurBaseOperation::determineResolution(unsigned int resolution[2],
unsigned int preferredResolution[2])
{
- NodeOperation::determineResolution(resolution, preferredResolution);
- if (this->m_extend_bounds) {
- resolution[0] += 2 * this->m_size * m_data.sizex;
- resolution[1] += 2 * this->m_size * m_data.sizey;
+ if (!m_extend_bounds) {
+ NodeOperation::determineResolution(resolution, preferredResolution);
+ return;
+ }
+
+ switch (execution_model_) {
+ case eExecutionModel::Tiled: {
+ NodeOperation::determineResolution(resolution, preferredResolution);
+ resolution[0] += 2 * m_size * m_data.sizex;
+ resolution[1] += 2 * m_size * m_data.sizey;
+ break;
+ }
+ case eExecutionModel::FullFrame: {
+ /* Setting a modifier ensures all non main inputs have extended bounds as preferred
+ * resolution, avoiding unnecessary resolution convertions that would hide constant
+ * operations. */
+ set_determined_resolution_modifier([=](unsigned int res[2]) {
+ /* Rounding to even prevents jiggling in backdrop while switching size values. */
+ res[0] += round_to_even(2 * m_size * m_data.sizex);
+ res[1] += round_to_even(2 * m_size * m_data.sizey);
+ });
+ NodeOperation::determineResolution(resolution, preferredResolution);
+ break;
+ }
+ }
+}
+
+void BlurBaseOperation::get_area_of_interest(const int input_idx,
+ const rcti &output_area,
+ rcti &r_input_area)
+{
+ switch (input_idx) {
+ case 0:
+ r_input_area = output_area;
+ break;
+ case 1:
+ r_input_area = use_variable_size_ ? output_area : COM_SINGLE_ELEM_AREA;
+ break;
}
}
diff --git a/source/blender/compositor/operations/COM_BlurBaseOperation.h b/source/blender/compositor/operations/COM_BlurBaseOperation.h
index 7937ebd69dc..78b1e919aa6 100644
--- a/source/blender/compositor/operations/COM_BlurBaseOperation.h
+++ b/source/blender/compositor/operations/COM_BlurBaseOperation.h
@@ -18,7 +18,7 @@
#pragma once
-#include "COM_NodeOperation.h"
+#include "COM_MultiThreadedOperation.h"
#include "COM_QualityStepHelper.h"
#define MAX_GAUSSTAB_RADIUS 30000
@@ -27,10 +27,16 @@
namespace blender::compositor {
-class BlurBaseOperation : public NodeOperation, public QualityStepHelper {
+class BlurBaseOperation : public MultiThreadedOperation, public QualityStepHelper {
private:
+ bool m_extend_bounds;
+
+ protected:
+ static constexpr int IMAGE_INPUT_INDEX = 0;
+ static constexpr int SIZE_INPUT_INDEX = 1;
+
protected:
- BlurBaseOperation(DataType data_type);
+ BlurBaseOperation(DataType data_type8);
float *make_gausstab(float rad, int size);
#ifdef BLI_HAVE_SSE2
__m128 *convert_gausstab_sse(const float *gausstab, int size);
@@ -49,9 +55,11 @@ class BlurBaseOperation : public NodeOperation, public QualityStepHelper {
float m_size;
bool m_sizeavailable;
- bool m_extend_bounds;
+ /* Flags for inheriting classes. */
+ bool use_variable_size_;
public:
+ virtual void init_data() override;
/**
* Initialize the execution
*/
@@ -75,8 +83,14 @@ class BlurBaseOperation : public NodeOperation, public QualityStepHelper {
this->m_extend_bounds = extend_bounds;
}
+ int get_blur_size(eDimension dim) const;
+
void determineResolution(unsigned int resolution[2],
unsigned int preferredResolution[2]) override;
+
+ virtual void get_area_of_interest(int input_idx,
+ const rcti &output_area,
+ rcti &r_input_area) override;
};
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_BokehBlurOperation.cc b/source/blender/compositor/operations/COM_BokehBlurOperation.cc
index 3f98732b403..f2a43b7dbca 100644
--- a/source/blender/compositor/operations/COM_BokehBlurOperation.cc
+++ b/source/blender/compositor/operations/COM_BokehBlurOperation.cc
@@ -17,6 +17,8 @@
*/
#include "COM_BokehBlurOperation.h"
+#include "COM_ConstantOperation.h"
+
#include "BLI_math.h"
#include "COM_OpenCLDevice.h"
@@ -24,6 +26,11 @@
namespace blender::compositor {
+constexpr int IMAGE_INPUT_INDEX = 0;
+constexpr int BOKEH_INPUT_INDEX = 1;
+constexpr int BOUNDING_BOX_INPUT_INDEX = 2;
+constexpr int SIZE_INPUT_INDEX = 3;
+
BokehBlurOperation::BokehBlurOperation()
{
this->addInputSocket(DataType::Color);
@@ -44,6 +51,23 @@ BokehBlurOperation::BokehBlurOperation()
this->m_extend_bounds = false;
}
+void BokehBlurOperation::init_data()
+{
+ if (execution_model_ == eExecutionModel::FullFrame) {
+ updateSize();
+ }
+
+ NodeOperation *bokeh = get_input_operation(BOKEH_INPUT_INDEX);
+ const int width = bokeh->getWidth();
+ const int height = bokeh->getHeight();
+
+ const float dimension = MIN2(width, height);
+
+ m_bokehMidX = width / 2.0f;
+ m_bokehMidY = height / 2.0f;
+ m_bokehDimension = dimension / 2.0f;
+}
+
void *BokehBlurOperation::initializeTileData(rcti * /*rect*/)
{
lockMutex();
@@ -58,18 +82,11 @@ void *BokehBlurOperation::initializeTileData(rcti * /*rect*/)
void BokehBlurOperation::initExecution()
{
initMutex();
+
this->m_inputProgram = getInputSocketReader(0);
this->m_inputBokehProgram = getInputSocketReader(1);
this->m_inputBoundingBoxReader = getInputSocketReader(2);
- int width = this->m_inputBokehProgram->getWidth();
- int height = this->m_inputBokehProgram->getHeight();
-
- float dimension = MIN2(width, height);
-
- this->m_bokehMidX = width / 2.0f;
- this->m_bokehMidY = height / 2.0f;
- this->m_bokehDimension = dimension / 2.0f;
QualityStepHelper::initExecution(COM_QH_INCREASE);
}
@@ -225,23 +242,146 @@ void BokehBlurOperation::executeOpenCL(OpenCLDevice *device,
void BokehBlurOperation::updateSize()
{
- if (!this->m_sizeavailable) {
- float result[4];
- this->getInputSocketReader(3)->readSampled(result, 0, 0, PixelSampler::Nearest);
- this->m_size = result[0];
- CLAMP(this->m_size, 0.0f, 10.0f);
- this->m_sizeavailable = true;
+ if (this->m_sizeavailable) {
+ return;
}
+
+ switch (execution_model_) {
+ case eExecutionModel::Tiled: {
+ float result[4];
+ this->getInputSocketReader(3)->readSampled(result, 0, 0, PixelSampler::Nearest);
+ this->m_size = result[0];
+ CLAMP(this->m_size, 0.0f, 10.0f);
+ break;
+ }
+ case eExecutionModel::FullFrame: {
+ NodeOperation *size_input = get_input_operation(SIZE_INPUT_INDEX);
+ if (size_input->get_flags().is_constant_operation) {
+ m_size = *static_cast<ConstantOperation *>(size_input)->get_constant_elem();
+ CLAMP(m_size, 0.0f, 10.0f);
+ } /* Else use default. */
+ break;
+ }
+ }
+ this->m_sizeavailable = true;
}
void BokehBlurOperation::determineResolution(unsigned int resolution[2],
unsigned int preferredResolution[2])
{
- NodeOperation::determineResolution(resolution, preferredResolution);
- if (this->m_extend_bounds) {
- const float max_dim = MAX2(resolution[0], resolution[1]);
- resolution[0] += 2 * this->m_size * max_dim / 100.0f;
- resolution[1] += 2 * this->m_size * max_dim / 100.0f;
+ if (!m_extend_bounds) {
+ NodeOperation::determineResolution(resolution, preferredResolution);
+ return;
+ }
+
+ switch (execution_model_) {
+ case eExecutionModel::Tiled: {
+ NodeOperation::determineResolution(resolution, preferredResolution);
+ const float max_dim = MAX2(resolution[0], resolution[1]);
+ resolution[0] += 2 * this->m_size * max_dim / 100.0f;
+ resolution[1] += 2 * this->m_size * max_dim / 100.0f;
+ break;
+ }
+ case eExecutionModel::FullFrame: {
+ set_determined_resolution_modifier([=](unsigned int res[2]) {
+ const float max_dim = MAX2(res[0], res[1]);
+ /* Rounding to even prevents image jiggling in backdrop while switching size values. */
+ float add_size = round_to_even(2 * this->m_size * max_dim / 100.0f);
+ res[0] += add_size;
+ res[1] += add_size;
+ });
+ NodeOperation::determineResolution(resolution, preferredResolution);
+ break;
+ }
+ }
+}
+
+void BokehBlurOperation::get_area_of_interest(const int input_idx,
+ const rcti &output_area,
+ rcti &r_input_area)
+{
+ switch (input_idx) {
+ case IMAGE_INPUT_INDEX: {
+ const float max_dim = MAX2(this->getWidth(), this->getHeight());
+ const float add_size = m_size * max_dim / 100.0f;
+ r_input_area.xmin = output_area.xmin - add_size;
+ r_input_area.xmax = output_area.xmax + add_size;
+ r_input_area.ymin = output_area.ymin - add_size;
+ r_input_area.ymax = output_area.ymax + add_size;
+ break;
+ }
+ case BOKEH_INPUT_INDEX: {
+ NodeOperation *bokeh_input = getInputOperation(BOKEH_INPUT_INDEX);
+ r_input_area.xmin = 0;
+ r_input_area.xmax = bokeh_input->getWidth();
+ r_input_area.ymin = 0;
+ r_input_area.ymax = bokeh_input->getHeight();
+ break;
+ }
+ case BOUNDING_BOX_INPUT_INDEX:
+ r_input_area = output_area;
+ break;
+ case SIZE_INPUT_INDEX: {
+ r_input_area = COM_SINGLE_ELEM_AREA;
+ break;
+ }
+ }
+}
+
+void BokehBlurOperation::update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs)
+{
+ const float max_dim = MAX2(this->getWidth(), this->getHeight());
+ const int pixel_size = m_size * max_dim / 100.0f;
+ const float m = m_bokehDimension / pixel_size;
+
+ const MemoryBuffer *image_input = inputs[IMAGE_INPUT_INDEX];
+ const MemoryBuffer *bokeh_input = inputs[BOKEH_INPUT_INDEX];
+ MemoryBuffer *bounding_input = inputs[BOUNDING_BOX_INPUT_INDEX];
+ BuffersIterator<float> it = output->iterate_with({bounding_input}, area);
+ const rcti &image_rect = image_input->get_rect();
+ for (; !it.is_end(); ++it) {
+ const int x = it.x;
+ const int y = it.y;
+ const float bounding_box = *it.in(0);
+ if (bounding_box <= 0.0f) {
+ image_input->read_elem(x, y, it.out);
+ continue;
+ }
+
+ float color_accum[4] = {0};
+ float multiplier_accum[4] = {0};
+ if (pixel_size < 2) {
+ image_input->read_elem(x, y, color_accum);
+ multiplier_accum[0] = 1.0f;
+ multiplier_accum[1] = 1.0f;
+ multiplier_accum[2] = 1.0f;
+ multiplier_accum[3] = 1.0f;
+ }
+ const int miny = MAX2(y - pixel_size, image_rect.ymin);
+ const int maxy = MIN2(y + pixel_size, image_rect.ymax);
+ const int minx = MAX2(x - pixel_size, image_rect.xmin);
+ const int maxx = MIN2(x + pixel_size, image_rect.xmax);
+ const int step = getStep();
+ const int elem_stride = image_input->elem_stride * step;
+ const int row_stride = image_input->row_stride * step;
+ const float *row_color = image_input->get_elem(minx, miny);
+ for (int ny = miny; ny < maxy; ny += step, row_color += row_stride) {
+ const float *color = row_color;
+ const float v = m_bokehMidY - (ny - y) * m;
+ for (int nx = minx; nx < maxx; nx += step, color += elem_stride) {
+ const float u = m_bokehMidX - (nx - x) * m;
+ float bokeh[4];
+ bokeh_input->read_elem_checked(u, v, bokeh);
+ madd_v4_v4v4(color_accum, bokeh, color);
+ add_v4_v4(multiplier_accum, bokeh);
+ }
+ }
+ it.out[0] = color_accum[0] * (1.0f / multiplier_accum[0]);
+ it.out[1] = color_accum[1] * (1.0f / multiplier_accum[1]);
+ it.out[2] = color_accum[2] * (1.0f / multiplier_accum[2]);
+ it.out[3] = color_accum[3] * (1.0f / multiplier_accum[3]);
}
}
diff --git a/source/blender/compositor/operations/COM_BokehBlurOperation.h b/source/blender/compositor/operations/COM_BokehBlurOperation.h
index 3ce06adb5d6..59c14305393 100644
--- a/source/blender/compositor/operations/COM_BokehBlurOperation.h
+++ b/source/blender/compositor/operations/COM_BokehBlurOperation.h
@@ -18,12 +18,12 @@
#pragma once
-#include "COM_NodeOperation.h"
+#include "COM_MultiThreadedOperation.h"
#include "COM_QualityStepHelper.h"
namespace blender::compositor {
-class BokehBlurOperation : public NodeOperation, public QualityStepHelper {
+class BokehBlurOperation : public MultiThreadedOperation, public QualityStepHelper {
private:
SocketReader *m_inputProgram;
SocketReader *m_inputBokehProgram;
@@ -31,6 +31,7 @@ class BokehBlurOperation : public NodeOperation, public QualityStepHelper {
void updateSize();
float m_size;
bool m_sizeavailable;
+
float m_bokehMidX;
float m_bokehMidY;
float m_bokehDimension;
@@ -39,6 +40,8 @@ class BokehBlurOperation : public NodeOperation, public QualityStepHelper {
public:
BokehBlurOperation();
+ void init_data() override;
+
void *initializeTileData(rcti *rect) override;
/**
* The inner loop of this operation.
@@ -79,6 +82,11 @@ class BokehBlurOperation : public NodeOperation, public QualityStepHelper {
void determineResolution(unsigned int resolution[2],
unsigned int preferredResolution[2]) override;
+
+ void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override;
+ void update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs) override;
};
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_ChannelMatteOperation.cc b/source/blender/compositor/operations/COM_ChannelMatteOperation.cc
index ec4331dc231..65742d0cfcc 100644
--- a/source/blender/compositor/operations/COM_ChannelMatteOperation.cc
+++ b/source/blender/compositor/operations/COM_ChannelMatteOperation.cc
@@ -27,6 +27,7 @@ ChannelMatteOperation::ChannelMatteOperation()
addOutputSocket(DataType::Value);
this->m_inputImageProgram = nullptr;
+ flags.can_be_constant = true;
}
void ChannelMatteOperation::initExecution()
@@ -121,4 +122,37 @@ void ChannelMatteOperation::executePixelSampled(float output[4],
output[0] = MIN2(alpha, inColor[3]);
}
+void ChannelMatteOperation::update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs)
+{
+ for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) {
+ const float *color = it.in(0);
+
+ /* Matte operation. */
+ float alpha = color[this->m_ids[0]] - MAX2(color[this->m_ids[1]], color[this->m_ids[2]]);
+
+ /* Flip because 0.0 is transparent, not 1.0. */
+ alpha = 1.0f - alpha;
+
+ /* Test range. */
+ if (alpha > m_limit_max) {
+ alpha = color[3]; /* Whatever it was prior. */
+ }
+ else if (alpha < m_limit_min) {
+ alpha = 0.0f;
+ }
+ else { /* Blend. */
+ alpha = (alpha - m_limit_min) / m_limit_range;
+ }
+
+ /* Store matte(alpha) value in [0] to go with
+ * COM_SetAlphaMultiplyOperation and the Value output.
+ */
+
+ /* Don't make something that was more transparent less transparent. */
+ *it.out = MIN2(alpha, color[3]);
+ }
+}
+
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_ChannelMatteOperation.h b/source/blender/compositor/operations/COM_ChannelMatteOperation.h
index 6e9dcccd36e..ba50105dd3b 100644
--- a/source/blender/compositor/operations/COM_ChannelMatteOperation.h
+++ b/source/blender/compositor/operations/COM_ChannelMatteOperation.h
@@ -18,7 +18,7 @@
#pragma once
-#include "COM_MixOperation.h"
+#include "COM_MultiThreadedOperation.h"
namespace blender::compositor {
@@ -26,7 +26,7 @@ namespace blender::compositor {
* this program converts an input color to an output value.
* it assumes we are in sRGB color space.
*/
-class ChannelMatteOperation : public NodeOperation {
+class ChannelMatteOperation : public MultiThreadedOperation {
private:
SocketReader *m_inputImageProgram;
@@ -71,6 +71,10 @@ class ChannelMatteOperation : public NodeOperation {
this->m_limit_channel = nodeChroma->channel;
this->m_matte_channel = custom2;
}
+
+ void update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs) override;
};
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_ChromaMatteOperation.cc b/source/blender/compositor/operations/COM_ChromaMatteOperation.cc
index b7fec5f07e5..0784f266b19 100644
--- a/source/blender/compositor/operations/COM_ChromaMatteOperation.cc
+++ b/source/blender/compositor/operations/COM_ChromaMatteOperation.cc
@@ -29,6 +29,7 @@ ChromaMatteOperation::ChromaMatteOperation()
this->m_inputImageProgram = nullptr;
this->m_inputKeyProgram = nullptr;
+ flags.can_be_constant = true;
}
void ChromaMatteOperation::initExecution()
@@ -110,4 +111,58 @@ void ChromaMatteOperation::executePixelSampled(float output[4],
}
}
+void ChromaMatteOperation::update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs)
+{
+ const float acceptance = this->m_settings->t1; /* In radians. */
+ const float cutoff = this->m_settings->t2; /* In radians. */
+ const float gain = this->m_settings->fstrength;
+ for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) {
+ const float *in_image = it.in(0);
+ const float *in_key = it.in(1);
+
+ /* Store matte(alpha) value in [0] to go with
+ * #COM_SetAlphaMultiplyOperation and the Value output. */
+
+ /* Algorithm from book "Video Demystified", does not include the spill reduction part. */
+ /* Find theta, the angle that the color space should be rotated based on key. */
+
+ /* Rescale to `-1.0..1.0`. */
+ // const float image_Y = (in_image[0] * 2.0f) - 1.0f; // UNUSED
+ const float image_cb = (in_image[1] * 2.0f) - 1.0f;
+ const float image_cr = (in_image[2] * 2.0f) - 1.0f;
+
+ // const float key_Y = (in_key[0] * 2.0f) - 1.0f; // UNUSED
+ const float key_cb = (in_key[1] * 2.0f) - 1.0f;
+ const float key_cr = (in_key[2] * 2.0f) - 1.0f;
+
+ const float theta = atan2(key_cr, key_cb);
+
+ /* Rotate the cb and cr into x/z space. */
+ const float x_angle = image_cb * cosf(theta) + image_cr * sinf(theta);
+ const float z_angle = image_cr * cosf(theta) - image_cb * sinf(theta);
+
+ /* If within the acceptance angle. */
+ /* If kfg is <0 then the pixel is outside of the key color. */
+ const float kfg = x_angle - (fabsf(z_angle) / tanf(acceptance / 2.0f));
+
+ if (kfg > 0.0f) { /* Found a pixel that is within key color. */
+ const float beta = atan2(z_angle, x_angle);
+ float alpha = 1.0f - (kfg / gain);
+
+ /* Ff beta is within the cutoff angle. */
+ if (fabsf(beta) < (cutoff / 2.0f)) {
+ alpha = 0.0f;
+ }
+
+ /* Don't make something that was more transparent less transparent. */
+ it.out[0] = alpha < in_image[3] ? alpha : in_image[3];
+ }
+ else { /* Pixel is outside key color. */
+ it.out[0] = in_image[3]; /* Make pixel just as transparent as it was before. */
+ }
+ }
+}
+
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_ChromaMatteOperation.h b/source/blender/compositor/operations/COM_ChromaMatteOperation.h
index 48c3a785011..065349910a7 100644
--- a/source/blender/compositor/operations/COM_ChromaMatteOperation.h
+++ b/source/blender/compositor/operations/COM_ChromaMatteOperation.h
@@ -18,7 +18,7 @@
#pragma once
-#include "COM_MixOperation.h"
+#include "COM_MultiThreadedOperation.h"
namespace blender::compositor {
@@ -26,7 +26,7 @@ namespace blender::compositor {
* this program converts an input color to an output value.
* it assumes we are in sRGB color space.
*/
-class ChromaMatteOperation : public NodeOperation {
+class ChromaMatteOperation : public MultiThreadedOperation {
private:
NodeChroma *m_settings;
SocketReader *m_inputImageProgram;
@@ -50,6 +50,10 @@ class ChromaMatteOperation : public NodeOperation {
{
this->m_settings = nodeChroma;
}
+
+ void update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs) override;
};
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_ColorMatteOperation.cc b/source/blender/compositor/operations/COM_ColorMatteOperation.cc
index ddfbf415d9c..dec6571f217 100644
--- a/source/blender/compositor/operations/COM_ColorMatteOperation.cc
+++ b/source/blender/compositor/operations/COM_ColorMatteOperation.cc
@@ -29,6 +29,7 @@ ColorMatteOperation::ColorMatteOperation()
this->m_inputImageProgram = nullptr;
this->m_inputKeyProgram = nullptr;
+ flags.can_be_constant = true;
}
void ColorMatteOperation::initExecution()
@@ -82,4 +83,40 @@ void ColorMatteOperation::executePixelSampled(float output[4],
}
}
+void ColorMatteOperation::update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs)
+{
+ const float hue = m_settings->t1;
+ const float sat = m_settings->t2;
+ const float val = m_settings->t3;
+ for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) {
+ const float *in_color = it.in(0);
+ const float *in_key = it.in(1);
+
+ /* Store matte(alpha) value in [0] to go with
+ * COM_SetAlphaMultiplyOperation and the Value output.
+ */
+
+ float h_wrap;
+ if (
+ /* Do hue last because it needs to wrap, and does some more checks. */
+
+ /* #sat */ (fabsf(in_color[1] - in_key[1]) < sat) &&
+ /* #val */ (fabsf(in_color[2] - in_key[2]) < val) &&
+
+ /* Multiply by 2 because it wraps on both sides of the hue,
+ * otherwise 0.5 would key all hue's. */
+
+ /* #hue */
+ ((h_wrap = 2.0f * fabsf(in_color[0] - in_key[0])) < hue || (2.0f - h_wrap) < hue)) {
+ it.out[0] = 0.0f; /* Make transparent. */
+ }
+
+ else { /* Pixel is outside key color. */
+ it.out[0] = in_color[3]; /* Make pixel just as transparent as it was before. */
+ }
+ }
+}
+
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_ColorMatteOperation.h b/source/blender/compositor/operations/COM_ColorMatteOperation.h
index 439a3b0741d..49d06e62e65 100644
--- a/source/blender/compositor/operations/COM_ColorMatteOperation.h
+++ b/source/blender/compositor/operations/COM_ColorMatteOperation.h
@@ -18,7 +18,7 @@
#pragma once
-#include "COM_MixOperation.h"
+#include "COM_MultiThreadedOperation.h"
namespace blender::compositor {
@@ -26,7 +26,7 @@ namespace blender::compositor {
* this program converts an input color to an output value.
* it assumes we are in sRGB color space.
*/
-class ColorMatteOperation : public NodeOperation {
+class ColorMatteOperation : public MultiThreadedOperation {
private:
NodeChroma *m_settings;
SocketReader *m_inputImageProgram;
@@ -50,6 +50,10 @@ class ColorMatteOperation : public NodeOperation {
{
this->m_settings = nodeChroma;
}
+
+ void update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs) override;
};
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_ColorRampOperation.cc b/source/blender/compositor/operations/COM_ColorRampOperation.cc
index 0ee65a6529e..6c1b23ea731 100644
--- a/source/blender/compositor/operations/COM_ColorRampOperation.cc
+++ b/source/blender/compositor/operations/COM_ColorRampOperation.cc
@@ -29,6 +29,7 @@ ColorRampOperation::ColorRampOperation()
this->m_inputProgram = nullptr;
this->m_colorBand = nullptr;
+ this->flags.can_be_constant = true;
}
void ColorRampOperation::initExecution()
{
@@ -51,4 +52,13 @@ void ColorRampOperation::deinitExecution()
this->m_inputProgram = nullptr;
}
+void ColorRampOperation::update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs)
+{
+ for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) {
+ BKE_colorband_evaluate(m_colorBand, *it.in(0), it.out);
+ }
+}
+
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_ColorRampOperation.h b/source/blender/compositor/operations/COM_ColorRampOperation.h
index d32af9bea24..ab64b9928fd 100644
--- a/source/blender/compositor/operations/COM_ColorRampOperation.h
+++ b/source/blender/compositor/operations/COM_ColorRampOperation.h
@@ -18,12 +18,12 @@
#pragma once
-#include "COM_NodeOperation.h"
+#include "COM_MultiThreadedOperation.h"
#include "DNA_texture_types.h"
namespace blender::compositor {
-class ColorRampOperation : public NodeOperation {
+class ColorRampOperation : public MultiThreadedOperation {
private:
/**
* Cached reference to the inputProgram
@@ -53,6 +53,10 @@ class ColorRampOperation : public NodeOperation {
{
this->m_colorBand = colorBand;
}
+
+ void update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs) override;
};
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_ColorSpillOperation.cc b/source/blender/compositor/operations/COM_ColorSpillOperation.cc
index 7dc7e2775fc..4b0e0520b75 100644
--- a/source/blender/compositor/operations/COM_ColorSpillOperation.cc
+++ b/source/blender/compositor/operations/COM_ColorSpillOperation.cc
@@ -32,6 +32,7 @@ ColorSpillOperation::ColorSpillOperation()
this->m_inputFacReader = nullptr;
this->m_spillChannel = 1; // GREEN
this->m_spillMethod = 0;
+ flags.can_be_constant = true;
}
void ColorSpillOperation::initExecution()
@@ -118,4 +119,36 @@ void ColorSpillOperation::executePixelSampled(float output[4],
}
}
+void ColorSpillOperation::update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs)
+{
+ for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) {
+ const float *color = it.in(0);
+ const float factor = MIN2(1.0f, *it.in(1));
+
+ float map;
+ switch (m_spillMethod) {
+ case 0: /* simple */
+ map = factor *
+ (color[m_spillChannel] - (m_settings->limscale * color[m_settings->limchan]));
+ break;
+ default: /* average */
+ map = factor * (color[m_spillChannel] -
+ (m_settings->limscale * AVG(color[m_channel2], color[m_channel3])));
+ break;
+ }
+
+ if (map > 0.0f) {
+ it.out[0] = color[0] + m_rmut * (m_settings->uspillr * map);
+ it.out[1] = color[1] + m_gmut * (m_settings->uspillg * map);
+ it.out[2] = color[2] + m_bmut * (m_settings->uspillb * map);
+ it.out[3] = color[3];
+ }
+ else {
+ copy_v4_v4(it.out, color);
+ }
+ }
+}
+
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_ColorSpillOperation.h b/source/blender/compositor/operations/COM_ColorSpillOperation.h
index 9b82e720527..6a5e688c160 100644
--- a/source/blender/compositor/operations/COM_ColorSpillOperation.h
+++ b/source/blender/compositor/operations/COM_ColorSpillOperation.h
@@ -18,7 +18,7 @@
#pragma once
-#include "COM_NodeOperation.h"
+#include "COM_MultiThreadedOperation.h"
namespace blender::compositor {
@@ -26,7 +26,7 @@ namespace blender::compositor {
* this program converts an input color to an output value.
* it assumes we are in sRGB color space.
*/
-class ColorSpillOperation : public NodeOperation {
+class ColorSpillOperation : public MultiThreadedOperation {
protected:
NodeColorspill *m_settings;
SocketReader *m_inputImageReader;
@@ -65,6 +65,10 @@ class ColorSpillOperation : public NodeOperation {
}
float calculateMapValue(float fac, float *input);
+
+ void update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs) override;
};
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_ConvertOperation.cc b/source/blender/compositor/operations/COM_ConvertOperation.cc
index 384936533c7..d377903efea 100644
--- a/source/blender/compositor/operations/COM_ConvertOperation.cc
+++ b/source/blender/compositor/operations/COM_ConvertOperation.cc
@@ -27,6 +27,7 @@ namespace blender::compositor {
ConvertBaseOperation::ConvertBaseOperation()
{
this->m_inputOperation = nullptr;
+ this->flags.can_be_constant = true;
}
void ConvertBaseOperation::initExecution()
@@ -39,6 +40,14 @@ void ConvertBaseOperation::deinitExecution()
this->m_inputOperation = nullptr;
}
+void ConvertBaseOperation::update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs)
+{
+ BuffersIterator<float> it = output->iterate_with(inputs, area);
+ update_memory_buffer_partial(it);
+}
+
/* ******** Value to Color ******** */
ConvertValueToColorOperation::ConvertValueToColorOperation() : ConvertBaseOperation()
@@ -58,6 +67,14 @@ void ConvertValueToColorOperation::executePixelSampled(float output[4],
output[3] = 1.0f;
}
+void ConvertValueToColorOperation::update_memory_buffer_partial(BuffersIterator<float> &it)
+{
+ for (; !it.is_end(); ++it) {
+ it.out[0] = it.out[1] = it.out[2] = *it.in(0);
+ it.out[3] = 1.0f;
+ }
+}
+
/* ******** Color to Value ******** */
ConvertColorToValueOperation::ConvertColorToValueOperation() : ConvertBaseOperation()
@@ -76,6 +93,14 @@ void ConvertColorToValueOperation::executePixelSampled(float output[4],
output[0] = (inputColor[0] + inputColor[1] + inputColor[2]) / 3.0f;
}
+void ConvertColorToValueOperation::update_memory_buffer_partial(BuffersIterator<float> &it)
+{
+ for (; !it.is_end(); ++it) {
+ const float *in = it.in(0);
+ it.out[0] = (in[0] + in[1] + in[2]) / 3.0f;
+ }
+}
+
/* ******** Color to BW ******** */
ConvertColorToBWOperation::ConvertColorToBWOperation() : ConvertBaseOperation()
@@ -94,6 +119,13 @@ void ConvertColorToBWOperation::executePixelSampled(float output[4],
output[0] = IMB_colormanagement_get_luminance(inputColor);
}
+void ConvertColorToBWOperation::update_memory_buffer_partial(BuffersIterator<float> &it)
+{
+ for (; !it.is_end(); ++it) {
+ it.out[0] = IMB_colormanagement_get_luminance(it.in(0));
+ }
+}
+
/* ******** Color to Vector ******** */
ConvertColorToVectorOperation::ConvertColorToVectorOperation() : ConvertBaseOperation()
@@ -112,6 +144,13 @@ void ConvertColorToVectorOperation::executePixelSampled(float output[4],
copy_v3_v3(output, color);
}
+void ConvertColorToVectorOperation::update_memory_buffer_partial(BuffersIterator<float> &it)
+{
+ for (; !it.is_end(); ++it) {
+ copy_v3_v3(it.out, it.in(0));
+ }
+}
+
/* ******** Value to Vector ******** */
ConvertValueToVectorOperation::ConvertValueToVectorOperation() : ConvertBaseOperation()
@@ -130,6 +169,13 @@ void ConvertValueToVectorOperation::executePixelSampled(float output[4],
output[0] = output[1] = output[2] = value;
}
+void ConvertValueToVectorOperation::update_memory_buffer_partial(BuffersIterator<float> &it)
+{
+ for (; !it.is_end(); ++it) {
+ it.out[0] = it.out[1] = it.out[2] = *it.in(0);
+ }
+}
+
/* ******** Vector to Color ******** */
ConvertVectorToColorOperation::ConvertVectorToColorOperation() : ConvertBaseOperation()
@@ -147,6 +193,14 @@ void ConvertVectorToColorOperation::executePixelSampled(float output[4],
output[3] = 1.0f;
}
+void ConvertVectorToColorOperation::update_memory_buffer_partial(BuffersIterator<float> &it)
+{
+ for (; !it.is_end(); ++it) {
+ copy_v3_v3(it.out, it.in(0));
+ it.out[3] = 1.0f;
+ }
+}
+
/* ******** Vector to Value ******** */
ConvertVectorToValueOperation::ConvertVectorToValueOperation() : ConvertBaseOperation()
@@ -165,6 +219,14 @@ void ConvertVectorToValueOperation::executePixelSampled(float output[4],
output[0] = (input[0] + input[1] + input[2]) / 3.0f;
}
+void ConvertVectorToValueOperation::update_memory_buffer_partial(BuffersIterator<float> &it)
+{
+ for (; !it.is_end(); ++it) {
+ const float *in = it.in(0);
+ it.out[0] = (in[0] + in[1] + in[2]) / 3.0f;
+ }
+}
+
/* ******** RGB to YCC ******** */
ConvertRGBToYCCOperation::ConvertRGBToYCCOperation() : ConvertBaseOperation()
@@ -207,6 +269,18 @@ void ConvertRGBToYCCOperation::executePixelSampled(float output[4],
output[3] = inputColor[3];
}
+void ConvertRGBToYCCOperation::update_memory_buffer_partial(BuffersIterator<float> &it)
+{
+ for (; !it.is_end(); ++it) {
+ const float *in = it.in(0);
+ rgb_to_ycc(in[0], in[1], in[2], &it.out[0], &it.out[1], &it.out[2], this->m_mode);
+
+ /* Normalize for viewing (#rgb_to_ycc returns 0-255 values). */
+ mul_v3_fl(it.out, 1.0f / 255.0f);
+ it.out[3] = in[3];
+ }
+}
+
/* ******** YCC to RGB ******** */
ConvertYCCToRGBOperation::ConvertYCCToRGBOperation() : ConvertBaseOperation()
@@ -253,6 +327,22 @@ void ConvertYCCToRGBOperation::executePixelSampled(float output[4],
output[3] = inputColor[3];
}
+void ConvertYCCToRGBOperation::update_memory_buffer_partial(BuffersIterator<float> &it)
+{
+ for (; !it.is_end(); ++it) {
+ const float *in = it.in(0);
+ /* Multiply by 255 to un-normalize (#ycc_to_rgb needs input values in 0-255 range). */
+ ycc_to_rgb(in[0] * 255.0f,
+ in[1] * 255.0f,
+ in[2] * 255.0f,
+ &it.out[0],
+ &it.out[1],
+ &it.out[2],
+ this->m_mode);
+ it.out[3] = in[3];
+ }
+}
+
/* ******** RGB to YUV ******** */
ConvertRGBToYUVOperation::ConvertRGBToYUVOperation() : ConvertBaseOperation()
@@ -278,6 +368,15 @@ void ConvertRGBToYUVOperation::executePixelSampled(float output[4],
output[3] = inputColor[3];
}
+void ConvertRGBToYUVOperation::update_memory_buffer_partial(BuffersIterator<float> &it)
+{
+ for (; !it.is_end(); ++it) {
+ const float *in = it.in(0);
+ rgb_to_yuv(in[0], in[1], in[2], &it.out[0], &it.out[1], &it.out[2], BLI_YUV_ITU_BT709);
+ it.out[3] = in[3];
+ }
+}
+
/* ******** YUV to RGB ******** */
ConvertYUVToRGBOperation::ConvertYUVToRGBOperation() : ConvertBaseOperation()
@@ -303,6 +402,15 @@ void ConvertYUVToRGBOperation::executePixelSampled(float output[4],
output[3] = inputColor[3];
}
+void ConvertYUVToRGBOperation::update_memory_buffer_partial(BuffersIterator<float> &it)
+{
+ for (; !it.is_end(); ++it) {
+ const float *in = it.in(0);
+ yuv_to_rgb(in[0], in[1], in[2], &it.out[0], &it.out[1], &it.out[2], BLI_YUV_ITU_BT709);
+ it.out[3] = in[3];
+ }
+}
+
/* ******** RGB to HSV ******** */
ConvertRGBToHSVOperation::ConvertRGBToHSVOperation() : ConvertBaseOperation()
@@ -322,6 +430,15 @@ void ConvertRGBToHSVOperation::executePixelSampled(float output[4],
output[3] = inputColor[3];
}
+void ConvertRGBToHSVOperation::update_memory_buffer_partial(BuffersIterator<float> &it)
+{
+ for (; !it.is_end(); ++it) {
+ const float *in = it.in(0);
+ rgb_to_hsv_v(in, it.out);
+ it.out[3] = in[3];
+ }
+}
+
/* ******** HSV to RGB ******** */
ConvertHSVToRGBOperation::ConvertHSVToRGBOperation() : ConvertBaseOperation()
@@ -344,6 +461,18 @@ void ConvertHSVToRGBOperation::executePixelSampled(float output[4],
output[3] = inputColor[3];
}
+void ConvertHSVToRGBOperation::update_memory_buffer_partial(BuffersIterator<float> &it)
+{
+ for (; !it.is_end(); ++it) {
+ const float *in = it.in(0);
+ hsv_to_rgb_v(in, it.out);
+ it.out[0] = max_ff(it.out[0], 0.0f);
+ it.out[1] = max_ff(it.out[1], 0.0f);
+ it.out[2] = max_ff(it.out[2], 0.0f);
+ it.out[3] = in[3];
+ }
+}
+
/* ******** Premul to Straight ******** */
ConvertPremulToStraightOperation::ConvertPremulToStraightOperation() : ConvertBaseOperation()
@@ -363,6 +492,13 @@ void ConvertPremulToStraightOperation::executePixelSampled(float output[4],
copy_v4_v4(output, converted);
}
+void ConvertPremulToStraightOperation::update_memory_buffer_partial(BuffersIterator<float> &it)
+{
+ for (; !it.is_end(); ++it) {
+ copy_v4_v4(it.out, ColorSceneLinear4f<eAlpha::Premultiplied>(it.in(0)).unpremultiply_alpha());
+ }
+}
+
/* ******** Straight to Premul ******** */
ConvertStraightToPremulOperation::ConvertStraightToPremulOperation() : ConvertBaseOperation()
@@ -382,6 +518,13 @@ void ConvertStraightToPremulOperation::executePixelSampled(float output[4],
copy_v4_v4(output, converted);
}
+void ConvertStraightToPremulOperation::update_memory_buffer_partial(BuffersIterator<float> &it)
+{
+ for (; !it.is_end(); ++it) {
+ copy_v4_v4(it.out, ColorSceneLinear4f<eAlpha::Straight>(it.in(0)).premultiply_alpha());
+ }
+}
+
/* ******** Separate Channels ******** */
SeparateChannelOperation::SeparateChannelOperation()
@@ -410,6 +553,15 @@ void SeparateChannelOperation::executePixelSampled(float output[4],
output[0] = input[this->m_channel];
}
+void SeparateChannelOperation::update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs)
+{
+ for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) {
+ it.out[0] = it.in(0)[this->m_channel];
+ }
+}
+
/* ******** Combine Channels ******** */
CombineChannelsOperation::CombineChannelsOperation()
@@ -466,4 +618,16 @@ void CombineChannelsOperation::executePixelSampled(float output[4],
}
}
+void CombineChannelsOperation::update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs)
+{
+ for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) {
+ it.out[0] = *it.in(0);
+ it.out[1] = *it.in(1);
+ it.out[2] = *it.in(2);
+ it.out[3] = *it.in(3);
+ }
+}
+
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_ConvertOperation.h b/source/blender/compositor/operations/COM_ConvertOperation.h
index 7a726e35c7c..0334959ae7e 100644
--- a/source/blender/compositor/operations/COM_ConvertOperation.h
+++ b/source/blender/compositor/operations/COM_ConvertOperation.h
@@ -18,11 +18,11 @@
#pragma once
-#include "COM_NodeOperation.h"
+#include "COM_MultiThreadedOperation.h"
namespace blender::compositor {
-class ConvertBaseOperation : public NodeOperation {
+class ConvertBaseOperation : public MultiThreadedOperation {
protected:
SocketReader *m_inputOperation;
@@ -31,6 +31,13 @@ class ConvertBaseOperation : public NodeOperation {
void initExecution() override;
void deinitExecution() override;
+
+ void update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs) final;
+
+ protected:
+ virtual void update_memory_buffer_partial(BuffersIterator<float> &it) = 0;
};
class ConvertValueToColorOperation : public ConvertBaseOperation {
@@ -38,6 +45,9 @@ class ConvertValueToColorOperation : public ConvertBaseOperation {
ConvertValueToColorOperation();
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
+
+ protected:
+ void update_memory_buffer_partial(BuffersIterator<float> &it) override;
};
class ConvertColorToValueOperation : public ConvertBaseOperation {
@@ -45,6 +55,9 @@ class ConvertColorToValueOperation : public ConvertBaseOperation {
ConvertColorToValueOperation();
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
+
+ protected:
+ void update_memory_buffer_partial(BuffersIterator<float> &it) override;
};
class ConvertColorToBWOperation : public ConvertBaseOperation {
@@ -52,6 +65,9 @@ class ConvertColorToBWOperation : public ConvertBaseOperation {
ConvertColorToBWOperation();
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
+
+ protected:
+ void update_memory_buffer_partial(BuffersIterator<float> &it) override;
};
class ConvertColorToVectorOperation : public ConvertBaseOperation {
@@ -59,6 +75,9 @@ class ConvertColorToVectorOperation : public ConvertBaseOperation {
ConvertColorToVectorOperation();
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
+
+ protected:
+ void update_memory_buffer_partial(BuffersIterator<float> &it) override;
};
class ConvertValueToVectorOperation : public ConvertBaseOperation {
@@ -66,6 +85,9 @@ class ConvertValueToVectorOperation : public ConvertBaseOperation {
ConvertValueToVectorOperation();
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
+
+ protected:
+ void update_memory_buffer_partial(BuffersIterator<float> &it) override;
};
class ConvertVectorToColorOperation : public ConvertBaseOperation {
@@ -73,6 +95,9 @@ class ConvertVectorToColorOperation : public ConvertBaseOperation {
ConvertVectorToColorOperation();
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
+
+ protected:
+ void update_memory_buffer_partial(BuffersIterator<float> &it) override;
};
class ConvertVectorToValueOperation : public ConvertBaseOperation {
@@ -80,6 +105,9 @@ class ConvertVectorToValueOperation : public ConvertBaseOperation {
ConvertVectorToValueOperation();
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
+
+ protected:
+ void update_memory_buffer_partial(BuffersIterator<float> &it) override;
};
class ConvertRGBToYCCOperation : public ConvertBaseOperation {
@@ -94,6 +122,9 @@ class ConvertRGBToYCCOperation : public ConvertBaseOperation {
/** Set the YCC mode */
void setMode(int mode);
+
+ protected:
+ void update_memory_buffer_partial(BuffersIterator<float> &it) override;
};
class ConvertYCCToRGBOperation : public ConvertBaseOperation {
@@ -108,6 +139,9 @@ class ConvertYCCToRGBOperation : public ConvertBaseOperation {
/** Set the YCC mode */
void setMode(int mode);
+
+ protected:
+ void update_memory_buffer_partial(BuffersIterator<float> &it) override;
};
class ConvertRGBToYUVOperation : public ConvertBaseOperation {
@@ -115,6 +149,9 @@ class ConvertRGBToYUVOperation : public ConvertBaseOperation {
ConvertRGBToYUVOperation();
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
+
+ protected:
+ void update_memory_buffer_partial(BuffersIterator<float> &it) override;
};
class ConvertYUVToRGBOperation : public ConvertBaseOperation {
@@ -122,6 +159,9 @@ class ConvertYUVToRGBOperation : public ConvertBaseOperation {
ConvertYUVToRGBOperation();
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
+
+ protected:
+ void update_memory_buffer_partial(BuffersIterator<float> &it) override;
};
class ConvertRGBToHSVOperation : public ConvertBaseOperation {
@@ -129,6 +169,9 @@ class ConvertRGBToHSVOperation : public ConvertBaseOperation {
ConvertRGBToHSVOperation();
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
+
+ protected:
+ void update_memory_buffer_partial(BuffersIterator<float> &it) override;
};
class ConvertHSVToRGBOperation : public ConvertBaseOperation {
@@ -136,6 +179,9 @@ class ConvertHSVToRGBOperation : public ConvertBaseOperation {
ConvertHSVToRGBOperation();
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
+
+ protected:
+ void update_memory_buffer_partial(BuffersIterator<float> &it) override;
};
class ConvertPremulToStraightOperation : public ConvertBaseOperation {
@@ -143,6 +189,9 @@ class ConvertPremulToStraightOperation : public ConvertBaseOperation {
ConvertPremulToStraightOperation();
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
+
+ protected:
+ void update_memory_buffer_partial(BuffersIterator<float> &it) override;
};
class ConvertStraightToPremulOperation : public ConvertBaseOperation {
@@ -150,9 +199,12 @@ class ConvertStraightToPremulOperation : public ConvertBaseOperation {
ConvertStraightToPremulOperation();
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
+
+ protected:
+ void update_memory_buffer_partial(BuffersIterator<float> &it) override;
};
-class SeparateChannelOperation : public NodeOperation {
+class SeparateChannelOperation : public MultiThreadedOperation {
private:
SocketReader *m_inputOperation;
int m_channel;
@@ -168,9 +220,13 @@ class SeparateChannelOperation : public NodeOperation {
{
this->m_channel = channel;
}
+
+ void update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs) override;
};
-class CombineChannelsOperation : public NodeOperation {
+class CombineChannelsOperation : public MultiThreadedOperation {
private:
SocketReader *m_inputChannel1Operation;
SocketReader *m_inputChannel2Operation;
@@ -183,6 +239,10 @@ class CombineChannelsOperation : public NodeOperation {
void initExecution() override;
void deinitExecution() override;
+
+ void update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs) override;
};
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_CropOperation.cc b/source/blender/compositor/operations/COM_CropOperation.cc
index f12d93bc8d3..12833660fcb 100644
--- a/source/blender/compositor/operations/COM_CropOperation.cc
+++ b/source/blender/compositor/operations/COM_CropOperation.cc
@@ -95,6 +95,22 @@ void CropOperation::executePixelSampled(float output[4], float x, float y, Pixel
}
}
+void CropOperation::update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs)
+{
+ rcti crop_area;
+ BLI_rcti_init(&crop_area, m_xmin, m_xmax, m_ymin, m_ymax);
+ for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) {
+ if (BLI_rcti_isect_pt(&crop_area, it.x, it.y)) {
+ copy_v4_v4(it.out, it.in(0));
+ }
+ else {
+ zero_v4(it.out);
+ }
+ }
+}
+
CropImageOperation::CropImageOperation() : CropBaseOperation()
{
/* pass */
@@ -114,6 +130,18 @@ bool CropImageOperation::determineDependingAreaOfInterest(rcti *input,
return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output);
}
+void CropImageOperation::get_area_of_interest(const int input_idx,
+ const rcti &output_area,
+ rcti &r_input_area)
+{
+ BLI_assert(input_idx == 0);
+ UNUSED_VARS_NDEBUG(input_idx);
+ r_input_area.xmax = output_area.xmax + this->m_xmin;
+ r_input_area.xmin = output_area.xmin + this->m_xmin;
+ r_input_area.ymax = output_area.ymax + this->m_ymin;
+ r_input_area.ymin = output_area.ymin + this->m_ymin;
+}
+
void CropImageOperation::determineResolution(unsigned int resolution[2],
unsigned int preferredResolution[2])
{
@@ -136,4 +164,21 @@ void CropImageOperation::executePixelSampled(float output[4],
}
}
+void CropImageOperation::update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs)
+{
+ rcti op_area;
+ BLI_rcti_init(&op_area, 0, getWidth(), 0, getHeight());
+ const MemoryBuffer *input = inputs[0];
+ for (BuffersIterator<float> it = output->iterate_with({}, area); !it.is_end(); ++it) {
+ if (BLI_rcti_isect_pt(&op_area, it.x, it.y)) {
+ input->read_elem_checked(it.x + this->m_xmin, it.y + this->m_ymin, it.out);
+ }
+ else {
+ zero_v4(it.out);
+ }
+ }
+}
+
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_CropOperation.h b/source/blender/compositor/operations/COM_CropOperation.h
index acdff79a77c..57caa4e5834 100644
--- a/source/blender/compositor/operations/COM_CropOperation.h
+++ b/source/blender/compositor/operations/COM_CropOperation.h
@@ -18,11 +18,11 @@
#pragma once
-#include "COM_NodeOperation.h"
+#include "COM_MultiThreadedOperation.h"
namespace blender::compositor {
-class CropBaseOperation : public NodeOperation {
+class CropBaseOperation : public MultiThreadedOperation {
protected:
SocketReader *m_inputOperation;
NodeTwoXYs *m_settings;
@@ -53,6 +53,10 @@ class CropOperation : public CropBaseOperation {
public:
CropOperation();
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
+
+ void update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs) override;
};
class CropImageOperation : public CropBaseOperation {
@@ -65,6 +69,11 @@ class CropImageOperation : public CropBaseOperation {
void determineResolution(unsigned int resolution[2],
unsigned int preferredResolution[2]) override;
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
+
+ void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override;
+ void update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs) override;
};
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_CryptomatteOperation.cc b/source/blender/compositor/operations/COM_CryptomatteOperation.cc
index 1a86fadad76..02e7c5607d8 100644
--- a/source/blender/compositor/operations/COM_CryptomatteOperation.cc
+++ b/source/blender/compositor/operations/COM_CryptomatteOperation.cc
@@ -71,4 +71,34 @@ void CryptomatteOperation::executePixel(float output[4], int x, int y, void *dat
}
}
+void CryptomatteOperation::update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs)
+{
+ for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) {
+ zero_v4(it.out);
+ for (int i = 0; i < it.get_num_inputs(); i++) {
+ const float *input = it.in(i);
+ if (i == 0) {
+ /* Write the front-most object as false color for picking. */
+ it.out[0] = input[0];
+ uint32_t m3hash;
+ ::memcpy(&m3hash, &input[0], sizeof(uint32_t));
+ /* Since the red channel is likely to be out of display range,
+ * setting green and blue gives more meaningful images. */
+ it.out[1] = ((float)(m3hash << 8) / (float)UINT32_MAX);
+ it.out[2] = ((float)(m3hash << 16) / (float)UINT32_MAX);
+ }
+ for (const float hash : m_objectIndex) {
+ if (input[0] == hash) {
+ it.out[3] += input[1];
+ }
+ if (input[2] == hash) {
+ it.out[3] += input[3];
+ }
+ }
+ }
+ }
+}
+
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_CryptomatteOperation.h b/source/blender/compositor/operations/COM_CryptomatteOperation.h
index 1b91358d228..f1bf4cdf624 100644
--- a/source/blender/compositor/operations/COM_CryptomatteOperation.h
+++ b/source/blender/compositor/operations/COM_CryptomatteOperation.h
@@ -18,11 +18,11 @@
#pragma once
-#include "COM_NodeOperation.h"
+#include "COM_MultiThreadedOperation.h"
namespace blender::compositor {
-class CryptomatteOperation : public NodeOperation {
+class CryptomatteOperation : public MultiThreadedOperation {
private:
Vector<float> m_objectIndex;
@@ -35,6 +35,10 @@ class CryptomatteOperation : public NodeOperation {
void executePixel(float output[4], int x, int y, void *data) override;
void addObjectIndex(float objectIndex);
+
+ void update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs) override;
};
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_DifferenceMatteOperation.cc b/source/blender/compositor/operations/COM_DifferenceMatteOperation.cc
index 0acdfc1651f..31714b03b06 100644
--- a/source/blender/compositor/operations/COM_DifferenceMatteOperation.cc
+++ b/source/blender/compositor/operations/COM_DifferenceMatteOperation.cc
@@ -29,6 +29,7 @@ DifferenceMatteOperation::DifferenceMatteOperation()
this->m_inputImage1Program = nullptr;
this->m_inputImage2Program = nullptr;
+ flags.can_be_constant = true;
}
void DifferenceMatteOperation::initExecution()
@@ -86,4 +87,44 @@ void DifferenceMatteOperation::executePixelSampled(float output[4],
}
}
+void DifferenceMatteOperation::update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs)
+{
+ for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) {
+ const float *color1 = it.in(0);
+ const float *color2 = it.in(1);
+
+ float difference = (fabsf(color2[0] - color1[0]) + fabsf(color2[1] - color1[1]) +
+ fabsf(color2[2] - color1[2]));
+
+ /* Average together the distances. */
+ difference = difference / 3.0f;
+
+ const float tolerance = m_settings->t1;
+ const float falloff = m_settings->t2;
+
+ /* Make 100% transparent. */
+ if (difference <= tolerance) {
+ it.out[0] = 0.0f;
+ }
+ /* In the falloff region, make partially transparent. */
+ else if (difference <= falloff + tolerance) {
+ difference = difference - tolerance;
+ const float alpha = difference / falloff;
+ /* Only change if more transparent than before. */
+ if (alpha < color1[3]) {
+ it.out[0] = alpha;
+ }
+ else { /* Leave as before. */
+ it.out[0] = color1[3];
+ }
+ }
+ else {
+ /* Foreground object. */
+ it.out[0] = color1[3];
+ }
+ }
+}
+
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_DifferenceMatteOperation.h b/source/blender/compositor/operations/COM_DifferenceMatteOperation.h
index d3963fee1c1..0a86535d946 100644
--- a/source/blender/compositor/operations/COM_DifferenceMatteOperation.h
+++ b/source/blender/compositor/operations/COM_DifferenceMatteOperation.h
@@ -18,7 +18,7 @@
#pragma once
-#include "COM_MixOperation.h"
+#include "COM_MultiThreadedOperation.h"
namespace blender::compositor {
@@ -26,7 +26,7 @@ namespace blender::compositor {
* this program converts an input color to an output value.
* it assumes we are in sRGB color space.
*/
-class DifferenceMatteOperation : public NodeOperation {
+class DifferenceMatteOperation : public MultiThreadedOperation {
private:
NodeChroma *m_settings;
SocketReader *m_inputImage1Program;
@@ -50,6 +50,10 @@ class DifferenceMatteOperation : public NodeOperation {
{
this->m_settings = nodeChroma;
}
+
+ void update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs) override;
};
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_DisplaceOperation.cc b/source/blender/compositor/operations/COM_DisplaceOperation.cc
index 9f3f5cfe489..a4c01fda7ca 100644
--- a/source/blender/compositor/operations/COM_DisplaceOperation.cc
+++ b/source/blender/compositor/operations/COM_DisplaceOperation.cc
@@ -32,20 +32,30 @@ DisplaceOperation::DisplaceOperation()
this->flags.complex = true;
this->m_inputColorProgram = nullptr;
- this->m_inputVectorProgram = nullptr;
- this->m_inputScaleXProgram = nullptr;
- this->m_inputScaleYProgram = nullptr;
}
void DisplaceOperation::initExecution()
{
this->m_inputColorProgram = this->getInputSocketReader(0);
- this->m_inputVectorProgram = this->getInputSocketReader(1);
- this->m_inputScaleXProgram = this->getInputSocketReader(2);
- this->m_inputScaleYProgram = this->getInputSocketReader(3);
+ NodeOperation *vector = this->getInputSocketReader(1);
+ NodeOperation *scale_x = this->getInputSocketReader(2);
+ NodeOperation *scale_y = this->getInputSocketReader(3);
+ if (execution_model_ == eExecutionModel::Tiled) {
+ vector_read_fn_ = [=](float x, float y, float *out) {
+ vector->readSampled(out, x, y, PixelSampler::Bilinear);
+ };
+ scale_x_read_fn_ = [=](float x, float y, float *out) {
+ scale_x->readSampled(out, x, y, PixelSampler::Nearest);
+ };
+ scale_y_read_fn_ = [=](float x, float y, float *out) {
+ scale_y->readSampled(out, x, y, PixelSampler::Nearest);
+ };
+ }
this->m_width_x4 = this->getWidth() * 4;
this->m_height_x4 = this->getHeight() * 4;
+ input_vector_width_ = vector->getWidth();
+ input_vector_height_ = vector->getHeight();
}
void DisplaceOperation::executePixelSampled(float output[4],
@@ -69,8 +79,8 @@ void DisplaceOperation::executePixelSampled(float output[4],
bool DisplaceOperation::read_displacement(
float x, float y, float xscale, float yscale, const float origin[2], float &r_u, float &r_v)
{
- float width = m_inputVectorProgram->getWidth();
- float height = m_inputVectorProgram->getHeight();
+ float width = input_vector_width_;
+ float height = input_vector_height_;
if (x < 0.0f || x >= width || y < 0.0f || y >= height) {
r_u = 0.0f;
r_v = 0.0f;
@@ -78,7 +88,7 @@ bool DisplaceOperation::read_displacement(
}
float col[4];
- m_inputVectorProgram->readSampled(col, x, y, PixelSampler::Bilinear);
+ vector_read_fn_(x, y, col);
r_u = origin[0] - col[0] * xscale;
r_v = origin[1] - col[1] * yscale;
return true;
@@ -90,9 +100,9 @@ void DisplaceOperation::pixelTransform(const float xy[2], float r_uv[2], float r
float uv[2]; /* temporary variables for derivative estimation */
int num;
- m_inputScaleXProgram->readSampled(col, xy[0], xy[1], PixelSampler::Nearest);
+ scale_x_read_fn_(xy[0], xy[1], col);
float xs = col[0];
- m_inputScaleYProgram->readSampled(col, xy[0], xy[1], PixelSampler::Nearest);
+ scale_y_read_fn_(xy[0], xy[1], col);
float ys = col[0];
/* clamp x and y displacement to triple image resolution -
* to prevent hangs from huge values mistakenly plugged in eg. z buffers */
@@ -146,9 +156,9 @@ void DisplaceOperation::pixelTransform(const float xy[2], float r_uv[2], float r
void DisplaceOperation::deinitExecution()
{
this->m_inputColorProgram = nullptr;
- this->m_inputVectorProgram = nullptr;
- this->m_inputScaleXProgram = nullptr;
- this->m_inputScaleYProgram = nullptr;
+ vector_read_fn_ = nullptr;
+ scale_x_read_fn_ = nullptr;
+ scale_y_read_fn_ = nullptr;
}
bool DisplaceOperation::determineDependingAreaOfInterest(rcti *input,
@@ -195,4 +205,61 @@ bool DisplaceOperation::determineDependingAreaOfInterest(rcti *input,
return false;
}
+void DisplaceOperation::get_area_of_interest(const int input_idx,
+ const rcti &output_area,
+ rcti &r_input_area)
+{
+ switch (input_idx) {
+ case 0: {
+ r_input_area.xmin = 0;
+ r_input_area.ymin = 0;
+ r_input_area.xmax = getInputOperation(input_idx)->getWidth();
+ r_input_area.ymax = getInputOperation(input_idx)->getHeight();
+ break;
+ }
+ case 1: {
+ r_input_area = output_area;
+ expand_area_for_sampler(r_input_area, PixelSampler::Bilinear);
+ break;
+ }
+ default: {
+ r_input_area = output_area;
+ break;
+ }
+ }
+}
+
+void DisplaceOperation::update_memory_buffer_started(MemoryBuffer *UNUSED(output),
+ const rcti &UNUSED(area),
+ Span<MemoryBuffer *> inputs)
+{
+ MemoryBuffer *vector = inputs[1];
+ MemoryBuffer *scale_x = inputs[2];
+ MemoryBuffer *scale_y = inputs[3];
+ vector_read_fn_ = [=](float x, float y, float *out) { vector->read_elem_bilinear(x, y, out); };
+ scale_x_read_fn_ = [=](float x, float y, float *out) { scale_x->read_elem_checked(x, y, out); };
+ scale_y_read_fn_ = [=](float x, float y, float *out) { scale_y->read_elem_checked(x, y, out); };
+}
+
+void DisplaceOperation::update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs)
+{
+ const MemoryBuffer *input_color = inputs[0];
+ for (BuffersIterator<float> it = output->iterate_with({}, area); !it.is_end(); ++it) {
+ const float xy[2] = {(float)it.x, (float)it.y};
+ float uv[2];
+ float deriv[2][2];
+
+ pixelTransform(xy, uv, deriv);
+ if (is_zero_v2(deriv[0]) && is_zero_v2(deriv[1])) {
+ input_color->read_elem_bilinear(uv[0], uv[1], it.out);
+ }
+ else {
+ /* EWA filtering (without nearest it gets blurry with NO distortion). */
+ input_color->read_elem_filtered(uv[0], uv[1], deriv[0], deriv[1], it.out);
+ }
+ }
+}
+
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_DisplaceOperation.h b/source/blender/compositor/operations/COM_DisplaceOperation.h
index fd82692f687..5be914ab672 100644
--- a/source/blender/compositor/operations/COM_DisplaceOperation.h
+++ b/source/blender/compositor/operations/COM_DisplaceOperation.h
@@ -18,23 +18,27 @@
#pragma once
-#include "COM_NodeOperation.h"
+#include "COM_MultiThreadedOperation.h"
namespace blender::compositor {
-class DisplaceOperation : public NodeOperation {
+class DisplaceOperation : public MultiThreadedOperation {
private:
/**
* Cached reference to the inputProgram
*/
SocketReader *m_inputColorProgram;
- SocketReader *m_inputVectorProgram;
- SocketReader *m_inputScaleXProgram;
- SocketReader *m_inputScaleYProgram;
float m_width_x4;
float m_height_x4;
+ int input_vector_width_;
+ int input_vector_height_;
+
+ std::function<void(float x, float y, float *out)> vector_read_fn_;
+ std::function<void(float x, float y, float *out)> scale_x_read_fn_;
+ std::function<void(float x, float y, float *out)> scale_y_read_fn_;
+
public:
DisplaceOperation();
@@ -62,6 +66,14 @@ class DisplaceOperation : public NodeOperation {
*/
void deinitExecution() override;
+ void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override;
+ void update_memory_buffer_started(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs) override;
+ void update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs) override;
+
private:
bool read_displacement(
float x, float y, float xscale, float yscale, const float origin[2], float &r_u, float &r_v);
diff --git a/source/blender/compositor/operations/COM_DisplaceSimpleOperation.cc b/source/blender/compositor/operations/COM_DisplaceSimpleOperation.cc
index f4b77f5d32c..e1c531bd49e 100644
--- a/source/blender/compositor/operations/COM_DisplaceSimpleOperation.cc
+++ b/source/blender/compositor/operations/COM_DisplaceSimpleOperation.cc
@@ -132,4 +132,56 @@ bool DisplaceSimpleOperation::determineDependingAreaOfInterest(rcti *input,
return false;
}
+void DisplaceSimpleOperation::get_area_of_interest(const int input_idx,
+ const rcti &output_area,
+ rcti &r_input_area)
+{
+ switch (input_idx) {
+ case 0: {
+ r_input_area.xmin = 0;
+ r_input_area.ymin = 0;
+ r_input_area.xmax = getInputOperation(input_idx)->getWidth();
+ r_input_area.ymax = getInputOperation(input_idx)->getHeight();
+ break;
+ }
+ default: {
+ r_input_area = output_area;
+ break;
+ }
+ }
+}
+
+void DisplaceSimpleOperation::update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs)
+{
+ const float width = this->getWidth();
+ const float height = this->getHeight();
+ const MemoryBuffer *input_color = inputs[0];
+ for (BuffersIterator<float> it = output->iterate_with(inputs.drop_front(1), area); !it.is_end();
+ ++it) {
+ float scale_x = *it.in(1);
+ float scale_y = *it.in(2);
+
+ /* Clamp x and y displacement to triple image resolution -
+ * to prevent hangs from huge values mistakenly plugged in eg. z buffers. */
+ CLAMP(scale_x, -m_width_x4, m_width_x4);
+ CLAMP(scale_y, -m_height_x4, m_height_x4);
+
+ /* Main displacement in pixel space. */
+ const float *vector = it.in(0);
+ const float p_dx = vector[0] * scale_x;
+ const float p_dy = vector[1] * scale_y;
+
+ /* Displaced pixel in uv coords, for image sampling. */
+ /* Clamp nodes to avoid glitches. */
+ float u = it.x - p_dx + 0.5f;
+ float v = it.y - p_dy + 0.5f;
+ CLAMP(u, 0.0f, width - 1.0f);
+ CLAMP(v, 0.0f, height - 1.0f);
+
+ input_color->read_elem_checked(u, v, it.out);
+ }
+}
+
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_DisplaceSimpleOperation.h b/source/blender/compositor/operations/COM_DisplaceSimpleOperation.h
index 15e6fcd0523..99f52155466 100644
--- a/source/blender/compositor/operations/COM_DisplaceSimpleOperation.h
+++ b/source/blender/compositor/operations/COM_DisplaceSimpleOperation.h
@@ -18,11 +18,11 @@
#pragma once
-#include "COM_NodeOperation.h"
+#include "COM_MultiThreadedOperation.h"
namespace blender::compositor {
-class DisplaceSimpleOperation : public NodeOperation {
+class DisplaceSimpleOperation : public MultiThreadedOperation {
private:
/**
* Cached reference to the inputProgram
@@ -59,6 +59,11 @@ class DisplaceSimpleOperation : public NodeOperation {
* Deinitialize the execution
*/
void deinitExecution() override;
+
+ void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override;
+ void update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs) override;
};
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_DistanceRGBMatteOperation.cc b/source/blender/compositor/operations/COM_DistanceRGBMatteOperation.cc
index 1b3403cbb29..8155ff769a0 100644
--- a/source/blender/compositor/operations/COM_DistanceRGBMatteOperation.cc
+++ b/source/blender/compositor/operations/COM_DistanceRGBMatteOperation.cc
@@ -29,6 +29,7 @@ DistanceRGBMatteOperation::DistanceRGBMatteOperation()
this->m_inputImageProgram = nullptr;
this->m_inputKeyProgram = nullptr;
+ flags.can_be_constant = true;
}
void DistanceRGBMatteOperation::initExecution()
@@ -43,7 +44,7 @@ void DistanceRGBMatteOperation::deinitExecution()
this->m_inputKeyProgram = nullptr;
}
-float DistanceRGBMatteOperation::calculateDistance(float key[4], float image[4])
+float DistanceRGBMatteOperation::calculateDistance(const float key[4], const float image[4])
{
return len_v3v3(key, image);
}
@@ -93,4 +94,43 @@ void DistanceRGBMatteOperation::executePixelSampled(float output[4],
}
}
+void DistanceRGBMatteOperation::update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs)
+{
+ for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) {
+ const float *in_image = it.in(0);
+ const float *in_key = it.in(1);
+
+ float distance = this->calculateDistance(in_key, in_image);
+ const float tolerance = this->m_settings->t1;
+ const float falloff = this->m_settings->t2;
+
+ /* Store matte(alpha) value in [0] to go with
+ * COM_SetAlphaMultiplyOperation and the Value output.
+ */
+
+ /* Make 100% transparent. */
+ if (distance < tolerance) {
+ it.out[0] = 0.0f;
+ }
+ /* In the falloff region, make partially transparent. */
+ else if (distance < falloff + tolerance) {
+ distance = distance - tolerance;
+ const float alpha = distance / falloff;
+ /* Only change if more transparent than before. */
+ if (alpha < in_image[3]) {
+ it.out[0] = alpha;
+ }
+ else { /* Leave as before. */
+ it.out[0] = in_image[3];
+ }
+ }
+ else {
+ /* Leave as before. */
+ it.out[0] = in_image[3];
+ }
+ }
+}
+
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_DistanceRGBMatteOperation.h b/source/blender/compositor/operations/COM_DistanceRGBMatteOperation.h
index 6fe603233b7..ba6682214ae 100644
--- a/source/blender/compositor/operations/COM_DistanceRGBMatteOperation.h
+++ b/source/blender/compositor/operations/COM_DistanceRGBMatteOperation.h
@@ -18,7 +18,7 @@
#pragma once
-#include "COM_MixOperation.h"
+#include "COM_MultiThreadedOperation.h"
namespace blender::compositor {
@@ -26,13 +26,13 @@ namespace blender::compositor {
* this program converts an input color to an output value.
* it assumes we are in sRGB color space.
*/
-class DistanceRGBMatteOperation : public NodeOperation {
+class DistanceRGBMatteOperation : public MultiThreadedOperation {
protected:
NodeChroma *m_settings;
SocketReader *m_inputImageProgram;
SocketReader *m_inputKeyProgram;
- virtual float calculateDistance(float key[4], float image[4]);
+ virtual float calculateDistance(const float key[4], const float image[4]);
public:
/**
@@ -52,6 +52,10 @@ class DistanceRGBMatteOperation : public NodeOperation {
{
this->m_settings = nodeChroma;
}
+
+ void update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs) override;
};
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_DistanceYCCMatteOperation.cc b/source/blender/compositor/operations/COM_DistanceYCCMatteOperation.cc
index 597545dd706..50e473ea5b3 100644
--- a/source/blender/compositor/operations/COM_DistanceYCCMatteOperation.cc
+++ b/source/blender/compositor/operations/COM_DistanceYCCMatteOperation.cc
@@ -21,7 +21,7 @@
namespace blender::compositor {
-float DistanceYCCMatteOperation::calculateDistance(float key[4], float image[4])
+float DistanceYCCMatteOperation::calculateDistance(const float key[4], const float image[4])
{
/* only measure the second 2 values */
return len_v2v2(key + 1, image + 1);
diff --git a/source/blender/compositor/operations/COM_DistanceYCCMatteOperation.h b/source/blender/compositor/operations/COM_DistanceYCCMatteOperation.h
index a87e885e5d8..0e178fddc39 100644
--- a/source/blender/compositor/operations/COM_DistanceYCCMatteOperation.h
+++ b/source/blender/compositor/operations/COM_DistanceYCCMatteOperation.h
@@ -29,7 +29,7 @@ namespace blender::compositor {
*/
class DistanceYCCMatteOperation : public DistanceRGBMatteOperation {
protected:
- float calculateDistance(float key[4], float image[4]) override;
+ float calculateDistance(const float key[4], const float image[4]) override;
};
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_FastGaussianBlurOperation.cc b/source/blender/compositor/operations/COM_FastGaussianBlurOperation.cc
index 3804e6ec646..e0fc45811cb 100644
--- a/source/blender/compositor/operations/COM_FastGaussianBlurOperation.cc
+++ b/source/blender/compositor/operations/COM_FastGaussianBlurOperation.cc
@@ -62,6 +62,13 @@ bool FastGaussianBlurOperation::determineDependingAreaOfInterest(
return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output);
}
+void FastGaussianBlurOperation::init_data()
+{
+ BlurBaseOperation::init_data();
+ this->m_sx = this->m_data.sizex * this->m_size / 2.0f;
+ this->m_sy = this->m_data.sizey * this->m_size / 2.0f;
+}
+
void FastGaussianBlurOperation::initExecution()
{
BlurBaseOperation::initExecution();
@@ -117,6 +124,7 @@ void FastGaussianBlurOperation::IIR_gauss(MemoryBuffer *src,
unsigned int chan,
unsigned int xy)
{
+ BLI_assert(!src->is_a_single_elem());
double q, q2, sc, cf[4], tsM[9], tsu[3], tsv[3];
double *X, *Y, *W;
const unsigned int src_width = src->getWidth();
@@ -257,6 +265,64 @@ void FastGaussianBlurOperation::IIR_gauss(MemoryBuffer *src,
#undef YVV
}
+void FastGaussianBlurOperation::get_area_of_interest(const int input_idx,
+ const rcti &output_area,
+ rcti &r_input_area)
+{
+ switch (input_idx) {
+ case IMAGE_INPUT_INDEX:
+ r_input_area.xmin = 0;
+ r_input_area.xmax = getWidth();
+ r_input_area.ymin = 0;
+ r_input_area.ymax = getHeight();
+ break;
+ default:
+ BlurBaseOperation::get_area_of_interest(input_idx, output_area, r_input_area);
+ return;
+ }
+}
+
+void FastGaussianBlurOperation::update_memory_buffer_started(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs)
+{
+ /* TODO(manzanilla): Add a render test and make #IIR_gauss multi-threaded with support for
+ * an output buffer. */
+ const MemoryBuffer *input = inputs[IMAGE_INPUT_INDEX];
+ MemoryBuffer *image = nullptr;
+ const bool is_full_output = BLI_rcti_compare(&output->get_rect(), &area);
+ if (is_full_output) {
+ image = output;
+ }
+ else {
+ image = new MemoryBuffer(getOutputSocket()->getDataType(), area);
+ }
+ image->copy_from(input, area);
+
+ if ((this->m_sx == this->m_sy) && (this->m_sx > 0.0f)) {
+ for (const int c : IndexRange(COM_DATA_TYPE_COLOR_CHANNELS)) {
+ IIR_gauss(image, this->m_sx, c, 3);
+ }
+ }
+ else {
+ if (this->m_sx > 0.0f) {
+ for (const int c : IndexRange(COM_DATA_TYPE_COLOR_CHANNELS)) {
+ IIR_gauss(image, this->m_sx, c, 1);
+ }
+ }
+ if (this->m_sy > 0.0f) {
+ for (const int c : IndexRange(COM_DATA_TYPE_COLOR_CHANNELS)) {
+ IIR_gauss(image, this->m_sy, c, 2);
+ }
+ }
+ }
+
+ if (!is_full_output) {
+ output->copy_from(image, area);
+ delete image;
+ }
+}
+
FastGaussianBlurValueOperation::FastGaussianBlurValueOperation()
{
this->addInputSocket(DataType::Value);
@@ -341,4 +407,44 @@ void *FastGaussianBlurValueOperation::initializeTileData(rcti *rect)
return this->m_iirgaus;
}
+void FastGaussianBlurValueOperation::get_area_of_interest(const int UNUSED(input_idx),
+ const rcti &UNUSED(output_area),
+ rcti &r_input_area)
+{
+ r_input_area.xmin = 0;
+ r_input_area.xmax = getWidth();
+ r_input_area.ymin = 0;
+ r_input_area.ymax = getHeight();
+}
+
+void FastGaussianBlurValueOperation::update_memory_buffer_started(MemoryBuffer *UNUSED(output),
+ const rcti &UNUSED(area),
+ Span<MemoryBuffer *> inputs)
+{
+ if (m_iirgaus == nullptr) {
+ const MemoryBuffer *image = inputs[0];
+ MemoryBuffer *gauss = new MemoryBuffer(*image);
+ FastGaussianBlurOperation::IIR_gauss(gauss, m_sigma, 0, 3);
+ m_iirgaus = gauss;
+ }
+}
+
+void FastGaussianBlurValueOperation::update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs)
+{
+ MemoryBuffer *image = inputs[0];
+ BuffersIterator<float> it = output->iterate_with({image, m_iirgaus}, area);
+ if (this->m_overlay == FAST_GAUSS_OVERLAY_MIN) {
+ for (; !it.is_end(); ++it) {
+ *it.out = MIN2(*it.in(0), *it.in(1));
+ }
+ }
+ else if (this->m_overlay == FAST_GAUSS_OVERLAY_MAX) {
+ for (; !it.is_end(); ++it) {
+ *it.out = MAX2(*it.in(0), *it.in(1));
+ }
+ }
+}
+
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_FastGaussianBlurOperation.h b/source/blender/compositor/operations/COM_FastGaussianBlurOperation.h
index c25afe6c4a4..f42fc76a119 100644
--- a/source/blender/compositor/operations/COM_FastGaussianBlurOperation.h
+++ b/source/blender/compositor/operations/COM_FastGaussianBlurOperation.h
@@ -38,8 +38,19 @@ class FastGaussianBlurOperation : public BlurBaseOperation {
static void IIR_gauss(MemoryBuffer *src, float sigma, unsigned int channel, unsigned int xy);
void *initializeTileData(rcti *rect) override;
+ void init_data() override;
void deinitExecution() override;
void initExecution() override;
+
+ void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override;
+ void update_memory_buffer_started(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs) override;
+ void update_memory_buffer_partial(MemoryBuffer *UNUSED(output),
+ const rcti &UNUSED(area),
+ Span<MemoryBuffer *> UNUSED(inputs)) override
+ {
+ }
};
enum {
@@ -48,7 +59,7 @@ enum {
FAST_GAUSS_OVERLAY_MAX = 1,
};
-class FastGaussianBlurValueOperation : public NodeOperation {
+class FastGaussianBlurValueOperation : public MultiThreadedOperation {
private:
float m_sigma;
MemoryBuffer *m_iirgaus;
@@ -80,6 +91,14 @@ class FastGaussianBlurValueOperation : public NodeOperation {
{
this->m_overlay = overlay;
}
+
+ void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override;
+ void update_memory_buffer_started(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs) override;
+ void update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs) override;
};
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_FlipOperation.cc b/source/blender/compositor/operations/COM_FlipOperation.cc
index 8afbec4ddbe..d0dc6c0b570 100644
--- a/source/blender/compositor/operations/COM_FlipOperation.cc
+++ b/source/blender/compositor/operations/COM_FlipOperation.cc
@@ -75,4 +75,42 @@ bool FlipOperation::determineDependingAreaOfInterest(rcti *input,
return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output);
}
+void FlipOperation::get_area_of_interest(const int input_idx,
+ const rcti &output_area,
+ rcti &r_input_area)
+{
+ BLI_assert(input_idx == 0);
+ UNUSED_VARS_NDEBUG(input_idx);
+ if (this->m_flipX) {
+ const int w = (int)this->getWidth() - 1;
+ r_input_area.xmax = (w - output_area.xmin) + 1;
+ r_input_area.xmin = (w - output_area.xmax) - 1;
+ }
+ else {
+ r_input_area.xmin = output_area.xmin;
+ r_input_area.xmax = output_area.xmax;
+ }
+ if (this->m_flipY) {
+ const int h = (int)this->getHeight() - 1;
+ r_input_area.ymax = (h - output_area.ymin) + 1;
+ r_input_area.ymin = (h - output_area.ymax) - 1;
+ }
+ else {
+ r_input_area.ymin = output_area.ymin;
+ r_input_area.ymax = output_area.ymax;
+ }
+}
+
+void FlipOperation::update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs)
+{
+ const MemoryBuffer *input_img = inputs[0];
+ for (BuffersIterator<float> it = output->iterate_with({}, area); !it.is_end(); ++it) {
+ const int nx = this->m_flipX ? ((int)this->getWidth() - 1) - it.x : it.x;
+ const int ny = this->m_flipY ? ((int)this->getHeight() - 1) - it.y : it.y;
+ input_img->read_elem(nx, ny, it.out);
+ }
+}
+
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_FlipOperation.h b/source/blender/compositor/operations/COM_FlipOperation.h
index f26d587fde6..dba7f82c341 100644
--- a/source/blender/compositor/operations/COM_FlipOperation.h
+++ b/source/blender/compositor/operations/COM_FlipOperation.h
@@ -18,11 +18,11 @@
#pragma once
-#include "COM_NodeOperation.h"
+#include "COM_MultiThreadedOperation.h"
namespace blender::compositor {
-class FlipOperation : public NodeOperation {
+class FlipOperation : public MultiThreadedOperation {
private:
SocketReader *m_inputOperation;
bool m_flipX;
@@ -45,6 +45,11 @@ class FlipOperation : public NodeOperation {
{
this->m_flipY = flipY;
}
+
+ void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override;
+ void update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs) override;
};
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_GammaCorrectOperation.cc b/source/blender/compositor/operations/COM_GammaCorrectOperation.cc
index 16b79fddd06..1bff3b965c6 100644
--- a/source/blender/compositor/operations/COM_GammaCorrectOperation.cc
+++ b/source/blender/compositor/operations/COM_GammaCorrectOperation.cc
@@ -26,6 +26,7 @@ GammaCorrectOperation::GammaCorrectOperation()
this->addInputSocket(DataType::Color);
this->addOutputSocket(DataType::Color);
this->m_inputProgram = nullptr;
+ flags.can_be_constant = true;
}
void GammaCorrectOperation::initExecution()
{
@@ -58,6 +59,34 @@ void GammaCorrectOperation::executePixelSampled(float output[4],
}
}
+void GammaCorrectOperation::update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs)
+{
+ const MemoryBuffer *input = inputs[0];
+ for (BuffersIterator<float> it = output->iterate_with({}, area); !it.is_end(); ++it) {
+ float color[4];
+ input->read_elem(it.x, it.y, color);
+ if (color[3] > 0.0f) {
+ color[0] /= color[3];
+ color[1] /= color[3];
+ color[2] /= color[3];
+ }
+
+ /* Check for negative to avoid nan's. */
+ it.out[0] = color[0] > 0.0f ? color[0] * color[0] : 0.0f;
+ it.out[1] = color[1] > 0.0f ? color[1] * color[1] : 0.0f;
+ it.out[2] = color[2] > 0.0f ? color[2] * color[2] : 0.0f;
+ it.out[3] = color[3];
+
+ if (color[3] > 0.0f) {
+ it.out[0] *= color[3];
+ it.out[1] *= color[3];
+ it.out[2] *= color[3];
+ }
+ }
+}
+
void GammaCorrectOperation::deinitExecution()
{
this->m_inputProgram = nullptr;
@@ -68,6 +97,7 @@ GammaUncorrectOperation::GammaUncorrectOperation()
this->addInputSocket(DataType::Color);
this->addOutputSocket(DataType::Color);
this->m_inputProgram = nullptr;
+ flags.can_be_constant = true;
}
void GammaUncorrectOperation::initExecution()
{
@@ -100,6 +130,33 @@ void GammaUncorrectOperation::executePixelSampled(float output[4],
}
}
+void GammaUncorrectOperation::update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs)
+{
+ const MemoryBuffer *input = inputs[0];
+ for (BuffersIterator<float> it = output->iterate_with({}, area); !it.is_end(); ++it) {
+ float color[4];
+ input->read_elem(it.x, it.y, color);
+ if (color[3] > 0.0f) {
+ color[0] /= color[3];
+ color[1] /= color[3];
+ color[2] /= color[3];
+ }
+
+ it.out[0] = color[0] > 0.0f ? sqrtf(color[0]) : 0.0f;
+ it.out[1] = color[1] > 0.0f ? sqrtf(color[1]) : 0.0f;
+ it.out[2] = color[2] > 0.0f ? sqrtf(color[2]) : 0.0f;
+ it.out[3] = color[3];
+
+ if (color[3] > 0.0f) {
+ it.out[0] *= color[3];
+ it.out[1] *= color[3];
+ it.out[2] *= color[3];
+ }
+ }
+}
+
void GammaUncorrectOperation::deinitExecution()
{
this->m_inputProgram = nullptr;
diff --git a/source/blender/compositor/operations/COM_GammaCorrectOperation.h b/source/blender/compositor/operations/COM_GammaCorrectOperation.h
index ac3d45b94b1..2a9fde70e87 100644
--- a/source/blender/compositor/operations/COM_GammaCorrectOperation.h
+++ b/source/blender/compositor/operations/COM_GammaCorrectOperation.h
@@ -18,11 +18,11 @@
#pragma once
-#include "COM_NodeOperation.h"
+#include "COM_MultiThreadedOperation.h"
namespace blender::compositor {
-class GammaCorrectOperation : public NodeOperation {
+class GammaCorrectOperation : public MultiThreadedOperation {
private:
/**
* Cached reference to the inputProgram
@@ -46,9 +46,13 @@ class GammaCorrectOperation : public NodeOperation {
* Deinitialize the execution
*/
void deinitExecution() override;
+
+ void update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs) override;
};
-class GammaUncorrectOperation : public NodeOperation {
+class GammaUncorrectOperation : public MultiThreadedOperation {
private:
/**
* Cached reference to the inputProgram
@@ -72,6 +76,10 @@ class GammaUncorrectOperation : public NodeOperation {
* Deinitialize the execution
*/
void deinitExecution() override;
+
+ void update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs) override;
};
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_GaussianAlphaBlurBaseOperation.cc b/source/blender/compositor/operations/COM_GaussianAlphaBlurBaseOperation.cc
new file mode 100644
index 00000000000..9bdc652b466
--- /dev/null
+++ b/source/blender/compositor/operations/COM_GaussianAlphaBlurBaseOperation.cc
@@ -0,0 +1,168 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Copyright 2021, Blender Foundation.
+ */
+
+#include "COM_GaussianAlphaBlurBaseOperation.h"
+
+namespace blender::compositor {
+
+GaussianAlphaBlurBaseOperation::GaussianAlphaBlurBaseOperation(eDimension dim)
+ : BlurBaseOperation(DataType::Value)
+{
+ this->m_gausstab = nullptr;
+ this->m_filtersize = 0;
+ this->m_falloff = -1; /* Intentionally invalid, so we can detect uninitialized values. */
+ dimension_ = dim;
+}
+
+void GaussianAlphaBlurBaseOperation::init_data()
+{
+ BlurBaseOperation::init_data();
+ if (execution_model_ == eExecutionModel::FullFrame) {
+ rad_ = max_ff(m_size * this->get_blur_size(dimension_), 0.0f);
+ rad_ = min_ff(rad_, MAX_GAUSSTAB_RADIUS);
+ m_filtersize = min_ii(ceil(rad_), MAX_GAUSSTAB_RADIUS);
+ }
+}
+
+void GaussianAlphaBlurBaseOperation::initExecution()
+{
+ BlurBaseOperation::initExecution();
+ if (execution_model_ == eExecutionModel::FullFrame) {
+ m_gausstab = BlurBaseOperation::make_gausstab(rad_, m_filtersize);
+ m_distbuf_inv = BlurBaseOperation::make_dist_fac_inverse(rad_, m_filtersize, m_falloff);
+ }
+}
+
+void GaussianAlphaBlurBaseOperation::deinitExecution()
+{
+ BlurBaseOperation::deinitExecution();
+
+ if (this->m_gausstab) {
+ MEM_freeN(this->m_gausstab);
+ this->m_gausstab = nullptr;
+ }
+
+ if (this->m_distbuf_inv) {
+ MEM_freeN(this->m_distbuf_inv);
+ this->m_distbuf_inv = nullptr;
+ }
+}
+
+void GaussianAlphaBlurBaseOperation::get_area_of_interest(const int input_idx,
+ const rcti &output_area,
+ rcti &r_input_area)
+{
+ if (input_idx != IMAGE_INPUT_INDEX) {
+ BlurBaseOperation::get_area_of_interest(input_idx, output_area, r_input_area);
+ return;
+ }
+
+ r_input_area = output_area;
+ switch (dimension_) {
+ case eDimension::X:
+ r_input_area.xmin = output_area.xmin - m_filtersize - 1;
+ r_input_area.xmax = output_area.xmax + m_filtersize + 1;
+ break;
+ case eDimension::Y:
+ r_input_area.ymin = output_area.ymin - m_filtersize - 1;
+ r_input_area.ymax = output_area.ymax + m_filtersize + 1;
+ break;
+ }
+}
+
+BLI_INLINE float finv_test(const float f, const bool test)
+{
+ return (LIKELY(test == false)) ? f : 1.0f - f;
+}
+
+void GaussianAlphaBlurBaseOperation::update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs)
+{
+ MemoryBuffer *input = inputs[IMAGE_INPUT_INDEX];
+ const rcti &input_rect = input->get_rect();
+ BuffersIterator<float> it = output->iterate_with({input}, area);
+
+ int min_input_coord = -1;
+ int max_input_coord = -1;
+ int elem_stride = -1;
+ std::function<int()> get_current_coord;
+ switch (dimension_) {
+ case eDimension::X:
+ min_input_coord = input_rect.xmin;
+ max_input_coord = input_rect.xmax;
+ get_current_coord = [&] { return it.x; };
+ elem_stride = input->elem_stride;
+ break;
+ case eDimension::Y:
+ min_input_coord = input_rect.ymin;
+ max_input_coord = input_rect.ymax;
+ get_current_coord = [&] { return it.y; };
+ elem_stride = input->row_stride;
+ break;
+ }
+
+ for (; !it.is_end(); ++it) {
+ const int coord = get_current_coord();
+ const int coord_min = max_ii(coord - m_filtersize, min_input_coord);
+ const int coord_max = min_ii(coord + m_filtersize + 1, max_input_coord);
+
+ /* *** This is the main part which is different to #GaussianBlurBaseOperation. *** */
+ /* Gauss. */
+ float alpha_accum = 0.0f;
+ float multiplier_accum = 0.0f;
+
+ /* Dilate. */
+ const bool do_invert = m_do_subtract;
+ /* Init with the current color to avoid unneeded lookups. */
+ float value_max = finv_test(*it.in(0), do_invert);
+ float distfacinv_max = 1.0f; /* 0 to 1 */
+
+ const int step = QualityStepHelper::getStep();
+ const float *in = it.in(0) + ((intptr_t)coord_min - coord) * elem_stride;
+ const int in_stride = elem_stride * step;
+ int index = (coord_min - coord) + m_filtersize;
+ const int index_end = index + (coord_max - coord_min);
+ for (; index < index_end; in += in_stride, index += step) {
+ float value = finv_test(*in, do_invert);
+
+ /* Gauss. */
+ float multiplier = m_gausstab[index];
+ alpha_accum += value * multiplier;
+ multiplier_accum += multiplier;
+
+ /* Dilate - find most extreme color. */
+ if (value > value_max) {
+ multiplier = m_distbuf_inv[index];
+ value *= multiplier;
+ if (value > value_max) {
+ value_max = value;
+ distfacinv_max = multiplier;
+ }
+ }
+ }
+
+ /* Blend between the max value and gauss blue - gives nice feather. */
+ const float value_blur = alpha_accum / multiplier_accum;
+ const float value_final = (value_max * distfacinv_max) +
+ (value_blur * (1.0f - distfacinv_max));
+ *it.out = finv_test(value_final, do_invert);
+ }
+}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_GaussianAlphaBlurBaseOperation.h b/source/blender/compositor/operations/COM_GaussianAlphaBlurBaseOperation.h
new file mode 100644
index 00000000000..d7ca975ca0a
--- /dev/null
+++ b/source/blender/compositor/operations/COM_GaussianAlphaBlurBaseOperation.h
@@ -0,0 +1,62 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Copyright 2021, Blender Foundation.
+ */
+
+#pragma once
+
+#include "COM_BlurBaseOperation.h"
+
+namespace blender::compositor {
+
+class GaussianAlphaBlurBaseOperation : public BlurBaseOperation {
+ protected:
+ float *m_gausstab;
+ float *m_distbuf_inv;
+ int m_falloff; /* Falloff for #distbuf_inv. */
+ bool m_do_subtract;
+ int m_filtersize;
+ float rad_;
+ eDimension dimension_;
+
+ public:
+ GaussianAlphaBlurBaseOperation(eDimension dim);
+
+ virtual void init_data() override;
+ virtual void initExecution() override;
+ virtual void deinitExecution() override;
+
+ void get_area_of_interest(const int input_idx,
+ const rcti &output_area,
+ rcti &r_input_area) final;
+ void update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs) final;
+
+ /**
+ * Set subtract for Dilate/Erode functionality
+ */
+ void setSubtract(bool subtract)
+ {
+ this->m_do_subtract = subtract;
+ }
+ void setFalloff(int falloff)
+ {
+ this->m_falloff = falloff;
+ }
+};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_GaussianAlphaXBlurOperation.cc b/source/blender/compositor/operations/COM_GaussianAlphaXBlurOperation.cc
index 7ca5dc4ca76..6710ed3cf5b 100644
--- a/source/blender/compositor/operations/COM_GaussianAlphaXBlurOperation.cc
+++ b/source/blender/compositor/operations/COM_GaussianAlphaXBlurOperation.cc
@@ -24,11 +24,9 @@
namespace blender::compositor {
-GaussianAlphaXBlurOperation::GaussianAlphaXBlurOperation() : BlurBaseOperation(DataType::Value)
+GaussianAlphaXBlurOperation::GaussianAlphaXBlurOperation()
+ : GaussianAlphaBlurBaseOperation(eDimension::X)
{
- this->m_gausstab = nullptr;
- this->m_filtersize = 0;
- this->m_falloff = -1; /* intentionally invalid, so we can detect uninitialized values */
}
void *GaussianAlphaXBlurOperation::initializeTileData(rcti * /*rect*/)
@@ -44,12 +42,11 @@ void *GaussianAlphaXBlurOperation::initializeTileData(rcti * /*rect*/)
void GaussianAlphaXBlurOperation::initExecution()
{
- /* Until we support size input - comment this. */
- // BlurBaseOperation::initExecution();
+ GaussianAlphaBlurBaseOperation::initExecution();
initMutex();
- if (this->m_sizeavailable) {
+ if (this->m_sizeavailable && execution_model_ == eExecutionModel::Tiled) {
float rad = max_ff(m_size * m_data.sizex, 0.0f);
m_filtersize = min_ii(ceil(rad), MAX_GAUSSTAB_RADIUS);
@@ -144,7 +141,7 @@ void GaussianAlphaXBlurOperation::executePixel(float output[4], int x, int y, vo
void GaussianAlphaXBlurOperation::deinitExecution()
{
- BlurBaseOperation::deinitExecution();
+ GaussianAlphaBlurBaseOperation::deinitExecution();
if (this->m_gausstab) {
MEM_freeN(this->m_gausstab);
diff --git a/source/blender/compositor/operations/COM_GaussianAlphaXBlurOperation.h b/source/blender/compositor/operations/COM_GaussianAlphaXBlurOperation.h
index 949956fae04..2a44c639665 100644
--- a/source/blender/compositor/operations/COM_GaussianAlphaXBlurOperation.h
+++ b/source/blender/compositor/operations/COM_GaussianAlphaXBlurOperation.h
@@ -18,18 +18,13 @@
#pragma once
-#include "COM_BlurBaseOperation.h"
-#include "COM_NodeOperation.h"
+#include "COM_GaussianAlphaBlurBaseOperation.h"
namespace blender::compositor {
-class GaussianAlphaXBlurOperation : public BlurBaseOperation {
+/* TODO(manzanilla): everything to be removed with tiled implementation except the constructor. */
+class GaussianAlphaXBlurOperation : public GaussianAlphaBlurBaseOperation {
private:
- float *m_gausstab;
- float *m_distbuf_inv;
- int m_falloff; /* falloff for distbuf_inv */
- bool m_do_subtract;
- int m_filtersize;
void updateGauss();
public:
@@ -54,18 +49,6 @@ class GaussianAlphaXBlurOperation : public BlurBaseOperation {
bool determineDependingAreaOfInterest(rcti *input,
ReadBufferOperation *readOperation,
rcti *output) override;
-
- /**
- * Set subtract for Dilate/Erode functionality
- */
- void setSubtract(bool subtract)
- {
- this->m_do_subtract = subtract;
- }
- void setFalloff(int falloff)
- {
- this->m_falloff = falloff;
- }
};
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_GaussianAlphaYBlurOperation.cc b/source/blender/compositor/operations/COM_GaussianAlphaYBlurOperation.cc
index d2385a972dd..09aeddb6573 100644
--- a/source/blender/compositor/operations/COM_GaussianAlphaYBlurOperation.cc
+++ b/source/blender/compositor/operations/COM_GaussianAlphaYBlurOperation.cc
@@ -24,11 +24,9 @@
namespace blender::compositor {
-GaussianAlphaYBlurOperation::GaussianAlphaYBlurOperation() : BlurBaseOperation(DataType::Value)
+GaussianAlphaYBlurOperation::GaussianAlphaYBlurOperation()
+ : GaussianAlphaBlurBaseOperation(eDimension::Y)
{
- this->m_gausstab = nullptr;
- this->m_filtersize = 0;
- this->m_falloff = -1; /* intentionally invalid, so we can detect uninitialized values */
}
void *GaussianAlphaYBlurOperation::initializeTileData(rcti * /*rect*/)
@@ -42,14 +40,14 @@ void *GaussianAlphaYBlurOperation::initializeTileData(rcti * /*rect*/)
return buffer;
}
+/* TODO(manzanilla): to be removed with tiled implementation. */
void GaussianAlphaYBlurOperation::initExecution()
{
- /* Until we support size input - comment this. */
- // BlurBaseOperation::initExecution();
+ GaussianAlphaBlurBaseOperation::initExecution();
initMutex();
- if (this->m_sizeavailable) {
+ if (this->m_sizeavailable && execution_model_ == eExecutionModel::Tiled) {
float rad = max_ff(m_size * m_data.sizey, 0.0f);
m_filtersize = min_ii(ceil(rad), MAX_GAUSSTAB_RADIUS);
@@ -58,6 +56,7 @@ void GaussianAlphaYBlurOperation::initExecution()
}
}
+/* TODO(manzanilla): to be removed with tiled implementation. */
void GaussianAlphaYBlurOperation::updateGauss()
{
if (this->m_gausstab == nullptr) {
@@ -143,7 +142,7 @@ void GaussianAlphaYBlurOperation::executePixel(float output[4], int x, int y, vo
void GaussianAlphaYBlurOperation::deinitExecution()
{
- BlurBaseOperation::deinitExecution();
+ GaussianAlphaBlurBaseOperation::deinitExecution();
if (this->m_gausstab) {
MEM_freeN(this->m_gausstab);
diff --git a/source/blender/compositor/operations/COM_GaussianAlphaYBlurOperation.h b/source/blender/compositor/operations/COM_GaussianAlphaYBlurOperation.h
index d25770386c4..ef01f7e0f92 100644
--- a/source/blender/compositor/operations/COM_GaussianAlphaYBlurOperation.h
+++ b/source/blender/compositor/operations/COM_GaussianAlphaYBlurOperation.h
@@ -18,18 +18,13 @@
#pragma once
-#include "COM_BlurBaseOperation.h"
-#include "COM_NodeOperation.h"
+#include "COM_GaussianAlphaBlurBaseOperation.h"
namespace blender::compositor {
-class GaussianAlphaYBlurOperation : public BlurBaseOperation {
+/* TODO(manzanilla): everything to be removed with tiled implementation except the constructor. */
+class GaussianAlphaYBlurOperation : public GaussianAlphaBlurBaseOperation {
private:
- float *m_gausstab;
- float *m_distbuf_inv;
- bool m_do_subtract;
- int m_falloff;
- int m_filtersize;
void updateGauss();
public:
@@ -54,18 +49,6 @@ class GaussianAlphaYBlurOperation : public BlurBaseOperation {
bool determineDependingAreaOfInterest(rcti *input,
ReadBufferOperation *readOperation,
rcti *output) override;
-
- /**
- * Set subtract for Dilate/Erode functionality
- */
- void setSubtract(bool subtract)
- {
- this->m_do_subtract = subtract;
- }
- void setFalloff(int falloff)
- {
- this->m_falloff = falloff;
- }
};
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_GaussianBlurBaseOperation.cc b/source/blender/compositor/operations/COM_GaussianBlurBaseOperation.cc
new file mode 100644
index 00000000000..959f599fab4
--- /dev/null
+++ b/source/blender/compositor/operations/COM_GaussianBlurBaseOperation.cc
@@ -0,0 +1,154 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Copyright 2021, Blender Foundation.
+ */
+
+#include "COM_GaussianBlurBaseOperation.h"
+
+namespace blender::compositor {
+
+GaussianBlurBaseOperation::GaussianBlurBaseOperation(eDimension dim)
+ : BlurBaseOperation(DataType::Color)
+{
+ m_gausstab = nullptr;
+#ifdef BLI_HAVE_SSE2
+ m_gausstab_sse = nullptr;
+#endif
+ m_filtersize = 0;
+ rad_ = 0.0f;
+ dimension_ = dim;
+}
+
+void GaussianBlurBaseOperation::init_data()
+{
+ BlurBaseOperation::init_data();
+ if (execution_model_ == eExecutionModel::FullFrame) {
+ rad_ = max_ff(m_size * this->get_blur_size(dimension_), 0.0f);
+ rad_ = min_ff(rad_, MAX_GAUSSTAB_RADIUS);
+ m_filtersize = min_ii(ceil(rad_), MAX_GAUSSTAB_RADIUS);
+ }
+}
+
+void GaussianBlurBaseOperation::initExecution()
+{
+ BlurBaseOperation::initExecution();
+ if (execution_model_ == eExecutionModel::FullFrame) {
+ m_gausstab = BlurBaseOperation::make_gausstab(rad_, m_filtersize);
+#ifdef BLI_HAVE_SSE2
+ m_gausstab_sse = BlurBaseOperation::convert_gausstab_sse(m_gausstab, m_filtersize);
+#endif
+ }
+}
+
+void GaussianBlurBaseOperation::deinitExecution()
+{
+ BlurBaseOperation::deinitExecution();
+
+ if (m_gausstab) {
+ MEM_freeN(m_gausstab);
+ m_gausstab = nullptr;
+ }
+#ifdef BLI_HAVE_SSE2
+ if (m_gausstab_sse) {
+ MEM_freeN(m_gausstab_sse);
+ m_gausstab_sse = nullptr;
+ }
+#endif
+}
+
+void GaussianBlurBaseOperation::get_area_of_interest(const int input_idx,
+ const rcti &output_area,
+ rcti &r_input_area)
+{
+ if (input_idx != IMAGE_INPUT_INDEX) {
+ BlurBaseOperation::get_area_of_interest(input_idx, output_area, r_input_area);
+ return;
+ }
+
+ r_input_area = output_area;
+ switch (dimension_) {
+ case eDimension::X:
+ r_input_area.xmin = output_area.xmin - m_filtersize - 1;
+ r_input_area.xmax = output_area.xmax + m_filtersize + 1;
+ break;
+ case eDimension::Y:
+ r_input_area.ymin = output_area.ymin - m_filtersize - 1;
+ r_input_area.ymax = output_area.ymax + m_filtersize + 1;
+ break;
+ }
+}
+
+void GaussianBlurBaseOperation::update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs)
+{
+ MemoryBuffer *input = inputs[IMAGE_INPUT_INDEX];
+ const rcti &input_rect = input->get_rect();
+ BuffersIterator<float> it = output->iterate_with({input}, area);
+
+ int min_input_coord = -1;
+ int max_input_coord = -1;
+ int elem_stride = -1;
+ std::function<int()> get_current_coord;
+ switch (dimension_) {
+ case eDimension::X:
+ min_input_coord = input_rect.xmin;
+ max_input_coord = input_rect.xmax;
+ elem_stride = input->elem_stride;
+ get_current_coord = [&] { return it.x; };
+ break;
+ case eDimension::Y:
+ min_input_coord = input_rect.ymin;
+ max_input_coord = input_rect.ymax;
+ elem_stride = input->row_stride;
+ get_current_coord = [&] { return it.y; };
+ break;
+ }
+
+ for (; !it.is_end(); ++it) {
+ const int coord = get_current_coord();
+ const int coord_min = max_ii(coord - m_filtersize, min_input_coord);
+ const int coord_max = min_ii(coord + m_filtersize + 1, max_input_coord);
+
+ float ATTR_ALIGN(16) color_accum[4] = {0.0f, 0.0f, 0.0f, 0.0f};
+ float multiplier_accum = 0.0f;
+
+ const int step = QualityStepHelper::getStep();
+ const float *in = it.in(0) + ((intptr_t)coord_min - coord) * elem_stride;
+ const int in_stride = elem_stride * step;
+ int gauss_idx = (coord_min - coord) + m_filtersize;
+ const int gauss_end = gauss_idx + (coord_max - coord_min);
+#ifdef BLI_HAVE_SSE2
+ __m128 accum_r = _mm_load_ps(color_accum);
+ for (; gauss_idx < gauss_end; in += in_stride, gauss_idx += step) {
+ __m128 reg_a = _mm_load_ps(in);
+ reg_a = _mm_mul_ps(reg_a, m_gausstab_sse[gauss_idx]);
+ accum_r = _mm_add_ps(accum_r, reg_a);
+ multiplier_accum += m_gausstab[gauss_idx];
+ }
+ _mm_store_ps(color_accum, accum_r);
+#else
+ for (; gauss_idx < gauss_end; in += in_stride, gauss_idx += step) {
+ const float multiplier = m_gausstab[gauss_idx];
+ madd_v4_v4fl(color_accum, in, multiplier);
+ multiplier_accum += multiplier;
+ }
+#endif
+ mul_v4_v4fl(it.out, color_accum, 1.0f / multiplier_accum);
+ }
+}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_GaussianBlurBaseOperation.h b/source/blender/compositor/operations/COM_GaussianBlurBaseOperation.h
new file mode 100644
index 00000000000..c0b27078a24
--- /dev/null
+++ b/source/blender/compositor/operations/COM_GaussianBlurBaseOperation.h
@@ -0,0 +1,50 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Copyright 2021, Blender Foundation.
+ */
+
+#pragma once
+
+#include "COM_BlurBaseOperation.h"
+
+namespace blender::compositor {
+
+class GaussianBlurBaseOperation : public BlurBaseOperation {
+ protected:
+ float *m_gausstab;
+#ifdef BLI_HAVE_SSE2
+ __m128 *m_gausstab_sse;
+#endif
+ int m_filtersize;
+ float rad_;
+ eDimension dimension_;
+
+ public:
+ GaussianBlurBaseOperation(eDimension dim);
+
+ virtual void init_data() override;
+ virtual void initExecution() override;
+ virtual void deinitExecution() override;
+
+ void get_area_of_interest(const int input_idx,
+ const rcti &output_area,
+ rcti &r_input_area) override;
+ virtual void update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs) override;
+};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_GaussianBokehBlurOperation.cc b/source/blender/compositor/operations/COM_GaussianBokehBlurOperation.cc
index b2c65ff2c96..aafc269abac 100644
--- a/source/blender/compositor/operations/COM_GaussianBokehBlurOperation.cc
+++ b/source/blender/compositor/operations/COM_GaussianBokehBlurOperation.cc
@@ -40,6 +40,27 @@ void *GaussianBokehBlurOperation::initializeTileData(rcti * /*rect*/)
return buffer;
}
+void GaussianBokehBlurOperation::init_data()
+{
+ BlurBaseOperation::init_data();
+ const float width = this->getWidth();
+ const float height = this->getHeight();
+
+ if (!this->m_sizeavailable) {
+ updateSize();
+ }
+
+ radxf_ = this->m_size * (float)this->m_data.sizex;
+ CLAMP(radxf_, 0.0f, width / 2.0f);
+
+ /* Vertical. */
+ radyf_ = this->m_size * (float)this->m_data.sizey;
+ CLAMP(radyf_, 0.0f, height / 2.0f);
+
+ this->m_radx = ceil(radxf_);
+ this->m_rady = ceil(radyf_);
+}
+
void GaussianBokehBlurOperation::initExecution()
{
BlurBaseOperation::initExecution();
@@ -54,39 +75,17 @@ void GaussianBokehBlurOperation::initExecution()
void GaussianBokehBlurOperation::updateGauss()
{
if (this->m_gausstab == nullptr) {
- float radxf;
- float radyf;
- int n;
- float *dgauss;
- float *ddgauss;
- int j, i;
- const float width = this->getWidth();
- const float height = this->getHeight();
- if (!this->m_sizeavailable) {
- updateSize();
- }
- radxf = this->m_size * (float)this->m_data.sizex;
- CLAMP(radxf, 0.0f, width / 2.0f);
-
- /* vertical */
- radyf = this->m_size * (float)this->m_data.sizey;
- CLAMP(radyf, 0.0f, height / 2.0f);
-
- this->m_radx = ceil(radxf);
- this->m_rady = ceil(radyf);
-
int ddwidth = 2 * this->m_radx + 1;
int ddheight = 2 * this->m_rady + 1;
- n = ddwidth * ddheight;
-
+ int n = ddwidth * ddheight;
/* create a full filter image */
- ddgauss = (float *)MEM_mallocN(sizeof(float) * n, __func__);
- dgauss = ddgauss;
+ float *ddgauss = (float *)MEM_mallocN(sizeof(float) * n, __func__);
+ float *dgauss = ddgauss;
float sum = 0.0f;
- float facx = (radxf > 0.0f ? 1.0f / radxf : 0.0f);
- float facy = (radyf > 0.0f ? 1.0f / radyf : 0.0f);
- for (j = -this->m_rady; j <= this->m_rady; j++) {
- for (i = -this->m_radx; i <= this->m_radx; i++, dgauss++) {
+ float facx = (radxf_ > 0.0f ? 1.0f / radxf_ : 0.0f);
+ float facy = (radyf_ > 0.0f ? 1.0f / radyf_ : 0.0f);
+ for (int j = -this->m_rady; j <= this->m_rady; j++) {
+ for (int i = -this->m_radx; i <= this->m_radx; i++, dgauss++) {
float fj = (float)j * facy;
float fi = (float)i * facx;
float dist = sqrt(fj * fj + fi * fi);
@@ -99,7 +98,7 @@ void GaussianBokehBlurOperation::updateGauss()
if (sum > 0.0f) {
/* normalize */
float norm = 1.0f / sum;
- for (j = n - 1; j >= 0; j--) {
+ for (int j = n - 1; j >= 0; j--) {
ddgauss[j] *= norm;
}
}
@@ -196,23 +195,69 @@ bool GaussianBokehBlurOperation::determineDependingAreaOfInterest(
return BlurBaseOperation::determineDependingAreaOfInterest(&newInput, readOperation, output);
}
+void GaussianBokehBlurOperation::get_area_of_interest(const int input_idx,
+ const rcti &output_area,
+ rcti &r_input_area)
+{
+ if (input_idx != IMAGE_INPUT_INDEX) {
+ BlurBaseOperation::get_area_of_interest(input_idx, output_area, r_input_area);
+ return;
+ }
+
+ r_input_area.xmax = output_area.xmax + m_radx;
+ r_input_area.xmin = output_area.xmin - m_radx;
+ r_input_area.ymax = output_area.ymax + m_rady;
+ r_input_area.ymin = output_area.ymin - m_rady;
+}
+
+void GaussianBokehBlurOperation::update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs)
+{
+ const MemoryBuffer *input = inputs[IMAGE_INPUT_INDEX];
+ BuffersIterator<float> it = output->iterate_with({}, area);
+ const rcti &input_rect = input->get_rect();
+ for (; !it.is_end(); ++it) {
+ const int x = it.x;
+ const int y = it.y;
+
+ const int ymin = max_ii(y - this->m_rady, input_rect.ymin);
+ const int ymax = min_ii(y + this->m_rady + 1, input_rect.ymax);
+ const int xmin = max_ii(x - this->m_radx, input_rect.xmin);
+ const int xmax = min_ii(x + this->m_radx + 1, input_rect.xmax);
+
+ float tempColor[4] = {0};
+ float multiplier_accum = 0;
+ const int step = QualityStepHelper::getStep();
+ const int elem_step = step * input->elem_stride;
+ const int add_const = (xmin - x + this->m_radx);
+ const int mul_const = (this->m_radx * 2 + 1);
+ for (int ny = ymin; ny < ymax; ny += step) {
+ const float *color = input->get_elem(xmin, ny);
+ int gauss_index = ((ny - y) + this->m_rady) * mul_const + add_const;
+ const int gauss_end = gauss_index + (xmax - xmin);
+ for (; gauss_index < gauss_end; gauss_index += step, color += elem_step) {
+ const float multiplier = this->m_gausstab[gauss_index];
+ madd_v4_v4fl(tempColor, color, multiplier);
+ multiplier_accum += multiplier;
+ }
+ }
+
+ mul_v4_v4fl(it.out, tempColor, 1.0f / multiplier_accum);
+ }
+}
+
// reference image
GaussianBlurReferenceOperation::GaussianBlurReferenceOperation()
: BlurBaseOperation(DataType::Color)
{
this->m_maintabs = nullptr;
+ use_variable_size_ = true;
}
-void *GaussianBlurReferenceOperation::initializeTileData(rcti * /*rect*/)
-{
- void *buffer = getInputOperation(0)->initializeTileData(nullptr);
- return buffer;
-}
-
-void GaussianBlurReferenceOperation::initExecution()
+void GaussianBlurReferenceOperation::init_data()
{
- BlurBaseOperation::initExecution();
- // setup gaustab
+ /* Setup variables for gausstab and area of interest. */
this->m_data.image_in_width = this->getWidth();
this->m_data.image_in_height = this->getHeight();
if (this->m_data.relative) {
@@ -232,7 +277,7 @@ void GaussianBlurReferenceOperation::initExecution()
}
}
- /* horizontal */
+ /* Horizontal. */
m_filtersizex = (float)this->m_data.sizex;
int imgx = getWidth() / 2;
if (m_filtersizex > imgx) {
@@ -243,7 +288,7 @@ void GaussianBlurReferenceOperation::initExecution()
}
m_radx = (float)m_filtersizex;
- /* vertical */
+ /* Vertical. */
m_filtersizey = (float)this->m_data.sizey;
int imgy = getHeight() / 2;
if (m_filtersizey > imgy) {
@@ -253,6 +298,18 @@ void GaussianBlurReferenceOperation::initExecution()
m_filtersizey = 1;
}
m_rady = (float)m_filtersizey;
+}
+
+void *GaussianBlurReferenceOperation::initializeTileData(rcti * /*rect*/)
+{
+ void *buffer = getInputOperation(0)->initializeTileData(nullptr);
+ return buffer;
+}
+
+void GaussianBlurReferenceOperation::initExecution()
+{
+ BlurBaseOperation::initExecution();
+
updateGauss();
}
@@ -363,4 +420,78 @@ bool GaussianBlurReferenceOperation::determineDependingAreaOfInterest(
return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output);
}
+void GaussianBlurReferenceOperation::get_area_of_interest(const int input_idx,
+ const rcti &output_area,
+ rcti &r_input_area)
+{
+ if (input_idx != IMAGE_INPUT_INDEX) {
+ BlurBaseOperation::get_area_of_interest(input_idx, output_area, r_input_area);
+ return;
+ }
+
+ const int add_x = this->m_data.sizex + 2;
+ const int add_y = this->m_data.sizey + 2;
+ r_input_area.xmax = output_area.xmax + add_x;
+ r_input_area.xmin = output_area.xmin - add_x;
+ r_input_area.ymax = output_area.ymax + add_y;
+ r_input_area.ymin = output_area.ymin - add_y;
+}
+
+void GaussianBlurReferenceOperation::update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs)
+{
+ const MemoryBuffer *image_input = inputs[IMAGE_INPUT_INDEX];
+ MemoryBuffer *size_input = inputs[SIZE_INPUT_INDEX];
+ for (BuffersIterator<float> it = output->iterate_with({size_input}, area); !it.is_end(); ++it) {
+ const float ref_size = *it.in(0);
+ int ref_radx = (int)(ref_size * m_radx);
+ int ref_rady = (int)(ref_size * m_rady);
+ if (ref_radx > m_filtersizex) {
+ ref_radx = m_filtersizex;
+ }
+ else if (ref_radx < 1) {
+ ref_radx = 1;
+ }
+ if (ref_rady > m_filtersizey) {
+ ref_rady = m_filtersizey;
+ }
+ else if (ref_rady < 1) {
+ ref_rady = 1;
+ }
+
+ const int x = it.x;
+ const int y = it.y;
+ if (ref_radx == 1 && ref_rady == 1) {
+ image_input->read_elem(x, y, it.out);
+ continue;
+ }
+
+ const int w = getWidth();
+ const int height = getHeight();
+ const int minxr = x - ref_radx < 0 ? -x : -ref_radx;
+ const int maxxr = x + ref_radx > w ? w - x : ref_radx;
+ const int minyr = y - ref_rady < 0 ? -y : -ref_rady;
+ const int maxyr = y + ref_rady > height ? height - y : ref_rady;
+
+ const float *gausstabx = m_maintabs[ref_radx - 1];
+ const float *gausstabcentx = gausstabx + ref_radx;
+ const float *gausstaby = m_maintabs[ref_rady - 1];
+ const float *gausstabcenty = gausstaby + ref_rady;
+
+ float gauss_sum = 0.0f;
+ float color_sum[4] = {0};
+ const float *row_color = image_input->get_elem(x + minxr, y + minyr);
+ for (int i = minyr; i < maxyr; i++, row_color += image_input->row_stride) {
+ const float *color = row_color;
+ for (int j = minxr; j < maxxr; j++, color += image_input->elem_stride) {
+ const float val = gausstabcenty[i] * gausstabcentx[j];
+ gauss_sum += val;
+ madd_v4_v4fl(color_sum, color, val);
+ }
+ }
+ mul_v4_v4fl(it.out, color_sum, 1.0f / gauss_sum);
+ }
+}
+
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_GaussianBokehBlurOperation.h b/source/blender/compositor/operations/COM_GaussianBokehBlurOperation.h
index 59ba3d06619..a64b5b327b0 100644
--- a/source/blender/compositor/operations/COM_GaussianBokehBlurOperation.h
+++ b/source/blender/compositor/operations/COM_GaussianBokehBlurOperation.h
@@ -28,10 +28,13 @@ class GaussianBokehBlurOperation : public BlurBaseOperation {
private:
float *m_gausstab;
int m_radx, m_rady;
+ float radxf_;
+ float radyf_;
void updateGauss();
public:
GaussianBokehBlurOperation();
+ void init_data() override;
void initExecution() override;
void *initializeTileData(rcti *rect) override;
/**
@@ -47,6 +50,13 @@ class GaussianBokehBlurOperation : public BlurBaseOperation {
bool determineDependingAreaOfInterest(rcti *input,
ReadBufferOperation *readOperation,
rcti *output) override;
+
+ void get_area_of_interest(const int input_idx,
+ const rcti &output_area,
+ rcti &r_input_area) override;
+ void update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs) override;
};
class GaussianBlurReferenceOperation : public BlurBaseOperation {
@@ -61,6 +71,7 @@ class GaussianBlurReferenceOperation : public BlurBaseOperation {
public:
GaussianBlurReferenceOperation();
+ void init_data() override;
void initExecution() override;
void *initializeTileData(rcti *rect) override;
/**
@@ -76,6 +87,13 @@ class GaussianBlurReferenceOperation : public BlurBaseOperation {
bool determineDependingAreaOfInterest(rcti *input,
ReadBufferOperation *readOperation,
rcti *output) override;
+
+ void get_area_of_interest(const int input_idx,
+ const rcti &output_area,
+ rcti &r_input_area) override;
+ void update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs) override;
};
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_GaussianXBlurOperation.cc b/source/blender/compositor/operations/COM_GaussianXBlurOperation.cc
index 4b46cfc8776..8d686265231 100644
--- a/source/blender/compositor/operations/COM_GaussianXBlurOperation.cc
+++ b/source/blender/compositor/operations/COM_GaussianXBlurOperation.cc
@@ -25,13 +25,8 @@
namespace blender::compositor {
-GaussianXBlurOperation::GaussianXBlurOperation() : BlurBaseOperation(DataType::Color)
+GaussianXBlurOperation::GaussianXBlurOperation() : GaussianBlurBaseOperation(eDimension::X)
{
- this->m_gausstab = nullptr;
-#ifdef BLI_HAVE_SSE2
- this->m_gausstab_sse = nullptr;
-#endif
- this->m_filtersize = 0;
}
void *GaussianXBlurOperation::initializeTileData(rcti * /*rect*/)
@@ -45,13 +40,14 @@ void *GaussianXBlurOperation::initializeTileData(rcti * /*rect*/)
return buffer;
}
+/* TODO(manzanilla): to be removed with tiled implementation. */
void GaussianXBlurOperation::initExecution()
{
- BlurBaseOperation::initExecution();
+ GaussianBlurBaseOperation::initExecution();
initMutex();
- if (this->m_sizeavailable) {
+ if (this->m_sizeavailable && execution_model_ == eExecutionModel::Tiled) {
float rad = max_ff(m_size * m_data.sizex, 0.0f);
m_filtersize = min_ii(ceil(rad), MAX_GAUSSTAB_RADIUS);
@@ -63,6 +59,7 @@ void GaussianXBlurOperation::initExecution()
}
}
+/* TODO(manzanilla): to be removed with tiled implementation. */
void GaussianXBlurOperation::updateGauss()
{
if (this->m_gausstab == nullptr) {
@@ -158,7 +155,7 @@ void GaussianXBlurOperation::executeOpenCL(OpenCLDevice *device,
void GaussianXBlurOperation::deinitExecution()
{
- BlurBaseOperation::deinitExecution();
+ GaussianBlurBaseOperation::deinitExecution();
if (this->m_gausstab) {
MEM_freeN(this->m_gausstab);
diff --git a/source/blender/compositor/operations/COM_GaussianXBlurOperation.h b/source/blender/compositor/operations/COM_GaussianXBlurOperation.h
index 15277f0a42d..e09e57bad67 100644
--- a/source/blender/compositor/operations/COM_GaussianXBlurOperation.h
+++ b/source/blender/compositor/operations/COM_GaussianXBlurOperation.h
@@ -18,18 +18,13 @@
#pragma once
-#include "COM_BlurBaseOperation.h"
-#include "COM_NodeOperation.h"
+#include "COM_GaussianBlurBaseOperation.h"
namespace blender::compositor {
-class GaussianXBlurOperation : public BlurBaseOperation {
+/* TODO(manzanilla): everything to be removed with tiled implementation except the constructor. */
+class GaussianXBlurOperation : public GaussianBlurBaseOperation {
private:
- float *m_gausstab;
-#ifdef BLI_HAVE_SSE2
- __m128 *m_gausstab_sse;
-#endif
- int m_filtersize;
void updateGauss();
public:
diff --git a/source/blender/compositor/operations/COM_GaussianYBlurOperation.cc b/source/blender/compositor/operations/COM_GaussianYBlurOperation.cc
index 590ac5faa6a..32d469a0ae4 100644
--- a/source/blender/compositor/operations/COM_GaussianYBlurOperation.cc
+++ b/source/blender/compositor/operations/COM_GaussianYBlurOperation.cc
@@ -25,13 +25,8 @@
namespace blender::compositor {
-GaussianYBlurOperation::GaussianYBlurOperation() : BlurBaseOperation(DataType::Color)
+GaussianYBlurOperation::GaussianYBlurOperation() : GaussianBlurBaseOperation(eDimension::Y)
{
- this->m_gausstab = nullptr;
-#ifdef BLI_HAVE_SSE2
- this->m_gausstab_sse = nullptr;
-#endif
- this->m_filtersize = 0;
}
void *GaussianYBlurOperation::initializeTileData(rcti * /*rect*/)
@@ -47,11 +42,11 @@ void *GaussianYBlurOperation::initializeTileData(rcti * /*rect*/)
void GaussianYBlurOperation::initExecution()
{
- BlurBaseOperation::initExecution();
+ GaussianBlurBaseOperation::initExecution();
initMutex();
- if (this->m_sizeavailable) {
+ if (this->m_sizeavailable && execution_model_ == eExecutionModel::Tiled) {
float rad = max_ff(m_size * m_data.sizey, 0.0f);
m_filtersize = min_ii(ceil(rad), MAX_GAUSSTAB_RADIUS);
@@ -158,7 +153,7 @@ void GaussianYBlurOperation::executeOpenCL(OpenCLDevice *device,
void GaussianYBlurOperation::deinitExecution()
{
- BlurBaseOperation::deinitExecution();
+ GaussianBlurBaseOperation::deinitExecution();
if (this->m_gausstab) {
MEM_freeN(this->m_gausstab);
diff --git a/source/blender/compositor/operations/COM_GaussianYBlurOperation.h b/source/blender/compositor/operations/COM_GaussianYBlurOperation.h
index 56d40849ba4..bb33f8b74cb 100644
--- a/source/blender/compositor/operations/COM_GaussianYBlurOperation.h
+++ b/source/blender/compositor/operations/COM_GaussianYBlurOperation.h
@@ -18,18 +18,13 @@
#pragma once
-#include "COM_BlurBaseOperation.h"
-#include "COM_NodeOperation.h"
+#include "COM_GaussianBlurBaseOperation.h"
namespace blender::compositor {
-class GaussianYBlurOperation : public BlurBaseOperation {
+/* TODO(manzanilla): everything to be removed with tiled implementation except the constructor. */
+class GaussianYBlurOperation : public GaussianBlurBaseOperation {
private:
- float *m_gausstab;
-#ifdef BLI_HAVE_SSE2
- __m128 *m_gausstab_sse;
-#endif
- int m_filtersize;
void updateGauss();
public:
diff --git a/source/blender/compositor/operations/COM_KeyingBlurOperation.cc b/source/blender/compositor/operations/COM_KeyingBlurOperation.cc
index 994b00cd3f4..d5ebd5e9df7 100644
--- a/source/blender/compositor/operations/COM_KeyingBlurOperation.cc
+++ b/source/blender/compositor/operations/COM_KeyingBlurOperation.cc
@@ -96,4 +96,67 @@ bool KeyingBlurOperation::determineDependingAreaOfInterest(rcti *input,
return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output);
}
+void KeyingBlurOperation::get_area_of_interest(const int UNUSED(input_idx),
+ const rcti &output_area,
+ rcti &r_input_area)
+{
+ switch (m_axis) {
+ case BLUR_AXIS_X:
+ r_input_area.xmin = output_area.xmin - m_size;
+ r_input_area.ymin = output_area.ymin;
+ r_input_area.xmax = output_area.xmax + m_size;
+ r_input_area.ymax = output_area.ymax;
+ break;
+ case BLUR_AXIS_Y:
+ r_input_area.xmin = output_area.xmin;
+ r_input_area.ymin = output_area.ymin - m_size;
+ r_input_area.xmax = output_area.xmax;
+ r_input_area.ymax = output_area.ymax + m_size;
+ break;
+ default:
+ BLI_assert_msg(0, "Unknown axis");
+ break;
+ }
+}
+
+void KeyingBlurOperation::update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs)
+{
+ const MemoryBuffer *input = inputs[0];
+ BuffersIterator<float> it = output->iterate_with(inputs, area);
+
+ int coord_max;
+ int elem_stride;
+ std::function<int()> get_current_coord;
+ switch (m_axis) {
+ case BLUR_AXIS_X:
+ get_current_coord = [&] { return it.x; };
+ coord_max = this->getWidth();
+ elem_stride = input->elem_stride;
+ break;
+ case BLUR_AXIS_Y:
+ get_current_coord = [&] { return it.y; };
+ coord_max = this->getHeight();
+ elem_stride = input->row_stride;
+ break;
+ }
+
+ for (; !it.is_end(); ++it) {
+ const int coord = get_current_coord();
+ const int start_coord = MAX2(0, coord - m_size + 1);
+ const int end_coord = MIN2(coord_max, coord + m_size);
+ const int count = end_coord - start_coord;
+
+ float sum = 0.0f;
+ const float *start = it.in(0) + (start_coord - coord) * elem_stride;
+ const float *end = start + count * elem_stride;
+ for (const float *elem = start; elem < end; elem += elem_stride) {
+ sum += *elem;
+ }
+
+ *it.out = sum / count;
+ }
+}
+
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_KeyingBlurOperation.h b/source/blender/compositor/operations/COM_KeyingBlurOperation.h
index b055d7713f1..b290b905e63 100644
--- a/source/blender/compositor/operations/COM_KeyingBlurOperation.h
+++ b/source/blender/compositor/operations/COM_KeyingBlurOperation.h
@@ -18,14 +18,14 @@
#pragma once
-#include "COM_NodeOperation.h"
+#include "COM_MultiThreadedOperation.h"
namespace blender::compositor {
/**
* Class with implementation of blurring for keying node
*/
-class KeyingBlurOperation : public NodeOperation {
+class KeyingBlurOperation : public MultiThreadedOperation {
protected:
int m_size;
int m_axis;
@@ -54,6 +54,13 @@ class KeyingBlurOperation : public NodeOperation {
bool determineDependingAreaOfInterest(rcti *input,
ReadBufferOperation *readOperation,
rcti *output) override;
+
+ void get_area_of_interest(const int input_idx,
+ const rcti &output_area,
+ rcti &r_input_area) override;
+ void update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs) override;
};
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_KeyingClipOperation.cc b/source/blender/compositor/operations/COM_KeyingClipOperation.cc
index 4029be4e077..817c920ed91 100644
--- a/source/blender/compositor/operations/COM_KeyingClipOperation.cc
+++ b/source/blender/compositor/operations/COM_KeyingClipOperation.cc
@@ -130,4 +130,89 @@ bool KeyingClipOperation::determineDependingAreaOfInterest(rcti *input,
return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output);
}
+void KeyingClipOperation::get_area_of_interest(const int input_idx,
+ const rcti &output_area,
+ rcti &r_input_area)
+{
+ BLI_assert(input_idx == 0);
+ UNUSED_VARS_NDEBUG(input_idx);
+ r_input_area.xmin = output_area.xmin - m_kernelRadius;
+ r_input_area.xmax = output_area.xmax + m_kernelRadius;
+ r_input_area.ymin = output_area.ymin - m_kernelRadius;
+ r_input_area.ymax = output_area.ymax + m_kernelRadius;
+}
+
+void KeyingClipOperation::update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs)
+{
+ const MemoryBuffer *input = inputs[0];
+ BuffersIterator<float> it = output->iterate_with(inputs, area);
+
+ const int delta = m_kernelRadius;
+ const float tolerance = m_kernelTolerance;
+ const int width = this->getWidth();
+ const int height = this->getHeight();
+ const int row_stride = input->row_stride;
+ const int elem_stride = input->elem_stride;
+ for (; !it.is_end(); ++it) {
+ const int x = it.x;
+ const int y = it.y;
+
+ const int start_x = MAX2(0, x - delta + 1);
+ const int start_y = MAX2(0, y - delta + 1);
+ const int end_x = MIN2(x + delta, width);
+ const int end_y = MIN2(y + delta, height);
+ const int x_len = end_x - start_x;
+ const int y_len = end_y - start_y;
+
+ const int total_count = x_len * y_len - 1;
+ const int threshold_count = ceil((float)total_count * 0.9f);
+ bool ok = false;
+ if (delta == 0) {
+ ok = true;
+ }
+
+ const float *main_elem = it.in(0);
+ const float value = *main_elem;
+ const float *row = input->get_elem(start_x, start_y);
+ const float *end_row = row + y_len * row_stride;
+ int count = 0;
+ for (; ok == false && row < end_row; row += row_stride) {
+ const float *end_elem = row + x_len * elem_stride;
+ for (const float *elem = row; ok == false && elem < end_elem; elem += elem_stride) {
+ if (UNLIKELY(elem == main_elem)) {
+ continue;
+ }
+
+ const float current_value = *elem;
+ if (fabsf(current_value - value) < tolerance) {
+ count++;
+ if (count >= threshold_count) {
+ ok = true;
+ }
+ }
+ }
+ }
+
+ if (m_isEdgeMatte) {
+ *it.out = ok ? 0.0f : 1.0f;
+ }
+ else {
+ if (!ok) {
+ *it.out = value;
+ }
+ else if (value < m_clipBlack) {
+ *it.out = 0.0f;
+ }
+ else if (value >= m_clipWhite) {
+ *it.out = 1.0f;
+ }
+ else {
+ *it.out = (value - m_clipBlack) / (m_clipWhite - m_clipBlack);
+ }
+ }
+ }
+}
+
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_KeyingClipOperation.h b/source/blender/compositor/operations/COM_KeyingClipOperation.h
index 0a21fb48c99..1a17d591781 100644
--- a/source/blender/compositor/operations/COM_KeyingClipOperation.h
+++ b/source/blender/compositor/operations/COM_KeyingClipOperation.h
@@ -18,14 +18,14 @@
#pragma once
-#include "COM_NodeOperation.h"
+#include "COM_MultiThreadedOperation.h"
namespace blender::compositor {
/**
* Class with implementation of black/white clipping for keying node
*/
-class KeyingClipOperation : public NodeOperation {
+class KeyingClipOperation : public MultiThreadedOperation {
protected:
float m_clipBlack;
float m_clipWhite;
@@ -68,6 +68,13 @@ class KeyingClipOperation : public NodeOperation {
bool determineDependingAreaOfInterest(rcti *input,
ReadBufferOperation *readOperation,
rcti *output) override;
+
+ void get_area_of_interest(const int input_idx,
+ const rcti &output_area,
+ rcti &r_input_area) override;
+ void update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs) override;
};
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_KeyingDespillOperation.cc b/source/blender/compositor/operations/COM_KeyingDespillOperation.cc
index d31a88cb91e..620b767e584 100644
--- a/source/blender/compositor/operations/COM_KeyingDespillOperation.cc
+++ b/source/blender/compositor/operations/COM_KeyingDespillOperation.cc
@@ -36,6 +36,7 @@ KeyingDespillOperation::KeyingDespillOperation()
this->m_pixelReader = nullptr;
this->m_screenReader = nullptr;
+ flags.can_be_constant = true;
}
void KeyingDespillOperation::initExecution()
@@ -82,4 +83,32 @@ void KeyingDespillOperation::executePixelSampled(float output[4],
}
}
+void KeyingDespillOperation::update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs)
+{
+ for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) {
+ const float *pixel_color = it.in(0);
+ const float *screen_color = it.in(1);
+
+ const int screen_primary_channel = max_axis_v3(screen_color);
+ const int other_1 = (screen_primary_channel + 1) % 3;
+ const int other_2 = (screen_primary_channel + 2) % 3;
+
+ const int min_channel = MIN2(other_1, other_2);
+ const int max_channel = MAX2(other_1, other_2);
+
+ const float average_value = m_colorBalance * pixel_color[min_channel] +
+ (1.0f - m_colorBalance) * pixel_color[max_channel];
+ const float amount = (pixel_color[screen_primary_channel] - average_value);
+
+ copy_v4_v4(it.out, pixel_color);
+
+ const float amount_despill = m_despillFactor * amount;
+ if (amount_despill > 0.0f) {
+ it.out[screen_primary_channel] = pixel_color[screen_primary_channel] - amount_despill;
+ }
+ }
+}
+
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_KeyingDespillOperation.h b/source/blender/compositor/operations/COM_KeyingDespillOperation.h
index 279ac60e6e9..16bed651d3a 100644
--- a/source/blender/compositor/operations/COM_KeyingDespillOperation.h
+++ b/source/blender/compositor/operations/COM_KeyingDespillOperation.h
@@ -18,14 +18,14 @@
#pragma once
-#include "COM_NodeOperation.h"
+#include "COM_MultiThreadedOperation.h"
namespace blender::compositor {
/**
* Class with implementation of keying despill node
*/
-class KeyingDespillOperation : public NodeOperation {
+class KeyingDespillOperation : public MultiThreadedOperation {
protected:
SocketReader *m_pixelReader;
SocketReader *m_screenReader;
@@ -48,6 +48,10 @@ class KeyingDespillOperation : public NodeOperation {
}
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
+
+ void update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs) override;
};
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_KeyingOperation.cc b/source/blender/compositor/operations/COM_KeyingOperation.cc
index e786e4b8219..3edb5a5d34e 100644
--- a/source/blender/compositor/operations/COM_KeyingOperation.cc
+++ b/source/blender/compositor/operations/COM_KeyingOperation.cc
@@ -110,4 +110,49 @@ void KeyingOperation::executePixelSampled(float output[4], float x, float y, Pix
}
}
+void KeyingOperation::update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs)
+{
+ for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) {
+ const float *pixel_color = it.in(0);
+ const float *screen_color = it.in(1);
+
+ const int primary_channel = max_axis_v3(screen_color);
+ const float min_pixel_color = min_fff(pixel_color[0], pixel_color[1], pixel_color[2]);
+
+ if (min_pixel_color > 1.0f) {
+ /* Overexposure doesn't happen on screen itself and usually happens
+ * on light sources in the shot, this need to be checked separately
+ * because saturation and falloff calculation is based on the fact
+ * that pixels are not overexposed.
+ */
+ it.out[0] = 1.0f;
+ }
+ else {
+ const float saturation = get_pixel_saturation(pixel_color, m_screenBalance, primary_channel);
+ const float screen_saturation = get_pixel_saturation(
+ screen_color, m_screenBalance, primary_channel);
+
+ if (saturation < 0) {
+ /* Means main channel of pixel is different from screen,
+ * assume this is completely a foreground.
+ */
+ it.out[0] = 1.0f;
+ }
+ else if (saturation >= screen_saturation) {
+ /* Matched main channels and higher saturation on pixel
+ * is treated as completely background.
+ */
+ it.out[0] = 0.0f;
+ }
+ else {
+ /* Nice alpha falloff on edges. */
+ const float distance = 1.0f - saturation / screen_saturation;
+ it.out[0] = distance;
+ }
+ }
+ }
+}
+
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_KeyingOperation.h b/source/blender/compositor/operations/COM_KeyingOperation.h
index 3d41ecaa0f6..e134ad54896 100644
--- a/source/blender/compositor/operations/COM_KeyingOperation.h
+++ b/source/blender/compositor/operations/COM_KeyingOperation.h
@@ -20,7 +20,7 @@
#include <string.h>
-#include "COM_NodeOperation.h"
+#include "COM_MultiThreadedOperation.h"
#include "BLI_listbase.h"
@@ -29,7 +29,7 @@ namespace blender::compositor {
/**
* Class with implementation of keying node
*/
-class KeyingOperation : public NodeOperation {
+class KeyingOperation : public MultiThreadedOperation {
protected:
SocketReader *m_pixelReader;
SocketReader *m_screenReader;
@@ -48,6 +48,10 @@ class KeyingOperation : public NodeOperation {
}
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
+
+ void update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs) override;
};
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_KeyingScreenOperation.cc b/source/blender/compositor/operations/COM_KeyingScreenOperation.cc
index 17b613246ad..c00aafc19a2 100644
--- a/source/blender/compositor/operations/COM_KeyingScreenOperation.cc
+++ b/source/blender/compositor/operations/COM_KeyingScreenOperation.cc
@@ -39,12 +39,21 @@ KeyingScreenOperation::KeyingScreenOperation()
this->m_framenumber = 0;
this->m_trackingObject[0] = 0;
flags.complex = true;
+ m_cachedTriangulation = nullptr;
}
void KeyingScreenOperation::initExecution()
{
initMutex();
- this->m_cachedTriangulation = nullptr;
+ if (execution_model_ == eExecutionModel::FullFrame) {
+ BLI_assert(m_cachedTriangulation == nullptr);
+ if (m_movieClip) {
+ m_cachedTriangulation = buildVoronoiTriangulation();
+ }
+ }
+ else {
+ this->m_cachedTriangulation = nullptr;
+ }
}
void KeyingScreenOperation::deinitExecution()
@@ -226,7 +235,7 @@ KeyingScreenOperation::TriangulationData *KeyingScreenOperation::buildVoronoiTri
return triangulation;
}
-void *KeyingScreenOperation::initializeTileData(rcti *rect)
+KeyingScreenOperation::TileData *KeyingScreenOperation::triangulate(const rcti *rect)
{
TileData *tile_data;
TriangulationData *triangulation;
@@ -234,18 +243,6 @@ void *KeyingScreenOperation::initializeTileData(rcti *rect)
int chunk_size = 20;
int i;
- if (this->m_movieClip == nullptr) {
- return nullptr;
- }
-
- if (!this->m_cachedTriangulation) {
- lockMutex();
- if (this->m_cachedTriangulation == nullptr) {
- this->m_cachedTriangulation = buildVoronoiTriangulation();
- }
- unlockMutex();
- }
-
triangulation = this->m_cachedTriangulation;
if (!triangulation) {
@@ -278,6 +275,23 @@ void *KeyingScreenOperation::initializeTileData(rcti *rect)
return tile_data;
}
+void *KeyingScreenOperation::initializeTileData(rcti *rect)
+{
+ if (this->m_movieClip == nullptr) {
+ return nullptr;
+ }
+
+ if (!this->m_cachedTriangulation) {
+ lockMutex();
+ if (this->m_cachedTriangulation == nullptr) {
+ this->m_cachedTriangulation = buildVoronoiTriangulation();
+ }
+ unlockMutex();
+ }
+
+ return triangulate(rect);
+}
+
void KeyingScreenOperation::deinitializeTileData(rcti * /*rect*/, void *data)
{
TileData *tile_data = (TileData *)data;
@@ -347,4 +361,57 @@ void KeyingScreenOperation::executePixel(float output[4], int x, int y, void *da
}
}
+void KeyingScreenOperation::update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs)
+{
+ if (m_movieClip == nullptr) {
+ output->fill(area, COM_COLOR_BLACK);
+ return;
+ }
+
+ TileData *tri_area = this->triangulate(&area);
+ BLI_assert(tri_area != nullptr);
+
+ const int *triangles = tri_area->triangles;
+ const int num_triangles = tri_area->triangles_total;
+ const TriangulationData *triangulation = m_cachedTriangulation;
+ for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) {
+ copy_v4_v4(it.out, COM_COLOR_BLACK);
+
+ const float co[2] = {(float)it.x, (float)it.y};
+ for (int i = 0; i < num_triangles; i++) {
+ const int triangle_idx = triangles[i];
+ const rcti *rect = &triangulation->triangles_AABB[triangle_idx];
+
+ if (!BLI_rcti_isect_pt(rect, it.x, it.y)) {
+ continue;
+ }
+
+ const int *triangle = triangulation->triangles[triangle_idx];
+ const VoronoiTriangulationPoint &a = triangulation->triangulated_points[triangle[0]];
+ const VoronoiTriangulationPoint &b = triangulation->triangulated_points[triangle[1]];
+ const VoronoiTriangulationPoint &c = triangulation->triangulated_points[triangle[2]];
+
+ float w[3];
+ if (!barycentric_coords_v2(a.co, b.co, c.co, co, w)) {
+ continue;
+ }
+
+ if (barycentric_inside_triangle_v2(w)) {
+ it.out[0] = a.color[0] * w[0] + b.color[0] * w[1] + c.color[0] * w[2];
+ it.out[1] = a.color[1] * w[0] + b.color[1] * w[1] + c.color[1] * w[2];
+ it.out[2] = a.color[2] * w[0] + b.color[2] * w[1] + c.color[2] * w[2];
+ break;
+ }
+ }
+ }
+
+ if (tri_area->triangles) {
+ MEM_freeN(tri_area->triangles);
+ }
+
+ MEM_freeN(tri_area);
+}
+
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_KeyingScreenOperation.h b/source/blender/compositor/operations/COM_KeyingScreenOperation.h
index 4118d229be9..0bc47dbea30 100644
--- a/source/blender/compositor/operations/COM_KeyingScreenOperation.h
+++ b/source/blender/compositor/operations/COM_KeyingScreenOperation.h
@@ -20,7 +20,7 @@
#include <string.h>
-#include "COM_NodeOperation.h"
+#include "COM_MultiThreadedOperation.h"
#include "DNA_movieclip_types.h"
@@ -34,7 +34,7 @@ namespace blender::compositor {
/**
* Class with implementation of green screen gradient rasterization
*/
-class KeyingScreenOperation : public NodeOperation {
+class KeyingScreenOperation : public MultiThreadedOperation {
protected:
typedef struct TriangulationData {
VoronoiTriangulationPoint *triangulated_points;
@@ -43,6 +43,7 @@ class KeyingScreenOperation : public NodeOperation {
rcti *triangles_AABB;
} TriangulationData;
+ /* TODO(manzanilla): rename to #TrianguledArea on removing tiled implementation. */
typedef struct TileData {
int *triangles;
int triangles_total;
@@ -84,6 +85,13 @@ class KeyingScreenOperation : public NodeOperation {
}
void executePixel(float output[4], int x, int y, void *data) override;
+
+ void update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs) override;
+
+ private:
+ TileData *triangulate(const rcti *rect);
};
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_LuminanceMatteOperation.cc b/source/blender/compositor/operations/COM_LuminanceMatteOperation.cc
index 5ca16e40ce3..c642c60b912 100644
--- a/source/blender/compositor/operations/COM_LuminanceMatteOperation.cc
+++ b/source/blender/compositor/operations/COM_LuminanceMatteOperation.cc
@@ -29,6 +29,7 @@ LuminanceMatteOperation::LuminanceMatteOperation()
addOutputSocket(DataType::Value);
this->m_inputImageProgram = nullptr;
+ flags.can_be_constant = true;
}
void LuminanceMatteOperation::initExecution()
@@ -78,4 +79,39 @@ void LuminanceMatteOperation::executePixelSampled(float output[4],
output[0] = min_ff(alpha, inColor[3]);
}
+void LuminanceMatteOperation::update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs)
+{
+ for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) {
+ const float *color = it.in(0);
+ const float luminance = IMB_colormanagement_get_luminance(color);
+
+ /* One line thread-friend algorithm:
+ * `it.out[0] = MIN2(color[3], MIN2(1.0f, MAX2(0.0f, ((luminance - low) / (high - low))));`
+ */
+
+ /* Test range. */
+ const float high = m_settings->t1;
+ const float low = m_settings->t2;
+ float alpha;
+ if (luminance > high) {
+ alpha = 1.0f;
+ }
+ else if (luminance < low) {
+ alpha = 0.0f;
+ }
+ else { /* Blend. */
+ alpha = (luminance - low) / (high - low);
+ }
+
+ /* Store matte(alpha) value in [0] to go with
+ * COM_SetAlphaMultiplyOperation and the Value output.
+ */
+
+ /* Don't make something that was more transparent less transparent. */
+ it.out[0] = MIN2(alpha, color[3]);
+ }
+}
+
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_LuminanceMatteOperation.h b/source/blender/compositor/operations/COM_LuminanceMatteOperation.h
index 035c68b9d59..aedfc715382 100644
--- a/source/blender/compositor/operations/COM_LuminanceMatteOperation.h
+++ b/source/blender/compositor/operations/COM_LuminanceMatteOperation.h
@@ -18,7 +18,7 @@
#pragma once
-#include "COM_MixOperation.h"
+#include "COM_MultiThreadedOperation.h"
namespace blender::compositor {
@@ -26,7 +26,7 @@ namespace blender::compositor {
* this program converts an input color to an output value.
* it assumes we are in sRGB color space.
*/
-class LuminanceMatteOperation : public NodeOperation {
+class LuminanceMatteOperation : public MultiThreadedOperation {
private:
NodeChroma *m_settings;
SocketReader *m_inputImageProgram;
@@ -49,6 +49,10 @@ class LuminanceMatteOperation : public NodeOperation {
{
this->m_settings = nodeChroma;
}
+
+ void update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs) override;
};
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_MapUVOperation.cc b/source/blender/compositor/operations/COM_MapUVOperation.cc
index 74e3d965d41..ad047c619f8 100644
--- a/source/blender/compositor/operations/COM_MapUVOperation.cc
+++ b/source/blender/compositor/operations/COM_MapUVOperation.cc
@@ -34,10 +34,26 @@ MapUVOperation::MapUVOperation()
this->m_inputColorProgram = nullptr;
}
+void MapUVOperation::init_data()
+{
+ NodeOperation *image_input = get_input_operation(0);
+ image_width_ = image_input->getWidth();
+ image_height_ = image_input->getHeight();
+
+ NodeOperation *uv_input = get_input_operation(1);
+ uv_width_ = uv_input->getWidth();
+ uv_height_ = uv_input->getHeight();
+}
+
void MapUVOperation::initExecution()
{
this->m_inputColorProgram = this->getInputSocketReader(0);
this->m_inputUVProgram = this->getInputSocketReader(1);
+ if (execution_model_ == eExecutionModel::Tiled) {
+ uv_input_read_fn_ = [=](float x, float y, float *out) {
+ this->m_inputUVProgram->readSampled(out, x, y, PixelSampler::Bilinear);
+ };
+ }
}
void MapUVOperation::executePixelSampled(float output[4],
@@ -81,9 +97,7 @@ void MapUVOperation::executePixelSampled(float output[4],
bool MapUVOperation::read_uv(float x, float y, float &r_u, float &r_v, float &r_alpha)
{
- float width = m_inputUVProgram->getWidth();
- float height = m_inputUVProgram->getHeight();
- if (x < 0.0f || x >= width || y < 0.0f || y >= height) {
+ if (x < 0.0f || x >= uv_width_ || y < 0.0f || y >= uv_height_) {
r_u = 0.0f;
r_v = 0.0f;
r_alpha = 0.0f;
@@ -91,9 +105,9 @@ bool MapUVOperation::read_uv(float x, float y, float &r_u, float &r_v, float &r_
}
float vector[3];
- m_inputUVProgram->readSampled(vector, x, y, PixelSampler::Bilinear);
- r_u = vector[0] * m_inputColorProgram->getWidth();
- r_v = vector[1] * m_inputColorProgram->getHeight();
+ uv_input_read_fn_(x, y, vector);
+ r_u = vector[0] * image_width_;
+ r_v = vector[1] * image_height_;
r_alpha = vector[2];
return true;
}
@@ -186,4 +200,75 @@ bool MapUVOperation::determineDependingAreaOfInterest(rcti *input,
return false;
}
+void MapUVOperation::get_area_of_interest(const int input_idx,
+ const rcti &output_area,
+ rcti &r_input_area)
+{
+ switch (input_idx) {
+ case 0: {
+ r_input_area.xmin = 0;
+ r_input_area.xmax = image_width_;
+ r_input_area.ymin = 0;
+ r_input_area.ymax = image_height_;
+ break;
+ }
+ case 1: {
+ r_input_area = output_area;
+ expand_area_for_sampler(r_input_area, PixelSampler::Bilinear);
+ break;
+ }
+ }
+}
+
+void MapUVOperation::update_memory_buffer_started(MemoryBuffer *UNUSED(output),
+ const rcti &UNUSED(area),
+ Span<MemoryBuffer *> inputs)
+{
+ const MemoryBuffer *uv_input = inputs[1];
+ uv_input_read_fn_ = [=](float x, float y, float *out) {
+ uv_input->read_elem_bilinear(x, y, out);
+ };
+}
+
+void MapUVOperation::update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs)
+{
+ const MemoryBuffer *input_image = inputs[0];
+ for (BuffersIterator<float> it = output->iterate_with({}, area); !it.is_end(); ++it) {
+ float xy[2] = {(float)it.x, (float)it.y};
+ float uv[2];
+ float deriv[2][2];
+ float alpha;
+ pixelTransform(xy, uv, deriv, alpha);
+ if (alpha == 0.0f) {
+ zero_v4(it.out);
+ continue;
+ }
+
+ /* EWA filtering. */
+ input_image->read_elem_filtered(uv[0], uv[1], deriv[0], deriv[1], it.out);
+
+ /* UV to alpha threshold. */
+ const float threshold = this->m_alpha * 0.05f;
+ /* XXX alpha threshold is used to fade out pixels on boundaries with invalid derivatives.
+ * this calculation is not very well defined, should be looked into if it becomes a problem ...
+ */
+ const float du = len_v2(deriv[0]);
+ const float dv = len_v2(deriv[1]);
+ const float factor = 1.0f - threshold * (du / image_width_ + dv / image_height_);
+ if (factor < 0.0f) {
+ alpha = 0.0f;
+ }
+ else {
+ alpha *= factor;
+ }
+
+ /* "premul" */
+ if (alpha < 1.0f) {
+ mul_v4_fl(it.out, alpha);
+ }
+ }
+}
+
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_MapUVOperation.h b/source/blender/compositor/operations/COM_MapUVOperation.h
index eb5f7d49122..65fbcb461c9 100644
--- a/source/blender/compositor/operations/COM_MapUVOperation.h
+++ b/source/blender/compositor/operations/COM_MapUVOperation.h
@@ -18,11 +18,11 @@
#pragma once
-#include "COM_NodeOperation.h"
+#include "COM_MultiThreadedOperation.h"
namespace blender::compositor {
-class MapUVOperation : public NodeOperation {
+class MapUVOperation : public MultiThreadedOperation {
private:
/**
* Cached reference to the inputProgram
@@ -30,8 +30,15 @@ class MapUVOperation : public NodeOperation {
SocketReader *m_inputUVProgram;
SocketReader *m_inputColorProgram;
+ int uv_width_;
+ int uv_height_;
+ int image_width_;
+ int image_height_;
+
float m_alpha;
+ std::function<void(float x, float y, float *out)> uv_input_read_fn_;
+
public:
MapUVOperation();
@@ -49,6 +56,8 @@ class MapUVOperation : public NodeOperation {
void pixelTransform(const float xy[2], float r_uv[2], float r_deriv[2][2], float &r_alpha);
+ void init_data() override;
+
/**
* Initialize the execution
*/
@@ -64,6 +73,14 @@ class MapUVOperation : public NodeOperation {
this->m_alpha = alpha;
}
+ void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override;
+ void update_memory_buffer_started(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs) override;
+ void update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs) override;
+
private:
bool read_uv(float x, float y, float &r_u, float &r_v, float &r_alpha);
};
diff --git a/source/blender/compositor/operations/COM_MathBaseOperation.cc b/source/blender/compositor/operations/COM_MathBaseOperation.cc
index a94c14347fb..2256dce011b 100644
--- a/source/blender/compositor/operations/COM_MathBaseOperation.cc
+++ b/source/blender/compositor/operations/COM_MathBaseOperation.cc
@@ -24,6 +24,8 @@ namespace blender::compositor {
MathBaseOperation::MathBaseOperation()
{
+ /* TODO(manzanilla): after removing tiled implementation, template this class to only add needed
+ * number of inputs. */
this->addInputSocket(DataType::Value);
this->addInputSocket(DataType::Value);
this->addInputSocket(DataType::Value);
@@ -32,6 +34,7 @@ MathBaseOperation::MathBaseOperation()
this->m_inputValue2Operation = nullptr;
this->m_inputValue3Operation = nullptr;
this->m_useClamp = false;
+ this->flags.can_be_constant = true;
}
void MathBaseOperation::initExecution()
@@ -73,6 +76,14 @@ void MathBaseOperation::clampIfNeeded(float *color)
}
}
+void MathBaseOperation::update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs)
+{
+ BuffersIterator<float> it = output->iterate_with(inputs, area);
+ update_memory_buffer_partial(it);
+}
+
void MathAddOperation::executePixelSampled(float output[4], float x, float y, PixelSampler sampler)
{
float inputValue1[4];
@@ -139,6 +150,14 @@ void MathDivideOperation::executePixelSampled(float output[4],
clampIfNeeded(output);
}
+void MathDivideOperation::update_memory_buffer_partial(BuffersIterator<float> &it)
+{
+ for (; !it.is_end(); ++it) {
+ const float divisor = *it.in(1);
+ *it.out = clamp_when_enabled((divisor == 0) ? 0 : *it.in(0) / divisor);
+ }
+}
+
void MathSineOperation::executePixelSampled(float output[4],
float x,
float y,
@@ -155,6 +174,14 @@ void MathSineOperation::executePixelSampled(float output[4],
clampIfNeeded(output);
}
+void MathSineOperation::update_memory_buffer_partial(BuffersIterator<float> &it)
+{
+ for (; !it.is_end(); ++it) {
+ *it.out = sin(*it.in(0));
+ clamp_when_enabled(it.out);
+ }
+}
+
void MathCosineOperation::executePixelSampled(float output[4],
float x,
float y,
@@ -171,6 +198,14 @@ void MathCosineOperation::executePixelSampled(float output[4],
clampIfNeeded(output);
}
+void MathCosineOperation::update_memory_buffer_partial(BuffersIterator<float> &it)
+{
+ for (; !it.is_end(); ++it) {
+ *it.out = cos(*it.in(0));
+ clamp_when_enabled(it.out);
+ }
+}
+
void MathTangentOperation::executePixelSampled(float output[4],
float x,
float y,
@@ -187,6 +222,14 @@ void MathTangentOperation::executePixelSampled(float output[4],
clampIfNeeded(output);
}
+void MathTangentOperation::update_memory_buffer_partial(BuffersIterator<float> &it)
+{
+ for (; !it.is_end(); ++it) {
+ *it.out = tan(*it.in(0));
+ clamp_when_enabled(it.out);
+ }
+}
+
void MathHyperbolicSineOperation::executePixelSampled(float output[4],
float x,
float y,
@@ -203,6 +246,14 @@ void MathHyperbolicSineOperation::executePixelSampled(float output[4],
clampIfNeeded(output);
}
+void MathHyperbolicSineOperation::update_memory_buffer_partial(BuffersIterator<float> &it)
+{
+ for (; !it.is_end(); ++it) {
+ *it.out = sinh(*it.in(0));
+ clamp_when_enabled(it.out);
+ }
+}
+
void MathHyperbolicCosineOperation::executePixelSampled(float output[4],
float x,
float y,
@@ -219,6 +270,14 @@ void MathHyperbolicCosineOperation::executePixelSampled(float output[4],
clampIfNeeded(output);
}
+void MathHyperbolicCosineOperation::update_memory_buffer_partial(BuffersIterator<float> &it)
+{
+ for (; !it.is_end(); ++it) {
+ *it.out = cosh(*it.in(0));
+ clamp_when_enabled(it.out);
+ }
+}
+
void MathHyperbolicTangentOperation::executePixelSampled(float output[4],
float x,
float y,
@@ -235,6 +294,14 @@ void MathHyperbolicTangentOperation::executePixelSampled(float output[4],
clampIfNeeded(output);
}
+void MathHyperbolicTangentOperation::update_memory_buffer_partial(BuffersIterator<float> &it)
+{
+ for (; !it.is_end(); ++it) {
+ *it.out = tanh(*it.in(0));
+ clamp_when_enabled(it.out);
+ }
+}
+
void MathArcSineOperation::executePixelSampled(float output[4],
float x,
float y,
@@ -256,6 +323,14 @@ void MathArcSineOperation::executePixelSampled(float output[4],
clampIfNeeded(output);
}
+void MathArcSineOperation::update_memory_buffer_partial(BuffersIterator<float> &it)
+{
+ for (; !it.is_end(); ++it) {
+ float value1 = *it.in(0);
+ *it.out = clamp_when_enabled((value1 <= 1 && value1 >= -1) ? asin(value1) : 0.0f);
+ }
+}
+
void MathArcCosineOperation::executePixelSampled(float output[4],
float x,
float y,
@@ -277,6 +352,14 @@ void MathArcCosineOperation::executePixelSampled(float output[4],
clampIfNeeded(output);
}
+void MathArcCosineOperation::update_memory_buffer_partial(BuffersIterator<float> &it)
+{
+ for (; !it.is_end(); ++it) {
+ float value1 = *it.in(0);
+ *it.out = clamp_when_enabled((value1 <= 1 && value1 >= -1) ? acos(value1) : 0.0f);
+ }
+}
+
void MathArcTangentOperation::executePixelSampled(float output[4],
float x,
float y,
@@ -293,6 +376,14 @@ void MathArcTangentOperation::executePixelSampled(float output[4],
clampIfNeeded(output);
}
+void MathArcTangentOperation::update_memory_buffer_partial(BuffersIterator<float> &it)
+{
+ for (; !it.is_end(); ++it) {
+ *it.out = atan(*it.in(0));
+ clamp_when_enabled(it.out);
+ }
+}
+
void MathPowerOperation::executePixelSampled(float output[4],
float x,
float y,
@@ -321,6 +412,29 @@ void MathPowerOperation::executePixelSampled(float output[4],
clampIfNeeded(output);
}
+void MathPowerOperation::update_memory_buffer_partial(BuffersIterator<float> &it)
+{
+ for (; !it.is_end(); ++it) {
+ const float value1 = *it.in(0);
+ const float value2 = *it.in(1);
+ if (value1 >= 0) {
+ *it.out = pow(value1, value2);
+ }
+ else {
+ const float y_mod_1 = fmod(value2, 1);
+ /* If input value is not nearly an integer, fall back to zero, nicer than straight rounding.
+ */
+ if (y_mod_1 > 0.999f || y_mod_1 < 0.001f) {
+ *it.out = pow(value1, floorf(value2 + 0.5f));
+ }
+ else {
+ *it.out = 0.0f;
+ }
+ }
+ clamp_when_enabled(it.out);
+ }
+}
+
void MathLogarithmOperation::executePixelSampled(float output[4],
float x,
float y,
@@ -342,6 +456,21 @@ void MathLogarithmOperation::executePixelSampled(float output[4],
clampIfNeeded(output);
}
+void MathLogarithmOperation::update_memory_buffer_partial(BuffersIterator<float> &it)
+{
+ for (; !it.is_end(); ++it) {
+ const float value1 = *it.in(0);
+ const float value2 = *it.in(1);
+ if (value1 > 0 && value2 > 0) {
+ *it.out = log(value1) / log(value2);
+ }
+ else {
+ *it.out = 0.0;
+ }
+ clamp_when_enabled(it.out);
+ }
+}
+
void MathMinimumOperation::executePixelSampled(float output[4],
float x,
float y,
@@ -358,6 +487,14 @@ void MathMinimumOperation::executePixelSampled(float output[4],
clampIfNeeded(output);
}
+void MathMinimumOperation::update_memory_buffer_partial(BuffersIterator<float> &it)
+{
+ for (; !it.is_end(); ++it) {
+ *it.out = MIN2(*it.in(0), *it.in(1));
+ clamp_when_enabled(it.out);
+ }
+}
+
void MathMaximumOperation::executePixelSampled(float output[4],
float x,
float y,
@@ -374,6 +511,14 @@ void MathMaximumOperation::executePixelSampled(float output[4],
clampIfNeeded(output);
}
+void MathMaximumOperation::update_memory_buffer_partial(BuffersIterator<float> &it)
+{
+ for (; !it.is_end(); ++it) {
+ *it.out = MAX2(*it.in(0), *it.in(1));
+ clamp_when_enabled(it.out);
+ }
+}
+
void MathRoundOperation::executePixelSampled(float output[4],
float x,
float y,
@@ -390,6 +535,14 @@ void MathRoundOperation::executePixelSampled(float output[4],
clampIfNeeded(output);
}
+void MathRoundOperation::update_memory_buffer_partial(BuffersIterator<float> &it)
+{
+ for (; !it.is_end(); ++it) {
+ *it.out = round(*it.in(0));
+ clamp_when_enabled(it.out);
+ }
+}
+
void MathLessThanOperation::executePixelSampled(float output[4],
float x,
float y,
@@ -443,6 +596,15 @@ void MathModuloOperation::executePixelSampled(float output[4],
clampIfNeeded(output);
}
+void MathModuloOperation::update_memory_buffer_partial(BuffersIterator<float> &it)
+{
+ for (; !it.is_end(); ++it) {
+ const float value2 = *it.in(1);
+ *it.out = (value2 == 0) ? 0 : fmod(*it.in(0), value2);
+ clamp_when_enabled(it.out);
+ }
+}
+
void MathAbsoluteOperation::executePixelSampled(float output[4],
float x,
float y,
@@ -457,6 +619,14 @@ void MathAbsoluteOperation::executePixelSampled(float output[4],
clampIfNeeded(output);
}
+void MathAbsoluteOperation::update_memory_buffer_partial(BuffersIterator<float> &it)
+{
+ for (; !it.is_end(); ++it) {
+ *it.out = fabs(*it.in(0));
+ clamp_when_enabled(it.out);
+ }
+}
+
void MathRadiansOperation::executePixelSampled(float output[4],
float x,
float y,
@@ -471,6 +641,14 @@ void MathRadiansOperation::executePixelSampled(float output[4],
clampIfNeeded(output);
}
+void MathRadiansOperation::update_memory_buffer_partial(BuffersIterator<float> &it)
+{
+ for (; !it.is_end(); ++it) {
+ *it.out = DEG2RADF(*it.in(0));
+ clamp_when_enabled(it.out);
+ }
+}
+
void MathDegreesOperation::executePixelSampled(float output[4],
float x,
float y,
@@ -485,6 +663,14 @@ void MathDegreesOperation::executePixelSampled(float output[4],
clampIfNeeded(output);
}
+void MathDegreesOperation::update_memory_buffer_partial(BuffersIterator<float> &it)
+{
+ for (; !it.is_end(); ++it) {
+ *it.out = RAD2DEGF(*it.in(0));
+ clamp_when_enabled(it.out);
+ }
+}
+
void MathArcTan2Operation::executePixelSampled(float output[4],
float x,
float y,
@@ -501,6 +687,14 @@ void MathArcTan2Operation::executePixelSampled(float output[4],
clampIfNeeded(output);
}
+void MathArcTan2Operation::update_memory_buffer_partial(BuffersIterator<float> &it)
+{
+ for (; !it.is_end(); ++it) {
+ *it.out = atan2(*it.in(0), *it.in(1));
+ clamp_when_enabled(it.out);
+ }
+}
+
void MathFloorOperation::executePixelSampled(float output[4],
float x,
float y,
@@ -515,6 +709,14 @@ void MathFloorOperation::executePixelSampled(float output[4],
clampIfNeeded(output);
}
+void MathFloorOperation::update_memory_buffer_partial(BuffersIterator<float> &it)
+{
+ for (; !it.is_end(); ++it) {
+ *it.out = floor(*it.in(0));
+ clamp_when_enabled(it.out);
+ }
+}
+
void MathCeilOperation::executePixelSampled(float output[4],
float x,
float y,
@@ -529,6 +731,14 @@ void MathCeilOperation::executePixelSampled(float output[4],
clampIfNeeded(output);
}
+void MathCeilOperation::update_memory_buffer_partial(BuffersIterator<float> &it)
+{
+ for (; !it.is_end(); ++it) {
+ *it.out = ceil(*it.in(0));
+ clamp_when_enabled(it.out);
+ }
+}
+
void MathFractOperation::executePixelSampled(float output[4],
float x,
float y,
@@ -543,6 +753,14 @@ void MathFractOperation::executePixelSampled(float output[4],
clampIfNeeded(output);
}
+void MathFractOperation::update_memory_buffer_partial(BuffersIterator<float> &it)
+{
+ for (; !it.is_end(); ++it) {
+ const float value = *it.in(0);
+ *it.out = clamp_when_enabled(value - floor(value));
+ }
+}
+
void MathSqrtOperation::executePixelSampled(float output[4],
float x,
float y,
@@ -562,6 +780,14 @@ void MathSqrtOperation::executePixelSampled(float output[4],
clampIfNeeded(output);
}
+void MathSqrtOperation::update_memory_buffer_partial(BuffersIterator<float> &it)
+{
+ for (; !it.is_end(); ++it) {
+ const float value = *it.in(0);
+ *it.out = clamp_when_enabled(value > 0 ? sqrt(value) : 0.0f);
+ }
+}
+
void MathInverseSqrtOperation::executePixelSampled(float output[4],
float x,
float y,
@@ -581,6 +807,14 @@ void MathInverseSqrtOperation::executePixelSampled(float output[4],
clampIfNeeded(output);
}
+void MathInverseSqrtOperation::update_memory_buffer_partial(BuffersIterator<float> &it)
+{
+ for (; !it.is_end(); ++it) {
+ const float value = *it.in(0);
+ *it.out = clamp_when_enabled(value > 0 ? 1.0f / sqrt(value) : 0.0f);
+ }
+}
+
void MathSignOperation::executePixelSampled(float output[4],
float x,
float y,
@@ -595,6 +829,14 @@ void MathSignOperation::executePixelSampled(float output[4],
clampIfNeeded(output);
}
+void MathSignOperation::update_memory_buffer_partial(BuffersIterator<float> &it)
+{
+ for (; !it.is_end(); ++it) {
+ *it.out = compatible_signf(*it.in(0));
+ clamp_when_enabled(it.out);
+ }
+}
+
void MathExponentOperation::executePixelSampled(float output[4],
float x,
float y,
@@ -609,6 +851,14 @@ void MathExponentOperation::executePixelSampled(float output[4],
clampIfNeeded(output);
}
+void MathExponentOperation::update_memory_buffer_partial(BuffersIterator<float> &it)
+{
+ for (; !it.is_end(); ++it) {
+ *it.out = expf(*it.in(0));
+ clamp_when_enabled(it.out);
+ }
+}
+
void MathTruncOperation::executePixelSampled(float output[4],
float x,
float y,
@@ -623,6 +873,15 @@ void MathTruncOperation::executePixelSampled(float output[4],
clampIfNeeded(output);
}
+void MathTruncOperation::update_memory_buffer_partial(BuffersIterator<float> &it)
+{
+ for (; !it.is_end(); ++it) {
+ const float value = *it.in(0);
+ *it.out = (value >= 0.0f) ? floor(value) : ceil(value);
+ clamp_when_enabled(it.out);
+ }
+}
+
void MathSnapOperation::executePixelSampled(float output[4],
float x,
float y,
@@ -644,6 +903,21 @@ void MathSnapOperation::executePixelSampled(float output[4],
clampIfNeeded(output);
}
+void MathSnapOperation::update_memory_buffer_partial(BuffersIterator<float> &it)
+{
+ for (; !it.is_end(); ++it) {
+ const float value1 = *it.in(0);
+ const float value2 = *it.in(1);
+ if (value1 == 0 || value2 == 0) { /* Avoid dividing by zero. */
+ *it.out = 0.0f;
+ }
+ else {
+ *it.out = floorf(value1 / value2) * value2;
+ }
+ clamp_when_enabled(it.out);
+ }
+}
+
void MathWrapOperation::executePixelSampled(float output[4],
float x,
float y,
@@ -662,6 +936,14 @@ void MathWrapOperation::executePixelSampled(float output[4],
clampIfNeeded(output);
}
+void MathWrapOperation::update_memory_buffer_partial(BuffersIterator<float> &it)
+{
+ for (; !it.is_end(); ++it) {
+ *it.out = wrapf(*it.in(0), *it.in(1), *it.in(2));
+ clamp_when_enabled(it.out);
+ }
+}
+
void MathPingpongOperation::executePixelSampled(float output[4],
float x,
float y,
@@ -678,6 +960,14 @@ void MathPingpongOperation::executePixelSampled(float output[4],
clampIfNeeded(output);
}
+void MathPingpongOperation::update_memory_buffer_partial(BuffersIterator<float> &it)
+{
+ for (; !it.is_end(); ++it) {
+ *it.out = pingpongf(*it.in(0), *it.in(1));
+ clamp_when_enabled(it.out);
+ }
+}
+
void MathCompareOperation::executePixelSampled(float output[4],
float x,
float y,
@@ -697,6 +987,14 @@ void MathCompareOperation::executePixelSampled(float output[4],
clampIfNeeded(output);
}
+void MathCompareOperation::update_memory_buffer_partial(BuffersIterator<float> &it)
+{
+ for (; !it.is_end(); ++it) {
+ *it.out = (fabsf(*it.in(0) - *it.in(1)) <= MAX2(*it.in(2), 1e-5f)) ? 1.0f : 0.0f;
+ clamp_when_enabled(it.out);
+ }
+}
+
void MathMultiplyAddOperation::executePixelSampled(float output[4],
float x,
float y,
@@ -715,6 +1013,14 @@ void MathMultiplyAddOperation::executePixelSampled(float output[4],
clampIfNeeded(output);
}
+void MathMultiplyAddOperation::update_memory_buffer_partial(BuffersIterator<float> &it)
+{
+ for (; !it.is_end(); ++it) {
+ *it.out = it.in(0)[0] * it.in(1)[0] + it.in(2)[0];
+ clamp_when_enabled(it.out);
+ }
+}
+
void MathSmoothMinOperation::executePixelSampled(float output[4],
float x,
float y,
@@ -733,6 +1039,14 @@ void MathSmoothMinOperation::executePixelSampled(float output[4],
clampIfNeeded(output);
}
+void MathSmoothMinOperation::update_memory_buffer_partial(BuffersIterator<float> &it)
+{
+ for (; !it.is_end(); ++it) {
+ *it.out = smoothminf(*it.in(0), *it.in(1), *it.in(2));
+ clamp_when_enabled(it.out);
+ }
+}
+
void MathSmoothMaxOperation::executePixelSampled(float output[4],
float x,
float y,
@@ -751,4 +1065,12 @@ void MathSmoothMaxOperation::executePixelSampled(float output[4],
clampIfNeeded(output);
}
+void MathSmoothMaxOperation::update_memory_buffer_partial(BuffersIterator<float> &it)
+{
+ for (; !it.is_end(); ++it) {
+ *it.out = -smoothminf(-it.in(0)[0], -it.in(1)[0], it.in(2)[0]);
+ clamp_when_enabled(it.out);
+ }
+}
+
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_MathBaseOperation.h b/source/blender/compositor/operations/COM_MathBaseOperation.h
index 08794c8db22..d2da05db68e 100644
--- a/source/blender/compositor/operations/COM_MathBaseOperation.h
+++ b/source/blender/compositor/operations/COM_MathBaseOperation.h
@@ -18,7 +18,7 @@
#pragma once
-#include "COM_NodeOperation.h"
+#include "COM_MultiThreadedOperation.h"
namespace blender::compositor {
@@ -26,7 +26,7 @@ namespace blender::compositor {
* this program converts an input color to an output value.
* it assumes we are in sRGB color space.
*/
-class MathBaseOperation : public NodeOperation {
+class MathBaseOperation : public MultiThreadedOperation {
protected:
/**
* Prefetched reference to the inputProgram
@@ -43,8 +43,24 @@ class MathBaseOperation : public NodeOperation {
*/
MathBaseOperation();
+ /* TODO(manzanilla): to be removed with tiled implementation. */
void clampIfNeeded(float color[4]);
+ float clamp_when_enabled(float value)
+ {
+ if (this->m_useClamp) {
+ return CLAMPIS(value, 0.0f, 1.0f);
+ }
+ return value;
+ }
+
+ void clamp_when_enabled(float *out)
+ {
+ if (this->m_useClamp) {
+ CLAMP(*out, 0.0f, 1.0f);
+ }
+ }
+
public:
/**
* Initialize the execution
@@ -66,87 +82,151 @@ class MathBaseOperation : public NodeOperation {
{
this->m_useClamp = value;
}
+
+ void update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs) final;
+
+ protected:
+ virtual void update_memory_buffer_partial(BuffersIterator<float> &it) = 0;
};
-class MathAddOperation : public MathBaseOperation {
+template<template<typename> typename TFunctor>
+class MathFunctor2Operation : public MathBaseOperation {
+ void update_memory_buffer_partial(BuffersIterator<float> &it) final
+ {
+ TFunctor functor;
+ for (; !it.is_end(); ++it) {
+ *it.out = functor(*it.in(0), *it.in(1));
+ clamp_when_enabled(it.out);
+ }
+ }
+};
+
+class MathAddOperation : public MathFunctor2Operation<std::plus> {
public:
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
};
-class MathSubtractOperation : public MathBaseOperation {
+class MathSubtractOperation : public MathFunctor2Operation<std::minus> {
public:
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
};
-class MathMultiplyOperation : public MathBaseOperation {
+class MathMultiplyOperation : public MathFunctor2Operation<std::multiplies> {
public:
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
};
class MathDivideOperation : public MathBaseOperation {
public:
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
+
+ protected:
+ void update_memory_buffer_partial(BuffersIterator<float> &it) override;
};
class MathSineOperation : public MathBaseOperation {
public:
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
+
+ protected:
+ void update_memory_buffer_partial(BuffersIterator<float> &it) override;
};
class MathCosineOperation : public MathBaseOperation {
public:
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
+
+ protected:
+ void update_memory_buffer_partial(BuffersIterator<float> &it) override;
};
class MathTangentOperation : public MathBaseOperation {
public:
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
+
+ protected:
+ void update_memory_buffer_partial(BuffersIterator<float> &it) override;
};
class MathHyperbolicSineOperation : public MathBaseOperation {
public:
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
+
+ protected:
+ void update_memory_buffer_partial(BuffersIterator<float> &it) override;
};
class MathHyperbolicCosineOperation : public MathBaseOperation {
public:
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
+
+ protected:
+ void update_memory_buffer_partial(BuffersIterator<float> &it) override;
};
class MathHyperbolicTangentOperation : public MathBaseOperation {
public:
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
+
+ protected:
+ void update_memory_buffer_partial(BuffersIterator<float> &it) override;
};
class MathArcSineOperation : public MathBaseOperation {
public:
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
+
+ protected:
+ void update_memory_buffer_partial(BuffersIterator<float> &it) override;
};
class MathArcCosineOperation : public MathBaseOperation {
public:
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
+
+ protected:
+ void update_memory_buffer_partial(BuffersIterator<float> &it) override;
};
class MathArcTangentOperation : public MathBaseOperation {
public:
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
+
+ protected:
+ void update_memory_buffer_partial(BuffersIterator<float> &it) override;
};
class MathPowerOperation : public MathBaseOperation {
public:
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
+
+ protected:
+ void update_memory_buffer_partial(BuffersIterator<float> &it) override;
};
class MathLogarithmOperation : public MathBaseOperation {
public:
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
+
+ protected:
+ void update_memory_buffer_partial(BuffersIterator<float> &it) override;
};
class MathMinimumOperation : public MathBaseOperation {
public:
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
+
+ protected:
+ void update_memory_buffer_partial(BuffersIterator<float> &it) override;
};
class MathMaximumOperation : public MathBaseOperation {
public:
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
+
+ protected:
+ void update_memory_buffer_partial(BuffersIterator<float> &it) override;
};
class MathRoundOperation : public MathBaseOperation {
public:
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
+
+ protected:
+ void update_memory_buffer_partial(BuffersIterator<float> &it) override;
};
-class MathLessThanOperation : public MathBaseOperation {
+class MathLessThanOperation : public MathFunctor2Operation<std::less> {
public:
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
};
-class MathGreaterThanOperation : public MathBaseOperation {
+class MathGreaterThanOperation : public MathFunctor2Operation<std::greater> {
public:
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
};
@@ -154,101 +234,161 @@ class MathGreaterThanOperation : public MathBaseOperation {
class MathModuloOperation : public MathBaseOperation {
public:
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
+
+ protected:
+ void update_memory_buffer_partial(BuffersIterator<float> &it) override;
};
class MathAbsoluteOperation : public MathBaseOperation {
public:
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
+
+ protected:
+ void update_memory_buffer_partial(BuffersIterator<float> &it) override;
};
class MathRadiansOperation : public MathBaseOperation {
public:
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
+
+ protected:
+ void update_memory_buffer_partial(BuffersIterator<float> &it) override;
};
class MathDegreesOperation : public MathBaseOperation {
public:
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
+
+ protected:
+ void update_memory_buffer_partial(BuffersIterator<float> &it) override;
};
class MathArcTan2Operation : public MathBaseOperation {
public:
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
+
+ protected:
+ void update_memory_buffer_partial(BuffersIterator<float> &it) override;
};
class MathFloorOperation : public MathBaseOperation {
public:
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
+
+ protected:
+ void update_memory_buffer_partial(BuffersIterator<float> &it) override;
};
class MathCeilOperation : public MathBaseOperation {
public:
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
+
+ protected:
+ void update_memory_buffer_partial(BuffersIterator<float> &it) override;
};
class MathFractOperation : public MathBaseOperation {
public:
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
+
+ protected:
+ void update_memory_buffer_partial(BuffersIterator<float> &it) override;
};
class MathSqrtOperation : public MathBaseOperation {
public:
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
+
+ protected:
+ void update_memory_buffer_partial(BuffersIterator<float> &it) override;
};
class MathInverseSqrtOperation : public MathBaseOperation {
public:
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
+
+ protected:
+ void update_memory_buffer_partial(BuffersIterator<float> &it) override;
};
class MathSignOperation : public MathBaseOperation {
public:
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
+
+ protected:
+ void update_memory_buffer_partial(BuffersIterator<float> &it) override;
};
class MathExponentOperation : public MathBaseOperation {
public:
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
+
+ protected:
+ void update_memory_buffer_partial(BuffersIterator<float> &it) override;
};
class MathTruncOperation : public MathBaseOperation {
public:
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
+
+ protected:
+ void update_memory_buffer_partial(BuffersIterator<float> &it) override;
};
class MathSnapOperation : public MathBaseOperation {
public:
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
+
+ protected:
+ void update_memory_buffer_partial(BuffersIterator<float> &it) override;
};
class MathWrapOperation : public MathBaseOperation {
public:
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
+
+ protected:
+ void update_memory_buffer_partial(BuffersIterator<float> &it) override;
};
class MathPingpongOperation : public MathBaseOperation {
public:
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
+
+ protected:
+ void update_memory_buffer_partial(BuffersIterator<float> &it) override;
};
class MathCompareOperation : public MathBaseOperation {
public:
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
+
+ protected:
+ void update_memory_buffer_partial(BuffersIterator<float> &it) override;
};
class MathMultiplyAddOperation : public MathBaseOperation {
public:
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
+
+ protected:
+ void update_memory_buffer_partial(BuffersIterator<float> &it) override;
};
class MathSmoothMinOperation : public MathBaseOperation {
public:
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
+
+ protected:
+ void update_memory_buffer_partial(BuffersIterator<float> &it) override;
};
class MathSmoothMaxOperation : public MathBaseOperation {
public:
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
+
+ protected:
+ void update_memory_buffer_partial(BuffersIterator<float> &it) override;
};
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_MovieDistortionOperation.cc b/source/blender/compositor/operations/COM_MovieDistortionOperation.cc
index c8e045ea117..d3424959061 100644
--- a/source/blender/compositor/operations/COM_MovieDistortionOperation.cc
+++ b/source/blender/compositor/operations/COM_MovieDistortionOperation.cc
@@ -128,4 +128,51 @@ bool MovieDistortionOperation::determineDependingAreaOfInterest(rcti *input,
return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output);
}
+void MovieDistortionOperation::get_area_of_interest(const int input_idx,
+ const rcti &output_area,
+ rcti &r_input_area)
+{
+ BLI_assert(input_idx == 0);
+ UNUSED_VARS_NDEBUG(input_idx);
+ r_input_area.xmin = output_area.xmin - m_margin[0];
+ r_input_area.ymin = output_area.ymin - m_margin[1];
+ r_input_area.xmax = output_area.xmax + m_margin[0];
+ r_input_area.ymax = output_area.ymax + m_margin[1];
+}
+
+void MovieDistortionOperation::update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs)
+{
+ const MemoryBuffer *input_img = inputs[0];
+ if (this->m_distortion == nullptr) {
+ output->copy_from(input_img, area);
+ return;
+ }
+
+ /* `float overscan = 0.0f;` */
+ const float pixel_aspect = this->m_pixel_aspect;
+ const float w = (float)this->m_width /* `/ (1 + overscan)` */;
+ const float h = (float)this->m_height /* `/ (1 + overscan)` */;
+ const float aspx = w / (float)this->m_calibration_width;
+ const float aspy = h / (float)this->m_calibration_height;
+ float xy[2];
+ float distorted_xy[2];
+ for (BuffersIterator<float> it = output->iterate_with({}, area); !it.is_end(); ++it) {
+ xy[0] = (it.x /* `- 0.5 * overscan * w` */) / aspx;
+ xy[1] = (it.y /* `- 0.5 * overscan * h` */) / aspy / pixel_aspect;
+
+ if (this->m_apply) {
+ BKE_tracking_distortion_undistort_v2(this->m_distortion, xy, distorted_xy);
+ }
+ else {
+ BKE_tracking_distortion_distort_v2(this->m_distortion, xy, distorted_xy);
+ }
+
+ const float u = distorted_xy[0] * aspx /* `+ 0.5 * overscan * w` */;
+ const float v = (distorted_xy[1] * aspy /* `+ 0.5 * overscan * h` */) * pixel_aspect;
+ input_img->read_elem_bilinear(u, v, it.out);
+ }
+}
+
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_MovieDistortionOperation.h b/source/blender/compositor/operations/COM_MovieDistortionOperation.h
index 631a62f7ebf..69c2f9c269c 100644
--- a/source/blender/compositor/operations/COM_MovieDistortionOperation.h
+++ b/source/blender/compositor/operations/COM_MovieDistortionOperation.h
@@ -18,7 +18,7 @@
#pragma once
-#include "COM_NodeOperation.h"
+#include "COM_MultiThreadedOperation.h"
#include "DNA_movieclip_types.h"
#include "MEM_guardedalloc.h"
@@ -26,7 +26,7 @@
namespace blender::compositor {
-class MovieDistortionOperation : public NodeOperation {
+class MovieDistortionOperation : public MultiThreadedOperation {
private:
SocketReader *m_inputOperation;
MovieClip *m_movieClip;
@@ -58,6 +58,11 @@ class MovieDistortionOperation : public NodeOperation {
bool determineDependingAreaOfInterest(rcti *input,
ReadBufferOperation *readOperation,
rcti *output) override;
+
+ void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override;
+ void update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs) override;
};
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_PlaneCornerPinOperation.cc b/source/blender/compositor/operations/COM_PlaneCornerPinOperation.cc
index 3577860b93d..d2a06ddd7c4 100644
--- a/source/blender/compositor/operations/COM_PlaneCornerPinOperation.cc
+++ b/source/blender/compositor/operations/COM_PlaneCornerPinOperation.cc
@@ -16,6 +16,7 @@
*/
#include "COM_PlaneCornerPinOperation.h"
+#include "COM_ConstantOperation.h"
#include "COM_ReadBufferOperation.h"
#include "MEM_guardedalloc.h"
@@ -28,6 +29,11 @@
namespace blender::compositor {
+constexpr int LOWER_LEFT_CORNER_INDEX = 0;
+constexpr int LOWER_RIGHT_CORNER_INDEX = 1;
+constexpr int UPPER_RIGHT_CORNER_INDEX = 2;
+constexpr int UPPER_LEFT_CORNER_INDEX = 3;
+
static bool check_corners(float corners[4][2])
{
int i, next, prev;
@@ -58,6 +64,7 @@ static bool check_corners(float corners[4][2])
return true;
}
+/* TODO(manzanilla): to be removed with tiled implementation. */
static void readCornersFromSockets(rcti *rect, SocketReader *readers[4], float corners[4][2])
{
for (int i = 0; i < 4; i++) {
@@ -87,6 +94,53 @@ static void readCornersFromSockets(rcti *rect, SocketReader *readers[4], float c
}
}
+static void set_default_corner(const int corner_idx, float r_corner[2])
+{
+ BLI_assert(corner_idx >= 0 && corner_idx < 4);
+ switch (corner_idx) {
+ case LOWER_LEFT_CORNER_INDEX:
+ r_corner[0] = 0.0f;
+ r_corner[1] = 0.0f;
+ break;
+ case LOWER_RIGHT_CORNER_INDEX:
+ r_corner[0] = 1.0f;
+ r_corner[1] = 0.0f;
+ break;
+ case UPPER_RIGHT_CORNER_INDEX:
+ r_corner[0] = 1.0f;
+ r_corner[1] = 1.0f;
+ break;
+ case UPPER_LEFT_CORNER_INDEX:
+ r_corner[0] = 0.0f;
+ r_corner[1] = 1.0f;
+ break;
+ }
+}
+
+static void read_input_corners(NodeOperation *op, const int first_input_idx, float r_corners[4][2])
+{
+ for (const int i : IndexRange(4)) {
+ NodeOperation *input = op->get_input_operation(i + first_input_idx);
+ if (input->get_flags().is_constant_operation) {
+ ConstantOperation *corner_input = static_cast<ConstantOperation *>(input);
+ copy_v2_v2(r_corners[i], corner_input->get_constant_elem());
+ }
+ else {
+ set_default_corner(i, r_corners[i]);
+ }
+ }
+
+ /* Convexity check: concave corners need to be prevented, otherwise
+ * #BKE_tracking_homography_between_two_quads will freeze. */
+ if (!check_corners(r_corners)) {
+ /* Revert to default corners. There could be a more elegant solution,
+ * this prevents freezing at least. */
+ for (const int i : IndexRange(4)) {
+ set_default_corner(i, r_corners[i]);
+ }
+ }
+}
+
/* ******** PlaneCornerPinMaskOperation ******** */
PlaneCornerPinMaskOperation::PlaneCornerPinMaskOperation() : m_corners_ready(false)
@@ -103,6 +157,17 @@ PlaneCornerPinMaskOperation::PlaneCornerPinMaskOperation() : m_corners_ready(fal
flags.complex = true;
}
+void PlaneCornerPinMaskOperation::init_data()
+{
+ if (execution_model_ == eExecutionModel::FullFrame) {
+ float corners[4][2];
+ read_input_corners(this, 0, corners);
+ calculateCorners(corners, true, 0);
+ }
+}
+
+/* TODO(manzanilla): to be removed with tiled implementation. Same for #deinitExecution and do the
+ * same on #PlaneCornerPinWarpImageOperation. */
void PlaneCornerPinMaskOperation::initExecution()
{
PlaneDistortMaskOperation::initExecution();
@@ -147,10 +212,22 @@ void *PlaneCornerPinMaskOperation::initializeTileData(rcti *rect)
void PlaneCornerPinMaskOperation::determineResolution(unsigned int resolution[2],
unsigned int preferredResolution[2])
{
+ if (execution_model_ == eExecutionModel::FullFrame) {
+ /* Determine inputs resolution. */
+ PlaneDistortMaskOperation::determineResolution(resolution, preferredResolution);
+ }
resolution[0] = preferredResolution[0];
resolution[1] = preferredResolution[1];
}
+void PlaneCornerPinMaskOperation::get_area_of_interest(const int UNUSED(input_idx),
+ const rcti &UNUSED(output_area),
+ rcti &r_input_area)
+{
+ /* All corner inputs are used as constants. */
+ r_input_area = COM_SINGLE_ELEM_AREA;
+}
+
/* ******** PlaneCornerPinWarpImageOperation ******** */
PlaneCornerPinWarpImageOperation::PlaneCornerPinWarpImageOperation() : m_corners_ready(false)
@@ -161,6 +238,15 @@ PlaneCornerPinWarpImageOperation::PlaneCornerPinWarpImageOperation() : m_corners
addInputSocket(DataType::Vector);
}
+void PlaneCornerPinWarpImageOperation::init_data()
+{
+ if (execution_model_ == eExecutionModel::FullFrame) {
+ float corners[4][2];
+ read_input_corners(this, 1, corners);
+ calculateCorners(corners, true, 0);
+ }
+}
+
void PlaneCornerPinWarpImageOperation::initExecution()
{
PlaneDistortWarpImageOperation::initExecution();
@@ -227,4 +313,17 @@ bool PlaneCornerPinWarpImageOperation::determineDependingAreaOfInterest(
#endif
}
+void PlaneCornerPinWarpImageOperation::get_area_of_interest(const int input_idx,
+ const rcti &output_area,
+ rcti &r_input_area)
+{
+ if (input_idx == 0) {
+ PlaneDistortWarpImageOperation::get_area_of_interest(input_idx, output_area, r_input_area);
+ }
+ else {
+ /* Corner inputs are used as constants. */
+ r_input_area = COM_SINGLE_ELEM_AREA;
+ }
+}
+
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_PlaneCornerPinOperation.h b/source/blender/compositor/operations/COM_PlaneCornerPinOperation.h
index 91c0cd9e16b..2831e937147 100644
--- a/source/blender/compositor/operations/COM_PlaneCornerPinOperation.h
+++ b/source/blender/compositor/operations/COM_PlaneCornerPinOperation.h
@@ -31,11 +31,13 @@ namespace blender::compositor {
class PlaneCornerPinMaskOperation : public PlaneDistortMaskOperation {
private:
+ /* TODO(manzanilla): to be removed with tiled implementation. */
bool m_corners_ready;
public:
PlaneCornerPinMaskOperation();
+ void init_data() override;
void initExecution() override;
void deinitExecution() override;
@@ -43,6 +45,8 @@ class PlaneCornerPinMaskOperation : public PlaneDistortMaskOperation {
void determineResolution(unsigned int resolution[2],
unsigned int preferredResolution[2]) override;
+
+ void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override;
};
class PlaneCornerPinWarpImageOperation : public PlaneDistortWarpImageOperation {
@@ -52,6 +56,7 @@ class PlaneCornerPinWarpImageOperation : public PlaneDistortWarpImageOperation {
public:
PlaneCornerPinWarpImageOperation();
+ void init_data() override;
void initExecution() override;
void deinitExecution() override;
@@ -60,6 +65,8 @@ class PlaneCornerPinWarpImageOperation : public PlaneDistortWarpImageOperation {
bool determineDependingAreaOfInterest(rcti *input,
ReadBufferOperation *readOperation,
rcti *output) override;
+
+ void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override;
};
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_PlaneDistortCommonOperation.cc b/source/blender/compositor/operations/COM_PlaneDistortCommonOperation.cc
index 4edcc206f5b..a80cbbe942a 100644
--- a/source/blender/compositor/operations/COM_PlaneDistortCommonOperation.cc
+++ b/source/blender/compositor/operations/COM_PlaneDistortCommonOperation.cc
@@ -85,8 +85,9 @@ void PlaneDistortWarpImageOperation::calculateCorners(const float corners[4][2],
{
PlaneDistortBaseOperation::calculateCorners(corners, normalized, sample);
- const int width = this->m_pixelReader->getWidth();
- const int height = this->m_pixelReader->getHeight();
+ const NodeOperation *image = get_input_operation(0);
+ const int width = image->getWidth();
+ const int height = image->getHeight();
float frame_corners[4][2] = {
{0.0f, 0.0f}, {(float)width, 0.0f}, {(float)width, (float)height}, {0.0f, (float)height}};
MotionSample *sample_data = &this->m_samples[sample];
@@ -127,6 +128,34 @@ void PlaneDistortWarpImageOperation::executePixelSampled(float output[4],
}
}
+void PlaneDistortWarpImageOperation::update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs)
+{
+ const MemoryBuffer *input_img = inputs[0];
+ float uv[2];
+ float deriv[2][2];
+ BuffersIterator<float> it = output->iterate_with({}, area);
+ if (this->m_motion_blur_samples == 1) {
+ for (; !it.is_end(); ++it) {
+ warpCoord(it.x, it.y, this->m_samples[0].perspectiveMatrix, uv, deriv);
+ input_img->read_elem_filtered(uv[0], uv[1], deriv[0], deriv[1], it.out);
+ }
+ }
+ else {
+ for (; !it.is_end(); ++it) {
+ zero_v4(it.out);
+ for (const int sample : IndexRange(this->m_motion_blur_samples)) {
+ float color[4];
+ warpCoord(it.x, it.y, this->m_samples[sample].perspectiveMatrix, uv, deriv);
+ input_img->read_elem_filtered(uv[0], uv[1], deriv[0], deriv[1], color);
+ add_v4_v4(it.out, color);
+ }
+ mul_v4_fl(it.out, 1.0f / (float)this->m_motion_blur_samples);
+ }
+ }
+}
+
bool PlaneDistortWarpImageOperation::determineDependingAreaOfInterest(
rcti *input, ReadBufferOperation *readOperation, rcti *output)
{
@@ -157,6 +186,51 @@ bool PlaneDistortWarpImageOperation::determineDependingAreaOfInterest(
return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output);
}
+void PlaneDistortWarpImageOperation::get_area_of_interest(const int input_idx,
+ const rcti &output_area,
+ rcti &r_input_area)
+{
+ if (input_idx != 0) {
+ r_input_area = output_area;
+ return;
+ }
+
+ /* TODO: figure out the area needed for warping and EWA filtering. */
+ r_input_area.xmin = 0;
+ r_input_area.ymin = 0;
+ r_input_area.xmax = get_input_operation(0)->getWidth();
+ r_input_area.ymax = get_input_operation(0)->getHeight();
+
+/* Old implemention but resulting coordinates are way out of input operation bounds and in some
+ * cases the area result may incorrectly cause cropping. */
+#if 0
+ float min[2], max[2];
+ INIT_MINMAX2(min, max);
+ for (int sample = 0; sample < this->m_motion_blur_samples; sample++) {
+ float UVs[4][2];
+ float deriv[2][2];
+ MotionSample *sample_data = &this->m_samples[sample];
+ /* TODO(sergey): figure out proper way to do this. */
+ warpCoord(
+ output_area.xmin - 2, output_area.ymin - 2, sample_data->perspectiveMatrix, UVs[0], deriv);
+ warpCoord(
+ output_area.xmax + 2, output_area.ymin - 2, sample_data->perspectiveMatrix, UVs[1], deriv);
+ warpCoord(
+ output_area.xmax + 2, output_area.ymax + 2, sample_data->perspectiveMatrix, UVs[2], deriv);
+ warpCoord(
+ output_area.xmin - 2, output_area.ymax + 2, sample_data->perspectiveMatrix, UVs[3], deriv);
+ for (int i = 0; i < 4; i++) {
+ minmax_v2v2_v2(min, max, UVs[i]);
+ }
+ }
+
+ r_input_area.xmin = min[0] - 1;
+ r_input_area.ymin = min[1] - 1;
+ r_input_area.xmax = max[0] + 1;
+ r_input_area.ymax = max[1] + 1;
+#endif
+}
+
/* ******** PlaneDistort Mask ******** */
PlaneDistortMaskOperation::PlaneDistortMaskOperation() : PlaneDistortBaseOperation()
@@ -219,4 +293,41 @@ void PlaneDistortMaskOperation::executePixelSampled(float output[4],
}
}
+void PlaneDistortMaskOperation::update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> UNUSED(inputs))
+{
+ for (BuffersIterator<float> it = output->iterate_with({}, area); !it.is_end(); ++it) {
+ int inside_count = 0;
+ for (const int motion_sample : IndexRange(this->m_motion_blur_samples)) {
+ MotionSample &sample = this->m_samples[motion_sample];
+ inside_count += get_jitter_samples_inside_count(it.x, it.y, sample);
+ }
+ *it.out = (float)inside_count / (this->m_osa * this->m_motion_blur_samples);
+ }
+}
+
+int PlaneDistortMaskOperation::get_jitter_samples_inside_count(int x,
+ int y,
+ MotionSample &sample_data)
+{
+ float point[2];
+ int inside_count = 0;
+ for (int sample = 0; sample < this->m_osa; sample++) {
+ point[0] = x + this->m_jitter[sample][0];
+ point[1] = y + this->m_jitter[sample][1];
+ if (isect_point_tri_v2(point,
+ sample_data.frameSpaceCorners[0],
+ sample_data.frameSpaceCorners[1],
+ sample_data.frameSpaceCorners[2]) ||
+ isect_point_tri_v2(point,
+ sample_data.frameSpaceCorners[0],
+ sample_data.frameSpaceCorners[2],
+ sample_data.frameSpaceCorners[3])) {
+ inside_count++;
+ }
+ }
+ return inside_count;
+}
+
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_PlaneDistortCommonOperation.h b/source/blender/compositor/operations/COM_PlaneDistortCommonOperation.h
index cc6e4d00d71..3ef9c1dfab8 100644
--- a/source/blender/compositor/operations/COM_PlaneDistortCommonOperation.h
+++ b/source/blender/compositor/operations/COM_PlaneDistortCommonOperation.h
@@ -20,7 +20,7 @@
#include <string.h>
-#include "COM_NodeOperation.h"
+#include "COM_MultiThreadedOperation.h"
#include "DNA_movieclip_types.h"
#include "DNA_tracking_types.h"
@@ -32,7 +32,7 @@ namespace blender::compositor {
#define PLANE_DISTORT_MAX_SAMPLES 64
-class PlaneDistortBaseOperation : public NodeOperation {
+class PlaneDistortBaseOperation : public MultiThreadedOperation {
protected:
struct MotionSample {
float frameSpaceCorners[4][2]; /* Corners coordinates in pixel space. */
@@ -78,6 +78,11 @@ class PlaneDistortWarpImageOperation : public PlaneDistortBaseOperation {
bool determineDependingAreaOfInterest(rcti *input,
ReadBufferOperation *readOperation,
rcti *output) override;
+
+ void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override;
+ void update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs) override;
};
class PlaneDistortMaskOperation : public PlaneDistortBaseOperation {
@@ -91,6 +96,13 @@ class PlaneDistortMaskOperation : public PlaneDistortBaseOperation {
void initExecution() override;
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
+
+ void update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs) override;
+
+ private:
+ int get_jitter_samples_inside_count(int x, int y, MotionSample &sample_data);
};
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_PlaneTrackOperation.cc b/source/blender/compositor/operations/COM_PlaneTrackOperation.cc
index 0884f2ad979..bf24f843ca2 100644
--- a/source/blender/compositor/operations/COM_PlaneTrackOperation.cc
+++ b/source/blender/compositor/operations/COM_PlaneTrackOperation.cc
@@ -101,18 +101,40 @@ void PlaneTrackCommon::determineResolution(unsigned int resolution[2],
/* ******** PlaneTrackMaskOperation ******** */
+void PlaneTrackMaskOperation::init_data()
+{
+ PlaneDistortMaskOperation::init_data();
+ if (execution_model_ == eExecutionModel::FullFrame) {
+ PlaneTrackCommon::read_and_calculate_corners(this);
+ }
+}
+
+/* TODO(manzanilla): to be removed with tiled implementation. */
void PlaneTrackMaskOperation::initExecution()
{
PlaneDistortMaskOperation::initExecution();
- PlaneTrackCommon::read_and_calculate_corners(this);
+ if (execution_model_ == eExecutionModel::Tiled) {
+ PlaneTrackCommon::read_and_calculate_corners(this);
+ }
}
/* ******** PlaneTrackWarpImageOperation ******** */
+void PlaneTrackWarpImageOperation::init_data()
+{
+ PlaneDistortWarpImageOperation::init_data();
+ if (execution_model_ == eExecutionModel::FullFrame) {
+ PlaneTrackCommon::read_and_calculate_corners(this);
+ }
+}
+
+/* TODO(manzanilla): to be removed with tiled implementation. */
void PlaneTrackWarpImageOperation::initExecution()
{
PlaneDistortWarpImageOperation::initExecution();
- PlaneTrackCommon::read_and_calculate_corners(this);
+ if (execution_model_ == eExecutionModel::Tiled) {
+ PlaneTrackCommon::read_and_calculate_corners(this);
+ }
}
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_PlaneTrackOperation.h b/source/blender/compositor/operations/COM_PlaneTrackOperation.h
index 3bae230aa06..d2027755162 100644
--- a/source/blender/compositor/operations/COM_PlaneTrackOperation.h
+++ b/source/blender/compositor/operations/COM_PlaneTrackOperation.h
@@ -73,6 +73,8 @@ class PlaneTrackMaskOperation : public PlaneDistortMaskOperation, public PlaneTr
{
}
+ void init_data() override;
+
void initExecution() override;
void determineResolution(unsigned int resolution[2],
@@ -92,6 +94,8 @@ class PlaneTrackWarpImageOperation : public PlaneDistortWarpImageOperation,
{
}
+ void init_data() override;
+
void initExecution() override;
void determineResolution(unsigned int resolution[2],
diff --git a/source/blender/compositor/operations/COM_ProjectorLensDistortionOperation.cc b/source/blender/compositor/operations/COM_ProjectorLensDistortionOperation.cc
index 93702d3f0cf..fcab5dd5751 100644
--- a/source/blender/compositor/operations/COM_ProjectorLensDistortionOperation.cc
+++ b/source/blender/compositor/operations/COM_ProjectorLensDistortionOperation.cc
@@ -17,6 +17,8 @@
*/
#include "COM_ProjectorLensDistortionOperation.h"
+#include "COM_ConstantOperation.h"
+
#include "BLI_math.h"
#include "BLI_utildefines.h"
@@ -32,6 +34,20 @@ ProjectorLensDistortionOperation::ProjectorLensDistortionOperation()
this->m_dispersionAvailable = false;
this->m_dispersion = 0.0f;
}
+
+void ProjectorLensDistortionOperation::init_data()
+{
+ if (execution_model_ == eExecutionModel::FullFrame) {
+ NodeOperation *dispersion_input = get_input_operation(1);
+ if (dispersion_input->get_flags().is_constant_operation) {
+ this->m_dispersion =
+ static_cast<ConstantOperation *>(dispersion_input)->get_constant_elem()[0];
+ }
+ this->m_kr = 0.25f * max_ff(min_ff(this->m_dispersion, 1.0f), 0.0f);
+ this->m_kr2 = this->m_kr * 20;
+ }
+}
+
void ProjectorLensDistortionOperation::initExecution()
{
this->initMutex();
@@ -97,6 +113,7 @@ bool ProjectorLensDistortionOperation::determineDependingAreaOfInterest(
return false;
}
+/* TODO(manzanilla): to be removed with tiled implementation. */
void ProjectorLensDistortionOperation::updateDispersion()
{
if (this->m_dispersionAvailable) {
@@ -114,4 +131,41 @@ void ProjectorLensDistortionOperation::updateDispersion()
this->unlockMutex();
}
+void ProjectorLensDistortionOperation::get_area_of_interest(const int input_idx,
+ const rcti &output_area,
+ rcti &r_input_area)
+{
+ if (input_idx == 1) {
+ /* Dispersion input is used as constant only. */
+ r_input_area = COM_SINGLE_ELEM_AREA;
+ return;
+ }
+
+ r_input_area.ymax = output_area.ymax;
+ r_input_area.ymin = output_area.ymin;
+ r_input_area.xmin = output_area.xmin - this->m_kr2 - 2;
+ r_input_area.xmax = output_area.xmax + this->m_kr2 + 2;
+}
+
+void ProjectorLensDistortionOperation::update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs)
+{
+ const MemoryBuffer *input_image = inputs[0];
+ const float height = this->getHeight();
+ const float width = this->getWidth();
+ float color[4];
+ for (BuffersIterator<float> it = output->iterate_with({}, area); !it.is_end(); ++it) {
+ const float v = (it.y + 0.5f) / height;
+ const float u = (it.x + 0.5f) / width;
+ input_image->read_elem_bilinear((u * width + this->m_kr2) - 0.5f, v * height - 0.5f, color);
+ it.out[0] = color[0];
+ input_image->read_elem(it.x, it.y, color);
+ it.out[1] = color[1];
+ input_image->read_elem_bilinear((u * width - this->m_kr2) - 0.5f, v * height - 0.5f, color);
+ it.out[2] = color[2];
+ it.out[3] = 1.0f;
+ }
+}
+
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_ProjectorLensDistortionOperation.h b/source/blender/compositor/operations/COM_ProjectorLensDistortionOperation.h
index bce61d3de15..7c7626bf271 100644
--- a/source/blender/compositor/operations/COM_ProjectorLensDistortionOperation.h
+++ b/source/blender/compositor/operations/COM_ProjectorLensDistortionOperation.h
@@ -18,12 +18,12 @@
#pragma once
-#include "COM_NodeOperation.h"
+#include "COM_MultiThreadedOperation.h"
#include "DNA_node_types.h"
namespace blender::compositor {
-class ProjectorLensDistortionOperation : public NodeOperation {
+class ProjectorLensDistortionOperation : public MultiThreadedOperation {
private:
/**
* Cached reference to the inputProgram
@@ -31,6 +31,7 @@ class ProjectorLensDistortionOperation : public NodeOperation {
SocketReader *m_inputProgram;
float m_dispersion;
+ /* TODO(manzanilla): to be removed with tiled implementation. */
bool m_dispersionAvailable;
float m_kr, m_kr2;
@@ -43,6 +44,7 @@ class ProjectorLensDistortionOperation : public NodeOperation {
*/
void executePixel(float output[4], int x, int y, void *data) override;
+ void init_data() override;
/**
* Initialize the execution
*/
@@ -59,6 +61,11 @@ class ProjectorLensDistortionOperation : public NodeOperation {
rcti *output) override;
void updateDispersion();
+
+ void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override;
+ void update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs) override;
};
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_RotateOperation.cc b/source/blender/compositor/operations/COM_RotateOperation.cc
index 4fb3d324992..e3c482c15cb 100644
--- a/source/blender/compositor/operations/COM_RotateOperation.cc
+++ b/source/blender/compositor/operations/COM_RotateOperation.cc
@@ -17,6 +17,8 @@
*/
#include "COM_RotateOperation.h"
+#include "COM_ConstantOperation.h"
+
#include "BLI_math.h"
namespace blender::compositor {
@@ -31,13 +33,50 @@ RotateOperation::RotateOperation()
this->m_degreeSocket = nullptr;
this->m_doDegree2RadConversion = false;
this->m_isDegreeSet = false;
+ sampler_ = PixelSampler::Bilinear;
+}
+
+void RotateOperation::get_area_rotation_bounds(const rcti &area,
+ const float center_x,
+ const float center_y,
+ const float sine,
+ const float cosine,
+ rcti &r_bounds)
+{
+ const float dxmin = area.xmin - center_x;
+ const float dymin = area.ymin - center_y;
+ const float dxmax = area.xmax - center_x;
+ const float dymax = area.ymax - center_y;
+
+ const float x1 = center_x + (cosine * dxmin + sine * dymin);
+ const float x2 = center_x + (cosine * dxmax + sine * dymin);
+ const float x3 = center_x + (cosine * dxmin + sine * dymax);
+ const float x4 = center_x + (cosine * dxmax + sine * dymax);
+ const float y1 = center_y + (-sine * dxmin + cosine * dymin);
+ const float y2 = center_y + (-sine * dxmax + cosine * dymin);
+ const float y3 = center_y + (-sine * dxmin + cosine * dymax);
+ const float y4 = center_y + (-sine * dxmax + cosine * dymax);
+ const float minx = MIN2(x1, MIN2(x2, MIN2(x3, x4)));
+ const float maxx = MAX2(x1, MAX2(x2, MAX2(x3, x4)));
+ const float miny = MIN2(y1, MIN2(y2, MIN2(y3, y4)));
+ const float maxy = MAX2(y1, MAX2(y2, MAX2(y3, y4)));
+
+ r_bounds.xmin = floor(minx);
+ r_bounds.xmax = ceil(maxx);
+ r_bounds.ymin = floor(miny);
+ r_bounds.ymax = ceil(maxy);
+}
+
+void RotateOperation::init_data()
+{
+ this->m_centerX = (getWidth() - 1) / 2.0;
+ this->m_centerY = (getHeight() - 1) / 2.0;
}
+
void RotateOperation::initExecution()
{
this->m_imageSocket = this->getInputSocketReader(0);
this->m_degreeSocket = this->getInputSocketReader(1);
- this->m_centerX = (getWidth() - 1) / 2.0;
- this->m_centerY = (getHeight() - 1) / 2.0;
}
void RotateOperation::deinitExecution()
@@ -50,7 +89,19 @@ inline void RotateOperation::ensureDegree()
{
if (!this->m_isDegreeSet) {
float degree[4];
- this->m_degreeSocket->readSampled(degree, 0, 0, PixelSampler::Nearest);
+ switch (execution_model_) {
+ case eExecutionModel::Tiled:
+ this->m_degreeSocket->readSampled(degree, 0, 0, PixelSampler::Nearest);
+ break;
+ case eExecutionModel::FullFrame:
+ NodeOperation *degree_op = getInputOperation(DEGREE_INPUT_INDEX);
+ const bool is_constant_degree = degree_op->get_flags().is_constant_operation;
+ degree[0] = is_constant_degree ?
+ static_cast<ConstantOperation *>(degree_op)->get_constant_elem()[0] :
+ 0.0f;
+ break;
+ }
+
double rad;
if (this->m_doDegree2RadConversion) {
rad = DEG2RAD((double)degree[0]);
@@ -108,4 +159,33 @@ bool RotateOperation::determineDependingAreaOfInterest(rcti *input,
return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output);
}
+void RotateOperation::get_area_of_interest(const int input_idx,
+ const rcti &output_area,
+ rcti &r_input_area)
+{
+ if (input_idx == DEGREE_INPUT_INDEX) {
+ /* Degrees input is always used as constant. */
+ r_input_area = COM_SINGLE_ELEM_AREA;
+ return;
+ }
+
+ ensureDegree();
+ get_area_rotation_bounds(output_area, m_centerX, m_centerY, m_sine, m_cosine, r_input_area);
+ expand_area_for_sampler(r_input_area, sampler_);
+}
+
+void RotateOperation::update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs)
+{
+ ensureDegree();
+ const MemoryBuffer *input_img = inputs[IMAGE_INPUT_INDEX];
+ for (BuffersIterator<float> it = output->iterate_with({}, area); !it.is_end(); ++it) {
+ float x = it.x;
+ float y = it.y;
+ rotate_coords(x, y, m_centerX, m_centerY, m_sine, m_cosine);
+ input_img->read_elem_sampled(x, y, sampler_, it.out);
+ }
+}
+
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_RotateOperation.h b/source/blender/compositor/operations/COM_RotateOperation.h
index d76507f9816..f0de699f9ce 100644
--- a/source/blender/compositor/operations/COM_RotateOperation.h
+++ b/source/blender/compositor/operations/COM_RotateOperation.h
@@ -18,12 +18,15 @@
#pragma once
-#include "COM_NodeOperation.h"
+#include "COM_MultiThreadedOperation.h"
namespace blender::compositor {
-class RotateOperation : public NodeOperation {
+class RotateOperation : public MultiThreadedOperation {
private:
+ constexpr static int IMAGE_INPUT_INDEX = 0;
+ constexpr static int DEGREE_INPUT_INDEX = 1;
+
SocketReader *m_imageSocket;
SocketReader *m_degreeSocket;
float m_centerX;
@@ -32,21 +35,51 @@ class RotateOperation : public NodeOperation {
float m_sine;
bool m_doDegree2RadConversion;
bool m_isDegreeSet;
+ PixelSampler sampler_;
public:
RotateOperation();
+
+ static void rotate_coords(
+ float &x, float &y, float center_x, float center_y, float sine, float cosine)
+ {
+ const float dx = x - center_x;
+ const float dy = y - center_y;
+ x = center_x + (cosine * dx + sine * dy);
+ y = center_y + (-sine * dx + cosine * dy);
+ }
+
+ static void get_area_rotation_bounds(const rcti &area,
+ const float center_x,
+ const float center_y,
+ const float sine,
+ const float cosine,
+ rcti &r_bounds);
+
bool determineDependingAreaOfInterest(rcti *input,
ReadBufferOperation *readOperation,
rcti *output) override;
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
+ void init_data() override;
void initExecution() override;
void deinitExecution() override;
+
void setDoDegree2RadConversion(bool abool)
{
this->m_doDegree2RadConversion = abool;
}
+ void set_sampler(PixelSampler sampler)
+ {
+ sampler_ = sampler;
+ }
+
void ensureDegree();
+
+ void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override;
+ void update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs) override;
};
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_ScaleOperation.cc b/source/blender/compositor/operations/COM_ScaleOperation.cc
index 5410b2c832a..ef34bc7bee6 100644
--- a/source/blender/compositor/operations/COM_ScaleOperation.cc
+++ b/source/blender/compositor/operations/COM_ScaleOperation.cc
@@ -74,17 +74,18 @@ float ScaleOperation::get_constant_scale_y()
return get_constant_scale(2, get_relative_scale_y_factor());
}
-BLI_INLINE float scale_coord(const int coord, const float center, const float relative_scale)
+void ScaleOperation::scale_area(
+ rcti &rect, float center_x, float center_y, float scale_x, float scale_y)
{
- return center + (coord - center) / relative_scale;
+ rect.xmin = scale_coord(rect.xmin, center_x, scale_x);
+ rect.xmax = scale_coord(rect.xmax, center_x, scale_x);
+ rect.ymin = scale_coord(rect.ymin, center_y, scale_y);
+ rect.ymax = scale_coord(rect.ymax, center_y, scale_y);
}
void ScaleOperation::scale_area(rcti &rect, float scale_x, float scale_y)
{
- rect.xmin = scale_coord(rect.xmin, m_centerX, scale_x);
- rect.xmax = scale_coord(rect.xmax, m_centerX, scale_x);
- rect.ymin = scale_coord(rect.ymin, m_centerY, scale_y);
- rect.ymax = scale_coord(rect.ymax, m_centerY, scale_y);
+ scale_area(rect, m_centerX, m_centerY, scale_x, scale_y);
}
void ScaleOperation::init_data()
diff --git a/source/blender/compositor/operations/COM_ScaleOperation.h b/source/blender/compositor/operations/COM_ScaleOperation.h
index 62a2cabc8e6..65762d1ce62 100644
--- a/source/blender/compositor/operations/COM_ScaleOperation.h
+++ b/source/blender/compositor/operations/COM_ScaleOperation.h
@@ -46,6 +46,9 @@ class BaseScaleOperation : public MultiThreadedOperation {
};
class ScaleOperation : public BaseScaleOperation {
+ public:
+ static constexpr float MIN_SCALE = 0.0001f;
+
protected:
SocketReader *m_inputOperation;
SocketReader *m_inputXOperation;
@@ -57,6 +60,12 @@ class ScaleOperation : public BaseScaleOperation {
ScaleOperation();
ScaleOperation(DataType data_type);
+ static float scale_coord(const float coord, const float center, const float relative_scale)
+ {
+ return center + (coord - center) / MAX2(relative_scale, MIN_SCALE);
+ }
+ static void scale_area(rcti &rect, float center_x, float center_y, float scale_x, float scale_y);
+
void init_data() override;
void initExecution() override;
void deinitExecution() override;
diff --git a/source/blender/compositor/operations/COM_ScreenLensDistortionOperation.cc b/source/blender/compositor/operations/COM_ScreenLensDistortionOperation.cc
index 634fe66b0dd..f9ba2ef69ad 100644
--- a/source/blender/compositor/operations/COM_ScreenLensDistortionOperation.cc
+++ b/source/blender/compositor/operations/COM_ScreenLensDistortionOperation.cc
@@ -17,6 +17,7 @@
*/
#include "COM_ScreenLensDistortionOperation.h"
+#include "COM_ConstantOperation.h"
#include "BLI_math.h"
#include "BLI_rand.h"
@@ -53,6 +54,35 @@ void ScreenLensDistortionOperation::setDispersion(float dispersion)
m_dispersion_const = true;
}
+void ScreenLensDistortionOperation::init_data()
+{
+ this->m_cx = 0.5f * (float)getWidth();
+ this->m_cy = 0.5f * (float)getHeight();
+
+ switch (execution_model_) {
+ case eExecutionModel::FullFrame: {
+ NodeOperation *distortion_op = get_input_operation(1);
+ NodeOperation *dispersion_op = get_input_operation(2);
+ if (!m_distortion_const && distortion_op->get_flags().is_constant_operation) {
+ m_distortion = static_cast<ConstantOperation *>(distortion_op)->get_constant_elem()[0];
+ }
+ if (!m_dispersion_const && distortion_op->get_flags().is_constant_operation) {
+ m_dispersion = static_cast<ConstantOperation *>(dispersion_op)->get_constant_elem()[0];
+ }
+ updateVariables(m_distortion, m_dispersion);
+ break;
+ }
+ case eExecutionModel::Tiled: {
+ /* If both are constant, init variables once. */
+ if (m_distortion_const && m_dispersion_const) {
+ updateVariables(m_distortion, m_dispersion);
+ m_variables_ready = true;
+ }
+ break;
+ }
+ }
+}
+
void ScreenLensDistortionOperation::initExecution()
{
this->m_inputProgram = this->getInputSocketReader(0);
@@ -61,15 +91,6 @@ void ScreenLensDistortionOperation::initExecution()
uint rng_seed = (uint)(PIL_check_seconds_timer_i() & UINT_MAX);
rng_seed ^= (uint)POINTER_AS_INT(m_inputProgram);
this->m_rng = BLI_rng_new(rng_seed);
-
- this->m_cx = 0.5f * (float)getWidth();
- this->m_cy = 0.5f * (float)getHeight();
-
- /* if both are constant, init variables once */
- if (m_distortion_const && m_dispersion_const) {
- updateVariables(m_distortion, m_dispersion);
- m_variables_ready = true;
- }
}
void *ScreenLensDistortionOperation::initializeTileData(rcti * /*rect*/)
@@ -130,7 +151,7 @@ bool ScreenLensDistortionOperation::get_delta(float r_sq,
return false;
}
-void ScreenLensDistortionOperation::accumulate(MemoryBuffer *buffer,
+void ScreenLensDistortionOperation::accumulate(const MemoryBuffer *buffer,
int a,
int b,
float r_sq,
@@ -154,7 +175,14 @@ void ScreenLensDistortionOperation::accumulate(MemoryBuffer *buffer,
float xy[2];
distort_uv(uv, t, xy);
- buffer->readBilinear(color, xy[0], xy[1]);
+ switch (execution_model_) {
+ case eExecutionModel::Tiled:
+ buffer->readBilinear(color, xy[0], xy[1]);
+ break;
+ case eExecutionModel::FullFrame:
+ buffer->read_elem_bilinear(xy[0], xy[1], color);
+ break;
+ }
sum[a] += (1.0f - tz) * color[a];
sum[b] += (tz)*color[b];
@@ -354,4 +382,143 @@ void ScreenLensDistortionOperation::updateVariables(float distortion, float disp
mul_v3_v3fl(m_k4, m_k, 4.0f);
}
+void ScreenLensDistortionOperation::get_area_of_interest(const int input_idx,
+ const rcti &UNUSED(output_area),
+ rcti &r_input_area)
+{
+ if (input_idx != 0) {
+ /* Dispersion and distorsion inputs are used as constants only. */
+ r_input_area = COM_SINGLE_ELEM_AREA;
+ }
+
+ /* XXX the original method of estimating the area-of-interest does not work
+ * it assumes a linear increase/decrease of mapped coordinates, which does not
+ * yield correct results for the area and leaves uninitialized buffer areas.
+ * So now just use the full image area, which may not be as efficient but works at least ...
+ */
+#if 1
+ NodeOperation *image = getInputOperation(0);
+ r_input_area.xmax = image->getWidth();
+ r_input_area.xmin = 0;
+ r_input_area.ymax = image->getHeight();
+ r_input_area.ymin = 0;
+
+#else /* Original method in tiled implementation. */
+ rcti newInput;
+ const float margin = 2;
+
+ BLI_rcti_init_minmax(&newInput);
+
+ if (m_dispersion_const && m_distortion_const) {
+ /* update from fixed distortion/dispersion */
+# define UPDATE_INPUT(x, y) \
+ { \
+ float coords[6]; \
+ determineUV(coords, x, y); \
+ newInput.xmin = min_ffff(newInput.xmin, coords[0], coords[2], coords[4]); \
+ newInput.ymin = min_ffff(newInput.ymin, coords[1], coords[3], coords[5]); \
+ newInput.xmax = max_ffff(newInput.xmax, coords[0], coords[2], coords[4]); \
+ newInput.ymax = max_ffff(newInput.ymax, coords[1], coords[3], coords[5]); \
+ } \
+ (void)0
+
+ UPDATE_INPUT(input->xmin, input->xmax);
+ UPDATE_INPUT(input->xmin, input->ymax);
+ UPDATE_INPUT(input->xmax, input->ymax);
+ UPDATE_INPUT(input->xmax, input->ymin);
+
+# undef UPDATE_INPUT
+ }
+ else {
+ /* use maximum dispersion 1.0 if not const */
+ float dispersion = m_dispersion_const ? m_dispersion : 1.0f;
+
+# define UPDATE_INPUT(x, y, distortion) \
+ { \
+ float coords[6]; \
+ updateVariables(distortion, dispersion); \
+ determineUV(coords, x, y); \
+ newInput.xmin = min_ffff(newInput.xmin, coords[0], coords[2], coords[4]); \
+ newInput.ymin = min_ffff(newInput.ymin, coords[1], coords[3], coords[5]); \
+ newInput.xmax = max_ffff(newInput.xmax, coords[0], coords[2], coords[4]); \
+ newInput.ymax = max_ffff(newInput.ymax, coords[1], coords[3], coords[5]); \
+ } \
+ (void)0
+
+ if (m_distortion_const) {
+ /* update from fixed distortion */
+ UPDATE_INPUT(input->xmin, input->xmax, m_distortion);
+ UPDATE_INPUT(input->xmin, input->ymax, m_distortion);
+ UPDATE_INPUT(input->xmax, input->ymax, m_distortion);
+ UPDATE_INPUT(input->xmax, input->ymin, m_distortion);
+ }
+ else {
+ /* update from min/max distortion (-1..1) */
+ UPDATE_INPUT(input->xmin, input->xmax, -1.0f);
+ UPDATE_INPUT(input->xmin, input->ymax, -1.0f);
+ UPDATE_INPUT(input->xmax, input->ymax, -1.0f);
+ UPDATE_INPUT(input->xmax, input->ymin, -1.0f);
+
+ UPDATE_INPUT(input->xmin, input->xmax, 1.0f);
+ UPDATE_INPUT(input->xmin, input->ymax, 1.0f);
+ UPDATE_INPUT(input->xmax, input->ymax, 1.0f);
+ UPDATE_INPUT(input->xmax, input->ymin, 1.0f);
+
+# undef UPDATE_INPUT
+ }
+ }
+
+ newInput.xmin -= margin;
+ newInput.ymin -= margin;
+ newInput.xmax += margin;
+ newInput.ymax += margin;
+
+ operation = getInputOperation(0);
+ if (operation->determineDependingAreaOfInterest(&newInput, readOperation, output)) {
+ return true;
+ }
+ return false;
+#endif
+}
+
+void ScreenLensDistortionOperation::update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs)
+{
+ const MemoryBuffer *input_image = inputs[0];
+ for (BuffersIterator<float> it = output->iterate_with({}, area); !it.is_end(); ++it) {
+ float xy[2] = {(float)it.x, (float)it.y};
+ float uv[2];
+ get_uv(xy, uv);
+ const float uv_dot = len_squared_v2(uv);
+
+ float delta[3][2];
+ const bool valid_r = get_delta(uv_dot, m_k4[0], uv, delta[0]);
+ const bool valid_g = get_delta(uv_dot, m_k4[1], uv, delta[1]);
+ const bool valid_b = get_delta(uv_dot, m_k4[2], uv, delta[2]);
+ if (!(valid_r && valid_g && valid_b)) {
+ zero_v4(it.out);
+ continue;
+ }
+
+ int count[3] = {0, 0, 0};
+ float sum[4] = {0, 0, 0, 0};
+ accumulate(input_image, 0, 1, uv_dot, uv, delta, sum, count);
+ accumulate(input_image, 1, 2, uv_dot, uv, delta, sum, count);
+
+ if (count[0]) {
+ it.out[0] = 2.0f * sum[0] / (float)count[0];
+ }
+ if (count[1]) {
+ it.out[1] = 2.0f * sum[1] / (float)count[1];
+ }
+ if (count[2]) {
+ it.out[2] = 2.0f * sum[2] / (float)count[2];
+ }
+
+ /* Set alpha. */
+ it.out[3] = 1.0f;
+ }
+}
+
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_ScreenLensDistortionOperation.h b/source/blender/compositor/operations/COM_ScreenLensDistortionOperation.h
index 98872bfe142..616fc8883b0 100644
--- a/source/blender/compositor/operations/COM_ScreenLensDistortionOperation.h
+++ b/source/blender/compositor/operations/COM_ScreenLensDistortionOperation.h
@@ -18,14 +18,14 @@
#pragma once
-#include "COM_NodeOperation.h"
+#include "COM_MultiThreadedOperation.h"
#include "DNA_node_types.h"
struct RNG;
namespace blender::compositor {
-class ScreenLensDistortionOperation : public NodeOperation {
+class ScreenLensDistortionOperation : public MultiThreadedOperation {
private:
/**
* Cached reference to the inputProgram
@@ -50,6 +50,8 @@ class ScreenLensDistortionOperation : public NodeOperation {
public:
ScreenLensDistortionOperation();
+ void init_data() override;
+
/**
* The inner loop of this operation.
*/
@@ -84,6 +86,11 @@ class ScreenLensDistortionOperation : public NodeOperation {
ReadBufferOperation *readOperation,
rcti *output) override;
+ void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override;
+ void update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs) override;
+
private:
void determineUV(float result[6], float x, float y) const;
void updateVariables(float distortion, float dispersion);
@@ -91,7 +98,7 @@ class ScreenLensDistortionOperation : public NodeOperation {
void get_uv(const float xy[2], float uv[2]) const;
void distort_uv(const float uv[2], float t, float xy[2]) const;
bool get_delta(float r_sq, float k4, const float uv[2], float delta[2]) const;
- void accumulate(MemoryBuffer *buffer,
+ void accumulate(const MemoryBuffer *buffer,
int a,
int b,
float r_sq,
diff --git a/source/blender/compositor/operations/COM_SetAlphaMultiplyOperation.cc b/source/blender/compositor/operations/COM_SetAlphaMultiplyOperation.cc
index 24edbc61d40..e4686ffa76d 100644
--- a/source/blender/compositor/operations/COM_SetAlphaMultiplyOperation.cc
+++ b/source/blender/compositor/operations/COM_SetAlphaMultiplyOperation.cc
@@ -28,6 +28,7 @@ SetAlphaMultiplyOperation::SetAlphaMultiplyOperation()
this->m_inputColor = nullptr;
this->m_inputAlpha = nullptr;
+ this->flags.can_be_constant = true;
}
void SetAlphaMultiplyOperation::initExecution()
@@ -56,4 +57,15 @@ void SetAlphaMultiplyOperation::deinitExecution()
this->m_inputAlpha = nullptr;
}
+void SetAlphaMultiplyOperation::update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs)
+{
+ for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) {
+ const float *color = it.in(0);
+ const float alpha = *it.in(1);
+ mul_v4_v4fl(it.out, color, alpha);
+ }
+}
+
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_SetAlphaMultiplyOperation.h b/source/blender/compositor/operations/COM_SetAlphaMultiplyOperation.h
index b4eea659fa2..44885318901 100644
--- a/source/blender/compositor/operations/COM_SetAlphaMultiplyOperation.h
+++ b/source/blender/compositor/operations/COM_SetAlphaMultiplyOperation.h
@@ -18,7 +18,7 @@
#pragma once
-#include "COM_NodeOperation.h"
+#include "COM_MultiThreadedOperation.h"
namespace blender::compositor {
@@ -27,7 +27,7 @@ namespace blender::compositor {
*
* `output color.rgba = input color.rgba * input alpha`
*/
-class SetAlphaMultiplyOperation : public NodeOperation {
+class SetAlphaMultiplyOperation : public MultiThreadedOperation {
private:
SocketReader *m_inputColor;
SocketReader *m_inputAlpha;
@@ -39,6 +39,10 @@ class SetAlphaMultiplyOperation : public NodeOperation {
void initExecution() override;
void deinitExecution() override;
+
+ void update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs) override;
};
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_SetAlphaReplaceOperation.cc b/source/blender/compositor/operations/COM_SetAlphaReplaceOperation.cc
index 90bfc814b09..434f5d9b63c 100644
--- a/source/blender/compositor/operations/COM_SetAlphaReplaceOperation.cc
+++ b/source/blender/compositor/operations/COM_SetAlphaReplaceOperation.cc
@@ -28,6 +28,7 @@ SetAlphaReplaceOperation::SetAlphaReplaceOperation()
this->m_inputColor = nullptr;
this->m_inputAlpha = nullptr;
+ this->flags.can_be_constant = true;
}
void SetAlphaReplaceOperation::initExecution()
@@ -54,4 +55,16 @@ void SetAlphaReplaceOperation::deinitExecution()
this->m_inputAlpha = nullptr;
}
+void SetAlphaReplaceOperation::update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs)
+{
+ for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) {
+ const float *color = it.in(0);
+ const float alpha = *it.in(1);
+ copy_v3_v3(it.out, color);
+ it.out[3] = alpha;
+ }
+}
+
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_SetAlphaReplaceOperation.h b/source/blender/compositor/operations/COM_SetAlphaReplaceOperation.h
index c84299b6d82..2c2d4cddf5b 100644
--- a/source/blender/compositor/operations/COM_SetAlphaReplaceOperation.h
+++ b/source/blender/compositor/operations/COM_SetAlphaReplaceOperation.h
@@ -18,7 +18,7 @@
#pragma once
-#include "COM_NodeOperation.h"
+#include "COM_MultiThreadedOperation.h"
namespace blender::compositor {
@@ -26,7 +26,7 @@ namespace blender::compositor {
* this program converts an input color to an output value.
* it assumes we are in sRGB color space.
*/
-class SetAlphaReplaceOperation : public NodeOperation {
+class SetAlphaReplaceOperation : public MultiThreadedOperation {
private:
SocketReader *m_inputColor;
SocketReader *m_inputAlpha;
@@ -44,6 +44,10 @@ class SetAlphaReplaceOperation : public NodeOperation {
void initExecution() override;
void deinitExecution() override;
+
+ void update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs) override;
};
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_TransformOperation.cc b/source/blender/compositor/operations/COM_TransformOperation.cc
new file mode 100644
index 00000000000..2feaa0ae16d
--- /dev/null
+++ b/source/blender/compositor/operations/COM_TransformOperation.cc
@@ -0,0 +1,156 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Copyright 2021, Blender Foundation.
+ */
+
+#include "COM_TransformOperation.h"
+#include "COM_ConstantOperation.h"
+#include "COM_RotateOperation.h"
+#include "COM_ScaleOperation.h"
+
+#include "BLI_math.h"
+
+namespace blender::compositor {
+
+TransformOperation::TransformOperation()
+{
+ addInputSocket(DataType::Color);
+ addInputSocket(DataType::Value);
+ addInputSocket(DataType::Value);
+ addInputSocket(DataType::Value);
+ addInputSocket(DataType::Value);
+ addOutputSocket(DataType::Color);
+ translate_factor_x_ = 1.0f;
+ translate_factor_y_ = 1.0f;
+ convert_degree_to_rad_ = false;
+ sampler_ = PixelSampler::Bilinear;
+ invert_ = false;
+}
+
+void TransformOperation::init_data()
+{
+ /* Translation. */
+ translate_x_ = 0;
+ NodeOperation *x_op = getInputOperation(X_INPUT_INDEX);
+ if (x_op->get_flags().is_constant_operation) {
+ translate_x_ = static_cast<ConstantOperation *>(x_op)->get_constant_elem()[0] *
+ translate_factor_x_;
+ }
+ translate_y_ = 0;
+ NodeOperation *y_op = getInputOperation(Y_INPUT_INDEX);
+ if (y_op->get_flags().is_constant_operation) {
+ translate_y_ = static_cast<ConstantOperation *>(y_op)->get_constant_elem()[0] *
+ translate_factor_y_;
+ }
+
+ /* Scaling. */
+ scale_center_x_ = getWidth() / 2.0;
+ scale_center_y_ = getHeight() / 2.0;
+ constant_scale_ = 1.0f;
+ NodeOperation *scale_op = getInputOperation(SCALE_INPUT_INDEX);
+ if (scale_op->get_flags().is_constant_operation) {
+ constant_scale_ = static_cast<ConstantOperation *>(scale_op)->get_constant_elem()[0];
+ }
+
+ /* Rotation. */
+ rotate_center_x_ = (getWidth() - 1.0) / 2.0;
+ rotate_center_y_ = (getHeight() - 1.0) / 2.0;
+ NodeOperation *degree_op = getInputOperation(DEGREE_INPUT_INDEX);
+ const bool is_constant_degree = degree_op->get_flags().is_constant_operation;
+ const float degree = is_constant_degree ?
+ static_cast<ConstantOperation *>(degree_op)->get_constant_elem()[0] :
+ 0.0f;
+ const double rad = convert_degree_to_rad_ ? DEG2RAD((double)degree) : degree;
+ rotate_cosine_ = cos(rad);
+ rotate_sine_ = sin(rad);
+}
+
+void TransformOperation::get_area_of_interest(const int input_idx,
+ const rcti &output_area,
+ rcti &r_input_area)
+{
+ switch (input_idx) {
+ case IMAGE_INPUT_INDEX: {
+ BLI_rcti_translate(&r_input_area, translate_x_, translate_y_);
+ ScaleOperation::scale_area(
+ r_input_area, scale_center_x_, scale_center_y_, constant_scale_, constant_scale_);
+ RotateOperation::get_area_rotation_bounds(r_input_area,
+ rotate_center_x_,
+ rotate_center_y_,
+ rotate_sine_,
+ rotate_cosine_,
+ r_input_area);
+ expand_area_for_sampler(r_input_area, sampler_);
+ break;
+ }
+ case X_INPUT_INDEX:
+ case Y_INPUT_INDEX:
+ case DEGREE_INPUT_INDEX: {
+ r_input_area = COM_SINGLE_ELEM_AREA;
+ break;
+ }
+ case SCALE_INPUT_INDEX: {
+ r_input_area = output_area;
+ break;
+ }
+ }
+}
+
+void TransformOperation::update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs)
+{
+ const MemoryBuffer *input_img = inputs[IMAGE_INPUT_INDEX];
+ MemoryBuffer *input_scale = inputs[SCALE_INPUT_INDEX];
+ BuffersIterator<float> it = output->iterate_with({input_scale}, area);
+ if (invert_) {
+ transform_inverted(it, input_img);
+ }
+ else {
+ transform(it, input_img);
+ }
+}
+
+void TransformOperation::transform(BuffersIterator<float> &it, const MemoryBuffer *input_img)
+{
+ for (; !it.is_end(); ++it) {
+ const float scale = *it.in(0);
+ float x = it.x - translate_x_;
+ float y = it.y - translate_y_;
+ RotateOperation::rotate_coords(
+ x, y, rotate_center_x_, rotate_center_y_, rotate_sine_, rotate_cosine_);
+ x = ScaleOperation::scale_coord(x, scale_center_x_, scale);
+ y = ScaleOperation::scale_coord(y, scale_center_y_, scale);
+ input_img->read_elem_sampled(x, y, sampler_, it.out);
+ }
+}
+
+void TransformOperation::transform_inverted(BuffersIterator<float> &it,
+ const MemoryBuffer *input_img)
+{
+ for (; !it.is_end(); ++it) {
+ const float scale = *it.in(0);
+ float x = ScaleOperation::scale_coord(it.x, scale_center_x_, scale);
+ float y = ScaleOperation::scale_coord(it.y, scale_center_y_, scale);
+ RotateOperation::rotate_coords(
+ x, y, rotate_center_x_, rotate_center_y_, rotate_sine_, rotate_cosine_);
+ x -= translate_x_;
+ y -= translate_y_;
+ input_img->read_elem_sampled(x, y, sampler_, it.out);
+ }
+}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_TransformOperation.h b/source/blender/compositor/operations/COM_TransformOperation.h
new file mode 100644
index 00000000000..480998a0207
--- /dev/null
+++ b/source/blender/compositor/operations/COM_TransformOperation.h
@@ -0,0 +1,87 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Copyright 2021, Blender Foundation.
+ */
+
+#pragma once
+
+#include "COM_MultiThreadedOperation.h"
+
+namespace blender::compositor {
+
+class TransformOperation : public MultiThreadedOperation {
+ private:
+ constexpr static int IMAGE_INPUT_INDEX = 0;
+ constexpr static int X_INPUT_INDEX = 1;
+ constexpr static int Y_INPUT_INDEX = 2;
+ constexpr static int DEGREE_INPUT_INDEX = 3;
+ constexpr static int SCALE_INPUT_INDEX = 4;
+
+ float scale_center_x_;
+ float scale_center_y_;
+ float rotate_center_x_;
+ float rotate_center_y_;
+ float rotate_cosine_;
+ float rotate_sine_;
+ float translate_x_;
+ float translate_y_;
+ float constant_scale_;
+
+ /* Set variables. */
+ PixelSampler sampler_;
+ bool convert_degree_to_rad_;
+ float translate_factor_x_;
+ float translate_factor_y_;
+ bool invert_;
+
+ public:
+ TransformOperation();
+
+ void set_translate_factor_xy(float x, float y)
+ {
+ translate_factor_x_ = x;
+ translate_factor_y_ = y;
+ }
+
+ void set_convert_rotate_degree_to_rad(bool value)
+ {
+ convert_degree_to_rad_ = value;
+ }
+
+ void set_sampler(PixelSampler sampler)
+ {
+ sampler_ = sampler;
+ }
+
+ void set_invert(bool value)
+ {
+ invert_ = value;
+ }
+
+ void init_data() override;
+ void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override;
+ void update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs) override;
+
+ private:
+ /** Translate -> Rotate -> Scale. */
+ void transform(BuffersIterator<float> &it, const MemoryBuffer *input_img);
+ /** Scale -> Rotate -> Translate. */
+ void transform_inverted(BuffersIterator<float> &it, const MemoryBuffer *input_img);
+};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_VariableSizeBokehBlurOperation.cc b/source/blender/compositor/operations/COM_VariableSizeBokehBlurOperation.cc
index 19cd5a53084..6af6f5a6244 100644
--- a/source/blender/compositor/operations/COM_VariableSizeBokehBlurOperation.cc
+++ b/source/blender/compositor/operations/COM_VariableSizeBokehBlurOperation.cc
@@ -18,6 +18,7 @@
#include "COM_VariableSizeBokehBlurOperation.h"
#include "BLI_math.h"
+#include "COM_ExecutionSystem.h"
#include "COM_OpenCLDevice.h"
#include "RE_pipeline.h"
@@ -276,6 +277,166 @@ bool VariableSizeBokehBlurOperation::determineDependingAreaOfInterest(
return false;
}
+void VariableSizeBokehBlurOperation::get_area_of_interest(const int input_idx,
+ const rcti &output_area,
+ rcti &r_input_area)
+{
+ switch (input_idx) {
+ case IMAGE_INPUT_INDEX:
+ case SIZE_INPUT_INDEX: {
+ const float max_dim = MAX2(getWidth(), getHeight());
+ const float scalar = m_do_size_scale ? (max_dim / 100.0f) : 1.0f;
+ const int max_blur_scalar = m_maxBlur * scalar;
+ r_input_area.xmax = output_area.xmax + max_blur_scalar + 2;
+ r_input_area.xmin = output_area.xmin - max_blur_scalar - 2;
+ r_input_area.ymax = output_area.ymax + max_blur_scalar + 2;
+ r_input_area.ymin = output_area.ymin - max_blur_scalar - 2;
+ break;
+ }
+ case BOKEH_INPUT_INDEX: {
+ r_input_area.xmax = COM_BLUR_BOKEH_PIXELS;
+ r_input_area.xmin = 0;
+ r_input_area.ymax = COM_BLUR_BOKEH_PIXELS;
+ r_input_area.ymin = 0;
+ break;
+ }
+#ifdef COM_DEFOCUS_SEARCH
+ case DEFOCUS_INPUT_INDEX: {
+ r_input_area.xmax = (output_area.xmax / InverseSearchRadiusOperation::DIVIDER) + 1;
+ r_input_area.xmin = (output_area.xmin / InverseSearchRadiusOperation::DIVIDER) - 1;
+ r_input_area.ymax = (output_area.ymax / InverseSearchRadiusOperation::DIVIDER) + 1;
+ r_input_area.ymin = (output_area.ymin / InverseSearchRadiusOperation::DIVIDER) - 1;
+ break;
+ }
+#endif
+ }
+}
+
+struct PixelData {
+ float multiplier_accum[4];
+ float color_accum[4];
+ float threshold;
+ float scalar;
+ float size_center;
+ int max_blur_scalar;
+ int step;
+ MemoryBuffer *bokeh_input;
+ MemoryBuffer *size_input;
+ MemoryBuffer *image_input;
+ int image_width;
+ int image_height;
+};
+
+static void blur_pixel(int x, int y, PixelData &p)
+{
+ BLI_assert(p.bokeh_input->getWidth() == COM_BLUR_BOKEH_PIXELS);
+ BLI_assert(p.bokeh_input->getHeight() == COM_BLUR_BOKEH_PIXELS);
+
+#ifdef COM_DEFOCUS_SEARCH
+ float search[4];
+ inputs[DEFOCUS_INPUT_INDEX]->read_elem_checked(x / InverseSearchRadiusOperation::DIVIDER,
+ y / InverseSearchRadiusOperation::DIVIDER,
+ search);
+ const int minx = search[0];
+ const int miny = search[1];
+ const int maxx = search[2];
+ const int maxy = search[3];
+#else
+ const int minx = MAX2(x - p.max_blur_scalar, 0);
+ const int miny = MAX2(y - p.max_blur_scalar, 0);
+ const int maxx = MIN2(x + p.max_blur_scalar, p.image_width);
+ const int maxy = MIN2(y + p.max_blur_scalar, p.image_height);
+#endif
+
+ const int color_row_stride = p.image_input->row_stride * p.step;
+ const int color_elem_stride = p.image_input->elem_stride * p.step;
+ const int size_row_stride = p.size_input->row_stride * p.step;
+ const int size_elem_stride = p.size_input->elem_stride * p.step;
+ const float *row_color = p.image_input->get_elem(minx, miny);
+ const float *row_size = p.size_input->get_elem(minx, miny);
+ for (int ny = miny; ny < maxy;
+ ny += p.step, row_size += size_row_stride, row_color += color_row_stride) {
+ const float dy = ny - y;
+ const float *size_elem = row_size;
+ const float *color = row_color;
+ for (int nx = minx; nx < maxx;
+ nx += p.step, size_elem += size_elem_stride, color += color_elem_stride) {
+ if (nx == x && ny == y) {
+ continue;
+ }
+ const float size = MIN2(size_elem[0] * p.scalar, p.size_center);
+ if (size <= p.threshold) {
+ continue;
+ }
+ const float dx = nx - x;
+ if (size <= fabsf(dx) || size <= fabsf(dy)) {
+ continue;
+ }
+
+ /* XXX: There is no way to ensure bokeh input is an actual bokeh with #COM_BLUR_BOKEH_PIXELS
+ * size, anything may be connected. Use the real input size and remove asserts? */
+ const float u = (float)(COM_BLUR_BOKEH_PIXELS / 2) +
+ (dx / size) * (float)((COM_BLUR_BOKEH_PIXELS / 2) - 1);
+ const float v = (float)(COM_BLUR_BOKEH_PIXELS / 2) +
+ (dy / size) * (float)((COM_BLUR_BOKEH_PIXELS / 2) - 1);
+ float bokeh[4];
+ p.bokeh_input->read_elem_checked(u, v, bokeh);
+ madd_v4_v4v4(p.color_accum, bokeh, color);
+ add_v4_v4(p.multiplier_accum, bokeh);
+ }
+ }
+}
+
+void VariableSizeBokehBlurOperation::update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs)
+{
+ PixelData p;
+ p.bokeh_input = inputs[BOKEH_INPUT_INDEX];
+ p.size_input = inputs[SIZE_INPUT_INDEX];
+ p.image_input = inputs[IMAGE_INPUT_INDEX];
+ p.step = QualityStepHelper::getStep();
+ p.threshold = m_threshold;
+ p.image_width = this->getWidth();
+ p.image_height = this->getHeight();
+
+ rcti scalar_area;
+ this->get_area_of_interest(SIZE_INPUT_INDEX, area, scalar_area);
+ BLI_rcti_isect(&scalar_area, &p.size_input->get_rect(), &scalar_area);
+ const float max_size = p.size_input->get_max_value(scalar_area);
+
+ const float max_dim = MAX2(this->getWidth(), this->getHeight());
+ p.scalar = m_do_size_scale ? (max_dim / 100.0f) : 1.0f;
+ p.max_blur_scalar = static_cast<int>(max_size * p.scalar);
+ CLAMP(p.max_blur_scalar, 1, m_maxBlur);
+
+ for (BuffersIterator<float> it = output->iterate_with({p.image_input, p.size_input}, area);
+ !it.is_end();
+ ++it) {
+ const float *color = it.in(0);
+ const float size = *it.in(1);
+ copy_v4_v4(p.color_accum, color);
+ copy_v4_fl(p.multiplier_accum, 1.0f);
+ p.size_center = size * p.scalar;
+
+ if (p.size_center > p.threshold) {
+ blur_pixel(it.x, it.y, p);
+ }
+
+ it.out[0] = p.color_accum[0] / p.multiplier_accum[0];
+ it.out[1] = p.color_accum[1] / p.multiplier_accum[1];
+ it.out[2] = p.color_accum[2] / p.multiplier_accum[2];
+ it.out[3] = p.color_accum[3] / p.multiplier_accum[3];
+
+ /* Blend in out values over the threshold, otherwise we get sharp, ugly transitions. */
+ if ((p.size_center > p.threshold) && (p.size_center < p.threshold * 2.0f)) {
+ /* Factor from 0-1. */
+ const float fac = (p.size_center - p.threshold) / p.threshold;
+ interp_v4_v4v4(it.out, color, it.out, fac);
+ }
+ }
+}
+
#ifdef COM_DEFOCUS_SEARCH
// InverseSearchRadiusOperation
InverseSearchRadiusOperation::InverseSearchRadiusOperation()
diff --git a/source/blender/compositor/operations/COM_VariableSizeBokehBlurOperation.h b/source/blender/compositor/operations/COM_VariableSizeBokehBlurOperation.h
index baeab6a646e..d6df9f5b858 100644
--- a/source/blender/compositor/operations/COM_VariableSizeBokehBlurOperation.h
+++ b/source/blender/compositor/operations/COM_VariableSizeBokehBlurOperation.h
@@ -18,15 +18,22 @@
#pragma once
-#include "COM_NodeOperation.h"
+#include "COM_MultiThreadedOperation.h"
#include "COM_QualityStepHelper.h"
namespace blender::compositor {
//#define COM_DEFOCUS_SEARCH
-class VariableSizeBokehBlurOperation : public NodeOperation, public QualityStepHelper {
+class VariableSizeBokehBlurOperation : public MultiThreadedOperation, public QualityStepHelper {
private:
+ static constexpr int IMAGE_INPUT_INDEX = 0;
+ static constexpr int BOKEH_INPUT_INDEX = 1;
+ static constexpr int SIZE_INPUT_INDEX = 2;
+#ifdef COM_DEFOCUS_SEARCH
+ static constexpr int DEFOCUS_INPUT_INDEX = 3;
+#endif
+
int m_maxBlur;
float m_threshold;
bool m_do_size_scale; /* scale size, matching 'BokehBlurNode' */
@@ -84,8 +91,14 @@ class VariableSizeBokehBlurOperation : public NodeOperation, public QualityStepH
MemoryBuffer **inputMemoryBuffers,
std::list<cl_mem> *clMemToCleanUp,
std::list<cl_kernel> *clKernelsToCleanUp) override;
+
+ void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override;
+ void update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs) override;
};
+/* Currently unused. If ever used, it needs fullframe implementation. */
#ifdef COM_DEFOCUS_SEARCH
class InverseSearchRadiusOperation : public NodeOperation {
private:
diff --git a/source/blender/draw/engines/eevee/eevee_lookdev.c b/source/blender/draw/engines/eevee/eevee_lookdev.c
index f4dc553e982..879a7b08eba 100644
--- a/source/blender/draw/engines/eevee/eevee_lookdev.c
+++ b/source/blender/draw/engines/eevee/eevee_lookdev.c
@@ -246,7 +246,7 @@ void EEVEE_lookdev_cache_init(EEVEE_Data *vedata,
DRW_shgroup_uniform_float_copy(grp, "studioLightIntensity", shading->studiolight_intensity);
BKE_studiolight_ensure_flag(sl, STUDIOLIGHT_EQUIRECT_RADIANCE_GPUTEXTURE);
DRW_shgroup_uniform_texture_ex(grp, "studioLight", sl->equirect_radiance_gputexture, state);
- /* Do not fadeout when doing probe rendering, only when drawing the background */
+ /* Do not fade-out when doing probe rendering, only when drawing the background. */
DRW_shgroup_uniform_float_copy(grp, "backgroundAlpha", 1.0f);
}
else {
diff --git a/source/blender/draw/engines/image/image_engine.c b/source/blender/draw/engines/image/image_engine.c
index 395d50fbc6b..438c95118ef 100644
--- a/source/blender/draw/engines/image/image_engine.c
+++ b/source/blender/draw/engines/image/image_engine.c
@@ -111,7 +111,9 @@ static void space_image_gpu_texture_get(Image *image,
/* Update multi-index and pass for the current eye. */
BKE_image_multilayer_index(image->rr, &sima->iuser);
}
- BKE_image_multiview_index(image, &sima->iuser);
+ else {
+ BKE_image_multiview_index(image, &sima->iuser);
+ }
if (ibuf) {
const int sima_flag = sima->flag & ED_space_image_get_display_channel_mask(ibuf);
diff --git a/source/blender/draw/engines/overlay/overlay_extra.c b/source/blender/draw/engines/overlay/overlay_extra.c
index 2a9080eb217..04d90919a20 100644
--- a/source/blender/draw/engines/overlay/overlay_extra.c
+++ b/source/blender/draw/engines/overlay/overlay_extra.c
@@ -864,7 +864,6 @@ typedef union OVERLAY_CameraInstanceData {
static void camera_view3d_reconstruction(OVERLAY_ExtraCallBuffers *cb,
Scene *scene,
View3D *v3d,
- Object *camera_object,
Object *ob,
const float color[4])
{
@@ -943,7 +942,7 @@ static void camera_view3d_reconstruction(OVERLAY_ExtraCallBuffers *cb,
}
if (is_select) {
- DRW_select_load_id(camera_object->runtime.select_id | (track_index << 16));
+ DRW_select_load_id(ob->runtime.select_id | (track_index << 16));
track_index++;
}
@@ -1251,7 +1250,7 @@ void OVERLAY_camera_cache_populate(OVERLAY_Data *vedata, Object *ob)
/* Motion Tracking. */
if ((v3d->flag2 & V3D_SHOW_RECONSTRUCTION) != 0) {
- camera_view3d_reconstruction(cb, scene, v3d, camera_object, ob, color_p);
+ camera_view3d_reconstruction(cb, scene, v3d, ob, color_p);
}
/* Background images. */
diff --git a/source/blender/draw/intern/draw_cache_extract.h b/source/blender/draw/intern/draw_cache_extract.h
index 7dc468d1a73..db96d6a774f 100644
--- a/source/blender/draw/intern/draw_cache_extract.h
+++ b/source/blender/draw/intern/draw_cache_extract.h
@@ -101,7 +101,7 @@ BLI_INLINE int mesh_render_mat_len_get(const Mesh *me)
return MAX2(1, me->totcol);
}
-typedef struct MeshBufferCache {
+typedef struct MeshBufferList {
/* Every VBO below contains at least enough
* data for every loops in the mesh (except fdots and skin roots).
* For some VBOs, it extends to (in this exact order) :
@@ -152,16 +152,91 @@ typedef struct MeshBufferCache {
GPUIndexBuf *edituv_points;
GPUIndexBuf *edituv_fdots;
} ibo;
- /* Index buffer per material. These are subranges of `ibo.tris` */
- GPUIndexBuf **tris_per_mat;
-} MeshBufferCache;
+} MeshBufferList;
+
+typedef struct MeshBatchList {
+ /* Surfaces / Render */
+ GPUBatch *surface;
+ GPUBatch *surface_weights;
+ /* Edit mode */
+ GPUBatch *edit_triangles;
+ GPUBatch *edit_vertices;
+ GPUBatch *edit_edges;
+ GPUBatch *edit_vnor;
+ GPUBatch *edit_lnor;
+ GPUBatch *edit_fdots;
+ GPUBatch *edit_mesh_analysis;
+ GPUBatch *edit_skin_roots;
+ /* Edit UVs */
+ GPUBatch *edituv_faces_stretch_area;
+ GPUBatch *edituv_faces_stretch_angle;
+ GPUBatch *edituv_faces;
+ GPUBatch *edituv_edges;
+ GPUBatch *edituv_verts;
+ GPUBatch *edituv_fdots;
+ /* Edit selection */
+ GPUBatch *edit_selection_verts;
+ GPUBatch *edit_selection_edges;
+ GPUBatch *edit_selection_faces;
+ GPUBatch *edit_selection_fdots;
+ /* Common display / Other */
+ GPUBatch *all_verts;
+ GPUBatch *all_edges;
+ GPUBatch *loose_edges;
+ GPUBatch *edge_detection;
+ GPUBatch *wire_edges; /* Individual edges with face normals. */
+ GPUBatch *wire_loops; /* Loops around faces. no edges between selected faces */
+ GPUBatch *wire_loops_uvs; /* Same as wire_loops but only has uvs. */
+ GPUBatch *sculpt_overlays;
+} MeshBatchList;
+
+#define MBC_BATCH_LEN (sizeof(MeshBatchList) / sizeof(void *))
+#define MBC_VBO_LEN (sizeof(((MeshBufferList){0}).vbo) / sizeof(void *))
+#define MBC_IBO_LEN (sizeof(((MeshBufferList){0}).ibo) / sizeof(void *))
+
+#define MBC_BATCH_INDEX(batch) (offsetof(MeshBatchList, batch) / sizeof(void *))
+
+typedef enum DRWBatchFlag {
+ MBC_SURFACE = (1u << MBC_BATCH_INDEX(surface)),
+ MBC_SURFACE_WEIGHTS = (1u << MBC_BATCH_INDEX(surface_weights)),
+ MBC_EDIT_TRIANGLES = (1u << MBC_BATCH_INDEX(edit_triangles)),
+ MBC_EDIT_VERTICES = (1u << MBC_BATCH_INDEX(edit_vertices)),
+ MBC_EDIT_EDGES = (1u << MBC_BATCH_INDEX(edit_edges)),
+ MBC_EDIT_VNOR = (1u << MBC_BATCH_INDEX(edit_vnor)),
+ MBC_EDIT_LNOR = (1u << MBC_BATCH_INDEX(edit_lnor)),
+ MBC_EDIT_FACEDOTS = (1u << MBC_BATCH_INDEX(edit_fdots)),
+ MBC_EDIT_MESH_ANALYSIS = (1u << MBC_BATCH_INDEX(edit_mesh_analysis)),
+ MBC_SKIN_ROOTS = (1u << MBC_BATCH_INDEX(edit_skin_roots)),
+ MBC_EDITUV_FACES_STRETCH_AREA = (1u << MBC_BATCH_INDEX(edituv_faces_stretch_area)),
+ MBC_EDITUV_FACES_STRETCH_ANGLE = (1u << MBC_BATCH_INDEX(edituv_faces_stretch_angle)),
+ MBC_EDITUV_FACES = (1u << MBC_BATCH_INDEX(edituv_faces)),
+ MBC_EDITUV_EDGES = (1u << MBC_BATCH_INDEX(edituv_edges)),
+ MBC_EDITUV_VERTS = (1u << MBC_BATCH_INDEX(edituv_verts)),
+ MBC_EDITUV_FACEDOTS = (1u << MBC_BATCH_INDEX(edituv_fdots)),
+ MBC_EDIT_SELECTION_VERTS = (1u << MBC_BATCH_INDEX(edit_selection_verts)),
+ MBC_EDIT_SELECTION_EDGES = (1u << MBC_BATCH_INDEX(edit_selection_edges)),
+ MBC_EDIT_SELECTION_FACES = (1u << MBC_BATCH_INDEX(edit_selection_faces)),
+ MBC_EDIT_SELECTION_FACEDOTS = (1u << MBC_BATCH_INDEX(edit_selection_fdots)),
+ MBC_ALL_VERTS = (1u << MBC_BATCH_INDEX(all_verts)),
+ MBC_ALL_EDGES = (1u << MBC_BATCH_INDEX(all_edges)),
+ MBC_LOOSE_EDGES = (1u << MBC_BATCH_INDEX(loose_edges)),
+ MBC_EDGE_DETECTION = (1u << MBC_BATCH_INDEX(edge_detection)),
+ MBC_WIRE_EDGES = (1u << MBC_BATCH_INDEX(wire_edges)),
+ MBC_WIRE_LOOPS = (1u << MBC_BATCH_INDEX(wire_loops)),
+ MBC_WIRE_LOOPS_UVS = (1u << MBC_BATCH_INDEX(wire_loops_uvs)),
+ MBC_SCULPT_OVERLAYS = (1u << MBC_BATCH_INDEX(sculpt_overlays)),
+} DRWBatchFlag;
+
+BLI_STATIC_ASSERT(MBC_BATCH_LEN < 32, "Number of batches exceeded the limit of bit fields");
/**
* Data that are kept around between extractions to reduce rebuilding time.
*
* - Loose geometry.
*/
-typedef struct MeshBufferExtractionCache {
+typedef struct MeshBufferCache {
+ MeshBufferList buff;
+
struct {
int edge_len;
int vert_len;
@@ -174,7 +249,7 @@ typedef struct MeshBufferExtractionCache {
int *mat_tri_len;
int visible_tri_len;
} poly_sorted;
-} MeshBufferExtractionCache;
+} MeshBufferCache;
#define FOREACH_MESH_BUFFER_CACHE(batch_cache, mbc) \
for (MeshBufferCache *mbc = &batch_cache->final; \
@@ -186,50 +261,15 @@ typedef struct MeshBufferExtractionCache {
typedef struct MeshBatchCache {
MeshBufferCache final, cage, uv_cage;
- MeshBufferExtractionCache final_extraction_cache;
- MeshBufferExtractionCache cage_extraction_cache;
- MeshBufferExtractionCache uv_cage_extraction_cache;
+ MeshBatchList batch;
- struct {
- /* Surfaces / Render */
- GPUBatch *surface;
- GPUBatch *surface_weights;
- /* Edit mode */
- GPUBatch *edit_triangles;
- GPUBatch *edit_vertices;
- GPUBatch *edit_edges;
- GPUBatch *edit_vnor;
- GPUBatch *edit_lnor;
- GPUBatch *edit_fdots;
- GPUBatch *edit_mesh_analysis;
- GPUBatch *edit_skin_roots;
- /* Edit UVs */
- GPUBatch *edituv_faces_stretch_area;
- GPUBatch *edituv_faces_stretch_angle;
- GPUBatch *edituv_faces;
- GPUBatch *edituv_edges;
- GPUBatch *edituv_verts;
- GPUBatch *edituv_fdots;
- /* Edit selection */
- GPUBatch *edit_selection_verts;
- GPUBatch *edit_selection_edges;
- GPUBatch *edit_selection_faces;
- GPUBatch *edit_selection_fdots;
- /* Common display / Other */
- GPUBatch *all_verts;
- GPUBatch *all_edges;
- GPUBatch *loose_edges;
- GPUBatch *edge_detection;
- GPUBatch *wire_edges; /* Individual edges with face normals. */
- GPUBatch *wire_loops; /* Loops around faces. no edges between selected faces */
- GPUBatch *wire_loops_uvs; /* Same as wire_loops but only has uvs. */
- GPUBatch *sculpt_overlays;
- } batch;
+ /* Index buffer per material. These are subranges of `ibo.tris` */
+ GPUIndexBuf **tris_per_mat;
GPUBatch **surface_per_mat;
- uint32_t batch_requested; /* DRWBatchFlag */
- uint32_t batch_ready; /* DRWBatchFlag */
+ DRWBatchFlag batch_requested; /* DRWBatchFlag */
+ DRWBatchFlag batch_ready; /* DRWBatchFlag */
/* settings to determine if cache is invalid */
int edge_len;
@@ -259,55 +299,13 @@ typedef struct MeshBatchCache {
bool no_loose_wire;
} MeshBatchCache;
-#define MBC_BATCH_LEN (sizeof(((MeshBatchCache){0}).batch) / sizeof(void *))
-#define MBC_VBO_LEN (sizeof(((MeshBufferCache){0}).vbo) / sizeof(void *))
-#define MBC_IBO_LEN (sizeof(((MeshBufferCache){0}).ibo) / sizeof(void *))
-
-#define MBC_BATCH_INDEX(batch_name) \
- ((offsetof(MeshBatchCache, batch_name) - offsetof(MeshBatchCache, batch)) / sizeof(void *))
-
-typedef enum DRWBatchFlag {
- MBC_SURFACE = (1u << MBC_BATCH_INDEX(batch.surface)),
- MBC_SURFACE_WEIGHTS = (1u << MBC_BATCH_INDEX(batch.surface_weights)),
- MBC_EDIT_TRIANGLES = (1u << MBC_BATCH_INDEX(batch.edit_triangles)),
- MBC_EDIT_VERTICES = (1u << MBC_BATCH_INDEX(batch.edit_vertices)),
- MBC_EDIT_EDGES = (1u << MBC_BATCH_INDEX(batch.edit_edges)),
- MBC_EDIT_VNOR = (1u << MBC_BATCH_INDEX(batch.edit_vnor)),
- MBC_EDIT_LNOR = (1u << MBC_BATCH_INDEX(batch.edit_lnor)),
- MBC_EDIT_FACEDOTS = (1u << MBC_BATCH_INDEX(batch.edit_fdots)),
- MBC_EDIT_MESH_ANALYSIS = (1u << MBC_BATCH_INDEX(batch.edit_mesh_analysis)),
- MBC_SKIN_ROOTS = (1u << MBC_BATCH_INDEX(batch.edit_skin_roots)),
- MBC_EDITUV_FACES_STRETCH_AREA = (1u << MBC_BATCH_INDEX(batch.edituv_faces_stretch_area)),
- MBC_EDITUV_FACES_STRETCH_ANGLE = (1u << MBC_BATCH_INDEX(batch.edituv_faces_stretch_angle)),
- MBC_EDITUV_FACES = (1u << MBC_BATCH_INDEX(batch.edituv_faces)),
- MBC_EDITUV_EDGES = (1u << MBC_BATCH_INDEX(batch.edituv_edges)),
- MBC_EDITUV_VERTS = (1u << MBC_BATCH_INDEX(batch.edituv_verts)),
- MBC_EDITUV_FACEDOTS = (1u << MBC_BATCH_INDEX(batch.edituv_fdots)),
- MBC_EDIT_SELECTION_VERTS = (1u << MBC_BATCH_INDEX(batch.edit_selection_verts)),
- MBC_EDIT_SELECTION_EDGES = (1u << MBC_BATCH_INDEX(batch.edit_selection_edges)),
- MBC_EDIT_SELECTION_FACES = (1u << MBC_BATCH_INDEX(batch.edit_selection_faces)),
- MBC_EDIT_SELECTION_FACEDOTS = (1u << MBC_BATCH_INDEX(batch.edit_selection_fdots)),
- MBC_ALL_VERTS = (1u << MBC_BATCH_INDEX(batch.all_verts)),
- MBC_ALL_EDGES = (1u << MBC_BATCH_INDEX(batch.all_edges)),
- MBC_LOOSE_EDGES = (1u << MBC_BATCH_INDEX(batch.loose_edges)),
- MBC_EDGE_DETECTION = (1u << MBC_BATCH_INDEX(batch.edge_detection)),
- MBC_WIRE_EDGES = (1u << MBC_BATCH_INDEX(batch.wire_edges)),
- MBC_WIRE_LOOPS = (1u << MBC_BATCH_INDEX(batch.wire_loops)),
- MBC_WIRE_LOOPS_UVS = (1u << MBC_BATCH_INDEX(batch.wire_loops_uvs)),
- MBC_SCULPT_OVERLAYS = (1u << MBC_BATCH_INDEX(batch.sculpt_overlays)),
-} DRWBatchFlag;
-
-BLI_STATIC_ASSERT(MBC_BATCH_INDEX(surface_per_mat) < 32,
- "Number of batches exceeded the limit of bit fields");
-
#define MBC_EDITUV \
(MBC_EDITUV_FACES_STRETCH_AREA | MBC_EDITUV_FACES_STRETCH_ANGLE | MBC_EDITUV_FACES | \
MBC_EDITUV_EDGES | MBC_EDITUV_VERTS | MBC_EDITUV_FACEDOTS | MBC_WIRE_LOOPS_UVS)
void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph,
- MeshBatchCache *cache,
- MeshBufferCache *mbc,
- MeshBufferExtractionCache *extraction_cache,
+ MeshBatchCache *mbc,
+ MeshBufferCache *extraction_cache,
Mesh *me,
const bool is_editmode,
const bool is_paint_mode,
diff --git a/source/blender/draw/intern/draw_cache_extract_mesh.cc b/source/blender/draw/intern/draw_cache_extract_mesh.cc
index 1bc2b8a9def..06c449fe590 100644
--- a/source/blender/draw/intern/draw_cache_extract_mesh.cc
+++ b/source/blender/draw/intern/draw_cache_extract_mesh.cc
@@ -162,7 +162,7 @@ struct ExtractTaskData {
const MeshRenderData *mr = nullptr;
MeshBatchCache *cache = nullptr;
ExtractorRunDatas *extractors = nullptr;
- MeshBufferCache *mbc = nullptr;
+ MeshBufferList *mbuflist = nullptr;
eMRIterType iter_type;
bool use_threading = false;
@@ -170,9 +170,13 @@ struct ExtractTaskData {
ExtractTaskData(const MeshRenderData *mr,
struct MeshBatchCache *cache,
ExtractorRunDatas *extractors,
- MeshBufferCache *mbc,
+ MeshBufferList *mbuflist,
const bool use_threading)
- : mr(mr), cache(cache), extractors(extractors), mbc(mbc), use_threading(use_threading)
+ : mr(mr),
+ cache(cache),
+ extractors(extractors),
+ mbuflist(mbuflist),
+ use_threading(use_threading)
{
iter_type = extractors->iter_types();
};
@@ -204,13 +208,13 @@ static void extract_task_data_free(void *data)
BLI_INLINE void extract_init(const MeshRenderData *mr,
struct MeshBatchCache *cache,
ExtractorRunDatas &extractors,
- MeshBufferCache *mbc,
+ MeshBufferList *mbuflist,
void *data_stack)
{
uint32_t data_offset = 0;
for (ExtractorRunData &run_data : extractors) {
const MeshExtract *extractor = run_data.extractor;
- run_data.buffer = mesh_extract_buffer_get(extractor, mbc);
+ run_data.buffer = mesh_extract_buffer_get(extractor, mbuflist);
run_data.data_offset = data_offset;
extractor->init(mr, cache, run_data.buffer, POINTER_OFFSET(data_stack, data_offset));
data_offset += (uint32_t)extractor->data_size;
@@ -445,7 +449,7 @@ static void extract_task_range_run(void *__restrict taskdata)
settings.func_reduce = extract_task_reduce;
settings.min_iter_per_thread = MIN_RANGE_LEN;
- extract_init(data->mr, data->cache, *data->extractors, data->mbc, userdata_chunk);
+ extract_init(data->mr, data->cache, *data->extractors, data->mbuflist, userdata_chunk);
if (iter_type & MR_ITER_LOOPTRI) {
extract_task_range_run_iter(data->mr, data->extractors, MR_ITER_LOOPTRI, is_mesh, &settings);
@@ -474,10 +478,10 @@ static struct TaskNode *extract_task_node_create(struct TaskGraph *task_graph,
const MeshRenderData *mr,
MeshBatchCache *cache,
ExtractorRunDatas *extractors,
- MeshBufferCache *mbc,
+ MeshBufferList *mbuflist,
const bool use_threading)
{
- ExtractTaskData *taskdata = new ExtractTaskData(mr, cache, extractors, mbc, use_threading);
+ ExtractTaskData *taskdata = new ExtractTaskData(mr, cache, extractors, mbuflist, use_threading);
struct TaskNode *task_node = BLI_task_graph_node_create(
task_graph,
extract_task_range_run,
@@ -493,12 +497,12 @@ static struct TaskNode *extract_task_node_create(struct TaskGraph *task_graph,
* \{ */
struct MeshRenderDataUpdateTaskData {
MeshRenderData *mr = nullptr;
- MeshBufferExtractionCache *cache = nullptr;
+ MeshBufferCache *cache = nullptr;
eMRIterType iter_type;
eMRDataType data_flag;
MeshRenderDataUpdateTaskData(MeshRenderData *mr,
- MeshBufferExtractionCache *cache,
+ MeshBufferCache *cache,
eMRIterType iter_type,
eMRDataType data_flag)
: mr(mr), cache(cache), iter_type(iter_type), data_flag(data_flag)
@@ -538,7 +542,7 @@ static void mesh_extract_render_data_node_exec(void *__restrict task_data)
static struct TaskNode *mesh_extract_render_data_node_create(struct TaskGraph *task_graph,
MeshRenderData *mr,
- MeshBufferExtractionCache *cache,
+ MeshBufferCache *cache,
const eMRIterType iter_type,
const eMRDataType data_flag)
{
@@ -562,7 +566,6 @@ static struct TaskNode *mesh_extract_render_data_node_create(struct TaskGraph *t
static void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph,
MeshBatchCache *cache,
MeshBufferCache *mbc,
- MeshBufferExtractionCache *extraction_cache,
Mesh *me,
const bool is_editmode,
@@ -613,9 +616,11 @@ static void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph,
/* Create an array containing all the extractors that needs to be executed. */
ExtractorRunDatas extractors;
+ MeshBufferList *mbuflist = &mbc->buff;
+
#define EXTRACT_ADD_REQUESTED(type, name) \
do { \
- if (DRW_##type##_requested(mbc->type.name)) { \
+ if (DRW_##type##_requested(mbuflist->type.name)) { \
const MeshExtract *extractor = mesh_extract_override_get( \
&extract_##name, do_hq_normals, override_single_mat); \
extractors.append(extractor); \
@@ -647,19 +652,19 @@ static void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph,
EXTRACT_ADD_REQUESTED(vbo, skin_roots);
EXTRACT_ADD_REQUESTED(ibo, tris);
- if (DRW_ibo_requested(mbc->ibo.lines_loose)) {
+ if (DRW_ibo_requested(mbuflist->ibo.lines_loose)) {
/* `ibo.lines_loose` require the `ibo.lines` buffer. */
- if (mbc->ibo.lines == nullptr) {
- DRW_ibo_request(nullptr, &mbc->ibo.lines);
+ if (mbuflist->ibo.lines == nullptr) {
+ DRW_ibo_request(nullptr, &mbuflist->ibo.lines);
}
- const MeshExtract *extractor = DRW_ibo_requested(mbc->ibo.lines) ?
+ const MeshExtract *extractor = DRW_ibo_requested(mbuflist->ibo.lines) ?
&extract_lines_with_lines_loose :
&extract_lines_loose_only;
extractors.append(extractor);
}
- else if (DRW_ibo_requested(mbc->ibo.lines)) {
+ else if (DRW_ibo_requested(mbuflist->ibo.lines)) {
const MeshExtract *extractor;
- if (mbc->ibo.lines_loose != nullptr) {
+ if (mbuflist->ibo.lines_loose != nullptr) {
/* Update `ibo.lines_loose` as it depends on `ibo.lines`. */
extractor = &extract_lines_with_lines_loose;
}
@@ -701,7 +706,7 @@ static void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph,
eMRDataType data_flag = extractors.data_types();
struct TaskNode *task_node_mesh_render_data = mesh_extract_render_data_node_create(
- task_graph, mr, extraction_cache, iter_type, data_flag);
+ task_graph, mr, mbc, iter_type, data_flag);
/* Simple heuristic. */
const bool use_thread = (mr->loop_len + mr->loop_loose_len) > MIN_RANGE_LEN;
@@ -714,7 +719,7 @@ static void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph,
ExtractorRunDatas *single_threaded_extractors = new ExtractorRunDatas();
single_threaded_extractors->append(extractor);
struct TaskNode *task_node = extract_task_node_create(
- task_graph, mr, cache, single_threaded_extractors, mbc, false);
+ task_graph, mr, cache, single_threaded_extractors, mbuflist, false);
BLI_task_graph_edge_create(task_node_mesh_render_data, task_node);
}
@@ -725,7 +730,7 @@ static void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph,
extractors.filter_threaded_extractors_into(*multi_threaded_extractors);
if (!multi_threaded_extractors->is_empty()) {
struct TaskNode *task_node = extract_task_node_create(
- task_graph, mr, cache, multi_threaded_extractors, mbc, true);
+ task_graph, mr, cache, multi_threaded_extractors, mbuflist, true);
BLI_task_graph_edge_create(task_node_mesh_render_data, task_node);
}
@@ -738,7 +743,7 @@ static void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph,
/* Run all requests on the same thread. */
ExtractorRunDatas *extractors_copy = new ExtractorRunDatas(extractors);
struct TaskNode *task_node = extract_task_node_create(
- task_graph, mr, cache, extractors_copy, mbc, false);
+ task_graph, mr, cache, extractors_copy, mbuflist, false);
BLI_task_graph_edge_create(task_node_mesh_render_data, task_node);
}
@@ -776,7 +781,6 @@ extern "C" {
void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph,
MeshBatchCache *cache,
MeshBufferCache *mbc,
- MeshBufferExtractionCache *extraction_cache,
Mesh *me,
const bool is_editmode,
@@ -793,7 +797,6 @@ void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph,
blender::draw::mesh_buffer_cache_create_requested(task_graph,
cache,
mbc,
- extraction_cache,
me,
is_editmode,
is_paint_mode,
diff --git a/source/blender/draw/intern/draw_cache_extract_mesh_render_data.c b/source/blender/draw/intern/draw_cache_extract_mesh_render_data.c
index 27fd6ca9134..abfbeabef6b 100644
--- a/source/blender/draw/intern/draw_cache_extract_mesh_render_data.c
+++ b/source/blender/draw/intern/draw_cache_extract_mesh_render_data.c
@@ -45,17 +45,15 @@
* \{ */
static void mesh_render_data_lverts_bm(const MeshRenderData *mr,
- MeshBufferExtractionCache *cache,
+ MeshBufferCache *cache,
BMesh *bm);
static void mesh_render_data_ledges_bm(const MeshRenderData *mr,
- MeshBufferExtractionCache *cache,
+ MeshBufferCache *cache,
BMesh *bm);
-static void mesh_render_data_loose_geom_mesh(const MeshRenderData *mr,
- MeshBufferExtractionCache *cache);
-static void mesh_render_data_loose_geom_build(const MeshRenderData *mr,
- MeshBufferExtractionCache *cache);
+static void mesh_render_data_loose_geom_mesh(const MeshRenderData *mr, MeshBufferCache *cache);
+static void mesh_render_data_loose_geom_build(const MeshRenderData *mr, MeshBufferCache *cache);
-static void mesh_render_data_loose_geom_load(MeshRenderData *mr, MeshBufferExtractionCache *cache)
+static void mesh_render_data_loose_geom_load(MeshRenderData *mr, MeshBufferCache *cache)
{
mr->ledges = cache->loose_geom.edges;
mr->lverts = cache->loose_geom.verts;
@@ -65,8 +63,7 @@ static void mesh_render_data_loose_geom_load(MeshRenderData *mr, MeshBufferExtra
mr->loop_loose_len = mr->vert_loose_len + (mr->edge_loose_len * 2);
}
-static void mesh_render_data_loose_geom_ensure(const MeshRenderData *mr,
- MeshBufferExtractionCache *cache)
+static void mesh_render_data_loose_geom_ensure(const MeshRenderData *mr, MeshBufferCache *cache)
{
/* Early exit: Are loose geometry already available.
* Only checking for loose verts as loose edges and verts are calculated at the same time. */
@@ -76,8 +73,7 @@ static void mesh_render_data_loose_geom_ensure(const MeshRenderData *mr,
mesh_render_data_loose_geom_build(mr, cache);
}
-static void mesh_render_data_loose_geom_build(const MeshRenderData *mr,
- MeshBufferExtractionCache *cache)
+static void mesh_render_data_loose_geom_build(const MeshRenderData *mr, MeshBufferCache *cache)
{
cache->loose_geom.vert_len = 0;
cache->loose_geom.edge_len = 0;
@@ -94,8 +90,7 @@ static void mesh_render_data_loose_geom_build(const MeshRenderData *mr,
}
}
-static void mesh_render_data_loose_geom_mesh(const MeshRenderData *mr,
- MeshBufferExtractionCache *cache)
+static void mesh_render_data_loose_geom_mesh(const MeshRenderData *mr, MeshBufferCache *cache)
{
BLI_bitmap *lvert_map = BLI_BITMAP_NEW(mr->vert_len, __func__);
@@ -128,9 +123,7 @@ static void mesh_render_data_loose_geom_mesh(const MeshRenderData *mr,
MEM_freeN(lvert_map);
}
-static void mesh_render_data_lverts_bm(const MeshRenderData *mr,
- MeshBufferExtractionCache *cache,
- BMesh *bm)
+static void mesh_render_data_lverts_bm(const MeshRenderData *mr, MeshBufferCache *cache, BMesh *bm)
{
int elem_id;
BMIter iter;
@@ -147,9 +140,7 @@ static void mesh_render_data_lverts_bm(const MeshRenderData *mr,
}
}
-static void mesh_render_data_ledges_bm(const MeshRenderData *mr,
- MeshBufferExtractionCache *cache,
- BMesh *bm)
+static void mesh_render_data_ledges_bm(const MeshRenderData *mr, MeshBufferCache *cache, BMesh *bm)
{
int elem_id;
BMIter iter;
@@ -167,7 +158,7 @@ static void mesh_render_data_ledges_bm(const MeshRenderData *mr,
}
void mesh_render_data_update_loose_geom(MeshRenderData *mr,
- MeshBufferExtractionCache *cache,
+ MeshBufferCache *cache,
const eMRIterType iter_type,
const eMRDataType data_flag)
{
@@ -185,16 +176,13 @@ void mesh_render_data_update_loose_geom(MeshRenderData *mr,
* Contains polygon indices sorted based on their material.
*
* \{ */
-static void mesh_render_data_polys_sorted_load(MeshRenderData *mr,
- const MeshBufferExtractionCache *cache);
-static void mesh_render_data_polys_sorted_ensure(MeshRenderData *mr,
- MeshBufferExtractionCache *cache);
-static void mesh_render_data_polys_sorted_build(MeshRenderData *mr,
- MeshBufferExtractionCache *cache);
+static void mesh_render_data_polys_sorted_load(MeshRenderData *mr, const MeshBufferCache *cache);
+static void mesh_render_data_polys_sorted_ensure(MeshRenderData *mr, MeshBufferCache *cache);
+static void mesh_render_data_polys_sorted_build(MeshRenderData *mr, MeshBufferCache *cache);
static int *mesh_render_data_mat_tri_len_build(MeshRenderData *mr);
void mesh_render_data_update_polys_sorted(MeshRenderData *mr,
- MeshBufferExtractionCache *cache,
+ MeshBufferCache *cache,
const eMRDataType data_flag)
{
if (data_flag & MR_DATA_POLYS_SORTED) {
@@ -203,16 +191,14 @@ void mesh_render_data_update_polys_sorted(MeshRenderData *mr,
}
}
-static void mesh_render_data_polys_sorted_load(MeshRenderData *mr,
- const MeshBufferExtractionCache *cache)
+static void mesh_render_data_polys_sorted_load(MeshRenderData *mr, const MeshBufferCache *cache)
{
mr->poly_sorted.tri_first_index = cache->poly_sorted.tri_first_index;
mr->poly_sorted.mat_tri_len = cache->poly_sorted.mat_tri_len;
mr->poly_sorted.visible_tri_len = cache->poly_sorted.visible_tri_len;
}
-static void mesh_render_data_polys_sorted_ensure(MeshRenderData *mr,
- MeshBufferExtractionCache *cache)
+static void mesh_render_data_polys_sorted_ensure(MeshRenderData *mr, MeshBufferCache *cache)
{
if (cache->poly_sorted.tri_first_index) {
return;
@@ -220,8 +206,7 @@ static void mesh_render_data_polys_sorted_ensure(MeshRenderData *mr,
mesh_render_data_polys_sorted_build(mr, cache);
}
-static void mesh_render_data_polys_sorted_build(MeshRenderData *mr,
- MeshBufferExtractionCache *cache)
+static void mesh_render_data_polys_sorted_build(MeshRenderData *mr, MeshBufferCache *cache)
{
int *tri_first_index = MEM_mallocN(sizeof(*tri_first_index) * mr->poly_len, __func__);
int *mat_tri_len = mesh_render_data_mat_tri_len_build(mr);
@@ -584,7 +569,7 @@ void mesh_render_data_free(MeshRenderData *mr)
MEM_SAFE_FREE(mr->mlooptri);
MEM_SAFE_FREE(mr->loop_normals);
- /* Loose geometry are owned by #MeshBufferExtractionCache. */
+ /* Loose geometry are owned by #MeshBufferCache. */
mr->ledges = NULL;
mr->lverts = NULL;
diff --git a/source/blender/draw/intern/draw_cache_impl_mesh.c b/source/blender/draw/intern/draw_cache_impl_mesh.c
index 52b76733b78..18664498d00 100644
--- a/source/blender/draw/intern/draw_cache_impl_mesh.c
+++ b/source/blender/draw/intern/draw_cache_impl_mesh.c
@@ -79,39 +79,42 @@
/* clang-format off */
-#define _BUFFER_INDEX(buff_name) ((offsetof(MeshBufferCache, buff_name) - offsetof(MeshBufferCache, vbo)) / sizeof(void *))
-
-#define _MDEPS_CREATE1(b) (1u << MBC_BATCH_INDEX(b))
-#define _MDEPS_CREATE2(b1, b2) _MDEPS_CREATE1(b1) | _MDEPS_CREATE1(b2)
-#define _MDEPS_CREATE3(b1, b2, b3) _MDEPS_CREATE2(b1, b2) | _MDEPS_CREATE1(b3)
-#define _MDEPS_CREATE4(b1, b2, b3, b4) _MDEPS_CREATE3(b1, b2, b3) | _MDEPS_CREATE1(b4)
-#define _MDEPS_CREATE5(b1, b2, b3, b4, b5) _MDEPS_CREATE4(b1, b2, b3, b4) | _MDEPS_CREATE1(b5)
-#define _MDEPS_CREATE6(b1, b2, b3, b4, b5, b6) _MDEPS_CREATE5(b1, b2, b3, b4, b5) | _MDEPS_CREATE1(b6)
-#define _MDEPS_CREATE7(b1, b2, b3, b4, b5, b6, b7) _MDEPS_CREATE6(b1, b2, b3, b4, b5, b6) | _MDEPS_CREATE1(b7)
-#define _MDEPS_CREATE8(b1, b2, b3, b4, b5, b6, b7, b8) _MDEPS_CREATE7(b1, b2, b3, b4, b5, b6, b7) | _MDEPS_CREATE1(b8)
-#define _MDEPS_CREATE9(b1, b2, b3, b4, b5, b6, b7, b8, b9) _MDEPS_CREATE8(b1, b2, b3, b4, b5, b6, b7, b8) | _MDEPS_CREATE1(b9)
-#define _MDEPS_CREATE10(b1, b2, b3, b4, b5, b6, b7, b8, b9, b10) _MDEPS_CREATE9(b1, b2, b3, b4, b5, b6, b7, b8, b9) | _MDEPS_CREATE1(b10)
-#define _MDEPS_CREATE19(b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17, b18, b19) _MDEPS_CREATE10(b1, b2, b3, b4, b5, b6, b7, b8, b9, b10) | _MDEPS_CREATE9(b11, b12, b13, b14, b15, b16, b17, b18, b19)
-
-#define MDEPS_CREATE(buff_name, ...) [_BUFFER_INDEX(buff_name)] = VA_NARGS_CALL_OVERLOAD(_MDEPS_CREATE, __VA_ARGS__)
-
-#define _MDEPS_CREATE_MAP1(a) g_buffer_deps[_BUFFER_INDEX(a)]
-#define _MDEPS_CREATE_MAP2(a, b) _MDEPS_CREATE_MAP1(a) | _MDEPS_CREATE_MAP1(b)
-#define _MDEPS_CREATE_MAP3(a, b, c) _MDEPS_CREATE_MAP2(a, b) | _MDEPS_CREATE_MAP1(c)
-#define _MDEPS_CREATE_MAP4(a, b, c, d) _MDEPS_CREATE_MAP3(a, b, c) | _MDEPS_CREATE_MAP1(d)
-#define _MDEPS_CREATE_MAP5(a, b, c, d, e) _MDEPS_CREATE_MAP4(a, b, c, d) | _MDEPS_CREATE_MAP1(e)
-#define _MDEPS_CREATE_MAP6(a, b, c, d, e, f) _MDEPS_CREATE_MAP5(a, b, c, d, e) | _MDEPS_CREATE_MAP1(f)
-#define _MDEPS_CREATE_MAP7(a, b, c, d, e, f, g) _MDEPS_CREATE_MAP6(a, b, c, d, e, f) | _MDEPS_CREATE_MAP1(g)
-#define _MDEPS_CREATE_MAP8(a, b, c, d, e, f, g, h) _MDEPS_CREATE_MAP7(a, b, c, d, e, f, g) | _MDEPS_CREATE_MAP1(h)
-#define _MDEPS_CREATE_MAP9(a, b, c, d, e, f, g, h, i) _MDEPS_CREATE_MAP8(a, b, c, d, e, f, g, h) | _MDEPS_CREATE_MAP1(i)
-#define _MDEPS_CREATE_MAP10(a, b, c, d, e, f, g, h, i, j) _MDEPS_CREATE_MAP9(a, b, c, d, e, f, g, h, i) | _MDEPS_CREATE_MAP1(j)
-
-#define MDEPS_CREATE_MAP(...) VA_NARGS_CALL_OVERLOAD(_MDEPS_CREATE_MAP, __VA_ARGS__)
+#define BUFFER_INDEX(buff_name) ((offsetof(MeshBufferList, buff_name) - offsetof(MeshBufferList, vbo)) / sizeof(void *))
+#define BUFFER_LEN (sizeof(MeshBufferList) / sizeof(void *))
+
+#define _BATCH_FLAG1(b) (1u << MBC_BATCH_INDEX(b))
+#define _BATCH_FLAG2(b1, b2) _BATCH_FLAG1(b1) | _BATCH_FLAG1(b2)
+#define _BATCH_FLAG3(b1, b2, b3) _BATCH_FLAG2(b1, b2) | _BATCH_FLAG1(b3)
+#define _BATCH_FLAG4(b1, b2, b3, b4) _BATCH_FLAG3(b1, b2, b3) | _BATCH_FLAG1(b4)
+#define _BATCH_FLAG5(b1, b2, b3, b4, b5) _BATCH_FLAG4(b1, b2, b3, b4) | _BATCH_FLAG1(b5)
+#define _BATCH_FLAG6(b1, b2, b3, b4, b5, b6) _BATCH_FLAG5(b1, b2, b3, b4, b5) | _BATCH_FLAG1(b6)
+#define _BATCH_FLAG7(b1, b2, b3, b4, b5, b6, b7) _BATCH_FLAG6(b1, b2, b3, b4, b5, b6) | _BATCH_FLAG1(b7)
+#define _BATCH_FLAG8(b1, b2, b3, b4, b5, b6, b7, b8) _BATCH_FLAG7(b1, b2, b3, b4, b5, b6, b7) | _BATCH_FLAG1(b8)
+#define _BATCH_FLAG9(b1, b2, b3, b4, b5, b6, b7, b8, b9) _BATCH_FLAG8(b1, b2, b3, b4, b5, b6, b7, b8) | _BATCH_FLAG1(b9)
+#define _BATCH_FLAG10(b1, b2, b3, b4, b5, b6, b7, b8, b9, b10) _BATCH_FLAG9(b1, b2, b3, b4, b5, b6, b7, b8, b9) | _BATCH_FLAG1(b10)
+#define _BATCH_FLAG18(b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17, b18) _BATCH_FLAG10(b1, b2, b3, b4, b5, b6, b7, b8, b9, b10) | _BATCH_FLAG8(b11, b12, b13, b14, b15, b16, b17, b18)
+
+#define BATCH_FLAG(...) VA_NARGS_CALL_OVERLOAD(_BATCH_FLAG, __VA_ARGS__)
+
+#define _BATCH_MAP1(a) g_buffer_deps[BUFFER_INDEX(a)]
+#define _BATCH_MAP2(a, b) _BATCH_MAP1(a) | _BATCH_MAP1(b)
+#define _BATCH_MAP3(a, b, c) _BATCH_MAP2(a, b) | _BATCH_MAP1(c)
+#define _BATCH_MAP4(a, b, c, d) _BATCH_MAP3(a, b, c) | _BATCH_MAP1(d)
+#define _BATCH_MAP5(a, b, c, d, e) _BATCH_MAP4(a, b, c, d) | _BATCH_MAP1(e)
+#define _BATCH_MAP6(a, b, c, d, e, f) _BATCH_MAP5(a, b, c, d, e) | _BATCH_MAP1(f)
+#define _BATCH_MAP7(a, b, c, d, e, f, g) _BATCH_MAP6(a, b, c, d, e, f) | _BATCH_MAP1(g)
+#define _BATCH_MAP8(a, b, c, d, e, f, g, h) _BATCH_MAP7(a, b, c, d, e, f, g) | _BATCH_MAP1(h)
+#define _BATCH_MAP9(a, b, c, d, e, f, g, h, i) _BATCH_MAP8(a, b, c, d, e, f, g, h) | _BATCH_MAP1(i)
+#define _BATCH_MAP10(a, b, c, d, e, f, g, h, i, j) _BATCH_MAP9(a, b, c, d, e, f, g, h, i) | _BATCH_MAP1(j)
+
+#define BATCH_MAP(...) VA_NARGS_CALL_OVERLOAD(_BATCH_MAP, __VA_ARGS__)
#ifndef NDEBUG
-# define _MDEPS_ASSERT2(b, name) \
- g_buffer_deps_d[_BUFFER_INDEX(name)] |= _MDEPS_CREATE1(b); \
- BLI_assert(g_buffer_deps[_BUFFER_INDEX(name)] & _MDEPS_CREATE1(b))
+# define MDEPS_ASSERT_INDEX(buffer_index, batch_flag) \
+ g_buffer_deps_d[buffer_index] |= batch_flag; \
+ BLI_assert(g_buffer_deps[buffer_index] & batch_flag)
+
+# define _MDEPS_ASSERT2(b, n1) MDEPS_ASSERT_INDEX(BUFFER_INDEX(n1), b)
# define _MDEPS_ASSERT3(b, n1, n2) _MDEPS_ASSERT2(b, n1); _MDEPS_ASSERT2(b, n2)
# define _MDEPS_ASSERT4(b, n1, n2, n3) _MDEPS_ASSERT3(b, n1, n2); _MDEPS_ASSERT2(b, n3)
# define _MDEPS_ASSERT5(b, n1, n2, n3, n4) _MDEPS_ASSERT4(b, n1, n2, n3); _MDEPS_ASSERT2(b, n4)
@@ -119,99 +122,97 @@
# define _MDEPS_ASSERT7(b, n1, n2, n3, n4, n5, n6) _MDEPS_ASSERT6(b, n1, n2, n3, n4, n5); _MDEPS_ASSERT2(b, n6)
# define _MDEPS_ASSERT8(b, n1, n2, n3, n4, n5, n6, n7) _MDEPS_ASSERT7(b, n1, n2, n3, n4, n5, n6); _MDEPS_ASSERT2(b, n7)
-# define MDEPS_ASSERT(...) VA_NARGS_CALL_OVERLOAD(_MDEPS_ASSERT, __VA_ARGS__)
-# define MDEPS_ASSERT_MAP(name) BLI_assert(g_buffer_deps_d[_BUFFER_INDEX(name)] == g_buffer_deps[_BUFFER_INDEX(name)])
+# define MDEPS_ASSERT_FLAG(...) VA_NARGS_CALL_OVERLOAD(_MDEPS_ASSERT, __VA_ARGS__)
+# define MDEPS_ASSERT(batch_name, ...) MDEPS_ASSERT_FLAG(BATCH_FLAG(batch_name), __VA_ARGS__)
+# define MDEPS_ASSERT_MAP_INDEX(buff_index) BLI_assert(g_buffer_deps_d[buff_index] == g_buffer_deps[buff_index])
+# define MDEPS_ASSERT_MAP(buff_name) MDEPS_ASSERT_MAP_INDEX(BUFFER_INDEX(buff_name))
#else
-# define MDEPS_ASSERT(...)
-# define MDEPS_ASSERT_MAP(name)
+# define MDEPS_ASSERT_INDEX(buffer_index, batch_flag)
+# define MDEPS_ASSERT_FLAG(...)
+# define MDEPS_ASSERT(batch_name, ...)
+# define MDEPS_ASSERT_MAP_INDEX(buff_index)
+# define MDEPS_ASSERT_MAP(buff_name)
#endif
/* clang-format on */
+#define TRIS_PER_MAT_INDEX BUFFER_LEN
+#define SURFACE_PER_MAT_FLAG (1u << MBC_BATCH_LEN)
+
static const DRWBatchFlag g_buffer_deps[] = {
- MDEPS_CREATE(vbo.pos_nor,
- batch.surface,
- batch.surface_weights,
- batch.edit_triangles,
- batch.edit_vertices,
- batch.edit_edges,
- batch.edit_vnor,
- batch.edit_lnor,
- batch.edit_mesh_analysis,
- batch.edit_selection_verts,
- batch.edit_selection_edges,
- batch.edit_selection_faces,
- batch.all_verts,
- batch.all_edges,
- batch.loose_edges,
- batch.edge_detection,
- batch.wire_edges,
- batch.wire_loops,
- batch.sculpt_overlays,
- surface_per_mat),
- MDEPS_CREATE(vbo.lnor, batch.surface, batch.edit_lnor, batch.wire_loops, surface_per_mat),
- MDEPS_CREATE(vbo.edge_fac, batch.wire_edges),
- MDEPS_CREATE(vbo.weights, batch.surface_weights),
- MDEPS_CREATE(vbo.uv,
- batch.surface,
- batch.edituv_faces_stretch_area,
- batch.edituv_faces_stretch_angle,
- batch.edituv_faces,
- batch.edituv_edges,
- batch.edituv_verts,
- batch.wire_loops_uvs,
- surface_per_mat),
- MDEPS_CREATE(vbo.tan, surface_per_mat),
- MDEPS_CREATE(vbo.vcol, batch.surface, surface_per_mat),
- MDEPS_CREATE(vbo.sculpt_data, batch.sculpt_overlays),
- MDEPS_CREATE(vbo.orco, surface_per_mat),
- MDEPS_CREATE(vbo.edit_data, batch.edit_triangles, batch.edit_edges, batch.edit_vertices),
- MDEPS_CREATE(vbo.edituv_data,
- batch.edituv_faces,
- batch.edituv_faces_stretch_area,
- batch.edituv_faces_stretch_angle,
- batch.edituv_edges,
- batch.edituv_verts),
- MDEPS_CREATE(vbo.edituv_stretch_area, batch.edituv_faces_stretch_area),
- MDEPS_CREATE(vbo.edituv_stretch_angle, batch.edituv_faces_stretch_angle),
- MDEPS_CREATE(vbo.mesh_analysis, batch.edit_mesh_analysis),
- MDEPS_CREATE(vbo.fdots_pos, batch.edit_fdots, batch.edit_selection_fdots),
- MDEPS_CREATE(vbo.fdots_nor, batch.edit_fdots),
- MDEPS_CREATE(vbo.fdots_uv, batch.edituv_fdots),
- MDEPS_CREATE(vbo.fdots_edituv_data, batch.edituv_fdots),
- MDEPS_CREATE(vbo.skin_roots, batch.edit_skin_roots),
- MDEPS_CREATE(vbo.vert_idx, batch.edit_selection_verts),
- MDEPS_CREATE(vbo.edge_idx, batch.edit_selection_edges),
- MDEPS_CREATE(vbo.poly_idx, batch.edit_selection_faces),
- MDEPS_CREATE(vbo.fdot_idx, batch.edit_selection_fdots),
-
- MDEPS_CREATE(ibo.tris,
- batch.surface,
- batch.surface_weights,
- batch.edit_triangles,
- batch.edit_lnor,
- batch.edit_mesh_analysis,
- batch.edit_selection_faces,
- batch.sculpt_overlays),
- MDEPS_CREATE(ibo.lines,
- batch.edit_edges,
- batch.edit_selection_edges,
- batch.all_edges,
- batch.wire_edges),
- MDEPS_CREATE(ibo.lines_loose, batch.loose_edges),
- MDEPS_CREATE(ibo.points, batch.edit_vnor, batch.edit_vertices, batch.edit_selection_verts),
- MDEPS_CREATE(ibo.fdots, batch.edit_fdots, batch.edit_selection_fdots),
- MDEPS_CREATE(ibo.lines_paint_mask, batch.wire_loops),
- MDEPS_CREATE(ibo.lines_adjacency, batch.edge_detection),
- MDEPS_CREATE(ibo.edituv_tris,
- batch.edituv_faces,
- batch.edituv_faces_stretch_area,
- batch.edituv_faces_stretch_angle),
- MDEPS_CREATE(ibo.edituv_lines, batch.edituv_edges, batch.wire_loops_uvs),
- MDEPS_CREATE(ibo.edituv_points, batch.edituv_verts),
- MDEPS_CREATE(ibo.edituv_fdots, batch.edituv_fdots),
-
- MDEPS_CREATE(tris_per_mat, surface_per_mat),
+ [BUFFER_INDEX(vbo.pos_nor)] = BATCH_FLAG(surface,
+ surface_weights,
+ edit_triangles,
+ edit_vertices,
+ edit_edges,
+ edit_vnor,
+ edit_lnor,
+ edit_mesh_analysis,
+ edit_selection_verts,
+ edit_selection_edges,
+ edit_selection_faces,
+ all_verts,
+ all_edges,
+ loose_edges,
+ edge_detection,
+ wire_edges,
+ wire_loops,
+ sculpt_overlays) |
+ SURFACE_PER_MAT_FLAG,
+ [BUFFER_INDEX(vbo.lnor)] = BATCH_FLAG(surface, edit_lnor, wire_loops) | SURFACE_PER_MAT_FLAG,
+ [BUFFER_INDEX(vbo.edge_fac)] = BATCH_FLAG(wire_edges),
+ [BUFFER_INDEX(vbo.weights)] = BATCH_FLAG(surface_weights),
+ [BUFFER_INDEX(vbo.uv)] = BATCH_FLAG(surface,
+ edituv_faces_stretch_area,
+ edituv_faces_stretch_angle,
+ edituv_faces,
+ edituv_edges,
+ edituv_verts,
+ wire_loops_uvs) |
+ SURFACE_PER_MAT_FLAG,
+ [BUFFER_INDEX(vbo.tan)] = SURFACE_PER_MAT_FLAG,
+ [BUFFER_INDEX(vbo.vcol)] = BATCH_FLAG(surface) | SURFACE_PER_MAT_FLAG,
+ [BUFFER_INDEX(vbo.sculpt_data)] = BATCH_FLAG(sculpt_overlays),
+ [BUFFER_INDEX(vbo.orco)] = SURFACE_PER_MAT_FLAG,
+ [BUFFER_INDEX(vbo.edit_data)] = BATCH_FLAG(edit_triangles, edit_edges, edit_vertices),
+ [BUFFER_INDEX(vbo.edituv_data)] = BATCH_FLAG(edituv_faces,
+ edituv_faces_stretch_area,
+ edituv_faces_stretch_angle,
+ edituv_edges,
+ edituv_verts),
+ [BUFFER_INDEX(vbo.edituv_stretch_area)] = BATCH_FLAG(edituv_faces_stretch_area),
+ [BUFFER_INDEX(vbo.edituv_stretch_angle)] = BATCH_FLAG(edituv_faces_stretch_angle),
+ [BUFFER_INDEX(vbo.mesh_analysis)] = BATCH_FLAG(edit_mesh_analysis),
+ [BUFFER_INDEX(vbo.fdots_pos)] = BATCH_FLAG(edit_fdots, edit_selection_fdots),
+ [BUFFER_INDEX(vbo.fdots_nor)] = BATCH_FLAG(edit_fdots),
+ [BUFFER_INDEX(vbo.fdots_uv)] = BATCH_FLAG(edituv_fdots),
+ [BUFFER_INDEX(vbo.fdots_edituv_data)] = BATCH_FLAG(edituv_fdots),
+ [BUFFER_INDEX(vbo.skin_roots)] = BATCH_FLAG(edit_skin_roots),
+ [BUFFER_INDEX(vbo.vert_idx)] = BATCH_FLAG(edit_selection_verts),
+ [BUFFER_INDEX(vbo.edge_idx)] = BATCH_FLAG(edit_selection_edges),
+ [BUFFER_INDEX(vbo.poly_idx)] = BATCH_FLAG(edit_selection_faces),
+ [BUFFER_INDEX(vbo.fdot_idx)] = BATCH_FLAG(edit_selection_fdots),
+
+ [BUFFER_INDEX(ibo.tris)] = BATCH_FLAG(surface,
+ surface_weights,
+ edit_triangles,
+ edit_lnor,
+ edit_mesh_analysis,
+ edit_selection_faces,
+ sculpt_overlays),
+ [BUFFER_INDEX(ibo.lines)] = BATCH_FLAG(
+ edit_edges, edit_selection_edges, all_edges, wire_edges),
+ [BUFFER_INDEX(ibo.lines_loose)] = BATCH_FLAG(loose_edges),
+ [BUFFER_INDEX(ibo.points)] = BATCH_FLAG(edit_vnor, edit_vertices, edit_selection_verts),
+ [BUFFER_INDEX(ibo.fdots)] = BATCH_FLAG(edit_fdots, edit_selection_fdots),
+ [BUFFER_INDEX(ibo.lines_paint_mask)] = BATCH_FLAG(wire_loops),
+ [BUFFER_INDEX(ibo.lines_adjacency)] = BATCH_FLAG(edge_detection),
+ [BUFFER_INDEX(ibo.edituv_tris)] = BATCH_FLAG(
+ edituv_faces, edituv_faces_stretch_area, edituv_faces_stretch_angle),
+ [BUFFER_INDEX(ibo.edituv_lines)] = BATCH_FLAG(edituv_edges, wire_loops_uvs),
+ [BUFFER_INDEX(ibo.edituv_points)] = BATCH_FLAG(edituv_verts),
+ [BUFFER_INDEX(ibo.edituv_fdots)] = BATCH_FLAG(edituv_fdots),
+ [TRIS_PER_MAT_INDEX] = SURFACE_PER_MAT_FLAG,
};
#ifndef NDEBUG
@@ -231,7 +232,7 @@ static void mesh_batch_cache_discard_batch(MeshBatchCache *cache, const DRWBatch
}
}
- if (batch_map & (1u << MBC_BATCH_INDEX(surface_per_mat))) {
+ if (batch_map & SURFACE_PER_MAT_FLAG) {
mesh_batch_cache_discard_surface_batches(cache);
}
}
@@ -661,8 +662,7 @@ static void mesh_batch_cache_init(Mesh *me)
cache->mat_len = mesh_render_mat_len_get(me);
cache->surface_per_mat = MEM_callocN(sizeof(*cache->surface_per_mat) * cache->mat_len, __func__);
- cache->final.tris_per_mat = MEM_callocN(sizeof(*cache->final.tris_per_mat) * cache->mat_len,
- __func__);
+ cache->tris_per_mat = MEM_callocN(sizeof(*cache->tris_per_mat) * cache->mat_len, __func__);
cache->is_dirty = false;
cache->batch_ready = 0;
@@ -688,8 +688,8 @@ static void mesh_batch_cache_check_vertex_group(MeshBatchCache *cache,
const struct DRW_MeshWeightState *wstate)
{
if (!drw_mesh_weight_state_compare(&cache->weight_state, wstate)) {
- FOREACH_MESH_BUFFER_CACHE (cache, mbufcache) {
- GPU_VERTBUF_DISCARD_SAFE(mbufcache->vbo.weights);
+ FOREACH_MESH_BUFFER_CACHE (cache, mbc) {
+ GPU_VERTBUF_DISCARD_SAFE(mbc->buff.vbo.weights);
}
GPU_BATCH_CLEAR_SAFE(cache->batch.surface_weights);
@@ -708,6 +708,9 @@ static void mesh_batch_cache_request_surface_batches(MeshBatchCache *cache)
}
}
+/* Free batches with material-mapped looptris.
+ * NOTE: The updating of the indices buffers (#tris_per_mat) is handled in the extractors.
+ * No need to discard they here. */
static void mesh_batch_cache_discard_surface_batches(MeshBatchCache *cache)
{
GPU_BATCH_DISCARD_SAFE(cache->batch.surface);
@@ -719,41 +722,41 @@ static void mesh_batch_cache_discard_surface_batches(MeshBatchCache *cache)
static void mesh_batch_cache_discard_shaded_tri(MeshBatchCache *cache)
{
- FOREACH_MESH_BUFFER_CACHE (cache, mbufcache) {
- GPU_VERTBUF_DISCARD_SAFE(mbufcache->vbo.uv);
- GPU_VERTBUF_DISCARD_SAFE(mbufcache->vbo.tan);
- GPU_VERTBUF_DISCARD_SAFE(mbufcache->vbo.vcol);
- GPU_VERTBUF_DISCARD_SAFE(mbufcache->vbo.orco);
+ FOREACH_MESH_BUFFER_CACHE (cache, mbc) {
+ GPU_VERTBUF_DISCARD_SAFE(mbc->buff.vbo.uv);
+ GPU_VERTBUF_DISCARD_SAFE(mbc->buff.vbo.tan);
+ GPU_VERTBUF_DISCARD_SAFE(mbc->buff.vbo.vcol);
+ GPU_VERTBUF_DISCARD_SAFE(mbc->buff.vbo.orco);
}
- DRWBatchFlag batch_map = MDEPS_CREATE_MAP(vbo.uv, vbo.tan, vbo.vcol, vbo.orco);
+ DRWBatchFlag batch_map = BATCH_MAP(vbo.uv, vbo.tan, vbo.vcol, vbo.orco);
mesh_batch_cache_discard_batch(cache, batch_map);
mesh_cd_layers_type_clear(&cache->cd_used);
}
static void mesh_batch_cache_discard_uvedit(MeshBatchCache *cache)
{
- FOREACH_MESH_BUFFER_CACHE (cache, mbufcache) {
- GPU_VERTBUF_DISCARD_SAFE(mbufcache->vbo.edituv_stretch_angle);
- GPU_VERTBUF_DISCARD_SAFE(mbufcache->vbo.edituv_stretch_area);
- GPU_VERTBUF_DISCARD_SAFE(mbufcache->vbo.uv);
- GPU_VERTBUF_DISCARD_SAFE(mbufcache->vbo.edituv_data);
- GPU_VERTBUF_DISCARD_SAFE(mbufcache->vbo.fdots_uv);
- GPU_VERTBUF_DISCARD_SAFE(mbufcache->vbo.fdots_edituv_data);
- GPU_INDEXBUF_DISCARD_SAFE(mbufcache->ibo.edituv_tris);
- GPU_INDEXBUF_DISCARD_SAFE(mbufcache->ibo.edituv_lines);
- GPU_INDEXBUF_DISCARD_SAFE(mbufcache->ibo.edituv_points);
- GPU_INDEXBUF_DISCARD_SAFE(mbufcache->ibo.edituv_fdots);
- }
- DRWBatchFlag batch_map = MDEPS_CREATE_MAP(vbo.edituv_stretch_angle,
- vbo.edituv_stretch_area,
- vbo.uv,
- vbo.edituv_data,
- vbo.fdots_uv,
- vbo.fdots_edituv_data,
- ibo.edituv_tris,
- ibo.edituv_lines,
- ibo.edituv_points,
- ibo.edituv_fdots);
+ FOREACH_MESH_BUFFER_CACHE (cache, mbc) {
+ GPU_VERTBUF_DISCARD_SAFE(mbc->buff.vbo.edituv_stretch_angle);
+ GPU_VERTBUF_DISCARD_SAFE(mbc->buff.vbo.edituv_stretch_area);
+ GPU_VERTBUF_DISCARD_SAFE(mbc->buff.vbo.uv);
+ GPU_VERTBUF_DISCARD_SAFE(mbc->buff.vbo.edituv_data);
+ GPU_VERTBUF_DISCARD_SAFE(mbc->buff.vbo.fdots_uv);
+ GPU_VERTBUF_DISCARD_SAFE(mbc->buff.vbo.fdots_edituv_data);
+ GPU_INDEXBUF_DISCARD_SAFE(mbc->buff.ibo.edituv_tris);
+ GPU_INDEXBUF_DISCARD_SAFE(mbc->buff.ibo.edituv_lines);
+ GPU_INDEXBUF_DISCARD_SAFE(mbc->buff.ibo.edituv_points);
+ GPU_INDEXBUF_DISCARD_SAFE(mbc->buff.ibo.edituv_fdots);
+ }
+ DRWBatchFlag batch_map = BATCH_MAP(vbo.edituv_stretch_angle,
+ vbo.edituv_stretch_area,
+ vbo.uv,
+ vbo.edituv_data,
+ vbo.fdots_uv,
+ vbo.fdots_edituv_data,
+ ibo.edituv_tris,
+ ibo.edituv_lines,
+ ibo.edituv_points,
+ ibo.edituv_fdots);
mesh_batch_cache_discard_batch(cache, batch_map);
cache->tot_area = 0.0f;
@@ -768,20 +771,20 @@ static void mesh_batch_cache_discard_uvedit(MeshBatchCache *cache)
static void mesh_batch_cache_discard_uvedit_select(MeshBatchCache *cache)
{
- FOREACH_MESH_BUFFER_CACHE (cache, mbufcache) {
- GPU_VERTBUF_DISCARD_SAFE(mbufcache->vbo.edituv_data);
- GPU_VERTBUF_DISCARD_SAFE(mbufcache->vbo.fdots_edituv_data);
- GPU_INDEXBUF_DISCARD_SAFE(mbufcache->ibo.edituv_tris);
- GPU_INDEXBUF_DISCARD_SAFE(mbufcache->ibo.edituv_lines);
- GPU_INDEXBUF_DISCARD_SAFE(mbufcache->ibo.edituv_points);
- GPU_INDEXBUF_DISCARD_SAFE(mbufcache->ibo.edituv_fdots);
- }
- DRWBatchFlag batch_map = MDEPS_CREATE_MAP(vbo.edituv_data,
- vbo.fdots_edituv_data,
- ibo.edituv_tris,
- ibo.edituv_lines,
- ibo.edituv_points,
- ibo.edituv_fdots);
+ FOREACH_MESH_BUFFER_CACHE (cache, mbc) {
+ GPU_VERTBUF_DISCARD_SAFE(mbc->buff.vbo.edituv_data);
+ GPU_VERTBUF_DISCARD_SAFE(mbc->buff.vbo.fdots_edituv_data);
+ GPU_INDEXBUF_DISCARD_SAFE(mbc->buff.ibo.edituv_tris);
+ GPU_INDEXBUF_DISCARD_SAFE(mbc->buff.ibo.edituv_lines);
+ GPU_INDEXBUF_DISCARD_SAFE(mbc->buff.ibo.edituv_points);
+ GPU_INDEXBUF_DISCARD_SAFE(mbc->buff.ibo.edituv_fdots);
+ }
+ DRWBatchFlag batch_map = BATCH_MAP(vbo.edituv_data,
+ vbo.fdots_edituv_data,
+ ibo.edituv_tris,
+ ibo.edituv_lines,
+ ibo.edituv_points,
+ ibo.edituv_fdots);
mesh_batch_cache_discard_batch(cache, batch_map);
}
@@ -794,11 +797,11 @@ void DRW_mesh_batch_cache_dirty_tag(Mesh *me, eMeshBatchDirtyMode mode)
DRWBatchFlag batch_map;
switch (mode) {
case BKE_MESH_BATCH_DIRTY_SELECT:
- FOREACH_MESH_BUFFER_CACHE (cache, mbufcache) {
- GPU_VERTBUF_DISCARD_SAFE(mbufcache->vbo.edit_data);
- GPU_VERTBUF_DISCARD_SAFE(mbufcache->vbo.fdots_nor);
+ FOREACH_MESH_BUFFER_CACHE (cache, mbc) {
+ GPU_VERTBUF_DISCARD_SAFE(mbc->buff.vbo.edit_data);
+ GPU_VERTBUF_DISCARD_SAFE(mbc->buff.vbo.fdots_nor);
}
- batch_map = MDEPS_CREATE_MAP(vbo.edit_data, vbo.fdots_nor);
+ batch_map = BATCH_MAP(vbo.edit_data, vbo.fdots_nor);
mesh_batch_cache_discard_batch(cache, batch_map);
/* Because visible UVs depends on edit mode selection, discard topology. */
@@ -807,12 +810,12 @@ void DRW_mesh_batch_cache_dirty_tag(Mesh *me, eMeshBatchDirtyMode mode)
case BKE_MESH_BATCH_DIRTY_SELECT_PAINT:
/* Paint mode selection flag is packed inside the nor attribute.
* Note that it can be slow if auto smooth is enabled. (see T63946) */
- FOREACH_MESH_BUFFER_CACHE (cache, mbufcache) {
- GPU_INDEXBUF_DISCARD_SAFE(mbufcache->ibo.lines_paint_mask);
- GPU_VERTBUF_DISCARD_SAFE(mbufcache->vbo.pos_nor);
- GPU_VERTBUF_DISCARD_SAFE(mbufcache->vbo.lnor);
+ FOREACH_MESH_BUFFER_CACHE (cache, mbc) {
+ GPU_INDEXBUF_DISCARD_SAFE(mbc->buff.ibo.lines_paint_mask);
+ GPU_VERTBUF_DISCARD_SAFE(mbc->buff.vbo.pos_nor);
+ GPU_VERTBUF_DISCARD_SAFE(mbc->buff.vbo.lnor);
}
- batch_map = MDEPS_CREATE_MAP(ibo.lines_paint_mask, vbo.pos_nor, vbo.lnor);
+ batch_map = BATCH_MAP(ibo.lines_paint_mask, vbo.pos_nor, vbo.lnor);
mesh_batch_cache_discard_batch(cache, batch_map);
break;
case BKE_MESH_BATCH_DIRTY_ALL:
@@ -826,11 +829,11 @@ void DRW_mesh_batch_cache_dirty_tag(Mesh *me, eMeshBatchDirtyMode mode)
mesh_batch_cache_discard_uvedit(cache);
break;
case BKE_MESH_BATCH_DIRTY_UVEDIT_SELECT:
- FOREACH_MESH_BUFFER_CACHE (cache, mbufcache) {
- GPU_VERTBUF_DISCARD_SAFE(mbufcache->vbo.edituv_data);
- GPU_VERTBUF_DISCARD_SAFE(mbufcache->vbo.fdots_edituv_data);
+ FOREACH_MESH_BUFFER_CACHE (cache, mbc) {
+ GPU_VERTBUF_DISCARD_SAFE(mbc->buff.vbo.edituv_data);
+ GPU_VERTBUF_DISCARD_SAFE(mbc->buff.vbo.fdots_edituv_data);
}
- batch_map = MDEPS_CREATE_MAP(vbo.edituv_data, vbo.fdots_edituv_data);
+ batch_map = BATCH_MAP(vbo.edituv_data, vbo.fdots_edituv_data);
mesh_batch_cache_discard_batch(cache, batch_map);
break;
default:
@@ -838,28 +841,30 @@ void DRW_mesh_batch_cache_dirty_tag(Mesh *me, eMeshBatchDirtyMode mode)
}
}
-static void mesh_buffer_cache_clear(MeshBufferCache *mbufcache)
+static void mesh_buffer_list_clear(MeshBufferList *mbuflist)
{
- GPUVertBuf **vbos = (GPUVertBuf **)&mbufcache->vbo;
- GPUIndexBuf **ibos = (GPUIndexBuf **)&mbufcache->ibo;
- for (int i = 0; i < sizeof(mbufcache->vbo) / sizeof(void *); i++) {
+ GPUVertBuf **vbos = (GPUVertBuf **)&mbuflist->vbo;
+ GPUIndexBuf **ibos = (GPUIndexBuf **)&mbuflist->ibo;
+ for (int i = 0; i < sizeof(mbuflist->vbo) / sizeof(void *); i++) {
GPU_VERTBUF_DISCARD_SAFE(vbos[i]);
}
- for (int i = 0; i < sizeof(mbufcache->ibo) / sizeof(void *); i++) {
+ for (int i = 0; i < sizeof(mbuflist->ibo) / sizeof(void *); i++) {
GPU_INDEXBUF_DISCARD_SAFE(ibos[i]);
}
}
-static void mesh_buffer_extraction_cache_clear(MeshBufferExtractionCache *extraction_cache)
+static void mesh_buffer_cache_clear(MeshBufferCache *mbc)
{
- MEM_SAFE_FREE(extraction_cache->loose_geom.verts);
- MEM_SAFE_FREE(extraction_cache->loose_geom.edges);
- extraction_cache->loose_geom.edge_len = 0;
- extraction_cache->loose_geom.vert_len = 0;
+ mesh_buffer_list_clear(&mbc->buff);
- MEM_SAFE_FREE(extraction_cache->poly_sorted.tri_first_index);
- MEM_SAFE_FREE(extraction_cache->poly_sorted.mat_tri_len);
- extraction_cache->poly_sorted.visible_tri_len = 0;
+ MEM_SAFE_FREE(mbc->loose_geom.verts);
+ MEM_SAFE_FREE(mbc->loose_geom.edges);
+ mbc->loose_geom.edge_len = 0;
+ mbc->loose_geom.vert_len = 0;
+
+ MEM_SAFE_FREE(mbc->poly_sorted.tri_first_index);
+ MEM_SAFE_FREE(mbc->poly_sorted.mat_tri_len);
+ mbc->poly_sorted.visible_tri_len = 0;
}
static void mesh_batch_cache_clear(Mesh *me)
@@ -868,18 +873,14 @@ static void mesh_batch_cache_clear(Mesh *me)
if (!cache) {
return;
}
- FOREACH_MESH_BUFFER_CACHE (cache, mbufcache) {
- mesh_buffer_cache_clear(mbufcache);
+ FOREACH_MESH_BUFFER_CACHE (cache, mbc) {
+ mesh_buffer_cache_clear(mbc);
}
- mesh_buffer_extraction_cache_clear(&cache->final_extraction_cache);
- mesh_buffer_extraction_cache_clear(&cache->cage_extraction_cache);
- mesh_buffer_extraction_cache_clear(&cache->uv_cage_extraction_cache);
-
for (int i = 0; i < cache->mat_len; i++) {
- GPU_INDEXBUF_DISCARD_SAFE(cache->final.tris_per_mat[i]);
+ GPU_INDEXBUF_DISCARD_SAFE(cache->tris_per_mat[i]);
}
- MEM_SAFE_FREE(cache->final.tris_per_mat);
+ MEM_SAFE_FREE(cache->tris_per_mat);
for (int i = 0; i < sizeof(cache->batch) / sizeof(void *); i++) {
GPUBatch **batch = (GPUBatch **)&cache->batch;
@@ -1083,8 +1084,8 @@ GPUVertBuf *DRW_mesh_batch_cache_pos_vertbuf_get(Mesh *me)
/* Request surface to trigger the vbo filling. Otherwise it may do nothing. */
mesh_batch_cache_request_surface_batches(cache);
- DRW_vbo_request(NULL, &cache->final.vbo.pos_nor);
- return cache->final.vbo.pos_nor;
+ DRW_vbo_request(NULL, &cache->final.buff.vbo.pos_nor);
+ return cache->final.buff.vbo.pos_nor;
}
/** \} */
@@ -1316,22 +1317,22 @@ static void drw_mesh_batch_cache_check_available(struct TaskGraph *task_graph, M
BLI_assert(!DRW_batch_requested(((GPUBatch **)&cache->batch)[i], 0));
}
for (int i = 0; i < MBC_VBO_LEN; i++) {
- BLI_assert(!DRW_vbo_requested(((GPUVertBuf **)&cache->final.vbo)[i]));
+ BLI_assert(!DRW_vbo_requested(((GPUVertBuf **)&cache->final.buff.vbo)[i]));
}
for (int i = 0; i < MBC_IBO_LEN; i++) {
- BLI_assert(!DRW_ibo_requested(((GPUIndexBuf **)&cache->final.ibo)[i]));
+ BLI_assert(!DRW_ibo_requested(((GPUIndexBuf **)&cache->final.buff.ibo)[i]));
}
for (int i = 0; i < MBC_VBO_LEN; i++) {
- BLI_assert(!DRW_vbo_requested(((GPUVertBuf **)&cache->cage.vbo)[i]));
+ BLI_assert(!DRW_vbo_requested(((GPUVertBuf **)&cache->cage.buff.vbo)[i]));
}
for (int i = 0; i < MBC_IBO_LEN; i++) {
- BLI_assert(!DRW_ibo_requested(((GPUIndexBuf **)&cache->cage.ibo)[i]));
+ BLI_assert(!DRW_ibo_requested(((GPUIndexBuf **)&cache->cage.buff.ibo)[i]));
}
for (int i = 0; i < MBC_VBO_LEN; i++) {
- BLI_assert(!DRW_vbo_requested(((GPUVertBuf **)&cache->uv_cage.vbo)[i]));
+ BLI_assert(!DRW_vbo_requested(((GPUVertBuf **)&cache->uv_cage.buff.vbo)[i]));
}
for (int i = 0; i < MBC_IBO_LEN; i++) {
- BLI_assert(!DRW_ibo_requested(((GPUIndexBuf **)&cache->uv_cage.ibo)[i]));
+ BLI_assert(!DRW_ibo_requested(((GPUIndexBuf **)&cache->uv_cage.buff.ibo)[i]));
}
}
#endif
@@ -1414,25 +1415,25 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph,
* material. */
bool cd_overlap = mesh_cd_layers_type_overlap(cache->cd_used, cache->cd_needed);
if (cd_overlap == false) {
- FOREACH_MESH_BUFFER_CACHE (cache, mbuffercache) {
+ FOREACH_MESH_BUFFER_CACHE (cache, mbc) {
if ((cache->cd_used.uv & cache->cd_needed.uv) != cache->cd_needed.uv) {
- GPU_VERTBUF_DISCARD_SAFE(mbuffercache->vbo.uv);
+ GPU_VERTBUF_DISCARD_SAFE(mbc->buff.vbo.uv);
cd_uv_update = true;
}
if ((cache->cd_used.tan & cache->cd_needed.tan) != cache->cd_needed.tan ||
cache->cd_used.tan_orco != cache->cd_needed.tan_orco) {
- GPU_VERTBUF_DISCARD_SAFE(mbuffercache->vbo.tan);
+ GPU_VERTBUF_DISCARD_SAFE(mbc->buff.vbo.tan);
}
if (cache->cd_used.orco != cache->cd_needed.orco) {
- GPU_VERTBUF_DISCARD_SAFE(mbuffercache->vbo.orco);
+ GPU_VERTBUF_DISCARD_SAFE(mbc->buff.vbo.orco);
}
if (cache->cd_used.sculpt_overlays != cache->cd_needed.sculpt_overlays) {
- GPU_VERTBUF_DISCARD_SAFE(mbuffercache->vbo.sculpt_data);
+ GPU_VERTBUF_DISCARD_SAFE(mbc->buff.vbo.sculpt_data);
}
if (((cache->cd_used.vcol & cache->cd_needed.vcol) != cache->cd_needed.vcol) ||
((cache->cd_used.sculpt_vcol & cache->cd_needed.sculpt_vcol) !=
cache->cd_needed.sculpt_vcol)) {
- GPU_VERTBUF_DISCARD_SAFE(mbuffercache->vbo.vcol);
+ GPU_VERTBUF_DISCARD_SAFE(mbc->buff.vbo.vcol);
}
}
/* We can't discard batches at this point as they have been
@@ -1454,14 +1455,14 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph,
const bool is_uvsyncsel = ts && (ts->uv_flag & UV_SYNC_SELECTION);
if (cd_uv_update || (cache->is_uvsyncsel != is_uvsyncsel)) {
cache->is_uvsyncsel = is_uvsyncsel;
- FOREACH_MESH_BUFFER_CACHE (cache, mbuffercache) {
- GPU_VERTBUF_DISCARD_SAFE(mbuffercache->vbo.edituv_data);
- GPU_VERTBUF_DISCARD_SAFE(mbuffercache->vbo.fdots_uv);
- GPU_VERTBUF_DISCARD_SAFE(mbuffercache->vbo.fdots_edituv_data);
- GPU_INDEXBUF_DISCARD_SAFE(mbuffercache->ibo.edituv_tris);
- GPU_INDEXBUF_DISCARD_SAFE(mbuffercache->ibo.edituv_lines);
- GPU_INDEXBUF_DISCARD_SAFE(mbuffercache->ibo.edituv_points);
- GPU_INDEXBUF_DISCARD_SAFE(mbuffercache->ibo.edituv_fdots);
+ FOREACH_MESH_BUFFER_CACHE (cache, mbc) {
+ GPU_VERTBUF_DISCARD_SAFE(mbc->buff.vbo.edituv_data);
+ GPU_VERTBUF_DISCARD_SAFE(mbc->buff.vbo.fdots_uv);
+ GPU_VERTBUF_DISCARD_SAFE(mbc->buff.vbo.fdots_edituv_data);
+ GPU_INDEXBUF_DISCARD_SAFE(mbc->buff.ibo.edituv_tris);
+ GPU_INDEXBUF_DISCARD_SAFE(mbc->buff.ibo.edituv_lines);
+ GPU_INDEXBUF_DISCARD_SAFE(mbc->buff.ibo.edituv_points);
+ GPU_INDEXBUF_DISCARD_SAFE(mbc->buff.ibo.edituv_fdots);
}
/* We only clear the batches as they may already have been
* referenced. */
@@ -1502,173 +1503,174 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph,
const bool do_uvcage = is_editmode && !me->edit_mesh->mesh_eval_final->runtime.is_original;
- MeshBufferCache *mbufcache = &cache->final;
+ MeshBufferList *mbuflist = &cache->final.buff;
/* Initialize batches and request VBO's & IBO's. */
- MDEPS_ASSERT(batch.surface, ibo.tris, vbo.lnor, vbo.pos_nor, vbo.uv, vbo.vcol);
+ MDEPS_ASSERT(surface, ibo.tris, vbo.lnor, vbo.pos_nor, vbo.uv, vbo.vcol);
if (DRW_batch_requested(cache->batch.surface, GPU_PRIM_TRIS)) {
- DRW_ibo_request(cache->batch.surface, &mbufcache->ibo.tris);
+ DRW_ibo_request(cache->batch.surface, &mbuflist->ibo.tris);
/* Order matters. First ones override latest VBO's attributes. */
- DRW_vbo_request(cache->batch.surface, &mbufcache->vbo.lnor);
- DRW_vbo_request(cache->batch.surface, &mbufcache->vbo.pos_nor);
+ DRW_vbo_request(cache->batch.surface, &mbuflist->vbo.lnor);
+ DRW_vbo_request(cache->batch.surface, &mbuflist->vbo.pos_nor);
if (cache->cd_used.uv != 0) {
- DRW_vbo_request(cache->batch.surface, &mbufcache->vbo.uv);
+ DRW_vbo_request(cache->batch.surface, &mbuflist->vbo.uv);
}
if (cache->cd_used.vcol != 0 || cache->cd_used.sculpt_vcol != 0) {
- DRW_vbo_request(cache->batch.surface, &mbufcache->vbo.vcol);
+ DRW_vbo_request(cache->batch.surface, &mbuflist->vbo.vcol);
}
}
- MDEPS_ASSERT(batch.all_verts, vbo.pos_nor);
+ MDEPS_ASSERT(all_verts, vbo.pos_nor);
if (DRW_batch_requested(cache->batch.all_verts, GPU_PRIM_POINTS)) {
- DRW_vbo_request(cache->batch.all_verts, &mbufcache->vbo.pos_nor);
+ DRW_vbo_request(cache->batch.all_verts, &mbuflist->vbo.pos_nor);
}
- MDEPS_ASSERT(batch.sculpt_overlays, ibo.tris, vbo.pos_nor, vbo.sculpt_data);
+ MDEPS_ASSERT(sculpt_overlays, ibo.tris, vbo.pos_nor, vbo.sculpt_data);
if (DRW_batch_requested(cache->batch.sculpt_overlays, GPU_PRIM_TRIS)) {
- DRW_ibo_request(cache->batch.sculpt_overlays, &mbufcache->ibo.tris);
- DRW_vbo_request(cache->batch.sculpt_overlays, &mbufcache->vbo.pos_nor);
- DRW_vbo_request(cache->batch.sculpt_overlays, &mbufcache->vbo.sculpt_data);
+ DRW_ibo_request(cache->batch.sculpt_overlays, &mbuflist->ibo.tris);
+ DRW_vbo_request(cache->batch.sculpt_overlays, &mbuflist->vbo.pos_nor);
+ DRW_vbo_request(cache->batch.sculpt_overlays, &mbuflist->vbo.sculpt_data);
}
- MDEPS_ASSERT(batch.all_edges, ibo.lines, vbo.pos_nor);
+ MDEPS_ASSERT(all_edges, ibo.lines, vbo.pos_nor);
if (DRW_batch_requested(cache->batch.all_edges, GPU_PRIM_LINES)) {
- DRW_ibo_request(cache->batch.all_edges, &mbufcache->ibo.lines);
- DRW_vbo_request(cache->batch.all_edges, &mbufcache->vbo.pos_nor);
+ DRW_ibo_request(cache->batch.all_edges, &mbuflist->ibo.lines);
+ DRW_vbo_request(cache->batch.all_edges, &mbuflist->vbo.pos_nor);
}
- MDEPS_ASSERT(batch.loose_edges, ibo.lines_loose, vbo.pos_nor);
+ MDEPS_ASSERT(loose_edges, ibo.lines_loose, vbo.pos_nor);
if (DRW_batch_requested(cache->batch.loose_edges, GPU_PRIM_LINES)) {
- DRW_ibo_request(NULL, &mbufcache->ibo.lines);
- DRW_ibo_request(cache->batch.loose_edges, &mbufcache->ibo.lines_loose);
- DRW_vbo_request(cache->batch.loose_edges, &mbufcache->vbo.pos_nor);
+ DRW_ibo_request(NULL, &mbuflist->ibo.lines);
+ DRW_ibo_request(cache->batch.loose_edges, &mbuflist->ibo.lines_loose);
+ DRW_vbo_request(cache->batch.loose_edges, &mbuflist->vbo.pos_nor);
}
- MDEPS_ASSERT(batch.edge_detection, ibo.lines_adjacency, vbo.pos_nor);
+ MDEPS_ASSERT(edge_detection, ibo.lines_adjacency, vbo.pos_nor);
if (DRW_batch_requested(cache->batch.edge_detection, GPU_PRIM_LINES_ADJ)) {
- DRW_ibo_request(cache->batch.edge_detection, &mbufcache->ibo.lines_adjacency);
- DRW_vbo_request(cache->batch.edge_detection, &mbufcache->vbo.pos_nor);
+ DRW_ibo_request(cache->batch.edge_detection, &mbuflist->ibo.lines_adjacency);
+ DRW_vbo_request(cache->batch.edge_detection, &mbuflist->vbo.pos_nor);
}
- MDEPS_ASSERT(batch.surface_weights, ibo.tris, vbo.pos_nor, vbo.weights);
+ MDEPS_ASSERT(surface_weights, ibo.tris, vbo.pos_nor, vbo.weights);
if (DRW_batch_requested(cache->batch.surface_weights, GPU_PRIM_TRIS)) {
- DRW_ibo_request(cache->batch.surface_weights, &mbufcache->ibo.tris);
- DRW_vbo_request(cache->batch.surface_weights, &mbufcache->vbo.pos_nor);
- DRW_vbo_request(cache->batch.surface_weights, &mbufcache->vbo.weights);
+ DRW_ibo_request(cache->batch.surface_weights, &mbuflist->ibo.tris);
+ DRW_vbo_request(cache->batch.surface_weights, &mbuflist->vbo.pos_nor);
+ DRW_vbo_request(cache->batch.surface_weights, &mbuflist->vbo.weights);
}
- MDEPS_ASSERT(batch.wire_loops, ibo.lines_paint_mask, vbo.lnor, vbo.pos_nor);
+ MDEPS_ASSERT(wire_loops, ibo.lines_paint_mask, vbo.lnor, vbo.pos_nor);
if (DRW_batch_requested(cache->batch.wire_loops, GPU_PRIM_LINES)) {
- DRW_ibo_request(cache->batch.wire_loops, &mbufcache->ibo.lines_paint_mask);
+ DRW_ibo_request(cache->batch.wire_loops, &mbuflist->ibo.lines_paint_mask);
/* Order matters. First ones override latest VBO's attributes. */
- DRW_vbo_request(cache->batch.wire_loops, &mbufcache->vbo.lnor);
- DRW_vbo_request(cache->batch.wire_loops, &mbufcache->vbo.pos_nor);
+ DRW_vbo_request(cache->batch.wire_loops, &mbuflist->vbo.lnor);
+ DRW_vbo_request(cache->batch.wire_loops, &mbuflist->vbo.pos_nor);
}
- MDEPS_ASSERT(batch.wire_edges, ibo.lines, vbo.pos_nor, vbo.edge_fac);
+ MDEPS_ASSERT(wire_edges, ibo.lines, vbo.pos_nor, vbo.edge_fac);
if (DRW_batch_requested(cache->batch.wire_edges, GPU_PRIM_LINES)) {
- DRW_ibo_request(cache->batch.wire_edges, &mbufcache->ibo.lines);
- DRW_vbo_request(cache->batch.wire_edges, &mbufcache->vbo.pos_nor);
- DRW_vbo_request(cache->batch.wire_edges, &mbufcache->vbo.edge_fac);
+ DRW_ibo_request(cache->batch.wire_edges, &mbuflist->ibo.lines);
+ DRW_vbo_request(cache->batch.wire_edges, &mbuflist->vbo.pos_nor);
+ DRW_vbo_request(cache->batch.wire_edges, &mbuflist->vbo.edge_fac);
}
- MDEPS_ASSERT(batch.wire_loops_uvs, ibo.edituv_lines, vbo.uv);
+ MDEPS_ASSERT(wire_loops_uvs, ibo.edituv_lines, vbo.uv);
if (DRW_batch_requested(cache->batch.wire_loops_uvs, GPU_PRIM_LINES)) {
- DRW_ibo_request(cache->batch.wire_loops_uvs, &mbufcache->ibo.edituv_lines);
+ DRW_ibo_request(cache->batch.wire_loops_uvs, &mbuflist->ibo.edituv_lines);
/* For paint overlay. Active layer should have been queried. */
if (cache->cd_used.uv != 0) {
- DRW_vbo_request(cache->batch.wire_loops_uvs, &mbufcache->vbo.uv);
+ DRW_vbo_request(cache->batch.wire_loops_uvs, &mbuflist->vbo.uv);
}
}
- MDEPS_ASSERT(batch.edit_mesh_analysis, ibo.tris, vbo.pos_nor, vbo.mesh_analysis);
+ MDEPS_ASSERT(edit_mesh_analysis, ibo.tris, vbo.pos_nor, vbo.mesh_analysis);
if (DRW_batch_requested(cache->batch.edit_mesh_analysis, GPU_PRIM_TRIS)) {
- DRW_ibo_request(cache->batch.edit_mesh_analysis, &mbufcache->ibo.tris);
- DRW_vbo_request(cache->batch.edit_mesh_analysis, &mbufcache->vbo.pos_nor);
- DRW_vbo_request(cache->batch.edit_mesh_analysis, &mbufcache->vbo.mesh_analysis);
+ DRW_ibo_request(cache->batch.edit_mesh_analysis, &mbuflist->ibo.tris);
+ DRW_vbo_request(cache->batch.edit_mesh_analysis, &mbuflist->vbo.pos_nor);
+ DRW_vbo_request(cache->batch.edit_mesh_analysis, &mbuflist->vbo.mesh_analysis);
}
/* Per Material */
- MDEPS_ASSERT(
- surface_per_mat, tris_per_mat, vbo.lnor, vbo.pos_nor, vbo.uv, vbo.tan, vbo.vcol, vbo.orco);
+ MDEPS_ASSERT_FLAG(
+ SURFACE_PER_MAT_FLAG, vbo.lnor, vbo.pos_nor, vbo.uv, vbo.tan, vbo.vcol, vbo.orco);
+ MDEPS_ASSERT_INDEX(TRIS_PER_MAT_INDEX, SURFACE_PER_MAT_FLAG);
for (int i = 0; i < cache->mat_len; i++) {
if (DRW_batch_requested(cache->surface_per_mat[i], GPU_PRIM_TRIS)) {
- DRW_ibo_request(cache->surface_per_mat[i], &mbufcache->tris_per_mat[i]);
+ DRW_ibo_request(cache->surface_per_mat[i], &cache->tris_per_mat[i]);
/* Order matters. First ones override latest VBO's attributes. */
- DRW_vbo_request(cache->surface_per_mat[i], &mbufcache->vbo.lnor);
- DRW_vbo_request(cache->surface_per_mat[i], &mbufcache->vbo.pos_nor);
+ DRW_vbo_request(cache->surface_per_mat[i], &mbuflist->vbo.lnor);
+ DRW_vbo_request(cache->surface_per_mat[i], &mbuflist->vbo.pos_nor);
if (cache->cd_used.uv != 0) {
- DRW_vbo_request(cache->surface_per_mat[i], &mbufcache->vbo.uv);
+ DRW_vbo_request(cache->surface_per_mat[i], &mbuflist->vbo.uv);
}
if ((cache->cd_used.tan != 0) || (cache->cd_used.tan_orco != 0)) {
- DRW_vbo_request(cache->surface_per_mat[i], &mbufcache->vbo.tan);
+ DRW_vbo_request(cache->surface_per_mat[i], &mbuflist->vbo.tan);
}
if (cache->cd_used.vcol != 0 || cache->cd_used.sculpt_vcol != 0) {
- DRW_vbo_request(cache->surface_per_mat[i], &mbufcache->vbo.vcol);
+ DRW_vbo_request(cache->surface_per_mat[i], &mbuflist->vbo.vcol);
}
if (cache->cd_used.orco != 0) {
- DRW_vbo_request(cache->surface_per_mat[i], &mbufcache->vbo.orco);
+ DRW_vbo_request(cache->surface_per_mat[i], &mbuflist->vbo.orco);
}
}
}
- mbufcache = (do_cage) ? &cache->cage : &cache->final;
+ mbuflist = (do_cage) ? &cache->cage.buff : &cache->final.buff;
/* Edit Mesh */
- MDEPS_ASSERT(batch.edit_triangles, ibo.tris, vbo.pos_nor, vbo.edit_data);
+ MDEPS_ASSERT(edit_triangles, ibo.tris, vbo.pos_nor, vbo.edit_data);
if (DRW_batch_requested(cache->batch.edit_triangles, GPU_PRIM_TRIS)) {
- DRW_ibo_request(cache->batch.edit_triangles, &mbufcache->ibo.tris);
- DRW_vbo_request(cache->batch.edit_triangles, &mbufcache->vbo.pos_nor);
- DRW_vbo_request(cache->batch.edit_triangles, &mbufcache->vbo.edit_data);
+ DRW_ibo_request(cache->batch.edit_triangles, &mbuflist->ibo.tris);
+ DRW_vbo_request(cache->batch.edit_triangles, &mbuflist->vbo.pos_nor);
+ DRW_vbo_request(cache->batch.edit_triangles, &mbuflist->vbo.edit_data);
}
- MDEPS_ASSERT(batch.edit_vertices, ibo.points, vbo.pos_nor, vbo.edit_data);
+ MDEPS_ASSERT(edit_vertices, ibo.points, vbo.pos_nor, vbo.edit_data);
if (DRW_batch_requested(cache->batch.edit_vertices, GPU_PRIM_POINTS)) {
- DRW_ibo_request(cache->batch.edit_vertices, &mbufcache->ibo.points);
- DRW_vbo_request(cache->batch.edit_vertices, &mbufcache->vbo.pos_nor);
- DRW_vbo_request(cache->batch.edit_vertices, &mbufcache->vbo.edit_data);
+ DRW_ibo_request(cache->batch.edit_vertices, &mbuflist->ibo.points);
+ DRW_vbo_request(cache->batch.edit_vertices, &mbuflist->vbo.pos_nor);
+ DRW_vbo_request(cache->batch.edit_vertices, &mbuflist->vbo.edit_data);
}
- MDEPS_ASSERT(batch.edit_edges, ibo.lines, vbo.pos_nor, vbo.edit_data);
+ MDEPS_ASSERT(edit_edges, ibo.lines, vbo.pos_nor, vbo.edit_data);
if (DRW_batch_requested(cache->batch.edit_edges, GPU_PRIM_LINES)) {
- DRW_ibo_request(cache->batch.edit_edges, &mbufcache->ibo.lines);
- DRW_vbo_request(cache->batch.edit_edges, &mbufcache->vbo.pos_nor);
- DRW_vbo_request(cache->batch.edit_edges, &mbufcache->vbo.edit_data);
+ DRW_ibo_request(cache->batch.edit_edges, &mbuflist->ibo.lines);
+ DRW_vbo_request(cache->batch.edit_edges, &mbuflist->vbo.pos_nor);
+ DRW_vbo_request(cache->batch.edit_edges, &mbuflist->vbo.edit_data);
}
- MDEPS_ASSERT(batch.edit_vnor, ibo.points, vbo.pos_nor);
+ MDEPS_ASSERT(edit_vnor, ibo.points, vbo.pos_nor);
if (DRW_batch_requested(cache->batch.edit_vnor, GPU_PRIM_POINTS)) {
- DRW_ibo_request(cache->batch.edit_vnor, &mbufcache->ibo.points);
- DRW_vbo_request(cache->batch.edit_vnor, &mbufcache->vbo.pos_nor);
+ DRW_ibo_request(cache->batch.edit_vnor, &mbuflist->ibo.points);
+ DRW_vbo_request(cache->batch.edit_vnor, &mbuflist->vbo.pos_nor);
}
- MDEPS_ASSERT(batch.edit_lnor, ibo.tris, vbo.pos_nor, vbo.lnor);
+ MDEPS_ASSERT(edit_lnor, ibo.tris, vbo.pos_nor, vbo.lnor);
if (DRW_batch_requested(cache->batch.edit_lnor, GPU_PRIM_POINTS)) {
- DRW_ibo_request(cache->batch.edit_lnor, &mbufcache->ibo.tris);
- DRW_vbo_request(cache->batch.edit_lnor, &mbufcache->vbo.pos_nor);
- DRW_vbo_request(cache->batch.edit_lnor, &mbufcache->vbo.lnor);
+ DRW_ibo_request(cache->batch.edit_lnor, &mbuflist->ibo.tris);
+ DRW_vbo_request(cache->batch.edit_lnor, &mbuflist->vbo.pos_nor);
+ DRW_vbo_request(cache->batch.edit_lnor, &mbuflist->vbo.lnor);
}
- MDEPS_ASSERT(batch.edit_fdots, ibo.fdots, vbo.fdots_pos, vbo.fdots_nor);
+ MDEPS_ASSERT(edit_fdots, ibo.fdots, vbo.fdots_pos, vbo.fdots_nor);
if (DRW_batch_requested(cache->batch.edit_fdots, GPU_PRIM_POINTS)) {
- DRW_ibo_request(cache->batch.edit_fdots, &mbufcache->ibo.fdots);
- DRW_vbo_request(cache->batch.edit_fdots, &mbufcache->vbo.fdots_pos);
- DRW_vbo_request(cache->batch.edit_fdots, &mbufcache->vbo.fdots_nor);
+ DRW_ibo_request(cache->batch.edit_fdots, &mbuflist->ibo.fdots);
+ DRW_vbo_request(cache->batch.edit_fdots, &mbuflist->vbo.fdots_pos);
+ DRW_vbo_request(cache->batch.edit_fdots, &mbuflist->vbo.fdots_nor);
}
- MDEPS_ASSERT(batch.edit_skin_roots, vbo.skin_roots);
+ MDEPS_ASSERT(edit_skin_roots, vbo.skin_roots);
if (DRW_batch_requested(cache->batch.edit_skin_roots, GPU_PRIM_POINTS)) {
- DRW_vbo_request(cache->batch.edit_skin_roots, &mbufcache->vbo.skin_roots);
+ DRW_vbo_request(cache->batch.edit_skin_roots, &mbuflist->vbo.skin_roots);
}
/* Selection */
- MDEPS_ASSERT(batch.edit_selection_verts, ibo.points, vbo.pos_nor, vbo.vert_idx);
+ MDEPS_ASSERT(edit_selection_verts, ibo.points, vbo.pos_nor, vbo.vert_idx);
if (DRW_batch_requested(cache->batch.edit_selection_verts, GPU_PRIM_POINTS)) {
- DRW_ibo_request(cache->batch.edit_selection_verts, &mbufcache->ibo.points);
- DRW_vbo_request(cache->batch.edit_selection_verts, &mbufcache->vbo.pos_nor);
- DRW_vbo_request(cache->batch.edit_selection_verts, &mbufcache->vbo.vert_idx);
+ DRW_ibo_request(cache->batch.edit_selection_verts, &mbuflist->ibo.points);
+ DRW_vbo_request(cache->batch.edit_selection_verts, &mbuflist->vbo.pos_nor);
+ DRW_vbo_request(cache->batch.edit_selection_verts, &mbuflist->vbo.vert_idx);
}
- MDEPS_ASSERT(batch.edit_selection_edges, ibo.lines, vbo.pos_nor, vbo.edge_idx);
+ MDEPS_ASSERT(edit_selection_edges, ibo.lines, vbo.pos_nor, vbo.edge_idx);
if (DRW_batch_requested(cache->batch.edit_selection_edges, GPU_PRIM_LINES)) {
- DRW_ibo_request(cache->batch.edit_selection_edges, &mbufcache->ibo.lines);
- DRW_vbo_request(cache->batch.edit_selection_edges, &mbufcache->vbo.pos_nor);
- DRW_vbo_request(cache->batch.edit_selection_edges, &mbufcache->vbo.edge_idx);
+ DRW_ibo_request(cache->batch.edit_selection_edges, &mbuflist->ibo.lines);
+ DRW_vbo_request(cache->batch.edit_selection_edges, &mbuflist->vbo.pos_nor);
+ DRW_vbo_request(cache->batch.edit_selection_edges, &mbuflist->vbo.edge_idx);
}
- MDEPS_ASSERT(batch.edit_selection_faces, ibo.tris, vbo.pos_nor, vbo.poly_idx);
+ MDEPS_ASSERT(edit_selection_faces, ibo.tris, vbo.pos_nor, vbo.poly_idx);
if (DRW_batch_requested(cache->batch.edit_selection_faces, GPU_PRIM_TRIS)) {
- DRW_ibo_request(cache->batch.edit_selection_faces, &mbufcache->ibo.tris);
- DRW_vbo_request(cache->batch.edit_selection_faces, &mbufcache->vbo.pos_nor);
- DRW_vbo_request(cache->batch.edit_selection_faces, &mbufcache->vbo.poly_idx);
+ DRW_ibo_request(cache->batch.edit_selection_faces, &mbuflist->ibo.tris);
+ DRW_vbo_request(cache->batch.edit_selection_faces, &mbuflist->vbo.pos_nor);
+ DRW_vbo_request(cache->batch.edit_selection_faces, &mbuflist->vbo.poly_idx);
}
- MDEPS_ASSERT(batch.edit_selection_fdots, ibo.fdots, vbo.fdots_pos, vbo.fdot_idx);
+ MDEPS_ASSERT(edit_selection_fdots, ibo.fdots, vbo.fdots_pos, vbo.fdot_idx);
if (DRW_batch_requested(cache->batch.edit_selection_fdots, GPU_PRIM_POINTS)) {
- DRW_ibo_request(cache->batch.edit_selection_fdots, &mbufcache->ibo.fdots);
- DRW_vbo_request(cache->batch.edit_selection_fdots, &mbufcache->vbo.fdots_pos);
- DRW_vbo_request(cache->batch.edit_selection_fdots, &mbufcache->vbo.fdot_idx);
+ DRW_ibo_request(cache->batch.edit_selection_fdots, &mbuflist->ibo.fdots);
+ DRW_vbo_request(cache->batch.edit_selection_fdots, &mbuflist->vbo.fdots_pos);
+ DRW_vbo_request(cache->batch.edit_selection_fdots, &mbuflist->vbo.fdot_idx);
}
/**
@@ -1676,54 +1678,54 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph,
* but the selection code for UVs needs to support it first. So for now, only
* display the cage in all cases.
*/
- mbufcache = (do_uvcage) ? &cache->uv_cage : &cache->final;
+ mbuflist = (do_uvcage) ? &cache->uv_cage.buff : &cache->final.buff;
/* Edit UV */
- MDEPS_ASSERT(batch.edituv_faces, ibo.edituv_tris, vbo.uv, vbo.edituv_data);
+ MDEPS_ASSERT(edituv_faces, ibo.edituv_tris, vbo.uv, vbo.edituv_data);
if (DRW_batch_requested(cache->batch.edituv_faces, GPU_PRIM_TRIS)) {
- DRW_ibo_request(cache->batch.edituv_faces, &mbufcache->ibo.edituv_tris);
- DRW_vbo_request(cache->batch.edituv_faces, &mbufcache->vbo.uv);
- DRW_vbo_request(cache->batch.edituv_faces, &mbufcache->vbo.edituv_data);
+ DRW_ibo_request(cache->batch.edituv_faces, &mbuflist->ibo.edituv_tris);
+ DRW_vbo_request(cache->batch.edituv_faces, &mbuflist->vbo.uv);
+ DRW_vbo_request(cache->batch.edituv_faces, &mbuflist->vbo.edituv_data);
}
- MDEPS_ASSERT(batch.edituv_faces_stretch_area,
+ MDEPS_ASSERT(edituv_faces_stretch_area,
ibo.edituv_tris,
vbo.uv,
vbo.edituv_data,
vbo.edituv_stretch_area);
if (DRW_batch_requested(cache->batch.edituv_faces_stretch_area, GPU_PRIM_TRIS)) {
- DRW_ibo_request(cache->batch.edituv_faces_stretch_area, &mbufcache->ibo.edituv_tris);
- DRW_vbo_request(cache->batch.edituv_faces_stretch_area, &mbufcache->vbo.uv);
- DRW_vbo_request(cache->batch.edituv_faces_stretch_area, &mbufcache->vbo.edituv_data);
- DRW_vbo_request(cache->batch.edituv_faces_stretch_area, &mbufcache->vbo.edituv_stretch_area);
+ DRW_ibo_request(cache->batch.edituv_faces_stretch_area, &mbuflist->ibo.edituv_tris);
+ DRW_vbo_request(cache->batch.edituv_faces_stretch_area, &mbuflist->vbo.uv);
+ DRW_vbo_request(cache->batch.edituv_faces_stretch_area, &mbuflist->vbo.edituv_data);
+ DRW_vbo_request(cache->batch.edituv_faces_stretch_area, &mbuflist->vbo.edituv_stretch_area);
}
- MDEPS_ASSERT(batch.edituv_faces_stretch_angle,
+ MDEPS_ASSERT(edituv_faces_stretch_angle,
ibo.edituv_tris,
vbo.uv,
vbo.edituv_data,
vbo.edituv_stretch_angle);
if (DRW_batch_requested(cache->batch.edituv_faces_stretch_angle, GPU_PRIM_TRIS)) {
- DRW_ibo_request(cache->batch.edituv_faces_stretch_angle, &mbufcache->ibo.edituv_tris);
- DRW_vbo_request(cache->batch.edituv_faces_stretch_angle, &mbufcache->vbo.uv);
- DRW_vbo_request(cache->batch.edituv_faces_stretch_angle, &mbufcache->vbo.edituv_data);
- DRW_vbo_request(cache->batch.edituv_faces_stretch_angle, &mbufcache->vbo.edituv_stretch_angle);
+ DRW_ibo_request(cache->batch.edituv_faces_stretch_angle, &mbuflist->ibo.edituv_tris);
+ DRW_vbo_request(cache->batch.edituv_faces_stretch_angle, &mbuflist->vbo.uv);
+ DRW_vbo_request(cache->batch.edituv_faces_stretch_angle, &mbuflist->vbo.edituv_data);
+ DRW_vbo_request(cache->batch.edituv_faces_stretch_angle, &mbuflist->vbo.edituv_stretch_angle);
}
- MDEPS_ASSERT(batch.edituv_edges, ibo.edituv_lines, vbo.uv, vbo.edituv_data);
+ MDEPS_ASSERT(edituv_edges, ibo.edituv_lines, vbo.uv, vbo.edituv_data);
if (DRW_batch_requested(cache->batch.edituv_edges, GPU_PRIM_LINES)) {
- DRW_ibo_request(cache->batch.edituv_edges, &mbufcache->ibo.edituv_lines);
- DRW_vbo_request(cache->batch.edituv_edges, &mbufcache->vbo.uv);
- DRW_vbo_request(cache->batch.edituv_edges, &mbufcache->vbo.edituv_data);
+ DRW_ibo_request(cache->batch.edituv_edges, &mbuflist->ibo.edituv_lines);
+ DRW_vbo_request(cache->batch.edituv_edges, &mbuflist->vbo.uv);
+ DRW_vbo_request(cache->batch.edituv_edges, &mbuflist->vbo.edituv_data);
}
- MDEPS_ASSERT(batch.edituv_verts, ibo.edituv_points, vbo.uv, vbo.edituv_data);
+ MDEPS_ASSERT(edituv_verts, ibo.edituv_points, vbo.uv, vbo.edituv_data);
if (DRW_batch_requested(cache->batch.edituv_verts, GPU_PRIM_POINTS)) {
- DRW_ibo_request(cache->batch.edituv_verts, &mbufcache->ibo.edituv_points);
- DRW_vbo_request(cache->batch.edituv_verts, &mbufcache->vbo.uv);
- DRW_vbo_request(cache->batch.edituv_verts, &mbufcache->vbo.edituv_data);
+ DRW_ibo_request(cache->batch.edituv_verts, &mbuflist->ibo.edituv_points);
+ DRW_vbo_request(cache->batch.edituv_verts, &mbuflist->vbo.uv);
+ DRW_vbo_request(cache->batch.edituv_verts, &mbuflist->vbo.edituv_data);
}
- MDEPS_ASSERT(batch.edituv_fdots, ibo.edituv_fdots, vbo.fdots_uv, vbo.fdots_edituv_data);
+ MDEPS_ASSERT(edituv_fdots, ibo.edituv_fdots, vbo.fdots_uv, vbo.fdots_edituv_data);
if (DRW_batch_requested(cache->batch.edituv_fdots, GPU_PRIM_POINTS)) {
- DRW_ibo_request(cache->batch.edituv_fdots, &mbufcache->ibo.edituv_fdots);
- DRW_vbo_request(cache->batch.edituv_fdots, &mbufcache->vbo.fdots_uv);
- DRW_vbo_request(cache->batch.edituv_fdots, &mbufcache->vbo.fdots_edituv_data);
+ DRW_ibo_request(cache->batch.edituv_fdots, &mbuflist->ibo.edituv_fdots);
+ DRW_vbo_request(cache->batch.edituv_fdots, &mbuflist->vbo.fdots_uv);
+ DRW_vbo_request(cache->batch.edituv_fdots, &mbuflist->vbo.fdots_edituv_data);
}
MDEPS_ASSERT_MAP(vbo.lnor);
@@ -1762,7 +1764,7 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph,
MDEPS_ASSERT_MAP(ibo.edituv_points);
MDEPS_ASSERT_MAP(ibo.edituv_fdots);
- MDEPS_ASSERT_MAP(tris_per_mat);
+ MDEPS_ASSERT_MAP_INDEX(TRIS_PER_MAT_INDEX);
/* Meh loose Scene const correctness here. */
const bool use_subsurf_fdots = scene ? BKE_modifiers_uses_subsurf_facedots(scene, ob) : false;
@@ -1771,7 +1773,6 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph,
mesh_buffer_cache_create_requested(task_graph,
cache,
&cache->uv_cage,
- &cache->uv_cage_extraction_cache,
me,
is_editmode,
is_paint_mode,
@@ -1789,7 +1790,6 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph,
mesh_buffer_cache_create_requested(task_graph,
cache,
&cache->cage,
- &cache->cage_extraction_cache,
me,
is_editmode,
is_paint_mode,
@@ -1806,7 +1806,6 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph,
mesh_buffer_cache_create_requested(task_graph,
cache,
&cache->final,
- &cache->final_extraction_cache,
me,
is_editmode,
is_paint_mode,
diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh.c b/source/blender/draw/intern/mesh_extractors/extract_mesh.c
index 53827dcc7d9..44de07c8594 100644
--- a/source/blender/draw/intern/mesh_extractors/extract_mesh.c
+++ b/source/blender/draw/intern/mesh_extractors/extract_mesh.c
@@ -33,11 +33,11 @@
#include "draw_cache_impl.h"
-void *mesh_extract_buffer_get(const MeshExtract *extractor, MeshBufferCache *mbc)
+void *mesh_extract_buffer_get(const MeshExtract *extractor, MeshBufferList *mbuflist)
{
/* NOTE: POINTER_OFFSET on windows platforms casts internally to `void *`, but on GCC/CLANG to
- * `MeshBufferCache *`. What shows a different usage versus intent. */
- void **buffer_ptr = (void **)POINTER_OFFSET(mbc, extractor->mesh_buffer_offset);
+ * `MeshBufferList *`. What shows a different usage versus intent. */
+ void **buffer_ptr = (void **)POINTER_OFFSET(mbuflist, extractor->mesh_buffer_offset);
void *buffer = *buffer_ptr;
BLI_assert(buffer);
return buffer;
diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh.h b/source/blender/draw/intern/mesh_extractors/extract_mesh.h
index f24ccf1a028..d9f397fd8b8 100644
--- a/source/blender/draw/intern/mesh_extractors/extract_mesh.h
+++ b/source/blender/draw/intern/mesh_extractors/extract_mesh.h
@@ -232,7 +232,7 @@ typedef struct MeshExtract {
/** Used to know if the element callbacks are thread-safe and can be parallelized. */
bool use_threading;
/**
- * Offset in bytes of the buffer inside a MeshBufferCache instance. Points to a vertex or index
+ * Offset in bytes of the buffer inside a MeshBufferList instance. Points to a vertex or index
* buffer.
*/
size_t mesh_buffer_offset;
@@ -252,11 +252,11 @@ MeshRenderData *mesh_render_data_create(Mesh *me,
void mesh_render_data_free(MeshRenderData *mr);
void mesh_render_data_update_normals(MeshRenderData *mr, const eMRDataType data_flag);
void mesh_render_data_update_loose_geom(MeshRenderData *mr,
- MeshBufferExtractionCache *cache,
+ MeshBufferCache *cache,
const eMRIterType iter_type,
const eMRDataType data_flag);
void mesh_render_data_update_polys_sorted(MeshRenderData *mr,
- MeshBufferExtractionCache *cache,
+ MeshBufferCache *cache,
const eMRDataType data_flag);
void mesh_render_data_update_looptris(MeshRenderData *mr,
const eMRIterType iter_type,
@@ -270,7 +270,7 @@ typedef struct EditLoopData {
uchar bweight;
} EditLoopData;
-void *mesh_extract_buffer_get(const MeshExtract *extractor, MeshBufferCache *mbc);
+void *mesh_extract_buffer_get(const MeshExtract *extractor, MeshBufferList *mbuflist);
eMRIterType mesh_extract_iter_type(const MeshExtract *ext);
const MeshExtract *mesh_extract_override_get(const MeshExtract *extractor,
const bool do_hq_normals,
diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_edituv.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_edituv.cc
index 5bd5f7adaa8..d06fb91411e 100644
--- a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_edituv.cc
+++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_edituv.cc
@@ -104,7 +104,7 @@ constexpr MeshExtract create_extractor_edituv_tris()
extractor.data_type = MR_DATA_NONE;
extractor.data_size = sizeof(MeshExtract_EditUvElem_Data);
extractor.use_threading = false;
- extractor.mesh_buffer_offset = offsetof(MeshBufferCache, ibo.edituv_tris);
+ extractor.mesh_buffer_offset = offsetof(MeshBufferList, ibo.edituv_tris);
return extractor;
}
@@ -194,7 +194,7 @@ constexpr MeshExtract create_extractor_edituv_lines()
extractor.data_type = MR_DATA_NONE;
extractor.data_size = sizeof(MeshExtract_EditUvElem_Data);
extractor.use_threading = false;
- extractor.mesh_buffer_offset = offsetof(MeshBufferCache, ibo.edituv_lines);
+ extractor.mesh_buffer_offset = offsetof(MeshBufferList, ibo.edituv_lines);
return extractor;
}
@@ -278,7 +278,7 @@ constexpr MeshExtract create_extractor_edituv_points()
extractor.data_type = MR_DATA_NONE;
extractor.data_size = sizeof(MeshExtract_EditUvElem_Data);
extractor.use_threading = false;
- extractor.mesh_buffer_offset = offsetof(MeshBufferCache, ibo.edituv_points);
+ extractor.mesh_buffer_offset = offsetof(MeshBufferList, ibo.edituv_points);
return extractor;
}
@@ -374,7 +374,7 @@ constexpr MeshExtract create_extractor_edituv_fdots()
extractor.data_type = MR_DATA_NONE;
extractor.data_size = sizeof(MeshExtract_EditUvElem_Data);
extractor.use_threading = false;
- extractor.mesh_buffer_offset = offsetof(MeshBufferCache, ibo.edituv_fdots);
+ extractor.mesh_buffer_offset = offsetof(MeshBufferList, ibo.edituv_fdots);
return extractor;
}
diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_fdots.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_fdots.cc
index 0f41702a22c..ea58e1aeed8 100644
--- a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_fdots.cc
+++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_fdots.cc
@@ -105,7 +105,7 @@ constexpr MeshExtract create_extractor_fdots()
extractor.data_type = MR_DATA_NONE;
extractor.data_size = sizeof(GPUIndexBufBuilder);
extractor.use_threading = false;
- extractor.mesh_buffer_offset = offsetof(MeshBufferCache, ibo.fdots);
+ extractor.mesh_buffer_offset = offsetof(MeshBufferList, ibo.fdots);
return extractor;
}
diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines.cc
index 0096da9e56f..54f5611106f 100644
--- a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines.cc
+++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines.cc
@@ -168,7 +168,7 @@ constexpr MeshExtract create_extractor_lines()
extractor.data_type = MR_DATA_NONE;
extractor.data_size = sizeof(GPUIndexBufBuilder);
extractor.use_threading = true;
- extractor.mesh_buffer_offset = offsetof(MeshBufferCache, ibo.lines);
+ extractor.mesh_buffer_offset = offsetof(MeshBufferList, ibo.lines);
return extractor;
}
@@ -180,12 +180,12 @@ constexpr MeshExtract create_extractor_lines()
static void extract_lines_loose_subbuffer(const MeshRenderData *mr, struct MeshBatchCache *cache)
{
- BLI_assert(cache->final.ibo.lines);
+ BLI_assert(cache->final.buff.ibo.lines);
/* Multiply by 2 because these are edges indices. */
const int start = mr->edge_len * 2;
const int len = mr->edge_loose_len * 2;
GPU_indexbuf_create_subrange_in_place(
- cache->final.ibo.lines_loose, cache->final.ibo.lines, start, len);
+ cache->final.buff.ibo.lines_loose, cache->final.buff.ibo.lines, start, len);
cache->no_loose_wire = (len == 0);
}
@@ -213,7 +213,7 @@ constexpr MeshExtract create_extractor_lines_with_lines_loose()
extractor.data_type = MR_DATA_NONE;
extractor.data_size = sizeof(GPUIndexBufBuilder);
extractor.use_threading = true;
- extractor.mesh_buffer_offset = offsetof(MeshBufferCache, ibo.lines);
+ extractor.mesh_buffer_offset = offsetof(MeshBufferList, ibo.lines);
return extractor;
}
@@ -228,7 +228,7 @@ static void extract_lines_loose_only_init(const MeshRenderData *mr,
void *buf,
void *UNUSED(tls_data))
{
- BLI_assert(buf == cache->final.ibo.lines_loose);
+ BLI_assert(buf == cache->final.buff.ibo.lines_loose);
UNUSED_VARS_NDEBUG(buf);
extract_lines_loose_subbuffer(mr, cache);
}
@@ -240,7 +240,7 @@ constexpr MeshExtract create_extractor_lines_loose_only()
extractor.data_type = MR_DATA_LOOSE_GEOM;
extractor.data_size = 0;
extractor.use_threading = false;
- extractor.mesh_buffer_offset = offsetof(MeshBufferCache, ibo.lines_loose);
+ extractor.mesh_buffer_offset = offsetof(MeshBufferList, ibo.lines_loose);
return extractor;
}
diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines_adjacency.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines_adjacency.cc
index 7a37cf50264..522afcd44a1 100644
--- a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines_adjacency.cc
+++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines_adjacency.cc
@@ -185,7 +185,7 @@ constexpr MeshExtract create_extractor_lines_adjacency()
extractor.data_type = MR_DATA_NONE;
extractor.data_size = sizeof(MeshExtract_LineAdjacency_Data);
extractor.use_threading = false;
- extractor.mesh_buffer_offset = offsetof(MeshBufferCache, ibo.lines_adjacency);
+ extractor.mesh_buffer_offset = offsetof(MeshBufferList, ibo.lines_adjacency);
return extractor;
}
diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines_paint_mask.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines_paint_mask.cc
index 5def7edb75a..494a43e97d1 100644
--- a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines_paint_mask.cc
+++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines_paint_mask.cc
@@ -114,7 +114,7 @@ constexpr MeshExtract create_extractor_lines_paint_mask()
extractor.data_type = MR_DATA_NONE;
extractor.data_size = sizeof(MeshExtract_LinePaintMask_Data);
extractor.use_threading = false;
- extractor.mesh_buffer_offset = offsetof(MeshBufferCache, ibo.lines_paint_mask);
+ extractor.mesh_buffer_offset = offsetof(MeshBufferList, ibo.lines_paint_mask);
return extractor;
}
diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_points.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_points.cc
index cc1a19b8d26..b801ba04162 100644
--- a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_points.cc
+++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_points.cc
@@ -169,7 +169,7 @@ constexpr MeshExtract create_extractor_points()
extractor.use_threading = true;
extractor.data_type = MR_DATA_NONE;
extractor.data_size = sizeof(GPUIndexBufBuilder);
- extractor.mesh_buffer_offset = offsetof(MeshBufferCache, ibo.points);
+ extractor.mesh_buffer_offset = offsetof(MeshBufferList, ibo.points);
return extractor;
}
diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_tris.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_tris.cc
index e346177af52..54e733d3d86 100644
--- a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_tris.cc
+++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_tris.cc
@@ -105,20 +105,19 @@ static void extract_tris_finish(const MeshRenderData *mr,
/* Create ibo sub-ranges. Always do this to avoid error when the standard surface batch
* is created before the surfaces-per-material. */
- if (mr->use_final_mesh && cache->final.tris_per_mat) {
- MeshBufferCache *mbc_final = &cache->final;
+ if (mr->use_final_mesh && cache->tris_per_mat) {
int mat_start = 0;
for (int i = 0; i < mr->mat_len; i++) {
/* These IBOs have not been queried yet but we create them just in case they are needed
* later since they are not tracked by mesh_buffer_cache_create_requested(). */
- if (mbc_final->tris_per_mat[i] == nullptr) {
- mbc_final->tris_per_mat[i] = GPU_indexbuf_calloc();
+ if (cache->tris_per_mat[i] == nullptr) {
+ cache->tris_per_mat[i] = GPU_indexbuf_calloc();
}
const int mat_tri_len = mr->poly_sorted.mat_tri_len[i];
/* Multiply by 3 because these are triangle indices. */
const int start = mat_start * 3;
const int len = mat_tri_len * 3;
- GPU_indexbuf_create_subrange_in_place(mbc_final->tris_per_mat[i], ibo, start, len);
+ GPU_indexbuf_create_subrange_in_place(cache->tris_per_mat[i], ibo, start, len);
mat_start += mat_tri_len;
}
}
@@ -135,7 +134,7 @@ constexpr MeshExtract create_extractor_tris()
extractor.data_type = MR_DATA_LOOPTRI | MR_DATA_POLYS_SORTED;
extractor.data_size = sizeof(GPUIndexBufBuilder);
extractor.use_threading = true;
- extractor.mesh_buffer_offset = offsetof(MeshBufferCache, ibo.tris);
+ extractor.mesh_buffer_offset = offsetof(MeshBufferList, ibo.tris);
return extractor;
}
@@ -197,17 +196,16 @@ static void extract_tris_single_mat_finish(const MeshRenderData *mr,
/* Create ibo sub-ranges. Always do this to avoid error when the standard surface batch
* is created before the surfaces-per-material. */
- if (mr->use_final_mesh && cache->final.tris_per_mat) {
- MeshBufferCache *mbc = &cache->final;
+ if (mr->use_final_mesh && cache->tris_per_mat) {
for (int i = 0; i < mr->mat_len; i++) {
/* These IBOs have not been queried yet but we create them just in case they are needed
* later since they are not tracked by mesh_buffer_cache_create_requested(). */
- if (mbc->tris_per_mat[i] == nullptr) {
- mbc->tris_per_mat[i] = GPU_indexbuf_calloc();
+ if (cache->tris_per_mat[i] == nullptr) {
+ cache->tris_per_mat[i] = GPU_indexbuf_calloc();
}
/* Multiply by 3 because these are triangle indices. */
const int len = mr->tri_len * 3;
- GPU_indexbuf_create_subrange_in_place(mbc->tris_per_mat[i], ibo, 0, len);
+ GPU_indexbuf_create_subrange_in_place(cache->tris_per_mat[i], ibo, 0, len);
}
}
}
@@ -223,7 +221,7 @@ constexpr MeshExtract create_extractor_tris_single_mat()
extractor.data_type = MR_DATA_NONE;
extractor.data_size = sizeof(GPUIndexBufBuilder);
extractor.use_threading = true;
- extractor.mesh_buffer_offset = offsetof(MeshBufferCache, ibo.tris);
+ extractor.mesh_buffer_offset = offsetof(MeshBufferList, ibo.tris);
return extractor;
}
diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_edge_fac.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_edge_fac.cc
index 302616d4da9..2e2444a8e3d 100644
--- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_edge_fac.cc
+++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_edge_fac.cc
@@ -228,7 +228,7 @@ constexpr MeshExtract create_extractor_edge_fac()
extractor.data_type = MR_DATA_POLY_NOR;
extractor.data_size = sizeof(MeshExtract_EdgeFac_Data);
extractor.use_threading = false;
- extractor.mesh_buffer_offset = offsetof(MeshBufferCache, vbo.edge_fac);
+ extractor.mesh_buffer_offset = offsetof(MeshBufferList, vbo.edge_fac);
return extractor;
}
diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_edit_data.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_edit_data.cc
index a7efb9c8a1b..5232346e51e 100644
--- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_edit_data.cc
+++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_edit_data.cc
@@ -253,7 +253,7 @@ constexpr MeshExtract create_extractor_edit_data()
extractor.data_type = MR_DATA_NONE;
extractor.data_size = sizeof(EditLoopData *);
extractor.use_threading = true;
- extractor.mesh_buffer_offset = offsetof(MeshBufferCache, vbo.edit_data);
+ extractor.mesh_buffer_offset = offsetof(MeshBufferList, vbo.edit_data);
return extractor;
}
diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_edituv_data.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_edituv_data.cc
index 0378aadabd0..b8494428eed 100644
--- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_edituv_data.cc
+++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_edituv_data.cc
@@ -128,7 +128,7 @@ constexpr MeshExtract create_extractor_edituv_data()
extractor.data_type = MR_DATA_NONE;
extractor.data_size = sizeof(MeshExtract_EditUVData_Data);
extractor.use_threading = true;
- extractor.mesh_buffer_offset = offsetof(MeshBufferCache, vbo.edituv_data);
+ extractor.mesh_buffer_offset = offsetof(MeshBufferList, vbo.edituv_data);
return extractor;
}
diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_edituv_stretch_angle.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_edituv_stretch_angle.cc
index a60c0182e89..a947d98f955 100644
--- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_edituv_stretch_angle.cc
+++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_edituv_stretch_angle.cc
@@ -222,7 +222,7 @@ constexpr MeshExtract create_extractor_edituv_edituv_stretch_angle()
extractor.data_type = MR_DATA_NONE;
extractor.data_size = sizeof(MeshExtract_StretchAngle_Data);
extractor.use_threading = false;
- extractor.mesh_buffer_offset = offsetof(MeshBufferCache, vbo.edituv_stretch_angle);
+ extractor.mesh_buffer_offset = offsetof(MeshBufferList, vbo.edituv_stretch_angle);
return extractor;
}
diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_edituv_stretch_area.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_edituv_stretch_area.cc
index d79ac493c33..3db8cd79af5 100644
--- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_edituv_stretch_area.cc
+++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_edituv_stretch_area.cc
@@ -143,7 +143,7 @@ constexpr MeshExtract create_extractor_edituv_stretch_area()
extractor.data_type = MR_DATA_NONE;
extractor.data_size = 0;
extractor.use_threading = false;
- extractor.mesh_buffer_offset = offsetof(MeshBufferCache, vbo.edituv_stretch_area);
+ extractor.mesh_buffer_offset = offsetof(MeshBufferList, vbo.edituv_stretch_area);
return extractor;
}
diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_fdots_edituv_data.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_fdots_edituv_data.cc
index b7182d1b60f..28592417183 100644
--- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_fdots_edituv_data.cc
+++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_fdots_edituv_data.cc
@@ -89,7 +89,7 @@ constexpr MeshExtract create_extractor_fdots_edituv_data()
extractor.data_type = MR_DATA_NONE;
extractor.data_size = sizeof(MeshExtract_EditUVFdotData_Data);
extractor.use_threading = true;
- extractor.mesh_buffer_offset = offsetof(MeshBufferCache, vbo.fdots_edituv_data);
+ extractor.mesh_buffer_offset = offsetof(MeshBufferList, vbo.fdots_edituv_data);
return extractor;
}
diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_fdots_nor.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_fdots_nor.cc
index 5e4ad54f7b6..fed66f0057d 100644
--- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_fdots_nor.cc
+++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_fdots_nor.cc
@@ -105,7 +105,7 @@ constexpr MeshExtract create_extractor_fdots_nor()
extractor.data_type = MR_DATA_LOOP_NOR;
extractor.data_size = 0;
extractor.use_threading = false;
- extractor.mesh_buffer_offset = offsetof(MeshBufferCache, vbo.fdots_nor);
+ extractor.mesh_buffer_offset = offsetof(MeshBufferList, vbo.fdots_nor);
return extractor;
}
@@ -186,7 +186,7 @@ constexpr MeshExtract create_extractor_fdots_nor_hq()
extractor.data_type = MR_DATA_LOOP_NOR;
extractor.data_size = 0;
extractor.use_threading = false;
- extractor.mesh_buffer_offset = offsetof(MeshBufferCache, vbo.fdots_nor);
+ extractor.mesh_buffer_offset = offsetof(MeshBufferList, vbo.fdots_nor);
return extractor;
}
diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_fdots_pos.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_fdots_pos.cc
index e765fb8a8bf..33f9180e122 100644
--- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_fdots_pos.cc
+++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_fdots_pos.cc
@@ -106,7 +106,7 @@ constexpr MeshExtract create_extractor_fdots_pos()
extractor.data_type = MR_DATA_NONE;
extractor.data_size = sizeof(float(*)[3]);
extractor.use_threading = true;
- extractor.mesh_buffer_offset = offsetof(MeshBufferCache, vbo.fdots_pos);
+ extractor.mesh_buffer_offset = offsetof(MeshBufferList, vbo.fdots_pos);
return extractor;
}
diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_fdots_uv.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_fdots_uv.cc
index 042a0d2debe..6fe714def30 100644
--- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_fdots_uv.cc
+++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_fdots_uv.cc
@@ -114,7 +114,7 @@ constexpr MeshExtract create_extractor_fdots_uv()
extractor.data_type = MR_DATA_NONE;
extractor.data_size = sizeof(MeshExtract_FdotUV_Data);
extractor.use_threading = true;
- extractor.mesh_buffer_offset = offsetof(MeshBufferCache, vbo.fdots_uv);
+ extractor.mesh_buffer_offset = offsetof(MeshBufferList, vbo.fdots_uv);
return extractor;
}
diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_lnor.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_lnor.cc
index 8344a615cbe..f9f66c27aaa 100644
--- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_lnor.cc
+++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_lnor.cc
@@ -116,7 +116,7 @@ constexpr MeshExtract create_extractor_lnor()
extractor.data_type = MR_DATA_LOOP_NOR;
extractor.data_size = sizeof(GPUPackedNormal *);
extractor.use_threading = true;
- extractor.mesh_buffer_offset = offsetof(MeshBufferCache, vbo.lnor);
+ extractor.mesh_buffer_offset = offsetof(MeshBufferList, vbo.lnor);
return extractor;
}
@@ -214,7 +214,7 @@ constexpr MeshExtract create_extractor_lnor_hq()
extractor.data_type = MR_DATA_LOOP_NOR;
extractor.data_size = sizeof(gpuHQNor *);
extractor.use_threading = true;
- extractor.mesh_buffer_offset = offsetof(MeshBufferCache, vbo.lnor);
+ extractor.mesh_buffer_offset = offsetof(MeshBufferList, vbo.lnor);
return extractor;
}
diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_mesh_analysis.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_mesh_analysis.cc
index 075d54e268e..33a33c81bc2 100644
--- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_mesh_analysis.cc
+++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_mesh_analysis.cc
@@ -641,7 +641,7 @@ constexpr MeshExtract create_extractor_mesh_analysis()
extractor.data_type = MR_DATA_POLY_NOR | MR_DATA_LOOPTRI;
extractor.data_size = 0;
extractor.use_threading = false;
- extractor.mesh_buffer_offset = offsetof(MeshBufferCache, vbo.mesh_analysis);
+ extractor.mesh_buffer_offset = offsetof(MeshBufferList, vbo.mesh_analysis);
return extractor;
}
diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_orco.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_orco.cc
index 269c0343e32..c5187a47892 100644
--- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_orco.cc
+++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_orco.cc
@@ -102,7 +102,7 @@ constexpr MeshExtract create_extractor_orco()
extractor.data_type = MR_DATA_NONE;
extractor.data_size = sizeof(MeshExtract_Orco_Data);
extractor.use_threading = true;
- extractor.mesh_buffer_offset = offsetof(MeshBufferCache, vbo.orco);
+ extractor.mesh_buffer_offset = offsetof(MeshBufferList, vbo.orco);
return extractor;
}
diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_pos_nor.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_pos_nor.cc
index b8e5dcb613f..eb9a138590c 100644
--- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_pos_nor.cc
+++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_pos_nor.cc
@@ -208,7 +208,7 @@ constexpr MeshExtract create_extractor_pos_nor()
extractor.data_type = MR_DATA_NONE;
extractor.data_size = sizeof(MeshExtract_PosNor_Data);
extractor.use_threading = true;
- extractor.mesh_buffer_offset = offsetof(MeshBufferCache, vbo.pos_nor);
+ extractor.mesh_buffer_offset = offsetof(MeshBufferList, vbo.pos_nor);
return extractor;
}
@@ -401,7 +401,7 @@ constexpr MeshExtract create_extractor_pos_nor_hq()
extractor.data_type = MR_DATA_NONE;
extractor.data_size = sizeof(MeshExtract_PosNorHQ_Data);
extractor.use_threading = true;
- extractor.mesh_buffer_offset = offsetof(MeshBufferCache, vbo.pos_nor);
+ extractor.mesh_buffer_offset = offsetof(MeshBufferList, vbo.pos_nor);
return extractor;
}
diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_sculpt_data.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_sculpt_data.cc
index dbb1e1f880b..fd91bc5258f 100644
--- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_sculpt_data.cc
+++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_sculpt_data.cc
@@ -128,7 +128,7 @@ constexpr MeshExtract create_extractor_sculpt_data()
extractor.data_type = MR_DATA_NONE;
extractor.data_size = 0;
extractor.use_threading = false;
- extractor.mesh_buffer_offset = offsetof(MeshBufferCache, vbo.sculpt_data);
+ extractor.mesh_buffer_offset = offsetof(MeshBufferList, vbo.sculpt_data);
return extractor;
}
diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_select_idx.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_select_idx.cc
index cd8d46901c5..5ac30dd3be9 100644
--- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_select_idx.cc
+++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_select_idx.cc
@@ -205,7 +205,7 @@ constexpr MeshExtract create_extractor_poly_idx()
extractor.data_type = MR_DATA_NONE;
extractor.data_size = sizeof(uint32_t *);
extractor.use_threading = true;
- extractor.mesh_buffer_offset = offsetof(MeshBufferCache, vbo.poly_idx);
+ extractor.mesh_buffer_offset = offsetof(MeshBufferList, vbo.poly_idx);
return extractor;
}
@@ -220,7 +220,7 @@ constexpr MeshExtract create_extractor_edge_idx()
extractor.data_type = MR_DATA_NONE;
extractor.data_size = sizeof(uint32_t *);
extractor.use_threading = true;
- extractor.mesh_buffer_offset = offsetof(MeshBufferCache, vbo.edge_idx);
+ extractor.mesh_buffer_offset = offsetof(MeshBufferList, vbo.edge_idx);
return extractor;
}
@@ -237,7 +237,7 @@ constexpr MeshExtract create_extractor_vert_idx()
extractor.data_type = MR_DATA_NONE;
extractor.data_size = sizeof(uint32_t *);
extractor.use_threading = true;
- extractor.mesh_buffer_offset = offsetof(MeshBufferCache, vbo.vert_idx);
+ extractor.mesh_buffer_offset = offsetof(MeshBufferList, vbo.vert_idx);
return extractor;
}
@@ -279,7 +279,7 @@ constexpr MeshExtract create_extractor_fdot_idx()
extractor.data_type = MR_DATA_NONE;
extractor.data_size = sizeof(uint32_t *);
extractor.use_threading = true;
- extractor.mesh_buffer_offset = offsetof(MeshBufferCache, vbo.fdot_idx);
+ extractor.mesh_buffer_offset = offsetof(MeshBufferList, vbo.fdot_idx);
return extractor;
}
diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_skin_roots.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_skin_roots.cc
index ffca01d00dd..1847a360837 100644
--- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_skin_roots.cc
+++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_skin_roots.cc
@@ -80,7 +80,7 @@ constexpr MeshExtract create_extractor_skin_roots()
extractor.data_type = MR_DATA_NONE;
extractor.data_size = 0;
extractor.use_threading = false;
- extractor.mesh_buffer_offset = offsetof(MeshBufferCache, vbo.skin_roots);
+ extractor.mesh_buffer_offset = offsetof(MeshBufferList, vbo.skin_roots);
return extractor;
}
diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_tan.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_tan.cc
index 8f36bfdf1ef..1f8776fc98e 100644
--- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_tan.cc
+++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_tan.cc
@@ -226,7 +226,7 @@ constexpr MeshExtract create_extractor_tan()
extractor.data_type = MR_DATA_POLY_NOR | MR_DATA_TAN_LOOP_NOR | MR_DATA_LOOPTRI;
extractor.data_size = 0;
extractor.use_threading = false;
- extractor.mesh_buffer_offset = offsetof(MeshBufferCache, vbo.tan);
+ extractor.mesh_buffer_offset = offsetof(MeshBufferList, vbo.tan);
return extractor;
}
@@ -252,7 +252,7 @@ constexpr MeshExtract create_extractor_tan_hq()
extractor.data_type = MR_DATA_POLY_NOR | MR_DATA_TAN_LOOP_NOR | MR_DATA_LOOPTRI;
extractor.data_size = 0;
extractor.use_threading = false;
- extractor.mesh_buffer_offset = offsetof(MeshBufferCache, vbo.tan);
+ extractor.mesh_buffer_offset = offsetof(MeshBufferList, vbo.tan);
return extractor;
}
diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_uv.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_uv.cc
index 013da7d674a..af279b08a59 100644
--- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_uv.cc
+++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_uv.cc
@@ -123,7 +123,7 @@ constexpr MeshExtract create_extractor_uv()
extractor.data_type = MR_DATA_NONE;
extractor.data_size = 0;
extractor.use_threading = false;
- extractor.mesh_buffer_offset = offsetof(MeshBufferCache, vbo.uv);
+ extractor.mesh_buffer_offset = offsetof(MeshBufferList, vbo.uv);
return extractor;
}
diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_vcol.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_vcol.cc
index d810acfb617..2c7770c8e72 100644
--- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_vcol.cc
+++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_vcol.cc
@@ -178,7 +178,7 @@ constexpr MeshExtract create_extractor_vcol()
extractor.data_type = MR_DATA_NONE;
extractor.data_size = 0;
extractor.use_threading = false;
- extractor.mesh_buffer_offset = offsetof(MeshBufferCache, vbo.vcol);
+ extractor.mesh_buffer_offset = offsetof(MeshBufferList, vbo.vcol);
return extractor;
}
diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_weights.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_weights.cc
index c547a453aeb..bdb1410a755 100644
--- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_weights.cc
+++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_weights.cc
@@ -176,7 +176,7 @@ constexpr MeshExtract create_extractor_weights()
extractor.data_type = MR_DATA_NONE;
extractor.data_size = sizeof(MeshExtract_Weight_Data);
extractor.use_threading = true;
- extractor.mesh_buffer_offset = offsetof(MeshBufferCache, vbo.weights);
+ extractor.mesh_buffer_offset = offsetof(MeshBufferList, vbo.weights);
return extractor;
}
diff --git a/source/blender/editors/curve/editfont.c b/source/blender/editors/curve/editfont.c
index 39fb2882e4b..b7deea5069e 100644
--- a/source/blender/editors/curve/editfont.c
+++ b/source/blender/editors/curve/editfont.c
@@ -576,7 +576,7 @@ static int paste_from_file_exec(bContext *C, wmOperator *op)
char *path;
int retval;
- path = RNA_string_get_alloc(op->ptr, "filepath", NULL, 0);
+ path = RNA_string_get_alloc(op->ptr, "filepath", NULL, 0, NULL);
retval = paste_from_file(C, op->reports, path);
MEM_freeN(path);
@@ -1627,7 +1627,7 @@ static int insert_text_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
- inserted_utf8 = RNA_string_get_alloc(op->ptr, "text", NULL, 0);
+ inserted_utf8 = RNA_string_get_alloc(op->ptr, "text", NULL, 0, NULL);
len = BLI_strlen_utf8(inserted_utf8);
inserted_text = MEM_callocN(sizeof(char32_t) * (len + 1), "FONT_insert_text");
diff --git a/source/blender/editors/gpencil/gpencil_interpolate.c b/source/blender/editors/gpencil/gpencil_interpolate.c
index 8640ffa67cf..a8bd3b11bb1 100644
--- a/source/blender/editors/gpencil/gpencil_interpolate.c
+++ b/source/blender/editors/gpencil/gpencil_interpolate.c
@@ -890,9 +890,9 @@ static int gpencil_interpolate_modal(bContext *C, wmOperator *op, const wmEvent
}
case MOUSEMOVE: /* calculate new position */
{
- /* only handle mousemove if not doing numinput */
+ /* Only handle mouse-move if not doing numeric-input. */
if (has_numinput == false) {
- /* update shift based on position of mouse */
+ /* Update shift based on position of mouse. */
gpencil_mouse_update_shift(tgpi, op, event);
/* update screen */
diff --git a/source/blender/editors/gpencil/gpencil_primitive.c b/source/blender/editors/gpencil/gpencil_primitive.c
index 894fb83d70e..5ecb6d9a212 100644
--- a/source/blender/editors/gpencil/gpencil_primitive.c
+++ b/source/blender/editors/gpencil/gpencil_primitive.c
@@ -1944,9 +1944,9 @@ static int gpencil_primitive_modal(bContext *C, wmOperator *op, const wmEvent *e
if (ELEM(tgpi->flag, IN_CURVE_EDIT)) {
break;
}
- /* only handle mousemove if not doing numinput */
+ /* Only handle mouse-move if not doing numeric-input. */
if (has_numinput == false) {
- /* update position of mouse */
+ /* Update position of mouse. */
copy_v2_v2(tgpi->end, tgpi->mval);
copy_v2_v2(tgpi->start, tgpi->origin);
if (tgpi->flag == IDLE) {
diff --git a/source/blender/editors/include/UI_resources.h b/source/blender/editors/include/UI_resources.h
index c99c7f681b3..dde8a637e05 100644
--- a/source/blender/editors/include/UI_resources.h
+++ b/source/blender/editors/include/UI_resources.h
@@ -185,7 +185,7 @@ typedef enum ThemeColorID {
TH_NODE_LAYOUT,
TH_NODE_SHADER,
TH_NODE_INTERFACE,
- TH_NODE_CONVERTOR,
+ TH_NODE_CONVERTER,
TH_NODE_GROUP,
TH_NODE_FRAME,
TH_NODE_MATTE,
diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c
index b8f324cba83..a618daec4bc 100644
--- a/source/blender/editors/interface/interface_handlers.c
+++ b/source/blender/editors/interface/interface_handlers.c
@@ -3350,7 +3350,7 @@ static bool ui_textedit_copypaste(uiBut *but, uiHandleButtonData *data, const in
if (pbuf) {
if (UI_but_is_utf8(but)) {
- buf_len -= BLI_utf8_invalid_strip(pbuf, (size_t)buf_len);
+ buf_len -= BLI_str_utf8_invalid_strip(pbuf, (size_t)buf_len);
}
ui_textedit_insert_buf(but, data, pbuf, buf_len);
@@ -3527,7 +3527,7 @@ static void ui_textedit_end(bContext *C, uiBut *but, uiHandleButtonData *data)
if (but) {
if (UI_but_is_utf8(but)) {
- const int strip = BLI_utf8_invalid_strip(but->editstr, strlen(but->editstr));
+ const int strip = BLI_str_utf8_invalid_strip(but->editstr, strlen(but->editstr));
/* not a file?, strip non utf-8 chars */
if (strip) {
/* won't happen often so isn't that annoying to keep it here for a while */
@@ -6025,7 +6025,7 @@ static int ui_do_but_BLOCK(bContext *C, uiBut *but, uiHandleButtonData *data, co
* the slot menu fails to switch a second time.
*
* The active state of the button could be maintained some other way
- * and remove this mousemove event.
+ * and remove this mouse-move event.
*/
WM_event_add_mousemove(data->window);
@@ -8364,7 +8364,7 @@ static void button_activate_state(bContext *C, uiBut *but, uiHandleButtonState s
}
}
- /* wait for mousemove to enable drag */
+ /* Wait for mouse-move to enable drag. */
if (state == BUTTON_STATE_WAIT_DRAG) {
but->flag &= ~UI_SELECT;
}
@@ -8631,9 +8631,9 @@ static void button_activate_exit(
ui_but_update(but);
}
- /* adds empty mousemove in queue for re-init handler, in case mouse is
+ /* Adds empty mouse-move in queue for re-initialize handler, in case mouse is
* still over a button. We cannot just check for this ourselves because
- * at this point the mouse may be over a button in another region */
+ * at this point the mouse may be over a button in another region. */
if (mousemove) {
WM_event_add_mousemove(CTX_wm_window(C));
}
diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c
index 351b73c320b..0e5a6a79137 100644
--- a/source/blender/editors/interface/interface_templates.c
+++ b/source/blender/editors/interface/interface_templates.c
@@ -6489,6 +6489,17 @@ void uiTemplateCacheFile(uiLayout *layout,
uiLayoutSetActive(row, engine_supports_procedural);
uiItemR(row, &fileptr, "use_render_procedural", 0, NULL, ICON_NONE);
+ const bool use_render_procedural = RNA_boolean_get(&fileptr, "use_render_procedural");
+ const bool use_prefetch = RNA_boolean_get(&fileptr, "use_prefetch");
+
+ row = uiLayoutRow(layout, false);
+ uiLayoutSetEnabled(row, use_render_procedural);
+ uiItemR(row, &fileptr, "use_prefetch", 0, NULL, ICON_NONE);
+
+ sub = uiLayoutRow(layout, false);
+ uiLayoutSetEnabled(sub, use_prefetch && use_render_procedural);
+ uiItemR(sub, &fileptr, "prefetch_cache_size", 0, NULL, ICON_NONE);
+
row = uiLayoutRowWithHeading(layout, true, IFACE_("Override Frame"));
sub = uiLayoutRow(row, true);
uiLayoutSetPropDecorate(sub, false);
diff --git a/source/blender/editors/interface/resources.c b/source/blender/editors/interface/resources.c
index c9bfd883332..e13b69a9763 100644
--- a/source/blender/editors/interface/resources.c
+++ b/source/blender/editors/interface/resources.c
@@ -631,7 +631,7 @@ const uchar *UI_ThemeGetColorPtr(bTheme *btheme, int spacetype, int colorid)
case TH_NODE_SHADER:
cp = ts->nodeclass_shader;
break;
- case TH_NODE_CONVERTOR:
+ case TH_NODE_CONVERTER:
cp = ts->syntaxv;
break;
case TH_NODE_GROUP:
diff --git a/source/blender/editors/mesh/editmesh_tools.c b/source/blender/editors/mesh/editmesh_tools.c
index b8bbf2d3e70..956658bd2b7 100644
--- a/source/blender/editors/mesh/editmesh_tools.c
+++ b/source/blender/editors/mesh/editmesh_tools.c
@@ -8627,7 +8627,7 @@ static int edbm_point_normals_modal(bContext *C, wmOperator *op, const wmEvent *
RNA_enum_set(op->ptr, "mode", mode);
}
- /* Only handle mousemove event in case we are in mouse mode. */
+ /* Only handle mouse-move event in case we are in mouse mode. */
if (event->type == MOUSEMOVE || force_mousemove) {
if (mode == EDBM_CLNOR_POINTTO_MODE_MOUSE) {
ARegion *region = CTX_wm_region(C);
diff --git a/source/blender/editors/physics/particle_edit.c b/source/blender/editors/physics/particle_edit.c
index 85883a2d29a..8afc5c583e0 100644
--- a/source/blender/editors/physics/particle_edit.c
+++ b/source/blender/editors/physics/particle_edit.c
@@ -4667,7 +4667,7 @@ typedef struct BrushEdit {
int lastmouse[2];
float zfac;
- /* optional cached view settings to avoid setting on every mousemove */
+ /** Optional cached view settings to avoid setting on every mouse-move. */
PEData data;
} BrushEdit;
diff --git a/source/blender/editors/render/render_update.c b/source/blender/editors/render/render_update.c
index 6db148eb4e1..8bc2281db73 100644
--- a/source/blender/editors/render/render_update.c
+++ b/source/blender/editors/render/render_update.c
@@ -64,7 +64,9 @@
#include <stdio.h>
-/***************************** Render Engines ********************************/
+/* -------------------------------------------------------------------- */
+/** \name Render Engines
+ * \{ */
/* Update 3D viewport render or draw engine on changes to the scene or view settings. */
void ED_render_view3d_update(Depsgraph *depsgraph,
@@ -206,15 +208,15 @@ void ED_render_engine_changed(Main *bmain, const bool update_scene_data)
}
}
- /* Update CacheFiles to ensure that procedurals are properly taken into account. */
+ /* Update #CacheFiles to ensure that procedurals are properly taken into account. */
LISTBASE_FOREACH (CacheFile *, cachefile, &bmain->cachefiles) {
- /* Only update cachefiles which are set to use a render procedural. We do not use
- * BKE_cachefile_uses_render_procedural here as we need to update regardless of the current
- * engine or its settings. */
+ /* Only update cache-files which are set to use a render procedural.
+ * We do not use #BKE_cachefile_uses_render_procedural here as we need to update regardless of
+ * the current engine or its settings. */
if (cachefile->use_render_procedural) {
DEG_id_tag_update(&cachefile->id, ID_RECALC_COPY_ON_WRITE);
- /* Rebuild relations so that modifiers are reconnected to or disconnected from the cachefile.
- */
+ /* Rebuild relations so that modifiers are reconnected to or disconnected from the
+ * cache-file. */
DEG_relations_tag_update(bmain);
}
}
@@ -227,10 +229,16 @@ void ED_render_view_layer_changed(Main *bmain, bScreen *screen)
}
}
-/***************************** Updates ***********************************
- * ED_render_id_flush_update gets called from DEG_id_tag_update, to do *
- * editor level updates when the ID changes. when these ID blocks are in *
- * the dependency graph, we can get rid of the manual dependency checks. */
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Updates
+ *
+ * #ED_render_id_flush_update gets called from #DEG_id_tag_update,
+ * to do editor level updates when the ID changes.
+ * When these ID blocks are in the dependency graph,
+ * we can get rid of the manual dependency checks.
+ * \{ */
static void material_changed(Main *UNUSED(bmain), Material *ma)
{
@@ -336,3 +344,5 @@ void ED_render_id_flush_update(const DEGEditorUpdateContext *update_ctx, ID *id)
break;
}
}
+
+/** \} */
diff --git a/source/blender/editors/space_buttons/buttons_ops.c b/source/blender/editors/space_buttons/buttons_ops.c
index c141789f171..798f4898aaa 100644
--- a/source/blender/editors/space_buttons/buttons_ops.c
+++ b/source/blender/editors/space_buttons/buttons_ops.c
@@ -198,7 +198,8 @@ static int file_browse_exec(bContext *C, wmOperator *op)
Main *bmain = CTX_data_main(C);
FileBrowseOp *fbo = op->customdata;
ID *id;
- char *str, path[FILE_MAX];
+ char *str;
+ int str_len;
const char *path_prop = RNA_struct_find_property(op->ptr, "directory") ? "directory" :
"filepath";
@@ -206,10 +207,11 @@ static int file_browse_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
- str = RNA_string_get_alloc(op->ptr, path_prop, NULL, 0);
+ str = RNA_string_get_alloc(op->ptr, path_prop, NULL, 0, &str_len);
/* Add slash for directories, important for some properties. */
if (RNA_property_subtype(fbo->prop) == PROP_DIRPATH) {
+ char path[FILE_MAX];
const bool is_relative = RNA_boolean_get(op->ptr, "relative_path");
id = fbo->ptr.owner_id;
@@ -220,13 +222,13 @@ static int file_browse_exec(bContext *C, wmOperator *op)
/* Do this first so '//' isn't converted to '//\' on windows. */
BLI_path_slash_ensure(path);
if (is_relative) {
- BLI_strncpy(path, str, FILE_MAX);
+ const int path_len = BLI_strncpy_rlen(path, str, FILE_MAX);
BLI_path_rel(path, BKE_main_blendfile_path(bmain));
- str = MEM_reallocN(str, strlen(path) + 2);
+ str = MEM_reallocN(str, path_len + 2);
BLI_strncpy(str, path, FILE_MAX);
}
else {
- str = MEM_reallocN(str, strlen(str) + 2);
+ str = MEM_reallocN(str, str_len + 2);
}
}
else {
diff --git a/source/blender/editors/space_console/console_ops.c b/source/blender/editors/space_console/console_ops.c
index bdb7c622cd2..763beb8671b 100644
--- a/source/blender/editors/space_console/console_ops.c
+++ b/source/blender/editors/space_console/console_ops.c
@@ -384,7 +384,7 @@ static int console_insert_exec(bContext *C, wmOperator *op)
SpaceConsole *sc = CTX_wm_space_console(C);
ARegion *region = CTX_wm_region(C);
ConsoleLine *ci = console_history_verify(C);
- char *str = RNA_string_get_alloc(op->ptr, "text", NULL, 0);
+ char *str = RNA_string_get_alloc(op->ptr, "text", NULL, 0, NULL);
int len;
if (str[0] == '\t' && str[1] == '\0') {
@@ -860,7 +860,7 @@ static int console_history_append_exec(bContext *C, wmOperator *op)
ScrArea *area = CTX_wm_area(C);
ConsoleLine *ci = console_history_verify(C);
/* own this text in the new line, don't free */
- char *str = RNA_string_get_alloc(op->ptr, "text", NULL, 0);
+ char *str = RNA_string_get_alloc(op->ptr, "text", NULL, 0, NULL);
int cursor = RNA_int_get(op->ptr, "current_character");
const bool rem_dupes = RNA_boolean_get(op->ptr, "remove_duplicates");
int prev_len = ci->len;
@@ -923,7 +923,7 @@ static int console_scrollback_append_exec(bContext *C, wmOperator *op)
ConsoleLine *ci;
/* own this text in the new line, don't free */
- char *str = RNA_string_get_alloc(op->ptr, "text", NULL, 0);
+ char *str = RNA_string_get_alloc(op->ptr, "text", NULL, 0, NULL);
int type = RNA_enum_get(op->ptr, "type");
console_history_verify(C);
diff --git a/source/blender/editors/space_image/image_ops.c b/source/blender/editors/space_image/image_ops.c
index 29c1452b988..4f8feda3911 100644
--- a/source/blender/editors/space_image/image_ops.c
+++ b/source/blender/editors/space_image/image_ops.c
@@ -3935,7 +3935,7 @@ static int tile_add_exec(bContext *C, wmOperator *op)
}
bool fill_tile = RNA_boolean_get(op->ptr, "fill");
- char *label = RNA_string_get_alloc(op->ptr, "label", NULL, 0);
+ char *label = RNA_string_get_alloc(op->ptr, "label", NULL, 0, NULL);
/* BKE_image_add_tile assumes a pre-sorted list of tiles. */
BKE_image_sort_tiles(ima);
diff --git a/source/blender/editors/space_image/image_sequence.c b/source/blender/editors/space_image/image_sequence.c
index 288b3d94b1d..c4f111264a3 100644
--- a/source/blender/editors/space_image/image_sequence.c
+++ b/source/blender/editors/space_image/image_sequence.c
@@ -68,7 +68,7 @@ static void image_sequence_get_frame_ranges(wmOperator *op, ListBase *ranges)
RNA_BEGIN (op->ptr, itemptr, "files") {
char head[FILE_MAX], tail[FILE_MAX];
ushort digits;
- char *filename = RNA_string_get_alloc(&itemptr, "name", NULL, 0);
+ char *filename = RNA_string_get_alloc(&itemptr, "name", NULL, 0, NULL);
ImageFrame *frame = MEM_callocN(sizeof(ImageFrame), "image_frame");
/* use the first file in the list as base filename */
diff --git a/source/blender/editors/space_info/info_ops.c b/source/blender/editors/space_info/info_ops.c
index 94e53958524..a99396ecdf0 100644
--- a/source/blender/editors/space_info/info_ops.c
+++ b/source/blender/editors/space_info/info_ops.c
@@ -512,7 +512,7 @@ void FILE_OT_report_missing_files(wmOperatorType *ot)
static int find_missing_files_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
- const char *searchpath = RNA_string_get_alloc(op->ptr, "directory", NULL, 0);
+ const char *searchpath = RNA_string_get_alloc(op->ptr, "directory", NULL, 0, NULL);
const bool find_all = RNA_boolean_get(op->ptr, "find_all");
BKE_bpath_missing_files_find(bmain, searchpath, op->reports, find_all);
diff --git a/source/blender/editors/space_node/node_draw.cc b/source/blender/editors/space_node/node_draw.cc
index 84741f61245..cca61148c85 100644
--- a/source/blender/editors/space_node/node_draw.cc
+++ b/source/blender/editors/space_node/node_draw.cc
@@ -671,8 +671,8 @@ int node_get_colorid(bNode *node)
return TH_NODE_INPUT;
case NODE_CLASS_OUTPUT:
return (node->flag & NODE_DO_OUTPUT) ? TH_NODE_OUTPUT : TH_NODE;
- case NODE_CLASS_CONVERTOR:
- return TH_NODE_CONVERTOR;
+ case NODE_CLASS_CONVERTER:
+ return TH_NODE_CONVERTER;
case NODE_CLASS_OP_COLOR:
return TH_NODE_COLOR;
case NODE_CLASS_OP_VECTOR:
diff --git a/source/blender/editors/space_sequencer/sequencer_add.c b/source/blender/editors/space_sequencer/sequencer_add.c
index ff8cbdb1a59..16b690dd6e4 100644
--- a/source/blender/editors/space_sequencer/sequencer_add.c
+++ b/source/blender/editors/space_sequencer/sequencer_add.c
@@ -254,11 +254,11 @@ static void load_data_init_from_operator(SeqLoadData *load_data, bContext *C, wm
BLI_strncpy(load_data->name, BLI_path_basename(load_data->path), sizeof(load_data->name));
}
else if ((prop = RNA_struct_find_property(op->ptr, "directory"))) {
- char *directory = RNA_string_get_alloc(op->ptr, "directory", NULL, 0);
+ char *directory = RNA_string_get_alloc(op->ptr, "directory", NULL, 0, NULL);
if ((prop = RNA_struct_find_property(op->ptr, "files"))) {
RNA_PROP_BEGIN (op->ptr, itemptr, prop) {
- char *filename = RNA_string_get_alloc(&itemptr, "name", NULL, 0);
+ char *filename = RNA_string_get_alloc(&itemptr, "name", NULL, 0, NULL);
BLI_strncpy(load_data->name, filename, sizeof(load_data->name));
BLI_join_dirfile(load_data->path, sizeof(load_data->path), directory, filename);
MEM_freeN(filename);
@@ -944,7 +944,7 @@ int sequencer_image_seq_get_minmax_frame(wmOperator *op,
RNA_BEGIN (op->ptr, itemptr, "files") {
char *filename;
int frame;
- filename = RNA_string_get_alloc(&itemptr, "name", NULL, 0);
+ filename = RNA_string_get_alloc(&itemptr, "name", NULL, 0, NULL);
if (filename) {
if (BLI_path_frame_get(filename, &frame, &numdigits)) {
@@ -973,7 +973,7 @@ void sequencer_image_seq_reserve_frames(
{
char *filename = NULL;
RNA_BEGIN (op->ptr, itemptr, "files") {
- filename = RNA_string_get_alloc(&itemptr, "name", NULL, 0);
+ filename = RNA_string_get_alloc(&itemptr, "name", NULL, 0, NULL);
break;
}
RNA_END;
@@ -1023,7 +1023,7 @@ static void sequencer_add_image_strip_load_files(
else {
size_t strip_frame = 0;
RNA_BEGIN (op->ptr, itemptr, "files") {
- char *filename = RNA_string_get_alloc(&itemptr, "name", NULL, 0);
+ char *filename = RNA_string_get_alloc(&itemptr, "name", NULL, 0, NULL);
SEQ_add_image_load_file(seq, strip_frame, filename);
MEM_freeN(filename);
strip_frame++;
diff --git a/source/blender/editors/space_sequencer/sequencer_edit.c b/source/blender/editors/space_sequencer/sequencer_edit.c
index afad8999e88..9a2225a44c5 100644
--- a/source/blender/editors/space_sequencer/sequencer_edit.c
+++ b/source/blender/editors/space_sequencer/sequencer_edit.c
@@ -2826,7 +2826,7 @@ static int sequencer_change_path_exec(bContext *C, wmOperator *op)
}
else {
RNA_BEGIN (op->ptr, itemptr, "files") {
- char *filename = RNA_string_get_alloc(&itemptr, "name", NULL, 0);
+ char *filename = RNA_string_get_alloc(&itemptr, "name", NULL, 0, NULL);
BLI_strncpy(se->name, filename, sizeof(se->name));
MEM_freeN(filename);
se++;
diff --git a/source/blender/editors/space_text/text_ops.c b/source/blender/editors/space_text/text_ops.c
index 2b78ecb245d..b5fcadbefe8 100644
--- a/source/blender/editors/space_text/text_ops.c
+++ b/source/blender/editors/space_text/text_ops.c
@@ -3430,7 +3430,7 @@ static int text_insert_exec(bContext *C, wmOperator *op)
text_drawcache_tag_update(st, 0);
- str = RNA_string_get_alloc(op->ptr, "text", NULL, 0);
+ str = RNA_string_get_alloc(op->ptr, "text", NULL, 0, NULL);
ED_text_undo_push_init(C);
diff --git a/source/blender/editors/space_view3d/view3d_gizmo_ruler.c b/source/blender/editors/space_view3d/view3d_gizmo_ruler.c
index 49299d73337..edc34d0d883 100644
--- a/source/blender/editors/space_view3d/view3d_gizmo_ruler.c
+++ b/source/blender/editors/space_view3d/view3d_gizmo_ruler.c
@@ -299,7 +299,9 @@ static void view3d_ruler_item_project(RulerInfo *ruler_info, float r_co[3], cons
ED_view3d_win_to_3d_int(ruler_info->area->spacedata.first, ruler_info->region, r_co, xy, r_co);
}
-/* use for mousemove events */
+/**
+ * Use for mouse-move events.
+ */
static bool view3d_ruler_item_mousemove(struct Depsgraph *depsgraph,
RulerInfo *ruler_info,
RulerItem *ruler_item,
diff --git a/source/blender/editors/transform/transform_convert.c b/source/blender/editors/transform/transform_convert.c
index 094ae080de0..d756e2c90a6 100644
--- a/source/blender/editors/transform/transform_convert.c
+++ b/source/blender/editors/transform/transform_convert.c
@@ -1662,20 +1662,23 @@ void animrecord_check_state(TransInfo *t, struct Object *ob)
}
}
-void transform_convert_flush_handle2D(TransData *td, TransData2D *td2d, const float inv_unit_scale)
+void transform_convert_flush_handle2D(TransData *td, TransData2D *td2d, const float y_fac)
{
+ float delta_x = td->loc[0] - td->iloc[0];
+ float delta_y = (td->loc[1] - td->iloc[1]) * y_fac;
+
/* If the handles are to be moved too
* (as side-effect of keyframes moving, to keep the general effect)
* offset them by the same amount so that the general angles are maintained
* (i.e. won't change while handles are free-to-roam and keyframes are snap-locked).
*/
if ((td->flag & TD_MOVEHANDLE1) && td2d->h1) {
- td2d->h1[0] = td2d->ih1[0] + td->loc[0] - td->iloc[0];
- td2d->h1[1] = td2d->ih1[1] + (td->loc[1] - td->iloc[1]) * inv_unit_scale;
+ td2d->h1[0] = td2d->ih1[0] + delta_x;
+ td2d->h1[1] = td2d->ih1[1] + delta_y;
}
if ((td->flag & TD_MOVEHANDLE2) && td2d->h2) {
- td2d->h2[0] = td2d->ih2[0] + td->loc[0] - td->iloc[0];
- td2d->h2[1] = td2d->ih2[1] + (td->loc[1] - td->iloc[1]) * inv_unit_scale;
+ td2d->h2[0] = td2d->ih2[0] + delta_x;
+ td2d->h2[1] = td2d->ih2[1] + delta_y;
}
}
diff --git a/source/blender/editors/transform/transform_convert.h b/source/blender/editors/transform/transform_convert.h
index fa34e2555d6..9cb0400cad9 100644
--- a/source/blender/editors/transform/transform_convert.h
+++ b/source/blender/editors/transform/transform_convert.h
@@ -43,9 +43,7 @@ void sort_trans_data_dist(TransInfo *t);
void createTransData(struct bContext *C, TransInfo *t);
bool clipUVTransform(TransInfo *t, float vec[2], const bool resize);
void clipUVData(TransInfo *t);
-void transform_convert_flush_handle2D(TransData *td,
- TransData2D *td2d,
- const float inv_unit_scale);
+void transform_convert_flush_handle2D(TransData *td, TransData2D *td2d, const float y_fac);
void recalcData(TransInfo *t);
/* transform_convert_mesh.c */
diff --git a/source/blender/editors/transform/transform_convert_action.c b/source/blender/editors/transform/transform_convert_action.c
index 8a3b254be5c..075db30fa61 100644
--- a/source/blender/editors/transform/transform_convert_action.c
+++ b/source/blender/editors/transform/transform_convert_action.c
@@ -158,8 +158,9 @@ static void TimeToTransData(
/* Setup #TransData. */
td->loc = time; /* Usually #td2d->loc is used here. But this is for when the original location is
not float[3]. */
+ copy_v3_v3(td->iloc, td->loc);
td->val = time;
- td->ival = td->iloc[0] = *(time);
+ td->ival = *(time);
td->center[0] = td->ival;
td->center[1] = ypos;
@@ -241,16 +242,15 @@ static int GPLayerToTransData(TransData *td,
for (gpf = gpl->frames.first; gpf; gpf = gpf->next) {
if (is_prop_edit || (gpf->flag & GP_FRAME_SELECT)) {
if (FrameOnMouseSide(side, (float)gpf->framenum, cfra)) {
- /* memory is calloc'ed, so that should zero everything nicely for us */
- td->val = &tfd->val;
- td->ival = (float)gpf->framenum;
+ tfd->val = (float)gpf->framenum;
+ tfd->sdata = &gpf->framenum;
+
+ td->val = td->loc = &tfd->val; /* XXX: It's not a 3d array. */
+ td->ival = td->iloc[0] = (float)gpf->framenum;
td->center[0] = td->ival;
td->center[1] = ypos;
- tfd->val = (float)gpf->framenum;
- tfd->sdata = &gpf->framenum;
-
/* Advance `td` now. */
td++;
tfd++;
@@ -616,7 +616,11 @@ void recalcData_actedit(TransInfo *t)
if ((autosnap != SACTSNAP_OFF) && (t->state != TRANS_CANCEL) && !(td->flag & TD_NOTIMESNAP)) {
transform_snap_anim_flush_data(t, td, autosnap, td->loc);
}
- transform_convert_flush_handle2D(td, td2d, 1.0f);
+
+ /* Constrain Y. */
+ td->loc[1] = td->iloc[1];
+
+ transform_convert_flush_handle2D(td, td2d, 0.0f);
}
if (ac.datatype != ANIMCONT_MASK) {
diff --git a/source/blender/editors/transform/transform_convert_armature.c b/source/blender/editors/transform/transform_convert_armature.c
index f56d60b7376..5627a910ab4 100644
--- a/source/blender/editors/transform/transform_convert_armature.c
+++ b/source/blender/editors/transform/transform_convert_armature.c
@@ -131,12 +131,12 @@ static void autokeyframe_pose(
ListBase dsources = {NULL, NULL};
- /* add datasource override for the camera object */
+ /* Add data-source override for the camera object. */
ANIM_relative_keyingset_add_source(&dsources, id, &RNA_PoseBone, pchan);
/* only insert into active keyingset? */
if (IS_AUTOKEY_FLAG(scene, ONLYKEYINGSET) && (active_ks)) {
- /* run the active Keying Set on the current datasource */
+ /* Run the active Keying Set on the current data-source. */
ANIM_apply_keyingset(
C, &dsources, NULL, active_ks, MODIFYKEY_MODE_INSERT, anim_eval_context.eval_time);
}
diff --git a/source/blender/editors/transform/transform_convert_object.c b/source/blender/editors/transform/transform_convert_object.c
index ee6cb391fdc..bcbac009948 100644
--- a/source/blender/editors/transform/transform_convert_object.c
+++ b/source/blender/editors/transform/transform_convert_object.c
@@ -749,7 +749,7 @@ static void autokeyframe_object(
/* Get flags used for inserting keyframes. */
flag = ANIM_get_keyframing_flags(scene, true);
- /* add datasource override for the object */
+ /* Add data-source override for the object. */
ANIM_relative_keyingset_add_source(&dsources, id, NULL, NULL);
if (IS_AUTOKEY_FLAG(scene, ONLYKEYINGSET) && (active_ks)) {
diff --git a/source/blender/editors/transform/transform_snap_animation.c b/source/blender/editors/transform/transform_snap_animation.c
index be7fc65a6c2..08335924ddf 100644
--- a/source/blender/editors/transform/transform_snap_animation.c
+++ b/source/blender/editors/transform/transform_snap_animation.c
@@ -35,14 +35,14 @@
#include "transform_snap.h"
/* -------------------------------------------------------------------- */
-/** \name Snappint in Anim Editors
+/** \name Snapping in Anim Editors
* \{ */
/**
* This function returns the snapping 'mode' for Animation Editors only.
* We cannot use the standard snapping due to NLA-strip scaling complexities.
*
- * TODO: these modifier checks should be key-mappable.
+ * TODO: these modifier checks should be accessible from the key-map.
*/
short getAnimEdit_SnapMode(TransInfo *t)
{
diff --git a/source/blender/editors/util/numinput.c b/source/blender/editors/util/numinput.c
index 823837e2a42..d0eed6a6eb1 100644
--- a/source/blender/editors/util/numinput.c
+++ b/source/blender/editors/util/numinput.c
@@ -178,7 +178,7 @@ void outputNumInput(NumInput *n, char *str, UnitSettings *unit_settings)
}
/* We might have cut some multi-bytes utf8 chars
* (e.g. trailing '°' of degrees values can become only 'A')... */
- BLI_utf8_invalid_strip(&str[j * ln], strlen(&str[j * ln]));
+ BLI_str_utf8_invalid_strip(&str[j * ln], strlen(&str[j * ln]));
}
}
diff --git a/source/blender/functions/CMakeLists.txt b/source/blender/functions/CMakeLists.txt
index 809294ad274..f8d2acc74a8 100644
--- a/source/blender/functions/CMakeLists.txt
+++ b/source/blender/functions/CMakeLists.txt
@@ -33,9 +33,6 @@ set(SRC
intern/generic_virtual_vector_array.cc
intern/multi_function.cc
intern/multi_function_builder.cc
- intern/multi_function_network.cc
- intern/multi_function_network_evaluation.cc
- intern/multi_function_network_optimization.cc
FN_cpp_type.hh
FN_cpp_type_make.hh
@@ -49,9 +46,6 @@ set(SRC
FN_multi_function_builder.hh
FN_multi_function_context.hh
FN_multi_function_data_type.hh
- FN_multi_function_network.hh
- FN_multi_function_network_evaluation.hh
- FN_multi_function_network_optimization.hh
FN_multi_function_param_type.hh
FN_multi_function_params.hh
FN_multi_function_signature.hh
@@ -68,7 +62,6 @@ if(WITH_GTESTS)
tests/FN_cpp_type_test.cc
tests/FN_generic_span_test.cc
tests/FN_generic_vector_array_test.cc
- tests/FN_multi_function_network_test.cc
tests/FN_multi_function_test.cc
)
set(TEST_LIB
diff --git a/source/blender/functions/FN_generic_virtual_array.hh b/source/blender/functions/FN_generic_virtual_array.hh
index e2ba603415b..d58fb263d7e 100644
--- a/source/blender/functions/FN_generic_virtual_array.hh
+++ b/source/blender/functions/FN_generic_virtual_array.hh
@@ -130,7 +130,7 @@ class GVArray {
}
/* Same as `get_internal_single`, but `r_value` points to initialized memory. */
- void get_single_to_uninitialized(void *r_value) const
+ void get_internal_single_to_uninitialized(void *r_value) const
{
type_->default_construct(r_value);
this->get_internal_single(r_value);
diff --git a/source/blender/functions/FN_multi_function_network.hh b/source/blender/functions/FN_multi_function_network.hh
deleted file mode 100644
index b303589106a..00000000000
--- a/source/blender/functions/FN_multi_function_network.hh
+++ /dev/null
@@ -1,536 +0,0 @@
-/*
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-
-#pragma once
-
-/** \file
- * \ingroup fn
- *
- * A multi-function network (`MFNetwork`) allows you to connect multiple multi-functions. The
- * `MFNetworkEvaluator` is a multi-function that wraps an entire network into a new multi-function
- * (which can be used in another network and so on).
- *
- * A MFNetwork is a graph data structure with two kinds of nodes:
- * - MFFunctionNode: Represents a multi-function. Its input and output sockets correspond to
- * parameters of the referenced multi-function.
- * - MFDummyNode: Does not reference a multi-function. Instead it just has sockets that can be
- * used to represent node group inputs and outputs.
- *
- * Links represent data flow. Unlinked input sockets have no value. In order to execute a function
- * node, all its inputs have to be connected to something.
- *
- * Links are only allowed between sockets with the exact same MFDataType. There are no implicit
- * conversions.
- *
- * Every input and output parameter of a multi-function corresponds to exactly one input or output
- * socket respectively. A multiple parameter belongs to exactly one input AND one output socket.
- *
- * There is an .to_dot() method that generates a graph in dot format for debugging purposes.
- */
-
-#include "FN_multi_function.hh"
-
-#include "BLI_vector_set.hh"
-
-namespace blender::fn {
-
-class MFNode;
-class MFFunctionNode;
-class MFDummyNode;
-class MFSocket;
-class MFInputSocket;
-class MFOutputSocket;
-class MFNetwork;
-
-class MFNode : NonCopyable, NonMovable {
- protected:
- MFNetwork *network_;
- Span<MFInputSocket *> inputs_;
- Span<MFOutputSocket *> outputs_;
- bool is_dummy_;
- int id_;
-
- friend MFNetwork;
-
- public:
- StringRefNull name() const;
-
- int id() const;
-
- MFNetwork &network();
- const MFNetwork &network() const;
-
- bool is_dummy() const;
- bool is_function() const;
-
- MFDummyNode &as_dummy();
- const MFDummyNode &as_dummy() const;
-
- MFFunctionNode &as_function();
- const MFFunctionNode &as_function() const;
-
- MFInputSocket &input(int index);
- const MFInputSocket &input(int index) const;
-
- MFOutputSocket &output(int index);
- const MFOutputSocket &output(int index) const;
-
- Span<MFInputSocket *> inputs();
- Span<const MFInputSocket *> inputs() const;
-
- Span<MFOutputSocket *> outputs();
- Span<const MFOutputSocket *> outputs() const;
-
- bool has_unlinked_inputs() const;
-
- private:
- void destruct_sockets();
-};
-
-class MFFunctionNode : public MFNode {
- private:
- const MultiFunction *function_;
- Span<int> input_param_indices_;
- Span<int> output_param_indices_;
-
- friend MFNetwork;
-
- public:
- StringRefNull name() const;
-
- const MultiFunction &function() const;
-
- const MFInputSocket &input_for_param(int param_index) const;
- const MFOutputSocket &output_for_param(int param_index) const;
-};
-
-class MFDummyNode : public MFNode {
- protected:
- StringRefNull name_;
- MutableSpan<StringRefNull> input_names_;
- MutableSpan<StringRefNull> output_names_;
-
- friend MFNetwork;
-
- public:
- StringRefNull name() const;
-
- Span<StringRefNull> input_names() const;
- Span<StringRefNull> output_names() const;
-};
-
-class MFSocket : NonCopyable, NonMovable {
- protected:
- MFNode *node_;
- bool is_output_;
- int index_;
- MFDataType data_type_;
- int id_;
- StringRefNull name_;
-
- friend MFNetwork;
-
- public:
- StringRefNull name() const;
-
- int id() const;
- int index() const;
-
- const MFDataType &data_type() const;
-
- MFNode &node();
- const MFNode &node() const;
-
- bool is_input() const;
- bool is_output() const;
-
- MFInputSocket &as_input();
- const MFInputSocket &as_input() const;
-
- MFOutputSocket &as_output();
- const MFOutputSocket &as_output() const;
-};
-
-class MFInputSocket : public MFSocket {
- private:
- MFOutputSocket *origin_;
-
- friend MFNetwork;
-
- public:
- MFOutputSocket *origin();
- const MFOutputSocket *origin() const;
-};
-
-class MFOutputSocket : public MFSocket {
- private:
- Vector<MFInputSocket *, 1> targets_;
-
- friend MFNetwork;
-
- public:
- Span<MFInputSocket *> targets();
- Span<const MFInputSocket *> targets() const;
-};
-
-class MFNetwork : NonCopyable, NonMovable {
- private:
- LinearAllocator<> allocator_;
-
- VectorSet<MFFunctionNode *> function_nodes_;
- VectorSet<MFDummyNode *> dummy_nodes_;
-
- Vector<MFNode *> node_or_null_by_id_;
- Vector<MFSocket *> socket_or_null_by_id_;
-
- public:
- MFNetwork() = default;
- ~MFNetwork();
-
- MFFunctionNode &add_function(const MultiFunction &function);
- MFDummyNode &add_dummy(StringRef name,
- Span<MFDataType> input_types,
- Span<MFDataType> output_types,
- Span<StringRef> input_names,
- Span<StringRef> output_names);
- void add_link(MFOutputSocket &from, MFInputSocket &to);
-
- MFOutputSocket &add_input(StringRef name, MFDataType data_type);
- MFInputSocket &add_output(StringRef name, MFDataType data_type);
-
- void relink(MFOutputSocket &old_output, MFOutputSocket &new_output);
-
- void remove(MFNode &node);
- void remove(Span<MFNode *> nodes);
-
- int socket_id_amount() const;
- int node_id_amount() const;
-
- Span<MFDummyNode *> dummy_nodes();
- Span<MFFunctionNode *> function_nodes();
-
- MFNode *node_or_null_by_id(int id);
- const MFNode *node_or_null_by_id(int id) const;
-
- MFSocket *socket_or_null_by_id(int id);
- const MFSocket *socket_or_null_by_id(int id) const;
-
- void find_dependencies(Span<const MFInputSocket *> sockets,
- VectorSet<const MFOutputSocket *> &r_dummy_sockets,
- VectorSet<const MFInputSocket *> &r_unlinked_inputs) const;
-
- bool have_dummy_or_unlinked_dependencies(Span<const MFInputSocket *> sockets) const;
-
- std::string to_dot(Span<const MFNode *> marked_nodes = {}) const;
-};
-
-/* --------------------------------------------------------------------
- * MFNode inline methods.
- */
-
-inline StringRefNull MFNode::name() const
-{
- if (is_dummy_) {
- return this->as_dummy().name();
- }
- else {
- return this->as_function().name();
- }
-}
-
-inline int MFNode::id() const
-{
- return id_;
-}
-
-inline MFNetwork &MFNode::network()
-{
- return *network_;
-}
-
-inline const MFNetwork &MFNode::network() const
-{
- return *network_;
-}
-
-inline bool MFNode::is_dummy() const
-{
- return is_dummy_;
-}
-
-inline bool MFNode::is_function() const
-{
- return !is_dummy_;
-}
-
-inline MFDummyNode &MFNode::as_dummy()
-{
- BLI_assert(is_dummy_);
- return static_cast<MFDummyNode &>(*this);
-}
-
-inline const MFDummyNode &MFNode::as_dummy() const
-{
- BLI_assert(is_dummy_);
- return static_cast<const MFDummyNode &>(*this);
-}
-
-inline MFFunctionNode &MFNode::as_function()
-{
- BLI_assert(!is_dummy_);
- return static_cast<MFFunctionNode &>(*this);
-}
-
-inline const MFFunctionNode &MFNode::as_function() const
-{
- BLI_assert(!is_dummy_);
- return static_cast<const MFFunctionNode &>(*this);
-}
-
-inline MFInputSocket &MFNode::input(int index)
-{
- return *inputs_[index];
-}
-
-inline const MFInputSocket &MFNode::input(int index) const
-{
- return *inputs_[index];
-}
-
-inline MFOutputSocket &MFNode::output(int index)
-{
- return *outputs_[index];
-}
-
-inline const MFOutputSocket &MFNode::output(int index) const
-{
- return *outputs_[index];
-}
-
-inline Span<MFInputSocket *> MFNode::inputs()
-{
- return inputs_;
-}
-
-inline Span<const MFInputSocket *> MFNode::inputs() const
-{
- return inputs_;
-}
-
-inline Span<MFOutputSocket *> MFNode::outputs()
-{
- return outputs_;
-}
-
-inline Span<const MFOutputSocket *> MFNode::outputs() const
-{
- return outputs_;
-}
-
-inline bool MFNode::has_unlinked_inputs() const
-{
- for (const MFInputSocket *socket : inputs_) {
- if (socket->origin() == nullptr) {
- return true;
- }
- }
- return false;
-}
-
-/* --------------------------------------------------------------------
- * MFFunctionNode inline methods.
- */
-
-inline StringRefNull MFFunctionNode::name() const
-{
- return function_->name();
-}
-
-inline const MultiFunction &MFFunctionNode::function() const
-{
- return *function_;
-}
-
-inline const MFInputSocket &MFFunctionNode::input_for_param(int param_index) const
-{
- return this->input(input_param_indices_.first_index(param_index));
-}
-
-inline const MFOutputSocket &MFFunctionNode::output_for_param(int param_index) const
-{
- return this->output(output_param_indices_.first_index(param_index));
-}
-
-/* --------------------------------------------------------------------
- * MFDummyNode inline methods.
- */
-
-inline StringRefNull MFDummyNode::name() const
-{
- return name_;
-}
-
-inline Span<StringRefNull> MFDummyNode::input_names() const
-{
- return input_names_;
-}
-
-inline Span<StringRefNull> MFDummyNode::output_names() const
-{
- return output_names_;
-}
-
-/* --------------------------------------------------------------------
- * MFSocket inline methods.
- */
-
-inline StringRefNull MFSocket::name() const
-{
- return name_;
-}
-
-inline int MFSocket::id() const
-{
- return id_;
-}
-
-inline int MFSocket::index() const
-{
- return index_;
-}
-
-inline const MFDataType &MFSocket::data_type() const
-{
- return data_type_;
-}
-
-inline MFNode &MFSocket::node()
-{
- return *node_;
-}
-
-inline const MFNode &MFSocket::node() const
-{
- return *node_;
-}
-
-inline bool MFSocket::is_input() const
-{
- return !is_output_;
-}
-
-inline bool MFSocket::is_output() const
-{
- return is_output_;
-}
-
-inline MFInputSocket &MFSocket::as_input()
-{
- BLI_assert(this->is_input());
- return static_cast<MFInputSocket &>(*this);
-}
-
-inline const MFInputSocket &MFSocket::as_input() const
-{
- BLI_assert(this->is_input());
- return static_cast<const MFInputSocket &>(*this);
-}
-
-inline MFOutputSocket &MFSocket::as_output()
-{
- BLI_assert(this->is_output());
- return static_cast<MFOutputSocket &>(*this);
-}
-
-inline const MFOutputSocket &MFSocket::as_output() const
-{
- BLI_assert(this->is_output());
- return static_cast<const MFOutputSocket &>(*this);
-}
-
-/* --------------------------------------------------------------------
- * MFInputSocket inline methods.
- */
-
-inline MFOutputSocket *MFInputSocket::origin()
-{
- return origin_;
-}
-
-inline const MFOutputSocket *MFInputSocket::origin() const
-{
- return origin_;
-}
-
-/* --------------------------------------------------------------------
- * MFOutputSocket inline methods.
- */
-
-inline Span<MFInputSocket *> MFOutputSocket::targets()
-{
- return targets_;
-}
-
-inline Span<const MFInputSocket *> MFOutputSocket::targets() const
-{
- return targets_;
-}
-
-/* --------------------------------------------------------------------
- * MFNetwork inline methods.
- */
-
-inline Span<MFDummyNode *> MFNetwork::dummy_nodes()
-{
- return dummy_nodes_;
-}
-
-inline Span<MFFunctionNode *> MFNetwork::function_nodes()
-{
- return function_nodes_;
-}
-
-inline MFNode *MFNetwork::node_or_null_by_id(int id)
-{
- return node_or_null_by_id_[id];
-}
-
-inline const MFNode *MFNetwork::node_or_null_by_id(int id) const
-{
- return node_or_null_by_id_[id];
-}
-
-inline MFSocket *MFNetwork::socket_or_null_by_id(int id)
-{
- return socket_or_null_by_id_[id];
-}
-
-inline const MFSocket *MFNetwork::socket_or_null_by_id(int id) const
-{
- return socket_or_null_by_id_[id];
-}
-
-inline int MFNetwork::socket_id_amount() const
-{
- return socket_or_null_by_id_.size();
-}
-
-inline int MFNetwork::node_id_amount() const
-{
- return node_or_null_by_id_.size();
-}
-
-} // namespace blender::fn
diff --git a/source/blender/functions/FN_multi_function_network_evaluation.hh b/source/blender/functions/FN_multi_function_network_evaluation.hh
deleted file mode 100644
index 17cffa406f7..00000000000
--- a/source/blender/functions/FN_multi_function_network_evaluation.hh
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-
-#pragma once
-
-/** \file
- * \ingroup fn
- */
-
-#include "FN_multi_function_network.hh"
-
-namespace blender::fn {
-
-class MFNetworkEvaluationStorage;
-
-class MFNetworkEvaluator : public MultiFunction {
- private:
- MFSignature signature_;
- Vector<const MFOutputSocket *> inputs_;
- Vector<const MFInputSocket *> outputs_;
-
- public:
- MFNetworkEvaluator(Vector<const MFOutputSocket *> inputs, Vector<const MFInputSocket *> outputs);
-
- void call(IndexMask mask, MFParams params, MFContext context) const override;
-
- private:
- using Storage = MFNetworkEvaluationStorage;
-
- void copy_inputs_to_storage(MFParams params, Storage &storage) const;
- void copy_outputs_to_storage(
- MFParams params,
- Storage &storage,
- Vector<const MFInputSocket *> &outputs_to_initialize_in_the_end) const;
-
- void evaluate_network_to_compute_outputs(MFContext &global_context, Storage &storage) const;
-
- void evaluate_function(MFContext &global_context,
- const MFFunctionNode &function_node,
- Storage &storage) const;
-
- bool can_do_single_value_evaluation(const MFFunctionNode &function_node, Storage &storage) const;
-
- void initialize_remaining_outputs(MFParams params,
- Storage &storage,
- Span<const MFInputSocket *> remaining_outputs) const;
-};
-
-} // namespace blender::fn
diff --git a/source/blender/functions/FN_multi_function_network_optimization.hh b/source/blender/functions/FN_multi_function_network_optimization.hh
deleted file mode 100644
index 96664fa368e..00000000000
--- a/source/blender/functions/FN_multi_function_network_optimization.hh
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-
-#pragma once
-
-#include "FN_multi_function_network.hh"
-
-#include "BLI_resource_scope.hh"
-
-namespace blender::fn::mf_network_optimization {
-
-void dead_node_removal(MFNetwork &network);
-void constant_folding(MFNetwork &network, ResourceScope &scope);
-void common_subnetwork_elimination(MFNetwork &network);
-
-} // namespace blender::fn::mf_network_optimization
diff --git a/source/blender/functions/FN_multi_function_signature.hh b/source/blender/functions/FN_multi_function_signature.hh
index 23309c9a5e6..d05948cc645 100644
--- a/source/blender/functions/FN_multi_function_signature.hh
+++ b/source/blender/functions/FN_multi_function_signature.hh
@@ -160,6 +160,21 @@ class MFSignatureBuilder {
}
}
+ void add(StringRef name, const MFParamType &param_type)
+ {
+ switch (param_type.interface_type()) {
+ case MFParamType::Input:
+ this->input(name, param_type.data_type());
+ break;
+ case MFParamType::Mutable:
+ this->mutable_(name, param_type.data_type());
+ break;
+ case MFParamType::Output:
+ this->output(name, param_type.data_type());
+ break;
+ }
+ }
+
/* Context */
/** This indicates that the function accesses the context. This disables optimizations that
diff --git a/source/blender/functions/intern/multi_function_network.cc b/source/blender/functions/intern/multi_function_network.cc
deleted file mode 100644
index b5c2c09a35a..00000000000
--- a/source/blender/functions/intern/multi_function_network.cc
+++ /dev/null
@@ -1,330 +0,0 @@
-/*
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-
-#include "BLI_dot_export.hh"
-#include "BLI_stack.hh"
-
-#include "FN_multi_function_network.hh"
-
-namespace blender::fn {
-
-MFNetwork::~MFNetwork()
-{
- for (MFFunctionNode *node : function_nodes_) {
- node->destruct_sockets();
- node->~MFFunctionNode();
- }
- for (MFDummyNode *node : dummy_nodes_) {
- node->destruct_sockets();
- node->~MFDummyNode();
- }
-}
-
-void MFNode::destruct_sockets()
-{
- for (MFInputSocket *socket : inputs_) {
- socket->~MFInputSocket();
- }
- for (MFOutputSocket *socket : outputs_) {
- socket->~MFOutputSocket();
- }
-}
-
-/**
- * Add a new function node to the network. The caller keeps the ownership of the function. The
- * function should not be freed before the network. A reference to the new node is returned. The
- * node is owned by the network.
- */
-MFFunctionNode &MFNetwork::add_function(const MultiFunction &function)
-{
- Vector<int, 16> input_param_indices, output_param_indices;
-
- for (int param_index : function.param_indices()) {
- switch (function.param_type(param_index).interface_type()) {
- case MFParamType::Input: {
- input_param_indices.append(param_index);
- break;
- }
- case MFParamType::Output: {
- output_param_indices.append(param_index);
- break;
- }
- case MFParamType::Mutable: {
- input_param_indices.append(param_index);
- output_param_indices.append(param_index);
- break;
- }
- }
- }
-
- MFFunctionNode &node = *allocator_.construct<MFFunctionNode>().release();
- function_nodes_.add_new(&node);
-
- node.network_ = this;
- node.is_dummy_ = false;
- node.id_ = node_or_null_by_id_.append_and_get_index(&node);
- node.function_ = &function;
- node.input_param_indices_ = allocator_.construct_array_copy<int>(input_param_indices);
- node.output_param_indices_ = allocator_.construct_array_copy<int>(output_param_indices);
-
- node.inputs_ = allocator_.construct_elements_and_pointer_array<MFInputSocket>(
- input_param_indices.size());
- node.outputs_ = allocator_.construct_elements_and_pointer_array<MFOutputSocket>(
- output_param_indices.size());
-
- for (int i : input_param_indices.index_range()) {
- int param_index = input_param_indices[i];
- MFParamType param = function.param_type(param_index);
- BLI_assert(param.is_input_or_mutable());
-
- MFInputSocket &socket = *node.inputs_[i];
- socket.data_type_ = param.data_type();
- socket.node_ = &node;
- socket.index_ = i;
- socket.is_output_ = false;
- socket.name_ = function.param_name(param_index);
- socket.origin_ = nullptr;
- socket.id_ = socket_or_null_by_id_.append_and_get_index(&socket);
- }
-
- for (int i : output_param_indices.index_range()) {
- int param_index = output_param_indices[i];
- MFParamType param = function.param_type(param_index);
- BLI_assert(param.is_output_or_mutable());
-
- MFOutputSocket &socket = *node.outputs_[i];
- socket.data_type_ = param.data_type();
- socket.node_ = &node;
- socket.index_ = i;
- socket.is_output_ = true;
- socket.name_ = function.param_name(param_index);
- socket.id_ = socket_or_null_by_id_.append_and_get_index(&socket);
- }
-
- return node;
-}
-
-/**
- * Add a dummy node with the given input and output sockets.
- */
-MFDummyNode &MFNetwork::add_dummy(StringRef name,
- Span<MFDataType> input_types,
- Span<MFDataType> output_types,
- Span<StringRef> input_names,
- Span<StringRef> output_names)
-{
- assert_same_size(input_types, input_names);
- assert_same_size(output_types, output_names);
-
- MFDummyNode &node = *allocator_.construct<MFDummyNode>().release();
- dummy_nodes_.add_new(&node);
-
- node.network_ = this;
- node.is_dummy_ = true;
- node.name_ = allocator_.copy_string(name);
- node.id_ = node_or_null_by_id_.append_and_get_index(&node);
-
- node.inputs_ = allocator_.construct_elements_and_pointer_array<MFInputSocket>(
- input_types.size());
- node.outputs_ = allocator_.construct_elements_and_pointer_array<MFOutputSocket>(
- output_types.size());
-
- node.input_names_ = allocator_.allocate_array<StringRefNull>(input_types.size());
- node.output_names_ = allocator_.allocate_array<StringRefNull>(output_types.size());
-
- for (int i : input_types.index_range()) {
- MFInputSocket &socket = *node.inputs_[i];
- socket.data_type_ = input_types[i];
- socket.node_ = &node;
- socket.index_ = i;
- socket.is_output_ = false;
- socket.name_ = allocator_.copy_string(input_names[i]);
- socket.id_ = socket_or_null_by_id_.append_and_get_index(&socket);
- node.input_names_[i] = socket.name_;
- }
-
- for (int i : output_types.index_range()) {
- MFOutputSocket &socket = *node.outputs_[i];
- socket.data_type_ = output_types[i];
- socket.node_ = &node;
- socket.index_ = i;
- socket.is_output_ = true;
- socket.name_ = allocator_.copy_string(output_names[i]);
- socket.id_ = socket_or_null_by_id_.append_and_get_index(&socket);
- node.output_names_[i] = socket.name_;
- }
-
- return node;
-}
-
-/**
- * Connect two sockets. This invokes undefined behavior if the sockets belong to different
- * networks, the sockets have a different data type, or the `to` socket is connected to something
- * else already.
- */
-void MFNetwork::add_link(MFOutputSocket &from, MFInputSocket &to)
-{
- BLI_assert(to.origin_ == nullptr);
- BLI_assert(from.node_->network_ == to.node_->network_);
- BLI_assert(from.data_type_ == to.data_type_);
- from.targets_.append(&to);
- to.origin_ = &from;
-}
-
-MFOutputSocket &MFNetwork::add_input(StringRef name, MFDataType data_type)
-{
- return this->add_dummy(name, {}, {data_type}, {}, {"Value"}).output(0);
-}
-
-MFInputSocket &MFNetwork::add_output(StringRef name, MFDataType data_type)
-{
- return this->add_dummy(name, {data_type}, {}, {"Value"}, {}).input(0);
-}
-
-void MFNetwork::relink(MFOutputSocket &old_output, MFOutputSocket &new_output)
-{
- BLI_assert(&old_output != &new_output);
- BLI_assert(old_output.data_type_ == new_output.data_type_);
- for (MFInputSocket *input : old_output.targets()) {
- input->origin_ = &new_output;
- }
- new_output.targets_.extend(old_output.targets_);
- old_output.targets_.clear();
-}
-
-void MFNetwork::remove(MFNode &node)
-{
- for (MFInputSocket *socket : node.inputs_) {
- if (socket->origin_ != nullptr) {
- socket->origin_->targets_.remove_first_occurrence_and_reorder(socket);
- }
- socket_or_null_by_id_[socket->id_] = nullptr;
- }
- for (MFOutputSocket *socket : node.outputs_) {
- for (MFInputSocket *other : socket->targets_) {
- other->origin_ = nullptr;
- }
- socket_or_null_by_id_[socket->id_] = nullptr;
- }
- node.destruct_sockets();
- if (node.is_dummy()) {
- MFDummyNode &dummy_node = node.as_dummy();
- dummy_node.~MFDummyNode();
- dummy_nodes_.remove_contained(&dummy_node);
- }
- else {
- MFFunctionNode &function_node = node.as_function();
- function_node.~MFFunctionNode();
- function_nodes_.remove_contained(&function_node);
- }
- node_or_null_by_id_[node.id_] = nullptr;
-}
-
-void MFNetwork::remove(Span<MFNode *> nodes)
-{
- for (MFNode *node : nodes) {
- this->remove(*node);
- }
-}
-
-void MFNetwork::find_dependencies(Span<const MFInputSocket *> sockets,
- VectorSet<const MFOutputSocket *> &r_dummy_sockets,
- VectorSet<const MFInputSocket *> &r_unlinked_inputs) const
-{
- Set<const MFNode *> visited_nodes;
- Stack<const MFInputSocket *> sockets_to_check;
- sockets_to_check.push_multiple(sockets);
-
- while (!sockets_to_check.is_empty()) {
- const MFInputSocket &socket = *sockets_to_check.pop();
- const MFOutputSocket *origin_socket = socket.origin();
- if (origin_socket == nullptr) {
- r_unlinked_inputs.add(&socket);
- continue;
- }
-
- const MFNode &origin_node = origin_socket->node();
-
- if (origin_node.is_dummy()) {
- r_dummy_sockets.add(origin_socket);
- continue;
- }
-
- if (visited_nodes.add(&origin_node)) {
- sockets_to_check.push_multiple(origin_node.inputs());
- }
- }
-}
-
-bool MFNetwork::have_dummy_or_unlinked_dependencies(Span<const MFInputSocket *> sockets) const
-{
- VectorSet<const MFOutputSocket *> dummy_sockets;
- VectorSet<const MFInputSocket *> unlinked_inputs;
- this->find_dependencies(sockets, dummy_sockets, unlinked_inputs);
- return dummy_sockets.size() + unlinked_inputs.size() > 0;
-}
-
-std::string MFNetwork::to_dot(Span<const MFNode *> marked_nodes) const
-{
- dot::DirectedGraph digraph;
- digraph.set_rankdir(dot::Attr_rankdir::LeftToRight);
-
- Map<const MFNode *, dot::NodeWithSocketsRef> dot_nodes;
-
- Vector<const MFNode *> all_nodes;
- all_nodes.extend(function_nodes_.as_span().cast<const MFNode *>());
- all_nodes.extend(dummy_nodes_.as_span().cast<const MFNode *>());
-
- for (const MFNode *node : all_nodes) {
- dot::Node &dot_node = digraph.new_node("");
-
- Vector<std::string> input_names, output_names;
- for (const MFInputSocket *socket : node->inputs_) {
- input_names.append(socket->name() + "(" + socket->data_type().to_string() + ")");
- }
- for (const MFOutputSocket *socket : node->outputs_) {
- output_names.append(socket->name() + " (" + socket->data_type().to_string() + ")");
- }
-
- dot::NodeWithSocketsRef dot_node_ref{dot_node, node->name(), input_names, output_names};
- dot_nodes.add_new(node, dot_node_ref);
- }
-
- for (const MFDummyNode *node : dummy_nodes_) {
- dot_nodes.lookup(node).node().set_background_color("#77EE77");
- }
- for (const MFNode *node : marked_nodes) {
- dot_nodes.lookup(node).node().set_background_color("#7777EE");
- }
-
- for (const MFNode *to_node : all_nodes) {
- dot::NodeWithSocketsRef to_dot_node = dot_nodes.lookup(to_node);
-
- for (const MFInputSocket *to_socket : to_node->inputs_) {
- const MFOutputSocket *from_socket = to_socket->origin_;
- if (from_socket != nullptr) {
- const MFNode *from_node = from_socket->node_;
- dot::NodeWithSocketsRef from_dot_node = dot_nodes.lookup(from_node);
- digraph.new_edge(from_dot_node.output(from_socket->index_),
- to_dot_node.input(to_socket->index_));
- }
- }
- }
-
- return digraph.to_dot_string();
-}
-
-} // namespace blender::fn
diff --git a/source/blender/functions/intern/multi_function_network_evaluation.cc b/source/blender/functions/intern/multi_function_network_evaluation.cc
deleted file mode 100644
index 9a0cb0c35ce..00000000000
--- a/source/blender/functions/intern/multi_function_network_evaluation.cc
+++ /dev/null
@@ -1,1083 +0,0 @@
-/*
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-
-/** \file
- * \ingroup fn
- *
- * The `MFNetworkEvaluator` class is a multi-function that consists of potentially many smaller
- * multi-functions. When called, it traverses the underlying MFNetwork and executes the required
- * function nodes.
- *
- * There are many possible approaches to evaluate a function network. The approach implemented
- * below has the following features:
- * - It does not use recursion. Those could become problematic with long node chains.
- * - It can handle all existing parameter types (including mutable parameters).
- * - Avoids data copies in many cases.
- * - Every node is executed at most once.
- * - Can compute sub-functions on a single element, when the result is the same for all elements.
- *
- * Possible improvements:
- * - Cache and reuse buffers.
- * - Use "deepest depth first" heuristic to decide which order the inputs of a node should be
- * computed. This reduces the number of required temporary buffers when they are reused.
- */
-
-#include "FN_multi_function_network_evaluation.hh"
-
-#include "BLI_resource_scope.hh"
-#include "BLI_stack.hh"
-
-namespace blender::fn {
-
-struct Value;
-
-/**
- * This keeps track of all the values that flow through the multi-function network. Therefore it
- * maintains a mapping between output sockets and their corresponding values. Every `value`
- * references some memory, that is owned either by the caller or this storage.
- *
- * A value can be owned by different sockets over time to avoid unnecessary copies.
- */
-class MFNetworkEvaluationStorage {
- private:
- LinearAllocator<> allocator_;
- IndexMask mask_;
- Array<Value *> value_per_output_id_;
- int64_t min_array_size_;
-
- public:
- MFNetworkEvaluationStorage(IndexMask mask, int socket_id_amount);
- ~MFNetworkEvaluationStorage();
-
- /* Add the values that have been provided by the caller of the multi-function network. */
- void add_single_input_from_caller(const MFOutputSocket &socket, const GVArray &virtual_array);
- void add_vector_input_from_caller(const MFOutputSocket &socket,
- const GVVectorArray &virtual_vector_array);
- void add_single_output_from_caller(const MFOutputSocket &socket, GMutableSpan span);
- void add_vector_output_from_caller(const MFOutputSocket &socket, GVectorArray &vector_array);
-
- /* Get input buffers for function node evaluations. */
- const GVArray &get_single_input__full(const MFInputSocket &socket, ResourceScope &scope);
- const GVArray &get_single_input__single(const MFInputSocket &socket, ResourceScope &scope);
- const GVVectorArray &get_vector_input__full(const MFInputSocket &socket, ResourceScope &scope);
- const GVVectorArray &get_vector_input__single(const MFInputSocket &socket, ResourceScope &scope);
-
- /* Get output buffers for function node evaluations. */
- GMutableSpan get_single_output__full(const MFOutputSocket &socket);
- GMutableSpan get_single_output__single(const MFOutputSocket &socket);
- GVectorArray &get_vector_output__full(const MFOutputSocket &socket);
- GVectorArray &get_vector_output__single(const MFOutputSocket &socket);
-
- /* Get mutable buffers for function node evaluations. */
- GMutableSpan get_mutable_single__full(const MFInputSocket &input,
- const MFOutputSocket &output,
- ResourceScope &scope);
- GMutableSpan get_mutable_single__single(const MFInputSocket &input,
- const MFOutputSocket &output,
- ResourceScope &scope);
- GVectorArray &get_mutable_vector__full(const MFInputSocket &input,
- const MFOutputSocket &output,
- ResourceScope &scope);
- GVectorArray &get_mutable_vector__single(const MFInputSocket &input,
- const MFOutputSocket &output,
- ResourceScope &scope);
-
- /* Mark a node as being done with evaluation. This might free temporary buffers that are no
- * longer needed. */
- void finish_node(const MFFunctionNode &node);
- void finish_output_socket(const MFOutputSocket &socket);
- void finish_input_socket(const MFInputSocket &socket);
-
- IndexMask mask() const;
- bool socket_is_computed(const MFOutputSocket &socket);
- bool is_same_value_for_every_index(const MFOutputSocket &socket);
- bool socket_has_buffer_for_output(const MFOutputSocket &socket);
-};
-
-MFNetworkEvaluator::MFNetworkEvaluator(Vector<const MFOutputSocket *> inputs,
- Vector<const MFInputSocket *> outputs)
- : inputs_(std::move(inputs)), outputs_(std::move(outputs))
-{
- BLI_assert(outputs_.size() > 0);
- MFSignatureBuilder signature{"Function Tree"};
-
- for (const MFOutputSocket *socket : inputs_) {
- BLI_assert(socket->node().is_dummy());
-
- MFDataType type = socket->data_type();
- switch (type.category()) {
- case MFDataType::Single:
- signature.single_input(socket->name(), type.single_type());
- break;
- case MFDataType::Vector:
- signature.vector_input(socket->name(), type.vector_base_type());
- break;
- }
- }
-
- for (const MFInputSocket *socket : outputs_) {
- BLI_assert(socket->node().is_dummy());
-
- MFDataType type = socket->data_type();
- switch (type.category()) {
- case MFDataType::Single:
- signature.single_output(socket->name(), type.single_type());
- break;
- case MFDataType::Vector:
- signature.vector_output(socket->name(), type.vector_base_type());
- break;
- }
- }
-
- signature_ = signature.build();
- this->set_signature(&signature_);
-}
-
-void MFNetworkEvaluator::call(IndexMask mask, MFParams params, MFContext context) const
-{
- if (mask.size() == 0) {
- return;
- }
-
- const MFNetwork &network = outputs_[0]->node().network();
- Storage storage(mask, network.socket_id_amount());
-
- Vector<const MFInputSocket *> outputs_to_initialize_in_the_end;
-
- this->copy_inputs_to_storage(params, storage);
- this->copy_outputs_to_storage(params, storage, outputs_to_initialize_in_the_end);
- this->evaluate_network_to_compute_outputs(context, storage);
- this->initialize_remaining_outputs(params, storage, outputs_to_initialize_in_the_end);
-}
-
-BLI_NOINLINE void MFNetworkEvaluator::copy_inputs_to_storage(MFParams params,
- Storage &storage) const
-{
- for (int input_index : inputs_.index_range()) {
- int param_index = input_index + 0;
- const MFOutputSocket &socket = *inputs_[input_index];
- switch (socket.data_type().category()) {
- case MFDataType::Single: {
- const GVArray &input_list = params.readonly_single_input(param_index);
- storage.add_single_input_from_caller(socket, input_list);
- break;
- }
- case MFDataType::Vector: {
- const GVVectorArray &input_list_list = params.readonly_vector_input(param_index);
- storage.add_vector_input_from_caller(socket, input_list_list);
- break;
- }
- }
- }
-}
-
-BLI_NOINLINE void MFNetworkEvaluator::copy_outputs_to_storage(
- MFParams params,
- Storage &storage,
- Vector<const MFInputSocket *> &outputs_to_initialize_in_the_end) const
-{
- for (int output_index : outputs_.index_range()) {
- int param_index = output_index + inputs_.size();
- const MFInputSocket &socket = *outputs_[output_index];
- const MFOutputSocket &origin = *socket.origin();
-
- if (origin.node().is_dummy()) {
- BLI_assert(inputs_.contains(&origin));
- /* Don't overwrite input buffers. */
- outputs_to_initialize_in_the_end.append(&socket);
- continue;
- }
-
- if (storage.socket_has_buffer_for_output(origin)) {
- /* When two outputs will be initialized to the same values. */
- outputs_to_initialize_in_the_end.append(&socket);
- continue;
- }
-
- switch (socket.data_type().category()) {
- case MFDataType::Single: {
- GMutableSpan span = params.uninitialized_single_output(param_index);
- storage.add_single_output_from_caller(origin, span);
- break;
- }
- case MFDataType::Vector: {
- GVectorArray &vector_array = params.vector_output(param_index);
- storage.add_vector_output_from_caller(origin, vector_array);
- break;
- }
- }
- }
-}
-
-BLI_NOINLINE void MFNetworkEvaluator::evaluate_network_to_compute_outputs(
- MFContext &global_context, Storage &storage) const
-{
- Stack<const MFOutputSocket *, 32> sockets_to_compute;
- for (const MFInputSocket *socket : outputs_) {
- sockets_to_compute.push(socket->origin());
- }
-
- /* This is the main loop that traverses the MFNetwork. */
- while (!sockets_to_compute.is_empty()) {
- const MFOutputSocket &socket = *sockets_to_compute.peek();
- const MFNode &node = socket.node();
-
- if (storage.socket_is_computed(socket)) {
- sockets_to_compute.pop();
- continue;
- }
-
- BLI_assert(node.is_function());
- BLI_assert(!node.has_unlinked_inputs());
- const MFFunctionNode &function_node = node.as_function();
-
- bool all_origins_are_computed = true;
- for (const MFInputSocket *input_socket : function_node.inputs()) {
- const MFOutputSocket *origin = input_socket->origin();
- if (origin != nullptr) {
- if (!storage.socket_is_computed(*origin)) {
- sockets_to_compute.push(origin);
- all_origins_are_computed = false;
- }
- }
- }
-
- if (all_origins_are_computed) {
- this->evaluate_function(global_context, function_node, storage);
- sockets_to_compute.pop();
- }
- }
-}
-
-BLI_NOINLINE void MFNetworkEvaluator::evaluate_function(MFContext &global_context,
- const MFFunctionNode &function_node,
- Storage &storage) const
-{
-
- const MultiFunction &function = function_node.function();
- // std::cout << "Function: " << function.name() << "\n";
-
- if (this->can_do_single_value_evaluation(function_node, storage)) {
- /* The function output would be the same for all elements. Therefore, it is enough to call the
- * function only on a single element. This can avoid many duplicate computations. */
- MFParamsBuilder params{function, 1};
- ResourceScope &scope = params.resource_scope();
-
- for (int param_index : function.param_indices()) {
- MFParamType param_type = function.param_type(param_index);
- switch (param_type.category()) {
- case MFParamType::SingleInput: {
- const MFInputSocket &socket = function_node.input_for_param(param_index);
- const GVArray &values = storage.get_single_input__single(socket, scope);
- params.add_readonly_single_input(values);
- break;
- }
- case MFParamType::VectorInput: {
- const MFInputSocket &socket = function_node.input_for_param(param_index);
- const GVVectorArray &values = storage.get_vector_input__single(socket, scope);
- params.add_readonly_vector_input(values);
- break;
- }
- case MFParamType::SingleOutput: {
- const MFOutputSocket &socket = function_node.output_for_param(param_index);
- GMutableSpan values = storage.get_single_output__single(socket);
- params.add_uninitialized_single_output(values);
- break;
- }
- case MFParamType::VectorOutput: {
- const MFOutputSocket &socket = function_node.output_for_param(param_index);
- GVectorArray &values = storage.get_vector_output__single(socket);
- params.add_vector_output(values);
- break;
- }
- case MFParamType::SingleMutable: {
- const MFInputSocket &input = function_node.input_for_param(param_index);
- const MFOutputSocket &output = function_node.output_for_param(param_index);
- GMutableSpan values = storage.get_mutable_single__single(input, output, scope);
- params.add_single_mutable(values);
- break;
- }
- case MFParamType::VectorMutable: {
- const MFInputSocket &input = function_node.input_for_param(param_index);
- const MFOutputSocket &output = function_node.output_for_param(param_index);
- GVectorArray &values = storage.get_mutable_vector__single(input, output, scope);
- params.add_vector_mutable(values);
- break;
- }
- }
- }
-
- function.call(IndexRange(1), params, global_context);
- }
- else {
- MFParamsBuilder params{function, storage.mask().min_array_size()};
- ResourceScope &scope = params.resource_scope();
-
- for (int param_index : function.param_indices()) {
- MFParamType param_type = function.param_type(param_index);
- switch (param_type.category()) {
- case MFParamType::SingleInput: {
- const MFInputSocket &socket = function_node.input_for_param(param_index);
- const GVArray &values = storage.get_single_input__full(socket, scope);
- params.add_readonly_single_input(values);
- break;
- }
- case MFParamType::VectorInput: {
- const MFInputSocket &socket = function_node.input_for_param(param_index);
- const GVVectorArray &values = storage.get_vector_input__full(socket, scope);
- params.add_readonly_vector_input(values);
- break;
- }
- case MFParamType::SingleOutput: {
- const MFOutputSocket &socket = function_node.output_for_param(param_index);
- GMutableSpan values = storage.get_single_output__full(socket);
- params.add_uninitialized_single_output(values);
- break;
- }
- case MFParamType::VectorOutput: {
- const MFOutputSocket &socket = function_node.output_for_param(param_index);
- GVectorArray &values = storage.get_vector_output__full(socket);
- params.add_vector_output(values);
- break;
- }
- case MFParamType::SingleMutable: {
- const MFInputSocket &input = function_node.input_for_param(param_index);
- const MFOutputSocket &output = function_node.output_for_param(param_index);
- GMutableSpan values = storage.get_mutable_single__full(input, output, scope);
- params.add_single_mutable(values);
- break;
- }
- case MFParamType::VectorMutable: {
- const MFInputSocket &input = function_node.input_for_param(param_index);
- const MFOutputSocket &output = function_node.output_for_param(param_index);
- GVectorArray &values = storage.get_mutable_vector__full(input, output, scope);
- params.add_vector_mutable(values);
- break;
- }
- }
- }
-
- function.call(storage.mask(), params, global_context);
- }
-
- storage.finish_node(function_node);
-}
-
-bool MFNetworkEvaluator::can_do_single_value_evaluation(const MFFunctionNode &function_node,
- Storage &storage) const
-{
- for (const MFInputSocket *socket : function_node.inputs()) {
- if (!storage.is_same_value_for_every_index(*socket->origin())) {
- return false;
- }
- }
- if (storage.mask().min_array_size() >= 1) {
- for (const MFOutputSocket *socket : function_node.outputs()) {
- if (storage.socket_has_buffer_for_output(*socket)) {
- return false;
- }
- }
- }
- return true;
-}
-
-BLI_NOINLINE void MFNetworkEvaluator::initialize_remaining_outputs(
- MFParams params, Storage &storage, Span<const MFInputSocket *> remaining_outputs) const
-{
- ResourceScope scope;
- for (const MFInputSocket *socket : remaining_outputs) {
- int param_index = inputs_.size() + outputs_.first_index_of(socket);
-
- switch (socket->data_type().category()) {
- case MFDataType::Single: {
- const GVArray &values = storage.get_single_input__full(*socket, scope);
- GMutableSpan output_values = params.uninitialized_single_output(param_index);
- values.materialize_to_uninitialized(storage.mask(), output_values.data());
- break;
- }
- case MFDataType::Vector: {
- const GVVectorArray &values = storage.get_vector_input__full(*socket, scope);
- GVectorArray &output_values = params.vector_output(param_index);
- output_values.extend(storage.mask(), values);
- break;
- }
- }
- }
-}
-
-/* -------------------------------------------------------------------- */
-/** \name Value Types
- * \{ */
-
-enum class ValueType {
- InputSingle,
- InputVector,
- OutputSingle,
- OutputVector,
- OwnSingle,
- OwnVector,
-};
-
-struct Value {
- ValueType type;
-
- Value(ValueType type) : type(type)
- {
- }
-};
-
-struct InputSingleValue : public Value {
- /** This virtual array has been provided by the code that called the multi-function network. */
- const GVArray &virtual_array;
-
- InputSingleValue(const GVArray &virtual_array)
- : Value(ValueType::InputSingle), virtual_array(virtual_array)
- {
- }
-};
-
-struct InputVectorValue : public Value {
- /** This virtual vector has been provided by the code that called the multi-function network. */
- const GVVectorArray &virtual_vector_array;
-
- InputVectorValue(const GVVectorArray &virtual_vector_array)
- : Value(ValueType::InputVector), virtual_vector_array(virtual_vector_array)
- {
- }
-};
-
-struct OutputValue : public Value {
- bool is_computed = false;
-
- OutputValue(ValueType type) : Value(type)
- {
- }
-};
-
-struct OutputSingleValue : public OutputValue {
- /** This span has been provided by the code that called the multi-function network. */
- GMutableSpan span;
-
- OutputSingleValue(GMutableSpan span) : OutputValue(ValueType::OutputSingle), span(span)
- {
- }
-};
-
-struct OutputVectorValue : public OutputValue {
- /** This vector array has been provided by the code that called the multi-function network. */
- GVectorArray *vector_array;
-
- OutputVectorValue(GVectorArray &vector_array)
- : OutputValue(ValueType::OutputVector), vector_array(&vector_array)
- {
- }
-};
-
-struct OwnSingleValue : public Value {
- /** This span has been allocated during the evaluation of the multi-function network and contains
- * intermediate data. It has to be freed once the network evaluation is finished. */
- GMutableSpan span;
- int max_remaining_users;
- bool is_single_allocated;
-
- OwnSingleValue(GMutableSpan span, int max_remaining_users, bool is_single_allocated)
- : Value(ValueType::OwnSingle),
- span(span),
- max_remaining_users(max_remaining_users),
- is_single_allocated(is_single_allocated)
- {
- }
-};
-
-struct OwnVectorValue : public Value {
- /** This vector array has been allocated during the evaluation of the multi-function network and
- * contains intermediate data. It has to be freed once the network evaluation is finished. */
- GVectorArray *vector_array;
- int max_remaining_users;
-
- OwnVectorValue(GVectorArray &vector_array, int max_remaining_users)
- : Value(ValueType::OwnVector),
- vector_array(&vector_array),
- max_remaining_users(max_remaining_users)
- {
- }
-};
-
-/** \} */
-
-/* -------------------------------------------------------------------- */
-/** \name Storage methods
- * \{ */
-
-MFNetworkEvaluationStorage::MFNetworkEvaluationStorage(IndexMask mask, int socket_id_amount)
- : mask_(mask),
- value_per_output_id_(socket_id_amount, nullptr),
- min_array_size_(mask.min_array_size())
-{
-}
-
-MFNetworkEvaluationStorage::~MFNetworkEvaluationStorage()
-{
- for (Value *any_value : value_per_output_id_) {
- if (any_value == nullptr) {
- continue;
- }
- if (any_value->type == ValueType::OwnSingle) {
- OwnSingleValue *value = static_cast<OwnSingleValue *>(any_value);
- GMutableSpan span = value->span;
- const CPPType &type = span.type();
- if (value->is_single_allocated) {
- type.destruct(span.data());
- }
- else {
- type.destruct_indices(span.data(), mask_);
- MEM_freeN(span.data());
- }
- }
- else if (any_value->type == ValueType::OwnVector) {
- OwnVectorValue *value = static_cast<OwnVectorValue *>(any_value);
- delete value->vector_array;
- }
- }
-}
-
-IndexMask MFNetworkEvaluationStorage::mask() const
-{
- return mask_;
-}
-
-bool MFNetworkEvaluationStorage::socket_is_computed(const MFOutputSocket &socket)
-{
- Value *any_value = value_per_output_id_[socket.id()];
- if (any_value == nullptr) {
- return false;
- }
- if (ELEM(any_value->type, ValueType::OutputSingle, ValueType::OutputVector)) {
- return static_cast<OutputValue *>(any_value)->is_computed;
- }
- return true;
-}
-
-bool MFNetworkEvaluationStorage::is_same_value_for_every_index(const MFOutputSocket &socket)
-{
- Value *any_value = value_per_output_id_[socket.id()];
- switch (any_value->type) {
- case ValueType::OwnSingle:
- return static_cast<OwnSingleValue *>(any_value)->span.size() == 1;
- case ValueType::OwnVector:
- return static_cast<OwnVectorValue *>(any_value)->vector_array->size() == 1;
- case ValueType::InputSingle:
- return static_cast<InputSingleValue *>(any_value)->virtual_array.is_single();
- case ValueType::InputVector:
- return static_cast<InputVectorValue *>(any_value)->virtual_vector_array.is_single_vector();
- case ValueType::OutputSingle:
- return static_cast<OutputSingleValue *>(any_value)->span.size() == 1;
- case ValueType::OutputVector:
- return static_cast<OutputVectorValue *>(any_value)->vector_array->size() == 1;
- }
- BLI_assert(false);
- return false;
-}
-
-bool MFNetworkEvaluationStorage::socket_has_buffer_for_output(const MFOutputSocket &socket)
-{
- Value *any_value = value_per_output_id_[socket.id()];
- if (any_value == nullptr) {
- return false;
- }
-
- BLI_assert(ELEM(any_value->type, ValueType::OutputSingle, ValueType::OutputVector));
- return true;
-}
-
-void MFNetworkEvaluationStorage::finish_node(const MFFunctionNode &node)
-{
- for (const MFInputSocket *socket : node.inputs()) {
- this->finish_input_socket(*socket);
- }
- for (const MFOutputSocket *socket : node.outputs()) {
- this->finish_output_socket(*socket);
- }
-}
-
-void MFNetworkEvaluationStorage::finish_output_socket(const MFOutputSocket &socket)
-{
- Value *any_value = value_per_output_id_[socket.id()];
- if (any_value == nullptr) {
- return;
- }
-
- if (ELEM(any_value->type, ValueType::OutputSingle, ValueType::OutputVector)) {
- static_cast<OutputValue *>(any_value)->is_computed = true;
- }
-}
-
-void MFNetworkEvaluationStorage::finish_input_socket(const MFInputSocket &socket)
-{
- const MFOutputSocket &origin = *socket.origin();
-
- Value *any_value = value_per_output_id_[origin.id()];
- if (any_value == nullptr) {
- /* Can happen when a value has been forward to the next node. */
- return;
- }
-
- switch (any_value->type) {
- case ValueType::InputSingle:
- case ValueType::OutputSingle:
- case ValueType::InputVector:
- case ValueType::OutputVector: {
- break;
- }
- case ValueType::OwnSingle: {
- OwnSingleValue *value = static_cast<OwnSingleValue *>(any_value);
- BLI_assert(value->max_remaining_users >= 1);
- value->max_remaining_users--;
- if (value->max_remaining_users == 0) {
- GMutableSpan span = value->span;
- const CPPType &type = span.type();
- if (value->is_single_allocated) {
- type.destruct(span.data());
- }
- else {
- type.destruct_indices(span.data(), mask_);
- MEM_freeN(span.data());
- }
- value_per_output_id_[origin.id()] = nullptr;
- }
- break;
- }
- case ValueType::OwnVector: {
- OwnVectorValue *value = static_cast<OwnVectorValue *>(any_value);
- BLI_assert(value->max_remaining_users >= 1);
- value->max_remaining_users--;
- if (value->max_remaining_users == 0) {
- delete value->vector_array;
- value_per_output_id_[origin.id()] = nullptr;
- }
- break;
- }
- }
-}
-
-void MFNetworkEvaluationStorage::add_single_input_from_caller(const MFOutputSocket &socket,
- const GVArray &virtual_array)
-{
- BLI_assert(value_per_output_id_[socket.id()] == nullptr);
- BLI_assert(virtual_array.size() >= min_array_size_);
-
- auto *value = allocator_.construct<InputSingleValue>(virtual_array).release();
- value_per_output_id_[socket.id()] = value;
-}
-
-void MFNetworkEvaluationStorage::add_vector_input_from_caller(
- const MFOutputSocket &socket, const GVVectorArray &virtual_vector_array)
-{
- BLI_assert(value_per_output_id_[socket.id()] == nullptr);
- BLI_assert(virtual_vector_array.size() >= min_array_size_);
-
- auto *value = allocator_.construct<InputVectorValue>(virtual_vector_array).release();
- value_per_output_id_[socket.id()] = value;
-}
-
-void MFNetworkEvaluationStorage::add_single_output_from_caller(const MFOutputSocket &socket,
- GMutableSpan span)
-{
- BLI_assert(value_per_output_id_[socket.id()] == nullptr);
- BLI_assert(span.size() >= min_array_size_);
-
- auto *value = allocator_.construct<OutputSingleValue>(span).release();
- value_per_output_id_[socket.id()] = value;
-}
-
-void MFNetworkEvaluationStorage::add_vector_output_from_caller(const MFOutputSocket &socket,
- GVectorArray &vector_array)
-{
- BLI_assert(value_per_output_id_[socket.id()] == nullptr);
- BLI_assert(vector_array.size() >= min_array_size_);
-
- auto *value = allocator_.construct<OutputVectorValue>(vector_array).release();
- value_per_output_id_[socket.id()] = value;
-}
-
-GMutableSpan MFNetworkEvaluationStorage::get_single_output__full(const MFOutputSocket &socket)
-{
- Value *any_value = value_per_output_id_[socket.id()];
- if (any_value == nullptr) {
- const CPPType &type = socket.data_type().single_type();
- void *buffer = MEM_mallocN_aligned(min_array_size_ * type.size(), type.alignment(), AT);
- GMutableSpan span(type, buffer, min_array_size_);
-
- auto *value =
- allocator_.construct<OwnSingleValue>(span, socket.targets().size(), false).release();
- value_per_output_id_[socket.id()] = value;
-
- return span;
- }
-
- BLI_assert(any_value->type == ValueType::OutputSingle);
- return static_cast<OutputSingleValue *>(any_value)->span;
-}
-
-GMutableSpan MFNetworkEvaluationStorage::get_single_output__single(const MFOutputSocket &socket)
-{
- Value *any_value = value_per_output_id_[socket.id()];
- if (any_value == nullptr) {
- const CPPType &type = socket.data_type().single_type();
- void *buffer = allocator_.allocate(type.size(), type.alignment());
- GMutableSpan span(type, buffer, 1);
-
- auto *value =
- allocator_.construct<OwnSingleValue>(span, socket.targets().size(), true).release();
- value_per_output_id_[socket.id()] = value;
-
- return value->span;
- }
-
- BLI_assert(any_value->type == ValueType::OutputSingle);
- GMutableSpan span = static_cast<OutputSingleValue *>(any_value)->span;
- BLI_assert(span.size() == 1);
- return span;
-}
-
-GVectorArray &MFNetworkEvaluationStorage::get_vector_output__full(const MFOutputSocket &socket)
-{
- Value *any_value = value_per_output_id_[socket.id()];
- if (any_value == nullptr) {
- const CPPType &type = socket.data_type().vector_base_type();
- GVectorArray *vector_array = new GVectorArray(type, min_array_size_);
-
- auto *value =
- allocator_.construct<OwnVectorValue>(*vector_array, socket.targets().size()).release();
- value_per_output_id_[socket.id()] = value;
-
- return *value->vector_array;
- }
-
- BLI_assert(any_value->type == ValueType::OutputVector);
- return *static_cast<OutputVectorValue *>(any_value)->vector_array;
-}
-
-GVectorArray &MFNetworkEvaluationStorage::get_vector_output__single(const MFOutputSocket &socket)
-{
- Value *any_value = value_per_output_id_[socket.id()];
- if (any_value == nullptr) {
- const CPPType &type = socket.data_type().vector_base_type();
- GVectorArray *vector_array = new GVectorArray(type, 1);
-
- auto *value =
- allocator_.construct<OwnVectorValue>(*vector_array, socket.targets().size()).release();
- value_per_output_id_[socket.id()] = value;
-
- return *value->vector_array;
- }
-
- BLI_assert(any_value->type == ValueType::OutputVector);
- GVectorArray &vector_array = *static_cast<OutputVectorValue *>(any_value)->vector_array;
- BLI_assert(vector_array.size() == 1);
- return vector_array;
-}
-
-GMutableSpan MFNetworkEvaluationStorage::get_mutable_single__full(const MFInputSocket &input,
- const MFOutputSocket &output,
- ResourceScope &scope)
-{
- const MFOutputSocket &from = *input.origin();
- const MFOutputSocket &to = output;
- const CPPType &type = from.data_type().single_type();
-
- Value *from_any_value = value_per_output_id_[from.id()];
- Value *to_any_value = value_per_output_id_[to.id()];
- BLI_assert(from_any_value != nullptr);
- BLI_assert(type == to.data_type().single_type());
-
- if (to_any_value != nullptr) {
- BLI_assert(to_any_value->type == ValueType::OutputSingle);
- GMutableSpan span = static_cast<OutputSingleValue *>(to_any_value)->span;
- const GVArray &virtual_array = this->get_single_input__full(input, scope);
- virtual_array.materialize_to_uninitialized(mask_, span.data());
- return span;
- }
-
- if (from_any_value->type == ValueType::OwnSingle) {
- OwnSingleValue *value = static_cast<OwnSingleValue *>(from_any_value);
- if (value->max_remaining_users == 1 && !value->is_single_allocated) {
- value_per_output_id_[to.id()] = value;
- value_per_output_id_[from.id()] = nullptr;
- value->max_remaining_users = to.targets().size();
- return value->span;
- }
- }
-
- const GVArray &virtual_array = this->get_single_input__full(input, scope);
- void *new_buffer = MEM_mallocN_aligned(min_array_size_ * type.size(), type.alignment(), AT);
- GMutableSpan new_array_ref(type, new_buffer, min_array_size_);
- virtual_array.materialize_to_uninitialized(mask_, new_array_ref.data());
-
- OwnSingleValue *new_value =
- allocator_.construct<OwnSingleValue>(new_array_ref, to.targets().size(), false).release();
- value_per_output_id_[to.id()] = new_value;
- return new_array_ref;
-}
-
-GMutableSpan MFNetworkEvaluationStorage::get_mutable_single__single(const MFInputSocket &input,
- const MFOutputSocket &output,
- ResourceScope &scope)
-{
- const MFOutputSocket &from = *input.origin();
- const MFOutputSocket &to = output;
- const CPPType &type = from.data_type().single_type();
-
- Value *from_any_value = value_per_output_id_[from.id()];
- Value *to_any_value = value_per_output_id_[to.id()];
- BLI_assert(from_any_value != nullptr);
- BLI_assert(type == to.data_type().single_type());
-
- if (to_any_value != nullptr) {
- BLI_assert(to_any_value->type == ValueType::OutputSingle);
- GMutableSpan span = static_cast<OutputSingleValue *>(to_any_value)->span;
- BLI_assert(span.size() == 1);
- const GVArray &virtual_array = this->get_single_input__single(input, scope);
- virtual_array.get_single_to_uninitialized(span[0]);
- return span;
- }
-
- if (from_any_value->type == ValueType::OwnSingle) {
- OwnSingleValue *value = static_cast<OwnSingleValue *>(from_any_value);
- if (value->max_remaining_users == 1) {
- value_per_output_id_[to.id()] = value;
- value_per_output_id_[from.id()] = nullptr;
- value->max_remaining_users = to.targets().size();
- BLI_assert(value->span.size() == 1);
- return value->span;
- }
- }
-
- const GVArray &virtual_array = this->get_single_input__single(input, scope);
-
- void *new_buffer = allocator_.allocate(type.size(), type.alignment());
- virtual_array.get_single_to_uninitialized(new_buffer);
- GMutableSpan new_array_ref(type, new_buffer, 1);
-
- OwnSingleValue *new_value =
- allocator_.construct<OwnSingleValue>(new_array_ref, to.targets().size(), true).release();
- value_per_output_id_[to.id()] = new_value;
- return new_array_ref;
-}
-
-GVectorArray &MFNetworkEvaluationStorage::get_mutable_vector__full(const MFInputSocket &input,
- const MFOutputSocket &output,
- ResourceScope &scope)
-{
- const MFOutputSocket &from = *input.origin();
- const MFOutputSocket &to = output;
- const CPPType &base_type = from.data_type().vector_base_type();
-
- Value *from_any_value = value_per_output_id_[from.id()];
- Value *to_any_value = value_per_output_id_[to.id()];
- BLI_assert(from_any_value != nullptr);
- BLI_assert(base_type == to.data_type().vector_base_type());
-
- if (to_any_value != nullptr) {
- BLI_assert(to_any_value->type == ValueType::OutputVector);
- GVectorArray &vector_array = *static_cast<OutputVectorValue *>(to_any_value)->vector_array;
- const GVVectorArray &virtual_vector_array = this->get_vector_input__full(input, scope);
- vector_array.extend(mask_, virtual_vector_array);
- return vector_array;
- }
-
- if (from_any_value->type == ValueType::OwnVector) {
- OwnVectorValue *value = static_cast<OwnVectorValue *>(from_any_value);
- if (value->max_remaining_users == 1) {
- value_per_output_id_[to.id()] = value;
- value_per_output_id_[from.id()] = nullptr;
- value->max_remaining_users = to.targets().size();
- return *value->vector_array;
- }
- }
-
- const GVVectorArray &virtual_vector_array = this->get_vector_input__full(input, scope);
-
- GVectorArray *new_vector_array = new GVectorArray(base_type, min_array_size_);
- new_vector_array->extend(mask_, virtual_vector_array);
-
- OwnVectorValue *new_value =
- allocator_.construct<OwnVectorValue>(*new_vector_array, to.targets().size()).release();
- value_per_output_id_[to.id()] = new_value;
-
- return *new_vector_array;
-}
-
-GVectorArray &MFNetworkEvaluationStorage::get_mutable_vector__single(const MFInputSocket &input,
- const MFOutputSocket &output,
- ResourceScope &scope)
-{
- const MFOutputSocket &from = *input.origin();
- const MFOutputSocket &to = output;
- const CPPType &base_type = from.data_type().vector_base_type();
-
- Value *from_any_value = value_per_output_id_[from.id()];
- Value *to_any_value = value_per_output_id_[to.id()];
- BLI_assert(from_any_value != nullptr);
- BLI_assert(base_type == to.data_type().vector_base_type());
-
- if (to_any_value != nullptr) {
- BLI_assert(to_any_value->type == ValueType::OutputVector);
- GVectorArray &vector_array = *static_cast<OutputVectorValue *>(to_any_value)->vector_array;
- BLI_assert(vector_array.size() == 1);
- const GVVectorArray &virtual_vector_array = this->get_vector_input__single(input, scope);
- vector_array.extend({0}, virtual_vector_array);
- return vector_array;
- }
-
- if (from_any_value->type == ValueType::OwnVector) {
- OwnVectorValue *value = static_cast<OwnVectorValue *>(from_any_value);
- if (value->max_remaining_users == 1) {
- value_per_output_id_[to.id()] = value;
- value_per_output_id_[from.id()] = nullptr;
- value->max_remaining_users = to.targets().size();
- return *value->vector_array;
- }
- }
-
- const GVVectorArray &virtual_vector_array = this->get_vector_input__single(input, scope);
-
- GVectorArray *new_vector_array = new GVectorArray(base_type, 1);
- new_vector_array->extend({0}, virtual_vector_array);
-
- OwnVectorValue *new_value =
- allocator_.construct<OwnVectorValue>(*new_vector_array, to.targets().size()).release();
- value_per_output_id_[to.id()] = new_value;
- return *new_vector_array;
-}
-
-const GVArray &MFNetworkEvaluationStorage::get_single_input__full(const MFInputSocket &socket,
- ResourceScope &scope)
-{
- const MFOutputSocket &origin = *socket.origin();
- Value *any_value = value_per_output_id_[origin.id()];
- BLI_assert(any_value != nullptr);
-
- if (any_value->type == ValueType::OwnSingle) {
- OwnSingleValue *value = static_cast<OwnSingleValue *>(any_value);
- if (value->is_single_allocated) {
- return scope.construct<GVArray_For_SingleValueRef>(
- __func__, value->span.type(), min_array_size_, value->span.data());
- }
-
- return scope.construct<GVArray_For_GSpan>(__func__, value->span);
- }
- if (any_value->type == ValueType::InputSingle) {
- InputSingleValue *value = static_cast<InputSingleValue *>(any_value);
- return value->virtual_array;
- }
- if (any_value->type == ValueType::OutputSingle) {
- OutputSingleValue *value = static_cast<OutputSingleValue *>(any_value);
- BLI_assert(value->is_computed);
- return scope.construct<GVArray_For_GSpan>(__func__, value->span);
- }
-
- BLI_assert(false);
- return scope.construct<GVArray_For_Empty>(__func__, CPPType::get<float>());
-}
-
-const GVArray &MFNetworkEvaluationStorage::get_single_input__single(const MFInputSocket &socket,
- ResourceScope &scope)
-{
- const MFOutputSocket &origin = *socket.origin();
- Value *any_value = value_per_output_id_[origin.id()];
- BLI_assert(any_value != nullptr);
-
- if (any_value->type == ValueType::OwnSingle) {
- OwnSingleValue *value = static_cast<OwnSingleValue *>(any_value);
- BLI_assert(value->span.size() == 1);
- return scope.construct<GVArray_For_GSpan>(__func__, value->span);
- }
- if (any_value->type == ValueType::InputSingle) {
- InputSingleValue *value = static_cast<InputSingleValue *>(any_value);
- BLI_assert(value->virtual_array.is_single());
- return value->virtual_array;
- }
- if (any_value->type == ValueType::OutputSingle) {
- OutputSingleValue *value = static_cast<OutputSingleValue *>(any_value);
- BLI_assert(value->is_computed);
- BLI_assert(value->span.size() == 1);
- return scope.construct<GVArray_For_GSpan>(__func__, value->span);
- }
-
- BLI_assert(false);
- return scope.construct<GVArray_For_Empty>(__func__, CPPType::get<float>());
-}
-
-const GVVectorArray &MFNetworkEvaluationStorage::get_vector_input__full(
- const MFInputSocket &socket, ResourceScope &scope)
-{
- const MFOutputSocket &origin = *socket.origin();
- Value *any_value = value_per_output_id_[origin.id()];
- BLI_assert(any_value != nullptr);
-
- if (any_value->type == ValueType::OwnVector) {
- OwnVectorValue *value = static_cast<OwnVectorValue *>(any_value);
- if (value->vector_array->size() == 1) {
- GSpan span = (*value->vector_array)[0];
- return scope.construct<GVVectorArray_For_SingleGSpan>(__func__, span, min_array_size_);
- }
-
- return scope.construct<GVVectorArray_For_GVectorArray>(__func__, *value->vector_array);
- }
- if (any_value->type == ValueType::InputVector) {
- InputVectorValue *value = static_cast<InputVectorValue *>(any_value);
- return value->virtual_vector_array;
- }
- if (any_value->type == ValueType::OutputVector) {
- OutputVectorValue *value = static_cast<OutputVectorValue *>(any_value);
- return scope.construct<GVVectorArray_For_GVectorArray>(__func__, *value->vector_array);
- }
-
- BLI_assert(false);
- return scope.construct<GVVectorArray_For_SingleGSpan>(__func__, GSpan(CPPType::get<float>()), 0);
-}
-
-const GVVectorArray &MFNetworkEvaluationStorage::get_vector_input__single(
- const MFInputSocket &socket, ResourceScope &scope)
-{
- const MFOutputSocket &origin = *socket.origin();
- Value *any_value = value_per_output_id_[origin.id()];
- BLI_assert(any_value != nullptr);
-
- if (any_value->type == ValueType::OwnVector) {
- OwnVectorValue *value = static_cast<OwnVectorValue *>(any_value);
- BLI_assert(value->vector_array->size() == 1);
- return scope.construct<GVVectorArray_For_GVectorArray>(__func__, *value->vector_array);
- }
- if (any_value->type == ValueType::InputVector) {
- InputVectorValue *value = static_cast<InputVectorValue *>(any_value);
- BLI_assert(value->virtual_vector_array.is_single_vector());
- return value->virtual_vector_array;
- }
- if (any_value->type == ValueType::OutputVector) {
- OutputVectorValue *value = static_cast<OutputVectorValue *>(any_value);
- BLI_assert(value->vector_array->size() == 1);
- return scope.construct<GVVectorArray_For_GVectorArray>(__func__, *value->vector_array);
- }
-
- BLI_assert(false);
- return scope.construct<GVVectorArray_For_SingleGSpan>(__func__, GSpan(CPPType::get<float>()), 0);
-}
-
-/** \} */
-
-} // namespace blender::fn
diff --git a/source/blender/functions/intern/multi_function_network_optimization.cc b/source/blender/functions/intern/multi_function_network_optimization.cc
deleted file mode 100644
index 75c3583c5e5..00000000000
--- a/source/blender/functions/intern/multi_function_network_optimization.cc
+++ /dev/null
@@ -1,501 +0,0 @@
-/*
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-
-/** \file
- * \ingroup fn
- */
-
-/* Used to check if two multi-functions have the exact same type. */
-#include <typeinfo>
-
-#include "FN_multi_function_builder.hh"
-#include "FN_multi_function_network_evaluation.hh"
-#include "FN_multi_function_network_optimization.hh"
-
-#include "BLI_disjoint_set.hh"
-#include "BLI_ghash.h"
-#include "BLI_map.hh"
-#include "BLI_multi_value_map.hh"
-#include "BLI_rand.h"
-#include "BLI_stack.hh"
-
-namespace blender::fn::mf_network_optimization {
-
-/* -------------------------------------------------------------------- */
-/** \name Utility functions to find nodes in a network.
- * \{ */
-
-static bool set_tag_and_check_if_modified(bool &tag, bool new_value)
-{
- if (tag != new_value) {
- tag = new_value;
- return true;
- }
-
- return false;
-}
-
-static Array<bool> mask_nodes_to_the_left(MFNetwork &network, Span<MFNode *> nodes)
-{
- Array<bool> is_to_the_left(network.node_id_amount(), false);
- Stack<MFNode *> nodes_to_check;
-
- for (MFNode *node : nodes) {
- is_to_the_left[node->id()] = true;
- nodes_to_check.push(node);
- }
-
- while (!nodes_to_check.is_empty()) {
- MFNode &node = *nodes_to_check.pop();
-
- for (MFInputSocket *input_socket : node.inputs()) {
- MFOutputSocket *origin = input_socket->origin();
- if (origin != nullptr) {
- MFNode &origin_node = origin->node();
- if (set_tag_and_check_if_modified(is_to_the_left[origin_node.id()], true)) {
- nodes_to_check.push(&origin_node);
- }
- }
- }
- }
-
- return is_to_the_left;
-}
-
-static Array<bool> mask_nodes_to_the_right(MFNetwork &network, Span<MFNode *> nodes)
-{
- Array<bool> is_to_the_right(network.node_id_amount(), false);
- Stack<MFNode *> nodes_to_check;
-
- for (MFNode *node : nodes) {
- is_to_the_right[node->id()] = true;
- nodes_to_check.push(node);
- }
-
- while (!nodes_to_check.is_empty()) {
- MFNode &node = *nodes_to_check.pop();
-
- for (MFOutputSocket *output_socket : node.outputs()) {
- for (MFInputSocket *target_socket : output_socket->targets()) {
- MFNode &target_node = target_socket->node();
- if (set_tag_and_check_if_modified(is_to_the_right[target_node.id()], true)) {
- nodes_to_check.push(&target_node);
- }
- }
- }
- }
-
- return is_to_the_right;
-}
-
-static Vector<MFNode *> find_nodes_based_on_mask(MFNetwork &network,
- Span<bool> id_mask,
- bool mask_value)
-{
- Vector<MFNode *> nodes;
- for (int id : id_mask.index_range()) {
- if (id_mask[id] == mask_value) {
- MFNode *node = network.node_or_null_by_id(id);
- if (node != nullptr) {
- nodes.append(node);
- }
- }
- }
- return nodes;
-}
-
-/** \} */
-
-/* -------------------------------------------------------------------- */
-/** \name Dead Node Removal
- * \{ */
-
-/**
- * Unused nodes are all those nodes that no dummy node depends upon.
- */
-void dead_node_removal(MFNetwork &network)
-{
- Array<bool> node_is_used_mask = mask_nodes_to_the_left(network,
- network.dummy_nodes().cast<MFNode *>());
- Vector<MFNode *> nodes_to_remove = find_nodes_based_on_mask(network, node_is_used_mask, false);
- network.remove(nodes_to_remove);
-}
-
-/** \} */
-
-/* -------------------------------------------------------------------- */
-/** \name Constant Folding
- * \{ */
-
-static bool function_node_can_be_constant(MFFunctionNode *node)
-{
- if (node->has_unlinked_inputs()) {
- return false;
- }
- if (node->function().depends_on_context()) {
- return false;
- }
- return true;
-}
-
-static Vector<MFNode *> find_non_constant_nodes(MFNetwork &network)
-{
- Vector<MFNode *> non_constant_nodes;
- non_constant_nodes.extend(network.dummy_nodes().cast<MFNode *>());
-
- for (MFFunctionNode *node : network.function_nodes()) {
- if (!function_node_can_be_constant(node)) {
- non_constant_nodes.append(node);
- }
- }
- return non_constant_nodes;
-}
-
-static bool output_has_non_constant_target_node(MFOutputSocket *output_socket,
- Span<bool> is_not_constant_mask)
-{
- for (MFInputSocket *target_socket : output_socket->targets()) {
- MFNode &target_node = target_socket->node();
- bool target_is_not_constant = is_not_constant_mask[target_node.id()];
- if (target_is_not_constant) {
- return true;
- }
- }
- return false;
-}
-
-static MFInputSocket *try_find_dummy_target_socket(MFOutputSocket *output_socket)
-{
- for (MFInputSocket *target_socket : output_socket->targets()) {
- if (target_socket->node().is_dummy()) {
- return target_socket;
- }
- }
- return nullptr;
-}
-
-static Vector<MFInputSocket *> find_constant_inputs_to_fold(
- MFNetwork &network, Vector<MFDummyNode *> &r_temporary_nodes)
-{
- Vector<MFNode *> non_constant_nodes = find_non_constant_nodes(network);
- Array<bool> is_not_constant_mask = mask_nodes_to_the_right(network, non_constant_nodes);
- Vector<MFNode *> constant_nodes = find_nodes_based_on_mask(network, is_not_constant_mask, false);
-
- Vector<MFInputSocket *> sockets_to_compute;
- for (MFNode *node : constant_nodes) {
- if (node->inputs().size() == 0) {
- continue;
- }
-
- for (MFOutputSocket *output_socket : node->outputs()) {
- MFDataType data_type = output_socket->data_type();
- if (output_has_non_constant_target_node(output_socket, is_not_constant_mask)) {
- MFInputSocket *dummy_target = try_find_dummy_target_socket(output_socket);
- if (dummy_target == nullptr) {
- dummy_target = &network.add_output("Dummy", data_type);
- network.add_link(*output_socket, *dummy_target);
- r_temporary_nodes.append(&dummy_target->node().as_dummy());
- }
-
- sockets_to_compute.append(dummy_target);
- }
- }
- }
- return sockets_to_compute;
-}
-
-static void prepare_params_for_constant_folding(const MultiFunction &network_fn,
- MFParamsBuilder &params,
- ResourceScope &scope)
-{
- for (int param_index : network_fn.param_indices()) {
- MFParamType param_type = network_fn.param_type(param_index);
- MFDataType data_type = param_type.data_type();
-
- switch (data_type.category()) {
- case MFDataType::Single: {
- /* Allocates memory for a single constant folded value. */
- const CPPType &cpp_type = data_type.single_type();
- void *buffer = scope.linear_allocator().allocate(cpp_type.size(), cpp_type.alignment());
- GMutableSpan array{cpp_type, buffer, 1};
- params.add_uninitialized_single_output(array);
- break;
- }
- case MFDataType::Vector: {
- /* Allocates memory for a constant folded vector. */
- const CPPType &cpp_type = data_type.vector_base_type();
- GVectorArray &vector_array = scope.construct<GVectorArray>(AT, cpp_type, 1);
- params.add_vector_output(vector_array);
- break;
- }
- }
- }
-}
-
-static Array<MFOutputSocket *> add_constant_folded_sockets(const MultiFunction &network_fn,
- MFParamsBuilder &params,
- ResourceScope &scope,
- MFNetwork &network)
-{
- Array<MFOutputSocket *> folded_sockets{network_fn.param_indices().size(), nullptr};
-
- for (int param_index : network_fn.param_indices()) {
- MFParamType param_type = network_fn.param_type(param_index);
- MFDataType data_type = param_type.data_type();
-
- const MultiFunction *constant_fn = nullptr;
-
- switch (data_type.category()) {
- case MFDataType::Single: {
- const CPPType &cpp_type = data_type.single_type();
- GMutableSpan array = params.computed_array(param_index);
- void *buffer = array.data();
- scope.add(buffer, array.type().destruct_fn(), AT);
-
- constant_fn = &scope.construct<CustomMF_GenericConstant>(AT, cpp_type, buffer);
- break;
- }
- case MFDataType::Vector: {
- GVectorArray &vector_array = params.computed_vector_array(param_index);
- GSpan array = vector_array[0];
- constant_fn = &scope.construct<CustomMF_GenericConstantArray>(AT, array);
- break;
- }
- }
-
- MFFunctionNode &folded_node = network.add_function(*constant_fn);
- folded_sockets[param_index] = &folded_node.output(0);
- }
- return folded_sockets;
-}
-
-static Array<MFOutputSocket *> compute_constant_sockets_and_add_folded_nodes(
- MFNetwork &network, Span<const MFInputSocket *> sockets_to_compute, ResourceScope &scope)
-{
- MFNetworkEvaluator network_fn{{}, sockets_to_compute};
-
- MFContextBuilder context;
- MFParamsBuilder params{network_fn, 1};
- prepare_params_for_constant_folding(network_fn, params, scope);
- network_fn.call({0}, params, context);
- return add_constant_folded_sockets(network_fn, params, scope, network);
-}
-
-class MyClass {
- MFDummyNode node;
-};
-
-/**
- * Find function nodes that always output the same value and replace those with constant nodes.
- */
-void constant_folding(MFNetwork &network, ResourceScope &scope)
-{
- Vector<MFDummyNode *> temporary_nodes;
- Vector<MFInputSocket *> inputs_to_fold = find_constant_inputs_to_fold(network, temporary_nodes);
- if (inputs_to_fold.size() == 0) {
- return;
- }
-
- Array<MFOutputSocket *> folded_sockets = compute_constant_sockets_and_add_folded_nodes(
- network, inputs_to_fold, scope);
-
- for (int i : inputs_to_fold.index_range()) {
- MFOutputSocket &original_socket = *inputs_to_fold[i]->origin();
- network.relink(original_socket, *folded_sockets[i]);
- }
-
- network.remove(temporary_nodes.as_span().cast<MFNode *>());
-}
-
-/** \} */
-
-/* -------------------------------------------------------------------- */
-/** \name Common Sub-network Elimination
- * \{ */
-
-static uint64_t compute_node_hash(MFFunctionNode &node, RNG *rng, Span<uint64_t> node_hashes)
-{
- if (node.function().depends_on_context()) {
- return BLI_rng_get_uint(rng);
- }
- if (node.has_unlinked_inputs()) {
- return BLI_rng_get_uint(rng);
- }
-
- uint64_t combined_inputs_hash = 394659347u;
- for (MFInputSocket *input_socket : node.inputs()) {
- MFOutputSocket *origin_socket = input_socket->origin();
- uint64_t input_hash = BLI_ghashutil_combine_hash(node_hashes[origin_socket->node().id()],
- origin_socket->index());
- combined_inputs_hash = BLI_ghashutil_combine_hash(combined_inputs_hash, input_hash);
- }
-
- uint64_t function_hash = node.function().hash();
- uint64_t node_hash = BLI_ghashutil_combine_hash(combined_inputs_hash, function_hash);
- return node_hash;
-}
-
-/**
- * Produces a hash for every node. Two nodes with the same hash should have a high probability of
- * outputting the same values.
- */
-static Array<uint64_t> compute_node_hashes(MFNetwork &network)
-{
- RNG *rng = BLI_rng_new(0);
- Array<uint64_t> node_hashes(network.node_id_amount());
- Array<bool> node_is_hashed(network.node_id_amount(), false);
-
- /* No dummy nodes are not assumed to output the same values. */
- for (MFDummyNode *node : network.dummy_nodes()) {
- uint64_t node_hash = BLI_rng_get_uint(rng);
- node_hashes[node->id()] = node_hash;
- node_is_hashed[node->id()] = true;
- }
-
- Stack<MFFunctionNode *> nodes_to_check;
- nodes_to_check.push_multiple(network.function_nodes());
-
- while (!nodes_to_check.is_empty()) {
- MFFunctionNode &node = *nodes_to_check.peek();
- if (node_is_hashed[node.id()]) {
- nodes_to_check.pop();
- continue;
- }
-
- /* Make sure that origin nodes are hashed first. */
- bool all_dependencies_ready = true;
- for (MFInputSocket *input_socket : node.inputs()) {
- MFOutputSocket *origin_socket = input_socket->origin();
- if (origin_socket != nullptr) {
- MFNode &origin_node = origin_socket->node();
- if (!node_is_hashed[origin_node.id()]) {
- all_dependencies_ready = false;
- nodes_to_check.push(&origin_node.as_function());
- }
- }
- }
- if (!all_dependencies_ready) {
- continue;
- }
-
- uint64_t node_hash = compute_node_hash(node, rng, node_hashes);
- node_hashes[node.id()] = node_hash;
- node_is_hashed[node.id()] = true;
- nodes_to_check.pop();
- }
-
- BLI_rng_free(rng);
- return node_hashes;
-}
-
-static MultiValueMap<uint64_t, MFNode *> group_nodes_by_hash(MFNetwork &network,
- Span<uint64_t> node_hashes)
-{
- MultiValueMap<uint64_t, MFNode *> nodes_by_hash;
- for (int id : IndexRange(network.node_id_amount())) {
- MFNode *node = network.node_or_null_by_id(id);
- if (node != nullptr) {
- uint64_t node_hash = node_hashes[id];
- nodes_by_hash.add(node_hash, node);
- }
- }
- return nodes_by_hash;
-}
-
-static bool functions_are_equal(const MultiFunction &a, const MultiFunction &b)
-{
- if (&a == &b) {
- return true;
- }
- if (typeid(a) == typeid(b)) {
- return a.equals(b);
- }
- return false;
-}
-
-static bool nodes_output_same_values(DisjointSet &cache, const MFNode &a, const MFNode &b)
-{
- if (cache.in_same_set(a.id(), b.id())) {
- return true;
- }
-
- if (a.is_dummy() || b.is_dummy()) {
- return false;
- }
- if (!functions_are_equal(a.as_function().function(), b.as_function().function())) {
- return false;
- }
- for (int i : a.inputs().index_range()) {
- const MFOutputSocket *origin_a = a.input(i).origin();
- const MFOutputSocket *origin_b = b.input(i).origin();
- if (origin_a == nullptr || origin_b == nullptr) {
- return false;
- }
- if (!nodes_output_same_values(cache, origin_a->node(), origin_b->node())) {
- return false;
- }
- }
-
- cache.join(a.id(), b.id());
- return true;
-}
-
-static void relink_duplicate_nodes(MFNetwork &network,
- MultiValueMap<uint64_t, MFNode *> &nodes_by_hash)
-{
- DisjointSet same_node_cache{network.node_id_amount()};
-
- for (Span<MFNode *> nodes_with_same_hash : nodes_by_hash.values()) {
- if (nodes_with_same_hash.size() <= 1) {
- continue;
- }
-
- Vector<MFNode *, 16> nodes_to_check = nodes_with_same_hash;
- while (nodes_to_check.size() >= 2) {
- Vector<MFNode *, 16> remaining_nodes;
-
- MFNode &deduplicated_node = *nodes_to_check[0];
- for (MFNode *node : nodes_to_check.as_span().drop_front(1)) {
- /* This is true with fairly high probability, but hash collisions can happen. So we have to
- * check if the node actually output the same values. */
- if (nodes_output_same_values(same_node_cache, deduplicated_node, *node)) {
- for (int i : deduplicated_node.outputs().index_range()) {
- network.relink(node->output(i), deduplicated_node.output(i));
- }
- }
- else {
- remaining_nodes.append(node);
- }
- }
- nodes_to_check = std::move(remaining_nodes);
- }
- }
-}
-
-/**
- * Tries to detect duplicate sub-networks and eliminates them. This can help quite a lot when node
- * groups were used to create the network.
- */
-void common_subnetwork_elimination(MFNetwork &network)
-{
- Array<uint64_t> node_hashes = compute_node_hashes(network);
- MultiValueMap<uint64_t, MFNode *> nodes_by_hash = group_nodes_by_hash(network, node_hashes);
- relink_duplicate_nodes(network, nodes_by_hash);
-}
-
-/** \} */
-
-} // namespace blender::fn::mf_network_optimization
diff --git a/source/blender/functions/tests/FN_multi_function_network_test.cc b/source/blender/functions/tests/FN_multi_function_network_test.cc
deleted file mode 100644
index 7b9738e5ca4..00000000000
--- a/source/blender/functions/tests/FN_multi_function_network_test.cc
+++ /dev/null
@@ -1,280 +0,0 @@
-/* Apache License, Version 2.0 */
-
-#include "testing/testing.h"
-
-#include "FN_multi_function_builder.hh"
-#include "FN_multi_function_network.hh"
-#include "FN_multi_function_network_evaluation.hh"
-
-namespace blender::fn::tests {
-namespace {
-
-TEST(multi_function_network, Test1)
-{
- CustomMF_SI_SO<int, int> add_10_fn("add 10", [](int value) { return value + 10; });
- CustomMF_SI_SI_SO<int, int, int> multiply_fn("multiply", [](int a, int b) { return a * b; });
-
- MFNetwork network;
-
- MFNode &node1 = network.add_function(add_10_fn);
- MFNode &node2 = network.add_function(multiply_fn);
- MFOutputSocket &input_socket = network.add_input("Input", MFDataType::ForSingle<int>());
- MFInputSocket &output_socket = network.add_output("Output", MFDataType::ForSingle<int>());
- network.add_link(node1.output(0), node2.input(0));
- network.add_link(node1.output(0), node2.input(1));
- network.add_link(node2.output(0), output_socket);
- network.add_link(input_socket, node1.input(0));
-
- MFNetworkEvaluator network_fn{{&input_socket}, {&output_socket}};
-
- {
- Array<int> values = {4, 6, 1, 2, 0};
- Array<int> results(values.size(), 0);
-
- MFParamsBuilder params(network_fn, values.size());
- params.add_readonly_single_input(values.as_span());
- params.add_uninitialized_single_output(results.as_mutable_span());
-
- MFContextBuilder context;
-
- network_fn.call({0, 2, 3, 4}, params, context);
-
- EXPECT_EQ(results[0], 14 * 14);
- EXPECT_EQ(results[1], 0);
- EXPECT_EQ(results[2], 11 * 11);
- EXPECT_EQ(results[3], 12 * 12);
- EXPECT_EQ(results[4], 10 * 10);
- }
- {
- int value = 3;
- Array<int> results(5, 0);
-
- MFParamsBuilder params(network_fn, results.size());
- params.add_readonly_single_input(&value);
- params.add_uninitialized_single_output(results.as_mutable_span());
-
- MFContextBuilder context;
-
- network_fn.call({1, 2, 4}, params, context);
-
- EXPECT_EQ(results[0], 0);
- EXPECT_EQ(results[1], 13 * 13);
- EXPECT_EQ(results[2], 13 * 13);
- EXPECT_EQ(results[3], 0);
- EXPECT_EQ(results[4], 13 * 13);
- }
-}
-
-class ConcatVectorsFunction : public MultiFunction {
- public:
- ConcatVectorsFunction()
- {
- static MFSignature signature = create_signature();
- this->set_signature(&signature);
- }
-
- static MFSignature create_signature()
- {
- MFSignatureBuilder signature{"Concat Vectors"};
- signature.vector_mutable<int>("A");
- signature.vector_input<int>("B");
- return signature.build();
- }
-
- void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override
- {
- GVectorArray &a = params.vector_mutable(0);
- const GVVectorArray &b = params.readonly_vector_input(1);
- a.extend(mask, b);
- }
-};
-
-class AppendFunction : public MultiFunction {
- public:
- AppendFunction()
- {
- static MFSignature signature = create_signature();
- this->set_signature(&signature);
- }
-
- static MFSignature create_signature()
- {
- MFSignatureBuilder signature{"Append"};
- signature.vector_mutable<int>("Vector");
- signature.single_input<int>("Value");
- return signature.build();
- }
-
- void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override
- {
- GVectorArray_TypedMutableRef<int> vectors = params.vector_mutable<int>(0);
- const VArray<int> &values = params.readonly_single_input<int>(1);
-
- for (int64_t i : mask) {
- vectors.append(i, values[i]);
- }
- }
-};
-
-class SumVectorFunction : public MultiFunction {
- public:
- SumVectorFunction()
- {
- static MFSignature signature = create_signature();
- this->set_signature(&signature);
- }
-
- static MFSignature create_signature()
- {
- MFSignatureBuilder signature{"Sum Vectors"};
- signature.vector_input<int>("Vector");
- signature.single_output<int>("Sum");
- return signature.build();
- }
-
- void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override
- {
- const VVectorArray<int> &vectors = params.readonly_vector_input<int>(0);
- MutableSpan<int> sums = params.uninitialized_single_output<int>(1);
-
- for (int64_t i : mask) {
- int sum = 0;
- for (int j : IndexRange(vectors.get_vector_size(i))) {
- sum += vectors.get_vector_element(i, j);
- }
- sums[i] = sum;
- }
- }
-};
-
-class CreateRangeFunction : public MultiFunction {
- public:
- CreateRangeFunction()
- {
- static MFSignature signature = create_signature();
- this->set_signature(&signature);
- }
-
- static MFSignature create_signature()
- {
- MFSignatureBuilder signature{"Create Range"};
- signature.single_input<int>("Size");
- signature.vector_output<int>("Range");
- return signature.build();
- }
-
- void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override
- {
- const VArray<int> &sizes = params.readonly_single_input<int>(0, "Size");
- GVectorArray_TypedMutableRef<int> ranges = params.vector_output<int>(1, "Range");
-
- for (int64_t i : mask) {
- int size = sizes[i];
- for (int j : IndexRange(size)) {
- ranges.append(i, j);
- }
- }
- }
-};
-
-TEST(multi_function_network, Test2)
-{
- CustomMF_SI_SO<int, int> add_3_fn("add 3", [](int value) { return value + 3; });
-
- ConcatVectorsFunction concat_vectors_fn;
- AppendFunction append_fn;
- SumVectorFunction sum_fn;
- CreateRangeFunction create_range_fn;
-
- MFNetwork network;
-
- MFOutputSocket &input1 = network.add_input("Input 1", MFDataType::ForVector<int>());
- MFOutputSocket &input2 = network.add_input("Input 2", MFDataType::ForSingle<int>());
- MFInputSocket &output1 = network.add_output("Output 1", MFDataType::ForVector<int>());
- MFInputSocket &output2 = network.add_output("Output 2", MFDataType::ForSingle<int>());
-
- MFNode &node1 = network.add_function(add_3_fn);
- MFNode &node2 = network.add_function(create_range_fn);
- MFNode &node3 = network.add_function(concat_vectors_fn);
- MFNode &node4 = network.add_function(sum_fn);
- MFNode &node5 = network.add_function(append_fn);
- MFNode &node6 = network.add_function(sum_fn);
-
- network.add_link(input2, node1.input(0));
- network.add_link(node1.output(0), node2.input(0));
- network.add_link(node2.output(0), node3.input(1));
- network.add_link(input1, node3.input(0));
- network.add_link(input1, node4.input(0));
- network.add_link(node4.output(0), node5.input(1));
- network.add_link(node3.output(0), node5.input(0));
- network.add_link(node5.output(0), node6.input(0));
- network.add_link(node3.output(0), output1);
- network.add_link(node6.output(0), output2);
-
- // std::cout << network.to_dot() << "\n\n";
-
- MFNetworkEvaluator network_fn{{&input1, &input2}, {&output1, &output2}};
-
- {
- Array<int> input_value_1 = {3, 6};
- int input_value_2 = 4;
-
- GVectorArray output_value_1(CPPType::get<int32_t>(), 5);
- Array<int> output_value_2(5, -1);
-
- MFParamsBuilder params(network_fn, 5);
- GVVectorArray_For_SingleGSpan inputs_1{input_value_1.as_span(), 5};
- params.add_readonly_vector_input(inputs_1);
- params.add_readonly_single_input(&input_value_2);
- params.add_vector_output(output_value_1);
- params.add_uninitialized_single_output(output_value_2.as_mutable_span());
-
- MFContextBuilder context;
-
- network_fn.call({1, 2, 4}, params, context);
-
- EXPECT_EQ(output_value_1[0].size(), 0);
- EXPECT_EQ(output_value_1[1].size(), 9);
- EXPECT_EQ(output_value_1[2].size(), 9);
- EXPECT_EQ(output_value_1[3].size(), 0);
- EXPECT_EQ(output_value_1[4].size(), 9);
-
- EXPECT_EQ(output_value_2[0], -1);
- EXPECT_EQ(output_value_2[1], 39);
- EXPECT_EQ(output_value_2[2], 39);
- EXPECT_EQ(output_value_2[3], -1);
- EXPECT_EQ(output_value_2[4], 39);
- }
- {
- GVectorArray input_value_1(CPPType::get<int32_t>(), 3);
- GVectorArray_TypedMutableRef<int> input_value_1_ref{input_value_1};
- input_value_1_ref.extend(0, {3, 4, 5});
- input_value_1_ref.extend(1, {1, 2});
-
- Array<int> input_value_2 = {4, 2, 3};
-
- GVectorArray output_value_1(CPPType::get<int32_t>(), 3);
- Array<int> output_value_2(3, -1);
-
- MFParamsBuilder params(network_fn, 3);
- params.add_readonly_vector_input(input_value_1);
- params.add_readonly_single_input(input_value_2.as_span());
- params.add_vector_output(output_value_1);
- params.add_uninitialized_single_output(output_value_2.as_mutable_span());
-
- MFContextBuilder context;
-
- network_fn.call({0, 1, 2}, params, context);
-
- EXPECT_EQ(output_value_1[0].size(), 10);
- EXPECT_EQ(output_value_1[1].size(), 7);
- EXPECT_EQ(output_value_1[2].size(), 6);
-
- EXPECT_EQ(output_value_2[0], 45);
- EXPECT_EQ(output_value_2[1], 16);
- EXPECT_EQ(output_value_2[2], 15);
- }
-}
-
-} // namespace
-} // namespace blender::fn::tests
diff --git a/source/blender/functions/tests/FN_multi_function_test.cc b/source/blender/functions/tests/FN_multi_function_test.cc
index 3d73e020eb2..91c72a51dd6 100644
--- a/source/blender/functions/tests/FN_multi_function_test.cc
+++ b/source/blender/functions/tests/FN_multi_function_test.cc
@@ -4,6 +4,7 @@
#include "FN_multi_function.hh"
#include "FN_multi_function_builder.hh"
+#include "FN_multi_function_test_common.hh"
namespace blender::fn::tests {
namespace {
@@ -59,33 +60,6 @@ TEST(multi_function, AddFunction)
EXPECT_EQ(output[2], 36);
}
-class AddPrefixFunction : public MultiFunction {
- public:
- AddPrefixFunction()
- {
- static MFSignature signature = create_signature();
- this->set_signature(&signature);
- }
-
- static MFSignature create_signature()
- {
- MFSignatureBuilder signature{"Add Prefix"};
- signature.single_input<std::string>("Prefix");
- signature.single_mutable<std::string>("Strings");
- return signature.build();
- }
-
- void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override
- {
- const VArray<std::string> &prefixes = params.readonly_single_input<std::string>(0, "Prefix");
- MutableSpan<std::string> strings = params.single_mutable<std::string>(1, "Strings");
-
- for (int64_t i : mask) {
- strings[i] = prefixes[i] + strings[i];
- }
- }
-};
-
TEST(multi_function, AddPrefixFunction)
{
AddPrefixFunction fn;
@@ -113,43 +87,13 @@ TEST(multi_function, AddPrefixFunction)
EXPECT_EQ(strings[3], "ABAnother much longer string to trigger an allocation");
}
-class CreateRangeFunction : public MultiFunction {
- public:
- CreateRangeFunction()
- {
- static MFSignature signature = create_signature();
- this->set_signature(&signature);
- }
-
- static MFSignature create_signature()
- {
- MFSignatureBuilder signature{"Create Range"};
- signature.single_input<uint>("Size");
- signature.vector_output<uint>("Range");
- return signature.build();
- }
-
- void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override
- {
- const VArray<uint> &sizes = params.readonly_single_input<uint>(0, "Size");
- GVectorArray &ranges = params.vector_output(1, "Range");
-
- for (int64_t i : mask) {
- uint size = sizes[i];
- for (uint j : IndexRange(size)) {
- ranges.append(i, &j);
- }
- }
- }
-};
-
TEST(multi_function, CreateRangeFunction)
{
CreateRangeFunction fn;
- GVectorArray ranges(CPPType::get<uint>(), 5);
- GVectorArray_TypedMutableRef<uint> ranges_ref{ranges};
- Array<uint> sizes = {3, 0, 6, 1, 4};
+ GVectorArray ranges(CPPType::get<int>(), 5);
+ GVectorArray_TypedMutableRef<int> ranges_ref{ranges};
+ Array<int> sizes = {3, 0, 6, 1, 4};
MFParamsBuilder params(fn, ranges.size());
params.add_readonly_single_input(sizes.as_span());
@@ -172,34 +116,6 @@ TEST(multi_function, CreateRangeFunction)
EXPECT_EQ(ranges_ref[2][1], 1);
}
-class GenericAppendFunction : public MultiFunction {
- private:
- MFSignature signature_;
-
- public:
- GenericAppendFunction(const CPPType &type)
- {
- MFSignatureBuilder signature{"Append"};
- signature.vector_mutable("Vector", type);
- signature.single_input("Value", type);
- signature_ = signature.build();
- this->set_signature(&signature_);
- }
-
- void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override
- {
- GVectorArray &vectors = params.vector_mutable(0, "Vector");
- const GVArray &values = params.readonly_single_input(1, "Value");
-
- for (int64_t i : mask) {
- BUFFER_FOR_CPP_TYPE_VALUE(values.type(), buffer);
- values.get(i, buffer);
- vectors.append(i, buffer);
- values.type().destruct(buffer);
- }
- }
-};
-
TEST(multi_function, GenericAppendFunction)
{
GenericAppendFunction fn(CPPType::get<int32_t>());
diff --git a/source/blender/functions/tests/FN_multi_function_test_common.hh b/source/blender/functions/tests/FN_multi_function_test_common.hh
new file mode 100644
index 00000000000..51c8fac8a96
--- /dev/null
+++ b/source/blender/functions/tests/FN_multi_function_test_common.hh
@@ -0,0 +1,174 @@
+/* Apache License, Version 2.0 */
+
+#include "FN_multi_function.hh"
+
+namespace blender::fn::tests {
+
+class AddPrefixFunction : public MultiFunction {
+ public:
+ AddPrefixFunction()
+ {
+ static MFSignature signature = create_signature();
+ this->set_signature(&signature);
+ }
+
+ static MFSignature create_signature()
+ {
+ MFSignatureBuilder signature{"Add Prefix"};
+ signature.single_input<std::string>("Prefix");
+ signature.single_mutable<std::string>("Strings");
+ return signature.build();
+ }
+
+ void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override
+ {
+ const VArray<std::string> &prefixes = params.readonly_single_input<std::string>(0, "Prefix");
+ MutableSpan<std::string> strings = params.single_mutable<std::string>(1, "Strings");
+
+ for (int64_t i : mask) {
+ strings[i] = prefixes[i] + strings[i];
+ }
+ }
+};
+
+class CreateRangeFunction : public MultiFunction {
+ public:
+ CreateRangeFunction()
+ {
+ static MFSignature signature = create_signature();
+ this->set_signature(&signature);
+ }
+
+ static MFSignature create_signature()
+ {
+ MFSignatureBuilder signature{"Create Range"};
+ signature.single_input<int>("Size");
+ signature.vector_output<int>("Range");
+ return signature.build();
+ }
+
+ void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override
+ {
+ const VArray<int> &sizes = params.readonly_single_input<int>(0, "Size");
+ GVectorArray &ranges = params.vector_output(1, "Range");
+
+ for (int64_t i : mask) {
+ int size = sizes[i];
+ for (int j : IndexRange(size)) {
+ ranges.append(i, &j);
+ }
+ }
+ }
+};
+
+class GenericAppendFunction : public MultiFunction {
+ private:
+ MFSignature signature_;
+
+ public:
+ GenericAppendFunction(const CPPType &type)
+ {
+ MFSignatureBuilder signature{"Append"};
+ signature.vector_mutable("Vector", type);
+ signature.single_input("Value", type);
+ signature_ = signature.build();
+ this->set_signature(&signature_);
+ }
+
+ void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override
+ {
+ GVectorArray &vectors = params.vector_mutable(0, "Vector");
+ const GVArray &values = params.readonly_single_input(1, "Value");
+
+ for (int64_t i : mask) {
+ BUFFER_FOR_CPP_TYPE_VALUE(values.type(), buffer);
+ values.get(i, buffer);
+ vectors.append(i, buffer);
+ values.type().destruct(buffer);
+ }
+ }
+};
+
+class ConcatVectorsFunction : public MultiFunction {
+ public:
+ ConcatVectorsFunction()
+ {
+ static MFSignature signature = create_signature();
+ this->set_signature(&signature);
+ }
+
+ static MFSignature create_signature()
+ {
+ MFSignatureBuilder signature{"Concat Vectors"};
+ signature.vector_mutable<int>("A");
+ signature.vector_input<int>("B");
+ return signature.build();
+ }
+
+ void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override
+ {
+ GVectorArray &a = params.vector_mutable(0);
+ const GVVectorArray &b = params.readonly_vector_input(1);
+ a.extend(mask, b);
+ }
+};
+
+class AppendFunction : public MultiFunction {
+ public:
+ AppendFunction()
+ {
+ static MFSignature signature = create_signature();
+ this->set_signature(&signature);
+ }
+
+ static MFSignature create_signature()
+ {
+ MFSignatureBuilder signature{"Append"};
+ signature.vector_mutable<int>("Vector");
+ signature.single_input<int>("Value");
+ return signature.build();
+ }
+
+ void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override
+ {
+ GVectorArray_TypedMutableRef<int> vectors = params.vector_mutable<int>(0);
+ const VArray<int> &values = params.readonly_single_input<int>(1);
+
+ for (int64_t i : mask) {
+ vectors.append(i, values[i]);
+ }
+ }
+};
+
+class SumVectorFunction : public MultiFunction {
+ public:
+ SumVectorFunction()
+ {
+ static MFSignature signature = create_signature();
+ this->set_signature(&signature);
+ }
+
+ static MFSignature create_signature()
+ {
+ MFSignatureBuilder signature{"Sum Vectors"};
+ signature.vector_input<int>("Vector");
+ signature.single_output<int>("Sum");
+ return signature.build();
+ }
+
+ void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override
+ {
+ const VVectorArray<int> &vectors = params.readonly_vector_input<int>(0);
+ MutableSpan<int> sums = params.uninitialized_single_output<int>(1);
+
+ for (int64_t i : mask) {
+ int sum = 0;
+ for (int j : IndexRange(vectors.get_vector_size(i))) {
+ sum += vectors.get_vector_element(i, j);
+ }
+ sums[i] = sum;
+ }
+ }
+};
+
+} // namespace blender::fn::tests
diff --git a/source/blender/io/collada/collada_internal.cpp b/source/blender/io/collada/collada_internal.cpp
index 355aa5c22f0..bd6f496c8ec 100644
--- a/source/blender/io/collada/collada_internal.cpp
+++ b/source/blender/io/collada/collada_internal.cpp
@@ -162,7 +162,7 @@ void UnitConverter::calculate_scale(Scene &sce)
* Translation map.
* Used to translate every COLLADA id to a valid id, no matter what "wrong" letters may be
* included. Look at the IDREF XSD declaration for more.
- * Follows strictly the COLLADA XSD declaration which explicitly allows non-english chars,
+ * Follows strictly the COLLADA XSD declaration which explicitly allows non-English chars,
* like special chars (e.g. micro sign), umlauts and so on.
* The COLLADA spec also allows additional chars for member access ('.'), these
* must obviously be removed too, otherwise they would be heavily misinterpreted.
diff --git a/source/blender/makesdna/DNA_cachefile_defaults.h b/source/blender/makesdna/DNA_cachefile_defaults.h
index 521b72567d4..74fbe5012ab 100644
--- a/source/blender/makesdna/DNA_cachefile_defaults.h
+++ b/source/blender/makesdna/DNA_cachefile_defaults.h
@@ -40,6 +40,8 @@
.handle = NULL, \
.handle_filepath[0] = '\0', \
.handle_readers = NULL, \
+ .use_prefetch = 1, \
+ .prefetch_cache_size = 4096, \
}
/** \} */
diff --git a/source/blender/makesdna/DNA_cachefile_types.h b/source/blender/makesdna/DNA_cachefile_types.h
index ae4ade49be1..0f4c53a6e7e 100644
--- a/source/blender/makesdna/DNA_cachefile_types.h
+++ b/source/blender/makesdna/DNA_cachefile_types.h
@@ -101,7 +101,15 @@ typedef struct CacheFile {
*/
char use_render_procedural;
- char _pad1[7];
+ char _pad1[3];
+
+ /** Enable data prefetching when using the Cycles Procedural. */
+ char use_prefetch;
+
+ /** Size in megabytes for the prefetch cache used by the Cycles Procedural. */
+ int prefetch_cache_size;
+
+ char _pad2[7];
char velocity_unit;
/* Name of the velocity property in the archive. */
diff --git a/source/blender/makesrna/RNA_access.h b/source/blender/makesrna/RNA_access.h
index f206bde4705..b943a8fad5a 100644
--- a/source/blender/makesrna/RNA_access.h
+++ b/source/blender/makesrna/RNA_access.h
@@ -1029,10 +1029,8 @@ void RNA_property_string_set(PointerRNA *ptr, PropertyRNA *prop, const char *val
void RNA_property_string_set_bytes(PointerRNA *ptr, PropertyRNA *prop, const char *value, int len);
int RNA_property_string_length(PointerRNA *ptr, PropertyRNA *prop);
void RNA_property_string_get_default(PointerRNA *ptr, PropertyRNA *prop, char *value);
-char *RNA_property_string_get_default_alloc(PointerRNA *ptr,
- PropertyRNA *prop,
- char *fixedbuf,
- int fixedlen);
+char *RNA_property_string_get_default_alloc(
+ PointerRNA *ptr, PropertyRNA *prop, char *fixedbuf, int fixedlen, int *r_len);
int RNA_property_string_default_length(PointerRNA *ptr, PropertyRNA *prop);
int RNA_property_enum_get(PointerRNA *ptr, PropertyRNA *prop);
@@ -1238,7 +1236,8 @@ bool RNA_enum_icon_from_value(const EnumPropertyItem *item, int value, int *r_ic
bool RNA_enum_name_from_value(const EnumPropertyItem *item, int value, const char **r_name);
void RNA_string_get(PointerRNA *ptr, const char *name, char *value);
-char *RNA_string_get_alloc(PointerRNA *ptr, const char *name, char *fixedbuf, int fixedlen);
+char *RNA_string_get_alloc(
+ PointerRNA *ptr, const char *name, char *fixedbuf, int fixedlen, int *r_len);
int RNA_string_length(PointerRNA *ptr, const char *name);
void RNA_string_set(PointerRNA *ptr, const char *name, const char *value);
diff --git a/source/blender/makesrna/intern/rna_access.c b/source/blender/makesrna/intern/rna_access.c
index 41c31dfebcb..c838032f1bb 100644
--- a/source/blender/makesrna/intern/rna_access.c
+++ b/source/blender/makesrna/intern/rna_access.c
@@ -3397,7 +3397,7 @@ char *RNA_property_string_get_alloc(
buf = fixedbuf;
}
else {
- buf = MEM_mallocN(sizeof(char) * (length + 1), "RNA_string_get_alloc");
+ buf = MEM_mallocN(sizeof(char) * (length + 1), __func__);
}
#ifndef NDEBUG
@@ -3537,10 +3537,8 @@ void RNA_property_string_get_default(PointerRNA *UNUSED(ptr), PropertyRNA *prop,
strcpy(value, sprop->defaultvalue);
}
-char *RNA_property_string_get_default_alloc(PointerRNA *ptr,
- PropertyRNA *prop,
- char *fixedbuf,
- int fixedlen)
+char *RNA_property_string_get_default_alloc(
+ PointerRNA *ptr, PropertyRNA *prop, char *fixedbuf, int fixedlen, int *r_len)
{
char *buf;
int length;
@@ -3553,11 +3551,15 @@ char *RNA_property_string_get_default_alloc(PointerRNA *ptr,
buf = fixedbuf;
}
else {
- buf = MEM_callocN(sizeof(char) * (length + 1), "RNA_string_get_alloc");
+ buf = MEM_callocN(sizeof(char) * (length + 1), __func__);
}
RNA_property_string_get_default(ptr, prop, buf);
+ if (r_len) {
+ *r_len = length;
+ }
+
return buf;
}
@@ -6587,15 +6589,18 @@ void RNA_string_get(PointerRNA *ptr, const char *name, char *value)
}
}
-char *RNA_string_get_alloc(PointerRNA *ptr, const char *name, char *fixedbuf, int fixedlen)
+char *RNA_string_get_alloc(
+ PointerRNA *ptr, const char *name, char *fixedbuf, int fixedlen, int *r_len)
{
PropertyRNA *prop = RNA_struct_find_property(ptr, name);
if (prop) {
- /* TODO: pass length. */
- return RNA_property_string_get_alloc(ptr, prop, fixedbuf, fixedlen, NULL);
+ return RNA_property_string_get_alloc(ptr, prop, fixedbuf, fixedlen, r_len);
}
printf("%s: %s.%s not found.\n", __func__, ptr->type->identifier, name);
+ if (r_len != NULL) {
+ *r_len = 0;
+ }
return NULL;
}
@@ -8166,7 +8171,7 @@ bool RNA_property_reset(PointerRNA *ptr, PropertyRNA *prop, int index)
}
case PROP_STRING: {
- char *value = RNA_property_string_get_default_alloc(ptr, prop, NULL, 0);
+ char *value = RNA_property_string_get_default_alloc(ptr, prop, NULL, 0, NULL);
RNA_property_string_set(ptr, prop, value);
MEM_freeN(value);
return true;
diff --git a/source/blender/makesrna/intern/rna_cachefile.c b/source/blender/makesrna/intern/rna_cachefile.c
index cb0a490417d..74d924b8937 100644
--- a/source/blender/makesrna/intern/rna_cachefile.c
+++ b/source/blender/makesrna/intern/rna_cachefile.c
@@ -150,6 +150,23 @@ static void rna_def_cachefile(BlenderRNA *brna)
"determine which file to use in a file sequence");
RNA_def_property_update(prop, 0, "rna_CacheFile_update");
+ /* ----------------- Cache controls ----------------- */
+
+ prop = RNA_def_property(srna, "use_prefetch", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_ui_text(
+ prop,
+ "Use Prefetch",
+ "When enabled, the Cycles Procedural will preload animation data for faster updates");
+ RNA_def_property_update(prop, 0, "rna_CacheFile_update");
+
+ prop = RNA_def_property(srna, "prefetch_cache_size", PROP_INT, PROP_UNSIGNED);
+ RNA_def_property_ui_text(
+ prop,
+ "Prefetch Cache Size",
+ "Memory usage limit in megabytes for the Cycles Procedural cache, if the data does not "
+ "fit within the limit, rendering is aborted");
+ RNA_def_property_update(prop, 0, "rna_CacheFile_update");
+
/* ----------------- Axis Conversion ----------------- */
prop = RNA_def_property(srna, "forward_axis", PROP_ENUM, PROP_NONE);
diff --git a/source/blender/makesrna/intern/rna_main_api.c b/source/blender/makesrna/intern/rna_main_api.c
index 8e6ff961721..9a33849b645 100644
--- a/source/blender/makesrna/intern/rna_main_api.c
+++ b/source/blender/makesrna/intern/rna_main_api.c
@@ -122,7 +122,7 @@
static void rna_idname_validate(const char *name, char *r_name)
{
BLI_strncpy(r_name, name, MAX_ID_NAME - 2);
- BLI_utf8_invalid_strip(r_name, strlen(r_name));
+ BLI_str_utf8_invalid_strip(r_name, strlen(r_name));
}
static void rna_Main_ID_remove(Main *bmain,
diff --git a/source/blender/modifiers/intern/MOD_nodes.cc b/source/blender/modifiers/intern/MOD_nodes.cc
index 57295f5e132..98521b43f52 100644
--- a/source/blender/modifiers/intern/MOD_nodes.cc
+++ b/source/blender/modifiers/intern/MOD_nodes.cc
@@ -85,7 +85,8 @@
#include "NOD_derived_node_tree.hh"
#include "NOD_geometry.h"
#include "NOD_geometry_nodes_eval_log.hh"
-#include "NOD_node_tree_multi_function.hh"
+
+#include "FN_multi_function.hh"
#include "WM_types.h"
@@ -925,7 +926,7 @@ static GeometrySet compute_geometry(const DerivedNodeTree &tree,
{
blender::ResourceScope scope;
blender::LinearAllocator<> &allocator = scope.linear_allocator();
- blender::nodes::MultiFunctionByNode mf_by_node = get_multi_function_per_node(tree, scope);
+ blender::nodes::NodeMultiFunctions mf_by_node{tree, scope};
Map<DOutputSocket, GMutablePointer> group_inputs;
diff --git a/source/blender/modifiers/intern/MOD_nodes_evaluator.cc b/source/blender/modifiers/intern/MOD_nodes_evaluator.cc
index e255922787e..5c529d5295b 100644
--- a/source/blender/modifiers/intern/MOD_nodes_evaluator.cc
+++ b/source/blender/modifiers/intern/MOD_nodes_evaluator.cc
@@ -844,7 +844,7 @@ class GeometryNodesEvaluator {
}
/* Use the multi-function implementation if it exists. */
- const MultiFunction *multi_function = params_.mf_by_node->lookup_default(node, nullptr);
+ const MultiFunction *multi_function = params_.mf_by_node->try_get(node);
if (multi_function != nullptr) {
this->execute_multi_function_node(node, *multi_function, node_state);
return;
diff --git a/source/blender/modifiers/intern/MOD_nodes_evaluator.hh b/source/blender/modifiers/intern/MOD_nodes_evaluator.hh
index d8c60d31986..5151be07aa2 100644
--- a/source/blender/modifiers/intern/MOD_nodes_evaluator.hh
+++ b/source/blender/modifiers/intern/MOD_nodes_evaluator.hh
@@ -20,12 +20,14 @@
#include "NOD_derived_node_tree.hh"
#include "NOD_geometry_nodes_eval_log.hh"
-#include "NOD_node_tree_multi_function.hh"
+#include "NOD_multi_function.hh"
#include "FN_generic_pointer.hh"
#include "DNA_modifier_types.h"
+#include "FN_multi_function.hh"
+
namespace geo_log = blender::nodes::geometry_nodes_eval_log;
namespace blender::modifiers::geometry_nodes {
@@ -45,7 +47,7 @@ struct GeometryNodesEvaluationParams {
* necessary in all cases. Sometimes `log_socket_value_fn` might just want to look at the value
* and then it can be freed. */
Vector<DSocket> force_compute_sockets;
- nodes::MultiFunctionByNode *mf_by_node;
+ nodes::NodeMultiFunctions *mf_by_node;
const NodesModifierData *modifier_;
Depsgraph *depsgraph;
Object *self_object;
diff --git a/source/blender/modifiers/intern/MOD_subsurf.c b/source/blender/modifiers/intern/MOD_subsurf.c
index ce427281db3..db0b769684e 100644
--- a/source/blender/modifiers/intern/MOD_subsurf.c
+++ b/source/blender/modifiers/intern/MOD_subsurf.c
@@ -358,13 +358,8 @@ static bool get_show_adaptive_options(const bContext *C, Panel *panel)
/* Don't show adaptive options if the cycles experimental feature set is disabled. */
Scene *scene = CTX_data_scene(C);
- PointerRNA scene_ptr;
- RNA_id_pointer_create(&scene->id, &scene_ptr);
- if (BKE_scene_uses_cycles(scene)) {
- PointerRNA cycles_ptr = RNA_pointer_get(&scene_ptr, "cycles");
- if (RNA_enum_get(&cycles_ptr, "feature_set") != 1) { /* EXPERIMENTAL */
- return false;
- }
+ if (!BKE_scene_uses_cycles_experimental_features(scene)) {
+ return false;
}
return true;
diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt
index aff21e9d906..9335950a062 100644
--- a/source/blender/nodes/CMakeLists.txt
+++ b/source/blender/nodes/CMakeLists.txt
@@ -357,11 +357,10 @@ set(SRC
intern/node_common.c
intern/node_exec.cc
intern/node_geometry_exec.cc
+ intern/node_multi_function.cc
intern/node_socket.cc
- intern/node_tree_multi_function.cc
intern/node_tree_ref.cc
intern/node_util.c
- intern/type_callbacks.cc
intern/type_conversions.cc
composite/node_composite_util.h
@@ -378,13 +377,12 @@ set(SRC
NOD_geometry_exec.hh
NOD_geometry_nodes_eval_log.hh
NOD_math_functions.hh
- NOD_node_tree_multi_function.hh
+ NOD_multi_function.hh
NOD_node_tree_ref.hh
NOD_shader.h
NOD_socket.h
NOD_static_types.h
NOD_texture.h
- NOD_type_callbacks.hh
NOD_type_conversions.hh
intern/node_common.h
intern/node_exec.h
diff --git a/source/blender/nodes/NOD_multi_function.hh b/source/blender/nodes/NOD_multi_function.hh
new file mode 100644
index 00000000000..2f4b104fb4c
--- /dev/null
+++ b/source/blender/nodes/NOD_multi_function.hh
@@ -0,0 +1,130 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#pragma once
+
+#include "FN_multi_function.hh"
+
+#include "DNA_node_types.h"
+
+#include "NOD_derived_node_tree.hh"
+
+namespace blender::nodes {
+
+using namespace fn::multi_function_types;
+
+class NodeMultiFunctions;
+
+/**
+ * Utility class to help nodes build a multi-function for themselves.
+ */
+class NodeMultiFunctionBuilder : NonCopyable, NonMovable {
+ private:
+ ResourceScope &resource_scope_;
+ bNode &node_;
+ bNodeTree &tree_;
+ const MultiFunction *built_fn_ = nullptr;
+
+ friend NodeMultiFunctions;
+
+ public:
+ NodeMultiFunctionBuilder(ResourceScope &resource_scope, bNode &node, bNodeTree &tree);
+
+ /**
+ * Assign a multi-function for the current node. The input and output parameters of the function
+ * have to match the available sockets in the node.
+ */
+ void set_matching_fn(const MultiFunction *fn);
+ void set_matching_fn(const MultiFunction &fn);
+
+ /**
+ * Utility method for creating and assigning a multi-function when it can't have a static
+ * lifetime.
+ */
+ template<typename T, typename... Args> void construct_and_set_matching_fn(Args &&...args);
+
+ bNode &node();
+ bNodeTree &tree();
+
+ ResourceScope &resource_scope();
+};
+
+/**
+ * Gives access to multi-functions for all nodes in a node tree that support them.
+ */
+class NodeMultiFunctions {
+ private:
+ Map<const bNode *, const MultiFunction *> map_;
+
+ public:
+ NodeMultiFunctions(const DerivedNodeTree &tree, ResourceScope &resource_scope);
+
+ const MultiFunction *try_get(const DNode &node) const;
+};
+
+/* --------------------------------------------------------------------
+ * NodeMultiFunctionBuilder inline methods.
+ */
+
+inline NodeMultiFunctionBuilder::NodeMultiFunctionBuilder(ResourceScope &resource_scope,
+ bNode &node,
+ bNodeTree &tree)
+ : resource_scope_(resource_scope), node_(node), tree_(tree)
+{
+}
+
+inline bNode &NodeMultiFunctionBuilder::node()
+{
+ return node_;
+}
+
+inline bNodeTree &NodeMultiFunctionBuilder::tree()
+{
+ return tree_;
+}
+
+inline ResourceScope &NodeMultiFunctionBuilder::resource_scope()
+{
+ return resource_scope_;
+}
+
+inline void NodeMultiFunctionBuilder::set_matching_fn(const MultiFunction *fn)
+{
+ built_fn_ = fn;
+}
+
+inline void NodeMultiFunctionBuilder::set_matching_fn(const MultiFunction &fn)
+{
+ this->set_matching_fn(&fn);
+}
+
+template<typename T, typename... Args>
+inline void NodeMultiFunctionBuilder::construct_and_set_matching_fn(Args &&...args)
+{
+ const T &fn = resource_scope_.construct<T>(__func__, std::forward<Args>(args)...);
+ this->set_matching_fn(&fn);
+}
+
+/* --------------------------------------------------------------------
+ * NodeMultiFunctions inline methods.
+ */
+
+inline const MultiFunction *NodeMultiFunctions::try_get(const DNode &node) const
+{
+ return map_.lookup_default(node->bnode(), nullptr);
+}
+
+} // namespace blender::nodes
diff --git a/source/blender/nodes/NOD_node_tree_multi_function.hh b/source/blender/nodes/NOD_node_tree_multi_function.hh
deleted file mode 100644
index 7eeeaef0b98..00000000000
--- a/source/blender/nodes/NOD_node_tree_multi_function.hh
+++ /dev/null
@@ -1,390 +0,0 @@
-/*
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-
-#pragma once
-
-/** \file
- * \ingroup nodes
- *
- * This file allows you to generate a multi-function network from a user-generated node tree.
- */
-
-#include "FN_multi_function_builder.hh"
-#include "FN_multi_function_network.hh"
-
-#include "NOD_derived_node_tree.hh"
-#include "NOD_type_callbacks.hh"
-
-#include "BLI_multi_value_map.hh"
-#include "BLI_resource_scope.hh"
-
-namespace blender::nodes {
-
-/**
- * A MFNetworkTreeMap maps various components of a node tree to components of a fn::MFNetwork. This
- * is necessary for further processing of a multi-function network that has been generated from a
- * node tree.
- */
-class MFNetworkTreeMap {
- private:
- /**
- * Store by id instead of using a hash table to avoid unnecessary hash table lookups.
- *
- * Input sockets in a node tree can have multiple corresponding sockets in the generated
- * MFNetwork. This is because nodes are allowed to expand into multiple multi-function nodes.
- */
- const DerivedNodeTree &tree_;
- fn::MFNetwork &network_;
- MultiValueMap<DSocket, fn::MFSocket *> sockets_by_dsocket_;
-
- public:
- MFNetworkTreeMap(const DerivedNodeTree &tree, fn::MFNetwork &network)
- : tree_(tree), network_(network)
- {
- }
-
- const DerivedNodeTree &tree() const
- {
- return tree_;
- }
-
- const fn::MFNetwork &network() const
- {
- return network_;
- }
-
- fn::MFNetwork &network()
- {
- return network_;
- }
-
- void add(const DSocket &dsocket, fn::MFSocket &socket)
- {
- BLI_assert(dsocket->is_input() == socket.is_input());
- BLI_assert(dsocket->is_input() || sockets_by_dsocket_.lookup(dsocket).is_empty());
- sockets_by_dsocket_.add(dsocket, &socket);
- }
-
- void add(const DInputSocket &dsocket, fn::MFInputSocket &socket)
- {
- sockets_by_dsocket_.add(dsocket, &socket);
- }
-
- void add(const DOutputSocket &dsocket, fn::MFOutputSocket &socket)
- {
- /* There can be at most one matching output socket. */
- BLI_assert(sockets_by_dsocket_.lookup(dsocket).is_empty());
- sockets_by_dsocket_.add(dsocket, &socket);
- }
-
- void add(const DTreeContext &context,
- Span<const InputSocketRef *> dsockets,
- Span<fn::MFInputSocket *> sockets)
- {
- assert_same_size(dsockets, sockets);
- for (int i : dsockets.index_range()) {
- this->add(DInputSocket(&context, dsockets[i]), *sockets[i]);
- }
- }
-
- void add(const DTreeContext &context,
- Span<const OutputSocketRef *> dsockets,
- Span<fn::MFOutputSocket *> sockets)
- {
- assert_same_size(dsockets, sockets);
- for (int i : dsockets.index_range()) {
- this->add(DOutputSocket(&context, dsockets[i]), *sockets[i]);
- }
- }
-
- void add_try_match(const DNode &dnode, fn::MFNode &node)
- {
- this->add_try_match(*dnode.context(),
- dnode->inputs().cast<const SocketRef *>(),
- node.inputs().cast<fn::MFSocket *>());
- this->add_try_match(*dnode.context(),
- dnode->outputs().cast<const SocketRef *>(),
- node.outputs().cast<fn::MFSocket *>());
- }
-
- void add_try_match(const DTreeContext &context,
- Span<const InputSocketRef *> dsockets,
- Span<fn::MFInputSocket *> sockets)
- {
- this->add_try_match(
- context, dsockets.cast<const SocketRef *>(), sockets.cast<fn::MFSocket *>());
- }
-
- void add_try_match(const DTreeContext &context,
- Span<const OutputSocketRef *> dsockets,
- Span<fn::MFOutputSocket *> sockets)
- {
- this->add_try_match(
- context, dsockets.cast<const SocketRef *>(), sockets.cast<fn::MFSocket *>());
- }
-
- void add_try_match(const DTreeContext &context,
- Span<const SocketRef *> dsockets,
- Span<fn::MFSocket *> sockets)
- {
- int used_sockets = 0;
- for (const SocketRef *dsocket : dsockets) {
- if (!dsocket->is_available()) {
- continue;
- }
- if (!socket_is_mf_data_socket(*dsocket->typeinfo())) {
- continue;
- }
- fn::MFSocket *socket = sockets[used_sockets];
- this->add(DSocket(&context, dsocket), *socket);
- used_sockets++;
- }
- }
-
- fn::MFOutputSocket &lookup(const DOutputSocket &dsocket)
- {
- return sockets_by_dsocket_.lookup(dsocket)[0]->as_output();
- }
-
- Span<fn::MFInputSocket *> lookup(const DInputSocket &dsocket)
- {
- return sockets_by_dsocket_.lookup(dsocket).cast<fn::MFInputSocket *>();
- }
-
- fn::MFInputSocket &lookup_dummy(const DInputSocket &dsocket)
- {
- Span<fn::MFInputSocket *> sockets = this->lookup(dsocket);
- BLI_assert(sockets.size() == 1);
- fn::MFInputSocket &socket = *sockets[0];
- BLI_assert(socket.node().is_dummy());
- return socket;
- }
-
- fn::MFOutputSocket &lookup_dummy(const DOutputSocket &dsocket)
- {
- fn::MFOutputSocket &socket = this->lookup(dsocket);
- BLI_assert(socket.node().is_dummy());
- return socket;
- }
-
- bool is_mapped(const DSocket &dsocket) const
- {
- return !sockets_by_dsocket_.lookup(dsocket).is_empty();
- }
-};
-
-/**
- * This data is necessary throughout the generation of a MFNetwork from a node tree.
- */
-struct CommonMFNetworkBuilderData {
- ResourceScope &scope;
- fn::MFNetwork &network;
- MFNetworkTreeMap &network_map;
- const DerivedNodeTree &tree;
-};
-
-class MFNetworkBuilderBase {
- protected:
- CommonMFNetworkBuilderData &common_;
-
- public:
- MFNetworkBuilderBase(CommonMFNetworkBuilderData &common) : common_(common)
- {
- }
-
- /**
- * Returns the network that is currently being built.
- */
- fn::MFNetwork &network()
- {
- return common_.network;
- }
-
- /**
- * Returns the map between the node tree and the multi-function network that is being built.
- */
- MFNetworkTreeMap &network_map()
- {
- return common_.network_map;
- }
-
- /**
- * Returns a resource collector that will only be destructed after the multi-function network is
- * destructed.
- */
- ResourceScope &resource_scope()
- {
- return common_.scope;
- }
-
- /**
- * Constructs a new function that will live at least as long as the MFNetwork.
- */
- template<typename T, typename... Args> T &construct_fn(Args &&...args)
- {
- BLI_STATIC_ASSERT((std::is_base_of_v<fn::MultiFunction, T>), "");
- void *buffer = common_.scope.linear_allocator().allocate(sizeof(T), alignof(T));
- T *fn = new (buffer) T(std::forward<Args>(args)...);
- common_.scope.add(destruct_ptr<T>(fn), fn->name().c_str());
- return *fn;
- }
-};
-
-/**
- * This class is used by socket implementations to define how an unlinked input socket is handled
- * in a multi-function network.
- */
-class SocketMFNetworkBuilder : public MFNetworkBuilderBase {
- private:
- bNodeSocket *bsocket_;
- fn::MFOutputSocket *built_socket_ = nullptr;
-
- public:
- SocketMFNetworkBuilder(CommonMFNetworkBuilderData &common, const DSocket &dsocket)
- : MFNetworkBuilderBase(common), bsocket_(dsocket->bsocket())
- {
- }
-
- /**
- * Returns the socket that is currently being built.
- */
- bNodeSocket &bsocket()
- {
- return *bsocket_;
- }
-
- /**
- * Utility method that returns bsocket->default_value for the current socket.
- */
- template<typename T> T *socket_default_value()
- {
- return static_cast<T *>(bsocket_->default_value);
- }
-
- /**
- * Builds a function node for that socket that outputs the given constant value.
- */
- template<typename T> void set_constant_value(T value)
- {
- this->construct_generator_fn<fn::CustomMF_Constant<T>>(std::move(value));
- }
- void set_constant_value(const CPPType &type, const void *value)
- {
- /* The value has live as long as the generated mf network. */
- this->construct_generator_fn<fn::CustomMF_GenericConstant>(type, value);
- }
-
- template<typename T, typename... Args> void construct_generator_fn(Args &&...args)
- {
- const fn::MultiFunction &fn = this->construct_fn<T>(std::forward<Args>(args)...);
- this->set_generator_fn(fn);
- }
-
- /**
- * Uses the first output of the given multi-function as value of the socket.
- */
- void set_generator_fn(const fn::MultiFunction &fn)
- {
- fn::MFFunctionNode &node = common_.network.add_function(fn);
- this->set_socket(node.output(0));
- }
-
- /**
- * Define a multi-function socket that outputs the value of the bsocket.
- */
- void set_socket(fn::MFOutputSocket &socket)
- {
- built_socket_ = &socket;
- }
-
- fn::MFOutputSocket *built_socket()
- {
- return built_socket_;
- }
-};
-
-/**
- * This class is used by node implementations to define how a user-level node expands into
- * multi-function nodes internally.
- */
-class NodeMFNetworkBuilder : public MFNetworkBuilderBase {
- private:
- DNode dnode_;
-
- public:
- NodeMFNetworkBuilder(CommonMFNetworkBuilderData &common, DNode dnode)
- : MFNetworkBuilderBase(common), dnode_(dnode)
- {
- }
-
- /**
- * Tells the builder to build a function that corresponds to the node that is being built. It
- * will try to match up sockets.
- */
- template<typename T, typename... Args> T &construct_and_set_matching_fn(Args &&...args)
- {
- T &function = this->construct_fn<T>(std::forward<Args>(args)...);
- this->set_matching_fn(function);
- return function;
- }
-
- const fn::MultiFunction &get_not_implemented_fn()
- {
- return this->get_default_fn("Not Implemented (" + dnode_->name() + ")");
- }
-
- const fn::MultiFunction &get_default_fn(StringRef name);
-
- const void set_not_implemented()
- {
- this->set_matching_fn(this->get_not_implemented_fn());
- }
-
- /**
- * Tells the builder that the given function corresponds to the node that is being built. It will
- * try to match up sockets. For that it skips unavailable and non-data sockets.
- */
- void set_matching_fn(const fn::MultiFunction &function)
- {
- fn::MFFunctionNode &node = common_.network.add_function(function);
- common_.network_map.add_try_match(dnode_, node);
- }
-
- /**
- * Returns the node that is currently being built.
- */
- bNode &bnode()
- {
- return *dnode_->bnode();
- }
-
- /**
- * Returns the node that is currently being built.
- */
- const DNode &dnode() const
- {
- return dnode_;
- }
-};
-
-MFNetworkTreeMap insert_node_tree_into_mf_network(fn::MFNetwork &network,
- const DerivedNodeTree &tree,
- ResourceScope &scope);
-
-using MultiFunctionByNode = Map<DNode, const fn::MultiFunction *>;
-MultiFunctionByNode get_multi_function_per_node(const DerivedNodeTree &tree, ResourceScope &scope);
-
-} // namespace blender::nodes
diff --git a/source/blender/nodes/composite/node_composite_tree.c b/source/blender/nodes/composite/node_composite_tree.c
index d21e0938356..cc657d6f91d 100644
--- a/source/blender/nodes/composite/node_composite_tree.c
+++ b/source/blender/nodes/composite/node_composite_tree.c
@@ -67,7 +67,7 @@ static void foreach_nodeclass(Scene *UNUSED(scene), void *calldata, bNodeClassCa
func(calldata, NODE_CLASS_OP_COLOR, N_("Color"));
func(calldata, NODE_CLASS_OP_VECTOR, N_("Vector"));
func(calldata, NODE_CLASS_OP_FILTER, N_("Filter"));
- func(calldata, NODE_CLASS_CONVERTOR, N_("Convertor"));
+ func(calldata, NODE_CLASS_CONVERTER, N_("Converter"));
func(calldata, NODE_CLASS_MATTE, N_("Matte"));
func(calldata, NODE_CLASS_DISTORT, N_("Distort"));
func(calldata, NODE_CLASS_GROUP, N_("Group"));
diff --git a/source/blender/nodes/composite/nodes/node_composite_idMask.c b/source/blender/nodes/composite/nodes/node_composite_idMask.c
index a81dce10284..84563e7560b 100644
--- a/source/blender/nodes/composite/nodes/node_composite_idMask.c
+++ b/source/blender/nodes/composite/nodes/node_composite_idMask.c
@@ -38,7 +38,7 @@ void register_node_type_cmp_idmask(void)
{
static bNodeType ntype;
- cmp_node_type_base(&ntype, CMP_NODE_ID_MASK, "ID Mask", NODE_CLASS_CONVERTOR, 0);
+ cmp_node_type_base(&ntype, CMP_NODE_ID_MASK, "ID Mask", NODE_CLASS_CONVERTER, 0);
node_type_socket_templates(&ntype, cmp_node_idmask_in, cmp_node_idmask_out);
nodeRegisterType(&ntype);
diff --git a/source/blender/nodes/composite/nodes/node_composite_math.c b/source/blender/nodes/composite/nodes/node_composite_math.c
index c2982964a9f..2191c6bcdc3 100644
--- a/source/blender/nodes/composite/nodes/node_composite_math.c
+++ b/source/blender/nodes/composite/nodes/node_composite_math.c
@@ -36,7 +36,7 @@ void register_node_type_cmp_math(void)
{
static bNodeType ntype;
- cmp_node_type_base(&ntype, CMP_NODE_MATH, "Math", NODE_CLASS_CONVERTOR, 0);
+ cmp_node_type_base(&ntype, CMP_NODE_MATH, "Math", NODE_CLASS_CONVERTER, 0);
node_type_socket_templates(&ntype, cmp_node_math_in, cmp_node_math_out);
node_type_label(&ntype, node_math_label);
node_type_update(&ntype, node_math_update);
diff --git a/source/blender/nodes/composite/nodes/node_composite_premulkey.c b/source/blender/nodes/composite/nodes/node_composite_premulkey.c
index b5dc15a2789..be76bbf01cf 100644
--- a/source/blender/nodes/composite/nodes/node_composite_premulkey.c
+++ b/source/blender/nodes/composite/nodes/node_composite_premulkey.c
@@ -38,7 +38,7 @@ void register_node_type_cmp_premulkey(void)
{
static bNodeType ntype;
- cmp_node_type_base(&ntype, CMP_NODE_PREMULKEY, "Alpha Convert", NODE_CLASS_CONVERTOR, 0);
+ cmp_node_type_base(&ntype, CMP_NODE_PREMULKEY, "Alpha Convert", NODE_CLASS_CONVERTER, 0);
node_type_socket_templates(&ntype, cmp_node_premulkey_in, cmp_node_premulkey_out);
nodeRegisterType(&ntype);
diff --git a/source/blender/nodes/composite/nodes/node_composite_sepcombHSVA.c b/source/blender/nodes/composite/nodes/node_composite_sepcombHSVA.c
index 03d27caa701..001b197e23a 100644
--- a/source/blender/nodes/composite/nodes/node_composite_sepcombHSVA.c
+++ b/source/blender/nodes/composite/nodes/node_composite_sepcombHSVA.c
@@ -40,7 +40,7 @@ void register_node_type_cmp_sephsva(void)
{
static bNodeType ntype;
- cmp_node_type_base(&ntype, CMP_NODE_SEPHSVA, "Separate HSVA", NODE_CLASS_CONVERTOR, 0);
+ cmp_node_type_base(&ntype, CMP_NODE_SEPHSVA, "Separate HSVA", NODE_CLASS_CONVERTER, 0);
node_type_socket_templates(&ntype, cmp_node_sephsva_in, cmp_node_sephsva_out);
nodeRegisterType(&ntype);
@@ -63,7 +63,7 @@ void register_node_type_cmp_combhsva(void)
{
static bNodeType ntype;
- cmp_node_type_base(&ntype, CMP_NODE_COMBHSVA, "Combine HSVA", NODE_CLASS_CONVERTOR, 0);
+ cmp_node_type_base(&ntype, CMP_NODE_COMBHSVA, "Combine HSVA", NODE_CLASS_CONVERTER, 0);
node_type_socket_templates(&ntype, cmp_node_combhsva_in, cmp_node_combhsva_out);
nodeRegisterType(&ntype);
diff --git a/source/blender/nodes/composite/nodes/node_composite_sepcombRGBA.c b/source/blender/nodes/composite/nodes/node_composite_sepcombRGBA.c
index ee8d6b0c186..e08f27db254 100644
--- a/source/blender/nodes/composite/nodes/node_composite_sepcombRGBA.c
+++ b/source/blender/nodes/composite/nodes/node_composite_sepcombRGBA.c
@@ -40,7 +40,7 @@ void register_node_type_cmp_seprgba(void)
{
static bNodeType ntype;
- cmp_node_type_base(&ntype, CMP_NODE_SEPRGBA, "Separate RGBA", NODE_CLASS_CONVERTOR, 0);
+ cmp_node_type_base(&ntype, CMP_NODE_SEPRGBA, "Separate RGBA", NODE_CLASS_CONVERTER, 0);
node_type_socket_templates(&ntype, cmp_node_seprgba_in, cmp_node_seprgba_out);
nodeRegisterType(&ntype);
@@ -63,7 +63,7 @@ void register_node_type_cmp_combrgba(void)
{
static bNodeType ntype;
- cmp_node_type_base(&ntype, CMP_NODE_COMBRGBA, "Combine RGBA", NODE_CLASS_CONVERTOR, 0);
+ cmp_node_type_base(&ntype, CMP_NODE_COMBRGBA, "Combine RGBA", NODE_CLASS_CONVERTER, 0);
node_type_socket_templates(&ntype, cmp_node_combrgba_in, cmp_node_combrgba_out);
nodeRegisterType(&ntype);
diff --git a/source/blender/nodes/composite/nodes/node_composite_sepcombYCCA.c b/source/blender/nodes/composite/nodes/node_composite_sepcombYCCA.c
index a4aa6e4bf77..b3884296600 100644
--- a/source/blender/nodes/composite/nodes/node_composite_sepcombYCCA.c
+++ b/source/blender/nodes/composite/nodes/node_composite_sepcombYCCA.c
@@ -43,7 +43,7 @@ void register_node_type_cmp_sepycca(void)
{
static bNodeType ntype;
- cmp_node_type_base(&ntype, CMP_NODE_SEPYCCA, "Separate YCbCrA", NODE_CLASS_CONVERTOR, 0);
+ cmp_node_type_base(&ntype, CMP_NODE_SEPYCCA, "Separate YCbCrA", NODE_CLASS_CONVERTER, 0);
node_type_socket_templates(&ntype, cmp_node_sepycca_in, cmp_node_sepycca_out);
node_type_init(&ntype, node_composit_init_mode_sepycca);
@@ -72,7 +72,7 @@ void register_node_type_cmp_combycca(void)
{
static bNodeType ntype;
- cmp_node_type_base(&ntype, CMP_NODE_COMBYCCA, "Combine YCbCrA", NODE_CLASS_CONVERTOR, 0);
+ cmp_node_type_base(&ntype, CMP_NODE_COMBYCCA, "Combine YCbCrA", NODE_CLASS_CONVERTER, 0);
node_type_socket_templates(&ntype, cmp_node_combycca_in, cmp_node_combycca_out);
node_type_init(&ntype, node_composit_init_mode_combycca);
diff --git a/source/blender/nodes/composite/nodes/node_composite_sepcombYUVA.c b/source/blender/nodes/composite/nodes/node_composite_sepcombYUVA.c
index 6c416b4f485..4da79ec7981 100644
--- a/source/blender/nodes/composite/nodes/node_composite_sepcombYUVA.c
+++ b/source/blender/nodes/composite/nodes/node_composite_sepcombYUVA.c
@@ -38,7 +38,7 @@ void register_node_type_cmp_sepyuva(void)
{
static bNodeType ntype;
- cmp_node_type_base(&ntype, CMP_NODE_SEPYUVA, "Separate YUVA", NODE_CLASS_CONVERTOR, 0);
+ cmp_node_type_base(&ntype, CMP_NODE_SEPYUVA, "Separate YUVA", NODE_CLASS_CONVERTER, 0);
node_type_socket_templates(&ntype, cmp_node_sepyuva_in, cmp_node_sepyuva_out);
nodeRegisterType(&ntype);
@@ -61,7 +61,7 @@ void register_node_type_cmp_combyuva(void)
{
static bNodeType ntype;
- cmp_node_type_base(&ntype, CMP_NODE_COMBYUVA, "Combine YUVA", NODE_CLASS_CONVERTOR, 0);
+ cmp_node_type_base(&ntype, CMP_NODE_COMBYUVA, "Combine YUVA", NODE_CLASS_CONVERTER, 0);
node_type_socket_templates(&ntype, cmp_node_combyuva_in, cmp_node_combyuva_out);
nodeRegisterType(&ntype);
diff --git a/source/blender/nodes/composite/nodes/node_composite_setalpha.c b/source/blender/nodes/composite/nodes/node_composite_setalpha.c
index 1488ff1a25f..1b44cc011e9 100644
--- a/source/blender/nodes/composite/nodes/node_composite_setalpha.c
+++ b/source/blender/nodes/composite/nodes/node_composite_setalpha.c
@@ -45,7 +45,7 @@ void register_node_type_cmp_setalpha(void)
{
static bNodeType ntype;
- cmp_node_type_base(&ntype, CMP_NODE_SETALPHA, "Set Alpha", NODE_CLASS_CONVERTOR, 0);
+ cmp_node_type_base(&ntype, CMP_NODE_SETALPHA, "Set Alpha", NODE_CLASS_CONVERTER, 0);
node_type_socket_templates(&ntype, cmp_node_setalpha_in, cmp_node_setalpha_out);
node_type_init(&ntype, node_composit_init_setalpha);
node_type_storage(
diff --git a/source/blender/nodes/composite/nodes/node_composite_switchview.c b/source/blender/nodes/composite/nodes/node_composite_switchview.c
index ec5c79cc087..b09d5119bc4 100644
--- a/source/blender/nodes/composite/nodes/node_composite_switchview.c
+++ b/source/blender/nodes/composite/nodes/node_composite_switchview.c
@@ -146,7 +146,7 @@ void register_node_type_cmp_switch_view(void)
{
static bNodeType ntype;
- cmp_node_type_base(&ntype, CMP_NODE_SWITCH_VIEW, "Switch View", NODE_CLASS_CONVERTOR, 0);
+ cmp_node_type_base(&ntype, CMP_NODE_SWITCH_VIEW, "Switch View", NODE_CLASS_CONVERTER, 0);
node_type_socket_templates(&ntype, NULL, cmp_node_switch_view_out);
ntype.initfunc_api = init_switch_view;
diff --git a/source/blender/nodes/composite/nodes/node_composite_valToRgb.c b/source/blender/nodes/composite/nodes/node_composite_valToRgb.c
index 0d46341b610..ed6dbfa2bf3 100644
--- a/source/blender/nodes/composite/nodes/node_composite_valToRgb.c
+++ b/source/blender/nodes/composite/nodes/node_composite_valToRgb.c
@@ -43,7 +43,7 @@ void register_node_type_cmp_valtorgb(void)
{
static bNodeType ntype;
- cmp_node_type_base(&ntype, CMP_NODE_VALTORGB, "ColorRamp", NODE_CLASS_CONVERTOR, 0);
+ cmp_node_type_base(&ntype, CMP_NODE_VALTORGB, "ColorRamp", NODE_CLASS_CONVERTER, 0);
node_type_socket_templates(&ntype, cmp_node_valtorgb_in, cmp_node_valtorgb_out);
node_type_size(&ntype, 240, 200, 320);
node_type_init(&ntype, node_composit_init_valtorgb);
@@ -66,7 +66,7 @@ void register_node_type_cmp_rgbtobw(void)
{
static bNodeType ntype;
- cmp_node_type_base(&ntype, CMP_NODE_RGBTOBW, "RGB to BW", NODE_CLASS_CONVERTOR, 0);
+ cmp_node_type_base(&ntype, CMP_NODE_RGBTOBW, "RGB to BW", NODE_CLASS_CONVERTER, 0);
node_type_socket_templates(&ntype, cmp_node_rgbtobw_in, cmp_node_rgbtobw_out);
node_type_size_preset(&ntype, NODE_SIZE_SMALL);
diff --git a/source/blender/nodes/function/node_function_util.hh b/source/blender/nodes/function/node_function_util.hh
index 9fbd6712827..96a8f29c3e9 100644
--- a/source/blender/nodes/function/node_function_util.hh
+++ b/source/blender/nodes/function/node_function_util.hh
@@ -30,7 +30,7 @@
#include "BLT_translation.h"
#include "NOD_function.h"
-#include "NOD_node_tree_multi_function.hh"
+#include "NOD_multi_function.hh"
#include "node_util.h"
diff --git a/source/blender/nodes/function/nodes/node_fn_boolean_math.cc b/source/blender/nodes/function/nodes/node_fn_boolean_math.cc
index 7a83ff8e016..58e7d82beea 100644
--- a/source/blender/nodes/function/nodes/node_fn_boolean_math.cc
+++ b/source/blender/nodes/function/nodes/node_fn_boolean_math.cc
@@ -58,7 +58,7 @@ static void node_boolean_math_label(bNodeTree *UNUSED(ntree), bNode *node, char
BLI_strncpy(label, IFACE_(name), maxlen);
}
-static const blender::fn::MultiFunction &get_multi_function(bNode &bnode)
+static const blender::fn::MultiFunction *get_multi_function(bNode &bnode)
{
static blender::fn::CustomMF_SI_SI_SO<bool, bool, bool> and_fn{
"And", [](bool a, bool b) { return a && b; }};
@@ -68,20 +68,21 @@ static const blender::fn::MultiFunction &get_multi_function(bNode &bnode)
switch (bnode.custom1) {
case NODE_BOOLEAN_MATH_AND:
- return and_fn;
+ return &and_fn;
case NODE_BOOLEAN_MATH_OR:
- return or_fn;
+ return &or_fn;
case NODE_BOOLEAN_MATH_NOT:
- return not_fn;
+ return &not_fn;
}
- BLI_assert(false);
- return blender::fn::dummy_multi_function;
+ BLI_assert_unreachable();
+ return nullptr;
}
-static void node_boolean_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder)
+static void fn_node_boolean_math_build_multi_function(
+ blender::nodes::NodeMultiFunctionBuilder &builder)
{
- const blender::fn::MultiFunction &fn = get_multi_function(builder.bnode());
+ const blender::fn::MultiFunction *fn = get_multi_function(builder.node());
builder.set_matching_fn(fn);
}
@@ -89,11 +90,11 @@ void register_node_type_fn_boolean_math()
{
static bNodeType ntype;
- fn_node_type_base(&ntype, FN_NODE_BOOLEAN_MATH, "Boolean Math", NODE_CLASS_CONVERTOR, 0);
+ fn_node_type_base(&ntype, FN_NODE_BOOLEAN_MATH, "Boolean Math", NODE_CLASS_CONVERTER, 0);
node_type_socket_templates(&ntype, fn_node_boolean_math_in, fn_node_boolean_math_out);
node_type_label(&ntype, node_boolean_math_label);
node_type_update(&ntype, node_boolean_math_update);
- ntype.expand_in_mf_network = node_boolean_expand_in_mf_network;
+ ntype.build_multi_function = fn_node_boolean_math_build_multi_function;
ntype.draw_buttons = fn_node_boolean_math_layout;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/function/nodes/node_fn_float_compare.cc b/source/blender/nodes/function/nodes/node_fn_float_compare.cc
index 6c8df8f2ea0..918dd24e520 100644
--- a/source/blender/nodes/function/nodes/node_fn_float_compare.cc
+++ b/source/blender/nodes/function/nodes/node_fn_float_compare.cc
@@ -64,7 +64,7 @@ static void node_float_compare_label(bNodeTree *UNUSED(ntree),
BLI_strncpy(label, IFACE_(name), maxlen);
}
-static const blender::fn::MultiFunction &get_multi_function(bNode &node)
+static const blender::fn::MultiFunction *get_multi_function(bNode &node)
{
static blender::fn::CustomMF_SI_SI_SO<float, float, bool> less_than_fn{
"Less Than", [](float a, float b) { return a < b; }};
@@ -81,26 +81,27 @@ static const blender::fn::MultiFunction &get_multi_function(bNode &node)
switch (node.custom1) {
case NODE_FLOAT_COMPARE_LESS_THAN:
- return less_than_fn;
+ return &less_than_fn;
case NODE_FLOAT_COMPARE_LESS_EQUAL:
- return less_equal_fn;
+ return &less_equal_fn;
case NODE_FLOAT_COMPARE_GREATER_THAN:
- return greater_than_fn;
+ return &greater_than_fn;
case NODE_FLOAT_COMPARE_GREATER_EQUAL:
- return greater_equal_fn;
+ return &greater_equal_fn;
case NODE_FLOAT_COMPARE_EQUAL:
- return equal_fn;
+ return &equal_fn;
case NODE_FLOAT_COMPARE_NOT_EQUAL:
- return not_equal_fn;
+ return &not_equal_fn;
}
- BLI_assert(false);
- return blender::fn::dummy_multi_function;
+ BLI_assert_unreachable();
+ return nullptr;
}
-static void node_float_compare_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder)
+static void fn_node_float_compare_build_multi_function(
+ blender::nodes::NodeMultiFunctionBuilder &builder)
{
- const blender::fn::MultiFunction &fn = get_multi_function(builder.bnode());
+ const blender::fn::MultiFunction *fn = get_multi_function(builder.node());
builder.set_matching_fn(fn);
}
@@ -108,11 +109,11 @@ void register_node_type_fn_float_compare()
{
static bNodeType ntype;
- fn_node_type_base(&ntype, FN_NODE_FLOAT_COMPARE, "Float Compare", NODE_CLASS_CONVERTOR, 0);
+ fn_node_type_base(&ntype, FN_NODE_FLOAT_COMPARE, "Float Compare", NODE_CLASS_CONVERTER, 0);
node_type_socket_templates(&ntype, fn_node_float_compare_in, fn_node_float_compare_out);
node_type_label(&ntype, node_float_compare_label);
node_type_update(&ntype, node_float_compare_update);
- ntype.expand_in_mf_network = node_float_compare_expand_in_mf_network;
+ ntype.build_multi_function = fn_node_float_compare_build_multi_function;
ntype.draw_buttons = geo_node_float_compare_layout;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/function/nodes/node_fn_float_to_int.cc b/source/blender/nodes/function/nodes/node_fn_float_to_int.cc
index 26cde576400..40b8f27f895 100644
--- a/source/blender/nodes/function/nodes/node_fn_float_to_int.cc
+++ b/source/blender/nodes/function/nodes/node_fn_float_to_int.cc
@@ -50,7 +50,7 @@ static void node_float_to_int_label(bNodeTree *UNUSED(ntree), bNode *node, char
BLI_strncpy(label, IFACE_(name), maxlen);
}
-static const blender::fn::MultiFunction &get_multi_function(bNode &bnode)
+static const blender::fn::MultiFunction *get_multi_function(bNode &bnode)
{
static blender::fn::CustomMF_SI_SO<float, int> round_fn{"Round",
[](float a) { return (int)round(a); }};
@@ -63,22 +63,23 @@ static const blender::fn::MultiFunction &get_multi_function(bNode &bnode)
switch (static_cast<FloatToIntRoundingMode>(bnode.custom1)) {
case FN_NODE_FLOAT_TO_INT_ROUND:
- return round_fn;
+ return &round_fn;
case FN_NODE_FLOAT_TO_INT_FLOOR:
- return floor_fn;
+ return &floor_fn;
case FN_NODE_FLOAT_TO_INT_CEIL:
- return ceil_fn;
+ return &ceil_fn;
case FN_NODE_FLOAT_TO_INT_TRUNCATE:
- return trunc_fn;
+ return &trunc_fn;
}
BLI_assert_unreachable();
- return blender::fn::dummy_multi_function;
+ return nullptr;
}
-static void node_float_to_int_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder)
+static void fn_node_float_to_int_build_multi_function(
+ blender::nodes::NodeMultiFunctionBuilder &builder)
{
- const blender::fn::MultiFunction &fn = get_multi_function(builder.bnode());
+ const blender::fn::MultiFunction *fn = get_multi_function(builder.node());
builder.set_matching_fn(fn);
}
@@ -86,10 +87,10 @@ void register_node_type_fn_float_to_int()
{
static bNodeType ntype;
- fn_node_type_base(&ntype, FN_NODE_FLOAT_TO_INT, "Float to Integer", NODE_CLASS_CONVERTOR, 0);
+ fn_node_type_base(&ntype, FN_NODE_FLOAT_TO_INT, "Float to Integer", NODE_CLASS_CONVERTER, 0);
node_type_socket_templates(&ntype, fn_node_float_to_int_in, fn_node_float_to_int_out);
node_type_label(&ntype, node_float_to_int_label);
- ntype.expand_in_mf_network = node_float_to_int_expand_in_mf_network;
+ ntype.build_multi_function = fn_node_float_to_int_build_multi_function;
ntype.draw_buttons = fn_node_float_to_int_layout;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/function/nodes/node_fn_input_string.cc b/source/blender/nodes/function/nodes/node_fn_input_string.cc
index f16bdef2f38..560ace57aba 100644
--- a/source/blender/nodes/function/nodes/node_fn_input_string.cc
+++ b/source/blender/nodes/function/nodes/node_fn_input_string.cc
@@ -29,14 +29,14 @@ static void fn_node_input_string_layout(uiLayout *layout, bContext *UNUSED(C), P
uiItemR(layout, ptr, "string", 0, "", ICON_NONE);
}
-static void fn_node_input_string_expand_in_mf_network(
- blender::nodes::NodeMFNetworkBuilder &builder)
+static void fn_node_input_string_build_multi_function(
+ blender::nodes::NodeMultiFunctionBuilder &builder)
{
- bNode &bnode = builder.bnode();
+ bNode &bnode = builder.node();
NodeInputString *node_storage = static_cast<NodeInputString *>(bnode.storage);
std::string string = std::string((node_storage->string) ? node_storage->string : "");
-
- builder.construct_and_set_matching_fn<blender::fn::CustomMF_Constant<std::string>>(string);
+ builder.construct_and_set_matching_fn<blender::fn::CustomMF_Constant<std::string>>(
+ std::move(string));
}
static void fn_node_input_string_init(bNodeTree *UNUSED(ntree), bNode *node)
@@ -78,7 +78,7 @@ void register_node_type_fn_input_string()
node_type_socket_templates(&ntype, nullptr, fn_node_input_string_out);
node_type_init(&ntype, fn_node_input_string_init);
node_type_storage(&ntype, "NodeInputString", fn_node_input_string_free, fn_node_string_copy);
- ntype.expand_in_mf_network = fn_node_input_string_expand_in_mf_network;
+ ntype.build_multi_function = fn_node_input_string_build_multi_function;
ntype.draw_buttons = fn_node_input_string_layout;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/function/nodes/node_fn_input_vector.cc b/source/blender/nodes/function/nodes/node_fn_input_vector.cc
index 2cd4eb1d9df..244c045de9a 100644
--- a/source/blender/nodes/function/nodes/node_fn_input_vector.cc
+++ b/source/blender/nodes/function/nodes/node_fn_input_vector.cc
@@ -32,16 +32,14 @@ static void fn_node_input_vector_layout(uiLayout *layout, bContext *UNUSED(C), P
uiItemR(col, ptr, "vector", UI_ITEM_R_EXPAND, "", ICON_NONE);
}
-static void fn_node_vector_input_expand_in_mf_network(
- blender::nodes::NodeMFNetworkBuilder &builder)
+static void fn_node_vector_input_build_multi_function(
+ blender::nodes::NodeMultiFunctionBuilder &builder)
{
- bNode &bnode = builder.bnode();
+ bNode &bnode = builder.node();
NodeInputVector *node_storage = static_cast<NodeInputVector *>(bnode.storage);
blender::float3 vector(node_storage->vector);
-
builder.construct_and_set_matching_fn<blender::fn::CustomMF_Constant<blender::float3>>(vector);
}
-
static void fn_node_input_vector_init(bNodeTree *UNUSED(ntree), bNode *node)
{
NodeInputVector *data = (NodeInputVector *)MEM_callocN(sizeof(NodeInputVector),
@@ -58,7 +56,7 @@ void register_node_type_fn_input_vector()
node_type_init(&ntype, fn_node_input_vector_init);
node_type_storage(
&ntype, "NodeInputVector", node_free_standard_storage, node_copy_standard_storage);
- ntype.expand_in_mf_network = fn_node_vector_input_expand_in_mf_network;
+ ntype.build_multi_function = fn_node_vector_input_build_multi_function;
ntype.draw_buttons = fn_node_input_vector_layout;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/function/nodes/node_fn_random_float.cc b/source/blender/nodes/function/nodes/node_fn_random_float.cc
index a3c9f44b6a1..47ec9adf6bd 100644
--- a/source/blender/nodes/function/nodes/node_fn_random_float.cc
+++ b/source/blender/nodes/function/nodes/node_fn_random_float.cc
@@ -67,10 +67,11 @@ class RandomFloatFunction : public blender::fn::MultiFunction {
}
};
-static void fn_node_random_float_expand_in_mf_network(
- blender::nodes::NodeMFNetworkBuilder &builder)
+static void fn_node_random_float_build_multi_function(
+ blender::nodes::NodeMultiFunctionBuilder &builder)
{
- builder.construct_and_set_matching_fn<RandomFloatFunction>();
+ static RandomFloatFunction fn;
+ builder.set_matching_fn(fn);
}
void register_node_type_fn_random_float()
@@ -79,6 +80,6 @@ void register_node_type_fn_random_float()
fn_node_type_base(&ntype, FN_NODE_RANDOM_FLOAT, "Random Float", 0, 0);
node_type_socket_templates(&ntype, fn_node_random_float_in, fn_node_random_float_out);
- ntype.expand_in_mf_network = fn_node_random_float_expand_in_mf_network;
+ ntype.build_multi_function = fn_node_random_float_build_multi_function;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/geometry/node_geometry_tree.cc b/source/blender/nodes/geometry/node_geometry_tree.cc
index 07a89a1fa8c..d6b23c38ee4 100644
--- a/source/blender/nodes/geometry/node_geometry_tree.cc
+++ b/source/blender/nodes/geometry/node_geometry_tree.cc
@@ -80,7 +80,7 @@ static void foreach_nodeclass(Scene *UNUSED(scene), void *calldata, bNodeClassCa
func(calldata, NODE_CLASS_ATTRIBUTE, N_("Attribute"));
func(calldata, NODE_CLASS_OP_COLOR, N_("Color"));
func(calldata, NODE_CLASS_OP_VECTOR, N_("Vector"));
- func(calldata, NODE_CLASS_CONVERTOR, N_("Convertor"));
+ func(calldata, NODE_CLASS_CONVERTER, N_("Converter"));
func(calldata, NODE_CLASS_LAYOUT, N_("Layout"));
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_switch.cc b/source/blender/nodes/geometry/nodes/node_geo_switch.cc
index 30f6dc2394f..67b7065ca94 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_switch.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_switch.cc
@@ -204,7 +204,7 @@ void register_node_type_geo_switch()
{
static bNodeType ntype;
- geo_node_type_base(&ntype, GEO_NODE_SWITCH, "Switch", NODE_CLASS_CONVERTOR, 0);
+ geo_node_type_base(&ntype, GEO_NODE_SWITCH, "Switch", NODE_CLASS_CONVERTER, 0);
node_type_socket_templates(&ntype, geo_node_switch_in, geo_node_switch_out);
node_type_init(&ntype, geo_node_switch_init);
node_type_update(&ntype, blender::nodes::geo_node_switch_update);
diff --git a/source/blender/nodes/intern/node_geometry_exec.cc b/source/blender/nodes/intern/node_geometry_exec.cc
index ffa20579acc..a3bbca90731 100644
--- a/source/blender/nodes/intern/node_geometry_exec.cc
+++ b/source/blender/nodes/intern/node_geometry_exec.cc
@@ -19,7 +19,6 @@
#include "DEG_depsgraph_query.h"
#include "NOD_geometry_exec.hh"
-#include "NOD_type_callbacks.hh"
#include "NOD_type_conversions.hh"
#include "node_geometry_util.hh"
diff --git a/source/blender/nodes/NOD_type_callbacks.hh b/source/blender/nodes/intern/node_multi_function.cc
index 2be78f929db..c91899ed8c2 100644
--- a/source/blender/nodes/NOD_type_callbacks.hh
+++ b/source/blender/nodes/intern/node_multi_function.cc
@@ -14,21 +14,27 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-#pragma once
-
-#include <optional>
-
-#include "BKE_node.h"
-
-#include "FN_multi_function_data_type.hh"
+#include "NOD_multi_function.hh"
namespace blender::nodes {
-using fn::CPPType;
-using fn::MFDataType;
-
-std::optional<MFDataType> socket_mf_type_get(const bNodeSocketType &stype);
-bool socket_is_mf_data_socket(const bNodeSocketType &stype);
-void socket_expand_in_mf_network(SocketMFNetworkBuilder &builder);
+NodeMultiFunctions::NodeMultiFunctions(const DerivedNodeTree &tree, ResourceScope &resource_scope)
+{
+ for (const NodeTreeRef *tree_ref : tree.used_node_tree_refs()) {
+ bNodeTree *btree = tree_ref->btree();
+ for (const NodeRef *node : tree_ref->nodes()) {
+ bNode *bnode = node->bnode();
+ if (bnode->typeinfo->build_multi_function == nullptr) {
+ continue;
+ }
+ NodeMultiFunctionBuilder builder{resource_scope, *bnode, *btree};
+ bnode->typeinfo->build_multi_function(builder);
+ const MultiFunction *fn = builder.built_fn_;
+ if (fn != nullptr) {
+ map_.add_new(bnode, fn);
+ }
+ }
+ }
+}
} // namespace blender::nodes
diff --git a/source/blender/nodes/intern/node_socket.cc b/source/blender/nodes/intern/node_socket.cc
index 2202ef2b64a..c761dfa473a 100644
--- a/source/blender/nodes/intern/node_socket.cc
+++ b/source/blender/nodes/intern/node_socket.cc
@@ -45,7 +45,6 @@
#include "MEM_guardedalloc.h"
-#include "NOD_node_tree_multi_function.hh"
#include "NOD_socket.h"
#include "FN_cpp_type_make.hh"
diff --git a/source/blender/nodes/intern/node_tree_multi_function.cc b/source/blender/nodes/intern/node_tree_multi_function.cc
deleted file mode 100644
index 7ab6495f733..00000000000
--- a/source/blender/nodes/intern/node_tree_multi_function.cc
+++ /dev/null
@@ -1,409 +0,0 @@
-/*
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-
-#include "NOD_node_tree_multi_function.hh"
-#include "NOD_type_conversions.hh"
-
-#include "FN_multi_function_network_evaluation.hh"
-
-#include "BLI_color.hh"
-#include "BLI_float2.hh"
-#include "BLI_float3.hh"
-
-namespace blender::nodes {
-
-const fn::MultiFunction &NodeMFNetworkBuilder::get_default_fn(StringRef name)
-{
- Vector<fn::MFDataType, 10> input_types;
- Vector<fn::MFDataType, 10> output_types;
-
- for (const InputSocketRef *dsocket : dnode_->inputs()) {
- if (dsocket->is_available()) {
- std::optional<fn::MFDataType> data_type = socket_mf_type_get(*dsocket->typeinfo());
- if (data_type.has_value()) {
- input_types.append(*data_type);
- }
- }
- }
- for (const OutputSocketRef *dsocket : dnode_->outputs()) {
- if (dsocket->is_available()) {
- std::optional<fn::MFDataType> data_type = socket_mf_type_get(*dsocket->typeinfo());
- if (data_type.has_value()) {
- output_types.append(*data_type);
- }
- }
- }
-
- const fn::MultiFunction &fn = this->construct_fn<fn::CustomMF_DefaultOutput>(
- name, input_types, output_types);
- return fn;
-}
-
-static void insert_dummy_node(CommonMFNetworkBuilderData &common, const DNode &dnode)
-{
- constexpr int stack_capacity = 10;
-
- Vector<fn::MFDataType, stack_capacity> input_types;
- Vector<StringRef, stack_capacity> input_names;
- Vector<const InputSocketRef *, stack_capacity> input_dsockets;
-
- for (const InputSocketRef *dsocket : dnode->inputs()) {
- if (dsocket->is_available()) {
- std::optional<fn::MFDataType> data_type = socket_mf_type_get(*dsocket->bsocket()->typeinfo);
- if (data_type.has_value()) {
- input_types.append(*data_type);
- input_names.append(dsocket->name());
- input_dsockets.append(dsocket);
- }
- }
- }
-
- Vector<fn::MFDataType, stack_capacity> output_types;
- Vector<StringRef, stack_capacity> output_names;
- Vector<const OutputSocketRef *, stack_capacity> output_dsockets;
-
- for (const OutputSocketRef *dsocket : dnode->outputs()) {
- if (dsocket->is_available()) {
- std::optional<fn::MFDataType> data_type = socket_mf_type_get(*dsocket->bsocket()->typeinfo);
- if (data_type.has_value()) {
- output_types.append(*data_type);
- output_names.append(dsocket->name());
- output_dsockets.append(dsocket);
- }
- }
- }
-
- fn::MFDummyNode &dummy_node = common.network.add_dummy(
- dnode->name(), input_types, output_types, input_names, output_names);
-
- common.network_map.add(*dnode.context(), input_dsockets, dummy_node.inputs());
- common.network_map.add(*dnode.context(), output_dsockets, dummy_node.outputs());
-}
-
-static bool has_data_sockets(const DNode &dnode)
-{
- for (const InputSocketRef *socket : dnode->inputs()) {
- if (socket_is_mf_data_socket(*socket->bsocket()->typeinfo)) {
- return true;
- }
- }
- for (const OutputSocketRef *socket : dnode->outputs()) {
- if (socket_is_mf_data_socket(*socket->bsocket()->typeinfo)) {
- return true;
- }
- }
- return false;
-}
-
-static void foreach_node_to_insert(CommonMFNetworkBuilderData &common,
- FunctionRef<void(DNode)> callback)
-{
- common.tree.foreach_node([&](const DNode dnode) {
- if (dnode->is_group_node()) {
- return;
- }
- /* Don't insert non-root group input/output nodes, because they will be inlined. */
- if (!dnode.context()->is_root()) {
- if (dnode->is_group_input_node() || dnode->is_group_output_node()) {
- return;
- }
- }
- callback(dnode);
- });
-}
-
-/**
- * Expands all function nodes in the multi-function network. Nodes that don't have an expand
- * function, but do have data sockets, will get corresponding dummy nodes.
- */
-static void insert_nodes(CommonMFNetworkBuilderData &common)
-{
- foreach_node_to_insert(common, [&](const DNode dnode) {
- const bNodeType *node_type = dnode->typeinfo();
- if (node_type->expand_in_mf_network != nullptr) {
- NodeMFNetworkBuilder builder{common, dnode};
- node_type->expand_in_mf_network(builder);
- }
- else if (has_data_sockets(dnode)) {
- insert_dummy_node(common, dnode);
- }
- });
-}
-
-static fn::MFOutputSocket &insert_default_value_for_type(CommonMFNetworkBuilderData &common,
- fn::MFDataType type)
-{
- const fn::MultiFunction *default_fn;
- if (type.is_single()) {
- default_fn = &common.scope.construct<fn::CustomMF_GenericConstant>(
- AT, type.single_type(), type.single_type().default_value());
- }
- else {
- default_fn = &common.scope.construct<fn::CustomMF_GenericConstantArray>(
- AT, fn::GSpan(type.vector_base_type()));
- }
-
- fn::MFNode &node = common.network.add_function(*default_fn);
- return node.output(0);
-}
-
-static fn::MFOutputSocket *insert_unlinked_input(CommonMFNetworkBuilderData &common,
- const DInputSocket &dsocket)
-{
- BLI_assert(socket_is_mf_data_socket(*dsocket->typeinfo()));
-
- SocketMFNetworkBuilder builder{common, dsocket};
- socket_expand_in_mf_network(builder);
-
- fn::MFOutputSocket *built_socket = builder.built_socket();
- BLI_assert(built_socket != nullptr);
- return built_socket;
-}
-
-static void insert_links_and_unlinked_inputs(CommonMFNetworkBuilderData &common)
-{
- foreach_node_to_insert(common, [&](const DNode dnode) {
- for (const InputSocketRef *socket_ref : dnode->inputs()) {
- const DInputSocket to_dsocket{dnode.context(), socket_ref};
- if (!to_dsocket->is_available()) {
- continue;
- }
- if (!socket_is_mf_data_socket(*to_dsocket->typeinfo())) {
- continue;
- }
-
- Span<fn::MFInputSocket *> to_sockets = common.network_map.lookup(to_dsocket);
- BLI_assert(to_sockets.size() >= 1);
- const fn::MFDataType to_type = to_sockets[0]->data_type();
-
- Vector<DSocket> from_dsockets;
- to_dsocket.foreach_origin_socket([&](DSocket socket) { from_dsockets.append(socket); });
- if (from_dsockets.size() > 1) {
- fn::MFOutputSocket &from_socket = insert_default_value_for_type(common, to_type);
- for (fn::MFInputSocket *to_socket : to_sockets) {
- common.network.add_link(from_socket, *to_socket);
- }
- continue;
- }
- if (from_dsockets.is_empty()) {
- /* The socket is not linked. Need to use the value of the socket itself. */
- fn::MFOutputSocket *built_socket = insert_unlinked_input(common, to_dsocket);
- for (fn::MFInputSocket *to_socket : to_sockets) {
- common.network.add_link(*built_socket, *to_socket);
- }
- continue;
- }
- if (from_dsockets[0]->is_input()) {
- DInputSocket from_dsocket{from_dsockets[0]};
- fn::MFOutputSocket *built_socket = insert_unlinked_input(common, from_dsocket);
- for (fn::MFInputSocket *to_socket : to_sockets) {
- common.network.add_link(*built_socket, *to_socket);
- }
- continue;
- }
- DOutputSocket from_dsocket{from_dsockets[0]};
- fn::MFOutputSocket *from_socket = &common.network_map.lookup(from_dsocket);
- const fn::MFDataType from_type = from_socket->data_type();
-
- if (from_type != to_type) {
- const fn::MultiFunction *conversion_fn =
- get_implicit_type_conversions().get_conversion_multi_function(from_type, to_type);
- if (conversion_fn != nullptr) {
- fn::MFNode &node = common.network.add_function(*conversion_fn);
- common.network.add_link(*from_socket, node.input(0));
- from_socket = &node.output(0);
- }
- else {
- from_socket = &insert_default_value_for_type(common, to_type);
- }
- }
-
- for (fn::MFInputSocket *to_socket : to_sockets) {
- common.network.add_link(*from_socket, *to_socket);
- }
- }
- });
-}
-
-/**
- * Expands all function nodes contained in the given node tree within the given multi-function
- * network.
- *
- * Returns a mapping between the original node tree and the generated nodes/sockets for further
- * processing.
- */
-MFNetworkTreeMap insert_node_tree_into_mf_network(fn::MFNetwork &network,
- const DerivedNodeTree &tree,
- ResourceScope &scope)
-{
- MFNetworkTreeMap network_map{tree, network};
-
- CommonMFNetworkBuilderData common{scope, network, network_map, tree};
-
- insert_nodes(common);
- insert_links_and_unlinked_inputs(common);
-
- return network_map;
-}
-
-/**
- * A single node is allowed to expand into multiple nodes before evaluation. Depending on what
- * nodes it expands to, it belongs a different type of the ones below.
- */
-enum class NodeExpandType {
- SingleFunctionNode,
- MultipleFunctionNodes,
- HasDummyNodes,
-};
-
-/**
- * Checks how the given node expanded in the multi-function network. If it is only a single
- * function node, the corresponding function is returned as well.
- */
-static NodeExpandType get_node_expand_type(MFNetworkTreeMap &network_map,
- const DNode &dnode,
- const fn::MultiFunction **r_single_function)
-{
- const fn::MFFunctionNode *single_function_node = nullptr;
- bool has_multiple_nodes = false;
- bool has_dummy_nodes = false;
-
- auto check_mf_node = [&](fn::MFNode &mf_node) {
- if (mf_node.is_function()) {
- if (single_function_node == nullptr) {
- single_function_node = &mf_node.as_function();
- }
- if (&mf_node != single_function_node) {
- has_multiple_nodes = true;
- }
- }
- else {
- BLI_assert(mf_node.is_dummy());
- has_dummy_nodes = true;
- }
- };
-
- for (const InputSocketRef *dsocket : dnode->inputs()) {
- if (dsocket->is_available()) {
- for (fn::MFInputSocket *mf_input :
- network_map.lookup(DInputSocket(dnode.context(), dsocket))) {
- check_mf_node(mf_input->node());
- }
- }
- }
- for (const OutputSocketRef *dsocket : dnode->outputs()) {
- if (dsocket->is_available()) {
- fn::MFOutputSocket &mf_output = network_map.lookup(DOutputSocket(dnode.context(), dsocket));
- check_mf_node(mf_output.node());
- }
- }
-
- if (has_dummy_nodes) {
- return NodeExpandType::HasDummyNodes;
- }
- if (has_multiple_nodes) {
- return NodeExpandType::MultipleFunctionNodes;
- }
- *r_single_function = &single_function_node->function();
- return NodeExpandType::SingleFunctionNode;
-}
-
-static const fn::MultiFunction &create_function_for_node_that_expands_into_multiple(
- const DNode &dnode,
- fn::MFNetwork &network,
- MFNetworkTreeMap &network_map,
- ResourceScope &scope)
-{
- Vector<const fn::MFOutputSocket *> dummy_fn_inputs;
- for (const InputSocketRef *dsocket : dnode->inputs()) {
- if (dsocket->is_available()) {
- MFDataType data_type = *socket_mf_type_get(*dsocket->typeinfo());
- fn::MFOutputSocket &fn_input = network.add_input(data_type.to_string(), data_type);
- for (fn::MFInputSocket *mf_input :
- network_map.lookup(DInputSocket(dnode.context(), dsocket))) {
- network.add_link(fn_input, *mf_input);
- dummy_fn_inputs.append(&fn_input);
- }
- }
- }
- Vector<const fn::MFInputSocket *> dummy_fn_outputs;
- for (const OutputSocketRef *dsocket : dnode->outputs()) {
- if (dsocket->is_available()) {
- fn::MFOutputSocket &mf_output = network_map.lookup(DOutputSocket(dnode.context(), dsocket));
- MFDataType data_type = mf_output.data_type();
- fn::MFInputSocket &fn_output = network.add_output(data_type.to_string(), data_type);
- network.add_link(mf_output, fn_output);
- dummy_fn_outputs.append(&fn_output);
- }
- }
-
- fn::MFNetworkEvaluator &fn_evaluator = scope.construct<fn::MFNetworkEvaluator>(
- __func__, std::move(dummy_fn_inputs), std::move(dummy_fn_outputs));
- return fn_evaluator;
-}
-
-/**
- * Returns a single multi-function for every node that supports it. This makes it easier to reuse
- * the multi-function implementation of nodes in different contexts.
- */
-MultiFunctionByNode get_multi_function_per_node(const DerivedNodeTree &tree, ResourceScope &scope)
-{
- /* Build a network that nodes can insert themselves into. However, the individual nodes are not
- * connected. */
- fn::MFNetwork &network = scope.construct<fn::MFNetwork>(__func__);
- MFNetworkTreeMap network_map{tree, network};
- MultiFunctionByNode functions_by_node;
-
- CommonMFNetworkBuilderData common{scope, network, network_map, tree};
-
- tree.foreach_node([&](DNode dnode) {
- const bNodeType *node_type = dnode->typeinfo();
- if (node_type->expand_in_mf_network == nullptr) {
- /* This node does not have a multi-function implementation. */
- return;
- }
-
- NodeMFNetworkBuilder builder{common, dnode};
- node_type->expand_in_mf_network(builder);
-
- const fn::MultiFunction *single_function = nullptr;
- const NodeExpandType expand_type = get_node_expand_type(network_map, dnode, &single_function);
-
- switch (expand_type) {
- case NodeExpandType::HasDummyNodes: {
- /* Dummy nodes cannot be executed, so skip them. */
- break;
- }
- case NodeExpandType::SingleFunctionNode: {
- /* This is the common case. Most nodes just expand to a single function. */
- functions_by_node.add_new(dnode, single_function);
- break;
- }
- case NodeExpandType::MultipleFunctionNodes: {
- /* If a node expanded into multiple functions, a new function has to be created that
- * combines those. */
- const fn::MultiFunction &fn = create_function_for_node_that_expands_into_multiple(
- dnode, network, network_map, scope);
- functions_by_node.add_new(dnode, &fn);
- break;
- }
- }
- });
-
- return functions_by_node;
-}
-
-} // namespace blender::nodes
diff --git a/source/blender/nodes/intern/type_callbacks.cc b/source/blender/nodes/intern/type_callbacks.cc
deleted file mode 100644
index 881a02c92e9..00000000000
--- a/source/blender/nodes/intern/type_callbacks.cc
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-
-#include "NOD_node_tree_multi_function.hh"
-#include "NOD_type_callbacks.hh"
-
-namespace blender::nodes {
-
-std::optional<MFDataType> socket_mf_type_get(const bNodeSocketType &stype)
-{
- const CPPType *cpp_type = stype.get_base_cpp_type ? stype.get_base_cpp_type() : nullptr;
- if (cpp_type != nullptr) {
- return MFDataType::ForSingle(*cpp_type);
- }
- return {};
-}
-
-bool socket_is_mf_data_socket(const bNodeSocketType &stype)
-{
- if (!socket_mf_type_get(stype).has_value()) {
- return false;
- }
- if (stype.expand_in_mf_network == nullptr && stype.get_base_cpp_value == nullptr) {
- return false;
- }
- return true;
-}
-
-void socket_expand_in_mf_network(SocketMFNetworkBuilder &builder)
-{
- bNodeSocket &socket = builder.bsocket();
- if (socket.typeinfo->expand_in_mf_network != nullptr) {
- socket.typeinfo->expand_in_mf_network(builder);
- }
- else if (socket.typeinfo->get_base_cpp_value != nullptr) {
- const CPPType &type = *socket.typeinfo->get_base_cpp_type();
- void *buffer = builder.resource_scope().linear_allocator().allocate(type.size(),
- type.alignment());
- socket.typeinfo->get_base_cpp_value(socket, buffer);
- builder.set_constant_value(type, buffer);
- }
- else {
- BLI_assert_unreachable();
- }
-}
-
-} // namespace blender::nodes
diff --git a/source/blender/nodes/shader/node_shader_tree.c b/source/blender/nodes/shader/node_shader_tree.c
index 98edc133a1d..c3a675fcd20 100644
--- a/source/blender/nodes/shader/node_shader_tree.c
+++ b/source/blender/nodes/shader/node_shader_tree.c
@@ -130,7 +130,7 @@ static void foreach_nodeclass(Scene *UNUSED(scene), void *calldata, bNodeClassCa
func(calldata, NODE_CLASS_TEXTURE, N_("Texture"));
func(calldata, NODE_CLASS_OP_COLOR, N_("Color"));
func(calldata, NODE_CLASS_OP_VECTOR, N_("Vector"));
- func(calldata, NODE_CLASS_CONVERTOR, N_("Convertor"));
+ func(calldata, NODE_CLASS_CONVERTER, N_("Converter"));
func(calldata, NODE_CLASS_SCRIPT, N_("Script"));
func(calldata, NODE_CLASS_GROUP, N_("Group"));
func(calldata, NODE_CLASS_INTERFACE, N_("Interface"));
diff --git a/source/blender/nodes/shader/node_shader_util.h b/source/blender/nodes/shader/node_shader_util.h
index dc44f0fa98f..a75354d3381 100644
--- a/source/blender/nodes/shader/node_shader_util.h
+++ b/source/blender/nodes/shader/node_shader_util.h
@@ -72,7 +72,7 @@
#ifdef __cplusplus
# include "FN_multi_function_builder.hh"
-# include "NOD_node_tree_multi_function.hh"
+# include "NOD_multi_function.hh"
# include "BLI_color.hh"
# include "BLI_float3.hh"
diff --git a/source/blender/nodes/shader/nodes/node_shader_blackbody.c b/source/blender/nodes/shader/nodes/node_shader_blackbody.c
index c946a76ab51..95c35affc27 100644
--- a/source/blender/nodes/shader/nodes/node_shader_blackbody.c
+++ b/source/blender/nodes/shader/nodes/node_shader_blackbody.c
@@ -52,7 +52,7 @@ void register_node_type_sh_blackbody(void)
{
static bNodeType ntype;
- sh_node_type_base(&ntype, SH_NODE_BLACKBODY, "Blackbody", NODE_CLASS_CONVERTOR, 0);
+ sh_node_type_base(&ntype, SH_NODE_BLACKBODY, "Blackbody", NODE_CLASS_CONVERTER, 0);
node_type_size_preset(&ntype, NODE_SIZE_MIDDLE);
node_type_socket_templates(&ntype, sh_node_blackbody_in, sh_node_blackbody_out);
node_type_init(&ntype, NULL);
diff --git a/source/blender/nodes/shader/nodes/node_shader_clamp.cc b/source/blender/nodes/shader/nodes/node_shader_clamp.cc
index 4f77421cfe0..b90397e4892 100644
--- a/source/blender/nodes/shader/nodes/node_shader_clamp.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_clamp.cc
@@ -51,7 +51,7 @@ static int gpu_shader_clamp(GPUMaterial *mat,
GPU_stack_link(mat, node, "clamp_range", in, out);
}
-static void sh_node_clamp_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder)
+static void sh_node_clamp_build_multi_function(blender::nodes::NodeMultiFunctionBuilder &builder)
{
static blender::fn::CustomMF_SI_SI_SI_SO<float, float, float, float> minmax_fn{
"Clamp (Min Max)",
@@ -65,7 +65,7 @@ static void sh_node_clamp_expand_in_mf_network(blender::nodes::NodeMFNetworkBuil
return clamp_f(value, b, a);
}};
- int clamp_type = builder.bnode().custom1;
+ int clamp_type = builder.node().custom1;
if (clamp_type == NODE_CLAMP_MINMAX) {
builder.set_matching_fn(minmax_fn);
}
@@ -78,11 +78,11 @@ void register_node_type_sh_clamp(void)
{
static bNodeType ntype;
- sh_fn_node_type_base(&ntype, SH_NODE_CLAMP, "Clamp", NODE_CLASS_CONVERTOR, 0);
+ sh_fn_node_type_base(&ntype, SH_NODE_CLAMP, "Clamp", NODE_CLASS_CONVERTER, 0);
node_type_socket_templates(&ntype, sh_node_clamp_in, sh_node_clamp_out);
node_type_init(&ntype, node_shader_init_clamp);
node_type_gpu(&ntype, gpu_shader_clamp);
- ntype.expand_in_mf_network = sh_node_clamp_expand_in_mf_network;
+ ntype.build_multi_function = sh_node_clamp_build_multi_function;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/shader/nodes/node_shader_curves.cc b/source/blender/nodes/shader/nodes/node_shader_curves.cc
index f1d5040a292..df075d6e973 100644
--- a/source/blender/nodes/shader/nodes/node_shader_curves.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_curves.cc
@@ -143,9 +143,10 @@ class CurveVecFunction : public blender::fn::MultiFunction {
}
};
-static void sh_node_curve_vec_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder)
+static void sh_node_curve_vec_build_multi_function(
+ blender::nodes::NodeMultiFunctionBuilder &builder)
{
- bNode &bnode = builder.bnode();
+ bNode &bnode = builder.node();
CurveMapping *cumap = (CurveMapping *)bnode.storage;
BKE_curvemapping_init(cumap);
builder.construct_and_set_matching_fn<CurveVecFunction>(*cumap);
@@ -162,7 +163,7 @@ void register_node_type_sh_curve_vec(void)
node_type_storage(&ntype, "CurveMapping", node_free_curves, node_copy_curves);
node_type_exec(&ntype, node_initexec_curves, nullptr, node_shader_exec_curve_vec);
node_type_gpu(&ntype, gpu_shader_curve_vec);
- ntype.expand_in_mf_network = sh_node_curve_vec_expand_in_mf_network;
+ ntype.build_multi_function = sh_node_curve_vec_build_multi_function;
nodeRegisterType(&ntype);
}
@@ -317,9 +318,10 @@ class CurveRGBFunction : public blender::fn::MultiFunction {
}
};
-static void sh_node_curve_rgb_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder)
+static void sh_node_curve_rgb_build_multi_function(
+ blender::nodes::NodeMultiFunctionBuilder &builder)
{
- bNode &bnode = builder.bnode();
+ bNode &bnode = builder.node();
CurveMapping *cumap = (CurveMapping *)bnode.storage;
BKE_curvemapping_init(cumap);
builder.construct_and_set_matching_fn<CurveRGBFunction>(*cumap);
@@ -336,7 +338,7 @@ void register_node_type_sh_curve_rgb(void)
node_type_storage(&ntype, "CurveMapping", node_free_curves, node_copy_curves);
node_type_exec(&ntype, node_initexec_curves, nullptr, node_shader_exec_curve_rgb);
node_type_gpu(&ntype, gpu_shader_curve_rgb);
- ntype.expand_in_mf_network = sh_node_curve_rgb_expand_in_mf_network;
+ ntype.build_multi_function = sh_node_curve_rgb_build_multi_function;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/shader/nodes/node_shader_map_range.cc b/source/blender/nodes/shader/nodes/node_shader_map_range.cc
index ad7abd9d491..f48e824ccb5 100644
--- a/source/blender/nodes/shader/nodes/node_shader_map_range.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_map_range.cc
@@ -261,9 +261,10 @@ class MapRangeSmootherstepFunction : public blender::fn::MultiFunction {
}
};
-static void sh_node_map_range_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder)
+static void sh_node_map_range_build_multi_function(
+ blender::nodes::NodeMultiFunctionBuilder &builder)
{
- bNode &bnode = builder.bnode();
+ bNode &bnode = builder.node();
bool clamp = bnode.custom1 != 0;
int interpolation_type = bnode.custom2;
@@ -301,7 +302,6 @@ static void sh_node_map_range_expand_in_mf_network(blender::nodes::NodeMFNetwork
break;
}
default:
- builder.set_not_implemented();
break;
}
}
@@ -310,12 +310,12 @@ void register_node_type_sh_map_range(void)
{
static bNodeType ntype;
- sh_fn_node_type_base(&ntype, SH_NODE_MAP_RANGE, "Map Range", NODE_CLASS_CONVERTOR, 0);
+ sh_fn_node_type_base(&ntype, SH_NODE_MAP_RANGE, "Map Range", NODE_CLASS_CONVERTER, 0);
node_type_socket_templates(&ntype, sh_node_map_range_in, sh_node_map_range_out);
node_type_init(&ntype, node_shader_init_map_range);
node_type_update(&ntype, node_shader_update_map_range);
node_type_gpu(&ntype, gpu_shader_map_range);
- ntype.expand_in_mf_network = sh_node_map_range_expand_in_mf_network;
+ ntype.build_multi_function = sh_node_map_range_build_multi_function;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/shader/nodes/node_shader_math.cc b/source/blender/nodes/shader/nodes/node_shader_math.cc
index 7a846031456..66c50b6de46 100644
--- a/source/blender/nodes/shader/nodes/node_shader_math.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_math.cc
@@ -69,11 +69,9 @@ static int gpu_shader_math(GPUMaterial *mat,
return 0;
}
-static const blender::fn::MultiFunction &get_base_multi_function(
- blender::nodes::NodeMFNetworkBuilder &builder)
+static const blender::fn::MultiFunction *get_base_multi_function(bNode &node)
{
- const int mode = builder.bnode().custom1;
-
+ const int mode = node.custom1;
const blender::fn::MultiFunction *base_fn = nullptr;
blender::nodes::try_dispatch_float_math_fl_to_fl(
@@ -82,7 +80,7 @@ static const blender::fn::MultiFunction &get_base_multi_function(
base_fn = &fn;
});
if (base_fn != nullptr) {
- return *base_fn;
+ return base_fn;
}
blender::nodes::try_dispatch_float_math_fl_fl_to_fl(
@@ -92,7 +90,7 @@ static const blender::fn::MultiFunction &get_base_multi_function(
base_fn = &fn;
});
if (base_fn != nullptr) {
- return *base_fn;
+ return base_fn;
}
blender::nodes::try_dispatch_float_math_fl_fl_fl_to_fl(
@@ -102,36 +100,51 @@ static const blender::fn::MultiFunction &get_base_multi_function(
base_fn = &fn;
});
if (base_fn != nullptr) {
- return *base_fn;
+ return base_fn;
}
- return builder.get_not_implemented_fn();
+ return nullptr;
}
-static void sh_node_math_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder)
-{
- const blender::fn::MultiFunction &base_function = get_base_multi_function(builder);
+class ClampWrapperFunction : public blender::fn::MultiFunction {
+ private:
+ const blender::fn::MultiFunction &fn_;
- const blender::nodes::DNode &dnode = builder.dnode();
- blender::fn::MFNetwork &network = builder.network();
- blender::fn::MFFunctionNode &base_node = network.add_function(base_function);
+ public:
+ ClampWrapperFunction(const blender::fn::MultiFunction &fn) : fn_(fn)
+ {
+ this->set_signature(&fn.signature());
+ }
- builder.network_map().add_try_match(*dnode.context(), dnode->inputs(), base_node.inputs());
+ void call(blender::IndexMask mask,
+ blender::fn::MFParams params,
+ blender::fn::MFContext context) const override
+ {
+ fn_.call(mask, params, context);
+
+ /* Assumes the output parameter is the last one. */
+ const int output_param_index = this->param_amount() - 1;
+ /* This has actually been initialized in the call above. */
+ blender::MutableSpan<float> results = params.uninitialized_single_output<float>(
+ output_param_index);
+
+ for (const int i : mask) {
+ float &value = results[i];
+ CLAMP(value, 0.0f, 1.0f);
+ }
+ }
+};
+
+static void sh_node_math_build_multi_function(blender::nodes::NodeMultiFunctionBuilder &builder)
+{
+ const blender::fn::MultiFunction *base_function = get_base_multi_function(builder.node());
- const bool clamp_output = builder.bnode().custom2 != 0;
+ const bool clamp_output = builder.node().custom2 != 0;
if (clamp_output) {
- static blender::fn::CustomMF_SI_SO<float, float> clamp_fn{"Clamp", [](float value) {
- CLAMP(value, 0.0f, 1.0f);
- return value;
- }};
- blender::fn::MFFunctionNode &clamp_node = network.add_function(clamp_fn);
- network.add_link(base_node.output(0), clamp_node.input(0));
- builder.network_map().add(blender::nodes::DOutputSocket(dnode.context(), &dnode->output(0)),
- clamp_node.output(0));
+ builder.construct_and_set_matching_fn<ClampWrapperFunction>(*base_function);
}
else {
- builder.network_map().add(blender::nodes::DOutputSocket(dnode.context(), &dnode->output(0)),
- base_node.output(0));
+ builder.set_matching_fn(base_function);
}
}
@@ -139,12 +152,12 @@ void register_node_type_sh_math(void)
{
static bNodeType ntype;
- sh_fn_node_type_base(&ntype, SH_NODE_MATH, "Math", NODE_CLASS_CONVERTOR, 0);
+ sh_fn_node_type_base(&ntype, SH_NODE_MATH, "Math", NODE_CLASS_CONVERTER, 0);
node_type_socket_templates(&ntype, sh_node_math_in, sh_node_math_out);
node_type_label(&ntype, node_math_label);
node_type_gpu(&ntype, gpu_shader_math);
node_type_update(&ntype, node_math_update);
- ntype.expand_in_mf_network = sh_node_math_expand_in_mf_network;
+ ntype.build_multi_function = sh_node_math_build_multi_function;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/shader/nodes/node_shader_mixRgb.cc b/source/blender/nodes/shader/nodes/node_shader_mixRgb.cc
index 47011caeeb6..ade35a40366 100644
--- a/source/blender/nodes/shader/nodes/node_shader_mixRgb.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_mixRgb.cc
@@ -174,9 +174,9 @@ class MixRGBFunction : public blender::fn::MultiFunction {
}
};
-static void sh_node_mix_rgb_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder)
+static void sh_node_mix_rgb_build_multi_function(blender::nodes::NodeMultiFunctionBuilder &builder)
{
- bNode &node = builder.bnode();
+ bNode &node = builder.node();
bool clamp = node.custom2 & SHD_MIXRGB_CLAMP;
int mix_type = node.custom1;
builder.construct_and_set_matching_fn<MixRGBFunction>(clamp, mix_type);
@@ -191,7 +191,7 @@ void register_node_type_sh_mix_rgb(void)
node_type_label(&ntype, node_blend_label);
node_type_exec(&ntype, nullptr, nullptr, node_shader_exec_mix_rgb);
node_type_gpu(&ntype, gpu_shader_mix_rgb);
- ntype.expand_in_mf_network = sh_node_mix_rgb_expand_in_mf_network;
+ ntype.build_multi_function = sh_node_mix_rgb_build_multi_function;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/shader/nodes/node_shader_sepcombHSV.c b/source/blender/nodes/shader/nodes/node_shader_sepcombHSV.c
index 951755be4f3..dfecb830b35 100644
--- a/source/blender/nodes/shader/nodes/node_shader_sepcombHSV.c
+++ b/source/blender/nodes/shader/nodes/node_shader_sepcombHSV.c
@@ -61,7 +61,7 @@ void register_node_type_sh_sephsv(void)
{
static bNodeType ntype;
- sh_node_type_base(&ntype, SH_NODE_SEPHSV, "Separate HSV", NODE_CLASS_CONVERTOR, 0);
+ sh_node_type_base(&ntype, SH_NODE_SEPHSV, "Separate HSV", NODE_CLASS_CONVERTER, 0);
node_type_socket_templates(&ntype, sh_node_sephsv_in, sh_node_sephsv_out);
node_type_exec(&ntype, NULL, NULL, node_shader_exec_sephsv);
node_type_gpu(&ntype, gpu_shader_sephsv);
@@ -109,7 +109,7 @@ void register_node_type_sh_combhsv(void)
{
static bNodeType ntype;
- sh_node_type_base(&ntype, SH_NODE_COMBHSV, "Combine HSV", NODE_CLASS_CONVERTOR, 0);
+ sh_node_type_base(&ntype, SH_NODE_COMBHSV, "Combine HSV", NODE_CLASS_CONVERTER, 0);
node_type_socket_templates(&ntype, sh_node_combhsv_in, sh_node_combhsv_out);
node_type_exec(&ntype, NULL, NULL, node_shader_exec_combhsv);
node_type_gpu(&ntype, gpu_shader_combhsv);
diff --git a/source/blender/nodes/shader/nodes/node_shader_sepcombRGB.cc b/source/blender/nodes/shader/nodes/node_shader_sepcombRGB.cc
index a7239154633..d9cbee33c0f 100644
--- a/source/blender/nodes/shader/nodes/node_shader_sepcombRGB.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_sepcombRGB.cc
@@ -96,7 +96,7 @@ class SeparateRGBFunction : public blender::fn::MultiFunction {
}
};
-static void sh_node_seprgb_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder)
+static void sh_node_seprgb_build_multi_function(blender::nodes::NodeMultiFunctionBuilder &builder)
{
static SeparateRGBFunction fn;
builder.set_matching_fn(fn);
@@ -106,11 +106,11 @@ void register_node_type_sh_seprgb(void)
{
static bNodeType ntype;
- sh_fn_node_type_base(&ntype, SH_NODE_SEPRGB, "Separate RGB", NODE_CLASS_CONVERTOR, 0);
+ sh_fn_node_type_base(&ntype, SH_NODE_SEPRGB, "Separate RGB", NODE_CLASS_CONVERTER, 0);
node_type_socket_templates(&ntype, sh_node_seprgb_in, sh_node_seprgb_out);
node_type_exec(&ntype, nullptr, nullptr, node_shader_exec_seprgb);
node_type_gpu(&ntype, gpu_shader_seprgb);
- ntype.expand_in_mf_network = sh_node_seprgb_expand_in_mf_network;
+ ntype.build_multi_function = sh_node_seprgb_build_multi_function;
nodeRegisterType(&ntype);
}
@@ -153,7 +153,7 @@ static int gpu_shader_combrgb(GPUMaterial *mat,
return GPU_stack_link(mat, node, "combine_rgb", in, out);
}
-static void sh_node_combrgb_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder)
+static void sh_node_combrgb_build_multi_function(blender::nodes::NodeMultiFunctionBuilder &builder)
{
static blender::fn::CustomMF_SI_SI_SI_SO<float, float, float, blender::ColorGeometry4f> fn{
"Combine RGB",
@@ -165,11 +165,11 @@ void register_node_type_sh_combrgb(void)
{
static bNodeType ntype;
- sh_fn_node_type_base(&ntype, SH_NODE_COMBRGB, "Combine RGB", NODE_CLASS_CONVERTOR, 0);
+ sh_fn_node_type_base(&ntype, SH_NODE_COMBRGB, "Combine RGB", NODE_CLASS_CONVERTER, 0);
node_type_socket_templates(&ntype, sh_node_combrgb_in, sh_node_combrgb_out);
node_type_exec(&ntype, nullptr, nullptr, node_shader_exec_combrgb);
node_type_gpu(&ntype, gpu_shader_combrgb);
- ntype.expand_in_mf_network = sh_node_combrgb_expand_in_mf_network;
+ ntype.build_multi_function = sh_node_combrgb_build_multi_function;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/shader/nodes/node_shader_sepcombXYZ.cc b/source/blender/nodes/shader/nodes/node_shader_sepcombXYZ.cc
index efa9581c414..3048ed1e9fc 100644
--- a/source/blender/nodes/shader/nodes/node_shader_sepcombXYZ.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_sepcombXYZ.cc
@@ -81,7 +81,7 @@ class MF_SeparateXYZ : public blender::fn::MultiFunction {
}
};
-static void sh_node_sepxyz_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder)
+static void sh_node_sepxyz_build_multi_function(blender::nodes::NodeMultiFunctionBuilder &builder)
{
static MF_SeparateXYZ separate_fn;
builder.set_matching_fn(separate_fn);
@@ -91,10 +91,10 @@ void register_node_type_sh_sepxyz(void)
{
static bNodeType ntype;
- sh_fn_node_type_base(&ntype, SH_NODE_SEPXYZ, "Separate XYZ", NODE_CLASS_CONVERTOR, 0);
+ sh_fn_node_type_base(&ntype, SH_NODE_SEPXYZ, "Separate XYZ", NODE_CLASS_CONVERTER, 0);
node_type_socket_templates(&ntype, sh_node_sepxyz_in, sh_node_sepxyz_out);
node_type_gpu(&ntype, gpu_shader_sepxyz);
- ntype.expand_in_mf_network = sh_node_sepxyz_expand_in_mf_network;
+ ntype.build_multi_function = sh_node_sepxyz_build_multi_function;
nodeRegisterType(&ntype);
}
@@ -120,7 +120,7 @@ static int gpu_shader_combxyz(GPUMaterial *mat,
return GPU_stack_link(mat, node, "combine_xyz", in, out);
}
-static void sh_node_combxyz_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder)
+static void sh_node_combxyz_build_multi_function(blender::nodes::NodeMultiFunctionBuilder &builder)
{
static blender::fn::CustomMF_SI_SI_SI_SO<float, float, float, blender::float3> fn{
"Combine Vector", [](float x, float y, float z) { return blender::float3(x, y, z); }};
@@ -131,10 +131,10 @@ void register_node_type_sh_combxyz(void)
{
static bNodeType ntype;
- sh_fn_node_type_base(&ntype, SH_NODE_COMBXYZ, "Combine XYZ", NODE_CLASS_CONVERTOR, 0);
+ sh_fn_node_type_base(&ntype, SH_NODE_COMBXYZ, "Combine XYZ", NODE_CLASS_CONVERTER, 0);
node_type_socket_templates(&ntype, sh_node_combxyz_in, sh_node_combxyz_out);
node_type_gpu(&ntype, gpu_shader_combxyz);
- ntype.expand_in_mf_network = sh_node_combxyz_expand_in_mf_network;
+ ntype.build_multi_function = sh_node_combxyz_build_multi_function;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/shader/nodes/node_shader_shaderToRgb.c b/source/blender/nodes/shader/nodes/node_shader_shaderToRgb.c
index 4216207d643..25c30aa4081 100644
--- a/source/blender/nodes/shader/nodes/node_shader_shaderToRgb.c
+++ b/source/blender/nodes/shader/nodes/node_shader_shaderToRgb.c
@@ -50,7 +50,7 @@ void register_node_type_sh_shadertorgb(void)
{
static bNodeType ntype;
- sh_node_type_base(&ntype, SH_NODE_SHADERTORGB, "Shader to RGB", NODE_CLASS_CONVERTOR, 0);
+ sh_node_type_base(&ntype, SH_NODE_SHADERTORGB, "Shader to RGB", NODE_CLASS_CONVERTER, 0);
node_type_socket_templates(&ntype, sh_node_shadertorgb_in, sh_node_shadertorgb_out);
node_type_init(&ntype, NULL);
node_type_storage(&ntype, "", NULL, NULL);
diff --git a/source/blender/nodes/shader/nodes/node_shader_squeeze.c b/source/blender/nodes/shader/nodes/node_shader_squeeze.c
index c7b6af3980a..ca7bdf41df9 100644
--- a/source/blender/nodes/shader/nodes/node_shader_squeeze.c
+++ b/source/blender/nodes/shader/nodes/node_shader_squeeze.c
@@ -61,7 +61,7 @@ void register_node_type_sh_squeeze(void)
{
static bNodeType ntype;
- sh_node_type_base(&ntype, SH_NODE_SQUEEZE, "Squeeze Value", NODE_CLASS_CONVERTOR, 0);
+ sh_node_type_base(&ntype, SH_NODE_SQUEEZE, "Squeeze Value", NODE_CLASS_CONVERTER, 0);
node_type_socket_templates(&ntype, sh_node_squeeze_in, sh_node_squeeze_out);
node_type_storage(&ntype, "", NULL, NULL);
node_type_exec(&ntype, NULL, NULL, node_shader_exec_squeeze);
diff --git a/source/blender/nodes/shader/nodes/node_shader_valToRgb.cc b/source/blender/nodes/shader/nodes/node_shader_valToRgb.cc
index 5b2eb300aac..62f6b38c79f 100644
--- a/source/blender/nodes/shader/nodes/node_shader_valToRgb.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_valToRgb.cc
@@ -163,9 +163,10 @@ class ColorBandFunction : public blender::fn::MultiFunction {
}
};
-static void sh_node_valtorgb_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder)
+static void sh_node_valtorgb_build_multi_function(
+ blender::nodes::NodeMultiFunctionBuilder &builder)
{
- bNode &bnode = builder.bnode();
+ bNode &bnode = builder.node();
const ColorBand *color_band = (const ColorBand *)bnode.storage;
builder.construct_and_set_matching_fn<ColorBandFunction>(*color_band);
}
@@ -174,14 +175,14 @@ void register_node_type_sh_valtorgb(void)
{
static bNodeType ntype;
- sh_fn_node_type_base(&ntype, SH_NODE_VALTORGB, "ColorRamp", NODE_CLASS_CONVERTOR, 0);
+ sh_fn_node_type_base(&ntype, SH_NODE_VALTORGB, "ColorRamp", NODE_CLASS_CONVERTER, 0);
node_type_socket_templates(&ntype, sh_node_valtorgb_in, sh_node_valtorgb_out);
node_type_init(&ntype, node_shader_init_valtorgb);
node_type_size_preset(&ntype, NODE_SIZE_LARGE);
node_type_storage(&ntype, "ColorBand", node_free_standard_storage, node_copy_standard_storage);
node_type_exec(&ntype, nullptr, nullptr, node_shader_exec_valtorgb);
node_type_gpu(&ntype, gpu_shader_valtorgb);
- ntype.expand_in_mf_network = sh_node_valtorgb_expand_in_mf_network;
+ ntype.build_multi_function = sh_node_valtorgb_build_multi_function;
nodeRegisterType(&ntype);
}
@@ -220,7 +221,7 @@ void register_node_type_sh_rgbtobw(void)
{
static bNodeType ntype;
- sh_node_type_base(&ntype, SH_NODE_RGBTOBW, "RGB to BW", NODE_CLASS_CONVERTOR, 0);
+ sh_node_type_base(&ntype, SH_NODE_RGBTOBW, "RGB to BW", NODE_CLASS_CONVERTER, 0);
node_type_socket_templates(&ntype, sh_node_rgbtobw_in, sh_node_rgbtobw_out);
node_type_exec(&ntype, nullptr, nullptr, node_shader_exec_rgbtobw);
node_type_gpu(&ntype, gpu_shader_rgbtobw);
diff --git a/source/blender/nodes/shader/nodes/node_shader_value.cc b/source/blender/nodes/shader/nodes/node_shader_value.cc
index 495c8d12824..602d5a1cf56 100644
--- a/source/blender/nodes/shader/nodes/node_shader_value.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_value.cc
@@ -39,9 +39,9 @@ static int gpu_shader_value(GPUMaterial *mat,
return GPU_stack_link(mat, node, "set_value", in, out, link);
}
-static void sh_node_value_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder)
+static void sh_node_value_build_multi_function(blender::nodes::NodeMultiFunctionBuilder &builder)
{
- const bNodeSocket *bsocket = builder.dnode()->output(0).bsocket();
+ const bNodeSocket *bsocket = (bNodeSocket *)builder.node().outputs.first;
const bNodeSocketValueFloat *value = (const bNodeSocketValueFloat *)bsocket->default_value;
builder.construct_and_set_matching_fn<blender::fn::CustomMF_Constant<float>>(value->value);
}
@@ -53,7 +53,7 @@ void register_node_type_sh_value(void)
sh_fn_node_type_base(&ntype, SH_NODE_VALUE, "Value", NODE_CLASS_INPUT, 0);
node_type_socket_templates(&ntype, nullptr, sh_node_value_out);
node_type_gpu(&ntype, gpu_shader_value);
- ntype.expand_in_mf_network = sh_node_value_expand_in_mf_network;
+ ntype.build_multi_function = sh_node_value_build_multi_function;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/shader/nodes/node_shader_vector_math.cc b/source/blender/nodes/shader/nodes/node_shader_vector_math.cc
index 419a11201aa..4424db6aed1 100644
--- a/source/blender/nodes/shader/nodes/node_shader_vector_math.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_vector_math.cc
@@ -183,12 +183,11 @@ static void node_shader_update_vector_math(bNodeTree *UNUSED(ntree), bNode *node
}
}
-static const blender::fn::MultiFunction &get_multi_function(
- blender::nodes::NodeMFNetworkBuilder &builder)
+static const blender::fn::MultiFunction *get_multi_function(bNode &node)
{
using blender::float3;
- NodeVectorMathOperation operation = NodeVectorMathOperation(builder.bnode().custom1);
+ NodeVectorMathOperation operation = NodeVectorMathOperation(node.custom1);
const blender::fn::MultiFunction *multi_fn = nullptr;
@@ -199,7 +198,7 @@ static const blender::fn::MultiFunction &get_multi_function(
multi_fn = &fn;
});
if (multi_fn != nullptr) {
- return *multi_fn;
+ return multi_fn;
}
blender::nodes::try_dispatch_float_math_fl3_fl3_fl3_to_fl3(
@@ -209,7 +208,7 @@ static const blender::fn::MultiFunction &get_multi_function(
multi_fn = &fn;
});
if (multi_fn != nullptr) {
- return *multi_fn;
+ return multi_fn;
}
blender::nodes::try_dispatch_float_math_fl3_fl3_fl_to_fl3(
@@ -219,7 +218,7 @@ static const blender::fn::MultiFunction &get_multi_function(
multi_fn = &fn;
});
if (multi_fn != nullptr) {
- return *multi_fn;
+ return multi_fn;
}
blender::nodes::try_dispatch_float_math_fl3_fl3_to_fl(
@@ -229,7 +228,7 @@ static const blender::fn::MultiFunction &get_multi_function(
multi_fn = &fn;
});
if (multi_fn != nullptr) {
- return *multi_fn;
+ return multi_fn;
}
blender::nodes::try_dispatch_float_math_fl3_fl_to_fl3(
@@ -239,7 +238,7 @@ static const blender::fn::MultiFunction &get_multi_function(
multi_fn = &fn;
});
if (multi_fn != nullptr) {
- return *multi_fn;
+ return multi_fn;
}
blender::nodes::try_dispatch_float_math_fl3_to_fl3(
@@ -248,7 +247,7 @@ static const blender::fn::MultiFunction &get_multi_function(
multi_fn = &fn;
});
if (multi_fn != nullptr) {
- return *multi_fn;
+ return multi_fn;
}
blender::nodes::try_dispatch_float_math_fl3_to_fl(
@@ -257,15 +256,16 @@ static const blender::fn::MultiFunction &get_multi_function(
multi_fn = &fn;
});
if (multi_fn != nullptr) {
- return *multi_fn;
+ return multi_fn;
}
- return builder.get_not_implemented_fn();
+ return nullptr;
}
-static void sh_node_vector_math_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder)
+static void sh_node_vector_math_build_multi_function(
+ blender::nodes::NodeMultiFunctionBuilder &builder)
{
- const blender::fn::MultiFunction &fn = get_multi_function(builder);
+ const blender::fn::MultiFunction *fn = get_multi_function(builder.node());
builder.set_matching_fn(fn);
}
@@ -278,7 +278,7 @@ void register_node_type_sh_vect_math(void)
node_type_label(&ntype, node_vector_math_label);
node_type_gpu(&ntype, gpu_shader_vector_math);
node_type_update(&ntype, node_shader_update_vector_math);
- ntype.expand_in_mf_network = sh_node_vector_math_expand_in_mf_network;
+ ntype.build_multi_function = sh_node_vector_math_build_multi_function;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/shader/nodes/node_shader_vector_rotate.cc b/source/blender/nodes/shader/nodes/node_shader_vector_rotate.cc
index 3b2c2fa5a03..bc51b7e29ea 100644
--- a/source/blender/nodes/shader/nodes/node_shader_vector_rotate.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_vector_rotate.cc
@@ -100,11 +100,10 @@ static float3 sh_node_vector_rotate_euler(const float3 vector,
return result + center;
}
-static const blender::fn::MultiFunction &get_multi_function(
- blender::nodes::NodeMFNetworkBuilder &builder)
+static const blender::fn::MultiFunction *get_multi_function(bNode &node)
{
- bool invert = builder.bnode().custom2;
- const int mode = builder.bnode().custom1;
+ bool invert = node.custom2;
+ const int mode = node.custom1;
switch (mode) {
case NODE_VECTOR_ROTATE_TYPE_AXIS: {
@@ -113,13 +112,13 @@ static const blender::fn::MultiFunction &get_multi_function(
"Rotate Axis", [](float3 in, float3 center, float3 axis, float angle) {
return sh_node_vector_rotate_around_axis(in, center, axis, -angle);
}};
- return fn;
+ return &fn;
}
static blender::fn::CustomMF_SI_SI_SI_SI_SO<float3, float3, float3, float, float3> fn{
"Rotate Axis", [](float3 in, float3 center, float3 axis, float angle) {
return sh_node_vector_rotate_around_axis(in, center, axis, angle);
}};
- return fn;
+ return &fn;
}
case NODE_VECTOR_ROTATE_TYPE_AXIS_X: {
float3 axis = float3(1.0f, 0.0f, 0.0f);
@@ -128,13 +127,13 @@ static const blender::fn::MultiFunction &get_multi_function(
"Rotate X-Axis", [=](float3 in, float3 center, float angle) {
return sh_node_vector_rotate_around_axis(in, center, axis, -angle);
}};
- return fn;
+ return &fn;
}
static blender::fn::CustomMF_SI_SI_SI_SO<float3, float3, float, float3> fn{
"Rotate X-Axis", [=](float3 in, float3 center, float angle) {
return sh_node_vector_rotate_around_axis(in, center, axis, angle);
}};
- return fn;
+ return &fn;
}
case NODE_VECTOR_ROTATE_TYPE_AXIS_Y: {
float3 axis = float3(0.0f, 1.0f, 0.0f);
@@ -143,13 +142,13 @@ static const blender::fn::MultiFunction &get_multi_function(
"Rotate Y-Axis", [=](float3 in, float3 center, float angle) {
return sh_node_vector_rotate_around_axis(in, center, axis, -angle);
}};
- return fn;
+ return &fn;
}
static blender::fn::CustomMF_SI_SI_SI_SO<float3, float3, float, float3> fn{
"Rotate Y-Axis", [=](float3 in, float3 center, float angle) {
return sh_node_vector_rotate_around_axis(in, center, axis, angle);
}};
- return fn;
+ return &fn;
}
case NODE_VECTOR_ROTATE_TYPE_AXIS_Z: {
float3 axis = float3(0.0f, 0.0f, 1.0f);
@@ -158,13 +157,13 @@ static const blender::fn::MultiFunction &get_multi_function(
"Rotate Z-Axis", [=](float3 in, float3 center, float angle) {
return sh_node_vector_rotate_around_axis(in, center, axis, -angle);
}};
- return fn;
+ return &fn;
}
static blender::fn::CustomMF_SI_SI_SI_SO<float3, float3, float, float3> fn{
"Rotate Z-Axis", [=](float3 in, float3 center, float angle) {
return sh_node_vector_rotate_around_axis(in, center, axis, angle);
}};
- return fn;
+ return &fn;
}
case NODE_VECTOR_ROTATE_TYPE_EULER_XYZ: {
if (invert) {
@@ -172,24 +171,24 @@ static const blender::fn::MultiFunction &get_multi_function(
"Rotate Euler", [](float3 in, float3 center, float3 rotation) {
return sh_node_vector_rotate_euler(in, center, rotation, true);
}};
- return fn;
+ return &fn;
}
static blender::fn::CustomMF_SI_SI_SI_SO<float3, float3, float3, float3> fn{
"Rotate Euler", [](float3 in, float3 center, float3 rotation) {
return sh_node_vector_rotate_euler(in, center, rotation, false);
}};
- return fn;
+ return &fn;
}
default:
BLI_assert_unreachable();
- return builder.get_not_implemented_fn();
+ return nullptr;
}
}
-static void sh_node_vector_rotate_expand_in_mf_network(
- blender::nodes::NodeMFNetworkBuilder &builder)
+static void sh_node_vector_rotate_build_multi_function(
+ blender::nodes::NodeMultiFunctionBuilder &builder)
{
- const blender::fn::MultiFunction &fn = get_multi_function(builder);
+ const blender::fn::MultiFunction *fn = get_multi_function(builder.node());
builder.set_matching_fn(fn);
}
@@ -211,7 +210,7 @@ void register_node_type_sh_vector_rotate(void)
node_type_socket_templates(&ntype, sh_node_vector_rotate_in, sh_node_vector_rotate_out);
node_type_gpu(&ntype, gpu_shader_vector_rotate);
node_type_update(&ntype, node_shader_update_vector_rotate);
- ntype.expand_in_mf_network = sh_node_vector_rotate_expand_in_mf_network;
+ ntype.build_multi_function = sh_node_vector_rotate_build_multi_function;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/shader/nodes/node_shader_wavelength.c b/source/blender/nodes/shader/nodes/node_shader_wavelength.c
index 30f69557020..f978537ee85 100644
--- a/source/blender/nodes/shader/nodes/node_shader_wavelength.c
+++ b/source/blender/nodes/shader/nodes/node_shader_wavelength.c
@@ -62,7 +62,7 @@ void register_node_type_sh_wavelength(void)
{
static bNodeType ntype;
- sh_node_type_base(&ntype, SH_NODE_WAVELENGTH, "Wavelength", NODE_CLASS_CONVERTOR, 0);
+ sh_node_type_base(&ntype, SH_NODE_WAVELENGTH, "Wavelength", NODE_CLASS_CONVERTER, 0);
node_type_size_preset(&ntype, NODE_SIZE_MIDDLE);
node_type_socket_templates(&ntype, sh_node_wavelength_in, sh_node_wavelength_out);
node_type_init(&ntype, NULL);
diff --git a/source/blender/nodes/texture/node_texture_tree.c b/source/blender/nodes/texture/node_texture_tree.c
index f771b4934b2..7452007639c 100644
--- a/source/blender/nodes/texture/node_texture_tree.c
+++ b/source/blender/nodes/texture/node_texture_tree.c
@@ -101,7 +101,7 @@ static void foreach_nodeclass(Scene *UNUSED(scene), void *calldata, bNodeClassCa
func(calldata, NODE_CLASS_OP_COLOR, N_("Color"));
func(calldata, NODE_CLASS_PATTERN, N_("Patterns"));
func(calldata, NODE_CLASS_TEXTURE, N_("Textures"));
- func(calldata, NODE_CLASS_CONVERTOR, N_("Convertor"));
+ func(calldata, NODE_CLASS_CONVERTER, N_("Converter"));
func(calldata, NODE_CLASS_DISTORT, N_("Distort"));
func(calldata, NODE_CLASS_GROUP, N_("Group"));
func(calldata, NODE_CLASS_INTERFACE, N_("Interface"));
diff --git a/source/blender/nodes/texture/nodes/node_texture_distance.c b/source/blender/nodes/texture/nodes/node_texture_distance.c
index 489318514e5..f7deac9ff4a 100644
--- a/source/blender/nodes/texture/nodes/node_texture_distance.c
+++ b/source/blender/nodes/texture/nodes/node_texture_distance.c
@@ -61,7 +61,7 @@ void register_node_type_tex_distance(void)
{
static bNodeType ntype;
- tex_node_type_base(&ntype, TEX_NODE_DISTANCE, "Distance", NODE_CLASS_CONVERTOR, 0);
+ tex_node_type_base(&ntype, TEX_NODE_DISTANCE, "Distance", NODE_CLASS_CONVERTER, 0);
node_type_socket_templates(&ntype, inputs, outputs);
node_type_storage(&ntype, "", NULL, NULL);
node_type_exec(&ntype, NULL, NULL, exec);
diff --git a/source/blender/nodes/texture/nodes/node_texture_math.c b/source/blender/nodes/texture/nodes/node_texture_math.c
index 53022c9e120..ab226a4dd38 100644
--- a/source/blender/nodes/texture/nodes/node_texture_math.c
+++ b/source/blender/nodes/texture/nodes/node_texture_math.c
@@ -335,7 +335,7 @@ void register_node_type_tex_math(void)
{
static bNodeType ntype;
- tex_node_type_base(&ntype, TEX_NODE_MATH, "Math", NODE_CLASS_CONVERTOR, 0);
+ tex_node_type_base(&ntype, TEX_NODE_MATH, "Math", NODE_CLASS_CONVERTER, 0);
node_type_socket_templates(&ntype, inputs, outputs);
node_type_label(&ntype, node_math_label);
node_type_storage(&ntype, "", NULL, NULL);
diff --git a/source/blender/nodes/texture/nodes/node_texture_valToNor.c b/source/blender/nodes/texture/nodes/node_texture_valToNor.c
index 2fb04777848..5ccd44b8bf0 100644
--- a/source/blender/nodes/texture/nodes/node_texture_valToNor.c
+++ b/source/blender/nodes/texture/nodes/node_texture_valToNor.c
@@ -80,7 +80,7 @@ void register_node_type_tex_valtonor(void)
{
static bNodeType ntype;
- tex_node_type_base(&ntype, TEX_NODE_VALTONOR, "Value to Normal", NODE_CLASS_CONVERTOR, 0);
+ tex_node_type_base(&ntype, TEX_NODE_VALTONOR, "Value to Normal", NODE_CLASS_CONVERTER, 0);
node_type_socket_templates(&ntype, inputs, outputs);
node_type_exec(&ntype, NULL, NULL, exec);
diff --git a/source/blender/nodes/texture/nodes/node_texture_valToRgb.c b/source/blender/nodes/texture/nodes/node_texture_valToRgb.c
index 8d365e13fc4..2446ef05e0c 100644
--- a/source/blender/nodes/texture/nodes/node_texture_valToRgb.c
+++ b/source/blender/nodes/texture/nodes/node_texture_valToRgb.c
@@ -63,7 +63,7 @@ void register_node_type_tex_valtorgb(void)
{
static bNodeType ntype;
- tex_node_type_base(&ntype, TEX_NODE_VALTORGB, "ColorRamp", NODE_CLASS_CONVERTOR, 0);
+ tex_node_type_base(&ntype, TEX_NODE_VALTORGB, "ColorRamp", NODE_CLASS_CONVERTER, 0);
node_type_socket_templates(&ntype, valtorgb_in, valtorgb_out);
node_type_size_preset(&ntype, NODE_SIZE_LARGE);
node_type_init(&ntype, valtorgb_init);
@@ -105,7 +105,7 @@ void register_node_type_tex_rgbtobw(void)
{
static bNodeType ntype;
- tex_node_type_base(&ntype, TEX_NODE_RGBTOBW, "RGB to BW", NODE_CLASS_CONVERTOR, 0);
+ tex_node_type_base(&ntype, TEX_NODE_RGBTOBW, "RGB to BW", NODE_CLASS_CONVERTER, 0);
node_type_socket_templates(&ntype, rgbtobw_in, rgbtobw_out);
node_type_exec(&ntype, NULL, NULL, rgbtobw_exec);
diff --git a/source/blender/python/intern/bpy_app_icons.c b/source/blender/python/intern/bpy_app_icons.c
index 7cca3ae4700..acd809fb8d5 100644
--- a/source/blender/python/intern/bpy_app_icons.c
+++ b/source/blender/python/intern/bpy_app_icons.c
@@ -35,7 +35,7 @@
/* We may want to load direct from file. */
PyDoc_STRVAR(
bpy_app_icons_new_triangles_doc,
- ".. function:: new_triangles(range, coords, colors)"
+ ".. function:: new_triangles(range, coords, colors)\n"
"\n"
" Create a new icon from triangle geometry.\n"
"\n"
@@ -91,7 +91,7 @@ static PyObject *bpy_app_icons_new_triangles(PyObject *UNUSED(self), PyObject *a
}
PyDoc_STRVAR(bpy_app_icons_new_triangles_from_file_doc,
- ".. function:: new_triangles_from_file(filename)"
+ ".. function:: new_triangles_from_file(filename)\n"
"\n"
" Create a new icon from triangle geometry.\n"
"\n"
@@ -122,7 +122,7 @@ static PyObject *bpy_app_icons_new_triangles_from_file(PyObject *UNUSED(self),
}
PyDoc_STRVAR(bpy_app_icons_release_doc,
- ".. function:: release(icon_id)"
+ ".. function:: release(icon_id)\n"
"\n"
" Release the icon.\n");
static PyObject *bpy_app_icons_release(PyObject *UNUSED(self), PyObject *args, PyObject *kw)
diff --git a/source/blender/sequencer/intern/image_cache.c b/source/blender/sequencer/intern/image_cache.c
index 604c9900355..86bd840ce31 100644
--- a/source/blender/sequencer/intern/image_cache.c
+++ b/source/blender/sequencer/intern/image_cache.c
@@ -102,7 +102,7 @@
/* <cache type>-<resolution X>x<resolution Y>-<rendersize>%(<view_id>)-<frame no>.dcf */
#define DCACHE_FNAME_FORMAT "%d-%dx%d-%d%%(%d)-%d.dcf"
#define DCACHE_IMAGES_PER_FILE 100
-#define DCACHE_CURRENT_VERSION 1
+#define DCACHE_CURRENT_VERSION 2
#define COLORSPACE_NAME_MAX 64 /* XXX: defined in imb intern */
typedef struct DiskCacheHeaderEntry {
@@ -496,24 +496,34 @@ static size_t deflate_imbuf_to_file(ImBuf *ibuf,
int level,
DiskCacheHeaderEntry *header_entry)
{
- if (ibuf->rect) {
- return BLI_gzip_mem_to_file_at_pos(
- ibuf->rect, header_entry->size_raw, file, header_entry->offset, level);
+ void *data = (ibuf->rect != NULL) ? (void *)ibuf->rect : (void *)ibuf->rect_float;
+
+ /* Apply compression if wanted, otherwise just write directly to the file. */
+ if (level > 0) {
+ return BLI_file_zstd_from_mem_at_pos(
+ data, header_entry->size_raw, file, header_entry->offset, level);
}
- return BLI_gzip_mem_to_file_at_pos(
- ibuf->rect_float, header_entry->size_raw, file, header_entry->offset, level);
+ fseek(file, header_entry->offset, SEEK_SET);
+ return fwrite(data, 1, header_entry->size_raw, file);
}
static size_t inflate_file_to_imbuf(ImBuf *ibuf, FILE *file, DiskCacheHeaderEntry *header_entry)
{
- if (ibuf->rect) {
- return BLI_ungzip_file_to_mem_at_pos(
- ibuf->rect, header_entry->size_raw, file, header_entry->offset);
+ void *data = (ibuf->rect != NULL) ? (void *)ibuf->rect : (void *)ibuf->rect_float;
+ char header[4];
+ fseek(file, header_entry->offset, SEEK_SET);
+ if (fread(header, 1, sizeof(header), file) != sizeof(header)) {
+ return 0;
+ }
+
+ /* Check if the data is compressed or raw. */
+ if (BLI_file_magic_is_zstd(header)) {
+ return BLI_file_unzstd_to_mem_at_pos(data, header_entry->size_raw, file, header_entry->offset);
}
- return BLI_ungzip_file_to_mem_at_pos(
- ibuf->rect_float, header_entry->size_raw, file, header_entry->offset);
+ fseek(file, header_entry->offset, SEEK_SET);
+ return fread(data, 1, header_entry->size_raw, file);
}
static bool seq_disk_cache_read_header(FILE *file, DiskCacheHeader *header)
diff --git a/source/blender/sequencer/intern/strip_edit.c b/source/blender/sequencer/intern/strip_edit.c
index e92afee08cd..b83ee92f117 100644
--- a/source/blender/sequencer/intern/strip_edit.c
+++ b/source/blender/sequencer/intern/strip_edit.c
@@ -476,6 +476,6 @@ bool SEQ_edit_remove_gaps(Scene *scene,
void SEQ_edit_sequence_name_set(Scene *scene, Sequence *seq, const char *new_name)
{
BLI_strncpy_utf8(seq->name + 2, new_name, MAX_NAME - 2);
- BLI_utf8_invalid_strip(seq->name + 2, strlen(seq->name + 2));
+ BLI_str_utf8_invalid_strip(seq->name + 2, strlen(seq->name + 2));
SEQ_sequence_lookup_tag(scene, SEQ_LOOKUP_TAG_INVALID);
}
diff --git a/source/blender/windowmanager/CMakeLists.txt b/source/blender/windowmanager/CMakeLists.txt
index b7fbb9bb82b..4d65726fe2b 100644
--- a/source/blender/windowmanager/CMakeLists.txt
+++ b/source/blender/windowmanager/CMakeLists.txt
@@ -48,10 +48,6 @@ set(INC
${CMAKE_BINARY_DIR}/source/blender/makesdna/intern
)
-set(INC_SYS
- ${ZLIB_INCLUDE_DIRS}
-)
-
set(SRC
intern/wm.c
intern/wm_cursors.c
diff --git a/source/blender/windowmanager/intern/wm_event_system.c b/source/blender/windowmanager/intern/wm_event_system.c
index 5cc361fc1bd..b9a3dd0c3fb 100644
--- a/source/blender/windowmanager/intern/wm_event_system.c
+++ b/source/blender/windowmanager/intern/wm_event_system.c
@@ -3471,8 +3471,8 @@ void wm_event_do_handlers(bContext *C)
}
CTX_wm_area_set(C, NULL);
- /* NOTE: do not escape on WM_HANDLER_BREAK,
- * mousemove needs handled for previous area. */
+ /* NOTE: do not escape on #WM_HANDLER_BREAK,
+ * mouse-move needs handled for previous area. */
}
}
diff --git a/source/blender/windowmanager/intern/wm_files.c b/source/blender/windowmanager/intern/wm_files.c
index b53ad0ee927..f83511e76f0 100644
--- a/source/blender/windowmanager/intern/wm_files.c
+++ b/source/blender/windowmanager/intern/wm_files.c
@@ -28,11 +28,10 @@
* winsock stuff.
*/
#include <errno.h>
+#include <fcntl.h> /* for open flags (O_BINARY, O_RDONLY). */
#include <stddef.h>
#include <string.h>
-#include "zlib.h" /* wm_read_exotic() */
-
#ifdef WIN32
/* Need to include windows.h so _WIN32_IE is defined. */
# include <windows.h>
@@ -51,6 +50,7 @@
#include "BLI_blenlib.h"
#include "BLI_fileops_types.h"
+#include "BLI_filereader.h"
#include "BLI_linklist.h"
#include "BLI_math.h"
#include "BLI_system.h"
@@ -481,53 +481,64 @@ static void wm_init_userdef(Main *bmain)
/* intended to check for non-blender formats but for now it only reads blends */
static int wm_read_exotic(const char *name)
{
- int len;
- gzFile gzfile;
+ /* make sure we're not trying to read a directory.... */
+
+ int namelen = strlen(name);
+ if (namelen > 0 && ELEM(name[namelen - 1], '/', '\\')) {
+ return BKE_READ_EXOTIC_FAIL_PATH;
+ }
+
+ /* open the file. */
+ const int filedes = BLI_open(name, O_BINARY | O_RDONLY, 0);
+ if (filedes == -1) {
+ return BKE_READ_EXOTIC_FAIL_OPEN;
+ }
+
+ FileReader *rawfile = BLI_filereader_new_file(filedes);
+ if (rawfile == NULL) {
+ return BKE_READ_EXOTIC_FAIL_OPEN;
+ }
+
+ /* read the header (7 bytes are enough to identify all known types). */
char header[7];
- int retval;
+ if (rawfile->read(rawfile, header, sizeof(header)) != sizeof(header)) {
+ rawfile->close(rawfile);
+ return BKE_READ_EXOTIC_FAIL_FORMAT;
+ }
+ rawfile->seek(rawfile, 0, SEEK_SET);
- /* make sure we're not trying to read a directory.... */
+ /* check for uncompressed .blend */
+ if (STREQLEN(header, "BLENDER", 7)) {
+ rawfile->close(rawfile);
+ return BKE_READ_EXOTIC_OK_BLEND;
+ }
- len = strlen(name);
- if (len > 0 && ELEM(name[len - 1], '/', '\\')) {
- retval = BKE_READ_EXOTIC_FAIL_PATH;
+ /* check for compressed .blend */
+ FileReader *compressed_file = NULL;
+ if (BLI_file_magic_is_gzip(header)) {
+ /* In earlier versions of Blender (before 3.0), compressed files used Gzip instead of Zstd.
+ * While these files will no longer be written, there still needs to be reading support. */
+ compressed_file = BLI_filereader_new_gzip(rawfile);
+ }
+ else if (BLI_file_magic_is_zstd(header)) {
+ compressed_file = BLI_filereader_new_zstd(rawfile);
}
- else {
- gzfile = BLI_gzopen(name, "rb");
- if (gzfile == NULL) {
- retval = BKE_READ_EXOTIC_FAIL_OPEN;
- }
- else {
- len = gzread(gzfile, header, sizeof(header));
- gzclose(gzfile);
- if (len == sizeof(header) && STREQLEN(header, "BLENDER", 7)) {
- retval = BKE_READ_EXOTIC_OK_BLEND;
- }
- else {
- /* We may want to support loading other file formats
- * from their header bytes or file extension.
- * This used to be supported in the code below and may be added
- * back at some point. */
-#if 0
- WM_cursor_wait(true);
- if (is_foo_format(name)) {
- read_foo(name);
- retval = BKE_READ_EXOTIC_OK_OTHER;
- }
- else
-#endif
- {
- retval = BKE_READ_EXOTIC_FAIL_FORMAT;
- }
-#if 0
- WM_cursor_wait(false);
-#endif
- }
+ /* If a compression signature matches, try decompressing the start and check if it's a .blend */
+ if (compressed_file != NULL) {
+ size_t len = compressed_file->read(compressed_file, header, sizeof(header));
+ compressed_file->close(compressed_file);
+ if (len == sizeof(header) && STREQLEN(header, "BLENDER", 7)) {
+ return BKE_READ_EXOTIC_OK_BLEND;
}
}
+ else {
+ rawfile->close(rawfile);
+ }
+
+ /* Add check for future file formats here. */
- return retval;
+ return BKE_READ_EXOTIC_FAIL_FORMAT;
}
/** \} */
diff --git a/source/blender/windowmanager/intern/wm_operators.c b/source/blender/windowmanager/intern/wm_operators.c
index b5a038757c2..f1a8f4ffd71 100644
--- a/source/blender/windowmanager/intern/wm_operators.c
+++ b/source/blender/windowmanager/intern/wm_operators.c
@@ -2497,7 +2497,7 @@ static int radial_control_get_path(PointerRNA *ctx_ptr,
/* get an rna string path from the operator's properties */
char *str;
- if (!(str = RNA_string_get_alloc(op->ptr, name, NULL, 0))) {
+ if (!(str = RNA_string_get_alloc(op->ptr, name, NULL, 0, NULL))) {
return 1;
}