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>2022-08-14 04:50:06 +0300
committerHans Goudey <h.goudey@me.com>2022-08-14 04:50:06 +0300
commit6d041a7d510b43a128b3be393a69c2b45ae4885c (patch)
tree5bcf7e158da5c2d912e0a0e62909664752cdaa3a /source/blender
parent0018db40a6923d78d879e0ac2f1793c13e9b5737 (diff)
parentb5e92c3dfe70f94556719594c6c1b457cda59768 (diff)
Merge branch 'master' into refactor-mesh-bevel-weight-generic
Diffstat (limited to 'source/blender')
-rw-r--r--source/blender/CMakeLists.txt4
-rw-r--r--source/blender/blendthumb/src/blendthumb_extract.cc2
-rw-r--r--source/blender/blenfont/intern/blf_font.c126
-rw-r--r--source/blender/blenfont/intern/blf_glyph.c15
-rw-r--r--source/blender/blenfont/intern/blf_internal.h9
-rw-r--r--source/blender/blenfont/intern/blf_internal_types.h7
-rw-r--r--source/blender/blenkernel/BKE_curves.hh2
-rw-r--r--source/blender/blenkernel/BKE_customdata.h23
-rw-r--r--source/blender/blenkernel/BKE_geometry_set.hh2
-rw-r--r--source/blender/blenkernel/BKE_idtype.h8
-rw-r--r--source/blender/blenkernel/BKE_key.h2
-rw-r--r--source/blender/blenkernel/BKE_lib_id.h10
-rw-r--r--source/blender/blenkernel/BKE_lib_override.h4
-rw-r--r--source/blender/blenkernel/BKE_mesh.h14
-rw-r--r--source/blender/blenkernel/BKE_mesh_legacy_convert.h11
-rw-r--r--source/blender/blenkernel/BKE_mesh_mapping.h20
-rw-r--r--source/blender/blenkernel/BKE_node.h21
-rw-r--r--source/blender/blenkernel/BKE_object.h1
-rw-r--r--source/blender/blenkernel/BKE_paint.h2
-rw-r--r--source/blender/blenkernel/BKE_pbvh.h5
-rw-r--r--source/blender/blenkernel/CMakeLists.txt2
-rw-r--r--source/blender/blenkernel/intern/DerivedMesh.cc2
-rw-r--r--source/blender/blenkernel/intern/attribute_access.cc2
-rw-r--r--source/blender/blenkernel/intern/brush.cc8
-rw-r--r--source/blender/blenkernel/intern/bvhutils.cc19
-rw-r--r--source/blender/blenkernel/intern/collection.c7
-rw-r--r--source/blender/blenkernel/intern/colorband.c2
-rw-r--r--source/blender/blenkernel/intern/constraint.c2
-rw-r--r--source/blender/blenkernel/intern/cryptomatte_test.cc2
-rw-r--r--source/blender/blenkernel/intern/curve.cc2
-rw-r--r--source/blender/blenkernel/intern/curves_geometry.cc2
-rw-r--r--source/blender/blenkernel/intern/customdata.cc57
-rw-r--r--source/blender/blenkernel/intern/displist.cc2
-rw-r--r--source/blender/blenkernel/intern/gpencil_curve.c11
-rw-r--r--source/blender/blenkernel/intern/gpencil_modifier.c3
-rw-r--r--source/blender/blenkernel/intern/icons_rasterize.c2
-rw-r--r--source/blender/blenkernel/intern/idprop.c2
-rw-r--r--source/blender/blenkernel/intern/key.c2
-rw-r--r--source/blender/blenkernel/intern/lib_override.cc26
-rw-r--r--source/blender/blenkernel/intern/lib_query.c6
-rw-r--r--source/blender/blenkernel/intern/library.c2
-rw-r--r--source/blender/blenkernel/intern/material.c2
-rw-r--r--source/blender/blenkernel/intern/mball.cc (renamed from source/blender/blenkernel/intern/mball.c)140
-rw-r--r--source/blender/blenkernel/intern/mesh.cc28
-rw-r--r--source/blender/blenkernel/intern/mesh_convert.cc160
-rw-r--r--source/blender/blenkernel/intern/mesh_evaluate.cc149
-rw-r--r--source/blender/blenkernel/intern/mesh_legacy_convert.cc85
-rw-r--r--source/blender/blenkernel/intern/mesh_mapping.c5
-rw-r--r--source/blender/blenkernel/intern/mesh_tangent.c2
-rw-r--r--source/blender/blenkernel/intern/modifier.c3
-rw-r--r--source/blender/blenkernel/intern/node.cc13
-rw-r--r--source/blender/blenkernel/intern/object_dupli.cc29
-rw-r--r--source/blender/blenkernel/intern/object_update.c42
-rw-r--r--source/blender/blenkernel/intern/ocean.c7
-rw-r--r--source/blender/blenkernel/intern/outliner_treehash.c20
-rw-r--r--source/blender/blenkernel/intern/paint.c22
-rw-r--r--source/blender/blenkernel/intern/particle.c4
-rw-r--r--source/blender/blenkernel/intern/particle_system.c2
-rw-r--r--source/blender/blenkernel/intern/pbvh.c37
-rw-r--r--source/blender/blenkernel/intern/pbvh_intern.h3
-rw-r--r--source/blender/blenkernel/intern/scene.cc2
-rw-r--r--source/blender/blenkernel/intern/shader_fx.c3
-rw-r--r--source/blender/blenkernel/intern/subdiv_converter_mesh.c10
-rw-r--r--source/blender/blenkernel/intern/subsurf_ccg.c3
-rw-r--r--source/blender/blenlib/BLI_cpp_type.hh2
-rw-r--r--source/blender/blenlib/BLI_scanfill.h2
-rw-r--r--source/blender/blenlib/intern/math_geom.c6
-rw-r--r--source/blender/blenlib/intern/mesh_boolean.cc2
-rw-r--r--source/blender/blenlib/intern/smallhash.c3
-rw-r--r--source/blender/blenlib/intern/string_utf8.c2
-rw-r--r--source/blender/blenloader/intern/versioning_290.c8
-rw-r--r--source/blender/bmesh/intern/bmesh_construct.c36
-rw-r--r--source/blender/bmesh/intern/bmesh_mesh.cc145
-rw-r--r--source/blender/bmesh/intern/bmesh_mesh.h2
-rw-r--r--source/blender/bmesh/intern/bmesh_mesh_convert.cc139
-rw-r--r--source/blender/compositor/CMakeLists.txt1328
-rw-r--r--source/blender/compositor/nodes/COM_OutputFileNode.cc4
-rw-r--r--source/blender/compositor/operations/COM_MovieClipOperation.cc2
-rw-r--r--source/blender/compositor/operations/COM_ScaleOperation.cc2
-rw-r--r--source/blender/compositor/realtime_compositor/CMakeLists.txt66
-rw-r--r--source/blender/compositor/realtime_compositor/COM_compile_state.hh170
-rw-r--r--source/blender/compositor/realtime_compositor/COM_context.hh72
-rw-r--r--source/blender/compositor/realtime_compositor/COM_conversion_operation.hh126
-rw-r--r--source/blender/compositor/realtime_compositor/COM_domain.hh166
-rw-r--r--source/blender/compositor/realtime_compositor/COM_evaluator.hh173
-rw-r--r--source/blender/compositor/realtime_compositor/COM_input_descriptor.hh34
-rw-r--r--source/blender/compositor/realtime_compositor/COM_input_single_value_operation.hh46
-rw-r--r--source/blender/compositor/realtime_compositor/COM_node_operation.hh56
-rw-r--r--source/blender/compositor/realtime_compositor/COM_operation.hh175
-rw-r--r--source/blender/compositor/realtime_compositor/COM_realize_on_domain_operation.hh49
-rw-r--r--source/blender/compositor/realtime_compositor/COM_reduce_to_single_value_operation.hh31
-rw-r--r--source/blender/compositor/realtime_compositor/COM_result.hh234
-rw-r--r--source/blender/compositor/realtime_compositor/COM_scheduler.hh21
-rw-r--r--source/blender/compositor/realtime_compositor/COM_shader_node.hh87
-rw-r--r--source/blender/compositor/realtime_compositor/COM_shader_operation.hh242
-rw-r--r--source/blender/compositor/realtime_compositor/COM_simple_operation.hh64
-rw-r--r--source/blender/compositor/realtime_compositor/COM_static_shader_manager.hh33
-rw-r--r--source/blender/compositor/realtime_compositor/COM_texture_pool.hh86
-rw-r--r--source/blender/compositor/realtime_compositor/COM_utilities.hh61
-rw-r--r--source/blender/compositor/realtime_compositor/intern/compile_state.cc163
-rw-r--r--source/blender/compositor/realtime_compositor/intern/context.cc36
-rw-r--r--source/blender/compositor/realtime_compositor/intern/conversion_operation.cc225
-rw-r--r--source/blender/compositor/realtime_compositor/intern/domain.cc38
-rw-r--r--source/blender/compositor/realtime_compositor/intern/evaluator.cc171
-rw-r--r--source/blender/compositor/realtime_compositor/intern/input_single_value_operation.cc57
-rw-r--r--source/blender/compositor/realtime_compositor/intern/node_operation.cc67
-rw-r--r--source/blender/compositor/realtime_compositor/intern/operation.cc201
-rw-r--r--source/blender/compositor/realtime_compositor/intern/realize_on_domain_operation.cc130
-rw-r--r--source/blender/compositor/realtime_compositor/intern/reduce_to_single_value_operation.cc67
-rw-r--r--source/blender/compositor/realtime_compositor/intern/result.cc257
-rw-r--r--source/blender/compositor/realtime_compositor/intern/scheduler.cc311
-rw-r--r--source/blender/compositor/realtime_compositor/intern/shader_node.cc155
-rw-r--r--source/blender/compositor/realtime_compositor/intern/shader_operation.cc522
-rw-r--r--source/blender/compositor/realtime_compositor/intern/simple_operation.cc55
-rw-r--r--source/blender/compositor/realtime_compositor/intern/static_shader_manager.cc24
-rw-r--r--source/blender/compositor/realtime_compositor/intern/texture_pool.cc84
-rw-r--r--source/blender/compositor/realtime_compositor/intern/utilities.cc134
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder.cc19
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder.h2
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_nodes.cc44
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_nodes.h1
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_relations.cc95
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_relations.h1
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_rna.cc12
-rw-r--r--source/blender/depsgraph/intern/depsgraph_relation.h2
-rw-r--r--source/blender/depsgraph/intern/eval/deg_eval.cc2
-rw-r--r--source/blender/depsgraph/intern/eval/deg_eval_visibility.cc57
-rw-r--r--source/blender/depsgraph/intern/eval/deg_eval_visibility.h3
-rw-r--r--source/blender/depsgraph/intern/node/deg_node_operation.cc12
-rw-r--r--source/blender/depsgraph/intern/node/deg_node_operation.h5
-rw-r--r--source/blender/draw/CMakeLists.txt31
-rw-r--r--source/blender/draw/engines/compositor/compositor_engine.cc203
-rw-r--r--source/blender/draw/engines/compositor/compositor_engine.h13
-rw-r--r--source/blender/draw/engines/eevee/eevee_shadows_cascade.c2
-rw-r--r--source/blender/draw/engines/eevee_next/shaders/infos/eevee_depth_of_field_info.hh1
-rw-r--r--source/blender/draw/engines/eevee_next/shaders/infos/eevee_motion_blur_info.hh1
-rw-r--r--source/blender/draw/engines/overlay/overlay_armature.c2
-rw-r--r--source/blender/draw/intern/draw_cache_extract_mesh_render_data.cc17
-rw-r--r--source/blender/draw/intern/draw_cache_impl_pointcloud.cc (renamed from source/blender/draw/intern/draw_cache_impl_pointcloud.c)164
-rw-r--r--source/blender/draw/intern/draw_cache_impl_subdivision.cc10
-rw-r--r--source/blender/draw/intern/draw_debug.c196
-rw-r--r--source/blender/draw/intern/draw_debug.cc721
-rw-r--r--source/blender/draw/intern/draw_debug.h19
-rw-r--r--source/blender/draw/intern/draw_debug.hh198
-rw-r--r--source/blender/draw/intern/draw_manager.c37
-rw-r--r--source/blender/draw/intern/draw_manager.h23
-rw-r--r--source/blender/draw/intern/draw_manager_data.c43
-rw-r--r--source/blender/draw/intern/draw_shader.cc20
-rw-r--r--source/blender/draw/intern/draw_shader.h3
-rw-r--r--source/blender/draw/intern/draw_shader_shared.h65
-rw-r--r--source/blender/draw/intern/mesh_extractors/extract_mesh.hh3
-rw-r--r--source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_edituv.cc57
-rw-r--r--source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_fdots.cc6
-rw-r--r--source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines.cc40
-rw-r--r--source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines_adjacency.cc19
-rw-r--r--source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines_paint_mask.cc6
-rw-r--r--source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_points.cc11
-rw-r--r--source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_tris.cc8
-rw-r--r--source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_lnor.cc12
-rw-r--r--source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_pos_nor.cc11
-rw-r--r--source/blender/draw/intern/shaders/common_debug_draw_lib.glsl216
-rw-r--r--source/blender/draw/intern/shaders/common_debug_print_lib.glsl389
-rw-r--r--source/blender/draw/intern/shaders/common_view_lib.glsl3
-rw-r--r--source/blender/draw/intern/shaders/draw_debug_draw_display_frag.glsl9
-rw-r--r--source/blender/draw/intern/shaders/draw_debug_draw_display_vert.glsl15
-rw-r--r--source/blender/draw/intern/shaders/draw_debug_info.hh52
-rw-r--r--source/blender/draw/intern/shaders/draw_debug_print_display_frag.glsl133
-rw-r--r--source/blender/draw/intern/shaders/draw_debug_print_display_vert.glsl29
-rw-r--r--source/blender/editors/armature/armature_select.c8
-rw-r--r--source/blender/editors/armature/pose_select.c2
-rw-r--r--source/blender/editors/curve/editcurve.c4
-rw-r--r--source/blender/editors/gpencil/gpencil_primitive.c2
-rw-r--r--source/blender/editors/include/ED_mesh.h5
-rw-r--r--source/blender/editors/include/ED_transform_snap_object_context.h6
-rw-r--r--source/blender/editors/include/ED_view3d.h18
-rw-r--r--source/blender/editors/interface/CMakeLists.txt2
-rw-r--r--source/blender/editors/interface/interface.cc2
-rw-r--r--source/blender/editors/interface/interface_anim.cc2
-rw-r--r--source/blender/editors/interface/interface_handlers.c11
-rw-r--r--source/blender/editors/interface/interface_icons.c4
-rw-r--r--source/blender/editors/interface/interface_region_popover.cc2
-rw-r--r--source/blender/editors/interface/interface_region_tooltip.cc2
-rw-r--r--source/blender/editors/interface/interface_templates.c6
-rw-r--r--source/blender/editors/io/CMakeLists.txt6
-rw-r--r--source/blender/editors/io/io_obj.c39
-rw-r--r--source/blender/editors/lattice/editlattice_undo.c2
-rw-r--r--source/blender/editors/mesh/editface.cc179
-rw-r--r--source/blender/editors/mesh/editmesh_knife.c2
-rw-r--r--source/blender/editors/mesh/editmesh_tools.c2
-rw-r--r--source/blender/editors/mesh/editmesh_undo.c4
-rw-r--r--source/blender/editors/mesh/editmesh_utils.c329
-rw-r--r--source/blender/editors/mesh/meshtools.cc23
-rw-r--r--source/blender/editors/metaball/mball_edit.c2
-rw-r--r--source/blender/editors/object/object_select.c2
-rw-r--r--source/blender/editors/object/object_vgroup.c21
-rw-r--r--source/blender/editors/screen/screen_edit.c2
-rw-r--r--source/blender/editors/screen/workspace_edit.c2
-rw-r--r--source/blender/editors/sculpt_paint/CMakeLists.txt2
-rw-r--r--source/blender/editors/sculpt_paint/paint_hide.c15
-rw-r--r--source/blender/editors/sculpt_paint/sculpt.c14
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_dyntopo.c5
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_undo.c16
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_uv.c19
-rw-r--r--source/blender/editors/space_action/action_select.c2
-rw-r--r--source/blender/editors/space_graph/graph_select.c2
-rw-r--r--source/blender/editors/space_node/CMakeLists.txt4
-rw-r--r--source/blender/editors/space_outliner/CMakeLists.txt4
-rw-r--r--source/blender/editors/space_outliner/outliner_collections.cc4
-rw-r--r--source/blender/editors/space_outliner/outliner_draw.cc2
-rw-r--r--source/blender/editors/space_outliner/outliner_intern.hh2
-rw-r--r--source/blender/editors/space_outliner/outliner_ops.cc2
-rw-r--r--source/blender/editors/space_outliner/outliner_tools.cc661
-rw-r--r--source/blender/editors/space_outliner/tree/tree_display_override_library_hierarchies.cc42
-rw-r--r--source/blender/editors/space_outliner/tree/tree_element_overrides.cc3
-rw-r--r--source/blender/editors/space_script/script_edit.c2
-rw-r--r--source/blender/editors/space_sequencer/sequencer_edit.c2
-rw-r--r--source/blender/editors/space_sequencer/sequencer_scopes.c2
-rw-r--r--source/blender/editors/space_text/text_autocomplete.c2
-rw-r--r--source/blender/editors/space_view3d/space_view3d.c5
-rw-r--r--source/blender/editors/space_view3d/view3d_cursor_snap.c147
-rw-r--r--source/blender/editors/space_view3d/view3d_gizmo_armature.c2
-rw-r--r--source/blender/editors/space_view3d/view3d_iterators.c20
-rw-r--r--source/blender/editors/space_view3d/view3d_navigate.c74
-rw-r--r--source/blender/editors/space_view3d/view3d_navigate.h24
-rw-r--r--source/blender/editors/space_view3d/view3d_navigate_roll.c13
-rw-r--r--source/blender/editors/space_view3d/view3d_navigate_smoothview.c278
-rw-r--r--source/blender/editors/space_view3d/view3d_navigate_zoom_border.c11
-rw-r--r--source/blender/editors/space_view3d/view3d_select.c40
-rw-r--r--source/blender/editors/space_view3d/view3d_utils.c46
-rw-r--r--source/blender/editors/space_view3d/view3d_view.c8
-rw-r--r--source/blender/editors/transform/transform_convert_mesh.c5
-rw-r--r--source/blender/editors/transform/transform_convert_mesh_uv.c6
-rw-r--r--source/blender/editors/transform/transform_gizmo_extrude_3d.c2
-rw-r--r--source/blender/editors/transform/transform_mode_bend.c2
-rw-r--r--source/blender/editors/transform/transform_mode_edge_slide.c2
-rw-r--r--source/blender/editors/undo/ed_undo.c2
-rw-r--r--source/blender/editors/util/ed_util.c2
-rw-r--r--source/blender/editors/util/ed_util_imbuf.c2
-rw-r--r--source/blender/editors/uvedit/uvedit_ops.c13
-rw-r--r--source/blender/editors/uvedit/uvedit_select.c4
-rw-r--r--source/blender/editors/uvedit/uvedit_smart_stitch.c43
-rw-r--r--source/blender/geometry/GEO_uv_parametrizer.h4
-rw-r--r--source/blender/geometry/intern/resample_curves.cc2
-rw-r--r--source/blender/geometry/intern/uv_parametrizer.cc216
-rw-r--r--source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c14
-rw-r--r--source/blender/gpu/CMakeLists.txt56
-rw-r--r--source/blender/gpu/GPU_batch.h6
-rw-r--r--source/blender/gpu/GPU_buffers.h1
-rw-r--r--source/blender/gpu/GPU_shader.h8
-rw-r--r--source/blender/gpu/intern/gpu_buffers.c21
-rw-r--r--source/blender/gpu/intern/gpu_shader.cc6
-rw-r--r--source/blender/gpu/intern/gpu_shader_builder_stubs.cc2
-rw-r--r--source/blender/gpu/intern/gpu_shader_create_info.cc8
-rw-r--r--source/blender/gpu/intern/gpu_shader_create_info.hh6
-rw-r--r--source/blender/gpu/intern/gpu_shader_dependency.cc265
-rw-r--r--source/blender/gpu/intern/gpu_shader_interface.hh21
-rw-r--r--source/blender/gpu/metal/mtl_query.mm2
-rw-r--r--source/blender/gpu/opengl/gl_shader_interface.cc14
-rw-r--r--source/blender/gpu/opengl/gl_storage_buffer.cc2
-rw-r--r--source/blender/gpu/opengl/gl_uniform_buffer.cc11
-rw-r--r--source/blender/gpu/shaders/common/gpu_shader_common_color_utils.glsl119
-rw-r--r--source/blender/gpu/shaders/common/gpu_shader_common_curves.glsl75
-rw-r--r--source/blender/gpu/shaders/common/gpu_shader_common_math_utils.glsl27
-rw-r--r--source/blender/gpu/shaders/common/gpu_shader_common_mix_rgb.glsl46
-rw-r--r--source/blender/gpu/shaders/compositor/compositor_alpha_crop.glsl11
-rw-r--r--source/blender/gpu/shaders/compositor/compositor_box_mask.glsl27
-rw-r--r--source/blender/gpu/shaders/compositor/compositor_convert.glsl8
-rw-r--r--source/blender/gpu/shaders/compositor/compositor_ellipse_mask.glsl27
-rw-r--r--source/blender/gpu/shaders/compositor/compositor_flip.glsl15
-rw-r--r--source/blender/gpu/shaders/compositor/compositor_image_crop.glsl7
-rw-r--r--source/blender/gpu/shaders/compositor/compositor_projector_lens_distortion.glsl16
-rw-r--r--source/blender/gpu/shaders/compositor/compositor_realize_on_domain.glsl25
-rw-r--r--source/blender/gpu/shaders/compositor/compositor_screen_lens_distortion.glsl151
-rw-r--r--source/blender/gpu/shaders/compositor/compositor_set_alpha.glsl8
-rw-r--r--source/blender/gpu/shaders/compositor/compositor_split_viewer.glsl14
-rw-r--r--source/blender/gpu/shaders/compositor/infos/compositor_alpha_crop_info.hh12
-rw-r--r--source/blender/gpu/shaders/compositor/infos/compositor_box_mask_info.hh35
-rw-r--r--source/blender/gpu/shaders/compositor/infos/compositor_convert_info.hh69
-rw-r--r--source/blender/gpu/shaders/compositor/infos/compositor_ellipse_mask_info.hh35
-rw-r--r--source/blender/gpu/shaders/compositor/infos/compositor_flip_info.hh12
-rw-r--r--source/blender/gpu/shaders/compositor/infos/compositor_image_crop_info.hh11
-rw-r--r--source/blender/gpu/shaders/compositor/infos/compositor_projector_lens_distortion_info.hh11
-rw-r--r--source/blender/gpu/shaders/compositor/infos/compositor_realize_on_domain_info.hh24
-rw-r--r--source/blender/gpu/shaders/compositor/infos/compositor_screen_lens_distortion_info.hh20
-rw-r--r--source/blender/gpu/shaders/compositor/infos/compositor_set_alpha_info.hh11
-rw-r--r--source/blender/gpu/shaders/compositor/infos/compositor_split_viewer_info.hh22
-rw-r--r--source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_alpha_over.glsl48
-rw-r--r--source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_bright_contrast.glsl38
-rw-r--r--source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_channel_matte.glsl52
-rw-r--r--source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_chroma_matte.glsl43
-rw-r--r--source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_color_balance.glsl34
-rw-r--r--source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_color_correction.glsl87
-rw-r--r--source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_color_matte.glsl27
-rw-r--r--source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_color_spill.glsl13
-rw-r--r--source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_color_to_luminance.glsl6
-rw-r--r--source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_difference_matte.glsl10
-rw-r--r--source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_distance_matte.glsl26
-rw-r--r--source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_exposure.glsl6
-rw-r--r--source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_gamma.glsl7
-rw-r--r--source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_hue_correct.glsl39
-rw-r--r--source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_hue_saturation_value.glsl16
-rw-r--r--source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_invert.glsl13
-rw-r--r--source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_luminance_matte.glsl14
-rw-r--r--source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_main.glsl7
-rw-r--r--source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_map_value.glsl56
-rw-r--r--source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_normal.glsl9
-rw-r--r--source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_posterize.glsl6
-rw-r--r--source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_separate_combine.glsl132
-rw-r--r--source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_set_alpha.glsl9
-rw-r--r--source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_store_output.glsl26
-rw-r--r--source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_texture_utilities.glsl25
-rw-r--r--source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_type_conversion.glsl29
-rw-r--r--source/blender/imbuf/IMB_colormanagement.h2
-rw-r--r--source/blender/imbuf/IMB_imbuf_types.h6
-rw-r--r--source/blender/imbuf/intern/colormanagement_inline.c5
-rw-r--r--source/blender/io/collada/EffectExporter.cpp6
-rw-r--r--source/blender/io/common/CMakeLists.txt4
-rw-r--r--source/blender/io/stl/CMakeLists.txt8
-rw-r--r--source/blender/io/usd/intern/usd_reader_mesh.cc5
-rw-r--r--source/blender/io/wavefront_obj/IO_wavefront_obj.cc17
-rw-r--r--source/blender/io/wavefront_obj/IO_wavefront_obj.h1
-rw-r--r--source/blender/io/wavefront_obj/exporter/obj_export_file_writer.cc47
-rw-r--r--source/blender/io/wavefront_obj/exporter/obj_export_mesh.cc2
-rw-r--r--source/blender/io/wavefront_obj/exporter/obj_export_mtl.hh26
-rw-r--r--source/blender/io/wavefront_obj/importer/obj_import_file_reader.cc3
-rw-r--r--source/blender/io/wavefront_obj/importer/obj_import_mesh.cc14
-rw-r--r--source/blender/io/wavefront_obj/importer/obj_import_mtl.cc12
-rw-r--r--source/blender/io/wavefront_obj/importer/obj_import_objects.hh25
-rw-r--r--source/blender/io/wavefront_obj/importer/obj_importer.cc4
-rw-r--r--source/blender/io/wavefront_obj/tests/obj_importer_tests.cc99
-rw-r--r--source/blender/makesdna/DNA_meshdata_types.h5
-rw-r--r--source/blender/makesdna/DNA_modifier_types.h2
-rw-r--r--source/blender/makesdna/DNA_node_types.h47
-rw-r--r--source/blender/makesdna/DNA_scene_types.h2
-rw-r--r--source/blender/makesdna/DNA_userdef_types.h2
-rw-r--r--source/blender/makesdna/DNA_view3d_types.h1
-rw-r--r--source/blender/makesrna/intern/rna_ID.c26
-rw-r--r--source/blender/makesrna/intern/rna_access.c2
-rw-r--r--source/blender/makesrna/intern/rna_action.c2
-rw-r--r--source/blender/makesrna/intern/rna_camera.c2
-rw-r--r--source/blender/makesrna/intern/rna_curves.c2
-rw-r--r--source/blender/makesrna/intern/rna_mesh.c171
-rw-r--r--source/blender/makesrna/intern/rna_nodetree.c4
-rw-r--r--source/blender/makesrna/intern/rna_path.cc2
-rw-r--r--source/blender/makesrna/intern/rna_pose.c2
-rw-r--r--source/blender/makesrna/intern/rna_scene.c4
-rw-r--r--source/blender/makesrna/intern/rna_sculpt_paint.c2
-rw-r--r--source/blender/makesrna/intern/rna_space.c8
-rw-r--r--source/blender/makesrna/intern/rna_ui_api.c3
-rw-r--r--source/blender/makesrna/intern/rna_userdef.c4
-rw-r--r--source/blender/makesrna/intern/rna_wm.c32
-rw-r--r--source/blender/modifiers/intern/MOD_armature.c3
-rw-r--r--source/blender/modifiers/intern/MOD_cast.c7
-rw-r--r--source/blender/modifiers/intern/MOD_cloth.c2
-rw-r--r--source/blender/modifiers/intern/MOD_collision.c2
-rw-r--r--source/blender/modifiers/intern/MOD_correctivesmooth.c8
-rw-r--r--source/blender/modifiers/intern/MOD_curve.c2
-rw-r--r--source/blender/modifiers/intern/MOD_decimate.c3
-rw-r--r--source/blender/modifiers/intern/MOD_displace.c8
-rw-r--r--source/blender/modifiers/intern/MOD_explode.c2
-rw-r--r--source/blender/modifiers/intern/MOD_hook.c3
-rw-r--r--source/blender/modifiers/intern/MOD_laplaciandeform.c8
-rw-r--r--source/blender/modifiers/intern/MOD_laplaciansmooth.c6
-rw-r--r--source/blender/modifiers/intern/MOD_lattice.c2
-rw-r--r--source/blender/modifiers/intern/MOD_meshcache.c5
-rw-r--r--source/blender/modifiers/intern/MOD_meshdeform.c8
-rw-r--r--source/blender/modifiers/intern/MOD_particleinstance.c2
-rw-r--r--source/blender/modifiers/intern/MOD_particlesystem.cc3
-rw-r--r--source/blender/modifiers/intern/MOD_screw.c69
-rw-r--r--source/blender/modifiers/intern/MOD_shrinkwrap.c7
-rw-r--r--source/blender/modifiers/intern/MOD_simpledeform.c7
-rw-r--r--source/blender/modifiers/intern/MOD_smooth.c6
-rw-r--r--source/blender/modifiers/intern/MOD_solidify_extrude.c6
-rw-r--r--source/blender/modifiers/intern/MOD_surface.c2
-rw-r--r--source/blender/modifiers/intern/MOD_surfacedeform.c7
-rw-r--r--source/blender/modifiers/intern/MOD_util.c9
-rw-r--r--source/blender/modifiers/intern/MOD_util.h1
-rw-r--r--source/blender/modifiers/intern/MOD_warp.c6
-rw-r--r--source/blender/modifiers/intern/MOD_wave.c19
-rw-r--r--source/blender/modifiers/intern/MOD_weightvgedit.c4
-rw-r--r--source/blender/nodes/NOD_node_declaration.hh37
-rw-r--r--source/blender/nodes/composite/CMakeLists.txt11
-rw-r--r--source/blender/nodes/composite/node_composite_tree.cc4
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_alpha_over.cc66
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_antialiasing.cc20
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_bilateralblur.cc20
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_blur.cc20
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_bokehblur.cc20
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_bokehimage.cc20
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_boxmask.cc102
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_brightness.cc45
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_channel_matte.cc99
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_chroma_matte.cc66
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_color_matte.cc65
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_color_spill.cc115
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_colorbalance.cc72
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_colorcorrection.cc84
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_composite.cc129
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_convert_color_space.cc20
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_cornerpin.cc21
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_crop.cc171
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_cryptomatte.cc53
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_curves.cc246
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_defocus.cc20
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_denoise.cc20
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_despeckle.cc20
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_diff_matte.cc57
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_dilate.cc20
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_directionalblur.cc20
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_displace.cc20
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_distance_matte.cc75
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_double_edge_mask.cc20
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_ellipsemask.cc102
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_exposure.cc31
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_filter.cc20
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_flip.cc64
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_gamma.cc32
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_glare.cc20
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_hue_sat_val.cc49
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_huecorrect.cc65
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_id_mask.cc20
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_image.cc275
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_inpaint.cc20
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_invert.cc55
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_keying.cc22
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_keyingscreen.cc20
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_lensdist.cc207
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_levels.cc21
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_luma_matte.cc58
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_map_range.cc67
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_map_uv.cc20
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_map_value.cc61
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_mask.cc20
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_math.cc67
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_mixrgb.cc125
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_movieclip.cc183
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_moviedistortion.cc20
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_normal.cc39
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_normalize.cc20
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_output_file.cc19
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_pixelate.cc20
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_planetrackdeform.cc21
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_posterize.cc35
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_premulkey.cc39
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_rgb.cc30
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_rotate.cc54
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_scale.cc20
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_scene_time.cc36
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_sepcomb_color.cc130
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_sepcomb_hsva.cc74
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_sepcomb_rgba.cc75
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_sepcomb_xyz.cc63
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_sepcomb_ycca.cc124
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_sepcomb_yuva.cc74
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_setalpha.cc45
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_split_viewer.cc71
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_stabilize2d.cc20
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_sunbeams.cc20
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_switch.cc28
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_switchview.cc22
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_texture.cc21
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_tonemap.cc20
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_trackpos.cc22
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_transform.cc77
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_translate.cc75
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_val_to_rgb.cc148
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_value.cc26
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_vec_blur.cc20
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_viewer.cc129
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_zcombine.cc21
-rw-r--r--source/blender/nodes/geometry/CMakeLists.txt6
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc2
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_mix_rgb.cc27
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_rgb.cc2
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_value.cc2
-rw-r--r--source/blender/python/intern/CMakeLists.txt4
-rw-r--r--source/blender/python/intern/bpy_app_build_options.c4
-rw-r--r--source/blender/python/intern/bpy_interface_atexit.c2
-rw-r--r--source/blender/python/intern/bpy_rna.c6
-rw-r--r--source/blender/render/intern/bake.c4
-rw-r--r--source/blender/render/intern/texture_margin.cc4
-rw-r--r--source/blender/windowmanager/CMakeLists.txt4
-rw-r--r--source/blender/windowmanager/gizmo/intern/wm_gizmo_group.c2
-rw-r--r--source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c10
-rw-r--r--source/blender/windowmanager/intern/wm_event_system.cc15
-rw-r--r--source/blender/windowmanager/intern/wm_files.c5
-rw-r--r--source/blender/windowmanager/intern/wm_init_exit.c2
-rw-r--r--source/blender/windowmanager/intern/wm_playanim.c14
-rw-r--r--source/blender/windowmanager/intern/wm_window.c15
489 files changed, 17988 insertions, 3197 deletions
diff --git a/source/blender/CMakeLists.txt b/source/blender/CMakeLists.txt
index 8ba6e7318bb..28c15d9224c 100644
--- a/source/blender/CMakeLists.txt
+++ b/source/blender/CMakeLists.txt
@@ -151,14 +151,12 @@ add_subdirectory(io)
add_subdirectory(functions)
add_subdirectory(makesdna)
add_subdirectory(makesrna)
+add_subdirectory(compositor)
if(WITH_BLENDER_THUMBNAILER)
add_subdirectory(blendthumb)
endif()
-if(WITH_COMPOSITOR)
- add_subdirectory(compositor)
-endif()
if(WITH_IMAGE_OPENEXR)
add_subdirectory(imbuf/intern/openexr)
diff --git a/source/blender/blendthumb/src/blendthumb_extract.cc b/source/blender/blendthumb/src/blendthumb_extract.cc
index 163197c8b67..fff1242f2ce 100644
--- a/source/blender/blendthumb/src/blendthumb_extract.cc
+++ b/source/blender/blendthumb/src/blendthumb_extract.cc
@@ -136,7 +136,7 @@ static eThumbStatus blendthumb_extract_from_file_impl(FileReader *file,
thumb->height = bytes_to_native_i32(&shape[4], endian_switch);
/* Verify that image dimensions and data size make sense. */
- size_t data_size = block_size - 8;
+ size_t data_size = block_size - sizeof(shape);
const uint64_t expected_size = static_cast<uint64_t>(thumb->width) *
static_cast<uint64_t>(thumb->height) * 4;
if (thumb->width < 0 || thumb->height < 0 || data_size != expected_size) {
diff --git a/source/blender/blenfont/intern/blf_font.c b/source/blender/blenfont/intern/blf_font.c
index 17145bdbe99..b3729b7a673 100644
--- a/source/blender/blenfont/intern/blf_font.c
+++ b/source/blender/blenfont/intern/blf_font.c
@@ -17,7 +17,6 @@
#include <ft2build.h>
#include FT_FREETYPE_H
-#include FT_CACHE_H /* FreeType Cache. */
#include FT_GLYPH_H
#include FT_MULTIPLE_MASTERS_H /* Variable font support. */
#include FT_TRUETYPE_IDS_H /* Codepoint coverage constants. */
@@ -56,11 +55,9 @@ BatchBLF g_batch;
/* freetype2 handle ONLY for this file! */
static FT_Library ft_lib = NULL;
-static FTC_Manager ftc_manager = NULL;
-static FTC_CMapCache ftc_charmap_cache = NULL;
-static SpinLock ft_lib_mutex;
-static SpinLock blf_glyph_cache_mutex;
+/* Lock for FreeType library, used around face creation and deletion. */
+static ThreadMutex ft_lib_mutex;
/* May be set to #UI_widgetbase_draw_cache_flush. */
static void (*blf_draw_cache_flush)(void) = NULL;
@@ -69,50 +66,11 @@ static ft_pix blf_font_height_max_ft_pix(struct FontBLF *font);
static ft_pix blf_font_width_max_ft_pix(struct FontBLF *font);
/* -------------------------------------------------------------------- */
-/** \name FreeType Caching
- * \{ */
-
-/* Called when a face is removed. FreeType will call FT_Done_Face itself. */
-static void blf_face_finalizer(void *object)
-{
- FT_Face face = object;
- FontBLF *font = (FontBLF *)face->generic.data;
- font->face = NULL;
-}
-
-/* Called in response to FTC_Manager_LookupFace. Add a face to our font. */
-static FT_Error blf_cache_face_requester(FTC_FaceID faceID,
- FT_Library lib,
- FT_Pointer UNUSED(reqData),
- FT_Face *face)
-{
- FontBLF *font = (FontBLF *)faceID;
- int err = FT_Err_Cannot_Open_Resource;
-
- BLI_spin_lock(font->ft_lib_mutex);
-
- if (font->filepath) {
- err = FT_New_Face(lib, font->filepath, 0, face);
- }
- else if (font->mem) {
- err = FT_New_Memory_Face(lib, font->mem, (FT_Long)font->mem_size, 0, face);
- }
-
- BLI_spin_unlock(font->ft_lib_mutex);
-
- if (err == FT_Err_Ok) {
- font->face = *face;
- font->face->generic.data = font;
- font->face->generic.finalizer = blf_face_finalizer;
- }
-
- return err;
-}
-/* Use cache, not blf_get_char_index, to return glyph id from charcode. */
+/* Return glyph id from charcode. */
uint blf_get_char_index(struct FontBLF *font, uint charcode)
{
- return FTC_CMapCache_Lookup(ftc_charmap_cache, font, -1, charcode);
+ return blf_ensure_face(font) ? FT_Get_Char_Index(font->face, charcode) : 0;
}
/* -------------------------------------------------------------------- */
@@ -1204,34 +1162,17 @@ char *blf_display_name(FontBLF *font)
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);
+ BLI_mutex_init(&ft_lib_mutex);
int err = FT_Init_FreeType(&ft_lib);
- if (err == FT_Err_Ok) {
- err = FTC_Manager_New(ft_lib,
- BLF_CACHE_MAX_FACES,
- BLF_CACHE_MAX_SIZES,
- BLF_CACHE_BYTES,
- blf_cache_face_requester,
- NULL,
- &ftc_manager);
- if (err == FT_Err_Ok) {
- err = FTC_CMapCache_New(ftc_manager, &ftc_charmap_cache);
- }
- }
return err;
}
void blf_font_exit(void)
{
- BLI_spin_end(&ft_lib_mutex);
- if (ftc_manager) {
- FTC_Manager_Done(ftc_manager);
- }
+ BLI_mutex_end(&ft_lib_mutex);
if (ft_lib) {
FT_Done_FreeType(ft_lib);
}
- BLI_spin_end(&blf_glyph_cache_mutex);
blf_batch_draw_exit();
}
@@ -1290,8 +1231,6 @@ static void blf_font_fill(FontBLF *font)
font->buf_info.col_init[3] = 0;
font->ft_lib = ft_lib;
- font->ft_lib_mutex = &ft_lib_mutex;
- font->glyph_cache_mutex = &blf_glyph_cache_mutex;
}
/**
@@ -1309,7 +1248,14 @@ bool blf_ensure_face(FontBLF *font)
FT_Error err;
- err = FTC_Manager_LookupFace(ftc_manager, font, &font->face);
+ BLI_mutex_lock(&ft_lib_mutex);
+ if (font->filepath) {
+ err = FT_New_Face(ft_lib, font->filepath, 0, &font->face);
+ }
+ if (font->mem) {
+ err = FT_New_Memory_Face(ft_lib, font->mem, (FT_Long)font->mem_size, 0, &font->face);
+ }
+ BLI_mutex_unlock(&ft_lib_mutex);
if (err) {
if (ELEM(err, FT_Err_Unknown_File_Format, FT_Err_Unimplemented_Feature)) {
@@ -1349,6 +1295,7 @@ bool blf_ensure_face(FontBLF *font)
}
}
+ font->ft_size = font->face->size;
font->face_flags = font->face->face_flags;
if (FT_HAS_MULTIPLE_MASTERS(font)) {
@@ -1394,12 +1341,12 @@ static const eFaceDetails static_face_details[] = {
{"lastresort.woff2", UINT32_MAX, UINT32_MAX, UINT32_MAX, UINT32_MAX},
{"Noto Sans CJK Regular.woff2", 0x30000083L, 0x2BDF3C10L, 0x16L, 0},
{"NotoEmoji-VariableFont_wght.woff2", 0x80000003L, 0x241E4ACL, 0x14000000L, 0x4000000L},
- {"NotoSansArabic-VariableFont_wdth,wght.woff2", TT_UCR_ARABIC, 0, 0, 0},
- {"NotoSansArmenian-VariableFont_wdth,wght.woff2",
- TT_UCR_ARMENIAN,
- TT_UCR_ALPHABETIC_PRESENTATION_FORMS,
- 0,
+ {"NotoSansArabic-VariableFont_wdth,wght.woff2",
+ TT_UCR_ARABIC,
+ (uint)TT_UCR_ARABIC_PRESENTATION_FORMS_A,
+ TT_UCR_ARABIC_PRESENTATION_FORMS_B,
0},
+ {"NotoSansArmenian-VariableFont_wdth,wght.woff2", TT_UCR_ARMENIAN, 0, 0, 0},
{"NotoSansBengali-VariableFont_wdth,wght.woff2", TT_UCR_BENGALI, 0, 0, 0},
{"NotoSansDevanagari-Regular.woff2", TT_UCR_DEVANAGARI, 0, 0, 0},
{"NotoSansEthiopic-Regular.woff2", 0, 0, TT_UCR_ETHIOPIC, 0},
@@ -1435,6 +1382,8 @@ static FontBLF *blf_font_new_ex(const char *name,
}
blf_font_fill(font);
+ BLI_mutex_init(&font->glyph_cache_mutex);
+
/* If we have static details about this font we don't need to load the Face. */
const eFaceDetails *static_details = NULL;
char filename[256];
@@ -1501,7 +1450,9 @@ void blf_font_free(FontBLF *font)
}
if (font->face) {
- FTC_Manager_RemoveFaceID(ftc_manager, font);
+ BLI_mutex_lock(&ft_lib_mutex);
+ FT_Done_Face(font->face);
+ BLI_mutex_unlock(&ft_lib_mutex);
font->face = NULL;
}
if (font->filepath) {
@@ -1510,6 +1461,9 @@ void blf_font_free(FontBLF *font)
if (font->name) {
MEM_freeN(font->name);
}
+
+ BLI_mutex_end(&font->glyph_cache_mutex);
+
MEM_freeN(font);
}
@@ -1530,23 +1484,17 @@ bool blf_font_size(FontBLF *font, float size, unsigned int dpi)
/* Adjust our new size to be on even 64ths. */
size = (float)ft_size / 64.0f;
- FTC_ScalerRec scaler = {0};
- scaler.face_id = font;
- scaler.width = 0;
- scaler.height = ft_size;
- scaler.pixel = 0;
- scaler.x_res = dpi;
- scaler.y_res = dpi;
-
- if (FTC_Manager_LookupSize(ftc_manager, &scaler, &font->ft_size) != FT_Err_Ok) {
- printf("The current font don't support the size, %f and dpi, %u\n", size, dpi);
- return false;
+ if (font->size != size || font->dpi != dpi) {
+ if (FT_Set_Char_Size(font->face, 0, ft_size, dpi, dpi) == FT_Err_Ok) {
+ font->size = size;
+ font->dpi = dpi;
+ }
+ else {
+ printf("The current font does not support the size, %f and DPI, %u\n", size, dpi);
+ return false;
+ }
}
- font->size = size;
- font->dpi = dpi;
- font->ft_size->generic.data = (void *)font;
-
return true;
}
diff --git a/source/blender/blenfont/intern/blf_glyph.c b/source/blender/blenfont/intern/blf_glyph.c
index 780b75c6296..f938174f92e 100644
--- a/source/blender/blenfont/intern/blf_glyph.c
+++ b/source/blender/blenfont/intern/blf_glyph.c
@@ -115,7 +115,7 @@ static GlyphCacheBLF *blf_glyph_cache_new(FontBLF *font)
GlyphCacheBLF *blf_glyph_cache_acquire(FontBLF *font)
{
- BLI_spin_lock(font->glyph_cache_mutex);
+ BLI_mutex_lock(&font->glyph_cache_mutex);
GlyphCacheBLF *gc = blf_glyph_cache_find(font, font->size, font->dpi);
@@ -128,7 +128,7 @@ GlyphCacheBLF *blf_glyph_cache_acquire(FontBLF *font)
void blf_glyph_cache_release(FontBLF *font)
{
- BLI_spin_unlock(font->glyph_cache_mutex);
+ BLI_mutex_unlock(&font->glyph_cache_mutex);
}
static void blf_glyph_cache_free(GlyphCacheBLF *gc)
@@ -152,13 +152,13 @@ void blf_glyph_cache_clear(FontBLF *font)
{
GlyphCacheBLF *gc;
- BLI_spin_lock(font->glyph_cache_mutex);
+ BLI_mutex_lock(&font->glyph_cache_mutex);
while ((gc = BLI_pophead(&font->cache))) {
blf_glyph_cache_free(gc);
}
- BLI_spin_unlock(font->glyph_cache_mutex);
+ BLI_mutex_unlock(&font->glyph_cache_mutex);
}
/**
@@ -787,8 +787,8 @@ static bool blf_glyph_transform_weight(FT_GlyphSlot glyph, float factor, bool mo
{
if (glyph->format == FT_GLYPH_FORMAT_OUTLINE) {
/* Fake bold if the font does not have this variable axis. */
- const FontBLF *font = (FontBLF *)glyph->face->generic.data;
- const FT_Pos average_width = font->ft_size->metrics.height;
+ const FT_Pos average_width = FT_MulFix(glyph->face->units_per_EM,
+ glyph->face->size->metrics.x_scale);
FT_Pos change = (FT_Pos)((float)average_width * factor * 0.1f);
FT_Outline_EmboldenXY(&glyph->outline, change, change / 2);
if (monospaced) {
@@ -847,8 +847,7 @@ static bool blf_glyph_transform_width(FT_GlyphSlot glyph, float factor)
static bool blf_glyph_transform_spacing(FT_GlyphSlot glyph, float factor)
{
if (glyph->advance.x > 0) {
- const FontBLF *font = (FontBLF *)glyph->face->generic.data;
- const long int size = font->ft_size->metrics.height;
+ const long int size = glyph->face->size->metrics.height;
glyph->advance.x += (FT_Pos)(factor * (float)size / 6.0f);
return true;
}
diff --git a/source/blender/blenfont/intern/blf_internal.h b/source/blender/blenfont/intern/blf_internal.h
index 8ff00d05e02..221e656f096 100644
--- a/source/blender/blenfont/intern/blf_internal.h
+++ b/source/blender/blenfont/intern/blf_internal.h
@@ -16,14 +16,7 @@ struct rcti;
/* Max number of FontBLFs in memory. Take care that every font has a glyph cache per size/dpi,
* so we don't need load the same font with different size, just load one and call BLF_size. */
-#define BLF_MAX_FONT 64
-
-/* Maximum number of opened FT_Face objects managed by cache. 0 is default of 2. */
-#define BLF_CACHE_MAX_FACES 0
-/* Maximum number of opened FT_Size objects managed by cache. 0 is default of 4 */
-#define BLF_CACHE_MAX_SIZES 0
-/* Maximum number of bytes to use for cached data nodes. 0 is default of 200,000. */
-#define BLF_CACHE_BYTES 0
+#define BLF_MAX_FONT 32
extern struct FontBLF *global_font[BLF_MAX_FONT];
diff --git a/source/blender/blenfont/intern/blf_internal_types.h b/source/blender/blenfont/intern/blf_internal_types.h
index 007b717ab93..dfe24c1aa47 100644
--- a/source/blender/blenfont/intern/blf_internal_types.h
+++ b/source/blender/blenfont/intern/blf_internal_types.h
@@ -323,13 +323,10 @@ typedef struct FontBLF {
/* freetype2 lib handle. */
FT_Library ft_lib;
- /* Mutex lock for library */
- SpinLock *ft_lib_mutex;
-
/* freetype2 face. */
FT_Face face;
- /* FreeType size is separated from face when using their caching subsystem. */
+ /* Point to face->size or to cache's size. */
FT_Size ft_size;
/* Copy of the font->face->face_flags, in case we don't have a face loaded. */
@@ -339,7 +336,7 @@ typedef struct FontBLF {
FontBufInfoBLF buf_info;
/* Mutex lock for glyph cache. */
- SpinLock *glyph_cache_mutex;
+ ThreadMutex glyph_cache_mutex;
} FontBLF;
typedef struct DirBLF {
diff --git a/source/blender/blenkernel/BKE_curves.hh b/source/blender/blenkernel/BKE_curves.hh
index 568899721a9..fc8a00af4a1 100644
--- a/source/blender/blenkernel/BKE_curves.hh
+++ b/source/blender/blenkernel/BKE_curves.hh
@@ -97,7 +97,7 @@ class CurvesGeometryRuntime {
mutable Span<float3> evaluated_positions_span;
/**
- * Cache of lengths along each evaluated curve for for each evaluated point. If a curve is
+ * Cache of lengths along each evaluated curve for each evaluated point. If a curve is
* cyclic, it needs one more length value to correspond to the last segment, so in order to
* make slicing this array for a curve fast, an extra float is stored for every curve.
*/
diff --git a/source/blender/blenkernel/BKE_customdata.h b/source/blender/blenkernel/BKE_customdata.h
index be8823c5ad2..d36e6031557 100644
--- a/source/blender/blenkernel/BKE_customdata.h
+++ b/source/blender/blenkernel/BKE_customdata.h
@@ -11,7 +11,9 @@
#include "BLI_sys_types.h"
#include "BLI_utildefines.h"
#ifdef __cplusplus
+# include "BLI_set.hh"
# include "BLI_span.hh"
+# include "BLI_string_ref.hh"
# include "BLI_vector.hh"
#endif
@@ -141,6 +143,15 @@ void CustomData_copy(const struct CustomData *source,
eCDAllocType alloctype,
int totelem);
+/**
+ * Like #CustomData_copy but skips copying layers that are stored as flags on #BMesh.
+ */
+void CustomData_copy_mesh_to_bmesh(const struct CustomData *source,
+ struct CustomData *dest,
+ eCustomDataMask mask,
+ eCDAllocType alloctype,
+ int totelem);
+
/* BMESH_TODO, not really a public function but readfile.c needs it */
void CustomData_update_typemap(struct CustomData *data);
@@ -155,6 +166,15 @@ bool CustomData_merge(const struct CustomData *source,
int totelem);
/**
+ * Like #CustomData_copy but skips copying layers that are stored as flags on #BMesh.
+ */
+bool CustomData_merge_mesh_to_bmesh(const struct CustomData *source,
+ struct CustomData *dest,
+ eCustomDataMask mask,
+ eCDAllocType alloctype,
+ int totelem);
+
+/**
* Reallocate custom data to a new element count.
* Only affects on data layers which are owned by the CustomData itself,
* referenced data is kept unchanged,
@@ -696,7 +716,8 @@ void CustomData_data_transfer(const struct MeshPairRemap *me_remap,
* the struct.
*/
void CustomData_blend_write_prepare(CustomData &data,
- blender::Vector<CustomDataLayer, 16> &layers_to_write);
+ blender::Vector<CustomDataLayer, 16> &layers_to_write,
+ const blender::Set<blender::StringRef> &skip_names = {});
/**
* \param layers_to_write: Layers created by #CustomData_blend_write_prepare.
diff --git a/source/blender/blenkernel/BKE_geometry_set.hh b/source/blender/blenkernel/BKE_geometry_set.hh
index be2ec3e3dca..a5c4d5e1365 100644
--- a/source/blender/blenkernel/BKE_geometry_set.hh
+++ b/source/blender/blenkernel/BKE_geometry_set.hh
@@ -505,7 +505,7 @@ class CurveComponentLegacy : public GeometryComponent {
/**
* A geometry component that stores a group of curves, corresponding the #Curves data-block type
- * and the #CurvesGeometry type. Attributes are are stored on the control point domain and the
+ * and the #CurvesGeometry type. Attributes are stored on the control point domain and the
* curve domain.
*/
class CurveComponent : public GeometryComponent {
diff --git a/source/blender/blenkernel/BKE_idtype.h b/source/blender/blenkernel/BKE_idtype.h
index 30f7ed45859..95f4c8f6dce 100644
--- a/source/blender/blenkernel/BKE_idtype.h
+++ b/source/blender/blenkernel/BKE_idtype.h
@@ -85,7 +85,11 @@ typedef void (*IDTypeForeachCacheFunction)(struct ID *id,
typedef void (*IDTypeForeachPathFunction)(struct ID *id, struct BPathForeachPathData *bpath_data);
-typedef struct ID *(*IDTypeEmbeddedOwnerGetFunction)(struct Main *bmain, struct ID *id);
+/** \param owner_id_hint: If non-NULL, a potential owner of the given embedded ID. Can speed up
+ * look-up of the owner ID in some cases. */
+typedef struct ID *(*IDTypeEmbeddedOwnerGetFunction)(struct Main *bmain,
+ struct ID *id,
+ struct ID *owner_id_hint);
typedef void (*IDTypeBlendWriteFunction)(struct BlendWriter *writer,
struct ID *id,
@@ -109,7 +113,7 @@ typedef struct IDTypeInfo {
*/
short id_code;
/**
- * Bitflag matching id_code, used for filtering (e.g. in file browser), see DNA_ID.h's
+ * Bit-flag matching id_code, used for filtering (e.g. in file browser), see DNA_ID.h's
* FILTER_ID_XX enums.
*/
uint64_t id_filter;
diff --git a/source/blender/blenkernel/BKE_key.h b/source/blender/blenkernel/BKE_key.h
index 37b30d63722..9f506ded8e9 100644
--- a/source/blender/blenkernel/BKE_key.h
+++ b/source/blender/blenkernel/BKE_key.h
@@ -47,7 +47,7 @@ void key_curve_normal_weights(float t, float data[4], int type);
/**
* Returns key coordinates (+ tilt) when key applied, NULL otherwise.
*
- * \param obdata if given, also update that geometry with the result of the shape keys evaluation.
+ * \param obdata: if given, also update that geometry with the result of the shape keys evaluation.
*/
float *BKE_key_evaluate_object_ex(
struct Object *ob, int *r_totelem, float *arr, size_t arr_size, struct ID *obdata);
diff --git a/source/blender/blenkernel/BKE_lib_id.h b/source/blender/blenkernel/BKE_lib_id.h
index 148e6ec2791..febdad2ca0d 100644
--- a/source/blender/blenkernel/BKE_lib_id.h
+++ b/source/blender/blenkernel/BKE_lib_id.h
@@ -444,18 +444,20 @@ struct ID *BKE_id_copy(struct Main *bmain, const struct ID *id);
* Currently, it only handles the given ID, and their shape keys and actions if any, according to
* the given `duplicate_flags`.
*
- * \param duplicate_flags is of type #eDupli_ID_Flags, see #UserDef.dupflag. Currently only
+ * \param duplicate_flags: is of type #eDupli_ID_Flags, see #UserDef.dupflag. Currently only
* `USER_DUP_LINKED_ID` and `USER_DUP_ACT` have an effect here.
- * \param copy_flags flags passed to #BKE_id_copy_ex.
+ * \param copy_flags: flags passed to #BKE_id_copy_ex.
*/
struct ID *BKE_id_copy_for_duplicate(struct Main *bmain,
struct ID *id,
uint duplicate_flags,
int copy_flags);
-/* Special version of BKE_id_copy which is safe from using evaluated id as source with a copy
+/**
+ * Special version of #BKE_id_copy which is safe from using evaluated id as source with a copy
* result appearing in the main database.
- * Takes care of the referenced data-blocks consistency. */
+ * Takes care of the referenced data-blocks consistency.
+ */
struct ID *BKE_id_copy_for_use_in_bmain(struct Main *bmain, const struct ID *id);
/**
diff --git a/source/blender/blenkernel/BKE_lib_override.h b/source/blender/blenkernel/BKE_lib_override.h
index f933946164c..8542c02fab5 100644
--- a/source/blender/blenkernel/BKE_lib_override.h
+++ b/source/blender/blenkernel/BKE_lib_override.h
@@ -66,10 +66,12 @@ void BKE_lib_override_library_free(struct IDOverrideLibrary **override, bool do_
* \note This is especially useful when `id` is a non-real override (e.g. embedded ID like a master
* collection or root node tree, or a shape key).
*
- * \param r_owner_id If given, will be set with the actual ID owning the return liboverride data.
+ * \param owner_id_hint: If not NULL, a potential owner for the given override-embedded `id`.
+ * \param r_owner_id: If given, will be set with the actual ID owning the return liboverride data.
*/
IDOverrideLibrary *BKE_lib_override_library_get(struct Main *bmain,
struct ID *id,
+ struct ID *owner_id_hint,
struct ID **r_owner_id);
/**
diff --git a/source/blender/blenkernel/BKE_mesh.h b/source/blender/blenkernel/BKE_mesh.h
index 17f541b362e..b9f6b4b73f3 100644
--- a/source/blender/blenkernel/BKE_mesh.h
+++ b/source/blender/blenkernel/BKE_mesh.h
@@ -853,7 +853,7 @@ struct Mesh *BKE_mesh_merge_verts(struct Mesh *mesh,
int merge_mode);
/**
- * Account for custom-data such as UV's becoming detached because of of imprecision
+ * Account for custom-data such as UV's becoming detached because of imprecision
* in custom-data interpolation.
* Without running this operation subdivision surface can cause UV's to be disconnected,
* see: T81065.
@@ -865,19 +865,7 @@ void BKE_mesh_merge_customdata_for_apply_modifier(struct Mesh *me);
/**
* Update the hide flag for edges and faces from the corresponding flag in verts.
*/
-void BKE_mesh_flush_hidden_from_verts_ex(const struct MVert *mvert,
- const struct MLoop *mloop,
- struct MEdge *medge,
- int totedge,
- struct MPoly *mpoly,
- int totpoly);
void BKE_mesh_flush_hidden_from_verts(struct Mesh *me);
-void BKE_mesh_flush_hidden_from_polys_ex(struct MVert *mvert,
- const struct MLoop *mloop,
- struct MEdge *medge,
- int totedge,
- const struct MPoly *mpoly,
- int totpoly);
void BKE_mesh_flush_hidden_from_polys(struct Mesh *me);
/**
* simple poly -> vert/edge selection.
diff --git a/source/blender/blenkernel/BKE_mesh_legacy_convert.h b/source/blender/blenkernel/BKE_mesh_legacy_convert.h
index 208f0877e11..7dcfc8379ef 100644
--- a/source/blender/blenkernel/BKE_mesh_legacy_convert.h
+++ b/source/blender/blenkernel/BKE_mesh_legacy_convert.h
@@ -18,6 +18,7 @@ struct Mesh;
struct MFace;
/**
+<<<<<<< HEAD
* Copy bevel weights from separate layers into vertices and edges.
*/
void BKE_mesh_legacy_bevel_weight_from_layers(struct Mesh *mesh);
@@ -25,6 +26,16 @@ void BKE_mesh_legacy_bevel_weight_from_layers(struct Mesh *mesh);
* Copy bevel weights from vertices and edges to separate layers.
*/
void BKE_mesh_legacy_bevel_weight_to_layers(struct Mesh *mesh);
+=======
+ * Convert the hidden element attributes to the old flag format for writing.
+ */
+void BKE_mesh_legacy_convert_hide_layers_to_flags(struct Mesh *mesh);
+/**
+ * Convert the old hide flags (#ME_HIDE) to the hidden element attribute for reading.
+ * Only add the attributes when there are any elements in each domain hidden.
+ */
+void BKE_mesh_legacy_convert_flags_to_hide_layers(struct Mesh *mesh);
+>>>>>>> master
/**
* Recreate #MFace Tessellation.
diff --git a/source/blender/blenkernel/BKE_mesh_mapping.h b/source/blender/blenkernel/BKE_mesh_mapping.h
index 44588a06119..abe590b6806 100644
--- a/source/blender/blenkernel/BKE_mesh_mapping.h
+++ b/source/blender/blenkernel/BKE_mesh_mapping.h
@@ -20,7 +20,7 @@ struct MVert;
/* UvVertMap */
#define STD_UV_CONNECT_LIMIT 0.0001f
-/* Map from uv vertex to face. Used by select linked, uv subsurf and obj exporter. */
+/* Map from uv vertex to face. Used by select linked, uv subdivision-surface and obj exporter. */
typedef struct UvVertMap {
struct UvMapVert **vert;
struct UvMapVert *buf;
@@ -68,13 +68,20 @@ typedef struct UvElementMap {
/** Total number of unique UVs. */
int total_unique_uvs;
- /* If Non-NULL, address UvElements by `BM_elem_index_get(BMVert*)`. */
+ /** If Non-NULL, address UvElements by `BM_elem_index_get(BMVert*)`. */
struct UvElement **vertex;
- /* Number of Islands in the mesh */
- int totalIslands;
- /* Stores the starting index in buf where each island begins */
- int *islandIndices;
+ /** If Non-NULL, pointer to local head of each unique UV. */
+ struct UvElement **head_table;
+
+ /** Number of islands, or zero if not calculated. */
+ int total_islands;
+ /** Array of starting index in #storage where each island begins. */
+ int *island_indices;
+ /** Array of number of UVs in each island. */
+ int *island_total_uvs;
+ /** Array of number of unique UVs in each island. */
+ int *island_total_unique_uvs;
} UvElementMap;
/* Connectivity data */
@@ -85,6 +92,7 @@ typedef struct MeshElemMap {
/* mapping */
UvVertMap *BKE_mesh_uv_vert_map_create(const struct MPoly *mpoly,
+ const bool *hide_poly,
const struct MLoop *mloop,
const struct MLoopUV *mloopuv,
unsigned int totpoly,
diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h
index 02e3cefe6a5..b42b9df510d 100644
--- a/source/blender/blenkernel/BKE_node.h
+++ b/source/blender/blenkernel/BKE_node.h
@@ -101,6 +101,7 @@ typedef struct bNodeSocketTemplate {
namespace blender {
class CPPType;
namespace nodes {
+class DNode;
class NodeMultiFunctionBuilder;
class GeoNodeExecParams;
class NodeDeclarationBuilder;
@@ -109,6 +110,11 @@ class GatherLinkSearchOpParams;
namespace fn {
class MFDataType;
} // namespace fn
+namespace realtime_compositor {
+class Context;
+class NodeOperation;
+class ShaderNode;
+} // namespace realtime_compositor
} // namespace blender
using CPPTypeHandle = blender::CPPType;
@@ -123,7 +129,14 @@ using SocketGetGeometryNodesCPPValueFunction = void (*)(const struct bNodeSocket
using NodeGatherSocketLinkOperationsFunction =
void (*)(blender::nodes::GatherLinkSearchOpParams &params);
+using NodeGetCompositorOperationFunction = blender::realtime_compositor::NodeOperation
+ *(*)(blender::realtime_compositor::Context &context, blender::nodes::DNode node);
+using NodeGetCompositorShaderNodeFunction =
+ blender::realtime_compositor::ShaderNode *(*)(blender::nodes::DNode node);
+
#else
+typedef void *NodeGetCompositorOperationFunction;
+typedef void *NodeGetCompositorShaderNodeFunction;
typedef void *NodeMultiFunctionBuildFunction;
typedef void *NodeGeometryExecFunction;
typedef void *NodeDeclareFunction;
@@ -309,6 +322,14 @@ typedef struct bNodeType {
/* gpu */
NodeGPUExecFunction gpu_fn;
+ /* Get an instance of this node's compositor operation. Freeing the instance is the
+ * responsibility of the caller. */
+ NodeGetCompositorOperationFunction get_compositor_operation;
+
+ /* Get an instance of this node's compositor shader node. Freeing the instance is the
+ * responsibility of the caller. */
+ NodeGetCompositorShaderNodeFunction get_compositor_shader_node;
+
/* Build a multi-function for this node. */
NodeMultiFunctionBuildFunction build_multi_function;
diff --git a/source/blender/blenkernel/BKE_object.h b/source/blender/blenkernel/BKE_object.h
index f0eb16a819d..8f3b488c7db 100644
--- a/source/blender/blenkernel/BKE_object.h
+++ b/source/blender/blenkernel/BKE_object.h
@@ -586,7 +586,6 @@ void BKE_object_runtime_reset_on_copy(struct Object *object, int flag);
void BKE_object_runtime_free_data(struct Object *object);
void BKE_object_batch_cache_dirty_tag(struct Object *ob);
-void BKE_object_data_batch_cache_dirty_tag(struct ID *object_data);
/* this function returns a superset of the scenes selection based on relationships */
diff --git a/source/blender/blenkernel/BKE_paint.h b/source/blender/blenkernel/BKE_paint.h
index 8b9deadc960..fa67ff08383 100644
--- a/source/blender/blenkernel/BKE_paint.h
+++ b/source/blender/blenkernel/BKE_paint.h
@@ -213,7 +213,7 @@ bool BKE_paint_always_hide_test(struct Object *ob);
* Returns non-zero if any of the face's vertices are hidden, zero otherwise.
*/
bool paint_is_face_hidden(const struct MLoopTri *lt,
- const struct MVert *mvert,
+ const bool *hide_vert,
const struct MLoop *mloop);
/**
* Returns non-zero if any of the corners of the grid
diff --git a/source/blender/blenkernel/BKE_pbvh.h b/source/blender/blenkernel/BKE_pbvh.h
index af7effe806c..2be3f323d07 100644
--- a/source/blender/blenkernel/BKE_pbvh.h
+++ b/source/blender/blenkernel/BKE_pbvh.h
@@ -533,6 +533,7 @@ typedef struct PBVHVertexIter {
/* mesh */
struct MVert *mverts;
float (*vert_normals)[3];
+ const bool *hide_vert;
int totvert;
const int *vert_indices;
float *vmask;
@@ -593,7 +594,7 @@ void pbvh_vertex_iter_init(PBVH *pbvh, PBVHNode *node, PBVHVertexIter *vi, int m
else if (vi.mverts) { \
vi.mvert = &vi.mverts[vi.vert_indices[vi.gx]]; \
if (vi.respect_hide) { \
- vi.visible = !(vi.mvert->flag & ME_HIDE); \
+ vi.visible = !(vi.hide_vert && vi.hide_vert[vi.vert_indices[vi.gx]]); \
if (mode == PBVH_ITER_UNIQUE && !vi.visible) { \
continue; \
} \
@@ -667,6 +668,8 @@ void BKE_pbvh_parallel_range_settings(struct TaskParallelSettings *settings,
struct MVert *BKE_pbvh_get_verts(const PBVH *pbvh);
const float (*BKE_pbvh_get_vert_normals(const PBVH *pbvh))[3];
+const bool *BKE_pbvh_get_vert_hide(const PBVH *pbvh);
+bool *BKE_pbvh_get_vert_hide_for_write(PBVH *pbvh);
PBVHColorBufferNode *BKE_pbvh_node_color_buffer_get(PBVHNode *node);
void BKE_pbvh_node_color_buffer_free(PBVH *pbvh);
diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt
index f76f7f5a968..e3f00d03a3b 100644
--- a/source/blender/blenkernel/CMakeLists.txt
+++ b/source/blender/blenkernel/CMakeLists.txt
@@ -191,7 +191,7 @@ set(SRC
intern/mask_evaluate.c
intern/mask_rasterize.c
intern/material.c
- intern/mball.c
+ intern/mball.cc
intern/mball_tessellate.c
intern/mesh.cc
intern/mesh_boolean_convert.cc
diff --git a/source/blender/blenkernel/intern/DerivedMesh.cc b/source/blender/blenkernel/intern/DerivedMesh.cc
index a29d8726f21..2ce5863c176 100644
--- a/source/blender/blenkernel/intern/DerivedMesh.cc
+++ b/source/blender/blenkernel/intern/DerivedMesh.cc
@@ -1595,7 +1595,7 @@ static void editbmesh_calc_modifiers(struct Depsgraph *depsgraph,
/* Add orco coordinates to final and deformed mesh if requested. */
if (final_datamask.vmask & CD_MASK_ORCO) {
- /* FIXME(Campbell): avoid the need to convert to mesh data just to add an orco layer. */
+ /* FIXME(@campbellbarton): avoid the need to convert to mesh data just to add an orco layer. */
BKE_mesh_wrapper_ensure_mdata(mesh_final);
add_orco_mesh(ob, em_input, mesh_final, mesh_orco, CD_ORCO);
diff --git a/source/blender/blenkernel/intern/attribute_access.cc b/source/blender/blenkernel/intern/attribute_access.cc
index 1af3cde1821..b9995796a21 100644
--- a/source/blender/blenkernel/intern/attribute_access.cc
+++ b/source/blender/blenkernel/intern/attribute_access.cc
@@ -56,7 +56,7 @@ const char *no_procedural_access_message =
bool allow_procedural_attribute_access(StringRef attribute_name)
{
- return !attribute_name.startswith(".selection");
+ return !attribute_name.startswith(".selection") && !attribute_name.startswith(".hide");
}
static int attribute_data_type_complexity(const eCustomDataType data_type)
diff --git a/source/blender/blenkernel/intern/brush.cc b/source/blender/blenkernel/intern/brush.cc
index 99733c8edb3..fc45ce0bbe7 100644
--- a/source/blender/blenkernel/intern/brush.cc
+++ b/source/blender/blenkernel/intern/brush.cc
@@ -597,7 +597,7 @@ using eGPCurveMappingPreset = enum eGPCurveMappingPreset {
GPCURVE_PRESET_CHISEL_STRENGTH = 5,
};
-static void brush_gpencil_curvemap_reset(CurveMap *cuma, int tot, int preset)
+static void brush_gpencil_curvemap_reset(CurveMap *cuma, int tot, eGPCurveMappingPreset preset)
{
if (cuma->curve) {
MEM_freeN(cuma->curve);
@@ -2512,8 +2512,10 @@ struct ImBuf *BKE_brush_gen_radial_control_imbuf(Brush *br, bool secondary, bool
if (display_gradient || have_texture) {
for (int i = 0; i < side; i++) {
for (int j = 0; j < side; j++) {
- float magn = sqrtf(pow2f(i - half) + pow2f(j - half));
- im->rect_float[i * side + j] *= BKE_brush_curve_strength_clamped(br, magn, half);
+ const float magn = sqrtf(pow2f(i - half) + pow2f(j - half));
+ const float strength = BKE_brush_curve_strength_clamped(br, magn, half);
+ im->rect_float[i * side + j] = (have_texture) ? im->rect_float[i * side + j] * strength :
+ strength;
}
}
}
diff --git a/source/blender/blenkernel/intern/bvhutils.cc b/source/blender/blenkernel/intern/bvhutils.cc
index 03dd5c89b70..d0b57b45d35 100644
--- a/source/blender/blenkernel/intern/bvhutils.cc
+++ b/source/blender/blenkernel/intern/bvhutils.cc
@@ -27,6 +27,8 @@
#include "MEM_guardedalloc.h"
+using blender::VArray;
+
/* -------------------------------------------------------------------- */
/** \name BVHCache
* \{ */
@@ -1181,9 +1183,13 @@ static BLI_bitmap *loose_edges_map_get(const MEdge *medge,
}
static BLI_bitmap *looptri_no_hidden_map_get(const MPoly *mpoly,
+ const VArray<bool> &hide_poly,
const int looptri_len,
int *r_looptri_active_len)
{
+ if (hide_poly.is_single() && !hide_poly.get_internal_single()) {
+ return nullptr;
+ }
BLI_bitmap *looptri_mask = BLI_BITMAP_NEW(looptri_len, __func__);
int looptri_no_hidden_len = 0;
@@ -1191,8 +1197,7 @@ static BLI_bitmap *looptri_no_hidden_map_get(const MPoly *mpoly,
int i_poly = 0;
while (looptri_iter != looptri_len) {
int mp_totlooptri = mpoly[i_poly].totloop - 2;
- const MPoly &mp = mpoly[i_poly];
- if (mp.flag & ME_HIDE) {
+ if (hide_poly[i_poly]) {
looptri_iter += mp_totlooptri;
}
else {
@@ -1276,9 +1281,15 @@ BVHTree *BKE_bvhtree_from_mesh_get(struct BVHTreeFromMesh *data,
0.0f, tree_type, 6, mesh->mvert, mesh->mface, mesh->totface, nullptr, -1);
break;
- case BVHTREE_FROM_LOOPTRI_NO_HIDDEN:
- mask = looptri_no_hidden_map_get(mesh->mpoly, looptri_len, &mask_bits_act_len);
+ case BVHTREE_FROM_LOOPTRI_NO_HIDDEN: {
+ blender::bke::AttributeAccessor attributes = blender::bke::mesh_attributes(*mesh);
+ mask = looptri_no_hidden_map_get(
+ mesh->mpoly,
+ attributes.lookup_or_default(".hide_poly", ATTR_DOMAIN_FACE, false),
+ looptri_len,
+ &mask_bits_act_len);
ATTR_FALLTHROUGH;
+ }
case BVHTREE_FROM_LOOPTRI:
data->tree = bvhtree_from_mesh_looptri_create_tree(0.0f,
tree_type,
diff --git a/source/blender/blenkernel/intern/collection.c b/source/blender/blenkernel/intern/collection.c
index 934c3053a02..4967e3482c6 100644
--- a/source/blender/blenkernel/intern/collection.c
+++ b/source/blender/blenkernel/intern/collection.c
@@ -162,7 +162,7 @@ static void collection_foreach_id(ID *id, LibraryForeachIDData *data)
}
}
-static ID *collection_owner_get(Main *bmain, ID *id)
+static ID *collection_owner_get(Main *bmain, ID *id, ID *owner_id_hint)
{
if ((id->flag & LIB_EMBEDDED_DATA) == 0) {
return id;
@@ -172,6 +172,11 @@ static ID *collection_owner_get(Main *bmain, ID *id)
Collection *master_collection = (Collection *)id;
BLI_assert((master_collection->flag & COLLECTION_IS_MASTER) != 0);
+ if (owner_id_hint != NULL && GS(owner_id_hint->name) == ID_SCE &&
+ ((Scene *)owner_id_hint)->master_collection == master_collection) {
+ return owner_id_hint;
+ }
+
LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
if (scene->master_collection == master_collection) {
return &scene->id;
diff --git a/source/blender/blenkernel/intern/colorband.c b/source/blender/blenkernel/intern/colorband.c
index b2f817b7821..5145f1cbbc0 100644
--- a/source/blender/blenkernel/intern/colorband.c
+++ b/source/blender/blenkernel/intern/colorband.c
@@ -144,7 +144,7 @@ static float color_sample_remove_cost(const struct ColorResampleElem *c)
return area;
}
-/* TODO(campbell): create BLI_math_filter? */
+/* TODO(@campbellbarton): create `BLI_math_filter` ? */
static float filter_gauss(float x)
{
const float gaussfac = 1.6f;
diff --git a/source/blender/blenkernel/intern/constraint.c b/source/blender/blenkernel/intern/constraint.c
index 9f1d2d7bbb2..071fa5fe6bf 100644
--- a/source/blender/blenkernel/intern/constraint.c
+++ b/source/blender/blenkernel/intern/constraint.c
@@ -1559,7 +1559,7 @@ static void followpath_evaluate(bConstraint *con, bConstraintOb *cob, ListBase *
/* un-apply scaling caused by path */
if ((data->followflag & FOLLOWPATH_RADIUS) == 0) {
- /* XXX(campbell): Assume that scale correction means that radius
+ /* XXX(@campbellbarton): Assume that scale correction means that radius
* will have some scale error in it. */
float obsize[3];
diff --git a/source/blender/blenkernel/intern/cryptomatte_test.cc b/source/blender/blenkernel/intern/cryptomatte_test.cc
index 2f15242b4a4..bb09b276645 100644
--- a/source/blender/blenkernel/intern/cryptomatte_test.cc
+++ b/source/blender/blenkernel/intern/cryptomatte_test.cc
@@ -163,7 +163,7 @@ TEST(cryptomatte, session_from_stamp_data)
* best as possible. */
TEST(cryptomatte, parsing_malformed_manifests)
{
- /* Manifest from multilayer.exr in the cryptomatte git-repository. */
+ /* Manifest from `multilayer.exr` in the cryptomatte git-repository. */
test_cryptomatte_manifest(
R"({"/obj/instance1:instances:0":"0d54c6cc","/obj/instance1:instances:1":"293d9340","/obj/instance1:instances:110":"ccb9e1f2","/obj/instance1:instances:111":"f8dd3a48","/obj/instance1:instances:112":"a99e07a8","/obj/instance1:instances:113":"e75599a4","/obj/instance1:instances:114":"794200f3","/obj/instance1:instances:115":"2a3a1728","/obj/instance1:instances:116":"478544a1","/obj/instance1:instances:117":"b2bd969a","/obj/instance1:instances:10":"3a0c8681","/obj/instance1:instances:11":"01e5970d","/obj/box:polygons:1":"9d416418","/obj/instance1:instances:100":"2dcd2966","/obj/instance1:instances:101":"9331cd82","/obj/instance1:instances:102":"df50fccb","/obj/instance1:instances:103":"97f8590d","/obj/instance1:instances:104":"bbcd220d","/obj/instance1:instances:105":"4ae06139","/obj/instance1:instances:106":"8873d5ea","/obj/instance1:instances:107":"39d8af8d","/obj/instance1:instances:108":"bb11bd4e","/obj/instance1:instances:109":"a32bba35"})",
R"({"\/obj\/box:polygons:1":"9d416418","\/obj\/instance1:instances:0":"0d54c6cc","\/obj\/instance1:instances:1":"293d9340","\/obj\/instance1:instances:10":"3a0c8681","\/obj\/instance1:instances:100":"2dcd2966","\/obj\/instance1:instances:101":"9331cd82","\/obj\/instance1:instances:102":"df50fccb","\/obj\/instance1:instances:103":"97f8590d","\/obj\/instance1:instances:104":"bbcd220d","\/obj\/instance1:instances:105":"4ae06139","\/obj\/instance1:instances:106":"8873d5ea","\/obj\/instance1:instances:107":"39d8af8d","\/obj\/instance1:instances:108":"bb11bd4e","\/obj\/instance1:instances:109":"a32bba35","\/obj\/instance1:instances:11":"01e5970d","\/obj\/instance1:instances:110":"ccb9e1f2","\/obj\/instance1:instances:111":"f8dd3a48","\/obj\/instance1:instances:112":"a99e07a8","\/obj\/instance1:instances:113":"e75599a4","\/obj\/instance1:instances:114":"794200f3","\/obj\/instance1:instances:115":"2a3a1728","\/obj\/instance1:instances:116":"478544a1","\/obj\/instance1:instances:117":"b2bd969a","\/obj\/instance1:instance)");
diff --git a/source/blender/blenkernel/intern/curve.cc b/source/blender/blenkernel/intern/curve.cc
index 5125e010b81..40b64aa8dc8 100644
--- a/source/blender/blenkernel/intern/curve.cc
+++ b/source/blender/blenkernel/intern/curve.cc
@@ -2470,7 +2470,7 @@ static void make_bevel_list_segment_2D(BevList *bl)
static void make_bevel_list_2D(BevList *bl)
{
- /* NOTE(campbell): `bevp->dir` and `bevp->quat` are not needed for beveling but are
+ /* NOTE(@campbellbarton): `bevp->dir` and `bevp->quat` are not needed for beveling but are
* used when making a path from a 2D curve, therefore they need to be set. */
BevPoint *bevp0, *bevp1, *bevp2;
diff --git a/source/blender/blenkernel/intern/curves_geometry.cc b/source/blender/blenkernel/intern/curves_geometry.cc
index c37634b5d3d..6fdcb56fc91 100644
--- a/source/blender/blenkernel/intern/curves_geometry.cc
+++ b/source/blender/blenkernel/intern/curves_geometry.cc
@@ -1190,7 +1190,7 @@ static CurvesGeometry copy_with_removed_points(const CurvesGeometry &curves,
},
[&]() {
/* Copy over curve attributes.
- * In some cases points are just dissolved, so the the number of
+ * In some cases points are just dissolved, so the number of
* curves will be the same. That could be optimized in the future. */
for (bke::AttributeTransferData &attribute : curve_attributes) {
if (new_curves.curves_num() == curves.curves_num()) {
diff --git a/source/blender/blenkernel/intern/customdata.cc b/source/blender/blenkernel/intern/customdata.cc
index bc3c6a8269d..6c6160ac0f1 100644
--- a/source/blender/blenkernel/intern/customdata.cc
+++ b/source/blender/blenkernel/intern/customdata.cc
@@ -26,6 +26,7 @@
#include "BLI_math_vector.hh"
#include "BLI_mempool.h"
#include "BLI_path_util.h"
+#include "BLI_set.hh"
#include "BLI_span.hh"
#include "BLI_string.h"
#include "BLI_string_ref.hh"
@@ -58,6 +59,7 @@
#include "data_transfer_intern.h"
using blender::IndexRange;
+using blender::Set;
using blender::Span;
using blender::StringRef;
using blender::Vector;
@@ -2342,6 +2344,43 @@ bool CustomData_merge(const CustomData *source,
return changed;
}
+static bool attribute_stored_in_bmesh_flag(const StringRef name)
+{
+ return ELEM(name, ".hide_vert", ".hide_edge", ".hide_poly");
+}
+
+static CustomData shallow_copy_remove_non_bmesh_attributes(const CustomData &src)
+{
+ Vector<CustomDataLayer> dst_layers;
+ for (const CustomDataLayer &layer : Span<CustomDataLayer>{src.layers, src.totlayer}) {
+ if (!attribute_stored_in_bmesh_flag(layer.name)) {
+ dst_layers.append(layer);
+ }
+ }
+
+ CustomData dst = src;
+ dst.layers = static_cast<CustomDataLayer *>(
+ MEM_calloc_arrayN(dst_layers.size(), sizeof(CustomDataLayer), __func__));
+ dst.totlayer = dst_layers.size();
+ memcpy(dst.layers, dst_layers.data(), dst_layers.as_span().size_in_bytes());
+
+ CustomData_update_typemap(&dst);
+
+ return dst;
+}
+
+bool CustomData_merge_mesh_to_bmesh(const CustomData *source,
+ CustomData *dest,
+ const eCustomDataMask mask,
+ const eCDAllocType alloctype,
+ const int totelem)
+{
+ CustomData source_copy = shallow_copy_remove_non_bmesh_attributes(*source);
+ const bool result = CustomData_merge(&source_copy, dest, mask, alloctype, totelem);
+ MEM_SAFE_FREE(source_copy.layers);
+ return result;
+}
+
void CustomData_realloc(CustomData *data, const int totelem)
{
BLI_assert(totelem >= 0);
@@ -2373,6 +2412,17 @@ void CustomData_copy(const CustomData *source,
CustomData_merge(source, dest, mask, alloctype, totelem);
}
+void CustomData_copy_mesh_to_bmesh(const CustomData *source,
+ CustomData *dest,
+ const eCustomDataMask mask,
+ const eCDAllocType alloctype,
+ const int totelem)
+{
+ CustomData source_copy = shallow_copy_remove_non_bmesh_attributes(*source);
+ CustomData_copy(&source_copy, dest, mask, alloctype, totelem);
+ MEM_SAFE_FREE(source_copy.layers);
+}
+
static void customData_free_layer__internal(CustomDataLayer *layer, const int totelem)
{
const LayerTypeInfo *typeInfo;
@@ -4303,7 +4353,9 @@ void CustomData_file_write_info(int type, const char **r_struct_name, int *r_str
*r_struct_num = typeInfo->structnum;
}
-void CustomData_blend_write_prepare(CustomData &data, Vector<CustomDataLayer, 16> &layers_to_write)
+void CustomData_blend_write_prepare(CustomData &data,
+ Vector<CustomDataLayer, 16> &layers_to_write,
+ const Set<StringRef> &skip_names)
{
for (const CustomDataLayer &layer : Span(data.layers, data.totlayer)) {
if (layer.flag & CD_FLAG_NOCOPY) {
@@ -4312,6 +4364,9 @@ void CustomData_blend_write_prepare(CustomData &data, Vector<CustomDataLayer, 16
if (layer.anonymous_id != nullptr) {
continue;
}
+ if (skip_names.contains(layer.name)) {
+ continue;
+ }
layers_to_write.append(layer);
}
data.totlayer = layers_to_write.size();
diff --git a/source/blender/blenkernel/intern/displist.cc b/source/blender/blenkernel/intern/displist.cc
index b1017a78e15..0b3ed584246 100644
--- a/source/blender/blenkernel/intern/displist.cc
+++ b/source/blender/blenkernel/intern/displist.cc
@@ -1480,7 +1480,7 @@ void BKE_displist_make_curveTypes(Depsgraph *depsgraph,
* - The dependency graph has handling of edit mode pointers (see #update_edit_mode_pointers)
* but it doesn't seem to work in this case.
*
- * Since the the plan is to replace this legacy curve object with the curves data-block
+ * Since the plan is to replace this legacy curve object with the curves data-block
* (see T95355), this somewhat hacky inefficient solution is relatively temporary.
*/
Curve &cow_curve = *reinterpret_cast<Curve *>(
diff --git a/source/blender/blenkernel/intern/gpencil_curve.c b/source/blender/blenkernel/intern/gpencil_curve.c
index d68b322e4c5..bf224a9613e 100644
--- a/source/blender/blenkernel/intern/gpencil_curve.c
+++ b/source/blender/blenkernel/intern/gpencil_curve.c
@@ -337,7 +337,6 @@ static void gpencil_convert_spline(Main *bmain,
/* Add stroke to frame. */
BLI_addtail(&gpf->strokes, gps);
- float *coord_array = NULL;
float init_co[3];
switch (nu->type) {
@@ -376,8 +375,7 @@ static void gpencil_convert_spline(Main *bmain,
BezTriple *bezt = &nu->bezt[inext];
bool last = (bool)(s == segments - 1);
- coord_array = MEM_callocN((size_t)3 * resolu * sizeof(float), __func__);
-
+ float *coord_array = MEM_callocN(sizeof(float[3]) * resolu, __func__);
for (int j = 0; j < 3; j++) {
BKE_curve_forward_diff_bezier(prevbezt->vec[1][j],
prevbezt->vec[2][j],
@@ -397,8 +395,9 @@ static void gpencil_convert_spline(Main *bmain,
gpencil_add_new_points(
gps, coord_array, radius_start, radius_end, init, resolu, init_co, last);
+
/* Free memory. */
- MEM_SAFE_FREE(coord_array);
+ MEM_freeN(coord_array);
/* As the last point of segment is the first point of next segment, back one array
* element to avoid duplicated points on the same location.
@@ -419,7 +418,7 @@ static void gpencil_convert_spline(Main *bmain,
nurb_points = (nu->pntsu - 1) * resolu;
}
/* Get all curve points. */
- coord_array = MEM_callocN(sizeof(float[3]) * nurb_points, __func__);
+ float *coord_array = MEM_callocN(sizeof(float[3]) * nurb_points, __func__);
BKE_nurb_makeCurve(nu, coord_array, NULL, NULL, NULL, resolu, sizeof(float[3]));
/* Allocate memory for storage points. */
@@ -429,7 +428,7 @@ static void gpencil_convert_spline(Main *bmain,
/* Add points. */
gpencil_add_new_points(gps, coord_array, 1.0f, 1.0f, 0, gps->totpoints, init_co, false);
- MEM_SAFE_FREE(coord_array);
+ MEM_freeN(coord_array);
}
break;
}
diff --git a/source/blender/blenkernel/intern/gpencil_modifier.c b/source/blender/blenkernel/intern/gpencil_modifier.c
index 82899b974bc..8ac268b26b0 100644
--- a/source/blender/blenkernel/intern/gpencil_modifier.c
+++ b/source/blender/blenkernel/intern/gpencil_modifier.c
@@ -360,7 +360,8 @@ GpencilModifierData *BKE_gpencil_modifier_new(int type)
md->type = type;
md->mode = eGpencilModifierMode_Realtime | eGpencilModifierMode_Render;
md->flag = eGpencilModifierFlag_OverrideLibrary_Local;
- md->ui_expand_flag = 1; /* Only expand the parent panel at first. */
+ /* Only expand the parent panel at first. */
+ md->ui_expand_flag = UI_PANEL_DATA_EXPAND_ROOT;
if (mti->flags & eGpencilModifierTypeFlag_EnableInEditmode) {
md->mode |= eGpencilModifierMode_Editmode;
diff --git a/source/blender/blenkernel/intern/icons_rasterize.c b/source/blender/blenkernel/intern/icons_rasterize.c
index 5603d84022d..00dbdcfa1e5 100644
--- a/source/blender/blenkernel/intern/icons_rasterize.c
+++ b/source/blender/blenkernel/intern/icons_rasterize.c
@@ -76,7 +76,7 @@ ImBuf *BKE_icon_geom_rasterize(const struct Icon_Geom *geom,
const uchar(*pos)[2] = geom->coords;
const uint *col = (void *)geom->colors;
- /* TODO(campbell): Currently rasterizes to fixed size, then scales.
+ /* TODO(@campbellbarton): Currently rasterizes to fixed size, then scales.
* Should rasterize to double size for eg instead. */
const int rect_size[2] = {max_ii(256, (int)size_x * 2), max_ii(256, (int)size_y * 2)};
diff --git a/source/blender/blenkernel/intern/idprop.c b/source/blender/blenkernel/intern/idprop.c
index 43e732b428d..98c317c547b 100644
--- a/source/blender/blenkernel/intern/idprop.c
+++ b/source/blender/blenkernel/intern/idprop.c
@@ -784,7 +784,7 @@ IDProperty *IDP_GetProperties(ID *id, const bool create_if_needed)
if (create_if_needed) {
id->properties = MEM_callocN(sizeof(IDProperty), "IDProperty");
id->properties->type = IDP_GROUP;
- /* NOTE(campbell): Don't overwrite the data's name and type
+ /* NOTE(@campbellbarton): Don't overwrite the data's name and type
* some functions might need this if they
* don't have a real ID, should be named elsewhere. */
// strcpy(id->name, "top_level_group");
diff --git a/source/blender/blenkernel/intern/key.c b/source/blender/blenkernel/intern/key.c
index 07ce4e46e9b..461a6f15ca1 100644
--- a/source/blender/blenkernel/intern/key.c
+++ b/source/blender/blenkernel/intern/key.c
@@ -91,7 +91,7 @@ static void shapekey_foreach_id(ID *id, LibraryForeachIDData *data)
BKE_LIB_FOREACHID_PROCESS_ID(data, key->from, IDWALK_CB_LOOPBACK);
}
-static ID *shapekey_owner_get(Main *UNUSED(bmain), ID *id)
+static ID *shapekey_owner_get(Main *UNUSED(bmain), ID *id, ID *UNUSED(owner_id_hint))
{
return ((Key *)id)->from;
}
diff --git a/source/blender/blenkernel/intern/lib_override.cc b/source/blender/blenkernel/intern/lib_override.cc
index 758a317e415..05a00fb54fd 100644
--- a/source/blender/blenkernel/intern/lib_override.cc
+++ b/source/blender/blenkernel/intern/lib_override.cc
@@ -94,6 +94,7 @@ BLI_INLINE void lib_override_object_posemode_transfer(ID *id_dst, ID *id_src)
/** Get override data for a given ID. Needed because of our beloved shape keys snowflake. */
BLI_INLINE const IDOverrideLibrary *BKE_lib_override_library_get(const Main *bmain,
const ID *id,
+ const ID *owner_id_hint,
const ID **r_owner_id)
{
if (r_owner_id != nullptr) {
@@ -104,7 +105,8 @@ BLI_INLINE const IDOverrideLibrary *BKE_lib_override_library_get(const Main *bma
if (id_type->owner_get != nullptr) {
/* The #IDTypeInfo::owner_get callback should not modify the arguments, so casting away const
* is okay. */
- const ID *owner_id = id_type->owner_get(const_cast<Main *>(bmain), const_cast<ID *>(id));
+ const ID *owner_id = id_type->owner_get(
+ const_cast<Main *>(bmain), const_cast<ID *>(id), const_cast<ID *>(owner_id_hint));
if (r_owner_id != nullptr) {
*r_owner_id = owner_id;
}
@@ -115,13 +117,17 @@ BLI_INLINE const IDOverrideLibrary *BKE_lib_override_library_get(const Main *bma
return id->override_library;
}
-IDOverrideLibrary *BKE_lib_override_library_get(Main *bmain, ID *id, ID **r_owner_id)
+IDOverrideLibrary *BKE_lib_override_library_get(Main *bmain,
+ ID *id,
+ ID *owner_id_hint,
+ ID **r_owner_id)
{
/* Reuse the implementation of the const access function, which does not change the arguments.
* Add const explicitly to make it clear to the compiler to avoid just calling this function. */
return const_cast<IDOverrideLibrary *>(
BKE_lib_override_library_get(const_cast<const Main *>(bmain),
const_cast<const ID *>(id),
+ const_cast<const ID *>(owner_id_hint),
const_cast<const ID **>(r_owner_id)));
}
@@ -319,7 +325,7 @@ bool BKE_lib_override_library_is_system_defined(const Main *bmain, const ID *id)
{
if (ID_IS_OVERRIDE_LIBRARY(id)) {
const ID *override_owner_id;
- BKE_lib_override_library_get(bmain, id, &override_owner_id);
+ BKE_lib_override_library_get(bmain, id, nullptr, &override_owner_id);
return (override_owner_id->override_library->flag & IDOVERRIDE_LIBRARY_FLAG_SYSTEM_DEFINED) !=
0;
}
@@ -1087,8 +1093,9 @@ static void lib_override_overrides_group_tag_recursive(LibOverrideGroupTagData *
}
const Library *reference_lib =
- BKE_lib_override_library_get(bmain, id_owner, nullptr)->reference->lib;
- const ID *to_id_reference = BKE_lib_override_library_get(bmain, to_id, nullptr)->reference;
+ BKE_lib_override_library_get(bmain, id_owner, nullptr, nullptr)->reference->lib;
+ const ID *to_id_reference =
+ BKE_lib_override_library_get(bmain, to_id, nullptr, nullptr)->reference;
if (to_id_reference->lib != reference_lib) {
/* We do not override data-blocks from other libraries, nor do we process them. */
continue;
@@ -1439,7 +1446,7 @@ static ID *lib_override_root_find(Main *bmain, ID *id, const int curr_level, int
BLI_assert(id->flag & LIB_EMBEDDED_DATA_LIB_OVERRIDE);
ID *id_owner;
int best_level_placeholder = 0;
- BKE_lib_override_library_get(bmain, id, &id_owner);
+ BKE_lib_override_library_get(bmain, id, nullptr, &id_owner);
return lib_override_root_find(bmain, id_owner, curr_level + 1, &best_level_placeholder);
}
/* This way we won't process again that ID, should we encounter it again through another
@@ -1478,7 +1485,7 @@ static ID *lib_override_root_find(Main *bmain, ID *id, const int curr_level, int
BLI_assert(id->flag & LIB_EMBEDDED_DATA_LIB_OVERRIDE);
ID *id_owner;
int best_level_placeholder = 0;
- BKE_lib_override_library_get(bmain, best_root_id_candidate, &id_owner);
+ BKE_lib_override_library_get(bmain, best_root_id_candidate, nullptr, &id_owner);
best_root_id_candidate = lib_override_root_find(
bmain, id_owner, curr_level + 1, &best_level_placeholder);
}
@@ -1795,7 +1802,8 @@ static bool lib_override_library_resync(Main *bmain,
/* While this should not happen in typical cases (and won't be properly supported here),
* user is free to do all kind of very bad things, including having different local
* overrides of a same linked ID in a same hierarchy. */
- IDOverrideLibrary *id_override_library = BKE_lib_override_library_get(bmain, id, nullptr);
+ IDOverrideLibrary *id_override_library = BKE_lib_override_library_get(
+ bmain, id, nullptr, nullptr);
if (id_override_library->hierarchy_root != id_root->override_library->hierarchy_root) {
continue;
@@ -2177,7 +2185,7 @@ static ID *lib_override_library_main_resync_root_get(Main *bmain, ID *id)
if (!ID_IS_OVERRIDE_LIBRARY_REAL(id)) {
const IDTypeInfo *id_type = BKE_idtype_get_info_from_id(id);
if (id_type->owner_get != nullptr) {
- id = id_type->owner_get(bmain, id);
+ id = id_type->owner_get(bmain, id, nullptr);
}
BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id));
}
diff --git a/source/blender/blenkernel/intern/lib_query.c b/source/blender/blenkernel/intern/lib_query.c
index a869bf4c4b0..38d1a30592d 100644
--- a/source/blender/blenkernel/intern/lib_query.c
+++ b/source/blender/blenkernel/intern/lib_query.c
@@ -696,8 +696,8 @@ static void lib_query_unused_ids_tag_recurse(Main *bmain,
bool has_valid_from_users = false;
/* Preemptively consider this ID as unused. That way if there is a loop of dependency leading
* back to it, it won't create a fake 'valid user' detection.
- * NOTE: This can only only be done for a subset of IDs, some types are never 'indirectly
- * unused', same for IDs with a fake user. */
+ * NOTE: This can only be done for a subset of IDs, some types are never 'indirectly unused',
+ * same for IDs with a fake user. */
if ((id->flag & LIB_FAKEUSER) == 0 && !ELEM(GS(id->name), ID_SCE, ID_WM, ID_SCR, ID_WS, ID_LI)) {
id->tag |= tag;
}
@@ -713,7 +713,7 @@ static void lib_query_unused_ids_tag_recurse(Main *bmain,
/* Directly 'by-pass' to actual real ID owner. */
const IDTypeInfo *type_info_from = BKE_idtype_get_info_from_id(id_from);
BLI_assert(type_info_from->owner_get != NULL);
- id_from = type_info_from->owner_get(bmain, id_from);
+ id_from = type_info_from->owner_get(bmain, id_from, NULL);
}
lib_query_unused_ids_tag_recurse(
diff --git a/source/blender/blenkernel/intern/library.c b/source/blender/blenkernel/intern/library.c
index dd58c9cc4fe..9424b615031 100644
--- a/source/blender/blenkernel/intern/library.c
+++ b/source/blender/blenkernel/intern/library.c
@@ -122,7 +122,7 @@ void BKE_library_filepath_set(Main *bmain, Library *lib, const char *filepath)
/* Not essential but set `filepath_abs` is an absolute copy of value which
* is more useful if its kept in sync. */
if (BLI_path_is_rel(lib->filepath_abs)) {
- /* NOTE(campbell): the file may be unsaved, in this case, setting the
+ /* NOTE(@campbellbarton): the file may be unsaved, in this case, setting the
* `filepath_abs` on an indirectly linked path is not allowed from the
* outliner, and its not really supported but allow from here for now
* since making local could cause this to be directly linked.
diff --git a/source/blender/blenkernel/intern/material.c b/source/blender/blenkernel/intern/material.c
index f899901b54e..248d292664a 100644
--- a/source/blender/blenkernel/intern/material.c
+++ b/source/blender/blenkernel/intern/material.c
@@ -852,7 +852,7 @@ void BKE_object_material_resize(Main *bmain, Object *ob, const short totcol, boo
ob->mat = newmatar;
ob->matbits = newmatbits;
}
- /* XXX(campbell): why not realloc on shrink? */
+ /* XXX(@campbellbarton): why not realloc on shrink? */
ob->totcol = totcol;
if (ob->totcol && ob->actcol == 0) {
diff --git a/source/blender/blenkernel/intern/mball.c b/source/blender/blenkernel/intern/mball.cc
index 2a1c940493c..084fea6abbd 100644
--- a/source/blender/blenkernel/intern/mball.c
+++ b/source/blender/blenkernel/intern/mball.cc
@@ -11,12 +11,12 @@
* texture coordinates are patched within the displist
*/
-#include <ctype.h>
-#include <float.h>
-#include <math.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
+#include <cctype>
+#include <cfloat>
+#include <cmath>
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
#include "MEM_guardedalloc.h"
@@ -72,11 +72,11 @@ static void metaball_copy_data(Main *UNUSED(bmain),
BLI_duplicatelist(&metaball_dst->elems, &metaball_src->elems);
- metaball_dst->mat = MEM_dupallocN(metaball_src->mat);
+ metaball_dst->mat = static_cast<Material **>(MEM_dupallocN(metaball_src->mat));
- metaball_dst->editelems = NULL;
- metaball_dst->lastelem = NULL;
- metaball_dst->batch_cache = NULL;
+ metaball_dst->editelems = nullptr;
+ metaball_dst->lastelem = nullptr;
+ metaball_dst->batch_cache = nullptr;
}
static void metaball_free_data(ID *id)
@@ -107,11 +107,11 @@ static void metaball_blend_write(BlendWriter *writer, ID *id, const void *id_add
/* Clean up, important in undo case to reduce false detection of changed datablocks. */
BLI_listbase_clear(&mb->disp);
- mb->editelems = NULL;
+ mb->editelems = nullptr;
/* Must always be cleared (meta's don't have their own edit-data). */
mb->needs_flush_to_id = 0;
- mb->lastelem = NULL;
- mb->batch_cache = NULL;
+ mb->lastelem = nullptr;
+ mb->batch_cache = nullptr;
/* write LibData */
BLO_write_id_struct(writer, MetaBall, id_address, &mb->id);
@@ -139,12 +139,12 @@ static void metaball_blend_read_data(BlendDataReader *reader, ID *id)
BLO_read_list(reader, &(mb->elems));
BLI_listbase_clear(&mb->disp);
- mb->editelems = NULL;
+ mb->editelems = nullptr;
/* Must always be cleared (meta's don't have their own edit-data). */
mb->needs_flush_to_id = 0;
- // mb->edit_elems.first = mb->edit_elems.last = NULL;
- mb->lastelem = NULL;
- mb->batch_cache = NULL;
+ // mb->edit_elems.first = mb->edit_elems.last = nullptr;
+ mb->lastelem = nullptr;
+ mb->batch_cache = nullptr;
}
static void metaball_blend_read_lib(BlendLibReader *reader, ID *id)
@@ -166,49 +166,46 @@ static void metaball_blend_read_expand(BlendExpander *expander, ID *id)
}
IDTypeInfo IDType_ID_MB = {
- .id_code = ID_MB,
- .id_filter = FILTER_ID_MB,
- .main_listbase_index = INDEX_ID_MB,
- .struct_size = sizeof(MetaBall),
- .name = "Metaball",
- .name_plural = "metaballs",
- .translation_context = BLT_I18NCONTEXT_ID_METABALL,
- .flags = IDTYPE_FLAGS_APPEND_IS_REUSABLE,
- .asset_type_info = NULL,
-
- .init_data = metaball_init_data,
- .copy_data = metaball_copy_data,
- .free_data = metaball_free_data,
- .make_local = NULL,
- .foreach_id = metaball_foreach_id,
- .foreach_cache = NULL,
- .foreach_path = NULL,
- .owner_get = NULL,
-
- .blend_write = metaball_blend_write,
- .blend_read_data = metaball_blend_read_data,
- .blend_read_lib = metaball_blend_read_lib,
- .blend_read_expand = metaball_blend_read_expand,
-
- .blend_read_undo_preserve = NULL,
-
- .lib_override_apply_post = NULL,
+ /* id_code */ ID_MB,
+ /* id_filter */ FILTER_ID_MB,
+ /* main_listbase_index */ INDEX_ID_MB,
+ /* struct_size */ sizeof(MetaBall),
+ /* name */ "Metaball",
+ /* name_plural */ "metaballs",
+ /* translation_context */ BLT_I18NCONTEXT_ID_METABALL,
+ /* flags */ IDTYPE_FLAGS_APPEND_IS_REUSABLE,
+ /* asset_type_info */ nullptr,
+
+ /* init_data */ metaball_init_data,
+ /* copy_data */ metaball_copy_data,
+ /* free_data */ metaball_free_data,
+ /* make_local */ nullptr,
+ /* foreach_id */ metaball_foreach_id,
+ /* foreach_cache */ nullptr,
+ /* foreach_path */ nullptr,
+ /* owner_get */ nullptr,
+
+ /* blend_write */ metaball_blend_write,
+ /* blend_read_data */ metaball_blend_read_data,
+ /* blend_read_lib */ metaball_blend_read_lib,
+ /* blend_read_expand */ metaball_blend_read_expand,
+
+ /* blend_read_undo_preserve */ nullptr,
+
+ /* lib_override_apply_post */ nullptr,
};
/* Functions */
MetaBall *BKE_mball_add(Main *bmain, const char *name)
{
- MetaBall *mb;
-
- mb = BKE_id_new(bmain, ID_MB, name);
-
+ MetaBall *mb = static_cast<MetaBall *>(BKE_id_new(bmain, ID_MB, name));
return mb;
}
MetaElem *BKE_mball_element_add(MetaBall *mb, const int type)
{
- MetaElem *ml = MEM_callocN(sizeof(MetaElem), "metaelem");
+ MetaElem *ml = MEM_cnew<MetaElem>(__func__);
unit_qt(ml->quat);
@@ -260,8 +257,8 @@ void BKE_mball_texspace_calc(Object *ob)
int tot;
bool do_it = false;
- if (ob->runtime.bb == NULL) {
- ob->runtime.bb = MEM_callocN(sizeof(BoundBox), "mb boundbox");
+ if (ob->runtime.bb == nullptr) {
+ ob->runtime.bb = MEM_cnew<BoundBox>(__func__);
}
bb = ob->runtime.bb;
@@ -270,7 +267,7 @@ void BKE_mball_texspace_calc(Object *ob)
(min)[0] = (min)[1] = (min)[2] = 1.0e30f;
(max)[0] = (max)[1] = (max)[2] = -1.0e30f;
- dl = ob->runtime.curve_cache->disp.first;
+ dl = static_cast<DispList *>(ob->runtime.curve_cache->disp.first);
while (dl) {
tot = dl->nr;
if (tot) {
@@ -299,13 +296,13 @@ BoundBox *BKE_mball_boundbox_get(Object *ob)
{
BLI_assert(ob->type == OB_MBALL);
- if (ob->runtime.bb != NULL && (ob->runtime.bb->flag & BOUNDBOX_DIRTY) == 0) {
+ if (ob->runtime.bb != nullptr && (ob->runtime.bb->flag & BOUNDBOX_DIRTY) == 0) {
return ob->runtime.bb;
}
/* This should always only be called with evaluated objects,
* but currently RNA is a problem here... */
- if (ob->runtime.curve_cache != NULL) {
+ if (ob->runtime.curve_cache != nullptr) {
BKE_mball_texspace_calc(ob);
}
@@ -329,8 +326,8 @@ float *BKE_mball_make_orco(Object *ob, ListBase *dispbase)
loc[2] = (bb->vec[0][2] + bb->vec[1][2]) / 2.0f;
size[2] = bb->vec[1][2] - loc[2];
- dl = dispbase->first;
- orcodata = MEM_mallocN(sizeof(float[3]) * dl->nr, "MballOrco");
+ dl = static_cast<DispList *>(dispbase->first);
+ orcodata = static_cast<float *>(MEM_mallocN(sizeof(float[3]) * dl->nr, __func__));
data = dl->verts;
orco = orcodata;
@@ -393,7 +390,7 @@ bool BKE_mball_is_basis_for(const Object *ob1, const Object *ob2)
bool BKE_mball_is_any_selected(const MetaBall *mb)
{
- for (const MetaElem *ml = mb->editelems->first; ml != NULL; ml = ml->next) {
+ LISTBASE_FOREACH (const MetaElem *, ml, mb->editelems) {
if (ml->flag & SELECT) {
return true;
}
@@ -415,7 +412,7 @@ bool BKE_mball_is_any_selected_multi(Base **bases, int bases_len)
bool BKE_mball_is_any_unselected(const MetaBall *mb)
{
- for (const MetaElem *ml = mb->editelems->first; ml != NULL; ml = ml->next) {
+ LISTBASE_FOREACH (const MetaElem *, ml, mb->editelems) {
if ((ml->flag & SELECT) == 0) {
return true;
}
@@ -451,9 +448,10 @@ void BKE_mball_properties_copy(Main *bmain, MetaBall *metaball_src)
* Solving this case would drastically increase the complexity of this code though, so don't
* think it would be worth it.
*/
- for (Object *ob_src = bmain->objects.first; ob_src != NULL && !ID_IS_LINKED(ob_src);) {
+ for (Object *ob_src = static_cast<Object *>(bmain->objects.first);
+ ob_src != nullptr && !ID_IS_LINKED(ob_src);) {
if (ob_src->data != metaball_src) {
- ob_src = ob_src->id.next;
+ ob_src = static_cast<Object *>(ob_src->id.next);
continue;
}
@@ -466,12 +464,13 @@ void BKE_mball_properties_copy(Main *bmain, MetaBall *metaball_src)
* Using this, it is possible to process the whole set of meta-balls with a single loop on the
* whole list of Objects, though additionally going backward on part of the list in some cases.
*/
- Object *ob_iter = NULL;
+ Object *ob_iter = nullptr;
int obactive_nr, ob_nr;
char obactive_name[MAX_ID_NAME], ob_name[MAX_ID_NAME];
BLI_split_name_num(obactive_name, &obactive_nr, ob_src->id.name + 2, '.');
- for (ob_iter = ob_src->id.prev; ob_iter != NULL; ob_iter = ob_iter->id.prev) {
+ for (ob_iter = static_cast<Object *>(ob_src->id.prev); ob_iter != nullptr;
+ ob_iter = static_cast<Object *>(ob_iter->id.prev)) {
if (ob_iter->id.name[2] != obactive_name[0]) {
break;
}
@@ -483,10 +482,11 @@ void BKE_mball_properties_copy(Main *bmain, MetaBall *metaball_src)
break;
}
- mball_data_properties_copy(ob_iter->data, metaball_src);
+ mball_data_properties_copy(static_cast<MetaBall *>(ob_iter->data), metaball_src);
}
- for (ob_iter = ob_src->id.next; ob_iter != NULL; ob_iter = ob_iter->id.next) {
+ for (ob_iter = static_cast<Object *>(ob_src->id.next); ob_iter != nullptr;
+ ob_iter = static_cast<Object *>(ob_iter->id.next)) {
if (ob_iter->id.name[2] != obactive_name[0] || ID_IS_LINKED(ob_iter)) {
break;
}
@@ -498,7 +498,7 @@ void BKE_mball_properties_copy(Main *bmain, MetaBall *metaball_src)
break;
}
- mball_data_properties_copy(ob_iter->data, metaball_src);
+ mball_data_properties_copy(static_cast<MetaBall *>(ob_iter->data), metaball_src);
}
ob_src = ob_iter;
@@ -556,7 +556,7 @@ bool BKE_mball_minmax_ex(
copy_v3_v3(centroid, &ml->x);
}
- /* TODO(campbell): non circle shapes cubes etc, probably nobody notices. */
+ /* TODO(@campbellbarton): non circle shapes cubes etc, probably nobody notices. */
for (int i = -1; i != 3; i += 2) {
copy_v3_v3(vec, centroid);
add_v3_fl(vec, scale_mb * i);
@@ -682,7 +682,7 @@ bool BKE_mball_select_all_multi_ex(Base **bases, int bases_len)
bool changed_multi = false;
for (uint ob_index = 0; ob_index < bases_len; ob_index++) {
Object *obedit = bases[ob_index]->object;
- MetaBall *mb = obedit->data;
+ MetaBall *mb = static_cast<MetaBall *>(obedit->data);
changed_multi |= BKE_mball_select_all(mb);
}
return changed_multi;
@@ -705,7 +705,7 @@ bool BKE_mball_deselect_all_multi_ex(Base **bases, int bases_len)
bool changed_multi = false;
for (uint ob_index = 0; ob_index < bases_len; ob_index++) {
Object *obedit = bases[ob_index]->object;
- MetaBall *mb = obedit->data;
+ MetaBall *mb = static_cast<MetaBall *>(obedit->data);
changed_multi |= BKE_mball_deselect_all(mb);
DEG_id_tag_update(&mb->id, ID_RECALC_SELECT);
}
@@ -737,8 +737,8 @@ bool BKE_mball_select_swap_multi_ex(Base **bases, int bases_len)
/* Draw Engine */
-void (*BKE_mball_batch_cache_dirty_tag_cb)(MetaBall *mb, int mode) = NULL;
-void (*BKE_mball_batch_cache_free_cb)(MetaBall *mb) = NULL;
+void (*BKE_mball_batch_cache_dirty_tag_cb)(MetaBall *mb, int mode) = nullptr;
+void (*BKE_mball_batch_cache_free_cb)(MetaBall *mb) = nullptr;
void BKE_mball_batch_cache_dirty_tag(MetaBall *mb, int mode)
{
diff --git a/source/blender/blenkernel/intern/mesh.cc b/source/blender/blenkernel/intern/mesh.cc
index 5775d29479e..c7a32fd7b03 100644
--- a/source/blender/blenkernel/intern/mesh.cc
+++ b/source/blender/blenkernel/intern/mesh.cc
@@ -39,6 +39,7 @@
#include "BLT_translation.h"
#include "BKE_anim_data.h"
+#include "BKE_attribute.hh"
#include "BKE_bpath.h"
#include "BKE_deform.h"
#include "BKE_editmesh.h"
@@ -65,8 +66,13 @@
#include "BLO_read_write.h"
using blender::float3;
+<<<<<<< HEAD
using blender::IndexRange;
using blender::MutableSpan;
+=======
+using blender::MutableSpan;
+using blender::VArray;
+>>>>>>> master
using blender::Vector;
static void mesh_clear_geometry(Mesh *mesh);
@@ -246,10 +252,14 @@ static void mesh_blend_write(BlendWriter *writer, ID *id, const void *id_address
memset(&mesh->pdata, 0, sizeof(mesh->pdata));
}
else {
- CustomData_blend_write_prepare(mesh->vdata, vert_layers);
- CustomData_blend_write_prepare(mesh->edata, edge_layers);
+ if (!BLO_write_is_undo(writer)) {
+ BKE_mesh_legacy_convert_hide_layers_to_flags(mesh);
+ }
+
+ CustomData_blend_write_prepare(mesh->vdata, vert_layers, {".hide_vert"});
+ CustomData_blend_write_prepare(mesh->edata, edge_layers, {".hide_edge"});
CustomData_blend_write_prepare(mesh->ldata, loop_layers);
- CustomData_blend_write_prepare(mesh->pdata, poly_layers);
+ CustomData_blend_write_prepare(mesh->pdata, poly_layers, {".hide_poly"});
}
if (!BLO_write_is_undo(writer)) {
@@ -333,7 +343,11 @@ static void mesh_blend_read_data(BlendDataReader *reader, ID *id)
}
if (!BLO_read_data_is_undo(reader)) {
+<<<<<<< HEAD
BKE_mesh_legacy_bevel_weight_to_layers(mesh);
+=======
+ BKE_mesh_legacy_convert_flags_to_hide_layers(mesh);
+>>>>>>> master
}
/* We don't expect to load normals from files, since they are derived data. */
@@ -773,10 +787,10 @@ static void mesh_ensure_tessellation_customdata(Mesh *me)
/* TODO: add some `--debug-mesh` option. */
if (G.debug & G_DEBUG) {
- /* NOTE(campbell): this warning may be un-called for if we are initializing the mesh for
- * the first time from #BMesh, rather than giving a warning about this we could be smarter
- * and check if there was any data to begin with, for now just print the warning with
- * some info to help troubleshoot what's going on. */
+ /* NOTE(@campbellbarton): this warning may be un-called for if we are initializing the mesh
+ * for the first time from #BMesh, rather than giving a warning about this we could be
+ * smarter and check if there was any data to begin with, for now just print the warning
+ * with some info to help troubleshoot what's going on. */
printf(
"%s: warning! Tessellation uvs or vcol data got out of sync, "
"had to reset!\n CD_MTFACE: %d != CD_MLOOPUV: %d || CD_MCOL: %d != "
diff --git a/source/blender/blenkernel/intern/mesh_convert.cc b/source/blender/blenkernel/intern/mesh_convert.cc
index fce001826e0..000f356fd6a 100644
--- a/source/blender/blenkernel/intern/mesh_convert.cc
+++ b/source/blender/blenkernel/intern/mesh_convert.cc
@@ -22,6 +22,7 @@
#include "BLI_index_range.hh"
#include "BLI_listbase.h"
#include "BLI_math.h"
+#include "BLI_span.hh"
#include "BLI_string.h"
#include "BLI_utildefines.h"
@@ -54,6 +55,8 @@
#include "DEG_depsgraph_query.h"
using blender::IndexRange;
+using blender::MutableSpan;
+using blender::Span;
/* Define for cases when you want extra validation of mesh
* after certain modifications.
@@ -127,29 +130,28 @@ void BKE_mesh_from_metaball(ListBase *lb, Mesh *me)
/**
* Specialized function to use when we _know_ existing edges don't overlap with poly edges.
*/
-static void make_edges_mdata_extend(
- MEdge **r_alledge, int *r_totedge, const MPoly *mpoly, MLoop *mloop, const int totpoly)
+static void make_edges_mdata_extend(Mesh &mesh)
{
- int totedge = *r_totedge;
- int totedge_new;
- EdgeHash *eh;
- uint eh_reserve;
+ int totedge = mesh.totedge;
const MPoly *mp;
int i;
- eh_reserve = max_ii(totedge, BLI_EDGEHASH_SIZE_GUESS_FROM_POLYS(totpoly));
- eh = BLI_edgehash_new_ex(__func__, eh_reserve);
+ Span<MPoly> polys(mesh.mpoly, mesh.totpoly);
+ MutableSpan<MLoop> loops(mesh.mloop, mesh.totloop);
- for (i = 0, mp = mpoly; i < totpoly; i++, mp++) {
- BKE_mesh_poly_edgehash_insert(eh, mp, mloop + mp->loopstart);
+ const int eh_reserve = max_ii(totedge, BLI_EDGEHASH_SIZE_GUESS_FROM_POLYS(mesh.totpoly));
+ EdgeHash *eh = BLI_edgehash_new_ex(__func__, eh_reserve);
+
+ for (const MPoly &poly : polys) {
+ BKE_mesh_poly_edgehash_insert(eh, &poly, &loops[poly.loopstart]);
}
- totedge_new = BLI_edgehash_len(eh);
+ const int totedge_new = BLI_edgehash_len(eh);
#ifdef DEBUG
/* ensure that there's no overlap! */
if (totedge_new) {
- MEdge *medge = *r_alledge;
+ MEdge *medge = mesh.medge;
for (i = 0; i < totedge; i++, medge++) {
BLI_assert(BLI_edgehash_haskey(eh, medge->v1, medge->v2) == false);
}
@@ -157,19 +159,15 @@ static void make_edges_mdata_extend(
#endif
if (totedge_new) {
- EdgeHashIterator *ehi;
- MEdge *medge;
- uint e_index = totedge;
+ CustomData_realloc(&mesh.edata, totedge + totedge_new);
+ BKE_mesh_update_customdata_pointers(&mesh, false);
- *r_alledge = medge = (MEdge *)(*r_alledge ?
- MEM_reallocN(*r_alledge,
- sizeof(MEdge) * (totedge + totedge_new)) :
- MEM_calloc_arrayN(totedge_new, sizeof(MEdge), __func__));
- medge += totedge;
+ MEdge *medge = mesh.medge + totedge;
- totedge += totedge_new;
+ mesh.totedge += totedge_new;
- /* --- */
+ EdgeHashIterator *ehi;
+ uint e_index = totedge;
for (ehi = BLI_edgehashIterator_new(eh); BLI_edgehashIterator_isDone(ehi) == false;
BLI_edgehashIterator_step(ehi), ++medge, e_index++) {
BLI_edgehashIterator_getKey(ehi, &medge->v1, &medge->v2);
@@ -180,10 +178,8 @@ static void make_edges_mdata_extend(
}
BLI_edgehashIterator_free(ehi);
- *r_totedge = totedge;
-
- for (i = 0, mp = mpoly; i < totpoly; i++, mp++) {
- MLoop *l = &mloop[mp->loopstart];
+ for (i = 0, mp = mesh.mpoly; i < mesh.totpoly; i++, mp++) {
+ MLoop *l = &loops[mp->loopstart];
MLoop *l_prev = (l + (mp->totloop - 1));
int j;
for (j = 0; j < mp->totloop; j++, l++) {
@@ -197,25 +193,8 @@ static void make_edges_mdata_extend(
BLI_edgehash_free(eh, nullptr);
}
-/* Initialize mverts, medges and, faces for converting nurbs to mesh and derived mesh */
-/* use specified dispbase */
-static int mesh_nurbs_displist_to_mdata(const Curve *cu,
- const ListBase *dispbase,
- MVert **r_allvert,
- int *r_totvert,
- MEdge **r_alledge,
- int *r_totedge,
- MLoop **r_allloop,
- MPoly **r_allpoly,
- MLoopUV **r_alluv,
- int *r_totloop,
- int *r_totpoly)
+static Mesh *mesh_nurbs_displist_to_mesh(const Curve *cu, const ListBase *dispbase)
{
- MVert *mvert;
- MPoly *mpoly;
- MLoop *mloop;
- MLoopUV *mloopuv = nullptr;
- MEdge *medge;
const float *data;
int a, b, ofs, vertcount, startvert, totvert = 0, totedge = 0, totloop = 0, totpoly = 0;
int p1, p2, p3, p4, *index;
@@ -257,21 +236,21 @@ static int mesh_nurbs_displist_to_mdata(const Curve *cu,
}
if (totvert == 0) {
- /* Make Sure you check ob->data is a curve. */
- // error("can't convert");
- return -1;
+ return BKE_mesh_new_nomain(0, 0, 0, 0, 0);
}
- *r_allvert = mvert = (MVert *)MEM_calloc_arrayN(totvert, sizeof(MVert), "nurbs_init mvert");
- *r_alledge = medge = (MEdge *)MEM_calloc_arrayN(totedge, sizeof(MEdge), "nurbs_init medge");
- *r_allloop = mloop = (MLoop *)MEM_calloc_arrayN(
- totpoly, sizeof(MLoop[4]), "nurbs_init mloop"); /* totloop */
- *r_allpoly = mpoly = (MPoly *)MEM_calloc_arrayN(totpoly, sizeof(MPoly), "nurbs_init mloop");
+ Mesh *mesh = BKE_mesh_new_nomain(totvert, totedge, 0, totloop, totpoly);
+ MutableSpan<MVert> verts(mesh->mvert, mesh->totvert);
+ MutableSpan<MEdge> edges(mesh->medge, mesh->totedge);
+ MutableSpan<MPoly> polys(mesh->mpoly, mesh->totpoly);
+ MutableSpan<MLoop> loops(mesh->mloop, mesh->totloop);
- if (r_alluv) {
- *r_alluv = mloopuv = (MLoopUV *)MEM_calloc_arrayN(
- totpoly, sizeof(MLoopUV[4]), "nurbs_init mloopuv");
- }
+ MVert *mvert = verts.data();
+ MEdge *medge = edges.data();
+ MPoly *mpoly = polys.data();
+ MLoop *mloop = loops.data();
+ MLoopUV *mloopuv = static_cast<MLoopUV *>(CustomData_add_layer_named(
+ &mesh->ldata, CD_MLOOPUV, CD_CALLOC, nullptr, mesh->totloop, "UVMap"));
/* verts and faces */
vertcount = 0;
@@ -346,7 +325,7 @@ static int mesh_nurbs_displist_to_mdata(const Curve *cu,
mloop[0].v = startvert + index[0];
mloop[1].v = startvert + index[2];
mloop[2].v = startvert + index[1];
- mpoly->loopstart = (int)(mloop - (*r_allloop));
+ mpoly->loopstart = (int)(mloop - loops.data());
mpoly->totloop = 3;
mpoly->mat_nr = dl->col;
@@ -406,7 +385,7 @@ static int mesh_nurbs_displist_to_mdata(const Curve *cu,
mloop[1].v = p3;
mloop[2].v = p4;
mloop[3].v = p2;
- mpoly->loopstart = (int)(mloop - (*r_allloop));
+ mpoly->loopstart = (int)(mloop - loops.data());
mpoly->totloop = 4;
mpoly->mat_nr = dl->col;
@@ -458,15 +437,10 @@ static int mesh_nurbs_displist_to_mdata(const Curve *cu,
}
if (totpoly) {
- make_edges_mdata_extend(r_alledge, &totedge, *r_allpoly, *r_allloop, totpoly);
+ make_edges_mdata_extend(*mesh);
}
- *r_totpoly = totpoly;
- *r_totloop = totloop;
- *r_totedge = totedge;
- *r_totvert = totvert;
-
- return 0;
+ return mesh;
}
/**
@@ -487,60 +461,12 @@ static void mesh_copy_texture_space_from_curve_type(const Curve *cu, Mesh *me)
Mesh *BKE_mesh_new_nomain_from_curve_displist(const Object *ob, const ListBase *dispbase)
{
const Curve *cu = (const Curve *)ob->data;
- Mesh *mesh;
- MVert *allvert;
- MEdge *alledge;
- MLoop *allloop;
- MPoly *allpoly;
- MLoopUV *alluv = nullptr;
- int totvert, totedge, totloop, totpoly;
-
- if (mesh_nurbs_displist_to_mdata(cu,
- dispbase,
- &allvert,
- &totvert,
- &alledge,
- &totedge,
- &allloop,
- &allpoly,
- &alluv,
- &totloop,
- &totpoly) != 0) {
- /* Error initializing mdata. This often happens when curve is empty */
- return BKE_mesh_new_nomain(0, 0, 0, 0, 0);
- }
-
- mesh = BKE_mesh_new_nomain(totvert, totedge, 0, totloop, totpoly);
-
- if (totvert != 0) {
- memcpy(mesh->mvert, allvert, totvert * sizeof(MVert));
- }
- if (totedge != 0) {
- memcpy(mesh->medge, alledge, totedge * sizeof(MEdge));
- }
- if (totloop != 0) {
- memcpy(mesh->mloop, allloop, totloop * sizeof(MLoop));
- }
- if (totpoly != 0) {
- memcpy(mesh->mpoly, allpoly, totpoly * sizeof(MPoly));
- }
-
- if (alluv) {
- const char *uvname = "UVMap";
- CustomData_add_layer_named(&mesh->ldata, CD_MLOOPUV, CD_ASSIGN, alluv, totloop, uvname);
- }
+ Mesh *mesh = mesh_nurbs_displist_to_mesh(cu, dispbase);
mesh_copy_texture_space_from_curve_type(cu, mesh);
-
- /* Copy curve materials. */
mesh->mat = (Material **)MEM_dupallocN(cu->mat);
mesh->totcol = cu->totcol;
- MEM_freeN(allvert);
- MEM_freeN(alledge);
- MEM_freeN(allloop);
- MEM_freeN(allpoly);
-
return mesh;
}
@@ -985,6 +911,12 @@ static Mesh *mesh_new_from_curve_type_object(const Object *object)
/* If evaluating the curve replaced object data with different data, free the original data. */
if (temp_data != temp_object->data) {
+ if (GS(temp_data->name) == ID_CU_LEGACY) {
+ /* Clear edit mode pointers that were explicitly copied to the temporary curve. */
+ Curve *curve = reinterpret_cast<Curve *>(temp_data);
+ curve->editfont = nullptr;
+ curve->editnurb = nullptr;
+ }
BKE_id_free(nullptr, temp_data);
}
diff --git a/source/blender/blenkernel/intern/mesh_evaluate.cc b/source/blender/blenkernel/intern/mesh_evaluate.cc
index 7d26262a504..9dba8eab340 100644
--- a/source/blender/blenkernel/intern/mesh_evaluate.cc
+++ b/source/blender/blenkernel/intern/mesh_evaluate.cc
@@ -22,15 +22,17 @@
#include "BLI_math.h"
#include "BLI_span.hh"
#include "BLI_utildefines.h"
+#include "BLI_virtual_array.hh"
#include "BKE_customdata.h"
+#include "BKE_attribute.hh"
#include "BKE_mesh.h"
#include "BKE_multires.h"
-using blender::IndexRange;
using blender::MutableSpan;
using blender::Span;
+using blender::VArray;
/* -------------------------------------------------------------------- */
/** \name Polygon Calculations
@@ -732,75 +734,90 @@ void BKE_mesh_polygons_flip(MPoly *mpoly, MLoop *mloop, CustomData *ldata, int t
/** \name Mesh Flag Flushing
* \{ */
-void BKE_mesh_flush_hidden_from_verts_ex(const MVert *mvert,
- const MLoop *mloop,
- MEdge *medge,
- const int totedge,
- MPoly *mpoly,
- const int totpoly)
+void BKE_mesh_flush_hidden_from_verts(Mesh *me)
{
- int i, j;
+ using namespace blender;
+ using namespace blender::bke;
+ MutableAttributeAccessor attributes = mesh_attributes_for_write(*me);
- for (i = 0; i < totedge; i++) {
- MEdge *e = &medge[i];
- if (mvert[e->v1].flag & ME_HIDE || mvert[e->v2].flag & ME_HIDE) {
- e->flag |= ME_HIDE;
- }
- else {
- e->flag &= ~ME_HIDE;
- }
+ const VArray<bool> hide_vert = attributes.lookup_or_default<bool>(
+ ".hide_vert", ATTR_DOMAIN_POINT, false);
+ if (hide_vert.is_single() && !hide_vert.get_internal_single()) {
+ attributes.remove(".hide_edge");
+ attributes.remove(".hide_poly");
+ return;
}
- for (i = 0; i < totpoly; i++) {
- MPoly *p = &mpoly[i];
- p->flag &= (char)~ME_HIDE;
- for (j = 0; j < p->totloop; j++) {
- if (mvert[mloop[p->loopstart + j].v].flag & ME_HIDE) {
- p->flag |= ME_HIDE;
- }
- }
+ const VArraySpan<bool> hide_vert_span{hide_vert};
+ const Span<MEdge> edges(me->medge, me->totedge);
+ const Span<MPoly> polys(me->mpoly, me->totpoly);
+ const Span<MLoop> loops(me->mloop, me->totloop);
+
+ /* Hide edges when either of their vertices are hidden. */
+ SpanAttributeWriter<bool> hide_edge = attributes.lookup_or_add_for_write_only_span<bool>(
+ ".hide_edge", ATTR_DOMAIN_EDGE);
+ for (const int i : edges.index_range()) {
+ const MEdge &edge = edges[i];
+ hide_edge.span[i] = hide_vert_span[edge.v1] || hide_vert_span[edge.v2];
}
-}
-void BKE_mesh_flush_hidden_from_verts(Mesh *me)
-{
- BKE_mesh_flush_hidden_from_verts_ex(
- me->mvert, me->mloop, me->medge, me->totedge, me->mpoly, me->totpoly);
+ hide_edge.finish();
+
+ /* Hide polygons when any of their vertices are hidden. */
+ SpanAttributeWriter<bool> hide_poly = attributes.lookup_or_add_for_write_only_span<bool>(
+ ".hide_poly", ATTR_DOMAIN_FACE);
+ for (const int i : polys.index_range()) {
+ const MPoly &poly = polys[i];
+ const Span<MLoop> poly_loops = loops.slice(poly.loopstart, poly.totloop);
+ hide_poly.span[i] = std::any_of(poly_loops.begin(), poly_loops.end(), [&](const MLoop &loop) {
+ return hide_vert_span[loop.v];
+ });
+ }
+ hide_poly.finish();
}
-void BKE_mesh_flush_hidden_from_polys_ex(MVert *mvert,
- const MLoop *mloop,
- MEdge *medge,
- const int UNUSED(totedge),
- const MPoly *mpoly,
- const int totpoly)
+void BKE_mesh_flush_hidden_from_polys(Mesh *me)
{
- int i = totpoly;
- for (const MPoly *mp = mpoly; i--; mp++) {
- if (mp->flag & ME_HIDE) {
- const MLoop *ml;
- int j = mp->totloop;
- for (ml = &mloop[mp->loopstart]; j--; ml++) {
- mvert[ml->v].flag |= ME_HIDE;
- medge[ml->e].flag |= ME_HIDE;
+ using namespace blender;
+ using namespace blender::bke;
+ MutableAttributeAccessor attributes = mesh_attributes_for_write(*me);
+
+ const VArray<bool> hide_poly = attributes.lookup_or_default<bool>(
+ ".hide_poly", ATTR_DOMAIN_FACE, false);
+ if (hide_poly.is_single() && !hide_poly.get_internal_single()) {
+ attributes.remove(".hide_vert");
+ attributes.remove(".hide_edge");
+ return;
+ }
+ const VArraySpan<bool> hide_poly_span{hide_poly};
+ const Span<MPoly> polys(me->mpoly, me->totpoly);
+ const Span<MLoop> loops(me->mloop, me->totloop);
+ SpanAttributeWriter<bool> hide_vert = attributes.lookup_or_add_for_write_only_span<bool>(
+ ".hide_vert", ATTR_DOMAIN_POINT);
+ SpanAttributeWriter<bool> hide_edge = attributes.lookup_or_add_for_write_only_span<bool>(
+ ".hide_edge", ATTR_DOMAIN_EDGE);
+
+ /* Hide all edges or vertices connected to hidden polygons. */
+ for (const int i : polys.index_range()) {
+ if (hide_poly_span[i]) {
+ const MPoly &poly = polys[i];
+ for (const MLoop &loop : loops.slice(poly.loopstart, poly.totloop)) {
+ hide_vert.span[loop.v] = true;
+ hide_edge.span[loop.e] = true;
}
}
}
-
- i = totpoly;
- for (const MPoly *mp = mpoly; i--; mp++) {
- if ((mp->flag & ME_HIDE) == 0) {
- const MLoop *ml;
- int j = mp->totloop;
- for (ml = &mloop[mp->loopstart]; j--; ml++) {
- mvert[ml->v].flag &= (char)~ME_HIDE;
- medge[ml->e].flag &= (short)~ME_HIDE;
+ /* Unhide vertices and edges connected to visible polygons. */
+ for (const int i : polys.index_range()) {
+ if (!hide_poly_span[i]) {
+ const MPoly &poly = polys[i];
+ for (const MLoop &loop : loops.slice(poly.loopstart, poly.totloop)) {
+ hide_vert.span[loop.v] = false;
+ hide_edge.span[loop.e] = false;
}
}
}
-}
-void BKE_mesh_flush_hidden_from_polys(Mesh *me)
-{
- BKE_mesh_flush_hidden_from_polys_ex(
- me->mvert, me->mloop, me->medge, me->totedge, me->mpoly, me->totpoly);
+
+ hide_vert.finish();
+ hide_edge.finish();
}
void BKE_mesh_flush_select_from_polys_ex(MVert *mvert,
@@ -848,11 +865,13 @@ void BKE_mesh_flush_select_from_polys(Mesh *me)
static void mesh_flush_select_from_verts(const Span<MVert> verts,
const Span<MLoop> loops,
+ const VArray<bool> &hide_edge,
+ const VArray<bool> &hide_poly,
MutableSpan<MEdge> edges,
MutableSpan<MPoly> polys)
{
for (const int i : edges.index_range()) {
- if ((edges[i].flag & ME_HIDE) == 0) {
+ if (!hide_edge[i]) {
MEdge &edge = edges[i];
if ((verts[edge.v1].flag & SELECT) && (verts[edge.v2].flag & SELECT)) {
edge.flag |= SELECT;
@@ -864,7 +883,7 @@ static void mesh_flush_select_from_verts(const Span<MVert> verts,
}
for (const int i : polys.index_range()) {
- if (polys[i].flag & ME_HIDE) {
+ if (hide_poly[i]) {
continue;
}
MPoly &poly = polys[i];
@@ -885,10 +904,14 @@ static void mesh_flush_select_from_verts(const Span<MVert> verts,
void BKE_mesh_flush_select_from_verts(Mesh *me)
{
- mesh_flush_select_from_verts({me->mvert, me->totvert},
- {me->mloop, me->totloop},
- {me->medge, me->totedge},
- {me->mpoly, me->totpoly});
+ const blender::bke::AttributeAccessor attributes = blender::bke::mesh_attributes(*me);
+ mesh_flush_select_from_verts(
+ {me->mvert, me->totvert},
+ {me->mloop, me->totloop},
+ attributes.lookup_or_default<bool>(".hide_edge", ATTR_DOMAIN_EDGE, false),
+ attributes.lookup_or_default<bool>(".hide_poly", ATTR_DOMAIN_FACE, false),
+ {me->medge, me->totedge},
+ {me->mpoly, me->totpoly});
}
/** \} */
diff --git a/source/blender/blenkernel/intern/mesh_legacy_convert.cc b/source/blender/blenkernel/intern/mesh_legacy_convert.cc
index 16fd9cf9de9..00c6e3f2806 100644
--- a/source/blender/blenkernel/intern/mesh_legacy_convert.cc
+++ b/source/blender/blenkernel/intern/mesh_legacy_convert.cc
@@ -18,8 +18,10 @@
#include "BLI_math.h"
#include "BLI_memarena.h"
#include "BLI_polyfill_2d.h"
+#include "BLI_task.hh"
#include "BLI_utildefines.h"
+#include "BKE_attribute.hh"
#include "BKE_customdata.h"
#include "BKE_mesh.h"
#include "BKE_mesh_legacy_convert.h"
@@ -876,6 +878,7 @@ void BKE_mesh_add_mface_layers(CustomData *fdata, CustomData *ldata, int total)
/** \} */
/* -------------------------------------------------------------------- */
+<<<<<<< HEAD
/** \name Bevel Weight Conversion
* \{ */
@@ -929,6 +932,88 @@ void BKE_mesh_legacy_bevel_weight_to_layers(Mesh *mesh)
for (const int i : edges.index_range()) {
weights[i] = edges[i].bweight / 255.0f;
}
+=======
+/** \name Hide Attribute and Legacy Flag Conversion
+ * \{ */
+
+void BKE_mesh_legacy_convert_hide_layers_to_flags(Mesh *mesh)
+{
+ using namespace blender;
+ using namespace blender::bke;
+ const AttributeAccessor attributes = mesh_attributes(*mesh);
+
+ MutableSpan<MVert> verts(mesh->mvert, mesh->totvert);
+ const VArray<bool> hide_vert = attributes.lookup_or_default<bool>(
+ ".hide_vert", ATTR_DOMAIN_POINT, false);
+ threading::parallel_for(verts.index_range(), 4096, [&](IndexRange range) {
+ for (const int i : range) {
+ SET_FLAG_FROM_TEST(verts[i].flag, hide_vert[i], ME_HIDE);
+ }
+ });
+
+ MutableSpan<MEdge> edges(mesh->medge, mesh->totedge);
+ const VArray<bool> hide_edge = attributes.lookup_or_default<bool>(
+ ".hide_edge", ATTR_DOMAIN_EDGE, false);
+ threading::parallel_for(edges.index_range(), 4096, [&](IndexRange range) {
+ for (const int i : range) {
+ SET_FLAG_FROM_TEST(edges[i].flag, hide_edge[i], ME_HIDE);
+ }
+ });
+
+ MutableSpan<MPoly> polys(mesh->mpoly, mesh->totpoly);
+ const VArray<bool> hide_poly = attributes.lookup_or_default<bool>(
+ ".hide_poly", ATTR_DOMAIN_FACE, false);
+ threading::parallel_for(polys.index_range(), 4096, [&](IndexRange range) {
+ for (const int i : range) {
+ SET_FLAG_FROM_TEST(polys[i].flag, hide_poly[i], ME_HIDE);
+ }
+ });
+}
+
+void BKE_mesh_legacy_convert_flags_to_hide_layers(Mesh *mesh)
+{
+ using namespace blender;
+ using namespace blender::bke;
+ MutableAttributeAccessor attributes = mesh_attributes_for_write(*mesh);
+
+ const Span<MVert> verts(mesh->mvert, mesh->totvert);
+ if (std::any_of(
+ verts.begin(), verts.end(), [](const MVert &vert) { return vert.flag & ME_HIDE; })) {
+ SpanAttributeWriter<bool> hide_vert = attributes.lookup_or_add_for_write_only_span<bool>(
+ ".hide_vert", ATTR_DOMAIN_POINT);
+ threading::parallel_for(verts.index_range(), 4096, [&](IndexRange range) {
+ for (const int i : range) {
+ hide_vert.span[i] = verts[i].flag & ME_HIDE;
+ }
+ });
+ hide_vert.finish();
+ }
+
+ const Span<MEdge> edges(mesh->medge, mesh->totedge);
+ if (std::any_of(
+ edges.begin(), edges.end(), [](const MEdge &edge) { return edge.flag & ME_HIDE; })) {
+ SpanAttributeWriter<bool> hide_edge = attributes.lookup_or_add_for_write_only_span<bool>(
+ ".hide_edge", ATTR_DOMAIN_EDGE);
+ threading::parallel_for(edges.index_range(), 4096, [&](IndexRange range) {
+ for (const int i : range) {
+ hide_edge.span[i] = edges[i].flag & ME_HIDE;
+ }
+ });
+ hide_edge.finish();
+ }
+
+ const Span<MPoly> polys(mesh->mpoly, mesh->totpoly);
+ if (std::any_of(
+ polys.begin(), polys.end(), [](const MPoly &poly) { return poly.flag & ME_HIDE; })) {
+ SpanAttributeWriter<bool> hide_poly = attributes.lookup_or_add_for_write_only_span<bool>(
+ ".hide_poly", ATTR_DOMAIN_FACE);
+ threading::parallel_for(polys.index_range(), 4096, [&](IndexRange range) {
+ for (const int i : range) {
+ hide_poly.span[i] = polys[i].flag & ME_HIDE;
+ }
+ });
+ hide_poly.finish();
+>>>>>>> master
}
}
diff --git a/source/blender/blenkernel/intern/mesh_mapping.c b/source/blender/blenkernel/intern/mesh_mapping.c
index 9c4098e2db6..798fe087ea8 100644
--- a/source/blender/blenkernel/intern/mesh_mapping.c
+++ b/source/blender/blenkernel/intern/mesh_mapping.c
@@ -29,6 +29,7 @@
/* ngon version wip, based on BM_uv_vert_map_create */
UvVertMap *BKE_mesh_uv_vert_map_create(const MPoly *mpoly,
+ const bool *hide_poly,
const MLoop *mloop,
const MLoopUV *mloopuv,
uint totpoly,
@@ -51,7 +52,7 @@ UvVertMap *BKE_mesh_uv_vert_map_create(const MPoly *mpoly,
/* generate UvMapVert array */
mp = mpoly;
for (a = 0; a < totpoly; a++, mp++) {
- if (!selected || (!(mp->flag & ME_HIDE) && (mp->flag & ME_FACE_SEL))) {
+ if (!selected || (!(hide_poly && hide_poly[a]) && (mp->flag & ME_FACE_SEL))) {
totuv += mp->totloop;
}
}
@@ -74,7 +75,7 @@ UvVertMap *BKE_mesh_uv_vert_map_create(const MPoly *mpoly,
mp = mpoly;
for (a = 0; a < totpoly; a++, mp++) {
- if (!selected || (!(mp->flag & ME_HIDE) && (mp->flag & ME_FACE_SEL))) {
+ if (!selected || (!(hide_poly && hide_poly[a]) && (mp->flag & ME_FACE_SEL))) {
float(*tf_uv)[2] = NULL;
if (use_winding) {
diff --git a/source/blender/blenkernel/intern/mesh_tangent.c b/source/blender/blenkernel/intern/mesh_tangent.c
index a677a0d6ebb..1772419e1f3 100644
--- a/source/blender/blenkernel/intern/mesh_tangent.c
+++ b/source/blender/blenkernel/intern/mesh_tangent.c
@@ -716,7 +716,7 @@ void BKE_mesh_calc_loop_tangents(Mesh *me_eval,
{
BKE_mesh_runtime_looptri_ensure(me_eval);
- /* TODO(campbell): store in Mesh.runtime to avoid recalculation. */
+ /* TODO(@campbellbarton): store in Mesh.runtime to avoid recalculation. */
short tangent_mask = 0;
BKE_mesh_calc_loop_tangent_ex(me_eval->mvert,
me_eval->mpoly,
diff --git a/source/blender/blenkernel/intern/modifier.c b/source/blender/blenkernel/intern/modifier.c
index 01eb4970f7e..60d6b51594a 100644
--- a/source/blender/blenkernel/intern/modifier.c
+++ b/source/blender/blenkernel/intern/modifier.c
@@ -142,7 +142,8 @@ static ModifierData *modifier_allocate_and_init(int type)
md->type = type;
md->mode = eModifierMode_Realtime | eModifierMode_Render;
md->flag = eModifierFlag_OverrideLibrary_Local;
- md->ui_expand_flag = 1; /* Only open the main panel at the beginning, not the sub-panels. */
+ /* Only open the main panel at the beginning, not the sub-panels. */
+ md->ui_expand_flag = UI_PANEL_DATA_EXPAND_ROOT;
if (mti->flags & eModifierTypeFlag_EnableInEditmode) {
md->mode |= eModifierMode_Editmode;
diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc
index ba473b29474..d50b8662f82 100644
--- a/source/blender/blenkernel/intern/node.cc
+++ b/source/blender/blenkernel/intern/node.cc
@@ -404,7 +404,7 @@ static void node_foreach_path(ID *id, BPathForeachPathData *bpath_data)
}
}
-static ID *node_owner_get(Main *bmain, ID *id)
+static ID *node_owner_get(Main *bmain, ID *id, ID *owner_id_hint)
{
if ((id->flag & LIB_EMBEDDED_DATA) == 0) {
return id;
@@ -412,6 +412,12 @@ static ID *node_owner_get(Main *bmain, ID *id)
/* TODO: Sort this NO_MAIN or not for embedded node trees. See T86119. */
// BLI_assert((id->tag & LIB_TAG_NO_MAIN) == 0);
+ bNodeTree *ntree = reinterpret_cast<bNodeTree *>(id);
+
+ if (owner_id_hint != nullptr && ntreeFromID(owner_id_hint) == ntree) {
+ return owner_id_hint;
+ }
+
ListBase *lists[] = {&bmain->materials,
&bmain->lights,
&bmain->worlds,
@@ -421,7 +427,6 @@ static ID *node_owner_get(Main *bmain, ID *id)
&bmain->simulations,
nullptr};
- bNodeTree *ntree = (bNodeTree *)id;
for (int i = 0; lists[i] != nullptr; i++) {
LISTBASE_FOREACH (ID *, id_iter, lists[i]) {
if (ntreeFromID(id_iter) == ntree) {
@@ -3047,7 +3052,9 @@ void nodeRemoveNode(Main *bmain, bNodeTree *ntree, bNode *node, bool do_id_user)
}
}
- if (node_has_id) {
+ /* Also update relations for the scene time node, which causes a dependency
+ * on time that users expect to be removed when the node is removed. */
+ if (node_has_id || node->type == GEO_NODE_INPUT_SCENE_TIME) {
if (bmain != nullptr) {
DEG_relations_tag_update(bmain);
}
diff --git a/source/blender/blenkernel/intern/object_dupli.cc b/source/blender/blenkernel/intern/object_dupli.cc
index 407a2c8955c..cc3a8b5bb0e 100644
--- a/source/blender/blenkernel/intern/object_dupli.cc
+++ b/source/blender/blenkernel/intern/object_dupli.cc
@@ -29,6 +29,7 @@
#include "DNA_pointcloud_types.h"
#include "DNA_scene_types.h"
#include "DNA_vfont_types.h"
+#include "DNA_volume_types.h"
#include "BKE_collection.h"
#include "BKE_duplilist.h"
@@ -164,10 +165,8 @@ static bool copy_dupli_context(
*
* \param mat: is transform of the object relative to current context (including #Object.obmat).
*/
-static DupliObject *make_dupli(const DupliContext *ctx,
- Object *ob,
- const float mat[4][4],
- int index)
+static DupliObject *make_dupli(
+ const DupliContext *ctx, Object *ob, const ID *object_data, const float mat[4][4], int index)
{
DupliObject *dob;
int i;
@@ -182,7 +181,7 @@ static DupliObject *make_dupli(const DupliContext *ctx,
}
dob->ob = ob;
- dob->ob_data = (ID *)ob->data;
+ dob->ob_data = const_cast<ID *>(object_data);
mul_m4_m4m4(dob->mat, (float(*)[4])ctx->space_mat, mat);
dob->type = ctx->gen->type;
@@ -226,6 +225,14 @@ static DupliObject *make_dupli(const DupliContext *ctx,
return dob;
}
+static DupliObject *make_dupli(const DupliContext *ctx,
+ Object *ob,
+ const float mat[4][4],
+ int index)
+{
+ return make_dupli(ctx, ob, static_cast<ID *>(ob->data), mat, index);
+}
+
/**
* Recursive dupli-objects.
*
@@ -777,28 +784,24 @@ static void make_duplis_geometry_set_impl(const DupliContext *ctx,
int component_index = 0;
if (ctx->object->type != OB_MESH || geometry_set_is_instance) {
if (const Mesh *mesh = geometry_set.get_mesh_for_read()) {
- DupliObject *dupli = make_dupli(ctx, ctx->object, parent_transform, component_index++);
- dupli->ob_data = (ID *)mesh;
+ make_dupli(ctx, ctx->object, &mesh->id, parent_transform, component_index++);
}
}
if (ctx->object->type != OB_VOLUME || geometry_set_is_instance) {
if (const Volume *volume = geometry_set.get_volume_for_read()) {
- DupliObject *dupli = make_dupli(ctx, ctx->object, parent_transform, component_index++);
- dupli->ob_data = (ID *)volume;
+ make_dupli(ctx, ctx->object, &volume->id, parent_transform, component_index++);
}
}
if (!ELEM(ctx->object->type, OB_CURVES_LEGACY, OB_FONT, OB_CURVES) || geometry_set_is_instance) {
if (const CurveComponent *component = geometry_set.get_component_for_read<CurveComponent>()) {
if (const Curve *curve = component->get_curve_for_render()) {
- DupliObject *dupli = make_dupli(ctx, ctx->object, parent_transform, component_index++);
- dupli->ob_data = (ID *)curve;
+ make_dupli(ctx, ctx->object, &curve->id, parent_transform, component_index++);
}
}
}
if (ctx->object->type != OB_POINTCLOUD || geometry_set_is_instance) {
if (const PointCloud *pointcloud = geometry_set.get_pointcloud_for_read()) {
- DupliObject *dupli = make_dupli(ctx, ctx->object, parent_transform, component_index++);
- dupli->ob_data = (ID *)pointcloud;
+ make_dupli(ctx, ctx->object, &pointcloud->id, parent_transform, component_index++);
}
}
const bool creates_duplis_for_components = component_index >= 1;
diff --git a/source/blender/blenkernel/intern/object_update.c b/source/blender/blenkernel/intern/object_update.c
index 1f7df2773dc..99c4d92d284 100644
--- a/source/blender/blenkernel/intern/object_update.c
+++ b/source/blender/blenkernel/intern/object_update.c
@@ -281,45 +281,39 @@ void BKE_object_eval_uber_transform(Depsgraph *UNUSED(depsgraph), Object *UNUSED
{
}
-void BKE_object_data_batch_cache_dirty_tag(ID *object_data)
+void BKE_object_batch_cache_dirty_tag(Object *ob)
{
- switch (GS(object_data->name)) {
- case ID_ME:
- BKE_mesh_batch_cache_dirty_tag((struct Mesh *)object_data, BKE_MESH_BATCH_DIRTY_ALL);
+ switch (ob->type) {
+ case OB_MESH:
+ BKE_mesh_batch_cache_dirty_tag((struct Mesh *)ob->data, BKE_MESH_BATCH_DIRTY_ALL);
break;
- case ID_LT:
- BKE_lattice_batch_cache_dirty_tag((struct Lattice *)object_data,
- BKE_LATTICE_BATCH_DIRTY_ALL);
+ case OB_LATTICE:
+ BKE_lattice_batch_cache_dirty_tag((struct Lattice *)ob->data, BKE_LATTICE_BATCH_DIRTY_ALL);
break;
- case ID_CU_LEGACY:
- BKE_curve_batch_cache_dirty_tag((struct Curve *)object_data, BKE_CURVE_BATCH_DIRTY_ALL);
+ case OB_CURVES_LEGACY:
+ BKE_curve_batch_cache_dirty_tag((struct Curve *)ob->data, BKE_CURVE_BATCH_DIRTY_ALL);
break;
- case ID_MB:
- BKE_mball_batch_cache_dirty_tag((struct MetaBall *)object_data, BKE_MBALL_BATCH_DIRTY_ALL);
+ case OB_MBALL:
+ BKE_mball_batch_cache_dirty_tag((struct MetaBall *)ob->data, BKE_MBALL_BATCH_DIRTY_ALL);
break;
- case ID_GD:
- BKE_gpencil_batch_cache_dirty_tag((struct bGPdata *)object_data);
+ case OB_GPENCIL:
+ BKE_gpencil_batch_cache_dirty_tag((struct bGPdata *)ob->data);
break;
- case ID_CV:
- BKE_curves_batch_cache_dirty_tag((struct Curves *)object_data, BKE_CURVES_BATCH_DIRTY_ALL);
+ case OB_CURVES:
+ BKE_curves_batch_cache_dirty_tag((struct Curves *)ob->data, BKE_CURVES_BATCH_DIRTY_ALL);
break;
- case ID_PT:
- BKE_pointcloud_batch_cache_dirty_tag((struct PointCloud *)object_data,
+ case OB_POINTCLOUD:
+ BKE_pointcloud_batch_cache_dirty_tag((struct PointCloud *)ob->data,
BKE_POINTCLOUD_BATCH_DIRTY_ALL);
break;
- case ID_VO:
- BKE_volume_batch_cache_dirty_tag((struct Volume *)object_data, BKE_VOLUME_BATCH_DIRTY_ALL);
+ case OB_VOLUME:
+ BKE_volume_batch_cache_dirty_tag((struct Volume *)ob->data, BKE_VOLUME_BATCH_DIRTY_ALL);
break;
default:
break;
}
}
-void BKE_object_batch_cache_dirty_tag(Object *ob)
-{
- BKE_object_data_batch_cache_dirty_tag(ob->data);
-}
-
void BKE_object_eval_uber_data(Depsgraph *depsgraph, Scene *scene, Object *ob)
{
DEG_debug_print_eval(depsgraph, __func__, ob->id.name, ob);
diff --git a/source/blender/blenkernel/intern/ocean.c b/source/blender/blenkernel/intern/ocean.c
index dec9a594938..cd1f24fee37 100644
--- a/source/blender/blenkernel/intern/ocean.c
+++ b/source/blender/blenkernel/intern/ocean.c
@@ -1385,9 +1385,8 @@ void BKE_ocean_bake(struct Ocean *o,
void (*update_cb)(void *, float progress, int *cancel),
void *update_cb_data)
{
- /* NOTE(campbell): some of these values remain uninitialized unless certain options
- * are enabled, take care that BKE_ocean_eval_ij() initializes a member
- * before use. */
+ /* NOTE(@campbellbarton): some of these values remain uninitialized unless certain options
+ * are enabled, take care that #BKE_ocean_eval_ij() initializes a member before use. */
OceanResult ocr;
ImageFormatData imf = {0};
@@ -1441,7 +1440,7 @@ void BKE_ocean_bake(struct Ocean *o,
rgb_to_rgba_unit_alpha(&ibuf_disp->rect_float[4 * (res_x * y + x)], ocr.disp);
if (o->_do_jacobian) {
- /* TODO(campbell): cleanup unused code. */
+ /* TODO(@campbellbarton): cleanup unused code. */
float /* r, */ /* UNUSED */ pr = 0.0f, foam_result;
float neg_disp, neg_eplus;
diff --git a/source/blender/blenkernel/intern/outliner_treehash.c b/source/blender/blenkernel/intern/outliner_treehash.c
index 03c327bec2f..09e2baf2be1 100644
--- a/source/blender/blenkernel/intern/outliner_treehash.c
+++ b/source/blender/blenkernel/intern/outliner_treehash.c
@@ -21,11 +21,20 @@
typedef struct TseGroup {
TreeStoreElem **elems;
+ /* Index of last used #TreeStoreElem item, to speed up search for another one. */
int lastused;
+ /* Counter used to reduce the amount of 'rests' of `lastused` index, otherwise search for unused
+ * item is exponential and becomes critically slow when there are a lot of items in the group. */
+ int lastused_reset_count;
+ /* Number of items currently in use. */
int size;
+ /* Number of items currently allocated. */
int allocated;
} TseGroup;
+/* Only allow reset of #TseGroup.lastused counter to 0 once every 1k search. */
+#define TSEGROUP_LASTUSED_RESET_VALUE 10000
+
/* Allocate structure for TreeStoreElements;
* Most of elements in treestore have no duplicates,
* so there is no need to preallocate memory for more than one pointer */
@@ -47,6 +56,7 @@ static void tse_group_add_element(TseGroup *tse_group, TreeStoreElem *elem)
sizeof(TreeStoreElem *) * tse_group->allocated);
}
tse_group->elems[tse_group->size] = elem;
+ tse_group->lastused = tse_group->size;
tse_group->size++;
}
@@ -136,6 +146,7 @@ void BKE_outliner_treehash_clear_used(void *treehash)
GHASH_ITER (gh_iter, treehash) {
TseGroup *group = BLI_ghashIterator_getValue(&gh_iter);
group->lastused = 0;
+ group->lastused_reset_count = 0;
}
}
@@ -157,7 +168,6 @@ void BKE_outliner_treehash_add_element(void *treehash, TreeStoreElem *elem)
*val_p = tse_group_create();
}
group = *val_p;
- group->lastused = 0;
tse_group_add_element(group, elem);
}
@@ -204,7 +214,15 @@ TreeStoreElem *BKE_outliner_treehash_lookup_unused(void *treehash,
int offset = group->lastused;
for (int i = 0; i < size; i++, offset++) {
+ /* Once at the end of the array of items, in most cases it just means that all items are
+ * used, so only check the whole array once every TSEGROUP_LASTUSED_RESET_VALUE times. */
if (offset >= size) {
+ if (LIKELY(group->lastused_reset_count <= TSEGROUP_LASTUSED_RESET_VALUE)) {
+ group->lastused_reset_count++;
+ group->lastused = group->size - 1;
+ break;
+ }
+ group->lastused_reset_count = 0;
offset = 0;
}
diff --git a/source/blender/blenkernel/intern/paint.c b/source/blender/blenkernel/intern/paint.c
index b540357ce49..922ea45703d 100644
--- a/source/blender/blenkernel/intern/paint.c
+++ b/source/blender/blenkernel/intern/paint.c
@@ -1243,11 +1243,13 @@ void BKE_paint_blend_read_lib(BlendLibReader *reader, Scene *sce, Paint *p)
}
}
-bool paint_is_face_hidden(const MLoopTri *lt, const MVert *mvert, const MLoop *mloop)
+bool paint_is_face_hidden(const MLoopTri *lt, const bool *hide_vert, const MLoop *mloop)
{
- return ((mvert[mloop[lt->tri[0]].v].flag & ME_HIDE) ||
- (mvert[mloop[lt->tri[1]].v].flag & ME_HIDE) ||
- (mvert[mloop[lt->tri[2]].v].flag & ME_HIDE));
+ if (!hide_vert) {
+ return false;
+ }
+ return ((hide_vert[mloop[lt->tri[0]].v]) || (hide_vert[mloop[lt->tri[1]].v]) ||
+ (hide_vert[mloop[lt->tri[2]].v]));
}
bool paint_is_grid_face_hidden(const uint *grid_hidden, int gridsize, int x, int y)
@@ -2068,9 +2070,11 @@ void BKE_sculpt_face_sets_ensure_from_base_mesh_visibility(Mesh *mesh)
}
int *face_sets = CustomData_get_layer(&mesh->pdata, CD_SCULPT_FACE_SETS);
+ const bool *hide_poly = (const bool *)CustomData_get_layer_named(
+ &mesh->pdata, CD_PROP_BOOL, ".hide_poly");
for (int i = 0; i < mesh->totpoly; i++) {
- if (!(mesh->mpoly[i].flag & ME_HIDE)) {
+ if (!(hide_poly && hide_poly[i])) {
continue;
}
@@ -2095,9 +2099,13 @@ void BKE_sculpt_sync_face_sets_visibility_to_base_mesh(Mesh *mesh)
return;
}
+ bool *hide_poly = (bool *)CustomData_get_layer_named(&mesh->pdata, CD_PROP_BOOL, ".hide_poly");
+ if (!hide_poly) {
+ return;
+ }
+
for (int i = 0; i < mesh->totpoly; i++) {
- const bool is_face_set_visible = face_sets[i] >= 0;
- SET_FLAG_FROM_TEST(mesh->mpoly[i].flag, !is_face_set_visible, ME_HIDE);
+ hide_poly[i] = face_sets[i] < 0;
}
BKE_mesh_flush_hidden_from_polys(mesh);
diff --git a/source/blender/blenkernel/intern/particle.c b/source/blender/blenkernel/intern/particle.c
index 2471d3baa59..85a8d6c817f 100644
--- a/source/blender/blenkernel/intern/particle.c
+++ b/source/blender/blenkernel/intern/particle.c
@@ -5413,8 +5413,8 @@ void BKE_particle_system_blend_read_lib(BlendLibReader *reader,
BLO_read_id_address(reader, id->lib, &psys->target_ob);
if (psys->clmd) {
- /* XXX(campbell): from reading existing code this seems correct but intended usage of
- * pointcache /w cloth should be added in 'ParticleSystem'. */
+ /* XXX(@campbellbarton): from reading existing code this seems correct but intended usage
+ * of pointcache /w cloth should be added in 'ParticleSystem'. */
psys->clmd->point_cache = psys->pointcache;
psys->clmd->ptcaches.first = psys->clmd->ptcaches.last = NULL;
BLO_read_id_address(reader, id->lib, &psys->clmd->coll_parms->group);
diff --git a/source/blender/blenkernel/intern/particle_system.c b/source/blender/blenkernel/intern/particle_system.c
index 4a8f029beee..e9bbcea241e 100644
--- a/source/blender/blenkernel/intern/particle_system.c
+++ b/source/blender/blenkernel/intern/particle_system.c
@@ -969,7 +969,7 @@ void psys_get_birth_coords(
float tmat[3][3];
/* NOTE: utan_local is not taken from 'utan', we calculate from rot_vec/vtan. */
- /* NOTE(campbell): it looks like rotation phase may be applied twice
+ /* NOTE(@campbellbarton): it looks like rotation phase may be applied twice
* (once with vtan, again below) however this isn't the case. */
float *rot_vec_local = tmat[0];
float *vtan_local = tmat[1];
diff --git a/source/blender/blenkernel/intern/pbvh.c b/source/blender/blenkernel/intern/pbvh.c
index dae9788d21c..4e6418942be 100644
--- a/source/blender/blenkernel/intern/pbvh.c
+++ b/source/blender/blenkernel/intern/pbvh.c
@@ -287,7 +287,7 @@ static void build_mesh_leaf_node(PBVH *pbvh, PBVHNode *node)
}
if (has_visible == false) {
- if (!paint_is_face_hidden(lt, pbvh->verts, pbvh->mloop)) {
+ if (!paint_is_face_hidden(lt, pbvh->hide_vert, pbvh->mloop)) {
has_visible = true;
}
}
@@ -562,6 +562,7 @@ void BKE_pbvh_build_mesh(PBVH *pbvh,
pbvh->verts = verts;
BKE_mesh_vertex_normals_ensure(mesh);
pbvh->vert_normals = BKE_mesh_vertex_normals_for_write(mesh);
+ pbvh->hide_vert = (bool *)CustomData_get_layer_named(&mesh->vdata, CD_PROP_BOOL, ".hide_vert");
pbvh->vert_bitmap = MEM_calloc_arrayN(totvert, sizeof(bool), "bvh->vert_bitmap");
pbvh->totvert = totvert;
pbvh->leaf_limit = LEAF_LIMIT;
@@ -1316,7 +1317,6 @@ static void pbvh_update_draw_buffer_cb(void *__restrict userdata,
case PBVH_FACES:
node->draw_buffers = GPU_pbvh_mesh_buffers_build(
pbvh->mesh,
- pbvh->verts,
pbvh->looptri,
CustomData_get_layer(pbvh->pdata, CD_SCULPT_FACE_SETS),
node->prim_indices,
@@ -1590,9 +1590,12 @@ static void pbvh_faces_node_visibility_update(PBVH *pbvh, PBVHNode *node)
BKE_pbvh_node_num_verts(pbvh, node, NULL, &totvert);
BKE_pbvh_node_get_verts(pbvh, node, &vert_indices, &mvert);
+ if (pbvh->hide_vert == NULL) {
+ BKE_pbvh_node_fully_hidden_set(node, false);
+ return;
+ }
for (i = 0; i < totvert; i++) {
- MVert *v = &mvert[vert_indices[i]];
- if (!(v->flag & ME_HIDE)) {
+ if (!(pbvh->hide_vert[vert_indices[i]])) {
BKE_pbvh_node_fully_hidden_set(node, false);
return;
}
@@ -2291,7 +2294,7 @@ static bool pbvh_faces_node_raycast(PBVH *pbvh,
const MLoopTri *lt = &pbvh->looptri[faces[i]];
const int *face_verts = node->face_vert_indices[i];
- if (pbvh->respect_hide && paint_is_face_hidden(lt, vert, mloop)) {
+ if (pbvh->respect_hide && paint_is_face_hidden(lt, pbvh->hide_vert, mloop)) {
continue;
}
@@ -2600,7 +2603,7 @@ static bool pbvh_faces_node_nearest_to_ray(PBVH *pbvh,
const MLoopTri *lt = &pbvh->looptri[faces[i]];
const int *face_verts = node->face_vert_indices[i];
- if (pbvh->respect_hide && paint_is_face_hidden(lt, vert, mloop)) {
+ if (pbvh->respect_hide && paint_is_face_hidden(lt, pbvh->hide_vert, mloop)) {
continue;
}
@@ -3127,6 +3130,7 @@ void pbvh_vertex_iter_init(PBVH *pbvh, PBVHNode *node, PBVHVertexIter *vi, int m
vi->mask = NULL;
if (pbvh->header.type == PBVH_FACES) {
vi->vert_normals = pbvh->vert_normals;
+ vi->hide_vert = pbvh->hide_vert;
vi->vmask = CustomData_get_layer(pbvh->vdata, CD_PAINT_MASK);
}
@@ -3207,6 +3211,27 @@ const float (*BKE_pbvh_get_vert_normals(const PBVH *pbvh))[3]
return pbvh->vert_normals;
}
+const bool *BKE_pbvh_get_vert_hide(const PBVH *pbvh)
+{
+ BLI_assert(pbvh->header.type == PBVH_FACES);
+ return pbvh->hide_vert;
+}
+
+bool *BKE_pbvh_get_vert_hide_for_write(PBVH *pbvh)
+{
+ BLI_assert(pbvh->header.type == PBVH_FACES);
+ if (pbvh->hide_vert) {
+ return pbvh->hide_vert;
+ }
+ pbvh->hide_vert = CustomData_get_layer_named(&pbvh->mesh->vdata, CD_PROP_BOOL, ".hide_vert");
+ if (pbvh->hide_vert) {
+ return pbvh->hide_vert;
+ }
+ pbvh->hide_vert = (bool *)CustomData_add_layer_named(
+ &pbvh->mesh->vdata, CD_PROP_BOOL, CD_CALLOC, NULL, pbvh->mesh->totvert, ".hide_vert");
+ return pbvh->hide_vert;
+}
+
void BKE_pbvh_subdiv_cgg_set(PBVH *pbvh, SubdivCCG *subdiv_ccg)
{
pbvh->subdiv_ccg = subdiv_ccg;
diff --git a/source/blender/blenkernel/intern/pbvh_intern.h b/source/blender/blenkernel/intern/pbvh_intern.h
index 0edf224e5ff..5babfd3acbe 100644
--- a/source/blender/blenkernel/intern/pbvh_intern.h
+++ b/source/blender/blenkernel/intern/pbvh_intern.h
@@ -144,10 +144,11 @@ struct PBVH {
int leaf_limit;
/* Mesh data */
- const struct Mesh *mesh;
+ struct Mesh *mesh;
/* NOTE: Normals are not `const` because they can be updated for drawing by sculpt code. */
float (*vert_normals)[3];
+ bool *hide_vert;
struct MVert *verts;
const struct MPoly *mpoly;
const struct MLoop *mloop;
diff --git a/source/blender/blenkernel/intern/scene.cc b/source/blender/blenkernel/intern/scene.cc
index aaa6baac1ff..4d41471e1fb 100644
--- a/source/blender/blenkernel/intern/scene.cc
+++ b/source/blender/blenkernel/intern/scene.cc
@@ -2501,7 +2501,7 @@ static bool check_rendered_viewport_visible(Main *bmain)
return false;
}
-/* TODO(campbell): shouldn't we be able to use 'DEG_get_view_layer' here?
+/* TODO(@campbellbarton): shouldn't we be able to use 'DEG_get_view_layer' here?
* Currently this is nullptr on load, so don't. */
static void prepare_mesh_for_viewport_render(Main *bmain, const ViewLayer *view_layer)
{
diff --git a/source/blender/blenkernel/intern/shader_fx.c b/source/blender/blenkernel/intern/shader_fx.c
index 51ebd232978..745bd2a97e6 100644
--- a/source/blender/blenkernel/intern/shader_fx.c
+++ b/source/blender/blenkernel/intern/shader_fx.c
@@ -70,7 +70,8 @@ ShaderFxData *BKE_shaderfx_new(int type)
fx->type = type;
fx->mode = eShaderFxMode_Realtime | eShaderFxMode_Render;
fx->flag = eShaderFxFlag_OverrideLibrary_Local;
- fx->ui_expand_flag = 1; /* Expand only the parent panel by default. */
+ /* Expand only the parent panel by default. */
+ fx->ui_expand_flag = UI_PANEL_DATA_EXPAND_ROOT;
if (fxi->flags & eShaderFxTypeFlag_EnableInEditmode) {
fx->mode |= eShaderFxMode_Editmode;
diff --git a/source/blender/blenkernel/intern/subdiv_converter_mesh.c b/source/blender/blenkernel/intern/subdiv_converter_mesh.c
index 12a5f00a68b..9c6d507d42c 100644
--- a/source/blender/blenkernel/intern/subdiv_converter_mesh.c
+++ b/source/blender/blenkernel/intern/subdiv_converter_mesh.c
@@ -205,7 +205,15 @@ static void precalc_uv_layer(const OpenSubdiv_Converter *converter, const int la
mesh->totloop, sizeof(int), "loop uv vertex index");
}
UvVertMap *uv_vert_map = BKE_mesh_uv_vert_map_create(
- mpoly, mloop, mloopuv, num_poly, num_vert, limit, false, true);
+ mpoly,
+ (const bool *)CustomData_get_layer_named(&mesh->pdata, CD_PROP_BOOL, ".hide_poly"),
+ mloop,
+ mloopuv,
+ num_poly,
+ num_vert,
+ limit,
+ false,
+ true);
/* NOTE: First UV vertex is supposed to be always marked as separate. */
storage->num_uv_coordinates = -1;
for (int vertex_index = 0; vertex_index < num_vert; vertex_index++) {
diff --git a/source/blender/blenkernel/intern/subsurf_ccg.c b/source/blender/blenkernel/intern/subsurf_ccg.c
index 966ef0af1fc..03d61469bd1 100644
--- a/source/blender/blenkernel/intern/subsurf_ccg.c
+++ b/source/blender/blenkernel/intern/subsurf_ccg.c
@@ -284,7 +284,8 @@ static int ss_sync_from_uv(CCGSubSurf *ss,
* UV map in really simple cases with mirror + subsurf, see second part of T44530.
* Also, initially intention is to treat merged vertices from mirror modifier as seams.
* This fixes a very old regression (2.49 was correct here) */
- vmap = BKE_mesh_uv_vert_map_create(mpoly, mloop, mloopuv, totface, totvert, limit, false, true);
+ vmap = BKE_mesh_uv_vert_map_create(
+ mpoly, NULL, mloop, mloopuv, totface, totvert, limit, false, true);
if (!vmap) {
return 0;
}
diff --git a/source/blender/blenlib/BLI_cpp_type.hh b/source/blender/blenlib/BLI_cpp_type.hh
index cc48b456da7..8cf5ead1c7b 100644
--- a/source/blender/blenlib/BLI_cpp_type.hh
+++ b/source/blender/blenlib/BLI_cpp_type.hh
@@ -20,7 +20,7 @@
* cost of longer compile time, a larger binary and the complexity that comes from using
* templates).
* - If the code is not performance sensitive, it usually makes sense to use #CPPType instead.
- * - Sometimes a combination can make sense. Optimized code can be be generated at compile-time for
+ * - Sometimes a combination can make sense. Optimized code can be generated at compile-time for
* some types, while there is a fallback code path using #CPPType for all other types.
* #CPPType::to_static_type allows dispatching between both versions based on the type.
*
diff --git a/source/blender/blenlib/BLI_scanfill.h b/source/blender/blenlib/BLI_scanfill.h
index 04ac7cb05e4..b5b100ac27d 100644
--- a/source/blender/blenlib/BLI_scanfill.h
+++ b/source/blender/blenlib/BLI_scanfill.h
@@ -82,7 +82,7 @@ struct ScanFillEdge *BLI_scanfill_edge_add(ScanFillContext *sf_ctx,
struct ScanFillVert *v2);
enum {
- /* NOTE(campbell): using BLI_SCANFILL_CALC_REMOVE_DOUBLES
+ /* NOTE(@campbellbarton): using #BLI_SCANFILL_CALC_REMOVE_DOUBLES
* Assumes ordered edges, otherwise we risk an eternal loop
* removing double verts. */
BLI_SCANFILL_CALC_REMOVE_DOUBLES = (1 << 1),
diff --git a/source/blender/blenlib/intern/math_geom.c b/source/blender/blenlib/intern/math_geom.c
index e7ccdeab80a..773aac95193 100644
--- a/source/blender/blenlib/intern/math_geom.c
+++ b/source/blender/blenlib/intern/math_geom.c
@@ -1667,8 +1667,8 @@ bool isect_ray_tri_v3(const float ray_origin[3],
float *r_lambda,
float r_uv[2])
{
- /* NOTE(campbell): these values were 0.000001 in 2.4x but for projection snapping on
- * a human head (1BU == 1m), subsurf level 2, this gave many errors. */
+ /* NOTE(@campbellbarton): these values were 0.000001 in 2.4x but for projection snapping on
+ * a human head `(1BU == 1m)`, subdivision-surface level 2, this gave many errors. */
const float epsilon = 0.00000001f;
float p[3], s[3], e1[3], e2[3], q[3];
float a, f, u, v;
@@ -3773,7 +3773,7 @@ void barycentric_weights_v2_quad(const float v1[2],
const float co[2],
float w[4])
{
- /* NOTE(campbell): fabsf() here is not needed for convex quads
+ /* NOTE(@campbellbarton): fabsf() here is not needed for convex quads
* (and not used in #interp_weights_poly_v2).
* But in the case of concave/bow-tie quads for the mask rasterizer it
* gives unreliable results without adding `absf()`. If this becomes an issue for more general
diff --git a/source/blender/blenlib/intern/mesh_boolean.cc b/source/blender/blenlib/intern/mesh_boolean.cc
index 357dba154af..0d8ad1da582 100644
--- a/source/blender/blenlib/intern/mesh_boolean.cc
+++ b/source/blender/blenlib/intern/mesh_boolean.cc
@@ -1675,7 +1675,7 @@ static Edge find_good_sorting_edge(const Vert *testp,
* The algorithm is similar to the one for find_ambient_cell, except that
* instead of an arbitrary point known to be outside the whole mesh, we
* have a particular point (v) and we just want to determine the patches
- * that that point is between in sorting-around-an-edge order.
+ * that point is between in sorting-around-an-edge order.
*/
static int find_containing_cell(const Vert *v,
int t,
diff --git a/source/blender/blenlib/intern/smallhash.c b/source/blender/blenlib/intern/smallhash.c
index 2d76f662611..8263f8ff34e 100644
--- a/source/blender/blenlib/intern/smallhash.c
+++ b/source/blender/blenlib/intern/smallhash.c
@@ -329,8 +329,7 @@ void **BLI_smallhash_iternew_p(const SmallHash *sh, SmallHashIter *iter, uintptr
/** \name Debugging & Introspection
* \{ */
-/* NOTE(campbell): this was called _print_smhash in knifetool.c
- * it may not be intended for general use. */
+/* NOTE(@campbellbarton): useful for debugging but may not be intended for general use. */
#if 0
void BLI_smallhash_print(SmallHash *sh)
{
diff --git a/source/blender/blenlib/intern/string_utf8.c b/source/blender/blenlib/intern/string_utf8.c
index 0cbf62cce03..93045bd3680 100644
--- a/source/blender/blenlib/intern/string_utf8.c
+++ b/source/blender/blenlib/intern/string_utf8.c
@@ -403,7 +403,7 @@ int BLI_str_utf8_char_width_safe(const char *p)
/* copied from glib's gutf8.c, added 'Err' arg */
-/* NOTE(campbell): glib uses uint for unicode, best we do the same,
+/* NOTE(@campbellbarton): glib uses uint for unicode, best we do the same,
* though we don't typedef it. */
#define UTF8_COMPUTE(Char, Mask, Len, Err) \
diff --git a/source/blender/blenloader/intern/versioning_290.c b/source/blender/blenloader/intern/versioning_290.c
index ff72bfe95b8..c79eb2d530b 100644
--- a/source/blender/blenloader/intern/versioning_290.c
+++ b/source/blender/blenloader/intern/versioning_290.c
@@ -926,7 +926,7 @@ void blo_do_versions_290(FileData *fd, Library *UNUSED(lib), Main *bmain)
for (Object *object = bmain->objects.first; object != NULL; object = object->id.next) {
LISTBASE_FOREACH (ModifierData *, md, &object->modifiers) {
if (md->mode & eModifierMode_Expanded_DEPRECATED) {
- md->ui_expand_flag = 1;
+ md->ui_expand_flag = UI_PANEL_DATA_EXPAND_ROOT;
}
else {
md->ui_expand_flag = 0;
@@ -954,7 +954,7 @@ void blo_do_versions_290(FileData *fd, Library *UNUSED(lib), Main *bmain)
for (Object *object = bmain->objects.first; object != NULL; object = object->id.next) {
LISTBASE_FOREACH (bConstraint *, con, &object->constraints) {
if (con->flag & CONSTRAINT_EXPAND_DEPRECATED) {
- con->ui_expand_flag = 1;
+ con->ui_expand_flag = UI_PANEL_DATA_EXPAND_ROOT;
}
else {
con->ui_expand_flag = 0;
@@ -968,7 +968,7 @@ void blo_do_versions_290(FileData *fd, Library *UNUSED(lib), Main *bmain)
for (Object *object = bmain->objects.first; object != NULL; object = object->id.next) {
LISTBASE_FOREACH (GpencilModifierData *, md, &object->greasepencil_modifiers) {
if (md->mode & eGpencilModifierMode_Expanded_DEPRECATED) {
- md->ui_expand_flag = 1;
+ md->ui_expand_flag = UI_PANEL_DATA_EXPAND_ROOT;
}
else {
md->ui_expand_flag = 0;
@@ -982,7 +982,7 @@ void blo_do_versions_290(FileData *fd, Library *UNUSED(lib), Main *bmain)
for (Object *object = bmain->objects.first; object != NULL; object = object->id.next) {
LISTBASE_FOREACH (ShaderFxData *, fx, &object->shader_fx) {
if (fx->mode & eShaderFxMode_Expanded_DEPRECATED) {
- fx->ui_expand_flag = 1;
+ fx->ui_expand_flag = UI_PANEL_DATA_EXPAND_ROOT;
}
else {
fx->ui_expand_flag = 0;
diff --git a/source/blender/bmesh/intern/bmesh_construct.c b/source/blender/bmesh/intern/bmesh_construct.c
index a7637d2712c..0ee5545527b 100644
--- a/source/blender/bmesh/intern/bmesh_construct.c
+++ b/source/blender/bmesh/intern/bmesh_construct.c
@@ -512,16 +512,24 @@ void BM_mesh_copy_init_customdata_from_mesh_array(BMesh *bm_dst,
for (int i = 0; i < me_src_array_len; i++) {
const Mesh *me_src = me_src_array[i];
if (i == 0) {
- CustomData_copy(&me_src->vdata, &bm_dst->vdata, CD_MASK_BMESH.vmask, CD_CALLOC, 0);
- CustomData_copy(&me_src->edata, &bm_dst->edata, CD_MASK_BMESH.emask, CD_CALLOC, 0);
- CustomData_copy(&me_src->ldata, &bm_dst->ldata, CD_MASK_BMESH.lmask, CD_CALLOC, 0);
- CustomData_copy(&me_src->pdata, &bm_dst->pdata, CD_MASK_BMESH.pmask, CD_CALLOC, 0);
+ CustomData_copy_mesh_to_bmesh(
+ &me_src->vdata, &bm_dst->vdata, CD_MASK_BMESH.vmask, CD_CALLOC, 0);
+ CustomData_copy_mesh_to_bmesh(
+ &me_src->edata, &bm_dst->edata, CD_MASK_BMESH.emask, CD_CALLOC, 0);
+ CustomData_copy_mesh_to_bmesh(
+ &me_src->ldata, &bm_dst->ldata, CD_MASK_BMESH.lmask, CD_CALLOC, 0);
+ CustomData_copy_mesh_to_bmesh(
+ &me_src->pdata, &bm_dst->pdata, CD_MASK_BMESH.pmask, CD_CALLOC, 0);
}
else {
- CustomData_merge(&me_src->vdata, &bm_dst->vdata, CD_MASK_BMESH.vmask, CD_CALLOC, 0);
- CustomData_merge(&me_src->edata, &bm_dst->edata, CD_MASK_BMESH.emask, CD_CALLOC, 0);
- CustomData_merge(&me_src->ldata, &bm_dst->ldata, CD_MASK_BMESH.lmask, CD_CALLOC, 0);
- CustomData_merge(&me_src->pdata, &bm_dst->pdata, CD_MASK_BMESH.pmask, CD_CALLOC, 0);
+ CustomData_merge_mesh_to_bmesh(
+ &me_src->vdata, &bm_dst->vdata, CD_MASK_BMESH.vmask, CD_CALLOC, 0);
+ CustomData_merge_mesh_to_bmesh(
+ &me_src->edata, &bm_dst->edata, CD_MASK_BMESH.emask, CD_CALLOC, 0);
+ CustomData_merge_mesh_to_bmesh(
+ &me_src->ldata, &bm_dst->ldata, CD_MASK_BMESH.lmask, CD_CALLOC, 0);
+ CustomData_merge_mesh_to_bmesh(
+ &me_src->pdata, &bm_dst->pdata, CD_MASK_BMESH.pmask, CD_CALLOC, 0);
}
cd_flag |= me_src->cd_flag;
@@ -714,26 +722,25 @@ BMesh *BM_mesh_copy(BMesh *bm_old)
char BM_vert_flag_from_mflag(const char mflag)
{
- return (((mflag & SELECT) ? BM_ELEM_SELECT : 0) | ((mflag & ME_HIDE) ? BM_ELEM_HIDDEN : 0));
+ return ((mflag & SELECT) ? BM_ELEM_SELECT : 0);
}
char BM_edge_flag_from_mflag(const short mflag)
{
return (((mflag & SELECT) ? BM_ELEM_SELECT : 0) | ((mflag & ME_SEAM) ? BM_ELEM_SEAM : 0) |
((mflag & ME_EDGEDRAW) ? BM_ELEM_DRAW : 0) |
- ((mflag & ME_SHARP) == 0 ? BM_ELEM_SMOOTH : 0) | /* invert */
- ((mflag & ME_HIDE) ? BM_ELEM_HIDDEN : 0));
+ ((mflag & ME_SHARP) == 0 ? BM_ELEM_SMOOTH : 0));
}
char BM_face_flag_from_mflag(const char mflag)
{
return (((mflag & ME_FACE_SEL) ? BM_ELEM_SELECT : 0) |
- ((mflag & ME_SMOOTH) ? BM_ELEM_SMOOTH : 0) | ((mflag & ME_HIDE) ? BM_ELEM_HIDDEN : 0));
+ ((mflag & ME_SMOOTH) ? BM_ELEM_SMOOTH : 0));
}
char BM_vert_flag_to_mflag(BMVert *v)
{
const char hflag = v->head.hflag;
- return (((hflag & BM_ELEM_SELECT) ? SELECT : 0) | ((hflag & BM_ELEM_HIDDEN) ? ME_HIDE : 0));
+ return (((hflag & BM_ELEM_SELECT) ? SELECT : 0));
}
short BM_edge_flag_to_mflag(BMEdge *e)
@@ -743,7 +750,6 @@ short BM_edge_flag_to_mflag(BMEdge *e)
return (((hflag & BM_ELEM_SELECT) ? SELECT : 0) | ((hflag & BM_ELEM_SEAM) ? ME_SEAM : 0) |
((hflag & BM_ELEM_DRAW) ? ME_EDGEDRAW : 0) |
((hflag & BM_ELEM_SMOOTH) == 0 ? ME_SHARP : 0) |
- ((hflag & BM_ELEM_HIDDEN) ? ME_HIDE : 0) |
(BM_edge_is_wire(e) ? ME_LOOSEEDGE : 0) | /* not typical */
ME_EDGERENDER);
}
@@ -752,5 +758,5 @@ char BM_face_flag_to_mflag(BMFace *f)
const char hflag = f->head.hflag;
return (((hflag & BM_ELEM_SELECT) ? ME_FACE_SEL : 0) |
- ((hflag & BM_ELEM_SMOOTH) ? ME_SMOOTH : 0) | ((hflag & BM_ELEM_HIDDEN) ? ME_HIDE : 0));
+ ((hflag & BM_ELEM_SMOOTH) ? ME_SMOOTH : 0));
}
diff --git a/source/blender/bmesh/intern/bmesh_mesh.cc b/source/blender/bmesh/intern/bmesh_mesh.cc
index c16d874e3ec..4f42ce4a470 100644
--- a/source/blender/bmesh/intern/bmesh_mesh.cc
+++ b/source/blender/bmesh/intern/bmesh_mesh.cc
@@ -88,19 +88,19 @@ void BM_mesh_elem_toolflags_ensure(BMesh *bm)
BMVert_OFlag *v_olfag;
BLI_mempool *toolflagpool = bm->vtoolflagpool;
BM_ITER_MESH (v_olfag, &iter, bm, BM_VERTS_OF_MESH) {
- v_olfag->oflags = (BMFlagLayer *)BLI_mempool_calloc(toolflagpool);
+ v_olfag->oflags = static_cast<BMFlagLayer *>(BLI_mempool_calloc(toolflagpool));
}
BMEdge_OFlag *e_olfag;
toolflagpool = bm->etoolflagpool;
BM_ITER_MESH (e_olfag, &iter, bm, BM_EDGES_OF_MESH) {
- e_olfag->oflags = (BMFlagLayer *)BLI_mempool_calloc(toolflagpool);
+ e_olfag->oflags = static_cast<BMFlagLayer *>(BLI_mempool_calloc(toolflagpool));
}
BMFace_OFlag *f_olfag;
toolflagpool = bm->ftoolflagpool;
BM_ITER_MESH (f_olfag, &iter, bm, BM_FACES_OF_MESH) {
- f_olfag->oflags = (BMFlagLayer *)BLI_mempool_calloc(toolflagpool);
+ f_olfag->oflags = static_cast<BMFlagLayer *>(BLI_mempool_calloc(toolflagpool));
}
bm->totflags = 1;
@@ -125,7 +125,7 @@ void BM_mesh_elem_toolflags_clear(BMesh *bm)
BMesh *BM_mesh_create(const BMAllocTemplate *allocsize, const struct BMeshCreateParams *params)
{
/* allocate the structure */
- BMesh *bm = (BMesh *)MEM_callocN(sizeof(BMesh), __func__);
+ BMesh *bm = static_cast<BMesh *>(MEM_callocN(sizeof(BMesh), __func__));
/* allocate the memory pools for the mesh elements */
bm_mempool_init(bm, allocsize, params->use_toolflags);
@@ -262,7 +262,7 @@ void BM_mesh_free(BMesh *bm)
if (bm->py_handle) {
/* keep this out of 'BM_mesh_data_free' because we want python
* to be able to clear the mesh and maintain access. */
- bpy_bm_generic_invalidate((BPy_BMGeneric *)bm->py_handle);
+ bpy_bm_generic_invalidate(static_cast<BPy_BMGeneric *>(bm->py_handle));
bm->py_handle = nullptr;
}
@@ -581,7 +581,8 @@ void BM_mesh_elem_table_ensure(BMesh *bm, const char htype)
if (bm->vtable) {
MEM_freeN(bm->vtable);
}
- bm->vtable = (BMVert **)MEM_mallocN(sizeof(void **) * bm->totvert, "bm->vtable");
+ bm->vtable = static_cast<BMVert **>(
+ MEM_mallocN(sizeof(void **) * bm->totvert, "bm->vtable"));
bm->vtable_tot = bm->totvert;
}
BM_iter_as_array(bm, BM_VERTS_OF_MESH, nullptr, (void **)bm->vtable, bm->totvert);
@@ -594,7 +595,8 @@ void BM_mesh_elem_table_ensure(BMesh *bm, const char htype)
if (bm->etable) {
MEM_freeN(bm->etable);
}
- bm->etable = (BMEdge **)MEM_mallocN(sizeof(void **) * bm->totedge, "bm->etable");
+ bm->etable = static_cast<BMEdge **>(
+ MEM_mallocN(sizeof(void **) * bm->totedge, "bm->etable"));
bm->etable_tot = bm->totedge;
}
BM_iter_as_array(bm, BM_EDGES_OF_MESH, nullptr, (void **)bm->etable, bm->totedge);
@@ -607,7 +609,8 @@ void BM_mesh_elem_table_ensure(BMesh *bm, const char htype)
if (bm->ftable) {
MEM_freeN(bm->ftable);
}
- bm->ftable = (BMFace **)MEM_mallocN(sizeof(void **) * bm->totface, "bm->ftable");
+ bm->ftable = static_cast<BMFace **>(
+ MEM_mallocN(sizeof(void **) * bm->totface, "bm->ftable"));
bm->ftable_tot = bm->totface;
}
BM_iter_as_array(bm, BM_FACES_OF_MESH, nullptr, (void **)bm->ftable, bm->totface);
@@ -647,17 +650,17 @@ void BM_mesh_elem_table_free(BMesh *bm, const char htype)
BMVert *BM_vert_at_index_find(BMesh *bm, const int index)
{
- return (BMVert *)BLI_mempool_findelem(bm->vpool, index);
+ return static_cast<BMVert *>(BLI_mempool_findelem(bm->vpool, index));
}
BMEdge *BM_edge_at_index_find(BMesh *bm, const int index)
{
- return (BMEdge *)BLI_mempool_findelem(bm->epool, index);
+ return static_cast<BMEdge *>(BLI_mempool_findelem(bm->epool, index));
}
BMFace *BM_face_at_index_find(BMesh *bm, const int index)
{
- return (BMFace *)BLI_mempool_findelem(bm->fpool, index);
+ return static_cast<BMFace *>(BLI_mempool_findelem(bm->fpool, index));
}
BMLoop *BM_loop_at_index_find(BMesh *bm, const int index)
@@ -754,16 +757,17 @@ void BM_mesh_remap(BMesh *bm, const uint *vert_idx, const uint *edge_idx, const
/* Make a copy of all vertices. */
verts_pool = bm->vtable;
- verts_copy = (BMVert *)MEM_mallocN(sizeof(BMVert) * totvert, "BM_mesh_remap verts copy");
+ verts_copy = static_cast<BMVert *>(
+ MEM_mallocN(sizeof(BMVert) * totvert, "BM_mesh_remap verts copy"));
void **pyptrs = (cd_vert_pyptr != -1) ?
- (void **)MEM_mallocN(sizeof(void *) * totvert, __func__) :
+ static_cast<void **>(MEM_mallocN(sizeof(void *) * totvert, __func__)) :
nullptr;
for (i = totvert, ve = verts_copy + totvert - 1, vep = verts_pool + totvert - 1; i--;
ve--, vep--) {
*ve = **vep;
// printf("*vep: %p, verts_pool[%d]: %p\n", *vep, i, verts_pool[i]);
if (cd_vert_pyptr != -1) {
- void **pyptr = (void **)BM_ELEM_CD_GET_VOID_P(((BMElem *)ve), cd_vert_pyptr);
+ void **pyptr = static_cast<void **>(BM_ELEM_CD_GET_VOID_P(((BMElem *)ve), cd_vert_pyptr));
pyptrs[i] = *pyptr;
}
}
@@ -781,7 +785,8 @@ void BM_mesh_remap(BMesh *bm, const uint *vert_idx, const uint *edge_idx, const
#endif
BLI_ghash_insert(vptr_map, *vep, new_vep);
if (cd_vert_pyptr != -1) {
- void **pyptr = (void **)BM_ELEM_CD_GET_VOID_P(((BMElem *)new_vep), cd_vert_pyptr);
+ void **pyptr = static_cast<void **>(
+ BM_ELEM_CD_GET_VOID_P(((BMElem *)new_vep), cd_vert_pyptr));
*pyptr = pyptrs[*new_idx];
}
}
@@ -808,15 +813,16 @@ void BM_mesh_remap(BMesh *bm, const uint *vert_idx, const uint *edge_idx, const
/* Make a copy of all vertices. */
edges_pool = bm->etable;
- edges_copy = (BMEdge *)MEM_mallocN(sizeof(BMEdge) * totedge, "BM_mesh_remap edges copy");
+ edges_copy = static_cast<BMEdge *>(
+ MEM_mallocN(sizeof(BMEdge) * totedge, "BM_mesh_remap edges copy"));
void **pyptrs = (cd_edge_pyptr != -1) ?
- (void **)MEM_mallocN(sizeof(void *) * totedge, __func__) :
+ static_cast<void **>(MEM_mallocN(sizeof(void *) * totedge, __func__)) :
nullptr;
for (i = totedge, ed = edges_copy + totedge - 1, edp = edges_pool + totedge - 1; i--;
ed--, edp--) {
*ed = **edp;
if (cd_edge_pyptr != -1) {
- void **pyptr = (void **)BM_ELEM_CD_GET_VOID_P(((BMElem *)ed), cd_edge_pyptr);
+ void **pyptr = static_cast<void **>(BM_ELEM_CD_GET_VOID_P(((BMElem *)ed), cd_edge_pyptr));
pyptrs[i] = *pyptr;
}
}
@@ -834,7 +840,8 @@ void BM_mesh_remap(BMesh *bm, const uint *vert_idx, const uint *edge_idx, const
"mapping edge from %d to %d (%p/%p to %p)\n", i, *new_idx, *edp, edges_pool[i], new_edp);
#endif
if (cd_edge_pyptr != -1) {
- void **pyptr = (void **)BM_ELEM_CD_GET_VOID_P(((BMElem *)new_edp), cd_edge_pyptr);
+ void **pyptr = static_cast<void **>(
+ BM_ELEM_CD_GET_VOID_P(((BMElem *)new_edp), cd_edge_pyptr));
*pyptr = pyptrs[*new_idx];
}
}
@@ -861,15 +868,16 @@ void BM_mesh_remap(BMesh *bm, const uint *vert_idx, const uint *edge_idx, const
/* Make a copy of all vertices. */
faces_pool = bm->ftable;
- faces_copy = (BMFace *)MEM_mallocN(sizeof(BMFace) * totface, "BM_mesh_remap faces copy");
+ faces_copy = static_cast<BMFace *>(
+ MEM_mallocN(sizeof(BMFace) * totface, "BM_mesh_remap faces copy"));
void **pyptrs = (cd_poly_pyptr != -1) ?
- (void **)MEM_mallocN(sizeof(void *) * totface, __func__) :
+ static_cast<void **>(MEM_mallocN(sizeof(void *) * totface, __func__)) :
nullptr;
for (i = totface, fa = faces_copy + totface - 1, fap = faces_pool + totface - 1; i--;
fa--, fap--) {
*fa = **fap;
if (cd_poly_pyptr != -1) {
- void **pyptr = (void **)BM_ELEM_CD_GET_VOID_P(((BMElem *)fa), cd_poly_pyptr);
+ void **pyptr = static_cast<void **>(BM_ELEM_CD_GET_VOID_P(((BMElem *)fa), cd_poly_pyptr));
pyptrs[i] = *pyptr;
}
}
@@ -883,7 +891,8 @@ void BM_mesh_remap(BMesh *bm, const uint *vert_idx, const uint *edge_idx, const
*new_fap = *fa;
BLI_ghash_insert(fptr_map, *fap, new_fap);
if (cd_poly_pyptr != -1) {
- void **pyptr = (void **)BM_ELEM_CD_GET_VOID_P(((BMElem *)new_fap), cd_poly_pyptr);
+ void **pyptr = static_cast<void **>(
+ BM_ELEM_CD_GET_VOID_P(((BMElem *)new_fap), cd_poly_pyptr));
*pyptr = pyptrs[*new_idx];
}
}
@@ -903,7 +912,7 @@ void BM_mesh_remap(BMesh *bm, const uint *vert_idx, const uint *edge_idx, const
BM_ITER_MESH (ve, &iter, bm, BM_VERTS_OF_MESH) {
// printf("Vert e: %p -> %p\n", ve->e, BLI_ghash_lookup(eptr_map, ve->e));
if (ve->e) {
- ve->e = (BMEdge *)BLI_ghash_lookup(eptr_map, ve->e);
+ ve->e = static_cast<BMEdge *>(BLI_ghash_lookup(eptr_map, ve->e));
BLI_assert(ve->e);
}
}
@@ -919,8 +928,8 @@ void BM_mesh_remap(BMesh *bm, const uint *vert_idx, const uint *edge_idx, const
printf("Edge v1: %p -> %p\n", ed->v1, BLI_ghash_lookup(vptr_map, ed->v1));
printf("Edge v2: %p -> %p\n", ed->v2, BLI_ghash_lookup(vptr_map, ed->v2));
#endif
- ed->v1 = (BMVert *)BLI_ghash_lookup(vptr_map, ed->v1);
- ed->v2 = (BMVert *)BLI_ghash_lookup(vptr_map, ed->v2);
+ ed->v1 = static_cast<BMVert *>(BLI_ghash_lookup(vptr_map, ed->v1));
+ ed->v2 = static_cast<BMVert *>(BLI_ghash_lookup(vptr_map, ed->v2));
BLI_assert(ed->v1);
BLI_assert(ed->v2);
}
@@ -939,10 +948,14 @@ void BM_mesh_remap(BMesh *bm, const uint *vert_idx, const uint *edge_idx, const
ed->v2_disk_link.next,
BLI_ghash_lookup(eptr_map, ed->v2_disk_link.next));
#endif
- ed->v1_disk_link.prev = (BMEdge *)BLI_ghash_lookup(eptr_map, ed->v1_disk_link.prev);
- ed->v1_disk_link.next = (BMEdge *)BLI_ghash_lookup(eptr_map, ed->v1_disk_link.next);
- ed->v2_disk_link.prev = (BMEdge *)BLI_ghash_lookup(eptr_map, ed->v2_disk_link.prev);
- ed->v2_disk_link.next = (BMEdge *)BLI_ghash_lookup(eptr_map, ed->v2_disk_link.next);
+ ed->v1_disk_link.prev = static_cast<BMEdge *>(
+ BLI_ghash_lookup(eptr_map, ed->v1_disk_link.prev));
+ ed->v1_disk_link.next = static_cast<BMEdge *>(
+ BLI_ghash_lookup(eptr_map, ed->v1_disk_link.next));
+ ed->v2_disk_link.prev = static_cast<BMEdge *>(
+ BLI_ghash_lookup(eptr_map, ed->v2_disk_link.prev));
+ ed->v2_disk_link.next = static_cast<BMEdge *>(
+ BLI_ghash_lookup(eptr_map, ed->v2_disk_link.next));
BLI_assert(ed->v1_disk_link.prev);
BLI_assert(ed->v1_disk_link.next);
BLI_assert(ed->v2_disk_link.prev);
@@ -956,17 +969,17 @@ void BM_mesh_remap(BMesh *bm, const uint *vert_idx, const uint *edge_idx, const
BM_ITER_ELEM (lo, &iterl, fa, BM_LOOPS_OF_FACE) {
if (vptr_map) {
// printf("Loop v: %p -> %p\n", lo->v, BLI_ghash_lookup(vptr_map, lo->v));
- lo->v = (BMVert *)BLI_ghash_lookup(vptr_map, lo->v);
+ lo->v = static_cast<BMVert *>(BLI_ghash_lookup(vptr_map, lo->v));
BLI_assert(lo->v);
}
if (eptr_map) {
// printf("Loop e: %p -> %p\n", lo->e, BLI_ghash_lookup(eptr_map, lo->e));
- lo->e = (BMEdge *)BLI_ghash_lookup(eptr_map, lo->e);
+ lo->e = static_cast<BMEdge *>(BLI_ghash_lookup(eptr_map, lo->e));
BLI_assert(lo->e);
}
if (fptr_map) {
// printf("Loop f: %p -> %p\n", lo->f, BLI_ghash_lookup(fptr_map, lo->f));
- lo->f = (BMFace *)BLI_ghash_lookup(fptr_map, lo->f);
+ lo->f = static_cast<BMFace *>(BLI_ghash_lookup(fptr_map, lo->f));
BLI_assert(lo->f);
}
}
@@ -975,23 +988,23 @@ void BM_mesh_remap(BMesh *bm, const uint *vert_idx, const uint *edge_idx, const
/* Selection history */
{
BMEditSelection *ese;
- for (ese = (BMEditSelection *)bm->selected.first; ese; ese = ese->next) {
+ for (ese = static_cast<BMEditSelection *>(bm->selected.first); ese; ese = ese->next) {
switch (ese->htype) {
case BM_VERT:
if (vptr_map) {
- ese->ele = (BMElem *)BLI_ghash_lookup(vptr_map, ese->ele);
+ ese->ele = static_cast<BMElem *>(BLI_ghash_lookup(vptr_map, ese->ele));
BLI_assert(ese->ele);
}
break;
case BM_EDGE:
if (eptr_map) {
- ese->ele = (BMElem *)BLI_ghash_lookup(eptr_map, ese->ele);
+ ese->ele = static_cast<BMElem *>(BLI_ghash_lookup(eptr_map, ese->ele));
BLI_assert(ese->ele);
}
break;
case BM_FACE:
if (fptr_map) {
- ese->ele = (BMElem *)BLI_ghash_lookup(fptr_map, ese->ele);
+ ese->ele = static_cast<BMElem *>(BLI_ghash_lookup(fptr_map, ese->ele));
BLI_assert(ese->ele);
}
break;
@@ -1001,7 +1014,7 @@ void BM_mesh_remap(BMesh *bm, const uint *vert_idx, const uint *edge_idx, const
if (fptr_map) {
if (bm->act_face) {
- bm->act_face = (BMFace *)BLI_ghash_lookup(fptr_map, bm->act_face);
+ bm->act_face = static_cast<BMFace *>(BLI_ghash_lookup(fptr_map, bm->act_face));
BLI_assert(bm->act_face);
}
}
@@ -1027,18 +1040,18 @@ void BM_mesh_rebuild(BMesh *bm,
const char remap = (vpool_dst ? BM_VERT : 0) | (epool_dst ? BM_EDGE : 0) |
(lpool_dst ? BM_LOOP : 0) | (fpool_dst ? BM_FACE : 0);
- BMVert **vtable_dst = (remap & BM_VERT) ?
- (BMVert **)MEM_mallocN(bm->totvert * sizeof(BMVert *), __func__) :
- nullptr;
- BMEdge **etable_dst = (remap & BM_EDGE) ?
- (BMEdge **)MEM_mallocN(bm->totedge * sizeof(BMEdge *), __func__) :
- nullptr;
- BMLoop **ltable_dst = (remap & BM_LOOP) ?
- (BMLoop **)MEM_mallocN(bm->totloop * sizeof(BMLoop *), __func__) :
- nullptr;
- BMFace **ftable_dst = (remap & BM_FACE) ?
- (BMFace **)MEM_mallocN(bm->totface * sizeof(BMFace *), __func__) :
- nullptr;
+ BMVert **vtable_dst = (remap & BM_VERT) ? static_cast<BMVert **>(MEM_mallocN(
+ sizeof(BMVert *) * bm->totvert, __func__)) :
+ nullptr;
+ BMEdge **etable_dst = (remap & BM_EDGE) ? static_cast<BMEdge **>(MEM_mallocN(
+ sizeof(BMEdge *) * bm->totedge, __func__)) :
+ nullptr;
+ BMLoop **ltable_dst = (remap & BM_LOOP) ? static_cast<BMLoop **>(MEM_mallocN(
+ sizeof(BMLoop *) * bm->totloop, __func__)) :
+ nullptr;
+ BMFace **ftable_dst = (remap & BM_FACE) ? static_cast<BMFace **>(MEM_mallocN(
+ sizeof(BMFace *) * bm->totface, __func__)) :
+ nullptr;
const bool use_toolflags = params->use_toolflags;
@@ -1047,12 +1060,13 @@ void BM_mesh_rebuild(BMesh *bm,
int index;
BMVert *v_src;
BM_ITER_MESH_INDEX (v_src, &iter, bm, BM_VERTS_OF_MESH, index) {
- BMVert *v_dst = (BMVert *)BLI_mempool_alloc(vpool_dst);
+ BMVert *v_dst = static_cast<BMVert *>(BLI_mempool_alloc(vpool_dst));
memcpy(v_dst, v_src, sizeof(BMVert));
if (use_toolflags) {
- ((BMVert_OFlag *)v_dst)->oflags = bm->vtoolflagpool ? (BMFlagLayer *)BLI_mempool_calloc(
- bm->vtoolflagpool) :
- nullptr;
+ ((BMVert_OFlag *)v_dst)->oflags = bm->vtoolflagpool ?
+ static_cast<BMFlagLayer *>(
+ BLI_mempool_calloc(bm->vtoolflagpool)) :
+ nullptr;
}
vtable_dst[index] = v_dst;
@@ -1065,12 +1079,13 @@ void BM_mesh_rebuild(BMesh *bm,
int index;
BMEdge *e_src;
BM_ITER_MESH_INDEX (e_src, &iter, bm, BM_EDGES_OF_MESH, index) {
- BMEdge *e_dst = (BMEdge *)BLI_mempool_alloc(epool_dst);
+ BMEdge *e_dst = static_cast<BMEdge *>(BLI_mempool_alloc(epool_dst));
memcpy(e_dst, e_src, sizeof(BMEdge));
if (use_toolflags) {
- ((BMEdge_OFlag *)e_dst)->oflags = bm->etoolflagpool ? (BMFlagLayer *)BLI_mempool_calloc(
- bm->etoolflagpool) :
- nullptr;
+ ((BMEdge_OFlag *)e_dst)->oflags = bm->etoolflagpool ?
+ static_cast<BMFlagLayer *>(
+ BLI_mempool_calloc(bm->etoolflagpool)) :
+ nullptr;
}
etable_dst[index] = e_dst;
@@ -1085,12 +1100,13 @@ void BM_mesh_rebuild(BMesh *bm,
BM_ITER_MESH_INDEX (f_src, &iter, bm, BM_FACES_OF_MESH, index) {
if (remap & BM_FACE) {
- BMFace *f_dst = (BMFace *)BLI_mempool_alloc(fpool_dst);
+ BMFace *f_dst = static_cast<BMFace *>(BLI_mempool_alloc(fpool_dst));
memcpy(f_dst, f_src, sizeof(BMFace));
if (use_toolflags) {
- ((BMFace_OFlag *)f_dst)->oflags = bm->ftoolflagpool ? (BMFlagLayer *)BLI_mempool_calloc(
- bm->ftoolflagpool) :
- nullptr;
+ ((BMFace_OFlag *)f_dst)->oflags = bm->ftoolflagpool ?
+ static_cast<BMFlagLayer *>(
+ BLI_mempool_calloc(bm->ftoolflagpool)) :
+ nullptr;
}
ftable_dst[index] = f_dst;
@@ -1102,7 +1118,7 @@ void BM_mesh_rebuild(BMesh *bm,
BMLoop *l_iter_src, *l_first_src;
l_iter_src = l_first_src = BM_FACE_FIRST_LOOP((BMFace *)f_src);
do {
- BMLoop *l_dst = (BMLoop *)BLI_mempool_alloc(lpool_dst);
+ BMLoop *l_dst = static_cast<BMLoop *>(BLI_mempool_alloc(lpool_dst));
memcpy(l_dst, l_iter_src, sizeof(BMLoop));
ltable_dst[index_loop] = l_dst;
BM_elem_index_set(l_iter_src, index_loop++); /* set_ok */
@@ -1319,7 +1335,8 @@ void BM_mesh_vert_coords_get(BMesh *bm, float (*vert_coords)[3])
float (*BM_mesh_vert_coords_alloc(BMesh *bm, int *r_vert_len))[3]
{
- float(*vert_coords)[3] = (float(*)[3])MEM_mallocN(bm->totvert * sizeof(*vert_coords), __func__);
+ float(*vert_coords)[3] = static_cast<float(*)[3]>(
+ MEM_mallocN(bm->totvert * sizeof(*vert_coords), __func__));
BM_mesh_vert_coords_get(bm, vert_coords);
*r_vert_len = bm->totvert;
return vert_coords;
diff --git a/source/blender/bmesh/intern/bmesh_mesh.h b/source/blender/bmesh/intern/bmesh_mesh.h
index a5994b52bc2..d766a26cf6e 100644
--- a/source/blender/bmesh/intern/bmesh_mesh.h
+++ b/source/blender/bmesh/intern/bmesh_mesh.h
@@ -17,7 +17,7 @@ void BM_mesh_elem_toolflags_ensure(BMesh *bm);
void BM_mesh_elem_toolflags_clear(BMesh *bm);
struct BMeshCreateParams {
- bool use_toolflags : true;
+ bool use_toolflags : 1;
};
/**
diff --git a/source/blender/bmesh/intern/bmesh_mesh_convert.cc b/source/blender/bmesh/intern/bmesh_mesh_convert.cc
index 739e474c59a..eadf87d7ebd 100644
--- a/source/blender/bmesh/intern/bmesh_mesh_convert.cc
+++ b/source/blender/bmesh/intern/bmesh_mesh_convert.cc
@@ -83,7 +83,10 @@
#include "BLI_listbase.h"
#include "BLI_math_vector.h"
#include "BLI_span.hh"
+#include "BLI_string_ref.hh"
+#include "BLI_task.hh"
+#include "BKE_attribute.hh"
#include "BKE_customdata.h"
#include "BKE_mesh.h"
#include "BKE_mesh_runtime.h"
@@ -103,7 +106,9 @@ static CLG_LogRef LOG = {"bmesh.mesh.convert"};
using blender::Array;
using blender::IndexRange;
+using blender::MutableSpan;
using blender::Span;
+using blender::StringRef;
void BM_mesh_cd_flag_ensure(BMesh *bm, Mesh *mesh, const char cd_flag)
{
@@ -184,10 +189,10 @@ void BM_mesh_bm_from_me(BMesh *bm, const Mesh *me, const struct BMeshFromMeshPar
if (!me || !me->totvert) {
if (me && is_new) { /* No verts? still copy custom-data layout. */
- CustomData_copy(&me->vdata, &bm->vdata, mask.vmask, CD_DEFAULT, 0);
- CustomData_copy(&me->edata, &bm->edata, mask.emask, CD_DEFAULT, 0);
- CustomData_copy(&me->ldata, &bm->ldata, mask.lmask, CD_DEFAULT, 0);
- CustomData_copy(&me->pdata, &bm->pdata, mask.pmask, CD_DEFAULT, 0);
+ CustomData_copy_mesh_to_bmesh(&me->vdata, &bm->vdata, mask.vmask, CD_DEFAULT, 0);
+ CustomData_copy_mesh_to_bmesh(&me->edata, &bm->edata, mask.emask, CD_DEFAULT, 0);
+ CustomData_copy_mesh_to_bmesh(&me->ldata, &bm->ldata, mask.lmask, CD_DEFAULT, 0);
+ CustomData_copy_mesh_to_bmesh(&me->pdata, &bm->pdata, mask.pmask, CD_DEFAULT, 0);
CustomData_bmesh_init_pool(&bm->vdata, me->totvert, BM_VERT);
CustomData_bmesh_init_pool(&bm->edata, me->totedge, BM_EDGE);
@@ -203,10 +208,10 @@ void BM_mesh_bm_from_me(BMesh *bm, const Mesh *me, const struct BMeshFromMeshPar
}
if (is_new) {
- CustomData_copy(&me->vdata, &bm->vdata, mask.vmask, CD_CALLOC, 0);
- CustomData_copy(&me->edata, &bm->edata, mask.emask, CD_CALLOC, 0);
- CustomData_copy(&me->ldata, &bm->ldata, mask.lmask, CD_CALLOC, 0);
- CustomData_copy(&me->pdata, &bm->pdata, mask.pmask, CD_CALLOC, 0);
+ CustomData_copy_mesh_to_bmesh(&me->vdata, &bm->vdata, mask.vmask, CD_CALLOC, 0);
+ CustomData_copy_mesh_to_bmesh(&me->edata, &bm->edata, mask.emask, CD_CALLOC, 0);
+ CustomData_copy_mesh_to_bmesh(&me->ldata, &bm->ldata, mask.lmask, CD_CALLOC, 0);
+ CustomData_copy_mesh_to_bmesh(&me->pdata, &bm->pdata, mask.pmask, CD_CALLOC, 0);
}
else {
CustomData_bmesh_merge(&me->vdata, &bm->vdata, mask.vmask, CD_CALLOC, bm, BM_VERT);
@@ -318,6 +323,13 @@ void BM_mesh_bm_from_me(BMesh *bm, const Mesh *me, const struct BMeshFromMeshPar
CustomData_get_offset(&bm->vdata, CD_SHAPE_KEYINDEX) :
-1;
+ const bool *hide_vert = (const bool *)CustomData_get_layer_named(
+ &me->vdata, CD_PROP_BOOL, ".hide_vert");
+ const bool *hide_edge = (const bool *)CustomData_get_layer_named(
+ &me->edata, CD_PROP_BOOL, ".hide_edge");
+ const bool *hide_poly = (const bool *)CustomData_get_layer_named(
+ &me->pdata, CD_PROP_BOOL, ".hide_poly");
+
Span<MVert> mvert{me->mvert, me->totvert};
Array<BMVert *> vtable(me->totvert);
for (const int i : mvert.index_range()) {
@@ -327,6 +339,9 @@ void BM_mesh_bm_from_me(BMesh *bm, const Mesh *me, const struct BMeshFromMeshPar
/* Transfer flag. */
v->head.hflag = BM_vert_flag_from_mflag(mvert[i].flag & ~SELECT);
+ if (hide_vert && hide_vert[i]) {
+ BM_elem_flag_enable(v, BM_ELEM_HIDDEN);
+ }
/* This is necessary for selection counts to work properly. */
if (mvert[i].flag & SELECT) {
@@ -366,6 +381,9 @@ void BM_mesh_bm_from_me(BMesh *bm, const Mesh *me, const struct BMeshFromMeshPar
/* Transfer flags. */
e->head.hflag = BM_edge_flag_from_mflag(medge[i].flag & ~SELECT);
+ if (hide_edge && hide_edge[i]) {
+ BM_elem_flag_enable(e, BM_ELEM_HIDDEN);
+ }
/* This is necessary for selection counts to work properly. */
if (medge[i].flag & SELECT) {
@@ -416,6 +434,9 @@ void BM_mesh_bm_from_me(BMesh *bm, const Mesh *me, const struct BMeshFromMeshPar
/* Transfer flag. */
f->head.hflag = BM_face_flag_from_mflag(mpoly[i].flag & ~ME_FACE_SEL);
+ if (hide_poly && hide_poly[i]) {
+ BM_elem_flag_enable(f, BM_ELEM_HIDDEN);
+ }
/* This is necessary for selection counts to work properly. */
if (mpoly[i].flag & ME_FACE_SEL) {
@@ -861,6 +882,63 @@ BLI_INLINE void bmesh_quick_edgedraw_flag(MEdge *med, BMEdge *e)
}
}
+template<typename GetFn>
+static void write_elem_flag_to_attribute(blender::bke::MutableAttributeAccessor &attributes,
+ const StringRef attribute_name,
+ const eAttrDomain domain,
+ const bool do_write,
+ const GetFn &get_fn)
+{
+ using namespace blender;
+ if (do_write) {
+ bke::SpanAttributeWriter<bool> attribute = attributes.lookup_or_add_for_write_only_span<bool>(
+ attribute_name, domain);
+ threading::parallel_for(attribute.span.index_range(), 4096, [&](IndexRange range) {
+ for (const int i : range) {
+ attribute.span[i] = get_fn(i);
+ }
+ });
+ attribute.finish();
+ }
+ else {
+ /* To avoid overhead, remove the hide attribute if possible. */
+ attributes.remove(attribute_name);
+ }
+}
+
+static void convert_bmesh_hide_flags_to_mesh_attributes(BMesh &bm,
+ const bool need_hide_vert,
+ const bool need_hide_edge,
+ const bool need_hide_poly,
+ Mesh &mesh)
+{
+ using namespace blender;
+ /* The "hide" attributes are stored as flags on #BMesh. */
+ BLI_assert(CustomData_get_layer_named(&bm.vdata, CD_PROP_BOOL, ".hide_vert") == nullptr);
+ BLI_assert(CustomData_get_layer_named(&bm.edata, CD_PROP_BOOL, ".hide_edge") == nullptr);
+ BLI_assert(CustomData_get_layer_named(&bm.pdata, CD_PROP_BOOL, ".hide_poly") == nullptr);
+
+ if (!(need_hide_vert || need_hide_edge || need_hide_poly)) {
+ return;
+ }
+
+ bke::MutableAttributeAccessor attributes = bke::mesh_attributes_for_write(mesh);
+ BM_mesh_elem_table_ensure(&bm, BM_VERT | BM_EDGE | BM_FACE);
+
+ write_elem_flag_to_attribute(
+ attributes, ".hide_vert", ATTR_DOMAIN_POINT, need_hide_vert, [&](const int i) {
+ return BM_elem_flag_test(BM_vert_at_index(&bm, i), BM_ELEM_HIDDEN);
+ });
+ write_elem_flag_to_attribute(
+ attributes, ".hide_edge", ATTR_DOMAIN_EDGE, need_hide_edge, [&](const int i) {
+ return BM_elem_flag_test(BM_edge_at_index(&bm, i), BM_ELEM_HIDDEN);
+ });
+ write_elem_flag_to_attribute(
+ attributes, ".hide_poly", ATTR_DOMAIN_FACE, need_hide_poly, [&](const int i) {
+ return BM_elem_flag_test(BM_face_at_index(&bm, i), BM_ELEM_HIDDEN);
+ });
+}
+
void BM_mesh_bm_to_me(Main *bmain, BMesh *bm, Mesh *me, const struct BMeshToMeshParams *params)
{
MEdge *med;
@@ -895,10 +973,10 @@ void BM_mesh_bm_to_me(Main *bmain, BMesh *bm, Mesh *me, const struct BMeshToMesh
{
CustomData_MeshMasks mask = CD_MASK_MESH;
CustomData_MeshMasks_update(&mask, &params->cd_mask_extra);
- CustomData_copy(&bm->vdata, &me->vdata, mask.vmask, CD_CALLOC, me->totvert);
- CustomData_copy(&bm->edata, &me->edata, mask.emask, CD_CALLOC, me->totedge);
- CustomData_copy(&bm->ldata, &me->ldata, mask.lmask, CD_CALLOC, me->totloop);
- CustomData_copy(&bm->pdata, &me->pdata, mask.pmask, CD_CALLOC, me->totpoly);
+ CustomData_copy_mesh_to_bmesh(&bm->vdata, &me->vdata, mask.vmask, CD_CALLOC, me->totvert);
+ CustomData_copy_mesh_to_bmesh(&bm->edata, &me->edata, mask.emask, CD_CALLOC, me->totedge);
+ CustomData_copy_mesh_to_bmesh(&bm->ldata, &me->ldata, mask.lmask, CD_CALLOC, me->totloop);
+ CustomData_copy_mesh_to_bmesh(&bm->pdata, &me->pdata, mask.pmask, CD_CALLOC, me->totpoly);
}
MVert *mvert = bm->totvert ? (MVert *)MEM_callocN(sizeof(MVert) * bm->totvert, "bm_to_me.vert") :
@@ -915,6 +993,10 @@ void BM_mesh_bm_to_me(Main *bmain, BMesh *bm, Mesh *me, const struct BMeshToMesh
CustomData_add_layer(&me->ldata, CD_MLOOP, CD_ASSIGN, mloop, me->totloop);
CustomData_add_layer(&me->pdata, CD_MPOLY, CD_ASSIGN, mpoly, me->totpoly);
+ bool need_hide_vert = false;
+ bool need_hide_edge = false;
+ bool need_hide_poly = false;
+
/* Clear normals on the mesh completely, since the original vertex and polygon count might be
* different than the BMesh's. */
BKE_mesh_clear_derived_normals(me);
@@ -929,6 +1011,9 @@ void BM_mesh_bm_to_me(Main *bmain, BMesh *bm, Mesh *me, const struct BMeshToMesh
copy_v3_v3(mvert->co, v->co);
mvert->flag = BM_vert_flag_to_mflag(v);
+ if (BM_elem_flag_test(v, BM_ELEM_HIDDEN)) {
+ need_hide_vert = true;
+ }
BM_elem_index_set(v, i); /* set_inline */
@@ -949,6 +1034,9 @@ void BM_mesh_bm_to_me(Main *bmain, BMesh *bm, Mesh *me, const struct BMeshToMesh
med->v2 = BM_elem_index_get(e->v2);
med->flag = BM_edge_flag_to_mflag(e);
+ if (BM_elem_flag_test(e, BM_ELEM_HIDDEN)) {
+ need_hide_edge = true;
+ }
BM_elem_index_set(e, i); /* set_inline */
@@ -975,6 +1063,9 @@ void BM_mesh_bm_to_me(Main *bmain, BMesh *bm, Mesh *me, const struct BMeshToMesh
mpoly->totloop = f->len;
mpoly->mat_nr = f->mat_nr;
mpoly->flag = BM_face_flag_to_mflag(f);
+ if (BM_elem_flag_test(f, BM_ELEM_HIDDEN)) {
+ need_hide_poly = true;
+ }
l_iter = l_first = BM_FACE_FIRST_LOOP(f);
do {
@@ -1067,6 +1158,9 @@ void BM_mesh_bm_to_me(Main *bmain, BMesh *bm, Mesh *me, const struct BMeshToMesh
}
}
+ convert_bmesh_hide_flags_to_mesh_attributes(
+ *bm, need_hide_vert, need_hide_edge, need_hide_poly, *me);
+
BKE_mesh_update_customdata_pointers(me, false);
{
@@ -1158,6 +1252,10 @@ void BM_mesh_bm_to_me_for_eval(BMesh *bm, Mesh *me, const CustomData_MeshMasks *
const int cd_edge_crease_offset = CustomData_get_offset(&bm->edata, CD_CREASE);
+ bool need_hide_vert = false;
+ bool need_hide_edge = false;
+ bool need_hide_poly = false;
+
/* Clear normals on the mesh completely, since the original vertex and polygon count might be
* different than the BMesh's. */
BKE_mesh_clear_derived_normals(me);
@@ -1172,6 +1270,13 @@ void BM_mesh_bm_to_me_for_eval(BMesh *bm, Mesh *me, const CustomData_MeshMasks *
BM_elem_index_set(eve, i); /* set_inline */
mv->flag = BM_vert_flag_to_mflag(eve);
+ if (BM_elem_flag_test(eve, BM_ELEM_HIDDEN)) {
+ need_hide_vert = true;
+ }
+
+ if (cd_vert_bweight_offset != -1) {
+ mv->bweight = BM_ELEM_CD_GET_FLOAT_AS_UCHAR(eve, cd_vert_bweight_offset);
+ }
CustomData_from_bmesh_block(&bm->vdata, &me->vdata, eve->head.data, i);
}
@@ -1186,6 +1291,9 @@ void BM_mesh_bm_to_me_for_eval(BMesh *bm, Mesh *me, const CustomData_MeshMasks *
med->v2 = BM_elem_index_get(eed->v2);
med->flag = BM_edge_flag_to_mflag(eed);
+ if (BM_elem_flag_test(eed, BM_ELEM_HIDDEN)) {
+ need_hide_edge = true;
+ }
/* Handle this differently to editmode switching,
* only enable draw for single user edges rather than calculating angle. */
@@ -1213,6 +1321,10 @@ void BM_mesh_bm_to_me_for_eval(BMesh *bm, Mesh *me, const CustomData_MeshMasks *
mp->totloop = efa->len;
mp->flag = BM_face_flag_to_mflag(efa);
+ if (BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) {
+ need_hide_poly = true;
+ }
+
mp->loopstart = j;
mp->mat_nr = efa->mat_nr;
@@ -1232,5 +1344,8 @@ void BM_mesh_bm_to_me_for_eval(BMesh *bm, Mesh *me, const CustomData_MeshMasks *
}
bm->elem_index_dirty &= ~(BM_FACE | BM_LOOP);
+ convert_bmesh_hide_flags_to_mesh_attributes(
+ *bm, need_hide_vert, need_hide_edge, need_hide_poly, *me);
+
me->cd_flag = BM_mesh_cd_flag_from_bmesh(bm);
}
diff --git a/source/blender/compositor/CMakeLists.txt b/source/blender/compositor/CMakeLists.txt
index 55e349423bb..f49a9694ab3 100644
--- a/source/blender/compositor/CMakeLists.txt
+++ b/source/blender/compositor/CMakeLists.txt
@@ -1,676 +1,682 @@
# SPDX-License-Identifier: GPL-2.0-or-later
# Copyright 2011 Blender Foundation. All rights reserved.
-set(INC
- .
- intern
- nodes
- operations
- ../blenkernel
- ../blenlib
- ../blentranslation
- ../depsgraph
- ../imbuf
- ../makesdna
- ../makesrna
- ../nodes
- ../windowmanager
- ../nodes/composite
- ../nodes/intern
- ../render
- ../render/intern
- ../../../extern/clew/include
- ../../../intern/atomic
- ../../../intern/clog
- ../../../intern/guardedalloc
-
- # dna_type_offsets.h
- ${CMAKE_CURRENT_BINARY_DIR}/../makesdna/intern
- # RNA_prototypes.h
- ${CMAKE_BINARY_DIR}/source/blender/makesrna
-)
-
-set(INC_SYS
-
-)
-
-set(SRC
- COM_compositor.h
- COM_defines.h
-
- intern/COM_BufferArea.h
- intern/COM_BufferOperation.cc
- intern/COM_BufferOperation.h
- intern/COM_BufferRange.h
- intern/COM_BuffersIterator.h
- intern/COM_CPUDevice.cc
- intern/COM_CPUDevice.h
- intern/COM_ChunkOrder.cc
- intern/COM_ChunkOrder.h
- intern/COM_ChunkOrderHotspot.cc
- intern/COM_ChunkOrderHotspot.h
- intern/COM_CompositorContext.cc
- intern/COM_CompositorContext.h
- intern/COM_ConstantFolder.cc
- intern/COM_ConstantFolder.h
- intern/COM_Converter.cc
- intern/COM_Converter.h
- intern/COM_Debug.cc
- intern/COM_Debug.h
- intern/COM_Device.cc
- intern/COM_Device.h
- intern/COM_Enums.cc
- intern/COM_Enums.h
- intern/COM_ExecutionGroup.cc
- intern/COM_ExecutionGroup.h
- intern/COM_ExecutionModel.cc
- intern/COM_ExecutionModel.h
- intern/COM_ExecutionSystem.cc
- intern/COM_ExecutionSystem.h
- intern/COM_FullFrameExecutionModel.cc
- intern/COM_FullFrameExecutionModel.h
- intern/COM_MemoryBuffer.cc
- intern/COM_MemoryBuffer.h
- intern/COM_MemoryProxy.cc
- intern/COM_MemoryProxy.h
- intern/COM_MetaData.cc
- intern/COM_MetaData.h
- intern/COM_MultiThreadedOperation.cc
- intern/COM_MultiThreadedOperation.h
- intern/COM_MultiThreadedRowOperation.cc
- intern/COM_MultiThreadedRowOperation.h
- intern/COM_Node.cc
- intern/COM_Node.h
- intern/COM_NodeConverter.cc
- intern/COM_NodeConverter.h
- intern/COM_NodeGraph.cc
- intern/COM_NodeGraph.h
- intern/COM_NodeOperation.cc
- intern/COM_NodeOperation.h
- intern/COM_NodeOperationBuilder.cc
- intern/COM_NodeOperationBuilder.h
- intern/COM_OpenCLDevice.cc
- intern/COM_OpenCLDevice.h
- intern/COM_SharedOperationBuffers.cc
- intern/COM_SharedOperationBuffers.h
- intern/COM_SingleThreadedOperation.cc
- intern/COM_SingleThreadedOperation.h
- intern/COM_TiledExecutionModel.cc
- intern/COM_TiledExecutionModel.h
- intern/COM_WorkPackage.cc
- intern/COM_WorkPackage.h
- intern/COM_WorkScheduler.cc
- intern/COM_WorkScheduler.h
- intern/COM_compositor.cc
-
- operations/COM_QualityStepHelper.cc
- operations/COM_QualityStepHelper.h
-
- # Internal nodes
- nodes/COM_SocketProxyNode.cc
- nodes/COM_SocketProxyNode.h
-
- # input nodes
- nodes/COM_BokehImageNode.cc
- nodes/COM_BokehImageNode.h
- nodes/COM_ColorNode.cc
- nodes/COM_ColorNode.h
- nodes/COM_ImageNode.cc
- nodes/COM_ImageNode.h
- nodes/COM_MaskNode.cc
- nodes/COM_MaskNode.h
- nodes/COM_MovieClipNode.cc
- nodes/COM_MovieClipNode.h
- nodes/COM_OutputFileNode.cc
- nodes/COM_OutputFileNode.h
- nodes/COM_RenderLayersNode.cc
- nodes/COM_RenderLayersNode.h
- nodes/COM_SceneTimeNode.cc
- nodes/COM_SceneTimeNode.h
- nodes/COM_SwitchNode.cc
- nodes/COM_SwitchNode.h
- nodes/COM_SwitchViewNode.cc
- nodes/COM_SwitchViewNode.h
- nodes/COM_TextureNode.cc
- nodes/COM_TextureNode.h
- nodes/COM_TimeNode.cc
- nodes/COM_TimeNode.h
- nodes/COM_ValueNode.cc
- nodes/COM_ValueNode.h
-
- # output nodes
- nodes/COM_CompositorNode.cc
- nodes/COM_CompositorNode.h
- nodes/COM_SplitViewerNode.cc
- nodes/COM_SplitViewerNode.h
- nodes/COM_ViewLevelsNode.cc
- nodes/COM_ViewLevelsNode.h
- nodes/COM_ViewerNode.cc
- nodes/COM_ViewerNode.h
- operations/COM_CalculateMeanOperation.cc
- operations/COM_CalculateMeanOperation.h
- operations/COM_CalculateStandardDeviationOperation.cc
- operations/COM_CalculateStandardDeviationOperation.h
-
- # distort nodes
- nodes/COM_FlipNode.cc
- nodes/COM_FlipNode.h
- nodes/COM_RotateNode.cc
- nodes/COM_RotateNode.h
- nodes/COM_ScaleNode.cc
- nodes/COM_ScaleNode.h
- nodes/COM_TranslateNode.cc
- nodes/COM_TranslateNode.h
-
- nodes/COM_DisplaceNode.cc
- nodes/COM_DisplaceNode.h
- nodes/COM_MapUVNode.cc
- nodes/COM_MapUVNode.h
-
- nodes/COM_ChannelMatteNode.cc
- nodes/COM_ChannelMatteNode.h
- nodes/COM_ChromaMatteNode.cc
- nodes/COM_ChromaMatteNode.h
- nodes/COM_ColorMatteNode.cc
- nodes/COM_ColorMatteNode.h
- nodes/COM_DifferenceMatteNode.cc
- nodes/COM_DifferenceMatteNode.h
- nodes/COM_DistanceMatteNode.cc
- nodes/COM_DistanceMatteNode.h
- nodes/COM_LensDistortionNode.cc
- nodes/COM_LensDistortionNode.h
- nodes/COM_LuminanceMatteNode.cc
- nodes/COM_LuminanceMatteNode.h
-
- nodes/COM_GlareNode.cc
- nodes/COM_GlareNode.h
-
- nodes/COM_SunBeamsNode.cc
- nodes/COM_SunBeamsNode.h
- operations/COM_SunBeamsOperation.cc
- operations/COM_SunBeamsOperation.h
-
- nodes/COM_CryptomatteNode.cc
- nodes/COM_CryptomatteNode.h
- operations/COM_CryptomatteOperation.cc
- operations/COM_CryptomatteOperation.h
-
- nodes/COM_CornerPinNode.cc
- nodes/COM_CornerPinNode.h
- nodes/COM_PlaneTrackDeformNode.cc
- nodes/COM_PlaneTrackDeformNode.h
-
- nodes/COM_CropNode.cc
- nodes/COM_CropNode.h
- operations/COM_CropOperation.cc
- operations/COM_CropOperation.h
-
- nodes/COM_DefocusNode.cc
- nodes/COM_DefocusNode.h
- nodes/COM_MovieDistortionNode.cc
- nodes/COM_MovieDistortionNode.h
- nodes/COM_Stabilize2dNode.cc
- nodes/COM_Stabilize2dNode.h
- nodes/COM_TransformNode.cc
- nodes/COM_TransformNode.h
-
- # color nodes
- nodes/COM_AlphaOverNode.cc
- nodes/COM_AlphaOverNode.h
- nodes/COM_BrightnessNode.cc
- nodes/COM_BrightnessNode.h
- nodes/COM_ColorBalanceNode.cc
- nodes/COM_ColorBalanceNode.h
- nodes/COM_ColorCorrectionNode.cc
- nodes/COM_ColorCorrectionNode.h
- nodes/COM_ColorCurveNode.cc
- nodes/COM_ColorCurveNode.h
- nodes/COM_ColorExposureNode.cc
- nodes/COM_ColorExposureNode.h
- nodes/COM_ColorRampNode.cc
- nodes/COM_ColorRampNode.h
- nodes/COM_ColorToBWNode.cc
- nodes/COM_ColorToBWNode.h
- nodes/COM_ConvertAlphaNode.cc
- nodes/COM_ConvertAlphaNode.h
- nodes/COM_ConvertColorSpaceNode.cc
- nodes/COM_ConvertColorSpaceNode.h
- nodes/COM_GammaNode.cc
- nodes/COM_GammaNode.h
- nodes/COM_HueSaturationValueCorrectNode.cc
- nodes/COM_HueSaturationValueCorrectNode.h
- nodes/COM_HueSaturationValueNode.cc
- nodes/COM_HueSaturationValueNode.h
- nodes/COM_InvertNode.cc
- nodes/COM_InvertNode.h
- nodes/COM_MixNode.cc
- nodes/COM_MixNode.h
- nodes/COM_SetAlphaNode.cc
- nodes/COM_SetAlphaNode.h
- nodes/COM_TonemapNode.cc
- nodes/COM_TonemapNode.h
- nodes/COM_VectorCurveNode.cc
- nodes/COM_VectorCurveNode.h
- nodes/COM_ZCombineNode.cc
- nodes/COM_ZCombineNode.h
- operations/COM_TonemapOperation.cc
- operations/COM_TonemapOperation.h
-
- # converter nodes
- nodes/COM_CombineColorNode.cc
- nodes/COM_CombineColorNode.h
- nodes/COM_CombineColorNodeLegacy.cc
- nodes/COM_CombineColorNodeLegacy.h
- nodes/COM_CombineXYZNode.cc
- nodes/COM_CombineXYZNode.h
- nodes/COM_IDMaskNode.cc
- nodes/COM_IDMaskNode.h
- nodes/COM_SeparateColorNode.cc
- nodes/COM_SeparateColorNode.h
- nodes/COM_SeparateColorNodeLegacy.cc
- nodes/COM_SeparateColorNodeLegacy.h
- nodes/COM_SeparateXYZNode.cc
- nodes/COM_SeparateXYZNode.h
-
- nodes/COM_MapRangeNode.cc
- nodes/COM_MapRangeNode.h
- nodes/COM_MapValueNode.cc
- nodes/COM_MapValueNode.h
- nodes/COM_MathNode.cc
- nodes/COM_MathNode.h
- nodes/COM_NormalNode.cc
- nodes/COM_NormalNode.h
- nodes/COM_NormalizeNode.cc
- nodes/COM_NormalizeNode.h
-
- operations/COM_NormalizeOperation.cc
- operations/COM_NormalizeOperation.h
-
- nodes/COM_PixelateNode.cc
- nodes/COM_PixelateNode.h
- operations/COM_PixelateOperation.cc
- operations/COM_PixelateOperation.h
-
- # Filter nodes
- nodes/COM_BilateralBlurNode.cc
- nodes/COM_BilateralBlurNode.h
- operations/COM_BilateralBlurOperation.cc
- operations/COM_BilateralBlurOperation.h
- nodes/COM_VectorBlurNode.cc
- nodes/COM_VectorBlurNode.h
- operations/COM_VectorBlurOperation.cc
- operations/COM_VectorBlurOperation.h
- nodes/COM_AntiAliasingNode.cc
- nodes/COM_AntiAliasingNode.h
- nodes/COM_BlurNode.cc
- nodes/COM_BlurNode.h
- nodes/COM_BokehBlurNode.cc
- nodes/COM_BokehBlurNode.h
- nodes/COM_DenoiseNode.cc
- nodes/COM_DenoiseNode.h
- nodes/COM_DespeckleNode.cc
- nodes/COM_DespeckleNode.h
- nodes/COM_DilateErodeNode.cc
- nodes/COM_DilateErodeNode.h
- nodes/COM_DirectionalBlurNode.cc
- nodes/COM_DirectionalBlurNode.h
- nodes/COM_FilterNode.cc
- nodes/COM_FilterNode.h
- nodes/COM_InpaintNode.cc
- nodes/COM_InpaintNode.h
- nodes/COM_PosterizeNode.cc
- nodes/COM_PosterizeNode.h
-
- operations/COM_BlurBaseOperation.cc
- operations/COM_BlurBaseOperation.h
- operations/COM_BokehBlurOperation.cc
- operations/COM_BokehBlurOperation.h
- operations/COM_DirectionalBlurOperation.cc
- operations/COM_DirectionalBlurOperation.h
- operations/COM_FastGaussianBlurOperation.cc
- operations/COM_FastGaussianBlurOperation.h
- operations/COM_GammaCorrectOperation.cc
- operations/COM_GammaCorrectOperation.h
- operations/COM_GaussianAlphaBlurBaseOperation.cc
- operations/COM_GaussianAlphaBlurBaseOperation.h
- operations/COM_GaussianAlphaXBlurOperation.cc
- operations/COM_GaussianAlphaXBlurOperation.h
- operations/COM_GaussianAlphaYBlurOperation.cc
- operations/COM_GaussianAlphaYBlurOperation.h
- operations/COM_GaussianBlurBaseOperation.cc
- operations/COM_GaussianBlurBaseOperation.h
- operations/COM_GaussianBokehBlurOperation.cc
- operations/COM_GaussianBokehBlurOperation.h
- operations/COM_GaussianXBlurOperation.cc
- operations/COM_GaussianXBlurOperation.h
- operations/COM_GaussianYBlurOperation.cc
- operations/COM_GaussianYBlurOperation.h
- operations/COM_MovieClipAttributeOperation.cc
- operations/COM_MovieClipAttributeOperation.h
- operations/COM_MovieDistortionOperation.cc
- operations/COM_MovieDistortionOperation.h
- operations/COM_PosterizeOperation.cc
- operations/COM_PosterizeOperation.h
- operations/COM_SMAAOperation.cc
- operations/COM_SMAAOperation.h
- operations/COM_VariableSizeBokehBlurOperation.cc
- operations/COM_VariableSizeBokehBlurOperation.h
-
- # Matte nodes
- nodes/COM_BoxMaskNode.cc
- nodes/COM_BoxMaskNode.h
- nodes/COM_ColorSpillNode.cc
- nodes/COM_ColorSpillNode.h
- nodes/COM_DoubleEdgeMaskNode.cc
- nodes/COM_DoubleEdgeMaskNode.h
- nodes/COM_EllipseMaskNode.cc
- nodes/COM_EllipseMaskNode.h
-
- operations/COM_DoubleEdgeMaskOperation.cc
- operations/COM_DoubleEdgeMaskOperation.h
-
-
- nodes/COM_KeyingScreenNode.cc
- nodes/COM_KeyingScreenNode.h
- operations/COM_KeyingScreenOperation.cc
- operations/COM_KeyingScreenOperation.h
-
- nodes/COM_TrackPositionNode.cc
- nodes/COM_TrackPositionNode.h
- operations/COM_TrackPositionOperation.cc
- operations/COM_TrackPositionOperation.h
-
- nodes/COM_KeyingNode.cc
- nodes/COM_KeyingNode.h
- operations/COM_KeyingBlurOperation.cc
- operations/COM_KeyingBlurOperation.h
- operations/COM_KeyingClipOperation.cc
- operations/COM_KeyingClipOperation.h
- operations/COM_KeyingDespillOperation.cc
- operations/COM_KeyingDespillOperation.h
- operations/COM_KeyingOperation.cc
- operations/COM_KeyingOperation.h
-
- operations/COM_ColorSpillOperation.cc
- operations/COM_ColorSpillOperation.h
- operations/COM_RenderLayersProg.cc
- operations/COM_RenderLayersProg.h
-
- operations/COM_BokehImageOperation.cc
- operations/COM_BokehImageOperation.h
- operations/COM_ImageOperation.cc
- operations/COM_ImageOperation.h
- operations/COM_MultilayerImageOperation.cc
- operations/COM_MultilayerImageOperation.h
- operations/COM_TextureOperation.cc
- operations/COM_TextureOperation.h
-
-
- operations/COM_SocketProxyOperation.cc
- operations/COM_SocketProxyOperation.h
-
- operations/COM_CompositorOperation.cc
- operations/COM_CompositorOperation.h
- operations/COM_ConvertDepthToRadiusOperation.cc
- operations/COM_ConvertDepthToRadiusOperation.h
- operations/COM_OutputFileMultiViewOperation.cc
- operations/COM_OutputFileMultiViewOperation.h
- operations/COM_OutputFileOperation.cc
- operations/COM_OutputFileOperation.h
- operations/COM_PreviewOperation.cc
- operations/COM_PreviewOperation.h
- operations/COM_SplitOperation.cc
- operations/COM_SplitOperation.h
- operations/COM_ViewerOperation.cc
- operations/COM_ViewerOperation.h
- operations/COM_ZCombineOperation.cc
- operations/COM_ZCombineOperation.h
-
- operations/COM_ChangeHSVOperation.cc
- operations/COM_ChangeHSVOperation.h
- operations/COM_ChannelMatteOperation.cc
- operations/COM_ChannelMatteOperation.h
- operations/COM_ChromaMatteOperation.cc
- operations/COM_ChromaMatteOperation.h
- operations/COM_ColorCurveOperation.cc
- operations/COM_ColorCurveOperation.h
- operations/COM_ColorExposureOperation.cc
- operations/COM_ColorExposureOperation.h
- operations/COM_ColorMatteOperation.cc
- operations/COM_ColorMatteOperation.h
- operations/COM_ColorRampOperation.cc
- operations/COM_ColorRampOperation.h
- operations/COM_CurveBaseOperation.cc
- operations/COM_CurveBaseOperation.h
- operations/COM_DifferenceMatteOperation.cc
- operations/COM_DifferenceMatteOperation.h
- operations/COM_DistanceRGBMatteOperation.cc
- operations/COM_DistanceRGBMatteOperation.h
- operations/COM_DistanceYCCMatteOperation.cc
- operations/COM_DistanceYCCMatteOperation.h
- operations/COM_HueSaturationValueCorrectOperation.cc
- operations/COM_HueSaturationValueCorrectOperation.h
- operations/COM_LuminanceMatteOperation.cc
- operations/COM_LuminanceMatteOperation.h
- operations/COM_VectorCurveOperation.cc
- operations/COM_VectorCurveOperation.h
-
- operations/COM_BrightnessOperation.cc
- operations/COM_BrightnessOperation.h
- operations/COM_ColorCorrectionOperation.cc
- operations/COM_ColorCorrectionOperation.h
- operations/COM_ConstantOperation.cc
- operations/COM_ConstantOperation.h
- operations/COM_GammaOperation.cc
- operations/COM_GammaOperation.h
- operations/COM_MixOperation.cc
- operations/COM_MixOperation.h
- operations/COM_ReadBufferOperation.cc
- operations/COM_ReadBufferOperation.h
- operations/COM_SetColorOperation.cc
- operations/COM_SetColorOperation.h
- operations/COM_SetValueOperation.cc
- operations/COM_SetValueOperation.h
- operations/COM_SetVectorOperation.cc
- operations/COM_SetVectorOperation.h
- operations/COM_WriteBufferOperation.cc
- operations/COM_WriteBufferOperation.h
-
- operations/COM_MathBaseOperation.cc
- operations/COM_MathBaseOperation.h
-
- operations/COM_AlphaOverKeyOperation.cc
- operations/COM_AlphaOverKeyOperation.h
- operations/COM_AlphaOverMixedOperation.cc
- operations/COM_AlphaOverMixedOperation.h
- operations/COM_AlphaOverPremultiplyOperation.cc
- operations/COM_AlphaOverPremultiplyOperation.h
-
- operations/COM_ColorBalanceASCCDLOperation.cc
- operations/COM_ColorBalanceASCCDLOperation.h
- operations/COM_ColorBalanceLGGOperation.cc
- operations/COM_ColorBalanceLGGOperation.h
- operations/COM_InvertOperation.cc
- operations/COM_InvertOperation.h
- operations/COM_MapRangeOperation.cc
- operations/COM_MapRangeOperation.h
- operations/COM_MapValueOperation.cc
- operations/COM_MapValueOperation.h
- operations/COM_SetAlphaMultiplyOperation.cc
- operations/COM_SetAlphaMultiplyOperation.h
- operations/COM_SetAlphaReplaceOperation.cc
- operations/COM_SetAlphaReplaceOperation.h
-
- # Distort operation
- operations/COM_DisplaceOperation.cc
- operations/COM_DisplaceOperation.h
- operations/COM_DisplaceSimpleOperation.cc
- operations/COM_DisplaceSimpleOperation.h
- operations/COM_FlipOperation.cc
- operations/COM_FlipOperation.h
- operations/COM_MapUVOperation.cc
- operations/COM_MapUVOperation.h
- operations/COM_PlaneCornerPinOperation.cc
- operations/COM_PlaneCornerPinOperation.h
- operations/COM_PlaneDistortCommonOperation.cc
- operations/COM_PlaneDistortCommonOperation.h
- operations/COM_PlaneTrackOperation.cc
- operations/COM_PlaneTrackOperation.h
- operations/COM_ProjectorLensDistortionOperation.cc
- operations/COM_ProjectorLensDistortionOperation.h
- operations/COM_RotateOperation.cc
- operations/COM_RotateOperation.h
- operations/COM_ScaleOperation.cc
- operations/COM_ScaleOperation.h
- operations/COM_ScreenLensDistortionOperation.cc
- operations/COM_ScreenLensDistortionOperation.h
- operations/COM_TransformOperation.cc
- operations/COM_TransformOperation.h
- operations/COM_TranslateOperation.cc
- operations/COM_TranslateOperation.h
- operations/COM_WrapOperation.cc
- operations/COM_WrapOperation.h
-
- # Filter operations
- operations/COM_ConvolutionEdgeFilterOperation.cc
- operations/COM_ConvolutionEdgeFilterOperation.h
- operations/COM_ConvolutionFilterOperation.cc
- operations/COM_ConvolutionFilterOperation.h
- operations/COM_DenoiseOperation.cc
- operations/COM_DenoiseOperation.h
- operations/COM_DespeckleOperation.cc
- operations/COM_DespeckleOperation.h
- operations/COM_DilateErodeOperation.cc
- operations/COM_DilateErodeOperation.h
- operations/COM_GlareBaseOperation.cc
- operations/COM_GlareBaseOperation.h
- operations/COM_GlareFogGlowOperation.cc
- operations/COM_GlareFogGlowOperation.h
- operations/COM_GlareGhostOperation.cc
- operations/COM_GlareGhostOperation.h
- operations/COM_GlareSimpleStarOperation.cc
- operations/COM_GlareSimpleStarOperation.h
- operations/COM_GlareStreaksOperation.cc
- operations/COM_GlareStreaksOperation.h
- operations/COM_GlareThresholdOperation.cc
- operations/COM_GlareThresholdOperation.h
- operations/COM_InpaintOperation.cc
- operations/COM_InpaintOperation.h
- operations/COM_SetSamplerOperation.cc
- operations/COM_SetSamplerOperation.h
-
-
- # Convert operations
- operations/COM_ConvertOperation.cc
- operations/COM_ConvertOperation.h
- operations/COM_IDMaskOperation.cc
- operations/COM_IDMaskOperation.h
-
- operations/COM_ConvertColorSpaceOperation.cc
- operations/COM_ConvertColorSpaceOperation.h
- operations/COM_DotproductOperation.cc
- operations/COM_DotproductOperation.h
-
- # Matte operation
- operations/COM_BoxMaskOperation.cc
- operations/COM_BoxMaskOperation.h
- operations/COM_EllipseMaskOperation.cc
- operations/COM_EllipseMaskOperation.h
-
- operations/COM_ConvertColorProfileOperation.cc
- operations/COM_ConvertColorProfileOperation.h
- operations/COM_MovieClipOperation.cc
- operations/COM_MovieClipOperation.h
-
- operations/COM_AntiAliasOperation.cc
- operations/COM_AntiAliasOperation.h
-
- operations/COM_MaskOperation.cc
- operations/COM_MaskOperation.h
-)
-
-set(LIB
- bf_blenkernel
- bf_blenlib
- extern_clew
-)
-
-list(APPEND INC
- ${CMAKE_CURRENT_BINARY_DIR}/operations
-)
-
-data_to_c(
- ${CMAKE_CURRENT_SOURCE_DIR}/operations/COM_OpenCLKernels.cl
- ${CMAKE_CURRENT_BINARY_DIR}/operations/COM_OpenCLKernels.cl.h
- SRC
-)
-
-add_definitions(-DCL_USE_DEPRECATED_OPENCL_1_1_APIS)
-
-set(GENSRC_DIR ${CMAKE_CURRENT_BINARY_DIR}/operations)
-set(GENSRC ${GENSRC_DIR}/COM_SMAAAreaTexture.h)
-add_custom_command(
- OUTPUT ${GENSRC}
- COMMAND ${CMAKE_COMMAND} -E make_directory ${GENSRC_DIR}
- COMMAND "$<TARGET_FILE:smaa_areatex>" ${GENSRC}
- DEPENDS smaa_areatex
-)
-add_custom_target(smaa_areatex_header
- SOURCES ${GENSRC}
-)
-list(APPEND SRC
- ${GENSRC}
-)
-unset(GENSRC)
-unset(GENSRC_DIR)
-
-if(WITH_OPENIMAGEDENOISE)
- add_definitions(-DWITH_OPENIMAGEDENOISE)
- add_definitions(-DOIDN_STATIC_LIB)
- list(APPEND INC_SYS
- ${OPENIMAGEDENOISE_INCLUDE_DIRS}
- ${TBB_INCLUDE_DIRS}
+add_subdirectory(realtime_compositor)
+
+if(WITH_COMPOSITOR_CPU)
+ set(INC
+ .
+ intern
+ nodes
+ operations
+ ../blenkernel
+ ../blenlib
+ ../blentranslation
+ ../depsgraph
+ ../imbuf
+ ../makesdna
+ ../makesrna
+ ../nodes
+ ../windowmanager
+ ../nodes/composite
+ ../nodes/intern
+ ../render
+ ../render/intern
+ ../../../extern/clew/include
+ ../../../intern/atomic
+ ../../../intern/clog
+ ../../../intern/guardedalloc
+
+ # dna_type_offsets.h
+ ${CMAKE_CURRENT_BINARY_DIR}/../makesdna/intern
+ # RNA_prototypes.h
+ ${CMAKE_BINARY_DIR}/source/blender/makesrna
)
- list(APPEND LIB
- ${OPENIMAGEDENOISE_LIBRARIES}
- ${TBB_LIBRARIES}
+
+ set(INC_SYS
+
)
-endif()
-blender_add_lib(bf_compositor "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
+ set(SRC
+ COM_compositor.h
+ COM_defines.h
+
+ intern/COM_BufferArea.h
+ intern/COM_BufferOperation.cc
+ intern/COM_BufferOperation.h
+ intern/COM_BufferRange.h
+ intern/COM_BuffersIterator.h
+ intern/COM_CPUDevice.cc
+ intern/COM_CPUDevice.h
+ intern/COM_ChunkOrder.cc
+ intern/COM_ChunkOrder.h
+ intern/COM_ChunkOrderHotspot.cc
+ intern/COM_ChunkOrderHotspot.h
+ intern/COM_CompositorContext.cc
+ intern/COM_CompositorContext.h
+ intern/COM_ConstantFolder.cc
+ intern/COM_ConstantFolder.h
+ intern/COM_Converter.cc
+ intern/COM_Converter.h
+ intern/COM_Debug.cc
+ intern/COM_Debug.h
+ intern/COM_Device.cc
+ intern/COM_Device.h
+ intern/COM_Enums.cc
+ intern/COM_Enums.h
+ intern/COM_ExecutionGroup.cc
+ intern/COM_ExecutionGroup.h
+ intern/COM_ExecutionModel.cc
+ intern/COM_ExecutionModel.h
+ intern/COM_ExecutionSystem.cc
+ intern/COM_ExecutionSystem.h
+ intern/COM_FullFrameExecutionModel.cc
+ intern/COM_FullFrameExecutionModel.h
+ intern/COM_MemoryBuffer.cc
+ intern/COM_MemoryBuffer.h
+ intern/COM_MemoryProxy.cc
+ intern/COM_MemoryProxy.h
+ intern/COM_MetaData.cc
+ intern/COM_MetaData.h
+ intern/COM_MultiThreadedOperation.cc
+ intern/COM_MultiThreadedOperation.h
+ intern/COM_MultiThreadedRowOperation.cc
+ intern/COM_MultiThreadedRowOperation.h
+ intern/COM_Node.cc
+ intern/COM_Node.h
+ intern/COM_NodeConverter.cc
+ intern/COM_NodeConverter.h
+ intern/COM_NodeGraph.cc
+ intern/COM_NodeGraph.h
+ intern/COM_NodeOperation.cc
+ intern/COM_NodeOperation.h
+ intern/COM_NodeOperationBuilder.cc
+ intern/COM_NodeOperationBuilder.h
+ intern/COM_OpenCLDevice.cc
+ intern/COM_OpenCLDevice.h
+ intern/COM_SharedOperationBuffers.cc
+ intern/COM_SharedOperationBuffers.h
+ intern/COM_SingleThreadedOperation.cc
+ intern/COM_SingleThreadedOperation.h
+ intern/COM_TiledExecutionModel.cc
+ intern/COM_TiledExecutionModel.h
+ intern/COM_WorkPackage.cc
+ intern/COM_WorkPackage.h
+ intern/COM_WorkScheduler.cc
+ intern/COM_WorkScheduler.h
+ intern/COM_compositor.cc
+
+ operations/COM_QualityStepHelper.cc
+ operations/COM_QualityStepHelper.h
+
+ # Internal nodes
+ nodes/COM_SocketProxyNode.cc
+ nodes/COM_SocketProxyNode.h
+
+ # input nodes
+ nodes/COM_BokehImageNode.cc
+ nodes/COM_BokehImageNode.h
+ nodes/COM_ColorNode.cc
+ nodes/COM_ColorNode.h
+ nodes/COM_ImageNode.cc
+ nodes/COM_ImageNode.h
+ nodes/COM_MaskNode.cc
+ nodes/COM_MaskNode.h
+ nodes/COM_MovieClipNode.cc
+ nodes/COM_MovieClipNode.h
+ nodes/COM_OutputFileNode.cc
+ nodes/COM_OutputFileNode.h
+ nodes/COM_RenderLayersNode.cc
+ nodes/COM_RenderLayersNode.h
+ nodes/COM_SceneTimeNode.cc
+ nodes/COM_SceneTimeNode.h
+ nodes/COM_SwitchNode.cc
+ nodes/COM_SwitchNode.h
+ nodes/COM_SwitchViewNode.cc
+ nodes/COM_SwitchViewNode.h
+ nodes/COM_TextureNode.cc
+ nodes/COM_TextureNode.h
+ nodes/COM_TimeNode.cc
+ nodes/COM_TimeNode.h
+ nodes/COM_ValueNode.cc
+ nodes/COM_ValueNode.h
+
+ # output nodes
+ nodes/COM_CompositorNode.cc
+ nodes/COM_CompositorNode.h
+ nodes/COM_SplitViewerNode.cc
+ nodes/COM_SplitViewerNode.h
+ nodes/COM_ViewLevelsNode.cc
+ nodes/COM_ViewLevelsNode.h
+ nodes/COM_ViewerNode.cc
+ nodes/COM_ViewerNode.h
+ operations/COM_CalculateMeanOperation.cc
+ operations/COM_CalculateMeanOperation.h
+ operations/COM_CalculateStandardDeviationOperation.cc
+ operations/COM_CalculateStandardDeviationOperation.h
+
+ # distort nodes
+ nodes/COM_FlipNode.cc
+ nodes/COM_FlipNode.h
+ nodes/COM_RotateNode.cc
+ nodes/COM_RotateNode.h
+ nodes/COM_ScaleNode.cc
+ nodes/COM_ScaleNode.h
+ nodes/COM_TranslateNode.cc
+ nodes/COM_TranslateNode.h
+
+ nodes/COM_DisplaceNode.cc
+ nodes/COM_DisplaceNode.h
+ nodes/COM_MapUVNode.cc
+ nodes/COM_MapUVNode.h
+
+ nodes/COM_ChannelMatteNode.cc
+ nodes/COM_ChannelMatteNode.h
+ nodes/COM_ChromaMatteNode.cc
+ nodes/COM_ChromaMatteNode.h
+ nodes/COM_ColorMatteNode.cc
+ nodes/COM_ColorMatteNode.h
+ nodes/COM_DifferenceMatteNode.cc
+ nodes/COM_DifferenceMatteNode.h
+ nodes/COM_DistanceMatteNode.cc
+ nodes/COM_DistanceMatteNode.h
+ nodes/COM_LensDistortionNode.cc
+ nodes/COM_LensDistortionNode.h
+ nodes/COM_LuminanceMatteNode.cc
+ nodes/COM_LuminanceMatteNode.h
+
+ nodes/COM_GlareNode.cc
+ nodes/COM_GlareNode.h
+
+ nodes/COM_SunBeamsNode.cc
+ nodes/COM_SunBeamsNode.h
+ operations/COM_SunBeamsOperation.cc
+ operations/COM_SunBeamsOperation.h
+
+ nodes/COM_CryptomatteNode.cc
+ nodes/COM_CryptomatteNode.h
+ operations/COM_CryptomatteOperation.cc
+ operations/COM_CryptomatteOperation.h
+
+ nodes/COM_CornerPinNode.cc
+ nodes/COM_CornerPinNode.h
+ nodes/COM_PlaneTrackDeformNode.cc
+ nodes/COM_PlaneTrackDeformNode.h
+
+ nodes/COM_CropNode.cc
+ nodes/COM_CropNode.h
+ operations/COM_CropOperation.cc
+ operations/COM_CropOperation.h
+
+ nodes/COM_DefocusNode.cc
+ nodes/COM_DefocusNode.h
+ nodes/COM_MovieDistortionNode.cc
+ nodes/COM_MovieDistortionNode.h
+ nodes/COM_Stabilize2dNode.cc
+ nodes/COM_Stabilize2dNode.h
+ nodes/COM_TransformNode.cc
+ nodes/COM_TransformNode.h
+
+ # color nodes
+ nodes/COM_AlphaOverNode.cc
+ nodes/COM_AlphaOverNode.h
+ nodes/COM_BrightnessNode.cc
+ nodes/COM_BrightnessNode.h
+ nodes/COM_ColorBalanceNode.cc
+ nodes/COM_ColorBalanceNode.h
+ nodes/COM_ColorCorrectionNode.cc
+ nodes/COM_ColorCorrectionNode.h
+ nodes/COM_ColorCurveNode.cc
+ nodes/COM_ColorCurveNode.h
+ nodes/COM_ColorExposureNode.cc
+ nodes/COM_ColorExposureNode.h
+ nodes/COM_ColorRampNode.cc
+ nodes/COM_ColorRampNode.h
+ nodes/COM_ColorToBWNode.cc
+ nodes/COM_ColorToBWNode.h
+ nodes/COM_ConvertAlphaNode.cc
+ nodes/COM_ConvertAlphaNode.h
+ nodes/COM_ConvertColorSpaceNode.cc
+ nodes/COM_ConvertColorSpaceNode.h
+ nodes/COM_GammaNode.cc
+ nodes/COM_GammaNode.h
+ nodes/COM_HueSaturationValueCorrectNode.cc
+ nodes/COM_HueSaturationValueCorrectNode.h
+ nodes/COM_HueSaturationValueNode.cc
+ nodes/COM_HueSaturationValueNode.h
+ nodes/COM_InvertNode.cc
+ nodes/COM_InvertNode.h
+ nodes/COM_MixNode.cc
+ nodes/COM_MixNode.h
+ nodes/COM_SetAlphaNode.cc
+ nodes/COM_SetAlphaNode.h
+ nodes/COM_TonemapNode.cc
+ nodes/COM_TonemapNode.h
+ nodes/COM_VectorCurveNode.cc
+ nodes/COM_VectorCurveNode.h
+ nodes/COM_ZCombineNode.cc
+ nodes/COM_ZCombineNode.h
+ operations/COM_TonemapOperation.cc
+ operations/COM_TonemapOperation.h
+
+ # converter nodes
+ nodes/COM_CombineColorNode.cc
+ nodes/COM_CombineColorNode.h
+ nodes/COM_CombineColorNodeLegacy.cc
+ nodes/COM_CombineColorNodeLegacy.h
+ nodes/COM_CombineXYZNode.cc
+ nodes/COM_CombineXYZNode.h
+ nodes/COM_IDMaskNode.cc
+ nodes/COM_IDMaskNode.h
+ nodes/COM_SeparateColorNode.cc
+ nodes/COM_SeparateColorNode.h
+ nodes/COM_SeparateColorNodeLegacy.cc
+ nodes/COM_SeparateColorNodeLegacy.h
+ nodes/COM_SeparateXYZNode.cc
+ nodes/COM_SeparateXYZNode.h
+
+ nodes/COM_MapRangeNode.cc
+ nodes/COM_MapRangeNode.h
+ nodes/COM_MapValueNode.cc
+ nodes/COM_MapValueNode.h
+ nodes/COM_MathNode.cc
+ nodes/COM_MathNode.h
+ nodes/COM_NormalNode.cc
+ nodes/COM_NormalNode.h
+ nodes/COM_NormalizeNode.cc
+ nodes/COM_NormalizeNode.h
+
+ operations/COM_NormalizeOperation.cc
+ operations/COM_NormalizeOperation.h
+
+ nodes/COM_PixelateNode.cc
+ nodes/COM_PixelateNode.h
+ operations/COM_PixelateOperation.cc
+ operations/COM_PixelateOperation.h
+
+ # Filter nodes
+ nodes/COM_BilateralBlurNode.cc
+ nodes/COM_BilateralBlurNode.h
+ operations/COM_BilateralBlurOperation.cc
+ operations/COM_BilateralBlurOperation.h
+ nodes/COM_VectorBlurNode.cc
+ nodes/COM_VectorBlurNode.h
+ operations/COM_VectorBlurOperation.cc
+ operations/COM_VectorBlurOperation.h
+ nodes/COM_AntiAliasingNode.cc
+ nodes/COM_AntiAliasingNode.h
+ nodes/COM_BlurNode.cc
+ nodes/COM_BlurNode.h
+ nodes/COM_BokehBlurNode.cc
+ nodes/COM_BokehBlurNode.h
+ nodes/COM_DenoiseNode.cc
+ nodes/COM_DenoiseNode.h
+ nodes/COM_DespeckleNode.cc
+ nodes/COM_DespeckleNode.h
+ nodes/COM_DilateErodeNode.cc
+ nodes/COM_DilateErodeNode.h
+ nodes/COM_DirectionalBlurNode.cc
+ nodes/COM_DirectionalBlurNode.h
+ nodes/COM_FilterNode.cc
+ nodes/COM_FilterNode.h
+ nodes/COM_InpaintNode.cc
+ nodes/COM_InpaintNode.h
+ nodes/COM_PosterizeNode.cc
+ nodes/COM_PosterizeNode.h
+
+ operations/COM_BlurBaseOperation.cc
+ operations/COM_BlurBaseOperation.h
+ operations/COM_BokehBlurOperation.cc
+ operations/COM_BokehBlurOperation.h
+ operations/COM_DirectionalBlurOperation.cc
+ operations/COM_DirectionalBlurOperation.h
+ operations/COM_FastGaussianBlurOperation.cc
+ operations/COM_FastGaussianBlurOperation.h
+ operations/COM_GammaCorrectOperation.cc
+ operations/COM_GammaCorrectOperation.h
+ operations/COM_GaussianAlphaBlurBaseOperation.cc
+ operations/COM_GaussianAlphaBlurBaseOperation.h
+ operations/COM_GaussianAlphaXBlurOperation.cc
+ operations/COM_GaussianAlphaXBlurOperation.h
+ operations/COM_GaussianAlphaYBlurOperation.cc
+ operations/COM_GaussianAlphaYBlurOperation.h
+ operations/COM_GaussianBlurBaseOperation.cc
+ operations/COM_GaussianBlurBaseOperation.h
+ operations/COM_GaussianBokehBlurOperation.cc
+ operations/COM_GaussianBokehBlurOperation.h
+ operations/COM_GaussianXBlurOperation.cc
+ operations/COM_GaussianXBlurOperation.h
+ operations/COM_GaussianYBlurOperation.cc
+ operations/COM_GaussianYBlurOperation.h
+ operations/COM_MovieClipAttributeOperation.cc
+ operations/COM_MovieClipAttributeOperation.h
+ operations/COM_MovieDistortionOperation.cc
+ operations/COM_MovieDistortionOperation.h
+ operations/COM_PosterizeOperation.cc
+ operations/COM_PosterizeOperation.h
+ operations/COM_SMAAOperation.cc
+ operations/COM_SMAAOperation.h
+ operations/COM_VariableSizeBokehBlurOperation.cc
+ operations/COM_VariableSizeBokehBlurOperation.h
+
+ # Matte nodes
+ nodes/COM_BoxMaskNode.cc
+ nodes/COM_BoxMaskNode.h
+ nodes/COM_ColorSpillNode.cc
+ nodes/COM_ColorSpillNode.h
+ nodes/COM_DoubleEdgeMaskNode.cc
+ nodes/COM_DoubleEdgeMaskNode.h
+ nodes/COM_EllipseMaskNode.cc
+ nodes/COM_EllipseMaskNode.h
+
+ operations/COM_DoubleEdgeMaskOperation.cc
+ operations/COM_DoubleEdgeMaskOperation.h
+
+
+ nodes/COM_KeyingScreenNode.cc
+ nodes/COM_KeyingScreenNode.h
+ operations/COM_KeyingScreenOperation.cc
+ operations/COM_KeyingScreenOperation.h
+
+ nodes/COM_TrackPositionNode.cc
+ nodes/COM_TrackPositionNode.h
+ operations/COM_TrackPositionOperation.cc
+ operations/COM_TrackPositionOperation.h
+
+ nodes/COM_KeyingNode.cc
+ nodes/COM_KeyingNode.h
+ operations/COM_KeyingBlurOperation.cc
+ operations/COM_KeyingBlurOperation.h
+ operations/COM_KeyingClipOperation.cc
+ operations/COM_KeyingClipOperation.h
+ operations/COM_KeyingDespillOperation.cc
+ operations/COM_KeyingDespillOperation.h
+ operations/COM_KeyingOperation.cc
+ operations/COM_KeyingOperation.h
+
+ operations/COM_ColorSpillOperation.cc
+ operations/COM_ColorSpillOperation.h
+ operations/COM_RenderLayersProg.cc
+ operations/COM_RenderLayersProg.h
+
+ operations/COM_BokehImageOperation.cc
+ operations/COM_BokehImageOperation.h
+ operations/COM_ImageOperation.cc
+ operations/COM_ImageOperation.h
+ operations/COM_MultilayerImageOperation.cc
+ operations/COM_MultilayerImageOperation.h
+ operations/COM_TextureOperation.cc
+ operations/COM_TextureOperation.h
+
+
+ operations/COM_SocketProxyOperation.cc
+ operations/COM_SocketProxyOperation.h
+
+ operations/COM_CompositorOperation.cc
+ operations/COM_CompositorOperation.h
+ operations/COM_ConvertDepthToRadiusOperation.cc
+ operations/COM_ConvertDepthToRadiusOperation.h
+ operations/COM_OutputFileMultiViewOperation.cc
+ operations/COM_OutputFileMultiViewOperation.h
+ operations/COM_OutputFileOperation.cc
+ operations/COM_OutputFileOperation.h
+ operations/COM_PreviewOperation.cc
+ operations/COM_PreviewOperation.h
+ operations/COM_SplitOperation.cc
+ operations/COM_SplitOperation.h
+ operations/COM_ViewerOperation.cc
+ operations/COM_ViewerOperation.h
+ operations/COM_ZCombineOperation.cc
+ operations/COM_ZCombineOperation.h
+
+ operations/COM_ChangeHSVOperation.cc
+ operations/COM_ChangeHSVOperation.h
+ operations/COM_ChannelMatteOperation.cc
+ operations/COM_ChannelMatteOperation.h
+ operations/COM_ChromaMatteOperation.cc
+ operations/COM_ChromaMatteOperation.h
+ operations/COM_ColorCurveOperation.cc
+ operations/COM_ColorCurveOperation.h
+ operations/COM_ColorExposureOperation.cc
+ operations/COM_ColorExposureOperation.h
+ operations/COM_ColorMatteOperation.cc
+ operations/COM_ColorMatteOperation.h
+ operations/COM_ColorRampOperation.cc
+ operations/COM_ColorRampOperation.h
+ operations/COM_CurveBaseOperation.cc
+ operations/COM_CurveBaseOperation.h
+ operations/COM_DifferenceMatteOperation.cc
+ operations/COM_DifferenceMatteOperation.h
+ operations/COM_DistanceRGBMatteOperation.cc
+ operations/COM_DistanceRGBMatteOperation.h
+ operations/COM_DistanceYCCMatteOperation.cc
+ operations/COM_DistanceYCCMatteOperation.h
+ operations/COM_HueSaturationValueCorrectOperation.cc
+ operations/COM_HueSaturationValueCorrectOperation.h
+ operations/COM_LuminanceMatteOperation.cc
+ operations/COM_LuminanceMatteOperation.h
+ operations/COM_VectorCurveOperation.cc
+ operations/COM_VectorCurveOperation.h
+
+ operations/COM_BrightnessOperation.cc
+ operations/COM_BrightnessOperation.h
+ operations/COM_ColorCorrectionOperation.cc
+ operations/COM_ColorCorrectionOperation.h
+ operations/COM_ConstantOperation.cc
+ operations/COM_ConstantOperation.h
+ operations/COM_GammaOperation.cc
+ operations/COM_GammaOperation.h
+ operations/COM_MixOperation.cc
+ operations/COM_MixOperation.h
+ operations/COM_ReadBufferOperation.cc
+ operations/COM_ReadBufferOperation.h
+ operations/COM_SetColorOperation.cc
+ operations/COM_SetColorOperation.h
+ operations/COM_SetValueOperation.cc
+ operations/COM_SetValueOperation.h
+ operations/COM_SetVectorOperation.cc
+ operations/COM_SetVectorOperation.h
+ operations/COM_WriteBufferOperation.cc
+ operations/COM_WriteBufferOperation.h
+
+ operations/COM_MathBaseOperation.cc
+ operations/COM_MathBaseOperation.h
+
+ operations/COM_AlphaOverKeyOperation.cc
+ operations/COM_AlphaOverKeyOperation.h
+ operations/COM_AlphaOverMixedOperation.cc
+ operations/COM_AlphaOverMixedOperation.h
+ operations/COM_AlphaOverPremultiplyOperation.cc
+ operations/COM_AlphaOverPremultiplyOperation.h
+
+ operations/COM_ColorBalanceASCCDLOperation.cc
+ operations/COM_ColorBalanceASCCDLOperation.h
+ operations/COM_ColorBalanceLGGOperation.cc
+ operations/COM_ColorBalanceLGGOperation.h
+ operations/COM_InvertOperation.cc
+ operations/COM_InvertOperation.h
+ operations/COM_MapRangeOperation.cc
+ operations/COM_MapRangeOperation.h
+ operations/COM_MapValueOperation.cc
+ operations/COM_MapValueOperation.h
+ operations/COM_SetAlphaMultiplyOperation.cc
+ operations/COM_SetAlphaMultiplyOperation.h
+ operations/COM_SetAlphaReplaceOperation.cc
+ operations/COM_SetAlphaReplaceOperation.h
+
+ # Distort operation
+ operations/COM_DisplaceOperation.cc
+ operations/COM_DisplaceOperation.h
+ operations/COM_DisplaceSimpleOperation.cc
+ operations/COM_DisplaceSimpleOperation.h
+ operations/COM_FlipOperation.cc
+ operations/COM_FlipOperation.h
+ operations/COM_MapUVOperation.cc
+ operations/COM_MapUVOperation.h
+ operations/COM_PlaneCornerPinOperation.cc
+ operations/COM_PlaneCornerPinOperation.h
+ operations/COM_PlaneDistortCommonOperation.cc
+ operations/COM_PlaneDistortCommonOperation.h
+ operations/COM_PlaneTrackOperation.cc
+ operations/COM_PlaneTrackOperation.h
+ operations/COM_ProjectorLensDistortionOperation.cc
+ operations/COM_ProjectorLensDistortionOperation.h
+ operations/COM_RotateOperation.cc
+ operations/COM_RotateOperation.h
+ operations/COM_ScaleOperation.cc
+ operations/COM_ScaleOperation.h
+ operations/COM_ScreenLensDistortionOperation.cc
+ operations/COM_ScreenLensDistortionOperation.h
+ operations/COM_TransformOperation.cc
+ operations/COM_TransformOperation.h
+ operations/COM_TranslateOperation.cc
+ operations/COM_TranslateOperation.h
+ operations/COM_WrapOperation.cc
+ operations/COM_WrapOperation.h
+
+ # Filter operations
+ operations/COM_ConvolutionEdgeFilterOperation.cc
+ operations/COM_ConvolutionEdgeFilterOperation.h
+ operations/COM_ConvolutionFilterOperation.cc
+ operations/COM_ConvolutionFilterOperation.h
+ operations/COM_DenoiseOperation.cc
+ operations/COM_DenoiseOperation.h
+ operations/COM_DespeckleOperation.cc
+ operations/COM_DespeckleOperation.h
+ operations/COM_DilateErodeOperation.cc
+ operations/COM_DilateErodeOperation.h
+ operations/COM_GlareBaseOperation.cc
+ operations/COM_GlareBaseOperation.h
+ operations/COM_GlareFogGlowOperation.cc
+ operations/COM_GlareFogGlowOperation.h
+ operations/COM_GlareGhostOperation.cc
+ operations/COM_GlareGhostOperation.h
+ operations/COM_GlareSimpleStarOperation.cc
+ operations/COM_GlareSimpleStarOperation.h
+ operations/COM_GlareStreaksOperation.cc
+ operations/COM_GlareStreaksOperation.h
+ operations/COM_GlareThresholdOperation.cc
+ operations/COM_GlareThresholdOperation.h
+ operations/COM_InpaintOperation.cc
+ operations/COM_InpaintOperation.h
+ operations/COM_SetSamplerOperation.cc
+ operations/COM_SetSamplerOperation.h
+
+
+ # Convert operations
+ operations/COM_ConvertOperation.cc
+ operations/COM_ConvertOperation.h
+ operations/COM_IDMaskOperation.cc
+ operations/COM_IDMaskOperation.h
+
+ operations/COM_ConvertColorSpaceOperation.cc
+ operations/COM_ConvertColorSpaceOperation.h
+ operations/COM_DotproductOperation.cc
+ operations/COM_DotproductOperation.h
+
+ # Matte operation
+ operations/COM_BoxMaskOperation.cc
+ operations/COM_BoxMaskOperation.h
+ operations/COM_EllipseMaskOperation.cc
+ operations/COM_EllipseMaskOperation.h
+
+ operations/COM_ConvertColorProfileOperation.cc
+ operations/COM_ConvertColorProfileOperation.h
+ operations/COM_MovieClipOperation.cc
+ operations/COM_MovieClipOperation.h
+
+ operations/COM_AntiAliasOperation.cc
+ operations/COM_AntiAliasOperation.h
+
+ operations/COM_MaskOperation.cc
+ operations/COM_MaskOperation.h
+ )
-if(WITH_UNITY_BUILD)
- set_target_properties(bf_compositor PROPERTIES UNITY_BUILD ON)
- set_target_properties(bf_compositor PROPERTIES UNITY_BUILD_BATCH_SIZE 10)
-endif()
+ set(LIB
+ bf_blenkernel
+ bf_blenlib
+ extern_clew
+ )
-if(COMMAND target_precompile_headers)
- target_precompile_headers(bf_compositor PRIVATE COM_precomp.h)
-endif()
+ list(APPEND INC
+ ${CMAKE_CURRENT_BINARY_DIR}/operations
+ )
-if(CXX_WARN_NO_SUGGEST_OVERRIDE)
- target_compile_options(bf_compositor PRIVATE "-Wsuggest-override")
-endif()
+ data_to_c(
+ ${CMAKE_CURRENT_SOURCE_DIR}/operations/COM_OpenCLKernels.cl
+ ${CMAKE_CURRENT_BINARY_DIR}/operations/COM_OpenCLKernels.cl.h
+ SRC
+ )
-add_dependencies(bf_compositor smaa_areatex_header)
+ add_definitions(-DCL_USE_DEPRECATED_OPENCL_1_1_APIS)
-if(WITH_GTESTS)
- set(TEST_SRC
- tests/COM_BufferArea_test.cc
- tests/COM_BufferRange_test.cc
- tests/COM_BuffersIterator_test.cc
- tests/COM_NodeOperation_test.cc
+ set(GENSRC_DIR ${CMAKE_CURRENT_BINARY_DIR}/operations)
+ set(GENSRC ${GENSRC_DIR}/COM_SMAAAreaTexture.h)
+ add_custom_command(
+ OUTPUT ${GENSRC}
+ COMMAND ${CMAKE_COMMAND} -E make_directory ${GENSRC_DIR}
+ COMMAND "$<TARGET_FILE:smaa_areatex>" ${GENSRC}
+ DEPENDS smaa_areatex
)
- set(TEST_INC
+ add_custom_target(smaa_areatex_header
+ SOURCES ${GENSRC}
)
- set(TEST_LIB
- bf_compositor
+ list(APPEND SRC
+ ${GENSRC}
)
- include(GTestTesting)
- blender_add_test_lib(bf_compositor_tests "${TEST_SRC}" "${INC};${TEST_INC}" "${INC_SYS}" "${LIB};${TEST_LIB}")
-endif()
+ unset(GENSRC)
+ unset(GENSRC_DIR)
+
+ if(WITH_OPENIMAGEDENOISE)
+ add_definitions(-DWITH_OPENIMAGEDENOISE)
+ add_definitions(-DOIDN_STATIC_LIB)
+ list(APPEND INC_SYS
+ ${OPENIMAGEDENOISE_INCLUDE_DIRS}
+ ${TBB_INCLUDE_DIRS}
+ )
+ list(APPEND LIB
+ ${OPENIMAGEDENOISE_LIBRARIES}
+ ${TBB_LIBRARIES}
+ )
+ endif()
+
+ blender_add_lib(bf_compositor "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
+
+ if(WITH_UNITY_BUILD)
+ set_target_properties(bf_compositor PROPERTIES UNITY_BUILD ON)
+ set_target_properties(bf_compositor PROPERTIES UNITY_BUILD_BATCH_SIZE 10)
+ endif()
+
+ if(COMMAND target_precompile_headers)
+ target_precompile_headers(bf_compositor PRIVATE COM_precomp.h)
+ endif()
+
+ if(CXX_WARN_NO_SUGGEST_OVERRIDE)
+ target_compile_options(bf_compositor PRIVATE "-Wsuggest-override")
+ endif()
+
+ add_dependencies(bf_compositor smaa_areatex_header)
+
+ if(WITH_GTESTS)
+ set(TEST_SRC
+ tests/COM_BufferArea_test.cc
+ tests/COM_BufferRange_test.cc
+ tests/COM_BuffersIterator_test.cc
+ tests/COM_NodeOperation_test.cc
+ )
+ set(TEST_INC
+ )
+ set(TEST_LIB
+ bf_compositor
+ )
+ include(GTestTesting)
+ blender_add_test_lib(bf_compositor_tests "${TEST_SRC}" "${INC};${TEST_INC}" "${INC_SYS}" "${LIB};${TEST_LIB}")
+ endif()
+
+ # Needed so we can use dna_type_offsets.h for defaults initialization.
+ add_dependencies(bf_compositor bf_dna)
+ # RNA_prototypes.h
+ add_dependencies(bf_compositor bf_rna)
-# Needed so we can use dna_type_offsets.h for defaults initialization.
-add_dependencies(bf_compositor bf_dna)
-# RNA_prototypes.h
-add_dependencies(bf_compositor bf_rna)
+# End WITH_COMPOSITOR_CPU.
+endif()
diff --git a/source/blender/compositor/nodes/COM_OutputFileNode.cc b/source/blender/compositor/nodes/COM_OutputFileNode.cc
index f69511d88e6..a62d21bb657 100644
--- a/source/blender/compositor/nodes/COM_OutputFileNode.cc
+++ b/source/blender/compositor/nodes/COM_OutputFileNode.cc
@@ -37,6 +37,10 @@ void OutputFileNode::map_input_sockets(NodeConverter &converter,
void OutputFileNode::add_preview_to_first_linked_input(NodeConverter &converter) const
{
+ if (get_input_sockets().is_empty()) {
+ return;
+ }
+
NodeInput *first_socket = this->get_input_socket(0);
if (first_socket->is_linked()) {
converter.add_node_input_preview(first_socket);
diff --git a/source/blender/compositor/operations/COM_MovieClipOperation.cc b/source/blender/compositor/operations/COM_MovieClipOperation.cc
index d7cf41cf422..b62d972e807 100644
--- a/source/blender/compositor/operations/COM_MovieClipOperation.cc
+++ b/source/blender/compositor/operations/COM_MovieClipOperation.cc
@@ -74,7 +74,7 @@ void MovieClipBaseOperation::execute_pixel_sampled(float output[4],
zero_v4(output);
}
else if (ibuf->rect == nullptr && ibuf->rect_float == nullptr) {
- /* Happens for multilayer exr, i.e. */
+ /* Happens for multi-layer EXR, i.e. */
zero_v4(output);
}
else {
diff --git a/source/blender/compositor/operations/COM_ScaleOperation.cc b/source/blender/compositor/operations/COM_ScaleOperation.cc
index 1957c5eb5fc..2a2aff31893 100644
--- a/source/blender/compositor/operations/COM_ScaleOperation.cc
+++ b/source/blender/compositor/operations/COM_ScaleOperation.cc
@@ -7,7 +7,7 @@
namespace blender::compositor {
#define USE_FORCE_BILINEAR
-/* XXX(campbell): ignore input and use default from old compositor,
+/* XXX(@campbellbarton): ignore input and use default from old compositor,
* could become an option like the transform node.
*
* NOTE: use bilinear because bicubic makes fuzzy even when not scaling at all (1:1)
diff --git a/source/blender/compositor/realtime_compositor/CMakeLists.txt b/source/blender/compositor/realtime_compositor/CMakeLists.txt
new file mode 100644
index 00000000000..9fe156c3ef2
--- /dev/null
+++ b/source/blender/compositor/realtime_compositor/CMakeLists.txt
@@ -0,0 +1,66 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+set(INC
+ .
+ ../../gpu
+ ../../nodes
+ ../../imbuf
+ ../../blenlib
+ ../../makesdna
+ ../../makesrna
+ ../../blenkernel
+ ../../gpu/intern
+ ../../../../intern/guardedalloc
+)
+
+
+set(SRC
+ intern/compile_state.cc
+ intern/context.cc
+ intern/conversion_operation.cc
+ intern/domain.cc
+ intern/evaluator.cc
+ intern/input_single_value_operation.cc
+ intern/node_operation.cc
+ intern/operation.cc
+ intern/realize_on_domain_operation.cc
+ intern/reduce_to_single_value_operation.cc
+ intern/result.cc
+ intern/scheduler.cc
+ intern/shader_node.cc
+ intern/shader_operation.cc
+ intern/simple_operation.cc
+ intern/static_shader_manager.cc
+ intern/texture_pool.cc
+ intern/utilities.cc
+
+ COM_compile_state.hh
+ COM_context.hh
+ COM_conversion_operation.hh
+ COM_domain.hh
+ COM_evaluator.hh
+ COM_input_descriptor.hh
+ COM_input_single_value_operation.hh
+ COM_node_operation.hh
+ COM_operation.hh
+ COM_realize_on_domain_operation.hh
+ COM_reduce_to_single_value_operation.hh
+ COM_result.hh
+ COM_scheduler.hh
+ COM_shader_node.hh
+ COM_shader_operation.hh
+ COM_simple_operation.hh
+ COM_static_shader_manager.hh
+ COM_texture_pool.hh
+ COM_utilities.hh
+)
+
+set(LIB
+ bf_gpu
+ bf_nodes
+ bf_imbuf
+ bf_blenlib
+ bf_blenkernel
+)
+
+blender_add_lib(bf_realtime_compositor "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/source/blender/compositor/realtime_compositor/COM_compile_state.hh b/source/blender/compositor/realtime_compositor/COM_compile_state.hh
new file mode 100644
index 00000000000..ed6ad414e3b
--- /dev/null
+++ b/source/blender/compositor/realtime_compositor/COM_compile_state.hh
@@ -0,0 +1,170 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#pragma once
+
+#include "BLI_map.hh"
+
+#include "NOD_derived_node_tree.hh"
+
+#include "COM_domain.hh"
+#include "COM_node_operation.hh"
+#include "COM_scheduler.hh"
+#include "COM_shader_operation.hh"
+
+namespace blender::realtime_compositor {
+
+using namespace nodes::derived_node_tree_types;
+
+/* ------------------------------------------------------------------------------------------------
+ * Compile State
+ *
+ * The compile state is a utility class used to track the state of compilation when compiling the
+ * node tree. In particular, it tracks two important pieces of information, each of which is
+ * described in one of the following sections.
+ *
+ * First, it stores a mapping between all nodes and the operations they were compiled into. The
+ * mapping are stored independently depending on the type of the operation in the node_operations_
+ * and shader_operations_ maps. So those two maps are mutually exclusive. The compiler should call
+ * the map_node_to_node_operation and map_node_to_shader_operation methods to populate those maps
+ * as soon as it compiles a node or multiple nodes into an operation. Those maps are used to
+ * retrieve the results of outputs linked to the inputs of operations. For more details, see the
+ * get_result_from_output_socket method. For the node tree shown below, nodes 1, 2, and 6 are
+ * mapped to their compiled operations in the node_operation_ map. While nodes 3 and 4 are both
+ * mapped to the first shader operation, and node 5 is mapped to the second shader operation in the
+ * shader_operations_ map.
+ *
+ * Shader Operation 1 Shader Operation 2
+ * +-----------------------------------+ +------------------+
+ * .------------. | .------------. .------------. | | .------------. | .------------.
+ * | Node 1 | | | Node 3 | | Node 4 | | | | Node 5 | | | Node 6 |
+ * | |----|--| |--| |---|-----|--| |--|--| |
+ * | | .-|--| | | | | .--|--| | | | |
+ * '------------' | | '------------' '------------' | | | '------------' | '------------'
+ * | +-----------------------------------+ | +------------------+
+ * .------------. | |
+ * | Node 2 | | |
+ * | |--'----------------------------------------'
+ * | |
+ * '------------'
+ *
+ * Second, it stores the shader compile unit as well as its domain. One should first go over the
+ * discussion in COM_evaluator.hh for a high level description of the mechanism of the compile
+ * unit. The one important detail in this class is the should_compile_shader_compile_unit method,
+ * which implements the criteria of whether the compile unit should be compiled given the node
+ * currently being processed as an argument. Those criteria are described as follows. If the
+ * compile unit is empty as is the case when processing nodes 1, 2, and 3, then it plainly
+ * shouldn't be compiled. If the given node is not a shader node, then it can't be added to the
+ * compile unit and the unit is considered complete and should be compiled, as is the case when
+ * processing node 6. If the computed domain of the given node is not compatible with the domain of
+ * the compiled unit, then it can't be added to the unit and the unit is considered complete and
+ * should be compiled, as is the case when processing node 5, more on this in the next section.
+ * Otherwise, the given node is compatible with the compile unit and can be added to it, so the
+ * unit shouldn't be compiled just yet, as is the case when processing node 4.
+ *
+ * Special attention should be given to the aforementioned domain compatibility criterion. One
+ * should first go over the discussion in COM_domain.hh for more information on domains. When a
+ * compile unit gets eventually compiled to a shader operation, that operation will have a certain
+ * operation domain, and any node that gets added to the compile unit should itself have a computed
+ * node domain that is compatible with that operation domain, otherwise, had the node been compiled
+ * into its own operation separately, the result would have been be different. For instance,
+ * consider the above node tree where node 1 outputs a 100x100 result, node 2 outputs a 50x50
+ * result, the first input in node 3 has the highest domain priority, and the second input in node
+ * 5 has the highest domain priority. In this case, shader operation 1 will output a 100x100
+ * result, and shader operation 2 will output a 50x50 result, because that's the computed operation
+ * domain for each of them. So node 6 will get a 50x50 result. Now consider the same node tree, but
+ * where all three nodes 3, 4, and 5 were compiled into a single shader operation as shown the node
+ * tree below. In that case, shader operation 1 will output a 100x100 result, because that's its
+ * computed operation domain. So node 6 will get a 100x100 result. As can be seen, the final result
+ * is different even though the node tree is the same. That's why the compiler can decide to
+ * compile the compile unit early even though further nodes can still be technically added to it.
+ *
+ * Shader Operation 1
+ * +------------------------------------------------------+
+ * .------------. | .------------. .------------. .------------. | .------------.
+ * | Node 1 | | | Node 3 | | Node 4 | | Node 5 | | | Node 6 |
+ * | |----|--| |--| |------| |--|--| |
+ * | | .-|--| | | | .---| | | | |
+ * '------------' | | '------------' '------------' | '------------' | '------------'
+ * | +----------------------------------|-------------------+
+ * .------------. | |
+ * | Node 2 | | |
+ * | |--'------------------------------------'
+ * | |
+ * '------------'
+ *
+ * To check for the domain compatibility between the compile unit and the node being processed,
+ * the domain of the compile unit is assumed to be the domain of the first node whose computed
+ * domain is not an identity domain. Identity domains corresponds to single value results, so those
+ * are always compatible with any domain. The domain of the compile unit is computed and set in
+ * the add_node_to_shader_compile_unit method. When processing a node, the computed domain of node
+ * is compared to the compile unit domain in the should_compile_shader_compile_unit method, noting
+ * that identity domains are always compatible. Node domains are computed in the
+ * compute_shader_node_domain method, which is analogous to Operation::compute_domain for nodes
+ * that are not yet compiled. */
+class CompileState {
+ private:
+ /* A reference to the node execution schedule that is being compiled. */
+ const Schedule &schedule_;
+ /* Those two maps associate each node with the operation it was compiled into. Each node is
+ * either compiled into a node operation and added to node_operations, or compiled into a shader
+ * operation and added to shader_operations. Those maps are used to retrieve the results of
+ * outputs linked to the inputs of operations. See the get_result_from_output_socket method for
+ * more information. */
+ Map<DNode, NodeOperation *> node_operations_;
+ Map<DNode, ShaderOperation *> shader_operations_;
+ /* A contiguous subset of the node execution schedule that contains the group of nodes that will
+ * be compiled together into a Shader Operation. See the discussion in COM_evaluator.hh for
+ * more information. */
+ ShaderCompileUnit shader_compile_unit_;
+ /* The domain of the shader compile unit. */
+ Domain shader_compile_unit_domain_ = Domain::identity();
+
+ public:
+ /* Construct a compile state from the node execution schedule being compiled. */
+ CompileState(const Schedule &schedule);
+
+ /* Get a reference to the node execution schedule being compiled. */
+ const Schedule &get_schedule();
+
+ /* Add an association between the given node and the give node operation that the node was
+ * compiled into in the node_operations_ map. */
+ void map_node_to_node_operation(DNode node, NodeOperation *operation);
+
+ /* Add an association between the given node and the give shader operation that the node was
+ * compiled into in the shader_operations_ map. */
+ void map_node_to_shader_operation(DNode node, ShaderOperation *operation);
+
+ /* Returns a reference to the result of the operation corresponding to the given output that the
+ * given output's node was compiled to. */
+ Result &get_result_from_output_socket(DOutputSocket output);
+
+ /* Add the given node to the compile unit. And if the domain of the compile unit is not yet
+ * determined or was determined to be an identity domain, update it to the computed domain for
+ * the give node. */
+ void add_node_to_shader_compile_unit(DNode node);
+
+ /* Get a reference to the shader compile unit. */
+ ShaderCompileUnit &get_shader_compile_unit();
+
+ /* Clear the compile unit. This should be called once the compile unit is compiled to ready it to
+ * track the next potential compile unit. */
+ void reset_shader_compile_unit();
+
+ /* Determines if the compile unit should be compiled based on a number of criteria give the node
+ * currently being processed. Those criteria are as follows:
+ * - If compile unit is empty, then it can't and shouldn't be compiled.
+ * - If the given node is not a shader node, then it can't be added to the compile unit
+ * and the unit is considered complete and should be compiled.
+ * - If the computed domain of the given node is not compatible with the domain of the compile
+ * unit, then it can't be added to it and the unit is considered complete and should be
+ * compiled. */
+ bool should_compile_shader_compile_unit(DNode node);
+
+ private:
+ /* Compute the node domain of the given shader node. This is analogous to the
+ * Operation::compute_domain method, except it is computed from the node itself as opposed to a
+ * compiled operation. See the discussion in COM_domain.hh for more information. */
+ Domain compute_shader_node_domain(DNode node);
+};
+
+} // namespace blender::realtime_compositor
diff --git a/source/blender/compositor/realtime_compositor/COM_context.hh b/source/blender/compositor/realtime_compositor/COM_context.hh
new file mode 100644
index 00000000000..b5c8cea641f
--- /dev/null
+++ b/source/blender/compositor/realtime_compositor/COM_context.hh
@@ -0,0 +1,72 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#pragma once
+
+#include "BLI_math_vec_types.hh"
+#include "BLI_string_ref.hh"
+
+#include "DNA_scene_types.h"
+
+#include "GPU_texture.h"
+
+#include "COM_static_shader_manager.hh"
+#include "COM_texture_pool.hh"
+
+namespace blender::realtime_compositor {
+
+/* ------------------------------------------------------------------------------------------------
+ * Context
+ *
+ * A Context is an abstract class that is implemented by the caller of the evaluator to provide the
+ * necessary data and functionalities for the correct operation of the evaluator. This includes
+ * providing input data like render passes and the active scene, as well as references to the data
+ * where the output of the evaluator will be written. The class also provides a reference to the
+ * texture pool which should be implemented by the caller and provided during construction.
+ * Finally, the class have an instance of a static shader manager for convenient shader
+ * acquisition. */
+class Context {
+ private:
+ /* A texture pool that can be used to allocate textures for the compositor efficiently. */
+ TexturePool &texture_pool_;
+ /* A static shader manager that can be used to acquire shaders for the compositor efficiently. */
+ StaticShaderManager shader_manager_;
+
+ public:
+ Context(TexturePool &texture_pool);
+
+ /* Get the active compositing scene. */
+ virtual const Scene *get_scene() const = 0;
+
+ /* Get the dimensions of the output. */
+ virtual int2 get_output_size() = 0;
+
+ /* Get the texture representing the output where the result of the compositor should be
+ * written. This should be called by output nodes to get their target texture. */
+ virtual GPUTexture *get_output_texture() = 0;
+
+ /* Get the texture where the given render pass is stored. This should be called by the Render
+ * Layer node to populate its outputs. */
+ virtual GPUTexture *get_input_texture(int view_layer, eScenePassType pass_type) = 0;
+
+ /* Get the name of the view currently being rendered. */
+ virtual StringRef get_view_name() = 0;
+
+ /* Set an info message. This is called by the compositor evaluator to inform or warn the user
+ * about something, typically an error. The implementation should display the message in an
+ * appropriate place, which can be directly in the UI or just logged to the output stream. */
+ virtual void set_info_message(StringRef message) const = 0;
+
+ /* Get the current frame number of the active scene. */
+ int get_frame_number() const;
+
+ /* Get the current time in seconds of the active scene. */
+ float get_time() const;
+
+ /* Get a reference to the texture pool of this context. */
+ TexturePool &texture_pool();
+
+ /* Get a reference to the static shader manager of this context. */
+ StaticShaderManager &shader_manager();
+};
+
+} // namespace blender::realtime_compositor
diff --git a/source/blender/compositor/realtime_compositor/COM_conversion_operation.hh b/source/blender/compositor/realtime_compositor/COM_conversion_operation.hh
new file mode 100644
index 00000000000..15e1d0722ea
--- /dev/null
+++ b/source/blender/compositor/realtime_compositor/COM_conversion_operation.hh
@@ -0,0 +1,126 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#pragma once
+
+#include "GPU_shader.h"
+
+#include "COM_context.hh"
+#include "COM_input_descriptor.hh"
+#include "COM_result.hh"
+#include "COM_simple_operation.hh"
+
+namespace blender::realtime_compositor {
+
+/* -------------------------------------------------------------------------------------------------
+ * Conversion Operation
+ *
+ * A simple operation that converts a result from a certain type to another. See the derived
+ * classes for more details. */
+class ConversionOperation : public SimpleOperation {
+ public:
+ using SimpleOperation::SimpleOperation;
+
+ /* If the input result is a single value, execute_single is called. Otherwise, the shader
+ * provided by get_conversion_shader is dispatched. */
+ void execute() override;
+
+ /* Determine if a conversion operation is needed for the input with the given result and
+ * descriptor. If it is not needed, return a null pointer. If it is needed, return an instance of
+ * the appropriate conversion operation. */
+ static SimpleOperation *construct_if_needed(Context &context,
+ const Result &input_result,
+ const InputDescriptor &input_descriptor);
+
+ protected:
+ /* Convert the input single value result to the output single value result. */
+ virtual void execute_single(const Result &input, Result &output) = 0;
+
+ /* Get the shader the will be used for conversion. */
+ virtual GPUShader *get_conversion_shader() const = 0;
+};
+
+/* -------------------------------------------------------------------------------------------------
+ * Convert Float To Vector Operation
+ *
+ * Takes a float result and outputs a vector result. All three components of the output are filled
+ * with the input float. */
+class ConvertFloatToVectorOperation : public ConversionOperation {
+ public:
+ ConvertFloatToVectorOperation(Context &context);
+
+ void execute_single(const Result &input, Result &output) override;
+
+ GPUShader *get_conversion_shader() const override;
+};
+
+/* -------------------------------------------------------------------------------------------------
+ * Convert Float To Color Operation
+ *
+ * Takes a float result and outputs a color result. All three color channels of the output are
+ * filled with the input float and the alpha channel is set to 1. */
+class ConvertFloatToColorOperation : public ConversionOperation {
+ public:
+ ConvertFloatToColorOperation(Context &context);
+
+ void execute_single(const Result &input, Result &output) override;
+
+ GPUShader *get_conversion_shader() const override;
+};
+
+/* -------------------------------------------------------------------------------------------------
+ * Convert Color To Float Operation
+ *
+ * Takes a color result and outputs a float result. The output is the average of the three color
+ * channels, the alpha channel is ignored. */
+class ConvertColorToFloatOperation : public ConversionOperation {
+ public:
+ ConvertColorToFloatOperation(Context &context);
+
+ void execute_single(const Result &input, Result &output) override;
+
+ GPUShader *get_conversion_shader() const override;
+};
+
+/* -------------------------------------------------------------------------------------------------
+ * Convert Color To Vector Operation
+ *
+ * Takes a color result and outputs a vector result. The output is a copy of the three color
+ * channels to the three vector components. */
+class ConvertColorToVectorOperation : public ConversionOperation {
+ public:
+ ConvertColorToVectorOperation(Context &context);
+
+ void execute_single(const Result &input, Result &output) override;
+
+ GPUShader *get_conversion_shader() const override;
+};
+
+/* -------------------------------------------------------------------------------------------------
+ * Convert Vector To Float Operation
+ *
+ * Takes a vector result and outputs a float result. The output is the average of the three
+ * components. */
+class ConvertVectorToFloatOperation : public ConversionOperation {
+ public:
+ ConvertVectorToFloatOperation(Context &context);
+
+ void execute_single(const Result &input, Result &output) override;
+
+ GPUShader *get_conversion_shader() const override;
+};
+
+/* -------------------------------------------------------------------------------------------------
+ * Convert Vector To Color Operation
+ *
+ * Takes a vector result and outputs a color result. The output is a copy of the three vector
+ * components to the three color channels with the alpha channel set to 1. */
+class ConvertVectorToColorOperation : public ConversionOperation {
+ public:
+ ConvertVectorToColorOperation(Context &context);
+
+ void execute_single(const Result &input, Result &output) override;
+
+ GPUShader *get_conversion_shader() const override;
+};
+
+} // namespace blender::realtime_compositor
diff --git a/source/blender/compositor/realtime_compositor/COM_domain.hh b/source/blender/compositor/realtime_compositor/COM_domain.hh
new file mode 100644
index 00000000000..a4f9eb68db4
--- /dev/null
+++ b/source/blender/compositor/realtime_compositor/COM_domain.hh
@@ -0,0 +1,166 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#pragma once
+
+#include <cstdint>
+
+#include "BLI_float3x3.hh"
+#include "BLI_math_vec_types.hh"
+
+namespace blender::realtime_compositor {
+
+/* Possible interpolations to use when realizing an input result of some domain on another domain.
+ * See the RealizationOptions struct for more information. */
+enum class Interpolation : uint8_t {
+ Nearest,
+ Bilinear,
+ Bicubic,
+};
+
+/* ------------------------------------------------------------------------------------------------
+ * Realization Options
+ *
+ * The options that describe how an input result prefer to be realized on some other domain. This
+ * is used by the Realize On Domain Operation to identify the appropriate method of realization.
+ * See the Domain class for more information. */
+struct RealizationOptions {
+ /* The interpolation method that should be used when performing realization. Since realizing a
+ * result involves projecting it on a different domain, which in turn, involves sampling the
+ * result at arbitrary locations, the interpolation identifies the method used for computing the
+ * value at those arbitrary locations. */
+ Interpolation interpolation = Interpolation::Nearest;
+ /* If true, the result will be repeated infinitely along the horizontal axis when realizing the
+ * result. If false, regions outside of bounds of the result along the horizontal axis will be
+ * filled with zeros. */
+ bool repeat_x = false;
+ /* If true, the result will be repeated infinitely along the vertical axis when realizing the
+ * result. If false, regions outside of bounds of the result along the vertical axis will be
+ * filled with zeros. */
+ bool repeat_y = false;
+};
+
+/* ------------------------------------------------------------------------------------------------
+ * Domain
+ *
+ * The compositor is designed in such a way as to allow compositing in an infinite virtual
+ * compositing space. Consequently, any result of an operation is not only represented by its image
+ * output, but also by its transformation in that virtual space. The transformation of the result
+ * together with the dimension of its image is stored and represented by a Domain. In the figure
+ * below, two results of different domains are illustrated on the virtual compositing space. One of
+ * the results is centered in space with an image dimension of 800px × 600px, and the other result
+ * is scaled down and translated such that it lies in the upper right quadrant of the space with an
+ * image dimension of 800px × 400px. The position of the domain is in pixel space, and the domain
+ * is considered centered if it has an identity transformation. Note that both results have the
+ * same resolution, but occupy different areas of the virtual compositing space.
+ *
+ * y
+ * ^
+ * 800px x 600px |
+ * .---------------------|---------------------.
+ * | | 800px x 600px |
+ * | | .-------------. |
+ * | | | | |
+ * | | '-------------' |
+ * ------|---------------------|---------------------|------> x
+ * | | |
+ * | | |
+ * | | |
+ * | | |
+ * '---------------------|---------------------'
+ * |
+ *
+ * By default, results have domains of identity transformations, that is, they are centered in
+ * space, but a transformation operation like the rotate, translate, or transform operations will
+ * adjust the transformation to make the result reside somewhere different in space. The domain of
+ * a single value result is irrelevant and always set to an identity domain.
+ *
+ * An operation is typically only concerned about a subset of the virtual compositing space, this
+ * subset is represented by a domain which is called the Operation Domain. It follows that before
+ * the operation itself is executed, inputs will typically be realized on the operation domain to
+ * be in the same domain and have the same dimension as that of the operation domain. This process
+ * is called Domain Realization and is implemented using an operation called the Realize On Domain
+ * Operation. Realization involves projecting the result onto the target domain, copying the area
+ * of the result that intersects the target domain, and filling the rest with zeros or repetitions
+ * of the result depending on the realization options that can be set by the user. Consequently,
+ * operations can generally expect their inputs to have the same dimension and can operate on them
+ * directly and transparently. For instance, if an operation takes both results illustrated in
+ * the figure above, and the operation has an operation domain that matches the bigger domain, the
+ * result with the bigger domain will not need to be realized because it already has a domain that
+ * matches that of the operation domain, but the result with the smaller domain will have to be
+ * realized into a new result that has the same domain as the domain of the bigger result. Assuming
+ * no repetition, the output of the realization will be an all zeros image with dimension 800px ×
+ * 600px with a small scaled version of the smaller result copied into the upper right quadrant of
+ * the image. The following figure illustrates the realization process on a different set of
+ * results
+ *
+ * Realized Result
+ * +-------------+ +-------------+
+ * | Operation | | |
+ * | Domain | | Zeros |
+ * | | ----> | |
+ * +-----|-----+ | |-----+ |
+ * | | C | | | C | |
+ * | +-----|-------+ +-----'-------+
+ * | Domain Of |
+ * | Input |
+ * +-----------+
+ *
+ * An operation can operate in an arbitrary operation domain, but in most cases, the operation
+ * domain is inferred from the inputs of the operation. In particular, one of the inputs is said to
+ * be the Domain Input of the operation and the operation domain is inferred from its domain. It
+ * follows that this particular input will not need realization, because it already has the correct
+ * domain. The domain input selection mechanism is as follows. Each of the inputs are assigned a
+ * value by the developer called the Domain Priority, the domain input is then chosen as the
+ * non-single value input with the highest domain priority, zero being the highest priority. See
+ * Operation::compute_domain for more information.
+ *
+ * The aforementioned logic for operation domain computation is only a default that works for most
+ * cases, but an operation can override the compute_domain method to implement a different logic.
+ * For instance, output nodes have an operation domain the same size as the viewport and with an
+ * identity transformation, their operation domain doesn't depend on the inputs at all.
+ *
+ * For instance, a filter operation has two inputs, a factor and a color, the latter of which is
+ * assigned a domain priority of 0 and the former is assigned a domain priority of 1. If the color
+ * input is not a single value input, then the color input is considered to be the domain input of
+ * the operation and the operation domain is computed to be the same domain as the color input,
+ * because it has the highest priority. It follows that if the factor input has a different domain
+ * than the computed domain of the operation, it will be projected and realized on it to have the
+ * same domain as described above. On the other hand, if the color input is a single value input,
+ * then the factor input is considered to be the domain input and the operation domain will be the
+ * same as the domain of the factor input, because it has the second highest domain priority.
+ * Finally, if both inputs are single value inputs, the operation domain will be an identity domain
+ * and is irrelevant, because the output will be a domain-less single value. */
+class Domain {
+ public:
+ /* The size of the domain in pixels. */
+ int2 size;
+ /* The 2D transformation of the domain defining its translation in pixels, rotation, and scale in
+ * the virtual compositing space. */
+ float3x3 transformation;
+ /* The options that describe how this domain prefer to be realized on some other domain. See the
+ * RealizationOptions struct for more information. */
+ RealizationOptions realization_options;
+
+ public:
+ /* A size only constructor that sets the transformation to identity. */
+ Domain(int2 size);
+
+ Domain(int2 size, float3x3 transformation);
+
+ /* Transform the domain by the given transformation. This effectively pre-multiply the given
+ * transformation by the current transformation of the domain. */
+ void transform(const float3x3 &transformation);
+
+ /* Returns a domain of size 1x1 and an identity transformation. */
+ static Domain identity();
+};
+
+/* Compare the size and transformation of the domain. The realization_options are not compared
+ * because they only describe the method of realization on another domain, which is not technically
+ * a property of the domain itself. */
+bool operator==(const Domain &a, const Domain &b);
+
+/* Inverse of the above equality operator. */
+bool operator!=(const Domain &a, const Domain &b);
+
+} // namespace blender::realtime_compositor
diff --git a/source/blender/compositor/realtime_compositor/COM_evaluator.hh b/source/blender/compositor/realtime_compositor/COM_evaluator.hh
new file mode 100644
index 00000000000..fd6feb0948b
--- /dev/null
+++ b/source/blender/compositor/realtime_compositor/COM_evaluator.hh
@@ -0,0 +1,173 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#pragma once
+
+#include <memory>
+
+#include "BLI_vector.hh"
+
+#include "DNA_node_types.h"
+
+#include "NOD_derived_node_tree.hh"
+
+#include "COM_compile_state.hh"
+#include "COM_context.hh"
+#include "COM_node_operation.hh"
+#include "COM_operation.hh"
+#include "COM_shader_operation.hh"
+
+namespace blender::realtime_compositor {
+
+using namespace nodes::derived_node_tree_types;
+
+/* ------------------------------------------------------------------------------------------------
+ * Evaluator
+ *
+ * The evaluator is the main class of the compositor and the entry point of its execution. The
+ * evaluator compiles the compositor node tree and evaluates it to compute its output. It is
+ * constructed from a compositor node tree and a compositor context. Upon calling the evaluate
+ * method, the evaluator will check if the node tree is already compiled into an operations stream,
+ * and if it is, it will go over it and evaluate the operations in order. It is then the
+ * responsibility of the caller to call the reset method when the node tree changes to invalidate
+ * the operations stream. A reset is also required if the resources used by the node tree change,
+ * for instances, when the dimensions of an image used by the node tree changes. This is necessary
+ * because the evaluator compiles the node tree into an operations stream that is specifically
+ * optimized for the structure of the resources used by the node tree.
+ *
+ * Otherwise, if the node tree is not yet compiled, the evaluator will compile it into an
+ * operations stream, evaluating the operations in the process. It should be noted that operations
+ * are evaluated as soon as they are compiled, as opposed to compiling the whole operations stream
+ * and then evaluating it in a separate step. This is important because, as mentioned before, the
+ * operations stream is optimized specifically for the structure of the resources used by the node
+ * tree, which is only known after the operations are evaluated. In other words, the evaluator uses
+ * the evaluated results of previously compiled operations to compile the operations that follow
+ * them in an optimized manner.
+ *
+ * Compilation starts by computing an optimized node execution schedule by calling the
+ * compute_schedule function, see the discussion in COM_scheduler.hh for more details. For the node
+ * tree shown below, the execution schedule is denoted by the node numbers. The compiler then goes
+ * over the execution schedule in order and compiles each node into either a Node Operation or a
+ * Shader Operation, depending on the node type, see the is_shader_node function. A Shader
+ * operation is constructed from a group of nodes forming a contiguous subset of the node execution
+ * schedule. For instance, in the node tree shown below, nodes 3 and 4 are compiled together into a
+ * shader operation and node 5 is compiled into its own shader operation, both of which are
+ * contiguous subsets of the node execution schedule. This process is described in details in the
+ * following section.
+ *
+ * Shader Operation 1 Shader Operation 2
+ * +-----------------------------------+ +------------------+
+ * .------------. | .------------. .------------. | | .------------. | .------------.
+ * | Node 1 | | | Node 3 | | Node 4 | | | | Node 5 | | | Node 6 |
+ * | |----|--| |--| |---|-----|--| |--|--| |
+ * | | .-|--| | | | | .--|--| | | | |
+ * '------------' | | '------------' '------------' | | | '------------' | '------------'
+ * | +-----------------------------------+ | +------------------+
+ * .------------. | |
+ * | Node 2 | | |
+ * | |--'----------------------------------------'
+ * | |
+ * '------------'
+ *
+ * For non shader nodes, the compilation process is straight forward, the compiler instantiates a
+ * node operation from the node, map its inputs to the results of the outputs they are linked to,
+ * and evaluates the operations. However, for shader nodes, since a group of nodes can be compiled
+ * together into a shader operation, the compilation process is a bit involved. The compiler uses
+ * an instance of the Compile State class to keep track of the compilation process. The compiler
+ * state stores the so called "shader compile unit", which is the current group of nodes that will
+ * eventually be compiled together into a shader operation. While going over the schedule, the
+ * compiler adds the shader nodes to the compile unit until it decides that the compile unit is
+ * complete and should be compiled. This is typically decided when the current node is not
+ * compatible with the compile unit and can't be added to it, only then it compiles the compile
+ * unit into a shader operation and resets it to ready it to track the next potential group of
+ * nodes that will form a shader operation. This decision is made based on various criteria in the
+ * should_compile_shader_compile_unit function. See the discussion in COM_compile_state.hh for more
+ * details of those criteria, but perhaps the most evident of which is whether the node is actually
+ * a shader node, if it isn't, then it evidently can't be added to the compile unit and the compile
+ * unit is should be compiled.
+ *
+ * For the node tree above, the compilation process is as follows. The compiler goes over the node
+ * execution schedule in order considering each node. Nodes 1 and 2 are not shader node so they are
+ * compiled into node operations and added to the operations stream. The current compile unit is
+ * empty, so it is not compiled. Node 3 is a shader node, and since the compile unit is currently
+ * empty, it is unconditionally added to it. Node 4 is a shader node, it was decided---for the sake
+ * of the demonstration---that it is compatible with the compile unit and can be added to it. Node
+ * 5 is a shader node, but it was decided---for the sake of the demonstration---that it is not
+ * compatible with the compile unit, so the compile unit is considered complete and is compiled
+ * first, adding the first shader operation to the operations stream and resetting the compile
+ * unit. Node 5 is then added to the now empty compile unit similar to node 3. Node 6 is not a
+ * shader node, so the compile unit is considered complete and is compiled first, adding the first
+ * shader operation to the operations stream and resetting the compile unit. Finally, node 6 is
+ * compiled into a node operation similar to nodes 1 and 2 and added to the operations stream. */
+class Evaluator {
+ private:
+ /* A reference to the compositor context. */
+ Context &context_;
+ /* A reference to the compositor node tree. */
+ bNodeTree &node_tree_;
+ /* The derived and reference node trees representing the compositor node tree. Those are
+ * initialized when the node tree is compiled and freed when the evaluator resets. */
+ NodeTreeRefMap node_tree_reference_map_;
+ std::unique_ptr<DerivedNodeTree> derived_node_tree_;
+ /* The compiled operations stream. This contains ordered pointers to the operations that were
+ * compiled. This is initialized when the node tree is compiled and freed when the evaluator
+ * resets. The is_compiled_ member indicates whether the operation stream can be used or needs to
+ * be compiled first. Note that the operations stream can be empty even when compiled, this can
+ * happen when the node tree is empty or invalid for instance. */
+ Vector<std::unique_ptr<Operation>> operations_stream_;
+ /* True if the node tree is already compiled into an operations stream that can be evaluated
+ * directly. False if the node tree is not compiled yet and needs to be compiled. */
+ bool is_compiled_ = false;
+
+ public:
+ /* Construct an evaluator from a compositor node tree and a context. */
+ Evaluator(Context &context, bNodeTree &node_tree);
+
+ /* Evaluate the compositor node tree. If the node tree is already compiled into an operations
+ * stream, that stream will be evaluated directly. Otherwise, the node tree will be compiled and
+ * evaluated. */
+ void evaluate();
+
+ /* Invalidate the operations stream that was compiled for the node tree. This should be called
+ * when the node tree changes or the structure of any of the resources used by it changes. By
+ * structure, we mean things like the dimensions of the used images, while changes to their
+ * contents do not necessitate a reset. */
+ void reset();
+
+ private:
+ /* Check if the compositor node tree is valid by checking if it has:
+ * - Cyclic links.
+ * - Undefined nodes or sockets.
+ * - Unsupported nodes.
+ * If the node tree is valid, true is returned. Otherwise, false is returned, and an appropriate
+ * error message is set by calling the context's set_info_message method. */
+ bool validate_node_tree();
+
+ /* Compile the node tree into an operations stream and evaluate it. */
+ void compile_and_evaluate();
+
+ /* Compile the given node into a node operation, map each input to the result of the output
+ * linked to it, update the compile state, add the newly created operation to the operations
+ * stream, and evaluate the operation. */
+ void compile_and_evaluate_node(DNode node, CompileState &compile_state);
+
+ /* Map each input of the node operation to the result of the output linked to it. Unlinked inputs
+ * are mapped to the result of a newly created Input Single Value Operation, which is added to
+ * the operations stream and evaluated. Since this method might add operations to the operations
+ * stream, the actual node operation should only be added to the stream once this method is
+ * called. */
+ void map_node_operation_inputs_to_their_results(DNode node,
+ NodeOperation *operation,
+ CompileState &compile_state);
+
+ /* Compile the shader compile unit into a shader operation, map each input of the operation to
+ * the result of the output linked to it, update the compile state, add the newly created
+ * operation to the operations stream, evaluate the operation, and finally reset the shader
+ * compile unit. */
+ void compile_and_evaluate_shader_compile_unit(CompileState &compile_state);
+
+ /* Map each input of the shader operation to the result of the output linked to it. */
+ void map_shader_operation_inputs_to_their_results(ShaderOperation *operation,
+ CompileState &compile_state);
+};
+
+} // namespace blender::realtime_compositor
diff --git a/source/blender/compositor/realtime_compositor/COM_input_descriptor.hh b/source/blender/compositor/realtime_compositor/COM_input_descriptor.hh
new file mode 100644
index 00000000000..215a92ab3b4
--- /dev/null
+++ b/source/blender/compositor/realtime_compositor/COM_input_descriptor.hh
@@ -0,0 +1,34 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#pragma once
+
+#include "COM_result.hh"
+
+namespace blender::realtime_compositor {
+
+/* ------------------------------------------------------------------------------------------------
+ * Input Descriptor
+ *
+ * A class that describes an input of an operation. */
+class InputDescriptor {
+ public:
+ /* The type of input. This may be different that the type of result that the operation will
+ * receive for the input, in which case, an implicit conversion operation will be added as an
+ * input processor to convert it to the required type. */
+ ResultType type;
+ /* If true, then the input does not need to be realized on the domain of the operation before its
+ * execution. See the discussion in COM_domain.hh for more information. */
+ bool skip_realization = false;
+ /* The priority of the input for determining the operation domain. The non-single value input
+ * with the highest priority will be used to infer the operation domain, the highest priority
+ * being zero. See the discussion in COM_domain.hh for more information. */
+ int domain_priority = 0;
+ /* If true, the input expects a single value, and if a non-single value is provided, a default
+ * single value will be used instead, see the get_<type>_value_default methods in the Result
+ * class. It follows that this also implies skip_realization, because we don't need to realize a
+ * result that will be discarded anyways. If false, the input can work with both single and
+ * non-single values. */
+ bool expects_single_value = false;
+};
+
+} // namespace blender::realtime_compositor
diff --git a/source/blender/compositor/realtime_compositor/COM_input_single_value_operation.hh b/source/blender/compositor/realtime_compositor/COM_input_single_value_operation.hh
new file mode 100644
index 00000000000..bbcd336ee11
--- /dev/null
+++ b/source/blender/compositor/realtime_compositor/COM_input_single_value_operation.hh
@@ -0,0 +1,46 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#pragma once
+
+#include "BLI_string_ref.hh"
+
+#include "NOD_derived_node_tree.hh"
+
+#include "COM_context.hh"
+#include "COM_operation.hh"
+#include "COM_result.hh"
+
+namespace blender::realtime_compositor {
+
+using namespace nodes::derived_node_tree_types;
+
+/* ------------------------------------------------------------------------------------------------
+ * Input Single Value Operation
+ *
+ * An input single value operation is an operation that outputs a single value result whose value
+ * is the value of an unlinked input socket. This is typically used to initialize the values of
+ * unlinked node input sockets. */
+class InputSingleValueOperation : public Operation {
+ private:
+ /* The identifier of the output. */
+ static const StringRef output_identifier_;
+ /* The input socket whose value will be computed as the operation's result. */
+ DInputSocket input_socket_;
+
+ public:
+ InputSingleValueOperation(Context &context, DInputSocket input_socket);
+
+ /* Allocate a single value result and set its value to the default value of the input socket. */
+ void execute() override;
+
+ /* Get a reference to the output result of the operation, this essentially calls the super
+ * get_result with the output identifier of the operation. */
+ Result &get_result();
+
+ private:
+ /* Populate the result of the operation, this essentially calls the super populate_result method
+ * with the output identifier of the operation. */
+ void populate_result(Result result);
+};
+
+} // namespace blender::realtime_compositor
diff --git a/source/blender/compositor/realtime_compositor/COM_node_operation.hh b/source/blender/compositor/realtime_compositor/COM_node_operation.hh
new file mode 100644
index 00000000000..94bc4dfd39d
--- /dev/null
+++ b/source/blender/compositor/realtime_compositor/COM_node_operation.hh
@@ -0,0 +1,56 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#pragma once
+
+#include "BLI_string_ref.hh"
+
+#include "DNA_node_types.h"
+
+#include "NOD_derived_node_tree.hh"
+
+#include "COM_context.hh"
+#include "COM_operation.hh"
+#include "COM_scheduler.hh"
+
+namespace blender::realtime_compositor {
+
+using namespace nodes::derived_node_tree_types;
+
+/* ------------------------------------------------------------------------------------------------
+ * Node Operation
+ *
+ * A node operation is a subclass of operation that nodes should implement and instantiate in the
+ * get_compositor_operation function of bNodeType, passing the inputs given to that function to the
+ * constructor. This class essentially just implements a default constructor that populates output
+ * results for all outputs of the node as well as input descriptors for all inputs of the nodes
+ * based on their socket declaration. The class also provides some utility methods for easier
+ * implementation of nodes. */
+class NodeOperation : public Operation {
+ private:
+ /* The node that this operation represents. */
+ DNode node_;
+
+ public:
+ /* Populate the output results based on the node outputs and populate the input descriptors based
+ * on the node inputs. */
+ NodeOperation(Context &context, DNode node);
+
+ /* Compute and set the initial reference counts of all the results of the operation. The
+ * reference counts of the results are the number of operations that use those results, which is
+ * computed as the number of inputs whose node is part of the schedule and is linked to the
+ * output corresponding to each result. The node execution schedule is given as an input. */
+ void compute_results_reference_counts(const Schedule &schedule);
+
+ protected:
+ /* Returns a reference to the derived node that this operation represents. */
+ const DNode &node() const;
+
+ /* Returns a reference to the node that this operation represents. */
+ const bNode &bnode() const;
+
+ /* Returns true if the output identified by the given identifier is needed and should be
+ * computed, otherwise returns false. */
+ bool should_compute_output(StringRef identifier);
+};
+
+} // namespace blender::realtime_compositor
diff --git a/source/blender/compositor/realtime_compositor/COM_operation.hh b/source/blender/compositor/realtime_compositor/COM_operation.hh
new file mode 100644
index 00000000000..38518c00c04
--- /dev/null
+++ b/source/blender/compositor/realtime_compositor/COM_operation.hh
@@ -0,0 +1,175 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#pragma once
+
+#include <memory>
+#include <string>
+
+#include "BLI_map.hh"
+#include "BLI_string_ref.hh"
+#include "BLI_vector.hh"
+
+#include "COM_context.hh"
+#include "COM_domain.hh"
+#include "COM_input_descriptor.hh"
+#include "COM_result.hh"
+#include "COM_static_shader_manager.hh"
+#include "COM_texture_pool.hh"
+
+namespace blender::realtime_compositor {
+
+class SimpleOperation;
+
+/* A type representing a vector of simple operations that store the input processors for a
+ * particular input. */
+using ProcessorsVector = Vector<std::unique_ptr<SimpleOperation>>;
+
+/* ------------------------------------------------------------------------------------------------
+ * Operation
+ *
+ * The operation is the basic unit of the compositor. The evaluator compiles the compositor node
+ * tree into an ordered stream of operations which are then executed in order during evaluation.
+ * The operation class can be sub-classed to implement a new operation. Operations have a number of
+ * inputs and outputs that are declared during construction and are identified by string
+ * identifiers. Inputs are declared by calling declare_input_descriptor providing an appropriate
+ * descriptor. Those inputs are mapped to the results computed by other operations whose outputs
+ * are linked to the inputs. Such mappings are established by the compiler during compilation by
+ * calling the map_input_to_result method. Outputs are populated by calling the populate_result
+ * method, providing a result of an appropriate type. Upon execution, the operation allocates a
+ * result for each of its outputs and computes their value based on its inputs and options.
+ *
+ * Each input may have one or more input processors, which are simple operations that process the
+ * inputs before the operation is executed, see the discussion in COM_simple_operation.hh for more
+ * information. And thus the effective input of the operation is the result of the last input
+ * processor if one exists. Input processors are added and evaluated by calling the
+ * add_and_evaluate_input_processors method, which provides a default implementation that does
+ * things like implicit conversion, domain realization, and more. This default implementation can,
+ * however, be overridden, extended, or removed. Once the input processors are added and evaluated
+ * for the first time, they are stored in the operation and future evaluations can evaluate them
+ * directly without having to add them again.
+ *
+ * The operation is evaluated by calling the evaluate method, which first adds the input processors
+ * if they weren't added already and evaluates them, then it resets the results of the operation,
+ * then it calls the execute method of the operation, and finally it releases the results mapped to
+ * the inputs to declare that they are no longer needed. */
+class Operation {
+ private:
+ /* A reference to the compositor context. This member references the same object in all
+ * operations but is included in the class for convenience. */
+ Context &context_;
+ /* A mapping between each output of the operation identified by its identifier and the result for
+ * that output. A result for each output of the operation should be constructed and added to the
+ * map during operation construction by calling the populate_result method. The results should be
+ * allocated and their contents should be computed in the execute method. */
+ Map<std::string, Result> results_;
+ /* A mapping between each input of the operation identified by its identifier and its input
+ * descriptor. Those descriptors should be declared during operation construction by calling the
+ * declare_input_descriptor method. */
+ Map<std::string, InputDescriptor> input_descriptors_;
+ /* A mapping between each input of the operation identified by its identifier and a pointer to
+ * the computed result providing its data. The mapped result is either one that was computed by
+ * another operation or one that was internally computed in the operation by the last input
+ * processor for that input. It is the responsibility of the evaluator to map the inputs to their
+ * linked results before evaluating the operation by calling the map_input_to_result method. */
+ Map<StringRef, Result *> results_mapped_to_inputs_;
+ /* A mapping between each input of the operation identified by its identifier and an ordered list
+ * of simple operations to process that input. This is initialized the first time the input
+ * processors are evaluated by calling the add_and_evaluate_input_processors method. Further
+ * evaluations will evaluate the processors directly without the need to add them again. The
+ * input_processors_added_ member indicates whether the processors were already added and can be
+ * evaluated directly or need to be added and evaluated. */
+ Map<StringRef, ProcessorsVector> input_processors_;
+ /* True if the input processors were already added and can be evaluated directly. False if the
+ * input processors are not yet added and needs to be added. */
+ bool input_processors_added_ = false;
+
+ public:
+ Operation(Context &context);
+
+ virtual ~Operation();
+
+ /* Evaluate the operation by:
+ * 1. Evaluating the input processors.
+ * 2. Resetting the results of the operation.
+ * 3. Calling the execute method of the operation.
+ * 4. Releasing the results mapped to the inputs. */
+ void evaluate();
+
+ /* Get a reference to the output result identified by the given identifier. */
+ Result &get_result(StringRef identifier);
+
+ /* Map the input identified by the given identifier to the result providing its data. See
+ * results_mapped_to_inputs_ for more details. This should be called by the evaluator to
+ * establish links between different operations. */
+ void map_input_to_result(StringRef identifier, Result *result);
+
+ protected:
+ /* Compute the operation domain of this operation. By default, this implements a default logic
+ * that infers the operation domain from the inputs, which may be overridden for a different
+ * logic. See the discussion in COM_domain.hh for the inference logic and more information. */
+ virtual Domain compute_domain();
+
+ /* Add and evaluate any needed input processors, which essentially just involves calling the
+ * add_and_evaluate_input_processor method with the needed processors. This is called before
+ * executing the operation to prepare its inputs. The class defines a default implementation
+ * which adds typically needed processors, but derived classes can override the method to have
+ * a different implementation, extend the implementation, or remove it entirely. */
+ virtual void add_and_evaluate_input_processors();
+
+ /* Given the identifier of an input of the operation and a processor operation:
+ * - Add the given processor to the list of input processors for the input.
+ * - Map the input of the processor to be the result of the last input processor or the result
+ * mapped to the input if no previous processors exists.
+ * - Switch the result mapped to the input to be the output result of the processor.
+ * - Evaluate the processor. */
+ void add_and_evaluate_input_processor(StringRef identifier, SimpleOperation *processor);
+
+ /* This method should allocate the operation results, execute the operation, and compute the
+ * output results. */
+ virtual void execute() = 0;
+
+ /* Get a reference to the result connected to the input identified by the given identifier. */
+ Result &get_input(StringRef identifier) const;
+
+ /* Switch the result mapped to the input identified by the given identifier with the given
+ * result. */
+ void switch_result_mapped_to_input(StringRef identifier, Result *result);
+
+ /* Add the given result to the results_ map identified by the given output identifier. This
+ * should be called during operation construction for all outputs. The provided result shouldn't
+ * be allocated or initialized, this will happen later during execution. */
+ void populate_result(StringRef identifier, Result result);
+
+ /* Declare the descriptor of the input identified by the given identifier to be the given
+ * descriptor. Adds the given descriptor to the input_descriptors_ map identified by the given
+ * input identifier. This should be called during operation constructor for all inputs. */
+ void declare_input_descriptor(StringRef identifier, InputDescriptor descriptor);
+
+ /* Get a reference to the descriptor of the input identified by the given identified. */
+ InputDescriptor &get_input_descriptor(StringRef identifier);
+
+ /* Returns a reference to the compositor context. */
+ Context &context();
+
+ /* Returns a reference to the texture pool of the compositor context. */
+ TexturePool &texture_pool() const;
+
+ /* Returns a reference to the shader manager of the compositor context. */
+ StaticShaderManager &shader_manager() const;
+
+ private:
+ /* Evaluate the input processors. If the input processors were already added they will be
+ * evaluated directly. Otherwise, the input processors will be added and evaluated. */
+ void evaluate_input_processors();
+
+ /* Resets the results of the operation. See the reset method in the Result class for more
+ * information. */
+ void reset_results();
+
+ /* Release the results that are mapped to the inputs of the operation. This is called after the
+ * evaluation of the operation to declare that the results are no longer needed by this
+ * operation. */
+ void release_inputs();
+};
+
+} // namespace blender::realtime_compositor
diff --git a/source/blender/compositor/realtime_compositor/COM_realize_on_domain_operation.hh b/source/blender/compositor/realtime_compositor/COM_realize_on_domain_operation.hh
new file mode 100644
index 00000000000..5a842e16008
--- /dev/null
+++ b/source/blender/compositor/realtime_compositor/COM_realize_on_domain_operation.hh
@@ -0,0 +1,49 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#pragma once
+
+#include "GPU_shader.h"
+
+#include "COM_context.hh"
+#include "COM_domain.hh"
+#include "COM_input_descriptor.hh"
+#include "COM_result.hh"
+#include "COM_simple_operation.hh"
+
+namespace blender::realtime_compositor {
+
+/* ------------------------------------------------------------------------------------------------
+ * Realize On Domain Operation
+ *
+ * A simple operation that projects the input on a certain target domain, copies the area of the
+ * input that intersects the target domain, and fill the rest with zeros or repetitions of the
+ * input depending on the realization options of the target domain. See the discussion in
+ * COM_domain.hh for more information. */
+class RealizeOnDomainOperation : public SimpleOperation {
+ private:
+ /* The target domain to realize the input on. */
+ Domain domain_;
+
+ public:
+ RealizeOnDomainOperation(Context &context, Domain domain, ResultType type);
+
+ void execute() override;
+
+ /* Determine if a realize on domain operation is needed for the input with the given result and
+ * descriptor in an operation with the given operation domain. If it is not needed, return a null
+ * pointer. If it is needed, return an instance of the operation. */
+ static SimpleOperation *construct_if_needed(Context &context,
+ const Result &input_result,
+ const InputDescriptor &input_descriptor,
+ const Domain &operation_domain);
+
+ protected:
+ /* The operation domain is just the target domain. */
+ Domain compute_domain() override;
+
+ private:
+ /* Get the realization shader of the appropriate type. */
+ GPUShader *get_realization_shader();
+};
+
+} // namespace blender::realtime_compositor
diff --git a/source/blender/compositor/realtime_compositor/COM_reduce_to_single_value_operation.hh b/source/blender/compositor/realtime_compositor/COM_reduce_to_single_value_operation.hh
new file mode 100644
index 00000000000..2f5a82c79b7
--- /dev/null
+++ b/source/blender/compositor/realtime_compositor/COM_reduce_to_single_value_operation.hh
@@ -0,0 +1,31 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#pragma once
+
+#include "COM_context.hh"
+#include "COM_result.hh"
+#include "COM_simple_operation.hh"
+
+namespace blender::realtime_compositor {
+
+/* ------------------------------------------------------------------------------------------------
+ * Reduce To Single Value Operation
+ *
+ * A simple operation that reduces its input result into a single value output result. The input is
+ * assumed to be a texture result of size 1x1, that is, a texture composed of a single pixel, the
+ * value of which shall serve as the single value of the output result. */
+class ReduceToSingleValueOperation : public SimpleOperation {
+ public:
+ ReduceToSingleValueOperation(Context &context, ResultType type);
+
+ /* Download the input pixel from the GPU texture and set its value to the value of the allocated
+ * single value output result. */
+ void execute() override;
+
+ /* Determine if a reduce to single value operation is needed for the input with the
+ * given result. If it is not needed, return a null pointer. If it is needed, return an instance
+ * of the operation. */
+ static SimpleOperation *construct_if_needed(Context &context, const Result &input_result);
+};
+
+} // namespace blender::realtime_compositor
diff --git a/source/blender/compositor/realtime_compositor/COM_result.hh b/source/blender/compositor/realtime_compositor/COM_result.hh
new file mode 100644
index 00000000000..a16d68bb92d
--- /dev/null
+++ b/source/blender/compositor/realtime_compositor/COM_result.hh
@@ -0,0 +1,234 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#pragma once
+
+#include "BLI_float3x3.hh"
+#include "BLI_math_vec_types.hh"
+
+#include "GPU_shader.h"
+#include "GPU_texture.h"
+
+#include "COM_domain.hh"
+#include "COM_texture_pool.hh"
+
+namespace blender::realtime_compositor {
+
+/* Possible data types that operations can operate on. They either represent the base type of the
+ * result texture or a single value result. */
+enum class ResultType : uint8_t {
+ Float,
+ Vector,
+ Color,
+};
+
+/* ------------------------------------------------------------------------------------------------
+ * Result
+ *
+ * A result represents the computed value of an output of an operation. A result can either
+ * represent an image or a single value. A result is typed, and can be of type color, vector, or
+ * float. Single value results are stored in 1x1 textures to make them easily accessible in
+ * shaders. But the same value is also stored in the value union member of the result for any
+ * host-side processing. The texture of the result is allocated from the texture pool referenced by
+ * the result.
+ *
+ * Results are reference counted and their textures are released once their reference count reaches
+ * zero. After constructing a result, the set_initial_reference_count method is called to declare
+ * the number of operations that needs this result. Once each operation that needs the result no
+ * longer needs it, the release method is called and the reference count is decremented, until it
+ * reaches zero, where the result's texture is then released. Since results are eventually
+ * decremented to zero by the end of every evaluation, the reference count is restored before every
+ * evaluation to its initial reference count by calling the reset method, which is why a separate
+ * member initial_reference_count_ is stored to keep track of the initial value.
+ *
+ * A result not only represents an image, but also the area it occupies in the virtual compositing
+ * space. This area is called the Domain of the result, see the discussion in COM_domain.hh for
+ * more information.
+ *
+ * A result can be a proxy result that merely wraps another master result, in which case, it shares
+ * its values and delegates all reference counting to it. While a proxy result shares the value of
+ * the master result, it can have a different domain. Consequently, transformation operations are
+ * implemented using proxy results, where their results are proxy results of their inputs but with
+ * their domains transformed based on their options. Moreover, proxy results can also be used as
+ * the results of identity operations, that is, operations that do nothing to their inputs in
+ * certain configurations. In which case, the proxy result is left as is with no extra
+ * transformation on its domain whatsoever. Proxy results can be created by calling the
+ * pass_through method, see that method for more details. */
+class Result {
+ private:
+ /* The base type of the texture or the type of the single value. */
+ ResultType type_;
+ /* If true, the result is a single value, otherwise, the result is a texture. */
+ bool is_single_value_;
+ /* A GPU texture storing the result data. This will be a 1x1 texture if the result is a single
+ * value, the value of which will be identical to that of the value member. See class description
+ * for more information. */
+ GPUTexture *texture_ = nullptr;
+ /* The texture pool used to allocate the texture of the result, this should be initialized during
+ * construction. */
+ TexturePool *texture_pool_ = nullptr;
+ /* The number of operations that currently needs this result. At the time when the result is
+ * computed, this member will have a value that matches initial_reference_count_. Once each
+ * operation that needs the result no longer needs it, the release method is called and the
+ * reference count is decremented, until it reaches zero, where the result's texture is then
+ * released. If this result have a master result, then this reference count is irrelevant and
+ * shadowed by the reference count of the master result. */
+ int reference_count_;
+ /* The number of operations that reference and use this result at the time when it was initially
+ * computed. Since reference_count_ is decremented and always becomes zero at the end of the
+ * evaluation, this member is used to reset the reference count of the results for later
+ * evaluations by calling the reset method. This member is also used to determine if this result
+ * should be computed by calling the should_compute method. */
+ int initial_reference_count_;
+ /* If the result is a single value, this member stores the value of the result, the value of
+ * which will be identical to that stored in the texture member. The active union member depends
+ * on the type of the result. This member is uninitialized and should not be used if the result
+ * is a texture. */
+ union {
+ float float_value_;
+ float3 vector_value_;
+ float4 color_value_;
+ };
+ /* The domain of the result. This only matters if the result was a texture. See the discussion in
+ * COM_domain.hh for more information. */
+ Domain domain_ = Domain::identity();
+ /* If not nullptr, then this result wraps and shares the value of another master result. In this
+ * case, calls to texture-related methods like increment_reference_count and release should
+ * operate on the master result as opposed to this result. This member is typically set upon
+ * calling the pass_through method, which sets this result to be the master of a target result.
+ * See that method for more information. */
+ Result *master_ = nullptr;
+
+ public:
+ /* Construct a result of the given type with the given texture pool that will be used to allocate
+ * and release the result's texture. */
+ Result(ResultType type, TexturePool &texture_pool);
+
+ /* Declare the result to be a texture result, allocate a texture of an appropriate type with
+ * the size of the given domain from the result's texture pool, and set the domain of the result
+ * to the given domain. */
+ void allocate_texture(Domain domain);
+
+ /* Declare the result to be a single value result, allocate a texture of an appropriate
+ * type with size 1x1 from the result's texture pool, and set the domain to be an identity
+ * domain. See class description for more information. */
+ void allocate_single_value();
+
+ /* Allocate a single value result and set its value to zero. This is called for results whose
+ * value can't be computed and are considered invalid. */
+ void allocate_invalid();
+
+ /* Bind the texture of the result to the texture image unit with the given name in the currently
+ * bound given shader. This also inserts a memory barrier for texture fetches to ensure any prior
+ * writes to the texture are reflected before reading from it. */
+ void bind_as_texture(GPUShader *shader, const char *texture_name) const;
+
+ /* Bind the texture of the result to the image unit with the given name in the currently bound
+ * given shader. */
+ void bind_as_image(GPUShader *shader, const char *image_name) const;
+
+ /* Unbind the texture which was previously bound using bind_as_texture. */
+ void unbind_as_texture() const;
+
+ /* Unbind the texture which was previously bound using bind_as_image. */
+ void unbind_as_image() const;
+
+ /* Pass this result through to a target result, in which case, the target result becomes a proxy
+ * result with this result as its master result. This is done by making the target result a copy
+ * of this result, essentially having identical values between the two and consequently sharing
+ * the underlying texture. An exception is the initial reference count, whose value is retained
+ * and not copied, because it is a property of the original result and is needed for correctly
+ * resetting the result before the next evaluation. Additionally, this result is set to be the
+ * master of the target result, by setting the master member of the target. Finally, the
+ * reference count of the result is incremented by the reference count of the target result. See
+ * the discussion above for more information. */
+ void pass_through(Result &target);
+
+ /* Transform the result by the given transformation. This effectively pre-multiply the given
+ * transformation by the current transformation of the domain of the result. */
+ void transform(const float3x3 &transformation);
+
+ /* Get a reference to the realization options of this result. See the RealizationOptions struct
+ * for more information. */
+ RealizationOptions &get_realization_options();
+
+ /* If the result is a single value result of type float, return its float value. Otherwise, an
+ * uninitialized value is returned. */
+ float get_float_value() const;
+
+ /* If the result is a single value result of type vector, return its vector value. Otherwise, an
+ * uninitialized value is returned. */
+ float3 get_vector_value() const;
+
+ /* If the result is a single value result of type color, return its color value. Otherwise, an
+ * uninitialized value is returned. */
+ float4 get_color_value() const;
+
+ /* Same as get_float_value but returns a default value if the result is not a single value. */
+ float get_float_value_default(float default_value) const;
+
+ /* Same as get_vector_value but returns a default value if the result is not a single value. */
+ float3 get_vector_value_default(const float3 &default_value) const;
+
+ /* Same as get_color_value but returns a default value if the result is not a single value. */
+ float4 get_color_value_default(const float4 &default_value) const;
+
+ /* If the result is a single value result of type float, set its float value and upload it to the
+ * texture. Otherwise, an undefined behavior is invoked. */
+ void set_float_value(float value);
+
+ /* If the result is a single value result of type vector, set its vector value and upload it to
+ * the texture. Otherwise, an undefined behavior is invoked. */
+ void set_vector_value(const float3 &value);
+
+ /* If the result is a single value result of type color, set its color value and upload it to the
+ * texture. Otherwise, an undefined behavior is invoked. */
+ void set_color_value(const float4 &value);
+
+ /* Set the value of initial_reference_count_, see that member for more details. This should be
+ * called after constructing the result to declare the number of operations that needs it. */
+ void set_initial_reference_count(int count);
+
+ /* Reset the result to prepare it for a new evaluation. This should be called before evaluating
+ * the operation that computes this result. First, set the value of reference_count_ to the value
+ * of initial_reference_count_ since reference_count_ may have already been decremented to zero
+ * in a previous evaluation. Second, set master_ to nullptr because the result may have been
+ * turned into a proxy result in a previous evaluation. Other fields don't need to be reset
+ * because they are runtime and overwritten during evaluation. */
+ void reset();
+
+ /* Increment the reference count of the result by the given count. If this result have a master
+ * result, the reference count of the master result is incremented instead. */
+ void increment_reference_count(int count = 1);
+
+ /* Decrement the reference count of the result and release the its texture back into the texture
+ * pool if the reference count reaches zero. This should be called when an operation that used
+ * this result no longer needs it. If this result have a master result, the master result is
+ * released instead. */
+ void release();
+
+ /* Returns true if this result should be computed and false otherwise. The result should be
+ * computed if its reference count is not zero, that is, its result is used by at least one
+ * operation. */
+ bool should_compute();
+
+ /* Returns the type of the result. */
+ ResultType type() const;
+
+ /* Returns true if the result is a texture and false of it is a single value. */
+ bool is_texture() const;
+
+ /* Returns true if the result is a single value and false of it is a texture. */
+ bool is_single_value() const;
+
+ /* Returns the allocated GPU texture of the result. */
+ GPUTexture *texture() const;
+
+ /* Returns the reference count of the result. If this result have a master result, then the
+ * reference count of the master result is returned instead. */
+ int reference_count() const;
+
+ /* Returns a reference to the domain of the result. See the Domain class. */
+ const Domain &domain() const;
+};
+
+} // namespace blender::realtime_compositor
diff --git a/source/blender/compositor/realtime_compositor/COM_scheduler.hh b/source/blender/compositor/realtime_compositor/COM_scheduler.hh
new file mode 100644
index 00000000000..4f778b32145
--- /dev/null
+++ b/source/blender/compositor/realtime_compositor/COM_scheduler.hh
@@ -0,0 +1,21 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#pragma once
+
+#include "BLI_vector_set.hh"
+
+#include "NOD_derived_node_tree.hh"
+
+namespace blender::realtime_compositor {
+
+using namespace nodes::derived_node_tree_types;
+
+/* A type representing the ordered set of nodes defining the schedule of node execution. */
+using Schedule = VectorSet<DNode>;
+
+/* Computes the execution schedule of the node tree. This is essentially a post-order depth first
+ * traversal of the node tree from the output node to the leaf input nodes, with informed order of
+ * traversal of dependencies based on a heuristic estimation of the number of needed buffers. */
+Schedule compute_schedule(DerivedNodeTree &tree);
+
+} // namespace blender::realtime_compositor
diff --git a/source/blender/compositor/realtime_compositor/COM_shader_node.hh b/source/blender/compositor/realtime_compositor/COM_shader_node.hh
new file mode 100644
index 00000000000..453226ec452
--- /dev/null
+++ b/source/blender/compositor/realtime_compositor/COM_shader_node.hh
@@ -0,0 +1,87 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#pragma once
+
+#include "BLI_string_ref.hh"
+#include "BLI_vector.hh"
+
+#include "DNA_node_types.h"
+
+#include "GPU_material.h"
+
+#include "NOD_derived_node_tree.hh"
+
+namespace blender::realtime_compositor {
+
+using namespace nodes::derived_node_tree_types;
+
+/* ------------------------------------------------------------------------------------------------
+ * Shader Node
+ *
+ * A shader node encapsulates a compositor node tree that is capable of being used together with
+ * other shader nodes to construct a Shader Operation using the GPU material compiler. A GPU node
+ * stack for each of the node inputs and outputs is stored and populated during construction in
+ * order to represent the node as a GPU node inside the GPU material graph, see GPU_material.h for
+ * more information. Derived classes should implement the compile method to add the node and link
+ * it to the GPU material given to the method. The compiler is expected to initialize the input
+ * links of the node before invoking the compile method. See the discussion in
+ * COM_shader_operation.hh for more information. */
+class ShaderNode {
+ private:
+ /* The node that this operation represents. */
+ DNode node_;
+ /* The GPU node stacks of the inputs of the node. Those are populated during construction in the
+ * populate_inputs method. The links of the inputs are initialized by the GPU material compiler
+ * prior to calling the compile method. There is an extra stack at the end to mark the end of the
+ * array, as this is what the GPU module functions expect. */
+ Vector<GPUNodeStack> inputs_;
+ /* The GPU node stacks of the outputs of the node. Those are populated during construction in the
+ * populate_outputs method. There is an extra stack at the end to mark the end of the array, as
+ * this is what the GPU module functions expect. */
+ Vector<GPUNodeStack> outputs_;
+
+ public:
+ /* Construct the node by populating both its inputs and outputs. */
+ ShaderNode(DNode node);
+
+ virtual ~ShaderNode() = default;
+
+ /* Compile the node by adding the appropriate GPU material graph nodes and linking the
+ * appropriate resources. */
+ virtual void compile(GPUMaterial *material) = 0;
+
+ /* Returns a contiguous array containing the GPU node stacks of each input. */
+ GPUNodeStack *get_inputs_array();
+
+ /* Returns a contiguous array containing the GPU node stacks of each output. */
+ GPUNodeStack *get_outputs_array();
+
+ /* Returns the GPU node stack of the input with the given identifier. */
+ GPUNodeStack &get_input(StringRef identifier);
+
+ /* Returns the GPU node stack of the output with the given identifier. */
+ GPUNodeStack &get_output(StringRef identifier);
+
+ /* Returns the GPU node link of the input with the given identifier, if the input is not linked,
+ * a uniform link carrying the value of the input will be created a returned. It is expected that
+ * the caller will use the returned link in a GPU material, otherwise, the link may not be
+ * properly freed. */
+ GPUNodeLink *get_input_link(StringRef identifier);
+
+ protected:
+ /* Returns a reference to the derived node that this operation represents. */
+ const DNode &node() const;
+
+ /* Returns a reference to the node this operations represents. */
+ bNode &bnode() const;
+
+ private:
+ /* Populate the inputs of the node. The input link is set to nullptr and is expected to be
+ * initialized by the GPU material compiler before calling the compile method. */
+ void populate_inputs();
+ /* Populate the outputs of the node. The output link is set to nullptr and is expected to be
+ * initialized by the compile method. */
+ void populate_outputs();
+};
+
+} // namespace blender::realtime_compositor
diff --git a/source/blender/compositor/realtime_compositor/COM_shader_operation.hh b/source/blender/compositor/realtime_compositor/COM_shader_operation.hh
new file mode 100644
index 00000000000..a33dcbf25be
--- /dev/null
+++ b/source/blender/compositor/realtime_compositor/COM_shader_operation.hh
@@ -0,0 +1,242 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#pragma once
+
+#include <memory>
+
+#include "BLI_map.hh"
+#include "BLI_string_ref.hh"
+#include "BLI_vector_set.hh"
+
+#include "GPU_material.h"
+#include "GPU_shader.h"
+
+#include "gpu_shader_create_info.hh"
+
+#include "NOD_derived_node_tree.hh"
+
+#include "COM_context.hh"
+#include "COM_operation.hh"
+#include "COM_scheduler.hh"
+
+namespace blender::realtime_compositor {
+
+using namespace nodes::derived_node_tree_types;
+
+/* A type representing a contiguous subset of the node execution schedule that will be compiled
+ * into a Shader Operation. */
+using ShaderCompileUnit = VectorSet<DNode>;
+
+/* ------------------------------------------------------------------------------------------------
+ * Shader Operation
+ *
+ * An operation that evaluates a shader compiled from a contiguous subset of the node execution
+ * schedule using the GPU material compiler, see GPU_material.h for more information. The subset
+ * of the node execution schedule is called a shader compile unit, see the discussion in
+ * COM_compile_state.hh for more information.
+ *
+ * Consider the following node graph with a node execution schedule denoted by the number on each
+ * node. The compiler may decide to compile a subset of the execution schedule into a shader
+ * operation, in this case, the nodes from 3 to 5 were compiled together into a shader operation.
+ * This subset is called the shader compile unit. See the discussion in COM_evaluator.hh for more
+ * information on the compilation process. Each of the nodes inside the compile unit implements a
+ * Shader Node which is instantiated, stored in shader_nodes_, and used during compilation. See the
+ * discussion in COM_shader_node.hh for more information. Links that are internal to the shader
+ * operation are established between the input and outputs of the shader nodes, for instance, the
+ * links between nodes 3 and 4 as well as those between nodes 4 and 5. However, links that cross
+ * the boundary of the shader operation needs special handling.
+ *
+ * Shader Operation
+ * +------------------------------------------------------+
+ * .------------. | .------------. .------------. .------------. | .------------.
+ * | Node 1 | | | Node 3 | | Node 4 | | Node 5 | | | Node 6 |
+ * | |----|--| |--| |------| |--|--| |
+ * | | .-|--| | | | .---| | | | |
+ * '------------' | | '------------' '------------' | '------------' | '------------'
+ * | +----------------------------------|-------------------+
+ * .------------. | |
+ * | Node 2 | | |
+ * | |--'------------------------------------'
+ * | |
+ * '------------'
+ *
+ * Links from nodes that are not part of the shader operation to nodes that are part of the shader
+ * operation are considered inputs of the operation itself and are declared as such. For instance,
+ * the link from node 1 to node 3 is declared as an input to the operation, and the same applies
+ * for the links from node 2 to nodes 3 and 5. Note, however, that only one input is declared for
+ * each distinct output socket, so both links from node 2 share the same input of the operation.
+ * An input to the operation is declared for a distinct output socket as follows:
+ *
+ * - A texture is added to the shader, which will be bound to the result of the output socket
+ * during evaluation.
+ * - A GPU attribute is added to the GPU material for that output socket and is linked to the GPU
+ * input stack of the inputs linked to the output socket.
+ * - Code is emitted to initialize the values of the attributes by sampling the textures
+ * corresponding to each of the inputs.
+ * - The newly added attribute is mapped to the output socket in output_to_material_attribute_map_
+ * to share that same attributes for all inputs linked to the same output socket.
+ *
+ * Links from nodes that are part of the shader operation to nodes that are not part of the shader
+ * operation are considered outputs of the operation itself and are declared as such. For instance,
+ * the link from node 5 to node 6 is declared as an output to the operation. An output to the
+ * operation is declared for an output socket as follows:
+ *
+ * - An image is added in the shader where the output value will be written.
+ * - A storer GPU material node that stores the value of the output is added and linked to the GPU
+ * output stack of the output. The storer will store the value in the image identified by the
+ * index of the output given to the storer.
+ * - The storer functions are generated dynamically to map each index with its appropriate image.
+ *
+ * The GPU material code generator source is used to construct a compute shader that is then
+ * dispatched during operation evaluation after binding the inputs, outputs, and any necessary
+ * resources. */
+class ShaderOperation : public Operation {
+ private:
+ /* The compile unit that will be compiled into this shader operation. */
+ ShaderCompileUnit compile_unit_;
+ /* The GPU material backing the operation. This is created and compiled during construction and
+ * freed during destruction. */
+ GPUMaterial *material_;
+ /* A map that associates each node in the compile unit with an instance of its shader node. */
+ Map<DNode, std::unique_ptr<ShaderNode>> shader_nodes_;
+ /* A map that associates the identifier of each input of the operation with the output socket it
+ * is linked to. This is needed to help the compiler establish links between operations. */
+ Map<std::string, DOutputSocket> inputs_to_linked_outputs_map_;
+ /* A map that associates the output socket that provides the result of an output of the operation
+ * with the identifier of that output. This is needed to help the compiler establish links
+ * between operations. */
+ Map<DOutputSocket, std::string> output_sockets_to_output_identifiers_map_;
+ /* A map that associates the output socket of a node that is not part of the shader operation to
+ * the attribute that was created for it. This is used to share the same attribute with all
+ * inputs that are linked to the same output socket. */
+ Map<DOutputSocket, GPUNodeLink *> output_to_material_attribute_map_;
+
+ public:
+ /* Construct and compile a GPU material from the given shader compile unit by calling
+ * GPU_material_from_callbacks with the appropriate callbacks. */
+ ShaderOperation(Context &context, ShaderCompileUnit &compile_unit);
+
+ /* Free the GPU material. */
+ ~ShaderOperation();
+
+ /* Allocate the output results, bind the shader and all its needed resources, then dispatch the
+ * shader. */
+ void execute() override;
+
+ /* Get the identifier of the operation output corresponding to the given output socket. This is
+ * called by the compiler to identify the operation output that provides the result for an input
+ * by providing the output socket that the input is linked to. See
+ * output_sockets_to_output_identifiers_map_ for more information. */
+ StringRef get_output_identifier_from_output_socket(DOutputSocket output_socket);
+
+ /* Get a reference to the inputs to linked outputs map of the operation. This is called by the
+ * compiler to identify the output that each input of the operation is linked to for correct
+ * input mapping. See inputs_to_linked_outputs_map_ for more information. */
+ Map<std::string, DOutputSocket> &get_inputs_to_linked_outputs_map();
+
+ /* Compute and set the initial reference counts of all the results of the operation. The
+ * reference counts of the results are the number of operations that use those results, which is
+ * computed as the number of inputs whose node is part of the schedule and is linked to the
+ * output corresponding to each of the results of the operation. The node execution schedule is
+ * given as an input. */
+ void compute_results_reference_counts(const Schedule &schedule);
+
+ private:
+ /* Bind the uniform buffer of the GPU material as well as any color band textures needed by the
+ * GPU material. The compiled shader of the material is given as an argument and assumed to be
+ * bound. */
+ void bind_material_resources(GPUShader *shader);
+
+ /* Bind the input results of the operation to the appropriate textures in the GPU material. The
+ * attributes stored in output_to_material_attribute_map_ have names that match the texture
+ * samplers in the shader as well as the identifiers of the operation inputs that they correspond
+ * to. The compiled shader of the material is given as an argument and assumed to be bound. */
+ void bind_inputs(GPUShader *shader);
+
+ /* Bind the output results of the operation to the appropriate images in the GPU material. The
+ * name of the images in the shader match the identifier of their corresponding outputs. The
+ * compiled shader of the material is given as an argument and assumed to be bound. */
+ void bind_outputs(GPUShader *shader);
+
+ /* A static callback method of interface ConstructGPUMaterialFn that is passed to
+ * GPU_material_from_callbacks to construct the GPU material graph. The thunk parameter will be a
+ * pointer to the instance of ShaderOperation that is being compiled. The method goes over the
+ * compile unit and does the following for each node:
+ *
+ * - Instantiate a ShaderNode from the node and add it to shader_nodes_.
+ * - Link the inputs of the node if needed. The inputs are either linked to other nodes in the
+ * GPU material graph or are exposed as inputs to the shader operation itself if they are
+ * linked to nodes that are not part of the shader operation.
+ * - Call the compile method of the shader node to actually add and link the GPU material graph
+ * nodes.
+ * - If any of the outputs of the node are linked to nodes that are not part of the shader
+ * operation, they are exposed as outputs to the shader operation itself. */
+ static void construct_material(void *thunk, GPUMaterial *material);
+
+ /* Link the inputs of the node if needed. Unlinked inputs are ignored as they will be linked by
+ * the node compile method. If the input is linked to a node that is not part of the shader
+ * operation, the input will be exposed as an input to the shader operation and linked to it.
+ * While if the input is linked to a node that is part of the shader operation, then it is linked
+ * to that node in the GPU material node graph. */
+ void link_node_inputs(DNode node, GPUMaterial *material);
+
+ /* Given the input socket of a node that is part of the shader operation which is linked to the
+ * given output socket of a node that is also part of the shader operation, just link the output
+ * link of the GPU node stack of the output socket to the input link of the GPU node stack of the
+ * input socket. This essentially establishes the needed links in the GPU material node graph. */
+ void link_node_input_internal(DInputSocket input_socket, DOutputSocket output_socket);
+
+ /* Given the input socket of a node that is part of the shader operation which is linked to the
+ * given output socket of a node that is not part of the shader operation, declare a new
+ * operation input and link it to the input link of the GPU node stack of the input socket. An
+ * operation input is only declared if no input was already declared for that same output socket
+ * before. */
+ void link_node_input_external(DInputSocket input_socket,
+ DOutputSocket output_socket,
+ GPUMaterial *material);
+
+ /* Given the input socket of a node that is part of the shader operation which is linked to the
+ * given output socket of a node that is not part of the shader operation, declare a new input to
+ * the operation that is represented in the GPU material by a newly created GPU attribute. It is
+ * assumed that no operation input was declared for this same output socket before. In the
+ * generate_code_for_inputs method, a texture will be added in the shader for each of the
+ * declared inputs, having the same name as the attribute. Additionally, code will be emitted to
+ * initialize the attributes by sampling their corresponding textures. */
+ void declare_operation_input(DInputSocket input_socket,
+ DOutputSocket output_socket,
+ GPUMaterial *material);
+
+ /* Populate the output results of the shader operation for output sockets of the given node that
+ * are linked to nodes outside of the shader operation. */
+ void populate_results_for_node(DNode node, GPUMaterial *material);
+
+ /* Given the output socket of a node that is part of the shader operation which is linked to an
+ * input socket of a node that is not part of the shader operation, declare a new output to the
+ * operation and link it to an output storer passing in the index of the output. In the
+ * generate_code_for_outputs method, an image will be added in the shader for each of the
+ * declared outputs. Additionally, code will be emitted to define the storer functions that store
+ * the value in the appropriate image identified by the given index. */
+ void populate_operation_result(DOutputSocket output_socket, GPUMaterial *material);
+
+ /* A static callback method of interface GPUCodegenCallbackFn that is passed to
+ * GPU_material_from_callbacks to create the shader create info of the GPU material. The thunk
+ * parameter will be a pointer to the instance of ShaderOperation that is being compiled.
+ *
+ * This method first generates the necessary code to load the inputs and store the outputs. Then,
+ * it creates a compute shader from the generated sources. Finally, it adds the necessary GPU
+ * resources to the shader. */
+ static void generate_code(void *thunk, GPUMaterial *material, GPUCodegenOutput *code_generator);
+
+ /* Add an image in the shader for each of the declared outputs. Additionally, emit code to define
+ * the storer functions that store the given value in the appropriate image identified by the
+ * given index. */
+ void generate_code_for_outputs(gpu::shader::ShaderCreateInfo &shader_create_info);
+
+ /* Add a texture will in the shader for each of the declared inputs/attributes in the operation,
+ * having the same name as the attribute. Additionally, emit code to initialize the attributes by
+ * sampling their corresponding textures. */
+ void generate_code_for_inputs(GPUMaterial *material,
+ gpu::shader::ShaderCreateInfo &shader_create_info);
+};
+
+} // namespace blender::realtime_compositor
diff --git a/source/blender/compositor/realtime_compositor/COM_simple_operation.hh b/source/blender/compositor/realtime_compositor/COM_simple_operation.hh
new file mode 100644
index 00000000000..1655e52ac9a
--- /dev/null
+++ b/source/blender/compositor/realtime_compositor/COM_simple_operation.hh
@@ -0,0 +1,64 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#pragma once
+
+#include "BLI_string_ref.hh"
+
+#include "COM_operation.hh"
+#include "COM_result.hh"
+
+namespace blender::realtime_compositor {
+
+/* ------------------------------------------------------------------------------------------------
+ * Simple Operation
+ *
+ * A simple operation is an operation that takes exactly one input and computes exactly one output.
+ * Moreover, the output is guaranteed to only have a single user, that is, its reference count will
+ * be one. Such operations can be attached to the inputs of operations to pre-process the inputs to
+ * prepare them before the operation is executed.*/
+class SimpleOperation : public Operation {
+ private:
+ /* The identifier of the output. This is constant for all operations. */
+ static const StringRef output_identifier_;
+ /* The identifier of the input. This is constant for all operations. */
+ static const StringRef input_identifier_;
+
+ public:
+ using Operation::Operation;
+
+ /* Get a reference to the output result of the operation, this essentially calls the super
+ * get_result method with the output identifier of the operation. */
+ Result &get_result();
+
+ /* Map the input of the operation to the given result, this essentially calls the super
+ * map_input_to_result method with the input identifier of the operation. */
+ void map_input_to_result(Result *result);
+
+ protected:
+ /* Simple operations don't need input processors, so override with an empty implementation. */
+ void add_and_evaluate_input_processors() override;
+
+ /* Get a reference to the input result of the operation, this essentially calls the super
+ * get_result method with the input identifier of the operation. */
+ Result &get_input();
+
+ /* Switch the result mapped to the input with the given result, this essentially calls the super
+ * switch_result_mapped_to_input method with the input identifier of the operation. */
+ void switch_result_mapped_to_input(Result *result);
+
+ /* Populate the result of the operation, this essentially calls the super populate_result method
+ * with the output identifier of the operation and sets the initial reference count of the result
+ * to 1, since the result of an operation is guaranteed to have a single user. */
+ void populate_result(Result result);
+
+ /* Declare the descriptor of the input of the operation to be the given descriptor, this
+ * essentially calls the super declare_input_descriptor method with the input identifier of the
+ * operation. */
+ void declare_input_descriptor(InputDescriptor descriptor);
+
+ /* Get a reference to the descriptor of the input, this essentially calls the super
+ * get_input_descriptor method with the input identifier of the operation. */
+ InputDescriptor &get_input_descriptor();
+};
+
+} // namespace blender::realtime_compositor
diff --git a/source/blender/compositor/realtime_compositor/COM_static_shader_manager.hh b/source/blender/compositor/realtime_compositor/COM_static_shader_manager.hh
new file mode 100644
index 00000000000..161a80862a0
--- /dev/null
+++ b/source/blender/compositor/realtime_compositor/COM_static_shader_manager.hh
@@ -0,0 +1,33 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#pragma once
+
+#include "BLI_map.hh"
+#include "BLI_string_ref.hh"
+
+#include "GPU_shader.h"
+
+namespace blender::realtime_compositor {
+
+/* -------------------------------------------------------------------------------------------------
+ * Static Shader Manager
+ *
+ * A static shader manager is a map of shaders identified by their info name that can be acquired
+ * and reused throughout the evaluation of the compositor and are only freed when the shader
+ * manager is destroyed. Once a shader is acquired for the first time, it will be cached in the
+ * manager to be potentially acquired later if needed without the shader creation overhead. */
+class StaticShaderManager {
+ private:
+ /* The set of shaders identified by their info name that are currently available in the manager
+ * to be acquired. */
+ Map<StringRef, GPUShader *> shaders_;
+
+ public:
+ ~StaticShaderManager();
+
+ /* Check if there is an available shader with the given info name in the manager, if such shader
+ * exists, return it, otherwise, return a newly created shader and add it to the manager. */
+ GPUShader *get(const char *info_name);
+};
+
+} // namespace blender::realtime_compositor
diff --git a/source/blender/compositor/realtime_compositor/COM_texture_pool.hh b/source/blender/compositor/realtime_compositor/COM_texture_pool.hh
new file mode 100644
index 00000000000..cc6641d288f
--- /dev/null
+++ b/source/blender/compositor/realtime_compositor/COM_texture_pool.hh
@@ -0,0 +1,86 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#pragma once
+
+#include <cstdint>
+
+#include "BLI_map.hh"
+#include "BLI_math_vec_types.hh"
+#include "BLI_vector.hh"
+
+#include "GPU_texture.h"
+
+namespace blender::realtime_compositor {
+
+/* ------------------------------------------------------------------------------------------------
+ * Texture Pool Key
+ *
+ * A key used to identify a texture specification in a texture pool. Defines a hash and an equality
+ * operator for use in a hash map. */
+class TexturePoolKey {
+ public:
+ int2 size;
+ eGPUTextureFormat format;
+
+ /* Construct a key from the given texture size and format. */
+ TexturePoolKey(int2 size, eGPUTextureFormat format);
+
+ /* Construct a key from the size and format of the given texture. */
+ TexturePoolKey(const GPUTexture *texture);
+
+ uint64_t hash() const;
+};
+
+bool operator==(const TexturePoolKey &a, const TexturePoolKey &b);
+
+/* ------------------------------------------------------------------------------------------------
+ * Texture Pool
+ *
+ * A texture pool allows the allocation and reuse of textures throughout the execution of the
+ * compositor to avoid memory fragmentation and texture allocation overheads. The texture pool
+ * delegates the actual texture allocation to an allocate_texture method that should be implemented
+ * by the caller of the compositor evaluator, allowing a more agnostic and flexible execution that
+ * can be controlled by the caller. If the compositor is expected to execute frequently, like on
+ * every redraw, then the allocation method should use a persistent texture pool to allow
+ * cross-evaluation texture pooling, for instance, by using the DRWTexturePool. But if the
+ * evaluator is expected to execute infrequently, the allocated textures can just be freed when the
+ * evaluator is done, that is, when the pool is destructed. */
+class TexturePool {
+ private:
+ /* The set of textures in the pool that are available to acquire for each distinct texture
+ * specification. */
+ Map<TexturePoolKey, Vector<GPUTexture *>> textures_;
+
+ public:
+ /* Check if there is an available texture with the given specification in the pool, if such
+ * texture exists, return it, otherwise, return a newly allocated texture. Expect the texture to
+ * be uncleared and possibly contains garbage data. */
+ GPUTexture *acquire(int2 size, eGPUTextureFormat format);
+
+ /* Shorthand for acquire with GPU_RGBA16F format. */
+ GPUTexture *acquire_color(int2 size);
+
+ /* Shorthand for acquire with GPU_RGBA16F format. Identical to acquire_color because vectors
+ * are stored in RGBA textures, due to the limited support for RGB textures. */
+ GPUTexture *acquire_vector(int2 size);
+
+ /* Shorthand for acquire with GPU_R16F format. */
+ GPUTexture *acquire_float(int2 size);
+
+ /* Put the texture back into the pool, potentially to be acquired later by another user. Expects
+ * the texture to be one that was acquired using the same texture pool. */
+ void release(GPUTexture *texture);
+
+ /* Reset the texture pool by clearing all available textures without freeing the textures. If the
+ * textures will no longer be needed, they should be freed in the destructor. This should be
+ * called after the compositor is done evaluating. */
+ void reset();
+
+ private:
+ /* Returns a newly allocated texture with the given specification. This method should be
+ * implemented by the caller of the compositor evaluator. See the class description for more
+ * information. */
+ virtual GPUTexture *allocate_texture(int2 size, eGPUTextureFormat format) = 0;
+};
+
+} // namespace blender::realtime_compositor
diff --git a/source/blender/compositor/realtime_compositor/COM_utilities.hh b/source/blender/compositor/realtime_compositor/COM_utilities.hh
new file mode 100644
index 00000000000..4bd61aab5cb
--- /dev/null
+++ b/source/blender/compositor/realtime_compositor/COM_utilities.hh
@@ -0,0 +1,61 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#pragma once
+
+#include "BLI_function_ref.hh"
+#include "BLI_math_vec_types.hh"
+
+#include "NOD_derived_node_tree.hh"
+
+#include "GPU_shader.h"
+
+#include "COM_input_descriptor.hh"
+#include "COM_result.hh"
+
+namespace blender::realtime_compositor {
+
+using namespace nodes::derived_node_tree_types;
+
+/* Get the origin socket of the given node input. If the input is not linked, the socket itself is
+ * returned. If the input is linked, the socket that is linked to it is returned, which could
+ * either be an input or an output. An input socket is returned when the given input is connected
+ * to an unlinked input of a group input node. */
+DSocket get_input_origin_socket(DInputSocket input);
+
+/* Get the output socket linked to the given node input. If the input is not linked to an output, a
+ * null output is returned. */
+DOutputSocket get_output_linked_to_input(DInputSocket input);
+
+/* Get the result type that corresponds to the type of the given socket. */
+ResultType get_node_socket_result_type(const SocketRef *socket);
+
+/* Returns true if any of the nodes linked to the given output satisfies the given condition, and
+ * false otherwise. */
+bool is_output_linked_to_node_conditioned(DOutputSocket output,
+ FunctionRef<bool(DNode)> condition);
+
+/* Returns the number of inputs linked to the given output that satisfy the given condition. */
+int number_of_inputs_linked_to_output_conditioned(DOutputSocket output,
+ FunctionRef<bool(DInputSocket)> condition);
+
+/* A node is a shader node if it defines a method to get a shader node operation. */
+bool is_shader_node(DNode node);
+
+/* Returns true if the given node is supported, that is, have an implementation. Returns false
+ * otherwise. */
+bool is_node_supported(DNode node);
+
+/* Get the input descriptor of the given input socket. */
+InputDescriptor input_descriptor_from_input_socket(const InputSocketRef *socket);
+
+/* Dispatch the given compute shader in a 2D compute space such that the number of threads in both
+ * dimensions is as small as possible but at least covers the entirety of threads_range assuming
+ * the shader has a local group size given by local_size. That means that the number of threads
+ * might be a bit larger than threads_range, so shaders has to put that into consideration. A
+ * default local size of 16x16 is assumed, which is the optimal local size for many image
+ * processing shaders. */
+void compute_dispatch_threads_at_least(GPUShader *shader,
+ int2 threads_range,
+ int2 local_size = int2(16));
+
+} // namespace blender::realtime_compositor
diff --git a/source/blender/compositor/realtime_compositor/intern/compile_state.cc b/source/blender/compositor/realtime_compositor/intern/compile_state.cc
new file mode 100644
index 00000000000..5b485224111
--- /dev/null
+++ b/source/blender/compositor/realtime_compositor/intern/compile_state.cc
@@ -0,0 +1,163 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include <limits>
+
+#include "BLI_math_vec_types.hh"
+
+#include "DNA_node_types.h"
+
+#include "NOD_derived_node_tree.hh"
+
+#include "COM_compile_state.hh"
+#include "COM_domain.hh"
+#include "COM_input_descriptor.hh"
+#include "COM_node_operation.hh"
+#include "COM_result.hh"
+#include "COM_scheduler.hh"
+#include "COM_shader_operation.hh"
+#include "COM_utilities.hh"
+
+namespace blender::realtime_compositor {
+
+using namespace nodes::derived_node_tree_types;
+
+CompileState::CompileState(const Schedule &schedule) : schedule_(schedule)
+{
+}
+
+const Schedule &CompileState::get_schedule()
+{
+ return schedule_;
+}
+
+void CompileState::map_node_to_node_operation(DNode node, NodeOperation *operations)
+{
+ return node_operations_.add_new(node, operations);
+}
+
+void CompileState::map_node_to_shader_operation(DNode node, ShaderOperation *operations)
+{
+ return shader_operations_.add_new(node, operations);
+}
+
+Result &CompileState::get_result_from_output_socket(DOutputSocket output)
+{
+ /* The output belongs to a node that was compiled into a standard node operation, so return a
+ * reference to the result from that operation using the output identifier. */
+ if (node_operations_.contains(output.node())) {
+ NodeOperation *operation = node_operations_.lookup(output.node());
+ return operation->get_result(output->identifier());
+ }
+
+ /* Otherwise, the output belongs to a node that was compiled into a shader operation, so
+ * retrieve the internal identifier of that output and return a reference to the result from
+ * that operation using the retrieved identifier. */
+ ShaderOperation *operation = shader_operations_.lookup(output.node());
+ return operation->get_result(operation->get_output_identifier_from_output_socket(output));
+}
+
+void CompileState::add_node_to_shader_compile_unit(DNode node)
+{
+ shader_compile_unit_.add_new(node);
+
+ /* If the domain of the shader compile unit is not yet determined or was determined to be
+ * an identity domain, update it to be the computed domain of the node. */
+ if (shader_compile_unit_domain_ == Domain::identity()) {
+ shader_compile_unit_domain_ = compute_shader_node_domain(node);
+ }
+}
+
+ShaderCompileUnit &CompileState::get_shader_compile_unit()
+{
+ return shader_compile_unit_;
+}
+
+void CompileState::reset_shader_compile_unit()
+{
+ return shader_compile_unit_.clear();
+}
+
+bool CompileState::should_compile_shader_compile_unit(DNode node)
+{
+ /* If the shader compile unit is empty, then it can't be compiled yet. */
+ if (shader_compile_unit_.is_empty()) {
+ return false;
+ }
+
+ /* If the node is not a shader node, then it can't be added to the shader compile unit and the
+ * shader compile unit is considered complete and should be compiled. */
+ if (!is_shader_node(node)) {
+ return true;
+ }
+
+ /* If the computed domain of the node doesn't matches the domain of the shader compile unit, then
+ * it can't be added to the shader compile unit and the shader compile unit is considered
+ * complete and should be compiled. Identity domains are an exception as they are always
+ * compatible because they represents single values. */
+ if (shader_compile_unit_domain_ != Domain::identity() &&
+ shader_compile_unit_domain_ != compute_shader_node_domain(node)) {
+ return true;
+ }
+
+ /* Otherwise, the node is compatible and can be added to the compile unit and it shouldn't be
+ * compiled just yet. */
+ return false;
+}
+
+Domain CompileState::compute_shader_node_domain(DNode node)
+{
+ /* Default to an identity domain in case no domain input was found, most likely because all
+ * inputs are single values. */
+ Domain node_domain = Domain::identity();
+ int current_domain_priority = std::numeric_limits<int>::max();
+
+ /* Go over the inputs and find the domain of the non single value input with the highest domain
+ * priority. */
+ for (const InputSocketRef *input_ref : node->inputs()) {
+ const DInputSocket input{node.context(), input_ref};
+
+ /* Get the output linked to the input. If it is null, that means the input is unlinked, so skip
+ * it. */
+ const DOutputSocket output = get_output_linked_to_input(input);
+ if (!output) {
+ continue;
+ }
+
+ const InputDescriptor input_descriptor = input_descriptor_from_input_socket(input_ref);
+
+ /* If the output belongs to a node that is part of the shader compile unit, then the domain of
+ * the input is the domain of the compile unit itself. */
+ if (shader_compile_unit_.contains(output.node())) {
+ /* Single value inputs can't be domain inputs. */
+ if (shader_compile_unit_domain_.size == int2(1)) {
+ continue;
+ }
+
+ /* Notice that the lower the domain priority value is, the higher the priority is, hence the
+ * less than comparison. */
+ if (input_descriptor.domain_priority < current_domain_priority) {
+ node_domain = shader_compile_unit_domain_;
+ current_domain_priority = input_descriptor.domain_priority;
+ }
+ continue;
+ }
+
+ const Result &result = get_result_from_output_socket(output);
+
+ /* A single value input can't be a domain input. */
+ if (result.is_single_value() || input_descriptor.expects_single_value) {
+ continue;
+ }
+
+ /* Notice that the lower the domain priority value is, the higher the priority is, hence the
+ * less than comparison. */
+ if (input_descriptor.domain_priority < current_domain_priority) {
+ node_domain = result.domain();
+ current_domain_priority = input_descriptor.domain_priority;
+ }
+ }
+
+ return node_domain;
+}
+
+} // namespace blender::realtime_compositor
diff --git a/source/blender/compositor/realtime_compositor/intern/context.cc b/source/blender/compositor/realtime_compositor/intern/context.cc
new file mode 100644
index 00000000000..64ac29af3d1
--- /dev/null
+++ b/source/blender/compositor/realtime_compositor/intern/context.cc
@@ -0,0 +1,36 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include "COM_context.hh"
+#include "COM_static_shader_manager.hh"
+#include "COM_texture_pool.hh"
+
+namespace blender::realtime_compositor {
+
+Context::Context(TexturePool &texture_pool) : texture_pool_(texture_pool)
+{
+}
+
+int Context::get_frame_number() const
+{
+ return get_scene()->r.cfra;
+}
+
+float Context::get_time() const
+{
+ const float frame_number = static_cast<float>(get_frame_number());
+ const float frame_rate = static_cast<float>(get_scene()->r.frs_sec) /
+ static_cast<float>(get_scene()->r.frs_sec_base);
+ return frame_number / frame_rate;
+}
+
+TexturePool &Context::texture_pool()
+{
+ return texture_pool_;
+}
+
+StaticShaderManager &Context::shader_manager()
+{
+ return shader_manager_;
+}
+
+} // namespace blender::realtime_compositor
diff --git a/source/blender/compositor/realtime_compositor/intern/conversion_operation.cc b/source/blender/compositor/realtime_compositor/intern/conversion_operation.cc
new file mode 100644
index 00000000000..d6bf74ffbee
--- /dev/null
+++ b/source/blender/compositor/realtime_compositor/intern/conversion_operation.cc
@@ -0,0 +1,225 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include "BLI_math_vec_types.hh"
+
+#include "GPU_shader.h"
+
+#include "COM_context.hh"
+#include "COM_conversion_operation.hh"
+#include "COM_input_descriptor.hh"
+#include "COM_result.hh"
+#include "COM_utilities.hh"
+
+namespace blender::realtime_compositor {
+
+/* -------------------------------------------------------------------------------------------------
+ * Conversion Operation.
+ */
+
+void ConversionOperation::execute()
+{
+ Result &result = get_result();
+ const Result &input = get_input();
+
+ if (input.is_single_value()) {
+ result.allocate_single_value();
+ execute_single(input, result);
+ return;
+ }
+
+ result.allocate_texture(input.domain());
+
+ GPUShader *shader = get_conversion_shader();
+ GPU_shader_bind(shader);
+
+ input.bind_as_texture(shader, "input_tx");
+ result.bind_as_image(shader, "output_img");
+
+ compute_dispatch_threads_at_least(shader, input.domain().size);
+
+ input.unbind_as_texture();
+ result.unbind_as_image();
+ GPU_shader_unbind();
+}
+
+SimpleOperation *ConversionOperation::construct_if_needed(Context &context,
+ const Result &input_result,
+ const InputDescriptor &input_descriptor)
+{
+ ResultType result_type = input_result.type();
+ ResultType expected_type = input_descriptor.type;
+
+ /* If the result type differs from the expected type, return an instance of an appropriate
+ * conversion operation. Otherwise, return a null pointer. */
+
+ if (result_type == ResultType::Float && expected_type == ResultType::Vector) {
+ return new ConvertFloatToVectorOperation(context);
+ }
+
+ if (result_type == ResultType::Float && expected_type == ResultType::Color) {
+ return new ConvertFloatToColorOperation(context);
+ }
+
+ if (result_type == ResultType::Color && expected_type == ResultType::Float) {
+ return new ConvertColorToFloatOperation(context);
+ }
+
+ if (result_type == ResultType::Color && expected_type == ResultType::Vector) {
+ return new ConvertColorToVectorOperation(context);
+ }
+
+ if (result_type == ResultType::Vector && expected_type == ResultType::Float) {
+ return new ConvertVectorToFloatOperation(context);
+ }
+
+ if (result_type == ResultType::Vector && expected_type == ResultType::Color) {
+ return new ConvertVectorToColorOperation(context);
+ }
+
+ return nullptr;
+}
+
+/* -------------------------------------------------------------------------------------------------
+ * Convert Float To Vector Operation.
+ */
+
+ConvertFloatToVectorOperation::ConvertFloatToVectorOperation(Context &context)
+ : ConversionOperation(context)
+{
+ InputDescriptor input_descriptor;
+ input_descriptor.type = ResultType::Float;
+ declare_input_descriptor(input_descriptor);
+ populate_result(Result(ResultType::Vector, texture_pool()));
+}
+
+void ConvertFloatToVectorOperation::execute_single(const Result &input, Result &output)
+{
+ output.set_vector_value(float3(input.get_float_value()));
+}
+
+GPUShader *ConvertFloatToVectorOperation::get_conversion_shader() const
+{
+ return shader_manager().get("compositor_convert_float_to_vector");
+}
+
+/* -------------------------------------------------------------------------------------------------
+ * Convert Float To Color Operation.
+ */
+
+ConvertFloatToColorOperation::ConvertFloatToColorOperation(Context &context)
+ : ConversionOperation(context)
+{
+ InputDescriptor input_descriptor;
+ input_descriptor.type = ResultType::Float;
+ declare_input_descriptor(input_descriptor);
+ populate_result(Result(ResultType::Color, texture_pool()));
+}
+
+void ConvertFloatToColorOperation::execute_single(const Result &input, Result &output)
+{
+ float4 color = float4(input.get_float_value());
+ color[3] = 1.0f;
+ output.set_color_value(color);
+}
+
+GPUShader *ConvertFloatToColorOperation::get_conversion_shader() const
+{
+ return shader_manager().get("compositor_convert_float_to_color");
+}
+
+/* -------------------------------------------------------------------------------------------------
+ * Convert Color To Float Operation.
+ */
+
+ConvertColorToFloatOperation::ConvertColorToFloatOperation(Context &context)
+ : ConversionOperation(context)
+{
+ InputDescriptor input_descriptor;
+ input_descriptor.type = ResultType::Color;
+ declare_input_descriptor(input_descriptor);
+ populate_result(Result(ResultType::Float, texture_pool()));
+}
+
+void ConvertColorToFloatOperation::execute_single(const Result &input, Result &output)
+{
+ float4 color = input.get_color_value();
+ output.set_float_value((color[0] + color[1] + color[2]) / 3.0f);
+}
+
+GPUShader *ConvertColorToFloatOperation::get_conversion_shader() const
+{
+ return shader_manager().get("compositor_convert_color_to_float");
+}
+
+/* -------------------------------------------------------------------------------------------------
+ * Convert Color To Vector Operation.
+ */
+
+ConvertColorToVectorOperation::ConvertColorToVectorOperation(Context &context)
+ : ConversionOperation(context)
+{
+ InputDescriptor input_descriptor;
+ input_descriptor.type = ResultType::Color;
+ declare_input_descriptor(input_descriptor);
+ populate_result(Result(ResultType::Vector, texture_pool()));
+}
+
+void ConvertColorToVectorOperation::execute_single(const Result &input, Result &output)
+{
+ float4 color = input.get_color_value();
+ output.set_vector_value(float3(color));
+}
+
+GPUShader *ConvertColorToVectorOperation::get_conversion_shader() const
+{
+ return shader_manager().get("compositor_convert_color_to_vector");
+}
+
+/* -------------------------------------------------------------------------------------------------
+ * Convert Vector To Float Operation.
+ */
+
+ConvertVectorToFloatOperation::ConvertVectorToFloatOperation(Context &context)
+ : ConversionOperation(context)
+{
+ InputDescriptor input_descriptor;
+ input_descriptor.type = ResultType::Vector;
+ declare_input_descriptor(input_descriptor);
+ populate_result(Result(ResultType::Float, texture_pool()));
+}
+
+void ConvertVectorToFloatOperation::execute_single(const Result &input, Result &output)
+{
+ float3 vector = input.get_vector_value();
+ output.set_float_value((vector[0] + vector[1] + vector[2]) / 3.0f);
+}
+
+GPUShader *ConvertVectorToFloatOperation::get_conversion_shader() const
+{
+ return shader_manager().get("compositor_convert_vector_to_float");
+}
+
+/* -------------------------------------------------------------------------------------------------
+ * Convert Vector To Color Operation.
+ */
+
+ConvertVectorToColorOperation::ConvertVectorToColorOperation(Context &context)
+ : ConversionOperation(context)
+{
+ InputDescriptor input_descriptor;
+ input_descriptor.type = ResultType::Vector;
+ declare_input_descriptor(input_descriptor);
+ populate_result(Result(ResultType::Color, texture_pool()));
+}
+
+void ConvertVectorToColorOperation::execute_single(const Result &input, Result &output)
+{
+ output.set_color_value(float4(input.get_vector_value(), 1.0f));
+}
+
+GPUShader *ConvertVectorToColorOperation::get_conversion_shader() const
+{
+ return shader_manager().get("compositor_convert_vector_to_color");
+}
+
+} // namespace blender::realtime_compositor
diff --git a/source/blender/compositor/realtime_compositor/intern/domain.cc b/source/blender/compositor/realtime_compositor/intern/domain.cc
new file mode 100644
index 00000000000..31b297c212e
--- /dev/null
+++ b/source/blender/compositor/realtime_compositor/intern/domain.cc
@@ -0,0 +1,38 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include "BLI_float3x3.hh"
+#include "BLI_math_vec_types.hh"
+
+#include "COM_domain.hh"
+
+namespace blender::realtime_compositor {
+
+Domain::Domain(int2 size) : size(size), transformation(float3x3::identity())
+{
+}
+
+Domain::Domain(int2 size, float3x3 transformation) : size(size), transformation(transformation)
+{
+}
+
+void Domain::transform(const float3x3 &input_transformation)
+{
+ transformation = input_transformation * transformation;
+}
+
+Domain Domain::identity()
+{
+ return Domain(int2(1), float3x3::identity());
+}
+
+bool operator==(const Domain &a, const Domain &b)
+{
+ return a.size == b.size && a.transformation == b.transformation;
+}
+
+bool operator!=(const Domain &a, const Domain &b)
+{
+ return !(a == b);
+}
+
+} // namespace blender::realtime_compositor
diff --git a/source/blender/compositor/realtime_compositor/intern/evaluator.cc b/source/blender/compositor/realtime_compositor/intern/evaluator.cc
new file mode 100644
index 00000000000..d358389f2e9
--- /dev/null
+++ b/source/blender/compositor/realtime_compositor/intern/evaluator.cc
@@ -0,0 +1,171 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include <string>
+
+#include "DNA_node_types.h"
+
+#include "NOD_derived_node_tree.hh"
+
+#include "COM_compile_state.hh"
+#include "COM_context.hh"
+#include "COM_evaluator.hh"
+#include "COM_input_single_value_operation.hh"
+#include "COM_node_operation.hh"
+#include "COM_operation.hh"
+#include "COM_result.hh"
+#include "COM_scheduler.hh"
+#include "COM_shader_operation.hh"
+#include "COM_utilities.hh"
+
+namespace blender::realtime_compositor {
+
+using namespace nodes::derived_node_tree_types;
+
+Evaluator::Evaluator(Context &context, bNodeTree &node_tree)
+ : context_(context), node_tree_(node_tree)
+{
+}
+
+void Evaluator::evaluate()
+{
+ context_.texture_pool().reset();
+
+ if (!is_compiled_) {
+ compile_and_evaluate();
+ is_compiled_ = true;
+ return;
+ }
+
+ for (const std::unique_ptr<Operation> &operation : operations_stream_) {
+ operation->evaluate();
+ }
+}
+
+void Evaluator::reset()
+{
+ operations_stream_.clear();
+ derived_node_tree_.reset();
+ node_tree_reference_map_.clear();
+
+ is_compiled_ = false;
+}
+
+bool Evaluator::validate_node_tree()
+{
+ if (derived_node_tree_->has_link_cycles()) {
+ context_.set_info_message("Compositor node tree has cyclic links!");
+ return false;
+ }
+
+ if (derived_node_tree_->has_undefined_nodes_or_sockets()) {
+ context_.set_info_message("Compositor node tree has undefined nodes or sockets!");
+ return false;
+ }
+
+ return true;
+}
+
+void Evaluator::compile_and_evaluate()
+{
+ derived_node_tree_ = std::make_unique<DerivedNodeTree>(node_tree_, node_tree_reference_map_);
+
+ if (!validate_node_tree()) {
+ return;
+ }
+
+ const Schedule schedule = compute_schedule(*derived_node_tree_);
+
+ CompileState compile_state(schedule);
+
+ for (const DNode &node : schedule) {
+ if (compile_state.should_compile_shader_compile_unit(node)) {
+ compile_and_evaluate_shader_compile_unit(compile_state);
+ }
+
+ if (is_shader_node(node)) {
+ compile_state.add_node_to_shader_compile_unit(node);
+ }
+ else {
+ compile_and_evaluate_node(node, compile_state);
+ }
+ }
+}
+
+void Evaluator::compile_and_evaluate_node(DNode node, CompileState &compile_state)
+{
+ NodeOperation *operation = node->typeinfo()->get_compositor_operation(context_, node);
+
+ compile_state.map_node_to_node_operation(node, operation);
+
+ map_node_operation_inputs_to_their_results(node, operation, compile_state);
+
+ /* This has to be done after input mapping because the method may add Input Single Value
+ * Operations to the operations stream, which needs to be evaluated before the operation itself
+ * is evaluated. */
+ operations_stream_.append(std::unique_ptr<Operation>(operation));
+
+ operation->compute_results_reference_counts(compile_state.get_schedule());
+
+ operation->evaluate();
+}
+
+void Evaluator::map_node_operation_inputs_to_their_results(DNode node,
+ NodeOperation *operation,
+ CompileState &compile_state)
+{
+ for (const InputSocketRef *input_ref : node->inputs()) {
+ const DInputSocket input{node.context(), input_ref};
+
+ DSocket origin = get_input_origin_socket(input);
+
+ /* The origin socket is an output, which means the input is linked. So map the input to the
+ * result we get from the output. */
+ if (origin->is_output()) {
+ Result &result = compile_state.get_result_from_output_socket(DOutputSocket(origin));
+ operation->map_input_to_result(input->identifier(), &result);
+ continue;
+ }
+
+ /* Otherwise, the origin socket is an input, which either means the input is unlinked and the
+ * origin is the input socket itself or the input is connected to an unlinked input of a group
+ * input node and the origin is the input of the group input node. So map the input to the
+ * result of a newly created Input Single Value Operation. */
+ auto *input_operation = new InputSingleValueOperation(context_, DInputSocket(origin));
+ operation->map_input_to_result(input->identifier(), &input_operation->get_result());
+
+ operations_stream_.append(std::unique_ptr<InputSingleValueOperation>(input_operation));
+
+ input_operation->evaluate();
+ }
+}
+
+void Evaluator::compile_and_evaluate_shader_compile_unit(CompileState &compile_state)
+{
+ ShaderCompileUnit &compile_unit = compile_state.get_shader_compile_unit();
+ ShaderOperation *operation = new ShaderOperation(context_, compile_unit);
+
+ for (DNode node : compile_unit) {
+ compile_state.map_node_to_shader_operation(node, operation);
+ }
+
+ map_shader_operation_inputs_to_their_results(operation, compile_state);
+
+ operations_stream_.append(std::unique_ptr<Operation>(operation));
+
+ operation->compute_results_reference_counts(compile_state.get_schedule());
+
+ operation->evaluate();
+
+ compile_state.reset_shader_compile_unit();
+}
+
+void Evaluator::map_shader_operation_inputs_to_their_results(ShaderOperation *operation,
+ CompileState &compile_state)
+{
+ for (const auto &item : operation->get_inputs_to_linked_outputs_map().items()) {
+ Result &result = compile_state.get_result_from_output_socket(item.value);
+ operation->map_input_to_result(item.key, &result);
+ }
+}
+
+} // namespace blender::realtime_compositor
diff --git a/source/blender/compositor/realtime_compositor/intern/input_single_value_operation.cc b/source/blender/compositor/realtime_compositor/intern/input_single_value_operation.cc
new file mode 100644
index 00000000000..0bdd40e3636
--- /dev/null
+++ b/source/blender/compositor/realtime_compositor/intern/input_single_value_operation.cc
@@ -0,0 +1,57 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include "BLI_math_vec_types.hh"
+
+#include "COM_input_single_value_operation.hh"
+#include "COM_operation.hh"
+#include "COM_result.hh"
+#include "COM_utilities.hh"
+
+namespace blender::realtime_compositor {
+
+const StringRef InputSingleValueOperation::output_identifier_ = StringRef("Output");
+
+InputSingleValueOperation::InputSingleValueOperation(Context &context, DInputSocket input_socket)
+ : Operation(context), input_socket_(input_socket)
+{
+ const ResultType result_type = get_node_socket_result_type(input_socket_.socket_ref());
+ Result result = Result(result_type, texture_pool());
+
+ /* The result of an input single value operation is guaranteed to have a single user. */
+ result.set_initial_reference_count(1);
+
+ populate_result(result);
+}
+
+void InputSingleValueOperation::execute()
+{
+ /* Allocate a single value for the result. */
+ Result &result = get_result();
+ result.allocate_single_value();
+
+ /* Set the value of the result to the default value of the input socket. */
+ switch (result.type()) {
+ case ResultType::Float:
+ result.set_float_value(input_socket_->default_value<bNodeSocketValueFloat>()->value);
+ break;
+ case ResultType::Vector:
+ result.set_vector_value(
+ float3(input_socket_->default_value<bNodeSocketValueVector>()->value));
+ break;
+ case ResultType::Color:
+ result.set_color_value(float4(input_socket_->default_value<bNodeSocketValueRGBA>()->value));
+ break;
+ }
+}
+
+Result &InputSingleValueOperation::get_result()
+{
+ return Operation::get_result(output_identifier_);
+}
+
+void InputSingleValueOperation::populate_result(Result result)
+{
+ Operation::populate_result(output_identifier_, result);
+}
+
+} // namespace blender::realtime_compositor
diff --git a/source/blender/compositor/realtime_compositor/intern/node_operation.cc b/source/blender/compositor/realtime_compositor/intern/node_operation.cc
new file mode 100644
index 00000000000..f02d0906447
--- /dev/null
+++ b/source/blender/compositor/realtime_compositor/intern/node_operation.cc
@@ -0,0 +1,67 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include <memory>
+
+#include "BLI_map.hh"
+#include "BLI_string_ref.hh"
+#include "BLI_vector.hh"
+
+#include "DNA_node_types.h"
+
+#include "NOD_derived_node_tree.hh"
+#include "NOD_node_declaration.hh"
+
+#include "COM_context.hh"
+#include "COM_input_descriptor.hh"
+#include "COM_node_operation.hh"
+#include "COM_operation.hh"
+#include "COM_result.hh"
+#include "COM_scheduler.hh"
+#include "COM_utilities.hh"
+
+namespace blender::realtime_compositor {
+
+using namespace nodes::derived_node_tree_types;
+
+NodeOperation::NodeOperation(Context &context, DNode node) : Operation(context), node_(node)
+{
+ for (const OutputSocketRef *output : node->outputs()) {
+ const ResultType result_type = get_node_socket_result_type(output);
+ const Result result = Result(result_type, texture_pool());
+ populate_result(output->identifier(), result);
+ }
+
+ for (const InputSocketRef *input : node->inputs()) {
+ const InputDescriptor input_descriptor = input_descriptor_from_input_socket(input);
+ declare_input_descriptor(input->identifier(), input_descriptor);
+ }
+}
+
+void NodeOperation::compute_results_reference_counts(const Schedule &schedule)
+{
+ for (const OutputSocketRef *output_ref : node()->outputs()) {
+ const DOutputSocket output{node().context(), output_ref};
+
+ const int reference_count = number_of_inputs_linked_to_output_conditioned(
+ output, [&](DInputSocket input) { return schedule.contains(input.node()); });
+
+ get_result(output->identifier()).set_initial_reference_count(reference_count);
+ }
+}
+
+const DNode &NodeOperation::node() const
+{
+ return node_;
+}
+
+const bNode &NodeOperation::bnode() const
+{
+ return *node_->bnode();
+}
+
+bool NodeOperation::should_compute_output(StringRef identifier)
+{
+ return get_result(identifier).should_compute();
+}
+
+} // namespace blender::realtime_compositor
diff --git a/source/blender/compositor/realtime_compositor/intern/operation.cc b/source/blender/compositor/realtime_compositor/intern/operation.cc
new file mode 100644
index 00000000000..42dd5aeebe8
--- /dev/null
+++ b/source/blender/compositor/realtime_compositor/intern/operation.cc
@@ -0,0 +1,201 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include <limits>
+#include <memory>
+
+#include "BLI_map.hh"
+#include "BLI_string_ref.hh"
+#include "BLI_vector.hh"
+
+#include "COM_context.hh"
+#include "COM_conversion_operation.hh"
+#include "COM_domain.hh"
+#include "COM_input_descriptor.hh"
+#include "COM_operation.hh"
+#include "COM_realize_on_domain_operation.hh"
+#include "COM_reduce_to_single_value_operation.hh"
+#include "COM_result.hh"
+#include "COM_simple_operation.hh"
+#include "COM_static_shader_manager.hh"
+#include "COM_texture_pool.hh"
+
+namespace blender::realtime_compositor {
+
+Operation::Operation(Context &context) : context_(context)
+{
+}
+
+Operation::~Operation() = default;
+
+void Operation::evaluate()
+{
+ evaluate_input_processors();
+
+ reset_results();
+
+ execute();
+
+ release_inputs();
+}
+
+Result &Operation::get_result(StringRef identifier)
+{
+ return results_.lookup(identifier);
+}
+
+void Operation::map_input_to_result(StringRef identifier, Result *result)
+{
+ results_mapped_to_inputs_.add_new(identifier, result);
+}
+
+Domain Operation::compute_domain()
+{
+ /* Default to an identity domain in case no domain input was found, most likely because all
+ * inputs are single values. */
+ Domain operation_domain = Domain::identity();
+ int current_domain_priority = std::numeric_limits<int>::max();
+
+ /* Go over the inputs and find the domain of the non single value input with the highest domain
+ * priority. */
+ for (StringRef identifier : input_descriptors_.keys()) {
+ const Result &result = get_input(identifier);
+ const InputDescriptor &descriptor = get_input_descriptor(identifier);
+
+ /* A single value input can't be a domain input. */
+ if (result.is_single_value() || descriptor.expects_single_value) {
+ continue;
+ }
+
+ /* Notice that the lower the domain priority value is, the higher the priority is, hence the
+ * less than comparison. */
+ if (descriptor.domain_priority < current_domain_priority) {
+ operation_domain = result.domain();
+ current_domain_priority = descriptor.domain_priority;
+ }
+ }
+
+ return operation_domain;
+}
+
+void Operation::add_and_evaluate_input_processors()
+{
+ /* Each input processor type is added to all inputs entirely before the next type. This is done
+ * because the construction of the input processors may depend on the result of previous input
+ * processors for all inputs. For instance, the realize on domain input processor considers the
+ * value of all inputs, so previous input processors for all inputs needs to be added and
+ * evaluated first. */
+
+ for (const StringRef &identifier : results_mapped_to_inputs_.keys()) {
+ SimpleOperation *single_value = ReduceToSingleValueOperation::construct_if_needed(
+ context(), get_input(identifier));
+ add_and_evaluate_input_processor(identifier, single_value);
+ }
+
+ for (const StringRef &identifier : results_mapped_to_inputs_.keys()) {
+ SimpleOperation *conversion = ConversionOperation::construct_if_needed(
+ context(), get_input(identifier), get_input_descriptor(identifier));
+ add_and_evaluate_input_processor(identifier, conversion);
+ }
+
+ for (const StringRef &identifier : results_mapped_to_inputs_.keys()) {
+ SimpleOperation *realize_on_domain = RealizeOnDomainOperation::construct_if_needed(
+ context(), get_input(identifier), get_input_descriptor(identifier), compute_domain());
+ add_and_evaluate_input_processor(identifier, realize_on_domain);
+ }
+}
+
+void Operation::add_and_evaluate_input_processor(StringRef identifier, SimpleOperation *processor)
+{
+ /* Allow null inputs to facilitate construct_if_needed pattern of addition. For instance, see the
+ * implementation of the add_and_evaluate_input_processors method. */
+ if (!processor) {
+ return;
+ }
+
+ ProcessorsVector &processors = input_processors_.lookup_or_add_default(identifier);
+
+ /* Get the result that should serve as the input for the processor. This is either the result
+ * mapped to the input or the result of the last processor depending on whether this is the first
+ * processor or not. */
+ Result &result = processors.is_empty() ? get_input(identifier) : processors.last()->get_result();
+
+ /* Map the input result of the processor and add it to the processors vector. */
+ processor->map_input_to_result(&result);
+ processors.append(std::unique_ptr<SimpleOperation>(processor));
+
+ /* Switch the result mapped to the input to be the output result of the processor. */
+ switch_result_mapped_to_input(identifier, &processor->get_result());
+
+ processor->evaluate();
+}
+
+Result &Operation::get_input(StringRef identifier) const
+{
+ return *results_mapped_to_inputs_.lookup(identifier);
+}
+
+void Operation::switch_result_mapped_to_input(StringRef identifier, Result *result)
+{
+ results_mapped_to_inputs_.lookup(identifier) = result;
+}
+
+void Operation::populate_result(StringRef identifier, Result result)
+{
+ results_.add_new(identifier, result);
+}
+
+void Operation::declare_input_descriptor(StringRef identifier, InputDescriptor descriptor)
+{
+ input_descriptors_.add_new(identifier, descriptor);
+}
+
+InputDescriptor &Operation::get_input_descriptor(StringRef identifier)
+{
+ return input_descriptors_.lookup(identifier);
+}
+
+Context &Operation::context()
+{
+ return context_;
+}
+
+TexturePool &Operation::texture_pool() const
+{
+ return context_.texture_pool();
+}
+
+StaticShaderManager &Operation::shader_manager() const
+{
+ return context_.shader_manager();
+}
+
+void Operation::evaluate_input_processors()
+{
+ if (!input_processors_added_) {
+ add_and_evaluate_input_processors();
+ input_processors_added_ = true;
+ return;
+ }
+
+ for (const ProcessorsVector &processors : input_processors_.values()) {
+ for (const std::unique_ptr<SimpleOperation> &processor : processors) {
+ processor->evaluate();
+ }
+ }
+}
+
+void Operation::reset_results()
+{
+ for (Result &result : results_.values()) {
+ result.reset();
+ }
+}
+
+void Operation::release_inputs()
+{
+ for (Result *result : results_mapped_to_inputs_.values()) {
+ result->release();
+ }
+}
+
+} // namespace blender::realtime_compositor
diff --git a/source/blender/compositor/realtime_compositor/intern/realize_on_domain_operation.cc b/source/blender/compositor/realtime_compositor/intern/realize_on_domain_operation.cc
new file mode 100644
index 00000000000..47993060a74
--- /dev/null
+++ b/source/blender/compositor/realtime_compositor/intern/realize_on_domain_operation.cc
@@ -0,0 +1,130 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include "BLI_float3x3.hh"
+#include "BLI_math_vec_types.hh"
+#include "BLI_utildefines.h"
+
+#include "GPU_shader.h"
+#include "GPU_texture.h"
+
+#include "COM_context.hh"
+#include "COM_domain.hh"
+#include "COM_input_descriptor.hh"
+#include "COM_realize_on_domain_operation.hh"
+#include "COM_result.hh"
+#include "COM_utilities.hh"
+
+namespace blender::realtime_compositor {
+
+RealizeOnDomainOperation::RealizeOnDomainOperation(Context &context,
+ Domain domain,
+ ResultType type)
+ : SimpleOperation(context), domain_(domain)
+{
+ InputDescriptor input_descriptor;
+ input_descriptor.type = type;
+ declare_input_descriptor(input_descriptor);
+ populate_result(Result(type, texture_pool()));
+}
+
+void RealizeOnDomainOperation::execute()
+{
+ Result &input = get_input();
+ Result &result = get_result();
+
+ result.allocate_texture(domain_);
+
+ GPUShader *shader = get_realization_shader();
+ GPU_shader_bind(shader);
+
+ /* Transform the input space into the domain space. */
+ const float3x3 local_transformation = input.domain().transformation *
+ domain_.transformation.inverted();
+
+ /* Set the origin of the transformation to be the center of the domain. */
+ const float3x3 transformation = float3x3::from_origin_transformation(
+ local_transformation, float2(domain_.size) / 2.0f);
+
+ /* Invert the transformation because the shader transforms the domain coordinates instead of the
+ * input image itself and thus expect the inverse. */
+ const float3x3 inverse_transformation = transformation.inverted();
+
+ GPU_shader_uniform_mat3_as_mat4(shader, "inverse_transformation", inverse_transformation.ptr());
+
+ /* The texture sampler should use bilinear interpolation for both the bilinear and bicubic
+ * cases, as the logic used by the bicubic realization shader expects textures to use bilinear
+ * interpolation. */
+ const bool use_bilinear = ELEM(input.get_realization_options().interpolation,
+ Interpolation::Bilinear,
+ Interpolation::Bicubic);
+ GPU_texture_filter_mode(input.texture(), use_bilinear);
+
+ /* Make out-of-bound texture access return zero by clamping to border color. And make texture
+ * wrap appropriately if the input repeats. */
+ const bool repeats = input.get_realization_options().repeat_x ||
+ input.get_realization_options().repeat_y;
+ GPU_texture_wrap_mode(input.texture(), repeats, false);
+
+ input.bind_as_texture(shader, "input_tx");
+ result.bind_as_image(shader, "domain_img");
+
+ compute_dispatch_threads_at_least(shader, domain_.size);
+
+ input.unbind_as_texture();
+ result.unbind_as_image();
+ GPU_shader_unbind();
+}
+
+GPUShader *RealizeOnDomainOperation::get_realization_shader()
+{
+ switch (get_result().type()) {
+ case ResultType::Color:
+ return shader_manager().get("compositor_realize_on_domain_color");
+ case ResultType::Vector:
+ return shader_manager().get("compositor_realize_on_domain_vector");
+ case ResultType::Float:
+ return shader_manager().get("compositor_realize_on_domain_float");
+ }
+
+ BLI_assert_unreachable();
+ return nullptr;
+}
+
+Domain RealizeOnDomainOperation::compute_domain()
+{
+ return domain_;
+}
+
+SimpleOperation *RealizeOnDomainOperation::construct_if_needed(
+ Context &context,
+ const Result &input_result,
+ const InputDescriptor &input_descriptor,
+ const Domain &operation_domain)
+{
+ /* This input wants to skip realization, the operation is not needed. */
+ if (input_descriptor.skip_realization) {
+ return nullptr;
+ }
+
+ /* The input expects a single value and if no single value is provided, it will be ignored and a
+ * default value will be used, so no need to realize it and the operation is not needed. */
+ if (input_descriptor.expects_single_value) {
+ return nullptr;
+ }
+
+ /* Input result is a single value and does not need realization, the operation is not needed. */
+ if (input_result.is_single_value()) {
+ return nullptr;
+ }
+
+ /* The input have an identical domain to the operation domain, so no need to realize it and the
+ * operation is not needed. */
+ if (input_result.domain() == operation_domain) {
+ return nullptr;
+ }
+
+ /* Otherwise, realization is needed. */
+ return new RealizeOnDomainOperation(context, operation_domain, input_descriptor.type);
+}
+
+} // namespace blender::realtime_compositor
diff --git a/source/blender/compositor/realtime_compositor/intern/reduce_to_single_value_operation.cc b/source/blender/compositor/realtime_compositor/intern/reduce_to_single_value_operation.cc
new file mode 100644
index 00000000000..acc9b4ab7d6
--- /dev/null
+++ b/source/blender/compositor/realtime_compositor/intern/reduce_to_single_value_operation.cc
@@ -0,0 +1,67 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include "GPU_state.h"
+#include "GPU_texture.h"
+
+#include "MEM_guardedalloc.h"
+
+#include "COM_context.hh"
+#include "COM_input_descriptor.hh"
+#include "COM_reduce_to_single_value_operation.hh"
+#include "COM_result.hh"
+
+namespace blender::realtime_compositor {
+
+ReduceToSingleValueOperation::ReduceToSingleValueOperation(Context &context, ResultType type)
+ : SimpleOperation(context)
+{
+ InputDescriptor input_descriptor;
+ input_descriptor.type = type;
+ declare_input_descriptor(input_descriptor);
+ populate_result(Result(type, texture_pool()));
+}
+
+void ReduceToSingleValueOperation::execute()
+{
+ /* Make sure any prior writes to the texture are reflected before downloading it. */
+ GPU_memory_barrier(GPU_BARRIER_TEXTURE_UPDATE);
+
+ const Result &input = get_input();
+ float *pixel = static_cast<float *>(GPU_texture_read(input.texture(), GPU_DATA_FLOAT, 0));
+
+ Result &result = get_result();
+ result.allocate_single_value();
+ switch (result.type()) {
+ case ResultType::Color:
+ result.set_color_value(pixel);
+ break;
+ case ResultType::Vector:
+ result.set_vector_value(pixel);
+ break;
+ case ResultType::Float:
+ result.set_float_value(*pixel);
+ break;
+ }
+
+ MEM_freeN(pixel);
+}
+
+SimpleOperation *ReduceToSingleValueOperation::construct_if_needed(Context &context,
+ const Result &input_result)
+{
+ /* Input result is already a single value, the operation is not needed. */
+ if (input_result.is_single_value()) {
+ return nullptr;
+ }
+
+ /* The input is a full sized texture and can't be reduced to a single value, the operation is not
+ * needed. */
+ if (input_result.domain().size != int2(1)) {
+ return nullptr;
+ }
+
+ /* The input is a texture of a single pixel and can be reduced to a single value. */
+ return new ReduceToSingleValueOperation(context, input_result.type());
+}
+
+} // namespace blender::realtime_compositor
diff --git a/source/blender/compositor/realtime_compositor/intern/result.cc b/source/blender/compositor/realtime_compositor/intern/result.cc
new file mode 100644
index 00000000000..8059367d211
--- /dev/null
+++ b/source/blender/compositor/realtime_compositor/intern/result.cc
@@ -0,0 +1,257 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include "BLI_float3x3.hh"
+#include "BLI_math_vec_types.hh"
+
+#include "GPU_shader.h"
+#include "GPU_state.h"
+#include "GPU_texture.h"
+
+#include "COM_domain.hh"
+#include "COM_result.hh"
+#include "COM_texture_pool.hh"
+
+namespace blender::realtime_compositor {
+
+Result::Result(ResultType type, TexturePool &texture_pool)
+ : type_(type), texture_pool_(&texture_pool)
+{
+}
+
+void Result::allocate_texture(Domain domain)
+{
+ is_single_value_ = false;
+ switch (type_) {
+ case ResultType::Float:
+ texture_ = texture_pool_->acquire_float(domain.size);
+ break;
+ case ResultType::Vector:
+ texture_ = texture_pool_->acquire_vector(domain.size);
+ break;
+ case ResultType::Color:
+ texture_ = texture_pool_->acquire_color(domain.size);
+ break;
+ }
+ domain_ = domain;
+}
+
+void Result::allocate_single_value()
+{
+ is_single_value_ = true;
+ /* Single values are stored in 1x1 textures as well as the single value members. */
+ const int2 texture_size{1, 1};
+ switch (type_) {
+ case ResultType::Float:
+ texture_ = texture_pool_->acquire_float(texture_size);
+ break;
+ case ResultType::Vector:
+ texture_ = texture_pool_->acquire_vector(texture_size);
+ break;
+ case ResultType::Color:
+ texture_ = texture_pool_->acquire_color(texture_size);
+ break;
+ }
+ domain_ = Domain::identity();
+}
+
+void Result::allocate_invalid()
+{
+ allocate_single_value();
+ switch (type_) {
+ case ResultType::Float:
+ set_float_value(0.0f);
+ break;
+ case ResultType::Vector:
+ set_vector_value(float3(0.0f));
+ break;
+ case ResultType::Color:
+ set_color_value(float4(0.0f));
+ break;
+ }
+}
+
+void Result::bind_as_texture(GPUShader *shader, const char *texture_name) const
+{
+ /* Make sure any prior writes to the texture are reflected before reading from it. */
+ GPU_memory_barrier(GPU_BARRIER_TEXTURE_FETCH);
+
+ const int texture_image_unit = GPU_shader_get_texture_binding(shader, texture_name);
+ GPU_texture_bind(texture_, texture_image_unit);
+}
+
+void Result::bind_as_image(GPUShader *shader, const char *image_name) const
+{
+ const int image_unit = GPU_shader_get_texture_binding(shader, image_name);
+ GPU_texture_image_bind(texture_, image_unit);
+}
+
+void Result::unbind_as_texture() const
+{
+ GPU_texture_unbind(texture_);
+}
+
+void Result::unbind_as_image() const
+{
+ GPU_texture_image_unbind(texture_);
+}
+
+void Result::pass_through(Result &target)
+{
+ /* Increment the reference count of the master by the original reference count of the target. */
+ increment_reference_count(target.reference_count());
+
+ /* Make the target an exact copy of this result, but keep the initial reference count, as this is
+ * a property of the original result and is needed for correctly resetting the result before the
+ * next evaluation. */
+ const int initial_reference_count = target.initial_reference_count_;
+ target = *this;
+ target.initial_reference_count_ = initial_reference_count;
+
+ target.master_ = this;
+}
+
+void Result::transform(const float3x3 &transformation)
+{
+ domain_.transform(transformation);
+}
+
+RealizationOptions &Result::get_realization_options()
+{
+ return domain_.realization_options;
+}
+
+float Result::get_float_value() const
+{
+ return float_value_;
+}
+
+float3 Result::get_vector_value() const
+{
+ return vector_value_;
+}
+
+float4 Result::get_color_value() const
+{
+ return color_value_;
+}
+
+float Result::get_float_value_default(float default_value) const
+{
+ if (is_single_value()) {
+ return get_float_value();
+ }
+ return default_value;
+}
+
+float3 Result::get_vector_value_default(const float3 &default_value) const
+{
+ if (is_single_value()) {
+ return get_vector_value();
+ }
+ return default_value;
+}
+
+float4 Result::get_color_value_default(const float4 &default_value) const
+{
+ if (is_single_value()) {
+ return get_color_value();
+ }
+ return default_value;
+}
+
+void Result::set_float_value(float value)
+{
+ float_value_ = value;
+ GPU_texture_update(texture_, GPU_DATA_FLOAT, &float_value_);
+}
+
+void Result::set_vector_value(const float3 &value)
+{
+ vector_value_ = value;
+ GPU_texture_update(texture_, GPU_DATA_FLOAT, vector_value_);
+}
+
+void Result::set_color_value(const float4 &value)
+{
+ color_value_ = value;
+ GPU_texture_update(texture_, GPU_DATA_FLOAT, color_value_);
+}
+
+void Result::set_initial_reference_count(int count)
+{
+ initial_reference_count_ = count;
+}
+
+void Result::reset()
+{
+ master_ = nullptr;
+ reference_count_ = initial_reference_count_;
+}
+
+void Result::increment_reference_count(int count)
+{
+ /* If there is a master result, increment its reference count instead. */
+ if (master_) {
+ master_->increment_reference_count(count);
+ return;
+ }
+
+ reference_count_ += count;
+}
+
+void Result::release()
+{
+ /* If there is a master result, release it instead. */
+ if (master_) {
+ master_->release();
+ return;
+ }
+
+ /* Decrement the reference count, and if it reaches zero, release the texture back into the
+ * texture pool. */
+ reference_count_--;
+ if (reference_count_ == 0) {
+ texture_pool_->release(texture_);
+ }
+}
+
+bool Result::should_compute()
+{
+ return initial_reference_count_ != 0;
+}
+
+ResultType Result::type() const
+{
+ return type_;
+}
+
+bool Result::is_texture() const
+{
+ return !is_single_value_;
+}
+
+bool Result::is_single_value() const
+{
+ return is_single_value_;
+}
+
+GPUTexture *Result::texture() const
+{
+ return texture_;
+}
+
+int Result::reference_count() const
+{
+ /* If there is a master result, return its reference count instead. */
+ if (master_) {
+ return master_->reference_count();
+ }
+ return reference_count_;
+}
+
+const Domain &Result::domain() const
+{
+ return domain_;
+}
+
+} // namespace blender::realtime_compositor
diff --git a/source/blender/compositor/realtime_compositor/intern/scheduler.cc b/source/blender/compositor/realtime_compositor/intern/scheduler.cc
new file mode 100644
index 00000000000..ce8b9330541
--- /dev/null
+++ b/source/blender/compositor/realtime_compositor/intern/scheduler.cc
@@ -0,0 +1,311 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include "BLI_map.hh"
+#include "BLI_set.hh"
+#include "BLI_stack.hh"
+#include "BLI_vector.hh"
+#include "BLI_vector_set.hh"
+
+#include "NOD_derived_node_tree.hh"
+
+#include "COM_scheduler.hh"
+#include "COM_utilities.hh"
+
+namespace blender::realtime_compositor {
+
+using namespace nodes::derived_node_tree_types;
+
+/* Compute the output node whose result should be computed. The output node is the node marked as
+ * NODE_DO_OUTPUT. If multiple types of output nodes are marked, then the preference will be
+ * CMP_NODE_COMPOSITE > CMP_NODE_VIEWER > CMP_NODE_SPLITVIEWER. If no output node exists, a null
+ * node will be returned. */
+static DNode compute_output_node(DerivedNodeTree &tree)
+{
+ const NodeTreeRef &root_tree = tree.root_context().tree();
+
+ for (const NodeRef *node : root_tree.nodes_by_type("CompositorNodeComposite")) {
+ if (node->bnode()->flag & NODE_DO_OUTPUT) {
+ return DNode(&tree.root_context(), node);
+ }
+ }
+
+ for (const NodeRef *node : root_tree.nodes_by_type("CompositorNodeViewer")) {
+ if (node->bnode()->flag & NODE_DO_OUTPUT) {
+ return DNode(&tree.root_context(), node);
+ }
+ }
+
+ for (const NodeRef *node : root_tree.nodes_by_type("CompositorNodeSplitViewer")) {
+ if (node->bnode()->flag & NODE_DO_OUTPUT) {
+ return DNode(&tree.root_context(), node);
+ }
+ }
+
+ /* No output node found, return a null node. */
+ return DNode();
+}
+
+/* A type representing a mapping that associates each node with a heuristic estimation of the
+ * number of intermediate buffers needed to compute it and all of its dependencies. See the
+ * compute_number_of_needed_buffers function for more information. */
+using NeededBuffers = Map<DNode, int>;
+
+/* Compute a heuristic estimation of the number of intermediate buffers needed to compute each node
+ * and all of its dependencies for all nodes that the given node depends on. The output is a map
+ * that maps each node with the number of intermediate buffers needed to compute it and all of its
+ * dependencies.
+ *
+ * Consider a node that takes n number of buffers as an input from a number of node dependencies,
+ * which we shall call the input nodes. The node also computes and outputs m number of buffers.
+ * In order for the node to compute its output, a number of intermediate buffers will be needed.
+ * Since the node takes n buffers and outputs m buffers, then the number of buffers directly
+ * needed by the node is (n + m). But each of the input buffers are computed by a node that, in
+ * turn, needs a number of buffers to compute its output. So the total number of buffers needed
+ * to compute the output of the node is max(n + m, d) where d is the number of buffers needed by
+ * the input node that needs the largest number of buffers. We only consider the input node that
+ * needs the largest number of buffers, because those buffers can be reused by any input node
+ * that needs a lesser number of buffers.
+ *
+ * Shader nodes, however, are a special case because links between two shader nodes inside the same
+ * shader operation don't pass a buffer, but a single value in the compiled shader. So for shader
+ * nodes, only inputs and outputs linked to nodes that are not shader nodes should be considered.
+ * Note that this might not actually be true, because the compiler may decide to split a shader
+ * operation into multiples ones that will pass buffers, but this is not something that can be
+ * known at scheduling-time. See the discussion in COM_compile_state.hh, COM_evaluator.hh, and
+ * COM_shader_operation.hh for more information. In the node tree shown below, node 4 will have
+ * exactly the same number of needed buffers by node 3, because its inputs and outputs are all
+ * internally linked in the shader operation.
+ *
+ * Shader Operation
+ * +------------------------------------------------------+
+ * .------------. | .------------. .------------. .------------. | .------------.
+ * | Node 1 | | | Node 3 | | Node 4 | | Node 5 | | | Node 6 |
+ * | |----|--| |--| |------| |--|--| |
+ * | | .-|--| | | | .---| | | | |
+ * '------------' | | '------------' '------------' | '------------' | '------------'
+ * | +----------------------------------|-------------------+
+ * .------------. | |
+ * | Node 2 | | |
+ * | |--'------------------------------------'
+ * | |
+ * '------------'
+ *
+ * Note that the computed output is not guaranteed to be accurate, and will not be in most cases.
+ * The computation is merely a heuristic estimation that works well in most cases. This is due to a
+ * number of reasons:
+ * - The node tree is actually a graph that allows output sharing, which is not something that was
+ * taken into consideration in this implementation because it is difficult to correctly consider.
+ * - Each node may allocate any number of internal buffers, which is not taken into account in this
+ * implementation because it rarely affects the output and is done by very few nodes.
+ * - The compiler may decide to compiler the schedule differently depending on runtime information
+ * which we can merely speculate at scheduling-time as described above. */
+static NeededBuffers compute_number_of_needed_buffers(DNode output_node)
+{
+ NeededBuffers needed_buffers;
+
+ /* A stack of nodes used to traverse the node tree starting from the output node. */
+ Stack<DNode> node_stack = {output_node};
+
+ /* Traverse the node tree in a post order depth first manner and compute the number of needed
+ * buffers for each node. Post order traversal guarantee that all the node dependencies of each
+ * node are computed before it. This is done by pushing all the uncomputed node dependencies to
+ * the node stack first and only popping and computing the node when all its node dependencies
+ * were computed. */
+ while (!node_stack.is_empty()) {
+ /* Do not pop the node immediately, as it may turn out that we can't compute its number of
+ * needed buffers just yet because its dependencies weren't computed, it will be popped later
+ * when needed. */
+ DNode &node = node_stack.peek();
+
+ /* Go over the node dependencies connected to the inputs of the node and push them to the node
+ * stack if they were not computed already. */
+ Set<DNode> pushed_nodes;
+ for (const InputSocketRef *input_ref : node->inputs()) {
+ const DInputSocket input{node.context(), input_ref};
+
+ /* Get the output linked to the input. If it is null, that means the input is unlinked and
+ * has no dependency node. */
+ const DOutputSocket output = get_output_linked_to_input(input);
+ if (!output) {
+ continue;
+ }
+
+ /* The node dependency was already computed or pushed before, so skip it. */
+ if (needed_buffers.contains(output.node()) || pushed_nodes.contains(output.node())) {
+ continue;
+ }
+
+ /* The output node needs to be computed, push the node dependency to the node stack and
+ * indicate that it was pushed. */
+ node_stack.push(output.node());
+ pushed_nodes.add_new(output.node());
+ }
+
+ /* If any of the node dependencies were pushed, that means that not all of them were computed
+ * and consequently we can't compute the number of needed buffers for this node just yet. */
+ if (!pushed_nodes.is_empty()) {
+ continue;
+ }
+
+ /* We don't need to store the result of the pop because we already peeked at it before. */
+ node_stack.pop();
+
+ /* Compute the number of buffers that the node takes as an input as well as the number of
+ * buffers needed to compute the most demanding of the node dependencies. */
+ int number_of_input_buffers = 0;
+ int buffers_needed_by_dependencies = 0;
+ for (const InputSocketRef *input_ref : node->inputs()) {
+ const DInputSocket input{node.context(), input_ref};
+
+ /* Get the output linked to the input. If it is null, that means the input is unlinked.
+ * Unlinked inputs do not take a buffer, so skip those inputs. */
+ const DOutputSocket output = get_output_linked_to_input(input);
+ if (!output) {
+ continue;
+ }
+
+ /* Since this input is linked, if the link is not between two shader nodes, it means that the
+ * node takes a buffer through this input and so we increment the number of input buffers. */
+ if (!is_shader_node(node) || !is_shader_node(output.node())) {
+ number_of_input_buffers++;
+ }
+
+ /* If the number of buffers needed by the node dependency is more than the total number of
+ * buffers needed by the dependencies, then update the latter to be the former. This is
+ * computing the "d" in the aforementioned equation "max(n + m, d)". */
+ const int buffers_needed_by_dependency = needed_buffers.lookup(output.node());
+ if (buffers_needed_by_dependency > buffers_needed_by_dependencies) {
+ buffers_needed_by_dependencies = buffers_needed_by_dependency;
+ }
+ }
+
+ /* Compute the number of buffers that will be computed/output by this node. */
+ int number_of_output_buffers = 0;
+ for (const OutputSocketRef *output_ref : node->outputs()) {
+ const DOutputSocket output{node.context(), output_ref};
+
+ /* The output is not linked, it outputs no buffer. */
+ if (output->logically_linked_sockets().is_empty()) {
+ continue;
+ }
+
+ /* If any of the links is not between two shader nodes, it means that the node outputs
+ * a buffer through this output and so we increment the number of output buffers. */
+ if (!is_output_linked_to_node_conditioned(output, is_shader_node) || !is_shader_node(node)) {
+ number_of_output_buffers++;
+ }
+ }
+
+ /* Compute the heuristic estimation of the number of needed intermediate buffers to compute
+ * this node and all of its dependencies. This is computing the aforementioned equation
+ * "max(n + m, d)". */
+ const int total_buffers = MAX2(number_of_input_buffers + number_of_output_buffers,
+ buffers_needed_by_dependencies);
+ needed_buffers.add(node, total_buffers);
+ }
+
+ return needed_buffers;
+}
+
+/* There are multiple different possible orders of evaluating a node graph, each of which needs
+ * to allocate a number of intermediate buffers to store its intermediate results. It follows
+ * that we need to find the evaluation order which uses the least amount of intermediate buffers.
+ * For instance, consider a node that takes two input buffers A and B. Each of those buffers is
+ * computed through a number of nodes constituting a sub-graph whose root is the node that
+ * outputs that buffer. Suppose the number of intermediate buffers needed to compute A and B are
+ * N(A) and N(B) respectively and N(A) > N(B). Then evaluating the sub-graph computing A would be
+ * a better option than that of B, because had B was computed first, its outputs will need to be
+ * stored in extra buffers in addition to the buffers needed by A. The number of buffers needed by
+ * each node is estimated as described in the compute_number_of_needed_buffers function.
+ *
+ * This is a heuristic generalization of the Sethi–Ullman algorithm, a generalization that
+ * doesn't always guarantee an optimal evaluation order, as the optimal evaluation order is very
+ * difficult to compute, however, this method works well in most cases. Moreover it assumes that
+ * all buffers will have roughly the same size, which may not always be the case. */
+Schedule compute_schedule(DerivedNodeTree &tree)
+{
+ Schedule schedule;
+
+ /* Compute the output node whose result should be computed. */
+ const DNode output_node = compute_output_node(tree);
+
+ /* No output node, the node tree has no effect, return an empty schedule. */
+ if (!output_node) {
+ return schedule;
+ }
+
+ /* Compute the number of buffers needed by each node connected to the output. */
+ const NeededBuffers needed_buffers = compute_number_of_needed_buffers(output_node);
+
+ /* A stack of nodes used to traverse the node tree starting from the output node. */
+ Stack<DNode> node_stack = {output_node};
+
+ /* Traverse the node tree in a post order depth first manner, scheduling the nodes in an order
+ * informed by the number of buffers needed by each node. Post order traversal guarantee that all
+ * the node dependencies of each node are scheduled before it. This is done by pushing all the
+ * unscheduled node dependencies to the node stack first and only popping and scheduling the node
+ * when all its node dependencies were scheduled. */
+ while (!node_stack.is_empty()) {
+ /* Do not pop the node immediately, as it may turn out that we can't schedule it just yet
+ * because its dependencies weren't scheduled, it will be popped later when needed. */
+ DNode &node = node_stack.peek();
+
+ /* Compute the nodes directly connected to the node inputs sorted by their needed buffers such
+ * that the node with the lowest number of needed buffers comes first. Note that we actually
+ * want the node with the highest number of needed buffers to be schedule first, but since
+ * those are pushed to the traversal stack, we need to push them in reverse order. */
+ Vector<DNode> sorted_dependency_nodes;
+ for (const InputSocketRef *input_ref : node->inputs()) {
+ const DInputSocket input{node.context(), input_ref};
+
+ /* Get the output linked to the input. If it is null, that means the input is unlinked and
+ * has no dependency node, so skip it. */
+ const DOutputSocket output = get_output_linked_to_input(input);
+ if (!output) {
+ continue;
+ }
+
+ /* The dependency node was added before, so skip it. The number of dependency nodes is very
+ * small, typically less than 3, so a linear search is okay. */
+ if (sorted_dependency_nodes.contains(output.node())) {
+ continue;
+ }
+
+ /* The dependency node was already schedule, so skip it. */
+ if (schedule.contains(output.node())) {
+ continue;
+ }
+
+ /* Sort in ascending order on insertion, the number of dependency nodes is very small,
+ * typically less than 3, so insertion sort is okay. */
+ int insertion_position = 0;
+ for (int i = 0; i < sorted_dependency_nodes.size(); i++) {
+ if (needed_buffers.lookup(output.node()) >
+ needed_buffers.lookup(sorted_dependency_nodes[i])) {
+ insertion_position++;
+ }
+ else {
+ break;
+ }
+ }
+ sorted_dependency_nodes.insert(insertion_position, output.node());
+ }
+
+ /* Push the sorted dependency nodes to the node stack in order. */
+ for (const DNode &dependency_node : sorted_dependency_nodes) {
+ node_stack.push(dependency_node);
+ }
+
+ /* If there are no sorted dependency nodes, that means they were all already scheduled or that
+ * none exists in the first place, so we can pop and schedule the node now. */
+ if (sorted_dependency_nodes.is_empty()) {
+ /* The node might have already been scheduled, so we don't use add_new here and simply don't
+ * add it if it was already scheduled. */
+ schedule.add(node_stack.pop());
+ }
+ }
+
+ return schedule;
+}
+
+} // namespace blender::realtime_compositor
diff --git a/source/blender/compositor/realtime_compositor/intern/shader_node.cc b/source/blender/compositor/realtime_compositor/intern/shader_node.cc
new file mode 100644
index 00000000000..f23485cee96
--- /dev/null
+++ b/source/blender/compositor/realtime_compositor/intern/shader_node.cc
@@ -0,0 +1,155 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include "BLI_assert.h"
+#include "BLI_math_vector.h"
+#include "BLI_string_ref.hh"
+
+#include "DNA_node_types.h"
+
+#include "NOD_derived_node_tree.hh"
+
+#include "GPU_material.h"
+
+#include "COM_shader_node.hh"
+#include "COM_utilities.hh"
+
+namespace blender::realtime_compositor {
+
+using namespace nodes::derived_node_tree_types;
+
+ShaderNode::ShaderNode(DNode node) : node_(node)
+{
+ populate_inputs();
+ populate_outputs();
+}
+
+GPUNodeStack *ShaderNode::get_inputs_array()
+{
+ return inputs_.data();
+}
+
+GPUNodeStack *ShaderNode::get_outputs_array()
+{
+ return outputs_.data();
+}
+
+GPUNodeStack &ShaderNode::get_input(StringRef identifier)
+{
+ return inputs_[node_.input_by_identifier(identifier)->index()];
+}
+
+GPUNodeStack &ShaderNode::get_output(StringRef identifier)
+{
+ return outputs_[node_.output_by_identifier(identifier)->index()];
+}
+
+GPUNodeLink *ShaderNode::get_input_link(StringRef identifier)
+{
+ GPUNodeStack &input = get_input(identifier);
+ if (input.link) {
+ return input.link;
+ }
+ return GPU_uniform(input.vec);
+}
+
+const DNode &ShaderNode::node() const
+{
+ return node_;
+}
+
+bNode &ShaderNode::bnode() const
+{
+ return *node_->bnode();
+}
+
+static eGPUType gpu_type_from_socket_type(eNodeSocketDatatype type)
+{
+ switch (type) {
+ case SOCK_FLOAT:
+ return GPU_FLOAT;
+ case SOCK_VECTOR:
+ return GPU_VEC3;
+ case SOCK_RGBA:
+ return GPU_VEC4;
+ default:
+ BLI_assert_unreachable();
+ return GPU_NONE;
+ }
+}
+
+static void gpu_stack_vector_from_socket(float *vector, const SocketRef *socket)
+{
+ switch (socket->bsocket()->type) {
+ case SOCK_FLOAT:
+ vector[0] = socket->default_value<bNodeSocketValueFloat>()->value;
+ return;
+ case SOCK_VECTOR:
+ copy_v3_v3(vector, socket->default_value<bNodeSocketValueVector>()->value);
+ return;
+ case SOCK_RGBA:
+ copy_v4_v4(vector, socket->default_value<bNodeSocketValueRGBA>()->value);
+ return;
+ default:
+ BLI_assert_unreachable();
+ }
+}
+
+static void populate_gpu_node_stack(DSocket socket, GPUNodeStack &stack)
+{
+ /* Make sure this stack is not marked as the end of the stack array. */
+ stack.end = false;
+ /* This will be initialized later by the GPU material compiler or the compile method. */
+ stack.link = nullptr;
+
+ stack.sockettype = socket->bsocket()->type;
+ stack.type = gpu_type_from_socket_type((eNodeSocketDatatype)socket->bsocket()->type);
+
+ if (socket->is_input()) {
+ const DInputSocket input(socket);
+
+ DSocket origin = get_input_origin_socket(input);
+
+ /* The input is linked if the origin socket is an output socket. Had it been an input socket,
+ * then it is an unlinked input of a group input node. */
+ stack.hasinput = origin->is_output();
+
+ /* Get the socket value from the origin if it is an input, because then it would either be an
+ * unlinked input or an unlinked input of a group input node that the socket is linked to,
+ * otherwise, get the value from the socket itself. */
+ if (origin->is_input()) {
+ gpu_stack_vector_from_socket(stack.vec, origin.socket_ref());
+ }
+ else {
+ gpu_stack_vector_from_socket(stack.vec, socket.socket_ref());
+ }
+ }
+ else {
+ stack.hasoutput = socket->is_logically_linked();
+ }
+}
+
+void ShaderNode::populate_inputs()
+{
+ /* Reserve a stack for each input in addition to an extra stack at the end to mark the end of the
+ * array, as this is what the GPU module functions expect. */
+ inputs_.resize(node_->inputs().size() + 1);
+ inputs_.last().end = true;
+
+ for (int i = 0; i < node_->inputs().size(); i++) {
+ populate_gpu_node_stack(node_.input(i), inputs_[i]);
+ }
+}
+
+void ShaderNode::populate_outputs()
+{
+ /* Reserve a stack for each output in addition to an extra stack at the end to mark the end of
+ * the array, as this is what the GPU module functions expect. */
+ outputs_.resize(node_->outputs().size() + 1);
+ outputs_.last().end = true;
+
+ for (int i = 0; i < node_->outputs().size(); i++) {
+ populate_gpu_node_stack(node_.output(i), outputs_[i]);
+ }
+}
+
+} // namespace blender::realtime_compositor
diff --git a/source/blender/compositor/realtime_compositor/intern/shader_operation.cc b/source/blender/compositor/realtime_compositor/intern/shader_operation.cc
new file mode 100644
index 00000000000..a097c81a4c5
--- /dev/null
+++ b/source/blender/compositor/realtime_compositor/intern/shader_operation.cc
@@ -0,0 +1,522 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include <memory>
+#include <string>
+
+#include "BLI_listbase.h"
+#include "BLI_map.hh"
+#include "BLI_string_ref.hh"
+#include "BLI_utildefines.h"
+
+#include "DNA_customdata_types.h"
+
+#include "GPU_material.h"
+#include "GPU_shader.h"
+#include "GPU_texture.h"
+#include "GPU_uniform_buffer.h"
+
+#include "gpu_shader_create_info.hh"
+
+#include "NOD_derived_node_tree.hh"
+#include "NOD_node_declaration.hh"
+
+#include "COM_context.hh"
+#include "COM_operation.hh"
+#include "COM_result.hh"
+#include "COM_scheduler.hh"
+#include "COM_shader_node.hh"
+#include "COM_shader_operation.hh"
+#include "COM_utilities.hh"
+
+namespace blender::realtime_compositor {
+
+using namespace nodes::derived_node_tree_types;
+
+ShaderOperation::ShaderOperation(Context &context, ShaderCompileUnit &compile_unit)
+ : Operation(context), compile_unit_(compile_unit)
+{
+ material_ = GPU_material_from_callbacks(&construct_material, &generate_code, this);
+ GPU_material_status_set(material_, GPU_MAT_QUEUED);
+ GPU_material_compile(material_);
+}
+
+ShaderOperation::~ShaderOperation()
+{
+ GPU_material_free_single(material_);
+}
+
+void ShaderOperation::execute()
+{
+ const Domain domain = compute_domain();
+ for (StringRef identifier : output_sockets_to_output_identifiers_map_.values()) {
+ Result &result = get_result(identifier);
+ result.allocate_texture(domain);
+ }
+
+ GPUShader *shader = GPU_material_get_shader(material_);
+ GPU_shader_bind(shader);
+
+ bind_material_resources(shader);
+ bind_inputs(shader);
+ bind_outputs(shader);
+
+ compute_dispatch_threads_at_least(shader, domain.size);
+
+ GPU_texture_unbind_all();
+ GPU_texture_image_unbind_all();
+ GPU_uniformbuf_unbind_all();
+ GPU_shader_unbind();
+}
+
+StringRef ShaderOperation::get_output_identifier_from_output_socket(DOutputSocket output_socket)
+{
+ return output_sockets_to_output_identifiers_map_.lookup(output_socket);
+}
+
+Map<std::string, DOutputSocket> &ShaderOperation::get_inputs_to_linked_outputs_map()
+{
+ return inputs_to_linked_outputs_map_;
+}
+
+void ShaderOperation::compute_results_reference_counts(const Schedule &schedule)
+{
+ for (const auto &item : output_sockets_to_output_identifiers_map_.items()) {
+ const int reference_count = number_of_inputs_linked_to_output_conditioned(
+ item.key, [&](DInputSocket input) { return schedule.contains(input.node()); });
+
+ get_result(item.value).set_initial_reference_count(reference_count);
+ }
+}
+
+void ShaderOperation::bind_material_resources(GPUShader *shader)
+{
+ /* Bind the uniform buffer of the material if it exists. It may not exist if the GPU material has
+ * no uniforms. */
+ GPUUniformBuf *ubo = GPU_material_uniform_buffer_get(material_);
+ if (ubo) {
+ GPU_uniformbuf_bind(ubo, GPU_shader_get_uniform_block_binding(shader, GPU_UBO_BLOCK_NAME));
+ }
+
+ /* Bind color band textures needed by curve and ramp nodes. */
+ ListBase textures = GPU_material_textures(material_);
+ LISTBASE_FOREACH (GPUMaterialTexture *, texture, &textures) {
+ if (texture->colorband) {
+ const int texture_image_unit = GPU_shader_get_texture_binding(shader, texture->sampler_name);
+ GPU_texture_bind(*texture->colorband, texture_image_unit);
+ }
+ }
+}
+
+void ShaderOperation::bind_inputs(GPUShader *shader)
+{
+ /* Attributes represents the inputs of the operation and their names match those of the inputs of
+ * the operation as well as the corresponding texture samples in the shader. */
+ ListBase attributes = GPU_material_attributes(material_);
+ LISTBASE_FOREACH (GPUMaterialAttribute *, attribute, &attributes) {
+ get_input(attribute->name).bind_as_texture(shader, attribute->name);
+ }
+}
+
+void ShaderOperation::bind_outputs(GPUShader *shader)
+{
+ for (StringRefNull output_identifier : output_sockets_to_output_identifiers_map_.values()) {
+ get_result(output_identifier).bind_as_image(shader, output_identifier.c_str());
+ }
+}
+
+void ShaderOperation::construct_material(void *thunk, GPUMaterial *material)
+{
+ ShaderOperation *operation = static_cast<ShaderOperation *>(thunk);
+ for (DNode node : operation->compile_unit_) {
+ ShaderNode *shader_node = node->typeinfo()->get_compositor_shader_node(node);
+ operation->shader_nodes_.add_new(node, std::unique_ptr<ShaderNode>(shader_node));
+
+ operation->link_node_inputs(node, material);
+
+ shader_node->compile(material);
+
+ operation->populate_results_for_node(node, material);
+ }
+}
+
+void ShaderOperation::link_node_inputs(DNode node, GPUMaterial *material)
+{
+ for (const InputSocketRef *input_ref : node->inputs()) {
+ const DInputSocket input{node.context(), input_ref};
+
+ /* Get the output linked to the input. If it is null, that means the input is unlinked.
+ * Unlinked inputs are linked by the node compile method, so skip this here. */
+ const DOutputSocket output = get_output_linked_to_input(input);
+ if (!output) {
+ continue;
+ }
+
+ /* If the origin node is part of the shader operation, then the link is internal to the GPU
+ * material graph and is linked appropriately. */
+ if (compile_unit_.contains(output.node())) {
+ link_node_input_internal(input, output);
+ continue;
+ }
+
+ /* Otherwise, the origin node is not part of the shader operation, then the link is external to
+ * the GPU material graph and an input to the shader operation must be declared and linked to
+ * the node input. */
+ link_node_input_external(input, output, material);
+ }
+}
+
+void ShaderOperation::link_node_input_internal(DInputSocket input_socket,
+ DOutputSocket output_socket)
+{
+ ShaderNode &output_node = *shader_nodes_.lookup(output_socket.node());
+ GPUNodeStack &output_stack = output_node.get_output(output_socket->identifier());
+
+ ShaderNode &input_node = *shader_nodes_.lookup(input_socket.node());
+ GPUNodeStack &input_stack = input_node.get_input(input_socket->identifier());
+
+ input_stack.link = output_stack.link;
+}
+
+void ShaderOperation::link_node_input_external(DInputSocket input_socket,
+ DOutputSocket output_socket,
+ GPUMaterial *material)
+{
+
+ ShaderNode &node = *shader_nodes_.lookup(input_socket.node());
+ GPUNodeStack &stack = node.get_input(input_socket->identifier());
+
+ /* An input was already declared for that same output socket, so no need to declare it again. */
+ if (!output_to_material_attribute_map_.contains(output_socket)) {
+ declare_operation_input(input_socket, output_socket, material);
+ }
+
+ /* Link the attribute representing the shader operation input corresponding to the given output
+ * socket. */
+ stack.link = output_to_material_attribute_map_.lookup(output_socket);
+}
+
+static const char *get_set_function_name(ResultType type)
+{
+ switch (type) {
+ case ResultType::Float:
+ return "set_value";
+ case ResultType::Vector:
+ return "set_rgb";
+ case ResultType::Color:
+ return "set_rgba";
+ }
+
+ BLI_assert_unreachable();
+ return nullptr;
+}
+
+void ShaderOperation::declare_operation_input(DInputSocket input_socket,
+ DOutputSocket output_socket,
+ GPUMaterial *material)
+{
+ const int input_index = output_to_material_attribute_map_.size();
+ std::string input_identifier = "input" + std::to_string(input_index);
+
+ /* Declare the input descriptor for this input and prefer to declare its type to be the same as
+ * the type of the output socket because doing type conversion in the shader is much cheaper. */
+ InputDescriptor input_descriptor = input_descriptor_from_input_socket(input_socket.socket_ref());
+ input_descriptor.type = get_node_socket_result_type(output_socket.socket_ref());
+ declare_input_descriptor(input_identifier, input_descriptor);
+
+ /* Add a new GPU attribute representing an input to the GPU material. Instead of using the
+ * attribute directly, we link it to an appropriate set function and use its output link instead.
+ * This is needed because the `gputype` member of the attribute is only initialized if it is
+ * linked to a GPU node. */
+ GPUNodeLink *attribute_link;
+ GPU_link(material,
+ get_set_function_name(input_descriptor.type),
+ GPU_attribute(material, CD_AUTO_FROM_NAME, input_identifier.c_str()),
+ &attribute_link);
+
+ /* Map the output socket to the attribute that was created for it. */
+ output_to_material_attribute_map_.add(output_socket, attribute_link);
+
+ /* Map the identifier of the operation input to the output socket it is linked to. */
+ inputs_to_linked_outputs_map_.add_new(input_identifier, output_socket);
+}
+
+void ShaderOperation::populate_results_for_node(DNode node, GPUMaterial *material)
+{
+ for (const OutputSocketRef *output_ref : node->outputs()) {
+ const DOutputSocket output{node.context(), output_ref};
+
+ /* If any of the nodes linked to the output are not part of the shader operation, then an
+ * output result needs to be populated for it. */
+ const bool need_to_populate_result = is_output_linked_to_node_conditioned(
+ output, [&](DNode node) { return !compile_unit_.contains(node); });
+
+ if (need_to_populate_result) {
+ populate_operation_result(output, material);
+ }
+ }
+}
+
+static const char *get_store_function_name(ResultType type)
+{
+ switch (type) {
+ case ResultType::Float:
+ return "node_compositor_store_output_float";
+ case ResultType::Vector:
+ return "node_compositor_store_output_vector";
+ case ResultType::Color:
+ return "node_compositor_store_output_color";
+ }
+
+ BLI_assert_unreachable();
+ return nullptr;
+}
+
+void ShaderOperation::populate_operation_result(DOutputSocket output_socket, GPUMaterial *material)
+{
+ const unsigned int output_id = output_sockets_to_output_identifiers_map_.size();
+ std::string output_identifier = "output" + std::to_string(output_id);
+
+ const ResultType result_type = get_node_socket_result_type(output_socket.socket_ref());
+ const Result result = Result(result_type, texture_pool());
+ populate_result(output_identifier, result);
+
+ /* Map the output socket to the identifier of the newly populated result. */
+ output_sockets_to_output_identifiers_map_.add_new(output_socket, output_identifier);
+
+ ShaderNode &node = *shader_nodes_.lookup(output_socket.node());
+ GPUNodeLink *output_link = node.get_output(output_socket->identifier()).link;
+
+ /* Link the output node stack to an output storer storing in the appropriate result. The result
+ * is identified by its index in the operation and the index is encoded as a float to be passed
+ * to the GPU function. Additionally, create an output link from the storer node to declare as an
+ * output to the GPU material. This storer output link is a dummy link in the sense that its
+ * value is ignored since it is already written in the output, but it is used to track nodes that
+ * contribute to the output of the compositor node tree. */
+ GPUNodeLink *storer_output_link;
+ GPUNodeLink *id_link = GPU_constant((float *)&output_id);
+ const char *store_function_name = get_store_function_name(result_type);
+ GPU_link(material, store_function_name, id_link, output_link, &storer_output_link);
+
+ /* Declare the output link of the storer node as an output of the GPU material to help the GPU
+ * code generator to track the nodes that contribute to the output of the shader. */
+ GPU_material_add_output_link_composite(material, storer_output_link);
+}
+
+using namespace gpu::shader;
+
+void ShaderOperation::generate_code(void *thunk,
+ GPUMaterial *material,
+ GPUCodegenOutput *code_generator_output)
+{
+ ShaderOperation *operation = static_cast<ShaderOperation *>(thunk);
+ ShaderCreateInfo &shader_create_info = *reinterpret_cast<ShaderCreateInfo *>(
+ code_generator_output->create_info);
+
+ shader_create_info.local_group_size(16, 16);
+
+ /* The resources are added without explicit locations, so make sure it is done by the
+ * shader creator. */
+ shader_create_info.auto_resource_location(true);
+
+ /* Add implementation for implicit conversion operations inserted by the code generator. This
+ * file should include the functions [float|vec3|vec4]_from_[float|vec3|vec4]. */
+ shader_create_info.typedef_source("gpu_shader_compositor_type_conversion.glsl");
+
+ /* The source shader is a compute shader with a main function that calls the dynamically
+ * generated evaluate function. The evaluate function includes the serialized GPU material graph
+ * preceded by code that initialized the inputs of the operation. Additionally, the storer
+ * functions that writes the outputs are defined outside the evaluate function. */
+ shader_create_info.compute_source("gpu_shader_compositor_main.glsl");
+
+ /* The main function is emitted in the shader before the evaluate function, so the evaluate
+ * function needs to be forward declared here. */
+ shader_create_info.typedef_source_generated += "void evaluate();\n";
+
+ operation->generate_code_for_outputs(shader_create_info);
+
+ shader_create_info.compute_source_generated += "void evaluate()\n{\n";
+
+ operation->generate_code_for_inputs(material, shader_create_info);
+
+ shader_create_info.compute_source_generated += code_generator_output->composite;
+
+ shader_create_info.compute_source_generated += "}\n";
+}
+
+static eGPUTextureFormat texture_format_from_result_type(ResultType type)
+{
+ switch (type) {
+ case ResultType::Float:
+ return GPU_R16F;
+ case ResultType::Vector:
+ return GPU_RGBA16F;
+ case ResultType::Color:
+ return GPU_RGBA16F;
+ }
+
+ BLI_assert_unreachable();
+ return GPU_RGBA16F;
+}
+
+/* Texture storers in the shader always take a vec4 as an argument, so encode each type in a vec4
+ * appropriately. */
+static const char *glsl_store_expression_from_result_type(ResultType type)
+{
+ switch (type) {
+ case ResultType::Float:
+ return "vec4(value)";
+ case ResultType::Vector:
+ return "vec4(vector, 0.0)";
+ case ResultType::Color:
+ return "color";
+ }
+
+ BLI_assert_unreachable();
+ return nullptr;
+}
+
+void ShaderOperation::generate_code_for_outputs(ShaderCreateInfo &shader_create_info)
+{
+ const std::string store_float_function_header = "void store_float(const uint id, float value)";
+ const std::string store_vector_function_header = "void store_vector(const uint id, vec3 vector)";
+ const std::string store_color_function_header = "void store_color(const uint id, vec4 color)";
+
+ /* The store functions are used by the node_compositor_store_output_[float|vector|color]
+ * functions but are only defined later as part of the compute source, so they need to be forward
+ * declared. */
+ shader_create_info.typedef_source_generated += store_float_function_header + ";\n";
+ shader_create_info.typedef_source_generated += store_vector_function_header + ";\n";
+ shader_create_info.typedef_source_generated += store_color_function_header + ";\n";
+
+ /* Each of the store functions is essentially a single switch case on the given ID, so start by
+ * opening the function with a curly bracket followed by opening a switch statement in each of
+ * the functions. */
+ std::stringstream store_float_function;
+ std::stringstream store_vector_function;
+ std::stringstream store_color_function;
+ const std::string store_function_start = "\n{\n switch (id) {\n";
+ store_float_function << store_float_function_header << store_function_start;
+ store_vector_function << store_vector_function_header << store_function_start;
+ store_color_function << store_color_function_header << store_function_start;
+
+ for (StringRefNull output_identifier : output_sockets_to_output_identifiers_map_.values()) {
+ const Result &result = get_result(output_identifier);
+
+ /* Add a write-only image for this output where its values will be written. */
+ shader_create_info.image(0,
+ texture_format_from_result_type(result.type()),
+ Qualifier::WRITE,
+ ImageType::FLOAT_2D,
+ output_identifier,
+ Frequency::BATCH);
+
+ /* Add a case for the index of this output followed by a break statement. */
+ std::stringstream case_code;
+ const std::string store_expression = glsl_store_expression_from_result_type(result.type());
+ const std::string texel = ", ivec2(gl_GlobalInvocationID.xy), ";
+ case_code << " case " << StringRef(output_identifier).drop_known_prefix("output") << ":\n"
+ << " imageStore(" << output_identifier << texel << store_expression << ");\n"
+ << " break;\n";
+
+ /* Only add the case to the function with the matching type. */
+ switch (result.type()) {
+ case ResultType::Float:
+ store_float_function << case_code.str();
+ break;
+ case ResultType::Vector:
+ store_vector_function << case_code.str();
+ break;
+ case ResultType::Color:
+ store_color_function << case_code.str();
+ break;
+ }
+ }
+
+ /* Close the previously opened switch statement as well as the function itself. */
+ const std::string store_function_end = " }\n}\n\n";
+ store_float_function << store_function_end;
+ store_vector_function << store_function_end;
+ store_color_function << store_function_end;
+
+ shader_create_info.compute_source_generated += store_float_function.str() +
+ store_vector_function.str() +
+ store_color_function.str();
+}
+
+static const char *glsl_type_from_result_type(ResultType type)
+{
+ switch (type) {
+ case ResultType::Float:
+ return "float";
+ case ResultType::Vector:
+ return "vec3";
+ case ResultType::Color:
+ return "vec4";
+ }
+
+ BLI_assert_unreachable();
+ return nullptr;
+}
+
+/* Texture loaders in the shader always return a vec4, so a swizzle is needed to retrieve the
+ * actual value for each type. */
+static const char *glsl_swizzle_from_result_type(ResultType type)
+{
+ switch (type) {
+ case ResultType::Float:
+ return "x";
+ case ResultType::Vector:
+ return "xyz";
+ case ResultType::Color:
+ return "rgba";
+ }
+
+ BLI_assert_unreachable();
+ return nullptr;
+}
+
+void ShaderOperation::generate_code_for_inputs(GPUMaterial *material,
+ ShaderCreateInfo &shader_create_info)
+{
+ /* The attributes of the GPU material represents the inputs of the operation. */
+ ListBase attributes = GPU_material_attributes(material);
+
+ /* Add a texture sampler for each of the inputs with the same name as the attribute. */
+ LISTBASE_FOREACH (GPUMaterialAttribute *, attribute, &attributes) {
+ shader_create_info.sampler(0, ImageType::FLOAT_2D, attribute->name, Frequency::BATCH);
+ }
+
+ /* Declare a struct called var_attrs that includes an appropriately typed member for each of the
+ * inputs. The names of the members should be the letter v followed by the ID of the attribute
+ * corresponding to the input. Such names are expected by the code generator. */
+ std::stringstream declare_attributes;
+ declare_attributes << "struct {\n";
+ LISTBASE_FOREACH (GPUMaterialAttribute *, attribute, &attributes) {
+ const InputDescriptor &input_descriptor = get_input_descriptor(attribute->name);
+ const std::string type = glsl_type_from_result_type(input_descriptor.type);
+ declare_attributes << " " << type << " v" << attribute->id << ";\n";
+ }
+ declare_attributes << "} var_attrs;\n\n";
+
+ shader_create_info.compute_source_generated += declare_attributes.str();
+
+ /* The texture loader utilities are needed to sample the input textures and initialize the
+ * attributes. */
+ shader_create_info.typedef_source("gpu_shader_compositor_texture_utilities.glsl");
+
+ /* Initialize each member of the previously declared struct by loading its corresponding texture
+ * with an appropriate swizzle for its type. */
+ std::stringstream initialize_attributes;
+ LISTBASE_FOREACH (GPUMaterialAttribute *, attribute, &attributes) {
+ const InputDescriptor &input_descriptor = get_input_descriptor(attribute->name);
+ const std::string swizzle = glsl_swizzle_from_result_type(input_descriptor.type);
+ initialize_attributes << "var_attrs.v" << attribute->id << " = "
+ << "texture_load(" << attribute->name
+ << ", ivec2(gl_GlobalInvocationID.xy))." << swizzle << ";\n";
+ }
+ initialize_attributes << "\n";
+
+ shader_create_info.compute_source_generated += initialize_attributes.str();
+}
+
+} // namespace blender::realtime_compositor
diff --git a/source/blender/compositor/realtime_compositor/intern/simple_operation.cc b/source/blender/compositor/realtime_compositor/intern/simple_operation.cc
new file mode 100644
index 00000000000..d55a20e5c54
--- /dev/null
+++ b/source/blender/compositor/realtime_compositor/intern/simple_operation.cc
@@ -0,0 +1,55 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include "COM_input_descriptor.hh"
+#include "COM_operation.hh"
+#include "COM_result.hh"
+#include "COM_simple_operation.hh"
+
+namespace blender::realtime_compositor {
+
+const StringRef SimpleOperation::input_identifier_ = StringRef("Input");
+const StringRef SimpleOperation::output_identifier_ = StringRef("Output");
+
+Result &SimpleOperation::get_result()
+{
+ return Operation::get_result(output_identifier_);
+}
+
+void SimpleOperation::map_input_to_result(Result *result)
+{
+ Operation::map_input_to_result(input_identifier_, result);
+}
+
+void SimpleOperation::add_and_evaluate_input_processors()
+{
+}
+
+Result &SimpleOperation::get_input()
+{
+ return Operation::get_input(input_identifier_);
+}
+
+void SimpleOperation::switch_result_mapped_to_input(Result *result)
+{
+ Operation::switch_result_mapped_to_input(input_identifier_, result);
+}
+
+void SimpleOperation::populate_result(Result result)
+{
+ Operation::populate_result(output_identifier_, result);
+
+ /* The result of a simple operation is guaranteed to have a single user. */
+ get_result().set_initial_reference_count(1);
+}
+
+void SimpleOperation::declare_input_descriptor(InputDescriptor descriptor)
+{
+ Operation::declare_input_descriptor(input_identifier_, descriptor);
+}
+
+InputDescriptor &SimpleOperation::get_input_descriptor()
+{
+ return Operation::get_input_descriptor(input_identifier_);
+}
+
+} // namespace blender::realtime_compositor
diff --git a/source/blender/compositor/realtime_compositor/intern/static_shader_manager.cc b/source/blender/compositor/realtime_compositor/intern/static_shader_manager.cc
new file mode 100644
index 00000000000..c9c8a056f87
--- /dev/null
+++ b/source/blender/compositor/realtime_compositor/intern/static_shader_manager.cc
@@ -0,0 +1,24 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include "GPU_shader.h"
+
+#include "COM_static_shader_manager.hh"
+
+namespace blender::realtime_compositor {
+
+StaticShaderManager::~StaticShaderManager()
+{
+ for (GPUShader *shader : shaders_.values()) {
+ GPU_shader_free(shader);
+ }
+}
+
+GPUShader *StaticShaderManager::get(const char *info_name)
+{
+ /* If a shader with the same info name already exists in the manager, return it, otherwise,
+ * create a new shader from the info name and return it. */
+ return shaders_.lookup_or_add_cb(
+ info_name, [info_name]() { return GPU_shader_create_from_info_name(info_name); });
+}
+
+} // namespace blender::realtime_compositor
diff --git a/source/blender/compositor/realtime_compositor/intern/texture_pool.cc b/source/blender/compositor/realtime_compositor/intern/texture_pool.cc
new file mode 100644
index 00000000000..1568970a030
--- /dev/null
+++ b/source/blender/compositor/realtime_compositor/intern/texture_pool.cc
@@ -0,0 +1,84 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include <cstdint>
+
+#include "BLI_hash.hh"
+#include "BLI_map.hh"
+#include "BLI_math_vec_types.hh"
+#include "BLI_vector.hh"
+
+#include "GPU_texture.h"
+
+#include "COM_texture_pool.hh"
+
+namespace blender::realtime_compositor {
+
+/* --------------------------------------------------------------------
+ * Texture Pool Key.
+ */
+
+TexturePoolKey::TexturePoolKey(int2 size, eGPUTextureFormat format) : size(size), format(format)
+{
+}
+
+TexturePoolKey::TexturePoolKey(const GPUTexture *texture)
+{
+ size = int2(GPU_texture_width(texture), GPU_texture_height(texture));
+ format = GPU_texture_format(texture);
+}
+
+uint64_t TexturePoolKey::hash() const
+{
+ return get_default_hash_3(size.x, size.y, format);
+}
+
+bool operator==(const TexturePoolKey &a, const TexturePoolKey &b)
+{
+ return a.size == b.size && a.format == b.format;
+}
+
+/* --------------------------------------------------------------------
+ * Texture Pool.
+ */
+
+GPUTexture *TexturePool::acquire(int2 size, eGPUTextureFormat format)
+{
+ /* Check if there is an available texture with the required specification, and if one exists,
+ * return it. */
+ const TexturePoolKey key = TexturePoolKey(size, format);
+ Vector<GPUTexture *> &available_textures = textures_.lookup_or_add_default(key);
+ if (!available_textures.is_empty()) {
+ return available_textures.pop_last();
+ }
+
+ /* Otherwise, allocate a new texture. */
+ return allocate_texture(size, format);
+}
+
+GPUTexture *TexturePool::acquire_color(int2 size)
+{
+ return acquire(size, GPU_RGBA16F);
+}
+
+GPUTexture *TexturePool::acquire_vector(int2 size)
+{
+ /* Vectors are stored in RGBA textures because RGB textures have limited support. */
+ return acquire(size, GPU_RGBA16F);
+}
+
+GPUTexture *TexturePool::acquire_float(int2 size)
+{
+ return acquire(size, GPU_R16F);
+}
+
+void TexturePool::release(GPUTexture *texture)
+{
+ textures_.lookup(TexturePoolKey(texture)).append(texture);
+}
+
+void TexturePool::reset()
+{
+ textures_.clear();
+}
+
+} // namespace blender::realtime_compositor
diff --git a/source/blender/compositor/realtime_compositor/intern/utilities.cc b/source/blender/compositor/realtime_compositor/intern/utilities.cc
new file mode 100644
index 00000000000..169ba70e9eb
--- /dev/null
+++ b/source/blender/compositor/realtime_compositor/intern/utilities.cc
@@ -0,0 +1,134 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include "BLI_assert.h"
+#include "BLI_function_ref.hh"
+#include "BLI_math_vec_types.hh"
+#include "BLI_math_vector.hh"
+#include "BLI_utildefines.h"
+
+#include "DNA_node_types.h"
+
+#include "NOD_derived_node_tree.hh"
+#include "NOD_node_declaration.hh"
+
+#include "GPU_compute.h"
+#include "GPU_shader.h"
+
+#include "COM_operation.hh"
+#include "COM_result.hh"
+#include "COM_utilities.hh"
+
+namespace blender::realtime_compositor {
+
+using namespace nodes::derived_node_tree_types;
+using TargetSocketPathInfo = DOutputSocket::TargetSocketPathInfo;
+
+DSocket get_input_origin_socket(DInputSocket input)
+{
+ /* The input is unlinked. Return the socket itself. */
+ if (input->logically_linked_sockets().is_empty()) {
+ return input;
+ }
+
+ /* Only a single origin socket is guaranteed to exist. */
+ DSocket socket;
+ input.foreach_origin_socket([&](const DSocket origin) { socket = origin; });
+ return socket;
+}
+
+DOutputSocket get_output_linked_to_input(DInputSocket input)
+{
+ /* Get the origin socket of this input, which will be an output socket if the input is linked
+ * to an output. */
+ const DSocket origin = get_input_origin_socket(input);
+
+ /* If the origin socket is an input, that means the input is unlinked, so return a null output
+ * socket. */
+ if (origin->is_input()) {
+ return DOutputSocket();
+ }
+
+ /* Now that we know the origin is an output, return a derived output from it. */
+ return DOutputSocket(origin);
+}
+
+ResultType get_node_socket_result_type(const SocketRef *socket)
+{
+ switch (socket->bsocket()->type) {
+ case SOCK_FLOAT:
+ return ResultType::Float;
+ case SOCK_VECTOR:
+ return ResultType::Vector;
+ case SOCK_RGBA:
+ return ResultType::Color;
+ default:
+ BLI_assert_unreachable();
+ return ResultType::Float;
+ }
+}
+
+bool is_output_linked_to_node_conditioned(DOutputSocket output, FunctionRef<bool(DNode)> condition)
+{
+ bool condition_satisfied = false;
+ output.foreach_target_socket(
+ [&](DInputSocket target, const TargetSocketPathInfo &UNUSED(path_info)) {
+ if (condition(target.node())) {
+ condition_satisfied = true;
+ return;
+ }
+ });
+ return condition_satisfied;
+}
+
+int number_of_inputs_linked_to_output_conditioned(DOutputSocket output,
+ FunctionRef<bool(DInputSocket)> condition)
+{
+ int count = 0;
+ output.foreach_target_socket(
+ [&](DInputSocket target, const TargetSocketPathInfo &UNUSED(path_info)) {
+ if (condition(target)) {
+ count++;
+ }
+ });
+ return count;
+}
+
+bool is_shader_node(DNode node)
+{
+ return node->typeinfo()->get_compositor_shader_node;
+}
+
+bool is_node_supported(DNode node)
+{
+ return node->typeinfo()->get_compositor_operation ||
+ node->typeinfo()->get_compositor_shader_node;
+}
+
+InputDescriptor input_descriptor_from_input_socket(const InputSocketRef *socket)
+{
+ using namespace nodes;
+ InputDescriptor input_descriptor;
+ input_descriptor.type = get_node_socket_result_type(socket);
+ const NodeDeclaration *node_declaration = socket->node().declaration();
+ /* Not every node have a declaration, in which case, we assume the default values for the rest of
+ * the properties. */
+ if (!node_declaration) {
+ return input_descriptor;
+ }
+ const SocketDeclarationPtr &socket_declaration = node_declaration->inputs()[socket->index()];
+ input_descriptor.domain_priority = socket_declaration->compositor_domain_priority();
+ input_descriptor.expects_single_value = socket_declaration->compositor_expects_single_value();
+ return input_descriptor;
+}
+
+void compute_dispatch_threads_at_least(GPUShader *shader, int2 threads_range, int2 local_size)
+{
+ /* If the threads range is divisible by the local size, dispatch the number of needed groups,
+ * which is their division. If it is not divisible, then dispatch an extra group to cover the
+ * remaining invocations, which means the actual threads range of the dispatch will be a bit
+ * larger than the given one. */
+ const int2 groups_to_dispatch = math::divide_ceil(threads_range, local_size);
+ GPU_compute_dispatch(shader, groups_to_dispatch.x, groups_to_dispatch.y, 1);
+}
+
+} // namespace blender::realtime_compositor
diff --git a/source/blender/depsgraph/intern/builder/deg_builder.cc b/source/blender/depsgraph/intern/builder/deg_builder.cc
index 5353f71685c..097c377ece4 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder.cc
+++ b/source/blender/depsgraph/intern/builder/deg_builder.cc
@@ -13,6 +13,7 @@
#include "DNA_anim_types.h"
#include "DNA_armature_types.h"
#include "DNA_layer_types.h"
+#include "DNA_modifier_types.h"
#include "DNA_object_types.h"
#include "BLI_stack.h"
@@ -95,6 +96,24 @@ bool DepsgraphBuilder::is_object_visibility_animated(const Object *object)
return cache_->isPropertyAnimated(&object->id, property_id);
}
+bool DepsgraphBuilder::is_modifier_visibility_animated(const Object *object,
+ const ModifierData *modifier)
+{
+ AnimatedPropertyID property_id;
+ if (graph_->mode == DAG_EVAL_VIEWPORT) {
+ property_id = AnimatedPropertyID(
+ &object->id, &RNA_Modifier, (void *)modifier, "show_viewport");
+ }
+ else if (graph_->mode == DAG_EVAL_RENDER) {
+ property_id = AnimatedPropertyID(&object->id, &RNA_Modifier, (void *)modifier, "show_render");
+ }
+ else {
+ BLI_assert_msg(0, "Unknown evaluation mode.");
+ return false;
+ }
+ return cache_->isPropertyAnimated(&object->id, property_id);
+}
+
bool DepsgraphBuilder::check_pchan_has_bbone(const Object *object, const bPoseChannel *pchan)
{
BLI_assert(object->type == OB_ARMATURE);
diff --git a/source/blender/depsgraph/intern/builder/deg_builder.h b/source/blender/depsgraph/intern/builder/deg_builder.h
index c44e5fd5f4d..5d043f1fd3a 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder.h
+++ b/source/blender/depsgraph/intern/builder/deg_builder.h
@@ -10,6 +10,7 @@
struct Base;
struct ID;
struct Main;
+struct ModifierData;
struct Object;
struct bPoseChannel;
@@ -25,6 +26,7 @@ class DepsgraphBuilder {
virtual bool need_pull_base_into_graph(const Base *base);
virtual bool is_object_visibility_animated(const Object *object);
+ virtual bool is_modifier_visibility_animated(const Object *object, const ModifierData *modifier);
virtual bool check_pchan_has_bbone(const Object *object, const bPoseChannel *pchan);
virtual bool check_pchan_has_bbone_segments(const Object *object, const bPoseChannel *pchan);
diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc
index be087c0b2d4..dd62a6cdea2 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc
+++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc
@@ -769,11 +769,7 @@ void DepsgraphNodeBuilder::build_object(int base_index,
build_object(-1, object->parent, DEG_ID_LINKED_INDIRECTLY, is_visible);
}
/* Modifiers. */
- if (object->modifiers.first != nullptr) {
- BuilderWalkUserData data;
- data.builder = this;
- BKE_modifiers_foreach_ID_link(object, modifier_walk, &data);
- }
+ build_object_modifiers(object);
/* Grease Pencil Modifiers. */
if (object->greasepencil_modifiers.first != nullptr) {
BuilderWalkUserData data;
@@ -877,6 +873,44 @@ void DepsgraphNodeBuilder::build_object_instance_collection(Object *object, bool
is_parent_collection_visible_ = is_current_parent_collection_visible;
}
+void DepsgraphNodeBuilder::build_object_modifiers(Object *object)
+{
+ if (BLI_listbase_is_empty(&object->modifiers)) {
+ return;
+ }
+
+ const ModifierMode modifier_mode = (graph_->mode == DAG_EVAL_VIEWPORT) ? eModifierMode_Realtime :
+ eModifierMode_Render;
+
+ IDNode *id_node = find_id_node(&object->id);
+
+ add_operation_node(&object->id,
+ NodeType::GEOMETRY,
+ OperationCode::VISIBILITY,
+ [id_node](::Depsgraph *depsgraph) {
+ deg_evaluate_object_modifiers_mode_node_visibility(depsgraph, id_node);
+ });
+
+ LISTBASE_FOREACH (ModifierData *, modifier, &object->modifiers) {
+ OperationNode *modifier_node = add_operation_node(
+ &object->id, NodeType::GEOMETRY, OperationCode::MODIFIER, nullptr, modifier->name);
+
+ /* Mute modifier mode if the modifier is not enabled for the dependency graph mode.
+ * This handles static (non-animated) mode of the modifier. */
+ if ((modifier->mode & modifier_mode) == 0) {
+ modifier_node->flag |= DEPSOP_FLAG_MUTE;
+ }
+
+ if (is_modifier_visibility_animated(object, modifier)) {
+ graph_->has_animated_visibility = true;
+ }
+ }
+
+ BuilderWalkUserData data;
+ data.builder = this;
+ BKE_modifiers_foreach_ID_link(object, modifier_walk, &data);
+}
+
void DepsgraphNodeBuilder::build_object_data(Object *object)
{
if (object->data == nullptr) {
diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes.h b/source/blender/depsgraph/intern/builder/deg_builder_nodes.h
index 18e28311132..d5ac601ebff 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder_nodes.h
+++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes.h
@@ -174,6 +174,7 @@ class DepsgraphNodeBuilder : public DepsgraphBuilder {
virtual void build_object_flags(int base_index,
Object *object,
eDepsNode_LinkedState_Type linked_state);
+ virtual void build_object_modifiers(Object *object);
virtual void build_object_data(Object *object);
virtual void build_object_data_camera(Object *object);
virtual void build_object_data_geometry(Object *object);
diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc
index fc5e5189e82..d6ee1286fc4 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc
+++ b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc
@@ -298,7 +298,8 @@ void DepsgraphRelationBuilder::add_depends_on_transform_relation(const DepsNodeH
{
IDNode *id_node = handle->node->owner->owner;
ID *id = id_node->id_orig;
- ComponentKey geometry_key(id, NodeType::GEOMETRY);
+ const OperationKey geometry_key(
+ id, NodeType::GEOMETRY, OperationCode::MODIFIER, handle->node->name.c_str());
/* Wire up the actual relation. */
add_depends_on_transform_relation(id, geometry_key, description);
}
@@ -718,11 +719,7 @@ void DepsgraphRelationBuilder::build_object(Object *object)
}
/* Modifiers. */
- if (object->modifiers.first != nullptr) {
- BuilderWalkUserData data;
- data.builder = this;
- BKE_modifiers_foreach_ID_link(object, modifier_walk, &data);
- }
+ build_object_modifiers(object);
/* Grease Pencil Modifiers. */
if (object->greasepencil_modifiers.first != nullptr) {
@@ -870,6 +867,63 @@ void DepsgraphRelationBuilder::build_object_layer_component_relations(Object *ob
add_relation(object_from_layer_exit_key, synchronize_key, "Synchronize to Original");
}
+void DepsgraphRelationBuilder::build_object_modifiers(Object *object)
+{
+ if (BLI_listbase_is_empty(&object->modifiers)) {
+ return;
+ }
+
+ const OperationKey eval_init_key(
+ &object->id, NodeType::GEOMETRY, OperationCode::GEOMETRY_EVAL_INIT);
+ const OperationKey eval_key(&object->id, NodeType::GEOMETRY, OperationCode::GEOMETRY_EVAL);
+
+ const ComponentKey object_visibility_key(&object->id, NodeType::VISIBILITY);
+ const OperationKey modifier_visibility_key(
+ &object->id, NodeType::GEOMETRY, OperationCode::VISIBILITY);
+ add_relation(modifier_visibility_key,
+ object_visibility_key,
+ "modifier -> object visibility",
+ RELATION_NO_VISIBILITY_CHANGE);
+
+ add_relation(modifier_visibility_key, eval_key, "modifier visibility -> geometry eval");
+
+ ModifierUpdateDepsgraphContext ctx = {};
+ ctx.scene = scene_;
+ ctx.object = object;
+
+ OperationKey previous_key = eval_init_key;
+ LISTBASE_FOREACH (ModifierData *, modifier, &object->modifiers) {
+ const OperationKey modifier_key(
+ &object->id, NodeType::GEOMETRY, OperationCode::MODIFIER, modifier->name);
+
+ /* Relation for the modifier stack chain. */
+ add_relation(previous_key, modifier_key, "Modifier");
+
+ const ModifierTypeInfo *mti = BKE_modifier_get_info((ModifierType)modifier->type);
+ if (mti->updateDepsgraph) {
+ const BuilderStack::ScopedEntry stack_entry = stack_.trace(*modifier);
+
+ DepsNodeHandle handle = create_node_handle(modifier_key);
+ ctx.node = reinterpret_cast<::DepsNodeHandle *>(&handle);
+ mti->updateDepsgraph(modifier, &ctx);
+ }
+
+ /* Time dependency. */
+ if (BKE_modifier_depends_ontime(scene_, modifier)) {
+ const TimeSourceKey time_src_key;
+ add_relation(time_src_key, modifier_key, "Time Source -> Modifier");
+ }
+
+ previous_key = modifier_key;
+ }
+ add_relation(previous_key, eval_key, "modifier stack order");
+
+ /* Build IDs referenced by the modifiers. */
+ BuilderWalkUserData data;
+ data.builder = this;
+ BKE_modifiers_foreach_ID_link(object, modifier_walk, &data);
+}
+
void DepsgraphRelationBuilder::build_object_data(Object *object)
{
if (object->data == nullptr) {
@@ -2193,26 +2247,6 @@ void DepsgraphRelationBuilder::build_object_data_geometry(Object *object)
* evaluated prior to Scene's CoW is ready. */
OperationKey scene_key(&scene_->id, NodeType::PARAMETERS, OperationCode::SCENE_EVAL);
add_relation(scene_key, obdata_ubereval_key, "CoW Relation", RELATION_FLAG_NO_FLUSH);
- /* Modifiers */
- if (object->modifiers.first != nullptr) {
- ModifierUpdateDepsgraphContext ctx = {};
- ctx.scene = scene_;
- ctx.object = object;
- LISTBASE_FOREACH (ModifierData *, md, &object->modifiers) {
- const ModifierTypeInfo *mti = BKE_modifier_get_info((ModifierType)md->type);
- if (mti->updateDepsgraph) {
- const BuilderStack::ScopedEntry stack_entry = stack_.trace(*md);
-
- DepsNodeHandle handle = create_node_handle(obdata_ubereval_key);
- ctx.node = reinterpret_cast<::DepsNodeHandle *>(&handle);
- mti->updateDepsgraph(md, &ctx);
- }
- if (BKE_modifier_depends_ontime(scene_, md)) {
- TimeSourceKey time_src_key;
- add_relation(time_src_key, obdata_ubereval_key, "Time Source -> Modifier");
- }
- }
- }
/* Grease Pencil Modifiers. */
if (object->greasepencil_modifiers.first != nullptr) {
ModifierUpdateDepsgraphContext ctx = {};
@@ -2256,8 +2290,13 @@ void DepsgraphRelationBuilder::build_object_data_geometry(Object *object)
if (ELEM(object->type, OB_MESH, OB_CURVES_LEGACY, OB_LATTICE)) {
// add geometry collider relations
}
- /* Make sure uber update is the last in the dependencies. */
- add_relation(geom_init_key, obdata_ubereval_key, "Object Geometry UberEval");
+ /* Make sure uber update is the last in the dependencies.
+ * Only do it here unless there are modifiers. This avoids transitive relations. */
+ if (BLI_listbase_is_empty(&object->modifiers)) {
+ OperationKey obdata_ubereval_key(
+ &object->id, NodeType::GEOMETRY, OperationCode::GEOMETRY_EVAL);
+ add_relation(geom_init_key, obdata_ubereval_key, "Object Geometry UberEval");
+ }
if (object->type == OB_MBALL) {
Object *mom = BKE_mball_basis_find(scene_, object);
ComponentKey mom_geom_key(&mom->id, NodeType::GEOMETRY);
diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations.h b/source/blender/depsgraph/intern/builder/deg_builder_relations.h
index db237303027..7d3a0fd9217 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder_relations.h
+++ b/source/blender/depsgraph/intern/builder/deg_builder_relations.h
@@ -265,6 +265,7 @@ class DepsgraphRelationBuilder : public DepsgraphBuilder {
virtual void build_object(Object *object);
virtual void build_object_from_view_layer_base(Object *object);
virtual void build_object_layer_component_relations(Object *object);
+ virtual void build_object_modifiers(Object *object);
virtual void build_object_data(Object *object);
virtual void build_object_data_camera(Object *object);
virtual void build_object_data_geometry(Object *object);
diff --git a/source/blender/depsgraph/intern/builder/deg_builder_rna.cc b/source/blender/depsgraph/intern/builder/deg_builder_rna.cc
index 5202ada5408..d94746fb7fa 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder_rna.cc
+++ b/source/blender/depsgraph/intern/builder/deg_builder_rna.cc
@@ -225,6 +225,10 @@ RNANodeIdentifier RNANodeQuery::construct_node_identifier(const PointerRNA *ptr,
}
return node_identifier;
}
+
+ const char *prop_identifier = prop != nullptr ? RNA_property_identifier((PropertyRNA *)prop) :
+ "";
+
if (RNA_struct_is_a(ptr->type, &RNA_Constraint)) {
const Object *object = reinterpret_cast<const Object *>(ptr->owner_id);
const bConstraint *constraint = static_cast<const bConstraint *>(ptr->data);
@@ -264,6 +268,13 @@ RNANodeIdentifier RNANodeQuery::construct_node_identifier(const PointerRNA *ptr,
return node_identifier;
}
}
+ else if (RNA_struct_is_a(ptr->type, &RNA_Modifier) &&
+ (contains(prop_identifier, "show_viewport") ||
+ contains(prop_identifier, "show_render"))) {
+ node_identifier.type = NodeType::GEOMETRY;
+ node_identifier.operation_code = OperationCode::VISIBILITY;
+ return node_identifier;
+ }
else if (RNA_struct_is_a(ptr->type, &RNA_Mesh) || RNA_struct_is_a(ptr->type, &RNA_Modifier) ||
RNA_struct_is_a(ptr->type, &RNA_GpencilModifier) ||
RNA_struct_is_a(ptr->type, &RNA_Spline) || RNA_struct_is_a(ptr->type, &RNA_TextBox) ||
@@ -290,7 +301,6 @@ RNANodeIdentifier RNANodeQuery::construct_node_identifier(const PointerRNA *ptr,
else if (ptr->type == &RNA_Object) {
/* Transforms props? */
if (prop != nullptr) {
- const char *prop_identifier = RNA_property_identifier((PropertyRNA *)prop);
/* TODO(sergey): How to optimize this? */
if (contains(prop_identifier, "location") || contains(prop_identifier, "matrix_basis") ||
contains(prop_identifier, "matrix_channel") ||
diff --git a/source/blender/depsgraph/intern/depsgraph_relation.h b/source/blender/depsgraph/intern/depsgraph_relation.h
index 1bacb9abfa6..3f316fa84e8 100644
--- a/source/blender/depsgraph/intern/depsgraph_relation.h
+++ b/source/blender/depsgraph/intern/depsgraph_relation.h
@@ -28,6 +28,8 @@ enum RelationFlag {
RELATION_FLAG_GODMODE = (1 << 4),
/* Relation will check existence before being added. */
RELATION_CHECK_BEFORE_ADD = (1 << 5),
+ /* The relation does not participate in visibility checks. */
+ RELATION_NO_VISIBILITY_CHANGE = (1 << 6),
};
/* B depends on A (A -> B) */
diff --git a/source/blender/depsgraph/intern/eval/deg_eval.cc b/source/blender/depsgraph/intern/eval/deg_eval.cc
index 9b2ce2bb707..cd0015ff717 100644
--- a/source/blender/depsgraph/intern/eval/deg_eval.cc
+++ b/source/blender/depsgraph/intern/eval/deg_eval.cc
@@ -437,7 +437,7 @@ void deg_evaluate_on_refresh(Depsgraph *graph)
evaluate_graph_threaded_stage(&state, task_pool, EvaluationStage::COPY_ON_WRITE);
- if (graph->has_animated_visibility) {
+ if (graph->has_animated_visibility || graph->need_update_nodes_visibility) {
/* Update pending parents including only the ones which are affecting operations which are
* affecting visibility. */
state.need_update_pending_parents = true;
diff --git a/source/blender/depsgraph/intern/eval/deg_eval_visibility.cc b/source/blender/depsgraph/intern/eval/deg_eval_visibility.cc
index 05f7631b0d4..7b6aec0a73c 100644
--- a/source/blender/depsgraph/intern/eval/deg_eval_visibility.cc
+++ b/source/blender/depsgraph/intern/eval/deg_eval_visibility.cc
@@ -8,6 +8,7 @@
#include "intern/eval/deg_eval_visibility.h"
#include "DNA_layer_types.h"
+#include "DNA_modifier_types.h"
#include "DNA_object_types.h"
#include "BLI_assert.h"
@@ -47,6 +48,38 @@ void deg_evaluate_object_node_visibility(::Depsgraph *depsgraph, IDNode *id_node
}
}
+void deg_evaluate_object_modifiers_mode_node_visibility(::Depsgraph *depsgraph, IDNode *id_node)
+{
+ BLI_assert(GS(id_node->id_cow->name) == ID_OB);
+
+ Depsgraph *graph = reinterpret_cast<Depsgraph *>(depsgraph);
+ const Object *object = reinterpret_cast<const Object *>(id_node->id_cow);
+
+ DEG_debug_print_eval(depsgraph, __func__, object->id.name, &object->id);
+
+ if (BLI_listbase_is_empty(&object->modifiers)) {
+ return;
+ }
+
+ const ModifierMode modifier_mode = (graph->mode == DAG_EVAL_VIEWPORT) ? eModifierMode_Realtime :
+ eModifierMode_Render;
+
+ const ComponentNode *geometry_component = id_node->find_component(NodeType::GEOMETRY);
+ LISTBASE_FOREACH (ModifierData *, modifier, &object->modifiers) {
+ OperationNode *modifier_node = geometry_component->find_operation(OperationCode::MODIFIER,
+ modifier->name);
+
+ const bool modifier_enabled = modifier->mode & modifier_mode;
+ const int mute_flag = modifier_enabled ? 0 : DEPSOP_FLAG_MUTE;
+ if ((modifier_node->flag & DEPSOP_FLAG_MUTE) != mute_flag) {
+ modifier_node->flag &= ~DEPSOP_FLAG_MUTE;
+ modifier_node->flag |= mute_flag;
+
+ graph->need_update_nodes_visibility = true;
+ }
+ }
+}
+
void deg_graph_flush_visibility_flags(Depsgraph *graph)
{
enum {
@@ -116,10 +149,30 @@ void deg_graph_flush_visibility_flags(Depsgraph *graph)
OperationNode *op_from = reinterpret_cast<OperationNode *>(rel->from);
ComponentNode *comp_from = op_from->owner;
+ op_from->flag |= (op_to->flag & OperationFlag::DEPSOP_FLAG_AFFECTS_VISIBILITY);
+
+ if (rel->flag & RELATION_NO_VISIBILITY_CHANGE) {
+ continue;
+ }
+
const bool target_possibly_affects_visible_id = comp_to->possibly_affects_visible_id;
- const bool target_affects_visible_id = comp_to->affects_visible_id;
- op_from->flag |= (op_to->flag & OperationFlag::DEPSOP_FLAG_AFFECTS_VISIBILITY);
+ bool target_affects_visible_id = comp_to->affects_visible_id;
+
+ /* This is a bit arbitrary but the idea here is following:
+ *
+ * - When another object is used by a disabled modifier we do not want that object to
+ * be considered needed for evaluation.
+ *
+ * - However, we do not want to take mute flag during visibility propagation within the
+ * same object. Otherwise drivers and transform dependencies of the geometry component
+ * entry component might not be properly handled.
+ *
+ * This code works fine for muting modifiers, but might need tweaks when mute is used for
+ * something else. */
+ if (comp_from != comp_to && (op_to->flag & DEPSOP_FLAG_MUTE)) {
+ target_affects_visible_id = false;
+ }
/* Visibility component forces all components of the current ID to be considered as
* affecting directly visible. */
diff --git a/source/blender/depsgraph/intern/eval/deg_eval_visibility.h b/source/blender/depsgraph/intern/eval/deg_eval_visibility.h
index 9e9db8ab34a..6544654f3cf 100644
--- a/source/blender/depsgraph/intern/eval/deg_eval_visibility.h
+++ b/source/blender/depsgraph/intern/eval/deg_eval_visibility.h
@@ -18,6 +18,9 @@ struct IDNode;
* restriction flags. */
void deg_evaluate_object_node_visibility(::Depsgraph *depsgraph, IDNode *id_node);
+/* Update node visibility flags based on actual modifiers mode flags. */
+void deg_evaluate_object_modifiers_mode_node_visibility(::Depsgraph *depsgraph, IDNode *id_node);
+
/* Flush both static and dynamic visibility flags from leaves up to the roots, making it possible
* to know whether a node has affect on something (potentially) visible. */
void deg_graph_flush_visibility_flags(Depsgraph *graph);
diff --git a/source/blender/depsgraph/intern/node/deg_node_operation.cc b/source/blender/depsgraph/intern/node/deg_node_operation.cc
index de4bc0668cf..016af735fcf 100644
--- a/source/blender/depsgraph/intern/node/deg_node_operation.cc
+++ b/source/blender/depsgraph/intern/node/deg_node_operation.cc
@@ -84,6 +84,8 @@ const char *operationCodeAsString(OperationCode opcode)
/* Geometry. */
case OperationCode::GEOMETRY_EVAL_INIT:
return "GEOMETRY_EVAL_INIT";
+ case OperationCode::MODIFIER:
+ return "MODIFIER";
case OperationCode::GEOMETRY_EVAL:
return "GEOMETRY_EVAL";
case OperationCode::GEOMETRY_EVAL_DONE:
@@ -225,6 +227,16 @@ void OperationNode::tag_update(Depsgraph *graph, eUpdateSource source)
* the evaluated clues that evaluation needs to happen again. */
graph->add_entry_tag(this);
+ /* Enforce dynamic visibility code-path update.
+ * This ensures visibility flags are consistently propagated throughout the dependency graph when
+ * there is no animated visibility in the graph.
+ *
+ * For example this ensures that graph is updated properly when manually toggling non-animated
+ * modifier visibility. */
+ if (opcode == OperationCode::VISIBILITY) {
+ graph->need_update_nodes_visibility = true;
+ }
+
/* Tag for update, but also note that this was the source of an update. */
flag |= (DEPSOP_FLAG_NEEDS_UPDATE | DEPSOP_FLAG_DIRECTLY_MODIFIED);
switch (source) {
diff --git a/source/blender/depsgraph/intern/node/deg_node_operation.h b/source/blender/depsgraph/intern/node/deg_node_operation.h
index 656b27550f6..cb3beb56556 100644
--- a/source/blender/depsgraph/intern/node/deg_node_operation.h
+++ b/source/blender/depsgraph/intern/node/deg_node_operation.h
@@ -84,6 +84,8 @@ enum class OperationCode {
/* Initialize evaluation of the geometry. Is an entry operation of geometry
* component. */
GEOMETRY_EVAL_INIT,
+ /* Modifier. */
+ MODIFIER,
/* Evaluate the whole geometry, including modifiers. */
GEOMETRY_EVAL,
/* Evaluation of geometry is completely done. */
@@ -217,6 +219,9 @@ enum OperationFlag {
/* The operation directly or indirectly affects ID node visibility. */
DEPSOP_FLAG_AFFECTS_VISIBILITY = (1 << 4),
+ /* Evaluation of the node is temporarily disabled. */
+ DEPSOP_FLAG_MUTE = (1 << 5),
+
/* Set of flags which gets flushed along the relations. */
DEPSOP_FLAG_FLUSH = (DEPSOP_FLAG_USER_MODIFIED),
};
diff --git a/source/blender/draw/CMakeLists.txt b/source/blender/draw/CMakeLists.txt
index c34a6daa126..391c3bb3512 100644
--- a/source/blender/draw/CMakeLists.txt
+++ b/source/blender/draw/CMakeLists.txt
@@ -23,6 +23,7 @@ set(INC
../nodes
../render
../render/intern
+ ../compositor/realtime_compositor
../windowmanager
../../../intern/atomic
@@ -77,13 +78,13 @@ set(SRC
intern/draw_cache_impl_mesh.cc
intern/draw_cache_impl_metaball.c
intern/draw_cache_impl_particles.c
- intern/draw_cache_impl_pointcloud.c
+ intern/draw_cache_impl_pointcloud.cc
intern/draw_cache_impl_subdivision.cc
intern/draw_cache_impl_volume.c
intern/draw_color_management.cc
intern/draw_common.c
intern/draw_curves.cc
- intern/draw_debug.c
+ intern/draw_debug.cc
intern/draw_fluid.c
intern/draw_hair.cc
intern/draw_instance_data.c
@@ -103,6 +104,7 @@ set(SRC
intern/smaa_textures.c
engines/basic/basic_engine.c
engines/basic/basic_shader.c
+ engines/compositor/compositor_engine.cc
engines/image/image_engine.cc
engines/image/image_shader.cc
engines/eevee/eevee_bloom.c
@@ -214,6 +216,7 @@ set(SRC
intern/draw_common_shader_shared.h
intern/draw_curves_private.h
intern/draw_debug.h
+ intern/draw_debug.hh
intern/draw_hair_private.h
intern/draw_instance_data.h
intern/draw_manager.h
@@ -230,6 +233,7 @@ set(SRC
intern/smaa_textures.h
engines/basic/basic_engine.h
engines/basic/basic_private.h
+ engines/compositor/compositor_engine.h
engines/eevee/eevee_engine.h
engines/eevee/eevee_lightcache.h
engines/eevee/eevee_lut.h
@@ -261,6 +265,7 @@ set(SRC
set(LIB
bf_blenkernel
bf_blenlib
+ bf_realtime_compositor
bf_windowmanager
)
@@ -434,20 +439,19 @@ set(GLSL_SRC
intern/shaders/common_attribute_lib.glsl
intern/shaders/common_colormanagement_lib.glsl
+ intern/shaders/common_debug_draw_lib.glsl
+ intern/shaders/common_debug_print_lib.glsl
+ intern/shaders/common_fullscreen_vert.glsl
+ intern/shaders/common_fxaa_lib.glsl
intern/shaders/common_globals_lib.glsl
intern/shaders/common_gpencil_lib.glsl
- intern/shaders/common_pointcloud_lib.glsl
intern/shaders/common_hair_lib.glsl
- intern/shaders/common_hair_refine_vert.glsl
intern/shaders/common_hair_refine_comp.glsl
- intern/shaders/common_math_lib.glsl
+ intern/shaders/common_hair_refine_vert.glsl
intern/shaders/common_math_geom_lib.glsl
- intern/shaders/common_view_clipping_lib.glsl
- intern/shaders/common_view_lib.glsl
- intern/shaders/common_fxaa_lib.glsl
+ intern/shaders/common_math_lib.glsl
+ intern/shaders/common_pointcloud_lib.glsl
intern/shaders/common_smaa_lib.glsl
- intern/shaders/common_fullscreen_vert.glsl
-
intern/shaders/common_subdiv_custom_data_interp_comp.glsl
intern/shaders/common_subdiv_ibo_lines_comp.glsl
intern/shaders/common_subdiv_ibo_tris_comp.glsl
@@ -460,6 +464,13 @@ set(GLSL_SRC
intern/shaders/common_subdiv_vbo_edituv_strech_area_comp.glsl
intern/shaders/common_subdiv_vbo_lnor_comp.glsl
intern/shaders/common_subdiv_vbo_sculpt_data_comp.glsl
+ intern/shaders/common_view_clipping_lib.glsl
+ intern/shaders/common_view_lib.glsl
+ intern/shaders/draw_debug_draw_display_frag.glsl
+ intern/shaders/draw_debug_draw_display_vert.glsl
+ intern/shaders/draw_debug_info.hh
+ intern/shaders/draw_debug_print_display_frag.glsl
+ intern/shaders/draw_debug_print_display_vert.glsl
intern/draw_common_shader_shared.h
intern/draw_shader_shared.h
diff --git a/source/blender/draw/engines/compositor/compositor_engine.cc b/source/blender/draw/engines/compositor/compositor_engine.cc
new file mode 100644
index 00000000000..f36a59a4ce6
--- /dev/null
+++ b/source/blender/draw/engines/compositor/compositor_engine.cc
@@ -0,0 +1,203 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include "BLI_listbase.h"
+#include "BLI_math_vec_types.hh"
+#include "BLI_string_ref.hh"
+#include "BLI_utildefines.h"
+
+#include "BLT_translation.h"
+
+#include "DNA_ID_enums.h"
+#include "DNA_scene_types.h"
+
+#include "DEG_depsgraph_query.h"
+
+#include "DRW_render.h"
+
+#include "IMB_colormanagement.h"
+
+#include "COM_context.hh"
+#include "COM_evaluator.hh"
+#include "COM_texture_pool.hh"
+
+#include "GPU_texture.h"
+
+namespace blender::draw::compositor {
+
+class TexturePool : public realtime_compositor::TexturePool {
+ public:
+ GPUTexture *allocate_texture(int2 size, eGPUTextureFormat format) override
+ {
+ DrawEngineType *owner = (DrawEngineType *)this;
+ return DRW_texture_pool_query_2d(size.x, size.y, format, owner);
+ }
+};
+
+class Context : public realtime_compositor::Context {
+ private:
+ /* A pointer to the info message of the compositor engine. This is a char array of size
+ * GPU_INFO_SIZE. The message is cleared prior to updating or evaluating the compositor. */
+ char *info_message_;
+
+ public:
+ Context(realtime_compositor::TexturePool &texture_pool, char *info_message)
+ : realtime_compositor::Context(texture_pool), info_message_(info_message)
+ {
+ }
+
+ const Scene *get_scene() const override
+ {
+ return DRW_context_state_get()->scene;
+ }
+
+ int2 get_output_size() override
+ {
+ return int2(float2(DRW_viewport_size_get()));
+ }
+
+ GPUTexture *get_output_texture() override
+ {
+ return DRW_viewport_texture_list_get()->color;
+ }
+
+ GPUTexture *get_input_texture(int UNUSED(view_layer), eScenePassType UNUSED(pass_type)) override
+ {
+ return get_output_texture();
+ }
+
+ StringRef get_view_name() override
+ {
+ const SceneRenderView *view = static_cast<SceneRenderView *>(
+ BLI_findlink(&get_scene()->r.views, DRW_context_state_get()->v3d->multiview_eye));
+ return view->name;
+ }
+
+ void set_info_message(StringRef message) const override
+ {
+ message.copy(info_message_, GPU_INFO_SIZE);
+ }
+};
+
+class Engine {
+ private:
+ TexturePool texture_pool_;
+ Context context_;
+ realtime_compositor::Evaluator evaluator_;
+ /* Stores the viewport size at the time the last compositor evaluation happened. See the
+ * update_viewport_size method for more information. */
+ int2 last_viewport_size_;
+
+ public:
+ Engine(char *info_message)
+ : context_(texture_pool_, info_message),
+ evaluator_(context_, node_tree()),
+ last_viewport_size_(context_.get_output_size())
+ {
+ }
+
+ /* Update the viewport size and evaluate the compositor. */
+ void draw()
+ {
+ update_viewport_size();
+ evaluator_.evaluate();
+ }
+
+ /* If the size of the viewport changed from the last time the compositor was evaluated, update
+ * the viewport size and reset the evaluator. That's because the evaluator compiles the node tree
+ * in a manner that is specifically optimized for the size of the viewport. This should be called
+ * before evaluating the compositor. */
+ void update_viewport_size()
+ {
+ if (last_viewport_size_ == context_.get_output_size()) {
+ return;
+ }
+
+ last_viewport_size_ = context_.get_output_size();
+
+ evaluator_.reset();
+ }
+
+ /* If the compositor node tree changed, reset the evaluator. */
+ void update(const Depsgraph *depsgraph)
+ {
+ if (DEG_id_type_updated(depsgraph, ID_NT)) {
+ evaluator_.reset();
+ }
+ }
+
+ /* Get a reference to the compositor node tree. */
+ static bNodeTree &node_tree()
+ {
+ return *DRW_context_state_get()->scene->nodetree;
+ }
+};
+
+} // namespace blender::draw::compositor
+
+using namespace blender::draw::compositor;
+
+struct COMPOSITOR_Data {
+ DrawEngineType *engine_type;
+ DRWViewportEmptyList *fbl;
+ DRWViewportEmptyList *txl;
+ DRWViewportEmptyList *psl;
+ DRWViewportEmptyList *stl;
+ Engine *instance_data;
+ char info[GPU_INFO_SIZE];
+};
+
+static void compositor_engine_init(void *data)
+{
+ COMPOSITOR_Data *compositor_data = static_cast<COMPOSITOR_Data *>(data);
+
+ if (!compositor_data->instance_data) {
+ compositor_data->instance_data = new Engine(compositor_data->info);
+ }
+}
+
+static void compositor_engine_free(void *instance_data)
+{
+ Engine *engine = static_cast<Engine *>(instance_data);
+ delete engine;
+}
+
+static void compositor_engine_draw(void *data)
+{
+ const COMPOSITOR_Data *compositor_data = static_cast<COMPOSITOR_Data *>(data);
+ compositor_data->instance_data->draw();
+}
+
+static void compositor_engine_update(void *data)
+{
+ COMPOSITOR_Data *compositor_data = static_cast<COMPOSITOR_Data *>(data);
+
+ /* Clear any info message that was set in a previous update. */
+ compositor_data->info[0] = '\0';
+
+ if (compositor_data->instance_data) {
+ compositor_data->instance_data->update(DRW_context_state_get()->depsgraph);
+ }
+}
+
+extern "C" {
+
+static const DrawEngineDataSize compositor_data_size = DRW_VIEWPORT_DATA_SIZE(COMPOSITOR_Data);
+
+DrawEngineType draw_engine_compositor_type = {
+ nullptr, /* next */
+ nullptr, /* prev */
+ N_("Compositor"), /* idname */
+ &compositor_data_size, /* vedata_size */
+ &compositor_engine_init, /* engine_init */
+ nullptr, /* engine_free */
+ &compositor_engine_free, /* instance_free */
+ nullptr, /* cache_init */
+ nullptr, /* cache_populate */
+ nullptr, /* cache_finish */
+ &compositor_engine_draw, /* draw_scene */
+ &compositor_engine_update, /* view_update */
+ nullptr, /* id_update */
+ nullptr, /* render_to_image */
+ nullptr, /* store_metadata */
+};
+}
diff --git a/source/blender/draw/engines/compositor/compositor_engine.h b/source/blender/draw/engines/compositor/compositor_engine.h
new file mode 100644
index 00000000000..5de0de8a0b3
--- /dev/null
+++ b/source/blender/draw/engines/compositor/compositor_engine.h
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#pragma once
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern DrawEngineType draw_engine_compositor_type;
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/source/blender/draw/engines/eevee/eevee_shadows_cascade.c b/source/blender/draw/engines/eevee/eevee_shadows_cascade.c
index 536242f67d8..a3ab4cdb830 100644
--- a/source/blender/draw/engines/eevee/eevee_shadows_cascade.c
+++ b/source/blender/draw/engines/eevee/eevee_shadows_cascade.c
@@ -357,7 +357,7 @@ static void eevee_shadow_cascade_setup(EEVEE_LightsInfo *linfo,
mul_m4_m4m4(csm_data->shadowmat[c], texcomat, viewprojmat);
#ifdef DEBUG_CSM
- DRW_debug_m4_as_bbox(viewprojmat, dbg_col, true);
+ DRW_debug_m4_as_bbox(viewprojmat, true, dbg_col);
#endif
}
diff --git a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_depth_of_field_info.hh b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_depth_of_field_info.hh
index 94ff694b147..b398a6cc4e7 100644
--- a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_depth_of_field_info.hh
+++ b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_depth_of_field_info.hh
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
#include "eevee_defines.hh"
#include "gpu_shader_create_info.hh"
diff --git a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_motion_blur_info.hh b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_motion_blur_info.hh
index e32020f2be6..d6ff34b0ed2 100644
--- a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_motion_blur_info.hh
+++ b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_motion_blur_info.hh
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
#include "eevee_defines.hh"
#include "gpu_shader_create_info.hh"
diff --git a/source/blender/draw/engines/overlay/overlay_armature.c b/source/blender/draw/engines/overlay/overlay_armature.c
index e38695c76ab..df5ee6a18c0 100644
--- a/source/blender/draw/engines/overlay/overlay_armature.c
+++ b/source/blender/draw/engines/overlay/overlay_armature.c
@@ -2220,7 +2220,7 @@ static void draw_armature_edit(ArmatureDrawContext *ctx)
const bool show_text = DRW_state_show_text();
const Object *ob_orig = DEG_get_original_object(ob);
- /* FIXME(campbell): We should be able to use the CoW object,
+ /* FIXME(@campbellbarton): We should be able to use the CoW object,
* however the active bone isn't updated. Long term solution is an 'EditArmature' struct.
* for now we can draw from the original armature. See: T66773. */
// bArmature *arm = ob->data;
diff --git a/source/blender/draw/intern/draw_cache_extract_mesh_render_data.cc b/source/blender/draw/intern/draw_cache_extract_mesh_render_data.cc
index af8e58c78f8..0159c9fc86e 100644
--- a/source/blender/draw/intern/draw_cache_extract_mesh_render_data.cc
+++ b/source/blender/draw/intern/draw_cache_extract_mesh_render_data.cc
@@ -228,9 +228,9 @@ static void mesh_render_data_polys_sorted_build(MeshRenderData *mr, MeshBufferCa
}
}
else {
- const MPoly *mp = &mr->mpoly[0];
- for (int i = 0; i < mr->poly_len; i++, mp++) {
- if (!(mr->use_hide && (mp->flag & ME_HIDE))) {
+ for (int i = 0; i < mr->poly_len; i++) {
+ if (!(mr->use_hide && mr->hide_poly && mr->hide_poly[i])) {
+ const MPoly *mp = &mr->mpoly[i];
const int mat = min_ii(mp->mat_nr, mat_last);
tri_first_index[i] = mat_tri_offs[mat];
mat_tri_offs[mat] += mp->totloop - 2;
@@ -269,7 +269,7 @@ static void mesh_render_data_mat_tri_len_mesh_range_fn(void *__restrict userdata
int *mat_tri_len = static_cast<int *>(tls->userdata_chunk);
const MPoly *mp = &mr->mpoly[iter];
- if (!(mr->use_hide && (mp->flag & ME_HIDE))) {
+ if (!(mr->use_hide && mr->hide_poly && mr->hide_poly[iter])) {
int mat = min_ii(mp->mat_nr, mr->mat_len - 1);
mat_tri_len[mat] += mp->totloop - 2;
}
@@ -332,7 +332,7 @@ void mesh_render_data_update_looptris(MeshRenderData *mr,
if (mr->extract_type != MR_EXTRACT_BMESH) {
/* Mesh */
if ((iter_type & MR_ITER_LOOPTRI) || (data_flag & MR_DATA_LOOPTRI)) {
- /* NOTE(campbell): It's possible to skip allocating tessellation,
+ /* NOTE(@campbellbarton): It's possible to skip allocating tessellation,
* the tessellation can be calculated as part of the iterator, see: P2188.
* The overall advantage is small (around 1%), so keep this as-is. */
mr->mlooptri = static_cast<MLoopTri *>(
@@ -578,6 +578,13 @@ MeshRenderData *mesh_render_data_create(Object *object,
mr->v_origindex = static_cast<const int *>(CustomData_get_layer(&mr->me->vdata, CD_ORIGINDEX));
mr->e_origindex = static_cast<const int *>(CustomData_get_layer(&mr->me->edata, CD_ORIGINDEX));
mr->p_origindex = static_cast<const int *>(CustomData_get_layer(&mr->me->pdata, CD_ORIGINDEX));
+
+ mr->hide_vert = static_cast<const bool *>(
+ CustomData_get_layer_named(&me->vdata, CD_PROP_BOOL, ".hide_vert"));
+ mr->hide_edge = static_cast<const bool *>(
+ CustomData_get_layer_named(&me->edata, CD_PROP_BOOL, ".hide_edge"));
+ mr->hide_poly = static_cast<const bool *>(
+ CustomData_get_layer_named(&me->pdata, CD_PROP_BOOL, ".hide_poly"));
}
else {
/* #BMesh */
diff --git a/source/blender/draw/intern/draw_cache_impl_pointcloud.c b/source/blender/draw/intern/draw_cache_impl_pointcloud.cc
index 55d0eee00e5..d99af0c77e4 100644
--- a/source/blender/draw/intern/draw_cache_impl_pointcloud.c
+++ b/source/blender/draw/intern/draw_cache_impl_pointcloud.cc
@@ -13,24 +13,23 @@
#include "BLI_math_base.h"
#include "BLI_math_vector.h"
+#include "BLI_task.hh"
#include "BLI_utildefines.h"
#include "DNA_object_types.h"
#include "DNA_pointcloud_types.h"
-#include "BKE_customdata.h"
+#include "BKE_attribute.hh"
#include "BKE_pointcloud.h"
#include "GPU_batch.h"
#include "draw_cache_impl.h" /* own include */
-static void pointcloud_batch_cache_clear(PointCloud *pointcloud);
-
/* ---------------------------------------------------------------------- */
/* PointCloud GPUBatch Cache */
-typedef struct PointCloudBatchCache {
+struct PointCloudBatchCache {
GPUVertBuf *pos; /* Position and radius. */
GPUVertBuf *geom; /* Instanced geometry for each point in the cloud (small sphere). */
GPUIndexBuf *geom_indices;
@@ -43,57 +42,50 @@ typedef struct PointCloudBatchCache {
bool is_dirty;
int mat_len;
-} PointCloudBatchCache;
+};
/* GPUBatch cache management. */
-static bool pointcloud_batch_cache_valid(PointCloud *pointcloud)
+static PointCloudBatchCache *pointcloud_batch_cache_get(PointCloud &pointcloud)
+{
+ return static_cast<PointCloudBatchCache *>(pointcloud.batch_cache);
+}
+
+static bool pointcloud_batch_cache_valid(PointCloud &pointcloud)
{
- PointCloudBatchCache *cache = pointcloud->batch_cache;
+ PointCloudBatchCache *cache = pointcloud_batch_cache_get(pointcloud);
if (cache == NULL) {
return false;
}
- if (cache->mat_len != DRW_pointcloud_material_count_get(pointcloud)) {
+ if (cache->mat_len != DRW_pointcloud_material_count_get(&pointcloud)) {
return false;
}
return cache->is_dirty == false;
}
-static void pointcloud_batch_cache_init(PointCloud *pointcloud)
+static void pointcloud_batch_cache_init(PointCloud &pointcloud)
{
- PointCloudBatchCache *cache = pointcloud->batch_cache;
+ PointCloudBatchCache *cache = pointcloud_batch_cache_get(pointcloud);
if (!cache) {
- cache = pointcloud->batch_cache = MEM_callocN(sizeof(*cache), __func__);
+ cache = MEM_cnew<PointCloudBatchCache>(__func__);
+ pointcloud.batch_cache = cache;
}
else {
memset(cache, 0, sizeof(*cache));
}
- cache->mat_len = DRW_pointcloud_material_count_get(pointcloud);
- cache->surface_per_mat = MEM_callocN(sizeof(GPUBatch *) * cache->mat_len,
- "pointcloud suface_per_mat");
+ cache->mat_len = DRW_pointcloud_material_count_get(&pointcloud);
+ cache->surface_per_mat = static_cast<GPUBatch **>(
+ MEM_callocN(sizeof(GPUBatch *) * cache->mat_len, __func__));
cache->is_dirty = false;
}
-void DRW_pointcloud_batch_cache_validate(PointCloud *pointcloud)
-{
- if (!pointcloud_batch_cache_valid(pointcloud)) {
- pointcloud_batch_cache_clear(pointcloud);
- pointcloud_batch_cache_init(pointcloud);
- }
-}
-
-static PointCloudBatchCache *pointcloud_batch_cache_get(PointCloud *pointcloud)
-{
- return pointcloud->batch_cache;
-}
-
void DRW_pointcloud_batch_cache_dirty_tag(PointCloud *pointcloud, int mode)
{
- PointCloudBatchCache *cache = pointcloud->batch_cache;
+ PointCloudBatchCache *cache = pointcloud_batch_cache_get(*pointcloud);
if (cache == NULL) {
return;
}
@@ -106,9 +98,9 @@ void DRW_pointcloud_batch_cache_dirty_tag(PointCloud *pointcloud, int mode)
}
}
-static void pointcloud_batch_cache_clear(PointCloud *pointcloud)
+static void pointcloud_batch_cache_clear(PointCloud &pointcloud)
{
- PointCloudBatchCache *cache = pointcloud->batch_cache;
+ PointCloudBatchCache *cache = pointcloud_batch_cache_get(pointcloud);
if (!cache) {
return;
}
@@ -127,54 +119,65 @@ static void pointcloud_batch_cache_clear(PointCloud *pointcloud)
MEM_SAFE_FREE(cache->surface_per_mat);
}
+void DRW_pointcloud_batch_cache_validate(PointCloud *pointcloud)
+{
+ if (!pointcloud_batch_cache_valid(*pointcloud)) {
+ pointcloud_batch_cache_clear(*pointcloud);
+ pointcloud_batch_cache_init(*pointcloud);
+ }
+}
+
void DRW_pointcloud_batch_cache_free(PointCloud *pointcloud)
{
- pointcloud_batch_cache_clear(pointcloud);
+ pointcloud_batch_cache_clear(*pointcloud);
MEM_SAFE_FREE(pointcloud->batch_cache);
}
-static void pointcloud_batch_cache_ensure_pos(Object *ob, PointCloudBatchCache *cache)
+static void pointcloud_batch_cache_ensure_pos(const PointCloud &pointcloud,
+ PointCloudBatchCache &cache)
{
- if (cache->pos != NULL) {
+ using namespace blender;
+ if (cache.pos != NULL) {
return;
}
- PointCloud *pointcloud = ob->data;
- const float(*positions)[3] = (float(*)[3])CustomData_get_layer_named(
- &pointcloud->pdata, CD_PROP_FLOAT3, "position");
- const float *radii = (float *)CustomData_get_layer_named(
- &pointcloud->pdata, CD_PROP_FLOAT, "radius");
- const bool has_radius = radii != NULL;
-
- static GPUVertFormat format = {0};
- static GPUVertFormat format_no_radius = {0};
- static uint pos;
- if (format.attr_len == 0) {
- /* initialize vertex format */
- /* From the opengl wiki:
- * Note that size does not have to exactly match the size used by the vertex shader. If the
- * vertex shader has fewer components than the attribute provides, then the extras are ignored.
- * If the vertex shader has more components than the array provides, the extras are given
- * values from the vector (0, 0, 0, 1) for the missing XYZW components.
- */
- pos = GPU_vertformat_attr_add(&format_no_radius, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
- pos = GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
- }
-
- cache->pos = GPU_vertbuf_create_with_format(has_radius ? &format : &format_no_radius);
- GPU_vertbuf_data_alloc(cache->pos, pointcloud->totpoint);
-
- if (has_radius) {
- float(*vbo_data)[4] = (float(*)[4])GPU_vertbuf_get_data(cache->pos);
- for (int i = 0; i < pointcloud->totpoint; i++) {
- copy_v3_v3(vbo_data[i], positions[i]);
- /* TODO(fclem): remove multiplication here.
- * Here only for keeping the size correct for now. */
- vbo_data[i][3] = radii[i] * 100.0f;
+ const bke::AttributeAccessor attributes = bke::pointcloud_attributes(pointcloud);
+ const VArraySpan<float3> positions = attributes.lookup<float3>("position", ATTR_DOMAIN_POINT);
+ const VArray<float> radii = attributes.lookup<float>("radius", ATTR_DOMAIN_POINT);
+ /* From the opengl wiki:
+ * Note that size does not have to exactly match the size used by the vertex shader. If the
+ * vertex shader has fewer components than the attribute provides, then the extras are ignored.
+ * If the vertex shader has more components than the array provides, the extras are given
+ * values from the vector (0, 0, 0, 1) for the missing XYZW components. */
+ if (radii) {
+ static GPUVertFormat format = {0};
+ if (format.attr_len == 0) {
+ GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
}
+ cache.pos = GPU_vertbuf_create_with_format(&format);
+ GPU_vertbuf_data_alloc(cache.pos, positions.size());
+ const VArraySpan<float> radii_span(radii);
+ MutableSpan<float4> vbo_data{static_cast<float4 *>(GPU_vertbuf_get_data(cache.pos)),
+ pointcloud.totpoint};
+ threading::parallel_for(vbo_data.index_range(), 4096, [&](IndexRange range) {
+ for (const int i : range) {
+ vbo_data[i].x = positions[i].x;
+ vbo_data[i].y = positions[i].y;
+ vbo_data[i].z = positions[i].z;
+ /* TODO(fclem): remove multiplication. Here only for keeping the size correct for now. */
+ vbo_data[i].w = radii_span[i] * 100.0f;
+ }
+ });
}
else {
- GPU_vertbuf_attr_fill(cache->pos, pos, positions);
+ static GPUVertFormat format = {0};
+ static uint pos;
+ if (format.attr_len == 0) {
+ pos = GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
+ }
+ cache.pos = GPU_vertbuf_create_with_format(&format);
+ GPU_vertbuf_data_alloc(cache.pos, positions.size());
+ GPU_vertbuf_attr_fill(cache.pos, pos, positions.data());
}
}
@@ -193,24 +196,23 @@ static const uint half_octahedron_tris[4][3] = {
{0, 4, 1},
};
-static void pointcloud_batch_cache_ensure_geom(Object *UNUSED(ob), PointCloudBatchCache *cache)
+static void pointcloud_batch_cache_ensure_geom(PointCloudBatchCache &cache)
{
- if (cache->geom != NULL) {
+ if (cache.geom != NULL) {
return;
}
static GPUVertFormat format = {0};
static uint pos;
if (format.attr_len == 0) {
- /* initialize vertex format */
pos = GPU_vertformat_attr_add(&format, "pos_inst", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
GPU_vertformat_alias_add(&format, "nor");
}
- cache->geom = GPU_vertbuf_create_with_format(&format);
- GPU_vertbuf_data_alloc(cache->geom, ARRAY_SIZE(half_octahedron_normals));
+ cache.geom = GPU_vertbuf_create_with_format(&format);
+ GPU_vertbuf_data_alloc(cache.geom, ARRAY_SIZE(half_octahedron_normals));
- GPU_vertbuf_attr_fill(cache->geom, pos, half_octahedron_normals);
+ GPU_vertbuf_attr_fill(cache.geom, pos, half_octahedron_normals);
GPUIndexBufBuilder builder;
GPU_indexbuf_init(&builder,
@@ -222,16 +224,16 @@ static void pointcloud_batch_cache_ensure_geom(Object *UNUSED(ob), PointCloudBat
GPU_indexbuf_add_tri_verts(&builder, UNPACK3(half_octahedron_tris[i]));
}
- cache->geom_indices = GPU_indexbuf_build(&builder);
+ cache.geom_indices = GPU_indexbuf_build(&builder);
}
GPUBatch *DRW_pointcloud_batch_cache_get_dots(Object *ob)
{
- PointCloud *pointcloud = ob->data;
+ PointCloud &pointcloud = *static_cast<PointCloud *>(ob->data);
PointCloudBatchCache *cache = pointcloud_batch_cache_get(pointcloud);
if (cache->dots == NULL) {
- pointcloud_batch_cache_ensure_pos(ob, cache);
+ pointcloud_batch_cache_ensure_pos(pointcloud, *cache);
cache->dots = GPU_batch_create(GPU_PRIM_POINTS, cache->pos, NULL);
}
@@ -240,12 +242,12 @@ GPUBatch *DRW_pointcloud_batch_cache_get_dots(Object *ob)
GPUBatch *DRW_pointcloud_batch_cache_get_surface(Object *ob)
{
- PointCloud *pointcloud = ob->data;
+ PointCloud &pointcloud = *static_cast<PointCloud *>(ob->data);
PointCloudBatchCache *cache = pointcloud_batch_cache_get(pointcloud);
if (cache->surface == NULL) {
- pointcloud_batch_cache_ensure_pos(ob, cache);
- pointcloud_batch_cache_ensure_geom(ob, cache);
+ pointcloud_batch_cache_ensure_pos(pointcloud, *cache);
+ pointcloud_batch_cache_ensure_geom(*cache);
cache->surface = GPU_batch_create(GPU_PRIM_TRIS, cache->geom, cache->geom_indices);
GPU_batch_instbuf_add_ex(cache->surface, cache->pos, false);
@@ -258,14 +260,14 @@ GPUBatch **DRW_cache_pointcloud_surface_shaded_get(Object *ob,
struct GPUMaterial **UNUSED(gpumat_array),
uint gpumat_array_len)
{
- PointCloud *pointcloud = ob->data;
+ PointCloud &pointcloud = *static_cast<PointCloud *>(ob->data);
PointCloudBatchCache *cache = pointcloud_batch_cache_get(pointcloud);
BLI_assert(cache->mat_len == gpumat_array_len);
UNUSED_VARS(gpumat_array_len);
if (cache->surface_per_mat[0] == NULL) {
- pointcloud_batch_cache_ensure_pos(ob, cache);
- pointcloud_batch_cache_ensure_geom(ob, cache);
+ pointcloud_batch_cache_ensure_pos(pointcloud, *cache);
+ pointcloud_batch_cache_ensure_geom(*cache);
cache->surface_per_mat[0] = GPU_batch_create(GPU_PRIM_TRIS, cache->geom, cache->geom_indices);
GPU_batch_instbuf_add_ex(cache->surface_per_mat[0], cache->pos, false);
diff --git a/source/blender/draw/intern/draw_cache_impl_subdivision.cc b/source/blender/draw/intern/draw_cache_impl_subdivision.cc
index 2f8a2540776..075e08eb49f 100644
--- a/source/blender/draw/intern/draw_cache_impl_subdivision.cc
+++ b/source/blender/draw/intern/draw_cache_impl_subdivision.cc
@@ -668,7 +668,9 @@ static void draw_subdiv_cache_extra_coarse_face_data_bm(BMesh *bm,
}
}
-static void draw_subdiv_cache_extra_coarse_face_data_mesh(Mesh *mesh, uint32_t *flags_data)
+static void draw_subdiv_cache_extra_coarse_face_data_mesh(const MeshRenderData *mr,
+ Mesh *mesh,
+ uint32_t *flags_data)
{
for (int i = 0; i < mesh->totpoly; i++) {
uint32_t flag = 0;
@@ -678,7 +680,7 @@ static void draw_subdiv_cache_extra_coarse_face_data_mesh(Mesh *mesh, uint32_t *
if ((mesh->mpoly[i].flag & ME_FACE_SEL) != 0) {
flag |= SUBDIV_COARSE_FACE_FLAG_SELECT;
}
- if ((mesh->mpoly[i].flag & ME_HIDE) != 0) {
+ if (mr->hide_poly && mr->hide_poly[i]) {
flag |= SUBDIV_COARSE_FACE_FLAG_HIDDEN;
}
flags_data[i] = (uint)(mesh->mpoly[i].loopstart) | (flag << SUBDIV_COARSE_FACE_FLAG_OFFSET);
@@ -691,7 +693,7 @@ static void draw_subdiv_cache_extra_coarse_face_data_mapped(Mesh *mesh,
uint32_t *flags_data)
{
if (bm == nullptr) {
- draw_subdiv_cache_extra_coarse_face_data_mesh(mesh, flags_data);
+ draw_subdiv_cache_extra_coarse_face_data_mesh(mr, mesh, flags_data);
return;
}
@@ -726,7 +728,7 @@ static void draw_subdiv_cache_update_extra_coarse_face_data(DRWSubdivCache *cach
draw_subdiv_cache_extra_coarse_face_data_mapped(mesh, cache->bm, mr, flags_data);
}
else {
- draw_subdiv_cache_extra_coarse_face_data_mesh(mesh, flags_data);
+ draw_subdiv_cache_extra_coarse_face_data_mesh(mr, mesh, flags_data);
}
/* Make sure updated data is re-uploaded. */
diff --git a/source/blender/draw/intern/draw_debug.c b/source/blender/draw/intern/draw_debug.c
deleted file mode 100644
index b568119627e..00000000000
--- a/source/blender/draw/intern/draw_debug.c
+++ /dev/null
@@ -1,196 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later
- * Copyright 2018 Blender Foundation. */
-
-/** \file
- * \ingroup draw
- *
- * \brief Simple API to draw debug shapes in the viewport.
- */
-
-#include "MEM_guardedalloc.h"
-
-#include "DNA_object_types.h"
-
-#include "BKE_object.h"
-
-#include "BLI_link_utils.h"
-
-#include "GPU_immediate.h"
-#include "GPU_matrix.h"
-
-#include "draw_debug.h"
-#include "draw_manager.h"
-
-/* --------- Register --------- */
-
-/* Matrix applied to all points before drawing. Could be a stack if needed. */
-static float g_modelmat[4][4];
-
-void DRW_debug_modelmat_reset(void)
-{
- unit_m4(g_modelmat);
-}
-
-void DRW_debug_modelmat(const float modelmat[4][4])
-{
- copy_m4_m4(g_modelmat, modelmat);
-}
-
-void DRW_debug_line_v3v3(const float v1[3], const float v2[3], const float color[4])
-{
- DRWDebugLine *line = MEM_mallocN(sizeof(DRWDebugLine), "DRWDebugLine");
- mul_v3_m4v3(line->pos[0], g_modelmat, v1);
- mul_v3_m4v3(line->pos[1], g_modelmat, v2);
- copy_v4_v4(line->color, color);
- BLI_LINKS_PREPEND(DST.debug.lines, line);
-}
-
-void DRW_debug_polygon_v3(const float (*v)[3], const int vert_len, const float color[4])
-{
- BLI_assert(vert_len > 1);
-
- for (int i = 0; i < vert_len; i++) {
- DRW_debug_line_v3v3(v[i], v[(i + 1) % vert_len], color);
- }
-}
-
-void DRW_debug_m4(const float m[4][4])
-{
- float v0[3] = {0.0f, 0.0f, 0.0f};
- float v1[3] = {1.0f, 0.0f, 0.0f};
- float v2[3] = {0.0f, 1.0f, 0.0f};
- float v3[3] = {0.0f, 0.0f, 1.0f};
-
- mul_m4_v3(m, v0);
- mul_m4_v3(m, v1);
- mul_m4_v3(m, v2);
- mul_m4_v3(m, v3);
-
- DRW_debug_line_v3v3(v0, v1, (float[4]){1.0f, 0.0f, 0.0f, 1.0f});
- DRW_debug_line_v3v3(v0, v2, (float[4]){0.0f, 1.0f, 0.0f, 1.0f});
- DRW_debug_line_v3v3(v0, v3, (float[4]){0.0f, 0.0f, 1.0f, 1.0f});
-}
-
-void DRW_debug_bbox(const BoundBox *bbox, const float color[4])
-{
- DRW_debug_line_v3v3(bbox->vec[0], bbox->vec[1], color);
- DRW_debug_line_v3v3(bbox->vec[1], bbox->vec[2], color);
- DRW_debug_line_v3v3(bbox->vec[2], bbox->vec[3], color);
- DRW_debug_line_v3v3(bbox->vec[3], bbox->vec[0], color);
-
- DRW_debug_line_v3v3(bbox->vec[4], bbox->vec[5], color);
- DRW_debug_line_v3v3(bbox->vec[5], bbox->vec[6], color);
- DRW_debug_line_v3v3(bbox->vec[6], bbox->vec[7], color);
- DRW_debug_line_v3v3(bbox->vec[7], bbox->vec[4], color);
-
- DRW_debug_line_v3v3(bbox->vec[0], bbox->vec[4], color);
- DRW_debug_line_v3v3(bbox->vec[1], bbox->vec[5], color);
- DRW_debug_line_v3v3(bbox->vec[2], bbox->vec[6], color);
- DRW_debug_line_v3v3(bbox->vec[3], bbox->vec[7], color);
-}
-
-void DRW_debug_m4_as_bbox(const float m[4][4], const float color[4], const bool invert)
-{
- BoundBox bb;
- const float min[3] = {-1.0f, -1.0f, -1.0f}, max[3] = {1.0f, 1.0f, 1.0f};
- float project_matrix[4][4];
- if (invert) {
- invert_m4_m4(project_matrix, m);
- }
- else {
- copy_m4_m4(project_matrix, m);
- }
-
- BKE_boundbox_init_from_minmax(&bb, min, max);
- for (int i = 0; i < 8; i++) {
- mul_project_m4_v3(project_matrix, bb.vec[i]);
- }
- DRW_debug_bbox(&bb, color);
-}
-
-void DRW_debug_sphere(const float center[3], const float radius, const float color[4])
-{
- float size_mat[4][4];
- DRWDebugSphere *sphere = MEM_mallocN(sizeof(DRWDebugSphere), "DRWDebugSphere");
- /* Bake all transform into a Matrix4 */
- scale_m4_fl(size_mat, radius);
- copy_m4_m4(sphere->mat, g_modelmat);
- translate_m4(sphere->mat, center[0], center[1], center[2]);
- mul_m4_m4m4(sphere->mat, sphere->mat, size_mat);
-
- copy_v4_v4(sphere->color, color);
- BLI_LINKS_PREPEND(DST.debug.spheres, sphere);
-}
-
-/* --------- Render --------- */
-
-static void drw_debug_draw_lines(void)
-{
- int count = BLI_linklist_count((LinkNode *)DST.debug.lines);
-
- if (count == 0) {
- return;
- }
-
- GPUVertFormat *vert_format = immVertexFormat();
- uint pos = GPU_vertformat_attr_add(vert_format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
- uint col = GPU_vertformat_attr_add(vert_format, "color", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
-
- immBindBuiltinProgram(GPU_SHADER_3D_FLAT_COLOR);
-
- immBegin(GPU_PRIM_LINES, count * 2);
-
- while (DST.debug.lines) {
- void *next = DST.debug.lines->next;
-
- immAttr4fv(col, DST.debug.lines->color);
- immVertex3fv(pos, DST.debug.lines->pos[0]);
-
- immAttr4fv(col, DST.debug.lines->color);
- immVertex3fv(pos, DST.debug.lines->pos[1]);
-
- MEM_freeN(DST.debug.lines);
- DST.debug.lines = next;
- }
- immEnd();
-
- immUnbindProgram();
-}
-
-static void drw_debug_draw_spheres(void)
-{
- int count = BLI_linklist_count((LinkNode *)DST.debug.spheres);
-
- if (count == 0) {
- return;
- }
-
- float persmat[4][4];
- DRW_view_persmat_get(NULL, persmat, false);
-
- GPUBatch *empty_sphere = DRW_cache_empty_sphere_get();
- GPU_batch_program_set_builtin(empty_sphere, GPU_SHADER_3D_UNIFORM_COLOR);
- while (DST.debug.spheres) {
- void *next = DST.debug.spheres->next;
- float MVP[4][4];
-
- mul_m4_m4m4(MVP, persmat, DST.debug.spheres->mat);
- GPU_batch_uniform_mat4(empty_sphere, "ModelViewProjectionMatrix", MVP);
- GPU_batch_uniform_4fv(empty_sphere, "color", DST.debug.spheres->color);
- GPU_batch_draw(empty_sphere);
-
- MEM_freeN(DST.debug.spheres);
- DST.debug.spheres = next;
- }
-}
-
-void drw_debug_draw(void)
-{
- drw_debug_draw_lines();
- drw_debug_draw_spheres();
-}
-
-void drw_debug_init(void)
-{
- DRW_debug_modelmat_reset();
-}
diff --git a/source/blender/draw/intern/draw_debug.cc b/source/blender/draw/intern/draw_debug.cc
new file mode 100644
index 00000000000..aaf18014143
--- /dev/null
+++ b/source/blender/draw/intern/draw_debug.cc
@@ -0,0 +1,721 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright 2018 Blender Foundation. */
+
+/** \file
+ * \ingroup draw
+ *
+ * \brief Simple API to draw debug shapes in the viewport.
+ */
+
+#include "BKE_object.h"
+#include "BLI_link_utils.h"
+#include "GPU_batch.h"
+#include "GPU_capabilities.h"
+#include "GPU_debug.h"
+
+#include "draw_debug.h"
+#include "draw_debug.hh"
+#include "draw_manager.h"
+#include "draw_shader.h"
+#include "draw_shader_shared.h"
+
+#include <iomanip>
+
+namespace blender::draw {
+
+/* -------------------------------------------------------------------- */
+/** \name Init and state
+ * \{ */
+
+DebugDraw::DebugDraw()
+{
+ constexpr int circle_resolution = 16;
+ for (auto axis : IndexRange(3)) {
+ for (auto edge : IndexRange(circle_resolution)) {
+ for (auto vert : IndexRange(2)) {
+ const float angle = (2 * M_PI) * (edge + vert) / float(circle_resolution);
+ float point[3] = {cosf(angle), sinf(angle), 0.0f};
+ sphere_verts_.append(
+ float3(point[(0 + axis) % 3], point[(1 + axis) % 3], point[(2 + axis) % 3]));
+ }
+ }
+ }
+
+ constexpr int point_resolution = 4;
+ for (auto axis : IndexRange(3)) {
+ for (auto edge : IndexRange(point_resolution)) {
+ for (auto vert : IndexRange(2)) {
+ const float angle = (2 * M_PI) * (edge + vert) / float(point_resolution);
+ float point[3] = {cosf(angle), sinf(angle), 0.0f};
+ point_verts_.append(
+ float3(point[(0 + axis) % 3], point[(1 + axis) % 3], point[(2 + axis) % 3]));
+ }
+ }
+ }
+};
+
+void DebugDraw::init()
+{
+ cpu_print_buf_.command.v_count = 0;
+ cpu_print_buf_.command.v_first = 0;
+ cpu_print_buf_.command.i_count = 1;
+ cpu_print_buf_.command.i_first = 0;
+
+ cpu_draw_buf_.command.v_count = 0;
+ cpu_draw_buf_.command.v_first = 0;
+ cpu_draw_buf_.command.i_count = 1;
+ cpu_draw_buf_.command.i_first = 0;
+
+ gpu_print_buf_.command.v_count = 0;
+ gpu_print_buf_.command.v_first = 0;
+ gpu_print_buf_.command.i_count = 1;
+ gpu_print_buf_.command.i_first = 0;
+ gpu_print_buf_used = false;
+
+ gpu_draw_buf_.command.v_count = 0;
+ gpu_draw_buf_.command.v_first = 0;
+ gpu_draw_buf_.command.i_count = 1;
+ gpu_draw_buf_.command.i_first = 0;
+ gpu_draw_buf_used = false;
+
+ modelmat_reset();
+}
+
+void DebugDraw::modelmat_reset()
+{
+ model_mat_ = float4x4::identity();
+}
+
+void DebugDraw::modelmat_set(const float modelmat[4][4])
+{
+ model_mat_ = modelmat;
+}
+
+GPUStorageBuf *DebugDraw::gpu_draw_buf_get()
+{
+ BLI_assert(GPU_shader_storage_buffer_objects_support());
+ if (!gpu_draw_buf_used) {
+ gpu_draw_buf_used = true;
+ gpu_draw_buf_.push_update();
+ }
+ return gpu_draw_buf_;
+}
+
+GPUStorageBuf *DebugDraw::gpu_print_buf_get()
+{
+ BLI_assert(GPU_shader_storage_buffer_objects_support());
+ if (!gpu_print_buf_used) {
+ gpu_print_buf_used = true;
+ gpu_print_buf_.push_update();
+ }
+ return gpu_print_buf_;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Draw functions
+ * \{ */
+
+void DebugDraw::draw_line(float3 v1, float3 v2, float4 color)
+{
+ draw_line(v1, v2, color_pack(color));
+}
+
+void DebugDraw::draw_polygon(Span<float3> poly_verts, float4 color)
+{
+ BLI_assert(!poly_verts.is_empty());
+
+ uint col = color_pack(color);
+ float3 v0 = model_mat_ * poly_verts.last();
+ for (auto vert : poly_verts) {
+ float3 v1 = model_mat_ * vert;
+ draw_line(v0, v1, col);
+ v0 = v1;
+ }
+}
+
+void DebugDraw::draw_matrix(const float4x4 m4)
+{
+ float3 v0 = float3(0.0f, 0.0f, 0.0f);
+ float3 v1 = float3(1.0f, 0.0f, 0.0f);
+ float3 v2 = float3(0.0f, 1.0f, 0.0f);
+ float3 v3 = float3(0.0f, 0.0f, 1.0f);
+
+ mul_project_m4_v3(m4.ptr(), v0);
+ mul_project_m4_v3(m4.ptr(), v1);
+ mul_project_m4_v3(m4.ptr(), v2);
+ mul_project_m4_v3(m4.ptr(), v3);
+
+ draw_line(v0, v1, float4(1.0f, 0.0f, 0.0f, 1.0f));
+ draw_line(v0, v2, float4(0.0f, 1.0f, 0.0f, 1.0f));
+ draw_line(v0, v3, float4(0.0f, 0.0f, 1.0f, 1.0f));
+}
+
+void DebugDraw::draw_bbox(const BoundBox &bbox, const float4 color)
+{
+ uint col = color_pack(color);
+ draw_line(bbox.vec[0], bbox.vec[1], col);
+ draw_line(bbox.vec[1], bbox.vec[2], col);
+ draw_line(bbox.vec[2], bbox.vec[3], col);
+ draw_line(bbox.vec[3], bbox.vec[0], col);
+
+ draw_line(bbox.vec[4], bbox.vec[5], col);
+ draw_line(bbox.vec[5], bbox.vec[6], col);
+ draw_line(bbox.vec[6], bbox.vec[7], col);
+ draw_line(bbox.vec[7], bbox.vec[4], col);
+
+ draw_line(bbox.vec[0], bbox.vec[4], col);
+ draw_line(bbox.vec[1], bbox.vec[5], col);
+ draw_line(bbox.vec[2], bbox.vec[6], col);
+ draw_line(bbox.vec[3], bbox.vec[7], col);
+}
+
+void DebugDraw::draw_matrix_as_bbox(float4x4 mat, const float4 color)
+{
+ BoundBox bb;
+ const float min[3] = {-1.0f, -1.0f, -1.0f}, max[3] = {1.0f, 1.0f, 1.0f};
+ BKE_boundbox_init_from_minmax(&bb, min, max);
+ for (auto i : IndexRange(8)) {
+ mul_project_m4_v3(mat.ptr(), bb.vec[i]);
+ }
+ draw_bbox(bb, color);
+}
+
+void DebugDraw::draw_sphere(const float3 center, float radius, const float4 color)
+{
+ uint col = color_pack(color);
+ for (auto i : IndexRange(sphere_verts_.size() / 2)) {
+ float3 v0 = sphere_verts_[i * 2] * radius + center;
+ float3 v1 = sphere_verts_[i * 2 + 1] * radius + center;
+ draw_line(v0, v1, col);
+ }
+}
+
+void DebugDraw::draw_point(const float3 center, float radius, const float4 color)
+{
+ uint col = color_pack(color);
+ for (auto i : IndexRange(point_verts_.size() / 2)) {
+ float3 v0 = point_verts_[i * 2] * radius + center;
+ float3 v1 = point_verts_[i * 2 + 1] * radius + center;
+ draw_line(v0, v1, col);
+ }
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Print functions
+ * \{ */
+
+template<> void DebugDraw::print_value<uint>(const uint &value)
+{
+ print_value_uint(value, false, false, true);
+}
+template<> void DebugDraw::print_value<int>(const int &value)
+{
+ print_value_uint(uint(abs(value)), false, (value < 0), false);
+}
+template<> void DebugDraw::print_value<bool>(const bool &value)
+{
+ print_string(value ? "true " : "false");
+}
+template<> void DebugDraw::print_value<float>(const float &val)
+{
+ std::stringstream ss;
+ ss << std::setw(12) << std::to_string(val);
+ print_string(ss.str());
+}
+template<> void DebugDraw::print_value<double>(const double &val)
+{
+ print_value(float(val));
+}
+
+template<> void DebugDraw::print_value_hex<uint>(const uint &value)
+{
+ print_value_uint(value, true, false, false);
+}
+template<> void DebugDraw::print_value_hex<int>(const int &value)
+{
+ print_value_uint(uint(value), true, false, false);
+}
+template<> void DebugDraw::print_value_hex<float>(const float &value)
+{
+ print_value_uint(*reinterpret_cast<const uint *>(&value), true, false, false);
+}
+template<> void DebugDraw::print_value_hex<double>(const double &val)
+{
+ print_value_hex(float(val));
+}
+
+template<> void DebugDraw::print_value_binary<uint>(const uint &value)
+{
+ print_value_binary(value);
+}
+template<> void DebugDraw::print_value_binary<int>(const int &value)
+{
+ print_value_binary(uint(value));
+}
+template<> void DebugDraw::print_value_binary<float>(const float &value)
+{
+ print_value_binary(*reinterpret_cast<const uint *>(&value));
+}
+template<> void DebugDraw::print_value_binary<double>(const double &val)
+{
+ print_value_binary(float(val));
+}
+
+template<> void DebugDraw::print_value<float2>(const float2 &value)
+{
+ print_no_endl("float2(", value[0], ", ", value[1], ")");
+}
+template<> void DebugDraw::print_value<float3>(const float3 &value)
+{
+ print_no_endl("float3(", value[0], ", ", value[1], ", ", value[1], ")");
+}
+template<> void DebugDraw::print_value<float4>(const float4 &value)
+{
+ print_no_endl("float4(", value[0], ", ", value[1], ", ", value[2], ", ", value[3], ")");
+}
+
+template<> void DebugDraw::print_value<int2>(const int2 &value)
+{
+ print_no_endl("int2(", value[0], ", ", value[1], ")");
+}
+template<> void DebugDraw::print_value<int3>(const int3 &value)
+{
+ print_no_endl("int3(", value[0], ", ", value[1], ", ", value[1], ")");
+}
+template<> void DebugDraw::print_value<int4>(const int4 &value)
+{
+ print_no_endl("int4(", value[0], ", ", value[1], ", ", value[2], ", ", value[3], ")");
+}
+
+template<> void DebugDraw::print_value<uint2>(const uint2 &value)
+{
+ print_no_endl("uint2(", value[0], ", ", value[1], ")");
+}
+template<> void DebugDraw::print_value<uint3>(const uint3 &value)
+{
+ print_no_endl("uint3(", value[0], ", ", value[1], ", ", value[1], ")");
+}
+template<> void DebugDraw::print_value<uint4>(const uint4 &value)
+{
+ print_no_endl("uint4(", value[0], ", ", value[1], ", ", value[2], ", ", value[3], ")");
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Internals
+ *
+ * IMPORTANT: All of these are copied from the shader libs (common_debug_draw_lib.glsl &
+ * common_debug_print_lib.glsl). They need to be kept in sync to write the same data.
+ * \{ */
+
+void DebugDraw::draw_line(float3 v1, float3 v2, uint color)
+{
+ DebugDrawBuf &buf = cpu_draw_buf_;
+ uint index = buf.command.v_count;
+ if (index + 2 < DRW_DEBUG_DRAW_VERT_MAX) {
+ buf.verts[index + 0] = vert_pack(model_mat_ * v1, color);
+ buf.verts[index + 1] = vert_pack(model_mat_ * v2, color);
+ buf.command.v_count += 2;
+ }
+}
+
+/* Keep in sync with drw_debug_color_pack(). */
+uint DebugDraw::color_pack(float4 color)
+{
+ color = math::clamp(color, 0.0f, 1.0f);
+ uint result = 0;
+ result |= uint(color.x * 255.0f) << 0u;
+ result |= uint(color.y * 255.0f) << 8u;
+ result |= uint(color.z * 255.0f) << 16u;
+ result |= uint(color.w * 255.0f) << 24u;
+ return result;
+}
+
+DRWDebugVert DebugDraw::vert_pack(float3 pos, uint color)
+{
+ DRWDebugVert vert;
+ vert.pos0 = *reinterpret_cast<uint32_t *>(&pos.x);
+ vert.pos1 = *reinterpret_cast<uint32_t *>(&pos.y);
+ vert.pos2 = *reinterpret_cast<uint32_t *>(&pos.z);
+ vert.color = color;
+ return vert;
+}
+
+void DebugDraw::print_newline()
+{
+ print_col_ = 0u;
+ print_row_ = ++cpu_print_buf_.command.i_first;
+}
+
+void DebugDraw::print_string_start(uint len)
+{
+ /* Break before word. */
+ if (print_col_ + len > DRW_DEBUG_PRINT_WORD_WRAP_COLUMN) {
+ print_newline();
+ }
+}
+
+/* Copied from gpu_shader_dependency. */
+void DebugDraw::print_string(std::string str)
+{
+ size_t len_before_pad = str.length();
+ /* Pad string to uint size to avoid out of bound reads. */
+ while (str.length() % 4 != 0) {
+ str += " ";
+ }
+
+ print_string_start(len_before_pad);
+ for (size_t i = 0; i < len_before_pad; i += 4) {
+ union {
+ uint8_t chars[4];
+ uint32_t word;
+ };
+
+ chars[0] = *(reinterpret_cast<const uint8_t *>(str.c_str()) + i + 0);
+ chars[1] = *(reinterpret_cast<const uint8_t *>(str.c_str()) + i + 1);
+ chars[2] = *(reinterpret_cast<const uint8_t *>(str.c_str()) + i + 2);
+ chars[3] = *(reinterpret_cast<const uint8_t *>(str.c_str()) + i + 3);
+
+ if (i + 4 > len_before_pad) {
+ chars[len_before_pad - i] = '\0';
+ }
+ print_char4(word);
+ }
+}
+
+/* Keep in sync with shader. */
+void DebugDraw::print_char4(uint data)
+{
+ /* Convert into char stream. */
+ for (; data != 0u; data >>= 8u) {
+ uint char1 = data & 0xFFu;
+ /* Check for null terminator. */
+ if (char1 == 0x00) {
+ break;
+ }
+ /* NOTE: Do not skip the header manually like in GPU. */
+ uint cursor = cpu_print_buf_.command.v_count++;
+ if (cursor < DRW_DEBUG_PRINT_MAX) {
+ /* For future usage. (i.e: Color) */
+ uint flags = 0u;
+ uint col = print_col_++;
+ uint print_header = (flags << 24u) | (print_row_ << 16u) | (col << 8u);
+ cpu_print_buf_.char_array[cursor] = print_header | char1;
+ /* Break word. */
+ if (print_col_ > DRW_DEBUG_PRINT_WORD_WRAP_COLUMN) {
+ print_newline();
+ }
+ }
+ }
+}
+
+void DebugDraw::print_append_char(uint char1, uint &char4)
+{
+ char4 = (char4 << 8u) | char1;
+}
+
+void DebugDraw::print_append_digit(uint digit, uint &char4)
+{
+ const uint char_A = 0x41u;
+ const uint char_0 = 0x30u;
+ bool is_hexadecimal = digit > 9u;
+ char4 = (char4 << 8u) | (is_hexadecimal ? (char_A + digit - 10u) : (char_0 + digit));
+}
+
+void DebugDraw::print_append_space(uint &char4)
+{
+ char4 = (char4 << 8u) | 0x20u;
+}
+
+void DebugDraw::print_value_binary(uint value)
+{
+ print_string("0b");
+ print_string_start(10u * 4u);
+ uint digits[10] = {0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u};
+ uint digit = 0u;
+ for (uint i = 0u; i < 32u; i++) {
+ print_append_digit(((value >> i) & 1u), digits[digit / 4u]);
+ digit++;
+ if ((i % 4u) == 3u) {
+ print_append_space(digits[digit / 4u]);
+ digit++;
+ }
+ }
+ /* Numbers are written from right to left. So we need to reverse the order. */
+ for (int j = 9; j >= 0; j--) {
+ print_char4(digits[j]);
+ }
+}
+
+void DebugDraw::print_value_uint(uint value,
+ const bool hex,
+ bool is_negative,
+ const bool is_unsigned)
+{
+ print_string_start(3u * 4u);
+ const uint blank_value = hex ? 0x30303030u : 0x20202020u;
+ const uint prefix = hex ? 0x78302020u : 0x20202020u;
+ uint digits[3] = {blank_value, blank_value, prefix};
+ const uint base = hex ? 16u : 10u;
+ uint digit = 0u;
+ /* Add `u` suffix. */
+ if (is_unsigned) {
+ print_append_char('u', digits[digit / 4u]);
+ digit++;
+ }
+ /* Number's digits. */
+ for (; value != 0u || digit == uint(is_unsigned); value /= base) {
+ print_append_digit(value % base, digits[digit / 4u]);
+ digit++;
+ }
+ /* Add negative sign. */
+ if (is_negative) {
+ print_append_char('-', digits[digit / 4u]);
+ digit++;
+ }
+ /* Need to pad to uint alignment because we are issuing chars in "reverse". */
+ for (uint i = digit % 4u; i < 4u && i > 0u; i++) {
+ print_append_space(digits[digit / 4u]);
+ digit++;
+ }
+ /* Numbers are written from right to left. So we need to reverse the order. */
+ for (int j = 2; j >= 0; j--) {
+ print_char4(digits[j]);
+ }
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Display
+ * \{ */
+
+void DebugDraw::display_lines()
+{
+ if (cpu_draw_buf_.command.v_count == 0 && gpu_draw_buf_used == false) {
+ return;
+ }
+ GPU_debug_group_begin("Lines");
+ cpu_draw_buf_.push_update();
+
+ float4x4 persmat;
+ const DRWView *view = DRW_view_get_active();
+ DRW_view_persmat_get(view, persmat.ptr(), false);
+
+ drw_state_set(DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS);
+
+ GPUBatch *batch = drw_cache_procedural_lines_get();
+ GPUShader *shader = DRW_shader_debug_draw_display_get();
+ GPU_batch_set_shader(batch, shader);
+ int slot = GPU_shader_get_builtin_ssbo(shader, GPU_STORAGE_BUFFER_DEBUG_VERTS);
+ GPU_shader_uniform_mat4(shader, "persmat", persmat.ptr());
+
+ if (gpu_draw_buf_used) {
+ GPU_debug_group_begin("GPU");
+ GPU_storagebuf_bind(gpu_draw_buf_, slot);
+ GPU_batch_draw_indirect(batch, gpu_draw_buf_);
+ GPU_storagebuf_unbind(gpu_draw_buf_);
+ GPU_debug_group_end();
+ }
+
+ GPU_debug_group_begin("CPU");
+ GPU_storagebuf_bind(cpu_draw_buf_, slot);
+ GPU_batch_draw_indirect(batch, cpu_draw_buf_);
+ GPU_storagebuf_unbind(cpu_draw_buf_);
+ GPU_debug_group_end();
+
+ GPU_debug_group_end();
+}
+
+void DebugDraw::display_prints()
+{
+ if (cpu_print_buf_.command.v_count == 0 && gpu_print_buf_used == false) {
+ return;
+ }
+ GPU_debug_group_begin("Prints");
+ cpu_print_buf_.push_update();
+
+ drw_state_set(DRW_STATE_WRITE_COLOR | DRW_STATE_PROGRAM_POINT_SIZE);
+
+ GPUBatch *batch = drw_cache_procedural_points_get();
+ GPUShader *shader = DRW_shader_debug_print_display_get();
+ GPU_batch_set_shader(batch, shader);
+ int slot = GPU_shader_get_builtin_ssbo(shader, GPU_STORAGE_BUFFER_DEBUG_PRINT);
+
+ if (gpu_print_buf_used) {
+ GPU_debug_group_begin("GPU");
+ GPU_storagebuf_bind(gpu_print_buf_, slot);
+ GPU_batch_draw_indirect(batch, gpu_print_buf_);
+ GPU_storagebuf_unbind(gpu_print_buf_);
+ GPU_debug_group_end();
+ }
+
+ GPU_debug_group_begin("CPU");
+ GPU_storagebuf_bind(cpu_print_buf_, slot);
+ GPU_batch_draw_indirect(batch, cpu_print_buf_);
+ GPU_storagebuf_unbind(cpu_print_buf_);
+ GPU_debug_group_end();
+
+ GPU_debug_group_end();
+}
+
+void DebugDraw::display_to_view()
+{
+ GPU_debug_group_begin("DebugDraw");
+
+ display_lines();
+ /* Print 3D shapes before text to avoid overlaps. */
+ display_prints();
+ /* Init again so we don't draw the same thing twice. */
+ init();
+
+ GPU_debug_group_end();
+}
+
+} // namespace blender::draw
+
+blender::draw::DebugDraw *DRW_debug_get()
+{
+ if (!GPU_shader_storage_buffer_objects_support()) {
+ return nullptr;
+ }
+ return reinterpret_cast<blender::draw::DebugDraw *>(DST.debug);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name C-API private
+ * \{ */
+
+void drw_debug_draw()
+{
+#ifdef DEBUG
+ if (!GPU_shader_storage_buffer_objects_support() || DST.debug == nullptr) {
+ return;
+ }
+ /* TODO(@fclem): Convenience for now. Will have to move to #DRWManager. */
+ reinterpret_cast<blender::draw::DebugDraw *>(DST.debug)->display_to_view();
+#endif
+}
+
+/**
+ * NOTE: Init is once per draw manager cycle.
+ */
+void drw_debug_init()
+{
+ /* Module should not be used in release builds. */
+ /* TODO(@fclem): Hide the functions declarations without using `ifdefs` everywhere. */
+#ifdef DEBUG
+ if (!GPU_shader_storage_buffer_objects_support()) {
+ return;
+ }
+ /* TODO(@fclem): Convenience for now. Will have to move to #DRWManager. */
+ if (DST.debug == nullptr) {
+ DST.debug = reinterpret_cast<DRWDebugModule *>(new blender::draw::DebugDraw());
+ }
+ reinterpret_cast<blender::draw::DebugDraw *>(DST.debug)->init();
+#endif
+}
+
+void drw_debug_module_free(DRWDebugModule *module)
+{
+ if (!GPU_shader_storage_buffer_objects_support()) {
+ return;
+ }
+ if (module != nullptr) {
+ delete reinterpret_cast<blender::draw::DebugDraw *>(module);
+ }
+}
+
+GPUStorageBuf *drw_debug_gpu_draw_buf_get()
+{
+ return reinterpret_cast<blender::draw::DebugDraw *>(DST.debug)->gpu_draw_buf_get();
+}
+
+GPUStorageBuf *drw_debug_gpu_print_buf_get()
+{
+ return reinterpret_cast<blender::draw::DebugDraw *>(DST.debug)->gpu_print_buf_get();
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name C-API public
+ * \{ */
+
+void DRW_debug_modelmat_reset()
+{
+ if (!GPU_shader_storage_buffer_objects_support()) {
+ return;
+ }
+ reinterpret_cast<blender::draw::DebugDraw *>(DST.debug)->modelmat_reset();
+}
+
+void DRW_debug_modelmat(const float modelmat[4][4])
+{
+ if (!GPU_shader_storage_buffer_objects_support()) {
+ return;
+ }
+ reinterpret_cast<blender::draw::DebugDraw *>(DST.debug)->modelmat_set(modelmat);
+}
+
+void DRW_debug_line_v3v3(const float v1[3], const float v2[3], const float color[4])
+{
+ if (!GPU_shader_storage_buffer_objects_support()) {
+ return;
+ }
+ reinterpret_cast<blender::draw::DebugDraw *>(DST.debug)->draw_line(v1, v2, color);
+}
+
+void DRW_debug_polygon_v3(const float (*v)[3], int vert_len, const float color[4])
+{
+ if (!GPU_shader_storage_buffer_objects_support()) {
+ return;
+ }
+ reinterpret_cast<blender::draw::DebugDraw *>(DST.debug)->draw_polygon(
+ blender::Span<float3>((float3 *)v, vert_len), color);
+}
+
+void DRW_debug_m4(const float m[4][4])
+{
+ if (!GPU_shader_storage_buffer_objects_support()) {
+ return;
+ }
+ reinterpret_cast<blender::draw::DebugDraw *>(DST.debug)->draw_matrix(m);
+}
+
+void DRW_debug_m4_as_bbox(const float m[4][4], bool invert, const float color[4])
+{
+ if (!GPU_shader_storage_buffer_objects_support()) {
+ return;
+ }
+ blender::float4x4 m4 = m;
+ if (invert) {
+ m4 = m4.inverted();
+ }
+ reinterpret_cast<blender::draw::DebugDraw *>(DST.debug)->draw_matrix_as_bbox(m4, color);
+}
+
+void DRW_debug_bbox(const BoundBox *bbox, const float color[4])
+{
+ if (!GPU_shader_storage_buffer_objects_support()) {
+ return;
+ }
+ reinterpret_cast<blender::draw::DebugDraw *>(DST.debug)->draw_bbox(*bbox, color);
+}
+
+void DRW_debug_sphere(const float center[3], float radius, const float color[4])
+{
+ if (!GPU_shader_storage_buffer_objects_support()) {
+ return;
+ }
+ reinterpret_cast<blender::draw::DebugDraw *>(DST.debug)->draw_sphere(center, radius, color);
+}
+
+/** \} */
diff --git a/source/blender/draw/intern/draw_debug.h b/source/blender/draw/intern/draw_debug.h
index 333d734edb9..9a56a12242e 100644
--- a/source/blender/draw/intern/draw_debug.h
+++ b/source/blender/draw/intern/draw_debug.h
@@ -3,21 +3,38 @@
/** \file
* \ingroup draw
+ *
+ * \brief Simple API to draw debug shapes in the viewport.
+ * IMPORTANT: This is the legacy API for C. Use draw_debug.hh instead in new C++ code.
*/
#pragma once
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct DRWDebugModule DRWDebugModule;
+
struct BoundBox;
void DRW_debug_modelmat_reset(void);
void DRW_debug_modelmat(const float modelmat[4][4]);
+/**
+ * IMPORTANT: For now there is a limit of DRW_DEBUG_DRAW_VERT_MAX that can be drawn
+ * using all the draw functions.
+ */
void DRW_debug_line_v3v3(const float v1[3], const float v2[3], const float color[4]);
void DRW_debug_polygon_v3(const float (*v)[3], int vert_len, const float color[4]);
/**
* \note g_modelmat is still applied on top.
*/
void DRW_debug_m4(const float m[4][4]);
-void DRW_debug_m4_as_bbox(const float m[4][4], const float color[4], bool invert);
+void DRW_debug_m4_as_bbox(const float m[4][4], bool invert, const float color[4]);
void DRW_debug_bbox(const BoundBox *bbox, const float color[4]);
void DRW_debug_sphere(const float center[3], float radius, const float color[4]);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/source/blender/draw/intern/draw_debug.hh b/source/blender/draw/intern/draw_debug.hh
new file mode 100644
index 00000000000..730b69ed954
--- /dev/null
+++ b/source/blender/draw/intern/draw_debug.hh
@@ -0,0 +1,198 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright 2022 Blender Foundation. */
+
+/** \file
+ * \ingroup draw
+ *
+ * \brief Simple API to draw debug shapes and log in the viewport.
+ *
+ * Both CPU and GPU implementation are supported and symmetrical (meaning GPU shader can use it
+ * too, see common_debug_print/draw_lib.glsl).
+ *
+ * NOTE: CPU logging will overlap GPU logging on screen as it is drawn after.
+ */
+
+#pragma once
+
+#include "BLI_math_vec_types.hh"
+#include "BLI_string_ref.hh"
+#include "BLI_vector.hh"
+#include "DNA_object_types.h"
+#include "DRW_gpu_wrapper.hh"
+
+namespace blender::draw {
+
+/* Shortcuts to avoid boilerplate code and match shader API. */
+#define drw_debug_line(...) DRW_debug_get()->draw_line(__VA_ARGS__)
+#define drw_debug_polygon(...) DRW_debug_get()->draw_polygon(__VA_ARGS__)
+#define drw_debug_bbox(...) DRW_debug_get()->draw_bbox(__VA_ARGS__)
+#define drw_debug_sphere(...) DRW_debug_get()->draw_sphere(__VA_ARGS__)
+#define drw_debug_point(...) DRW_debug_get()->draw_point(__VA_ARGS__)
+#define drw_debug_matrix(...) DRW_debug_get()->draw_matrix(__VA_ARGS__)
+#define drw_debug_matrix_as_bbox(...) DRW_debug_get()->draw_matrix_as_bbox(__VA_ARGS__)
+#define drw_print(...) DRW_debug_get()->print(__VA_ARGS__)
+#define drw_print_hex(...) DRW_debug_get()->print_hex(__VA_ARGS__)
+#define drw_print_binary(...) DRW_debug_get()->print_binary(__VA_ARGS__)
+#define drw_print_no_endl(...) DRW_debug_get()->print_no_endl(__VA_ARGS__)
+
+/* Will log variable along with its name, like the shader version of print(). */
+#define drw_print_id(v_) DRW_debug_get()->print(#v_, "= ", v_)
+#define drw_print_id_no_endl(v_) DRW_debug_get()->print_no_endl(#v_, "= ", v_)
+
+class DebugDraw {
+ private:
+ using DebugDrawBuf = StorageBuffer<DRWDebugDrawBuffer>;
+ using DebugPrintBuf = StorageBuffer<DRWDebugPrintBuffer>;
+
+ /** Data buffers containing all verts or chars to draw. */
+ DebugDrawBuf cpu_draw_buf_ = {"DebugDrawBuf-CPU"};
+ DebugDrawBuf gpu_draw_buf_ = {"DebugDrawBuf-GPU"};
+ DebugPrintBuf cpu_print_buf_ = {"DebugPrintBuf-CPU"};
+ DebugPrintBuf gpu_print_buf_ = {"DebugPrintBuf-GPU"};
+ /** True if the gpu buffer have been requested and may contain data to draw. */
+ bool gpu_print_buf_used = false;
+ bool gpu_draw_buf_used = false;
+ /** Matrix applied to all points before drawing. Could be a stack if needed. */
+ float4x4 model_mat_;
+ /** Precomputed shapes verts. */
+ Vector<float3> sphere_verts_;
+ Vector<float3> point_verts_;
+ /** Cursor position for print functionality. */
+ uint print_col_ = 0;
+ uint print_row_ = 0;
+
+ public:
+ DebugDraw();
+ ~DebugDraw(){};
+
+ /**
+ * Resets all buffers and reset model matrix state.
+ * Not to be called by user.
+ */
+ void init();
+
+ /**
+ * Resets model matrix state to identity.
+ */
+ void modelmat_reset();
+ /**
+ * Sets model matrix transform to apply to any vertex passed to drawing functions.
+ */
+ void modelmat_set(const float modelmat[4][4]);
+
+ /**
+ * Drawing functions that will draw wire-frames with the given color.
+ */
+ void draw_line(float3 v1, float3 v2, float4 color = {1, 0, 0, 1});
+ void draw_polygon(Span<float3> poly_verts, float4 color = {1, 0, 0, 1});
+ void draw_bbox(const BoundBox &bbox, const float4 color = {1, 0, 0, 1});
+ void draw_sphere(const float3 center, float radius, const float4 color = {1, 0, 0, 1});
+ void draw_point(const float3 center, float radius = 0.01f, const float4 color = {1, 0, 0, 1});
+ /**
+ * Draw a matrix transformation as 3 colored axes.
+ */
+ void draw_matrix(const float4x4 m4);
+ /**
+ * Draw a matrix as a 2 units length bounding box, centered on origin.
+ */
+ void draw_matrix_as_bbox(float4x4 mat, const float4 color = {1, 0, 0, 1});
+
+ /**
+ * Will draw all debug shapes and text cached up until now to the current view / frame-buffer.
+ * Draw buffers will be emptied and ready for new debug data.
+ */
+ void display_to_view();
+
+ /**
+ * Log variable or strings inside the viewport.
+ * Using a unique non string argument will print the variable name with it.
+ * Concatenate by using multiple arguments. i.e: `print("Looped ", n, "times.")`.
+ */
+ template<typename... Ts> void print(StringRefNull str, Ts... args)
+ {
+ print_no_endl(str, args...);
+ print_newline();
+ }
+ template<typename T> void print(const T &&value)
+ {
+ print_value(value);
+ print_newline();
+ }
+ template<typename T> void print_hex(const T &&value)
+ {
+ print_value_hex(value);
+ print_newline();
+ }
+ template<typename T> void print_binary(const T &&value)
+ {
+ print_value_binary(value);
+ print_newline();
+ }
+
+ /**
+ * Same as `print()` but does not finish the line.
+ */
+ void print_no_endl(std::string arg)
+ {
+ print_string(arg);
+ }
+ void print_no_endl(StringRef arg)
+ {
+ print_string(arg);
+ }
+ void print_no_endl(StringRefNull arg)
+ {
+ print_string(arg);
+ }
+ void print_no_endl(char const *arg)
+ {
+ print_string(StringRefNull(arg));
+ }
+ template<typename T> void print_no_endl(T arg)
+ {
+ print_value(arg);
+ }
+ template<typename T, typename... Ts> void print_no_endl(T arg, Ts... args)
+ {
+ print_no_endl(arg);
+ print_no_endl(args...);
+ }
+
+ /**
+ * Not to be called by user. Should become private.
+ */
+ GPUStorageBuf *gpu_draw_buf_get();
+ GPUStorageBuf *gpu_print_buf_get();
+
+ private:
+ uint color_pack(float4 color);
+ DRWDebugVert vert_pack(float3 pos, uint color);
+
+ void draw_line(float3 v1, float3 v2, uint color);
+
+ void print_newline();
+ void print_string_start(uint len);
+ void print_string(std::string str);
+ void print_char4(uint data);
+ void print_append_char(uint char1, uint &char4);
+ void print_append_digit(uint digit, uint &char4);
+ void print_append_space(uint &char4);
+ void print_value_binary(uint value);
+ void print_value_uint(uint value, const bool hex, bool is_negative, const bool is_unsigned);
+
+ template<typename T> void print_value(const T &value);
+ template<typename T> void print_value_hex(const T &value);
+ template<typename T> void print_value_binary(const T &value);
+
+ void display_lines();
+ void display_prints();
+};
+
+} // namespace blender::draw
+
+/**
+ * Ease of use function to get the debug module.
+ * TODO(fclem): Should be removed once DRWManager is no longer global.
+ * IMPORTANT: Can return nullptr if storage buffer is not supported.
+ */
+blender::draw::DebugDraw *DRW_debug_get();
diff --git a/source/blender/draw/intern/draw_manager.c b/source/blender/draw/intern/draw_manager.c
index b2422504825..4693e5f8e20 100644
--- a/source/blender/draw/intern/draw_manager.c
+++ b/source/blender/draw/intern/draw_manager.c
@@ -43,6 +43,7 @@
#include "DNA_camera_types.h"
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
+#include "DNA_userdef_types.h"
#include "DNA_world_types.h"
#include "ED_gpencil.h"
@@ -84,6 +85,7 @@
#include "draw_cache_impl.h"
#include "engines/basic/basic_engine.h"
+#include "engines/compositor/compositor_engine.h"
#include "engines/eevee/eevee_engine.h"
#include "engines/eevee_next/eevee_engine.h"
#include "engines/external/external_engine.h"
@@ -1214,6 +1216,31 @@ static void drw_engines_enable_editors(void)
}
}
+static bool is_compositor_enabled(void)
+{
+ if (!U.experimental.use_realtime_compositor) {
+ return false;
+ }
+
+ if (!(DST.draw_ctx.v3d->shading.flag & V3D_SHADING_COMPOSITOR)) {
+ return false;
+ }
+
+ if (!(DST.draw_ctx.v3d->shading.type >= OB_MATERIAL)) {
+ return false;
+ }
+
+ if (!DST.draw_ctx.scene->use_nodes) {
+ return false;
+ }
+
+ if (!DST.draw_ctx.scene->nodetree) {
+ return false;
+ }
+
+ return true;
+}
+
static void drw_engines_enable(ViewLayer *UNUSED(view_layer),
RenderEngineType *engine_type,
bool gpencil_engine_needed)
@@ -1226,6 +1253,11 @@ static void drw_engines_enable(ViewLayer *UNUSED(view_layer),
if (gpencil_engine_needed && ((drawtype >= OB_SOLID) || !use_xray)) {
use_drw_engine(&draw_engine_gpencil_type);
}
+
+ if (is_compositor_enabled()) {
+ use_drw_engine(&draw_engine_compositor_type);
+ }
+
drw_engines_enable_overlays();
#ifdef WITH_DRAW_DEBUG
@@ -1597,7 +1629,6 @@ void DRW_draw_render_loop_ex(struct Depsgraph *depsgraph,
GPUViewport *viewport,
const bContext *evil_C)
{
-
Scene *scene = DEG_get_evaluated_scene(depsgraph);
ViewLayer *view_layer = DEG_get_evaluated_view_layer(depsgraph);
RegionView3D *rv3d = region->regiondata;
@@ -2948,6 +2979,7 @@ void DRW_engines_register(void)
DRW_engine_register(&draw_engine_overlay_type);
DRW_engine_register(&draw_engine_select_type);
DRW_engine_register(&draw_engine_basic_type);
+ DRW_engine_register(&draw_engine_compositor_type);
#ifdef WITH_DRAW_DEBUG
DRW_engine_register(&draw_engine_debug_select_type);
#endif
@@ -3028,6 +3060,9 @@ void DRW_engines_free(void)
DRW_stats_free();
DRW_globals_free();
+ drw_debug_module_free(DST.debug);
+ DST.debug = NULL;
+
DRW_UBO_FREE_SAFE(G_draw.block_ubo);
DRW_UBO_FREE_SAFE(G_draw.view_ubo);
DRW_TEXTURE_FREE_SAFE(G_draw.ramp);
diff --git a/source/blender/draw/intern/draw_manager.h b/source/blender/draw/intern/draw_manager.h
index 411123f00bf..a29f2fa7507 100644
--- a/source/blender/draw/intern/draw_manager.h
+++ b/source/blender/draw/intern/draw_manager.h
@@ -501,20 +501,6 @@ typedef struct DRWCommandSmallChunk {
BLI_STATIC_ASSERT_ALIGN(DRWCommandChunk, 16);
#endif
-/* ------------- DRAW DEBUG ------------ */
-
-typedef struct DRWDebugLine {
- struct DRWDebugLine *next; /* linked list */
- float pos[2][3];
- float color[4];
-} DRWDebugLine;
-
-typedef struct DRWDebugSphere {
- struct DRWDebugSphere *next; /* linked list */
- float mat[4][4];
- float color[4];
-} DRWDebugSphere;
-
/* ------------- Memory Pools ------------ */
/* Contains memory pools information */
@@ -656,11 +642,7 @@ typedef struct DRWManager {
GPUDrawList *draw_list;
- struct {
- /* TODO(@fclem): optimize: use chunks. */
- DRWDebugLine *lines;
- DRWDebugSphere *spheres;
- } debug;
+ DRWDebugModule *debug;
} DRWManager;
extern DRWManager DST; /* TODO: get rid of this and allow multi-threaded rendering. */
@@ -675,6 +657,9 @@ void drw_state_set(DRWState state);
void drw_debug_draw(void);
void drw_debug_init(void);
+void drw_debug_module_free(DRWDebugModule *module);
+GPUStorageBuf *drw_debug_gpu_draw_buf_get(void);
+GPUStorageBuf *drw_debug_gpu_print_buf_get(void);
eDRWCommandType command_type_get(const uint64_t *command_type_bits, int index);
diff --git a/source/blender/draw/intern/draw_manager_data.c b/source/blender/draw/intern/draw_manager_data.c
index 9392efcf92b..9d4a2c58ab6 100644
--- a/source/blender/draw/intern/draw_manager_data.c
+++ b/source/blender/draw/intern/draw_manager_data.c
@@ -17,9 +17,14 @@
#include "BKE_pbvh.h"
#include "BKE_volume.h"
+/* For debug cursor position. */
+#include "WM_api.h"
+#include "wm_window.h"
+
#include "DNA_curve_types.h"
#include "DNA_mesh_types.h"
#include "DNA_meta_types.h"
+#include "DNA_screen_types.h"
#include "BLI_alloca.h"
#include "BLI_hash.h"
@@ -39,6 +44,16 @@
#include "intern/gpu_codegen.h"
+/**
+ * IMPORTANT:
+ * In order to be able to write to the same print buffer sequentially, we add a barrier to allow
+ * multiple shader calls writing to the same buffer.
+ * However, this adds explicit synchronization events which might change the rest of the
+ * application behavior and hide some bugs. If you know you are using shader debug print in only
+ * one shader pass, you can comment this out to remove the aforementioned barrier.
+ */
+#define DISABLE_DEBUG_SHADER_PRINT_BARRIER
+
/* -------------------------------------------------------------------- */
/** \name Uniform Buffer Object (DRW_uniformbuffer)
* \{ */
@@ -1510,6 +1525,27 @@ static void drw_shgroup_init(DRWShadingGroup *shgroup, GPUShader *shader)
shgroup, view_ubo_location, DRW_UNIFORM_BLOCK, G_draw.view_ubo, 0, 0, 1);
}
+#ifdef DEBUG
+ int debug_print_location = GPU_shader_get_builtin_ssbo(shader, GPU_STORAGE_BUFFER_DEBUG_PRINT);
+ if (debug_print_location != -1) {
+ GPUStorageBuf *buf = drw_debug_gpu_print_buf_get();
+ drw_shgroup_uniform_create_ex(
+ shgroup, debug_print_location, DRW_UNIFORM_STORAGE_BLOCK, buf, 0, 0, 1);
+# ifndef DISABLE_DEBUG_SHADER_PRINT_BARRIER
+ /* Add a barrier to allow multiple shader writing to the same buffer. */
+ DRW_shgroup_barrier(shgroup, GPU_BARRIER_SHADER_STORAGE);
+# endif
+ }
+
+ int debug_draw_location = GPU_shader_get_builtin_ssbo(shader, GPU_STORAGE_BUFFER_DEBUG_VERTS);
+ if (debug_draw_location != -1) {
+ GPUStorageBuf *buf = drw_debug_gpu_draw_buf_get();
+ drw_shgroup_uniform_create_ex(
+ shgroup, debug_draw_location, DRW_UNIFORM_STORAGE_BLOCK, buf, 0, 0, 1);
+ /* NOTE(fclem): No barrier as ordering is not important. */
+ }
+#endif
+
/* Not supported. */
BLI_assert(GPU_shader_get_builtin_uniform(shader, GPU_UNIFORM_MODELVIEW_INV) == -1);
BLI_assert(GPU_shader_get_builtin_uniform(shader, GPU_UNIFORM_MODELVIEW) == -1);
@@ -1986,6 +2022,13 @@ DRWView *DRW_view_create(const float viewmat[4][4],
copy_v4_fl4(view->storage.viewcamtexcofac, 1.0f, 1.0f, 0.0f, 0.0f);
+ if (DST.draw_ctx.evil_C && DST.draw_ctx.region) {
+ int region_origin[2] = {DST.draw_ctx.region->winrct.xmin, DST.draw_ctx.region->winrct.ymin};
+ struct wmWindow *win = CTX_wm_window(DST.draw_ctx.evil_C);
+ wm_cursor_position_get(win, &view->storage.mouse_pixel[0], &view->storage.mouse_pixel[1]);
+ sub_v2_v2v2_int(view->storage.mouse_pixel, view->storage.mouse_pixel, region_origin);
+ }
+
DRW_view_update(view, viewmat, winmat, culling_viewmat, culling_winmat);
return view;
diff --git a/source/blender/draw/intern/draw_shader.cc b/source/blender/draw/intern/draw_shader.cc
index 001ceb0ae8d..ecb30d54b64 100644
--- a/source/blender/draw/intern/draw_shader.cc
+++ b/source/blender/draw/intern/draw_shader.cc
@@ -24,6 +24,8 @@ extern "C" char datatoc_gpu_shader_3D_smooth_color_frag_glsl[];
static struct {
struct GPUShader *hair_refine_sh[PART_REFINE_MAX_SHADER];
+ struct GPUShader *debug_print_display_sh;
+ struct GPUShader *debug_draw_display_sh;
} e_data = {{nullptr}};
/* -------------------------------------------------------------------- */
@@ -109,6 +111,22 @@ GPUShader *DRW_shader_curves_refine_get(CurvesEvalShader type, eParticleRefineSh
return e_data.hair_refine_sh[type];
}
+GPUShader *DRW_shader_debug_print_display_get()
+{
+ if (e_data.debug_print_display_sh == nullptr) {
+ e_data.debug_print_display_sh = GPU_shader_create_from_info_name("draw_debug_print_display");
+ }
+ return e_data.debug_print_display_sh;
+}
+
+GPUShader *DRW_shader_debug_draw_display_get()
+{
+ if (e_data.debug_draw_display_sh == nullptr) {
+ e_data.debug_draw_display_sh = GPU_shader_create_from_info_name("draw_debug_draw_display");
+ }
+ return e_data.debug_draw_display_sh;
+}
+
/** \} */
void DRW_shaders_free()
@@ -116,4 +134,6 @@ void DRW_shaders_free()
for (int i = 0; i < PART_REFINE_MAX_SHADER; i++) {
DRW_SHADER_FREE_SAFE(e_data.hair_refine_sh[i]);
}
+ DRW_SHADER_FREE_SAFE(e_data.debug_print_display_sh);
+ DRW_SHADER_FREE_SAFE(e_data.debug_draw_display_sh);
}
diff --git a/source/blender/draw/intern/draw_shader.h b/source/blender/draw/intern/draw_shader.h
index 63d755cc334..dabb4b3327f 100644
--- a/source/blender/draw/intern/draw_shader.h
+++ b/source/blender/draw/intern/draw_shader.h
@@ -30,6 +30,9 @@ struct GPUShader *DRW_shader_hair_refine_get(ParticleRefineShader refinement,
struct GPUShader *DRW_shader_curves_refine_get(CurvesEvalShader type,
eParticleRefineShaderType sh_type);
+struct GPUShader *DRW_shader_debug_print_display_get(void);
+struct GPUShader *DRW_shader_debug_draw_display_get(void);
+
void DRW_shaders_free(void);
#ifdef __cplusplus
diff --git a/source/blender/draw/intern/draw_shader_shared.h b/source/blender/draw/intern/draw_shader_shared.h
index fbcc78e52f9..90a6475c42b 100644
--- a/source/blender/draw/intern/draw_shader_shared.h
+++ b/source/blender/draw/intern/draw_shader_shared.h
@@ -14,6 +14,9 @@ typedef struct CurvesInfos CurvesInfos;
typedef struct DrawCommand DrawCommand;
typedef struct DrawCommandIndexed DrawCommandIndexed;
typedef struct DispatchCommand DispatchCommand;
+typedef struct DRWDebugPrintBuffer DRWDebugPrintBuffer;
+typedef struct DRWDebugVert DRWDebugVert;
+typedef struct DRWDebugDrawBuffer DRWDebugDrawBuffer;
#endif
#define DRW_SHADER_SHARED_H
@@ -48,6 +51,12 @@ struct ViewInfos {
/** NOTE: vec3 arrays are padded to vec4. */
float4 frustum_corners[8];
float4 frustum_planes[6];
+
+ /** For debugging purpose */
+ /* Mouse pixel. */
+ int2 mouse_pixel;
+
+ int2 _pad0;
};
BLI_STATIC_ASSERT_ALIGN(ViewInfos, 16)
@@ -131,3 +140,59 @@ struct DispatchCommand {
uint _pad0;
};
BLI_STATIC_ASSERT_ALIGN(DispatchCommand, 16)
+
+/* -------------------------------------------------------------------- */
+/** \name Debug print
+ * \{ */
+
+/* Take the header (DrawCommand) into account. */
+#define DRW_DEBUG_PRINT_MAX (8 * 1024) - 4
+/* NOTE: Cannot be more than 255 (because of column encoding). */
+#define DRW_DEBUG_PRINT_WORD_WRAP_COLUMN 120u
+
+/* The debug print buffer is laid-out as the following struct.
+ * But we use plain array in shader code instead because of driver issues. */
+struct DRWDebugPrintBuffer {
+ DrawCommand command;
+ /** Each character is encoded as 3 `uchar` with char_index, row and column position. */
+ uint char_array[DRW_DEBUG_PRINT_MAX];
+};
+BLI_STATIC_ASSERT_ALIGN(DRWDebugPrintBuffer, 16)
+
+/* Use number of char as vertex count. Equivalent to `DRWDebugPrintBuffer.command.v_count`. */
+#define drw_debug_print_cursor drw_debug_print_buf[0]
+/* Reuse first instance as row index as we don't use instancing. Equivalent to
+ * `DRWDebugPrintBuffer.command.i_first`. */
+#define drw_debug_print_row_shared drw_debug_print_buf[3]
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Debug draw shapes
+ * \{ */
+
+struct DRWDebugVert {
+ /* This is a weird layout, but needed to be able to use DRWDebugVert as
+ * a DrawCommand and avoid alignment issues. See drw_debug_verts_buf[] definition. */
+ uint pos0;
+ uint pos1;
+ uint pos2;
+ uint color;
+};
+BLI_STATIC_ASSERT_ALIGN(DRWDebugVert, 16)
+
+/* Take the header (DrawCommand) into account. */
+#define DRW_DEBUG_DRAW_VERT_MAX (64 * 1024) - 1
+
+/* The debug draw buffer is laid-out as the following struct.
+ * But we use plain array in shader code instead because of driver issues. */
+struct DRWDebugDrawBuffer {
+ DrawCommand command;
+ DRWDebugVert verts[DRW_DEBUG_DRAW_VERT_MAX];
+};
+BLI_STATIC_ASSERT_ALIGN(DRWDebugPrintBuffer, 16)
+
+/* Equivalent to `DRWDebugDrawBuffer.command.v_count`. */
+#define drw_debug_draw_v_count drw_debug_verts_buf[0].pos0
+
+/** \} */
diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh.hh b/source/blender/draw/intern/mesh_extractors/extract_mesh.hh
index a4773736f0c..57abf088c16 100644
--- a/source/blender/draw/intern/mesh_extractors/extract_mesh.hh
+++ b/source/blender/draw/intern/mesh_extractors/extract_mesh.hh
@@ -83,6 +83,9 @@ struct MeshRenderData {
MLoopTri *mlooptri;
const float (*vert_normals)[3];
const float (*poly_normals)[3];
+ const bool *hide_vert;
+ const bool *hide_edge;
+ const bool *hide_poly;
float (*loop_normals)[3];
int *lverts, *ledges;
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 2ff093d0bd8..9f82cc56941 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
@@ -61,7 +61,7 @@ static void extract_edituv_tris_iter_looptri_mesh(const MeshRenderData *mr,
MeshExtract_EditUvElem_Data *data = static_cast<MeshExtract_EditUvElem_Data *>(_data);
const MPoly *mp = &mr->mpoly[mlt->poly];
edituv_tri_add(data,
- (mp->flag & ME_HIDE) != 0,
+ mr->hide_poly && mr->hide_poly[mlt->poly],
(mp->flag & ME_FACE_SEL) != 0,
mlt->tri[0],
mlt->tri[1],
@@ -117,7 +117,7 @@ static void extract_edituv_tris_iter_subdiv_bm(const DRWSubdivCache *UNUSED(subd
}
static void extract_edituv_tris_iter_subdiv_mesh(const DRWSubdivCache *UNUSED(subdiv_cache),
- const MeshRenderData *UNUSED(mr),
+ const MeshRenderData *mr,
void *_data,
uint subdiv_quad_index,
const MPoly *coarse_quad)
@@ -125,19 +125,13 @@ static void extract_edituv_tris_iter_subdiv_mesh(const DRWSubdivCache *UNUSED(su
MeshExtract_EditUvElem_Data *data = static_cast<MeshExtract_EditUvElem_Data *>(_data);
const uint loop_idx = subdiv_quad_index * 4;
- edituv_tri_add(data,
- (coarse_quad->flag & ME_HIDE) != 0,
- (coarse_quad->flag & ME_FACE_SEL) != 0,
- loop_idx,
- loop_idx + 1,
- loop_idx + 2);
+ const bool hidden = mr->hide_poly && mr->hide_poly[coarse_quad - mr->mpoly];
- edituv_tri_add(data,
- (coarse_quad->flag & ME_HIDE) != 0,
- (coarse_quad->flag & ME_FACE_SEL) != 0,
- loop_idx,
- loop_idx + 2,
- loop_idx + 3);
+ edituv_tri_add(
+ data, hidden, (coarse_quad->flag & ME_FACE_SEL) != 0, loop_idx, loop_idx + 1, loop_idx + 2);
+
+ edituv_tri_add(
+ data, hidden, (coarse_quad->flag & ME_FACE_SEL) != 0, loop_idx, loop_idx + 2, loop_idx + 3);
}
static void extract_edituv_tris_finish_subdiv(const struct DRWSubdivCache *UNUSED(subdiv_cache),
@@ -218,6 +212,8 @@ static void extract_edituv_lines_iter_poly_mesh(const MeshRenderData *mr,
void *_data)
{
MeshExtract_EditUvElem_Data *data = static_cast<MeshExtract_EditUvElem_Data *>(_data);
+ const bool hidden = mr->hide_poly && mr->hide_poly[mp - mr->mpoly];
+
const MLoop *mloop = mr->mloop;
const int ml_index_end = mp->loopstart + mp->totloop;
for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) {
@@ -227,11 +223,8 @@ static void extract_edituv_lines_iter_poly_mesh(const MeshRenderData *mr,
const int ml_index_next = (ml_index == ml_index_last) ? mp->loopstart : (ml_index + 1);
const bool real_edge = (mr->e_origindex == nullptr ||
mr->e_origindex[ml->e] != ORIGINDEX_NONE);
- edituv_edge_add(data,
- (mp->flag & ME_HIDE) != 0 || !real_edge,
- (mp->flag & ME_FACE_SEL) != 0,
- ml_index,
- ml_index_next);
+ edituv_edge_add(
+ data, hidden || !real_edge, (mp->flag & ME_FACE_SEL) != 0, ml_index, ml_index_next);
}
}
@@ -288,6 +281,8 @@ static void extract_edituv_lines_iter_subdiv_mesh(const DRWSubdivCache *subdiv_c
const MPoly *coarse_poly)
{
MeshExtract_EditUvElem_Data *data = static_cast<MeshExtract_EditUvElem_Data *>(_data);
+ const bool hidden = mr->hide_poly && mr->hide_poly[coarse_poly - mr->mpoly];
+
int *subdiv_loop_edge_index = (int *)GPU_vertbuf_get_data(subdiv_cache->edges_orig_index);
uint start_loop_idx = subdiv_quad_index * 4;
@@ -298,7 +293,7 @@ static void extract_edituv_lines_iter_subdiv_mesh(const DRWSubdivCache *subdiv_c
(mr->e_origindex == nullptr ||
mr->e_origindex[edge_origindex] != ORIGINDEX_NONE));
edituv_edge_add(data,
- (coarse_poly->flag & ME_HIDE) != 0 || !real_edge,
+ hidden || !real_edge,
(coarse_poly->flag & ME_FACE_SEL) != 0,
loop_idx,
(loop_idx + 1 == end_loop_idx) ? start_loop_idx : (loop_idx + 1));
@@ -382,14 +377,15 @@ static void extract_edituv_points_iter_poly_mesh(const MeshRenderData *mr,
void *_data)
{
MeshExtract_EditUvElem_Data *data = static_cast<MeshExtract_EditUvElem_Data *>(_data);
+ const bool hidden = mr->hide_poly && mr->hide_poly[mp - mr->mpoly];
+
const MLoop *mloop = mr->mloop;
const int ml_index_end = mp->loopstart + mp->totloop;
for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) {
const MLoop *ml = &mloop[ml_index];
const bool real_vert = !mr->v_origindex || mr->v_origindex[ml->v] != ORIGINDEX_NONE;
- edituv_point_add(
- data, ((mp->flag & ME_HIDE) != 0) || !real_vert, (mp->flag & ME_FACE_SEL) != 0, ml_index);
+ edituv_point_add(data, hidden || !real_vert, (mp->flag & ME_FACE_SEL) != 0, ml_index);
}
}
@@ -442,6 +438,7 @@ static void extract_edituv_points_iter_subdiv_mesh(const DRWSubdivCache *subdiv_
const MPoly *coarse_quad)
{
MeshExtract_EditUvElem_Data *data = static_cast<MeshExtract_EditUvElem_Data *>(_data);
+ const bool hidden = mr->hide_poly && mr->hide_poly[coarse_quad - mr->mpoly];
int *subdiv_loop_vert_index = (int *)GPU_vertbuf_get_data(subdiv_cache->verts_orig_index);
uint start_loop_idx = subdiv_quad_index * 4;
@@ -450,10 +447,7 @@ static void extract_edituv_points_iter_subdiv_mesh(const DRWSubdivCache *subdiv_
const int vert_origindex = subdiv_loop_vert_index[i];
const bool real_vert = !mr->v_origindex || (vert_origindex != -1 &&
mr->v_origindex[vert_origindex] != ORIGINDEX_NONE);
- edituv_point_add(data,
- ((coarse_quad->flag & ME_HIDE) != 0) || !real_vert,
- (coarse_quad->flag & ME_FACE_SEL) != 0,
- i);
+ edituv_point_add(data, hidden || !real_vert, (coarse_quad->flag & ME_FACE_SEL) != 0, i);
}
}
@@ -533,6 +527,8 @@ static void extract_edituv_fdots_iter_poly_mesh(const MeshRenderData *mr,
void *_data)
{
MeshExtract_EditUvElem_Data *data = static_cast<MeshExtract_EditUvElem_Data *>(_data);
+ const bool hidden = mr->hide_poly && mr->hide_poly[mp - mr->mpoly];
+
if (mr->use_subsurf_fdots) {
const BLI_bitmap *facedot_tags = mr->me->runtime.subsurf_face_dot_tags;
@@ -543,16 +539,13 @@ static void extract_edituv_fdots_iter_poly_mesh(const MeshRenderData *mr,
const bool real_fdot = !mr->p_origindex || (mr->p_origindex[mp_index] != ORIGINDEX_NONE);
const bool subd_fdot = BLI_BITMAP_TEST(facedot_tags, ml->v);
- edituv_facedot_add(data,
- ((mp->flag & ME_HIDE) != 0) || !real_fdot || !subd_fdot,
- (mp->flag & ME_FACE_SEL) != 0,
- mp_index);
+ edituv_facedot_add(
+ data, hidden || !real_fdot || !subd_fdot, (mp->flag & ME_FACE_SEL) != 0, mp_index);
}
}
else {
const bool real_fdot = !mr->p_origindex || (mr->p_origindex[mp_index] != ORIGINDEX_NONE);
- edituv_facedot_add(
- data, ((mp->flag & ME_HIDE) != 0) || !real_fdot, (mp->flag & ME_FACE_SEL) != 0, mp_index);
+ edituv_facedot_add(data, hidden || !real_fdot, (mp->flag & ME_FACE_SEL) != 0, mp_index);
}
}
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 cc0b383f12b..8dc00617039 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
@@ -42,6 +42,8 @@ static void extract_fdots_iter_poly_mesh(const MeshRenderData *mr,
const int mp_index,
void *_userdata)
{
+ const bool hidden = mr->use_hide && mr->hide_poly && mr->hide_poly[mp - mr->mpoly];
+
GPUIndexBufBuilder *elb = static_cast<GPUIndexBufBuilder *>(_userdata);
if (mr->use_subsurf_fdots) {
const BLI_bitmap *facedot_tags = mr->me->runtime.subsurf_face_dot_tags;
@@ -50,7 +52,7 @@ static void extract_fdots_iter_poly_mesh(const MeshRenderData *mr,
const int ml_index_end = mp->loopstart + mp->totloop;
for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) {
const MLoop *ml = &mloop[ml_index];
- if (BLI_BITMAP_TEST(facedot_tags, ml->v) && !(mr->use_hide && (mp->flag & ME_HIDE))) {
+ if (BLI_BITMAP_TEST(facedot_tags, ml->v) && !hidden) {
GPU_indexbuf_set_point_vert(elb, mp_index, mp_index);
return;
}
@@ -58,7 +60,7 @@ static void extract_fdots_iter_poly_mesh(const MeshRenderData *mr,
GPU_indexbuf_set_point_restart(elb, mp_index);
}
else {
- if (!(mr->use_hide && (mp->flag & ME_HIDE))) {
+ if (!hidden) {
GPU_indexbuf_set_point_vert(elb, mp_index, mp_index);
}
else {
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 e6c0d815963..fe883fb0c96 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
@@ -58,14 +58,12 @@ static void extract_lines_iter_poly_mesh(const MeshRenderData *mr,
GPUIndexBufBuilder *elb = static_cast<GPUIndexBufBuilder *>(data);
/* Using poly & loop iterator would complicate accessing the adjacent loop. */
const MLoop *mloop = mr->mloop;
- const MEdge *medge = mr->medge;
if (mr->use_hide || (mr->extract_type == MR_EXTRACT_MAPPED) || (mr->e_origindex != nullptr)) {
const int ml_index_last = mp->loopstart + (mp->totloop - 1);
int ml_index = ml_index_last, ml_index_next = mp->loopstart;
do {
const MLoop *ml = &mloop[ml_index];
- const MEdge *med = &medge[ml->e];
- if (!((mr->use_hide && (med->flag & ME_HIDE)) ||
+ if (!((mr->use_hide && mr->hide_edge && mr->hide_edge[ml->e]) ||
((mr->extract_type == MR_EXTRACT_MAPPED) && (mr->e_origindex) &&
(mr->e_origindex[ml->e] == ORIGINDEX_NONE)))) {
GPU_indexbuf_set_line_verts(elb, ml->e, ml_index, ml_index_next);
@@ -111,7 +109,7 @@ static void extract_lines_iter_ledge_mesh(const MeshRenderData *mr,
GPUIndexBufBuilder *elb = static_cast<GPUIndexBufBuilder *>(data);
const int l_index_offset = mr->edge_len + ledge_index;
const int e_index = mr->ledges[ledge_index];
- if (!((mr->use_hide && (med->flag & ME_HIDE)) ||
+ if (!((mr->use_hide && mr->hide_edge && mr->hide_edge[med - mr->medge]) ||
((mr->extract_type == MR_EXTRACT_MAPPED) && (mr->e_origindex) &&
(mr->e_origindex[e_index] == ORIGINDEX_NONE)))) {
const int l_index = mr->loop_len + ledge_index * 2;
@@ -185,9 +183,14 @@ static void extract_lines_loose_geom_subdiv(const DRWSubdivCache *subdiv_cache,
switch (mr->extract_type) {
case MR_EXTRACT_MESH: {
- const MEdge *medge = mr->medge;
- for (DRWSubdivLooseEdge edge : loose_edges) {
- *flags_data++ = (medge[edge.coarse_edge_index].flag & ME_HIDE) != 0;
+ const bool *hide_vert = mr->hide_vert;
+ if (hide_vert) {
+ for (DRWSubdivLooseEdge edge : loose_edges) {
+ *flags_data++ = hide_vert[edge.coarse_edge_index];
+ }
+ }
+ else {
+ MutableSpan<uint>(flags_data, loose_edges.size()).fill(0);
}
break;
}
@@ -199,18 +202,23 @@ static void extract_lines_loose_geom_subdiv(const DRWSubdivCache *subdiv_cache,
}
}
else {
- for (DRWSubdivLooseEdge edge : loose_edges) {
- int e = edge.coarse_edge_index;
-
- if (mr->e_origindex && mr->e_origindex[e] != ORIGINDEX_NONE) {
- *flags_data++ = (mr->medge[mr->e_origindex[e]].flag & ME_HIDE) != 0;
- }
- else {
- *flags_data++ = false;
+ const bool *hide_vert = mr->hide_vert;
+ if (hide_vert) {
+ for (DRWSubdivLooseEdge edge : loose_edges) {
+ int e = edge.coarse_edge_index;
+
+ if (mr->e_origindex && mr->e_origindex[e] != ORIGINDEX_NONE) {
+ *flags_data++ = hide_vert[edge.coarse_edge_index];
+ }
+ else {
+ *flags_data++ = false;
+ }
}
}
+ else {
+ MutableSpan<uint>(flags_data, loose_edges.size()).fill(0);
+ }
}
-
break;
}
case MR_EXTRACT_BMESH: {
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 c2cfb66ec28..d6c246c51a9 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
@@ -119,16 +119,17 @@ static void extract_lines_adjacency_iter_looptri_mesh(const MeshRenderData *mr,
void *_data)
{
MeshExtract_LineAdjacency_Data *data = static_cast<MeshExtract_LineAdjacency_Data *>(_data);
- const MPoly *mp = &mr->mpoly[mlt->poly];
- if (!(mr->use_hide && (mp->flag & ME_HIDE))) {
- lines_adjacency_triangle(mr->mloop[mlt->tri[0]].v,
- mr->mloop[mlt->tri[1]].v,
- mr->mloop[mlt->tri[2]].v,
- mlt->tri[0],
- mlt->tri[1],
- mlt->tri[2],
- data);
+ const bool hidden = mr->use_hide && mr->hide_poly && mr->hide_poly[mlt->poly];
+ if (hidden) {
+ return;
}
+ lines_adjacency_triangle(mr->mloop[mlt->tri[0]].v,
+ mr->mloop[mlt->tri[1]].v,
+ mr->mloop[mlt->tri[2]].v,
+ mlt->tri[0],
+ mlt->tri[1],
+ mlt->tri[2],
+ data);
}
static void extract_lines_adjacency_finish(const MeshRenderData *UNUSED(mr),
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 11c71d61775..d5f31c08eaf 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
@@ -47,8 +47,7 @@ static void extract_lines_paint_mask_iter_poly_mesh(const MeshRenderData *mr,
const MLoop *ml = &mloop[ml_index];
const int e_index = ml->e;
- const MEdge *me = &mr->medge[e_index];
- if (!((mr->use_hide && (me->flag & ME_HIDE)) ||
+ if (!((mr->use_hide && mr->hide_edge && mr->hide_edge[e_index]) ||
((mr->extract_type == MR_EXTRACT_MAPPED) && (mr->e_origindex) &&
(mr->e_origindex[e_index] == ORIGINDEX_NONE)))) {
@@ -122,8 +121,7 @@ static void extract_lines_paint_mask_iter_subdiv_mesh(const DRWSubdivCache *subd
GPU_indexbuf_set_line_restart(&data->elb, subdiv_edge_index);
}
else {
- const MEdge *me = &mr->medge[coarse_edge_index];
- if (!((mr->use_hide && (me->flag & ME_HIDE)) ||
+ if (!((mr->use_hide && mr->hide_edge && mr->hide_edge[coarse_edge_index]) ||
((mr->extract_type == MR_EXTRACT_MAPPED) && (mr->e_origindex) &&
(mr->e_origindex[coarse_edge_index] == ORIGINDEX_NONE)))) {
const uint ml_index_other = (loop_idx == (end_loop_idx - 1)) ? start_loop_idx :
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 f7c5505422b..ca46a38823d 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
@@ -43,10 +43,10 @@ BLI_INLINE void vert_set_mesh(GPUIndexBufBuilder *elb,
const int v_index,
const int l_index)
{
- const MVert *mv = &mr->mvert[v_index];
- if (!((mr->use_hide && (mv->flag & ME_HIDE)) ||
- ((mr->extract_type == MR_EXTRACT_MAPPED) && (mr->v_origindex) &&
- (mr->v_origindex[v_index] == ORIGINDEX_NONE)))) {
+ const bool hidden = mr->use_hide && mr->hide_vert && mr->hide_vert[v_index];
+
+ if (!(hidden || ((mr->extract_type == MR_EXTRACT_MAPPED) && (mr->v_origindex) &&
+ (mr->v_origindex[v_index] == ORIGINDEX_NONE)))) {
GPU_indexbuf_set_point_vert(elb, v_index, l_index);
}
else {
@@ -181,8 +181,7 @@ static void extract_points_iter_subdiv_common(GPUIndexBufBuilder *elb,
}
}
else {
- const MVert *mv = &mr->mvert[coarse_vertex_index];
- if (mr->use_hide && (mv->flag & ME_HIDE)) {
+ if (mr->use_hide && mr->hide_vert && mr->hide_vert[coarse_vertex_index]) {
GPU_indexbuf_set_point_restart(elb, coarse_vertex_index);
continue;
}
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 9fc18620d11..2e3e6c7b6b1 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
@@ -189,12 +189,12 @@ static void extract_tris_single_mat_iter_looptri_mesh(const MeshRenderData *mr,
void *_data)
{
GPUIndexBufBuilder *elb = static_cast<GPUIndexBufBuilder *>(_data);
- const MPoly *mp = &mr->mpoly[mlt->poly];
- if (!(mr->use_hide && (mp->flag & ME_HIDE))) {
- GPU_indexbuf_set_tri_verts(elb, mlt_index, mlt->tri[0], mlt->tri[1], mlt->tri[2]);
+ const bool hidden = mr->use_hide && mr->hide_poly && mr->hide_poly[mlt->poly];
+ if (hidden) {
+ GPU_indexbuf_set_tri_restart(elb, mlt_index);
}
else {
- GPU_indexbuf_set_tri_restart(elb, mlt_index);
+ GPU_indexbuf_set_tri_verts(elb, mlt_index, mlt->tri[0], mlt->tri[1], mlt->tri[2]);
}
}
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 ac517269e7d..ef67e1b540d 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
@@ -62,6 +62,8 @@ static void extract_lnor_iter_poly_mesh(const MeshRenderData *mr,
const int mp_index,
void *data)
{
+ const bool hidden = mr->hide_poly && mr->hide_poly[mp_index];
+
const MLoop *mloop = mr->mloop;
const int ml_index_end = mp->loopstart + mp->totloop;
for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) {
@@ -80,8 +82,8 @@ static void extract_lnor_iter_poly_mesh(const MeshRenderData *mr,
/* Flag for paint mode overlay.
* Only use MR_EXTRACT_MAPPED in edit mode where it is used to display the edge-normals.
* In paint mode it will use the un-mapped data to draw the wire-frame. */
- if (mp->flag & ME_HIDE || (mr->edit_bmesh && mr->extract_type == MR_EXTRACT_MAPPED &&
- (mr->v_origindex) && mr->v_origindex[ml->v] == ORIGINDEX_NONE)) {
+ if (hidden || (mr->edit_bmesh && mr->extract_type == MR_EXTRACT_MAPPED && (mr->v_origindex) &&
+ mr->v_origindex[ml->v] == ORIGINDEX_NONE)) {
lnor_data->w = -1;
}
else if (mp->flag & ME_FACE_SEL) {
@@ -185,6 +187,8 @@ static void extract_lnor_hq_iter_poly_mesh(const MeshRenderData *mr,
const int mp_index,
void *data)
{
+ const bool hidden = mr->hide_poly && mr->hide_poly[mp_index];
+
const MLoop *mloop = mr->mloop;
const int ml_index_end = mp->loopstart + mp->totloop;
for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) {
@@ -203,8 +207,8 @@ static void extract_lnor_hq_iter_poly_mesh(const MeshRenderData *mr,
/* Flag for paint mode overlay.
* Only use #MR_EXTRACT_MAPPED in edit mode where it is used to display the edge-normals.
* In paint mode it will use the un-mapped data to draw the wire-frame. */
- if (mp->flag & ME_HIDE || (mr->edit_bmesh && mr->extract_type == MR_EXTRACT_MAPPED &&
- (mr->v_origindex) && mr->v_origindex[ml->v] == ORIGINDEX_NONE)) {
+ if (hidden || (mr->edit_bmesh && mr->extract_type == MR_EXTRACT_MAPPED && (mr->v_origindex) &&
+ mr->v_origindex[ml->v] == ORIGINDEX_NONE)) {
lnor_data->w = -1;
}
else if (mp->flag & ME_FACE_SEL) {
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 9788beabeb5..313838be9e8 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
@@ -83,10 +83,11 @@ static void extract_pos_nor_iter_poly_bm(const MeshRenderData *mr,
static void extract_pos_nor_iter_poly_mesh(const MeshRenderData *mr,
const MPoly *mp,
- const int UNUSED(mp_index),
+ const int mp_index,
void *_data)
{
MeshExtract_PosNor_Data *data = static_cast<MeshExtract_PosNor_Data *>(_data);
+ const bool poly_hidden = mr->hide_poly && mr->hide_poly[mp_index];
const MLoop *mloop = mr->mloop;
const int ml_index_end = mp->loopstart + mp->totloop;
@@ -95,10 +96,11 @@ static void extract_pos_nor_iter_poly_mesh(const MeshRenderData *mr,
PosNorLoop *vert = &data->vbo_data[ml_index];
const MVert *mv = &mr->mvert[ml->v];
+ const bool vert_hidden = mr->hide_vert && mr->hide_vert[ml->v];
copy_v3_v3(vert->pos, mv->co);
vert->nor = data->normals[ml->v].low;
/* Flag for paint mode overlay. */
- if (mp->flag & ME_HIDE || mv->flag & ME_HIDE ||
+ if (poly_hidden || vert_hidden ||
((mr->extract_type == MR_EXTRACT_MAPPED) && (mr->v_origindex) &&
(mr->v_origindex[ml->v] == ORIGINDEX_NONE))) {
vert->nor.w = -1;
@@ -432,18 +434,21 @@ static void extract_pos_nor_hq_iter_poly_mesh(const MeshRenderData *mr,
void *_data)
{
MeshExtract_PosNorHQ_Data *data = static_cast<MeshExtract_PosNorHQ_Data *>(_data);
+ const bool poly_hidden = mr->hide_poly && mr->hide_poly[mp - mr->mpoly];
+
const MLoop *mloop = mr->mloop;
const int ml_index_end = mp->loopstart + mp->totloop;
for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) {
const MLoop *ml = &mloop[ml_index];
+ const bool vert_hidden = mr->hide_vert && mr->hide_vert[ml->v];
PosNorHQLoop *vert = &data->vbo_data[ml_index];
const MVert *mv = &mr->mvert[ml->v];
copy_v3_v3(vert->pos, mv->co);
copy_v3_v3_short(vert->nor, data->normals[ml->v].high);
/* Flag for paint mode overlay. */
- if (mp->flag & ME_HIDE || mv->flag & ME_HIDE ||
+ if (poly_hidden || vert_hidden ||
((mr->extract_type == MR_EXTRACT_MAPPED) && (mr->v_origindex) &&
(mr->v_origindex[ml->v] == ORIGINDEX_NONE))) {
vert->nor[3] = -1;
diff --git a/source/blender/draw/intern/shaders/common_debug_draw_lib.glsl b/source/blender/draw/intern/shaders/common_debug_draw_lib.glsl
new file mode 100644
index 00000000000..7a79f957462
--- /dev/null
+++ b/source/blender/draw/intern/shaders/common_debug_draw_lib.glsl
@@ -0,0 +1,216 @@
+
+/**
+ * Debugging drawing library
+ *
+ * Quick way to draw debug geometry. All input should be in world space and
+ * will be rendered in the default view. No additional setup required.
+ **/
+
+/** Global switch option. */
+bool drw_debug_draw_enable = true;
+const vec4 drw_debug_default_color = vec4(1.0, 0.0, 0.0, 1.0);
+
+/* -------------------------------------------------------------------- */
+/** \name Interals
+ * \{ */
+
+uint drw_debug_start_draw(uint v_needed)
+{
+ uint vertid = atomicAdd(drw_debug_draw_v_count, v_needed);
+ /* NOTE: Skip the header manually. */
+ vertid += 1;
+ return vertid;
+}
+
+uint drw_debug_color_pack(vec4 color)
+{
+ color = clamp(color, 0.0, 1.0);
+ uint result = 0;
+ result |= uint(color.x * 255.0) << 0u;
+ result |= uint(color.y * 255.0) << 8u;
+ result |= uint(color.z * 255.0) << 16u;
+ result |= uint(color.w * 255.0) << 24u;
+ return result;
+}
+
+void drw_debug_line(inout uint vertid, vec3 v1, vec3 v2, uint color)
+{
+ drw_debug_verts_buf[vertid++] = DRWDebugVert(
+ floatBitsToUint(v1.x), floatBitsToUint(v1.y), floatBitsToUint(v1.z), color);
+ drw_debug_verts_buf[vertid++] = DRWDebugVert(
+ floatBitsToUint(v2.x), floatBitsToUint(v2.y), floatBitsToUint(v2.z), color);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name API
+ * \{ */
+
+/**
+ * Draw a line.
+ */
+void drw_debug_line(vec3 v1, vec3 v2, vec4 color)
+{
+ if (!drw_debug_draw_enable) {
+ return;
+ }
+ const uint v_needed = 2;
+ uint vertid = drw_debug_start_draw(v_needed);
+ if (vertid + v_needed < DRW_DEBUG_DRAW_VERT_MAX) {
+ drw_debug_line(vertid, v1, v2, drw_debug_color_pack(color));
+ }
+}
+void drw_debug_line(vec3 v1, vec3 v2)
+{
+ drw_debug_line(v1, v2, drw_debug_default_color);
+}
+
+/**
+ * Draw a quad contour.
+ */
+void drw_debug_quad(vec3 v1, vec3 v2, vec3 v3, vec3 v4, vec4 color)
+{
+ if (!drw_debug_draw_enable) {
+ return;
+ }
+ const uint v_needed = 8;
+ uint vertid = drw_debug_start_draw(v_needed);
+ if (vertid + v_needed < DRW_DEBUG_DRAW_VERT_MAX) {
+ uint pcolor = drw_debug_color_pack(color);
+ drw_debug_line(vertid, v1, v2, pcolor);
+ drw_debug_line(vertid, v2, v3, pcolor);
+ drw_debug_line(vertid, v3, v4, pcolor);
+ drw_debug_line(vertid, v4, v1, pcolor);
+ }
+}
+void drw_debug_quad(vec3 v1, vec3 v2, vec3 v3, vec3 v4)
+{
+ drw_debug_quad(v1, v2, v3, v4, drw_debug_default_color);
+}
+
+/**
+ * Draw a point as octahedron wireframe.
+ */
+void drw_debug_point(vec3 p, float radius, vec4 color)
+{
+ if (!drw_debug_draw_enable) {
+ return;
+ }
+ vec3 c = vec3(radius, -radius, 0);
+ vec3 v1 = p + c.xzz;
+ vec3 v2 = p + c.zxz;
+ vec3 v3 = p + c.yzz;
+ vec3 v4 = p + c.zyz;
+ vec3 v5 = p + c.zzx;
+ vec3 v6 = p + c.zzy;
+
+ const uint v_needed = 12 * 2;
+ uint vertid = drw_debug_start_draw(v_needed);
+ if (vertid + v_needed < DRW_DEBUG_DRAW_VERT_MAX) {
+ uint pcolor = drw_debug_color_pack(color);
+ drw_debug_line(vertid, v1, v2, pcolor);
+ drw_debug_line(vertid, v2, v3, pcolor);
+ drw_debug_line(vertid, v3, v4, pcolor);
+ drw_debug_line(vertid, v4, v1, pcolor);
+ drw_debug_line(vertid, v1, v5, pcolor);
+ drw_debug_line(vertid, v2, v5, pcolor);
+ drw_debug_line(vertid, v3, v5, pcolor);
+ drw_debug_line(vertid, v4, v5, pcolor);
+ drw_debug_line(vertid, v1, v6, pcolor);
+ drw_debug_line(vertid, v2, v6, pcolor);
+ drw_debug_line(vertid, v3, v6, pcolor);
+ drw_debug_line(vertid, v4, v6, pcolor);
+ }
+}
+void drw_debug_point(vec3 p, float radius)
+{
+ drw_debug_point(p, radius, drw_debug_default_color);
+}
+void drw_debug_point(vec3 p)
+{
+ drw_debug_point(p, 0.01);
+}
+
+/**
+ * Draw a sphere wireframe as 3 axes circle.
+ */
+void drw_debug_sphere(vec3 p, float radius, vec4 color)
+{
+ if (!drw_debug_draw_enable) {
+ return;
+ }
+ const int circle_resolution = 16;
+ const uint v_needed = circle_resolution * 2 * 3;
+ uint vertid = drw_debug_start_draw(v_needed);
+ if (vertid + v_needed < DRW_DEBUG_DRAW_VERT_MAX) {
+ uint pcolor = drw_debug_color_pack(color);
+ for (int axis = 0; axis < 3; axis++) {
+ for (int edge = 0; edge < circle_resolution; edge++) {
+ float angle1 = (2.0 * 3.141592) * float(edge + 0) / float(circle_resolution);
+ vec3 p1 = vec3(cos(angle1), sin(angle1), 0.0);
+ p1 = vec3(p1[(0 + axis) % 3], p1[(1 + axis) % 3], p1[(2 + axis) % 3]);
+
+ float angle2 = (2.0 * 3.141592) * float(edge + 1) / float(circle_resolution);
+ vec3 p2 = vec3(cos(angle2), sin(angle2), 0.0);
+ p2 = vec3(p2[(0 + axis) % 3], p2[(1 + axis) % 3], p2[(2 + axis) % 3]);
+
+ drw_debug_line(vertid, p1, p2, pcolor);
+ }
+ }
+ }
+}
+void drw_debug_sphere(vec3 p, float radius)
+{
+ drw_debug_sphere(p, radius, drw_debug_default_color);
+}
+
+/**
+ * Draw a matrix transformation as 3 colored axes.
+ */
+void drw_debug_matrix(mat4 mat, vec4 color)
+{
+ vec4 p[4] = vec4[4](vec4(0, 0, 0, 1), vec4(1, 0, 0, 1), vec4(0, 1, 0, 1), vec4(0, 0, 1, 1));
+ for (int i = 0; i < 4; i++) {
+ p[i] = mat * p[i];
+ p[i].xyz /= p[i].w;
+ }
+ drw_debug_line(p[0].xyz, p[0].xyz, vec4(1, 0, 0, 1));
+ drw_debug_line(p[0].xyz, p[1].xyz, vec4(0, 1, 0, 1));
+ drw_debug_line(p[0].xyz, p[2].xyz, vec4(0, 0, 1, 1));
+}
+void drw_debug_matrix(mat4 mat)
+{
+ drw_debug_matrix(mat, drw_debug_default_color);
+}
+
+/**
+ * Draw a matrix as a 2 units length bounding box, centered on origin.
+ */
+void drw_debug_matrix_as_bbox(mat4 mat, vec4 color)
+{
+ vec4 p[8] = vec4[8](vec4(-1, -1, -1, 1),
+ vec4(1, -1, -1, 1),
+ vec4(1, 1, -1, 1),
+ vec4(-1, 1, -1, 1),
+ vec4(-1, -1, 1, 1),
+ vec4(1, -1, 1, 1),
+ vec4(1, 1, 1, 1),
+ vec4(-1, 1, 1, 1));
+ for (int i = 0; i < 8; i++) {
+ p[i] = mat * p[i];
+ p[i].xyz /= p[i].w;
+ }
+ drw_debug_quad(p[0].xyz, p[1].xyz, p[2].xyz, p[3].xyz, color);
+ drw_debug_line(p[0].xyz, p[4].xyz, color);
+ drw_debug_line(p[1].xyz, p[5].xyz, color);
+ drw_debug_line(p[2].xyz, p[6].xyz, color);
+ drw_debug_line(p[3].xyz, p[7].xyz, color);
+ drw_debug_quad(p[4].xyz, p[5].xyz, p[6].xyz, p[7].xyz, color);
+}
+void drw_debug_matrix_as_bbox(mat4 mat)
+{
+ drw_debug_matrix_as_bbox(mat, drw_debug_default_color);
+}
+
+/** \} */
diff --git a/source/blender/draw/intern/shaders/common_debug_print_lib.glsl b/source/blender/draw/intern/shaders/common_debug_print_lib.glsl
new file mode 100644
index 00000000000..0c7f32bd00d
--- /dev/null
+++ b/source/blender/draw/intern/shaders/common_debug_print_lib.glsl
@@ -0,0 +1,389 @@
+
+/**
+ * Debug print implementation for shaders.
+ *
+ * `print()`:
+ * Log variable or strings inside the viewport.
+ * Using a unique non string argument will print the variable name with it.
+ * Concatenate by using multiple arguments. i.e: `print("Looped ", n, "times.")`.
+ * `drw_print_no_endl()`:
+ * Same as `print()` but does not finish the line.
+ * `drw_print_value()`:
+ * Display only the value of a variable. Does not finish the line.
+ * `drw_print_value_hex()`:
+ * Display only the hex representation of a variable. Does not finish the line.
+ * `drw_print_value_binary()`: Display only the binary representation of a
+ * variable. Does not finish the line.
+ *
+ * IMPORTANT: As it is now, it is not yet thread safe. Only print from one thread. You can use the
+ * IS_DEBUG_MOUSE_FRAGMENT macro in fragment shader to filter using mouse position or
+ * IS_FIRST_INVOCATION in compute shaders.
+ *
+ * NOTE: Floating point representation might not be very precise (see drw_print_value(float)).
+ *
+ * IMPORTANT: Multipler drawcalls can write to the buffer in sequence (if they are from different
+ * shgroups). However, we add barriers to support this case and it might change the application
+ * behavior. Uncomment DISABLE_DEBUG_SHADER_drw_print_BARRIER to remove the barriers if that
+ * happens. But then you are limited to a single invocation output.
+ *
+ * IMPORTANT: All of these are copied to the CPU debug libs (draw_debug.cc). They need to be kept
+ * in sync to write the same data.
+ */
+
+/** Global switch option when you want to silence all prints from all shaders at once. */
+bool drw_debug_print_enable = true;
+
+/* Set drw_print_col to max value so we will start by creating a new line and get the correct
+ * threadsafe row. */
+uint drw_print_col = DRW_DEBUG_PRINT_WORD_WRAP_COLUMN;
+uint drw_print_row = 0u;
+
+void drw_print_newline()
+{
+ if (!drw_debug_print_enable) {
+ return;
+ }
+ drw_print_col = 0u;
+ drw_print_row = atomicAdd(drw_debug_print_row_shared, 1u) + 1u;
+}
+
+void drw_print_string_start(uint len)
+{
+ if (!drw_debug_print_enable) {
+ return;
+ }
+ /* Break before word. */
+ if (drw_print_col + len > DRW_DEBUG_PRINT_WORD_WRAP_COLUMN) {
+ drw_print_newline();
+ }
+}
+
+void drw_print_char4(uint data)
+{
+ if (!drw_debug_print_enable) {
+ return;
+ }
+ /* Convert into char stream. */
+ for (; data != 0u; data >>= 8u) {
+ uint char1 = data & 0xFFu;
+ /* Check for null terminator. */
+ if (char1 == 0x00) {
+ break;
+ }
+ uint cursor = atomicAdd(drw_debug_print_cursor, 1u);
+ /* NOTE: Skip the header manually. */
+ cursor += 4;
+ if (cursor < DRW_DEBUG_PRINT_MAX) {
+ /* For future usage. (i.e: Color) */
+ uint flags = 0u;
+ uint col = drw_print_col++;
+ uint drw_print_header = (flags << 24u) | (drw_print_row << 16u) | (col << 8u);
+ drw_debug_print_buf[cursor] = drw_print_header | char1;
+ /* Break word. */
+ if (drw_print_col > DRW_DEBUG_PRINT_WORD_WRAP_COLUMN) {
+ drw_print_newline();
+ }
+ }
+ }
+}
+
+/**
+ * NOTE(fclem): Strange behavior emerge when trying to increment the digit
+ * counter inside the append function. It looks like the compiler does not see
+ * it is referenced as an index for char4 and thus do not capture the right
+ * reference. I do not know if this is undefined behavior. As a matter of
+ * precaution, we implement all the append function separately. This behavior
+ * was observed on both Mesa & amdgpu-pro.
+ */
+/* Using ascii char code. Expect char1 to be less or equal to 0xFF. Appends chars to the right. */
+void drw_print_append_char(uint char1, inout uint char4)
+{
+ char4 = (char4 << 8u) | char1;
+}
+
+void drw_print_append_digit(uint digit, inout uint char4)
+{
+ const uint char_A = 0x41u;
+ const uint char_0 = 0x30u;
+ bool is_hexadecimal = digit > 9u;
+ char4 = (char4 << 8u) | (is_hexadecimal ? (char_A + digit - 10u) : (char_0 + digit));
+}
+
+void drw_print_append_space(inout uint char4)
+{
+ char4 = (char4 << 8u) | 0x20u;
+}
+
+void drw_print_value_binary(uint value)
+{
+ drw_print_no_endl("0b");
+ drw_print_string_start(10u * 4u);
+ uint digits[10] = uint[10](0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u);
+ uint digit = 0u;
+ for (uint i = 0u; i < 32u; i++) {
+ drw_print_append_digit(((value >> i) & 1u), digits[digit / 4u]);
+ digit++;
+ if ((i % 4u) == 3u) {
+ drw_print_append_space(digits[digit / 4u]);
+ digit++;
+ }
+ }
+ /* Numbers are written from right to left. So we need to reverse the order. */
+ for (int j = 9; j >= 0; j--) {
+ drw_print_char4(digits[j]);
+ }
+}
+
+void drw_print_value_binary(int value)
+{
+ drw_print_value_binary(uint(value));
+}
+
+void drw_print_value_binary(float value)
+{
+ drw_print_value_binary(floatBitsToUint(value));
+}
+
+void drw_print_value_uint(uint value, const bool hex, bool is_negative, const bool is_unsigned)
+{
+ drw_print_string_start(3u * 4u);
+ const uint blank_value = hex ? 0x30303030u : 0x20202020u;
+ const uint prefix = hex ? 0x78302020u : 0x20202020u;
+ uint digits[3] = uint[3](blank_value, blank_value, prefix);
+ const uint base = hex ? 16u : 10u;
+ uint digit = 0u;
+ /* Add `u` suffix. */
+ if (is_unsigned) {
+ drw_print_append_char('u', digits[digit / 4u]);
+ digit++;
+ }
+ /* Number's digits. */
+ for (; value != 0u || digit == uint(is_unsigned); value /= base) {
+ drw_print_append_digit(value % base, digits[digit / 4u]);
+ digit++;
+ }
+ /* Add negative sign. */
+ if (is_negative) {
+ drw_print_append_char('-', digits[digit / 4u]);
+ digit++;
+ }
+ /* Need to pad to uint alignment because we are issuing chars in "reverse". */
+ for (uint i = digit % 4u; i < 4u && i > 0u; i++) {
+ drw_print_append_space(digits[digit / 4u]);
+ digit++;
+ }
+ /* Numbers are written from right to left. So we need to reverse the order. */
+ for (int j = 2; j >= 0; j--) {
+ drw_print_char4(digits[j]);
+ }
+}
+
+void drw_print_value_hex(uint value)
+{
+ drw_print_value_uint(value, true, false, false);
+}
+
+void drw_print_value_hex(int value)
+{
+ drw_print_value_uint(uint(value), true, false, false);
+}
+
+void drw_print_value_hex(float value)
+{
+ drw_print_value_uint(floatBitsToUint(value), true, false, false);
+}
+
+void drw_print_value(uint value)
+{
+ drw_print_value_uint(value, false, false, true);
+}
+
+void drw_print_value(int value)
+{
+ drw_print_value_uint(uint(abs(value)), false, (value < 0), false);
+}
+
+void drw_print_value(bool value)
+{
+ if (value) {
+ drw_print_no_endl("true ");
+ }
+ else {
+ drw_print_no_endl("false");
+ }
+}
+
+/* NOTE(@fclem): This is homebrew and might not be 100% accurate (accuracy has
+ * not been tested and might dependent on compiler implementation). If unsure,
+ * use drw_print_value_hex and transcribe the value manually with another tool. */
+void drw_print_value(float val)
+{
+ /* We pad the string to match normal float values length. */
+ if (isnan(val)) {
+ drw_print_no_endl(" NaN");
+ return;
+ }
+ if (isinf(val)) {
+ if (sign(val) < 0.0) {
+ drw_print_no_endl(" -Inf");
+ }
+ else {
+ drw_print_no_endl(" Inf");
+ }
+ return;
+ }
+
+ /* Adjusted for significant digits (6) with sign (1), decimal separator (1)
+ * and exponent (4). */
+ const float significant_digits = 6.0;
+ drw_print_string_start(3u * 4u);
+ uint digits[3] = uint[3](0x20202020u, 0x20202020u, 0x20202020u);
+
+ float exponent = floor(log(abs(val)) / log(10.0));
+ bool display_exponent = exponent >= (significant_digits) ||
+ exponent <= (-significant_digits + 1.0);
+
+ float int_significant_digits = min(exponent + 1.0, significant_digits);
+ float dec_significant_digits = max(0.0, significant_digits - int_significant_digits);
+ /* Power to get to the rounding point. */
+ float rounding_power = dec_significant_digits;
+
+ if (val == 0.0 || isinf(exponent)) {
+ display_exponent = false;
+ int_significant_digits = dec_significant_digits = 1.0;
+ }
+ /* Remap to keep significant numbers count. */
+ if (display_exponent) {
+ int_significant_digits = 1.0;
+ dec_significant_digits = significant_digits - int_significant_digits;
+ rounding_power = -exponent + dec_significant_digits;
+ }
+ /* Round at the last significant digit. */
+ val = round(val * pow(10.0, rounding_power));
+ /* Get back to final exponent. */
+ val *= pow(10.0, -dec_significant_digits);
+
+ float int_part;
+ float dec_part = modf(val, int_part);
+
+ dec_part *= pow(10.0, dec_significant_digits);
+
+ const uint base = 10u;
+ uint digit = 0u;
+ /* Exponent */
+ uint value = uint(abs(exponent));
+ if (display_exponent) {
+ for (int i = 0; value != 0u || i == 0; i++, value /= base) {
+ drw_print_append_digit(value % base, digits[digit / 4u]);
+ digit++;
+ }
+ /* Exponent sign. */
+ uint sign_char = (exponent < 0.0) ? '-' : '+';
+ drw_print_append_char(sign_char, digits[digit / 4u]);
+ digit++;
+ /* Exponent `e` suffix. */
+ drw_print_append_char(0x65u, digits[digit / 4u]);
+ digit++;
+ }
+ /* Decimal part. */
+ value = uint(abs(dec_part));
+#if 0 /* We don't do that because it makes unstable values really hard to \
+ read. */
+ /* Trim trailing zeros. */
+ while ((value % base) == 0u) {
+ value /= base;
+ if (value == 0u) {
+ break;
+ }
+ }
+#endif
+ if (value != 0u) {
+ for (int i = 0; value != 0u || i == 0; i++, value /= base) {
+ drw_print_append_digit(value % base, digits[digit / 4u]);
+ digit++;
+ }
+ /* Point separator. */
+ drw_print_append_char('.', digits[digit / 4u]);
+ digit++;
+ }
+ /* Integer part. */
+ value = uint(abs(int_part));
+ for (int i = 0; value != 0u || i == 0; i++, value /= base) {
+ drw_print_append_digit(value % base, digits[digit / 4u]);
+ digit++;
+ }
+ /* Negative sign. */
+ if (val < 0.0) {
+ drw_print_append_char('-', digits[digit / 4u]);
+ digit++;
+ }
+ /* Need to pad to uint alignment because we are issuing chars in "reverse". */
+ for (uint i = digit % 4u; i < 4u && i > 0u; i++) {
+ drw_print_append_space(digits[digit / 4u]);
+ digit++;
+ }
+ /* Numbers are written from right to left. So we need to reverse the order. */
+ for (int j = 2; j >= 0; j--) {
+ drw_print_char4(digits[j]);
+ }
+}
+
+void drw_print_value(vec2 value)
+{
+ drw_print_no_endl("vec2(", value[0], ", ", value[1], ")");
+}
+
+void drw_print_value(vec3 value)
+{
+ drw_print_no_endl("vec3(", value[0], ", ", value[1], ", ", value[1], ")");
+}
+
+void drw_print_value(vec4 value)
+{
+ drw_print_no_endl("vec4(", value[0], ", ", value[1], ", ", value[2], ", ", value[3], ")");
+}
+
+void drw_print_value(ivec2 value)
+{
+ drw_print_no_endl("ivec2(", value[0], ", ", value[1], ")");
+}
+
+void drw_print_value(ivec3 value)
+{
+ drw_print_no_endl("ivec3(", value[0], ", ", value[1], ", ", value[1], ")");
+}
+
+void drw_print_value(ivec4 value)
+{
+ drw_print_no_endl("ivec4(", value[0], ", ", value[1], ", ", value[2], ", ", value[3], ")");
+}
+
+void drw_print_value(uvec2 value)
+{
+ drw_print_no_endl("uvec2(", value[0], ", ", value[1], ")");
+}
+
+void drw_print_value(uvec3 value)
+{
+ drw_print_no_endl("uvec3(", value[0], ", ", value[1], ", ", value[1], ")");
+}
+
+void drw_print_value(uvec4 value)
+{
+ drw_print_no_endl("uvec4(", value[0], ", ", value[1], ", ", value[2], ", ", value[3], ")");
+}
+
+void drw_print_value(bvec2 value)
+{
+ drw_print_no_endl("bvec2(", value[0], ", ", value[1], ")");
+}
+
+void drw_print_value(bvec3 value)
+{
+ drw_print_no_endl("bvec3(", value[0], ", ", value[1], ", ", value[1], ")");
+}
+
+void drw_print_value(bvec4 value)
+{
+ drw_print_no_endl("bvec4(", value[0], ", ", value[1], ", ", value[2], ", ", value[3], ")");
+}
diff --git a/source/blender/draw/intern/shaders/common_view_lib.glsl b/source/blender/draw/intern/shaders/common_view_lib.glsl
index 8eecaa46b58..8ab2ef10e4c 100644
--- a/source/blender/draw/intern/shaders/common_view_lib.glsl
+++ b/source/blender/draw/intern/shaders/common_view_lib.glsl
@@ -37,6 +37,9 @@ layout(std140) uniform viewBlock
# endif
#endif
+#define IS_DEBUG_MOUSE_FRAGMENT (ivec2(gl_FragCoord) == drw_view.mouse_pixel)
+#define IS_FIRST_INVOCATION (gl_GlobalInvocationID == uvec3(0))
+
#define ViewNear (ViewVecs[0].w)
#define ViewFar (ViewVecs[1].w)
diff --git a/source/blender/draw/intern/shaders/draw_debug_draw_display_frag.glsl b/source/blender/draw/intern/shaders/draw_debug_draw_display_frag.glsl
new file mode 100644
index 00000000000..3fc5294b024
--- /dev/null
+++ b/source/blender/draw/intern/shaders/draw_debug_draw_display_frag.glsl
@@ -0,0 +1,9 @@
+
+/**
+ * Display debug edge list.
+ **/
+
+void main()
+{
+ out_color = interp.color;
+}
diff --git a/source/blender/draw/intern/shaders/draw_debug_draw_display_vert.glsl b/source/blender/draw/intern/shaders/draw_debug_draw_display_vert.glsl
new file mode 100644
index 00000000000..92c546aa203
--- /dev/null
+++ b/source/blender/draw/intern/shaders/draw_debug_draw_display_vert.glsl
@@ -0,0 +1,15 @@
+
+/**
+ * Display debug edge list.
+ **/
+
+void main()
+{
+ /* Skip the first vertex containing header data. */
+ DRWDebugVert vert = drw_debug_verts_buf[gl_VertexID + 1];
+ vec3 pos = uintBitsToFloat(uvec3(vert.pos0, vert.pos1, vert.pos2));
+ vec4 col = vec4((uvec4(vert.color) >> uvec4(0, 8, 16, 24)) & 0xFFu);
+
+ interp.color = col;
+ gl_Position = persmat * vec4(pos, 1.0);
+}
diff --git a/source/blender/draw/intern/shaders/draw_debug_info.hh b/source/blender/draw/intern/shaders/draw_debug_info.hh
new file mode 100644
index 00000000000..893a5e537d9
--- /dev/null
+++ b/source/blender/draw/intern/shaders/draw_debug_info.hh
@@ -0,0 +1,52 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include "gpu_shader_create_info.hh"
+
+/* -------------------------------------------------------------------- */
+/** \name Debug print
+ *
+ * Allows print() function to have logging support inside shaders.
+ * \{ */
+
+GPU_SHADER_CREATE_INFO(draw_debug_print)
+ .typedef_source("draw_shader_shared.h")
+ .storage_buf(7, Qualifier::READ_WRITE, "uint", "drw_debug_print_buf[]");
+
+GPU_SHADER_INTERFACE_INFO(draw_debug_print_display_iface, "").flat(Type::UINT, "char_index");
+
+GPU_SHADER_CREATE_INFO(draw_debug_print_display)
+ .do_static_compilation(true)
+ .typedef_source("draw_shader_shared.h")
+ .storage_buf(7, Qualifier::READ, "uint", "drw_debug_print_buf[]")
+ .vertex_out(draw_debug_print_display_iface)
+ .fragment_out(0, Type::VEC4, "out_color")
+ .vertex_source("draw_debug_print_display_vert.glsl")
+ .fragment_source("draw_debug_print_display_frag.glsl")
+ .additional_info("draw_view");
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Debug draw shapes
+ *
+ * Allows to draw lines and points just like the DRW_debug module functions.
+ * \{ */
+
+GPU_SHADER_CREATE_INFO(draw_debug_draw)
+ .typedef_source("draw_shader_shared.h")
+ .storage_buf(6, Qualifier::READ_WRITE, "DRWDebugVert", "drw_debug_verts_buf[]");
+
+GPU_SHADER_INTERFACE_INFO(draw_debug_draw_display_iface, "interp").flat(Type::VEC4, "color");
+
+GPU_SHADER_CREATE_INFO(draw_debug_draw_display)
+ .do_static_compilation(true)
+ .typedef_source("draw_shader_shared.h")
+ .storage_buf(6, Qualifier::READ, "DRWDebugVert", "drw_debug_verts_buf[]")
+ .vertex_out(draw_debug_draw_display_iface)
+ .fragment_out(0, Type::VEC4, "out_color")
+ .push_constant(Type::MAT4, "persmat")
+ .vertex_source("draw_debug_draw_display_vert.glsl")
+ .fragment_source("draw_debug_draw_display_frag.glsl")
+ .additional_info("draw_view");
+
+/** \} */
diff --git a/source/blender/draw/intern/shaders/draw_debug_print_display_frag.glsl b/source/blender/draw/intern/shaders/draw_debug_print_display_frag.glsl
new file mode 100644
index 00000000000..fe608816109
--- /dev/null
+++ b/source/blender/draw/intern/shaders/draw_debug_print_display_frag.glsl
@@ -0,0 +1,133 @@
+
+/**
+ * Display characters using an ascii table.
+ **/
+
+#pragma BLENDER_REQUIRE(common_math_lib.glsl)
+
+bool char_intersect(uvec2 bitmap_position)
+{
+ /* Using 8x8 = 64bits = uvec2. */
+ uvec2 ascii_bitmap[96] = uvec2[96](uvec2(0x00000000u, 0x00000000u),
+ uvec2(0x18001800u, 0x183c3c18u),
+ uvec2(0x00000000u, 0x36360000u),
+ uvec2(0x7f363600u, 0x36367f36u),
+ uvec2(0x301f0c00u, 0x0c3e031eu),
+ uvec2(0x0c666300u, 0x00633318u),
+ uvec2(0x3b336e00u, 0x1c361c6eu),
+ uvec2(0x00000000u, 0x06060300u),
+ uvec2(0x060c1800u, 0x180c0606u),
+ uvec2(0x180c0600u, 0x060c1818u),
+ uvec2(0x3c660000u, 0x00663cffu),
+ uvec2(0x0c0c0000u, 0x000c0c3fu),
+ uvec2(0x000c0c06u, 0x00000000u),
+ uvec2(0x00000000u, 0x0000003fu),
+ uvec2(0x000c0c00u, 0x00000000u),
+ uvec2(0x06030100u, 0x6030180cu),
+ uvec2(0x6f673e00u, 0x3e63737bu),
+ uvec2(0x0c0c3f00u, 0x0c0e0c0cu),
+ uvec2(0x06333f00u, 0x1e33301cu),
+ uvec2(0x30331e00u, 0x1e33301cu),
+ uvec2(0x7f307800u, 0x383c3633u),
+ uvec2(0x30331e00u, 0x3f031f30u),
+ uvec2(0x33331e00u, 0x1c06031fu),
+ uvec2(0x0c0c0c00u, 0x3f333018u),
+ uvec2(0x33331e00u, 0x1e33331eu),
+ uvec2(0x30180e00u, 0x1e33333eu),
+ uvec2(0x000c0c00u, 0x000c0c00u),
+ uvec2(0x000c0c06u, 0x000c0c00u),
+ uvec2(0x060c1800u, 0x180c0603u),
+ uvec2(0x003f0000u, 0x00003f00u),
+ uvec2(0x180c0600u, 0x060c1830u),
+ uvec2(0x0c000c00u, 0x1e333018u),
+ uvec2(0x7b031e00u, 0x3e637b7bu),
+ uvec2(0x3f333300u, 0x0c1e3333u),
+ uvec2(0x66663f00u, 0x3f66663eu),
+ uvec2(0x03663c00u, 0x3c660303u),
+ uvec2(0x66361f00u, 0x1f366666u),
+ uvec2(0x16467f00u, 0x7f46161eu),
+ uvec2(0x16060f00u, 0x7f46161eu),
+ uvec2(0x73667c00u, 0x3c660303u),
+ uvec2(0x33333300u, 0x3333333fu),
+ uvec2(0x0c0c1e00u, 0x1e0c0c0cu),
+ uvec2(0x33331e00u, 0x78303030u),
+ uvec2(0x36666700u, 0x6766361eu),
+ uvec2(0x46667f00u, 0x0f060606u),
+ uvec2(0x6b636300u, 0x63777f7fu),
+ uvec2(0x73636300u, 0x63676f7bu),
+ uvec2(0x63361c00u, 0x1c366363u),
+ uvec2(0x06060f00u, 0x3f66663eu),
+ uvec2(0x3b1e3800u, 0x1e333333u),
+ uvec2(0x36666700u, 0x3f66663eu),
+ uvec2(0x38331e00u, 0x1e33070eu),
+ uvec2(0x0c0c1e00u, 0x3f2d0c0cu),
+ uvec2(0x33333f00u, 0x33333333u),
+ uvec2(0x331e0c00u, 0x33333333u),
+ uvec2(0x7f776300u, 0x6363636bu),
+ uvec2(0x1c366300u, 0x6363361cu),
+ uvec2(0x0c0c1e00u, 0x3333331eu),
+ uvec2(0x4c667f00u, 0x7f633118u),
+ uvec2(0x06061e00u, 0x1e060606u),
+ uvec2(0x30604000u, 0x03060c18u),
+ uvec2(0x18181e00u, 0x1e181818u),
+ uvec2(0x00000000u, 0x081c3663u),
+ uvec2(0x000000ffu, 0x00000000u),
+ uvec2(0x00000000u, 0x0c0c1800u),
+ uvec2(0x3e336e00u, 0x00001e30u),
+ uvec2(0x66663b00u, 0x0706063eu),
+ uvec2(0x03331e00u, 0x00001e33u),
+ uvec2(0x33336e00u, 0x3830303eu),
+ uvec2(0x3f031e00u, 0x00001e33u),
+ uvec2(0x06060f00u, 0x1c36060fu),
+ uvec2(0x333e301fu, 0x00006e33u),
+ uvec2(0x66666700u, 0x0706366eu),
+ uvec2(0x0c0c1e00u, 0x0c000e0cu),
+ uvec2(0x3033331eu, 0x30003030u),
+ uvec2(0x1e366700u, 0x07066636u),
+ uvec2(0x0c0c1e00u, 0x0e0c0c0cu),
+ uvec2(0x7f6b6300u, 0x0000337fu),
+ uvec2(0x33333300u, 0x00001f33u),
+ uvec2(0x33331e00u, 0x00001e33u),
+ uvec2(0x663e060fu, 0x00003b66u),
+ uvec2(0x333e3078u, 0x00006e33u),
+ uvec2(0x66060f00u, 0x00003b6eu),
+ uvec2(0x1e301f00u, 0x00003e03u),
+ uvec2(0x0c2c1800u, 0x080c3e0cu),
+ uvec2(0x33336e00u, 0x00003333u),
+ uvec2(0x331e0c00u, 0x00003333u),
+ uvec2(0x7f7f3600u, 0x0000636bu),
+ uvec2(0x1c366300u, 0x00006336u),
+ uvec2(0x333e301fu, 0x00003333u),
+ uvec2(0x0c263f00u, 0x00003f19u),
+ uvec2(0x0c0c3800u, 0x380c0c07u),
+ uvec2(0x18181800u, 0x18181800u),
+ uvec2(0x0c0c0700u, 0x070c0c38u),
+ uvec2(0x00000000u, 0x6e3b0000u),
+ uvec2(0x00000000u, 0x00000000u));
+
+ if (!in_range_inclusive(bitmap_position, uvec2(0), uvec2(7))) {
+ return false;
+ }
+ uint char_bits = ascii_bitmap[char_index][bitmap_position.y >> 2u & 1u];
+ char_bits = (char_bits >> ((bitmap_position.y & 3u) * 8u + bitmap_position.x));
+ return (char_bits & 1u) != 0u;
+}
+
+void main()
+{
+ uvec2 bitmap_position = uvec2(gl_PointCoord.xy * 8.0);
+ /* Point coord start from top left corner. But layout is from bottom to top. */
+ bitmap_position.y = 7 - bitmap_position.y;
+
+ if (char_intersect(bitmap_position)) {
+ out_color = vec4(1);
+ }
+ else if (char_intersect(bitmap_position + uvec2(0, 1))) {
+ /* Shadow */
+ out_color = vec4(0, 0, 0, 1);
+ }
+ else {
+ /* Transparent Background for ease of read. */
+ out_color = vec4(0, 0, 0, 0.2);
+ }
+} \ No newline at end of file
diff --git a/source/blender/draw/intern/shaders/draw_debug_print_display_vert.glsl b/source/blender/draw/intern/shaders/draw_debug_print_display_vert.glsl
new file mode 100644
index 00000000000..c8fc3815436
--- /dev/null
+++ b/source/blender/draw/intern/shaders/draw_debug_print_display_vert.glsl
@@ -0,0 +1,29 @@
+
+/**
+ * Display characters using an ascii table. Outputs one point per character.
+ **/
+
+#pragma BLENDER_REQUIRE(common_view_lib.glsl)
+
+void main()
+{
+ /* Skip first 4 chars containing header data. */
+ uint char_data = drw_debug_print_buf[gl_VertexID + 4];
+ char_index = (char_data & 0xFFu) - 0x20u;
+
+ /* Discard invalid chars. */
+ if (char_index >= 96u) {
+ gl_Position = vec4(-1);
+ gl_PointSize = 0.0;
+ return;
+ }
+ uint row = (char_data >> 16u) & 0xFFu;
+ uint col = (char_data >> 8u) & 0xFFu;
+
+ float char_size = 16.0;
+ /* Change anchor point to the top left. */
+ vec2 pos_on_screen = char_size * vec2(col, row) + char_size * 4;
+ gl_Position = vec4(
+ pos_on_screen * drw_view.viewport_size_inverse * vec2(2.0, -2.0) - vec2(1.0, -1.0), 0, 1);
+ gl_PointSize = char_size;
+} \ No newline at end of file
diff --git a/source/blender/editors/armature/armature_select.c b/source/blender/editors/armature/armature_select.c
index 08d5d6558e0..ae15bc39ec8 100644
--- a/source/blender/editors/armature/armature_select.c
+++ b/source/blender/editors/armature/armature_select.c
@@ -59,7 +59,7 @@ Base *ED_armature_base_and_ebone_from_select_buffer(Base **bases,
const uint hit_object = select_id & 0xFFFF;
Base *base = NULL;
EditBone *ebone = NULL;
- /* TODO(campbell): optimize, eg: sort & binary search. */
+ /* TODO(@campbellbarton): optimize, eg: sort & binary search. */
for (uint base_index = 0; base_index < bases_len; base_index++) {
if (bases[base_index]->object->runtime.select_id == hit_object) {
base = bases[base_index];
@@ -83,7 +83,7 @@ Object *ED_armature_object_and_ebone_from_select_buffer(Object **objects,
const uint hit_object = select_id & 0xFFFF;
Object *ob = NULL;
EditBone *ebone = NULL;
- /* TODO(campbell): optimize, eg: sort & binary search. */
+ /* TODO(@campbellbarton): optimize, eg: sort & binary search. */
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
if (objects[ob_index]->runtime.select_id == hit_object) {
ob = objects[ob_index];
@@ -107,7 +107,7 @@ Base *ED_armature_base_and_pchan_from_select_buffer(Base **bases,
const uint hit_object = select_id & 0xFFFF;
Base *base = NULL;
bPoseChannel *pchan = NULL;
- /* TODO(campbell): optimize, eg: sort & binary search. */
+ /* TODO(@campbellbarton): optimize, eg: sort & binary search. */
for (uint base_index = 0; base_index < bases_len; base_index++) {
if (bases[base_index]->object->runtime.select_id == hit_object) {
base = bases[base_index];
@@ -1452,7 +1452,7 @@ static void armature_select_more_less(Object *ob, bool more)
bArmature *arm = (bArmature *)ob->data;
EditBone *ebone;
- /* XXX(campbell): eventually we shouldn't need this. */
+ /* XXX(@campbellbarton): eventually we shouldn't need this. */
ED_armature_edit_sync_selection(arm->edbo);
/* count bones & store selection state */
diff --git a/source/blender/editors/armature/pose_select.c b/source/blender/editors/armature/pose_select.c
index b6b5d3ee495..6756dec1c95 100644
--- a/source/blender/editors/armature/pose_select.c
+++ b/source/blender/editors/armature/pose_select.c
@@ -166,7 +166,7 @@ bool ED_armature_pose_select_pick_bone(ViewLayer *view_layer,
/* Since we do unified select, we don't shift+select a bone if the
* armature object was not active yet.
- * NOTE(campbell): special exception for armature mode so we can do multi-select
+ * NOTE(@campbellbarton): special exception for armature mode so we can do multi-select
* we could check for multi-select explicitly but think its fine to
* always give predictable behavior in weight paint mode. */
if ((ob_act == NULL) || ((ob_act != ob) && (ob_act->mode & OB_MODE_ALL_WEIGHT_PAINT) == 0)) {
diff --git a/source/blender/editors/curve/editcurve.c b/source/blender/editors/curve/editcurve.c
index 852bfb00ea6..164336c4b22 100644
--- a/source/blender/editors/curve/editcurve.c
+++ b/source/blender/editors/curve/editcurve.c
@@ -1279,7 +1279,7 @@ void ED_curve_editnurb_make(Object *obedit)
if (actkey) {
// XXX strcpy(G.editModeTitleExtra, "(Key) ");
- /* TODO(campbell): undo_system: investigate why this was needed. */
+ /* TODO(@campbellbarton): undo_system: investigate why this was needed. */
#if 0
undo_editmode_clear();
#endif
@@ -1975,7 +1975,7 @@ static int sel_to_copy_ints(const BPoint *bp,
else if (not_full == -1) {
not_full = selected_in_curr_leg;
}
- /* We have partialy selected leg in opposite dimension if condition is met. */
+ /* We have partially selected leg in opposite dimension if condition is met. */
else if (not_full != selected_in_curr_leg) {
return -1;
}
diff --git a/source/blender/editors/gpencil/gpencil_primitive.c b/source/blender/editors/gpencil/gpencil_primitive.c
index befff611d58..70f12151fdd 100644
--- a/source/blender/editors/gpencil/gpencil_primitive.c
+++ b/source/blender/editors/gpencil/gpencil_primitive.c
@@ -1107,7 +1107,7 @@ static void gpencil_primitive_update(bContext *C, wmOperator *op, tGPDprimitive
/* Initialize mouse points. */
static void gpencil_primitive_interaction_begin(tGPDprimitive *tgpi, const wmEvent *event)
{
- copy_v2fl_v2i(tgpi->mval, event->mval);
+ WM_event_drag_start_mval_fl(event, tgpi->region, tgpi->mval);
copy_v2_v2(tgpi->origin, tgpi->mval);
copy_v2_v2(tgpi->start, tgpi->mval);
copy_v2_v2(tgpi->end, tgpi->mval);
diff --git a/source/blender/editors/include/ED_mesh.h b/source/blender/editors/include/ED_mesh.h
index 9d3fd5af47f..71ddffca8a9 100644
--- a/source/blender/editors/include/ED_mesh.h
+++ b/source/blender/editors/include/ED_mesh.h
@@ -390,7 +390,10 @@ void ED_keymap_mesh(struct wmKeyConfig *keyconf);
* Copy the face flags, most importantly selection from the mesh to the final derived mesh,
* use in object mode when selecting faces (while painting).
*/
-void paintface_flush_flags(struct bContext *C, struct Object *ob, short flag);
+void paintface_flush_flags(struct bContext *C,
+ struct Object *ob,
+ bool flush_selection,
+ bool flush_hidden);
/**
* \return True when pick finds an element or the selection changed.
*/
diff --git a/source/blender/editors/include/ED_transform_snap_object_context.h b/source/blender/editors/include/ED_transform_snap_object_context.h
index db44d9af706..f9ca578f282 100644
--- a/source/blender/editors/include/ED_transform_snap_object_context.h
+++ b/source/blender/editors/include/ED_transform_snap_object_context.h
@@ -57,13 +57,13 @@ struct SnapObjectParams {
/* Geometry for snapping in edit mode. */
eSnapEditType edit_mode_type;
/* snap to the closest element, use when using more than one snap type */
- bool use_occlusion_test : true;
+ bool use_occlusion_test : 1;
/* exclude back facing geometry from snapping */
- bool use_backface_culling : true;
+ bool use_backface_culling : 1;
/* Break nearest face snapping into steps to improve transformations across U-shaped targets. */
short face_nearest_steps;
/* Enable to force nearest face snapping to snap to target the source was initially near. */
- bool keep_on_same_target;
+ bool keep_on_same_target : 1;
};
typedef struct SnapObjectContext SnapObjectContext;
diff --git a/source/blender/editors/include/ED_view3d.h b/source/blender/editors/include/ED_view3d.h
index 931bb7be8bf..bb95ea97c1c 100644
--- a/source/blender/editors/include/ED_view3d.h
+++ b/source/blender/editors/include/ED_view3d.h
@@ -1171,7 +1171,7 @@ void ED_view3d_camera_lock_init(const struct Depsgraph *depsgraph,
*
* Apply the 3D Viewport transformation back to the camera object.
*
- * \return true if the camera is moved.
+ * \return true if the camera (or one of it's parents) was moved.
*/
bool ED_view3d_camera_lock_sync(const struct Depsgraph *depsgraph,
struct View3D *v3d,
@@ -1197,14 +1197,22 @@ bool ED_view3d_camera_lock_autokey(struct View3D *v3d,
void ED_view3d_lock_clear(struct View3D *v3d);
/**
+ * Check if creating an undo step should be performed if the viewport moves.
+ * \return true if #ED_view3d_camera_lock_undo_push would do an undo push.
+ */
+bool ED_view3d_camera_lock_undo_test(const View3D *v3d,
+ const RegionView3D *rv3d,
+ struct bContext *C);
+
+/**
* Create an undo step when the camera is locked to the view.
* \param str: The name of the undo step (typically #wmOperatorType.name should be used).
*
* \return true when the call to push an undo step was made.
*/
bool ED_view3d_camera_lock_undo_push(const char *str,
- View3D *v3d,
- struct RegionView3D *rv3d,
+ const View3D *v3d,
+ const struct RegionView3D *rv3d,
struct bContext *C);
/**
@@ -1214,8 +1222,8 @@ bool ED_view3d_camera_lock_undo_push(const char *str,
* where adding a separate undo step each time isn't desirable.
*/
bool ED_view3d_camera_lock_undo_grouped_push(const char *str,
- View3D *v3d,
- struct RegionView3D *rv3d,
+ const View3D *v3d,
+ const struct RegionView3D *rv3d,
struct bContext *C);
#define VIEW3D_MARGIN 1.4f
diff --git a/source/blender/editors/interface/CMakeLists.txt b/source/blender/editors/interface/CMakeLists.txt
index c54bcad905f..e4a973a375e 100644
--- a/source/blender/editors/interface/CMakeLists.txt
+++ b/source/blender/editors/interface/CMakeLists.txt
@@ -27,13 +27,13 @@ set(INC
)
set(SRC
- eyedroppers/interface_eyedropper.c
eyedroppers/eyedropper_color.c
eyedroppers/eyedropper_colorband.c
eyedroppers/eyedropper_datablock.c
eyedroppers/eyedropper_depth.c
eyedroppers/eyedropper_driver.c
eyedroppers/eyedropper_gpencil_color.c
+ eyedroppers/interface_eyedropper.c
interface.cc
interface_align.c
interface_anim.cc
diff --git a/source/blender/editors/interface/interface.cc b/source/blender/editors/interface/interface.cc
index 2f9e69137ed..c076845af3c 100644
--- a/source/blender/editors/interface/interface.cc
+++ b/source/blender/editors/interface/interface.cc
@@ -1310,7 +1310,7 @@ static bool ui_but_event_operator_string_from_panel(const bContext *C,
IDP_AddToGroup(prop_panel, IDP_New(IDP_INT, &region_type_val, "region_type"));
for (int i = 0; i < 2; i++) {
- /* FIXME(campbell): We can't reasonably search all configurations - long term. */
+ /* FIXME(@campbellbarton): We can't reasonably search all configurations - long term. */
IDPropertyTemplate val = {0};
val.i = i;
diff --git a/source/blender/editors/interface/interface_anim.cc b/source/blender/editors/interface/interface_anim.cc
index 8e898b7fe66..4da6cefd8de 100644
--- a/source/blender/editors/interface/interface_anim.cc
+++ b/source/blender/editors/interface/interface_anim.cc
@@ -325,7 +325,7 @@ void ui_but_anim_decorate_cb(bContext *C, void *arg_but, void *UNUSED(arg_dummy)
return;
}
- /* FIXME(campbell), swapping active pointer is weak. */
+ /* FIXME(@campbellbarton): swapping active pointer is weak. */
SWAP(struct uiHandleButtonData *, but_anim->active, but_decorate->but.active);
wm->op_undo_depth++;
diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c
index 80fd0cbe16e..6ee421fb4d2 100644
--- a/source/blender/editors/interface/interface_handlers.c
+++ b/source/blender/editors/interface/interface_handlers.c
@@ -4344,15 +4344,18 @@ static uiButExtraOpIcon *ui_but_extra_operator_icon_mouse_over_get(uiBut *but,
ARegion *region,
const wmEvent *event)
{
- float xmax = but->rect.xmax;
- const float icon_size = 0.8f * BLI_rctf_size_y(&but->rect); /* ICON_SIZE_FROM_BUTRECT */
- int x = event->xy[0], y = event->xy[1];
+ if (BLI_listbase_is_empty(&but->extra_op_icons)) {
+ return NULL;
+ }
+ int x = event->xy[0], y = event->xy[1];
ui_window_to_block(region, but->block, &x, &y);
if (!BLI_rctf_isect_pt(&but->rect, x, y)) {
return NULL;
}
+ const float icon_size = 0.8f * BLI_rctf_size_y(&but->rect); /* ICON_SIZE_FROM_BUTRECT */
+ float xmax = but->rect.xmax;
/* Same as in 'widget_draw_extra_icons', icon padding from the right edge. */
xmax -= 0.2 * icon_size;
@@ -8810,7 +8813,7 @@ void UI_context_active_but_prop_handle(bContext *C, const bool handle_undo)
{
uiBut *activebut = ui_context_rna_button_active(C);
if (activebut) {
- /* TODO(campbell): look into a better way to handle the button change
+ /* TODO(@campbellbarton): look into a better way to handle the button change
* currently this is mainly so reset defaults works for the
* operator redo panel. */
uiBlock *block = activebut->block;
diff --git a/source/blender/editors/interface/interface_icons.c b/source/blender/editors/interface/interface_icons.c
index c19e842aad8..5bb33576723 100644
--- a/source/blender/editors/interface/interface_icons.c
+++ b/source/blender/editors/interface/interface_icons.c
@@ -1824,7 +1824,7 @@ static void icon_draw_size(float x,
}
else if (di->type == ICON_TYPE_GEOM) {
#ifdef USE_UI_TOOLBAR_HACK
- /* TODO(campbell): scale icons up for toolbar,
+ /* TODO(@campbellbarton): scale icons up for toolbar,
* we need a way to detect larger buttons and do this automatic. */
{
float scale = (float)ICON_DEFAULT_HEIGHT_TOOLBAR / (float)ICON_DEFAULT_HEIGHT;
@@ -1839,7 +1839,7 @@ static void icon_draw_size(float x,
const bool geom_inverted = di->data.geom.inverted;
/* This could re-generate often if rendered at different sizes in the one interface.
- * TODO(campbell): support caching multiple sizes. */
+ * TODO(@campbellbarton): support caching multiple sizes. */
ImBuf *ibuf = di->data.geom.image_cache;
if ((ibuf == NULL) || (ibuf->x != w) || (ibuf->y != h) || (invert != geom_inverted)) {
if (ibuf) {
diff --git a/source/blender/editors/interface/interface_region_popover.cc b/source/blender/editors/interface/interface_region_popover.cc
index c152a9aacd5..17c8d890755 100644
--- a/source/blender/editors/interface/interface_region_popover.cc
+++ b/source/blender/editors/interface/interface_region_popover.cc
@@ -397,7 +397,7 @@ void UI_popover_end(bContext *C, uiPopover *pup, wmKeyMap *keymap)
pup->window = window;
- /* TODO(campbell): we may want to make this configurable.
+ /* TODO(@campbellbarton): we may want to make this configurable.
* The begin/end stype of calling popups doesn't allow 'can_refresh' to be set.
* For now close this style of popovers when accessed. */
UI_block_flag_disable(pup->block, UI_BLOCK_KEEP_OPEN);
diff --git a/source/blender/editors/interface/interface_region_tooltip.cc b/source/blender/editors/interface/interface_region_tooltip.cc
index 6a39b761983..8d88261c328 100644
--- a/source/blender/editors/interface/interface_region_tooltip.cc
+++ b/source/blender/editors/interface/interface_region_tooltip.cc
@@ -7,7 +7,7 @@
* ToolTip Region and Construction
*/
-/* TODO(campbell):
+/* TODO(@campbellbarton):
* We may want to have a higher level API that initializes a timer,
* checks for mouse motion and clears the tool-tip afterwards.
* We never want multiple tool-tips at once
diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c
index f566299960a..5813f1d090c 100644
--- a/source/blender/editors/interface/interface_templates.c
+++ b/source/blender/editors/interface/interface_templates.c
@@ -663,7 +663,7 @@ static void template_id_liboverride_hierarchy_create(bContext *C,
* system override with reset. */
if (!ID_IS_LINKED(id) && ID_IS_OVERRIDE_LIBRARY(id)) {
if (!ID_IS_OVERRIDE_LIBRARY_REAL(id)) {
- BKE_lib_override_library_get(bmain, id, &id);
+ BKE_lib_override_library_get(bmain, id, NULL, &id);
}
if (id->override_library->flag & IDOVERRIDE_LIBRARY_FLAG_SYSTEM_DEFINED) {
id->override_library->flag &= ~IDOVERRIDE_LIBRARY_FLAG_SYSTEM_DEFINED;
@@ -6458,13 +6458,13 @@ bool uiTemplateEventFromKeymapItem(struct uiLayout *layout,
for (int j = 0; j < ARRAY_SIZE(icon_mod) && icon_mod[j]; j++) {
uiItemL(layout, "", icon_mod[j]);
}
- uiItemL(layout, text, icon);
+ uiItemL(layout, TIP_(text), icon);
ok = true;
}
else if (text_fallback) {
const char *event_text = WM_key_event_string(kmi->type, true);
uiItemL(layout, event_text, ICON_NONE);
- uiItemL(layout, text, ICON_NONE);
+ uiItemL(layout, TIP_(text), ICON_NONE);
ok = true;
}
return ok;
diff --git a/source/blender/editors/io/CMakeLists.txt b/source/blender/editors/io/CMakeLists.txt
index a716c00d5d9..568ece00c4c 100644
--- a/source/blender/editors/io/CMakeLists.txt
+++ b/source/blender/editors/io/CMakeLists.txt
@@ -11,9 +11,9 @@ set(INC
../../io/collada
../../io/common
../../io/gpencil
+ ../../io/stl
../../io/usd
../../io/wavefront_obj
- ../../io/stl
../../makesdna
../../makesrna
../../windowmanager
@@ -33,8 +33,8 @@ set(SRC
io_gpencil_utils.c
io_obj.c
io_ops.c
- io_usd.c
io_stl_ops.c
+ io_usd.c
io_alembic.h
io_cache.h
@@ -42,8 +42,8 @@ set(SRC
io_gpencil.h
io_obj.h
io_ops.h
- io_usd.h
io_stl_ops.h
+ io_usd.h
)
set(LIB
diff --git a/source/blender/editors/io/io_obj.c b/source/blender/editors/io/io_obj.c
index 662ff601e29..c151baf13ef 100644
--- a/source/blender/editors/io/io_obj.c
+++ b/source/blender/editors/io/io_obj.c
@@ -382,11 +382,6 @@ static int wm_obj_import_invoke(bContext *C, wmOperator *op, const wmEvent *UNUS
static int wm_obj_import_exec(bContext *C, wmOperator *op)
{
- if (!RNA_struct_property_is_set(op->ptr, "filepath")) {
- BKE_report(op->reports, RPT_ERROR, "No filename given");
- return OPERATOR_CANCELLED;
- }
-
struct OBJImportParams import_params;
RNA_string_get(op->ptr, "filepath", import_params.filepath);
import_params.clamp_size = RNA_float_get(op->ptr, "clamp_size");
@@ -395,8 +390,35 @@ static int wm_obj_import_exec(bContext *C, wmOperator *op)
import_params.import_vertex_groups = RNA_boolean_get(op->ptr, "import_vertex_groups");
import_params.validate_meshes = RNA_boolean_get(op->ptr, "validate_meshes");
import_params.relative_paths = ((U.flag & USER_RELPATHS) != 0);
-
- OBJ_import(C, &import_params);
+ import_params.clear_selection = true;
+
+ int files_len = RNA_collection_length(op->ptr, "files");
+ if (files_len) {
+ /* Importing multiple files: loop over them and import one by one. */
+ PointerRNA fileptr;
+ PropertyRNA *prop;
+ char dir_only[FILE_MAX], file_only[FILE_MAX];
+
+ RNA_string_get(op->ptr, "directory", dir_only);
+ prop = RNA_struct_find_property(op->ptr, "files");
+ for (int i = 0; i < files_len; i++) {
+ RNA_property_collection_lookup_int(op->ptr, prop, i, &fileptr);
+ RNA_string_get(&fileptr, "name", file_only);
+ BLI_join_dirfile(
+ import_params.filepath, sizeof(import_params.filepath), dir_only, file_only);
+ import_params.clear_selection = (i == 0);
+ OBJ_import(C, &import_params);
+ }
+ }
+ else if (RNA_struct_property_is_set(op->ptr, "filepath")) {
+ /* Importing one file. */
+ RNA_string_get(op->ptr, "filepath", import_params.filepath);
+ OBJ_import(C, &import_params);
+ }
+ else {
+ BKE_report(op->reports, RPT_ERROR, "No filename given");
+ return OPERATOR_CANCELLED;
+ }
Scene *scene = CTX_data_scene(C);
WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene);
@@ -454,7 +476,8 @@ void WM_OT_obj_import(struct wmOperatorType *ot)
FILE_TYPE_FOLDER,
FILE_BLENDER,
FILE_OPENFILE,
- WM_FILESEL_FILEPATH | WM_FILESEL_SHOW_PROPS,
+ WM_FILESEL_FILEPATH | WM_FILESEL_SHOW_PROPS |
+ WM_FILESEL_DIRECTORY | WM_FILESEL_FILES,
FILE_DEFAULTDISPLAY,
FILE_SORT_ALPHA);
RNA_def_float(
diff --git a/source/blender/editors/lattice/editlattice_undo.c b/source/blender/editors/lattice/editlattice_undo.c
index 7615a57c8fe..8265225e08a 100644
--- a/source/blender/editors/lattice/editlattice_undo.c
+++ b/source/blender/editors/lattice/editlattice_undo.c
@@ -46,7 +46,7 @@ static CLG_LogRef LOG = {"ed.undo.lattice"};
/** \name Undo Conversion
* \{ */
-/* TODO(Campbell): this could contain an entire 'Lattice' struct. */
+/* TODO(@campbellbarton): this could contain an entire 'Lattice' struct. */
typedef struct UndoLattice {
BPoint *def;
int pntsu, pntsv, pntsw, actbp;
diff --git a/source/blender/editors/mesh/editface.cc b/source/blender/editors/mesh/editface.cc
index 3608e162727..0d988b9551f 100644
--- a/source/blender/editors/mesh/editface.cc
+++ b/source/blender/editors/mesh/editface.cc
@@ -17,6 +17,7 @@
#include "DNA_meshdata_types.h"
#include "DNA_object_types.h"
+#include "BKE_attribute.hh"
#include "BKE_context.h"
#include "BKE_customdata.h"
#include "BKE_global.h"
@@ -36,14 +37,18 @@
/* own include */
-void paintface_flush_flags(bContext *C, Object *ob, short flag)
+void paintface_flush_flags(bContext *C,
+ Object *ob,
+ const bool flush_selection,
+ const bool flush_hidden)
{
+ using namespace blender;
Mesh *me = BKE_mesh_from_object(ob);
MPoly *polys, *mp_orig;
const int *index_array = nullptr;
int totpoly;
- BLI_assert((flag & ~(SELECT | ME_HIDE)) == 0);
+ BLI_assert(flush_selection || flush_hidden);
if (me == nullptr) {
return;
@@ -53,7 +58,7 @@ void paintface_flush_flags(bContext *C, Object *ob, short flag)
/* we could call this directly in all areas that change selection,
* since this could become slow for realtime updates (circle-select for eg) */
- if (flag & SELECT) {
+ if (flush_selection) {
BKE_mesh_flush_select_from_polys(me);
}
@@ -64,8 +69,11 @@ void paintface_flush_flags(bContext *C, Object *ob, short flag)
return;
}
+ bke::AttributeAccessor attributes_me = bke::mesh_attributes(*me);
Mesh *me_orig = (Mesh *)ob_eval->runtime.data_orig;
+ bke::MutableAttributeAccessor attributes_orig = bke::mesh_attributes_for_write(*me_orig);
Mesh *me_eval = (Mesh *)ob_eval->runtime.data_eval;
+ bke::MutableAttributeAccessor attributes_eval = bke::mesh_attributes_for_write(*me_eval);
bool updated = false;
if (me_orig != nullptr && me_eval != nullptr && me_orig->totpoly == me->totpoly) {
@@ -73,13 +81,17 @@ void paintface_flush_flags(bContext *C, Object *ob, short flag)
for (int i = 0; i < me->totpoly; i++) {
me_orig->mpoly[i].flag = me->mpoly[i].flag;
}
-
- /* If the mesh has only deform modifiers, the evaluated mesh shares arrays. */
- if (me_eval->mpoly == me_orig->mpoly) {
- updated = true;
+ if (flush_hidden) {
+ const VArray<bool> hide_poly_me = attributes_me.lookup_or_default<bool>(
+ ".hide_poly", ATTR_DOMAIN_FACE, false);
+ bke::SpanAttributeWriter<bool> hide_poly_orig =
+ attributes_orig.lookup_or_add_for_write_only_span<bool>(".hide_poly", ATTR_DOMAIN_FACE);
+ hide_poly_me.materialize(hide_poly_orig.span);
+ hide_poly_orig.finish();
}
+
/* Mesh polys => Final derived polys */
- else if ((index_array = (const int *)CustomData_get_layer(&me_eval->pdata, CD_ORIGINDEX))) {
+ if ((index_array = (const int *)CustomData_get_layer(&me_eval->pdata, CD_ORIGINDEX))) {
polys = me_eval->mpoly;
totpoly = me_eval->totpoly;
@@ -91,13 +103,24 @@ void paintface_flush_flags(bContext *C, Object *ob, short flag)
polys[i].flag = mp_orig->flag;
}
}
+ const VArray<bool> hide_poly_orig = attributes_orig.lookup_or_default<bool>(
+ ".hide_poly", ATTR_DOMAIN_FACE, false);
+ bke::SpanAttributeWriter<bool> hide_poly_eval =
+ attributes_eval.lookup_or_add_for_write_only_span<bool>(".hide_poly", ATTR_DOMAIN_FACE);
+ for (const int i : IndexRange(me_eval->totpoly)) {
+ const int orig_poly_index = index_array[i];
+ if (orig_poly_index != ORIGINDEX_NONE) {
+ hide_poly_eval.span[i] = hide_poly_orig[orig_poly_index];
+ }
+ }
+ hide_poly_eval.finish();
updated = true;
}
}
if (updated) {
- if (flag & ME_HIDE) {
+ if (flush_hidden) {
BKE_mesh_batch_cache_dirty_tag(me_eval, BKE_MESH_BATCH_DIRTY_ALL);
}
else {
@@ -115,59 +138,79 @@ void paintface_flush_flags(bContext *C, Object *ob, short flag)
void paintface_hide(bContext *C, Object *ob, const bool unselected)
{
+ using namespace blender;
Mesh *me = BKE_mesh_from_object(ob);
if (me == nullptr || me->totpoly == 0) {
return;
}
+ bke::MutableAttributeAccessor attributes = bke::mesh_attributes_for_write(*me);
+ bke::SpanAttributeWriter<bool> hide_poly = attributes.lookup_or_add_for_write_span<bool>(
+ ".hide_poly", ATTR_DOMAIN_FACE);
+
for (int i = 0; i < me->totpoly; i++) {
MPoly *mpoly = &me->mpoly[i];
- if ((mpoly->flag & ME_HIDE) == 0) {
+ if (!hide_poly.span[i]) {
if (((mpoly->flag & ME_FACE_SEL) == 0) == unselected) {
- mpoly->flag |= ME_HIDE;
+ hide_poly.span[i] = true;
}
}
- if (mpoly->flag & ME_HIDE) {
+ if (hide_poly.span[i]) {
mpoly->flag &= ~ME_FACE_SEL;
}
}
+ hide_poly.finish();
+
BKE_mesh_flush_hidden_from_polys(me);
- paintface_flush_flags(C, ob, SELECT | ME_HIDE);
+ paintface_flush_flags(C, ob, true, true);
}
void paintface_reveal(bContext *C, Object *ob, const bool select)
{
+ using namespace blender;
Mesh *me = BKE_mesh_from_object(ob);
if (me == nullptr || me->totpoly == 0) {
return;
}
- for (int i = 0; i < me->totpoly; i++) {
- MPoly *mpoly = &me->mpoly[i];
- if (mpoly->flag & ME_HIDE) {
- SET_FLAG_FROM_TEST(mpoly->flag, select, ME_FACE_SEL);
- mpoly->flag &= ~ME_HIDE;
+ bke::MutableAttributeAccessor attributes = bke::mesh_attributes_for_write(*me);
+
+ if (select) {
+ const VArray<bool> hide_poly = attributes.lookup_or_default<bool>(
+ ".hide_poly", ATTR_DOMAIN_FACE, false);
+ for (int i = 0; i < me->totpoly; i++) {
+ MPoly *mpoly = &me->mpoly[i];
+ if (hide_poly[i]) {
+ mpoly->flag |= ME_FACE_SEL;
+ }
}
}
+ attributes.remove(".hide_poly");
+
BKE_mesh_flush_hidden_from_polys(me);
- paintface_flush_flags(C, ob, SELECT | ME_HIDE);
+ paintface_flush_flags(C, ob, true, true);
}
/* Set object-mode face selection seams based on edge data, uses hash table to find seam edges. */
static void select_linked_tfaces_with_seams(Mesh *me, const uint index, const bool select)
{
+ using namespace blender;
bool do_it = true;
bool mark = false;
BLI_bitmap *edge_tag = BLI_BITMAP_NEW(me->totedge, __func__);
BLI_bitmap *poly_tag = BLI_BITMAP_NEW(me->totpoly, __func__);
+ bke::AttributeAccessor attributes = bke::mesh_attributes(*me);
+ const VArray<bool> hide_poly = attributes.lookup_or_default<bool>(
+ ".hide_poly", ATTR_DOMAIN_FACE, false);
+
if (index != (uint)-1) {
/* only put face under cursor in array */
MPoly *mp = &me->mpoly[index];
@@ -178,7 +221,7 @@ static void select_linked_tfaces_with_seams(Mesh *me, const uint index, const bo
/* fill array by selection */
for (int i = 0; i < me->totpoly; i++) {
MPoly *mp = &me->mpoly[i];
- if (mp->flag & ME_HIDE) {
+ if (hide_poly[i]) {
/* pass */
}
else if (mp->flag & ME_FACE_SEL) {
@@ -194,7 +237,7 @@ static void select_linked_tfaces_with_seams(Mesh *me, const uint index, const bo
/* expand selection */
for (int i = 0; i < me->totpoly; i++) {
MPoly *mp = &me->mpoly[i];
- if (mp->flag & ME_HIDE) {
+ if (hide_poly[i]) {
continue;
}
@@ -249,22 +292,27 @@ void paintface_select_linked(bContext *C, Object *ob, const int mval[2], const b
select_linked_tfaces_with_seams(me, index, select);
- paintface_flush_flags(C, ob, SELECT);
+ paintface_flush_flags(C, ob, true, false);
}
bool paintface_deselect_all_visible(bContext *C, Object *ob, int action, bool flush_flags)
{
+ using namespace blender;
Mesh *me = BKE_mesh_from_object(ob);
if (me == nullptr) {
return false;
}
+ bke::AttributeAccessor attributes = bke::mesh_attributes(*me);
+ const VArray<bool> hide_poly = attributes.lookup_or_default<bool>(
+ ".hide_poly", ATTR_DOMAIN_FACE, false);
+
if (action == SEL_TOGGLE) {
action = SEL_SELECT;
for (int i = 0; i < me->totpoly; i++) {
MPoly *mpoly = &me->mpoly[i];
- if ((mpoly->flag & ME_HIDE) == 0 && mpoly->flag & ME_FACE_SEL) {
+ if (!hide_poly[i] && mpoly->flag & ME_FACE_SEL) {
action = SEL_DESELECT;
break;
}
@@ -275,7 +323,7 @@ bool paintface_deselect_all_visible(bContext *C, Object *ob, int action, bool fl
for (int i = 0; i < me->totpoly; i++) {
MPoly *mpoly = &me->mpoly[i];
- if ((mpoly->flag & ME_HIDE) == 0) {
+ if (!hide_poly[i]) {
switch (action) {
case SEL_SELECT:
if ((mpoly->flag & ME_FACE_SEL) == 0) {
@@ -299,7 +347,7 @@ bool paintface_deselect_all_visible(bContext *C, Object *ob, int action, bool fl
if (changed) {
if (flush_flags) {
- paintface_flush_flags(C, ob, SELECT);
+ paintface_flush_flags(C, ob, true, false);
}
}
return changed;
@@ -307,6 +355,7 @@ bool paintface_deselect_all_visible(bContext *C, Object *ob, int action, bool fl
bool paintface_minmax(Object *ob, float r_min[3], float r_max[3])
{
+ using namespace blender;
bool ok = false;
float vec[3], bmat[3][3];
@@ -318,9 +367,13 @@ bool paintface_minmax(Object *ob, float r_min[3], float r_max[3])
copy_m3_m4(bmat, ob->obmat);
+ bke::AttributeAccessor attributes = bke::mesh_attributes(*me);
+ const VArray<bool> hide_poly = attributes.lookup_or_default<bool>(
+ ".hide_poly", ATTR_DOMAIN_FACE, false);
+
for (int i = 0; i < me->totpoly; i++) {
MPoly *mp = &me->mpoly[i];
- if (mp->flag & ME_HIDE || !(mp->flag & ME_FACE_SEL)) {
+ if (hide_poly[i] || !(mp->flag & ME_FACE_SEL)) {
continue;
}
@@ -342,6 +395,7 @@ bool paintface_mouse_select(bContext *C,
const SelectPick_Params *params,
Object *ob)
{
+ using namespace blender;
MPoly *mpoly_sel = nullptr;
uint index;
bool changed = false;
@@ -350,10 +404,14 @@ bool paintface_mouse_select(bContext *C,
/* Get the face under the cursor */
Mesh *me = BKE_mesh_from_object(ob);
+ bke::AttributeAccessor attributes = bke::mesh_attributes(*me);
+ const VArray<bool> hide_poly = attributes.lookup_or_default<bool>(
+ ".hide_poly", ATTR_DOMAIN_FACE, false);
+
if (ED_mesh_pick_face(C, ob, mval, ED_MESH_PICK_DEFAULT_FACE_DIST, &index)) {
if (index < me->totpoly) {
mpoly_sel = me->mpoly + index;
- if ((mpoly_sel->flag & ME_HIDE) == 0) {
+ if (!hide_poly[index]) {
found = true;
}
}
@@ -402,7 +460,7 @@ bool paintface_mouse_select(bContext *C,
/* image window redraw */
- paintface_flush_flags(C, ob, SELECT);
+ paintface_flush_flags(C, ob, true, false);
ED_region_tag_redraw(CTX_wm_region(C)); /* XXX: should redraw all 3D views. */
changed = true;
}
@@ -463,17 +521,24 @@ void paintvert_tag_select_update(bContext *C, Object *ob)
bool paintvert_deselect_all_visible(Object *ob, int action, bool flush_flags)
{
+ using namespace blender;
Mesh *me = BKE_mesh_from_object(ob);
if (me == nullptr) {
return false;
}
+ bke::AttributeAccessor attributes = bke::mesh_attributes(*me);
+ const VArray<bool> hide_vert = attributes.lookup_or_default<bool>(
+ ".hide_vert", ATTR_DOMAIN_POINT, false);
+ const VArray<bool> hide_poly = attributes.lookup_or_default<bool>(
+ ".hide_poly", ATTR_DOMAIN_FACE, false);
+
if (action == SEL_TOGGLE) {
action = SEL_SELECT;
for (int i = 0; i < me->totvert; i++) {
MVert *mvert = &me->mvert[i];
- if ((mvert->flag & ME_HIDE) == 0 && mvert->flag & SELECT) {
+ if (!hide_poly[i] && mvert->flag & SELECT) {
action = SEL_DESELECT;
break;
}
@@ -483,7 +548,7 @@ bool paintvert_deselect_all_visible(Object *ob, int action, bool flush_flags)
bool changed = false;
for (int i = 0; i < me->totvert; i++) {
MVert *mvert = &me->mvert[i];
- if ((mvert->flag & ME_HIDE) == 0) {
+ if (!hide_vert[i]) {
switch (action) {
case SEL_SELECT:
if ((mvert->flag & SELECT) == 0) {
@@ -526,6 +591,7 @@ bool paintvert_deselect_all_visible(Object *ob, int action, bool flush_flags)
void paintvert_select_ungrouped(Object *ob, bool extend, bool flush_flags)
{
+ using namespace blender;
Mesh *me = BKE_mesh_from_object(ob);
if (me == nullptr || me->dvert == nullptr) {
@@ -536,10 +602,14 @@ void paintvert_select_ungrouped(Object *ob, bool extend, bool flush_flags)
paintvert_deselect_all_visible(ob, SEL_DESELECT, false);
}
+ bke::AttributeAccessor attributes = bke::mesh_attributes(*me);
+ const VArray<bool> hide_poly = attributes.lookup_or_default<bool>(
+ ".hide_poly", ATTR_DOMAIN_FACE, false);
+
for (int i = 0; i < me->totvert; i++) {
MVert *mv = &me->mvert[i];
MDeformVert *dv = &me->dvert[i];
- if ((mv->flag & ME_HIDE) == 0) {
+ if (!hide_poly[i]) {
if (dv->dw == nullptr) {
/* if null weight then not grouped */
mv->flag |= SELECT;
@@ -554,25 +624,30 @@ void paintvert_select_ungrouped(Object *ob, bool extend, bool flush_flags)
void paintvert_hide(bContext *C, Object *ob, const bool unselected)
{
- Mesh *const me = BKE_mesh_from_object(ob);
-
- if (me == nullptr || me->totvert == 0) {
+ using namespace blender;
+ Mesh *me = BKE_mesh_from_object(ob);
+ if (me == NULL || me->totvert == 0) {
return;
}
- for (int i = 0; i < me->totvert; i++) {
- MVert *const mvert = &me->mvert[i];
+ bke::MutableAttributeAccessor attributes = bke::mesh_attributes_for_write(*me);
+ bke::SpanAttributeWriter<bool> hide_vert = attributes.lookup_or_add_for_write_span<bool>(
+ ".hide_vert", ATTR_DOMAIN_POINT);
+ MutableSpan<MVert> verts(me->mvert, me->totvert);
- if ((mvert->flag & ME_HIDE) == 0) {
- if (((mvert->flag & SELECT) == 0) == unselected) {
- mvert->flag |= ME_HIDE;
+ for (const int i : verts.index_range()) {
+ MVert &vert = verts[i];
+ if (!hide_vert.span[i]) {
+ if (((vert.flag & SELECT) == 0) == unselected) {
+ hide_vert.span[i] = true;
}
}
- if (mvert->flag & ME_HIDE) {
- mvert->flag &= ~SELECT;
+ if (hide_vert.span[i]) {
+ vert.flag &= ~SELECT;
}
}
+ hide_vert.finish();
BKE_mesh_flush_hidden_from_verts(me);
@@ -582,21 +657,27 @@ void paintvert_hide(bContext *C, Object *ob, const bool unselected)
void paintvert_reveal(bContext *C, Object *ob, const bool select)
{
- Mesh *const me = BKE_mesh_from_object(ob);
-
- if (me == nullptr || me->totvert == 0) {
+ using namespace blender;
+ Mesh *me = BKE_mesh_from_object(ob);
+ if (me == NULL || me->totvert == 0) {
return;
}
- for (int i = 0; i < me->totvert; i++) {
- MVert *const mvert = &me->mvert[i];
+ bke::MutableAttributeAccessor attributes = bke::mesh_attributes_for_write(*me);
+ const VArray<bool> hide_vert = attributes.lookup_or_default<bool>(
+ ".hide_vert", ATTR_DOMAIN_POINT, false);
+ MutableSpan<MVert> verts(me->mvert, me->totvert);
- if (mvert->flag & ME_HIDE) {
- SET_FLAG_FROM_TEST(mvert->flag, select, SELECT);
- mvert->flag &= ~ME_HIDE;
+ for (const int i : verts.index_range()) {
+ MVert &vert = verts[i];
+ if (hide_vert[i]) {
+ SET_FLAG_FROM_TEST(vert.flag, select, SELECT);
}
}
+ /* Remove the hide attribute to reveal all vertices. */
+ attributes.remove(".hide_vert");
+
BKE_mesh_flush_hidden_from_verts(me);
paintvert_flush_flags(ob);
diff --git a/source/blender/editors/mesh/editmesh_knife.c b/source/blender/editors/mesh/editmesh_knife.c
index 5680865ae67..d4c5504615a 100644
--- a/source/blender/editors/mesh/editmesh_knife.c
+++ b/source/blender/editors/mesh/editmesh_knife.c
@@ -4300,7 +4300,7 @@ static void knifetool_finish_single_pre(KnifeTool_OpData *kcd, Object *ob)
}
/**
- * A post version is needed to to delay recalculating tessellation after making cuts.
+ * A post version is needed to delay recalculating tessellation after making cuts.
* Without this, knife-project can't use the BVH tree to select geometry after a cut, see: T98349.
*/
static void knifetool_finish_single_post(KnifeTool_OpData *UNUSED(kcd), Object *ob)
diff --git a/source/blender/editors/mesh/editmesh_tools.c b/source/blender/editors/mesh/editmesh_tools.c
index c5add97fb00..7de5ad9f151 100644
--- a/source/blender/editors/mesh/editmesh_tools.c
+++ b/source/blender/editors/mesh/editmesh_tools.c
@@ -936,7 +936,7 @@ static int edbm_add_edge_face_exec(bContext *C, wmOperator *op)
Object *obedit = objects[ob_index];
BMEditMesh *em = BKE_editmesh_from_object(obedit);
- if ((em->bm->totvertsel == 0) && (em->bm->totedgesel == 0) && (em->bm->totvertsel == 0)) {
+ if ((em->bm->totvertsel == 0) && (em->bm->totedgesel == 0) && (em->bm->totfacesel == 0)) {
continue;
}
diff --git a/source/blender/editors/mesh/editmesh_undo.c b/source/blender/editors/mesh/editmesh_undo.c
index d75c92f963f..af8084e16c4 100644
--- a/source/blender/editors/mesh/editmesh_undo.c
+++ b/source/blender/editors/mesh/editmesh_undo.c
@@ -594,6 +594,10 @@ static void *undomesh_from_editmesh(UndoMesh *um, BMEditMesh *em, Key *key, Undo
/* Uncomment for troubleshooting. */
// BM_mesh_validate(em->bm);
+ /* Copy the ID name characters to the mesh so code that depends on accessing the ID type can work
+ * on it. Necessary to use the attribute API. */
+ strcpy(um->me.id.name, "MEundomesh_from_editmesh");
+
BM_mesh_bm_to_me(
NULL,
em->bm,
diff --git a/source/blender/editors/mesh/editmesh_utils.c b/source/blender/editors/mesh/editmesh_utils.c
index 195a3686b3b..e931dd02a9e 100644
--- a/source/blender/editors/mesh/editmesh_utils.c
+++ b/source/blender/editors/mesh/editmesh_utils.c
@@ -593,6 +593,31 @@ UvMapVert *BM_uv_vert_map_at_index(UvVertMap *vmap, uint v)
return vmap->vert[v];
}
+static void bm_uv_ensure_head_table(UvElementMap *element_map)
+{
+ if (element_map->head_table) {
+ return;
+ }
+
+ /* For each UvElement, locate the "separate" UvElement that precedes it in the linked list. */
+ element_map->head_table = MEM_mallocN(sizeof(*element_map->head_table) * element_map->total_uvs,
+ "uv_element_map_head_table");
+ UvElement **head_table = element_map->head_table;
+ for (int i = 0; i < element_map->total_uvs; i++) {
+ UvElement *head = element_map->storage + i;
+ if (head->separate) {
+ UvElement *element = head;
+ while (element) {
+ head_table[element - element_map->storage] = head;
+ element = element->next;
+ if (element && element->separate) {
+ break;
+ }
+ }
+ }
+ }
+}
+
#define INVALID_ISLAND ((unsigned int)-1)
static void bm_uv_assign_island(UvElementMap *element_map,
@@ -620,23 +645,9 @@ static int bm_uv_edge_select_build_islands(UvElementMap *element_map,
bool uv_selected,
int cd_loop_uv_offset)
{
- int total_uvs = element_map->total_uvs;
+ bm_uv_ensure_head_table(element_map);
- /* For each UvElement, locate the "separate" UvElement that precedes it in the linked list. */
- UvElement **head_table = MEM_mallocN(sizeof(*head_table) * total_uvs, "uv_island_head_table");
- for (int i = 0; i < total_uvs; i++) {
- UvElement *head = element_map->storage + i;
- if (head->separate) {
- UvElement *element = head;
- while (element) {
- head_table[element - element_map->storage] = head;
- element = element->next;
- if (element && element->separate) {
- break;
- }
- }
- }
- }
+ int total_uvs = element_map->total_uvs;
/* Depth first search the graph, building islands as we go. */
int nislands = 0;
@@ -676,7 +687,7 @@ static int bm_uv_edge_select_build_islands(UvElementMap *element_map,
if (!uv_selected || uvedit_edge_select_test(scene, element->l, cd_loop_uv_offset)) {
UvElement *next = BM_uv_element_get(element_map, element->l->next->f, element->l->next);
if (next->island == INVALID_ISLAND) {
- UvElement *tail = head_table[next - element_map->storage];
+ UvElement *tail = element_map->head_table[next - element_map->storage];
stack_uv[stacksize_uv++] = tail;
while (tail) {
bm_uv_assign_island(element_map, tail, nislands, map, islandbuf, islandbufsize++);
@@ -692,7 +703,7 @@ static int bm_uv_edge_select_build_islands(UvElementMap *element_map,
if (!uv_selected || uvedit_edge_select_test(scene, element->l->prev, cd_loop_uv_offset)) {
UvElement *prev = BM_uv_element_get(element_map, element->l->prev->f, element->l->prev);
if (prev->island == INVALID_ISLAND) {
- UvElement *tail = head_table[prev - element_map->storage];
+ UvElement *tail = element_map->head_table[prev - element_map->storage];
stack_uv[stacksize_uv++] = tail;
while (tail) {
bm_uv_assign_island(element_map, tail, nislands, map, islandbuf, islandbufsize++);
@@ -716,11 +727,129 @@ static int bm_uv_edge_select_build_islands(UvElementMap *element_map,
BLI_assert(islandbufsize == total_uvs);
MEM_SAFE_FREE(stack_uv);
- MEM_SAFE_FREE(head_table);
+ MEM_SAFE_FREE(element_map->head_table);
return nislands;
}
+static void bm_uv_build_islands(UvElementMap *element_map,
+ BMesh *bm,
+ const Scene *scene,
+ bool uv_selected)
+{
+ int totuv = element_map->total_uvs;
+ int nislands = 0;
+ int islandbufsize = 0;
+
+ /* map holds the map from current vmap->buf to the new, sorted map */
+ uint *map = MEM_mallocN(sizeof(*map) * totuv, "uvelement_remap");
+ BMFace **stack = MEM_mallocN(sizeof(*stack) * bm->totface, "uv_island_face_stack");
+ UvElement *islandbuf = MEM_callocN(sizeof(*islandbuf) * totuv, "uvelement_island_buffer");
+ /* Island number for BMFaces. */
+ int *island_number = MEM_callocN(sizeof(*island_number) * bm->totface, "uv_island_number_face");
+ copy_vn_i(island_number, bm->totface, INVALID_ISLAND);
+
+ const int cd_loop_uv_offset = CustomData_get_offset(&bm->ldata, CD_MLOOPUV);
+
+ const bool use_uv_edge_connectivity = scene->toolsettings->uv_flag & UV_SYNC_SELECTION ?
+ scene->toolsettings->selectmode & SCE_SELECT_EDGE :
+ scene->toolsettings->uv_selectmode & UV_SELECT_EDGE;
+ if (use_uv_edge_connectivity) {
+ nislands = bm_uv_edge_select_build_islands(
+ element_map, scene, islandbuf, map, uv_selected, cd_loop_uv_offset);
+ islandbufsize = totuv;
+ }
+
+ for (int i = 0; i < totuv; i++) {
+ if (element_map->storage[i].island == INVALID_ISLAND) {
+ int stacksize = 0;
+ element_map->storage[i].island = nislands;
+ stack[0] = element_map->storage[i].l->f;
+ island_number[BM_elem_index_get(stack[0])] = nislands;
+ stacksize = 1;
+
+ while (stacksize > 0) {
+ BMFace *efa = stack[--stacksize];
+
+ BMLoop *l;
+ BMIter liter;
+ BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
+ if (uv_selected && !uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) {
+ continue;
+ }
+
+ UvElement *initelement = element_map->vertex[BM_elem_index_get(l->v)];
+
+ for (UvElement *element = initelement; element; element = element->next) {
+ if (element->separate) {
+ initelement = element;
+ }
+
+ if (element->l->f == efa) {
+ /* found the uv corresponding to our face and vertex.
+ * Now fill it to the buffer */
+ bm_uv_assign_island(element_map, element, nislands, map, islandbuf, islandbufsize++);
+
+ for (element = initelement; element; element = element->next) {
+ if (element->separate && element != initelement) {
+ break;
+ }
+
+ if (island_number[BM_elem_index_get(element->l->f)] == INVALID_ISLAND) {
+ stack[stacksize++] = element->l->f;
+ island_number[BM_elem_index_get(element->l->f)] = nislands;
+ }
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ nislands++;
+ }
+ }
+
+ MEM_SAFE_FREE(island_number);
+
+ /* remap */
+ for (int i = 0; i < bm->totvert; i++) {
+ /* important since we may do selection only. Some of these may be NULL */
+ if (element_map->vertex[i]) {
+ element_map->vertex[i] = &islandbuf[map[element_map->vertex[i] - element_map->storage]];
+ }
+ }
+
+ element_map->island_indices = MEM_callocN(sizeof(*element_map->island_indices) * nislands,
+ __func__);
+ element_map->island_total_uvs = MEM_callocN(sizeof(*element_map->island_total_uvs) * nislands,
+ __func__);
+ element_map->island_total_unique_uvs = MEM_callocN(
+ sizeof(*element_map->island_total_unique_uvs) * nislands, __func__);
+ int j = 0;
+ for (int i = 0; i < totuv; i++) {
+ UvElement *next = element_map->storage[i].next;
+ islandbuf[map[i]].next = next ? &islandbuf[map[next - element_map->storage]] : NULL;
+
+ if (islandbuf[i].island != j) {
+ j++;
+ element_map->island_indices[j] = i;
+ }
+ BLI_assert(islandbuf[i].island == j);
+ element_map->island_total_uvs[j]++;
+ if (islandbuf[i].separate) {
+ element_map->island_total_unique_uvs[j]++;
+ }
+ }
+
+ MEM_SAFE_FREE(element_map->storage);
+ element_map->storage = islandbuf;
+ islandbuf = NULL;
+ element_map->total_islands = nislands;
+ MEM_SAFE_FREE(stack);
+ MEM_SAFE_FREE(map);
+}
+
UvElementMap *BM_uv_element_map_create(BMesh *bm,
const Scene *scene,
const bool uv_selected,
@@ -824,12 +953,13 @@ UvElementMap *BM_uv_element_map_create(BMesh *bm,
winding[j] = cross_poly_v2(tf_uv, efa->len) > 0;
}
}
+ BLI_buffer_free(&tf_uv_buf);
/* For each BMVert, sort associated linked list into unique uvs. */
- int i;
- BM_ITER_MESH_INDEX (ev, &iter, bm, BM_VERTS_OF_MESH, i) {
+ int ev_index;
+ BM_ITER_MESH_INDEX (ev, &iter, bm, BM_VERTS_OF_MESH, ev_index) {
UvElement *newvlist = NULL;
- UvElement *vlist = element_map->vertex[i];
+ UvElement *vlist = element_map->vertex[ev_index];
while (vlist) {
/* Detach head from unsorted list. */
@@ -845,21 +975,32 @@ UvElementMap *BM_uv_element_map_create(BMesh *bm,
UvElement *lastv = NULL;
UvElement *iterv = vlist;
- /* Scan through unsorted list, finding UvElements which match `v`. */
+ /* Scan through unsorted list, finding UvElements which are connected to `v`. */
while (iterv) {
UvElement *next = iterv->next;
-
luv = BM_ELEM_CD_GET_VOID_P(iterv->l, cd_loop_uv_offset);
- const float *uv2 = luv->uv;
- const bool uv2_vert_sel = uvedit_uv_select_test(scene, iterv->l, cd_loop_uv_offset);
- /* Check if the uv loops share the same selection state (if not, they are not connected as
- * they have been ripped or other edit commands have separated them). */
- const bool connected = (uv_vert_sel == uv2_vert_sel) &&
- compare_v2v2(uv2, uv, STD_UV_CONNECT_LIMIT);
+ bool connected = true; /* Assume connected unless we can prove otherwise. */
+
+ if (connected) {
+ /* Are the two UVs close together? */
+ const float *uv2 = luv->uv;
+ connected = compare_v2v2(uv2, uv, STD_UV_CONNECT_LIMIT);
+ }
+
+ if (connected) {
+ /* Check if the uv loops share the same selection state (if not, they are not connected
+ * as they have been ripped or other edit commands have separated them). */
+ const bool uv2_vert_sel = uvedit_uv_select_test(scene, iterv->l, cd_loop_uv_offset);
+ connected = (uv_vert_sel == uv2_vert_sel);
+ }
+
+ if (connected && use_winding) {
+ connected = winding[BM_elem_index_get(iterv->l->f)] ==
+ winding[BM_elem_index_get(v->l->f)];
+ }
- if (connected && (!use_winding || winding[BM_elem_index_get(iterv->l->f)] ==
- winding[BM_elem_index_get(v->l->f)])) {
+ if (connected) {
if (lastv) {
lastv->next = next;
}
@@ -881,125 +1022,18 @@ UvElementMap *BM_uv_element_map_create(BMesh *bm,
}
/* Write back sorted list. */
- element_map->vertex[i] = newvlist;
+ element_map->vertex[ev_index] = newvlist;
}
MEM_SAFE_FREE(winding);
+ /* at this point, every UvElement in vert points to a UvElement sharing the same vertex.
+ * Now we should sort uv's in islands. */
if (do_islands) {
- uint *map;
- UvElement *islandbuf;
-
- int nislands = 0, islandbufsize = 0;
-
- /* map holds the map from current vmap->buf to the new, sorted map */
- map = MEM_mallocN(sizeof(*map) * totuv, "uvelement_remap");
- BMFace **stack = MEM_mallocN(sizeof(*stack) * bm->totface, "uv_island_face_stack");
- islandbuf = MEM_callocN(sizeof(*islandbuf) * totuv, "uvelement_island_buffer");
- /* Island number for BMFaces. */
- int *island_number = MEM_callocN(sizeof(*island_number) * bm->totface,
- "uv_island_number_face");
- copy_vn_i(island_number, bm->totface, INVALID_ISLAND);
-
- const bool use_uv_edge_connectivity = scene->toolsettings->uv_flag & UV_SYNC_SELECTION ?
- scene->toolsettings->selectmode & SCE_SELECT_EDGE :
- scene->toolsettings->uv_selectmode & UV_SELECT_EDGE;
- if (use_uv_edge_connectivity) {
- nislands = bm_uv_edge_select_build_islands(
- element_map, scene, islandbuf, map, uv_selected, cd_loop_uv_offset);
- islandbufsize = totuv;
- }
-
- /* at this point, every UvElement in vert points to a UvElement sharing the same vertex.
- * Now we should sort uv's in islands. */
- for (i = 0; i < totuv; i++) {
- if (element_map->storage[i].island == INVALID_ISLAND) {
- int stacksize = 0;
- element_map->storage[i].island = nislands;
- stack[0] = element_map->storage[i].l->f;
- island_number[BM_elem_index_get(stack[0])] = nislands;
- stacksize = 1;
-
- while (stacksize > 0) {
- efa = stack[--stacksize];
-
- BMLoop *l;
- BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
- if (uv_selected && !uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) {
- continue;
- }
-
- UvElement *initelement = element_map->vertex[BM_elem_index_get(l->v)];
-
- for (UvElement *element = initelement; element; element = element->next) {
- if (element->separate) {
- initelement = element;
- }
-
- if (element->l->f == efa) {
- /* found the uv corresponding to our face and vertex.
- * Now fill it to the buffer */
- bm_uv_assign_island(
- element_map, element, nislands, map, islandbuf, islandbufsize++);
-
- for (element = initelement; element; element = element->next) {
- if (element->separate && element != initelement) {
- break;
- }
-
- if (island_number[BM_elem_index_get(element->l->f)] == INVALID_ISLAND) {
- stack[stacksize++] = element->l->f;
- island_number[BM_elem_index_get(element->l->f)] = nislands;
- }
- }
- break;
- }
- }
- }
- }
-
- nislands++;
- }
- }
-
- MEM_SAFE_FREE(island_number);
-
- /* remap */
- for (i = 0; i < bm->totvert; i++) {
- /* important since we may do selection only. Some of these may be NULL */
- if (element_map->vertex[i]) {
- element_map->vertex[i] = &islandbuf[map[element_map->vertex[i] - element_map->storage]];
- }
- }
-
- element_map->islandIndices = MEM_callocN(sizeof(*element_map->islandIndices) * nislands,
- "UvElementMap_island_indices");
- j = 0;
- for (i = 0; i < totuv; i++) {
- UvElement *element = element_map->storage[i].next;
- if (element == NULL) {
- islandbuf[map[i]].next = NULL;
- }
- else {
- islandbuf[map[i]].next = &islandbuf[map[element - element_map->storage]];
- }
-
- if (islandbuf[i].island != j) {
- j++;
- element_map->islandIndices[j] = i;
- }
- }
-
- MEM_SAFE_FREE(element_map->storage);
- element_map->storage = islandbuf;
- islandbuf = NULL;
- element_map->totalIslands = nislands;
- MEM_SAFE_FREE(stack);
- MEM_SAFE_FREE(map);
+ bm_uv_build_islands(element_map, bm, scene, uv_selected);
}
- BLI_buffer_free(&tf_uv_buf);
-
+ /* TODO: Confirm element_map->total_unique_uvs doesn't require recalculating. */
element_map->total_unique_uvs = 0;
for (int i = 0; i < element_map->total_uvs; i++) {
if (element_map->storage[i].separate) {
@@ -1028,7 +1062,10 @@ void BM_uv_element_map_free(UvElementMap *element_map)
if (element_map) {
MEM_SAFE_FREE(element_map->storage);
MEM_SAFE_FREE(element_map->vertex);
- MEM_SAFE_FREE(element_map->islandIndices);
+ MEM_SAFE_FREE(element_map->head_table);
+ MEM_SAFE_FREE(element_map->island_indices);
+ MEM_SAFE_FREE(element_map->island_total_uvs);
+ MEM_SAFE_FREE(element_map->island_total_unique_uvs);
MEM_SAFE_FREE(element_map);
}
}
@@ -1524,7 +1561,7 @@ void EDBM_update(Mesh *mesh, const struct EDBMUpdate_Params *params)
}
if (params->is_destructive) {
- /* TODO(campbell): we may be able to remove this now! */
+ /* TODO(@campbellbarton): we may be able to remove this now! */
// BM_mesh_elem_table_free(em->bm, BM_ALL_NOLOOP);
}
else {
diff --git a/source/blender/editors/mesh/meshtools.cc b/source/blender/editors/mesh/meshtools.cc
index 9e28e1bafdd..b1004b23a21 100644
--- a/source/blender/editors/mesh/meshtools.cc
+++ b/source/blender/editors/mesh/meshtools.cc
@@ -1329,6 +1329,7 @@ bool ED_mesh_pick_face_vert(
*/
struct VertPickData {
const MVert *mvert;
+ const bool *hide_vert;
const float *mval_f; /* [2] */
ARegion *region;
@@ -1343,16 +1344,16 @@ static void ed_mesh_pick_vert__mapFunc(void *userData,
const float UNUSED(no[3]))
{
VertPickData *data = static_cast<VertPickData *>(userData);
- if ((data->mvert[index].flag & ME_HIDE) == 0) {
- float sco[2];
-
- if (ED_view3d_project_float_object(data->region, co, sco, V3D_PROJ_TEST_CLIP_DEFAULT) ==
- V3D_PROJ_RET_OK) {
- const float len = len_manhattan_v2v2(data->mval_f, sco);
- if (len < data->len_best) {
- data->len_best = len;
- data->v_idx_best = index;
- }
+ if (data->hide_vert && data->hide_vert[index]) {
+ return;
+ }
+ float sco[2];
+ if (ED_view3d_project_float_object(data->region, co, sco, V3D_PROJ_TEST_CLIP_DEFAULT) ==
+ V3D_PROJ_RET_OK) {
+ const float len = len_manhattan_v2v2(data->mval_f, sco);
+ if (len < data->len_best) {
+ data->len_best = len;
+ data->v_idx_best = index;
}
}
}
@@ -1416,6 +1417,8 @@ bool ED_mesh_pick_vert(
data.mval_f = mval_f;
data.len_best = FLT_MAX;
data.v_idx_best = -1;
+ data.hide_vert = (const bool *)CustomData_get_layer_named(
+ &me_eval->vdata, CD_PROP_BOOL, ".hide_vert");
BKE_mesh_foreach_mapped_vert(me_eval, ed_mesh_pick_vert__mapFunc, &data, MESH_FOREACH_NOP);
diff --git a/source/blender/editors/metaball/mball_edit.c b/source/blender/editors/metaball/mball_edit.c
index 06a649e5b6c..6a5d620b546 100644
--- a/source/blender/editors/metaball/mball_edit.c
+++ b/source/blender/editors/metaball/mball_edit.c
@@ -744,7 +744,7 @@ Base *ED_mball_base_and_elem_from_select_buffer(Base **bases,
const uint hit_object = select_id & 0xFFFF;
Base *base = NULL;
MetaElem *ml = NULL;
- /* TODO(campbell): optimize, eg: sort & binary search. */
+ /* TODO(@campbellbarton): optimize, eg: sort & binary search. */
for (uint base_index = 0; base_index < bases_len; base_index++) {
if (bases[base_index]->object->runtime.select_id == hit_object) {
base = bases[base_index];
diff --git a/source/blender/editors/object/object_select.c b/source/blender/editors/object/object_select.c
index c3d8fb9cfe5..6f7fc2efa61 100644
--- a/source/blender/editors/object/object_select.c
+++ b/source/blender/editors/object/object_select.c
@@ -1128,7 +1128,7 @@ static int object_select_all_exec(bContext *C, wmOperator *op)
return OPERATOR_FINISHED;
}
if (any_visible == false) {
- /* TODO(campbell): Looks like we could remove this,
+ /* TODO(@campbellbarton): Looks like we could remove this,
* if not comment should say why its needed. */
return OPERATOR_PASS_THROUGH;
}
diff --git a/source/blender/editors/object/object_vgroup.c b/source/blender/editors/object/object_vgroup.c
index 17b7fe7fe5e..eb5aaf7ef61 100644
--- a/source/blender/editors/object/object_vgroup.c
+++ b/source/blender/editors/object/object_vgroup.c
@@ -891,7 +891,7 @@ void ED_vgroup_vert_remove(Object *ob, bDeformGroup *dg, int vertnum)
* deform group.
*/
- /* TODO(campbell): This is slow in a loop, better pass def_nr directly,
+ /* TODO(@campbellbarton): This is slow in a loop, better pass def_nr directly,
* but leave for later. */
const ListBase *defbase = BKE_object_defgroup_list(ob);
const int def_nr = BLI_findindex(defbase, dg);
@@ -1034,6 +1034,7 @@ static void vgroup_select_verts(Object *ob, int select)
}
else {
if (me->dvert) {
+ const bool *hide_vert = CustomData_get_layer_named(&me->vdata, CD_PROP_BOOL, ".hide_vert");
MVert *mv;
MDeformVert *dv;
int i;
@@ -1042,7 +1043,7 @@ static void vgroup_select_verts(Object *ob, int select)
dv = me->dvert;
for (i = 0; i < me->totvert; i++, mv++, dv++) {
- if (!(mv->flag & ME_HIDE)) {
+ if (hide_vert != NULL && !hide_vert[i]) {
if (BKE_defvert_find_index(dv, def_nr)) {
if (select) {
mv->flag |= SELECT;
@@ -1118,7 +1119,7 @@ static void vgroup_duplicate(Object *ob)
BKE_object_defgroup_active_index_set(ob, BLI_listbase_count(defbase));
icdg = BKE_object_defgroup_active_index_get(ob) - 1;
- /* TODO(campbell): we might want to allow only copy selected verts here? */
+ /* TODO(@campbellbarton): we might want to allow only copy selected verts here? */
ED_vgroup_parray_alloc(ob->data, &dvert_array, &dvert_tot, false);
if (dvert_array) {
@@ -1930,7 +1931,11 @@ static void vgroup_smooth_subset(Object *ob,
#define IS_BM_VERT_READ(v) (use_hide ? (BM_elem_flag_test(v, BM_ELEM_HIDDEN) == 0) : true)
#define IS_BM_VERT_WRITE(v) (use_select ? (BM_elem_flag_test(v, BM_ELEM_SELECT) != 0) : true)
-#define IS_ME_VERT_READ(v) (use_hide ? (((v)->flag & ME_HIDE) == 0) : true)
+ const bool *hide_vert = me ? (const bool *)CustomData_get_layer_named(
+ &me->vdata, CD_PROP_BOOL, ".hide_vert") :
+ NULL;
+
+#define IS_ME_VERT_READ(v) (use_hide ? (hide_vert && hide_vert[v]) : true)
#define IS_ME_VERT_WRITE(v) (use_select ? (((v)->flag & SELECT) != 0) : true)
/* initialize used verts */
@@ -1956,8 +1961,8 @@ static void vgroup_smooth_subset(Object *ob,
if (IS_ME_VERT_WRITE(v)) {
for (int j = 0; j < emap[i].count; j++) {
const MEdge *e = &me->medge[emap[i].indices[j]];
- const MVert *v_other = &me->mvert[(e->v1 == i) ? e->v2 : e->v1];
- if (IS_ME_VERT_READ(v_other)) {
+ const int i_other = (e->v1 == i) ? e->v2 : e->v1;
+ if (IS_ME_VERT_READ(i_other)) {
STACK_PUSH(verts_used, i);
break;
}
@@ -2031,9 +2036,7 @@ static void vgroup_smooth_subset(Object *ob,
for (j = 0; j < emap[i].count; j++) {
MEdge *e = &me->medge[emap[i].indices[j]];
const int i_other = (e->v1 == i ? e->v2 : e->v1);
- MVert *v_other = &me->mvert[i_other];
-
- if (IS_ME_VERT_READ(v_other)) {
+ if (IS_ME_VERT_READ(i_other)) {
WEIGHT_ACCUMULATE;
}
}
diff --git a/source/blender/editors/screen/screen_edit.c b/source/blender/editors/screen/screen_edit.c
index 08c8c863729..73195168d38 100644
--- a/source/blender/editors/screen/screen_edit.c
+++ b/source/blender/editors/screen/screen_edit.c
@@ -110,7 +110,7 @@ ScrArea *area_split(const wmWindow *win,
return NULL;
}
- /* NOTE(campbell): regarding (fac > 0.5f) checks below.
+ /* NOTE(@campbellbarton): regarding (fac > 0.5f) checks below.
* normally it shouldn't matter which is used since the copy should match the original
* however with viewport rendering and python console this isn't the case. */
diff --git a/source/blender/editors/screen/workspace_edit.c b/source/blender/editors/screen/workspace_edit.c
index cb29f15420c..fc3ac53ef0b 100644
--- a/source/blender/editors/screen/workspace_edit.c
+++ b/source/blender/editors/screen/workspace_edit.c
@@ -220,7 +220,7 @@ WorkSpace *ED_workspace_duplicate(WorkSpace *workspace_old, Main *bmain, wmWindo
workspace_new->order = workspace_old->order;
BLI_duplicatelist(&workspace_new->owner_ids, &workspace_old->owner_ids);
- /* TODO(campbell): tools */
+ /* TODO(@campbellbarton): tools */
LISTBASE_FOREACH (WorkSpaceLayout *, layout_old, &workspace_old->layouts) {
WorkSpaceLayout *layout_new = ED_workspace_layout_duplicate(
diff --git a/source/blender/editors/sculpt_paint/CMakeLists.txt b/source/blender/editors/sculpt_paint/CMakeLists.txt
index edb0f1cda4d..b170280ccf3 100644
--- a/source/blender/editors/sculpt_paint/CMakeLists.txt
+++ b/source/blender/editors/sculpt_paint/CMakeLists.txt
@@ -37,8 +37,8 @@ set(SRC
curves_sculpt_ops.cc
curves_sculpt_pinch.cc
curves_sculpt_puff.cc
- curves_sculpt_selection_paint.cc
curves_sculpt_selection.cc
+ curves_sculpt_selection_paint.cc
curves_sculpt_slide.cc
curves_sculpt_smooth.cc
curves_sculpt_snake_hook.cc
diff --git a/source/blender/editors/sculpt_paint/paint_hide.c b/source/blender/editors/sculpt_paint/paint_hide.c
index 944b3f953a0..c904d533db8 100644
--- a/source/blender/editors/sculpt_paint/paint_hide.c
+++ b/source/blender/editors/sculpt_paint/paint_hide.c
@@ -78,6 +78,12 @@ static void partialvis_update_mesh(Object *ob,
BKE_pbvh_node_get_verts(pbvh, node, &vert_indices, &mvert);
paint_mask = CustomData_get_layer(&me->vdata, CD_PAINT_MASK);
+ bool *hide_vert = CustomData_get_layer_named(&me->vdata, CD_PROP_BOOL, ".hide_vert");
+ if (hide_vert == NULL) {
+ hide_vert = CustomData_add_layer_named(
+ &me->vdata, CD_PROP_BOOL, CD_CALLOC, NULL, me->totvert, ".hide_vert");
+ }
+
SCULPT_undo_push_node(ob, node, SCULPT_UNDO_HIDDEN);
for (i = 0; i < totvert; i++) {
@@ -86,16 +92,11 @@ static void partialvis_update_mesh(Object *ob,
/* Hide vertex if in the hide volume. */
if (is_effected(area, planes, v->co, vmask)) {
- if (action == PARTIALVIS_HIDE) {
- v->flag |= ME_HIDE;
- }
- else {
- v->flag &= ~ME_HIDE;
- }
+ hide_vert[vert_indices[i]] = (action == PARTIALVIS_HIDE);
any_changed = true;
}
- if (!(v->flag & ME_HIDE)) {
+ if (!hide_vert[vert_indices[i]]) {
any_visible = true;
}
}
diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c
index 8df5b093560..ffa931268fd 100644
--- a/source/blender/editors/sculpt_paint/sculpt.c
+++ b/source/blender/editors/sculpt_paint/sculpt.c
@@ -344,9 +344,11 @@ int SCULPT_active_face_set_get(SculptSession *ss)
void SCULPT_vertex_visible_set(SculptSession *ss, PBVHVertRef vertex, bool visible)
{
switch (BKE_pbvh_type(ss->pbvh)) {
- case PBVH_FACES:
- SET_FLAG_FROM_TEST(ss->mvert[vertex.i].flag, !visible, ME_HIDE);
+ case PBVH_FACES: {
+ bool *hide_vert = BKE_pbvh_get_vert_hide_for_write(ss->pbvh);
+ hide_vert[vertex.i] = visible;
break;
+ }
case PBVH_BMESH: {
BMVert *v = (BMVert *)vertex.i;
BM_elem_flag_set(v, BM_ELEM_HIDDEN, !visible);
@@ -360,8 +362,10 @@ void SCULPT_vertex_visible_set(SculptSession *ss, PBVHVertRef vertex, bool visib
bool SCULPT_vertex_visible_get(SculptSession *ss, PBVHVertRef vertex)
{
switch (BKE_pbvh_type(ss->pbvh)) {
- case PBVH_FACES:
- return !(ss->mvert[vertex.i].flag & ME_HIDE);
+ case PBVH_FACES: {
+ const bool *hide_vert = BKE_pbvh_get_vert_hide(ss->pbvh);
+ return hide_vert == NULL || !hide_vert[vertex.i];
+ }
case PBVH_BMESH:
return !BM_elem_flag_test((BMVert *)vertex.i, BM_ELEM_HIDDEN);
case PBVH_GRIDS: {
@@ -5656,7 +5660,7 @@ void SCULPT_OT_brush_stroke(wmOperatorType *ot)
ot->cancel = sculpt_brush_stroke_cancel;
/* Flags (sculpt does own undo? (ton)). */
- ot->flag = OPTYPE_BLOCKING;
+ ot->flag = OPTYPE_BLOCKING | OPTYPE_REGISTER | OPTYPE_UNDO;
/* Properties. */
diff --git a/source/blender/editors/sculpt_paint/sculpt_dyntopo.c b/source/blender/editors/sculpt_paint/sculpt_dyntopo.c
index 4f91d2215fb..40b4b74a441 100644
--- a/source/blender/editors/sculpt_paint/sculpt_dyntopo.c
+++ b/source/blender/editors/sculpt_paint/sculpt_dyntopo.c
@@ -227,8 +227,9 @@ static void SCULPT_dynamic_topology_disable_ex(
me->face_sets_color_default = 1;
/* Sync the visibility to vertices manually as the pmap is still not initialized. */
- for (int i = 0; i < me->totvert; i++) {
- me->mvert[i].flag &= ~ME_HIDE;
+ bool *hide_vert = (bool *)CustomData_get_layer_named(&me->vdata, CD_PROP_BOOL, ".hide_vert");
+ if (hide_vert != NULL) {
+ memset(hide_vert, 0, sizeof(bool) * me->totvert);
}
}
diff --git a/source/blender/editors/sculpt_paint/sculpt_undo.c b/source/blender/editors/sculpt_paint/sculpt_undo.c
index dc814d6d22f..04b2b2f04bf 100644
--- a/source/blender/editors/sculpt_paint/sculpt_undo.c
+++ b/source/blender/editors/sculpt_paint/sculpt_undo.c
@@ -346,14 +346,13 @@ static bool sculpt_undo_restore_hidden(bContext *C, SculptUndoNode *unode, bool
SculptSession *ss = ob->sculpt;
SubdivCCG *subdiv_ccg = ss->subdiv_ccg;
- if (unode->maxvert) {
- MVert *mvert = ss->mvert;
+ bool *hide_vert = BKE_pbvh_get_vert_hide_for_write(ss->pbvh);
+ if (unode->maxvert) {
for (int i = 0; i < unode->totvert; i++) {
- MVert *v = &mvert[unode->index[i]];
- if ((BLI_BITMAP_TEST(unode->vert_hidden, i) != 0) != ((v->flag & ME_HIDE) != 0)) {
+ if ((BLI_BITMAP_TEST(unode->vert_hidden, i) != 0) != hide_vert[i]) {
BLI_BITMAP_FLIP(unode->vert_hidden, i);
- v->flag ^= ME_HIDE;
+ hide_vert[unode->index[i]] = !hide_vert[i];
modified_vertices[unode->index[i]] = true;
}
}
@@ -1247,6 +1246,11 @@ static void sculpt_undo_store_hidden(Object *ob, SculptUndoNode *unode)
PBVH *pbvh = ob->sculpt->pbvh;
PBVHNode *node = unode->node;
+ const bool *hide_vert = BKE_pbvh_get_vert_hide(pbvh);
+ if (hide_vert == NULL) {
+ return;
+ }
+
if (unode->grids) {
/* Already stored during allocation. */
}
@@ -1258,7 +1262,7 @@ static void sculpt_undo_store_hidden(Object *ob, SculptUndoNode *unode)
BKE_pbvh_node_num_verts(pbvh, node, NULL, &allvert);
BKE_pbvh_node_get_verts(pbvh, node, &vert_indices, &mvert);
for (int i = 0; i < allvert; i++) {
- BLI_BITMAP_SET(unode->vert_hidden, i, mvert[vert_indices[i]].flag & ME_HIDE);
+ BLI_BITMAP_SET(unode->vert_hidden, i, hide_vert[vert_indices[i]]);
}
}
}
diff --git a/source/blender/editors/sculpt_paint/sculpt_uv.c b/source/blender/editors/sculpt_paint/sculpt_uv.c
index f2017e68b4c..14b06f888fe 100644
--- a/source/blender/editors/sculpt_paint/sculpt_uv.c
+++ b/source/blender/editors/sculpt_paint/sculpt_uv.c
@@ -518,13 +518,7 @@ static UvSculptData *uv_sculpt_stroke_init(bContext *C, wmOperator *op, const wm
/* Count 'unique' UV's */
int unique_uvs = data->elementMap->total_unique_uvs;
if (do_island_optimization) {
- unique_uvs = 0;
- for (int i = 0; i < data->elementMap->total_uvs; i++) {
- if (data->elementMap->storage[i].separate &&
- (data->elementMap->storage[i].island == island_index)) {
- unique_uvs++;
- }
- }
+ unique_uvs = data->elementMap->island_total_unique_uvs[island_index];
}
/* Allocate the unique uv buffers */
@@ -572,6 +566,7 @@ static UvSculptData *uv_sculpt_stroke_init(bContext *C, wmOperator *op, const wm
uniqueUv[element - data->elementMap->storage] = counter;
}
}
+ BLI_assert(counter + 1 == unique_uvs);
/* Now, on to generate our uv connectivity data */
counter = 0;
@@ -629,11 +624,13 @@ static UvSculptData *uv_sculpt_stroke_init(bContext *C, wmOperator *op, const wm
}
/* fill the edges with data */
- int i = 0;
- GHASH_ITER (gh_iter, edgeHash) {
- data->uvedges[i++] = *((UvEdge *)BLI_ghashIterator_getKey(&gh_iter));
+ {
+ int i = 0;
+ GHASH_ITER (gh_iter, edgeHash) {
+ data->uvedges[i++] = *((UvEdge *)BLI_ghashIterator_getKey(&gh_iter));
+ }
+ data->totalUvEdges = BLI_ghash_len(edgeHash);
}
- data->totalUvEdges = BLI_ghash_len(edgeHash);
/* cleanup temporary stuff */
BLI_ghash_free(edgeHash, NULL, NULL);
diff --git a/source/blender/editors/space_action/action_select.c b/source/blender/editors/space_action/action_select.c
index d1a8592ae9d..ed9d86e1a4e 100644
--- a/source/blender/editors/space_action/action_select.c
+++ b/source/blender/editors/space_action/action_select.c
@@ -919,7 +919,7 @@ static const EnumPropertyItem prop_column_select_types[] = {
/* ------------------- */
/* Selects all visible keyframes between the specified markers */
-/* TODO(campbell): this is almost an _exact_ duplicate of a function of the same name in
+/* TODO(@campbellbarton): this is almost an _exact_ duplicate of a function of the same name in
* graph_select.c should de-duplicate. */
static void markers_selectkeys_between(bAnimContext *ac)
{
diff --git a/source/blender/editors/space_graph/graph_select.c b/source/blender/editors/space_graph/graph_select.c
index a36bd5c1461..0ce3e1a797a 100644
--- a/source/blender/editors/space_graph/graph_select.c
+++ b/source/blender/editors/space_graph/graph_select.c
@@ -1128,7 +1128,7 @@ static const EnumPropertyItem prop_column_select_types[] = {
/* ------------------- */
/* Selects all visible keyframes between the specified markers */
-/* TODO(campbell): this is almost an _exact_ duplicate of a function of the same name in
+/* TODO(@campbellbarton): this is almost an _exact_ duplicate of a function of the same name in
* action_select.c should de-duplicate. */
static void markers_selectkeys_between(bAnimContext *ac)
{
diff --git a/source/blender/editors/space_node/CMakeLists.txt b/source/blender/editors/space_node/CMakeLists.txt
index badcccca87b..26fddda8c22 100644
--- a/source/blender/editors/space_node/CMakeLists.txt
+++ b/source/blender/editors/space_node/CMakeLists.txt
@@ -50,8 +50,8 @@ set(LIB
bf_editor_screen
)
-if(WITH_COMPOSITOR)
- add_definitions(-DWITH_COMPOSITOR)
+if(WITH_COMPOSITOR_CPU)
+ add_definitions(-DWITH_COMPOSITOR_CPU)
endif()
if(WITH_OPENIMAGEDENOISE)
diff --git a/source/blender/editors/space_outliner/CMakeLists.txt b/source/blender/editors/space_outliner/CMakeLists.txt
index 78ec057f921..b9f79303a06 100644
--- a/source/blender/editors/space_outliner/CMakeLists.txt
+++ b/source/blender/editors/space_outliner/CMakeLists.txt
@@ -48,11 +48,11 @@ set(SRC
tree/tree_element_anim_data.cc
tree/tree_element_collection.cc
tree/tree_element_driver.cc
- tree/tree_element_label.cc
tree/tree_element_gpencil_layer.cc
tree/tree_element_id.cc
tree/tree_element_id_library.cc
tree/tree_element_id_scene.cc
+ tree/tree_element_label.cc
tree/tree_element_nla.cc
tree/tree_element_overrides.cc
tree/tree_element_rna.cc
@@ -68,11 +68,11 @@ set(SRC
tree/tree_element_anim_data.hh
tree/tree_element_collection.hh
tree/tree_element_driver.hh
- tree/tree_element_label.hh
tree/tree_element_gpencil_layer.hh
tree/tree_element_id.hh
tree/tree_element_id_library.hh
tree/tree_element_id_scene.hh
+ tree/tree_element_label.hh
tree/tree_element_nla.hh
tree/tree_element_overrides.hh
tree/tree_element_rna.hh
diff --git a/source/blender/editors/space_outliner/outliner_collections.cc b/source/blender/editors/space_outliner/outliner_collections.cc
index 7d0a0a921e4..02d54e4f702 100644
--- a/source/blender/editors/space_outliner/outliner_collections.cc
+++ b/source/blender/editors/space_outliner/outliner_collections.cc
@@ -364,7 +364,7 @@ void outliner_collection_delete(
const IDTypeInfo *id_type = BKE_idtype_get_info_from_id(&parent->id);
BLI_assert(id_type->owner_get != nullptr);
- ID *scene_owner = id_type->owner_get(bmain, &parent->id);
+ ID *scene_owner = id_type->owner_get(bmain, &parent->id, NULL);
BLI_assert(GS(scene_owner->name) == ID_SCE);
if (ID_IS_LINKED(scene_owner) || ID_IS_OVERRIDE_LIBRARY(scene_owner)) {
skip = true;
@@ -597,7 +597,7 @@ static int collection_duplicate_exec(bContext *C, wmOperator *op)
const IDTypeInfo *id_type = BKE_idtype_get_info_from_id(&parent->id);
BLI_assert(id_type->owner_get != nullptr);
- Scene *scene_owner = (Scene *)id_type->owner_get(bmain, &parent->id);
+ Scene *scene_owner = (Scene *)id_type->owner_get(bmain, &parent->id, NULL);
BLI_assert(scene_owner != nullptr);
BLI_assert(GS(scene_owner->id.name) == ID_SCE);
diff --git a/source/blender/editors/space_outliner/outliner_draw.cc b/source/blender/editors/space_outliner/outliner_draw.cc
index 8bc1ed8c84e..e67eab4e432 100644
--- a/source/blender/editors/space_outliner/outliner_draw.cc
+++ b/source/blender/editors/space_outliner/outliner_draw.cc
@@ -2855,7 +2855,7 @@ TreeElementIcon tree_element_get_icon(TreeStoreElem *tselem, TreeElement *te)
}
/**
- * \return Return true if the element has an icon that was drawn, false if it doesn't have an icon.
+ * \return true if the element has an icon that was drawn, false if it doesn't have an icon.
*/
static bool tselem_draw_icon(uiBlock *block,
int xmax,
diff --git a/source/blender/editors/space_outliner/outliner_intern.hh b/source/blender/editors/space_outliner/outliner_intern.hh
index 18173b37123..5362782dd84 100644
--- a/source/blender/editors/space_outliner/outliner_intern.hh
+++ b/source/blender/editors/space_outliner/outliner_intern.hh
@@ -530,6 +530,8 @@ void OUTLINER_OT_operation(struct wmOperatorType *ot);
void OUTLINER_OT_scene_operation(struct wmOperatorType *ot);
void OUTLINER_OT_object_operation(struct wmOperatorType *ot);
void OUTLINER_OT_lib_operation(struct wmOperatorType *ot);
+void OUTLINER_OT_liboverride_operation(struct wmOperatorType *ot);
+void OUTLINER_OT_liboverride_troubleshoot_operation(struct wmOperatorType *ot);
void OUTLINER_OT_id_operation(struct wmOperatorType *ot);
void OUTLINER_OT_id_remap(struct wmOperatorType *ot);
void OUTLINER_OT_id_copy(struct wmOperatorType *ot);
diff --git a/source/blender/editors/space_outliner/outliner_ops.cc b/source/blender/editors/space_outliner/outliner_ops.cc
index 8baac45666e..b384c41aa69 100644
--- a/source/blender/editors/space_outliner/outliner_ops.cc
+++ b/source/blender/editors/space_outliner/outliner_ops.cc
@@ -29,6 +29,8 @@ void outliner_operatortypes(void)
WM_operatortype_append(OUTLINER_OT_object_operation);
WM_operatortype_append(OUTLINER_OT_lib_operation);
WM_operatortype_append(OUTLINER_OT_lib_relocate);
+ WM_operatortype_append(OUTLINER_OT_liboverride_operation);
+ WM_operatortype_append(OUTLINER_OT_liboverride_troubleshoot_operation);
WM_operatortype_append(OUTLINER_OT_id_operation);
WM_operatortype_append(OUTLINER_OT_id_delete);
WM_operatortype_append(OUTLINER_OT_id_remap);
diff --git a/source/blender/editors/space_outliner/outliner_tools.cc b/source/blender/editors/space_outliner/outliner_tools.cc
index c408eca654c..f51a70af3bc 100644
--- a/source/blender/editors/space_outliner/outliner_tools.cc
+++ b/source/blender/editors/space_outliner/outliner_tools.cc
@@ -453,6 +453,99 @@ static void outliner_do_libdata_operation(bContext *C,
});
}
+typedef enum eOutlinerLibOpSelectionSet {
+ /* Only selected items. */
+ OUTLINER_LIB_SELECTIONSET_SELECTED,
+ /* Only content 'inside' selected items (their sub-tree). */
+ OUTLINER_LIB_LIB_SELECTIONSET_CONTENT,
+ /* Combining both options above. */
+ OUTLINER_LIB_LIB_SELECTIONSET_SELECTED_AND_CONTENT,
+} eOutlinerLibOpSelectionSet;
+
+static const EnumPropertyItem prop_lib_op_selection_set[] = {
+ {OUTLINER_LIB_SELECTIONSET_SELECTED,
+ "SELECTED",
+ 0,
+ "Selected",
+ "Apply the operation over selected data-blocks only"},
+ {OUTLINER_LIB_LIB_SELECTIONSET_CONTENT,
+ "CONTENT",
+ 0,
+ "Content",
+ "Apply the operation over content of the selected items only (the data-blocks in their "
+ "sub-tree)"},
+ {OUTLINER_LIB_LIB_SELECTIONSET_SELECTED_AND_CONTENT,
+ "SELECTED_AND_CONTENT",
+ 0,
+ "Selected & Content",
+ "Apply the operation over selected data-blocks and all their dependencies"},
+ {0, nullptr, 0, nullptr, nullptr},
+};
+
+static void outliner_do_libdata_operation_selection_set(bContext *C,
+ ReportList *reports,
+ Scene *scene,
+ SpaceOutliner *space_outliner,
+ const ListBase &subtree,
+ const bool has_parent_selected,
+ outliner_operation_fn operation_fn,
+ eOutlinerLibOpSelectionSet selection_set,
+ void *user_data)
+{
+ const bool do_selected = ELEM(selection_set,
+ OUTLINER_LIB_SELECTIONSET_SELECTED,
+ OUTLINER_LIB_LIB_SELECTIONSET_SELECTED_AND_CONTENT);
+ const bool do_content = ELEM(selection_set,
+ OUTLINER_LIB_LIB_SELECTIONSET_CONTENT,
+ OUTLINER_LIB_LIB_SELECTIONSET_SELECTED_AND_CONTENT);
+
+ LISTBASE_FOREACH_MUTABLE (TreeElement *, element, &subtree) {
+ /* Get needed data out in case element gets freed. */
+ TreeStoreElem *tselem = TREESTORE(element);
+ const ListBase subtree = element->subtree;
+
+ bool is_selected = tselem->flag & TSE_SELECTED;
+ if ((is_selected && do_selected) || (has_parent_selected && do_content)) {
+ if (((tselem->type == TSE_SOME_ID) && (element->idcode != 0)) ||
+ tselem->type == TSE_LAYER_COLLECTION) {
+ TreeStoreElem *tsep = element->parent ? TREESTORE(element->parent) : nullptr;
+ operation_fn(C, reports, scene, element, tsep, tselem, user_data);
+ }
+ }
+
+ /* Don't access element from now on, it may be freed. Note that the open/collapsed state may
+ * also have been changed in the visitor callback. */
+ outliner_do_libdata_operation_selection_set(C,
+ reports,
+ scene,
+ space_outliner,
+ subtree,
+ is_selected || has_parent_selected,
+ operation_fn,
+ selection_set,
+ user_data);
+ }
+}
+
+static void outliner_do_libdata_operation_selection_set(bContext *C,
+ ReportList *reports,
+ Scene *scene,
+ SpaceOutliner *space_outliner,
+ outliner_operation_fn operation_fn,
+ eOutlinerLibOpSelectionSet selection_set,
+ void *user_data)
+{
+ outliner_do_libdata_operation_selection_set(C,
+ reports,
+ scene,
+ space_outliner,
+ space_outliner->tree,
+ false,
+ operation_fn,
+ selection_set,
+ user_data);
+}
+
/** \} */
/* -------------------------------------------------------------------- */
@@ -842,6 +935,20 @@ struct OutlinerLibOverrideData {
id_hierarchy_root_reference);
value.append(id_root_data);
}
+ void id_root_set(ID *id_hierarchy_root_reference)
+ {
+ OutlinerLiboverrideDataIDRoot id_root_data;
+ id_root_data.id_root_reference = nullptr;
+ id_root_data.id_hierarchy_root_override = nullptr;
+ id_root_data.id_instance_hint = nullptr;
+ id_root_data.is_override_instancing_object = false;
+
+ Vector<OutlinerLiboverrideDataIDRoot> &value = id_hierarchy_roots.lookup_or_add_default(
+ id_hierarchy_root_reference);
+ if (value.is_empty()) {
+ value.append(id_root_data);
+ }
+ }
};
/* Store 'UUID' of IDs of selected elements in the Outliner tree, before generating the override
@@ -860,11 +967,21 @@ static void id_override_library_create_hierarchy_pre_process_fn(bContext *C,
const bool do_hierarchy = data->do_hierarchy;
ID *id_root_reference = tselem->id;
+ if (!BKE_idtype_idcode_is_linkable(GS(id_root_reference->name)) ||
+ (id_root_reference->flag & (LIB_EMBEDDED_DATA | LIB_EMBEDDED_DATA_LIB_OVERRIDE)) != 0) {
+ return;
+ }
+
BLI_assert(do_hierarchy);
UNUSED_VARS_NDEBUG(do_hierarchy);
data->selected_id_uid.add(id_root_reference->session_uuid);
+ if (ID_IS_OVERRIDE_LIBRARY_REAL(id_root_reference) && !ID_IS_LINKED(id_root_reference)) {
+ id_root_reference->override_library->flag &= ~IDOVERRIDE_LIBRARY_FLAG_SYSTEM_DEFINED;
+ return;
+ }
+
if (GS(id_root_reference->name) == ID_GR && (tselem->flag & TSE_CLOSED) != 0) {
/* If selected element is a (closed) collection, check all of its objects recursively, and also
* consider the armature ones as 'selected' (i.e. to not become system overrides). */
@@ -983,6 +1100,17 @@ static void id_override_library_create_hierarchy_pre_process_fn(bContext *C,
return;
}
+ /* While ideally this should not be needed, in practice user almost _never_ wants to actually
+ * create liboverrides for all data under a selected hierarchy node, and this has currently a
+ * dreadful consequences over performances (since it would call
+ * #BKE_lib_override_library_create over _all_ items in the hierarchy). So only the clearing of
+ * the system override flag is supported for non-selected items for now.
+ */
+ const bool is_selected = tselem->flag & TSE_SELECTED;
+ if (!is_selected && data->id_hierarchy_roots.contains(id_hierarchy_root_reference)) {
+ return;
+ }
+
data->id_root_add(id_hierarchy_root_reference,
id_root_reference,
id_instance_hint,
@@ -1133,23 +1261,6 @@ static void id_override_library_create_hierarchy_process(bContext *C,
FOREACH_MAIN_ID_END;
}
-static void id_override_library_toggle_flag_fn(bContext *UNUSED(C),
- ReportList *UNUSED(reports),
- Scene *UNUSED(scene),
- TreeElement *UNUSED(te),
- TreeStoreElem *UNUSED(tsep),
- TreeStoreElem *tselem,
- void *user_data)
-{
- BLI_assert(TSE_IS_REAL_ID(tselem));
- ID *id = tselem->id;
-
- if (ID_IS_OVERRIDE_LIBRARY_REAL(id)) {
- const uint flag = POINTER_AS_UINT(user_data);
- id->override_library->flag ^= flag;
- }
-}
-
static void id_override_library_reset_fn(bContext *C,
ReportList *UNUSED(reports),
Scene *UNUSED(scene),
@@ -1181,10 +1292,10 @@ static void id_override_library_reset_fn(bContext *C,
}
}
-static void id_override_library_resync_fn(bContext *C,
- ReportList *reports,
- Scene *scene,
- TreeElement *te,
+static void id_override_library_resync_fn(bContext *UNUSED(C),
+ ReportList *UNUSED(reports),
+ Scene *UNUSED(scene),
+ TreeElement *UNUSED(te),
TreeStoreElem *UNUSED(tsep),
TreeStoreElem *tselem,
void *user_data)
@@ -1192,44 +1303,53 @@ static void id_override_library_resync_fn(bContext *C,
BLI_assert(TSE_IS_REAL_ID(tselem));
ID *id_root = tselem->id;
OutlinerLibOverrideData *data = static_cast<OutlinerLibOverrideData *>(user_data);
- const bool do_hierarchy_enforce = data->do_resync_hierarchy_enforce;
- if (ID_IS_OVERRIDE_LIBRARY_REAL(id_root)) {
- Main *bmain = CTX_data_main(C);
+ if (!ID_IS_OVERRIDE_LIBRARY_REAL(id_root)) {
+ CLOG_WARN(&LOG, "Could not resync library override of data block '%s'", id_root->name);
+ }
- id_root->tag |= LIB_TAG_DOIT;
+ if (id_root->override_library->hierarchy_root != nullptr) {
+ id_root = id_root->override_library->hierarchy_root;
+ }
- /* Tag all linked parents in tree hierarchy to be also overridden. */
- while ((te = te->parent) != nullptr) {
- if (!TSE_IS_REAL_ID(te->store_elem)) {
- continue;
- }
- if (!ID_IS_OVERRIDE_LIBRARY_REAL(te->store_elem->id)) {
- break;
- }
- te->store_elem->id->tag |= LIB_TAG_DOIT;
- }
+ data->id_root_set(id_root);
+}
- BlendFileReadReport report{};
- report.reports = reports;
- BKE_lib_override_library_resync(
- bmain, scene, CTX_data_view_layer(C), id_root, nullptr, do_hierarchy_enforce, &report);
+/* Resync a hierarchy of library overrides. */
+static void id_override_library_resync_hierarchy_process(bContext *C,
+ ReportList *reports,
+ OutlinerLibOverrideData &data)
+{
+ Main *bmain = CTX_data_main(C);
+ Scene *scene = CTX_data_scene(C);
+ const bool do_hierarchy_enforce = data.do_resync_hierarchy_enforce;
- WM_event_add_notifier(C, NC_WINDOW, nullptr);
- }
- else {
- CLOG_WARN(&LOG, "Could not resync library override of data block '%s'", id_root->name);
+ BlendFileReadReport report{};
+ report.reports = reports;
+
+ for (auto &&id_hierarchy_root : data.id_hierarchy_roots.keys()) {
+ BKE_lib_override_library_resync(bmain,
+ scene,
+ CTX_data_view_layer(C),
+ id_hierarchy_root,
+ nullptr,
+ do_hierarchy_enforce,
+ &report);
}
+
+ WM_event_add_notifier(C, NC_WINDOW, nullptr);
}
-static void id_override_library_clear_hierarchy_fn(bContext *C,
+static void id_override_library_clear_hierarchy_fn(bContext *UNUSED(C),
ReportList *UNUSED(reports),
Scene *UNUSED(scene),
- TreeElement *te,
+ TreeElement *UNUSED(te),
TreeStoreElem *UNUSED(tsep),
TreeStoreElem *tselem,
- void *UNUSED(user_data))
+ void *user_data)
{
+ OutlinerLibOverrideData *data = reinterpret_cast<OutlinerLibOverrideData *>(user_data);
+
BLI_assert(TSE_IS_REAL_ID(tselem));
ID *id_root = tselem->id;
@@ -1238,22 +1358,23 @@ static void id_override_library_clear_hierarchy_fn(bContext *C,
return;
}
- Main *bmain = CTX_data_main(C);
+ if (id_root->override_library->hierarchy_root != nullptr) {
+ id_root = id_root->override_library->hierarchy_root;
+ }
- id_root->tag |= LIB_TAG_DOIT;
+ data->id_root_set(id_root);
+}
- /* Tag all override parents in tree hierarchy to be also processed. */
- while ((te = te->parent) != nullptr) {
- if (!TSE_IS_REAL_ID(te->store_elem)) {
- continue;
- }
- if (!ID_IS_OVERRIDE_LIBRARY_REAL(te->store_elem->id)) {
- break;
- }
- te->store_elem->id->tag |= LIB_TAG_DOIT;
- }
+/* Clear (delete) a hierarchy of library overrides. */
+static void id_override_library_clear_hierarchy_process(bContext *C,
+ ReportList *UNUSED(reports),
+ OutlinerLibOverrideData &data)
+{
+ Main *bmain = CTX_data_main(C);
- BKE_lib_override_library_delete(bmain, id_root);
+ for (auto &&id_hierarchy_root : data.id_hierarchy_roots.keys()) {
+ BKE_lib_override_library_delete(bmain, id_hierarchy_root);
+ }
WM_event_add_notifier(C, NC_WINDOW, nullptr);
}
@@ -1494,6 +1615,250 @@ static void refreshdrivers_animdata_fn(int UNUSED(event),
/** \} */
/* -------------------------------------------------------------------- */
+/** \name Library Overrides Operation Menu.
+ * \{ */
+
+enum eOutlinerLibOverrideOpTypes {
+ OUTLINER_LIBOVERRIDE_OP_INVALID = 0,
+
+ OUTLINER_LIBOVERRIDE_OP_CREATE_HIERARCHY,
+ OUTLINER_LIBOVERRIDE_OP_RESET,
+ OUTLINER_LIBOVERRIDE_OP_CLEAR_SINGLE,
+
+ OUTLINER_LIBOVERRIDE_OP_RESYNC_HIERARCHY,
+ OUTLINER_LIBOVERRIDE_OP_RESYNC_HIERARCHY_ENFORCE,
+ OUTLINER_LIBOVERRIDE_OP_DELETE_HIERARCHY,
+};
+
+static const EnumPropertyItem prop_liboverride_op_types[] = {
+ {OUTLINER_LIBOVERRIDE_OP_CREATE_HIERARCHY,
+ "OVERRIDE_LIBRARY_CREATE_HIERARCHY",
+ 0,
+ "Create",
+ "Make a local override of the selected linked data-blocks, and their hierarchy of "
+ "dependencies"},
+ {OUTLINER_LIBOVERRIDE_OP_RESET,
+ "OVERRIDE_LIBRARY_RESET",
+ 0,
+ "Reset",
+ "Reset the selected local override to their linked references values"},
+ {OUTLINER_LIBOVERRIDE_OP_CLEAR_SINGLE,
+ "OVERRIDE_LIBRARY_CLEAR_SINGLE",
+ 0,
+ "Clear",
+ "Delete the selected local overrides and relink their usages to the linked data-blocks if "
+ "possible, else reset them and mark them as non editable"},
+ {0, nullptr, 0, nullptr, nullptr},
+};
+
+static const EnumPropertyItem prop_liboverride_troubleshoot_op_types[] = {
+ {OUTLINER_LIBOVERRIDE_OP_RESYNC_HIERARCHY,
+ "OVERRIDE_LIBRARY_RESYNC_HIERARCHY",
+ 0,
+ "Resync",
+ "Rebuild the selected local overrides from their linked references, as well as their "
+ "hierarchies of dependencies"},
+ {OUTLINER_LIBOVERRIDE_OP_RESYNC_HIERARCHY_ENFORCE,
+ "OVERRIDE_LIBRARY_RESYNC_HIERARCHY_ENFORCE",
+ 0,
+ "Resync Enforce",
+ "Rebuild the selected local overrides from their linked references, as well as their "
+ "hierarchies of dependencies, enforcing these hierarchies to match the linked data (i.e. "
+ "ignoring existing overrides on data-blocks pointer properties)"},
+ {OUTLINER_LIBOVERRIDE_OP_DELETE_HIERARCHY,
+ "OVERRIDE_LIBRARY_DELETE_HIERARCHY",
+ 0,
+ "Delete",
+ "Delete the selected local overrides (including their hierarchies of override dependencies) "
+ "and relink their usages to the linked data-blocks"},
+ {0, nullptr, 0, nullptr, nullptr},
+};
+
+static bool outliner_liboverride_operation_poll(bContext *C)
+{
+ if (!outliner_operation_tree_element_poll(C)) {
+ return false;
+ }
+ return true;
+}
+
+static int outliner_liboverride_operation_exec(bContext *C, wmOperator *op)
+{
+ Scene *scene = CTX_data_scene(C);
+ SpaceOutliner *space_outliner = CTX_wm_space_outliner(C);
+ int scenelevel = 0, objectlevel = 0, idlevel = 0, datalevel = 0;
+
+ /* check for invalid states */
+ if (space_outliner == nullptr) {
+ return OPERATOR_CANCELLED;
+ }
+
+ TreeElement *te = get_target_element(space_outliner);
+ get_element_operation_type(te, &scenelevel, &objectlevel, &idlevel, &datalevel);
+
+ const eOutlinerLibOpSelectionSet selection_set = static_cast<eOutlinerLibOpSelectionSet>(
+ RNA_enum_get(op->ptr, "selection_set"));
+ const eOutlinerLibOverrideOpTypes event = static_cast<eOutlinerLibOverrideOpTypes>(
+ RNA_enum_get(op->ptr, "type"));
+ switch (event) {
+ case OUTLINER_LIBOVERRIDE_OP_CREATE_HIERARCHY: {
+ OutlinerLibOverrideData override_data{};
+ override_data.do_hierarchy = true;
+ override_data.do_fully_editable = false;
+
+ outliner_do_libdata_operation_selection_set(
+ C,
+ op->reports,
+ scene,
+ space_outliner,
+ id_override_library_create_hierarchy_pre_process_fn,
+ selection_set,
+ &override_data);
+
+ id_override_library_create_hierarchy_process(C, op->reports, override_data);
+
+ ED_undo_push(C, "Overridden Data Hierarchy");
+ break;
+ }
+ case OUTLINER_LIBOVERRIDE_OP_RESET: {
+ OutlinerLibOverrideData override_data{};
+ outliner_do_libdata_operation_selection_set(C,
+ op->reports,
+ scene,
+ space_outliner,
+ id_override_library_reset_fn,
+ selection_set,
+ &override_data);
+ ED_undo_push(C, "Reset Overridden Data");
+ break;
+ }
+ case OUTLINER_LIBOVERRIDE_OP_CLEAR_SINGLE: {
+ outliner_do_libdata_operation_selection_set(C,
+ op->reports,
+ scene,
+ space_outliner,
+ id_override_library_clear_single_fn,
+ selection_set,
+ nullptr);
+ ED_undo_push(C, "Clear Overridden Data");
+ break;
+ }
+
+ case OUTLINER_LIBOVERRIDE_OP_RESYNC_HIERARCHY: {
+ OutlinerLibOverrideData override_data{};
+ override_data.do_hierarchy = true;
+ outliner_do_libdata_operation_selection_set(C,
+ op->reports,
+ scene,
+ space_outliner,
+ id_override_library_resync_fn,
+ OUTLINER_LIB_SELECTIONSET_SELECTED,
+ &override_data);
+
+ id_override_library_resync_hierarchy_process(C, op->reports, override_data);
+
+ ED_undo_push(C, "Resync Overridden Data Hierarchy");
+ break;
+ }
+ case OUTLINER_LIBOVERRIDE_OP_RESYNC_HIERARCHY_ENFORCE: {
+ OutlinerLibOverrideData override_data{};
+ override_data.do_hierarchy = true;
+ override_data.do_resync_hierarchy_enforce = true;
+ outliner_do_libdata_operation_selection_set(C,
+ op->reports,
+ scene,
+ space_outliner,
+ id_override_library_resync_fn,
+ OUTLINER_LIB_SELECTIONSET_SELECTED,
+ &override_data);
+
+ id_override_library_resync_hierarchy_process(C, op->reports, override_data);
+
+ ED_undo_push(C, "Resync Overridden Data Hierarchy Enforce");
+ break;
+ }
+ case OUTLINER_LIBOVERRIDE_OP_DELETE_HIERARCHY: {
+ OutlinerLibOverrideData override_data{};
+ override_data.do_hierarchy = true;
+ outliner_do_libdata_operation_selection_set(C,
+ op->reports,
+ scene,
+ space_outliner,
+ id_override_library_clear_hierarchy_fn,
+ OUTLINER_LIB_SELECTIONSET_SELECTED,
+ nullptr);
+
+ id_override_library_clear_hierarchy_process(C, op->reports, override_data);
+
+ ED_undo_push(C, "Delete Overridden Data Hierarchy");
+ break;
+ }
+ default:
+ /* Invalid - unhandled. */
+ break;
+ }
+
+ /* wrong notifier still... */
+ WM_event_add_notifier(C, NC_ID | NA_EDITED, nullptr);
+
+ /* XXX: this is just so that outliner is always up to date. */
+ WM_event_add_notifier(C, NC_SPACE | ND_SPACE_OUTLINER, nullptr);
+
+ return OPERATOR_FINISHED;
+}
+
+void OUTLINER_OT_liboverride_operation(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Outliner Library Override Operation";
+ ot->idname = "OUTLINER_OT_liboverride_operation";
+
+ /* callbacks */
+ ot->invoke = WM_menu_invoke;
+ ot->exec = outliner_liboverride_operation_exec;
+ ot->poll = outliner_liboverride_operation_poll;
+
+ ot->flag = 0;
+
+ RNA_def_enum(ot->srna, "type", prop_liboverride_op_types, 0, "Library Override Operation", "");
+ ot->prop = RNA_def_enum(ot->srna,
+ "selection_set",
+ prop_lib_op_selection_set,
+ 0,
+ "Selection Set",
+ "Over which part of the tree items to apply the operation");
+}
+
+void OUTLINER_OT_liboverride_troubleshoot_operation(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Outliner Library Override Troubleshoot Operation";
+ ot->idname = "OUTLINER_OT_liboverride_troubleshoot_operation";
+
+ /* callbacks */
+ ot->invoke = WM_menu_invoke;
+ ot->exec = outliner_liboverride_operation_exec;
+ ot->poll = outliner_liboverride_operation_poll;
+
+ ot->flag = 0;
+
+ RNA_def_enum(ot->srna,
+ "type",
+ prop_liboverride_troubleshoot_op_types,
+ 0,
+ "Library Override Troubleshoot Operation",
+ "");
+ ot->prop = RNA_def_enum(ot->srna,
+ "selection_set",
+ prop_lib_op_selection_set,
+ 0,
+ "Selection Set",
+ "Over which part of the tree items to apply the operation");
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
/** \name Object Operation Utilities
* \{ */
@@ -2088,15 +2453,6 @@ enum eOutlinerIdOpTypes {
OUTLINER_IDOP_UNLINK,
OUTLINER_IDOP_LOCAL,
- OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE,
- OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE_HIERARCHY,
- OUTLINER_IDOP_OVERRIDE_LIBRARY_MAKE_EDITABLE,
- OUTLINER_IDOP_OVERRIDE_LIBRARY_RESET,
- OUTLINER_IDOP_OVERRIDE_LIBRARY_RESET_HIERARCHY,
- OUTLINER_IDOP_OVERRIDE_LIBRARY_RESYNC_HIERARCHY,
- OUTLINER_IDOP_OVERRIDE_LIBRARY_RESYNC_HIERARCHY_ENFORCE,
- OUTLINER_IDOP_OVERRIDE_LIBRARY_CLEAR_HIERARCHY,
- OUTLINER_IDOP_OVERRIDE_LIBRARY_CLEAR_SINGLE,
OUTLINER_IDOP_SINGLE,
OUTLINER_IDOP_DELETE,
OUTLINER_IDOP_REMAP,
@@ -2123,59 +2479,6 @@ static const EnumPropertyItem prop_id_op_types[] = {
"Remap Users",
"Make all users of selected data-blocks to use instead current (clicked) one"},
RNA_ENUM_ITEM_SEPR,
- {OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE,
- "OVERRIDE_LIBRARY_CREATE",
- 0,
- "Make Library Override Single",
- "Make a single, out-of-hierarchy local override of this linked data-block - only applies to "
- "active Outliner item"},
- {OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE_HIERARCHY,
- "OVERRIDE_LIBRARY_CREATE_HIERARCHY",
- 0,
- "Make Library Override Hierarchy",
- "Make a local override of this linked data-block, and its hierarchy of dependencies - only "
- "applies to active Outliner item"},
- {OUTLINER_IDOP_OVERRIDE_LIBRARY_MAKE_EDITABLE,
- "OVERRIDE_LIBRARY_MAKE_EDITABLE",
- 0,
- "Make Library Override Editable",
- "Make the library override data-block editable"},
- {OUTLINER_IDOP_OVERRIDE_LIBRARY_RESET,
- "OVERRIDE_LIBRARY_RESET",
- 0,
- "Reset Library Override Single",
- "Reset this local override to its linked values"},
- {OUTLINER_IDOP_OVERRIDE_LIBRARY_RESET_HIERARCHY,
- "OVERRIDE_LIBRARY_RESET_HIERARCHY",
- 0,
- "Reset Library Override Hierarchy",
- "Reset this local override to its linked values, as well as its hierarchy of dependencies"},
- {OUTLINER_IDOP_OVERRIDE_LIBRARY_RESYNC_HIERARCHY,
- "OVERRIDE_LIBRARY_RESYNC_HIERARCHY",
- 0,
- "Resync Library Override Hierarchy",
- "Rebuild this local override from its linked reference, as well as its hierarchy of "
- "dependencies"},
- {OUTLINER_IDOP_OVERRIDE_LIBRARY_RESYNC_HIERARCHY_ENFORCE,
- "OVERRIDE_LIBRARY_RESYNC_HIERARCHY_ENFORCE",
- 0,
- "Resync Library Override Hierarchy Enforce",
- "Rebuild this local override from its linked reference, as well as its hierarchy of "
- "dependencies, enforcing that hierarchy to match the linked data (i.e. ignoring exiting "
- "overrides on data-blocks pointer properties)"},
- {OUTLINER_IDOP_OVERRIDE_LIBRARY_CLEAR_SINGLE,
- "OVERRIDE_LIBRARY_CLEAR_SINGLE",
- 0,
- "Clear Library Override Single",
- "Delete this local override and relink its usages to the linked data-blocks if possible, "
- "else reset it and mark it as non editable"},
- {OUTLINER_IDOP_OVERRIDE_LIBRARY_CLEAR_HIERARCHY,
- "OVERRIDE_LIBRARY_CLEAR_HIERARCHY",
- 0,
- "Clear Library Override Hierarchy",
- "Delete this local override (including its hierarchy of override dependencies) and relink "
- "its usages to the linked data-blocks"},
- RNA_ENUM_ITEM_SEPR,
{OUTLINER_IDOP_COPY, "COPY", ICON_COPYDOWN, "Copy", ""},
{OUTLINER_IDOP_PASTE, "PASTE", ICON_PASTEDOWN, "Paste", ""},
RNA_ENUM_ITEM_SEPR,
@@ -2208,33 +2511,6 @@ static bool outliner_id_operation_item_poll(bContext *C,
}
switch (enum_value) {
- case OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE:
- if (ID_IS_OVERRIDABLE_LIBRARY(tselem->id)) {
- return true;
- }
- return false;
- case OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE_HIERARCHY:
- if (ID_IS_OVERRIDABLE_LIBRARY(tselem->id) || (ID_IS_LINKED(tselem->id))) {
- return true;
- }
- return false;
- case OUTLINER_IDOP_OVERRIDE_LIBRARY_MAKE_EDITABLE:
- if (ID_IS_OVERRIDE_LIBRARY_REAL(tselem->id) && !ID_IS_LINKED(tselem->id)) {
- if (tselem->id->override_library->flag & IDOVERRIDE_LIBRARY_FLAG_SYSTEM_DEFINED) {
- return true;
- }
- }
- return false;
- case OUTLINER_IDOP_OVERRIDE_LIBRARY_RESET:
- case OUTLINER_IDOP_OVERRIDE_LIBRARY_RESET_HIERARCHY:
- case OUTLINER_IDOP_OVERRIDE_LIBRARY_RESYNC_HIERARCHY:
- case OUTLINER_IDOP_OVERRIDE_LIBRARY_RESYNC_HIERARCHY_ENFORCE:
- case OUTLINER_IDOP_OVERRIDE_LIBRARY_CLEAR_HIERARCHY:
- case OUTLINER_IDOP_OVERRIDE_LIBRARY_CLEAR_SINGLE:
- if (ID_IS_OVERRIDE_LIBRARY_REAL(tselem->id) && !ID_IS_LINKED(tselem->id)) {
- return true;
- }
- return false;
case OUTLINER_IDOP_SINGLE:
if (ELEM(space_outliner->outlinevis, SO_SCENES, SO_VIEW_LAYER)) {
return true;
@@ -2347,95 +2623,6 @@ static int outliner_id_operation_exec(bContext *C, wmOperator *op)
ED_undo_push(C, "Localized Data");
break;
}
- case OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE: {
- OutlinerLibOverrideData override_data{};
- override_data.do_hierarchy = false;
- override_data.do_fully_editable = true;
-
- outliner_do_libdata_operation(C,
- op->reports,
- scene,
- space_outliner,
- id_override_library_create_hierarchy_pre_process_fn,
- &override_data);
-
- id_override_library_create_hierarchy_process(C, op->reports, override_data);
-
- ED_undo_push(C, "Overridden Data");
- break;
- }
- case OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE_HIERARCHY: {
- OutlinerLibOverrideData override_data{};
- override_data.do_hierarchy = true;
- override_data.do_fully_editable = U.experimental.use_override_new_fully_editable;
-
- outliner_do_libdata_operation(C,
- op->reports,
- scene,
- space_outliner,
- id_override_library_create_hierarchy_pre_process_fn,
- &override_data);
-
- id_override_library_create_hierarchy_process(C, op->reports, override_data);
-
- ED_undo_push(C, "Overridden Data Hierarchy");
- break;
- }
- case OUTLINER_IDOP_OVERRIDE_LIBRARY_MAKE_EDITABLE: {
- outliner_do_libdata_operation(C,
- op->reports,
- scene,
- space_outliner,
- id_override_library_toggle_flag_fn,
- POINTER_FROM_UINT(IDOVERRIDE_LIBRARY_FLAG_SYSTEM_DEFINED));
-
- ED_undo_push(C, "Make Overridden Data Editable");
- break;
- }
- case OUTLINER_IDOP_OVERRIDE_LIBRARY_RESET: {
- OutlinerLibOverrideData override_data{};
- outliner_do_libdata_operation(
- C, op->reports, scene, space_outliner, id_override_library_reset_fn, &override_data);
- ED_undo_push(C, "Reset Overridden Data");
- break;
- }
- case OUTLINER_IDOP_OVERRIDE_LIBRARY_RESET_HIERARCHY: {
- OutlinerLibOverrideData override_data{};
- override_data.do_hierarchy = true;
- outliner_do_libdata_operation(
- C, op->reports, scene, space_outliner, id_override_library_reset_fn, &override_data);
- ED_undo_push(C, "Reset Overridden Data Hierarchy");
- break;
- }
- case OUTLINER_IDOP_OVERRIDE_LIBRARY_RESYNC_HIERARCHY: {
- OutlinerLibOverrideData override_data{};
- override_data.do_hierarchy = true;
- outliner_do_libdata_operation(
- C, op->reports, scene, space_outliner, id_override_library_resync_fn, &override_data);
- ED_undo_push(C, "Resync Overridden Data Hierarchy");
- break;
- }
- case OUTLINER_IDOP_OVERRIDE_LIBRARY_RESYNC_HIERARCHY_ENFORCE: {
- OutlinerLibOverrideData override_data{};
- override_data.do_hierarchy = true;
- override_data.do_resync_hierarchy_enforce = true;
- outliner_do_libdata_operation(
- C, op->reports, scene, space_outliner, id_override_library_resync_fn, &override_data);
- ED_undo_push(C, "Resync Overridden Data Hierarchy");
- break;
- }
- case OUTLINER_IDOP_OVERRIDE_LIBRARY_CLEAR_HIERARCHY: {
- outliner_do_libdata_operation(
- C, op->reports, scene, space_outliner, id_override_library_clear_hierarchy_fn, nullptr);
- ED_undo_push(C, "Clear Overridden Data Hierarchy");
- break;
- }
- case OUTLINER_IDOP_OVERRIDE_LIBRARY_CLEAR_SINGLE: {
- outliner_do_libdata_operation(
- C, op->reports, scene, space_outliner, id_override_library_clear_single_fn, nullptr);
- ED_undo_push(C, "Clear Overridden Data Hierarchy");
- break;
- }
case OUTLINER_IDOP_SINGLE: {
/* make single user */
switch (idlevel) {
diff --git a/source/blender/editors/space_outliner/tree/tree_display_override_library_hierarchies.cc b/source/blender/editors/space_outliner/tree/tree_display_override_library_hierarchies.cc
index f8705c3f0ad..e0a1958795a 100644
--- a/source/blender/editors/space_outliner/tree/tree_display_override_library_hierarchies.cc
+++ b/source/blender/editors/space_outliner/tree/tree_display_override_library_hierarchies.cc
@@ -15,6 +15,7 @@
#include "BLT_translation.h"
+#include "BKE_lib_override.h"
#include "BKE_lib_query.h"
#include "BKE_main.h"
@@ -80,6 +81,7 @@ ListBase TreeDisplayOverrideLibraryHierarchies::buildTree(const TreeSourceData &
class OverrideIDHierarchyBuilder {
SpaceOutliner &space_outliner_;
+ Main &bmain_;
MainIDRelations &id_relations_;
struct HierarchyBuildData {
@@ -93,8 +95,10 @@ class OverrideIDHierarchyBuilder {
};
public:
- OverrideIDHierarchyBuilder(SpaceOutliner &space_outliner, MainIDRelations &id_relations)
- : space_outliner_(space_outliner), id_relations_(id_relations)
+ OverrideIDHierarchyBuilder(SpaceOutliner &space_outliner,
+ Main &bmain,
+ MainIDRelations &id_relations)
+ : space_outliner_(space_outliner), bmain_(bmain), id_relations_(id_relations)
{
}
@@ -115,7 +119,7 @@ ListBase TreeDisplayOverrideLibraryHierarchies::build_hierarchy_for_lib_or_main(
* returning. */
BKE_main_relations_create(bmain, 0);
- OverrideIDHierarchyBuilder builder(space_outliner_, *bmain->relations);
+ OverrideIDHierarchyBuilder builder(space_outliner_, *bmain, *bmain->relations);
/* Keep track over which ID base elements were already added, and expand them once added. */
Map<ID_Type, TreeElement *> id_base_te_map;
@@ -165,7 +169,8 @@ void OverrideIDHierarchyBuilder::build_hierarchy_for_ID(ID &override_root_id,
static void foreach_natural_hierarchy_child(const MainIDRelations &id_relations,
const ID &parent_id,
FunctionRef<void(ID &)> fn);
-static bool id_is_in_override_hierarchy(const ID &id,
+static bool id_is_in_override_hierarchy(const Main &bmain,
+ const ID &id,
const ID &relationship_parent_id,
const ID &override_root_id);
@@ -177,7 +182,11 @@ void OverrideIDHierarchyBuilder::build_hierarchy_for_ID_recursive(const ID &pare
build_data.parent_ids.add(&parent_id);
foreach_natural_hierarchy_child(id_relations_, parent_id, [&](ID &id) {
- if (!id_is_in_override_hierarchy(id, parent_id, build_data.override_root_id_)) {
+ /* Some IDs can use themselves, early abort. */
+ if (&id == &parent_id) {
+ return;
+ }
+ if (!id_is_in_override_hierarchy(bmain_, id, parent_id, build_data.override_root_id_)) {
return;
}
@@ -276,7 +285,8 @@ static void foreach_natural_hierarchy_child(const MainIDRelations &id_relations,
}
}
-static bool id_is_in_override_hierarchy(const ID &id,
+static bool id_is_in_override_hierarchy(const Main &bmain,
+ const ID &id,
const ID &relationship_parent_id,
const ID &override_root_id)
{
@@ -286,20 +296,12 @@ static bool id_is_in_override_hierarchy(const ID &id,
const ID *real_override_id = &id;
if (ID_IS_OVERRIDE_LIBRARY_VIRTUAL(&id)) {
- /* This assumes that the parent ID is always the owner of the 'embedded' one, I.e. that no
- * other ID directly uses the embedded one. Should be true, but the debug code adds some checks
- * to validate this assumption. */
- real_override_id = &relationship_parent_id;
-
-#ifndef NDEBUG
- if (GS(id.name) == ID_KE) {
- const Key *key = (Key *)&id;
- BLI_assert(real_override_id == key->from);
- }
- else {
- BLI_assert((id.flag & LIB_EMBEDDED_DATA) != 0);
- }
-#endif
+ /* In many cases, `relationship_parent_id` is the owner, but not always (e.g. there can be
+ * drivers directly between an object and a shapekey). */
+ BKE_lib_override_library_get(const_cast<Main *>(&bmain),
+ const_cast<ID *>(&id),
+ const_cast<ID *>(&relationship_parent_id),
+ const_cast<ID **>(&real_override_id));
}
if (!ID_IS_OVERRIDE_LIBRARY(real_override_id)) {
diff --git a/source/blender/editors/space_outliner/tree/tree_element_overrides.cc b/source/blender/editors/space_outliner/tree/tree_element_overrides.cc
index e19459ced61..49cabd5117f 100644
--- a/source/blender/editors/space_outliner/tree/tree_element_overrides.cc
+++ b/source/blender/editors/space_outliner/tree/tree_element_overrides.cc
@@ -371,8 +371,7 @@ void OverrideRNAPathTreeBuilder::ensure_entire_collection(
const char *coll_prop_path,
short &index)
{
- AbstractTreeElement *abstract_parent = tree_element_cast<AbstractTreeElement>(&te_to_expand);
- BLI_assert(abstract_parent != nullptr);
+ BLI_assert(tree_element_cast<AbstractTreeElement>(&te_to_expand) != nullptr);
TreeElement *previous_te = nullptr;
int item_idx = 0;
diff --git a/source/blender/editors/space_script/script_edit.c b/source/blender/editors/space_script/script_edit.c
index e8c7590c1fe..a32c8a3f85a 100644
--- a/source/blender/editors/space_script/script_edit.c
+++ b/source/blender/editors/space_script/script_edit.c
@@ -100,7 +100,7 @@ static int script_reload_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
- /* TODO(campbell): this crashes on netrender and keying sets, need to look into why
+ /* TODO(@campbellbarton): this crashes on netrender and keying sets, need to look into why
* disable for now unless running in debug mode. */
/* It would be nice if we could detect when this is called from the Python
diff --git a/source/blender/editors/space_sequencer/sequencer_edit.c b/source/blender/editors/space_sequencer/sequencer_edit.c
index cb95e9a75de..9313e45a1d4 100644
--- a/source/blender/editors/space_sequencer/sequencer_edit.c
+++ b/source/blender/editors/space_sequencer/sequencer_edit.c
@@ -2869,7 +2869,7 @@ static int sequencer_change_path_exec(bContext *C, wmOperator *op)
RNA_string_get(op->ptr, "directory", directory);
if (is_relative_path) {
- /* TODO(campbell): shouldn't this already be relative from the filesel?
+ /* TODO(@campbellbarton): shouldn't this already be relative from the filesel?
* (as the 'filepath' is) for now just make relative here,
* but look into changing after 2.60. */
BLI_path_rel(directory, BKE_main_blendfile_path(bmain));
diff --git a/source/blender/editors/space_sequencer/sequencer_scopes.c b/source/blender/editors/space_sequencer/sequencer_scopes.c
index 6ba1dcc5eb8..af0aa093e40 100644
--- a/source/blender/editors/space_sequencer/sequencer_scopes.c
+++ b/source/blender/editors/space_sequencer/sequencer_scopes.c
@@ -17,7 +17,7 @@
#include "sequencer_intern.h"
-/* XXX(campbell): why is this function better than BLI_math version?
+/* XXX(@campbellbarton): why is this function better than BLI_math version?
* only difference is it does some normalize after, need to double check on this. */
static void rgb_to_yuv_normalized(const float rgb[3], float yuv[3])
{
diff --git a/source/blender/editors/space_text/text_autocomplete.c b/source/blender/editors/space_text/text_autocomplete.c
index 54735a4d481..461606f63aa 100644
--- a/source/blender/editors/space_text/text_autocomplete.c
+++ b/source/blender/editors/space_text/text_autocomplete.c
@@ -314,7 +314,7 @@ static int doc_scroll = 0;
static int text_autocomplete_modal(bContext *C, wmOperator *op, const wmEvent *event)
{
- /* NOTE(campbell): this code could be refactored or rewritten. */
+ /* NOTE(@campbellbarton): this code could be refactored or rewritten. */
SpaceText *st = CTX_wm_space_text(C);
ScrArea *area = CTX_wm_area(C);
ARegion *region = BKE_area_find_region_type(area, RGN_TYPE_WINDOW);
diff --git a/source/blender/editors/space_view3d/space_view3d.c b/source/blender/editors/space_view3d/space_view3d.c
index a423a842019..1a2eb20d1a9 100644
--- a/source/blender/editors/space_view3d/space_view3d.c
+++ b/source/blender/editors/space_view3d/space_view3d.c
@@ -960,7 +960,7 @@ static void view3d_widgets(void)
WM_gizmogrouptype_append_and_link(gzmap_type, VIEW3D_GGT_camera);
WM_gizmogrouptype_append_and_link(gzmap_type, VIEW3D_GGT_camera_view);
WM_gizmogrouptype_append_and_link(gzmap_type, VIEW3D_GGT_empty_image);
- /* TODO(campbell): Not working well enough, disable for now. */
+ /* TODO(@campbellbarton): Not working well enough, disable for now. */
#if 0
WM_gizmogrouptype_append_and_link(gzmap_type, VIEW3D_GGT_armature_spline);
#endif
@@ -1210,6 +1210,9 @@ static void view3d_main_region_listener(const wmRegionListenerParams *params)
break;
}
break;
+ case NC_NODE:
+ ED_region_tag_redraw(region);
+ break;
case NC_WORLD:
switch (wmn->data) {
case ND_WORLD_DRAW:
diff --git a/source/blender/editors/space_view3d/view3d_cursor_snap.c b/source/blender/editors/space_view3d/view3d_cursor_snap.c
index 4a1bd6ba945..fb44797eded 100644
--- a/source/blender/editors/space_view3d/view3d_cursor_snap.c
+++ b/source/blender/editors/space_view3d/view3d_cursor_snap.c
@@ -495,6 +495,16 @@ static void v3d_cursor_eventstate_save_xy(SnapCursorDataIntern *cursor_snap,
}
#ifdef USE_SNAP_DETECT_FROM_KEYMAP_HACK
+static void v3d_cursor_eventstate_save_modifier(SnapCursorDataIntern *data_intern,
+ const wmWindowManager *wm)
+{
+ if (!wm || !wm->winactive) {
+ return;
+ }
+ const wmEvent *event = wm->winactive->eventstate;
+ data_intern->last_eventstate.modifier = event->modifier;
+}
+
static bool v3d_cursor_is_snap_invert(SnapCursorDataIntern *data_intern, const wmWindowManager *wm)
{
if (!wm || !wm->winactive) {
@@ -582,10 +592,14 @@ static void v3d_cursor_snap_update(V3DSnapCursorState *state,
{
SnapCursorDataIntern *data_intern = &g_data_intern;
V3DSnapCursorData *snap_data = &data_intern->snap_data;
- v3d_cursor_snap_context_ensure(scene);
+
+ const bool use_surface_nor = state->plane_orient == V3D_PLACE_ORIENT_SURFACE;
+ const bool use_surface_co = state->plane_depth == V3D_PLACE_DEPTH_SURFACE;
+ const bool calc_plane_omat = v3d_cursor_snap_calc_plane();
float co[3], no[3], face_nor[3], obmat[4][4], omat[3][3];
eSnapMode snap_elem = SCE_SNAP_MODE_NONE;
+ eSnapMode snap_elements = v3d_cursor_snap_elements(state, scene);
int snap_elem_index[3] = {-1, -1, -1};
int index = -1;
@@ -594,77 +608,84 @@ static void v3d_cursor_snap_update(V3DSnapCursorState *state,
zero_v3(face_nor);
unit_m3(omat);
- eSnapMode snap_elements = v3d_cursor_snap_elements(state, scene);
- data_intern->snap_elem_hidden = SCE_SNAP_MODE_NONE;
- const bool calc_plane_omat = v3d_cursor_snap_calc_plane();
- if (calc_plane_omat && !(snap_elements & SCE_SNAP_MODE_FACE_RAYCAST)) {
- data_intern->snap_elem_hidden = SCE_SNAP_MODE_FACE_RAYCAST;
- snap_elements |= SCE_SNAP_MODE_FACE_RAYCAST;
- }
+ if (use_surface_nor || use_surface_co) {
+ v3d_cursor_snap_context_ensure(scene);
+
+ data_intern->snap_elem_hidden = SCE_SNAP_MODE_NONE;
+ if (calc_plane_omat && !(snap_elements & SCE_SNAP_MODE_FACE_RAYCAST)) {
+ data_intern->snap_elem_hidden = SCE_SNAP_MODE_FACE_RAYCAST;
+ snap_elements |= SCE_SNAP_MODE_FACE_RAYCAST;
+ }
- snap_data->is_enabled = true;
+ snap_data->is_enabled = true;
#ifdef USE_SNAP_DETECT_FROM_KEYMAP_HACK
- if (!(state->flag & V3D_SNAPCURSOR_TOGGLE_ALWAYS_TRUE)) {
- snap_data->is_snap_invert = v3d_cursor_is_snap_invert(data_intern, wm);
-
- const ToolSettings *ts = scene->toolsettings;
- if (snap_data->is_snap_invert != !(ts->snap_flag & SCE_SNAP)) {
- snap_data->is_enabled = false;
- if (!calc_plane_omat) {
- snap_data->snap_elem = SCE_SNAP_MODE_NONE;
- return;
+ if (!(state->flag & V3D_SNAPCURSOR_TOGGLE_ALWAYS_TRUE)) {
+ snap_data->is_snap_invert = v3d_cursor_is_snap_invert(data_intern, wm);
+
+ const ToolSettings *ts = scene->toolsettings;
+ if (snap_data->is_snap_invert != !(ts->snap_flag & SCE_SNAP)) {
+ snap_data->is_enabled = false;
+ if (!calc_plane_omat) {
+ snap_data->snap_elem = SCE_SNAP_MODE_NONE;
+ return;
+ }
+ snap_elements = data_intern->snap_elem_hidden = SCE_SNAP_MODE_FACE_RAYCAST;
}
- snap_elements = data_intern->snap_elem_hidden = SCE_SNAP_MODE_FACE_RAYCAST;
}
- }
#endif
- if (snap_elements & SCE_SNAP_MODE_GEOM) {
- float prev_co[3] = {0.0f};
- if (state->prevpoint) {
- copy_v3_v3(prev_co, state->prevpoint);
- }
- else {
- snap_elements &= ~SCE_SNAP_MODE_EDGE_PERPENDICULAR;
- }
+ if (snap_elements & SCE_SNAP_MODE_GEOM) {
+ float prev_co[3] = {0.0f};
+ if (state->prevpoint) {
+ copy_v3_v3(prev_co, state->prevpoint);
+ }
+ else {
+ snap_elements &= ~SCE_SNAP_MODE_EDGE_PERPENDICULAR;
+ }
- eSnapEditType edit_mode_type = (state->flag & V3D_SNAPCURSOR_SNAP_EDIT_GEOM_FINAL) ?
- SNAP_GEOM_FINAL :
- (state->flag & V3D_SNAPCURSOR_SNAP_EDIT_GEOM_CAGE) ?
- SNAP_GEOM_CAGE :
- SNAP_GEOM_EDIT;
-
- bool use_occlusion_test = (state->flag & V3D_SNAPCURSOR_OCCLUSION_ALWAYS_TRUE) ? false : true;
-
- float dist_px = 12.0f * U.pixelsize;
-
- snap_elem = ED_transform_snap_object_project_view3d_ex(
- data_intern->snap_context_v3d,
- depsgraph,
- region,
- v3d,
- snap_elements,
- &(const struct SnapObjectParams){
- .snap_target_select = SCE_SNAP_TARGET_ALL,
- .edit_mode_type = edit_mode_type,
- .use_occlusion_test = use_occlusion_test,
- },
- NULL,
- mval_fl,
- prev_co,
- &dist_px,
- co,
- no,
- &index,
- NULL,
- obmat,
- face_nor);
+ eSnapEditType edit_mode_type = (state->flag & V3D_SNAPCURSOR_SNAP_EDIT_GEOM_FINAL) ?
+ SNAP_GEOM_FINAL :
+ (state->flag & V3D_SNAPCURSOR_SNAP_EDIT_GEOM_CAGE) ?
+ SNAP_GEOM_CAGE :
+ SNAP_GEOM_EDIT;
+
+ bool use_occlusion_test = (state->flag & V3D_SNAPCURSOR_OCCLUSION_ALWAYS_TRUE) ? false :
+ true;
+
+ float dist_px = 12.0f * U.pixelsize;
+
+ snap_elem = ED_transform_snap_object_project_view3d_ex(
+ data_intern->snap_context_v3d,
+ depsgraph,
+ region,
+ v3d,
+ snap_elements,
+ &(const struct SnapObjectParams){
+ .snap_target_select = SCE_SNAP_TARGET_ALL,
+ .edit_mode_type = edit_mode_type,
+ .use_occlusion_test = use_occlusion_test,
+ },
+ NULL,
+ mval_fl,
+ prev_co,
+ &dist_px,
+ co,
+ no,
+ &index,
+ NULL,
+ obmat,
+ face_nor);
+ }
+ }
+#ifdef USE_SNAP_DETECT_FROM_KEYMAP_HACK
+ else {
+ v3d_cursor_eventstate_save_modifier(data_intern, wm);
}
+#endif
if (calc_plane_omat) {
RegionView3D *rv3d = region->regiondata;
- bool orient_surface = (snap_elem != SCE_SNAP_MODE_NONE) &&
- (state->plane_orient == V3D_PLACE_ORIENT_SURFACE);
+ bool orient_surface = use_surface_nor && (snap_elem != SCE_SNAP_MODE_NONE);
if (orient_surface) {
copy_m3_m4(omat, obmat);
}
@@ -715,6 +736,10 @@ static void v3d_cursor_snap_update(V3DSnapCursorState *state,
}
}
+ if (!use_surface_co) {
+ snap_elem = SCE_SNAP_MODE_NONE;
+ }
+
float *co_depth = (snap_elem != SCE_SNAP_MODE_NONE) ? co : scene->cursor.location;
snap_elem &= ~data_intern->snap_elem_hidden;
if (snap_elem == SCE_SNAP_MODE_NONE) {
diff --git a/source/blender/editors/space_view3d/view3d_gizmo_armature.c b/source/blender/editors/space_view3d/view3d_gizmo_armature.c
index 3f6167d92ca..62799dd7a5c 100644
--- a/source/blender/editors/space_view3d/view3d_gizmo_armature.c
+++ b/source/blender/editors/space_view3d/view3d_gizmo_armature.c
@@ -37,7 +37,7 @@
* \{ */
/*
- * TODO(campbell): Current conversion is a approximation (usable not correct),
+ * TODO(@campbellbarton): Current conversion is a approximation (usable not correct),
* we'll need to take the next/previous bones into account to get the tangent directions.
* First last matrices from 'BKE_pchan_bbone_spline_setup' are close but also not quite accurate
* since they're not at either end-points on the curve.
diff --git a/source/blender/editors/space_view3d/view3d_iterators.c b/source/blender/editors/space_view3d/view3d_iterators.c
index 35d4746608b..6256eeb9621 100644
--- a/source/blender/editors/space_view3d/view3d_iterators.c
+++ b/source/blender/editors/space_view3d/view3d_iterators.c
@@ -205,6 +205,7 @@ typedef struct foreachScreenObjectVert_userData {
void (*func)(void *userData, MVert *mv, const float screen_co[2], int index);
void *userData;
ViewContext vc;
+ const bool *hide_vert;
eV3DProjTest clip_flag;
} foreachScreenObjectVert_userData;
@@ -262,18 +263,19 @@ static void meshobject_foreachScreenVert__mapFunc(void *userData,
const float UNUSED(no[3]))
{
foreachScreenObjectVert_userData *data = userData;
+ if (data->hide_vert && data->hide_vert[index]) {
+ return;
+ }
struct MVert *mv = &((Mesh *)(data->vc.obact->data))->mvert[index];
- if (!(mv->flag & ME_HIDE)) {
- float screen_co[2];
-
- if (ED_view3d_project_float_object(data->vc.region, co, screen_co, data->clip_flag) !=
- V3D_PROJ_RET_OK) {
- return;
- }
+ float screen_co[2];
- data->func(data->userData, mv, screen_co, index);
+ if (ED_view3d_project_float_object(data->vc.region, co, screen_co, data->clip_flag) !=
+ V3D_PROJ_RET_OK) {
+ return;
}
+
+ data->func(data->userData, mv, screen_co, index);
}
void meshobject_foreachScreenVert(
@@ -297,6 +299,8 @@ void meshobject_foreachScreenVert(
data.func = func;
data.userData = userData;
data.clip_flag = clip_flag;
+ data.hide_vert = (const bool *)CustomData_get_layer_named(
+ &me->vdata, CD_PROP_BOOL, ".hide_vert");
if (clip_flag & V3D_PROJ_TEST_CLIP_BB) {
ED_view3d_clipping_local(vc->rv3d, vc->obact->obmat);
diff --git a/source/blender/editors/space_view3d/view3d_navigate.c b/source/blender/editors/space_view3d/view3d_navigate.c
index 88e004aac48..f50e933fdac 100644
--- a/source/blender/editors/space_view3d/view3d_navigate.c
+++ b/source/blender/editors/space_view3d/view3d_navigate.c
@@ -495,6 +495,8 @@ static void axis_set_view(bContext *C,
.camera_old = v3d->camera,
.ofs = rv3d->ofs,
.quat = quat,
+ /* No undo because this switches to/from camera. */
+ .undo_str = NULL,
});
}
else if (orig_persp == RV3D_CAMOB && v3d->camera) {
@@ -518,6 +520,8 @@ static void axis_set_view(bContext *C,
.ofs = ofs,
.quat = quat,
.dist = &dist,
+ /* No undo because this switches to/from camera. */
+ .undo_str = NULL,
});
}
else {
@@ -540,6 +544,8 @@ static void axis_set_view(bContext *C,
&(const V3D_SmoothParams){
.quat = quat,
.dyn_ofs = dyn_ofs_pt,
+ /* No undo because this isn't a camera view. */
+ .undo_str = NULL,
});
}
}
@@ -694,6 +700,8 @@ static void view3d_from_minmax(bContext *C,
.camera_old = v3d->camera,
.ofs = new_ofs,
.dist = ok_dist ? &new_dist : NULL,
+ /* The caller needs to use undo begin/end calls. */
+ .undo_str = NULL,
});
}
else {
@@ -704,6 +712,8 @@ static void view3d_from_minmax(bContext *C,
&(const V3D_SmoothParams){
.ofs = new_ofs,
.dist = ok_dist ? &new_dist : NULL,
+ /* The caller needs to use undo begin/end calls. */
+ .undo_str = NULL,
});
}
@@ -736,6 +746,7 @@ static void view3d_from_minmax_multi(bContext *C,
static int view3d_all_exec(bContext *C, wmOperator *op)
{
+ ScrArea *area = CTX_wm_area(C);
ARegion *region = CTX_wm_region(C);
View3D *v3d = CTX_wm_view3d(C);
RegionView3D *rv3d = CTX_wm_region_view3d(C);
@@ -802,6 +813,7 @@ static int view3d_all_exec(bContext *C, wmOperator *op)
/* This is an approximation, see function documentation for details. */
ED_view3d_clipping_clamp_minmax(rv3d, min, max);
}
+ ED_view3d_smooth_view_undo_begin(C, area);
if (use_all_regions) {
view3d_from_minmax_multi(C, v3d, min, max, true, smooth_viewtx);
@@ -810,6 +822,8 @@ static int view3d_all_exec(bContext *C, wmOperator *op)
view3d_from_minmax(C, v3d, region, min, max, true, smooth_viewtx);
}
+ ED_view3d_smooth_view_undo_end(C, area, op->type->name, false);
+
return OPERATOR_FINISHED;
}
@@ -842,6 +856,7 @@ void VIEW3D_OT_view_all(wmOperatorType *ot)
static int viewselected_exec(bContext *C, wmOperator *op)
{
+ ScrArea *area = CTX_wm_area(C);
ARegion *region = CTX_wm_region(C);
View3D *v3d = CTX_wm_view3d(C);
RegionView3D *rv3d = CTX_wm_region_view3d(C);
@@ -971,6 +986,8 @@ static int viewselected_exec(bContext *C, wmOperator *op)
ED_view3d_clipping_clamp_minmax(rv3d, min, max);
}
+ ED_view3d_smooth_view_undo_begin(C, area);
+
if (use_all_regions) {
view3d_from_minmax_multi(C, v3d, min, max, ok_dist, smooth_viewtx);
}
@@ -978,6 +995,8 @@ static int viewselected_exec(bContext *C, wmOperator *op)
view3d_from_minmax(C, v3d, region, min, max, ok_dist, smooth_viewtx);
}
+ ED_view3d_smooth_view_undo_end(C, area, op->type->name, false);
+
return OPERATOR_FINISHED;
}
@@ -1020,8 +1039,14 @@ static int viewcenter_cursor_exec(bContext *C, wmOperator *op)
/* non camera center */
float new_ofs[3];
negate_v3_v3(new_ofs, scene->cursor.location);
- ED_view3d_smooth_view(
- C, v3d, region, smooth_viewtx, &(const V3D_SmoothParams){.ofs = new_ofs});
+ ED_view3d_smooth_view(C,
+ v3d,
+ region,
+ smooth_viewtx,
+ &(const V3D_SmoothParams){
+ .ofs = new_ofs,
+ .undo_str = op->type->name,
+ });
/* Smooth view does view-lock #RV3D_BOXVIEW copy. */
}
@@ -1074,8 +1099,14 @@ static int viewcenter_pick_invoke(bContext *C, wmOperator *op, const wmEvent *ev
ED_view3d_win_to_3d_int(v3d, region, new_ofs, event->mval, new_ofs);
}
negate_v3(new_ofs);
- ED_view3d_smooth_view(
- C, v3d, region, smooth_viewtx, &(const V3D_SmoothParams){.ofs = new_ofs});
+ ED_view3d_smooth_view(C,
+ v3d,
+ region,
+ smooth_viewtx,
+ &(const V3D_SmoothParams){
+ .ofs = new_ofs,
+ .undo_str = op->type->name,
+ });
}
return OPERATOR_FINISHED;
@@ -1318,17 +1349,20 @@ static int view_camera_exec(bContext *C, wmOperator *op)
/* finally do snazzy view zooming */
rv3d->persp = RV3D_CAMOB;
- ED_view3d_smooth_view(C,
- v3d,
- region,
- smooth_viewtx,
- &(const V3D_SmoothParams){
- .camera = v3d->camera,
- .ofs = rv3d->ofs,
- .quat = rv3d->viewquat,
- .dist = &rv3d->dist,
- .lens = &v3d->lens,
- });
+ ED_view3d_smooth_view(
+ C,
+ v3d,
+ region,
+ smooth_viewtx,
+ &(const V3D_SmoothParams){
+ .camera = v3d->camera,
+ .ofs = rv3d->ofs,
+ .quat = rv3d->viewquat,
+ .dist = &rv3d->dist,
+ .lens = &v3d->lens,
+ /* No undo because this changes cameras (and wont move the camera). */
+ .undo_str = NULL,
+ });
}
else {
/* return to settings of last view */
@@ -1417,7 +1451,12 @@ static int vieworbit_exec(bContext *C, wmOperator *op)
ED_view3d_smooth_view_force_finish(C, v3d, region);
if ((RV3D_LOCK_FLAGS(rv3d) & RV3D_LOCK_ROTATION) == 0 || (view_opposite != RV3D_VIEW_USER)) {
- if ((rv3d->persp != RV3D_CAMOB) || ED_view3d_camera_lock_check(v3d, rv3d)) {
+ const bool is_camera_lock = ED_view3d_camera_lock_check(v3d, rv3d);
+ if ((rv3d->persp != RV3D_CAMOB) || is_camera_lock) {
+ if (is_camera_lock) {
+ const Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
+ ED_view3d_camera_lock_init(depsgraph, v3d, rv3d);
+ }
int smooth_viewtx = WM_operator_smooth_viewtx_get(op);
float quat_mul[4];
float quat_new[4];
@@ -1475,6 +1514,9 @@ static int vieworbit_exec(bContext *C, wmOperator *op)
&(const V3D_SmoothParams){
.quat = quat_new,
.dyn_ofs = dyn_ofs_pt,
+ /* Group as successive orbit may run by holding a key. */
+ .undo_str = op->type->name,
+ .undo_grouped = true,
});
return OPERATOR_FINISHED;
diff --git a/source/blender/editors/space_view3d/view3d_navigate.h b/source/blender/editors/space_view3d/view3d_navigate.h
index fc7bc11295a..925acd90573 100644
--- a/source/blender/editors/space_view3d/view3d_navigate.h
+++ b/source/blender/editors/space_view3d/view3d_navigate.h
@@ -231,6 +231,14 @@ typedef struct V3D_SmoothParams {
/** Alternate rotation center, when set `ofs` must be NULL. */
const float *dyn_ofs;
+
+ /** When non-NULL, perform undo pushes when transforming the camera. */
+ const char *undo_str;
+ /**
+ * When true use grouped undo pushes, use for incremental viewport manipulation
+ * which are likely to be activated by holding a key or from the mouse-wheel.
+ */
+ bool undo_grouped;
} V3D_SmoothParams;
/**
@@ -252,6 +260,22 @@ void ED_view3d_smooth_view(struct bContext *C,
const V3D_SmoothParams *sview);
/**
+ * Call before multiple smooth-view operations begin to properly handle undo.
+ *
+ * \note Only use explicit undo calls when multiple calls to smooth-view are necessary
+ * or when calling #ED_view3d_smooth_view_ex.
+ * Otherwise pass in #V3D_SmoothParams.undo_str so an undo step is pushed as needed.
+ */
+void ED_view3d_smooth_view_undo_begin(struct bContext *C, const struct ScrArea *area);
+/**
+ * Run after multiple smooth-view operations have run to push undo as needed.
+ */
+void ED_view3d_smooth_view_undo_end(struct bContext *C,
+ const struct ScrArea *area,
+ const char *undo_str,
+ bool undo_grouped);
+
+/**
* Apply the smooth-view immediately, use when we need to start a new view operation.
* (so we don't end up half-applying a view operation when pressing keys quickly).
*/
diff --git a/source/blender/editors/space_view3d/view3d_navigate_roll.c b/source/blender/editors/space_view3d/view3d_navigate_roll.c
index 087ca72211e..af93aa50238 100644
--- a/source/blender/editors/space_view3d/view3d_navigate_roll.c
+++ b/source/blender/editors/space_view3d/view3d_navigate_roll.c
@@ -15,6 +15,8 @@
#include "RNA_access.h"
#include "RNA_define.h"
+#include "DEG_depsgraph_query.h"
+
#include "ED_screen.h"
#include "view3d_intern.h"
@@ -167,7 +169,13 @@ static int viewroll_exec(bContext *C, wmOperator *op)
}
rv3d = region->regiondata;
- if ((rv3d->persp != RV3D_CAMOB) || ED_view3d_camera_lock_check(v3d, rv3d)) {
+
+ const bool is_camera_lock = ED_view3d_camera_lock_check(v3d, rv3d);
+ if ((rv3d->persp != RV3D_CAMOB) || is_camera_lock) {
+ if (is_camera_lock) {
+ const Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
+ ED_view3d_camera_lock_init(depsgraph, v3d, rv3d);
+ }
ED_view3d_smooth_view_force_finish(C, v3d, region);
@@ -202,6 +210,9 @@ static int viewroll_exec(bContext *C, wmOperator *op)
&(const V3D_SmoothParams){
.quat = quat_new,
.dyn_ofs = dyn_ofs_pt,
+ /* Group as successive roll may run by holding a key. */
+ .undo_str = op->type->name,
+ .undo_grouped = true,
});
viewops_data_free(C, op->customdata);
diff --git a/source/blender/editors/space_view3d/view3d_navigate_smoothview.c b/source/blender/editors/space_view3d/view3d_navigate_smoothview.c
index 48af126d8a9..6b150d1e771 100644
--- a/source/blender/editors/space_view3d/view3d_navigate_smoothview.c
+++ b/source/blender/editors/space_view3d/view3d_navigate_smoothview.c
@@ -8,6 +8,7 @@
#include "MEM_guardedalloc.h"
+#include "BLI_listbase.h"
#include "BLI_math.h"
#include "BKE_context.h"
@@ -21,6 +22,115 @@
#include "view3d_intern.h"
#include "view3d_navigate.h" /* own include */
+static void view3d_smoothview_apply_with_interp(
+ bContext *C, View3D *v3d, RegionView3D *rv3d, const bool use_autokey, const float factor);
+
+/* -------------------------------------------------------------------- */
+/** \name Smooth View Undo Handling
+ *
+ * When the camera is locked to the viewport smooth-view operations
+ * may need to perform an undo push.
+ *
+ * In this case the smooth-view camera transformation is temporarily completed,
+ * undo is pushed then the change is rewound, and smooth-view completes from it's timer.
+ * In the case smooth-view executed the change immediately - an undo push is called.
+ *
+ * NOTE(@campbellbarton): While this is not ideal it's necessary as making the undo-push
+ * once smooth-view is complete because smooth-view is non-blocking and it's possible other
+ * operations are executed once smooth-view has started.
+ * \{ */
+
+void ED_view3d_smooth_view_undo_begin(bContext *C, const ScrArea *area)
+{
+ const View3D *v3d = area->spacedata.first;
+ Object *camera = v3d->camera;
+ if (!camera) {
+ return;
+ }
+
+ /* Tag the camera object so it's known smooth-view is applied to the view-ports camera
+ * (needed to detect when a locked camera is being manipulated).
+ * NOTE: It doesn't matter if the actual object being manipulated is the camera or not. */
+ camera->id.tag &= ~LIB_TAG_DOIT;
+
+ LISTBASE_FOREACH (const ARegion *, region, &area->regionbase) {
+ if (region->regiontype != RGN_TYPE_WINDOW) {
+ continue;
+ }
+ const RegionView3D *rv3d = region->regiondata;
+ if (ED_view3d_camera_lock_undo_test(v3d, rv3d, C)) {
+ camera->id.tag |= LIB_TAG_DOIT;
+ break;
+ }
+ }
+}
+
+void ED_view3d_smooth_view_undo_end(bContext *C,
+ const ScrArea *area,
+ const char *undo_str,
+ const bool undo_grouped)
+{
+ View3D *v3d = area->spacedata.first;
+ Object *camera = v3d->camera;
+ if (!camera) {
+ return;
+ }
+ if (camera->id.tag & LIB_TAG_DOIT) {
+ /* Smooth view didn't touch the camera. */
+ camera->id.tag &= ~LIB_TAG_DOIT;
+ return;
+ }
+
+ if ((U.uiflag & USER_GLOBALUNDO) == 0) {
+ return;
+ }
+
+ /* NOTE(@campbellbarton): It is not possible that a single viewport references different cameras
+ * so even in the case there is a quad-view with multiple camera views set, these will all
+ * reference the same camera. In this case it doesn't matter which region is used.
+ * If in the future multiple cameras are supported, this logic can be extended. */
+ const ARegion *region_camera = NULL;
+
+ /* An undo push should be performed. */
+ bool is_interactive = false;
+ LISTBASE_FOREACH (const ARegion *, region, &area->regionbase) {
+ if (region->regiontype != RGN_TYPE_WINDOW) {
+ continue;
+ }
+ const RegionView3D *rv3d = region->regiondata;
+ if (ED_view3d_camera_lock_undo_test(v3d, rv3d, C)) {
+ region_camera = region;
+ if (rv3d->sms) {
+ is_interactive = true;
+ }
+ }
+ }
+
+ if (region_camera == NULL) {
+ return;
+ }
+
+ RegionView3D *rv3d = region_camera->regiondata;
+
+ /* Fast forward, undo push, then rewind. */
+ if (is_interactive) {
+ view3d_smoothview_apply_with_interp(C, v3d, rv3d, false, 1.0f);
+ }
+
+ if (undo_grouped) {
+ ED_view3d_camera_lock_undo_grouped_push(undo_str, v3d, rv3d, C);
+ }
+ else {
+ ED_view3d_camera_lock_undo_push(undo_str, v3d, rv3d, C);
+ }
+
+ if (is_interactive) {
+ view3d_smoothview_apply_with_interp(C, v3d, rv3d, false, 0.0f);
+ }
+}
+
+/** \} */
+
/* -------------------------------------------------------------------- */
/** \name Smooth View Operator & Utilities
*
@@ -86,6 +196,11 @@ void ED_view3d_smooth_view_ex(
const int smooth_viewtx,
const V3D_SmoothParams *sview)
{
+ /* In this case use #ED_view3d_smooth_view_undo_begin & end functions
+ * instead of passing in undo. */
+ BLI_assert_msg(sview->undo_str == NULL,
+ "Only the 'ED_view3d_smooth_view' version of this function handles undo!");
+
RegionView3D *rv3d = region->regiondata;
struct SmoothView3DStore sms = {{0}};
@@ -236,6 +351,13 @@ void ED_view3d_smooth_view_ex(
WM_event_add_mousemove(win);
}
+
+ if (sms.to_camera == false) {
+ /* See comments in #ED_view3d_smooth_view_undo_begin for why this is needed. */
+ if (v3d->camera) {
+ v3d->camera->id.tag &= ~LIB_TAG_DOIT;
+ }
+ }
}
void ED_view3d_smooth_view(bContext *C,
@@ -249,97 +371,129 @@ void ED_view3d_smooth_view(bContext *C,
wmWindow *win = CTX_wm_window(C);
ScrArea *area = CTX_wm_area(C);
- ED_view3d_smooth_view_ex(depsgraph, wm, win, area, v3d, region, smooth_viewtx, sview);
+ /* #ED_view3d_smooth_view_ex asserts this is not set as it doesn't support undo. */
+ struct V3D_SmoothParams sview_no_undo = *sview;
+ sview_no_undo.undo_str = NULL;
+ sview_no_undo.undo_grouped = false;
+
+ const bool do_undo = (sview->undo_str != NULL);
+ if (do_undo) {
+ ED_view3d_smooth_view_undo_begin(C, area);
+ }
+
+ ED_view3d_smooth_view_ex(depsgraph, wm, win, area, v3d, region, smooth_viewtx, &sview_no_undo);
+
+ if (do_undo) {
+ ED_view3d_smooth_view_undo_end(C, area, sview->undo_str, sview->undo_grouped);
+ }
}
-/* only meant for timer usage */
-static void view3d_smoothview_apply(bContext *C, View3D *v3d, ARegion *region, bool sync_boxview)
+/**
+ * Apply with interpolation, on completion run #view3d_smoothview_apply_and_finish.
+ */
+static void view3d_smoothview_apply_with_interp(
+ bContext *C, View3D *v3d, RegionView3D *rv3d, const bool use_autokey, const float factor)
{
- wmWindowManager *wm = CTX_wm_manager(C);
- RegionView3D *rv3d = region->regiondata;
struct SmoothView3DStore *sms = rv3d->sms;
- float step, step_inv;
- if (sms->time_allowed != 0.0) {
- step = (float)((rv3d->smooth_timer->duration) / sms->time_allowed);
+ interp_qt_qtqt(rv3d->viewquat, sms->src.quat, sms->dst.quat, factor);
+
+ if (sms->use_dyn_ofs) {
+ view3d_orbit_apply_dyn_ofs(
+ rv3d->ofs, sms->src.ofs, sms->src.quat, rv3d->viewquat, sms->dyn_ofs);
}
else {
- step = 1.0f;
+ interp_v3_v3v3(rv3d->ofs, sms->src.ofs, sms->dst.ofs, factor);
}
- /* end timer */
- if (step >= 1.0f) {
- wmWindow *win = CTX_wm_window(C);
+ rv3d->dist = interpf(sms->dst.dist, sms->src.dist, factor);
+ v3d->lens = interpf(sms->dst.lens, sms->src.lens, factor);
- /* if we went to camera, store the original */
- if (sms->to_camera) {
- rv3d->persp = RV3D_CAMOB;
- view3d_smooth_view_state_restore(&sms->org, v3d, rv3d);
- }
- else {
- const Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
-
- view3d_smooth_view_state_restore(&sms->dst, v3d, rv3d);
-
- ED_view3d_camera_lock_sync(depsgraph, v3d, rv3d);
+ const Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
+ if (ED_view3d_camera_lock_sync(depsgraph, v3d, rv3d)) {
+ if (use_autokey) {
ED_view3d_camera_lock_autokey(v3d, rv3d, C, true, true);
}
+ }
+}
- if ((RV3D_LOCK_FLAGS(rv3d) & RV3D_LOCK_ROTATION) == 0) {
- rv3d->view = sms->org_view;
- }
-
- MEM_freeN(rv3d->sms);
- rv3d->sms = NULL;
+/**
+ * Apply the view-port transformation & free smooth-view related data.
+ */
+static void view3d_smoothview_apply_and_finish(bContext *C, View3D *v3d, RegionView3D *rv3d)
+{
+ wmWindowManager *wm = CTX_wm_manager(C);
+ struct SmoothView3DStore *sms = rv3d->sms;
- WM_event_remove_timer(wm, win, rv3d->smooth_timer);
- rv3d->smooth_timer = NULL;
- rv3d->rflag &= ~RV3D_NAVIGATING;
+ wmWindow *win = CTX_wm_window(C);
- /* Event handling won't know if a UI item has been moved under the pointer. */
- WM_event_add_mousemove(win);
+ /* if we went to camera, store the original */
+ if (sms->to_camera) {
+ rv3d->persp = RV3D_CAMOB;
+ view3d_smooth_view_state_restore(&sms->org, v3d, rv3d);
}
else {
- /* ease in/out */
- step = (3.0f * step * step - 2.0f * step * step * step);
-
- step_inv = 1.0f - step;
-
- interp_qt_qtqt(rv3d->viewquat, sms->src.quat, sms->dst.quat, step);
-
- if (sms->use_dyn_ofs) {
- view3d_orbit_apply_dyn_ofs(
- rv3d->ofs, sms->src.ofs, sms->src.quat, rv3d->viewquat, sms->dyn_ofs);
- }
- else {
- interp_v3_v3v3(rv3d->ofs, sms->src.ofs, sms->dst.ofs, step);
- }
+ const Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
- rv3d->dist = sms->dst.dist * step + sms->src.dist * step_inv;
- v3d->lens = sms->dst.lens * step + sms->src.lens * step_inv;
+ view3d_smooth_view_state_restore(&sms->dst, v3d, rv3d);
- const Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
- ED_view3d_camera_lock_sync(depsgraph, v3d, rv3d);
- if (ED_screen_animation_playing(wm)) {
+ if (ED_view3d_camera_lock_sync(depsgraph, v3d, rv3d)) {
ED_view3d_camera_lock_autokey(v3d, rv3d, C, true, true);
}
}
- if (sync_boxview && (RV3D_LOCK_FLAGS(rv3d) & RV3D_BOXVIEW)) {
- view3d_boxview_copy(CTX_wm_area(C), region);
+ if ((RV3D_LOCK_FLAGS(rv3d) & RV3D_LOCK_ROTATION) == 0) {
+ rv3d->view = sms->org_view;
}
+ MEM_freeN(rv3d->sms);
+ rv3d->sms = NULL;
+
+ WM_event_remove_timer(wm, win, rv3d->smooth_timer);
+ rv3d->smooth_timer = NULL;
+ rv3d->rflag &= ~RV3D_NAVIGATING;
+
+ /* Event handling won't know if a UI item has been moved under the pointer. */
+ WM_event_add_mousemove(win);
+
/* NOTE: this doesn't work right because the v3d->lens is now used in ortho mode r51636,
* when switching camera in quad-view the other ortho views would zoom & reset.
*
* For now only redraw all regions when smooth-view finishes.
*/
- if (step >= 1.0f) {
- WM_event_add_notifier(C, NC_SPACE | ND_SPACE_VIEW3D, v3d);
+ WM_event_add_notifier(C, NC_SPACE | ND_SPACE_VIEW3D, v3d);
+}
+
+/* only meant for timer usage */
+
+static void view3d_smoothview_apply_from_timer(bContext *C, View3D *v3d, ARegion *region)
+{
+ wmWindowManager *wm = CTX_wm_manager(C);
+ RegionView3D *rv3d = region->regiondata;
+ struct SmoothView3DStore *sms = rv3d->sms;
+ float factor;
+
+ if (sms->time_allowed != 0.0) {
+ factor = (float)((rv3d->smooth_timer->duration) / sms->time_allowed);
}
else {
- ED_region_tag_redraw(region);
+ factor = 1.0f;
+ }
+ if (factor >= 1.0f) {
+ view3d_smoothview_apply_and_finish(C, v3d, rv3d);
}
+ else {
+ /* Ease in/out smoothing. */
+ factor = (3.0f * factor * factor - 2.0f * factor * factor * factor);
+ const bool use_autokey = ED_screen_animation_playing(wm);
+ view3d_smoothview_apply_with_interp(C, v3d, rv3d, use_autokey, factor);
+ }
+
+ if (RV3D_LOCK_FLAGS(rv3d) & RV3D_BOXVIEW) {
+ view3d_boxview_copy(CTX_wm_area(C), region);
+ }
+
+ ED_region_tag_redraw(region);
}
static int view3d_smoothview_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *event)
@@ -353,7 +507,7 @@ static int view3d_smoothview_invoke(bContext *C, wmOperator *UNUSED(op), const w
return OPERATOR_PASS_THROUGH;
}
- view3d_smoothview_apply(C, v3d, region, true);
+ view3d_smoothview_apply_from_timer(C, v3d, region);
return OPERATOR_FINISHED;
}
@@ -361,12 +515,10 @@ static int view3d_smoothview_invoke(bContext *C, wmOperator *UNUSED(op), const w
void ED_view3d_smooth_view_force_finish(bContext *C, View3D *v3d, ARegion *region)
{
RegionView3D *rv3d = region->regiondata;
-
if (rv3d && rv3d->sms) {
- rv3d->sms->time_allowed = 0.0; /* force finishing */
- view3d_smoothview_apply(C, v3d, region, false);
+ view3d_smoothview_apply_and_finish(C, v3d, rv3d);
- /* force update of view matrix so tools that run immediately after
+ /* Force update of view matrix so tools that run immediately after
* can use them without redrawing first */
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
Scene *scene = CTX_data_scene(C);
diff --git a/source/blender/editors/space_view3d/view3d_navigate_zoom_border.c b/source/blender/editors/space_view3d/view3d_navigate_zoom_border.c
index f834efe4a7b..7cafc3dfd42 100644
--- a/source/blender/editors/space_view3d/view3d_navigate_zoom_border.c
+++ b/source/blender/editors/space_view3d/view3d_navigate_zoom_border.c
@@ -159,11 +159,15 @@ static int view3d_zoom_border_exec(bContext *C, wmOperator *op)
/* clamp after because we may have been zooming out */
CLAMP(new_dist, dist_range[0], dist_range[1]);
- /* TODO(campbell): 'is_camera_lock' not currently working well. */
const bool is_camera_lock = ED_view3d_camera_lock_check(v3d, rv3d);
- if ((rv3d->persp == RV3D_CAMOB) && (is_camera_lock == false)) {
+ if (rv3d->persp == RV3D_CAMOB) {
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
- ED_view3d_persp_switch_from_camera(depsgraph, v3d, rv3d, RV3D_PERSP);
+ if (is_camera_lock) {
+ ED_view3d_camera_lock_init(depsgraph, v3d, rv3d);
+ }
+ else {
+ ED_view3d_persp_switch_from_camera(depsgraph, v3d, rv3d, RV3D_PERSP);
+ }
}
ED_view3d_smooth_view(C,
@@ -173,6 +177,7 @@ static int view3d_zoom_border_exec(bContext *C, wmOperator *op)
&(const V3D_SmoothParams){
.ofs = new_ofs,
.dist = &new_dist,
+ .undo_str = op->type->name,
});
if (RV3D_LOCK_FLAGS(rv3d) & RV3D_BOXVIEW) {
diff --git a/source/blender/editors/space_view3d/view3d_select.c b/source/blender/editors/space_view3d/view3d_select.c
index 8eff9ee472f..763848574ed 100644
--- a/source/blender/editors/space_view3d/view3d_select.c
+++ b/source/blender/editors/space_view3d/view3d_select.c
@@ -335,14 +335,16 @@ static bool edbm_backbuf_check_and_select_verts_obmode(Mesh *me,
const eSelectOp sel_op)
{
MVert *mv = me->mvert;
- uint index;
bool changed = false;
const BLI_bitmap *select_bitmap = esel->select_bitmap;
if (mv) {
- for (index = 0; index < me->totvert; index++, mv++) {
- if (!(mv->flag & ME_HIDE)) {
+ const bool *hide_vert = (const bool *)CustomData_get_layer_named(
+ &me->vdata, CD_PROP_BOOL, ".hide_vert");
+
+ for (int index = 0; index < me->totvert; index++, mv++) {
+ if (!(hide_vert && hide_vert[index])) {
const bool is_select = mv->flag & SELECT;
const bool is_inside = BLI_BITMAP_TEST_BOOL(select_bitmap, index);
const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside);
@@ -362,14 +364,16 @@ static bool edbm_backbuf_check_and_select_faces_obmode(Mesh *me,
const eSelectOp sel_op)
{
MPoly *mpoly = me->mpoly;
- uint index;
bool changed = false;
const BLI_bitmap *select_bitmap = esel->select_bitmap;
if (mpoly) {
- for (index = 0; index < me->totpoly; index++, mpoly++) {
- if (!(mpoly->flag & ME_HIDE)) {
+ const bool *hide_poly = (const bool *)CustomData_get_layer_named(
+ &me->pdata, CD_PROP_BOOL, ".hide_poly");
+
+ for (int index = 0; index < me->totpoly; index++, mpoly++) {
+ if (!(hide_poly && hide_poly[index])) {
const bool is_select = mpoly->flag & ME_FACE_SEL;
const bool is_inside = BLI_BITMAP_TEST_BOOL(select_bitmap, index);
const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside);
@@ -1260,7 +1264,7 @@ static bool do_lasso_select_paintface(ViewContext *vc,
}
if (changed) {
- paintface_flush_flags(vc->C, ob, SELECT);
+ paintface_flush_flags(vc->C, ob, true, false);
}
return changed;
}
@@ -2893,11 +2897,6 @@ static int view3d_select_exec(bContext *C, wmOperator *op)
bool changed = false;
int mval[2];
- RNA_int_get_array(op->ptr, "location", mval);
-
- view3d_operator_needs_opengl(C);
- BKE_object_update_select_id(CTX_data_main(C));
-
if (object_only) {
obedit = NULL;
obact = NULL;
@@ -2908,6 +2907,19 @@ static int view3d_select_exec(bContext *C, wmOperator *op)
center = false;
}
+ if (obedit && enumerate) {
+ /* Enumerate makes no sense in edit-mode unless also explicitly picking objects or bones.
+ * Pass the event through so the event may be handled by loop-select for e.g. see: T100204. */
+ if (obedit->type != OB_ARMATURE) {
+ return OPERATOR_PASS_THROUGH | OPERATOR_CANCELLED;
+ }
+ }
+
+ RNA_int_get_array(op->ptr, "location", mval);
+
+ view3d_operator_needs_opengl(C);
+ BKE_object_update_select_id(CTX_data_main(C));
+
if (obedit && object_only == false) {
if (obedit->type == OB_MESH) {
changed = EDBM_select_pick(C, mval, &params);
@@ -3184,7 +3196,7 @@ static bool do_paintface_box_select(ViewContext *vc,
}
if (changed) {
- paintface_flush_flags(vc->C, vc->obact, SELECT);
+ paintface_flush_flags(vc->C, vc->obact, true, false);
}
return changed;
}
@@ -4085,7 +4097,7 @@ static bool paint_facesel_circle_select(ViewContext *vc,
}
if (changed) {
- paintface_flush_flags(vc->C, ob, SELECT);
+ paintface_flush_flags(vc->C, ob, true, false);
}
return changed;
}
diff --git a/source/blender/editors/space_view3d/view3d_utils.c b/source/blender/editors/space_view3d/view3d_utils.c
index 99f8cbc975b..5f2a4e8c4cc 100644
--- a/source/blender/editors/space_view3d/view3d_utils.c
+++ b/source/blender/editors/space_view3d/view3d_utils.c
@@ -689,6 +689,18 @@ bool ED_view3d_camera_lock_autokey(View3D *v3d,
return false;
}
+bool ED_view3d_camera_lock_undo_test(const View3D *v3d,
+ const RegionView3D *rv3d,
+ struct bContext *C)
+{
+ if (ED_view3d_camera_lock_check(v3d, rv3d)) {
+ if (ED_undo_is_memfile_compatible(C)) {
+ return true;
+ }
+ }
+ return false;
+}
+
/**
* Create a MEMFILE undo-step for locked camera movement when transforming the view.
* Edit and texture paint mode don't use MEMFILE undo so undo push is skipped for them.
@@ -696,31 +708,35 @@ bool ED_view3d_camera_lock_autokey(View3D *v3d,
* unnecessary undo steps so undo push for them is not supported for now. Also operators that uses
* smooth view for navigation are excluded too, but they can be supported, see: D15345.
*/
-static bool view3d_camera_lock_undo_ex(
- const char *str, View3D *v3d, RegionView3D *rv3d, struct bContext *C, bool undo_group)
-{
- if (ED_view3d_camera_lock_check(v3d, rv3d)) {
- if (ED_undo_is_memfile_compatible(C)) {
- if (undo_group) {
- ED_undo_grouped_push(C, str);
- }
- else {
- ED_undo_push(C, str);
- }
- return true;
+static bool view3d_camera_lock_undo_ex(const char *str,
+ const View3D *v3d,
+ const RegionView3D *rv3d,
+ struct bContext *C,
+ const bool undo_group)
+{
+ if (ED_view3d_camera_lock_undo_test(v3d, rv3d, C)) {
+ if (undo_group) {
+ ED_undo_grouped_push(C, str);
+ }
+ else {
+ ED_undo_push(C, str);
}
+ return true;
}
return false;
}
-bool ED_view3d_camera_lock_undo_push(const char *str, View3D *v3d, RegionView3D *rv3d, bContext *C)
+bool ED_view3d_camera_lock_undo_push(const char *str,
+ const View3D *v3d,
+ const RegionView3D *rv3d,
+ bContext *C)
{
return view3d_camera_lock_undo_ex(str, v3d, rv3d, C, false);
}
bool ED_view3d_camera_lock_undo_grouped_push(const char *str,
- View3D *v3d,
- RegionView3D *rv3d,
+ const View3D *v3d,
+ const RegionView3D *rv3d,
bContext *C)
{
return view3d_camera_lock_undo_ex(str, v3d, rv3d, C, true);
diff --git a/source/blender/editors/space_view3d/view3d_view.c b/source/blender/editors/space_view3d/view3d_view.c
index fc88737ca70..b8042a9f215 100644
--- a/source/blender/editors/space_view3d/view3d_view.c
+++ b/source/blender/editors/space_view3d/view3d_view.c
@@ -202,6 +202,8 @@ static void sync_viewport_camera_smoothview(bContext *C,
.quat = other_rv3d->viewquat,
.dist = &other_rv3d->dist,
.lens = &other_v3d->lens,
+ /* No undo because this switches cameras. */
+ .undo_str = NULL,
});
}
else {
@@ -256,6 +258,8 @@ static int view3d_setobjectascamera_exec(bContext *C, wmOperator *op)
.quat = rv3d->viewquat,
.dist = &rv3d->dist,
.lens = &v3d->lens,
+ /* No undo because this switches cameras. */
+ .undo_str = NULL,
});
}
@@ -939,6 +943,8 @@ static bool view3d_localview_init(const Depsgraph *depsgraph,
.quat = rv3d->viewquat,
.dist = ok_dist ? &dist_new : NULL,
.lens = &v3d->lens,
+ /* No undo because this doesn't move the camera. */
+ .undo_str = NULL,
});
}
}
@@ -1008,6 +1014,8 @@ static void view3d_localview_exit(const Depsgraph *depsgraph,
.ofs = rv3d->localvd->ofs,
.quat = rv3d->localvd->viewquat,
.dist = &rv3d->localvd->dist,
+ /* No undo because this doesn't move the camera. */
+ .undo_str = NULL,
});
}
diff --git a/source/blender/editors/transform/transform_convert_mesh.c b/source/blender/editors/transform/transform_convert_mesh.c
index b5a85f57b84..f67a44703e5 100644
--- a/source/blender/editors/transform/transform_convert_mesh.c
+++ b/source/blender/editors/transform/transform_convert_mesh.c
@@ -1314,7 +1314,8 @@ void transform_convert_mesh_crazyspace_detect(TransInfo *t,
* correction with \a quats, relative to the coordinates after
* the modifiers that support deform matrices \a defcos. */
-#if 0 /* TODO(campbell): fix crazy-space & extrude so it can be enabled for general use. */
+#if 0 /* TODO(@campbellbarton): fix crazy-space & extrude so it can be enabled for general use. \
+ */
if ((totleft > 0) || (totleft == -1))
#else
if (totleft > 0)
@@ -2123,7 +2124,7 @@ void special_aftertrans_update__mesh(bContext *UNUSED(C), TransInfo *t)
FOREACH_TRANS_DATA_CONTAINER (t, tc) {
/* table needs to be created for each edit command, since vertices can move etc */
ED_mesh_mirror_spatial_table_end(tc->obedit);
- /* TODO(campbell): xform: We need support for many mirror objects at once! */
+ /* TODO(@campbellbarton): xform: We need support for many mirror objects at once! */
break;
}
}
diff --git a/source/blender/editors/transform/transform_convert_mesh_uv.c b/source/blender/editors/transform/transform_convert_mesh_uv.c
index d95bc7b976f..f3bef2c283b 100644
--- a/source/blender/editors/transform/transform_convert_mesh_uv.c
+++ b/source/blender/editors/transform/transform_convert_mesh_uv.c
@@ -270,7 +270,7 @@ static void createTransUVs(bContext *C, TransInfo *t)
continue;
}
- island_center = MEM_callocN(sizeof(*island_center) * elementmap->totalIslands, __func__);
+ island_center = MEM_callocN(sizeof(*island_center) * elementmap->total_islands, __func__);
}
BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
@@ -315,9 +315,7 @@ static void createTransUVs(bContext *C, TransInfo *t)
}
if (is_island_center) {
- int i;
-
- for (i = 0; i < elementmap->totalIslands; i++) {
+ for (int i = 0; i < elementmap->total_islands; i++) {
mul_v2_fl(island_center[i].co, 1.0f / island_center[i].co_num);
mul_v2_v2(island_center[i].co, t->aspect);
}
diff --git a/source/blender/editors/transform/transform_gizmo_extrude_3d.c b/source/blender/editors/transform/transform_gizmo_extrude_3d.c
index 131a7fd517f..a3b5fd2c575 100644
--- a/source/blender/editors/transform/transform_gizmo_extrude_3d.c
+++ b/source/blender/editors/transform/transform_gizmo_extrude_3d.c
@@ -261,7 +261,7 @@ static void gizmo_mesh_extrude_refresh(const bContext *C, wmGizmoGroup *gzgroup)
copy_m3_m3(ggd->data.normal_mat3, tbounds_normal.axis);
}
- /* TODO(campbell): run second since this modifies the 3D view, it should not. */
+ /* TODO(@campbellbarton): run second since this modifies the 3D view, it should not. */
if (!ED_transform_calc_gizmo_stats(C,
&(struct TransformCalcParams){
.orientation_index = ggd->data.orientation_index + 1,
diff --git a/source/blender/editors/transform/transform_mode_bend.c b/source/blender/editors/transform/transform_mode_bend.c
index acc6b20810f..a48f84ef0bc 100644
--- a/source/blender/editors/transform/transform_mode_bend.c
+++ b/source/blender/editors/transform/transform_mode_bend.c
@@ -262,7 +262,7 @@ static void Bend(TransInfo *t, const int UNUSED(mval[2]))
+values.scale * shell_angle_to_dist((float)M_PI_2 + values.angle));
}
- /* TODO(campbell): xform, compensate object center. */
+ /* TODO(@campbellbarton): xform, compensate object center. */
FOREACH_TRANS_DATA_CONTAINER (t, tc) {
float warp_sta_local[3];
diff --git a/source/blender/editors/transform/transform_mode_edge_slide.c b/source/blender/editors/transform/transform_mode_edge_slide.c
index a3c49d2362f..b48ba0640ad 100644
--- a/source/blender/editors/transform/transform_mode_edge_slide.c
+++ b/source/blender/editors/transform/transform_mode_edge_slide.c
@@ -1204,7 +1204,7 @@ void drawEdgeSlide(TransInfo *t)
immUniformThemeColorShadeAlpha(TH_EDGE_SELECT, 80, alpha_shade);
immBegin(GPU_PRIM_LINES, sld->totsv * 2);
- /* TODO(campbell): Loop over all verts. */
+ /* TODO(@campbellbarton): Loop over all verts. */
sv = sld->sv;
for (i = 0; i < sld->totsv; i++, sv++) {
float a[3], b[3];
diff --git a/source/blender/editors/undo/ed_undo.c b/source/blender/editors/undo/ed_undo.c
index ce14e6b180f..bb24bdac690 100644
--- a/source/blender/editors/undo/ed_undo.c
+++ b/source/blender/editors/undo/ed_undo.c
@@ -269,7 +269,7 @@ static int ed_undo_step_direction(bContext *C, enum eUndoStepDir step, ReportLis
CLOG_INFO(&LOG, 1, "direction=%s", (step == STEP_UNDO) ? "STEP_UNDO" : "STEP_REDO");
- /* TODO(campbell): undo_system: use undo system */
+ /* TODO(@campbellbarton): undo_system: use undo system */
/* grease pencil can be can be used in plenty of spaces, so check it first */
/* FIXME: This gpencil undo effectively only supports the one step undo/redo, undo based on name
* or index is fully not implemented.
diff --git a/source/blender/editors/util/ed_util.c b/source/blender/editors/util/ed_util.c
index 3cfe6bd61a4..36a881ec158 100644
--- a/source/blender/editors/util/ed_util.c
+++ b/source/blender/editors/util/ed_util.c
@@ -176,7 +176,7 @@ void ED_editors_init(bContext *C)
}
}
else {
- /* TODO(campbell): avoid operator calls. */
+ /* TODO(@campbellbarton): avoid operator calls. */
if (obact == ob) {
ED_object_mode_set(C, mode);
}
diff --git a/source/blender/editors/util/ed_util_imbuf.c b/source/blender/editors/util/ed_util_imbuf.c
index fc5fb9f9c28..1ebbb0cecc3 100644
--- a/source/blender/editors/util/ed_util_imbuf.c
+++ b/source/blender/editors/util/ed_util_imbuf.c
@@ -431,7 +431,7 @@ void ED_imbuf_sample_draw(const bContext *C, ARegion *region, void *arg_info)
immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
immUniformColor3fv(color);
- /* TODO(campbell): lock to pixels. */
+ /* TODO(@campbellbarton): lock to pixels. */
rctf sample_rect_fl;
BLI_rctf_init_pt_radius(
&sample_rect_fl,
diff --git a/source/blender/editors/uvedit/uvedit_ops.c b/source/blender/editors/uvedit/uvedit_ops.c
index 092f0c49d8a..6755630d3ef 100644
--- a/source/blender/editors/uvedit/uvedit_ops.c
+++ b/source/blender/editors/uvedit/uvedit_ops.c
@@ -541,14 +541,11 @@ static bool uvedit_uv_straighten(Scene *scene, BMesh *bm, eUVWeldAlign tool)
}
bool changed = false;
-
- /* Loop backwards to simplify logic. */
- int j1 = element_map->total_uvs;
- for (int i = element_map->totalIslands - 1; i >= 0; --i) {
- int j0 = element_map->islandIndices[i];
- changed |= uvedit_uv_straighten_elements(
- element_map->storage + j0, j1 - j0, cd_loop_uv_offset, tool);
- j1 = j0;
+ for (int i = 0; i < element_map->total_islands; i++) {
+ changed |= uvedit_uv_straighten_elements(element_map->storage + element_map->island_indices[i],
+ element_map->island_total_uvs[i],
+ cd_loop_uv_offset,
+ tool);
}
BM_uv_element_map_free(element_map);
diff --git a/source/blender/editors/uvedit/uvedit_select.c b/source/blender/editors/uvedit/uvedit_select.c
index b1ed00e2c57..d88da21ef98 100644
--- a/source/blender/editors/uvedit/uvedit_select.c
+++ b/source/blender/editors/uvedit/uvedit_select.c
@@ -1422,7 +1422,7 @@ static BMLoop *bm_select_edgeloop_single_side_next(const Scene *scene,
scene, l_step, v_from_next, cd_loop_uv_offset);
}
-/* TODO(campbell): support this in the BMesh API, as we have for clearing other types. */
+/* TODO(@campbellbarton): support this in the BMesh API, as we have for clearing other types. */
static void bm_loop_tags_clear(BMesh *bm)
{
BMIter iter;
@@ -5395,7 +5395,7 @@ static void uv_isolate_selected_islands(const Scene *scene,
return;
}
- int num_islands = elementmap->totalIslands;
+ int num_islands = elementmap->total_islands;
/* Boolean array that tells if island with index i is completely selected or not. */
bool *is_island_not_selected = MEM_callocN(sizeof(bool) * (num_islands), __func__);
diff --git a/source/blender/editors/uvedit/uvedit_smart_stitch.c b/source/blender/editors/uvedit/uvedit_smart_stitch.c
index 26ed98ba236..4a2ea5c3aa6 100644
--- a/source/blender/editors/uvedit/uvedit_smart_stitch.c
+++ b/source/blender/editors/uvedit/uvedit_smart_stitch.c
@@ -287,14 +287,6 @@ static void stitch_update_header(StitchStateContainer *ssc, bContext *C)
}
}
-static int getNumOfIslandUvs(UvElementMap *elementMap, int island)
-{
- if (island == elementMap->totalIslands - 1) {
- return elementMap->total_uvs - elementMap->islandIndices[island];
- }
- return elementMap->islandIndices[island + 1] - elementMap->islandIndices[island];
-}
-
static void stitch_uv_rotate(const float mat[2][2],
const float medianPoint[2],
float uv[2],
@@ -419,10 +411,9 @@ static void stitch_calculate_island_snapping(StitchState *state,
int final)
{
BMesh *bm = state->em->bm;
- int i;
UvElement *element;
- for (i = 0; i < state->element_map->totalIslands; i++) {
+ for (int i = 0; i < state->element_map->total_islands; i++) {
if (island_stitch_data[i].addedForPreview) {
int numOfIslandUVs = 0, j;
int totelem = island_stitch_data[i].num_rot_elements_neg +
@@ -464,8 +455,8 @@ static void stitch_calculate_island_snapping(StitchState *state,
}
angle_to_mat2(rotation_mat, rotation);
- numOfIslandUVs = getNumOfIslandUvs(state->element_map, i);
- element = &state->element_map->storage[state->element_map->islandIndices[i]];
+ numOfIslandUVs = state->element_map->island_total_uvs[i];
+ element = &state->element_map->storage[state->element_map->island_indices[i]];
for (j = 0; j < numOfIslandUVs; j++, element++) {
/* stitchable uvs have already been processed, don't process */
if (!(element->flag & STITCH_PROCESSED)) {
@@ -984,7 +975,7 @@ static int stitch_process_data(StitchStateContainer *ssc,
preview_position[i].data_position = STITCH_NO_PREVIEW;
}
- island_stitch_data = MEM_callocN(sizeof(*island_stitch_data) * state->element_map->totalIslands,
+ island_stitch_data = MEM_callocN(sizeof(*island_stitch_data) * state->element_map->total_islands,
"stitch_island_data");
if (!island_stitch_data) {
return 0;
@@ -1009,7 +1000,7 @@ static int stitch_process_data(StitchStateContainer *ssc,
}
/* Remember stitchable candidates as places the 'I' button will stop at. */
- for (int island_idx = 0; island_idx < state->element_map->totalIslands; island_idx++) {
+ for (int island_idx = 0; island_idx < state->element_map->total_islands; island_idx++) {
state->island_is_stitchable[island_idx] = island_stitch_data[island_idx].stitchableCandidate ?
true :
false;
@@ -1017,10 +1008,10 @@ static int stitch_process_data(StitchStateContainer *ssc,
if (is_active_state) {
/* set static island to one that is added for preview */
- ssc->static_island %= state->element_map->totalIslands;
+ ssc->static_island %= state->element_map->total_islands;
while (!(island_stitch_data[ssc->static_island].stitchableCandidate)) {
ssc->static_island++;
- ssc->static_island %= state->element_map->totalIslands;
+ ssc->static_island %= state->element_map->total_islands;
/* this is entirely possible if for example limit stitching
* with no stitchable verts or no selection */
if (ssc->static_island == previous_island) {
@@ -1141,13 +1132,11 @@ static int stitch_process_data(StitchStateContainer *ssc,
* Setup preview for stitchable islands *
****************************************/
if (ssc->snap_islands) {
- for (i = 0; i < state->element_map->totalIslands; i++) {
+ for (i = 0; i < state->element_map->total_islands; i++) {
if (island_stitch_data[i].addedForPreview) {
- int numOfIslandUVs = 0, j;
- UvElement *element;
- numOfIslandUVs = getNumOfIslandUvs(state->element_map, i);
- element = &state->element_map->storage[state->element_map->islandIndices[i]];
- for (j = 0; j < numOfIslandUVs; j++, element++) {
+ int numOfIslandUVs = state->element_map->island_total_uvs[i];
+ UvElement *element = &state->element_map->storage[state->element_map->island_indices[i]];
+ for (int j = 0; j < numOfIslandUVs; j++, element++) {
stitch_set_face_preview_buffer_position(element->l->f, preview, preview_position);
}
}
@@ -2120,8 +2109,8 @@ static StitchState *stitch_init(bContext *C,
/***** initialize static island preview data *****/
state->tris_per_island = MEM_mallocN(
- sizeof(*state->tris_per_island) * state->element_map->totalIslands, "stitch island tris");
- for (i = 0; i < state->element_map->totalIslands; i++) {
+ sizeof(*state->tris_per_island) * state->element_map->total_islands, "stitch island tris");
+ for (i = 0; i < state->element_map->total_islands; i++) {
state->tris_per_island[i] = 0;
}
@@ -2133,7 +2122,7 @@ static StitchState *stitch_init(bContext *C,
}
}
- state->island_is_stitchable = MEM_callocN(sizeof(bool) * state->element_map->totalIslands,
+ state->island_is_stitchable = MEM_callocN(sizeof(bool) * state->element_map->total_islands,
"stitch I stops");
if (!state->island_is_stitchable) {
state_delete(state);
@@ -2157,7 +2146,7 @@ static bool goto_next_island(StitchStateContainer *ssc)
do {
ssc->static_island++;
- if (ssc->static_island >= active_state->element_map->totalIslands) {
+ if (ssc->static_island >= active_state->element_map->total_islands) {
/* go to next object */
ssc->active_object_index++;
ssc->active_object_index %= ssc->objects_len;
@@ -2307,7 +2296,7 @@ static int stitch_init_all(bContext *C, wmOperator *op)
ssc->static_island = RNA_int_get(op->ptr, "static_island");
StitchState *state = ssc->states[ssc->active_object_index];
- ssc->static_island %= state->element_map->totalIslands;
+ ssc->static_island %= state->element_map->total_islands;
/* If the initial active object doesn't have any stitchable islands
* then no active island will be seen in the UI.
diff --git a/source/blender/geometry/GEO_uv_parametrizer.h b/source/blender/geometry/GEO_uv_parametrizer.h
index 5285aefbd4c..ff110f18ffb 100644
--- a/source/blender/geometry/GEO_uv_parametrizer.h
+++ b/source/blender/geometry/GEO_uv_parametrizer.h
@@ -13,8 +13,8 @@ extern "C" {
#endif
typedef struct ParamHandle ParamHandle; /* Handle to an array of charts. */
-typedef intptr_t ParamKey; /* Key (hash) for identifying verts and faces. */
-#define PARAM_KEY_MAX INTPTR_MAX
+typedef uintptr_t ParamKey; /* Key (hash) for identifying verts and faces. */
+#define PARAM_KEY_MAX UINTPTR_MAX
/* -------------------------------------------------------------------- */
/** \name Chart Construction:
diff --git a/source/blender/geometry/intern/resample_curves.cc b/source/blender/geometry/intern/resample_curves.cc
index d61941aa071..e252e28805e 100644
--- a/source/blender/geometry/intern/resample_curves.cc
+++ b/source/blender/geometry/intern/resample_curves.cc
@@ -154,7 +154,7 @@ static void gather_point_attributes_to_interpolate(const CurveComponent &src_com
retrieve_attribute_spans(
ids, src_component, dst_component, result.src, result.dst, result.dst_attributes);
- /* Attributes that aren't interpolated like Bezier handles still have to be be copied
+ /* Attributes that aren't interpolated like Bezier handles still have to be copied
* to the result when there are any unselected curves of the corresponding type. */
retrieve_attribute_spans(ids_no_interpolation,
src_component,
diff --git a/source/blender/geometry/intern/uv_parametrizer.cc b/source/blender/geometry/intern/uv_parametrizer.cc
index ae2794bf9bc..b7526d82ecc 100644
--- a/source/blender/geometry/intern/uv_parametrizer.cc
+++ b/source/blender/geometry/intern/uv_parametrizer.cc
@@ -30,7 +30,7 @@
/* Special Purpose Hash */
-typedef intptr_t PHashKey;
+typedef uintptr_t PHashKey;
typedef struct PHashLink {
struct PHashLink *next;
@@ -45,7 +45,7 @@ typedef struct PHash {
/* Simplices */
-typedef struct PVert {
+struct PVert {
struct PVert *nextlink;
union PVertUnion {
@@ -58,9 +58,9 @@ typedef struct PVert {
float co[3];
float uv[2];
uint flag;
-} PVert;
+};
-typedef struct PEdge {
+struct PEdge {
struct PEdge *nextlink;
union PEdgeUnion {
@@ -76,9 +76,9 @@ typedef struct PEdge {
struct PFace *face;
float *orig_uv, old_uv[2];
uint flag;
-} PEdge;
+};
-typedef struct PFace {
+struct PFace {
struct PFace *nextlink;
union PFaceUnion {
@@ -89,8 +89,8 @@ typedef struct PFace {
} u;
struct PEdge *edge;
- uchar flag;
-} PFace;
+ uint flag;
+};
enum PVertFlag {
PVERT_PIN = 1,
@@ -123,7 +123,7 @@ enum PFaceFlag {
/* Chart */
-typedef struct PChart {
+struct PChart {
PVert *verts;
PEdge *edges;
PFace *faces;
@@ -151,7 +151,7 @@ typedef struct PChart {
} u;
bool has_pins;
-} PChart;
+};
enum PHandleState {
PHANDLE_STATE_ALLOCATED,
@@ -160,7 +160,7 @@ enum PHandleState {
PHANDLE_STATE_STRETCH,
};
-typedef struct ParamHandle {
+struct ParamHandle {
enum PHandleState state;
MemArena *arena;
MemArena *polyfill_arena;
@@ -181,7 +181,7 @@ typedef struct ParamHandle {
RNG *rng;
float blend;
-} ParamHandle;
+};
/* PHash
* - special purpose hash that keeps all its elements in a single linked list.
@@ -219,8 +219,8 @@ static void phash_delete(PHash *ph)
{
if (ph) {
MEM_SAFE_FREE(ph->buckets);
+ MEM_freeN(ph);
}
- MEM_SAFE_FREE(ph);
}
static int phash_size(PHash *ph)
@@ -234,7 +234,7 @@ static void phash_insert(PHash *ph, PHashLink *link)
uintptr_t hash = PHASH_hash(ph, link->key);
PHashLink *lookup = ph->buckets[hash];
- if (lookup == NULL) {
+ if (lookup == nullptr) {
/* insert in front of the list */
ph->buckets[hash] = link;
link->next = *(ph->list);
@@ -249,13 +249,13 @@ static void phash_insert(PHash *ph, PHashLink *link)
ph->size++;
if (ph->size > (size * 3)) {
- PHashLink *next = NULL, *first = *(ph->list);
+ PHashLink *next = nullptr, *first = *(ph->list);
ph->cursize = PHashSizes[++ph->cursize_id];
MEM_freeN(ph->buckets);
ph->buckets = (PHashLink **)MEM_callocN(ph->cursize * sizeof(*ph->buckets), "PHashBuckets");
ph->size = 0;
- *(ph->list) = NULL;
+ *(ph->list) = nullptr;
for (link = first; link; link = next) {
next = link->next;
@@ -274,7 +274,7 @@ static PHashLink *phash_lookup(PHash *ph, PHashKey key)
return link;
}
if (PHASH_hash(ph, link->key) != hash) {
- return NULL;
+ return nullptr;
}
}
@@ -290,7 +290,7 @@ static PHashLink *phash_next(PHash *ph, PHashKey key, PHashLink *link)
return link;
}
if (PHASH_hash(ph, link->key) != hash) {
- return NULL;
+ return nullptr;
}
}
@@ -456,7 +456,7 @@ static PEdge *p_wheel_edge_next(PEdge *e)
static PEdge *p_wheel_edge_prev(PEdge *e)
{
- return (e->pair) ? e->pair->next : NULL;
+ return (e->pair) ? e->pair->next : nullptr;
}
static PEdge *p_boundary_edge_next(PEdge *e)
@@ -694,7 +694,7 @@ static PEdge *p_edge_lookup(ParamHandle *handle, const PHashKey *vkeys)
e = (PEdge *)phash_next(handle->hash_edges, key, (PHashLink *)e);
}
- return NULL;
+ return nullptr;
}
static int p_face_exists(ParamHandle *handle, const ParamKey *pvkeys, int i1, int i2, int i3)
@@ -769,7 +769,7 @@ static bool p_edge_has_pair(ParamHandle *handle, PEdge *e, bool topology_from_uv
key = PHASH_edge(key1, key2);
pe = (PEdge *)phash_lookup(handle->hash_edges, key);
- *r_pair = NULL;
+ *r_pair = nullptr;
while (pe) {
if (pe != e) {
@@ -782,7 +782,7 @@ static bool p_edge_has_pair(ParamHandle *handle, PEdge *e, bool topology_from_uv
/* don't connect seams and t-junctions */
if ((pe->flag & PEDGE_SEAM) || *r_pair ||
(topology_from_uvs && p_edge_implicit_seam(e, pe))) {
- *r_pair = NULL;
+ *r_pair = nullptr;
return false;
}
@@ -796,12 +796,12 @@ static bool p_edge_has_pair(ParamHandle *handle, PEdge *e, bool topology_from_uv
if (*r_pair && (e->vert == (*r_pair)->vert)) {
if ((*r_pair)->next->pair || (*r_pair)->next->next->pair) {
/* non unfoldable, maybe mobius ring or klein bottle */
- *r_pair = NULL;
+ *r_pair = nullptr;
return false;
}
}
- return (*r_pair != NULL);
+ return (*r_pair != nullptr);
}
static bool p_edge_connect_pair(ParamHandle *handle,
@@ -809,7 +809,7 @@ static bool p_edge_connect_pair(ParamHandle *handle,
bool topology_from_uvs,
PEdge ***stack)
{
- PEdge *pair = NULL;
+ PEdge *pair = nullptr;
if (!e->pair && p_edge_has_pair(handle, e, topology_from_uvs, &pair)) {
if (e->vert == pair->vert) {
@@ -825,7 +825,7 @@ static bool p_edge_connect_pair(ParamHandle *handle,
}
}
- return (e->pair != NULL);
+ return (e->pair != nullptr);
}
static int p_connect_pairs(ParamHandle *handle, bool topology_from_uvs)
@@ -873,14 +873,14 @@ static int p_connect_pairs(ParamHandle *handle, bool topology_from_uvs)
ncharts++;
}
- MEM_SAFE_FREE(stackbase);
+ MEM_freeN(stackbase);
return ncharts;
}
static void p_split_vert(ParamHandle *handle, PChart *chart, PEdge *e)
{
- PEdge *we, *lastwe = NULL;
+ PEdge *we, *lastwe = nullptr;
PVert *v = e->vert;
bool copy = true;
@@ -993,9 +993,9 @@ static PFace *p_face_add(ParamHandle *handle)
e2->next = e3;
e3->next = e1;
- e1->pair = NULL;
- e2->pair = NULL;
- e3->pair = NULL;
+ e1->pair = nullptr;
+ e2->pair = nullptr;
+ e3->pair = nullptr;
e1->flag = 0;
e2->flag = 0;
@@ -1073,7 +1073,7 @@ static PFace *p_face_add_fill(ParamHandle *handle, PChart *chart, PVert *v1, PVe
e2->vert = v2;
e3->vert = v3;
- e1->orig_uv = e2->orig_uv = e3->orig_uv = NULL;
+ e1->orig_uv = e2->orig_uv = e3->orig_uv = nullptr;
f->nextlink = chart->faces;
chart->faces = f;
@@ -1125,7 +1125,7 @@ static void p_chart_boundaries(PChart *chart, PEdge **r_outer)
chart->nboundaries = 0;
if (r_outer) {
- *r_outer = NULL;
+ *r_outer = nullptr;
}
for (e = chart->edges; e; e = e->nextlink) {
@@ -1216,7 +1216,7 @@ static void p_chart_fill_boundary(ParamHandle *handle, PChart *chart, PEdge *be,
BLI_heap_remove(heap, e1->u.heaplink);
BLI_heap_remove(heap, e2->u.heaplink);
- e->u.heaplink = e1->u.heaplink = e2->u.heaplink = NULL;
+ e->u.heaplink = e1->u.heaplink = e2->u.heaplink = nullptr;
e->flag |= PEDGE_FILLED;
e1->flag |= PEDGE_FILLED;
@@ -1254,7 +1254,7 @@ static void p_chart_fill_boundary(ParamHandle *handle, PChart *chart, PEdge *be,
}
}
- BLI_heap_free(heap, NULL);
+ BLI_heap_free(heap, nullptr);
}
static void p_chart_fill_boundaries(ParamHandle *handle, PChart *chart, PEdge *outer)
@@ -1537,7 +1537,7 @@ static void p_vert_harmonic_insert(PVert *v)
e = p_wheel_edge_next(e);
} while (e && (e != v->edge));
- if (e == NULL) {
+ if (e == nullptr) {
npoints++;
}
@@ -1551,7 +1551,7 @@ static void p_vert_harmonic_insert(PVert *v)
points[i][0] = e->next->vert->uv[0];
points[i][1] = e->next->vert->uv[1];
- if (nexte == NULL) {
+ if (nexte == nullptr) {
i++;
points[i][0] = e->next->next->vert->uv[0];
points[i][1] = e->next->next->vert->uv[1];
@@ -1895,8 +1895,8 @@ static float p_collapse_cost(PEdge *edge, PEdge *pair)
int nshapeold = 0, nshapenew = 0;
p_collapsing_verts(edge, pair, &oldv, &keepv);
- oldf1 = (edge) ? edge->face : NULL;
- oldf2 = (pair) ? pair->face : NULL;
+ oldf1 = (edge) ? edge->face : nullptr;
+ oldf2 = (pair) ? pair->face : nullptr;
sub_v3_v3v3(edgevec, keepv->co, oldv->co);
@@ -1917,7 +1917,7 @@ static float p_collapse_cost(PEdge *edge, PEdge *pair)
# if 0
shapecost += dot_v3v3(co1, keepv->co);
- if (p_wheel_edge_next(e) == NULL) {
+ if (p_wheel_edge_next(e) == nullptr) {
shapecost += dot_v3v3(co2, keepv->co);
}
# endif
@@ -1970,14 +1970,14 @@ static void p_collapse_cost_vertex(PVert *vert, float *r_mincost, PEdge **r_mine
{
PEdge *e, *enext, *pair;
- *r_mine = NULL;
+ *r_mine = nullptr;
*r_mincost = 0.0f;
e = vert->edge;
do {
if (p_collapse_allowed(e, e->pair)) {
float cost = p_collapse_cost(e, e->pair);
- if ((*r_mine == NULL) || (cost < *r_mincost)) {
+ if ((*r_mine == nullptr) || (cost < *r_mincost)) {
*r_mincost = cost;
*r_mine = e;
}
@@ -1985,14 +1985,14 @@ static void p_collapse_cost_vertex(PVert *vert, float *r_mincost, PEdge **r_mine
enext = p_wheel_edge_next(e);
- if (enext == NULL) {
+ if (enext == nullptr) {
/* The other boundary edge, where we only have the pair half-edge. */
pair = e->next->next;
- if (p_collapse_allowed(NULL, pair)) {
- float cost = p_collapse_cost(NULL, pair);
+ if (p_collapse_allowed(nullptr, pair)) {
+ float cost = p_collapse_cost(nullptr, pair);
- if ((*r_mine == NULL) || (cost < *r_mincost)) {
+ if ((*r_mine == nullptr) || (cost < *r_mincost)) {
*r_mincost = cost;
*r_mine = pair;
}
@@ -2009,13 +2009,13 @@ static void p_chart_post_collapse_flush(PChart *chart, PEdge *collapsed)
{
/* Move to `collapsed_*`. */
- PVert *v, *nextv = NULL, *verts = chart->verts;
- PEdge *e, *nexte = NULL, *edges = chart->edges, *laste = NULL;
- PFace *f, *nextf = NULL, *faces = chart->faces;
+ PVert *v, *nextv = nullptr, *verts = chart->verts;
+ PEdge *e, *nexte = nullptr, *edges = chart->edges, *laste = nullptr;
+ PFace *f, *nextf = nullptr, *faces = chart->faces;
- chart->verts = chart->collapsed_verts = NULL;
- chart->edges = chart->collapsed_edges = NULL;
- chart->faces = chart->collapsed_faces = NULL;
+ chart->verts = chart->collapsed_verts = nullptr;
+ chart->edges = chart->collapsed_edges = nullptr;
+ chart->faces = chart->collapsed_faces = nullptr;
chart->nverts = chart->nedges = chart->nfaces = 0;
@@ -2079,9 +2079,9 @@ static void p_chart_post_split_flush(PChart *chart)
{
/* Move from `collapsed_*`. */
- PVert *v, *nextv = NULL;
- PEdge *e, *nexte = NULL;
- PFace *f, *nextf = NULL;
+ PVert *v, *nextv = nullptr;
+ PEdge *e, *nexte = nullptr;
+ PFace *f, *nextf = nullptr;
for (v = chart->collapsed_verts; v; v = nextv) {
nextv = v->nextlink;
@@ -2104,9 +2104,9 @@ static void p_chart_post_split_flush(PChart *chart)
chart->nfaces++;
}
- chart->collapsed_verts = NULL;
- chart->collapsed_edges = NULL;
- chart->collapsed_faces = NULL;
+ chart->collapsed_verts = nullptr;
+ chart->collapsed_edges = nullptr;
+ chart->collapsed_faces = nullptr;
}
static void p_chart_simplify_compute(PChart *chart)
@@ -2118,7 +2118,7 @@ static void p_chart_simplify_compute(PChart *chart)
Heap *heap = BLI_heap_new();
PVert *v, **wheelverts;
- PEdge *collapsededges = NULL, *e;
+ PEdge *collapsededges = nullptr, *e;
int nwheelverts, i, ncollapsed = 0;
wheelverts = MEM_mallocN(sizeof(PVert *) * chart->nverts, "PChartWheelVerts");
@@ -2126,7 +2126,7 @@ static void p_chart_simplify_compute(PChart *chart)
/* insert all potential collapses into heap */
for (v = chart->verts; v; v = v->nextlink) {
float cost;
- PEdge *e = NULL;
+ PEdge *e = nullptr;
p_collapse_cost_vertex(v, &cost, &e);
@@ -2134,12 +2134,12 @@ static void p_chart_simplify_compute(PChart *chart)
v->u.heaplink = BLI_heap_insert(heap, cost, e);
}
else {
- v->u.heaplink = NULL;
+ v->u.heaplink = nullptr;
}
}
for (e = chart->edges; e; e = e->nextlink) {
- e->u.nextcollapse = NULL;
+ e->u.nextcollapse = nullptr;
}
/* pop edge collapse out of heap one by one */
@@ -2159,12 +2159,12 @@ static void p_chart_simplify_compute(PChart *chart)
if (edge->vert->u.heaplink != link) {
edge->flag |= (PEDGE_COLLAPSE_EDGE | PEDGE_COLLAPSE_PAIR);
- edge->next->vert->u.heaplink = NULL;
+ edge->next->vert->u.heaplink = nullptr;
SWAP(PEdge *, edge, pair);
}
else {
edge->flag |= PEDGE_COLLAPSE_EDGE;
- edge->vert->u.heaplink = NULL;
+ edge->vert->u.heaplink = nullptr;
}
p_collapsing_verts(edge, pair, &oldv, &keepv);
@@ -2177,7 +2177,7 @@ static void p_chart_simplify_compute(PChart *chart)
wheelverts[nwheelverts++] = wheele->next->vert;
nexte = p_wheel_edge_next(wheele);
- if (nexte == NULL) {
+ if (nexte == nullptr) {
wheelverts[nwheelverts++] = wheele->next->next->vert;
}
@@ -2189,13 +2189,13 @@ static void p_chart_simplify_compute(PChart *chart)
for (i = 0; i < nwheelverts; i++) {
float cost;
- PEdge *collapse = NULL;
+ PEdge *collapse = nullptr;
v = wheelverts[i];
if (v->u.heaplink) {
BLI_heap_remove(heap, v->u.heaplink);
- v->u.heaplink = NULL;
+ v->u.heaplink = nullptr;
}
p_collapse_cost_vertex(v, &cost, &collapse);
@@ -2209,7 +2209,7 @@ static void p_chart_simplify_compute(PChart *chart)
}
MEM_freeN(wheelverts);
- BLI_heap_free(heap, NULL);
+ BLI_heap_free(heap, nullptr);
p_chart_post_collapse_flush(chart, collapsededges);
}
@@ -2260,14 +2260,14 @@ static void p_chart_simplify(PChart *chart)
#define ABF_MAX_ITER 20
-typedef struct PAbfSystem {
+using PAbfSystem = struct PAbfSystem {
int ninterior, nfaces, nangles;
float *alpha, *beta, *sine, *cosine, *weight;
float *bAlpha, *bTriangle, *bInterior;
float *lambdaTriangle, *lambdaPlanar, *lambdaLength;
float (*J2dt)[3], *bstar, *dstar;
float minangle, maxangle;
-} PAbfSystem;
+};
static void p_abf_setup_system(PAbfSystem *sys)
{
@@ -2847,8 +2847,8 @@ static void p_chart_pin_positions(PChart *chart, PVert **pin1, PVert **pin2)
static bool p_chart_symmetry_pins(PChart *chart, PEdge *outer, PVert **pin1, PVert **pin2)
{
- PEdge *be, *lastbe = NULL, *maxe1 = NULL, *maxe2 = NULL, *be1, *be2;
- PEdge *cure = NULL, *firste1 = NULL, *firste2 = NULL, *nextbe;
+ PEdge *be, *lastbe = nullptr, *maxe1 = nullptr, *maxe2 = nullptr, *be1, *be2;
+ PEdge *cure = nullptr, *firste1 = nullptr, *firste2 = nullptr, *nextbe;
float maxlen = 0.0f, curlen = 0.0f, totlen = 0.0f, firstlen = 0.0f;
float len1, len2;
@@ -2887,7 +2887,7 @@ static bool p_chart_symmetry_pins(PChart *chart, PEdge *outer, PVert **pin1, PVe
}
curlen = 0.0f;
- cure = NULL;
+ cure = nullptr;
}
lastbe = be;
@@ -2962,8 +2962,8 @@ static void p_chart_extrema_verts(PChart *chart, PVert **pin1, PVert **pin2)
minv[0] = minv[1] = minv[2] = 1e20;
maxv[0] = maxv[1] = maxv[2] = -1e20;
- minvert[0] = minvert[1] = minvert[2] = NULL;
- maxvert[0] = maxvert[1] = maxvert[2] = NULL;
+ minvert[0] = minvert[1] = minvert[2] = nullptr;
+ maxvert[0] = maxvert[1] = maxvert[2] = nullptr;
for (v = chart->verts; v; v = v->nextlink) {
for (i = 0; i < 3; i++) {
@@ -3027,7 +3027,7 @@ static void p_chart_lscm_begin(PChart *chart, bool live, bool abf)
}
if ((live && (!select || !deselect))) {
- chart->u.lscm.context = NULL;
+ chart->u.lscm.context = nullptr;
}
else {
#if 0
@@ -3245,13 +3245,13 @@ static void p_chart_lscm_transform_single_pin(PChart *chart)
static void p_chart_lscm_end(PChart *chart)
{
EIG_linear_solver_delete(chart->u.lscm.context);
- chart->u.lscm.context = NULL;
+ chart->u.lscm.context = nullptr;
MEM_SAFE_FREE(chart->u.lscm.abf_alpha);
- chart->u.lscm.pin1 = NULL;
- chart->u.lscm.pin2 = NULL;
- chart->u.lscm.single_pin = NULL;
+ chart->u.lscm.pin1 = nullptr;
+ chart->u.lscm.pin2 = nullptr;
+ chart->u.lscm.single_pin = nullptr;
chart->u.lscm.single_pin_area = 0.0f;
}
@@ -3264,7 +3264,7 @@ static void p_stretch_pin_boundary(PChart *chart)
PVert *v;
for (v = chart->verts; v; v = v->nextlink) {
- if (v->edge->pair == NULL) {
+ if (v->edge->pair == nullptr) {
v->flag |= PVERT_PIN;
}
else {
@@ -3495,8 +3495,8 @@ static bool p_chart_convex_hull(PChart *chart, PVert ***r_verts, int *r_nverts,
*r_nverts = npoints;
*r_right = ulen - 1;
- MEM_SAFE_FREE(U);
- MEM_SAFE_FREE(L);
+ MEM_freeN(U);
+ MEM_freeN(L);
return true;
}
@@ -3644,8 +3644,8 @@ static float p_chart_minimum_area_angle(PChart *chart)
minangle -= (float)M_PI_2;
}
- MEM_SAFE_FREE(angles);
- MEM_SAFE_FREE(points);
+ MEM_freeN(angles);
+ MEM_freeN(points);
return minangle;
}
@@ -3684,9 +3684,9 @@ static void p_chart_rotate_fit_aabb(PChart *chart)
/* Exported */
-ParamHandle *GEO_uv_parametrizer_construct_begin(void)
+ParamHandle *GEO_uv_parametrizer_construct_begin()
{
- ParamHandle *handle = (ParamHandle *)MEM_callocN(sizeof(*handle), "ParamHandle");
+ ParamHandle *handle = new ParamHandle();
handle->construction_chart = (PChart *)MEM_callocN(sizeof(PChart), "PChart");
handle->state = PHANDLE_STATE_ALLOCATED;
handle->arena = BLI_memarena_new(MEM_SIZE_OPTIMAL(1 << 16), "param construct arena");
@@ -3710,6 +3710,9 @@ void GEO_uv_parametrizer_aspect_ratio(ParamHandle *phandle, float aspx, float as
void GEO_uv_parametrizer_delete(ParamHandle *phandle)
{
+ if (!phandle) {
+ return;
+ }
param_assert(ELEM(phandle->state, PHANDLE_STATE_ALLOCATED, PHANDLE_STATE_CONSTRUCTED));
for (int i = 0; i < phandle->ncharts; i++) {
@@ -3719,8 +3722,8 @@ void GEO_uv_parametrizer_delete(ParamHandle *phandle)
MEM_SAFE_FREE(phandle->charts);
if (phandle->pin_hash) {
- BLI_ghash_free(phandle->pin_hash, NULL, NULL);
- phandle->pin_hash = NULL;
+ BLI_ghash_free(phandle->pin_hash, nullptr, nullptr);
+ phandle->pin_hash = nullptr;
}
MEM_SAFE_FREE(phandle->construction_chart);
@@ -3731,19 +3734,21 @@ void GEO_uv_parametrizer_delete(ParamHandle *phandle)
BLI_memarena_free(phandle->arena);
BLI_memarena_free(phandle->polyfill_arena);
- BLI_heap_free(phandle->polyfill_heap, NULL);
+ BLI_heap_free(phandle->polyfill_heap, nullptr);
- BLI_rng_free(phandle->rng);
- phandle->rng = NULL;
+ if (phandle->rng) {
+ BLI_rng_free(phandle->rng);
+ phandle->rng = nullptr;
+ }
- MEM_freeN(phandle);
+ delete phandle;
}
-typedef struct GeoUVPinIndex {
+using GeoUVPinIndex = struct GeoUVPinIndex {
struct GeoUVPinIndex *next;
float uv[2];
ParamKey reindex;
-} GeoUVPinIndex;
+};
/* Find a (mostly) unique ParamKey given a BMVert index and UV co-ordinates.
* For each unique pinned UVs, return a unique ParamKey, starting with
@@ -3783,7 +3788,7 @@ ParamKey GEO_uv_find_pin_index(ParamHandle *handle, const int bmvertindex, const
static GeoUVPinIndex *new_geo_uv_pinindex(ParamHandle *handle, const float uv[2])
{
GeoUVPinIndex *pinuv = (GeoUVPinIndex *)BLI_memarena_alloc(handle->arena, sizeof(*pinuv));
- pinuv->next = NULL;
+ pinuv->next = nullptr;
copy_v2_v2(pinuv->uv, uv);
pinuv->reindex = PARAM_KEY_MAX - (handle->unique_pin_count++);
return pinuv;
@@ -3860,7 +3865,7 @@ static void p_add_ngon(ParamHandle *handle,
BLI_polyfill_beautify(projverts, nverts, tris, arena, heap);
/* Add triangles. */
- for (int j = 0; j < nfilltri; j++) {
+ for (uint j = 0; j < nfilltri; j++) {
uint *tri = tris[j];
uint v0 = tri[0];
uint v1 = tri[1];
@@ -3887,7 +3892,7 @@ void GEO_uv_parametrizer_face_add(ParamHandle *phandle,
const bool *pin,
const bool *select)
{
- param_assert(phash_lookup(phandle->hash_faces, key) == NULL);
+ param_assert(phash_lookup(phandle->hash_faces, key) == nullptr);
param_assert(phandle->state == PHANDLE_STATE_ALLOCATED);
param_assert(ELEM(nverts, 3, 4));
@@ -3938,12 +3943,13 @@ void GEO_uv_parametrizer_construct_end(ParamHandle *phandle,
phandle->ncharts = p_connect_pairs(phandle, topology_from_uvs);
phandle->charts = p_split_charts(phandle, chart, phandle->ncharts);
- MEM_SAFE_FREE(phandle->construction_chart);
+ MEM_freeN(phandle->construction_chart);
+ phandle->construction_chart = nullptr;
phash_delete(phandle->hash_verts);
phash_delete(phandle->hash_edges);
phash_delete(phandle->hash_faces);
- phandle->hash_verts = phandle->hash_edges = phandle->hash_faces = NULL;
+ phandle->hash_verts = phandle->hash_edges = phandle->hash_faces = nullptr;
for (i = j = 0; i < phandle->ncharts; i++) {
PVert *v;
@@ -3952,8 +3958,8 @@ void GEO_uv_parametrizer_construct_end(ParamHandle *phandle,
p_chart_boundaries(chart, &outer);
if (!topology_from_uvs && chart->nboundaries == 0) {
- MEM_SAFE_FREE(chart);
- if (count_fail != NULL) {
+ MEM_freeN(chart);
+ if (count_fail != nullptr) {
*count_fail += 1;
}
continue;
@@ -4018,12 +4024,12 @@ void GEO_uv_parametrizer_lscm_solve(ParamHandle *phandle, int *count_changed, in
}
if (result) {
- if (count_changed != NULL) {
+ if (count_changed != nullptr) {
*count_changed += 1;
}
}
else {
- if (count_failed != NULL) {
+ if (count_failed != nullptr) {
*count_failed += 1;
}
}
@@ -4214,7 +4220,7 @@ void GEO_uv_parametrizer_pack(ParamHandle *handle,
p_chart_uv_translate(chart, trans);
p_chart_uv_scale(chart, scale);
}
- MEM_SAFE_FREE(boxarray);
+ MEM_freeN(boxarray);
if (handle->aspx != handle->aspy) {
GEO_uv_parametrizer_scale(handle, handle->aspx, handle->aspy);
diff --git a/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c b/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c
index 011c79025c4..0fd1d8ff51d 100644
--- a/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c
+++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c
@@ -50,7 +50,7 @@
#include "lineart_intern.h"
typedef struct LineartIsecSingle {
- float v1[3], v2[3];
+ double v1[3], v2[3];
LineartTriangle *tri1, *tri2;
} LineartIsecSingle;
@@ -1799,7 +1799,7 @@ static void lineart_add_edge_to_array_thread(LineartObjectInfo *obi, LineartEdge
* #pe. */
void lineart_finalize_object_edge_array_reserve(LineartPendingEdges *pe, int count)
{
- if (pe->max || pe->array) {
+ if (pe->max || pe->array || count == 0) {
return;
}
@@ -3285,8 +3285,8 @@ static void lineart_add_isec_thread(LineartIsecThread *th,
th->array = new_array;
}
LineartIsecSingle *isec_single = &th->array[th->current];
- copy_v3fl_v3db(isec_single->v1, v1);
- copy_v3fl_v3db(isec_single->v2, v2);
+ copy_v3_v3_db(isec_single->v1, v1);
+ copy_v3_v3_db(isec_single->v2, v2);
isec_single->tri1 = tri1;
isec_single->tri2 = tri2;
if (tri1->target_reference > tri2->target_reference) {
@@ -4582,8 +4582,8 @@ static void lineart_create_edges_from_isec_data(LineartIsecData *d)
LineartIsecSingle *is = &th->array[j];
LineartVert *v1 = v;
LineartVert *v2 = v + 1;
- copy_v3db_v3fl(v1->gloc, is->v1);
- copy_v3db_v3fl(v2->gloc, is->v2);
+ copy_v3_v3_db(v1->gloc, is->v1);
+ copy_v3_v3_db(v2->gloc, is->v2);
/* The intersection line has been generated only in geometry space, so we need to transform
* them as well. */
mul_v4_m4v3_db(v1->fbcoord, ld->conf.view_projection, v1->gloc);
@@ -5227,7 +5227,7 @@ static void lineart_gpencil_generate(LineartCache *cache,
}
if (shaodow_selection) {
if (ec->shadow_mask_bits != LRT_SHADOW_MASK_UNDEFINED) {
- /* TODO(Yiming): Give a behaviour option for how to display undefined shadow info. */
+ /* TODO(Yiming): Give a behavior option for how to display undefined shadow info. */
if ((shaodow_selection == LRT_SHADOW_FILTER_ILLUMINATED &&
(!(ec->shadow_mask_bits & LRT_SHADOW_MASK_ILLUMINATED)))) {
continue;
diff --git a/source/blender/gpu/CMakeLists.txt b/source/blender/gpu/CMakeLists.txt
index 4157caf45d7..65a6a2dc6b7 100644
--- a/source/blender/gpu/CMakeLists.txt
+++ b/source/blender/gpu/CMakeLists.txt
@@ -190,8 +190,8 @@ set(OPENGL_SRC
set(METAL_SRC
metal/mtl_backend.mm
- metal/mtl_context.mm
metal/mtl_command_buffer.mm
+ metal/mtl_context.mm
metal/mtl_debug.mm
metal/mtl_framebuffer.mm
metal/mtl_memory.mm
@@ -323,6 +323,45 @@ set(GLSL_SRC
shaders/common/gpu_shader_common_math_utils.glsl
shaders/common/gpu_shader_common_mix_rgb.glsl
+ shaders/compositor/compositor_alpha_crop.glsl
+ shaders/compositor/compositor_box_mask.glsl
+ shaders/compositor/compositor_convert.glsl
+ shaders/compositor/compositor_ellipse_mask.glsl
+ shaders/compositor/compositor_flip.glsl
+ shaders/compositor/compositor_image_crop.glsl
+ shaders/compositor/compositor_projector_lens_distortion.glsl
+ shaders/compositor/compositor_realize_on_domain.glsl
+ shaders/compositor/compositor_screen_lens_distortion.glsl
+ shaders/compositor/compositor_set_alpha.glsl
+ shaders/compositor/compositor_split_viewer.glsl
+
+ shaders/compositor/library/gpu_shader_compositor_alpha_over.glsl
+ shaders/compositor/library/gpu_shader_compositor_bright_contrast.glsl
+ shaders/compositor/library/gpu_shader_compositor_channel_matte.glsl
+ shaders/compositor/library/gpu_shader_compositor_chroma_matte.glsl
+ shaders/compositor/library/gpu_shader_compositor_color_balance.glsl
+ shaders/compositor/library/gpu_shader_compositor_color_correction.glsl
+ shaders/compositor/library/gpu_shader_compositor_color_matte.glsl
+ shaders/compositor/library/gpu_shader_compositor_color_spill.glsl
+ shaders/compositor/library/gpu_shader_compositor_color_to_luminance.glsl
+ shaders/compositor/library/gpu_shader_compositor_difference_matte.glsl
+ shaders/compositor/library/gpu_shader_compositor_distance_matte.glsl
+ shaders/compositor/library/gpu_shader_compositor_exposure.glsl
+ shaders/compositor/library/gpu_shader_compositor_gamma.glsl
+ shaders/compositor/library/gpu_shader_compositor_hue_correct.glsl
+ shaders/compositor/library/gpu_shader_compositor_hue_saturation_value.glsl
+ shaders/compositor/library/gpu_shader_compositor_invert.glsl
+ shaders/compositor/library/gpu_shader_compositor_luminance_matte.glsl
+ shaders/compositor/library/gpu_shader_compositor_main.glsl
+ shaders/compositor/library/gpu_shader_compositor_map_value.glsl
+ shaders/compositor/library/gpu_shader_compositor_normal.glsl
+ shaders/compositor/library/gpu_shader_compositor_posterize.glsl
+ shaders/compositor/library/gpu_shader_compositor_separate_combine.glsl
+ shaders/compositor/library/gpu_shader_compositor_set_alpha.glsl
+ shaders/compositor/library/gpu_shader_compositor_store_output.glsl
+ shaders/compositor/library/gpu_shader_compositor_texture_utilities.glsl
+ shaders/compositor/library/gpu_shader_compositor_type_conversion.glsl
+
shaders/material/gpu_shader_material_add_shader.glsl
shaders/material/gpu_shader_material_ambient_occlusion.glsl
shaders/material/gpu_shader_material_anisotropic.glsl
@@ -470,8 +509,8 @@ set(SRC_SHADER_CREATE_INFOS
../draw/engines/overlay/shaders/infos/overlay_grid_info.hh
../draw/engines/overlay/shaders/infos/overlay_outline_info.hh
../draw/engines/overlay/shaders/infos/overlay_paint_info.hh
- ../draw/engines/overlay/shaders/infos/overlay_sculpt_info.hh
../draw/engines/overlay/shaders/infos/overlay_sculpt_curves_info.hh
+ ../draw/engines/overlay/shaders/infos/overlay_sculpt_info.hh
../draw/engines/overlay/shaders/infos/overlay_volume_info.hh
../draw/engines/overlay/shaders/infos/overlay_wireframe_info.hh
../draw/engines/select/shaders/infos/select_id_info.hh
@@ -486,6 +525,7 @@ set(SRC_SHADER_CREATE_INFOS
../draw/engines/workbench/shaders/infos/workbench_transparent_resolve_info.hh
../draw/engines/workbench/shaders/infos/workbench_volume_info.hh
../draw/engines/image/shaders/infos/engine_image_info.hh
+ ../draw/intern/shaders/draw_debug_info.hh
../draw/intern/shaders/draw_fullscreen_info.hh
../draw/intern/shaders/draw_hair_refine_info.hh
../draw/intern/shaders/draw_object_infos_info.hh
@@ -526,6 +566,18 @@ set(SRC_SHADER_CREATE_INFOS
shaders/infos/gpu_shader_simple_lighting_info.hh
shaders/infos/gpu_shader_text_info.hh
shaders/infos/gpu_srgb_to_framebuffer_space_info.hh
+
+ shaders/compositor/infos/compositor_alpha_crop_info.hh
+ shaders/compositor/infos/compositor_box_mask_info.hh
+ shaders/compositor/infos/compositor_convert_info.hh
+ shaders/compositor/infos/compositor_ellipse_mask_info.hh
+ shaders/compositor/infos/compositor_flip_info.hh
+ shaders/compositor/infos/compositor_image_crop_info.hh
+ shaders/compositor/infos/compositor_projector_lens_distortion_info.hh
+ shaders/compositor/infos/compositor_realize_on_domain_info.hh
+ shaders/compositor/infos/compositor_screen_lens_distortion_info.hh
+ shaders/compositor/infos/compositor_set_alpha_info.hh
+ shaders/compositor/infos/compositor_split_viewer_info.hh
)
set(SHADER_CREATE_INFOS_CONTENT "")
diff --git a/source/blender/gpu/GPU_batch.h b/source/blender/gpu/GPU_batch.h
index 7ef2d81ac31..c085b592a77 100644
--- a/source/blender/gpu/GPU_batch.h
+++ b/source/blender/gpu/GPU_batch.h
@@ -93,8 +93,10 @@ void GPU_batch_init_ex(GPUBatch *batch,
*/
void GPU_batch_copy(GPUBatch *batch_dst, GPUBatch *batch_src);
-#define GPU_batch_create(prim, verts, elem) GPU_batch_create_ex(prim, verts, elem, 0)
-#define GPU_batch_init(batch, prim, verts, elem) GPU_batch_init_ex(batch, prim, verts, elem, 0)
+#define GPU_batch_create(prim, verts, elem) \
+ GPU_batch_create_ex(prim, verts, elem, (eGPUBatchFlag)0)
+#define GPU_batch_init(batch, prim, verts, elem) \
+ GPU_batch_init_ex(batch, prim, verts, elem, (eGPUBatchFlag)0)
/**
* Same as discard but does not free. (does not call free callback).
diff --git a/source/blender/gpu/GPU_buffers.h b/source/blender/gpu/GPU_buffers.h
index 6dc49ff494d..d1d91cb7508 100644
--- a/source/blender/gpu/GPU_buffers.h
+++ b/source/blender/gpu/GPU_buffers.h
@@ -48,7 +48,6 @@ typedef struct GPU_PBVH_Buffers GPU_PBVH_Buffers;
* Threaded: do not call any functions that use OpenGL calls!
*/
GPU_PBVH_Buffers *GPU_pbvh_mesh_buffers_build(const struct Mesh *mesh,
- const struct MVert *vertices,
const struct MLoopTri *looptri,
const int *sculpt_face_sets,
const int *face_indices,
diff --git a/source/blender/gpu/GPU_shader.h b/source/blender/gpu/GPU_shader.h
index c154f1adc8b..529a3da3ab9 100644
--- a/source/blender/gpu/GPU_shader.h
+++ b/source/blender/gpu/GPU_shader.h
@@ -148,11 +148,19 @@ typedef enum {
GPU_NUM_UNIFORM_BLOCKS, /* Special value, denotes number of builtin uniforms block. */
} GPUUniformBlockBuiltin;
+typedef enum {
+ GPU_STORAGE_BUFFER_DEBUG_VERTS = 0, /* drw_debug_verts_buf */
+ GPU_STORAGE_BUFFER_DEBUG_PRINT, /* drw_debug_print_buf */
+
+ GPU_NUM_STORAGE_BUFFERS, /* Special value, denotes number of builtin buffer blocks. */
+} GPUStorageBufferBuiltin;
+
void GPU_shader_set_srgb_uniform(GPUShader *shader);
int GPU_shader_get_uniform(GPUShader *shader, const char *name);
int GPU_shader_get_builtin_uniform(GPUShader *shader, int builtin);
int GPU_shader_get_builtin_block(GPUShader *shader, int builtin);
+int GPU_shader_get_builtin_ssbo(GPUShader *shader, int builtin);
/** DEPRECATED: Kept only because of Python GPU API. */
int GPU_shader_get_uniform_block(GPUShader *shader, const char *name);
int GPU_shader_get_ssbo(GPUShader *shader, const char *name);
diff --git a/source/blender/gpu/intern/gpu_buffers.c b/source/blender/gpu/intern/gpu_buffers.c
index 2a0f624ac45..d64b8b4118a 100644
--- a/source/blender/gpu/intern/gpu_buffers.c
+++ b/source/blender/gpu/intern/gpu_buffers.c
@@ -211,11 +211,11 @@ static void gpu_pbvh_batch_init(GPU_PBVH_Buffers *buffers, GPUPrimType prim)
* \{ */
static bool gpu_pbvh_is_looptri_visible(const MLoopTri *lt,
- const MVert *mvert,
+ const bool *hide_vert,
const MLoop *mloop,
const int *sculpt_face_sets)
{
- return (!paint_is_face_hidden(lt, mvert, mloop) && sculpt_face_sets &&
+ return (!paint_is_face_hidden(lt, hide_vert, mloop) && sculpt_face_sets &&
sculpt_face_sets[lt->poly] > SCULPT_FACE_SET_NONE);
}
@@ -233,6 +233,9 @@ void GPU_pbvh_mesh_buffers_update(PBVHGPUFormat *vbo_id,
GPUAttrRef vcol_refs[MAX_GPU_ATTR];
GPUAttrRef cd_uvs[MAX_GPU_ATTR];
+ const bool *hide_vert = (bool *)CustomData_get_layer_named(
+ &mesh->vdata, CD_PROP_BOOL, ".hide_vert");
+
const CustomDataLayer *actcol = BKE_id_attributes_active_color_get(&mesh->id);
eAttrDomain actcol_domain = actcol ? BKE_id_attribute_domain(&mesh->id, actcol) :
ATTR_DOMAIN_AUTO;
@@ -310,7 +313,7 @@ void GPU_pbvh_mesh_buffers_update(PBVHGPUFormat *vbo_id,
for (uint i = 0; i < buffers->face_indices_len; i++) {
const MLoopTri *lt = &buffers->looptri[buffers->face_indices[i]];
- if (!gpu_pbvh_is_looptri_visible(lt, mvert, buffers->mloop, sculpt_face_sets)) {
+ if (!gpu_pbvh_is_looptri_visible(lt, hide_vert, buffers->mloop, sculpt_face_sets)) {
continue;
}
@@ -350,7 +353,7 @@ void GPU_pbvh_mesh_buffers_update(PBVHGPUFormat *vbo_id,
buffers->mloop[lt->tri[2]].v,
};
- if (!gpu_pbvh_is_looptri_visible(lt, mvert, buffers->mloop, sculpt_face_sets)) {
+ if (!gpu_pbvh_is_looptri_visible(lt, hide_vert, buffers->mloop, sculpt_face_sets)) {
continue;
}
@@ -390,7 +393,7 @@ void GPU_pbvh_mesh_buffers_update(PBVHGPUFormat *vbo_id,
buffers->mloop[lt->tri[2]].v,
};
- if (!gpu_pbvh_is_looptri_visible(lt, mvert, buffers->mloop, sculpt_face_sets)) {
+ if (!gpu_pbvh_is_looptri_visible(lt, hide_vert, buffers->mloop, sculpt_face_sets)) {
continue;
}
@@ -454,7 +457,6 @@ void GPU_pbvh_mesh_buffers_update(PBVHGPUFormat *vbo_id,
}
GPU_PBVH_Buffers *GPU_pbvh_mesh_buffers_build(const Mesh *mesh,
- const MVert *vertices,
const MLoopTri *looptri,
const int *sculpt_face_sets,
const int *face_indices,
@@ -469,6 +471,9 @@ GPU_PBVH_Buffers *GPU_pbvh_mesh_buffers_build(const Mesh *mesh,
buffers = MEM_callocN(sizeof(GPU_PBVH_Buffers), "GPU_Buffers");
+ const bool *hide_vert = (bool *)CustomData_get_layer_named(
+ &mesh->vdata, CD_PROP_BOOL, ".hide_vert");
+
/* smooth or flat for all */
buffers->smooth = mpoly[looptri[face_indices[0]].poly].flag & ME_SMOOTH;
@@ -477,7 +482,7 @@ GPU_PBVH_Buffers *GPU_pbvh_mesh_buffers_build(const Mesh *mesh,
/* Count the number of visible triangles */
for (i = 0, tottri = 0; i < face_indices_len; i++) {
const MLoopTri *lt = &looptri[face_indices[i]];
- if (gpu_pbvh_is_looptri_visible(lt, vertices, mloop, sculpt_face_sets)) {
+ if (gpu_pbvh_is_looptri_visible(lt, hide_vert, mloop, sculpt_face_sets)) {
int r_edges[3];
BKE_mesh_looptri_get_real_edges(mesh, lt, r_edges);
for (int j = 0; j < 3; j++) {
@@ -510,7 +515,7 @@ GPU_PBVH_Buffers *GPU_pbvh_mesh_buffers_build(const Mesh *mesh,
const MLoopTri *lt = &looptri[face_indices[i]];
/* Skip hidden faces */
- if (!gpu_pbvh_is_looptri_visible(lt, vertices, mloop, sculpt_face_sets)) {
+ if (!gpu_pbvh_is_looptri_visible(lt, hide_vert, mloop, sculpt_face_sets)) {
continue;
}
diff --git a/source/blender/gpu/intern/gpu_shader.cc b/source/blender/gpu/intern/gpu_shader.cc
index 8a630d9e3ca..08c768b28ba 100644
--- a/source/blender/gpu/intern/gpu_shader.cc
+++ b/source/blender/gpu/intern/gpu_shader.cc
@@ -578,6 +578,12 @@ int GPU_shader_get_builtin_block(GPUShader *shader, int builtin)
return interface->ubo_builtin((GPUUniformBlockBuiltin)builtin);
}
+int GPU_shader_get_builtin_ssbo(GPUShader *shader, int builtin)
+{
+ ShaderInterface *interface = unwrap(shader)->interface;
+ return interface->ssbo_builtin((GPUStorageBufferBuiltin)builtin);
+}
+
int GPU_shader_get_ssbo(GPUShader *shader, const char *name)
{
ShaderInterface *interface = unwrap(shader)->interface;
diff --git a/source/blender/gpu/intern/gpu_shader_builder_stubs.cc b/source/blender/gpu/intern/gpu_shader_builder_stubs.cc
index d8af2fc584d..3a14c060484 100644
--- a/source/blender/gpu/intern/gpu_shader_builder_stubs.cc
+++ b/source/blender/gpu/intern/gpu_shader_builder_stubs.cc
@@ -137,7 +137,7 @@ eAttrDomain BKE_id_attribute_domain(const struct ID *UNUSED(id),
/** \name Stubs of BKE_paint.h
* \{ */
bool paint_is_face_hidden(const struct MLoopTri *UNUSED(lt),
- const struct MVert *UNUSED(mvert),
+ const bool *UNUSED(hide_vert),
const struct MLoop *UNUSED(mloop))
{
BLI_assert_unreachable();
diff --git a/source/blender/gpu/intern/gpu_shader_create_info.cc b/source/blender/gpu/intern/gpu_shader_create_info.cc
index bc0731862cb..110b77f1f52 100644
--- a/source/blender/gpu/intern/gpu_shader_create_info.cc
+++ b/source/blender/gpu/intern/gpu_shader_create_info.cc
@@ -306,6 +306,14 @@ void gpu_shader_create_info_init()
info->builtins_ |= gpu_shader_dependency_get_builtins(info->fragment_source_);
info->builtins_ |= gpu_shader_dependency_get_builtins(info->geometry_source_);
info->builtins_ |= gpu_shader_dependency_get_builtins(info->compute_source_);
+
+ /* Automatically amend the create info for ease of use of the debug feature. */
+ if ((info->builtins_ & BuiltinBits::USE_DEBUG_DRAW) == BuiltinBits::USE_DEBUG_DRAW) {
+ info->additional_info("draw_debug_draw");
+ }
+ if ((info->builtins_ & BuiltinBits::USE_DEBUG_PRINT) == BuiltinBits::USE_DEBUG_PRINT) {
+ info->additional_info("draw_debug_print");
+ }
}
}
diff --git a/source/blender/gpu/intern/gpu_shader_create_info.hh b/source/blender/gpu/intern/gpu_shader_create_info.hh
index fb8efbb209a..82defc436e0 100644
--- a/source/blender/gpu/intern/gpu_shader_create_info.hh
+++ b/source/blender/gpu/intern/gpu_shader_create_info.hh
@@ -127,8 +127,12 @@ enum class BuiltinBits {
VERTEX_ID = (1 << 14),
WORK_GROUP_ID = (1 << 15),
WORK_GROUP_SIZE = (1 << 16),
+
+ /* Not a builtin but a flag we use to tag shaders that use the debug features. */
+ USE_DEBUG_DRAW = (1 << 29),
+ USE_DEBUG_PRINT = (1 << 30),
};
-ENUM_OPERATORS(BuiltinBits, BuiltinBits::WORK_GROUP_SIZE);
+ENUM_OPERATORS(BuiltinBits, BuiltinBits::USE_DEBUG_PRINT);
/**
* Follow convention described in:
diff --git a/source/blender/gpu/intern/gpu_shader_dependency.cc b/source/blender/gpu/intern/gpu_shader_dependency.cc
index aff7df9ac33..961d3fcfe5b 100644
--- a/source/blender/gpu/intern/gpu_shader_dependency.cc
+++ b/source/blender/gpu/intern/gpu_shader_dependency.cc
@@ -11,6 +11,7 @@
#include <algorithm>
#include <iomanip>
#include <iostream>
+#include <regex>
#include <sstream>
#include "BLI_ghash.h"
@@ -42,7 +43,7 @@ struct GPUSource {
StringRefNull source;
Vector<GPUSource *> dependencies;
bool dependencies_init = false;
- shader::BuiltinBits builtins = (shader::BuiltinBits)0;
+ shader::BuiltinBits builtins = shader::BuiltinBits::NONE;
std::string processed_source;
GPUSource(const char *path,
@@ -54,46 +55,45 @@ struct GPUSource {
/* Scan for builtins. */
/* FIXME: This can trigger false positive caused by disabled #if blocks. */
/* TODO(fclem): Could be made faster by scanning once. */
- if (source.find("gl_FragCoord", 0)) {
+ if (source.find("gl_FragCoord", 0) != StringRef::not_found) {
builtins |= shader::BuiltinBits::FRAG_COORD;
}
- if (source.find("gl_FrontFacing", 0)) {
+ if (source.find("gl_FrontFacing", 0) != StringRef::not_found) {
builtins |= shader::BuiltinBits::FRONT_FACING;
}
- if (source.find("gl_GlobalInvocationID", 0)) {
+ if (source.find("gl_GlobalInvocationID", 0) != StringRef::not_found) {
builtins |= shader::BuiltinBits::GLOBAL_INVOCATION_ID;
}
- if (source.find("gl_InstanceID", 0)) {
+ if (source.find("gl_InstanceID", 0) != StringRef::not_found) {
builtins |= shader::BuiltinBits::INSTANCE_ID;
}
- if (source.find("gl_LocalInvocationID", 0)) {
+ if (source.find("gl_LocalInvocationID", 0) != StringRef::not_found) {
builtins |= shader::BuiltinBits::LOCAL_INVOCATION_ID;
}
- if (source.find("gl_LocalInvocationIndex", 0)) {
+ if (source.find("gl_LocalInvocationIndex", 0) != StringRef::not_found) {
builtins |= shader::BuiltinBits::LOCAL_INVOCATION_INDEX;
}
- if (source.find("gl_NumWorkGroup", 0)) {
+ if (source.find("gl_NumWorkGroup", 0) != StringRef::not_found) {
builtins |= shader::BuiltinBits::NUM_WORK_GROUP;
}
- if (source.find("gl_PointCoord", 0)) {
+ if (source.find("gl_PointCoord", 0) != StringRef::not_found) {
builtins |= shader::BuiltinBits::POINT_COORD;
}
- if (source.find("gl_PointSize", 0)) {
+ if (source.find("gl_PointSize", 0) != StringRef::not_found) {
builtins |= shader::BuiltinBits::POINT_SIZE;
}
- if (source.find("gl_PrimitiveID", 0)) {
+ if (source.find("gl_PrimitiveID", 0) != StringRef::not_found) {
builtins |= shader::BuiltinBits::PRIMITIVE_ID;
}
- if (source.find("gl_VertexID", 0)) {
+ if (source.find("gl_VertexID", 0) != StringRef::not_found) {
builtins |= shader::BuiltinBits::VERTEX_ID;
}
- if (source.find("gl_WorkGroupID", 0)) {
+ if (source.find("gl_WorkGroupID", 0) != StringRef::not_found) {
builtins |= shader::BuiltinBits::WORK_GROUP_ID;
}
- if (source.find("gl_WorkGroupSize", 0)) {
+ if (source.find("gl_WorkGroupSize", 0) != StringRef::not_found) {
builtins |= shader::BuiltinBits::WORK_GROUP_SIZE;
}
-
/* TODO(fclem): We could do that at compile time. */
/* Limit to shared header files to avoid the temptation to use C++ syntax in .glsl files. */
if (filename.endswith(".h") || filename.endswith(".hh")) {
@@ -101,6 +101,18 @@ struct GPUSource {
quote_preprocess();
}
else {
+ if (source.find("'") != StringRef::not_found) {
+ char_literals_preprocess();
+ }
+ if (source.find("drw_print") != StringRef::not_found) {
+ string_preprocess();
+ }
+ if ((source.find("drw_debug_") != StringRef::not_found) &&
+ /* Avoid theses two files where it makes no sense to add the dependency. */
+ (filename != "common_debug_draw_lib.glsl" &&
+ filename != "draw_debug_draw_display_vert.glsl")) {
+ builtins |= shader::BuiltinBits::USE_DEBUG_DRAW;
+ }
check_no_quotes();
}
@@ -522,6 +534,217 @@ struct GPUSource {
}
}
+ void char_literals_preprocess()
+ {
+ const StringRefNull input = source;
+ std::stringstream output;
+ int64_t cursor = -1;
+ int64_t last_pos = 0;
+
+ while (true) {
+ cursor = find_token(input, '\'', cursor + 1);
+ if (cursor == -1) {
+ break;
+ }
+ /* Output anything between 2 print statement. */
+ output << input.substr(last_pos, cursor - last_pos);
+
+ /* Extract string. */
+ int64_t char_start = cursor + 1;
+ int64_t char_end = find_token(input, '\'', char_start);
+ CHECK(char_end, input, cursor, "Malformed char literal. Missing ending `'`.");
+
+ StringRef input_char = input.substr(char_start, char_end - char_start);
+ if (input_char.size() == 0) {
+ CHECK(-1, input, cursor, "Malformed char literal. Empty character constant");
+ }
+
+ uint8_t char_value = input_char[0];
+
+ if (input_char[0] == '\\') {
+ if (input_char[1] == 'n') {
+ char_value = '\n';
+ }
+ else {
+ CHECK(-1, input, cursor, "Unsupported escaped character");
+ }
+ }
+ else {
+ if (input_char.size() > 1) {
+ CHECK(-1, input, cursor, "Malformed char literal. Multi-character character constant");
+ }
+ }
+
+ char hex[8];
+ SNPRINTF(hex, "0x%.2Xu", char_value);
+ output << hex;
+
+ cursor = last_pos = char_end + 1;
+ }
+ /* If nothing has been changed, do not allocate processed_source. */
+ if (last_pos == 0) {
+ return;
+ }
+
+ if (last_pos != 0) {
+ output << input.substr(last_pos);
+ }
+ processed_source = output.str();
+ source = processed_source.c_str();
+ }
+
+ /* Replace print(string) by equivalent drw_print_char4() sequence. */
+ void string_preprocess()
+ {
+ const StringRefNull input = source;
+ std::stringstream output;
+ int64_t cursor = -1;
+ int64_t last_pos = 0;
+
+ while (true) {
+ cursor = find_keyword(input, "drw_print", cursor + 1);
+ if (cursor == -1) {
+ break;
+ }
+
+ bool do_endl = false;
+ StringRef func = input.substr(cursor);
+ if (func.startswith("drw_print(")) {
+ do_endl = true;
+ }
+ else if (func.startswith("drw_print_no_endl(")) {
+ do_endl = false;
+ }
+ else {
+ continue;
+ }
+
+ /* Output anything between 2 print statement. */
+ output << input.substr(last_pos, cursor - last_pos);
+
+ /* Extract string. */
+ int64_t str_start = input.find('(', cursor) + 1;
+ int64_t semicolon = find_token(input, ';', str_start + 1);
+ CHECK(semicolon, input, cursor, "Malformed print(). Missing `;` .");
+ int64_t str_end = rfind_token(input, ')', semicolon);
+ if (str_end < str_start) {
+ CHECK(-1, input, cursor, "Malformed print(). Missing closing `)` .");
+ }
+
+ std::stringstream sub_output;
+ StringRef input_args = input.substr(str_start, str_end - str_start);
+
+ auto print_string = [&](std::string str) -> int {
+ size_t len_before_pad = str.length();
+ /* Pad string to uint size. */
+ while (str.length() % 4 != 0) {
+ str += " ";
+ }
+ /* Keep everything in one line to not mess with the shader logs. */
+ sub_output << "/* " << str << "*/";
+ sub_output << "drw_print_string_start(" << len_before_pad << ");";
+ for (size_t i = 0; i < len_before_pad; i += 4) {
+ uint8_t chars[4] = {*(reinterpret_cast<const uint8_t *>(str.c_str()) + i + 0),
+ *(reinterpret_cast<const uint8_t *>(str.c_str()) + i + 1),
+ *(reinterpret_cast<const uint8_t *>(str.c_str()) + i + 2),
+ *(reinterpret_cast<const uint8_t *>(str.c_str()) + i + 3)};
+ if (i + 4 > len_before_pad) {
+ chars[len_before_pad - i] = '\0';
+ }
+ char uint_hex[12];
+ SNPRINTF(uint_hex, "0x%.2X%.2X%.2X%.2Xu", chars[3], chars[2], chars[1], chars[0]);
+ sub_output << "drw_print_char4(" << StringRefNull(uint_hex) << ");";
+ }
+ return 0;
+ };
+
+ std::string func_args = input_args;
+ /* Workaround to support function call inside prints. We replace commas by a non control
+ * character `$` in order to use simpler regex later. */
+ bool string_scope = false;
+ int func_scope = 0;
+ for (char &c : func_args) {
+ if (c == '"') {
+ string_scope = !string_scope;
+ }
+ else if (!string_scope) {
+ if (c == '(') {
+ func_scope++;
+ }
+ else if (c == ')') {
+ func_scope--;
+ }
+ else if (c == ',' && func_scope != 0) {
+ c = '$';
+ }
+ }
+ }
+
+ const bool print_as_variable = (input_args[0] != '"') && find_token(input_args, ',') == -1;
+ if (print_as_variable) {
+ /* Variable or expression debugging. */
+ std::string arg = input_args;
+ /* Pad align most values. */
+ while (arg.length() % 4 != 0) {
+ arg += " ";
+ }
+ print_string(arg);
+ print_string("= ");
+ sub_output << "drw_print_value(" << input_args << ");";
+ }
+ else {
+ const std::regex arg_regex(
+ /* String args. */
+ "[\\s]*\"([^\r\n\t\f\v\"]*)\""
+ /* OR. */
+ "|"
+ /* value args. */
+ "([^,]+)");
+ std::smatch args_match;
+ std::string::const_iterator args_search_start(func_args.cbegin());
+ while (std::regex_search(args_search_start, func_args.cend(), args_match, arg_regex)) {
+ args_search_start = args_match.suffix().first;
+ std::string arg_string = args_match[1].str();
+ std::string arg_val = args_match[2].str();
+
+ if (arg_string.empty()) {
+ for (char &c : arg_val) {
+ if (c == '$') {
+ c = ',';
+ }
+ }
+ sub_output << "drw_print_value(" << arg_val << ");";
+ }
+ else {
+ print_string(arg_string);
+ }
+ }
+ }
+
+ if (do_endl) {
+ sub_output << "drw_print_newline();";
+ }
+
+ output << sub_output.str();
+
+ cursor = last_pos = str_end + 1;
+ }
+ /* If nothing has been changed, do not allocate processed_source. */
+ if (last_pos == 0) {
+ return;
+ }
+
+ if (filename != "common_debug_print_lib.glsl") {
+ builtins |= shader::BuiltinBits::USE_DEBUG_PRINT;
+ }
+
+ if (last_pos != 0) {
+ output << input.substr(last_pos);
+ }
+ processed_source = output.str();
+ source = processed_source.c_str();
+ }
+
#undef find_keyword
#undef rfind_keyword
#undef find_token
@@ -537,6 +760,15 @@ struct GPUSource {
this->dependencies_init = true;
int64_t pos = -1;
+ using namespace shader;
+ /* Auto dependency injection for debug capabilities. */
+ if ((builtins & BuiltinBits::USE_DEBUG_DRAW) == BuiltinBits::USE_DEBUG_DRAW) {
+ dependencies.append_non_duplicates(dict.lookup("common_debug_draw_lib.glsl"));
+ }
+ if ((builtins & BuiltinBits::USE_DEBUG_PRINT) == BuiltinBits::USE_DEBUG_PRINT) {
+ dependencies.append_non_duplicates(dict.lookup("common_debug_print_lib.glsl"));
+ }
+
while (true) {
GPUSource *dependency_source = nullptr;
@@ -558,6 +790,7 @@ struct GPUSource {
return 1;
}
}
+
/* Recursive. */
int result = dependency_source->init_dependencies(dict, g_functions);
if (result != 0) {
@@ -583,7 +816,7 @@ struct GPUSource {
shader::BuiltinBits builtins_get() const
{
- shader::BuiltinBits out_builtins = shader::BuiltinBits::NONE;
+ shader::BuiltinBits out_builtins = builtins;
for (auto *dep : dependencies) {
out_builtins |= dep->builtins;
}
diff --git a/source/blender/gpu/intern/gpu_shader_interface.hh b/source/blender/gpu/intern/gpu_shader_interface.hh
index 60344757b43..812244c9b3a 100644
--- a/source/blender/gpu/intern/gpu_shader_interface.hh
+++ b/source/blender/gpu/intern/gpu_shader_interface.hh
@@ -56,6 +56,7 @@ class ShaderInterface {
/** Location of builtin uniforms. Fast access, no lookup needed. */
int32_t builtins_[GPU_NUM_UNIFORMS];
int32_t builtin_blocks_[GPU_NUM_UNIFORM_BLOCKS];
+ int32_t builtin_buffers_[GPU_NUM_STORAGE_BUFFERS];
public:
ShaderInterface();
@@ -116,9 +117,17 @@ class ShaderInterface {
return builtin_blocks_[builtin];
}
+ /* Returns binding position. */
+ inline int32_t ssbo_builtin(const GPUStorageBufferBuiltin builtin) const
+ {
+ BLI_assert(builtin >= 0 && builtin < GPU_NUM_STORAGE_BUFFERS);
+ return builtin_buffers_[builtin];
+ }
+
protected:
static inline const char *builtin_uniform_name(GPUUniformBuiltin u);
static inline const char *builtin_uniform_block_name(GPUUniformBlockBuiltin u);
+ static inline const char *builtin_storage_block_name(GPUStorageBufferBuiltin u);
inline uint32_t set_input_name(ShaderInput *input, char *name, uint32_t name_len) const;
inline void copy_input_name(ShaderInput *input,
@@ -212,6 +221,18 @@ inline const char *ShaderInterface::builtin_uniform_block_name(GPUUniformBlockBu
}
}
+inline const char *ShaderInterface::builtin_storage_block_name(GPUStorageBufferBuiltin u)
+{
+ switch (u) {
+ case GPU_STORAGE_BUFFER_DEBUG_VERTS:
+ return "drw_debug_verts_buf";
+ case GPU_STORAGE_BUFFER_DEBUG_PRINT:
+ return "drw_debug_print_buf";
+ default:
+ return nullptr;
+ }
+}
+
/* Returns string length including '\0' terminator. */
inline uint32_t ShaderInterface::set_input_name(ShaderInput *input,
char *name,
diff --git a/source/blender/gpu/metal/mtl_query.mm b/source/blender/gpu/metal/mtl_query.mm
index dfda0a8de7f..f574140531d 100644
--- a/source/blender/gpu/metal/mtl_query.mm
+++ b/source/blender/gpu/metal/mtl_query.mm
@@ -9,7 +9,7 @@
namespace blender::gpu {
static const size_t VISIBILITY_COUNT_PER_BUFFER = 512;
-/* defined in the documentation but not queryable programmatically:
+/* Defined in the documentation but can't be queried programmatically:
* https://developer.apple.com/documentation/metal/mtlvisibilityresultmode/mtlvisibilityresultmodeboolean?language=objc
*/
static const size_t VISIBILITY_RESULT_SIZE_IN_BYTES = 8;
diff --git a/source/blender/gpu/opengl/gl_shader_interface.cc b/source/blender/gpu/opengl/gl_shader_interface.cc
index 1b3ab2941a8..4623a14dab3 100644
--- a/source/blender/gpu/opengl/gl_shader_interface.cc
+++ b/source/blender/gpu/opengl/gl_shader_interface.cc
@@ -318,6 +318,13 @@ GLShaderInterface::GLShaderInterface(GLuint program)
builtin_blocks_[u] = (block != nullptr) ? block->binding : -1;
}
+ /* Builtin Storage Buffers */
+ for (int32_t u_int = 0; u_int < GPU_NUM_STORAGE_BUFFERS; u_int++) {
+ GPUStorageBufferBuiltin u = static_cast<GPUStorageBufferBuiltin>(u_int);
+ const ShaderInput *block = this->ssbo_get(builtin_storage_block_name(u));
+ builtin_buffers_[u] = (block != nullptr) ? block->binding : -1;
+ }
+
MEM_freeN(uniforms_from_blocks);
/* Resize name buffer to save some memory. */
@@ -481,6 +488,13 @@ GLShaderInterface::GLShaderInterface(GLuint program, const shader::ShaderCreateI
builtin_blocks_[u] = (block != nullptr) ? block->binding : -1;
}
+ /* Builtin Storage Buffers */
+ for (int32_t u_int = 0; u_int < GPU_NUM_STORAGE_BUFFERS; u_int++) {
+ GPUStorageBufferBuiltin u = static_cast<GPUStorageBufferBuiltin>(u_int);
+ const ShaderInput *block = this->ssbo_get(builtin_storage_block_name(u));
+ builtin_buffers_[u] = (block != nullptr) ? block->binding : -1;
+ }
+
this->sort_inputs();
// this->debug_print();
diff --git a/source/blender/gpu/opengl/gl_storage_buffer.cc b/source/blender/gpu/opengl/gl_storage_buffer.cc
index 4592adc3a61..83a56edcf04 100644
--- a/source/blender/gpu/opengl/gl_storage_buffer.cc
+++ b/source/blender/gpu/opengl/gl_storage_buffer.cc
@@ -72,7 +72,7 @@ void GLStorageBuf::bind(int slot)
if (slot >= GLContext::max_ssbo_binds) {
fprintf(
stderr,
- "Error: Trying to bind \"%s\" ssbo to slot %d which is above the reported limit of %d.",
+ "Error: Trying to bind \"%s\" ssbo to slot %d which is above the reported limit of %d.\n",
name_,
slot,
GLContext::max_ssbo_binds);
diff --git a/source/blender/gpu/opengl/gl_uniform_buffer.cc b/source/blender/gpu/opengl/gl_uniform_buffer.cc
index e58cea9de43..022fbcfdf29 100644
--- a/source/blender/gpu/opengl/gl_uniform_buffer.cc
+++ b/source/blender/gpu/opengl/gl_uniform_buffer.cc
@@ -65,11 +65,12 @@ void GLUniformBuf::update(const void *data)
void GLUniformBuf::bind(int slot)
{
if (slot >= GLContext::max_ubo_binds) {
- fprintf(stderr,
- "Error: Trying to bind \"%s\" ubo to slot %d which is above the reported limit of %d.",
- name_,
- slot,
- GLContext::max_ubo_binds);
+ fprintf(
+ stderr,
+ "Error: Trying to bind \"%s\" ubo to slot %d which is above the reported limit of %d.\n",
+ name_,
+ slot,
+ GLContext::max_ubo_binds);
return;
}
diff --git a/source/blender/gpu/shaders/common/gpu_shader_common_color_utils.glsl b/source/blender/gpu/shaders/common/gpu_shader_common_color_utils.glsl
index fe89985ae7f..33108d3a989 100644
--- a/source/blender/gpu/shaders/common/gpu_shader_common_color_utils.glsl
+++ b/source/blender/gpu/shaders/common/gpu_shader_common_color_utils.glsl
@@ -140,6 +140,84 @@ void hsl_to_rgb(vec4 hsl, out vec4 outcol)
outcol = vec4((nr - 0.5) * chroma + l, (ng - 0.5) * chroma + l, (nb - 0.5) * chroma + l, hsl.w);
}
+/* ** YCCA to RGBA ** */
+
+void ycca_to_rgba_itu_601(vec4 ycca, out vec4 color)
+{
+ ycca.xyz *= 255.0;
+ ycca.xyz -= vec3(16.0, 128.0, 128.0);
+ color.rgb = mat3(vec3(1.164), 0.0, -0.392, 2.017, 1.596, -0.813, 0.0) * ycca.xyz;
+ color.rgb /= 255.0;
+ color.a = ycca.a;
+}
+
+void ycca_to_rgba_itu_709(vec4 ycca, out vec4 color)
+{
+ ycca.xyz *= 255.0;
+ ycca.xyz -= vec3(16.0, 128.0, 128.0);
+ color.rgb = mat3(vec3(1.164), 0.0, -0.213, 2.115, 1.793, -0.534, 0.0) * ycca.xyz;
+ color.rgb /= 255.0;
+ color.a = ycca.a;
+}
+
+void ycca_to_rgba_jpeg(vec4 ycca, out vec4 color)
+{
+ ycca.xyz *= 255.0;
+ color.rgb = mat3(vec3(1.0), 0.0, -0.34414, 1.772, 1.402, -0.71414, 0.0) * ycca.xyz;
+ color.rgb += vec3(-179.456, 135.45984, -226.816);
+ color.rgb /= 255.0;
+ color.a = ycca.a;
+}
+
+/* ** RGBA to YCCA ** */
+
+void rgba_to_ycca_itu_601(vec4 rgba, out vec4 ycca)
+{
+ rgba.rgb *= 255.0;
+ ycca.xyz = mat3(0.257, -0.148, 0.439, 0.504, -0.291, -0.368, 0.098, 0.439, -0.071) * rgba.rgb;
+ ycca.xyz += vec3(16.0, 128.0, 128.0);
+ ycca.xyz /= 255.0;
+ ycca.a = rgba.a;
+}
+
+void rgba_to_ycca_itu_709(vec4 rgba, out vec4 ycca)
+{
+ rgba.rgb *= 255.0;
+ ycca.xyz = mat3(0.183, -0.101, 0.439, 0.614, -0.338, -0.399, 0.062, 0.439, -0.040) * rgba.rgb;
+ ycca.xyz += vec3(16.0, 128.0, 128.0);
+ ycca.xyz /= 255.0;
+ ycca.a = rgba.a;
+}
+
+void rgba_to_ycca_jpeg(vec4 rgba, out vec4 ycca)
+{
+ rgba.rgb *= 255.0;
+ ycca.xyz = mat3(0.299, -0.16874, 0.5, 0.587, -0.33126, -0.41869, 0.114, 0.5, -0.08131) *
+ rgba.rgb;
+ ycca.xyz += vec3(0.0, 128.0, 128.0);
+ ycca.xyz /= 255.0;
+ ycca.a = rgba.a;
+}
+
+/* ** YUVA to RGBA ** */
+
+void yuva_to_rgba_itu_709(vec4 yuva, out vec4 color)
+{
+ color.rgb = mat3(vec3(1.0), 0.0, -0.21482, 2.12798, 1.28033, -0.38059, 0.0) * yuva.xyz;
+ color.a = yuva.a;
+}
+
+/* ** RGBA to YUVA ** */
+
+void rgba_to_yuva_itu_709(vec4 rgba, out vec4 yuva)
+{
+ yuva.xyz = mat3(0.2126, -0.09991, 0.615, 0.7152, -0.33609, -0.55861, 0.0722, 0.436, -0.05639) *
+ rgba.rgb;
+ yuva.a = rgba.a;
+}
+
+/* ** Alpha Handling ** */
+
void color_alpha_clear(vec4 color, out vec4 result)
{
result = vec4(color.rgb, 1.0);
@@ -147,15 +225,50 @@ void color_alpha_clear(vec4 color, out vec4 result)
void color_alpha_premultiply(vec4 color, out vec4 result)
{
- result = vec4(color.rgb * color.a, 1.0);
+ result = vec4(color.rgb * color.a, color.a);
}
void color_alpha_unpremultiply(vec4 color, out vec4 result)
{
if (color.a == 0.0 || color.a == 1.0) {
- result = vec4(color.rgb, 1.0);
+ result = color;
}
else {
- result = vec4(color.rgb / color.a, 1.0);
+ result = vec4(color.rgb / color.a, color.a);
+ }
+}
+
+float linear_rgb_to_srgb(float color)
+{
+ if (color < 0.0031308) {
+ return (color < 0.0) ? 0.0 : color * 12.92;
+ }
+
+ return 1.055 * pow(color, 1.0 / 2.4) - 0.055;
+}
+
+vec3 linear_rgb_to_srgb(vec3 color)
+{
+ return vec3(
+ linear_rgb_to_srgb(color.r), linear_rgb_to_srgb(color.g), linear_rgb_to_srgb(color.b));
+}
+
+float srgb_to_linear_rgb(float color)
+{
+ if (color < 0.04045) {
+ return (color < 0.0) ? 0.0 : color * (1.0 / 12.92);
}
+
+ return pow((color + 0.055) * (1.0 / 1.055), 2.4);
+}
+
+vec3 srgb_to_linear_rgb(vec3 color)
+{
+ return vec3(
+ srgb_to_linear_rgb(color.r), srgb_to_linear_rgb(color.g), srgb_to_linear_rgb(color.b));
+}
+
+float get_luminance(vec3 color, vec3 luminance_coefficients)
+{
+ return dot(color, luminance_coefficients);
}
diff --git a/source/blender/gpu/shaders/common/gpu_shader_common_curves.glsl b/source/blender/gpu/shaders/common/gpu_shader_common_curves.glsl
index 8948ed77557..db8e114ec7a 100644
--- a/source/blender/gpu/shaders/common/gpu_shader_common_curves.glsl
+++ b/source/blender/gpu/shaders/common/gpu_shader_common_curves.glsl
@@ -95,6 +95,81 @@ void curves_combined_only(float factor,
result = mix(color, result, factor);
}
+/* Contrary to standard tone curve implementations, the film-like implementation tries to preserve
+ * the hue of the colors as much as possible. To understand why this might be a problem, consider
+ * the violet color (0.5, 0.0, 1.0). If this color was to be evaluated at a power curve x^4, the
+ * color will be blue (0.0625, 0.0, 1.0). So the color changes and not just its luminosity, which
+ * is what film-like tone curves tries to avoid.
+ *
+ * First, the channels with the lowest and highest values are identified and evaluated at the
+ * curve. Then, the third channel---the median---is computed while maintaining the original hue of
+ * the color. To do that, we look at the equation for deriving the hue from RGB values. Assuming
+ * the maximum, minimum, and median channels are known, and ignoring the 1/3 period offset of the
+ * hue, the equation is:
+ *
+ * hue = (median - min) / (max - min) [1]
+ *
+ * Since we have the new values for the minimum and maximum after evaluating at the curve, we also
+ * have:
+ *
+ * hue = (new_median - new_min) / (new_max - new_min) [2]
+ *
+ * Since we want the hue to be equivalent, by equating [1] and [2] and rearranging:
+ *
+ * (new_median - new_min) / (new_max - new_min) = (median - min) / (max - min)
+ * new_median - new_min = (new_max - new_min) * (median - min) / (max - min)
+ * new_median = new_min + (new_max - new_min) * (median - min) / (max - min)
+ * new_median = new_min + (median - min) * ((new_max - new_min) / (max - min)) [QED]
+ *
+ * Which gives us the median color that preserves the hue. More intuitively, the median is computed
+ * such that the change in the distance from the median to the minimum is proportional to the
+ * change in the distance from the minimum to the maximum. Finally, each of the new minimum,
+ * maximum, and median values are written to the color channel that they were originally extracted
+ * from. */
+void curves_film_like(float factor,
+ vec4 color,
+ vec4 black_level,
+ vec4 white_level,
+ sampler1DArray curve_map,
+ const float layer,
+ float range_minimum,
+ float range_divider,
+ float start_slope,
+ float end_slope,
+ out vec4 result)
+{
+ vec4 balanced = white_balance(color, black_level, white_level);
+
+ /* Find the maximum, minimum, and median of the color channels. */
+ float minimum = min(balanced.r, min(balanced.g, balanced.b));
+ float maximum = max(balanced.r, max(balanced.g, balanced.b));
+ float median = max(min(balanced.r, balanced.g), min(balanced.b, max(balanced.r, balanced.g)));
+
+ /* Evaluate alpha curve map at the maximum and minimum channels. The alpha curve is the Combined
+ * curve in the UI. */
+ float min_parameter = NORMALIZE_PARAMETER(minimum, range_minimum, range_divider);
+ float max_parameter = NORMALIZE_PARAMETER(maximum, range_minimum, range_divider);
+ float new_min = texture(curve_map, vec2(min_parameter, layer)).a;
+ float new_max = texture(curve_map, vec2(max_parameter, layer)).a;
+
+ /* Then, extrapolate if needed. */
+ new_min = extrapolate_if_needed(min_parameter, new_min, start_slope, end_slope);
+ new_max = extrapolate_if_needed(max_parameter, new_max, start_slope, end_slope);
+
+ /* Compute the new median using the ratio between the new and the original range. */
+ float scaling_ratio = (new_max - new_min) / (maximum - minimum);
+ float new_median = new_min + (median - minimum) * scaling_ratio;
+
+ /* Write each value to its original channel. */
+ bvec3 channel_is_min = equal(balanced.rgb, vec3(minimum));
+ vec3 median_or_min = mix(vec3(new_median), vec3(new_min), channel_is_min);
+ bvec3 channel_is_max = equal(balanced.rgb, vec3(maximum));
+ result.rgb = mix(median_or_min, vec3(new_max), channel_is_max);
+ result.a = color.a;
+
+ result = mix(color, result, clamp(factor, 0.0, 1.0));
+}
+
void curves_vector(vec3 vector,
sampler1DArray curve_map,
const float layer,
diff --git a/source/blender/gpu/shaders/common/gpu_shader_common_math_utils.glsl b/source/blender/gpu/shaders/common/gpu_shader_common_math_utils.glsl
index 124654963fd..1ba22b4c5da 100644
--- a/source/blender/gpu/shaders/common/gpu_shader_common_math_utils.glsl
+++ b/source/blender/gpu/shaders/common/gpu_shader_common_math_utils.glsl
@@ -34,6 +34,17 @@ float compatible_pow(float x, float y)
return pow(x, y);
}
+/* A version of pow that returns a fallback value if the computation is undefined. From the spec:
+ * The result is undefined if x < 0 or if x = 0 and y is less than or equal 0. */
+float fallback_pow(float x, float y, float fallback)
+{
+ if (x < 0.0 || (x == 0.0 && y <= 0.0)) {
+ return fallback;
+ }
+
+ return pow(x, y);
+}
+
float wrap(float a, float b, float c)
{
float range = b - c;
@@ -114,8 +125,24 @@ void vector_copy(vec3 normal, out vec3 outnormal)
outnormal = normal;
}
+vec3 fallback_pow(vec3 a, float b, vec3 fallback)
+{
+ return vec3(fallback_pow(a.x, b, fallback.x),
+ fallback_pow(a.y, b, fallback.y),
+ fallback_pow(a.z, b, fallback.z));
+}
+
/* Matirx Math */
+/* Return a 2D rotation matrix with the angle that the input 2D vector makes with the x axis. */
+mat2 vector_to_rotation_matrix(vec2 vector)
+{
+ vec2 normalized_vector = normalize(vector);
+ float cos_angle = normalized_vector.x;
+ float sin_angle = normalized_vector.y;
+ return mat2(cos_angle, sin_angle, -sin_angle, cos_angle);
+}
+
mat3 euler_to_mat3(vec3 euler)
{
float cx = cos(euler.x);
diff --git a/source/blender/gpu/shaders/common/gpu_shader_common_mix_rgb.glsl b/source/blender/gpu/shaders/common/gpu_shader_common_mix_rgb.glsl
index f9652f1150b..39f3c722dd2 100644
--- a/source/blender/gpu/shaders/common/gpu_shader_common_mix_rgb.glsl
+++ b/source/blender/gpu/shaders/common/gpu_shader_common_mix_rgb.glsl
@@ -2,28 +2,24 @@
void mix_blend(float fac, vec4 col1, vec4 col2, out vec4 outcol)
{
- fac = clamp(fac, 0.0, 1.0);
outcol = mix(col1, col2, fac);
outcol.a = col1.a;
}
void mix_add(float fac, vec4 col1, vec4 col2, out vec4 outcol)
{
- fac = clamp(fac, 0.0, 1.0);
outcol = mix(col1, col1 + col2, fac);
outcol.a = col1.a;
}
void mix_mult(float fac, vec4 col1, vec4 col2, out vec4 outcol)
{
- fac = clamp(fac, 0.0, 1.0);
outcol = mix(col1, col1 * col2, fac);
outcol.a = col1.a;
}
void mix_screen(float fac, vec4 col1, vec4 col2, out vec4 outcol)
{
- fac = clamp(fac, 0.0, 1.0);
float facm = 1.0 - fac;
outcol = vec4(1.0) - (vec4(facm) + fac * (vec4(1.0) - col2)) * (vec4(1.0) - col1);
@@ -32,7 +28,6 @@ void mix_screen(float fac, vec4 col1, vec4 col2, out vec4 outcol)
void mix_overlay(float fac, vec4 col1, vec4 col2, out vec4 outcol)
{
- fac = clamp(fac, 0.0, 1.0);
float facm = 1.0 - fac;
outcol = col1;
@@ -61,14 +56,30 @@ void mix_overlay(float fac, vec4 col1, vec4 col2, out vec4 outcol)
void mix_sub(float fac, vec4 col1, vec4 col2, out vec4 outcol)
{
- fac = clamp(fac, 0.0, 1.0);
outcol = mix(col1, col1 - col2, fac);
outcol.a = col1.a;
}
void mix_div(float fac, vec4 col1, vec4 col2, out vec4 outcol)
{
- fac = clamp(fac, 0.0, 1.0);
+ float facm = 1.0 - fac;
+
+ outcol = vec4(vec3(0.0), col1.a);
+
+ if (col2.r != 0.0) {
+ outcol.r = facm * col1.r + fac * col1.r / col2.r;
+ }
+ if (col2.g != 0.0) {
+ outcol.g = facm * col1.g + fac * col1.g / col2.g;
+ }
+ if (col2.b != 0.0) {
+ outcol.b = facm * col1.b + fac * col1.b / col2.b;
+ }
+}
+
+/* A variant of mix_div that fallback to the first color upon zero division. */
+void mix_div_fallback(float fac, vec4 col1, vec4 col2, out vec4 outcol)
+{
float facm = 1.0 - fac;
outcol = col1;
@@ -86,28 +97,24 @@ void mix_div(float fac, vec4 col1, vec4 col2, out vec4 outcol)
void mix_diff(float fac, vec4 col1, vec4 col2, out vec4 outcol)
{
- fac = clamp(fac, 0.0, 1.0);
outcol = mix(col1, abs(col1 - col2), fac);
outcol.a = col1.a;
}
void mix_dark(float fac, vec4 col1, vec4 col2, out vec4 outcol)
{
- fac = clamp(fac, 0.0, 1.0);
outcol.rgb = mix(col1.rgb, min(col1.rgb, col2.rgb), fac);
outcol.a = col1.a;
}
void mix_light(float fac, vec4 col1, vec4 col2, out vec4 outcol)
{
- fac = clamp(fac, 0.0, 1.0);
outcol.rgb = mix(col1.rgb, max(col1.rgb, col2.rgb), fac);
outcol.a = col1.a;
}
void mix_dodge(float fac, vec4 col1, vec4 col2, out vec4 outcol)
{
- fac = clamp(fac, 0.0, 1.0);
outcol = col1;
if (outcol.r != 0.0) {
@@ -150,7 +157,6 @@ void mix_dodge(float fac, vec4 col1, vec4 col2, out vec4 outcol)
void mix_burn(float fac, vec4 col1, vec4 col2, out vec4 outcol)
{
- fac = clamp(fac, 0.0, 1.0);
float tmp, facm = 1.0 - fac;
outcol = col1;
@@ -200,7 +206,6 @@ void mix_burn(float fac, vec4 col1, vec4 col2, out vec4 outcol)
void mix_hue(float fac, vec4 col1, vec4 col2, out vec4 outcol)
{
- fac = clamp(fac, 0.0, 1.0);
float facm = 1.0 - fac;
outcol = col1;
@@ -220,7 +225,6 @@ void mix_hue(float fac, vec4 col1, vec4 col2, out vec4 outcol)
void mix_sat(float fac, vec4 col1, vec4 col2, out vec4 outcol)
{
- fac = clamp(fac, 0.0, 1.0);
float facm = 1.0 - fac;
outcol = col1;
@@ -238,7 +242,6 @@ void mix_sat(float fac, vec4 col1, vec4 col2, out vec4 outcol)
void mix_val(float fac, vec4 col1, vec4 col2, out vec4 outcol)
{
- fac = clamp(fac, 0.0, 1.0);
float facm = 1.0 - fac;
vec4 hsv, hsv2;
@@ -251,7 +254,6 @@ void mix_val(float fac, vec4 col1, vec4 col2, out vec4 outcol)
void mix_color(float fac, vec4 col1, vec4 col2, out vec4 outcol)
{
- fac = clamp(fac, 0.0, 1.0);
float facm = 1.0 - fac;
outcol = col1;
@@ -272,22 +274,26 @@ void mix_color(float fac, vec4 col1, vec4 col2, out vec4 outcol)
void mix_soft(float fac, vec4 col1, vec4 col2, out vec4 outcol)
{
- fac = clamp(fac, 0.0, 1.0);
float facm = 1.0 - fac;
vec4 one = vec4(1.0);
vec4 scr = one - (one - col2) * (one - col1);
outcol = facm * col1 + fac * ((one - col1) * col2 * col1 + col1 * scr);
+ outcol.a = col1.a;
}
void mix_linear(float fac, vec4 col1, vec4 col2, out vec4 outcol)
{
- fac = clamp(fac, 0.0, 1.0);
-
outcol = col1 + fac * (2.0 * (col2 - vec4(0.5)));
+ outcol.a = col1.a;
}
-void clamp_color(vec3 vec, vec3 min, vec3 max, out vec3 out_vec)
+void clamp_color(vec4 vec, const vec4 min, const vec4 max, out vec4 out_vec)
{
out_vec = clamp(vec, min, max);
}
+
+void multiply_by_alpha(float factor, vec4 color, out float result)
+{
+ result = factor * color.a;
+}
diff --git a/source/blender/gpu/shaders/compositor/compositor_alpha_crop.glsl b/source/blender/gpu/shaders/compositor/compositor_alpha_crop.glsl
new file mode 100644
index 00000000000..d55c8efd4c6
--- /dev/null
+++ b/source/blender/gpu/shaders/compositor/compositor_alpha_crop.glsl
@@ -0,0 +1,11 @@
+#pragma BLENDER_REQUIRE(gpu_shader_compositor_texture_utilities.glsl)
+
+void main()
+{
+ ivec2 texel = ivec2(gl_GlobalInvocationID.xy);
+ /* The lower bound is inclusive and upper bound is exclusive. */
+ bool is_inside = all(greaterThanEqual(texel, lower_bound)) && all(lessThan(texel, upper_bound));
+ /* Write the pixel color if it is inside the cropping region, otherwise, write zero. */
+ vec4 color = is_inside ? texture_load(input_tx, texel) : vec4(0.0);
+ imageStore(output_img, texel, color);
+}
diff --git a/source/blender/gpu/shaders/compositor/compositor_box_mask.glsl b/source/blender/gpu/shaders/compositor/compositor_box_mask.glsl
new file mode 100644
index 00000000000..fad23f28fde
--- /dev/null
+++ b/source/blender/gpu/shaders/compositor/compositor_box_mask.glsl
@@ -0,0 +1,27 @@
+#pragma BLENDER_REQUIRE(gpu_shader_compositor_texture_utilities.glsl)
+
+void main()
+{
+ ivec2 texel = ivec2(gl_GlobalInvocationID.xy);
+
+ vec2 uv = vec2(texel) / vec2(domain_size - ivec2(1));
+ uv -= location;
+ uv.y *= float(domain_size.y) / float(domain_size.x);
+ uv = mat2(cos_angle, -sin_angle, sin_angle, cos_angle) * uv;
+ bool is_inside = all(lessThan(abs(uv), size));
+
+ float base_mask_value = texture_load(base_mask_tx, texel).x;
+ float value = texture_load(mask_value_tx, texel).x;
+
+#if defined(CMP_NODE_MASKTYPE_ADD)
+ float output_mask_value = is_inside ? max(base_mask_value, value) : base_mask_value;
+#elif defined(CMP_NODE_MASKTYPE_SUBTRACT)
+ float output_mask_value = is_inside ? clamp(base_mask_value - value, 0.0, 1.0) : base_mask_value;
+#elif defined(CMP_NODE_MASKTYPE_MULTIPLY)
+ float output_mask_value = is_inside ? base_mask_value * value : 0.0;
+#elif defined(CMP_NODE_MASKTYPE_NOT)
+ float output_mask_value = is_inside ? (base_mask_value > 0.0 ? 0.0 : value) : base_mask_value;
+#endif
+
+ imageStore(output_mask_img, texel, vec4(output_mask_value));
+}
diff --git a/source/blender/gpu/shaders/compositor/compositor_convert.glsl b/source/blender/gpu/shaders/compositor/compositor_convert.glsl
new file mode 100644
index 00000000000..044fb057ca5
--- /dev/null
+++ b/source/blender/gpu/shaders/compositor/compositor_convert.glsl
@@ -0,0 +1,8 @@
+#pragma BLENDER_REQUIRE(gpu_shader_compositor_texture_utilities.glsl)
+
+void main()
+{
+ ivec2 texel = ivec2(gl_GlobalInvocationID.xy);
+ vec4 value = texture_load(input_tx, texel);
+ imageStore(output_img, texel, CONVERT_EXPRESSION(value));
+}
diff --git a/source/blender/gpu/shaders/compositor/compositor_ellipse_mask.glsl b/source/blender/gpu/shaders/compositor/compositor_ellipse_mask.glsl
new file mode 100644
index 00000000000..28f725067e0
--- /dev/null
+++ b/source/blender/gpu/shaders/compositor/compositor_ellipse_mask.glsl
@@ -0,0 +1,27 @@
+#pragma BLENDER_REQUIRE(gpu_shader_compositor_texture_utilities.glsl)
+
+void main()
+{
+ ivec2 texel = ivec2(gl_GlobalInvocationID.xy);
+
+ vec2 uv = vec2(texel) / vec2(domain_size - ivec2(1));
+ uv -= location;
+ uv.y *= float(domain_size.y) / float(domain_size.x);
+ uv = mat2(cos_angle, -sin_angle, sin_angle, cos_angle) * uv;
+ bool is_inside = length(uv / radius) < 1.0;
+
+ float base_mask_value = texture_load(base_mask_tx, texel).x;
+ float value = texture_load(mask_value_tx, texel).x;
+
+#if defined(CMP_NODE_MASKTYPE_ADD)
+ float output_mask_value = is_inside ? max(base_mask_value, value) : base_mask_value;
+#elif defined(CMP_NODE_MASKTYPE_SUBTRACT)
+ float output_mask_value = is_inside ? clamp(base_mask_value - value, 0.0, 1.0) : base_mask_value;
+#elif defined(CMP_NODE_MASKTYPE_MULTIPLY)
+ float output_mask_value = is_inside ? base_mask_value * value : 0.0;
+#elif defined(CMP_NODE_MASKTYPE_NOT)
+ float output_mask_value = is_inside ? (base_mask_value > 0.0 ? 0.0 : value) : base_mask_value;
+#endif
+
+ imageStore(output_mask_img, texel, vec4(output_mask_value));
+}
diff --git a/source/blender/gpu/shaders/compositor/compositor_flip.glsl b/source/blender/gpu/shaders/compositor/compositor_flip.glsl
new file mode 100644
index 00000000000..919c454ee63
--- /dev/null
+++ b/source/blender/gpu/shaders/compositor/compositor_flip.glsl
@@ -0,0 +1,15 @@
+#pragma BLENDER_REQUIRE(gpu_shader_compositor_texture_utilities.glsl)
+
+void main()
+{
+ ivec2 texel = ivec2(gl_GlobalInvocationID.xy);
+ ivec2 size = texture_size(input_tx);
+ ivec2 flipped_texel = texel;
+ if (flip_x) {
+ flipped_texel.x = size.x - texel.x - 1;
+ }
+ if (flip_y) {
+ flipped_texel.y = size.y - texel.y - 1;
+ }
+ imageStore(output_img, texel, texture_load(input_tx, flipped_texel));
+}
diff --git a/source/blender/gpu/shaders/compositor/compositor_image_crop.glsl b/source/blender/gpu/shaders/compositor/compositor_image_crop.glsl
new file mode 100644
index 00000000000..f20e033dee4
--- /dev/null
+++ b/source/blender/gpu/shaders/compositor/compositor_image_crop.glsl
@@ -0,0 +1,7 @@
+#pragma BLENDER_REQUIRE(gpu_shader_compositor_texture_utilities.glsl)
+
+void main()
+{
+ ivec2 texel = ivec2(gl_GlobalInvocationID.xy);
+ imageStore(output_img, texel, texture_load(input_tx, texel + lower_bound));
+}
diff --git a/source/blender/gpu/shaders/compositor/compositor_projector_lens_distortion.glsl b/source/blender/gpu/shaders/compositor/compositor_projector_lens_distortion.glsl
new file mode 100644
index 00000000000..cf961b20b34
--- /dev/null
+++ b/source/blender/gpu/shaders/compositor/compositor_projector_lens_distortion.glsl
@@ -0,0 +1,16 @@
+#pragma BLENDER_REQUIRE(gpu_shader_compositor_texture_utilities.glsl)
+
+void main()
+{
+ ivec2 texel = ivec2(gl_GlobalInvocationID.xy);
+
+ /* Get the normalized coordinates of the pixel centers. */
+ vec2 normalized_texel = (vec2(texel) + vec2(0.5)) / vec2(texture_size(input_tx));
+
+ /* Sample the red and blue channels shifted by the dispersion amount. */
+ const float red = texture(input_tx, normalized_texel + vec2(dispersion, 0.0)).r;
+ const float green = texture_load(input_tx, texel).g;
+ const float blue = texture(input_tx, normalized_texel - vec2(dispersion, 0.0)).b;
+
+ imageStore(output_img, texel, vec4(red, green, blue, 1.0));
+}
diff --git a/source/blender/gpu/shaders/compositor/compositor_realize_on_domain.glsl b/source/blender/gpu/shaders/compositor/compositor_realize_on_domain.glsl
new file mode 100644
index 00000000000..b2961d07219
--- /dev/null
+++ b/source/blender/gpu/shaders/compositor/compositor_realize_on_domain.glsl
@@ -0,0 +1,25 @@
+#pragma BLENDER_REQUIRE(gpu_shader_compositor_texture_utilities.glsl)
+
+void main()
+{
+ ivec2 texel = ivec2(gl_GlobalInvocationID.xy);
+
+ /* First, transform the input image by transforming the domain coordinates with the inverse of
+ * input image's transformation. The inverse transformation is an affine matrix and thus the
+ * coordinates should be in homogeneous coordinates. */
+ vec2 coordinates = (mat3(inverse_transformation) * vec3(texel, 1.0)).xy;
+
+ /* Since an input image with an identity transformation is supposed to be centered in the domain,
+ * we subtract the offset between the lower left corners of the input image and the domain, which
+ * is half the difference between their sizes, because the difference in size is on both sides of
+ * the centered image. */
+ ivec2 domain_size = imageSize(domain_img);
+ ivec2 input_size = texture_size(input_tx);
+ vec2 offset = (domain_size - input_size) / 2.0;
+
+ /* Subtract the offset and divide by the input image size to get the relevant coordinates into
+ * the sampler's expected [0, 1] range. */
+ vec2 normalized_coordinates = (coordinates - offset) / input_size;
+
+ imageStore(domain_img, texel, texture(input_tx, normalized_coordinates));
+}
diff --git a/source/blender/gpu/shaders/compositor/compositor_screen_lens_distortion.glsl b/source/blender/gpu/shaders/compositor/compositor_screen_lens_distortion.glsl
new file mode 100644
index 00000000000..dc572ea5aaf
--- /dev/null
+++ b/source/blender/gpu/shaders/compositor/compositor_screen_lens_distortion.glsl
@@ -0,0 +1,151 @@
+#pragma BLENDER_REQUIRE(gpu_shader_common_hash.glsl)
+#pragma BLENDER_REQUIRE(gpu_shader_compositor_texture_utilities.glsl)
+
+/* A model that approximates lens distortion parameterized by a distortion parameter and dependent
+ * on the squared distance to the center of the image. The distorted pixel is then computed as the
+ * scalar multiplication of the pixel coordinates with the value returned by this model. See the
+ * compute_distorted_uv function for more details. */
+float compute_distortion_scale(float distortion, float distance_squared)
+{
+ return 1.0 / (1.0 + sqrt(max(0.0, 1.0 - distortion * distance_squared)));
+}
+
+/* A vectorized version of compute_distortion_scale that is applied on the chromatic distortion
+ * parameters passed to the shader. */
+vec3 compute_chromatic_distortion_scale(float distance_squared)
+{
+ return 1.0 / (1.0 + sqrt(max(vec3(0.0), 1.0 - chromatic_distortion * distance_squared)));
+}
+
+/* Compute the image coordinates after distortion by the given distortion scale computed by the
+ * compute_distortion_scale function. Note that the function expects centered normalized UV
+ * coordinates but outputs non-centered image coordinates. */
+vec2 compute_distorted_uv(vec2 uv, float scale)
+{
+ return (uv * scale + 0.5) * texture_size(input_tx) - 0.5;
+}
+
+/* Compute the number of integration steps that should be used to approximate the distorted pixel
+ * using a heuristic, see the compute_number_of_steps function for more details. The numbers of
+ * steps is proportional to the number of pixels spanned by the distortion amount. For jitter
+ * distortion, the square root of the distortion amount plus 1 is used with a minimum of 2 steps.
+ * For non-jitter distortion, the distortion amount plus 1 is used as the number of steps */
+int compute_number_of_integration_steps_heuristic(float distortion)
+{
+#if defined(JITTER)
+ return distortion < 4.0 ? 2 : int(sqrt(distortion + 1.0));
+#else
+ return int(distortion + 1.0);
+#endif
+}
+
+/* Compute the number of integration steps that should be used to compute each channel of the
+ * distorted pixel. Each of the channels are distorted by their respective chromatic distortion
+ * amount, then the amount of distortion between each two consecutive channels is computed, this
+ * amount is then used to heuristically infer the number of needed integration steps, see the
+ * integrate_distortion function for more information. */
+ivec3 compute_number_of_integration_steps(vec2 uv, float distance_squared)
+{
+ /* Distort each channel by its respective chromatic distortion amount. */
+ vec3 distortion_scale = compute_chromatic_distortion_scale(distance_squared);
+ vec2 distorted_uv_red = compute_distorted_uv(uv, distortion_scale.r);
+ vec2 distorted_uv_green = compute_distorted_uv(uv, distortion_scale.g);
+ vec2 distorted_uv_blue = compute_distorted_uv(uv, distortion_scale.b);
+
+ /* Infer the number of needed integration steps to compute the distorted red channel starting
+ * from the green channel. */
+ float distortion_red = distance(distorted_uv_red, distorted_uv_green);
+ int steps_red = compute_number_of_integration_steps_heuristic(distortion_red);
+
+ /* Infer the number of needed integration steps to compute the distorted blue channel starting
+ * from the green channel. */
+ float distortion_blue = distance(distorted_uv_green, distorted_uv_blue);
+ int steps_blue = compute_number_of_integration_steps_heuristic(distortion_blue);
+
+ /* The number of integration steps used to compute the green channel is the sum of both the red
+ * and the blue channel steps because it is computed once with each of them. */
+ return ivec3(steps_red, steps_red + steps_blue, steps_blue);
+}
+
+/* Returns a random jitter amount, which is essentially a random value in the [0, 1] range. If
+ * jitter is not enabled, return a constant 0.5 value instead. */
+float get_jitter(int seed)
+{
+#if defined(JITTER)
+ return hash_uint3_to_float(gl_GlobalInvocationID.x, gl_GlobalInvocationID.y, seed);
+#else
+ return 0.5;
+#endif
+}
+
+/* Each color channel may have a different distortion with the guarantee that the red will have the
+ * lowest distortion while the blue will have the highest one. If each channel is distorted
+ * independently, the image will look disintegrated, with each channel seemingly merely shifted.
+ * Consequently, the distorted pixels needs to be computed by integrating along the path of change
+ * of distortion starting from one channel to another. For instance, to compute the distorted red
+ * from the distorted green, we accumulate the color of the distorted pixel starting from the
+ * distortion of the red, taking small steps until we reach the distortion of the green. The pixel
+ * color is weighted such that it is maximum at the start distortion and zero at the end distortion
+ * in an arithmetic progression. The integration steps can be augmented with random values to
+ * simulate lens jitter. Finally, it should be noted that this function integrates both the start
+ * and end channels in reverse directions for more efficient computation. */
+vec3 integrate_distortion(int start, int end, float distance_squared, vec2 uv, int steps)
+{
+ vec3 accumulated_color = vec3(0.0);
+ float distortion_amount = chromatic_distortion[end] - chromatic_distortion[start];
+ for (int i = 0; i < steps; i++) {
+ /* The increment will be in the [0, 1) range across iterations. */
+ float increment = (i + get_jitter(i)) / steps;
+ float distortion = chromatic_distortion[start] + increment * distortion_amount;
+ float distortion_scale = compute_distortion_scale(distortion, distance_squared);
+
+ /* Sample the color at the distorted coordinates and accumulate it weighted by the increment
+ * value for both the start and end channels. */
+ vec2 distorted_uv = compute_distorted_uv(uv, distortion_scale);
+ vec4 color = texture(input_tx, distorted_uv / texture_size(input_tx));
+ accumulated_color[start] += (1.0 - increment) * color[start];
+ accumulated_color[end] += increment * color[end];
+ }
+ return accumulated_color;
+}
+
+void main()
+{
+ ivec2 texel = ivec2(gl_GlobalInvocationID.xy);
+
+ /* Compute the UV image coordinates in the range [-1, 1] as well as the squared distance to the
+ * center of the image, which is at (0, 0) in the UV coordinates. */
+ vec2 center = texture_size(input_tx) / 2.0;
+ vec2 uv = scale * (texel + 0.5 - center) / center;
+ float distance_squared = dot(uv, uv);
+
+ /* If any of the color channels will get distorted outside of the screen beyond what is possible,
+ * write a zero transparent color and return. */
+ if (any(greaterThan(chromatic_distortion * distance_squared, vec3(1.0)))) {
+ imageStore(output_img, texel, vec4(0.0));
+ return;
+ }
+
+ /* Compute the number of integration steps that should be used to compute each channel of the
+ * distorted pixel. */
+ ivec3 number_of_steps = compute_number_of_integration_steps(uv, distance_squared);
+
+ /* Integrate the distortion of the red and green, then the green and blue channels. That means
+ * the green will be integrated twice, but this is accounted for in the number of steps which the
+ * color will later be divided by. See the compute_number_of_integration_steps function for more
+ * details. */
+ vec3 color = vec3(0.0);
+ color += integrate_distortion(0, 1, distance_squared, uv, number_of_steps.r);
+ color += integrate_distortion(1, 2, distance_squared, uv, number_of_steps.b);
+
+ /* The integration above performed weighted accumulation, and thus the color needs to be divided
+ * by the sum of the weights. Assuming no jitter, the weights are generated as an arithmetic
+ * progression starting from (0.5 / n) to ((n - 0.5) / n) for n terms. The sum of an arithmetic
+ * progression can be computed as (n * (start + end) / 2), which when subsisting the start and
+ * end reduces to (n / 2). So the color should be multiplied by 2 / n. The jitter sequence
+ * approximately sums to the same value because it is a uniform random value whose mean value is
+ * 0.5, so the expression doesn't change regardless of jitter. */
+ color *= 2.0 / vec3(number_of_steps);
+
+ imageStore(output_img, texel, vec4(color, 1.0));
+}
diff --git a/source/blender/gpu/shaders/compositor/compositor_set_alpha.glsl b/source/blender/gpu/shaders/compositor/compositor_set_alpha.glsl
new file mode 100644
index 00000000000..7dd40581790
--- /dev/null
+++ b/source/blender/gpu/shaders/compositor/compositor_set_alpha.glsl
@@ -0,0 +1,8 @@
+#pragma BLENDER_REQUIRE(gpu_shader_compositor_texture_utilities.glsl)
+
+void main()
+{
+ ivec2 texel = ivec2(gl_GlobalInvocationID.xy);
+ vec4 color = vec4(texture_load(image_tx, texel).rgb, texture_load(alpha_tx, texel).x);
+ imageStore(output_img, texel, color);
+}
diff --git a/source/blender/gpu/shaders/compositor/compositor_split_viewer.glsl b/source/blender/gpu/shaders/compositor/compositor_split_viewer.glsl
new file mode 100644
index 00000000000..866b9045da2
--- /dev/null
+++ b/source/blender/gpu/shaders/compositor/compositor_split_viewer.glsl
@@ -0,0 +1,14 @@
+#pragma BLENDER_REQUIRE(gpu_shader_compositor_texture_utilities.glsl)
+
+void main()
+{
+ ivec2 texel = ivec2(gl_GlobalInvocationID.xy);
+#if defined(SPLIT_HORIZONTAL)
+ bool condition = (view_size.x * split_ratio) < texel.x;
+#elif defined(SPLIT_VERTICAL)
+ bool condition = (view_size.y * split_ratio) < texel.y;
+#endif
+ vec4 color = condition ? texture_load(first_image_tx, texel) :
+ texture_load(second_image_tx, texel);
+ imageStore(output_img, texel, color);
+}
diff --git a/source/blender/gpu/shaders/compositor/infos/compositor_alpha_crop_info.hh b/source/blender/gpu/shaders/compositor/infos/compositor_alpha_crop_info.hh
new file mode 100644
index 00000000000..11f2f329cd8
--- /dev/null
+++ b/source/blender/gpu/shaders/compositor/infos/compositor_alpha_crop_info.hh
@@ -0,0 +1,12 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include "gpu_shader_create_info.hh"
+
+GPU_SHADER_CREATE_INFO(compositor_alpha_crop)
+ .local_group_size(16, 16)
+ .push_constant(Type::IVEC2, "lower_bound")
+ .push_constant(Type::IVEC2, "upper_bound")
+ .sampler(0, ImageType::FLOAT_2D, "input_tx")
+ .image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img")
+ .compute_source("compositor_alpha_crop.glsl")
+ .do_static_compilation(true);
diff --git a/source/blender/gpu/shaders/compositor/infos/compositor_box_mask_info.hh b/source/blender/gpu/shaders/compositor/infos/compositor_box_mask_info.hh
new file mode 100644
index 00000000000..ecb253bbab1
--- /dev/null
+++ b/source/blender/gpu/shaders/compositor/infos/compositor_box_mask_info.hh
@@ -0,0 +1,35 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include "gpu_shader_create_info.hh"
+
+GPU_SHADER_CREATE_INFO(compositor_box_mask_shared)
+ .local_group_size(16, 16)
+ .push_constant(Type::IVEC2, "domain_size")
+ .push_constant(Type::VEC2, "location")
+ .push_constant(Type::VEC2, "size")
+ .push_constant(Type::FLOAT, "cos_angle")
+ .push_constant(Type::FLOAT, "sin_angle")
+ .sampler(0, ImageType::FLOAT_2D, "base_mask_tx")
+ .sampler(1, ImageType::FLOAT_2D, "mask_value_tx")
+ .image(0, GPU_R16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_mask_img")
+ .compute_source("compositor_box_mask.glsl");
+
+GPU_SHADER_CREATE_INFO(compositor_box_mask_add)
+ .additional_info("compositor_box_mask_shared")
+ .define("CMP_NODE_MASKTYPE_ADD")
+ .do_static_compilation(true);
+
+GPU_SHADER_CREATE_INFO(compositor_box_mask_subtract)
+ .additional_info("compositor_box_mask_shared")
+ .define("CMP_NODE_MASKTYPE_SUBTRACT")
+ .do_static_compilation(true);
+
+GPU_SHADER_CREATE_INFO(compositor_box_mask_multiply)
+ .additional_info("compositor_box_mask_shared")
+ .define("CMP_NODE_MASKTYPE_MULTIPLY")
+ .do_static_compilation(true);
+
+GPU_SHADER_CREATE_INFO(compositor_box_mask_not)
+ .additional_info("compositor_box_mask_shared")
+ .define("CMP_NODE_MASKTYPE_NOT")
+ .do_static_compilation(true);
diff --git a/source/blender/gpu/shaders/compositor/infos/compositor_convert_info.hh b/source/blender/gpu/shaders/compositor/infos/compositor_convert_info.hh
new file mode 100644
index 00000000000..35e60056736
--- /dev/null
+++ b/source/blender/gpu/shaders/compositor/infos/compositor_convert_info.hh
@@ -0,0 +1,69 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include "gpu_shader_create_info.hh"
+
+GPU_SHADER_CREATE_INFO(compositor_convert_shared)
+ .local_group_size(16, 16)
+ .sampler(0, ImageType::FLOAT_2D, "input_tx")
+ .typedef_source("gpu_shader_compositor_type_conversion.glsl")
+ .compute_source("compositor_convert.glsl");
+
+GPU_SHADER_CREATE_INFO(compositor_convert_float_to_vector)
+ .additional_info("compositor_convert_shared")
+ .image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img")
+ .define("CONVERT_EXPRESSION(value)", "vec4(vec3_from_float(value.x), 0.0)")
+ .do_static_compilation(true);
+
+GPU_SHADER_CREATE_INFO(compositor_convert_float_to_color)
+ .additional_info("compositor_convert_shared")
+ .image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img")
+ .define("CONVERT_EXPRESSION(value)", "vec4_from_float(value.x)")
+ .do_static_compilation(true);
+
+GPU_SHADER_CREATE_INFO(compositor_convert_color_to_float)
+ .additional_info("compositor_convert_shared")
+ .image(0, GPU_R16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img")
+ .define("CONVERT_EXPRESSION(value)", "vec4(float_from_vec4(value), vec3(0.0))")
+ .do_static_compilation(true);
+
+GPU_SHADER_CREATE_INFO(compositor_convert_color_to_vector)
+ .additional_info("compositor_convert_shared")
+ .image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img")
+ .define("CONVERT_EXPRESSION(value)", "vec4(vec3_from_vec4(value), 0.0)")
+ .do_static_compilation(true);
+
+GPU_SHADER_CREATE_INFO(compositor_convert_vector_to_float)
+ .additional_info("compositor_convert_shared")
+ .image(0, GPU_R16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img")
+ .define("CONVERT_EXPRESSION(value)", "vec4(float_from_vec3(value.xyz), vec3(0.0))")
+ .do_static_compilation(true);
+
+GPU_SHADER_CREATE_INFO(compositor_convert_vector_to_color)
+ .additional_info("compositor_convert_shared")
+ .image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img")
+ .define("CONVERT_EXPRESSION(value)", "vec4_from_vec3(value.xyz)")
+ .do_static_compilation(true);
+
+GPU_SHADER_CREATE_INFO(compositor_extract_alpha_from_color)
+ .additional_info("compositor_convert_shared")
+ .image(0, GPU_R16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img")
+ .define("CONVERT_EXPRESSION(value)", "vec4(value.a, vec3(0.0))")
+ .do_static_compilation(true);
+
+GPU_SHADER_CREATE_INFO(compositor_convert_color_to_half_color)
+ .additional_info("compositor_convert_shared")
+ .image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img")
+ .define("CONVERT_EXPRESSION(value)", "value")
+ .do_static_compilation(true);
+
+GPU_SHADER_CREATE_INFO(compositor_convert_float_to_half_float)
+ .additional_info("compositor_convert_shared")
+ .image(0, GPU_R16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img")
+ .define("CONVERT_EXPRESSION(value)", "vec4(value.r, vec3(0.0))")
+ .do_static_compilation(true);
+
+GPU_SHADER_CREATE_INFO(compositor_convert_color_to_opaque)
+ .additional_info("compositor_convert_shared")
+ .image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img")
+ .define("CONVERT_EXPRESSION(value)", "vec4(value.rgb, 1.0)")
+ .do_static_compilation(true);
diff --git a/source/blender/gpu/shaders/compositor/infos/compositor_ellipse_mask_info.hh b/source/blender/gpu/shaders/compositor/infos/compositor_ellipse_mask_info.hh
new file mode 100644
index 00000000000..52db91c94e5
--- /dev/null
+++ b/source/blender/gpu/shaders/compositor/infos/compositor_ellipse_mask_info.hh
@@ -0,0 +1,35 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include "gpu_shader_create_info.hh"
+
+GPU_SHADER_CREATE_INFO(compositor_ellipse_mask_shared)
+ .local_group_size(16, 16)
+ .push_constant(Type::IVEC2, "domain_size")
+ .push_constant(Type::VEC2, "location")
+ .push_constant(Type::VEC2, "radius")
+ .push_constant(Type::FLOAT, "cos_angle")
+ .push_constant(Type::FLOAT, "sin_angle")
+ .sampler(0, ImageType::FLOAT_2D, "base_mask_tx")
+ .sampler(1, ImageType::FLOAT_2D, "mask_value_tx")
+ .image(0, GPU_R16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_mask_img")
+ .compute_source("compositor_ellipse_mask.glsl");
+
+GPU_SHADER_CREATE_INFO(compositor_ellipse_mask_add)
+ .additional_info("compositor_ellipse_mask_shared")
+ .define("CMP_NODE_MASKTYPE_ADD")
+ .do_static_compilation(true);
+
+GPU_SHADER_CREATE_INFO(compositor_ellipse_mask_subtract)
+ .additional_info("compositor_ellipse_mask_shared")
+ .define("CMP_NODE_MASKTYPE_SUBTRACT")
+ .do_static_compilation(true);
+
+GPU_SHADER_CREATE_INFO(compositor_ellipse_mask_multiply)
+ .additional_info("compositor_ellipse_mask_shared")
+ .define("CMP_NODE_MASKTYPE_MULTIPLY")
+ .do_static_compilation(true);
+
+GPU_SHADER_CREATE_INFO(compositor_ellipse_mask_not)
+ .additional_info("compositor_ellipse_mask_shared")
+ .define("CMP_NODE_MASKTYPE_NOT")
+ .do_static_compilation(true);
diff --git a/source/blender/gpu/shaders/compositor/infos/compositor_flip_info.hh b/source/blender/gpu/shaders/compositor/infos/compositor_flip_info.hh
new file mode 100644
index 00000000000..db831518cb7
--- /dev/null
+++ b/source/blender/gpu/shaders/compositor/infos/compositor_flip_info.hh
@@ -0,0 +1,12 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include "gpu_shader_create_info.hh"
+
+GPU_SHADER_CREATE_INFO(compositor_flip)
+ .local_group_size(16, 16)
+ .push_constant(Type::BOOL, "flip_x")
+ .push_constant(Type::BOOL, "flip_y")
+ .sampler(0, ImageType::FLOAT_2D, "input_tx")
+ .image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img")
+ .compute_source("compositor_flip.glsl")
+ .do_static_compilation(true);
diff --git a/source/blender/gpu/shaders/compositor/infos/compositor_image_crop_info.hh b/source/blender/gpu/shaders/compositor/infos/compositor_image_crop_info.hh
new file mode 100644
index 00000000000..e7736744c40
--- /dev/null
+++ b/source/blender/gpu/shaders/compositor/infos/compositor_image_crop_info.hh
@@ -0,0 +1,11 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include "gpu_shader_create_info.hh"
+
+GPU_SHADER_CREATE_INFO(compositor_image_crop)
+ .local_group_size(16, 16)
+ .push_constant(Type::IVEC2, "lower_bound")
+ .sampler(0, ImageType::FLOAT_2D, "input_tx")
+ .image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img")
+ .compute_source("compositor_image_crop.glsl")
+ .do_static_compilation(true);
diff --git a/source/blender/gpu/shaders/compositor/infos/compositor_projector_lens_distortion_info.hh b/source/blender/gpu/shaders/compositor/infos/compositor_projector_lens_distortion_info.hh
new file mode 100644
index 00000000000..98fe1731703
--- /dev/null
+++ b/source/blender/gpu/shaders/compositor/infos/compositor_projector_lens_distortion_info.hh
@@ -0,0 +1,11 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include "gpu_shader_create_info.hh"
+
+GPU_SHADER_CREATE_INFO(compositor_projector_lens_distortion)
+ .local_group_size(16, 16)
+ .push_constant(Type::FLOAT, "dispersion")
+ .sampler(0, ImageType::FLOAT_2D, "input_tx")
+ .image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img")
+ .compute_source("compositor_projector_lens_distortion.glsl")
+ .do_static_compilation(true);
diff --git a/source/blender/gpu/shaders/compositor/infos/compositor_realize_on_domain_info.hh b/source/blender/gpu/shaders/compositor/infos/compositor_realize_on_domain_info.hh
new file mode 100644
index 00000000000..4528649ae98
--- /dev/null
+++ b/source/blender/gpu/shaders/compositor/infos/compositor_realize_on_domain_info.hh
@@ -0,0 +1,24 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include "gpu_shader_create_info.hh"
+
+GPU_SHADER_CREATE_INFO(compositor_realize_on_domain_shared)
+ .local_group_size(16, 16)
+ .push_constant(Type::MAT4, "inverse_transformation")
+ .sampler(0, ImageType::FLOAT_2D, "input_tx")
+ .compute_source("compositor_realize_on_domain.glsl");
+
+GPU_SHADER_CREATE_INFO(compositor_realize_on_domain_color)
+ .additional_info("compositor_realize_on_domain_shared")
+ .image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "domain_img")
+ .do_static_compilation(true);
+
+GPU_SHADER_CREATE_INFO(compositor_realize_on_domain_vector)
+ .additional_info("compositor_realize_on_domain_shared")
+ .image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "domain_img")
+ .do_static_compilation(true);
+
+GPU_SHADER_CREATE_INFO(compositor_realize_on_domain_float)
+ .additional_info("compositor_realize_on_domain_shared")
+ .image(0, GPU_R16F, Qualifier::WRITE, ImageType::FLOAT_2D, "domain_img")
+ .do_static_compilation(true);
diff --git a/source/blender/gpu/shaders/compositor/infos/compositor_screen_lens_distortion_info.hh b/source/blender/gpu/shaders/compositor/infos/compositor_screen_lens_distortion_info.hh
new file mode 100644
index 00000000000..c42f2b328d4
--- /dev/null
+++ b/source/blender/gpu/shaders/compositor/infos/compositor_screen_lens_distortion_info.hh
@@ -0,0 +1,20 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include "gpu_shader_create_info.hh"
+
+GPU_SHADER_CREATE_INFO(compositor_screen_lens_distortion_shared)
+ .local_group_size(16, 16)
+ .push_constant(Type::VEC3, "chromatic_distortion")
+ .push_constant(Type::FLOAT, "scale")
+ .sampler(0, ImageType::FLOAT_2D, "input_tx")
+ .image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img")
+ .compute_source("compositor_screen_lens_distortion.glsl");
+
+GPU_SHADER_CREATE_INFO(compositor_screen_lens_distortion)
+ .additional_info("compositor_screen_lens_distortion_shared")
+ .do_static_compilation(true);
+
+GPU_SHADER_CREATE_INFO(compositor_screen_lens_distortion_jitter)
+ .additional_info("compositor_screen_lens_distortion_shared")
+ .define("JITTER")
+ .do_static_compilation(true);
diff --git a/source/blender/gpu/shaders/compositor/infos/compositor_set_alpha_info.hh b/source/blender/gpu/shaders/compositor/infos/compositor_set_alpha_info.hh
new file mode 100644
index 00000000000..ca28194e921
--- /dev/null
+++ b/source/blender/gpu/shaders/compositor/infos/compositor_set_alpha_info.hh
@@ -0,0 +1,11 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include "gpu_shader_create_info.hh"
+
+GPU_SHADER_CREATE_INFO(compositor_set_alpha)
+ .local_group_size(16, 16)
+ .sampler(0, ImageType::FLOAT_2D, "image_tx")
+ .sampler(1, ImageType::FLOAT_2D, "alpha_tx")
+ .image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img")
+ .compute_source("compositor_set_alpha.glsl")
+ .do_static_compilation(true);
diff --git a/source/blender/gpu/shaders/compositor/infos/compositor_split_viewer_info.hh b/source/blender/gpu/shaders/compositor/infos/compositor_split_viewer_info.hh
new file mode 100644
index 00000000000..d5793b0ce59
--- /dev/null
+++ b/source/blender/gpu/shaders/compositor/infos/compositor_split_viewer_info.hh
@@ -0,0 +1,22 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include "gpu_shader_create_info.hh"
+
+GPU_SHADER_CREATE_INFO(compositor_split_viewer_shared)
+ .local_group_size(16, 16)
+ .push_constant(Type::FLOAT, "split_ratio")
+ .push_constant(Type::IVEC2, "view_size")
+ .sampler(0, ImageType::FLOAT_2D, "first_image_tx")
+ .sampler(1, ImageType::FLOAT_2D, "second_image_tx")
+ .image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img")
+ .compute_source("compositor_split_viewer.glsl");
+
+GPU_SHADER_CREATE_INFO(compositor_split_viewer_horizontal)
+ .additional_info("compositor_split_viewer_shared")
+ .define("SPLIT_HORIZONTAL")
+ .do_static_compilation(true);
+
+GPU_SHADER_CREATE_INFO(compositor_split_viewer_vertical)
+ .additional_info("compositor_split_viewer_shared")
+ .define("SPLIT_VERTICAL")
+ .do_static_compilation(true);
diff --git a/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_alpha_over.glsl b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_alpha_over.glsl
new file mode 100644
index 00000000000..8e3e033147f
--- /dev/null
+++ b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_alpha_over.glsl
@@ -0,0 +1,48 @@
+void node_composite_alpha_over_mixed(
+ float factor, vec4 color, vec4 over_color, float premultiply_factor, out vec4 result)
+{
+ if (over_color.a <= 0.0) {
+ result = color;
+ }
+ else if (factor == 1.0 && over_color.a >= 1.0) {
+ result = over_color;
+ }
+ else {
+ float add_factor = 1.0 - premultiply_factor + over_color.a * premultiply_factor;
+ float premultiplier = factor * add_factor;
+ float multiplier = 1.0 - factor * over_color.a;
+
+ result = multiplier * color + vec2(premultiplier, factor).xxxy * over_color;
+ }
+}
+
+void node_composite_alpha_over_key(float factor, vec4 color, vec4 over_color, out vec4 result)
+{
+ if (over_color.a <= 0.0) {
+ result = color;
+ }
+ else if (factor == 1.0 && over_color.a >= 1.0) {
+ result = over_color;
+ }
+ else {
+ result = mix(color, vec4(over_color.rgb, 1.0), factor * over_color.a);
+ }
+}
+
+void node_composite_alpha_over_premultiply(float factor,
+ vec4 color,
+ vec4 over_color,
+ out vec4 result)
+{
+ if (over_color.a < 0.0) {
+ result = color;
+ }
+ else if (factor == 1.0 && over_color.a >= 1.0) {
+ result = over_color;
+ }
+ else {
+ float multiplier = 1.0 - factor * over_color.a;
+
+ result = multiplier * color + factor * over_color;
+ }
+}
diff --git a/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_bright_contrast.glsl b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_bright_contrast.glsl
new file mode 100644
index 00000000000..ce71b4fd8a4
--- /dev/null
+++ b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_bright_contrast.glsl
@@ -0,0 +1,38 @@
+#pragma BLENDER_REQUIRE(gpu_shader_common_color_utils.glsl)
+
+/* The algorithm is by Werner D. Streidt
+ * (http://visca.com/ffactory/archives/5-99/msg00021.html)
+ * Extracted of OpenCV demhist.c
+ */
+
+#define FLT_EPSILON 1.192092896e-07F
+
+void node_composite_bright_contrast(
+ vec4 color, float brightness, float contrast, const float use_premultiply, out vec4 result)
+{
+ brightness /= 100.0;
+ float delta = contrast / 200.0;
+
+ float multiplier, offset;
+ if (contrast > 0.0) {
+ multiplier = 1.0 - delta * 2.0;
+ multiplier = 1.0 / max(multiplier, FLT_EPSILON);
+ offset = multiplier * (brightness - delta);
+ }
+ else {
+ delta *= -1.0;
+ multiplier = max(1.0 - delta * 2.0, 0.0);
+ offset = multiplier * brightness + delta;
+ }
+
+ if (use_premultiply != 0.0) {
+ color_alpha_unpremultiply(color, color);
+ }
+
+ result.rgb = color.rgb * multiplier + offset;
+ result.a = color.a;
+
+ if (use_premultiply != 0.0) {
+ color_alpha_premultiply(result, result);
+ }
+}
diff --git a/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_channel_matte.glsl b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_channel_matte.glsl
new file mode 100644
index 00000000000..f2dcc9543f2
--- /dev/null
+++ b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_channel_matte.glsl
@@ -0,0 +1,52 @@
+#pragma BLENDER_REQUIRE(gpu_shader_common_color_utils.glsl)
+
+#define CMP_NODE_CHANNEL_MATTE_CS_RGB 1.0
+#define CMP_NODE_CHANNEL_MATTE_CS_HSV 2.0
+#define CMP_NODE_CHANNEL_MATTE_CS_YUV 3.0
+#define CMP_NODE_CHANNEL_MATTE_CS_YCC 4.0
+
+void node_composite_channel_matte(vec4 color,
+ const float color_space,
+ const float matte_channel,
+ const vec2 limit_channels,
+ float max_limit,
+ float min_limit,
+ out vec4 result,
+ out float matte)
+{
+ vec4 channels;
+ if (color_space == CMP_NODE_CHANNEL_MATTE_CS_HSV) {
+ rgb_to_hsv(color, channels);
+ }
+ else if (color_space == CMP_NODE_CHANNEL_MATTE_CS_YUV) {
+ rgba_to_yuva_itu_709(color, channels);
+ }
+ else if (color_space == CMP_NODE_CHANNEL_MATTE_CS_YCC) {
+ rgba_to_ycca_itu_709(color, channels);
+ }
+ else {
+ channels = color;
+ }
+
+ float matte_value = channels[int(matte_channel)];
+ float limit_value = max(channels[int(limit_channels.x)], channels[int(limit_channels.y)]);
+
+ float alpha = 1.0 - (matte_value - limit_value);
+ if (alpha > max_limit) {
+ alpha = color.a;
+ }
+ else if (alpha < min_limit) {
+ alpha = 0.0;
+ }
+ else {
+ alpha = (alpha - min_limit) / (max_limit - min_limit);
+ }
+
+ matte = min(alpha, color.a);
+ result = color * matte;
+}
+
+#undef CMP_NODE_CHANNEL_MATTE_CS_RGB
+#undef CMP_NODE_CHANNEL_MATTE_CS_HSV
+#undef CMP_NODE_CHANNEL_MATTE_CS_YUV
+#undef CMP_NODE_CHANNEL_MATTE_CS_YCC
diff --git a/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_chroma_matte.glsl b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_chroma_matte.glsl
new file mode 100644
index 00000000000..5d6bea0c9db
--- /dev/null
+++ b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_chroma_matte.glsl
@@ -0,0 +1,43 @@
+#pragma BLENDER_REQUIRE(gpu_shader_common_math_utils.glsl)
+#pragma BLENDER_REQUIRE(gpu_shader_common_color_utils.glsl)
+
+/* Algorithm from the book Video Demystified. Chapter 7. Chroma Keying. */
+void node_composite_chroma_matte(vec4 color,
+ vec4 key,
+ float acceptance,
+ float cutoff,
+ float falloff,
+ out vec4 result,
+ out float matte)
+{
+ vec4 color_ycca;
+ rgba_to_ycca_itu_709(color, color_ycca);
+ vec4 key_ycca;
+ rgba_to_ycca_itu_709(key, key_ycca);
+
+ /* Normalize the CrCb components into the [-1, 1] range. */
+ vec2 color_cc = color_ycca.yz * 2.0 - 1.0;
+ vec2 key_cc = key_ycca.yz * 2.0 - 1.0;
+
+ /* Rotate the color onto the space of the key such that x axis of the color space passes through
+ * the key color. */
+ color_cc = vector_to_rotation_matrix(key_cc * vec2(1.0, -1.0)) * color_cc;
+
+ /* Compute foreground key. If positive, the value is in the [0, 1] range. */
+ float foreground_key = color_cc.x - (abs(color_cc.y) / acceptance);
+
+ /* Negative foreground key values retain the original alpha. Positive values are scaled by the
+ * falloff, while colors that make an angle less than the cutoff angle get a zero alpha. */
+ float alpha = color.a;
+ if (foreground_key > 0.0) {
+ alpha = 1.0 - (foreground_key / falloff);
+
+ if (abs(atan(color_cc.y, color_cc.x)) < (cutoff / 2.0)) {
+ alpha = 0.0;
+ }
+ }
+
+ /* Compute output. */
+ matte = min(alpha, color.a);
+ result = color * matte;
+}
diff --git a/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_color_balance.glsl b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_color_balance.glsl
new file mode 100644
index 00000000000..bffb94cdedb
--- /dev/null
+++ b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_color_balance.glsl
@@ -0,0 +1,34 @@
+#pragma BLENDER_REQUIRE(gpu_shader_common_color_utils.glsl)
+
+void node_composite_color_balance_lgg(
+ float factor, vec4 color, vec3 lift, vec3 gamma, vec3 gain, out vec4 result)
+{
+ lift = 2.0 - lift;
+ vec3 srgb_color = linear_rgb_to_srgb(color.rgb);
+ vec3 lift_balanced = ((srgb_color - 1.0) * lift) + 1.0;
+
+ vec3 gain_balanced = lift_balanced * gain;
+ gain_balanced = max(gain_balanced, vec3(0.0));
+
+ vec3 linear_color = srgb_to_linear_rgb(gain_balanced);
+ gamma = mix(gamma, vec3(1e-6), equal(gamma, vec3(0.0)));
+ vec3 gamma_balanced = pow(linear_color, 1.0 / gamma);
+
+ result.rgb = mix(color.rgb, gamma_balanced, min(factor, 1.0));
+ result.a = color.a;
+}
+
+void node_composite_color_balance_asc_cdl(float factor,
+ vec4 color,
+ vec3 offset,
+ vec3 power,
+ vec3 slope,
+ float offset_basis,
+ out vec4 result)
+{
+ offset += offset_basis;
+ vec3 balanced = color.rgb * slope + offset;
+ balanced = pow(max(balanced, vec3(0.0)), power);
+ result.rgb = mix(color.rgb, balanced, min(factor, 1.0));
+ result.a = color.a;
+}
diff --git a/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_color_correction.glsl b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_color_correction.glsl
new file mode 100644
index 00000000000..9b4858f03be
--- /dev/null
+++ b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_color_correction.glsl
@@ -0,0 +1,87 @@
+#pragma BLENDER_REQUIRE(gpu_shader_common_math_utils.glsl)
+#pragma BLENDER_REQUIRE(gpu_shader_common_color_utils.glsl)
+
+void node_composite_color_correction(vec4 color,
+ float mask,
+ const vec3 enabled_channels,
+ float start_midtones,
+ float end_midtones,
+ float master_saturation,
+ float master_contrast,
+ float master_gamma,
+ float master_gain,
+ float master_lift,
+ float shadows_saturation,
+ float shadows_contrast,
+ float shadows_gamma,
+ float shadows_gain,
+ float shadows_lift,
+ float midtones_saturation,
+ float midtones_contrast,
+ float midtones_gamma,
+ float midtones_gain,
+ float midtones_lift,
+ float highlights_saturation,
+ float highlights_contrast,
+ float highlights_gamma,
+ float highlights_gain,
+ float highlights_lift,
+ const vec3 luminance_coefficients,
+ out vec4 result)
+{
+ const float margin = 0.10;
+ const float margin_divider = 0.5 / margin;
+ float level = (color.r + color.g + color.b) / 3.0;
+ float level_shadows = 0.0;
+ float level_midtones = 0.0;
+ float level_highlights = 0.0;
+ if (level < (start_midtones - margin)) {
+ level_shadows = 1.0;
+ }
+ else if (level < (start_midtones + margin)) {
+ level_midtones = ((level - start_midtones) * margin_divider) + 0.5;
+ level_shadows = 1.0 - level_midtones;
+ }
+ else if (level < (end_midtones - margin)) {
+ level_midtones = 1.0;
+ }
+ else if (level < (end_midtones + margin)) {
+ level_highlights = ((level - end_midtones) * margin_divider) + 0.5;
+ level_midtones = 1.0 - level_highlights;
+ }
+ else {
+ level_highlights = 1.0;
+ }
+
+ float contrast = level_shadows * shadows_contrast;
+ contrast += level_midtones * midtones_contrast;
+ contrast += level_highlights * highlights_contrast;
+ contrast *= master_contrast;
+ float saturation = level_shadows * shadows_saturation;
+ saturation += level_midtones * midtones_saturation;
+ saturation += level_highlights * highlights_saturation;
+ saturation *= master_saturation;
+ float gamma = level_shadows * shadows_gamma;
+ gamma += level_midtones * midtones_gamma;
+ gamma += level_highlights * highlights_gamma;
+ gamma *= master_gamma;
+ float gain = level_shadows * shadows_gain;
+ gain += level_midtones * midtones_gain;
+ gain += level_highlights * highlights_gain;
+ gain *= master_gain;
+ float lift = level_shadows * shadows_lift;
+ lift += level_midtones * midtones_lift;
+ lift += level_highlights * highlights_lift;
+ lift += master_lift;
+
+ float inverse_gamma = 1.0 / gamma;
+ float luma = get_luminance(color.rgb, luminance_coefficients);
+
+ vec3 corrected = luma + saturation * (color.rgb - luma);
+ corrected = 0.5 + (corrected - 0.5) * contrast;
+ corrected = fallback_pow(corrected * gain + lift, inverse_gamma, corrected);
+ corrected = mix(color.rgb, corrected, min(mask, 1.0));
+
+ result.rgb = mix(corrected, color.rgb, equal(enabled_channels, vec3(0.0)));
+ result.a = color.a;
+}
diff --git a/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_color_matte.glsl b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_color_matte.glsl
new file mode 100644
index 00000000000..038471bc1bc
--- /dev/null
+++ b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_color_matte.glsl
@@ -0,0 +1,27 @@
+#pragma BLENDER_REQUIRE(gpu_shader_common_color_utils.glsl)
+
+void node_composite_color_matte(vec4 color,
+ vec4 key,
+ float hue_epsilon,
+ float saturation_epsilon,
+ float value_epsilon,
+ out vec4 result,
+ out float matte)
+
+{
+ vec4 color_hsva;
+ rgb_to_hsv(color, color_hsva);
+ vec4 key_hsva;
+ rgb_to_hsv(key, key_hsva);
+
+ bool is_within_saturation = distance(color_hsva.y, key_hsva.y) < saturation_epsilon;
+ bool is_within_value = distance(color_hsva.z, key_hsva.z) < value_epsilon;
+ bool is_within_hue = distance(color_hsva.x, key_hsva.x) < hue_epsilon;
+ /* Hue wraps around, so check the distance around the boundary. */
+ float min_hue = min(color_hsva.x, key_hsva.x);
+ float max_hue = max(color_hsva.x, key_hsva.x);
+ is_within_hue = is_within_hue || ((min_hue + (1.0 - max_hue)) < hue_epsilon);
+
+ matte = (is_within_hue && is_within_saturation && is_within_value) ? 0.0 : color.a;
+ result = color * matte;
+}
diff --git a/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_color_spill.glsl b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_color_spill.glsl
new file mode 100644
index 00000000000..0adad53ad80
--- /dev/null
+++ b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_color_spill.glsl
@@ -0,0 +1,13 @@
+void node_composite_color_spill(vec4 color,
+ float factor,
+ const float spill_channel,
+ vec3 spill_scale,
+ const vec2 limit_channels,
+ float limit_scale,
+ out vec4 result)
+{
+ float average_limit = (color[int(limit_channels.x)] + color[int(limit_channels.y)]) / 2.0;
+ float map = factor * color[int(spill_channel)] - limit_scale * average_limit;
+ result.rgb = map > 0.0 ? color.rgb + spill_scale * map : color.rgb;
+ result.a = color.a;
+}
diff --git a/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_color_to_luminance.glsl b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_color_to_luminance.glsl
new file mode 100644
index 00000000000..bcdd625bd4f
--- /dev/null
+++ b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_color_to_luminance.glsl
@@ -0,0 +1,6 @@
+#pragma BLENDER_REQUIRE(gpu_shader_common_color_utils.glsl)
+
+void color_to_luminance(vec4 color, const vec3 luminance_coefficients, out float result)
+{
+ result = get_luminance(color.rgb, luminance_coefficients);
+}
diff --git a/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_difference_matte.glsl b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_difference_matte.glsl
new file mode 100644
index 00000000000..d769cadce3c
--- /dev/null
+++ b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_difference_matte.glsl
@@ -0,0 +1,10 @@
+void node_composite_difference_matte(
+ vec4 color, vec4 key, float tolerance, float falloff, out vec4 result, out float matte)
+{
+ vec4 difference = abs(color - key);
+ float average_difference = (difference.r + difference.g + difference.b) / 3.0;
+ bool is_opaque = average_difference > tolerance + falloff;
+ float alpha = is_opaque ? color.a : (max(0.0, average_difference - tolerance) / falloff);
+ matte = min(alpha, color.a);
+ result = color * matte;
+}
diff --git a/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_distance_matte.glsl b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_distance_matte.glsl
new file mode 100644
index 00000000000..9beed66826c
--- /dev/null
+++ b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_distance_matte.glsl
@@ -0,0 +1,26 @@
+#pragma BLENDER_REQUIRE(gpu_shader_common_color_utils.glsl)
+
+void node_composite_distance_matte_rgba(
+ vec4 color, vec4 key, float tolerance, float falloff, out vec4 result, out float matte)
+{
+ float difference = distance(color.rgb, key.rgb);
+ bool is_opaque = difference > tolerance + falloff;
+ float alpha = is_opaque ? color.a : max(0.0, difference - tolerance) / falloff;
+ matte = min(alpha, color.a);
+ result = color * matte;
+}
+
+void node_composite_distance_matte_ycca(
+ vec4 color, vec4 key, float tolerance, float falloff, out vec4 result, out float matte)
+{
+ vec4 color_ycca;
+ rgba_to_ycca_itu_709(color, color_ycca);
+ vec4 key_ycca;
+ rgba_to_ycca_itu_709(key, key_ycca);
+
+ float difference = distance(color_ycca.yz, key_ycca.yz);
+ bool is_opaque = difference > tolerance + falloff;
+ float alpha = is_opaque ? color.a : max(0.0, difference - tolerance) / falloff;
+ matte = min(alpha, color.a);
+ result = color * matte;
+}
diff --git a/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_exposure.glsl b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_exposure.glsl
new file mode 100644
index 00000000000..f246635a91e
--- /dev/null
+++ b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_exposure.glsl
@@ -0,0 +1,6 @@
+void node_composite_exposure(vec4 color, float exposure, out vec4 result)
+{
+ float multiplier = exp2(exposure);
+ result.rgb = color.rgb * multiplier;
+ result.a = color.a;
+}
diff --git a/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_gamma.glsl b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_gamma.glsl
new file mode 100644
index 00000000000..53070d4b0e2
--- /dev/null
+++ b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_gamma.glsl
@@ -0,0 +1,7 @@
+#pragma BLENDER_REQUIRE(gpu_shader_common_math_utils.glsl)
+
+void node_composite_gamma(vec4 color, float gamma, out vec4 result)
+{
+ result.rgb = fallback_pow(color.rgb, gamma, color.rgb);
+ result.a = color.a;
+}
diff --git a/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_hue_correct.glsl b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_hue_correct.glsl
new file mode 100644
index 00000000000..99eb125cdf2
--- /dev/null
+++ b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_hue_correct.glsl
@@ -0,0 +1,39 @@
+#pragma BLENDER_REQUIRE(gpu_shader_common_color_utils.glsl)
+
+/* Curve maps are stored in sampler objects that are evaluated in the [0, 1] range, so normalize
+ * parameters accordingly. */
+#define NORMALIZE_PARAMETER(parameter, minimum, range) ((parameter - minimum) * range)
+
+void node_composite_hue_correct(float factor,
+ vec4 color,
+ sampler1DArray curve_map,
+ const float layer,
+ vec3 minimums,
+ vec3 range_dividers,
+ out vec4 result)
+{
+ vec4 hsv;
+ rgb_to_hsv(color, hsv);
+
+ /* First, adjust the hue channel on its own, since corrections in the saturation and value
+ * channels depends on the new value of the hue, not its original value. A curve map value of 0.5
+ * means no change in hue, so adjust the value to get an identity at 0.5. Since the identity of
+ * addition is 0, we subtract 0.5 (0.5 - 0.5 = 0). */
+ const float hue_parameter = NORMALIZE_PARAMETER(hsv.x, minimums.x, range_dividers.x);
+ hsv.x += texture(curve_map, vec2(hue_parameter, layer)).x - 0.5;
+
+ /* Second, adjust the saturation and value based on the new value of the hue. A curve map value
+ * of 0.5 means no change in hue, so adjust the value to get an identity at 0.5. Since the
+ * identity of duplication is 1, we multiply by 2 (0.5 * 2 = 1). */
+ vec2 parameters = NORMALIZE_PARAMETER(hsv.x, minimums.yz, range_dividers.yz);
+ hsv.y *= texture(curve_map, vec2(parameters.x, layer)).y * 2.0;
+ hsv.z *= texture(curve_map, vec2(parameters.y, layer)).z * 2.0;
+
+ /* Sanitize the new hue and saturation values. */
+ hsv.x = fract(hsv.x);
+ hsv.y = clamp(hsv.y, 0.0, 1.0);
+
+ hsv_to_rgb(hsv, result);
+
+ result = mix(color, result, factor);
+}
diff --git a/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_hue_saturation_value.glsl b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_hue_saturation_value.glsl
new file mode 100644
index 00000000000..dd5eb33d318
--- /dev/null
+++ b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_hue_saturation_value.glsl
@@ -0,0 +1,16 @@
+#pragma BLENDER_REQUIRE(gpu_shader_common_color_utils.glsl)
+
+void node_composite_hue_saturation_value(
+ vec4 color, float hue, float saturation, float value, float factor, out vec4 result)
+{
+ vec4 hsv;
+ rgb_to_hsv(color, hsv);
+
+ hsv.x = fract(hsv.x + hue + 0.5);
+ hsv.y = clamp(hsv.y * saturation, 0.0, 1.0);
+ hsv.z = hsv.z * value;
+
+ hsv_to_rgb(hsv, result);
+
+ result = mix(color, result, factor);
+}
diff --git a/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_invert.glsl b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_invert.glsl
new file mode 100644
index 00000000000..59be746da7f
--- /dev/null
+++ b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_invert.glsl
@@ -0,0 +1,13 @@
+#pragma BLENDER_REQUIRE(gpu_shader_common_color_utils.glsl)
+
+void node_composite_invert(float fac, vec4 color, float do_rgb, float do_alpha, out vec4 result)
+{
+ result = color;
+ if (do_rgb != 0.0) {
+ result.rgb = 1.0 - result.rgb;
+ }
+ if (do_alpha != 0.0) {
+ result.a = 1.0 - result.a;
+ }
+ result = mix(color, result, fac);
+}
diff --git a/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_luminance_matte.glsl b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_luminance_matte.glsl
new file mode 100644
index 00000000000..3647ac583fe
--- /dev/null
+++ b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_luminance_matte.glsl
@@ -0,0 +1,14 @@
+#pragma BLENDER_REQUIRE(gpu_shader_common_color_utils.glsl)
+
+void node_composite_luminance_matte(vec4 color,
+ float high,
+ float low,
+ const vec3 luminance_coefficients,
+ out vec4 result,
+ out float matte)
+{
+ float luminance = get_luminance(color.rgb, luminance_coefficients);
+ float alpha = clamp(0.0, 1.0, (luminance - low) / (high - low));
+ matte = min(alpha, color.a);
+ result = color * matte;
+}
diff --git a/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_main.glsl b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_main.glsl
new file mode 100644
index 00000000000..27624223dbc
--- /dev/null
+++ b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_main.glsl
@@ -0,0 +1,7 @@
+/* The compute shader that will be dispatched by the compositor ShaderOperation. It just calls the
+ * evaluate function that will be dynamically generated and appended to this shader in the
+ * ShaderOperation::generate_code method. */
+void main()
+{
+ evaluate();
+}
diff --git a/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_map_value.glsl b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_map_value.glsl
new file mode 100644
index 00000000000..20874b4ef44
--- /dev/null
+++ b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_map_value.glsl
@@ -0,0 +1,56 @@
+/* An arbitrary value determined by Blender. */
+#define BLENDER_ZMAX 10000.0
+
+void node_composite_map_range(float value,
+ float from_min,
+ float from_max,
+ float to_min,
+ float to_max,
+ const float should_clamp,
+ out float result)
+{
+ if (abs(from_max - from_min) < 1e-6) {
+ result = 0.0;
+ }
+ else {
+ if (value >= -BLENDER_ZMAX && value <= BLENDER_ZMAX) {
+ result = (value - from_min) / (from_max - from_min);
+ result = to_min + result * (to_max - to_min);
+ }
+ else if (value > BLENDER_ZMAX) {
+ result = to_max;
+ }
+ else {
+ result = to_min;
+ }
+
+ if (should_clamp != 0.0) {
+ if (to_max > to_min) {
+ result = clamp(result, to_min, to_max);
+ }
+ else {
+ result = clamp(result, to_max, to_min);
+ }
+ }
+ }
+}
+
+void node_composite_map_value(float value,
+ float offset,
+ float size,
+ const float use_min,
+ float min,
+ const float use_max,
+ float max,
+ out float result)
+{
+ result = (value + offset) * size;
+
+ if (use_min != 0.0 && result < min) {
+ result = min;
+ }
+
+ if (use_max != 0.0 && result > max) {
+ result = max;
+ }
+}
diff --git a/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_normal.glsl b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_normal.glsl
new file mode 100644
index 00000000000..a2e3b6c4aaa
--- /dev/null
+++ b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_normal.glsl
@@ -0,0 +1,9 @@
+void node_composite_normal(vec3 input_vector,
+ vec3 input_normal,
+ out vec3 result_normal,
+ out float result_dot)
+{
+ vec3 normal = normalize(input_normal);
+ result_normal = normal;
+ result_dot = -dot(input_vector, normal);
+}
diff --git a/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_posterize.glsl b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_posterize.glsl
new file mode 100644
index 00000000000..ee8ae234abe
--- /dev/null
+++ b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_posterize.glsl
@@ -0,0 +1,6 @@
+void node_composite_posterize(vec4 color, float steps, out vec4 result)
+{
+ steps = clamp(steps, 2.0, 1024.0);
+ result = floor(color * steps) / steps;
+ result.a = color.a;
+}
diff --git a/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_separate_combine.glsl b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_separate_combine.glsl
new file mode 100644
index 00000000000..d72d2260394
--- /dev/null
+++ b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_separate_combine.glsl
@@ -0,0 +1,132 @@
+#pragma BLENDER_REQUIRE(gpu_shader_common_color_utils.glsl)
+
+/* ** Combine/Separate XYZ ** */
+
+void node_composite_combine_xyz(float x, float y, float z, out vec3 vector)
+{
+ vector = vec3(x, y, z);
+}
+
+void node_composite_separate_xyz(vec3 vector, out float x, out float y, out float z)
+{
+ x = vector.x;
+ y = vector.y;
+ z = vector.z;
+}
+
+/* ** Combine/Separate RGBA ** */
+
+void node_composite_combine_rgba(float r, float g, float b, float a, out vec4 color)
+{
+ color = vec4(r, g, b, a);
+}
+
+void node_composite_separate_rgba(vec4 color, out float r, out float g, out float b, out float a)
+{
+ r = color.r;
+ g = color.g;
+ b = color.b;
+ a = color.a;
+}
+
+/* ** Combine/Separate HSVA ** */
+
+void node_composite_combine_hsva(float h, float s, float v, float a, out vec4 color)
+{
+ hsv_to_rgb(vec4(h, s, v, a), color);
+}
+
+void node_composite_separate_hsva(vec4 color, out float h, out float s, out float v, out float a)
+{
+ vec4 hsva;
+ rgb_to_hsv(color, hsva);
+ h = hsva.x;
+ s = hsva.y;
+ v = hsva.z;
+ a = hsva.a;
+}
+
+/* ** Combine/Separate HSLA ** */
+
+void node_composite_combine_hsla(float h, float s, float l, float a, out vec4 color)
+{
+ hsl_to_rgb(vec4(h, s, l, a), color);
+}
+
+void node_composite_separate_hsla(vec4 color, out float h, out float s, out float l, out float a)
+{
+ vec4 hsla;
+ rgb_to_hsl(color, hsla);
+ h = hsla.x;
+ s = hsla.y;
+ l = hsla.z;
+ a = hsla.a;
+}
+
+/* ** Combine/Separate YCCA ** */
+
+void node_composite_combine_ycca_itu_601(float y, float cb, float cr, float a, out vec4 color)
+{
+ ycca_to_rgba_itu_601(vec4(y, cb, cr, a), color);
+}
+
+void node_composite_combine_ycca_itu_709(float y, float cb, float cr, float a, out vec4 color)
+{
+ ycca_to_rgba_itu_709(vec4(y, cb, cr, a), color);
+}
+
+void node_composite_combine_ycca_jpeg(float y, float cb, float cr, float a, out vec4 color)
+{
+ ycca_to_rgba_jpeg(vec4(y, cb, cr, a), color);
+}
+
+void node_composite_separate_ycca_itu_601(
+ vec4 color, out float y, out float cb, out float cr, out float a)
+{
+ vec4 ycca;
+ rgba_to_ycca_itu_601(color, ycca);
+ y = ycca.x;
+ cb = ycca.y;
+ cr = ycca.z;
+ a = ycca.a;
+}
+
+void node_composite_separate_ycca_itu_709(
+ vec4 color, out float y, out float cb, out float cr, out float a)
+{
+ vec4 ycca;
+ rgba_to_ycca_itu_709(color, ycca);
+ y = ycca.x;
+ cb = ycca.y;
+ cr = ycca.z;
+ a = ycca.a;
+}
+
+void node_composite_separate_ycca_jpeg(
+ vec4 color, out float y, out float cb, out float cr, out float a)
+{
+ vec4 ycca;
+ rgba_to_ycca_jpeg(color, ycca);
+ y = ycca.x;
+ cb = ycca.y;
+ cr = ycca.z;
+ a = ycca.a;
+}
+
+/* ** Combine/Separate YUVA ** */
+
+void node_composite_combine_yuva_itu_709(float y, float u, float v, float a, out vec4 color)
+{
+ yuva_to_rgba_itu_709(vec4(y, u, v, a), color);
+}
+
+void node_composite_separate_yuva_itu_709(
+ vec4 color, out float y, out float u, out float v, out float a)
+{
+ vec4 yuva;
+ rgba_to_yuva_itu_709(color, yuva);
+ y = yuva.x;
+ u = yuva.y;
+ v = yuva.z;
+ a = yuva.a;
+}
diff --git a/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_set_alpha.glsl b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_set_alpha.glsl
new file mode 100644
index 00000000000..95380d1ed0f
--- /dev/null
+++ b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_set_alpha.glsl
@@ -0,0 +1,9 @@
+void node_composite_set_alpha_apply(vec4 color, float alpha, out vec4 result)
+{
+ result = color * alpha;
+}
+
+void node_composite_set_alpha_replace(vec4 color, float alpha, out vec4 result)
+{
+ result = vec4(color.rgb, alpha);
+}
diff --git a/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_store_output.glsl b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_store_output.glsl
new file mode 100644
index 00000000000..7fba26907b5
--- /dev/null
+++ b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_store_output.glsl
@@ -0,0 +1,26 @@
+/* The following functions are called to store the given value in the output identified by the
+ * given ID. The ID is an unsigned integer that is encoded in a float, so floatBitsToUint is called
+ * to get the actual identifier. The functions have an output value as their last argument that is
+ * used to establish an output link that is then used to track the nodes that contribute to the
+ * output of the compositor node tree.
+ *
+ * The store_[float|vector|color] functions are dynamically generated in
+ * ShaderOperation::generate_code_for_outputs. */
+
+void node_compositor_store_output_float(const float id, float value, out float out_value)
+{
+ store_float(floatBitsToUint(id), value);
+ out_value = value;
+}
+
+void node_compositor_store_output_vector(const float id, vec3 vector, out vec3 out_vector)
+{
+ store_vector(floatBitsToUint(id), vector);
+ out_vector = vector;
+}
+
+void node_compositor_store_output_color(const float id, vec4 color, out vec4 out_color)
+{
+ store_color(floatBitsToUint(id), color);
+ out_color = color;
+}
diff --git a/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_texture_utilities.glsl b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_texture_utilities.glsl
new file mode 100644
index 00000000000..00e9a391097
--- /dev/null
+++ b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_texture_utilities.glsl
@@ -0,0 +1,25 @@
+/* A shorthand for 1D textureSize with a zero LOD. */
+int texture_size(sampler1D sampler)
+{
+ return textureSize(sampler, 0);
+}
+
+/* A shorthand for 1D texelFetch with zero LOD and bounded access clamped to border. */
+vec4 texture_load(sampler1D sampler, int x)
+{
+ const int texture_bound = texture_size(sampler) - 1;
+ return texelFetch(sampler, clamp(x, 0, texture_bound), 0);
+}
+
+/* A shorthand for 2D textureSize with a zero LOD. */
+ivec2 texture_size(sampler2D sampler)
+{
+ return textureSize(sampler, 0);
+}
+
+/* A shorthand for 2D texelFetch with zero LOD and bounded access clamped to border. */
+vec4 texture_load(sampler2D sampler, ivec2 texel)
+{
+ const ivec2 texture_bounds = texture_size(sampler) - ivec2(1);
+ return texelFetch(sampler, clamp(texel, ivec2(0), texture_bounds), 0);
+}
diff --git a/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_type_conversion.glsl b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_type_conversion.glsl
new file mode 100644
index 00000000000..75c76fd7341
--- /dev/null
+++ b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_type_conversion.glsl
@@ -0,0 +1,29 @@
+float float_from_vec4(vec4 vector)
+{
+ return dot(vector.rgb, vec3(1.0)) / 3.0;
+}
+
+float float_from_vec3(vec3 vector)
+{
+ return dot(vector, vec3(1.0)) / 3.0;
+}
+
+vec3 vec3_from_vec4(vec4 vector)
+{
+ return vector.rgb;
+}
+
+vec3 vec3_from_float(float value)
+{
+ return vec3(value);
+}
+
+vec4 vec4_from_vec3(vec3 vector)
+{
+ return vec4(vector, 1.0);
+}
+
+vec4 vec4_from_float(float value)
+{
+ return vec4(vec3(value), 1.0);
+}
diff --git a/source/blender/imbuf/IMB_colormanagement.h b/source/blender/imbuf/IMB_colormanagement.h
index 0818dd653a1..2fb1d814c83 100644
--- a/source/blender/imbuf/IMB_colormanagement.h
+++ b/source/blender/imbuf/IMB_colormanagement.h
@@ -56,6 +56,8 @@ bool IMB_colormanagement_space_name_is_data(const char *name);
bool IMB_colormanagement_space_name_is_scene_linear(const char *name);
bool IMB_colormanagement_space_name_is_srgb(const char *name);
+BLI_INLINE void IMB_colormanagement_get_luminance_coefficients(float r_rgb[3]);
+
/**
* Convert a float RGB triplet to the correct luminance weighted average.
*
diff --git a/source/blender/imbuf/IMB_imbuf_types.h b/source/blender/imbuf/IMB_imbuf_types.h
index 1b32bef0a98..5ad226a26f2 100644
--- a/source/blender/imbuf/IMB_imbuf_types.h
+++ b/source/blender/imbuf/IMB_imbuf_types.h
@@ -251,11 +251,11 @@ typedef struct ImBuf {
int refcounter;
/* some parameters to pass along for packing images */
- /** Compressed image only used with png and exr currently */
+ /** Compressed image only used with PNG and EXR currently */
unsigned char *encodedbuffer;
- /** Size of data written to encodedbuffer */
+ /** Size of data written to `encodedbuffer`. */
unsigned int encodedsize;
- /** Size of encodedbuffer */
+ /** Size of `encodedbuffer` */
unsigned int encodedbuffersize;
/* color management */
diff --git a/source/blender/imbuf/intern/colormanagement_inline.c b/source/blender/imbuf/intern/colormanagement_inline.c
index 668307ec802..3c6c0f5fd0a 100644
--- a/source/blender/imbuf/intern/colormanagement_inline.c
+++ b/source/blender/imbuf/intern/colormanagement_inline.c
@@ -11,6 +11,11 @@
#include "BLI_math_vector.h"
#include "IMB_colormanagement_intern.h"
+void IMB_colormanagement_get_luminance_coefficients(float r_rgb[3])
+{
+ copy_v3_v3(r_rgb, imbuf_luma_coefficients);
+}
+
float IMB_colormanagement_get_luminance(const float rgb[3])
{
return dot_v3v3(imbuf_luma_coefficients, rgb);
diff --git a/source/blender/io/collada/EffectExporter.cpp b/source/blender/io/collada/EffectExporter.cpp
index 71a54e3a7c9..40ce20617fc 100644
--- a/source/blender/io/collada/EffectExporter.cpp
+++ b/source/blender/io/collada/EffectExporter.cpp
@@ -46,6 +46,7 @@ EffectsExporter::EffectsExporter(COLLADASW::StreamWriter *sw,
bool EffectsExporter::hasEffects(Scene *sce)
{
+ bool result = false;
FOREACH_SCENE_OBJECT_BEGIN (sce, ob) {
int a;
for (a = 0; a < ob->totcol; a++) {
@@ -56,11 +57,12 @@ bool EffectsExporter::hasEffects(Scene *sce)
continue;
}
- return true;
+ result = true;
+ break;
}
}
FOREACH_SCENE_OBJECT_END;
- return false;
+ return result;
}
void EffectsExporter::exportEffects(bContext *C, Scene *sce)
diff --git a/source/blender/io/common/CMakeLists.txt b/source/blender/io/common/CMakeLists.txt
index a6818c0bf5d..ee5c6a0a47f 100644
--- a/source/blender/io/common/CMakeLists.txt
+++ b/source/blender/io/common/CMakeLists.txt
@@ -19,15 +19,15 @@ set(SRC
intern/dupli_parent_finder.cc
intern/dupli_persistent_id.cc
intern/object_identifier.cc
- intern/path_util.cc
intern/orientation.c
+ intern/path_util.cc
IO_abstract_hierarchy_iterator.h
IO_dupli_persistent_id.hh
+ IO_orientation.h
IO_path_util.hh
IO_path_util_types.h
IO_types.h
- IO_orientation.h
intern/dupli_parent_finder.hh
)
diff --git a/source/blender/io/stl/CMakeLists.txt b/source/blender/io/stl/CMakeLists.txt
index e0c48bbbf7e..3a21da5c579 100644
--- a/source/blender/io/stl/CMakeLists.txt
+++ b/source/blender/io/stl/CMakeLists.txt
@@ -24,16 +24,16 @@ set(INC_SYS
set(SRC
IO_stl.cc
- importer/stl_import_mesh.cc
+ importer/stl_import.cc
importer/stl_import_ascii_reader.cc
importer/stl_import_binary_reader.cc
- importer/stl_import.cc
+ importer/stl_import_mesh.cc
IO_stl.h
- importer/stl_import_mesh.hh
+ importer/stl_import.hh
importer/stl_import_ascii_reader.hh
importer/stl_import_binary_reader.hh
- importer/stl_import.hh
+ importer/stl_import_mesh.hh
)
set(LIB
diff --git a/source/blender/io/usd/intern/usd_reader_mesh.cc b/source/blender/io/usd/intern/usd_reader_mesh.cc
index f65657b240c..103bb0d0cef 100644
--- a/source/blender/io/usd/intern/usd_reader_mesh.cc
+++ b/source/blender/io/usd/intern/usd_reader_mesh.cc
@@ -82,9 +82,8 @@ static pxr::UsdShadeMaterial compute_bound_material(const pxr::UsdPrim &prim)
return mtl;
}
-/* Returns an existing Blender material that corresponds to the USD
- * material with with the given path. Returns null if no such material
- * exists. */
+/* Returns an existing Blender material that corresponds to the USD material with the given path.
+ * Returns null if no such material exists. */
static Material *find_existing_material(
const pxr::SdfPath &usd_mat_path,
const USDImportParams &params,
diff --git a/source/blender/io/wavefront_obj/IO_wavefront_obj.cc b/source/blender/io/wavefront_obj/IO_wavefront_obj.cc
index fb0b4a1aca9..2fd2973ee73 100644
--- a/source/blender/io/wavefront_obj/IO_wavefront_obj.cc
+++ b/source/blender/io/wavefront_obj/IO_wavefront_obj.cc
@@ -4,6 +4,7 @@
* \ingroup obj
*/
+#include "BLI_path_util.h"
#include "BLI_timeit.hh"
#include "IO_wavefront_obj.h"
@@ -11,14 +12,26 @@
#include "obj_exporter.hh"
#include "obj_importer.hh"
+using namespace blender::timeit;
+
+static void report_duration(const char *job, const TimePoint &start_time, const char *path)
+{
+ Nanoseconds duration = Clock::now() - start_time;
+ std::cout << "OBJ " << job << " of '" << BLI_path_basename(path) << "' took ";
+ print_duration(duration);
+ std::cout << '\n';
+}
+
void OBJ_export(bContext *C, const OBJExportParams *export_params)
{
- SCOPED_TIMER("OBJ export");
+ TimePoint start_time = Clock::now();
blender::io::obj::exporter_main(C, *export_params);
+ report_duration("export", start_time, export_params->filepath);
}
void OBJ_import(bContext *C, const OBJImportParams *import_params)
{
- SCOPED_TIMER(__func__);
+ TimePoint start_time = Clock::now();
blender::io::obj::importer_main(C, *import_params);
+ report_duration("import", start_time, import_params->filepath);
}
diff --git a/source/blender/io/wavefront_obj/IO_wavefront_obj.h b/source/blender/io/wavefront_obj/IO_wavefront_obj.h
index 6ad96083e37..847b02d3fd1 100644
--- a/source/blender/io/wavefront_obj/IO_wavefront_obj.h
+++ b/source/blender/io/wavefront_obj/IO_wavefront_obj.h
@@ -75,6 +75,7 @@ struct OBJImportParams {
bool import_vertex_groups;
bool validate_meshes;
bool relative_paths;
+ bool clear_selection;
};
/**
diff --git a/source/blender/io/wavefront_obj/exporter/obj_export_file_writer.cc b/source/blender/io/wavefront_obj/exporter/obj_export_file_writer.cc
index 36a9cf1b9ae..53aa80700cc 100644
--- a/source/blender/io/wavefront_obj/exporter/obj_export_file_writer.cc
+++ b/source/blender/io/wavefront_obj/exporter/obj_export_file_writer.cc
@@ -547,20 +547,29 @@ StringRefNull MTLWriter::mtl_file_path() const
return mtl_filepath_;
}
-void MTLWriter::write_bsdf_properties(const MTLMaterial &mtl_material)
+void MTLWriter::write_bsdf_properties(const MTLMaterial &mtl)
{
- fmt_handler_.write<eMTLSyntaxElement::Ns>(mtl_material.Ns);
- fmt_handler_.write<eMTLSyntaxElement::Ka>(
- mtl_material.Ka.x, mtl_material.Ka.y, mtl_material.Ka.z);
- fmt_handler_.write<eMTLSyntaxElement::Kd>(
- mtl_material.Kd.x, mtl_material.Kd.y, mtl_material.Kd.z);
- fmt_handler_.write<eMTLSyntaxElement::Ks>(
- mtl_material.Ks.x, mtl_material.Ks.y, mtl_material.Ks.z);
- fmt_handler_.write<eMTLSyntaxElement::Ke>(
- mtl_material.Ke.x, mtl_material.Ke.y, mtl_material.Ke.z);
- fmt_handler_.write<eMTLSyntaxElement::Ni>(mtl_material.Ni);
- fmt_handler_.write<eMTLSyntaxElement::d>(mtl_material.d);
- fmt_handler_.write<eMTLSyntaxElement::illum>(mtl_material.illum);
+ /* For various material properties, we only capture information
+ * coming from the texture, or the default value of the socket.
+ * When the texture is present, do not emit the default value. */
+ if (!mtl.tex_map_of_type(eMTLSyntaxElement::map_Ns).is_valid()) {
+ fmt_handler_.write<eMTLSyntaxElement::Ns>(mtl.Ns);
+ }
+ fmt_handler_.write<eMTLSyntaxElement::Ka>(mtl.Ka.x, mtl.Ka.y, mtl.Ka.z);
+ if (!mtl.tex_map_of_type(eMTLSyntaxElement::map_Kd).is_valid()) {
+ fmt_handler_.write<eMTLSyntaxElement::Kd>(mtl.Kd.x, mtl.Kd.y, mtl.Kd.z);
+ }
+ if (!mtl.tex_map_of_type(eMTLSyntaxElement::map_Ks).is_valid()) {
+ fmt_handler_.write<eMTLSyntaxElement::Ks>(mtl.Ks.x, mtl.Ks.y, mtl.Ks.z);
+ }
+ if (!mtl.tex_map_of_type(eMTLSyntaxElement::map_Ke).is_valid()) {
+ fmt_handler_.write<eMTLSyntaxElement::Ke>(mtl.Ke.x, mtl.Ke.y, mtl.Ke.z);
+ }
+ fmt_handler_.write<eMTLSyntaxElement::Ni>(mtl.Ni);
+ if (!mtl.tex_map_of_type(eMTLSyntaxElement::map_d).is_valid()) {
+ fmt_handler_.write<eMTLSyntaxElement::d>(mtl.d);
+ }
+ fmt_handler_.write<eMTLSyntaxElement::illum>(mtl.illum);
}
void MTLWriter::write_texture_map(
@@ -583,12 +592,13 @@ void MTLWriter::write_texture_map(
options.append(" -bm ").append(std::to_string(mtl_material.map_Bump_strength));
}
+ std::string path = path_reference(
+ texture_map.value.image_path.c_str(), blen_filedir, dest_dir, path_mode, &copy_set);
+ /* Always emit forward slashes for cross-platform compatibility. */
+ std::replace(path.begin(), path.end(), '\\', '/');
+
#define SYNTAX_DISPATCH(eMTLSyntaxElement) \
if (texture_map.key == eMTLSyntaxElement) { \
- std::string path = path_reference( \
- texture_map.value.image_path.c_str(), blen_filedir, dest_dir, path_mode, &copy_set); \
- /* Always emit forward slashes for cross-platform compatibility. */ \
- std::replace(path.begin(), path.end(), '\\', '/'); \
fmt_handler_.write<eMTLSyntaxElement>(options, path.c_str()); \
return; \
}
@@ -600,6 +610,7 @@ void MTLWriter::write_texture_map(
SYNTAX_DISPATCH(eMTLSyntaxElement::map_refl);
SYNTAX_DISPATCH(eMTLSyntaxElement::map_Ke);
SYNTAX_DISPATCH(eMTLSyntaxElement::map_Bump);
+#undef SYNTAX_DISPATCH
BLI_assert(!"This map type was not written to the file.");
}
@@ -626,7 +637,7 @@ void MTLWriter::write_materials(const char *blen_filepath,
fmt_handler_.write<eMTLSyntaxElement::newmtl>(mtlmat.name);
write_bsdf_properties(mtlmat);
for (const auto &tex : mtlmat.texture_maps.items()) {
- if (tex.value.image_path.empty()) {
+ if (!tex.value.is_valid()) {
continue;
}
write_texture_map(mtlmat, tex, blen_filedir, dest_dir, path_mode, copy_set);
diff --git a/source/blender/io/wavefront_obj/exporter/obj_export_mesh.cc b/source/blender/io/wavefront_obj/exporter/obj_export_mesh.cc
index 9460746630d..9b050af0891 100644
--- a/source/blender/io/wavefront_obj/exporter/obj_export_mesh.cc
+++ b/source/blender/io/wavefront_obj/exporter/obj_export_mesh.cc
@@ -296,7 +296,7 @@ void OBJMesh::store_uv_coords_and_indices()
const float limit[2] = {STD_UV_CONNECT_LIMIT, STD_UV_CONNECT_LIMIT};
UvVertMap *uv_vert_map = BKE_mesh_uv_vert_map_create(
- mpoly, mloop, mloopuv, totpoly, totvert, limit, false, false);
+ mpoly, nullptr, mloop, mloopuv, totpoly, totvert, limit, false, false);
uv_indices_.resize(totpoly);
/* At least total vertices of a mesh will be present in its texture map. So
diff --git a/source/blender/io/wavefront_obj/exporter/obj_export_mtl.hh b/source/blender/io/wavefront_obj/exporter/obj_export_mtl.hh
index 80e3127f69f..f83b3b49bf5 100644
--- a/source/blender/io/wavefront_obj/exporter/obj_export_mtl.hh
+++ b/source/blender/io/wavefront_obj/exporter/obj_export_mtl.hh
@@ -8,8 +8,6 @@
#include "BLI_map.hh"
#include "BLI_math_vec_types.hh"
-#include "BLI_string_ref.hh"
-#include "BLI_vector.hh"
#include "DNA_node_types.h"
#include "obj_export_io.hh"
@@ -25,19 +23,22 @@ template<> struct DefaultHash<io::obj::eMTLSyntaxElement> {
} // namespace blender
namespace blender::io::obj {
-class OBJMesh;
/**
* Generic container for texture node properties.
*/
struct tex_map_XX {
tex_map_XX(StringRef to_socket_id) : dest_socket_id(to_socket_id){};
+ bool is_valid() const
+ {
+ return !image_path.empty();
+ }
- /** Target socket which this texture node connects to. */
+ /* Target socket which this texture node connects to. */
const std::string dest_socket_id;
float3 translation{0.0f};
float3 scale{1.0f};
- /* Only Flat and Smooth projections are supported. */
+ /* Only Flat and Sphere projections are supported. */
int projection_type = SHD_PROJ_FLAT;
std::string image_path;
std::string mtl_dir_path;
@@ -58,16 +59,15 @@ struct MTLMaterial {
texture_maps.add(eMTLSyntaxElement::map_Bump, tex_map_XX("Normal"));
}
- /**
- * Caller must ensure that the given lookup key exists in the Map.
- * \return Texture map corresponding to the given ID.
- */
+ const tex_map_XX &tex_map_of_type(const eMTLSyntaxElement key) const
+ {
+ BLI_assert(texture_maps.contains(key));
+ return texture_maps.lookup(key);
+ }
tex_map_XX &tex_map_of_type(const eMTLSyntaxElement key)
{
- {
- BLI_assert(texture_maps.contains_as(key));
- return texture_maps.lookup_as(key);
- }
+ BLI_assert(texture_maps.contains(key));
+ return texture_maps.lookup(key);
}
std::string name;
diff --git a/source/blender/io/wavefront_obj/importer/obj_import_file_reader.cc b/source/blender/io/wavefront_obj/importer/obj_import_file_reader.cc
index 8594603867f..7069e1185e0 100644
--- a/source/blender/io/wavefront_obj/importer/obj_import_file_reader.cc
+++ b/source/blender/io/wavefront_obj/importer/obj_import_file_reader.cc
@@ -406,8 +406,7 @@ static void use_all_vertices_if_no_faces(Geometry *geom,
all_geometries.begin(), all_geometries.end(), [](const std::unique_ptr<Geometry> &g) {
return g->get_vertex_count() == 0;
})) {
- geom->track_vertex_index(0);
- geom->track_vertex_index(global_vertices.vertices.size() - 1);
+ geom->track_all_vertices(global_vertices.vertices.size());
}
}
}
diff --git a/source/blender/io/wavefront_obj/importer/obj_import_mesh.cc b/source/blender/io/wavefront_obj/importer/obj_import_mesh.cc
index d7b2bc2e67c..e62470588ec 100644
--- a/source/blender/io/wavefront_obj/importer/obj_import_mesh.cc
+++ b/source/blender/io/wavefront_obj/importer/obj_import_mesh.cc
@@ -157,17 +157,17 @@ void MeshFromGeometry::fixup_invalid_faces()
void MeshFromGeometry::create_vertices(Mesh *mesh)
{
- const int tot_verts_object{mesh_geometry_.get_vertex_count()};
- for (int i = 0; i < tot_verts_object; ++i) {
- int vi = mesh_geometry_.vertex_index_min_ + i;
+ int mi = 0;
+ for (int vi : mesh_geometry_.vertices_) {
if (vi < global_vertices_.vertices.size()) {
- copy_v3_v3(mesh->mvert[i].co, global_vertices_.vertices[vi]);
+ copy_v3_v3(mesh->mvert[mi].co, global_vertices_.vertices[vi]);
}
else {
std::cerr << "Vertex index:" << vi
<< " larger than total vertices:" << global_vertices_.vertices.size() << " ."
<< std::endl;
}
+ ++mi;
}
}
@@ -208,7 +208,7 @@ void MeshFromGeometry::create_polys_loops(Mesh *mesh, bool use_vertex_groups)
const PolyCorner &curr_corner = mesh_geometry_.face_corners_[curr_face.start_index_ + idx];
MLoop &mloop = mesh->mloop[tot_loop_idx];
tot_loop_idx++;
- mloop.v = curr_corner.vert_index - mesh_geometry_.vertex_index_min_;
+ mloop.v = mesh_geometry_.global_to_local_vertices_.lookup_default(curr_corner.vert_index, 0);
/* Setup vertex group data, if needed. */
if (!mesh->dvert) {
@@ -240,8 +240,8 @@ void MeshFromGeometry::create_edges(Mesh *mesh)
for (int i = 0; i < tot_edges; ++i) {
const MEdge &src_edge = mesh_geometry_.edges_[i];
MEdge &dst_edge = mesh->medge[i];
- dst_edge.v1 = src_edge.v1 - mesh_geometry_.vertex_index_min_;
- dst_edge.v2 = src_edge.v2 - mesh_geometry_.vertex_index_min_;
+ dst_edge.v1 = mesh_geometry_.global_to_local_vertices_.lookup_default(src_edge.v1, 0);
+ dst_edge.v2 = mesh_geometry_.global_to_local_vertices_.lookup_default(src_edge.v2, 0);
BLI_assert(dst_edge.v1 < total_verts && dst_edge.v2 < total_verts);
dst_edge.flag = ME_LOOSEEDGE;
}
diff --git a/source/blender/io/wavefront_obj/importer/obj_import_mtl.cc b/source/blender/io/wavefront_obj/importer/obj_import_mtl.cc
index 0023d1159c5..02e09a77a5d 100644
--- a/source/blender/io/wavefront_obj/importer/obj_import_mtl.cc
+++ b/source/blender/io/wavefront_obj/importer/obj_import_mtl.cc
@@ -134,6 +134,14 @@ static Image *load_texture_image(Main *bmain,
return image;
}
}
+ /* Try taking just the basename from input path. */
+ std::string base_path{tex_map.mtl_dir_path + BLI_path_basename(tex_map.image_path.c_str())};
+ if (base_path != tex_path) {
+ image = load_image_at_path(bmain, base_path, relative_paths);
+ if (image != nullptr) {
+ return image;
+ }
+ }
image = create_placeholder_image(bmain, tex_path);
return image;
@@ -334,7 +342,7 @@ void ShaderNodetreeWrap::set_bsdf_socket_values(Material *mat)
if (emission_color.x >= 0 && emission_color.y >= 0 && emission_color.z >= 0) {
set_property_of_socket(SOCK_RGBA, "Emission", {emission_color, 3}, bsdf_);
}
- if (mtl_mat_.texture_maps.contains_as(eMTLSyntaxElement::map_Ke)) {
+ if (mtl_mat_.tex_map_of_type(eMTLSyntaxElement::map_Ke).is_valid()) {
set_property_of_socket(SOCK_FLOAT, "Emission Strength", {1.0f}, bsdf_);
}
set_property_of_socket(SOCK_FLOAT, "Specular", {specular}, bsdf_);
@@ -357,7 +365,7 @@ void ShaderNodetreeWrap::add_image_textures(Main *bmain, Material *mat, bool rel
{
for (const Map<const eMTLSyntaxElement, tex_map_XX>::Item texture_map :
mtl_mat_.texture_maps.items()) {
- if (texture_map.value.image_path.empty()) {
+ if (!texture_map.value.is_valid()) {
/* No Image texture node of this map type can be added to this material. */
continue;
}
diff --git a/source/blender/io/wavefront_obj/importer/obj_import_objects.hh b/source/blender/io/wavefront_obj/importer/obj_import_objects.hh
index 9f0079d7c53..f48b6dd55e8 100644
--- a/source/blender/io/wavefront_obj/importer/obj_import_objects.hh
+++ b/source/blender/io/wavefront_obj/importer/obj_import_objects.hh
@@ -9,6 +9,7 @@
#include "BKE_lib_id.h"
#include "BLI_map.hh"
+#include "BLI_math_base.hh"
#include "BLI_math_vec_types.hh"
#include "BLI_vector.hh"
#include "BLI_vector_set.hh"
@@ -92,6 +93,8 @@ struct Geometry {
int vertex_index_min_ = INT_MAX;
int vertex_index_max_ = -1;
+ VectorSet<int> vertices_;
+ Map<int, int> global_to_local_vertices_;
/** Edges written in the file in addition to (or even without polygon) elements. */
Vector<MEdge> edges_;
@@ -105,14 +108,26 @@ struct Geometry {
int get_vertex_count() const
{
- if (vertex_index_max_ < vertex_index_min_)
- return 0;
- return vertex_index_max_ - vertex_index_min_ + 1;
+ return (int)vertices_.size();
}
void track_vertex_index(int index)
{
- vertex_index_min_ = std::min(vertex_index_min_, index);
- vertex_index_max_ = std::max(vertex_index_max_, index);
+ if (vertices_.add(index)) {
+ global_to_local_vertices_.add_new(index, (int)vertices_.size() - 1);
+ }
+ math::min_inplace(vertex_index_min_, index);
+ math::max_inplace(vertex_index_max_, index);
+ }
+ void track_all_vertices(int count)
+ {
+ vertices_.reserve(count);
+ global_to_local_vertices_.reserve(count);
+ for (int i = 0; i < count; ++i) {
+ vertices_.add(i);
+ global_to_local_vertices_.add(i, i);
+ }
+ vertex_index_min_ = 0;
+ vertex_index_max_ = count - 1;
}
};
diff --git a/source/blender/io/wavefront_obj/importer/obj_importer.cc b/source/blender/io/wavefront_obj/importer/obj_importer.cc
index bb32776d2be..5d3f75e7f38 100644
--- a/source/blender/io/wavefront_obj/importer/obj_importer.cc
+++ b/source/blender/io/wavefront_obj/importer/obj_importer.cc
@@ -39,7 +39,6 @@ static void geometry_to_blender_objects(Main *bmain,
Map<std::string, std::unique_ptr<MTLMaterial>> &materials,
Map<std::string, Material *> &created_materials)
{
- BKE_view_layer_base_deselect_all(view_layer);
LayerCollection *lc = BKE_layer_collection_get_active(view_layer);
/* Don't do collection syncs for each object, will do once after the loop. */
@@ -122,6 +121,9 @@ void importer_main(Main *bmain,
mtl_parser.parse_and_store(materials);
}
+ if (import_params.clear_selection) {
+ BKE_view_layer_base_deselect_all(view_layer);
+ }
geometry_to_blender_objects(bmain,
scene,
view_layer,
diff --git a/source/blender/io/wavefront_obj/tests/obj_importer_tests.cc b/source/blender/io/wavefront_obj/tests/obj_importer_tests.cc
index 339f672c3e0..132bb03357f 100644
--- a/source/blender/io/wavefront_obj/tests/obj_importer_tests.cc
+++ b/source/blender/io/wavefront_obj/tests/obj_importer_tests.cc
@@ -62,6 +62,7 @@ class obj_importer_test : public BlendfileLoadingBaseTest {
params.validate_meshes = true;
params.import_vertex_groups = false;
params.relative_paths = true;
+ params.clear_selection = true;
std::string obj_path = blender::tests::flags_test_asset_dir() + "/io_tests/obj/" + path;
strncpy(params.filepath, obj_path.c_str(), FILE_MAX - 1);
@@ -153,7 +154,7 @@ TEST_F(obj_importer_test, import_cube)
12,
6,
24,
- float3(-1, -1, 1),
+ float3(1, -1, 1),
float3(1, -1, -1),
float3(-0.57735f, 0.57735f, -0.57735f)},
};
@@ -171,19 +172,19 @@ TEST_F(obj_importer_test, import_cube_o_after_verts)
12,
6,
24,
- float3(-1, -1, 1),
+ float3(1, -1, 1),
float3(1, -1, -1),
float3(0, 0, 1),
},
{
"OBSparseTri",
OB_MESH,
- 6,
+ 3,
3,
1,
3,
- float3(1, -1, 1),
float3(-2, -2, 2),
+ float3(-1, -1, -1),
float3(-0.2357f, 0.9428f, 0.2357f),
},
};
@@ -200,8 +201,8 @@ TEST_F(obj_importer_test, import_suzanne_all_data)
1005,
500,
1968,
- float3(-0.4375f, 0.164062f, 0.765625f),
- float3(0.4375f, 0.164062f, 0.765625f),
+ float3(-0.5f, 0.09375f, 0.6875f),
+ float3(0.546875f, 0.054688f, 0.578125f),
float3(-0.6040f, -0.5102f, 0.6122f),
float2(0.692094f, 0.40191f)},
};
@@ -306,7 +307,7 @@ TEST_F(obj_importer_test, import_materials)
{
Expectation expect[] = {
{"OBCube", OB_MESH, 8, 12, 6, 24, float3(1, 1, -1), float3(-1, 1, 1)},
- {"OBmaterials", OB_MESH, 8, 12, 6, 24, float3(-1, -1, 1), float3(1, -1, -1)},
+ {"OBmaterials", OB_MESH, 8, 12, 6, 24, float3(1, -1, 1), float3(1, -1, -1)},
};
import_and_check("materials.obj", expect, std::size(expect), 4, 8);
}
@@ -322,7 +323,17 @@ TEST_F(obj_importer_test, import_cubes_with_textures_rel)
6,
24,
float3(1, 1, -1),
- float3(-1, -1, 1),
+ float3(1, -1, -1),
+ float3(0, 1, 0),
+ float2(0.9935f, 0.0020f)},
+ {"OBCubeTexMul",
+ OB_MESH,
+ 8,
+ 12,
+ 6,
+ 24,
+ float3(4, -2, -1),
+ float3(4, -4, -1),
float3(0, 1, 0),
float2(0.9935f, 0.0020f)},
{"OBCubeTiledTex",
@@ -332,7 +343,7 @@ TEST_F(obj_importer_test, import_cubes_with_textures_rel)
6,
24,
float3(4, 1, -1),
- float3(2, -1, 1),
+ float3(4, -1, -1),
float3(0, 1, 0),
float2(0.9935f, 0.0020f)},
{"OBCubeTiledTexFromAnotherFolder",
@@ -342,11 +353,11 @@ TEST_F(obj_importer_test, import_cubes_with_textures_rel)
6,
24,
float3(7, 1, -1),
- float3(5, -1, 1),
+ float3(7, -1, -1),
float3(0, 1, 0),
float2(0.9935f, 0.0020f)},
};
- import_and_check("cubes_with_textures_rel.obj", expect, std::size(expect), 3, 4);
+ import_and_check("cubes_with_textures_rel.obj", expect, std::size(expect), 4, 4);
}
TEST_F(obj_importer_test, import_faces_invalid_or_with_holes)
@@ -433,15 +444,15 @@ TEST_F(obj_importer_test, import_all_objects)
{"OBCube", OB_MESH, 8, 12, 6, 24, float3(1, 1, -1), float3(-1, 1, 1)},
/* .obj file has empty EmptyText and EmptyMesh objects; these are ignored and skipped */
{"OBBezierCurve", OB_MESH, 13, 12, 0, 0, float3(-1, -2, 0), float3(1, -2, 0)},
- {"OBBlankCube", OB_MESH, 8, 13, 7, 26, float3(1, 1, -1), float3(-1, 1, 1), float3(0, 0, 1)},
+ {"OBBlankCube", OB_MESH, 8, 13, 7, 26, float3(1, 1, 1), float3(-1, 1, -1), float3(0, 0, 1)},
{"OBMaterialCube",
OB_MESH,
8,
13,
7,
26,
- float3(28, 1, -1),
- float3(26, 1, 1),
+ float3(26, -1, -1),
+ float3(28, -1, -1),
float3(-1, 0, 0)},
{"OBNurbsCircle",
OB_MESH,
@@ -451,15 +462,15 @@ TEST_F(obj_importer_test, import_all_objects)
0,
float3(3.292893f, -2.707107f, 0),
float3(3.369084f, -2.77607f, 0)},
- {"OBNurbsCircle.001", OB_MESH, 4, 4, 0, 0, float3(2, -3, 0), float3(3, -2, 0)},
+ {"OBNurbsCircle.001", OB_MESH, 4, 4, 0, 0, float3(3, -2, 0), float3(2, -1, 0)},
{"OBParticleCube",
OB_MESH,
8,
13,
7,
26,
- float3(22, 1, -1),
- float3(20, 1, 1),
+ float3(22, 1, 1),
+ float3(20, 1, -1),
float3(0, 0, 1)},
{"OBShapeKeyCube",
OB_MESH,
@@ -467,8 +478,8 @@ TEST_F(obj_importer_test, import_all_objects)
13,
7,
26,
- float3(19, 1, -1),
- float3(17, 1, 1),
+ float3(19, 1, 2),
+ float3(17, 1, -1),
float3(-0.4082f, -0.4082f, 0.8165f)},
{"OBSmoothCube",
OB_MESH,
@@ -476,8 +487,8 @@ TEST_F(obj_importer_test, import_all_objects)
13,
7,
26,
- float3(4, 1, -1),
- float3(2, 1, 1),
+ float3(4, 1, 1),
+ float3(2, 1, -1),
float3(0.5774f, 0.5773f, 0.5774f)},
{"OBSurface",
OB_MESH,
@@ -485,7 +496,7 @@ TEST_F(obj_importer_test, import_all_objects)
480,
224,
896,
- float3(7.292893f, -2.707107f, -1),
+ float3(7.292893f, -2.707107f, -0.714285f),
float3(7.525872f, -2.883338f, 1),
float3(-0.7071f, -0.7071f, 0),
float2(0, 0.142857f)},
@@ -495,7 +506,7 @@ TEST_F(obj_importer_test, import_all_objects)
480,
225,
900,
- float3(12.5f, -2.5f, 0.694444f),
+ float3(12.56667f, -2.5f, 0.72037f),
float3(13.5f, -1.5f, 0.694444f),
float3(-0.3246f, -0.3531f, 0.8775f),
float2(0, 0.066667f)},
@@ -516,7 +527,7 @@ TEST_F(obj_importer_test, import_all_objects)
1024,
4096,
float3(5.34467f, -2.65533f, -0.176777f),
- float3(5.232792f, -2.411795f, -0.220835f),
+ float3(5.158205f, -2.234695f, -0.220835f),
float3(-0.5042f, -0.5042f, -0.7011f),
float2(0, 1)},
{"OBTaperCube",
@@ -525,8 +536,8 @@ TEST_F(obj_importer_test, import_all_objects)
208,
104,
416,
- float3(24.444445f, 0.502543f, -0.753814f),
- float3(23.790743f, 0.460522f, -0.766546f),
+ float3(24.316156f, 0.345556f, 0.796778f),
+ float3(23.551804f, 0.389113f, -0.639607f),
float3(-0.0546f, 0.1716f, 0.9837f)},
{"OBText",
OB_MESH,
@@ -534,8 +545,8 @@ TEST_F(obj_importer_test, import_all_objects)
345,
171,
513,
- float3(1.75f, -9.458f, 0),
- float3(0.587f, -9.406f, 0),
+ float3(1.583f, -9.621f, 0),
+ float3(0.351f, -10.0f, 0),
float3(0, 0, 1),
float2(0.017544f, 0)},
{"OBUVCube",
@@ -544,8 +555,8 @@ TEST_F(obj_importer_test, import_all_objects)
13,
7,
26,
- float3(7, 1, -1),
- float3(5, 1, 1),
+ float3(7, 1, 1),
+ float3(5, 1, -1),
float3(0, 0, 1),
float2(0.654526f, 0.579873f)},
{"OBUVImageCube",
@@ -554,8 +565,8 @@ TEST_F(obj_importer_test, import_all_objects)
13,
7,
26,
- float3(10, 1, -1),
- float3(8, 1, 1),
+ float3(10, 1, 1),
+ float3(8, 1, -1),
float3(0, 0, 1),
float2(0.654526f, 0.579873f)},
{"OBVColCube",
@@ -564,8 +575,8 @@ TEST_F(obj_importer_test, import_all_objects)
13,
7,
26,
- float3(13, 1, -1),
- float3(11, 1, 1),
+ float3(13, 1, 1),
+ float3(11, 1, -1),
float3(0, 0, 1),
float2(0, 0),
float4(0.0f, 0.002125f, 1.0f, 1.0f)},
@@ -575,8 +586,8 @@ TEST_F(obj_importer_test, import_all_objects)
13,
7,
26,
- float3(16, 1, -1),
- float3(14, 1, 1),
+ float3(16, 1, 1),
+ float3(14, 1, -1),
float3(0, 0, 1)},
};
import_and_check("all_objects.obj", expect, std::size(expect), 7);
@@ -593,7 +604,7 @@ TEST_F(obj_importer_test, import_cubes_vertex_colors)
6,
24,
float3(1.0f, 1.0f, -3.812445f),
- float3(-1.0f, -1.0f, -1.812445f),
+ float3(1.0f, -1.0f, -3.812445f),
float3(0, 0, 0),
float2(0, 0),
float4(0.89627f, 0.036889f, 0.47932f, 1.0f)},
@@ -604,7 +615,7 @@ TEST_F(obj_importer_test, import_cubes_vertex_colors)
6,
24,
float3(3.481967f, 1.0f, -3.812445f),
- float3(1.481967f, -1.0f, -1.812445f),
+ float3(3.481967f, -1.0f, -3.812445f),
float3(0, 0, 0),
float2(0, 0),
float4(1.564582f, 0.039217f, 0.664309f, 1.0f)},
@@ -615,7 +626,7 @@ TEST_F(obj_importer_test, import_cubes_vertex_colors)
6,
24,
float3(-4.725068f, -1.0f, 1.0f),
- float3(-2.725068f, 1.0f, -1.0f),
+ float3(-2.725068f, -1.0f, 1.0f),
float3(0, 0, 0),
float2(0, 0),
float4(0.270498f, 0.47932f, 0.262251f, 1.0f)},
@@ -626,7 +637,7 @@ TEST_F(obj_importer_test, import_cubes_vertex_colors)
6,
24,
float3(-4.550208f, -1.0f, -1.918042f),
- float3(-2.550208f, 1.0f, -3.918042f)},
+ float3(-2.550208f, -1.0f, -1.918042f)},
{"OBCubeVertexByte",
OB_MESH,
8,
@@ -634,7 +645,7 @@ TEST_F(obj_importer_test, import_cubes_vertex_colors)
6,
24,
float3(1.0f, 1.0f, -1.0f),
- float3(-1.0f, -1.0f, 1.0f),
+ float3(1.0f, -1.0f, -1.0f),
float3(0, 0, 0),
float2(0, 0),
float4(0.846873f, 0.027321f, 0.982123f, 1.0f)},
@@ -645,7 +656,7 @@ TEST_F(obj_importer_test, import_cubes_vertex_colors)
6,
24,
float3(3.392028f, 1.0f, -1.0f),
- float3(1.392028f, -1.0f, 1.0f),
+ float3(3.392028f, -1.0f, -1.0f),
float3(0, 0, 0),
float2(0, 0),
float4(49.99467f, 0.027321f, 0.982123f, 1.0f)},
@@ -664,7 +675,7 @@ TEST_F(obj_importer_test, import_cubes_vertex_colors_mrgb)
6,
24,
float3(4, 1, -1),
- float3(2, -1, 1),
+ float3(4, -1, -1),
float3(0, 0, 0),
float2(0, 0),
float4(0.8714f, 0.6308f, 0.5271f, 1.0f)},
@@ -675,7 +686,7 @@ TEST_F(obj_importer_test, import_cubes_vertex_colors_mrgb)
6,
24,
float3(1, 1, -1),
- float3(-1, -1, 1),
+ float3(1, -1, -1),
float3(0, 0, 0),
float2(0, 0),
float4(0.6038f, 0.3185f, 0.1329f, 1.0f)},
diff --git a/source/blender/makesdna/DNA_meshdata_types.h b/source/blender/makesdna/DNA_meshdata_types.h
index c8081c49ca4..209ffd3342d 100644
--- a/source/blender/makesdna/DNA_meshdata_types.h
+++ b/source/blender/makesdna/DNA_meshdata_types.h
@@ -34,10 +34,13 @@ typedef struct MVert {
} MVert;
/** #MVert.flag */
+
+#ifdef DNA_DEPRECATED_ALLOW
enum {
/* SELECT = (1 << 0), */
ME_HIDE = (1 << 4),
};
+#endif
/**
* Mesh Edges.
@@ -359,7 +362,7 @@ typedef struct MDisps {
/**
* Used for hiding parts of a multires mesh.
- * Essentially the multires equivalent of #MVert.flag's ME_HIDE bit.
+ * Essentially the multires equivalent of the mesh ".hide_vert" boolean layer.
*
* \note This is a bitmap, keep in sync with type used in BLI_bitmap.h
*/
diff --git a/source/blender/makesdna/DNA_modifier_types.h b/source/blender/makesdna/DNA_modifier_types.h
index f148116eba8..787f52f9891 100644
--- a/source/blender/makesdna/DNA_modifier_types.h
+++ b/source/blender/makesdna/DNA_modifier_types.h
@@ -2217,7 +2217,7 @@ typedef struct SurfaceDeformModifierData {
SDefVert *verts;
void *_pad1;
float falloff;
- /* Number of of vertices on the deformed mesh upon the bind process. */
+ /* Number of vertices on the deformed mesh upon the bind process. */
unsigned int mesh_verts_num;
/* Number of vertices in the `verts` array of this modifier. */
unsigned int bind_verts_num;
diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h
index 76d8207eead..b9161e918c0 100644
--- a/source/blender/makesdna/DNA_node_types.h
+++ b/source/blender/makesdna/DNA_node_types.h
@@ -632,12 +632,12 @@ typedef struct bNodeSocketValueMaterial {
/* Data structs, for `node->storage`. */
-enum {
+typedef enum CMPNodeMaskType {
CMP_NODE_MASKTYPE_ADD = 0,
CMP_NODE_MASKTYPE_SUBTRACT = 1,
CMP_NODE_MASKTYPE_MULTIPLY = 2,
CMP_NODE_MASKTYPE_NOT = 3,
-};
+} CMPNodeMaskType;
enum {
CMP_NODE_DILATEERODE_STEP = 0,
@@ -1838,6 +1838,49 @@ enum {
/* viewer and composite output. */
#define CMP_NODE_OUTPUT_IGNORE_ALPHA 1
+/** Split Viewer Node. Stored in `custom2`. */
+typedef enum CMPNodeSplitViewerAxis {
+ CMP_NODE_SPLIT_VIEWER_HORIZONTAL = 0,
+ CMP_NODE_SPLIT_VIEWER_VERTICAL = 1,
+} CMPNodeSplitViewerAxis;
+
+/** Color Balance Node. Stored in `custom1`. */
+typedef enum CMPNodeColorBalanceMethod {
+ CMP_NODE_COLOR_BALANCE_LGG = 0,
+ CMP_NODE_COLOR_BALANCE_ASC_CDL = 1,
+} CMPNodeColorBalanceMethod;
+
+/** Alpha Convert Node. Stored in `custom1`. */
+typedef enum CMPNodeAlphaConvertMode {
+ CMP_NODE_ALPHA_CONVERT_PREMULTIPLY = 0,
+ CMP_NODE_ALPHA_CONVERT_UNPREMULTIPLY = 1,
+} CMPNodeAlphaConvertMode;
+
+/** Distance Matte Node. Stored in #NodeChroma.channel. */
+typedef enum CMPNodeDistanceMatteColorSpace {
+ CMP_NODE_DISTANCE_MATTE_COLOR_SPACE_YCCA = 0,
+ CMP_NODE_DISTANCE_MATTE_COLOR_SPACE_RGBA = 1,
+} CMPNodeDistanceMatteColorSpace;
+
+/** Color Spill Node. Stored in `custom2`. */
+typedef enum CMPNodeColorSpillLimitAlgorithm {
+ CMP_NODE_COLOR_SPILL_LIMIT_ALGORITHM_SINGLE = 0,
+ CMP_NODE_COLOR_SPILL_LIMIT_ALGORITHM_AVERAGE = 1,
+} CMPNodeColorSpillLimitAlgorithm;
+
+/** Channel Matte Node. Stored in #NodeChroma.algorithm. */
+typedef enum CMPNodeChannelMatteLimitAlgorithm {
+ CMP_NODE_CHANNEL_MATTE_LIMIT_ALGORITHM_SINGLE = 0,
+ CMP_NODE_CHANNEL_MATTE_LIMIT_ALGORITHM_MAX = 1,
+} CMPNodeChannelMatteLimitAlgorithm;
+
+/* Flip Node. Stored in custom1. */
+typedef enum CMPNodeFlipMode {
+ CMP_NODE_FLIP_X = 0,
+ CMP_NODE_FLIP_Y = 1,
+ CMP_NODE_FLIP_X_Y = 2,
+} CMPNodeFlipMode;
+
/* Plane track deform node. */
enum {
diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h
index f8fcd78d63b..cfb077748ef 100644
--- a/source/blender/makesdna/DNA_scene_types.h
+++ b/source/blender/makesdna/DNA_scene_types.h
@@ -9,7 +9,7 @@
#include "DNA_defs.h"
-/* XXX(campbell): temp feature. */
+/* XXX(@campbellbarton): temp feature. */
#define DURIAN_CAMERA_SWITCH
/* check for cyclic set-scene,
diff --git a/source/blender/makesdna/DNA_userdef_types.h b/source/blender/makesdna/DNA_userdef_types.h
index 74fb1c3ac96..ac553c2655f 100644
--- a/source/blender/makesdna/DNA_userdef_types.h
+++ b/source/blender/makesdna/DNA_userdef_types.h
@@ -653,6 +653,8 @@ typedef struct UserDef_Experimental {
char enable_eevee_next;
char use_sculpt_texture_paint;
char use_draw_manager_acquire_lock;
+ char use_realtime_compositor;
+ char _pad[7];
/** `makesdna` does not allow empty structs. */
} UserDef_Experimental;
diff --git a/source/blender/makesdna/DNA_view3d_types.h b/source/blender/makesdna/DNA_view3d_types.h
index 8554d070dc3..0d281032b7e 100644
--- a/source/blender/makesdna/DNA_view3d_types.h
+++ b/source/blender/makesdna/DNA_view3d_types.h
@@ -486,6 +486,7 @@ enum {
V3D_SHADING_SCENE_LIGHTS_RENDER = (1 << 12),
V3D_SHADING_SCENE_WORLD_RENDER = (1 << 13),
V3D_SHADING_STUDIOLIGHT_VIEW_ROTATION = (1 << 14),
+ V3D_SHADING_COMPOSITOR = (1 << 15),
};
#define V3D_USES_SCENE_LIGHTS(v3d) \
diff --git a/source/blender/makesrna/intern/rna_ID.c b/source/blender/makesrna/intern/rna_ID.c
index 4b8f8ca7b4b..c59e45e1b01 100644
--- a/source/blender/makesrna/intern/rna_ID.c
+++ b/source/blender/makesrna/intern/rna_ID.c
@@ -234,6 +234,10 @@ const struct IDFilterEnumPropertyItem rna_enum_id_type_filter_items[] = {
# include "WM_api.h"
+# ifdef WITH_PYTHON
+# include "BPY_extern.h"
+# endif
+
void rna_ID_override_library_property_operation_refname_get(PointerRNA *ptr, char *value)
{
IDOverrideLibraryPropertyOperation *opop = ptr->data;
@@ -695,7 +699,16 @@ static ID *rna_ID_override_create(ID *id, Main *bmain, bool remap_local_usages)
BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, true);
}
- ID *local_id = BKE_lib_override_library_create_from_id(bmain, id, remap_local_usages);
+ ID *local_id = NULL;
+# ifdef WITH_PYTHON
+ BPy_BEGIN_ALLOW_THREADS;
+# endif
+
+ local_id = BKE_lib_override_library_create_from_id(bmain, id, remap_local_usages);
+
+# ifdef WITH_PYTHON
+ BPy_END_ALLOW_THREADS;
+# endif
if (remap_local_usages) {
BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false);
@@ -717,9 +730,18 @@ static ID *rna_ID_override_hierarchy_create(
BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false);
ID *id_root_override = NULL;
+
+# ifdef WITH_PYTHON
+ BPy_BEGIN_ALLOW_THREADS;
+# endif
+
BKE_lib_override_library_create(
bmain, scene, view_layer, NULL, id, id, id_instance_hint, &id_root_override, false);
+# ifdef WITH_PYTHON
+ BPy_END_ALLOW_THREADS;
+# endif
+
WM_main_add_notifier(NC_ID | NA_ADDED, NULL);
WM_main_add_notifier(NC_WM | ND_LIB_OVERRIDE_CHANGED, NULL);
@@ -2158,7 +2180,7 @@ static void rna_def_ID(BlenderRNA *brna)
func = RNA_def_function(srna, "animation_data_clear", "rna_ID_animation_data_free");
RNA_def_function_flag(func, FUNC_USE_MAIN);
- RNA_def_function_ui_description(func, "Clear animation on this this ID");
+ RNA_def_function_ui_description(func, "Clear animation on this ID");
func = RNA_def_function(srna, "update_tag", "rna_ID_update_tag");
RNA_def_function_flag(func, FUNC_USE_MAIN | FUNC_USE_REPORTS);
diff --git a/source/blender/makesrna/intern/rna_access.c b/source/blender/makesrna/intern/rna_access.c
index 04707c01d6b..ada73157026 100644
--- a/source/blender/makesrna/intern/rna_access.c
+++ b/source/blender/makesrna/intern/rna_access.c
@@ -2073,7 +2073,7 @@ static void rna_property_update(
}
#if 1
- /* TODO(campbell): Should eventually be replaced entirely by message bus (below)
+ /* TODO(@campbellbarton): Should eventually be replaced entirely by message bus (below)
* for now keep since COW, bugs are hard to track when we have other missing updates. */
if (prop->noteflag) {
WM_main_add_notifier(prop->noteflag, ptr->owner_id);
diff --git a/source/blender/makesrna/intern/rna_action.c b/source/blender/makesrna/intern/rna_action.c
index bcfb646ca19..14f4a82c62b 100644
--- a/source/blender/makesrna/intern/rna_action.c
+++ b/source/blender/makesrna/intern/rna_action.c
@@ -177,7 +177,7 @@ static void rna_Action_fcurve_clear(bAction *act)
static TimeMarker *rna_Action_pose_markers_new(bAction *act, const char name[])
{
TimeMarker *marker = MEM_callocN(sizeof(TimeMarker), "TimeMarker");
- marker->flag = 1;
+ marker->flag = SELECT;
marker->frame = 1;
BLI_strncpy_utf8(marker->name, name, sizeof(marker->name));
BLI_addtail(&act->markers, marker);
diff --git a/source/blender/makesrna/intern/rna_camera.c b/source/blender/makesrna/intern/rna_camera.c
index 99f8c263da6..988e65b4ba8 100644
--- a/source/blender/makesrna/intern/rna_camera.c
+++ b/source/blender/makesrna/intern/rna_camera.c
@@ -695,14 +695,12 @@ void RNA_def_camera(BlenderRNA *brna)
prop = RNA_def_property(srna, "shift_x", PROP_FLOAT, PROP_NONE);
RNA_def_property_float_sdna(prop, NULL, "shiftx");
- RNA_def_property_range(prop, -10.0f, 10.0f);
RNA_def_property_ui_range(prop, -2.0, 2.0, 1, 3);
RNA_def_property_ui_text(prop, "Shift X", "Camera horizontal shift");
RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_Camera_update");
prop = RNA_def_property(srna, "shift_y", PROP_FLOAT, PROP_NONE);
RNA_def_property_float_sdna(prop, NULL, "shifty");
- RNA_def_property_range(prop, -10.0f, 10.0f);
RNA_def_property_ui_range(prop, -2.0, 2.0, 1, 3);
RNA_def_property_ui_text(prop, "Shift Y", "Camera vertical shift");
RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_Camera_update");
diff --git a/source/blender/makesrna/intern/rna_curves.c b/source/blender/makesrna/intern/rna_curves.c
index cb8b36f41d2..17290d1c582 100644
--- a/source/blender/makesrna/intern/rna_curves.c
+++ b/source/blender/makesrna/intern/rna_curves.c
@@ -195,7 +195,7 @@ static void rna_def_curves_point(BlenderRNA *brna)
PropertyRNA *prop;
srna = RNA_def_struct(brna, "CurvePoint", NULL);
- RNA_def_struct_ui_text(srna, "Curve Point", "Curve curve control point");
+ RNA_def_struct_ui_text(srna, "Curve Point", "Curve control point");
RNA_def_struct_path_func(srna, "rna_CurvePoint_path");
prop = RNA_def_property(srna, "position", PROP_FLOAT, PROP_TRANSLATION);
diff --git a/source/blender/makesrna/intern/rna_mesh.c b/source/blender/makesrna/intern/rna_mesh.c
index 22a43f1df8f..63d8d572c5f 100644
--- a/source/blender/makesrna/intern/rna_mesh.c
+++ b/source/blender/makesrna/intern/rna_mesh.c
@@ -352,19 +352,93 @@ static void rna_Mesh_update_positions_tag(Main *bmain, Scene *scene, PointerRNA
/** \name Property get/set Callbacks
* \{ */
+<<<<<<< HEAD
static int rna_MeshVertex_index_get(PointerRNA *ptr);
static int rna_MeshEdge_index_get(PointerRNA *ptr);
+=======
+static int rna_MeshVertex_index_get(PointerRNA *ptr)
+{
+ const Mesh *mesh = rna_mesh(ptr);
+ const MVert *vert = (MVert *)ptr->data;
+ const int index = (int)(vert - mesh->mvert);
+ BLI_assert(index >= 0);
+ BLI_assert(index < mesh->totvert);
+ return index;
+}
+
+static int rna_MeshEdge_index_get(PointerRNA *ptr)
+{
+ const Mesh *mesh = rna_mesh(ptr);
+ const MEdge *edge = (MEdge *)ptr->data;
+ const int index = (int)(edge - mesh->medge);
+ BLI_assert(index >= 0);
+ BLI_assert(index < mesh->totedge);
+ return index;
+}
+
+static int rna_MeshPolygon_index_get(PointerRNA *ptr)
+{
+ const Mesh *mesh = rna_mesh(ptr);
+ const MPoly *mpoly = (MPoly *)ptr->data;
+ const int index = (int)(mpoly - mesh->mpoly);
+ BLI_assert(index >= 0);
+ BLI_assert(index < mesh->totpoly);
+ return index;
+}
+
+static int rna_MeshLoop_index_get(PointerRNA *ptr)
+{
+ const Mesh *mesh = rna_mesh(ptr);
+ const MLoop *mloop = (MLoop *)ptr->data;
+ const int index = (int)(mloop - mesh->mloop);
+ BLI_assert(index >= 0);
+ BLI_assert(index < mesh->totloop);
+ return index;
+}
+
+static int rna_MeshLoopTriangle_index_get(PointerRNA *ptr)
+{
+ const Mesh *mesh = rna_mesh(ptr);
+ const MLoopTri *ltri = (MLoopTri *)ptr->data;
+ const int index = (int)(ltri - mesh->runtime.looptris.array);
+ BLI_assert(index >= 0);
+ BLI_assert(index < mesh->runtime.looptris.len);
+ return index;
+}
+>>>>>>> master
static void rna_MeshVertex_normal_get(PointerRNA *ptr, float *value)
{
Mesh *mesh = rna_mesh(ptr);
const float(*vert_normals)[3] = BKE_mesh_vertex_normals_ensure(mesh);
+ const int index = rna_MeshVertex_index_get(ptr);
+ copy_v3_v3(value, vert_normals[index]);
+}
- const int index = (MVert *)ptr->data - mesh->mvert;
- BLI_assert(index >= 0);
- BLI_assert(index < mesh->totvert);
+static bool rna_MeshVertex_hide_get(PointerRNA *ptr)
+{
+ const Mesh *mesh = rna_mesh(ptr);
+ const bool *hide_vert = (const bool *)CustomData_get_layer_named(
+ &mesh->vdata, CD_PROP_BOOL, ".hide_vert");
+ const int index = rna_MeshVertex_index_get(ptr);
+ return hide_vert == NULL ? false : hide_vert[index];
+}
- copy_v3_v3(value, vert_normals[index]);
+static void rna_MeshVertex_hide_set(PointerRNA *ptr, bool value)
+{
+ Mesh *mesh = rna_mesh(ptr);
+ bool *hide_vert = (bool *)CustomData_duplicate_referenced_layer_named(
+ &mesh->vdata, CD_PROP_BOOL, ".hide_vert", mesh->totvert);
+ if (!hide_vert) {
+ if (!value) {
+ /* Skip adding layer if it doesn't exist already anyway and we're not hiding an element. */
+ return;
+ }
+ hide_vert = (bool *)CustomData_add_layer_named(
+ &mesh->vdata, CD_PROP_BOOL, CD_CALLOC, NULL, mesh->totvert, ".hide_vert");
+ }
+ const int index = rna_MeshVertex_index_get(ptr);
+ hide_vert[index] = value;
}
static float rna_MeshVertex_bevel_weight_get(PointerRNA *ptr)
@@ -483,6 +557,32 @@ static void rna_MeshPolygon_normal_get(PointerRNA *ptr, float *values)
BKE_mesh_calc_poly_normal(mp, me->mloop + mp->loopstart, me->mvert, values);
}
+static bool rna_MeshPolygon_hide_get(PointerRNA *ptr)
+{
+ const Mesh *mesh = rna_mesh(ptr);
+ const bool *hide_poly = (const bool *)CustomData_get_layer_named(
+ &mesh->pdata, CD_PROP_BOOL, ".hide_poly");
+ const int index = rna_MeshPolygon_index_get(ptr);
+ return hide_poly == NULL ? false : hide_poly[index];
+}
+
+static void rna_MeshPolygon_hide_set(PointerRNA *ptr, bool value)
+{
+ Mesh *mesh = rna_mesh(ptr);
+ bool *hide_poly = (bool *)CustomData_duplicate_referenced_layer_named(
+ &mesh->pdata, CD_PROP_BOOL, ".hide_poly", mesh->totpoly);
+ if (!hide_poly) {
+ if (!value) {
+ /* Skip adding layer if it doesn't exist already anyway and we're not hiding an element. */
+ return;
+ }
+ hide_poly = (bool *)CustomData_add_layer_named(
+ &mesh->pdata, CD_PROP_BOOL, CD_CALLOC, NULL, mesh->totpoly, ".hide_poly");
+ }
+ const int index = rna_MeshPolygon_index_get(ptr);
+ hide_poly[index] = value;
+}
+
static void rna_MeshPolygon_center_get(PointerRNA *ptr, float *values)
{
Mesh *me = rna_mesh(ptr);
@@ -1193,55 +1293,46 @@ static void rna_MeshPoly_material_index_range(
}
# endif
-static int rna_MeshVertex_index_get(PointerRNA *ptr)
+static bool rna_MeshEdge_hide_get(PointerRNA *ptr)
{
- Mesh *me = rna_mesh(ptr);
- MVert *vert = (MVert *)ptr->data;
- return (int)(vert - me->mvert);
+ const Mesh *mesh = rna_mesh(ptr);
+ const bool *hide_edge = (const bool *)CustomData_get_layer_named(
+ &mesh->edata, CD_PROP_BOOL, ".hide_edge");
+ const int index = rna_MeshEdge_index_get(ptr);
+ return hide_edge == NULL ? false : hide_edge[index];
}
-static int rna_MeshEdge_index_get(PointerRNA *ptr)
+static void rna_MeshEdge_hide_set(PointerRNA *ptr, bool value)
{
- Mesh *me = rna_mesh(ptr);
- MEdge *edge = (MEdge *)ptr->data;
- return (int)(edge - me->medge);
-}
-
-static int rna_MeshLoopTriangle_index_get(PointerRNA *ptr)
-{
- Mesh *me = rna_mesh(ptr);
- MLoopTri *ltri = (MLoopTri *)ptr->data;
- return (int)(ltri - me->runtime.looptris.array);
+ Mesh *mesh = rna_mesh(ptr);
+ bool *hide_edge = (bool *)CustomData_duplicate_referenced_layer_named(
+ &mesh->edata, CD_PROP_BOOL, ".hide_edge", mesh->totedge);
+ if (!hide_edge) {
+ if (!value) {
+ /* Skip adding layer if it doesn't exist already anyway and we're not hiding an element. */
+ return;
+ }
+ hide_edge = (bool *)CustomData_add_layer_named(
+ &mesh->edata, CD_PROP_BOOL, CD_CALLOC, NULL, mesh->totedge, ".hide_edge");
+ }
+ const int index = rna_MeshEdge_index_get(ptr);
+ hide_edge[index] = value;
}
static int rna_MeshLoopTriangle_material_index_get(PointerRNA *ptr)
{
- Mesh *me = rna_mesh(ptr);
- MLoopTri *ltri = (MLoopTri *)ptr->data;
+ const Mesh *me = rna_mesh(ptr);
+ const MLoopTri *ltri = (MLoopTri *)ptr->data;
return me->mpoly[ltri->poly].mat_nr;
}
static bool rna_MeshLoopTriangle_use_smooth_get(PointerRNA *ptr)
{
- Mesh *me = rna_mesh(ptr);
- MLoopTri *ltri = (MLoopTri *)ptr->data;
+ const Mesh *me = rna_mesh(ptr);
+ const MLoopTri *ltri = (MLoopTri *)ptr->data;
return me->mpoly[ltri->poly].flag & ME_SMOOTH;
}
-static int rna_MeshPolygon_index_get(PointerRNA *ptr)
-{
- Mesh *me = rna_mesh(ptr);
- MPoly *mpoly = (MPoly *)ptr->data;
- return (int)(mpoly - me->mpoly);
-}
-
-static int rna_MeshLoop_index_get(PointerRNA *ptr)
-{
- Mesh *me = rna_mesh(ptr);
- MLoop *mloop = (MLoop *)ptr->data;
- return (int)(mloop - me->mloop);
-}
-
/* path construction */
static char *rna_VertexGroupElement_path(const PointerRNA *ptr)
@@ -1849,8 +1940,8 @@ static void rna_def_mvert(BlenderRNA *brna)
RNA_def_property_update(prop, 0, "rna_Mesh_update_select");
prop = RNA_def_property(srna, "hide", PROP_BOOLEAN, PROP_NONE);
- RNA_def_property_boolean_sdna(prop, NULL, "flag", ME_HIDE);
RNA_def_property_ui_text(prop, "Hide", "");
+ RNA_def_property_boolean_funcs(prop, "rna_MeshVertex_hide_get", "rna_MeshVertex_hide_set");
RNA_def_property_update(prop, 0, "rna_Mesh_update_select");
prop = RNA_def_property(srna, "bevel_weight", PROP_FLOAT, PROP_NONE);
@@ -1925,8 +2016,8 @@ static void rna_def_medge(BlenderRNA *brna)
RNA_def_property_update(prop, 0, "rna_Mesh_update_select");
prop = RNA_def_property(srna, "hide", PROP_BOOLEAN, PROP_NONE);
- RNA_def_property_boolean_sdna(prop, NULL, "flag", ME_HIDE);
RNA_def_property_ui_text(prop, "Hide", "");
+ RNA_def_property_boolean_funcs(prop, "rna_MeshEdge_hide_get", "rna_MeshEdge_hide_set");
RNA_def_property_update(prop, 0, "rna_Mesh_update_select");
prop = RNA_def_property(srna, "use_seam", PROP_BOOLEAN, PROP_NONE);
@@ -2138,8 +2229,8 @@ static void rna_def_mpolygon(BlenderRNA *brna)
RNA_def_property_update(prop, 0, "rna_Mesh_update_select");
prop = RNA_def_property(srna, "hide", PROP_BOOLEAN, PROP_NONE);
- RNA_def_property_boolean_sdna(prop, NULL, "flag", ME_HIDE);
RNA_def_property_ui_text(prop, "Hide", "");
+ RNA_def_property_boolean_funcs(prop, "rna_MeshPolygon_hide_get", "rna_MeshPolygon_hide_set");
RNA_def_property_update(prop, 0, "rna_Mesh_update_select");
prop = RNA_def_property(srna, "use_smooth", PROP_BOOLEAN, PROP_NONE);
diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c
index 24ba8f0fd30..7d2fa8022dd 100644
--- a/source/blender/makesrna/intern/rna_nodetree.c
+++ b/source/blender/makesrna/intern/rna_nodetree.c
@@ -6477,7 +6477,7 @@ static void def_sh_script(StructRNA *srna)
RNA_def_function_return(func, parm);
func = RNA_def_function(srna, "add_socket", "rna_ShaderNodeScript_add_socket");
- RNA_def_function_ui_description(func, "Add a socket socket");
+ RNA_def_function_ui_description(func, "Add a socket");
RNA_def_function_flag(func, FUNC_USE_SELF_ID);
parm = RNA_def_string(func, "name", NULL, 0, "Name", "");
RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
@@ -6488,7 +6488,7 @@ static void def_sh_script(StructRNA *srna)
RNA_def_function_return(func, parm);
func = RNA_def_function(srna, "remove_socket", "rna_ShaderNodeScript_remove_socket");
- RNA_def_function_ui_description(func, "Remove a socket socket");
+ RNA_def_function_ui_description(func, "Remove a socket");
RNA_def_function_flag(func, FUNC_USE_SELF_ID);
parm = RNA_def_pointer(func, "sock", "NodeSocket", "Socket", "");
RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED);
diff --git a/source/blender/makesrna/intern/rna_path.cc b/source/blender/makesrna/intern/rna_path.cc
index 5d570657b53..f14e2113e13 100644
--- a/source/blender/makesrna/intern/rna_path.cc
+++ b/source/blender/makesrna/intern/rna_path.cc
@@ -940,7 +940,7 @@ ID *RNA_find_real_ID_and_path(Main *bmain, ID *id, const char **r_path)
BLI_assert_msg(0, "Missing handling of embedded id type.");
return id;
}
- return id_type->owner_get(bmain, id);
+ return id_type->owner_get(bmain, id, nullptr);
}
static char *rna_prepend_real_ID_path(Main *bmain, ID *id, char *path, ID **r_real_id)
diff --git a/source/blender/makesrna/intern/rna_pose.c b/source/blender/makesrna/intern/rna_pose.c
index 30df8e20e8d..e1a46b01db2 100644
--- a/source/blender/makesrna/intern/rna_pose.c
+++ b/source/blender/makesrna/intern/rna_pose.c
@@ -600,7 +600,7 @@ static void rna_PoseChannel_constraints_remove(
ED_object_constraint_update(bmain, ob);
- /* XXX(Campbell): is this really needed? */
+ /* XXX(@campbellbarton): is this really needed? */
BKE_constraints_active_set(&pchan->constraints, NULL);
WM_main_add_notifier(NC_OBJECT | ND_CONSTRAINT | NA_REMOVED, id);
diff --git a/source/blender/makesrna/intern/rna_scene.c b/source/blender/makesrna/intern/rna_scene.c
index f24aec3447b..2e78cc97099 100644
--- a/source/blender/makesrna/intern/rna_scene.c
+++ b/source/blender/makesrna/intern/rna_scene.c
@@ -1295,7 +1295,7 @@ static const EnumPropertyItem *rna_ImageFormatSettings_color_mode_itemf(bContext
ID *id = ptr->owner_id;
const bool is_render = (id && GS(id->name) == ID_SCE);
- /* NOTE(campbell): we need to act differently for render
+ /* NOTE(@campbellbarton): we need to act differently for render
* where 'BW' will force grayscale even if the output format writes
* as RGBA, this is age old blender convention and not sure how useful
* it really is but keep it for now. */
@@ -1478,7 +1478,7 @@ static void rna_ImageFormatSettings_color_management_set(PointerRNA *ptr, int va
if (owner_id && GS(owner_id->name) == ID_NT) {
/* For compositing nodes, find the corresponding scene. */
const IDTypeInfo *type_info = BKE_idtype_get_info_from_id(owner_id);
- owner_id = type_info->owner_get(G_MAIN, owner_id);
+ owner_id = type_info->owner_get(G_MAIN, owner_id, NULL);
}
if (owner_id && GS(owner_id->name) == ID_SCE) {
BKE_image_format_color_management_copy_from_scene(imf, (Scene *)owner_id);
diff --git a/source/blender/makesrna/intern/rna_sculpt_paint.c b/source/blender/makesrna/intern/rna_sculpt_paint.c
index 2e1fa8db7fe..4cd0b27c772 100644
--- a/source/blender/makesrna/intern/rna_sculpt_paint.c
+++ b/source/blender/makesrna/intern/rna_sculpt_paint.c
@@ -1040,7 +1040,7 @@ static void rna_def_paint_mode(BlenderRNA *brna)
RNA_def_property_pointer_funcs(
prop, NULL, NULL, NULL, "rna_PaintModeSettings_canvas_image_poll");
RNA_def_property_flag(prop, PROP_EDITABLE | PROP_CONTEXT_UPDATE);
- RNA_def_property_ui_text(prop, "Texture", "Image used as as painting target");
+ RNA_def_property_ui_text(prop, "Texture", "Image used as painting target");
}
static void rna_def_image_paint(BlenderRNA *brna)
diff --git a/source/blender/makesrna/intern/rna_space.c b/source/blender/makesrna/intern/rna_space.c
index 5cee2ca00a3..502b0404764 100644
--- a/source/blender/makesrna/intern/rna_space.c
+++ b/source/blender/makesrna/intern/rna_space.c
@@ -4199,6 +4199,14 @@ static void rna_def_space_view3d_shading(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Shader AOV Name", "Name of the active Shader AOV");
RNA_def_property_flag(prop, PROP_HIDDEN);
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL);
+
+ prop = RNA_def_property(srna, "use_compositor", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", V3D_SHADING_COMPOSITOR);
+ RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
+ RNA_def_property_boolean_default(prop, false);
+ RNA_def_property_ui_text(
+ prop, "Compositor", "Preview the compositor output inside the viewport");
+ RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D | NS_VIEW3D_SHADING, NULL);
}
static void rna_def_space_view3d_overlay(BlenderRNA *brna)
diff --git a/source/blender/makesrna/intern/rna_ui_api.c b/source/blender/makesrna/intern/rna_ui_api.c
index 1b416e4b6e5..fc68e8421d7 100644
--- a/source/blender/makesrna/intern/rna_ui_api.c
+++ b/source/blender/makesrna/intern/rna_ui_api.c
@@ -1808,8 +1808,7 @@ void RNA_api_ui_layout(StructRNA *srna)
func = RNA_def_function(
srna, "template_colormanaged_view_settings", "uiTemplateColormanagedViewSettings");
- RNA_def_function_ui_description(
- func, "Item. A widget to control color managed view settings settings.");
+ RNA_def_function_ui_description(func, "Item. A widget to control color managed view settings.");
RNA_def_function_flag(func, FUNC_USE_CONTEXT);
api_ui_item_rna_common(func);
# if 0
diff --git a/source/blender/makesrna/intern/rna_userdef.c b/source/blender/makesrna/intern/rna_userdef.c
index cfc72791123..438bac9b458 100644
--- a/source/blender/makesrna/intern/rna_userdef.c
+++ b/source/blender/makesrna/intern/rna_userdef.c
@@ -6338,6 +6338,10 @@ static void rna_def_userdef_experimental(BlenderRNA *brna)
RNA_def_property_ui_text(
prop, "Sculpt Mode Tilt Support", "Support for pen tablet tilt events in Sculpt Mode");
+ prop = RNA_def_property(srna, "use_realtime_compositor", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "use_realtime_compositor", 1);
+ RNA_def_property_ui_text(prop, "Realtime Compositor", "Enable the new realtime compositor");
+
prop = RNA_def_property(srna, "use_sculpt_texture_paint", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "use_sculpt_texture_paint", 1);
RNA_def_property_ui_text(prop, "Sculpt Texture Paint", "Use texture painting in Sculpt Mode");
diff --git a/source/blender/makesrna/intern/rna_wm.c b/source/blender/makesrna/intern/rna_wm.c
index 2009f51e1f2..ac1803b0a11 100644
--- a/source/blender/makesrna/intern/rna_wm.c
+++ b/source/blender/makesrna/intern/rna_wm.c
@@ -864,6 +864,21 @@ static void rna_Window_view_layer_set(PointerRNA *ptr,
WM_window_set_active_view_layer(win, view_layer);
}
+static void rna_KeyMap_modal_event_values_items_begin(CollectionPropertyIterator *iter,
+ PointerRNA *ptr)
+{
+ wmKeyMap *km = ptr->data;
+
+ const EnumPropertyItem *items = rna_enum_keymap_propvalue_items;
+ if ((km->flag & KEYMAP_MODAL) != 0 && km->modal_items != NULL) {
+ items = km->modal_items;
+ }
+
+ const int totitem = RNA_enum_items_count(items);
+
+ rna_iterator_array_begin(iter, (void *)items, sizeof(EnumPropertyItem), totitem, false, NULL);
+}
+
static PointerRNA rna_KeyMapItem_properties_get(PointerRNA *ptr)
{
wmKeyMapItem *kmi = ptr->data;
@@ -2617,6 +2632,23 @@ static void rna_def_keyconfig(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Children Expanded", "Children expanded in the user interface");
RNA_def_property_ui_icon(prop, ICON_DISCLOSURE_TRI_RIGHT, 1);
+ prop = RNA_def_property(srna, "modal_event_values", PROP_COLLECTION, PROP_NONE);
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+ RNA_def_property_struct_type(prop, "EnumPropertyItem");
+ RNA_def_property_collection_funcs(prop,
+ "rna_KeyMap_modal_event_values_items_begin",
+ "rna_iterator_array_next",
+ "rna_iterator_array_end",
+ "rna_iterator_array_get",
+ NULL,
+ NULL,
+ NULL,
+ NULL);
+ RNA_def_property_ui_text(prop,
+ "Modal Events",
+ "Give access to the possible event values of this modal keymap's items "
+ "(#KeyMapItem.propvalue), for API introspection");
+
RNA_api_keymap(srna);
/* KeyMapItem */
diff --git a/source/blender/modifiers/intern/MOD_armature.c b/source/blender/modifiers/intern/MOD_armature.c
index 3fce556ce77..43f650e025c 100644
--- a/source/blender/modifiers/intern/MOD_armature.c
+++ b/source/blender/modifiers/intern/MOD_armature.c
@@ -211,8 +211,7 @@ static void deformMatrices(ModifierData *md,
int verts_num)
{
ArmatureModifierData *amd = (ArmatureModifierData *)md;
- Mesh *mesh_src = MOD_deform_mesh_eval_get(
- ctx->object, NULL, mesh, NULL, verts_num, false, false);
+ Mesh *mesh_src = MOD_deform_mesh_eval_get(ctx->object, NULL, mesh, NULL, verts_num, false);
BKE_armature_deform_coords_with_mesh(amd->object,
ctx->object,
diff --git a/source/blender/modifiers/intern/MOD_cast.c b/source/blender/modifiers/intern/MOD_cast.c
index 874dd20691f..e17a612376d 100644
--- a/source/blender/modifiers/intern/MOD_cast.c
+++ b/source/blender/modifiers/intern/MOD_cast.c
@@ -467,7 +467,7 @@ static void deformVerts(ModifierData *md,
if (ctx->object->type == OB_MESH && cmd->defgrp_name[0] != '\0') {
/* mesh_src is only needed for vgroups. */
- mesh_src = MOD_deform_mesh_eval_get(ctx->object, NULL, mesh, NULL, verts_num, false, false);
+ mesh_src = MOD_deform_mesh_eval_get(ctx->object, NULL, mesh, NULL, verts_num, false);
}
if (cmd->type == MOD_CAST_TYPE_CUBOID) {
@@ -493,15 +493,14 @@ static void deformVertsEM(ModifierData *md,
Mesh *mesh_src = NULL;
if (cmd->defgrp_name[0] != '\0') {
- mesh_src = MOD_deform_mesh_eval_get(
- ctx->object, editData, mesh, NULL, verts_num, false, false);
+ mesh_src = MOD_deform_mesh_eval_get(ctx->object, editData, mesh, NULL, verts_num, false);
}
if (mesh && mesh->runtime.wrapper_type == ME_WRAPPER_TYPE_MDATA) {
BLI_assert(mesh->totvert == verts_num);
}
- /* TODO(Campbell): use edit-mode data only (remove this line). */
+ /* TODO(@campbellbarton): use edit-mode data only (remove this line). */
if (mesh_src != NULL) {
BKE_mesh_wrapper_ensure_mdata(mesh_src);
}
diff --git a/source/blender/modifiers/intern/MOD_cloth.c b/source/blender/modifiers/intern/MOD_cloth.c
index 4fe65bf28ac..11bbe8dc83e 100644
--- a/source/blender/modifiers/intern/MOD_cloth.c
+++ b/source/blender/modifiers/intern/MOD_cloth.c
@@ -94,7 +94,7 @@ static void deformVerts(ModifierData *md,
}
if (mesh == NULL) {
- mesh_src = MOD_deform_mesh_eval_get(ctx->object, NULL, NULL, NULL, verts_num, false, false);
+ mesh_src = MOD_deform_mesh_eval_get(ctx->object, NULL, NULL, NULL, verts_num, false);
}
else {
/* Not possible to use get_mesh() in this case as we'll modify its vertices
diff --git a/source/blender/modifiers/intern/MOD_collision.c b/source/blender/modifiers/intern/MOD_collision.c
index 868e164e223..42a8ba804ed 100644
--- a/source/blender/modifiers/intern/MOD_collision.c
+++ b/source/blender/modifiers/intern/MOD_collision.c
@@ -107,7 +107,7 @@ static void deformVerts(ModifierData *md,
}
if (mesh == NULL) {
- mesh_src = MOD_deform_mesh_eval_get(ob, NULL, NULL, NULL, verts_num, false, false);
+ mesh_src = MOD_deform_mesh_eval_get(ob, NULL, NULL, NULL, verts_num, false);
}
else {
/* Not possible to use get_mesh() in this case as we'll modify its vertices
diff --git a/source/blender/modifiers/intern/MOD_correctivesmooth.c b/source/blender/modifiers/intern/MOD_correctivesmooth.c
index 2beb1be6749..4df0479372f 100644
--- a/source/blender/modifiers/intern/MOD_correctivesmooth.c
+++ b/source/blender/modifiers/intern/MOD_correctivesmooth.c
@@ -729,8 +729,7 @@ static void deformVerts(ModifierData *md,
float (*vertexCos)[3],
int verts_num)
{
- Mesh *mesh_src = MOD_deform_mesh_eval_get(
- ctx->object, NULL, mesh, NULL, verts_num, false, false);
+ Mesh *mesh_src = MOD_deform_mesh_eval_get(ctx->object, NULL, mesh, NULL, verts_num, false);
correctivesmooth_modifier_do(
md, ctx->depsgraph, ctx->object, mesh_src, vertexCos, (uint)verts_num, NULL);
@@ -747,10 +746,9 @@ static void deformVertsEM(ModifierData *md,
float (*vertexCos)[3],
int verts_num)
{
- Mesh *mesh_src = MOD_deform_mesh_eval_get(
- ctx->object, editData, mesh, NULL, verts_num, false, false);
+ Mesh *mesh_src = MOD_deform_mesh_eval_get(ctx->object, editData, mesh, NULL, verts_num, false);
- /* TODO(Campbell): use edit-mode data only (remove this line). */
+ /* TODO(@campbellbarton): use edit-mode data only (remove this line). */
if (mesh_src != NULL) {
BKE_mesh_wrapper_ensure_mdata(mesh_src);
}
diff --git a/source/blender/modifiers/intern/MOD_curve.c b/source/blender/modifiers/intern/MOD_curve.c
index bd622fc1373..af639915bd8 100644
--- a/source/blender/modifiers/intern/MOD_curve.c
+++ b/source/blender/modifiers/intern/MOD_curve.c
@@ -111,7 +111,7 @@ static void deformVerts(ModifierData *md,
if (ctx->object->type == OB_MESH && cmd->name[0] != '\0') {
/* mesh_src is only needed for vgroups. */
- mesh_src = MOD_deform_mesh_eval_get(ctx->object, NULL, mesh, NULL, verts_num, false, false);
+ mesh_src = MOD_deform_mesh_eval_get(ctx->object, NULL, mesh, NULL, verts_num, false);
}
struct MDeformVert *dvert = NULL;
diff --git a/source/blender/modifiers/intern/MOD_decimate.c b/source/blender/modifiers/intern/MOD_decimate.c
index 3df4fbcbea8..55d9d148d10 100644
--- a/source/blender/modifiers/intern/MOD_decimate.c
+++ b/source/blender/modifiers/intern/MOD_decimate.c
@@ -201,11 +201,12 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *
updateFaceCount(ctx, dmd, bm->totface);
- result = BKE_mesh_from_bmesh_for_eval_nomain(bm, NULL, mesh);
/* make sure we never alloc'd these */
BLI_assert(bm->vtoolflagpool == NULL && bm->etoolflagpool == NULL && bm->ftoolflagpool == NULL);
BLI_assert(bm->vtable == NULL && bm->etable == NULL && bm->ftable == NULL);
+ result = BKE_mesh_from_bmesh_for_eval_nomain(bm, NULL, mesh);
+
BM_mesh_free(bm);
#ifdef USE_TIMEIT
diff --git a/source/blender/modifiers/intern/MOD_displace.c b/source/blender/modifiers/intern/MOD_displace.c
index cf1bb64a41d..367809953b6 100644
--- a/source/blender/modifiers/intern/MOD_displace.c
+++ b/source/blender/modifiers/intern/MOD_displace.c
@@ -372,8 +372,7 @@ static void deformVerts(ModifierData *md,
float (*vertexCos)[3],
int verts_num)
{
- Mesh *mesh_src = MOD_deform_mesh_eval_get(
- ctx->object, NULL, mesh, NULL, verts_num, false, false);
+ Mesh *mesh_src = MOD_deform_mesh_eval_get(ctx->object, NULL, mesh, NULL, verts_num, false);
displaceModifier_do((DisplaceModifierData *)md, ctx, mesh_src, vertexCos, verts_num);
@@ -389,10 +388,9 @@ static void deformVertsEM(ModifierData *md,
float (*vertexCos)[3],
int verts_num)
{
- Mesh *mesh_src = MOD_deform_mesh_eval_get(
- ctx->object, editData, mesh, NULL, verts_num, false, false);
+ Mesh *mesh_src = MOD_deform_mesh_eval_get(ctx->object, editData, mesh, NULL, verts_num, false);
- /* TODO(Campbell): use edit-mode data only (remove this line). */
+ /* TODO(@campbellbarton): use edit-mode data only (remove this line). */
if (mesh_src != NULL) {
BKE_mesh_wrapper_ensure_mdata(mesh_src);
}
diff --git a/source/blender/modifiers/intern/MOD_explode.c b/source/blender/modifiers/intern/MOD_explode.c
index ff0616fd288..e243c32173d 100644
--- a/source/blender/modifiers/intern/MOD_explode.c
+++ b/source/blender/modifiers/intern/MOD_explode.c
@@ -742,7 +742,7 @@ static Mesh *cutEdges(ExplodeModifierData *emd, Mesh *mesh)
/* override original facepa (original pointer is saved in caller function) */
- /* TODO(campbell): `(totfsplit * 2)` over allocation is used since the quads are
+ /* TODO(@campbellbarton): `(totfsplit * 2)` over allocation is used since the quads are
* later interpreted as tri's, for this to work right I think we probably
* have to stop using tessface. */
diff --git a/source/blender/modifiers/intern/MOD_hook.c b/source/blender/modifiers/intern/MOD_hook.c
index b9942ff8a4d..979a08483e1 100644
--- a/source/blender/modifiers/intern/MOD_hook.c
+++ b/source/blender/modifiers/intern/MOD_hook.c
@@ -430,8 +430,7 @@ static void deformVerts(struct ModifierData *md,
int verts_num)
{
HookModifierData *hmd = (HookModifierData *)md;
- Mesh *mesh_src = MOD_deform_mesh_eval_get(
- ctx->object, NULL, mesh, NULL, verts_num, false, false);
+ Mesh *mesh_src = MOD_deform_mesh_eval_get(ctx->object, NULL, mesh, NULL, verts_num, false);
deformVerts_do(hmd, ctx, ctx->object, mesh_src, NULL, vertexCos, verts_num);
diff --git a/source/blender/modifiers/intern/MOD_laplaciandeform.c b/source/blender/modifiers/intern/MOD_laplaciandeform.c
index e29098eb218..6333eb699b3 100644
--- a/source/blender/modifiers/intern/MOD_laplaciandeform.c
+++ b/source/blender/modifiers/intern/MOD_laplaciandeform.c
@@ -764,8 +764,7 @@ static void deformVerts(ModifierData *md,
float (*vertexCos)[3],
int verts_num)
{
- Mesh *mesh_src = MOD_deform_mesh_eval_get(
- ctx->object, NULL, mesh, NULL, verts_num, false, false);
+ Mesh *mesh_src = MOD_deform_mesh_eval_get(ctx->object, NULL, mesh, NULL, verts_num, false);
LaplacianDeformModifier_do(
(LaplacianDeformModifierData *)md, ctx->object, mesh_src, vertexCos, verts_num);
@@ -782,10 +781,9 @@ static void deformVertsEM(ModifierData *md,
float (*vertexCos)[3],
int verts_num)
{
- Mesh *mesh_src = MOD_deform_mesh_eval_get(
- ctx->object, editData, mesh, NULL, verts_num, false, false);
+ Mesh *mesh_src = MOD_deform_mesh_eval_get(ctx->object, editData, mesh, NULL, verts_num, false);
- /* TODO(Campbell): use edit-mode data only (remove this line). */
+ /* TODO(@campbellbarton): use edit-mode data only (remove this line). */
if (mesh_src != NULL) {
BKE_mesh_wrapper_ensure_mdata(mesh_src);
}
diff --git a/source/blender/modifiers/intern/MOD_laplaciansmooth.c b/source/blender/modifiers/intern/MOD_laplaciansmooth.c
index 2cce0c14e4c..c42f7b33919 100644
--- a/source/blender/modifiers/intern/MOD_laplaciansmooth.c
+++ b/source/blender/modifiers/intern/MOD_laplaciansmooth.c
@@ -535,7 +535,7 @@ static void deformVerts(ModifierData *md,
return;
}
- mesh_src = MOD_deform_mesh_eval_get(ctx->object, NULL, mesh, NULL, verts_num, false, false);
+ mesh_src = MOD_deform_mesh_eval_get(ctx->object, NULL, mesh, NULL, verts_num, false);
laplaciansmoothModifier_do(
(LaplacianSmoothModifierData *)md, ctx->object, mesh_src, vertexCos, verts_num);
@@ -558,9 +558,9 @@ static void deformVertsEM(ModifierData *md,
return;
}
- mesh_src = MOD_deform_mesh_eval_get(ctx->object, editData, mesh, NULL, verts_num, false, false);
+ mesh_src = MOD_deform_mesh_eval_get(ctx->object, editData, mesh, NULL, verts_num, false);
- /* TODO(Campbell): use edit-mode data only (remove this line). */
+ /* TODO(@campbellbarton): use edit-mode data only (remove this line). */
if (mesh_src != NULL) {
BKE_mesh_wrapper_ensure_mdata(mesh_src);
}
diff --git a/source/blender/modifiers/intern/MOD_lattice.c b/source/blender/modifiers/intern/MOD_lattice.c
index 2edcbd8e59a..81b60b660c6 100644
--- a/source/blender/modifiers/intern/MOD_lattice.c
+++ b/source/blender/modifiers/intern/MOD_lattice.c
@@ -98,7 +98,7 @@ static void deformVerts(ModifierData *md,
{
LatticeModifierData *lmd = (LatticeModifierData *)md;
struct Mesh *mesh_src = MOD_deform_mesh_eval_get(
- ctx->object, NULL, mesh, NULL, verts_num, false, false);
+ ctx->object, NULL, mesh, NULL, verts_num, false);
MOD_previous_vcos_store(md, vertexCos); /* if next modifier needs original vertices */
diff --git a/source/blender/modifiers/intern/MOD_meshcache.c b/source/blender/modifiers/intern/MOD_meshcache.c
index 8dfdd07ace9..3e81f987da3 100644
--- a/source/blender/modifiers/intern/MOD_meshcache.c
+++ b/source/blender/modifiers/intern/MOD_meshcache.c
@@ -297,7 +297,7 @@ static void deformVerts(ModifierData *md,
if (ctx->object->type == OB_MESH && mcmd->defgrp_name[0] != '\0') {
/* `mesh_src` is only needed for vertex groups. */
- mesh_src = MOD_deform_mesh_eval_get(ctx->object, NULL, mesh, NULL, verts_num, false, false);
+ mesh_src = MOD_deform_mesh_eval_get(ctx->object, NULL, mesh, NULL, verts_num, false);
}
meshcache_do(mcmd, scene, ctx->object, mesh_src, vertexCos, verts_num);
@@ -320,8 +320,7 @@ static void deformVertsEM(ModifierData *md,
if (ctx->object->type == OB_MESH && mcmd->defgrp_name[0] != '\0') {
/* `mesh_src` is only needed for vertex groups. */
- mesh_src = MOD_deform_mesh_eval_get(
- ctx->object, editData, mesh, NULL, verts_num, false, false);
+ mesh_src = MOD_deform_mesh_eval_get(ctx->object, editData, mesh, NULL, verts_num, false);
}
if (mesh_src != NULL) {
BKE_mesh_wrapper_ensure_mdata(mesh_src);
diff --git a/source/blender/modifiers/intern/MOD_meshdeform.c b/source/blender/modifiers/intern/MOD_meshdeform.c
index 3b7e62f99e1..d1df86b1010 100644
--- a/source/blender/modifiers/intern/MOD_meshdeform.c
+++ b/source/blender/modifiers/intern/MOD_meshdeform.c
@@ -444,8 +444,7 @@ static void deformVerts(ModifierData *md,
float (*vertexCos)[3],
int verts_num)
{
- Mesh *mesh_src = MOD_deform_mesh_eval_get(
- ctx->object, NULL, mesh, NULL, verts_num, false, false);
+ Mesh *mesh_src = MOD_deform_mesh_eval_get(ctx->object, NULL, mesh, NULL, verts_num, false);
MOD_previous_vcos_store(md, vertexCos); /* if next modifier needs original vertices */
@@ -463,10 +462,9 @@ static void deformVertsEM(ModifierData *md,
float (*vertexCos)[3],
int verts_num)
{
- Mesh *mesh_src = MOD_deform_mesh_eval_get(
- ctx->object, editData, mesh, NULL, verts_num, false, false);
+ Mesh *mesh_src = MOD_deform_mesh_eval_get(ctx->object, editData, mesh, NULL, verts_num, false);
- /* TODO(Campbell): use edit-mode data only (remove this line). */
+ /* TODO(@campbellbarton): use edit-mode data only (remove this line). */
if (mesh_src != NULL) {
BKE_mesh_wrapper_ensure_mdata(mesh_src);
}
diff --git a/source/blender/modifiers/intern/MOD_particleinstance.c b/source/blender/modifiers/intern/MOD_particleinstance.c
index 5018b2d1030..d6435c55211 100644
--- a/source/blender/modifiers/intern/MOD_particleinstance.c
+++ b/source/blender/modifiers/intern/MOD_particleinstance.c
@@ -553,7 +553,7 @@ static void panel_draw(const bContext *UNUSED(C), Panel *panel)
"particle_system",
&particle_obj_ptr,
"particle_systems",
- "Particle System",
+ IFACE_("Particle System"),
ICON_NONE);
}
else {
diff --git a/source/blender/modifiers/intern/MOD_particlesystem.cc b/source/blender/modifiers/intern/MOD_particlesystem.cc
index 7f7465947f9..0c04c6fc062 100644
--- a/source/blender/modifiers/intern/MOD_particlesystem.cc
+++ b/source/blender/modifiers/intern/MOD_particlesystem.cc
@@ -119,8 +119,7 @@ static void deformVerts(ModifierData *md,
}
if (mesh_src == nullptr) {
- mesh_src = MOD_deform_mesh_eval_get(
- ctx->object, nullptr, nullptr, vertexCos, verts_num, false, true);
+ mesh_src = MOD_deform_mesh_eval_get(ctx->object, nullptr, nullptr, vertexCos, verts_num, true);
if (mesh_src == nullptr) {
return;
}
diff --git a/source/blender/modifiers/intern/MOD_screw.c b/source/blender/modifiers/intern/MOD_screw.c
index 109795df796..6095be48f8f 100644
--- a/source/blender/modifiers/intern/MOD_screw.c
+++ b/source/blender/modifiers/intern/MOD_screw.c
@@ -52,13 +52,18 @@ static void initData(ModifierData *md)
#include "BLI_strict_flags.h"
-/* used for gathering edge connectivity */
+/** Used for gathering edge connectivity. */
typedef struct ScrewVertConnect {
- float dist; /* distance from the center axis */
- float co[3]; /* location relative to the transformed axis */
- float no[3]; /* calc normal of the vertex */
- uint v[2]; /* 2 verts on either side of this one */
- MEdge *e[2]; /* edges on either side, a bit of a waste since each edge ref's 2 edges */
+ /** Distance from the center axis. */
+ float dist_sq;
+ /** Location relative to the transformed axis. */
+ float co[3];
+ /** Calc normal of the vertex. */
+ float no[3];
+ /** 2 verts on either side of this one. */
+ uint v[2];
+ /** Edges on either side, a bit of a waste since each edge ref's 2 edges. */
+ MEdge *e[2];
char flag;
} ScrewVertConnect;
@@ -270,18 +275,18 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *
axis_vec[ltmd->axis] = 1.0f;
if (ob_axis != NULL) {
- /* calc the matrix relative to the axis object */
+ /* Calculate the matrix relative to the axis object. */
invert_m4_m4(mtx_tmp_a, ctx->object->obmat);
copy_m4_m4(mtx_tx_inv, ob_axis->obmat);
mul_m4_m4m4(mtx_tx, mtx_tmp_a, mtx_tx_inv);
- /* calc the axis vec */
+ /* Calculate the axis vector. */
mul_mat3_m4_v3(mtx_tx, axis_vec); /* only rotation component */
normalize_v3(axis_vec);
/* screw */
if (ltmd->flag & MOD_SCREW_OBJECT_OFFSET) {
- /* find the offset along this axis relative to this objects matrix */
+ /* Find the offset along this axis relative to this objects matrix. */
float totlen = len_v3(mtx_tx[3]);
if (totlen != 0.0f) {
@@ -330,7 +335,7 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *
else {
axis_char = (char)(axis_char + ltmd->axis); /* 'X' + axis */
- /* useful to be able to use the axis vec in some cases still */
+ /* Useful to be able to use the axis vector in some cases still. */
zero_v3(axis_vec);
axis_vec[ltmd->axis] = 1.0f;
}
@@ -441,7 +446,7 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *
med_new->crease = med_orig->crease;
med_new->flag = med_orig->flag & ~ME_LOOSEEDGE;
- /* Tag mvert as not loose. */
+ /* Tag #MVert as not loose. */
BLI_BITMAP_ENABLE(vert_tag, med_orig->v1);
BLI_BITMAP_ENABLE(vert_tag, med_orig->v2);
}
@@ -481,8 +486,7 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *
if (ltmd->flag & MOD_SCREW_NORMAL_CALC) {
- /*
- * Normal Calculation (for face flipping)
+ /* Normal Calculation (for face flipping)
* Sort edge verts for correct face flipping
* NOT REALLY NEEDED but face flipping is nice. */
@@ -490,19 +494,19 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *
*
* Since we are only ordering the edges here it can avoid mallocing the
* extra space by abusing the vert array before its filled with new verts.
- * The new array for vert_connect must be at least sizeof(ScrewVertConnect) * totvert
- * and the size of our resulting meshes array is sizeof(MVert) * totvert * 3
- * so its safe to use the second 2 thirds of MVert the array for vert_connect,
- * just make sure ScrewVertConnect struct is no more than twice as big as MVert,
+ * The new array for vert_connect must be at least `sizeof(ScrewVertConnect) * totvert`
+ * and the size of our resulting meshes array is `sizeof(MVert) * totvert * 3`
+ * so its safe to use the second 2 thirds of #MVert the array for vert_connect,
+ * just make sure #ScrewVertConnect struct is no more than twice as big as #MVert,
* at the moment there is no chance of that being a problem,
- * unless MVert becomes half its current size.
+ * unless #MVert becomes half its current size.
*
* once the edges are ordered, vert_connect is not needed and it can be used for verts
*
- * This makes the modifier faster with one less alloc.
+ * This makes the modifier faster with one less allocate.
*/
- vert_connect = MEM_malloc_arrayN(totvert, sizeof(ScrewVertConnect), "ScrewVertConnect");
+ vert_connect = MEM_malloc_arrayN(totvert, sizeof(ScrewVertConnect), __func__);
/* skip the first slice of verts. */
// vert_connect = (ScrewVertConnect *) &medge_new[totvert];
vc = vert_connect;
@@ -512,7 +516,8 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *
if (!totedge) {
for (i = 0; i < totvert; i++, mv_orig++, mv_new++) {
copy_v3_v3(mv_new->co, mv_orig->co);
- normalize_v3_v3(vc->no, mv_new->co); /* no edges- this is really a dummy normal */
+ /* No edges: this is really a dummy normal. */
+ normalize_v3_v3(vc->no, mv_new->co);
}
}
else {
@@ -533,11 +538,11 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *
vc->v[0] = vc->v[1] = SV_UNUSED;
mul_m4_v3(mtx_tx, vc->co);
- /* length in 2d, don't sqrt because this is only for comparison */
- vc->dist = vc->co[other_axis_1] * vc->co[other_axis_1] +
- vc->co[other_axis_2] * vc->co[other_axis_2];
+ /* Length in 2D, don't `sqrt` because this is only for comparison. */
+ vc->dist_sq = vc->co[other_axis_1] * vc->co[other_axis_1] +
+ vc->co[other_axis_2] * vc->co[other_axis_2];
- // printf("location %f %f %f -- %f\n", vc->co[0], vc->co[1], vc->co[2], vc->dist);
+ // printf("location %f %f %f -- %f\n", vc->co[0], vc->co[1], vc->co[2], vc->dist_sq);
}
}
else {
@@ -550,11 +555,11 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *
vc->e[0] = vc->e[1] = NULL;
vc->v[0] = vc->v[1] = SV_UNUSED;
- /* length in 2d, don't sqrt because this is only for comparison */
- vc->dist = vc->co[other_axis_1] * vc->co[other_axis_1] +
- vc->co[other_axis_2] * vc->co[other_axis_2];
+ /* Length in 2D, don't sqrt because this is only for comparison. */
+ vc->dist_sq = vc->co[other_axis_1] * vc->co[other_axis_1] +
+ vc->co[other_axis_2] * vc->co[other_axis_2];
- // printf("location %f %f %f -- %f\n", vc->co[0], vc->co[1], vc->co[2], vc->dist);
+ // printf("location %f %f %f -- %f\n", vc->co[0], vc->co[1], vc->co[2], vc->dist_sq);
}
}
@@ -622,9 +627,9 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *
}
lt_iter.v_poin->flag = 1;
vc_tot_linked++;
- // printf("Testing 2 floats %f : %f\n", fl, lt_iter.v_poin->dist);
- if (fl <= lt_iter.v_poin->dist) {
- fl = lt_iter.v_poin->dist;
+ // printf("Testing 2 floats %f : %f\n", fl, lt_iter.v_poin->dist_sq);
+ if (fl <= lt_iter.v_poin->dist_sq) {
+ fl = lt_iter.v_poin->dist_sq;
v_best = lt_iter.v;
// printf("\t\t\tVERT BEST: %i\n", v_best);
}
diff --git a/source/blender/modifiers/intern/MOD_shrinkwrap.c b/source/blender/modifiers/intern/MOD_shrinkwrap.c
index 1c4ef5698b4..4a927d92956 100644
--- a/source/blender/modifiers/intern/MOD_shrinkwrap.c
+++ b/source/blender/modifiers/intern/MOD_shrinkwrap.c
@@ -108,7 +108,7 @@ static void deformVerts(ModifierData *md,
(swmd->shrinkType == MOD_SHRINKWRAP_PROJECT)) {
/* mesh_src is needed for vgroups, but also used as ShrinkwrapCalcData.vert when projecting.
* Avoid time-consuming mesh conversion for curves when not projecting. */
- mesh_src = MOD_deform_mesh_eval_get(ctx->object, NULL, mesh, NULL, verts_num, false, false);
+ mesh_src = MOD_deform_mesh_eval_get(ctx->object, NULL, mesh, NULL, verts_num, false);
}
struct MDeformVert *dvert = NULL;
@@ -135,11 +135,10 @@ static void deformVertsEM(ModifierData *md,
Mesh *mesh_src = NULL;
if ((swmd->vgroup_name[0] != '\0') || (swmd->shrinkType == MOD_SHRINKWRAP_PROJECT)) {
- mesh_src = MOD_deform_mesh_eval_get(
- ctx->object, editData, mesh, NULL, verts_num, false, false);
+ mesh_src = MOD_deform_mesh_eval_get(ctx->object, editData, mesh, NULL, verts_num, false);
}
- /* TODO(Campbell): use edit-mode data only (remove this line). */
+ /* TODO(@campbellbarton): use edit-mode data only (remove this line). */
if (mesh_src != NULL) {
BKE_mesh_wrapper_ensure_mdata(mesh_src);
}
diff --git a/source/blender/modifiers/intern/MOD_simpledeform.c b/source/blender/modifiers/intern/MOD_simpledeform.c
index 168575b6330..1fc4f11bc66 100644
--- a/source/blender/modifiers/intern/MOD_simpledeform.c
+++ b/source/blender/modifiers/intern/MOD_simpledeform.c
@@ -453,7 +453,7 @@ static void deformVerts(ModifierData *md,
if (ctx->object->type == OB_MESH && sdmd->vgroup_name[0] != '\0') {
/* mesh_src is only needed for vgroups. */
- mesh_src = MOD_deform_mesh_eval_get(ctx->object, NULL, mesh, NULL, verts_num, false, false);
+ mesh_src = MOD_deform_mesh_eval_get(ctx->object, NULL, mesh, NULL, verts_num, false);
}
SimpleDeformModifier_do(sdmd, ctx, ctx->object, mesh_src, vertexCos, verts_num);
@@ -475,11 +475,10 @@ static void deformVertsEM(ModifierData *md,
if (ctx->object->type == OB_MESH && sdmd->vgroup_name[0] != '\0') {
/* mesh_src is only needed for vgroups. */
- mesh_src = MOD_deform_mesh_eval_get(
- ctx->object, editData, mesh, NULL, verts_num, false, false);
+ mesh_src = MOD_deform_mesh_eval_get(ctx->object, editData, mesh, NULL, verts_num, false);
}
- /* TODO(Campbell): use edit-mode data only (remove this line). */
+ /* TODO(@campbellbarton): use edit-mode data only (remove this line). */
if (mesh_src != NULL) {
BKE_mesh_wrapper_ensure_mdata(mesh_src);
}
diff --git a/source/blender/modifiers/intern/MOD_smooth.c b/source/blender/modifiers/intern/MOD_smooth.c
index c868c47cb90..6dd3d491283 100644
--- a/source/blender/modifiers/intern/MOD_smooth.c
+++ b/source/blender/modifiers/intern/MOD_smooth.c
@@ -190,7 +190,7 @@ static void deformVerts(ModifierData *md,
Mesh *mesh_src = NULL;
/* mesh_src is needed for vgroups, and taking edges into account. */
- mesh_src = MOD_deform_mesh_eval_get(ctx->object, NULL, mesh, NULL, verts_num, false, false);
+ mesh_src = MOD_deform_mesh_eval_get(ctx->object, NULL, mesh, NULL, verts_num, false);
smoothModifier_do(smd, ctx->object, mesh_src, vertexCos, verts_num);
@@ -210,9 +210,9 @@ static void deformVertsEM(ModifierData *md,
Mesh *mesh_src = NULL;
/* mesh_src is needed for vgroups, and taking edges into account. */
- mesh_src = MOD_deform_mesh_eval_get(ctx->object, editData, mesh, NULL, verts_num, false, false);
+ mesh_src = MOD_deform_mesh_eval_get(ctx->object, editData, mesh, NULL, verts_num, false);
- /* TODO(campbell): use edit-mode data only (remove this line). */
+ /* TODO(@campbellbarton): use edit-mode data only (remove this line). */
BKE_mesh_wrapper_ensure_mdata(mesh_src);
smoothModifier_do(smd, ctx->object, mesh_src, vertexCos, verts_num);
diff --git a/source/blender/modifiers/intern/MOD_solidify_extrude.c b/source/blender/modifiers/intern/MOD_solidify_extrude.c
index 8bd66de966f..2247a6331fe 100644
--- a/source/blender/modifiers/intern/MOD_solidify_extrude.c
+++ b/source/blender/modifiers/intern/MOD_solidify_extrude.c
@@ -1016,9 +1016,9 @@ Mesh *MOD_solidify_extrude_modifyMesh(ModifierData *md, const ModifierEvalContex
if (do_rim) {
uint i;
- /* NOTE(campbell): Unfortunately re-calculate the normals for the new edge faces is necessary.
- * This could be done in many ways, but probably the quickest way
- * is to calculate the average normals for side faces only.
+ /* NOTE(@campbellbarton): Unfortunately re-calculate the normals for the new edge
+ * faces is necessary. This could be done in many ways, but probably the quickest
+ * way is to calculate the average normals for side faces only.
* Then blend them with the normals of the edge verts.
*
* At the moment its easiest to allocate an entire array for every vertex,
diff --git a/source/blender/modifiers/intern/MOD_surface.c b/source/blender/modifiers/intern/MOD_surface.c
index 8cfe3b35949..3e5a577a806 100644
--- a/source/blender/modifiers/intern/MOD_surface.c
+++ b/source/blender/modifiers/intern/MOD_surface.c
@@ -114,7 +114,7 @@ static void deformVerts(ModifierData *md,
surmd->mesh = (Mesh *)BKE_id_copy_ex(NULL, (ID *)mesh, NULL, LIB_ID_COPY_LOCALIZE);
}
else {
- surmd->mesh = MOD_deform_mesh_eval_get(ctx->object, NULL, NULL, NULL, verts_num, false, false);
+ surmd->mesh = MOD_deform_mesh_eval_get(ctx->object, NULL, NULL, NULL, verts_num, false);
}
if (!ctx->object->pd) {
diff --git a/source/blender/modifiers/intern/MOD_surfacedeform.c b/source/blender/modifiers/intern/MOD_surfacedeform.c
index ad19ecf5720..3c0842a6e93 100644
--- a/source/blender/modifiers/intern/MOD_surfacedeform.c
+++ b/source/blender/modifiers/intern/MOD_surfacedeform.c
@@ -215,8 +215,7 @@ static void freeData(ModifierData *md)
MEM_SAFE_FREE(smd->verts[i].binds[j].vert_inds);
MEM_SAFE_FREE(smd->verts[i].binds[j].vert_weights);
}
-
- MEM_SAFE_FREE(smd->verts[i].binds);
+ MEM_freeN(smd->verts[i].binds);
}
}
@@ -1578,7 +1577,7 @@ static void deformVerts(ModifierData *md,
if (smd->defgrp_name[0] != '\0') {
/* Only need to use mesh_src when a vgroup is used. */
- mesh_src = MOD_deform_mesh_eval_get(ctx->object, NULL, mesh, NULL, verts_num, false, false);
+ mesh_src = MOD_deform_mesh_eval_get(ctx->object, NULL, mesh, NULL, verts_num, false);
}
surfacedeformModifier_do(md, ctx, vertexCos, verts_num, ctx->object, mesh_src);
@@ -1600,7 +1599,7 @@ static void deformVertsEM(ModifierData *md,
if (smd->defgrp_name[0] != '\0') {
/* Only need to use mesh_src when a vgroup is used. */
- mesh_src = MOD_deform_mesh_eval_get(ctx->object, em, mesh, NULL, verts_num, false, false);
+ mesh_src = MOD_deform_mesh_eval_get(ctx->object, em, mesh, NULL, verts_num, false);
}
surfacedeformModifier_do(md, ctx, vertexCos, verts_num, ctx->object, mesh_src);
diff --git a/source/blender/modifiers/intern/MOD_util.c b/source/blender/modifiers/intern/MOD_util.c
index 575182a846b..fc17ddffa87 100644
--- a/source/blender/modifiers/intern/MOD_util.c
+++ b/source/blender/modifiers/intern/MOD_util.c
@@ -169,7 +169,6 @@ Mesh *MOD_deform_mesh_eval_get(Object *ob,
Mesh *mesh,
const float (*vertexCos)[3],
const int verts_num,
- const bool use_normals,
const bool use_orco)
{
if (mesh != NULL) {
@@ -217,14 +216,6 @@ Mesh *MOD_deform_mesh_eval_get(Object *ob,
}
}
- /* TODO: Remove this "use_normals" argument, since the caller should retrieve normals afterwards
- * if necessary. */
- if (use_normals) {
- if (LIKELY(mesh)) {
- BKE_mesh_vertex_normals_ensure(mesh);
- }
- }
-
if (mesh && mesh->runtime.wrapper_type == ME_WRAPPER_TYPE_MDATA) {
BLI_assert(mesh->totvert == verts_num);
}
diff --git a/source/blender/modifiers/intern/MOD_util.h b/source/blender/modifiers/intern/MOD_util.h
index b3b75898557..b675c11b370 100644
--- a/source/blender/modifiers/intern/MOD_util.h
+++ b/source/blender/modifiers/intern/MOD_util.h
@@ -42,7 +42,6 @@ struct Mesh *MOD_deform_mesh_eval_get(struct Object *ob,
struct Mesh *mesh,
const float (*vertexCos)[3],
int verts_num,
- bool use_normals,
bool use_orco);
void MOD_get_vgroup(struct Object *ob,
diff --git a/source/blender/modifiers/intern/MOD_warp.c b/source/blender/modifiers/intern/MOD_warp.c
index 5bef19da53a..0968d0646a5 100644
--- a/source/blender/modifiers/intern/MOD_warp.c
+++ b/source/blender/modifiers/intern/MOD_warp.c
@@ -348,7 +348,7 @@ static void deformVerts(ModifierData *md,
if (wmd->defgrp_name[0] != '\0' || wmd->texture != NULL) {
/* mesh_src is only needed for vgroups and textures. */
- mesh_src = MOD_deform_mesh_eval_get(ctx->object, NULL, mesh, NULL, verts_num, false, false);
+ mesh_src = MOD_deform_mesh_eval_get(ctx->object, NULL, mesh, NULL, verts_num, false);
}
warpModifier_do(wmd, ctx, mesh_src, vertexCos, verts_num);
@@ -370,10 +370,10 @@ static void deformVertsEM(ModifierData *md,
if (wmd->defgrp_name[0] != '\0' || wmd->texture != NULL) {
/* mesh_src is only needed for vgroups and textures. */
- mesh_src = MOD_deform_mesh_eval_get(ctx->object, em, mesh, NULL, verts_num, false, false);
+ mesh_src = MOD_deform_mesh_eval_get(ctx->object, em, mesh, NULL, verts_num, false);
}
- /* TODO(Campbell): use edit-mode data only (remove this line). */
+ /* TODO(@campbellbarton): use edit-mode data only (remove this line). */
if (mesh_src != NULL) {
BKE_mesh_wrapper_ensure_mdata(mesh_src);
}
diff --git a/source/blender/modifiers/intern/MOD_wave.c b/source/blender/modifiers/intern/MOD_wave.c
index 136ff6b6d15..9647f47c6e0 100644
--- a/source/blender/modifiers/intern/MOD_wave.c
+++ b/source/blender/modifiers/intern/MOD_wave.c
@@ -302,11 +302,10 @@ static void deformVerts(ModifierData *md,
Mesh *mesh_src = NULL;
if (wmd->flag & MOD_WAVE_NORM) {
- mesh_src = MOD_deform_mesh_eval_get(
- ctx->object, NULL, mesh, vertexCos, verts_num, true, false);
+ mesh_src = MOD_deform_mesh_eval_get(ctx->object, NULL, mesh, vertexCos, verts_num, false);
}
else if (wmd->texture != NULL || wmd->defgrp_name[0] != '\0') {
- mesh_src = MOD_deform_mesh_eval_get(ctx->object, NULL, mesh, NULL, verts_num, false, false);
+ mesh_src = MOD_deform_mesh_eval_get(ctx->object, NULL, mesh, NULL, verts_num, false);
}
waveModifier_do(wmd, ctx, ctx->object, mesh_src, vertexCos, verts_num);
@@ -327,15 +326,13 @@ static void deformVertsEM(ModifierData *md,
Mesh *mesh_src = NULL;
if (wmd->flag & MOD_WAVE_NORM) {
- mesh_src = MOD_deform_mesh_eval_get(
- ctx->object, editData, mesh, vertexCos, verts_num, true, false);
+ mesh_src = MOD_deform_mesh_eval_get(ctx->object, editData, mesh, vertexCos, verts_num, false);
}
else if (wmd->texture != NULL || wmd->defgrp_name[0] != '\0') {
- mesh_src = MOD_deform_mesh_eval_get(
- ctx->object, editData, mesh, NULL, verts_num, false, false);
+ mesh_src = MOD_deform_mesh_eval_get(ctx->object, editData, mesh, NULL, verts_num, false);
}
- /* TODO(Campbell): use edit-mode data only (remove this line). */
+ /* TODO(@campbellbarton): use edit-mode data only (remove this line). */
if (mesh_src != NULL) {
BKE_mesh_wrapper_ensure_mdata(mesh_src);
}
@@ -343,6 +340,12 @@ static void deformVertsEM(ModifierData *md,
waveModifier_do(wmd, ctx, ctx->object, mesh_src, vertexCos, verts_num);
if (!ELEM(mesh_src, NULL, mesh)) {
+ /* Important not to free `vertexCos` owned by the caller. */
+ EditMeshData *edit_data = mesh_src->runtime.edit_data;
+ if (edit_data->vertexCos == vertexCos) {
+ edit_data->vertexCos = NULL;
+ }
+
BKE_id_free(NULL, mesh_src);
}
}
diff --git a/source/blender/modifiers/intern/MOD_weightvgedit.c b/source/blender/modifiers/intern/MOD_weightvgedit.c
index 7ffaa120ba2..8ccf140e665 100644
--- a/source/blender/modifiers/intern/MOD_weightvgedit.c
+++ b/source/blender/modifiers/intern/MOD_weightvgedit.c
@@ -316,7 +316,7 @@ static void panel_draw(const bContext *UNUSED(C), Panel *panel)
sub = uiLayoutRow(sub, true);
uiLayoutSetActive(sub, RNA_boolean_get(ptr, "use_add"));
uiLayoutSetPropSep(sub, false);
- uiItemR(sub, ptr, "add_threshold", UI_ITEM_R_SLIDER, "Threshold", ICON_NONE);
+ uiItemR(sub, ptr, "add_threshold", UI_ITEM_R_SLIDER, IFACE_("Threshold"), ICON_NONE);
uiItemDecoratorR(row, ptr, "add_threshold", 0);
col = uiLayoutColumnWithHeading(layout, false, IFACE_("Group Remove"));
@@ -327,7 +327,7 @@ static void panel_draw(const bContext *UNUSED(C), Panel *panel)
sub = uiLayoutRow(sub, true);
uiLayoutSetActive(sub, RNA_boolean_get(ptr, "use_remove"));
uiLayoutSetPropSep(sub, false);
- uiItemR(sub, ptr, "remove_threshold", UI_ITEM_R_SLIDER, "Threshold", ICON_NONE);
+ uiItemR(sub, ptr, "remove_threshold", UI_ITEM_R_SLIDER, IFACE_("Threshold"), ICON_NONE);
uiItemDecoratorR(row, ptr, "remove_threshold", 0);
uiItemR(layout, ptr, "normalize", 0, NULL, ICON_NONE);
diff --git a/source/blender/nodes/NOD_node_declaration.hh b/source/blender/nodes/NOD_node_declaration.hh
index 4e78f6c1142..d8b8c354230 100644
--- a/source/blender/nodes/NOD_node_declaration.hh
+++ b/source/blender/nodes/NOD_node_declaration.hh
@@ -88,6 +88,14 @@ class SocketDeclaration {
InputSocketFieldType input_field_type_ = InputSocketFieldType::None;
OutputFieldDependency output_field_dependency_;
+ /** The priority of the input for determining the domain of the node. See
+ * realtime_compositor::InputDescriptor for more information. */
+ int compositor_domain_priority_ = 0;
+
+ /** This input expects a single value and can't operate on non-single values. See
+ * realtime_compositor::InputDescriptor for more information. */
+ bool compositor_expects_single_value_ = false;
+
/** Utility method to make the socket available if there is a straightforward way to do so. */
std::function<void(bNode &)> make_available_fn_;
@@ -124,6 +132,9 @@ class SocketDeclaration {
InputSocketFieldType input_field_type() const;
const OutputFieldDependency &output_field_dependency() const;
+ int compositor_domain_priority() const;
+ bool compositor_expects_single_value() const;
+
protected:
void set_common_flags(bNodeSocket &socket) const;
bool matches_common_data(const bNodeSocket &socket) const;
@@ -238,6 +249,22 @@ class SocketDeclarationBuilder : public BaseSocketDeclarationBuilder {
return *(Self *)this;
}
+ /** The priority of the input for determining the domain of the node. See
+ * realtime_compositor::InputDescriptor for more information. */
+ Self &compositor_domain_priority(int priority)
+ {
+ decl_->compositor_domain_priority_ = priority;
+ return *(Self *)this;
+ }
+
+ /** This input expects a single value and can't operate on non-single values. See
+ * realtime_compositor::InputDescriptor for more information. */
+ Self &compositor_expects_single_value(bool value = true)
+ {
+ decl_->compositor_expects_single_value_ = value;
+ return *(Self *)this;
+ }
+
/**
* Pass a function that sets properties on the node required to make the corresponding socket
* available, if it is not available on the default state of the node. The function is allowed to
@@ -428,6 +455,16 @@ inline const OutputFieldDependency &SocketDeclaration::output_field_dependency()
return output_field_dependency_;
}
+inline int SocketDeclaration::compositor_domain_priority() const
+{
+ return compositor_domain_priority_;
+}
+
+inline bool SocketDeclaration::compositor_expects_single_value() const
+{
+ return compositor_expects_single_value_;
+}
+
inline void SocketDeclaration::make_available(bNode &node) const
{
if (make_available_fn_) {
diff --git a/source/blender/nodes/composite/CMakeLists.txt b/source/blender/nodes/composite/CMakeLists.txt
index c0100d77889..2537e8e93cc 100644
--- a/source/blender/nodes/composite/CMakeLists.txt
+++ b/source/blender/nodes/composite/CMakeLists.txt
@@ -10,11 +10,14 @@ set(INC
../../blenlib
../../blentranslation
../../depsgraph
+ ../../functions
+ ../../gpu
../../imbuf
../../makesdna
../../makesrna
../../render
../../windowmanager
+ ../../compositor/realtime_compositor
../../../../intern/guardedalloc
# dna_type_offsets.h
@@ -120,15 +123,19 @@ set(SRC
node_composite_util.hh
)
+set(LIB
+ bf_realtime_compositor
+)
+
if(WITH_IMAGE_OPENEXR)
add_definitions(-DWITH_OPENEXR)
endif()
-if(WITH_COMPOSITOR)
+if(WITH_COMPOSITOR_CPU)
list(APPEND INC
../../compositor
)
- add_definitions(-DWITH_COMPOSITOR)
+ add_definitions(-DWITH_COMPOSITOR_CPU)
endif()
if(WITH_OPENIMAGEDENOISE)
diff --git a/source/blender/nodes/composite/node_composite_tree.cc b/source/blender/nodes/composite/node_composite_tree.cc
index 6efe6f231f5..9792c55b590 100644
--- a/source/blender/nodes/composite/node_composite_tree.cc
+++ b/source/blender/nodes/composite/node_composite_tree.cc
@@ -32,7 +32,7 @@
#include "NOD_composite.h"
#include "node_composite_util.hh"
-#ifdef WITH_COMPOSITOR
+#ifdef WITH_COMPOSITOR_CPU
# include "COM_compositor.h"
#endif
@@ -210,7 +210,7 @@ void ntreeCompositExecTree(Scene *scene,
int do_preview,
const char *view_name)
{
-#ifdef WITH_COMPOSITOR
+#ifdef WITH_COMPOSITOR_CPU
COM_execute(rd, scene, ntree, rendering, view_name);
#else
UNUSED_VARS(scene, ntree, rd, rendering, view_name);
diff --git a/source/blender/nodes/composite/nodes/node_composite_alpha_over.cc b/source/blender/nodes/composite/nodes/node_composite_alpha_over.cc
index d392b810bc1..64c59eb24e3 100644
--- a/source/blender/nodes/composite/nodes/node_composite_alpha_over.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_alpha_over.cc
@@ -8,6 +8,10 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "GPU_material.h"
+
+#include "COM_shader_node.hh"
+
#include "node_composite_util.hh"
/* **************** ALPHAOVER ******************** */
@@ -16,9 +20,18 @@ namespace blender::nodes::node_composite_alpha_over_cc {
static void cmp_node_alphaover_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Float>(N_("Fac")).default_value(1.0f).min(0.0f).max(1.0f).subtype(PROP_FACTOR);
- b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
- b.add_input<decl::Color>(N_("Image"), "Image_001").default_value({1.0f, 1.0f, 1.0f, 1.0f});
+ b.add_input<decl::Float>(N_("Fac"))
+ .default_value(1.0f)
+ .min(0.0f)
+ .max(1.0f)
+ .subtype(PROP_FACTOR)
+ .compositor_domain_priority(2);
+ b.add_input<decl::Color>(N_("Image"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(0);
+ b.add_input<decl::Color>(N_("Image"), "Image_001")
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(1);
b.add_output<decl::Color>(N_("Image"));
}
@@ -36,6 +49,52 @@ static void node_composit_buts_alphaover(uiLayout *layout, bContext *UNUSED(C),
uiItemR(col, ptr, "premul", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class AlphaOverShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ const float premultiply_factor = get_premultiply_factor();
+ if (premultiply_factor != 0.0f) {
+ GPU_stack_link(material,
+ &bnode(),
+ "node_composite_alpha_over_mixed",
+ inputs,
+ outputs,
+ GPU_uniform(&premultiply_factor));
+ return;
+ }
+
+ if (get_use_premultiply()) {
+ GPU_stack_link(material, &bnode(), "node_composite_alpha_over_key", inputs, outputs);
+ return;
+ }
+
+ GPU_stack_link(material, &bnode(), "node_composite_alpha_over_premultiply", inputs, outputs);
+ }
+
+ bool get_use_premultiply()
+ {
+ return bnode().custom1;
+ }
+
+ float get_premultiply_factor()
+ {
+ return ((NodeTwoFloats *)bnode().storage)->x;
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new AlphaOverShaderNode(node);
+}
+
} // namespace blender::nodes::node_composite_alpha_over_cc
void register_node_type_cmp_alphaover()
@@ -50,6 +109,7 @@ void register_node_type_cmp_alphaover()
node_type_init(&ntype, file_ns::node_alphaover_init);
node_type_storage(
&ntype, "NodeTwoFloats", node_free_standard_storage, node_copy_standard_storage);
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_antialiasing.cc b/source/blender/nodes/composite/nodes/node_composite_antialiasing.cc
index f45b678fc50..55fe3366526 100644
--- a/source/blender/nodes/composite/nodes/node_composite_antialiasing.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_antialiasing.cc
@@ -8,6 +8,8 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
/* **************** Anti-Aliasing (SMAA 1x) ******************** */
@@ -42,6 +44,23 @@ static void node_composit_buts_antialiasing(uiLayout *layout, bContext *UNUSED(C
uiItemR(col, ptr, "corner_rounding", 0, nullptr, ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class AntiAliasingOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ get_input("Image").pass_through(get_result("Image"));
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new AntiAliasingOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_antialiasing_cc
void register_node_type_cmp_antialiasing()
@@ -58,6 +77,7 @@ void register_node_type_cmp_antialiasing()
node_type_init(&ntype, file_ns::node_composit_init_antialiasing);
node_type_storage(
&ntype, "NodeAntiAliasingData", node_free_standard_storage, node_copy_standard_storage);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_bilateralblur.cc b/source/blender/nodes/composite/nodes/node_composite_bilateralblur.cc
index ad4a1f701d6..66a321eb088 100644
--- a/source/blender/nodes/composite/nodes/node_composite_bilateralblur.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_bilateralblur.cc
@@ -8,6 +8,8 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
/* **************** BILATERALBLUR ******************** */
@@ -42,6 +44,23 @@ static void node_composit_buts_bilateralblur(uiLayout *layout,
uiItemR(col, ptr, "sigma_space", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class BilateralBlurOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ get_input("Image").pass_through(get_result("Image"));
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new BilateralBlurOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_bilateralblur_cc
void register_node_type_cmp_bilateralblur()
@@ -56,6 +75,7 @@ void register_node_type_cmp_bilateralblur()
node_type_init(&ntype, file_ns::node_composit_init_bilateralblur);
node_type_storage(
&ntype, "NodeBilateralBlurData", node_free_standard_storage, node_copy_standard_storage);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_blur.cc b/source/blender/nodes/composite/nodes/node_composite_blur.cc
index 7beffe15c8e..cb1d93fe10b 100644
--- a/source/blender/nodes/composite/nodes/node_composite_blur.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_blur.cc
@@ -10,6 +10,8 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
/* **************** BLUR ******************** */
@@ -71,6 +73,23 @@ static void node_composit_buts_blur(uiLayout *layout, bContext *UNUSED(C), Point
uiItemR(col, ptr, "use_extended_bounds", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class BlurOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ get_input("Image").pass_through(get_result("Image"));
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new BlurOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_blur_cc
void register_node_type_cmp_blur()
@@ -86,6 +105,7 @@ void register_node_type_cmp_blur()
node_type_init(&ntype, file_ns::node_composit_init_blur);
node_type_storage(
&ntype, "NodeBlurData", node_free_standard_storage, node_copy_standard_storage);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_bokehblur.cc b/source/blender/nodes/composite/nodes/node_composite_bokehblur.cc
index a936bafe671..538f00af12d 100644
--- a/source/blender/nodes/composite/nodes/node_composite_bokehblur.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_bokehblur.cc
@@ -8,6 +8,8 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
/* **************** BLUR ******************** */
@@ -37,6 +39,23 @@ static void node_composit_buts_bokehblur(uiLayout *layout, bContext *UNUSED(C),
uiItemR(layout, ptr, "use_extended_bounds", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class BokehBlurOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ get_input("Image").pass_through(get_result("Image"));
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new BokehBlurOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_bokehblur_cc
void register_node_type_cmp_bokehblur()
@@ -49,6 +68,7 @@ void register_node_type_cmp_bokehblur()
ntype.declare = file_ns::cmp_node_bokehblur_declare;
ntype.draw_buttons = file_ns::node_composit_buts_bokehblur;
node_type_init(&ntype, file_ns::node_composit_init_bokehblur);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_bokehimage.cc b/source/blender/nodes/composite/nodes/node_composite_bokehimage.cc
index 8330c56736a..a11cba37191 100644
--- a/source/blender/nodes/composite/nodes/node_composite_bokehimage.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_bokehimage.cc
@@ -8,6 +8,8 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
/* **************** Bokeh image Tools ******************** */
@@ -45,6 +47,23 @@ static void node_composit_buts_bokehimage(uiLayout *layout, bContext *UNUSED(C),
uiItemR(layout, ptr, "shift", UI_ITEM_R_SPLIT_EMPTY_NAME | UI_ITEM_R_SLIDER, nullptr, ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class BokehImageOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ get_result("Image").allocate_invalid();
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new BokehImageOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_bokehimage_cc
void register_node_type_cmp_bokehimage()
@@ -60,6 +79,7 @@ void register_node_type_cmp_bokehimage()
node_type_init(&ntype, file_ns::node_composit_init_bokehimage);
node_type_storage(
&ntype, "NodeBokehImage", node_free_standard_storage, node_copy_standard_storage);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_boxmask.cc b/source/blender/nodes/composite/nodes/node_composite_boxmask.cc
index f39b69c63f2..9c7bb6432cb 100644
--- a/source/blender/nodes/composite/nodes/node_composite_boxmask.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_boxmask.cc
@@ -5,9 +5,18 @@
* \ingroup cmpnodes
*/
+#include <cmath>
+
+#include "BLI_math_vec_types.hh"
+
#include "UI_interface.h"
#include "UI_resources.h"
+#include "GPU_shader.h"
+
+#include "COM_node_operation.hh"
+#include "COM_utilities.hh"
+
#include "node_composite_util.hh"
/* **************** SCALAR MATH ******************** */
@@ -48,6 +57,98 @@ static void node_composit_buts_boxmask(uiLayout *layout, bContext *UNUSED(C), Po
uiItemR(layout, ptr, "mask_type", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class BoxMaskOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ GPUShader *shader = shader_manager().get(get_shader_name());
+ GPU_shader_bind(shader);
+
+ const Domain domain = compute_domain();
+
+ GPU_shader_uniform_2iv(shader, "domain_size", domain.size);
+
+ GPU_shader_uniform_2fv(shader, "location", get_location());
+ GPU_shader_uniform_2fv(shader, "size", get_size() / 2.0f);
+ GPU_shader_uniform_1f(shader, "cos_angle", std::cos(get_angle()));
+ GPU_shader_uniform_1f(shader, "sin_angle", std::sin(get_angle()));
+
+ const Result &input_mask = get_input("Mask");
+ input_mask.bind_as_texture(shader, "base_mask_tx");
+
+ const Result &value = get_input("Value");
+ value.bind_as_texture(shader, "mask_value_tx");
+
+ Result &output_mask = get_result("Mask");
+ output_mask.allocate_texture(domain);
+ output_mask.bind_as_image(shader, "output_mask_img");
+
+ compute_dispatch_threads_at_least(shader, domain.size);
+
+ input_mask.unbind_as_texture();
+ value.unbind_as_texture();
+ output_mask.unbind_as_image();
+ GPU_shader_unbind();
+ }
+
+ Domain compute_domain() override
+ {
+ if (get_input("Mask").is_single_value()) {
+ return Domain(context().get_output_size());
+ }
+ return get_input("Mask").domain();
+ }
+
+ CMPNodeMaskType get_mask_type()
+ {
+ return (CMPNodeMaskType)bnode().custom1;
+ }
+
+ const char *get_shader_name()
+ {
+ switch (get_mask_type()) {
+ default:
+ case CMP_NODE_MASKTYPE_ADD:
+ return "compositor_box_mask_add";
+ case CMP_NODE_MASKTYPE_SUBTRACT:
+ return "compositor_box_mask_subtract";
+ case CMP_NODE_MASKTYPE_MULTIPLY:
+ return "compositor_box_mask_multiply";
+ case CMP_NODE_MASKTYPE_NOT:
+ return "compositor_box_mask_not";
+ }
+ }
+
+ NodeBoxMask &get_node_box_mask()
+ {
+ return *static_cast<NodeBoxMask *>(bnode().storage);
+ }
+
+ float2 get_location()
+ {
+ return float2(get_node_box_mask().x, get_node_box_mask().y);
+ }
+
+ float2 get_size()
+ {
+ return float2(get_node_box_mask().width, get_node_box_mask().height);
+ }
+
+ float get_angle()
+ {
+ return get_node_box_mask().rotation;
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new BoxMaskOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_boxmask_cc
void register_node_type_cmp_boxmask()
@@ -61,6 +162,7 @@ void register_node_type_cmp_boxmask()
ntype.draw_buttons = file_ns::node_composit_buts_boxmask;
node_type_init(&ntype, file_ns::node_composit_init_boxmask);
node_type_storage(&ntype, "NodeBoxMask", node_free_standard_storage, node_copy_standard_storage);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_brightness.cc b/source/blender/nodes/composite/nodes/node_composite_brightness.cc
index 65ed2885d9b..fa22f551de6 100644
--- a/source/blender/nodes/composite/nodes/node_composite_brightness.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_brightness.cc
@@ -8,6 +8,10 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "GPU_material.h"
+
+#include "COM_shader_node.hh"
+
#include "node_composite_util.hh"
/* **************** Bright and Contrast ******************** */
@@ -16,9 +20,11 @@ namespace blender::nodes::node_composite_brightness_cc {
static void cmp_node_brightcontrast_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
- b.add_input<decl::Float>(N_("Bright")).min(-100.0f).max(100.0f);
- b.add_input<decl::Float>(N_("Contrast")).min(-100.0f).max(100.0f);
+ b.add_input<decl::Color>(N_("Image"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(0);
+ b.add_input<decl::Float>(N_("Bright")).min(-100.0f).max(100.0f).compositor_domain_priority(1);
+ b.add_input<decl::Float>(N_("Contrast")).min(-100.0f).max(100.0f).compositor_domain_priority(2);
b.add_output<decl::Color>(N_("Image"));
}
@@ -34,6 +40,38 @@ static void node_composit_buts_brightcontrast(uiLayout *layout,
uiItemR(layout, ptr, "use_premultiply", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class BrightContrastShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ const float use_premultiply = get_use_premultiply();
+
+ GPU_stack_link(material,
+ &bnode(),
+ "node_composite_bright_contrast",
+ inputs,
+ outputs,
+ GPU_constant(&use_premultiply));
+ }
+
+ bool get_use_premultiply()
+ {
+ return bnode().custom1;
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new BrightContrastShaderNode(node);
+}
+
} // namespace blender::nodes::node_composite_brightness_cc
void register_node_type_cmp_brightcontrast()
@@ -46,6 +84,7 @@ void register_node_type_cmp_brightcontrast()
ntype.declare = file_ns::cmp_node_brightcontrast_declare;
ntype.draw_buttons = file_ns::node_composit_buts_brightcontrast;
node_type_init(&ntype, file_ns::node_composit_init_brightcontrast);
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_channel_matte.cc b/source/blender/nodes/composite/nodes/node_composite_channel_matte.cc
index 627f07fdfce..018632f776c 100644
--- a/source/blender/nodes/composite/nodes/node_composite_channel_matte.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_channel_matte.cc
@@ -10,6 +10,10 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "GPU_material.h"
+
+#include "COM_shader_node.hh"
+
#include "node_composite_util.hh"
/* ******************* Channel Matte Node ********************************* */
@@ -18,7 +22,9 @@ namespace blender::nodes::node_composite_channel_matte_cc {
static void cmp_node_channel_matte_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
+ b.add_input<decl::Color>(N_("Image"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(0);
b.add_output<decl::Color>(N_("Image"));
b.add_output<decl::Float>(N_("Matte"));
}
@@ -79,6 +85,96 @@ static void node_composit_buts_channel_matte(uiLayout *layout,
col, ptr, "limit_min", UI_ITEM_R_SPLIT_EMPTY_NAME | UI_ITEM_R_SLIDER, nullptr, ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class ChannelMatteShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ const float color_space = get_color_space();
+ const float matte_channel = get_matte_channel();
+ float limit_channels[2];
+ get_limit_channels(limit_channels);
+ const float max_limit = get_max_limit();
+ const float min_limit = get_min_limit();
+
+ GPU_stack_link(material,
+ &bnode(),
+ "node_composite_channel_matte",
+ inputs,
+ outputs,
+ GPU_constant(&color_space),
+ GPU_constant(&matte_channel),
+ GPU_constant(limit_channels),
+ GPU_uniform(&max_limit),
+ GPU_uniform(&min_limit));
+ }
+
+ /* 1 -> CMP_NODE_CHANNEL_MATTE_CS_RGB
+ * 2 -> CMP_NODE_CHANNEL_MATTE_CS_HSV
+ * 3 -> CMP_NODE_CHANNEL_MATTE_CS_YUV
+ * 4 -> CMP_NODE_CHANNEL_MATTE_CS_YCC */
+ int get_color_space()
+ {
+ return bnode().custom1;
+ }
+
+ /* Get the index of the channel used to generate the matte. */
+ int get_matte_channel()
+ {
+ return bnode().custom2 - 1;
+ }
+
+ NodeChroma *get_node_chroma()
+ {
+ return static_cast<NodeChroma *>(bnode().storage);
+ }
+
+ /* Get the index of the channel used to compute the limit value. */
+ int get_limit_channel()
+ {
+ return get_node_chroma()->channel - 1;
+ }
+
+ /* Get the indices of the channels used to compute the limit value. We always assume the limit
+ * algorithm is Max, if it is a single limit channel, store it in both limit channels, because
+ * the maximum of two identical values is the same value. */
+ void get_limit_channels(float limit_channels[2])
+ {
+ if (get_node_chroma()->algorithm == CMP_NODE_CHANNEL_MATTE_LIMIT_ALGORITHM_MAX) {
+ /* If the algorithm is Max, store the indices of the other two channels other than the matte
+ * channel. */
+ limit_channels[0] = (get_matte_channel() + 1) % 3;
+ limit_channels[1] = (get_matte_channel() + 2) % 3;
+ }
+ else {
+ /* If the algorithm is Single, store the index of the limit channel in both channels. */
+ limit_channels[0] = get_limit_channel();
+ limit_channels[1] = get_limit_channel();
+ }
+ }
+
+ float get_max_limit()
+ {
+ return get_node_chroma()->t1;
+ }
+
+ float get_min_limit()
+ {
+ return get_node_chroma()->t2;
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new ChannelMatteShaderNode(node);
+}
+
} // namespace blender::nodes::node_composite_channel_matte_cc
void register_node_type_cmp_channel_matte()
@@ -93,6 +189,7 @@ void register_node_type_cmp_channel_matte()
ntype.flag |= NODE_PREVIEW;
node_type_init(&ntype, file_ns::node_composit_init_channel_matte);
node_type_storage(&ntype, "NodeChroma", node_free_standard_storage, node_copy_standard_storage);
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_chroma_matte.cc b/source/blender/nodes/composite/nodes/node_composite_chroma_matte.cc
index 69319c6825d..cb3648c5680 100644
--- a/source/blender/nodes/composite/nodes/node_composite_chroma_matte.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_chroma_matte.cc
@@ -5,11 +5,17 @@
* \ingroup cmpnodes
*/
+#include <cmath>
+
#include "BLI_math_rotation.h"
#include "UI_interface.h"
#include "UI_resources.h"
+#include "GPU_material.h"
+
+#include "COM_shader_node.hh"
+
#include "node_composite_util.hh"
/* ******************* Chroma Key ********************************************************** */
@@ -18,8 +24,12 @@ namespace blender::nodes::node_composite_chroma_matte_cc {
static void cmp_node_chroma_matte_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
- b.add_input<decl::Color>(N_("Key Color")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
+ b.add_input<decl::Color>(N_("Image"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(0);
+ b.add_input<decl::Color>(N_("Key Color"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(1);
b.add_output<decl::Color>(N_("Image"));
b.add_output<decl::Float>(N_("Matte"));
}
@@ -51,6 +61,57 @@ static void node_composit_buts_chroma_matte(uiLayout *layout, bContext *UNUSED(C
// uiItemR(col, ptr, "shadow_adjust", UI_ITEM_R_SLIDER, nullptr, ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class ChromaMatteShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ const float acceptance = get_acceptance();
+ const float cutoff = get_cutoff();
+ const float falloff = get_falloff();
+
+ GPU_stack_link(material,
+ &bnode(),
+ "node_composite_chroma_matte",
+ inputs,
+ outputs,
+ GPU_uniform(&acceptance),
+ GPU_uniform(&cutoff),
+ GPU_uniform(&falloff));
+ }
+
+ NodeChroma *get_node_chroma()
+ {
+ return static_cast<NodeChroma *>(bnode().storage);
+ }
+
+ float get_acceptance()
+ {
+ return std::tan(get_node_chroma()->t1) / 2.0f;
+ }
+
+ float get_cutoff()
+ {
+ return get_node_chroma()->t2;
+ }
+
+ float get_falloff()
+ {
+ return get_node_chroma()->fstrength;
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new ChromaMatteShaderNode(node);
+}
+
} // namespace blender::nodes::node_composite_chroma_matte_cc
void register_node_type_cmp_chroma_matte()
@@ -65,6 +126,7 @@ void register_node_type_cmp_chroma_matte()
ntype.flag |= NODE_PREVIEW;
node_type_init(&ntype, file_ns::node_composit_init_chroma_matte);
node_type_storage(&ntype, "NodeChroma", node_free_standard_storage, node_copy_standard_storage);
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_color_matte.cc b/source/blender/nodes/composite/nodes/node_composite_color_matte.cc
index 474fb1b72f2..5e3aaf512e6 100644
--- a/source/blender/nodes/composite/nodes/node_composite_color_matte.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_color_matte.cc
@@ -8,6 +8,10 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "GPU_material.h"
+
+#include "COM_shader_node.hh"
+
#include "node_composite_util.hh"
/* ******************* Color Matte ********************************************************** */
@@ -16,8 +20,12 @@ namespace blender::nodes::node_composite_color_matte_cc {
static void cmp_node_color_matte_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
- b.add_input<decl::Color>(N_("Key Color")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
+ b.add_input<decl::Color>(N_("Image"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(0);
+ b.add_input<decl::Color>(N_("Key Color"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(1);
b.add_output<decl::Color>(N_("Image"));
b.add_output<decl::Float>(N_("Matte"));
}
@@ -50,6 +58,58 @@ static void node_composit_buts_color_matte(uiLayout *layout, bContext *UNUSED(C)
col, ptr, "color_value", UI_ITEM_R_SPLIT_EMPTY_NAME | UI_ITEM_R_SLIDER, nullptr, ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class ColorMatteShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ const float hue_epsilon = get_hue_epsilon();
+ const float saturation_epsilon = get_saturation_epsilon();
+ const float value_epsilon = get_value_epsilon();
+
+ GPU_stack_link(material,
+ &bnode(),
+ "node_composite_color_matte",
+ inputs,
+ outputs,
+ GPU_uniform(&hue_epsilon),
+ GPU_uniform(&saturation_epsilon),
+ GPU_uniform(&value_epsilon));
+ }
+
+ NodeChroma *get_node_chroma()
+ {
+ return static_cast<NodeChroma *>(bnode().storage);
+ }
+
+ float get_hue_epsilon()
+ {
+ /* Divide by 2 because the hue wraps around. */
+ return get_node_chroma()->t1 / 2.0f;
+ }
+
+ float get_saturation_epsilon()
+ {
+ return get_node_chroma()->t2;
+ }
+
+ float get_value_epsilon()
+ {
+ return get_node_chroma()->t3;
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new ColorMatteShaderNode(node);
+}
+
} // namespace blender::nodes::node_composite_color_matte_cc
void register_node_type_cmp_color_matte()
@@ -64,6 +124,7 @@ void register_node_type_cmp_color_matte()
ntype.flag |= NODE_PREVIEW;
node_type_init(&ntype, file_ns::node_composit_init_color_matte);
node_type_storage(&ntype, "NodeChroma", node_free_standard_storage, node_copy_standard_storage);
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_color_spill.cc b/source/blender/nodes/composite/nodes/node_composite_color_spill.cc
index 9ad5dfbaeb2..9744c01a256 100644
--- a/source/blender/nodes/composite/nodes/node_composite_color_spill.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_color_spill.cc
@@ -10,6 +10,10 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "GPU_material.h"
+
+#include "COM_shader_node.hh"
+
#include "node_composite_util.hh"
/* ******************* Color Spill Suppression ********************************* */
@@ -18,8 +22,15 @@ namespace blender::nodes::node_composite_color_spill_cc {
static void cmp_node_color_spill_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
- b.add_input<decl::Float>(N_("Fac")).default_value(1.0f).min(0.0f).max(1.0f).subtype(PROP_FACTOR);
+ b.add_input<decl::Color>(N_("Image"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(0);
+ b.add_input<decl::Float>(N_("Fac"))
+ .default_value(1.0f)
+ .min(0.0f)
+ .max(1.0f)
+ .subtype(PROP_FACTOR)
+ .compositor_domain_priority(1);
b.add_output<decl::Color>(N_("Image"));
}
@@ -27,8 +38,8 @@ static void node_composit_init_color_spill(bNodeTree *UNUSED(ntree), bNode *node
{
NodeColorspill *ncs = MEM_cnew<NodeColorspill>(__func__);
node->storage = ncs;
+ node->custom2 = CMP_NODE_COLOR_SPILL_LIMIT_ALGORITHM_SINGLE;
node->custom1 = 2; /* green channel */
- node->custom2 = 0; /* simple limit algorithm */
ncs->limchan = 0; /* limit by red */
ncs->limscale = 1.0f; /* limit scaling factor */
ncs->unspill = 0; /* do not use unspill */
@@ -80,6 +91,103 @@ static void node_composit_buts_color_spill(uiLayout *layout, bContext *UNUSED(C)
}
}
+using namespace blender::realtime_compositor;
+
+class ColorSpillShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ const float spill_channel = get_spill_channel();
+ float spill_scale[3];
+ get_spill_scale(spill_scale);
+ float limit_channels[2];
+ get_limit_channels(limit_channels);
+ const float limit_scale = get_limit_scale();
+
+ GPU_stack_link(material,
+ &bnode(),
+ "node_composite_color_spill",
+ inputs,
+ outputs,
+ GPU_constant(&spill_channel),
+ GPU_uniform(spill_scale),
+ GPU_constant(limit_channels),
+ GPU_uniform(&limit_scale));
+ }
+
+ /* Get the index of the channel used for spilling. */
+ int get_spill_channel()
+ {
+ return bnode().custom1 - 1;
+ }
+
+ CMPNodeColorSpillLimitAlgorithm get_limit_algorithm()
+ {
+ return (CMPNodeColorSpillLimitAlgorithm)bnode().custom2;
+ }
+
+ NodeColorspill *get_node_color_spill()
+ {
+ return static_cast<NodeColorspill *>(bnode().storage);
+ }
+
+ void get_spill_scale(float spill_scale[3])
+ {
+ const NodeColorspill *node_color_spill = get_node_color_spill();
+ if (node_color_spill->unspill) {
+ spill_scale[0] = node_color_spill->uspillr;
+ spill_scale[1] = node_color_spill->uspillg;
+ spill_scale[2] = node_color_spill->uspillb;
+ spill_scale[get_spill_channel()] *= -1.0f;
+ }
+ else {
+ spill_scale[0] = 0.0f;
+ spill_scale[1] = 0.0f;
+ spill_scale[2] = 0.0f;
+ spill_scale[get_spill_channel()] = -1.0f;
+ }
+ }
+
+ /* Get the index of the channel used for limiting. */
+ int get_limit_channel()
+ {
+ return get_node_color_spill()->limchan;
+ }
+
+ /* Get the indices of the channels used to compute the limit value. We always assume the limit
+ * algorithm is Average, if it is a single limit channel, store it in both limit channels,
+ * because the average of two identical values is the same value. */
+ void get_limit_channels(float limit_channels[2])
+ {
+ if (get_limit_algorithm() == CMP_NODE_COLOR_SPILL_LIMIT_ALGORITHM_AVERAGE) {
+ /* If the algorithm is Average, store the indices of the other two channels other than the
+ * spill channel. */
+ limit_channels[0] = (get_spill_channel() + 1) % 3;
+ limit_channels[1] = (get_spill_channel() + 2) % 3;
+ }
+ else {
+ /* If the algorithm is Single, store the index of the limit channel in both channels. */
+ limit_channels[0] = get_limit_channel();
+ limit_channels[1] = get_limit_channel();
+ }
+ }
+
+ float get_limit_scale()
+ {
+ return get_node_color_spill()->limscale;
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new ColorSpillShaderNode(node);
+}
+
} // namespace blender::nodes::node_composite_color_spill_cc
void register_node_type_cmp_color_spill()
@@ -94,6 +202,7 @@ void register_node_type_cmp_color_spill()
node_type_init(&ntype, file_ns::node_composit_init_color_spill);
node_type_storage(
&ntype, "NodeColorspill", node_free_standard_storage, node_copy_standard_storage);
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_colorbalance.cc b/source/blender/nodes/composite/nodes/node_composite_colorbalance.cc
index dd081c8fc12..95675169c76 100644
--- a/source/blender/nodes/composite/nodes/node_composite_colorbalance.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_colorbalance.cc
@@ -10,6 +10,10 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "GPU_material.h"
+
+#include "COM_shader_node.hh"
+
#include "node_composite_util.hh"
/* ******************* Color Balance ********************************* */
@@ -46,8 +50,15 @@ namespace blender::nodes::node_composite_colorbalance_cc {
static void cmp_node_colorbalance_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Float>(N_("Fac")).default_value(1.0f).min(0.0f).max(1.0f).subtype(PROP_FACTOR);
- b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
+ b.add_input<decl::Float>(N_("Fac"))
+ .default_value(1.0f)
+ .min(0.0f)
+ .max(1.0f)
+ .subtype(PROP_FACTOR)
+ .compositor_domain_priority(1);
+ b.add_input<decl::Color>(N_("Image"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(0);
b.add_output<decl::Color>(N_("Image"));
}
@@ -71,7 +82,7 @@ static void node_composit_buts_colorbalance(uiLayout *layout, bContext *UNUSED(C
uiItemR(layout, ptr, "correction_method", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
- if (RNA_enum_get(ptr, "correction_method") == 0) {
+ if (RNA_enum_get(ptr, "correction_method") == CMP_NODE_COLOR_BALANCE_LGG) {
split = uiLayoutSplit(layout, 0.0f, false);
col = uiLayoutColumn(split, false);
@@ -116,7 +127,7 @@ static void node_composit_buts_colorbalance_ex(uiLayout *layout,
{
uiItemR(layout, ptr, "correction_method", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
- if (RNA_enum_get(ptr, "correction_method") == 0) {
+ if (RNA_enum_get(ptr, "correction_method") == CMP_NODE_COLOR_BALANCE_LGG) {
uiTemplateColorPicker(layout, ptr, "lift", true, true, false, true);
uiItemR(layout, ptr, "lift", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
@@ -139,6 +150,58 @@ static void node_composit_buts_colorbalance_ex(uiLayout *layout,
}
}
+using namespace blender::realtime_compositor;
+
+class ColorBalanceShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ const NodeColorBalance *node_color_balance = get_node_color_balance();
+
+ if (get_color_balance_method() == CMP_NODE_COLOR_BALANCE_LGG) {
+ GPU_stack_link(material,
+ &bnode(),
+ "node_composite_color_balance_lgg",
+ inputs,
+ outputs,
+ GPU_uniform(node_color_balance->lift),
+ GPU_uniform(node_color_balance->gamma),
+ GPU_uniform(node_color_balance->gain));
+ return;
+ }
+
+ GPU_stack_link(material,
+ &bnode(),
+ "node_composite_color_balance_asc_cdl",
+ inputs,
+ outputs,
+ GPU_uniform(node_color_balance->offset),
+ GPU_uniform(node_color_balance->power),
+ GPU_uniform(node_color_balance->slope),
+ GPU_uniform(&node_color_balance->offset_basis));
+ }
+
+ CMPNodeColorBalanceMethod get_color_balance_method()
+ {
+ return (CMPNodeColorBalanceMethod)bnode().custom1;
+ }
+
+ NodeColorBalance *get_node_color_balance()
+ {
+ return static_cast<NodeColorBalance *>(bnode().storage);
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new ColorBalanceShaderNode(node);
+}
+
} // namespace blender::nodes::node_composite_colorbalance_cc
void register_node_type_cmp_colorbalance()
@@ -155,6 +218,7 @@ void register_node_type_cmp_colorbalance()
node_type_init(&ntype, file_ns::node_composit_init_colorbalance);
node_type_storage(
&ntype, "NodeColorBalance", node_free_standard_storage, node_copy_standard_storage);
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_colorcorrection.cc b/source/blender/nodes/composite/nodes/node_composite_colorcorrection.cc
index 39ecd277cec..36e6672ce1c 100644
--- a/source/blender/nodes/composite/nodes/node_composite_colorcorrection.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_colorcorrection.cc
@@ -5,9 +5,15 @@
* \ingroup cmpnodes
*/
+#include "IMB_colormanagement.h"
+
#include "UI_interface.h"
#include "UI_resources.h"
+#include "GPU_material.h"
+
+#include "COM_shader_node.hh"
+
#include "node_composite_util.hh"
/* ******************* Color Correction ********************************* */
@@ -16,8 +22,14 @@ namespace blender::nodes::node_composite_colorcorrection_cc {
static void cmp_node_colorcorrection_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
- b.add_input<decl::Float>(N_("Mask")).default_value(1.0f).min(0.0f).max(1.0f);
+ b.add_input<decl::Color>(N_("Image"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(0);
+ b.add_input<decl::Float>(N_("Mask"))
+ .default_value(1.0f)
+ .min(0.0f)
+ .max(1.0f)
+ .compositor_domain_priority(1);
b.add_output<decl::Color>(N_("Image"));
}
@@ -266,6 +278,73 @@ static void node_composit_buts_colorcorrection_ex(uiLayout *layout,
uiItemR(row, ptr, "midtones_end", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class ColorCorrectionShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ float enabled_channels[3];
+ get_enabled_channels(enabled_channels);
+ float luminance_coefficients[3];
+ IMB_colormanagement_get_luminance_coefficients(luminance_coefficients);
+
+ const NodeColorCorrection *node_color_correction = get_node_color_correction();
+
+ GPU_stack_link(material,
+ &bnode(),
+ "node_composite_color_correction",
+ inputs,
+ outputs,
+ GPU_constant(enabled_channels),
+ GPU_uniform(&node_color_correction->startmidtones),
+ GPU_uniform(&node_color_correction->endmidtones),
+ GPU_uniform(&node_color_correction->master.saturation),
+ GPU_uniform(&node_color_correction->master.contrast),
+ GPU_uniform(&node_color_correction->master.gamma),
+ GPU_uniform(&node_color_correction->master.gain),
+ GPU_uniform(&node_color_correction->master.lift),
+ GPU_uniform(&node_color_correction->shadows.saturation),
+ GPU_uniform(&node_color_correction->shadows.contrast),
+ GPU_uniform(&node_color_correction->shadows.gamma),
+ GPU_uniform(&node_color_correction->shadows.gain),
+ GPU_uniform(&node_color_correction->shadows.lift),
+ GPU_uniform(&node_color_correction->midtones.saturation),
+ GPU_uniform(&node_color_correction->midtones.contrast),
+ GPU_uniform(&node_color_correction->midtones.gamma),
+ GPU_uniform(&node_color_correction->midtones.gain),
+ GPU_uniform(&node_color_correction->midtones.lift),
+ GPU_uniform(&node_color_correction->highlights.saturation),
+ GPU_uniform(&node_color_correction->highlights.contrast),
+ GPU_uniform(&node_color_correction->highlights.gamma),
+ GPU_uniform(&node_color_correction->highlights.gain),
+ GPU_uniform(&node_color_correction->highlights.lift),
+ GPU_constant(luminance_coefficients));
+ }
+
+ void get_enabled_channels(float enabled_channels[3])
+ {
+ for (int i = 0; i < 3; i++) {
+ enabled_channels[i] = (bnode().custom1 & (1 << i)) ? 1.0f : 0.0f;
+ }
+ }
+
+ NodeColorCorrection *get_node_color_correction()
+ {
+ return static_cast<NodeColorCorrection *>(bnode().storage);
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new ColorCorrectionShaderNode(node);
+}
+
} // namespace blender::nodes::node_composite_colorcorrection_cc
void register_node_type_cmp_colorcorrection()
@@ -282,6 +361,7 @@ void register_node_type_cmp_colorcorrection()
node_type_init(&ntype, file_ns::node_composit_init_colorcorrection);
node_type_storage(
&ntype, "NodeColorCorrection", node_free_standard_storage, node_copy_standard_storage);
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_composite.cc b/source/blender/nodes/composite/nodes/node_composite_composite.cc
index d35ce7dc11a..68061bb434d 100644
--- a/source/blender/nodes/composite/nodes/node_composite_composite.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_composite.cc
@@ -5,9 +5,18 @@
* \ingroup cmpnodes
*/
+#include "BLI_math_vec_types.hh"
+
#include "UI_interface.h"
#include "UI_resources.h"
+#include "GPU_shader.h"
+#include "GPU_state.h"
+#include "GPU_texture.h"
+
+#include "COM_node_operation.hh"
+#include "COM_utilities.hh"
+
#include "node_composite_util.hh"
/* **************** COMPOSITE ******************** */
@@ -26,6 +35,125 @@ static void node_composit_buts_composite(uiLayout *layout, bContext *UNUSED(C),
uiItemR(layout, ptr, "use_alpha", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class CompositeOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ const Result &image = get_input("Image");
+ const Result &alpha = get_input("Alpha");
+
+ if (image.is_single_value() && alpha.is_single_value()) {
+ execute_clear();
+ }
+ else if (ignore_alpha()) {
+ execute_ignore_alpha();
+ }
+ else if (!node().input_by_identifier("Alpha")->is_logically_linked()) {
+ execute_copy();
+ }
+ else {
+ execute_set_alpha();
+ }
+ }
+
+ /* Executes when all inputs are single values, in which case, the output texture can just be
+ * cleared to the appropriate color. */
+ void execute_clear()
+ {
+ const Result &image = get_input("Image");
+ const Result &alpha = get_input("Alpha");
+
+ float4 color = image.get_color_value();
+ if (ignore_alpha()) {
+ color.w = 1.0f;
+ }
+ else if (node().input_by_identifier("Alpha")->is_logically_linked()) {
+ color.w = alpha.get_float_value();
+ }
+
+ GPU_texture_clear(context().get_output_texture(), GPU_DATA_FLOAT, color);
+ }
+
+ /* Executes when the alpha channel of the image is ignored. */
+ void execute_ignore_alpha()
+ {
+ GPUShader *shader = shader_manager().get("compositor_convert_color_to_opaque");
+ GPU_shader_bind(shader);
+
+ const Result &image = get_input("Image");
+ image.bind_as_texture(shader, "input_tx");
+
+ GPUTexture *output_texture = context().get_output_texture();
+ const int image_unit = GPU_shader_get_texture_binding(shader, "output_img");
+ GPU_texture_image_bind(output_texture, image_unit);
+
+ compute_dispatch_threads_at_least(shader, compute_domain().size);
+
+ image.unbind_as_texture();
+ GPU_texture_image_unbind(output_texture);
+ GPU_shader_unbind();
+ }
+
+ /* Executes when the image texture is written with no adjustments and can thus be copied directly
+ * to the output texture. */
+ void execute_copy()
+ {
+ const Result &image = get_input("Image");
+
+ /* Make sure any prior writes to the texture are reflected before copying it. */
+ GPU_memory_barrier(GPU_BARRIER_TEXTURE_UPDATE);
+
+ GPU_texture_copy(context().get_output_texture(), image.texture());
+ }
+
+ /* Executes when the alpha channel of the image is set as the value of the input alpha. */
+ void execute_set_alpha()
+ {
+ GPUShader *shader = shader_manager().get("compositor_set_alpha");
+ GPU_shader_bind(shader);
+
+ const Result &image = get_input("Image");
+ image.bind_as_texture(shader, "image_tx");
+
+ const Result &alpha = get_input("Alpha");
+ alpha.bind_as_texture(shader, "alpha_tx");
+
+ GPUTexture *output_texture = context().get_output_texture();
+ const int image_unit = GPU_shader_get_texture_binding(shader, "output_img");
+ GPU_texture_image_bind(output_texture, image_unit);
+
+ compute_dispatch_threads_at_least(shader, compute_domain().size);
+
+ image.unbind_as_texture();
+ alpha.unbind_as_texture();
+ GPU_texture_image_unbind(output_texture);
+ GPU_shader_unbind();
+ }
+
+ /* If true, the alpha channel of the image is set to 1, that is, it becomes opaque. If false, the
+ * alpha channel of the image is retained, but only if the alpha input is not linked. If the
+ * alpha input is linked, it the value of that input will be used as the alpha of the image. */
+ bool ignore_alpha()
+ {
+ return bnode().custom2 & CMP_NODE_OUTPUT_IGNORE_ALPHA;
+ }
+
+ /* The operation domain have the same dimensions of the output without any transformations. */
+ Domain compute_domain() override
+ {
+ return Domain(context().get_output_size());
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new CompositeOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_composite_cc
void register_node_type_cmp_composite()
@@ -37,6 +165,7 @@ void register_node_type_cmp_composite()
cmp_node_type_base(&ntype, CMP_NODE_COMPOSITE, "Composite", NODE_CLASS_OUTPUT);
ntype.declare = file_ns::cmp_node_composite_declare;
ntype.draw_buttons = file_ns::node_composit_buts_composite;
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
ntype.flag |= NODE_PREVIEW;
ntype.no_muting = true;
diff --git a/source/blender/nodes/composite/nodes/node_composite_convert_color_space.cc b/source/blender/nodes/composite/nodes/node_composite_convert_color_space.cc
index 303248c3852..e36da39cca1 100644
--- a/source/blender/nodes/composite/nodes/node_composite_convert_color_space.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_convert_color_space.cc
@@ -14,6 +14,8 @@
#include "IMB_colormanagement.h"
+#include "COM_node_operation.hh"
+
namespace blender::nodes::node_composite_convert_color_space_cc {
static void CMP_NODE_CONVERT_COLOR_SPACE_declare(NodeDeclarationBuilder &b)
@@ -47,6 +49,23 @@ static void node_composit_buts_convert_colorspace(uiLayout *layout,
uiItemR(layout, ptr, "to_color_space", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class ConvertColorSpaceOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ get_input("Image").pass_through(get_result("Image"));
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new ConvertColorSpaceOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_convert_color_space_cc
void register_node_type_cmp_convert_color_space(void)
@@ -62,6 +81,7 @@ void register_node_type_cmp_convert_color_space(void)
node_type_init(&ntype, file_ns::node_composit_init_convert_colorspace);
node_type_storage(
&ntype, "NodeConvertColorSpace", node_free_standard_storage, node_copy_standard_storage);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_cornerpin.cc b/source/blender/nodes/composite/nodes/node_composite_cornerpin.cc
index 07da0da0be1..9679701a7cf 100644
--- a/source/blender/nodes/composite/nodes/node_composite_cornerpin.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_cornerpin.cc
@@ -5,6 +5,8 @@
* \ingroup cmpnodes
*/
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
namespace blender::nodes::node_composite_cornerpin_cc {
@@ -32,6 +34,24 @@ static void cmp_node_cornerpin_declare(NodeDeclarationBuilder &b)
b.add_output<decl::Float>(N_("Plane"));
}
+using namespace blender::realtime_compositor;
+
+class CornerPinOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ get_input("Image").pass_through(get_result("Image"));
+ get_result("Plane").allocate_invalid();
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new CornerPinOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_cornerpin_cc
void register_node_type_cmp_cornerpin()
@@ -42,6 +62,7 @@ void register_node_type_cmp_cornerpin()
cmp_node_type_base(&ntype, CMP_NODE_CORNERPIN, "Corner Pin", NODE_CLASS_DISTORT);
ntype.declare = file_ns::cmp_node_cornerpin_declare;
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_crop.cc b/source/blender/nodes/composite/nodes/node_composite_crop.cc
index 823e1052dd0..d7331732fc7 100644
--- a/source/blender/nodes/composite/nodes/node_composite_crop.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_crop.cc
@@ -5,11 +5,22 @@
* \ingroup cmpnodes
*/
+#include "BLI_math_base.h"
+#include "BLI_math_vec_types.hh"
+
+#include "DNA_node_types.h"
+
#include "RNA_access.h"
#include "UI_interface.h"
#include "UI_resources.h"
+#include "GPU_shader.h"
+#include "GPU_texture.h"
+
+#include "COM_node_operation.hh"
+#include "COM_utilities.hh"
+
#include "node_composite_util.hh"
/* **************** Crop ******************** */
@@ -18,7 +29,9 @@ namespace blender::nodes::node_composite_crop_cc {
static void cmp_node_crop_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
+ b.add_input<decl::Color>(N_("Image"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(0);
b.add_output<decl::Color>(N_("Image"));
}
@@ -54,6 +67,161 @@ static void node_composit_buts_crop(uiLayout *layout, bContext *UNUSED(C), Point
}
}
+using namespace blender::realtime_compositor;
+
+class CropOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ /* The operation does nothing, so just pass the input through. */
+ if (is_identity()) {
+ get_input("Image").pass_through(get_result("Image"));
+ return;
+ }
+
+ if (get_is_image_crop()) {
+ execute_image_crop();
+ }
+ else {
+ execute_alpha_crop();
+ }
+ }
+
+ /* Crop by replacing areas outside of the cropping bounds with zero alpha. The output have the
+ * same domain as the input image. */
+ void execute_alpha_crop()
+ {
+ GPUShader *shader = shader_manager().get("compositor_alpha_crop");
+ GPU_shader_bind(shader);
+
+ int2 lower_bound, upper_bound;
+ compute_cropping_bounds(lower_bound, upper_bound);
+ GPU_shader_uniform_2iv(shader, "lower_bound", lower_bound);
+ GPU_shader_uniform_2iv(shader, "upper_bound", upper_bound);
+
+ const Result &input_image = get_input("Image");
+ input_image.bind_as_texture(shader, "input_tx");
+
+ const Domain domain = compute_domain();
+
+ Result &output_image = get_result("Image");
+ output_image.allocate_texture(domain);
+ output_image.bind_as_image(shader, "output_img");
+
+ compute_dispatch_threads_at_least(shader, domain.size);
+
+ input_image.unbind_as_texture();
+ output_image.unbind_as_image();
+ GPU_shader_unbind();
+ }
+
+ /* Crop the image into a new size that matches the cropping bounds. */
+ void execute_image_crop()
+ {
+ int2 lower_bound, upper_bound;
+ compute_cropping_bounds(lower_bound, upper_bound);
+
+ /* The image is cropped into nothing, so just return a single zero value. */
+ if (lower_bound.x == upper_bound.x || lower_bound.y == upper_bound.y) {
+ Result &result = get_result("Image");
+ result.allocate_invalid();
+ return;
+ }
+
+ GPUShader *shader = shader_manager().get("compositor_image_crop");
+ GPU_shader_bind(shader);
+
+ GPU_shader_uniform_2iv(shader, "lower_bound", lower_bound);
+
+ const Result &input_image = get_input("Image");
+ input_image.bind_as_texture(shader, "input_tx");
+
+ const int2 size = upper_bound - lower_bound;
+
+ Result &output_image = get_result("Image");
+ output_image.allocate_texture(Domain(size, compute_domain().transformation));
+ output_image.bind_as_image(shader, "output_img");
+
+ compute_dispatch_threads_at_least(shader, size);
+
+ input_image.unbind_as_texture();
+ output_image.unbind_as_image();
+ GPU_shader_unbind();
+ }
+
+ /* If true, the image should actually be cropped into a new size. Otherwise, if false, the region
+ * outside of the cropping bounds will be set to a zero alpha value. */
+ bool get_is_image_crop()
+ {
+ return bnode().custom1;
+ }
+
+ bool get_is_relative()
+ {
+ return bnode().custom2;
+ }
+
+ NodeTwoXYs &get_node_two_xys()
+ {
+ return *static_cast<NodeTwoXYs *>(bnode().storage);
+ }
+
+ /* Returns true if the operation does nothing and the input can be passed through. */
+ bool is_identity()
+ {
+ const Result &input = get_input("Image");
+ /* Single value inputs can't be cropped and are returned as is. */
+ if (input.is_single_value()) {
+ return true;
+ }
+
+ int2 lower_bound, upper_bound;
+ compute_cropping_bounds(lower_bound, upper_bound);
+ const int2 input_size = input.domain().size;
+ /* The cropping bounds cover the whole image, so no cropping happens. */
+ if (lower_bound == int2(0) && upper_bound == input_size) {
+ return true;
+ }
+
+ return false;
+ }
+
+ void compute_cropping_bounds(int2 &lower_bound, int2 &upper_bound)
+ {
+ const NodeTwoXYs &node_two_xys = get_node_two_xys();
+ const int2 input_size = get_input("Image").domain().size;
+
+ if (get_is_relative()) {
+ /* The cropping bounds are relative to the image size. The factors are in the [0, 1] range,
+ * so it is guaranteed that they won't go over the input image size. */
+ lower_bound.x = input_size.x * node_two_xys.fac_x1;
+ lower_bound.y = input_size.y * node_two_xys.fac_y2;
+ upper_bound.x = input_size.x * node_two_xys.fac_x2;
+ upper_bound.y = input_size.y * node_two_xys.fac_y1;
+ }
+ else {
+ /* Make sure the bounds don't go over the input image size. */
+ lower_bound.x = min_ii(node_two_xys.x1, input_size.x);
+ lower_bound.y = min_ii(node_two_xys.y2, input_size.y);
+ upper_bound.x = min_ii(node_two_xys.x2, input_size.x);
+ upper_bound.y = min_ii(node_two_xys.y1, input_size.y);
+ }
+
+ /* Make sure upper bound is actually higher than the lower bound. */
+ lower_bound.x = min_ii(lower_bound.x, upper_bound.x);
+ lower_bound.y = min_ii(lower_bound.y, upper_bound.y);
+ upper_bound.x = max_ii(lower_bound.x, upper_bound.x);
+ upper_bound.y = max_ii(lower_bound.y, upper_bound.y);
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new CropOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_crop_cc
void register_node_type_cmp_crop()
@@ -67,6 +235,7 @@ void register_node_type_cmp_crop()
ntype.draw_buttons = file_ns::node_composit_buts_crop;
node_type_init(&ntype, file_ns::node_composit_init_crop);
node_type_storage(&ntype, "NodeTwoXYs", node_free_standard_storage, node_copy_standard_storage);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_cryptomatte.cc b/source/blender/nodes/composite/nodes/node_composite_cryptomatte.cc
index 2d362a39814..7e5544381a4 100644
--- a/source/blender/nodes/composite/nodes/node_composite_cryptomatte.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_cryptomatte.cc
@@ -26,6 +26,8 @@
#include "RE_pipeline.h"
+#include "COM_node_operation.hh"
+
#include <optional>
/* -------------------------------------------------------------------- */
@@ -105,7 +107,6 @@ static blender::bke::cryptomatte::CryptomatteSessionPtr cryptomatte_init_from_no
return session;
}
-extern "C" {
static CryptomatteEntry *cryptomatte_find(const NodeCryptomatte &n, float encoded_hash)
{
LISTBASE_FOREACH (CryptomatteEntry *, entry, &n.entries) {
@@ -299,6 +300,25 @@ static bool node_poll_cryptomatte(bNodeType *UNUSED(ntype),
return false;
}
+using namespace blender::realtime_compositor;
+
+class CryptoMatteOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ get_input("Image").pass_through(get_result("Image"));
+ get_result("Matte").allocate_invalid();
+ get_result("Pick").allocate_invalid();
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new CryptoMatteOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_cryptomatte_cc
void register_node_type_cmp_cryptomatte()
@@ -316,6 +336,8 @@ void register_node_type_cmp_cryptomatte()
ntype.poll = file_ns::node_poll_cryptomatte;
node_type_storage(
&ntype, "NodeCryptomatte", file_ns::node_free_cryptomatte, file_ns::node_copy_cryptomatte);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
+
nodeRegisterType(&ntype);
}
@@ -350,7 +372,7 @@ int ntreeCompositCryptomatteRemoveSocket(bNodeTree *ntree, bNode *node)
return 1;
}
-namespace blender::nodes::node_composite_cryptomatte_cc {
+namespace blender::nodes::node_composite_legacy_cryptomatte_cc {
static void node_init_cryptomatte_legacy(bNodeTree *ntree, bNode *node)
{
@@ -365,24 +387,43 @@ static void node_init_cryptomatte_legacy(bNodeTree *ntree, bNode *node)
ntreeCompositCryptomatteAddSocket(ntree, node);
}
-} // namespace blender::nodes::node_composite_cryptomatte_cc
+using namespace blender::realtime_compositor;
+
+class CryptoMatteOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ get_input("image").pass_through(get_result("Image"));
+ get_result("Matte").allocate_invalid();
+ get_result("Pick").allocate_invalid();
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new CryptoMatteOperation(context, node);
+}
+
+} // namespace blender::nodes::node_composite_legacy_cryptomatte_cc
void register_node_type_cmp_cryptomatte_legacy()
{
- namespace legacy_file_ns = blender::nodes::node_composite_cryptomatte_cc;
+ namespace legacy_file_ns = blender::nodes::node_composite_legacy_cryptomatte_cc;
namespace file_ns = blender::nodes::node_composite_cryptomatte_cc;
static bNodeType ntype;
cmp_node_type_base(&ntype, CMP_NODE_CRYPTOMATTE_LEGACY, "Cryptomatte", NODE_CLASS_MATTE);
node_type_socket_templates(&ntype, nullptr, file_ns::cmp_node_cryptomatte_out);
- node_type_init(&ntype, file_ns::node_init_cryptomatte_legacy);
+ node_type_init(&ntype, legacy_file_ns::node_init_cryptomatte_legacy);
node_type_storage(
&ntype, "NodeCryptomatte", file_ns::node_free_cryptomatte, file_ns::node_copy_cryptomatte);
ntype.gather_link_search_ops = nullptr;
+ ntype.get_compositor_operation = legacy_file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
/** \} */
-}
diff --git a/source/blender/nodes/composite/nodes/node_composite_curves.cc b/source/blender/nodes/composite/nodes/node_composite_curves.cc
index 802664d7934..c5d303c576a 100644
--- a/source/blender/nodes/composite/nodes/node_composite_curves.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_curves.cc
@@ -5,16 +5,23 @@
* \ingroup cmpnodes
*/
+#include "BLI_math_base.h"
+
#include "BKE_colortools.h"
#include "UI_interface.h"
#include "UI_resources.h"
+#include "GPU_material.h"
+
+#include "COM_node_operation.hh"
+#include "COM_shader_node.hh"
+
#include "node_composite_util.hh"
/* **************** CURVE Time ******************** */
-namespace blender::nodes::node_composite_curves_cc {
+namespace blender::nodes::node_composite_time_curves_cc {
static void cmp_node_time_declare(NodeDeclarationBuilder &b)
{
@@ -29,11 +36,65 @@ static void node_composit_init_curves_time(bNodeTree *UNUSED(ntree), bNode *node
node->storage = BKE_curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f);
}
-} // namespace blender::nodes::node_composite_curves_cc
+using namespace blender::realtime_compositor;
+
+class TimeCurveOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ Result &result = get_result("Fac");
+ result.allocate_single_value();
+
+ CurveMapping *curve_mapping = get_curve_mapping();
+ BKE_curvemapping_init(curve_mapping);
+ const float time = BKE_curvemapping_evaluateF(curve_mapping, 0, compute_normalized_time());
+ result.set_float_value(clamp_f(time, 0.0f, 1.0f));
+ }
+
+ CurveMapping *get_curve_mapping()
+ {
+ return static_cast<CurveMapping *>(bnode().storage);
+ }
+
+ int get_start_time()
+ {
+ return bnode().custom1;
+ }
+
+ int get_end_time()
+ {
+ return bnode().custom2;
+ }
+
+ float compute_normalized_time()
+ {
+ const int frame_number = context().get_frame_number();
+ if (frame_number < get_start_time()) {
+ return 0.0f;
+ }
+ if (frame_number > get_end_time()) {
+ return 1.0f;
+ }
+ if (get_start_time() == get_end_time()) {
+ return 0.0f;
+ }
+ return static_cast<float>(frame_number - get_start_time()) /
+ static_cast<float>(get_end_time() - get_start_time());
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new TimeCurveOperation(context, node);
+}
+
+} // namespace blender::nodes::node_composite_time_curves_cc
void register_node_type_cmp_curve_time()
{
- namespace file_ns = blender::nodes::node_composite_curves_cc;
+ namespace file_ns = blender::nodes::node_composite_time_curves_cc;
static bNodeType ntype;
@@ -42,17 +103,22 @@ void register_node_type_cmp_curve_time()
node_type_size(&ntype, 200, 140, 320);
node_type_init(&ntype, file_ns::node_composit_init_curves_time);
node_type_storage(&ntype, "CurveMapping", node_free_curves, node_copy_curves);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
/* **************** CURVE VEC ******************** */
-namespace blender::nodes::node_composite_curves_cc {
+namespace blender::nodes::node_composite_vector_curves_cc {
static void cmp_node_curve_vec_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Vector>(N_("Vector")).default_value({0.0f, 0.0f, 0.0f}).min(-1.0f).max(1.0f);
+ b.add_input<decl::Vector>(N_("Vector"))
+ .default_value({0.0f, 0.0f, 0.0f})
+ .min(-1.0f)
+ .max(1.0f)
+ .compositor_domain_priority(0);
b.add_output<decl::Vector>(N_("Vector"));
}
@@ -66,11 +132,63 @@ static void node_buts_curvevec(uiLayout *layout, bContext *UNUSED(C), PointerRNA
uiTemplateCurveMapping(layout, ptr, "mapping", 'v', false, false, false, false);
}
-} // namespace blender::nodes::node_composite_curves_cc
+using namespace blender::realtime_compositor;
+
+class VectorCurvesShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ CurveMapping *curve_mapping = get_curve_mapping();
+
+ BKE_curvemapping_init(curve_mapping);
+ float *band_values;
+ int band_size;
+ BKE_curvemapping_table_RGBA(curve_mapping, &band_values, &band_size);
+ float band_layer;
+ GPUNodeLink *band_texture = GPU_color_band(material, band_size, band_values, &band_layer);
+
+ float start_slopes[CM_TOT];
+ float end_slopes[CM_TOT];
+ BKE_curvemapping_compute_slopes(curve_mapping, start_slopes, end_slopes);
+ float range_minimums[CM_TOT];
+ BKE_curvemapping_get_range_minimums(curve_mapping, range_minimums);
+ float range_dividers[CM_TOT];
+ BKE_curvemapping_compute_range_dividers(curve_mapping, range_dividers);
+
+ GPU_stack_link(material,
+ &bnode(),
+ "curves_vector",
+ inputs,
+ outputs,
+ band_texture,
+ GPU_constant(&band_layer),
+ GPU_uniform(range_minimums),
+ GPU_uniform(range_dividers),
+ GPU_uniform(start_slopes),
+ GPU_uniform(end_slopes));
+ }
+
+ CurveMapping *get_curve_mapping()
+ {
+ return static_cast<CurveMapping *>(bnode().storage);
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new VectorCurvesShaderNode(node);
+}
+
+} // namespace blender::nodes::node_composite_vector_curves_cc
void register_node_type_cmp_curve_vec()
{
- namespace file_ns = blender::nodes::node_composite_curves_cc;
+ namespace file_ns = blender::nodes::node_composite_vector_curves_cc;
static bNodeType ntype;
@@ -80,19 +198,26 @@ void register_node_type_cmp_curve_vec()
node_type_size(&ntype, 200, 140, 320);
node_type_init(&ntype, file_ns::node_composit_init_curve_vec);
node_type_storage(&ntype, "CurveMapping", node_free_curves, node_copy_curves);
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
/* **************** CURVE RGB ******************** */
-namespace blender::nodes::node_composite_curves_cc {
+namespace blender::nodes::node_composite_rgb_curves_cc {
static void cmp_node_rgbcurves_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Float>(N_("Fac")).default_value(1.0f).min(-1.0f).max(1.0f).subtype(
- PROP_FACTOR);
- b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
+ b.add_input<decl::Float>(N_("Fac"))
+ .default_value(1.0f)
+ .min(-1.0f)
+ .max(1.0f)
+ .subtype(PROP_FACTOR)
+ .compositor_domain_priority(1);
+ b.add_input<decl::Color>(N_("Image"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(0);
b.add_input<decl::Color>(N_("Black Level")).default_value({0.0f, 0.0f, 0.0f, 1.0f});
b.add_input<decl::Color>(N_("White Level")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
b.add_output<decl::Color>(N_("Image"));
@@ -103,11 +228,105 @@ static void node_composit_init_curve_rgb(bNodeTree *UNUSED(ntree), bNode *node)
node->storage = BKE_curvemapping_add(4, 0.0f, 0.0f, 1.0f, 1.0f);
}
-} // namespace blender::nodes::node_composite_curves_cc
+using namespace blender::realtime_compositor;
+
+class RGBCurvesShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ CurveMapping *curve_mapping = get_curve_mapping();
+
+ BKE_curvemapping_init(curve_mapping);
+ float *band_values;
+ int band_size;
+ BKE_curvemapping_table_RGBA(curve_mapping, &band_values, &band_size);
+ float band_layer;
+ GPUNodeLink *band_texture = GPU_color_band(material, band_size, band_values, &band_layer);
+
+ float start_slopes[CM_TOT];
+ float end_slopes[CM_TOT];
+ BKE_curvemapping_compute_slopes(curve_mapping, start_slopes, end_slopes);
+ float range_minimums[CM_TOT];
+ BKE_curvemapping_get_range_minimums(curve_mapping, range_minimums);
+ float range_dividers[CM_TOT];
+ BKE_curvemapping_compute_range_dividers(curve_mapping, range_dividers);
+
+ if (curve_mapping->tone == CURVE_TONE_FILMLIKE) {
+ GPU_stack_link(material,
+ &bnode(),
+ "curves_film_like",
+ inputs,
+ outputs,
+ band_texture,
+ GPU_constant(&band_layer),
+ GPU_uniform(&range_minimums[3]),
+ GPU_uniform(&range_dividers[3]),
+ GPU_uniform(&start_slopes[3]),
+ GPU_uniform(&end_slopes[3]));
+ return;
+ }
+
+ const float min = 0.0f;
+ const float max = 1.0f;
+ GPU_link(material,
+ "clamp_value",
+ get_input_link("Fac"),
+ GPU_constant(&min),
+ GPU_constant(&max),
+ &get_input("Fac").link);
+
+ /* If the RGB curves do nothing, use a function that skips RGB computations. */
+ if (BKE_curvemapping_is_map_identity(curve_mapping, 0) &&
+ BKE_curvemapping_is_map_identity(curve_mapping, 1) &&
+ BKE_curvemapping_is_map_identity(curve_mapping, 2)) {
+ GPU_stack_link(material,
+ &bnode(),
+ "curves_combined_only",
+ inputs,
+ outputs,
+ band_texture,
+ GPU_constant(&band_layer),
+ GPU_uniform(&range_minimums[3]),
+ GPU_uniform(&range_dividers[3]),
+ GPU_uniform(&start_slopes[3]),
+ GPU_uniform(&end_slopes[3]));
+ return;
+ }
+
+ GPU_stack_link(material,
+ &bnode(),
+ "curves_combined_rgb",
+ inputs,
+ outputs,
+ band_texture,
+ GPU_constant(&band_layer),
+ GPU_uniform(range_minimums),
+ GPU_uniform(range_dividers),
+ GPU_uniform(start_slopes),
+ GPU_uniform(end_slopes));
+ }
+
+ CurveMapping *get_curve_mapping()
+ {
+ return static_cast<CurveMapping *>(bnode().storage);
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new RGBCurvesShaderNode(node);
+}
+
+} // namespace blender::nodes::node_composite_rgb_curves_cc
void register_node_type_cmp_curve_rgb()
{
- namespace file_ns = blender::nodes::node_composite_curves_cc;
+ namespace file_ns = blender::nodes::node_composite_rgb_curves_cc;
static bNodeType ntype;
@@ -116,6 +335,7 @@ void register_node_type_cmp_curve_rgb()
node_type_size(&ntype, 200, 140, 320);
node_type_init(&ntype, file_ns::node_composit_init_curve_rgb);
node_type_storage(&ntype, "CurveMapping", node_free_curves, node_copy_curves);
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_defocus.cc b/source/blender/nodes/composite/nodes/node_composite_defocus.cc
index 83dd397ff1f..94b4908a1bd 100644
--- a/source/blender/nodes/composite/nodes/node_composite_defocus.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_defocus.cc
@@ -12,6 +12,8 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
/* ************ Defocus Node ****************** */
@@ -81,6 +83,23 @@ static void node_composit_buts_defocus(uiLayout *layout, bContext *C, PointerRNA
uiItemR(sub, ptr, "z_scale", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class DefocusOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ get_input("Image").pass_through(get_result("Image"));
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new DefocusOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_defocus_cc
void register_node_type_cmp_defocus()
@@ -94,6 +113,7 @@ void register_node_type_cmp_defocus()
ntype.draw_buttons = file_ns::node_composit_buts_defocus;
node_type_init(&ntype, file_ns::node_composit_init_defocus);
node_type_storage(&ntype, "NodeDefocus", node_free_standard_storage, node_copy_standard_storage);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_denoise.cc b/source/blender/nodes/composite/nodes/node_composite_denoise.cc
index 051a2580ef9..0452e7cd943 100644
--- a/source/blender/nodes/composite/nodes/node_composite_denoise.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_denoise.cc
@@ -10,6 +10,8 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
namespace blender::nodes::node_composite_denoise_cc {
@@ -52,6 +54,23 @@ static void node_composit_buts_denoise(uiLayout *layout, bContext *UNUSED(C), Po
uiItemR(layout, ptr, "use_hdr", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class DenoiseOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ get_input("Image").pass_through(get_result("Image"));
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new DenoiseOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_denoise_cc
void register_node_type_cmp_denoise()
@@ -65,6 +84,7 @@ void register_node_type_cmp_denoise()
ntype.draw_buttons = file_ns::node_composit_buts_denoise;
node_type_init(&ntype, file_ns::node_composit_init_denonise);
node_type_storage(&ntype, "NodeDenoise", node_free_standard_storage, node_copy_standard_storage);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_despeckle.cc b/source/blender/nodes/composite/nodes/node_composite_despeckle.cc
index 66a18cfa369..0b9f9c8f76d 100644
--- a/source/blender/nodes/composite/nodes/node_composite_despeckle.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_despeckle.cc
@@ -8,6 +8,8 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
/* **************** FILTER ******************** */
@@ -36,6 +38,23 @@ static void node_composit_buts_despeckle(uiLayout *layout, bContext *UNUSED(C),
uiItemR(col, ptr, "threshold_neighbor", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class DespeckleOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ get_input("Image").pass_through(get_result("Image"));
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new DespeckleOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_despeckle_cc
void register_node_type_cmp_despeckle()
@@ -49,6 +68,7 @@ void register_node_type_cmp_despeckle()
ntype.draw_buttons = file_ns::node_composit_buts_despeckle;
ntype.flag |= NODE_PREVIEW;
node_type_init(&ntype, file_ns::node_composit_init_despeckle);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_diff_matte.cc b/source/blender/nodes/composite/nodes/node_composite_diff_matte.cc
index b87bbe439db..e129dcaa6ef 100644
--- a/source/blender/nodes/composite/nodes/node_composite_diff_matte.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_diff_matte.cc
@@ -8,6 +8,10 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "GPU_material.h"
+
+#include "COM_shader_node.hh"
+
#include "node_composite_util.hh"
/* ******************* channel Difference Matte ********************************* */
@@ -16,8 +20,12 @@ namespace blender::nodes::node_composite_diff_matte_cc {
static void cmp_node_diff_matte_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Color>(N_("Image 1")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
- b.add_input<decl::Color>(N_("Image 2")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
+ b.add_input<decl::Color>(N_("Image 1"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(0);
+ b.add_input<decl::Color>(N_("Image 2"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(1);
b.add_output<decl::Color>(N_("Image"));
b.add_output<decl::Float>(N_("Matte"));
}
@@ -40,6 +48,50 @@ static void node_composit_buts_diff_matte(uiLayout *layout, bContext *UNUSED(C),
uiItemR(col, ptr, "falloff", UI_ITEM_R_SPLIT_EMPTY_NAME | UI_ITEM_R_SLIDER, nullptr, ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class DifferenceMatteShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ const float tolerance = get_tolerance();
+ const float falloff = get_falloff();
+
+ GPU_stack_link(material,
+ &bnode(),
+ "node_composite_difference_matte",
+ inputs,
+ outputs,
+ GPU_uniform(&tolerance),
+ GPU_uniform(&falloff));
+ }
+
+ NodeChroma *get_node_chroma()
+ {
+ return static_cast<NodeChroma *>(bnode().storage);
+ }
+
+ float get_tolerance()
+ {
+ return get_node_chroma()->t1;
+ }
+
+ float get_falloff()
+ {
+ return get_node_chroma()->t2;
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new DifferenceMatteShaderNode(node);
+}
+
} // namespace blender::nodes::node_composite_diff_matte_cc
void register_node_type_cmp_diff_matte()
@@ -54,6 +106,7 @@ void register_node_type_cmp_diff_matte()
ntype.flag |= NODE_PREVIEW;
node_type_init(&ntype, file_ns::node_composit_init_diff_matte);
node_type_storage(&ntype, "NodeChroma", node_free_standard_storage, node_copy_standard_storage);
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_dilate.cc b/source/blender/nodes/composite/nodes/node_composite_dilate.cc
index 9bdb9ae0837..46199d3ff04 100644
--- a/source/blender/nodes/composite/nodes/node_composite_dilate.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_dilate.cc
@@ -10,6 +10,8 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
/* **************** Dilate/Erode ******************** */
@@ -43,6 +45,23 @@ static void node_composit_buts_dilateerode(uiLayout *layout, bContext *UNUSED(C)
}
}
+using namespace blender::realtime_compositor;
+
+class DilateErodeOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ get_input("Mask").pass_through(get_result("Mask"));
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new DilateErodeOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_dilate_cc
void register_node_type_cmp_dilateerode()
@@ -57,6 +76,7 @@ void register_node_type_cmp_dilateerode()
node_type_init(&ntype, file_ns::node_composit_init_dilateerode);
node_type_storage(
&ntype, "NodeDilateErode", node_free_standard_storage, node_copy_standard_storage);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_directionalblur.cc b/source/blender/nodes/composite/nodes/node_composite_directionalblur.cc
index 3d82ab04fc9..eacba5ad12d 100644
--- a/source/blender/nodes/composite/nodes/node_composite_directionalblur.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_directionalblur.cc
@@ -8,6 +8,8 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
namespace blender::nodes::node_composite_directionalblur_cc {
@@ -51,6 +53,23 @@ static void node_composit_buts_dblur(uiLayout *layout, bContext *UNUSED(C), Poin
uiItemR(layout, ptr, "zoom", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class DirectionalBlurOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ get_input("Image").pass_through(get_result("Image"));
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new DirectionalBlurOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_directionalblur_cc
void register_node_type_cmp_dblur()
@@ -65,6 +84,7 @@ void register_node_type_cmp_dblur()
node_type_init(&ntype, file_ns::node_composit_init_dblur);
node_type_storage(
&ntype, "NodeDBlurData", node_free_standard_storage, node_copy_standard_storage);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_displace.cc b/source/blender/nodes/composite/nodes/node_composite_displace.cc
index 0b0d42cbb08..1049f2fa4a9 100644
--- a/source/blender/nodes/composite/nodes/node_composite_displace.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_displace.cc
@@ -5,6 +5,8 @@
* \ingroup cmpnodes
*/
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
/* **************** Displace ******************** */
@@ -24,6 +26,23 @@ static void cmp_node_displace_declare(NodeDeclarationBuilder &b)
b.add_output<decl::Color>(N_("Image"));
}
+using namespace blender::realtime_compositor;
+
+class DisplaceOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ get_input("Image").pass_through(get_result("Image"));
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new DisplaceOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_displace_cc
void register_node_type_cmp_displace()
@@ -34,6 +53,7 @@ void register_node_type_cmp_displace()
cmp_node_type_base(&ntype, CMP_NODE_DISPLACE, "Displace", NODE_CLASS_DISTORT);
ntype.declare = file_ns::cmp_node_displace_declare;
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_distance_matte.cc b/source/blender/nodes/composite/nodes/node_composite_distance_matte.cc
index a8646d8498e..9d910b3f409 100644
--- a/source/blender/nodes/composite/nodes/node_composite_distance_matte.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_distance_matte.cc
@@ -8,6 +8,10 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "GPU_material.h"
+
+#include "COM_shader_node.hh"
+
#include "node_composite_util.hh"
/* ******************* channel Distance Matte ********************************* */
@@ -16,8 +20,12 @@ namespace blender::nodes::node_composite_distance_matte_cc {
static void cmp_node_distance_matte_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
- b.add_input<decl::Color>(N_("Key Color")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
+ b.add_input<decl::Color>(N_("Image"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(0);
+ b.add_input<decl::Color>(N_("Key Color"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(1);
b.add_output<decl::Color>(N_("Image"));
b.add_output<decl::Float>(N_("Matte"));
}
@@ -26,7 +34,7 @@ static void node_composit_init_distance_matte(bNodeTree *UNUSED(ntree), bNode *n
{
NodeChroma *c = MEM_cnew<NodeChroma>(__func__);
node->storage = c;
- c->channel = 1;
+ c->channel = CMP_NODE_DISTANCE_MATTE_COLOR_SPACE_RGBA;
c->t1 = 0.1f;
c->t2 = 0.1f;
}
@@ -48,6 +56,66 @@ static void node_composit_buts_distance_matte(uiLayout *layout,
uiItemR(col, ptr, "falloff", UI_ITEM_R_SPLIT_EMPTY_NAME | UI_ITEM_R_SLIDER, nullptr, ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class DistanceMatteShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ const float tolerance = get_tolerance();
+ const float falloff = get_falloff();
+
+ if (get_color_space() == CMP_NODE_DISTANCE_MATTE_COLOR_SPACE_RGBA) {
+ GPU_stack_link(material,
+ &bnode(),
+ "node_composite_distance_matte_rgba",
+ inputs,
+ outputs,
+ GPU_uniform(&tolerance),
+ GPU_uniform(&falloff));
+ return;
+ }
+
+ GPU_stack_link(material,
+ &bnode(),
+ "node_composite_distance_matte_ycca",
+ inputs,
+ outputs,
+ GPU_uniform(&tolerance),
+ GPU_uniform(&falloff));
+ }
+
+ NodeChroma *get_node_chroma()
+ {
+ return static_cast<NodeChroma *>(bnode().storage);
+ }
+
+ CMPNodeDistanceMatteColorSpace get_color_space()
+ {
+ return (CMPNodeDistanceMatteColorSpace)get_node_chroma()->channel;
+ }
+
+ float get_tolerance()
+ {
+ return get_node_chroma()->t1;
+ }
+
+ float get_falloff()
+ {
+ return get_node_chroma()->t2;
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new DistanceMatteShaderNode(node);
+}
+
} // namespace blender::nodes::node_composite_distance_matte_cc
void register_node_type_cmp_distance_matte()
@@ -62,6 +130,7 @@ void register_node_type_cmp_distance_matte()
ntype.flag |= NODE_PREVIEW;
node_type_init(&ntype, file_ns::node_composit_init_distance_matte);
node_type_storage(&ntype, "NodeChroma", node_free_standard_storage, node_copy_standard_storage);
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_double_edge_mask.cc b/source/blender/nodes/composite/nodes/node_composite_double_edge_mask.cc
index 9dc2b223618..fec7879ed78 100644
--- a/source/blender/nodes/composite/nodes/node_composite_double_edge_mask.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_double_edge_mask.cc
@@ -8,6 +8,8 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
/* **************** Double Edge Mask ******************** */
@@ -35,6 +37,23 @@ static void node_composit_buts_double_edge_mask(uiLayout *layout,
uiItemR(col, ptr, "edge_mode", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class DoubleEdgeMaskOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ get_input("Inner Mask").pass_through(get_result("Mask"));
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new DoubleEdgeMaskOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_double_edge_mask_cc
void register_node_type_cmp_doubleedgemask()
@@ -46,6 +65,7 @@ void register_node_type_cmp_doubleedgemask()
cmp_node_type_base(&ntype, CMP_NODE_DOUBLEEDGEMASK, "Double Edge Mask", NODE_CLASS_MATTE);
ntype.declare = file_ns::cmp_node_double_edge_mask_declare;
ntype.draw_buttons = file_ns::node_composit_buts_double_edge_mask;
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_ellipsemask.cc b/source/blender/nodes/composite/nodes/node_composite_ellipsemask.cc
index 4da6a0a442e..54dfa00eadd 100644
--- a/source/blender/nodes/composite/nodes/node_composite_ellipsemask.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_ellipsemask.cc
@@ -5,9 +5,18 @@
* \ingroup cmpnodes
*/
+#include <cmath>
+
+#include "BLI_math_vec_types.hh"
+
#include "UI_interface.h"
#include "UI_resources.h"
+#include "GPU_shader.h"
+
+#include "COM_node_operation.hh"
+#include "COM_utilities.hh"
+
#include "node_composite_util.hh"
/* **************** SCALAR MATH ******************** */
@@ -46,6 +55,98 @@ static void node_composit_buts_ellipsemask(uiLayout *layout, bContext *UNUSED(C)
uiItemR(layout, ptr, "mask_type", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class EllipseMaskOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ GPUShader *shader = shader_manager().get(get_shader_name());
+ GPU_shader_bind(shader);
+
+ const Domain domain = compute_domain();
+
+ GPU_shader_uniform_2iv(shader, "domain_size", domain.size);
+
+ GPU_shader_uniform_2fv(shader, "location", get_location());
+ GPU_shader_uniform_2fv(shader, "radius", get_size() / 2.0f);
+ GPU_shader_uniform_1f(shader, "cos_angle", std::cos(get_angle()));
+ GPU_shader_uniform_1f(shader, "sin_angle", std::sin(get_angle()));
+
+ const Result &input_mask = get_input("Mask");
+ input_mask.bind_as_texture(shader, "base_mask_tx");
+
+ const Result &value = get_input("Value");
+ value.bind_as_texture(shader, "mask_value_tx");
+
+ Result &output_mask = get_result("Mask");
+ output_mask.allocate_texture(domain);
+ output_mask.bind_as_image(shader, "output_mask_img");
+
+ compute_dispatch_threads_at_least(shader, domain.size);
+
+ input_mask.unbind_as_texture();
+ value.unbind_as_texture();
+ output_mask.unbind_as_image();
+ GPU_shader_unbind();
+ }
+
+ Domain compute_domain() override
+ {
+ if (get_input("Mask").is_single_value()) {
+ return Domain(context().get_output_size());
+ }
+ return get_input("Mask").domain();
+ }
+
+ CMPNodeMaskType get_mask_type()
+ {
+ return (CMPNodeMaskType)bnode().custom1;
+ }
+
+ const char *get_shader_name()
+ {
+ switch (get_mask_type()) {
+ default:
+ case CMP_NODE_MASKTYPE_ADD:
+ return "compositor_ellipse_mask_add";
+ case CMP_NODE_MASKTYPE_SUBTRACT:
+ return "compositor_ellipse_mask_subtract";
+ case CMP_NODE_MASKTYPE_MULTIPLY:
+ return "compositor_ellipse_mask_multiply";
+ case CMP_NODE_MASKTYPE_NOT:
+ return "compositor_ellipse_mask_not";
+ }
+ }
+
+ NodeEllipseMask &get_node_ellipse_mask()
+ {
+ return *static_cast<NodeEllipseMask *>(bnode().storage);
+ }
+
+ float2 get_location()
+ {
+ return float2(get_node_ellipse_mask().x, get_node_ellipse_mask().y);
+ }
+
+ float2 get_size()
+ {
+ return float2(get_node_ellipse_mask().width, get_node_ellipse_mask().height);
+ }
+
+ float get_angle()
+ {
+ return get_node_ellipse_mask().rotation;
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new EllipseMaskOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_ellipsemask_cc
void register_node_type_cmp_ellipsemask()
@@ -61,6 +162,7 @@ void register_node_type_cmp_ellipsemask()
node_type_init(&ntype, file_ns::node_composit_init_ellipsemask);
node_type_storage(
&ntype, "NodeEllipseMask", node_free_standard_storage, node_copy_standard_storage);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_exposure.cc b/source/blender/nodes/composite/nodes/node_composite_exposure.cc
index 881cfc11058..19b93680ff6 100644
--- a/source/blender/nodes/composite/nodes/node_composite_exposure.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_exposure.cc
@@ -5,6 +5,10 @@
* \ingroup cmpnodes
*/
+#include "GPU_material.h"
+
+#include "COM_shader_node.hh"
+
#include "node_composite_util.hh"
/* **************** Exposure ******************** */
@@ -13,11 +17,33 @@ namespace blender::nodes::node_composite_exposure_cc {
static void cmp_node_exposure_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
- b.add_input<decl::Float>(N_("Exposure")).min(-10.0f).max(10.0f);
+ b.add_input<decl::Color>(N_("Image"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(0);
+ b.add_input<decl::Float>(N_("Exposure")).min(-10.0f).max(10.0f).compositor_domain_priority(1);
b.add_output<decl::Color>(N_("Image"));
}
+using namespace blender::realtime_compositor;
+
+class ExposureShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ GPU_stack_link(material, &bnode(), "node_composite_exposure", inputs, outputs);
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new ExposureShaderNode(node);
+}
+
} // namespace blender::nodes::node_composite_exposure_cc
void register_node_type_cmp_exposure()
@@ -28,6 +54,7 @@ void register_node_type_cmp_exposure()
cmp_node_type_base(&ntype, CMP_NODE_EXPOSURE, "Exposure", NODE_CLASS_OP_COLOR);
ntype.declare = file_ns::cmp_node_exposure_declare;
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_filter.cc b/source/blender/nodes/composite/nodes/node_composite_filter.cc
index c343c21feb2..854cf684806 100644
--- a/source/blender/nodes/composite/nodes/node_composite_filter.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_filter.cc
@@ -8,6 +8,8 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
/* **************** FILTER ******************** */
@@ -26,6 +28,23 @@ static void node_composit_buts_filter(uiLayout *layout, bContext *UNUSED(C), Poi
uiItemR(layout, ptr, "filter_type", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class FilterOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ get_input("Image").pass_through(get_result("Image"));
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new FilterOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_filter_cc
void register_node_type_cmp_filter()
@@ -39,6 +58,7 @@ void register_node_type_cmp_filter()
ntype.draw_buttons = file_ns::node_composit_buts_filter;
ntype.labelfunc = node_filter_label;
ntype.flag |= NODE_PREVIEW;
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_flip.cc b/source/blender/nodes/composite/nodes/node_composite_flip.cc
index 37b9a2d020d..aaa2b565ed2 100644
--- a/source/blender/nodes/composite/nodes/node_composite_flip.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_flip.cc
@@ -5,9 +5,18 @@
* \ingroup cmpnodes
*/
+#include "BLI_assert.h"
+#include "BLI_utildefines.h"
+
#include "UI_interface.h"
#include "UI_resources.h"
+#include "GPU_shader.h"
+#include "GPU_texture.h"
+
+#include "COM_node_operation.hh"
+#include "COM_utilities.hh"
+
#include "node_composite_util.hh"
/* **************** Flip ******************** */
@@ -16,7 +25,9 @@ namespace blender::nodes::node_composite_flip_cc {
static void cmp_node_flip_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
+ b.add_input<decl::Color>(N_("Image"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(0);
b.add_output<decl::Color>(N_("Image"));
}
@@ -25,6 +36,56 @@ static void node_composit_buts_flip(uiLayout *layout, bContext *UNUSED(C), Point
uiItemR(layout, ptr, "axis", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class FlipOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ Result &input = get_input("Image");
+ Result &result = get_result("Image");
+
+ /* Can't flip a single value, pass it through to the output. */
+ if (input.is_single_value()) {
+ input.pass_through(result);
+ return;
+ }
+
+ GPUShader *shader = shader_manager().get("compositor_flip");
+ GPU_shader_bind(shader);
+
+ GPU_shader_uniform_1b(
+ shader, "flip_x", ELEM(get_flip_mode(), CMP_NODE_FLIP_X, CMP_NODE_FLIP_X_Y));
+ GPU_shader_uniform_1b(
+ shader, "flip_y", ELEM(get_flip_mode(), CMP_NODE_FLIP_Y, CMP_NODE_FLIP_X_Y));
+
+ input.bind_as_texture(shader, "input_tx");
+
+ const Domain domain = compute_domain();
+
+ result.allocate_texture(domain);
+ result.bind_as_image(shader, "output_img");
+
+ compute_dispatch_threads_at_least(shader, domain.size);
+
+ input.unbind_as_texture();
+ result.unbind_as_image();
+ GPU_shader_unbind();
+ }
+
+ CMPNodeFlipMode get_flip_mode()
+ {
+ return (CMPNodeFlipMode)bnode().custom1;
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new FlipOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_flip_cc
void register_node_type_cmp_flip()
@@ -36,6 +97,7 @@ void register_node_type_cmp_flip()
cmp_node_type_base(&ntype, CMP_NODE_FLIP, "Flip", NODE_CLASS_DISTORT);
ntype.declare = file_ns::cmp_node_flip_declare;
ntype.draw_buttons = file_ns::node_composit_buts_flip;
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_gamma.cc b/source/blender/nodes/composite/nodes/node_composite_gamma.cc
index b4b8502e915..660d8068231 100644
--- a/source/blender/nodes/composite/nodes/node_composite_gamma.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_gamma.cc
@@ -5,6 +5,10 @@
* \ingroup cmpnodes
*/
+#include "GPU_material.h"
+
+#include "COM_shader_node.hh"
+
#include "node_composite_util.hh"
/* **************** Gamma Tools ******************** */
@@ -13,15 +17,38 @@ namespace blender::nodes::node_composite_gamma_cc {
static void cmp_node_gamma_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
+ b.add_input<decl::Color>(N_("Image"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(0);
b.add_input<decl::Float>(N_("Gamma"))
.default_value(1.0f)
.min(0.001f)
.max(10.0f)
- .subtype(PROP_UNSIGNED);
+ .subtype(PROP_UNSIGNED)
+ .compositor_domain_priority(1);
b.add_output<decl::Color>(N_("Image"));
}
+using namespace blender::realtime_compositor;
+
+class GammaShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ GPU_stack_link(material, &bnode(), "node_composite_gamma", inputs, outputs);
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new GammaShaderNode(node);
+}
+
} // namespace blender::nodes::node_composite_gamma_cc
void register_node_type_cmp_gamma()
@@ -32,6 +59,7 @@ void register_node_type_cmp_gamma()
cmp_node_type_base(&ntype, CMP_NODE_GAMMA, "Gamma", NODE_CLASS_OP_COLOR);
ntype.declare = file_ns::cmp_node_gamma_declare;
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_glare.cc b/source/blender/nodes/composite/nodes/node_composite_glare.cc
index 7f21d30cfa6..33577d5caf8 100644
--- a/source/blender/nodes/composite/nodes/node_composite_glare.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_glare.cc
@@ -10,6 +10,8 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
namespace blender::nodes::node_composite_glare_cc {
@@ -75,6 +77,23 @@ static void node_composit_buts_glare(uiLayout *layout, bContext *UNUSED(C), Poin
}
}
+using namespace blender::realtime_compositor;
+
+class GlareOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ get_input("Image").pass_through(get_result("Image"));
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new GlareOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_glare_cc
void register_node_type_cmp_glare()
@@ -88,6 +107,7 @@ void register_node_type_cmp_glare()
ntype.draw_buttons = file_ns::node_composit_buts_glare;
node_type_init(&ntype, file_ns::node_composit_init_glare);
node_type_storage(&ntype, "NodeGlare", node_free_standard_storage, node_copy_standard_storage);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_hue_sat_val.cc b/source/blender/nodes/composite/nodes/node_composite_hue_sat_val.cc
index 08a048829df..091864a06f7 100644
--- a/source/blender/nodes/composite/nodes/node_composite_hue_sat_val.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_hue_sat_val.cc
@@ -5,6 +5,10 @@
* \ingroup cmpnodes
*/
+#include "GPU_material.h"
+
+#include "COM_shader_node.hh"
+
#include "node_composite_util.hh"
/* **************** Hue Saturation ******************** */
@@ -13,22 +17,56 @@ namespace blender::nodes::node_composite_hue_sat_val_cc {
static void cmp_node_huesatval_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
- b.add_input<decl::Float>(N_("Hue")).default_value(0.5f).min(0.0f).max(1.0f).subtype(PROP_FACTOR);
+ b.add_input<decl::Color>(N_("Image"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(0);
+ b.add_input<decl::Float>(N_("Hue"))
+ .default_value(0.5f)
+ .min(0.0f)
+ .max(1.0f)
+ .subtype(PROP_FACTOR)
+ .compositor_domain_priority(1);
b.add_input<decl::Float>(N_("Saturation"))
.default_value(1.0f)
.min(0.0f)
.max(2.0f)
- .subtype(PROP_FACTOR);
+ .subtype(PROP_FACTOR)
+ .compositor_domain_priority(2);
b.add_input<decl::Float>(N_("Value"))
.default_value(1.0f)
.min(0.0f)
.max(2.0f)
- .subtype(PROP_FACTOR);
- b.add_input<decl::Float>(N_("Fac")).default_value(1.0f).min(0.0f).max(1.0f).subtype(PROP_FACTOR);
+ .subtype(PROP_FACTOR)
+ .compositor_domain_priority(3);
+ b.add_input<decl::Float>(N_("Fac"))
+ .default_value(1.0f)
+ .min(0.0f)
+ .max(1.0f)
+ .subtype(PROP_FACTOR)
+ .compositor_domain_priority(4);
b.add_output<decl::Color>(N_("Image"));
}
+using namespace blender::realtime_compositor;
+
+class HueSaturationValueShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ GPU_stack_link(material, &bnode(), "node_composite_hue_saturation_value", inputs, outputs);
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new HueSaturationValueShaderNode(node);
+}
+
} // namespace blender::nodes::node_composite_hue_sat_val_cc
void register_node_type_cmp_hue_sat()
@@ -39,6 +77,7 @@ void register_node_type_cmp_hue_sat()
cmp_node_type_base(&ntype, CMP_NODE_HUE_SAT, "Hue Saturation Value", NODE_CLASS_OP_COLOR);
ntype.declare = file_ns::cmp_node_huesatval_declare;
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_huecorrect.cc b/source/blender/nodes/composite/nodes/node_composite_huecorrect.cc
index d252d96f8c3..a84420231aa 100644
--- a/source/blender/nodes/composite/nodes/node_composite_huecorrect.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_huecorrect.cc
@@ -5,6 +5,12 @@
* \ingroup cmpnodes
*/
+#include "BKE_colortools.h"
+
+#include "GPU_material.h"
+
+#include "COM_shader_node.hh"
+
#include "node_composite_util.hh"
#include "BKE_colortools.h"
@@ -13,8 +19,15 @@ namespace blender::nodes::node_composite_huecorrect_cc {
static void cmp_node_huecorrect_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Float>(N_("Fac")).default_value(1.0f).min(0.0f).max(1.0f).subtype(PROP_FACTOR);
- b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
+ b.add_input<decl::Float>(N_("Fac"))
+ .default_value(1.0f)
+ .min(0.0f)
+ .max(1.0f)
+ .subtype(PROP_FACTOR)
+ .compositor_domain_priority(1);
+ b.add_input<decl::Color>(N_("Image"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(0);
b.add_output<decl::Color>(N_("Image"));
}
@@ -35,6 +48,53 @@ static void node_composit_init_huecorrect(bNodeTree *UNUSED(ntree), bNode *node)
cumapping->cur = 1;
}
+using namespace blender::realtime_compositor;
+
+class HueCorrectShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ CurveMapping *curve_mapping = get_curve_mapping();
+
+ BKE_curvemapping_init(curve_mapping);
+ float *band_values;
+ int band_size;
+ BKE_curvemapping_table_RGBA(curve_mapping, &band_values, &band_size);
+ float band_layer;
+ GPUNodeLink *band_texture = GPU_color_band(material, band_size, band_values, &band_layer);
+
+ float range_minimums[CM_TOT];
+ BKE_curvemapping_get_range_minimums(curve_mapping, range_minimums);
+ float range_dividers[CM_TOT];
+ BKE_curvemapping_compute_range_dividers(curve_mapping, range_dividers);
+
+ GPU_stack_link(material,
+ &bnode(),
+ "node_composite_hue_correct",
+ inputs,
+ outputs,
+ band_texture,
+ GPU_constant(&band_layer),
+ GPU_uniform(range_minimums),
+ GPU_uniform(range_dividers));
+ }
+
+ CurveMapping *get_curve_mapping()
+ {
+ return static_cast<CurveMapping *>(bnode().storage);
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new HueCorrectShaderNode(node);
+}
+
} // namespace blender::nodes::node_composite_huecorrect_cc
void register_node_type_cmp_huecorrect()
@@ -48,6 +108,7 @@ void register_node_type_cmp_huecorrect()
node_type_size(&ntype, 320, 140, 500);
node_type_init(&ntype, file_ns::node_composit_init_huecorrect);
node_type_storage(&ntype, "CurveMapping", node_free_curves, node_copy_curves);
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_id_mask.cc b/source/blender/nodes/composite/nodes/node_composite_id_mask.cc
index 25ab9aa63fc..ac8456cb931 100644
--- a/source/blender/nodes/composite/nodes/node_composite_id_mask.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_id_mask.cc
@@ -8,6 +8,8 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
/* **************** ID Mask ******************** */
@@ -26,6 +28,23 @@ static void node_composit_buts_id_mask(uiLayout *layout, bContext *UNUSED(C), Po
uiItemR(layout, ptr, "use_antialiasing", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class IDMaskOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ get_input("ID value").pass_through(get_result("Alpha"));
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new IDMaskOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_id_mask_cc
void register_node_type_cmp_idmask()
@@ -37,6 +56,7 @@ void register_node_type_cmp_idmask()
cmp_node_type_base(&ntype, CMP_NODE_ID_MASK, "ID Mask", NODE_CLASS_CONVERTER);
ntype.declare = file_ns::cmp_node_idmask_declare;
ntype.draw_buttons = file_ns::node_composit_buts_id_mask;
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_image.cc b/source/blender/nodes/composite/nodes/node_composite_image.cc
index d75aa575395..d8852e9333f 100644
--- a/source/blender/nodes/composite/nodes/node_composite_image.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_image.cc
@@ -8,6 +8,7 @@
#include "node_composite_util.hh"
#include "BLI_linklist.h"
+#include "BLI_math_vec_types.hh"
#include "BLI_utildefines.h"
#include "BKE_context.h"
@@ -17,6 +18,8 @@
#include "BKE_main.h"
#include "BKE_scene.h"
+#include "DEG_depsgraph_query.h"
+
#include "DNA_scene_types.h"
#include "RE_engine.h"
@@ -27,6 +30,12 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "GPU_shader.h"
+#include "GPU_texture.h"
+
+#include "COM_node_operation.hh"
+#include "COM_utilities.hh"
+
/* **************** IMAGE (and RenderResult, multilayer image) ******************** */
static bNodeSocketTemplate cmp_node_rlayers_out[] = {
@@ -433,6 +442,215 @@ static void node_composit_copy_image(bNodeTree *UNUSED(dest_ntree),
}
}
+using namespace blender::realtime_compositor;
+
+class ImageOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ if (!is_valid()) {
+ allocate_invalid();
+ return;
+ }
+
+ update_image_frame_number();
+
+ for (const OutputSocketRef *output : node()->outputs()) {
+ compute_output(output->identifier());
+ }
+ }
+
+ /* Returns true if the node results can be computed, otherwise, returns false. */
+ bool is_valid()
+ {
+ Image *image = get_image();
+ ImageUser *image_user = get_image_user();
+ if (!image || !image_user) {
+ return false;
+ }
+
+ if (BKE_image_is_multilayer(image)) {
+ if (!image->rr) {
+ return false;
+ }
+
+ RenderLayer *render_layer = get_render_layer();
+ if (!render_layer) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /* Allocate all needed outputs as invalid. This should be called when is_valid returns false. */
+ void allocate_invalid()
+ {
+ for (const OutputSocketRef *output : node()->outputs()) {
+ if (!should_compute_output(output->identifier())) {
+ continue;
+ }
+
+ Result &result = get_result(output->identifier());
+ result.allocate_invalid();
+ }
+ }
+
+ /* Compute the effective frame number of the image if it was animated and invalidate the cached
+ * GPU texture if the computed frame number is different. */
+ void update_image_frame_number()
+ {
+ BKE_image_user_frame_calc(get_image(), get_image_user(), context().get_frame_number());
+ }
+
+ void compute_output(StringRef identifier)
+ {
+ if (!should_compute_output(identifier)) {
+ return;
+ }
+
+ ImageUser image_user = compute_image_user_for_output(identifier);
+ GPUTexture *image_texture = BKE_image_get_gpu_texture(get_image(), &image_user, nullptr);
+
+ const int2 size = int2(GPU_texture_width(image_texture), GPU_texture_height(image_texture));
+ Result &result = get_result(identifier);
+ result.allocate_texture(Domain(size));
+
+ GPUShader *shader = shader_manager().get(get_shader_name(identifier));
+ GPU_shader_bind(shader);
+
+ const int input_unit = GPU_shader_get_texture_binding(shader, "input_tx");
+ GPU_texture_bind(image_texture, input_unit);
+
+ result.bind_as_image(shader, "output_img");
+
+ compute_dispatch_threads_at_least(shader, size);
+
+ GPU_shader_unbind();
+ GPU_texture_unbind(image_texture);
+ result.unbind_as_image();
+ }
+
+ /* Get a copy of the image user that is appropriate to retrieve the image buffer for the output
+ * with the given identifier. This essentially sets the appropriate pass and view indices that
+ * corresponds to the output. */
+ ImageUser compute_image_user_for_output(StringRef identifier)
+ {
+ ImageUser image_user = *get_image_user();
+
+ /* Set the needed view. */
+ image_user.view = get_view_index();
+
+ /* Set the needed pass. */
+ if (BKE_image_is_multilayer(get_image())) {
+ image_user.pass = get_pass_index(get_pass_name(identifier));
+ BKE_image_multilayer_index(get_image()->rr, &image_user);
+ }
+ else {
+ BKE_image_multiview_index(get_image(), &image_user);
+ }
+
+ return image_user;
+ }
+
+ /* Get the shader that should be used to compute the output with the given identifier. The
+ * shaders just copy the retrieved image textures into the results except for the alpha output,
+ * which extracts the alpha and writes it to the result instead. Note that a call to a host
+ * texture copy doesn't work because results are stored in a different half float formats. */
+ const char *get_shader_name(StringRef identifier)
+ {
+ if (identifier == "Alpha") {
+ return "compositor_extract_alpha_from_color";
+ }
+ else if (get_result(identifier).type() == ResultType::Color) {
+ return "compositor_convert_color_to_half_color";
+ }
+ else {
+ return "compositor_convert_float_to_half_float";
+ }
+ }
+
+ Image *get_image()
+ {
+ return (Image *)bnode().id;
+ }
+
+ ImageUser *get_image_user()
+ {
+ return static_cast<ImageUser *>(bnode().storage);
+ }
+
+ /* Get the render layer selected in the node assuming the image is a multilayer image. */
+ RenderLayer *get_render_layer()
+ {
+ const ListBase *layers = &get_image()->rr->layers;
+ return static_cast<RenderLayer *>(BLI_findlink(layers, get_image_user()->layer));
+ }
+
+ /* Get the name of the pass corresponding to the output with the given identifier assuming the
+ * image is a multilayer image. */
+ const char *get_pass_name(StringRef identifier)
+ {
+ DOutputSocket output = node().output_by_identifier(identifier);
+ return static_cast<NodeImageLayer *>(output->bsocket()->storage)->pass_name;
+ }
+
+ /* Get the index of the pass with the given name in the selected render layer's passes list
+ * assuming the image is a multilayer image. */
+ int get_pass_index(const char *name)
+ {
+ return BLI_findstringindex(&get_render_layer()->passes, name, offsetof(RenderPass, name));
+ }
+
+ /* Get the index of the view selected in the node. If the image is not a multi-view image or only
+ * has a single view, then zero is returned. Otherwise, if the image is a multi-view image, the
+ * index of the selected view is returned. However, note that the value of the view member of the
+ * image user is not the actual index of the view. More specifically, the index 0 is reserved to
+ * denote the special mode of operation "All", which dynamically selects the view whose name
+ * matches the view currently being rendered. It follows that the views are then indexed starting
+ * from 1. So for non zero view values, the actual index of the view is the value of the view
+ * member of the image user minus 1. */
+ int get_view_index()
+ {
+ /* The image is not a multi-view image, so just return zero. */
+ if (!BKE_image_is_multiview(get_image())) {
+ return 0;
+ }
+
+ const ListBase *views = &get_image()->rr->views;
+ /* There is only one view and its index is 0. */
+ if (BLI_listbase_count_at_most(views, 2) < 2) {
+ return 0;
+ }
+
+ const int view = get_image_user()->view;
+ /* The view is not zero, which means it is manually specified and the actual index is then the
+ * view value minus 1. */
+ if (view != 0) {
+ return view - 1;
+ }
+
+ /* Otherwise, the view value is zero, denoting the special mode of operation "All", which finds
+ * the index of the view whose name matches the view currently being rendered. */
+ const char *view_name = context().get_view_name().data();
+ const int matched_view = BLI_findstringindex(views, view_name, offsetof(RenderView, name));
+
+ /* No view matches the view currently being rendered, so fallback to the first view. */
+ if (matched_view == -1) {
+ return 0;
+ }
+
+ return matched_view;
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new ImageOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_image_cc
void register_node_type_cmp_image()
@@ -446,6 +664,7 @@ void register_node_type_cmp_image()
node_type_storage(
&ntype, "ImageUser", file_ns::node_composit_free_image, file_ns::node_composit_copy_image);
node_type_update(&ntype, file_ns::cmp_node_image_update);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
ntype.labelfunc = node_image_label;
ntype.flag |= NODE_PREVIEW;
@@ -469,7 +688,7 @@ const char *node_cmp_rlayers_sock_to_pass(int sock_index)
return (STREQ(name, "Alpha")) ? RE_PASSNAME_COMBINED : name;
}
-namespace blender::nodes::node_composite_image_cc {
+namespace blender::nodes::node_composite_render_layer_cc {
static void node_composit_init_rlayers(const bContext *C, PointerRNA *ptr)
{
@@ -595,11 +814,60 @@ static void node_composit_buts_viewlayers(uiLayout *layout, bContext *C, Pointer
RNA_string_set(&op_ptr, "scene", scene_name);
}
-} // namespace blender::nodes::node_composite_image_cc
+using namespace blender::realtime_compositor;
+
+class RenderLayerOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ const int view_layer = bnode().custom1;
+ GPUTexture *pass_texture = context().get_input_texture(view_layer, SCE_PASS_COMBINED);
+ const int2 size = int2(GPU_texture_width(pass_texture), GPU_texture_height(pass_texture));
+
+ /* Compute image output. */
+ Result &image_result = get_result("Image");
+ image_result.allocate_texture(Domain(size));
+ GPU_texture_copy(image_result.texture(), pass_texture);
+
+ /* Compute alpha output. */
+ Result &alpha_result = get_result("Alpha");
+ alpha_result.allocate_texture(Domain(size));
+
+ GPUShader *shader = shader_manager().get("compositor_extract_alpha_from_color");
+ GPU_shader_bind(shader);
+
+ const int input_unit = GPU_shader_get_texture_binding(shader, "input_tx");
+ GPU_texture_bind(pass_texture, input_unit);
+
+ alpha_result.bind_as_image(shader, "output_img");
+
+ compute_dispatch_threads_at_least(shader, size);
+
+ GPU_shader_unbind();
+ GPU_texture_unbind(pass_texture);
+ alpha_result.unbind_as_image();
+
+ /* Other output passes are not supported for now, so allocate them as invalid. */
+ for (const OutputSocketRef *output : node()->outputs()) {
+ if (output->identifier() != "Image" && output->identifier() != "Alpha") {
+ get_result(output->identifier()).allocate_invalid();
+ }
+ }
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new RenderLayerOperation(context, node);
+}
+
+} // namespace blender::nodes::node_composite_render_layer_cc
void register_node_type_cmp_rlayers()
{
- namespace file_ns = blender::nodes::node_composite_image_cc;
+ namespace file_ns = blender::nodes::node_composite_render_layer_cc;
static bNodeType ntype;
@@ -608,6 +876,7 @@ void register_node_type_cmp_rlayers()
ntype.draw_buttons = file_ns::node_composit_buts_viewlayers;
ntype.initfunc_api = file_ns::node_composit_init_rlayers;
ntype.poll = file_ns::node_composit_poll_rlayers;
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
ntype.flag |= NODE_PREVIEW;
node_type_storage(
&ntype, nullptr, file_ns::node_composit_free_rlayers, file_ns::node_composit_copy_rlayers);
diff --git a/source/blender/nodes/composite/nodes/node_composite_inpaint.cc b/source/blender/nodes/composite/nodes/node_composite_inpaint.cc
index 2958d1b2869..f6e46bef299 100644
--- a/source/blender/nodes/composite/nodes/node_composite_inpaint.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_inpaint.cc
@@ -8,6 +8,8 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
/* **************** Inpaint/ ******************** */
@@ -25,6 +27,23 @@ static void node_composit_buts_inpaint(uiLayout *layout, bContext *UNUSED(C), Po
uiItemR(layout, ptr, "distance", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class InpaintOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ get_input("Image").pass_through(get_result("Image"));
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new InpaintOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_inpaint_cc
void register_node_type_cmp_inpaint()
@@ -36,6 +55,7 @@ void register_node_type_cmp_inpaint()
cmp_node_type_base(&ntype, CMP_NODE_INPAINT, "Inpaint", NODE_CLASS_OP_FILTER);
ntype.declare = file_ns::cmp_node_inpaint_declare;
ntype.draw_buttons = file_ns::node_composit_buts_inpaint;
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_invert.cc b/source/blender/nodes/composite/nodes/node_composite_invert.cc
index 6dff043537a..4bfcc7b6b9c 100644
--- a/source/blender/nodes/composite/nodes/node_composite_invert.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_invert.cc
@@ -8,6 +8,10 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "GPU_material.h"
+
+#include "COM_shader_node.hh"
+
#include "node_composite_util.hh"
/* **************** INVERT ******************** */
@@ -16,8 +20,15 @@ namespace blender::nodes::node_composite_invert_cc {
static void cmp_node_invert_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Float>(N_("Fac")).default_value(1.0f).min(0.0f).max(1.0f).subtype(PROP_FACTOR);
- b.add_input<decl::Color>(N_("Color")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
+ b.add_input<decl::Float>(N_("Fac"))
+ .default_value(1.0f)
+ .min(0.0f)
+ .max(1.0f)
+ .subtype(PROP_FACTOR)
+ .compositor_domain_priority(1);
+ b.add_input<decl::Color>(N_("Color"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(0);
b.add_output<decl::Color>(N_("Color"));
}
@@ -35,6 +46,45 @@ static void node_composit_buts_invert(uiLayout *layout, bContext *UNUSED(C), Poi
uiItemR(col, ptr, "invert_alpha", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class InvertShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ const float do_rgb = get_do_rgb();
+ const float do_alpha = get_do_alpha();
+
+ GPU_stack_link(material,
+ &bnode(),
+ "node_composite_invert",
+ inputs,
+ outputs,
+ GPU_constant(&do_rgb),
+ GPU_constant(&do_alpha));
+ }
+
+ bool get_do_rgb()
+ {
+ return bnode().custom1 & CMP_CHAN_RGB;
+ }
+
+ bool get_do_alpha()
+ {
+ return bnode().custom1 & CMP_CHAN_A;
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new InvertShaderNode(node);
+}
+
} // namespace blender::nodes::node_composite_invert_cc
void register_node_type_cmp_invert()
@@ -47,6 +97,7 @@ void register_node_type_cmp_invert()
ntype.declare = file_ns::cmp_node_invert_declare;
ntype.draw_buttons = file_ns::node_composit_buts_invert;
node_type_init(&ntype, file_ns::node_composit_init_invert);
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_keying.cc b/source/blender/nodes/composite/nodes/node_composite_keying.cc
index fbfdf2ad3c6..8b584e216cd 100644
--- a/source/blender/nodes/composite/nodes/node_composite_keying.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_keying.cc
@@ -14,6 +14,8 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
/* **************** Keying ******************** */
@@ -63,6 +65,25 @@ static void node_composit_buts_keying(uiLayout *layout, bContext *UNUSED(C), Poi
uiItemR(layout, ptr, "blur_post", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class KeyingOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ get_input("Image").pass_through(get_result("Image"));
+ get_result("Matte").allocate_invalid();
+ get_result("Edges").allocate_invalid();
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new KeyingOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_keying_cc
void register_node_type_cmp_keying()
@@ -77,6 +98,7 @@ void register_node_type_cmp_keying()
node_type_init(&ntype, file_ns::node_composit_init_keying);
node_type_storage(
&ntype, "NodeKeyingData", node_free_standard_storage, node_copy_standard_storage);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_keyingscreen.cc b/source/blender/nodes/composite/nodes/node_composite_keyingscreen.cc
index e835ee9e721..9eec705b6ca 100644
--- a/source/blender/nodes/composite/nodes/node_composite_keyingscreen.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_keyingscreen.cc
@@ -21,6 +21,8 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
/* **************** Keying Screen ******************** */
@@ -78,6 +80,23 @@ static void node_composit_buts_keyingscreen(uiLayout *layout, bContext *C, Point
}
}
+using namespace blender::realtime_compositor;
+
+class KeyingScreenOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ get_result("Screen").allocate_invalid();
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new KeyingScreenOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_keyingscreen_cc
void register_node_type_cmp_keyingscreen()
@@ -92,6 +111,7 @@ void register_node_type_cmp_keyingscreen()
ntype.initfunc_api = file_ns::node_composit_init_keyingscreen;
node_type_storage(
&ntype, "NodeKeyingScreenData", node_free_standard_storage, node_copy_standard_storage);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_lensdist.cc b/source/blender/nodes/composite/nodes/node_composite_lensdist.cc
index 593b7cc9b71..2d4c0afcda7 100644
--- a/source/blender/nodes/composite/nodes/node_composite_lensdist.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_lensdist.cc
@@ -5,20 +5,48 @@
* \ingroup cmpnodes
*/
+#include "BLI_math_base.h"
+#include "BLI_math_vec_types.hh"
+
#include "RNA_access.h"
#include "UI_interface.h"
#include "UI_resources.h"
+#include "GPU_shader.h"
+#include "GPU_texture.h"
+
+#include "COM_node_operation.hh"
+#include "COM_utilities.hh"
+
#include "node_composite_util.hh"
+/* Distortion can't be exactly -1.0 as it will cause infinite pincushion distortion. */
+#define MINIMUM_DISTORTION -0.999f
+/* Arbitrary scaling factor for the dispersion input in projector distortion mode. */
+#define PROJECTOR_DISPERSION_SCALE 5.0f
+/* Arbitrary scaling factor for the dispersion input in screen distortion mode. */
+#define SCREEN_DISPERSION_SCALE 4.0f
+/* Arbitrary scaling factor for the distortion input. */
+#define DISTORTION_SCALE 4.0f
+
namespace blender::nodes::node_composite_lensdist_cc {
static void cmp_node_lensdist_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
- b.add_input<decl::Float>(N_("Distort")).default_value(0.0f).min(-0.999f).max(1.0f);
- b.add_input<decl::Float>(N_("Dispersion")).default_value(0.0f).min(0.0f).max(1.0f);
+ b.add_input<decl::Color>(N_("Image"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(0);
+ b.add_input<decl::Float>(N_("Distort"))
+ .default_value(0.0f)
+ .min(MINIMUM_DISTORTION)
+ .max(1.0f)
+ .compositor_expects_single_value();
+ b.add_input<decl::Float>(N_("Dispersion"))
+ .default_value(0.0f)
+ .min(0.0f)
+ .max(1.0f)
+ .compositor_expects_single_value();
b.add_output<decl::Color>(N_("Image"));
}
@@ -42,6 +70,178 @@ static void node_composit_buts_lensdist(uiLayout *layout, bContext *UNUSED(C), P
uiItemR(col, ptr, "use_fit", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class LensDistortionOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ if (is_identity()) {
+ get_input("Image").pass_through(get_result("Image"));
+ return;
+ }
+
+ if (get_is_projector()) {
+ execute_projector_distortion();
+ }
+ else {
+ execute_screen_distortion();
+ }
+ }
+
+ void execute_projector_distortion()
+ {
+ GPUShader *shader = shader_manager().get("compositor_projector_lens_distortion");
+ GPU_shader_bind(shader);
+
+ const Result &input_image = get_input("Image");
+ input_image.bind_as_texture(shader, "input_tx");
+
+ GPU_texture_filter_mode(input_image.texture(), true);
+ GPU_texture_wrap_mode(input_image.texture(), false, false);
+
+ const Domain domain = compute_domain();
+
+ const float dispersion = (get_dispersion() * PROJECTOR_DISPERSION_SCALE) / domain.size.x;
+ GPU_shader_uniform_1f(shader, "dispersion", dispersion);
+
+ Result &output_image = get_result("Image");
+ output_image.allocate_texture(domain);
+ output_image.bind_as_image(shader, "output_img");
+
+ compute_dispatch_threads_at_least(shader, domain.size);
+
+ input_image.unbind_as_texture();
+ output_image.unbind_as_image();
+ GPU_shader_unbind();
+ }
+
+ void execute_screen_distortion()
+ {
+ GPUShader *shader = shader_manager().get(get_screen_distortion_shader());
+ GPU_shader_bind(shader);
+
+ const Result &input_image = get_input("Image");
+ input_image.bind_as_texture(shader, "input_tx");
+
+ GPU_texture_filter_mode(input_image.texture(), true);
+ GPU_texture_wrap_mode(input_image.texture(), false, false);
+
+ const Domain domain = compute_domain();
+
+ const float3 chromatic_distortion = compute_chromatic_distortion();
+ GPU_shader_uniform_3fv(shader, "chromatic_distortion", chromatic_distortion);
+
+ GPU_shader_uniform_1f(shader, "scale", compute_scale());
+
+ Result &output_image = get_result("Image");
+ output_image.allocate_texture(domain);
+ output_image.bind_as_image(shader, "output_img");
+
+ compute_dispatch_threads_at_least(shader, domain.size);
+
+ input_image.unbind_as_texture();
+ output_image.unbind_as_image();
+ GPU_shader_unbind();
+ }
+
+ const char *get_screen_distortion_shader()
+ {
+ if (get_is_jitter()) {
+ return "compositor_screen_lens_distortion_jitter";
+ }
+ return "compositor_screen_lens_distortion";
+ }
+
+ float get_distortion()
+ {
+ const Result &input = get_input("Distort");
+ return clamp_f(input.get_float_value_default(0.0f), MINIMUM_DISTORTION, 1.0f);
+ }
+
+ float get_dispersion()
+ {
+ const Result &input = get_input("Dispersion");
+ return clamp_f(input.get_float_value_default(0.0f), 0.0f, 1.0f);
+ }
+
+ /* Get the distortion amount for each channel. The green channel has a distortion amount that
+ * matches that specified in the node inputs, while the red and blue channels have higher and
+ * lower distortion amounts respectively based on the dispersion value. */
+ float3 compute_chromatic_distortion()
+ {
+ const float green_distortion = get_distortion();
+ const float dispersion = get_dispersion() / SCREEN_DISPERSION_SCALE;
+ const float red_distortion = clamp_f(green_distortion + dispersion, MINIMUM_DISTORTION, 1.0f);
+ const float blue_distortion = clamp_f(green_distortion - dispersion, MINIMUM_DISTORTION, 1.0f);
+ return float3(red_distortion, green_distortion, blue_distortion) * DISTORTION_SCALE;
+ }
+
+ /* The distortion model will distort the image in such a way that the result will no longer
+ * fit the domain of the original image, so we scale the image to account for that. If get_is_fit
+ * is false, then the scaling factor will be such that the furthest pixels horizontally and
+ * vertically are at the boundary of the image. Otherwise, if get_is_fit is true, the scaling
+ * factor will be such that the furthest pixels diagonally are at the corner of the image. */
+ float compute_scale()
+ {
+ const float3 distortion = compute_chromatic_distortion() / DISTORTION_SCALE;
+ const float maximum_distortion = max_fff(distortion[0], distortion[1], distortion[2]);
+
+ if (get_is_fit() && (maximum_distortion > 0.0f)) {
+ return 1.0f / (1.0f + 2.0f * maximum_distortion);
+ }
+ return 1.0f / (1.0f + maximum_distortion);
+ }
+
+ bool get_is_projector()
+ {
+ return get_node_lens_distortion().proj;
+ }
+
+ bool get_is_jitter()
+ {
+ return get_node_lens_distortion().jit;
+ }
+
+ bool get_is_fit()
+ {
+ return get_node_lens_distortion().fit;
+ }
+
+ NodeLensDist &get_node_lens_distortion()
+ {
+ return *static_cast<NodeLensDist *>(bnode().storage);
+ }
+
+ /* Returns true if the operation does nothing and the input can be passed through. */
+ bool is_identity()
+ {
+ /* The input is a single value and the operation does nothing. */
+ if (get_input("Image").is_single_value()) {
+ return true;
+ }
+
+ /* Projector have zero dispersion and does nothing. */
+ if (get_is_projector() && get_dispersion() == 0.0f) {
+ return true;
+ }
+
+ /* Both distortion and dispersion are zero and the operation does nothing. */
+ if (get_distortion() == 0.0f && get_dispersion() == 0.0f) {
+ return true;
+ }
+
+ return false;
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new LensDistortionOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_lensdist_cc
void register_node_type_cmp_lensdist()
@@ -56,6 +256,7 @@ void register_node_type_cmp_lensdist()
node_type_init(&ntype, file_ns::node_composit_init_lensdist);
node_type_storage(
&ntype, "NodeLensDist", node_free_standard_storage, node_copy_standard_storage);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_levels.cc b/source/blender/nodes/composite/nodes/node_composite_levels.cc
index a30567672f0..2f1ebeb79b5 100644
--- a/source/blender/nodes/composite/nodes/node_composite_levels.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_levels.cc
@@ -8,6 +8,8 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
/* **************** LEVELS ******************** */
@@ -31,6 +33,24 @@ static void node_composit_buts_view_levels(uiLayout *layout, bContext *UNUSED(C)
uiItemR(layout, ptr, "channel", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class LevelsOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ get_result("Mean").allocate_invalid();
+ get_result("Std Dev").allocate_invalid();
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new LevelsOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_levels_cc
void register_node_type_cmp_view_levels()
@@ -44,6 +64,7 @@ void register_node_type_cmp_view_levels()
ntype.draw_buttons = file_ns::node_composit_buts_view_levels;
ntype.flag |= NODE_PREVIEW;
node_type_init(&ntype, file_ns::node_composit_init_view_levels);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_luma_matte.cc b/source/blender/nodes/composite/nodes/node_composite_luma_matte.cc
index 94697a2aafd..092a12a7ea4 100644
--- a/source/blender/nodes/composite/nodes/node_composite_luma_matte.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_luma_matte.cc
@@ -5,9 +5,15 @@
* \ingroup cmpnodes
*/
+#include "IMB_colormanagement.h"
+
#include "UI_interface.h"
#include "UI_resources.h"
+#include "GPU_material.h"
+
+#include "COM_shader_node.hh"
+
#include "node_composite_util.hh"
/* ******************* Luma Matte Node ********************************* */
@@ -16,7 +22,9 @@ namespace blender::nodes::node_composite_luma_matte_cc {
static void cmp_node_luma_matte_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
+ b.add_input<decl::Color>(N_("Image"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(0);
b.add_output<decl::Color>(N_("Image"));
b.add_output<decl::Float>(N_("Matte"));
}
@@ -40,6 +48,53 @@ static void node_composit_buts_luma_matte(uiLayout *layout, bContext *UNUSED(C),
col, ptr, "limit_min", UI_ITEM_R_SPLIT_EMPTY_NAME | UI_ITEM_R_SLIDER, nullptr, ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class LuminanceMatteShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ const float high = get_high();
+ const float low = get_low();
+ float luminance_coefficients[3];
+ IMB_colormanagement_get_luminance_coefficients(luminance_coefficients);
+
+ GPU_stack_link(material,
+ &bnode(),
+ "node_composite_luminance_matte",
+ inputs,
+ outputs,
+ GPU_uniform(&high),
+ GPU_uniform(&low),
+ GPU_constant(luminance_coefficients));
+ }
+
+ NodeChroma *get_node_chroma()
+ {
+ return static_cast<NodeChroma *>(bnode().storage);
+ }
+
+ float get_high()
+ {
+ return get_node_chroma()->t1;
+ }
+
+ float get_low()
+ {
+ return get_node_chroma()->t2;
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new LuminanceMatteShaderNode(node);
+}
+
} // namespace blender::nodes::node_composite_luma_matte_cc
void register_node_type_cmp_luma_matte()
@@ -54,6 +109,7 @@ void register_node_type_cmp_luma_matte()
ntype.flag |= NODE_PREVIEW;
node_type_init(&ntype, file_ns::node_composit_init_luma_matte);
node_type_storage(&ntype, "NodeChroma", node_free_standard_storage, node_copy_standard_storage);
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_map_range.cc b/source/blender/nodes/composite/nodes/node_composite_map_range.cc
index e52c6d096b9..e72869efa93 100644
--- a/source/blender/nodes/composite/nodes/node_composite_map_range.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_map_range.cc
@@ -8,6 +8,10 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "GPU_material.h"
+
+#include "COM_shader_node.hh"
+
#include "node_composite_util.hh"
/* **************** Map Range ******************** */
@@ -16,11 +20,31 @@ namespace blender::nodes::node_composite_map_range_cc {
static void cmp_node_map_range_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Float>(N_("Value")).default_value(1.0f).min(0.0f).max(1.0f);
- b.add_input<decl::Float>(N_("From Min")).default_value(0.0f).min(-10000.0f).max(10000.0f);
- b.add_input<decl::Float>(N_("From Max")).default_value(1.0f).min(-10000.0f).max(10000.0f);
- b.add_input<decl::Float>(N_("To Min")).default_value(0.0f).min(-10000.0f).max(10000.0f);
- b.add_input<decl::Float>(N_("To Max")).default_value(1.0f).min(-10000.0f).max(10000.0f);
+ b.add_input<decl::Float>(N_("Value"))
+ .default_value(1.0f)
+ .min(0.0f)
+ .max(1.0f)
+ .compositor_domain_priority(0);
+ b.add_input<decl::Float>(N_("From Min"))
+ .default_value(0.0f)
+ .min(-10000.0f)
+ .max(10000.0f)
+ .compositor_domain_priority(1);
+ b.add_input<decl::Float>(N_("From Max"))
+ .default_value(1.0f)
+ .min(-10000.0f)
+ .max(10000.0f)
+ .compositor_domain_priority(2);
+ b.add_input<decl::Float>(N_("To Min"))
+ .default_value(0.0f)
+ .min(-10000.0f)
+ .max(10000.0f)
+ .compositor_domain_priority(3);
+ b.add_input<decl::Float>(N_("To Max"))
+ .default_value(1.0f)
+ .min(-10000.0f)
+ .max(10000.0f)
+ .compositor_domain_priority(4);
b.add_output<decl::Float>(N_("Value"));
}
@@ -32,6 +56,38 @@ static void node_composit_buts_map_range(uiLayout *layout, bContext *UNUSED(C),
uiItemR(col, ptr, "use_clamp", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class MapRangeShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ const float should_clamp = get_should_clamp();
+
+ GPU_stack_link(material,
+ &bnode(),
+ "node_composite_map_range",
+ inputs,
+ outputs,
+ GPU_constant(&should_clamp));
+ }
+
+ bool get_should_clamp()
+ {
+ return bnode().custom1;
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new MapRangeShaderNode(node);
+}
+
} // namespace blender::nodes::node_composite_map_range_cc
void register_node_type_cmp_map_range()
@@ -43,6 +99,7 @@ void register_node_type_cmp_map_range()
cmp_node_type_base(&ntype, CMP_NODE_MAP_RANGE, "Map Range", NODE_CLASS_OP_VECTOR);
ntype.declare = file_ns::cmp_node_map_range_declare;
ntype.draw_buttons = file_ns::node_composit_buts_map_range;
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_map_uv.cc b/source/blender/nodes/composite/nodes/node_composite_map_uv.cc
index 31961f07ea4..4f660d62c3b 100644
--- a/source/blender/nodes/composite/nodes/node_composite_map_uv.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_map_uv.cc
@@ -8,6 +8,8 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
/* **************** Map UV ******************** */
@@ -26,6 +28,23 @@ static void node_composit_buts_map_uv(uiLayout *layout, bContext *UNUSED(C), Poi
uiItemR(layout, ptr, "alpha", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class MapUVOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ get_input("Image").pass_through(get_result("Image"));
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new MapUVOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_map_uv_cc
void register_node_type_cmp_mapuv()
@@ -37,6 +56,7 @@ void register_node_type_cmp_mapuv()
cmp_node_type_base(&ntype, CMP_NODE_MAP_UV, "Map UV", NODE_CLASS_DISTORT);
ntype.declare = file_ns::cmp_node_map_uv_declare;
ntype.draw_buttons = file_ns::node_composit_buts_map_uv;
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_map_value.cc b/source/blender/nodes/composite/nodes/node_composite_map_value.cc
index bb42628ed3d..ec9b2d56636 100644
--- a/source/blender/nodes/composite/nodes/node_composite_map_value.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_map_value.cc
@@ -12,6 +12,10 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "GPU_material.h"
+
+#include "COM_shader_node.hh"
+
#include "node_composite_util.hh"
/* **************** MAP VALUE ******************** */
@@ -20,7 +24,11 @@ namespace blender::nodes::node_composite_map_value_cc {
static void cmp_node_map_value_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Float>(N_("Value")).default_value(1.0f).min(0.0f).max(1.0f);
+ b.add_input<decl::Float>(N_("Value"))
+ .default_value(1.0f)
+ .min(0.0f)
+ .max(1.0f)
+ .compositor_domain_priority(0);
b.add_output<decl::Float>(N_("Value"));
}
@@ -50,6 +58,56 @@ static void node_composit_buts_map_value(uiLayout *layout, bContext *UNUSED(C),
uiItemR(sub, ptr, "max", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class MapValueShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ const TexMapping *texture_mapping = get_texture_mapping();
+
+ const float use_min = get_use_min();
+ const float use_max = get_use_max();
+
+ GPU_stack_link(material,
+ &bnode(),
+ "node_composite_map_value",
+ inputs,
+ outputs,
+ GPU_uniform(texture_mapping->loc),
+ GPU_uniform(texture_mapping->size),
+ GPU_constant(&use_min),
+ GPU_uniform(texture_mapping->min),
+ GPU_constant(&use_max),
+ GPU_uniform(texture_mapping->max));
+ }
+
+ TexMapping *get_texture_mapping()
+ {
+ return static_cast<TexMapping *>(bnode().storage);
+ }
+
+ bool get_use_min()
+ {
+ return get_texture_mapping()->flag & TEXMAP_CLIP_MIN;
+ }
+
+ bool get_use_max()
+ {
+ return get_texture_mapping()->flag & TEXMAP_CLIP_MAX;
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new MapValueShaderNode(node);
+}
+
} // namespace blender::nodes::node_composite_map_value_cc
void register_node_type_cmp_map_value()
@@ -63,6 +121,7 @@ void register_node_type_cmp_map_value()
ntype.draw_buttons = file_ns::node_composit_buts_map_value;
node_type_init(&ntype, file_ns::node_composit_init_map_value);
node_type_storage(&ntype, "TexMapping", node_free_standard_storage, node_copy_standard_storage);
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_mask.cc b/source/blender/nodes/composite/nodes/node_composite_mask.cc
index 5b8fac5d1c0..2372dbae3f2 100644
--- a/source/blender/nodes/composite/nodes/node_composite_mask.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_mask.cc
@@ -10,6 +10,8 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
/* **************** Mask ******************** */
@@ -74,6 +76,23 @@ static void node_composit_buts_mask(uiLayout *layout, bContext *C, PointerRNA *p
}
}
+using namespace blender::realtime_compositor;
+
+class MaskOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ get_result("Mask").allocate_invalid();
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new MaskOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_mask_cc
void register_node_type_cmp_mask()
@@ -87,6 +106,7 @@ void register_node_type_cmp_mask()
ntype.draw_buttons = file_ns::node_composit_buts_mask;
node_type_init(&ntype, file_ns::node_composit_init_mask);
ntype.labelfunc = file_ns::node_mask_label;
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
node_type_storage(&ntype, "NodeMask", node_free_standard_storage, node_copy_standard_storage);
diff --git a/source/blender/nodes/composite/nodes/node_composite_math.cc b/source/blender/nodes/composite/nodes/node_composite_math.cc
index 7b2eadef2cb..4baf057913e 100644
--- a/source/blender/nodes/composite/nodes/node_composite_math.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_math.cc
@@ -5,6 +5,12 @@
* \ingroup cmpnodes
*/
+#include "GPU_material.h"
+
+#include "COM_shader_node.hh"
+
+#include "NOD_math_functions.hh"
+
#include "node_composite_util.hh"
/* **************** SCALAR MATH ******************** */
@@ -13,18 +19,72 @@ namespace blender::nodes::node_composite_math_cc {
static void cmp_node_math_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Float>(N_("Value")).default_value(0.5f).min(-10000.0f).max(10000.0f);
+ b.add_input<decl::Float>(N_("Value"))
+ .default_value(0.5f)
+ .min(-10000.0f)
+ .max(10000.0f)
+ .compositor_domain_priority(0);
b.add_input<decl::Float>(N_("Value"), "Value_001")
.default_value(0.5f)
.min(-10000.0f)
- .max(10000.0f);
+ .max(10000.0f)
+ .compositor_domain_priority(1);
b.add_input<decl::Float>(N_("Value"), "Value_002")
.default_value(0.5f)
.min(-10000.0f)
- .max(10000.0f);
+ .max(10000.0f)
+ .compositor_domain_priority(2);
b.add_output<decl::Float>(N_("Value"));
}
+using namespace blender::realtime_compositor;
+
+class MathShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ GPU_stack_link(material, &bnode(), get_shader_function_name(), inputs, outputs);
+
+ if (!get_should_clamp()) {
+ return;
+ }
+
+ const float min = 0.0f;
+ const float max = 1.0f;
+ GPU_link(material,
+ "clamp_value",
+ get_output("Value").link,
+ GPU_constant(&min),
+ GPU_constant(&max),
+ &get_output("Value").link);
+ }
+
+ NodeMathOperation get_operation()
+ {
+ return (NodeMathOperation)bnode().custom1;
+ }
+
+ const char *get_shader_function_name()
+ {
+ return get_float_math_operation_info(get_operation())->shader_name.c_str();
+ }
+
+ bool get_should_clamp()
+ {
+ return bnode().custom2 & SHD_MATH_CLAMP;
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new MathShaderNode(node);
+}
+
} // namespace blender::nodes::node_composite_math_cc
void register_node_type_cmp_math()
@@ -37,6 +97,7 @@ void register_node_type_cmp_math()
ntype.declare = file_ns::cmp_node_math_declare;
ntype.labelfunc = node_math_label;
node_type_update(&ntype, node_math_update);
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_mixrgb.cc b/source/blender/nodes/composite/nodes/node_composite_mixrgb.cc
index fc11aa188b0..a1fbbfe7d40 100644
--- a/source/blender/nodes/composite/nodes/node_composite_mixrgb.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_mixrgb.cc
@@ -5,6 +5,14 @@
* \ingroup cmpnodes
*/
+#include "BLI_assert.h"
+
+#include "DNA_material_types.h"
+
+#include "GPU_material.h"
+
+#include "COM_shader_node.hh"
+
#include "node_composite_util.hh"
/* **************** MIX RGB ******************** */
@@ -13,12 +21,122 @@ namespace blender::nodes::node_composite_mixrgb_cc {
static void cmp_node_mixrgb_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Float>(N_("Fac")).default_value(1.0f).min(0.0f).max(1.0f).subtype(PROP_FACTOR);
- b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
- b.add_input<decl::Color>(N_("Image"), "Image_001").default_value({1.0f, 1.0f, 1.0f, 1.0f});
+ b.add_input<decl::Float>(N_("Fac"))
+ .default_value(1.0f)
+ .min(0.0f)
+ .max(1.0f)
+ .subtype(PROP_FACTOR)
+ .compositor_domain_priority(2);
+ b.add_input<decl::Color>(N_("Image"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(0);
+ b.add_input<decl::Color>(N_("Image"), "Image_001")
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(1);
b.add_output<decl::Color>(N_("Image"));
}
+using namespace blender::realtime_compositor;
+
+class MixRGBShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ if (get_use_alpha()) {
+ GPU_link(material,
+ "multiply_by_alpha",
+ get_input_link("Fac"),
+ get_input_link("Image_001"),
+ &get_input("Fac").link);
+ }
+
+ GPU_stack_link(material, &bnode(), get_shader_function_name(), inputs, outputs);
+
+ if (!get_should_clamp()) {
+ return;
+ }
+
+ const float min[4] = {0.0f, 0.0f, 0.0f, 0.0f};
+ const float max[4] = {1.0f, 1.0f, 1.0f, 1.0f};
+ GPU_link(material,
+ "clamp_color",
+ get_output("Image").link,
+ GPU_constant(min),
+ GPU_constant(max),
+ &get_output("Image").link);
+ }
+
+ int get_mode()
+ {
+ return bnode().custom1;
+ }
+
+ const char *get_shader_function_name()
+ {
+ switch (get_mode()) {
+ case MA_RAMP_BLEND:
+ return "mix_blend";
+ case MA_RAMP_ADD:
+ return "mix_add";
+ case MA_RAMP_MULT:
+ return "mix_mult";
+ case MA_RAMP_SUB:
+ return "mix_sub";
+ case MA_RAMP_SCREEN:
+ return "mix_screen";
+ case MA_RAMP_DIV:
+ return "mix_div";
+ case MA_RAMP_DIFF:
+ return "mix_diff";
+ case MA_RAMP_DARK:
+ return "mix_dark";
+ case MA_RAMP_LIGHT:
+ return "mix_light";
+ case MA_RAMP_OVERLAY:
+ return "mix_overlay";
+ case MA_RAMP_DODGE:
+ return "mix_dodge";
+ case MA_RAMP_BURN:
+ return "mix_burn";
+ case MA_RAMP_HUE:
+ return "mix_hue";
+ case MA_RAMP_SAT:
+ return "mix_sat";
+ case MA_RAMP_VAL:
+ return "mix_val";
+ case MA_RAMP_COLOR:
+ return "mix_color";
+ case MA_RAMP_SOFT:
+ return "mix_soft";
+ case MA_RAMP_LINEAR:
+ return "mix_linear";
+ }
+
+ BLI_assert_unreachable();
+ return nullptr;
+ }
+
+ bool get_use_alpha()
+ {
+ return bnode().custom2 & SHD_MIXRGB_USE_ALPHA;
+ }
+
+ bool get_should_clamp()
+ {
+ return bnode().custom2 & SHD_MIXRGB_CLAMP;
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new MixRGBShaderNode(node);
+}
+
} // namespace blender::nodes::node_composite_mixrgb_cc
void register_node_type_cmp_mix_rgb()
@@ -31,6 +149,7 @@ void register_node_type_cmp_mix_rgb()
ntype.flag |= NODE_PREVIEW;
ntype.declare = file_ns::cmp_node_mixrgb_declare;
ntype.labelfunc = node_blend_label;
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_movieclip.cc b/source/blender/nodes/composite/nodes/node_composite_movieclip.cc
index a4d5f294fe0..ec95de3da18 100644
--- a/source/blender/nodes/composite/nodes/node_composite_movieclip.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_movieclip.cc
@@ -5,8 +5,13 @@
* \ingroup cmpnodes
*/
+#include "BLI_math_vec_types.hh"
+
#include "BKE_context.h"
#include "BKE_lib_id.h"
+#include "BKE_movieclip.h"
+#include "BKE_tracking.h"
+
#include "DNA_defaults.h"
#include "RNA_access.h"
@@ -14,6 +19,12 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "GPU_shader.h"
+#include "GPU_texture.h"
+
+#include "COM_node_operation.hh"
+#include "COM_utilities.hh"
+
#include "node_composite_util.hh"
namespace blender::nodes::node_composite_movieclip_cc {
@@ -79,6 +90,177 @@ static void node_composit_buts_movieclip_ex(uiLayout *layout, bContext *C, Point
uiTemplateColorspaceSettings(layout, &clipptr, "colorspace_settings");
}
+using namespace blender::realtime_compositor;
+
+class MovieClipOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ GPUTexture *movie_clip_texture = get_movie_clip_texture();
+
+ compute_image(movie_clip_texture);
+ compute_alpha(movie_clip_texture);
+ compute_stabilization_data(movie_clip_texture);
+
+ free_movie_clip_texture();
+ }
+
+ void compute_image(GPUTexture *movie_clip_texture)
+ {
+ if (!should_compute_output("Image")) {
+ return;
+ }
+
+ Result &result = get_result("Image");
+
+ /* The movie clip texture is invalid or missing, set an appropriate fallback value. */
+ if (!movie_clip_texture) {
+ result.allocate_invalid();
+ return;
+ }
+
+ const int2 size = int2(GPU_texture_width(movie_clip_texture),
+ GPU_texture_height(movie_clip_texture));
+ result.allocate_texture(Domain(size));
+
+ GPUShader *shader = shader_manager().get("compositor_convert_color_to_half_color");
+ GPU_shader_bind(shader);
+
+ const int input_unit = GPU_shader_get_texture_binding(shader, "input_tx");
+ GPU_texture_bind(movie_clip_texture, input_unit);
+
+ result.bind_as_image(shader, "output_img");
+
+ compute_dispatch_threads_at_least(shader, size);
+
+ GPU_shader_unbind();
+ GPU_texture_unbind(movie_clip_texture);
+ result.unbind_as_image();
+ }
+
+ void compute_alpha(GPUTexture *movie_clip_texture)
+ {
+ if (!should_compute_output("Alpha")) {
+ return;
+ }
+
+ Result &result = get_result("Alpha");
+
+ /* The movie clip texture is invalid or missing, set an appropriate fallback value. */
+ if (!movie_clip_texture) {
+ result.allocate_single_value();
+ result.set_float_value(1.0f);
+ return;
+ }
+
+ const int2 size = int2(GPU_texture_width(movie_clip_texture),
+ GPU_texture_height(movie_clip_texture));
+ result.allocate_texture(Domain(size));
+
+ GPUShader *shader = shader_manager().get("compositor_extract_alpha_from_color");
+ GPU_shader_bind(shader);
+
+ const int input_unit = GPU_shader_get_texture_binding(shader, "input_tx");
+ GPU_texture_bind(movie_clip_texture, input_unit);
+
+ result.bind_as_image(shader, "output_img");
+
+ compute_dispatch_threads_at_least(shader, size);
+
+ GPU_shader_unbind();
+ GPU_texture_unbind(movie_clip_texture);
+ result.unbind_as_image();
+ }
+
+ void compute_stabilization_data(GPUTexture *movie_clip_texture)
+ {
+ /* The movie clip texture is invalid or missing, set appropriate fallback values. */
+ if (!movie_clip_texture) {
+ if (should_compute_output("Offset X")) {
+ Result &result = get_result("Offset X");
+ result.allocate_single_value();
+ result.set_float_value(0.0f);
+ }
+ if (should_compute_output("Offset Y")) {
+ Result &result = get_result("Offset Y");
+ result.allocate_single_value();
+ result.set_float_value(0.0f);
+ }
+ if (should_compute_output("Scale")) {
+ Result &result = get_result("Scale");
+ result.allocate_single_value();
+ result.set_float_value(1.0f);
+ }
+ if (should_compute_output("Angle")) {
+ Result &result = get_result("Angle");
+ result.allocate_single_value();
+ result.set_float_value(0.0f);
+ }
+ return;
+ }
+
+ MovieClip *movie_clip = get_movie_clip();
+ const int frame_number = BKE_movieclip_remap_scene_to_clip_frame(movie_clip,
+ context().get_frame_number());
+ const int width = GPU_texture_width(movie_clip_texture);
+ const int height = GPU_texture_height(movie_clip_texture);
+
+ /* If the movie clip has no stabilization data, it will initialize the given values with
+ * fallback values regardless, so no need to handle that case. */
+ float2 offset;
+ float scale, angle;
+ BKE_tracking_stabilization_data_get(
+ movie_clip, frame_number, width, height, offset, &scale, &angle);
+
+ if (should_compute_output("Offset X")) {
+ Result &result = get_result("Offset X");
+ result.allocate_single_value();
+ result.set_float_value(offset.x);
+ }
+ if (should_compute_output("Offset Y")) {
+ Result &result = get_result("Offset Y");
+ result.allocate_single_value();
+ result.set_float_value(offset.y);
+ }
+ if (should_compute_output("Scale")) {
+ Result &result = get_result("Scale");
+ result.allocate_single_value();
+ result.set_float_value(scale);
+ }
+ if (should_compute_output("Angle")) {
+ Result &result = get_result("Angle");
+ result.allocate_single_value();
+ result.set_float_value(angle);
+ }
+ }
+
+ GPUTexture *get_movie_clip_texture()
+ {
+ MovieClip *movie_clip = get_movie_clip();
+ MovieClipUser *movie_clip_user = static_cast<MovieClipUser *>(bnode().storage);
+ BKE_movieclip_user_set_frame(movie_clip_user, context().get_frame_number());
+ return BKE_movieclip_get_gpu_texture(movie_clip, movie_clip_user);
+ }
+
+ void free_movie_clip_texture()
+ {
+ MovieClip *movie_clip = get_movie_clip();
+ return BKE_movieclip_free_gputexture(movie_clip);
+ }
+
+ MovieClip *get_movie_clip()
+ {
+ return (MovieClip *)bnode().id;
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new MovieClipOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_movieclip_cc
void register_node_type_cmp_movieclip()
@@ -91,6 +273,7 @@ void register_node_type_cmp_movieclip()
ntype.declare = file_ns::cmp_node_movieclip_declare;
ntype.draw_buttons = file_ns::node_composit_buts_movieclip;
ntype.draw_buttons_ex = file_ns::node_composit_buts_movieclip_ex;
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
ntype.initfunc_api = file_ns::init;
ntype.flag |= NODE_PREVIEW;
node_type_storage(
diff --git a/source/blender/nodes/composite/nodes/node_composite_moviedistortion.cc b/source/blender/nodes/composite/nodes/node_composite_moviedistortion.cc
index 4d52a767b8a..88638586594 100644
--- a/source/blender/nodes/composite/nodes/node_composite_moviedistortion.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_moviedistortion.cc
@@ -12,6 +12,8 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
/* **************** Translate ******************** */
@@ -81,6 +83,23 @@ static void node_composit_buts_moviedistortion(uiLayout *layout, bContext *C, Po
uiItemR(layout, ptr, "distortion_type", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class MovieDistortionOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ get_input("Image").pass_through(get_result("Image"));
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new MovieDistortionOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_moviedistortion_cc
void register_node_type_cmp_moviedistortion()
@@ -95,6 +114,7 @@ void register_node_type_cmp_moviedistortion()
ntype.labelfunc = file_ns::label;
ntype.initfunc_api = file_ns::init;
node_type_storage(&ntype, nullptr, file_ns::storage_free, file_ns::storage_copy);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_normal.cc b/source/blender/nodes/composite/nodes/node_composite_normal.cc
index b4dd0bbacd0..f61ace01cfd 100644
--- a/source/blender/nodes/composite/nodes/node_composite_normal.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_normal.cc
@@ -5,6 +5,10 @@
* \ingroup cmpnodes
*/
+#include "GPU_material.h"
+
+#include "COM_shader_node.hh"
+
#include "node_composite_util.hh"
/* **************** NORMAL ******************** */
@@ -17,7 +21,8 @@ static void cmp_node_normal_declare(NodeDeclarationBuilder &b)
.default_value({0.0f, 0.0f, 1.0f})
.min(-1.0f)
.max(1.0f)
- .subtype(PROP_DIRECTION);
+ .subtype(PROP_DIRECTION)
+ .compositor_domain_priority(0);
b.add_output<decl::Vector>(N_("Normal"))
.default_value({0.0f, 0.0f, 1.0f})
.min(-1.0f)
@@ -26,6 +31,37 @@ static void cmp_node_normal_declare(NodeDeclarationBuilder &b)
b.add_output<decl::Float>(N_("Dot"));
}
+using namespace blender::realtime_compositor;
+
+class NormalShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ GPU_stack_link(material,
+ &bnode(),
+ "node_composite_normal",
+ inputs,
+ outputs,
+ GPU_uniform(get_vector_value()));
+ }
+
+ /* The vector value is stored in the default value of the output socket. */
+ float *get_vector_value()
+ {
+ return node().output_by_identifier("Normal")->default_value<bNodeSocketValueVector>()->value;
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new NormalShaderNode(node);
+}
+
} // namespace blender::nodes::node_composite_normal_cc
void register_node_type_cmp_normal()
@@ -36,6 +72,7 @@ void register_node_type_cmp_normal()
cmp_node_type_base(&ntype, CMP_NODE_NORMAL, "Normal", NODE_CLASS_OP_VECTOR);
ntype.declare = file_ns::cmp_node_normal_declare;
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_normalize.cc b/source/blender/nodes/composite/nodes/node_composite_normalize.cc
index 49318279bdb..21765825468 100644
--- a/source/blender/nodes/composite/nodes/node_composite_normalize.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_normalize.cc
@@ -5,6 +5,8 @@
* \ingroup cmpnodes
*/
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
/* **************** NORMALIZE single channel, useful for Z buffer ******************** */
@@ -17,6 +19,23 @@ static void cmp_node_normalize_declare(NodeDeclarationBuilder &b)
b.add_output<decl::Float>(N_("Value"));
}
+using namespace blender::realtime_compositor;
+
+class NormalizeOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ get_input("Value").pass_through(get_result("Value"));
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new NormalizeOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_normalize_cc
void register_node_type_cmp_normalize()
@@ -27,6 +46,7 @@ void register_node_type_cmp_normalize()
cmp_node_type_base(&ntype, CMP_NODE_NORMALIZE, "Normalize", NODE_CLASS_OP_VECTOR);
ntype.declare = file_ns::cmp_node_normalize_declare;
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_output_file.cc b/source/blender/nodes/composite/nodes/node_composite_output_file.cc
index 84235b085a4..5ed383977a5 100644
--- a/source/blender/nodes/composite/nodes/node_composite_output_file.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_output_file.cc
@@ -24,6 +24,8 @@
#include "IMB_openexr.h"
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
/* **************** OUTPUT FILE ******************** */
@@ -439,6 +441,22 @@ static void node_composit_buts_file_output_ex(uiLayout *layout, bContext *C, Poi
}
}
+using namespace blender::realtime_compositor;
+
+class OutputFileOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new OutputFileOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_output_file_cc
void register_node_type_cmp_output_file()
@@ -455,6 +473,7 @@ void register_node_type_cmp_output_file()
node_type_storage(
&ntype, "NodeImageMultiFile", file_ns::free_output_file, file_ns::copy_output_file);
node_type_update(&ntype, file_ns::update_output_file);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_pixelate.cc b/source/blender/nodes/composite/nodes/node_composite_pixelate.cc
index 529aa0f84de..4567464a547 100644
--- a/source/blender/nodes/composite/nodes/node_composite_pixelate.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_pixelate.cc
@@ -5,6 +5,8 @@
* \ingroup cmpnodes
*/
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
/* **************** Pixelate ******************** */
@@ -17,6 +19,23 @@ static void cmp_node_pixelate_declare(NodeDeclarationBuilder &b)
b.add_output<decl::Color>(N_("Color"));
}
+using namespace blender::realtime_compositor;
+
+class PixelateOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ get_input("Color").pass_through(get_result("Color"));
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new PixelateOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_pixelate_cc
void register_node_type_cmp_pixelate()
@@ -27,6 +46,7 @@ void register_node_type_cmp_pixelate()
cmp_node_type_base(&ntype, CMP_NODE_PIXELATE, "Pixelate", NODE_CLASS_OP_FILTER);
ntype.declare = file_ns::cmp_node_pixelate_declare;
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_planetrackdeform.cc b/source/blender/nodes/composite/nodes/node_composite_planetrackdeform.cc
index 6557478fc4b..68dc020a02e 100644
--- a/source/blender/nodes/composite/nodes/node_composite_planetrackdeform.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_planetrackdeform.cc
@@ -18,6 +18,8 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
namespace blender::nodes::node_composite_planetrackdeform_cc {
@@ -107,6 +109,24 @@ static void node_composit_buts_planetrackdeform(uiLayout *layout, bContext *C, P
}
}
+using namespace blender::realtime_compositor;
+
+class PlaneTrackDeformOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ get_input("Image").pass_through(get_result("Image"));
+ get_result("Plane").allocate_invalid();
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new PlaneTrackDeformOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_planetrackdeform_cc
void register_node_type_cmp_planetrackdeform()
@@ -121,6 +141,7 @@ void register_node_type_cmp_planetrackdeform()
ntype.initfunc_api = file_ns::init;
node_type_storage(
&ntype, "NodePlaneTrackDeformData", node_free_standard_storage, node_copy_standard_storage);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_posterize.cc b/source/blender/nodes/composite/nodes/node_composite_posterize.cc
index c97035d55ea..1268219e7e2 100644
--- a/source/blender/nodes/composite/nodes/node_composite_posterize.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_posterize.cc
@@ -5,6 +5,10 @@
* \ingroup cmpnodes
*/
+#include "GPU_material.h"
+
+#include "COM_shader_node.hh"
+
#include "node_composite_util.hh"
/* **************** Posterize ******************** */
@@ -13,11 +17,37 @@ namespace blender::nodes::node_composite_posterize_cc {
static void cmp_node_posterize_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
- b.add_input<decl::Float>(N_("Steps")).default_value(8.0f).min(2.0f).max(1024.0f);
+ b.add_input<decl::Color>(N_("Image"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(0);
+ b.add_input<decl::Float>(N_("Steps"))
+ .default_value(8.0f)
+ .min(2.0f)
+ .max(1024.0f)
+ .compositor_domain_priority(1);
b.add_output<decl::Color>(N_("Image"));
}
+using namespace blender::realtime_compositor;
+
+class PosterizeShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ GPU_stack_link(material, &bnode(), "node_composite_posterize", inputs, outputs);
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new PosterizeShaderNode(node);
+}
+
} // namespace blender::nodes::node_composite_posterize_cc
void register_node_type_cmp_posterize()
@@ -28,6 +58,7 @@ void register_node_type_cmp_posterize()
cmp_node_type_base(&ntype, CMP_NODE_POSTERIZE, "Posterize", NODE_CLASS_OP_COLOR);
ntype.declare = file_ns::cmp_node_posterize_declare;
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_premulkey.cc b/source/blender/nodes/composite/nodes/node_composite_premulkey.cc
index 000cc9df90a..c814ea5f738 100644
--- a/source/blender/nodes/composite/nodes/node_composite_premulkey.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_premulkey.cc
@@ -8,6 +8,10 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "GPU_material.h"
+
+#include "COM_shader_node.hh"
+
#include "node_composite_util.hh"
/* **************** Premul and Key Alpha Convert ******************** */
@@ -16,7 +20,9 @@ namespace blender::nodes::node_composite_premulkey_cc {
static void cmp_node_premulkey_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
+ b.add_input<decl::Color>(N_("Image"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(0);
b.add_output<decl::Color>(N_("Image"));
}
@@ -25,6 +31,36 @@ static void node_composit_buts_premulkey(uiLayout *layout, bContext *UNUSED(C),
uiItemR(layout, ptr, "mapping", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class AlphaConvertShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ if (get_mode() == 0) {
+ GPU_stack_link(material, &bnode(), "color_alpha_premultiply", inputs, outputs);
+ return;
+ }
+
+ GPU_stack_link(material, &bnode(), "color_alpha_unpremultiply", inputs, outputs);
+ }
+
+ CMPNodeAlphaConvertMode get_mode()
+ {
+ return (CMPNodeAlphaConvertMode)bnode().custom1;
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new AlphaConvertShaderNode(node);
+}
+
} // namespace blender::nodes::node_composite_premulkey_cc
void register_node_type_cmp_premulkey()
@@ -36,6 +72,7 @@ void register_node_type_cmp_premulkey()
cmp_node_type_base(&ntype, CMP_NODE_PREMULKEY, "Alpha Convert", NODE_CLASS_CONVERTER);
ntype.declare = file_ns::cmp_node_premulkey_declare;
ntype.draw_buttons = file_ns::node_composit_buts_premulkey;
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_rgb.cc b/source/blender/nodes/composite/nodes/node_composite_rgb.cc
index 5bc4c67dd8e..6f3a00af7e3 100644
--- a/source/blender/nodes/composite/nodes/node_composite_rgb.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_rgb.cc
@@ -5,6 +5,12 @@
* \ingroup cmpnodes
*/
+#include "BLI_math_vec_types.hh"
+
+#include "DNA_node_types.h"
+
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
/* **************** RGB ******************** */
@@ -16,6 +22,29 @@ static void cmp_node_rgb_declare(NodeDeclarationBuilder &b)
b.add_output<decl::Color>(N_("RGBA")).default_value({0.5f, 0.5f, 0.5f, 1.0f});
}
+using namespace blender::realtime_compositor;
+
+class RGBOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ Result &result = get_result("RGBA");
+ result.allocate_single_value();
+
+ const bNodeSocket *socket = static_cast<bNodeSocket *>(bnode().outputs.first);
+ float4 color = float4(static_cast<bNodeSocketValueRGBA *>(socket->default_value)->value);
+
+ result.set_color_value(color);
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new RGBOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_rgb_cc
void register_node_type_cmp_rgb()
@@ -27,6 +56,7 @@ void register_node_type_cmp_rgb()
cmp_node_type_base(&ntype, CMP_NODE_RGB, "RGB", NODE_CLASS_INPUT);
ntype.declare = file_ns::cmp_node_rgb_declare;
node_type_size_preset(&ntype, NODE_SIZE_SMALL);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_rotate.cc b/source/blender/nodes/composite/nodes/node_composite_rotate.cc
index a083bc1837b..35caa3cd242 100644
--- a/source/blender/nodes/composite/nodes/node_composite_rotate.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_rotate.cc
@@ -5,9 +5,14 @@
* \ingroup cmpnodes
*/
+#include "BLI_assert.h"
+#include "BLI_float3x3.hh"
+
#include "UI_interface.h"
#include "UI_resources.h"
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
/* **************** Rotate ******************** */
@@ -16,12 +21,15 @@ namespace blender::nodes::node_composite_rotate_cc {
static void cmp_node_rotate_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
+ b.add_input<decl::Color>(N_("Image"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(0);
b.add_input<decl::Float>(N_("Degr"))
.default_value(0.0f)
.min(-10000.0f)
.max(10000.0f)
- .subtype(PROP_ANGLE);
+ .subtype(PROP_ANGLE)
+ .compositor_expects_single_value();
b.add_output<decl::Color>(N_("Image"));
}
@@ -35,6 +43,47 @@ static void node_composit_buts_rotate(uiLayout *layout, bContext *UNUSED(C), Poi
uiItemR(layout, ptr, "filter_type", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class RotateOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ Result &input = get_input("Image");
+ Result &result = get_result("Image");
+ input.pass_through(result);
+
+ const float rotation = get_input("Degr").get_float_value_default(0.0f);
+
+ const float3x3 transformation = float3x3::from_rotation(rotation);
+
+ result.transform(transformation);
+ result.get_realization_options().interpolation = get_interpolation();
+ }
+
+ Interpolation get_interpolation()
+ {
+ switch (bnode().custom1) {
+ case 0:
+ return Interpolation::Nearest;
+ case 1:
+ return Interpolation::Bilinear;
+ case 2:
+ return Interpolation::Bicubic;
+ }
+
+ BLI_assert_unreachable();
+ return Interpolation::Nearest;
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new RotateOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_rotate_cc
void register_node_type_cmp_rotate()
@@ -47,6 +96,7 @@ void register_node_type_cmp_rotate()
ntype.declare = file_ns::cmp_node_rotate_declare;
ntype.draw_buttons = file_ns::node_composit_buts_rotate;
node_type_init(&ntype, file_ns::node_composit_init_rotate);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_scale.cc b/source/blender/nodes/composite/nodes/node_composite_scale.cc
index b2b42a3613c..8b43ae8c9ca 100644
--- a/source/blender/nodes/composite/nodes/node_composite_scale.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_scale.cc
@@ -10,6 +10,8 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
/* **************** Scale ******************** */
@@ -55,6 +57,23 @@ static void node_composit_buts_scale(uiLayout *layout, bContext *UNUSED(C), Poin
}
}
+using namespace blender::realtime_compositor;
+
+class ScaleOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ get_input("Image").pass_through(get_result("Image"));
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new ScaleOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_scale_cc
void register_node_type_cmp_scale()
@@ -67,6 +86,7 @@ void register_node_type_cmp_scale()
ntype.declare = file_ns::cmp_node_scale_declare;
ntype.draw_buttons = file_ns::node_composit_buts_scale;
node_type_update(&ntype, file_ns::node_composite_update_scale);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_scene_time.cc b/source/blender/nodes/composite/nodes/node_composite_scene_time.cc
index 20bafb0d3d4..1f5317378bb 100644
--- a/source/blender/nodes/composite/nodes/node_composite_scene_time.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_scene_time.cc
@@ -3,6 +3,8 @@
* \ingroup cmpnodes
*/
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
namespace blender::nodes {
@@ -13,6 +15,38 @@ static void cmp_node_scene_time_declare(NodeDeclarationBuilder &b)
b.add_output<decl::Float>(N_("Frame"));
}
+using namespace blender::realtime_compositor;
+
+class SceneTimeOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ execute_seconds();
+ execute_frame();
+ }
+
+ void execute_seconds()
+ {
+ Result &result = get_result("Seconds");
+ result.allocate_single_value();
+ result.set_float_value(context().get_time());
+ }
+
+ void execute_frame()
+ {
+ Result &result = get_result("Frame");
+ result.allocate_single_value();
+ result.set_float_value(static_cast<float>(context().get_frame_number()));
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new SceneTimeOperation(context, node);
+}
+
} // namespace blender::nodes
void register_node_type_cmp_scene_time()
@@ -21,5 +55,7 @@ void register_node_type_cmp_scene_time()
cmp_node_type_base(&ntype, CMP_NODE_SCENE_TIME, "Scene Time", NODE_CLASS_INPUT);
ntype.declare = blender::nodes::cmp_node_scene_time_declare;
+ ntype.get_compositor_operation = blender::nodes::get_compositor_operation;
+
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_sepcomb_color.cc b/source/blender/nodes/composite/nodes/node_composite_sepcomb_color.cc
index b253656a628..d1f0b7977f8 100644
--- a/source/blender/nodes/composite/nodes/node_composite_sepcomb_color.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_sepcomb_color.cc
@@ -1,5 +1,11 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
+#include "BLI_assert.h"
+
+#include "GPU_material.h"
+
+#include "COM_shader_node.hh"
+
#include "node_composite_util.hh"
static void node_cmp_combsep_color_init(bNodeTree *UNUSED(ntree), bNode *node)
@@ -58,7 +64,9 @@ namespace blender::nodes::node_composite_separate_color_cc {
static void cmp_node_separate_color_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
+ b.add_input<decl::Color>(N_("Image"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(0);
b.add_output<decl::Float>(N_("Red"));
b.add_output<decl::Float>(N_("Green"));
b.add_output<decl::Float>(N_("Blue"));
@@ -71,6 +79,57 @@ static void cmp_node_separate_color_update(bNodeTree *UNUSED(ntree), bNode *node
node_cmp_combsep_color_label(&node->outputs, (CMPNodeCombSepColorMode)storage->mode);
}
+using namespace blender::realtime_compositor;
+
+class SeparateColorShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ GPU_stack_link(material, &bnode(), get_shader_function_name(), inputs, outputs);
+ }
+
+ NodeCMPCombSepColor *get_node_combine_separate_color()
+ {
+ return static_cast<NodeCMPCombSepColor *>(bnode().storage);
+ }
+
+ const char *get_shader_function_name()
+ {
+ switch (get_node_combine_separate_color()->mode) {
+ case CMP_NODE_COMBSEP_COLOR_RGB:
+ return "node_composite_separate_rgba";
+ case CMP_NODE_COMBSEP_COLOR_HSV:
+ return "node_composite_separate_hsva";
+ case CMP_NODE_COMBSEP_COLOR_HSL:
+ return "node_composite_separate_hsla";
+ case CMP_NODE_COMBSEP_COLOR_YUV:
+ return "node_composite_separate_yuva_itu_709";
+ case CMP_NODE_COMBSEP_COLOR_YCC:
+ switch (get_node_combine_separate_color()->ycc_mode) {
+ case BLI_YCC_ITU_BT601:
+ return "node_composite_separate_ycca_itu_601";
+ case BLI_YCC_ITU_BT709:
+ return "node_composite_separate_ycca_itu_709";
+ case BLI_YCC_JFIF_0_255:
+ return "node_composite_separate_ycca_jpeg";
+ }
+ }
+
+ BLI_assert_unreachable();
+ return nullptr;
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new SeparateColorShaderNode(node);
+}
+
} // namespace blender::nodes::node_composite_separate_color_cc
void register_node_type_cmp_separate_color()
@@ -85,6 +144,7 @@ void register_node_type_cmp_separate_color()
node_type_storage(
&ntype, "NodeCMPCombSepColor", node_free_standard_storage, node_copy_standard_storage);
node_type_update(&ntype, file_ns::cmp_node_separate_color_update);
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
@@ -95,22 +155,30 @@ namespace blender::nodes::node_composite_combine_color_cc {
static void cmp_node_combine_color_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Float>(N_("Red")).default_value(0.0f).min(0.0f).max(1.0f).subtype(PROP_FACTOR);
+ b.add_input<decl::Float>(N_("Red"))
+ .default_value(0.0f)
+ .min(0.0f)
+ .max(1.0f)
+ .subtype(PROP_FACTOR)
+ .compositor_domain_priority(0);
b.add_input<decl::Float>(N_("Green"))
.default_value(0.0f)
.min(0.0f)
.max(1.0f)
- .subtype(PROP_FACTOR);
+ .subtype(PROP_FACTOR)
+ .compositor_domain_priority(1);
b.add_input<decl::Float>(N_("Blue"))
.default_value(0.0f)
.min(0.0f)
.max(1.0f)
- .subtype(PROP_FACTOR);
+ .subtype(PROP_FACTOR)
+ .compositor_domain_priority(2);
b.add_input<decl::Float>(N_("Alpha"))
.default_value(1.0f)
.min(0.0f)
.max(1.0f)
- .subtype(PROP_FACTOR);
+ .subtype(PROP_FACTOR)
+ .compositor_domain_priority(3);
b.add_output<decl::Color>(N_("Image"));
}
@@ -120,6 +188,57 @@ static void cmp_node_combine_color_update(bNodeTree *UNUSED(ntree), bNode *node)
node_cmp_combsep_color_label(&node->inputs, (CMPNodeCombSepColorMode)storage->mode);
}
+using namespace blender::realtime_compositor;
+
+class CombineColorShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ GPU_stack_link(material, &bnode(), get_shader_function_name(), inputs, outputs);
+ }
+
+ NodeCMPCombSepColor *get_node_combine_separate_color()
+ {
+ return static_cast<NodeCMPCombSepColor *>(bnode().storage);
+ }
+
+ const char *get_shader_function_name()
+ {
+ switch (get_node_combine_separate_color()->mode) {
+ case CMP_NODE_COMBSEP_COLOR_RGB:
+ return "node_composite_combine_rgba";
+ case CMP_NODE_COMBSEP_COLOR_HSV:
+ return "node_composite_combine_hsva";
+ case CMP_NODE_COMBSEP_COLOR_HSL:
+ return "node_composite_combine_hsla";
+ case CMP_NODE_COMBSEP_COLOR_YUV:
+ return "node_composite_combine_yuva_itu_709";
+ case CMP_NODE_COMBSEP_COLOR_YCC:
+ switch (get_node_combine_separate_color()->ycc_mode) {
+ case BLI_YCC_ITU_BT601:
+ return "node_composite_combine_ycca_itu_601";
+ case BLI_YCC_ITU_BT709:
+ return "node_composite_combine_ycca_itu_709";
+ case BLI_YCC_JFIF_0_255:
+ return "node_composite_combine_ycca_jpeg";
+ }
+ }
+
+ BLI_assert_unreachable();
+ return nullptr;
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new CombineColorShaderNode(node);
+}
+
} // namespace blender::nodes::node_composite_combine_color_cc
void register_node_type_cmp_combine_color()
@@ -134,6 +253,7 @@ void register_node_type_cmp_combine_color()
node_type_storage(
&ntype, "NodeCMPCombSepColor", node_free_standard_storage, node_copy_standard_storage);
node_type_update(&ntype, file_ns::cmp_node_combine_color_update);
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_sepcomb_hsva.cc b/source/blender/nodes/composite/nodes/node_composite_sepcomb_hsva.cc
index a169f7e0dd3..b655c0db106 100644
--- a/source/blender/nodes/composite/nodes/node_composite_sepcomb_hsva.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_sepcomb_hsva.cc
@@ -5,60 +5,112 @@
* \ingroup cmpnodes
*/
+#include "GPU_material.h"
+
+#include "COM_shader_node.hh"
+
#include "node_composite_util.hh"
/* **************** SEPARATE HSVA ******************** */
-namespace blender::nodes::node_composite_sepcomb_hsva_cc {
+namespace blender::nodes::node_composite_separate_hsva_cc {
static void cmp_node_sephsva_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
+ b.add_input<decl::Color>(N_("Image"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(0);
b.add_output<decl::Float>(N_("H"));
b.add_output<decl::Float>(N_("S"));
b.add_output<decl::Float>(N_("V"));
b.add_output<decl::Float>(N_("A"));
}
-} // namespace blender::nodes::node_composite_sepcomb_hsva_cc
+using namespace blender::realtime_compositor;
+
+class SeparateHSVAShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ GPU_stack_link(material, &bnode(), "node_composite_separate_hsva", inputs, outputs);
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new SeparateHSVAShaderNode(node);
+}
+
+} // namespace blender::nodes::node_composite_separate_hsva_cc
void register_node_type_cmp_sephsva()
{
- namespace file_ns = blender::nodes::node_composite_sepcomb_hsva_cc;
+ namespace file_ns = blender::nodes::node_composite_separate_hsva_cc;
static bNodeType ntype;
cmp_node_type_base(&ntype, CMP_NODE_SEPHSVA_LEGACY, "Separate HSVA", NODE_CLASS_CONVERTER);
ntype.declare = file_ns::cmp_node_sephsva_declare;
ntype.gather_link_search_ops = nullptr;
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
/* **************** COMBINE HSVA ******************** */
-namespace blender::nodes::node_composite_sepcomb_hsva_cc {
+namespace blender::nodes::node_composite_combine_hsva_cc {
static void cmp_node_combhsva_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Float>(N_("H")).min(0.0f).max(1.0f);
- b.add_input<decl::Float>(N_("S")).min(0.0f).max(1.0f);
- b.add_input<decl::Float>(N_("V")).min(0.0f).max(1.0f);
- b.add_input<decl::Float>(N_("A")).default_value(1.0f).min(0.0f).max(1.0f);
+ b.add_input<decl::Float>(N_("H")).min(0.0f).max(1.0f).compositor_domain_priority(0);
+ b.add_input<decl::Float>(N_("S")).min(0.0f).max(1.0f).compositor_domain_priority(1);
+ b.add_input<decl::Float>(N_("V")).min(0.0f).max(1.0f).compositor_domain_priority(2);
+ b.add_input<decl::Float>(N_("A"))
+ .default_value(1.0f)
+ .min(0.0f)
+ .max(1.0f)
+ .compositor_domain_priority(3);
b.add_output<decl::Color>(N_("Image"));
}
-} // namespace blender::nodes::node_composite_sepcomb_hsva_cc
+using namespace blender::realtime_compositor;
+
+class CombineHSVAShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ GPU_stack_link(material, &bnode(), "node_composite_combine_hsva", inputs, outputs);
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new CombineHSVAShaderNode(node);
+}
+
+} // namespace blender::nodes::node_composite_combine_hsva_cc
void register_node_type_cmp_combhsva()
{
- namespace file_ns = blender::nodes::node_composite_sepcomb_hsva_cc;
+ namespace file_ns = blender::nodes::node_composite_combine_hsva_cc;
static bNodeType ntype;
cmp_node_type_base(&ntype, CMP_NODE_COMBHSVA_LEGACY, "Combine HSVA", NODE_CLASS_CONVERTER);
ntype.declare = file_ns::cmp_node_combhsva_declare;
ntype.gather_link_search_ops = nullptr;
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_sepcomb_rgba.cc b/source/blender/nodes/composite/nodes/node_composite_sepcomb_rgba.cc
index a243500b56d..1f4c9fd153f 100644
--- a/source/blender/nodes/composite/nodes/node_composite_sepcomb_rgba.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_sepcomb_rgba.cc
@@ -5,59 +5,112 @@
* \ingroup cmpnodes
*/
+#include "GPU_material.h"
+
+#include "COM_shader_node.hh"
+
#include "node_composite_util.hh"
/* **************** SEPARATE RGBA ******************** */
-namespace blender::nodes::node_composite_sepcomb_rgba_cc {
+
+namespace blender::nodes::node_composite_separate_rgba_cc {
static void cmp_node_seprgba_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
+ b.add_input<decl::Color>(N_("Image"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(0);
b.add_output<decl::Float>(N_("R"));
b.add_output<decl::Float>(N_("G"));
b.add_output<decl::Float>(N_("B"));
b.add_output<decl::Float>(N_("A"));
}
-} // namespace blender::nodes::node_composite_sepcomb_rgba_cc
+using namespace blender::realtime_compositor;
+
+class SeparateRGBAShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ GPU_stack_link(material, &bnode(), "node_composite_separate_rgba", inputs, outputs);
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new SeparateRGBAShaderNode(node);
+}
+
+} // namespace blender::nodes::node_composite_separate_rgba_cc
void register_node_type_cmp_seprgba()
{
- namespace file_ns = blender::nodes::node_composite_sepcomb_rgba_cc;
+ namespace file_ns = blender::nodes::node_composite_separate_rgba_cc;
static bNodeType ntype;
cmp_node_type_base(&ntype, CMP_NODE_SEPRGBA_LEGACY, "Separate RGBA", NODE_CLASS_CONVERTER);
ntype.declare = file_ns::cmp_node_seprgba_declare;
ntype.gather_link_search_ops = nullptr;
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
/* **************** COMBINE RGBA ******************** */
-namespace blender::nodes::node_composite_sepcomb_rgba_cc {
+namespace blender::nodes::node_composite_combine_rgba_cc {
static void cmp_node_combrgba_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Float>(N_("R")).min(0.0f).max(1.0f);
- b.add_input<decl::Float>(N_("G")).min(0.0f).max(1.0f);
- b.add_input<decl::Float>(N_("B")).min(0.0f).max(1.0f);
- b.add_input<decl::Float>(N_("A")).default_value(1.0f).min(0.0f).max(1.0f);
+ b.add_input<decl::Float>(N_("R")).min(0.0f).max(1.0f).compositor_domain_priority(0);
+ b.add_input<decl::Float>(N_("G")).min(0.0f).max(1.0f).compositor_domain_priority(1);
+ b.add_input<decl::Float>(N_("B")).min(0.0f).max(1.0f).compositor_domain_priority(2);
+ b.add_input<decl::Float>(N_("A"))
+ .default_value(1.0f)
+ .min(0.0f)
+ .max(1.0f)
+ .compositor_domain_priority(3);
b.add_output<decl::Color>(N_("Image"));
}
-} // namespace blender::nodes::node_composite_sepcomb_rgba_cc
+using namespace blender::realtime_compositor;
+
+class CombineRGBAShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ GPU_stack_link(material, &bnode(), "node_composite_combine_rgba", inputs, outputs);
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new CombineRGBAShaderNode(node);
+}
+
+} // namespace blender::nodes::node_composite_combine_rgba_cc
void register_node_type_cmp_combrgba()
{
- namespace file_ns = blender::nodes::node_composite_sepcomb_rgba_cc;
+ namespace file_ns = blender::nodes::node_composite_combine_rgba_cc;
static bNodeType ntype;
cmp_node_type_base(&ntype, CMP_NODE_COMBRGBA_LEGACY, "Combine RGBA", NODE_CLASS_CONVERTER);
ntype.declare = file_ns::cmp_node_combrgba_declare;
ntype.gather_link_search_ops = nullptr;
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_sepcomb_xyz.cc b/source/blender/nodes/composite/nodes/node_composite_sepcomb_xyz.cc
index 4979c376cab..e288e698808 100644
--- a/source/blender/nodes/composite/nodes/node_composite_sepcomb_xyz.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_sepcomb_xyz.cc
@@ -5,10 +5,15 @@
* \ingroup cmpnodes
*/
+#include "GPU_material.h"
+
+#include "COM_shader_node.hh"
+
#include "node_composite_util.hh"
/* **************** SEPARATE XYZ ******************** */
-namespace blender::nodes {
+
+namespace blender::nodes::node_composite_separate_xyz_cc {
static void cmp_node_separate_xyz_declare(NodeDeclarationBuilder &b)
{
@@ -18,21 +23,44 @@ static void cmp_node_separate_xyz_declare(NodeDeclarationBuilder &b)
b.add_output<decl::Float>("Z");
}
-} // namespace blender::nodes
+using namespace blender::realtime_compositor;
+
+class SeparateXYZShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ GPU_stack_link(material, &bnode(), "node_composite_separate_xyz", inputs, outputs);
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new SeparateXYZShaderNode(node);
+}
+
+} // namespace blender::nodes::node_composite_separate_xyz_cc
void register_node_type_cmp_separate_xyz()
{
+ namespace file_ns = blender::nodes::node_composite_separate_xyz_cc;
+
static bNodeType ntype;
cmp_node_type_base(&ntype, CMP_NODE_SEPARATE_XYZ, "Separate XYZ", NODE_CLASS_CONVERTER);
- ntype.declare = blender::nodes::cmp_node_separate_xyz_declare;
+ ntype.declare = file_ns::cmp_node_separate_xyz_declare;
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
/* **************** COMBINE XYZ ******************** */
-namespace blender::nodes {
+namespace blender::nodes::node_composite_combine_xyz_cc {
static void cmp_node_combine_xyz_declare(NodeDeclarationBuilder &b)
{
@@ -42,14 +70,37 @@ static void cmp_node_combine_xyz_declare(NodeDeclarationBuilder &b)
b.add_output<decl::Vector>("Vector");
}
-} // namespace blender::nodes
+using namespace blender::realtime_compositor;
+
+class CombineXYZShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ GPU_stack_link(material, &bnode(), "node_composite_combine_xyz", inputs, outputs);
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new CombineXYZShaderNode(node);
+}
+
+} // namespace blender::nodes::node_composite_combine_xyz_cc
void register_node_type_cmp_combine_xyz()
{
+ namespace file_ns = blender::nodes::node_composite_combine_xyz_cc;
+
static bNodeType ntype;
cmp_node_type_base(&ntype, CMP_NODE_COMBINE_XYZ, "Combine XYZ", NODE_CLASS_CONVERTER);
- ntype.declare = blender::nodes::cmp_node_combine_xyz_declare;
+ ntype.declare = file_ns::cmp_node_combine_xyz_declare;
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_sepcomb_ycca.cc b/source/blender/nodes/composite/nodes/node_composite_sepcomb_ycca.cc
index 51d3c18d238..bebe6abe115 100644
--- a/source/blender/nodes/composite/nodes/node_composite_sepcomb_ycca.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_sepcomb_ycca.cc
@@ -5,15 +5,23 @@
* \ingroup cmpnodes
*/
+#include "BLI_assert.h"
+
+#include "GPU_material.h"
+
+#include "COM_shader_node.hh"
+
#include "node_composite_util.hh"
/* **************** SEPARATE YCCA ******************** */
-namespace blender::nodes::node_composite_sepcomb_ycca_cc {
+namespace blender::nodes::node_composite_separate_ycca_cc {
static void cmp_node_sepycca_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
+ b.add_input<decl::Color>(N_("Image"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(0);
b.add_output<decl::Float>(N_("Y"));
b.add_output<decl::Float>(N_("Cb"));
b.add_output<decl::Float>(N_("Cr"));
@@ -25,11 +33,51 @@ static void node_composit_init_mode_sepycca(bNodeTree *UNUSED(ntree), bNode *nod
node->custom1 = 1; /* BLI_YCC_ITU_BT709 */
}
-} // namespace blender::nodes::node_composite_sepcomb_ycca_cc
+using namespace blender::realtime_compositor;
+
+class SeparateYCCAShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ GPU_stack_link(material, &bnode(), get_shader_function_name(), inputs, outputs);
+ }
+
+ int get_mode()
+ {
+ return bnode().custom1;
+ }
+
+ const char *get_shader_function_name()
+ {
+ switch (get_mode()) {
+ case BLI_YCC_ITU_BT601:
+ return "node_composite_separate_ycca_itu_601";
+ case BLI_YCC_ITU_BT709:
+ return "node_composite_separate_ycca_itu_709";
+ case BLI_YCC_JFIF_0_255:
+ return "node_composite_separate_ycca_jpeg";
+ }
+
+ BLI_assert_unreachable();
+ return nullptr;
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new SeparateYCCAShaderNode(node);
+}
+
+} // namespace blender::nodes::node_composite_separate_ycca_cc
void register_node_type_cmp_sepycca()
{
- namespace file_ns = blender::nodes::node_composite_sepcomb_ycca_cc;
+ namespace file_ns = blender::nodes::node_composite_separate_ycca_cc;
static bNodeType ntype;
@@ -37,20 +85,33 @@ void register_node_type_cmp_sepycca()
ntype.declare = file_ns::cmp_node_sepycca_declare;
node_type_init(&ntype, file_ns::node_composit_init_mode_sepycca);
ntype.gather_link_search_ops = nullptr;
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
/* **************** COMBINE YCCA ******************** */
-namespace blender::nodes::node_composite_sepcomb_ycca_cc {
+namespace blender::nodes::node_composite_combine_ycca_cc {
static void cmp_node_combycca_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Float>(N_("Y")).min(0.0f).max(1.0f);
- b.add_input<decl::Float>(N_("Cb")).default_value(0.5f).min(0.0f).max(1.0f);
- b.add_input<decl::Float>(N_("Cr")).default_value(0.5f).min(0.0f).max(1.0f);
- b.add_input<decl::Float>(N_("A")).default_value(1.0f).min(0.0f).max(1.0f);
+ b.add_input<decl::Float>(N_("Y")).min(0.0f).max(1.0f).compositor_domain_priority(0);
+ b.add_input<decl::Float>(N_("Cb"))
+ .default_value(0.5f)
+ .min(0.0f)
+ .max(1.0f)
+ .compositor_domain_priority(1);
+ b.add_input<decl::Float>(N_("Cr"))
+ .default_value(0.5f)
+ .min(0.0f)
+ .max(1.0f)
+ .compositor_domain_priority(2);
+ b.add_input<decl::Float>(N_("A"))
+ .default_value(1.0f)
+ .min(0.0f)
+ .max(1.0f)
+ .compositor_domain_priority(3);
b.add_output<decl::Color>(N_("Image"));
}
@@ -59,11 +120,51 @@ static void node_composit_init_mode_combycca(bNodeTree *UNUSED(ntree), bNode *no
node->custom1 = 1; /* BLI_YCC_ITU_BT709 */
}
-} // namespace blender::nodes::node_composite_sepcomb_ycca_cc
+using namespace blender::realtime_compositor;
+
+class CombineYCCAShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ GPU_stack_link(material, &bnode(), get_shader_function_name(), inputs, outputs);
+ }
+
+ int get_mode()
+ {
+ return bnode().custom1;
+ }
+
+ const char *get_shader_function_name()
+ {
+ switch (get_mode()) {
+ case BLI_YCC_ITU_BT601:
+ return "node_composite_combine_ycca_itu_601";
+ case BLI_YCC_ITU_BT709:
+ return "node_composite_combine_ycca_itu_709";
+ case BLI_YCC_JFIF_0_255:
+ return "node_composite_combine_ycca_jpeg";
+ }
+
+ BLI_assert_unreachable();
+ return nullptr;
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new CombineYCCAShaderNode(node);
+}
+
+} // namespace blender::nodes::node_composite_combine_ycca_cc
void register_node_type_cmp_combycca()
{
- namespace file_ns = blender::nodes::node_composite_sepcomb_ycca_cc;
+ namespace file_ns = blender::nodes::node_composite_combine_ycca_cc;
static bNodeType ntype;
@@ -71,6 +172,7 @@ void register_node_type_cmp_combycca()
ntype.declare = file_ns::cmp_node_combycca_declare;
node_type_init(&ntype, file_ns::node_composit_init_mode_combycca);
ntype.gather_link_search_ops = nullptr;
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_sepcomb_yuva.cc b/source/blender/nodes/composite/nodes/node_composite_sepcomb_yuva.cc
index 4acd2294114..1f0eb04cfc3 100644
--- a/source/blender/nodes/composite/nodes/node_composite_sepcomb_yuva.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_sepcomb_yuva.cc
@@ -5,60 +5,112 @@
* \ingroup cmpnodes
*/
+#include "GPU_material.h"
+
+#include "COM_shader_node.hh"
+
#include "node_composite_util.hh"
/* **************** SEPARATE YUVA ******************** */
-namespace blender::nodes::node_composite_sepcomb_yuva_cc {
+namespace blender::nodes::node_composite_separate_yuva_cc {
static void cmp_node_sepyuva_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
+ b.add_input<decl::Color>(N_("Image"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(0);
b.add_output<decl::Float>(N_("Y"));
b.add_output<decl::Float>(N_("U"));
b.add_output<decl::Float>(N_("V"));
b.add_output<decl::Float>(N_("A"));
}
-} // namespace blender::nodes::node_composite_sepcomb_yuva_cc
+using namespace blender::realtime_compositor;
+
+class SeparateYUVAShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ GPU_stack_link(material, &bnode(), "node_composite_separate_yuva_itu_709", inputs, outputs);
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new SeparateYUVAShaderNode(node);
+}
+
+} // namespace blender::nodes::node_composite_separate_yuva_cc
void register_node_type_cmp_sepyuva()
{
- namespace file_ns = blender::nodes::node_composite_sepcomb_yuva_cc;
+ namespace file_ns = blender::nodes::node_composite_separate_yuva_cc;
static bNodeType ntype;
cmp_node_type_base(&ntype, CMP_NODE_SEPYUVA_LEGACY, "Separate YUVA", NODE_CLASS_CONVERTER);
ntype.declare = file_ns::cmp_node_sepyuva_declare;
ntype.gather_link_search_ops = nullptr;
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
/* **************** COMBINE YUVA ******************** */
-namespace blender::nodes::node_composite_sepcomb_yuva_cc {
+namespace blender::nodes::node_composite_combine_yuva_cc {
static void cmp_node_combyuva_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Float>(N_("Y")).min(0.0f).max(1.0f);
- b.add_input<decl::Float>(N_("U")).min(0.0f).max(1.0f);
- b.add_input<decl::Float>(N_("V")).min(0.0f).max(1.0f);
- b.add_input<decl::Float>(N_("A")).default_value(1.0f).min(0.0f).max(1.0f);
+ b.add_input<decl::Float>(N_("Y")).min(0.0f).max(1.0f).compositor_domain_priority(0);
+ b.add_input<decl::Float>(N_("U")).min(0.0f).max(1.0f).compositor_domain_priority(1);
+ b.add_input<decl::Float>(N_("V")).min(0.0f).max(1.0f).compositor_domain_priority(2);
+ b.add_input<decl::Float>(N_("A"))
+ .default_value(1.0f)
+ .min(0.0f)
+ .max(1.0f)
+ .compositor_domain_priority(3);
b.add_output<decl::Color>(N_("Image"));
}
-} // namespace blender::nodes::node_composite_sepcomb_yuva_cc
+using namespace blender::realtime_compositor;
+
+class CombineYUVAShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ GPU_stack_link(material, &bnode(), "node_composite_combine_yuva_itu_709", inputs, outputs);
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new CombineYUVAShaderNode(node);
+}
+
+} // namespace blender::nodes::node_composite_combine_yuva_cc
void register_node_type_cmp_combyuva()
{
- namespace file_ns = blender::nodes::node_composite_sepcomb_yuva_cc;
+ namespace file_ns = blender::nodes::node_composite_combine_yuva_cc;
static bNodeType ntype;
cmp_node_type_base(&ntype, CMP_NODE_COMBYUVA_LEGACY, "Combine YUVA", NODE_CLASS_CONVERTER);
ntype.declare = file_ns::cmp_node_combyuva_declare;
ntype.gather_link_search_ops = nullptr;
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_setalpha.cc b/source/blender/nodes/composite/nodes/node_composite_setalpha.cc
index 8aeaafbbf67..9930125aa70 100644
--- a/source/blender/nodes/composite/nodes/node_composite_setalpha.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_setalpha.cc
@@ -8,6 +8,10 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "GPU_material.h"
+
+#include "COM_shader_node.hh"
+
#include "node_composite_util.hh"
/* **************** SET ALPHA ******************** */
@@ -16,8 +20,14 @@ namespace blender::nodes::node_composite_setalpha_cc {
static void cmp_node_setalpha_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
- b.add_input<decl::Float>(N_("Alpha")).default_value(1.0f).min(0.0f).max(1.0f);
+ b.add_input<decl::Color>(N_("Image"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(0);
+ b.add_input<decl::Float>(N_("Alpha"))
+ .default_value(1.0f)
+ .min(0.0f)
+ .max(1.0f)
+ .compositor_domain_priority(1);
b.add_output<decl::Color>(N_("Image"));
}
@@ -33,6 +43,36 @@ static void node_composit_buts_set_alpha(uiLayout *layout, bContext *UNUSED(C),
uiItemR(layout, ptr, "mode", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class SetAlphaShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ if (get_node_set_alpha()->mode == CMP_NODE_SETALPHA_MODE_APPLY) {
+ GPU_stack_link(material, &bnode(), "node_composite_set_alpha_apply", inputs, outputs);
+ return;
+ }
+
+ GPU_stack_link(material, &bnode(), "node_composite_set_alpha_replace", inputs, outputs);
+ }
+
+ NodeSetAlpha *get_node_set_alpha()
+ {
+ return static_cast<NodeSetAlpha *>(bnode().storage);
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new SetAlphaShaderNode(node);
+}
+
} // namespace blender::nodes::node_composite_setalpha_cc
void register_node_type_cmp_setalpha()
@@ -47,6 +87,7 @@ void register_node_type_cmp_setalpha()
node_type_init(&ntype, file_ns::node_composit_init_setalpha);
node_type_storage(
&ntype, "NodeSetAlpha", node_free_standard_storage, node_copy_standard_storage);
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_split_viewer.cc b/source/blender/nodes/composite/nodes/node_composite_split_viewer.cc
index ab325c4559f..085de69e63e 100644
--- a/source/blender/nodes/composite/nodes/node_composite_split_viewer.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_split_viewer.cc
@@ -11,6 +11,12 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "GPU_shader.h"
+#include "GPU_texture.h"
+
+#include "COM_node_operation.hh"
+#include "COM_utilities.hh"
+
#include "node_composite_util.hh"
/* **************** SPLIT VIEWER ******************** */
@@ -43,6 +49,70 @@ static void node_composit_buts_splitviewer(uiLayout *layout, bContext *UNUSED(C)
uiItemR(col, ptr, "factor", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class ViewerOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ GPUShader *shader = get_split_viewer_shader();
+ GPU_shader_bind(shader);
+
+ const int2 size = compute_domain().size;
+
+ GPU_shader_uniform_1f(shader, "split_ratio", get_split_ratio());
+ GPU_shader_uniform_2iv(shader, "view_size", size);
+
+ const Result &first_image = get_input("Image");
+ first_image.bind_as_texture(shader, "first_image_tx");
+ const Result &second_image = get_input("Image_001");
+ second_image.bind_as_texture(shader, "second_image_tx");
+
+ GPUTexture *output_texture = context().get_output_texture();
+ const int image_unit = GPU_shader_get_texture_binding(shader, "output_img");
+ GPU_texture_image_bind(output_texture, image_unit);
+
+ compute_dispatch_threads_at_least(shader, size);
+
+ first_image.unbind_as_texture();
+ second_image.unbind_as_texture();
+ GPU_texture_image_unbind(output_texture);
+ GPU_shader_unbind();
+ }
+
+ /* The operation domain have the same dimensions of the output without any transformations. */
+ Domain compute_domain() override
+ {
+ return Domain(context().get_output_size());
+ }
+
+ GPUShader *get_split_viewer_shader()
+ {
+ if (get_split_axis() == CMP_NODE_SPLIT_VIEWER_HORIZONTAL) {
+ return shader_manager().get("compositor_split_viewer_horizontal");
+ }
+
+ return shader_manager().get("compositor_split_viewer_vertical");
+ }
+
+ CMPNodeSplitViewerAxis get_split_axis()
+ {
+ return (CMPNodeSplitViewerAxis)bnode().custom2;
+ }
+
+ float get_split_ratio()
+ {
+ return bnode().custom1 / 100.0f;
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new ViewerOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_split_viewer_cc
void register_node_type_cmp_splitviewer()
@@ -57,6 +127,7 @@ void register_node_type_cmp_splitviewer()
ntype.flag |= NODE_PREVIEW;
node_type_init(&ntype, file_ns::node_composit_init_splitviewer);
node_type_storage(&ntype, "ImageUser", node_free_standard_storage, node_copy_standard_storage);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
ntype.no_muting = true;
diff --git a/source/blender/nodes/composite/nodes/node_composite_stabilize2d.cc b/source/blender/nodes/composite/nodes/node_composite_stabilize2d.cc
index 63d00a0864b..75a96a05863 100644
--- a/source/blender/nodes/composite/nodes/node_composite_stabilize2d.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_stabilize2d.cc
@@ -11,6 +11,8 @@
#include "BKE_context.h"
#include "BKE_lib_id.h"
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
/* **************** Stabilize 2D ******************** */
@@ -58,6 +60,23 @@ static void node_composit_buts_stabilize2d(uiLayout *layout, bContext *C, Pointe
uiItemR(layout, ptr, "invert", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class Stabilize2DOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ get_input("Image").pass_through(get_result("Image"));
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new Stabilize2DOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_stabilize2d_cc
void register_node_type_cmp_stabilize2d()
@@ -70,6 +89,7 @@ void register_node_type_cmp_stabilize2d()
ntype.declare = file_ns::cmp_node_stabilize2d_declare;
ntype.draw_buttons = file_ns::node_composit_buts_stabilize2d;
ntype.initfunc_api = file_ns::init;
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_sunbeams.cc b/source/blender/nodes/composite/nodes/node_composite_sunbeams.cc
index 766f26745ef..4b9264d7e35 100644
--- a/source/blender/nodes/composite/nodes/node_composite_sunbeams.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_sunbeams.cc
@@ -8,6 +8,8 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
namespace blender::nodes::node_composite_sunbeams_cc {
@@ -38,6 +40,23 @@ static void node_composit_buts_sunbeams(uiLayout *layout, bContext *UNUSED(C), P
ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class SunBeamsOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ get_input("Image").pass_through(get_result("Image"));
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new SunBeamsOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_sunbeams_cc
void register_node_type_cmp_sunbeams()
@@ -52,6 +71,7 @@ void register_node_type_cmp_sunbeams()
node_type_init(&ntype, file_ns::init);
node_type_storage(
&ntype, "NodeSunBeams", node_free_standard_storage, node_copy_standard_storage);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_switch.cc b/source/blender/nodes/composite/nodes/node_composite_switch.cc
index bda490572e9..767802cc442 100644
--- a/source/blender/nodes/composite/nodes/node_composite_switch.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_switch.cc
@@ -8,6 +8,8 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
/* **************** Switch ******************** */
@@ -26,6 +28,30 @@ static void node_composit_buts_switch(uiLayout *layout, bContext *UNUSED(C), Poi
uiItemR(layout, ptr, "check", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class SwitchOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ Result &input = get_input(get_condition() ? "On" : "Off");
+ Result &result = get_result("Image");
+ input.pass_through(result);
+ }
+
+ bool get_condition()
+ {
+ return bnode().custom1;
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new SwitchOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_switch_cc
void register_node_type_cmp_switch()
@@ -38,5 +64,7 @@ void register_node_type_cmp_switch()
ntype.declare = file_ns::cmp_node_switch_declare;
ntype.draw_buttons = file_ns::node_composit_buts_switch;
node_type_size_preset(&ntype, NODE_SIZE_SMALL);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
+
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_switchview.cc b/source/blender/nodes/composite/nodes/node_composite_switchview.cc
index 2cf3da03a05..e74c3b6007a 100644
--- a/source/blender/nodes/composite/nodes/node_composite_switchview.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_switchview.cc
@@ -11,6 +11,8 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
/* **************** SWITCH VIEW ******************** */
@@ -140,6 +142,25 @@ static void node_composit_buts_switch_view_ex(uiLayout *layout,
nullptr);
}
+using namespace blender::realtime_compositor;
+
+class SwitchViewOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ Result &input = get_input(context().get_view_name());
+ Result &result = get_result("Image");
+ input.pass_through(result);
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new SwitchViewOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_switchview_cc
void register_node_type_cmp_switch_view()
@@ -153,6 +174,7 @@ void register_node_type_cmp_switch_view()
ntype.draw_buttons_ex = file_ns::node_composit_buts_switch_view_ex;
ntype.initfunc_api = file_ns::init_switch_view;
node_type_update(&ntype, file_ns::cmp_node_switch_view_update);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_texture.cc b/source/blender/nodes/composite/nodes/node_composite_texture.cc
index 7571e97a2cd..5a628aae7a7 100644
--- a/source/blender/nodes/composite/nodes/node_composite_texture.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_texture.cc
@@ -5,6 +5,8 @@
* \ingroup cmpnodes
*/
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
/* **************** TEXTURE ******************** */
@@ -23,6 +25,24 @@ static void cmp_node_texture_declare(NodeDeclarationBuilder &b)
b.add_output<decl::Color>(N_("Color"));
}
+using namespace blender::realtime_compositor;
+
+class TextureOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ get_result("Value").allocate_invalid();
+ get_result("Color").allocate_invalid();
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new TextureOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_texture_cc
void register_node_type_cmp_texture()
@@ -34,6 +54,7 @@ void register_node_type_cmp_texture()
cmp_node_type_base(&ntype, CMP_NODE_TEXTURE, "Texture", NODE_CLASS_INPUT);
ntype.declare = file_ns::cmp_node_texture_declare;
ntype.flag |= NODE_PREVIEW;
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_tonemap.cc b/source/blender/nodes/composite/nodes/node_composite_tonemap.cc
index cdfe97b038d..4cc3d4f32a3 100644
--- a/source/blender/nodes/composite/nodes/node_composite_tonemap.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_tonemap.cc
@@ -10,6 +10,8 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
namespace blender::nodes::node_composite_tonemap_cc {
@@ -58,6 +60,23 @@ static void node_composit_buts_tonemap(uiLayout *layout, bContext *UNUSED(C), Po
}
}
+using namespace blender::realtime_compositor;
+
+class ToneMapOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ get_input("Image").pass_through(get_result("Image"));
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new ToneMapOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_tonemap_cc
void register_node_type_cmp_tonemap()
@@ -71,6 +90,7 @@ void register_node_type_cmp_tonemap()
ntype.draw_buttons = file_ns::node_composit_buts_tonemap;
node_type_init(&ntype, file_ns::node_composit_init_tonemap);
node_type_storage(&ntype, "NodeTonemap", node_free_standard_storage, node_copy_standard_storage);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_trackpos.cc b/source/blender/nodes/composite/nodes/node_composite_trackpos.cc
index 0e99ff59327..0e9bd800f44 100644
--- a/source/blender/nodes/composite/nodes/node_composite_trackpos.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_trackpos.cc
@@ -18,6 +18,8 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
namespace blender::nodes::node_composite_trackpos_cc {
@@ -102,6 +104,25 @@ static void node_composit_buts_trackpos(uiLayout *layout, bContext *C, PointerRN
}
}
+using namespace blender::realtime_compositor;
+
+class TrackPositionOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ get_result("X").allocate_invalid();
+ get_result("Y").allocate_invalid();
+ get_result("Speed").allocate_invalid();
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new TrackPositionOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_trackpos_cc
void register_node_type_cmp_trackpos()
@@ -116,6 +137,7 @@ void register_node_type_cmp_trackpos()
ntype.initfunc_api = file_ns::init;
node_type_storage(
&ntype, "NodeTrackPosData", node_free_standard_storage, node_copy_standard_storage);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_transform.cc b/source/blender/nodes/composite/nodes/node_composite_transform.cc
index fe72f5e33ca..7c5866d2d06 100644
--- a/source/blender/nodes/composite/nodes/node_composite_transform.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_transform.cc
@@ -5,9 +5,15 @@
* \ingroup cmpnodes
*/
+#include "BLI_assert.h"
+#include "BLI_float3x3.hh"
+#include "BLI_math_vector.h"
+
#include "UI_interface.h"
#include "UI_resources.h"
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
/* **************** Transform ******************** */
@@ -16,15 +22,30 @@ namespace blender::nodes::node_composite_transform_cc {
static void cmp_node_transform_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Color>(N_("Image")).default_value({0.8f, 0.8f, 0.8f, 1.0f});
- b.add_input<decl::Float>(N_("X")).default_value(0.0f).min(-10000.0f).max(10000.0f);
- b.add_input<decl::Float>(N_("Y")).default_value(0.0f).min(-10000.0f).max(10000.0f);
+ b.add_input<decl::Color>(N_("Image"))
+ .default_value({0.8f, 0.8f, 0.8f, 1.0f})
+ .compositor_domain_priority(0);
+ b.add_input<decl::Float>(N_("X"))
+ .default_value(0.0f)
+ .min(-10000.0f)
+ .max(10000.0f)
+ .compositor_expects_single_value();
+ b.add_input<decl::Float>(N_("Y"))
+ .default_value(0.0f)
+ .min(-10000.0f)
+ .max(10000.0f)
+ .compositor_expects_single_value();
b.add_input<decl::Float>(N_("Angle"))
.default_value(0.0f)
.min(-10000.0f)
.max(10000.0f)
- .subtype(PROP_ANGLE);
- b.add_input<decl::Float>(N_("Scale")).default_value(1.0f).min(0.0001f).max(CMP_SCALE_MAX);
+ .subtype(PROP_ANGLE)
+ .compositor_expects_single_value();
+ b.add_input<decl::Float>(N_("Scale"))
+ .default_value(1.0f)
+ .min(0.0001f)
+ .max(CMP_SCALE_MAX)
+ .compositor_expects_single_value();
b.add_output<decl::Color>(N_("Image"));
}
@@ -33,6 +54,51 @@ static void node_composit_buts_transform(uiLayout *layout, bContext *UNUSED(C),
uiItemR(layout, ptr, "filter_type", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class TransformOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ Result &input = get_input("Image");
+ Result &result = get_result("Image");
+ input.pass_through(result);
+
+ const float2 translation = float2(get_input("X").get_float_value_default(0.0f),
+ get_input("Y").get_float_value_default(0.0f));
+ const float rotation = get_input("Angle").get_float_value_default(0.0f);
+ const float2 scale = float2(get_input("Scale").get_float_value_default(1.0f));
+
+ const float3x3 transformation = float3x3::from_translation_rotation_scale(
+ translation, rotation, scale);
+
+ result.transform(transformation);
+ result.get_realization_options().interpolation = get_interpolation();
+ }
+
+ Interpolation get_interpolation()
+ {
+ switch (bnode().custom1) {
+ case 0:
+ return Interpolation::Nearest;
+ case 1:
+ return Interpolation::Bilinear;
+ case 2:
+ return Interpolation::Bicubic;
+ }
+
+ BLI_assert_unreachable();
+ return Interpolation::Nearest;
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new TransformOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_transform_cc
void register_node_type_cmp_transform()
@@ -44,6 +110,7 @@ void register_node_type_cmp_transform()
cmp_node_type_base(&ntype, CMP_NODE_TRANSFORM, "Transform", NODE_CLASS_DISTORT);
ntype.declare = file_ns::cmp_node_transform_declare;
ntype.draw_buttons = file_ns::node_composit_buts_transform;
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_translate.cc b/source/blender/nodes/composite/nodes/node_composite_translate.cc
index bbdc8ca4d31..fbd53b8310f 100644
--- a/source/blender/nodes/composite/nodes/node_composite_translate.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_translate.cc
@@ -5,9 +5,14 @@
* \ingroup cmpnodes
*/
+#include "BLI_float3x3.hh"
+#include "BLI_math_vec_types.hh"
+
#include "UI_interface.h"
#include "UI_resources.h"
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
/* **************** Translate ******************** */
@@ -16,9 +21,19 @@ namespace blender::nodes::node_composite_translate_cc {
static void cmp_node_translate_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
- b.add_input<decl::Float>(N_("X")).default_value(0.0f).min(-10000.0f).max(10000.0f);
- b.add_input<decl::Float>(N_("Y")).default_value(0.0f).min(-10000.0f).max(10000.0f);
+ b.add_input<decl::Color>(N_("Image"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(0);
+ b.add_input<decl::Float>(N_("X"))
+ .default_value(0.0f)
+ .min(-10000.0f)
+ .max(10000.0f)
+ .compositor_expects_single_value();
+ b.add_input<decl::Float>(N_("Y"))
+ .default_value(0.0f)
+ .min(-10000.0f)
+ .max(10000.0f)
+ .compositor_expects_single_value();
b.add_output<decl::Color>(N_("Image"));
}
@@ -34,6 +49,59 @@ static void node_composit_buts_translate(uiLayout *layout, bContext *UNUSED(C),
uiItemR(layout, ptr, "wrap_axis", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class TranslateOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ Result &input = get_input("Image");
+ Result &result = get_result("Image");
+ input.pass_through(result);
+
+ float x = get_input("X").get_float_value_default(0.0f);
+ float y = get_input("Y").get_float_value_default(0.0f);
+ if (get_use_relative()) {
+ x *= input.domain().size.x;
+ y *= input.domain().size.y;
+ }
+
+ const float2 translation = float2(x, y);
+ const float3x3 transformation = float3x3::from_translation(translation);
+
+ result.transform(transformation);
+ result.get_realization_options().repeat_x = get_repeat_x();
+ result.get_realization_options().repeat_y = get_repeat_y();
+ }
+
+ NodeTranslateData &get_node_translate()
+ {
+ return *static_cast<NodeTranslateData *>(bnode().storage);
+ }
+
+ bool get_use_relative()
+ {
+ return get_node_translate().relative;
+ }
+
+ bool get_repeat_x()
+ {
+ return ELEM(get_node_translate().wrap_axis, CMP_NODE_WRAP_X, CMP_NODE_WRAP_XY);
+ }
+
+ bool get_repeat_y()
+ {
+ return ELEM(get_node_translate().wrap_axis, CMP_NODE_WRAP_Y, CMP_NODE_WRAP_XY);
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new TranslateOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_translate_cc
void register_node_type_cmp_translate()
@@ -48,6 +116,7 @@ void register_node_type_cmp_translate()
node_type_init(&ntype, file_ns::node_composit_init_translate);
node_type_storage(
&ntype, "NodeTranslateData", node_free_standard_storage, node_copy_standard_storage);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_val_to_rgb.cc b/source/blender/nodes/composite/nodes/node_composite_val_to_rgb.cc
index df669d5beda..03a7bc61924 100644
--- a/source/blender/nodes/composite/nodes/node_composite_val_to_rgb.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_val_to_rgb.cc
@@ -5,18 +5,33 @@
* \ingroup cmpnodes
*/
+#include "BLI_assert.h"
+
+#include "IMB_colormanagement.h"
+
+#include "BKE_colorband.h"
+
+#include "GPU_material.h"
+
+#include "COM_shader_node.hh"
+
#include "node_composite_util.hh"
#include "BKE_colorband.h"
/* **************** VALTORGB ******************** */
-namespace blender::nodes::node_composite_val_to_rgb_cc {
+namespace blender::nodes::node_composite_color_ramp_cc {
static void cmp_node_valtorgb_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Float>(N_("Fac")).default_value(0.5f).min(0.0f).max(1.0f).subtype(PROP_FACTOR);
- b.add_output<decl::Color>(N_("Image"));
+ b.add_input<decl::Float>(N_("Fac"))
+ .default_value(0.5f)
+ .min(0.0f)
+ .max(1.0f)
+ .subtype(PROP_FACTOR)
+ .compositor_domain_priority(1);
+ b.add_output<decl::Color>(N_("Image")).compositor_domain_priority(0);
b.add_output<decl::Float>(N_("Alpha"));
}
@@ -25,11 +40,94 @@ static void node_composit_init_valtorgb(bNodeTree *UNUSED(ntree), bNode *node)
node->storage = BKE_colorband_add(true);
}
-} // namespace blender::nodes::node_composite_val_to_rgb_cc
+using namespace blender::realtime_compositor;
+
+class ColorRampShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ struct ColorBand *color_band = get_color_band();
+
+ /* Common / easy case optimization. */
+ if ((color_band->tot <= 2) && (color_band->color_mode == COLBAND_BLEND_RGB)) {
+ float mul_bias[2];
+ switch (color_band->ipotype) {
+ case COLBAND_INTERP_LINEAR:
+ mul_bias[0] = 1.0f / (color_band->data[1].pos - color_band->data[0].pos);
+ mul_bias[1] = -mul_bias[0] * color_band->data[0].pos;
+ GPU_stack_link(material,
+ &bnode(),
+ "valtorgb_opti_linear",
+ inputs,
+ outputs,
+ GPU_uniform(mul_bias),
+ GPU_uniform(&color_band->data[0].r),
+ GPU_uniform(&color_band->data[1].r));
+ return;
+ case COLBAND_INTERP_CONSTANT:
+ mul_bias[1] = max_ff(color_band->data[0].pos, color_band->data[1].pos);
+ GPU_stack_link(material,
+ &bnode(),
+ "valtorgb_opti_constant",
+ inputs,
+ outputs,
+ GPU_uniform(&mul_bias[1]),
+ GPU_uniform(&color_band->data[0].r),
+ GPU_uniform(&color_band->data[1].r));
+ return;
+ case COLBAND_INTERP_EASE:
+ mul_bias[0] = 1.0f / (color_band->data[1].pos - color_band->data[0].pos);
+ mul_bias[1] = -mul_bias[0] * color_band->data[0].pos;
+ GPU_stack_link(material,
+ &bnode(),
+ "valtorgb_opti_ease",
+ inputs,
+ outputs,
+ GPU_uniform(mul_bias),
+ GPU_uniform(&color_band->data[0].r),
+ GPU_uniform(&color_band->data[1].r));
+ return;
+ default:
+ BLI_assert_unreachable();
+ return;
+ }
+ }
+
+ float *array, layer;
+ int size;
+ BKE_colorband_evaluate_table_rgba(color_band, &array, &size);
+ GPUNodeLink *tex = GPU_color_band(material, size, array, &layer);
+
+ if (color_band->ipotype == COLBAND_INTERP_CONSTANT) {
+ GPU_stack_link(
+ material, &bnode(), "valtorgb_nearest", inputs, outputs, tex, GPU_constant(&layer));
+ return;
+ }
+
+ GPU_stack_link(material, &bnode(), "valtorgb", inputs, outputs, tex, GPU_constant(&layer));
+ }
+
+ struct ColorBand *get_color_band()
+ {
+ return static_cast<struct ColorBand *>(bnode().storage);
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new ColorRampShaderNode(node);
+}
+
+} // namespace blender::nodes::node_composite_color_ramp_cc
void register_node_type_cmp_valtorgb()
{
- namespace file_ns = blender::nodes::node_composite_val_to_rgb_cc;
+ namespace file_ns = blender::nodes::node_composite_color_ramp_cc;
static bNodeType ntype;
@@ -38,31 +136,63 @@ void register_node_type_cmp_valtorgb()
node_type_size(&ntype, 240, 200, 320);
node_type_init(&ntype, file_ns::node_composit_init_valtorgb);
node_type_storage(&ntype, "ColorBand", node_free_standard_storage, node_copy_standard_storage);
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
/* **************** RGBTOBW ******************** */
-namespace blender::nodes::node_composite_val_to_rgb_cc {
+namespace blender::nodes::node_composite_rgb_to_bw_cc {
static void cmp_node_rgbtobw_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Color>(N_("Image")).default_value({0.8f, 0.8f, 0.8f, 1.0f});
+ b.add_input<decl::Color>(N_("Image"))
+ .default_value({0.8f, 0.8f, 0.8f, 1.0f})
+ .compositor_domain_priority(0);
b.add_output<decl::Float>(N_("Val"));
}
-} // namespace blender::nodes::node_composite_val_to_rgb_cc
+using namespace blender::realtime_compositor;
+
+class RGBToBWShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ float luminance_coefficients[3];
+ IMB_colormanagement_get_luminance_coefficients(luminance_coefficients);
+
+ GPU_stack_link(material,
+ &bnode(),
+ "color_to_luminance",
+ inputs,
+ outputs,
+ GPU_constant(luminance_coefficients));
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new RGBToBWShaderNode(node);
+}
+
+} // namespace blender::nodes::node_composite_rgb_to_bw_cc
void register_node_type_cmp_rgbtobw()
{
- namespace file_ns = blender::nodes::node_composite_val_to_rgb_cc;
+ namespace file_ns = blender::nodes::node_composite_rgb_to_bw_cc;
static bNodeType ntype;
cmp_node_type_base(&ntype, CMP_NODE_RGBTOBW, "RGB to BW", NODE_CLASS_CONVERTER);
ntype.declare = file_ns::cmp_node_rgbtobw_declare;
node_type_size_preset(&ntype, NODE_SIZE_SMALL);
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_value.cc b/source/blender/nodes/composite/nodes/node_composite_value.cc
index a3269d3d1c2..a96e1db14ad 100644
--- a/source/blender/nodes/composite/nodes/node_composite_value.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_value.cc
@@ -5,6 +5,8 @@
* \ingroup cmpnodes
*/
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
/* **************** VALUE ******************** */
@@ -16,6 +18,29 @@ static void cmp_node_value_declare(NodeDeclarationBuilder &b)
b.add_output<decl::Float>(N_("Value")).default_value(0.5f);
}
+using namespace blender::realtime_compositor;
+
+class ValueOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ Result &result = get_result("Value");
+ result.allocate_single_value();
+
+ const bNodeSocket *socket = static_cast<bNodeSocket *>(bnode().outputs.first);
+ float value = static_cast<bNodeSocketValueFloat *>(socket->default_value)->value;
+
+ result.set_float_value(value);
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new ValueOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_value_cc
void register_node_type_cmp_value()
@@ -27,6 +52,7 @@ void register_node_type_cmp_value()
cmp_node_type_base(&ntype, CMP_NODE_VALUE, "Value", NODE_CLASS_INPUT);
ntype.declare = file_ns::cmp_node_value_declare;
node_type_size_preset(&ntype, NODE_SIZE_SMALL);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_vec_blur.cc b/source/blender/nodes/composite/nodes/node_composite_vec_blur.cc
index 741f2e0e816..515478da75d 100644
--- a/source/blender/nodes/composite/nodes/node_composite_vec_blur.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_vec_blur.cc
@@ -8,6 +8,8 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
/* **************** VECTOR BLUR ******************** */
@@ -51,6 +53,23 @@ static void node_composit_buts_vecblur(uiLayout *layout, bContext *UNUSED(C), Po
uiItemR(layout, ptr, "use_curved", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class VectorBlurOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ get_input("Image").pass_through(get_result("Image"));
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new VectorBlurOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_vec_blur_cc
void register_node_type_cmp_vecblur()
@@ -65,6 +84,7 @@ void register_node_type_cmp_vecblur()
node_type_init(&ntype, file_ns::node_composit_init_vecblur);
node_type_storage(
&ntype, "NodeBlurData", node_free_standard_storage, node_copy_standard_storage);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_viewer.cc b/source/blender/nodes/composite/nodes/node_composite_viewer.cc
index 05f395183b5..4e82b31ca47 100644
--- a/source/blender/nodes/composite/nodes/node_composite_viewer.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_viewer.cc
@@ -5,6 +5,8 @@
* \ingroup cmpnodes
*/
+#include "BLI_math_vec_types.hh"
+
#include "BKE_global.h"
#include "BKE_image.h"
@@ -13,6 +15,13 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "GPU_shader.h"
+#include "GPU_state.h"
+#include "GPU_texture.h"
+
+#include "COM_node_operation.hh"
+#include "COM_utilities.hh"
+
#include "node_composite_util.hh"
/* **************** VIEWER ******************** */
@@ -55,6 +64,125 @@ static void node_composit_buts_viewer_ex(uiLayout *layout, bContext *UNUSED(C),
}
}
+using namespace blender::realtime_compositor;
+
+class ViewerOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ const Result &image = get_input("Image");
+ const Result &alpha = get_input("Alpha");
+
+ if (image.is_single_value() && alpha.is_single_value()) {
+ execute_clear();
+ }
+ else if (ignore_alpha()) {
+ execute_ignore_alpha();
+ }
+ else if (!node().input_by_identifier("Alpha")->is_logically_linked()) {
+ execute_copy();
+ }
+ else {
+ execute_set_alpha();
+ }
+ }
+
+ /* Executes when all inputs are single values, in which case, the output texture can just be
+ * cleared to the appropriate color. */
+ void execute_clear()
+ {
+ const Result &image = get_input("Image");
+ const Result &alpha = get_input("Alpha");
+
+ float4 color = image.get_color_value();
+ if (ignore_alpha()) {
+ color.w = 1.0f;
+ }
+ else if (node().input_by_identifier("Alpha")->is_logically_linked()) {
+ color.w = alpha.get_float_value();
+ }
+
+ GPU_texture_clear(context().get_output_texture(), GPU_DATA_FLOAT, color);
+ }
+
+ /* Executes when the alpha channel of the image is ignored. */
+ void execute_ignore_alpha()
+ {
+ GPUShader *shader = shader_manager().get("compositor_convert_color_to_opaque");
+ GPU_shader_bind(shader);
+
+ const Result &image = get_input("Image");
+ image.bind_as_texture(shader, "input_tx");
+
+ GPUTexture *output_texture = context().get_output_texture();
+ const int image_unit = GPU_shader_get_texture_binding(shader, "output_img");
+ GPU_texture_image_bind(output_texture, image_unit);
+
+ compute_dispatch_threads_at_least(shader, compute_domain().size);
+
+ image.unbind_as_texture();
+ GPU_texture_image_unbind(output_texture);
+ GPU_shader_unbind();
+ }
+
+ /* Executes when the image texture is written with no adjustments and can thus be copied directly
+ * to the output texture. */
+ void execute_copy()
+ {
+ const Result &image = get_input("Image");
+
+ /* Make sure any prior writes to the texture are reflected before copying it. */
+ GPU_memory_barrier(GPU_BARRIER_TEXTURE_UPDATE);
+
+ GPU_texture_copy(context().get_output_texture(), image.texture());
+ }
+
+ /* Executes when the alpha channel of the image is set as the value of the input alpha. */
+ void execute_set_alpha()
+ {
+ GPUShader *shader = shader_manager().get("compositor_set_alpha");
+ GPU_shader_bind(shader);
+
+ const Result &image = get_input("Image");
+ image.bind_as_texture(shader, "image_tx");
+
+ const Result &alpha = get_input("Alpha");
+ alpha.bind_as_texture(shader, "alpha_tx");
+
+ GPUTexture *output_texture = context().get_output_texture();
+ const int image_unit = GPU_shader_get_texture_binding(shader, "output_img");
+ GPU_texture_image_bind(output_texture, image_unit);
+
+ compute_dispatch_threads_at_least(shader, compute_domain().size);
+
+ image.unbind_as_texture();
+ alpha.unbind_as_texture();
+ GPU_texture_image_unbind(output_texture);
+ GPU_shader_unbind();
+ }
+
+ /* If true, the alpha channel of the image is set to 1, that is, it becomes opaque. If false, the
+ * alpha channel of the image is retained, but only if the alpha input is not linked. If the
+ * alpha input is linked, it the value of that input will be used as the alpha of the image. */
+ bool ignore_alpha()
+ {
+ return bnode().custom2 & CMP_NODE_OUTPUT_IGNORE_ALPHA;
+ }
+
+ /* The operation domain have the same dimensions of the output without any transformations. */
+ Domain compute_domain() override
+ {
+ return Domain(context().get_output_size());
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new ViewerOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_viewer_cc
void register_node_type_cmp_viewer()
@@ -70,6 +198,7 @@ void register_node_type_cmp_viewer()
ntype.flag |= NODE_PREVIEW;
node_type_init(&ntype, file_ns::node_composit_init_viewer);
node_type_storage(&ntype, "ImageUser", node_free_standard_storage, node_copy_standard_storage);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
ntype.no_muting = true;
diff --git a/source/blender/nodes/composite/nodes/node_composite_zcombine.cc b/source/blender/nodes/composite/nodes/node_composite_zcombine.cc
index be90aeb7acc..e5f460099e9 100644
--- a/source/blender/nodes/composite/nodes/node_composite_zcombine.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_zcombine.cc
@@ -8,6 +8,8 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
/* **************** Z COMBINE ******************** */
@@ -33,6 +35,24 @@ static void node_composit_buts_zcombine(uiLayout *layout, bContext *UNUSED(C), P
uiItemR(col, ptr, "use_antialias_z", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class ZCombineOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ get_input("Image").pass_through(get_result("Image"));
+ get_result("Z").allocate_invalid();
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new ZCombineOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_zcombine_cc
void register_node_type_cmp_zcombine()
@@ -44,6 +64,7 @@ void register_node_type_cmp_zcombine()
cmp_node_type_base(&ntype, CMP_NODE_ZCOMBINE, "Z Combine", NODE_CLASS_OP_COLOR);
ntype.declare = file_ns::cmp_node_zcombine_declare;
ntype.draw_buttons = file_ns::node_composit_buts_zcombine;
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/geometry/CMakeLists.txt b/source/blender/nodes/geometry/CMakeLists.txt
index ddd8c8949b1..31c00cc6b82 100644
--- a/source/blender/nodes/geometry/CMakeLists.txt
+++ b/source/blender/nodes/geometry/CMakeLists.txt
@@ -76,8 +76,8 @@ set(SRC
nodes/node_geo_input_index.cc
nodes/node_geo_input_instance_rotation.cc
nodes/node_geo_input_instance_scale.cc
- nodes/node_geo_input_material_index.cc
nodes/node_geo_input_material.cc
+ nodes/node_geo_input_material_index.cc
nodes/node_geo_input_mesh_edge_angle.cc
nodes/node_geo_input_mesh_edge_neighbors.cc
nodes/node_geo_input_mesh_edge_vertices.cc
@@ -118,9 +118,9 @@ set(SRC
nodes/node_geo_mesh_to_points.cc
nodes/node_geo_mesh_to_volume.cc
nodes/node_geo_object_info.cc
+ nodes/node_geo_points.cc
nodes/node_geo_points_to_vertices.cc
nodes/node_geo_points_to_volume.cc
- nodes/node_geo_points.cc
nodes/node_geo_proximity.cc
nodes/node_geo_raycast.cc
nodes/node_geo_realize_instances.cc
@@ -134,8 +134,8 @@ set(SRC
nodes/node_geo_set_curve_radius.cc
nodes/node_geo_set_curve_tilt.cc
nodes/node_geo_set_id.cc
- nodes/node_geo_set_material_index.cc
nodes/node_geo_set_material.cc
+ nodes/node_geo_set_material_index.cc
nodes/node_geo_set_point_radius.cc
nodes/node_geo_set_position.cc
nodes/node_geo_set_shade_smooth.cc
diff --git a/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc b/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc
index cf29c752257..a6c67cac916 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc
@@ -221,7 +221,7 @@ BLI_NOINLINE static void update_elimination_mask_based_on_density_factors(
const float v2_density_factor = std::max(0.0f, density_factors[v2_loop]);
const float probability = v0_density_factor * bary_coord.x + v1_density_factor * bary_coord.y +
- v2_density_factor * bary_coord.z;
+ v2_density_factor * bary_coord.z;
const float hash = noise::hash_float_to_float(bary_coord);
if (hash > probability) {
diff --git a/source/blender/nodes/shader/nodes/node_shader_mix_rgb.cc b/source/blender/nodes/shader/nodes/node_shader_mix_rgb.cc
index 12707623049..478a6812c36 100644
--- a/source/blender/nodes/shader/nodes/node_shader_mix_rgb.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_mix_rgb.cc
@@ -32,7 +32,7 @@ static const char *gpu_shader_get_name(int mode)
case MA_RAMP_SCREEN:
return "mix_screen";
case MA_RAMP_DIV:
- return "mix_div";
+ return "mix_div_fallback";
case MA_RAMP_DIFF:
return "mix_diff";
case MA_RAMP_DARK:
@@ -70,18 +70,23 @@ static int gpu_shader_mix_rgb(GPUMaterial *mat,
{
const char *name = gpu_shader_get_name(node->custom1);
- if (name != nullptr) {
- int ret = GPU_stack_link(mat, node, name, in, out);
- if (ret && node->custom2 & SHD_MIXRGB_CLAMP) {
- const float min[3] = {0.0f, 0.0f, 0.0f};
- const float max[3] = {1.0f, 1.0f, 1.0f};
- GPU_link(
- mat, "clamp_color", out[0].link, GPU_constant(min), GPU_constant(max), &out[0].link);
- }
- return ret;
+ if (name == nullptr) {
+ return 0;
}
- return 0;
+ const float min = 0.0f;
+ const float max = 1.0f;
+ const GPUNodeLink *factor_link = in[0].link ? in[0].link : GPU_uniform(in[0].vec);
+ GPU_link(mat, "clamp_value", factor_link, GPU_constant(&min), GPU_constant(&max), &in[0].link);
+
+ int ret = GPU_stack_link(mat, node, name, in, out);
+
+ if (ret && node->custom2 & SHD_MIXRGB_CLAMP) {
+ const float min[3] = {0.0f, 0.0f, 0.0f};
+ const float max[3] = {1.0f, 1.0f, 1.0f};
+ GPU_link(mat, "clamp_color", out[0].link, GPU_constant(min), GPU_constant(max), &out[0].link);
+ }
+ return ret;
}
class MixRGBFunction : public fn::MultiFunction {
diff --git a/source/blender/nodes/shader/nodes/node_shader_rgb.cc b/source/blender/nodes/shader/nodes/node_shader_rgb.cc
index c854bc733a3..3d28f5278a2 100644
--- a/source/blender/nodes/shader/nodes/node_shader_rgb.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_rgb.cc
@@ -17,7 +17,7 @@ static void node_declare(NodeDeclarationBuilder &b)
static int gpu_shader_rgb(GPUMaterial *mat,
bNode *node,
bNodeExecData *UNUSED(execdata),
- GPUNodeStack *in,
+ GPUNodeStack * /*in*/,
GPUNodeStack *out)
{
const bNodeSocket *socket = static_cast<bNodeSocket *>(node->outputs.first);
diff --git a/source/blender/nodes/shader/nodes/node_shader_value.cc b/source/blender/nodes/shader/nodes/node_shader_value.cc
index b6b7fe10cf9..14dbb3b25eb 100644
--- a/source/blender/nodes/shader/nodes/node_shader_value.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_value.cc
@@ -17,7 +17,7 @@ static void sh_node_value_declare(NodeDeclarationBuilder &b)
static int gpu_shader_value(GPUMaterial *mat,
bNode *node,
bNodeExecData *UNUSED(execdata),
- GPUNodeStack *in,
+ GPUNodeStack * /*in*/,
GPUNodeStack *out)
{
const bNodeSocket *socket = static_cast<bNodeSocket *>(node->outputs.first);
diff --git a/source/blender/python/intern/CMakeLists.txt b/source/blender/python/intern/CMakeLists.txt
index 71138134370..9d2516969cf 100644
--- a/source/blender/python/intern/CMakeLists.txt
+++ b/source/blender/python/intern/CMakeLists.txt
@@ -174,8 +174,8 @@ if(WITH_CODEC_SNDFILE)
add_definitions(-DWITH_SNDFILE)
endif()
-if(WITH_COMPOSITOR)
- add_definitions(-DWITH_COMPOSITOR)
+if(WITH_COMPOSITOR_CPU)
+ add_definitions(-DWITH_COMPOSITOR_CPU)
endif()
if(WITH_CYCLES)
diff --git a/source/blender/python/intern/bpy_app_build_options.c b/source/blender/python/intern/bpy_app_build_options.c
index fe5111c37f2..a744f3fd4fa 100644
--- a/source/blender/python/intern/bpy_app_build_options.c
+++ b/source/blender/python/intern/bpy_app_build_options.c
@@ -18,7 +18,7 @@ static PyStructSequence_Field app_builtopts_info_fields[] = {
{"codec_avi", NULL},
{"codec_ffmpeg", NULL},
{"codec_sndfile", NULL},
- {"compositor", NULL},
+ {"compositor_cpu", NULL},
{"cycles", NULL},
{"cycles_osl", NULL},
{"freestyle", NULL},
@@ -104,7 +104,7 @@ static PyObject *make_builtopts_info(void)
SetObjIncref(Py_False);
#endif
-#ifdef WITH_COMPOSITOR
+#ifdef WITH_COMPOSITOR_CPU
SetObjIncref(Py_True);
#else
SetObjIncref(Py_False);
diff --git a/source/blender/python/intern/bpy_interface_atexit.c b/source/blender/python/intern/bpy_interface_atexit.c
index 1ba3ab6a40b..cba9bd59abf 100644
--- a/source/blender/python/intern/bpy_interface_atexit.c
+++ b/source/blender/python/intern/bpy_interface_atexit.c
@@ -32,7 +32,7 @@ static PyObject *func_bpy_atregister = NULL; /* borrowed reference, `atexit` hol
static void atexit_func_call(const char *func_name, PyObject *atexit_func_arg)
{
- /* NOTE(campbell): no error checking, if any of these fail we'll get a crash
+ /* NOTE(@campbellbarton): no error checking, if any of these fail we'll get a crash
* this is intended, but if its problematic it could be changed. */
PyObject *atexit_mod = PyImport_ImportModuleLevel("atexit", NULL, NULL, NULL, 0);
diff --git a/source/blender/python/intern/bpy_rna.c b/source/blender/python/intern/bpy_rna.c
index d9c004fb6fa..179a0250688 100644
--- a/source/blender/python/intern/bpy_rna.c
+++ b/source/blender/python/intern/bpy_rna.c
@@ -780,7 +780,7 @@ PyObject *pyrna_math_object_from_array(PointerRNA *ptr, PropertyRNA *prop)
return ret;
}
-/* NOTE(campbell): Regarding comparison `__cmp__`:
+/* NOTE(@campbellbarton): Regarding comparison `__cmp__`:
* checking the 'ptr->data' matches works in almost all cases,
* however there are a few RNA properties that are fake sub-structs and
* share the pointer with the parent, in those cases this happens 'a.b == a'
@@ -8254,7 +8254,7 @@ static int bpy_class_validate_recursive(PointerRNA *dummyptr,
continue;
}
- /* TODO(campbell): this is used for classmethod's too,
+ /* TODO(@campbellbarton): this is used for classmethod's too,
* even though class methods should have 'FUNC_USE_SELF_TYPE' set, see Operator.poll for eg.
* Keep this as-is since it's working, but we should be using
* 'FUNC_USE_SELF_TYPE' for many functions. */
@@ -8345,7 +8345,7 @@ static int bpy_class_validate_recursive(PointerRNA *dummyptr,
continue;
}
- /* TODO(campbell): Use Python3.7x _PyObject_LookupAttr(), also in the macro below. */
+ /* TODO(@campbellbarton): Use Python3.7x _PyObject_LookupAttr(), also in the macro below. */
identifier = RNA_property_identifier(prop);
item = PyObject_GetAttrString(py_class, identifier);
diff --git a/source/blender/render/intern/bake.c b/source/blender/render/intern/bake.c
index 9ffe2879779..54497a6572f 100644
--- a/source/blender/render/intern/bake.c
+++ b/source/blender/render/intern/bake.c
@@ -760,8 +760,8 @@ void RE_bake_pixels_populate(Mesh *me,
for (int a = 0; a < 3; a++) {
const float *uv = mloopuv[lt->tri[a]].uv;
- /* NOTE(campbell): workaround for pixel aligned UVs which are common and can screw up our
- * intersection tests where a pixel gets in between 2 faces or the middle of a quad,
+ /* NOTE(@campbellbarton): workaround for pixel aligned UVs which are common and can screw
+ * up our intersection tests where a pixel gets in between 2 faces or the middle of a quad,
* camera aligned quads also have this problem but they are less common.
* Add a small offset to the UVs, fixes bug T18685. */
vec[a][0] = (uv[0] - bk_image->uv_offset[0]) * (float)bk_image->width - (0.5f + 0.001f);
diff --git a/source/blender/render/intern/texture_margin.cc b/source/blender/render/intern/texture_margin.cc
index 37ef9213615..92146155437 100644
--- a/source/blender/render/intern/texture_margin.cc
+++ b/source/blender/render/intern/texture_margin.cc
@@ -558,8 +558,8 @@ static void generate_margin(ImBuf *ibuf,
for (int a = 0; a < 3; a++) {
const float *uv = mloopuv[lt->tri[a]].uv;
- /* NOTE(campbell): workaround for pixel aligned UVs which are common and can screw up our
- * intersection tests where a pixel gets in between 2 faces or the middle of a quad,
+ /* NOTE(@campbellbarton): workaround for pixel aligned UVs which are common and can screw up
+ * our intersection tests where a pixel gets in between 2 faces or the middle of a quad,
* camera aligned quads also have this problem but they are less common.
* Add a small offset to the UVs, fixes bug T18685. */
vec[a][0] = (uv[0] - uv_offset[0]) * (float)ibuf->x - (0.5f + 0.001f);
diff --git a/source/blender/windowmanager/CMakeLists.txt b/source/blender/windowmanager/CMakeLists.txt
index 6cbd21d0abe..c434638549c 100644
--- a/source/blender/windowmanager/CMakeLists.txt
+++ b/source/blender/windowmanager/CMakeLists.txt
@@ -165,11 +165,11 @@ if(WIN32 OR APPLE)
endif()
endif()
-if(WITH_COMPOSITOR)
+if(WITH_COMPOSITOR_CPU)
list(APPEND LIB
bf_compositor
)
- add_definitions(-DWITH_COMPOSITOR)
+ add_definitions(-DWITH_COMPOSITOR_CPU)
endif()
if(WITH_XR_OPENXR)
diff --git a/source/blender/windowmanager/gizmo/intern/wm_gizmo_group.c b/source/blender/windowmanager/gizmo/intern/wm_gizmo_group.c
index 4ecadbc5685..e165cb6b4f8 100644
--- a/source/blender/windowmanager/gizmo/intern/wm_gizmo_group.c
+++ b/source/blender/windowmanager/gizmo/intern/wm_gizmo_group.c
@@ -1053,7 +1053,7 @@ void WM_gizmomaptype_group_unlink(bContext *C,
WM_gizmomaptype_group_free(gzgt_ref);
}
- /* TODO(campbell): Gizmos may share key-maps, for now don't
+ /* TODO(@campbellbarton): Gizmos may share key-maps, for now don't
* remove however we could flag them as temporary/owned by the gizmo. */
#if 0
/* NOTE: we may want to keep this key-map for editing. */
diff --git a/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c b/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c
index f5974a2176b..9903b0e50fd 100644
--- a/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c
+++ b/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c
@@ -259,7 +259,7 @@ bool WM_gizmomap_minmax(const wmGizmoMap *gzmap,
* \param poll: Polling function for excluding gizmos.
* \param data: Custom data passed to \a poll
*
- * TODO(campbell): this uses unreliable order,
+ * TODO(@campbellbarton): this uses unreliable order,
* best we use an iterator function instead of a hash.
*/
static GHash *WM_gizmomap_gizmo_hash_new(const bContext *C,
@@ -430,9 +430,9 @@ static void gizmos_draw_list(const wmGizmoMap *gzmap, const bContext *C, ListBas
return;
}
- /* TODO(campbell): This will need it own shader probably?
+ /* TODO(@campbellbarton): This will need it own shader probably?
* Don't think it can be handled from that point though. */
- /* const bool use_lighting = (U.gizmo_flag & V3D_GIZMO_SHADED) != 0; */
+ // const bool use_lighting = (U.gizmo_flag & V3D_GIZMO_SHADED) != 0;
bool is_depth_prev = false;
@@ -501,7 +501,7 @@ static void gizmo_draw_select_3d_loop(const bContext *C,
bool *r_use_select_bias)
{
- /* TODO(campbell): this depends on depth buffer being written to,
+ /* TODO(@campbellbarton): this depends on depth buffer being written to,
* currently broken for the 3D view. */
bool is_depth_prev = false;
bool is_depth_skip_prev = false;
@@ -674,7 +674,7 @@ static wmGizmo *gizmo_find_intersected_3d(bContext *C,
* This way we always use the first hit. */
if (has_3d) {
- /* The depth buffer is needed for for gizmos to obscure each other. */
+ /* The depth buffer is needed for gizmos to obscure each other. */
GPUViewport *viewport = WM_draw_region_get_viewport(CTX_wm_region(C));
/* When switching between modes and the mouse pointer is over a gizmo, the highlight test is
diff --git a/source/blender/windowmanager/intern/wm_event_system.cc b/source/blender/windowmanager/intern/wm_event_system.cc
index d90259c0cde..77f6b3c861f 100644
--- a/source/blender/windowmanager/intern/wm_event_system.cc
+++ b/source/blender/windowmanager/intern/wm_event_system.cc
@@ -5214,6 +5214,13 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, void
event.prev_type = event.type;
event.prev_val = event.val;
+ /* Always use modifiers from the active window since
+ changes to modifiers aren't sent to inactive windows, see: T66088. */
+ if ((wm->winactive != win) && (wm->winactive && wm->winactive->eventstate)) {
+ event.modifier = wm->winactive->eventstate->modifier;
+ event.keymodifier = wm->winactive->eventstate->keymodifier;
+ }
+
/* Ensure the event state is correct, any deviation from this may cause bugs.
*
* NOTE: #EVENT_NONE is set when unknown keys are pressed,
@@ -5256,6 +5263,10 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, void
if (win_other) {
wmEvent event_other = *win_other->eventstate;
+ /* Use the modifier state of this window. */
+ event_other.modifier = event.modifier;
+ event_other.keymodifier = event.keymodifier;
+
/* See comment for this operation on `event` for details. */
event_other.prev_type = event_other.type;
event_other.prev_val = event_other.val;
@@ -5345,6 +5356,10 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, void
if (win_other) {
wmEvent event_other = *win_other->eventstate;
+ /* Use the modifier state of this window. */
+ event_other.modifier = event.modifier;
+ event_other.keymodifier = event.keymodifier;
+
/* See comment for this operation on `event` for details. */
event_other.prev_type = event_other.type;
event_other.prev_val = event_other.val;
diff --git a/source/blender/windowmanager/intern/wm_files.c b/source/blender/windowmanager/intern/wm_files.c
index 45e8f8786df..1819ed13be3 100644
--- a/source/blender/windowmanager/intern/wm_files.c
+++ b/source/blender/windowmanager/intern/wm_files.c
@@ -1887,9 +1887,6 @@ static void wm_autosave_location(char *filepath)
{
const int pid = abs(getpid());
char path[1024];
-#ifdef WIN32
- const char *savedir;
-#endif
/* Normally there is no need to check for this to be NULL,
* however this runs on exit when it may be cleared. */
@@ -1915,7 +1912,7 @@ static void wm_autosave_location(char *filepath)
* through BLI_windows_get_default_root_dir().
* If there is no C:\tmp autosave fails. */
if (!BLI_exists(BKE_tempdir_base())) {
- savedir = BKE_appdir_folder_id_create(BLENDER_USER_AUTOSAVE, NULL);
+ const char *savedir = BKE_appdir_folder_id_create(BLENDER_USER_AUTOSAVE, NULL);
BLI_make_file_string("/", filepath, savedir, path);
return;
}
diff --git a/source/blender/windowmanager/intern/wm_init_exit.c b/source/blender/windowmanager/intern/wm_init_exit.c
index 7f5ec77e16d..624e434e784 100644
--- a/source/blender/windowmanager/intern/wm_init_exit.c
+++ b/source/blender/windowmanager/intern/wm_init_exit.c
@@ -540,7 +540,7 @@ void WM_exit_ex(bContext *C, const bool do_python)
BKE_vfont_clipboard_free();
BKE_node_clipboard_free();
-#ifdef WITH_COMPOSITOR
+#ifdef WITH_COMPOSITOR_CPU
COM_deinitialize();
#endif
diff --git a/source/blender/windowmanager/intern/wm_playanim.c b/source/blender/windowmanager/intern/wm_playanim.c
index 99f117f267a..790019b68b8 100644
--- a/source/blender/windowmanager/intern/wm_playanim.c
+++ b/source/blender/windowmanager/intern/wm_playanim.c
@@ -1807,21 +1807,22 @@ static char *wm_main_playanim_intern(int argc, const char **argv)
AUD_Sound_free(source);
source = NULL;
#endif
+
/* we still miss freeing a lot!,
* but many areas could skip initialization too for anim play */
- GPU_shader_free_builtin_shaders();
+ IMB_exit();
+ DEG_free_node_types();
+
+ BLF_exit();
if (g_WS.gpu_context) {
GPU_context_active_set(g_WS.gpu_context);
+ GPU_exit();
GPU_context_discard(g_WS.gpu_context);
g_WS.gpu_context = NULL;
}
- BLF_exit();
-
- GPU_exit();
-
GHOST_DisposeWindow(g_WS.ghost_system, g_WS.ghost_window);
/* early exit, IMB and BKE should be exited only in end */
@@ -1830,9 +1831,6 @@ static char *wm_main_playanim_intern(int argc, const char **argv)
return filepath;
}
- IMB_exit();
- DEG_free_node_types();
-
totblock = MEM_get_memory_blocks_in_use();
if (totblock != 0) {
/* prints many bAKey, bArgument's which are tricky to fix */
diff --git a/source/blender/windowmanager/intern/wm_window.c b/source/blender/windowmanager/intern/wm_window.c
index a5690b52a5a..0c31ff87fdd 100644
--- a/source/blender/windowmanager/intern/wm_window.c
+++ b/source/blender/windowmanager/intern/wm_window.c
@@ -1113,14 +1113,22 @@ static bool ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr C_void_pt
}
wmWindow *win = GHOST_GetWindowUserData(ghostwin);
+ /* Win23/GHOST modifier bug, see T40317 */
+#ifndef WIN32
+//# define USE_WIN_ACTIVATE
+#endif
+
switch (type) {
case GHOST_kEventWindowDeactivate:
wm_event_add_ghostevent(wm, win, type, data);
win->active = 0; /* XXX */
- /* clear modifiers for inactive windows */
+ /* When window activation is enabled, these modifiers are set with window activation.
+ * Otherwise leave them set so re-activation doesn't loose keys which are held. */
+#ifdef USE_WIN_ACTIVATE
win->eventstate->modifier = 0;
win->eventstate->keymodifier = 0;
+#endif
break;
case GHOST_kEventWindowActivate: {
@@ -1128,11 +1136,6 @@ static bool ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr C_void_pt
(query_qual(CONTROL) ? KM_CTRL : 0) |
(query_qual(ALT) ? KM_ALT : 0) | (query_qual(OS) ? KM_OSKEY : 0));
- /* Win23/GHOST modifier bug, see T40317 */
-#ifndef WIN32
-//# define USE_WIN_ACTIVATE
-#endif
-
/* No context change! C->wm->windrawable is drawable, or for area queues. */
wm->winactive = win;