From bc2121147f32f5ffde24bb35847f64f813e948ac Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Fri, 8 Jul 2022 18:47:31 -0500 Subject: Cleanup: Remove unused variable --- source/blender/editors/curves/intern/curves_ops.cc | 3 --- 1 file changed, 3 deletions(-) diff --git a/source/blender/editors/curves/intern/curves_ops.cc b/source/blender/editors/curves/intern/curves_ops.cc index a4492a1d516..f4d1c8046f1 100644 --- a/source/blender/editors/curves/intern/curves_ops.cc +++ b/source/blender/editors/curves/intern/curves_ops.cc @@ -528,9 +528,6 @@ static void snap_curves_to_surface_exec_object(Object &curves_ob, Mesh &surface_mesh = *static_cast(surface_ob.data); - MeshComponent surface_mesh_component; - surface_mesh_component.replace(&surface_mesh, GeometryOwnershipType::ReadOnly); - VArraySpan surface_uv_map; if (curves_id.surface_uv_map != nullptr) { const bke::AttributeAccessor surface_attributes = bke::mesh_attributes(surface_mesh); -- cgit v1.2.3 From d9e00fbbf613dda92f3e635e0c13094749447cae Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Sat, 9 Jul 2022 15:08:02 +1000 Subject: Cleanup: quiet class-memaccess warning --- source/blender/blenkernel/intern/brush.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/source/blender/blenkernel/intern/brush.cc b/source/blender/blenkernel/intern/brush.cc index ffa63f9792f..1c2408bab8a 100644 --- a/source/blender/blenkernel/intern/brush.cc +++ b/source/blender/blenkernel/intern/brush.cc @@ -444,7 +444,8 @@ static void brush_defaults(Brush *brush) const Brush *brush_def = DNA_struct_default_get(Brush); -#define FROM_DEFAULT(member) memcpy(&brush->member, &brush_def->member, sizeof(brush->member)) +#define FROM_DEFAULT(member) \ + memcpy((void *)&brush->member, (void *)&brush_def->member, sizeof(brush->member)) #define FROM_DEFAULT_PTR(member) memcpy(brush->member, brush_def->member, sizeof(brush->member)) FROM_DEFAULT(blend); -- cgit v1.2.3 From e3801a2bd417b63f8b30c4c2dfa19b2efdbf9ce7 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Sat, 19 Feb 2022 17:04:32 +0300 Subject: Weight & Vertex Paint: always respect edit mode hiding on faces. In some cases it is mandatory to be able to hide parts of the mesh in order to paint certain areas. The Mask modifier doesn't work in weight paint, and edit mode hiding requires using selection, which is not always convenient. This makes the weight and vertex paint modes always respect edit mode hiding like sculpt mode. The change in behavior affects drawing and building paint PBVH. Thus it affects brushes, but not menu operators like Smooth or Normalize. In addition, this makes the Alt-H shortcut available even without any selection enabled, and implements Hide for vertex selection. Differential Revision: https://developer.blender.org/D14163 --- .../keyconfig/keymap_data/blender_default.py | 5 +- .../keymap_data/industry_compatible_data.py | 7 ++- source/blender/blenkernel/BKE_paint.h | 5 ++ source/blender/blenkernel/intern/paint.c | 13 ++--- source/blender/draw/intern/draw_manager.c | 2 +- source/blender/editors/include/ED_mesh.h | 3 ++ source/blender/editors/mesh/editface.cc | 51 ++++++++++++++++++ source/blender/editors/sculpt_paint/paint_intern.h | 4 +- source/blender/editors/sculpt_paint/paint_ops.c | 4 +- source/blender/editors/sculpt_paint/paint_utils.c | 61 ++++++++++++++++++---- 10 files changed, 135 insertions(+), 20 deletions(-) diff --git a/release/scripts/presets/keyconfig/keymap_data/blender_default.py b/release/scripts/presets/keyconfig/keymap_data/blender_default.py index c0db6c5f523..9c7aa67ddad 100644 --- a/release/scripts/presets/keyconfig/keymap_data/blender_default.py +++ b/release/scripts/presets/keyconfig/keymap_data/blender_default.py @@ -4397,7 +4397,7 @@ def km_face_mask(params): items.extend([ *_template_items_select_actions(params, "paint.face_select_all"), - *_template_items_hide_reveal_actions("paint.face_select_hide", "paint.face_select_reveal"), + *_template_items_hide_reveal_actions("paint.face_select_hide", "paint.face_vert_reveal"), ("paint.face_select_linked", {"type": 'L', "value": 'PRESS', "ctrl": True}, None), ("paint.face_select_linked_pick", {"type": 'L', "value": 'PRESS'}, {"properties": [("deselect", False)]}), @@ -4418,6 +4418,7 @@ def km_weight_paint_vertex_selection(params): items.extend([ *_template_items_select_actions(params, "paint.vert_select_all"), + *_template_items_hide_reveal_actions("paint.vert_select_hide", "paint.face_vert_reveal"), ("view3d.select_box", {"type": 'B', "value": 'PRESS'}, None), ("view3d.select_lasso", {"type": params.action_mouse, "value": 'CLICK_DRAG', "ctrl": True}, {"properties": [("mode", 'ADD')]}), @@ -4968,6 +4969,7 @@ def km_vertex_paint(params): op_menu("VIEW3D_MT_angle_control", {"type": 'R', "value": 'PRESS'}), ("wm.context_menu_enum", {"type": 'E', "value": 'PRESS'}, {"properties": [("data_path", 'tool_settings.vertex_paint.brush.stroke_method')]}), + ("paint.face_vert_reveal", {"type": 'H', "value": 'PRESS', "alt": True}, None), *_template_items_context_panel("VIEW3D_PT_paint_vertex_context_menu", params.context_menu_event), ]) @@ -5015,6 +5017,7 @@ def km_weight_paint(params): ("wm.context_toggle", {"type": 'S', "value": 'PRESS', "shift": True}, {"properties": [("data_path", 'tool_settings.weight_paint.brush.use_smooth_stroke')]}), op_menu_pie("VIEW3D_MT_wpaint_vgroup_lock_pie", {"type": 'K', "value": 'PRESS'}), + ("paint.face_vert_reveal", {"type": 'H', "value": 'PRESS', "alt": True}, None), *_template_items_context_panel("VIEW3D_PT_paint_weight_context_menu", params.context_menu_event), ]) diff --git a/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py b/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py index d60bbfed67a..8feaa7e928f 100644 --- a/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py +++ b/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py @@ -2948,7 +2948,7 @@ def km_face_mask(params): {"properties": [("unselected", False)]}), ("paint.face_select_hide", {"type": 'H', "value": 'PRESS', "shift": True}, {"properties": [("unselected", True)]}), - ("paint.face_select_reveal", {"type": 'H', "value": 'PRESS', "alt": True}, None), + ("paint.face_vert_reveal", {"type": 'H', "value": 'PRESS', "alt": True}, None), ("paint.face_select_linked", {"type": 'L', "value": 'PRESS', "ctrl": True}, None), ("paint.face_select_linked_pick", {"type": 'L', "value": 'PRESS'}, {"properties": [("deselect", False)]}), @@ -2969,6 +2969,9 @@ def km_weight_paint_vertex_selection(params): items.extend([ ("paint.vert_select_all", {"type": 'A', "value": 'PRESS', "ctrl": True}, None), + ("paint.vert_select_hide", {"type": 'H', "value": 'PRESS', "shift": True}, + {"properties": [("unselected", True)]}), + ("paint.face_vert_reveal", {"type": 'H', "value": 'PRESS', "alt": True}, None), ]) return keymap @@ -3330,6 +3333,7 @@ def km_vertex_paint(params): ("wm.context_toggle", {"type": 'S', "value": 'PRESS', "shift": True}, {"properties": [("data_path", 'tool_settings.vertex_paint.brush.use_smooth_stroke')]}), op_menu("VIEW3D_MT_angle_control", {"type": 'R', "value": 'PRESS'}), + ("paint.face_vert_reveal", {"type": 'H', "value": 'PRESS', "alt": True}, None), *_template_items_context_panel("VIEW3D_PT_paint_vertex_context_menu", {"type": 'RIGHTMOUSE', "value": 'PRESS'}), # Tools ("paint.brush_select", {"type": 'D', "value": 'PRESS'}, @@ -3362,6 +3366,7 @@ def km_weight_paint(params): {"properties": [("data_path", 'weight_paint_object.data.use_paint_mask')]}), ("wm.context_toggle", {"type": 'S', "value": 'PRESS', "shift": True}, {"properties": [("data_path", 'tool_settings.weight_paint.brush.use_smooth_stroke')]}), + ("paint.face_vert_reveal", {"type": 'H', "value": 'PRESS', "alt": True}, None), *_template_items_context_panel("VIEW3D_PT_paint_weight_context_menu", {"type": 'RIGHTMOUSE', "value": 'PRESS'}), # Bone selection for combined weight paint + pose mode. ("view3d.select", {"type": 'LEFTMOUSE', "value": 'PRESS', "ctrl": True}, None), diff --git a/source/blender/blenkernel/BKE_paint.h b/source/blender/blenkernel/BKE_paint.h index ffe80ff47b6..162459d2005 100644 --- a/source/blender/blenkernel/BKE_paint.h +++ b/source/blender/blenkernel/BKE_paint.h @@ -200,6 +200,11 @@ bool BKE_paint_select_vert_test(struct Object *ob); * (when we don't care if its face or vert) */ bool BKE_paint_select_elem_test(struct Object *ob); +/** + * Checks if face/vertex hiding is always applied in the current mode. + * Returns true in vertex/weight paint. + */ +bool BKE_paint_always_hide_test(struct Object *ob); /* Partial visibility. */ diff --git a/source/blender/blenkernel/intern/paint.c b/source/blender/blenkernel/intern/paint.c index 89cc25b31e6..9b0d15ac702 100644 --- a/source/blender/blenkernel/intern/paint.c +++ b/source/blender/blenkernel/intern/paint.c @@ -996,6 +996,12 @@ bool BKE_paint_select_elem_test(Object *ob) return (BKE_paint_select_vert_test(ob) || BKE_paint_select_face_test(ob)); } +bool BKE_paint_always_hide_test(Object *ob) +{ + return ((ob != NULL) && (ob->type == OB_MESH) && (ob->data != NULL) && + (ob->mode & OB_MODE_WEIGHT_PAINT || ob->mode & OB_MODE_VERTEX_PAINT)); +} + void BKE_paint_cavity_curve_preset(Paint *p, int preset) { CurveMapping *cumap = NULL; @@ -2255,12 +2261,7 @@ PBVH *BKE_sculpt_object_pbvh_ensure(Depsgraph *depsgraph, Object *ob) return NULL; } - bool respect_hide = true; - if (ob->mode & (OB_MODE_VERTEX_PAINT | OB_MODE_WEIGHT_PAINT)) { - if (!(BKE_paint_select_vert_test(ob) || BKE_paint_select_face_test(ob))) { - respect_hide = false; - } - } + const bool respect_hide = true; PBVH *pbvh = ob->sculpt->pbvh; if (pbvh != NULL) { diff --git a/source/blender/draw/intern/draw_manager.c b/source/blender/draw/intern/draw_manager.c index bc9d0a3d02a..5d21ab75650 100644 --- a/source/blender/draw/intern/draw_manager.c +++ b/source/blender/draw/intern/draw_manager.c @@ -235,7 +235,7 @@ bool DRW_object_use_hide_faces(const struct Object *ob) return (me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0; case OB_MODE_VERTEX_PAINT: case OB_MODE_WEIGHT_PAINT: - return (me->editflag & (ME_EDIT_PAINT_FACE_SEL | ME_EDIT_PAINT_VERT_SEL)) != 0; + return true; } } diff --git a/source/blender/editors/include/ED_mesh.h b/source/blender/editors/include/ED_mesh.h index 30a98129ee6..52044109702 100644 --- a/source/blender/editors/include/ED_mesh.h +++ b/source/blender/editors/include/ED_mesh.h @@ -425,6 +425,9 @@ void paintvert_select_ungrouped(struct Object *ob, bool extend, bool flush_flags void paintvert_flush_flags(struct Object *ob); void paintvert_tag_select_update(struct bContext *C, struct Object *ob); +void paintvert_hide(struct bContext *C, struct Object *ob, bool unselected); +void paintvert_reveal(struct bContext *C, struct Object *ob, bool select); + /* mirrtopo */ typedef struct MirrTopoStore_t { intptr_t *index_lookup; diff --git a/source/blender/editors/mesh/editface.cc b/source/blender/editors/mesh/editface.cc index 69fe69fe117..b69cd8b8606 100644 --- a/source/blender/editors/mesh/editface.cc +++ b/source/blender/editors/mesh/editface.cc @@ -551,3 +551,54 @@ void paintvert_select_ungrouped(Object *ob, bool extend, bool flush_flags) paintvert_flush_flags(ob); } } + +void paintvert_hide(bContext *C, Object *ob, const bool unselected) +{ + Mesh *const 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]; + + if ((mvert->flag & ME_HIDE) == 0) { + if (((mvert->flag & SELECT) == 0) == unselected) { + mvert->flag |= ME_HIDE; + } + } + + if (mvert->flag & ME_HIDE) { + mvert->flag &= ~SELECT; + } + } + + BKE_mesh_flush_hidden_from_verts(me); + + paintvert_flush_flags(ob); + paintvert_tag_select_update(C, ob); +} + +void paintvert_reveal(bContext *C, Object *ob, const bool select) +{ + Mesh *const 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]; + + if (mvert->flag & ME_HIDE) { + SET_FLAG_FROM_TEST(mvert->flag, select, SELECT); + mvert->flag &= ~ME_HIDE; + } + } + + BKE_mesh_flush_hidden_from_verts(me); + + paintvert_flush_flags(ob); + paintvert_tag_select_update(C, ob); +} diff --git a/source/blender/editors/sculpt_paint/paint_intern.h b/source/blender/editors/sculpt_paint/paint_intern.h index ea17114efa5..02c3b5be8b9 100644 --- a/source/blender/editors/sculpt_paint/paint_intern.h +++ b/source/blender/editors/sculpt_paint/paint_intern.h @@ -370,10 +370,12 @@ void PAINT_OT_face_select_linked(struct wmOperatorType *ot); void PAINT_OT_face_select_linked_pick(struct wmOperatorType *ot); void PAINT_OT_face_select_all(struct wmOperatorType *ot); void PAINT_OT_face_select_hide(struct wmOperatorType *ot); -void PAINT_OT_face_select_reveal(struct wmOperatorType *ot); + +void PAINT_OT_face_vert_reveal(struct wmOperatorType *ot); void PAINT_OT_vert_select_all(struct wmOperatorType *ot); void PAINT_OT_vert_select_ungrouped(struct wmOperatorType *ot); +void PAINT_OT_vert_select_hide(struct wmOperatorType *ot); bool vert_paint_poll(struct bContext *C); bool mask_paint_poll(struct bContext *C); diff --git a/source/blender/editors/sculpt_paint/paint_ops.c b/source/blender/editors/sculpt_paint/paint_ops.c index ce6b397af15..994ae4011b4 100644 --- a/source/blender/editors/sculpt_paint/paint_ops.c +++ b/source/blender/editors/sculpt_paint/paint_ops.c @@ -1454,6 +1454,7 @@ void ED_operatortypes_paint(void) /* vertex selection */ WM_operatortype_append(PAINT_OT_vert_select_all); WM_operatortype_append(PAINT_OT_vert_select_ungrouped); + WM_operatortype_append(PAINT_OT_vert_select_hide); /* vertex */ WM_operatortype_append(PAINT_OT_vertex_paint_toggle); @@ -1472,7 +1473,8 @@ void ED_operatortypes_paint(void) WM_operatortype_append(PAINT_OT_face_select_linked_pick); WM_operatortype_append(PAINT_OT_face_select_all); WM_operatortype_append(PAINT_OT_face_select_hide); - WM_operatortype_append(PAINT_OT_face_select_reveal); + + WM_operatortype_append(PAINT_OT_face_vert_reveal); /* partial visibility */ WM_operatortype_append(PAINT_OT_hide_show); diff --git a/source/blender/editors/sculpt_paint/paint_utils.c b/source/blender/editors/sculpt_paint/paint_utils.c index 3f26f590b70..1f272882100 100644 --- a/source/blender/editors/sculpt_paint/paint_utils.c +++ b/source/blender/editors/sculpt_paint/paint_utils.c @@ -749,25 +749,68 @@ void PAINT_OT_face_select_hide(wmOperatorType *ot) ot->srna, "unselected", 0, "Unselected", "Hide unselected rather than selected objects"); } -static int face_select_reveal_exec(bContext *C, wmOperator *op) +static int vert_select_hide_exec(bContext *C, wmOperator *op) +{ + const bool unselected = RNA_boolean_get(op->ptr, "unselected"); + Object *ob = CTX_data_active_object(C); + paintvert_hide(C, ob, unselected); + ED_region_tag_redraw(CTX_wm_region(C)); + return OPERATOR_FINISHED; +} + +void PAINT_OT_vert_select_hide(wmOperatorType *ot) +{ + ot->name = "Vertex Select Hide"; + ot->description = "Hide selected vertices"; + ot->idname = "PAINT_OT_vert_select_hide"; + + ot->exec = vert_select_hide_exec; + ot->poll = vert_paint_poll; + + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + RNA_def_boolean( + ot->srna, "unselected", 0, "Unselected", "Hide unselected rather than selected vertices"); +} + +static int face_vert_reveal_exec(bContext *C, wmOperator *op) { const bool select = RNA_boolean_get(op->ptr, "select"); Object *ob = CTX_data_active_object(C); - paintface_reveal(C, ob, select); + + if (BKE_paint_select_vert_test(ob)) { + paintvert_reveal(C, ob, select); + } + else { + paintface_reveal(C, ob, select); + } + ED_region_tag_redraw(CTX_wm_region(C)); return OPERATOR_FINISHED; } -void PAINT_OT_face_select_reveal(wmOperatorType *ot) +static bool face_vert_reveal_poll(bContext *C) { - ot->name = "Face Select Reveal"; - ot->description = "Reveal hidden faces"; - ot->idname = "PAINT_OT_face_select_reveal"; + Object *ob = CTX_data_active_object(C); - ot->exec = face_select_reveal_exec; - ot->poll = facemask_paint_poll; + /* Allow using this operator when no selection is enabled but hiding is applied. */ + return BKE_paint_select_elem_test(ob) || BKE_paint_always_hide_test(ob); +} + +void PAINT_OT_face_vert_reveal(wmOperatorType *ot) +{ + ot->name = "Reveal Faces/Vertices"; + ot->description = "Reveal hidden faces and vertices"; + ot->idname = "PAINT_OT_face_vert_reveal"; + + ot->exec = face_vert_reveal_exec; + ot->poll = face_vert_reveal_poll; ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - RNA_def_boolean(ot->srna, "select", true, "Select", ""); + RNA_def_boolean(ot->srna, + "select", + true, + "Select", + "Specifies whether the newly revealed geometry should be selected"); } -- cgit v1.2.3 From 80f8b7cbbb562c6967ba81c76027a984cd9cd9b6 Mon Sep 17 00:00:00 2001 From: Daniel Salazar Date: Sat, 9 Jul 2022 02:06:32 -0600 Subject: UI: renaming fIle browser thumbnail sizes Rename the thumbnail size from Regular to Medium since it's the typical way to refer to sizing in American English Reviewed By: Campbell Barton Differential Revision: https://developer.blender.org/D15305 --- source/blender/makesrna/intern/rna_space.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/makesrna/intern/rna_space.c b/source/blender/makesrna/intern/rna_space.c index 969d1f2075e..e67d840eeac 100644 --- a/source/blender/makesrna/intern/rna_space.c +++ b/source/blender/makesrna/intern/rna_space.c @@ -6585,7 +6585,7 @@ static void rna_def_fileselect_params(BlenderRNA *brna) static const EnumPropertyItem display_size_items[] = { {64, "TINY", 0, "Tiny", ""}, {96, "SMALL", 0, "Small", ""}, - {128, "NORMAL", 0, "Regular", ""}, + {128, "NORMAL", 0, "Medium", ""}, {192, "LARGE", 0, "Large", ""}, {0, NULL, 0, NULL, NULL}, }; -- cgit v1.2.3 From b5d22a8134ac3bfc6c084b836729f4dd15e25bee Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Sat, 9 Jul 2022 22:27:26 +1000 Subject: Fix resource leaks setting custom cursors in Wayland - Memory from the prior cursor was never un-mapped. - posix_fallocate failure left a file handle open.. --- intern/ghost/intern/GHOST_SystemWayland.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/intern/ghost/intern/GHOST_SystemWayland.cpp b/intern/ghost/intern/GHOST_SystemWayland.cpp index 08aa640c5cd..76e5329a410 100644 --- a/intern/ghost/intern/GHOST_SystemWayland.cpp +++ b/intern/ghost/intern/GHOST_SystemWayland.cpp @@ -3347,6 +3347,11 @@ GHOST_TSuccess GHOST_SystemWayland::setCustomCursorShape(uint8_t *bitmap, cursor_t *cursor = &d->inputs[0]->cursor; + if (cursor->file_buffer->data) { + munmap(cursor->file_buffer->data, cursor->file_buffer->size); + cursor->file_buffer->data = nullptr; + } + static const int32_t stride = sizex * 4; /* ARGB */ cursor->file_buffer->size = (size_t)stride * sizey; @@ -3376,6 +3381,7 @@ GHOST_TSuccess GHOST_SystemWayland::setCustomCursorShape(uint8_t *bitmap, } if (UNLIKELY(posix_fallocate(fd, 0, int32_t(cursor->file_buffer->size)) != 0)) { + close(fd); return GHOST_kFailure; } -- cgit v1.2.3 From ef970b7756259747d7b724ae7d3195ec5520e478 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Sat, 9 Jul 2022 22:27:27 +1000 Subject: Cleanup: split memfd_create into it's own function for Wayland Avoid ifdef's in cursor loading by creating a memfd_create_sealed utility function that works irrespective of memfd_create availability. --- intern/ghost/intern/GHOST_SystemWayland.cpp | 47 +++++++++++++++-------------- 1 file changed, 25 insertions(+), 22 deletions(-) diff --git a/intern/ghost/intern/GHOST_SystemWayland.cpp b/intern/ghost/intern/GHOST_SystemWayland.cpp index 76e5329a410..25123fe651f 100644 --- a/intern/ghost/intern/GHOST_SystemWayland.cpp +++ b/intern/ghost/intern/GHOST_SystemWayland.cpp @@ -747,7 +747,30 @@ static const std::vector mime_send = { "text/plain", }; -#undef LOG +static int memfd_create_sealed(const char *name) +{ +#ifdef HAVE_MEMFD_CREATE + const int fd = memfd_create(name, MFD_CLOEXEC | MFD_ALLOW_SEALING); + if (fd >= 0) { + fcntl(fd, F_ADD_SEALS, F_SEAL_SHRINK | F_SEAL_SEAL); + } + return fd; +#else /* HAVE_MEMFD_CREATE */ + char *path = getenv("XDG_RUNTIME_DIR"); + if (!path) { + errno = ENOENT; + return -1; + } + char *tmpname; + asprintf(&tmpname, "%s/%s-XXXXXX", path, name); + const int fd = mkostemp(tmpname, O_CLOEXEC); + if (fd >= 0) { + unlink(tmpname); + } + free(tmpname); + return fd; +#endif /* !HAVE_MEMFD_CREATE */ +} /** \} */ @@ -3355,27 +3378,7 @@ GHOST_TSuccess GHOST_SystemWayland::setCustomCursorShape(uint8_t *bitmap, static const int32_t stride = sizex * 4; /* ARGB */ cursor->file_buffer->size = (size_t)stride * sizey; -#ifdef HAVE_MEMFD_CREATE - const int fd = memfd_create("blender-cursor-custom", MFD_CLOEXEC | MFD_ALLOW_SEALING); - if (fd >= 0) { - fcntl(fd, F_ADD_SEALS, F_SEAL_SHRINK | F_SEAL_SEAL); - } -#else - char *path = getenv("XDG_RUNTIME_DIR"); - if (!path) { - errno = ENOENT; - return GHOST_kFailure; - } - - char *tmpname; - asprintf(&tmpname, "%s/%s", path, "blender-XXXXXX"); - const int fd = mkostemp(tmpname, O_CLOEXEC); - if (fd >= 0) { - unlink(tmpname); - } - free(tmpname); -#endif - + const int fd = memfd_create_sealed("blender-cursor-custom"); if (UNLIKELY(fd < 0)) { return GHOST_kFailure; } -- cgit v1.2.3 From 9a1d772339d6eafe3ddbc05f36075ee01f654610 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Sat, 9 Jul 2022 22:27:28 +1000 Subject: Cleanup: remove buffer_t in GHOST/Wayland This was allocated and only used to store the custom cursor data. Use a pointer & size member instead for simplicity. --- intern/ghost/intern/GHOST_SystemWayland.cpp | 45 ++++++++++++++--------------- 1 file changed, 21 insertions(+), 24 deletions(-) diff --git a/intern/ghost/intern/GHOST_SystemWayland.cpp b/intern/ghost/intern/GHOST_SystemWayland.cpp index 25123fe651f..98437f65e7e 100644 --- a/intern/ghost/intern/GHOST_SystemWayland.cpp +++ b/intern/ghost/intern/GHOST_SystemWayland.cpp @@ -128,11 +128,6 @@ static bool use_gnome_confine_hack = false; */ #define EVDEV_OFFSET 8 -struct buffer_t { - void *data = nullptr; - size_t size = 0; -}; - struct cursor_t { bool visible = false; /** @@ -147,7 +142,8 @@ struct cursor_t { struct wl_buffer *wl_buffer = nullptr; struct wl_cursor_image wl_image = {0}; struct wl_cursor_theme *wl_theme = nullptr; - struct buffer_t *file_buffer = nullptr; + void *custom_data = nullptr; + size_t custom_data_size = 0; int size = 0; std::string theme_name; /** Outputs on which the cursor is visible. */ @@ -449,11 +445,12 @@ static void display_destroy(display_t *d) if (input->data_device) { wl_data_device_release(input->data_device); } + + if (input->cursor.custom_data) { + munmap(input->cursor.custom_data, input->cursor.custom_data_size); + } + if (input->wl_pointer) { - if (input->cursor.file_buffer) { - munmap(input->cursor.file_buffer->data, input->cursor.file_buffer->size); - delete input->cursor.file_buffer; - } if (input->cursor.wl_surface) { wl_surface_destroy(input->cursor.wl_surface); } @@ -2305,7 +2302,6 @@ static void seat_handle_capabilities(void *data, input->cursor.wl_surface = wl_compositor_create_surface(input->system->compositor()); input->cursor.visible = true; input->cursor.wl_buffer = nullptr; - input->cursor.file_buffer = new buffer_t; if (!get_cursor_settings(input->cursor.theme_name, input->cursor.size)) { input->cursor.theme_name = std::string(); input->cursor.size = default_cursor_size; @@ -3370,34 +3366,35 @@ GHOST_TSuccess GHOST_SystemWayland::setCustomCursorShape(uint8_t *bitmap, cursor_t *cursor = &d->inputs[0]->cursor; - if (cursor->file_buffer->data) { - munmap(cursor->file_buffer->data, cursor->file_buffer->size); - cursor->file_buffer->data = nullptr; + if (cursor->custom_data) { + munmap(cursor->custom_data, cursor->custom_data_size); + cursor->custom_data = nullptr; + cursor->custom_data_size = 0; /* Not needed, but the value is no longer meaningful. */ } static const int32_t stride = sizex * 4; /* ARGB */ - cursor->file_buffer->size = (size_t)stride * sizey; + cursor->custom_data_size = (size_t)stride * sizey; const int fd = memfd_create_sealed("blender-cursor-custom"); if (UNLIKELY(fd < 0)) { return GHOST_kFailure; } - if (UNLIKELY(posix_fallocate(fd, 0, int32_t(cursor->file_buffer->size)) != 0)) { + if (UNLIKELY(posix_fallocate(fd, 0, int32_t(cursor->custom_data_size)) != 0)) { close(fd); return GHOST_kFailure; } - cursor->file_buffer->data = mmap( - nullptr, cursor->file_buffer->size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + cursor->custom_data = mmap( + nullptr, cursor->custom_data_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); - if (UNLIKELY(cursor->file_buffer->data == MAP_FAILED)) { - cursor->file_buffer->data = nullptr; + if (UNLIKELY(cursor->custom_data == MAP_FAILED)) { + cursor->custom_data = nullptr; close(fd); return GHOST_kFailure; } - struct wl_shm_pool *pool = wl_shm_create_pool(d->shm, fd, int32_t(cursor->file_buffer->size)); + struct wl_shm_pool *pool = wl_shm_create_pool(d->shm, fd, int32_t(cursor->custom_data_size)); wl_buffer *buffer = wl_shm_pool_create_buffer( pool, 0, sizex, sizey, stride, WL_SHM_FORMAT_ARGB8888); @@ -3415,7 +3412,7 @@ GHOST_TSuccess GHOST_SystemWayland::setCustomCursorShape(uint8_t *bitmap, uint32_t *pixel; for (int y = 0; y < sizey; ++y) { - pixel = &static_cast(cursor->file_buffer->data)[y * sizex]; + pixel = &static_cast(cursor->custom_data)[y * sizex]; for (int x = 0; x < sizex; ++x) { if ((x % 8) == 0) { datab = *bitmap++; @@ -3454,7 +3451,7 @@ GHOST_TSuccess GHOST_SystemWayland::setCustomCursorShape(uint8_t *bitmap, GHOST_TSuccess GHOST_SystemWayland::getCursorBitmap(GHOST_CursorBitmapRef *bitmap) { cursor_t *cursor = &d->inputs[0]->cursor; - if (cursor->file_buffer->data == nullptr) { + if (cursor->custom_data == nullptr) { return GHOST_kFailure; } if (!cursor->is_custom) { @@ -3467,7 +3464,7 @@ GHOST_TSuccess GHOST_SystemWayland::getCursorBitmap(GHOST_CursorBitmapRef *bitma bitmap->hot_spot[0] = cursor->wl_image.hotspot_x; bitmap->hot_spot[1] = cursor->wl_image.hotspot_y; - bitmap->data = (uint8_t *)static_cast(cursor->file_buffer->data); + bitmap->data = (uint8_t *)static_cast(cursor->custom_data); return GHOST_kSuccess; } -- cgit v1.2.3 From 1de14061cbe33dd39ee48c65b609d550cac157ca Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Sat, 9 Jul 2022 22:27:30 +1000 Subject: Cleanup: split out wl_buffer creation into a utility function Simplify logic for initializing the wl_buffer, ensure the cursors custom data is never heft in a half initialized state. Also remove the need for multiple calls to close when handling errors. --- intern/ghost/intern/GHOST_SystemWayland.cpp | 84 +++++++++++++++++++---------- 1 file changed, 57 insertions(+), 27 deletions(-) diff --git a/intern/ghost/intern/GHOST_SystemWayland.cpp b/intern/ghost/intern/GHOST_SystemWayland.cpp index 98437f65e7e..4b390615435 100644 --- a/intern/ghost/intern/GHOST_SystemWayland.cpp +++ b/intern/ghost/intern/GHOST_SystemWayland.cpp @@ -769,6 +769,59 @@ static int memfd_create_sealed(const char *name) #endif /* !HAVE_MEMFD_CREATE */ } +static size_t ghost_wl_shm_format_as_size(enum wl_shm_format format) +{ + switch (format) { + case WL_SHM_FORMAT_ARGB8888: { + return 4; + } + default: { + /* Support other formats as needed. */ + GHOST_ASSERT(0, "Unexpected format passed in!"); + return 4; + } + } +} + +/** + * Return a #wl_buffer, ready to have it's data filled in or NULL in case of failure. + * The caller is responsible for calling `unmap(buffer_data, buffer_size)`. + * + * \param r_buffer_data: The buffer to be filled. + * \param r_buffer_data_size: The size of `r_buffer_data` in bytes. + */ +static wl_buffer *ghost_wl_buffer_create_for_image(struct wl_shm *shm, + const int32_t size_xy[2], + enum wl_shm_format format, + void **r_buffer_data, + size_t *r_buffer_data_size) +{ + const int fd = memfd_create_sealed("ghost-wl-buffer"); + wl_buffer *buffer = nullptr; + if (fd >= 0) { + const int32_t buffer_stride = size_xy[0] * ghost_wl_shm_format_as_size(format); + const int32_t buffer_size = buffer_stride * size_xy[1]; + if (posix_fallocate(fd, 0, buffer_size) == 0) { + void *buffer_data = mmap(nullptr, buffer_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if (buffer_data != MAP_FAILED) { + struct wl_shm_pool *pool = wl_shm_create_pool(shm, fd, buffer_size); + buffer = wl_shm_pool_create_buffer(pool, 0, UNPACK2(size_xy), buffer_stride, format); + wl_shm_pool_destroy(pool); + if (buffer) { + *r_buffer_data = buffer_data; + *r_buffer_data_size = (size_t)buffer_size; + } + else { + /* Highly unlikely. */ + munmap(buffer_data, buffer_size); + } + } + } + close(fd); + } + return buffer; +} + /** \} */ /* -------------------------------------------------------------------- */ @@ -3372,36 +3425,13 @@ GHOST_TSuccess GHOST_SystemWayland::setCustomCursorShape(uint8_t *bitmap, cursor->custom_data_size = 0; /* Not needed, but the value is no longer meaningful. */ } - static const int32_t stride = sizex * 4; /* ARGB */ - cursor->custom_data_size = (size_t)stride * sizey; - - const int fd = memfd_create_sealed("blender-cursor-custom"); - if (UNLIKELY(fd < 0)) { - return GHOST_kFailure; - } - - if (UNLIKELY(posix_fallocate(fd, 0, int32_t(cursor->custom_data_size)) != 0)) { - close(fd); - return GHOST_kFailure; - } - - cursor->custom_data = mmap( - nullptr, cursor->custom_data_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); - - if (UNLIKELY(cursor->custom_data == MAP_FAILED)) { - cursor->custom_data = nullptr; - close(fd); + const int32_t size_xy[2] = {sizex, sizey}; + wl_buffer *buffer = ghost_wl_buffer_create_for_image( + d->shm, size_xy, WL_SHM_FORMAT_ARGB8888, &cursor->custom_data, &cursor->custom_data_size); + if (buffer == nullptr) { return GHOST_kFailure; } - struct wl_shm_pool *pool = wl_shm_create_pool(d->shm, fd, int32_t(cursor->custom_data_size)); - - wl_buffer *buffer = wl_shm_pool_create_buffer( - pool, 0, sizex, sizey, stride, WL_SHM_FORMAT_ARGB8888); - - wl_shm_pool_destroy(pool); - close(fd); - wl_buffer_add_listener(buffer, &cursor_buffer_listener, cursor); static constexpr uint32_t black = 0xFF000000; -- cgit v1.2.3 From 443690604f4ed5bdcf05cca40c8d6f1f88a1c29a Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Sat, 9 Jul 2022 22:27:34 +1000 Subject: Fix cursor display size with tablet input in GHOST/Wayland The scale for tablet cursor surfaces was never set, making them display larger. Now the outputs scale is set for mouse & tablet cursors. --- intern/ghost/intern/GHOST_SystemWayland.cpp | 191 ++++++++++++++++++---------- intern/ghost/intern/GHOST_SystemWayland.h | 6 + 2 files changed, 133 insertions(+), 64 deletions(-) diff --git a/intern/ghost/intern/GHOST_SystemWayland.cpp b/intern/ghost/intern/GHOST_SystemWayland.cpp index 4b390615435..876f77732c2 100644 --- a/intern/ghost/intern/GHOST_SystemWayland.cpp +++ b/intern/ghost/intern/GHOST_SystemWayland.cpp @@ -146,10 +146,7 @@ struct cursor_t { size_t custom_data_size = 0; int size = 0; std::string theme_name; - /** Outputs on which the cursor is visible. */ - std::unordered_set outputs; - int theme_scale = 1; int custom_scale = 1; }; @@ -231,6 +228,11 @@ struct input_state_pointer_t { */ wl_fixed_t xy[2] = {0, 0}; + /** Outputs on which the cursor is visible. */ + std::unordered_set outputs; + + int theme_scale = 1; + /** The serial of the last used pointer or tablet. */ uint32_t serial = 0; @@ -396,6 +398,19 @@ static input_state_pointer_t *input_state_pointer_active(input_t *input) return nullptr; } +static input_state_pointer_t *input_state_pointer_from_cursor_surface(input_t *input, + const wl_surface *wl_surface) +{ + if (ghost_wl_surface_own_cursor_pointer(wl_surface)) { + return &input->pointer; + } + if (ghost_wl_surface_own_cursor_tablet(wl_surface)) { + return &input->tablet; + } + GHOST_ASSERT(0, "Surface found without pointer/tablet tag"); + return nullptr; +} + static void display_destroy(display_t *d) { if (d->data_device_manager) { @@ -1358,19 +1373,22 @@ static const struct wl_buffer_listener cursor_buffer_listener = { static CLG_LogRef LOG_WL_CURSOR_SURFACE = {"ghost.wl.handle.cursor_surface"}; #define LOG (&LOG_WL_CURSOR_SURFACE) -static bool update_cursor_scale(cursor_t &cursor, wl_shm *shm) +static bool update_cursor_scale(cursor_t &cursor, + wl_shm *shm, + input_state_pointer_t *input_state, + wl_surface *cursor_surface) { int scale = 0; - for (const output_t *output : cursor.outputs) { + for (const output_t *output : input_state->outputs) { if (output->scale > scale) { scale = output->scale; } } - if (scale > 0 && cursor.theme_scale != scale) { - cursor.theme_scale = scale; + if (scale > 0 && input_state->theme_scale != scale) { + input_state->theme_scale = scale; if (!cursor.is_custom) { - wl_surface_set_buffer_scale(cursor.wl_surface, scale); + wl_surface_set_buffer_scale(cursor_surface, scale); } wl_cursor_theme_destroy(cursor.wl_theme); cursor.wl_theme = wl_cursor_theme_load(cursor.theme_name.c_str(), scale * cursor.size, shm); @@ -1380,7 +1398,7 @@ static bool update_cursor_scale(cursor_t &cursor, wl_shm *shm) } static void cursor_surface_handle_enter(void *data, - struct wl_surface * /*wl_surface*/, + struct wl_surface *wl_surface, struct wl_output *output) { if (!ghost_wl_output_own(output)) { @@ -1390,13 +1408,14 @@ static void cursor_surface_handle_enter(void *data, CLOG_INFO(LOG, 2, "handle_enter"); input_t *input = static_cast(data); + input_state_pointer_t *input_state = input_state_pointer_from_cursor_surface(input, wl_surface); const output_t *reg_output = ghost_wl_output_user_data(output); - input->cursor.outputs.insert(reg_output); - update_cursor_scale(input->cursor, input->system->shm()); + input_state->outputs.insert(reg_output); + update_cursor_scale(input->cursor, input->system->shm(), input_state, wl_surface); } static void cursor_surface_handle_leave(void *data, - struct wl_surface * /*wl_surface*/, + struct wl_surface *wl_surface, struct wl_output *output) { if (!(output && ghost_wl_output_own(output))) { @@ -1406,9 +1425,10 @@ static void cursor_surface_handle_leave(void *data, CLOG_INFO(LOG, 2, "handle_leave"); input_t *input = static_cast(data); + input_state_pointer_t *input_state = input_state_pointer_from_cursor_surface(input, wl_surface); const output_t *reg_output = ghost_wl_output_user_data(output); - input->cursor.outputs.erase(reg_output); - update_cursor_scale(input->cursor, input->system->shm()); + input_state->outputs.erase(reg_output); + update_cursor_scale(input->cursor, input->system->shm(), input_state, wl_surface); } static const struct wl_surface_listener cursor_surface_listener = { @@ -1952,6 +1972,8 @@ static void tablet_seat_handle_tool_added(void *data, /* Every tool has it's own cursor surface. */ tool_input->cursor_surface = wl_compositor_create_surface(input->system->compositor()); + ghost_wl_surface_tag_cursor_tablet(tool_input->cursor_surface); + wl_surface_add_listener(tool_input->cursor_surface, &cursor_surface_listener, (void *)input); zwp_tablet_tool_v2_add_listener(id, &tablet_tool_listner, tool_input); @@ -2360,7 +2382,9 @@ static void seat_handle_capabilities(void *data, input->cursor.size = default_cursor_size; } wl_pointer_add_listener(input->wl_pointer, &pointer_listener, data); + wl_surface_add_listener(input->cursor.wl_surface, &cursor_surface_listener, data); + ghost_wl_surface_tag_cursor_pointer(input->cursor.wl_surface); } if (capabilities & WL_SEAT_CAPABILITY_KEYBOARD) { @@ -3211,19 +3235,30 @@ GHOST_IWindow *GHOST_SystemWayland::createWindow(const char *title, static void cursor_buffer_show(const input_t *input) { const cursor_t *c = &input->cursor; - const int scale = c->is_custom ? c->custom_scale : c->theme_scale; - const int32_t hotspot_x = int32_t(c->wl_image.hotspot_x) / scale; - const int32_t hotspot_y = int32_t(c->wl_image.hotspot_y) / scale; - wl_pointer_set_cursor( - input->wl_pointer, input->pointer.serial, c->wl_surface, hotspot_x, hotspot_y); - for (struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2 : input->tablet_tools) { - tablet_tool_input_t *tool_input = static_cast( - zwp_tablet_tool_v2_get_user_data(zwp_tablet_tool_v2)); - zwp_tablet_tool_v2_set_cursor(zwp_tablet_tool_v2, - input->tablet.serial, - tool_input->cursor_surface, - hotspot_x, - hotspot_y); + + if (input->wl_pointer) { + const int scale = c->is_custom ? c->custom_scale : input->pointer.theme_scale; + const int32_t hotspot_x = int32_t(c->wl_image.hotspot_x) / scale; + const int32_t hotspot_y = int32_t(c->wl_image.hotspot_y) / scale; + if (input->wl_pointer) { + wl_pointer_set_cursor( + input->wl_pointer, input->pointer.serial, c->wl_surface, hotspot_x, hotspot_y); + } + } + + if (!input->tablet_tools.empty()) { + const int scale = c->is_custom ? c->custom_scale : input->tablet.theme_scale; + const int32_t hotspot_x = int32_t(c->wl_image.hotspot_x) / scale; + const int32_t hotspot_y = int32_t(c->wl_image.hotspot_y) / scale; + for (struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2 : input->tablet_tools) { + tablet_tool_input_t *tool_input = static_cast( + zwp_tablet_tool_v2_get_user_data(zwp_tablet_tool_v2)); + zwp_tablet_tool_v2_set_cursor(zwp_tablet_tool_v2, + input->tablet.serial, + tool_input->cursor_surface, + hotspot_x, + hotspot_y); + } } } @@ -3241,52 +3276,58 @@ static void cursor_buffer_hide(const input_t *input) } } +static void cursor_buffer_set_surface_impl(const input_t *input, + wl_buffer *buffer, + struct wl_surface *wl_surface, + const int scale) +{ + const wl_cursor_image *wl_image = &input->cursor.wl_image; + const int32_t image_size_x = int32_t(wl_image->width); + const int32_t image_size_y = int32_t(wl_image->height); + GHOST_ASSERT((image_size_x % scale) == 0 && (image_size_y % scale) == 0, + "The size must be a multiple of the scale!"); + + wl_surface_set_buffer_scale(wl_surface, scale); + wl_surface_attach(wl_surface, buffer, 0, 0); + wl_surface_damage(wl_surface, 0, 0, image_size_x, image_size_y); + wl_surface_commit(wl_surface); +} + static void cursor_buffer_set(const input_t *input, wl_buffer *buffer) { const cursor_t *c = &input->cursor; - const int scale = c->is_custom ? c->custom_scale : c->theme_scale; - + const wl_cursor_image *wl_image = &input->cursor.wl_image; const bool visible = (c->visible && c->is_hardware); - const int32_t image_size_x = int32_t(c->wl_image.width); - const int32_t image_size_y = int32_t(c->wl_image.height); - /* This is a requirement of WAYLAND, when this isn't the case, * it causes Blender's window to close intermittently. */ - GHOST_ASSERT((image_size_x % scale) == 0 && (image_size_y % scale) == 0, - "The size must be a multiple of the scale!"); - - const int32_t hotspot_x = int32_t(c->wl_image.hotspot_x) / scale; - const int32_t hotspot_y = int32_t(c->wl_image.hotspot_y) / scale; - - wl_surface_set_buffer_scale(c->wl_surface, scale); - wl_surface_attach(c->wl_surface, buffer, 0, 0); - wl_surface_damage(c->wl_surface, 0, 0, image_size_x, image_size_y); - wl_surface_commit(c->wl_surface); - - wl_pointer_set_cursor(input->wl_pointer, - input->pointer.serial, - visible ? c->wl_surface : nullptr, - hotspot_x, - hotspot_y); + if (input->wl_pointer) { + const int scale = c->is_custom ? c->custom_scale : input->pointer.theme_scale; + const int32_t hotspot_x = int32_t(wl_image->hotspot_x) / scale; + const int32_t hotspot_y = int32_t(wl_image->hotspot_y) / scale; + cursor_buffer_set_surface_impl(input, buffer, c->wl_surface, scale); + wl_pointer_set_cursor(input->wl_pointer, + input->pointer.serial, + visible ? c->wl_surface : nullptr, + hotspot_x, + hotspot_y); + } /* Set the cursor for all tablet tools as well. */ - for (struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2 : input->tablet_tools) { - tablet_tool_input_t *tool_input = static_cast( - zwp_tablet_tool_v2_get_user_data(zwp_tablet_tool_v2)); - - /* FIXME: for some reason cursor scale is applied twice (when the scale isn't 1x), - * this happens both in gnome-shell & KDE. Setting the surface scale here doesn't help. */ - wl_surface_set_buffer_scale(tool_input->cursor_surface, scale); - wl_surface_attach(tool_input->cursor_surface, buffer, 0, 0); - wl_surface_damage(tool_input->cursor_surface, 0, 0, image_size_x, image_size_y); - wl_surface_commit(tool_input->cursor_surface); - - zwp_tablet_tool_v2_set_cursor(zwp_tablet_tool_v2, - input->tablet.serial, - visible ? tool_input->cursor_surface : nullptr, - hotspot_x, - hotspot_y); + if (!input->tablet_tools.empty()) { + const int scale = c->is_custom ? c->custom_scale : input->tablet.theme_scale; + const int32_t hotspot_x = int32_t(wl_image->hotspot_x) / scale; + const int32_t hotspot_y = int32_t(wl_image->hotspot_y) / scale; + for (struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2 : input->tablet_tools) { + tablet_tool_input_t *tool_input = static_cast( + zwp_tablet_tool_v2_get_user_data(zwp_tablet_tool_v2)); + cursor_buffer_set_surface_impl(input, buffer, tool_input->cursor_surface, scale); + zwp_tablet_tool_v2_set_cursor(zwp_tablet_tool_v2, + input->tablet.serial, + visible ? tool_input->cursor_surface : nullptr, + hotspot_x, + hotspot_y); + } } } @@ -3585,6 +3626,8 @@ static input_grab_state_t input_grab_state_from_mode(const GHOST_TGrabCursorMode static const char *ghost_wl_output_tag_id = "GHOST-output"; static const char *ghost_wl_surface_tag_id = "GHOST-window"; +static const char *ghost_wl_surface_cursor_pointer_tag_id = "GHOST-cursor-pointer"; +static const char *ghost_wl_surface_cursor_tablet_tag_id = "GHOST-cursor-tablet"; bool ghost_wl_output_own(const struct wl_output *output) { @@ -3596,6 +3639,16 @@ bool ghost_wl_surface_own(const struct wl_surface *surface) return wl_proxy_get_tag((struct wl_proxy *)surface) == &ghost_wl_surface_tag_id; } +bool ghost_wl_surface_own_cursor_pointer(const struct wl_surface *surface) +{ + return wl_proxy_get_tag((struct wl_proxy *)surface) == &ghost_wl_surface_cursor_pointer_tag_id; +} + +bool ghost_wl_surface_own_cursor_tablet(const struct wl_surface *surface) +{ + return wl_proxy_get_tag((struct wl_proxy *)surface) == &ghost_wl_surface_cursor_tablet_tag_id; +} + void ghost_wl_output_tag(struct wl_output *output) { wl_proxy_set_tag((struct wl_proxy *)output, &ghost_wl_output_tag_id); @@ -3606,6 +3659,16 @@ void ghost_wl_surface_tag(struct wl_surface *surface) wl_proxy_set_tag((struct wl_proxy *)surface, &ghost_wl_surface_tag_id); } +void ghost_wl_surface_tag_cursor_pointer(struct wl_surface *surface) +{ + wl_proxy_set_tag((struct wl_proxy *)surface, &ghost_wl_surface_cursor_pointer_tag_id); +} + +void ghost_wl_surface_tag_cursor_tablet(struct wl_surface *surface) +{ + wl_proxy_set_tag((struct wl_proxy *)surface, &ghost_wl_surface_cursor_tablet_tag_id); +} + /** \} */ /* -------------------------------------------------------------------- */ diff --git a/intern/ghost/intern/GHOST_SystemWayland.h b/intern/ghost/intern/GHOST_SystemWayland.h index 0d51759aa2f..bdf5f2fc273 100644 --- a/intern/ghost/intern/GHOST_SystemWayland.h +++ b/intern/ghost/intern/GHOST_SystemWayland.h @@ -41,6 +41,12 @@ bool ghost_wl_surface_own(const struct wl_surface *surface); void ghost_wl_surface_tag(struct wl_surface *surface); GHOST_WindowWayland *ghost_wl_surface_user_data(struct wl_surface *surface); +bool ghost_wl_surface_own_cursor_pointer(const struct wl_surface *surface); +void ghost_wl_surface_tag_cursor_pointer(struct wl_surface *surface); + +bool ghost_wl_surface_own_cursor_tablet(const struct wl_surface *surface); +void ghost_wl_surface_tag_cursor_tablet(struct wl_surface *surface); + #ifdef WITH_GHOST_WAYLAND_DYNLOAD /** * Return true when all required WAYLAND libraries are present, -- cgit v1.2.3 From 4114ace6169282390b199ea593c93445af748903 Mon Sep 17 00:00:00 2001 From: Aras Pranckevicius Date: Sun, 10 Jul 2022 18:27:07 +0300 Subject: Fix T99536: new 3.2 OBJ importer fails with trailing space after wrapped lines Address the issue by re-working line continuation handling: stop trying to parse sequences like "backslash, newline" (which is the bug: it should also handle "backslash, possible whitespace, newline") during parsing. Instead, fixup line continuations after reading chunks of input file data - turn backslash and the following newline into spaces. The rest of parsing code does not have to be aware of them at all then. Makes the file attached to T99536 load correctly now. Also will extend one of the test files in subversion tests repo to contain backslashes followed by newlines. --- .../importer/obj_import_file_reader.cc | 9 +++++--- .../importer/obj_import_string_utils.cc | 26 ++++++++++++++++++---- .../importer/obj_import_string_utils.hh | 14 +++++------- .../tests/obj_import_string_utils_tests.cc | 23 ++++++++++++++++--- 4 files changed, 54 insertions(+), 18 deletions(-) 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 c2aa96713b1..6eb1bbc51f3 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 @@ -427,6 +427,11 @@ void OBJParser::parse(Vector> &r_all_geometries, break; /* No more data to read. */ } + /* Take care of line continuations now (turn them into spaces); + * the rest of the parsing code does not need to worry about them anymore. */ + fixup_line_continuations(buffer.data() + buffer_offset, + buffer.data() + buffer_offset + bytes_read); + /* Ensure buffer ends in a newline. */ if (bytes_read < read_buffer_size_) { if (bytes_read == 0 || buffer[buffer_offset + bytes_read - 1] != '\n') { @@ -445,9 +450,7 @@ void OBJParser::parse(Vector> &r_all_geometries, while (last_nl > 0) { --last_nl; if (buffer[last_nl] == '\n') { - if (last_nl < 1 || buffer[last_nl - 1] != '\\') { - break; - } + break; } } if (buffer[last_nl] != '\n') { diff --git a/source/blender/io/wavefront_obj/importer/obj_import_string_utils.cc b/source/blender/io/wavefront_obj/importer/obj_import_string_utils.cc index bc9006e1051..9a457167fca 100644 --- a/source/blender/io/wavefront_obj/importer/obj_import_string_utils.cc +++ b/source/blender/io/wavefront_obj/importer/obj_import_string_utils.cc @@ -18,14 +18,12 @@ StringRef read_next_line(StringRef &buffer) const char *start = buffer.begin(); const char *end = buffer.end(); size_t len = 0; - char prev = 0; const char *ptr = start; while (ptr < end) { char c = *ptr++; - if (c == '\n' && prev != '\\') { + if (c == '\n') { break; } - prev = c; ++len; } @@ -35,7 +33,27 @@ StringRef read_next_line(StringRef &buffer) static bool is_whitespace(char c) { - return c <= ' ' || c == '\\'; + return c <= ' '; +} + +void fixup_line_continuations(char *p, char *end) +{ + while (true) { + /* Find next backslash, if any. */ + char *backslash = std::find(p, end, '\\'); + if (backslash == end) + break; + /* Skip over possible whitespace right after it. */ + p = backslash + 1; + while (p < end && is_whitespace(*p) && *p != '\n') + ++p; + /* If then we have a newline, turn both backslash + * and the newline into regular spaces. */ + if (p < end && *p == '\n') { + *backslash = ' '; + *p = ' '; + } + } } const char *drop_whitespace(const char *p, const char *end) diff --git a/source/blender/io/wavefront_obj/importer/obj_import_string_utils.hh b/source/blender/io/wavefront_obj/importer/obj_import_string_utils.hh index f6dd1a6b675..e42f5080d25 100644 --- a/source/blender/io/wavefront_obj/importer/obj_import_string_utils.hh +++ b/source/blender/io/wavefront_obj/importer/obj_import_string_utils.hh @@ -6,9 +6,6 @@ /* * Various text parsing utilities used by OBJ importer. - * The utilities are not directly usable by other formats, since - * they treat backslash (\) as a whitespace character (OBJ format - * allows backslashes to function as a line-continuation character). * * Many of these functions take two pointers (p, end) indicating * which part of a string to operate on, and return a possibly @@ -27,21 +24,22 @@ namespace blender::io::obj { * The returned line will not have '\n' characters at the end; * the `buffer` is modified to contain remaining text without * the input line. - * - * Note that backslash (\) character is treated as a line - * continuation. */ StringRef read_next_line(StringRef &buffer); +/** + * Fix up OBJ line continuations by replacing backslash (\) and the + * following newline with spaces. + */ +void fixup_line_continuations(char *p, char *end); + /** * Drop leading white-space from a string part. - * Note that backslash character is considered white-space. */ const char *drop_whitespace(const char *p, const char *end); /** * Drop leading non-white-space from a string part. - * Note that backslash character is considered white-space. */ const char *drop_non_whitespace(const char *p, const char *end); diff --git a/source/blender/io/wavefront_obj/tests/obj_import_string_utils_tests.cc b/source/blender/io/wavefront_obj/tests/obj_import_string_utils_tests.cc index e9747b437cc..dc1cfd2b449 100644 --- a/source/blender/io/wavefront_obj/tests/obj_import_string_utils_tests.cc +++ b/source/blender/io/wavefront_obj/tests/obj_import_string_utils_tests.cc @@ -10,17 +10,34 @@ namespace blender::io::obj { TEST(obj_import_string_utils, read_next_line) { - std::string str = "abc\n \n\nline with \\\ncontinuation\nCRLF ending:\r\na"; + std::string str = "abc\n \n\nline with \t spaces\nCRLF ending:\r\na"; StringRef s = str; EXPECT_STRREF_EQ("abc", read_next_line(s)); EXPECT_STRREF_EQ(" ", read_next_line(s)); EXPECT_STRREF_EQ("", read_next_line(s)); - EXPECT_STRREF_EQ("line with \\\ncontinuation", read_next_line(s)); + EXPECT_STRREF_EQ("line with \t spaces", read_next_line(s)); EXPECT_STRREF_EQ("CRLF ending:\r", read_next_line(s)); EXPECT_STRREF_EQ("a", read_next_line(s)); EXPECT_TRUE(s.is_empty()); } +TEST(obj_import_string_utils, fixup_line_continuations) +{ + const char *str = + "backslash \\\n eol\n" + "backslash spaces \\ \n eol\n" + "without eol \\ is \\\\ \\ left intact\n" + "\\"; + const char *exp = + "backslash eol\n" + "backslash spaces eol\n" + "without eol \\ is \\\\ \\ left intact\n" + "\\"; + std::string buf(str); + fixup_line_continuations(buf.data(), buf.data() + buf.size()); + EXPECT_STRREF_EQ(exp, buf); +} + static StringRef drop_whitespace(StringRef s) { return StringRef(drop_whitespace(s.begin(), s.end()), s.end()); @@ -54,7 +71,7 @@ TEST(obj_import_string_utils, drop_whitespace) /* No leading whitespace */ EXPECT_STRREF_EQ("c", drop_whitespace("c")); /* Case with backslash, should be treated as whitespace */ - EXPECT_STRREF_EQ("d", drop_whitespace(" \\ d")); + EXPECT_STRREF_EQ("d", drop_whitespace(" \t d")); } TEST(obj_import_string_utils, parse_int_valid) -- cgit v1.2.3 From fad857f47319166d3ff97029385b50059731a576 Mon Sep 17 00:00:00 2001 From: Aras Pranckevicius Date: Sun, 10 Jul 2022 20:09:29 +0300 Subject: Fix T99532: New OBJ importer in some cases fails to import faces The importer code was written under incorrect assumption that vertex data (v, vn, vt commands etc.) are grouped by object, i.e. follow the o command, and that each object has its own vertex data commands. This is not the case -- all the vertex data in the whole OBJ file is "global", with no relation to any objects/groups; it's just that the faces belong to the object, and then they pull in any vertices they like. This patch fixes this incorrect assumption in the importer: - Vertex data is now properly global; no need to track some sort of "offsets" per object like it was doing before. - For each object, face definitions track the minimum & maximum vertex indices referenced by the object, and then all that vertex range is created in the final Blender object. Note: it might be (unusual, but possible) that an object does not reference a sequential range of vertices, e.g. just a single face with vertex indices 1, 10, 100 -- the resulting Blender mesh will have all the 100 vertices (some "loose" without belonging to a face). It should be possible to track the used vertices exactly (e.g. with a vector set), but I haven't done that for performance reasons. Reviewed By: Howard Trickey Differential Revision: https://developer.blender.org/D15410 --- .../importer/obj_import_file_reader.cc | 106 +++++++++---------- .../io/wavefront_obj/importer/obj_import_mesh.cc | 57 +++++----- .../wavefront_obj/importer/obj_import_objects.hh | 52 +++++----- .../io/wavefront_obj/tests/obj_importer_tests.cc | 115 +++++++++++++++------ 4 files changed, 190 insertions(+), 140 deletions(-) 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 6eb1bbc51f3..a32fd90594d 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 @@ -22,32 +22,24 @@ using std::string; /** * Based on the properties of the given Geometry instance, create a new Geometry instance * or return the previous one. - * - * Also update index offsets which should always happen if a new Geometry instance is created. */ static Geometry *create_geometry(Geometry *const prev_geometry, const eGeometryType new_type, StringRef name, - const GlobalVertices &global_vertices, - Vector> &r_all_geometries, - VertexIndexOffset &r_offset) + Vector> &r_all_geometries) { auto new_geometry = [&]() { r_all_geometries.append(std::make_unique()); Geometry *g = r_all_geometries.last().get(); g->geom_type_ = new_type; g->geometry_name_ = name.is_empty() ? "New object" : name; - g->vertex_start_ = global_vertices.vertices.size(); - g->vertex_color_start_ = global_vertices.vertex_colors.size(); - r_offset.set_index_offset(g->vertex_start_); return g; }; if (prev_geometry && prev_geometry->geom_type_ == GEOM_MESH) { /* After the creation of a Geometry instance, at least one element has been found in the OBJ - * file that indicates that it is a mesh (basically anything but the vertex positions). */ - if (!prev_geometry->face_elements_.is_empty() || prev_geometry->has_vertex_normals_ || - !prev_geometry->edges_.is_empty()) { + * file that indicates that it is a mesh (faces or edges). */ + if (!prev_geometry->face_elements_.is_empty() || !prev_geometry->edges_.is_empty()) { return new_geometry(); } if (new_type == GEOM_MESH) { @@ -70,15 +62,11 @@ static Geometry *create_geometry(Geometry *const prev_geometry, return new_geometry(); } -static void geom_add_vertex(Geometry *geom, - const char *p, - const char *end, - GlobalVertices &r_global_vertices) +static void geom_add_vertex(const char *p, const char *end, GlobalVertices &r_global_vertices) { float3 vert; p = parse_floats(p, end, 0.0f, vert, 3); r_global_vertices.vertices.append(vert); - geom->vertex_count_++; /* OBJ extension: `xyzrgb` vertex colors, when the vertex position * is followed by 3 more RGB color components. See * http://paulbourke.net/dataformats/obj/colour.html */ @@ -88,16 +76,22 @@ static void geom_add_vertex(Geometry *geom, if (srgb.x >= 0 && srgb.y >= 0 && srgb.z >= 0) { float3 linear; srgb_to_linearrgb_v3_v3(linear, srgb); - r_global_vertices.vertex_colors.append(linear); - geom->vertex_color_count_++; + + auto &blocks = r_global_vertices.vertex_colors; + /* If we don't have vertex colors yet, or the previous vertex + * was without color, we need to start a new vertex colors block. */ + if (blocks.is_empty() || (blocks.last().start_vertex_index + blocks.last().colors.size() != + r_global_vertices.vertices.size() - 1)) { + GlobalVertices::VertexColorsBlock block; + block.start_vertex_index = r_global_vertices.vertices.size() - 1; + blocks.append(block); + } + blocks.last().colors.append(linear); } } } -static void geom_add_mrgb_colors(Geometry *geom, - const char *p, - const char *end, - GlobalVertices &r_global_vertices) +static void geom_add_mrgb_colors(const char *p, const char *end, GlobalVertices &r_global_vertices) { /* MRGB color extension, in the form of * "#MRGB MMRRGGBBMMRRGGBB ..." @@ -117,14 +111,26 @@ static void geom_add_mrgb_colors(Geometry *geom, srgb[3] = 0xFF; float linear[4]; srgb_to_linearrgb_uchar4(linear, srgb); - r_global_vertices.vertex_colors.append({linear[0], linear[1], linear[2]}); - geom->vertex_color_count_++; + + auto &blocks = r_global_vertices.vertex_colors; + /* If we don't have vertex colors yet, or the previous vertex + * was without color, we need to start a new vertex colors block. */ + if (blocks.is_empty() || (blocks.last().start_vertex_index + blocks.last().colors.size() != + r_global_vertices.vertices.size())) { + GlobalVertices::VertexColorsBlock block; + block.start_vertex_index = r_global_vertices.vertices.size(); + blocks.append(block); + } + blocks.last().colors.append({linear[0], linear[1], linear[2]}); + /* MRGB colors are specified after vertex positions; each new color + * "pushes" the vertex colors block further back into which vertices it is for. */ + blocks.last().start_vertex_index--; + p += mrgb_length; } } -static void geom_add_vertex_normal(Geometry *geom, - const char *p, +static void geom_add_vertex_normal(const char *p, const char *end, GlobalVertices &r_global_vertices) { @@ -135,7 +141,6 @@ static void geom_add_vertex_normal(Geometry *geom, * normalized. */ normalize_v3(normal); r_global_vertices.vertex_normals.append(normal); - geom->has_vertex_normals_ = true; } static void geom_add_uv_vertex(const char *p, const char *end, GlobalVertices &r_global_vertices) @@ -148,24 +153,24 @@ static void geom_add_uv_vertex(const char *p, const char *end, GlobalVertices &r static void geom_add_edge(Geometry *geom, const char *p, const char *end, - const VertexIndexOffset &offsets, GlobalVertices &r_global_vertices) { int edge_v1, edge_v2; p = parse_int(p, end, -1, edge_v1); p = parse_int(p, end, -1, edge_v2); /* Always keep stored indices non-negative and zero-based. */ - edge_v1 += edge_v1 < 0 ? r_global_vertices.vertices.size() : -offsets.get_index_offset() - 1; - edge_v2 += edge_v2 < 0 ? r_global_vertices.vertices.size() : -offsets.get_index_offset() - 1; + edge_v1 += edge_v1 < 0 ? r_global_vertices.vertices.size() : -1; + edge_v2 += edge_v2 < 0 ? r_global_vertices.vertices.size() : -1; BLI_assert(edge_v1 >= 0 && edge_v2 >= 0); geom->edges_.append({static_cast(edge_v1), static_cast(edge_v2)}); + geom->track_vertex_index(edge_v1); + geom->track_vertex_index(edge_v2); } static void geom_add_polygon(Geometry *geom, const char *p, const char *end, const GlobalVertices &global_vertices, - const VertexIndexOffset &offsets, const int material_index, const int group_index, const bool shaded_smooth) @@ -204,8 +209,7 @@ static void geom_add_polygon(Geometry *geom, } } /* Always keep stored indices non-negative and zero-based. */ - corner.vert_index += corner.vert_index < 0 ? global_vertices.vertices.size() : - -offsets.get_index_offset() - 1; + corner.vert_index += corner.vert_index < 0 ? global_vertices.vertices.size() : -1; if (corner.vert_index < 0 || corner.vert_index >= global_vertices.vertices.size()) { fprintf(stderr, "Invalid vertex index %i (valid range [0, %zu)), ignoring face\n", @@ -213,6 +217,9 @@ static void geom_add_polygon(Geometry *geom, (size_t)global_vertices.vertices.size()); face_valid = false; } + else { + geom->track_vertex_index(corner.vert_index); + } if (got_uv) { corner.uv_vert_index += corner.uv_vert_index < 0 ? global_vertices.uv_vertices.size() : -1; if (corner.uv_vert_index < 0 || corner.uv_vert_index >= global_vertices.uv_vertices.size()) { @@ -226,7 +233,7 @@ static void geom_add_polygon(Geometry *geom, /* Ignore corner normal index, if the geometry does not have any normals. * Some obj files out there do have face definitions that refer to normal indices, * without any normals being present (T98782). */ - if (got_normal && geom->has_vertex_normals_) { + if (got_normal && !global_vertices.vertex_normals.is_empty()) { corner.vertex_normal_index += corner.vertex_normal_index < 0 ? global_vertices.vertex_normals.size() : -1; @@ -260,9 +267,7 @@ static void geom_add_polygon(Geometry *geom, static Geometry *geom_set_curve_type(Geometry *geom, const char *p, const char *end, - const GlobalVertices &global_vertices, const StringRef group_name, - VertexIndexOffset &r_offsets, Vector> &r_all_geometries) { p = drop_whitespace(p, end); @@ -270,8 +275,7 @@ static Geometry *geom_set_curve_type(Geometry *geom, std::cerr << "Curve type not supported: '" << std::string(p, end) << "'" << std::endl; return geom; } - geom = create_geometry( - geom, GEOM_CURVE, group_name, global_vertices, r_all_geometries, r_offsets); + geom = create_geometry(geom, GEOM_CURVE, group_name, r_all_geometries); geom->nurbs_element_.group_ = group_name; return geom; } @@ -402,9 +406,7 @@ void OBJParser::parse(Vector> &r_all_geometries, BLI_strncpy(ob_name, BLI_path_basename(import_params_.filepath), FILE_MAXFILE); BLI_path_extension_replace(ob_name, FILE_MAXFILE, ""); - VertexIndexOffset offsets; - Geometry *curr_geom = create_geometry( - nullptr, GEOM_MESH, ob_name, r_global_vertices, r_all_geometries, offsets); + Geometry *curr_geom = create_geometry(nullptr, GEOM_MESH, ob_name, r_all_geometries); /* State variables: once set, they remain the same for the remaining * elements in the object. */ @@ -477,10 +479,10 @@ void OBJParser::parse(Vector> &r_all_geometries, /* Most common things that start with 'v': vertices, normals, UVs. */ if (*p == 'v') { if (parse_keyword(p, end, "v")) { - geom_add_vertex(curr_geom, p, end, r_global_vertices); + geom_add_vertex(p, end, r_global_vertices); } else if (parse_keyword(p, end, "vn")) { - geom_add_vertex_normal(curr_geom, p, end, r_global_vertices); + geom_add_vertex_normal(p, end, r_global_vertices); } else if (parse_keyword(p, end, "vt")) { geom_add_uv_vertex(p, end, r_global_vertices); @@ -492,26 +494,21 @@ void OBJParser::parse(Vector> &r_all_geometries, p, end, r_global_vertices, - offsets, state_material_index, - state_group_index, /* TODO was wrongly material name! */ + state_group_index, state_shaded_smooth); } /* Faces. */ else if (parse_keyword(p, end, "l")) { - geom_add_edge(curr_geom, p, end, offsets, r_global_vertices); + geom_add_edge(curr_geom, p, end, r_global_vertices); } /* Objects. */ else if (parse_keyword(p, end, "o")) { state_shaded_smooth = false; state_group_name = ""; state_material_name = ""; - curr_geom = create_geometry(curr_geom, - GEOM_MESH, - StringRef(p, end).trim(), - r_global_vertices, - r_all_geometries, - offsets); + curr_geom = create_geometry( + curr_geom, GEOM_MESH, StringRef(p, end).trim(), r_all_geometries); } /* Groups. */ else if (parse_keyword(p, end, "g")) { @@ -540,7 +537,7 @@ void OBJParser::parse(Vector> &r_all_geometries, add_mtl_library(StringRef(p, end).trim()); } else if (parse_keyword(p, end, "#MRGB")) { - geom_add_mrgb_colors(curr_geom, p, end, r_global_vertices); + geom_add_mrgb_colors(p, end, r_global_vertices); } /* Comments. */ else if (*p == '#') { @@ -548,8 +545,7 @@ void OBJParser::parse(Vector> &r_all_geometries, } /* Curve related things. */ else if (parse_keyword(p, end, "cstype")) { - curr_geom = geom_set_curve_type( - curr_geom, p, end, r_global_vertices, state_group_name, offsets, r_all_geometries); + curr_geom = geom_set_curve_type(curr_geom, p, end, state_group_name, r_all_geometries); } else if (parse_keyword(p, end, "deg")) { geom_set_curve_degree(curr_geom, p, end); 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 8cef0700b3b..01f05466b3a 100644 --- a/source/blender/io/wavefront_obj/importer/obj_import_mesh.cc +++ b/source/blender/io/wavefront_obj/importer/obj_import_mesh.cc @@ -31,13 +31,17 @@ Object *MeshFromGeometry::create_mesh(Main *bmain, Map &created_materials, const OBJImportParams &import_params) { + const int64_t tot_verts_object{mesh_geometry_.get_vertex_count()}; + if (tot_verts_object <= 0) { + /* Empty mesh */ + return nullptr; + } std::string ob_name{mesh_geometry_.geometry_name_}; if (ob_name.empty()) { ob_name = "Untitled"; } fixup_invalid_faces(); - const int64_t tot_verts_object{mesh_geometry_.vertex_count_}; /* Total explicitly imported edges, not the ones belonging the polygons to be created. */ const int64_t tot_edges{mesh_geometry_.edges_.size()}; const int64_t tot_face_elems{mesh_geometry_.face_elements_.size()}; @@ -153,9 +157,9 @@ void MeshFromGeometry::fixup_invalid_faces() void MeshFromGeometry::create_vertices(Mesh *mesh) { - const int tot_verts_object{mesh_geometry_.vertex_count_}; + const int tot_verts_object{mesh_geometry_.get_vertex_count()}; for (int i = 0; i < tot_verts_object; ++i) { - int vi = mesh_geometry_.vertex_start_ + i; + int vi = mesh_geometry_.vertex_index_min_ + i; if (vi < global_vertices_.vertices.size()) { copy_v3_v3(mesh->mvert[i].co, global_vertices_.vertices[vi]); } @@ -170,7 +174,7 @@ void MeshFromGeometry::create_vertices(Mesh *mesh) void MeshFromGeometry::create_polys_loops(Mesh *mesh, bool use_vertex_groups) { mesh->dvert = nullptr; - const int64_t total_verts = mesh_geometry_.vertex_count_; + const int64_t total_verts = mesh_geometry_.get_vertex_count(); if (use_vertex_groups && total_verts && mesh_geometry_.has_vertex_groups_) { mesh->dvert = static_cast( CustomData_add_layer(&mesh->vdata, CD_MDEFORMVERT, CD_CALLOC, nullptr, total_verts)); @@ -204,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; + mloop.v = curr_corner.vert_index - mesh_geometry_.vertex_index_min_; /* Setup vertex group data, if needed. */ if (!mesh->dvert) { @@ -231,14 +235,14 @@ void MeshFromGeometry::create_vertex_groups(Object *obj) void MeshFromGeometry::create_edges(Mesh *mesh) { const int64_t tot_edges{mesh_geometry_.edges_.size()}; - const int64_t total_verts{mesh_geometry_.vertex_count_}; + const int64_t total_verts{mesh_geometry_.get_vertex_count()}; UNUSED_VARS_NDEBUG(total_verts); for (int i = 0; i < tot_edges; ++i) { const MEdge &src_edge = mesh_geometry_.edges_[i]; MEdge &dst_edge = mesh->medge[i]; - BLI_assert(src_edge.v1 < total_verts && src_edge.v2 < total_verts); - dst_edge.v1 = src_edge.v1; - dst_edge.v2 = src_edge.v2; + dst_edge.v1 = src_edge.v1 - mesh_geometry_.vertex_index_min_; + dst_edge.v2 = src_edge.v2 - mesh_geometry_.vertex_index_min_; + BLI_assert(dst_edge.v1 < total_verts && dst_edge.v2 < total_verts); dst_edge.flag = ME_LOOSEEDGE; } @@ -312,10 +316,8 @@ void MeshFromGeometry::create_materials(Main *bmain, void MeshFromGeometry::create_normals(Mesh *mesh) { - /* NOTE: Needs more clarity about what is expected in the viewport if the function works. */ - /* No normal data: nothing to do. */ - if (global_vertices_.vertex_normals.is_empty() || !mesh_geometry_.has_vertex_normals_) { + if (global_vertices_.vertex_normals.is_empty()) { return; } @@ -341,23 +343,26 @@ void MeshFromGeometry::create_normals(Mesh *mesh) void MeshFromGeometry::create_colors(Mesh *mesh) { - /* Nothing to do if we don't have vertex colors. */ - if (mesh_geometry_.vertex_color_count_ < 1) { - return; - } - if (mesh_geometry_.vertex_color_count_ != mesh_geometry_.vertex_count_) { - std::cerr << "Mismatching number of vertices (" << mesh_geometry_.vertex_count_ - << ") and colors (" << mesh_geometry_.vertex_color_count_ << ") on object '" - << mesh_geometry_.geometry_name_ << "', ignoring colors." << std::endl; + /* Nothing to do if we don't have vertex colors at all. */ + if (global_vertices_.vertex_colors.is_empty()) { return; } - CustomDataLayer *color_layer = BKE_id_attribute_new( - &mesh->id, "Color", CD_PROP_COLOR, ATTR_DOMAIN_POINT, nullptr); - float4 *colors = (float4 *)color_layer->data; - for (int i = 0; i < mesh_geometry_.vertex_color_count_; ++i) { - float3 c = global_vertices_.vertex_colors[mesh_geometry_.vertex_color_start_ + i]; - colors[i] = float4(c.x, c.y, c.z, 1.0f); + /* Find which vertex color block is for this mesh (if any). */ + for (const auto &block : global_vertices_.vertex_colors) { + if (mesh_geometry_.vertex_index_min_ >= block.start_vertex_index && + mesh_geometry_.vertex_index_max_ < block.start_vertex_index + block.colors.size()) { + /* This block is suitable, use colors from it. */ + CustomDataLayer *color_layer = BKE_id_attribute_new( + &mesh->id, "Color", CD_PROP_COLOR, ATTR_DOMAIN_POINT, nullptr); + float4 *colors = (float4 *)color_layer->data; + int offset = mesh_geometry_.vertex_index_min_ - block.start_vertex_index; + for (int i = 0, n = mesh_geometry_.get_vertex_count(); i != n; ++i) { + float3 c = block.colors[offset + i]; + colors[i] = float4(c.x, c.y, c.z, 1.0f); + } + return; + } } } 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 3d6733d661e..76436315465 100644 --- a/source/blender/io/wavefront_obj/importer/obj_import_objects.hh +++ b/source/blender/io/wavefront_obj/importer/obj_import_objects.hh @@ -19,35 +19,22 @@ namespace blender::io::obj { /** - * List of all vertex and UV vertex coordinates in an OBJ file accessible to any - * Geometry instance at any time. + * All vertex positions, normals, UVs, colors in the OBJ file. */ struct GlobalVertices { Vector vertices; Vector uv_vertices; Vector vertex_normals; - Vector vertex_colors; -}; - -/** - * Keeps track of the vertices that belong to other Geometries. - * Needed only for MLoop.v and MEdge.v1 which needs vertex indices ranging from (0 to total - * vertices in the mesh) as opposed to the other OBJ indices ranging from (0 to total vertices - * in the global list). - */ -struct VertexIndexOffset { - private: - int offset_ = 0; - public: - void set_index_offset(const int64_t total_vertices) - { - offset_ = total_vertices; - } - int64_t get_index_offset() const - { - return offset_; - } + /* Vertex colors might not be present in the file at all, or only + * provided for some meshes. Store them in chunks as they are + * spelled out in the file, e.g. if there are 10 vertices in sequence, all + * with "xyzrgb" colors, they will be one block. */ + struct VertexColorsBlock { + Vector colors; + int start_vertex_index; + }; + Vector vertex_colors; }; /** @@ -101,10 +88,8 @@ struct Geometry { Map material_indices_; Vector material_order_; - int vertex_start_ = 0; - int vertex_count_ = 0; - int vertex_color_start_ = 0; - int vertex_color_count_ = 0; + int vertex_index_min_ = INT_MAX; + int vertex_index_max_ = -1; /** Edges written in the file in addition to (or even without polygon) elements. */ Vector edges_; @@ -112,10 +97,21 @@ struct Geometry { Vector face_elements_; bool has_invalid_polys_ = false; - bool has_vertex_normals_ = false; bool has_vertex_groups_ = false; NurbsElement nurbs_element_; int total_loops_ = 0; + + int get_vertex_count() const + { + if (vertex_index_max_ < vertex_index_min_) + return 0; + return vertex_index_max_ - vertex_index_min_ + 1; + } + void track_vertex_index(int index) + { + vertex_index_min_ = std::min(vertex_index_min_, index); + vertex_index_max_ = std::max(vertex_index_max_, index); + } }; } // namespace blender::io::obj 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 eeb81f5e23e..02565556c37 100644 --- a/source/blender/io/wavefront_obj/tests/obj_importer_tests.cc +++ b/source/blender/io/wavefront_obj/tests/obj_importer_tests.cc @@ -158,6 +158,36 @@ TEST_F(obj_importer_test, import_cube) import_and_check("cube.obj", expect, std::size(expect), 1); } +TEST_F(obj_importer_test, import_cube_o_after_verts) +{ + Expectation expect[] = { + {"OBCube", OB_MESH, 8, 12, 6, 24, float3(1, 1, -1), float3(-1, 1, 1)}, + { + "OBActualCube", + OB_MESH, + 8, + 12, + 6, + 24, + float3(-1, -1, 1), + float3(1, -1, -1), + float3(0, 0, 1), + }, + { + "OBSparseTri", + OB_MESH, + 6, + 3, + 1, + 3, + float3(1, -1, 1), + float3(-2, -2, 2), + float3(-0.2357f, 0.9428f, 0.2357f), + }, + }; + import_and_check("cube_o_after_verts.obj", expect, std::size(expect), 2); +} + TEST_F(obj_importer_test, import_suzanne_all_data) { Expectation expect[] = { @@ -293,13 +323,13 @@ TEST_F(obj_importer_test, import_faces_invalid_or_with_holes) float3(1, 0, -1)}, {"OBFaceQuadDupSomeVerts_BecomesOneQuadUsing4Verts", OB_MESH, - 8, + 4, 4, 1, 4, float3(3, 0, -2), - float3(6, 0, -1)}, - {"OBFaceTriDupVert_Becomes1Tri", OB_MESH, 8, 3, 1, 3, float3(-2, 0, 3), float3(1, 0, 4)}, + float3(7, 0, -2)}, + {"OBFaceTriDupVert_Becomes1Tri", OB_MESH, 3, 3, 1, 3, float3(-2, 0, 3), float3(2, 0, 7)}, {"OBFaceAllVertsDup_BecomesOneOverlappingFaceUsingAllVerts", OB_MESH, 8, @@ -316,7 +346,7 @@ TEST_F(obj_importer_test, import_faces_invalid_or_with_holes) 8, float3(8, 0, -2), float3(11, 0, -1)}, - {"OBFaceJustTwoVerts_IsSkipped", OB_MESH, 8, 0, 0, 0, float3(8, 0, 3), float3(11, 0, 4)}, + {"OBFaceJustTwoVerts_IsSkipped", OB_MESH, 2, 0, 0, 0, float3(8, 0, 3), float3(8, 0, 7)}, }; import_and_check("faces_invalid_or_with_holes.obj", expect, std::size(expect), 0); } @@ -327,12 +357,12 @@ TEST_F(obj_importer_test, import_invalid_indices) {"OBCube", OB_MESH, 8, 12, 6, 24, float3(1, 1, -1), float3(-1, 1, 1)}, {"OBQuad", OB_MESH, - 4, + 3, 3, 1, 3, float3(-2, 0, -2), - float3(2, 0, -2), + float3(2, 0, 2), float3(0, 1, 0), float2(0.5f, 0.25f)}, }; @@ -345,12 +375,12 @@ TEST_F(obj_importer_test, import_invalid_syntax) {"OBCube", OB_MESH, 8, 12, 6, 24, float3(1, 1, -1), float3(-1, 1, 1)}, {"OBObjectWithAReallyLongNameToCheckHowImportHandlesNamesThatAreLon", OB_MESH, - 10, /* NOTE: right now parses some invalid obj syntax as valid vertices. */ + 3, 3, 1, 3, float3(1, 2, 3), - float3(10, 11, 12), + float3(7, 8, 9), float3(0, 1, 0), float2(0.5f, 0.25f)}, }; @@ -585,29 +615,52 @@ TEST_F(obj_importer_test, import_cubes_vertex_colors) TEST_F(obj_importer_test, import_cubes_vertex_colors_mrgb) { - Expectation expect[] = {{"OBCube", OB_MESH, 8, 12, 6, 24, float3(1, 1, -1), float3(-1, 1, 1)}, - {"OBCubeXYZRGB", - OB_MESH, - 8, - 12, - 6, - 24, - float3(1, 1, -1), - float3(-1, -1, 1), - float3(0, 0, 0), - float2(0, 0), - float4(0.6038f, 0.3185f, 0.1329f, 1.0f)}, - {"OBCubeMRGB", - OB_MESH, - 8, - 12, - 6, - 24, - float3(4, 1, -1), - float3(2, -1, 1), - float3(0, 0, 0), - float2(0, 0), - float4(0.8714f, 0.6308f, 0.5271f, 1.0f)}}; + Expectation expect[] = { + {"OBCube", OB_MESH, 8, 12, 6, 24, float3(1, 1, -1), float3(-1, 1, 1)}, + {"OBCubeXYZRGB", + OB_MESH, + 8, + 12, + 6, + 24, + float3(1, 1, -1), + float3(-1, -1, 1), + float3(0, 0, 0), + float2(0, 0), + float4(0.6038f, 0.3185f, 0.1329f, 1.0f)}, + {"OBCubeMRGB", + OB_MESH, + 8, + 12, + 6, + 24, + float3(4, 1, -1), + float3(2, -1, 1), + float3(0, 0, 0), + float2(0, 0), + float4(0.8714f, 0.6308f, 0.5271f, 1.0f)}, + { + "OBTriNoColors", + OB_MESH, + 3, + 3, + 1, + 3, + float3(8, 1, -1), + float3(6, 0, -1), + }, + {"OBTriMRGB", + OB_MESH, + 3, + 3, + 1, + 3, + float3(12, 1, -1), + float3(10, 0, -1), + float3(0, 0, 0), + float2(0, 0), + float4(1.0f, 0.0f, 0.0f, 1.0f)}, + }; import_and_check("cubes_vertex_colors_mrgb.obj", expect, std::size(expect), 0); } -- cgit v1.2.3 From 65432901162c0dff124d55a04875050fd0f1ac22 Mon Sep 17 00:00:00 2001 From: Howard Trickey Date: Sun, 10 Jul 2022 14:50:17 -0400 Subject: Fix an assert trip in boolean tickled by D11272 example. The face merging code in exact boolean made an assumption that the tesselated original face was manifold except at the boundaries. This should be true but sometimes (e.g., if the input faces have self-intersection, as happens in the example), it is not. This commit makes face merging tolerant of such a situation. It might leave some stray edges from triangulation, but it should only happen if the input is malformed. Note: the input may be malformed if there were previous booleans in the stack, since snapping the exact result to float coordinates is not guaranteed to leave the mesh without defects. --- source/blender/blenlib/intern/mesh_boolean.cc | 32 +++++++++++++++++++++++---- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/source/blender/blenlib/intern/mesh_boolean.cc b/source/blender/blenlib/intern/mesh_boolean.cc index 700c126ca4c..464b8f4139e 100644 --- a/source/blender/blenlib/intern/mesh_boolean.cc +++ b/source/blender/blenlib/intern/mesh_boolean.cc @@ -2966,6 +2966,11 @@ static std::ostream &operator<<(std::ostream &os, const FaceMergeState &fms) * \a tris all have the same original face. * Find the 2d edge/triangle topology for these triangles, but only the ones facing in the * norm direction, and whether each edge is dissolvable or not. + * If we did the initial triangulation properly, and any Delaunay triangulations of interections + * properly, then each triangle edge should have at most one neighbor. + * However, there can be anonalies. For example, if an input face is self-intersecting, we fall + * back on the floating poing polyfill triangulation, which, after which all bets are off. + * Hence, try to be tolerant of such unexpected topology. */ static void init_face_merge_state(FaceMergeState *fms, const Vector &tris, @@ -3053,16 +3058,35 @@ static void init_face_merge_state(FaceMergeState *fms, std::cout << "me.v1 == mf.vert[i] so set edge[" << me_index << "].left_face = " << f << "\n"; } - BLI_assert(me.left_face == -1); - fms->edge[me_index].left_face = f; + if (me.left_face != 1) { + /* Unexpected in the normal case: this means more than one triangle shares this + * edge in the same orientation. But be tolerant of this case. By making this + * edge not dissolvable, we'll avoid future problems due to this non-manifold topology. + */ + if (dbg_level > 1) { + std::cout << "me.left_face was already occupied, so triangulation wasn't good\n"; + } + me.dissolvable = false; + } + else { + fms->edge[me_index].left_face = f; + } } else { if (dbg_level > 1) { std::cout << "me.v1 != mf.vert[i] so set edge[" << me_index << "].right_face = " << f << "\n"; } - BLI_assert(me.right_face == -1); - fms->edge[me_index].right_face = f; + if (me.right_face != -1) { + /* Unexpected, analogous to the me.left_face != -1 case above. */ + if (dbg_level > 1) { + std::cout << "me.right_face was already occupied, so triangulation wasn't good\n"; + } + me.dissolvable = false; + } + else { + fms->edge[me_index].right_face = f; + } } fms->face[f].edge.append(me_index); } -- cgit v1.2.3 From 7f4ee97b9ef93b0c8f9fc89d47ee8535ab665327 Mon Sep 17 00:00:00 2001 From: Howard Trickey Date: Sun, 10 Jul 2022 18:50:11 -0400 Subject: Revert "Fix an assert trip in boolean tickled by D11272 example." This reverts commit 65432901162c0dff124d55a04875050fd0f1ac22. It broke tests and I don't know why, so reverting this while figuring that out. --- source/blender/blenlib/intern/mesh_boolean.cc | 32 ++++----------------------- 1 file changed, 4 insertions(+), 28 deletions(-) diff --git a/source/blender/blenlib/intern/mesh_boolean.cc b/source/blender/blenlib/intern/mesh_boolean.cc index 464b8f4139e..700c126ca4c 100644 --- a/source/blender/blenlib/intern/mesh_boolean.cc +++ b/source/blender/blenlib/intern/mesh_boolean.cc @@ -2966,11 +2966,6 @@ static std::ostream &operator<<(std::ostream &os, const FaceMergeState &fms) * \a tris all have the same original face. * Find the 2d edge/triangle topology for these triangles, but only the ones facing in the * norm direction, and whether each edge is dissolvable or not. - * If we did the initial triangulation properly, and any Delaunay triangulations of interections - * properly, then each triangle edge should have at most one neighbor. - * However, there can be anonalies. For example, if an input face is self-intersecting, we fall - * back on the floating poing polyfill triangulation, which, after which all bets are off. - * Hence, try to be tolerant of such unexpected topology. */ static void init_face_merge_state(FaceMergeState *fms, const Vector &tris, @@ -3058,35 +3053,16 @@ static void init_face_merge_state(FaceMergeState *fms, std::cout << "me.v1 == mf.vert[i] so set edge[" << me_index << "].left_face = " << f << "\n"; } - if (me.left_face != 1) { - /* Unexpected in the normal case: this means more than one triangle shares this - * edge in the same orientation. But be tolerant of this case. By making this - * edge not dissolvable, we'll avoid future problems due to this non-manifold topology. - */ - if (dbg_level > 1) { - std::cout << "me.left_face was already occupied, so triangulation wasn't good\n"; - } - me.dissolvable = false; - } - else { - fms->edge[me_index].left_face = f; - } + BLI_assert(me.left_face == -1); + fms->edge[me_index].left_face = f; } else { if (dbg_level > 1) { std::cout << "me.v1 != mf.vert[i] so set edge[" << me_index << "].right_face = " << f << "\n"; } - if (me.right_face != -1) { - /* Unexpected, analogous to the me.left_face != -1 case above. */ - if (dbg_level > 1) { - std::cout << "me.right_face was already occupied, so triangulation wasn't good\n"; - } - me.dissolvable = false; - } - else { - fms->edge[me_index].right_face = f; - } + BLI_assert(me.right_face == -1); + fms->edge[me_index].right_face = f; } fms->face[f].edge.append(me_index); } -- cgit v1.2.3 From a83502f05f017fc4ad5bd910aff32fa457ad6702 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Mon, 11 Jul 2022 10:38:02 +1000 Subject: Cleanup: remove unused GHOST function getAnyModifiedState. Remove unused GHOST_WindowManager::getAnyModifiedState() --- intern/ghost/intern/GHOST_WindowManager.cpp | 14 -------------- intern/ghost/intern/GHOST_WindowManager.h | 6 ------ 2 files changed, 20 deletions(-) diff --git a/intern/ghost/intern/GHOST_WindowManager.cpp b/intern/ghost/intern/GHOST_WindowManager.cpp index 19684a44169..e8785cbdb24 100644 --- a/intern/ghost/intern/GHOST_WindowManager.cpp +++ b/intern/ghost/intern/GHOST_WindowManager.cpp @@ -162,17 +162,3 @@ GHOST_IWindow *GHOST_WindowManager::getWindowAssociatedWithOSWindow(void *osWind } return nullptr; } - -bool GHOST_WindowManager::getAnyModifiedState() -{ - bool isAnyModified = false; - std::vector::iterator iter; - - for (iter = m_windows.begin(); iter != m_windows.end(); ++iter) { - if ((*iter)->getModifiedState()) { - isAnyModified = true; - } - } - - return isAnyModified; -} diff --git a/intern/ghost/intern/GHOST_WindowManager.h b/intern/ghost/intern/GHOST_WindowManager.h index 9d20413c433..bf7a0f4ec61 100644 --- a/intern/ghost/intern/GHOST_WindowManager.h +++ b/intern/ghost/intern/GHOST_WindowManager.h @@ -109,12 +109,6 @@ class GHOST_WindowManager { */ GHOST_IWindow *getWindowAssociatedWithOSWindow(void *osWindow); - /** - * Return true if any windows has a modified status - * \return True if any window has unsaved changes - */ - bool getAnyModifiedState(); - protected: /** The list of windows managed */ std::vector m_windows; -- cgit v1.2.3 From d4a4691c0c395967e7e12d2405b561d1fd0b6365 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Mon, 11 Jul 2022 10:38:04 +1000 Subject: Cleanup: spelling in comments --- source/blender/blenkernel/BKE_mesh.h | 2 +- source/blender/blenloader/intern/versioning_legacy.c | 2 +- source/blender/draw/engines/eevee/eevee_sampling.c | 5 +++-- source/blender/editors/screen/screen_ops.c | 2 +- source/blender/editors/space_file/file_ops.c | 2 +- source/blender/io/wavefront_obj/importer/obj_import_objects.hh | 6 ++++-- .../nodes/geometry/nodes/node_geo_deform_curves_on_surface.cc | 2 +- 7 files changed, 12 insertions(+), 9 deletions(-) diff --git a/source/blender/blenkernel/BKE_mesh.h b/source/blender/blenkernel/BKE_mesh.h index 366083fee7f..731c9872aae 100644 --- a/source/blender/blenkernel/BKE_mesh.h +++ b/source/blender/blenkernel/BKE_mesh.h @@ -986,7 +986,7 @@ void BKE_mesh_strip_loose_edges(struct Mesh *me); /** * If the mesh is from a very old blender version, - * convert mface->edcode to edge drawflags + * convert #MFace.edcode to edge #ME_EDGEDRAW. */ void BKE_mesh_calc_edges_legacy(struct Mesh *me, bool use_old); void BKE_mesh_calc_edges_loose(struct Mesh *mesh); diff --git a/source/blender/blenloader/intern/versioning_legacy.c b/source/blender/blenloader/intern/versioning_legacy.c index a3f17878f68..75cc333e4b5 100644 --- a/source/blender/blenloader/intern/versioning_legacy.c +++ b/source/blender/blenloader/intern/versioning_legacy.c @@ -1468,7 +1468,7 @@ void blo_do_versions_pre250(FileData *fd, Library *lib, Main *bmain) for (me = bmain->meshes.first; me; me = me->id.next) { if (!me->medge) { - BKE_mesh_calc_edges_legacy(me, true); /* true = use mface->edcode */ + BKE_mesh_calc_edges_legacy(me, true); /* true = use #MFace.edcode. */ } else { BKE_mesh_strip_loose_faces(me); diff --git a/source/blender/draw/engines/eevee/eevee_sampling.c b/source/blender/draw/engines/eevee/eevee_sampling.c index a1a3e98f34f..34d3cd74b36 100644 --- a/source/blender/draw/engines/eevee/eevee_sampling.c +++ b/source/blender/draw/engines/eevee/eevee_sampling.c @@ -74,7 +74,8 @@ void EEVEE_sample_ellipse(int sample_ofs, BLI_halton_2d(ht_primes, ht_offset, sample_ofs, ht_point); - /* Decorelate AA and shadow samples. (see T68594) */ + /* Decorrelate AA and shadow samples. (see T68594) */ + ht_point[0] = fmod(ht_point[0] * 1151.0, 1.0); ht_point[1] = fmod(ht_point[1] * 1069.0, 1.0); @@ -97,7 +98,7 @@ void EEVEE_random_rotation_m4(int sample_ofs, float scale, float r_mat[4][4]) BLI_halton_3d(ht_primes, ht_offset, sample_ofs, ht_point); - /* Decorelate AA and shadow samples. (see T68594) */ + /* Decorrelate AA and shadow samples. (see T68594) */ ht_point[0] = fmod(ht_point[0] * 1151.0, 1.0); ht_point[1] = fmod(ht_point[1] * 1069.0, 1.0); ht_point[2] = fmod(ht_point[2] * 1151.0, 1.0); diff --git a/source/blender/editors/screen/screen_ops.c b/source/blender/editors/screen/screen_ops.c index c616ca2b5eb..3618b933443 100644 --- a/source/blender/editors/screen/screen_ops.c +++ b/source/blender/editors/screen/screen_ops.c @@ -4718,7 +4718,7 @@ static int screen_animation_step_invoke(bContext *C, wmOperator *UNUSED(op), con #endif } - /* since we follow drawflags, we can't send notifier but tag regions ourselves */ + /* Since we follow draw-flags, we can't send notifier but tag regions ourselves. */ if (depsgraph != NULL) { ED_update_for_newframe(bmain, depsgraph); } diff --git a/source/blender/editors/space_file/file_ops.c b/source/blender/editors/space_file/file_ops.c index 62bdd583bc1..59d9a15fbab 100644 --- a/source/blender/editors/space_file/file_ops.c +++ b/source/blender/editors/space_file/file_ops.c @@ -1793,7 +1793,7 @@ static bool file_execute(bContext *C, SpaceFile *sfile) } ED_file_change_dir(C); } - /* opening file - sends events now, so things get handled on windowqueue level */ + /* Opening file, sends events now, so things get handled on window-queue level. */ else if (sfile->op) { wmOperator *op = sfile->op; char filepath[FILE_MAX]; 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 76436315465..9f0079d7c53 100644 --- a/source/blender/io/wavefront_obj/importer/obj_import_objects.hh +++ b/source/blender/io/wavefront_obj/importer/obj_import_objects.hh @@ -26,10 +26,12 @@ struct GlobalVertices { Vector uv_vertices; Vector vertex_normals; - /* Vertex colors might not be present in the file at all, or only + /** + * Vertex colors might not be present in the file at all, or only * provided for some meshes. Store them in chunks as they are * spelled out in the file, e.g. if there are 10 vertices in sequence, all - * with "xyzrgb" colors, they will be one block. */ + * with `xyzrgb` colors, they will be one block. + */ struct VertexColorsBlock { Vector colors; int start_vertex_index; diff --git a/source/blender/nodes/geometry/nodes/node_geo_deform_curves_on_surface.cc b/source/blender/nodes/geometry/nodes/node_geo_deform_curves_on_surface.cc index ab7ddfa71f1..f08e857e9cc 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_deform_curves_on_surface.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_deform_curves_on_surface.cc @@ -136,7 +136,7 @@ static void deform_curves(CurvesGeometry &curves, * * TODO: Figure out if this can be smoothly interpolated across the surface as well. * Currently, this is a source of discontinuity in the deformation, because the vector - * changes intantly from one triangle to the next. */ + * changes instantly from one triangle to the next. */ const float3 tangent_reference_dir_old = rest_pos_1 - rest_pos_0; const float3 tangent_reference_dir_new = pos_1_new - pos_0_new; -- cgit v1.2.3 From d51bc8215f515144ae51cc5feec01b3dbc7900c4 Mon Sep 17 00:00:00 2001 From: YimingWu Date: Mon, 11 Jul 2022 10:12:49 +0800 Subject: GPencil: Dot-dash modifier rename segment bug fix. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch fixes naming and renaming issue with dot-dash modifier segment list. Before: when double clicking and exiting it would append number at the end regardless of name being changed or not. Now it works like in other areas. Authored by: Aleš Jelovčan (frogstomp) Reviewed By: YimingWu (NicksBest) Differential Revision: https://developer.blender.org/D15359 --- release/scripts/addons | 2 +- source/blender/makesrna/intern/rna_gpencil_modifier.c | 2 +- source/tools | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/release/scripts/addons b/release/scripts/addons index 403b95ef6ff..7ea2e74fc41 160000 --- a/release/scripts/addons +++ b/release/scripts/addons @@ -1 +1 @@ -Subproject commit 403b95ef6ff38918de966ed2a5843cfa3274a58b +Subproject commit 7ea2e74fc41b2eabdbf639b812082e73823b09d7 diff --git a/source/blender/makesrna/intern/rna_gpencil_modifier.c b/source/blender/makesrna/intern/rna_gpencil_modifier.c index dccf7d7a7a9..d3e1aab1ba0 100644 --- a/source/blender/makesrna/intern/rna_gpencil_modifier.c +++ b/source/blender/makesrna/intern/rna_gpencil_modifier.c @@ -774,7 +774,7 @@ static bool dash_segment_name_exists_fn(void *arg, const char *name) { const DashGpencilModifierData *dmd = (const DashGpencilModifierData *)arg; for (int i = 0; i < dmd->segments_len; i++) { - if (STREQ(dmd->segments[i].name, name)) { + if (STREQ(dmd->segments[i].name, name) && dmd->segments[i].name != name) { return true; } } diff --git a/source/tools b/source/tools index 01b4c0e4a17..da8bdd7244c 160000 --- a/source/tools +++ b/source/tools @@ -1 +1 @@ -Subproject commit 01b4c0e4a172819414229445c314be34527bf412 +Subproject commit da8bdd7244c7b6c2eadf4c949ff391d0cc430275 -- cgit v1.2.3 From 133d398120bfa5c6fe35e93b424cc86543747ccd Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Mon, 11 Jul 2022 12:43:24 +1000 Subject: PyAPI: add Matrix.is_identity read-only attribute Add a convenient way of checking if the matrix is an identity matrix. --- source/blender/python/mathutils/mathutils_Matrix.c | 23 ++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/source/blender/python/mathutils/mathutils_Matrix.c b/source/blender/python/mathutils/mathutils_Matrix.c index 8cd7a5c7d87..1e85ece124d 100644 --- a/source/blender/python/mathutils/mathutils_Matrix.c +++ b/source/blender/python/mathutils/mathutils_Matrix.c @@ -290,6 +290,18 @@ static PyObject *matrix__apply_to_copy(PyObject *(*matrix_func)(MatrixObject *), return NULL; } +static bool matrix_is_identity(MatrixObject *self) +{ + for (int row = 0; row < self->row_num; row++) { + for (int col = 0; col < self->col_num; col++) { + if (MATRIX_ITEM(self, row, col) != ((row != col) ? 0.0f : 1.0f)) { + return false; + } + } + } + return true; +} + /** \} */ /* -------------------------------------------------------------------- */ @@ -3104,6 +3116,16 @@ static PyObject *Matrix_median_scale_get(MatrixObject *self, void *UNUSED(closur return PyFloat_FromDouble(mat3_to_scale(mat)); } +PyDoc_STRVAR(Matrix_is_identity_doc, + "True if this is an identity matrix (read-only).\n\n:type: bool"); +static PyObject *Matrix_is_identity_get(MatrixObject *self, void *UNUSED(closure)) +{ + if (BaseMath_ReadCallback(self) == -1) { + return NULL; + } + return PyBool_FromLong(matrix_is_identity(self)); +} + PyDoc_STRVAR(Matrix_is_negative_doc, "True if this matrix results in a negative scale, 3x3 and 4x4 only, " "(read-only).\n\n:type: bool"); @@ -3187,6 +3209,7 @@ static PyGetSetDef Matrix_getseters[] = { NULL}, {"row", (getter)Matrix_row_get, (setter)NULL, Matrix_row_doc, NULL}, {"col", (getter)Matrix_col_get, (setter)NULL, Matrix_col_doc, NULL}, + {"is_identity", (getter)Matrix_is_identity_get, (setter)NULL, Matrix_is_identity_doc, NULL}, {"is_negative", (getter)Matrix_is_negative_get, (setter)NULL, Matrix_is_negative_doc, NULL}, {"is_orthogonal", (getter)Matrix_is_orthogonal_get, -- cgit v1.2.3 From cb39058f2f31815279ac72fce08ead7cbc979517 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Sun, 10 Jul 2022 23:39:45 -0700 Subject: Fix T94633: Sculpt mode missing check for hidden active object Note there is a bug in BKE_object_is_visible_in_viewport, it returns false when the object is in local mode. The transform operator poll should do a similar test. That would allow us to move the test from sculpt_brush_strok_invoke to SCULPT_mode_poll (at the moment we cannot do this due to the brush operator falling through to the translate keymap item in global view3d keymap). --- source/blender/editors/sculpt_paint/sculpt.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c index 906c4fd35fe..ae65ca8178b 100644 --- a/source/blender/editors/sculpt_paint/sculpt.c +++ b/source/blender/editors/sculpt_paint/sculpt.c @@ -5499,10 +5499,23 @@ static int sculpt_brush_stroke_invoke(bContext *C, wmOperator *op, const wmEvent struct PaintStroke *stroke; int ignore_background_click; int retval; + Object *ob = CTX_data_active_object(C); + + /* Test that ob is visible; otherwise we won't be able to get evaluated data + * from the depsgraph. We do this here instead of SCULPT_mode_poll + * to avoid falling through to the translate operator in the + * global view3d keymap. + * + * Note: BKE_object_is_visible_in_viewport is not working here (it returns false + * if the object is in local view); instead, test for OB_HIDE_VIEWPORT directly. + */ + + if (ob->visibility_flag & OB_HIDE_VIEWPORT) { + return OPERATOR_CANCELLED; + } sculpt_brush_stroke_init(C, op); - Object *ob = CTX_data_active_object(C); Sculpt *sd = CTX_data_tool_settings(C)->sculpt; Brush *brush = BKE_paint_brush(&sd->paint); -- cgit v1.2.3 From 7357176b5739ce9ea0d94d9ecdeaa0af7d64722e Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Mon, 11 Jul 2022 00:19:53 -0700 Subject: Fix T99383: Wrong origdata type in color filter --- source/blender/editors/sculpt_paint/sculpt_filter_color.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/editors/sculpt_paint/sculpt_filter_color.c b/source/blender/editors/sculpt_paint/sculpt_filter_color.c index a5d9f5306e2..a9186010a9f 100644 --- a/source/blender/editors/sculpt_paint/sculpt_filter_color.c +++ b/source/blender/editors/sculpt_paint/sculpt_filter_color.c @@ -93,7 +93,7 @@ static void color_filter_task_cb(void *__restrict userdata, const int mode = data->filter_type; SculptOrigVertData orig_data; - SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n], SCULPT_UNDO_COORDS); + SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n], SCULPT_UNDO_COLOR); PBVHVertexIter vd; BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { -- cgit v1.2.3 From da101118d4a5b1fe39181c4caaf78ccd4d715239 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Mon, 11 Jul 2022 11:14:21 +0200 Subject: Cleanup: Remove unused operator name storage in UI lists --- source/blender/editors/interface/interface_handlers.c | 6 +++--- source/blender/makesdna/DNA_screen_types.h | 6 ------ 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c index 7c00c4f1875..21fd14b86b7 100644 --- a/source/blender/editors/interface/interface_handlers.c +++ b/source/blender/editors/interface/interface_handlers.c @@ -9487,7 +9487,7 @@ static int ui_list_activate_hovered_row(bContext *C, } /* Simulate click on listrow button itself (which may be overlapped by another button). Also - * calls the custom activate operator (ui_list->custom_activate_opname). */ + * calls the custom activate operator (#uiListDyn::custom_activate_optype). */ UI_but_execute(C, region, listrow); ((uiList *)ui_list)->dyn_data->custom_activate_optype = custom_activate_optype; @@ -9558,13 +9558,13 @@ static void ui_list_activate_row_from_index( uiBut *new_active_row = ui_list_row_find_from_index(region, index, listbox); if (new_active_row) { /* Preferred way to update the active item, also calls the custom activate operator - * (#uiList.custom_activate_opname). */ + * (#uiListDyn::custom_activate_optype). */ UI_but_execute(C, region, new_active_row); } else { /* A bit ugly, set the active index in RNA directly. That's because a button that's * scrolled away in the list box isn't created at all. - * The custom activate operator (#uiList.custom_activate_opname) is not called in this case + * The custom activate operator (#uiListDyn::custom_activate_optype) is not called in this case * (which may need the row button context). */ RNA_property_int_set(&listbox->rnapoin, listbox->rnaprop, index); RNA_property_update(C, &listbox->rnapoin, listbox->rnaprop); diff --git a/source/blender/makesdna/DNA_screen_types.h b/source/blender/makesdna/DNA_screen_types.h index 8560f8a454e..e9178c0cbf5 100644 --- a/source/blender/makesdna/DNA_screen_types.h +++ b/source/blender/makesdna/DNA_screen_types.h @@ -305,12 +305,6 @@ typedef struct uiList { /* some list UI data need to be saved in file */ int filter_flag; int filter_sort_flag; - /** Operator executed when activating an item. */ - const char *custom_activate_opname; - /** Operator executed when dragging an item (item gets activated too, without running - * custom_activate_opname above). */ - const char *custom_drag_opname; - /* Custom sub-classes properties. */ IDProperty *properties; -- cgit v1.2.3 From 1c4c904786b55cf086a0536eda9594b0f49aefcf Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Mon, 11 Jul 2022 11:42:54 +0200 Subject: Deps Builder: Disable TermInfo and ncurses for DPC++ They are not strictly needed for compilation and disabling them makes the compiler more portable without any special trickery. This change aimed to solve problem which currently happens on the API documentation build which does not have terminfo installed, but needs to compile Cycles. Note that the DPC++ is to be re-compiled. --- build_files/build_environment/cmake/dpcpp.cmake | 2 ++ 1 file changed, 2 insertions(+) diff --git a/build_files/build_environment/cmake/dpcpp.cmake b/build_files/build_environment/cmake/dpcpp.cmake index 563bc7aeff4..3c3fe201073 100644 --- a/build_files/build_environment/cmake/dpcpp.cmake +++ b/build_files/build_environment/cmake/dpcpp.cmake @@ -63,6 +63,8 @@ set(DPCPP_EXTRA_ARGS -DPython3_ROOT_DIR=${LIBDIR}/python/ -DPython3_EXECUTABLE=${PYTHON_BINARY} -DPYTHON_EXECUTABLE=${PYTHON_BINARY} + -DLLDB_ENABLE_CURSES=OFF + -DLLVM_ENABLE_TERMINFO=OFF ) if(WIN32) -- cgit v1.2.3 From 275419f6fd4cdc06a9f840e9031d4f5153da6b0a Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Mon, 11 Jul 2022 12:46:01 +0200 Subject: Fix/Cleanup UI messages. --- release/scripts/modules/bl_i18n_utils/settings.py | 8 ++++++++ .../scripts/modules/bl_i18n_utils/utils_spell_check.py | 8 +++++++- release/scripts/startup/bl_ui/space_sequencer.py | 2 +- source/blender/editors/space_node/node_add.cc | 2 +- .../gpencil_modifiers/intern/MOD_gpencillineart.c | 2 +- source/blender/makesrna/intern/rna_gpencil_modifier.c | 2 +- source/blender/nodes/NOD_static_types.h | 2 +- .../geometry/nodes/node_geo_deform_curves_on_surface.cc | 17 ++++++++--------- 8 files changed, 28 insertions(+), 15 deletions(-) diff --git a/release/scripts/modules/bl_i18n_utils/settings.py b/release/scripts/modules/bl_i18n_utils/settings.py index 408f8523b8d..7aeef80b0bd 100644 --- a/release/scripts/modules/bl_i18n_utils/settings.py +++ b/release/scripts/modules/bl_i18n_utils/settings.py @@ -297,6 +297,7 @@ WARN_MSGID_NOT_CAPITALIZED_ALLOWED = { "ascii", "author", # Addons' field. :/ "bItasc", + "blender.org", "color_index is invalid", "cos(A)", "cosh(A)", @@ -312,6 +313,7 @@ WARN_MSGID_NOT_CAPITALIZED_ALLOWED = { "glTF 2.0 (.glb/.gltf)", "glTF Binary (.glb)", "glTF Embedded (.gltf)", + "glTF Original PBR data", "glTF Separate (.gltf + .bin + textures)", "invoke() needs to be called before execute()", "iScale", @@ -330,6 +332,7 @@ WARN_MSGID_NOT_CAPITALIZED_ALLOWED = { "mp3", "normal", "ogg", + "oneAPI", "p0", "px", "re", @@ -340,6 +343,8 @@ WARN_MSGID_NOT_CAPITALIZED_ALLOWED = { "sinh(A)", "sqrt(x*x+y*y+z*z)", "sRGB", + "sRGB display space", + "sRGB display space with Filmic view transform", "tan(A)", "tanh(A)", "utf-8", @@ -356,7 +361,9 @@ WARN_MSGID_NOT_CAPITALIZED_ALLOWED = { "all and invert unselected", "and AMD driver version 22.10 or newer", "and AMD Radeon Pro 21.Q4 driver or newer", + "and Linux driver version xx.xx.28000 or newer", "and NVIDIA driver version 470 or newer", + "and Windows driver version 101.1660 or newer", "available with", "brown fox", "can't save image while rendering", @@ -431,6 +438,7 @@ WARN_MSGID_END_POINT_ALLOWED = { "The program will now close.", "Your graphics card or driver has limited support. It may work, but with issues.", "Your graphics card or driver is not supported.", + "Invalid surface UVs on %d curves.", } PARSER_CACHE_HASH = 'sha1' diff --git a/release/scripts/modules/bl_i18n_utils/utils_spell_check.py b/release/scripts/modules/bl_i18n_utils/utils_spell_check.py index 74785c81bfd..462c954d54a 100644 --- a/release/scripts/modules/bl_i18n_utils/utils_spell_check.py +++ b/release/scripts/modules/bl_i18n_utils/utils_spell_check.py @@ -433,6 +433,7 @@ class SpellChecker: "polyline", "polylines", "probabilistically", "pulldown", "pulldowns", + "quadratically", "quantized", "quartic", "quaternion", "quaternions", @@ -501,6 +502,7 @@ class SpellChecker: "luminance", "mantaflow", "matcap", + "microfacet", "midtones", "mipmap", "mipmaps", "mip", "ngon", "ngons", @@ -508,6 +510,7 @@ class SpellChecker: "nurb", "nurbs", "perlin", "phong", + "photorealistic", "pinlight", "posterize", "qi", @@ -675,7 +678,7 @@ class SpellChecker: "ascii", "atrac", "avx", - "bsdf", + "bsdf", "bsdfs", "bssrdf", "bw", "ccd", @@ -708,14 +711,17 @@ class SpellChecker: "hdc", "hdr", "hdri", "hdris", "hh", "mm", "ss", "ff", # hh:mm:ss:ff timecode + "hpg", # Intel Xe-HPG architecture "hsv", "hsva", "hsl", "id", "ies", "ior", "itu", "jonswap", + "lfe", "lhs", "lmb", "mmb", "rmb", + "lscm", "kb", "mocap", "msgid", "msgids", diff --git a/release/scripts/startup/bl_ui/space_sequencer.py b/release/scripts/startup/bl_ui/space_sequencer.py index 060c00c0443..a99df1164a0 100644 --- a/release/scripts/startup/bl_ui/space_sequencer.py +++ b/release/scripts/startup/bl_ui/space_sequencer.py @@ -1650,7 +1650,7 @@ class SEQUENCER_PT_source(SequencerButtonsPanel, Panel): if sound.samplerate <= 0: split.label(text="Unknown") else: - split.label(text="%d Hz." % sound.samplerate, translate=False) + split.label(text="%d Hz" % sound.samplerate, translate=False) split = col.split(factor=0.5, align=False) split.alignment = 'RIGHT' diff --git a/source/blender/editors/space_node/node_add.cc b/source/blender/editors/space_node/node_add.cc index 975d4eda7e3..04bf5ef469e 100644 --- a/source/blender/editors/space_node/node_add.cc +++ b/source/blender/editors/space_node/node_add.cc @@ -382,7 +382,7 @@ static bool node_add_group_poll(bContext *C) if (snode->edittree->type == NTREE_CUSTOM) { CTX_wm_operator_poll_msg_set(C, "This node editor displays a custom (Python defined) node tree. " - "Dropping node groups isn't supported for this."); + "Dropping node groups isn't supported for this"); return false; } return true; diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c b/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c index 4f00b997902..77616ae13b6 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c @@ -448,7 +448,7 @@ static void options_light_reference_draw(const bContext *UNUSED(C), Panel *panel uiItemR(col, ptr, "shadow_camera_near", 0, "Near", ICON_NONE); uiItemR(col, ptr, "shadow_camera_far", 0, "Far", ICON_NONE); - uiItemR(layout, ptr, "use_shadow_enclosed_shapes", 0, IFACE_("Eclosed Shapes"), ICON_NONE); + uiItemR(layout, ptr, "use_shadow_enclosed_shapes", 0, IFACE_("Enclosed Shapes"), ICON_NONE); } static void options_panel_draw(const bContext *UNUSED(C), Panel *panel) diff --git a/source/blender/makesrna/intern/rna_gpencil_modifier.c b/source/blender/makesrna/intern/rna_gpencil_modifier.c index d3e1aab1ba0..0647bc62081 100644 --- a/source/blender/makesrna/intern/rna_gpencil_modifier.c +++ b/source/blender/makesrna/intern/rna_gpencil_modifier.c @@ -3452,7 +3452,7 @@ static void rna_def_modifier_gpencillineart(BlenderRNA *brna) prop = RNA_def_property(srna, "use_shadow", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "edge_types", LRT_EDGE_FLAG_PROJECTED_SHADOW); RNA_def_property_ui_text( - prop, "Use Shadow", "Project contour lines using a light shource object"); + prop, "Use Shadow", "Project contour lines using a light source object"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); prop = RNA_def_property(srna, "shadow_region_filtering", PROP_ENUM, PROP_NONE); diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h index 7c7f114bb78..71d8f014399 100644 --- a/source/blender/nodes/NOD_static_types.h +++ b/source/blender/nodes/NOD_static_types.h @@ -64,7 +64,7 @@ DefNode(ShaderNode, SH_NODE_BSDF_DIFFUSE, 0, "BSD DefNode(ShaderNode, SH_NODE_BSDF_PRINCIPLED, def_principled, "BSDF_PRINCIPLED", BsdfPrincipled, "Principled BSDF", "Physically-based, easy-to-use shader for rendering surface materials, based on the Disney principled model also known as the \"PBR\" shader") DefNode(ShaderNode, SH_NODE_BSDF_GLOSSY, def_glossy, "BSDF_GLOSSY", BsdfGlossy, "Glossy BSDF", "Reflection with microfacet distribution, used for materials such as metal or mirrors") DefNode(ShaderNode, SH_NODE_BSDF_GLASS, def_glass, "BSDF_GLASS", BsdfGlass, "Glass BSDF", "Glass-like shader mixing refraction and reflection at grazing angles") -DefNode(ShaderNode, SH_NODE_BSDF_REFRACTION, def_refraction, "BSDF_REFRACTION", BsdfRefraction, "Refraction BSDF", "Glossy refraction with sharp or microfacet distribution,. Typically used for materials that transmit light") +DefNode(ShaderNode, SH_NODE_BSDF_REFRACTION, def_refraction, "BSDF_REFRACTION", BsdfRefraction, "Refraction BSDF", "Glossy refraction with sharp or microfacet distribution, typically used for materials that transmit light") DefNode(ShaderNode, SH_NODE_BSDF_TRANSLUCENT, 0, "BSDF_TRANSLUCENT", BsdfTranslucent, "Translucent BSDF", "Lambertian diffuse transmission") DefNode(ShaderNode, SH_NODE_BSDF_TRANSPARENT, 0, "BSDF_TRANSPARENT", BsdfTransparent, "Transparent BSDF", "Transparency without refraction, passing straight through the surface as if there were no geometry") DefNode(ShaderNode, SH_NODE_BSDF_VELVET, 0, "BSDF_VELVET", BsdfVelvet, "Velvet BSDF", "Reflection for materials such as cloth.\nTypically mixed with other shaders (such as a Diffuse Shader) and is not particularly useful on its own") diff --git a/source/blender/nodes/geometry/nodes/node_geo_deform_curves_on_surface.cc b/source/blender/nodes/geometry/nodes/node_geo_deform_curves_on_surface.cc index f08e857e9cc..0a446492c6b 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_deform_curves_on_surface.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_deform_curves_on_surface.cc @@ -229,7 +229,7 @@ static void node_geo_exec(GeoNodeExecParams params) } if (surface_ob_eval == nullptr || surface_ob_eval->type != OB_MESH) { pass_through_input(); - params.error_message_add(NodeWarningType::Error, "Curves not attached to a surface."); + params.error_message_add(NodeWarningType::Error, "Curves not attached to a surface"); return; } Object *surface_ob_orig = DEG_get_original_object(surface_ob_eval); @@ -246,7 +246,7 @@ static void node_geo_exec(GeoNodeExecParams params) false); if (surface_mesh_eval == nullptr) { pass_through_input(); - params.error_message_add(NodeWarningType::Error, "Surface has no mesh."); + params.error_message_add(NodeWarningType::Error, "Surface has no mesh"); return; } @@ -260,13 +260,13 @@ static void node_geo_exec(GeoNodeExecParams params) if (uv_map_name.is_empty()) { pass_through_input(); - const char *message = TIP_("Surface UV map not defined."); + const char *message = TIP_("Surface UV map not defined"); params.error_message_add(NodeWarningType::Error, message); return; } if (!mesh_attributes_eval.contains(uv_map_name)) { pass_through_input(); - char *message = BLI_sprintfN(TIP_("Evaluated surface missing UV map: %s."), + char *message = BLI_sprintfN(TIP_("Evaluated surface missing UV map: %s"), uv_map_name.c_str()); params.error_message_add(NodeWarningType::Error, message); MEM_freeN(message); @@ -274,8 +274,7 @@ static void node_geo_exec(GeoNodeExecParams params) } if (!mesh_attributes_orig.contains(uv_map_name)) { pass_through_input(); - char *message = BLI_sprintfN(TIP_("Original surface missing UV map: %s."), - uv_map_name.c_str()); + char *message = BLI_sprintfN(TIP_("Original surface missing UV map: %s"), uv_map_name.c_str()); params.error_message_add(NodeWarningType::Error, message); MEM_freeN(message); return; @@ -283,13 +282,13 @@ static void node_geo_exec(GeoNodeExecParams params) if (!mesh_attributes_eval.contains(rest_position_name)) { pass_through_input(); params.error_message_add(NodeWarningType::Error, - TIP_("Evaluated surface missing attribute: rest_position.")); + TIP_("Evaluated surface missing attribute: rest_position")); return; } if (curves.surface_uv_coords().is_empty()) { pass_through_input(); params.error_message_add(NodeWarningType::Error, - TIP_("Curves are not attached to any UV map.")); + TIP_("Curves are not attached to any UV map")); return; } const VArraySpan uv_map_orig = mesh_attributes_orig.lookup(uv_map_name, @@ -337,7 +336,7 @@ static void node_geo_exec(GeoNodeExecParams params) curves.tag_positions_changed(); if (invalid_uv_count) { - char *message = BLI_sprintfN(TIP_("Invalid surface UVs on %d curves."), + char *message = BLI_sprintfN(TIP_("Invalid surface UVs on %d curves"), invalid_uv_count.load()); params.error_message_add(NodeWarningType::Warning, message); MEM_freeN(message); -- cgit v1.2.3 From 6ca5ac208477d89477c5c765bc0798700c7fc784 Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Mon, 11 Jul 2022 13:36:29 +0200 Subject: GPU: Update shader builder stubs. Fixes workflow when using WITH_GPU_SHADER_BUILDER=On. --- .../blender/gpu/intern/gpu_shader_builder_stubs.cc | 64 ++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/source/blender/gpu/intern/gpu_shader_builder_stubs.cc b/source/blender/gpu/intern/gpu_shader_builder_stubs.cc index 515f65adb73..d8af2fc584d 100644 --- a/source/blender/gpu/intern/gpu_shader_builder_stubs.cc +++ b/source/blender/gpu/intern/gpu_shader_builder_stubs.cc @@ -12,6 +12,7 @@ #include "IMB_imbuf.h" #include "IMB_imbuf_types.h" +#include "BKE_attribute.h" #include "BKE_customdata.h" #include "BKE_global.h" #include "BKE_material.h" @@ -100,6 +101,38 @@ void UI_GetThemeColorShadeAlpha4ubv(int UNUSED(colorid), /** \} */ +/* -------------------------------------------------------------------- */ +/** \name Stubs of BKE_attribute.h + * \{ */ + +void BKE_id_attribute_copy_domains_temp(short UNUSED(id_type), + const struct CustomData *UNUSED(vdata), + const struct CustomData *UNUSED(edata), + const struct CustomData *UNUSED(ldata), + const struct CustomData *UNUSED(pdata), + const struct CustomData *UNUSED(cdata), + struct ID *UNUSED(i_id)) +{ +} + +struct CustomDataLayer *BKE_id_attributes_active_color_get(const struct ID *UNUSED(id)) +{ + return nullptr; +} + +struct CustomDataLayer *BKE_id_attributes_render_color_get(const struct ID *UNUSED(id)) +{ + return nullptr; +} + +eAttrDomain BKE_id_attribute_domain(const struct ID *UNUSED(id), + const struct CustomDataLayer *UNUSED(layer)) +{ + return ATTR_DOMAIN_AUTO; +} + +/** \} */ + /* -------------------------------------------------------------------- */ /** \name Stubs of BKE_paint.h * \{ */ @@ -170,6 +203,28 @@ int CustomData_get_offset(const struct CustomData *UNUSED(data), int UNUSED(type return 0; } +int CustomData_get_named_layer_index(const struct CustomData *UNUSED(data), + int UNUSED(type), + const char *UNUSED(name)) +{ + return -1; +} + +int CustomData_get_active_layer_index(const struct CustomData *UNUSED(data), int UNUSED(type)) +{ + return -1; +} + +int CustomData_get_render_layer_index(const struct CustomData *UNUSED(data), int UNUSED(type)) +{ + return -1; +} + +bool CustomData_has_layer(const struct CustomData *UNUSED(data), int UNUSED(type)) +{ + return false; +} + /** \} */ /* -------------------------------------------------------------------- */ @@ -237,5 +292,14 @@ void DRW_deferred_shader_remove(struct GPUMaterial *UNUSED(mat)) BLI_assert_unreachable(); } +void DRW_cdlayer_attr_aliases_add(struct GPUVertFormat *UNUSED(format), + const char *UNUSED(base_name), + const struct CustomData *UNUSED(data), + const struct CustomDataLayer *UNUSED(cl), + bool UNUSED(is_active_render), + bool UNUSED(is_active_layer)) +{ +} + /** \} */ } -- cgit v1.2.3 From 6e426259b4de01598df564e5998043f5db43f1de Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Mon, 11 Jul 2022 14:00:49 +0200 Subject: Fix T99218: light group add button should be disabled when name is empty Previously it was inactive but still clickable. Ref D15316 --- intern/cycles/blender/addon/ui.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/intern/cycles/blender/addon/ui.py b/intern/cycles/blender/addon/ui.py index 77da3f36685..09aecb5cb81 100644 --- a/intern/cycles/blender/addon/ui.py +++ b/intern/cycles/blender/addon/ui.py @@ -1202,7 +1202,7 @@ class CYCLES_OBJECT_PT_lightgroup(CyclesButtonsPanel, Panel): sub.prop_search(ob, "lightgroup", view_layer, "lightgroups", text="Light Group", results_are_suggestions=True) sub = row.column(align=True) - sub.active = bool(ob.lightgroup) and not any(lg.name == ob.lightgroup for lg in view_layer.lightgroups) + sub.enabled = bool(ob.lightgroup) and not any(lg.name == ob.lightgroup for lg in view_layer.lightgroups) sub.operator("scene.view_layer_add_lightgroup", icon='ADD', text="").name = ob.lightgroup @@ -1640,7 +1640,7 @@ class CYCLES_WORLD_PT_settings_light_group(CyclesButtonsPanel, Panel): ) sub = row.column(align=True) - sub.active = bool(world.lightgroup) and not any(lg.name == world.lightgroup for lg in view_layer.lightgroups) + sub.enabled = bool(world.lightgroup) and not any(lg.name == world.lightgroup for lg in view_layer.lightgroups) sub.operator("scene.view_layer_add_lightgroup", icon='ADD', text="").name = world.lightgroup -- cgit v1.2.3 From 76d86142367e0eb67e7a423ae5a4884bbd21d6ac Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Mon, 11 Jul 2022 16:12:27 +0200 Subject: GPU: Do not allow GPU Shader builder when USD is enabled. Linking GPU shader builder requires stubs for many functions of the USD library. We don't want to rely on other modules to update the stubs for a tool that is only used by GPU developers. This patch raises an error when both WITH_GPU_SHADER_BUILDER and WITH_USD are enabled. This reduces the maintenance of updating the stubs when USD API changes. Reviewed By: LazyDodo Differential Revision: https://developer.blender.org/D15422 --- CMakeLists.txt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 1416b5b4189..22f31d6af48 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1340,6 +1340,12 @@ else() list(APPEND GL_DEFINITIONS -DWITH_GL_PROFILE_CORE) endif() +if (WITH_GPU_SHADER_BUILDER AND WITH_USD) + message(FATAL_ERROR + "Unable to compile WITH_GPU_SHADER_BUILDER and WITH_USD." + ) +endif() + #----------------------------------------------------------------------------- # Configure Metal. if (WITH_METAL_BACKEND) -- cgit v1.2.3 From 8ca09e6c5eb6ac5401f7e7d89185e341921e7d3a Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Mon, 11 Jul 2022 16:45:07 +0200 Subject: GPU: add BUIDTIME to WITH_GPU_SHADER_BUILDER Adds a better name that describes when it is used. The GPU_SHADER_BUILDER is a buildtime tool for developers to pre-validate GLSL (and in the overseen future pre-compile to SpirV). We don't see that this needs to become a required step in the future so WITH_GPU_BUILDTIME_SHADER_BUILDER is more descriptive name. --- CMakeLists.txt | 8 ++++---- release/datafiles/locale | 2 +- release/scripts/addons | 2 +- source/blender/gpu/CMakeLists.txt | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 22f31d6af48..6b7ee7f1887 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -565,13 +565,13 @@ endif() option(WITH_OPENGL "When off limits visibility of the opengl headers to just bf_gpu and gawain (temporary option for development purposes)" ON) option(WITH_GLEW_ES "Switches to experimental copy of GLEW that has support for OpenGL ES. (temporary option for development purposes)" OFF) option(WITH_GL_PROFILE_ES20 "Support using OpenGL ES 2.0. (through either EGL or the AGL/WGL/XGL 'es20' profile)" OFF) -option(WITH_GPU_SHADER_BUILDER "Shader builder is a developer option enabling linting on GLSL during compilation" OFF) +option(WITH_GPU_BUILDTIME_SHADER_BUILDER "Shader builder is a developer option enabling linting on GLSL during compilation" OFF) mark_as_advanced( WITH_OPENGL WITH_GLEW_ES WITH_GL_PROFILE_ES20 - WITH_GPU_SHADER_BUILDER + WITH_GPU_BUILDTIME_SHADER_BUILDER ) # Metal @@ -1340,9 +1340,9 @@ else() list(APPEND GL_DEFINITIONS -DWITH_GL_PROFILE_CORE) endif() -if (WITH_GPU_SHADER_BUILDER AND WITH_USD) +if (WITH_GPU_BUILDTIME_SHADER_BUILDER AND WITH_USD) message(FATAL_ERROR - "Unable to compile WITH_GPU_SHADER_BUILDER and WITH_USD." + "Unable to compile WITH_GPU_BUILDTIME_SHADER_BUILDER and WITH_USD." ) endif() diff --git a/release/datafiles/locale b/release/datafiles/locale index 055bc5223c1..a2eb5078914 160000 --- a/release/datafiles/locale +++ b/release/datafiles/locale @@ -1 +1 @@ -Subproject commit 055bc5223c1cd249e32ccbc8e8796ba9925c8c33 +Subproject commit a2eb507891449a0b67582be9561840075513661d diff --git a/release/scripts/addons b/release/scripts/addons index 7ea2e74fc41..7a8502871c3 160000 --- a/release/scripts/addons +++ b/release/scripts/addons @@ -1 +1 @@ -Subproject commit 7ea2e74fc41b2eabdbf639b812082e73823b09d7 +Subproject commit 7a8502871c34db0343cc7de52d6b49b15a84238a diff --git a/source/blender/gpu/CMakeLists.txt b/source/blender/gpu/CMakeLists.txt index 0f539d4b30a..1c883f7fa41 100644 --- a/source/blender/gpu/CMakeLists.txt +++ b/source/blender/gpu/CMakeLists.txt @@ -561,7 +561,7 @@ endif() -if(WITH_GPU_SHADER_BUILDER) +if(WITH_GPU_BUILDTIME_SHADER_BUILDER) # TODO(@fclem) Fix this mess. if(APPLE) add_executable(shader_builder -- cgit v1.2.3 From 995c904d00e1e0713a1549ca16b2775a9dcdd281 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Mon, 11 Jul 2022 19:04:24 +0200 Subject: Fix (unreported) crash in liboverride code on rare cases. When dealing with 'embedded' IDs (and the like, e.g. shape keys), liboverride code could fail in case the reference linked data (e.g. a mesh) would not have a shapekey anymore, while the override mesh would still have one. Found while investigating another issue in Heist production file `Heist/pro/animation_test/einar/einar_new_expression_shapes2.blend`, r1230. --- source/blender/blenkernel/intern/lib_override.cc | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/source/blender/blenkernel/intern/lib_override.cc b/source/blender/blenkernel/intern/lib_override.cc index d816b5ede5f..cdea97b6c6a 100644 --- a/source/blender/blenkernel/intern/lib_override.cc +++ b/source/blender/blenkernel/intern/lib_override.cc @@ -1771,6 +1771,11 @@ static bool lib_override_library_resync(Main *bmain, break; } } + if (reference_id == nullptr) { + /* Can happen e.g. when there is a local override of a shapekey, but the matching linked + * obdata (mesh etc.) does not have any shapekey anymore. */ + continue; + } BLI_assert(GS(reference_id->name) == GS(id->name)); if (!BLI_ghash_haskey(linkedref_to_old_override, reference_id)) { -- cgit v1.2.3 From 2a1d12d7a0edd7ecffda3c5e1bf5c3b512976bbb Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Mon, 11 Jul 2022 19:09:56 +0200 Subject: Fix (studio-reported) crash in ID remapping code on rare cases. Some ID types did not have a filter value, even though they would be used in remapping code, leading to missing remappings. In that specific case, shape keys would actually never be properly remapped. Reproducible in r1230 of `Heist/pro/animation_test/einar/einar_new_expression_shapes2.blend`, --- source/blender/blenkernel/intern/idtype.c | 26 +++++++++++++++++----- source/blender/blenkernel/intern/key.c | 2 +- .../blender/blenkernel/intern/lib_id_remapper.cc | 1 + source/blender/blenkernel/intern/screen.c | 2 +- source/blender/makesdna/DNA_ID.h | 6 ++++- source/blender/windowmanager/intern/wm.c | 2 +- 6 files changed, 30 insertions(+), 9 deletions(-) diff --git a/source/blender/blenkernel/intern/idtype.c b/source/blender/blenkernel/intern/idtype.c index e55143d6852..edb6fe5d69b 100644 --- a/source/blender/blenkernel/intern/idtype.c +++ b/source/blender/blenkernel/intern/idtype.c @@ -209,7 +209,11 @@ uint64_t BKE_idtype_idcode_to_idfilter(const short idcode) case ID_##_id: \ return FILTER_ID_##_id - switch (idcode) { +#define CASE_IDFILTER_NONE(_id) \ + case ID_##_id: \ + return 0 + + switch ((ID_Type)idcode) { CASE_IDFILTER(AC); CASE_IDFILTER(AR); CASE_IDFILTER(BR); @@ -220,7 +224,10 @@ uint64_t BKE_idtype_idcode_to_idfilter(const short idcode) CASE_IDFILTER(GR); CASE_IDFILTER(CV); CASE_IDFILTER(IM); + CASE_IDFILTER_NONE(IP); + CASE_IDFILTER(KE); CASE_IDFILTER(LA); + CASE_IDFILTER_NONE(LI); CASE_IDFILTER(LS); CASE_IDFILTER(LT); CASE_IDFILTER(MA); @@ -236,6 +243,7 @@ uint64_t BKE_idtype_idcode_to_idfilter(const short idcode) CASE_IDFILTER(PT); CASE_IDFILTER(LP); CASE_IDFILTER(SCE); + CASE_IDFILTER(SCR); CASE_IDFILTER(SIM); CASE_IDFILTER(SPK); CASE_IDFILTER(SO); @@ -243,13 +251,16 @@ uint64_t BKE_idtype_idcode_to_idfilter(const short idcode) CASE_IDFILTER(TXT); CASE_IDFILTER(VF); CASE_IDFILTER(VO); + CASE_IDFILTER(WM); CASE_IDFILTER(WO); CASE_IDFILTER(WS); - default: - return 0; } + BLI_assert_unreachable(); + return 0; + #undef CASE_IDFILTER +#undef CASE_IDFILTER_NONE } short BKE_idtype_idcode_from_idfilter(const uint64_t idfilter) @@ -269,6 +280,7 @@ short BKE_idtype_idcode_from_idfilter(const uint64_t idfilter) CASE_IDFILTER(GR); CASE_IDFILTER(CV); CASE_IDFILTER(IM); + CASE_IDFILTER(KE); CASE_IDFILTER(LA); CASE_IDFILTER(LS); CASE_IDFILTER(LT); @@ -285,6 +297,7 @@ short BKE_idtype_idcode_from_idfilter(const uint64_t idfilter) CASE_IDFILTER(PT); CASE_IDFILTER(LP); CASE_IDFILTER(SCE); + CASE_IDFILTER(SCR); CASE_IDFILTER(SIM); CASE_IDFILTER(SPK); CASE_IDFILTER(SO); @@ -292,11 +305,14 @@ short BKE_idtype_idcode_from_idfilter(const uint64_t idfilter) CASE_IDFILTER(TXT); CASE_IDFILTER(VF); CASE_IDFILTER(VO); + CASE_IDFILTER(WM); CASE_IDFILTER(WO); - default: - return 0; + CASE_IDFILTER(WS); } + BLI_assert_unreachable(); + return 0; + #undef CASE_IDFILTER } diff --git a/source/blender/blenkernel/intern/key.c b/source/blender/blenkernel/intern/key.c index 594cffe6406..97eac0b9f91 100644 --- a/source/blender/blenkernel/intern/key.c +++ b/source/blender/blenkernel/intern/key.c @@ -191,7 +191,7 @@ static void shapekey_blend_read_expand(BlendExpander *expander, ID *id) IDTypeInfo IDType_ID_KE = { .id_code = ID_KE, - .id_filter = 0, + .id_filter = FILTER_ID_KE, .main_listbase_index = INDEX_ID_KE, .struct_size = sizeof(Key), .name = "Key", diff --git a/source/blender/blenkernel/intern/lib_id_remapper.cc b/source/blender/blenkernel/intern/lib_id_remapper.cc index 7e75e0f5d93..98ac9110a48 100644 --- a/source/blender/blenkernel/intern/lib_id_remapper.cc +++ b/source/blender/blenkernel/intern/lib_id_remapper.cc @@ -36,6 +36,7 @@ struct IDRemapper { BLI_assert(old_id != nullptr); BLI_assert(new_id == nullptr || (GS(old_id->name) == GS(new_id->name))); mappings.add(old_id, new_id); + BLI_assert(BKE_idtype_idcode_to_idfilter(GS(old_id->name)) != 0); source_types |= BKE_idtype_idcode_to_idfilter(GS(old_id->name)); } diff --git a/source/blender/blenkernel/intern/screen.c b/source/blender/blenkernel/intern/screen.c index 12dc1b6d1fa..c16e5ce5655 100644 --- a/source/blender/blenkernel/intern/screen.c +++ b/source/blender/blenkernel/intern/screen.c @@ -277,7 +277,7 @@ static void screen_blend_read_lib(BlendLibReader *reader, ID *id) IDTypeInfo IDType_ID_SCR = { .id_code = ID_SCR, - .id_filter = 0, + .id_filter = FILTER_ID_SCR, .main_listbase_index = INDEX_ID_SCR, .struct_size = sizeof(bScreen), .name = "Screen", diff --git a/source/blender/makesdna/DNA_ID.h b/source/blender/makesdna/DNA_ID.h index 451c921c4ef..f6032b71155 100644 --- a/source/blender/makesdna/DNA_ID.h +++ b/source/blender/makesdna/DNA_ID.h @@ -923,6 +923,9 @@ typedef enum IDRecalcFlag { #define FILTER_ID_PT (1ULL << 33) #define FILTER_ID_VO (1ULL << 34) #define FILTER_ID_SIM (1ULL << 35) +#define FILTER_ID_KE (1ULL << 36) +#define FILTER_ID_SCR (1ULL << 37) +#define FILTER_ID_WM (1ULL << 38) #define FILTER_ID_ALL \ (FILTER_ID_AC | FILTER_ID_AR | FILTER_ID_BR | FILTER_ID_CA | FILTER_ID_CU_LEGACY | \ @@ -930,7 +933,8 @@ typedef enum IDRecalcFlag { FILTER_ID_MA | FILTER_ID_MB | FILTER_ID_MC | FILTER_ID_ME | FILTER_ID_MSK | FILTER_ID_NT | \ FILTER_ID_OB | FILTER_ID_PA | FILTER_ID_PAL | FILTER_ID_PC | FILTER_ID_SCE | FILTER_ID_SPK | \ FILTER_ID_SO | FILTER_ID_TE | FILTER_ID_TXT | FILTER_ID_VF | FILTER_ID_WO | FILTER_ID_CF | \ - FILTER_ID_WS | FILTER_ID_LP | FILTER_ID_CV | FILTER_ID_PT | FILTER_ID_VO | FILTER_ID_SIM) + FILTER_ID_WS | FILTER_ID_LP | FILTER_ID_CV | FILTER_ID_PT | FILTER_ID_VO | FILTER_ID_SIM | \ + FILTER_ID_KE | FILTER_ID_SCR | FILTER_ID_WM) /** * This enum defines the index assigned to each type of IDs in the array returned by diff --git a/source/blender/windowmanager/intern/wm.c b/source/blender/windowmanager/intern/wm.c index 32cec8f779c..0d74bc259f4 100644 --- a/source/blender/windowmanager/intern/wm.c +++ b/source/blender/windowmanager/intern/wm.c @@ -257,7 +257,7 @@ static void window_manager_blend_read_lib(BlendLibReader *reader, ID *id) IDTypeInfo IDType_ID_WM = { .id_code = ID_WM, - .id_filter = 0, + .id_filter = FILTER_ID_WM, .main_listbase_index = INDEX_ID_WM, .struct_size = sizeof(wmWindowManager), .name = "WindowManager", -- cgit v1.2.3 From 00c7e760b323e5fa46703d0e4769c8f1d9c35f2e Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 12 Jul 2022 16:05:13 +1000 Subject: Python: add opcodes for safe py-drivers The following opcodes have been added, see [0] for details: - LIST_TO_TUPLE: convert a list to a tuple, use for constructing lists/tuples in some cases. - LIST_EXTEND: use for constructing lists with unpacking. - SET_UPDATE: use for constructing sets with unpacking. - CONTAINS_OP: check if `a in b` generally useful. When writing tests these op-codes where needed for basic operations and can be safely supported. Add note why dictionary manipulation op-codes have been left out. Also restrict namsepace access to anything with an underscore prefix since these may be undocumented. [0]: https://docs.python.org/3.10/library/dis.html --- source/blender/python/intern/bpy_driver.c | 98 +++++++++++++++++++++++-------- source/blender/python/intern/bpy_driver.h | 10 ++++ 2 files changed, 83 insertions(+), 25 deletions(-) diff --git a/source/blender/python/intern/bpy_driver.c b/source/blender/python/intern/bpy_driver.c index cc64af2a489..04aa203d198 100644 --- a/source/blender/python/intern/bpy_driver.c +++ b/source/blender/python/intern/bpy_driver.c @@ -318,6 +318,7 @@ static const bool secure_opcodes[255] = { OK_OP(UNARY_INVERT), OK_OP(BINARY_SUBSCR), OK_OP(GET_LEN), + OK_OP(LIST_TO_TUPLE), OK_OP(RETURN_VALUE), OK_OP(SWAP), OK_OP(BUILD_TUPLE), @@ -332,6 +333,7 @@ static const bool secure_opcodes[255] = { OK_OP(POP_JUMP_FORWARD_IF_TRUE), OK_OP(LOAD_GLOBAL), OK_OP(IS_OP), + OK_OP(CONTAINS_OP), OK_OP(BINARY_OP), OK_OP(LOAD_FAST), OK_OP(STORE_FAST), @@ -342,6 +344,14 @@ static const bool secure_opcodes[255] = { OK_OP(LOAD_DEREF), OK_OP(STORE_DEREF), OK_OP(RESUME), + OK_OP(LIST_EXTEND), + OK_OP(SET_UPDATE), +/* NOTE(@campbellbarton): Don't enable dict manipulation, unless we can prove there is not way it + * can be used to manipulate the name-space (potentially allowing malicious code). */ +# if 0 + OK_OP(DICT_MERGE), + OK_OP(DICT_UPDATE), +# endif OK_OP(POP_JUMP_BACKWARD_IF_NOT_NONE), OK_OP(POP_JUMP_BACKWARD_IF_NONE), OK_OP(POP_JUMP_BACKWARD_IF_FALSE), @@ -395,6 +405,7 @@ static const bool secure_opcodes[255] = { OK_OP(INPLACE_AND), OK_OP(INPLACE_XOR), OK_OP(INPLACE_OR), + OK_OP(LIST_TO_TUPLE), OK_OP(RETURN_VALUE), OK_OP(ROT_N), OK_OP(BUILD_TUPLE), @@ -410,12 +421,21 @@ static const bool secure_opcodes[255] = { OK_OP(POP_JUMP_IF_TRUE), OK_OP(LOAD_GLOBAL), OK_OP(IS_OP), + OK_OP(CONTAINS_OP), OK_OP(LOAD_FAST), OK_OP(STORE_FAST), OK_OP(DELETE_FAST), OK_OP(BUILD_SLICE), OK_OP(LOAD_DEREF), OK_OP(STORE_DEREF), + OK_OP(LIST_EXTEND), + OK_OP(SET_UPDATE), +/* NOTE(@campbellbarton): Don't enable dict manipulation, unless we can prove there is not way it + * can be used to manipulate the name-space (potentially allowing malicious code). */ +# if 0 + OK_OP(DICT_MERGE), + OK_OP(DICT_UPDATE), +# endif /* Special cases. */ OK_OP(LOAD_CONST), /* Ok because constants are accepted. */ @@ -429,9 +449,10 @@ static const bool secure_opcodes[255] = { # undef OK_OP -static bool bpy_driver_secure_bytecode_validate(PyObject *expr_code, - PyObject *dict_arr[], - const char *error_prefix) +bool BPY_driver_secure_bytecode_test_ex(PyObject *expr_code, + PyObject *namespace_array[], + const bool verbose, + const char *error_prefix) { PyCodeObject *py_code = (PyCodeObject *)expr_code; @@ -439,21 +460,23 @@ static bool bpy_driver_secure_bytecode_validate(PyObject *expr_code, { for (int i = 0; i < PyTuple_GET_SIZE(py_code->co_names); i++) { PyObject *name = PyTuple_GET_ITEM(py_code->co_names, i); - + const char *name_str = PyUnicode_AsUTF8(name); bool contains_name = false; - for (int j = 0; dict_arr[j]; j++) { - if (PyDict_Contains(dict_arr[j], name)) { + for (int j = 0; namespace_array[j]; j++) { + if (PyDict_Contains(namespace_array[j], name)) { contains_name = true; break; } } - if (contains_name == false) { - fprintf(stderr, - "\t%s: restricted access disallows name '%s', " - "enable auto-execution to support\n", - error_prefix, - PyUnicode_AsUTF8(name)); + if ((contains_name == false) || (name_str[0] == '_')) { + if (verbose) { + fprintf(stderr, + "\t%s: restricted access disallows name '%s', " + "enable auto-execution to support\n", + error_prefix, + name_str); + } return false; } } @@ -485,11 +508,13 @@ static bool bpy_driver_secure_bytecode_validate(PyObject *expr_code, for (Py_ssize_t i = 0; i < code_len; i++) { const int opcode = _Py_OPCODE(codestr[i]); if (secure_opcodes[opcode] == false) { - fprintf(stderr, - "\t%s: restricted access disallows opcode '%d', " - "enable auto-execution to support\n", - error_prefix, - opcode); + if (verbose) { + fprintf(stderr, + "\t%s: restricted access disallows opcode '%d', " + "enable auto-execution to support\n", + error_prefix, + opcode); + } ok = false; break; } @@ -506,6 +531,26 @@ static bool bpy_driver_secure_bytecode_validate(PyObject *expr_code, return true; } +bool BPY_driver_secure_bytecode_test(PyObject *expr_code, PyObject *namespace, const bool verbose) +{ + + if (!bpy_pydriver_Dict) { + if (bpy_pydriver_create_dict() != 0) { + fprintf(stderr, "%s: couldn't create Python dictionary\n", __func__); + return false; + } + } + return BPY_driver_secure_bytecode_test_ex(expr_code, + (PyObject *[]){ + bpy_pydriver_Dict, + bpy_pydriver_Dict__whitelist, + namespace, + NULL, + }, + verbose, + __func__); +} + #endif /* USE_BYTECODE_WHITELIST */ float BPY_driver_exec(struct PathResolvedRNA *anim_rna, ChannelDriver *driver, @@ -697,14 +742,17 @@ float BPY_driver_exec(struct PathResolvedRNA *anim_rna, #ifdef USE_BYTECODE_WHITELIST if (is_recompile && expr_code) { if (!(G.f & G_FLAG_SCRIPT_AUTOEXEC)) { - if (!bpy_driver_secure_bytecode_validate(expr_code, - (PyObject *[]){ - bpy_pydriver_Dict, - bpy_pydriver_Dict__whitelist, - driver_vars, - NULL, - }, - __func__)) { + if (!BPY_driver_secure_bytecode_test_ex( + expr_code, + (PyObject *[]){ + bpy_pydriver_Dict, + bpy_pydriver_Dict__whitelist, + driver_vars, + NULL, + }, + /* Always be verbose since this can give hints to why evaluation fails. */ + true, + __func__)) { if (!(G.f & G_FLAG_SCRIPT_AUTOEXEC_FAIL_QUIET)) { G.f |= G_FLAG_SCRIPT_AUTOEXEC_FAIL; BLI_snprintf(G.autoexec_fail, sizeof(G.autoexec_fail), "Driver '%s'", expr); diff --git a/source/blender/python/intern/bpy_driver.h b/source/blender/python/intern/bpy_driver.h index 301c6b3662e..f0d9717dbbd 100644 --- a/source/blender/python/intern/bpy_driver.h +++ b/source/blender/python/intern/bpy_driver.h @@ -10,6 +10,8 @@ extern "C" { #endif +#include + /** * For faster execution we keep a special dictionary for py-drivers, with * the needed modules and aliases. @@ -21,6 +23,14 @@ int bpy_pydriver_create_dict(void); */ extern PyObject *bpy_pydriver_Dict; +extern bool BPY_driver_secure_bytecode_test_ex(PyObject *expr_code, + PyObject *namespace_array[], + const bool verbose, + const char *error_prefix); +extern bool BPY_driver_secure_bytecode_test(PyObject *expr_code, + PyObject *namespace, + const bool verbose); + #ifdef __cplusplus } #endif -- cgit v1.2.3 From ae6a4fcc7a707e419463c47e191afe54def26764 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 12 Jul 2022 16:05:15 +1000 Subject: Tests: add test to ensure restricted py-driver execution is working Add internal function (only used for testing at the moment) `_bpy._driver_secure_code_test`. Add test `script_pyapi_bpy_driver_secure_eval` to serves two purposes: - Ensure expressions that should be insecure remain so when upgrading Python or making any changes in this area. - Ensure new versions of Python don't introduce new byte-codes that prevent existing expressions from being executed (happened when upgrading from 3.7, see [0]). [0]: dfa52017638abdf59791e5588c439d3a558a191d --- source/blender/python/intern/bpy.c | 53 ++++++ tests/python/CMakeLists.txt | 5 + tests/python/bl_pyapi_bpy_driver_secure_eval.py | 220 ++++++++++++++++++++++++ 3 files changed, 278 insertions(+) create mode 100644 tests/python/bl_pyapi_bpy_driver_secure_eval.py diff --git a/source/blender/python/intern/bpy.c b/source/blender/python/intern/bpy.c index 2e97ae0fc1d..d1e8b894ac0 100644 --- a/source/blender/python/intern/bpy.c +++ b/source/blender/python/intern/bpy.c @@ -32,6 +32,7 @@ #include "bpy.h" #include "bpy_app.h" #include "bpy_capi_utils.h" +#include "bpy_driver.h" #include "bpy_library.h" #include "bpy_operator.h" #include "bpy_props.h" @@ -326,6 +327,49 @@ static PyObject *bpy_resource_path(PyObject *UNUSED(self), PyObject *args, PyObj return PyC_UnicodeFromByte(path ? path : ""); } +/* This is only exposed for tests, see: `tests/python/bl_pyapi_bpy_driver_secure_eval.py`. */ +PyDoc_STRVAR(bpy_driver_secure_code_test_doc, + ".. function:: _driver_secure_code_test(code)\n" + "\n" + " Test if the script should be considered trusted.\n" + "\n" + " :arg code: The code to test.\n" + " :type code: code\n" + " :arg namespace: The namespace of values which are allowed.\n" + " :type namespace: dict\n" + " :arg verbose: Print the reason for considering insecure to the ``stderr``.\n" + " :type verbose: bool\n" + " :return: True when the script is considered trusted.\n" + " :rtype: bool\n"); +static PyObject *bpy_driver_secure_code_test(PyObject *UNUSED(self), PyObject *args, PyObject *kw) +{ + PyObject *py_code; + PyObject *py_namespace = NULL; + const bool verbose = false; + static const char *_keywords[] = {"code", "namespace", "verbose", NULL}; + static _PyArg_Parser _parser = { + "O!" /* `expression` */ + "|$" /* Optional keyword only arguments. */ + "O!" /* `namespace` */ + "O&" /* `verbose` */ + ":driver_secure_code_test", + _keywords, + 0, + }; + if (!_PyArg_ParseTupleAndKeywordsFast(args, + kw, + &_parser, + &PyCode_Type, + &py_code, + &PyDict_Type, + &py_namespace, + PyC_ParseBool, + &verbose)) { + return NULL; + } + return PyBool_FromLong(BPY_driver_secure_bytecode_test(py_code, py_namespace, verbose)); +} + PyDoc_STRVAR(bpy_escape_identifier_doc, ".. function:: escape_identifier(string)\n" "\n" @@ -528,6 +572,12 @@ static PyMethodDef meth_bpy_resource_path = { METH_VARARGS | METH_KEYWORDS, bpy_resource_path_doc, }; +static PyMethodDef meth_bpy_driver_secure_code_test = { + "_driver_secure_code_test", + (PyCFunction)bpy_driver_secure_code_test, + METH_VARARGS | METH_KEYWORDS, + bpy_driver_secure_code_test_doc, +}; static PyMethodDef meth_bpy_escape_identifier = { "escape_identifier", (PyCFunction)bpy_escape_identifier, @@ -647,6 +697,9 @@ void BPy_init_modules(struct bContext *C) PyModule_AddObject(mod, meth_bpy_resource_path.ml_name, (PyObject *)PyCFunction_New(&meth_bpy_resource_path, NULL)); + PyModule_AddObject(mod, + meth_bpy_driver_secure_code_test.ml_name, + (PyObject *)PyCFunction_New(&meth_bpy_driver_secure_code_test, NULL)); PyModule_AddObject(mod, meth_bpy_escape_identifier.ml_name, (PyObject *)PyCFunction_New(&meth_bpy_escape_identifier, NULL)); diff --git a/tests/python/CMakeLists.txt b/tests/python/CMakeLists.txt index 38c3fc4389a..d95f2cd2644 100644 --- a/tests/python/CMakeLists.txt +++ b/tests/python/CMakeLists.txt @@ -101,6 +101,11 @@ add_blender_test( --python ${CMAKE_CURRENT_LIST_DIR}/bl_pyapi_mathutils.py ) +add_blender_test( + script_pyapi_bpy_driver_secure_eval + --python ${CMAKE_CURRENT_LIST_DIR}/bl_pyapi_bpy_driver_secure_eval.py +) + add_blender_test( script_pyapi_idprop --python ${CMAKE_CURRENT_LIST_DIR}/bl_pyapi_idprop.py diff --git a/tests/python/bl_pyapi_bpy_driver_secure_eval.py b/tests/python/bl_pyapi_bpy_driver_secure_eval.py new file mode 100644 index 00000000000..953dbcd5381 --- /dev/null +++ b/tests/python/bl_pyapi_bpy_driver_secure_eval.py @@ -0,0 +1,220 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +# ./blender.bin --background -noaudio --python tests/python/bl_pyapi_bpy_driver_secure_eval.py -- --verbose +import bpy +import unittest +import builtins +from types import ModuleType + + +# ----------------------------------------------------------------------------- +# Mock Environment + + +expect_unreachable_msg = "This function should _NEVER_ run!" +# Internal check, to ensure this actually runs as expected. +expect_unreachable_count = 0 + + +def expect_os_unreachable(): + global expect_unreachable_count + expect_unreachable_count += 1 + raise Exception(expect_unreachable_msg) + + +__import__("os").expect_os_unreachable = expect_os_unreachable + + +expect_open_unreachable_count = 0 + + +def open_expect_unreachable(*args, **kwargs): + global expect_open_unreachable_count + expect_open_unreachable_count += 1 + raise Exception(expect_unreachable_msg) + + +mock_builtins = {**builtins.__dict__, **{"open": open_expect_unreachable}} + + +# ----------------------------------------------------------------------------- +# Utility Functions + + +def is_expression_secure(expr_str, verbose): + """ + Return (ok, code) where ok is true if expr_str is considered secure. + """ + # Internal function only for testing (not part of the public API). + from _bpy import _driver_secure_code_test + expr_code = compile(expr_str, "", 'eval') + ok = _driver_secure_code_test(expr_code, verbose=verbose) + return ok, expr_code + + +# ----------------------------------------------------------------------------- +# Tests (Accept) + + +class _TestExprMixIn: + """ + Sub-classes must define: + - expressions_expect_secure: boolean, the expected secure state. + - expressions: A sequence of expressions that must evaluate in the driver name-space. + + Optionally: + - expressions_expect_unreachable: + A boolean, when true, it's expected each expression should call + ``expect_os_unreachable`` or ``expect_open_unreachable``. + """ + + # Sub-class may override. + expressions_expect_unreachable = False + + def assertSecure(self, expect_secure, expr_str): + is_secure, expr_code = is_expression_secure( + expr_str, + # Only verbose when secure as this is will result in an failure, + # in that case it's useful to know which op-codes caused the test to unexpectedly fail. + verbose=expect_secure, + ) + if is_secure != expect_secure: + raise self.failureException( + "Expression \"%s\" was expected to be %s" % + (expr_str, "secure" if expect_secure else "insecure")) + # NOTE: executing is not essential, it's just better to ensure the expressions make sense. + try: + exec( + expr_code, + {"__builtins__": mock_builtins}, + {**bpy.app.driver_namespace, **{"__builtins__": mock_builtins}}, + ) + # exec(expr_code, {}, bpy.app.driver_namespace) + ex = None + except BaseException as ex_test: + ex = ex_test + + if self.expressions_expect_unreachable: + if ex and ex.args == (expect_unreachable_msg,): + ex = None + elif not ex: + raise self.failureException("Expression \"%s\" failed to run `os.expect_os_unreachable`" % (expr_str,)) + else: + # An unknown exception was raised, use the exception below. + pass + + if ex: + raise self.failureException("Expression \"%s\" failed to evaluate with error: %r" % (expr_str, ex)) + + def test_expr(self): + expect_secure = self.expressions_expect_secure + for expr_str in self.expressions: + self.assertSecure(expect_secure, expr_str) + + +class TestExprMixIn_Accept(_TestExprMixIn): + expressions_expect_secure = True + + +class TestExprMixIn_Reject(_TestExprMixIn): + expressions_expect_secure = False + + +class TestAcceptLiteralNumbers(unittest.TestCase, TestExprMixIn_Accept): + expressions = ("1", "1_1", "1.1", "1j", "0x1", "0o1", "0b1") + + +class TestAcceptLiteralStrings(unittest.TestCase, TestExprMixIn_Accept): + expressions = ("''", "'_'", "r''", "r'_'", "'''_'''") + + +class TestAcceptSequencesEmpty(unittest.TestCase, TestExprMixIn_Accept): + expressions = ("()", "[]", "{}", "[[]]", "(())") + + +class TestAcceptSequencesSimple(unittest.TestCase, TestExprMixIn_Accept): + expressions = ("('', '')", "['', '_']", "{'', '_'}", "{'': '_'}") + + +class TestAcceptSequencesExpand(unittest.TestCase, TestExprMixIn_Accept): + expressions = ("(*('', '_'),)", "[*(), *[]]", "{*{1, 2}}") + + +class TestAcceptSequencesComplex(unittest.TestCase, TestExprMixIn_Accept): + expressions = ("[1, 2, 3][-1:0:-1][0]", "1 in (1, 2)", "False if 1 in {1, 2} else True") + + +class TestAcceptMathOperators(unittest.TestCase, TestExprMixIn_Accept): + expressions = ("4 / 4", "4 * 4", "4 // 4", "2 ** 2", "4 ^ -1", "4 & 1", "4 % 1") + + +class TestAcceptMathFunctionsSimple(unittest.TestCase, TestExprMixIn_Accept): + expressions = ("sin(pi)", "degrees(pi / 2)", "clamp(4, 0, 1)") + + +class TestAcceptMathFunctionsComplex(unittest.TestCase, TestExprMixIn_Accept): + expressions = ("-(sin(pi) ** 2) / 2", "floor(22 / 7)", "ceil(pi + 1)") + + +# ----------------------------------------------------------------------------- +# Tests (Reject) + +class TestRejectLiteralFStrings(unittest.TestCase, TestExprMixIn_Reject): + # F-String's are not supported as `BUILD_STRING` op-code is disabled, + # while it may be safe to enable that needs to be double-checked. + # Further it doesn't seem useful for typical math expressions used in drivers. + expressions = ("f''", "f'{1}'", "f'{\"_\"}'") + + +class TestRejectModuleAccess(unittest.TestCase, TestExprMixIn_Reject): + # Each of these commands _must_ run `expect_os_unreachable`, + # and must also be rejected as insecure - otherwise we have problems. + expressions_expect_unreachable = True + expressions = ( + "__import__('os').expect_os_unreachable()", + "exec(\"__import__('os').expect_os_unreachable()\")", + "(globals().update(__import__('os').__dict__), expect_os_unreachable())", + ) + + # Ensure the functions are actually called. + def setUp(self): + self._count = expect_unreachable_count + + def tearDown(self): + count_actual = expect_unreachable_count - self._count + count_expect = len(self.expressions) + if count_actual != count_expect: + raise Exception( + "Expected 'os.expect_os_unreachable' to be called %d times but was called %d times" % + (count_expect, count_actual), + ) + + +class TestRejectOpenAccess(unittest.TestCase, TestExprMixIn_Reject): + # Each of these commands _must_ run `expect_open_unreachable`, + # and must also be rejected as insecure - otherwise we have problems. + expressions_expect_unreachable = True + expressions = ( + "open('file.txt', 'r')", + "exec(\"open('file.txt', 'r')\")", + "(globals().update({'fake_open': __builtins__['open']}), fake_open())", + ) + + # Ensure the functions are actually called. + def setUp(self): + self._count = expect_open_unreachable_count + + def tearDown(self): + count_actual = expect_open_unreachable_count - self._count + count_expect = len(self.expressions) + if count_actual != count_expect: + raise Exception( + "Expected 'open' to be called %d times but was called %d times" % + (count_expect, count_actual), + ) + + +if __name__ == '__main__': + import sys + sys.argv = [__file__] + (sys.argv[sys.argv.index("--") + 1:] if "--" in sys.argv else []) + unittest.main() -- cgit v1.2.3 From b8d1e576bcabb4fad42ca9cf8f980240213ac0cf Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 12 Jul 2022 16:05:17 +1000 Subject: Cleanup: use array for internal _bpy methods --- source/blender/python/intern/bpy.c | 134 ++++++++++--------------------------- 1 file changed, 37 insertions(+), 97 deletions(-) diff --git a/source/blender/python/intern/bpy.c b/source/blender/python/intern/bpy.c index d1e8b894ac0..7fe0b9455e6 100644 --- a/source/blender/python/intern/bpy.c +++ b/source/blender/python/intern/bpy.c @@ -536,71 +536,37 @@ static PyObject *bpy_rna_enum_items_static(PyObject *UNUSED(self)) return result; } -static PyMethodDef meth_bpy_script_paths = { - "script_paths", - (PyCFunction)bpy_script_paths, - METH_NOARGS, - bpy_script_paths_doc, -}; -static PyMethodDef meth_bpy_blend_paths = { - "blend_paths", - (PyCFunction)bpy_blend_paths, - METH_VARARGS | METH_KEYWORDS, - bpy_blend_paths_doc, -}; -static PyMethodDef meth_bpy_flip_name = { - "flip_name", - (PyCFunction)bpy_flip_name, - METH_VARARGS | METH_KEYWORDS, - bpy_flip_name_doc, -}; -static PyMethodDef meth_bpy_user_resource = { - "user_resource", - (PyCFunction)bpy_user_resource, - METH_VARARGS | METH_KEYWORDS, - NULL, -}; -static PyMethodDef meth_bpy_system_resource = { - "system_resource", - (PyCFunction)bpy_system_resource, - METH_VARARGS | METH_KEYWORDS, - bpy_system_resource_doc, -}; -static PyMethodDef meth_bpy_resource_path = { - "resource_path", - (PyCFunction)bpy_resource_path, - METH_VARARGS | METH_KEYWORDS, - bpy_resource_path_doc, -}; -static PyMethodDef meth_bpy_driver_secure_code_test = { - "_driver_secure_code_test", - (PyCFunction)bpy_driver_secure_code_test, - METH_VARARGS | METH_KEYWORDS, - bpy_driver_secure_code_test_doc, -}; -static PyMethodDef meth_bpy_escape_identifier = { - "escape_identifier", - (PyCFunction)bpy_escape_identifier, - METH_O, - bpy_escape_identifier_doc, -}; -static PyMethodDef meth_bpy_unescape_identifier = { - "unescape_identifier", - (PyCFunction)bpy_unescape_identifier, - METH_O, - bpy_unescape_identifier_doc, -}; -static PyMethodDef meth_bpy_context_members = { - "context_members", - (PyCFunction)bpy_context_members, - METH_NOARGS, - bpy_context_members_doc, -}; -static PyMethodDef meth_bpy_rna_enum_items_static = { - "rna_enum_items_static", - (PyCFunction)bpy_rna_enum_items_static, - METH_NOARGS, - bpy_rna_enum_items_static_doc, +static PyMethodDef bpy_methods[] = { + {"script_paths", (PyCFunction)bpy_script_paths, METH_NOARGS, bpy_script_paths_doc}, + {"blend_paths", + (PyCFunction)bpy_blend_paths, + METH_VARARGS | METH_KEYWORDS, + bpy_blend_paths_doc}, + {"flip_name", (PyCFunction)bpy_flip_name, METH_VARARGS | METH_KEYWORDS, bpy_flip_name_doc}, + {"user_resource", (PyCFunction)bpy_user_resource, METH_VARARGS | METH_KEYWORDS, NULL}, + {"system_resource", + (PyCFunction)bpy_system_resource, + METH_VARARGS | METH_KEYWORDS, + bpy_system_resource_doc}, + {"resource_path", + (PyCFunction)bpy_resource_path, + METH_VARARGS | METH_KEYWORDS, + bpy_resource_path_doc}, + {"_driver_secure_code_test", + (PyCFunction)bpy_driver_secure_code_test, + METH_VARARGS | METH_KEYWORDS, + bpy_driver_secure_code_test_doc}, + {"escape_identifier", (PyCFunction)bpy_escape_identifier, METH_O, bpy_escape_identifier_doc}, + {"unescape_identifier", + (PyCFunction)bpy_unescape_identifier, + METH_O, + bpy_unescape_identifier_doc}, + {"context_members", (PyCFunction)bpy_context_members, METH_NOARGS, bpy_context_members_doc}, + {"rna_enum_items_static", + (PyCFunction)bpy_rna_enum_items_static, + METH_NOARGS, + bpy_rna_enum_items_static_doc}, + {NULL, NULL, 0, NULL}, }; static PyObject *bpy_import_test(const char *modname) @@ -682,38 +648,12 @@ void BPy_init_modules(struct bContext *C) /* Register methods and property get/set for RNA types. */ BPY_rna_types_extend_capi(); - /* utility func's that have nowhere else to go */ - PyModule_AddObject(mod, - meth_bpy_script_paths.ml_name, - (PyObject *)PyCFunction_New(&meth_bpy_script_paths, NULL)); - PyModule_AddObject( - mod, meth_bpy_blend_paths.ml_name, (PyObject *)PyCFunction_New(&meth_bpy_blend_paths, NULL)); - PyModule_AddObject(mod, - meth_bpy_user_resource.ml_name, - (PyObject *)PyCFunction_New(&meth_bpy_user_resource, NULL)); - PyModule_AddObject(mod, - meth_bpy_system_resource.ml_name, - (PyObject *)PyCFunction_New(&meth_bpy_system_resource, NULL)); - PyModule_AddObject(mod, - meth_bpy_resource_path.ml_name, - (PyObject *)PyCFunction_New(&meth_bpy_resource_path, NULL)); - PyModule_AddObject(mod, - meth_bpy_driver_secure_code_test.ml_name, - (PyObject *)PyCFunction_New(&meth_bpy_driver_secure_code_test, NULL)); - PyModule_AddObject(mod, - meth_bpy_escape_identifier.ml_name, - (PyObject *)PyCFunction_New(&meth_bpy_escape_identifier, NULL)); - PyModule_AddObject(mod, - meth_bpy_unescape_identifier.ml_name, - (PyObject *)PyCFunction_New(&meth_bpy_unescape_identifier, NULL)); - PyModule_AddObject( - mod, meth_bpy_flip_name.ml_name, (PyObject *)PyCFunction_New(&meth_bpy_flip_name, NULL)); - PyModule_AddObject(mod, - meth_bpy_context_members.ml_name, - (PyObject *)PyCFunction_New(&meth_bpy_context_members, NULL)); - PyModule_AddObject(mod, - meth_bpy_rna_enum_items_static.ml_name, - (PyObject *)PyCFunction_New(&meth_bpy_rna_enum_items_static, NULL)); + for (int i = 0; bpy_methods[i].ml_name; i++) { + PyMethodDef *m = &bpy_methods[i]; + /* Currently there is no need to support these. */ + BLI_assert((m->ml_flags & (METH_CLASS | METH_STATIC)) == 0); + PyModule_AddObject(mod, m->ml_name, (PyObject *)PyCFunction_New(m, NULL)); + } /* register funcs (bpy_rna.c) */ PyModule_AddObject(mod, -- cgit v1.2.3 From 6e6da22eb0f48c1189427040d765145766b366c0 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Tue, 12 Jul 2022 09:42:19 +0200 Subject: Fix: crash when iterating over all attributes --- source/blender/blenkernel/BKE_attribute.hh | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/source/blender/blenkernel/BKE_attribute.hh b/source/blender/blenkernel/BKE_attribute.hh index b92d0d4326b..05ab4f1f1f1 100644 --- a/source/blender/blenkernel/BKE_attribute.hh +++ b/source/blender/blenkernel/BKE_attribute.hh @@ -351,8 +351,9 @@ class AttributeAccessor { /** * The data that actually owns the attributes, for example, a pointer to a #Mesh or #PointCloud * Most commonly this is a pointer to a #Mesh or #PointCloud. - * Under some circumstances this can be null. In that case most methods can't be used. Just e.g. - * the #domain_size method works and returns 0 for every domain. + * Under some circumstances this can be null. In that case most methods can't be used. Allowed + * methods are #domain_size, #for_all and #is_builtin. We could potentially make these methods + * accessible without #AttributeAccessor and then #owner_ could always be non-null. * * \note This class cannot modify the owner's attributes, but the pointer is still non-const, so * this class can be a base class for the mutable version. @@ -509,7 +510,10 @@ class AttributeAccessor { */ bool for_all(const AttributeForeachCallback fn) const { - return fn_->for_all(owner_, fn); + if (owner_ != nullptr) { + return fn_->for_all(owner_, fn); + } + return true; } /** -- cgit v1.2.3 From 8bd32019cad3cf0c5f3ce51c12612d76ee5bf04b Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Mon, 11 Jul 2022 17:25:13 +0200 Subject: Fix threading crash due to conflict in mesh wrapper type A mesh wrapper might be being accessed for read-only from one thread while another thread converts the wrapper type to something else. The proposes solution is to defer assignment of the mesh wrapper type until the wrapper is fully converted. The good thing about this approach is that it does not introduce extra synchronization (and, potentially, evaluation pipeline stalls). The downside is that it might not work with all possible wrapper types in the future. If a wrapper type which does not clear data separation is ever added in the future we will re-consider the threading safety then. Unfortunately, some changes outside of the mesh wrapper file are to be made to allow "incremental" construction of the mesh prior changing its wrapper type. Unfortunately, there is no simplified file which demonstrates the issue. It was investigated using Heist production file checked at the revision 1228: `pro/lib/char/einar/einar.shading.blend`. The repro case is simple: tab into edit mode, possibly few times. The gist is that there several surface deform and shrinkwrap modifiers which uses the same target. While one of them is building BVH tree (which changes wrapper type) the other one accesses it for read-only via `BKE_mesh_wrapper_vert_coords_copy_with_mat4()`. Differential Revision: https://developer.blender.org/D15424 --- source/blender/blenkernel/BKE_mesh.h | 12 ++++++++-- source/blender/blenkernel/intern/DerivedMesh.cc | 28 +++++++++++++++--------- source/blender/blenkernel/intern/mesh.cc | 5 +++++ source/blender/blenkernel/intern/mesh_wrapper.cc | 14 ++++++------ 4 files changed, 40 insertions(+), 19 deletions(-) diff --git a/source/blender/blenkernel/BKE_mesh.h b/source/blender/blenkernel/BKE_mesh.h index 731c9872aae..6c61068b1c2 100644 --- a/source/blender/blenkernel/BKE_mesh.h +++ b/source/blender/blenkernel/BKE_mesh.h @@ -85,9 +85,17 @@ struct Mesh *BKE_mesh_from_bmesh_for_eval_nomain(struct BMesh *bm, * Add original index (#CD_ORIGINDEX) layers if they don't already exist. This is meant to be used * when creating an evaluated mesh from an original edit mode mesh, to allow mapping from the * evaluated vertices to the originals. + * + * The mesh is expected to of a `ME_WRAPPER_TYPE_MDATA` wrapper type. This is asserted. */ void BKE_mesh_ensure_default_orig_index_customdata(struct Mesh *mesh); +/** + * Same as #BKE_mesh_ensure_default_orig_index_customdata but does not perform any checks: they + * must be done by the caller. + */ +void BKE_mesh_ensure_default_orig_index_customdata_no_check(struct Mesh *mesh); + /** * Find the index of the loop in 'poly' which references vertex, * returns -1 if not found @@ -1002,8 +1010,8 @@ void BKE_mesh_calc_edges(struct Mesh *mesh, bool keep_existing_edges, bool selec void BKE_mesh_calc_edges_tessface(struct Mesh *mesh); /* In DerivedMesh.cc */ -void BKE_mesh_wrapper_deferred_finalize(struct Mesh *me_eval, - const struct CustomData_MeshMasks *cd_mask_finalize); +void BKE_mesh_wrapper_deferred_finalize_mdata(struct Mesh *me_eval, + const struct CustomData_MeshMasks *cd_mask_finalize); /* **** Depsgraph evaluation **** */ diff --git a/source/blender/blenkernel/intern/DerivedMesh.cc b/source/blender/blenkernel/intern/DerivedMesh.cc index c2ea01bcadf..a29d8726f21 100644 --- a/source/blender/blenkernel/intern/DerivedMesh.cc +++ b/source/blender/blenkernel/intern/DerivedMesh.cc @@ -84,6 +84,8 @@ static ThreadRWMutex loops_cache_lock = PTHREAD_RWLOCK_INITIALIZER; static void mesh_init_origspace(Mesh *mesh); static void editbmesh_calc_modifier_final_normals(Mesh *mesh_final, const CustomData_MeshMasks *final_datamask); +static void editbmesh_calc_modifier_final_normals_or_defer( + Mesh *mesh_final, const CustomData_MeshMasks *final_datamask); /* -------------------------------------------------------------------- */ @@ -663,8 +665,8 @@ static void mesh_calc_finalize(const Mesh *mesh_input, Mesh *mesh_eval) mesh_eval->edit_mesh = mesh_input->edit_mesh; } -void BKE_mesh_wrapper_deferred_finalize(Mesh *me_eval, - const CustomData_MeshMasks *cd_mask_finalize) +void BKE_mesh_wrapper_deferred_finalize_mdata(Mesh *me_eval, + const CustomData_MeshMasks *cd_mask_finalize) { if (me_eval->runtime.wrapper_type_finalize & (1 << ME_WRAPPER_TYPE_BMESH)) { editbmesh_calc_modifier_final_normals(me_eval, cd_mask_finalize); @@ -1286,12 +1288,6 @@ bool editbmesh_modifier_is_enabled(const Scene *scene, static void editbmesh_calc_modifier_final_normals(Mesh *mesh_final, const CustomData_MeshMasks *final_datamask) { - if (mesh_final->runtime.wrapper_type != ME_WRAPPER_TYPE_MDATA) { - /* Generated at draw time. */ - mesh_final->runtime.wrapper_type_finalize = (1 << mesh_final->runtime.wrapper_type); - return; - } - const bool calc_loop_normals = ((mesh_final->flag & ME_AUTOSMOOTH) != 0 || (final_datamask->lmask & CD_MASK_NORMAL) != 0); @@ -1319,6 +1315,18 @@ static void editbmesh_calc_modifier_final_normals(Mesh *mesh_final, } } +static void editbmesh_calc_modifier_final_normals_or_defer( + Mesh *mesh_final, const CustomData_MeshMasks *final_datamask) +{ + if (mesh_final->runtime.wrapper_type != ME_WRAPPER_TYPE_MDATA) { + /* Generated at draw time. */ + mesh_final->runtime.wrapper_type_finalize = (1 << mesh_final->runtime.wrapper_type); + return; + } + + editbmesh_calc_modifier_final_normals(mesh_final, final_datamask); +} + static void editbmesh_calc_modifiers(struct Depsgraph *depsgraph, const Scene *scene, Object *ob, @@ -1598,9 +1606,9 @@ static void editbmesh_calc_modifiers(struct Depsgraph *depsgraph, } /* Compute normals. */ - editbmesh_calc_modifier_final_normals(mesh_final, &final_datamask); + editbmesh_calc_modifier_final_normals_or_defer(mesh_final, &final_datamask); if (mesh_cage && (mesh_cage != mesh_final)) { - editbmesh_calc_modifier_final_normals(mesh_cage, &final_datamask); + editbmesh_calc_modifier_final_normals_or_defer(mesh_cage, &final_datamask); } /* Return final mesh. */ diff --git a/source/blender/blenkernel/intern/mesh.cc b/source/blender/blenkernel/intern/mesh.cc index 2a14370bf93..cf05dc0404e 100644 --- a/source/blender/blenkernel/intern/mesh.cc +++ b/source/blender/blenkernel/intern/mesh.cc @@ -1190,6 +1190,11 @@ static void ensure_orig_index_layer(CustomData &data, const int size) void BKE_mesh_ensure_default_orig_index_customdata(Mesh *mesh) { BLI_assert(mesh->runtime.wrapper_type == ME_WRAPPER_TYPE_MDATA); + BKE_mesh_ensure_default_orig_index_customdata_no_check(mesh); +} + +void BKE_mesh_ensure_default_orig_index_customdata_no_check(Mesh *mesh) +{ ensure_orig_index_layer(mesh->vdata, mesh->totvert); ensure_orig_index_layer(mesh->edata, mesh->totedge); ensure_orig_index_layer(mesh->pdata, mesh->totpoly); diff --git a/source/blender/blenkernel/intern/mesh_wrapper.cc b/source/blender/blenkernel/intern/mesh_wrapper.cc index 0362e4866e3..0b61b876abe 100644 --- a/source/blender/blenkernel/intern/mesh_wrapper.cc +++ b/source/blender/blenkernel/intern/mesh_wrapper.cc @@ -103,11 +103,7 @@ void BKE_mesh_wrapper_ensure_mdata(Mesh *me) /* Must isolate multithreaded tasks while holding a mutex lock. */ blender::threading::isolate_task([&]() { - const eMeshWrapperType geom_type_orig = static_cast( - me->runtime.wrapper_type); - me->runtime.wrapper_type = ME_WRAPPER_TYPE_MDATA; - - switch (geom_type_orig) { + switch (static_cast(me->runtime.wrapper_type)) { case ME_WRAPPER_TYPE_MDATA: case ME_WRAPPER_TYPE_SUBD: { break; /* Quiet warning. */ @@ -132,7 +128,7 @@ void BKE_mesh_wrapper_ensure_mdata(Mesh *me) * There is also a performance aspect, where this also assumes that original indices are * always needed when converting an edit mesh to a mesh. That might be wrong, but it's not * harmful. */ - BKE_mesh_ensure_default_orig_index_customdata(me); + BKE_mesh_ensure_default_orig_index_customdata_no_check(me); EditMeshData *edit_data = me->runtime.edit_data; if (edit_data->vertexCos) { @@ -144,8 +140,12 @@ void BKE_mesh_wrapper_ensure_mdata(Mesh *me) } if (me->runtime.wrapper_type_finalize) { - BKE_mesh_wrapper_deferred_finalize(me, &me->runtime.cd_mask_extra); + BKE_mesh_wrapper_deferred_finalize_mdata(me, &me->runtime.cd_mask_extra); } + + /* Keep type assignment last, so that read-only access only uses the mdata code paths after all + * the underlying data has been initialized. */ + me->runtime.wrapper_type = ME_WRAPPER_TYPE_MDATA; }); BLI_mutex_unlock(mesh_eval_mutex); -- cgit v1.2.3 From bb3a53884394908ba34459c38d419e6c4abe6107 Mon Sep 17 00:00:00 2001 From: Siddhartha Jejurkar Date: Tue, 12 Jul 2022 19:27:48 +1000 Subject: Fix: Incorrect coordinates used in BLI_rct*_isect_segment functions Ref D15330. --- source/blender/blenlib/intern/rct.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/blender/blenlib/intern/rct.c b/source/blender/blenlib/intern/rct.c index 0bb606c288e..7248db5b718 100644 --- a/source/blender/blenlib/intern/rct.c +++ b/source/blender/blenlib/intern/rct.c @@ -265,7 +265,7 @@ bool BLI_rcti_isect_segment(const rcti *rect, const int s1[2], const int s2[2]) /* diagonal: [/] */ tvec1[0] = rect->xmin; tvec1[1] = rect->ymin; - tvec2[0] = rect->xmin; + tvec2[0] = rect->xmax; tvec2[1] = rect->ymax; if (isect_segments_i(s1, s2, tvec1, tvec2)) { return true; @@ -311,7 +311,7 @@ bool BLI_rctf_isect_segment(const rctf *rect, const float s1[2], const float s2[ /* diagonal: [/] */ tvec1[0] = rect->xmin; tvec1[1] = rect->ymin; - tvec2[0] = rect->xmin; + tvec2[0] = rect->xmax; tvec2[1] = rect->ymax; if (isect_segments_fl(s1, s2, tvec1, tvec2)) { return true; -- cgit v1.2.3 From 52b7f2b089071a6d2281e369ab820c9406e06c4e Mon Sep 17 00:00:00 2001 From: Siddhartha Jejurkar Date: Tue, 12 Jul 2022 19:39:57 +1000 Subject: UV: Box and lasso selection for partially intersecting edges In UV edge mode, box and lasso selections allow edge selections only when the entire edge is contained within the selection area. This doesn't consider any edges that partially overlap with the selection area. This is now fixed by adding a second pass, similar to how these operators work for edit-mesh selections. Now if both operators are unable to find any edges contained within the selection area, then they will perform a second pass which checks for edges that partially intersect with the selection area. Now edge selection in the UV editor matches edit-mesh edge-selection when drawing wire-frame. Resolves T99443. Ref D15362 --- source/blender/editors/include/UI_view2d.h | 6 +++ source/blender/editors/interface/view2d.cc | 35 ++++++++++++++ source/blender/editors/uvedit/uvedit_select.c | 69 +++++++++++++++++++++++++++ 3 files changed, 110 insertions(+) diff --git a/source/blender/editors/include/UI_view2d.h b/source/blender/editors/include/UI_view2d.h index 5c4eb254462..e508c96b4f1 100644 --- a/source/blender/editors/include/UI_view2d.h +++ b/source/blender/editors/include/UI_view2d.h @@ -309,6 +309,12 @@ float UI_view2d_view_to_region_y(const struct View2D *v2d, float y); bool UI_view2d_view_to_region_clip( const struct View2D *v2d, float x, float y, int *r_region_x, int *r_region_y) ATTR_NONNULL(); +bool UI_view2d_view_to_region_segment_clip(const View2D *v2d, + const float xy_a[2], + const float xy_b[2], + int r_region_a[2], + int r_region_b[2]) ATTR_NONNULL(); + /** * Convert from 2d-view space to screen/region space * diff --git a/source/blender/editors/interface/view2d.cc b/source/blender/editors/interface/view2d.cc index ee4bfd351ae..1bf7e25b154 100644 --- a/source/blender/editors/interface/view2d.cc +++ b/source/blender/editors/interface/view2d.cc @@ -1695,6 +1695,41 @@ void UI_view2d_view_to_region_fl( *r_region_y = v2d->mask.ymin + (y * BLI_rcti_size_y(&v2d->mask)); } +bool UI_view2d_view_to_region_segment_clip(const View2D *v2d, + const float xy_a[2], + const float xy_b[2], + int r_region_a[2], + int r_region_b[2]) +{ + rctf rect_unit; + rect_unit.xmin = rect_unit.ymin = 0.0f; + rect_unit.xmax = rect_unit.ymax = 1.0f; + + /* Express given coordinates as proportional values. */ + const float s_a[2] = { + (xy_a[0] - v2d->cur.xmin) / BLI_rctf_size_x(&v2d->cur), + (xy_a[1] - v2d->cur.ymin) / BLI_rctf_size_y(&v2d->cur), + }; + const float s_b[2] = { + (xy_b[0] - v2d->cur.xmin) / BLI_rctf_size_x(&v2d->cur), + (xy_b[1] - v2d->cur.ymin) / BLI_rctf_size_y(&v2d->cur), + }; + + /* Set initial value in case coordinates lie outside bounds. */ + r_region_a[0] = r_region_b[0] = r_region_a[1] = r_region_b[1] = V2D_IS_CLIPPED; + + if (BLI_rctf_isect_segment(&rect_unit, s_a, s_b)) { + r_region_a[0] = (int)(v2d->mask.xmin + (s_a[0] * BLI_rcti_size_x(&v2d->mask))); + r_region_a[1] = (int)(v2d->mask.ymin + (s_a[1] * BLI_rcti_size_y(&v2d->mask))); + r_region_b[0] = (int)(v2d->mask.xmin + (s_b[0] * BLI_rcti_size_x(&v2d->mask))); + r_region_b[1] = (int)(v2d->mask.ymin + (s_b[1] * BLI_rcti_size_y(&v2d->mask))); + + return true; + } + + return false; +} + void UI_view2d_view_to_region_rcti(const View2D *v2d, const rctf *rect_src, rcti *rect_dst) { const float cur_size[2] = {BLI_rctf_size_x(&v2d->cur), BLI_rctf_size_y(&v2d->cur)}; diff --git a/source/blender/editors/uvedit/uvedit_select.c b/source/blender/editors/uvedit/uvedit_select.c index db834f6a0fd..b5a564fd984 100644 --- a/source/blender/editors/uvedit/uvedit_select.c +++ b/source/blender/editors/uvedit/uvedit_select.c @@ -3582,6 +3582,7 @@ static int uv_box_select_exec(bContext *C, wmOperator *op) } } else if (use_edge && !pinned) { + bool do_second_pass = true; BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { if (!uvedit_face_visible_test(scene, efa)) { continue; @@ -3596,11 +3597,35 @@ static int uv_box_select_exec(bContext *C, wmOperator *op) uvedit_edge_select_set_with_sticky( scene, em, l_prev, select, false, cd_loop_uv_offset); changed = true; + do_second_pass = false; } l_prev = l; luv_prev = luv; } } + /* Do a second pass if no complete edges could be selected. + * This matches wire-frame edit-mesh selection in the 3D view. */ + if (do_second_pass) { + /* Second pass to check if edges partially overlap with the selection area (box). */ + BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { + if (!uvedit_face_visible_test(scene, efa)) { + continue; + } + BMLoop *l_prev = BM_FACE_FIRST_LOOP(efa)->prev; + MLoopUV *luv_prev = BM_ELEM_CD_GET_VOID_P(l_prev, cd_loop_uv_offset); + + BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { + luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); + if (BLI_rctf_isect_segment(&rectf, luv_prev->uv, luv->uv)) { + uvedit_edge_select_set_with_sticky( + scene, em, l_prev, select, false, cd_loop_uv_offset); + changed = true; + } + l_prev = l; + luv_prev = luv; + } + } + } } else { /* other selection modes */ @@ -3920,6 +3945,24 @@ static bool do_lasso_select_mesh_uv_is_point_inside(const ARegion *region, return false; } +static bool do_lasso_select_mesh_uv_is_edge_inside(const ARegion *region, + const rcti *clip_rect, + const int mcoords[][2], + const int mcoords_len, + const float co_test_a[2], + const float co_test_b[2]) +{ + int co_screen_a[2], co_screen_b[2]; + if (UI_view2d_view_to_region_segment_clip( + ®ion->v2d, co_test_a, co_test_b, co_screen_a, co_screen_b) && + BLI_rcti_isect_segment(clip_rect, co_screen_a, co_screen_b) && + BLI_lasso_is_edge_inside( + mcoords, mcoords_len, UNPACK2(co_screen_a), UNPACK2(co_screen_b), V2D_IS_CLIPPED)) { + return true; + } + return false; +} + static bool do_lasso_select_mesh_uv(bContext *C, const int mcoords[][2], const int mcoords_len, @@ -3988,6 +4031,7 @@ static bool do_lasso_select_mesh_uv(bContext *C, } } else if (use_edge) { + bool do_second_pass = true; BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { if (!uvedit_face_visible_test(scene, efa)) { continue; @@ -4004,12 +4048,37 @@ static bool do_lasso_select_mesh_uv(bContext *C, region, &rect, mcoords, mcoords_len, luv_prev->uv)) { uvedit_edge_select_set_with_sticky( scene, em, l_prev, select, false, cd_loop_uv_offset); + do_second_pass = false; changed = true; } l_prev = l; luv_prev = luv; } } + /* Do a second pass if no complete edges could be selected. + * This matches wire-frame edit-mesh selection in the 3D view. */ + if (do_second_pass) { + /* Second pass to check if edges partially overlap with the selection area (lasso). */ + BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { + if (!uvedit_face_visible_test(scene, efa)) { + continue; + } + BMLoop *l_prev = BM_FACE_FIRST_LOOP(efa)->prev; + MLoopUV *luv_prev = BM_ELEM_CD_GET_VOID_P(l_prev, cd_loop_uv_offset); + + BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { + MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); + if (do_lasso_select_mesh_uv_is_edge_inside( + region, &rect, mcoords, mcoords_len, luv->uv, luv_prev->uv)) { + uvedit_edge_select_set_with_sticky( + scene, em, l_prev, select, false, cd_loop_uv_offset); + changed = true; + } + l_prev = l; + luv_prev = luv; + } + } + } } else { /* Vert Selection. */ BM_mesh_elem_hflag_disable_all(em->bm, BM_VERT, BM_ELEM_TAG, false); -- cgit v1.2.3 From 4344b2bf19e5f678de0d2cd5311a10390f123578 Mon Sep 17 00:00:00 2001 From: RedMser Date: Tue, 12 Jul 2022 12:56:53 +0200 Subject: Markers: Make delete confirmation depend on key used Add a 'Delete Confirmation' operator property to the 'Delete Marker' operator. This determines whether the user is asked to confirm the deletion or not. Defaults so that only {key X} ({key Backspace} for industry compatible keymap) prompts for deletion, whereas {key Del} does not show the confirmation popup. This also makes the default keymap for marker deletion consistent with the common delete operators (such as objects and keyframes). Reviewed By: sybren Differential Revision: https://developer.blender.org/D13818 --- release/scripts/presets/keyconfig/keymap_data/blender_default.py | 2 +- .../scripts/presets/keyconfig/keymap_data/industry_compatible_data.py | 2 +- source/blender/editors/animation/anim_markers.c | 3 ++- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/release/scripts/presets/keyconfig/keymap_data/blender_default.py b/release/scripts/presets/keyconfig/keymap_data/blender_default.py index 9c7aa67ddad..e1e88a0e48d 100644 --- a/release/scripts/presets/keyconfig/keymap_data/blender_default.py +++ b/release/scripts/presets/keyconfig/keymap_data/blender_default.py @@ -1027,7 +1027,7 @@ def km_markers(params): ("marker.select_box", {"type": 'B', "value": 'PRESS'}, None), *_template_items_select_actions(params, "marker.select_all"), ("marker.delete", {"type": 'X', "value": 'PRESS'}, None), - ("marker.delete", {"type": 'DEL', "value": 'PRESS'}, None), + ("marker.delete", {"type": 'DEL', "value": 'PRESS'}, {"properties": [("confirm", False)]}), op_panel("TOPBAR_PT_name_marker", {"type": 'F2', "value": 'PRESS'}, [("keep_open", False)]), ("marker.move", {"type": 'G', "value": 'PRESS'}, None), ("marker.camera_bind", {"type": 'B', "value": 'PRESS', "ctrl": True}, None), diff --git a/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py b/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py index 8feaa7e928f..c9d66afb796 100644 --- a/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py +++ b/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py @@ -844,7 +844,7 @@ def km_markers(params): "shift": True}, {"properties": [("action", 'DESELECT')]}), ("marker.select_all", {"type": 'I', "value": 'PRESS', "ctrl": True}, {"properties": [("action", 'INVERT')]}), ("marker.delete", {"type": 'BACK_SPACE', "value": 'PRESS'}, None), - ("marker.delete", {"type": 'DEL', "value": 'PRESS'}, None), + ("marker.delete", {"type": 'DEL', "value": 'PRESS'}, {"properties": [("confirm", False)]}), op_panel("TOPBAR_PT_name_marker", {"type": 'RET', "value": 'PRESS'}, [("keep_open", False)]), ("marker.move", {"type": 'W', "value": 'PRESS'}, None), ]) diff --git a/source/blender/editors/animation/anim_markers.c b/source/blender/editors/animation/anim_markers.c index 03a2caf4b7d..3608140a29d 100644 --- a/source/blender/editors/animation/anim_markers.c +++ b/source/blender/editors/animation/anim_markers.c @@ -1588,12 +1588,13 @@ static void MARKER_OT_delete(wmOperatorType *ot) ot->idname = "MARKER_OT_delete"; /* api callbacks */ - ot->invoke = WM_operator_confirm; + ot->invoke = WM_operator_confirm_or_exec; ot->exec = ed_marker_delete_exec; ot->poll = ed_markers_poll_selected_no_locked_markers; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + WM_operator_properties_confirm_or_exec(ot); } /** \} */ -- cgit v1.2.3 From 57097e9a8515048b580459354fa197f78a442409 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Tue, 12 Jul 2022 14:27:50 +0200 Subject: Fix T99261: partial LibOverride creation would not properly remap all needed data. When creating partial overrides, there may also be need to reamap usage of linked data towards already existing overrides, in newly created overrides. --- source/blender/blenkernel/intern/lib_override.cc | 51 ++++++++++++++++++------ 1 file changed, 38 insertions(+), 13 deletions(-) diff --git a/source/blender/blenkernel/intern/lib_override.cc b/source/blender/blenkernel/intern/lib_override.cc index cdea97b6c6a..1969ec9196c 100644 --- a/source/blender/blenkernel/intern/lib_override.cc +++ b/source/blender/blenkernel/intern/lib_override.cc @@ -428,14 +428,40 @@ static void lib_override_prefill_newid_from_existing_overrides(Main *bmain, ID * { ID *id_iter; FOREACH_MAIN_ID_BEGIN (bmain, id_iter) { - if (ID_IS_OVERRIDE_LIBRARY_REAL(id_iter) && - id_iter->override_library->hierarchy_root == id_hierarchy_root) { - id_iter->override_library->reference->newid = id_iter; + ID *id = id_iter; + if (GS(id_iter->name) == ID_KE) { + id = reinterpret_cast(id_iter)->from; + BLI_assert(id != nullptr); + } + if (ID_IS_OVERRIDE_LIBRARY_REAL(id) && + id->override_library->hierarchy_root == id_hierarchy_root) { + id->override_library->reference->newid = id; + if (GS(id_iter->name) == ID_KE) { + Key *reference_key = BKE_key_from_id(id->override_library->reference); + if (reference_key != nullptr) { + reference_key->id.newid = id_iter; + } + } } } FOREACH_MAIN_ID_END; } +static void lib_override_remapper_overrides_add(IDRemapper *id_remapper, + ID *reference_id, + ID *local_id) +{ + BKE_id_remapper_add(id_remapper, reference_id, local_id); + + Key *reference_key, *local_key = nullptr; + if ((reference_key = BKE_key_from_id(reference_id)) != nullptr) { + local_key = BKE_key_from_id(reference_id->newid); + BLI_assert(local_key != nullptr); + + BKE_id_remapper_add(id_remapper, &reference_key->id, &local_key->id); + } +} + /* TODO: Make this static local function instead? API is becoming complex, and it's not used * outside of this file anyway. */ bool BKE_lib_override_library_create_from_tag(Main *bmain, @@ -544,6 +570,7 @@ bool BKE_lib_override_library_create_from_tag(Main *bmain, BLI_assert(id_hierarchy_root != nullptr); LinkNode *relinked_ids = nullptr; + IDRemapper *id_remapper = BKE_id_remapper_create(); /* Still checking the whole Main, that way we can tag other local IDs as needing to be * remapped to use newly created overriding IDs, if needed. */ ID *id; @@ -568,6 +595,13 @@ bool BKE_lib_override_library_create_from_tag(Main *bmain, * consider we should also relink it, as part of recursive resync. */ if ((other_id->tag & LIB_TAG_DOIT) != 0 && other_id->lib != id_root_reference->lib) { BLI_linklist_prepend(&relinked_ids, other_id); + if (ID_IS_OVERRIDE_LIBRARY_REAL(other_id) && + other_id->override_library->hierarchy_root == id_hierarchy_root) { + reference_id = other_id->override_library->reference; + ID *local_id = reference_id->newid; + BLI_assert(other_id == local_id); + lib_override_remapper_overrides_add(id_remapper, reference_id, local_id); + } } if (other_id != id) { other_id->lib = id_root_reference->lib; @@ -575,7 +609,6 @@ bool BKE_lib_override_library_create_from_tag(Main *bmain, } FOREACH_MAIN_ID_END; - IDRemapper *id_remapper = BKE_id_remapper_create(); for (todo_id_iter = static_cast(todo_ids.first); todo_id_iter != nullptr; todo_id_iter = todo_id_iter->next) { reference_id = static_cast(todo_id_iter->data); @@ -587,15 +620,7 @@ bool BKE_lib_override_library_create_from_tag(Main *bmain, local_id->override_library->hierarchy_root = id_hierarchy_root; - BKE_id_remapper_add(id_remapper, reference_id, local_id); - - Key *reference_key, *local_key = nullptr; - if ((reference_key = BKE_key_from_id(reference_id)) != nullptr) { - local_key = BKE_key_from_id(reference_id->newid); - BLI_assert(local_key != nullptr); - - BKE_id_remapper_add(id_remapper, &reference_key->id, &local_key->id); - } + lib_override_remapper_overrides_add(id_remapper, reference_id, local_id); } BKE_libblock_relink_multiple(bmain, -- cgit v1.2.3 From 2d1fe736fabdcccdd46edfbdfc68720498c30e10 Mon Sep 17 00:00:00 2001 From: Germano Cavalcante Date: Tue, 12 Jul 2022 10:11:05 -0300 Subject: Fix T96238: shrinking to zero doesn't actually scales the radius to zero Probably to prevent the radius of a point from being stuck at zero, the `Shrink/Fatten` curve operator sets a minimum value of `0.001` for all points being transformed. This is an inconvenience as these points may have been purposely set to zero on the panel. And it also doesn't follow the convention seen in other operators (which keep the value zero). So remove this limitation. --- .../blender/editors/transform/transform_mode_curveshrinkfatten.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/source/blender/editors/transform/transform_mode_curveshrinkfatten.c b/source/blender/editors/transform/transform_mode_curveshrinkfatten.c index aa4d608de04..7b65417d32b 100644 --- a/source/blender/editors/transform/transform_mode_curveshrinkfatten.c +++ b/source/blender/editors/transform/transform_mode_curveshrinkfatten.c @@ -65,9 +65,7 @@ static void applyCurveShrinkFatten(TransInfo *t, const int UNUSED(mval[2])) *td->val = td->ival * ratio; /* apply PET */ *td->val = (*td->val * td->factor) + ((1.0f - td->factor) * td->ival); - if (*td->val <= 0.0f) { - *td->val = 0.001f; - } + CLAMP_MIN(*td->val, 0.0f); } } } @@ -93,10 +91,6 @@ void initCurveShrinkFatten(TransInfo *t) t->num.unit_sys = t->scene->unit.system; t->num.unit_type[0] = B_UNIT_NONE; -#ifdef USE_NUM_NO_ZERO - t->num.val_flag[0] |= NUM_NO_ZERO; -#endif - t->flag |= T_NO_CONSTRAINT; } -- cgit v1.2.3 From f72cedffb63e08abc71ae1c6d31408457b1005a9 Mon Sep 17 00:00:00 2001 From: Germano Cavalcante Date: Tue, 12 Jul 2022 10:20:01 -0300 Subject: Cleanup: Use interpf instead of repeating the logic This makes the code clearer. --- source/blender/editors/transform/transform_mode_curveshrinkfatten.c | 2 +- source/blender/editors/transform/transform_mode_gpopacity.c | 2 +- source/blender/editors/transform/transform_mode_gpshrinkfatten.c | 2 +- source/blender/editors/transform/transform_mode_maskshrinkfatten.c | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/source/blender/editors/transform/transform_mode_curveshrinkfatten.c b/source/blender/editors/transform/transform_mode_curveshrinkfatten.c index 7b65417d32b..f7f9e14b8ac 100644 --- a/source/blender/editors/transform/transform_mode_curveshrinkfatten.c +++ b/source/blender/editors/transform/transform_mode_curveshrinkfatten.c @@ -64,7 +64,7 @@ static void applyCurveShrinkFatten(TransInfo *t, const int UNUSED(mval[2])) if (td->val) { *td->val = td->ival * ratio; /* apply PET */ - *td->val = (*td->val * td->factor) + ((1.0f - td->factor) * td->ival); + *td->val = interpf(*td->val, td->ival, td->factor); CLAMP_MIN(*td->val, 0.0f); } } diff --git a/source/blender/editors/transform/transform_mode_gpopacity.c b/source/blender/editors/transform/transform_mode_gpopacity.c index 83dce17d104..8b9431b65ea 100644 --- a/source/blender/editors/transform/transform_mode_gpopacity.c +++ b/source/blender/editors/transform/transform_mode_gpopacity.c @@ -74,7 +74,7 @@ static void applyGPOpacity(TransInfo *t, const int UNUSED(mval[2])) if (td->val) { *td->val = td->ival * ratio; /* apply PET */ - *td->val = (*td->val * td->factor) + ((1.0f - td->factor) * td->ival); + *td->val = interpf(*td->val, td->ival, td->factor); CLAMP(*td->val, 0.0f, 1.0f); } } diff --git a/source/blender/editors/transform/transform_mode_gpshrinkfatten.c b/source/blender/editors/transform/transform_mode_gpshrinkfatten.c index 796d5c7ae9c..d8ec7d4ff50 100644 --- a/source/blender/editors/transform/transform_mode_gpshrinkfatten.c +++ b/source/blender/editors/transform/transform_mode_gpshrinkfatten.c @@ -74,7 +74,7 @@ static void applyGPShrinkFatten(TransInfo *t, const int UNUSED(mval[2])) if (td->val) { *td->val = td->ival * ratio; /* apply PET */ - *td->val = (*td->val * td->factor) + ((1.0f - td->factor) * td->ival); + *td->val = interpf(*td->val, td->ival, td->factor); if (*td->val <= 0.0f) { *td->val = 0.001f; } diff --git a/source/blender/editors/transform/transform_mode_maskshrinkfatten.c b/source/blender/editors/transform/transform_mode_maskshrinkfatten.c index 19a3deade63..e2ccf61796b 100644 --- a/source/blender/editors/transform/transform_mode_maskshrinkfatten.c +++ b/source/blender/editors/transform/transform_mode_maskshrinkfatten.c @@ -90,7 +90,7 @@ static void applyMaskShrinkFatten(TransInfo *t, const int UNUSED(mval[2])) } /* apply PET */ - *td->val = (*td->val * td->factor) + ((1.0f - td->factor) * td->ival); + *td->val = interpf(*td->val, td->ival, td->factor); if (*td->val <= 0.0f) { *td->val = 0.001f; } -- cgit v1.2.3 From 4a445c8dc0edfe793e547ac49194df72c372f95a Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Tue, 12 Jul 2022 15:34:40 +0200 Subject: LibOverride: Fix some issues from.revealed by recent previous commit. rB57097e9a8515 did not properly consider case where you have more than one override for a same reference linked ID. Also adds more security checks around shapekeys, since match between override and its linked reference is never ensured either way (fixes a crash reported by @Rik from Blender studio). --- source/blender/blenkernel/intern/lib_override.cc | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/source/blender/blenkernel/intern/lib_override.cc b/source/blender/blenkernel/intern/lib_override.cc index 1969ec9196c..aa3210b64ad 100644 --- a/source/blender/blenkernel/intern/lib_override.cc +++ b/source/blender/blenkernel/intern/lib_override.cc @@ -455,8 +455,10 @@ static void lib_override_remapper_overrides_add(IDRemapper *id_remapper, Key *reference_key, *local_key = nullptr; if ((reference_key = BKE_key_from_id(reference_id)) != nullptr) { - local_key = BKE_key_from_id(reference_id->newid); - BLI_assert(local_key != nullptr); + if (reference_id->newid != nullptr) { + local_key = BKE_key_from_id(reference_id->newid); + BLI_assert(local_key != nullptr); + } BKE_id_remapper_add(id_remapper, &reference_key->id, &local_key->id); } @@ -599,8 +601,9 @@ bool BKE_lib_override_library_create_from_tag(Main *bmain, other_id->override_library->hierarchy_root == id_hierarchy_root) { reference_id = other_id->override_library->reference; ID *local_id = reference_id->newid; - BLI_assert(other_id == local_id); - lib_override_remapper_overrides_add(id_remapper, reference_id, local_id); + if (other_id == local_id) { + lib_override_remapper_overrides_add(id_remapper, reference_id, local_id); + } } } if (other_id != id) { -- cgit v1.2.3 From 47dd42485e19106601167f6be7b5960c4be25167 Mon Sep 17 00:00:00 2001 From: Xavier Hallade Date: Tue, 12 Jul 2022 15:45:46 +0200 Subject: Cycles: fix and enable JIT oneAPI CentOS7 builds for drivers 23570+ The current specific CentOS7 workaround we have for AoT, which is to disable __FAST_MATH__ by using -fhonor-nans, now also fixes the compilation issue for JIT as well since at least driver 23570. --- intern/cycles/blender/addon/properties.py | 2 +- intern/cycles/kernel/CMakeLists.txt | 7 +++---- intern/cycles/kernel/device/oneapi/kernel.cpp | 5 +---- 3 files changed, 5 insertions(+), 9 deletions(-) diff --git a/intern/cycles/blender/addon/properties.py b/intern/cycles/blender/addon/properties.py index e88b65b5119..4a8854fd868 100644 --- a/intern/cycles/blender/addon/properties.py +++ b/intern/cycles/blender/addon/properties.py @@ -1560,7 +1560,7 @@ class CyclesPreferences(bpy.types.AddonPreferences): if sys.platform.startswith("win"): col.label(text="and Windows driver version 101.1660 or newer", icon='BLANK1') elif sys.platform.startswith("linux"): - col.label(text="and Linux driver version xx.xx.28000 or newer", icon='BLANK1') + col.label(text="and Linux driver version xx.xx.23570 or newer", icon='BLANK1') elif device_type == 'METAL': col.label(text="Requires Apple Silicon with macOS 12.2 or newer", icon='BLANK1') col.label(text="or AMD with macOS 12.3 or newer", icon='BLANK1') diff --git a/intern/cycles/kernel/CMakeLists.txt b/intern/cycles/kernel/CMakeLists.txt index b893ff6ef24..57a26edff50 100644 --- a/intern/cycles/kernel/CMakeLists.txt +++ b/intern/cycles/kernel/CMakeLists.txt @@ -844,10 +844,9 @@ if(WITH_CYCLES_DEVICE_ONEAPI) else() list(APPEND sycl_compiler_flags -fPIC) - # avoid getting __FAST_MATH__ to be defined for the graphics compiler on CentOS 7 until the compile-time issue it triggers gets fixed. - if(WITH_CYCLES_ONEAPI_BINARIES) - list(APPEND sycl_compiler_flags -fhonor-nans) - endif() + # We avoid getting __FAST_MATH__ to be defined when building on CentOS 7 until the compilation crash + # it triggers at either AoT or JIT stages gets fixed. + list(APPEND sycl_compiler_flags -fhonor-nans) # add $ORIGIN to cycles_kernel_oneapi.so rpath so libsycl.so and # libpi_level_zero.so can be placed next to it and get found. diff --git a/intern/cycles/kernel/device/oneapi/kernel.cpp b/intern/cycles/kernel/device/oneapi/kernel.cpp index 82910d72105..300e201600c 100644 --- a/intern/cycles/kernel/device/oneapi/kernel.cpp +++ b/intern/cycles/kernel/device/oneapi/kernel.cpp @@ -670,10 +670,7 @@ bool oneapi_enqueue_kernel(KernelContext *kernel_context, } static const int lowest_supported_driver_version_win = 1011660; -/* TODO: once Linux JIT compilation crash from CentOS generated spv is fixed, adjust version below. - * Until then, set CYCLES_ONEAPI_ALL_DEVICES environment variable to avoid getting it filtered out. - */ -static const int lowest_supported_driver_version_neo = 28000; +static const int lowest_supported_driver_version_neo = 23570; static int parse_driver_build_version(const sycl::device &device) { -- cgit v1.2.3 From d58072caf4abb254d5e5f0e3b9de9bef775f287a Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Tue, 12 Jul 2022 16:26:50 +0200 Subject: Fix: missing geometry copy before modifying it A geometry component may reference read-only geometry. In this case it has to be copied before making changes to it. This was caused by rBb876ce2a4a4638142. --- source/blender/blenkernel/intern/geometry_component_curve.cc | 3 ++- source/blender/blenkernel/intern/geometry_component_curves.cc | 3 ++- source/blender/blenkernel/intern/geometry_component_mesh.cc | 3 ++- source/blender/blenkernel/intern/geometry_component_pointcloud.cc | 3 ++- 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/source/blender/blenkernel/intern/geometry_component_curve.cc b/source/blender/blenkernel/intern/geometry_component_curve.cc index 0d899ec7b06..22f105af0f1 100644 --- a/source/blender/blenkernel/intern/geometry_component_curve.cc +++ b/source/blender/blenkernel/intern/geometry_component_curve.cc @@ -1452,7 +1452,8 @@ std::optional CurveComponentLegacy::attributes( std::optional CurveComponentLegacy::attributes_for_write() { - return blender::bke::MutableAttributeAccessor(curve_, + CurveEval *curve = this->get_for_write(); + return blender::bke::MutableAttributeAccessor(curve, blender::bke::get_curve_accessor_functions_ref()); } diff --git a/source/blender/blenkernel/intern/geometry_component_curves.cc b/source/blender/blenkernel/intern/geometry_component_curves.cc index 34c17bedc2c..f803b08e740 100644 --- a/source/blender/blenkernel/intern/geometry_component_curves.cc +++ b/source/blender/blenkernel/intern/geometry_component_curves.cc @@ -644,6 +644,7 @@ std::optional CurveComponent::attributes() cons std::optional CurveComponent::attributes_for_write() { - return blender::bke::MutableAttributeAccessor(curves_ ? &curves_->geometry : nullptr, + Curves *curves = this->get_for_write(); + return blender::bke::MutableAttributeAccessor(curves ? &curves->geometry : nullptr, blender::bke::get_curves_accessor_functions_ref()); } diff --git a/source/blender/blenkernel/intern/geometry_component_mesh.cc b/source/blender/blenkernel/intern/geometry_component_mesh.cc index cb36b9b19f7..cf6681a69be 100644 --- a/source/blender/blenkernel/intern/geometry_component_mesh.cc +++ b/source/blender/blenkernel/intern/geometry_component_mesh.cc @@ -1324,7 +1324,8 @@ std::optional MeshComponent::attributes() const std::optional MeshComponent::attributes_for_write() { - return blender::bke::MutableAttributeAccessor(mesh_, + Mesh *mesh = this->get_for_write(); + return blender::bke::MutableAttributeAccessor(mesh, blender::bke::get_mesh_accessor_functions_ref()); } diff --git a/source/blender/blenkernel/intern/geometry_component_pointcloud.cc b/source/blender/blenkernel/intern/geometry_component_pointcloud.cc index b439a9ba7f8..ccc97f92dbc 100644 --- a/source/blender/blenkernel/intern/geometry_component_pointcloud.cc +++ b/source/blender/blenkernel/intern/geometry_component_pointcloud.cc @@ -226,8 +226,9 @@ std::optional PointCloudComponent::attributes() std::optional PointCloudComponent::attributes_for_write() { + PointCloud *pointcloud = this->get_for_write(); return blender::bke::MutableAttributeAccessor( - pointcloud_, blender::bke::get_pointcloud_accessor_functions_ref()); + pointcloud, blender::bke::get_pointcloud_accessor_functions_ref()); } /** \} */ -- cgit v1.2.3 From 93253d5dcc2938903ae4de3a23fcdb724d202d44 Mon Sep 17 00:00:00 2001 From: Khoi Dau Date: Tue, 12 Jul 2022 11:30:41 -0300 Subject: Fix T99103: crash when displaying or rendering Grease Pencil object On some hardware/systems, blender may crash when adding, rendering or displaying Grease Pencil objects. In `/source/blender/draw/engines/gpencil/shaders/gpencil_vert.glsl`, line 35: ``` gpMaterialFlag gp_flag = floatBitsToInt(gp_mat._flag); ``` `gpMaterialFlag` is of type `uint`. This is a mismatched-type assignment that can cause crashes on some hardware/systems with GLSL that do not support implicit type casting. So use `floatBitsToUint` for type conversion. Differential Revision: https://developer.blender.org/D15433 --- source/blender/draw/engines/gpencil/shaders/gpencil_vert.glsl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/draw/engines/gpencil/shaders/gpencil_vert.glsl b/source/blender/draw/engines/gpencil/shaders/gpencil_vert.glsl index 5f5419bac47..b0ee059cb9d 100644 --- a/source/blender/draw/engines/gpencil/shaders/gpencil_vert.glsl +++ b/source/blender/draw/engines/gpencil/shaders/gpencil_vert.glsl @@ -32,7 +32,7 @@ void main() vec3 vert_N; gpMaterial gp_mat = materials[ma1.x + gpMaterialOffset]; - gpMaterialFlag gp_flag = floatBitsToInt(gp_mat._flag); + gpMaterialFlag gp_flag = floatBitsToUint(gp_mat._flag); gl_Position = gpencil_vertex(ma, ma1, -- cgit v1.2.3 From b767628173446433f12b321d9209f9be11aee58c Mon Sep 17 00:00:00 2001 From: Sebastian Parborg Date: Tue, 12 Jul 2022 16:58:04 +0200 Subject: Fix: Memory leaks in indexer code Reviewed By: Richard Antalik Differential Revision: http://developer.blender.org/D15376 --- source/blender/imbuf/intern/indexer.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/source/blender/imbuf/intern/indexer.c b/source/blender/imbuf/intern/indexer.c index cbc5d984755..00396c01d99 100644 --- a/source/blender/imbuf/intern/indexer.c +++ b/source/blender/imbuf/intern/indexer.c @@ -1098,6 +1098,7 @@ static int indexer_performance_get_decode_rate(FFmpegIndexBuilderContext *contex while (av_read_frame(context->iFormatCtx, packet) >= 0) { if (packet->stream_index != context->videoStream) { + av_packet_unref(packet); continue; } @@ -1121,6 +1122,7 @@ static int indexer_performance_get_decode_rate(FFmpegIndexBuilderContext *contex if (end > start + time_period) { break; } + av_packet_unref(packet); } av_packet_free(&packet); @@ -1145,6 +1147,7 @@ static int indexer_performance_get_max_gop_size(FFmpegIndexBuilderContext *conte while (av_read_frame(context->iFormatCtx, packet) >= 0) { if (packet->stream_index != context->videoStream) { + av_packet_unref(packet); continue; } packet_index++; @@ -1158,6 +1161,7 @@ static int indexer_performance_get_max_gop_size(FFmpegIndexBuilderContext *conte if (packet_index > packets_max) { break; } + av_packet_unref(packet); } av_packet_free(&packet); -- cgit v1.2.3 From 02aefa7659630258f2eb2498947faf398cb375ff Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Tue, 12 Jul 2022 17:09:21 +0200 Subject: Fix: wrong node name in menu --- release/scripts/startup/nodeitems_builtins.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py index e59c98163d7..6f3054100f8 100644 --- a/release/scripts/startup/nodeitems_builtins.py +++ b/release/scripts/startup/nodeitems_builtins.py @@ -73,7 +73,7 @@ def curve_node_items(context): yield NodeItem("GeometryNodeCurveLength") yield NodeItem("GeometryNodeCurveToMesh") yield NodeItem("GeometryNodeCurveToPoints") - yield NodeItem("GeometryNodeDeformCurvesWithSurface") + yield NodeItem("GeometryNodeDeformCurvesOnSurface") yield NodeItem("GeometryNodeFillCurve") yield NodeItem("GeometryNodeFilletCurve") yield NodeItem("GeometryNodeResampleCurve") -- cgit v1.2.3 From 1c382a4940c6e609786c92c795a25eb8ff7ea608 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Tue, 12 Jul 2022 17:10:04 +0200 Subject: Curves: improve error checking in deform curves on surface node --- .../geometry/nodes/node_geo_deform_curves_on_surface.cc | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/source/blender/nodes/geometry/nodes/node_geo_deform_curves_on_surface.cc b/source/blender/nodes/geometry/nodes/node_geo_deform_curves_on_surface.cc index 0a446492c6b..7f03d025db7 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_deform_curves_on_surface.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_deform_curves_on_surface.cc @@ -218,6 +218,12 @@ static void node_geo_exec(GeoNodeExecParams params) return; } const Curves *self_curves_eval = static_cast(self_ob_eval->data); + if (self_curves_eval->surface_uv_map == nullptr || self_curves_eval->surface_uv_map[0] == '\0') { + pass_through_input(); + const char *message = TIP_("Surface UV map not defined"); + params.error_message_add(NodeWarningType::Error, message); + return; + } /* Take surface information from self-object. */ Object *surface_ob_eval = self_curves_eval->surface; const StringRefNull uv_map_name = self_curves_eval->surface_uv_map; @@ -258,12 +264,6 @@ static void node_geo_exec(GeoNodeExecParams params) Curves &curves_id = *curves_geometry.get_curves_for_write(); CurvesGeometry &curves = CurvesGeometry::wrap(curves_id.geometry); - if (uv_map_name.is_empty()) { - pass_through_input(); - const char *message = TIP_("Surface UV map not defined"); - params.error_message_add(NodeWarningType::Error, message); - return; - } if (!mesh_attributes_eval.contains(uv_map_name)) { pass_through_input(); char *message = BLI_sprintfN(TIP_("Evaluated surface missing UV map: %s"), @@ -285,7 +285,7 @@ static void node_geo_exec(GeoNodeExecParams params) TIP_("Evaluated surface missing attribute: rest_position")); return; } - if (curves.surface_uv_coords().is_empty()) { + if (curves.surface_uv_coords().is_empty() && curves.curves_num() > 0) { pass_through_input(); params.error_message_add(NodeWarningType::Error, TIP_("Curves are not attached to any UV map")); -- cgit v1.2.3 From 9f153949f9ee1cbea04687c2a99d9e1e2f891962 Mon Sep 17 00:00:00 2001 From: Erik Abrahamsson Date: Tue, 12 Jul 2022 18:31:33 +0200 Subject: Enable "copy to selected" for new Curves modifiers The operator bpy.ops.object.modifier_copy_to_selected() does not work for the new Curves objects. This is because it isn't added to BKE_object_supports_modifiers. Differential Revision: https://developer.blender.org/D15439 --- source/blender/blenkernel/intern/object.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/source/blender/blenkernel/intern/object.cc b/source/blender/blenkernel/intern/object.cc index f7436b6112c..62ebb45b0ed 100644 --- a/source/blender/blenkernel/intern/object.cc +++ b/source/blender/blenkernel/intern/object.cc @@ -1402,6 +1402,7 @@ bool BKE_object_supports_modifiers(const Object *ob) { return (ELEM(ob->type, OB_MESH, + OB_CURVES, OB_CURVES_LEGACY, OB_SURF, OB_FONT, -- cgit v1.2.3 From 5f09440d5a6ca914651700c31cd8873fe4e496d0 Mon Sep 17 00:00:00 2001 From: Xavier Hallade Date: Tue, 12 Jul 2022 18:29:58 +0200 Subject: Cycles: Make not-compact BVH the default for embree Measurements shown on average a 1.08x speedup for a 1.04x increase in memory usage which is an acceptable trade off for a default setting, although discoverability of such settings influencing memory usage could be improved. Reviewed By: brecht Differential Revision: https://developer.blender.org/D15429 --- intern/cycles/blender/addon/properties.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/intern/cycles/blender/addon/properties.py b/intern/cycles/blender/addon/properties.py index 4a8854fd868..2c926893f9d 100644 --- a/intern/cycles/blender/addon/properties.py +++ b/intern/cycles/blender/addon/properties.py @@ -693,7 +693,7 @@ class CyclesRenderSettings(bpy.types.PropertyGroup): debug_use_compact_bvh: BoolProperty( name="Use Compact BVH", description="Use compact BVH structure (uses less ram but renders slower)", - default=True, + default=False, ) debug_bvh_time_steps: IntProperty( name="BVH Time Steps", -- cgit v1.2.3 From d0a552b5c602479f3a4f542f3735bc94b301139e Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Tue, 12 Jul 2022 18:47:32 +0200 Subject: Fix: set dangling pointer to null The data has been moved somewhere else, the span should not keep a pointer to it. --- source/blender/blenlib/intern/generic_virtual_array.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/source/blender/blenlib/intern/generic_virtual_array.cc b/source/blender/blenlib/intern/generic_virtual_array.cc index 28bc66e8524..1e548d006b2 100644 --- a/source/blender/blenlib/intern/generic_virtual_array.cc +++ b/source/blender/blenlib/intern/generic_virtual_array.cc @@ -323,6 +323,7 @@ GVArraySpan::GVArraySpan(GVArraySpan &&other) else { data_ = owned_data_; } + other.owned_data_ = nullptr; other.data_ = nullptr; other.size_ = 0; } @@ -393,6 +394,7 @@ GMutableVArraySpan::GMutableVArraySpan(GMutableVArraySpan &&other) else { data_ = owned_data_; } + other.owned_data_ = nullptr; other.data_ = nullptr; other.size_ = 0; } -- cgit v1.2.3 From 8f543a73abc42843fb924fc6d849e3055e3ae011 Mon Sep 17 00:00:00 2001 From: Chris Blackbourn Date: Wed, 13 Jul 2022 11:39:06 +1200 Subject: Fix T99659: Improve UV Island calculation with hidden faces. Simplify interface, regularize implementation and some light cleanup. See also: T79304 and D15419. --- source/blender/blenkernel/BKE_mesh_mapping.h | 2 - source/blender/editors/include/ED_mesh.h | 1 - source/blender/editors/mesh/editmesh_utils.c | 84 ++++++++++++---------- source/blender/editors/sculpt_paint/sculpt_uv.c | 14 +--- .../editors/transform/transform_convert_mesh_uv.c | 4 +- source/blender/editors/uvedit/uvedit_ops.c | 2 +- source/blender/editors/uvedit/uvedit_select.c | 2 +- .../blender/editors/uvedit/uvedit_smart_stitch.c | 10 +-- 8 files changed, 54 insertions(+), 65 deletions(-) diff --git a/source/blender/blenkernel/BKE_mesh_mapping.h b/source/blender/blenkernel/BKE_mesh_mapping.h index 163acf062e0..c58bcbea242 100644 --- a/source/blender/blenkernel/BKE_mesh_mapping.h +++ b/source/blender/blenkernel/BKE_mesh_mapping.h @@ -69,8 +69,6 @@ typedef struct UvElementMap { int *islandIndices; } UvElementMap; -#define INVALID_ISLAND ((unsigned int)-1) - /* Connectivity data */ typedef struct MeshElemMap { int *indices; diff --git a/source/blender/editors/include/ED_mesh.h b/source/blender/editors/include/ED_mesh.h index 52044109702..b73b62d5a9d 100644 --- a/source/blender/editors/include/ED_mesh.h +++ b/source/blender/editors/include/ED_mesh.h @@ -137,7 +137,6 @@ void EDBM_update_extern(struct Mesh *me, bool do_tessellation, bool is_destructi */ struct UvElementMap *BM_uv_element_map_create(struct BMesh *bm, const struct Scene *scene, - bool face_selected, bool uv_selected, bool use_winding, bool do_islands); diff --git a/source/blender/editors/mesh/editmesh_utils.c b/source/blender/editors/mesh/editmesh_utils.c index 83968955583..60a666ee755 100644 --- a/source/blender/editors/mesh/editmesh_utils.c +++ b/source/blender/editors/mesh/editmesh_utils.c @@ -593,13 +593,17 @@ UvMapVert *BM_uv_vert_map_at_index(UvVertMap *vmap, uint v) return vmap->vert[v]; } +#define INVALID_ISLAND ((unsigned int)-1) + UvElementMap *BM_uv_element_map_create(BMesh *bm, const Scene *scene, - const bool face_selected, const bool uv_selected, const bool use_winding, const bool do_islands) { + /* In uv sync selection, all UVs are visible. */ + const bool face_selected = !(scene->toolsettings->uv_flag & UV_SYNC_SELECTION); + BMVert *ev; BMFace *efa; BMLoop *l; @@ -623,15 +627,21 @@ UvElementMap *BM_uv_element_map_create(BMesh *bm, /* generate UvElement array */ BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) { - if (!face_selected || BM_elem_flag_test(efa, BM_ELEM_SELECT)) { - if (!uv_selected) { - totuv += efa->len; - } - else { - BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { - if (uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) { - totuv++; - } + if (BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) { + continue; + } + + if (face_selected && !BM_elem_flag_test(efa, BM_ELEM_SELECT)) { + continue; + } + + if (!uv_selected) { + totuv += efa->len; + } + else { + BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { + if (uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) { + totuv++; } } } @@ -649,46 +659,48 @@ UvElementMap *BM_uv_element_map_create(BMesh *bm, "UvElement"); if (use_winding) { - winding = MEM_mallocN(sizeof(*winding) * totfaces, "winding"); + winding = MEM_callocN(sizeof(*winding) * totfaces, "winding"); } BM_ITER_MESH_INDEX (efa, &iter, bm, BM_FACES_OF_MESH, j) { - if (use_winding) { - winding[j] = false; + if (BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) { + continue; } - if (!face_selected || BM_elem_flag_test(efa, BM_ELEM_SELECT)) { - float(*tf_uv)[2] = NULL; - - if (use_winding) { - tf_uv = (float(*)[2])BLI_buffer_reinit_data(&tf_uv_buf, vec2f, efa->len); - } + if (face_selected && !BM_elem_flag_test(efa, BM_ELEM_SELECT)) { + continue; + } - BM_ITER_ELEM_INDEX (l, &liter, efa, BM_LOOPS_OF_FACE, i) { - if (uv_selected && !uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) { - continue; - } + float(*tf_uv)[2] = NULL; - buf->l = l; - buf->separate = 0; - buf->island = INVALID_ISLAND; - buf->loop_of_poly_index = i; + if (use_winding) { + tf_uv = (float(*)[2])BLI_buffer_reinit_data(&tf_uv_buf, vec2f, efa->len); + } - buf->next = element_map->vert[BM_elem_index_get(l->v)]; - element_map->vert[BM_elem_index_get(l->v)] = buf; + BM_ITER_ELEM_INDEX (l, &liter, efa, BM_LOOPS_OF_FACE, i) { + if (uv_selected && !uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) { + continue; + } - if (use_winding) { - luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); - copy_v2_v2(tf_uv[i], luv->uv); - } + buf->l = l; + buf->separate = 0; + buf->island = INVALID_ISLAND; + buf->loop_of_poly_index = i; - buf++; - } + buf->next = element_map->vert[BM_elem_index_get(l->v)]; + element_map->vert[BM_elem_index_get(l->v)] = buf; if (use_winding) { - winding[j] = cross_poly_v2(tf_uv, efa->len) > 0; + luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); + copy_v2_v2(tf_uv[i], luv->uv); } + + buf++; + } + + if (winding) { + winding[j] = cross_poly_v2(tf_uv, efa->len) > 0; } } diff --git a/source/blender/editors/sculpt_paint/sculpt_uv.c b/source/blender/editors/sculpt_paint/sculpt_uv.c index 8e1f4f4d495..5c2dff7b252 100644 --- a/source/blender/editors/sculpt_paint/sculpt_uv.c +++ b/source/blender/editors/sculpt_paint/sculpt_uv.c @@ -500,20 +500,10 @@ static UvSculptData *uv_sculpt_stroke_init(bContext *C, wmOperator *op, const wm if (do_island_optimization) { /* We will need island information */ - if (ts->uv_flag & UV_SYNC_SELECTION) { - data->elementMap = BM_uv_element_map_create(bm, scene, false, false, true, true); - } - else { - data->elementMap = BM_uv_element_map_create(bm, scene, true, false, true, true); - } + data->elementMap = BM_uv_element_map_create(bm, scene, false, true, true); } else { - if (ts->uv_flag & UV_SYNC_SELECTION) { - data->elementMap = BM_uv_element_map_create(bm, scene, false, false, true, false); - } - else { - data->elementMap = BM_uv_element_map_create(bm, scene, true, false, true, false); - } + data->elementMap = BM_uv_element_map_create(bm, scene, false, true, false); } if (!data->elementMap) { diff --git a/source/blender/editors/transform/transform_convert_mesh_uv.c b/source/blender/editors/transform/transform_convert_mesh_uv.c index 18868643b6d..bb550909407 100644 --- a/source/blender/editors/transform/transform_convert_mesh_uv.c +++ b/source/blender/editors/transform/transform_convert_mesh_uv.c @@ -238,7 +238,6 @@ void createTransUVs(bContext *C, TransInfo *t) { SpaceImage *sima = CTX_wm_space_image(C); Scene *scene = t->scene; - ToolSettings *ts = CTX_data_tool_settings(C); const bool is_prop_edit = (t->flag & T_PROP_EDIT) != 0; const bool is_prop_connected = (t->flag & T_PROP_CONNECTED) != 0; @@ -266,8 +265,7 @@ void createTransUVs(bContext *C, TransInfo *t) /* count */ if (is_island_center) { /* create element map with island information */ - const bool use_facesel = (ts->uv_flag & UV_SYNC_SELECTION) == 0; - elementmap = BM_uv_element_map_create(em->bm, scene, use_facesel, true, false, true); + elementmap = BM_uv_element_map_create(em->bm, scene, true, false, true); if (elementmap == NULL) { continue; } diff --git a/source/blender/editors/uvedit/uvedit_ops.c b/source/blender/editors/uvedit/uvedit_ops.c index 4844ff22b68..4e99eb3fc0f 100644 --- a/source/blender/editors/uvedit/uvedit_ops.c +++ b/source/blender/editors/uvedit/uvedit_ops.c @@ -544,7 +544,7 @@ static bool uvedit_uv_straighten(Scene *scene, BMesh *bm, eUVWeldAlign tool) return false; } - UvElementMap *element_map = BM_uv_element_map_create(bm, scene, false, true, false, true); + UvElementMap *element_map = BM_uv_element_map_create(bm, scene, true, false, true); if (element_map == NULL) { return false; } diff --git a/source/blender/editors/uvedit/uvedit_select.c b/source/blender/editors/uvedit/uvedit_select.c index b5a564fd984..d59dcb4f4ed 100644 --- a/source/blender/editors/uvedit/uvedit_select.c +++ b/source/blender/editors/uvedit/uvedit_select.c @@ -5223,7 +5223,7 @@ static void uv_isolate_selected_islands(const Scene *scene, BLI_assert((scene->toolsettings->uv_flag & UV_SYNC_SELECTION) == 0); BMFace *efa; BMIter iter, liter; - UvElementMap *elementmap = BM_uv_element_map_create(em->bm, scene, true, false, false, true); + UvElementMap *elementmap = BM_uv_element_map_create(em->bm, scene, false, false, true); if (elementmap == NULL) { return; } diff --git a/source/blender/editors/uvedit/uvedit_smart_stitch.c b/source/blender/editors/uvedit/uvedit_smart_stitch.c index 55e44607f6f..579674930a6 100644 --- a/source/blender/editors/uvedit/uvedit_smart_stitch.c +++ b/source/blender/editors/uvedit/uvedit_smart_stitch.c @@ -1902,15 +1902,7 @@ static StitchState *stitch_init(bContext *C, * for stitch this isn't useful behavior, see T86924. */ const int selectmode_orig = scene->toolsettings->selectmode; scene->toolsettings->selectmode = SCE_SELECT_VERTEX; - - /* in uv synch selection, all uv's are visible */ - if (ts->uv_flag & UV_SYNC_SELECTION) { - state->element_map = BM_uv_element_map_create(state->em->bm, scene, false, false, true, true); - } - else { - state->element_map = BM_uv_element_map_create(state->em->bm, scene, true, false, true, true); - } - + state->element_map = BM_uv_element_map_create(state->em->bm, scene, false, true, true); scene->toolsettings->selectmode = selectmode_orig; if (!state->element_map) { -- cgit v1.2.3 From 94226271556baacc5a77432fc9c62638901ce664 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Wed, 13 Jul 2022 16:18:14 +1000 Subject: Fix T99653: "Align Active Camera to Selected" fails with ortho camera There were two bugs, a regression in [0] and the object-data wasn't tagged for depsgraph updating. [0]: 19df0e3cfd5b9fed891ed81dd1123b2351605a7d --- source/blender/blenkernel/intern/camera.c | 16 +++++++--------- source/blender/editors/space_view3d/view3d_utils.c | 5 +++++ 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/source/blender/blenkernel/intern/camera.c b/source/blender/blenkernel/intern/camera.c index fbe03ac365c..1c4267cac6d 100644 --- a/source/blender/blenkernel/intern/camera.c +++ b/source/blender/blenkernel/intern/camera.c @@ -622,15 +622,13 @@ static void camera_frame_fit_data_init(const Scene *scene, invert_m4(camera_rotmat_transposed_inversed); /* Extract frustum planes from projection matrix. */ - planes_from_projmat( - params->winmat, - /* left right top bottom near far */ - data->plane_tx[2], - data->plane_tx[0], - data->plane_tx[3], - data->plane_tx[1], - NULL, - NULL); + planes_from_projmat(params->winmat, + data->plane_tx[2], + data->plane_tx[0], + data->plane_tx[1], + data->plane_tx[3], + NULL, + NULL); /* Rotate planes and get normals from them */ for (uint i = 0; i < CAMERA_VIEWFRAME_NUM_PLANES; i++) { diff --git a/source/blender/editors/space_view3d/view3d_utils.c b/source/blender/editors/space_view3d/view3d_utils.c index 1fabdef8da2..85b1af8e55d 100644 --- a/source/blender/editors/space_view3d/view3d_utils.c +++ b/source/blender/editors/space_view3d/view3d_utils.c @@ -1492,10 +1492,12 @@ static bool view3d_camera_to_view_selected_impl(struct Main *bmain, depsgraph, scene, camera_ob_eval, co, &scale, r_clip_start, r_clip_end)) { ObjectTfmProtectedChannels obtfm; float obmat_new[4][4]; + bool is_ortho_camera = false; if ((camera_ob_eval->type == OB_CAMERA) && (((Camera *)camera_ob_eval->data)->type == CAM_ORTHO)) { ((Camera *)camera_ob->data)->ortho_scale = scale; + is_ortho_camera = true; } copy_m4_m4(obmat_new, camera_ob_eval->obmat); @@ -1508,6 +1510,9 @@ static bool view3d_camera_to_view_selected_impl(struct Main *bmain, /* notifiers */ DEG_id_tag_update_ex(bmain, &camera_ob->id, ID_RECALC_TRANSFORM); + if (is_ortho_camera) { + DEG_id_tag_update_ex(bmain, camera_ob->data, ID_RECALC_PARAMETERS); + } return true; } -- cgit v1.2.3 From b3913d755117c99af2e4b10deb4edcc846604b38 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Wed, 13 Jul 2022 16:18:16 +1000 Subject: Cleanup: use defines for camera axes for view frame fitting The values used for axes weren't in any meaningful order, use defines to improve readability. --- source/blender/blenkernel/intern/camera.c | 36 +++++++++++++++++++++---------- 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/source/blender/blenkernel/intern/camera.c b/source/blender/blenkernel/intern/camera.c index 1c4267cac6d..84551f8e782 100644 --- a/source/blender/blenkernel/intern/camera.c +++ b/source/blender/blenkernel/intern/camera.c @@ -559,6 +559,11 @@ void BKE_camera_view_frame(const Scene *scene, const Camera *camera, float r_vec #define CAMERA_VIEWFRAME_NUM_PLANES 4 +#define Y_MAX 0 +#define Z_MIN 1 +#define Y_MIN 2 +#define Z_MAX 3 + typedef struct CameraViewFrameData { float plane_tx[CAMERA_VIEWFRAME_NUM_PLANES][4]; /* 4 planes normalized */ float dist_vals[CAMERA_VIEWFRAME_NUM_PLANES]; /* distance (signed) */ @@ -623,10 +628,10 @@ static void camera_frame_fit_data_init(const Scene *scene, /* Extract frustum planes from projection matrix. */ planes_from_projmat(params->winmat, - data->plane_tx[2], - data->plane_tx[0], - data->plane_tx[1], - data->plane_tx[3], + data->plane_tx[Y_MIN], + data->plane_tx[Y_MAX], + data->plane_tx[Z_MIN], + data->plane_tx[Z_MAX], NULL, NULL); @@ -670,19 +675,21 @@ static bool camera_frame_fit_calc_from_data(CameraParams *params, const float *dists = data->dist_vals; float scale_diff; - if ((dists[0] + dists[2]) > (dists[1] + dists[3])) { - scale_diff = (dists[1] + dists[3]) * + if ((dists[Y_MAX] + dists[Y_MIN]) > (dists[Z_MIN] + dists[Z_MAX])) { + scale_diff = (dists[Z_MIN] + dists[Z_MAX]) * (BLI_rctf_size_x(¶ms->viewplane) / BLI_rctf_size_y(¶ms->viewplane)); } else { - scale_diff = (dists[0] + dists[2]) * + scale_diff = (dists[Y_MAX] + dists[Y_MIN]) * (BLI_rctf_size_y(¶ms->viewplane) / BLI_rctf_size_x(¶ms->viewplane)); } *r_scale = params->ortho_scale - scale_diff; zero_v3(r_co); - madd_v3_v3fl(r_co, cam_axis_x, (dists[2] - dists[0]) * 0.5f + params->shiftx * scale_diff); - madd_v3_v3fl(r_co, cam_axis_y, (dists[1] - dists[3]) * 0.5f + params->shifty * scale_diff); + madd_v3_v3fl( + r_co, cam_axis_x, (dists[Y_MIN] - dists[Y_MAX]) * 0.5f + params->shiftx * scale_diff); + madd_v3_v3fl( + r_co, cam_axis_y, (dists[Z_MIN] - dists[Z_MAX]) * 0.5f + params->shifty * scale_diff); madd_v3_v3fl(r_co, cam_axis_z, -(data->z_range[0] - 1.0f - params->clip_start)); } else { @@ -698,8 +705,10 @@ static bool camera_frame_fit_calc_from_data(CameraParams *params, plane_from_point_normal_v3(plane_tx[i], co, data->plane_tx[i]); } - if ((!isect_plane_plane_v3(plane_tx[0], plane_tx[2], plane_isect_1, plane_isect_1_no)) || - (!isect_plane_plane_v3(plane_tx[1], plane_tx[3], plane_isect_2, plane_isect_2_no))) { + if ((!isect_plane_plane_v3( + plane_tx[Y_MAX], plane_tx[Y_MIN], plane_isect_1, plane_isect_1_no)) || + (!isect_plane_plane_v3( + plane_tx[Z_MIN], plane_tx[Z_MAX], plane_isect_2, plane_isect_2_no))) { return false; } @@ -753,6 +762,11 @@ static bool camera_frame_fit_calc_from_data(CameraParams *params, return true; } +#undef Y_MIN +#undef Y_MAX +#undef Z_MIN +#undef Z_MAX + bool BKE_camera_view_frame_fit_to_scene(Depsgraph *depsgraph, const Scene *scene, Object *camera_ob, -- cgit v1.2.3 From a0848396051bd4aa8f611013f0ed722c9d85ec63 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Wed, 13 Jul 2022 16:18:17 +1000 Subject: Cleanup: logical order of axis defines, assign variables for readability --- source/blender/blenkernel/intern/camera.c | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/source/blender/blenkernel/intern/camera.c b/source/blender/blenkernel/intern/camera.c index 84551f8e782..48a48d6e2b4 100644 --- a/source/blender/blenkernel/intern/camera.c +++ b/source/blender/blenkernel/intern/camera.c @@ -559,9 +559,9 @@ void BKE_camera_view_frame(const Scene *scene, const Camera *camera, float r_vec #define CAMERA_VIEWFRAME_NUM_PLANES 4 -#define Y_MAX 0 -#define Z_MIN 1 -#define Y_MIN 2 +#define Y_MIN 0 +#define Y_MAX 1 +#define Z_MIN 2 #define Z_MAX 3 typedef struct CameraViewFrameData { @@ -673,23 +673,21 @@ static bool camera_frame_fit_calc_from_data(CameraParams *params, const float *cam_axis_y = data->camera_rotmat[1]; const float *cam_axis_z = data->camera_rotmat[2]; const float *dists = data->dist_vals; - float scale_diff; + const float dist_span_y = dists[Y_MIN] + dists[Y_MAX]; + const float dist_span_z = dists[Z_MIN] + dists[Z_MAX]; + const float dist_mid_y = (dists[Y_MIN] - dists[Y_MAX]) * 0.5f; + const float dist_mid_z = (dists[Z_MIN] - dists[Z_MAX]) * 0.5f; + const float scale_diff = (dist_span_z < dist_span_y) ? + (dist_span_z * (BLI_rctf_size_x(¶ms->viewplane) / + BLI_rctf_size_y(¶ms->viewplane))) : + (dist_span_y * (BLI_rctf_size_y(¶ms->viewplane) / + BLI_rctf_size_x(¶ms->viewplane))); - if ((dists[Y_MAX] + dists[Y_MIN]) > (dists[Z_MIN] + dists[Z_MAX])) { - scale_diff = (dists[Z_MIN] + dists[Z_MAX]) * - (BLI_rctf_size_x(¶ms->viewplane) / BLI_rctf_size_y(¶ms->viewplane)); - } - else { - scale_diff = (dists[Y_MAX] + dists[Y_MIN]) * - (BLI_rctf_size_y(¶ms->viewplane) / BLI_rctf_size_x(¶ms->viewplane)); - } *r_scale = params->ortho_scale - scale_diff; zero_v3(r_co); - madd_v3_v3fl( - r_co, cam_axis_x, (dists[Y_MIN] - dists[Y_MAX]) * 0.5f + params->shiftx * scale_diff); - madd_v3_v3fl( - r_co, cam_axis_y, (dists[Z_MIN] - dists[Z_MAX]) * 0.5f + params->shifty * scale_diff); + madd_v3_v3fl(r_co, cam_axis_x, dist_mid_y + (params->shiftx * scale_diff)); + madd_v3_v3fl(r_co, cam_axis_y, dist_mid_z + (params->shifty * scale_diff)); madd_v3_v3fl(r_co, cam_axis_z, -(data->z_range[0] - 1.0f - params->clip_start)); } else { -- cgit v1.2.3 From 74888cdbfd523d70ea0573e4452bf65c25b3a888 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Wed, 13 Jul 2022 11:15:19 +0200 Subject: Fix (studio-reported) issue in remapping code. Not clearing runtime remapping data for the new ID as well as the old one can lead to false stale data there, wichi could e.g. make indirectly linked data be tagged as directly linked. This would generate an error report on file write when hapening on ShapeKey ID, since that type is not allowed to be directly linked. --- source/blender/blenkernel/intern/lib_remap.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/source/blender/blenkernel/intern/lib_remap.c b/source/blender/blenkernel/intern/lib_remap.c index 246999a1179..28b0337d9a2 100644 --- a/source/blender/blenkernel/intern/lib_remap.c +++ b/source/blender/blenkernel/intern/lib_remap.c @@ -428,10 +428,13 @@ static void libblock_remap_data_update_tags(ID *old_id, ID *new_id, void *user_d } static void libblock_remap_reset_remapping_status_callback(ID *old_id, - ID *UNUSED(new_id), + ID *new_id, void *UNUSED(user_data)) { BKE_libblock_runtime_reset_remapping_status(old_id); + if (new_id != NULL) { + BKE_libblock_runtime_reset_remapping_status(new_id); + } } /** -- cgit v1.2.3 From c8be3d3b27d49a86772b1bc970fb5f5f79b891b4 Mon Sep 17 00:00:00 2001 From: Antonio Vazquez Date: Wed, 13 Jul 2022 12:56:48 +0200 Subject: Fix T99654: Applying Mirror modifier breaks the erase tool The problem was the new generated strokes were copied from original and the location was offset to mirror, but the internal geometry data was not updated and the collision check done by brushes was not working. Now, the internal geometry data is recalculated when the modifier is applied. --- source/blender/gpencil_modifiers/intern/MOD_gpencilmirror.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilmirror.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilmirror.c index 1d62e930caa..1a8d1e75746 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencilmirror.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilmirror.c @@ -23,6 +23,7 @@ #include "BKE_context.h" #include "BKE_gpencil.h" +#include "BKE_gpencil_geom.h" #include "BKE_gpencil_modifier.h" #include "BKE_lib_query.h" #include "BKE_main.h" @@ -100,9 +101,11 @@ static void update_position(Object *ob, MirrorGpencilModifierData *mmd, bGPDstro } } -static void generate_geometry(GpencilModifierData *md, Object *ob, bGPDlayer *gpl, bGPDframe *gpf) +static void generate_geometry( + GpencilModifierData *md, Object *ob, bGPDlayer *gpl, bGPDframe *gpf, const bool update) { MirrorGpencilModifierData *mmd = (MirrorGpencilModifierData *)md; + bGPdata *gpd = ob->data; bGPDstroke *gps, *gps_new = NULL; int tot_strokes; int i; @@ -129,6 +132,9 @@ static void generate_geometry(GpencilModifierData *md, Object *ob, bGPDlayer *gp mmd->flag & GP_MIRROR_INVERT_MATERIAL)) { gps_new = BKE_gpencil_stroke_duplicate(gps, true, true); update_position(ob, mmd, gps_new, xi); + if (update) { + BKE_gpencil_stroke_geometry_update(gpd, gps_new); + } BLI_addtail(&gpf->strokes, gps_new); } } @@ -147,7 +153,7 @@ static void generateStrokes(GpencilModifierData *md, Depsgraph *depsgraph, Objec if (gpf == NULL) { continue; } - generate_geometry(md, ob, gpl, gpf); + generate_geometry(md, ob, gpl, gpf, false); } } @@ -167,7 +173,7 @@ static void bakeModifier(Main *UNUSED(bmain), BKE_scene_graph_update_for_newframe(depsgraph); /* compute mirror effects on this frame */ - generate_geometry(md, ob, gpl, gpf); + generate_geometry(md, ob, gpl, gpf, true); } } -- cgit v1.2.3 From c484599687ba1757e3092f7fe2ffd0db56e9cbb6 Mon Sep 17 00:00:00 2001 From: jon denning Date: Wed, 13 Jul 2022 07:07:43 -0400 Subject: Expose snap options in transform operators This commit exposes snap options in transform operators. These options are needed for Python tools to control snapping without requiring the tool settings to be adjusted. The newly exposed options are: - `snap_elements` for choosing which element(s) of target the source geometry should snap to (ex: Face Raycast). - `use_snap_self`, `use_snap_edit`, `use_snap_nonedit`, `use_snap_selectable_only` for controlling target selection. - `use_snap_project` for controlling Face Raycast snapping. - `use_snap_to_same_target` and `snap_face_nearest_steps` for controlling Face Nearest snapping. Reviewed by: Campbell Barton (campbellbarton) Differential Revision: https://developer.blender.org/D15398 --- source/blender/editors/transform/transform_ops.c | 60 +++++++++++++++++++++--- 1 file changed, 53 insertions(+), 7 deletions(-) diff --git a/source/blender/editors/transform/transform_ops.c b/source/blender/editors/transform/transform_ops.c index a64eff8f981..7c94241f3e3 100644 --- a/source/blender/editors/transform/transform_ops.c +++ b/source/blender/editors/transform/transform_ops.c @@ -566,6 +566,17 @@ static bool transform_poll_property(const bContext *UNUSED(C), } } + /* Snapping. */ + { + PropertyRNA *prop_snap = RNA_struct_find_property(op->ptr, "snap"); + if (prop_snap && (prop_snap != prop) && + (RNA_property_boolean_get(op->ptr, prop_snap) == false)) { + if (STRPREFIX(prop_id, "snap") || STRPREFIX(prop_id, "use_snap")) { + return false; + } + } + } + return true; } @@ -644,28 +655,63 @@ void Transform_Properties(struct wmOperatorType *ot, int flags) } if (flags & P_SNAP) { - prop = RNA_def_boolean(ot->srna, "snap", 0, "Use Snapping Options", ""); + prop = RNA_def_boolean(ot->srna, "snap", false, "Use Snapping Options", ""); RNA_def_property_flag(prop, PROP_HIDDEN); + prop = RNA_def_enum(ot->srna, + "snap_elements", + rna_enum_snap_element_items, + SCE_SNAP_MODE_INCREMENT, + "Snap to Elements", + ""); + RNA_def_property_flag(prop, PROP_ENUM_FLAG); + + RNA_def_boolean(ot->srna, "use_snap_project", false, "Project Individual Elements", ""); + if (flags & P_GEO_SNAP) { - /* TODO(@gfxcoder): Rename `snap_target` to `snap_source` to avoid - * previous ambiguity of "target" (now, "source" is geometry to be moved and "target" is - * geometry to which moved geometry is snapped). Use "Source snap point" and "Point on - * source that will snap to target" for name and description, respectively. */ - prop = RNA_def_enum(ot->srna, "snap_target", rna_enum_snap_source_items, 0, "Target", ""); + /* TODO(@gfxcoder): Rename `snap_target` to `snap_source` to avoid previous ambiguity of + * "target" (now, "source" is geometry to be moved and "target" is geometry to which moved + * geometry is snapped). Use "Source snap point" and "Point on source that will snap to + * target" for name and description, respectively. */ + prop = RNA_def_enum(ot->srna, "snap_target", rna_enum_snap_source_items, 0, "Snap With", ""); + RNA_def_property_flag(prop, PROP_HIDDEN); + + /* Target selection. */ + prop = RNA_def_boolean(ot->srna, "use_snap_self", true, "Target: Include Active", ""); RNA_def_property_flag(prop, PROP_HIDDEN); + prop = RNA_def_boolean(ot->srna, "use_snap_edit", true, "Target: Include Edit", ""); + RNA_def_property_flag(prop, PROP_HIDDEN); + prop = RNA_def_boolean(ot->srna, "use_snap_nonedit", true, "Target: Include Non-Edited", ""); + RNA_def_property_flag(prop, PROP_HIDDEN); + prop = RNA_def_boolean( + ot->srna, "use_snap_selectable_only", false, "Target: Exclude Non-Selectable", ""); + RNA_def_property_flag(prop, PROP_HIDDEN); + + /* Face Nearest options */ + prop = RNA_def_boolean( + ot->srna, "use_snap_to_same_target", false, "Snap to Same Target", ""); + RNA_def_property_flag(prop, PROP_HIDDEN); + prop = RNA_def_int( + ot->srna, "snap_face_nearest_steps", 1, 1, 32767, "Face Nearest Steps", "", 1, 32767); + RNA_def_property_flag(prop, PROP_HIDDEN); + prop = RNA_def_float_vector( ot->srna, "snap_point", 3, NULL, -FLT_MAX, FLT_MAX, "Point", "", -FLT_MAX, FLT_MAX); RNA_def_property_flag(prop, PROP_HIDDEN); if (flags & P_ALIGN_SNAP) { - prop = RNA_def_boolean(ot->srna, "snap_align", 0, "Align with Point Normal", ""); + prop = RNA_def_boolean(ot->srna, "snap_align", false, "Align with Point Normal", ""); RNA_def_property_flag(prop, PROP_HIDDEN); prop = RNA_def_float_vector( ot->srna, "snap_normal", 3, NULL, -FLT_MAX, FLT_MAX, "Normal", "", -FLT_MAX, FLT_MAX); RNA_def_property_flag(prop, PROP_HIDDEN); } } + else { + prop = RNA_def_boolean( + ot->srna, "use_snap_selectable_only", false, "Target: Exclude Non-Selectable", ""); + RNA_def_property_flag(prop, PROP_HIDDEN); + } } if (flags & P_GPENCIL_EDIT) { -- cgit v1.2.3 From 441dd08dba34d22e8a34d54b252e8cdd38f2c56c Mon Sep 17 00:00:00 2001 From: jon denning Date: Wed, 13 Jul 2022 08:30:40 -0400 Subject: Expose option for fallback tools keymap in GizmoGroup type This patch allows new GizmoGroup classes to support tool fallback keymap. With this patch, when new gizmo groups add `'TOOL_FALLBACK_KEYMAP'` to its `bl_options`, the fallback tools are added to the group. This allows a `WorkSpaceTool` (for example) to have selection be a fallback tool if the user LeftMouse drags away from other gizmos in the group. Reviewed by: Campbell Barton (campbellbarton) Differential Revision: https://developer.blender.org/D15154 --- source/blender/makesrna/intern/rna_wm_gizmo.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/source/blender/makesrna/intern/rna_wm_gizmo.c b/source/blender/makesrna/intern/rna_wm_gizmo.c index 6c3d96726bb..0e307f5b424 100644 --- a/source/blender/makesrna/intern/rna_wm_gizmo.c +++ b/source/blender/makesrna/intern/rna_wm_gizmo.c @@ -1398,6 +1398,11 @@ static void rna_def_gizmogroup(BlenderRNA *brna) 0, "Tool Init", "Postpone running until tool operator run (when used with a tool)"}, + {WM_GIZMOGROUPTYPE_TOOL_FALLBACK_KEYMAP, + "TOOL_FALLBACK_KEYMAP", + 0, + "Use fallback tools keymap", + "Add fallback tools keymap to this gizmo type."}, {WM_GIZMOGROUPTYPE_VR_REDRAWS, "VR_REDRAWS", 0, -- cgit v1.2.3 From ccdf189d3c5c3b90dbb95b704f684f1cd1bd192c Mon Sep 17 00:00:00 2001 From: jon denning Date: Wed, 13 Jul 2022 08:43:57 -0400 Subject: Documentation: Update Docs for Gizmo This patch updates the documentation for arguments regarding the `Gizmo` type. - Corrected `select_id` doc for draw_preset_ functions. `-1` indicates that no selection ID is to be written, but previous docs incorrectly specified `0` instead. - Added missing doc for `target` argument for `target_set_handler` function. Reviewed by: Aaron Carlisle (Blendify) Differential Revision: https://developer.blender.org/D14834 --- source/blender/makesrna/intern/rna_wm_gizmo_api.c | 40 ++++++++++++++++++++--- source/blender/python/intern/bpy_rna_gizmo.c | 2 ++ 2 files changed, 38 insertions(+), 4 deletions(-) diff --git a/source/blender/makesrna/intern/rna_wm_gizmo_api.c b/source/blender/makesrna/intern/rna_wm_gizmo_api.c index 419dfa68305..760121d2279 100644 --- a/source/blender/makesrna/intern/rna_wm_gizmo_api.c +++ b/source/blender/makesrna/intern/rna_wm_gizmo_api.c @@ -211,7 +211,15 @@ void RNA_api_gizmo(StructRNA *srna) RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); RNA_def_property_multi_array(parm, 2, rna_matrix_dimsize_4x4); RNA_def_property_ui_text(parm, "", "The matrix to transform"); - RNA_def_int(func, "select_id", -1, -1, INT_MAX, "Zero when not selecting", "", -1, INT_MAX); + RNA_def_int(func, + "select_id", + -1, + -1, + INT_MAX, + "ID to use when gizmo is selectable. Use -1 when not selecting", + "", + -1, + INT_MAX); /* draw_preset_box */ func = RNA_def_function(srna, "draw_preset_arrow", "rna_gizmo_draw_preset_arrow"); @@ -221,7 +229,15 @@ void RNA_api_gizmo(StructRNA *srna) RNA_def_property_multi_array(parm, 2, rna_matrix_dimsize_4x4); RNA_def_property_ui_text(parm, "", "The matrix to transform"); RNA_def_enum(func, "axis", rna_enum_object_axis_items, 2, "", "Arrow Orientation"); - RNA_def_int(func, "select_id", -1, -1, INT_MAX, "Zero when not selecting", "", -1, INT_MAX); + RNA_def_int(func, + "select_id", + -1, + -1, + INT_MAX, + "ID to use when gizmo is selectable. Use -1 when not selecting", + "", + -1, + INT_MAX); func = RNA_def_function(srna, "draw_preset_circle", "rna_gizmo_draw_preset_circle"); RNA_def_function_ui_description(func, "Draw a box"); @@ -230,7 +246,15 @@ void RNA_api_gizmo(StructRNA *srna) RNA_def_property_multi_array(parm, 2, rna_matrix_dimsize_4x4); RNA_def_property_ui_text(parm, "", "The matrix to transform"); RNA_def_enum(func, "axis", rna_enum_object_axis_items, 2, "", "Arrow Orientation"); - RNA_def_int(func, "select_id", -1, -1, INT_MAX, "Zero when not selecting", "", -1, INT_MAX); + RNA_def_int(func, + "select_id", + -1, + -1, + INT_MAX, + "ID to use when gizmo is selectable. Use -1 when not selecting", + "", + -1, + INT_MAX); /* -------------------------------------------------------------------- */ /* Other Shapes */ @@ -243,7 +267,15 @@ void RNA_api_gizmo(StructRNA *srna) RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); parm = RNA_def_int(func, "face_map", 0, 0, INT_MAX, "Face map index", "", 0, INT_MAX); RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); - RNA_def_int(func, "select_id", -1, -1, INT_MAX, "Zero when not selecting", "", -1, INT_MAX); + RNA_def_int(func, + "select_id", + -1, + -1, + INT_MAX, + "ID to use when gizmo is selectable. Use -1 when not selecting", + "", + -1, + INT_MAX); /* -------------------------------------------------------------------- */ /* Property API */ diff --git a/source/blender/python/intern/bpy_rna_gizmo.c b/source/blender/python/intern/bpy_rna_gizmo.c index 61f439e5152..32ef7865e84 100644 --- a/source/blender/python/intern/bpy_rna_gizmo.c +++ b/source/blender/python/intern/bpy_rna_gizmo.c @@ -314,6 +314,8 @@ PyDoc_STRVAR( "\n" " Assigns callbacks to a gizmos property.\n" "\n" + " :arg target: Target property name.\n" + " :type target: string\n" " :arg get: Function that returns the value for this property (single value or sequence).\n" " :type get: callable\n" " :arg set: Function that takes a single value argument and applies it.\n" -- cgit v1.2.3 From 88fbf0a8fc1c4192279ebd4c31b66acf05117aa6 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Wed, 13 Jul 2022 16:10:03 +0200 Subject: Fix (studio-reported) bad remapping of libraries. New remapper code would also fail in some cases when remapping libraries, similar to the issue yesterday, because ID_LI type had no mask value. That would fail to remap `parent` member of a library to NULL when deleting that parent, leading to a crash e.g. in Outliner tree building code. Reported by @JulienKaspar from Blender studio. --- source/blender/blenkernel/intern/idtype.c | 15 ++++++++++----- source/blender/blenkernel/intern/lib_query.c | 5 +++-- source/blender/blenkernel/intern/library.c | 2 +- source/blender/makesdna/DNA_ID.h | 3 ++- 4 files changed, 16 insertions(+), 9 deletions(-) diff --git a/source/blender/blenkernel/intern/idtype.c b/source/blender/blenkernel/intern/idtype.c index edb6fe5d69b..923582dff4c 100644 --- a/source/blender/blenkernel/intern/idtype.c +++ b/source/blender/blenkernel/intern/idtype.c @@ -227,7 +227,8 @@ uint64_t BKE_idtype_idcode_to_idfilter(const short idcode) CASE_IDFILTER_NONE(IP); CASE_IDFILTER(KE); CASE_IDFILTER(LA); - CASE_IDFILTER_NONE(LI); + CASE_IDFILTER(LI); + CASE_IDFILTER(LP); CASE_IDFILTER(LS); CASE_IDFILTER(LT); CASE_IDFILTER(MA); @@ -241,12 +242,11 @@ uint64_t BKE_idtype_idcode_to_idfilter(const short idcode) CASE_IDFILTER(PAL); CASE_IDFILTER(PC); CASE_IDFILTER(PT); - CASE_IDFILTER(LP); CASE_IDFILTER(SCE); CASE_IDFILTER(SCR); CASE_IDFILTER(SIM); - CASE_IDFILTER(SPK); CASE_IDFILTER(SO); + CASE_IDFILTER(SPK); CASE_IDFILTER(TE); CASE_IDFILTER(TXT); CASE_IDFILTER(VF); @@ -269,6 +269,8 @@ short BKE_idtype_idcode_from_idfilter(const uint64_t idfilter) case FILTER_ID_##_id: \ return ID_##_id +#define CASE_IDFILTER_NONE(_id) (void)0 + switch (idfilter) { CASE_IDFILTER(AC); CASE_IDFILTER(AR); @@ -280,8 +282,11 @@ short BKE_idtype_idcode_from_idfilter(const uint64_t idfilter) CASE_IDFILTER(GR); CASE_IDFILTER(CV); CASE_IDFILTER(IM); + CASE_IDFILTER_NONE(IP); CASE_IDFILTER(KE); CASE_IDFILTER(LA); + CASE_IDFILTER(LI); + CASE_IDFILTER(LP); CASE_IDFILTER(LS); CASE_IDFILTER(LT); CASE_IDFILTER(MA); @@ -295,12 +300,11 @@ short BKE_idtype_idcode_from_idfilter(const uint64_t idfilter) CASE_IDFILTER(PAL); CASE_IDFILTER(PC); CASE_IDFILTER(PT); - CASE_IDFILTER(LP); CASE_IDFILTER(SCE); CASE_IDFILTER(SCR); CASE_IDFILTER(SIM); - CASE_IDFILTER(SPK); CASE_IDFILTER(SO); + CASE_IDFILTER(SPK); CASE_IDFILTER(TE); CASE_IDFILTER(TXT); CASE_IDFILTER(VF); @@ -314,6 +318,7 @@ short BKE_idtype_idcode_from_idfilter(const uint64_t idfilter) return 0; #undef CASE_IDFILTER +#undef CASE_IDFILTER_NONE } int BKE_idtype_idcode_to_index(const short idcode) diff --git a/source/blender/blenkernel/intern/lib_query.c b/source/blender/blenkernel/intern/lib_query.c index 38252a46b93..a869bf4c4b0 100644 --- a/source/blender/blenkernel/intern/lib_query.c +++ b/source/blender/blenkernel/intern/lib_query.c @@ -391,8 +391,7 @@ uint64_t BKE_library_id_can_use_filter_id(const ID *id_owner) switch ((ID_Type)id_type_owner) { case ID_LI: - /* ID_LI doesn't exist as filter_id. */ - return 0; + return FILTER_ID_LI; case ID_SCE: return FILTER_ID_OB | FILTER_ID_WO | FILTER_ID_SCE | FILTER_ID_MC | FILTER_ID_MA | FILTER_ID_GR | FILTER_ID_TXT | FILTER_ID_LS | FILTER_ID_MSK | FILTER_ID_SO | @@ -472,6 +471,8 @@ uint64_t BKE_library_id_can_use_filter_id(const ID *id_owner) /* Deprecated... */ return 0; } + + BLI_assert_unreachable(); return 0; } diff --git a/source/blender/blenkernel/intern/library.c b/source/blender/blenkernel/intern/library.c index 03a17b2ecc5..4962b1c448e 100644 --- a/source/blender/blenkernel/intern/library.c +++ b/source/blender/blenkernel/intern/library.c @@ -63,7 +63,7 @@ static void library_foreach_path(ID *id, BPathForeachPathData *bpath_data) IDTypeInfo IDType_ID_LI = { .id_code = ID_LI, - .id_filter = 0, + .id_filter = FILTER_ID_LI, .main_listbase_index = INDEX_ID_LI, .struct_size = sizeof(Library), .name = "Library", diff --git a/source/blender/makesdna/DNA_ID.h b/source/blender/makesdna/DNA_ID.h index f6032b71155..d3fc279381f 100644 --- a/source/blender/makesdna/DNA_ID.h +++ b/source/blender/makesdna/DNA_ID.h @@ -926,6 +926,7 @@ typedef enum IDRecalcFlag { #define FILTER_ID_KE (1ULL << 36) #define FILTER_ID_SCR (1ULL << 37) #define FILTER_ID_WM (1ULL << 38) +#define FILTER_ID_LI (1ULL << 39) #define FILTER_ID_ALL \ (FILTER_ID_AC | FILTER_ID_AR | FILTER_ID_BR | FILTER_ID_CA | FILTER_ID_CU_LEGACY | \ @@ -934,7 +935,7 @@ typedef enum IDRecalcFlag { FILTER_ID_OB | FILTER_ID_PA | FILTER_ID_PAL | FILTER_ID_PC | FILTER_ID_SCE | FILTER_ID_SPK | \ FILTER_ID_SO | FILTER_ID_TE | FILTER_ID_TXT | FILTER_ID_VF | FILTER_ID_WO | FILTER_ID_CF | \ FILTER_ID_WS | FILTER_ID_LP | FILTER_ID_CV | FILTER_ID_PT | FILTER_ID_VO | FILTER_ID_SIM | \ - FILTER_ID_KE | FILTER_ID_SCR | FILTER_ID_WM) + FILTER_ID_KE | FILTER_ID_SCR | FILTER_ID_WM | FILTER_ID_LI) /** * This enum defines the index assigned to each type of IDs in the array returned by -- cgit v1.2.3 From 144d9f2b2e80cf543e228ba5b0c0279aeca2c574 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Wed, 13 Jul 2022 17:39:19 +0200 Subject: Cleanup: Do not use spaces in default data names. Using white spaces in data names should not be encouraged in general, better not give wrong example here. Originally part of D15441. --- source/blender/editors/object/object_add.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/editors/object/object_add.cc b/source/blender/editors/object/object_add.cc index 66e76addd6f..671a32ce438 100644 --- a/source/blender/editors/object/object_add.cc +++ b/source/blender/editors/object/object_add.cc @@ -1349,7 +1349,7 @@ static int object_gpencil_add_exec(bContext *C, wmOperator *op) case GP_LRT_OBJECT: case GP_LRT_SCENE: case GP_LRT_COLLECTION: { - ob_name = "Line Art"; + ob_name = "LineArt"; break; } default: { -- cgit v1.2.3 From 50d832634eaa1b309306c4596f4213244213e104 Mon Sep 17 00:00:00 2001 From: Aaron Carlisle Date: Wed, 13 Jul 2022 16:25:57 -0400 Subject: Docs: Fix out of order parameters Fixes T99672 --- source/blender/python/gpu/gpu_py_vertex_buffer.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/source/blender/python/gpu/gpu_py_vertex_buffer.c b/source/blender/python/gpu/gpu_py_vertex_buffer.c index a295bedeae2..ac050128a1d 100644 --- a/source/blender/python/gpu/gpu_py_vertex_buffer.c +++ b/source/blender/python/gpu/gpu_py_vertex_buffer.c @@ -323,14 +323,14 @@ static void pygpu_vertbuf__tp_dealloc(BPyGPUVertBuf *self) } PyDoc_STRVAR(pygpu_vertbuf__tp_doc, - ".. class:: GPUVertBuf(len, format)\n" + ".. class:: GPUVertBuf(format, len)\n" "\n" " Contains a VBO.\n" "\n" - " :param len: Amount of vertices that will fit into this buffer.\n" - " :type type: `int`\n" " :param format: Vertex format.\n" - " :type buf: :class:`gpu.types.GPUVertFormat`\n"); + " :type buf: :class:`gpu.types.GPUVertFormat`\n" + " :param len: Amount of vertices that will fit into this buffer.\n" + " :type type: `int`\n"); PyTypeObject BPyGPUVertBuf_Type = { PyVarObject_HEAD_INIT(NULL, 0).tp_name = "GPUVertBuf", .tp_basicsize = sizeof(BPyGPUVertBuf), -- cgit v1.2.3 From 09a74ff8b6e815f3642229355be0a08dc1bcb391 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Thu, 14 Jul 2022 09:46:14 +1000 Subject: GHOST/SDL: add support for the key repeat flag Now all ghost back-ends support the key repeat flag (accessed as WM_EVENT_IS_REPEAT from wmEvent.flag). --- intern/ghost/intern/GHOST_SystemSDL.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/intern/ghost/intern/GHOST_SystemSDL.cpp b/intern/ghost/intern/GHOST_SystemSDL.cpp index a29bfd9489d..0ca37adced6 100644 --- a/intern/ghost/intern/GHOST_SystemSDL.cpp +++ b/intern/ghost/intern/GHOST_SystemSDL.cpp @@ -457,6 +457,7 @@ void GHOST_SystemSDL::processEvent(SDL_Event *sdl_event) SDL_Keycode sym = sdl_sub_evt.keysym.sym; GHOST_TEventType type = (sdl_sub_evt.state == SDL_PRESSED) ? GHOST_kEventKeyDown : GHOST_kEventKeyUp; + const bool is_repeat = sdl_sub_evt.repeat != 0; GHOST_WindowSDL *window = findGhostWindow( SDL_GetWindowFromID_fallback(sdl_sub_evt.windowID)); @@ -596,7 +597,7 @@ void GHOST_SystemSDL::processEvent(SDL_Event *sdl_event) } } - g_event = new GHOST_EventKey(getMilliSeconds(), type, window, gkey, sym, nullptr, false); + g_event = new GHOST_EventKey(getMilliSeconds(), type, window, gkey, sym, nullptr, is_repeat); break; } } -- cgit v1.2.3 From 931779197a9ce141eccc8b8c500f9ef726a833eb Mon Sep 17 00:00:00 2001 From: Chris Blackbourn Date: Thu, 14 Jul 2022 14:40:07 +1200 Subject: Fix T99684: Upgrade Averages Island Scale with options Scale UV and Shear Differential Revision: https://developer.blender.org/D15421 --- source/blender/blenlib/BLI_math_matrix.h | 1 + source/blender/blenlib/intern/math_matrix.c | 16 ++++ source/blender/editors/uvedit/uvedit_unwrap_ops.c | 14 ++- source/blender/geometry/GEO_uv_parametrizer.h | 5 +- source/blender/geometry/intern/uv_parametrizer.c | 103 +++++++++++++++++++--- 5 files changed, 123 insertions(+), 16 deletions(-) diff --git a/source/blender/blenlib/BLI_math_matrix.h b/source/blender/blenlib/BLI_math_matrix.h index 2cd2a299d53..c2dafbe3a1a 100644 --- a/source/blender/blenlib/BLI_math_matrix.h +++ b/source/blender/blenlib/BLI_math_matrix.h @@ -238,6 +238,7 @@ bool invert_m3_ex(float m[3][3], float epsilon); bool invert_m3_m3_ex(float m1[3][3], const float m2[3][3], float epsilon); bool invert_m3(float R[3][3]); +bool invert_m2_m2(float R[2][2], const float A[2][2]); bool invert_m3_m3(float R[3][3], const float A[3][3]); bool invert_m4(float R[4][4]); bool invert_m4_m4(float R[4][4], const float A[4][4]); diff --git a/source/blender/blenlib/intern/math_matrix.c b/source/blender/blenlib/intern/math_matrix.c index ce9abc36cad..fcd017b3082 100644 --- a/source/blender/blenlib/intern/math_matrix.c +++ b/source/blender/blenlib/intern/math_matrix.c @@ -1116,6 +1116,22 @@ double determinant_m3_array_db(const double m[3][3]) m[2][0] * (m[0][1] * m[1][2] - m[0][2] * m[1][1])); } +bool invert_m2_m2(float m1[2][2], const float m2[2][2]) +{ + adjoint_m2_m2(m1, m2); + float det = determinant_m2(m2[0][0], m2[1][0], m2[0][1], m2[1][1]); + + bool success = (det != 0.0f); + if (success) { + m1[0][0] /= det; + m1[1][0] /= det; + m1[0][1] /= det; + m1[1][1] /= det; + } + + return success; +} + bool invert_m3_ex(float m[3][3], const float epsilon) { float tmp[3][3]; diff --git a/source/blender/editors/uvedit/uvedit_unwrap_ops.c b/source/blender/editors/uvedit/uvedit_unwrap_ops.c index ae81aaffeb2..7ec80a05f6d 100644 --- a/source/blender/editors/uvedit/uvedit_unwrap_ops.c +++ b/source/blender/editors/uvedit/uvedit_unwrap_ops.c @@ -1191,7 +1191,7 @@ void UV_OT_pack_islands(wmOperatorType *ot) /** \name Average UV Islands Scale Operator * \{ */ -static int average_islands_scale_exec(bContext *C, wmOperator *UNUSED(op)) +static int average_islands_scale_exec(bContext *C, wmOperator *op) { const Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); @@ -1215,8 +1215,12 @@ static int average_islands_scale_exec(bContext *C, wmOperator *UNUSED(op)) return OPERATOR_CANCELLED; } + /* RNA props */ + const bool scale_uv = RNA_boolean_get(op->ptr, "scale_uv"); + const bool shear = RNA_boolean_get(op->ptr, "shear"); + ParamHandle *handle = construct_param_handle_multi(scene, objects, objects_len, &options); - GEO_uv_parametrizer_average(handle, false); + GEO_uv_parametrizer_average(handle, false, scale_uv, shear); GEO_uv_parametrizer_flush(handle); GEO_uv_parametrizer_delete(handle); @@ -1247,6 +1251,10 @@ void UV_OT_average_islands_scale(wmOperatorType *ot) /* api callbacks */ ot->exec = average_islands_scale_exec; ot->poll = ED_operator_uvedit; + + /* properties */ + RNA_def_boolean(ot->srna, "scale_uv", false, "Non-Uniform", "Scale U and V independently"); + RNA_def_boolean(ot->srna, "shear", false, "Shear", "Reduce shear within islands"); } /** \} */ @@ -1845,7 +1853,7 @@ static void uvedit_unwrap(const Scene *scene, result_info ? &result_info->count_failed : NULL); GEO_uv_parametrizer_lscm_end(handle); - GEO_uv_parametrizer_average(handle, true); + GEO_uv_parametrizer_average(handle, true, false, false); GEO_uv_parametrizer_flush(handle); diff --git a/source/blender/geometry/GEO_uv_parametrizer.h b/source/blender/geometry/GEO_uv_parametrizer.h index 2181f95945e..5285aefbd4c 100644 --- a/source/blender/geometry/GEO_uv_parametrizer.h +++ b/source/blender/geometry/GEO_uv_parametrizer.h @@ -103,7 +103,10 @@ void GEO_uv_parametrizer_pack(ParamHandle *handle, /** \name Average area for all charts * \{ */ -void GEO_uv_parametrizer_average(ParamHandle *handle, bool ignore_pinned); +void GEO_uv_parametrizer_average(ParamHandle *handle, + bool ignore_pinned, + bool scale_uv, + bool shear); /** \} */ diff --git a/source/blender/geometry/intern/uv_parametrizer.c b/source/blender/geometry/intern/uv_parametrizer.c index c75a302f8dc..38eb50722cf 100644 --- a/source/blender/geometry/intern/uv_parametrizer.c +++ b/source/blender/geometry/intern/uv_parametrizer.c @@ -148,7 +148,8 @@ typedef struct PChart { } lscm; struct PChartPack { float rescale, area; - float size[2] /* , trans[2] */; + float size[2]; + float origin[2]; } pack; } u; @@ -4243,7 +4244,10 @@ void GEO_uv_parametrizer_pack(ParamHandle *handle, } } -void GEO_uv_parametrizer_average(ParamHandle *phandle, bool ignore_pinned) +void GEO_uv_parametrizer_average(ParamHandle *phandle, + bool ignore_pinned, + bool scale_uv, + bool shear) { PChart *chart; int i; @@ -4263,6 +4267,83 @@ void GEO_uv_parametrizer_average(ParamHandle *phandle, bool ignore_pinned) continue; } + p_chart_uv_bbox(chart, minv, maxv); + mid_v2_v2v2(chart->u.pack.origin, minv, maxv); + + if (scale_uv || shear) { + /* It's possible that for some "bad" inputs, the following iteration will converge slowly or + * perhaps even diverge. Rather than infinite loop, we only iterate a maximum of `max_iter` + * times. (Also useful when making changes to the calculation.) */ + int max_iter = 10; + for (int j = 0; j < max_iter; j++) { + /* An island could contain millions of polygons. When summing many small values, we need to + * use double precision in the accumulator to maintain accuracy. Note that the individual + * calculations only need to be at single precision.*/ + double scale_cou = 0; + double scale_cov = 0; + double scale_cross = 0; + double weight_sum = 0; + for (PFace *f = chart->faces; f; f = f->nextlink) { + float m[2][2], s[2][2]; + PVert *va = f->edge->vert; + PVert *vb = f->edge->next->vert; + PVert *vc = f->edge->next->next->vert; + s[0][0] = va->uv[0] - vc->uv[0]; + s[0][1] = va->uv[1] - vc->uv[1]; + s[1][0] = vb->uv[0] - vc->uv[0]; + s[1][1] = vb->uv[1] - vc->uv[1]; + /* Find the "U" axis and "V" axis in triangle co-ordinates. Normally this would require + * SVD, but in 2D we can use a cheaper matrix inversion instead.*/ + if (!invert_m2_m2(m, s)) { + continue; + } + float cou[3], cov[3]; /* i.e. Texture "U" and texture "V" in 3D co-ordinates.*/ + for (int i = 0; i < 3; i++) { + cou[i] = m[0][0] * (va->co[i] - vc->co[i]) + m[0][1] * (vb->co[i] - vc->co[i]); + cov[i] = m[1][0] * (va->co[i] - vc->co[i]) + m[1][1] * (vb->co[i] - vc->co[i]); + } + const float weight = p_face_area(f); + scale_cou += len_v3(cou) * weight; + scale_cov += len_v3(cov) * weight; + if (shear) { + normalize_v3(cov); + normalize_v3(cou); + + /* Why is scale_cross called `cross` when we call `dot`? The next line calculates: + * `scale_cross += length(cross(cross(cou, face_normal), cov))` + * By construction, both `cou` and `cov` are orthogonal to the face normal. + * By definition, the normal vector has unit length. */ + scale_cross += dot_v3v3(cou, cov) * weight; + } + weight_sum += weight; + } + if (scale_cou * scale_cov < 1e-10f) { + break; + } + const float scale_factor_u = scale_uv ? sqrtf(scale_cou / scale_cov) : 1.0f; + + /* Compute correction transform. */ + float t[2][2]; + t[0][0] = scale_factor_u; + t[1][0] = clamp_f((float)(scale_cross / weight_sum), -0.5f, 0.5f); + t[0][1] = 0; + t[1][1] = 1.0f / scale_factor_u; + + /* Apply the correction. */ + p_chart_uv_transform(chart, t); + + /* How far from the identity transform are we? [[1,0],[0,1]] */ + const float err = fabsf(t[0][0] - 1.0f) + fabsf(t[1][0]) + fabsf(t[0][1]) + + fabsf(t[1][1] - 1.0f); + + const float tolerance = 1e-6f; /* Trade accuracy for performance. */ + if (err < tolerance) { + /* Too slow? Use Richardson Extrapolation to accelerate the convergence.*/ + break; + } + } + } + chart->u.pack.area = 0.0f; /* 3d area */ chart->u.pack.rescale = 0.0f; /* UV area, abusing rescale for tmp storage, oh well :/ */ @@ -4292,18 +4373,16 @@ void GEO_uv_parametrizer_average(ParamHandle *phandle, bool ignore_pinned) if (chart->u.pack.area != 0.0f && chart->u.pack.rescale != 0.0f) { fac = chart->u.pack.area / chart->u.pack.rescale; - /* Get the island center */ - p_chart_uv_bbox(chart, minv, maxv); - trans[0] = (minv[0] + maxv[0]) / -2.0f; - trans[1] = (minv[1] + maxv[1]) / -2.0f; - - /* Move center to 0,0 */ - p_chart_uv_translate(chart, trans); + /* Average scale. */ p_chart_uv_scale(chart, sqrtf(fac / tot_fac)); - /* Move to original center */ - trans[0] = -trans[0]; - trans[1] = -trans[1]; + /* Get the current island center. */ + p_chart_uv_bbox(chart, minv, maxv); + + /* Move to original center. */ + mid_v2_v2v2(trans, minv, maxv); + negate_v2(trans); + add_v2_v2(trans, chart->u.pack.origin); p_chart_uv_translate(chart, trans); } } -- cgit v1.2.3 From d3374e5337d5d50b842e274348813c15d4f57705 Mon Sep 17 00:00:00 2001 From: Chris Blackbourn Date: Thu, 14 Jul 2022 16:19:21 +1200 Subject: Fix build and warnings from previous commit. --- source/blender/geometry/intern/uv_parametrizer.c | 9 ++++----- source/blender/nodes/geometry/nodes/node_geo_uv_unwrap.cc | 2 +- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/source/blender/geometry/intern/uv_parametrizer.c b/source/blender/geometry/intern/uv_parametrizer.c index 38eb50722cf..7ef17d4e9d0 100644 --- a/source/blender/geometry/intern/uv_parametrizer.c +++ b/source/blender/geometry/intern/uv_parametrizer.c @@ -4260,7 +4260,6 @@ void GEO_uv_parametrizer_average(ParamHandle *phandle, } for (i = 0; i < phandle->ncharts; i++) { - PFace *f; chart = phandle->charts[i]; if (ignore_pinned && (chart->flag & PCHART_HAS_PINS)) { @@ -4298,9 +4297,9 @@ void GEO_uv_parametrizer_average(ParamHandle *phandle, continue; } float cou[3], cov[3]; /* i.e. Texture "U" and texture "V" in 3D co-ordinates.*/ - for (int i = 0; i < 3; i++) { - cou[i] = m[0][0] * (va->co[i] - vc->co[i]) + m[0][1] * (vb->co[i] - vc->co[i]); - cov[i] = m[1][0] * (va->co[i] - vc->co[i]) + m[1][1] * (vb->co[i] - vc->co[i]); + for (int k = 0; k < 3; k++) { + cou[k] = m[0][0] * (va->co[k] - vc->co[k]) + m[0][1] * (vb->co[k] - vc->co[k]); + cov[k] = m[1][0] * (va->co[k] - vc->co[k]) + m[1][1] * (vb->co[k] - vc->co[k]); } const float weight = p_face_area(f); scale_cou += len_v3(cou) * weight; @@ -4347,7 +4346,7 @@ void GEO_uv_parametrizer_average(ParamHandle *phandle, chart->u.pack.area = 0.0f; /* 3d area */ chart->u.pack.rescale = 0.0f; /* UV area, abusing rescale for tmp storage, oh well :/ */ - for (f = chart->faces; f; f = f->nextlink) { + for (PFace *f = chart->faces; f; f = f->nextlink) { chart->u.pack.area += p_face_area(f); chart->u.pack.rescale += fabsf(p_face_uv_area_signed(f)); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_uv_unwrap.cc b/source/blender/nodes/geometry/nodes/node_geo_uv_unwrap.cc index 2ec14ad2d29..03657f3e016 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_uv_unwrap.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_uv_unwrap.cc @@ -121,7 +121,7 @@ static VArray construct_uv_gvarray(const MeshComponent &component, GEO_uv_parametrizer_lscm_begin(handle, false, method == GEO_NODE_UV_UNWRAP_METHOD_ANGLE_BASED); GEO_uv_parametrizer_lscm_solve(handle, nullptr, nullptr); GEO_uv_parametrizer_lscm_end(handle); - GEO_uv_parametrizer_average(handle, true); + GEO_uv_parametrizer_average(handle, true, false, false); GEO_uv_parametrizer_pack(handle, margin, true, true); GEO_uv_parametrizer_flush(handle); GEO_uv_parametrizer_delete(handle); -- cgit v1.2.3 From 816a73891b69e2060c5b99d599e2a99e273db124 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Thu, 14 Jul 2022 13:23:35 +1000 Subject: GHOST/SDL: pass in utf8 buffer for keyboard events While GHOST/SDL doesn't support non-ASCII text input, use the UTF8 buffer to be consistent with all other back-ends. Move the conversion from SDL_KeyboardEvent to ASCII into a function. Also only lookup this value on key press (not release). --- intern/ghost/intern/GHOST_SystemSDL.cpp | 274 +++++++++++++++++--------------- 1 file changed, 143 insertions(+), 131 deletions(-) diff --git a/intern/ghost/intern/GHOST_SystemSDL.cpp b/intern/ghost/intern/GHOST_SystemSDL.cpp index 0ca37adced6..55a15ae470e 100644 --- a/intern/ghost/intern/GHOST_SystemSDL.cpp +++ b/intern/ghost/intern/GHOST_SystemSDL.cpp @@ -279,6 +279,141 @@ static GHOST_TKey convertSDLKey(SDL_Scancode key) } #undef GXMAP +static char convert_keyboard_event_to_ascii(const SDL_KeyboardEvent &sdl_sub_evt) +{ + SDL_Keycode sym = sdl_sub_evt.keysym.sym; + if (sym > 127) { + switch (sym) { + case SDLK_KP_DIVIDE: + sym = '/'; + break; + case SDLK_KP_MULTIPLY: + sym = '*'; + break; + case SDLK_KP_MINUS: + sym = '-'; + break; + case SDLK_KP_PLUS: + sym = '+'; + break; + case SDLK_KP_1: + sym = '1'; + break; + case SDLK_KP_2: + sym = '2'; + break; + case SDLK_KP_3: + sym = '3'; + break; + case SDLK_KP_4: + sym = '4'; + break; + case SDLK_KP_5: + sym = '5'; + break; + case SDLK_KP_6: + sym = '6'; + break; + case SDLK_KP_7: + sym = '7'; + break; + case SDLK_KP_8: + sym = '8'; + break; + case SDLK_KP_9: + sym = '9'; + break; + case SDLK_KP_0: + sym = '0'; + break; + case SDLK_KP_PERIOD: + sym = '.'; + break; + default: + sym = 0; + break; + } + } + else { + if (sdl_sub_evt.keysym.mod & (KMOD_LSHIFT | KMOD_RSHIFT)) { + /* Weak US keyboard assumptions. */ + if (sym >= 'a' && sym <= ('a' + 32)) { + sym -= 32; + } + else { + switch (sym) { + case '`': + sym = '~'; + break; + case '1': + sym = '!'; + break; + case '2': + sym = '@'; + break; + case '3': + sym = '#'; + break; + case '4': + sym = '$'; + break; + case '5': + sym = '%'; + break; + case '6': + sym = '^'; + break; + case '7': + sym = '&'; + break; + case '8': + sym = '*'; + break; + case '9': + sym = '('; + break; + case '0': + sym = ')'; + break; + case '-': + sym = '_'; + break; + case '=': + sym = '+'; + break; + case '[': + sym = '{'; + break; + case ']': + sym = '}'; + break; + case '\\': + sym = '|'; + break; + case ';': + sym = ':'; + break; + case '\'': + sym = '"'; + break; + case ',': + sym = '<'; + break; + case '.': + sym = '>'; + break; + case '/': + sym = '?'; + break; + default: + break; + } + } + } + } + return (char)sym; +} + /** * Events don't always have valid windows, * but GHOST needs a window _always_. fallback to the GL window. @@ -454,7 +589,6 @@ void GHOST_SystemSDL::processEvent(SDL_Event *sdl_event) case SDL_KEYDOWN: case SDL_KEYUP: { SDL_KeyboardEvent &sdl_sub_evt = sdl_event->key; - SDL_Keycode sym = sdl_sub_evt.keysym.sym; GHOST_TEventType type = (sdl_sub_evt.state == SDL_PRESSED) ? GHOST_kEventKeyDown : GHOST_kEventKeyUp; const bool is_repeat = sdl_sub_evt.repeat != 0; @@ -466,138 +600,16 @@ void GHOST_SystemSDL::processEvent(SDL_Event *sdl_event) GHOST_TKey gkey = convertSDLKey(sdl_sub_evt.keysym.scancode); /* NOTE: the `sdl_sub_evt.keysym.sym` is truncated, * for unicode support ghost has to be modified. */ - // printf("%d\n", sym); - if (sym > 127) { - switch (sym) { - case SDLK_KP_DIVIDE: - sym = '/'; - break; - case SDLK_KP_MULTIPLY: - sym = '*'; - break; - case SDLK_KP_MINUS: - sym = '-'; - break; - case SDLK_KP_PLUS: - sym = '+'; - break; - case SDLK_KP_1: - sym = '1'; - break; - case SDLK_KP_2: - sym = '2'; - break; - case SDLK_KP_3: - sym = '3'; - break; - case SDLK_KP_4: - sym = '4'; - break; - case SDLK_KP_5: - sym = '5'; - break; - case SDLK_KP_6: - sym = '6'; - break; - case SDLK_KP_7: - sym = '7'; - break; - case SDLK_KP_8: - sym = '8'; - break; - case SDLK_KP_9: - sym = '9'; - break; - case SDLK_KP_0: - sym = '0'; - break; - case SDLK_KP_PERIOD: - sym = '.'; - break; - default: - sym = 0; - break; - } - } - else { - if (sdl_sub_evt.keysym.mod & (KMOD_LSHIFT | KMOD_RSHIFT)) { - /* lame US keyboard assumptions */ - if (sym >= 'a' && sym <= ('a' + 32)) { - sym -= 32; - } - else { - switch (sym) { - case '`': - sym = '~'; - break; - case '1': - sym = '!'; - break; - case '2': - sym = '@'; - break; - case '3': - sym = '#'; - break; - case '4': - sym = '$'; - break; - case '5': - sym = '%'; - break; - case '6': - sym = '^'; - break; - case '7': - sym = '&'; - break; - case '8': - sym = '*'; - break; - case '9': - sym = '('; - break; - case '0': - sym = ')'; - break; - case '-': - sym = '_'; - break; - case '=': - sym = '+'; - break; - case '[': - sym = '{'; - break; - case ']': - sym = '}'; - break; - case '\\': - sym = '|'; - break; - case ';': - sym = ':'; - break; - case '\'': - sym = '"'; - break; - case ',': - sym = '<'; - break; - case '.': - sym = '>'; - break; - case '/': - sym = '?'; - break; - default: - break; - } - } - } + + /* TODO(@campbellbarton): support full unicode, SDL supports this but it needs to be + * explicitly enabled via #SDL_StartTextInput which GHOST would have to wrap. */ + char utf8_buf[sizeof(GHOST_TEventKeyData::utf8_buf)] = {'\0'}; + if (type == GHOST_kEventKeyDown) { + utf8_buf[0] = convert_keyboard_event_to_ascii(sdl_sub_evt); } - g_event = new GHOST_EventKey(getMilliSeconds(), type, window, gkey, sym, nullptr, is_repeat); + g_event = new GHOST_EventKey( + getMilliSeconds(), type, window, gkey, '\0', utf8_buf, is_repeat); break; } } -- cgit v1.2.3 From d6fef73ef110eb43756b7b87c2cba80abae3b39f Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Thu, 14 Jul 2022 13:54:26 +1000 Subject: WM: Remove ASCII members from wmEvent & GHOST_TEventKeyData The `ascii` member was only kept for historic reason as some platforms didn't support utf8 when it was first introduced. Remove the `ascii` struct members since many checks used this as a fall-back for utf8_buf not being set which isn't needed. There are a few cases where it's convenient to access the ASCII value of an event (or nil) so a function has been added to do that. *Details* - WM_event_utf8_to_ascii() has been added for the few cases an events ASCII value needs to be accessed, this just avoids having to do multi-byte character checks in-line. - RNA Event.ascii remains, using utf8_buf[0] for single byte characters. - GHOST_TEventKeyData.ascii has been removed. - To avoid regressions non-ASCII Latin1 characters from GHOST are converted into multi-byte UTF8, when building X11 without XInput & X_HAVE_UTF8_STRING it seems like could still occur. --- intern/ghost/GHOST_Types.h | 11 -------- intern/ghost/intern/GHOST_EventKey.h | 13 ++++++--- intern/ghost/test/multitest/EventToBuf.c | 6 ++-- source/blender/editors/armature/pose_lib.c | 4 +-- source/blender/editors/curve/editfont.c | 2 +- .../blender/editors/interface/interface_handlers.c | 32 +++++----------------- source/blender/editors/space_console/console_ops.c | 12 ++------ source/blender/editors/space_text/text_ops.c | 17 ++++-------- source/blender/editors/util/numinput.c | 13 +++++---- source/blender/makesrna/intern/rna_wm.c | 4 +-- source/blender/makesrna/intern/rna_wm_api.c | 6 ---- source/blender/windowmanager/WM_api.h | 1 + source/blender/windowmanager/WM_types.h | 8 ++---- .../blender/windowmanager/intern/wm_event_query.c | 17 ++++++++++-- .../windowmanager/intern/wm_event_system.cc | 25 +++++++++++------ source/blender/windowmanager/intern/wm_window.c | 1 - 16 files changed, 75 insertions(+), 97 deletions(-) diff --git a/intern/ghost/GHOST_Types.h b/intern/ghost/GHOST_Types.h index 2fc84349eb9..fa74bfde866 100644 --- a/intern/ghost/GHOST_Types.h +++ b/intern/ghost/GHOST_Types.h @@ -547,17 +547,6 @@ typedef struct { /** The key code. */ GHOST_TKey key; - /* ascii / utf8: both should always be set when possible, - * - ascii may be '\0' however if the user presses a non ascii key - * - unicode may not be set if the system has no unicode support - * - * These values are intended to be used as follows. - * For text input use unicode when available, fallback to ascii. - * For areas where unicode is not needed, number input for example, always - * use ascii, unicode is ignored - campbell. - */ - /** The ascii code for the key event ('\0' if none). */ - char ascii; /** The unicode character. if the length is 6, not NULL terminated if all 6 are set. */ char utf8_buf[6]; diff --git a/intern/ghost/intern/GHOST_EventKey.h b/intern/ghost/intern/GHOST_EventKey.h index 1c3156c27d2..d3cfbbeddd7 100644 --- a/intern/ghost/intern/GHOST_EventKey.h +++ b/intern/ghost/intern/GHOST_EventKey.h @@ -28,7 +28,6 @@ class GHOST_EventKey : public GHOST_Event { : GHOST_Event(msec, type, window) { m_keyEventData.key = key; - m_keyEventData.ascii = '\0'; m_keyEventData.utf8_buf[0] = '\0'; m_keyEventData.is_repeat = is_repeat; m_data = &m_keyEventData; @@ -51,11 +50,17 @@ class GHOST_EventKey : public GHOST_Event { : GHOST_Event(msec, type, window) { m_keyEventData.key = key; - m_keyEventData.ascii = ascii; - if (utf8_buf) + if (utf8_buf) { memcpy(m_keyEventData.utf8_buf, utf8_buf, sizeof(m_keyEventData.utf8_buf)); - else + } + else { m_keyEventData.utf8_buf[0] = '\0'; + } + /* TODO(@campbellbarton): phase out `ascii` and always use `utf8_buf`, this needs to be done + * on all platforms though, so for now write the ascii into the utf8_buf. */ + if (m_keyEventData.utf8_buf[0] == '\0' && ascii) { + m_keyEventData.utf8_buf[0] = ascii; + } m_keyEventData.is_repeat = is_repeat; m_data = &m_keyEventData; } diff --git a/intern/ghost/test/multitest/EventToBuf.c b/intern/ghost/test/multitest/EventToBuf.c index baab32328c3..846a867a371 100644 --- a/intern/ghost/test/multitest/EventToBuf.c +++ b/intern/ghost/test/multitest/EventToBuf.c @@ -218,8 +218,10 @@ void event_to_buf(GHOST_EventHandle evt, char buf[128]) case GHOST_kEventKeyUp: { GHOST_TEventKeyData *kd = data; pos += sprintf(pos, " - key: %s (%d)", keytype_to_string(kd->key), kd->key); - if (kd->ascii) - pos += sprintf(pos, " ascii: '%c' (%d)", kd->ascii, kd->ascii); + /* TODO: ideally this would print the unicode character. */ + if (kd->utf8_buf[0]) { + pos += sprintf(pos, " ascii: '%c' (%d)", kd->utf8_buf[0], kd->utf8_buf[0]); + } break; } } diff --git a/source/blender/editors/armature/pose_lib.c b/source/blender/editors/armature/pose_lib.c index b8e7c2624fd..a6742fbe2bf 100644 --- a/source/blender/editors/armature/pose_lib.c +++ b/source/blender/editors/armature/pose_lib.c @@ -1578,7 +1578,7 @@ static int poselib_preview_handle_event(bContext *UNUSED(C), wmOperator *op, con case EVT_PADMINUS: if (pld->searchstr[0]) { /* searching... */ - poselib_preview_handle_search(pld, event->type, event->ascii); + poselib_preview_handle_search(pld, event->type, WM_event_utf8_to_ascii(event)); } else { /* view manipulation (see above) */ @@ -1589,7 +1589,7 @@ static int poselib_preview_handle_event(bContext *UNUSED(C), wmOperator *op, con /* otherwise, assume that searching might be able to handle it */ default: - poselib_preview_handle_search(pld, event->type, event->ascii); + poselib_preview_handle_search(pld, event->type, WM_event_utf8_to_ascii(event)); break; } diff --git a/source/blender/editors/curve/editfont.c b/source/blender/editors/curve/editfont.c index 611dbb2e80c..33e3837c9d4 100644 --- a/source/blender/editors/curve/editfont.c +++ b/source/blender/editors/curve/editfont.c @@ -1639,7 +1639,7 @@ static int insert_text_invoke(bContext *C, wmOperator *op, const wmEvent *event) Curve *cu = obedit->data; EditFont *ef = cu->editfont; static int accentcode = 0; - uintptr_t ascii = event->ascii; + uintptr_t ascii = WM_event_utf8_to_ascii(event); const bool alt = event->modifier & KM_ALT; const bool shift = event->modifier & KM_SHIFT; const bool ctrl = event->modifier & KM_CTRL; diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c index 21fd14b86b7..2ad2cd15c43 100644 --- a/source/blender/editors/interface/interface_handlers.c +++ b/source/blender/editors/interface/interface_handlers.c @@ -3172,21 +3172,6 @@ static bool ui_textedit_insert_buf(uiBut *but, return changed; } -static bool ui_textedit_insert_ascii(uiBut *but, uiHandleButtonData *data, char ascii) -{ - const char buf[2] = {ascii, '\0'}; - - if (UI_but_is_utf8(but) && (BLI_str_utf8_size(buf) == -1)) { - printf( - "%s: entering invalid ascii char into an ascii key (%d)\n", __func__, (int)(uchar)ascii); - - return false; - } - - /* in some cases we want to allow invalid utf8 chars */ - return ui_textedit_insert_buf(but, data, buf, 1); -} - static void ui_textedit_move(uiBut *but, uiHandleButtonData *data, eStrCursorJumpDirection direction, @@ -3897,30 +3882,27 @@ static void ui_do_but_textedit( } } - if ((event->ascii || event->utf8_buf[0]) && (retval == WM_UI_HANDLER_CONTINUE) + if ((event->utf8_buf[0]) && (retval == WM_UI_HANDLER_CONTINUE) #ifdef WITH_INPUT_IME && !is_ime_composing && !WM_event_is_ime_switch(event) #endif ) { - char ascii = event->ascii; + char utf8_buf_override[2] = {'\0', '\0'}; const char *utf8_buf = event->utf8_buf; /* Exception that's useful for number buttons, some keyboard * numpads have a comma instead of a period. */ if (ELEM(but->type, UI_BTYPE_NUM, UI_BTYPE_NUM_SLIDER)) { /* Could use `data->min`. */ - if (event->type == EVT_PADPERIOD && ascii == ',') { - ascii = '.'; - utf8_buf = NULL; /* force ascii fallback */ + if ((event->type == EVT_PADPERIOD) && (utf8_buf[0] == ',')) { + utf8_buf_override[0] = '.'; + utf8_buf = utf8_buf_override; } } - if (utf8_buf && utf8_buf[0]) { + if (utf8_buf[0]) { const int utf8_buf_len = BLI_str_utf8_size(utf8_buf); BLI_assert(utf8_buf_len != -1); - changed = ui_textedit_insert_buf(but, data, event->utf8_buf, utf8_buf_len); - } - else { - changed = ui_textedit_insert_ascii(but, data, ascii); + changed = ui_textedit_insert_buf(but, data, utf8_buf, utf8_buf_len); } retval = WM_UI_HANDLER_BREAK; diff --git a/source/blender/editors/space_console/console_ops.c b/source/blender/editors/space_console/console_ops.c index 17fbef23eac..ef22b1b9f0b 100644 --- a/source/blender/editors/space_console/console_ops.c +++ b/source/blender/editors/space_console/console_ops.c @@ -413,16 +413,8 @@ static int console_insert_invoke(bContext *C, wmOperator *op, const wmEvent *eve } char str[BLI_UTF8_MAX + 1]; - size_t len; - - if (event->utf8_buf[0]) { - len = BLI_str_utf8_size_safe(event->utf8_buf); - memcpy(str, event->utf8_buf, len); - } - else { - /* in theory, ghost can set value to extended ascii here */ - len = BLI_str_utf8_from_unicode(event->ascii, str, sizeof(str) - 1); - } + const size_t len = BLI_str_utf8_size_safe(event->utf8_buf); + memcpy(str, event->utf8_buf, len); str[len] = '\0'; RNA_string_set(op->ptr, "text", str); } diff --git a/source/blender/editors/space_text/text_ops.c b/source/blender/editors/space_text/text_ops.c index 05d51cf6362..33219092d20 100644 --- a/source/blender/editors/space_text/text_ops.c +++ b/source/blender/editors/space_text/text_ops.c @@ -3396,7 +3396,8 @@ static int text_line_number_invoke(bContext *C, wmOperator *UNUSED(op), const wm return OPERATOR_PASS_THROUGH; } - if (!(event->ascii >= '0' && event->ascii <= '9')) { + const char event_ascii = WM_event_utf8_to_ascii(event); + if (!(event_ascii >= '0' && event_ascii <= '9')) { return OPERATOR_PASS_THROUGH; } @@ -3406,7 +3407,7 @@ static int text_line_number_invoke(bContext *C, wmOperator *UNUSED(op), const wm } jump_to *= 10; - jump_to += (int)(event->ascii - '0'); + jump_to += (int)(event_ascii - '0'); txt_move_toline(text, jump_to - 1, 0); last_jump = time; @@ -3495,16 +3496,8 @@ static int text_insert_invoke(bContext *C, wmOperator *op, const wmEvent *event) } char str[BLI_UTF8_MAX + 1]; - size_t len; - - if (event->utf8_buf[0]) { - len = BLI_str_utf8_size_safe(event->utf8_buf); - memcpy(str, event->utf8_buf, len); - } - else { - /* in theory, ghost can set value to extended ascii here */ - len = BLI_str_utf8_from_unicode(event->ascii, str, sizeof(str) - 1); - } + const size_t len = BLI_str_utf8_size_safe(event->utf8_buf); + memcpy(str, event->utf8_buf, len); str[len] = '\0'; RNA_string_set(op->ptr, "text", str); diff --git a/source/blender/editors/util/numinput.c b/source/blender/editors/util/numinput.c index be6ac6e13e6..60cbc2a2df6 100644 --- a/source/blender/editors/util/numinput.c +++ b/source/blender/editors/util/numinput.c @@ -311,6 +311,7 @@ static bool editstr_is_simple_numinput(const char ascii) bool handleNumInput(bContext *C, NumInput *n, const wmEvent *event) { const char *utf8_buf = NULL; + const char event_ascii = WM_event_utf8_to_ascii(event); char ascii[2] = {'\0', '\0'}; bool updated = false; short idx = n->idx, idx_max = n->idx_max; @@ -321,8 +322,8 @@ bool handleNumInput(bContext *C, NumInput *n, const wmEvent *event) if (U.flag & USER_FLAG_NUMINPUT_ADVANCED) #endif { - if (((event->modifier & (KM_CTRL | KM_ALT)) == 0) && (event->ascii != '\0') && - strchr("01234567890@%^&*-+/{}()[]<>.|", event->ascii)) { + if (((event->modifier & (KM_CTRL | KM_ALT)) == 0) && (event_ascii != '\0') && + strchr("01234567890@%^&*-+/{}()[]<>.|", event_ascii)) { if (!(n->flag & NUM_EDIT_FULL)) { n->flag |= NUM_EDITED; n->flag |= NUM_EDIT_FULL; @@ -333,7 +334,7 @@ bool handleNumInput(bContext *C, NumInput *n, const wmEvent *event) #ifdef USE_FAKE_EDIT /* XXX Hack around keyboards without direct access to '=' nor '*'... */ - if (ELEM(event->ascii, '=', '*')) { + if (ELEM(event_ascii, '=', '*')) { if (!(n->flag & NUM_EDIT_FULL)) { n->flag |= NUM_EDIT_FULL; n->val_flag[idx] |= NUM_EDITED; @@ -357,7 +358,7 @@ bool handleNumInput(bContext *C, NumInput *n, const wmEvent *event) else { /* might be a char too... */ utf8_buf = event->utf8_buf; - ascii[0] = event->ascii; + ascii[0] = event_ascii; } break; case EVT_BACKSPACEKEY: @@ -523,9 +524,9 @@ bool handleNumInput(bContext *C, NumInput *n, const wmEvent *event) break; } - if (!updated && !utf8_buf && (event->utf8_buf[0] || event->ascii)) { + if (!updated && !utf8_buf && event->utf8_buf[0]) { utf8_buf = event->utf8_buf; - ascii[0] = event->ascii; + ascii[0] = event_ascii; } /* Up to this point, if we have a ctrl modifier, skip. diff --git a/source/blender/makesrna/intern/rna_wm.c b/source/blender/makesrna/intern/rna_wm.c index 7893a2f4bc0..2009f51e1f2 100644 --- a/source/blender/makesrna/intern/rna_wm.c +++ b/source/blender/makesrna/intern/rna_wm.c @@ -607,14 +607,14 @@ static PointerRNA rna_OperatorMacro_properties_get(PointerRNA *ptr) static void rna_Event_ascii_get(PointerRNA *ptr, char *value) { const wmEvent *event = ptr->data; - value[0] = event->ascii; + value[0] = WM_event_utf8_to_ascii(event); value[1] = '\0'; } static int rna_Event_ascii_length(PointerRNA *ptr) { const wmEvent *event = ptr->data; - return (event->ascii) ? 1 : 0; + return WM_event_utf8_to_ascii(event) ? 1 : 0; } static void rna_Event_unicode_get(PointerRNA *ptr, char *value) diff --git a/source/blender/makesrna/intern/rna_wm_api.c b/source/blender/makesrna/intern/rna_wm_api.c index b9f36d35ee8..eb5e2549b1d 100644 --- a/source/blender/makesrna/intern/rna_wm_api.c +++ b/source/blender/makesrna/intern/rna_wm_api.c @@ -639,16 +639,12 @@ static wmEvent *rna_Window_event_add_simulate(wmWindow *win, } /* TODO: validate NDOF. */ - char ascii = 0; if (unicode != NULL) { int len = BLI_str_utf8_size(unicode); if (len == -1 || unicode[len] != '\0') { BKE_report(reports, RPT_ERROR, "Only a single character supported"); return NULL; } - if (len == 1 && isascii(unicode[0])) { - ascii = unicode[0]; - } } wmEvent e = *win->eventstate; @@ -672,10 +668,8 @@ static wmEvent *rna_Window_event_add_simulate(wmWindow *win, e.modifier |= KM_OSKEY; } - e.ascii = '\0'; e.utf8_buf[0] = '\0'; if (unicode != NULL) { - e.ascii = ascii; STRNCPY(e.utf8_buf, unicode); } diff --git a/source/blender/windowmanager/WM_api.h b/source/blender/windowmanager/WM_api.h index d852b85a3d0..f337dd9d89a 100644 --- a/source/blender/windowmanager/WM_api.h +++ b/source/blender/windowmanager/WM_api.h @@ -1530,6 +1530,7 @@ bool WM_event_is_modal_drag_exit(const struct wmEvent *event, bool WM_event_is_mouse_drag(const struct wmEvent *event); bool WM_event_is_mouse_drag_or_press(const wmEvent *event); int WM_event_drag_direction(const wmEvent *event); +char WM_event_utf8_to_ascii(const struct wmEvent *event) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT; /** * Detect motion between selection (callers should only use this for selection picking), diff --git a/source/blender/windowmanager/WM_types.h b/source/blender/windowmanager/WM_types.h index 32bc2f96365..e7cbe936607 100644 --- a/source/blender/windowmanager/WM_types.h +++ b/source/blender/windowmanager/WM_types.h @@ -683,13 +683,11 @@ typedef struct wmEvent { /** Region relative mouse position (name convention before Blender 2.5). */ int mval[2]; /** - * From, ghost if utf8 is enabled for the platform, - * #BLI_str_utf8_size() must _always_ be valid, check - * when assigning s we don't need to check on every access after. + * A single UTF8 encoded character. + * #BLI_str_utf8_size() must _always_ return a valid value, + * check when assigning so we don't need to check on every access after. */ char utf8_buf[6]; - /** From ghost, fallback if utf8 isn't set. */ - char ascii; /** Modifier states: #KM_SHIFT, #KM_CTRL, #KM_ALT & #KM_OSKEY. */ uint8_t modifier; diff --git a/source/blender/windowmanager/intern/wm_event_query.c b/source/blender/windowmanager/intern/wm_event_query.c index 221073d288a..81044197ae7 100644 --- a/source/blender/windowmanager/intern/wm_event_query.c +++ b/source/blender/windowmanager/intern/wm_event_query.c @@ -112,7 +112,7 @@ void WM_event_print(const wmEvent *event) "wmEvent type:%d/%s, val:%d/%s, " "prev_type:%d/%s, prev_val:%d/%s, " "modifier=%s, keymodifier:%d, flag:%s, " - "mouse:(%d,%d), ascii:'%c', utf8:'%.*s', pointer:%p", + "mouse:(%d,%d), utf8:'%.*s', pointer:%p", event->type, type_id, event->val, @@ -126,7 +126,6 @@ void WM_event_print(const wmEvent *event) flag_id, event->xy[0], event->xy[1], - event->ascii, BLI_str_utf8_size(event->utf8_buf), event->utf8_buf, (const void *)event); @@ -400,6 +399,20 @@ void WM_event_drag_start_xy(const wmEvent *event, int r_xy[2]) /** \} */ +/* -------------------------------------------------------------------- */ +/** \name Event Text Queries + * \{ */ + +char WM_event_utf8_to_ascii(const struct wmEvent *event) +{ + if (BLI_str_utf8_size(event->utf8_buf) == 1) { + return event->utf8_buf[0]; + } + return '\0'; +} + +/** \} */ + /* -------------------------------------------------------------------- */ /** \name Event Preference Mapping * \{ */ diff --git a/source/blender/windowmanager/intern/wm_event_system.cc b/source/blender/windowmanager/intern/wm_event_system.cc index 0395e8bda7a..a371fa7e185 100644 --- a/source/blender/windowmanager/intern/wm_event_system.cc +++ b/source/blender/windowmanager/intern/wm_event_system.cc @@ -2088,7 +2088,7 @@ static bool wm_eventmatch(const wmEvent *winevent, const wmKeyMapItem *kmi) if (winevent->val == KM_PRESS) { /* Prevent double clicks. */ /* Not using #ISTEXTINPUT anymore because (at least on Windows) some key codes above 255 * could have printable ascii keys, See T30479. */ - if (ISKEYBOARD(winevent->type) && (winevent->ascii || winevent->utf8_buf[0])) { + if (ISKEYBOARD(winevent->type) && winevent->utf8_buf[0]) { return true; } } @@ -5042,7 +5042,6 @@ static wmEvent *wm_event_add_mousemove_to_head(wmWindow *win) tevent = *event_last; tevent.flag = (eWM_EventFlag)0; - tevent.ascii = '\0'; tevent.utf8_buf[0] = '\0'; wm_event_custom_clear(&tevent); @@ -5329,7 +5328,6 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, void break; } - event.ascii = kd->ascii; /* Might be not nullptr terminated. */ memcpy(event.utf8_buf, kd->utf8_buf, sizeof(event.utf8_buf)); if (kd->is_repeat) { @@ -5341,8 +5339,6 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, void /* Exclude arrow keys, escape, etc from text input. */ if (type == GHOST_kEventKeyUp) { - event.ascii = '\0'; - /* Ghost should do this already for key up. */ if (event.utf8_buf[0]) { CLOG_ERROR(WM_LOG_EVENTS, @@ -5351,15 +5347,28 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, void event.utf8_buf[0] = '\0'; } else { - if (event.ascii < 32 && event.ascii > 0) { - event.ascii = '\0'; - } if (event.utf8_buf[0] < 32 && event.utf8_buf[0] > 0) { event.utf8_buf[0] = '\0'; } } if (event.utf8_buf[0]) { + /* NOTE(@campbellbarton): Detect non-ASCII characters stored in `utf8_buf`, + * ideally this would never happen but it can't be ruled out for X11 which has + * special handling of Latin1 when building without UTF8 support. + * Avoid regressions by adding this conversions, it should eventually be removed. */ + if ((event.utf8_buf[0] >= 0x80) && (event.utf8_buf[1] == '\0')) { + const uint c = (uint)event.utf8_buf[0]; + int utf8_buf_len = BLI_str_utf8_from_unicode(c, event.utf8_buf, sizeof(event.utf8_buf)); + CLOG_ERROR(WM_LOG_EVENTS, + "ghost detected non-ASCII single byte character '%u', converting to utf8 " + "('%.*s', length=%d)", + c, + utf8_buf_len, + event.utf8_buf, + utf8_buf_len); + } + if (BLI_str_utf8_size(event.utf8_buf) == -1) { CLOG_ERROR(WM_LOG_EVENTS, "ghost detected an invalid unicode character '%d'", diff --git a/source/blender/windowmanager/intern/wm_window.c b/source/blender/windowmanager/intern/wm_window.c index 36f91f8414a..70640eda605 100644 --- a/source/blender/windowmanager/intern/wm_window.c +++ b/source/blender/windowmanager/intern/wm_window.c @@ -1148,7 +1148,6 @@ static bool ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr C_void_pt */ GHOST_TEventKeyData kdata = { .key = GHOST_kKeyUnknown, - .ascii = '\0', .utf8_buf = {'\0'}, .is_repeat = false, }; -- cgit v1.2.3 From b35e33317dd3c1b0b4ceb3aa0b55f805661fdb05 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Thu, 14 Jul 2022 16:10:13 +1000 Subject: Cleanup: update & correct comments for event handling - Remove references to `ISTEXTINPUT` as any keyboard event with it's utf8_buf set can be handled as text input. - Update references to the key repeat flag. --- intern/ghost/GHOST_Types.h | 7 ++++++- source/blender/editors/interface/interface_region_menu_pie.cc | 2 +- source/blender/makesdna/DNA_windowmanager_types.h | 4 ++-- source/blender/windowmanager/intern/wm_event_system.cc | 4 +--- source/blender/windowmanager/wm_event_types.h | 6 ------ 5 files changed, 10 insertions(+), 13 deletions(-) diff --git a/intern/ghost/GHOST_Types.h b/intern/ghost/GHOST_Types.h index fa74bfde866..495fb739978 100644 --- a/intern/ghost/GHOST_Types.h +++ b/intern/ghost/GHOST_Types.h @@ -550,7 +550,12 @@ typedef struct { /** The unicode character. if the length is 6, not NULL terminated if all 6 are set. */ char utf8_buf[6]; - /** Generated by auto-repeat. */ + /** + * Enabled when the key is held (auto-repeat). + * In this case press events are sent without a corresponding release/up event. + * + * All back-ends must set this variable for correct behavior regarding repeatable keys. + */ char is_repeat; } GHOST_TEventKeyData; diff --git a/source/blender/editors/interface/interface_region_menu_pie.cc b/source/blender/editors/interface/interface_region_menu_pie.cc index b11564f09c5..09902dd6c35 100644 --- a/source/blender/editors/interface/interface_region_menu_pie.cc +++ b/source/blender/editors/interface/interface_region_menu_pie.cc @@ -371,7 +371,7 @@ void ui_pie_menu_level_create(uiBlock *block, EnumPropertyItem *remaining = static_cast( MEM_mallocN(array_size + sizeof(EnumPropertyItem), "pie_level_item_array")); memcpy(remaining, items + totitem_parent, array_size); - /* A nullptr terminating sentinel element is required. */ + /* A null terminating sentinel element is required. */ memset(&remaining[totitem_remain], 0, sizeof(EnumPropertyItem)); /* yuk, static... issue is we can't reliably free this without doing dangerous changes */ diff --git a/source/blender/makesdna/DNA_windowmanager_types.h b/source/blender/makesdna/DNA_windowmanager_types.h index a3d9b5fc7b6..116ea4821cb 100644 --- a/source/blender/makesdna/DNA_windowmanager_types.h +++ b/source/blender/makesdna/DNA_windowmanager_types.h @@ -407,13 +407,13 @@ enum { KMI_USER_MODIFIED = (1 << 2), KMI_UPDATE = (1 << 3), /** - * When set, ignore events with #wmEvent.is_repeat enabled. + * When set, ignore events with `wmEvent.flag & WM_EVENT_IS_REPEAT` enabled. * * \note this flag isn't cleared when editing/loading the key-map items, * so it may be set in cases which don't make sense (modifier-keys or mouse-motion for example). * * Knowing if an event may repeat is something set at the operating-systems event handling level - * so rely on #wmEvent.is_repeat being false non keyboard events instead of checking if this + * so rely on #WM_EVENT_IS_REPEAT being false non keyboard events instead of checking if this * flag makes sense. * * Only used when: `ISKEYBOARD(kmi->type) || (kmi->type == KM_TEXTINPUT)` diff --git a/source/blender/windowmanager/intern/wm_event_system.cc b/source/blender/windowmanager/intern/wm_event_system.cc index a371fa7e185..757c3aae00c 100644 --- a/source/blender/windowmanager/intern/wm_event_system.cc +++ b/source/blender/windowmanager/intern/wm_event_system.cc @@ -2086,8 +2086,6 @@ static bool wm_eventmatch(const wmEvent *winevent, const wmKeyMapItem *kmi) /* The matching rules. */ if (kmitype == KM_TEXTINPUT) { if (winevent->val == KM_PRESS) { /* Prevent double clicks. */ - /* Not using #ISTEXTINPUT anymore because (at least on Windows) some key codes above 255 - * could have printable ascii keys, See T30479. */ if (ISKEYBOARD(winevent->type) && winevent->utf8_buf[0]) { return true; } @@ -5328,7 +5326,7 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, void break; } - /* Might be not nullptr terminated. */ + /* Might be not null terminated. */ memcpy(event.utf8_buf, kd->utf8_buf, sizeof(event.utf8_buf)); if (kd->is_repeat) { event.flag |= WM_EVENT_IS_REPEAT; diff --git a/source/blender/windowmanager/wm_event_types.h b/source/blender/windowmanager/wm_event_types.h index 2d3624704d0..edac3ada73b 100644 --- a/source/blender/windowmanager/wm_event_types.h +++ b/source/blender/windowmanager/wm_event_types.h @@ -358,12 +358,6 @@ enum { /** Test whether the event is timer event. */ #define ISTIMER(event_type) ((event_type) >= TIMER && (event_type) <= TIMERF) -/* for event checks */ -/* only used for KM_TEXTINPUT, so assume that we want all user-inputtable ascii codes included */ -/* Unused, see #wm_eventmatch, see: T30479. */ -// #define ISTEXTINPUT(event_type) ((event_type) >= ' ' && (event_type) <= 255) -/* NOTE: an alternative could be to check `event->utf8_buf`. */ - /** Test whether the event is a key on the keyboard (including modifier keys). */ #define ISKEYBOARD(event_type) \ (((event_type) >= _EVT_KEYBOARD_MIN && (event_type) <= _EVT_KEYBOARD_MAX) || \ -- cgit v1.2.3 From 411bcf1fe7345f2e513b49249c34d3601631a5d7 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Thu, 14 Jul 2022 16:26:24 +1000 Subject: Cleanup: simplify 3D text insert, remove checks for ascii escape codes 3D text included checks for escape codes such as \n\r\b which have not been included in wmEvent.ascii since [0] (2009). Remove these and use more straightforward logic for overriding the events text input. [0] 66437a62a73966de8ccb673473ba69d6c1ed66a3 --- source/blender/editors/curve/editfont.c | 60 +++++++++++++++------------------ 1 file changed, 27 insertions(+), 33 deletions(-) diff --git a/source/blender/editors/curve/editfont.c b/source/blender/editors/curve/editfont.c index 33e3837c9d4..623f41dcd20 100644 --- a/source/blender/editors/curve/editfont.c +++ b/source/blender/editors/curve/editfont.c @@ -59,7 +59,7 @@ static int kill_selection(Object *obedit, int ins); /** \name Internal Utilities * \{ */ -static char32_t findaccent(char32_t char1, uint code) +static char32_t findaccent(char32_t char1, const char code) { char32_t new = 0; @@ -1638,12 +1638,12 @@ static int insert_text_invoke(bContext *C, wmOperator *op, const wmEvent *event) Object *obedit = CTX_data_edit_object(C); Curve *cu = obedit->data; EditFont *ef = cu->editfont; - static int accentcode = 0; - uintptr_t ascii = WM_event_utf8_to_ascii(event); + static bool accentcode = false; const bool alt = event->modifier & KM_ALT; const bool shift = event->modifier & KM_SHIFT; const bool ctrl = event->modifier & KM_CTRL; int event_type = event->type, event_val = event->val; + char32_t insert_char_override = 0; char32_t inserted_text[2] = {0}; if (RNA_struct_property_is_set(op->ptr, "text")) { @@ -1652,48 +1652,47 @@ static int insert_text_invoke(bContext *C, wmOperator *op, const wmEvent *event) if (RNA_struct_property_is_set(op->ptr, "accent")) { if (ef->len != 0 && ef->pos > 0) { - accentcode = 1; + accentcode = true; } return OPERATOR_FINISHED; } - /* tab should exit editmode, but we allow it to be typed using modifier keys */ - if (event_type == EVT_TABKEY) { - if ((alt || ctrl || shift) == 0) { - return OPERATOR_PASS_THROUGH; - } - - ascii = 9; - } - if (event_type == EVT_BACKSPACEKEY) { if (alt && ef->len != 0 && ef->pos > 0) { - accentcode = 1; + accentcode = true; } return OPERATOR_PASS_THROUGH; } - if (event_val && (ascii || event->utf8_buf[0])) { - /* handle case like TAB (== 9) */ - if ((ascii > 31 && ascii < 254 && ascii != 127) || (ELEM(ascii, 13, 10)) || (ascii == 8) || - (event->utf8_buf[0])) { + /* Tab typically exit edit-mode, but we allow it to be typed using modifier keys. */ + if (event_type == EVT_TABKEY) { + if ((alt || ctrl || shift) == 0) { + return OPERATOR_PASS_THROUGH; + } + insert_char_override = '\t'; + } + if (event_val && (insert_char_override || event->utf8_buf[0])) { + if (insert_char_override) { + /* Handle case like TAB ('\t'). */ + inserted_text[0] = insert_char_override; + insert_into_textbuf(obedit, insert_char_override); + text_update_edited(C, obedit, FO_EDIT); + } + else { + BLI_assert(event->utf8_buf[0]); if (accentcode) { if (ef->pos > 0) { - inserted_text[0] = findaccent(ef->textbuf[ef->pos - 1], ascii); + inserted_text[0] = findaccent(ef->textbuf[ef->pos - 1], + BLI_str_utf8_as_unicode(event->utf8_buf)); ef->textbuf[ef->pos - 1] = inserted_text[0]; } - accentcode = 0; + accentcode = false; } else if (event->utf8_buf[0]) { inserted_text[0] = BLI_str_utf8_as_unicode(event->utf8_buf); - ascii = inserted_text[0]; - insert_into_textbuf(obedit, ascii); - accentcode = 0; - } - else if (ascii) { - insert_into_textbuf(obedit, ascii); - accentcode = 0; + insert_into_textbuf(obedit, inserted_text[0]); + accentcode = false; } else { BLI_assert(0); @@ -1702,11 +1701,6 @@ static int insert_text_invoke(bContext *C, wmOperator *op, const wmEvent *event) kill_selection(obedit, 1); text_update_edited(C, obedit, FO_EDIT); } - else { - inserted_text[0] = ascii; - insert_into_textbuf(obedit, ascii); - text_update_edited(C, obedit, FO_EDIT); - } } else { return OPERATOR_PASS_THROUGH; @@ -1722,7 +1716,7 @@ static int insert_text_invoke(bContext *C, wmOperator *op, const wmEvent *event) /* reset property? */ if (event_val == 0) { - accentcode = 0; + accentcode = false; } return OPERATOR_FINISHED; -- cgit v1.2.3 From 3935bf255e3313534bc16090aac8118939a1b333 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Thu, 14 Jul 2022 09:57:08 +0200 Subject: Fix T99677: crash when convex hull node is used on empty mesh Fundamental issue is that the attribute api returns none, because the custom data api returns null for a layer when the size of 0. This should be improved separately. --- source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc b/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc index 489b618b8be..3394a7cad62 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc @@ -183,8 +183,10 @@ static Mesh *compute_hull(const GeometrySet &geometry_set) const MeshComponent *component = geometry_set.get_component_for_read(); const VArray varray = component->attributes()->lookup("position", ATTR_DOMAIN_POINT); - varray.materialize(positions.as_mutable_span().slice(offset, varray.size())); - offset += varray.size(); + if (varray) { + varray.materialize(positions.as_mutable_span().slice(offset, varray.size())); + offset += varray.size(); + } } if (geometry_set.has_pointcloud()) { @@ -192,8 +194,10 @@ static Mesh *compute_hull(const GeometrySet &geometry_set) geometry_set.get_component_for_read(); const VArray varray = component->attributes()->lookup("position", ATTR_DOMAIN_POINT); - varray.materialize(positions.as_mutable_span().slice(offset, varray.size())); - offset += varray.size(); + if (varray) { + varray.materialize(positions.as_mutable_span().slice(offset, varray.size())); + offset += varray.size(); + } } if (geometry_set.has_curves()) { -- cgit v1.2.3 From 1571ee66b51d5a8cd700228d01577608e62c0be9 Mon Sep 17 00:00:00 2001 From: Damien Picard Date: Thu, 14 Jul 2022 09:40:55 +0200 Subject: I18N: Allow translating newly added GP data names, and a missing Surface one. --- source/blender/editors/curve/editcurve_add.c | 2 ++ source/blender/editors/object/object_add.cc | 8 ++++---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/source/blender/editors/curve/editcurve_add.c b/source/blender/editors/curve/editcurve_add.c index ba5a7409ba7..ee6376ca95f 100644 --- a/source/blender/editors/curve/editcurve_add.c +++ b/source/blender/editors/curve/editcurve_add.c @@ -87,6 +87,8 @@ static const char *get_surf_defname(int type) return CTX_DATA_(BLT_I18NCONTEXT_ID_CURVE_LEGACY, "SurfCircle"); case CU_PRIM_PATCH: return CTX_DATA_(BLT_I18NCONTEXT_ID_CURVE_LEGACY, "SurfPatch"); + case CU_PRIM_TUBE: + return CTX_DATA_(BLT_I18NCONTEXT_ID_CURVE_LEGACY, "SurfCylinder"); case CU_PRIM_SPHERE: return CTX_DATA_(BLT_I18NCONTEXT_ID_CURVE_LEGACY, "SurfSphere"); case CU_PRIM_DONUT: diff --git a/source/blender/editors/object/object_add.cc b/source/blender/editors/object/object_add.cc index 671a32ce438..35d23edfbf0 100644 --- a/source/blender/editors/object/object_add.cc +++ b/source/blender/editors/object/object_add.cc @@ -1335,21 +1335,21 @@ static int object_gpencil_add_exec(bContext *C, wmOperator *op) const char *ob_name = nullptr; switch (type) { case GP_EMPTY: { - ob_name = "GPencil"; + ob_name = CTX_DATA_(BLT_I18NCONTEXT_ID_GPENCIL, "GPencil"); break; } case GP_MONKEY: { - ob_name = "Suzanne"; + ob_name = CTX_DATA_(BLT_I18NCONTEXT_ID_GPENCIL, "Suzanne"); break; } case GP_STROKE: { - ob_name = "Stroke"; + ob_name = CTX_DATA_(BLT_I18NCONTEXT_ID_GPENCIL, "Stroke"); break; } case GP_LRT_OBJECT: case GP_LRT_SCENE: case GP_LRT_COLLECTION: { - ob_name = "LineArt"; + ob_name = CTX_DATA_(BLT_I18NCONTEXT_ID_GPENCIL, "LineArt"); break; } default: { -- cgit v1.2.3 From f48fadc953f68ba24084bb277ff2ca05ddaa289d Mon Sep 17 00:00:00 2001 From: Damien Picard Date: Thu, 14 Jul 2022 09:42:48 +0200 Subject: UI: translate quick favorites menu operator names Some operator titles were not translated in the quick favorites menu. Before: {F13283724} After: {F13283725} Reviewed By: mont29 Differential Revision: https://developer.blender.org/D15416 --- source/blender/editors/screen/screen_user_menu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/editors/screen/screen_user_menu.c b/source/blender/editors/screen/screen_user_menu.c index 1156452310c..67ca6a83b2b 100644 --- a/source/blender/editors/screen/screen_user_menu.c +++ b/source/blender/editors/screen/screen_user_menu.c @@ -214,7 +214,7 @@ static void screen_user_menu_draw(const bContext *C, Menu *menu) wmOperatorType *ot = WM_operatortype_find(umi_op->op_idname, false); if (ot != NULL) { IDProperty *prop = umi_op->prop ? IDP_CopyProperty(umi_op->prop) : NULL; - uiItemFullO_ptr(menu->layout, ot, ui_name, ICON_NONE, prop, umi_op->opcontext, 0, NULL); + uiItemFullO_ptr(menu->layout, ot, CTX_IFACE_(ot->translation_context, ui_name), ICON_NONE, prop, umi_op->opcontext, 0, NULL); is_empty = false; } else { -- cgit v1.2.3 From 77df9d788a2bb9d7173f10edf3631dc26ccce8ed Mon Sep 17 00:00:00 2001 From: Iliay Katueshenock Date: Thu, 14 Jul 2022 10:04:35 +0200 Subject: Fix T99239: weird behavior in Field on Domain node --- .../blender/nodes/geometry/nodes/node_geo_field_on_domain.cc | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/source/blender/nodes/geometry/nodes/node_geo_field_on_domain.cc b/source/blender/nodes/geometry/nodes/node_geo_field_on_domain.cc index 5939ed5334d..59e243db4a2 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_field_on_domain.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_field_on_domain.cc @@ -85,12 +85,13 @@ class FieldOnDomain final : public GeometryFieldInput { IndexMask /* mask */) const final { const GeometryComponentFieldContext context{component, src_domain_}; - FieldEvaluator value_evaluator{context, component.attribute_domain_size(src_domain_)}; - value_evaluator.add(src_field_); + const int64_t src_domain_size = component.attribute_domain_size(src_domain_); + GArray values(src_field_.cpp_type(), src_domain_size); + FieldEvaluator value_evaluator{context, src_domain_size}; + value_evaluator.add_with_destination(src_field_, values.as_mutable_span()); value_evaluator.evaluate(); - const GVArray &values = value_evaluator.get_evaluated(0); - - return component.attributes()->adapt_domain(values, src_domain_, domain); + return component.attributes()->adapt_domain( + GVArray::ForGArray(std::move(values)), src_domain_, domain); } }; -- cgit v1.2.3 From 8e3879ab5276cf24ebd59a7bf0df69232fd5e4e6 Mon Sep 17 00:00:00 2001 From: Colin Basnett Date: Thu, 14 Jul 2022 10:22:30 +0200 Subject: Cleanup: Rename & refactor several F-curve functions Rename and refactor several F-curve key manipulation functions, and move them from `editors` to `blenkernel`. The functions formerly known as `delete_fcurve_key`, `delete_fcurve_keys`, and `clear_fcurve_keys` have been moved from `ED_keyframes_edit.h` to `BKE_fcurve.h` and have been renamed according to hierarchical naming rules. Below is a table of the naming changes. | From | To | | -- | -- | | `delete_fcurve_key(fcu, index, do_recalc)` | `BKE_fcurve_delete_key(fcu, index)` | | `delete_fcurve_keys(fcu)` | `BKE_fcurve_delete_keys_selected(fcu)` | | `clear_fcurve_keys(fcu)` | `BKE_fcurve_delete_keys_all(fcu)` | | `calchandles_fcurve()` | `BKE_fcurve_handles_recalc()` | | `calchandles_fcurve_ex()`| `BKE_fcurve_handles_recalc_ex()` | The function formerly known as `delete_fcurve_key` no longer takes a `do_fast` parameter, which determined whether or not to call `calchandles_fcurve`. Now, the responsibility is on the caller to run the new `BKE_fcurve_handles_recalc` function if they have want to recalculate the handles. In addition, there is now a new static private function called `fcurve_bezt_free` which sets the key count to zero and frees the key array. This function is now used in couple of instances of functionally equivalent code. Note that `BKE_fcurve_delete_keys_all` is just a wrapper around `fcurve_bezt_free`. This change was initially spurred by the fact that `delete_fcurve_keys` was improperly named; this was a good opportunity to fix the location and naming of a few of these functions. Reviewed By: sybren Differential Revision: https://developer.blender.org/D15282 --- source/blender/blenkernel/BKE_fcurve.h | 23 ++++-- source/blender/blenkernel/intern/action_mirror.c | 2 +- source/blender/blenkernel/intern/fcurve.c | 79 ++++++++++++++++++-- source/blender/blenkernel/intern/fmodifier.c | 4 +- source/blender/editors/animation/anim_deps.c | 2 +- source/blender/editors/animation/drivers.c | 2 +- source/blender/editors/animation/fmodifier_ui.c | 2 +- source/blender/editors/animation/keyframes_edit.c | 2 +- .../blender/editors/animation/keyframes_general.c | 83 ++-------------------- source/blender/editors/animation/keyframing.c | 14 ++-- source/blender/editors/armature/pose_lib.c | 4 +- source/blender/editors/gpencil/gpencil_convert.c | 2 +- source/blender/editors/include/ED_keyframes_edit.h | 3 - source/blender/editors/space_action/action_edit.c | 12 ++-- source/blender/editors/space_graph/graph_buttons.c | 2 +- source/blender/editors/space_graph/graph_edit.c | 16 ++--- source/blender/editors/space_nla/nla_edit.c | 2 +- .../blender/editors/transform/transform_convert.c | 4 +- .../editors/transform/transform_convert_graph.c | 2 +- source/blender/io/collada/AnimationImporter.cpp | 4 +- source/blender/io/collada/BCAnimationCurve.cpp | 2 +- source/blender/makesrna/intern/rna_fcurve.c | 10 ++- source/blender/python/intern/bpy_rna_anim.c | 3 +- 23 files changed, 150 insertions(+), 129 deletions(-) diff --git a/source/blender/blenkernel/BKE_fcurve.h b/source/blender/blenkernel/BKE_fcurve.h index 10d9ce3364d..3ccbd2ac1da 100644 --- a/source/blender/blenkernel/BKE_fcurve.h +++ b/source/blender/blenkernel/BKE_fcurve.h @@ -463,23 +463,38 @@ bool BKE_fcurve_bezt_subdivide_handles(struct BezTriple *bezt, struct BezTriple *next, float *r_pdelta); +/** + * Delete a keyframe from an F-curve at a specific index. + */ +void BKE_fcurve_delete_key(struct FCurve *fcu, int index); + +/** + * Delete selected keyframes from an F-curve. + */ +bool BKE_fcurve_delete_keys_selected(struct FCurve *fcu); + +/** + * Delete all keyframes from an F-curve. + */ +void BKE_fcurve_delete_keys_all(struct FCurve *fcu); + /* -------- Curve Sanity -------- */ /** * This function recalculates the handles of an F-Curve. Acts based on selection with `SELECT` - * flag. To use a different flag, use #calchandles_fcurve_ex(). + * flag. To use a different flag, use #BKE_fcurve_handles_recalc_ex(). * * If the BezTriples have been rearranged, sort them first before using this. */ -void calchandles_fcurve(struct FCurve *fcu); +void BKE_fcurve_handles_recalc(struct FCurve *fcu); /** - * Variant of #calchandles_fcurve() that allows calculating based on a different select flag. + * Variant of #BKE_fcurve_handles_recalc() that allows calculating based on a different select flag. * * \param handle_sel_flag: The flag (bezt.f1/2/3) value to use to determine selection. * Usually `SELECT`, but may want to use a different one at times * (if caller does not operate on selection). */ -void calchandles_fcurve_ex(struct FCurve *fcu, eBezTriple_Flag handle_sel_flag); +void BKE_fcurve_handles_recalc_ex(struct FCurve *fcu, eBezTriple_Flag handle_sel_flag); /** * Update handles, making sure the handle-types are valid (e.g. correctly deduced from an "Auto" * type), and recalculating their position vectors. diff --git a/source/blender/blenkernel/intern/action_mirror.c b/source/blender/blenkernel/intern/action_mirror.c index 7ef561c8216..0663538f3bb 100644 --- a/source/blender/blenkernel/intern/action_mirror.c +++ b/source/blender/blenkernel/intern/action_mirror.c @@ -363,7 +363,7 @@ static void action_flip_pchan(Object *ob_arm, /* Recalculate handles. */ for (int i = 0; i < fcurve_array_len; i++) { - calchandles_fcurve_ex(fcurve_array[i], 0); + BKE_fcurve_handles_recalc_ex(fcurve_array[i], 0); } MEM_freeN((void *)keyed_frames); diff --git a/source/blender/blenkernel/intern/fcurve.c b/source/blender/blenkernel/intern/fcurve.c index 0203620df84..972ff377519 100644 --- a/source/blender/blenkernel/intern/fcurve.c +++ b/source/blender/blenkernel/intern/fcurve.c @@ -1146,7 +1146,7 @@ void fcurve_samples_to_keyframes(FCurve *fcu, const int start, const int end) MEM_SAFE_FREE(fcu->fpt); /* Not strictly needed since we use linear interpolation, but better be consistent here. */ - calchandles_fcurve(fcu); + BKE_fcurve_handles_recalc(fcu); } /* ***************************** F-Curve Sanity ********************************* */ @@ -1216,7 +1216,7 @@ static BezTriple *cycle_offset_triple( return out; } -void calchandles_fcurve_ex(FCurve *fcu, eBezTriple_Flag handle_sel_flag) +void BKE_fcurve_handles_recalc_ex(FCurve *fcu, eBezTriple_Flag handle_sel_flag) { BezTriple *bezt, *prev, *next; int a = fcu->totvert; @@ -1299,9 +1299,9 @@ void calchandles_fcurve_ex(FCurve *fcu, eBezTriple_Flag handle_sel_flag) } } -void calchandles_fcurve(FCurve *fcu) +void BKE_fcurve_handles_recalc(FCurve *fcu) { - calchandles_fcurve_ex(fcu, SELECT); + BKE_fcurve_handles_recalc_ex(fcu, SELECT); } void testhandles_fcurve(FCurve *fcu, eBezTriple_Flag sel_flag, const bool use_handle) @@ -1320,7 +1320,7 @@ void testhandles_fcurve(FCurve *fcu, eBezTriple_Flag sel_flag, const bool use_ha } /* Recalculate handles. */ - calchandles_fcurve_ex(fcu, sel_flag); + BKE_fcurve_handles_recalc_ex(fcu, sel_flag); } void sort_time_fcurve(FCurve *fcu) @@ -1590,6 +1590,12 @@ static void berekeny(float f1, float f2, float f3, float f4, float *o, int b) } } +static void fcurve_bezt_free(FCurve *fcu) +{ + MEM_SAFE_FREE(fcu->bezt); + fcu->totvert = 0; +} + bool BKE_fcurve_bezt_subdivide_handles(struct BezTriple *bezt, struct BezTriple *prev, struct BezTriple *next, @@ -1651,6 +1657,69 @@ bool BKE_fcurve_bezt_subdivide_handles(struct BezTriple *bezt, return true; } +void BKE_fcurve_delete_key(FCurve *fcu, int index) +{ + /* sanity check */ + if (fcu == NULL) { + return; + } + + /* verify the index: + * 1) cannot be greater than the number of available keyframes + * 2) negative indices are for specifying a value from the end of the array + */ + if (abs(index) >= fcu->totvert) { + return; + } + if (index < 0) { + index += fcu->totvert; + } + + /* Delete this keyframe */ + memmove( + &fcu->bezt[index], &fcu->bezt[index + 1], sizeof(BezTriple) * (fcu->totvert - index - 1)); + fcu->totvert--; + + /* Free the array of BezTriples if there are not keyframes */ + if (fcu->totvert == 0) { + fcurve_bezt_free(fcu); + } +} + +bool BKE_fcurve_delete_keys_selected(FCurve *fcu) +{ + bool changed = false; + + if (fcu->bezt == NULL) { /* ignore baked curves */ + return false; + } + + /* Delete selected BezTriples */ + for (int i = 0; i < fcu->totvert; i++) { + if (fcu->bezt[i].f2 & SELECT) { + if (i == fcu->active_keyframe_index) { + BKE_fcurve_active_keyframe_set(fcu, NULL); + } + memmove(&fcu->bezt[i], &fcu->bezt[i + 1], sizeof(BezTriple) * (fcu->totvert - i - 1)); + fcu->totvert--; + i--; + changed = true; + } + } + + /* Free the array of BezTriples if there are not keyframes */ + if (fcu->totvert == 0) { + fcurve_bezt_free(fcu); + } + + return changed; +} + +void BKE_fcurve_delete_keys_all(FCurve *fcu) +{ + fcurve_bezt_free(fcu); +} + /** \} */ /* -------------------------------------------------------------------- */ diff --git a/source/blender/blenkernel/intern/fmodifier.c b/source/blender/blenkernel/intern/fmodifier.c index 1bb7c49d616..e4c7572b9e4 100644 --- a/source/blender/blenkernel/intern/fmodifier.c +++ b/source/blender/blenkernel/intern/fmodifier.c @@ -1127,7 +1127,7 @@ FModifier *add_fmodifier(ListBase *modifiers, int type, FCurve *owner_fcu) /* update the fcurve if the Cycles modifier is added */ if ((owner_fcu) && (type == FMODIFIER_TYPE_CYCLES)) { - calchandles_fcurve(owner_fcu); + BKE_fcurve_handles_recalc(owner_fcu); } /* return modifier for further editing */ @@ -1215,7 +1215,7 @@ bool remove_fmodifier(ListBase *modifiers, FModifier *fcm) /* update the fcurve if the Cycles modifier is removed */ if (update_fcu) { - calchandles_fcurve(update_fcu); + BKE_fcurve_handles_recalc(update_fcu); } return true; diff --git a/source/blender/editors/animation/anim_deps.c b/source/blender/editors/animation/anim_deps.c index d80eac2422e..ff53ad42e84 100644 --- a/source/blender/editors/animation/anim_deps.c +++ b/source/blender/editors/animation/anim_deps.c @@ -356,7 +356,7 @@ void ANIM_animdata_update(bAnimContext *ac, ListBase *anim_data) if (ale->update & ANIM_UPDATE_HANDLES) { ale->update &= ~ANIM_UPDATE_HANDLES; if (fcu) { - calchandles_fcurve(fcu); + BKE_fcurve_handles_recalc(fcu); } } diff --git a/source/blender/editors/animation/drivers.c b/source/blender/editors/animation/drivers.c index c6f68228807..effedd4307d 100644 --- a/source/blender/editors/animation/drivers.c +++ b/source/blender/editors/animation/drivers.c @@ -124,7 +124,7 @@ struct FCurve *alloc_driver_fcurve(const char rna_path[], insert_vert_fcurve( fcu, 1.0f, 1.0f, BEZT_KEYTYPE_KEYFRAME, INSERTKEY_FAST | INSERTKEY_NO_USERPREF); fcu->extend = FCURVE_EXTRAPOLATE_LINEAR; - calchandles_fcurve(fcu); + BKE_fcurve_handles_recalc(fcu); } } diff --git a/source/blender/editors/animation/fmodifier_ui.c b/source/blender/editors/animation/fmodifier_ui.c index 6f31472907b..d2f0ee622c4 100644 --- a/source/blender/editors/animation/fmodifier_ui.c +++ b/source/blender/editors/animation/fmodifier_ui.c @@ -1020,7 +1020,7 @@ bool ANIM_fmodifiers_paste_from_buf(ListBase *modifiers, bool replace, FCurve *c /* adding or removing the Cycles modifier requires an update to handles */ if (curve && BKE_fcurve_is_cyclic(curve) != was_cyclic) { - calchandles_fcurve(curve); + BKE_fcurve_handles_recalc(curve); } /* did we succeed? */ diff --git a/source/blender/editors/animation/keyframes_edit.c b/source/blender/editors/animation/keyframes_edit.c index 88207f7d514..706db498a82 100644 --- a/source/blender/editors/animation/keyframes_edit.c +++ b/source/blender/editors/animation/keyframes_edit.c @@ -444,7 +444,7 @@ void ANIM_animdata_keyframe_callback(bAnimContext *ac, ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); for (ale = anim_data.first; ale; ale = ale->next) { - ANIM_fcurve_keyframes_loop(NULL, ale->key_data, NULL, callback_fn, calchandles_fcurve); + ANIM_fcurve_keyframes_loop(NULL, ale->key_data, NULL, callback_fn, BKE_fcurve_handles_recalc); ale->update |= ANIM_UPDATE_DEFAULT; } diff --git a/source/blender/editors/animation/keyframes_general.c b/source/blender/editors/animation/keyframes_general.c index dd88752af14..7723c221a40 100644 --- a/source/blender/editors/animation/keyframes_general.c +++ b/source/blender/editors/animation/keyframes_general.c @@ -47,77 +47,6 @@ /* **************************************************** */ -void delete_fcurve_key(FCurve *fcu, int index, bool do_recalc) -{ - /* sanity check */ - if (fcu == NULL) { - return; - } - - /* verify the index: - * 1) cannot be greater than the number of available keyframes - * 2) negative indices are for specifying a value from the end of the array - */ - if (abs(index) >= fcu->totvert) { - return; - } - if (index < 0) { - index += fcu->totvert; - } - - /* Delete this keyframe */ - memmove( - &fcu->bezt[index], &fcu->bezt[index + 1], sizeof(BezTriple) * (fcu->totvert - index - 1)); - fcu->totvert--; - - if (fcu->totvert == 0) { - MEM_SAFE_FREE(fcu->bezt); - } - - /* recalc handles - only if it won't cause problems */ - if (do_recalc) { - calchandles_fcurve(fcu); - } -} - -bool delete_fcurve_keys(FCurve *fcu) -{ - bool changed = false; - - if (fcu->bezt == NULL) { /* ignore baked curves */ - return false; - } - - /* Delete selected BezTriples */ - for (int i = 0; i < fcu->totvert; i++) { - if (fcu->bezt[i].f2 & SELECT) { - if (i == fcu->active_keyframe_index) { - BKE_fcurve_active_keyframe_set(fcu, NULL); - } - memmove(&fcu->bezt[i], &fcu->bezt[i + 1], sizeof(BezTriple) * (fcu->totvert - i - 1)); - fcu->totvert--; - i--; - changed = true; - } - } - - /* Free the array of BezTriples if there are not keyframes */ - if (fcu->totvert == 0) { - clear_fcurve_keys(fcu); - } - - return changed; -} - -void clear_fcurve_keys(FCurve *fcu) -{ - MEM_SAFE_FREE(fcu->bezt); - - fcu->totvert = 0; -} - -/* ---------------- */ - bool duplicate_fcurve_keys(FCurve *fcu) { bool changed = false; @@ -282,7 +211,7 @@ void clean_fcurve(struct bAnimContext *ac, bAnimListElem *ale, float thresh, boo } if (fcu->bezt->vec[1][1] == default_value) { - clear_fcurve_keys(fcu); + BKE_fcurve_delete_keys_all(fcu); /* check if curve is really unused and if it is, return signal for deletion */ if (BKE_fcurve_is_empty(fcu)) { @@ -679,7 +608,7 @@ void smooth_fcurve(FCurve *fcu) } /* recalculate handles */ - calchandles_fcurve(fcu); + BKE_fcurve_handles_recalc(fcu); } /* ---------------- */ @@ -762,7 +691,7 @@ void sample_fcurve(FCurve *fcu) } /* recalculate channel's handles? */ - calchandles_fcurve(fcu); + BKE_fcurve_handles_recalc(fcu); } /* **************************************************** */ @@ -1121,7 +1050,7 @@ static void paste_animedit_keys_fcurve( case KEYFRAME_PASTE_MERGE_OVER: /* remove all keys */ - clear_fcurve_keys(fcu); + BKE_fcurve_delete_keys_all(fcu); break; case KEYFRAME_PASTE_MERGE_OVER_RANGE: @@ -1148,7 +1077,7 @@ static void paste_animedit_keys_fcurve( } /* remove frames in the range */ - delete_fcurve_keys(fcu); + BKE_fcurve_delete_keys_selected(fcu); } break; } @@ -1182,7 +1111,7 @@ static void paste_animedit_keys_fcurve( } /* recalculate F-Curve's handles? */ - calchandles_fcurve(fcu); + BKE_fcurve_handles_recalc(fcu); } const EnumPropertyItem rna_enum_keyframe_paste_offset_items[] = { diff --git a/source/blender/editors/animation/keyframing.c b/source/blender/editors/animation/keyframing.c index 2fa8907de71..9084b9bb214 100644 --- a/source/blender/editors/animation/keyframing.c +++ b/source/blender/editors/animation/keyframing.c @@ -639,7 +639,7 @@ int insert_vert_fcurve( * - we may calculate twice (due to auto-handle needing to be calculated twice) */ if ((flag & INSERTKEY_FAST) == 0) { - calchandles_fcurve(fcu); + BKE_fcurve_handles_recalc(fcu); } /* return the index at which the keyframe was added */ @@ -1282,10 +1282,12 @@ static bool insert_keyframe_value(ReportList *reports, /* delete keyframe immediately before/after newly added */ switch (insert_mode) { case KEYNEEDED_DELPREV: - delete_fcurve_key(fcu, fcu->totvert - 2, 1); + BKE_fcurve_delete_key(fcu, fcu->totvert - 2); + BKE_fcurve_handles_recalc(fcu); break; case KEYNEEDED_DELNEXT: - delete_fcurve_key(fcu, 1, 1); + BKE_fcurve_delete_key(fcu, 1); + BKE_fcurve_handles_recalc(fcu); break; } @@ -1683,7 +1685,8 @@ static bool delete_keyframe_fcurve(AnimData *adt, FCurve *fcu, float cfra) i = BKE_fcurve_bezt_binarysearch_index(fcu->bezt, cfra, fcu->totvert, &found); if (found) { /* delete the key at the index (will sanity check + do recalc afterwards) */ - delete_fcurve_key(fcu, i, 1); + BKE_fcurve_delete_key(fcu, i); + BKE_fcurve_handles_recalc(fcu); /* Only delete curve too if it won't be doing anything anymore */ if (BKE_fcurve_is_empty(fcu)) { @@ -2709,7 +2712,8 @@ static int delete_key_button_exec(bContext *C, wmOperator *op) i = BKE_fcurve_bezt_binarysearch_index(fcu->bezt, cfra, fcu->totvert, &found); if (found) { /* delete the key at the index (will sanity check + do recalc afterwards) */ - delete_fcurve_key(fcu, i, 1); + BKE_fcurve_delete_key(fcu, i); + BKE_fcurve_handles_recalc(fcu); changed = true; } } diff --git a/source/blender/editors/armature/pose_lib.c b/source/blender/editors/armature/pose_lib.c index a6742fbe2bf..ff187a52154 100644 --- a/source/blender/editors/armature/pose_lib.c +++ b/source/blender/editors/armature/pose_lib.c @@ -24,6 +24,7 @@ #include "BKE_action.h" #include "BKE_animsys.h" #include "BKE_armature.h" +#include "BKE_fcurve.h" #include "BKE_idprop.h" #include "BKE_lib_id.h" #include "BKE_main.h" @@ -615,7 +616,8 @@ static int poselib_remove_exec(bContext *C, wmOperator *op) for (i = 0, bezt = fcu->bezt; i < fcu->totvert; i++, bezt++) { /* check if remove */ if (IS_EQF(bezt->vec[1][0], (float)marker->frame)) { - delete_fcurve_key(fcu, i, 1); + BKE_fcurve_delete_key(fcu, i); + BKE_fcurve_handles_recalc(fcu); break; } } diff --git a/source/blender/editors/gpencil/gpencil_convert.c b/source/blender/editors/gpencil/gpencil_convert.c index 25b5466e260..e02a82f4555 100644 --- a/source/blender/editors/gpencil/gpencil_convert.c +++ b/source/blender/editors/gpencil/gpencil_convert.c @@ -588,7 +588,7 @@ static void gpencil_stroke_path_animation(bContext *C, } /* As we used INSERTKEY_FAST mode, we need to recompute all curve's handles now */ - calchandles_fcurve(fcu); + BKE_fcurve_handles_recalc(fcu); WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL); diff --git a/source/blender/editors/include/ED_keyframes_edit.h b/source/blender/editors/include/ED_keyframes_edit.h index 9596c3a7fed..1d63e01c84b 100644 --- a/source/blender/editors/include/ED_keyframes_edit.h +++ b/source/blender/editors/include/ED_keyframes_edit.h @@ -388,9 +388,6 @@ bool keyframe_region_circle_test(const KeyframeEdit_CircleData *data_circle, con /* ************************************************ */ /* Destructive Editing API (keyframes_general.c) */ -void delete_fcurve_key(struct FCurve *fcu, int index, bool do_recalc); -bool delete_fcurve_keys(struct FCurve *fcu); -void clear_fcurve_keys(struct FCurve *fcu); bool duplicate_fcurve_keys(struct FCurve *fcu); float get_default_rna_value(struct FCurve *fcu, struct PropertyRNA *prop, struct PointerRNA *ptr); diff --git a/source/blender/editors/space_action/action_edit.c b/source/blender/editors/space_action/action_edit.c index e97b666810c..23c92cbdaa0 100644 --- a/source/blender/editors/space_action/action_edit.c +++ b/source/blender/editors/space_action/action_edit.c @@ -1006,7 +1006,7 @@ static bool delete_action_keys(bAnimContext *ac) AnimData *adt = ale->adt; /* delete selected keyframes only */ - changed = delete_fcurve_keys(fcu); + changed = BKE_fcurve_delete_keys_selected(fcu); /* Only delete curve too if it won't be doing anything anymore */ if (BKE_fcurve_is_empty(fcu)) { @@ -1473,7 +1473,7 @@ static void sethandles_action_keys(bAnimContext *ac, short mode) /* any selected keyframes for editing? */ if (ANIM_fcurve_keyframes_loop(NULL, fcu, NULL, sel_cb, NULL)) { /* change type of selected handles */ - ANIM_fcurve_keyframes_loop(NULL, fcu, NULL, edit_cb, calchandles_fcurve); + ANIM_fcurve_keyframes_loop(NULL, fcu, NULL, edit_cb, BKE_fcurve_handles_recalc); ale->update |= ANIM_UPDATE_DEFAULT; } @@ -1790,11 +1790,11 @@ static void snap_action_keys(bAnimContext *ac, short mode) } else if (adt) { ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 0, 0); - ANIM_fcurve_keyframes_loop(&ked, ale->key_data, NULL, edit_cb, calchandles_fcurve); + ANIM_fcurve_keyframes_loop(&ked, ale->key_data, NULL, edit_cb, BKE_fcurve_handles_recalc); ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 1, 0); } else { - ANIM_fcurve_keyframes_loop(&ked, ale->key_data, NULL, edit_cb, calchandles_fcurve); + ANIM_fcurve_keyframes_loop(&ked, ale->key_data, NULL, edit_cb, BKE_fcurve_handles_recalc); } ale->update |= ANIM_UPDATE_DEFAULT; @@ -1914,11 +1914,11 @@ static void mirror_action_keys(bAnimContext *ac, short mode) } else if (adt) { ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 0, 0); - ANIM_fcurve_keyframes_loop(&ked, ale->key_data, NULL, edit_cb, calchandles_fcurve); + ANIM_fcurve_keyframes_loop(&ked, ale->key_data, NULL, edit_cb, BKE_fcurve_handles_recalc); ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 1, 0); } else { - ANIM_fcurve_keyframes_loop(&ked, ale->key_data, NULL, edit_cb, calchandles_fcurve); + ANIM_fcurve_keyframes_loop(&ked, ale->key_data, NULL, edit_cb, BKE_fcurve_handles_recalc); } ale->update |= ANIM_UPDATE_DEFAULT; diff --git a/source/blender/editors/space_graph/graph_buttons.c b/source/blender/editors/space_graph/graph_buttons.c index bc2705df314..3b8c6cbd1d0 100644 --- a/source/blender/editors/space_graph/graph_buttons.c +++ b/source/blender/editors/space_graph/graph_buttons.c @@ -277,7 +277,7 @@ static void graphedit_activekey_update_cb(bContext *UNUSED(C), /* make sure F-Curve and its handles are still valid after this editing */ sort_time_fcurve(fcu); - calchandles_fcurve(fcu); + BKE_fcurve_handles_recalc(fcu); } /* update callback for active keyframe properties - handle-editing wrapper */ diff --git a/source/blender/editors/space_graph/graph_edit.c b/source/blender/editors/space_graph/graph_edit.c index 7e8bca88744..64a3c603e73 100644 --- a/source/blender/editors/space_graph/graph_edit.c +++ b/source/blender/editors/space_graph/graph_edit.c @@ -723,7 +723,7 @@ static bool delete_graph_keys(bAnimContext *ac) bool changed; /* Delete selected keyframes only. */ - changed = delete_fcurve_keys(fcu); + changed = BKE_fcurve_delete_keys_selected(fcu); if (changed) { ale->update |= ANIM_UPDATE_DEFAULT; @@ -1488,7 +1488,7 @@ static void setipo_graph_keys(bAnimContext *ac, short mode) * Currently that's not necessary here. */ for (ale = anim_data.first; ale; ale = ale->next) { - ANIM_fcurve_keyframes_loop(NULL, ale->key_data, NULL, set_cb, calchandles_fcurve); + ANIM_fcurve_keyframes_loop(NULL, ale->key_data, NULL, set_cb, BKE_fcurve_handles_recalc); ale->update |= ANIM_UPDATE_DEFAULT_NOHANDLES; } @@ -1566,7 +1566,7 @@ static void seteasing_graph_keys(bAnimContext *ac, short mode) * Currently that's not necessary here. */ for (ale = anim_data.first; ale; ale = ale->next) { - ANIM_fcurve_keyframes_loop(NULL, ale->key_data, NULL, set_cb, calchandles_fcurve); + ANIM_fcurve_keyframes_loop(NULL, ale->key_data, NULL, set_cb, BKE_fcurve_handles_recalc); ale->update |= ANIM_UPDATE_DEFAULT_NOHANDLES; } @@ -1649,7 +1649,7 @@ static void sethandles_graph_keys(bAnimContext *ac, short mode) /* Any selected keyframes for editing? */ if (ANIM_fcurve_keyframes_loop(NULL, fcu, NULL, sel_cb, NULL)) { /* Change type of selected handles. */ - ANIM_fcurve_keyframes_loop(NULL, fcu, NULL, edit_cb, calchandles_fcurve); + ANIM_fcurve_keyframes_loop(NULL, fcu, NULL, edit_cb, BKE_fcurve_handles_recalc); ale->update |= ANIM_UPDATE_DEFAULT; } @@ -2295,11 +2295,11 @@ static void snap_graph_keys(bAnimContext *ac, short mode) /* Perform snapping. */ if (adt) { ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 0, 0); - ANIM_fcurve_keyframes_loop(&ked, ale->key_data, NULL, edit_cb, calchandles_fcurve); + ANIM_fcurve_keyframes_loop(&ked, ale->key_data, NULL, edit_cb, BKE_fcurve_handles_recalc); ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 1, 0); } else { - ANIM_fcurve_keyframes_loop(&ked, ale->key_data, NULL, edit_cb, calchandles_fcurve); + ANIM_fcurve_keyframes_loop(&ked, ale->key_data, NULL, edit_cb, BKE_fcurve_handles_recalc); } ale->update |= ANIM_UPDATE_DEFAULT; @@ -2555,11 +2555,11 @@ static void mirror_graph_keys(bAnimContext *ac, short mode) /* Perform actual mirroring. */ if (adt) { ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 0, 0); - ANIM_fcurve_keyframes_loop(&ked, ale->key_data, NULL, edit_cb, calchandles_fcurve); + ANIM_fcurve_keyframes_loop(&ked, ale->key_data, NULL, edit_cb, BKE_fcurve_handles_recalc); ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 1, 0); } else { - ANIM_fcurve_keyframes_loop(&ked, ale->key_data, NULL, edit_cb, calchandles_fcurve); + ANIM_fcurve_keyframes_loop(&ked, ale->key_data, NULL, edit_cb, BKE_fcurve_handles_recalc); } ale->update |= ANIM_UPDATE_DEFAULT; diff --git a/source/blender/editors/space_nla/nla_edit.c b/source/blender/editors/space_nla/nla_edit.c index cc45c86ddcd..42509f17b92 100644 --- a/source/blender/editors/space_nla/nla_edit.c +++ b/source/blender/editors/space_nla/nla_edit.c @@ -2205,7 +2205,7 @@ static int nlaedit_apply_scale_exec(bContext *C, wmOperator *UNUSED(op)) * applying this scaling */ ked.data = strip; ANIM_animchanneldata_keyframes_loop( - &ked, ac.ads, strip->act, ALE_ACT, NULL, bezt_apply_nlamapping, calchandles_fcurve); + &ked, ac.ads, strip->act, ALE_ACT, NULL, bezt_apply_nlamapping, BKE_fcurve_handles_recalc); /* clear scale of strip now that it has been applied, * and recalculate the extents of the action now that it has been scaled diff --git a/source/blender/editors/transform/transform_convert.c b/source/blender/editors/transform/transform_convert.c index d9b971c5478..a23689166c4 100644 --- a/source/blender/editors/transform/transform_convert.c +++ b/source/blender/editors/transform/transform_convert.c @@ -769,7 +769,7 @@ void posttrans_fcurve_clean(FCurve *fcu, const int sel_flag, const bool use_hand } else { /* Delete Keyframe */ - delete_fcurve_key(fcu, i, 0); + BKE_fcurve_delete_key(fcu, i); } /* Update count of how many we've deleted @@ -779,7 +779,7 @@ void posttrans_fcurve_clean(FCurve *fcu, const int sel_flag, const bool use_hand } else { /* Always delete - Unselected keys don't matter */ - delete_fcurve_key(fcu, i, 0); + BKE_fcurve_delete_key(fcu, i); } /* Stop the RK search... we've found our match now */ diff --git a/source/blender/editors/transform/transform_convert_graph.c b/source/blender/editors/transform/transform_convert_graph.c index d93fff72de6..b57f5b78939 100644 --- a/source/blender/editors/transform/transform_convert_graph.c +++ b/source/blender/editors/transform/transform_convert_graph.c @@ -934,7 +934,7 @@ void recalcData_graphedit(TransInfo *t) dosort++; } else { - calchandles_fcurve_ex(fcu, BEZT_FLAG_TEMP_TAG); + BKE_fcurve_handles_recalc_ex(fcu, BEZT_FLAG_TEMP_TAG); } /* set refresh tags for objects using this animation, diff --git a/source/blender/io/collada/AnimationImporter.cpp b/source/blender/io/collada/AnimationImporter.cpp index 923a392dbde..cc91c3eeac9 100644 --- a/source/blender/io/collada/AnimationImporter.cpp +++ b/source/blender/io/collada/AnimationImporter.cpp @@ -64,7 +64,7 @@ void AnimationImporter::add_bezt(FCurve *fcu, bez.f1 = bez.f2 = bez.f3 = SELECT; bez.h1 = bez.h2 = HD_AUTO; insert_bezt_fcurve(fcu, &bez, INSERTKEY_NOFLAGS); - calchandles_fcurve(fcu); + BKE_fcurve_handles_recalc(fcu); } void AnimationImporter::animation_to_fcurves(COLLADAFW::AnimationCurve *curve) @@ -132,7 +132,7 @@ void AnimationImporter::animation_to_fcurves(COLLADAFW::AnimationCurve *curve) insert_bezt_fcurve(fcu, &bez, INSERTKEY_NOFLAGS); } - calchandles_fcurve(fcu); + BKE_fcurve_handles_recalc(fcu); fcurves.push_back(fcu); unused_curves.push_back(fcu); diff --git a/source/blender/io/collada/BCAnimationCurve.cpp b/source/blender/io/collada/BCAnimationCurve.cpp index fbb2ba499a5..04a7a81c0a6 100644 --- a/source/blender/io/collada/BCAnimationCurve.cpp +++ b/source/blender/io/collada/BCAnimationCurve.cpp @@ -96,7 +96,7 @@ void BCAnimationCurve::create_bezt(float frame, float output) bez.f1 = bez.f2 = bez.f3 = SELECT; bez.h1 = bez.h2 = HD_AUTO; insert_bezt_fcurve(fcu, &bez, INSERTKEY_NOFLAGS); - calchandles_fcurve(fcu); + BKE_fcurve_handles_recalc(fcu); } BCAnimationCurve::~BCAnimationCurve() diff --git a/source/blender/makesrna/intern/rna_fcurve.c b/source/blender/makesrna/intern/rna_fcurve.c index ac8aebd2fdd..461536ffb8a 100644 --- a/source/blender/makesrna/intern/rna_fcurve.c +++ b/source/blender/makesrna/intern/rna_fcurve.c @@ -612,7 +612,7 @@ static void rna_tag_animation_update(Main *bmain, ID *id) static void rna_FCurve_update_data_ex(ID *id, FCurve *fcu, Main *bmain) { sort_time_fcurve(fcu); - calchandles_fcurve(fcu); + BKE_fcurve_handles_recalc(fcu); rna_tag_animation_update(bmain, id); } @@ -752,7 +752,7 @@ static void rna_FModifier_update(Main *bmain, Scene *UNUSED(scene), PointerRNA * FModifier *fcm = (FModifier *)ptr->data; if (fcm->curve && fcm->type == FMODIFIER_TYPE_CYCLES) { - calchandles_fcurve(fcm->curve); + BKE_fcurve_handles_recalc(fcm->curve); } rna_tag_animation_update(bmain, id); @@ -1021,9 +1021,13 @@ static void rna_FKeyframe_points_remove( return; } - delete_fcurve_key(fcu, index, !do_fast); + BKE_fcurve_delete_key(fcu, index); RNA_POINTER_INVALIDATE(bezt_ptr); + if (!do_fast) { + BKE_fcurve_handles_recalc(fcu); + } + rna_tag_animation_update(bmain, id); } diff --git a/source/blender/python/intern/bpy_rna_anim.c b/source/blender/python/intern/bpy_rna_anim.c index fb744432d4b..f25e9d0bbbc 100644 --- a/source/blender/python/intern/bpy_rna_anim.c +++ b/source/blender/python/intern/bpy_rna_anim.c @@ -487,7 +487,8 @@ PyObject *pyrna_struct_keyframe_delete(BPy_StructRNA *self, PyObject *args, PyOb i = BKE_fcurve_bezt_binarysearch_index(fcu->bezt, cfra, fcu->totvert, &found); if (found) { /* delete the key at the index (will sanity check + do recalc afterwards) */ - delete_fcurve_key(fcu, i, 1); + BKE_fcurve_delete_key(fcu, i); + BKE_fcurve_handles_recalc(fcu); result = true; } } -- cgit v1.2.3 From 9d73bbd9668aaa9fd407780309f5b63b05655fcd Mon Sep 17 00:00:00 2001 From: Damien Picard Date: Thu, 14 Jul 2022 10:28:52 +0200 Subject: UI: translate tooltips coming from menu descriptions Many menus get their labels exported to the .po file, but then are not actually translated in the UI. Before: {F13283752} After: {F13283750} Reviewed By: mont29 Differential Revision: https://developer.blender.org/D15417 --- source/blender/editors/interface/interface.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/blender/editors/interface/interface.cc b/source/blender/editors/interface/interface.cc index 3f623566807..c0df193de87 100644 --- a/source/blender/editors/interface/interface.cc +++ b/source/blender/editors/interface/interface.cc @@ -6623,7 +6623,7 @@ void UI_but_string_info_get(bContext *C, uiBut *but, ...) MenuType *mt = UI_but_menutype_get(but); if (mt) { if (type == BUT_GET_RNA_LABEL) { - tmp = BLI_strdup(mt->label); + tmp = BLI_strdup(CTX_TIP_(mt->translation_context, mt->label)); } else { /* Not all menus are from Python. */ @@ -6653,7 +6653,7 @@ void UI_but_string_info_get(bContext *C, uiBut *but, ...) PanelType *pt = UI_but_paneltype_get(but); if (pt) { if (type == BUT_GET_RNA_LABEL) { - tmp = BLI_strdup(pt->label); + tmp = BLI_strdup(CTX_TIP_(pt->translation_context, pt->label)); } else { /* Not all panels are from Python. */ -- cgit v1.2.3 From 44e530e1b107fd0d91f472f9a58642ab59fd5422 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Thu, 14 Jul 2022 10:35:46 +0200 Subject: Geometry Nodes: fix face corner to edge attribute interpolation Looks like this was wrong all the time.. Luckily, this conversion is not very common. Found when testing D15274. --- source/blender/blenkernel/intern/geometry_component_mesh.cc | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/source/blender/blenkernel/intern/geometry_component_mesh.cc b/source/blender/blenkernel/intern/geometry_component_mesh.cc index cf6681a69be..83bf2a5d27b 100644 --- a/source/blender/blenkernel/intern/geometry_component_mesh.cc +++ b/source/blender/blenkernel/intern/geometry_component_mesh.cc @@ -298,12 +298,14 @@ static void adapt_mesh_domain_corner_to_edge_impl(const Mesh &mesh, const MPoly &poly = mesh.mpoly[poly_index]; /* For every edge, mix values from the two adjacent corners (the current and next corner). */ - for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) { - const int loop_index_next = (loop_index + 1) % poly.totloop; - const MLoop &loop = mesh.mloop[loop_index]; + for (const int i : IndexRange(poly.totloop)) { + const int next_i = (i + 1) % poly.totloop; + const int loop_i = poly.loopstart + i; + const int next_loop_i = poly.loopstart + next_i; + const MLoop &loop = mesh.mloop[loop_i]; const int edge_index = loop.e; - mixer.mix_in(edge_index, old_values[loop_index]); - mixer.mix_in(edge_index, old_values[loop_index_next]); + mixer.mix_in(edge_index, old_values[loop_i]); + mixer.mix_in(edge_index, old_values[next_loop_i]); } } -- cgit v1.2.3 From bcdce4ffd848d5b47c467bedeb221645ec033f20 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Thu, 14 Jul 2022 10:47:26 +0200 Subject: Geometry Nodes: fix face corner to edge boolean interpolation This is a follow up for rB44e530e1b107fd0d91f472f9a58642ab59fd5422 which did not fix the function that interpolates boolean attributes. --- source/blender/blenkernel/intern/geometry_component_mesh.cc | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/source/blender/blenkernel/intern/geometry_component_mesh.cc b/source/blender/blenkernel/intern/geometry_component_mesh.cc index 83bf2a5d27b..436868ba375 100644 --- a/source/blender/blenkernel/intern/geometry_component_mesh.cc +++ b/source/blender/blenkernel/intern/geometry_component_mesh.cc @@ -327,13 +327,16 @@ void adapt_mesh_domain_corner_to_edge_impl(const Mesh &mesh, for (const int poly_index : IndexRange(mesh.totpoly)) { const MPoly &poly = mesh.mpoly[poly_index]; - for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) { - const int loop_index_next = (loop_index == poly.totloop) ? poly.loopstart : (loop_index + 1); - const MLoop &loop = mesh.mloop[loop_index]; + for (const int i : IndexRange(poly.totloop)) { + const int next_i = (i + 1) % poly.totloop; + const int loop_i = poly.loopstart + i; + const int next_loop_i = poly.loopstart + next_i; + const MLoop &loop = mesh.mloop[loop_i]; const int edge_index = loop.e; + loose_edges[edge_index] = false; - if (!old_values[loop_index] || !old_values[loop_index_next]) { + if (!old_values[loop_i] || !old_values[next_loop_i]) { r_values[edge_index] = false; } } -- cgit v1.2.3 From c8a07ef66311a31cc45901717845139ae0682f2f Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Thu, 14 Jul 2022 11:32:01 +0200 Subject: BLI: fix finding indices from virtual array The sorting of index vectors assumed that all vectors have at least one element. Now this is checked for more explicitely. --- source/blender/blenlib/intern/index_mask.cc | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/source/blender/blenlib/intern/index_mask.cc b/source/blender/blenlib/intern/index_mask.cc index f3590e4a41c..e9af183d60d 100644 --- a/source/blender/blenlib/intern/index_mask.cc +++ b/source/blender/blenlib/intern/index_mask.cc @@ -142,6 +142,7 @@ IndexMask find_indices_based_on_predicate__merge( int64_t result_mask_size = 0; for (Vector> &local_sub_masks : sub_masks) { for (Vector &sub_mask : local_sub_masks) { + BLI_assert(!sub_mask.is_empty()); all_vectors.append(&sub_mask); result_mask_size += sub_mask.size(); } @@ -232,7 +233,9 @@ IndexMask find_indices_from_virtual_array(const IndexMask indices_to_check, } } }); - sub_masks.local().append(std::move(masked_indices)); + if (!masked_indices.is_empty()) { + sub_masks.local().append(std::move(masked_indices)); + } }); return detail::find_indices_based_on_predicate__merge(indices_to_check, sub_masks, r_indices); -- cgit v1.2.3 From 96cc6030375b9173ae6b234a12d3af25bfbc0aa6 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Thu, 14 Jul 2022 11:49:28 +0200 Subject: Geometry Nodes: update curve type counts after realizing instances The type counts have to be updated eagerly. This was missing from the realize-instances code before, leading to bugs further down the line. --- source/blender/geometry/intern/realize_instances.cc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/source/blender/geometry/intern/realize_instances.cc b/source/blender/geometry/intern/realize_instances.cc index 25bf00c5783..66b856ee0c4 100644 --- a/source/blender/geometry/intern/realize_instances.cc +++ b/source/blender/geometry/intern/realize_instances.cc @@ -1295,6 +1295,15 @@ static void execute_realize_curve_tasks(const RealizeInstancesOptions &options, } }); + /* Type counts have to be updated eagerly. */ + dst_curves.runtime->type_counts.fill(0); + for (const RealizeCurveTask &task : tasks) { + for (const int i : IndexRange(CURVE_TYPES_NUM)) { + dst_curves.runtime->type_counts[i] += + task.curve_info->curves->geometry.runtime->type_counts[i]; + } + } + /* Tag modified attributes. */ for (GSpanAttributeWriter &dst_attribute : dst_attribute_writers) { dst_attribute.finish(); -- cgit v1.2.3 From cb62095c1c8afc4e2b5f28904e3ac4cbd47503b2 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Thu, 14 Jul 2022 20:07:06 +1000 Subject: Correct error with IME from d6fef73ef110eb43756b7b87c2cba80abae3b39f --- source/blender/editors/interface/interface_handlers.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c index 2ad2cd15c43..2496136883d 100644 --- a/source/blender/editors/interface/interface_handlers.c +++ b/source/blender/editors/interface/interface_handlers.c @@ -3172,6 +3172,13 @@ static bool ui_textedit_insert_buf(uiBut *but, return changed; } +static bool ui_textedit_insert_ascii(uiBut *but, uiHandleButtonData *data, const char ascii) +{ + BLI_assert(isascii(ascii)); + const char buf[2] = {ascii, '\0'}; + return ui_textedit_insert_buf(but, data, buf, sizeof(buf) - 1); +} + static void ui_textedit_move(uiBut *but, uiHandleButtonData *data, eStrCursorJumpDirection direction, @@ -3934,6 +3941,9 @@ static void ui_do_but_textedit( else if (event->type == WM_IME_COMPOSITE_END) { changed = true; } +#else + /* Prevent the function from being unused. */ + (void)ui_textedit_insert_ascii; #endif if (changed) { -- cgit v1.2.3 From 9024ac31be5c883b772c50d81d001cdd8fdb3388 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20T=C3=B6nne?= Date: Thu, 14 Jul 2022 11:17:59 +0100 Subject: Fix curve drawing crash after changing geometry nodes output. Using geometry nodes with attributes on a curve object and changing the output is crashing. This is because the `render_mutex` in the curve drawing cache is cleared after changes in `curves_batch_cache_init` and set to null. The cache isn't actually needed currently because all draw updates are single-threaded, but the new `drw_attributes_merge` function still tries to access it (this seems to be tolerated on Linux platforms but crashes on Windows). Make sure the render_mutex is always initialized after (re-)initializing the cache. --- source/blender/draw/intern/draw_cache_impl_curves.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/source/blender/draw/intern/draw_cache_impl_curves.cc b/source/blender/draw/intern/draw_cache_impl_curves.cc index d5f188402d6..1d3d6222f8f 100644 --- a/source/blender/draw/intern/draw_cache_impl_curves.cc +++ b/source/blender/draw/intern/draw_cache_impl_curves.cc @@ -75,13 +75,14 @@ static void curves_batch_cache_init(Curves &curves) if (!cache) { cache = MEM_cnew(__func__); - BLI_mutex_init(&cache->render_mutex); curves.batch_cache = cache; } else { memset(cache, 0, sizeof(*cache)); } + BLI_mutex_init(&cache->render_mutex); + cache->is_dirty = false; } -- cgit v1.2.3 From 6cd30d5ff04afd690e396f4aff14b38a6bc9e466 Mon Sep 17 00:00:00 2001 From: Aras Pranckevicius Date: Thu, 14 Jul 2022 13:41:43 +0300 Subject: IDManagement: add more ID naming tests As part of a larger change (https://developer.blender.org/D14162), adding more test coverage for existing functionality separately. New tests: - ids_sorted_by_default - ids_sorted_by_default_with_libraries - name_too_long_handling - create_equivalent_numeric_suffixes - zero_suffix_is_never_assigned - remove_after_dup_get_original_name - name_number_suffix_assignment - renames_with_duplicates - names_are_unique_per_id_type --- source/blender/blenkernel/intern/lib_id_test.cc | 316 +++++++++++++++++++++--- 1 file changed, 275 insertions(+), 41 deletions(-) diff --git a/source/blender/blenkernel/intern/lib_id_test.cc b/source/blender/blenkernel/intern/lib_id_test.cc index d6101d71be5..96718b3a65b 100644 --- a/source/blender/blenkernel/intern/lib_id_test.cc +++ b/source/blender/blenkernel/intern/lib_id_test.cc @@ -18,19 +18,18 @@ namespace blender::bke::tests { struct LibIDMainSortTestContext { - Main *bmain; -}; + Main *bmain = nullptr; -static void test_lib_id_main_sort_init(LibIDMainSortTestContext *ctx) -{ - BKE_idtype_init(); - ctx->bmain = BKE_main_new(); -} - -static void test_lib_id_main_sort_free(LibIDMainSortTestContext *ctx) -{ - BKE_main_free(ctx->bmain); -} + LibIDMainSortTestContext() + { + BKE_idtype_init(); + bmain = BKE_main_new(); + } + ~LibIDMainSortTestContext() + { + BKE_main_free(bmain); + } +}; static void test_lib_id_main_sort_check_order(std::initializer_list list) { @@ -47,8 +46,7 @@ static void test_lib_id_main_sort_check_order(std::initializer_list list) TEST(lib_id_main_sort, local_ids_1) { - LibIDMainSortTestContext ctx = {nullptr}; - test_lib_id_main_sort_init(&ctx); + LibIDMainSortTestContext ctx; EXPECT_TRUE(BLI_listbase_is_empty(&ctx.bmain->libraries)); ID *id_c = static_cast(BKE_id_new(ctx.bmain, ID_OB, "OB_C")); @@ -57,14 +55,22 @@ TEST(lib_id_main_sort, local_ids_1) EXPECT_TRUE(ctx.bmain->objects.first == id_a); EXPECT_TRUE(ctx.bmain->objects.last == id_c); test_lib_id_main_sort_check_order({id_a, id_b, id_c}); +} - test_lib_id_main_sort_free(&ctx); +static void change_lib(Main *bmain, ID *id, Library *lib) +{ + id->lib = lib; +} + +static void change_name(Main *bmain, ID *id, const char *name) +{ + BLI_strncpy(id->name + 2, name, MAX_NAME); + BKE_id_new_name_validate(&bmain->objects, id, nullptr, true); } TEST(lib_id_main_sort, linked_ids_1) { - LibIDMainSortTestContext ctx = {nullptr}; - test_lib_id_main_sort_init(&ctx); + LibIDMainSortTestContext ctx; EXPECT_TRUE(BLI_listbase_is_empty(&ctx.bmain->libraries)); Library *lib_a = static_cast(BKE_id_new(ctx.bmain, ID_LI, "LI_A")); @@ -92,14 +98,11 @@ TEST(lib_id_main_sort, linked_ids_1) EXPECT_TRUE(ctx.bmain->objects.first == id_c); EXPECT_TRUE(ctx.bmain->objects.last == id_b); test_lib_id_main_sort_check_order({id_c, id_a, id_b}); - - test_lib_id_main_sort_free(&ctx); } TEST(lib_id_main_unique_name, local_ids_1) { - LibIDMainSortTestContext ctx = {nullptr}; - test_lib_id_main_sort_init(&ctx); + LibIDMainSortTestContext ctx; EXPECT_TRUE(BLI_listbase_is_empty(&ctx.bmain->libraries)); ID *id_c = static_cast(BKE_id_new(ctx.bmain, ID_OB, "OB_C")); @@ -107,21 +110,18 @@ TEST(lib_id_main_unique_name, local_ids_1) ID *id_b = static_cast(BKE_id_new(ctx.bmain, ID_OB, "OB_B")); test_lib_id_main_sort_check_order({id_a, id_b, id_c}); - BLI_strncpy(id_c->name, id_a->name, sizeof(id_c->name)); - BKE_id_new_name_validate(&ctx.bmain->objects, id_c, nullptr, false); - EXPECT_TRUE(strcmp(id_c->name + 2, "OB_A.001") == 0); - EXPECT_TRUE(strcmp(id_a->name + 2, "OB_A") == 0); + change_name(ctx.bmain, id_c, "OB_A"); + + EXPECT_STREQ(id_c->name + 2, "OB_A.001"); + EXPECT_STREQ(id_a->name + 2, "OB_A"); EXPECT_TRUE(ctx.bmain->objects.first == id_a); EXPECT_TRUE(ctx.bmain->objects.last == id_b); test_lib_id_main_sort_check_order({id_a, id_c, id_b}); - - test_lib_id_main_sort_free(&ctx); } TEST(lib_id_main_unique_name, linked_ids_1) { - LibIDMainSortTestContext ctx = {nullptr}; - test_lib_id_main_sort_init(&ctx); + LibIDMainSortTestContext ctx; EXPECT_TRUE(BLI_listbase_is_empty(&ctx.bmain->libraries)); Library *lib_a = static_cast(BKE_id_new(ctx.bmain, ID_LI, "LI_A")); @@ -130,29 +130,263 @@ TEST(lib_id_main_unique_name, linked_ids_1) ID *id_a = static_cast(BKE_id_new(ctx.bmain, ID_OB, "OB_A")); ID *id_b = static_cast(BKE_id_new(ctx.bmain, ID_OB, "OB_B")); - id_a->lib = lib_a; + change_lib(ctx.bmain, id_a, lib_a); id_sort_by_name(&ctx.bmain->objects, id_a, nullptr); - id_b->lib = lib_a; + change_lib(ctx.bmain, id_b, lib_a); id_sort_by_name(&ctx.bmain->objects, id_b, nullptr); - BLI_strncpy(id_b->name, id_a->name, sizeof(id_b->name)); - BKE_id_new_name_validate(&ctx.bmain->objects, id_b, nullptr, true); - EXPECT_TRUE(strcmp(id_b->name + 2, "OB_A.001") == 0); - EXPECT_TRUE(strcmp(id_a->name + 2, "OB_A") == 0); + + change_name(ctx.bmain, id_b, "OB_A"); + EXPECT_STREQ(id_b->name + 2, "OB_A.001"); + EXPECT_STREQ(id_a->name + 2, "OB_A"); EXPECT_TRUE(ctx.bmain->objects.first == id_c); EXPECT_TRUE(ctx.bmain->objects.last == id_b); test_lib_id_main_sort_check_order({id_c, id_a, id_b}); - id_b->lib = lib_b; + change_lib(ctx.bmain, id_b, lib_b); id_sort_by_name(&ctx.bmain->objects, id_b, nullptr); - BLI_strncpy(id_b->name, id_a->name, sizeof(id_b->name)); - BKE_id_new_name_validate(&ctx.bmain->objects, id_b, nullptr, true); - EXPECT_TRUE(strcmp(id_b->name + 2, "OB_A") == 0); - EXPECT_TRUE(strcmp(id_a->name + 2, "OB_A") == 0); + change_name(ctx.bmain, id_b, "OB_A"); + EXPECT_STREQ(id_b->name + 2, "OB_A"); + EXPECT_STREQ(id_a->name + 2, "OB_A"); EXPECT_TRUE(ctx.bmain->objects.first == id_c); EXPECT_TRUE(ctx.bmain->objects.last == id_b); test_lib_id_main_sort_check_order({id_c, id_a, id_b}); +} + +TEST(lib_id_main_unique_name, ids_sorted_by_default) +{ + LibIDMainSortTestContext ctx; + + ID *id_foo = static_cast(BKE_id_new(ctx.bmain, ID_OB, "Foo")); + ID *id_bar = static_cast(BKE_id_new(ctx.bmain, ID_OB, "Bar")); + ID *id_baz = static_cast(BKE_id_new(ctx.bmain, ID_OB, "Baz")); + ID *id_yes = static_cast(BKE_id_new(ctx.bmain, ID_OB, "Yes")); + test_lib_id_main_sort_check_order({id_bar, id_baz, id_foo, id_yes}); +} + +static ID *add_id_in_library(Main *bmain, const char *name, Library *lib) +{ + ID *id = static_cast(BKE_id_new(bmain, ID_OB, name)); + id->lib = lib; + id_sort_by_name(&bmain->objects, id, nullptr); + return id; +} + +TEST(lib_id_main_unique_name, ids_sorted_by_default_with_libraries) +{ + LibIDMainSortTestContext ctx; + + Library *lib_one = static_cast(BKE_id_new(ctx.bmain, ID_LI, "LibOne")); + Library *lib_two = static_cast(BKE_id_new(ctx.bmain, ID_LI, "LibTwo")); + + ID *id_foo = static_cast(BKE_id_new(ctx.bmain, ID_OB, "Foo")); + ID *id_bar = static_cast(BKE_id_new(ctx.bmain, ID_OB, "Bar")); + + ID *id_l1c = add_id_in_library(ctx.bmain, "C", lib_one); + ID *id_l2b = add_id_in_library(ctx.bmain, "B", lib_two); + ID *id_l1a = add_id_in_library(ctx.bmain, "A", lib_one); + + ID *id_baz = static_cast(BKE_id_new(ctx.bmain, ID_OB, "Baz")); + ID *id_yes = static_cast(BKE_id_new(ctx.bmain, ID_OB, "Yes")); + + test_lib_id_main_sort_check_order({id_bar, id_baz, id_foo, id_yes, id_l1a, id_l1c, id_l2b}); +} + +TEST(lib_id_main_unique_name, name_too_long_handling) +{ + LibIDMainSortTestContext ctx; + const char *name_a = "Long_Name_That_Does_Not_Fit_Into_Max_Name_Limit_And_Should_Get_Truncated"; + const char *name_b = "Another_Long_Name_That_Does_Not_Fit_And_Has_A_Number_Suffix.123456"; + const char *name_c = "Name_That_Has_Too_Long_Number_Suffix.1234567890"; + + ID *id_a = static_cast(BKE_id_new(ctx.bmain, ID_OB, name_a)); + ID *id_b = static_cast(BKE_id_new(ctx.bmain, ID_OB, name_b)); + ID *id_c = static_cast(BKE_id_new(ctx.bmain, ID_OB, name_c)); + + EXPECT_STREQ(id_a->name + 2, "Long_Name_That_Does_Not_Fit_Into_Max_Name_Limit_And_Should_Get_"); + EXPECT_STREQ(id_b->name + 2, "Another_Long_Name_That_Does_Not_Fit_And_Has_A_Number_Suffix.123"); + EXPECT_STREQ(id_c->name + 2, "Name_That_Has_Too_Long_Number_Suffix.1234567890"); /* Unchanged */ +} + +TEST(lib_id_main_unique_name, create_equivalent_numeric_suffixes) +{ + LibIDMainSortTestContext ctx; + + /* Create names where many of their numeric suffixes are + * the same number, yet the names are different and thus + * should be allowed as-is. */ + ID *id_a = static_cast(BKE_id_new(ctx.bmain, ID_OB, "Foo.123")); + ID *id_b = static_cast(BKE_id_new(ctx.bmain, ID_OB, "Foo.000")); + ID *id_c = static_cast(BKE_id_new(ctx.bmain, ID_OB, "Foo.003")); + ID *id_d = static_cast(BKE_id_new(ctx.bmain, ID_OB, "Foo.3")); + ID *id_e = static_cast(BKE_id_new(ctx.bmain, ID_OB, "Foo.0")); + ID *id_f = static_cast(BKE_id_new(ctx.bmain, ID_OB, "Foo.")); + ID *id_g = static_cast(BKE_id_new(ctx.bmain, ID_OB, "Foo.0123")); + ID *id_h = static_cast(BKE_id_new(ctx.bmain, ID_OB, "Foo")); + ID *id_i = static_cast(BKE_id_new(ctx.bmain, ID_OB, "Foo..")); + ID *id_j = static_cast(BKE_id_new(ctx.bmain, ID_OB, "Foo..001")); + ID *id_k = static_cast(BKE_id_new(ctx.bmain, ID_OB, "Foo..000")); + + EXPECT_STREQ(id_a->name + 2, "Foo.123"); + EXPECT_STREQ(id_b->name + 2, "Foo.000"); + EXPECT_STREQ(id_c->name + 2, "Foo.003"); + EXPECT_STREQ(id_d->name + 2, "Foo.3"); + EXPECT_STREQ(id_e->name + 2, "Foo.0"); + EXPECT_STREQ(id_f->name + 2, "Foo."); + EXPECT_STREQ(id_g->name + 2, "Foo.0123"); + EXPECT_STREQ(id_h->name + 2, "Foo"); + EXPECT_STREQ(id_i->name + 2, "Foo.."); + EXPECT_STREQ(id_j->name + 2, "Foo..001"); + EXPECT_STREQ(id_k->name + 2, "Foo..000"); + + /* Now create their exact duplicates again, and check what happens. */ + id_a = static_cast(BKE_id_new(ctx.bmain, ID_OB, "Foo.123")); + id_b = static_cast(BKE_id_new(ctx.bmain, ID_OB, "Foo.000")); + id_c = static_cast(BKE_id_new(ctx.bmain, ID_OB, "Foo.003")); + id_d = static_cast(BKE_id_new(ctx.bmain, ID_OB, "Foo.3")); + id_e = static_cast(BKE_id_new(ctx.bmain, ID_OB, "Foo.0")); + id_f = static_cast(BKE_id_new(ctx.bmain, ID_OB, "Foo.")); + id_g = static_cast(BKE_id_new(ctx.bmain, ID_OB, "Foo.0123")); + id_h = static_cast(BKE_id_new(ctx.bmain, ID_OB, "Foo")); + id_i = static_cast(BKE_id_new(ctx.bmain, ID_OB, "Foo..")); + id_j = static_cast(BKE_id_new(ctx.bmain, ID_OB, "Foo..001")); + id_k = static_cast(BKE_id_new(ctx.bmain, ID_OB, "Foo..000")); + + EXPECT_STREQ(id_a->name + 2, "Foo.001"); + EXPECT_STREQ(id_b->name + 2, "Foo.002"); + EXPECT_STREQ(id_c->name + 2, "Foo.004"); + EXPECT_STREQ(id_d->name + 2, "Foo.005"); + EXPECT_STREQ(id_e->name + 2, "Foo.006"); + EXPECT_STREQ(id_f->name + 2, "Foo..002"); + EXPECT_STREQ(id_g->name + 2, "Foo.007"); + EXPECT_STREQ(id_h->name + 2, "Foo.008"); + EXPECT_STREQ(id_i->name + 2, "Foo...001"); + EXPECT_STREQ(id_j->name + 2, "Foo..003"); + EXPECT_STREQ(id_k->name + 2, "Foo..004"); +} + +TEST(lib_id_main_unique_name, zero_suffix_is_never_assigned) +{ + LibIDMainSortTestContext ctx; + + /* Creating these should assign 002 to the first one, but the next + * ones should start numbers starting from 1: 001 and 003. */ + ID *id_002 = static_cast(BKE_id_new(ctx.bmain, ID_OB, "Foo.002")); + ID *id_001 = static_cast(BKE_id_new(ctx.bmain, ID_OB, "Foo.002")); + ID *id_003 = static_cast(BKE_id_new(ctx.bmain, ID_OB, "Foo.002")); + + EXPECT_STREQ(id_002->name + 2, "Foo.002"); + EXPECT_STREQ(id_001->name + 2, "Foo.001"); + EXPECT_STREQ(id_003->name + 2, "Foo.003"); +} + +TEST(lib_id_main_unique_name, remove_after_dup_get_original_name) +{ + LibIDMainSortTestContext ctx; + + ID *id_a = static_cast(BKE_id_new(ctx.bmain, ID_OB, "Foo")); + ID *id_b = static_cast(BKE_id_new(ctx.bmain, ID_OB, "Foo")); + + EXPECT_STREQ(id_a->name + 2, "Foo"); + EXPECT_STREQ(id_b->name + 2, "Foo.001"); + BKE_id_free(ctx.bmain, id_a); + + id_a = static_cast(BKE_id_new(ctx.bmain, ID_OB, "Foo")); + EXPECT_STREQ(id_a->name + 2, "Foo"); +} + +TEST(lib_id_main_unique_name, name_number_suffix_assignment) +{ + LibIDMainSortTestContext ctx; + + /* Create <1k objects first. */ + const int total_object_count = 1200; + ID *ids[total_object_count] = {}; + for (int i = 0; i < total_object_count / 2; ++i) { + ids[i] = static_cast(BKE_id_new(ctx.bmain, ID_OB, "Foo")); + } + + /* They should get assigned sequential numeric suffixes. */ + EXPECT_STREQ(ids[0]->name + 2, "Foo"); + EXPECT_STREQ(ids[1]->name + 2, "Foo.001"); + EXPECT_STREQ(ids[total_object_count / 2 - 1]->name + 2, "Foo.599"); + + /* Free some of the objects. */ + BKE_id_free(ctx.bmain, ids[10]); + BKE_id_free(ctx.bmain, ids[20]); + BKE_id_free(ctx.bmain, ids[30]); + + /* Create objects again; they should get suffixes that were just free'd up. */ + ID *id_010 = static_cast(BKE_id_new(ctx.bmain, ID_OB, "Foo")); + EXPECT_STREQ(id_010->name + 2, "Foo.010"); + ID *id_020 = static_cast(BKE_id_new(ctx.bmain, ID_OB, "Foo.123")); + EXPECT_STREQ(id_020->name + 2, "Foo.020"); + /* Suffixes >1k do not get the "use the most proper free one" treatment. */ + ID *id_2000 = static_cast(BKE_id_new(ctx.bmain, ID_OB, "Foo.2000")); + EXPECT_STREQ(id_2000->name + 2, "Foo.2000"); + /* But smaller than 1k suffixes do get proper empty spots. */ + ID *id_030 = static_cast(BKE_id_new(ctx.bmain, ID_OB, "Foo")); + EXPECT_STREQ(id_030->name + 2, "Foo.030"); + ID *id_600 = static_cast(BKE_id_new(ctx.bmain, ID_OB, "Foo")); + EXPECT_STREQ(id_600->name + 2, "Foo.600"); + + /* Max possible numeric suffix. */ + ID *id_max = static_cast(BKE_id_new(ctx.bmain, ID_OB, "Foo.999999999")); + EXPECT_STREQ(id_max->name + 2, "Foo.999999999"); + /* Try with max. possible suffix again: will assign free suffix under 1k. */ + ID *id_max1 = static_cast(BKE_id_new(ctx.bmain, ID_OB, "Foo.999999999")); + EXPECT_STREQ(id_max1->name + 2, "Foo.601"); + + /* Now create the rest of objects, to use all the suffixes up to 1k. + * Once all the ones up to 1k are used, the logic will fall back to + * "use largest number seen + 1", but the largest one is already the max + * possible. So it will shorten the name part and restart the counter, + * i.e. "Fo.001". */ + for (int i = total_object_count / 2; i < total_object_count; ++i) { + ids[i] = static_cast(BKE_id_new(ctx.bmain, ID_OB, "Foo")); + } + /* At this point creating "Foo" based objects will fall always + * result in shortened name to "Fo". */ + ID *id_fo178 = static_cast(BKE_id_new(ctx.bmain, ID_OB, "Foo")); + EXPECT_STREQ(id_fo178->name + 2, "Fo.178"); + ID *id_fo179 = static_cast(BKE_id_new(ctx.bmain, ID_OB, "Foo.2000")); + EXPECT_STREQ(id_fo179->name + 2, "Fo.179"); + ID *id_fo180 = static_cast(BKE_id_new(ctx.bmain, ID_OB, "Foo.999999999")); + EXPECT_STREQ(id_fo180->name + 2, "Fo.180"); +} + +TEST(lib_id_main_unique_name, renames_with_duplicates) +{ + LibIDMainSortTestContext ctx; + + ID *id_a = static_cast(BKE_id_new(ctx.bmain, ID_OB, "Foo")); + ID *id_b = static_cast(BKE_id_new(ctx.bmain, ID_OB, "Foo")); + ID *id_c = static_cast(BKE_id_new(ctx.bmain, ID_OB, "Bar")); + + EXPECT_STREQ(id_a->name + 2, "Foo"); + EXPECT_STREQ(id_b->name + 2, "Foo.001"); + EXPECT_STREQ(id_c->name + 2, "Bar"); + + BKE_libblock_rename(ctx.bmain, id_a, "Foo.002"); + EXPECT_STREQ(id_a->name + 2, "Foo.002"); + BKE_libblock_rename(ctx.bmain, id_b, "Bar"); + EXPECT_STREQ(id_b->name + 2, "Bar.001"); + BKE_libblock_rename(ctx.bmain, id_c, "Foo"); + EXPECT_STREQ(id_c->name + 2, "Foo"); + BKE_libblock_rename(ctx.bmain, id_b, "Bar"); + EXPECT_STREQ(id_b->name + 2, "Bar"); +} + +TEST(lib_id_main_unique_name, names_are_unique_per_id_type) +{ + LibIDMainSortTestContext ctx; + + ID *id_a = static_cast(BKE_id_new(ctx.bmain, ID_OB, "Foo")); + ID *id_b = static_cast(BKE_id_new(ctx.bmain, ID_CA, "Foo")); + ID *id_c = static_cast(BKE_id_new(ctx.bmain, ID_OB, "Foo")); - test_lib_id_main_sort_free(&ctx); + EXPECT_STREQ(id_a->name + 2, "Foo"); + EXPECT_STREQ(id_b->name + 2, "Foo"); /* Different types (OB & CA) can have the same name. */ + EXPECT_STREQ(id_c->name + 2, "Foo.001"); } } // namespace blender::bke::tests -- cgit v1.2.3 From 7fa7722350b471e3ce6b369137c5b41574a4bda2 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Thu, 14 Jul 2022 20:52:47 +1000 Subject: Cleanup: format, unused argument --- source/blender/blenkernel/BKE_fcurve.h | 3 ++- source/blender/blenkernel/intern/lib_id_test.cc | 2 +- source/blender/editors/screen/screen_user_menu.c | 9 ++++++++- source/blender/editors/space_nla/nla_edit.c | 9 +++++++-- 4 files changed, 18 insertions(+), 5 deletions(-) diff --git a/source/blender/blenkernel/BKE_fcurve.h b/source/blender/blenkernel/BKE_fcurve.h index 3ccbd2ac1da..c5788c07336 100644 --- a/source/blender/blenkernel/BKE_fcurve.h +++ b/source/blender/blenkernel/BKE_fcurve.h @@ -488,7 +488,8 @@ void BKE_fcurve_delete_keys_all(struct FCurve *fcu); */ void BKE_fcurve_handles_recalc(struct FCurve *fcu); /** - * Variant of #BKE_fcurve_handles_recalc() that allows calculating based on a different select flag. + * Variant of #BKE_fcurve_handles_recalc() that allows calculating based on a different select + * flag. * * \param handle_sel_flag: The flag (bezt.f1/2/3) value to use to determine selection. * Usually `SELECT`, but may want to use a different one at times diff --git a/source/blender/blenkernel/intern/lib_id_test.cc b/source/blender/blenkernel/intern/lib_id_test.cc index 96718b3a65b..1aba78eed8f 100644 --- a/source/blender/blenkernel/intern/lib_id_test.cc +++ b/source/blender/blenkernel/intern/lib_id_test.cc @@ -57,7 +57,7 @@ TEST(lib_id_main_sort, local_ids_1) test_lib_id_main_sort_check_order({id_a, id_b, id_c}); } -static void change_lib(Main *bmain, ID *id, Library *lib) +static void change_lib(Main * /*bmain*/, ID *id, Library *lib) { id->lib = lib; } diff --git a/source/blender/editors/screen/screen_user_menu.c b/source/blender/editors/screen/screen_user_menu.c index 67ca6a83b2b..9d66debda6f 100644 --- a/source/blender/editors/screen/screen_user_menu.c +++ b/source/blender/editors/screen/screen_user_menu.c @@ -214,7 +214,14 @@ static void screen_user_menu_draw(const bContext *C, Menu *menu) wmOperatorType *ot = WM_operatortype_find(umi_op->op_idname, false); if (ot != NULL) { IDProperty *prop = umi_op->prop ? IDP_CopyProperty(umi_op->prop) : NULL; - uiItemFullO_ptr(menu->layout, ot, CTX_IFACE_(ot->translation_context, ui_name), ICON_NONE, prop, umi_op->opcontext, 0, NULL); + uiItemFullO_ptr(menu->layout, + ot, + CTX_IFACE_(ot->translation_context, ui_name), + ICON_NONE, + prop, + umi_op->opcontext, + 0, + NULL); is_empty = false; } else { diff --git a/source/blender/editors/space_nla/nla_edit.c b/source/blender/editors/space_nla/nla_edit.c index 42509f17b92..d1a667c6e4e 100644 --- a/source/blender/editors/space_nla/nla_edit.c +++ b/source/blender/editors/space_nla/nla_edit.c @@ -2204,8 +2204,13 @@ static int nlaedit_apply_scale_exec(bContext *C, wmOperator *UNUSED(op)) /* setup iterator, and iterate over all the keyframes in the action, * applying this scaling */ ked.data = strip; - ANIM_animchanneldata_keyframes_loop( - &ked, ac.ads, strip->act, ALE_ACT, NULL, bezt_apply_nlamapping, BKE_fcurve_handles_recalc); + ANIM_animchanneldata_keyframes_loop(&ked, + ac.ads, + strip->act, + ALE_ACT, + NULL, + bezt_apply_nlamapping, + BKE_fcurve_handles_recalc); /* clear scale of strip now that it has been applied, * and recalculate the extents of the action now that it has been scaled -- cgit v1.2.3 From 64e196422e85e5177e3b30d2646fc6e9c34e6628 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Thu, 14 Jul 2022 20:55:02 +1000 Subject: GHOST/X11: Quiet warning about key-release events having their utf8 set Quiet warning from [0], no functional change as the this information was always ignored. Key release events shouldn't have associated text, this was cleared for wmEvent's, so there is no reason to pass it from GHOST. [0]: d6fef73ef110eb43756b7b87c2cba80abae3b39f --- intern/ghost/intern/GHOST_SystemX11.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/intern/ghost/intern/GHOST_SystemX11.cpp b/intern/ghost/intern/GHOST_SystemX11.cpp index 5e549b54afd..6b28c271059 100644 --- a/intern/ghost/intern/GHOST_SystemX11.cpp +++ b/intern/ghost/intern/GHOST_SystemX11.cpp @@ -1194,6 +1194,11 @@ void GHOST_SystemX11::processEvent(XEvent *xe) } #endif + if (type != GHOST_kEventKeyDown) { + ascii = 0; + utf8_buf = nullptr; + } + g_event = new GHOST_EventKey( getMilliSeconds(), type, window, gkey, ascii, utf8_buf, is_repeat); -- cgit v1.2.3 From db80cf6ad79cad93ce5638d6e406e674775fc5dc Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Thu, 14 Jul 2022 21:07:37 +1000 Subject: GHOST/X11: avoid redundant utf8 text lookups for release events The text representation of release events is never used, so only calculate this for press events. --- intern/ghost/intern/GHOST_SystemX11.cpp | 91 ++++++++++++++++++--------------- 1 file changed, 49 insertions(+), 42 deletions(-) diff --git a/intern/ghost/intern/GHOST_SystemX11.cpp b/intern/ghost/intern/GHOST_SystemX11.cpp index 6b28c271059..4145856ee16 100644 --- a/intern/ghost/intern/GHOST_SystemX11.cpp +++ b/intern/ghost/intern/GHOST_SystemX11.cpp @@ -1017,7 +1017,9 @@ void GHOST_SystemX11::processEvent(XEvent *xe) case KeyRelease: { XKeyEvent *xke = &(xe->xkey); KeySym key_sym; + char *utf8_buf = nullptr; char ascii; + #if defined(WITH_X11_XINPUT) && defined(X_HAVE_UTF8_STRING) /* utf8_array[] is initial buffer used for Xutf8LookupString(). * if the length of the utf8 string exceeds this array, allocate @@ -1026,12 +1028,10 @@ void GHOST_SystemX11::processEvent(XEvent *xe) * at the end of this buffer when the constructor of GHOST_EventKey * reads 6 bytes regardless of the effective data length. */ char utf8_array[16 * 6 + 5]; /* 16 utf8 characters */ - char *utf8_buf = utf8_array; - int len = 1; /* at least one null character will be stored */ + int len = 1; /* at least one null character will be stored */ #else - char *utf8_buf = nullptr; + char utf8_array[sizeof(GHOST_TEventKeyData::utf8_buf)] = {'\0'}; #endif - GHOST_TEventType type = (xke->type == KeyPress) ? GHOST_kEventKeyDown : GHOST_kEventKeyUp; GHOST_TKey gkey; @@ -1151,61 +1151,68 @@ void GHOST_SystemX11::processEvent(XEvent *xe) #endif #if defined(WITH_X11_XINPUT) && defined(X_HAVE_UTF8_STRING) - /* Setting unicode on key-up events gives #XLookupNone status. */ - XIC xic = window->getX11_XIC(); - if (xic && xke->type == KeyPress) { - Status status; + /* Only used for key-press. */ + XIC xic = nullptr; +#endif - /* Use utf8 because its not locale repentant, from XORG docs. */ - if (!(len = Xutf8LookupString( - xic, xke, utf8_buf, sizeof(utf8_array) - 5, &key_sym, &status))) { - utf8_buf[0] = '\0'; - } + if (xke->type == KeyPress) { + utf8_buf = utf8_array; +#if defined(WITH_X11_XINPUT) && defined(X_HAVE_UTF8_STRING) + /* Setting unicode on key-up events gives #XLookupNone status. */ + xic = window->getX11_XIC(); + if (xic && xke->type == KeyPress) { + Status status; + + /* Use utf8 because its not locale repentant, from XORG docs. */ + if (!(len = Xutf8LookupString( + xic, xke, utf8_buf, sizeof(utf8_array) - 5, &key_sym, &status))) { + utf8_buf[0] = '\0'; + } - if (status == XBufferOverflow) { - utf8_buf = (char *)malloc(len + 5); - len = Xutf8LookupString(xic, xke, utf8_buf, len, &key_sym, &status); - } + if (status == XBufferOverflow) { + utf8_buf = (char *)malloc(len + 5); + len = Xutf8LookupString(xic, xke, utf8_buf, len, &key_sym, &status); + } - if (ELEM(status, XLookupChars, XLookupBoth)) { - if ((unsigned char)utf8_buf[0] >= 32) { /* not an ascii control character */ - /* do nothing for now, this is valid utf8 */ + if (ELEM(status, XLookupChars, XLookupBoth)) { + if ((unsigned char)utf8_buf[0] >= 32) { /* not an ascii control character */ + /* do nothing for now, this is valid utf8 */ + } + else { + utf8_buf[0] = '\0'; + } + } + else if (status == XLookupKeySym) { + /* this key doesn't have a text representation, it is a command + * key of some sort */ } else { - utf8_buf[0] = '\0'; + printf("Bad keycode lookup. Keysym 0x%x Status: %s\n", + (unsigned int)key_sym, + (status == XLookupNone ? "XLookupNone" : + status == XLookupKeySym ? "XLookupKeySym" : + "Unknown status")); + + printf("'%.*s' %p %p\n", len, utf8_buf, xic, m_xim); } } - else if (status == XLookupKeySym) { - /* this key doesn't have a text representation, it is a command - * key of some sort */ - } else { - printf("Bad keycode lookup. Keysym 0x%x Status: %s\n", - (unsigned int)key_sym, - (status == XLookupNone ? "XLookupNone" : - status == XLookupKeySym ? "XLookupKeySym" : - "Unknown status")); - - printf("'%.*s' %p %p\n", len, utf8_buf, xic, m_xim); + utf8_buf[0] = '\0'; } - } - else { - utf8_buf[0] = '\0'; - } #endif - - if (type != GHOST_kEventKeyDown) { - ascii = 0; - utf8_buf = nullptr; + if (!utf8_buf[0] && ascii) { + utf8_buf[0] = ascii; + utf8_buf[1] = '\0'; + } } g_event = new GHOST_EventKey( - getMilliSeconds(), type, window, gkey, ascii, utf8_buf, is_repeat); + getMilliSeconds(), type, window, gkey, '\0', utf8_buf, is_repeat); #if defined(WITH_X11_XINPUT) && defined(X_HAVE_UTF8_STRING) /* when using IM for some languages such as Japanese, * one event inserts multiple utf8 characters */ - if (xic && xke->type == KeyPress) { + if (xke->type == KeyPress && xic) { unsigned char c; int i = 0; while (1) { -- cgit v1.2.3 From eb3e56a36e18fd4d560a31c2b60ceb235fd0da09 Mon Sep 17 00:00:00 2001 From: Omar Emara Date: Thu, 14 Jul 2022 13:52:44 +0200 Subject: Fix: Wrong output types for some compositor nodes The Difference Matte and RGB To BW nodes have a wrong output type. They should be floats but are of type color. This is a regression that was introduced during the migration to the socket builder API in D13266. Reviewed By: Blendify, fclem Differential Revision: https://developer.blender.org/D15232 --- source/blender/nodes/composite/nodes/node_composite_diff_matte.cc | 2 +- source/blender/nodes/composite/nodes/node_composite_val_to_rgb.cc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) 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 20dd61a9725..b87bbe439db 100644 --- a/source/blender/nodes/composite/nodes/node_composite_diff_matte.cc +++ b/source/blender/nodes/composite/nodes/node_composite_diff_matte.cc @@ -19,7 +19,7 @@ static void cmp_node_diff_matte_declare(NodeDeclarationBuilder &b) b.add_input(N_("Image 1")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); b.add_input(N_("Image 2")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); b.add_output(N_("Image")); - b.add_output(N_("Matte")); + b.add_output(N_("Matte")); } static void node_composit_init_diff_matte(bNodeTree *UNUSED(ntree), bNode *node) 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 0dfdeda24e6..df669d5beda 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 @@ -49,7 +49,7 @@ namespace blender::nodes::node_composite_val_to_rgb_cc { static void cmp_node_rgbtobw_declare(NodeDeclarationBuilder &b) { b.add_input(N_("Image")).default_value({0.8f, 0.8f, 0.8f, 1.0f}); - b.add_output(N_("Val")); + b.add_output(N_("Val")); } } // namespace blender::nodes::node_composite_val_to_rgb_cc -- cgit v1.2.3 From cdd8b96e3b476df38847121102c6a267b5522a85 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Thu, 14 Jul 2022 21:39:31 +1000 Subject: GHOST: remove redundant ascii argument to GHOST_EventKey Now only the utf8 buffer is used there is no reason to pass both. --- intern/ghost/intern/GHOST_EventKey.h | 14 +++++--------- intern/ghost/intern/GHOST_SystemCocoa.mm | 23 +++++------------------ intern/ghost/intern/GHOST_SystemSDL.cpp | 3 +-- intern/ghost/intern/GHOST_SystemWayland.cpp | 9 ++++----- intern/ghost/intern/GHOST_SystemWin32.cpp | 5 ++--- intern/ghost/intern/GHOST_SystemX11.cpp | 10 ++++------ 6 files changed, 21 insertions(+), 43 deletions(-) diff --git a/intern/ghost/intern/GHOST_EventKey.h b/intern/ghost/intern/GHOST_EventKey.h index d3cfbbeddd7..f85a674be9f 100644 --- a/intern/ghost/intern/GHOST_EventKey.h +++ b/intern/ghost/intern/GHOST_EventKey.h @@ -22,6 +22,7 @@ class GHOST_EventKey : public GHOST_Event { * \param msec: The time this event was generated. * \param type: The type of key event. * \param key: The key code of the key. + * \param is_repeat: Enabled for key repeat events (only for press events). */ GHOST_EventKey( uint64_t msec, GHOST_TEventType type, GHOST_IWindow *window, GHOST_TKey key, bool is_repeat) @@ -38,15 +39,15 @@ class GHOST_EventKey : public GHOST_Event { * \param msec: The time this event was generated. * \param type: The type of key event. * \param key: The key code of the key. - * \param ascii: The ascii code for the key event. + * \param is_repeat: Enabled for key repeat events (only for press events). + * \param utf8_buf: The text associated with this key event (only for press events). */ GHOST_EventKey(uint64_t msec, GHOST_TEventType type, GHOST_IWindow *window, GHOST_TKey key, - char ascii, - const char utf8_buf[6], - bool is_repeat) + bool is_repeat, + const char utf8_buf[6]) : GHOST_Event(msec, type, window) { m_keyEventData.key = key; @@ -56,11 +57,6 @@ class GHOST_EventKey : public GHOST_Event { else { m_keyEventData.utf8_buf[0] = '\0'; } - /* TODO(@campbellbarton): phase out `ascii` and always use `utf8_buf`, this needs to be done - * on all platforms though, so for now write the ascii into the utf8_buf. */ - if (m_keyEventData.utf8_buf[0] == '\0' && ascii) { - m_keyEventData.utf8_buf[0] = ascii; - } m_keyEventData.is_repeat = is_repeat; m_data = &m_keyEventData; } diff --git a/intern/ghost/intern/GHOST_SystemCocoa.mm b/intern/ghost/intern/GHOST_SystemCocoa.mm index 0a905f5093e..c247ef3daa0 100644 --- a/intern/ghost/intern/GHOST_SystemCocoa.mm +++ b/intern/ghost/intern/GHOST_SystemCocoa.mm @@ -1779,7 +1779,6 @@ GHOST_TSuccess GHOST_SystemCocoa::handleKeyEvent(void *eventPtr) NSString *characters; NSData *convertedCharacters; GHOST_TKey keyCode; - unsigned char ascii; NSString *charsIgnoringModifiers; window = m_windowManager->getWindowAssociatedWithOSWindow((void *)[event window]); @@ -1789,7 +1788,6 @@ GHOST_TSuccess GHOST_SystemCocoa::handleKeyEvent(void *eventPtr) } char utf8_buf[6] = {'\0'}; - ascii = 0; switch ([event type]) { @@ -1809,7 +1807,6 @@ GHOST_TSuccess GHOST_SystemCocoa::handleKeyEvent(void *eventPtr) kUCKeyActionUp); } - /* handling both unicode or ascii */ characters = [event characters]; if ([characters length] > 0) { convertedCharacters = [characters dataUsingEncoding:NSUTF8StringEncoding]; @@ -1835,41 +1832,31 @@ GHOST_TSuccess GHOST_SystemCocoa::handleKeyEvent(void *eventPtr) if ((keyCode == GHOST_kKeyQ) && (m_modifierMask & NSEventModifierFlagCommand)) break; // Cmd-Q is directly handled by Cocoa - /* ascii is a subset of unicode */ - if (utf8_buf[0] && !utf8_buf[1]) { - ascii = utf8_buf[0]; - } - if ([event type] == NSEventTypeKeyDown) { pushEvent(new GHOST_EventKey([event timestamp] * 1000, GHOST_kEventKeyDown, window, keyCode, - ascii, - utf8_buf, - [event isARepeat])); + [event isARepeat], + utf8_buf)); #if 0 - printf("Key down rawCode=0x%x charsIgnoringModifiers=%c keyCode=%u ascii=%i %c utf8=%s\n", + printf("Key down rawCode=0x%x charsIgnoringModifiers=%c keyCode=%u utf8=%s\n", [event keyCode], [charsIgnoringModifiers length] > 0 ? [charsIgnoringModifiers characterAtIndex:0] : ' ', keyCode, - ascii, - ascii, utf8_buf); #endif } else { pushEvent(new GHOST_EventKey( - [event timestamp] * 1000, GHOST_kEventKeyUp, window, keyCode, 0, NULL, false)); + [event timestamp] * 1000, GHOST_kEventKeyUp, window, keyCode, false, NULL)); #if 0 - printf("Key up rawCode=0x%x charsIgnoringModifiers=%c keyCode=%u ascii=%i %c utf8=%s\n", + printf("Key up rawCode=0x%x charsIgnoringModifiers=%c keyCode=%u utf8=%s\n", [event keyCode], [charsIgnoringModifiers length] > 0 ? [charsIgnoringModifiers characterAtIndex:0] : ' ', keyCode, - ascii, - ascii, utf8_buf); #endif } diff --git a/intern/ghost/intern/GHOST_SystemSDL.cpp b/intern/ghost/intern/GHOST_SystemSDL.cpp index 55a15ae470e..d912b57f049 100644 --- a/intern/ghost/intern/GHOST_SystemSDL.cpp +++ b/intern/ghost/intern/GHOST_SystemSDL.cpp @@ -608,8 +608,7 @@ void GHOST_SystemSDL::processEvent(SDL_Event *sdl_event) utf8_buf[0] = convert_keyboard_event_to_ascii(sdl_sub_evt); } - g_event = new GHOST_EventKey( - getMilliSeconds(), type, window, gkey, '\0', utf8_buf, is_repeat); + g_event = new GHOST_EventKey(getMilliSeconds(), type, window, gkey, is_repeat, utf8_buf); break; } } diff --git a/intern/ghost/intern/GHOST_SystemWayland.cpp b/intern/ghost/intern/GHOST_SystemWayland.cpp index 876f77732c2..099792a4d06 100644 --- a/intern/ghost/intern/GHOST_SystemWayland.cpp +++ b/intern/ghost/intern/GHOST_SystemWayland.cpp @@ -2257,8 +2257,8 @@ static void keyboard_handle_key(void *data, if (wl_surface *focus_surface = input->keyboard.wl_surface) { GHOST_IWindow *win = ghost_wl_surface_user_data(focus_surface); - input->system->pushEvent(new GHOST_EventKey( - input->system->getMilliSeconds(), etype, win, gkey, '\0', utf8_buf, false)); + input->system->pushEvent( + new GHOST_EventKey(input->system->getMilliSeconds(), etype, win, gkey, false, utf8_buf)); } /* An existing payload means the key repeat timer is reset and will be added again. */ @@ -2290,9 +2290,8 @@ static void keyboard_handle_key(void *data, GHOST_kEventKeyDown, win, payload->key_data.gkey, - '\0', - utf8_buf, - true)); + true, + utf8_buf)); } }; input->key_repeat.timer = input->system->installTimer( diff --git a/intern/ghost/intern/GHOST_SystemWin32.cpp b/intern/ghost/intern/GHOST_SystemWin32.cpp index 1365c281ab4..5a930209376 100644 --- a/intern/ghost/intern/GHOST_SystemWin32.cpp +++ b/intern/ghost/intern/GHOST_SystemWin32.cpp @@ -1256,9 +1256,8 @@ GHOST_EventKey *GHOST_SystemWin32::processKeyEvent(GHOST_WindowWin32 *window, RA keyDown ? GHOST_kEventKeyDown : GHOST_kEventKeyUp, window, key, - ascii, - utf8_char, - is_repeat); + is_repeat, + utf8_char); // GHOST_PRINTF("%c\n", ascii); // we already get this info via EventPrinter } diff --git a/intern/ghost/intern/GHOST_SystemX11.cpp b/intern/ghost/intern/GHOST_SystemX11.cpp index 4145856ee16..70a9687672c 100644 --- a/intern/ghost/intern/GHOST_SystemX11.cpp +++ b/intern/ghost/intern/GHOST_SystemX11.cpp @@ -745,9 +745,8 @@ bool GHOST_SystemX11::processEvents(bool waitForEvent) GHOST_kEventKeyDown, window, ghost_key_from_keysym(modifiers[i]), - '\0', - nullptr, - false)); + false, + nullptr)); } } } @@ -1206,8 +1205,7 @@ void GHOST_SystemX11::processEvent(XEvent *xe) } } - g_event = new GHOST_EventKey( - getMilliSeconds(), type, window, gkey, '\0', utf8_buf, is_repeat); + g_event = new GHOST_EventKey(getMilliSeconds(), type, window, gkey, is_repeat, utf8_buf); #if defined(WITH_X11_XINPUT) && defined(X_HAVE_UTF8_STRING) /* when using IM for some languages such as Japanese, @@ -1232,7 +1230,7 @@ void GHOST_SystemX11::processEvent(XEvent *xe) pushEvent(g_event); g_event = new GHOST_EventKey( - getMilliSeconds(), type, window, gkey, '\0', &utf8_buf[i], is_repeat); + getMilliSeconds(), type, window, gkey, is_repeat, &utf8_buf[i]); } } -- cgit v1.2.3 From 93f74299f07585d5c922aa048055141e7a487e07 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Thu, 14 Jul 2022 21:55:46 +1000 Subject: Cleanup: clang-tidy changes to GHOST_SystemX11 Also remove redundant check. --- intern/ghost/intern/GHOST_SystemX11.cpp | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/intern/ghost/intern/GHOST_SystemX11.cpp b/intern/ghost/intern/GHOST_SystemX11.cpp index 70a9687672c..00cc5f3af8f 100644 --- a/intern/ghost/intern/GHOST_SystemX11.cpp +++ b/intern/ghost/intern/GHOST_SystemX11.cpp @@ -1159,7 +1159,7 @@ void GHOST_SystemX11::processEvent(XEvent *xe) #if defined(WITH_X11_XINPUT) && defined(X_HAVE_UTF8_STRING) /* Setting unicode on key-up events gives #XLookupNone status. */ xic = window->getX11_XIC(); - if (xic && xke->type == KeyPress) { + if (xic) { Status status; /* Use utf8 because its not locale repentant, from XORG docs. */ @@ -1213,20 +1213,21 @@ void GHOST_SystemX11::processEvent(XEvent *xe) if (xke->type == KeyPress && xic) { unsigned char c; int i = 0; - while (1) { - /* search character boundary */ - if ((unsigned char)utf8_buf[i++] > 0x7f) { + while (true) { + /* Search character boundary. */ + if ((uchar)utf8_buf[i++] > 0x7f) { for (; i < len; ++i) { c = utf8_buf[i]; - if (c < 0x80 || c > 0xbf) + if (c < 0x80 || c > 0xbf) { break; + } } } - if (i >= len) + if (i >= len) { break; - - /* enqueue previous character */ + } + /* Enqueue previous character. */ pushEvent(g_event); g_event = new GHOST_EventKey( @@ -1234,8 +1235,9 @@ void GHOST_SystemX11::processEvent(XEvent *xe) } } - if (utf8_buf != utf8_array) + if (utf8_buf != utf8_array) { free(utf8_buf); + } #endif break; -- cgit v1.2.3 From 9dfabc1de33f555d3e2856974952fc5c646b3b85 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Thu, 14 Jul 2022 22:01:43 +1000 Subject: Cleanup: remove redundant `event->val` check for 3D text insertion --- source/blender/editors/curve/editfont.c | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/source/blender/editors/curve/editfont.c b/source/blender/editors/curve/editfont.c index 623f41dcd20..ceed12dcff1 100644 --- a/source/blender/editors/curve/editfont.c +++ b/source/blender/editors/curve/editfont.c @@ -1642,7 +1642,6 @@ static int insert_text_invoke(bContext *C, wmOperator *op, const wmEvent *event) const bool alt = event->modifier & KM_ALT; const bool shift = event->modifier & KM_SHIFT; const bool ctrl = event->modifier & KM_CTRL; - int event_type = event->type, event_val = event->val; char32_t insert_char_override = 0; char32_t inserted_text[2] = {0}; @@ -1657,7 +1656,7 @@ static int insert_text_invoke(bContext *C, wmOperator *op, const wmEvent *event) return OPERATOR_FINISHED; } - if (event_type == EVT_BACKSPACEKEY) { + if (event->type == EVT_BACKSPACEKEY) { if (alt && ef->len != 0 && ef->pos > 0) { accentcode = true; } @@ -1665,14 +1664,14 @@ static int insert_text_invoke(bContext *C, wmOperator *op, const wmEvent *event) } /* Tab typically exit edit-mode, but we allow it to be typed using modifier keys. */ - if (event_type == EVT_TABKEY) { + if (event->type == EVT_TABKEY) { if ((alt || ctrl || shift) == 0) { return OPERATOR_PASS_THROUGH; } insert_char_override = '\t'; } - if (event_val && (insert_char_override || event->utf8_buf[0])) { + if (insert_char_override || event->utf8_buf[0]) { if (insert_char_override) { /* Handle case like TAB ('\t'). */ inserted_text[0] = insert_char_override; @@ -1714,11 +1713,6 @@ static int insert_text_invoke(bContext *C, wmOperator *op, const wmEvent *event) RNA_string_set(op->ptr, "text", inserted_utf8); } - /* reset property? */ - if (event_val == 0) { - accentcode = false; - } - return OPERATOR_FINISHED; } -- cgit v1.2.3 From 2d04012e57a75254ccfe97ed2df747e03dcd4da5 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Thu, 14 Jul 2022 22:02:52 +1000 Subject: Cleanup: spelling in comments Also remove duplicate comments in bmesh_log.h, caused by automated comment relocation in [0]. [0]: c4e041da23b9c45273fcd4874308c536b6a315d1 --- intern/cycles/scene/scene.h | 4 +- source/blender/blenkernel/intern/brush.cc | 6 +- .../blenkernel/intern/mesh_merge_customdata.cc | 6 +- source/blender/bmesh/intern/bmesh_log.c | 32 +++--- source/blender/bmesh/intern/bmesh_log.h | 113 +++++++++++---------- source/blender/gpu/metal/mtl_texture.hh | 2 +- 6 files changed, 84 insertions(+), 79 deletions(-) diff --git a/intern/cycles/scene/scene.h b/intern/cycles/scene/scene.h index d04c6a27f11..d1004bb7b66 100644 --- a/intern/cycles/scene/scene.h +++ b/intern/cycles/scene/scene.h @@ -82,7 +82,7 @@ class DeviceScene { device_vector patches; - /* pointcloud */ + /* point-cloud */ device_vector points; device_vector points_shader; @@ -124,7 +124,7 @@ class DeviceScene { /* integrator */ device_vector sample_pattern_lut; - /* ies lights */ + /* IES lights */ device_vector ies_lights; KernelData data; diff --git a/source/blender/blenkernel/intern/brush.cc b/source/blender/blenkernel/intern/brush.cc index 1c2408bab8a..99733c8edb3 100644 --- a/source/blender/blenkernel/intern/brush.cc +++ b/source/blender/blenkernel/intern/brush.cc @@ -587,15 +587,15 @@ bool BKE_brush_delete(Main *bmain, Brush *brush) return true; } -/* grease pencil cumapping->preset */ -typedef enum eGPCurveMappingPreset { +/** Local grease pencil curve mapping preset. */ +using eGPCurveMappingPreset = enum eGPCurveMappingPreset { GPCURVE_PRESET_PENCIL = 0, GPCURVE_PRESET_INK = 1, GPCURVE_PRESET_INKNOISE = 2, GPCURVE_PRESET_MARKER = 3, GPCURVE_PRESET_CHISEL_SENSIVITY = 4, GPCURVE_PRESET_CHISEL_STRENGTH = 5, -} eGPCurveMappingPreset; +}; static void brush_gpencil_curvemap_reset(CurveMap *cuma, int tot, int preset) { diff --git a/source/blender/blenkernel/intern/mesh_merge_customdata.cc b/source/blender/blenkernel/intern/mesh_merge_customdata.cc index adaf378ed27..7bc429954b0 100644 --- a/source/blender/blenkernel/intern/mesh_merge_customdata.cc +++ b/source/blender/blenkernel/intern/mesh_merge_customdata.cc @@ -33,11 +33,11 @@ static int compare_v2_classify(const float uv_a[2], const float uv_b[2]) if (uv_a[0] == uv_b[0] && uv_a[1] == uv_b[1]) { return CMP_EQUAL; } - /* Note that the ULP value is the primary value used to compare relative values - * as the absolute value doesn't account for float precision at difference scales. + /* NOTE(@campbellbarton): that the ULP value is the primary value used to compare relative + * values as the absolute value doesn't account for float precision at difference scales. * - For subdivision-surface ULP of 3 is sufficient, * although this value is extremely small. - * - For bevel the URL of 12 is sufficient to merge UV's that appear to be connected + * - For bevel the ULP of 12 is sufficient to merge UV's that appear to be connected * with bevel on Suzanne beveled 15% with 6 segments. * * These values could be tweaked but should be kept on the small side to prevent diff --git a/source/blender/bmesh/intern/bmesh_log.c b/source/blender/bmesh/intern/bmesh_log.c index a81ae934629..64e6c63e9f0 100644 --- a/source/blender/bmesh/intern/bmesh_log.c +++ b/source/blender/bmesh/intern/bmesh_log.c @@ -34,42 +34,41 @@ struct BMLogEntry { struct BMLogEntry *next, *prev; - /* The following GHashes map from an element ID to one of the log - * types above */ + /* The following #GHash members map from an element ID to one of the log types above. */ - /* Elements that were in the previous entry, but have been - * deleted */ + /** Elements that were in the previous entry, but have been deleted. */ GHash *deleted_verts; GHash *deleted_faces; - /* Elements that were not in the previous entry, but are in the - * result of this entry */ + /** Elements that were not in the previous entry, but are in the result of this entry. */ GHash *added_verts; GHash *added_faces; - /* Vertices whose coordinates, mask value, or hflag have changed */ + /** Vertices whose coordinates, mask value, or hflag have changed. */ GHash *modified_verts; GHash *modified_faces; BLI_mempool *pool_verts; BLI_mempool *pool_faces; - /* This is only needed for dropping BMLogEntries while still in + /** + * This is only needed for dropping BMLogEntries while still in * dynamic-topology mode, as that should release vert/face IDs - * back to the BMLog but no BMLog pointer is available at that - * time. + * back to the BMLog but no BMLog pointer is available at that time. * * This field is not guaranteed to be valid, any use of it should - * check for NULL. */ + * check for NULL. + */ BMLog *log; }; struct BMLog { - /* Tree of free IDs */ + /** Tree of free IDs */ struct RangeTreeUInt *unused_ids; - /* Mapping from unique IDs to vertices and faces + /** + * Mapping from unique IDs to vertices and faces * - * Each vertex and face in the log gets a unique uinteger + * Each vertex and face in the log gets a unique `uint` * assigned. That ID is taken from the set managed by the * unused_ids range tree. * @@ -79,10 +78,11 @@ struct BMLog { GHash *id_to_elem; GHash *elem_to_id; - /* All BMLogEntrys, ordered from earliest to most recent */ + /** All #BMLogEntrys, ordered from earliest to most recent. */ ListBase entries; - /* The current log entry from entries list + /** + * The current log entry from entries list * * If null, then the original mesh from before any of the log * entries is current (i.e. there is nothing left to undo.) diff --git a/source/blender/bmesh/intern/bmesh_log.h b/source/blender/bmesh/intern/bmesh_log.h index 189aa97509f..5daa5dd9a68 100644 --- a/source/blender/bmesh/intern/bmesh_log.h +++ b/source/blender/bmesh/intern/bmesh_log.h @@ -14,12 +14,13 @@ struct RangeTreeUInt; typedef struct BMLog BMLog; typedef struct BMLogEntry BMLogEntry; -/* Allocate and initialize a new BMLog */ -/* Allocate, initialize, and assign a new BMLog */ +/** + * Allocate, initialize, and assign a new BMLog. + */ BMLog *BM_log_create(BMesh *bm); -/* Allocate and initialize a new BMLog using existing BMLogEntries */ -/* Allocate and initialize a new BMLog using existing BMLogEntries +/** + * Allocate and initialize a new #BMLog using existing #BMLogEntries * * The 'entry' should be the last entry in the BMLog. Its prev pointer * will be followed back to find the first entry. @@ -29,20 +30,21 @@ BMLog *BM_log_create(BMesh *bm); */ BMLog *BM_log_from_existing_entries_create(BMesh *bm, BMLogEntry *entry); -/* Free all the data in a BMLog including the log itself */ -/* Free all the data in a BMLog including the log itself */ +/** + * Free all the data in a BMLog including the log itself. + */ void BM_log_free(BMLog *log); -/* Get the number of log entries */ -/* Get the number of log entries */ +/** + * Get the number of log entries. + */ int BM_log_length(const BMLog *log); -/* Apply a consistent ordering to BMesh vertices and faces */ -/* Apply a consistent ordering to BMesh vertices */ +/** Apply a consistent ordering to BMesh vertices and faces. */ void BM_log_mesh_elems_reorder(BMesh *bm, BMLog *log); -/* Start a new log entry and update the log entry list */ -/* Start a new log entry and update the log entry list +/** + * Start a new log entry and update the log entry list. * * If the log entry list is empty, or if the current log entry is the * last entry, the new entry is simply appended to the end. @@ -54,35 +56,36 @@ void BM_log_mesh_elems_reorder(BMesh *bm, BMLog *log); */ BMLogEntry *BM_log_entry_add(BMLog *log); -/* Mark all used ids as unused for this node */ +/** Mark all used ids as unused for this node */ void BM_log_cleanup_entry(BMLogEntry *entry); -/* Remove an entry from the log */ -/* Remove an entry from the log +/** + * Remove an entry from the log. * * Uses entry->log as the log. If the log is NULL, the entry will be - * free'd but not removed from any list, nor shall its IDs be - * released. + * free'd but not removed from any list, nor shall its IDs be released. * * This operation is only valid on the first and last entries in the * log. Deleting from the middle will assert. */ void BM_log_entry_drop(BMLogEntry *entry); -/* Undo one BMLogEntry */ -/* Undo one BMLogEntry +/** + * Undo one #BMLogEntry. * - * Has no effect if there's nothing left to undo */ + * Has no effect if there's nothing left to undo. + */ void BM_log_undo(BMesh *bm, BMLog *log); -/* Redo one BMLogEntry */ -/* Redo one BMLogEntry +/** + * Redo one #BMLogEntry. * - * Has no effect if there's nothing left to redo */ + * Has no effect if there's nothing left to redo. + */ void BM_log_redo(BMesh *bm, BMLog *log); -/* Log a vertex before it is modified */ -/* Log a vertex before it is modified +/** + * Log a vertex before it is modified. * * Before modifying vertex coordinates, masks, or hflags, call this * function to log its current values. This is better than logging @@ -107,8 +110,8 @@ void BM_log_redo(BMesh *bm, BMLog *log); */ void BM_log_vert_before_modified(BMLog *log, struct BMVert *v, int cd_vert_mask_offset); -/* Log a new vertex as added to the BMesh */ -/* Log a new vertex as added to the BMesh +/** + * Log a new vertex as added to the #BMesh. * * The new vertex gets a unique ID assigned. It is then added to a map * of added vertices, with the key being its ID and the value @@ -116,16 +119,16 @@ void BM_log_vert_before_modified(BMLog *log, struct BMVert *v, int cd_vert_mask_ */ void BM_log_vert_added(BMLog *log, struct BMVert *v, int cd_vert_mask_offset); -/* Log a face before it is modified */ -/* Log a face before it is modified +/** + * Log a face before it is modified. * * This is intended to handle only header flags and we always - * assume face has been added before + * assume face has been added before. */ void BM_log_face_modified(BMLog *log, struct BMFace *f); -/* Log a new face as added to the BMesh */ -/* Log a new face as added to the BMesh +/** + * Log a new face as added to the #BMesh. * * The new face gets a unique ID assigned. It is then added to a map * of added faces, with the key being its ID and the value containing @@ -133,8 +136,8 @@ void BM_log_face_modified(BMLog *log, struct BMFace *f); */ void BM_log_face_added(BMLog *log, struct BMFace *f); -/* Log a vertex as removed from the BMesh */ -/* Log a vertex as removed from the BMesh +/** + * Log a vertex as removed from the #BMesh. * * A couple things can happen here: * @@ -142,7 +145,7 @@ void BM_log_face_added(BMLog *log, struct BMFace *f); * deleted and forgotten about entirely. Its unique ID is returned to * the unused pool. * - * If the vertex was already part of the BMesh before the current log + * If the vertex was already part of the #BMesh before the current log * entry, it is added to a map of deleted vertices, with the key being * its ID and the value containing everything needed to reconstruct * that vertex. @@ -152,8 +155,8 @@ void BM_log_face_added(BMLog *log, struct BMFace *f); */ void BM_log_vert_removed(BMLog *log, struct BMVert *v, int cd_vert_mask_offset); -/* Log a face as removed from the BMesh */ -/* Log a face as removed from the BMesh +/** + * Log a face as removed from the #BMesh. * * A couple things can happen here: * @@ -161,43 +164,45 @@ void BM_log_vert_removed(BMLog *log, struct BMVert *v, int cd_vert_mask_offset); * deleted and forgotten about entirely. Its unique ID is returned to * the unused pool. * - * If the face was already part of the BMesh before the current log + * If the face was already part of the #BMesh before the current log * entry, it is added to a map of deleted faces, with the key being * its ID and the value containing everything needed to reconstruct * that face. */ void BM_log_face_removed(BMLog *log, struct BMFace *f); -/* Log all vertices/faces in the BMesh as added */ -/* Log all vertices/faces in the BMesh as added */ +/** + * Log all vertices/faces in the #BMesh as added. + */ void BM_log_all_added(BMesh *bm, BMLog *log); -/* Log all vertices/faces in the BMesh as removed */ -/* Log all vertices/faces in the BMesh as removed */ +/** Log all vertices/faces in the #BMesh as removed. */ void BM_log_before_all_removed(BMesh *bm, BMLog *log); -/* Get the logged coordinates of a vertex */ -/* Get the logged coordinates of a vertex +/** + * Get the logged coordinates of a vertex. * - * Does not modify the log or the vertex */ + * Does not modify the log or the vertex. + */ const float *BM_log_original_vert_co(BMLog *log, BMVert *v); -/* Get the logged normal of a vertex +/** + * Get the logged normal of a vertex * - * Does not modify the log or the vertex */ + * Does not modify the log or the vertex. + */ const float *BM_log_original_vert_no(BMLog *log, BMVert *v); -/* Get the logged mask of a vertex */ -/* Get the logged mask of a vertex +/** Get the logged mask of a vertex * - * Does not modify the log or the vertex */ + * Does not modify the log or the vertex. + */ float BM_log_original_mask(BMLog *log, BMVert *v); -/* Get the logged data of a vertex (avoid multiple lookups) */ +/** Get the logged data of a vertex (avoid multiple lookups). */ void BM_log_original_vert_data(BMLog *log, BMVert *v, const float **r_co, const float **r_no); -/* For internal use only (unit testing) */ -/* For internal use only (unit testing) */ +/** For internal use only (unit testing). */ BMLogEntry *BM_log_current_entry(BMLog *log); -/* For internal use only (unit testing) */ +/** For internal use only (unit testing) */ struct RangeTreeUInt *BM_log_unused_ids(BMLog *log); diff --git a/source/blender/gpu/metal/mtl_texture.hh b/source/blender/gpu/metal/mtl_texture.hh index 9387d5af814..82a7a20a310 100644 --- a/source/blender/gpu/metal/mtl_texture.hh +++ b/source/blender/gpu/metal/mtl_texture.hh @@ -93,7 +93,7 @@ struct TextureReadRoutineSpecialisation { * 0 = Not a Depth format, * 1 = FLOAT DEPTH, * 2 = 24Bit Integer Depth, - * 4 = 32bit uinteger Depth. */ + * 4 = 32bit Unsigned-Integer Depth. */ int depth_format_mode; bool operator==(const TextureReadRoutineSpecialisation &other) const -- cgit v1.2.3 From 47d4ce498e3f5a11a0210b1efd57053f0b1c85bd Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Thu, 14 Jul 2022 22:05:31 +1000 Subject: Cleanup: minor changes to camera frame fitting Use const vars & make order of min/max checks consistent. --- source/blender/blenkernel/intern/camera.c | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/source/blender/blenkernel/intern/camera.c b/source/blender/blenkernel/intern/camera.c index 48a48d6e2b4..7cfac0cb75a 100644 --- a/source/blender/blenkernel/intern/camera.c +++ b/source/blender/blenkernel/intern/camera.c @@ -704,7 +704,7 @@ static bool camera_frame_fit_calc_from_data(CameraParams *params, } if ((!isect_plane_plane_v3( - plane_tx[Y_MAX], plane_tx[Y_MIN], plane_isect_1, plane_isect_1_no)) || + plane_tx[Y_MIN], plane_tx[Y_MAX], plane_isect_1, plane_isect_1_no)) || (!isect_plane_plane_v3( plane_tx[Z_MIN], plane_tx[Z_MAX], plane_isect_2, plane_isect_2_no))) { return false; @@ -713,28 +713,27 @@ static bool camera_frame_fit_calc_from_data(CameraParams *params, add_v3_v3v3(plane_isect_1_other, plane_isect_1, plane_isect_1_no); add_v3_v3v3(plane_isect_2_other, plane_isect_2, plane_isect_2_no); - if (isect_line_line_v3(plane_isect_1, - plane_isect_1_other, - plane_isect_2, - plane_isect_2_other, - plane_isect_pt_1, - plane_isect_pt_2) == 0) { + if (!isect_line_line_v3(plane_isect_1, + plane_isect_1_other, + plane_isect_2, + plane_isect_2_other, + plane_isect_pt_1, + plane_isect_pt_2)) { return false; } float cam_plane_no[3]; float plane_isect_delta[3]; - float plane_isect_delta_len; - float shift_fac = BKE_camera_sensor_size( - params->sensor_fit, params->sensor_x, params->sensor_y) / - params->lens; + const float shift_fac = BKE_camera_sensor_size( + params->sensor_fit, params->sensor_x, params->sensor_y) / + params->lens; /* we want (0, 0, -1) transformed by camera_rotmat, this is a quicker shortcut. */ negate_v3_v3(cam_plane_no, data->camera_rotmat[2]); sub_v3_v3v3(plane_isect_delta, plane_isect_pt_2, plane_isect_pt_1); - plane_isect_delta_len = len_v3(plane_isect_delta); + const float plane_isect_delta_len = len_v3(plane_isect_delta); if (dot_v3v3(plane_isect_delta, cam_plane_no) > 0.0f) { copy_v3_v3(r_co, plane_isect_pt_1); -- cgit v1.2.3 From 4b1d315017ef103f3034160d349b3c3c21a4cd6a Mon Sep 17 00:00:00 2001 From: Michael Jones Date: Wed, 13 Jul 2022 20:56:57 +0100 Subject: Cycles: Improve cache usage on Apple GPUs by chunking active indices This patch partitions the active indices into chunks prior to sorting by material in order to tradeoff some material coherence for better locality. On Apple Silicon GPUs (particularly higher end M1-family GPUs), we observe overall render time speedups of up to 15%. The partitioning is implemented by repeating the range of `shader_sort_key` for each partition, and encoding a "locator" key which distributes the indices into sorted chunks. Reviewed By: brecht Differential Revision: https://developer.blender.org/D15331 --- intern/cycles/device/metal/device_impl.mm | 6 +++++- intern/cycles/device/metal/queue.h | 1 + intern/cycles/device/metal/queue.mm | 21 +++++++++++++++++++-- intern/cycles/device/metal/util.h | 1 + intern/cycles/device/metal/util.mm | 18 ++++++++++++++++++ intern/cycles/device/queue.h | 8 ++++++++ intern/cycles/integrator/path_trace.cpp | 2 +- intern/cycles/integrator/path_trace_work_gpu.cpp | 21 +++++++++++++-------- intern/cycles/integrator/path_trace_work_gpu.h | 3 +++ intern/cycles/kernel/integrator/state.h | 3 +++ intern/cycles/kernel/integrator/state_flow.h | 12 ++++++++++-- 11 files changed, 82 insertions(+), 14 deletions(-) diff --git a/intern/cycles/device/metal/device_impl.mm b/intern/cycles/device/metal/device_impl.mm index 87c83242240..ba9317e3204 100644 --- a/intern/cycles/device/metal/device_impl.mm +++ b/intern/cycles/device/metal/device_impl.mm @@ -217,6 +217,10 @@ string MetalDevice::get_source(const uint kernel_features) build_options += " -D__KERNEL_FEATURES__=" + to_string(kernel_features); } + if (MetalInfo::optimal_sort_partition_elements(mtlDevice) > 0) { + build_options += " -D__KERNEL_SORT_PARTITIONING__ "; + } + if (use_metalrt) { build_options += "-D__METALRT__ "; if (motion_blur) { @@ -652,7 +656,7 @@ void MetalDevice::const_copy_to(const char *name, void *host, size_t size) /* Update data storage pointers in launch parameters. */ if (strcmp(name, "integrator_state") == 0) { /* IntegratorStateGPU is contiguous pointers */ - const size_t pointer_block_size = sizeof(IntegratorStateGPU); + const size_t pointer_block_size = offsetof(IntegratorStateGPU, sort_partition_divisor); update_launch_pointers( offsetof(KernelParamsMetal, integrator_state), host, size, pointer_block_size); } diff --git a/intern/cycles/device/metal/queue.h b/intern/cycles/device/metal/queue.h index b0bd487c86d..836289172f7 100644 --- a/intern/cycles/device/metal/queue.h +++ b/intern/cycles/device/metal/queue.h @@ -24,6 +24,7 @@ class MetalDeviceQueue : public DeviceQueue { virtual int num_concurrent_states(const size_t) const override; virtual int num_concurrent_busy_states() const override; + virtual int num_sort_partitions(const size_t) const override; virtual void init_execution() override; diff --git a/intern/cycles/device/metal/queue.mm b/intern/cycles/device/metal/queue.mm index 03e60b6bb6e..6a9cc552098 100644 --- a/intern/cycles/device/metal/queue.mm +++ b/intern/cycles/device/metal/queue.mm @@ -293,6 +293,23 @@ int MetalDeviceQueue::num_concurrent_busy_states() const return result; } +int MetalDeviceQueue::num_sort_partitions(const size_t state_size) const +{ + /* Sort partitioning becomes less effective when more shaders are in the wavefront. In lieu of a + * more sophisticated heuristic we simply disable sort partitioning if the shader count is high. + */ + if (metal_device_->launch_params.data.max_shaders >= 300) { + return 1; + } + + const int optimal_partition_elements = MetalInfo::optimal_sort_partition_elements( + metal_device_->mtlDevice); + if (optimal_partition_elements) { + return num_concurrent_states(state_size) / optimal_partition_elements; + } + return 1; +} + void MetalDeviceQueue::init_execution() { /* Synchronize all textures and memory copies before executing task. */ @@ -359,7 +376,7 @@ bool MetalDeviceQueue::enqueue(DeviceKernel kernel, /* Prepare any non-pointer (i.e. plain-old-data) KernelParamsMetal data */ /* The plain-old-data is contiguous, continuing to the end of KernelParamsMetal */ size_t plain_old_launch_data_offset = offsetof(KernelParamsMetal, integrator_state) + - sizeof(IntegratorStateGPU); + offsetof(IntegratorStateGPU, sort_partition_divisor); size_t plain_old_launch_data_size = sizeof(KernelParamsMetal) - plain_old_launch_data_offset; memcpy(init_arg_buffer + globals_offsets + plain_old_launch_data_offset, (uint8_t *)&metal_device_->launch_params + plain_old_launch_data_offset, @@ -416,7 +433,7 @@ bool MetalDeviceQueue::enqueue(DeviceKernel kernel, /* this relies on IntegratorStateGPU layout being contiguous device_ptrs */ const size_t pointer_block_end = offsetof(KernelParamsMetal, integrator_state) + - sizeof(IntegratorStateGPU); + offsetof(IntegratorStateGPU, sort_partition_divisor); for (size_t offset = 0; offset < pointer_block_end; offset += sizeof(device_ptr)) { int pointer_index = int(offset / sizeof(device_ptr)); MetalDevice::MetalMem *mmem = *( diff --git a/intern/cycles/device/metal/util.h b/intern/cycles/device/metal/util.h index fd32d8a260f..a988d01d361 100644 --- a/intern/cycles/device/metal/util.h +++ b/intern/cycles/device/metal/util.h @@ -37,6 +37,7 @@ struct MetalInfo { static int get_apple_gpu_core_count(id device); static MetalGPUVendor get_device_vendor(id device); static AppleGPUArchitecture get_apple_gpu_architecture(id device); + static int optimal_sort_partition_elements(id device); static string get_device_name(id device); }; diff --git a/intern/cycles/device/metal/util.mm b/intern/cycles/device/metal/util.mm index a7a5b596b8f..c336dc310c8 100644 --- a/intern/cycles/device/metal/util.mm +++ b/intern/cycles/device/metal/util.mm @@ -72,6 +72,24 @@ MetalGPUVendor MetalInfo::get_device_vendor(id device) return METAL_GPU_UNKNOWN; } +int MetalInfo::optimal_sort_partition_elements(id device) +{ + if (auto str = getenv("CYCLES_METAL_SORT_PARTITION_ELEMENTS")) { + return atoi(str); + } + + /* On M1 and M2 GPUs, we see better cache utilization if we partition the active indices before + * sorting each partition by material. Partitioning into chunks of 65536 elements results in an + * overall render time speedup of up to 15%. */ + if (get_device_vendor(device) == METAL_GPU_APPLE) { + AppleGPUArchitecture arch = get_apple_gpu_architecture(device); + if (arch == APPLE_M1 || arch == APPLE_M2) { + return 65536; + } + } + return 0; +} + vector> const &MetalInfo::get_usable_devices() { static vector> usable_devices; diff --git a/intern/cycles/device/queue.h b/intern/cycles/device/queue.h index 14a5db3a204..20308e4a106 100644 --- a/intern/cycles/device/queue.h +++ b/intern/cycles/device/queue.h @@ -105,6 +105,14 @@ class DeviceQueue { * value. */ virtual int num_concurrent_busy_states() const = 0; + /* Number of partitions within which active indices are sorted by material ID. + * Using more partitions lets us trade off material coherence for better integrator state fetch + * locality. */ + virtual int num_sort_partitions(const size_t /*state_size*/) const + { + return 1; + } + /* Initialize execution of kernels on this queue. * * Will, for example, load all data required by the kernels from Device to global or path state. diff --git a/intern/cycles/integrator/path_trace.cpp b/intern/cycles/integrator/path_trace.cpp index 6912bf928cd..ed278821b46 100644 --- a/intern/cycles/integrator/path_trace.cpp +++ b/intern/cycles/integrator/path_trace.cpp @@ -373,7 +373,7 @@ void PathTrace::path_trace(RenderWork &render_work) work_balance_infos_[i].time_spent += work_time; work_balance_infos_[i].occupancy = statistics.occupancy; - VLOG_WORK << "Rendered " << num_samples << " samples in " << work_time << " seconds (" + VLOG_INFO << "Rendered " << num_samples << " samples in " << work_time << " seconds (" << work_time / num_samples << " seconds per sample), occupancy: " << statistics.occupancy; }); diff --git a/intern/cycles/integrator/path_trace_work_gpu.cpp b/intern/cycles/integrator/path_trace_work_gpu.cpp index e262c252ce3..d51e8a28bb4 100644 --- a/intern/cycles/integrator/path_trace_work_gpu.cpp +++ b/intern/cycles/integrator/path_trace_work_gpu.cpp @@ -182,18 +182,19 @@ void PathTraceWorkGPU::alloc_integrator_queue() void PathTraceWorkGPU::alloc_integrator_sorting() { /* Allocate arrays for shader sorting. */ - const int max_shaders = device_scene_->data.max_shaders; - if (integrator_shader_sort_counter_.size() < max_shaders) { - integrator_shader_sort_counter_.alloc(max_shaders); + num_sort_partitions_ = queue_->num_sort_partitions(estimate_single_state_size()); + const int sort_buckets = device_scene_->data.max_shaders * num_sort_partitions_; + if (integrator_shader_sort_counter_.size() < sort_buckets) { + integrator_shader_sort_counter_.alloc(sort_buckets); integrator_shader_sort_counter_.zero_to_device(); - integrator_shader_raytrace_sort_counter_.alloc(max_shaders); + integrator_shader_raytrace_sort_counter_.alloc(sort_buckets); integrator_shader_raytrace_sort_counter_.zero_to_device(); - integrator_shader_mnee_sort_counter_.alloc(max_shaders); + integrator_shader_mnee_sort_counter_.alloc(sort_buckets); integrator_shader_mnee_sort_counter_.zero_to_device(); - integrator_shader_sort_prefix_sum_.alloc(max_shaders); + integrator_shader_sort_prefix_sum_.alloc(sort_buckets); integrator_shader_sort_prefix_sum_.zero_to_device(); integrator_state_gpu_.sort_key_counter[DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE] = @@ -237,6 +238,10 @@ void PathTraceWorkGPU::init_execution() { queue_->init_execution(); + /* Setup sort partitioning divisor for better cache utilization. */ + integrator_state_gpu_.sort_partition_divisor = (int)divide_up(max_num_paths_, + num_sort_partitions_); + /* Copy to device side struct in constant memory. */ device_->const_copy_to( "integrator_state", &integrator_state_gpu_, sizeof(integrator_state_gpu_)); @@ -486,9 +491,9 @@ void PathTraceWorkGPU::compute_sorted_queued_paths(DeviceKernel kernel, /* Compute prefix sum of number of active paths with each shader. */ { const int work_size = 1; - int max_shaders = device_scene_->data.max_shaders; + int sort_buckets = device_scene_->data.max_shaders * num_sort_partitions_; - DeviceKernelArguments args(&d_counter, &d_prefix_sum, &max_shaders); + DeviceKernelArguments args(&d_counter, &d_prefix_sum, &sort_buckets); queue_->enqueue(DEVICE_KERNEL_PREFIX_SUM, work_size, args); } diff --git a/intern/cycles/integrator/path_trace_work_gpu.h b/intern/cycles/integrator/path_trace_work_gpu.h index 4c10a221a30..a805258d1b5 100644 --- a/intern/cycles/integrator/path_trace_work_gpu.h +++ b/intern/cycles/integrator/path_trace_work_gpu.h @@ -156,6 +156,9 @@ class PathTraceWorkGPU : public PathTraceWork { bool interop_use_checked_ = false; bool interop_use_ = false; + /* Number of partitions to sort state indices into prior to material sort. */ + int num_sort_partitions_; + /* Maximum number of concurrent integrator states. */ int max_num_paths_; diff --git a/intern/cycles/kernel/integrator/state.h b/intern/cycles/kernel/integrator/state.h index d6fef27f344..d10d31e930e 100644 --- a/intern/cycles/kernel/integrator/state.h +++ b/intern/cycles/kernel/integrator/state.h @@ -127,6 +127,9 @@ typedef struct IntegratorStateGPU { /* Index of main path which will be used by a next shadow catcher split. */ ccl_global int *next_main_path_index; + + /* Divisor used to partition active indices by locality when sorting by material. */ + uint sort_partition_divisor; } IntegratorStateGPU; /* Abstraction diff --git a/intern/cycles/kernel/integrator/state_flow.h b/intern/cycles/kernel/integrator/state_flow.h index fed74d49434..d397ef385e7 100644 --- a/intern/cycles/kernel/integrator/state_flow.h +++ b/intern/cycles/kernel/integrator/state_flow.h @@ -67,9 +67,17 @@ CCL_NAMESPACE_BEGIN &kernel_integrator_state.queue_counter->num_queued[current_kernel], 1); \ INTEGRATOR_STATE_WRITE(state, shadow_path, queued_kernel) = 0; +# ifdef __KERNEL_SORT_PARTITIONING__ +/* Sort first by truncated state index (for good locality), then by key (for good coherence). */ +# define INTEGRATOR_SORT_KEY(key, state) \ + (key + kernel_data.max_shaders * (state / kernel_integrator_state.sort_partition_divisor)) +# else +# define INTEGRATOR_SORT_KEY(key, state) (key) +# endif + # define INTEGRATOR_PATH_INIT_SORTED(next_kernel, key) \ { \ - const int key_ = key; \ + const int key_ = INTEGRATOR_SORT_KEY(key, state); \ atomic_fetch_and_add_uint32( \ &kernel_integrator_state.queue_counter->num_queued[next_kernel], 1); \ INTEGRATOR_STATE_WRITE(state, path, queued_kernel) = next_kernel; \ @@ -79,7 +87,7 @@ CCL_NAMESPACE_BEGIN } # define INTEGRATOR_PATH_NEXT_SORTED(current_kernel, next_kernel, key) \ { \ - const int key_ = key; \ + const int key_ = INTEGRATOR_SORT_KEY(key, state); \ atomic_fetch_and_sub_uint32( \ &kernel_integrator_state.queue_counter->num_queued[current_kernel], 1); \ atomic_fetch_and_add_uint32( \ -- cgit v1.2.3 From 02ce29c6ee82a22d1fce126188b8b9db1a221d4b Mon Sep 17 00:00:00 2001 From: Gaia Clary Date: Sun, 10 Jul 2022 10:35:05 +0200 Subject: Improve Tool tip for Add-on search Differential Revision: https://developer.blender.org/D15411 --- release/scripts/startup/bl_ui/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/release/scripts/startup/bl_ui/__init__.py b/release/scripts/startup/bl_ui/__init__.py index c61dde87d7c..c4e3df469b7 100644 --- a/release/scripts/startup/bl_ui/__init__.py +++ b/release/scripts/startup/bl_ui/__init__.py @@ -128,8 +128,8 @@ def register(): return items WindowManager.addon_search = StringProperty( - name="Search", - description="Search within the selected filter", + name="Filter", + description="Filter by add-on name, author & category", options={'TEXTEDIT_UPDATE'}, ) WindowManager.addon_filter = EnumProperty( -- cgit v1.2.3 From 5539fb3121313f91b6e46d982ef62ff97763d2c2 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Wed, 13 Jul 2022 15:23:50 +0200 Subject: Cycles: add presets to the Performance panel With choices Default, Lower Memory and Faster Render. For convenience, and to help communicate what the various settings do. Differential Revision: https://developer.blender.org/D15446 --- intern/cycles/blender/addon/presets.py | 26 ++++++++++++++++++++++ intern/cycles/blender/addon/ui.py | 10 +++++++++ .../scripts/presets/cycles/performance/Default.py | 12 ++++++++++ .../presets/cycles/performance/Faster_Render.py | 12 ++++++++++ .../presets/cycles/performance/Lower_Memory.py | 12 ++++++++++ 5 files changed, 72 insertions(+) create mode 100644 release/scripts/presets/cycles/performance/Default.py create mode 100644 release/scripts/presets/cycles/performance/Faster_Render.py create mode 100644 release/scripts/presets/cycles/performance/Lower_Memory.py diff --git a/intern/cycles/blender/addon/presets.py b/intern/cycles/blender/addon/presets.py index cc6d574da99..e1f08c07eaf 100644 --- a/intern/cycles/blender/addon/presets.py +++ b/intern/cycles/blender/addon/presets.py @@ -84,10 +84,36 @@ class AddPresetViewportSampling(AddPresetBase, Operator): preset_subdir = "cycles/viewport_sampling" +class AddPresetPerformance(AddPresetBase, Operator): + '''Add an Performance Preset''' + bl_idname = "render.cycles_performance_preset_add" + bl_label = "Add Performance Preset" + preset_menu = "CYCLES_PT_performance_presets" + + preset_defines = [ + "render = bpy.context.scene.render" + "cycles = bpy.context.scene.cycles" + ] + + preset_values = [ + "render.threads_mode", + "render.use_persistent_data", + "cycles.debug_use_spatial_splits", + "cycles.debug_use_compact_bvh", + "cycles.debug_use_hair_bvh", + "cycles.debug_bvh_time_steps", + "cycles.use_auto_tile", + "cycles.tile_size", + ] + + preset_subdir = "cycles/performance" + + classes = ( AddPresetIntegrator, AddPresetSampling, AddPresetViewportSampling, + AddPresetPerformance, ) diff --git a/intern/cycles/blender/addon/ui.py b/intern/cycles/blender/addon/ui.py index 09aecb5cb81..3c898d4be73 100644 --- a/intern/cycles/blender/addon/ui.py +++ b/intern/cycles/blender/addon/ui.py @@ -43,6 +43,12 @@ class CYCLES_PT_integrator_presets(CyclesPresetPanel): preset_add_operator = "render.cycles_integrator_preset_add" +class CYCLES_PT_performance_presets(CyclesPresetPanel): + bl_label = "Performance Presets" + preset_subdir = "cycles/performance" + preset_add_operator = "render.cycles_performance_preset_add" + + class CyclesButtonsPanel: bl_space_type = "PROPERTIES" bl_region_type = "WINDOW" @@ -624,6 +630,9 @@ class CYCLES_RENDER_PT_performance(CyclesButtonsPanel, Panel): bl_label = "Performance" bl_options = {'DEFAULT_CLOSED'} + def draw_header_preset(self, context): + CYCLES_PT_performance_presets.draw_panel_header(self.layout) + def draw(self, context): pass @@ -2269,6 +2278,7 @@ classes = ( CYCLES_PT_sampling_presets, CYCLES_PT_viewport_sampling_presets, CYCLES_PT_integrator_presets, + CYCLES_PT_performance_presets, CYCLES_RENDER_PT_sampling, CYCLES_RENDER_PT_sampling_viewport, CYCLES_RENDER_PT_sampling_viewport_denoise, diff --git a/release/scripts/presets/cycles/performance/Default.py b/release/scripts/presets/cycles/performance/Default.py new file mode 100644 index 00000000000..5c25f23eca0 --- /dev/null +++ b/release/scripts/presets/cycles/performance/Default.py @@ -0,0 +1,12 @@ +import bpy +render = bpy.context.scene.render +cycles = bpy.context.scene.cycles + +render.threads_mode = 'AUTO' +render.use_persistent_data = False +cycles.debug_use_spatial_splits = False +cycles.debug_use_compact_bvh = False +cycles.debug_use_hair_bvh = True +cycles.debug_bvh_time_steps = 0 +cycles.use_auto_tile = True +cycles.tile_size = 2048 diff --git a/release/scripts/presets/cycles/performance/Faster_Render.py b/release/scripts/presets/cycles/performance/Faster_Render.py new file mode 100644 index 00000000000..7f1e3c68f1f --- /dev/null +++ b/release/scripts/presets/cycles/performance/Faster_Render.py @@ -0,0 +1,12 @@ +import bpy +render = bpy.context.scene.render +cycles = bpy.context.scene.cycles + +render.threads_mode = 'AUTO' +render.use_persistent_data = True +cycles.debug_use_spatial_splits = True +cycles.debug_use_compact_bvh = False +cycles.debug_use_hair_bvh = True +cycles.debug_bvh_time_steps = 2 +cycles.use_auto_tile = True +cycles.tile_size = 2048 diff --git a/release/scripts/presets/cycles/performance/Lower_Memory.py b/release/scripts/presets/cycles/performance/Lower_Memory.py new file mode 100644 index 00000000000..d1a45f1888d --- /dev/null +++ b/release/scripts/presets/cycles/performance/Lower_Memory.py @@ -0,0 +1,12 @@ +import bpy +render = bpy.context.scene.render +cycles = bpy.context.scene.cycles + +render.threads_mode = 'AUTO' +render.use_persistent_data = False +cycles.debug_use_spatial_splits = False +cycles.debug_use_compact_bvh = True +cycles.debug_use_hair_bvh = True +cycles.debug_bvh_time_steps = 0 +cycles.use_auto_tile = True +cycles.tile_size = 512 -- cgit v1.2.3 From 28c3739a9bc055b3a3e5ba2f5cfc219f70d64dfa Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Wed, 13 Jul 2022 14:22:42 +0200 Subject: Cleanup: replace state flow macros in the kernel with functions --- intern/cycles/kernel/integrator/init_from_bake.h | 8 +- intern/cycles/kernel/integrator/init_from_camera.h | 4 +- .../cycles/kernel/integrator/intersect_closest.h | 71 +++--- intern/cycles/kernel/integrator/intersect_shadow.h | 4 +- .../kernel/integrator/intersect_subsurface.h | 2 +- .../kernel/integrator/intersect_volume_stack.h | 2 +- intern/cycles/kernel/integrator/shade_background.h | 2 +- intern/cycles/kernel/integrator/shade_light.h | 4 +- intern/cycles/kernel/integrator/shade_shadow.h | 6 +- intern/cycles/kernel/integrator/shade_surface.h | 16 +- intern/cycles/kernel/integrator/shade_volume.h | 10 +- intern/cycles/kernel/integrator/shadow_catcher.h | 2 +- intern/cycles/kernel/integrator/state_flow.h | 267 +++++++++++++-------- intern/cycles/kernel/integrator/subsurface.h | 6 +- 14 files changed, 239 insertions(+), 165 deletions(-) diff --git a/intern/cycles/kernel/integrator/init_from_bake.h b/intern/cycles/kernel/integrator/init_from_bake.h index c63684d58e6..4d75dcea190 100644 --- a/intern/cycles/kernel/integrator/init_from_bake.h +++ b/intern/cycles/kernel/integrator/init_from_bake.h @@ -181,7 +181,7 @@ ccl_device bool integrator_init_from_bake(KernelGlobals kg, integrator_state_write_ray(kg, state, &ray); /* Setup next kernel to execute. */ - INTEGRATOR_PATH_INIT(DEVICE_KERNEL_INTEGRATOR_SHADE_BACKGROUND); + integrator_path_init(kg, state, DEVICE_KERNEL_INTEGRATOR_SHADE_BACKGROUND); } else { /* Surface baking. */ @@ -247,13 +247,13 @@ ccl_device bool integrator_init_from_bake(KernelGlobals kg, const bool use_raytrace_kernel = (shader_flags & SD_HAS_RAYTRACE); if (use_caustics) { - INTEGRATOR_PATH_INIT_SORTED(DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE_MNEE, shader_index); + integrator_path_init_sorted(kg, state, DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE_MNEE, shader_index); } else if (use_raytrace_kernel) { - INTEGRATOR_PATH_INIT_SORTED(DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE_RAYTRACE, shader_index); + integrator_path_init_sorted(kg, state, DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE_RAYTRACE, shader_index); } else { - INTEGRATOR_PATH_INIT_SORTED(DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE, shader_index); + integrator_path_init_sorted(kg, state, DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE, shader_index); } } diff --git a/intern/cycles/kernel/integrator/init_from_camera.h b/intern/cycles/kernel/integrator/init_from_camera.h index 9fe27cdda9a..73db13be697 100644 --- a/intern/cycles/kernel/integrator/init_from_camera.h +++ b/intern/cycles/kernel/integrator/init_from_camera.h @@ -100,10 +100,10 @@ ccl_device bool integrator_init_from_camera(KernelGlobals kg, /* Continue with intersect_closest kernel, optionally initializing volume * stack before that if the camera may be inside a volume. */ if (kernel_data.cam.is_inside_volume) { - INTEGRATOR_PATH_INIT(DEVICE_KERNEL_INTEGRATOR_INTERSECT_VOLUME_STACK); + integrator_path_init(kg, state, DEVICE_KERNEL_INTEGRATOR_INTERSECT_VOLUME_STACK); } else { - INTEGRATOR_PATH_INIT(DEVICE_KERNEL_INTEGRATOR_INTERSECT_CLOSEST); + integrator_path_init(kg, state, DEVICE_KERNEL_INTEGRATOR_INTERSECT_CLOSEST); } return true; diff --git a/intern/cycles/kernel/integrator/intersect_closest.h b/intern/cycles/kernel/integrator/intersect_closest.h index 621aa05f46b..923dab9591a 100644 --- a/intern/cycles/kernel/integrator/intersect_closest.h +++ b/intern/cycles/kernel/integrator/intersect_closest.h @@ -109,14 +109,14 @@ ccl_device_forceinline void integrator_split_shadow_catcher( /* If using background pass, schedule background shading kernel so that we have a background * to alpha-over on. The background kernel will then continue the path afterwards. */ INTEGRATOR_STATE_WRITE(state, path, flag) |= PATH_RAY_SHADOW_CATCHER_BACKGROUND; - INTEGRATOR_PATH_INIT(DEVICE_KERNEL_INTEGRATOR_SHADE_BACKGROUND); + integrator_path_init(kg, state, DEVICE_KERNEL_INTEGRATOR_SHADE_BACKGROUND); return; } if (!integrator_state_volume_stack_is_empty(kg, state)) { /* Volume stack is not empty. Re-init the volume stack to exclude any non-shadow catcher * objects from it, and then continue shading volume and shadow catcher surface after. */ - INTEGRATOR_PATH_INIT(DEVICE_KERNEL_INTEGRATOR_INTERSECT_VOLUME_STACK); + integrator_path_init(kg, state, DEVICE_KERNEL_INTEGRATOR_INTERSECT_VOLUME_STACK); return; } @@ -128,18 +128,19 @@ ccl_device_forceinline void integrator_split_shadow_catcher( const bool use_raytrace_kernel = (flags & SD_HAS_RAYTRACE); if (use_caustics) { - INTEGRATOR_PATH_INIT_SORTED(DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE_MNEE, shader); + integrator_path_init_sorted(kg, state, DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE_MNEE, shader); } else if (use_raytrace_kernel) { - INTEGRATOR_PATH_INIT_SORTED(DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE_RAYTRACE, shader); + integrator_path_init_sorted( + kg, state, DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE_RAYTRACE, shader); } else { - INTEGRATOR_PATH_INIT_SORTED(DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE, shader); + integrator_path_init_sorted(kg, state, DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE, shader); } } /* Schedule next kernel to be executed after updating volume stack for shadow catcher. */ -template +template ccl_device_forceinline void integrator_intersect_next_kernel_after_shadow_catcher_volume( KernelGlobals kg, IntegratorState state) { @@ -156,20 +157,21 @@ ccl_device_forceinline void integrator_intersect_next_kernel_after_shadow_catche const bool use_raytrace_kernel = (flags & SD_HAS_RAYTRACE); if (use_caustics) { - INTEGRATOR_PATH_NEXT_SORTED( - current_kernel, DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE_MNEE, shader); + integrator_path_next_sorted( + kg, state, current_kernel, DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE_MNEE, shader); } else if (use_raytrace_kernel) { - INTEGRATOR_PATH_NEXT_SORTED( - current_kernel, DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE_RAYTRACE, shader); + integrator_path_next_sorted( + kg, state, current_kernel, DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE_RAYTRACE, shader); } else { - INTEGRATOR_PATH_NEXT_SORTED(current_kernel, DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE, shader); + integrator_path_next_sorted( + kg, state, current_kernel, DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE, shader); } } /* Schedule next kernel to be executed after executing background shader for shadow catcher. */ -template +template ccl_device_forceinline void integrator_intersect_next_kernel_after_shadow_catcher_background( KernelGlobals kg, IntegratorState state) { @@ -177,7 +179,8 @@ ccl_device_forceinline void integrator_intersect_next_kernel_after_shadow_catche if (!integrator_state_volume_stack_is_empty(kg, state)) { /* Volume stack is not empty. Re-init the volume stack to exclude any non-shadow catcher * objects from it, and then continue shading volume and shadow catcher surface after. */ - INTEGRATOR_PATH_NEXT(current_kernel, DEVICE_KERNEL_INTEGRATOR_INTERSECT_VOLUME_STACK); + integrator_path_next( + kg, state, current_kernel, DEVICE_KERNEL_INTEGRATOR_INTERSECT_VOLUME_STACK); return; } @@ -190,7 +193,7 @@ ccl_device_forceinline void integrator_intersect_next_kernel_after_shadow_catche * * Note that current_kernel is a template value since making this a variable * leads to poor performance with CUDA atomics. */ -template +template ccl_device_forceinline void integrator_intersect_next_kernel( KernelGlobals kg, IntegratorState state, @@ -206,10 +209,10 @@ ccl_device_forceinline void integrator_intersect_next_kernel( const int flags = (hit_surface) ? kernel_data_fetch(shaders, shader).flags : 0; if (!integrator_intersect_terminate(kg, state, flags)) { - INTEGRATOR_PATH_NEXT(current_kernel, DEVICE_KERNEL_INTEGRATOR_SHADE_VOLUME); + integrator_path_next(kg, state, current_kernel, DEVICE_KERNEL_INTEGRATOR_SHADE_VOLUME); } else { - INTEGRATOR_PATH_TERMINATE(current_kernel); + integrator_path_terminate(kg, state, current_kernel); } return; } @@ -218,7 +221,7 @@ ccl_device_forceinline void integrator_intersect_next_kernel( if (hit) { /* Hit a surface, continue with light or surface kernel. */ if (isect->type & PRIMITIVE_LAMP) { - INTEGRATOR_PATH_NEXT(current_kernel, DEVICE_KERNEL_INTEGRATOR_SHADE_LIGHT); + integrator_path_next(kg, state, current_kernel, DEVICE_KERNEL_INTEGRATOR_SHADE_LIGHT); } else { /* Hit a surface, continue with surface kernel unless terminated. */ @@ -231,16 +234,16 @@ ccl_device_forceinline void integrator_intersect_next_kernel( (object_flags & SD_OBJECT_CAUSTICS); const bool use_raytrace_kernel = (flags & SD_HAS_RAYTRACE); if (use_caustics) { - INTEGRATOR_PATH_NEXT_SORTED( - current_kernel, DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE_MNEE, shader); + integrator_path_next_sorted( + kg, state, current_kernel, DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE_MNEE, shader); } else if (use_raytrace_kernel) { - INTEGRATOR_PATH_NEXT_SORTED( - current_kernel, DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE_RAYTRACE, shader); + integrator_path_next_sorted( + kg, state, current_kernel, DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE_RAYTRACE, shader); } else { - INTEGRATOR_PATH_NEXT_SORTED( - current_kernel, DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE, shader); + integrator_path_next_sorted( + kg, state, current_kernel, DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE, shader); } #ifdef __SHADOW_CATCHER__ @@ -249,13 +252,13 @@ ccl_device_forceinline void integrator_intersect_next_kernel( #endif } else { - INTEGRATOR_PATH_TERMINATE(current_kernel); + integrator_path_terminate(kg, state, current_kernel); } } } else { /* Nothing hit, continue with background kernel. */ - INTEGRATOR_PATH_NEXT(current_kernel, DEVICE_KERNEL_INTEGRATOR_SHADE_BACKGROUND); + integrator_path_next(kg, state, current_kernel, DEVICE_KERNEL_INTEGRATOR_SHADE_BACKGROUND); } } @@ -263,7 +266,7 @@ ccl_device_forceinline void integrator_intersect_next_kernel( * * The logic here matches integrator_intersect_next_kernel, except that * volume shading and termination testing have already been done. */ -template +template ccl_device_forceinline void integrator_intersect_next_kernel_after_volume( KernelGlobals kg, IntegratorState state, @@ -273,7 +276,7 @@ ccl_device_forceinline void integrator_intersect_next_kernel_after_volume( if (isect->prim != PRIM_NONE) { /* Hit a surface, continue with light or surface kernel. */ if (isect->type & PRIMITIVE_LAMP) { - INTEGRATOR_PATH_NEXT(current_kernel, DEVICE_KERNEL_INTEGRATOR_SHADE_LIGHT); + integrator_path_next(kg, state, current_kernel, DEVICE_KERNEL_INTEGRATOR_SHADE_LIGHT); return; } else { @@ -286,16 +289,16 @@ ccl_device_forceinline void integrator_intersect_next_kernel_after_volume( const bool use_raytrace_kernel = (flags & SD_HAS_RAYTRACE); if (use_caustics) { - INTEGRATOR_PATH_NEXT_SORTED( - current_kernel, DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE_MNEE, shader); + integrator_path_next_sorted( + kg, state, current_kernel, DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE_MNEE, shader); } else if (use_raytrace_kernel) { - INTEGRATOR_PATH_NEXT_SORTED( - current_kernel, DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE_RAYTRACE, shader); + integrator_path_next_sorted( + kg, state, current_kernel, DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE_RAYTRACE, shader); } else { - INTEGRATOR_PATH_NEXT_SORTED( - current_kernel, DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE, shader); + integrator_path_next_sorted( + kg, state, current_kernel, DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE, shader); } #ifdef __SHADOW_CATCHER__ @@ -307,7 +310,7 @@ ccl_device_forceinline void integrator_intersect_next_kernel_after_volume( } else { /* Nothing hit, continue with background kernel. */ - INTEGRATOR_PATH_NEXT(current_kernel, DEVICE_KERNEL_INTEGRATOR_SHADE_BACKGROUND); + integrator_path_next(kg, state, current_kernel, DEVICE_KERNEL_INTEGRATOR_SHADE_BACKGROUND); return; } } diff --git a/intern/cycles/kernel/integrator/intersect_shadow.h b/intern/cycles/kernel/integrator/intersect_shadow.h index 3e746998225..adcbfa19afe 100644 --- a/intern/cycles/kernel/integrator/intersect_shadow.h +++ b/intern/cycles/kernel/integrator/intersect_shadow.h @@ -162,7 +162,7 @@ ccl_device void integrator_intersect_shadow(KernelGlobals kg, IntegratorShadowSt if (opaque_hit) { /* Hit an opaque surface, shadow path ends here. */ - INTEGRATOR_SHADOW_PATH_TERMINATE(DEVICE_KERNEL_INTEGRATOR_INTERSECT_SHADOW); + integrator_shadow_path_terminate(kg, state, DEVICE_KERNEL_INTEGRATOR_INTERSECT_SHADOW); return; } else { @@ -171,7 +171,7 @@ ccl_device void integrator_intersect_shadow(KernelGlobals kg, IntegratorShadowSt * * TODO: could also write to render buffer directly if no transparent shadows? * Could save a kernel execution for the common case. */ - INTEGRATOR_SHADOW_PATH_NEXT(DEVICE_KERNEL_INTEGRATOR_INTERSECT_SHADOW, + integrator_shadow_path_next(kg, state, DEVICE_KERNEL_INTEGRATOR_INTERSECT_SHADOW, DEVICE_KERNEL_INTEGRATOR_SHADE_SHADOW); return; } diff --git a/intern/cycles/kernel/integrator/intersect_subsurface.h b/intern/cycles/kernel/integrator/intersect_subsurface.h index 0a2c4ad680d..f439d6905a0 100644 --- a/intern/cycles/kernel/integrator/intersect_subsurface.h +++ b/intern/cycles/kernel/integrator/intersect_subsurface.h @@ -17,7 +17,7 @@ ccl_device void integrator_intersect_subsurface(KernelGlobals kg, IntegratorStat } #endif - INTEGRATOR_PATH_TERMINATE(DEVICE_KERNEL_INTEGRATOR_INTERSECT_SUBSURFACE); + integrator_path_terminate(kg, state, DEVICE_KERNEL_INTEGRATOR_INTERSECT_SUBSURFACE); } CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/integrator/intersect_volume_stack.h b/intern/cycles/kernel/integrator/intersect_volume_stack.h index 49ef01dc870..68c7fdd5909 100644 --- a/intern/cycles/kernel/integrator/intersect_volume_stack.h +++ b/intern/cycles/kernel/integrator/intersect_volume_stack.h @@ -222,7 +222,7 @@ ccl_device void integrator_intersect_volume_stack(KernelGlobals kg, IntegratorSt } else { /* Volume stack init for camera rays, continue with intersection of camera ray. */ - INTEGRATOR_PATH_NEXT(DEVICE_KERNEL_INTEGRATOR_INTERSECT_VOLUME_STACK, + integrator_path_next(kg, state, DEVICE_KERNEL_INTEGRATOR_INTERSECT_VOLUME_STACK, DEVICE_KERNEL_INTEGRATOR_INTERSECT_CLOSEST); } } diff --git a/intern/cycles/kernel/integrator/shade_background.h b/intern/cycles/kernel/integrator/shade_background.h index 4791a963ae6..47233634463 100644 --- a/intern/cycles/kernel/integrator/shade_background.h +++ b/intern/cycles/kernel/integrator/shade_background.h @@ -213,7 +213,7 @@ ccl_device void integrator_shade_background(KernelGlobals kg, } #endif - INTEGRATOR_PATH_TERMINATE(DEVICE_KERNEL_INTEGRATOR_SHADE_BACKGROUND); + integrator_path_terminate(kg, state, DEVICE_KERNEL_INTEGRATOR_SHADE_BACKGROUND); } CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/integrator/shade_light.h b/intern/cycles/kernel/integrator/shade_light.h index be926c78439..7b3e9e0ee7e 100644 --- a/intern/cycles/kernel/integrator/shade_light.h +++ b/intern/cycles/kernel/integrator/shade_light.h @@ -99,11 +99,11 @@ ccl_device void integrator_shade_light(KernelGlobals kg, INTEGRATOR_STATE_WRITE(state, path, transparent_bounce) = transparent_bounce; if (transparent_bounce >= kernel_data.integrator.transparent_max_bounce) { - INTEGRATOR_PATH_TERMINATE(DEVICE_KERNEL_INTEGRATOR_SHADE_LIGHT); + integrator_path_terminate(kg, state, DEVICE_KERNEL_INTEGRATOR_SHADE_LIGHT); return; } else { - INTEGRATOR_PATH_NEXT(DEVICE_KERNEL_INTEGRATOR_SHADE_LIGHT, + integrator_path_next(kg, state, DEVICE_KERNEL_INTEGRATOR_SHADE_LIGHT, DEVICE_KERNEL_INTEGRATOR_INTERSECT_CLOSEST); return; } diff --git a/intern/cycles/kernel/integrator/shade_shadow.h b/intern/cycles/kernel/integrator/shade_shadow.h index 2b929b7b62e..afe77590e9a 100644 --- a/intern/cycles/kernel/integrator/shade_shadow.h +++ b/intern/cycles/kernel/integrator/shade_shadow.h @@ -158,20 +158,20 @@ ccl_device void integrator_shade_shadow(KernelGlobals kg, /* Evaluate transparent shadows. */ const bool opaque = integrate_transparent_shadow(kg, state, num_hits); if (opaque) { - INTEGRATOR_SHADOW_PATH_TERMINATE(DEVICE_KERNEL_INTEGRATOR_SHADE_SHADOW); + integrator_shadow_path_terminate(kg, state, DEVICE_KERNEL_INTEGRATOR_SHADE_SHADOW); return; } #endif if (shadow_intersections_has_remaining(num_hits)) { /* More intersections to find, continue shadow ray. */ - INTEGRATOR_SHADOW_PATH_NEXT(DEVICE_KERNEL_INTEGRATOR_SHADE_SHADOW, + integrator_shadow_path_next(kg, state, DEVICE_KERNEL_INTEGRATOR_SHADE_SHADOW, DEVICE_KERNEL_INTEGRATOR_INTERSECT_SHADOW); return; } else { kernel_accum_light(kg, state, render_buffer); - INTEGRATOR_SHADOW_PATH_TERMINATE(DEVICE_KERNEL_INTEGRATOR_SHADE_SHADOW); + integrator_shadow_path_terminate(kg, state, DEVICE_KERNEL_INTEGRATOR_SHADE_SHADOW); return; } } diff --git a/intern/cycles/kernel/integrator/shade_surface.h b/intern/cycles/kernel/integrator/shade_surface.h index 57b88b806a4..91e34968148 100644 --- a/intern/cycles/kernel/integrator/shade_surface.h +++ b/intern/cycles/kernel/integrator/shade_surface.h @@ -190,8 +190,8 @@ ccl_device_forceinline void integrate_surface_direct_light(KernelGlobals kg, const bool is_light = light_sample_is_light(&ls); /* Branch off shadow kernel. */ - INTEGRATOR_SHADOW_PATH_INIT( - shadow_state, state, DEVICE_KERNEL_INTEGRATOR_INTERSECT_SHADOW, shadow); + IntegratorShadowState shadow_state = integrator_shadow_path_init( + kg, state, DEVICE_KERNEL_INTEGRATOR_INTERSECT_SHADOW, false); /* Copy volume stack and enter/exit volume. */ integrator_state_copy_volume_stack_to_shadow(kg, shadow_state, state); @@ -442,7 +442,8 @@ ccl_device_forceinline void integrate_surface_ao(KernelGlobals kg, ray.dD = differential_zero_compact(); /* Branch off shadow kernel. */ - INTEGRATOR_SHADOW_PATH_INIT(shadow_state, state, DEVICE_KERNEL_INTEGRATOR_INTERSECT_SHADOW, ao); + IntegratorShadowState shadow_state = integrator_shadow_path_init( + kg, state, DEVICE_KERNEL_INTEGRATOR_INTERSECT_SHADOW, true); /* Copy volume stack and enter/exit volume. */ integrator_state_copy_volume_stack_to_shadow(kg, shadow_state, state); @@ -604,22 +605,23 @@ ccl_device bool integrate_surface(KernelGlobals kg, } template + DeviceKernel current_kernel = DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE> ccl_device_forceinline void integrator_shade_surface(KernelGlobals kg, IntegratorState state, ccl_global float *ccl_restrict render_buffer) { if (integrate_surface(kg, state, render_buffer)) { if (INTEGRATOR_STATE(state, path, flag) & PATH_RAY_SUBSURFACE) { - INTEGRATOR_PATH_NEXT(current_kernel, DEVICE_KERNEL_INTEGRATOR_INTERSECT_SUBSURFACE); + integrator_path_next( + kg, state, current_kernel, DEVICE_KERNEL_INTEGRATOR_INTERSECT_SUBSURFACE); } else { kernel_assert(INTEGRATOR_STATE(state, ray, t) != 0.0f); - INTEGRATOR_PATH_NEXT(current_kernel, DEVICE_KERNEL_INTEGRATOR_INTERSECT_CLOSEST); + integrator_path_next(kg, state, current_kernel, DEVICE_KERNEL_INTEGRATOR_INTERSECT_CLOSEST); } } else { - INTEGRATOR_PATH_TERMINATE(current_kernel); + integrator_path_terminate(kg, state, current_kernel); } } diff --git a/intern/cycles/kernel/integrator/shade_volume.h b/intern/cycles/kernel/integrator/shade_volume.h index 6cf80f4ddc5..683c031f0d9 100644 --- a/intern/cycles/kernel/integrator/shade_volume.h +++ b/intern/cycles/kernel/integrator/shade_volume.h @@ -774,8 +774,8 @@ ccl_device_forceinline void integrate_volume_direct_light( const bool is_light = light_sample_is_light(ls); /* Branch off shadow kernel. */ - INTEGRATOR_SHADOW_PATH_INIT( - shadow_state, state, DEVICE_KERNEL_INTEGRATOR_INTERSECT_SHADOW, shadow); + IntegratorShadowState shadow_state = integrator_shadow_path_init( + kg, state, DEVICE_KERNEL_INTEGRATOR_INTERSECT_SHADOW, false); /* Write shadow ray and associated state to global memory. */ integrator_state_write_shadow_ray(kg, shadow_state, &ray); @@ -1032,13 +1032,15 @@ ccl_device void integrator_shade_volume(KernelGlobals kg, if (event == VOLUME_PATH_SCATTERED) { /* Queue intersect_closest kernel. */ - INTEGRATOR_PATH_NEXT(DEVICE_KERNEL_INTEGRATOR_SHADE_VOLUME, + integrator_path_next(kg, + state, + DEVICE_KERNEL_INTEGRATOR_SHADE_VOLUME, DEVICE_KERNEL_INTEGRATOR_INTERSECT_CLOSEST); return; } else if (event == VOLUME_PATH_MISSED) { /* End path. */ - INTEGRATOR_PATH_TERMINATE(DEVICE_KERNEL_INTEGRATOR_SHADE_VOLUME); + integrator_path_terminate(kg, state, DEVICE_KERNEL_INTEGRATOR_SHADE_VOLUME); return; } else { diff --git a/intern/cycles/kernel/integrator/shadow_catcher.h b/intern/cycles/kernel/integrator/shadow_catcher.h index 42d44580f80..ff63625aceb 100644 --- a/intern/cycles/kernel/integrator/shadow_catcher.h +++ b/intern/cycles/kernel/integrator/shadow_catcher.h @@ -50,7 +50,7 @@ ccl_device_inline bool kernel_shadow_catcher_is_path_split_bounce(KernelGlobals ccl_device_inline bool kernel_shadow_catcher_path_can_split(KernelGlobals kg, ConstIntegratorState state) { - if (INTEGRATOR_PATH_IS_TERMINATED) { + if (integrator_path_is_terminated(state)) { return false; } diff --git a/intern/cycles/kernel/integrator/state_flow.h b/intern/cycles/kernel/integrator/state_flow.h index d397ef385e7..1ae746022d0 100644 --- a/intern/cycles/kernel/integrator/state_flow.h +++ b/intern/cycles/kernel/integrator/state_flow.h @@ -10,62 +10,94 @@ CCL_NAMESPACE_BEGIN /* Control Flow * - * Utilities for control flow between kernels. The implementation may differ per device - * or even be handled on the host side. To abstract such differences, experiment with - * different implementations and for debugging, this is abstracted using macros. + * Utilities for control flow between kernels. The implementation is different between CPU and + * GPU devices. For the latter part of the logic is handled on the host side with wavefronts. * * There is a main path for regular path tracing camera for path tracing. Shadows for next * event estimation branch off from this into their own path, that may be computed in - * parallel while the main path continues. + * parallel while the main path continues. Additionally, shading kernels are sorted using + * a key for coherence. * * Each kernel on the main path must call one of these functions. These may not be called * multiple times from the same kernel. * - * INTEGRATOR_PATH_INIT(next_kernel) - * INTEGRATOR_PATH_NEXT(current_kernel, next_kernel) - * INTEGRATOR_PATH_TERMINATE(current_kernel) + * integrator_path_init(kg, state, next_kernel) + * integrator_path_next(kg, state, current_kernel, next_kernel) + * integrator_path_terminate(kg, state, current_kernel) * * For the shadow path similar functions are used, and again each shadow kernel must call * one of them, and only once. */ -#define INTEGRATOR_PATH_IS_TERMINATED (INTEGRATOR_STATE(state, path, queued_kernel) == 0) -#define INTEGRATOR_SHADOW_PATH_IS_TERMINATED \ - (INTEGRATOR_STATE(state, shadow_path, queued_kernel) == 0) +ccl_device_forceinline bool integrator_path_is_terminated(ConstIntegratorState state) +{ + return INTEGRATOR_STATE(state, path, queued_kernel) == 0; +} + +ccl_device_forceinline bool integrator_shadow_path_is_terminated(ConstIntegratorShadowState state) +{ + return INTEGRATOR_STATE(state, shadow_path, queued_kernel) == 0; +} #ifdef __KERNEL_GPU__ -# define INTEGRATOR_PATH_INIT(next_kernel) \ - atomic_fetch_and_add_uint32(&kernel_integrator_state.queue_counter->num_queued[next_kernel], \ - 1); \ - INTEGRATOR_STATE_WRITE(state, path, queued_kernel) = next_kernel; -# define INTEGRATOR_PATH_NEXT(current_kernel, next_kernel) \ - atomic_fetch_and_sub_uint32( \ - &kernel_integrator_state.queue_counter->num_queued[current_kernel], 1); \ - atomic_fetch_and_add_uint32(&kernel_integrator_state.queue_counter->num_queued[next_kernel], \ - 1); \ - INTEGRATOR_STATE_WRITE(state, path, queued_kernel) = next_kernel; -# define INTEGRATOR_PATH_TERMINATE(current_kernel) \ - atomic_fetch_and_sub_uint32( \ - &kernel_integrator_state.queue_counter->num_queued[current_kernel], 1); \ - INTEGRATOR_STATE_WRITE(state, path, queued_kernel) = 0; - -# define INTEGRATOR_SHADOW_PATH_INIT(shadow_state, state, next_kernel, shadow_type) \ - IntegratorShadowState shadow_state = atomic_fetch_and_add_uint32( \ - &kernel_integrator_state.next_shadow_path_index[0], 1); \ - atomic_fetch_and_add_uint32(&kernel_integrator_state.queue_counter->num_queued[next_kernel], \ - 1); \ - INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, queued_kernel) = next_kernel; -# define INTEGRATOR_SHADOW_PATH_NEXT(current_kernel, next_kernel) \ - atomic_fetch_and_sub_uint32( \ - &kernel_integrator_state.queue_counter->num_queued[current_kernel], 1); \ - atomic_fetch_and_add_uint32(&kernel_integrator_state.queue_counter->num_queued[next_kernel], \ - 1); \ - INTEGRATOR_STATE_WRITE(state, shadow_path, queued_kernel) = next_kernel; -# define INTEGRATOR_SHADOW_PATH_TERMINATE(current_kernel) \ - atomic_fetch_and_sub_uint32( \ - &kernel_integrator_state.queue_counter->num_queued[current_kernel], 1); \ - INTEGRATOR_STATE_WRITE(state, shadow_path, queued_kernel) = 0; +ccl_device_forceinline void integrator_path_init(KernelGlobals kg, + IntegratorState state, + const DeviceKernel next_kernel) +{ + atomic_fetch_and_add_uint32(&kernel_integrator_state.queue_counter->num_queued[next_kernel], 1); + INTEGRATOR_STATE_WRITE(state, path, queued_kernel) = next_kernel; +} + +ccl_device_forceinline void integrator_path_next(KernelGlobals kg, + IntegratorState state, + const DeviceKernel current_kernel, + const DeviceKernel next_kernel) +{ + atomic_fetch_and_sub_uint32(&kernel_integrator_state.queue_counter->num_queued[current_kernel], + 1); + atomic_fetch_and_add_uint32(&kernel_integrator_state.queue_counter->num_queued[next_kernel], 1); + INTEGRATOR_STATE_WRITE(state, path, queued_kernel) = next_kernel; +} + +ccl_device_forceinline void integrator_path_terminate(KernelGlobals kg, + IntegratorState state, + const DeviceKernel current_kernel) +{ + atomic_fetch_and_sub_uint32(&kernel_integrator_state.queue_counter->num_queued[current_kernel], + 1); + INTEGRATOR_STATE_WRITE(state, path, queued_kernel) = 0; +} + +ccl_device_forceinline IntegratorShadowState integrator_shadow_path_init( + KernelGlobals kg, IntegratorState state, const DeviceKernel next_kernel, const bool is_ao) +{ + IntegratorShadowState shadow_state = atomic_fetch_and_add_uint32( + &kernel_integrator_state.next_shadow_path_index[0], 1); + atomic_fetch_and_add_uint32(&kernel_integrator_state.queue_counter->num_queued[next_kernel], 1); + INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, queued_kernel) = next_kernel; + return shadow_state; +} + +ccl_device_forceinline void integrator_shadow_path_next(KernelGlobals kg, + IntegratorShadowState state, + const DeviceKernel current_kernel, + const DeviceKernel next_kernel) +{ + atomic_fetch_and_sub_uint32(&kernel_integrator_state.queue_counter->num_queued[current_kernel], + 1); + atomic_fetch_and_add_uint32(&kernel_integrator_state.queue_counter->num_queued[next_kernel], 1); + INTEGRATOR_STATE_WRITE(state, shadow_path, queued_kernel) = next_kernel; +} + +ccl_device_forceinline void integrator_shadow_path_terminate(KernelGlobals kg, + IntegratorShadowState state, + const DeviceKernel current_kernel) +{ + atomic_fetch_and_sub_uint32(&kernel_integrator_state.queue_counter->num_queued[current_kernel], + 1); + INTEGRATOR_STATE_WRITE(state, shadow_path, queued_kernel) = 0; +} # ifdef __KERNEL_SORT_PARTITIONING__ /* Sort first by truncated state index (for good locality), then by key (for good coherence). */ @@ -75,68 +107,103 @@ CCL_NAMESPACE_BEGIN # define INTEGRATOR_SORT_KEY(key, state) (key) # endif -# define INTEGRATOR_PATH_INIT_SORTED(next_kernel, key) \ - { \ - const int key_ = INTEGRATOR_SORT_KEY(key, state); \ - atomic_fetch_and_add_uint32( \ - &kernel_integrator_state.queue_counter->num_queued[next_kernel], 1); \ - INTEGRATOR_STATE_WRITE(state, path, queued_kernel) = next_kernel; \ - INTEGRATOR_STATE_WRITE(state, path, shader_sort_key) = key_; \ - atomic_fetch_and_add_uint32(&kernel_integrator_state.sort_key_counter[next_kernel][key_], \ - 1); \ - } -# define INTEGRATOR_PATH_NEXT_SORTED(current_kernel, next_kernel, key) \ - { \ - const int key_ = INTEGRATOR_SORT_KEY(key, state); \ - atomic_fetch_and_sub_uint32( \ - &kernel_integrator_state.queue_counter->num_queued[current_kernel], 1); \ - atomic_fetch_and_add_uint32( \ - &kernel_integrator_state.queue_counter->num_queued[next_kernel], 1); \ - INTEGRATOR_STATE_WRITE(state, path, queued_kernel) = next_kernel; \ - INTEGRATOR_STATE_WRITE(state, path, shader_sort_key) = key_; \ - atomic_fetch_and_add_uint32(&kernel_integrator_state.sort_key_counter[next_kernel][key_], \ - 1); \ - } +ccl_device_forceinline void integrator_path_init_sorted(KernelGlobals kg, + IntegratorState state, + const DeviceKernel next_kernel, + const uint32_t key) +{ + const int key_ = INTEGRATOR_SORT_KEY(key, state); + atomic_fetch_and_add_uint32(&kernel_integrator_state.queue_counter->num_queued[next_kernel], 1); + INTEGRATOR_STATE_WRITE(state, path, queued_kernel) = next_kernel; + INTEGRATOR_STATE_WRITE(state, path, shader_sort_key) = key_; + atomic_fetch_and_add_uint32(&kernel_integrator_state.sort_key_counter[next_kernel][key_], 1); +} + +ccl_device_forceinline void integrator_path_next_sorted(KernelGlobals kg, + IntegratorState state, + const DeviceKernel current_kernel, + const DeviceKernel next_kernel, + const uint32_t key) +{ + const int key_ = INTEGRATOR_SORT_KEY(key, state); + atomic_fetch_and_sub_uint32(&kernel_integrator_state.queue_counter->num_queued[current_kernel], + 1); + atomic_fetch_and_add_uint32(&kernel_integrator_state.queue_counter->num_queued[next_kernel], 1); + INTEGRATOR_STATE_WRITE(state, path, queued_kernel) = next_kernel; + INTEGRATOR_STATE_WRITE(state, path, shader_sort_key) = key_; + atomic_fetch_and_add_uint32(&kernel_integrator_state.sort_key_counter[next_kernel][key_], 1); +} #else -# define INTEGRATOR_PATH_INIT(next_kernel) \ - INTEGRATOR_STATE_WRITE(state, path, queued_kernel) = next_kernel; -# define INTEGRATOR_PATH_INIT_SORTED(next_kernel, key) \ - { \ - INTEGRATOR_STATE_WRITE(state, path, queued_kernel) = next_kernel; \ - (void)key; \ - } -# define INTEGRATOR_PATH_NEXT(current_kernel, next_kernel) \ - { \ - INTEGRATOR_STATE_WRITE(state, path, queued_kernel) = next_kernel; \ - (void)current_kernel; \ - } -# define INTEGRATOR_PATH_TERMINATE(current_kernel) \ - { \ - INTEGRATOR_STATE_WRITE(state, path, queued_kernel) = 0; \ - (void)current_kernel; \ - } -# define INTEGRATOR_PATH_NEXT_SORTED(current_kernel, next_kernel, key) \ - { \ - INTEGRATOR_STATE_WRITE(state, path, queued_kernel) = next_kernel; \ - (void)key; \ - (void)current_kernel; \ - } - -# define INTEGRATOR_SHADOW_PATH_INIT(shadow_state, state, next_kernel, shadow_type) \ - IntegratorShadowState shadow_state = &state->shadow_type; \ - INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, queued_kernel) = next_kernel; -# define INTEGRATOR_SHADOW_PATH_NEXT(current_kernel, next_kernel) \ - { \ - INTEGRATOR_STATE_WRITE(state, shadow_path, queued_kernel) = next_kernel; \ - (void)current_kernel; \ - } -# define INTEGRATOR_SHADOW_PATH_TERMINATE(current_kernel) \ - { \ - INTEGRATOR_STATE_WRITE(state, shadow_path, queued_kernel) = 0; \ - (void)current_kernel; \ - } +ccl_device_forceinline void integrator_path_init(KernelGlobals kg, + IntegratorState state, + const DeviceKernel next_kernel) +{ + INTEGRATOR_STATE_WRITE(state, path, queued_kernel) = next_kernel; +} + +ccl_device_forceinline void integrator_path_init_sorted(KernelGlobals kg, + IntegratorState state, + const DeviceKernel next_kernel, + const uint32_t key) +{ + INTEGRATOR_STATE_WRITE(state, path, queued_kernel) = next_kernel; + (void)key; +} + +ccl_device_forceinline void integrator_path_next(KernelGlobals kg, + IntegratorState state, + const DeviceKernel current_kernel, + const DeviceKernel next_kernel) +{ + INTEGRATOR_STATE_WRITE(state, path, queued_kernel) = next_kernel; + (void)current_kernel; +} + +ccl_device_forceinline void integrator_path_terminate(KernelGlobals kg, + IntegratorState state, + const DeviceKernel current_kernel) +{ + INTEGRATOR_STATE_WRITE(state, path, queued_kernel) = 0; + (void)current_kernel; +} + +ccl_device_forceinline void integrator_path_next_sorted(KernelGlobals kg, + IntegratorState state, + const DeviceKernel current_kernel, + const DeviceKernel next_kernel, + const uint32_t key) +{ + INTEGRATOR_STATE_WRITE(state, path, queued_kernel) = next_kernel; + (void)key; + (void)current_kernel; +} + +ccl_device_forceinline IntegratorShadowState integrator_shadow_path_init( + KernelGlobals kg, IntegratorState state, const DeviceKernel next_kernel, const bool is_ao) +{ + IntegratorShadowState shadow_state = (is_ao) ? &state->ao : &state->shadow; + INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, queued_kernel) = next_kernel; + return shadow_state; +} + +ccl_device_forceinline void integrator_shadow_path_next(KernelGlobals kg, + IntegratorShadowState state, + const DeviceKernel current_kernel, + const DeviceKernel next_kernel) +{ + INTEGRATOR_STATE_WRITE(state, shadow_path, queued_kernel) = next_kernel; + (void)current_kernel; +} + +ccl_device_forceinline void integrator_shadow_path_terminate(KernelGlobals kg, + IntegratorShadowState state, + const DeviceKernel current_kernel) +{ + INTEGRATOR_STATE_WRITE(state, shadow_path, queued_kernel) = 0; + (void)current_kernel; +} #endif diff --git a/intern/cycles/kernel/integrator/subsurface.h b/intern/cycles/kernel/integrator/subsurface.h index 1e6fcf4aff0..09e59919c86 100644 --- a/intern/cycles/kernel/integrator/subsurface.h +++ b/intern/cycles/kernel/integrator/subsurface.h @@ -177,17 +177,17 @@ ccl_device_inline bool subsurface_scatter(KernelGlobals kg, IntegratorState stat const bool use_raytrace_kernel = (shader_flags & SD_HAS_RAYTRACE); if (use_caustics) { - INTEGRATOR_PATH_NEXT_SORTED(DEVICE_KERNEL_INTEGRATOR_INTERSECT_SUBSURFACE, + integrator_path_next_sorted(kg, state, DEVICE_KERNEL_INTEGRATOR_INTERSECT_SUBSURFACE, DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE_MNEE, shader); } else if (use_raytrace_kernel) { - INTEGRATOR_PATH_NEXT_SORTED(DEVICE_KERNEL_INTEGRATOR_INTERSECT_SUBSURFACE, + integrator_path_next_sorted(kg, state, DEVICE_KERNEL_INTEGRATOR_INTERSECT_SUBSURFACE, DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE_RAYTRACE, shader); } else { - INTEGRATOR_PATH_NEXT_SORTED(DEVICE_KERNEL_INTEGRATOR_INTERSECT_SUBSURFACE, + integrator_path_next_sorted(kg, state, DEVICE_KERNEL_INTEGRATOR_INTERSECT_SUBSURFACE, DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE, shader); } -- cgit v1.2.3 From 1b5db02a0207621a0325c0d2ee6f748b2520d2fe Mon Sep 17 00:00:00 2001 From: Olivier Maury Date: Thu, 14 Jul 2022 15:59:26 +0200 Subject: Fix Cycles MNEE wrong results with area light spread When the solve is successful, the light sample needs to be updated since the effective shading point is now on the last refractive interface. Spread was not taken into account, creating false caustics. Differential Revision: https://developer.blender.org/D15449 --- intern/cycles/kernel/integrator/mnee.h | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/intern/cycles/kernel/integrator/mnee.h b/intern/cycles/kernel/integrator/mnee.h index 67505b9b612..70b009d3b48 100644 --- a/intern/cycles/kernel/integrator/mnee.h +++ b/intern/cycles/kernel/integrator/mnee.h @@ -137,8 +137,14 @@ ccl_device_forceinline void mnee_update_light_sample(KernelGlobals kg, } } else if (ls->type == LIGHT_AREA) { + float invarea = fabsf(klight->area.invarea); ls->D = normalize_len(ls->P - P, &ls->t); - ls->pdf = fabsf(klight->area.invarea); + ls->pdf = invarea; + if (klight->area.tan_spread > 0.f) { + ls->eval_fac = 0.25f * invarea; + ls->eval_fac *= light_spread_attenuation( + ls->D, ls->Ng, klight->area.tan_spread, klight->area.normalize_spread); + } } ls->pdf *= kernel_data.integrator.pdf_lights; -- cgit v1.2.3 From 3b15467e97abf473d4d25c7382999115d3169a57 Mon Sep 17 00:00:00 2001 From: Antonio Vazquez Date: Thu, 14 Jul 2022 16:33:21 +0200 Subject: Fix T99702: Gpencil Flip strokes did not support multiframe edit This was a missing feature and this commit solves this. --- source/blender/editors/gpencil/gpencil_edit.c | 51 ++++++++++++++++----------- 1 file changed, 30 insertions(+), 21 deletions(-) diff --git a/source/blender/editors/gpencil/gpencil_edit.c b/source/blender/editors/gpencil/gpencil_edit.c index 06f3c169fa9..71cf9b1fafd 100644 --- a/source/blender/editors/gpencil/gpencil_edit.c +++ b/source/blender/editors/gpencil/gpencil_edit.c @@ -3709,35 +3709,44 @@ static int gpencil_stroke_flip_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } + const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd); const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd); + bool changed = false; - /* read all selected strokes */ + /* Read all selected strokes. */ CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) { - bGPDframe *gpf = gpl->actframe; - if (gpf == NULL) { - continue; - } + bGPDframe *init_gpf = (is_multiedit) ? gpl->frames.first : gpl->actframe; - LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) { - if (gps->flag & GP_STROKE_SELECT) { - /* skip strokes that are invalid for current view */ - if (ED_gpencil_stroke_can_use(C, gps) == false) { - continue; - } - /* check if the color is editable */ - if (ED_gpencil_stroke_material_editable(ob, gpl, gps) == false) { + for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) { + if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) { + if (gpf == NULL) { continue; } + LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) { + if (gps->flag & GP_STROKE_SELECT) { + /* skip strokes that are invalid for current view */ + if (ED_gpencil_stroke_can_use(C, gps) == false) { + continue; + } + /* check if the color is editable */ + if (ED_gpencil_stroke_material_editable(ob, gpl, gps) == false) { + continue; + } - if (is_curve_edit) { - BKE_report(op->reports, RPT_ERROR, "Not implemented!"); - } - else { - /* Flip stroke. */ - BKE_gpencil_stroke_flip(gps); + if (is_curve_edit) { + BKE_report(op->reports, RPT_ERROR, "Not implemented!"); + } + else { + /* Flip stroke. */ + BKE_gpencil_stroke_flip(gps); + changed = true; + } + } } - - changed = true; + } + /* If not multi-edit, exit loop. */ + if (!is_multiedit) { + break; } } } -- cgit v1.2.3 From b6de6da59afbf84b21df661d096f324360c763d7 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Thu, 14 Jul 2022 18:46:52 +0200 Subject: I18n: Fix regex for messages from `BKE_modifier_set_error`. Signature of this function changed at some point, regex to extract messages from it was no longer working. Reported/detected as part of D15418. --- release/scripts/modules/bl_i18n_utils/settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/release/scripts/modules/bl_i18n_utils/settings.py b/release/scripts/modules/bl_i18n_utils/settings.py index 7aeef80b0bd..9b38c512d31 100644 --- a/release/scripts/modules/bl_i18n_utils/settings.py +++ b/release/scripts/modules/bl_i18n_utils/settings.py @@ -248,7 +248,7 @@ PYGETTEXT_KEYWORDS = (() + tuple(("{}\\((?:[^\"',]+,){{3}}\\s*" + _msg_re + r"\s*\)").format(it) for it in ("BMO_error_raise",)) + - tuple(("{}\\((?:[^\"',]+,)\\s*" + _msg_re + r"\s*(?:\)|,)").format(it) + tuple(("{}\\((?:[^\"',]+,){{2}}\\s*" + _msg_re + r"\s*(?:\)|,)").format(it) for it in ("BKE_modifier_set_error",)) + # bUnitDef unit names. -- cgit v1.2.3 From 1ef686bd26cc3c89849f41770ce76d7b94f169db Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Thu, 14 Jul 2022 19:07:13 +0200 Subject: UI: Tweak layout of File Browser Preferences * Don't nest "Show Recent Locations" and "Show System Locations" under a "Defaults" heading. They are not just a default setting but completely hide panels from the UI. * Use own "Show Locations" heading instead, and remove redundant words from labels. * Move the options to the top of the panel, they are more general since they can't be toggled in a File Browser session, and thus have bigger impact. We may want to remove these options in a future major release, I don't think they are useful. Agreed on with Pablo Vazquez. --- release/scripts/startup/bl_ui/space_userpref.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/release/scripts/startup/bl_ui/space_userpref.py b/release/scripts/startup/bl_ui/space_userpref.py index cccbb63d27c..7a651e8c3c8 100644 --- a/release/scripts/startup/bl_ui/space_userpref.py +++ b/release/scripts/startup/bl_ui/space_userpref.py @@ -1485,11 +1485,13 @@ class USERPREF_PT_saveload_file_browser(SaveLoadPanel, CenterAlignMixIn, Panel): prefs = context.preferences paths = prefs.filepaths + col = layout.column(heading="Show Locations") + col.prop(paths, "show_recent_locations", text="Recent") + col.prop(paths, "show_system_bookmarks", text="System") + col = layout.column(heading="Defaults") col.prop(paths, "use_filter_files") col.prop(paths, "show_hidden_files_datablocks") - col.prop(paths, "show_recent_locations") - col.prop(paths, "show_system_bookmarks") # ----------------------------------------------------------------------------- -- cgit v1.2.3 From bdd0ac5bcebd6deb3d590ada9a3f25c6ddd58ea4 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Thu, 14 Jul 2022 19:21:56 +0200 Subject: Fix `on_drag_start` handler not getting ID when dragging from Outliner We would first invoke the dragging, and then set the drag data (like the ID or the dragged modifier), so the `wmDropBox.on_drag_start()` handler wouldn't be able to access this. This broke dragging some IDs from the Outliner, noticed in D15333. It's now possible to first create/request drag data, extend it, and then invoke the actual dragging. The normal function to start dragging returns `void` now instead of `wmDrag *`, so the drag data can't easily be modified after starting anymore. --- source/blender/editors/interface/interface_drag.cc | 4 +++- .../editors/space_outliner/outliner_dragdrop.cc | 4 +++- source/blender/windowmanager/WM_api.h | 18 ++++++++++++++++-- source/blender/windowmanager/intern/wm_dragdrop.c | 19 +++++++++++++++---- 4 files changed, 37 insertions(+), 8 deletions(-) diff --git a/source/blender/editors/interface/interface_drag.cc b/source/blender/editors/interface/interface_drag.cc index 4c68870b2c7..1db3db32411 100644 --- a/source/blender/editors/interface/interface_drag.cc +++ b/source/blender/editors/interface/interface_drag.cc @@ -122,7 +122,7 @@ bool ui_but_drag_is_draggable(const uiBut *but) void ui_but_drag_start(bContext *C, uiBut *but) { - wmDrag *drag = WM_event_start_drag(C, + wmDrag *drag = WM_drag_data_create(C, but->icon, but->dragtype, but->dragpoin, @@ -136,6 +136,8 @@ void ui_but_drag_start(bContext *C, uiBut *but) WM_event_drag_image(drag, but->imb, but->imb_scale); } + WM_event_start_prepared_drag(C, drag); + /* Special feature for assets: We add another drag item that supports multiple assets. It * gets the assets from context. */ if (ELEM(but->dragtype, WM_DRAG_ASSET, WM_DRAG_ID)) { diff --git a/source/blender/editors/space_outliner/outliner_dragdrop.cc b/source/blender/editors/space_outliner/outliner_dragdrop.cc index c72080be811..7435fa50a93 100644 --- a/source/blender/editors/space_outliner/outliner_dragdrop.cc +++ b/source/blender/editors/space_outliner/outliner_dragdrop.cc @@ -1455,7 +1455,7 @@ static int outliner_item_drag_drop_invoke(bContext *C, TSE_GPENCIL_EFFECT_BASE); const int wm_drag_type = use_datastack_drag ? WM_DRAG_DATASTACK : WM_DRAG_ID; - wmDrag *drag = WM_event_start_drag(C, data.icon, wm_drag_type, nullptr, 0.0, WM_DRAG_NOP); + wmDrag *drag = WM_drag_data_create(C, data.icon, wm_drag_type, nullptr, 0.0, WM_DRAG_NOP); if (use_datastack_drag) { TreeElement *te_bone = nullptr; @@ -1545,6 +1545,8 @@ static int outliner_item_drag_drop_invoke(bContext *C, WM_drag_add_local_ID(drag, data.drag_id, data.drag_parent); } + WM_event_start_prepared_drag(C, drag); + ED_outliner_select_sync_from_outliner(C, space_outliner); return (OPERATOR_FINISHED | OPERATOR_PASS_THROUGH); diff --git a/source/blender/windowmanager/WM_api.h b/source/blender/windowmanager/WM_api.h index f337dd9d89a..44c5b86857d 100644 --- a/source/blender/windowmanager/WM_api.h +++ b/source/blender/windowmanager/WM_api.h @@ -1214,10 +1214,24 @@ int WM_operator_flag_only_pass_through_on_press(int retval, const struct wmEvent /* Drag and drop. */ /** - * Note that the pointer should be valid allocated and not on stack. + * Start dragging immediately with the given data. + * Note that \a poin should be valid allocated and not on stack. */ -struct wmDrag *WM_event_start_drag( +void WM_event_start_drag( struct bContext *C, int icon, int type, void *poin, double value, unsigned int flags); +/** + * Create and fill the dragging data, but don't start dragging just yet (unlike + * #WM_event_start_drag()). Must be followed up by #WM_event_start_prepared_drag(), otherwise the + * returned pointer will leak memory. + * + * Note that \a poin should be valid allocated and not on stack. + */ +wmDrag *WM_drag_data_create( + struct bContext *C, int icon, int type, void *poin, double value, unsigned int flags); +/** + * Invoke dragging using the given \a drag data. + */ +void WM_event_start_prepared_drag(struct bContext *C, wmDrag *drag); void WM_event_drag_image(struct wmDrag *, struct ImBuf *, float scale); void WM_drag_free(struct wmDrag *drag); void WM_drag_data_free(int dragtype, void *poin); diff --git a/source/blender/windowmanager/intern/wm_dragdrop.c b/source/blender/windowmanager/intern/wm_dragdrop.c index 546ba795892..36bd69a9b25 100644 --- a/source/blender/windowmanager/intern/wm_dragdrop.c +++ b/source/blender/windowmanager/intern/wm_dragdrop.c @@ -175,16 +175,14 @@ static void wm_dropbox_invoke(bContext *C, wmDrag *drag) } } -wmDrag *WM_event_start_drag( +wmDrag *WM_drag_data_create( struct bContext *C, int icon, int type, void *poin, double value, unsigned int flags) { - wmWindowManager *wm = CTX_wm_manager(C); wmDrag *drag = MEM_callocN(sizeof(struct wmDrag), "new drag"); /* Keep track of future multi-touch drag too, add a mouse-pointer id or so. */ /* if multiple drags are added, they're drawn as list */ - BLI_addtail(&wm->drags, drag); drag->flags = flags; drag->icon = icon; drag->type = type; @@ -226,9 +224,22 @@ wmDrag *WM_event_start_drag( } drag->value = value; + return drag; +} + +void WM_event_start_prepared_drag(bContext *C, wmDrag *drag) +{ + wmWindowManager *wm = CTX_wm_manager(C); + + BLI_addtail(&wm->drags, drag); wm_dropbox_invoke(C, drag); +} - return drag; +void WM_event_start_drag( + struct bContext *C, int icon, int type, void *poin, double value, unsigned int flags) +{ + wmDrag *drag = WM_drag_data_create(C, icon, type, poin, value, flags); + WM_event_start_prepared_drag(C, drag); } void wm_drags_exit(wmWindowManager *wm, wmWindow *win) -- cgit v1.2.3 From 9fedcde750bb1f6fbbdc00f8086b12f1fd523231 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Thu, 14 Jul 2022 20:05:14 +0200 Subject: Modifiers: fix mesh to volume modifier on non-volume objects --- source/blender/modifiers/intern/MOD_mesh_to_volume.cc | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/source/blender/modifiers/intern/MOD_mesh_to_volume.cc b/source/blender/modifiers/intern/MOD_mesh_to_volume.cc index 01c1875a760..39bd013609b 100644 --- a/source/blender/modifiers/intern/MOD_mesh_to_volume.cc +++ b/source/blender/modifiers/intern/MOD_mesh_to_volume.cc @@ -7,6 +7,7 @@ #include #include "BKE_geometry_set.hh" +#include "BKE_lib_id.h" #include "BKE_lib_query.h" #include "BKE_mesh_runtime.h" #include "BKE_mesh_wrapper.h" @@ -161,7 +162,13 @@ static Volume *mesh_to_volume(ModifierData *md, mesh_to_own_object_space_transform); /* Create a new volume. */ - Volume *volume = BKE_volume_new_for_eval(input_volume); + Volume *volume; + if (input_volume == nullptr) { + volume = static_cast(BKE_id_new_nomain(ID_VO, "Volume")); + } + else { + volume = BKE_volume_new_for_eval(input_volume); + } /* Convert mesh to grid and add to volume. */ geometry::volume_grid_add_from_mesh(volume, -- cgit v1.2.3 From b1329d7eaa52a11c73b75d19d20bd8f6d11ac535 Mon Sep 17 00:00:00 2001 From: Ray Molenkamp Date: Thu, 14 Jul 2022 12:18:35 -0600 Subject: Fix T99705: fix integer overflow in thumbnail extractor It was smart enough to check if the buffer had the right size but neglected to cast to a 64 bit value so it overflowed. Differential Revision: https://developer.blender.org/D15457 Reviewed By: brecht --- source/blender/blendthumb/src/blendthumb_extract.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/source/blender/blendthumb/src/blendthumb_extract.cc b/source/blender/blendthumb/src/blendthumb_extract.cc index de1f50dfdce..369da559fc8 100644 --- a/source/blender/blendthumb/src/blendthumb_extract.cc +++ b/source/blender/blendthumb/src/blendthumb_extract.cc @@ -134,7 +134,8 @@ static eThumbStatus blendthumb_extract_from_file_impl(FileReader *file, /* Verify that image dimensions and data size make sense. */ size_t data_size = block_size - 8; - const size_t expected_size = thumb->width * thumb->height * 4; + const uint64_t expected_size = static_cast(thumb->width) * + static_cast(thumb->height) * 4; if (thumb->width < 0 || thumb->height < 0 || data_size != expected_size) { return BT_INVALID_THUMB; } -- cgit v1.2.3 From 0e9367fc29bc42c80e10b7097facec2b029a7362 Mon Sep 17 00:00:00 2001 From: Chris Blackbourn Date: Fri, 15 Jul 2022 10:04:50 +1200 Subject: Cleanup: separate clipUVTransform into two different functions No functional changes. Prep for D15420 / T98061. --- .../blender/editors/transform/transform_convert.c | 126 --------------------- .../blender/editors/transform/transform_convert.h | 1 - .../editors/transform/transform_mode_resize.c | 99 +++++++++++++++- .../editors/transform/transform_mode_translate.c | 57 +++++++++- 4 files changed, 154 insertions(+), 129 deletions(-) diff --git a/source/blender/editors/transform/transform_convert.c b/source/blender/editors/transform/transform_convert.c index a23689166c4..bab700560fe 100644 --- a/source/blender/editors/transform/transform_convert.c +++ b/source/blender/editors/transform/transform_convert.c @@ -479,132 +479,6 @@ TransDataCurveHandleFlags *initTransDataCurveHandles(TransData *td, struct BezTr /** \name UV Coordinates * \{ */ -/** - * Find the correction for the scaling factor when "Constrain to Bounds" is active. - * \param numerator: How far the UV boundary (unit square) is from the origin of the scale. - * \param denominator: How far the AABB is from the origin of the scale. - * \param scale: Scale parameter to update. - */ -static void constrain_scale_to_boundary(const float numerator, - const float denominator, - float *scale) -{ - if (denominator == 0.0f) { - /* The origin of the scale is on the edge of the boundary. */ - if (numerator < 0.0f) { - /* Negative scale will wrap around and put us outside the boundary. */ - *scale = 0.0f; /* Hold at the boundary instead. */ - } - return; /* Nothing else we can do without more info. */ - } - - const float correction = numerator / denominator; - if (correction < 0.0f || !isfinite(correction)) { - /* TODO: Correction is negative or invalid, but we lack context to fix `*scale`. */ - return; - } - - if (denominator < 0.0f) { - /* Scale origin is outside boundary, only make scale bigger. */ - if (*scale < correction) { - *scale = correction; - } - return; - } - - /* Scale origin is inside boundary, the "regular" case, limit maximum scale. */ - if (*scale > correction) { - *scale = correction; - } -} - -bool clipUVTransform(TransInfo *t, float vec[2], const bool resize) -{ - bool clipx = true, clipy = true; - float min[2], max[2]; - - /* Check if the current image in UV editor is a tiled image or not. */ - const SpaceImage *sima = t->area->spacedata.first; - const Image *image = sima->image; - const bool is_tiled_image = image && (image->source == IMA_SRC_TILED); - /* Stores the coordinates of the closest UDIM tile. - * Also acts as an offset to the tile from the origin of UV space. */ - float base_offset[2] = {0.0f, 0.0f}; - - /* If tiled image then constrain to correct/closest UDIM tile, else 0-1 UV space. */ - if (is_tiled_image) { - int nearest_tile_index = BKE_image_find_nearest_tile(image, t->center_global); - if (nearest_tile_index != -1) { - nearest_tile_index -= 1001; - /* Getting coordinates of nearest tile from the tile index. */ - base_offset[0] = nearest_tile_index % 10; - base_offset[1] = nearest_tile_index / 10; - } - } - - min[0] = min[1] = FLT_MAX; - max[0] = max[1] = FLT_MIN; - - FOREACH_TRANS_DATA_CONTAINER (t, tc) { - - TransData *td; - int a; - - for (a = 0, td = tc->data; a < tc->data_len; a++, td++) { - minmax_v2v2_v2(min, max, td->loc); - } - } - - if (resize) { - /* Assume no change is required. */ - float scalex = 1.0f; - float scaley = 1.0f; - - /* Update U against the left border. */ - constrain_scale_to_boundary( - t->center_global[0] - base_offset[0], t->center_global[0] - min[0], &scalex); - /* Now the right border, negated, because `-1.0 / -1.0 = 1.0` */ - constrain_scale_to_boundary(base_offset[0] + t->aspect[0] - t->center_global[0], - max[0] - t->center_global[0], - &scalex); - - /* Do the same for the V co-ordinate, which is called `y`. */ - constrain_scale_to_boundary( - t->center_global[1] - base_offset[1], t->center_global[1] - min[1], &scaley); - constrain_scale_to_boundary(base_offset[1] + t->aspect[1] - t->center_global[1], - max[1] - t->center_global[1], - &scaley); - - clipx = (scalex != 1.0f); - clipy = (scaley != 1.0f); - vec[0] *= scalex; - vec[1] *= scaley; - } - else { - if (min[0] < base_offset[0]) { - vec[0] += base_offset[0] - min[0]; - } - else if (max[0] > base_offset[0] + t->aspect[0]) { - vec[0] -= max[0] - base_offset[0] - t->aspect[0]; - } - else { - clipx = 0; - } - - if (min[1] < base_offset[1]) { - vec[1] += base_offset[1] - min[1]; - } - else if (max[1] > base_offset[1] + t->aspect[1]) { - vec[1] -= max[1] - base_offset[1] - t->aspect[1]; - } - else { - clipy = 0; - } - } - - return (clipx || clipy); -} - void clipUVData(TransInfo *t) { FOREACH_TRANS_DATA_CONTAINER (t, tc) { diff --git a/source/blender/editors/transform/transform_convert.h b/source/blender/editors/transform/transform_convert.h index 037fbe26c77..f25a3db3f75 100644 --- a/source/blender/editors/transform/transform_convert.h +++ b/source/blender/editors/transform/transform_convert.h @@ -34,7 +34,6 @@ int special_transform_moving(TransInfo *t); void special_aftertrans_update(struct bContext *C, TransInfo *t); void sort_trans_data_dist(TransInfo *t); void createTransData(struct bContext *C, TransInfo *t); -bool clipUVTransform(TransInfo *t, float vec[2], bool resize); void clipUVData(TransInfo *t); void transform_convert_flush_handle2D(TransData *td, TransData2D *td2d, float y_fac); /** diff --git a/source/blender/editors/transform/transform_mode_resize.c b/source/blender/editors/transform/transform_mode_resize.c index 31d40486afc..bc45ec07eab 100644 --- a/source/blender/editors/transform/transform_mode_resize.c +++ b/source/blender/editors/transform/transform_mode_resize.c @@ -11,6 +11,7 @@ #include "BLI_task.h" #include "BKE_context.h" +#include "BKE_image.h" #include "BKE_unit.h" #include "ED_screen.h" @@ -84,6 +85,102 @@ static void ApplySnapResize(TransInfo *t, float vec[3]) } } +/** + * Find the correction for the scaling factor when "Constrain to Bounds" is active. + * \param numerator: How far the UV boundary (unit square) is from the origin of the scale. + * \param denominator: How far the AABB is from the origin of the scale. + * \param scale: Scale parameter to update. + */ +static void constrain_scale_to_boundary(const float numerator, + const float denominator, + float *scale) +{ + if (denominator == 0.0f) { + /* The origin of the scale is on the edge of the boundary. */ + if (numerator < 0.0f) { + /* Negative scale will wrap around and put us outside the boundary. */ + *scale = 0.0f; /* Hold at the boundary instead. */ + } + return; /* Nothing else we can do without more info. */ + } + + const float correction = numerator / denominator; + if (correction < 0.0f || !isfinite(correction)) { + /* TODO: Correction is negative or invalid, but we lack context to fix `*scale`. */ + return; + } + + if (denominator < 0.0f) { + /* Scale origin is outside boundary, only make scale bigger. */ + if (*scale < correction) { + *scale = correction; + } + return; + } + + /* Scale origin is inside boundary, the "regular" case, limit maximum scale. */ + if (*scale > correction) { + *scale = correction; + } +} + +static bool clip_uv_transform_resize(TransInfo *t, float vec[2]) +{ + /* Assume no change is required. */ + float scalex = 1.0f; + float scaley = 1.0f; + + /* Check if the current image in UV editor is a tiled image or not. */ + const SpaceImage *sima = t->area->spacedata.first; + const Image *image = sima->image; + const bool is_tiled_image = image && (image->source == IMA_SRC_TILED); + + /* Stores the coordinates of the closest UDIM tile. + * Also acts as an offset to the tile from the origin of UV space. */ + float base_offset[2] = {0.0f, 0.0f}; + + /* If tiled image then constrain to correct/closest UDIM tile, else 0-1 UV space. */ + if (is_tiled_image) { + int nearest_tile_index = BKE_image_find_nearest_tile(image, t->center_global); + if (nearest_tile_index != -1) { + nearest_tile_index -= 1001; + /* Getting coordinates of nearest tile from the tile index. */ + base_offset[0] = nearest_tile_index % 10; + base_offset[1] = nearest_tile_index / 10; + } + } + + float min[2], max[2]; + min[0] = min[1] = FLT_MAX; + max[0] = max[1] = -FLT_MAX; + + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + + TransData *td; + int a; + for (a = 0, td = tc->data; a < tc->data_len; a++, td++) { + minmax_v2v2_v2(min, max, td->loc); + } + } + + /* Update U against the left border. */ + constrain_scale_to_boundary( + t->center_global[0] - base_offset[0], t->center_global[0] - min[0], &scalex); + /* Now the right border, negated, because `-1.0 / -1.0 = 1.0` */ + constrain_scale_to_boundary( + base_offset[0] + t->aspect[0] - t->center_global[0], max[0] - t->center_global[0], &scalex); + + /* Do the same for the V co-ordinate, which is called `y`. */ + constrain_scale_to_boundary( + t->center_global[1] - base_offset[1], t->center_global[1] - min[1], &scaley); + constrain_scale_to_boundary( + base_offset[1] + t->aspect[1] - t->center_global[1], max[1] - t->center_global[1], &scaley); + + vec[0] *= scalex; + vec[1] *= scaley; + return (scalex != 1.0f) || (scaley != 1.0f); +} + static void applyResize(TransInfo *t, const int UNUSED(mval[2])) { float mat[3][3]; @@ -157,7 +254,7 @@ static void applyResize(TransInfo *t, const int UNUSED(mval[2])) } /* Evil hack - redo resize if clipping needed. */ - if (t->flag & T_CLIP_UV && clipUVTransform(t, t->values_final, 1)) { + if (t->flag & T_CLIP_UV && clip_uv_transform_resize(t, t->values_final)) { size_to_mat3(mat, t->values_final); if (t->con.mode & CON_APPLY) { diff --git a/source/blender/editors/transform/transform_mode_translate.c b/source/blender/editors/transform/transform_mode_translate.c index 65690f9069d..67bdeb3fed0 100644 --- a/source/blender/editors/transform/transform_mode_translate.c +++ b/source/blender/editors/transform/transform_mode_translate.c @@ -16,6 +16,7 @@ #include "BLI_task.h" #include "BKE_context.h" +#include "BKE_image.h" #include "BKE_report.h" #include "BKE_unit.h" @@ -434,6 +435,60 @@ static void applyTranslationValue(TransInfo *t, const float vec[3]) custom_data->prev.rotate_mode = rotate_mode; } +static bool clip_uv_transform_translation(TransInfo *t, float vec[2]) +{ + /* Check if the current image in UV editor is a tiled image or not. */ + const SpaceImage *sima = t->area->spacedata.first; + const Image *image = sima->image; + const bool is_tiled_image = image && (image->source == IMA_SRC_TILED); + + /* Stores the coordinates of the closest UDIM tile. + * Also acts as an offset to the tile from the origin of UV space. */ + float base_offset[2] = {0.0f, 0.0f}; + + /* If tiled image then constrain to correct/closest UDIM tile, else 0-1 UV space. */ + if (is_tiled_image) { + int nearest_tile_index = BKE_image_find_nearest_tile(image, t->center_global); + if (nearest_tile_index != -1) { + nearest_tile_index -= 1001; + /* Getting coordinates of nearest tile from the tile index. */ + base_offset[0] = nearest_tile_index % 10; + base_offset[1] = nearest_tile_index / 10; + } + } + + float min[2], max[2]; + min[0] = min[1] = FLT_MAX; + max[0] = max[1] = -FLT_MAX; + + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + for (TransData *td = tc->data; td < tc->data + tc->data_len; td++) { + minmax_v2v2_v2(min, max, td->loc); + } + } + + bool result = false; + if (min[0] < base_offset[0]) { + vec[0] += base_offset[0] - min[0]; + result = true; + } + else if (max[0] > base_offset[0] + t->aspect[0]) { + vec[0] -= max[0] - base_offset[0] - t->aspect[0]; + result = true; + } + + if (min[1] < base_offset[1]) { + vec[1] += base_offset[1] - min[1]; + result = true; + } + else if (max[1] > base_offset[1] + t->aspect[1]) { + vec[1] -= max[1] - base_offset[1] - t->aspect[1]; + result = true; + } + + return result; +} + static void applyTranslation(TransInfo *t, const int UNUSED(mval[2])) { char str[UI_MAX_DRAW_STR]; @@ -498,7 +553,7 @@ static void applyTranslation(TransInfo *t, const int UNUSED(mval[2])) applyTranslationValue(t, global_dir); /* evil hack - redo translation if clipping needed */ - if (t->flag & T_CLIP_UV && clipUVTransform(t, global_dir, 0)) { + if (t->flag & T_CLIP_UV && clip_uv_transform_translation(t, global_dir)) { applyTranslationValue(t, global_dir); /* In proportional edit it can happen that */ -- cgit v1.2.3 From 178868cf42594bf7eedfa4db93ba8b7f3bf017ce Mon Sep 17 00:00:00 2001 From: Chris Blackbourn Date: Thu, 14 Jul 2022 12:40:43 +1200 Subject: Fix T79304: improve uv island calculation when in edge selection mode Differential Revision: https://developer.blender.org/D15419 --- source/blender/editors/mesh/editmesh_utils.c | 144 +++++++++++++++++++++++++-- 1 file changed, 137 insertions(+), 7 deletions(-) diff --git a/source/blender/editors/mesh/editmesh_utils.c b/source/blender/editors/mesh/editmesh_utils.c index 60a666ee755..ac5530c8ea9 100644 --- a/source/blender/editors/mesh/editmesh_utils.c +++ b/source/blender/editors/mesh/editmesh_utils.c @@ -595,6 +595,132 @@ UvMapVert *BM_uv_vert_map_at_index(UvVertMap *vmap, uint v) #define INVALID_ISLAND ((unsigned int)-1) +static void bm_uv_assign_island(UvElementMap *element_map, + UvElement *element, + int nisland, + uint *map, + UvElement *islandbuf, + int islandbufsize) +{ + element->island = nisland; + map[element - element_map->buf] = islandbufsize; + + /* Copy *element to islandbuf[islandbufsize]. */ + islandbuf[islandbufsize].l = element->l; + islandbuf[islandbufsize].separate = element->separate; + islandbuf[islandbufsize].loop_of_poly_index = element->loop_of_poly_index; + islandbuf[islandbufsize].island = element->island; + islandbuf[islandbufsize].flag = element->flag; +} + +static int bm_uv_edge_select_build_islands(UvElementMap *element_map, + const Scene *scene, + UvElement *islandbuf, + uint *map, + bool uv_selected, + int cd_loop_uv_offset) +{ + int totuv = element_map->totalUVs; + + /* For each UvElement, locate the "separate" UvElement that precedes it in the linked list. */ + UvElement **head_table = MEM_mallocN(sizeof(*head_table) * totuv, "uv_island_head_table"); + for (int i = 0; i < totuv; i++) { + UvElement *head = element_map->buf + i; + if (head->separate) { + UvElement *element = head; + while (element) { + head_table[element - element_map->buf] = head; + element = element->next; + if (element && element->separate) { + break; + } + } + } + } + + /* Depth first search the graph, building islands as we go. */ + int nislands = 0; + int islandbufsize = 0; + int stack_upper_bound = totuv; + UvElement **stack_uv = MEM_mallocN(sizeof(*stack_uv) * stack_upper_bound, + "uv_island_element_stack"); + int stacksize_uv = 0; + for (int i = 0; i < totuv; i++) { + UvElement *element = element_map->buf + i; + if (element->island != INVALID_ISLAND) { + /* Unique UV (element and all it's children) are already part of an island. */ + continue; + } + + /* Create a new island, i.e. nislands++. */ + + BLI_assert(element->separate); /* Ensure we're the head of this unique UV. */ + + /* Seed the graph search. */ + stack_uv[stacksize_uv++] = element; + while (element) { + bm_uv_assign_island(element_map, element, nislands, map, islandbuf, islandbufsize++); + element = element->next; + if (element && element->separate) { + break; + } + } + + /* Traverse the graph. */ + while (stacksize_uv) { + BLI_assert(stacksize_uv < stack_upper_bound); + element = stack_uv[--stacksize_uv]; + while (element) { + + /* Scan forwards around the BMFace that contains element->l. */ + 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->buf]; + stack_uv[stacksize_uv++] = tail; + while (tail) { + bm_uv_assign_island(element_map, tail, nislands, map, islandbuf, islandbufsize++); + tail = tail->next; + if (tail && tail->separate) { + break; + } + } + } + } + + /* Scan backwards around the BMFace that contains element->l. */ + 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->buf]; + stack_uv[stacksize_uv++] = tail; + while (tail) { + bm_uv_assign_island(element_map, tail, nislands, map, islandbuf, islandbufsize++); + tail = tail->next; + if (tail && tail->separate) { + break; + } + } + } + } + + /* The same for all the UvElements in this unique UV. */ + element = element->next; + if (element && element->separate) { + break; + } + } + } + nislands++; + } + BLI_assert(islandbufsize == totuv); + + MEM_SAFE_FREE(stack_uv); + MEM_SAFE_FREE(head_table); + + return nislands; +} + UvElementMap *BM_uv_element_map_create(BMesh *bm, const Scene *scene, const bool uv_selected, @@ -783,6 +909,15 @@ UvElementMap *BM_uv_element_map_create(BMesh *bm, island_number = MEM_mallocN(sizeof(*island_number) * totfaces, "uv_island_number_face"); copy_vn_i(island_number, totfaces, 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++) { @@ -810,13 +945,8 @@ UvElementMap *BM_uv_element_map_create(BMesh *bm, if (element->l->f == efa) { /* found the uv corresponding to our face and vertex. * Now fill it to the buffer */ - element->island = nislands; - map[element - element_map->buf] = islandbufsize; - islandbuf[islandbufsize].l = element->l; - islandbuf[islandbufsize].separate = element->separate; - islandbuf[islandbufsize].loop_of_poly_index = element->loop_of_poly_index; - islandbuf[islandbufsize].island = nislands; - islandbufsize++; + bm_uv_assign_island( + element_map, element, nislands, map, islandbuf, islandbufsize++); for (element = initelement; element; element = element->next) { if (element->separate && element != initelement) { -- cgit v1.2.3 From 675f6ef089ceeed2f03284e7e81e6af7130e46d7 Mon Sep 17 00:00:00 2001 From: Jesse Yurkovich Date: Thu, 14 Jul 2022 21:27:58 -0700 Subject: Cleanup: Use const pointers for ImageSaveOptions and ImageFormatData Use const pointers to ImageSaveOptions and ImageFormatData for API parameters where appropriate. Differential Revision: https://developer.blender.org/D15400 --- source/blender/blenkernel/BKE_image.h | 2 +- source/blender/blenkernel/BKE_image_save.h | 4 ++-- source/blender/blenkernel/BKE_writeffmpeg.h | 2 +- source/blender/blenkernel/intern/image.cc | 5 ++++- source/blender/blenkernel/intern/image_save.cc | 10 +++++----- source/blender/blenkernel/intern/writeffmpeg.c | 2 +- .../compositor/operations/COM_OutputFileMultiViewOperation.cc | 4 ++-- .../compositor/operations/COM_OutputFileMultiViewOperation.h | 4 ++-- .../blender/compositor/operations/COM_OutputFileOperation.cc | 2 +- source/blender/compositor/operations/COM_OutputFileOperation.h | 2 +- source/blender/editors/object/object_bake_api.c | 2 +- source/blender/editors/space_image/image_ops.c | 2 +- source/blender/nodes/NOD_composite.h | 2 +- .../nodes/composite/nodes/node_composite_output_file.cc | 2 +- 14 files changed, 24 insertions(+), 21 deletions(-) diff --git a/source/blender/blenkernel/BKE_image.h b/source/blender/blenkernel/BKE_image.h index 6ec1285af0c..4e622a5708f 100644 --- a/source/blender/blenkernel/BKE_image.h +++ b/source/blender/blenkernel/BKE_image.h @@ -97,7 +97,7 @@ int BKE_imbuf_write(struct ImBuf *ibuf, const char *name, const struct ImageForm */ int BKE_imbuf_write_as(struct ImBuf *ibuf, const char *name, - struct ImageFormatData *imf, + const struct ImageFormatData *imf, bool save_copy); /** diff --git a/source/blender/blenkernel/BKE_image_save.h b/source/blender/blenkernel/BKE_image_save.h index 673a7dffb82..e17136174eb 100644 --- a/source/blender/blenkernel/BKE_image_save.h +++ b/source/blender/blenkernel/BKE_image_save.h @@ -48,14 +48,14 @@ bool BKE_image_save_options_init(ImageSaveOptions *opts, struct ImageUser *iuser, const bool guess_path, const bool save_as_render); -void BKE_image_save_options_update(struct ImageSaveOptions *opts, struct Image *ima); +void BKE_image_save_options_update(struct ImageSaveOptions *opts, const struct Image *ima); void BKE_image_save_options_free(struct ImageSaveOptions *opts); bool BKE_image_save(struct ReportList *reports, struct Main *bmain, struct Image *ima, struct ImageUser *iuser, - struct ImageSaveOptions *opts); + const struct ImageSaveOptions *opts); /* Render saving. */ diff --git a/source/blender/blenkernel/BKE_writeffmpeg.h b/source/blender/blenkernel/BKE_writeffmpeg.h index 3f92d6fa117..736f7548bb4 100644 --- a/source/blender/blenkernel/BKE_writeffmpeg.h +++ b/source/blender/blenkernel/BKE_writeffmpeg.h @@ -68,7 +68,7 @@ void BKE_ffmpeg_filepath_get(char *string, const char *suffix); void BKE_ffmpeg_preset_set(struct RenderData *rd, int preset); -void BKE_ffmpeg_image_type_verify(struct RenderData *rd, struct ImageFormatData *imf); +void BKE_ffmpeg_image_type_verify(struct RenderData *rd, const struct ImageFormatData *imf); bool BKE_ffmpeg_alpha_channel_is_supported(const struct RenderData *rd); void *BKE_ffmpeg_context_create(void); diff --git a/source/blender/blenkernel/intern/image.cc b/source/blender/blenkernel/intern/image.cc index 92e98935e83..6ec1c52c61a 100644 --- a/source/blender/blenkernel/intern/image.cc +++ b/source/blender/blenkernel/intern/image.cc @@ -2475,7 +2475,10 @@ int BKE_imbuf_write(ImBuf *ibuf, const char *name, const ImageFormatData *imf) return ok; } -int BKE_imbuf_write_as(ImBuf *ibuf, const char *name, ImageFormatData *imf, const bool save_copy) +int BKE_imbuf_write_as(ImBuf *ibuf, + const char *name, + const ImageFormatData *imf, + const bool save_copy) { ImBuf ibuf_back = *ibuf; int ok; diff --git a/source/blender/blenkernel/intern/image_save.cc b/source/blender/blenkernel/intern/image_save.cc index 3f6f81845e2..cd86d3f7087 100644 --- a/source/blender/blenkernel/intern/image_save.cc +++ b/source/blender/blenkernel/intern/image_save.cc @@ -204,7 +204,7 @@ bool BKE_image_save_options_init(ImageSaveOptions *opts, return (ibuf != nullptr); } -void BKE_image_save_options_update(ImageSaveOptions *opts, Image *image) +void BKE_image_save_options_update(ImageSaveOptions *opts, const Image *image) { /* Auto update color space when changing save as render and file type. */ if (opts->save_as_render) { @@ -272,7 +272,7 @@ static void image_save_post(ReportList *reports, Image *ima, ImBuf *ibuf, int ok, - ImageSaveOptions *opts, + const ImageSaveOptions *opts, int save_copy, const char *filepath, bool *r_colorspace_changed) @@ -359,7 +359,7 @@ static void imbuf_save_post(ImBuf *ibuf, ImBuf *colormanaged_ibuf) static bool image_save_single(ReportList *reports, Image *ima, ImageUser *iuser, - ImageSaveOptions *opts, + const ImageSaveOptions *opts, bool *r_colorspace_changed) { void *lock; @@ -375,7 +375,7 @@ static bool image_save_single(ReportList *reports, ImBuf *colormanaged_ibuf = nullptr; const bool save_copy = opts->save_copy; const bool save_as_render = opts->save_as_render; - ImageFormatData *imf = &opts->im_format; + const ImageFormatData *imf = &opts->im_format; if (ima->type == IMA_TYPE_R_RESULT) { /* enforce user setting for RGB or RGBA, but skip BW */ @@ -620,7 +620,7 @@ static bool image_save_single(ReportList *reports, } bool BKE_image_save( - ReportList *reports, Main *bmain, Image *ima, ImageUser *iuser, ImageSaveOptions *opts) + ReportList *reports, Main *bmain, Image *ima, ImageUser *iuser, const ImageSaveOptions *opts) { /* For saving a tiled image we need an iuser, so use a local one if there isn't already one. */ ImageUser save_iuser; diff --git a/source/blender/blenkernel/intern/writeffmpeg.c b/source/blender/blenkernel/intern/writeffmpeg.c index 5e11cd0703a..883591e3e87 100644 --- a/source/blender/blenkernel/intern/writeffmpeg.c +++ b/source/blender/blenkernel/intern/writeffmpeg.c @@ -1485,7 +1485,7 @@ void BKE_ffmpeg_preset_set(RenderData *rd, int preset) } } -void BKE_ffmpeg_image_type_verify(RenderData *rd, ImageFormatData *imf) +void BKE_ffmpeg_image_type_verify(RenderData *rd, const ImageFormatData *imf) { int audio = 0; diff --git a/source/blender/compositor/operations/COM_OutputFileMultiViewOperation.cc b/source/blender/compositor/operations/COM_OutputFileMultiViewOperation.cc index 341541b4cdd..760ed94f882 100644 --- a/source/blender/compositor/operations/COM_OutputFileMultiViewOperation.cc +++ b/source/blender/compositor/operations/COM_OutputFileMultiViewOperation.cc @@ -21,7 +21,7 @@ OutputOpenExrSingleLayerMultiViewOperation::OutputOpenExrSingleLayerMultiViewOpe const RenderData *rd, const bNodeTree *tree, DataType datatype, - ImageFormatData *format, + const ImageFormatData *format, const char *path, const char *view_name, const bool save_as_render) @@ -243,7 +243,7 @@ OutputStereoOperation::OutputStereoOperation(const Scene *scene, const RenderData *rd, const bNodeTree *tree, DataType datatype, - ImageFormatData *format, + const ImageFormatData *format, const char *path, const char *name, const char *view_name, diff --git a/source/blender/compositor/operations/COM_OutputFileMultiViewOperation.h b/source/blender/compositor/operations/COM_OutputFileMultiViewOperation.h index 69f4011d340..e36999e5cf1 100644 --- a/source/blender/compositor/operations/COM_OutputFileMultiViewOperation.h +++ b/source/blender/compositor/operations/COM_OutputFileMultiViewOperation.h @@ -22,7 +22,7 @@ class OutputOpenExrSingleLayerMultiViewOperation : public OutputSingleLayerOpera const RenderData *rd, const bNodeTree *tree, DataType datatype, - ImageFormatData *format, + const ImageFormatData *format, const char *path, const char *view_name, bool save_as_render); @@ -57,7 +57,7 @@ class OutputStereoOperation : public OutputSingleLayerOperation { const RenderData *rd, const bNodeTree *tree, DataType datatype, - struct ImageFormatData *format, + const struct ImageFormatData *format, const char *path, const char *name, const char *view_name, diff --git a/source/blender/compositor/operations/COM_OutputFileOperation.cc b/source/blender/compositor/operations/COM_OutputFileOperation.cc index 49de275c256..1d22f3e8cd2 100644 --- a/source/blender/compositor/operations/COM_OutputFileOperation.cc +++ b/source/blender/compositor/operations/COM_OutputFileOperation.cc @@ -204,7 +204,7 @@ OutputSingleLayerOperation::OutputSingleLayerOperation(const Scene *scene, const RenderData *rd, const bNodeTree *tree, DataType datatype, - ImageFormatData *format, + const ImageFormatData *format, const char *path, const char *view_name, const bool save_as_render) diff --git a/source/blender/compositor/operations/COM_OutputFileOperation.h b/source/blender/compositor/operations/COM_OutputFileOperation.h index 875defe00e9..df1d68838d9 100644 --- a/source/blender/compositor/operations/COM_OutputFileOperation.h +++ b/source/blender/compositor/operations/COM_OutputFileOperation.h @@ -35,7 +35,7 @@ class OutputSingleLayerOperation : public MultiThreadedOperation { const RenderData *rd, const bNodeTree *tree, DataType datatype, - ImageFormatData *format, + const ImageFormatData *format, const char *path, const char *view_name, bool save_as_render); diff --git a/source/blender/editors/object/object_bake_api.c b/source/blender/editors/object/object_bake_api.c index 58daf753679..a664d93bb2e 100644 --- a/source/blender/editors/object/object_bake_api.c +++ b/source/blender/editors/object/object_bake_api.c @@ -324,7 +324,7 @@ static bool write_external_bake_pixels(const char *filepath, const int height, const int margin, const int margin_type, - ImageFormatData *im_format, + ImageFormatData const *im_format, const bool is_noncolor, Mesh const *mesh_eval, char const *uv_layer, diff --git a/source/blender/editors/space_image/image_ops.c b/source/blender/editors/space_image/image_ops.c index 49489b696ef..8b975ee6173 100644 --- a/source/blender/editors/space_image/image_ops.c +++ b/source/blender/editors/space_image/image_ops.c @@ -1830,7 +1830,7 @@ static void image_save_options_from_op(Main *bmain, ImageSaveOptions *opts, wmOp } static bool save_image_op( - Main *bmain, Image *ima, ImageUser *iuser, wmOperator *op, ImageSaveOptions *opts) + Main *bmain, Image *ima, ImageUser *iuser, wmOperator *op, const ImageSaveOptions *opts) { WM_cursor_wait(true); diff --git a/source/blender/nodes/NOD_composite.h b/source/blender/nodes/NOD_composite.h index 5d782674f16..58126c5722d 100644 --- a/source/blender/nodes/NOD_composite.h +++ b/source/blender/nodes/NOD_composite.h @@ -169,7 +169,7 @@ void ntreeCompositClearTags(struct bNodeTree *ntree); struct bNodeSocket *ntreeCompositOutputFileAddSocket(struct bNodeTree *ntree, struct bNode *node, const char *name, - struct ImageFormatData *im_format); + const struct ImageFormatData *im_format); int ntreeCompositOutputFileRemoveActiveSocket(struct bNodeTree *ntree, struct bNode *node); void ntreeCompositOutputFileSetPath(struct bNode *node, 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 f1621f83ac3..84235b085a4 100644 --- a/source/blender/nodes/composite/nodes/node_composite_output_file.cc +++ b/source/blender/nodes/composite/nodes/node_composite_output_file.cc @@ -114,7 +114,7 @@ void ntreeCompositOutputFileUniqueLayer(ListBase *list, bNodeSocket *ntreeCompositOutputFileAddSocket(bNodeTree *ntree, bNode *node, const char *name, - ImageFormatData *im_format) + const ImageFormatData *im_format) { NodeImageMultiFile *nimf = (NodeImageMultiFile *)node->storage; bNodeSocket *sock = nodeAddStaticSocket( -- cgit v1.2.3 From c8e8f107bf82fb56f49101e1098f4c697b16cfeb Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Fri, 15 Jul 2022 14:47:18 +1000 Subject: Fix T99711: Eternal loop reading blend file thumbnail Account for negative BHead length (already handled by blend file loading). --- source/blender/blendthumb/src/blendthumb_extract.cc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/source/blender/blendthumb/src/blendthumb_extract.cc b/source/blender/blendthumb/src/blendthumb_extract.cc index 369da559fc8..163197c8b67 100644 --- a/source/blender/blendthumb/src/blendthumb_extract.cc +++ b/source/blender/blendthumb/src/blendthumb_extract.cc @@ -121,6 +121,9 @@ static eThumbStatus blendthumb_extract_from_file_impl(FileReader *file, while (file_read(file, bhead_data, bhead_size)) { /* Parse type and size from `BHead`. */ const int32_t block_size = bytes_to_native_i32(&bhead_data[4], endian_switch); + if (UNLIKELY(block_size < 0)) { + return BT_INVALID_THUMB; + } /* We're looking for the thumbnail, so skip any other block. */ switch (*((int32_t *)bhead_data)) { -- cgit v1.2.3 From d14d5705807c8216f3cfb1de3af6f448a1401599 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Fri, 15 Jul 2022 14:52:32 +1000 Subject: blend_render_info: add check for negative BHead length (corrupt file) Without this check, corrupt files would raise a Python exception, now early exit with a useful error. --- release/scripts/modules/blend_render_info.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/release/scripts/modules/blend_render_info.py b/release/scripts/modules/blend_render_info.py index 37c5f6dd3ba..6b45a6f7e72 100755 --- a/release/scripts/modules/blend_render_info.py +++ b/release/scripts/modules/blend_render_info.py @@ -93,6 +93,11 @@ def _read_blend_rend_chunk_from_file(blendfile, filepath): break sizeof_data_left = struct.unpack('>i' if is_big_endian else ' Date: Fri, 15 Jul 2022 15:36:21 +1000 Subject: GHOST/Wayland: fix error setting the cursor scale Calculate a scale that's compatible with the cursor size. Needed so the cursor is always a multiple of scale. --- intern/ghost/intern/GHOST_SystemWayland.cpp | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/intern/ghost/intern/GHOST_SystemWayland.cpp b/intern/ghost/intern/GHOST_SystemWayland.cpp index 099792a4d06..4124cd059b3 100644 --- a/intern/ghost/intern/GHOST_SystemWayland.cpp +++ b/intern/ghost/intern/GHOST_SystemWayland.cpp @@ -3275,6 +3275,23 @@ static void cursor_buffer_hide(const input_t *input) } } +/** + * Needed to ensure the cursor size is always a multiple of scale. + */ +static int cursor_buffer_compatible_scale_from_image(const struct wl_cursor_image *wl_image, + int scale) +{ + const int32_t image_size_x = int32_t(wl_image->width); + const int32_t image_size_y = int32_t(wl_image->height); + while (scale > 1) { + if ((image_size_x % scale) == 0 && (image_size_y % scale) == 0) { + break; + } + scale -= 1; + } + return scale; +} + static void cursor_buffer_set_surface_impl(const input_t *input, wl_buffer *buffer, struct wl_surface *wl_surface, @@ -3301,7 +3318,8 @@ static void cursor_buffer_set(const input_t *input, wl_buffer *buffer) /* This is a requirement of WAYLAND, when this isn't the case, * it causes Blender's window to close intermittently. */ if (input->wl_pointer) { - const int scale = c->is_custom ? c->custom_scale : input->pointer.theme_scale; + const int scale = cursor_buffer_compatible_scale_from_image( + wl_image, c->is_custom ? c->custom_scale : input->pointer.theme_scale); const int32_t hotspot_x = int32_t(wl_image->hotspot_x) / scale; const int32_t hotspot_y = int32_t(wl_image->hotspot_y) / scale; cursor_buffer_set_surface_impl(input, buffer, c->wl_surface, scale); @@ -3314,7 +3332,8 @@ static void cursor_buffer_set(const input_t *input, wl_buffer *buffer) /* Set the cursor for all tablet tools as well. */ if (!input->tablet_tools.empty()) { - const int scale = c->is_custom ? c->custom_scale : input->tablet.theme_scale; + const int scale = cursor_buffer_compatible_scale_from_image( + wl_image, c->is_custom ? c->custom_scale : input->tablet.theme_scale); const int32_t hotspot_x = int32_t(wl_image->hotspot_x) / scale; const int32_t hotspot_y = int32_t(wl_image->hotspot_y) / scale; for (struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2 : input->tablet_tools) { -- cgit v1.2.3 From d8094f9212c1703e6230825780c06beb630f6d19 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Fri, 15 Jul 2022 15:42:24 +1000 Subject: GHOST/Wayland: partial support for updating the UI scale Partial support for changing the UI scale while Blender is open. The scale is set but issues with the window size not updating remain. --- intern/ghost/intern/GHOST_SystemWayland.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/intern/ghost/intern/GHOST_SystemWayland.cpp b/intern/ghost/intern/GHOST_SystemWayland.cpp index 4124cd059b3..d7520f1243f 100644 --- a/intern/ghost/intern/GHOST_SystemWayland.cpp +++ b/intern/ghost/intern/GHOST_SystemWayland.cpp @@ -2597,7 +2597,17 @@ static void output_handle_done(void *data, struct wl_output * /*wl_output*/) static void output_handle_scale(void *data, struct wl_output * /*wl_output*/, const int32_t factor) { + CLOG_INFO(LOG, 2, "scale"); static_cast(data)->scale = factor; + + if (window_manager) { + for (GHOST_IWindow *iwin : window_manager->getWindows()) { + GHOST_WindowWayland *win = static_cast(iwin); + win->outputs_changed_update_scale(); + /* TODO(@campbellbarton): support refreshing the UI when the DPI changes. + * There are glitches when resizing the monitor which would be nice to solve. */ + } + } } static const struct wl_output_listener output_listener = { -- cgit v1.2.3 From 8fd2b79ca190946fe95d915d19abbe9ddac895e9 Mon Sep 17 00:00:00 2001 From: Aras Pranckevicius Date: Fri, 15 Jul 2022 10:20:04 +0300 Subject: BLI_bitmap: ability to declare by-value, and function to find lowest unset bit In preparation for a larger change (D14162), some BLI_bitmap functionality that could be submitted separately: - Ability to declare a fixed size bitmap by-value, without extra memory allocation: BLI_BITMAP_DECLARE - Function to find the index of lowest unset bit: BLI_bitmap_find_first_unset - Test coverage of the above. Reviewed By: Campbell Barton, Bastien Montagne Differential Revision: https://developer.blender.org/D15454 --- source/blender/blenlib/BLI_bitmap.h | 11 ++++++ source/blender/blenlib/CMakeLists.txt | 1 + source/blender/blenlib/intern/bitmap.c | 20 +++++++++++ source/blender/blenlib/tests/BLI_bitmap_test.cc | 46 +++++++++++++++++++++++++ 4 files changed, 78 insertions(+) create mode 100644 source/blender/blenlib/tests/BLI_bitmap_test.cc diff --git a/source/blender/blenlib/BLI_bitmap.h b/source/blender/blenlib/BLI_bitmap.h index 19d8525311c..26dc6c7ffc9 100644 --- a/source/blender/blenlib/BLI_bitmap.h +++ b/source/blender/blenlib/BLI_bitmap.h @@ -53,6 +53,11 @@ typedef unsigned int BLI_bitmap; (CHECK_TYPE_INLINE(_mem, MemArena *), \ ((BLI_bitmap *)BLI_memarena_calloc(_mem, BLI_BITMAP_SIZE(_num)))) +/** + * Declares a bitmap as a variable. + */ +#define BLI_BITMAP_DECLARE(_name, _num) BLI_bitmap _name[_BITMAP_NUM_BLOCKS(_num)] = {} + /** * Get the value of a single bit at '_index'. */ @@ -137,6 +142,12 @@ void BLI_bitmap_and_all(BLI_bitmap *dst, const BLI_bitmap *src, size_t bits); */ void BLI_bitmap_or_all(BLI_bitmap *dst, const BLI_bitmap *src, size_t bits); +/** + * Find index of the lowest unset bit. + * Returns -1 if all the bits are set. + */ +int BLI_bitmap_find_first_unset(const BLI_bitmap *bitmap, size_t bits); + #ifdef __cplusplus } #endif diff --git a/source/blender/blenlib/CMakeLists.txt b/source/blender/blenlib/CMakeLists.txt index 95b4987596e..d39a586206f 100644 --- a/source/blender/blenlib/CMakeLists.txt +++ b/source/blender/blenlib/CMakeLists.txt @@ -424,6 +424,7 @@ if(WITH_GTESTS) tests/BLI_array_store_test.cc tests/BLI_array_test.cc tests/BLI_array_utils_test.cc + tests/BLI_bitmap_test.cc tests/BLI_bounds_test.cc tests/BLI_color_test.cc tests/BLI_cpp_type_test.cc diff --git a/source/blender/blenlib/intern/bitmap.c b/source/blender/blenlib/intern/bitmap.c index 7fcbc31c066..2cc2fbc3e2f 100644 --- a/source/blender/blenlib/intern/bitmap.c +++ b/source/blender/blenlib/intern/bitmap.c @@ -11,6 +11,7 @@ #include #include "BLI_bitmap.h" +#include "BLI_math_bits.h" #include "BLI_utildefines.h" void BLI_bitmap_set_all(BLI_bitmap *bitmap, bool set, size_t bits) @@ -46,3 +47,22 @@ void BLI_bitmap_or_all(BLI_bitmap *dst, const BLI_bitmap *src, size_t bits) dst[i] |= src[i]; } } + +int BLI_bitmap_find_first_unset(const BLI_bitmap *bitmap, const size_t bits) +{ + const size_t blocks_num = _BITMAP_NUM_BLOCKS(bits); + int result = -1; + /* Skip over completely set blocks. */ + int index = 0; + while (index < blocks_num && bitmap[index] == ~0u) { + index++; + } + if (index < blocks_num) { + /* Found a partially used block: find the lowest unused bit. */ + const uint m = ~bitmap[index]; + BLI_assert(m != 0); + const uint bit_index = bitscan_forward_uint(m); + result = bit_index + (index << _BITMAP_POWER); + } + return result; +} diff --git a/source/blender/blenlib/tests/BLI_bitmap_test.cc b/source/blender/blenlib/tests/BLI_bitmap_test.cc new file mode 100644 index 00000000000..fb9e03e3136 --- /dev/null +++ b/source/blender/blenlib/tests/BLI_bitmap_test.cc @@ -0,0 +1,46 @@ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "BLI_bitmap.h" +#include "testing/testing.h" + +namespace blender::tests { + +TEST(bitmap, empty_is_all_unset) +{ + BLI_BITMAP_DECLARE(bitmap, 10); + for (int i = 0; i < 10; ++i) { + EXPECT_FALSE(BLI_BITMAP_TEST_BOOL(bitmap, i)); + } +} + +TEST(bitmap, find_first_unset_empty) +{ + BLI_BITMAP_DECLARE(bitmap, 10); + EXPECT_EQ(0, BLI_bitmap_find_first_unset(bitmap, 10)); +} + +TEST(bitmap, find_first_unset_full) +{ + BLI_BITMAP_DECLARE(bitmap, 10); + BLI_bitmap_flip_all(bitmap, 10); + EXPECT_EQ(-1, BLI_bitmap_find_first_unset(bitmap, 10)); +} + +TEST(bitmap, find_first_unset_middle) +{ + BLI_BITMAP_DECLARE(bitmap, 100); + BLI_bitmap_flip_all(bitmap, 100); + /* Turn some bits off */ + BLI_BITMAP_DISABLE(bitmap, 53); + BLI_BITMAP_DISABLE(bitmap, 81); + BLI_BITMAP_DISABLE(bitmap, 85); + BLI_BITMAP_DISABLE(bitmap, 86); + + /* Find lowest unset bit, and set it. */ + EXPECT_EQ(53, BLI_bitmap_find_first_unset(bitmap, 100)); + BLI_BITMAP_ENABLE(bitmap, 53); + /* Now should find the next lowest bit. */ + EXPECT_EQ(81, BLI_bitmap_find_first_unset(bitmap, 100)); +} + +} // namespace blender::tests -- cgit v1.2.3 From 63ea0f7581faeeb629b040b7a4dc661bc84c789c Mon Sep 17 00:00:00 2001 From: Aras Pranckevicius Date: Fri, 15 Jul 2022 10:21:27 +0300 Subject: BLI_bitmap: fix _BITMAP_NUM_BLOCKS to not over-count by one block For bit counts that were exact multiple of block size, the macro was computing one block too much. Reviewed By: Campbell Barton, Bastien Montagne Differential Revision: https://developer.blender.org/D15454 --- source/blender/blenlib/BLI_bitmap.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/blenlib/BLI_bitmap.h b/source/blender/blenlib/BLI_bitmap.h index 26dc6c7ffc9..973cc5c3d1e 100644 --- a/source/blender/blenlib/BLI_bitmap.h +++ b/source/blender/blenlib/BLI_bitmap.h @@ -27,7 +27,7 @@ typedef unsigned int BLI_bitmap; /** * Number of blocks needed to hold '_num' bits. */ -#define _BITMAP_NUM_BLOCKS(_num) (((_num) >> _BITMAP_POWER) + 1) +#define _BITMAP_NUM_BLOCKS(_num) (((_num) + _BITMAP_MASK) >> _BITMAP_POWER) /** * Size (in bytes) used to hold '_num' bits. -- cgit v1.2.3 From c2715dc41664781924f610d9c5c03362ccb29446 Mon Sep 17 00:00:00 2001 From: Ray Molenkamp Date: Fri, 15 Jul 2022 10:51:46 +0200 Subject: GPU: Remove USD dependency from shader_builder. Dependency was added as shader builder depended to blenkernel as an umbrella, in stead of adding the actual dependencies it required. --- CMakeLists.txt | 6 ------ source/blender/gpu/CMakeLists.txt | 18 ++++++++---------- 2 files changed, 8 insertions(+), 16 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 6b7ee7f1887..33064864be6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1340,12 +1340,6 @@ else() list(APPEND GL_DEFINITIONS -DWITH_GL_PROFILE_CORE) endif() -if (WITH_GPU_BUILDTIME_SHADER_BUILDER AND WITH_USD) - message(FATAL_ERROR - "Unable to compile WITH_GPU_BUILDTIME_SHADER_BUILDER and WITH_USD." - ) -endif() - #----------------------------------------------------------------------------- # Configure Metal. if (WITH_METAL_BACKEND) diff --git a/source/blender/gpu/CMakeLists.txt b/source/blender/gpu/CMakeLists.txt index 1c883f7fa41..0cb92e02515 100644 --- a/source/blender/gpu/CMakeLists.txt +++ b/source/blender/gpu/CMakeLists.txt @@ -570,11 +570,7 @@ if(WITH_GPU_BUILDTIME_SHADER_BUILDER) ) setup_platform_linker_flags(shader_builder) - - target_link_libraries(shader_builder PUBLIC - bf_blenkernel - buildinfoobj - ) + target_link_libraries(shader_builder PUBLIC buildinfoobj) else() if(WIN32) # We can re-use the manifest from tests.exe here since it's @@ -589,12 +585,14 @@ if(WITH_GPU_BUILDTIME_SHADER_BUILDER) ${MANIFEST} ) - target_link_libraries(shader_builder PUBLIC - bf_blenkernel - ${PLATFORM_LINKLIBS} - ) endif() - + target_link_libraries(shader_builder PUBLIC + bf_gpu + bf_intern_clog + bf_blenlib + bf_intern_ghost + ${PLATFORM_LINKLIBS} + ) target_include_directories(shader_builder PRIVATE ${INC} ${CMAKE_CURRENT_BINARY_DIR}) set(SRC_BAKED_CREATE_INFOS_FILE ${CMAKE_CURRENT_BINARY_DIR}/shader_baked.hh) -- cgit v1.2.3 From ca1daf4cdaaa9a806869586efe6a752b502c156c Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Wed, 13 Jul 2022 10:22:04 +0200 Subject: Fix an increasing bottleneck when key press operator is too slow The goal of this change is to fix an increasing bottleneck of the event queue handling when there is an operator bound to a key press event and is taking longer to finish than a key-repeat speed on the system. Practical example of when it happens is the marker tracking operator in a single-frame track mode. Quite often artists will hold down Alt-arrow to track a segment of footage which seems trivial to track. The issue arises when the Alt-arrow is released: prior to this change it is was possible that more frames will be tracked. It also seems that redraws are less smooth. It is a bit hard to make easily shareable computer-independent test case. Instead, a synthetic case can be reproduced by adding a 50 ms sleep in the `text_move_exec()`. In such synthetic case open a long text in the text editor and hold left/right arrow button to navigate the cursor. The observed behavior is that seemingly redraws happen less and less often and cursor travels longer and longer distances between redraws. The cursor will also keep moving after the buttons has been released. The proposed solution is to ignore sequential key-press events from being added to the event queue. This seems to be the least intrusive and the most safe approach: - If the operator is fast enough there will be no multiple press events in the queue in both prior and after of this change. - If the operator is slow enough, clicking the button multiple times (i.e. clicking arrow button 3 times in a heavy shot will change the scene frame by exactly 3 frames because no events are ignored in this case). - Only do it for key press events, keeping mouse and tabled behavior unchanged which is crucial for the paint mode. Note that this is a bit different from the key repeat tracking and filtering which is already implemented for keymaps as here we only want to avoid the event queue build-up and do want to ignore all repeat events. In other words: we do want to handle as many key presses as the operator performance allows it without clogging anything. A possible extension to this change could be a key press counter, so that instead of ignoring the event we merge it into the last event in the queue, incrementing some counter. This way if some operator really needs to know exact number of key repeats it can still access it. Differential Revision: https://developer.blender.org/D15444 --- .../windowmanager/intern/wm_event_system.cc | 51 +++++++++++++++++++++- 1 file changed, 50 insertions(+), 1 deletion(-) diff --git a/source/blender/windowmanager/intern/wm_event_system.cc b/source/blender/windowmanager/intern/wm_event_system.cc index 757c3aae00c..79b303364d8 100644 --- a/source/blender/windowmanager/intern/wm_event_system.cc +++ b/source/blender/windowmanager/intern/wm_event_system.cc @@ -5133,6 +5133,53 @@ static void wm_event_state_update_and_click_set(wmEvent *event, wm_event_state_update_and_click_set_ex(event, event_state, is_keyboard, check_double_click); } +/* Returns true when the two events corresponds to a press of the same key with the same modifiers. + */ +static bool wm_event_is_same_key_press(const wmEvent &event_a, const wmEvent &event_b) +{ + if (event_a.val != KM_PRESS || event_b.val != KM_PRESS) { + return false; + } + + if (event_a.modifier != event_b.modifier || event_a.type != event_b.type) { + return false; + } + + return true; +} + +/** + * Returns true if the event is a key press event which is to be ignored and not added to the event + * queue. + * + * A key press event will be ignored if there is already matched key press in the queue. + * This avoids the event queue "clogging" in the situations when there is an operator bound to a + * key press event and the execution time of the operator is longer than the key repeat. + */ +static bool wm_event_is_ignorable_key_press(const wmWindow *win, const wmEvent &event) +{ + if (BLI_listbase_is_empty(&win->event_queue)) { + /* If the queue is empty never ignore the event. + * Empty queue at this point means that the events are handled fast enough, and there is no + * reason to ignore anything. */ + return false; + } + + if ((event.flag & WM_EVENT_IS_REPEAT) == 0) { + /* Only ignore repeat events from the keyboard, and allow accumulation of non-repeat events. + * + * The goal of this check is to allow events coming from a keyboard macro software, which can + * generate events quicker than the main loop handles them. In this case we want all events to + * be handled (unless the keyboard macro software tags them as repeat) because otherwise it + * will become impossible to get reliable results of automated events testing. */ + return false; + } + + const wmEvent &last_event = *reinterpret_cast(win->event_queue.last); + + return wm_event_is_same_key_press(last_event, event); +} + void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, void *customdata) { if (UNLIKELY(G.f & G_FLAG_EVENT_SIMULATE)) { @@ -5439,7 +5486,9 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, void G.is_break = true; } - wm_event_add(win, &event); + if (!wm_event_is_ignorable_key_press(win, event)) { + wm_event_add(win, &event); + } break; } -- cgit v1.2.3 From 862170c0b1ed1fb4f967aead1979568c86b141e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cle=CC=81ment=20Foucault?= Date: Fri, 15 Jul 2022 10:36:24 +0200 Subject: Cleanup: GPU: Replace NULL by nullptr from C++ files --- source/blender/gpu/intern/gpu_context_private.hh | 20 ++++++++++---------- source/blender/gpu/intern/gpu_immediate_private.hh | 6 +++--- source/blender/gpu/intern/gpu_shader_interface.hh | 14 +++++++------- .../blender/gpu/intern/gpu_storage_buffer_private.hh | 2 +- .../blender/gpu/intern/gpu_uniform_buffer_private.hh | 2 +- .../blender/gpu/intern/gpu_vertex_buffer_private.hh | 2 +- source/blender/gpu/opengl/gl_batch.hh | 4 ++-- source/blender/gpu/opengl/gl_debug.cc | 2 +- source/blender/gpu/opengl/gl_framebuffer.hh | 4 ++-- source/blender/gpu/opengl/gl_texture.hh | 2 +- 10 files changed, 29 insertions(+), 29 deletions(-) diff --git a/source/blender/gpu/intern/gpu_context_private.hh b/source/blender/gpu/intern/gpu_context_private.hh index 9cdf0075632..f823a92893c 100644 --- a/source/blender/gpu/intern/gpu_context_private.hh +++ b/source/blender/gpu/intern/gpu_context_private.hh @@ -28,11 +28,11 @@ namespace blender::gpu { class Context { public: /** State management */ - Shader *shader = NULL; - FrameBuffer *active_fb = NULL; - GPUMatrixState *matrix_state = NULL; - StateManager *state_manager = NULL; - Immediate *imm = NULL; + Shader *shader = nullptr; + FrameBuffer *active_fb = nullptr; + GPUMatrixState *matrix_state = nullptr; + StateManager *state_manager = nullptr; + Immediate *imm = nullptr; /** * All 4 window frame-buffers. @@ -41,10 +41,10 @@ class Context { * Front frame-buffers contains (in principle, but not always) the last frame color. * Default frame-buffer is back_left. */ - FrameBuffer *back_left = NULL; - FrameBuffer *front_left = NULL; - FrameBuffer *back_right = NULL; - FrameBuffer *front_right = NULL; + FrameBuffer *back_left = nullptr; + FrameBuffer *front_left = nullptr; + FrameBuffer *back_right = nullptr; + FrameBuffer *front_right = nullptr; DebugStack debug_stack; @@ -52,7 +52,7 @@ class Context { /** Thread on which this context is active. */ pthread_t thread_; bool is_active_; - /** Avoid including GHOST headers. Can be NULL for off-screen contexts. */ + /** Avoid including GHOST headers. Can be nullptr for off-screen contexts. */ void *ghost_window_; public: diff --git a/source/blender/gpu/intern/gpu_immediate_private.hh b/source/blender/gpu/intern/gpu_immediate_private.hh index 6c50fa01071..74ebbdc7ae3 100644 --- a/source/blender/gpu/intern/gpu_immediate_private.hh +++ b/source/blender/gpu/intern/gpu_immediate_private.hh @@ -19,7 +19,7 @@ namespace blender::gpu { class Immediate { public: /** Pointer to the mapped buffer data for the current vertex. */ - uchar *vertex_data = NULL; + uchar *vertex_data = nullptr; /** Current vertex index. */ uint vertex_idx = 0; /** Length of the buffer in vertices. */ @@ -32,12 +32,12 @@ class Immediate { /** Current draw call specification. */ GPUPrimType prim_type = GPU_PRIM_NONE; GPUVertFormat vertex_format = {}; - GPUShader *shader = NULL; + GPUShader *shader = nullptr; /** Enforce strict vertex count (disabled when using #immBeginAtMost). */ bool strict_vertex_len = true; /** Batch in construction when using #immBeginBatch. */ - GPUBatch *batch = NULL; + GPUBatch *batch = nullptr; /** Wide Line workaround. */ diff --git a/source/blender/gpu/intern/gpu_shader_interface.hh b/source/blender/gpu/intern/gpu_shader_interface.hh index ac78af38fcc..60344757b43 100644 --- a/source/blender/gpu/intern/gpu_shader_interface.hh +++ b/source/blender/gpu/intern/gpu_shader_interface.hh @@ -39,9 +39,9 @@ class ShaderInterface { /* TODO(fclem): should be protected. */ public: /** Flat array. In this order: Attributes, Ubos, Uniforms. */ - ShaderInput *inputs_ = NULL; + ShaderInput *inputs_ = nullptr; /** Buffer containing all inputs names separated by '\0'. */ - char *name_buffer_ = NULL; + char *name_buffer_ = nullptr; /** Input counts inside input array. */ uint attr_len_ = 0; uint ubo_len_ = 0; @@ -187,7 +187,7 @@ inline const char *ShaderInterface::builtin_uniform_name(GPUUniformBuiltin u) return "srgbTarget"; default: - return NULL; + return nullptr; } } @@ -208,7 +208,7 @@ inline const char *ShaderInterface::builtin_uniform_block_name(GPUUniformBlockBu case GPU_UNIFORM_BLOCK_DRW_INFOS: return "drw_infos"; default: - return NULL; + return nullptr; } } @@ -258,7 +258,7 @@ inline const ShaderInput *ShaderInterface::input_lookup(const ShaderInput *const return inputs + i; /* not found */ } } - return NULL; /* not found */ + return nullptr; /* not found */ } /* This is a bit dangerous since we could have a hash collision. @@ -268,7 +268,7 @@ inline const ShaderInput *ShaderInterface::input_lookup(const ShaderInput *const return inputs + i; } } - return NULL; /* not found */ + return nullptr; /* not found */ } inline const ShaderInput *ShaderInterface::input_lookup(const ShaderInput *const inputs, @@ -281,7 +281,7 @@ inline const ShaderInput *ShaderInterface::input_lookup(const ShaderInput *const return inputs + i; } } - return NULL; /* not found */ + return nullptr; /* not found */ } } // namespace blender::gpu diff --git a/source/blender/gpu/intern/gpu_storage_buffer_private.hh b/source/blender/gpu/intern/gpu_storage_buffer_private.hh index 091e6c2d386..9baec0c2a77 100644 --- a/source/blender/gpu/intern/gpu_storage_buffer_private.hh +++ b/source/blender/gpu/intern/gpu_storage_buffer_private.hh @@ -29,7 +29,7 @@ class StorageBuf { /** Data size in bytes. */ size_t size_in_bytes_; /** Continuous memory block to copy to GPU. This data is owned by the StorageBuf. */ - void *data_ = NULL; + void *data_ = nullptr; /** Debugging name */ char name_[DEBUG_NAME_LEN]; diff --git a/source/blender/gpu/intern/gpu_uniform_buffer_private.hh b/source/blender/gpu/intern/gpu_uniform_buffer_private.hh index 6e3285b6fef..e3d70634ce1 100644 --- a/source/blender/gpu/intern/gpu_uniform_buffer_private.hh +++ b/source/blender/gpu/intern/gpu_uniform_buffer_private.hh @@ -29,7 +29,7 @@ class UniformBuf { /** Data size in bytes. */ size_t size_in_bytes_; /** Continuous memory block to copy to GPU. This data is owned by the UniformBuf. */ - void *data_ = NULL; + void *data_ = nullptr; /** Debugging name */ char name_[DEBUG_NAME_LEN]; diff --git a/source/blender/gpu/intern/gpu_vertex_buffer_private.hh b/source/blender/gpu/intern/gpu_vertex_buffer_private.hh index 7a0b53cf958..a7920bacaec 100644 --- a/source/blender/gpu/intern/gpu_vertex_buffer_private.hh +++ b/source/blender/gpu/intern/gpu_vertex_buffer_private.hh @@ -29,7 +29,7 @@ class VertBuf { /** Status flag. */ GPUVertBufStatus flag = GPU_VERTBUF_INVALID; /** NULL indicates data in VRAM (unmapped) */ - uchar *data = NULL; + uchar *data = nullptr; protected: /** Usage hint for GL optimization. */ diff --git a/source/blender/gpu/opengl/gl_batch.hh b/source/blender/gpu/opengl/gl_batch.hh index a25e495b3b1..1a18572c683 100644 --- a/source/blender/gpu/opengl/gl_batch.hh +++ b/source/blender/gpu/opengl/gl_batch.hh @@ -35,9 +35,9 @@ class GLShaderInterface; class GLVaoCache { private: /** Context for which the vao_cache_ was generated. */ - GLContext *context_ = NULL; + GLContext *context_ = nullptr; /** Last interface this batch was drawn with. */ - GLShaderInterface *interface_ = NULL; + GLShaderInterface *interface_ = nullptr; /** Cached VAO for the last interface. */ GLuint vao_id_ = 0; /** Used when arb_base_instance is not supported. */ diff --git a/source/blender/gpu/opengl/gl_debug.cc b/source/blender/gpu/opengl/gl_debug.cc index f82138e0d65..79b28642a67 100644 --- a/source/blender/gpu/opengl/gl_debug.cc +++ b/source/blender/gpu/opengl/gl_debug.cc @@ -189,7 +189,7 @@ void check_gl_error(const char *info) case err: { \ char msg[256]; \ SNPRINTF(msg, "%s : %s", #err, info); \ - debug_callback(0, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH, 0, msg, NULL); \ + debug_callback(0, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH, 0, msg, nullptr); \ break; \ } diff --git a/source/blender/gpu/opengl/gl_framebuffer.hh b/source/blender/gpu/opengl/gl_framebuffer.hh index 224c7a92c0a..2dc0936d0fe 100644 --- a/source/blender/gpu/opengl/gl_framebuffer.hh +++ b/source/blender/gpu/opengl/gl_framebuffer.hh @@ -30,9 +30,9 @@ class GLFrameBuffer : public FrameBuffer { /** OpenGL handle. */ GLuint fbo_id_ = 0; /** Context the handle is from. Frame-buffers are not shared across contexts. */ - GLContext *context_ = NULL; + GLContext *context_ = nullptr; /** State Manager of the same contexts. */ - GLStateManager *state_manager_ = NULL; + GLStateManager *state_manager_ = nullptr; /** Copy of the GL state. Contains ONLY color attachments enums for slot binding. */ GLenum gl_attachments_[GPU_FB_MAX_COLOR_ATTACHMENT]; /** Internal frame-buffers are immutable. */ diff --git a/source/blender/gpu/opengl/gl_texture.hh b/source/blender/gpu/opengl/gl_texture.hh index e5b879f1f15..aeb9fc0e6b7 100644 --- a/source/blender/gpu/opengl/gl_texture.hh +++ b/source/blender/gpu/opengl/gl_texture.hh @@ -33,7 +33,7 @@ class GLTexture : public Texture { /** opengl identifier for texture. */ GLuint tex_id_ = 0; /** Legacy workaround for texture copy. Created when using framebuffer_get(). */ - struct GPUFrameBuffer *framebuffer_ = NULL; + struct GPUFrameBuffer *framebuffer_ = nullptr; /** True if this texture is bound to at least one texture unit. */ /* TODO(fclem): How do we ensure thread safety here? */ bool is_bound_ = false; -- cgit v1.2.3 From 98f688ac42fa6a59b618de39a2f0b85f1f675160 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cle=CC=81ment=20Foucault?= Date: Fri, 15 Jul 2022 11:03:42 +0200 Subject: GPU: Fix shader builder on hardware that does not have all features --- .../blender/gpu/intern/gpu_shader_create_info.cc | 5 ++++- .../blender/gpu/intern/gpu_shader_create_info.hh | 25 ++++++++++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/source/blender/gpu/intern/gpu_shader_create_info.cc b/source/blender/gpu/intern/gpu_shader_create_info.cc index 16ce4f7723e..bc0731862cb 100644 --- a/source/blender/gpu/intern/gpu_shader_create_info.cc +++ b/source/blender/gpu/intern/gpu_shader_create_info.cc @@ -333,8 +333,11 @@ bool gpu_shader_create_info_compile_all() int skipped = 0; int total = 0; for (ShaderCreateInfo *info : g_create_infos->values()) { + info->finalize(); if (info->do_static_compilation_) { - if (GPU_compute_shader_support() == false && info->compute_source_ != nullptr) { + if ((GPU_compute_shader_support() == false && info->compute_source_ != nullptr) || + (GPU_shader_image_load_store_support() == false && info->has_resource_image()) || + (GPU_shader_storage_buffer_objects_support() == false && info->has_resource_storage())) { skipped++; continue; } diff --git a/source/blender/gpu/intern/gpu_shader_create_info.hh b/source/blender/gpu/intern/gpu_shader_create_info.hh index 4927ef75a75..8e05412d0ee 100644 --- a/source/blender/gpu/intern/gpu_shader_create_info.hh +++ b/source/blender/gpu/intern/gpu_shader_create_info.hh @@ -872,6 +872,31 @@ struct ShaderCreateInfo { return stream; } + bool has_resource_type(Resource::BindType bind_type) const + { + for (auto &res : batch_resources_) { + if (res.bind_type == bind_type) { + return true; + } + } + for (auto &res : pass_resources_) { + if (res.bind_type == bind_type) { + return true; + } + } + return false; + } + + bool has_resource_image() const + { + return has_resource_type(Resource::BindType::IMAGE); + } + + bool has_resource_storage() const + { + return has_resource_type(Resource::BindType::STORAGE_BUFFER); + } + /** \} */ #undef TEST_EQUAL -- cgit v1.2.3 From f4d7ea2cf61ed30ac15adc7966a08f61846d6f46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cle=CC=81ment=20Foucault?= Date: Fri, 15 Jul 2022 11:15:59 +0200 Subject: Fix T99606: Regression: TexCoordinate losing precision far away from origin Same root cause as T99128. The fix also needed to be done in another place. --- source/blender/draw/engines/eevee/shaders/surface_lib.glsl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/draw/engines/eevee/shaders/surface_lib.glsl b/source/blender/draw/engines/eevee/shaders/surface_lib.glsl index 8e1bafe8d92..80c6b935187 100644 --- a/source/blender/draw/engines/eevee/shaders/surface_lib.glsl +++ b/source/blender/draw/engines/eevee/shaders/surface_lib.glsl @@ -97,7 +97,7 @@ GlobalData init_globals(void) GlobalData surf; # if defined(WORLD_BACKGROUND) || defined(PROBE_CAPTURE) - surf.P = -cameraVec(worldPosition); + surf.P = transform_direction(ViewMatrixInverse, viewCameraVec(viewPosition)); surf.N = surf.Ng = -surf.P; surf.ray_length = 0.0; # else -- cgit v1.2.3 From 8e1323f6330937a964c3776431bab02c2cfb6e2f Mon Sep 17 00:00:00 2001 From: Martijn Versteegh Date: Fri, 15 Jul 2022 11:22:10 +0200 Subject: Fix: Move DRW_shgroup_add_material_resources(grp, mat) to after the null-check for grp. Reviewed By: fclem Maniphest Tasks: T99646 Differential Revision: https://developer.blender.org/D15436 --- source/blender/draw/engines/eevee/eevee_volumes.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/source/blender/draw/engines/eevee/eevee_volumes.c b/source/blender/draw/engines/eevee/eevee_volumes.c index 8223df22ee9..533e71b9b32 100644 --- a/source/blender/draw/engines/eevee/eevee_volumes.c +++ b/source/blender/draw/engines/eevee/eevee_volumes.c @@ -316,12 +316,13 @@ void EEVEE_volumes_cache_object_add(EEVEE_ViewLayerData *sldata, DRWShadingGroup *grp = DRW_shgroup_create(sh, vedata->psl->volumetric_objects_ps); grp = DRW_shgroup_volume_create_sub(scene, ob, grp, mat); - DRW_shgroup_add_material_resources(grp, mat); if (grp == NULL) { return; } + DRW_shgroup_add_material_resources(grp, mat); + /* TODO(fclem): remove those "unnecessary" UBOs */ DRW_shgroup_uniform_block(grp, "planar_block", sldata->planar_ubo); DRW_shgroup_uniform_block(grp, "probe_block", sldata->probe_ubo); -- cgit v1.2.3 From e69c4482f16d51e5fda4da70abb94a23f8c5b3d9 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Fri, 15 Jul 2022 11:41:13 +0200 Subject: I18n: Add suport for labels from modifiers' subpanels. Was a bit oif a struggle since those functions take a first string which is not our label, but should work fine now. Reported/detected as part of D15418. --- .../modules/bl_i18n_utils/bl_extract_messages.py | 23 +++++++++++----------- release/scripts/modules/bl_i18n_utils/settings.py | 5 +++++ 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/release/scripts/modules/bl_i18n_utils/bl_extract_messages.py b/release/scripts/modules/bl_i18n_utils/bl_extract_messages.py index 319fd3396a0..3edb5b445fe 100644 --- a/release/scripts/modules/bl_i18n_utils/bl_extract_messages.py +++ b/release/scripts/modules/bl_i18n_utils/bl_extract_messages.py @@ -803,20 +803,21 @@ def dump_src_messages(msgs, reports, settings): line += data[pos:m.start()].count('\n') msgsrc = rel_path + ":" + str(line) _msgid = d.get("msg_raw") - # First, try the "multi-contexts" stuff! - _msgctxts = tuple(d.get("ctxt_raw{}".format(i)) for i in range(settings.PYGETTEXT_MAX_MULTI_CTXT)) - if _msgctxts[0]: - for _msgctxt in _msgctxts: - if not _msgctxt: - break + if _msgid not in {'""', "''"}: + # First, try the "multi-contexts" stuff! + _msgctxts = tuple(d.get("ctxt_raw{}".format(i)) for i in range(settings.PYGETTEXT_MAX_MULTI_CTXT)) + if _msgctxts[0]: + for _msgctxt in _msgctxts: + if not _msgctxt: + break + msgctxt, msgid = process_entry(_msgctxt, _msgid) + process_msg(msgs, msgctxt, msgid, msgsrc, reports, check_ctxt_src, settings) + reports["src_messages"].append((msgctxt, msgid, msgsrc)) + else: + _msgctxt = d.get("ctxt_raw") msgctxt, msgid = process_entry(_msgctxt, _msgid) process_msg(msgs, msgctxt, msgid, msgsrc, reports, check_ctxt_src, settings) reports["src_messages"].append((msgctxt, msgid, msgsrc)) - else: - _msgctxt = d.get("ctxt_raw") - msgctxt, msgid = process_entry(_msgctxt, _msgid) - process_msg(msgs, msgctxt, msgid, msgsrc, reports, check_ctxt_src, settings) - reports["src_messages"].append((msgctxt, msgid, msgsrc)) pos = m.end() line += data[m.start():pos].count('\n') diff --git a/release/scripts/modules/bl_i18n_utils/settings.py b/release/scripts/modules/bl_i18n_utils/settings.py index 9b38c512d31..3b69f8a6bf7 100644 --- a/release/scripts/modules/bl_i18n_utils/settings.py +++ b/release/scripts/modules/bl_i18n_utils/settings.py @@ -251,6 +251,11 @@ PYGETTEXT_KEYWORDS = (() + tuple(("{}\\((?:[^\"',]+,){{2}}\\s*" + _msg_re + r"\s*(?:\)|,)").format(it) for it in ("BKE_modifier_set_error",)) + + # This one is a tad more risky, but in practice would not expect a name/uid string parameter + # (the second one in those functions) to ever have a comma in it, so think this is fine. + tuple(("{}\\((?:[^,]+,){{2}}\\s*" + _msg_re + r"\s*(?:\)|,)").format(it) + for it in ("modifier_subpanel_register", "gpencil_modifier_subpanel_register")) + + # bUnitDef unit names. # NOTE: regex is a bit more complex than it would need too. Since the actual # identifier (`B_UNIT_DEF_`) is at the end, if it's simpler/too general it -- cgit v1.2.3 From 4d7c9901800835122413faebd9ead267f1f32285 Mon Sep 17 00:00:00 2001 From: Chris Blackbourn Date: Fri, 15 Jul 2022 23:13:59 +1200 Subject: Fix T98061: uv resize with individual origins could break constrain to bounds Fix unreported: Resize with Constrain To Bounds will now limit one shared scale value for both U and V instead of calculating separate scale values for each. To fix T98061, the individual origins (transdata->center) is now used when that mode is active. See also: 0e9367fc29bc Differential Revision: https://developer.blender.org/D15420 --- .../editors/transform/transform_mode_resize.c | 63 ++++++++++++---------- 1 file changed, 35 insertions(+), 28 deletions(-) diff --git a/source/blender/editors/transform/transform_mode_resize.c b/source/blender/editors/transform/transform_mode_resize.c index bc45ec07eab..bbe1cfdf521 100644 --- a/source/blender/editors/transform/transform_mode_resize.c +++ b/source/blender/editors/transform/transform_mode_resize.c @@ -126,10 +126,6 @@ static void constrain_scale_to_boundary(const float numerator, static bool clip_uv_transform_resize(TransInfo *t, float vec[2]) { - /* Assume no change is required. */ - float scalex = 1.0f; - float scaley = 1.0f; - /* Check if the current image in UV editor is a tiled image or not. */ const SpaceImage *sima = t->area->spacedata.first; const Image *image = sima->image; @@ -150,35 +146,46 @@ static bool clip_uv_transform_resize(TransInfo *t, float vec[2]) } } - float min[2], max[2]; - min[0] = min[1] = FLT_MAX; - max[0] = max[1] = -FLT_MAX; + /* Assume no change is required. */ + float scale = 1.0f; + /* Are we scaling U and V together, or just one axis? */ + const bool adjust_u = !(t->con.mode & CON_AXIS1); + const bool adjust_v = !(t->con.mode & CON_AXIS0); + const bool use_local_center = transdata_check_local_center(t, t->around); FOREACH_TRANS_DATA_CONTAINER (t, tc) { + for (TransData *td = tc->data; td < tc->data + tc->data_len; td++) { + + /* Get scale origin. */ + const float *scale_origin = use_local_center ? td->center : t->center_global; + + /* Alias td->loc as min and max just in case we need to optimize later. */ + const float *min = td->loc; + const float *max = td->loc; + + if (adjust_u) { + /* Update U against the left border. */ + constrain_scale_to_boundary( + scale_origin[0] - base_offset[0], scale_origin[0] - min[0], &scale); - TransData *td; - int a; - for (a = 0, td = tc->data; a < tc->data_len; a++, td++) { - minmax_v2v2_v2(min, max, td->loc); + /* Now the right border, negated, because `-1.0 / -1.0 = 1.0` */ + constrain_scale_to_boundary( + base_offset[0] + t->aspect[0] - scale_origin[0], max[0] - scale_origin[0], &scale); + } + + /* Do the same for the V co-ordinate. */ + if (adjust_v) { + constrain_scale_to_boundary( + scale_origin[1] - base_offset[1], scale_origin[1] - min[1], &scale); + + constrain_scale_to_boundary( + base_offset[1] + t->aspect[1] - scale_origin[1], max[1] - scale_origin[1], &scale); + } } } - - /* Update U against the left border. */ - constrain_scale_to_boundary( - t->center_global[0] - base_offset[0], t->center_global[0] - min[0], &scalex); - /* Now the right border, negated, because `-1.0 / -1.0 = 1.0` */ - constrain_scale_to_boundary( - base_offset[0] + t->aspect[0] - t->center_global[0], max[0] - t->center_global[0], &scalex); - - /* Do the same for the V co-ordinate, which is called `y`. */ - constrain_scale_to_boundary( - t->center_global[1] - base_offset[1], t->center_global[1] - min[1], &scaley); - constrain_scale_to_boundary( - base_offset[1] + t->aspect[1] - t->center_global[1], max[1] - t->center_global[1], &scaley); - - vec[0] *= scalex; - vec[1] *= scaley; - return (scalex != 1.0f) || (scaley != 1.0f); + vec[0] *= scale; + vec[1] *= scale; + return scale != 1.0f; } static void applyResize(TransInfo *t, const int UNUSED(mval[2])) -- cgit v1.2.3 From b8ffd43bd28ff93c5420d8a50c2cde5a061be118 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Fri, 15 Jul 2022 13:08:51 +0200 Subject: Cleanup: make format --- intern/cycles/kernel/integrator/init_from_bake.h | 6 ++++-- intern/cycles/kernel/integrator/intersect_shadow.h | 4 +++- intern/cycles/kernel/integrator/intersect_volume_stack.h | 4 +++- intern/cycles/kernel/integrator/shade_light.h | 4 +++- intern/cycles/kernel/integrator/shade_shadow.h | 4 +++- intern/cycles/kernel/integrator/subsurface.h | 12 +++++++++--- 6 files changed, 25 insertions(+), 9 deletions(-) diff --git a/intern/cycles/kernel/integrator/init_from_bake.h b/intern/cycles/kernel/integrator/init_from_bake.h index 4d75dcea190..afd174de9e8 100644 --- a/intern/cycles/kernel/integrator/init_from_bake.h +++ b/intern/cycles/kernel/integrator/init_from_bake.h @@ -247,10 +247,12 @@ ccl_device bool integrator_init_from_bake(KernelGlobals kg, const bool use_raytrace_kernel = (shader_flags & SD_HAS_RAYTRACE); if (use_caustics) { - integrator_path_init_sorted(kg, state, DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE_MNEE, shader_index); + integrator_path_init_sorted( + kg, state, DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE_MNEE, shader_index); } else if (use_raytrace_kernel) { - integrator_path_init_sorted(kg, state, DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE_RAYTRACE, shader_index); + integrator_path_init_sorted( + kg, state, DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE_RAYTRACE, shader_index); } else { integrator_path_init_sorted(kg, state, DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE, shader_index); diff --git a/intern/cycles/kernel/integrator/intersect_shadow.h b/intern/cycles/kernel/integrator/intersect_shadow.h index adcbfa19afe..1b48b360858 100644 --- a/intern/cycles/kernel/integrator/intersect_shadow.h +++ b/intern/cycles/kernel/integrator/intersect_shadow.h @@ -171,7 +171,9 @@ ccl_device void integrator_intersect_shadow(KernelGlobals kg, IntegratorShadowSt * * TODO: could also write to render buffer directly if no transparent shadows? * Could save a kernel execution for the common case. */ - integrator_shadow_path_next(kg, state, DEVICE_KERNEL_INTEGRATOR_INTERSECT_SHADOW, + integrator_shadow_path_next(kg, + state, + DEVICE_KERNEL_INTEGRATOR_INTERSECT_SHADOW, DEVICE_KERNEL_INTEGRATOR_SHADE_SHADOW); return; } diff --git a/intern/cycles/kernel/integrator/intersect_volume_stack.h b/intern/cycles/kernel/integrator/intersect_volume_stack.h index 68c7fdd5909..78f0b4d62aa 100644 --- a/intern/cycles/kernel/integrator/intersect_volume_stack.h +++ b/intern/cycles/kernel/integrator/intersect_volume_stack.h @@ -222,7 +222,9 @@ ccl_device void integrator_intersect_volume_stack(KernelGlobals kg, IntegratorSt } else { /* Volume stack init for camera rays, continue with intersection of camera ray. */ - integrator_path_next(kg, state, DEVICE_KERNEL_INTEGRATOR_INTERSECT_VOLUME_STACK, + integrator_path_next(kg, + state, + DEVICE_KERNEL_INTEGRATOR_INTERSECT_VOLUME_STACK, DEVICE_KERNEL_INTEGRATOR_INTERSECT_CLOSEST); } } diff --git a/intern/cycles/kernel/integrator/shade_light.h b/intern/cycles/kernel/integrator/shade_light.h index 7b3e9e0ee7e..1a5ac3637f6 100644 --- a/intern/cycles/kernel/integrator/shade_light.h +++ b/intern/cycles/kernel/integrator/shade_light.h @@ -103,7 +103,9 @@ ccl_device void integrator_shade_light(KernelGlobals kg, return; } else { - integrator_path_next(kg, state, DEVICE_KERNEL_INTEGRATOR_SHADE_LIGHT, + integrator_path_next(kg, + state, + DEVICE_KERNEL_INTEGRATOR_SHADE_LIGHT, DEVICE_KERNEL_INTEGRATOR_INTERSECT_CLOSEST); return; } diff --git a/intern/cycles/kernel/integrator/shade_shadow.h b/intern/cycles/kernel/integrator/shade_shadow.h index afe77590e9a..830da0158cf 100644 --- a/intern/cycles/kernel/integrator/shade_shadow.h +++ b/intern/cycles/kernel/integrator/shade_shadow.h @@ -165,7 +165,9 @@ ccl_device void integrator_shade_shadow(KernelGlobals kg, if (shadow_intersections_has_remaining(num_hits)) { /* More intersections to find, continue shadow ray. */ - integrator_shadow_path_next(kg, state, DEVICE_KERNEL_INTEGRATOR_SHADE_SHADOW, + integrator_shadow_path_next(kg, + state, + DEVICE_KERNEL_INTEGRATOR_SHADE_SHADOW, DEVICE_KERNEL_INTEGRATOR_INTERSECT_SHADOW); return; } diff --git a/intern/cycles/kernel/integrator/subsurface.h b/intern/cycles/kernel/integrator/subsurface.h index 09e59919c86..ab26a2d93cc 100644 --- a/intern/cycles/kernel/integrator/subsurface.h +++ b/intern/cycles/kernel/integrator/subsurface.h @@ -177,17 +177,23 @@ ccl_device_inline bool subsurface_scatter(KernelGlobals kg, IntegratorState stat const bool use_raytrace_kernel = (shader_flags & SD_HAS_RAYTRACE); if (use_caustics) { - integrator_path_next_sorted(kg, state, DEVICE_KERNEL_INTEGRATOR_INTERSECT_SUBSURFACE, + integrator_path_next_sorted(kg, + state, + DEVICE_KERNEL_INTEGRATOR_INTERSECT_SUBSURFACE, DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE_MNEE, shader); } else if (use_raytrace_kernel) { - integrator_path_next_sorted(kg, state, DEVICE_KERNEL_INTEGRATOR_INTERSECT_SUBSURFACE, + integrator_path_next_sorted(kg, + state, + DEVICE_KERNEL_INTEGRATOR_INTERSECT_SUBSURFACE, DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE_RAYTRACE, shader); } else { - integrator_path_next_sorted(kg, state, DEVICE_KERNEL_INTEGRATOR_INTERSECT_SUBSURFACE, + integrator_path_next_sorted(kg, + state, + DEVICE_KERNEL_INTEGRATOR_INTERSECT_SUBSURFACE, DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE, shader); } -- cgit v1.2.3 From 9ea1b88f0f01a617f452d5eaa44ecbebbaa95c4e Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Thu, 14 Jul 2022 20:16:34 +0200 Subject: Cleanup: add utlity function to compute render resolution Instead of duplicating logic many times. --- source/blender/blenkernel/BKE_scene.h | 4 ++++ source/blender/blenkernel/intern/image.cc | 8 +------- source/blender/blenkernel/intern/scene.cc | 14 ++++++++++++++ .../compositor/operations/COM_CompositorOperation.cc | 9 +++++---- .../blender/compositor/operations/COM_RenderLayersProg.cc | 4 ++-- .../blender/compositor/operations/COM_TextureOperation.cc | 5 +++-- .../blender/compositor/operations/COM_ViewerOperation.cc | 4 ++-- source/blender/editors/mask/mask_query.c | 3 +-- source/blender/editors/render/render_opengl.cc | 3 +-- source/blender/editors/render/render_view.cc | 8 ++++++-- source/blender/editors/space_image/image_edit.c | 9 ++------- source/blender/editors/space_image/image_ops.c | 1 + source/blender/editors/space_sequencer/sequencer_draw.c | 3 +-- source/blender/editors/space_sequencer/sequencer_view.c | 12 ++++++------ source/blender/editors/space_view3d/view3d_edit.c | 4 +++- source/blender/io/gpencil/intern/gpencil_io_base.cc | 3 +-- source/blender/io/gpencil/intern/gpencil_io_base.hh | 4 ++-- source/blender/render/intern/pipeline.c | 12 ++++-------- source/blender/sequencer/intern/proxy.c | 12 ++++-------- source/blender/sequencer/intern/render.c | 4 ++-- 20 files changed, 65 insertions(+), 61 deletions(-) diff --git a/source/blender/blenkernel/BKE_scene.h b/source/blender/blenkernel/BKE_scene.h index a6402a1e8a1..61fc883fe7f 100644 --- a/source/blender/blenkernel/BKE_scene.h +++ b/source/blender/blenkernel/BKE_scene.h @@ -251,6 +251,10 @@ bool BKE_scene_check_rigidbody_active(const struct Scene *scene); int BKE_scene_num_threads(const struct Scene *scene); int BKE_render_num_threads(const struct RenderData *r); +void BKE_render_resolution(const struct RenderData *r, + const bool use_crop, + int *r_width, + int *r_height); int BKE_render_preview_pixel_size(const struct RenderData *r); /**********************************/ diff --git a/source/blender/blenkernel/intern/image.cc b/source/blender/blenkernel/intern/image.cc index 6ec1c52c61a..f8b2d841028 100644 --- a/source/blender/blenkernel/intern/image.cc +++ b/source/blender/blenkernel/intern/image.cc @@ -5060,13 +5060,7 @@ void BKE_image_get_size(Image *image, ImageUser *iuser, int *r_width, int *r_hei } else if (image != nullptr && image->type == IMA_TYPE_R_RESULT && iuser != nullptr && iuser->scene != nullptr) { - Scene *scene = iuser->scene; - *r_width = (scene->r.xsch * scene->r.size) / 100; - *r_height = (scene->r.ysch * scene->r.size) / 100; - if ((scene->r.mode & R_BORDER) && (scene->r.mode & R_CROP)) { - *r_width *= BLI_rctf_size_x(&scene->r.border); - *r_height *= BLI_rctf_size_y(&scene->r.border); - } + BKE_render_resolution(&iuser->scene->r, true, r_width, r_height); } else { *r_width = IMG_SIZE_FALLBACK; diff --git a/source/blender/blenkernel/intern/scene.cc b/source/blender/blenkernel/intern/scene.cc index 39157bc9890..e2da27fc840 100644 --- a/source/blender/blenkernel/intern/scene.cc +++ b/source/blender/blenkernel/intern/scene.cc @@ -2952,6 +2952,20 @@ int BKE_scene_num_threads(const Scene *scene) return BKE_render_num_threads(&scene->r); } +void BKE_render_resolution(const struct RenderData *r, + const bool use_crop, + int *r_width, + int *r_height) +{ + *r_width = (r->xsch * r->size) / 100; + *r_height = (r->ysch * r->size) / 100; + + if (use_crop && (r->mode & R_BORDER) && (r->mode & R_CROP)) { + *r_width *= BLI_rctf_size_x(&r->border); + *r_height *= BLI_rctf_size_y(&r->border); + } +} + int BKE_render_preview_pixel_size(const RenderData *r) { if (r->preview_pixel_size == 0) { diff --git a/source/blender/compositor/operations/COM_CompositorOperation.cc b/source/blender/compositor/operations/COM_CompositorOperation.cc index e8b61ff229e..c0cc3fa1ba4 100644 --- a/source/blender/compositor/operations/COM_CompositorOperation.cc +++ b/source/blender/compositor/operations/COM_CompositorOperation.cc @@ -5,6 +5,7 @@ #include "BKE_global.h" #include "BKE_image.h" +#include "BKE_scene.h" #include "RE_pipeline.h" @@ -164,8 +165,8 @@ void CompositorOperation::execute_region(rcti *rect, unsigned int /*tile_number* * Full frame */ - int full_width = rd->xsch * rd->size / 100; - int full_height = rd->ysch * rd->size / 100; + int full_width, full_height; + BKE_render_resolution(rd, false, &full_width, &full_height); dx = rd->border.xmin * full_width - (full_width - this->get_width()) / 2.0f; dy = rd->border.ymin * full_height - (full_height - this->get_height()) / 2.0f; @@ -214,8 +215,8 @@ void CompositorOperation::update_memory_buffer_partial(MemoryBuffer *UNUSED(outp void CompositorOperation::determine_canvas(const rcti &UNUSED(preferred_area), rcti &r_area) { - int width = rd_->xsch * rd_->size / 100; - int height = rd_->ysch * rd_->size / 100; + int width, height; + BKE_render_resolution(rd_, false, &width, &height); /* Check actual render resolution with cropping it may differ with cropped border.rendering * Fix for T31777 Border Crop gives black (easy). */ diff --git a/source/blender/compositor/operations/COM_RenderLayersProg.cc b/source/blender/compositor/operations/COM_RenderLayersProg.cc index 522086658f8..40f2187b27b 100644 --- a/source/blender/compositor/operations/COM_RenderLayersProg.cc +++ b/source/blender/compositor/operations/COM_RenderLayersProg.cc @@ -109,8 +109,8 @@ void RenderLayersProg::execute_pixel_sampled(float output[4], /* see comment in execute_region describing coordinate mapping, * here it simply goes other way around */ - int full_width = rd->xsch * rd->size / 100; - int full_height = rd->ysch * rd->size / 100; + int full_width, full_height; + BKE_render_resolution(rd, false, &full_width, &full_height); dx = rd->border.xmin * full_width - (full_width - this->get_width()) / 2.0f; dy = rd->border.ymin * full_height - (full_height - this->get_height()) / 2.0f; diff --git a/source/blender/compositor/operations/COM_TextureOperation.cc b/source/blender/compositor/operations/COM_TextureOperation.cc index 8b27c3aded4..c1c8db2ae3f 100644 --- a/source/blender/compositor/operations/COM_TextureOperation.cc +++ b/source/blender/compositor/operations/COM_TextureOperation.cc @@ -6,6 +6,7 @@ #include "BKE_image.h" #include "BKE_node.h" +#include "BKE_scene.h" #include "NOD_texture.h" @@ -59,8 +60,8 @@ void TextureBaseOperation::determine_canvas(const rcti &preferred_area, rcti &r_ { r_area = preferred_area; if (BLI_rcti_is_empty(&preferred_area)) { - int width = rd_->xsch * rd_->size / 100; - int height = rd_->ysch * rd_->size / 100; + int width, height; + BKE_render_resolution(rd_, false, &width, &height); r_area.xmax = preferred_area.xmin + width; r_area.ymax = preferred_area.ymin + height; } diff --git a/source/blender/compositor/operations/COM_ViewerOperation.cc b/source/blender/compositor/operations/COM_ViewerOperation.cc index 58392b8a638..aeadf8f255d 100644 --- a/source/blender/compositor/operations/COM_ViewerOperation.cc +++ b/source/blender/compositor/operations/COM_ViewerOperation.cc @@ -104,8 +104,8 @@ void ViewerOperation::execute_region(rcti *rect, unsigned int /*tile_number*/) void ViewerOperation::determine_canvas(const rcti &preferred_area, rcti &r_area) { - const int scene_render_width = rd_->xsch * rd_->size / 100; - const int scene_render_height = rd_->ysch * rd_->size / 100; + int scene_render_width, scene_render_height; + BKE_render_resolution(rd_, false, &scene_render_width, &scene_render_height); rcti local_preferred = preferred_area; local_preferred.xmax = local_preferred.xmin + scene_render_width; diff --git a/source/blender/editors/mask/mask_query.c b/source/blender/editors/mask/mask_query.c index 02e1524e23e..bb865e925d7 100644 --- a/source/blender/editors/mask/mask_query.c +++ b/source/blender/editors/mask/mask_query.c @@ -682,8 +682,7 @@ void ED_mask_get_size(ScrArea *area, int *width, int *height) } case SPACE_SEQ: { // Scene *scene = CTX_data_scene(C); - // *width = (scene->r.size * scene->r.xsch) / 100; - // *height = (scene->r.size * scene->r.ysch) / 100; + // BKE_render_resolution(&scene->r, false, width, height); break; } case SPACE_IMAGE: { diff --git a/source/blender/editors/render/render_opengl.cc b/source/blender/editors/render/render_opengl.cc index 7bd9812a178..77ad23f1e3f 100644 --- a/source/blender/editors/render/render_opengl.cc +++ b/source/blender/editors/render/render_opengl.cc @@ -758,8 +758,7 @@ static bool screen_opengl_render_init(bContext *C, wmOperator *op) WM_jobs_kill_all_except(wm, CTX_wm_screen(C)); /* create offscreen buffer */ - sizex = (scene->r.size * scene->r.xsch) / 100; - sizey = (scene->r.size * scene->r.ysch) / 100; + BKE_render_resolution(&scene->r, false, &sizex, &sizey); /* corrects render size with actual size, not every card supports non-power-of-two dimensions */ DRW_opengl_context_enable(); /* Off-screen creation needs to be done in DRW context. */ diff --git a/source/blender/editors/render/render_view.cc b/source/blender/editors/render/render_view.cc index a7ff2aad05a..9a16c910205 100644 --- a/source/blender/editors/render/render_view.cc +++ b/source/blender/editors/render/render_view.cc @@ -19,6 +19,7 @@ #include "BKE_image.h" #include "BKE_main.h" #include "BKE_report.h" +#include "BKE_scene.h" #include "BKE_screen.h" #include "BLT_translation.h" @@ -130,8 +131,11 @@ ScrArea *render_view_open(bContext *C, int mx, int my, ReportList *reports) } if (U.render_display_type == USER_RENDER_DISPLAY_WINDOW) { - int sizex = 30 * UI_DPI_FAC + (scene->r.xsch * scene->r.size) / 100; - int sizey = 60 * UI_DPI_FAC + (scene->r.ysch * scene->r.size) / 100; + int sizex, sizey; + BKE_render_resolution(&scene->r, false, &sizex, &sizey); + + sizex += 30 * UI_DPI_FAC; + sizey += 60 * UI_DPI_FAC; /* arbitrary... miniature image window views don't make much sense */ if (sizex < 320) { diff --git a/source/blender/editors/space_image/image_edit.c b/source/blender/editors/space_image/image_edit.c index 950acd77f6a..0de50474ab8 100644 --- a/source/blender/editors/space_image/image_edit.c +++ b/source/blender/editors/space_image/image_edit.c @@ -20,6 +20,7 @@ #include "BKE_image.h" #include "BKE_lib_id.h" #include "BKE_main.h" +#include "BKE_scene.h" #include "IMB_imbuf_types.h" @@ -212,13 +213,7 @@ void ED_space_image_get_size(SpaceImage *sima, int *r_width, int *r_height) } else if (sima->image && sima->image->type == IMA_TYPE_R_RESULT && scene) { /* not very important, just nice */ - *r_width = (scene->r.xsch * scene->r.size) / 100; - *r_height = (scene->r.ysch * scene->r.size) / 100; - - if ((scene->r.mode & R_BORDER) && (scene->r.mode & R_CROP)) { - *r_width *= BLI_rctf_size_x(&scene->r.border); - *r_height *= BLI_rctf_size_y(&scene->r.border); - } + BKE_render_resolution(&scene->r, true, r_width, r_height); } /* I know a bit weak... but preview uses not actual image size */ // XXX else if (image_preview_active(sima, r_width, r_height)); diff --git a/source/blender/editors/space_image/image_ops.c b/source/blender/editors/space_image/image_ops.c index 8b975ee6173..7a820de98b9 100644 --- a/source/blender/editors/space_image/image_ops.c +++ b/source/blender/editors/space_image/image_ops.c @@ -45,6 +45,7 @@ #include "BKE_main.h" #include "BKE_packedFile.h" #include "BKE_report.h" +#include "BKE_scene.h" #include "DEG_depsgraph.h" diff --git a/source/blender/editors/space_sequencer/sequencer_draw.c b/source/blender/editors/space_sequencer/sequencer_draw.c index 4b9ff1e170e..aa5681306a4 100644 --- a/source/blender/editors/space_sequencer/sequencer_draw.c +++ b/source/blender/editors/space_sequencer/sequencer_draw.c @@ -1762,8 +1762,7 @@ void sequencer_draw_maskedit(const bContext *C, Scene *scene, ARegion *region, S // ED_mask_get_size(C, &width, &height); //Scene *scene = CTX_data_scene(C); - width = (scene->r.size * scene->r.xsch) / 100; - height = (scene->r.size * scene->r.ysch) / 100; + BKE_render_resolution(&scene->r, false, &width, &height); ED_mask_draw_region(mask, region, diff --git a/source/blender/editors/space_sequencer/sequencer_view.c b/source/blender/editors/space_sequencer/sequencer_view.c index 4d32c00109a..78fa8c379d9 100644 --- a/source/blender/editors/space_sequencer/sequencer_view.c +++ b/source/blender/editors/space_sequencer/sequencer_view.c @@ -12,6 +12,7 @@ #include "DNA_scene_types.h" #include "BKE_context.h" +#include "BKE_scene.h" #include "WM_api.h" #include "WM_types.h" @@ -174,8 +175,7 @@ static int sequencer_view_all_preview_exec(bContext *C, wmOperator *UNUSED(op)) seq_reset_imageofs(sseq); - imgwidth = (scene->r.size * scene->r.xsch) / 100; - imgheight = (scene->r.size * scene->r.ysch) / 100; + BKE_render_resolution(&scene->r, false, &imgwidth, &imgheight); /* Apply aspect, doesn't need to be that accurate. */ imgwidth = (int)(imgwidth * (scene->r.xasp / scene->r.yasp)); @@ -227,11 +227,11 @@ static int sequencer_view_zoom_ratio_exec(bContext *C, wmOperator *op) float ratio = RNA_float_get(op->ptr, "ratio"); - float winx = (int)(rd->size * rd->xsch) / 100; - float winy = (int)(rd->size * rd->ysch) / 100; + int winx, winy; + BKE_render_resolution(rd, false, &winx, &winy); - float facx = BLI_rcti_size_x(&v2d->mask) / winx; - float facy = BLI_rcti_size_y(&v2d->mask) / winy; + float facx = BLI_rcti_size_x(&v2d->mask) / (float)winx; + float facy = BLI_rcti_size_y(&v2d->mask) / (float)winy; BLI_rctf_resize(&v2d->cur, ceilf(winx * facx / ratio + 0.5f), ceilf(winy * facy / ratio + 0.5f)); diff --git a/source/blender/editors/space_view3d/view3d_edit.c b/source/blender/editors/space_view3d/view3d_edit.c index 041663b4a00..d6ddd6d044e 100644 --- a/source/blender/editors/space_view3d/view3d_edit.c +++ b/source/blender/editors/space_view3d/view3d_edit.c @@ -413,7 +413,9 @@ static void view3d_set_1_to_1_viewborder(Scene *scene, { RegionView3D *rv3d = region->regiondata; float size[2]; - int im_width = (scene->r.size * scene->r.xsch) / 100; + + int im_width, im_height; + BKE_render_resolution(&scene->r, false, &im_width, &im_height); ED_view3d_calc_camera_border_size(scene, depsgraph, region, v3d, rv3d, size); diff --git a/source/blender/io/gpencil/intern/gpencil_io_base.cc b/source/blender/io/gpencil/intern/gpencil_io_base.cc index 05f1158c57d..6db3eccedbe 100644 --- a/source/blender/io/gpencil/intern/gpencil_io_base.cc +++ b/source/blender/io/gpencil/intern/gpencil_io_base.cc @@ -91,8 +91,7 @@ void GpencilIO::prepare_camera_params(Scene *scene, const GpencilIOParams *ipara /* Camera rectangle. */ if ((rv3d_->persp == RV3D_CAMOB) || (force_camera_view)) { - render_x_ = (scene_->r.xsch * scene_->r.size) / 100; - render_y_ = (scene_->r.ysch * scene_->r.size) / 100; + BKE_render_resolution(&scene->r, false, &render_x_, &render_y_); ED_view3d_calc_camera_border(CTX_data_scene(params_.C), depsgraph_, diff --git a/source/blender/io/gpencil/intern/gpencil_io_base.hh b/source/blender/io/gpencil/intern/gpencil_io_base.hh index a89b723ed6c..4987ab34ffc 100644 --- a/source/blender/io/gpencil/intern/gpencil_io_base.hh +++ b/source/blender/io/gpencil/intern/gpencil_io_base.hh @@ -58,8 +58,8 @@ class GpencilIO { struct Scene *scene_; struct RegionView3D *rv3d_; - int16_t winx_, winy_; - int16_t render_x_, render_y_; + int winx_, winy_; + int render_x_, render_y_; float camera_ratio_; rctf camera_rect_; diff --git a/source/blender/render/intern/pipeline.c b/source/blender/render/intern/pipeline.c index 154b689b4a5..1c42467bc3d 100644 --- a/source/blender/render/intern/pipeline.c +++ b/source/blender/render/intern/pipeline.c @@ -1036,8 +1036,7 @@ static void do_render_compositor_scene(Render *re, Scene *sce, int cfra) /* exception: scene uses own size (unfinished code) */ if (0) { - winx = (sce->r.size * sce->r.xsch) / 100; - winy = (sce->r.size * sce->r.ysch) / 100; + BKE_render_resolution(&sce->r, false, &winx, &winy); } /* initial setup */ @@ -1675,8 +1674,7 @@ static int render_init_from_main(Render *re, * r.border is the clipping rect */ /* calculate actual render result and display size */ - winx = (rd->size * rd->xsch) / 100; - winy = (rd->size * rd->ysch) / 100; + BKE_render_resolution(rd, false, &winx, &winy); /* We always render smaller part, inserting it in larger image is compositor business, * it uses 'disprect' for it. */ @@ -2409,8 +2407,7 @@ void RE_PreviewRender(Render *re, Main *bmain, Scene *sce) Object *camera; int winx, winy; - winx = (sce->r.size * sce->r.xsch) / 100; - winy = (sce->r.size * sce->r.ysch) / 100; + BKE_render_resolution(&sce->r, false, &winx, &winy); RE_InitState(re, NULL, &sce->r, &sce->view_layers, NULL, winx, winy, NULL); @@ -2439,8 +2436,7 @@ bool RE_ReadRenderResult(Scene *scene, Scene *scenode) rcti disprect; /* calculate actual render result and display size */ - winx = (scene->r.size * scene->r.xsch) / 100; - winy = (scene->r.size * scene->r.ysch) / 100; + BKE_render_resolution(&scene->r, false, &winx, &winy); /* only in movie case we render smaller part */ if (scene->r.mode & R_BORDER) { diff --git a/source/blender/sequencer/intern/proxy.c b/source/blender/sequencer/intern/proxy.c index 522c64f7791..374e18dd36a 100644 --- a/source/blender/sequencer/intern/proxy.c +++ b/source/blender/sequencer/intern/proxy.c @@ -511,15 +511,11 @@ void SEQ_proxy_rebuild(SeqIndexBuildContext *context, } /* fail safe code */ + int width, height; + BKE_render_resolution(&scene->r, false, &width, &height); - SEQ_render_new_render_data(bmain, - context->depsgraph, - context->scene, - roundf((scene->r.size * (float)scene->r.xsch) / 100.0f), - roundf((scene->r.size * (float)scene->r.ysch) / 100.0f), - 100, - false, - &render_context); + SEQ_render_new_render_data( + bmain, context->depsgraph, context->scene, width, height, 100, false, &render_context); render_context.skip_cache = true; render_context.is_proxy_render = true; diff --git a/source/blender/sequencer/intern/render.c b/source/blender/sequencer/intern/render.c index 15487a597d1..b7dc0e7035d 100644 --- a/source/blender/sequencer/intern/render.c +++ b/source/blender/sequencer/intern/render.c @@ -1447,8 +1447,8 @@ static ImBuf *seq_render_scene_strip(const SeqRenderData *context, if ((sequencer_view3d_fn && do_seq_gl && camera) && is_thread_main) { char err_out[256] = "unknown"; - const int width = (scene->r.xsch * scene->r.size) / 100; - const int height = (scene->r.ysch * scene->r.size) / 100; + int width, height; + BKE_render_resolution(&scene->r, false, &width, &height); const char *viewname = BKE_scene_multiview_render_view_name_get(&scene->r, context->view_id); unsigned int draw_flags = V3D_OFSDRAW_NONE; -- cgit v1.2.3 From 5ddbc14bb240a206ed38d5fd994db2a5ac2c4d3d Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Thu, 14 Jul 2022 20:18:56 +0200 Subject: Render: improve render border operator in image editor * Snap border to pixels just outside the drawn border, to more easily select specific pixels by drawing a border inside them. * Support cropped border renders. --- source/blender/editors/space_image/image_ops.c | 38 ++++++++++++++++++-------- 1 file changed, 26 insertions(+), 12 deletions(-) diff --git a/source/blender/editors/space_image/image_ops.c b/source/blender/editors/space_image/image_ops.c index 7a820de98b9..4036f859231 100644 --- a/source/blender/editors/space_image/image_ops.c +++ b/source/blender/editors/space_image/image_ops.c @@ -3733,38 +3733,52 @@ static int render_border_exec(bContext *C, wmOperator *op) ARegion *region = CTX_wm_region(C); Scene *scene = CTX_data_scene(C); Render *re = RE_GetSceneRender(scene); - RenderData *rd; - rctf border; + SpaceImage *sima = CTX_wm_space_image(C); if (re == NULL) { /* Shouldn't happen, but better be safe close to the release. */ return OPERATOR_CANCELLED; } - rd = RE_engine_get_render_data(re); - if ((rd->mode & (R_BORDER | R_CROP)) == (R_BORDER | R_CROP)) { - BKE_report(op->reports, RPT_INFO, "Can not set border from a cropped render"); - return OPERATOR_CANCELLED; - } + /* Get information about the previous render, or current scene if no render yet. */ + int width, height; + BKE_render_resolution(&scene->r, false, &width, &height); + const RenderData *rd = ED_space_image_has_buffer(sima) ? RE_engine_get_render_data(re) : + &scene->r; - /* get rectangle from operator */ + /* Get rectangle from the operator. */ + rctf border; WM_operator_properties_border_to_rctf(op, &border); UI_view2d_region_to_view_rctf(®ion->v2d, &border, &border); - /* actually set border */ + /* Adjust for cropping. */ + if ((rd->mode & (R_BORDER | R_CROP)) == (R_BORDER | R_CROP)) { + border.xmin = rd->border.xmin + border.xmin * (rd->border.xmax - rd->border.xmin); + border.xmax = rd->border.xmin + border.xmax * (rd->border.xmax - rd->border.xmin); + border.ymin = rd->border.ymin + border.ymin * (rd->border.ymax - rd->border.ymin); + border.ymax = rd->border.ymin + border.ymax * (rd->border.ymax - rd->border.ymin); + } + CLAMP(border.xmin, 0.0f, 1.0f); CLAMP(border.ymin, 0.0f, 1.0f); CLAMP(border.xmax, 0.0f, 1.0f); CLAMP(border.ymax, 0.0f, 1.0f); - scene->r.border = border; - /* drawing a border surrounding the entire camera view switches off border rendering - * or the border covers no pixels */ + /* Drawing a border surrounding the entire camera view switches off border rendering + * or the border covers no pixels. */ if ((border.xmin <= 0.0f && border.xmax >= 1.0f && border.ymin <= 0.0f && border.ymax >= 1.0f) || (border.xmin == border.xmax || border.ymin == border.ymax)) { scene->r.mode &= ~R_BORDER; } else { + /* Snap border to pixel boundaries, so drawing a border within a pixel selects that pixel. */ + border.xmin = floorf(border.xmin * width) / width; + border.xmax = ceilf(border.xmax * width) / width; + border.ymin = floorf(border.ymin * height) / height; + border.ymax = ceilf(border.ymax * height) / height; + + /* Set border. */ + scene->r.border = border; scene->r.mode |= R_BORDER; } -- cgit v1.2.3 From 2e70d5cb980ef2fda6d3bd7e89ed942f3f4b9c59 Mon Sep 17 00:00:00 2001 From: Damien Picard Date: Fri, 15 Jul 2022 13:30:57 +0200 Subject: Render: camera depth of field support for armature bone targets This is useful when using an armature as a camera rig, to avoid creating and targetting an empty object. Differential Revision: https://developer.blender.org/D7012 --- intern/cycles/blender/addon/ui.py | 2 ++ intern/cycles/blender/camera.cpp | 11 ++++++++++- release/scripts/startup/bl_ui/properties_data_camera.py | 2 ++ source/blender/blenkernel/intern/camera.c | 11 ++++++++++- .../depsgraph/intern/builder/deg_builder_relations.cc | 4 ++++ source/blender/editors/armature/armature_naming.c | 12 ++++++++++++ source/blender/makesdna/DNA_camera_types.h | 1 + source/blender/makesrna/intern/rna_camera.c | 6 ++++++ 8 files changed, 47 insertions(+), 2 deletions(-) diff --git a/intern/cycles/blender/addon/ui.py b/intern/cycles/blender/addon/ui.py index 3c898d4be73..0fead409866 100644 --- a/intern/cycles/blender/addon/ui.py +++ b/intern/cycles/blender/addon/ui.py @@ -952,6 +952,8 @@ class CYCLES_CAMERA_PT_dof(CyclesButtonsPanel, Panel): col = split.column() col.prop(dof, "focus_object", text="Focus Object") + if dof.focus_object and dof.focus_object.type == 'ARMATURE': + col.prop_search(dof, "focus_subtarget", dof.focus_object.data, "bones", text="Focus Bone") sub = col.row() sub.active = dof.focus_object is None diff --git a/intern/cycles/blender/camera.cpp b/intern/cycles/blender/camera.cpp index 2ab5f02a337..6926c833096 100644 --- a/intern/cycles/blender/camera.cpp +++ b/intern/cycles/blender/camera.cpp @@ -143,11 +143,20 @@ static float blender_camera_focal_distance(BL::RenderEngine &b_engine, if (!b_dof_object) return b_camera.dof().focus_distance(); + Transform dofmat = get_transform(b_dof_object.matrix_world()); + + string focus_subtarget = b_camera.dof().focus_subtarget(); + if (b_dof_object.pose() && !focus_subtarget.empty()) { + BL::PoseBone b_bone = b_dof_object.pose().bones[focus_subtarget]; + if (b_bone) { + dofmat = dofmat * get_transform(b_bone.matrix()); + } + } + /* for dof object, return distance along camera Z direction */ BL::Array b_ob_matrix; b_engine.camera_model_matrix(b_ob, bcam->use_spherical_stereo, b_ob_matrix); Transform obmat = transform_clear_scale(get_transform(b_ob_matrix)); - Transform dofmat = get_transform(b_dof_object.matrix_world()); float3 view_dir = normalize(transform_get_column(&obmat, 2)); float3 dof_dir = transform_get_column(&obmat, 3) - transform_get_column(&dofmat, 3); return fabsf(dot(view_dir, dof_dir)); diff --git a/release/scripts/startup/bl_ui/properties_data_camera.py b/release/scripts/startup/bl_ui/properties_data_camera.py index 8213a865990..a9a032ac4a3 100644 --- a/release/scripts/startup/bl_ui/properties_data_camera.py +++ b/release/scripts/startup/bl_ui/properties_data_camera.py @@ -218,6 +218,8 @@ class DATA_PT_camera_dof(CameraButtonsPanel, Panel): col = layout.column() col.prop(dof, "focus_object", text="Focus on Object") + if dof.focus_object and dof.focus_object.type == 'ARMATURE': + col.prop_search(dof, "focus_subtarget", dof.focus_object.data, "bones", text="Focus on Bone") sub = col.column() sub.active = (dof.focus_object is None) sub.prop(dof, "focus_distance", text="Focus Distance") diff --git a/source/blender/blenkernel/intern/camera.c b/source/blender/blenkernel/intern/camera.c index 7cfac0cb75a..2e6439f7e1a 100644 --- a/source/blender/blenkernel/intern/camera.c +++ b/source/blender/blenkernel/intern/camera.c @@ -26,6 +26,7 @@ #include "BLI_utildefines.h" #include "BKE_anim_data.h" +#include "BKE_action.h" #include "BKE_camera.h" #include "BKE_idtype.h" #include "BKE_layer.h" @@ -221,7 +222,15 @@ float BKE_camera_object_dof_distance(const Object *ob) if (cam->dof.focus_object) { float view_dir[3], dof_dir[3]; normalize_v3_v3(view_dir, ob->obmat[2]); - sub_v3_v3v3(dof_dir, ob->obmat[3], cam->dof.focus_object->obmat[3]); + bPoseChannel *pchan = BKE_pose_channel_find_name(cam->dof.focus_object->pose, cam->dof.focus_subtarget); + if (pchan) { + float posemat[4][4]; + mul_m4_m4m4(posemat, cam->dof.focus_object->obmat, pchan->pose_mat); + sub_v3_v3v3(dof_dir, ob->obmat[3], posemat[3]); + } + else { + sub_v3_v3v3(dof_dir, ob->obmat[3], cam->dof.focus_object->obmat[3]); + } return fabsf(dot_v3v3(view_dir, dof_dir)); } return cam->dof.focus_distance; diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc index 31d5308e825..92396a89cea 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc @@ -2459,6 +2459,10 @@ void DepsgraphRelationBuilder::build_camera(Camera *camera) ComponentKey camera_parameters_key(&camera->id, NodeType::PARAMETERS); ComponentKey dof_ob_key(&camera->dof.focus_object->id, NodeType::TRANSFORM); add_relation(dof_ob_key, camera_parameters_key, "Camera DOF"); + if (camera->dof.focus_subtarget[0]) { + OperationKey target_key(&camera->dof.focus_object->id, NodeType::BONE, camera->dof.focus_subtarget, OperationCode::BONE_DONE); + add_relation(target_key, camera_parameters_key, "Camera DOF subtarget"); + } } } diff --git a/source/blender/editors/armature/armature_naming.c b/source/blender/editors/armature/armature_naming.c index 1f02e24666d..8d5ae6f3654 100644 --- a/source/blender/editors/armature/armature_naming.c +++ b/source/blender/editors/armature/armature_naming.c @@ -13,6 +13,7 @@ #include "MEM_guardedalloc.h" #include "DNA_armature_types.h" +#include "DNA_camera_types.h" #include "DNA_constraint_types.h" #include "DNA_gpencil_modifier_types.h" #include "DNA_gpencil_types.h" @@ -281,6 +282,17 @@ void ED_armature_bone_rename(Main *bmain, } } + /* fix camera focus */ + if (ob->type == OB_CAMERA) { + Camera *cam = (Camera *)ob->data; + if (cam->dof.focus_object->data == arm){ + if (STREQ(cam->dof.focus_subtarget, oldname)) { + BLI_strncpy(cam->dof.focus_subtarget, newname, MAXBONENAME); + DEG_id_tag_update(&cam->id, ID_RECALC_COPY_ON_WRITE); + } + } + } + /* fix grease pencil modifiers and vertex groups */ if (ob->type == OB_GPENCIL) { diff --git a/source/blender/makesdna/DNA_camera_types.h b/source/blender/makesdna/DNA_camera_types.h index e0aec298cd0..10a6c936be1 100644 --- a/source/blender/makesdna/DNA_camera_types.h +++ b/source/blender/makesdna/DNA_camera_types.h @@ -54,6 +54,7 @@ typedef struct CameraBGImage { typedef struct CameraDOFSettings { /** Focal distance for depth of field. */ struct Object *focus_object; + char focus_subtarget[64]; float focus_distance; float aperture_fstop; float aperture_rotation; diff --git a/source/blender/makesrna/intern/rna_camera.c b/source/blender/makesrna/intern/rna_camera.c index 9628c6b2d65..99f8c263da6 100644 --- a/source/blender/makesrna/intern/rna_camera.c +++ b/source/blender/makesrna/intern/rna_camera.c @@ -519,6 +519,12 @@ static void rna_def_camera_dof_settings_data(BlenderRNA *brna) prop, "Focus Object", "Use this object to define the depth of field focal point"); RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_Camera_dependency_update"); + prop = RNA_def_property(srna, "focus_subtarget", PROP_STRING, PROP_NONE); + RNA_def_property_string_sdna(prop, NULL, "focus_subtarget"); + RNA_def_property_ui_text( + prop, "Focus Bone", "Use this armature bone to define the depth of field focal point"); + RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_Camera_dependency_update"); + prop = RNA_def_property(srna, "focus_distance", PROP_FLOAT, PROP_DISTANCE); // RNA_def_property_pointer_sdna(prop, NULL, "focus_distance"); RNA_def_property_range(prop, 0.0f, FLT_MAX); -- cgit v1.2.3 From 79da7f2a8fbe37070c899cf8d1298694dbbef86e Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Tue, 12 Jul 2022 17:26:29 +0200 Subject: Cycles: refactor to move part of KernelData definition to template header To be used for specialization on Metal in a following commit, turning these members into compile time constants. Ref D14645 --- intern/cycles/device/cpu/device_impl.cpp | 2 +- intern/cycles/device/optix/device_impl.cpp | 2 +- intern/cycles/kernel/CMakeLists.txt | 1 + intern/cycles/kernel/bvh/bvh.h | 26 +-- intern/cycles/kernel/bvh/embree.h | 10 +- intern/cycles/kernel/camera/camera.h | 2 +- intern/cycles/kernel/data_template.h | 199 ++++++++++++++++++++++ intern/cycles/kernel/types.h | 256 ++++------------------------- intern/cycles/scene/film.cpp | 2 +- intern/cycles/scene/geometry.cpp | 2 +- 10 files changed, 257 insertions(+), 245 deletions(-) create mode 100644 intern/cycles/kernel/data_template.h diff --git a/intern/cycles/device/cpu/device_impl.cpp b/intern/cycles/device/cpu/device_impl.cpp index d4f0532aa5e..1e4b9baa0c0 100644 --- a/intern/cycles/device/cpu/device_impl.cpp +++ b/intern/cycles/device/cpu/device_impl.cpp @@ -197,7 +197,7 @@ void CPUDevice::const_copy_to(const char *name, void *host, size_t size) // Update scene handle (since it is different for each device on multi devices) KernelData *const data = (KernelData *)host; - data->bvh.scene = embree_scene; + data->device_bvh = embree_scene; } #endif kernel_const_copy(&kernel_globals, name, host, size); diff --git a/intern/cycles/device/optix/device_impl.cpp b/intern/cycles/device/optix/device_impl.cpp index e7dcc29a2da..11c0d1bf8a0 100644 --- a/intern/cycles/device/optix/device_impl.cpp +++ b/intern/cycles/device/optix/device_impl.cpp @@ -2047,7 +2047,7 @@ void OptiXDevice::const_copy_to(const char *name, void *host, size_t size) /* Update traversable handle (since it is different for each device on multi devices). */ KernelData *const data = (KernelData *)host; - *(OptixTraversableHandle *)&data->bvh.scene = tlas_handle; + *(OptixTraversableHandle *)&data->device_bvh = tlas_handle; update_launch_params(offsetof(KernelParamsOptiX, data), host, size); return; diff --git a/intern/cycles/kernel/CMakeLists.txt b/intern/cycles/kernel/CMakeLists.txt index 57a26edff50..4ff947e7136 100644 --- a/intern/cycles/kernel/CMakeLists.txt +++ b/intern/cycles/kernel/CMakeLists.txt @@ -282,6 +282,7 @@ set(SRC_KERNEL_UTIL_HEADERS set(SRC_KERNEL_TYPES_HEADERS data_arrays.h + data_template.h tables.h types.h ) diff --git a/intern/cycles/kernel/bvh/bvh.h b/intern/cycles/kernel/bvh/bvh.h index a1d0e307170..f375529a6f6 100644 --- a/intern/cycles/kernel/bvh/bvh.h +++ b/intern/cycles/kernel/bvh/bvh.h @@ -172,7 +172,7 @@ ccl_device_intersect bool scene_intersect(KernelGlobals kg, ray_flags |= OPTIX_RAY_FLAG_TERMINATE_ON_FIRST_HIT; } - optixTrace(scene_intersect_valid(ray) ? kernel_data.bvh.scene : 0, + optixTrace(scene_intersect_valid(ray) ? kernel_data.device_bvh : 0, ray->P, ray->D, 0.0f, @@ -295,14 +295,14 @@ ccl_device_intersect bool scene_intersect(KernelGlobals kg, } # ifdef __EMBREE__ - if (kernel_data.bvh.scene) { + if (kernel_data.device_bvh) { isect->t = ray->t; CCLIntersectContext ctx(kg, CCLIntersectContext::RAY_REGULAR); IntersectContext rtc_ctx(&ctx); RTCRayHit ray_hit; ctx.ray = ray; kernel_embree_setup_rayhit(*ray, ray_hit, visibility); - rtcIntersect1(kernel_data.bvh.scene, &rtc_ctx.context, &ray_hit); + rtcIntersect1(kernel_data.device_bvh, &rtc_ctx.context, &ray_hit); if (ray_hit.hit.geomID != RTC_INVALID_GEOMETRY_ID && ray_hit.hit.primID != RTC_INVALID_GEOMETRY_ID) { kernel_embree_convert_hit(kg, &ray_hit.ray, &ray_hit.hit, isect); @@ -357,7 +357,7 @@ ccl_device_intersect bool scene_intersect_local(KernelGlobals kg, if (local_isect) { local_isect->num_hits = 0; /* Initialize hit count to zero. */ } - optixTrace(scene_intersect_valid(ray) ? kernel_data.bvh.scene : 0, + optixTrace(scene_intersect_valid(ray) ? kernel_data.device_bvh : 0, ray->P, ray->D, 0.0f, @@ -451,7 +451,7 @@ ccl_device_intersect bool scene_intersect_local(KernelGlobals kg, } # ifdef __EMBREE__ - if (kernel_data.bvh.scene) { + if (kernel_data.device_bvh) { const bool has_bvh = !(kernel_data_fetch(object_flag, local_object) & SD_OBJECT_TRANSFORM_APPLIED); CCLIntersectContext ctx( @@ -470,7 +470,7 @@ ccl_device_intersect bool scene_intersect_local(KernelGlobals kg, /* If this object has its own BVH, use it. */ if (has_bvh) { - RTCGeometry geom = rtcGetGeometry(kernel_data.bvh.scene, local_object * 2); + RTCGeometry geom = rtcGetGeometry(kernel_data.device_bvh, local_object * 2); if (geom) { float3 P = ray->P; float3 dir = ray->D; @@ -496,7 +496,7 @@ ccl_device_intersect bool scene_intersect_local(KernelGlobals kg, } } else { - rtcOccluded1(kernel_data.bvh.scene, &rtc_ctx.context, &rtc_ray); + rtcOccluded1(kernel_data.device_bvh, &rtc_ctx.context, &rtc_ray); } /* rtcOccluded1 sets tfar to -inf if a hit was found. */ @@ -539,7 +539,7 @@ ccl_device_intersect bool scene_intersect_shadow_all(KernelGlobals kg, ray_mask = 0xFF; } - optixTrace(scene_intersect_valid(ray) ? kernel_data.bvh.scene : 0, + optixTrace(scene_intersect_valid(ray) ? kernel_data.device_bvh : 0, ray->P, ray->D, 0.0f, @@ -633,7 +633,7 @@ ccl_device_intersect bool scene_intersect_shadow_all(KernelGlobals kg, } # ifdef __EMBREE__ - if (kernel_data.bvh.scene) { + if (kernel_data.device_bvh) { CCLIntersectContext ctx(kg, CCLIntersectContext::RAY_SHADOW_ALL); Intersection *isect_array = (Intersection *)state->shadow_isect; ctx.isect_s = isect_array; @@ -642,7 +642,7 @@ ccl_device_intersect bool scene_intersect_shadow_all(KernelGlobals kg, IntersectContext rtc_ctx(&ctx); RTCRay rtc_ray; kernel_embree_setup_ray(*ray, rtc_ray, visibility); - rtcOccluded1(kernel_data.bvh.scene, &rtc_ctx.context, &rtc_ray); + rtcOccluded1(kernel_data.device_bvh, &rtc_ctx.context, &rtc_ray); *num_recorded_hits = ctx.num_recorded_hits; *throughput = ctx.throughput; @@ -698,7 +698,7 @@ ccl_device_intersect bool scene_intersect_volume(KernelGlobals kg, ray_mask = 0xFF; } - optixTrace(scene_intersect_valid(ray) ? kernel_data.bvh.scene : 0, + optixTrace(scene_intersect_valid(ray) ? kernel_data.device_bvh : 0, ray->P, ray->D, 0.0f, @@ -825,7 +825,7 @@ ccl_device_intersect uint scene_intersect_volume_all(KernelGlobals kg, } # ifdef __EMBREE__ - if (kernel_data.bvh.scene) { + if (kernel_data.device_bvh) { CCLIntersectContext ctx(kg, CCLIntersectContext::RAY_VOLUME_ALL); ctx.isect_s = isect; ctx.max_hits = max_hits; @@ -834,7 +834,7 @@ ccl_device_intersect uint scene_intersect_volume_all(KernelGlobals kg, IntersectContext rtc_ctx(&ctx); RTCRay rtc_ray; kernel_embree_setup_ray(*ray, rtc_ray, visibility); - rtcOccluded1(kernel_data.bvh.scene, &rtc_ctx.context, &rtc_ray); + rtcOccluded1(kernel_data.device_bvh, &rtc_ctx.context, &rtc_ray); return ctx.num_hits; } # endif /* __EMBREE__ */ diff --git a/intern/cycles/kernel/bvh/embree.h b/intern/cycles/kernel/bvh/embree.h index 1c6b9bc1e62..77eec2468f4 100644 --- a/intern/cycles/kernel/bvh/embree.h +++ b/intern/cycles/kernel/bvh/embree.h @@ -107,7 +107,7 @@ ccl_device_inline bool kernel_embree_is_self_intersection(const KernelGlobals kg const int oID = hit->instID[0] / 2; if ((ray->self.object == oID) || (ray->self.light_object == oID)) { RTCScene inst_scene = (RTCScene)rtcGetGeometryUserData( - rtcGetGeometry(kernel_data.bvh.scene, hit->instID[0])); + rtcGetGeometry(kernel_data.device_bvh, hit->instID[0])); const int pID = hit->primID + (intptr_t)rtcGetGeometryUserData(rtcGetGeometry(inst_scene, hit->geomID)); status = intersection_skip_self_shadow(ray->self, oID, pID); @@ -117,7 +117,7 @@ ccl_device_inline bool kernel_embree_is_self_intersection(const KernelGlobals kg const int oID = hit->geomID / 2; if ((ray->self.object == oID) || (ray->self.light_object == oID)) { const int pID = hit->primID + (intptr_t)rtcGetGeometryUserData( - rtcGetGeometry(kernel_data.bvh.scene, hit->geomID)); + rtcGetGeometry(kernel_data.device_bvh, hit->geomID)); status = intersection_skip_self_shadow(ray->self, oID, pID); } } @@ -133,14 +133,14 @@ ccl_device_inline void kernel_embree_convert_hit(KernelGlobals kg, isect->t = ray->tfar; if (hit->instID[0] != RTC_INVALID_GEOMETRY_ID) { RTCScene inst_scene = (RTCScene)rtcGetGeometryUserData( - rtcGetGeometry(kernel_data.bvh.scene, hit->instID[0])); + rtcGetGeometry(kernel_data.device_bvh, hit->instID[0])); isect->prim = hit->primID + (intptr_t)rtcGetGeometryUserData(rtcGetGeometry(inst_scene, hit->geomID)); isect->object = hit->instID[0] / 2; } else { isect->prim = hit->primID + (intptr_t)rtcGetGeometryUserData( - rtcGetGeometry(kernel_data.bvh.scene, hit->geomID)); + rtcGetGeometry(kernel_data.device_bvh, hit->geomID)); isect->object = hit->geomID / 2; } @@ -166,7 +166,7 @@ ccl_device_inline void kernel_embree_convert_sss_hit( isect->v = hit->u; isect->t = ray->tfar; RTCScene inst_scene = (RTCScene)rtcGetGeometryUserData( - rtcGetGeometry(kernel_data.bvh.scene, object * 2)); + rtcGetGeometry(kernel_data.device_bvh, object * 2)); isect->prim = hit->primID + (intptr_t)rtcGetGeometryUserData(rtcGetGeometry(inst_scene, hit->geomID)); isect->object = object; diff --git a/intern/cycles/kernel/camera/camera.h b/intern/cycles/kernel/camera/camera.h index 25960a94ddb..7e1b1c037e9 100644 --- a/intern/cycles/kernel/camera/camera.h +++ b/intern/cycles/kernel/camera/camera.h @@ -368,7 +368,7 @@ ccl_device_inline void camera_sample(KernelGlobals kg, ccl_private Ray *ray) { /* pixel filter */ - int filter_table_offset = kernel_data.film.filter_table_offset; + int filter_table_offset = kernel_data.tables.filter_table_offset; float raster_x = x + lookup_table_read(kg, filter_u, filter_table_offset, FILTER_TABLE_SIZE); float raster_y = y + lookup_table_read(kg, filter_v, filter_table_offset, FILTER_TABLE_SIZE); diff --git a/intern/cycles/kernel/data_template.h b/intern/cycles/kernel/data_template.h new file mode 100644 index 00000000000..22f945f1335 --- /dev/null +++ b/intern/cycles/kernel/data_template.h @@ -0,0 +1,199 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright 2011-2022 Blender Foundation */ + +#ifndef KERNEL_STRUCT_BEGIN +# define KERNEL_STRUCT_BEGIN(name, parent) +#endif +#ifndef KERNEL_STRUCT_END +# define KERNEL_STRUCT_END(name) +#endif +#ifndef KERNEL_STRUCT_MEMBER +# define KERNEL_STRUCT_MEMBER(parent, type, name) +#endif + +/* Background. */ + +KERNEL_STRUCT_BEGIN(KernelBackground, background) +/* xyz store direction, w the angle. float4 instead of float3 is used + * to ensure consistent padding/alignment across devices. */ +KERNEL_STRUCT_MEMBER(background, float4, sun) +/* Only shader index. */ +KERNEL_STRUCT_MEMBER(background, int, surface_shader) +KERNEL_STRUCT_MEMBER(background, int, volume_shader) +KERNEL_STRUCT_MEMBER(background, float, volume_step_size) +KERNEL_STRUCT_MEMBER(background, int, transparent) +KERNEL_STRUCT_MEMBER(background, float, transparent_roughness_squared_threshold) +/* Portal sampling. */ +KERNEL_STRUCT_MEMBER(background, float, portal_weight) +KERNEL_STRUCT_MEMBER(background, int, num_portals) +KERNEL_STRUCT_MEMBER(background, int, portal_offset) +/* Sun sampling. */ +KERNEL_STRUCT_MEMBER(background, float, sun_weight) +/* Importance map sampling. */ +KERNEL_STRUCT_MEMBER(background, float, map_weight) +KERNEL_STRUCT_MEMBER(background, int, map_res_x) +KERNEL_STRUCT_MEMBER(background, int, map_res_y) +/* Multiple importance sampling. */ +KERNEL_STRUCT_MEMBER(background, int, use_mis) +/* Lightgroup. */ +KERNEL_STRUCT_MEMBER(background, int, lightgroup) +/* Padding. */ +KERNEL_STRUCT_MEMBER(background, int, pad1) +KERNEL_STRUCT_MEMBER(background, int, pad2) +KERNEL_STRUCT_MEMBER(background, int, pad3) +KERNEL_STRUCT_END(KernelBackground) + +/* BVH: own BVH2 if no native device acceleration struct used. */ + +KERNEL_STRUCT_BEGIN(KernelBVH, bvh) +KERNEL_STRUCT_MEMBER(bvh, int, root) +KERNEL_STRUCT_MEMBER(bvh, int, have_motion) +KERNEL_STRUCT_MEMBER(bvh, int, have_curves) +KERNEL_STRUCT_MEMBER(bvh, int, bvh_layout) +KERNEL_STRUCT_MEMBER(bvh, int, use_bvh_steps) +KERNEL_STRUCT_MEMBER(bvh, int, curve_subdivisions) +KERNEL_STRUCT_MEMBER(bvh, int, pad1) +KERNEL_STRUCT_MEMBER(bvh, int, pad2) +KERNEL_STRUCT_END(KernelBVH) + +/* Film. */ + +KERNEL_STRUCT_BEGIN(KernelFilm, film) +/* XYZ to rendering color space transform. float4 instead of float3 to + * ensure consistent padding/alignment across devices. */ +KERNEL_STRUCT_MEMBER(film, float4, xyz_to_r) +KERNEL_STRUCT_MEMBER(film, float4, xyz_to_g) +KERNEL_STRUCT_MEMBER(film, float4, xyz_to_b) +KERNEL_STRUCT_MEMBER(film, float4, rgb_to_y) +/* Rec709 to rendering color space. */ +KERNEL_STRUCT_MEMBER(film, float4, rec709_to_r) +KERNEL_STRUCT_MEMBER(film, float4, rec709_to_g) +KERNEL_STRUCT_MEMBER(film, float4, rec709_to_b) +KERNEL_STRUCT_MEMBER(film, int, is_rec709) +/* Exposuse. */ +KERNEL_STRUCT_MEMBER(film, float, exposure) +/* Passed used. */ +KERNEL_STRUCT_MEMBER(film, int, pass_flag) +KERNEL_STRUCT_MEMBER(film, int, light_pass_flag) +/* Pass offsets. */ +KERNEL_STRUCT_MEMBER(film, int, pass_stride) +KERNEL_STRUCT_MEMBER(film, int, pass_combined) +KERNEL_STRUCT_MEMBER(film, int, pass_depth) +KERNEL_STRUCT_MEMBER(film, int, pass_position) +KERNEL_STRUCT_MEMBER(film, int, pass_normal) +KERNEL_STRUCT_MEMBER(film, int, pass_roughness) +KERNEL_STRUCT_MEMBER(film, int, pass_motion) +KERNEL_STRUCT_MEMBER(film, int, pass_motion_weight) +KERNEL_STRUCT_MEMBER(film, int, pass_uv) +KERNEL_STRUCT_MEMBER(film, int, pass_object_id) +KERNEL_STRUCT_MEMBER(film, int, pass_material_id) +KERNEL_STRUCT_MEMBER(film, int, pass_diffuse_color) +KERNEL_STRUCT_MEMBER(film, int, pass_glossy_color) +KERNEL_STRUCT_MEMBER(film, int, pass_transmission_color) +KERNEL_STRUCT_MEMBER(film, int, pass_diffuse_indirect) +KERNEL_STRUCT_MEMBER(film, int, pass_glossy_indirect) +KERNEL_STRUCT_MEMBER(film, int, pass_transmission_indirect) +KERNEL_STRUCT_MEMBER(film, int, pass_volume_indirect) +KERNEL_STRUCT_MEMBER(film, int, pass_diffuse_direct) +KERNEL_STRUCT_MEMBER(film, int, pass_glossy_direct) +KERNEL_STRUCT_MEMBER(film, int, pass_transmission_direct) +KERNEL_STRUCT_MEMBER(film, int, pass_volume_direct) +KERNEL_STRUCT_MEMBER(film, int, pass_emission) +KERNEL_STRUCT_MEMBER(film, int, pass_background) +KERNEL_STRUCT_MEMBER(film, int, pass_ao) +KERNEL_STRUCT_MEMBER(film, float, pass_alpha_threshold) +KERNEL_STRUCT_MEMBER(film, int, pass_shadow) +KERNEL_STRUCT_MEMBER(film, float, pass_shadow_scale) +KERNEL_STRUCT_MEMBER(film, int, pass_shadow_catcher) +KERNEL_STRUCT_MEMBER(film, int, pass_shadow_catcher_sample_count) +KERNEL_STRUCT_MEMBER(film, int, pass_shadow_catcher_matte) +/* Cryptomatte. */ +KERNEL_STRUCT_MEMBER(film, int, cryptomatte_passes) +KERNEL_STRUCT_MEMBER(film, int, cryptomatte_depth) +KERNEL_STRUCT_MEMBER(film, int, pass_cryptomatte) +/* Adaptive sampling. */ +KERNEL_STRUCT_MEMBER(film, int, pass_adaptive_aux_buffer) +KERNEL_STRUCT_MEMBER(film, int, pass_sample_count) +/* Mist. */ +KERNEL_STRUCT_MEMBER(film, int, pass_mist) +KERNEL_STRUCT_MEMBER(film, float, mist_start) +KERNEL_STRUCT_MEMBER(film, float, mist_inv_depth) +KERNEL_STRUCT_MEMBER(film, float, mist_falloff) +/* Denoising. */ +KERNEL_STRUCT_MEMBER(film, int, pass_denoising_normal) +KERNEL_STRUCT_MEMBER(film, int, pass_denoising_albedo) +KERNEL_STRUCT_MEMBER(film, int, pass_denoising_depth) +/* AOVs. */ +KERNEL_STRUCT_MEMBER(film, int, pass_aov_color) +KERNEL_STRUCT_MEMBER(film, int, pass_aov_value) +/* Light groups. */ +KERNEL_STRUCT_MEMBER(film, int, pass_lightgroup) +/* Baking. */ +KERNEL_STRUCT_MEMBER(film, int, pass_bake_primitive) +KERNEL_STRUCT_MEMBER(film, int, pass_bake_differential) +/* Shadow catcher. */ +KERNEL_STRUCT_MEMBER(film, int, use_approximate_shadow_catcher) +/* Padding. */ +KERNEL_STRUCT_MEMBER(film, int, pad1) +KERNEL_STRUCT_MEMBER(film, int, pad2) +KERNEL_STRUCT_END(KernelFilm) + +/* Integrator. */ + +KERNEL_STRUCT_BEGIN(KernelIntegrator, integrator) +/* Emission. */ +KERNEL_STRUCT_MEMBER(integrator, int, use_direct_light) +KERNEL_STRUCT_MEMBER(integrator, int, num_distribution) +KERNEL_STRUCT_MEMBER(integrator, int, num_all_lights) +KERNEL_STRUCT_MEMBER(integrator, float, pdf_triangles) +KERNEL_STRUCT_MEMBER(integrator, float, pdf_lights) +KERNEL_STRUCT_MEMBER(integrator, float, light_inv_rr_threshold) +/* Bounces. */ +KERNEL_STRUCT_MEMBER(integrator, int, min_bounce) +KERNEL_STRUCT_MEMBER(integrator, int, max_bounce) +KERNEL_STRUCT_MEMBER(integrator, int, max_diffuse_bounce) +KERNEL_STRUCT_MEMBER(integrator, int, max_glossy_bounce) +KERNEL_STRUCT_MEMBER(integrator, int, max_transmission_bounce) +KERNEL_STRUCT_MEMBER(integrator, int, max_volume_bounce) +/* AO bounces. */ +KERNEL_STRUCT_MEMBER(integrator, int, ao_bounces) +KERNEL_STRUCT_MEMBER(integrator, float, ao_bounces_distance) +KERNEL_STRUCT_MEMBER(integrator, float, ao_bounces_factor) +KERNEL_STRUCT_MEMBER(integrator, float, ao_additive_factor) +/* Transparency. */ +KERNEL_STRUCT_MEMBER(integrator, int, transparent_min_bounce) +KERNEL_STRUCT_MEMBER(integrator, int, transparent_max_bounce) +KERNEL_STRUCT_MEMBER(integrator, int, transparent_shadows) +/* Caustics. */ +KERNEL_STRUCT_MEMBER(integrator, int, caustics_reflective) +KERNEL_STRUCT_MEMBER(integrator, int, caustics_refractive) +KERNEL_STRUCT_MEMBER(integrator, float, filter_glossy) +/* Seed. */ +KERNEL_STRUCT_MEMBER(integrator, int, seed) +/* Clamp. */ +KERNEL_STRUCT_MEMBER(integrator, float, sample_clamp_direct) +KERNEL_STRUCT_MEMBER(integrator, float, sample_clamp_indirect) +/* MIS. */ +KERNEL_STRUCT_MEMBER(integrator, int, use_lamp_mis) +/* Caustics. */ +KERNEL_STRUCT_MEMBER(integrator, int, use_caustics) +/* Sampling pattern. */ +KERNEL_STRUCT_MEMBER(integrator, int, sampling_pattern) +KERNEL_STRUCT_MEMBER(integrator, float, scrambling_distance) +/* Volume render. */ +KERNEL_STRUCT_MEMBER(integrator, int, use_volumes) +KERNEL_STRUCT_MEMBER(integrator, int, volume_max_steps) +KERNEL_STRUCT_MEMBER(integrator, float, volume_step_rate) +/* Shadow catcher. */ +KERNEL_STRUCT_MEMBER(integrator, int, has_shadow_catcher) +/* Closure filter. */ +KERNEL_STRUCT_MEMBER(integrator, int, filter_closures) +/* MIS debugging. */ +KERNEL_STRUCT_MEMBER(integrator, int, direct_light_sampling_type) +/* Padding */ +KERNEL_STRUCT_MEMBER(integrator, int, pad1) +KERNEL_STRUCT_END(KernelIntegrator) + +#undef KERNEL_STRUCT_BEGIN +#undef KERNEL_STRUCT_MEMBER +#undef KERNEL_STRUCT_END diff --git a/intern/cycles/kernel/types.h b/intern/cycles/kernel/types.h index f2e61d25002..72cee6ae344 100644 --- a/intern/cycles/kernel/types.h +++ b/intern/cycles/kernel/types.h @@ -1072,94 +1072,6 @@ typedef struct KernelCamera { } KernelCamera; static_assert_align(KernelCamera, 16); -typedef struct KernelFilm { - float exposure; - int pass_flag; - - int light_pass_flag; - int pass_stride; - - int pass_combined; - int pass_depth; - int pass_position; - int pass_normal; - int pass_roughness; - int pass_motion; - - int pass_motion_weight; - int pass_uv; - int pass_object_id; - int pass_material_id; - - int pass_diffuse_color; - int pass_glossy_color; - int pass_transmission_color; - - int pass_diffuse_indirect; - int pass_glossy_indirect; - int pass_transmission_indirect; - int pass_volume_indirect; - - int pass_diffuse_direct; - int pass_glossy_direct; - int pass_transmission_direct; - int pass_volume_direct; - - int pass_emission; - int pass_background; - int pass_ao; - float pass_alpha_threshold; - - int pass_shadow; - float pass_shadow_scale; - - int pass_shadow_catcher; - int pass_shadow_catcher_sample_count; - int pass_shadow_catcher_matte; - - int filter_table_offset; - - int cryptomatte_passes; - int cryptomatte_depth; - int pass_cryptomatte; - - int pass_adaptive_aux_buffer; - int pass_sample_count; - - int pass_mist; - float mist_start; - float mist_inv_depth; - float mist_falloff; - - int pass_denoising_normal; - int pass_denoising_albedo; - int pass_denoising_depth; - - int pass_aov_color; - int pass_aov_value; - int pass_lightgroup; - - /* XYZ to rendering color space transform. float4 instead of float3 to - * ensure consistent padding/alignment across devices. */ - float4 xyz_to_r; - float4 xyz_to_g; - float4 xyz_to_b; - float4 rgb_to_y; - /* Rec709 to rendering color space. */ - float4 rec709_to_r; - float4 rec709_to_g; - float4 rec709_to_b; - int is_rec709; - - int pass_bake_primitive; - int pass_bake_differential; - - int use_approximate_shadow_catcher; - - int pad1; -} KernelFilm; -static_assert_align(KernelFilm, 16); - typedef struct KernelFilmConvert { int pass_offset; int pass_stride; @@ -1201,108 +1113,6 @@ typedef struct KernelFilmConvert { } KernelFilmConvert; static_assert_align(KernelFilmConvert, 16); -typedef struct KernelBackground { - /* only shader index */ - int surface_shader; - int volume_shader; - float volume_step_size; - int transparent; - float transparent_roughness_squared_threshold; - - /* portal sampling */ - float portal_weight; - int num_portals; - int portal_offset; - - /* sun sampling */ - float sun_weight; - /* xyz store direction, w the angle. float4 instead of float3 is used - * to ensure consistent padding/alignment across devices. */ - float4 sun; - - /* map sampling */ - float map_weight; - int map_res_x; - int map_res_y; - - int use_mis; - - int lightgroup; - - /* Padding */ - int pad1, pad2; -} KernelBackground; -static_assert_align(KernelBackground, 16); - -typedef struct KernelIntegrator { - /* emission */ - int use_direct_light; - int num_distribution; - int num_all_lights; - float pdf_triangles; - float pdf_lights; - float light_inv_rr_threshold; - - /* bounces */ - int min_bounce; - int max_bounce; - - int max_diffuse_bounce; - int max_glossy_bounce; - int max_transmission_bounce; - int max_volume_bounce; - - /* AO bounces */ - int ao_bounces; - float ao_bounces_distance; - float ao_bounces_factor; - float ao_additive_factor; - - /* transparent */ - int transparent_min_bounce; - int transparent_max_bounce; - int transparent_shadows; - - /* caustics */ - int caustics_reflective; - int caustics_refractive; - float filter_glossy; - - /* seed */ - int seed; - - /* clamp */ - float sample_clamp_direct; - float sample_clamp_indirect; - - /* mis */ - int use_lamp_mis; - - /* caustics */ - int use_caustics; - - /* sampler */ - int sampling_pattern; - - /* volume render */ - int use_volumes; - int volume_max_steps; - float volume_step_rate; - - int has_shadow_catcher; - float scrambling_distance; - - /* Closure filter. */ - int filter_closures; - - /* MIS debugging. */ - int direct_light_sampling_type; - - /* padding */ - int pad1; -} KernelIntegrator; -static_assert_align(KernelIntegrator, 16); - typedef enum KernelBVHLayout { BVH_LAYOUT_NONE = 0, @@ -1320,36 +1130,19 @@ typedef enum KernelBVHLayout { BVH_LAYOUT_ALL = BVH_LAYOUT_BVH2 | BVH_LAYOUT_EMBREE | BVH_LAYOUT_OPTIX | BVH_LAYOUT_METAL, } KernelBVHLayout; -typedef struct KernelBVH { - /* Own BVH */ - int root; - int have_motion; - int have_curves; - int bvh_layout; - int use_bvh_steps; - int curve_subdivisions; - - /* Custom BVH */ -#ifdef __KERNEL_OPTIX__ - OptixTraversableHandle scene; -#elif defined __METALRT__ - metalrt_as_type scene; -#else -# ifdef __EMBREE__ - RTCScene scene; -# ifndef __KERNEL_64_BIT__ - int pad2; -# endif -# else - int scene, pad2; -# endif -#endif -} KernelBVH; -static_assert_align(KernelBVH, 16); +/* Specialized struct that can become constants in dynamic compilation. */ +#define KERNEL_STRUCT_BEGIN(name, parent) struct name { +#define KERNEL_STRUCT_END(name) \ + } \ + ; \ + static_assert_align(name, 16); +#define KERNEL_STRUCT_MEMBER(parent, type, name) type name; +#include "kernel/data_template.h" typedef struct KernelTables { int beckmann_offset; - int pad1, pad2, pad3; + int filter_table_offset; + int pad1, pad2; } KernelTables; static_assert_align(KernelTables, 16); @@ -1362,18 +1155,37 @@ typedef struct KernelBake { static_assert_align(KernelBake, 16); typedef struct KernelData { + /* Features and limits. */ uint kernel_features; uint max_closures; uint max_shaders; uint volume_stack_size; + /* Always dynamic data mambers. */ KernelCamera cam; - KernelFilm film; - KernelBackground background; - KernelIntegrator integrator; - KernelBVH bvh; - KernelTables tables; KernelBake bake; + KernelTables tables; + + /* Potentially specialized data members. */ +#define KERNEL_STRUCT_BEGIN(name, parent) name parent; +#include "kernel/data_template.h" + + /* Device specific BVH. */ +#ifdef __KERNEL_OPTIX__ + OptixTraversableHandle device_bvh; +#elif defined __METALRT__ + metalrt_as_type device_bvh; +#else +# ifdef __EMBREE__ + RTCScene device_bvh; +# ifndef __KERNEL_64_BIT__ + int pad1; +# endif +# else + int device_bvh, pad1; +# endif +#endif + int pad2, pad3; } KernelData; static_assert_align(KernelData, 16); diff --git a/intern/cycles/scene/film.cpp b/intern/cycles/scene/film.cpp index 8239ee84b82..a6a8f90a449 100644 --- a/intern/cycles/scene/film.cpp +++ b/intern/cycles/scene/film.cpp @@ -394,7 +394,7 @@ void Film::device_update(Device *device, DeviceScene *dscene, Scene *scene) vector table = filter_table(filter_type, filter_width); scene->lookup_tables->remove_table(&filter_table_offset_); filter_table_offset_ = scene->lookup_tables->add_table(dscene, table); - kfilm->filter_table_offset = (int)filter_table_offset_; + dscene->data.tables.filter_table_offset = (int)filter_table_offset_; /* mist pass parameters */ kfilm->mist_start = mist_start; diff --git a/intern/cycles/scene/geometry.cpp b/intern/cycles/scene/geometry.cpp index bdc8839e277..67ff118692e 100644 --- a/intern/cycles/scene/geometry.cpp +++ b/intern/cycles/scene/geometry.cpp @@ -1362,7 +1362,7 @@ void GeometryManager::device_update_bvh(Device *device, dscene->data.bvh.use_bvh_steps = (scene->params.num_bvh_time_steps != 0); dscene->data.bvh.curve_subdivisions = scene->params.curve_subdivisions(); /* The scene handle is set in 'CPUDevice::const_copy_to' and 'OptiXDevice::const_copy_to' */ - dscene->data.bvh.scene = 0; + dscene->data.device_bvh = 0; } /* Set of flags used to help determining what data has been modified or needs reallocation, so we -- cgit v1.2.3 From 5653c5fcdd9f424dc05ddf73b18ba8294daf4788 Mon Sep 17 00:00:00 2001 From: Michael Jones Date: Tue, 12 Jul 2022 17:22:36 +0200 Subject: Cycles: keep track of SVM nodes used in kernels To be used for specialization in Metal, to automatically leave out unused nodes from the kernel. Ref D14645 --- intern/cycles/kernel/CMakeLists.txt | 1 + intern/cycles/kernel/data_template.h | 7 + intern/cycles/kernel/svm/node_types_template.h | 110 ++++ intern/cycles/kernel/svm/svm.h | 683 +++++++++++++------------ intern/cycles/kernel/svm/types.h | 101 +--- intern/cycles/scene/shader_nodes.cpp | 6 +- intern/cycles/scene/svm.cpp | 12 +- intern/cycles/scene/svm.h | 1 + 8 files changed, 477 insertions(+), 444 deletions(-) create mode 100644 intern/cycles/kernel/svm/node_types_template.h diff --git a/intern/cycles/kernel/CMakeLists.txt b/intern/cycles/kernel/CMakeLists.txt index 4ff947e7136..527cc4ec111 100644 --- a/intern/cycles/kernel/CMakeLists.txt +++ b/intern/cycles/kernel/CMakeLists.txt @@ -154,6 +154,7 @@ set(SRC_KERNEL_SVM_HEADERS svm/math_util.h svm/mix.h svm/musgrave.h + svm/node_types_template.h svm/noise.h svm/noisetex.h svm/normal.h diff --git a/intern/cycles/kernel/data_template.h b/intern/cycles/kernel/data_template.h index 22f945f1335..b06ac62a5d8 100644 --- a/intern/cycles/kernel/data_template.h +++ b/intern/cycles/kernel/data_template.h @@ -194,6 +194,13 @@ KERNEL_STRUCT_MEMBER(integrator, int, direct_light_sampling_type) KERNEL_STRUCT_MEMBER(integrator, int, pad1) KERNEL_STRUCT_END(KernelIntegrator) +/* SVM. For shader specialization. */ + +KERNEL_STRUCT_BEGIN(KernelSVMUsage, svm_usage) +#define SHADER_NODE_TYPE(type) KERNEL_STRUCT_MEMBER(svm_usage, int, type) +#include "kernel/svm/node_types_template.h" +KERNEL_STRUCT_END(KernelSVMUsage) + #undef KERNEL_STRUCT_BEGIN #undef KERNEL_STRUCT_MEMBER #undef KERNEL_STRUCT_END diff --git a/intern/cycles/kernel/svm/node_types_template.h b/intern/cycles/kernel/svm/node_types_template.h new file mode 100644 index 00000000000..39d279be4cb --- /dev/null +++ b/intern/cycles/kernel/svm/node_types_template.h @@ -0,0 +1,110 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright 2011-2022 Blender Foundation */ + +#ifndef SHADER_NODE_TYPE +# define SHADER_NODE_TYPE(name) +#endif + +/* NOTE: for best OpenCL performance, item definition in the enum must + * match the switch case order in `svm.h`. */ + +SHADER_NODE_TYPE(NODE_END) +SHADER_NODE_TYPE(NODE_SHADER_JUMP) +SHADER_NODE_TYPE(NODE_CLOSURE_BSDF) +SHADER_NODE_TYPE(NODE_CLOSURE_EMISSION) +SHADER_NODE_TYPE(NODE_CLOSURE_BACKGROUND) +SHADER_NODE_TYPE(NODE_CLOSURE_SET_WEIGHT) +SHADER_NODE_TYPE(NODE_CLOSURE_WEIGHT) +SHADER_NODE_TYPE(NODE_EMISSION_WEIGHT) +SHADER_NODE_TYPE(NODE_MIX_CLOSURE) +SHADER_NODE_TYPE(NODE_JUMP_IF_ZERO) +SHADER_NODE_TYPE(NODE_JUMP_IF_ONE) +SHADER_NODE_TYPE(NODE_GEOMETRY) +SHADER_NODE_TYPE(NODE_CONVERT) +SHADER_NODE_TYPE(NODE_TEX_COORD) +SHADER_NODE_TYPE(NODE_VALUE_F) +SHADER_NODE_TYPE(NODE_VALUE_V) +SHADER_NODE_TYPE(NODE_ATTR) +SHADER_NODE_TYPE(NODE_VERTEX_COLOR) +SHADER_NODE_TYPE(NODE_GEOMETRY_BUMP_DX) +SHADER_NODE_TYPE(NODE_GEOMETRY_BUMP_DY) +SHADER_NODE_TYPE(NODE_SET_DISPLACEMENT) +SHADER_NODE_TYPE(NODE_DISPLACEMENT) +SHADER_NODE_TYPE(NODE_VECTOR_DISPLACEMENT) +SHADER_NODE_TYPE(NODE_TEX_IMAGE) +SHADER_NODE_TYPE(NODE_TEX_IMAGE_BOX) +SHADER_NODE_TYPE(NODE_TEX_NOISE) +SHADER_NODE_TYPE(NODE_SET_BUMP) +SHADER_NODE_TYPE(NODE_ATTR_BUMP_DX) +SHADER_NODE_TYPE(NODE_ATTR_BUMP_DY) +SHADER_NODE_TYPE(NODE_VERTEX_COLOR_BUMP_DX) +SHADER_NODE_TYPE(NODE_VERTEX_COLOR_BUMP_DY) +SHADER_NODE_TYPE(NODE_TEX_COORD_BUMP_DX) +SHADER_NODE_TYPE(NODE_TEX_COORD_BUMP_DY) +SHADER_NODE_TYPE(NODE_CLOSURE_SET_NORMAL) +SHADER_NODE_TYPE(NODE_ENTER_BUMP_EVAL) +SHADER_NODE_TYPE(NODE_LEAVE_BUMP_EVAL) +SHADER_NODE_TYPE(NODE_HSV) +SHADER_NODE_TYPE(NODE_CLOSURE_HOLDOUT) +SHADER_NODE_TYPE(NODE_FRESNEL) +SHADER_NODE_TYPE(NODE_LAYER_WEIGHT) +SHADER_NODE_TYPE(NODE_CLOSURE_VOLUME) +SHADER_NODE_TYPE(NODE_PRINCIPLED_VOLUME) +SHADER_NODE_TYPE(NODE_MATH) +SHADER_NODE_TYPE(NODE_VECTOR_MATH) +SHADER_NODE_TYPE(NODE_RGB_RAMP) +SHADER_NODE_TYPE(NODE_GAMMA) +SHADER_NODE_TYPE(NODE_BRIGHTCONTRAST) +SHADER_NODE_TYPE(NODE_LIGHT_PATH) +SHADER_NODE_TYPE(NODE_OBJECT_INFO) +SHADER_NODE_TYPE(NODE_PARTICLE_INFO) +SHADER_NODE_TYPE(NODE_HAIR_INFO) +SHADER_NODE_TYPE(NODE_POINT_INFO) +SHADER_NODE_TYPE(NODE_TEXTURE_MAPPING) +SHADER_NODE_TYPE(NODE_MAPPING) +SHADER_NODE_TYPE(NODE_MIN_MAX) +SHADER_NODE_TYPE(NODE_CAMERA) +SHADER_NODE_TYPE(NODE_TEX_ENVIRONMENT) +SHADER_NODE_TYPE(NODE_TEX_SKY) +SHADER_NODE_TYPE(NODE_TEX_GRADIENT) +SHADER_NODE_TYPE(NODE_TEX_VORONOI) +SHADER_NODE_TYPE(NODE_TEX_MUSGRAVE) +SHADER_NODE_TYPE(NODE_TEX_WAVE) +SHADER_NODE_TYPE(NODE_TEX_MAGIC) +SHADER_NODE_TYPE(NODE_TEX_CHECKER) +SHADER_NODE_TYPE(NODE_TEX_BRICK) +SHADER_NODE_TYPE(NODE_TEX_WHITE_NOISE) +SHADER_NODE_TYPE(NODE_NORMAL) +SHADER_NODE_TYPE(NODE_LIGHT_FALLOFF) +SHADER_NODE_TYPE(NODE_IES) +SHADER_NODE_TYPE(NODE_CURVES) +SHADER_NODE_TYPE(NODE_TANGENT) +SHADER_NODE_TYPE(NODE_NORMAL_MAP) +SHADER_NODE_TYPE(NODE_INVERT) +SHADER_NODE_TYPE(NODE_MIX) +SHADER_NODE_TYPE(NODE_SEPARATE_COLOR) +SHADER_NODE_TYPE(NODE_COMBINE_COLOR) +SHADER_NODE_TYPE(NODE_SEPARATE_VECTOR) +SHADER_NODE_TYPE(NODE_COMBINE_VECTOR) +SHADER_NODE_TYPE(NODE_SEPARATE_HSV) +SHADER_NODE_TYPE(NODE_COMBINE_HSV) +SHADER_NODE_TYPE(NODE_VECTOR_ROTATE) +SHADER_NODE_TYPE(NODE_VECTOR_TRANSFORM) +SHADER_NODE_TYPE(NODE_WIREFRAME) +SHADER_NODE_TYPE(NODE_WAVELENGTH) +SHADER_NODE_TYPE(NODE_BLACKBODY) +SHADER_NODE_TYPE(NODE_MAP_RANGE) +SHADER_NODE_TYPE(NODE_VECTOR_MAP_RANGE) +SHADER_NODE_TYPE(NODE_CLAMP) +SHADER_NODE_TYPE(NODE_BEVEL) +SHADER_NODE_TYPE(NODE_AMBIENT_OCCLUSION) +SHADER_NODE_TYPE(NODE_TEX_VOXEL) +SHADER_NODE_TYPE(NODE_AOV_START) +SHADER_NODE_TYPE(NODE_AOV_COLOR) +SHADER_NODE_TYPE(NODE_AOV_VALUE) +SHADER_NODE_TYPE(NODE_FLOAT_CURVE) + +/* Padding for struct alignment. */ +SHADER_NODE_TYPE(NODE_PAD1) + +#undef SHADER_NODE_TYPE diff --git a/intern/cycles/kernel/svm/svm.h b/intern/cycles/kernel/svm/svm.h index 8fd41ec8531..9840cda3655 100644 --- a/intern/cycles/kernel/svm/svm.h +++ b/intern/cycles/kernel/svm/svm.h @@ -204,6 +204,8 @@ CCL_NAMESPACE_END CCL_NAMESPACE_BEGIN +#define SVM_CASE(node) case node: + /* Main Interpreter Loop */ template ccl_device void svm_eval_nodes(KernelGlobals kg, @@ -219,9 +221,10 @@ ccl_device void svm_eval_nodes(KernelGlobals kg, uint4 node = read_node(kg, &offset); switch (node.x) { - case NODE_END: - return; - case NODE_SHADER_JUMP: { + SVM_CASE(NODE_END) + return; + SVM_CASE(NODE_SHADER_JUMP) + { if (type == SHADER_TYPE_SURFACE) offset = node.y; else if (type == SHADER_TYPE_VOLUME) @@ -232,351 +235,349 @@ ccl_device void svm_eval_nodes(KernelGlobals kg, return; break; } - case NODE_CLOSURE_BSDF: - offset = svm_node_closure_bsdf( - kg, sd, stack, node, path_flag, offset); - break; - case NODE_CLOSURE_EMISSION: - IF_KERNEL_NODES_FEATURE(EMISSION) - { - svm_node_closure_emission(sd, stack, node); - } - break; - case NODE_CLOSURE_BACKGROUND: - IF_KERNEL_NODES_FEATURE(EMISSION) - { - svm_node_closure_background(sd, stack, node); - } - break; - case NODE_CLOSURE_SET_WEIGHT: - svm_node_closure_set_weight(sd, node.y, node.z, node.w); - break; - case NODE_CLOSURE_WEIGHT: - svm_node_closure_weight(sd, stack, node.y); - break; - case NODE_EMISSION_WEIGHT: - IF_KERNEL_NODES_FEATURE(EMISSION) - { - svm_node_emission_weight(kg, sd, stack, node); - } - break; - case NODE_MIX_CLOSURE: - svm_node_mix_closure(sd, stack, node); - break; - case NODE_JUMP_IF_ZERO: - if (stack_load_float(stack, node.z) <= 0.0f) - offset += node.y; - break; - case NODE_JUMP_IF_ONE: - if (stack_load_float(stack, node.z) >= 1.0f) - offset += node.y; - break; - case NODE_GEOMETRY: - svm_node_geometry(kg, sd, stack, node.y, node.z); - break; - case NODE_CONVERT: - svm_node_convert(kg, sd, stack, node.y, node.z, node.w); - break; - case NODE_TEX_COORD: - offset = svm_node_tex_coord(kg, sd, path_flag, stack, node, offset); - break; - case NODE_VALUE_F: - svm_node_value_f(kg, sd, stack, node.y, node.z); - break; - case NODE_VALUE_V: - offset = svm_node_value_v(kg, sd, stack, node.y, offset); - break; - case NODE_ATTR: - svm_node_attr(kg, sd, stack, node); - break; - case NODE_VERTEX_COLOR: - svm_node_vertex_color(kg, sd, stack, node.y, node.z, node.w); - break; - case NODE_GEOMETRY_BUMP_DX: - IF_KERNEL_NODES_FEATURE(BUMP) - { - svm_node_geometry_bump_dx(kg, sd, stack, node.y, node.z); - } - break; - case NODE_GEOMETRY_BUMP_DY: - IF_KERNEL_NODES_FEATURE(BUMP) - { - svm_node_geometry_bump_dy(kg, sd, stack, node.y, node.z); - } - break; - case NODE_SET_DISPLACEMENT: - svm_node_set_displacement(kg, sd, stack, node.y); - break; - case NODE_DISPLACEMENT: - svm_node_displacement(kg, sd, stack, node); - break; - case NODE_VECTOR_DISPLACEMENT: - offset = svm_node_vector_displacement(kg, sd, stack, node, offset); - break; - case NODE_TEX_IMAGE: - offset = svm_node_tex_image(kg, sd, stack, node, offset); - break; - case NODE_TEX_IMAGE_BOX: - svm_node_tex_image_box(kg, sd, stack, node); - break; - case NODE_TEX_NOISE: - offset = svm_node_tex_noise(kg, sd, stack, node.y, node.z, node.w, offset); - break; - case NODE_SET_BUMP: - svm_node_set_bump(kg, sd, stack, node); - break; - case NODE_ATTR_BUMP_DX: - IF_KERNEL_NODES_FEATURE(BUMP) - { - svm_node_attr_bump_dx(kg, sd, stack, node); - } - break; - case NODE_ATTR_BUMP_DY: - IF_KERNEL_NODES_FEATURE(BUMP) - { - svm_node_attr_bump_dy(kg, sd, stack, node); - } - break; - case NODE_VERTEX_COLOR_BUMP_DX: - IF_KERNEL_NODES_FEATURE(BUMP) - { - svm_node_vertex_color_bump_dx(kg, sd, stack, node.y, node.z, node.w); - } - break; - case NODE_VERTEX_COLOR_BUMP_DY: - IF_KERNEL_NODES_FEATURE(BUMP) - { - svm_node_vertex_color_bump_dy(kg, sd, stack, node.y, node.z, node.w); - } - break; - case NODE_TEX_COORD_BUMP_DX: - IF_KERNEL_NODES_FEATURE(BUMP) - { - offset = svm_node_tex_coord_bump_dx(kg, sd, path_flag, stack, node, offset); - } - break; - case NODE_TEX_COORD_BUMP_DY: - IF_KERNEL_NODES_FEATURE(BUMP) - { - offset = svm_node_tex_coord_bump_dy(kg, sd, path_flag, stack, node, offset); - } - break; - case NODE_CLOSURE_SET_NORMAL: - IF_KERNEL_NODES_FEATURE(BUMP) - { - svm_node_set_normal(kg, sd, stack, node.y, node.z); - } - break; - case NODE_ENTER_BUMP_EVAL: - IF_KERNEL_NODES_FEATURE(BUMP_STATE) - { - svm_node_enter_bump_eval(kg, sd, stack, node.y); - } - break; - case NODE_LEAVE_BUMP_EVAL: - IF_KERNEL_NODES_FEATURE(BUMP_STATE) - { - svm_node_leave_bump_eval(kg, sd, stack, node.y); - } - break; - case NODE_HSV: - svm_node_hsv(kg, sd, stack, node); - break; - - case NODE_CLOSURE_HOLDOUT: - svm_node_closure_holdout(sd, stack, node); - break; - case NODE_FRESNEL: - svm_node_fresnel(sd, stack, node.y, node.z, node.w); - break; - case NODE_LAYER_WEIGHT: - svm_node_layer_weight(sd, stack, node); - break; - case NODE_CLOSURE_VOLUME: - IF_KERNEL_NODES_FEATURE(VOLUME) - { - svm_node_closure_volume(kg, sd, stack, node); - } - break; - case NODE_PRINCIPLED_VOLUME: - IF_KERNEL_NODES_FEATURE(VOLUME) - { - offset = svm_node_principled_volume(kg, sd, stack, node, path_flag, offset); - } - break; - case NODE_MATH: - svm_node_math(kg, sd, stack, node.y, node.z, node.w); - break; - case NODE_VECTOR_MATH: - offset = svm_node_vector_math(kg, sd, stack, node.y, node.z, node.w, offset); - break; - case NODE_RGB_RAMP: - offset = svm_node_rgb_ramp(kg, sd, stack, node, offset); - break; - case NODE_GAMMA: - svm_node_gamma(sd, stack, node.y, node.z, node.w); - break; - case NODE_BRIGHTCONTRAST: - svm_node_brightness(sd, stack, node.y, node.z, node.w); - break; - case NODE_LIGHT_PATH: - svm_node_light_path(kg, state, sd, stack, node.y, node.z, path_flag); - break; - case NODE_OBJECT_INFO: - svm_node_object_info(kg, sd, stack, node.y, node.z); - break; - case NODE_PARTICLE_INFO: - svm_node_particle_info(kg, sd, stack, node.y, node.z); - break; + SVM_CASE(NODE_CLOSURE_BSDF) + offset = svm_node_closure_bsdf( + kg, sd, stack, node, path_flag, offset); + break; + SVM_CASE(NODE_CLOSURE_EMISSION) + IF_KERNEL_NODES_FEATURE(EMISSION) + { + svm_node_closure_emission(sd, stack, node); + } + break; + SVM_CASE(NODE_CLOSURE_BACKGROUND) + IF_KERNEL_NODES_FEATURE(EMISSION) + { + svm_node_closure_background(sd, stack, node); + } + break; + SVM_CASE(NODE_CLOSURE_SET_WEIGHT) + svm_node_closure_set_weight(sd, node.y, node.z, node.w); + break; + SVM_CASE(NODE_CLOSURE_WEIGHT) + svm_node_closure_weight(sd, stack, node.y); + break; + SVM_CASE(NODE_EMISSION_WEIGHT) + IF_KERNEL_NODES_FEATURE(EMISSION) + { + svm_node_emission_weight(kg, sd, stack, node); + } + break; + SVM_CASE(NODE_MIX_CLOSURE) + svm_node_mix_closure(sd, stack, node); + break; + SVM_CASE(NODE_JUMP_IF_ZERO) + if (stack_load_float(stack, node.z) <= 0.0f) + offset += node.y; + break; + SVM_CASE(NODE_JUMP_IF_ONE) + if (stack_load_float(stack, node.z) >= 1.0f) + offset += node.y; + break; + SVM_CASE(NODE_GEOMETRY) + svm_node_geometry(kg, sd, stack, node.y, node.z); + break; + SVM_CASE(NODE_CONVERT) + svm_node_convert(kg, sd, stack, node.y, node.z, node.w); + break; + SVM_CASE(NODE_TEX_COORD) + offset = svm_node_tex_coord(kg, sd, path_flag, stack, node, offset); + break; + SVM_CASE(NODE_VALUE_F) + svm_node_value_f(kg, sd, stack, node.y, node.z); + break; + SVM_CASE(NODE_VALUE_V) + offset = svm_node_value_v(kg, sd, stack, node.y, offset); + break; + SVM_CASE(NODE_ATTR) + svm_node_attr(kg, sd, stack, node); + break; + SVM_CASE(NODE_VERTEX_COLOR) + svm_node_vertex_color(kg, sd, stack, node.y, node.z, node.w); + break; + SVM_CASE(NODE_GEOMETRY_BUMP_DX) + IF_KERNEL_NODES_FEATURE(BUMP) + { + svm_node_geometry_bump_dx(kg, sd, stack, node.y, node.z); + } + break; + SVM_CASE(NODE_GEOMETRY_BUMP_DY) + IF_KERNEL_NODES_FEATURE(BUMP) + { + svm_node_geometry_bump_dy(kg, sd, stack, node.y, node.z); + } + break; + SVM_CASE(NODE_SET_DISPLACEMENT) + svm_node_set_displacement(kg, sd, stack, node.y); + break; + SVM_CASE(NODE_DISPLACEMENT) + svm_node_displacement(kg, sd, stack, node); + break; + SVM_CASE(NODE_VECTOR_DISPLACEMENT) + offset = svm_node_vector_displacement(kg, sd, stack, node, offset); + break; + SVM_CASE(NODE_TEX_IMAGE) + offset = svm_node_tex_image(kg, sd, stack, node, offset); + break; + SVM_CASE(NODE_TEX_IMAGE_BOX) + svm_node_tex_image_box(kg, sd, stack, node); + break; + SVM_CASE(NODE_TEX_NOISE) + offset = svm_node_tex_noise(kg, sd, stack, node.y, node.z, node.w, offset); + break; + SVM_CASE(NODE_SET_BUMP) + svm_node_set_bump(kg, sd, stack, node); + break; + SVM_CASE(NODE_ATTR_BUMP_DX) + IF_KERNEL_NODES_FEATURE(BUMP) + { + svm_node_attr_bump_dx(kg, sd, stack, node); + } + break; + SVM_CASE(NODE_ATTR_BUMP_DY) + IF_KERNEL_NODES_FEATURE(BUMP) + { + svm_node_attr_bump_dy(kg, sd, stack, node); + } + break; + SVM_CASE(NODE_VERTEX_COLOR_BUMP_DX) + IF_KERNEL_NODES_FEATURE(BUMP) + { + svm_node_vertex_color_bump_dx(kg, sd, stack, node.y, node.z, node.w); + } + break; + SVM_CASE(NODE_VERTEX_COLOR_BUMP_DY) + IF_KERNEL_NODES_FEATURE(BUMP) + { + svm_node_vertex_color_bump_dy(kg, sd, stack, node.y, node.z, node.w); + } + break; + SVM_CASE(NODE_TEX_COORD_BUMP_DX) + IF_KERNEL_NODES_FEATURE(BUMP) + { + offset = svm_node_tex_coord_bump_dx(kg, sd, path_flag, stack, node, offset); + } + break; + SVM_CASE(NODE_TEX_COORD_BUMP_DY) + IF_KERNEL_NODES_FEATURE(BUMP) + { + offset = svm_node_tex_coord_bump_dy(kg, sd, path_flag, stack, node, offset); + } + break; + SVM_CASE(NODE_CLOSURE_SET_NORMAL) + IF_KERNEL_NODES_FEATURE(BUMP) + { + svm_node_set_normal(kg, sd, stack, node.y, node.z); + } + break; + SVM_CASE(NODE_ENTER_BUMP_EVAL) + IF_KERNEL_NODES_FEATURE(BUMP_STATE) + { + svm_node_enter_bump_eval(kg, sd, stack, node.y); + } + break; + SVM_CASE(NODE_LEAVE_BUMP_EVAL) + IF_KERNEL_NODES_FEATURE(BUMP_STATE) + { + svm_node_leave_bump_eval(kg, sd, stack, node.y); + } + break; + SVM_CASE(NODE_HSV) + svm_node_hsv(kg, sd, stack, node); + break; + SVM_CASE(NODE_CLOSURE_HOLDOUT) + svm_node_closure_holdout(sd, stack, node); + break; + SVM_CASE(NODE_FRESNEL) + svm_node_fresnel(sd, stack, node.y, node.z, node.w); + break; + SVM_CASE(NODE_LAYER_WEIGHT) + svm_node_layer_weight(sd, stack, node); + break; + SVM_CASE(NODE_CLOSURE_VOLUME) + IF_KERNEL_NODES_FEATURE(VOLUME) + { + svm_node_closure_volume(kg, sd, stack, node); + } + break; + SVM_CASE(NODE_PRINCIPLED_VOLUME) + IF_KERNEL_NODES_FEATURE(VOLUME) + { + offset = svm_node_principled_volume(kg, sd, stack, node, path_flag, offset); + } + break; + SVM_CASE(NODE_MATH) + svm_node_math(kg, sd, stack, node.y, node.z, node.w); + break; + SVM_CASE(NODE_VECTOR_MATH) + offset = svm_node_vector_math(kg, sd, stack, node.y, node.z, node.w, offset); + break; + SVM_CASE(NODE_RGB_RAMP) + offset = svm_node_rgb_ramp(kg, sd, stack, node, offset); + break; + SVM_CASE(NODE_GAMMA) + svm_node_gamma(sd, stack, node.y, node.z, node.w); + break; + SVM_CASE(NODE_BRIGHTCONTRAST) + svm_node_brightness(sd, stack, node.y, node.z, node.w); + break; + SVM_CASE(NODE_LIGHT_PATH) + svm_node_light_path(kg, state, sd, stack, node.y, node.z, path_flag); + break; + SVM_CASE(NODE_OBJECT_INFO) + svm_node_object_info(kg, sd, stack, node.y, node.z); + break; + SVM_CASE(NODE_PARTICLE_INFO) + svm_node_particle_info(kg, sd, stack, node.y, node.z); + break; #if defined(__HAIR__) - case NODE_HAIR_INFO: - svm_node_hair_info(kg, sd, stack, node.y, node.z); - break; + SVM_CASE(NODE_HAIR_INFO) + svm_node_hair_info(kg, sd, stack, node.y, node.z); + break; #endif #if defined(__POINTCLOUD__) - case NODE_POINT_INFO: - svm_node_point_info(kg, sd, stack, node.y, node.z); - break; + SVM_CASE(NODE_POINT_INFO) + svm_node_point_info(kg, sd, stack, node.y, node.z); + break; #endif - case NODE_TEXTURE_MAPPING: - offset = svm_node_texture_mapping(kg, sd, stack, node.y, node.z, offset); - break; - case NODE_MAPPING: - svm_node_mapping(kg, sd, stack, node.y, node.z, node.w); - break; - case NODE_MIN_MAX: - offset = svm_node_min_max(kg, sd, stack, node.y, node.z, offset); - break; - case NODE_CAMERA: - svm_node_camera(kg, sd, stack, node.y, node.z, node.w); - break; - case NODE_TEX_ENVIRONMENT: - svm_node_tex_environment(kg, sd, stack, node); - break; - case NODE_TEX_SKY: - offset = svm_node_tex_sky(kg, sd, stack, node, offset); - break; - case NODE_TEX_GRADIENT: - svm_node_tex_gradient(sd, stack, node); - break; - case NODE_TEX_VORONOI: - offset = svm_node_tex_voronoi( - kg, sd, stack, node.y, node.z, node.w, offset); - break; - case NODE_TEX_MUSGRAVE: - offset = svm_node_tex_musgrave(kg, sd, stack, node.y, node.z, node.w, offset); - break; - case NODE_TEX_WAVE: - offset = svm_node_tex_wave(kg, sd, stack, node, offset); - break; - case NODE_TEX_MAGIC: - offset = svm_node_tex_magic(kg, sd, stack, node, offset); - break; - case NODE_TEX_CHECKER: - svm_node_tex_checker(kg, sd, stack, node); - break; - case NODE_TEX_BRICK: - offset = svm_node_tex_brick(kg, sd, stack, node, offset); - break; - case NODE_TEX_WHITE_NOISE: - svm_node_tex_white_noise(kg, sd, stack, node.y, node.z, node.w); - break; - case NODE_NORMAL: - offset = svm_node_normal(kg, sd, stack, node.y, node.z, node.w, offset); - break; - case NODE_LIGHT_FALLOFF: - svm_node_light_falloff(sd, stack, node); - break; - case NODE_IES: - svm_node_ies(kg, sd, stack, node); - break; - case NODE_RGB_CURVES: - case NODE_VECTOR_CURVES: - offset = svm_node_curves(kg, sd, stack, node, offset); - break; - case NODE_FLOAT_CURVE: - offset = svm_node_curve(kg, sd, stack, node, offset); - break; - case NODE_TANGENT: - svm_node_tangent(kg, sd, stack, node); - break; - case NODE_NORMAL_MAP: - svm_node_normal_map(kg, sd, stack, node); - break; - case NODE_INVERT: - svm_node_invert(sd, stack, node.y, node.z, node.w); - break; - case NODE_MIX: - offset = svm_node_mix(kg, sd, stack, node.y, node.z, node.w, offset); - break; - case NODE_SEPARATE_COLOR: - svm_node_separate_color(kg, sd, stack, node.y, node.z, node.w); - break; - case NODE_COMBINE_COLOR: - svm_node_combine_color(kg, sd, stack, node.y, node.z, node.w); - break; - case NODE_SEPARATE_VECTOR: - svm_node_separate_vector(sd, stack, node.y, node.z, node.w); - break; - case NODE_COMBINE_VECTOR: - svm_node_combine_vector(sd, stack, node.y, node.z, node.w); - break; - case NODE_SEPARATE_HSV: - offset = svm_node_separate_hsv(kg, sd, stack, node.y, node.z, node.w, offset); - break; - case NODE_COMBINE_HSV: - offset = svm_node_combine_hsv(kg, sd, stack, node.y, node.z, node.w, offset); - break; - case NODE_VECTOR_ROTATE: - svm_node_vector_rotate(sd, stack, node.y, node.z, node.w); - break; - case NODE_VECTOR_TRANSFORM: - svm_node_vector_transform(kg, sd, stack, node); - break; - case NODE_WIREFRAME: - svm_node_wireframe(kg, sd, stack, node); - break; - case NODE_WAVELENGTH: - svm_node_wavelength(kg, sd, stack, node.y, node.z); - break; - case NODE_BLACKBODY: - svm_node_blackbody(kg, sd, stack, node.y, node.z); - break; - case NODE_MAP_RANGE: - offset = svm_node_map_range(kg, sd, stack, node.y, node.z, node.w, offset); - break; - case NODE_VECTOR_MAP_RANGE: - offset = svm_node_vector_map_range(kg, sd, stack, node.y, node.z, node.w, offset); - break; - case NODE_CLAMP: - offset = svm_node_clamp(kg, sd, stack, node.y, node.z, node.w, offset); - break; + SVM_CASE(NODE_TEXTURE_MAPPING) + offset = svm_node_texture_mapping(kg, sd, stack, node.y, node.z, offset); + break; + SVM_CASE(NODE_MAPPING) + svm_node_mapping(kg, sd, stack, node.y, node.z, node.w); + break; + SVM_CASE(NODE_MIN_MAX) + offset = svm_node_min_max(kg, sd, stack, node.y, node.z, offset); + break; + SVM_CASE(NODE_CAMERA) + svm_node_camera(kg, sd, stack, node.y, node.z, node.w); + break; + SVM_CASE(NODE_TEX_ENVIRONMENT) + svm_node_tex_environment(kg, sd, stack, node); + break; + SVM_CASE(NODE_TEX_SKY) + offset = svm_node_tex_sky(kg, sd, stack, node, offset); + break; + SVM_CASE(NODE_TEX_GRADIENT) + svm_node_tex_gradient(sd, stack, node); + break; + SVM_CASE(NODE_TEX_VORONOI) + offset = svm_node_tex_voronoi( + kg, sd, stack, node.y, node.z, node.w, offset); + break; + SVM_CASE(NODE_TEX_MUSGRAVE) + offset = svm_node_tex_musgrave(kg, sd, stack, node.y, node.z, node.w, offset); + break; + SVM_CASE(NODE_TEX_WAVE) + offset = svm_node_tex_wave(kg, sd, stack, node, offset); + break; + SVM_CASE(NODE_TEX_MAGIC) + offset = svm_node_tex_magic(kg, sd, stack, node, offset); + break; + SVM_CASE(NODE_TEX_CHECKER) + svm_node_tex_checker(kg, sd, stack, node); + break; + SVM_CASE(NODE_TEX_BRICK) + offset = svm_node_tex_brick(kg, sd, stack, node, offset); + break; + SVM_CASE(NODE_TEX_WHITE_NOISE) + svm_node_tex_white_noise(kg, sd, stack, node.y, node.z, node.w); + break; + SVM_CASE(NODE_NORMAL) + offset = svm_node_normal(kg, sd, stack, node.y, node.z, node.w, offset); + break; + SVM_CASE(NODE_LIGHT_FALLOFF) + svm_node_light_falloff(sd, stack, node); + break; + SVM_CASE(NODE_IES) + svm_node_ies(kg, sd, stack, node); + break; + SVM_CASE(NODE_CURVES) + offset = svm_node_curves(kg, sd, stack, node, offset); + break; + SVM_CASE(NODE_FLOAT_CURVE) + offset = svm_node_curve(kg, sd, stack, node, offset); + break; + SVM_CASE(NODE_TANGENT) + svm_node_tangent(kg, sd, stack, node); + break; + SVM_CASE(NODE_NORMAL_MAP) + svm_node_normal_map(kg, sd, stack, node); + break; + SVM_CASE(NODE_INVERT) + svm_node_invert(sd, stack, node.y, node.z, node.w); + break; + SVM_CASE(NODE_MIX) + offset = svm_node_mix(kg, sd, stack, node.y, node.z, node.w, offset); + break; + SVM_CASE(NODE_SEPARATE_COLOR) + svm_node_separate_color(kg, sd, stack, node.y, node.z, node.w); + break; + SVM_CASE(NODE_COMBINE_COLOR) + svm_node_combine_color(kg, sd, stack, node.y, node.z, node.w); + break; + SVM_CASE(NODE_SEPARATE_VECTOR) + svm_node_separate_vector(sd, stack, node.y, node.z, node.w); + break; + SVM_CASE(NODE_COMBINE_VECTOR) + svm_node_combine_vector(sd, stack, node.y, node.z, node.w); + break; + SVM_CASE(NODE_SEPARATE_HSV) + offset = svm_node_separate_hsv(kg, sd, stack, node.y, node.z, node.w, offset); + break; + SVM_CASE(NODE_COMBINE_HSV) + offset = svm_node_combine_hsv(kg, sd, stack, node.y, node.z, node.w, offset); + break; + SVM_CASE(NODE_VECTOR_ROTATE) + svm_node_vector_rotate(sd, stack, node.y, node.z, node.w); + break; + SVM_CASE(NODE_VECTOR_TRANSFORM) + svm_node_vector_transform(kg, sd, stack, node); + break; + SVM_CASE(NODE_WIREFRAME) + svm_node_wireframe(kg, sd, stack, node); + break; + SVM_CASE(NODE_WAVELENGTH) + svm_node_wavelength(kg, sd, stack, node.y, node.z); + break; + SVM_CASE(NODE_BLACKBODY) + svm_node_blackbody(kg, sd, stack, node.y, node.z); + break; + SVM_CASE(NODE_MAP_RANGE) + offset = svm_node_map_range(kg, sd, stack, node.y, node.z, node.w, offset); + break; + SVM_CASE(NODE_VECTOR_MAP_RANGE) + offset = svm_node_vector_map_range(kg, sd, stack, node.y, node.z, node.w, offset); + break; + SVM_CASE(NODE_CLAMP) + offset = svm_node_clamp(kg, sd, stack, node.y, node.z, node.w, offset); + break; #ifdef __SHADER_RAYTRACE__ - case NODE_BEVEL: - svm_node_bevel(kg, state, sd, stack, node); - break; - case NODE_AMBIENT_OCCLUSION: - svm_node_ao(kg, state, sd, stack, node); - break; + SVM_CASE(NODE_BEVEL) + svm_node_bevel(kg, state, sd, stack, node); + break; + SVM_CASE(NODE_AMBIENT_OCCLUSION) + svm_node_ao(kg, state, sd, stack, node); + break; #endif - case NODE_TEX_VOXEL: - IF_KERNEL_NODES_FEATURE(VOLUME) - { - offset = svm_node_tex_voxel(kg, sd, stack, node, offset); - } - break; - case NODE_AOV_START: - if (!svm_node_aov_check(path_flag, render_buffer)) { - return; - } - break; - case NODE_AOV_COLOR: - svm_node_aov_color(kg, state, sd, stack, node, render_buffer); - break; - case NODE_AOV_VALUE: - svm_node_aov_value(kg, state, sd, stack, node, render_buffer); - break; + SVM_CASE(NODE_TEX_VOXEL) + IF_KERNEL_NODES_FEATURE(VOLUME) + { + offset = svm_node_tex_voxel(kg, sd, stack, node, offset); + } + break; + SVM_CASE(NODE_AOV_START) + if (!svm_node_aov_check(path_flag, render_buffer)) { + return; + } + break; + SVM_CASE(NODE_AOV_COLOR) + svm_node_aov_color(kg, state, sd, stack, node, render_buffer); + break; + SVM_CASE(NODE_AOV_VALUE) + svm_node_aov_value(kg, state, sd, stack, node, render_buffer); + break; default: kernel_assert(!"Unknown node type was passed to the SVM machine"); return; diff --git a/intern/cycles/kernel/svm/types.h b/intern/cycles/kernel/svm/types.h index 82109ec4c4f..12d0ec141e6 100644 --- a/intern/cycles/kernel/svm/types.h +++ b/intern/cycles/kernel/svm/types.h @@ -17,104 +17,9 @@ CCL_NAMESPACE_BEGIN /* Nodes */ typedef enum ShaderNodeType { - NODE_END = 0, - NODE_SHADER_JUMP, - NODE_CLOSURE_BSDF, - NODE_CLOSURE_EMISSION, - NODE_CLOSURE_BACKGROUND, - NODE_CLOSURE_SET_WEIGHT, - NODE_CLOSURE_WEIGHT, - NODE_EMISSION_WEIGHT, - NODE_MIX_CLOSURE, - NODE_JUMP_IF_ZERO, - NODE_JUMP_IF_ONE, - NODE_GEOMETRY, - NODE_CONVERT, - NODE_TEX_COORD, - NODE_VALUE_F, - NODE_VALUE_V, - NODE_ATTR, - NODE_VERTEX_COLOR, - NODE_GEOMETRY_BUMP_DX, - NODE_GEOMETRY_BUMP_DY, - NODE_SET_DISPLACEMENT, - NODE_DISPLACEMENT, - NODE_VECTOR_DISPLACEMENT, - NODE_TEX_IMAGE, - NODE_TEX_IMAGE_BOX, - NODE_TEX_NOISE, - NODE_SET_BUMP, - NODE_ATTR_BUMP_DX, - NODE_ATTR_BUMP_DY, - NODE_VERTEX_COLOR_BUMP_DX, - NODE_VERTEX_COLOR_BUMP_DY, - NODE_TEX_COORD_BUMP_DX, - NODE_TEX_COORD_BUMP_DY, - NODE_CLOSURE_SET_NORMAL, - NODE_ENTER_BUMP_EVAL, - NODE_LEAVE_BUMP_EVAL, - NODE_HSV, - NODE_CLOSURE_HOLDOUT, - NODE_FRESNEL, - NODE_LAYER_WEIGHT, - NODE_CLOSURE_VOLUME, - NODE_PRINCIPLED_VOLUME, - NODE_MATH, - NODE_VECTOR_MATH, - NODE_RGB_RAMP, - NODE_GAMMA, - NODE_BRIGHTCONTRAST, - NODE_LIGHT_PATH, - NODE_OBJECT_INFO, - NODE_PARTICLE_INFO, - NODE_HAIR_INFO, - NODE_POINT_INFO, - NODE_TEXTURE_MAPPING, - NODE_MAPPING, - NODE_MIN_MAX, - NODE_CAMERA, - NODE_TEX_ENVIRONMENT, - NODE_TEX_SKY, - NODE_TEX_GRADIENT, - NODE_TEX_VORONOI, - NODE_TEX_MUSGRAVE, - NODE_TEX_WAVE, - NODE_TEX_MAGIC, - NODE_TEX_CHECKER, - NODE_TEX_BRICK, - NODE_TEX_WHITE_NOISE, - NODE_NORMAL, - NODE_LIGHT_FALLOFF, - NODE_IES, - NODE_RGB_CURVES, - NODE_VECTOR_CURVES, - NODE_TANGENT, - NODE_NORMAL_MAP, - NODE_INVERT, - NODE_MIX, - NODE_SEPARATE_COLOR, - NODE_COMBINE_COLOR, - NODE_SEPARATE_VECTOR, - NODE_COMBINE_VECTOR, - NODE_SEPARATE_HSV, - NODE_COMBINE_HSV, - NODE_VECTOR_ROTATE, - NODE_VECTOR_TRANSFORM, - NODE_WIREFRAME, - NODE_WAVELENGTH, - NODE_BLACKBODY, - NODE_MAP_RANGE, - NODE_VECTOR_MAP_RANGE, - NODE_CLAMP, - NODE_BEVEL, - NODE_AMBIENT_OCCLUSION, - NODE_TEX_VOXEL, - NODE_AOV_START, - NODE_AOV_COLOR, - NODE_AOV_VALUE, - NODE_FLOAT_CURVE, - /* NOTE: for best OpenCL performance, item definition in the enum must - * match the switch case order in `svm.h`. */ +#define SHADER_NODE_TYPE(name) name, +#include "node_types_template.h" + NODE_NUM } ShaderNodeType; typedef enum NodeAttributeOutputType { diff --git a/intern/cycles/scene/shader_nodes.cpp b/intern/cycles/scene/shader_nodes.cpp index f93a1a5231a..bedb0fe2902 100644 --- a/intern/cycles/scene/shader_nodes.cpp +++ b/intern/cycles/scene/shader_nodes.cpp @@ -6671,7 +6671,7 @@ void CurvesNode::compile(SVMCompiler &compiler, ShaderInput *fac_in = input("Fac"); - compiler.add_node(type, + compiler.add_node(ShaderNodeType(type), compiler.encode_uchar4(compiler.stack_assign(fac_in), compiler.stack_assign(value_in), compiler.stack_assign(value_out), @@ -6736,7 +6736,7 @@ void RGBCurvesNode::constant_fold(const ConstantFolder &folder) void RGBCurvesNode::compile(SVMCompiler &compiler) { - CurvesNode::compile(compiler, NODE_RGB_CURVES, input("Color"), output("Color")); + CurvesNode::compile(compiler, NODE_CURVES, input("Color"), output("Color")); } void RGBCurvesNode::compile(OSLCompiler &compiler) @@ -6774,7 +6774,7 @@ void VectorCurvesNode::constant_fold(const ConstantFolder &folder) void VectorCurvesNode::compile(SVMCompiler &compiler) { - CurvesNode::compile(compiler, NODE_VECTOR_CURVES, input("Vector"), output("Vector")); + CurvesNode::compile(compiler, NODE_CURVES, input("Vector"), output("Vector")); } void VectorCurvesNode::compile(OSLCompiler &compiler) diff --git a/intern/cycles/scene/svm.cpp b/intern/cycles/scene/svm.cpp index 4bc5a1b9cc2..ede3f87e7e3 100644 --- a/intern/cycles/scene/svm.cpp +++ b/intern/cycles/scene/svm.cpp @@ -44,8 +44,6 @@ void SVMShaderManager::device_update_shader(Scene *scene, } assert(shader->graph); - svm_nodes->push_back_slow(make_int4(NODE_SHADER_JUMP, 0, 0, 0)); - SVMCompiler::Summary summary; SVMCompiler compiler(scene); compiler.background = (shader == scene->background->get_shader(scene)); @@ -170,6 +168,9 @@ SVMCompiler::SVMCompiler(Scene *scene) : scene(scene) background = false; mix_weight_offset = SVM_STACK_INVALID; compile_failed = false; + + /* This struct has one entry for every node, in order of ShaderNodeType definition. */ + svm_node_types_used = (std::atomic_int *)&scene->dscene.data.svm_usage; } int SVMCompiler::stack_size(SocketType::Type type) @@ -378,11 +379,13 @@ void SVMCompiler::add_node(int a, int b, int c, int d) void SVMCompiler::add_node(ShaderNodeType type, int a, int b, int c) { + svm_node_types_used[type] = true; current_svm_nodes.push_back_slow(make_int4(type, a, b, c)); } void SVMCompiler::add_node(ShaderNodeType type, const float3 &f) { + svm_node_types_used[type] = true; current_svm_nodes.push_back_slow( make_int4(type, __float_as_int(f.x), __float_as_int(f.y), __float_as_int(f.z))); } @@ -663,6 +666,7 @@ void SVMCompiler::generate_multi_closure(ShaderNode *root_node, /* Add instruction to skip closure and its dependencies if mix * weight is zero. */ + svm_node_types_used[NODE_JUMP_IF_ONE] = true; current_svm_nodes.push_back_slow(make_int4(NODE_JUMP_IF_ONE, 0, stack_assign(facin), 0)); int node_jump_skip_index = current_svm_nodes.size() - 1; @@ -678,6 +682,7 @@ void SVMCompiler::generate_multi_closure(ShaderNode *root_node, /* Add instruction to skip closure and its dependencies if mix * weight is zero. */ + svm_node_types_used[NODE_JUMP_IF_ZERO] = true; current_svm_nodes.push_back_slow(make_int4(NODE_JUMP_IF_ZERO, 0, stack_assign(facin), 0)); int node_jump_skip_index = current_svm_nodes.size() - 1; @@ -844,6 +849,9 @@ void SVMCompiler::compile_type(Shader *shader, ShaderGraph *graph, ShaderType ty void SVMCompiler::compile(Shader *shader, array &svm_nodes, int index, Summary *summary) { + svm_node_types_used[NODE_SHADER_JUMP] = true; + svm_nodes.push_back_slow(make_int4(NODE_SHADER_JUMP, 0, 0, 0)); + /* copy graph for shader with bump mapping */ ShaderNode *output = shader->graph->output(); int start_num_svm_nodes = svm_nodes.size(); diff --git a/intern/cycles/scene/svm.h b/intern/cycles/scene/svm.h index 19746616207..f72375e7f87 100644 --- a/intern/cycles/scene/svm.h +++ b/intern/cycles/scene/svm.h @@ -211,6 +211,7 @@ class SVMCompiler { /* compile */ void compile_type(Shader *shader, ShaderGraph *graph, ShaderType type); + std::atomic_int *svm_node_types_used; array current_svm_nodes; ShaderType current_type; Shader *current_shader; -- cgit v1.2.3 From da4ef05e4dfb700a61910e6d8e02183d7c272963 Mon Sep 17 00:00:00 2001 From: Michael Jones Date: Tue, 12 Jul 2022 15:32:46 +0200 Subject: Cycles: Apple Silicon optimization to specialize intersection kernels The Metal backend now compiles and caches a second set of kernels which are optimized for scene contents, enabled for Apple Silicon. The implementation supports doing this both for intersection and shading kernels. However this is currently only enabled for intersection kernels that are quick to compile, and already give a good speedup. Enabling this for shading kernels would be faster still, however this also causes a long wait times and would need a good user interface to control this. M1 Max samples per minute (macOS 13.0): PSO_GENERIC PSO_SPECIALIZED_INTERSECT PSO_SPECIALIZED_SHADE barbershop_interior 83.4 89.5 93.7 bmw27 1486.1 1671.0 1825.8 classroom 175.2 196.8 206.3 fishy_cat 674.2 704.3 719.3 junkshop 205.4 212.0 257.7 koro 310.1 336.1 342.8 monster 376.7 418.6 424.1 pabellon 273.5 325.4 339.8 sponza 830.6 929.6 1142.4 victor 86.7 96.4 96.3 wdas_cloud 111.8 112.7 183.1 Code contributed by Jason Fielder, Morteza Mostajabodaveh and Michael Jones Differential Revision: https://developer.blender.org/D14645 --- intern/cycles/device/device.h | 6 + intern/cycles/device/metal/device_impl.h | 11 +- intern/cycles/device/metal/device_impl.mm | 193 +++++++++++++----- intern/cycles/device/metal/kernel.h | 30 ++- intern/cycles/device/metal/kernel.mm | 221 ++++++++++++++++----- intern/cycles/kernel/CMakeLists.txt | 1 + .../kernel/device/metal/function_constants.h | 14 ++ intern/cycles/kernel/device/metal/kernel.metal | 1 + intern/cycles/kernel/svm/svm.h | 9 +- intern/cycles/kernel/types.h | 8 +- intern/cycles/scene/scene.cpp | 2 + intern/cycles/util/string.cpp | 18 ++ intern/cycles/util/string.h | 2 + 13 files changed, 394 insertions(+), 122 deletions(-) create mode 100644 intern/cycles/kernel/device/metal/function_constants.h diff --git a/intern/cycles/device/device.h b/intern/cycles/device/device.h index 340be85e853..e7916ec3a52 100644 --- a/intern/cycles/device/device.h +++ b/intern/cycles/device/device.h @@ -29,6 +29,7 @@ class DeviceQueue; class Progress; class CPUKernels; class CPUKernelThreadGlobals; +class Scene; /* Device Types */ @@ -186,6 +187,11 @@ class Device { return 0; } + /* Called after kernel texture setup, and prior to integrator state setup. */ + virtual void optimize_for_scene(Scene *scene) + { + } + virtual bool is_resident(device_ptr /*key*/, Device *sub_device) { /* Memory is always resident if this is not a multi device, regardless of whether the pointer diff --git a/intern/cycles/device/metal/device_impl.h b/intern/cycles/device/metal/device_impl.h index 4aea8d697a5..99e60d3a788 100644 --- a/intern/cycles/device/metal/device_impl.h +++ b/intern/cycles/device/metal/device_impl.h @@ -75,7 +75,8 @@ class MetalDevice : public Device { std::vector> texture_slot_map; bool use_metalrt = false; - bool use_function_specialisation = false; + MetalPipelineType kernel_specialization_level = PSO_GENERIC; + std::atomic_bool async_compile_and_load = false; virtual BVHLayoutMask get_bvh_layout_mask() const override; @@ -91,9 +92,7 @@ class MetalDevice : public Device { bool use_adaptive_compilation(); - string get_source(const uint kernel_features); - - string compile_kernel(const uint kernel_features, const char *name); + void make_source(MetalPipelineType pso_type, const uint kernel_features); virtual bool load_kernels(const uint kernel_features) override; @@ -111,7 +110,9 @@ class MetalDevice : public Device { virtual void build_bvh(BVH *bvh, Progress &progress, bool refit) override; - id compile(string const &source); + virtual void optimize_for_scene(Scene *scene) override; + + bool compile_and_load(MetalPipelineType pso_type); /* ------------------------------------------------------------------ */ /* low-level memory management */ diff --git a/intern/cycles/device/metal/device_impl.mm b/intern/cycles/device/metal/device_impl.mm index ba9317e3204..d8bb3b867cd 100644 --- a/intern/cycles/device/metal/device_impl.mm +++ b/intern/cycles/device/metal/device_impl.mm @@ -6,6 +6,8 @@ # include "device/metal/device_impl.h" # include "device/metal/device.h" +# include "scene/scene.h" + # include "util/debug.h" # include "util/md5.h" # include "util/path.h" @@ -78,6 +80,10 @@ MetalDevice::MetalDevice(const DeviceInfo &info, Stats &stats, Profiler &profile case METAL_GPU_APPLE: { max_threads_per_threadgroup = 512; use_metalrt = info.use_metalrt; + + /* Specialize the intersection kernels on Apple GPUs by default as these can be built very + * quickly. */ + kernel_specialization_level = PSO_SPECIALIZED_INTERSECT; break; } } @@ -90,6 +96,13 @@ MetalDevice::MetalDevice(const DeviceInfo &info, Stats &stats, Profiler &profile capture_enabled = true; } + if (auto envstr = getenv("CYCLES_METAL_SPECIALIZATION_LEVEL")) { + kernel_specialization_level = (MetalPipelineType)atoi(envstr); + } + metal_printf("kernel_specialization_level = %s\n", + kernel_type_as_string( + (MetalPipelineType)min((int)kernel_specialization_level, (int)PSO_NUM - 1))); + MTLArgumentDescriptor *arg_desc_params = [[MTLArgumentDescriptor alloc] init]; arg_desc_params.dataType = MTLDataTypePointer; arg_desc_params.access = MTLArgumentAccessReadOnly; @@ -209,12 +222,11 @@ bool MetalDevice::use_adaptive_compilation() return DebugFlags().metal.adaptive_compile; } -string MetalDevice::get_source(const uint kernel_features) +void MetalDevice::make_source(MetalPipelineType pso_type, const uint kernel_features) { - string build_options; - + string global_defines; if (use_adaptive_compilation()) { - build_options += " -D__KERNEL_FEATURES__=" + to_string(kernel_features); + global_defines += "#define __KERNEL_FEATURES__ " + to_string(kernel_features) + "\n"; } if (MetalInfo::optimal_sort_partition_elements(mtlDevice) > 0) { @@ -222,52 +234,78 @@ string MetalDevice::get_source(const uint kernel_features) } if (use_metalrt) { - build_options += "-D__METALRT__ "; + global_defines += "#define __METALRT__\n"; if (motion_blur) { - build_options += "-D__METALRT_MOTION__ "; + global_defines += "#define __METALRT_MOTION__\n"; } } # ifdef WITH_CYCLES_DEBUG - build_options += "-D__KERNEL_DEBUG__ "; + global_defines += "#define __KERNEL_DEBUG__\n"; # endif switch (device_vendor) { default: break; case METAL_GPU_INTEL: - build_options += "-D__KERNEL_METAL_INTEL__ "; + global_defines += "#define __KERNEL_METAL_INTEL__\n"; break; case METAL_GPU_AMD: - build_options += "-D__KERNEL_METAL_AMD__ "; + global_defines += "#define __KERNEL_METAL_AMD__\n"; break; case METAL_GPU_APPLE: - build_options += "-D__KERNEL_METAL_APPLE__ "; + global_defines += "#define __KERNEL_METAL_APPLE__\n"; break; } - /* reformat -D defines list into compilable form */ - vector components; - string_replace(build_options, "-D", ""); - string_split(components, build_options, " "); + string &source = this->source[pso_type]; + source = "\n#include \"kernel/device/metal/kernel.metal\"\n"; + source = path_source_replace_includes(source, path_get("source")); - string globalDefines; - for (const string &component : components) { - vector assignments; - string_split(assignments, component, "="); - if (assignments.size() == 2) - globalDefines += string_printf( - "#define %s %s\n", assignments[0].c_str(), assignments[1].c_str()); - else - globalDefines += string_printf("#define %s\n", assignments[0].c_str()); + /* Perform any required specialization on the source. + * With Metal function constants we can generate a single variant of the kernel source which can + * be repeatedly respecialized. + */ + string baked_constants; + + /* Replace specific KernelData "dot" dereferences with a Metal function_constant identifier of + * the same character length. Build a string of all active constant values which is then hashed + * in order to identify the PSO. + */ + if (pso_type != PSO_GENERIC) { + const double starttime = time_dt(); + +# define KERNEL_STRUCT_BEGIN(name, parent) \ + string_replace_same_length(source, "kernel_data." #parent ".", "kernel_data_" #parent "_"); + + /* Add constants to md5 so that 'get_best_pipeline' is able to return a suitable match. */ +# define KERNEL_STRUCT_MEMBER(parent, _type, name) \ + baked_constants += string(#parent "." #name "=") + \ + to_string(_type(launch_params.data.parent.name)) + "\n"; + +# include "kernel/data_template.h" + + /* Opt in to all of available specializations. This can be made more granular for the + * PSO_SPECIALIZED_INTERSECT case in order to minimize the number of specialization requests, + * but the overhead should be negligible as these are very quick to (re)build and aren't + * serialized to disk via MTLBinaryArchives. + */ + global_defines += "#define __KERNEL_USE_DATA_CONSTANTS__\n"; + + metal_printf("KernelData patching took %.1f ms\n", (time_dt() - starttime) * 1000.0); } - string source = globalDefines + "\n#include \"kernel/device/metal/kernel.metal\"\n"; - source = path_source_replace_includes(source, path_get("source")); - - metal_printf("Global defines:\n%s\n", globalDefines.c_str()); + source = global_defines + source; + metal_printf("================\n%s================\n\%s================\n", + global_defines.c_str(), + baked_constants.c_str()); - return source; + /* Generate an MD5 from the source and include any baked constants. This is used when caching + * PSOs. */ + MD5Hash md5; + md5.append(baked_constants); + md5.append(source); + source_md5[pso_type] = md5.get_hex(); } bool MetalDevice::load_kernels(const uint _kernel_features) @@ -283,28 +321,22 @@ bool MetalDevice::load_kernels(const uint _kernel_features) * active, but may still need to be rendered without motion blur if that isn't active as well. */ motion_blur = kernel_features & KERNEL_FEATURE_OBJECT_MOTION; - source[PSO_GENERIC] = get_source(kernel_features); - - const double starttime = time_dt(); - - mtlLibrary[PSO_GENERIC] = compile(source[PSO_GENERIC]); - - metal_printf("Front-end compilation finished in %.1f seconds (generic)\n", - time_dt() - starttime); - - MD5Hash md5; - md5.append(source[PSO_GENERIC]); - source_md5[PSO_GENERIC] = md5.get_hex(); - - bool result = MetalDeviceKernels::load(this, false); + bool result = compile_and_load(PSO_GENERIC); reserve_local_memory(kernel_features); - return result; } -id MetalDevice::compile(string const &source) +bool MetalDevice::compile_and_load(MetalPipelineType pso_type) { + make_source(pso_type, kernel_features); + + if (!MetalDeviceKernels::should_load_kernels(this, pso_type)) { + /* We already have a full set of matching pipelines which are cached or queued. */ + metal_printf("%s kernels already requested\n", kernel_type_as_string(pso_type)); + return true; + } + MTLCompileOptions *options = [[MTLCompileOptions alloc] init]; options.fastMathEnabled = YES; @@ -312,19 +344,30 @@ id MetalDevice::compile(string const &source) options.languageVersion = MTLLanguageVersion2_4; } + if (getenv("CYCLES_METAL_PROFILING") || getenv("CYCLES_METAL_DEBUG")) { + path_write_text(path_cache_get(string_printf("%s.metal", kernel_type_as_string(pso_type))), + source[pso_type]); + } + + const double starttime = time_dt(); + NSError *error = NULL; - id mtlLibrary = [mtlDevice newLibraryWithSource:@(source.c_str()) - options:options - error:&error]; + mtlLibrary[pso_type] = [mtlDevice newLibraryWithSource:@(source[pso_type].c_str()) + options:options + error:&error]; - if (!mtlLibrary) { + if (!mtlLibrary[pso_type]) { NSString *err = [error localizedDescription]; set_error(string_printf("Failed to compile library:\n%s", [err UTF8String])); } + metal_printf("Front-end compilation finished in %.1f seconds (%s)\n", + time_dt() - starttime, + kernel_type_as_string(pso_type)); + [options release]; - return mtlLibrary; + return MetalDeviceKernels::load(this, pso_type); } void MetalDevice::reserve_local_memory(const uint kernel_features) @@ -631,6 +674,58 @@ device_ptr MetalDevice::mem_alloc_sub_ptr(device_memory &mem, size_t offset, siz return 0; } +void MetalDevice::optimize_for_scene(Scene *scene) +{ + MetalPipelineType specialization_level = kernel_specialization_level; + + if (specialization_level < PSO_SPECIALIZED_INTERSECT) { + return; + } + + /* PSO_SPECIALIZED_INTERSECT kernels are fast to specialize, so we always load them + * synchronously. */ + compile_and_load(PSO_SPECIALIZED_INTERSECT); + + if (specialization_level < PSO_SPECIALIZED_SHADE) { + return; + } + if (!scene->params.background) { + /* Don't load PSO_SPECIALIZED_SHADE kernels during viewport rendering as they are slower to + * build. */ + return; + } + + /* PSO_SPECIALIZED_SHADE kernels are slower to specialize, so we load them asynchronously, and + * only if there isn't an existing load in flight. + */ + auto specialize_shade_fn = ^() { + compile_and_load(PSO_SPECIALIZED_SHADE); + async_compile_and_load = false; + }; + + bool async_specialize_shade = true; + + /* Block if a per-kernel profiling is enabled (ensure steady rendering rate). */ + if (getenv("CYCLES_METAL_PROFILING") != nullptr) { + async_specialize_shade = false; + } + + if (async_specialize_shade) { + if (!async_compile_and_load) { + async_compile_and_load = true; + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), + specialize_shade_fn); + } + else { + metal_printf( + "Async PSO_SPECIALIZED_SHADE load request already in progress - dropping request\n"); + } + } + else { + specialize_shade_fn(); + } +} + void MetalDevice::const_copy_to(const char *name, void *host, size_t size) { if (strcmp(name, "data") == 0) { diff --git a/intern/cycles/device/metal/kernel.h b/intern/cycles/device/metal/kernel.h index 69b2a686ecc..11393f8b7e1 100644 --- a/intern/cycles/device/metal/kernel.h +++ b/intern/cycles/device/metal/kernel.h @@ -31,7 +31,7 @@ enum { enum { METALRT_TABLE_DEFAULT, METALRT_TABLE_SHADOW, METALRT_TABLE_LOCAL, METALRT_TABLE_NUM }; /* Pipeline State Object types */ -enum { +enum MetalPipelineType { /* A kernel that can be used with all scenes, supporting all features. * It is slow to compile, but only needs to be compiled once and is then * cached for future render sessions. This allows a render to get underway @@ -39,28 +39,33 @@ enum { */ PSO_GENERIC, - /* A kernel that is relatively quick to compile, but is specialized for the - * scene being rendered. It only contains the functionality and even baked in - * constants for values that means it needs to be recompiled whenever a - * dependent setting is changed. The render performance of this kernel is - * significantly faster though, and justifies the extra compile time. + /* A intersection kernel that is very quick to specialize and results in faster intersection + * kernel performance. It uses Metal function constants to replace several KernelData variables + * with fixed constants. + */ + PSO_SPECIALIZED_INTERSECT, + + /* A shading kernel that is slow to specialize, but results in faster shading kernel performance + * rendered. It uses Metal function constants to replace several KernelData variables with fixed + * constants and short-circuit all unused SVM node case handlers. */ - /* METAL_WIP: This isn't used and will require more changes to enable. */ - PSO_SPECIALISED, + PSO_SPECIALIZED_SHADE, PSO_NUM }; -const char *kernel_type_as_string(int kernel_type); +const char *kernel_type_as_string(MetalPipelineType pso_type); struct MetalKernelPipeline { void compile(); id mtlLibrary = nil; - bool scene_specialized; + MetalPipelineType pso_type; string source_md5; + size_t usage_count = 0; + KernelData kernel_data_; bool use_metalrt; bool metalrt_hair; bool metalrt_hair_thick; @@ -75,6 +80,8 @@ struct MetalKernelPipeline { id pipeline = nil; int num_threads_per_block = 0; + bool should_use_binary_archive() const; + string error_str; API_AVAILABLE(macos(11.0)) @@ -85,7 +92,8 @@ struct MetalKernelPipeline { /* Cache of Metal kernels for each DeviceKernel. */ namespace MetalDeviceKernels { -bool load(MetalDevice *device, bool scene_specialized); +bool should_load_kernels(MetalDevice *device, MetalPipelineType pso_type); +bool load(MetalDevice *device, MetalPipelineType pso_type); const MetalKernelPipeline *get_best_pipeline(const MetalDevice *device, DeviceKernel kernel); } /* namespace MetalDeviceKernels */ diff --git a/intern/cycles/device/metal/kernel.mm b/intern/cycles/device/metal/kernel.mm index fec4cd80466..385cb412b06 100644 --- a/intern/cycles/device/metal/kernel.mm +++ b/intern/cycles/device/metal/kernel.mm @@ -5,6 +5,7 @@ # include "device/metal/kernel.h" # include "device/metal/device_impl.h" +# include "kernel/device/metal/function_constants.h" # include "util/md5.h" # include "util/path.h" # include "util/tbb.h" @@ -16,13 +17,15 @@ CCL_NAMESPACE_BEGIN /* limit to 2 MTLCompiler instances */ int max_mtlcompiler_threads = 2; -const char *kernel_type_as_string(int kernel_type) +const char *kernel_type_as_string(MetalPipelineType pso_type) { - switch (kernel_type) { + switch (pso_type) { case PSO_GENERIC: return "PSO_GENERIC"; - case PSO_SPECIALISED: - return "PSO_SPECIALISED"; + case PSO_SPECIALIZED_INTERSECT: + return "PSO_SPECIALIZED_INTERSECT"; + case PSO_SPECIALIZED_SHADE: + return "PSO_SPECIALIZED_SHADE"; default: assert(0); } @@ -50,7 +53,11 @@ struct ShaderCache { /* Non-blocking request for a kernel, optionally specialized to the scene being rendered by * device. */ - void load_kernel(DeviceKernel kernel, MetalDevice *device, bool scene_specialized); + void load_kernel(DeviceKernel kernel, MetalDevice *device, MetalPipelineType pso_type); + + bool should_load_kernel(DeviceKernel device_kernel, + MetalDevice *device, + MetalPipelineType pso_type); void wait_for_all(); @@ -139,31 +146,34 @@ void ShaderCache::compile_thread_func(int thread_index) } } -void ShaderCache::load_kernel(DeviceKernel device_kernel, - MetalDevice *device, - bool scene_specialized) +bool ShaderCache::should_load_kernel(DeviceKernel device_kernel, + MetalDevice *device, + MetalPipelineType pso_type) { - { - /* create compiler threads on first run */ - thread_scoped_lock lock(cache_mutex); - if (compile_threads.empty()) { - running = true; - for (int i = 0; i < max_mtlcompiler_threads; i++) { - compile_threads.push_back(std::thread([&] { compile_thread_func(i); })); - } - } + if (device_kernel == DEVICE_KERNEL_INTEGRATOR_MEGAKERNEL) { + /* Skip megakernel. */ + return false; } - if (device_kernel == DEVICE_KERNEL_INTEGRATOR_MEGAKERNEL) { - /* skip megakernel */ - return; + if (device_kernel == DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE_RAYTRACE) { + if ((device->kernel_features & KERNEL_FEATURE_NODE_RAYTRACE) == 0) { + /* Skip shade_surface_raytrace kernel if the scene doesn't require it. */ + return false; + } } - if (scene_specialized) { + if (pso_type != PSO_GENERIC) { /* Only specialize kernels where it can make an impact. */ if (device_kernel < DEVICE_KERNEL_INTEGRATOR_INTERSECT_CLOSEST || device_kernel > DEVICE_KERNEL_INTEGRATOR_MEGAKERNEL) { - return; + return false; + } + + /* Only specialize shading / intersection kernels as requested. */ + bool is_shade_kernel = (device_kernel >= DEVICE_KERNEL_INTEGRATOR_SHADE_BACKGROUND); + bool is_shade_pso = (pso_type == PSO_SPECIALIZED_SHADE); + if (is_shade_pso != is_shade_kernel) { + return false; } } @@ -171,35 +181,45 @@ void ShaderCache::load_kernel(DeviceKernel device_kernel, /* check whether the kernel has already been requested / cached */ thread_scoped_lock lock(cache_mutex); for (auto &pipeline : pipelines[device_kernel]) { - if (scene_specialized) { - if (pipeline->source_md5 == device->source_md5[PSO_SPECIALISED]) { - /* we already requested a pipeline that is specialized for this kernel data */ - metal_printf("Specialized kernel already requested (%s)\n", - device_kernel_as_string(device_kernel)); - return; - } + if (pipeline->source_md5 == device->source_md5[pso_type]) { + return false; } - else { - if (pipeline->source_md5 == device->source_md5[PSO_GENERIC]) { - /* we already requested a generic pipeline for this kernel */ - metal_printf("Generic kernel already requested (%s)\n", - device_kernel_as_string(device_kernel)); - return; - } + } + } + + return true; +} + +void ShaderCache::load_kernel(DeviceKernel device_kernel, + MetalDevice *device, + MetalPipelineType pso_type) +{ + { + /* create compiler threads on first run */ + thread_scoped_lock lock(cache_mutex); + if (compile_threads.empty()) { + running = true; + for (int i = 0; i < max_mtlcompiler_threads; i++) { + compile_threads.push_back(std::thread([&] { compile_thread_func(i); })); } } } + if (!should_load_kernel(device_kernel, device, pso_type)) { + return; + } + incomplete_requests++; PipelineRequest request; request.pipeline = new MetalKernelPipeline; - request.pipeline->scene_specialized = scene_specialized; + memcpy(&request.pipeline->kernel_data_, + &device->launch_params.data, + sizeof(request.pipeline->kernel_data_)); + request.pipeline->pso_type = pso_type; request.pipeline->mtlDevice = mtlDevice; - request.pipeline->source_md5 = - device->source_md5[scene_specialized ? PSO_SPECIALISED : PSO_GENERIC]; - request.pipeline->mtlLibrary = - device->mtlLibrary[scene_specialized ? PSO_SPECIALISED : PSO_GENERIC]; + request.pipeline->source_md5 = device->source_md5[pso_type]; + request.pipeline->mtlLibrary = device->mtlLibrary[pso_type]; request.pipeline->device_kernel = device_kernel; request.pipeline->threads_per_threadgroup = device->max_threads_per_threadgroup; @@ -214,7 +234,24 @@ void ShaderCache::load_kernel(DeviceKernel device_kernel, { thread_scoped_lock lock(cache_mutex); - pipelines[device_kernel].push_back(unique_ptr(request.pipeline)); + auto &collection = pipelines[device_kernel]; + + /* Cache up to 3 kernel variants with the same pso_type, purging oldest first. */ + int max_entries_of_same_pso_type = 3; + for (int i = (int)collection.size() - 1; i >= 0; i--) { + if (collection[i]->pso_type == pso_type) { + max_entries_of_same_pso_type -= 1; + if (max_entries_of_same_pso_type == 0) { + metal_printf("Purging oldest %s:%s kernel from ShaderCache\n", + kernel_type_as_string(pso_type), + device_kernel_as_string(device_kernel)); + collection.erase(collection.begin() + i); + break; + } + } + } + + collection.push_back(unique_ptr(request.pipeline)); request_queue.push_back(request); } cond_var.notify_one(); @@ -248,8 +285,9 @@ MetalKernelPipeline *ShaderCache::get_best_pipeline(DeviceKernel kernel, const M continue; } - if (pipeline->scene_specialized) { - if (pipeline->source_md5 == device->source_md5[PSO_SPECIALISED]) { + if (pipeline->pso_type != PSO_GENERIC) { + if (pipeline->source_md5 == device->source_md5[PSO_SPECIALIZED_INTERSECT] || + pipeline->source_md5 == device->source_md5[PSO_SPECIALIZED_SHADE]) { best_pipeline = pipeline.get(); } } @@ -258,13 +296,65 @@ MetalKernelPipeline *ShaderCache::get_best_pipeline(DeviceKernel kernel, const M } } + if (best_pipeline->usage_count == 0 && best_pipeline->pso_type != PSO_GENERIC) { + metal_printf("Swapping in %s version of %s\n", + kernel_type_as_string(best_pipeline->pso_type), + device_kernel_as_string(kernel)); + } + best_pipeline->usage_count += 1; + return best_pipeline; } -void MetalKernelPipeline::compile() +bool MetalKernelPipeline::should_use_binary_archive() const { - int pso_type = scene_specialized ? PSO_SPECIALISED : PSO_GENERIC; + if (auto str = getenv("CYCLES_METAL_DISABLE_BINARY_ARCHIVES")) { + if (atoi(str) != 0) { + /* Don't archive if we have opted out by env var. */ + return false; + } + } + + if (pso_type == PSO_GENERIC) { + /* Archive the generic kernels. */ + return true; + } + + if (device_kernel >= DEVICE_KERNEL_INTEGRATOR_SHADE_BACKGROUND && + device_kernel <= DEVICE_KERNEL_INTEGRATOR_SHADE_SHADOW) { + /* Archive all shade kernels - they take a long time to compile. */ + return true; + } + + /* The remaining kernels are all fast to compile. They may get cached by the system shader cache, + * but will be quick to regenerate if not. */ + return false; +} + +static MTLFunctionConstantValues *GetConstantValues(KernelData const *data = nullptr) +{ + MTLFunctionConstantValues *constant_values = [MTLFunctionConstantValues new]; + + MTLDataType MTLDataType_int = MTLDataTypeInt; + MTLDataType MTLDataType_float = MTLDataTypeFloat; + MTLDataType MTLDataType_float4 = MTLDataTypeFloat4; + KernelData zero_data = {0}; + if (!data) { + data = &zero_data; + } +# define KERNEL_STRUCT_MEMBER(parent, _type, name) \ + [constant_values setConstantValue:&data->parent.name \ + type:MTLDataType_##_type \ + atIndex:KernelData_##parent##_##name]; + +# include "kernel/data_template.h" + + return constant_values; +} + +void MetalKernelPipeline::compile() +{ const std::string function_name = std::string("cycles_metal_") + device_kernel_as_string(device_kernel); @@ -281,6 +371,17 @@ void MetalKernelPipeline::compile() if (@available(macOS 11.0, *)) { MTLFunctionDescriptor *func_desc = [MTLIntersectionFunctionDescriptor functionDescriptor]; func_desc.name = entryPoint; + + if (pso_type == PSO_SPECIALIZED_SHADE) { + func_desc.constantValues = GetConstantValues(&kernel_data_); + } + else if (pso_type == PSO_SPECIALIZED_INTERSECT) { + func_desc.constantValues = GetConstantValues(&kernel_data_); + } + else { + func_desc.constantValues = GetConstantValues(); + } + function = [mtlLibrary newFunctionWithDescriptor:func_desc error:&error]; } @@ -427,10 +528,7 @@ void MetalKernelPipeline::compile() MTLPipelineOption pipelineOptions = MTLPipelineOptionNone; - bool use_binary_archive = true; - if (auto str = getenv("CYCLES_METAL_DISABLE_BINARY_ARCHIVES")) { - use_binary_archive = (atoi(str) == 0); - } + bool use_binary_archive = should_use_binary_archive(); id archive = nil; string metalbin_path; @@ -608,19 +706,32 @@ void MetalKernelPipeline::compile() } } -bool MetalDeviceKernels::load(MetalDevice *device, bool scene_specialized) +bool MetalDeviceKernels::load(MetalDevice *device, MetalPipelineType pso_type) { + const double starttime = time_dt(); auto shader_cache = get_shader_cache(device->mtlDevice); for (int i = 0; i < DEVICE_KERNEL_NUM; i++) { - shader_cache->load_kernel((DeviceKernel)i, device, scene_specialized); + shader_cache->load_kernel((DeviceKernel)i, device, pso_type); } - if (!scene_specialized || getenv("CYCLES_METAL_PROFILING")) { - shader_cache->wait_for_all(); - } + shader_cache->wait_for_all(); + metal_printf("Back-end compilation finished in %.1f seconds (%s)\n", + time_dt() - starttime, + kernel_type_as_string(pso_type)); return true; } +bool MetalDeviceKernels::should_load_kernels(MetalDevice *device, MetalPipelineType pso_type) +{ + auto shader_cache = get_shader_cache(device->mtlDevice); + for (int i = 0; i < DEVICE_KERNEL_NUM; i++) { + if (shader_cache->should_load_kernel((DeviceKernel)i, device, pso_type)) { + return true; + } + } + return false; +} + const MetalKernelPipeline *MetalDeviceKernels::get_best_pipeline(const MetalDevice *device, DeviceKernel kernel) { diff --git a/intern/cycles/kernel/CMakeLists.txt b/intern/cycles/kernel/CMakeLists.txt index 527cc4ec111..21a78722c0d 100644 --- a/intern/cycles/kernel/CMakeLists.txt +++ b/intern/cycles/kernel/CMakeLists.txt @@ -79,6 +79,7 @@ set(SRC_KERNEL_DEVICE_METAL_HEADERS device/metal/compat.h device/metal/context_begin.h device/metal/context_end.h + device/metal/function_constants.h device/metal/globals.h ) diff --git a/intern/cycles/kernel/device/metal/function_constants.h b/intern/cycles/kernel/device/metal/function_constants.h new file mode 100644 index 00000000000..f4001735672 --- /dev/null +++ b/intern/cycles/kernel/device/metal/function_constants.h @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright 2021-2022 Blender Foundation */ + +enum { +#define KERNEL_STRUCT_MEMBER(parent, type, name) KernelData_##parent##_##name, +#include "kernel/data_template.h" +}; + +#ifdef __KERNEL_METAL__ +# define KERNEL_STRUCT_MEMBER(parent, type, name) \ + constant type kernel_data_##parent##_##name \ + [[function_constant(KernelData_##parent##_##name)]]; +# include "kernel/data_template.h" +#endif diff --git a/intern/cycles/kernel/device/metal/kernel.metal b/intern/cycles/kernel/device/metal/kernel.metal index 3c31dc3354c..74b4b079a32 100644 --- a/intern/cycles/kernel/device/metal/kernel.metal +++ b/intern/cycles/kernel/device/metal/kernel.metal @@ -5,6 +5,7 @@ #include "kernel/device/metal/compat.h" #include "kernel/device/metal/globals.h" +#include "kernel/device/metal/function_constants.h" #include "kernel/device/gpu/kernel.h" /* MetalRT intersection handlers */ diff --git a/intern/cycles/kernel/svm/svm.h b/intern/cycles/kernel/svm/svm.h index 9840cda3655..9d6d3e9222c 100644 --- a/intern/cycles/kernel/svm/svm.h +++ b/intern/cycles/kernel/svm/svm.h @@ -204,7 +204,14 @@ CCL_NAMESPACE_END CCL_NAMESPACE_BEGIN -#define SVM_CASE(node) case node: +#ifdef __KERNEL_USE_DATA_CONSTANTS__ +# define SVM_CASE(node) \ + case node: \ + if (!kernel_data_svm_usage_##node) \ + break; +#else +# define SVM_CASE(node) case node: +#endif /* Main Interpreter Loop */ template diff --git a/intern/cycles/kernel/types.h b/intern/cycles/kernel/types.h index 72cee6ae344..62ac75e5e4d 100644 --- a/intern/cycles/kernel/types.h +++ b/intern/cycles/kernel/types.h @@ -1136,7 +1136,13 @@ typedef enum KernelBVHLayout { } \ ; \ static_assert_align(name, 16); -#define KERNEL_STRUCT_MEMBER(parent, type, name) type name; + +#ifdef __KERNEL_USE_DATA_CONSTANTS__ +# define KERNEL_STRUCT_MEMBER(parent, type, name) type __unused_##name; +#else +# define KERNEL_STRUCT_MEMBER(parent, type, name) type name; +#endif + #include "kernel/data_template.h" typedef struct KernelTables { diff --git a/intern/cycles/scene/scene.cpp b/intern/cycles/scene/scene.cpp index eedb2a4fa3a..18cd665ac74 100644 --- a/intern/cycles/scene/scene.cpp +++ b/intern/cycles/scene/scene.cpp @@ -369,6 +369,8 @@ void Scene::device_update(Device *device_, Progress &progress) device->const_copy_to("data", &dscene.data, sizeof(dscene.data)); } + device->optimize_for_scene(this); + if (print_stats) { size_t mem_used = util_guarded_get_mem_used(); size_t mem_peak = util_guarded_get_mem_peak(); diff --git a/intern/cycles/util/string.cpp b/intern/cycles/util/string.cpp index 66ff866ee10..0c318cea44a 100644 --- a/intern/cycles/util/string.cpp +++ b/intern/cycles/util/string.cpp @@ -136,6 +136,19 @@ void string_replace(string &haystack, const string &needle, const string &other) } } +void string_replace_same_length(string &haystack, const string &needle, const string &other) +{ + assert(needle.size() == other.size()); + size_t pos = 0; + while (pos != string::npos) { + pos = haystack.find(needle, pos); + if (pos != string::npos) { + memcpy(haystack.data() + pos, other.data(), other.size()); + pos += other.size(); + } + } +} + string string_remove_trademark(const string &s) { string result = s; @@ -164,6 +177,11 @@ string to_string(const char *str) return string(str); } +string to_string(const float4 &v) +{ + return string_printf("%f,%f,%f,%f", v.x, v.y, v.z, v.w); +} + string string_to_lower(const string &s) { string r = s; diff --git a/intern/cycles/util/string.h b/intern/cycles/util/string.h index a74feee1750..ecbe9e106c6 100644 --- a/intern/cycles/util/string.h +++ b/intern/cycles/util/string.h @@ -38,12 +38,14 @@ void string_split(vector &tokens, const string &separators = "\t ", bool skip_empty_tokens = true); void string_replace(string &haystack, const string &needle, const string &other); +void string_replace_same_length(string &haystack, const string &needle, const string &other); bool string_startswith(string_view s, string_view start); bool string_endswith(string_view s, string_view end); string string_strip(const string &s); string string_remove_trademark(const string &s); string string_from_bool(const bool var); string to_string(const char *str); +string to_string(const float4 &v); string string_to_lower(const string &s); /* Wide char strings are only used on Windows to deal with non-ASCII -- cgit v1.2.3 From 523bbf7065547a67e7c23f67f546a5ed6433f809 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Thu, 14 Jul 2022 16:42:43 +0200 Subject: Cycles: generalize shader sorting / locality heuristic to all GPU devices This was added for Metal, but also gives good results with CUDA and OptiX. Also enable it for future Apple GPUs instead of only M1 and M2, since this has been shown to help across multiple GPUs so the better bet seems to enable rather than disable it. Also moves some of the logic outside of the Metal device code, and always enables the code in the kernel since other devices don't do dynamic compile. Time per sample with OptiX + RTX A6000: new old barbershop_interior 0.0730s 0.0727s bmw27 0.0047s 0.0053s classroom 0.0428s 0.0464s fishy_cat 0.0102s 0.0108s junkshop 0.0366s 0.0395s koro 0.0567s 0.0578s monster 0.0206s 0.0223s pabellon 0.0158s 0.0174s sponza 0.0088s 0.0100s spring 0.1267s 0.1280s victor 0.0524s 0.0531s wdas_cloud 0.0817s 0.0816s Ref D15331, T87836 --- intern/cycles/device/metal/device_impl.mm | 4 -- intern/cycles/device/metal/queue.h | 2 +- intern/cycles/device/metal/queue.mm | 16 +------ intern/cycles/device/metal/util.mm | 5 +-- intern/cycles/device/queue.h | 9 ++-- intern/cycles/integrator/path_trace_work_gpu.cpp | 53 ++++++++++++++++-------- intern/cycles/kernel/integrator/state_flow.h | 8 +--- 7 files changed, 45 insertions(+), 52 deletions(-) diff --git a/intern/cycles/device/metal/device_impl.mm b/intern/cycles/device/metal/device_impl.mm index d8bb3b867cd..d1250b83d22 100644 --- a/intern/cycles/device/metal/device_impl.mm +++ b/intern/cycles/device/metal/device_impl.mm @@ -229,10 +229,6 @@ void MetalDevice::make_source(MetalPipelineType pso_type, const uint kernel_feat global_defines += "#define __KERNEL_FEATURES__ " + to_string(kernel_features) + "\n"; } - if (MetalInfo::optimal_sort_partition_elements(mtlDevice) > 0) { - build_options += " -D__KERNEL_SORT_PARTITIONING__ "; - } - if (use_metalrt) { global_defines += "#define __METALRT__\n"; if (motion_blur) { diff --git a/intern/cycles/device/metal/queue.h b/intern/cycles/device/metal/queue.h index 836289172f7..fc32740f3e1 100644 --- a/intern/cycles/device/metal/queue.h +++ b/intern/cycles/device/metal/queue.h @@ -24,7 +24,7 @@ class MetalDeviceQueue : public DeviceQueue { virtual int num_concurrent_states(const size_t) const override; virtual int num_concurrent_busy_states() const override; - virtual int num_sort_partitions(const size_t) const override; + virtual int num_sort_partition_elements() const override; virtual void init_execution() override; diff --git a/intern/cycles/device/metal/queue.mm b/intern/cycles/device/metal/queue.mm index 6a9cc552098..5ac63a16c61 100644 --- a/intern/cycles/device/metal/queue.mm +++ b/intern/cycles/device/metal/queue.mm @@ -293,21 +293,9 @@ int MetalDeviceQueue::num_concurrent_busy_states() const return result; } -int MetalDeviceQueue::num_sort_partitions(const size_t state_size) const +int MetalDeviceQueue::num_sort_partition_elements() const { - /* Sort partitioning becomes less effective when more shaders are in the wavefront. In lieu of a - * more sophisticated heuristic we simply disable sort partitioning if the shader count is high. - */ - if (metal_device_->launch_params.data.max_shaders >= 300) { - return 1; - } - - const int optimal_partition_elements = MetalInfo::optimal_sort_partition_elements( - metal_device_->mtlDevice); - if (optimal_partition_elements) { - return num_concurrent_states(state_size) / optimal_partition_elements; - } - return 1; + return MetalInfo::optimal_sort_partition_elements(metal_device_->mtlDevice); } void MetalDeviceQueue::init_execution() diff --git a/intern/cycles/device/metal/util.mm b/intern/cycles/device/metal/util.mm index c336dc310c8..65c67c400fe 100644 --- a/intern/cycles/device/metal/util.mm +++ b/intern/cycles/device/metal/util.mm @@ -82,10 +82,7 @@ int MetalInfo::optimal_sort_partition_elements(id device) * sorting each partition by material. Partitioning into chunks of 65536 elements results in an * overall render time speedup of up to 15%. */ if (get_device_vendor(device) == METAL_GPU_APPLE) { - AppleGPUArchitecture arch = get_apple_gpu_architecture(device); - if (arch == APPLE_M1 || arch == APPLE_M2) { - return 65536; - } + return 65536; } return 0; } diff --git a/intern/cycles/device/queue.h b/intern/cycles/device/queue.h index 20308e4a106..808431af401 100644 --- a/intern/cycles/device/queue.h +++ b/intern/cycles/device/queue.h @@ -105,12 +105,11 @@ class DeviceQueue { * value. */ virtual int num_concurrent_busy_states() const = 0; - /* Number of partitions within which active indices are sorted by material ID. - * Using more partitions lets us trade off material coherence for better integrator state fetch - * locality. */ - virtual int num_sort_partitions(const size_t /*state_size*/) const + /* Number of elements in a partition of sorted shaders, that improves memory locality of + * integrator state fetch at the cost of decreased coherence for shader kernel execution. */ + virtual int num_sort_partition_elements() const { - return 1; + return 65536; } /* Initialize execution of kernels on this queue. diff --git a/intern/cycles/integrator/path_trace_work_gpu.cpp b/intern/cycles/integrator/path_trace_work_gpu.cpp index d51e8a28bb4..fa313f6460a 100644 --- a/intern/cycles/integrator/path_trace_work_gpu.cpp +++ b/intern/cycles/integrator/path_trace_work_gpu.cpp @@ -181,28 +181,45 @@ void PathTraceWorkGPU::alloc_integrator_queue() void PathTraceWorkGPU::alloc_integrator_sorting() { + /* Compute sort partitions, to balance between memory locality and coherence. + * Sort partitioning becomes less effective when more shaders are in the wavefront. In lieu of a + * more sophisticated heuristic we simply disable sort partitioning if the shader count is high. + */ + num_sort_partitions_ = 1; + if (device_scene_->data.max_shaders < 300) { + const int num_elements = queue_->num_sort_partition_elements(); + if (num_elements) { + num_sort_partitions_ = max(max_num_paths_ / num_elements, 1); + } + } + + integrator_state_gpu_.sort_partition_divisor = (int)divide_up(max_num_paths_, + num_sort_partitions_); + /* Allocate arrays for shader sorting. */ - num_sort_partitions_ = queue_->num_sort_partitions(estimate_single_state_size()); const int sort_buckets = device_scene_->data.max_shaders * num_sort_partitions_; if (integrator_shader_sort_counter_.size() < sort_buckets) { integrator_shader_sort_counter_.alloc(sort_buckets); integrator_shader_sort_counter_.zero_to_device(); + integrator_state_gpu_.sort_key_counter[DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE] = + (int *)integrator_shader_sort_counter_.device_pointer; - integrator_shader_raytrace_sort_counter_.alloc(sort_buckets); - integrator_shader_raytrace_sort_counter_.zero_to_device(); + if (device_scene_->data.kernel_features & KERNEL_FEATURE_NODE_RAYTRACE) { + integrator_shader_raytrace_sort_counter_.alloc(sort_buckets); + integrator_shader_raytrace_sort_counter_.zero_to_device(); + integrator_state_gpu_.sort_key_counter[DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE_RAYTRACE] = + (int *)integrator_shader_raytrace_sort_counter_.device_pointer; + } - integrator_shader_mnee_sort_counter_.alloc(sort_buckets); - integrator_shader_mnee_sort_counter_.zero_to_device(); + if (device_scene_->data.kernel_features & KERNEL_FEATURE_MNEE) { + integrator_shader_mnee_sort_counter_.alloc(sort_buckets); + integrator_shader_mnee_sort_counter_.zero_to_device(); + integrator_state_gpu_.sort_key_counter[DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE_MNEE] = + (int *)integrator_shader_mnee_sort_counter_.device_pointer; + } integrator_shader_sort_prefix_sum_.alloc(sort_buckets); integrator_shader_sort_prefix_sum_.zero_to_device(); - - integrator_state_gpu_.sort_key_counter[DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE] = - (int *)integrator_shader_sort_counter_.device_pointer; - integrator_state_gpu_.sort_key_counter[DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE_RAYTRACE] = - (int *)integrator_shader_raytrace_sort_counter_.device_pointer; - integrator_state_gpu_.sort_key_counter[DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE_MNEE] = - (int *)integrator_shader_mnee_sort_counter_.device_pointer; } } @@ -238,10 +255,6 @@ void PathTraceWorkGPU::init_execution() { queue_->init_execution(); - /* Setup sort partitioning divisor for better cache utilization. */ - integrator_state_gpu_.sort_partition_divisor = (int)divide_up(max_num_paths_, - num_sort_partitions_); - /* Copy to device side struct in constant memory. */ device_->const_copy_to( "integrator_state", &integrator_state_gpu_, sizeof(integrator_state_gpu_)); @@ -338,8 +351,12 @@ void PathTraceWorkGPU::enqueue_reset() queue_->enqueue(DEVICE_KERNEL_INTEGRATOR_RESET, max_num_paths_, args); queue_->zero_to_device(integrator_queue_counter_); queue_->zero_to_device(integrator_shader_sort_counter_); - queue_->zero_to_device(integrator_shader_raytrace_sort_counter_); - queue_->zero_to_device(integrator_shader_mnee_sort_counter_); + if (device_scene_->data.kernel_features & KERNEL_FEATURE_NODE_RAYTRACE) { + queue_->zero_to_device(integrator_shader_raytrace_sort_counter_); + } + if (device_scene_->data.kernel_features & KERNEL_FEATURE_MNEE) { + queue_->zero_to_device(integrator_shader_mnee_sort_counter_); + } /* Tiles enqueue need to know number of active paths, which is based on this counter. Zero the * counter on the host side because `zero_to_device()` is not doing it. */ diff --git a/intern/cycles/kernel/integrator/state_flow.h b/intern/cycles/kernel/integrator/state_flow.h index 1ae746022d0..4b03c665e17 100644 --- a/intern/cycles/kernel/integrator/state_flow.h +++ b/intern/cycles/kernel/integrator/state_flow.h @@ -99,13 +99,9 @@ ccl_device_forceinline void integrator_shadow_path_terminate(KernelGlobals kg, INTEGRATOR_STATE_WRITE(state, shadow_path, queued_kernel) = 0; } -# ifdef __KERNEL_SORT_PARTITIONING__ /* Sort first by truncated state index (for good locality), then by key (for good coherence). */ -# define INTEGRATOR_SORT_KEY(key, state) \ - (key + kernel_data.max_shaders * (state / kernel_integrator_state.sort_partition_divisor)) -# else -# define INTEGRATOR_SORT_KEY(key, state) (key) -# endif +# define INTEGRATOR_SORT_KEY(key, state) \ + (key + kernel_data.max_shaders * (state / kernel_integrator_state.sort_partition_divisor)) ccl_device_forceinline void integrator_path_init_sorted(KernelGlobals kg, IntegratorState state, -- cgit v1.2.3 From 914617f8fd01f8e84daf4ae0b8ea750a1bfbdc42 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Fri, 15 Jul 2022 12:57:37 +0200 Subject: Fix (unreported) LibOverride: invalid behaviors when creating (partial) overrides. The outliner would tagg all existing local IDs (for remap from linked reference data to newly created overrides) when creating a new override. This would become critical issue in case there is several existing copies of the same override hierarchy (leading to several hierarchies using the same override). Further more, BKE override creation code would not systematically properly remapp linked usages to new overrides one whithin the affected override hierarchy, leading to potential undesired remaining usages of linked data. --- source/blender/blenkernel/intern/lib_override.cc | 18 +++++++++++++++--- .../blender/editors/space_outliner/outliner_tools.cc | 2 +- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/source/blender/blenkernel/intern/lib_override.cc b/source/blender/blenkernel/intern/lib_override.cc index aa3210b64ad..0c5f59be768 100644 --- a/source/blender/blenkernel/intern/lib_override.cc +++ b/source/blender/blenkernel/intern/lib_override.cc @@ -1149,14 +1149,26 @@ static bool lib_override_library_create_do(Main *bmain, BKE_main_relations_tag_set(bmain, MAINIDRELATIONS_ENTRY_TAGS_PROCESSED, false); lib_override_hierarchy_dependencies_recursive_tag(&data); + /* In case the operation is on an already partially overridden hierarchy, all existing overrides + * in that hierarchy need to be tagged for remapping from linked reference ID usages to newly + * created overrides ones. */ + if (id_hierarchy_root_reference->lib != id_root_reference->lib) { + BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id_hierarchy_root_reference)); + BLI_assert(id_hierarchy_root_reference->override_library->reference->lib == + id_root_reference->lib); + + BKE_main_relations_tag_set(bmain, MAINIDRELATIONS_ENTRY_TAGS_PROCESSED, false); + data.hierarchy_root_id = id_hierarchy_root_reference; + data.id_root = id_hierarchy_root_reference; + data.is_override = true; + lib_override_overrides_group_tag(&data); + } + BKE_main_relations_free(bmain); lib_override_group_tag_data_clear(&data); bool success = false; if (id_hierarchy_root_reference->lib != id_root_reference->lib) { - BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id_hierarchy_root_reference)); - BLI_assert(id_hierarchy_root_reference->override_library->reference->lib == - id_root_reference->lib); success = BKE_lib_override_library_create_from_tag(bmain, owner_library, id_root_reference, diff --git a/source/blender/editors/space_outliner/outliner_tools.cc b/source/blender/editors/space_outliner/outliner_tools.cc index 7b7182eb106..9d52103a266 100644 --- a/source/blender/editors/space_outliner/outliner_tools.cc +++ b/source/blender/editors/space_outliner/outliner_tools.cc @@ -875,7 +875,7 @@ static void id_override_library_create_fn(bContext *C, /* For now, remap all local usages of linked ID to local override one here. */ ID *id_iter; FOREACH_MAIN_ID_BEGIN (bmain, id_iter) { - if (ID_IS_LINKED(id_iter)) { + if (ID_IS_LINKED(id_iter) || ID_IS_OVERRIDE_LIBRARY(id_iter)) { id_iter->tag &= ~LIB_TAG_DOIT; } else { -- cgit v1.2.3 From 180db0f752c88d3bbd47774a0f7c9a31de5a3864 Mon Sep 17 00:00:00 2001 From: Damien Picard Date: Fri, 15 Jul 2022 14:12:34 +0200 Subject: UI: make many modifier strings translatable This includes: - new modifier names It mostly uses `N_` because the strings are actually translated elsewhere. The goal is simply to export them to .po files. Most of the new translations were reported in T43295#1105335. Reviewed By: mont29 Differential Revision: https://developer.blender.org/D15418 --- source/blender/modifiers/intern/MOD_armature.c | 2 +- source/blender/modifiers/intern/MOD_array.c | 2 +- source/blender/modifiers/intern/MOD_bevel.c | 2 +- source/blender/modifiers/intern/MOD_boolean.cc | 2 +- source/blender/modifiers/intern/MOD_build.c | 4 +++- source/blender/modifiers/intern/MOD_cast.c | 2 +- source/blender/modifiers/intern/MOD_cloth.c | 2 +- source/blender/modifiers/intern/MOD_collision.c | 2 +- source/blender/modifiers/intern/MOD_correctivesmooth.c | 2 +- source/blender/modifiers/intern/MOD_curve.c | 2 +- source/blender/modifiers/intern/MOD_datatransfer.c | 2 +- source/blender/modifiers/intern/MOD_decimate.c | 2 +- source/blender/modifiers/intern/MOD_displace.c | 2 +- source/blender/modifiers/intern/MOD_dynamicpaint.c | 2 +- source/blender/modifiers/intern/MOD_edgesplit.c | 2 +- source/blender/modifiers/intern/MOD_explode.c | 2 +- source/blender/modifiers/intern/MOD_fluid.c | 2 +- source/blender/modifiers/intern/MOD_hook.c | 2 +- source/blender/modifiers/intern/MOD_laplaciandeform.c | 2 +- source/blender/modifiers/intern/MOD_laplaciansmooth.c | 2 +- source/blender/modifiers/intern/MOD_lattice.c | 2 +- source/blender/modifiers/intern/MOD_mask.cc | 2 +- source/blender/modifiers/intern/MOD_mesh_to_volume.cc | 4 +++- source/blender/modifiers/intern/MOD_meshcache.c | 2 +- source/blender/modifiers/intern/MOD_meshdeform.c | 2 +- source/blender/modifiers/intern/MOD_meshsequencecache.cc | 2 +- source/blender/modifiers/intern/MOD_mirror.c | 2 +- source/blender/modifiers/intern/MOD_multires.c | 2 +- source/blender/modifiers/intern/MOD_nodes.cc | 2 +- source/blender/modifiers/intern/MOD_normal_edit.c | 2 +- source/blender/modifiers/intern/MOD_ocean.c | 2 +- source/blender/modifiers/intern/MOD_particleinstance.c | 2 +- source/blender/modifiers/intern/MOD_particlesystem.cc | 2 +- source/blender/modifiers/intern/MOD_remesh.c | 2 +- source/blender/modifiers/intern/MOD_screw.c | 2 +- source/blender/modifiers/intern/MOD_shapekey.c | 4 +++- source/blender/modifiers/intern/MOD_shrinkwrap.c | 2 +- source/blender/modifiers/intern/MOD_simpledeform.c | 2 +- source/blender/modifiers/intern/MOD_skin.c | 2 +- source/blender/modifiers/intern/MOD_smooth.c | 2 +- source/blender/modifiers/intern/MOD_softbody.c | 2 +- source/blender/modifiers/intern/MOD_solidify.c | 2 +- source/blender/modifiers/intern/MOD_subsurf.c | 2 +- source/blender/modifiers/intern/MOD_surface.c | 2 +- source/blender/modifiers/intern/MOD_surfacedeform.c | 2 +- source/blender/modifiers/intern/MOD_triangulate.c | 2 +- source/blender/modifiers/intern/MOD_uvproject.c | 2 +- source/blender/modifiers/intern/MOD_uvwarp.c | 2 +- source/blender/modifiers/intern/MOD_volume_displace.cc | 4 +++- source/blender/modifiers/intern/MOD_volume_to_mesh.cc | 4 +++- source/blender/modifiers/intern/MOD_warp.c | 2 +- source/blender/modifiers/intern/MOD_wave.c | 12 ++++++------ source/blender/modifiers/intern/MOD_weighted_normal.c | 2 +- source/blender/modifiers/intern/MOD_weightvgedit.c | 2 +- source/blender/modifiers/intern/MOD_weightvgmix.c | 2 +- source/blender/modifiers/intern/MOD_weightvgproximity.c | 2 +- source/blender/modifiers/intern/MOD_weld.cc | 2 +- source/blender/modifiers/intern/MOD_wireframe.c | 2 +- 58 files changed, 73 insertions(+), 63 deletions(-) diff --git a/source/blender/modifiers/intern/MOD_armature.c b/source/blender/modifiers/intern/MOD_armature.c index f6e8a26f0e1..15ad361a262 100644 --- a/source/blender/modifiers/intern/MOD_armature.c +++ b/source/blender/modifiers/intern/MOD_armature.c @@ -266,7 +266,7 @@ static void blendRead(BlendDataReader *UNUSED(reader), ModifierData *md) } ModifierTypeInfo modifierType_Armature = { - /* name */ "Armature", + /* name */ N_("Armature"), /* structName */ "ArmatureModifierData", /* structSize */ sizeof(ArmatureModifierData), /* srna */ &RNA_ArmatureModifier, diff --git a/source/blender/modifiers/intern/MOD_array.c b/source/blender/modifiers/intern/MOD_array.c index 77915702ee4..a7361f6d0b6 100644 --- a/source/blender/modifiers/intern/MOD_array.c +++ b/source/blender/modifiers/intern/MOD_array.c @@ -1002,7 +1002,7 @@ static void panelRegister(ARegionType *region_type) } ModifierTypeInfo modifierType_Array = { - /* name */ "Array", + /* name */ N_("Array"), /* structName */ "ArrayModifierData", /* structSize */ sizeof(ArrayModifierData), /* srna */ &RNA_ArrayModifier, diff --git a/source/blender/modifiers/intern/MOD_bevel.c b/source/blender/modifiers/intern/MOD_bevel.c index c634873cfe4..94f2090e081 100644 --- a/source/blender/modifiers/intern/MOD_bevel.c +++ b/source/blender/modifiers/intern/MOD_bevel.c @@ -417,7 +417,7 @@ static void blendRead(BlendDataReader *reader, ModifierData *md) } ModifierTypeInfo modifierType_Bevel = { - /* name */ "Bevel", + /* name */ N_("Bevel"), /* structName */ "BevelModifierData", /* structSize */ sizeof(BevelModifierData), /* srna */ &RNA_BevelModifier, diff --git a/source/blender/modifiers/intern/MOD_boolean.cc b/source/blender/modifiers/intern/MOD_boolean.cc index 09676d0e9ee..c9dc14b3b20 100644 --- a/source/blender/modifiers/intern/MOD_boolean.cc +++ b/source/blender/modifiers/intern/MOD_boolean.cc @@ -639,7 +639,7 @@ static void panelRegister(ARegionType *region_type) } ModifierTypeInfo modifierType_Boolean = { - /* name */ "Boolean", + /* name */ N_("Boolean"), /* structName */ "BooleanModifierData", /* structSize */ sizeof(BooleanModifierData), /* srna */ &RNA_BooleanModifier, diff --git a/source/blender/modifiers/intern/MOD_build.c b/source/blender/modifiers/intern/MOD_build.c index 687ff04cedf..a9b6af967be 100644 --- a/source/blender/modifiers/intern/MOD_build.c +++ b/source/blender/modifiers/intern/MOD_build.c @@ -13,6 +13,8 @@ #include "BLI_math_vector.h" #include "BLI_rand.h" +#include "BLT_translation.h" + #include "DNA_defaults.h" #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" @@ -311,7 +313,7 @@ static void panelRegister(ARegionType *region_type) } ModifierTypeInfo modifierType_Build = { - /* name */ "Build", + /* name */ N_("Build"), /* structName */ "BuildModifierData", /* structSize */ sizeof(BuildModifierData), /* srna */ &RNA_BuildModifier, diff --git a/source/blender/modifiers/intern/MOD_cast.c b/source/blender/modifiers/intern/MOD_cast.c index 3916551bc90..9aaf7fead36 100644 --- a/source/blender/modifiers/intern/MOD_cast.c +++ b/source/blender/modifiers/intern/MOD_cast.c @@ -559,7 +559,7 @@ static void panelRegister(ARegionType *region_type) } ModifierTypeInfo modifierType_Cast = { - /* name */ "Cast", + /* name */ N_("Cast"), /* structName */ "CastModifierData", /* structSize */ sizeof(CastModifierData), /* srna */ &RNA_CastModifier, diff --git a/source/blender/modifiers/intern/MOD_cloth.c b/source/blender/modifiers/intern/MOD_cloth.c index 185b05b4cf9..cc0bd87d614 100644 --- a/source/blender/modifiers/intern/MOD_cloth.c +++ b/source/blender/modifiers/intern/MOD_cloth.c @@ -275,7 +275,7 @@ static void panelRegister(ARegionType *region_type) } ModifierTypeInfo modifierType_Cloth = { - /* name */ "Cloth", + /* name */ N_("Cloth"), /* structName */ "ClothModifierData", /* structSize */ sizeof(ClothModifierData), /* srna */ &RNA_ClothModifier, diff --git a/source/blender/modifiers/intern/MOD_collision.c b/source/blender/modifiers/intern/MOD_collision.c index f9cd3d5937d..74cb4ac700a 100644 --- a/source/blender/modifiers/intern/MOD_collision.c +++ b/source/blender/modifiers/intern/MOD_collision.c @@ -284,7 +284,7 @@ static void blendRead(BlendDataReader *UNUSED(reader), ModifierData *md) } ModifierTypeInfo modifierType_Collision = { - /* name */ "Collision", + /* name */ N_("Collision"), /* structName */ "CollisionModifierData", /* structSize */ sizeof(CollisionModifierData), /* srna */ &RNA_CollisionModifier, diff --git a/source/blender/modifiers/intern/MOD_correctivesmooth.c b/source/blender/modifiers/intern/MOD_correctivesmooth.c index 3698f4403a1..2beb1be6749 100644 --- a/source/blender/modifiers/intern/MOD_correctivesmooth.c +++ b/source/blender/modifiers/intern/MOD_correctivesmooth.c @@ -835,7 +835,7 @@ static void blendRead(BlendDataReader *reader, ModifierData *md) } ModifierTypeInfo modifierType_CorrectiveSmooth = { - /* name */ "CorrectiveSmooth", + /* name */ N_("CorrectiveSmooth"), /* structName */ "CorrectiveSmoothModifierData", /* structSize */ sizeof(CorrectiveSmoothModifierData), /* srna */ &RNA_CorrectiveSmoothModifier, diff --git a/source/blender/modifiers/intern/MOD_curve.c b/source/blender/modifiers/intern/MOD_curve.c index a82b999f4dc..48a59f4d949 100644 --- a/source/blender/modifiers/intern/MOD_curve.c +++ b/source/blender/modifiers/intern/MOD_curve.c @@ -203,7 +203,7 @@ static void panelRegister(ARegionType *region_type) } ModifierTypeInfo modifierType_Curve = { - /* name */ "Curve", + /* name */ N_("Curve"), /* structName */ "CurveModifierData", /* structSize */ sizeof(CurveModifierData), /* srna */ &RNA_CurveModifier, diff --git a/source/blender/modifiers/intern/MOD_datatransfer.c b/source/blender/modifiers/intern/MOD_datatransfer.c index a3b088799cc..e9f1cf47e38 100644 --- a/source/blender/modifiers/intern/MOD_datatransfer.c +++ b/source/blender/modifiers/intern/MOD_datatransfer.c @@ -474,7 +474,7 @@ static void panelRegister(ARegionType *region_type) #undef DT_TYPES_AFFECT_MESH ModifierTypeInfo modifierType_DataTransfer = { - /* name */ "DataTransfer", + /* name */ N_("DataTransfer"), /* structName */ "DataTransferModifierData", /* structSize */ sizeof(DataTransferModifierData), /* srna */ &RNA_DataTransferModifier, diff --git a/source/blender/modifiers/intern/MOD_decimate.c b/source/blender/modifiers/intern/MOD_decimate.c index 70f028d6907..3df4fbcbea8 100644 --- a/source/blender/modifiers/intern/MOD_decimate.c +++ b/source/blender/modifiers/intern/MOD_decimate.c @@ -271,7 +271,7 @@ static void panelRegister(ARegionType *region_type) } ModifierTypeInfo modifierType_Decimate = { - /* name */ "Decimate", + /* name */ N_("Decimate"), /* structName */ "DecimateModifierData", /* structSize */ sizeof(DecimateModifierData), /* srna */ &RNA_DecimateModifier, diff --git a/source/blender/modifiers/intern/MOD_displace.c b/source/blender/modifiers/intern/MOD_displace.c index 1e2224e3a65..5289fc42e21 100644 --- a/source/blender/modifiers/intern/MOD_displace.c +++ b/source/blender/modifiers/intern/MOD_displace.c @@ -473,7 +473,7 @@ static void panelRegister(ARegionType *region_type) } ModifierTypeInfo modifierType_Displace = { - /* name */ "Displace", + /* name */ N_("Displace"), /* structName */ "DisplaceModifierData", /* structSize */ sizeof(DisplaceModifierData), /* srna */ &RNA_DisplaceModifier, diff --git a/source/blender/modifiers/intern/MOD_dynamicpaint.c b/source/blender/modifiers/intern/MOD_dynamicpaint.c index 6b0578c77f1..4afb81c04a9 100644 --- a/source/blender/modifiers/intern/MOD_dynamicpaint.c +++ b/source/blender/modifiers/intern/MOD_dynamicpaint.c @@ -189,7 +189,7 @@ static void panelRegister(ARegionType *region_type) } ModifierTypeInfo modifierType_DynamicPaint = { - /* name */ "Dynamic Paint", + /* name */ N_("Dynamic Paint"), /* structName */ "DynamicPaintModifierData", /* structSize */ sizeof(DynamicPaintModifierData), /* srna */ &RNA_DynamicPaintModifier, diff --git a/source/blender/modifiers/intern/MOD_edgesplit.c b/source/blender/modifiers/intern/MOD_edgesplit.c index 49ddcb9a61d..b381ff32aa2 100644 --- a/source/blender/modifiers/intern/MOD_edgesplit.c +++ b/source/blender/modifiers/intern/MOD_edgesplit.c @@ -154,7 +154,7 @@ static void panelRegister(ARegionType *region_type) } ModifierTypeInfo modifierType_EdgeSplit = { - /* name */ "EdgeSplit", + /* name */ N_("EdgeSplit"), /* structName */ "EdgeSplitModifierData", /* structSize */ sizeof(EdgeSplitModifierData), /* srna */ &RNA_EdgeSplitModifier, diff --git a/source/blender/modifiers/intern/MOD_explode.c b/source/blender/modifiers/intern/MOD_explode.c index f88e930e127..ff0616fd288 100644 --- a/source/blender/modifiers/intern/MOD_explode.c +++ b/source/blender/modifiers/intern/MOD_explode.c @@ -1224,7 +1224,7 @@ static void blendRead(BlendDataReader *UNUSED(reader), ModifierData *md) } ModifierTypeInfo modifierType_Explode = { - /* name */ "Explode", + /* name */ N_("Explode"), /* structName */ "ExplodeModifierData", /* structSize */ sizeof(ExplodeModifierData), /* srna */ &RNA_ExplodeModifier, diff --git a/source/blender/modifiers/intern/MOD_fluid.c b/source/blender/modifiers/intern/MOD_fluid.c index 562f0df510d..a3e9cd083d2 100644 --- a/source/blender/modifiers/intern/MOD_fluid.c +++ b/source/blender/modifiers/intern/MOD_fluid.c @@ -241,7 +241,7 @@ static void panelRegister(ARegionType *region_type) } ModifierTypeInfo modifierType_Fluid = { - /* name */ "Fluid", + /* name */ N_("Fluid"), /* structName */ "FluidModifierData", /* structSize */ sizeof(FluidModifierData), /* srna */ &RNA_FluidModifier, diff --git a/source/blender/modifiers/intern/MOD_hook.c b/source/blender/modifiers/intern/MOD_hook.c index 3649ece12e1..3c4e6b0d90f 100644 --- a/source/blender/modifiers/intern/MOD_hook.c +++ b/source/blender/modifiers/intern/MOD_hook.c @@ -545,7 +545,7 @@ static void blendRead(BlendDataReader *reader, ModifierData *md) } ModifierTypeInfo modifierType_Hook = { - /* name */ "Hook", + /* name */ N_("Hook"), /* structName */ "HookModifierData", /* structSize */ sizeof(HookModifierData), /* srna */ &RNA_HookModifier, diff --git a/source/blender/modifiers/intern/MOD_laplaciandeform.c b/source/blender/modifiers/intern/MOD_laplaciandeform.c index a22f4b35e0d..e29098eb218 100644 --- a/source/blender/modifiers/intern/MOD_laplaciandeform.c +++ b/source/blender/modifiers/intern/MOD_laplaciandeform.c @@ -875,7 +875,7 @@ static void blendRead(BlendDataReader *reader, ModifierData *md) } ModifierTypeInfo modifierType_LaplacianDeform = { - /* name */ "LaplacianDeform", + /* name */ N_("LaplacianDeform"), /* structName */ "LaplacianDeformModifierData", /* structSize */ sizeof(LaplacianDeformModifierData), /* srna */ &RNA_LaplacianDeformModifier, diff --git a/source/blender/modifiers/intern/MOD_laplaciansmooth.c b/source/blender/modifiers/intern/MOD_laplaciansmooth.c index 95283b1cd20..2cce0c14e4c 100644 --- a/source/blender/modifiers/intern/MOD_laplaciansmooth.c +++ b/source/blender/modifiers/intern/MOD_laplaciansmooth.c @@ -608,7 +608,7 @@ static void panelRegister(ARegionType *region_type) } ModifierTypeInfo modifierType_LaplacianSmooth = { - /* name */ "LaplacianSmooth", + /* name */ N_("LaplacianSmooth"), /* structName */ "LaplacianSmoothModifierData", /* structSize */ sizeof(LaplacianSmoothModifierData), /* srna */ &RNA_LaplacianSmoothModifier, diff --git a/source/blender/modifiers/intern/MOD_lattice.c b/source/blender/modifiers/intern/MOD_lattice.c index 832372304a0..0e1994eed36 100644 --- a/source/blender/modifiers/intern/MOD_lattice.c +++ b/source/blender/modifiers/intern/MOD_lattice.c @@ -160,7 +160,7 @@ static void panelRegister(ARegionType *region_type) } ModifierTypeInfo modifierType_Lattice = { - /* name */ "Lattice", + /* name */ N_("Lattice"), /* structName */ "LatticeModifierData", /* structSize */ sizeof(LatticeModifierData), /* srna */ &RNA_LatticeModifier, diff --git a/source/blender/modifiers/intern/MOD_mask.cc b/source/blender/modifiers/intern/MOD_mask.cc index 0813901fc49..fac3ea36537 100644 --- a/source/blender/modifiers/intern/MOD_mask.cc +++ b/source/blender/modifiers/intern/MOD_mask.cc @@ -804,7 +804,7 @@ static void panelRegister(ARegionType *region_type) } ModifierTypeInfo modifierType_Mask = { - /* name */ "Mask", + /* name */ N_("Mask"), /* structName */ "MaskModifierData", /* structSize */ sizeof(MaskModifierData), /* srna */ &RNA_MaskModifier, diff --git a/source/blender/modifiers/intern/MOD_mesh_to_volume.cc b/source/blender/modifiers/intern/MOD_mesh_to_volume.cc index 39bd013609b..7d4affa2dce 100644 --- a/source/blender/modifiers/intern/MOD_mesh_to_volume.cc +++ b/source/blender/modifiers/intern/MOD_mesh_to_volume.cc @@ -15,6 +15,8 @@ #include "BKE_object.h" #include "BKE_volume.h" +#include "BLT_translation.h" + #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" #include "DNA_object_types.h" @@ -202,7 +204,7 @@ static void modifyGeometrySet(ModifierData *md, } ModifierTypeInfo modifierType_MeshToVolume = { - /* name */ "Mesh to Volume", + /* name */ N_("Mesh to Volume"), /* structName */ "MeshToVolumeModifierData", /* structSize */ sizeof(MeshToVolumeModifierData), /* srna */ &RNA_MeshToVolumeModifier, diff --git a/source/blender/modifiers/intern/MOD_meshcache.c b/source/blender/modifiers/intern/MOD_meshcache.c index 6f065797b43..8dfdd07ace9 100644 --- a/source/blender/modifiers/intern/MOD_meshcache.c +++ b/source/blender/modifiers/intern/MOD_meshcache.c @@ -416,7 +416,7 @@ static void panelRegister(ARegionType *region_type) } ModifierTypeInfo modifierType_MeshCache = { - /* name */ "MeshCache", + /* name */ N_("MeshCache"), /* structName */ "MeshCacheModifierData", /* structSize */ sizeof(MeshCacheModifierData), /* srna */ &RNA_MeshCacheModifier, diff --git a/source/blender/modifiers/intern/MOD_meshdeform.c b/source/blender/modifiers/intern/MOD_meshdeform.c index 5dac31ac1df..40aa0f84f92 100644 --- a/source/blender/modifiers/intern/MOD_meshdeform.c +++ b/source/blender/modifiers/intern/MOD_meshdeform.c @@ -650,7 +650,7 @@ static void blendRead(BlendDataReader *reader, ModifierData *md) } ModifierTypeInfo modifierType_MeshDeform = { - /* name */ "MeshDeform", + /* name */ N_("MeshDeform"), /* structName */ "MeshDeformModifierData", /* structSize */ sizeof(MeshDeformModifierData), /* srna */ &RNA_MeshDeformModifier, diff --git a/source/blender/modifiers/intern/MOD_meshsequencecache.cc b/source/blender/modifiers/intern/MOD_meshsequencecache.cc index 273050eafd8..1c35160d3ef 100644 --- a/source/blender/modifiers/intern/MOD_meshsequencecache.cc +++ b/source/blender/modifiers/intern/MOD_meshsequencecache.cc @@ -392,7 +392,7 @@ static void blendRead(BlendDataReader *UNUSED(reader), ModifierData *md) } ModifierTypeInfo modifierType_MeshSequenceCache = { - /* name */ "MeshSequenceCache", + /* name */ N_("MeshSequenceCache"), /* structName */ "MeshSeqCacheModifierData", /* structSize */ sizeof(MeshSeqCacheModifierData), /* srna */ &RNA_MeshSequenceCacheModifier, diff --git a/source/blender/modifiers/intern/MOD_mirror.c b/source/blender/modifiers/intern/MOD_mirror.c index b6ba8c9e0f9..5f095a72dca 100644 --- a/source/blender/modifiers/intern/MOD_mirror.c +++ b/source/blender/modifiers/intern/MOD_mirror.c @@ -204,7 +204,7 @@ static void panelRegister(ARegionType *region_type) } ModifierTypeInfo modifierType_Mirror = { - /* name */ "Mirror", + /* name */ N_("Mirror"), /* structName */ "MirrorModifierData", /* structSize */ sizeof(MirrorModifierData), /* srna */ &RNA_MirrorModifier, diff --git a/source/blender/modifiers/intern/MOD_multires.c b/source/blender/modifiers/intern/MOD_multires.c index a4c5ddac5c9..cdad834f9b4 100644 --- a/source/blender/modifiers/intern/MOD_multires.c +++ b/source/blender/modifiers/intern/MOD_multires.c @@ -487,7 +487,7 @@ static void panelRegister(ARegionType *region_type) } ModifierTypeInfo modifierType_Multires = { - /* name */ "Multires", + /* name */ N_("Multires"), /* structName */ "MultiresModifierData", /* structSize */ sizeof(MultiresModifierData), /* srna */ &RNA_MultiresModifier, diff --git a/source/blender/modifiers/intern/MOD_nodes.cc b/source/blender/modifiers/intern/MOD_nodes.cc index 885d2f901ec..01e4d5ff6b3 100644 --- a/source/blender/modifiers/intern/MOD_nodes.cc +++ b/source/blender/modifiers/intern/MOD_nodes.cc @@ -1825,7 +1825,7 @@ static void requiredDataMask(Object *UNUSED(ob), } ModifierTypeInfo modifierType_Nodes = { - /* name */ "GeometryNodes", + /* name */ N_("GeometryNodes"), /* structName */ "NodesModifierData", /* structSize */ sizeof(NodesModifierData), /* srna */ &RNA_NodesModifier, diff --git a/source/blender/modifiers/intern/MOD_normal_edit.c b/source/blender/modifiers/intern/MOD_normal_edit.c index c215ac601a1..09bc9546325 100644 --- a/source/blender/modifiers/intern/MOD_normal_edit.c +++ b/source/blender/modifiers/intern/MOD_normal_edit.c @@ -756,7 +756,7 @@ static void panelRegister(ARegionType *region_type) } ModifierTypeInfo modifierType_NormalEdit = { - /* name */ "NormalEdit", + /* name */ N_("NormalEdit"), /* structName */ "NormalEditModifierData", /* structSize */ sizeof(NormalEditModifierData), /* srna */ &RNA_NormalEditModifier, diff --git a/source/blender/modifiers/intern/MOD_ocean.c b/source/blender/modifiers/intern/MOD_ocean.c index 75708851030..ea9049200cc 100644 --- a/source/blender/modifiers/intern/MOD_ocean.c +++ b/source/blender/modifiers/intern/MOD_ocean.c @@ -701,7 +701,7 @@ static void blendRead(BlendDataReader *UNUSED(reader), ModifierData *md) } ModifierTypeInfo modifierType_Ocean = { - /* name */ "Ocean", + /* name */ N_("Ocean"), /* structName */ "OceanModifierData", /* structSize */ sizeof(OceanModifierData), /* srna */ &RNA_OceanModifier, diff --git a/source/blender/modifiers/intern/MOD_particleinstance.c b/source/blender/modifiers/intern/MOD_particleinstance.c index 2a8d2454670..5018b2d1030 100644 --- a/source/blender/modifiers/intern/MOD_particleinstance.c +++ b/source/blender/modifiers/intern/MOD_particleinstance.c @@ -644,7 +644,7 @@ static void panelRegister(ARegionType *region_type) } ModifierTypeInfo modifierType_ParticleInstance = { - /* name */ "ParticleInstance", + /* name */ N_("ParticleInstance"), /* structName */ "ParticleInstanceModifierData", /* structSize */ sizeof(ParticleInstanceModifierData), /* srna */ &RNA_ParticleInstanceModifier, diff --git a/source/blender/modifiers/intern/MOD_particlesystem.cc b/source/blender/modifiers/intern/MOD_particlesystem.cc index ccbc8f1d835..7f7465947f9 100644 --- a/source/blender/modifiers/intern/MOD_particlesystem.cc +++ b/source/blender/modifiers/intern/MOD_particlesystem.cc @@ -301,7 +301,7 @@ static void blendRead(BlendDataReader *reader, ModifierData *md) } ModifierTypeInfo modifierType_ParticleSystem = { - /* name */ "ParticleSystem", + /* name */ N_("ParticleSystem"), /* structName */ "ParticleSystemModifierData", /* structSize */ sizeof(ParticleSystemModifierData), /* srna */ &RNA_ParticleSystemModifier, diff --git a/source/blender/modifiers/intern/MOD_remesh.c b/source/blender/modifiers/intern/MOD_remesh.c index 288faee247f..f21d536fadf 100644 --- a/source/blender/modifiers/intern/MOD_remesh.c +++ b/source/blender/modifiers/intern/MOD_remesh.c @@ -268,7 +268,7 @@ static void panelRegister(ARegionType *region_type) } ModifierTypeInfo modifierType_Remesh = { - /* name */ "Remesh", + /* name */ N_("Remesh"), /* structName */ "RemeshModifierData", /* structSize */ sizeof(RemeshModifierData), /* srna */ &RNA_RemeshModifier, diff --git a/source/blender/modifiers/intern/MOD_screw.c b/source/blender/modifiers/intern/MOD_screw.c index 0e22f59c2fb..9588b9acd3b 100644 --- a/source/blender/modifiers/intern/MOD_screw.c +++ b/source/blender/modifiers/intern/MOD_screw.c @@ -1235,7 +1235,7 @@ static void panelRegister(ARegionType *region_type) } ModifierTypeInfo modifierType_Screw = { - /* name */ "Screw", + /* name */ N_("Screw"), /* structName */ "ScrewModifierData", /* structSize */ sizeof(ScrewModifierData), /* srna */ &RNA_ScrewModifier, diff --git a/source/blender/modifiers/intern/MOD_shapekey.c b/source/blender/modifiers/intern/MOD_shapekey.c index 6953e89dfc2..cc214c1147a 100644 --- a/source/blender/modifiers/intern/MOD_shapekey.c +++ b/source/blender/modifiers/intern/MOD_shapekey.c @@ -9,6 +9,8 @@ #include "BLI_math.h" +#include "BLT_translation.h" + #include "DNA_key_types.h" #include "DNA_mesh_types.h" #include "DNA_object_types.h" @@ -108,7 +110,7 @@ static void deformMatricesEM(ModifierData *UNUSED(md), } ModifierTypeInfo modifierType_ShapeKey = { - /* name */ "ShapeKey", + /* name */ N_("ShapeKey"), /* structName */ "ShapeKeyModifierData", /* structSize */ sizeof(ShapeKeyModifierData), /* srna */ &RNA_Modifier, diff --git a/source/blender/modifiers/intern/MOD_shrinkwrap.c b/source/blender/modifiers/intern/MOD_shrinkwrap.c index 488df3d6f4c..be12dc6639b 100644 --- a/source/blender/modifiers/intern/MOD_shrinkwrap.c +++ b/source/blender/modifiers/intern/MOD_shrinkwrap.c @@ -260,7 +260,7 @@ static void panelRegister(ARegionType *region_type) } ModifierTypeInfo modifierType_Shrinkwrap = { - /* name */ "Shrinkwrap", + /* name */ N_("Shrinkwrap"), /* structName */ "ShrinkwrapModifierData", /* structSize */ sizeof(ShrinkwrapModifierData), /* srna */ &RNA_ShrinkwrapModifier, diff --git a/source/blender/modifiers/intern/MOD_simpledeform.c b/source/blender/modifiers/intern/MOD_simpledeform.c index e3c7f1c423b..9f1d0cd36c4 100644 --- a/source/blender/modifiers/intern/MOD_simpledeform.c +++ b/source/blender/modifiers/intern/MOD_simpledeform.c @@ -564,7 +564,7 @@ static void panelRegister(ARegionType *region_type) } ModifierTypeInfo modifierType_SimpleDeform = { - /* name */ "SimpleDeform", + /* name */ N_("SimpleDeform"), /* structName */ "SimpleDeformModifierData", /* structSize */ sizeof(SimpleDeformModifierData), /* srna */ &RNA_SimpleDeformModifier, diff --git a/source/blender/modifiers/intern/MOD_skin.c b/source/blender/modifiers/intern/MOD_skin.c index 5f238209015..84795cdb2d9 100644 --- a/source/blender/modifiers/intern/MOD_skin.c +++ b/source/blender/modifiers/intern/MOD_skin.c @@ -2069,7 +2069,7 @@ static void panelRegister(ARegionType *region_type) } ModifierTypeInfo modifierType_Skin = { - /* name */ "Skin", + /* name */ N_("Skin"), /* structName */ "SkinModifierData", /* structSize */ sizeof(SkinModifierData), /* srna */ &RNA_SkinModifier, diff --git a/source/blender/modifiers/intern/MOD_smooth.c b/source/blender/modifiers/intern/MOD_smooth.c index 5439083d9a7..c868c47cb90 100644 --- a/source/blender/modifiers/intern/MOD_smooth.c +++ b/source/blender/modifiers/intern/MOD_smooth.c @@ -253,7 +253,7 @@ static void panelRegister(ARegionType *region_type) } ModifierTypeInfo modifierType_Smooth = { - /* name */ "Smooth", + /* name */ N_("Smooth"), /* structName */ "SmoothModifierData", /* structSize */ sizeof(SmoothModifierData), /* srna */ &RNA_SmoothModifier, diff --git a/source/blender/modifiers/intern/MOD_softbody.c b/source/blender/modifiers/intern/MOD_softbody.c index d8379cc870a..a49f2609641 100644 --- a/source/blender/modifiers/intern/MOD_softbody.c +++ b/source/blender/modifiers/intern/MOD_softbody.c @@ -86,7 +86,7 @@ static void panelRegister(ARegionType *region_type) } ModifierTypeInfo modifierType_Softbody = { - /* name */ "Softbody", + /* name */ N_("Softbody"), /* structName */ "SoftbodyModifierData", /* structSize */ sizeof(SoftbodyModifierData), /* srna */ &RNA_SoftBodyModifier, diff --git a/source/blender/modifiers/intern/MOD_solidify.c b/source/blender/modifiers/intern/MOD_solidify.c index 1f0aee7d689..3e2d590c928 100644 --- a/source/blender/modifiers/intern/MOD_solidify.c +++ b/source/blender/modifiers/intern/MOD_solidify.c @@ -241,7 +241,7 @@ static void panelRegister(ARegionType *region_type) } ModifierTypeInfo modifierType_Solidify = { - /* name */ "Solidify", + /* name */ N_("Solidify"), /* structName */ "SolidifyModifierData", /* structSize */ sizeof(SolidifyModifierData), /* srna */ &RNA_SolidifyModifier, diff --git a/source/blender/modifiers/intern/MOD_subsurf.c b/source/blender/modifiers/intern/MOD_subsurf.c index 23f447f2469..8faf2bdbea2 100644 --- a/source/blender/modifiers/intern/MOD_subsurf.c +++ b/source/blender/modifiers/intern/MOD_subsurf.c @@ -472,7 +472,7 @@ static void blendRead(BlendDataReader *UNUSED(reader), ModifierData *md) } ModifierTypeInfo modifierType_Subsurf = { - /* name */ "Subdivision", + /* name */ N_("Subdivision"), /* structName */ "SubsurfModifierData", /* structSize */ sizeof(SubsurfModifierData), /* srna */ &RNA_SubsurfModifier, diff --git a/source/blender/modifiers/intern/MOD_surface.c b/source/blender/modifiers/intern/MOD_surface.c index af1de77d8c9..8cfe3b35949 100644 --- a/source/blender/modifiers/intern/MOD_surface.c +++ b/source/blender/modifiers/intern/MOD_surface.c @@ -211,7 +211,7 @@ static void blendRead(BlendDataReader *UNUSED(reader), ModifierData *md) } ModifierTypeInfo modifierType_Surface = { - /* name */ "Surface", + /* name */ N_("Surface"), /* structName */ "SurfaceModifierData", /* structSize */ sizeof(SurfaceModifierData), /* srna */ &RNA_SurfaceModifier, diff --git a/source/blender/modifiers/intern/MOD_surfacedeform.c b/source/blender/modifiers/intern/MOD_surfacedeform.c index 20cc9b2392f..d63ef12285b 100644 --- a/source/blender/modifiers/intern/MOD_surfacedeform.c +++ b/source/blender/modifiers/intern/MOD_surfacedeform.c @@ -1741,7 +1741,7 @@ static void blendRead(BlendDataReader *reader, ModifierData *md) } ModifierTypeInfo modifierType_SurfaceDeform = { - /* name */ "SurfaceDeform", + /* name */ N_("SurfaceDeform"), /* structName */ "SurfaceDeformModifierData", /* structSize */ sizeof(SurfaceDeformModifierData), /* srna */ &RNA_SurfaceDeformModifier, diff --git a/source/blender/modifiers/intern/MOD_triangulate.c b/source/blender/modifiers/intern/MOD_triangulate.c index f1e8ef5bf38..d4faf682cdc 100644 --- a/source/blender/modifiers/intern/MOD_triangulate.c +++ b/source/blender/modifiers/intern/MOD_triangulate.c @@ -139,7 +139,7 @@ static void panelRegister(ARegionType *region_type) } ModifierTypeInfo modifierType_Triangulate = { - /* name */ "Triangulate", + /* name */ N_("Triangulate"), /* structName */ "TriangulateModifierData", /* structSize */ sizeof(TriangulateModifierData), /* srna */ &RNA_TriangulateModifier, diff --git a/source/blender/modifiers/intern/MOD_uvproject.c b/source/blender/modifiers/intern/MOD_uvproject.c index d4d7ecef283..0474d3e47e6 100644 --- a/source/blender/modifiers/intern/MOD_uvproject.c +++ b/source/blender/modifiers/intern/MOD_uvproject.c @@ -349,7 +349,7 @@ static void panelRegister(ARegionType *region_type) } ModifierTypeInfo modifierType_UVProject = { - /* name */ "UVProject", + /* name */ N_("UVProject"), /* structName */ "UVProjectModifierData", /* structSize */ sizeof(UVProjectModifierData), /* srna */ &RNA_UVProjectModifier, diff --git a/source/blender/modifiers/intern/MOD_uvwarp.c b/source/blender/modifiers/intern/MOD_uvwarp.c index a15efdaa381..c33b25c38e3 100644 --- a/source/blender/modifiers/intern/MOD_uvwarp.c +++ b/source/blender/modifiers/intern/MOD_uvwarp.c @@ -308,7 +308,7 @@ static void panelRegister(ARegionType *region_type) } ModifierTypeInfo modifierType_UVWarp = { - /* name */ "UVWarp", + /* name */ N_("UVWarp"), /* structName */ "UVWarpModifierData", /* structSize */ sizeof(UVWarpModifierData), /* srna */ &RNA_UVWarpModifier, diff --git a/source/blender/modifiers/intern/MOD_volume_displace.cc b/source/blender/modifiers/intern/MOD_volume_displace.cc index 059cfdbdd4e..d9b94d79348 100644 --- a/source/blender/modifiers/intern/MOD_volume_displace.cc +++ b/source/blender/modifiers/intern/MOD_volume_displace.cc @@ -12,6 +12,8 @@ #include "BKE_texture.h" #include "BKE_volume.h" +#include "BLT_translation.h" + #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" #include "DNA_object_types.h" @@ -307,7 +309,7 @@ static void modifyGeometrySet(ModifierData *md, } ModifierTypeInfo modifierType_VolumeDisplace = { - /* name */ "Volume Displace", + /* name */ N_("Volume Displace"), /* structName */ "VolumeDisplaceModifierData", /* structSize */ sizeof(VolumeDisplaceModifierData), /* srna */ &RNA_VolumeDisplaceModifier, diff --git a/source/blender/modifiers/intern/MOD_volume_to_mesh.cc b/source/blender/modifiers/intern/MOD_volume_to_mesh.cc index d33687e4d92..3292f73137a 100644 --- a/source/blender/modifiers/intern/MOD_volume_to_mesh.cc +++ b/source/blender/modifiers/intern/MOD_volume_to_mesh.cc @@ -12,6 +12,8 @@ #include "BKE_volume.h" #include "BKE_volume_to_mesh.hh" +#include "BLT_translation.h" + #include "MOD_modifiertypes.h" #include "MOD_ui_common.h" @@ -193,7 +195,7 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * } ModifierTypeInfo modifierType_VolumeToMesh = { - /* name */ "Volume to Mesh", + /* name */ N_("Volume to Mesh"), /* structName */ "VolumeToMeshModifierData", /* structSize */ sizeof(VolumeToMeshModifierData), /* srna */ &RNA_VolumeToMeshModifier, diff --git a/source/blender/modifiers/intern/MOD_warp.c b/source/blender/modifiers/intern/MOD_warp.c index 9693cf0c0f2..afdc230a877 100644 --- a/source/blender/modifiers/intern/MOD_warp.c +++ b/source/blender/modifiers/intern/MOD_warp.c @@ -510,7 +510,7 @@ static void blendRead(BlendDataReader *reader, ModifierData *md) } ModifierTypeInfo modifierType_Warp = { - /* name */ "Warp", + /* name */ N_("Warp"), /* structName */ "WarpModifierData", /* structSize */ sizeof(WarpModifierData), /* srna */ &RNA_WarpModifier, diff --git a/source/blender/modifiers/intern/MOD_wave.c b/source/blender/modifiers/intern/MOD_wave.c index 4073f028db5..b92e3a0fa9d 100644 --- a/source/blender/modifiers/intern/MOD_wave.c +++ b/source/blender/modifiers/intern/MOD_wave.c @@ -372,7 +372,7 @@ static void panel_draw(const bContext *UNUSED(C), Panel *panel) uiItemR(sub, ptr, "use_normal_z", UI_ITEM_R_TOGGLE, "Z", ICON_NONE); col = uiLayoutColumn(layout, false); - uiItemR(col, ptr, "falloff_radius", 0, "Falloff", ICON_NONE); + uiItemR(col, ptr, "falloff_radius", 0, IFACE_("Falloff"), ICON_NONE); uiItemR(col, ptr, "height", UI_ITEM_R_SLIDER, NULL, ICON_NONE); uiItemR(col, ptr, "width", UI_ITEM_R_SLIDER, NULL, ICON_NONE); uiItemR(col, ptr, "narrowness", UI_ITEM_R_SLIDER, NULL, ICON_NONE); @@ -394,7 +394,7 @@ static void position_panel_draw(const bContext *UNUSED(C), Panel *panel) uiItemR(layout, ptr, "start_position_object", 0, IFACE_("Object"), ICON_NONE); col = uiLayoutColumn(layout, true); - uiItemR(col, ptr, "start_position_x", 0, "Start Position X", ICON_NONE); + uiItemR(col, ptr, "start_position_x", 0, IFACE_("Start Position X"), ICON_NONE); uiItemR(col, ptr, "start_position_y", 0, "Y", ICON_NONE); } @@ -408,9 +408,9 @@ static void time_panel_draw(const bContext *UNUSED(C), Panel *panel) uiLayoutSetPropSep(layout, true); col = uiLayoutColumn(layout, false); - uiItemR(col, ptr, "time_offset", 0, "Offset", ICON_NONE); - uiItemR(col, ptr, "lifetime", 0, "Life", ICON_NONE); - uiItemR(col, ptr, "damping_time", 0, "Damping", ICON_NONE); + uiItemR(col, ptr, "time_offset", 0, IFACE_("Offset"), ICON_NONE); + uiItemR(col, ptr, "lifetime", 0, IFACE_("Life"), ICON_NONE); + uiItemR(col, ptr, "damping_time", 0, IFACE_("Damping"), ICON_NONE); uiItemR(col, ptr, "speed", UI_ITEM_R_SLIDER, NULL, ICON_NONE); } @@ -462,7 +462,7 @@ static void panelRegister(ARegionType *region_type) } ModifierTypeInfo modifierType_Wave = { - /* name */ "Wave", + /* name */ N_("Wave"), /* structName */ "WaveModifierData", /* structSize */ sizeof(WaveModifierData), /* srna */ &RNA_WaveModifier, diff --git a/source/blender/modifiers/intern/MOD_weighted_normal.c b/source/blender/modifiers/intern/MOD_weighted_normal.c index d436acb8ad5..af992c00097 100644 --- a/source/blender/modifiers/intern/MOD_weighted_normal.c +++ b/source/blender/modifiers/intern/MOD_weighted_normal.c @@ -726,7 +726,7 @@ static void panelRegister(ARegionType *region_type) } ModifierTypeInfo modifierType_WeightedNormal = { - /* name */ "WeightedNormal", + /* name */ N_("WeightedNormal"), /* structName */ "WeightedNormalModifierData", /* structSize */ sizeof(WeightedNormalModifierData), /* srna */ &RNA_WeightedNormalModifier, diff --git a/source/blender/modifiers/intern/MOD_weightvgedit.c b/source/blender/modifiers/intern/MOD_weightvgedit.c index e1b43157adb..f6e0cd9303d 100644 --- a/source/blender/modifiers/intern/MOD_weightvgedit.c +++ b/source/blender/modifiers/intern/MOD_weightvgedit.c @@ -397,7 +397,7 @@ static void blendRead(BlendDataReader *reader, ModifierData *md) } ModifierTypeInfo modifierType_WeightVGEdit = { - /* name */ "VertexWeightEdit", + /* name */ N_("VertexWeightEdit"), /* structName */ "WeightVGEditModifierData", /* structSize */ sizeof(WeightVGEditModifierData), /* srna */ &RNA_VertexWeightEditModifier, diff --git a/source/blender/modifiers/intern/MOD_weightvgmix.c b/source/blender/modifiers/intern/MOD_weightvgmix.c index b827d41e80a..49088d42a5e 100644 --- a/source/blender/modifiers/intern/MOD_weightvgmix.c +++ b/source/blender/modifiers/intern/MOD_weightvgmix.c @@ -496,7 +496,7 @@ static void panelRegister(ARegionType *region_type) } ModifierTypeInfo modifierType_WeightVGMix = { - /* name */ "VertexWeightMix", + /* name */ N_("VertexWeightMix"), /* structName */ "WeightVGMixModifierData", /* structSize */ sizeof(WeightVGMixModifierData), /* srna */ &RNA_VertexWeightMixModifier, diff --git a/source/blender/modifiers/intern/MOD_weightvgproximity.c b/source/blender/modifiers/intern/MOD_weightvgproximity.c index 1bea5b93c97..d798bf88bbc 100644 --- a/source/blender/modifiers/intern/MOD_weightvgproximity.c +++ b/source/blender/modifiers/intern/MOD_weightvgproximity.c @@ -737,7 +737,7 @@ static void blendRead(BlendDataReader *reader, ModifierData *md) } ModifierTypeInfo modifierType_WeightVGProximity = { - /* name */ "VertexWeightProximity", + /* name */ N_("VertexWeightProximity"), /* structName */ "WeightVGProximityModifierData", /* structSize */ sizeof(WeightVGProximityModifierData), /* srna */ &RNA_VertexWeightProximityModifier, diff --git a/source/blender/modifiers/intern/MOD_weld.cc b/source/blender/modifiers/intern/MOD_weld.cc index 93d4b56176c..19b0bf62fea 100644 --- a/source/blender/modifiers/intern/MOD_weld.cc +++ b/source/blender/modifiers/intern/MOD_weld.cc @@ -185,7 +185,7 @@ static void panelRegister(ARegionType *region_type) } ModifierTypeInfo modifierType_Weld = { - /* name */ "Weld", + /* name */ N_("Weld"), /* structName */ "WeldModifierData", /* structSize */ sizeof(WeldModifierData), /* srna */ &RNA_WeldModifier, diff --git a/source/blender/modifiers/intern/MOD_wireframe.c b/source/blender/modifiers/intern/MOD_wireframe.c index b657ea87244..5799da5d156 100644 --- a/source/blender/modifiers/intern/MOD_wireframe.c +++ b/source/blender/modifiers/intern/MOD_wireframe.c @@ -165,7 +165,7 @@ static void panelRegister(ARegionType *region_type) } ModifierTypeInfo modifierType_Wireframe = { - /* name */ "Wireframe", + /* name */ N_("Wireframe"), /* structName */ "WireframeModifierData", /* structSize */ sizeof(WireframeModifierData), /* srna */ &RNA_WireframeModifier, -- cgit v1.2.3 From 00dc7477022acdd969e4d709a235c0be819efa6c Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Fri, 15 Jul 2022 22:14:04 +1000 Subject: Fix T99706: Crash rendering with headless builds When rendering with headless builds, show an error instead of crashing. Previously GPU_backend_init was called indirectly from DRW_opengl_context_create, a new function is now called from the window manager (GPU_backend_init_once), so it's possible to check if the GPU has a back-end. This also disables the `bgl` Python module when building WITH_HEADLESS. Reviewed By: fclem Ref D15463 --- CMakeLists.txt | 4 ++++ source/blender/gpu/CMakeLists.txt | 2 +- source/blender/gpu/GPU_context.h | 1 + source/blender/gpu/intern/gpu_context.cc | 17 +++++++++++++---- source/blender/python/generic/CMakeLists.txt | 10 ++++++++-- source/blender/python/intern/bpy_interface.c | 2 ++ source/blender/render/intern/engine.c | 12 ++++++++++++ source/blender/windowmanager/intern/wm_init_exit.c | 4 ++++ 8 files changed, 45 insertions(+), 7 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 33064864be6..c998919622e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -574,6 +574,10 @@ mark_as_advanced( WITH_GPU_BUILDTIME_SHADER_BUILDER ) +if(WITH_HEADLESS) + set(WITH_OPENGL OFF) +endif() + # Metal if (APPLE) diff --git a/source/blender/gpu/CMakeLists.txt b/source/blender/gpu/CMakeLists.txt index 0cb92e02515..774f2a0f312 100644 --- a/source/blender/gpu/CMakeLists.txt +++ b/source/blender/gpu/CMakeLists.txt @@ -5,7 +5,7 @@ # to more easily highlight code-paths in other libraries that need to be refactored, # bf_gpu is allowed to have opengl regardless of this option. -if(NOT WITH_OPENGL AND NOT WITH_METAL_BACKEND) +if(NOT WITH_OPENGL AND NOT WITH_METAL_BACKEND AND NOT WITH_HEADLESS) add_definitions(-DWITH_OPENGL) endif() diff --git a/source/blender/gpu/GPU_context.h b/source/blender/gpu/GPU_context.h index 1fcd94c48fc..b04a4422baa 100644 --- a/source/blender/gpu/GPU_context.h +++ b/source/blender/gpu/GPU_context.h @@ -17,6 +17,7 @@ extern "C" { #endif +bool GPU_backend_init_once(void); void GPU_backend_init(eGPUBackendType backend); void GPU_backend_exit(void); bool GPU_backend_supported(eGPUBackendType type); diff --git a/source/blender/gpu/intern/gpu_context.cc b/source/blender/gpu/intern/gpu_context.cc index 4a0a9ecc7f6..d3b208dc6f6 100644 --- a/source/blender/gpu/intern/gpu_context.cc +++ b/source/blender/gpu/intern/gpu_context.cc @@ -85,10 +85,7 @@ Context *Context::get() GPUContext *GPU_context_create(void *ghost_window) { - if (GPUBackend::get() == nullptr) { - /* TODO: move where it make sense. */ - GPU_backend_init(GPU_BACKEND_OPENGL); - } + GPU_backend_init_once(); Context *ctx = GPUBackend::get()->context_alloc(ghost_window); @@ -214,6 +211,18 @@ bool GPU_backend_supported(eGPUBackendType type) } } +bool GPU_backend_init_once() +{ + if (GPUBackend::get() == nullptr) { + if (!GPU_backend_supported(GPU_BACKEND_OPENGL)) { + return false; + } + /* TODO: move where it make sense. */ + GPU_backend_init(GPU_BACKEND_OPENGL); + } + return true; +} + void GPU_backend_init(eGPUBackendType backend_type) { BLI_assert(g_backend == nullptr); diff --git a/source/blender/python/generic/CMakeLists.txt b/source/blender/python/generic/CMakeLists.txt index 69bcfdfae4e..dfca528e758 100644 --- a/source/blender/python/generic/CMakeLists.txt +++ b/source/blender/python/generic/CMakeLists.txt @@ -17,7 +17,6 @@ set(INC_SYS ) set(SRC - bgl.c bl_math_py_api.c blf_py_api.c bpy_threads.c @@ -27,7 +26,6 @@ set(SRC py_capi_rna.c py_capi_utils.c - bgl.h bl_math_py_api.h blf_py_api.h idprop_py_api.h @@ -40,6 +38,14 @@ set(SRC python_utildefines.h ) +if(WITH_OPENGL) + list(APPEND SRC + bgl.c + + bgl.h + ) +endif() + set(LIB ${GLEW_LIBRARY} ${PYTHON_LINKFLAGS} diff --git a/source/blender/python/intern/bpy_interface.c b/source/blender/python/intern/bpy_interface.c index 939fa475344..08dd5fe9cfc 100644 --- a/source/blender/python/intern/bpy_interface.c +++ b/source/blender/python/intern/bpy_interface.c @@ -259,7 +259,9 @@ static struct _inittab bpy_internal_modules[] = { {"mathutils.kdtree", PyInit_mathutils_kdtree}, #endif {"_bpy_path", BPyInit__bpy_path}, +#ifdef WITH_OPENGL {"bgl", BPyInit_bgl}, +#endif {"blf", BPyInit_blf}, {"bl_math", BPyInit_bl_math}, {"imbuf", BPyInit_imbuf}, diff --git a/source/blender/render/intern/engine.c b/source/blender/render/intern/engine.c index 8a4b4c2a70d..113af393706 100644 --- a/source/blender/render/intern/engine.c +++ b/source/blender/render/intern/engine.c @@ -46,6 +46,8 @@ #include "DRW_engine.h" +#include "GPU_context.h" + #include "pipeline.h" #include "render_result.h" #include "render_types.h" @@ -950,6 +952,16 @@ bool RE_engine_render(Render *re, bool do_all) re->draw_lock(re->dlh, true); } + if ((type->flag & RE_USE_GPU_CONTEXT) && (GPU_backend_get_type() == GPU_BACKEND_NONE)) { + /* Clear UI drawing locks. */ + if (re->draw_lock) { + re->draw_lock(re->dlh, false); + } + BKE_report(re->reports, RPT_ERROR, "Can not initialize the GPU"); + G.is_break = true; + return true; + } + /* update animation here so any render layer animation is applied before * creating the render result */ if ((re->r.scemode & (R_NO_FRAME_UPDATE | R_BUTS_PREVIEW)) == 0) { diff --git a/source/blender/windowmanager/intern/wm_init_exit.c b/source/blender/windowmanager/intern/wm_init_exit.c index 252cfc6e143..b9bb1d88819 100644 --- a/source/blender/windowmanager/intern/wm_init_exit.c +++ b/source/blender/windowmanager/intern/wm_init_exit.c @@ -169,6 +169,10 @@ void WM_init_opengl(void) wm_ghost_init(NULL); } + if (!GPU_backend_init_once()) { + return; + } + /* Needs to be first to have an OpenGL context bound. */ DRW_opengl_context_create(); -- cgit v1.2.3 From 011d3c75a74532920bb90fb1d45ddf2dc836ba8c Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Fri, 15 Jul 2022 15:05:56 +0200 Subject: Cleanup: compiler warning --- intern/cycles/device/device.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/intern/cycles/device/device.h b/intern/cycles/device/device.h index e7916ec3a52..cdb13ca0a97 100644 --- a/intern/cycles/device/device.h +++ b/intern/cycles/device/device.h @@ -188,7 +188,7 @@ class Device { } /* Called after kernel texture setup, and prior to integrator state setup. */ - virtual void optimize_for_scene(Scene *scene) + virtual void optimize_for_scene(Scene * /*scene*/) { } -- cgit v1.2.3 From 82f65d8971ea2def50ab6cd6031793207cc45168 Mon Sep 17 00:00:00 2001 From: Richard Antalik Date: Fri, 15 Jul 2022 15:46:50 +0200 Subject: Cleanup: VSE waveform drawing No functional changes. --- .../editors/space_sequencer/sequencer_draw.c | 522 ++++++++++----------- 1 file changed, 240 insertions(+), 282 deletions(-) diff --git a/source/blender/editors/space_sequencer/sequencer_draw.c b/source/blender/editors/space_sequencer/sequencer_draw.c index aa5681306a4..b1e13850bf2 100644 --- a/source/blender/editors/space_sequencer/sequencer_draw.c +++ b/source/blender/editors/space_sequencer/sequencer_draw.c @@ -241,329 +241,296 @@ typedef struct WaveVizData { float pos[2]; float rms_pos; bool clip; - bool end; + bool draw_line; /* Draw triangle otherwise. */ + bool final_sample; /* There are no more samples. */ } WaveVizData; -static int get_section_len(WaveVizData *start, WaveVizData *end) +static bool seq_draw_waveforms_poll(const bContext *C, SpaceSeq *sseq, Sequence *seq) { - int len = 0; - while (start != end) { - len++; - if (start->end) { - return len; - } - start++; - } - return len; -} + const bool strip_is_valid = seq->type == SEQ_TYPE_SOUND_RAM && seq->sound != NULL; + const bool overlays_enabled = (sseq->flag & SEQ_SHOW_OVERLAY) != 0; + const bool ovelay_option = ((sseq->timeline_overlay.flag & SEQ_TIMELINE_ALL_WAVEFORMS) != 0 || + (seq->flag & SEQ_AUDIO_DRAW_WAVEFORM)); -static void draw_waveform(WaveVizData *iter, WaveVizData *end, GPUPrimType prim_type, bool use_rms) -{ - int strip_len = get_section_len(iter, end); - if (strip_len > 1) { - GPU_blend(GPU_BLEND_ALPHA); - GPUVertFormat *format = immVertexFormat(); - uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); - uint col = GPU_vertformat_attr_add(format, "color", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); + if ((sseq->timeline_overlay.flag & SEQ_TIMELINE_NO_WAVEFORMS) != 0) { + return false; + } - immBindBuiltinProgram(GPU_SHADER_2D_FLAT_COLOR); - immBegin(prim_type, strip_len); + if (strip_is_valid && overlays_enabled && ovelay_option) { + return true; + } - while (iter != end) { - if (iter->clip) { - immAttr4f(col, 1.0f, 0.0f, 0.0f, 0.5f); - } - else if (use_rms) { - immAttr4f(col, 1.0f, 1.0f, 1.0f, 0.8f); - } - else { - immAttr4f(col, 1.0f, 1.0f, 1.0f, 0.5f); - } + return false; +} - if (use_rms) { - immVertex2f(pos, iter->pos[0], iter->rms_pos); - } - else { - immVertex2f(pos, iter->pos[0], iter->pos[1]); - } +static void waveform_job_start_if_needed(const bContext *C, Sequence *seq) +{ + bSound *sound = seq->sound; - if (iter->end) { - /* End of line. */ - iter++; - strip_len = get_section_len(iter, end); - if (strip_len != 0) { - immEnd(); - immUnbindProgram(); - immBindBuiltinProgram(GPU_SHADER_2D_FLAT_COLOR); - immBegin(prim_type, strip_len); - } - } - else { - iter++; - } + BLI_spin_lock(sound->spinlock); + if (!sound->waveform) { + /* Load the waveform data if it hasn't been loaded and cached already. */ + if (!(sound->tags & SOUND_TAGS_WAVEFORM_LOADING)) { + /* Prevent sounds from reloading. */ + sound->tags |= SOUND_TAGS_WAVEFORM_LOADING; + BLI_spin_unlock(sound->spinlock); + sequencer_preview_add_sound(C, seq); + } + else { + BLI_spin_unlock(sound->spinlock); } - immEnd(); - immUnbindProgram(); - - GPU_blend(GPU_BLEND_NONE); } + BLI_spin_unlock(sound->spinlock); } -static float clamp_frame_coord_to_pixel(float frame_coord, - float pixel_frac, - float frames_per_pixel) +static size_t get_vertex_count(WaveVizData *waveform_data) { - float cur_pixel = (frame_coord / frames_per_pixel); - float new_pixel = (int)(frame_coord / frames_per_pixel) + pixel_frac; - if (cur_pixel > new_pixel) { - new_pixel += 1.0f; + bool draw_line = waveform_data->draw_line; + size_t length = 0; + + while (waveform_data->draw_line == draw_line && !waveform_data->final_sample) { + waveform_data++; + length++; } - return new_pixel * frames_per_pixel; + + return length; } -/** - * \param x1, x2, y1, y2: The starting and end X value to draw the wave, same for y1 and y2. - * \param frames_per_pixel: The amount of pixels a whole frame takes up (x-axis direction). - */ -static void draw_seq_waveform_overlay(View2D *v2d, - const bContext *C, - SpaceSeq *sseq, - Scene *scene, - Sequence *seq, - float x1, - float y1, - float x2, - float y2, - float frames_per_pixel) +static size_t draw_waveform_segment(WaveVizData *waveform_data, bool use_rms) { - if (seq->sound && ((sseq->timeline_overlay.flag & SEQ_TIMELINE_ALL_WAVEFORMS) || - (seq->flag & SEQ_AUDIO_DRAW_WAVEFORM))) { - /* Make sure that the start drawing position is aligned to the pixels on the screen to avoid - * flickering when moving around the strip. - * To do this we figure out the fractional offset in pixel space by checking where the - * window starts. - * We then append this pixel offset to our strip start coordinate to ensure we are aligned to - * the screen pixel grid. */ - float pixel_frac = v2d->cur.xmin / frames_per_pixel - floor(v2d->cur.xmin / frames_per_pixel); - float x1_adj = clamp_frame_coord_to_pixel(x1, pixel_frac, frames_per_pixel); - - /* Offset x1 and x2 values, to match view min/max, if strip is out of bounds. */ - float x1_offset = max_ff(v2d->cur.xmin, x1_adj); - float x2_offset = min_ff(v2d->cur.xmax, x2); - - /* Calculate how long the strip that is in view is in pixels. */ - int pix_strip_len = round((x2_offset - x1_offset) / frames_per_pixel); - - if (pix_strip_len < 2) { - return; - } + size_t vertices_done = 0; + size_t vertex_count = get_vertex_count(waveform_data); - bSound *sound = seq->sound; + /* Not enough data to draw. */ + if (vertex_count <= 2) { + return vertex_count; + } - BLI_spin_lock(sound->spinlock); - if (!sound->waveform) { - /* Load the waveform data if it hasn't been loaded and cached already. */ - if (!(sound->tags & SOUND_TAGS_WAVEFORM_LOADING)) { - /* Prevent sounds from reloading. */ - sound->tags |= SOUND_TAGS_WAVEFORM_LOADING; - BLI_spin_unlock(sound->spinlock); - sequencer_preview_add_sound(C, seq); - } - else { - BLI_spin_unlock(sound->spinlock); - } - return; /* Nothing to draw. */ - } - BLI_spin_unlock(sound->spinlock); + GPU_blend(GPU_BLEND_ALPHA); + GPUVertFormat *format = immVertexFormat(); + GPUPrimType prim_type = waveform_data->draw_line ? GPU_PRIM_LINE_STRIP : GPU_PRIM_TRI_STRIP; + uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + uint col = GPU_vertformat_attr_add(format, "color", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); + immBindBuiltinProgram(GPU_SHADER_2D_FLAT_COLOR); + immBegin(prim_type, vertex_count); - SoundWaveform *waveform = sound->waveform; + while (vertices_done < vertex_count && !waveform_data->final_sample) { + /* Color. */ + if (waveform_data->clip) { + immAttr4f(col, 1.0f, 0.0f, 0.0f, 0.5f); + } + else if (use_rms) { + immAttr4f(col, 1.0f, 1.0f, 1.0f, 0.8f); + } + else { + immAttr4f(col, 1.0f, 1.0f, 1.0f, 0.5f); + } - /* Waveform could not be built. */ - if (waveform->length == 0) { - return; + /* Vertices. */ + if (use_rms) { + immVertex2f(pos, waveform_data->pos[0], waveform_data->rms_pos); + } + else { + immVertex2f(pos, waveform_data->pos[0], waveform_data->pos[1]); } - /* F-Curve lookup is quite expensive, so do this after precondition. */ - FCurve *fcu = id_data_find_fcurve(&scene->id, seq, &RNA_Sequence, "volume", 0, NULL); + vertices_done++; + waveform_data++; + } - WaveVizData *tri_strip_arr = MEM_callocN(sizeof(*tri_strip_arr) * pix_strip_len * 2, - "tri_strip"); - WaveVizData *line_strip_arr = MEM_callocN(sizeof(*line_strip_arr) * pix_strip_len, - "line_strip"); + immEnd(); + immUnbindProgram(); - WaveVizData *tri_strip_iter = tri_strip_arr; - WaveVizData *line_strip_iter = line_strip_arr; + GPU_blend(GPU_BLEND_NONE); - /* The y coordinate for the middle of the strip. */ - float y_mid = (y1 + y2) / 2.0f; - /* The length from the middle of the strip to the top/bottom. */ - float y_scale = (y2 - y1) / 2.0f; - float volume = seq->volume; + return vertices_done; +} - /* Value to keep track if the previous item to be drawn was a line strip. */ - int8_t was_line_strip = -1; /* -1 == no previous value. */ +static void draw_waveform(WaveVizData *waveform_data, size_t wave_data_len) +{ + size_t items_done = 0; + while (items_done < wave_data_len) { + if (!waveform_data[items_done].draw_line) { /* Draw RMS. */ + draw_waveform_segment(&waveform_data[items_done], true); + } + items_done += draw_waveform_segment(&waveform_data[items_done], false); + } +} - float samples_per_frame = SOUND_WAVE_SAMPLES_PER_SECOND / FPS; +static float align_frame_with_pixel(const View2D *v2d, float frame_coord, float frames_per_pixel) +{ + return round_fl_to_int(frame_coord / frames_per_pixel) * frames_per_pixel; +} - /* How many samples do we have for each pixel? */ - float samples_per_pix = samples_per_frame * frames_per_pixel; +static void write_waveform_data(WaveVizData *waveform_data, + const vec2f pos, + const float rms, + const bool is_clipping, + const bool draw_line) +{ + waveform_data->pos[0] = pos.x; + waveform_data->pos[1] = pos.y; + waveform_data->clip = is_clipping; + waveform_data->rms_pos = rms; + waveform_data->draw_line = draw_line; +} - float strip_start_offset = seq->startofs + seq->anim_startofs; - float start_sample = 0; +static size_t waveform_append_sample(WaveVizData *waveform_data, + vec2f pos, + const float value_min, + const float value_max, + const float y_mid, + const float y_scale, + const float rms, + const bool is_clipping, + const bool is_line_strip) +{ + size_t data_written = 0; + pos.y = y_mid + value_min * y_scale; + float rms_value = y_mid + max_ff(-rms, value_min) * y_scale; + write_waveform_data(&waveform_data[0], pos, rms_value, is_clipping, is_line_strip); + data_written++; + + /* Use `value_max` as second vertex for triangle drawing. */ + if (!is_line_strip) { + pos.y = y_mid + value_max * y_scale; + rms_value = y_mid + min_ff(rms, value_max) * y_scale; + write_waveform_data(&waveform_data[1], pos, rms_value, is_clipping, is_line_strip); + data_written++; + } + return data_written; +} - if (strip_start_offset != 0) { - /* If start offset is not zero, we need to make sure that we pick the same start sample as if - * we simply scrolled the start of the strip off-screen. Otherwise we will get flickering - * when changing start offset as the pixel alignment will not be the same for the drawn - * samples. */ - strip_start_offset = clamp_frame_coord_to_pixel( - x1 - strip_start_offset, pixel_frac, frames_per_pixel); - start_sample = fabsf(strip_start_offset - x1_adj) * samples_per_frame; - } +/** + * \param x1, x2, y1, y2: The starting and end X value to draw the wave, same for y1 and y2. + * \param frames_per_pixel: The amount of pixels a whole frame takes up (x-axis direction). + */ +static void draw_seq_waveform_overlay( + const bContext *C, ARegion *region, Sequence *seq, float x1, float y1, float x2, float y2) +{ + const View2D *v2d = ®ion->v2d; + Scene *scene = CTX_data_scene(C); - start_sample += seq->sound->offset_time * SOUND_WAVE_SAMPLES_PER_SECOND; - /* If we scrolled the start off-screen, then the start sample should be at the first visible - * sample. */ - start_sample += (x1_offset - x1_adj) * samples_per_frame; + const float frames_per_pixel = BLI_rctf_size_x(®ion->v2d.cur) / region->winx; + const float samples_per_frame = SOUND_WAVE_SAMPLES_PER_SECOND / FPS; + float samples_per_pixel = samples_per_frame * frames_per_pixel; - for (int i = 0; i < pix_strip_len; i++) { - float sample_offset = start_sample + i * samples_per_pix; - int p = sample_offset; + /* Align strip start with nearest pixel to prevent waveform flickering. */ + const float x1_aligned = align_frame_with_pixel(v2d, x1, frames_per_pixel); + /* Offset x1 and x2 values, to match view min/max, if strip is out of bounds. */ + const float frame_start = max_ff(v2d->cur.xmin, x1_aligned); + const float frame_end = min_ff(v2d->cur.xmax, x2); + const int pixels_to_draw = round_fl_to_int((frame_end - frame_start) / frames_per_pixel); - if (p < 0) { - continue; - } + if (pixels_to_draw < 2) { + return; /* Not much to draw, exit before running job. */ + } - if (p >= waveform->length) { - break; - } + waveform_job_start_if_needed(C, seq); - float value_min = waveform->data[p * 3]; - float value_max = waveform->data[p * 3 + 1]; - float rms = waveform->data[p * 3 + 2]; - - if (p + 1 < waveform->length) { - /* Use simple linear interpolation. */ - float f = sample_offset - p; - value_min = (1.0f - f) * value_min + f * waveform->data[p * 3 + 3]; - value_max = (1.0f - f) * value_max + f * waveform->data[p * 3 + 4]; - rms = (1.0f - f) * rms + f * waveform->data[p * 3 + 5]; - if (samples_per_pix > 1.0f) { - /* We need to sum up the values we skip over until the next step. */ - float next_pos = sample_offset + samples_per_pix; - int end_idx = next_pos; - - for (int j = p + 1; (j < waveform->length) && (j < end_idx); j++) { - value_min = min_ff(value_min, waveform->data[j * 3]); - value_max = max_ff(value_max, waveform->data[j * 3 + 1]); - rms = max_ff(rms, waveform->data[j * 3 + 2]); - } - } - } + SoundWaveform *waveform = seq->sound->waveform; + if (waveform == NULL || waveform->length == 0) { + return; /* Waveform was not built. */ + } - if (fcu && !BKE_fcurve_is_empty(fcu)) { - float evaltime = x1_offset + (i * frames_per_pixel); - volume = evaluate_fcurve(fcu, evaltime); - CLAMP_MIN(volume, 0.0f); - } + /* F-Curve lookup is quite expensive, so do this after precondition. */ + FCurve *fcu = id_data_find_fcurve(&scene->id, seq, &RNA_Sequence, "volume", 0, NULL); + WaveVizData *waveform_data = MEM_callocN(sizeof(WaveVizData) * pixels_to_draw * 3, __func__); + size_t wave_data_len = 0; - value_min *= volume; - value_max *= volume; - rms *= volume; + /* Offset must be also aligned, otherwise waveform flickers when moving left handle. */ + const float strip_offset = align_frame_with_pixel( + v2d, seq->startofs + seq->anim_startofs, frames_per_pixel); + float start_sample = strip_offset * samples_per_frame; + start_sample += seq->sound->offset_time * SOUND_WAVE_SAMPLES_PER_SECOND; + /* Add off-screen part of strip to offset. */ + start_sample += (frame_start - x1_aligned) * samples_per_frame; - bool clipping = false; + for (int i = 0; i < pixels_to_draw; i++) { + float sample = start_sample + i * samples_per_pixel; + int sample_index = round_fl_to_int(sample); - if (value_max > 1 || value_min < -1) { - clipping = true; + if (sample_index < 0) { + continue; + } - CLAMP_MAX(value_max, 1.0f); - CLAMP_MIN(value_min, -1.0f); - } + if (sample_index >= waveform->length) { + break; + } - bool is_line_strip = (value_max - value_min < 0.05f); - - if (!ELEM(was_line_strip, -1, is_line_strip)) { - /* If the previously added strip type isn't the same as the current one, - * add transition areas so they transition smoothly between each other. */ - if (is_line_strip) { - /* This will be a line strip, end the tri strip. */ - tri_strip_iter->pos[0] = x1_offset + i * frames_per_pixel; - tri_strip_iter->pos[1] = y_mid + value_min * y_scale; - tri_strip_iter->clip = clipping; - tri_strip_iter->rms_pos = tri_strip_iter->pos[1]; - tri_strip_iter->end = true; - - /* End of section. */ - tri_strip_iter++; - - /* Check if we are at the end. - * If so, skip one point line. */ - if (i + 1 == pix_strip_len) { - continue; - } - } - else { - /* This will be a tri strip. */ - line_strip_iter--; - tri_strip_iter->pos[0] = line_strip_iter->pos[0]; - tri_strip_iter->pos[1] = line_strip_iter->pos[1]; - tri_strip_iter->clip = line_strip_iter->clip; - tri_strip_iter->rms_pos = line_strip_iter->pos[1]; - tri_strip_iter++; - - /* Check if line had only one point. */ - line_strip_iter--; - if (line_strip_iter < line_strip_arr || line_strip_iter->end) { - /* Only one point, skip it. */ - line_strip_iter++; - } - else { - /* End of section. */ - line_strip_iter++; - line_strip_iter->end = true; - line_strip_iter++; - } + float value_min = waveform->data[sample_index * 3]; + float value_max = waveform->data[sample_index * 3 + 1]; + float rms = waveform->data[sample_index * 3 + 2]; + + if (sample_index + 1 < waveform->length) { + /* Use simple linear interpolation. */ + float f = sample - sample_index; + value_min = (1.0f - f) * value_min + f * waveform->data[sample_index * 3 + 3]; + value_max = (1.0f - f) * value_max + f * waveform->data[sample_index * 3 + 4]; + rms = (1.0f - f) * rms + f * waveform->data[sample_index * 3 + 5]; + if (samples_per_pixel > 1.0f) { + /* We need to sum up the values we skip over until the next step. */ + float next_pos = sample + samples_per_pixel; + int end_idx = next_pos; + + for (int j = sample_index + 1; (j < waveform->length) && (j < end_idx); j++) { + value_min = min_ff(value_min, waveform->data[j * 3]); + value_max = max_ff(value_max, waveform->data[j * 3 + 1]); + rms = max_ff(rms, waveform->data[j * 3 + 2]); } } + } - was_line_strip = is_line_strip; - - if (is_line_strip) { - line_strip_iter->pos[0] = x1_offset + i * frames_per_pixel; - line_strip_iter->pos[1] = y_mid + value_min * y_scale; - line_strip_iter->clip = clipping; - line_strip_iter++; - } - else { - tri_strip_iter->pos[0] = x1_offset + i * frames_per_pixel; - tri_strip_iter->pos[1] = y_mid + value_min * y_scale; - tri_strip_iter->clip = clipping; - tri_strip_iter->rms_pos = y_mid + max_ff(-rms, value_min) * y_scale; - tri_strip_iter++; - - tri_strip_iter->pos[0] = x1_offset + i * frames_per_pixel; - tri_strip_iter->pos[1] = y_mid + value_max * y_scale; - tri_strip_iter->clip = clipping; - tri_strip_iter->rms_pos = y_mid + min_ff(rms, value_max) * y_scale; - tri_strip_iter++; - } + float volume = seq->volume; + if (fcu && !BKE_fcurve_is_empty(fcu)) { + float evaltime = frame_start + (i * frames_per_pixel); + volume = evaluate_fcurve(fcu, evaltime); + CLAMP_MIN(volume, 0.0f); } - WaveVizData *tri_strip_end = tri_strip_iter; - WaveVizData *line_strip_end = line_strip_iter; + value_min *= volume; + value_max *= volume; + rms *= volume; + + bool is_clipping = false; + + if (value_max > 1 || value_min < -1) { + is_clipping = true; - tri_strip_iter = tri_strip_arr; - line_strip_iter = line_strip_arr; + CLAMP_MAX(value_max, 1.0f); + CLAMP_MIN(value_min, -1.0f); + } - draw_waveform(line_strip_iter, line_strip_end, GPU_PRIM_LINE_STRIP, false); - draw_waveform(tri_strip_iter, tri_strip_end, GPU_PRIM_TRI_STRIP, false); - draw_waveform(tri_strip_iter, tri_strip_end, GPU_PRIM_TRI_STRIP, true); + bool is_line_strip = (value_max - value_min < 0.05f); + /* The y coordinate for the middle of the strip. */ + float y_mid = (y1 + y2) / 2.0f; + /* The length from the middle of the strip to the top/bottom. */ + float y_scale = (y2 - y1) / 2.0f; - MEM_freeN(tri_strip_arr); - MEM_freeN(line_strip_arr); + vec2f pos = {frame_start + i * frames_per_pixel, y_mid + value_min * y_scale}; + WaveVizData *new_data = &waveform_data[wave_data_len]; + wave_data_len += waveform_append_sample( + new_data, pos, value_min, value_max, y_mid, y_scale, rms, is_clipping, is_line_strip); } + + /* Terminate array, so `get_segment_length()` can know when to stop. */ + waveform_data[wave_data_len].final_sample = true; + draw_waveform(waveform_data, wave_data_len); + MEM_freeN(waveform_data); } +/* +static size_t *waveform_append(WaveVizData *waveform_data, + vec2f pos, + const float value_min, + const float value_max, + const float y_mid, + const float y_scale, + const float rms, + const bool is_clipping, + const bool is_line_strip) +*/ + static void drawmeta_contents(Scene *scene, Sequence *seqm, float x1, @@ -1430,18 +1397,9 @@ static void draw_seq_strip(const bContext *C, } /* Draw sound strip waveform. */ - if ((seq->type == SEQ_TYPE_SOUND_RAM) && ((sseq->flag & SEQ_SHOW_OVERLAY)) && - (sseq->timeline_overlay.flag & SEQ_TIMELINE_NO_WAVEFORMS) == 0) { - draw_seq_waveform_overlay(v2d, - C, - sseq, - scene, - seq, - x1, - y_threshold ? y1 + 0.05f : y1, - x2, - y_threshold ? text_margin_y : y2, - BLI_rctf_size_x(®ion->v2d.cur) / region->winx); + if (seq_draw_waveforms_poll(C, sseq, seq)) { + draw_seq_waveform_overlay( + C, region, seq, x1, y_threshold ? y1 + 0.05f : y1, x2, y_threshold ? text_margin_y : y2); } /* Draw locked state. */ if (SEQ_transform_is_locked(channels, seq)) { -- cgit v1.2.3 From 5e1229f25387a90fe626b4b2ac34f2eb5c7dc23a Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Fri, 15 Jul 2022 15:54:14 +0200 Subject: Fix overly noisy surface deform warning message An increased number of vertices is not a stopper for the surface deform modifier anymore. It might still be useful to expose the message in the UI, but printing error message to the console on every modifier evaluation makes real errors to become almost invisible. Differential Revision: https://developer.blender.org/D15468 --- source/blender/blenkernel/BKE_modifier.h | 12 ++++++++ source/blender/blenkernel/intern/modifier.c | 34 ++++++++++++++++++++++ .../blender/modifiers/intern/MOD_surfacedeform.c | 10 +++---- 3 files changed, 51 insertions(+), 5 deletions(-) diff --git a/source/blender/blenkernel/BKE_modifier.h b/source/blender/blenkernel/BKE_modifier.h index 866b0353d07..46d609f9aa3 100644 --- a/source/blender/blenkernel/BKE_modifier.h +++ b/source/blender/blenkernel/BKE_modifier.h @@ -446,10 +446,22 @@ bool BKE_modifier_is_enabled(const struct Scene *scene, */ bool BKE_modifier_is_nonlocal_in_liboverride(const struct Object *ob, const struct ModifierData *md); + +/* Set modifier execution error. + * The message will be shown in the interface and will be logged as an error to the console. */ void BKE_modifier_set_error(const struct Object *ob, struct ModifierData *md, const char *format, ...) ATTR_PRINTF_FORMAT(3, 4); + +/* Set modifier execution warning, which does not prevent the modifier from being applied but which + * might need an attention. The message will only be shown in the interface, but will not appear in + * the logs. */ +void BKE_modifier_set_warning(const struct Object *ob, + struct ModifierData *md, + const char *format, + ...) ATTR_PRINTF_FORMAT(3, 4); + bool BKE_modifier_is_preview(struct ModifierData *md); void BKE_modifiers_foreach_ID_link(struct Object *ob, IDWalkFunc walk, void *userData); diff --git a/source/blender/blenkernel/intern/modifier.c b/source/blender/blenkernel/intern/modifier.c index 6348d83362e..831ea084961 100644 --- a/source/blender/blenkernel/intern/modifier.c +++ b/source/blender/blenkernel/intern/modifier.c @@ -456,6 +456,40 @@ void BKE_modifier_set_error(const Object *ob, ModifierData *md, const char *_for CLOG_ERROR(&LOG, "Object: \"%s\", Modifier: \"%s\", %s", ob->id.name + 2, md->name, md->error); } +void BKE_modifier_set_warning(const struct Object *ob, + struct ModifierData *md, + const char *_format, + ...) +{ + char buffer[512]; + va_list ap; + const char *format = TIP_(_format); + + va_start(ap, _format); + vsnprintf(buffer, sizeof(buffer), format, ap); + va_end(ap); + buffer[sizeof(buffer) - 1] = '\0'; + + /* Store the warning in the same field as the error. + * It is not expected to have both error and warning and having a single place to store the + * message simplifies interface code. */ + + if (md->error) { + MEM_freeN(md->error); + } + + md->error = BLI_strdup(buffer); + +#ifndef NDEBUG + if ((md->mode & eModifierMode_Virtual) == 0) { + /* Ensure correct object is passed in. */ + BLI_assert(BKE_modifier_get_original(ob, md) != NULL); + } +#endif + + UNUSED_VARS_NDEBUG(ob); +} + int BKE_modifiers_get_cage_index(const Scene *scene, Object *ob, int *r_lastPossibleCageIndex, diff --git a/source/blender/modifiers/intern/MOD_surfacedeform.c b/source/blender/modifiers/intern/MOD_surfacedeform.c index d63ef12285b..edc6819a26c 100644 --- a/source/blender/modifiers/intern/MOD_surfacedeform.c +++ b/source/blender/modifiers/intern/MOD_surfacedeform.c @@ -1521,11 +1521,11 @@ static void surfacedeformModifier_do(ModifierData *md, * added after the original ones. This covers typical case when target was at the subdivision * level 0 and then subdivision was increased (i.e. for the render purposes). */ - BKE_modifier_set_error(ob, - md, - "Target vertices changed from %u to %u, continuing anyway", - smd->target_verts_num, - target_verts_num); + BKE_modifier_set_warning(ob, + md, + "Target vertices changed from %u to %u, continuing anyway", + smd->target_verts_num, + target_verts_num); /* In theory we only need the `smd->verts_num` vertices in the `targetCos` for evaluation, but * it is not currently possible to request a subset of coordinates: the API expects that the -- cgit v1.2.3 From 1cf465bbc3312ae8eac3e1ae573b716e0fad92cf Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Fri, 15 Jul 2022 12:44:35 +0200 Subject: Fix GPU backend deleting resources without an active context This causes an assert with libepoxy, but was wrong already regardless. Refactor logic to work as follows: * GPU_exit() deletes backend resources * Destroy UI GPU resources with the context active * Call GPU_backend_exit() after deleting the context Ref D15291 Differential Revision: https://developer.blender.org/D15465 --- source/blender/gpu/GPU_context.h | 5 +++- source/blender/gpu/intern/gpu_backend.hh | 1 + source/blender/gpu/intern/gpu_context.cc | 33 +++++++++++++--------- source/blender/gpu/intern/gpu_init_exit.c | 2 ++ source/blender/gpu/intern/gpu_private.h | 4 +++ source/blender/gpu/metal/mtl_backend.hh | 5 ++++ source/blender/gpu/opengl/gl_backend.hh | 8 ++++-- source/blender/gpu/tests/gpu_testing.cc | 3 +- source/blender/windowmanager/intern/wm_init_exit.c | 26 +++++++++-------- 9 files changed, 58 insertions(+), 29 deletions(-) diff --git a/source/blender/gpu/GPU_context.h b/source/blender/gpu/GPU_context.h index b04a4422baa..c81296093a1 100644 --- a/source/blender/gpu/GPU_context.h +++ b/source/blender/gpu/GPU_context.h @@ -17,8 +17,11 @@ extern "C" { #endif +/* GPU backends abstract the differences between different APIs. These must be + * initialized before creating contexts, and deleted after the last context is + * discarded. GPU_context_create automatically initializes a backend if none + * exists yet. */ bool GPU_backend_init_once(void); -void GPU_backend_init(eGPUBackendType backend); void GPU_backend_exit(void); bool GPU_backend_supported(eGPUBackendType type); diff --git a/source/blender/gpu/intern/gpu_backend.hh b/source/blender/gpu/intern/gpu_backend.hh index 6e07e6c3229..d2890efee72 100644 --- a/source/blender/gpu/intern/gpu_backend.hh +++ b/source/blender/gpu/intern/gpu_backend.hh @@ -30,6 +30,7 @@ class VertBuf; class GPUBackend { public: virtual ~GPUBackend() = default; + virtual void delete_resources() = 0; static GPUBackend *get(); diff --git a/source/blender/gpu/intern/gpu_context.cc b/source/blender/gpu/intern/gpu_context.cc index d3b208dc6f6..9b0670da8cb 100644 --- a/source/blender/gpu/intern/gpu_context.cc +++ b/source/blender/gpu/intern/gpu_context.cc @@ -27,6 +27,7 @@ #include "gpu_batch_private.hh" #include "gpu_context_private.hh" #include "gpu_matrix_private.h" +#include "gpu_private.h" #ifdef WITH_OPENGL_BACKEND # include "gl_backend.hh" @@ -213,20 +214,17 @@ bool GPU_backend_supported(eGPUBackendType type) bool GPU_backend_init_once() { - if (GPUBackend::get() == nullptr) { - if (!GPU_backend_supported(GPU_BACKEND_OPENGL)) { - return false; - } - /* TODO: move where it make sense. */ - GPU_backend_init(GPU_BACKEND_OPENGL); + if (GPUBackend::get() != nullptr) { + return true; } - return true; -} -void GPU_backend_init(eGPUBackendType backend_type) -{ - BLI_assert(g_backend == nullptr); - BLI_assert(GPU_backend_supported(backend_type)); + const eGPUBackendType backend_type = GPU_BACKEND_OPENGL; + if (!GPU_backend_supported(backend_type)) { + return false; + } + + static std::mutex backend_init_mutex; + std::scoped_lock lock(backend_init_mutex); switch (backend_type) { #ifdef WITH_OPENGL_BACKEND @@ -243,12 +241,19 @@ void GPU_backend_init(eGPUBackendType backend_type) BLI_assert(0); break; } + + return true; +} + +void gpu_backend_delete_resources() +{ + BLI_assert(backend); + g_backend->delete_resources(); } void GPU_backend_exit() { - /* TODO: assert no resource left. Currently UI textures are still not freed in their context - * correctly. */ + /* TODO: assert no resource left. */ delete g_backend; g_backend = nullptr; } diff --git a/source/blender/gpu/intern/gpu_init_exit.c b/source/blender/gpu/intern/gpu_init_exit.c index 062614fb5cb..34b355eefaf 100644 --- a/source/blender/gpu/intern/gpu_init_exit.c +++ b/source/blender/gpu/intern/gpu_init_exit.c @@ -55,6 +55,8 @@ void GPU_exit(void) gpu_shader_dependency_exit(); gpu_shader_create_info_exit(); + gpu_backend_delete_resources(); + initialized = false; } diff --git a/source/blender/gpu/intern/gpu_private.h b/source/blender/gpu/intern/gpu_private.h index a8ee5187d98..0e293302086 100644 --- a/source/blender/gpu/intern/gpu_private.h +++ b/source/blender/gpu/intern/gpu_private.h @@ -10,6 +10,10 @@ extern "C" { #endif +/* gpu_backend.cc */ + +void gpu_backend_delete_resources(void); + /* gpu_pbvh.c */ void gpu_pbvh_init(void); diff --git a/source/blender/gpu/metal/mtl_backend.hh b/source/blender/gpu/metal/mtl_backend.hh index 7228a5f7596..3e09408e43e 100644 --- a/source/blender/gpu/metal/mtl_backend.hh +++ b/source/blender/gpu/metal/mtl_backend.hh @@ -40,6 +40,11 @@ class MTLBackend : public GPUBackend { MTLBackend::platform_exit(); } + void delete_resources() + { + /* Delete any resources with context active. */ + } + static bool metal_is_supported(); static MTLBackend *get() { diff --git a/source/blender/gpu/opengl/gl_backend.hh b/source/blender/gpu/opengl/gl_backend.hh index 29249111294..e425b87afe8 100644 --- a/source/blender/gpu/opengl/gl_backend.hh +++ b/source/blender/gpu/opengl/gl_backend.hh @@ -42,11 +42,15 @@ class GLBackend : public GPUBackend { } ~GLBackend() { - GLTexture::samplers_free(); - GLBackend::platform_exit(); } + void delete_resources() override + { + /* Delete any resources with context active. */ + GLTexture::samplers_free(); + } + static GLBackend *get() { return static_cast(GPUBackend::get()); diff --git a/source/blender/gpu/tests/gpu_testing.cc b/source/blender/gpu/tests/gpu_testing.cc index 4e93e062b50..5a2ad893360 100644 --- a/source/blender/gpu/tests/gpu_testing.cc +++ b/source/blender/gpu/tests/gpu_testing.cc @@ -17,6 +17,7 @@ void GPUTest::SetUp() GHOST_GLSettings glSettings = {0}; CLG_init(); ghost_system = GHOST_CreateSystem(); + GPU_backend_init_once(); ghost_context = GHOST_CreateOpenGLContext(ghost_system, glSettings); GHOST_ActivateOpenGLContext(ghost_context); context = GPU_context_create(nullptr); @@ -26,9 +27,9 @@ void GPUTest::SetUp() void GPUTest::TearDown() { GPU_exit(); - GPU_backend_exit(); GPU_context_discard(context); GHOST_DisposeOpenGLContext(ghost_system, ghost_context); + GPU_backend_exit(); GHOST_DisposeSystem(ghost_system); CLG_exit(); } diff --git a/source/blender/windowmanager/intern/wm_init_exit.c b/source/blender/windowmanager/intern/wm_init_exit.c index b9bb1d88819..7324abfd096 100644 --- a/source/blender/windowmanager/intern/wm_init_exit.c +++ b/source/blender/windowmanager/intern/wm_init_exit.c @@ -576,14 +576,6 @@ void WM_exit_ex(bContext *C, const bool do_python) BLF_exit(); - if (opengl_is_init) { - DRW_opengl_context_enable_ex(false); - GPU_pass_cache_free(); - GPU_exit(); - DRW_opengl_context_disable_ex(false); - DRW_opengl_context_destroy(); - } - BLT_lang_free(); ANIM_keyingset_infos_exit(); @@ -608,13 +600,25 @@ void WM_exit_ex(bContext *C, const bool do_python) ED_file_exit(); /* for fsmenu */ - UI_exit(); + /* Delete GPU resources and context. The UI also uses GPU resources and so + * is also deleted with the context active. */ + if (opengl_is_init) { + DRW_opengl_context_enable_ex(false); + UI_exit(); + GPU_pass_cache_free(); + GPU_exit(); + DRW_opengl_context_disable_ex(false); + DRW_opengl_context_destroy(); + } + else { + UI_exit(); + } + GPU_backend_exit(); + BKE_blender_userdef_data_free(&U, false); RNA_exit(); /* should be after BPY_python_end so struct python slots are cleared */ - GPU_backend_exit(); - wm_ghost_exit(); CTX_free(C); -- cgit v1.2.3 From c505f19efe3f476e1164b455817ad275fa913454 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Fri, 15 Jul 2022 16:52:01 +0200 Subject: Fix compiler error in debug builds after 1cf465bbc331 --- source/blender/gpu/intern/gpu_context.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/gpu/intern/gpu_context.cc b/source/blender/gpu/intern/gpu_context.cc index 9b0670da8cb..5ae020e45a4 100644 --- a/source/blender/gpu/intern/gpu_context.cc +++ b/source/blender/gpu/intern/gpu_context.cc @@ -247,7 +247,7 @@ bool GPU_backend_init_once() void gpu_backend_delete_resources() { - BLI_assert(backend); + BLI_assert(g_backend); g_backend->delete_resources(); } -- cgit v1.2.3 From 03aeef64d5c2bd1eb1aa790bddbce728857778f3 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Fri, 15 Jul 2022 16:47:07 +0200 Subject: Cleanup: compiler warnings --- source/blender/editors/space_sequencer/sequencer_draw.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/source/blender/editors/space_sequencer/sequencer_draw.c b/source/blender/editors/space_sequencer/sequencer_draw.c index b1e13850bf2..eb2e4ef05e5 100644 --- a/source/blender/editors/space_sequencer/sequencer_draw.c +++ b/source/blender/editors/space_sequencer/sequencer_draw.c @@ -245,7 +245,7 @@ typedef struct WaveVizData { bool final_sample; /* There are no more samples. */ } WaveVizData; -static bool seq_draw_waveforms_poll(const bContext *C, SpaceSeq *sseq, Sequence *seq) +static bool seq_draw_waveforms_poll(const bContext *UNUSED(C), SpaceSeq *sseq, Sequence *seq) { const bool strip_is_valid = seq->type == SEQ_TYPE_SOUND_RAM && seq->sound != NULL; const bool overlays_enabled = (sseq->flag & SEQ_SHOW_OVERLAY) != 0; @@ -357,7 +357,7 @@ static void draw_waveform(WaveVizData *waveform_data, size_t wave_data_len) } } -static float align_frame_with_pixel(const View2D *v2d, float frame_coord, float frames_per_pixel) +static float align_frame_with_pixel(float frame_coord, float frames_per_pixel) { return round_fl_to_int(frame_coord / frames_per_pixel) * frames_per_pixel; } @@ -416,7 +416,7 @@ static void draw_seq_waveform_overlay( float samples_per_pixel = samples_per_frame * frames_per_pixel; /* Align strip start with nearest pixel to prevent waveform flickering. */ - const float x1_aligned = align_frame_with_pixel(v2d, x1, frames_per_pixel); + const float x1_aligned = align_frame_with_pixel(x1, frames_per_pixel); /* Offset x1 and x2 values, to match view min/max, if strip is out of bounds. */ const float frame_start = max_ff(v2d->cur.xmin, x1_aligned); const float frame_end = min_ff(v2d->cur.xmax, x2); @@ -439,8 +439,8 @@ static void draw_seq_waveform_overlay( size_t wave_data_len = 0; /* Offset must be also aligned, otherwise waveform flickers when moving left handle. */ - const float strip_offset = align_frame_with_pixel( - v2d, seq->startofs + seq->anim_startofs, frames_per_pixel); + const float strip_offset = align_frame_with_pixel(seq->startofs + seq->anim_startofs, + frames_per_pixel); float start_sample = strip_offset * samples_per_frame; start_sample += seq->sound->offset_time * SOUND_WAVE_SAMPLES_PER_SECOND; /* Add off-screen part of strip to offset. */ -- cgit v1.2.3 From bb376da6dfdd2476fc3738ce1fc89dac27825cef Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Fri, 15 Jul 2022 18:00:45 +0200 Subject: Fix Cycles MetalRT error after recent specialization changes --- intern/cycles/kernel/device/gpu/kernel.h | 2 +- intern/cycles/kernel/device/metal/function_constants.h | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/intern/cycles/kernel/device/gpu/kernel.h b/intern/cycles/kernel/device/gpu/kernel.h index b9a44ccad02..e1ab802aa80 100644 --- a/intern/cycles/kernel/device/gpu/kernel.h +++ b/intern/cycles/kernel/device/gpu/kernel.h @@ -246,7 +246,7 @@ ccl_gpu_kernel(GPU_KERNEL_BLOCK_NUM_THREADS, GPU_KERNEL_MAX_REGISTERS) ccl_gpu_kernel_postfix #if defined(__KERNEL_METAL_APPLE__) && defined(__METALRT__) -constant int __dummy_constant [[function_constant(0)]]; +constant int __dummy_constant [[function_constant(Kernel_DummyConstant)]]; #endif ccl_gpu_kernel(GPU_KERNEL_BLOCK_NUM_THREADS, GPU_KERNEL_MAX_REGISTERS) diff --git a/intern/cycles/kernel/device/metal/function_constants.h b/intern/cycles/kernel/device/metal/function_constants.h index f4001735672..3adf390c7f6 100644 --- a/intern/cycles/kernel/device/metal/function_constants.h +++ b/intern/cycles/kernel/device/metal/function_constants.h @@ -2,6 +2,7 @@ * Copyright 2021-2022 Blender Foundation */ enum { + Kernel_DummyConstant, #define KERNEL_STRUCT_MEMBER(parent, type, name) KernelData_##parent##_##name, #include "kernel/data_template.h" }; -- cgit v1.2.3 From 5152c7c152e52d563cbd3ba3c792de3af0c2c14f Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Wed, 13 Jul 2022 16:54:53 +0200 Subject: Cycles: refactor rays to have start and end distance, fix precision issues For transparency, volume and light intersection rays, adjust these distances rather than the ray start position. This way we increment the start distance by the smallest possible float increment to avoid self intersections, and be sure it works as the distance compared to be will be exactly the same as before, due to the ray start position and direction remaining the same. Fix T98764, T96537, hair ray tracing precision issues. Differential Revision: https://developer.blender.org/D15455 --- intern/cycles/kernel/bvh/bvh.h | 36 +++++------ intern/cycles/kernel/bvh/embree.h | 4 +- intern/cycles/kernel/bvh/local.h | 13 +++- intern/cycles/kernel/bvh/nodes.h | 32 +++++----- intern/cycles/kernel/bvh/shadow_all.h | 47 +++++++++++--- intern/cycles/kernel/bvh/traversal.h | 30 ++++++--- intern/cycles/kernel/bvh/util.h | 13 ++++ intern/cycles/kernel/bvh/volume.h | 17 +++-- intern/cycles/kernel/bvh/volume_all.h | 31 ++++++--- intern/cycles/kernel/camera/camera.h | 20 +++--- intern/cycles/kernel/device/metal/kernel.metal | 30 ++++++--- intern/cycles/kernel/device/optix/kernel.cu | 26 +++++--- intern/cycles/kernel/geom/curve_intersect.h | 36 ++++++----- .../cycles/kernel/geom/motion_triangle_intersect.h | 6 +- intern/cycles/kernel/geom/point_intersect.h | 15 +++-- intern/cycles/kernel/geom/shader_data.h | 3 +- intern/cycles/kernel/geom/triangle_intersect.h | 6 +- intern/cycles/kernel/integrator/init_from_bake.h | 6 +- intern/cycles/kernel/integrator/init_from_camera.h | 2 +- .../cycles/kernel/integrator/intersect_closest.h | 6 +- .../kernel/integrator/intersect_volume_stack.h | 13 ++-- intern/cycles/kernel/integrator/mnee.h | 22 +++---- intern/cycles/kernel/integrator/path_state.h | 1 - intern/cycles/kernel/integrator/shade_background.h | 3 +- intern/cycles/kernel/integrator/shade_light.h | 15 +---- intern/cycles/kernel/integrator/shade_shadow.h | 15 ++--- intern/cycles/kernel/integrator/shade_surface.h | 47 ++++++-------- intern/cycles/kernel/integrator/shade_volume.h | 73 ++++++++++++---------- .../kernel/integrator/shadow_state_template.h | 3 +- intern/cycles/kernel/integrator/state_template.h | 10 +-- intern/cycles/kernel/integrator/state_util.h | 12 ++-- intern/cycles/kernel/integrator/subsurface.h | 5 +- intern/cycles/kernel/integrator/subsurface_disk.h | 6 +- .../kernel/integrator/subsurface_random_walk.h | 17 ++--- intern/cycles/kernel/light/light.h | 34 ++++++---- intern/cycles/kernel/light/sample.h | 7 ++- intern/cycles/kernel/osl/services.cpp | 9 ++- intern/cycles/kernel/svm/ao.h | 3 +- intern/cycles/kernel/svm/bevel.h | 3 +- intern/cycles/kernel/svm/tex_coord.h | 4 +- intern/cycles/kernel/types.h | 3 +- intern/cycles/util/math_intersect.h | 37 ++++++----- 42 files changed, 427 insertions(+), 294 deletions(-) diff --git a/intern/cycles/kernel/bvh/bvh.h b/intern/cycles/kernel/bvh/bvh.h index f375529a6f6..9972de86c47 100644 --- a/intern/cycles/kernel/bvh/bvh.h +++ b/intern/cycles/kernel/bvh/bvh.h @@ -175,8 +175,8 @@ ccl_device_intersect bool scene_intersect(KernelGlobals kg, optixTrace(scene_intersect_valid(ray) ? kernel_data.device_bvh : 0, ray->P, ray->D, - 0.0f, - ray->t, + ray->tmin, + ray->tmax, ray->time, ray_mask, ray_flags, @@ -203,28 +203,28 @@ ccl_device_intersect bool scene_intersect(KernelGlobals kg, #elif defined(__METALRT__) if (!scene_intersect_valid(ray)) { - isect->t = ray->t; + isect->t = ray->tmax; isect->type = PRIMITIVE_NONE; return false; } # if defined(__KERNEL_DEBUG__) if (is_null_instance_acceleration_structure(metal_ancillaries->accel_struct)) { - isect->t = ray->t; + isect->t = ray->tmax; isect->type = PRIMITIVE_NONE; kernel_assert(!"Invalid metal_ancillaries->accel_struct pointer"); return false; } if (is_null_intersection_function_table(metal_ancillaries->ift_default)) { - isect->t = ray->t; + isect->t = ray->tmax; isect->type = PRIMITIVE_NONE; kernel_assert(!"Invalid ift_default"); return false; } # endif - metal::raytracing::ray r(ray->P, ray->D, 0.0f, ray->t); + metal::raytracing::ray r(ray->P, ray->D, ray->tmin, ray->tmax); metalrt_intersector_type metalrt_intersect; if (!kernel_data.bvh.have_curves) { @@ -263,7 +263,7 @@ ccl_device_intersect bool scene_intersect(KernelGlobals kg, # endif if (intersection.type == intersection_type::none) { - isect->t = ray->t; + isect->t = ray->tmax; isect->type = PRIMITIVE_NONE; return false; @@ -296,7 +296,7 @@ ccl_device_intersect bool scene_intersect(KernelGlobals kg, # ifdef __EMBREE__ if (kernel_data.device_bvh) { - isect->t = ray->t; + isect->t = ray->tmax; CCLIntersectContext ctx(kg, CCLIntersectContext::RAY_REGULAR); IntersectContext rtc_ctx(&ctx); RTCRayHit ray_hit; @@ -360,8 +360,8 @@ ccl_device_intersect bool scene_intersect_local(KernelGlobals kg, optixTrace(scene_intersect_valid(ray) ? kernel_data.device_bvh : 0, ray->P, ray->D, - 0.0f, - ray->t, + ray->tmin, + ray->tmax, ray->time, 0xFF, /* Need to always call into __anyhit__kernel_optix_local_hit. */ @@ -405,7 +405,7 @@ ccl_device_intersect bool scene_intersect_local(KernelGlobals kg, } # endif - metal::raytracing::ray r(ray->P, ray->D, 0.0f, ray->t); + metal::raytracing::ray r(ray->P, ray->D, ray->tmin, ray->tmax); metalrt_intersector_type metalrt_intersect; metalrt_intersect.force_opacity(metal::raytracing::forced_opacity::non_opaque); @@ -476,7 +476,7 @@ ccl_device_intersect bool scene_intersect_local(KernelGlobals kg, float3 dir = ray->D; float3 idir = ray->D; Transform ob_itfm; - rtc_ray.tfar = ray->t * + rtc_ray.tfar = ray->tmax * bvh_instance_motion_push(kg, local_object, ray, &P, &dir, &idir, &ob_itfm); /* bvh_instance_motion_push() returns the inverse transform but * it's not needed here. */ @@ -542,8 +542,8 @@ ccl_device_intersect bool scene_intersect_shadow_all(KernelGlobals kg, optixTrace(scene_intersect_valid(ray) ? kernel_data.device_bvh : 0, ray->P, ray->D, - 0.0f, - ray->t, + ray->tmin, + ray->tmax, ray->time, ray_mask, /* Need to always call into __anyhit__kernel_optix_shadow_all_hit. */ @@ -582,7 +582,7 @@ ccl_device_intersect bool scene_intersect_shadow_all(KernelGlobals kg, } # endif - metal::raytracing::ray r(ray->P, ray->D, 0.0f, ray->t); + metal::raytracing::ray r(ray->P, ray->D, ray->tmin, ray->tmax); metalrt_intersector_type metalrt_intersect; metalrt_intersect.force_opacity(metal::raytracing::forced_opacity::non_opaque); @@ -701,8 +701,8 @@ ccl_device_intersect bool scene_intersect_volume(KernelGlobals kg, optixTrace(scene_intersect_valid(ray) ? kernel_data.device_bvh : 0, ray->P, ray->D, - 0.0f, - ray->t, + ray->tmin, + ray->tmax, ray->time, ray_mask, /* Need to always call into __anyhit__kernel_optix_volume_test. */ @@ -744,7 +744,7 @@ ccl_device_intersect bool scene_intersect_volume(KernelGlobals kg, } # endif - metal::raytracing::ray r(ray->P, ray->D, 0.0f, ray->t); + metal::raytracing::ray r(ray->P, ray->D, ray->tmin, ray->tmax); metalrt_intersector_type metalrt_intersect; metalrt_intersect.force_opacity(metal::raytracing::forced_opacity::non_opaque); diff --git a/intern/cycles/kernel/bvh/embree.h b/intern/cycles/kernel/bvh/embree.h index 77eec2468f4..fecbccac2f8 100644 --- a/intern/cycles/kernel/bvh/embree.h +++ b/intern/cycles/kernel/bvh/embree.h @@ -83,8 +83,8 @@ ccl_device_inline void kernel_embree_setup_ray(const Ray &ray, rtc_ray.dir_x = ray.D.x; rtc_ray.dir_y = ray.D.y; rtc_ray.dir_z = ray.D.z; - rtc_ray.tnear = 0.0f; - rtc_ray.tfar = ray.t; + rtc_ray.tnear = ray.tmin; + rtc_ray.tfar = ray.tmax; rtc_ray.time = ray.time; rtc_ray.mask = visibility; } diff --git a/intern/cycles/kernel/bvh/local.h b/intern/cycles/kernel/bvh/local.h index 3b6b30ea93d..017a241ef4a 100644 --- a/intern/cycles/kernel/bvh/local.h +++ b/intern/cycles/kernel/bvh/local.h @@ -47,8 +47,9 @@ ccl_device_inline float3 P = ray->P; float3 dir = bvh_clamp_direction(ray->D); float3 idir = bvh_inverse_direction(dir); + float tmin = ray->tmin; int object = OBJECT_NONE; - float isect_t = ray->t; + float isect_t = ray->tmax; if (local_isect != NULL) { local_isect->num_hits = 0; @@ -59,10 +60,13 @@ ccl_device_inline if (!(object_flag & SD_OBJECT_TRANSFORM_APPLIED)) { #if BVH_FEATURE(BVH_MOTION) Transform ob_itfm; - isect_t *= bvh_instance_motion_push(kg, local_object, ray, &P, &dir, &idir, &ob_itfm); + const float t_world_to_instance = bvh_instance_motion_push( + kg, local_object, ray, &P, &dir, &idir, &ob_itfm); #else - isect_t *= bvh_instance_push(kg, local_object, ray, &P, &dir, &idir); + const float t_world_to_instance = bvh_instance_push(kg, local_object, ray, &P, &dir, &idir); #endif + isect_t *= t_world_to_instance; + tmin *= t_world_to_instance; object = local_object; } @@ -81,6 +85,7 @@ ccl_device_inline dir, #endif idir, + tmin, isect_t, node_addr, PATH_RAY_ALL_VISIBILITY, @@ -155,6 +160,7 @@ ccl_device_inline local_object, prim, prim_addr, + tmin, isect_t, lcg_state, max_hits)) { @@ -191,6 +197,7 @@ ccl_device_inline local_object, prim, prim_addr, + tmin, isect_t, lcg_state, max_hits)) { diff --git a/intern/cycles/kernel/bvh/nodes.h b/intern/cycles/kernel/bvh/nodes.h index c19dea9223b..e02841fad16 100644 --- a/intern/cycles/kernel/bvh/nodes.h +++ b/intern/cycles/kernel/bvh/nodes.h @@ -18,7 +18,8 @@ ccl_device_forceinline Transform bvh_unaligned_node_fetch_space(KernelGlobals kg ccl_device_forceinline int bvh_aligned_node_intersect(KernelGlobals kg, const float3 P, const float3 idir, - const float t, + const float tmin, + const float tmax, const int node_addr, const uint visibility, float dist[2]) @@ -39,8 +40,8 @@ ccl_device_forceinline int bvh_aligned_node_intersect(KernelGlobals kg, float c0hiy = (node1.z - P.y) * idir.y; float c0loz = (node2.x - P.z) * idir.z; float c0hiz = (node2.z - P.z) * idir.z; - float c0min = max4(0.0f, min(c0lox, c0hix), min(c0loy, c0hiy), min(c0loz, c0hiz)); - float c0max = min4(t, max(c0lox, c0hix), max(c0loy, c0hiy), max(c0loz, c0hiz)); + float c0min = max4(tmin, min(c0lox, c0hix), min(c0loy, c0hiy), min(c0loz, c0hiz)); + float c0max = min4(tmax, max(c0lox, c0hix), max(c0loy, c0hiy), max(c0loz, c0hiz)); float c1lox = (node0.y - P.x) * idir.x; float c1hix = (node0.w - P.x) * idir.x; @@ -48,8 +49,8 @@ ccl_device_forceinline int bvh_aligned_node_intersect(KernelGlobals kg, float c1hiy = (node1.w - P.y) * idir.y; float c1loz = (node2.y - P.z) * idir.z; float c1hiz = (node2.w - P.z) * idir.z; - float c1min = max4(0.0f, min(c1lox, c1hix), min(c1loy, c1hiy), min(c1loz, c1hiz)); - float c1max = min4(t, max(c1lox, c1hix), max(c1loy, c1hiy), max(c1loz, c1hiz)); + float c1min = max4(tmin, min(c1lox, c1hix), min(c1loy, c1hiy), min(c1loz, c1hiz)); + float c1max = min4(tmax, max(c1lox, c1hix), max(c1loy, c1hiy), max(c1loz, c1hiz)); dist[0] = c0min; dist[1] = c1min; @@ -66,7 +67,8 @@ ccl_device_forceinline int bvh_aligned_node_intersect(KernelGlobals kg, ccl_device_forceinline bool bvh_unaligned_node_intersect_child(KernelGlobals kg, const float3 P, const float3 dir, - const float t, + const float tmin, + const float tmax, int node_addr, int child, float dist[2]) @@ -83,8 +85,8 @@ ccl_device_forceinline bool bvh_unaligned_node_intersect_child(KernelGlobals kg, const float far_x = max(lower_xyz.x, upper_xyz.x); const float far_y = max(lower_xyz.y, upper_xyz.y); const float far_z = max(lower_xyz.z, upper_xyz.z); - const float tnear = max4(0.0f, near_x, near_y, near_z); - const float tfar = min4(t, far_x, far_y, far_z); + const float tnear = max4(tmin, near_x, near_y, near_z); + const float tfar = min4(tmax, far_x, far_y, far_z); *dist = tnear; return tnear <= tfar; } @@ -93,7 +95,8 @@ ccl_device_forceinline int bvh_unaligned_node_intersect(KernelGlobals kg, const float3 P, const float3 dir, const float3 idir, - const float t, + const float tmin, + const float tmax, const int node_addr, const uint visibility, float dist[2]) @@ -102,7 +105,7 @@ ccl_device_forceinline int bvh_unaligned_node_intersect(KernelGlobals kg, #ifdef __VISIBILITY_FLAG__ float4 cnodes = kernel_data_fetch(bvh_nodes, node_addr + 0); #endif - if (bvh_unaligned_node_intersect_child(kg, P, dir, t, node_addr, 0, &dist[0])) { + if (bvh_unaligned_node_intersect_child(kg, P, dir, tmin, tmax, node_addr, 0, &dist[0])) { #ifdef __VISIBILITY_FLAG__ if ((__float_as_uint(cnodes.x) & visibility)) #endif @@ -110,7 +113,7 @@ ccl_device_forceinline int bvh_unaligned_node_intersect(KernelGlobals kg, mask |= 1; } } - if (bvh_unaligned_node_intersect_child(kg, P, dir, t, node_addr, 1, &dist[1])) { + if (bvh_unaligned_node_intersect_child(kg, P, dir, tmin, tmax, node_addr, 1, &dist[1])) { #ifdef __VISIBILITY_FLAG__ if ((__float_as_uint(cnodes.y) & visibility)) #endif @@ -125,16 +128,17 @@ ccl_device_forceinline int bvh_node_intersect(KernelGlobals kg, const float3 P, const float3 dir, const float3 idir, - const float t, + const float tmin, + const float tmax, const int node_addr, const uint visibility, float dist[2]) { float4 node = kernel_data_fetch(bvh_nodes, node_addr); if (__float_as_uint(node.x) & PATH_RAY_NODE_UNALIGNED) { - return bvh_unaligned_node_intersect(kg, P, dir, idir, t, node_addr, visibility, dist); + return bvh_unaligned_node_intersect(kg, P, dir, idir, tmin, tmax, node_addr, visibility, dist); } else { - return bvh_aligned_node_intersect(kg, P, idir, t, node_addr, visibility, dist); + return bvh_aligned_node_intersect(kg, P, idir, tmin, tmax, node_addr, visibility, dist); } } diff --git a/intern/cycles/kernel/bvh/shadow_all.h b/intern/cycles/kernel/bvh/shadow_all.h index e86fe867eac..db3c91569aa 100644 --- a/intern/cycles/kernel/bvh/shadow_all.h +++ b/intern/cycles/kernel/bvh/shadow_all.h @@ -49,6 +49,7 @@ ccl_device_inline float3 P = ray->P; float3 dir = bvh_clamp_direction(ray->D); float3 idir = bvh_inverse_direction(dir); + float tmin = ray->tmin; int object = OBJECT_NONE; uint num_hits = 0; @@ -59,12 +60,12 @@ ccl_device_inline /* Max distance in world space. May be dynamically reduced when max number of * recorded hits is exceeded and we no longer need to find hits beyond the max * distance found. */ - float t_max_world = ray->t; + float t_max_world = ray->tmax; /* Current maximum distance to the intersection. * Is calculated as a ray length, transformed to an object space when entering * instance node. */ - float t_max_current = ray->t; + float t_max_current = ray->tmax; /* Conversion from world to local space for the current instance if any, 1.0 * otherwise. */ @@ -88,6 +89,7 @@ ccl_device_inline dir, #endif idir, + tmin, t_max_current, node_addr, visibility, @@ -156,8 +158,16 @@ ccl_device_inline switch (type & PRIMITIVE_ALL) { case PRIMITIVE_TRIANGLE: { - hit = triangle_intersect( - kg, &isect, P, dir, t_max_current, visibility, prim_object, prim, prim_addr); + hit = triangle_intersect(kg, + &isect, + P, + dir, + tmin, + t_max_current, + visibility, + prim_object, + prim, + prim_addr); break; } #if BVH_FEATURE(BVH_MOTION) @@ -166,6 +176,7 @@ ccl_device_inline &isect, P, dir, + tmin, t_max_current, ray->time, visibility, @@ -189,8 +200,16 @@ ccl_device_inline } const int curve_type = kernel_data_fetch(prim_type, prim_addr); - hit = curve_intersect( - kg, &isect, P, dir, t_max_current, prim_object, prim, ray->time, curve_type); + hit = curve_intersect(kg, + &isect, + P, + dir, + tmin, + t_max_current, + prim_object, + prim, + ray->time, + curve_type); break; } @@ -207,8 +226,16 @@ ccl_device_inline } const int point_type = kernel_data_fetch(prim_type, prim_addr); - hit = point_intersect( - kg, &isect, P, dir, t_max_current, prim_object, prim, ray->time, point_type); + hit = point_intersect(kg, + &isect, + P, + dir, + tmin, + t_max_current, + prim_object, + prim, + ray->time, + point_type); break; } #endif /* BVH_FEATURE(BVH_POINTCLOUD) */ @@ -302,6 +329,7 @@ ccl_device_inline /* Convert intersection to object space. */ t_max_current *= t_world_to_instance; + tmin *= t_world_to_instance; ++stack_ptr; kernel_assert(stack_ptr < BVH_STACK_SIZE); @@ -323,7 +351,8 @@ ccl_device_inline #endif /* Restore world space ray length. */ - t_max_current = ray->t; + tmin = ray->tmin; + t_max_current = ray->tmax; object = OBJECT_NONE; t_world_to_instance = 1.0f; diff --git a/intern/cycles/kernel/bvh/traversal.h b/intern/cycles/kernel/bvh/traversal.h index 784fbf4fd11..0ff38bf02de 100644 --- a/intern/cycles/kernel/bvh/traversal.h +++ b/intern/cycles/kernel/bvh/traversal.h @@ -43,13 +43,14 @@ ccl_device_noinline bool BVH_FUNCTION_FULL_NAME(BVH)(KernelGlobals kg, float3 P = ray->P; float3 dir = bvh_clamp_direction(ray->D); float3 idir = bvh_inverse_direction(dir); + float tmin = ray->tmin; int object = OBJECT_NONE; #if BVH_FEATURE(BVH_MOTION) Transform ob_itfm; #endif - isect->t = ray->t; + isect->t = ray->tmax; isect->u = 0.0f; isect->v = 0.0f; isect->prim = PRIM_NONE; @@ -71,6 +72,7 @@ ccl_device_noinline bool BVH_FUNCTION_FULL_NAME(BVH)(KernelGlobals kg, dir, #endif idir, + tmin, isect->t, node_addr, visibility, @@ -133,8 +135,16 @@ ccl_device_noinline bool BVH_FUNCTION_FULL_NAME(BVH)(KernelGlobals kg, switch (type & PRIMITIVE_ALL) { case PRIMITIVE_TRIANGLE: { - if (triangle_intersect( - kg, isect, P, dir, isect->t, visibility, prim_object, prim, prim_addr)) { + if (triangle_intersect(kg, + isect, + P, + dir, + tmin, + isect->t, + visibility, + prim_object, + prim, + prim_addr)) { /* shadow ray early termination */ if (visibility & PATH_RAY_SHADOW_OPAQUE) return true; @@ -147,6 +157,7 @@ ccl_device_noinline bool BVH_FUNCTION_FULL_NAME(BVH)(KernelGlobals kg, isect, P, dir, + tmin, isect->t, ray->time, visibility, @@ -174,7 +185,7 @@ ccl_device_noinline bool BVH_FUNCTION_FULL_NAME(BVH)(KernelGlobals kg, const int curve_type = kernel_data_fetch(prim_type, prim_addr); const bool hit = curve_intersect( - kg, isect, P, dir, isect->t, prim_object, prim, ray->time, curve_type); + kg, isect, P, dir, tmin, isect->t, prim_object, prim, ray->time, curve_type); if (hit) { /* shadow ray early termination */ if (visibility & PATH_RAY_SHADOW_OPAQUE) @@ -195,7 +206,7 @@ ccl_device_noinline bool BVH_FUNCTION_FULL_NAME(BVH)(KernelGlobals kg, const int point_type = kernel_data_fetch(prim_type, prim_addr); const bool hit = point_intersect( - kg, isect, P, dir, isect->t, prim_object, prim, ray->time, point_type); + kg, isect, P, dir, tmin, isect->t, prim_object, prim, ray->time, point_type); if (hit) { /* shadow ray early termination */ if (visibility & PATH_RAY_SHADOW_OPAQUE) @@ -212,11 +223,15 @@ ccl_device_noinline bool BVH_FUNCTION_FULL_NAME(BVH)(KernelGlobals kg, object = kernel_data_fetch(prim_object, -prim_addr - 1); #if BVH_FEATURE(BVH_MOTION) - isect->t *= bvh_instance_motion_push(kg, object, ray, &P, &dir, &idir, &ob_itfm); + const float t_world_to_instance = bvh_instance_motion_push( + kg, object, ray, &P, &dir, &idir, &ob_itfm); #else - isect->t *= bvh_instance_push(kg, object, ray, &P, &dir, &idir); + const float t_world_to_instance = bvh_instance_push(kg, object, ray, &P, &dir, &idir); #endif + isect->t *= t_world_to_instance; + tmin *= t_world_to_instance; + ++stack_ptr; kernel_assert(stack_ptr < BVH_STACK_SIZE); traversal_stack[stack_ptr] = ENTRYPOINT_SENTINEL; @@ -235,6 +250,7 @@ ccl_device_noinline bool BVH_FUNCTION_FULL_NAME(BVH)(KernelGlobals kg, #else isect->t = bvh_instance_pop(kg, object, ray, &P, &dir, &idir, isect->t); #endif + tmin = ray->tmin; object = OBJECT_NONE; node_addr = traversal_stack[stack_ptr]; diff --git a/intern/cycles/kernel/bvh/util.h b/intern/cycles/kernel/bvh/util.h index 572e023db25..1795ae4c790 100644 --- a/intern/cycles/kernel/bvh/util.h +++ b/intern/cycles/kernel/bvh/util.h @@ -5,6 +5,19 @@ CCL_NAMESPACE_BEGIN +/* Offset intersection distance by the smallest possible amount, to skip + * intersections at this distance. This works in cases where the ray start + * position is unchanged and only tmin is updated, since for self + * intersection we'll be comparing against the exact same distances. */ +ccl_device_forceinline float intersection_t_offset(const float t) +{ + /* This is a simplified version of nextafterf(t, FLT_MAX), only dealing with + * non-negative and finite t. */ + kernel_assert(t >= 0.0f && isfinite_safe(t)); + const uint32_t bits = (t == 0.0f) ? 1 : __float_as_uint(t) + 1; + return __uint_as_float(bits); +} + #if defined(__KERNEL_CPU__) ccl_device int intersections_compare(const void *a, const void *b) { diff --git a/intern/cycles/kernel/bvh/volume.h b/intern/cycles/kernel/bvh/volume.h index 9715712a8f2..bd4e508ecac 100644 --- a/intern/cycles/kernel/bvh/volume.h +++ b/intern/cycles/kernel/bvh/volume.h @@ -46,13 +46,14 @@ ccl_device_inline float3 P = ray->P; float3 dir = bvh_clamp_direction(ray->D); float3 idir = bvh_inverse_direction(dir); + float tmin = ray->tmin; int object = OBJECT_NONE; #if BVH_FEATURE(BVH_MOTION) Transform ob_itfm; #endif - isect->t = ray->t; + isect->t = ray->tmax; isect->u = 0.0f; isect->v = 0.0f; isect->prim = PRIM_NONE; @@ -73,6 +74,7 @@ ccl_device_inline dir, #endif idir, + tmin, isect->t, node_addr, visibility, @@ -140,7 +142,7 @@ ccl_device_inline continue; } triangle_intersect( - kg, isect, P, dir, isect->t, visibility, prim_object, prim, prim_addr); + kg, isect, P, dir, tmin, isect->t, visibility, prim_object, prim, prim_addr); } break; } @@ -165,6 +167,7 @@ ccl_device_inline isect, P, dir, + tmin, isect->t, ray->time, visibility, @@ -186,11 +189,15 @@ ccl_device_inline int object_flag = kernel_data_fetch(object_flag, object); if (object_flag & SD_OBJECT_HAS_VOLUME) { #if BVH_FEATURE(BVH_MOTION) - isect->t *= bvh_instance_motion_push(kg, object, ray, &P, &dir, &idir, &ob_itfm); + const float t_world_to_instance = bvh_instance_motion_push( + kg, object, ray, &P, &dir, &idir, &ob_itfm); #else - isect->t *= bvh_instance_push(kg, object, ray, &P, &dir, &idir); + const float t_world_to_instance = bvh_instance_push(kg, object, ray, &P, &dir, &idir); #endif + isect->t *= t_world_to_instance; + tmin *= t_world_to_instance; + ++stack_ptr; kernel_assert(stack_ptr < BVH_STACK_SIZE); traversal_stack[stack_ptr] = ENTRYPOINT_SENTINEL; @@ -217,6 +224,8 @@ ccl_device_inline isect->t = bvh_instance_pop(kg, object, ray, &P, &dir, &idir, isect->t); #endif + tmin = ray->tmin; + object = OBJECT_NONE; node_addr = traversal_stack[stack_ptr]; --stack_ptr; diff --git a/intern/cycles/kernel/bvh/volume_all.h b/intern/cycles/kernel/bvh/volume_all.h index d06ea8fe557..c6eeb07a14d 100644 --- a/intern/cycles/kernel/bvh/volume_all.h +++ b/intern/cycles/kernel/bvh/volume_all.h @@ -44,12 +44,12 @@ ccl_device_inline int node_addr = kernel_data.bvh.root; /* ray parameters in registers */ - const float tmax = ray->t; float3 P = ray->P; float3 dir = bvh_clamp_direction(ray->D); float3 idir = bvh_inverse_direction(dir); + float tmin = ray->tmin; int object = OBJECT_NONE; - float isect_t = tmax; + float isect_t = ray->tmax; #if BVH_FEATURE(BVH_MOTION) Transform ob_itfm; @@ -58,7 +58,7 @@ ccl_device_inline int num_hits_in_instance = 0; uint num_hits = 0; - isect_array->t = tmax; + isect_array->t = ray->tmax; /* traversal loop */ do { @@ -75,6 +75,7 @@ ccl_device_inline dir, #endif idir, + tmin, isect_t, node_addr, visibility, @@ -141,8 +142,16 @@ ccl_device_inline if ((object_flag & SD_OBJECT_HAS_VOLUME) == 0) { continue; } - hit = triangle_intersect( - kg, isect_array, P, dir, isect_t, visibility, prim_object, prim, prim_addr); + hit = triangle_intersect(kg, + isect_array, + P, + dir, + tmin, + isect_t, + visibility, + prim_object, + prim, + prim_addr); if (hit) { /* Move on to next entry in intersections array. */ isect_array++; @@ -189,6 +198,7 @@ ccl_device_inline isect_array, P, dir, + tmin, isect_t, ray->time, visibility, @@ -232,11 +242,15 @@ ccl_device_inline int object_flag = kernel_data_fetch(object_flag, object); if (object_flag & SD_OBJECT_HAS_VOLUME) { #if BVH_FEATURE(BVH_MOTION) - isect_t *= bvh_instance_motion_push(kg, object, ray, &P, &dir, &idir, &ob_itfm); + const float t_world_to_instance = bvh_instance_motion_push( + kg, object, ray, &P, &dir, &idir, &ob_itfm); #else - isect_t *= bvh_instance_push(kg, object, ray, &P, &dir, &idir); + const float t_world_to_instance = bvh_instance_push(kg, object, ray, &P, &dir, &idir); #endif + isect_t *= t_world_to_instance; + tmin *= t_world_to_instance; + num_hits_in_instance = 0; isect_array->t = isect_t; @@ -280,7 +294,8 @@ ccl_device_inline #endif } - isect_t = tmax; + tmin = ray->tmin; + isect_t = ray->tmax; isect_array->t = isect_t; object = OBJECT_NONE; diff --git a/intern/cycles/kernel/camera/camera.h b/intern/cycles/kernel/camera/camera.h index 7e1b1c037e9..926ccf7b86f 100644 --- a/intern/cycles/kernel/camera/camera.h +++ b/intern/cycles/kernel/camera/camera.h @@ -165,9 +165,11 @@ ccl_device void camera_sample_perspective(KernelGlobals kg, float nearclip = kernel_data.cam.nearclip * z_inv; ray->P += nearclip * ray->D; ray->dP += nearclip * ray->dD; - ray->t = kernel_data.cam.cliplength * z_inv; + ray->tmin = 0.0f; + ray->tmax = kernel_data.cam.cliplength * z_inv; #else - ray->t = FLT_MAX; + ray->tmin = 0.0f; + ray->tmax = FLT_MAX; #endif } @@ -231,9 +233,11 @@ ccl_device void camera_sample_orthographic(KernelGlobals kg, #ifdef __CAMERA_CLIPPING__ /* clipping */ - ray->t = kernel_data.cam.cliplength; + ray->tmin = 0.0f; + ray->tmax = kernel_data.cam.cliplength; #else - ray->t = FLT_MAX; + ray->tmin = 0.0f; + ray->tmax = FLT_MAX; #endif } @@ -258,7 +262,7 @@ ccl_device_inline void camera_sample_panorama(ccl_constant KernelCamera *cam, /* indicates ray should not receive any light, outside of the lens */ if (is_zero(D)) { - ray->t = 0.0f; + ray->tmax = 0.0f; return; } @@ -349,9 +353,11 @@ ccl_device_inline void camera_sample_panorama(ccl_constant KernelCamera *cam, float nearclip = cam->nearclip; ray->P += nearclip * ray->D; ray->dP += nearclip * ray->dD; - ray->t = cam->cliplength; + ray->tmin = 0.0f; + ray->tmax = cam->cliplength; #else - ray->t = FLT_MAX; + ray->tmin = 0.0f; + ray->tmax = FLT_MAX; #endif } diff --git a/intern/cycles/kernel/device/metal/kernel.metal b/intern/cycles/kernel/device/metal/kernel.metal index 74b4b079a32..764c26dbe8f 100644 --- a/intern/cycles/kernel/device/metal/kernel.metal +++ b/intern/cycles/kernel/device/metal/kernel.metal @@ -410,6 +410,7 @@ void metalrt_intersection_curve(constant KernelParamsMetal &launch_params_metal, const float3 ray_origin, const float3 ray_direction, float time, + const float ray_tmin, const float ray_tmax, thread BoundingBoxIntersectionResult &result) { @@ -434,7 +435,7 @@ void metalrt_intersection_curve(constant KernelParamsMetal &launch_params_metal, isect.t *= len; MetalKernelContext context(launch_params_metal); - if (context.curve_intersect(NULL, &isect, P, dir, isect.t, object, prim, time, type)) { + if (context.curve_intersect(NULL, &isect, P, dir, ray_tmin, isect.t, object, prim, time, type)) { result = metalrt_visibility_test( launch_params_metal, payload, object, prim, isect.u); if (result.accept) { @@ -456,6 +457,7 @@ void metalrt_intersection_curve_shadow(constant KernelParamsMetal &launch_params const float3 ray_origin, const float3 ray_direction, float time, + const float ray_tmin, const float ray_tmax, thread BoundingBoxIntersectionResult &result) { @@ -475,7 +477,7 @@ void metalrt_intersection_curve_shadow(constant KernelParamsMetal &launch_params isect.t *= len; MetalKernelContext context(launch_params_metal); - if (context.curve_intersect(NULL, &isect, P, dir, isect.t, object, prim, time, type)) { + if (context.curve_intersect(NULL, &isect, P, dir, ray_tmin, isect.t, object, prim, time, type)) { result.continue_search = metalrt_shadow_all_hit( launch_params_metal, payload, object, prim, float2(isect.u, isect.v), ray_tmax); result.accept = !result.continue_search; @@ -494,6 +496,7 @@ __intersection__curve_ribbon(constant KernelParamsMetal &launch_params_metal [[b const uint primitive_id [[primitive_id]], const float3 ray_origin [[origin]], const float3 ray_direction [[direction]], + const float ray_tmin [[min_distance]], const float ray_tmax [[max_distance]]) { uint prim = primitive_id + kernel_data_fetch(object_prim_offset, object); @@ -511,7 +514,7 @@ __intersection__curve_ribbon(constant KernelParamsMetal &launch_params_metal [[b # else 0.0f, # endif - ray_tmax, result); + ray_tmin, ray_tmax, result); } return result; @@ -525,6 +528,7 @@ __intersection__curve_ribbon_shadow(constant KernelParamsMetal &launch_params_me const uint primitive_id [[primitive_id]], const float3 ray_origin [[origin]], const float3 ray_direction [[direction]], + const float ray_tmin [[min_distance]], const float ray_tmax [[max_distance]]) { uint prim = primitive_id + kernel_data_fetch(object_prim_offset, object); @@ -542,7 +546,7 @@ __intersection__curve_ribbon_shadow(constant KernelParamsMetal &launch_params_me # else 0.0f, # endif - ray_tmax, result); + ray_tmin, ray_tmax, result); } return result; @@ -556,6 +560,7 @@ __intersection__curve_all(constant KernelParamsMetal &launch_params_metal [[buff const uint primitive_id [[primitive_id]], const float3 ray_origin [[origin]], const float3 ray_direction [[direction]], + const float ray_tmin [[min_distance]], const float ray_tmax [[max_distance]]) { uint prim = primitive_id + kernel_data_fetch(object_prim_offset, object); @@ -571,7 +576,7 @@ __intersection__curve_all(constant KernelParamsMetal &launch_params_metal [[buff # else 0.0f, # endif - ray_tmax, result); + ray_tmin, ray_tmax, result); return result; } @@ -584,6 +589,7 @@ __intersection__curve_all_shadow(constant KernelParamsMetal &launch_params_metal const uint primitive_id [[primitive_id]], const float3 ray_origin [[origin]], const float3 ray_direction [[direction]], + const float ray_tmin [[min_distance]], const float ray_tmax [[max_distance]]) { uint prim = primitive_id + kernel_data_fetch(object_prim_offset, object); @@ -600,7 +606,7 @@ __intersection__curve_all_shadow(constant KernelParamsMetal &launch_params_metal # else 0.0f, # endif - ray_tmax, result); + ray_tmin, ray_tmax, result); return result; } @@ -616,6 +622,7 @@ void metalrt_intersection_point(constant KernelParamsMetal &launch_params_metal, const float3 ray_origin, const float3 ray_direction, float time, + const float ray_tmin, const float ray_tmax, thread BoundingBoxIntersectionResult &result) { @@ -640,7 +647,7 @@ void metalrt_intersection_point(constant KernelParamsMetal &launch_params_metal, isect.t *= len; MetalKernelContext context(launch_params_metal); - if (context.point_intersect(NULL, &isect, P, dir, isect.t, object, prim, time, type)) { + if (context.point_intersect(NULL, &isect, P, dir, ray_tmin, isect.t, object, prim, time, type)) { result = metalrt_visibility_test( launch_params_metal, payload, object, prim, isect.u); if (result.accept) { @@ -662,6 +669,7 @@ void metalrt_intersection_point_shadow(constant KernelParamsMetal &launch_params const float3 ray_origin, const float3 ray_direction, float time, + const float ray_tmin, const float ray_tmax, thread BoundingBoxIntersectionResult &result) { @@ -681,7 +689,7 @@ void metalrt_intersection_point_shadow(constant KernelParamsMetal &launch_params isect.t *= len; MetalKernelContext context(launch_params_metal); - if (context.point_intersect(NULL, &isect, P, dir, isect.t, object, prim, time, type)) { + if (context.point_intersect(NULL, &isect, P, dir, ray_tmin, isect.t, object, prim, time, type)) { result.continue_search = metalrt_shadow_all_hit( launch_params_metal, payload, object, prim, float2(isect.u, isect.v), ray_tmax); result.accept = !result.continue_search; @@ -700,6 +708,7 @@ __intersection__point(constant KernelParamsMetal &launch_params_metal [[buffer(1 const uint primitive_id [[primitive_id]], const float3 ray_origin [[origin]], const float3 ray_direction [[direction]], + const float ray_tmin [[min_distance]], const float ray_tmax [[max_distance]]) { const uint prim = primitive_id + kernel_data_fetch(object_prim_offset, object); @@ -716,7 +725,7 @@ __intersection__point(constant KernelParamsMetal &launch_params_metal [[buffer(1 # else 0.0f, # endif - ray_tmax, result); + ray_tmin, ray_tmax, result); return result; } @@ -729,6 +738,7 @@ __intersection__point_shadow(constant KernelParamsMetal &launch_params_metal [[b const uint primitive_id [[primitive_id]], const float3 ray_origin [[origin]], const float3 ray_direction [[direction]], + const float ray_tmin [[min_distance]], const float ray_tmax [[max_distance]]) { const uint prim = primitive_id + kernel_data_fetch(object_prim_offset, object); @@ -745,7 +755,7 @@ __intersection__point_shadow(constant KernelParamsMetal &launch_params_metal [[b # else 0.0f, # endif - ray_tmax, result); + ray_tmin, ray_tmax, result); return result; } diff --git a/intern/cycles/kernel/device/optix/kernel.cu b/intern/cycles/kernel/device/optix/kernel.cu index 949bf41d171..510f7cca5d6 100644 --- a/intern/cycles/kernel/device/optix/kernel.cu +++ b/intern/cycles/kernel/device/optix/kernel.cu @@ -51,32 +51,36 @@ ccl_device_forceinline int get_object_id() extern "C" __global__ void __raygen__kernel_optix_integrator_intersect_closest() { const int global_index = optixGetLaunchIndex().x; - const int path_index = (kernel_params.path_index_array) ? kernel_params.path_index_array[global_index] : - global_index; + const int path_index = (kernel_params.path_index_array) ? + kernel_params.path_index_array[global_index] : + global_index; integrator_intersect_closest(nullptr, path_index, kernel_params.render_buffer); } extern "C" __global__ void __raygen__kernel_optix_integrator_intersect_shadow() { const int global_index = optixGetLaunchIndex().x; - const int path_index = (kernel_params.path_index_array) ? kernel_params.path_index_array[global_index] : - global_index; + const int path_index = (kernel_params.path_index_array) ? + kernel_params.path_index_array[global_index] : + global_index; integrator_intersect_shadow(nullptr, path_index); } extern "C" __global__ void __raygen__kernel_optix_integrator_intersect_subsurface() { const int global_index = optixGetLaunchIndex().x; - const int path_index = (kernel_params.path_index_array) ? kernel_params.path_index_array[global_index] : - global_index; + const int path_index = (kernel_params.path_index_array) ? + kernel_params.path_index_array[global_index] : + global_index; integrator_intersect_subsurface(nullptr, path_index); } extern "C" __global__ void __raygen__kernel_optix_integrator_intersect_volume_stack() { const int global_index = optixGetLaunchIndex().x; - const int path_index = (kernel_params.path_index_array) ? kernel_params.path_index_array[global_index] : - global_index; + const int path_index = (kernel_params.path_index_array) ? + kernel_params.path_index_array[global_index] : + global_index; integrator_intersect_volume_stack(nullptr, path_index); } @@ -408,6 +412,7 @@ ccl_device_inline void optix_intersection_curve(const int prim, const int type) float3 P = optixGetObjectRayOrigin(); float3 dir = optixGetObjectRayDirection(); + float tmin = optixGetRayTmin(); /* The direction is not normalized by default, but the curve intersection routine expects that */ float len; @@ -425,7 +430,7 @@ ccl_device_inline void optix_intersection_curve(const int prim, const int type) if (isect.t != FLT_MAX) isect.t *= len; - if (curve_intersect(NULL, &isect, P, dir, isect.t, object, prim, time, type)) { + if (curve_intersect(NULL, &isect, P, dir, tmin, isect.t, object, prim, time, type)) { static_assert(PRIMITIVE_ALL < 128, "Values >= 128 are reserved for OptiX internal use"); optixReportIntersection(isect.t / len, type & PRIMITIVE_ALL, @@ -462,6 +467,7 @@ extern "C" __global__ void __intersection__point() float3 P = optixGetObjectRayOrigin(); float3 dir = optixGetObjectRayDirection(); + float tmin = optixGetRayTmin(); /* The direction is not normalized by default, the point intersection routine expects that. */ float len; @@ -480,7 +486,7 @@ extern "C" __global__ void __intersection__point() isect.t *= len; } - if (point_intersect(NULL, &isect, P, dir, isect.t, object, prim, time, type)) { + if (point_intersect(NULL, &isect, P, dir, tmin, isect.t, object, prim, time, type)) { static_assert(PRIMITIVE_ALL < 128, "Values >= 128 are reserved for OptiX internal use"); optixReportIntersection(isect.t / len, type & PRIMITIVE_ALL); } diff --git a/intern/cycles/kernel/geom/curve_intersect.h b/intern/cycles/kernel/geom/curve_intersect.h index 001bec01749..9770105dd81 100644 --- a/intern/cycles/kernel/geom/curve_intersect.h +++ b/intern/cycles/kernel/geom/curve_intersect.h @@ -156,7 +156,8 @@ ccl_device_inline float2 half_plane_intersect(const float3 P, const float3 N, co } ccl_device bool curve_intersect_iterative(const float3 ray_dir, - ccl_private float *ray_tfar, + const float ray_tmin, + ccl_private float *ray_tmax, const float dt, const float4 curve[4], float u, @@ -220,7 +221,7 @@ ccl_device bool curve_intersect_iterative(const float3 ray_dir, if (fabsf(f) < f_err && fabsf(g) < g_err) { t += dt; - if (!(0.0f <= t && t <= *ray_tfar)) { + if (!(t >= ray_tmin && t <= *ray_tmax)) { return false; /* Rejects NaNs */ } if (!(u >= 0.0f && u <= 1.0f)) { @@ -237,7 +238,7 @@ ccl_device bool curve_intersect_iterative(const float3 ray_dir, } /* Record intersection. */ - *ray_tfar = t; + *ray_tmax = t; isect->t = t; isect->u = u; isect->v = 0.0f; @@ -250,7 +251,8 @@ ccl_device bool curve_intersect_iterative(const float3 ray_dir, ccl_device bool curve_intersect_recursive(const float3 ray_orig, const float3 ray_dir, - float ray_tfar, + const float ray_tmin, + float ray_tmax, float4 curve[4], ccl_private Intersection *isect) { @@ -331,7 +333,7 @@ ccl_device bool curve_intersect_recursive(const float3 ray_orig, } /* Intersect with cap-planes. */ - float2 tp = make_float2(-dt, ray_tfar - dt); + float2 tp = make_float2(ray_tmin - dt, ray_tmax - dt); tp = make_float2(max(tp.x, tc_outer.x), min(tp.y, tc_outer.y)); const float2 h0 = half_plane_intersect( float4_to_float3(P0), float4_to_float3(dP0du), ray_dir); @@ -394,19 +396,20 @@ ccl_device bool curve_intersect_recursive(const float3 ray_orig, CURVE_NUM_BEZIER_SUBDIVISIONS; if (depth >= termDepth) { found |= curve_intersect_iterative( - ray_dir, &ray_tfar, dt, curve, u_outer0, tp0.x, use_backfacing, isect); + ray_dir, ray_tmin, &ray_tmax, dt, curve, u_outer0, tp0.x, use_backfacing, isect); } else { recurse = true; } } - if (valid1 && (tp1.x + dt <= ray_tfar)) { + const float t1 = tp1.x + dt; + if (valid1 && (t1 >= ray_tmin && t1 <= ray_tmax)) { const int termDepth = unstable1 ? CURVE_NUM_BEZIER_SUBDIVISIONS_UNSTABLE : CURVE_NUM_BEZIER_SUBDIVISIONS; if (depth >= termDepth) { found |= curve_intersect_iterative( - ray_dir, &ray_tfar, dt, curve, u_outer1, tp1.y, use_backfacing, isect); + ray_dir, ray_tmin, &ray_tmax, dt, curve, u_outer1, tp1.y, use_backfacing, isect); } else { recurse = true; @@ -456,7 +459,8 @@ ccl_device_inline bool cylinder_culling_test(const float2 p1, const float2 p2, c * v0,v1,v3 and v2,v3,v1. The edge v1,v2 decides which of the two * triangles gets intersected. */ -ccl_device_inline bool ribbon_intersect_quad(const float ray_tfar, +ccl_device_inline bool ribbon_intersect_quad(const float ray_tmin, + const float ray_tmax, const float3 quad_v0, const float3 quad_v1, const float3 quad_v2, @@ -497,7 +501,7 @@ ccl_device_inline bool ribbon_intersect_quad(const float ray_tfar, /* Perform depth test? */ const float t = rcpDen * dot(v0, Ng); - if (!(0.0f <= t && t <= ray_tfar)) { + if (!(t >= ray_tmin && t <= ray_tmax)) { return false; } @@ -534,7 +538,8 @@ ccl_device_inline float4 ribbon_to_ray_space(const float3 ray_space[3], ccl_device_inline bool ribbon_intersect(const float3 ray_org, const float3 ray_dir, - float ray_tfar, + const float ray_tmin, + float ray_tmax, const int N, float4 curve[4], ccl_private Intersection *isect) @@ -582,7 +587,7 @@ ccl_device_inline bool ribbon_intersect(const float3 ray_org, /* Intersect quad. */ float vu, vv, vt; - bool valid0 = ribbon_intersect_quad(ray_tfar, lp0, lp1, up1, up0, &vu, &vv, &vt); + bool valid0 = ribbon_intersect_quad(ray_tmin, ray_tmax, lp0, lp1, up1, up0, &vu, &vv, &vt); if (valid0) { /* ignore self intersections */ @@ -596,7 +601,7 @@ ccl_device_inline bool ribbon_intersect(const float3 ray_org, vv = 2.0f * vv - 1.0f; /* Record intersection. */ - ray_tfar = vt; + ray_tmax = vt; isect->t = vt; isect->u = u + vu * step_size; isect->v = vv; @@ -616,6 +621,7 @@ ccl_device_forceinline bool curve_intersect(KernelGlobals kg, ccl_private Intersection *isect, const float3 P, const float3 dir, + const float tmin, const float tmax, int object, int prim, @@ -645,7 +651,7 @@ ccl_device_forceinline bool curve_intersect(KernelGlobals kg, if (type & PRIMITIVE_CURVE_RIBBON) { /* todo: adaptive number of subdivisions could help performance here. */ const int subdivisions = kernel_data.bvh.curve_subdivisions; - if (ribbon_intersect(P, dir, tmax, subdivisions, curve, isect)) { + if (ribbon_intersect(P, dir, tmin, tmax, subdivisions, curve, isect)) { isect->prim = prim; isect->object = object; isect->type = type; @@ -655,7 +661,7 @@ ccl_device_forceinline bool curve_intersect(KernelGlobals kg, return false; } else { - if (curve_intersect_recursive(P, dir, tmax, curve, isect)) { + if (curve_intersect_recursive(P, dir, tmin, tmax, curve, isect)) { isect->prim = prim; isect->object = object; isect->type = type; diff --git a/intern/cycles/kernel/geom/motion_triangle_intersect.h b/intern/cycles/kernel/geom/motion_triangle_intersect.h index 6eea5096567..b59c5c43c20 100644 --- a/intern/cycles/kernel/geom/motion_triangle_intersect.h +++ b/intern/cycles/kernel/geom/motion_triangle_intersect.h @@ -46,6 +46,7 @@ ccl_device_inline bool motion_triangle_intersect(KernelGlobals kg, ccl_private Intersection *isect, float3 P, float3 dir, + float tmin, float tmax, float time, uint visibility, @@ -58,7 +59,7 @@ ccl_device_inline bool motion_triangle_intersect(KernelGlobals kg, motion_triangle_vertices(kg, object, prim, time, verts); /* Ray-triangle intersection, unoptimized. */ float t, u, v; - if (ray_triangle_intersect(P, dir, tmax, verts[0], verts[1], verts[2], &u, &v, &t)) { + if (ray_triangle_intersect(P, dir, tmin, tmax, verts[0], verts[1], verts[2], &u, &v, &t)) { #ifdef __VISIBILITY_FLAG__ /* Visibility flag test. we do it here under the assumption * that most triangles are culled by node flags. @@ -92,6 +93,7 @@ ccl_device_inline bool motion_triangle_intersect_local(KernelGlobals kg, int object, int prim, int prim_addr, + float tmin, float tmax, ccl_private uint *lcg_state, int max_hits) @@ -101,7 +103,7 @@ ccl_device_inline bool motion_triangle_intersect_local(KernelGlobals kg, motion_triangle_vertices(kg, object, prim, time, verts); /* Ray-triangle intersection, unoptimized. */ float t, u, v; - if (!ray_triangle_intersect(P, dir, tmax, verts[0], verts[1], verts[2], &u, &v, &t)) { + if (!ray_triangle_intersect(P, dir, tmin, tmax, verts[0], verts[1], verts[2], &u, &v, &t)) { return false; } diff --git a/intern/cycles/kernel/geom/point_intersect.h b/intern/cycles/kernel/geom/point_intersect.h index dfd9d9a015b..ee5a564947b 100644 --- a/intern/cycles/kernel/geom/point_intersect.h +++ b/intern/cycles/kernel/geom/point_intersect.h @@ -9,8 +9,12 @@ CCL_NAMESPACE_BEGIN #ifdef __POINTCLOUD__ -ccl_device_forceinline bool point_intersect_test( - const float4 point, const float3 P, const float3 dir, const float tmax, ccl_private float *t) +ccl_device_forceinline bool point_intersect_test(const float4 point, + const float3 P, + const float3 dir, + const float tmin, + const float tmax, + ccl_private float *t) { const float3 center = float4_to_float3(point); const float radius = point.w; @@ -28,12 +32,12 @@ ccl_device_forceinline bool point_intersect_test( const float td = sqrt((r2 - l2) * rd2); const float t_front = projC0 - td; - const bool valid_front = (0.0f <= t_front) & (t_front <= tmax); + const bool valid_front = (tmin <= t_front) & (t_front <= tmax); /* Always back-face culling for now. */ # if 0 const float t_back = projC0 + td; - const bool valid_back = (0.0f <= t_back) & (t_back <= tmax); + const bool valid_back = (tmin <= t_back) & (t_back <= tmax); /* check if there is a first hit */ const bool valid_first = valid_front | valid_back; @@ -56,6 +60,7 @@ ccl_device_forceinline bool point_intersect(KernelGlobals kg, ccl_private Intersection *isect, const float3 P, const float3 dir, + const float tmin, const float tmax, const int object, const int prim, @@ -65,7 +70,7 @@ ccl_device_forceinline bool point_intersect(KernelGlobals kg, const float4 point = (type & PRIMITIVE_MOTION) ? motion_point(kg, object, prim, time) : kernel_data_fetch(points, prim); - if (!point_intersect_test(point, P, dir, tmax, &isect->t)) { + if (!point_intersect_test(point, P, dir, tmin, tmax, &isect->t)) { return false; } diff --git a/intern/cycles/kernel/geom/shader_data.h b/intern/cycles/kernel/geom/shader_data.h index e5dbeac5e66..99b9289cb4a 100644 --- a/intern/cycles/kernel/geom/shader_data.h +++ b/intern/cycles/kernel/geom/shader_data.h @@ -407,7 +407,7 @@ ccl_device_inline void shader_setup_from_volume(KernelGlobals kg, { /* vectors */ - sd->P = ray->P; + sd->P = ray->P + ray->D * ray->tmin; sd->N = -ray->D; sd->Ng = -ray->D; sd->I = -ray->D; @@ -441,7 +441,6 @@ ccl_device_inline void shader_setup_from_volume(KernelGlobals kg, /* for NDC coordinates */ sd->ray_P = ray->P; - sd->ray_dP = ray->dP; } #endif /* __VOLUME__ */ diff --git a/intern/cycles/kernel/geom/triangle_intersect.h b/intern/cycles/kernel/geom/triangle_intersect.h index 0c76de9ccc7..f968e537cfa 100644 --- a/intern/cycles/kernel/geom/triangle_intersect.h +++ b/intern/cycles/kernel/geom/triangle_intersect.h @@ -17,6 +17,7 @@ ccl_device_inline bool triangle_intersect(KernelGlobals kg, ccl_private Intersection *isect, float3 P, float3 dir, + float tmin, float tmax, uint visibility, int object, @@ -28,7 +29,7 @@ ccl_device_inline bool triangle_intersect(KernelGlobals kg, tri_b = kernel_data_fetch(tri_verts, tri_vindex + 1), tri_c = kernel_data_fetch(tri_verts, tri_vindex + 2); float t, u, v; - if (ray_triangle_intersect(P, dir, tmax, tri_a, tri_b, tri_c, &u, &v, &t)) { + if (ray_triangle_intersect(P, dir, tmin, tmax, tri_a, tri_b, tri_c, &u, &v, &t)) { #ifdef __VISIBILITY_FLAG__ /* Visibility flag test. we do it here under the assumption * that most triangles are culled by node flags. @@ -62,6 +63,7 @@ ccl_device_inline bool triangle_intersect_local(KernelGlobals kg, int object, int prim, int prim_addr, + float tmin, float tmax, ccl_private uint *lcg_state, int max_hits) @@ -71,7 +73,7 @@ ccl_device_inline bool triangle_intersect_local(KernelGlobals kg, tri_b = kernel_data_fetch(tri_verts, tri_vindex + 1), tri_c = kernel_data_fetch(tri_verts, tri_vindex + 2); float t, u, v; - if (!ray_triangle_intersect(P, dir, tmax, tri_a, tri_b, tri_c, &u, &v, &t)) { + if (!ray_triangle_intersect(P, dir, tmin, tmax, tri_a, tri_b, tri_c, &u, &v, &t)) { return false; } diff --git a/intern/cycles/kernel/integrator/init_from_bake.h b/intern/cycles/kernel/integrator/init_from_bake.h index afd174de9e8..bf3f41b52b9 100644 --- a/intern/cycles/kernel/integrator/init_from_bake.h +++ b/intern/cycles/kernel/integrator/init_from_bake.h @@ -174,7 +174,8 @@ ccl_device bool integrator_init_from_bake(KernelGlobals kg, Ray ray ccl_optional_struct_init; ray.P = zero_float3(); ray.D = normalize(P); - ray.t = FLT_MAX; + ray.tmin = 0.0f; + ray.tmax = FLT_MAX; ray.time = 0.5f; ray.dP = differential_zero_compact(); ray.dD = differential_zero_compact(); @@ -210,7 +211,8 @@ ccl_device bool integrator_init_from_bake(KernelGlobals kg, Ray ray ccl_optional_struct_init; ray.P = P + N; ray.D = -N; - ray.t = FLT_MAX; + ray.tmin = 0.0f; + ray.tmax = FLT_MAX; ray.time = 0.5f; /* Setup differentials. */ diff --git a/intern/cycles/kernel/integrator/init_from_camera.h b/intern/cycles/kernel/integrator/init_from_camera.h index 73db13be697..e89ab3991c7 100644 --- a/intern/cycles/kernel/integrator/init_from_camera.h +++ b/intern/cycles/kernel/integrator/init_from_camera.h @@ -86,7 +86,7 @@ ccl_device bool integrator_init_from_camera(KernelGlobals kg, /* Generate camera ray. */ Ray ray; integrate_camera_sample(kg, sample, x, y, rng_hash, &ray); - if (ray.t == 0.0f) { + if (ray.tmax == 0.0f) { return true; } diff --git a/intern/cycles/kernel/integrator/intersect_closest.h b/intern/cycles/kernel/integrator/intersect_closest.h index 923dab9591a..60299f2cb2f 100644 --- a/intern/cycles/kernel/integrator/intersect_closest.h +++ b/intern/cycles/kernel/integrator/intersect_closest.h @@ -324,7 +324,7 @@ ccl_device void integrator_intersect_closest(KernelGlobals kg, /* Read ray from integrator state into local memory. */ Ray ray ccl_optional_struct_init; integrator_state_read_ray(kg, state, &ray); - kernel_assert(ray.t != 0.0f); + kernel_assert(ray.tmax != 0.0f); const uint visibility = path_state_ray_visibility(state); const int last_isect_prim = INTEGRATOR_STATE(state, isect, prim); @@ -332,12 +332,12 @@ ccl_device void integrator_intersect_closest(KernelGlobals kg, /* Trick to use short AO rays to approximate indirect light at the end of the path. */ if (path_state_ao_bounce(kg, state)) { - ray.t = kernel_data.integrator.ao_bounces_distance; + ray.tmax = kernel_data.integrator.ao_bounces_distance; if (last_isect_object != OBJECT_NONE) { const float object_ao_distance = kernel_data_fetch(objects, last_isect_object).ao_distance; if (object_ao_distance != 0.0f) { - ray.t = object_ao_distance; + ray.tmax = object_ao_distance; } } } diff --git a/intern/cycles/kernel/integrator/intersect_volume_stack.h b/intern/cycles/kernel/integrator/intersect_volume_stack.h index 78f0b4d62aa..9ba4a0a3964 100644 --- a/intern/cycles/kernel/integrator/intersect_volume_stack.h +++ b/intern/cycles/kernel/integrator/intersect_volume_stack.h @@ -24,7 +24,8 @@ ccl_device void integrator_volume_stack_update_for_subsurface(KernelGlobals kg, Ray volume_ray ccl_optional_struct_init; volume_ray.P = from_P; - volume_ray.D = normalize_len(to_P - from_P, &volume_ray.t); + volume_ray.D = normalize_len(to_P - from_P, &volume_ray.tmax); + volume_ray.tmin = 0.0f; volume_ray.self.object = INTEGRATOR_STATE(state, isect, object); volume_ray.self.prim = INTEGRATOR_STATE(state, isect, prim); volume_ray.self.light_object = OBJECT_NONE; @@ -58,12 +59,9 @@ ccl_device void integrator_volume_stack_update_for_subsurface(KernelGlobals kg, volume_stack_enter_exit(kg, state, stack_sd); /* Move ray forward. */ - volume_ray.P = stack_sd->P; + volume_ray.tmin = intersection_t_offset(isect.t); volume_ray.self.object = isect.object; volume_ray.self.prim = isect.prim; - if (volume_ray.t != FLT_MAX) { - volume_ray.D = normalize_len(to_P - volume_ray.P, &volume_ray.t); - } ++step; } #endif @@ -82,7 +80,8 @@ ccl_device void integrator_volume_stack_init(KernelGlobals kg, IntegratorState s /* Trace ray in random direction. Any direction works, Z up is a guess to get the * fewest hits. */ volume_ray.D = make_float3(0.0f, 0.0f, 1.0f); - volume_ray.t = FLT_MAX; + volume_ray.tmin = 0.0f; + volume_ray.tmax = FLT_MAX; volume_ray.self.object = OBJECT_NONE; volume_ray.self.prim = PRIM_NONE; volume_ray.self.light_object = OBJECT_NONE; @@ -199,7 +198,7 @@ ccl_device void integrator_volume_stack_init(KernelGlobals kg, IntegratorState s } /* Move ray forward. */ - volume_ray.P = stack_sd->P; + volume_ray.tmin = intersection_t_offset(isect.t); volume_ray.self.object = isect.object; volume_ray.self.prim = isect.prim; ++step; diff --git a/intern/cycles/kernel/integrator/mnee.h b/intern/cycles/kernel/integrator/mnee.h index 70b009d3b48..7a6f866b1a0 100644 --- a/intern/cycles/kernel/integrator/mnee.h +++ b/intern/cycles/kernel/integrator/mnee.h @@ -442,6 +442,7 @@ ccl_device_forceinline bool mnee_newton_solver(KernelGlobals kg, projection_ray.self.light_prim = PRIM_NONE; projection_ray.dP = differential_make_compact(sd->dP); projection_ray.dD = differential_zero_compact(); + projection_ray.tmin = 0.0f; projection_ray.time = sd->time; Intersection projection_isect; @@ -505,8 +506,8 @@ ccl_device_forceinline bool mnee_newton_solver(KernelGlobals kg, projection_ray.self.prim = pv.prim; projection_ray.P = pv.p; } - projection_ray.D = normalize_len(tentative_p - projection_ray.P, &projection_ray.t); - projection_ray.t *= MNEE_PROJECTION_DISTANCE_MULTIPLIER; + projection_ray.D = normalize_len(tentative_p - projection_ray.P, &projection_ray.tmax); + projection_ray.tmax *= MNEE_PROJECTION_DISTANCE_MULTIPLIER; bool projection_success = false; for (int isect_count = 0; isect_count < MNEE_MAX_INTERSECTION_COUNT; isect_count++) { @@ -525,8 +526,7 @@ ccl_device_forceinline bool mnee_newton_solver(KernelGlobals kg, projection_ray.self.object = projection_isect.object; projection_ray.self.prim = projection_isect.prim; - projection_ray.P += projection_isect.t * projection_ray.D; - projection_ray.t -= projection_isect.t; + projection_ray.tmin = intersection_t_offset(projection_isect.t); } if (!projection_success) { reduce_stepsize = true; @@ -858,6 +858,7 @@ ccl_device_forceinline bool mnee_path_contribution(KernelGlobals kg, Ray probe_ray; probe_ray.self.light_object = ls->object; probe_ray.self.light_prim = ls->prim; + probe_ray.tmin = 0.0f; probe_ray.dP = differential_make_compact(sd->dP); probe_ray.dD = differential_zero_compact(); probe_ray.time = sd->time; @@ -873,13 +874,13 @@ ccl_device_forceinline bool mnee_path_contribution(KernelGlobals kg, ccl_private const ManifoldVertex &v = vertices[vi]; /* Check visibility. */ - probe_ray.D = normalize_len(v.p - probe_ray.P, &probe_ray.t); + probe_ray.D = normalize_len(v.p - probe_ray.P, &probe_ray.tmax); if (scene_intersect(kg, &probe_ray, PATH_RAY_TRANSMIT, &probe_isect)) { int hit_object = (probe_isect.object == OBJECT_NONE) ? kernel_data_fetch(prim_object, probe_isect.prim) : probe_isect.object; /* Test whether the ray hit the appropriate object at its intended location. */ - if (hit_object != v.object || fabsf(probe_ray.t - probe_isect.t) > MNEE_MIN_DISTANCE) + if (hit_object != v.object || fabsf(probe_ray.tmax - probe_isect.t) > MNEE_MIN_DISTANCE) return false; } probe_ray.self.object = v.object; @@ -958,15 +959,16 @@ ccl_device_forceinline int kernel_path_mnee_sample(KernelGlobals kg, probe_ray.self.light_object = ls->object; probe_ray.self.light_prim = ls->prim; probe_ray.P = sd->P; + probe_ray.tmin = 0.0f; if (ls->t == FLT_MAX) { /* Distant / env light. */ probe_ray.D = ls->D; - probe_ray.t = ls->t; + probe_ray.tmax = ls->t; } else { /* Other lights, avoid self-intersection. */ probe_ray.D = ls->P - probe_ray.P; - probe_ray.D = normalize_len(probe_ray.D, &probe_ray.t); + probe_ray.D = normalize_len(probe_ray.D, &probe_ray.tmax); } probe_ray.dP = differential_make_compact(sd->dP); probe_ray.dD = differential_zero_compact(); @@ -1048,9 +1050,7 @@ ccl_device_forceinline int kernel_path_mnee_sample(KernelGlobals kg, probe_ray.self.object = probe_isect.object; probe_ray.self.prim = probe_isect.prim; - probe_ray.P += probe_isect.t * probe_ray.D; - if (ls->t != FLT_MAX) - probe_ray.t -= probe_isect.t; + probe_ray.tmin = intersection_t_offset(probe_isect.t); }; /* Mark the manifold walk invalid to keep mollification on by default. */ diff --git a/intern/cycles/kernel/integrator/path_state.h b/intern/cycles/kernel/integrator/path_state.h index 1a085506a70..912c380cdb6 100644 --- a/intern/cycles/kernel/integrator/path_state.h +++ b/intern/cycles/kernel/integrator/path_state.h @@ -52,7 +52,6 @@ ccl_device_inline void path_state_init_integrator(KernelGlobals kg, INTEGRATOR_STATE_WRITE(state, path, flag) = PATH_RAY_CAMERA | PATH_RAY_MIS_SKIP | PATH_RAY_TRANSPARENT_BACKGROUND; INTEGRATOR_STATE_WRITE(state, path, mis_ray_pdf) = 0.0f; - INTEGRATOR_STATE_WRITE(state, path, mis_ray_t) = 0.0f; INTEGRATOR_STATE_WRITE(state, path, min_ray_pdf) = FLT_MAX; INTEGRATOR_STATE_WRITE(state, path, continuation_probability) = 1.0f; INTEGRATOR_STATE_WRITE(state, path, throughput) = make_float3(1.0f, 1.0f, 1.0f); diff --git a/intern/cycles/kernel/integrator/shade_background.h b/intern/cycles/kernel/integrator/shade_background.h index 47233634463..a7edfffd175 100644 --- a/intern/cycles/kernel/integrator/shade_background.h +++ b/intern/cycles/kernel/integrator/shade_background.h @@ -62,11 +62,10 @@ ccl_device float3 integrator_eval_background_shader(KernelGlobals kg, const float3 ray_P = INTEGRATOR_STATE(state, ray, P); const float3 ray_D = INTEGRATOR_STATE(state, ray, D); const float mis_ray_pdf = INTEGRATOR_STATE(state, path, mis_ray_pdf); - const float mis_ray_t = INTEGRATOR_STATE(state, path, mis_ray_t); /* multiple importance sampling, get background light pdf for ray * direction, and compute weight with respect to BSDF pdf */ - const float pdf = background_light_pdf(kg, ray_P - ray_D * mis_ray_t, ray_D); + const float pdf = background_light_pdf(kg, ray_P, ray_D); const float mis_weight = light_sample_mis_weight_forward(kg, mis_ray_pdf, pdf); L *= mis_weight; } diff --git a/intern/cycles/kernel/integrator/shade_light.h b/intern/cycles/kernel/integrator/shade_light.h index 1a5ac3637f6..910e3383f51 100644 --- a/intern/cycles/kernel/integrator/shade_light.h +++ b/intern/cycles/kernel/integrator/shade_light.h @@ -22,19 +22,8 @@ ccl_device_inline void integrate_light(KernelGlobals kg, const float3 ray_D = INTEGRATOR_STATE(state, ray, D); const float ray_time = INTEGRATOR_STATE(state, ray, time); - /* Advance ray beyond light. */ - /* TODO: can we make this more numerically robust to avoid reintersecting the - * same light in some cases? Ray should not intersect surface anymore as the - * object and prim ids will prevent self intersection. */ - const float3 new_ray_P = ray_P + ray_D * isect.t; - INTEGRATOR_STATE_WRITE(state, ray, P) = new_ray_P; - INTEGRATOR_STATE_WRITE(state, ray, t) -= isect.t; - - /* Set position to where the BSDF was sampled, for correct MIS PDF. */ - const float mis_ray_t = INTEGRATOR_STATE(state, path, mis_ray_t); - ray_P -= ray_D * mis_ray_t; - isect.t += mis_ray_t; - INTEGRATOR_STATE_WRITE(state, path, mis_ray_t) = isect.t; + /* Advance ray to new start distance. */ + INTEGRATOR_STATE_WRITE(state, ray, tmin) = intersection_t_offset(isect.t); LightSample ls ccl_optional_struct_init; const bool use_light_sample = light_sample_from_intersection(kg, &isect, ray_P, ray_D, &ls); diff --git a/intern/cycles/kernel/integrator/shade_shadow.h b/intern/cycles/kernel/integrator/shade_shadow.h index 830da0158cf..4b002a47bee 100644 --- a/intern/cycles/kernel/integrator/shade_shadow.h +++ b/intern/cycles/kernel/integrator/shade_shadow.h @@ -75,13 +75,9 @@ ccl_device_inline void integrate_transparent_volume_shadow(KernelGlobals kg, ray.self.light_object = OBJECT_NONE; ray.self.light_prim = PRIM_NONE; /* Modify ray position and length to match current segment. */ - const float start_t = (hit == 0) ? 0.0f : - INTEGRATOR_STATE_ARRAY(state, shadow_isect, hit - 1, t); - const float end_t = (hit < num_recorded_hits) ? - INTEGRATOR_STATE_ARRAY(state, shadow_isect, hit, t) : - ray.t; - ray.P += start_t * ray.D; - ray.t = end_t - start_t; + ray.tmin = (hit == 0) ? ray.tmin : INTEGRATOR_STATE_ARRAY(state, shadow_isect, hit - 1, t); + ray.tmax = (hit < num_recorded_hits) ? INTEGRATOR_STATE_ARRAY(state, shadow_isect, hit, t) : + ray.tmax; shader_setup_from_volume(kg, shadow_sd, &ray); @@ -137,10 +133,7 @@ ccl_device_inline bool integrate_transparent_shadow(KernelGlobals kg, /* There are more hits that we could not recorded due to memory usage, * adjust ray to intersect again from the last hit. */ const float last_hit_t = INTEGRATOR_STATE_ARRAY(state, shadow_isect, num_recorded_hits - 1, t); - const float3 ray_P = INTEGRATOR_STATE(state, shadow_ray, P); - const float3 ray_D = INTEGRATOR_STATE(state, shadow_ray, D); - INTEGRATOR_STATE_WRITE(state, shadow_ray, P) = ray_P + last_hit_t * ray_D; - INTEGRATOR_STATE_WRITE(state, shadow_ray, t) -= last_hit_t; + INTEGRATOR_STATE_WRITE(state, shadow_ray, tmin) = intersection_t_offset(last_hit_t); } return false; diff --git a/intern/cycles/kernel/integrator/shade_surface.h b/intern/cycles/kernel/integrator/shade_surface.h index 91e34968148..1514b3956ad 100644 --- a/intern/cycles/kernel/integrator/shade_surface.h +++ b/intern/cycles/kernel/integrator/shade_surface.h @@ -77,7 +77,7 @@ ccl_device_forceinline void integrate_surface_emission(KernelGlobals kg, # endif { const float bsdf_pdf = INTEGRATOR_STATE(state, path, mis_ray_pdf); - const float t = sd->ray_length + INTEGRATOR_STATE(state, path, mis_ray_t); + const float t = sd->ray_length; /* Multiple importance sampling, get triangle light pdf, * and compute weight with respect to BSDF pdf. */ @@ -323,16 +323,21 @@ ccl_device_forceinline int integrate_surface_bsdf_bssrdf_bounce( return LABEL_NONE; } - /* Setup ray. Note that clipping works through transparent bounces. */ - INTEGRATOR_STATE_WRITE(state, ray, P) = sd->P; - INTEGRATOR_STATE_WRITE(state, ray, D) = normalize(bsdf_omega_in); - INTEGRATOR_STATE_WRITE(state, ray, t) = (label & LABEL_TRANSPARENT) ? - INTEGRATOR_STATE(state, ray, t) - sd->ray_length : - FLT_MAX; + if (label & LABEL_TRANSPARENT) { + /* Only need to modify start distance for transparent. */ + INTEGRATOR_STATE_WRITE(state, ray, tmin) = intersection_t_offset(sd->ray_length); + } + else { + /* Setup ray with changed origin and direction. */ + INTEGRATOR_STATE_WRITE(state, ray, P) = sd->P; + INTEGRATOR_STATE_WRITE(state, ray, D) = normalize(bsdf_omega_in); + INTEGRATOR_STATE_WRITE(state, ray, tmin) = 0.0f; + INTEGRATOR_STATE_WRITE(state, ray, tmax) = FLT_MAX; #ifdef __RAY_DIFFERENTIALS__ - INTEGRATOR_STATE_WRITE(state, ray, dP) = differential_make_compact(sd->dP); - INTEGRATOR_STATE_WRITE(state, ray, dD) = differential_make_compact(bsdf_domega_in); + INTEGRATOR_STATE_WRITE(state, ray, dP) = differential_make_compact(sd->dP); + INTEGRATOR_STATE_WRITE(state, ray, dD) = differential_make_compact(bsdf_domega_in); #endif + } /* Update throughput. */ float3 throughput = INTEGRATOR_STATE(state, path, throughput); @@ -349,12 +354,8 @@ ccl_device_forceinline int integrate_surface_bsdf_bssrdf_bounce( } /* Update path state */ - if (label & LABEL_TRANSPARENT) { - INTEGRATOR_STATE_WRITE(state, path, mis_ray_t) += sd->ray_length; - } - else { + if (!(label & LABEL_TRANSPARENT)) { INTEGRATOR_STATE_WRITE(state, path, mis_ray_pdf) = bsdf_pdf; - INTEGRATOR_STATE_WRITE(state, path, mis_ray_t) = 0.0f; INTEGRATOR_STATE_WRITE(state, path, min_ray_pdf) = fminf( bsdf_pdf, INTEGRATOR_STATE(state, path, min_ray_pdf)); } @@ -371,17 +372,8 @@ ccl_device_forceinline int integrate_surface_volume_only_bounce(IntegratorState return LABEL_NONE; } - /* Setup ray position, direction stays unchanged. */ - INTEGRATOR_STATE_WRITE(state, ray, P) = sd->P; - - /* Clipping works through transparent. */ - INTEGRATOR_STATE_WRITE(state, ray, t) -= sd->ray_length; - -# ifdef __RAY_DIFFERENTIALS__ - INTEGRATOR_STATE_WRITE(state, ray, dP) = differential_make_compact(sd->dP); -# endif - - INTEGRATOR_STATE_WRITE(state, path, mis_ray_t) += sd->ray_length; + /* Only modify start distance. */ + INTEGRATOR_STATE_WRITE(state, ray, tmin) = intersection_t_offset(sd->ray_length); return LABEL_TRANSMIT | LABEL_TRANSPARENT; } @@ -432,7 +424,8 @@ ccl_device_forceinline void integrate_surface_ao(KernelGlobals kg, Ray ray ccl_optional_struct_init; ray.P = shadow_ray_offset(kg, sd, ao_D, &skip_self); ray.D = ao_D; - ray.t = kernel_data.integrator.ao_bounces_distance; + ray.tmin = 0.0f; + ray.tmax = kernel_data.integrator.ao_bounces_distance; ray.time = sd->time; ray.self.object = (skip_self) ? sd->object : OBJECT_NONE; ray.self.prim = (skip_self) ? sd->prim : PRIM_NONE; @@ -616,7 +609,7 @@ ccl_device_forceinline void integrator_shade_surface(KernelGlobals kg, kg, state, current_kernel, DEVICE_KERNEL_INTEGRATOR_INTERSECT_SUBSURFACE); } else { - kernel_assert(INTEGRATOR_STATE(state, ray, t) != 0.0f); + kernel_assert(INTEGRATOR_STATE(state, ray, tmax) != 0.0f); integrator_path_next(kg, state, current_kernel, DEVICE_KERNEL_INTEGRATOR_INTERSECT_CLOSEST); } } diff --git a/intern/cycles/kernel/integrator/shade_volume.h b/intern/cycles/kernel/integrator/shade_volume.h index 683c031f0d9..4aab097a7d8 100644 --- a/intern/cycles/kernel/integrator/shade_volume.h +++ b/intern/cycles/kernel/integrator/shade_volume.h @@ -114,7 +114,8 @@ ccl_device_inline bool volume_shader_sample(KernelGlobals kg, ccl_device_forceinline void volume_step_init(KernelGlobals kg, ccl_private const RNGState *rng_state, const float object_step_size, - float t, + const float tmin, + const float tmax, ccl_private float *step_size, ccl_private float *step_shade_offset, ccl_private float *steps_offset, @@ -122,7 +123,7 @@ ccl_device_forceinline void volume_step_init(KernelGlobals kg, { if (object_step_size == FLT_MAX) { /* Homogeneous volume. */ - *step_size = t; + *step_size = tmax - tmin; *step_shade_offset = 0.0f; *steps_offset = 1.0f; *max_steps = 1; @@ -130,6 +131,7 @@ ccl_device_forceinline void volume_step_init(KernelGlobals kg, else { /* Heterogeneous volume. */ *max_steps = kernel_data.integrator.volume_max_steps; + const float t = tmax - tmin; float step = min(object_step_size, t); /* compute exact steps in advance for malloc */ @@ -165,7 +167,7 @@ ccl_device void volume_shadow_homogeneous(KernelGlobals kg, IntegratorState stat float3 sigma_t = zero_float3(); if (shadow_volume_shader_sample(kg, state, sd, &sigma_t)) { - *throughput *= volume_color_transmittance(sigma_t, ray->t); + *throughput *= volume_color_transmittance(sigma_t, ray->tmax - ray->tmin); } } # endif @@ -194,7 +196,8 @@ ccl_device void volume_shadow_heterogeneous(KernelGlobals kg, volume_step_init(kg, &rng_state, object_step_size, - ray->t, + ray->tmin, + ray->tmax, &step_size, &step_shade_offset, &unused, @@ -202,13 +205,13 @@ ccl_device void volume_shadow_heterogeneous(KernelGlobals kg, const float steps_offset = 1.0f; /* compute extinction at the start */ - float t = 0.0f; + float t = ray->tmin; float3 sum = zero_float3(); for (int i = 0; i < max_steps; i++) { /* advance to new position */ - float new_t = min(ray->t, (i + steps_offset) * step_size); + float new_t = min(ray->tmax, ray->tmin + (i + steps_offset) * step_size); float dt = new_t - t; float3 new_P = ray->P + ray->D * (t + dt * step_shade_offset); @@ -233,7 +236,7 @@ ccl_device void volume_shadow_heterogeneous(KernelGlobals kg, /* stop if at the end of the volume */ t = new_t; - if (t == ray->t) { + if (t == ray->tmax) { /* Update throughput in case we haven't done it above */ tp = *throughput * exp(sum); break; @@ -257,15 +260,16 @@ ccl_device float volume_equiangular_sample(ccl_private const Ray *ccl_restrict r const float xi, ccl_private float *pdf) { - const float t = ray->t; + const float tmin = ray->tmin; + const float tmax = ray->tmax; const float delta = dot((light_P - ray->P), ray->D); const float D = safe_sqrtf(len_squared(light_P - ray->P) - delta * delta); if (UNLIKELY(D == 0.0f)) { *pdf = 0.0f; return 0.0f; } - const float theta_a = -atan2f(delta, D); - const float theta_b = atan2f(t - delta, D); + const float theta_a = atan2f(tmin - delta, D); + const float theta_b = atan2f(tmax - delta, D); const float t_ = D * tanf((xi * theta_b) + (1 - xi) * theta_a); if (UNLIKELY(theta_b == theta_a)) { *pdf = 0.0f; @@ -273,7 +277,7 @@ ccl_device float volume_equiangular_sample(ccl_private const Ray *ccl_restrict r } *pdf = D / ((theta_b - theta_a) * (D * D + t_ * t_)); - return min(t, delta + t_); /* min is only for float precision errors */ + return clamp(delta + t_, tmin, tmax); /* clamp is only for float precision errors */ } ccl_device float volume_equiangular_pdf(ccl_private const Ray *ccl_restrict ray, @@ -286,11 +290,12 @@ ccl_device float volume_equiangular_pdf(ccl_private const Ray *ccl_restrict ray, return 0.0f; } - const float t = ray->t; + const float tmin = ray->tmin; + const float tmax = ray->tmax; const float t_ = sample_t - delta; - const float theta_a = -atan2f(delta, D); - const float theta_b = atan2f(t - delta, D); + const float theta_a = atan2f(tmin - delta, D); + const float theta_b = atan2f(tmax - delta, D); if (UNLIKELY(theta_b == theta_a)) { return 0.0f; } @@ -310,11 +315,12 @@ ccl_device float volume_equiangular_cdf(ccl_private const Ray *ccl_restrict ray, return 0.0f; } - const float t = ray->t; + const float tmin = ray->tmin; + const float tmax = ray->tmax; const float t_ = sample_t - delta; - const float theta_a = -atan2f(delta, D); - const float theta_b = atan2f(t - delta, D); + const float theta_a = atan2f(tmin - delta, D); + const float theta_b = atan2f(tmax - delta, D); if (UNLIKELY(theta_b == theta_a)) { return 0.0f; } @@ -390,8 +396,8 @@ ccl_device float3 volume_emission_integrate(ccl_private VolumeShaderCoefficients typedef struct VolumeIntegrateState { /* Volume segment extents. */ - float start_t; - float end_t; + float tmin; + float tmax; /* If volume is absorption-only up to this point, and no probabilistic * scattering or termination has been used yet. */ @@ -426,9 +432,9 @@ ccl_device_forceinline void volume_integrate_step_scattering( /* Equiangular sampling for direct lighting. */ if (vstate.direct_sample_method == VOLUME_SAMPLE_EQUIANGULAR && !result.direct_scatter) { - if (result.direct_t >= vstate.start_t && result.direct_t <= vstate.end_t && + if (result.direct_t >= vstate.tmin && result.direct_t <= vstate.tmax && vstate.equiangular_pdf > VOLUME_SAMPLE_PDF_CUTOFF) { - const float new_dt = result.direct_t - vstate.start_t; + const float new_dt = result.direct_t - vstate.tmin; const float3 new_transmittance = volume_color_transmittance(coeff.sigma_t, new_dt); result.direct_scatter = true; @@ -458,7 +464,7 @@ ccl_device_forceinline void volume_integrate_step_scattering( /* compute sampling distance */ const float sample_sigma_t = volume_channel_get(coeff.sigma_t, channel); const float new_dt = -logf(1.0f - vstate.rscatter) / sample_sigma_t; - const float new_t = vstate.start_t + new_dt; + const float new_t = vstate.tmin + new_dt; /* transmittance and pdf */ const float3 new_transmittance = volume_color_transmittance(coeff.sigma_t, new_dt); @@ -528,7 +534,8 @@ ccl_device_forceinline void volume_integrate_heterogeneous( volume_step_init(kg, rng_state, object_step_size, - ray->t, + ray->tmin, + ray->tmax, &step_size, &step_shade_offset, &steps_offset, @@ -536,8 +543,8 @@ ccl_device_forceinline void volume_integrate_heterogeneous( /* Initialize volume integration state. */ VolumeIntegrateState vstate ccl_optional_struct_init; - vstate.start_t = 0.0f; - vstate.end_t = 0.0f; + vstate.tmin = ray->tmin; + vstate.tmax = ray->tmin; vstate.absorption_only = true; vstate.rscatter = path_state_rng_1D(kg, rng_state, PRNG_SCATTER_DISTANCE); vstate.rphase = path_state_rng_1D(kg, rng_state, PRNG_PHASE_CHANNEL); @@ -578,8 +585,8 @@ ccl_device_forceinline void volume_integrate_heterogeneous( for (int i = 0; i < max_steps; i++) { /* Advance to new position */ - vstate.end_t = min(ray->t, (i + steps_offset) * step_size); - const float shade_t = vstate.start_t + (vstate.end_t - vstate.start_t) * step_shade_offset; + vstate.tmax = min(ray->tmax, ray->tmin + (i + steps_offset) * step_size); + const float shade_t = vstate.tmin + (vstate.tmax - vstate.tmin) * step_shade_offset; sd->P = ray->P + ray->D * shade_t; /* compute segment */ @@ -588,7 +595,7 @@ ccl_device_forceinline void volume_integrate_heterogeneous( const int closure_flag = sd->flag; /* Evaluate transmittance over segment. */ - const float dt = (vstate.end_t - vstate.start_t); + const float dt = (vstate.tmax - vstate.tmin); const float3 transmittance = (closure_flag & SD_EXTINCTION) ? volume_color_transmittance(coeff.sigma_t, dt) : one_float3(); @@ -645,8 +652,8 @@ ccl_device_forceinline void volume_integrate_heterogeneous( } /* Stop if at the end of the volume. */ - vstate.start_t = vstate.end_t; - if (vstate.start_t == ray->t) { + vstate.tmin = vstate.tmax; + if (vstate.tmin == ray->tmax) { break; } } @@ -880,7 +887,8 @@ ccl_device_forceinline bool integrate_volume_phase_scatter( /* Setup ray. */ INTEGRATOR_STATE_WRITE(state, ray, P) = sd->P; INTEGRATOR_STATE_WRITE(state, ray, D) = normalize(phase_omega_in); - INTEGRATOR_STATE_WRITE(state, ray, t) = FLT_MAX; + INTEGRATOR_STATE_WRITE(state, ray, tmin) = 0.0f; + INTEGRATOR_STATE_WRITE(state, ray, tmax) = FLT_MAX; # ifdef __RAY_DIFFERENTIALS__ INTEGRATOR_STATE_WRITE(state, ray, dP) = differential_make_compact(sd->dP); INTEGRATOR_STATE_WRITE(state, ray, dD) = differential_make_compact(phase_domega_in); @@ -901,7 +909,6 @@ ccl_device_forceinline bool integrate_volume_phase_scatter( /* Update path state */ INTEGRATOR_STATE_WRITE(state, path, mis_ray_pdf) = phase_pdf; - INTEGRATOR_STATE_WRITE(state, path, mis_ray_t) = 0.0f; INTEGRATOR_STATE_WRITE(state, path, min_ray_pdf) = fminf( phase_pdf, INTEGRATOR_STATE(state, path, min_ray_pdf)); @@ -1021,7 +1028,7 @@ ccl_device void integrator_shade_volume(KernelGlobals kg, integrator_state_read_isect(kg, state, &isect); /* Set ray length to current segment. */ - ray.t = (isect.prim != PRIM_NONE) ? isect.t : FLT_MAX; + ray.tmax = (isect.prim != PRIM_NONE) ? isect.t : FLT_MAX; /* Clean volume stack for background rays. */ if (isect.prim == PRIM_NONE) { diff --git a/intern/cycles/kernel/integrator/shadow_state_template.h b/intern/cycles/kernel/integrator/shadow_state_template.h index eaee65ada40..c340467606d 100644 --- a/intern/cycles/kernel/integrator/shadow_state_template.h +++ b/intern/cycles/kernel/integrator/shadow_state_template.h @@ -47,7 +47,8 @@ KERNEL_STRUCT_END(shadow_path) KERNEL_STRUCT_BEGIN(shadow_ray) KERNEL_STRUCT_MEMBER(shadow_ray, packed_float3, P, KERNEL_FEATURE_PATH_TRACING) KERNEL_STRUCT_MEMBER(shadow_ray, packed_float3, D, KERNEL_FEATURE_PATH_TRACING) -KERNEL_STRUCT_MEMBER(shadow_ray, float, t, KERNEL_FEATURE_PATH_TRACING) +KERNEL_STRUCT_MEMBER(shadow_ray, float, tmin, KERNEL_FEATURE_PATH_TRACING) +KERNEL_STRUCT_MEMBER(shadow_ray, float, tmax, KERNEL_FEATURE_PATH_TRACING) KERNEL_STRUCT_MEMBER(shadow_ray, float, time, KERNEL_FEATURE_PATH_TRACING) KERNEL_STRUCT_MEMBER(shadow_ray, float, dP, KERNEL_FEATURE_PATH_TRACING) KERNEL_STRUCT_MEMBER(shadow_ray, int, object, KERNEL_FEATURE_PATH_TRACING) diff --git a/intern/cycles/kernel/integrator/state_template.h b/intern/cycles/kernel/integrator/state_template.h index e7e6db037b0..5c2af131945 100644 --- a/intern/cycles/kernel/integrator/state_template.h +++ b/intern/cycles/kernel/integrator/state_template.h @@ -37,11 +37,10 @@ KERNEL_STRUCT_MEMBER(path, uint32_t, flag, KERNEL_FEATURE_PATH_TRACING) /* enum PathRayMNEE */ KERNEL_STRUCT_MEMBER(path, uint8_t, mnee, KERNEL_FEATURE_PATH_TRACING) /* Multiple importance sampling - * The PDF of BSDF sampling at the last scatter point, and distance to the - * last scatter point minus the last ray segment. This distance lets us - * compute the complete distance through transparent surfaces and volumes. */ + * The PDF of BSDF sampling at the last scatter point, which is at ray distance + * zero and distance. Note that transparency and volume attenuation increase + * the ray tmin but keep P unmodified so that this works. */ KERNEL_STRUCT_MEMBER(path, float, mis_ray_pdf, KERNEL_FEATURE_PATH_TRACING) -KERNEL_STRUCT_MEMBER(path, float, mis_ray_t, KERNEL_FEATURE_PATH_TRACING) /* Filter glossy. */ KERNEL_STRUCT_MEMBER(path, float, min_ray_pdf, KERNEL_FEATURE_PATH_TRACING) /* Continuation probability for path termination. */ @@ -63,7 +62,8 @@ KERNEL_STRUCT_END(path) KERNEL_STRUCT_BEGIN(ray) KERNEL_STRUCT_MEMBER(ray, packed_float3, P, KERNEL_FEATURE_PATH_TRACING) KERNEL_STRUCT_MEMBER(ray, packed_float3, D, KERNEL_FEATURE_PATH_TRACING) -KERNEL_STRUCT_MEMBER(ray, float, t, KERNEL_FEATURE_PATH_TRACING) +KERNEL_STRUCT_MEMBER(ray, float, tmin, KERNEL_FEATURE_PATH_TRACING) +KERNEL_STRUCT_MEMBER(ray, float, tmax, KERNEL_FEATURE_PATH_TRACING) KERNEL_STRUCT_MEMBER(ray, float, time, KERNEL_FEATURE_PATH_TRACING) KERNEL_STRUCT_MEMBER(ray, float, dP, KERNEL_FEATURE_PATH_TRACING) KERNEL_STRUCT_MEMBER(ray, float, dD, KERNEL_FEATURE_PATH_TRACING) diff --git a/intern/cycles/kernel/integrator/state_util.h b/intern/cycles/kernel/integrator/state_util.h index 280db2d1aac..8dd58ad6bcd 100644 --- a/intern/cycles/kernel/integrator/state_util.h +++ b/intern/cycles/kernel/integrator/state_util.h @@ -17,7 +17,8 @@ ccl_device_forceinline void integrator_state_write_ray(KernelGlobals kg, { INTEGRATOR_STATE_WRITE(state, ray, P) = ray->P; INTEGRATOR_STATE_WRITE(state, ray, D) = ray->D; - INTEGRATOR_STATE_WRITE(state, ray, t) = ray->t; + INTEGRATOR_STATE_WRITE(state, ray, tmin) = ray->tmin; + INTEGRATOR_STATE_WRITE(state, ray, tmax) = ray->tmax; INTEGRATOR_STATE_WRITE(state, ray, time) = ray->time; INTEGRATOR_STATE_WRITE(state, ray, dP) = ray->dP; INTEGRATOR_STATE_WRITE(state, ray, dD) = ray->dD; @@ -29,7 +30,8 @@ ccl_device_forceinline void integrator_state_read_ray(KernelGlobals kg, { ray->P = INTEGRATOR_STATE(state, ray, P); ray->D = INTEGRATOR_STATE(state, ray, D); - ray->t = INTEGRATOR_STATE(state, ray, t); + ray->tmin = INTEGRATOR_STATE(state, ray, tmin); + ray->tmax = INTEGRATOR_STATE(state, ray, tmax); ray->time = INTEGRATOR_STATE(state, ray, time); ray->dP = INTEGRATOR_STATE(state, ray, dP); ray->dD = INTEGRATOR_STATE(state, ray, dD); @@ -42,7 +44,8 @@ ccl_device_forceinline void integrator_state_write_shadow_ray( { INTEGRATOR_STATE_WRITE(state, shadow_ray, P) = ray->P; INTEGRATOR_STATE_WRITE(state, shadow_ray, D) = ray->D; - INTEGRATOR_STATE_WRITE(state, shadow_ray, t) = ray->t; + INTEGRATOR_STATE_WRITE(state, shadow_ray, tmin) = ray->tmin; + INTEGRATOR_STATE_WRITE(state, shadow_ray, tmax) = ray->tmax; INTEGRATOR_STATE_WRITE(state, shadow_ray, time) = ray->time; INTEGRATOR_STATE_WRITE(state, shadow_ray, dP) = ray->dP; } @@ -53,7 +56,8 @@ ccl_device_forceinline void integrator_state_read_shadow_ray(KernelGlobals kg, { ray->P = INTEGRATOR_STATE(state, shadow_ray, P); ray->D = INTEGRATOR_STATE(state, shadow_ray, D); - ray->t = INTEGRATOR_STATE(state, shadow_ray, t); + ray->tmin = INTEGRATOR_STATE(state, shadow_ray, tmin); + ray->tmax = INTEGRATOR_STATE(state, shadow_ray, tmax); ray->time = INTEGRATOR_STATE(state, shadow_ray, time); ray->dP = INTEGRATOR_STATE(state, shadow_ray, dP); ray->dD = differential_zero_compact(); diff --git a/intern/cycles/kernel/integrator/subsurface.h b/intern/cycles/kernel/integrator/subsurface.h index ab26a2d93cc..2f96f215d8a 100644 --- a/intern/cycles/kernel/integrator/subsurface.h +++ b/intern/cycles/kernel/integrator/subsurface.h @@ -38,7 +38,8 @@ ccl_device int subsurface_bounce(KernelGlobals kg, /* Setup ray into surface. */ INTEGRATOR_STATE_WRITE(state, ray, P) = sd->P; INTEGRATOR_STATE_WRITE(state, ray, D) = bssrdf->N; - INTEGRATOR_STATE_WRITE(state, ray, t) = FLT_MAX; + INTEGRATOR_STATE_WRITE(state, ray, tmin) = 0.0f; + INTEGRATOR_STATE_WRITE(state, ray, tmax) = FLT_MAX; INTEGRATOR_STATE_WRITE(state, ray, dP) = differential_make_compact(sd->dP); INTEGRATOR_STATE_WRITE(state, ray, dD) = differential_zero_compact(); @@ -160,7 +161,7 @@ ccl_device_inline bool subsurface_scatter(KernelGlobals kg, IntegratorState stat /* Pretend ray is coming from the outside towards the exit point. This ensures * correct front/back facing normals. * TODO: find a more elegant solution? */ - ray.P += ray.D * ray.t * 2.0f; + ray.P += ray.D * ray.tmax * 2.0f; ray.D = -ray.D; integrator_state_write_isect(kg, state, &ss_isect.hits[0]); diff --git a/intern/cycles/kernel/integrator/subsurface_disk.h b/intern/cycles/kernel/integrator/subsurface_disk.h index ae857c50493..2836934f6dd 100644 --- a/intern/cycles/kernel/integrator/subsurface_disk.h +++ b/intern/cycles/kernel/integrator/subsurface_disk.h @@ -82,7 +82,8 @@ ccl_device_inline bool subsurface_disk(KernelGlobals kg, /* Create ray. */ ray.P = P + disk_N * disk_height + disk_P; ray.D = -disk_N; - ray.t = 2.0f * disk_height; + ray.tmin = 0.0f; + ray.tmax = 2.0f * disk_height; ray.dP = ray_dP; ray.dD = differential_zero_compact(); ray.time = time; @@ -188,7 +189,8 @@ ccl_device_inline bool subsurface_disk(KernelGlobals kg, ray.P = ray.P + ray.D * ss_isect.hits[hit].t; ray.D = ss_isect.Ng[hit]; - ray.t = 1.0f; + ray.tmin = 0.0f; + ray.tmax = 1.0f; return true; } diff --git a/intern/cycles/kernel/integrator/subsurface_random_walk.h b/intern/cycles/kernel/integrator/subsurface_random_walk.h index 8094bf7159e..c1691030817 100644 --- a/intern/cycles/kernel/integrator/subsurface_random_walk.h +++ b/intern/cycles/kernel/integrator/subsurface_random_walk.h @@ -195,7 +195,8 @@ ccl_device_inline bool subsurface_random_walk(KernelGlobals kg, /* Setup ray. */ ray.P = P; ray.D = D; - ray.t = FLT_MAX; + ray.tmin = 0.0f; + ray.tmax = FLT_MAX; ray.time = time; ray.dP = ray_dP; ray.dD = differential_zero_compact(); @@ -370,10 +371,10 @@ ccl_device_inline bool subsurface_random_walk(KernelGlobals kg, * chance of connecting to it. * TODO: Maybe use less than 10 times the mean free path? */ if (bounce == 0) { - ray.t = max(t, 10.0f / (reduce_min(sigma_t))); + ray.tmax = max(t, 10.0f / (reduce_min(sigma_t))); } else { - ray.t = t; + ray.tmax = t; /* After the first bounce the object can intersect the same surface again */ ray.self.object = OBJECT_NONE; ray.self.prim = PRIM_NONE; @@ -384,12 +385,12 @@ ccl_device_inline bool subsurface_random_walk(KernelGlobals kg, if (hit) { #ifdef __KERNEL_GPU_RAYTRACING__ /* t is always in world space with OptiX and MetalRT. */ - ray.t = ss_isect.hits[0].t; + ray.tmax = ss_isect.hits[0].t; #else /* Compute world space distance to surface hit. */ float3 D = transform_direction(&ob_itfm, ray.D); D = normalize(D) * ss_isect.hits[0].t; - ray.t = len(transform_direction(&ob_tfm, D)); + ray.tmax = len(transform_direction(&ob_tfm, D)); #endif } @@ -397,16 +398,16 @@ ccl_device_inline bool subsurface_random_walk(KernelGlobals kg, /* Check if we hit the opposite side. */ if (hit) { have_opposite_interface = true; - opposite_distance = dot(ray.P + ray.t * ray.D - P, -N); + opposite_distance = dot(ray.P + ray.tmax * ray.D - P, -N); } /* Apart from the opposite side check, we were supposed to only trace up to distance t, * so check if there would have been a hit in that case. */ - hit = ray.t < t; + hit = ray.tmax < t; } /* Use the distance to the exit point for the throughput update if we found one. */ if (hit) { - t = ray.t; + t = ray.tmax; } /* Advance to new scatter location. */ diff --git a/intern/cycles/kernel/light/light.h b/intern/cycles/kernel/light/light.h index 1e7a333d013..b939489bb18 100644 --- a/intern/cycles/kernel/light/light.h +++ b/intern/cycles/kernel/light/light.h @@ -270,31 +270,26 @@ ccl_device bool lights_intersect(KernelGlobals kg, if (type == LIGHT_SPOT) { /* Spot/Disk light. */ - const float mis_ray_t = INTEGRATOR_STATE(state, path, mis_ray_t); - const float3 ray_P = ray->P - ray->D * mis_ray_t; - const float3 lightP = make_float3(klight->co[0], klight->co[1], klight->co[2]); const float radius = klight->spot.radius; if (radius == 0.0f) { continue; } /* disk oriented normal */ - const float3 lightN = normalize(ray_P - lightP); + const float3 lightN = normalize(ray->P - lightP); /* One sided. */ if (dot(ray->D, lightN) >= 0.0f) { continue; } float3 P; - if (!ray_disk_intersect(ray->P, ray->D, ray->t, lightP, lightN, radius, &P, &t)) { + if (!ray_disk_intersect( + ray->P, ray->D, ray->tmin, ray->tmax, lightP, lightN, radius, &P, &t)) { continue; } } else if (type == LIGHT_POINT) { /* Sphere light (aka, aligned disk light). */ - const float mis_ray_t = INTEGRATOR_STATE(state, path, mis_ray_t); - const float3 ray_P = ray->P - ray->D * mis_ray_t; - const float3 lightP = make_float3(klight->co[0], klight->co[1], klight->co[2]); const float radius = klight->spot.radius; if (radius == 0.0f) { @@ -302,9 +297,10 @@ ccl_device bool lights_intersect(KernelGlobals kg, } /* disk oriented normal */ - const float3 lightN = normalize(ray_P - lightP); + const float3 lightN = normalize(ray->P - lightP); float3 P; - if (!ray_disk_intersect(ray->P, ray->D, ray->t, lightP, lightN, radius, &P, &t)) { + if (!ray_disk_intersect( + ray->P, ray->D, ray->tmin, ray->tmax, lightP, lightN, radius, &P, &t)) { continue; } } @@ -330,8 +326,19 @@ ccl_device bool lights_intersect(KernelGlobals kg, const float3 light_P = make_float3(klight->co[0], klight->co[1], klight->co[2]); float3 P; - if (!ray_quad_intersect( - ray->P, ray->D, 0.0f, ray->t, light_P, axisu, axisv, Ng, &P, &t, &u, &v, is_round)) { + if (!ray_quad_intersect(ray->P, + ray->D, + ray->tmin, + ray->tmax, + light_P, + axisu, + axisv, + Ng, + &P, + &t, + &u, + &v, + is_round)) { continue; } } @@ -775,7 +782,8 @@ ccl_device_forceinline void triangle_light_sample(KernelGlobals kg, ls->D = z * B + safe_sqrtf(1.0f - z * z) * safe_normalize(C_ - dot(C_, B) * B); /* calculate intersection with the planar triangle */ - if (!ray_triangle_intersect(P, ls->D, FLT_MAX, V[0], V[1], V[2], &ls->u, &ls->v, &ls->t)) { + if (!ray_triangle_intersect( + P, ls->D, 0.0f, FLT_MAX, V[0], V[1], V[2], &ls->u, &ls->v, &ls->t)) { ls->pdf = 0.0f; return; } diff --git a/intern/cycles/kernel/light/sample.h b/intern/cycles/kernel/light/sample.h index 5cf7dce683a..210bb1b35c2 100644 --- a/intern/cycles/kernel/light/sample.h +++ b/intern/cycles/kernel/light/sample.h @@ -227,23 +227,24 @@ ccl_device_inline void shadow_ray_setup(ccl_private const ShaderData *ccl_restri if (ls->shader & SHADER_CAST_SHADOW) { /* setup ray */ ray->P = P; + ray->tmin = 0.0f; if (ls->t == FLT_MAX) { /* distant light */ ray->D = ls->D; - ray->t = ls->t; + ray->tmax = ls->t; } else { /* other lights, avoid self-intersection */ ray->D = ls->P - P; - ray->D = normalize_len(ray->D, &ray->t); + ray->D = normalize_len(ray->D, &ray->tmax); } } else { /* signal to not cast shadow ray */ ray->P = zero_float3(); ray->D = zero_float3(); - ray->t = 0.0f; + ray->tmax = 0.0f; } ray->dP = differential_make_compact(sd->dP); diff --git a/intern/cycles/kernel/osl/services.cpp b/intern/cycles/kernel/osl/services.cpp index 78c23b858c4..6b7981b7f3a 100644 --- a/intern/cycles/kernel/osl/services.cpp +++ b/intern/cycles/kernel/osl/services.cpp @@ -1094,10 +1094,8 @@ bool OSLRenderServices::get_background_attribute(const KernelGlobalsCPU *kg, ndc[0] = camera_world_to_ndc(kg, sd, sd->ray_P); if (derivatives) { - ndc[1] = camera_world_to_ndc(kg, sd, sd->ray_P + make_float3(sd->ray_dP, 0.0f, 0.0f)) - - ndc[0]; - ndc[2] = camera_world_to_ndc(kg, sd, sd->ray_P + make_float3(0.0f, sd->ray_dP, 0.0f)) - - ndc[0]; + ndc[1] = zero_float3(); + ndc[2] = zero_float3(); } } else { @@ -1671,7 +1669,8 @@ bool OSLRenderServices::trace(TraceOpt &options, ray.P = TO_FLOAT3(P); ray.D = TO_FLOAT3(R); - ray.t = (options.maxdist == 1.0e30f) ? FLT_MAX : options.maxdist - options.mindist; + ray.tmin = 0.0f; + ray.tmax = (options.maxdist == 1.0e30f) ? FLT_MAX : options.maxdist - options.mindist; ray.time = sd->time; ray.self.object = OBJECT_NONE; ray.self.prim = PRIM_NONE; diff --git a/intern/cycles/kernel/svm/ao.h b/intern/cycles/kernel/svm/ao.h index e66c535824c..c57c68d6230 100644 --- a/intern/cycles/kernel/svm/ao.h +++ b/intern/cycles/kernel/svm/ao.h @@ -59,7 +59,8 @@ ccl_device float svm_ao( Ray ray; ray.P = sd->P; ray.D = D.x * T + D.y * B + D.z * N; - ray.t = max_dist; + ray.tmin = 0.0f; + ray.tmax = max_dist; ray.time = sd->time; ray.self.object = sd->object; ray.self.prim = sd->prim; diff --git a/intern/cycles/kernel/svm/bevel.h b/intern/cycles/kernel/svm/bevel.h index 790437d8e82..4617a056a52 100644 --- a/intern/cycles/kernel/svm/bevel.h +++ b/intern/cycles/kernel/svm/bevel.h @@ -179,7 +179,8 @@ ccl_device float3 svm_bevel( Ray ray ccl_optional_struct_init; ray.P = sd->P + disk_N * disk_height + disk_P; ray.D = -disk_N; - ray.t = 2.0f * disk_height; + ray.tmin = 0.0f; + ray.tmax = 2.0f * disk_height; ray.dP = differential_zero_compact(); ray.dD = differential_zero_compact(); ray.time = sd->time; diff --git a/intern/cycles/kernel/svm/tex_coord.h b/intern/cycles/kernel/svm/tex_coord.h index d9138796c45..2a0130e11d4 100644 --- a/intern/cycles/kernel/svm/tex_coord.h +++ b/intern/cycles/kernel/svm/tex_coord.h @@ -138,7 +138,7 @@ ccl_device_noinline int svm_node_tex_coord_bump_dx(KernelGlobals kg, case NODE_TEXCO_WINDOW: { if ((path_flag & PATH_RAY_CAMERA) && sd->object == OBJECT_NONE && kernel_data.cam.type == CAMERA_ORTHOGRAPHIC) - data = camera_world_to_ndc(kg, sd, sd->ray_P + make_float3(sd->ray_dP, 0.0f, 0.0f)); + data = camera_world_to_ndc(kg, sd, sd->ray_P); else data = camera_world_to_ndc(kg, sd, sd->P + sd->dP.dx); data.z = 0.0f; @@ -223,7 +223,7 @@ ccl_device_noinline int svm_node_tex_coord_bump_dy(KernelGlobals kg, case NODE_TEXCO_WINDOW: { if ((path_flag & PATH_RAY_CAMERA) && sd->object == OBJECT_NONE && kernel_data.cam.type == CAMERA_ORTHOGRAPHIC) - data = camera_world_to_ndc(kg, sd, sd->ray_P + make_float3(0.0f, sd->ray_dP, 0.0f)); + data = camera_world_to_ndc(kg, sd, sd->ray_P); else data = camera_world_to_ndc(kg, sd, sd->P + sd->dP.dy); data.z = 0.0f; diff --git a/intern/cycles/kernel/types.h b/intern/cycles/kernel/types.h index 62ac75e5e4d..05320deed19 100644 --- a/intern/cycles/kernel/types.h +++ b/intern/cycles/kernel/types.h @@ -535,7 +535,8 @@ typedef struct RaySelfPrimitives { typedef struct Ray { float3 P; /* origin */ float3 D; /* direction */ - float t; /* length of the ray */ + float tmin; /* start distance */ + float tmax; /* end distance */ float time; /* time (for motion blur) */ RaySelfPrimitives self; diff --git a/intern/cycles/util/math_intersect.h b/intern/cycles/util/math_intersect.h index b0de0b25a45..c5b1cd51030 100644 --- a/intern/cycles/util/math_intersect.h +++ b/intern/cycles/util/math_intersect.h @@ -10,7 +10,8 @@ CCL_NAMESPACE_BEGIN ccl_device bool ray_sphere_intersect(float3 ray_P, float3 ray_D, - float ray_t, + float ray_tmin, + float ray_tmax, float3 sphere_P, float sphere_radius, ccl_private float3 *isect_P, @@ -33,7 +34,7 @@ ccl_device bool ray_sphere_intersect(float3 ray_P, return false; } const float t = tp - sqrtf(radiussq - dsq); /* pythagoras */ - if (t < ray_t) { + if (t > ray_tmin && t < ray_tmax) { *isect_t = t; *isect_P = ray_P + ray_D * t; return true; @@ -44,7 +45,8 @@ ccl_device bool ray_sphere_intersect(float3 ray_P, ccl_device bool ray_aligned_disk_intersect(float3 ray_P, float3 ray_D, - float ray_t, + float ray_tmin, + float ray_tmax, float3 disk_P, float disk_radius, ccl_private float3 *isect_P, @@ -59,7 +61,7 @@ ccl_device bool ray_aligned_disk_intersect(float3 ray_P, } /* Compute t to intersection point. */ const float t = -disk_t / div; - if (t < 0.0f || t > ray_t) { + if (!(t > ray_tmin && t < ray_tmax)) { return false; } /* Test if within radius. */ @@ -74,7 +76,8 @@ ccl_device bool ray_aligned_disk_intersect(float3 ray_P, ccl_device bool ray_disk_intersect(float3 ray_P, float3 ray_D, - float ray_t, + float ray_tmin, + float ray_tmax, float3 disk_P, float3 disk_N, float disk_radius, @@ -92,7 +95,8 @@ ccl_device bool ray_disk_intersect(float3 ray_P, } float3 P = ray_P + t * ray_D; float3 T = P - disk_P; - if (dot(T, T) < sqr(disk_radius) /*&& t > 0.f*/ && t <= ray_t) { + + if (dot(T, T) < sqr(disk_radius) && (t > ray_tmin && t < ray_tmax)) { *isect_P = ray_P + t * ray_D; *isect_t = t; return true; @@ -103,7 +107,8 @@ ccl_device bool ray_disk_intersect(float3 ray_P, ccl_device_forceinline bool ray_triangle_intersect(float3 ray_P, float3 ray_dir, - float ray_t, + float ray_tmin, + float ray_tmax, const float3 tri_a, const float3 tri_b, const float3 tri_c, @@ -149,16 +154,14 @@ ccl_device_forceinline bool ray_triangle_intersect(float3 ray_P, /* Perform depth test. */ const float T = dot3(v0, Ng); - const int sign_den = (__float_as_int(den) & 0x80000000); - const float sign_T = xor_signmask(T, sign_den); - if ((sign_T < 0.0f) || (sign_T > ray_t * xor_signmask(den, sign_den))) { + const float t = T / den; + if (!(t >= ray_tmin && t <= ray_tmax)) { return false; } - const float inv_den = 1.0f / den; - *isect_u = U * inv_den; - *isect_v = V * inv_den; - *isect_t = T * inv_den; + *isect_u = U / den; + *isect_v = V / den; + *isect_t = t; return true; #undef dot3 @@ -171,8 +174,8 @@ ccl_device_forceinline bool ray_triangle_intersect(float3 ray_P, */ ccl_device bool ray_quad_intersect(float3 ray_P, float3 ray_D, - float ray_mint, - float ray_maxt, + float ray_tmin, + float ray_tmax, float3 quad_P, float3 quad_u, float3 quad_v, @@ -185,7 +188,7 @@ ccl_device bool ray_quad_intersect(float3 ray_P, { /* Perform intersection test. */ float t = -(dot(ray_P, quad_n) - dot(quad_P, quad_n)) / dot(ray_D, quad_n); - if (t < ray_mint || t > ray_maxt) { + if (!(t > ray_tmin && t < ray_tmax)) { return false; } const float3 hit = ray_P + t * ray_D; -- cgit v1.2.3 From 92a99c14965905e73f049bc1f92b597a903977fc Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Fri, 15 Jul 2022 18:58:35 +0200 Subject: Fix Eevee backround render crash after recent changes from D15463 Backend initialization needs to be delayed until after the OpenGL context is created. This worked fine in foreground mode because the OpenGL context already exists for the window at the point GPU_backend_init_once was called, but not for background mode. Create the backend just in time in GPU_context_create as before, and automatically free it when the last context id discarded. But check if any GPU backend is supported before creating the OpenGL context. Ref D15463, D15465 --- source/blender/gpu/GPU_context.h | 12 ++--- source/blender/gpu/intern/gpu_context.cc | 51 ++++++++++++++-------- source/blender/gpu/tests/gpu_testing.cc | 2 - source/blender/windowmanager/intern/wm_init_exit.c | 3 +- 4 files changed, 37 insertions(+), 31 deletions(-) diff --git a/source/blender/gpu/GPU_context.h b/source/blender/gpu/GPU_context.h index c81296093a1..9d92ea2cad9 100644 --- a/source/blender/gpu/GPU_context.h +++ b/source/blender/gpu/GPU_context.h @@ -17,14 +17,10 @@ extern "C" { #endif -/* GPU backends abstract the differences between different APIs. These must be - * initialized before creating contexts, and deleted after the last context is - * discarded. GPU_context_create automatically initializes a backend if none - * exists yet. */ -bool GPU_backend_init_once(void); -void GPU_backend_exit(void); -bool GPU_backend_supported(eGPUBackendType type); - +/* GPU backends abstract the differences between different APIs. GPU_context_create + * automatically initializes the backend, and GPU_context_discard frees it when there + * are no more contexts. */ +bool GPU_backend_supported(void); eGPUBackendType GPU_backend_get_type(void); /** Opaque type hiding blender::gpu::Context. */ diff --git a/source/blender/gpu/intern/gpu_context.cc b/source/blender/gpu/intern/gpu_context.cc index 5ae020e45a4..20d9208a199 100644 --- a/source/blender/gpu/intern/gpu_context.cc +++ b/source/blender/gpu/intern/gpu_context.cc @@ -44,6 +44,12 @@ using namespace blender::gpu; static thread_local Context *active_ctx = nullptr; +static std::mutex backend_users_mutex; +static int num_backend_users = 0; + +static void gpu_backend_create(); +static void gpu_backend_discard(); + /* -------------------------------------------------------------------- */ /** \name gpu::Context methods * \{ */ @@ -86,7 +92,14 @@ Context *Context::get() GPUContext *GPU_context_create(void *ghost_window) { - GPU_backend_init_once(); + { + std::scoped_lock lock(backend_users_mutex); + if (num_backend_users == 0) { + /* Automatically create backend when first context is created. */ + gpu_backend_create(); + } + num_backend_users++; + } Context *ctx = GPUBackend::get()->context_alloc(ghost_window); @@ -99,6 +112,16 @@ void GPU_context_discard(GPUContext *ctx_) Context *ctx = unwrap(ctx_); delete ctx; active_ctx = nullptr; + + { + std::scoped_lock lock(backend_users_mutex); + num_backend_users--; + BLI_assert(num_backend_users >= 0); + if (num_backend_users == 0) { + /* Discard backend when last context is discarded. */ + gpu_backend_discard(); + } + } } void GPU_context_active_set(GPUContext *ctx_) @@ -189,11 +212,12 @@ void GPU_render_step() /** \name Backend selection * \{ */ +static const eGPUBackendType g_backend_type = GPU_BACKEND_OPENGL; static GPUBackend *g_backend = nullptr; -bool GPU_backend_supported(eGPUBackendType type) +bool GPU_backend_supported(void) { - switch (type) { + switch (g_backend_type) { case GPU_BACKEND_OPENGL: #ifdef WITH_OPENGL_BACKEND return true; @@ -212,21 +236,12 @@ bool GPU_backend_supported(eGPUBackendType type) } } -bool GPU_backend_init_once() +static void gpu_backend_create() { - if (GPUBackend::get() != nullptr) { - return true; - } - - const eGPUBackendType backend_type = GPU_BACKEND_OPENGL; - if (!GPU_backend_supported(backend_type)) { - return false; - } + BLI_assert(g_backend == nullptr); + BLI_assert(GPU_backend_supported(g_backend_type)); - static std::mutex backend_init_mutex; - std::scoped_lock lock(backend_init_mutex); - - switch (backend_type) { + switch (g_backend_type) { #ifdef WITH_OPENGL_BACKEND case GPU_BACKEND_OPENGL: g_backend = new GLBackend; @@ -241,8 +256,6 @@ bool GPU_backend_init_once() BLI_assert(0); break; } - - return true; } void gpu_backend_delete_resources() @@ -251,7 +264,7 @@ void gpu_backend_delete_resources() g_backend->delete_resources(); } -void GPU_backend_exit() +void gpu_backend_discard() { /* TODO: assert no resource left. */ delete g_backend; diff --git a/source/blender/gpu/tests/gpu_testing.cc b/source/blender/gpu/tests/gpu_testing.cc index 5a2ad893360..224a9afcf59 100644 --- a/source/blender/gpu/tests/gpu_testing.cc +++ b/source/blender/gpu/tests/gpu_testing.cc @@ -17,7 +17,6 @@ void GPUTest::SetUp() GHOST_GLSettings glSettings = {0}; CLG_init(); ghost_system = GHOST_CreateSystem(); - GPU_backend_init_once(); ghost_context = GHOST_CreateOpenGLContext(ghost_system, glSettings); GHOST_ActivateOpenGLContext(ghost_context); context = GPU_context_create(nullptr); @@ -29,7 +28,6 @@ void GPUTest::TearDown() GPU_exit(); GPU_context_discard(context); GHOST_DisposeOpenGLContext(ghost_system, ghost_context); - GPU_backend_exit(); GHOST_DisposeSystem(ghost_system); CLG_exit(); } diff --git a/source/blender/windowmanager/intern/wm_init_exit.c b/source/blender/windowmanager/intern/wm_init_exit.c index 7324abfd096..7f5ec77e16d 100644 --- a/source/blender/windowmanager/intern/wm_init_exit.c +++ b/source/blender/windowmanager/intern/wm_init_exit.c @@ -169,7 +169,7 @@ void WM_init_opengl(void) wm_ghost_init(NULL); } - if (!GPU_backend_init_once()) { + if (!GPU_backend_supported()) { return; } @@ -613,7 +613,6 @@ void WM_exit_ex(bContext *C, const bool do_python) else { UI_exit(); } - GPU_backend_exit(); BKE_blender_userdef_data_free(&U, false); -- cgit v1.2.3 From b9854372832ac43978f6d999891944dbb7f2e752 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Fri, 15 Jul 2022 20:10:42 +0200 Subject: Fix workbench background render broken after recent changes from D15463 For Eevee the light baking can initialize OpenGL earlier, but for workbench we can't assume the backend exists here already. --- source/blender/render/intern/engine.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/render/intern/engine.c b/source/blender/render/intern/engine.c index 113af393706..266e66092b8 100644 --- a/source/blender/render/intern/engine.c +++ b/source/blender/render/intern/engine.c @@ -952,7 +952,7 @@ bool RE_engine_render(Render *re, bool do_all) re->draw_lock(re->dlh, true); } - if ((type->flag & RE_USE_GPU_CONTEXT) && (GPU_backend_get_type() == GPU_BACKEND_NONE)) { + if ((type->flag & RE_USE_GPU_CONTEXT) && !GPU_backend_supported()) { /* Clear UI drawing locks. */ if (re->draw_lock) { re->draw_lock(re->dlh, false); -- cgit v1.2.3 From bf49e6040caac1e975982375731c348a1fcb11c1 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Sat, 16 Jul 2022 16:12:48 +1000 Subject: Fix error in assertion after 92a99c14965905e73f049bc1f92b597a903977fc --- source/blender/gpu/intern/gpu_context.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/gpu/intern/gpu_context.cc b/source/blender/gpu/intern/gpu_context.cc index 20d9208a199..e29b0d5801d 100644 --- a/source/blender/gpu/intern/gpu_context.cc +++ b/source/blender/gpu/intern/gpu_context.cc @@ -239,7 +239,7 @@ bool GPU_backend_supported(void) static void gpu_backend_create() { BLI_assert(g_backend == nullptr); - BLI_assert(GPU_backend_supported(g_backend_type)); + BLI_assert(GPU_backend_supported()); switch (g_backend_type) { #ifdef WITH_OPENGL_BACKEND -- cgit v1.2.3 From f76b537d486029fc8ad5bf3bf9b122bc4b3c9d89 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Sat, 16 Jul 2022 16:32:36 +1000 Subject: Fix T99744: NULL pointer free with corrupt zSTD reading --- source/blender/blenlib/intern/filereader_zstd.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/source/blender/blenlib/intern/filereader_zstd.c b/source/blender/blenlib/intern/filereader_zstd.c index 5f114f24fb0..aeb000e9754 100644 --- a/source/blender/blenlib/intern/filereader_zstd.c +++ b/source/blender/blenlib/intern/filereader_zstd.c @@ -281,7 +281,10 @@ static void zstd_close(FileReader *reader) if (zstd->reader.seek) { MEM_freeN(zstd->seek.uncompressed_ofs); MEM_freeN(zstd->seek.compressed_ofs); - MEM_freeN(zstd->seek.cached_content); + /* When an error has occurred this may be NULL, see: T99744. */ + if (zstd->seek.cached_content) { + MEM_freeN(zstd->seek.cached_content); + } } else { MEM_freeN((void *)zstd->in_buf.src); -- cgit v1.2.3 From 49babc7caa82883fa891640406da89b68ae8d8e5 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Sat, 16 Jul 2022 17:13:25 +1000 Subject: Cleanup: early exit MEM_lockfree_freeN when called with NULL pointer Simplify logic for freeing a NULL pointer. While no null-pointer de-reference was performed, this wasn't as so obvious as the pointer was passed to MEM_lockfree_allocN_len before checking for NULL. NOTE: T99744 claimed the a NULL pointer free was a vulnerability, while I can't see evidence for this - exiting early makes it clearer the memory isn't accessed. *Details* - Add MEMHEAD_LEN macro, avoids redundant NULL check. - Use "UNLIKELY(..)" hint's for error cases (freeing NULL pointer and checking if `leak_detector_has_run`). --- intern/guardedalloc/intern/mallocn_lockfree_impl.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/intern/guardedalloc/intern/mallocn_lockfree_impl.c b/intern/guardedalloc/intern/mallocn_lockfree_impl.c index 73912ad07b1..300e2000a14 100644 --- a/intern/guardedalloc/intern/mallocn_lockfree_impl.c +++ b/intern/guardedalloc/intern/mallocn_lockfree_impl.c @@ -44,6 +44,7 @@ enum { #define PTR_FROM_MEMHEAD(memhead) (memhead + 1) #define MEMHEAD_ALIGNED_FROM_PTR(ptr) (((MemHeadAligned *)ptr) - 1) #define MEMHEAD_IS_ALIGNED(memhead) ((memhead)->len & (size_t)MEMHEAD_ALIGN_FLAG) +#define MEMHEAD_LEN(memhead) ((memhead)->len & ~((size_t)(MEMHEAD_ALIGN_FLAG))) /* Uncomment this to have proper peak counter. */ #define USE_ATOMIC_MAX @@ -78,8 +79,8 @@ print_error(const char *str, ...) size_t MEM_lockfree_allocN_len(const void *vmemh) { - if (vmemh) { - return MEMHEAD_FROM_PTR(vmemh)->len & ~((size_t)(MEMHEAD_ALIGN_FLAG)); + if (LIKELY(vmemh)) { + return MEMHEAD_LEN(MEMHEAD_FROM_PTR(vmemh)); } return 0; @@ -87,14 +88,11 @@ size_t MEM_lockfree_allocN_len(const void *vmemh) void MEM_lockfree_freeN(void *vmemh) { - if (leak_detector_has_run) { + if (UNLIKELY(leak_detector_has_run)) { print_error("%s\n", free_after_leak_detection_message); } - MemHead *memh = MEMHEAD_FROM_PTR(vmemh); - size_t len = MEM_lockfree_allocN_len(vmemh); - - if (vmemh == NULL) { + if (UNLIKELY(vmemh == NULL)) { print_error("Attempt to free NULL pointer\n"); #ifdef WITH_ASSERT_ABORT abort(); @@ -102,6 +100,9 @@ void MEM_lockfree_freeN(void *vmemh) return; } + MemHead *memh = MEMHEAD_FROM_PTR(vmemh); + size_t len = MEMHEAD_LEN(memh); + atomic_sub_and_fetch_u(&totblock, 1); atomic_sub_and_fetch_z(&mem_in_use, len); -- cgit v1.2.3 From 0a8d21e0c96e2f9c7f6f990d756f5abe011ed805 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Sat, 16 Jul 2022 17:28:09 +1000 Subject: PyAPI: re-enable the "bgl" module for headless builds Instead of removing the `bgl` module, set all it's functions to stubs so importing `bgl` or any of it's members doesn't raise an error. This avoids problems for scripts that import bgl but don't call it's functions when running in background mode. --- source/blender/python/generic/CMakeLists.txt | 10 +---- source/blender/python/generic/bgl.c | 66 ++++++++++++++++++++-------- source/blender/python/intern/bpy_interface.c | 2 - 3 files changed, 50 insertions(+), 28 deletions(-) diff --git a/source/blender/python/generic/CMakeLists.txt b/source/blender/python/generic/CMakeLists.txt index dfca528e758..69bcfdfae4e 100644 --- a/source/blender/python/generic/CMakeLists.txt +++ b/source/blender/python/generic/CMakeLists.txt @@ -17,6 +17,7 @@ set(INC_SYS ) set(SRC + bgl.c bl_math_py_api.c blf_py_api.c bpy_threads.c @@ -26,6 +27,7 @@ set(SRC py_capi_rna.c py_capi_utils.c + bgl.h bl_math_py_api.h blf_py_api.h idprop_py_api.h @@ -38,14 +40,6 @@ set(SRC python_utildefines.h ) -if(WITH_OPENGL) - list(APPEND SRC - bgl.c - - bgl.h - ) -endif() - set(LIB ${GLEW_LIBRARY} ${PYTHON_LINKFLAGS} diff --git a/source/blender/python/generic/bgl.c b/source/blender/python/generic/bgl.c index 92ed0c51b1f..f5c1f060e80 100644 --- a/source/blender/python/generic/bgl.c +++ b/source/blender/python/generic/bgl.c @@ -1082,18 +1082,34 @@ static PyObject *Buffer_repr(Buffer *self) /** \name OpenGL API Wrapping * \{ */ -#define BGL_Wrap(funcname, ret, arg_list) \ - static PyObject *Method_##funcname(PyObject *UNUSED(self), PyObject *args) \ - { \ - arg_def arg_list; \ - ret_def_##ret; \ - if (!PyArg_ParseTuple(args, arg_str arg_list, arg_ref arg_list)) { \ +#ifdef WITH_OPENGL +# define BGL_Wrap(funcname, ret, arg_list) \ + static PyObject *Method_##funcname(PyObject *UNUSED(self), PyObject *args) \ + { \ + arg_def arg_list; \ + ret_def_##ret; \ + if (!PyArg_ParseTuple(args, arg_str arg_list, arg_ref arg_list)) { \ + return NULL; \ + } \ + GPU_bgl_start(); \ + ret_set_##ret gl##funcname(arg_var arg_list); \ + ret_ret_##ret; \ + } +#else + +static void bgl_no_opengl_error(void) +{ + PyErr_SetString(PyExc_RuntimeError, "Built without OpenGL support"); +} + +# define BGL_Wrap(funcname, ret, arg_list) \ + static PyObject *Method_##funcname(PyObject *UNUSED(self), PyObject *args) \ + { \ + (void)args; \ + bgl_no_opengl_error(); \ return NULL; \ - } \ - GPU_bgl_start(); \ - ret_set_##ret gl##funcname(arg_var arg_list); \ - ret_ret_##ret; \ - } + } +#endif /* GL_VERSION_1_0 */ BGL_Wrap(BlendFunc, void, (GLenum, GLenum)); @@ -1421,12 +1437,22 @@ static void py_module_dict_add_method(PyObject *submodule, #ifdef __GNUC__ # pragma GCC diagnostic ignored "-Waddress" #endif -#define PY_MOD_ADD_METHOD(func) \ - { \ - static PyMethodDef method_def = {"gl" #func, Method_##func, METH_VARARGS}; \ - py_module_dict_add_method(submodule, dict, &method_def, (gl##func != NULL)); \ - } \ - ((void)0) + +#ifdef WITH_OPENGL +# define PY_MOD_ADD_METHOD(func) \ + { \ + static PyMethodDef method_def = {"gl" #func, Method_##func, METH_VARARGS}; \ + py_module_dict_add_method(submodule, dict, &method_def, (gl##func != NULL)); \ + } \ + ((void)0) +#else +# define PY_MOD_ADD_METHOD(func) \ + { \ + static PyMethodDef method_def = {"gl" #func, Method_##func, METH_VARARGS}; \ + py_module_dict_add_method(submodule, dict, &method_def, false); \ + } \ + ((void)0) +#endif static void init_bgl_version_1_0_methods(PyObject *submodule, PyObject *dict) { @@ -2620,9 +2646,13 @@ static PyObject *Method_ShaderSource(PyObject *UNUSED(self), PyObject *args) return NULL; } +#ifdef WITH_OPENGL glShaderSource(shader, 1, (const char **)&source, NULL); - Py_RETURN_NONE; +#else + bgl_no_opengl_error(); + return NULL; +#endif } /** \} */ diff --git a/source/blender/python/intern/bpy_interface.c b/source/blender/python/intern/bpy_interface.c index 08dd5fe9cfc..939fa475344 100644 --- a/source/blender/python/intern/bpy_interface.c +++ b/source/blender/python/intern/bpy_interface.c @@ -259,9 +259,7 @@ static struct _inittab bpy_internal_modules[] = { {"mathutils.kdtree", PyInit_mathutils_kdtree}, #endif {"_bpy_path", BPyInit__bpy_path}, -#ifdef WITH_OPENGL {"bgl", BPyInit_bgl}, -#endif {"blf", BPyInit_blf}, {"bl_math", BPyInit_bl_math}, {"imbuf", BPyInit_imbuf}, -- cgit v1.2.3 From 1e4c557d82b005578dd2b361701241b66ccee42f Mon Sep 17 00:00:00 2001 From: Colin Basnett Date: Sat, 16 Jul 2022 20:57:28 +0200 Subject: Fix T99039: bpy.ops.sound.mixdown returns indecipherable error Fix for {T99039}. The problem was that `AUD_mixdown` and `AUD_mixdown_per_channel` were returning pointers to freed memory. Two key changes are made: 1. The return value of those functions now simply return a bool as to whether the operation succeeded, instead of an optional error string pointer. 2. The error string buffer is now passed into the function to be filled in case an error occurs. In this way, the onus of memory ownership is unamibiguously on the caller. Differential Revision: https://developer.blender.org/D15260 --- extern/audaspace/bindings/C/AUD_Special.cpp | 20 ++++++++++++++------ extern/audaspace/bindings/C/AUD_Special.h | 16 ++++++++++------ source/blender/editors/sound/sound_ops.c | 15 ++++++++++----- 3 files changed, 34 insertions(+), 17 deletions(-) diff --git a/extern/audaspace/bindings/C/AUD_Special.cpp b/extern/audaspace/bindings/C/AUD_Special.cpp index 1ce25dcd41c..686187bc70c 100644 --- a/extern/audaspace/bindings/C/AUD_Special.cpp +++ b/extern/audaspace/bindings/C/AUD_Special.cpp @@ -270,7 +270,7 @@ AUD_API int AUD_readSound(AUD_Sound* sound, float* buffer, int length, int sampl return length; } -AUD_API const char* AUD_mixdown(AUD_Sound* sound, unsigned int start, unsigned int length, unsigned int buffersize, const char* filename, AUD_DeviceSpecs specs, AUD_Container format, AUD_Codec codec, unsigned int bitrate, void(*callback)(float, void*), void* data) +AUD_API int AUD_mixdown(AUD_Sound* sound, unsigned int start, unsigned int length, unsigned int buffersize, const char* filename, AUD_DeviceSpecs specs, AUD_Container format, AUD_Codec codec, unsigned int bitrate, void(*callback)(float, void*), void* data, char* error, size_t errorsize) { try { @@ -282,15 +282,19 @@ AUD_API const char* AUD_mixdown(AUD_Sound* sound, unsigned int start, unsigned i std::shared_ptr writer = FileWriter::createWriter(filename, convCToDSpec(specs), static_cast(format), static_cast(codec), bitrate); FileWriter::writeReader(reader, writer, length, buffersize, callback, data); - return nullptr; + return 1; } catch(Exception& e) { - return e.getMessage().c_str(); + if(error && errorsize) { + std::strncpy(error, e.getMessage().c_str(), errorsize); + error[errorsize - 1] = '\0'; + } + return 0; } } -AUD_API const char* AUD_mixdown_per_channel(AUD_Sound* sound, unsigned int start, unsigned int length, unsigned int buffersize, const char* filename, AUD_DeviceSpecs specs, AUD_Container format, AUD_Codec codec, unsigned int bitrate, void(*callback)(float, void*), void* data) +AUD_API int AUD_mixdown_per_channel(AUD_Sound* sound, unsigned int start, unsigned int length, unsigned int buffersize, const char* filename, AUD_DeviceSpecs specs, AUD_Container format, AUD_Codec codec, unsigned int bitrate, void(*callback)(float, void*), void* data, char* error, size_t errorsize) { try { @@ -328,11 +332,15 @@ AUD_API const char* AUD_mixdown_per_channel(AUD_Sound* sound, unsigned int start reader->seek(start); FileWriter::writeReader(reader, writers, length, buffersize, callback, data); - return nullptr; + return 1; } catch(Exception& e) { - return e.getMessage().c_str(); + if(error && errorsize) { + std::strncpy(error, e.getMessage().c_str(), errorsize); + error[errorsize - 1] = '\0'; + } + return 0; } } diff --git a/extern/audaspace/bindings/C/AUD_Special.h b/extern/audaspace/bindings/C/AUD_Special.h index 2f5d13c6fd9..1d181d33f87 100644 --- a/extern/audaspace/bindings/C/AUD_Special.h +++ b/extern/audaspace/bindings/C/AUD_Special.h @@ -70,13 +70,15 @@ extern AUD_API int AUD_readSound(AUD_Sound* sound, float* buffer, int length, in * \param bitrate The bitrate for encoding. * \param callback A callback function that is called periodically during mixdown, reporting progress if length > 0. Can be NULL. * \param data Pass through parameter that is passed to the callback. - * \return An error message or NULL in case of success. + * \param error String buffer to copy the error message to in case of failure. + * \param errorsize The size of the error buffer. + * \return Whether or not the operation succeeded. */ -extern AUD_API const char* AUD_mixdown(AUD_Sound* sound, unsigned int start, unsigned int length, +extern AUD_API int AUD_mixdown(AUD_Sound* sound, unsigned int start, unsigned int length, unsigned int buffersize, const char* filename, AUD_DeviceSpecs specs, AUD_Container format, AUD_Codec codec, unsigned int bitrate, - void(*callback)(float, void*), void* data); + void(*callback)(float, void*), void* data, char* error, size_t errorsize); /** * Mixes a sound down into multiple files. @@ -91,13 +93,15 @@ extern AUD_API const char* AUD_mixdown(AUD_Sound* sound, unsigned int start, uns * \param bitrate The bitrate for encoding. * \param callback A callback function that is called periodically during mixdown, reporting progress if length > 0. Can be NULL. * \param data Pass through parameter that is passed to the callback. - * \return An error message or NULL in case of success. + * \param error String buffer to copy the error message to in case of failure. + * \param errorsize The size of the error buffer. + * \return Whether or not the operation succeeded. */ -extern AUD_API const char* AUD_mixdown_per_channel(AUD_Sound* sound, unsigned int start, unsigned int length, +extern AUD_API int AUD_mixdown_per_channel(AUD_Sound* sound, unsigned int start, unsigned int length, unsigned int buffersize, const char* filename, AUD_DeviceSpecs specs, AUD_Container format, AUD_Codec codec, unsigned int bitrate, - void(*callback)(float, void*), void* data); + void(*callback)(float, void*), void* data, char* error, size_t errorsize); /** * Opens a read device and prepares it for mixdown of the sound scene. diff --git a/source/blender/editors/sound/sound_ops.c b/source/blender/editors/sound/sound_ops.c index 2a8a2be8b65..08b795db0c3 100644 --- a/source/blender/editors/sound/sound_ops.c +++ b/source/blender/editors/sound/sound_ops.c @@ -340,7 +340,8 @@ static int sound_mixdown_exec(bContext *C, wmOperator *op) AUD_DeviceSpecs specs; AUD_Container container; AUD_Codec codec; - const char *result; + int result; + char error_message[1024] = {'\0'}; sound_bake_animation_exec(C, op); @@ -372,7 +373,9 @@ static int sound_mixdown_exec(bContext *C, wmOperator *op) codec, bitrate, NULL, - NULL); + NULL, + error_message, + sizeof(error_message)); } else { result = AUD_mixdown(scene_eval->sound_scene, @@ -385,13 +388,15 @@ static int sound_mixdown_exec(bContext *C, wmOperator *op) codec, bitrate, NULL, - NULL); + NULL, + error_message, + sizeof(error_message)); } BKE_sound_reset_scene_specs(scene_eval); - if (result) { - BKE_report(op->reports, RPT_ERROR, result); + if (!result) { + BKE_report(op->reports, RPT_ERROR, error_message); return OPERATOR_CANCELLED; } #else /* WITH_AUDASPACE */ -- cgit v1.2.3 From d136a996caa9da3024bb90c543764b895201a4e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20M=C3=BCller?= Date: Sat, 16 Jul 2022 22:20:08 +0200 Subject: Audaspace: minor formatting fix for last commit. --- extern/audaspace/bindings/C/AUD_Special.cpp | 14 ++++++++------ release/datafiles/locale | 2 +- release/scripts/addons | 2 +- source/tools | 2 +- 4 files changed, 11 insertions(+), 9 deletions(-) diff --git a/extern/audaspace/bindings/C/AUD_Special.cpp b/extern/audaspace/bindings/C/AUD_Special.cpp index 686187bc70c..a5ecb7a6dc0 100644 --- a/extern/audaspace/bindings/C/AUD_Special.cpp +++ b/extern/audaspace/bindings/C/AUD_Special.cpp @@ -282,15 +282,16 @@ AUD_API int AUD_mixdown(AUD_Sound* sound, unsigned int start, unsigned int lengt std::shared_ptr writer = FileWriter::createWriter(filename, convCToDSpec(specs), static_cast(format), static_cast(codec), bitrate); FileWriter::writeReader(reader, writer, length, buffersize, callback, data); - return 1; + return true; } catch(Exception& e) { - if(error && errorsize) { + if(error && errorsize) + { std::strncpy(error, e.getMessage().c_str(), errorsize); error[errorsize - 1] = '\0'; } - return 0; + return false; } } @@ -332,15 +333,16 @@ AUD_API int AUD_mixdown_per_channel(AUD_Sound* sound, unsigned int start, unsign reader->seek(start); FileWriter::writeReader(reader, writers, length, buffersize, callback, data); - return 1; + return true; } catch(Exception& e) { - if(error && errorsize) { + if(error && errorsize) + { std::strncpy(error, e.getMessage().c_str(), errorsize); error[errorsize - 1] = '\0'; } - return 0; + return false; } } diff --git a/release/datafiles/locale b/release/datafiles/locale index a2eb5078914..9a85b137951 160000 --- a/release/datafiles/locale +++ b/release/datafiles/locale @@ -1 +1 @@ -Subproject commit a2eb507891449a0b67582be9561840075513661d +Subproject commit 9a85b13795157560b319235c63f5a13b0107ba41 diff --git a/release/scripts/addons b/release/scripts/addons index 7a8502871c3..bdf75cb276d 160000 --- a/release/scripts/addons +++ b/release/scripts/addons @@ -1 +1 @@ -Subproject commit 7a8502871c34db0343cc7de52d6b49b15a84238a +Subproject commit bdf75cb276dfd3b5266c909de4c099c00c68a659 diff --git a/source/tools b/source/tools index da8bdd7244c..01b4c0e4a17 160000 --- a/source/tools +++ b/source/tools @@ -1 +1 @@ -Subproject commit da8bdd7244c7b6c2eadf4c949ff391d0cc430275 +Subproject commit 01b4c0e4a172819414229445c314be34527bf412 -- cgit v1.2.3 From 9a1488790532b6a64dea66cbe696ac35c70e6793 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Sat, 16 Jul 2022 16:45:41 -0700 Subject: Sculpt: Fix scene spacing mode (phase 1) The scene spacing code was failing to check if a raycast failed, which can happen when sculpting the edges of objects in negative mode. Note I removed what I suspect was a hack put in to fix this, spacing was clamped to 0.001 scene units. Scene spacing mode is actually quite broken, so it will be fixed in a series of phases. --- release/datafiles/locale | 2 +- release/scripts/addons | 2 +- source/blender/editors/sculpt_paint/paint_stroke.c | 17 ++++++++++++++--- source/tools | 2 +- 4 files changed, 17 insertions(+), 6 deletions(-) diff --git a/release/datafiles/locale b/release/datafiles/locale index 9a85b137951..a2eb5078914 160000 --- a/release/datafiles/locale +++ b/release/datafiles/locale @@ -1 +1 @@ -Subproject commit 9a85b13795157560b319235c63f5a13b0107ba41 +Subproject commit a2eb507891449a0b67582be9561840075513661d diff --git a/release/scripts/addons b/release/scripts/addons index bdf75cb276d..7a8502871c3 160000 --- a/release/scripts/addons +++ b/release/scripts/addons @@ -1 +1 @@ -Subproject commit bdf75cb276dfd3b5266c909de4c099c00c68a659 +Subproject commit 7a8502871c34db0343cc7de52d6b49b15a84238a diff --git a/source/blender/editors/sculpt_paint/paint_stroke.c b/source/blender/editors/sculpt_paint/paint_stroke.c index 1ee26935dc9..a4c942796d8 100644 --- a/source/blender/editors/sculpt_paint/paint_stroke.c +++ b/source/blender/editors/sculpt_paint/paint_stroke.c @@ -79,6 +79,8 @@ typedef struct PaintStroke { float last_mouse_position[2]; float last_world_space_position[3]; + float last_scene_spacing_delta[3]; + bool stroke_over_mesh; /* space distance covered so far */ float stroke_distance; @@ -550,8 +552,15 @@ static void paint_brush_stroke_add_step( stroke->last_pressure = pressure; if (paint_stroke_use_scene_spacing(brush, mode)) { - SCULPT_stroke_get_location(C, stroke->last_world_space_position, stroke->last_mouse_position); - mul_m4_v3(stroke->vc.obact->obmat, stroke->last_world_space_position); + float world_space_position[3]; + + if (SCULPT_stroke_get_location(C, world_space_position, stroke->last_mouse_position)) { + copy_v3_v3(stroke->last_world_space_position, world_space_position); + mul_m4_v3(stroke->vc.obact->obmat, stroke->last_world_space_position); + } + else { + add_v3_v3(stroke->last_world_space_position, stroke->last_scene_spacing_delta); + } } if (paint_stroke_use_jitter(mode, brush, stroke->stroke_mode == BRUSH_STROKE_INVERT)) { @@ -698,7 +707,7 @@ static float paint_space_stroke_spacing(bContext *C, spacing *= stroke->zoom_2d; if (paint_stroke_use_scene_spacing(brush, mode)) { - return max_ff(0.001f, size_clamp * spacing / 50.0f); + return size_clamp * spacing / 50.0f; } return max_ff(stroke->zoom_2d, size_clamp * spacing / 50.0f); } @@ -838,6 +847,8 @@ static int paint_space_stroke(bContext *C, stroke->last_world_space_position, final_world_space_position); ED_view3d_project_v2(region, final_world_space_position, mouse); + + mul_v3_v3fl(stroke->last_scene_spacing_delta, d_world_space_position, spacing); } else { mouse[0] = stroke->last_mouse_position[0] + dmouse[0] * spacing; diff --git a/source/tools b/source/tools index 01b4c0e4a17..da8bdd7244c 160000 --- a/source/tools +++ b/source/tools @@ -1 +1 @@ -Subproject commit 01b4c0e4a172819414229445c314be34527bf412 +Subproject commit da8bdd7244c7b6c2eadf4c949ff391d0cc430275 -- cgit v1.2.3 From cd1e4ae4483056157f238f56f19367ad98ae3f3d Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Sat, 16 Jul 2022 17:27:25 -0700 Subject: Fix T99644: Anchored brush mode fails for negative brushes The stroke code now supports raycasting the original mesh. This fixes anchored mode not working for negative brushes, which might move the mesh out of the initial mouse cursor position. --- .../editors/sculpt_paint/curves_sculpt_ops.cc | 5 +++- source/blender/editors/sculpt_paint/paint_intern.h | 5 +++- source/blender/editors/sculpt_paint/paint_stroke.c | 28 +++++++++++++++------- source/blender/editors/sculpt_paint/sculpt.c | 9 ++++--- .../blender/editors/sculpt_paint/sculpt_intern.h | 5 +++- .../editors/sculpt_paint/sculpt_transform.c | 2 +- 6 files changed, 39 insertions(+), 15 deletions(-) diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc b/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc index 5c73c7a37d3..47e0fe3a61a 100644 --- a/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc +++ b/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc @@ -147,7 +147,10 @@ struct SculptCurvesBrushStrokeData { PaintStroke *stroke; }; -static bool stroke_get_location(bContext *C, float out[3], const float mouse[2]) +static bool stroke_get_location(bContext *C, + float out[3], + const float mouse[2], + bool UNUSED(force_original)) { out[0] = mouse[0]; out[1] = mouse[1]; diff --git a/source/blender/editors/sculpt_paint/paint_intern.h b/source/blender/editors/sculpt_paint/paint_intern.h index 02c3b5be8b9..99c25953d50 100644 --- a/source/blender/editors/sculpt_paint/paint_intern.h +++ b/source/blender/editors/sculpt_paint/paint_intern.h @@ -46,7 +46,10 @@ typedef struct CoNo { /* paint_stroke.c */ -typedef bool (*StrokeGetLocation)(struct bContext *C, float location[3], const float mouse[2]); +typedef bool (*StrokeGetLocation)(struct bContext *C, + float location[3], + const float mouse[2], + bool force_original); typedef bool (*StrokeTestStart)(struct bContext *C, struct wmOperator *op, const float mouse[2]); typedef void (*StrokeUpdateStep)(struct bContext *C, struct wmOperator *op, diff --git a/source/blender/editors/sculpt_paint/paint_stroke.c b/source/blender/editors/sculpt_paint/paint_stroke.c index a4c942796d8..33a307376bb 100644 --- a/source/blender/editors/sculpt_paint/paint_stroke.c +++ b/source/blender/editors/sculpt_paint/paint_stroke.c @@ -122,6 +122,8 @@ typedef struct PaintStroke { StrokeUpdateStep update_step; StrokeRedraw redraw; StrokeDone done; + + bool original; /* Raycast original mesh at start of stroke */ } PaintStroke; /*** Cursors ***/ @@ -245,6 +247,11 @@ static bool paint_stroke_use_scene_spacing(Brush *brush, ePaintMode mode) return false; } +static bool paint_tool_raycast_original(Brush *brush, ePaintMode mode) +{ + return brush->flag & BRUSH_ANCHORED; +} + static bool paint_tool_require_inbetween_mouse_events(Brush *brush, ePaintMode mode) { if (brush->flag & BRUSH_ANCHORED) { @@ -394,7 +401,7 @@ static bool paint_brush_update(bContext *C, halfway[1] = dy * 0.5f + stroke->initial_mouse[1]; if (stroke->get_location) { - if (stroke->get_location(C, r_location, halfway)) { + if (stroke->get_location(C, r_location, halfway, stroke->original)) { hit = true; location_sampled = true; location_success = true; @@ -468,7 +475,7 @@ static bool paint_brush_update(bContext *C, if (!location_sampled) { if (stroke->get_location) { - if (stroke->get_location(C, r_location, mouse)) { + if (stroke->get_location(C, r_location, mouse, stroke->original)) { location_success = true; *r_location_is_set = true; } @@ -554,7 +561,8 @@ static void paint_brush_stroke_add_step( if (paint_stroke_use_scene_spacing(brush, mode)) { float world_space_position[3]; - if (SCULPT_stroke_get_location(C, world_space_position, stroke->last_mouse_position)) { + if (SCULPT_stroke_get_location( + C, world_space_position, stroke->last_mouse_position, stroke->original)) { copy_v3_v3(stroke->last_world_space_position, world_space_position); mul_m4_v3(stroke->vc.obact->obmat, stroke->last_world_space_position); } @@ -816,7 +824,7 @@ static int paint_space_stroke(bContext *C, if (use_scene_spacing) { float world_space_position[3]; - bool hit = SCULPT_stroke_get_location(C, world_space_position, final_mouse); + bool hit = SCULPT_stroke_get_location(C, world_space_position, final_mouse, stroke->original); mul_m4_v3(stroke->vc.obact->obmat, world_space_position); if (hit && stroke->stroke_over_mesh) { sub_v3_v3v3(d_world_space_position, world_space_position, stroke->last_world_space_position); @@ -907,6 +915,8 @@ PaintStroke *paint_stroke_new(bContext *C, stroke->ups = ups; stroke->stroke_mode = RNA_enum_get(op->ptr, "mode"); + stroke->original = paint_tool_raycast_original(br, BKE_paintmode_get_active_from_context(C)); + get_imapaint_zoom(C, &zoomx, &zoomy); stroke->zoom_2d = max_ff(zoomx, zoomy); @@ -1202,8 +1212,10 @@ static void paint_line_strokes_spacing(bContext *C, copy_v2_v2(stroke->last_mouse_position, old_pos); if (use_scene_spacing) { - bool hit_old = SCULPT_stroke_get_location(C, world_space_position_old, old_pos); - bool hit_new = SCULPT_stroke_get_location(C, world_space_position_new, new_pos); + bool hit_old = SCULPT_stroke_get_location( + C, world_space_position_old, old_pos, stroke->original); + bool hit_new = SCULPT_stroke_get_location( + C, world_space_position_new, new_pos, stroke->original); mul_m4_v3(stroke->vc.obact->obmat, world_space_position_old); mul_m4_v3(stroke->vc.obact->obmat, world_space_position_new); if (hit_old && hit_new && stroke->stroke_over_mesh) { @@ -1347,7 +1359,7 @@ static bool paint_stroke_curve_end(bContext *C, wmOperator *op, PaintStroke *str if (paint_stroke_use_scene_spacing(br, BKE_paintmode_get_active_from_context(C))) { stroke->stroke_over_mesh = SCULPT_stroke_get_location( - C, stroke->last_world_space_position, data + 2 * j); + C, stroke->last_world_space_position, data + 2 * j, stroke->original); mul_m4_v3(stroke->vc.obact->obmat, stroke->last_world_space_position); } @@ -1479,7 +1491,7 @@ int paint_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event, PaintS copy_v2_v2(stroke->last_mouse_position, sample_average.mouse); if (paint_stroke_use_scene_spacing(br, mode)) { stroke->stroke_over_mesh = SCULPT_stroke_get_location( - C, stroke->last_world_space_position, sample_average.mouse); + C, stroke->last_world_space_position, sample_average.mouse, stroke->original); mul_m4_v3(stroke->vc.obact->obmat, stroke->last_world_space_position); } stroke->stroke_started = stroke->test_start(C, op, sample_average.mouse); diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c index ae65ca8178b..2951706ebd8 100644 --- a/source/blender/editors/sculpt_paint/sculpt.c +++ b/source/blender/editors/sculpt_paint/sculpt.c @@ -4952,7 +4952,10 @@ bool SCULPT_cursor_geometry_info_update(bContext *C, return true; } -bool SCULPT_stroke_get_location(bContext *C, float out[3], const float mval[2]) +bool SCULPT_stroke_get_location(bContext *C, + float out[3], + const float mval[2], + bool force_original) { Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); Object *ob; @@ -4968,7 +4971,7 @@ bool SCULPT_stroke_get_location(bContext *C, float out[3], const float mval[2]) ss = ob->sculpt; cache = ss->cache; - original = (cache) ? cache->original : false; + original = force_original || ((cache) ? cache->original : false); const Brush *brush = BKE_paint_brush(BKE_paint_get_active_from_context(C)); @@ -5284,7 +5287,7 @@ void SCULPT_flush_update_done(const bContext *C, Object *ob, SculptUpdateType up static bool over_mesh(bContext *C, struct wmOperator *UNUSED(op), const float mval[2]) { float co_dummy[3]; - return SCULPT_stroke_get_location(C, co_dummy, mval); + return SCULPT_stroke_get_location(C, co_dummy, mval, false); } bool SCULPT_handles_colors_report(SculptSession *ss, ReportList *reports) diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.h b/source/blender/editors/sculpt_paint/sculpt_intern.h index 8485c7fcbf9..83526006d7d 100644 --- a/source/blender/editors/sculpt_paint/sculpt_intern.h +++ b/source/blender/editors/sculpt_paint/sculpt_intern.h @@ -845,7 +845,10 @@ void SCULPT_tag_update_overlays(bContext *C); * (This allows us to ignore the GL depth buffer) * Returns 0 if the ray doesn't hit the mesh, non-zero otherwise. */ -bool SCULPT_stroke_get_location(struct bContext *C, float out[3], const float mouse[2]); +bool SCULPT_stroke_get_location(struct bContext *C, + float out[3], + const float mouse[2], + bool force_original); /** * Gets the normal, location and active vertex location of the geometry under the cursor. This also * updates the active vertex and cursor related data of the SculptSession using the mouse position diff --git a/source/blender/editors/sculpt_paint/sculpt_transform.c b/source/blender/editors/sculpt_paint/sculpt_transform.c index 365000ab163..8856e3bf3db 100644 --- a/source/blender/editors/sculpt_paint/sculpt_transform.c +++ b/source/blender/editors/sculpt_paint/sculpt_transform.c @@ -426,7 +426,7 @@ static int sculpt_set_pivot_position_exec(bContext *C, wmOperator *op) RNA_float_get(op->ptr, "mouse_x"), RNA_float_get(op->ptr, "mouse_y"), }; - if (SCULPT_stroke_get_location(C, stroke_location, mval)) { + if (SCULPT_stroke_get_location(C, stroke_location, mval, false)) { copy_v3_v3(ss->pivot_pos, stroke_location); } } -- cgit v1.2.3 From 3c016fbfd092e0ea2bbdc62b2967a6b1f7da00d5 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Mon, 18 Jul 2022 19:55:51 +1000 Subject: Fix error indenting new-lines in generated RST API docs New-lines in RNA type descriptions caused invalid RST indentation. This resolve the error noted by @nutti in D15481. --- doc/python_api/sphinx_doc_gen.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/doc/python_api/sphinx_doc_gen.py b/doc/python_api/sphinx_doc_gen.py index 17ffdb8e244..f5e0369cede 100644 --- a/doc/python_api/sphinx_doc_gen.py +++ b/doc/python_api/sphinx_doc_gen.py @@ -1529,7 +1529,8 @@ def pyrna2sphinx(basepath): else: fw(".. class:: %s\n\n" % struct_id) - fw(" %s\n\n" % struct.description) + write_indented_lines(" ", fw, struct.description, False) + fw("\n") # Properties sorted in alphabetical order. sorted_struct_properties = struct.properties[:] -- cgit v1.2.3 From c7f788b877cc60a8784c41b5cd5ba85044e1d04f Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Fri, 15 Jul 2022 19:32:09 +0200 Subject: Subdiv: remove unused GPU device choice, fix crash with libepoxy on init openSubdiv_init() would detect available evaluators before any OpenGL context exists, causing a crash with libepoxy. This test however is redundant as we already check the requirements on the Blender side through the GPU API. To simplify things, completely remove the device detection in the opensubdiv module and reduce the evaluators to just CPU and GPU. The plan here is to move to the GPU module abstraction over OpenGL/Metal/Vulkan and so all these different backends no longer make sense. This also removes the user preference for OpenSubdiv compute device, which was not used for the new GPU subdivision implementation. Ref D15291 Differential Revision: https://developer.blender.org/D15470 --- intern/opensubdiv/CMakeLists.txt | 12 ---- intern/opensubdiv/internal/base/opensubdiv_capi.cc | 40 ----------- .../internal/device/device_context_cuda.cc | 39 ----------- .../internal/device/device_context_cuda.h | 38 ---------- .../internal/device/device_context_glsl_compute.cc | 40 ----------- .../internal/device/device_context_glsl_compute.h | 38 ---------- .../device_context_glsl_transform_feedback.cc | 40 ----------- .../device_context_glsl_transform_feedback.h | 38 ---------- .../internal/device/device_context_opencl.cc | 39 ----------- .../internal/device/device_context_opencl.h | 38 ---------- .../internal/device/device_context_openmp.cc | 42 ----------- .../internal/device/device_context_openmp.h | 38 ---------- .../internal/evaluator/evaluator_cache_impl.cc | 2 +- .../internal/evaluator/evaluator_impl.cc | 9 +-- intern/opensubdiv/opensubdiv_capi.h | 3 - intern/opensubdiv/opensubdiv_capi_type.h | 10 +-- release/datafiles/userdef/userdef_default.c | 1 - release/scripts/startup/bl_ui/space_userpref.py | 6 -- source/blender/blenkernel/BKE_subdiv_eval.h | 2 +- source/blender/blenkernel/intern/subdiv_eval.c | 4 +- source/blender/blenkernel/intern/subdiv_modifier.c | 5 -- .../draw/intern/draw_cache_impl_subdivision.cc | 4 +- source/blender/makesdna/DNA_userdef_types.h | 3 +- source/blender/makesrna/intern/rna_userdef.c | 81 ---------------------- 24 files changed, 11 insertions(+), 561 deletions(-) delete mode 100644 intern/opensubdiv/internal/device/device_context_cuda.cc delete mode 100644 intern/opensubdiv/internal/device/device_context_cuda.h delete mode 100644 intern/opensubdiv/internal/device/device_context_glsl_compute.cc delete mode 100644 intern/opensubdiv/internal/device/device_context_glsl_compute.h delete mode 100644 intern/opensubdiv/internal/device/device_context_glsl_transform_feedback.cc delete mode 100644 intern/opensubdiv/internal/device/device_context_glsl_transform_feedback.h delete mode 100644 intern/opensubdiv/internal/device/device_context_opencl.cc delete mode 100644 intern/opensubdiv/internal/device/device_context_opencl.h delete mode 100644 intern/opensubdiv/internal/device/device_context_openmp.cc delete mode 100644 intern/opensubdiv/internal/device/device_context_openmp.h diff --git a/intern/opensubdiv/CMakeLists.txt b/intern/opensubdiv/CMakeLists.txt index bb3aa16a9fe..14cc6a70cd5 100644 --- a/intern/opensubdiv/CMakeLists.txt +++ b/intern/opensubdiv/CMakeLists.txt @@ -42,18 +42,6 @@ if(WITH_OPENSUBDIV) internal/base/util.cc internal/base/util.h - # Device. - internal/device/device_context_cuda.cc - internal/device/device_context_cuda.h - internal/device/device_context_glsl_compute.cc - internal/device/device_context_glsl_compute.h - internal/device/device_context_glsl_transform_feedback.cc - internal/device/device_context_glsl_transform_feedback.h - internal/device/device_context_opencl.cc - internal/device/device_context_opencl.h - internal/device/device_context_openmp.cc - internal/device/device_context_openmp.h - # Evaluator. internal/evaluator/eval_output.cc internal/evaluator/eval_output.h diff --git a/intern/opensubdiv/internal/base/opensubdiv_capi.cc b/intern/opensubdiv/internal/base/opensubdiv_capi.cc index 85f8120c76b..40d820836b9 100644 --- a/intern/opensubdiv/internal/base/opensubdiv_capi.cc +++ b/intern/opensubdiv/internal/base/opensubdiv_capi.cc @@ -21,55 +21,15 @@ #endif #include "internal/base/util.h" -#include "internal/device/device_context_cuda.h" -#include "internal/device/device_context_glsl_compute.h" -#include "internal/device/device_context_glsl_transform_feedback.h" -#include "internal/device/device_context_opencl.h" -#include "internal/device/device_context_openmp.h" - -using blender::opensubdiv::CUDADeviceContext; -using blender::opensubdiv::GLSLComputeDeviceContext; -using blender::opensubdiv::GLSLTransformFeedbackDeviceContext; -using blender::opensubdiv::OpenCLDeviceContext; -using blender::opensubdiv::OpenMPDeviceContext; void openSubdiv_init() { - // Ensure all OpenGL strings are cached. - openSubdiv_getAvailableEvaluators(); } void openSubdiv_cleanup() { } -int openSubdiv_getAvailableEvaluators() -{ - int flags = OPENSUBDIV_EVALUATOR_CPU; - - if (OpenMPDeviceContext::isSupported()) { - flags |= OPENSUBDIV_EVALUATOR_OPENMP; - } - - if (OpenCLDeviceContext::isSupported()) { - flags |= OPENSUBDIV_EVALUATOR_OPENCL; - } - - if (CUDADeviceContext::isSupported()) { - flags |= OPENSUBDIV_EVALUATOR_CUDA; - } - - if (GLSLTransformFeedbackDeviceContext::isSupported()) { - flags |= OPENSUBDIV_EVALUATOR_GLSL_TRANSFORM_FEEDBACK; - } - - if (GLSLComputeDeviceContext::isSupported()) { - flags |= OPENSUBDIV_EVALUATOR_GLSL_COMPUTE; - } - - return flags; -} - int openSubdiv_getVersionHex() { #if defined(OPENSUBDIV_VERSION_NUMBER) diff --git a/intern/opensubdiv/internal/device/device_context_cuda.cc b/intern/opensubdiv/internal/device/device_context_cuda.cc deleted file mode 100644 index cd4336265a5..00000000000 --- a/intern/opensubdiv/internal/device/device_context_cuda.cc +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright 2020 Blender Foundation. All rights reserved. -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 2 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software Foundation, -// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -// -// Author: Sergey Sharybin - -#include "internal/device/device_context_cuda.h" - -namespace blender { -namespace opensubdiv { - -bool CUDADeviceContext::isSupported() -{ - // TODO(sergey): Add CUDA device support, using CUDA-RT API. - return false; -} - -CUDADeviceContext::CUDADeviceContext() -{ -} - -CUDADeviceContext::~CUDADeviceContext() -{ -} - -} // namespace opensubdiv -} // namespace blender diff --git a/intern/opensubdiv/internal/device/device_context_cuda.h b/intern/opensubdiv/internal/device/device_context_cuda.h deleted file mode 100644 index d1bfb15fbcb..00000000000 --- a/intern/opensubdiv/internal/device/device_context_cuda.h +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright 2020 Blender Foundation. All rights reserved. -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 2 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software Foundation, -// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -// -// Author: Sergey Sharybin - -#ifndef OPENSUBDIV_DEVICE_CONTEXT_CUDA_H_ -#define OPENSUBDIV_DEVICE_CONTEXT_CUDA_H_ - -namespace blender { -namespace opensubdiv { - -class CUDADeviceContext { - public: - // Stateless check to see whether CUDA functionality is available on this - // platform. - static bool isSupported(); - - CUDADeviceContext(); - ~CUDADeviceContext(); -}; - -} // namespace opensubdiv -} // namespace blender - -#endif // _OPENSUBDIV_DEVICE_CONTEXT_CUDA_H_ diff --git a/intern/opensubdiv/internal/device/device_context_glsl_compute.cc b/intern/opensubdiv/internal/device/device_context_glsl_compute.cc deleted file mode 100644 index 7b416976099..00000000000 --- a/intern/opensubdiv/internal/device/device_context_glsl_compute.cc +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright 2020 Blender Foundation. All rights reserved. -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 2 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software Foundation, -// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -// -// Author: Sergey Sharybin - -#include "internal/device/device_context_glsl_compute.h" - -#include - -namespace blender { -namespace opensubdiv { - -bool GLSLComputeDeviceContext::isSupported() -{ - return GLEW_VERSION_4_3 || GLEW_ARB_compute_shader; -} - -GLSLComputeDeviceContext::GLSLComputeDeviceContext() -{ -} - -GLSLComputeDeviceContext::~GLSLComputeDeviceContext() -{ -} - -} // namespace opensubdiv -} // namespace blender diff --git a/intern/opensubdiv/internal/device/device_context_glsl_compute.h b/intern/opensubdiv/internal/device/device_context_glsl_compute.h deleted file mode 100644 index f64c7d1954b..00000000000 --- a/intern/opensubdiv/internal/device/device_context_glsl_compute.h +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright 2020 Blender Foundation. All rights reserved. -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 2 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software Foundation, -// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -// -// Author: Sergey Sharybin - -#ifndef OPENSUBDIV_DEVICE_CONTEXT_GLSL_COMPUTE_H_ -#define OPENSUBDIV_DEVICE_CONTEXT_GLSL_COMPUTE_H_ - -namespace blender { -namespace opensubdiv { - -class GLSLComputeDeviceContext { - public: - // Stateless check to see whether GLSL compute functionality is - // available on this platform. - static bool isSupported(); - - GLSLComputeDeviceContext(); - ~GLSLComputeDeviceContext(); -}; - -} // namespace opensubdiv -} // namespace blender - -#endif // _OPENSUBDIV_DEVICE_CONTEXT_GLSL_COMPUTE_H_ diff --git a/intern/opensubdiv/internal/device/device_context_glsl_transform_feedback.cc b/intern/opensubdiv/internal/device/device_context_glsl_transform_feedback.cc deleted file mode 100644 index ef897608b6e..00000000000 --- a/intern/opensubdiv/internal/device/device_context_glsl_transform_feedback.cc +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright 2020 Blender Foundation. All rights reserved. -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 2 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software Foundation, -// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -// -// Author: Sergey Sharybin - -#include "internal/device/device_context_glsl_transform_feedback.h" - -#include - -namespace blender { -namespace opensubdiv { - -bool GLSLTransformFeedbackDeviceContext::isSupported() -{ - return GLEW_VERSION_4_1; -} - -GLSLTransformFeedbackDeviceContext::GLSLTransformFeedbackDeviceContext() -{ -} - -GLSLTransformFeedbackDeviceContext::~GLSLTransformFeedbackDeviceContext() -{ -} - -} // namespace opensubdiv -} // namespace blender diff --git a/intern/opensubdiv/internal/device/device_context_glsl_transform_feedback.h b/intern/opensubdiv/internal/device/device_context_glsl_transform_feedback.h deleted file mode 100644 index 7bbbba1380f..00000000000 --- a/intern/opensubdiv/internal/device/device_context_glsl_transform_feedback.h +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright 2020 Blender Foundation. All rights reserved. -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 2 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software Foundation, -// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -// -// Author: Sergey Sharybin - -#ifndef OPENSUBDIV_DEVICE_CONTEXT_GLSL_TRANSFORM_FEEDBACK_H_ -#define OPENSUBDIV_DEVICE_CONTEXT_GLSL_TRANSFORM_FEEDBACK_H_ - -namespace blender { -namespace opensubdiv { - -class GLSLTransformFeedbackDeviceContext { - public: - // Stateless check to see whether GLSL transform feedback functionality is - // available on this platform. - static bool isSupported(); - - GLSLTransformFeedbackDeviceContext(); - ~GLSLTransformFeedbackDeviceContext(); -}; - -} // namespace opensubdiv -} // namespace blender - -#endif // _OPENSUBDIV_DEVICE_CONTEXT_GLSL_TRANSFORM_FEEDBACK_H_ diff --git a/intern/opensubdiv/internal/device/device_context_opencl.cc b/intern/opensubdiv/internal/device/device_context_opencl.cc deleted file mode 100644 index 1670ea3c9d0..00000000000 --- a/intern/opensubdiv/internal/device/device_context_opencl.cc +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright 2020 Blender Foundation. All rights reserved. -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 2 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software Foundation, -// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -// -// Author: Sergey Sharybin - -#include "internal/device/device_context_opencl.h" - -namespace blender { -namespace opensubdiv { - -bool OpenCLDeviceContext::isSupported() -{ - // TODO(sergey): Add support of OpenCL devices. - return false; -} - -OpenCLDeviceContext::OpenCLDeviceContext() -{ -} - -OpenCLDeviceContext::~OpenCLDeviceContext() -{ -} - -} // namespace opensubdiv -} // namespace blender diff --git a/intern/opensubdiv/internal/device/device_context_opencl.h b/intern/opensubdiv/internal/device/device_context_opencl.h deleted file mode 100644 index 57ec6ed3bb6..00000000000 --- a/intern/opensubdiv/internal/device/device_context_opencl.h +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright 2020 Blender Foundation. All rights reserved. -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 2 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software Foundation, -// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -// -// Author: Sergey Sharybin - -#ifndef OPENSUBDIV_DEVICE_CONTEXT_OPENCL_H_ -#define OPENSUBDIV_DEVICE_CONTEXT_OPENCL_H_ - -namespace blender { -namespace opensubdiv { - -class OpenCLDeviceContext { - public: - // Stateless check to see whether OpenCL functionality is available on this - // platform. - static bool isSupported(); - - OpenCLDeviceContext(); - ~OpenCLDeviceContext(); -}; - -} // namespace opensubdiv -} // namespace blender - -#endif // _OPENSUBDIV_DEVICE_CONTEXT_OPENCL_H_ diff --git a/intern/opensubdiv/internal/device/device_context_openmp.cc b/intern/opensubdiv/internal/device/device_context_openmp.cc deleted file mode 100644 index e01312fefaf..00000000000 --- a/intern/opensubdiv/internal/device/device_context_openmp.cc +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright 2020 Blender Foundation. All rights reserved. -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 2 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software Foundation, -// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -// -// Author: Sergey Sharybin - -#include "internal/device/device_context_openmp.h" - -namespace blender { -namespace opensubdiv { - -bool OpenMPDeviceContext::isSupported() -{ -#ifdef OPENSUBDIV_HAS_OPENMP - return true; -#else - return false; -#endif -} - -OpenMPDeviceContext::OpenMPDeviceContext() -{ -} - -OpenMPDeviceContext::~OpenMPDeviceContext() -{ -} - -} // namespace opensubdiv -} // namespace blender diff --git a/intern/opensubdiv/internal/device/device_context_openmp.h b/intern/opensubdiv/internal/device/device_context_openmp.h deleted file mode 100644 index 2bebbdf40bc..00000000000 --- a/intern/opensubdiv/internal/device/device_context_openmp.h +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright 2020 Blender Foundation. All rights reserved. -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 2 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software Foundation, -// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -// -// Author: Sergey Sharybin - -#ifndef OPENSUBDIV_DEVICE_CONTEXT_OPENMP_H_ -#define OPENSUBDIV_DEVICE_CONTEXT_OPENMP_H_ - -namespace blender { -namespace opensubdiv { - -class OpenMPDeviceContext { - public: - // Stateless check to see whether OpenMP functionality is available on this - // platform. - static bool isSupported(); - - OpenMPDeviceContext(); - ~OpenMPDeviceContext(); -}; - -} // namespace opensubdiv -} // namespace blender - -#endif // _OPENSUBDIV_DEVICE_CONTEXT_OPENMP_H_ diff --git a/intern/opensubdiv/internal/evaluator/evaluator_cache_impl.cc b/intern/opensubdiv/internal/evaluator/evaluator_cache_impl.cc index 2fffcefa460..efac0926fef 100644 --- a/intern/opensubdiv/internal/evaluator/evaluator_cache_impl.cc +++ b/intern/opensubdiv/internal/evaluator/evaluator_cache_impl.cc @@ -30,7 +30,7 @@ OpenSubdiv_EvaluatorCacheImpl::~OpenSubdiv_EvaluatorCacheImpl() OpenSubdiv_EvaluatorCacheImpl *openSubdiv_createEvaluatorCacheInternal( eOpenSubdivEvaluator evaluator_type) { - if (evaluator_type != eOpenSubdivEvaluator::OPENSUBDIV_EVALUATOR_GLSL_COMPUTE) { + if (evaluator_type != eOpenSubdivEvaluator::OPENSUBDIV_EVALUATOR_GPU) { return nullptr; } OpenSubdiv_EvaluatorCacheImpl *evaluator_cache; diff --git a/intern/opensubdiv/internal/evaluator/evaluator_impl.cc b/intern/opensubdiv/internal/evaluator/evaluator_impl.cc index 49a59c44be8..262b418169d 100644 --- a/intern/opensubdiv/internal/evaluator/evaluator_impl.cc +++ b/intern/opensubdiv/internal/evaluator/evaluator_impl.cc @@ -442,11 +442,6 @@ OpenSubdiv_EvaluatorImpl *openSubdiv_createEvaluatorInternal( eOpenSubdivEvaluator evaluator_type, OpenSubdiv_EvaluatorCacheImpl *evaluator_cache_descr) { - // Only CPU and GLCompute are implemented at the moment. - if (evaluator_type != OPENSUBDIV_EVALUATOR_CPU && - evaluator_type != OPENSUBDIV_EVALUATOR_GLSL_COMPUTE) { - return NULL; - } using blender::opensubdiv::vector; TopologyRefiner *refiner = topology_refiner->impl->topology_refiner; if (refiner == NULL) { @@ -551,8 +546,8 @@ OpenSubdiv_EvaluatorImpl *openSubdiv_createEvaluatorInternal( // Create OpenSubdiv's CPU side evaluator. blender::opensubdiv::EvalOutputAPI::EvalOutput *eval_output = nullptr; - const bool use_gl_evaluator = evaluator_type == OPENSUBDIV_EVALUATOR_GLSL_COMPUTE; - if (use_gl_evaluator) { + const bool use_gpu_evaluator = evaluator_type == OPENSUBDIV_EVALUATOR_GPU; + if (use_gpu_evaluator) { blender::opensubdiv::GpuEvalOutput::EvaluatorCache *evaluator_cache = nullptr; if (evaluator_cache_descr) { evaluator_cache = static_cast( diff --git a/intern/opensubdiv/opensubdiv_capi.h b/intern/opensubdiv/opensubdiv_capi.h index a26ea36b863..1fa75a9a8c2 100644 --- a/intern/opensubdiv/opensubdiv_capi.h +++ b/intern/opensubdiv/opensubdiv_capi.h @@ -31,9 +31,6 @@ extern "C" { void openSubdiv_init(void); void openSubdiv_cleanup(void); -// Bitmask of eOpenSubdivEvaluator. -int openSubdiv_getAvailableEvaluators(void); - int openSubdiv_getVersionHex(void); #ifdef __cplusplus diff --git a/intern/opensubdiv/opensubdiv_capi_type.h b/intern/opensubdiv/opensubdiv_capi_type.h index e78842036be..585d9bc49df 100644 --- a/intern/opensubdiv/opensubdiv_capi_type.h +++ b/intern/opensubdiv/opensubdiv_capi_type.h @@ -23,15 +23,9 @@ extern "C" { #endif -// Keep this a bitmask so it's possible to pass available -// evaluators to Blender. typedef enum eOpenSubdivEvaluator { - OPENSUBDIV_EVALUATOR_CPU = (1 << 0), - OPENSUBDIV_EVALUATOR_OPENMP = (1 << 1), - OPENSUBDIV_EVALUATOR_OPENCL = (1 << 2), - OPENSUBDIV_EVALUATOR_CUDA = (1 << 3), - OPENSUBDIV_EVALUATOR_GLSL_TRANSFORM_FEEDBACK = (1 << 4), - OPENSUBDIV_EVALUATOR_GLSL_COMPUTE = (1 << 5), + OPENSUBDIV_EVALUATOR_CPU = 0, + OPENSUBDIV_EVALUATOR_GPU = 1, } eOpenSubdivEvaluator; typedef enum OpenSubdiv_SchemeType { diff --git a/release/datafiles/userdef/userdef_default.c b/release/datafiles/userdef/userdef_default.c index fba3c7882c6..b4a1ab6d5f8 100644 --- a/release/datafiles/userdef/userdef_default.c +++ b/release/datafiles/userdef/userdef_default.c @@ -174,7 +174,6 @@ const UserDef U_default = { .pie_menu_confirm = 0, .pie_menu_radius = 100, .pie_menu_threshold = 12, - .opensubdiv_compute_type = 0, .factor_display_type = USER_FACTOR_AS_FACTOR, .render_display_type = USER_RENDER_DISPLAY_WINDOW, .filebrowser_display_type = USER_TEMP_SPACE_DISPLAY_WINDOW, diff --git a/release/scripts/startup/bl_ui/space_userpref.py b/release/scripts/startup/bl_ui/space_userpref.py index 7a651e8c3c8..52b2fb7f3da 100644 --- a/release/scripts/startup/bl_ui/space_userpref.py +++ b/release/scripts/startup/bl_ui/space_userpref.py @@ -589,12 +589,6 @@ class USERPREF_PT_system_cycles_devices(SystemPanel, CenterAlignMixIn, Panel): addon.preferences.draw_impl(col, context) del addon - # NOTE: Disabled for until GPU side of OpenSubdiv is brought back. - # system = prefs.system - # if hasattr(system, "opensubdiv_compute_type"): - # col.label(text="OpenSubdiv compute:") - # col.row().prop(system, "opensubdiv_compute_type", text="") - class USERPREF_PT_system_os_settings(SystemPanel, CenterAlignMixIn, Panel): bl_label = "Operating System Settings" diff --git a/source/blender/blenkernel/BKE_subdiv_eval.h b/source/blender/blenkernel/BKE_subdiv_eval.h index cb0f2ac7e32..e4a7f813a8b 100644 --- a/source/blender/blenkernel/BKE_subdiv_eval.h +++ b/source/blender/blenkernel/BKE_subdiv_eval.h @@ -20,7 +20,7 @@ struct Subdiv; typedef enum eSubdivEvaluatorType { SUBDIV_EVALUATOR_TYPE_CPU, - SUBDIV_EVALUATOR_TYPE_GLSL_COMPUTE, + SUBDIV_EVALUATOR_TYPE_GPU, } eSubdivEvaluatorType; /* Returns true if evaluator is ready for use. */ diff --git a/source/blender/blenkernel/intern/subdiv_eval.c b/source/blender/blenkernel/intern/subdiv_eval.c index fda833ffd27..841b47818f1 100644 --- a/source/blender/blenkernel/intern/subdiv_eval.c +++ b/source/blender/blenkernel/intern/subdiv_eval.c @@ -34,8 +34,8 @@ static eOpenSubdivEvaluator opensubdiv_evalutor_from_subdiv_evaluator_type( case SUBDIV_EVALUATOR_TYPE_CPU: { return OPENSUBDIV_EVALUATOR_CPU; } - case SUBDIV_EVALUATOR_TYPE_GLSL_COMPUTE: { - return OPENSUBDIV_EVALUATOR_GLSL_COMPUTE; + case SUBDIV_EVALUATOR_TYPE_GPU: { + return OPENSUBDIV_EVALUATOR_GPU; } } BLI_assert_msg(0, "Unknown evaluator type"); diff --git a/source/blender/blenkernel/intern/subdiv_modifier.c b/source/blender/blenkernel/intern/subdiv_modifier.c index 57af0192d59..2271fd90bda 100644 --- a/source/blender/blenkernel/intern/subdiv_modifier.c +++ b/source/blender/blenkernel/intern/subdiv_modifier.c @@ -98,11 +98,6 @@ static bool is_subdivision_evaluation_possible_on_gpu(void) return false; } - const int available_evaluators = openSubdiv_getAvailableEvaluators(); - if ((available_evaluators & OPENSUBDIV_EVALUATOR_GLSL_COMPUTE) == 0) { - return false; - } - return true; } diff --git a/source/blender/draw/intern/draw_cache_impl_subdivision.cc b/source/blender/draw/intern/draw_cache_impl_subdivision.cc index 29ab814cfa4..cde2b59ea23 100644 --- a/source/blender/draw/intern/draw_cache_impl_subdivision.cc +++ b/source/blender/draw/intern/draw_cache_impl_subdivision.cc @@ -2045,7 +2045,7 @@ static bool draw_subdiv_create_requested_buffers(Object *ob, draw_subdiv_invalidate_evaluator_for_orco(subdiv, mesh_eval); if (!BKE_subdiv_eval_begin_from_mesh( - subdiv, mesh_eval, nullptr, SUBDIV_EVALUATOR_TYPE_GLSL_COMPUTE, evaluator_cache)) { + subdiv, mesh_eval, nullptr, SUBDIV_EVALUATOR_TYPE_GPU, evaluator_cache)) { /* This could happen in two situations: * - OpenSubdiv is disabled. * - Something totally bad happened, and OpenSubdiv rejected our @@ -2220,7 +2220,7 @@ void DRW_create_subdivision(Object *ob, const bool use_hide) { if (g_evaluator_cache == nullptr) { - g_evaluator_cache = openSubdiv_createEvaluatorCache(OPENSUBDIV_EVALUATOR_GLSL_COMPUTE); + g_evaluator_cache = openSubdiv_createEvaluatorCache(OPENSUBDIV_EVALUATOR_GPU); } #undef TIME_SUBDIV diff --git a/source/blender/makesdna/DNA_userdef_types.h b/source/blender/makesdna/DNA_userdef_types.h index 2ceef4f623e..74fb1c3ac96 100644 --- a/source/blender/makesdna/DNA_userdef_types.h +++ b/source/blender/makesdna/DNA_userdef_types.h @@ -913,8 +913,7 @@ typedef struct UserDef { /** Pie menu distance from center before a direction is set. */ short pie_menu_threshold; - short opensubdiv_compute_type; - short _pad6; + short _pad6[2]; char factor_display_type; diff --git a/source/blender/makesrna/intern/rna_userdef.c b/source/blender/makesrna/intern/rna_userdef.c index 25eacdaab30..cfc72791123 100644 --- a/source/blender/makesrna/intern/rna_userdef.c +++ b/source/blender/makesrna/intern/rna_userdef.c @@ -38,23 +38,6 @@ #include "BLT_lang.h" -#ifdef WITH_OPENSUBDIV -static const EnumPropertyItem opensubdiv_compute_type_items[] = { - {USER_OPENSUBDIV_COMPUTE_NONE, "NONE", 0, "None", ""}, - {USER_OPENSUBDIV_COMPUTE_CPU, "CPU", 0, "CPU", ""}, - {USER_OPENSUBDIV_COMPUTE_OPENMP, "OPENMP", 0, "OpenMP", ""}, - {USER_OPENSUBDIV_COMPUTE_OPENCL, "OPENCL", 0, "OpenCL", ""}, - {USER_OPENSUBDIV_COMPUTE_CUDA, "CUDA", 0, "CUDA", ""}, - {USER_OPENSUBDIV_COMPUTE_GLSL_TRANSFORM_FEEDBACK, - "GLSL_TRANSFORM_FEEDBACK", - 0, - "GLSL Transform Feedback", - ""}, - {USER_OPENSUBDIV_COMPUTE_GLSL_COMPUTE, "GLSL_COMPUTE", 0, "GLSL Compute", ""}, - {0, NULL, 0, NULL, NULL}, -}; -#endif - const EnumPropertyItem rna_enum_preference_section_items[] = { {USER_SECTION_INTERFACE, "INTERFACE", 0, "Interface", ""}, {USER_SECTION_THEME, "THEMES", 0, "Themes", ""}, @@ -189,10 +172,6 @@ static const EnumPropertyItem rna_enum_userdef_viewport_aa_items[] = { # include "UI_interface.h" -# ifdef WITH_OPENSUBDIV -# include "opensubdiv_capi.h" -# endif - # ifdef WITH_SDL_DYNLOAD # include "sdlew.h" # endif @@ -728,55 +707,6 @@ static PointerRNA rna_Theme_space_list_generic_get(PointerRNA *ptr) return rna_pointer_inherit_refine(ptr, &RNA_ThemeSpaceListGeneric, ptr->data); } -# ifdef WITH_OPENSUBDIV -static const EnumPropertyItem *rna_userdef_opensubdiv_compute_type_itemf(bContext *UNUSED(C), - PointerRNA *UNUSED(ptr), - PropertyRNA *UNUSED(prop), - bool *r_free) -{ - EnumPropertyItem *item = NULL; - int totitem = 0; - int evaluators = openSubdiv_getAvailableEvaluators(); - - RNA_enum_items_add_value( - &item, &totitem, opensubdiv_compute_type_items, USER_OPENSUBDIV_COMPUTE_NONE); - -# define APPEND_COMPUTE(compute) \ - if (evaluators & OPENSUBDIV_EVALUATOR_##compute) { \ - RNA_enum_items_add_value( \ - &item, &totitem, opensubdiv_compute_type_items, USER_OPENSUBDIV_COMPUTE_##compute); \ - } \ - ((void)0) - - APPEND_COMPUTE(CPU); - APPEND_COMPUTE(OPENMP); - APPEND_COMPUTE(OPENCL); - APPEND_COMPUTE(CUDA); - APPEND_COMPUTE(GLSL_TRANSFORM_FEEDBACK); - APPEND_COMPUTE(GLSL_COMPUTE); - -# undef APPEND_COMPUTE - - RNA_enum_item_end(&item, &totitem); - *r_free = true; - - return item; -} - -static void rna_userdef_opensubdiv_update(Main *bmain, - Scene *UNUSED(scene), - PointerRNA *UNUSED(ptr)) -{ - Object *object; - - for (object = bmain->objects.first; object; object = object->id.next) { - DEG_id_tag_update(&object->id, ID_RECALC_TRANSFORM); - } - USERDEF_TAG_DIRTY; -} - -# endif - static const EnumPropertyItem *rna_userdef_audio_device_itemf(bContext *UNUSED(C), PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop), @@ -5699,17 +5629,6 @@ static void rna_def_userdef_system(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Audio Channels", "Audio channel count"); RNA_def_property_update(prop, 0, "rna_UserDef_audio_update"); -# ifdef WITH_OPENSUBDIV - prop = RNA_def_property(srna, "opensubdiv_compute_type", PROP_ENUM, PROP_NONE); - RNA_def_property_flag(prop, PROP_ENUM_NO_CONTEXT); - RNA_def_property_enum_sdna(prop, NULL, "opensubdiv_compute_type"); - RNA_def_property_enum_items(prop, opensubdiv_compute_type_items); - RNA_def_property_enum_funcs(prop, NULL, NULL, "rna_userdef_opensubdiv_compute_type_itemf"); - RNA_def_property_ui_text( - prop, "OpenSubdiv Compute Type", "Type of computer back-end used with OpenSubdiv"); - RNA_def_property_update(prop, NC_SPACE | ND_SPACE_PROPERTIES, "rna_userdef_opensubdiv_update"); -# endif - # ifdef WITH_CYCLES prop = RNA_def_property(srna, "legacy_compute_device_type", PROP_INT, PROP_NONE); RNA_def_property_int_sdna(prop, NULL, "compute_device_type"); -- cgit v1.2.3 From 47f3b5375648505f435f21417012ffcd78ddd923 Mon Sep 17 00:00:00 2001 From: Jun Mizutani Date: Mon, 18 Jul 2022 14:47:26 +0200 Subject: Fix T99742: crash when generating rigify rigs, after recent changes Differential Revision: https://developer.blender.org/D15473 --- source/blender/editors/armature/armature_naming.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/editors/armature/armature_naming.c b/source/blender/editors/armature/armature_naming.c index 8d5ae6f3654..4f329dbe449 100644 --- a/source/blender/editors/armature/armature_naming.c +++ b/source/blender/editors/armature/armature_naming.c @@ -285,7 +285,7 @@ void ED_armature_bone_rename(Main *bmain, /* fix camera focus */ if (ob->type == OB_CAMERA) { Camera *cam = (Camera *)ob->data; - if (cam->dof.focus_object->data == arm){ + if ((cam->dof.focus_object != NULL) && (cam->dof.focus_object->data == arm)) { if (STREQ(cam->dof.focus_subtarget, oldname)) { BLI_strncpy(cam->dof.focus_subtarget, newname, MAXBONENAME); DEG_id_tag_update(&cam->id, ID_RECALC_COPY_ON_WRITE); -- cgit v1.2.3 From dec8854bf31df3cedfa0efb5169160ba4a54d202 Mon Sep 17 00:00:00 2001 From: Damien Picard Date: Mon, 18 Jul 2022 15:09:37 +0200 Subject: I18n: translate add node operator tooltips The tooltips from the Add Node menu were extracted, but not translated. Reviewed By: mont29 Differential Revision: https://developer.blender.org/D15467 --- release/scripts/startup/bl_operators/node.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/release/scripts/startup/bl_operators/node.py b/release/scripts/startup/bl_operators/node.py index bad12ff4621..df4ca9ef170 100644 --- a/release/scripts/startup/bl_operators/node.py +++ b/release/scripts/startup/bl_operators/node.py @@ -14,6 +14,8 @@ from bpy.props import ( StringProperty, ) +from bpy.app.translations import pgettext_tip as tip_ + class NodeSetting(PropertyGroup): value: StringProperty( @@ -134,7 +136,7 @@ class NodeAddOperator: nodetype = properties["type"] bl_rna = bpy.types.Node.bl_rna_get_subclass(nodetype) if bl_rna is not None: - return bl_rna.description + return tip_(bl_rna.description) else: return "" -- cgit v1.2.3 From 3407ed5f9b489aaa81a730b0c0245dc6f3707abb Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Mon, 18 Jul 2022 14:55:21 +0200 Subject: Cleanup: change internal Cycles compact BVH default to match UI --- intern/cycles/bvh/params.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/intern/cycles/bvh/params.h b/intern/cycles/bvh/params.h index 41d851ee687..648350d03b0 100644 --- a/intern/cycles/bvh/params.h +++ b/intern/cycles/bvh/params.h @@ -129,7 +129,7 @@ class BVHParams { top_level = false; bvh_layout = BVH_LAYOUT_BVH2; - use_compact_structure = true; + use_compact_structure = false; use_unaligned_nodes = false; num_motion_curve_steps = 0; -- cgit v1.2.3 From 757041560f6dca8eecfa743dc563394623e3ad13 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Mon, 18 Jul 2022 14:38:43 +0200 Subject: Build: update Embree to 3.13.4, enable Neon2x on Arm * Allows Apple Silicon machines to use 8-wide BVH, which the release notes mention give an 8% performance boost. * An update to this version is also required for OpenPGL. This patch includes contributions from Jason Fielder and Sebastian Herholz. Ref D15286, T98555 Differential Revision: https://developer.blender.org/D15482 --- build_files/build_environment/cmake/embree.cmake | 23 +------- build_files/build_environment/cmake/versions.cmake | 4 +- build_files/build_environment/install_deps.sh | 2 +- build_files/build_environment/patches/embree.diff | 65 ++++++++++++---------- 4 files changed, 41 insertions(+), 53 deletions(-) diff --git a/build_files/build_environment/cmake/embree.cmake b/build_files/build_environment/cmake/embree.cmake index 2eafc729111..8c689cf000b 100644 --- a/build_files/build_environment/cmake/embree.cmake +++ b/build_files/build_environment/cmake/embree.cmake @@ -10,18 +10,12 @@ set(EMBREE_EXTRA_ARGS -DEMBREE_RAY_MASK=ON -DEMBREE_FILTER_FUNCTION=ON -DEMBREE_BACKFACE_CULLING=OFF - -DEMBREE_MAX_ISA=AVX2 -DEMBREE_TASKING_SYSTEM=TBB -DEMBREE_TBB_ROOT=${LIBDIR}/tbb -DTBB_ROOT=${LIBDIR}/tbb - -DTBB_STATIC_LIB=${TBB_STATIC_LIBRARY} ) -if(BLENDER_PLATFORM_ARM) - set(EMBREE_EXTRA_ARGS - ${EMBREE_EXTRA_ARGS} - -DEMBREE_MAX_ISA=NEON) -else() +if (NOT BLENDER_PLATFORM_ARM) set(EMBREE_EXTRA_ARGS ${EMBREE_EXTRA_ARGS} -DEMBREE_MAX_ISA=AVX2) @@ -30,23 +24,10 @@ endif() if(TBB_STATIC_LIBRARY) set(EMBREE_EXTRA_ARGS ${EMBREE_EXTRA_ARGS} - -DEMBREE_TBB_LIBRARY_NAME=tbb_static - -DEMBREE_TBBMALLOC_LIBRARY_NAME=tbbmalloc_static + -DEMBREE_TBB_COMPONENT=tbb_static ) endif() -if(WIN32) - set(EMBREE_BUILD_DIR ${BUILD_MODE}/) - if(BUILD_MODE STREQUAL Debug) - list(APPEND EMBREE_EXTRA_ARGS - -DEMBREE_TBBMALLOC_LIBRARY_NAME=tbbmalloc_debug - -DEMBREE_TBB_LIBRARY_NAME=tbb_debug - ) - endif() -else() - set(EMBREE_BUILD_DIR) -endif() - ExternalProject_Add(external_embree URL file://${PACKAGE_DIR}/${EMBREE_FILE} DOWNLOAD_DIR ${DOWNLOAD_DIR} diff --git a/build_files/build_environment/cmake/versions.cmake b/build_files/build_environment/cmake/versions.cmake index 1a4ad291581..42c82b68654 100644 --- a/build_files/build_environment/cmake/versions.cmake +++ b/build_files/build_environment/cmake/versions.cmake @@ -410,9 +410,9 @@ set(SQLITE_HASH fb558c49ee21a837713c4f1e7e413309aabdd9c7) set(SQLITE_HASH_TYPE SHA1) set(SQLITE_FILE sqlite-src-3240000.zip) -set(EMBREE_VERSION 3.13.3) +set(EMBREE_VERSION 3.13.4) set(EMBREE_URI https://github.com/embree/embree/archive/v${EMBREE_VERSION}.zip) -set(EMBREE_HASH f62766ba54e48a2f327c3a22596e7133) +set(EMBREE_HASH 52d0be294d6c88ba7a6c9e046796e7be) set(EMBREE_HASH_TYPE MD5) set(EMBREE_FILE embree-v${EMBREE_VERSION}.zip) diff --git a/build_files/build_environment/install_deps.sh b/build_files/build_environment/install_deps.sh index cfba4f12c73..2441b9ad89b 100755 --- a/build_files/build_environment/install_deps.sh +++ b/build_files/build_environment/install_deps.sh @@ -567,7 +567,7 @@ OPENCOLLADA_FORCE_BUILD=false OPENCOLLADA_FORCE_REBUILD=false OPENCOLLADA_SKIP=false -EMBREE_VERSION="3.13.3" +EMBREE_VERSION="3.13.4" EMBREE_VERSION_SHORT="3.13" EMBREE_VERSION_MIN="3.13" EMBREE_VERSION_MEX="4.0" diff --git a/build_files/build_environment/patches/embree.diff b/build_files/build_environment/patches/embree.diff index e83d754a465..e448fe5ee2e 100644 --- a/build_files/build_environment/patches/embree.diff +++ b/build_files/build_environment/patches/embree.diff @@ -1,30 +1,37 @@ -diff -Naur orig/common/sys/platform.h external_embree/common/sys/platform.h ---- orig/common/sys/platform.h 2020-05-13 23:08:53 -0600 -+++ external_embree/common/sys/platform.h 2020-06-13 17:40:26 -0600 -@@ -84,8 +84,8 @@ - //////////////////////////////////////////////////////////////////////////////// +diff -Naur org/kernels/rtcore_config.h.in embree-3.13.4/kernels/rtcore_config.h.in +--- org/kernels/rtcore_config.h.in 2022-06-14 22:13:52 -0600 ++++ embree-3.13.4/kernels/rtcore_config.h.in 2022-06-24 15:20:12 -0600 +@@ -14,6 +14,7 @@ + #cmakedefine01 EMBREE_MIN_WIDTH + #define RTC_MIN_WIDTH EMBREE_MIN_WIDTH + ++#cmakedefine EMBREE_STATIC_LIB + #cmakedefine EMBREE_API_NAMESPACE + + #if defined(EMBREE_API_NAMESPACE) +diff --git a/kernels/CMakeLists.txt b/kernels/CMakeLists.txt +index 7c2f43d..106b1d5 100644 +--- a/kernels/CMakeLists.txt ++++ b/kernels/CMakeLists.txt +@@ -201,6 +201,12 @@ embree_files(EMBREE_LIBRARY_FILES_AVX512 ${AVX512}) + #message("AVX2: ${EMBREE_LIBRARY_FILES_AVX2}") + #message("AVX512: ${EMBREE_LIBRARY_FILES_AVX512}") - #ifdef __WIN32__ --#define dll_export __declspec(dllexport) --#define dll_import __declspec(dllimport) -+#define dll_export -+#define dll_import - #else - #define dll_export __attribute__ ((visibility ("default"))) - #define dll_import -diff --git orig/common/tasking/CMakeLists.txt external_embree/common/tasking/CMakeLists.txt ---- orig/common/tasking/CMakeLists.txt -+++ external_embree/common/tasking/CMakeLists.txt -@@ -27,7 +27,11 @@ - else() - # If not found try getting older TBB via module (FindTBB.cmake) - unset(TBB_DIR CACHE) -- find_package(TBB 4.1 REQUIRED tbb) -+ if (TBB_STATIC_LIB) -+ find_package(TBB 4.1 REQUIRED tbb_static) -+ else() -+ find_package(TBB 4.1 REQUIRED tbb) -+ endif() - if (TBB_FOUND) - TARGET_LINK_LIBRARIES(tasking PUBLIC TBB) - TARGET_INCLUDE_DIRECTORIES(tasking PUBLIC "${TBB_INCLUDE_DIRS}") ++# Bundle Neon2x into the main static library. ++IF(EMBREE_ISA_NEON2X AND EMBREE_STATIC_LIB) ++ LIST(APPEND EMBREE_LIBRARY_FILES ${EMBREE_LIBRARY_FILES_AVX2}) ++ LIST(REMOVE_DUPLICATES EMBREE_LIBRARY_FILES) ++ENDIF() ++ + # replaces all .cpp files with a dummy file that includes that .cpp file + # this is to work around an ICC name mangling issue related to lambda functions under windows + MACRO (CreateISADummyFiles list isa) +@@ -277,7 +283,7 @@ IF (EMBREE_ISA_AVX AND EMBREE_LIBRARY_FILES_AVX) + ENDIF() + ENDIF() + +-IF (EMBREE_ISA_AVX2 AND EMBREE_LIBRARY_FILES_AVX2) ++IF (EMBREE_ISA_AVX2 AND EMBREE_LIBRARY_FILES_AVX2 AND NOT (EMBREE_ISA_NEON2X AND EMBREE_STATIC_LIB)) + DISABLE_STACK_PROTECTOR_FOR_INTERSECTORS(${EMBREE_LIBRARY_FILES_AVX2}) + ADD_LIBRARY(embree_avx2 STATIC ${EMBREE_LIBRARY_FILES_AVX2}) + TARGET_LINK_LIBRARIES(embree_avx2 PRIVATE tasking) -- cgit v1.2.3 From 1f8567ac68b667ba9b001fd2f33c4c1f24ccafa0 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Mon, 18 Jul 2022 15:47:21 +0200 Subject: Fix T99750: crash with file output node, after image colorspace saving changes --- source/blender/imbuf/intern/divers.c | 28 +++++++++++----------------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/source/blender/imbuf/intern/divers.c b/source/blender/imbuf/intern/divers.c index 588c92d748d..13c8f0887b3 100644 --- a/source/blender/imbuf/intern/divers.c +++ b/source/blender/imbuf/intern/divers.c @@ -695,9 +695,6 @@ void IMB_buffer_byte_from_byte(uchar *rect_to, void IMB_rect_from_float(ImBuf *ibuf) { - float *buffer; - const char *from_colorspace; - /* verify we have a float buffer */ if (ibuf->rect_float == NULL) { return; @@ -710,24 +707,21 @@ void IMB_rect_from_float(ImBuf *ibuf) } } - if (ibuf->float_colorspace == NULL) { - from_colorspace = IMB_colormanagement_role_colorspace_name_get(COLOR_ROLE_SCENE_LINEAR); - } - else { - from_colorspace = ibuf->float_colorspace->name; - } + const char *from_colorspace = (ibuf->float_colorspace == NULL) ? + IMB_colormanagement_role_colorspace_name_get( + COLOR_ROLE_SCENE_LINEAR) : + ibuf->float_colorspace->name; + const char *to_colorspace = (ibuf->rect_colorspace == NULL) ? + IMB_colormanagement_role_colorspace_name_get( + COLOR_ROLE_DEFAULT_BYTE) : + ibuf->rect_colorspace->name; - buffer = MEM_dupallocN(ibuf->rect_float); + float *buffer = MEM_dupallocN(ibuf->rect_float); /* first make float buffer in byte space */ const bool predivide = IMB_alpha_affects_rgb(ibuf); - IMB_colormanagement_transform(buffer, - ibuf->x, - ibuf->y, - ibuf->channels, - from_colorspace, - ibuf->rect_colorspace->name, - predivide); + IMB_colormanagement_transform( + buffer, ibuf->x, ibuf->y, ibuf->channels, from_colorspace, to_colorspace, predivide); /* convert from float's premul alpha to byte's straight alpha */ if (IMB_alpha_affects_rgb(ibuf)) { -- cgit v1.2.3 From 935b7a6f65d2c90e54b59a50fd1c488f02da0e8e Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Fri, 24 Jun 2022 17:08:54 +0300 Subject: Context: implement indexing for list properties in path_resolve. The real RNA path_resolve method supports indexing lists, but the python version on the Context object does not. This patch adds the missing feature for completeness. Differential Revision: https://developer.blender.org/D15413 --- release/scripts/modules/bpy_types.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/release/scripts/modules/bpy_types.py b/release/scripts/modules/bpy_types.py index aaa2ae59a0d..df0631ec26d 100644 --- a/release/scripts/modules/bpy_types.py +++ b/release/scripts/modules/bpy_types.py @@ -56,6 +56,21 @@ class Context(StructRNA): if value is None: return value + # If the attribute is a list property, apply subscripting. + if isinstance(value, list) and path_rest.startswith("["): + index_str, div, index_tail = path_rest[1:].partition("]") + if not div: + raise ValueError("Path index is not terminated: %s%s" % (attr, path_rest)) + try: + index = int(index_str) + except ValueError: + raise ValueError("Path index is invalid: %s[%s]" % (attr, index_str)) + if 0 <= index < len(value): + path_rest = index_tail + value = value[index] + else: + raise IndexError("Path index out of range: %s[%s]" % (attr, index_str)) + # Resolve the rest of the path if necessary. if path_rest: path_resolve_fn = getattr(value, "path_resolve", None) -- cgit v1.2.3 From cd21022b78fe48c16ab11d04682bb92271873a9c Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Sun, 10 Jul 2022 12:28:44 +0300 Subject: Context: implement an active_action property that returns a single action. Although e.g. in the dopesheet there is no specific concept of active action, displaying panels requires singling out one action reference. It is more efficient and clearer to implement this natively in the context rather than using selected_visible_actions[0]. - In the Action Editor the action is taken from the header. - In the Dope Sheet the first selected action is chosen, because there is no concept of an active channel or keyframe. - In the Graph Editor the action associated with the active curve is used, which should also be associated with the active vertex. This case may be different from selected_visible_actions[0]. Differential Revision: https://developer.blender.org/D15412 --- release/scripts/startup/bl_ui/space_dopesheet.py | 4 +- source/blender/editors/screen/screen_context.c | 54 +++++++++++++++++------- 2 files changed, 40 insertions(+), 18 deletions(-) diff --git a/release/scripts/startup/bl_ui/space_dopesheet.py b/release/scripts/startup/bl_ui/space_dopesheet.py index a1c086db745..4d1514c2777 100644 --- a/release/scripts/startup/bl_ui/space_dopesheet.py +++ b/release/scripts/startup/bl_ui/space_dopesheet.py @@ -567,10 +567,10 @@ class DOPESHEET_PT_action(DopesheetActionPanelBase, Panel): @classmethod def poll(cls, context): - return bool(context.selected_visible_actions) + return bool(context.active_action) def draw(self, context): - action = context.selected_visible_actions[0] + action = context.active_action self.draw_generic_panel(context, self.layout, action) diff --git a/source/blender/editors/screen/screen_context.c b/source/blender/editors/screen/screen_context.c index 239113c28a4..0d6b6ee1d78 100644 --- a/source/blender/editors/screen/screen_context.c +++ b/source/blender/editors/screen/screen_context.c @@ -100,6 +100,7 @@ const char *screen_context_dir[] = { "active_gpencil_frame", "active_annotation_layer", "active_operator", + "active_action", "selected_visible_actions", "selected_editable_actions", "visible_fcurves", @@ -969,6 +970,7 @@ static eContextResult screen_ctx_active_operator(const bContext *C, bContextData } static eContextResult screen_ctx_sel_actions_impl(const bContext *C, bContextDataResult *result, + bool active_only, bool editable) { bAnimContext ac; @@ -978,11 +980,17 @@ static eContextResult screen_ctx_sel_actions_impl(const bContext *C, SpaceAction *saction = (SpaceAction *)ac.sl; if (ELEM(saction->mode, SACTCONT_ACTION, SACTCONT_SHAPEKEY)) { - if (saction->action && !(editable && ID_IS_LINKED(saction->action))) { - CTX_data_id_list_add(result, &saction->action->id); + if (active_only) { + CTX_data_id_pointer_set(result, (ID *)saction->action); + } + else { + if (saction->action && !(editable && ID_IS_LINKED(saction->action))) { + CTX_data_id_list_add(result, &saction->action->id); + } + + CTX_data_type_set(result, CTX_DATA_TYPE_COLLECTION); } - CTX_data_type_set(result, CTX_DATA_TYPE_COLLECTION); return CTX_RESULT_OK; } } @@ -995,7 +1003,8 @@ static eContextResult screen_ctx_sel_actions_impl(const bContext *C, switch (ac.spacetype) { case SPACE_GRAPH: - filter |= ANIMFILTER_FCURVESONLY | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_SEL; + filter |= ANIMFILTER_FCURVESONLY | ANIMFILTER_CURVE_VISIBLE | + (active_only ? ANIMFILTER_ACTIVE : ANIMFILTER_SEL); break; case SPACE_ACTION: @@ -1006,7 +1015,7 @@ static eContextResult screen_ctx_sel_actions_impl(const bContext *C, ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype); - GSet *seen_set = BLI_gset_ptr_new("seen actions"); + GSet *seen_set = active_only ? NULL : BLI_gset_ptr_new("seen actions"); LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) { /* In dopesheet check selection status of individual items, skipping @@ -1019,36 +1028,48 @@ static eContextResult screen_ctx_sel_actions_impl(const bContext *C, bAction *action = ANIM_channel_action_get(ale); if (action) { - if (editable && ID_IS_LINKED(action)) { - continue; + if (active_only) { + CTX_data_id_pointer_set(result, (ID *)action); + break; } + else { + if (editable && ID_IS_LINKED(action)) { + continue; + } - /* Add the action to the output list if not already added. */ - if (BLI_gset_add(seen_set, action)) { - CTX_data_id_list_add(result, &action->id); + /* Add the action to the output list if not already added. */ + if (BLI_gset_add(seen_set, action)) { + CTX_data_id_list_add(result, &action->id); + } } } } - BLI_gset_free(seen_set, NULL); - ANIM_animdata_freelist(&anim_data); - CTX_data_type_set(result, CTX_DATA_TYPE_COLLECTION); + if (!active_only) { + BLI_gset_free(seen_set, NULL); + + CTX_data_type_set(result, CTX_DATA_TYPE_COLLECTION); + } + return CTX_RESULT_OK; } return CTX_RESULT_NO_DATA; } - +static eContextResult screen_ctx_active_action(const bContext *C, bContextDataResult *result) +{ + return screen_ctx_sel_actions_impl(C, result, true, false); +} static eContextResult screen_ctx_selected_visible_actions(const bContext *C, bContextDataResult *result) { - return screen_ctx_sel_actions_impl(C, result, false); + return screen_ctx_sel_actions_impl(C, result, false, false); } static eContextResult screen_ctx_selected_editable_actions(const bContext *C, bContextDataResult *result) { - return screen_ctx_sel_actions_impl(C, result, true); + return screen_ctx_sel_actions_impl(C, result, false, true); } static eContextResult screen_ctx_sel_edit_fcurves_(const bContext *C, bContextDataResult *result, @@ -1262,6 +1283,7 @@ static void ensure_ed_screen_context_functions(void) register_context_function("editable_gpencil_layers", screen_ctx_editable_gpencil_layers); register_context_function("editable_gpencil_strokes", screen_ctx_editable_gpencil_strokes); register_context_function("active_operator", screen_ctx_active_operator); + register_context_function("active_action", screen_ctx_active_action); register_context_function("selected_visible_actions", screen_ctx_selected_visible_actions); register_context_function("selected_editable_actions", screen_ctx_selected_editable_actions); register_context_function("editable_fcurves", screen_ctx_editable_fcurves); -- cgit v1.2.3 From 8358cc79639c9ee797c5de9f5496032016e945d2 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Mon, 18 Jul 2022 17:31:19 +0200 Subject: Fix wrong alpha for scene linear byte images during texture painting MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make the update logic consistent with the case where the initial texture is created. Also fixes a wrong assert. Thanks Clément for spotting this. --- source/blender/blenkernel/intern/image_gpu.cc | 6 +++--- source/blender/imbuf/intern/colormanagement.c | 3 ++- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/source/blender/blenkernel/intern/image_gpu.cc b/source/blender/blenkernel/intern/image_gpu.cc index 6edb9e1b24c..6506b40b603 100644 --- a/source/blender/blenkernel/intern/image_gpu.cc +++ b/source/blender/blenkernel/intern/image_gpu.cc @@ -737,11 +737,11 @@ static void gpu_texture_update_from_ibuf( } else { /* Byte image is in original colorspace from the file, and may need conversion. */ - if (IMB_colormanagement_space_is_data(ibuf->rect_colorspace) || - IMB_colormanagement_space_is_scene_linear(ibuf->rect_colorspace)) { + if (IMB_colormanagement_space_is_data(ibuf->rect_colorspace)) { /* Non-color data, just store buffer as is. */ } - else if (IMB_colormanagement_space_is_srgb(ibuf->rect_colorspace)) { + else if (IMB_colormanagement_space_is_srgb(ibuf->rect_colorspace) || + IMB_colormanagement_space_is_scene_linear(ibuf->rect_colorspace)) { /* sRGB or scene linear, store as byte texture that the GPU can decode directly. */ rect = (uchar *)MEM_mallocN(sizeof(uchar[4]) * w * h, __func__); if (rect == nullptr) { diff --git a/source/blender/imbuf/intern/colormanagement.c b/source/blender/imbuf/intern/colormanagement.c index b95ec1cd92b..b62bdd5521d 100644 --- a/source/blender/imbuf/intern/colormanagement.c +++ b/source/blender/imbuf/intern/colormanagement.c @@ -2213,10 +2213,11 @@ void IMB_colormanagement_imbuf_to_byte_texture(unsigned char *out_buffer, const struct ImBuf *ibuf, const bool store_premultiplied) { - /* Byte buffer storage, only for sRGB and data texture since other + /* Byte buffer storage, only for sRGB, scene linear and data texture since other * color space conversions can't be done on the GPU. */ BLI_assert(ibuf->rect && ibuf->rect_float == NULL); BLI_assert(IMB_colormanagement_space_is_srgb(ibuf->rect_colorspace) || + IMB_colormanagement_space_is_scene_linear(ibuf->rect_colorspace) || IMB_colormanagement_space_is_data(ibuf->rect_colorspace)); const unsigned char *in_buffer = (unsigned char *)ibuf->rect; -- cgit v1.2.3 From d175eb6c30b0eb9630669522c643debf6a183efd Mon Sep 17 00:00:00 2001 From: Henrik Dick Date: Mon, 18 Jul 2022 23:33:30 +0200 Subject: Fix Text Editor highlight of assert and async Due to the ordering of the checks, assert and async were not highlighted in the editor, even though they were in the list of keywords. Differential Revision: http://developer.blender.org/D15483 --- source/blender/editors/space_text/text_format_py.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/editors/space_text/text_format_py.c b/source/blender/editors/space_text/text_format_py.c index aa361f07932..28f536ffa98 100644 --- a/source/blender/editors/space_text/text_format_py.c +++ b/source/blender/editors/space_text/text_format_py.c @@ -59,9 +59,9 @@ static int txtfmt_py_find_builtinfunc(const char *string) /* clang-format off */ if (STR_LITERAL_STARTSWITH(string, "and", len)) { i = len; - } else if (STR_LITERAL_STARTSWITH(string, "as", len)) { i = len; } else if (STR_LITERAL_STARTSWITH(string, "assert", len)) { i = len; } else if (STR_LITERAL_STARTSWITH(string, "async", len)) { i = len; + } else if (STR_LITERAL_STARTSWITH(string, "as", len)) { i = len; } else if (STR_LITERAL_STARTSWITH(string, "await", len)) { i = len; } else if (STR_LITERAL_STARTSWITH(string, "break", len)) { i = len; } else if (STR_LITERAL_STARTSWITH(string, "case", len)) { i = len; -- cgit v1.2.3 From 135e530356d09378f153c4cb483a77b3375cc4f2 Mon Sep 17 00:00:00 2001 From: Chris Blackbourn Date: Tue, 19 Jul 2022 10:24:29 +1200 Subject: Fix T99781: uv minimize stretch now unflips flipped faces Add a small gradient to flipped faces proportional to length of edges. Differential Revision: https://developer.blender.org/D15475 --- source/blender/geometry/intern/uv_parametrizer.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/source/blender/geometry/intern/uv_parametrizer.c b/source/blender/geometry/intern/uv_parametrizer.c index 7ef17d4e9d0..38924c718c3 100644 --- a/source/blender/geometry/intern/uv_parametrizer.c +++ b/source/blender/geometry/intern/uv_parametrizer.c @@ -3307,8 +3307,10 @@ static float p_face_stretch(PFace *f) area = p_face_uv_area_signed(f); - if (area <= 0.0f) { /* flipped face -> infinite stretch */ - return 1e10f; + if (area <= 0.0f) { + /* When a face is flipped, provide a large penalty. + * Add on a slight gradient to unflip the face, see also: T99781. */ + return 1e8f * (1.0f + p_edge_uv_length(e1) + p_edge_uv_length(e2) + p_edge_uv_length(e3)); } w = 1.0f / (2.0f * area); -- cgit v1.2.3 From 37922eab90a9147cb0282cf648e4b6e1e5f05d66 Mon Sep 17 00:00:00 2001 From: Chris Blackbourn Date: Tue, 19 Jul 2022 13:25:46 +1200 Subject: Fix T99794: regression from uv unwrap selected Restore only_selected_faces flag inadvertently changed by c0e453233132 Differential Revision: https://developer.blender.org/D15480 --- source/blender/editors/uvedit/uvedit_unwrap_ops.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/source/blender/editors/uvedit/uvedit_unwrap_ops.c b/source/blender/editors/uvedit/uvedit_unwrap_ops.c index 7ec80a05f6d..2c7ad012dd2 100644 --- a/source/blender/editors/uvedit/uvedit_unwrap_ops.c +++ b/source/blender/editors/uvedit/uvedit_unwrap_ops.c @@ -1915,7 +1915,7 @@ static int unwrap_exec(bContext *C, wmOperator *op) UnwrapOptions options = { .topology_from_uvs = false, - .only_selected_faces = false, + .only_selected_faces = true, .only_selected_uvs = false, .fill_holes = RNA_boolean_get(op->ptr, "fill_holes"), .correct_aspect = RNA_boolean_get(op->ptr, "correct_aspect"), @@ -1926,7 +1926,6 @@ static int unwrap_exec(bContext *C, wmOperator *op) if (CTX_wm_space_image(C)) { /* Inside the UV Editor, only unwrap selected UVs. */ options.only_selected_uvs = true; - options.only_selected_faces = true; options.pin_unselected = true; } -- cgit v1.2.3 From 7ebd1f4b7960100ff9f4a1bfcf3e798fbec9dfbd Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 19 Jul 2022 13:32:23 +1000 Subject: Cleanup: quiet compiler warnings --- intern/opensubdiv/stub/opensubdiv_stub.cc | 5 ----- source/blender/editors/sculpt_paint/paint_stroke.c | 2 +- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/intern/opensubdiv/stub/opensubdiv_stub.cc b/intern/opensubdiv/stub/opensubdiv_stub.cc index 24bdcbc79ff..5eaa2df9a27 100644 --- a/intern/opensubdiv/stub/opensubdiv_stub.cc +++ b/intern/opensubdiv/stub/opensubdiv_stub.cc @@ -28,11 +28,6 @@ void openSubdiv_cleanup() { } -int openSubdiv_getAvailableEvaluators() -{ - return 0; -} - int openSubdiv_getVersionHex() { return 0; diff --git a/source/blender/editors/sculpt_paint/paint_stroke.c b/source/blender/editors/sculpt_paint/paint_stroke.c index 33a307376bb..88e7a786a47 100644 --- a/source/blender/editors/sculpt_paint/paint_stroke.c +++ b/source/blender/editors/sculpt_paint/paint_stroke.c @@ -247,7 +247,7 @@ static bool paint_stroke_use_scene_spacing(Brush *brush, ePaintMode mode) return false; } -static bool paint_tool_raycast_original(Brush *brush, ePaintMode mode) +static bool paint_tool_raycast_original(Brush *brush, ePaintMode UNUSED(mode)) { return brush->flag & BRUSH_ANCHORED; } -- cgit v1.2.3 From bbf87c4f750992f47f514733e3a7d33573f5c860 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 19 Jul 2022 13:33:02 +1000 Subject: Fix T99737: Dropping files fails with Wayland Drop events ignored the cursor coordinates, under the assumption that cursor motion events would also be sent to update the cursor location. This depended on the behavior of the compositor, it failed for Sway but worked for Gnome-shell & River. Resolve by making use of the drop events cursor coordinates. --- source/blender/windowmanager/intern/wm_window.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/source/blender/windowmanager/intern/wm_window.c b/source/blender/windowmanager/intern/wm_window.c index 70640eda605..d2182f759e5 100644 --- a/source/blender/windowmanager/intern/wm_window.c +++ b/source/blender/windowmanager/intern/wm_window.c @@ -1362,6 +1362,11 @@ static bool ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr C_void_pt event.type = MOUSEMOVE; event.val = KM_NOTHING; copy_v2_v2_int(event.prev_xy, event.xy); + + wm_cursor_position_from_ghost_screen_coords(win, &ddd->x, &ddd->y); + event.xy[0] = ddd->x; + event.xy[1] = ddd->y; + event.flag = 0; /* No context change! C->wm->windrawable is drawable, or for area queues. */ -- cgit v1.2.3 From 533a5a6a8c60686dd7d627cf7815915d7a6b467b Mon Sep 17 00:00:00 2001 From: Jesse Yurkovich Date: Mon, 18 Jul 2022 20:37:11 -0700 Subject: Fix T99785: Make Principled Hair IOR input behave like other IOR sliders Was accidental regression in rBed9b21098dd27bf9364397357f89b4c2648f40c2 Remove the input slider's PROP_FACTOR subtype in favor of the default to align with other IOR sliders. This provides much better control when dragging the value with the mouse. Differential Revision: https://developer.blender.org/D15477 --- source/blender/nodes/shader/nodes/node_shader_bsdf_hair_principled.cc | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/source/blender/nodes/shader/nodes/node_shader_bsdf_hair_principled.cc b/source/blender/nodes/shader/nodes/node_shader_bsdf_hair_principled.cc index 6495dcfffba..a0579372a15 100644 --- a/source/blender/nodes/shader/nodes/node_shader_bsdf_hair_principled.cc +++ b/source/blender/nodes/shader/nodes/node_shader_bsdf_hair_principled.cc @@ -42,8 +42,7 @@ static void node_declare(NodeDeclarationBuilder &b) .min(0.0f) .max(1.0f) .subtype(PROP_FACTOR); - b.add_input(N_("IOR")).default_value(1.55f).min(0.0f).max(1000.0f).subtype( - PROP_FACTOR); + b.add_input(N_("IOR")).default_value(1.55f).min(0.0f).max(1000.0f); b.add_input(N_("Offset")) .default_value(2.0f * ((float)M_PI) / 180.0f) .min(-M_PI_2) -- cgit v1.2.3 From e00a027c1e01396de9fa141965e57a0e6c1dc1cd Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Tue, 19 Jul 2022 11:18:30 +0200 Subject: Depsgraph: Use single task pool during evaluation Not sure why multiple pools were created: the pool should be able to handle two sets of tasks. Perhaps non-measurable improvement in terms of performance but this change simplifies code a bit. --- source/blender/depsgraph/intern/eval/deg_eval.cc | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/source/blender/depsgraph/intern/eval/deg_eval.cc b/source/blender/depsgraph/intern/eval/deg_eval.cc index 2d9d40aede6..64334e95c4c 100644 --- a/source/blender/depsgraph/intern/eval/deg_eval.cc +++ b/source/blender/depsgraph/intern/eval/deg_eval.cc @@ -361,27 +361,29 @@ void deg_evaluate_on_refresh(Depsgraph *graph) graph->is_evaluating = true; depsgraph_ensure_view_layer(graph); + /* Set up evaluation state. */ DepsgraphEvalState state; state.graph = graph; state.do_stats = graph->debug.do_time_debug(); state.need_single_thread_pass = false; + /* Prepare all nodes for evaluation. */ initialize_execution(&state, graph); + TaskPool *task_pool = deg_evaluate_task_pool_create(&state); + /* Do actual evaluation now. */ /* First, process all Copy-On-Write nodes. */ state.stage = EvaluationStage::COPY_ON_WRITE; - TaskPool *task_pool = deg_evaluate_task_pool_create(&state); schedule_graph(&state, schedule_node_to_pool, task_pool); BLI_task_pool_work_and_wait(task_pool); - BLI_task_pool_free(task_pool); /* After that, process all other nodes. */ state.stage = EvaluationStage::THREADED_EVALUATION; - task_pool = deg_evaluate_task_pool_create(&state); schedule_graph(&state, schedule_node_to_pool, task_pool); BLI_task_pool_work_and_wait(task_pool); + BLI_task_pool_free(task_pool); if (state.need_single_thread_pass) { @@ -395,6 +397,7 @@ void deg_evaluate_on_refresh(Depsgraph *graph) if (state.do_stats) { deg_eval_stats_aggregate(graph); } + /* Clear any uncleared tags - just in case. */ deg_graph_clear_tags(graph); graph->is_evaluating = false; -- cgit v1.2.3 From ff98b5eaa805d411f967921eca7bea24f24ea4dc Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Tue, 19 Jul 2022 11:27:50 +0200 Subject: Depsgraph: Clarify comment in the component node --- .../blender/depsgraph/intern/node/deg_node_component.h | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/source/blender/depsgraph/intern/node/deg_node_component.h b/source/blender/depsgraph/intern/node/deg_node_component.h index 6958866af3b..b31881a96c0 100644 --- a/source/blender/depsgraph/intern/node/deg_node_component.h +++ b/source/blender/depsgraph/intern/node/deg_node_component.h @@ -56,12 +56,14 @@ struct ComponentNode : public Node { virtual string identifier() const override; - /* Find an existing operation, if requested operation does not exist - * nullptr will be returned. */ + /* Find an existing operation, if requested operation does not exist nullptr will be returned. + * See #add_operation for the meaning and examples of #name and #name_tag. + */ OperationNode *find_operation(OperationIDKey key) const; OperationNode *find_operation(OperationCode opcode, const char *name, int name_tag) const; - /* Find an existing operation, will throw an assert() if it does not exist. */ + /* Find an existing operation, will throw an assert() if it does not exist. + * See #add_operation for the meaning and examples of #name and #name_tag. */ OperationNode *get_operation(OperationIDKey key) const; OperationNode *get_operation(OperationCode opcode, const char *name, int name_tag) const; @@ -71,12 +73,19 @@ struct ComponentNode : public Node { /** * Create a new node for representing an operation and add this to graph + * * \warning If an existing node is found, it will be modified. This helps * when node may have been partially created earlier (e.g. parent ref before * parent item is added) * * \param opcode: The operation to perform. - * \param name: Identifier for operation - used to find/locate it again. + * \param name: An optional identifier for operation. It will be used to tell operation nodes + * with the same code apart. For example, parameter operation code will have name + * set to the corresponding custom property name + * \param name_tag: An optional integer tag for the name. Is an additional way to tell operations + * apart. For example, RNA path to an array property will have the same opcode + * of PARAMETERS, name corresponding to the property name, and name tag + * corresponding to the array index within the property. */ OperationNode *add_operation(const DepsEvalOperationCb &op, OperationCode opcode, -- cgit v1.2.3 From d3c063188e00624040bc6a25441da456e3f4cbf6 Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Tue, 19 Jul 2022 11:31:44 +0200 Subject: Depsgraph: Make name and name tag optional in component node Matches the builder API, making some code less verbose. --- .../blender/depsgraph/intern/builder/deg_builder_nodes.cc | 6 ++---- source/blender/depsgraph/intern/node/deg_node_component.h | 14 +++++++++----- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc index 657bc3eb25c..ead0bdab3b2 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc @@ -174,15 +174,13 @@ IDNode *DepsgraphNodeBuilder::add_id_node(ID *id) ComponentNode *comp_cow = id_node->add_component(NodeType::COPY_ON_WRITE); OperationNode *op_cow = comp_cow->add_operation( [id_node](::Depsgraph *depsgraph) { deg_evaluate_copy_on_write(depsgraph, id_node); }, - OperationCode::COPY_ON_WRITE, - "", - -1); + OperationCode::COPY_ON_WRITE); graph_->operations.append(op_cow); } ComponentNode *visibility_component = id_node->add_component(NodeType::VISIBILITY); OperationNode *visibility_operation = visibility_component->add_operation( - nullptr, OperationCode::OPERATION, "", -1); + nullptr, OperationCode::OPERATION); /* Pin the node so that it and its relations are preserved by the unused nodes/relations * deletion. This is mainly to make it easier to debug visibility. */ visibility_operation->flag |= OperationFlag::DEPSOP_FLAG_PINNED; diff --git a/source/blender/depsgraph/intern/node/deg_node_component.h b/source/blender/depsgraph/intern/node/deg_node_component.h index b31881a96c0..ee6c56b1171 100644 --- a/source/blender/depsgraph/intern/node/deg_node_component.h +++ b/source/blender/depsgraph/intern/node/deg_node_component.h @@ -60,16 +60,20 @@ struct ComponentNode : public Node { * See #add_operation for the meaning and examples of #name and #name_tag. */ OperationNode *find_operation(OperationIDKey key) const; - OperationNode *find_operation(OperationCode opcode, const char *name, int name_tag) const; + OperationNode *find_operation(OperationCode opcode, + const char *name = "", + int name_tag = -1) const; /* Find an existing operation, will throw an assert() if it does not exist. * See #add_operation for the meaning and examples of #name and #name_tag. */ OperationNode *get_operation(OperationIDKey key) const; - OperationNode *get_operation(OperationCode opcode, const char *name, int name_tag) const; + OperationNode *get_operation(OperationCode opcode, + const char *name = "", + int name_tag = -1) const; /* Check operation exists and return it. */ bool has_operation(OperationIDKey key) const; - bool has_operation(OperationCode opcode, const char *name, int name_tag) const; + bool has_operation(OperationCode opcode, const char *name = "", int name_tag = -1) const; /** * Create a new node for representing an operation and add this to graph @@ -89,8 +93,8 @@ struct ComponentNode : public Node { */ OperationNode *add_operation(const DepsEvalOperationCb &op, OperationCode opcode, - const char *name, - int name_tag); + const char *name = "", + int name_tag = -1); /* Entry/exit operations management. * -- cgit v1.2.3 From 9b2b61a07b3fdd8eb0fecf10ec56f1b027fc255f Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Tue, 19 Jul 2022 11:40:18 +0200 Subject: Depsgraph: Cleanup, use nested namespace definition --- source/blender/depsgraph/intern/builder/deg_builder.h | 6 ++---- source/blender/depsgraph/intern/builder/deg_builder_cache.h | 6 ++---- source/blender/depsgraph/intern/builder/deg_builder_cycle.h | 6 ++---- source/blender/depsgraph/intern/builder/deg_builder_map.h | 6 ++---- source/blender/depsgraph/intern/builder/deg_builder_nodes.h | 6 ++---- source/blender/depsgraph/intern/builder/deg_builder_pchanmap.h | 6 ++---- source/blender/depsgraph/intern/builder/deg_builder_relations.h | 6 ++---- .../depsgraph/intern/builder/deg_builder_relations_drivers.h | 6 ++---- .../blender/depsgraph/intern/builder/deg_builder_relations_impl.h | 6 ++---- source/blender/depsgraph/intern/builder/deg_builder_remove_noop.h | 6 ++---- source/blender/depsgraph/intern/builder/deg_builder_rna.h | 6 ++---- source/blender/depsgraph/intern/builder/deg_builder_transitive.h | 6 ++---- source/blender/depsgraph/intern/builder/pipeline.h | 6 ++---- source/blender/depsgraph/intern/builder/pipeline_all_objects.h | 6 ++---- source/blender/depsgraph/intern/builder/pipeline_compositor.h | 6 ++---- source/blender/depsgraph/intern/builder/pipeline_from_ids.h | 6 ++---- source/blender/depsgraph/intern/builder/pipeline_render.h | 6 ++---- source/blender/depsgraph/intern/builder/pipeline_view_layer.h | 6 ++---- 18 files changed, 36 insertions(+), 72 deletions(-) diff --git a/source/blender/depsgraph/intern/builder/deg_builder.h b/source/blender/depsgraph/intern/builder/deg_builder.h index 8fbe255ed9d..6f2bde3a2ff 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder.h +++ b/source/blender/depsgraph/intern/builder/deg_builder.h @@ -13,8 +13,7 @@ struct Main; struct Object; struct bPoseChannel; -namespace blender { -namespace deg { +namespace blender::deg { struct Depsgraph; class DepsgraphBuilderCache; @@ -43,5 +42,4 @@ bool deg_check_id_in_depsgraph(const Depsgraph *graph, ID *id_orig); bool deg_check_base_in_depsgraph(const Depsgraph *graph, Base *base); void deg_graph_build_finalize(Main *bmain, Depsgraph *graph); -} // namespace deg -} // namespace blender +} // namespace blender::deg diff --git a/source/blender/depsgraph/intern/builder/deg_builder_cache.h b/source/blender/depsgraph/intern/builder/deg_builder_cache.h index 2d2bdeaf825..5568967f163 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_cache.h +++ b/source/blender/depsgraph/intern/builder/deg_builder_cache.h @@ -17,8 +17,7 @@ struct ID; struct PointerRNA; struct PropertyRNA; -namespace blender { -namespace deg { +namespace blender::deg { class DepsgraphBuilderCache; @@ -101,5 +100,4 @@ class DepsgraphBuilderCache { MEM_CXX_CLASS_ALLOC_FUNCS("DepsgraphBuilderCache"); }; -} // namespace deg -} // namespace blender +} // namespace blender::deg diff --git a/source/blender/depsgraph/intern/builder/deg_builder_cycle.h b/source/blender/depsgraph/intern/builder/deg_builder_cycle.h index 8e94e0ae21f..83d25f8c23f 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_cycle.h +++ b/source/blender/depsgraph/intern/builder/deg_builder_cycle.h @@ -7,13 +7,11 @@ #pragma once -namespace blender { -namespace deg { +namespace blender::deg { struct Depsgraph; /* Detect and solve dependency cycles. */ void deg_graph_detect_cycles(Depsgraph *graph); -} // namespace deg -} // namespace blender +} // namespace blender::deg diff --git a/source/blender/depsgraph/intern/builder/deg_builder_map.h b/source/blender/depsgraph/intern/builder/deg_builder_map.h index 50ebadeb382..2b2b4d089e9 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_map.h +++ b/source/blender/depsgraph/intern/builder/deg_builder_map.h @@ -11,8 +11,7 @@ struct ID; -namespace blender { -namespace deg { +namespace blender::deg { class BuilderMap { public: @@ -60,5 +59,4 @@ class BuilderMap { Map id_tags_; }; -} // namespace deg -} // namespace blender +} // namespace blender::deg diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes.h b/source/blender/depsgraph/intern/builder/deg_builder_nodes.h index be9983edf85..18e28311132 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_nodes.h +++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes.h @@ -50,8 +50,7 @@ struct bNodeTree; struct bPoseChannel; struct bSound; -namespace blender { -namespace deg { +namespace blender::deg { struct ComponentNode; struct Depsgraph; @@ -306,5 +305,4 @@ class DepsgraphNodeBuilder : public DepsgraphBuilder { BuilderMap built_map_; }; -} // namespace deg -} // namespace blender +} // namespace blender::deg diff --git a/source/blender/depsgraph/intern/builder/deg_builder_pchanmap.h b/source/blender/depsgraph/intern/builder/deg_builder_pchanmap.h index 2f98c8b419c..afb3a3c22bd 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_pchanmap.h +++ b/source/blender/depsgraph/intern/builder/deg_builder_pchanmap.h @@ -9,8 +9,7 @@ #include "intern/depsgraph_type.h" -namespace blender { -namespace deg { +namespace blender::deg { struct RootPChanMap { /** Debug contents of map. */ @@ -30,5 +29,4 @@ struct RootPChanMap { Map> map_; }; -} // namespace deg -} // namespace blender +} // namespace blender::deg diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations.h b/source/blender/depsgraph/intern/builder/deg_builder_relations.h index 64bdd2334d8..0cb0b60dfb0 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_relations.h +++ b/source/blender/depsgraph/intern/builder/deg_builder_relations.h @@ -70,8 +70,7 @@ struct bSound; struct PropertyRNA; -namespace blender { -namespace deg { +namespace blender::deg { struct ComponentNode; struct DepsNodeHandle; @@ -381,7 +380,6 @@ struct DepsNodeHandle { const char *default_name; }; -} // namespace deg -} // namespace blender +} // namespace blender::deg #include "intern/builder/deg_builder_relations_impl.h" diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations_drivers.h b/source/blender/depsgraph/intern/builder/deg_builder_relations_drivers.h index e517dd6a927..9860b17fd56 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_relations_drivers.h +++ b/source/blender/depsgraph/intern/builder/deg_builder_relations_drivers.h @@ -15,8 +15,7 @@ struct FCurve; -namespace blender { -namespace deg { +namespace blender::deg { /* Helper class for determining which relations are needed between driver evaluation nodes. */ class DriverDescriptor { @@ -59,5 +58,4 @@ class DriverDescriptor { bool resolve_rna(); }; -} // namespace deg -} // namespace blender +} // namespace blender::deg diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations_impl.h b/source/blender/depsgraph/intern/builder/deg_builder_relations_impl.h index aba4a011e72..76066ce97a7 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_relations_impl.h +++ b/source/blender/depsgraph/intern/builder/deg_builder_relations_impl.h @@ -15,8 +15,7 @@ #include "DNA_object_types.h" #include "DNA_rigidbody_types.h" -namespace blender { -namespace deg { +namespace blender::deg { template OperationNode *DepsgraphRelationBuilder::find_operation_node(const KeyType &key) @@ -209,5 +208,4 @@ bool DepsgraphRelationBuilder::is_same_nodetree_node_dependency(const KeyFrom &k return true; } -} // namespace deg -} // namespace blender +} // namespace blender::deg diff --git a/source/blender/depsgraph/intern/builder/deg_builder_remove_noop.h b/source/blender/depsgraph/intern/builder/deg_builder_remove_noop.h index 922d2d7dc05..ad10eb7cd10 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_remove_noop.h +++ b/source/blender/depsgraph/intern/builder/deg_builder_remove_noop.h @@ -7,13 +7,11 @@ #pragma once -namespace blender { -namespace deg { +namespace blender::deg { struct Depsgraph; /* Remove all no-op nodes that have zero outgoing relations. */ void deg_graph_remove_unused_noops(Depsgraph *graph); -} // namespace deg -} // namespace blender +} // namespace blender::deg diff --git a/source/blender/depsgraph/intern/builder/deg_builder_rna.h b/source/blender/depsgraph/intern/builder/deg_builder_rna.h index 4f482d4352d..9baa956bd80 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_rna.h +++ b/source/blender/depsgraph/intern/builder/deg_builder_rna.h @@ -14,8 +14,7 @@ struct ID; struct PointerRNA; struct PropertyRNA; -namespace blender { -namespace deg { +namespace blender::deg { struct Depsgraph; struct Node; @@ -94,5 +93,4 @@ class RNANodeQuery { bool rna_prop_affects_parameters_node(const PointerRNA *ptr, const PropertyRNA *prop); -} // namespace deg -} // namespace blender +} // namespace blender::deg diff --git a/source/blender/depsgraph/intern/builder/deg_builder_transitive.h b/source/blender/depsgraph/intern/builder/deg_builder_transitive.h index 8b208610203..63016431eec 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_transitive.h +++ b/source/blender/depsgraph/intern/builder/deg_builder_transitive.h @@ -7,13 +7,11 @@ #pragma once -namespace blender { -namespace deg { +namespace blender::deg { struct Depsgraph; /* Performs a transitive reduction to remove redundant relations. */ void deg_graph_transitive_reduction(Depsgraph *graph); -} // namespace deg -} // namespace blender +} // namespace blender::deg diff --git a/source/blender/depsgraph/intern/builder/pipeline.h b/source/blender/depsgraph/intern/builder/pipeline.h index b106e73b97c..7568aa78106 100644 --- a/source/blender/depsgraph/intern/builder/pipeline.h +++ b/source/blender/depsgraph/intern/builder/pipeline.h @@ -16,8 +16,7 @@ struct Main; struct Scene; struct ViewLayer; -namespace blender { -namespace deg { +namespace blender::deg { struct Depsgraph; class DepsgraphNodeBuilder; @@ -57,5 +56,4 @@ class AbstractBuilderPipeline { virtual void build_relations(DepsgraphRelationBuilder &relation_builder) = 0; }; -} // namespace deg -} // namespace blender +} // namespace blender::deg diff --git a/source/blender/depsgraph/intern/builder/pipeline_all_objects.h b/source/blender/depsgraph/intern/builder/pipeline_all_objects.h index 75d9605dec7..fde5e7e2163 100644 --- a/source/blender/depsgraph/intern/builder/pipeline_all_objects.h +++ b/source/blender/depsgraph/intern/builder/pipeline_all_objects.h @@ -9,8 +9,7 @@ #include "pipeline_view_layer.h" -namespace blender { -namespace deg { +namespace blender::deg { /* Builds a dependency graph that contains all objects in the view layer. * This is contrary to the regular ViewLayerBuilderPipeline, which is limited to visible objects @@ -24,5 +23,4 @@ class AllObjectsBuilderPipeline : public ViewLayerBuilderPipeline { virtual unique_ptr construct_relation_builder() override; }; -} // namespace deg -} // namespace blender +} // namespace blender::deg diff --git a/source/blender/depsgraph/intern/builder/pipeline_compositor.h b/source/blender/depsgraph/intern/builder/pipeline_compositor.h index 3325741c94a..304f2d4ec9a 100644 --- a/source/blender/depsgraph/intern/builder/pipeline_compositor.h +++ b/source/blender/depsgraph/intern/builder/pipeline_compositor.h @@ -11,8 +11,7 @@ struct bNodeTree; -namespace blender { -namespace deg { +namespace blender::deg { class CompositorBuilderPipeline : public AbstractBuilderPipeline { public: @@ -26,5 +25,4 @@ class CompositorBuilderPipeline : public AbstractBuilderPipeline { bNodeTree *nodetree_; }; -} // namespace deg -} // namespace blender +} // namespace blender::deg diff --git a/source/blender/depsgraph/intern/builder/pipeline_from_ids.h b/source/blender/depsgraph/intern/builder/pipeline_from_ids.h index a2c75c048cb..c277d44aaad 100644 --- a/source/blender/depsgraph/intern/builder/pipeline_from_ids.h +++ b/source/blender/depsgraph/intern/builder/pipeline_from_ids.h @@ -9,8 +9,7 @@ #include "pipeline.h" -namespace blender { -namespace deg { +namespace blender::deg { /* Optimized builders for dependency graph built from a given set of IDs. * @@ -37,5 +36,4 @@ class FromIDsBuilderPipeline : public AbstractBuilderPipeline { Span ids_; }; -} // namespace deg -} // namespace blender +} // namespace blender::deg diff --git a/source/blender/depsgraph/intern/builder/pipeline_render.h b/source/blender/depsgraph/intern/builder/pipeline_render.h index cb704f84c59..7eb65168ea6 100644 --- a/source/blender/depsgraph/intern/builder/pipeline_render.h +++ b/source/blender/depsgraph/intern/builder/pipeline_render.h @@ -9,8 +9,7 @@ #include "pipeline.h" -namespace blender { -namespace deg { +namespace blender::deg { class RenderBuilderPipeline : public AbstractBuilderPipeline { public: @@ -21,5 +20,4 @@ class RenderBuilderPipeline : public AbstractBuilderPipeline { virtual void build_relations(DepsgraphRelationBuilder &relation_builder) override; }; -} // namespace deg -} // namespace blender +} // namespace blender::deg diff --git a/source/blender/depsgraph/intern/builder/pipeline_view_layer.h b/source/blender/depsgraph/intern/builder/pipeline_view_layer.h index 18b9ce5d5ff..ffd6fa07776 100644 --- a/source/blender/depsgraph/intern/builder/pipeline_view_layer.h +++ b/source/blender/depsgraph/intern/builder/pipeline_view_layer.h @@ -9,8 +9,7 @@ #include "pipeline.h" -namespace blender { -namespace deg { +namespace blender::deg { class ViewLayerBuilderPipeline : public AbstractBuilderPipeline { public: @@ -21,5 +20,4 @@ class ViewLayerBuilderPipeline : public AbstractBuilderPipeline { virtual void build_relations(DepsgraphRelationBuilder &relation_builder) override; }; -} // namespace deg -} // namespace blender +} // namespace blender::deg -- cgit v1.2.3 From 2280a71f900eb7907c243f76930a7e9779a90d7d Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Tue, 19 Jul 2022 12:41:47 +0200 Subject: Depsgraph: Refactor evaluation into smaller reusable functions Should be no functional changes. --- source/blender/depsgraph/intern/eval/deg_eval.cc | 52 +++++++++++++++++------- 1 file changed, 37 insertions(+), 15 deletions(-) diff --git a/source/blender/depsgraph/intern/eval/deg_eval.cc b/source/blender/depsgraph/intern/eval/deg_eval.cc index 64334e95c4c..9da9586c1cf 100644 --- a/source/blender/depsgraph/intern/eval/deg_eval.cc +++ b/source/blender/depsgraph/intern/eval/deg_eval.cc @@ -302,8 +302,28 @@ void schedule_node_to_queue(OperationNode *node, BLI_gsqueue_push(evaluation_queue, &node); } -void evaluate_graph_single_threaded(DepsgraphEvalState *state) +/* Evaluate given stage of the dependency graph evaluation using multiple threads. + * + * NOTE: Will assign the `state->stage` to the given stage. */ +void evaluate_graph_threaded_stage(DepsgraphEvalState *state, + TaskPool *task_pool, + const EvaluationStage stage) { + state->stage = stage; + + schedule_graph(state, schedule_node_to_pool, task_pool); + BLI_task_pool_work_and_wait(task_pool); +} + +/* Evaluate remaining operations of the dependency graph in a single threaded manner. */ +void evaluate_graph_single_threaded_if_needed(DepsgraphEvalState *state) +{ + if (!state->need_single_thread_pass) { + return; + } + + state->stage = EvaluationStage::SINGLE_THREADED_WORKAROUND; + GSQueue *evaluation_queue = BLI_gsqueue_new(sizeof(OperationNode *)); schedule_graph(state, schedule_node_to_queue, evaluation_queue); @@ -371,25 +391,27 @@ void deg_evaluate_on_refresh(Depsgraph *graph) /* Prepare all nodes for evaluation. */ initialize_execution(&state, graph); - TaskPool *task_pool = deg_evaluate_task_pool_create(&state); + /* Evaluation happens in several incremental steps: + * + * - Start with the copy-on-write operations which never form dependency cycles. This will ensure + * that if a dependency graph has a cycle evaluation functions will always "see" valid expanded + * datablock. It might not be evaluated yet, but at least the datablock will be valid. + * + * - Multi-threaded evaluation of all possible nodes. + * Certain operations (and their subtrees) could be ignored. For example, meta-balls are not + * safe from threading point of view, so the threaded evaluation will stop at the metaball + * operation node. + * + * - Single-threaded pass of all remaining operations. */ - /* Do actual evaluation now. */ - /* First, process all Copy-On-Write nodes. */ - state.stage = EvaluationStage::COPY_ON_WRITE; - schedule_graph(&state, schedule_node_to_pool, task_pool); - BLI_task_pool_work_and_wait(task_pool); + TaskPool *task_pool = deg_evaluate_task_pool_create(&state); - /* After that, process all other nodes. */ - state.stage = EvaluationStage::THREADED_EVALUATION; - schedule_graph(&state, schedule_node_to_pool, task_pool); - BLI_task_pool_work_and_wait(task_pool); + evaluate_graph_threaded_stage(&state, task_pool, EvaluationStage::COPY_ON_WRITE); + evaluate_graph_threaded_stage(&state, task_pool, EvaluationStage::THREADED_EVALUATION); BLI_task_pool_free(task_pool); - if (state.need_single_thread_pass) { - state.stage = EvaluationStage::SINGLE_THREADED_WORKAROUND; - evaluate_graph_single_threaded(&state); - } + evaluate_graph_single_threaded_if_needed(&state); /* Finalize statistics gathering. This is because we only gather single * operation timing here, without aggregating anything to avoid any extra -- cgit v1.2.3 From 99faebfca6d4f7953d970dcdbdf901f78a8cf4c3 Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Tue, 19 Jul 2022 12:51:24 +0200 Subject: Depsgraph: Cleanup, don't mic static function and anonymous namespace --- source/blender/depsgraph/intern/eval/deg_eval.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/source/blender/depsgraph/intern/eval/deg_eval.cc b/source/blender/depsgraph/intern/eval/deg_eval.cc index 9da9586c1cf..9ac1f5275ac 100644 --- a/source/blender/depsgraph/intern/eval/deg_eval.cc +++ b/source/blender/depsgraph/intern/eval/deg_eval.cc @@ -354,9 +354,7 @@ void depsgraph_ensure_view_layer(Depsgraph *graph) deg_update_copy_on_write_datablock(graph, scene_id_node); } -} // namespace - -static TaskPool *deg_evaluate_task_pool_create(DepsgraphEvalState *state) +TaskPool *deg_evaluate_task_pool_create(DepsgraphEvalState *state) { if (G.debug & G_DEBUG_DEPSGRAPH_NO_THREADS) { return BLI_task_pool_create_no_threads(state); @@ -365,6 +363,8 @@ static TaskPool *deg_evaluate_task_pool_create(DepsgraphEvalState *state) return BLI_task_pool_create_suspended(state, TASK_PRIORITY_HIGH); } +} // namespace + void deg_evaluate_on_refresh(Depsgraph *graph) { /* Nothing to update, early out. */ -- cgit v1.2.3 From 6d2100f7de1002623172ede914fa754d714f71d8 Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Tue, 19 Jul 2022 13:00:19 +0200 Subject: Depsgraph: Introduce operation code for visibility operation No functional changes, just makes code more semantically clear. --- source/blender/depsgraph/intern/builder/deg_builder_nodes.cc | 2 +- source/blender/depsgraph/intern/node/deg_node_operation.cc | 2 ++ source/blender/depsgraph/intern/node/deg_node_operation.h | 1 + 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc index ead0bdab3b2..473dda2290c 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc @@ -180,7 +180,7 @@ IDNode *DepsgraphNodeBuilder::add_id_node(ID *id) ComponentNode *visibility_component = id_node->add_component(NodeType::VISIBILITY); OperationNode *visibility_operation = visibility_component->add_operation( - nullptr, OperationCode::OPERATION); + nullptr, OperationCode::VISIBILITY); /* Pin the node so that it and its relations are preserved by the unused nodes/relations * deletion. This is mainly to make it easier to debug visibility. */ visibility_operation->flag |= OperationFlag::DEPSOP_FLAG_PINNED; diff --git a/source/blender/depsgraph/intern/node/deg_node_operation.cc b/source/blender/depsgraph/intern/node/deg_node_operation.cc index c29aeefd9b2..3029379d141 100644 --- a/source/blender/depsgraph/intern/node/deg_node_operation.cc +++ b/source/blender/depsgraph/intern/node/deg_node_operation.cc @@ -32,6 +32,8 @@ const char *operationCodeAsString(OperationCode opcode) return "PARAMETERS_EVAL"; case OperationCode::PARAMETERS_EXIT: return "PARAMETERS_EXIT"; + case OperationCode::VISIBILITY: + return "VISIBILITY"; /* Animation, Drivers, etc. */ case OperationCode::ANIMATION_ENTRY: return "ANIMATION_ENTRY"; diff --git a/source/blender/depsgraph/intern/node/deg_node_operation.h b/source/blender/depsgraph/intern/node/deg_node_operation.h index d4916be1113..aaa508ab3e8 100644 --- a/source/blender/depsgraph/intern/node/deg_node_operation.h +++ b/source/blender/depsgraph/intern/node/deg_node_operation.h @@ -34,6 +34,7 @@ enum class OperationCode { PARAMETERS_ENTRY, PARAMETERS_EVAL, PARAMETERS_EXIT, + VISIBILITY, /* Animation, Drivers, etc. --------------------------------------------- */ /* NLA + Action */ -- cgit v1.2.3 From 73f8a7ca0a30de398b0309d15cc459cfeff495ce Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Tue, 19 Jul 2022 14:25:02 +0200 Subject: Depsgraph: Cleanup, comments wrapping and spacing between lines Should make it easier to read. No functional changes expected. --- .../intern/builder/deg_builder_relations.cc | 24 ++++++++++++++++++++-- .../depsgraph/intern/node/deg_node_operation.h | 9 +++++--- 2 files changed, 28 insertions(+), 5 deletions(-) diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc index 92396a89cea..4fe8a626af5 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc @@ -692,7 +692,7 @@ void DepsgraphRelationBuilder::build_object(Object *object) const BuilderStack::ScopedEntry stack_entry = stack_.trace(object->id); - /* Object Transforms */ + /* Object Transforms. */ OperationCode base_op = (object->parent) ? OperationCode::TRANSFORM_PARENT : OperationCode::TRANSFORM_LOCAL; OperationKey base_op_key(&object->id, NodeType::TRANSFORM, base_op); @@ -705,9 +705,12 @@ void DepsgraphRelationBuilder::build_object(Object *object) OperationKey final_transform_key( &object->id, NodeType::TRANSFORM, OperationCode::TRANSFORM_FINAL); OperationKey ob_eval_key(&object->id, NodeType::TRANSFORM, OperationCode::TRANSFORM_EVAL); + add_relation(init_transform_key, local_transform_key, "Transform Init"); + /* Various flags, flushing from bases/collections. */ build_object_layer_component_relations(object); + /* Parenting. */ if (object->parent != nullptr) { /* Make sure parent object's relations are built. */ @@ -717,30 +720,35 @@ void DepsgraphRelationBuilder::build_object(Object *object) /* Local -> parent. */ add_relation(local_transform_key, parent_transform_key, "ObLocal -> ObParent"); } + /* Modifiers. */ if (object->modifiers.first != nullptr) { BuilderWalkUserData data; data.builder = this; BKE_modifiers_foreach_ID_link(object, modifier_walk, &data); } + /* Grease Pencil Modifiers. */ if (object->greasepencil_modifiers.first != nullptr) { BuilderWalkUserData data; data.builder = this; BKE_gpencil_modifiers_foreach_ID_link(object, modifier_walk, &data); } + /* Shader FX. */ if (object->shader_fx.first != nullptr) { BuilderWalkUserData data; data.builder = this; BKE_shaderfx_foreach_ID_link(object, modifier_walk, &data); } + /* Constraints. */ if (object->constraints.first != nullptr) { BuilderWalkUserData data; data.builder = this; BKE_constraints_id_loop(&object->constraints, constraint_walk, &data); } + /* Object constraints. */ OperationKey object_transform_simulation_init_key( &object->id, NodeType::TRANSFORM, OperationCode::TRANSFORM_SIMULATION_INIT); @@ -767,30 +775,39 @@ void DepsgraphRelationBuilder::build_object(Object *object) final_transform_key, "Simulation -> Final Transform"); } + build_idproperties(object->id.properties); + /* Animation data */ build_animdata(&object->id); + /* Object data. */ build_object_data(object); + /* Particle systems. */ if (object->particlesystem.first != nullptr) { build_particle_systems(object); } + /* Force field Texture. */ if ((object->pd != nullptr) && (object->pd->forcefield == PFIELD_TEXTURE) && (object->pd->tex != nullptr)) { build_texture(object->pd->tex); } + /* Object dupligroup. */ if (object->instance_collection != nullptr) { build_collection(nullptr, object, object->instance_collection); } + /* Point caches. */ build_object_pointcache(object); + /* Synchronization back to original object. */ OperationKey synchronize_key( &object->id, NodeType::SYNCHRONIZATION, OperationCode::SYNCHRONIZE_TO_ORIGINAL); add_relation(final_transform_key, synchronize_key, "Synchronize to Original"); + /* Parameters. */ build_parameters(&object->id); } @@ -2460,7 +2477,10 @@ void DepsgraphRelationBuilder::build_camera(Camera *camera) ComponentKey dof_ob_key(&camera->dof.focus_object->id, NodeType::TRANSFORM); add_relation(dof_ob_key, camera_parameters_key, "Camera DOF"); if (camera->dof.focus_subtarget[0]) { - OperationKey target_key(&camera->dof.focus_object->id, NodeType::BONE, camera->dof.focus_subtarget, OperationCode::BONE_DONE); + OperationKey target_key(&camera->dof.focus_object->id, + NodeType::BONE, + camera->dof.focus_subtarget, + OperationCode::BONE_DONE); add_relation(target_key, camera_parameters_key, "Camera DOF subtarget"); } } diff --git a/source/blender/depsgraph/intern/node/deg_node_operation.h b/source/blender/depsgraph/intern/node/deg_node_operation.h index aaa508ab3e8..4808e004dc2 100644 --- a/source/blender/depsgraph/intern/node/deg_node_operation.h +++ b/source/blender/depsgraph/intern/node/deg_node_operation.h @@ -203,13 +203,16 @@ const char *operationCodeAsString(OperationCode opcode); enum OperationFlag { /* Node needs to be updated. */ DEPSOP_FLAG_NEEDS_UPDATE = (1 << 0), + /* Node was directly modified, causing need for update. */ DEPSOP_FLAG_DIRECTLY_MODIFIED = (1 << 1), + /* Node was updated due to user input. */ DEPSOP_FLAG_USER_MODIFIED = (1 << 2), - /* Node may not be removed, even when it has no evaluation callback and no - * outgoing relations. This is for NO-OP nodes that are purely used to indicate a - * relation between components/IDs, and not for connecting to an operation. */ + + /* Node may not be removed, even when it has no evaluation callback and no outgoing relations. + * This is for NO-OP nodes that are purely used to indicate a relation between components/IDs, + * and not for connecting to an operation. */ DEPSOP_FLAG_PINNED = (1 << 3), /* Set of flags which gets flushed along the relations. */ -- cgit v1.2.3 From 835203fde8d61014c727bfc9c8aa3d32862f2592 Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Tue, 19 Jul 2022 14:35:18 +0200 Subject: Depsgraph: Localize synchronization component visibility handling Move it from generic visibility handling to the synchronization component node implementation. Should be no functional changes. --- .../blender/depsgraph/intern/builder/deg_builder.cc | 13 ------------- .../depsgraph/intern/node/deg_node_component.h | 20 +++++++++++++++++++- 2 files changed, 19 insertions(+), 14 deletions(-) diff --git a/source/blender/depsgraph/intern/builder/deg_builder.cc b/source/blender/depsgraph/intern/builder/deg_builder.cc index a3cd821e82f..888e0649065 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder.cc @@ -138,19 +138,6 @@ void deg_graph_build_flush_visibility(Depsgraph *graph) for (IDNode *id_node : graph->id_nodes) { for (ComponentNode *comp_node : id_node->components.values()) { comp_node->affects_directly_visible |= id_node->is_directly_visible; - - /* Enforce "visibility" of the synchronization component. - * - * This component is never connected to other ID nodes, and hence can not be handled in the - * same way as other components needed for evaluation. It is only needed for proper - * evaluation of the ID node it belongs to. - * - * The design is such that the synchronization is supposed to happen whenever any part of the - * ID changed/evaluated. Here we mark the component as "visible" so that genetic recalc flag - * flushing and scheduling will handle the component in a generic manner. */ - if (comp_node->type == NodeType::SYNCHRONIZATION) { - comp_node->affects_directly_visible = true; - } } } diff --git a/source/blender/depsgraph/intern/node/deg_node_component.h b/source/blender/depsgraph/intern/node/deg_node_component.h index ee6c56b1171..fd6af43f40e 100644 --- a/source/blender/depsgraph/intern/node/deg_node_component.h +++ b/source/blender/depsgraph/intern/node/deg_node_component.h @@ -195,7 +195,6 @@ DEG_COMPONENT_NODE_DECLARE_GENERIC(ShadingParameters); DEG_COMPONENT_NODE_DECLARE_GENERIC(Transform); DEG_COMPONENT_NODE_DECLARE_NO_COW_TAG_ON_UPDATE(ObjectFromLayer); DEG_COMPONENT_NODE_DECLARE_GENERIC(Dupli); -DEG_COMPONENT_NODE_DECLARE_GENERIC(Synchronization); DEG_COMPONENT_NODE_DECLARE_GENERIC(Audio); DEG_COMPONENT_NODE_DECLARE_GENERIC(Armature); DEG_COMPONENT_NODE_DECLARE_GENERIC(GenericDatablock); @@ -203,6 +202,25 @@ DEG_COMPONENT_NODE_DECLARE_NO_COW(Visibility); DEG_COMPONENT_NODE_DECLARE_GENERIC(Simulation); DEG_COMPONENT_NODE_DECLARE_GENERIC(NTreeOutput); +/* Synchronization Component. */ +struct SynchronizationComponentNode : public ComponentNode { + SynchronizationComponentNode() + { + /* Enforce "visibility" of the synchronization component. + * + * This component is never connected to other ID nodes, and hence can not be handled in the + * same way as other components needed for evaluation. It is only needed for proper + * evaluation of the ID node it belongs to. + * + * The design is such that the synchronization is supposed to happen whenever any part of the + * ID changed/evaluated. Here we mark the component as "visible" so that genetic recalc flag + * flushing and scheduling will handle the component in a generic manner. */ + affects_directly_visible = true; + } + + DEG_COMPONENT_NODE_DECLARE; +}; + /* Bone Component */ struct BoneComponentNode : public ComponentNode { /** Initialize 'bone component' node - from pointer data given. */ -- cgit v1.2.3 From e8465f941cefe2cd408a5e9db97e9a9b3eef1d25 Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Tue, 19 Jul 2022 14:44:07 +0200 Subject: Depsgraph: Cleanup, use nested namespace definition --- source/blender/depsgraph/intern/debug/deg_debug.h | 6 ++---- source/blender/depsgraph/intern/debug/deg_time_average.h | 6 ++---- source/blender/depsgraph/intern/depsgraph.h | 6 ++---- source/blender/depsgraph/intern/depsgraph_physics.h | 6 ++---- source/blender/depsgraph/intern/depsgraph_registry.h | 6 ++---- source/blender/depsgraph/intern/depsgraph_relation.h | 6 ++---- source/blender/depsgraph/intern/depsgraph_tag.h | 6 ++---- source/blender/depsgraph/intern/depsgraph_type.h | 6 ++---- source/blender/depsgraph/intern/depsgraph_update.h | 6 ++---- source/blender/depsgraph/intern/eval/deg_eval.h | 6 ++---- source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.h | 6 ++---- source/blender/depsgraph/intern/eval/deg_eval_flush.h | 6 ++---- source/blender/depsgraph/intern/eval/deg_eval_runtime_backup.h | 6 ++---- .../depsgraph/intern/eval/deg_eval_runtime_backup_animation.h | 6 ++---- .../depsgraph/intern/eval/deg_eval_runtime_backup_gpencil.h | 6 ++---- .../depsgraph/intern/eval/deg_eval_runtime_backup_modifier.h | 6 ++---- .../depsgraph/intern/eval/deg_eval_runtime_backup_movieclip.h | 6 ++---- .../blender/depsgraph/intern/eval/deg_eval_runtime_backup_object.h | 6 ++---- .../blender/depsgraph/intern/eval/deg_eval_runtime_backup_pose.h | 6 ++---- .../blender/depsgraph/intern/eval/deg_eval_runtime_backup_scene.h | 6 ++---- .../depsgraph/intern/eval/deg_eval_runtime_backup_sequence.h | 6 ++---- .../depsgraph/intern/eval/deg_eval_runtime_backup_sequencer.h | 6 ++---- .../blender/depsgraph/intern/eval/deg_eval_runtime_backup_sound.h | 6 ++---- .../blender/depsgraph/intern/eval/deg_eval_runtime_backup_volume.h | 6 ++---- source/blender/depsgraph/intern/eval/deg_eval_stats.h | 6 ++---- source/blender/depsgraph/intern/node/deg_node.h | 6 ++---- source/blender/depsgraph/intern/node/deg_node_component.h | 6 ++---- source/blender/depsgraph/intern/node/deg_node_factory.h | 7 ++----- source/blender/depsgraph/intern/node/deg_node_factory_impl.h | 6 ++---- source/blender/depsgraph/intern/node/deg_node_id.h | 6 ++---- source/blender/depsgraph/intern/node/deg_node_operation.h | 6 ++---- source/blender/depsgraph/intern/node/deg_node_time.h | 6 ++---- 32 files changed, 64 insertions(+), 129 deletions(-) diff --git a/source/blender/depsgraph/intern/debug/deg_debug.h b/source/blender/depsgraph/intern/debug/deg_debug.h index ee6c5b046b7..8f18bd70d36 100644 --- a/source/blender/depsgraph/intern/debug/deg_debug.h +++ b/source/blender/depsgraph/intern/debug/deg_debug.h @@ -14,8 +14,7 @@ #include "DEG_depsgraph_debug.h" -namespace blender { -namespace deg { +namespace blender::deg { class DepsgraphDebug { public: @@ -74,5 +73,4 @@ bool terminal_do_color(void); string color_for_pointer(const void *pointer); string color_end(void); -} // namespace deg -} // namespace blender +} // namespace blender::deg diff --git a/source/blender/depsgraph/intern/debug/deg_time_average.h b/source/blender/depsgraph/intern/debug/deg_time_average.h index cc47f03b221..d4eb05a9ddb 100644 --- a/source/blender/depsgraph/intern/debug/deg_time_average.h +++ b/source/blender/depsgraph/intern/debug/deg_time_average.h @@ -7,8 +7,7 @@ #pragma once -namespace blender { -namespace deg { +namespace blender::deg { /* Utility class which takes care of calculating average of time series, such as FPS counters. */ template class AveragedTimeSampler { @@ -52,5 +51,4 @@ template class AveragedTimeSampler { int next_sample_index_; }; -} // namespace deg -} // namespace blender +} // namespace blender::deg diff --git a/source/blender/depsgraph/intern/depsgraph.h b/source/blender/depsgraph/intern/depsgraph.h index ca4ce058904..fc92580f5f5 100644 --- a/source/blender/depsgraph/intern/depsgraph.h +++ b/source/blender/depsgraph/intern/depsgraph.h @@ -31,8 +31,7 @@ struct ID; struct Scene; struct ViewLayer; -namespace blender { -namespace deg { +namespace blender::deg { struct IDNode; struct Node; @@ -162,5 +161,4 @@ struct Depsgraph { MEM_CXX_CLASS_ALLOC_FUNCS("Depsgraph"); }; -} // namespace deg -} // namespace blender +} // namespace blender::deg diff --git a/source/blender/depsgraph/intern/depsgraph_physics.h b/source/blender/depsgraph/intern/depsgraph_physics.h index a8d37f76b09..2105eb62ce1 100644 --- a/source/blender/depsgraph/intern/depsgraph_physics.h +++ b/source/blender/depsgraph/intern/depsgraph_physics.h @@ -10,8 +10,7 @@ struct Collection; struct ListBase; -namespace blender { -namespace deg { +namespace blender::deg { struct Depsgraph; @@ -21,5 +20,4 @@ ListBase *build_collision_relations(Depsgraph *graph, unsigned int modifier_type); void clear_physics_relations(Depsgraph *graph); -} // namespace deg -} // namespace blender +} // namespace blender::deg diff --git a/source/blender/depsgraph/intern/depsgraph_registry.h b/source/blender/depsgraph/intern/depsgraph_registry.h index 2746f17590a..4ab63dcc77d 100644 --- a/source/blender/depsgraph/intern/depsgraph_registry.h +++ b/source/blender/depsgraph/intern/depsgraph_registry.h @@ -11,8 +11,7 @@ struct Main; -namespace blender { -namespace deg { +namespace blender::deg { struct Depsgraph; @@ -20,5 +19,4 @@ void register_graph(Depsgraph *depsgraph); void unregister_graph(Depsgraph *depsgraph); Span get_all_registered_graphs(Main *bmain); -} // namespace deg -} // namespace blender +} // namespace blender::deg diff --git a/source/blender/depsgraph/intern/depsgraph_relation.h b/source/blender/depsgraph/intern/depsgraph_relation.h index 4d51ad85878..1bacb9abfa6 100644 --- a/source/blender/depsgraph/intern/depsgraph_relation.h +++ b/source/blender/depsgraph/intern/depsgraph_relation.h @@ -9,8 +9,7 @@ #include "MEM_guardedalloc.h" -namespace blender { -namespace deg { +namespace blender::deg { struct Node; @@ -49,5 +48,4 @@ struct Relation { MEM_CXX_CLASS_ALLOC_FUNCS("Relation"); }; -} // namespace deg -} // namespace blender +} // namespace blender::deg diff --git a/source/blender/depsgraph/intern/depsgraph_tag.h b/source/blender/depsgraph/intern/depsgraph_tag.h index a0518420bfb..b722aab5719 100644 --- a/source/blender/depsgraph/intern/depsgraph_tag.h +++ b/source/blender/depsgraph/intern/depsgraph_tag.h @@ -10,8 +10,7 @@ struct ID; struct Main; -namespace blender { -namespace deg { +namespace blender::deg { struct Depsgraph; @@ -29,5 +28,4 @@ void graph_id_tag_update( * Will do nothing if the graph is not tagged for visibility update. */ void graph_tag_ids_for_visible_update(Depsgraph *graph); -} // namespace deg -} // namespace blender +} // namespace blender::deg diff --git a/source/blender/depsgraph/intern/depsgraph_type.h b/source/blender/depsgraph/intern/depsgraph_type.h index 9cf14a831bc..9e21d124f83 100644 --- a/source/blender/depsgraph/intern/depsgraph_type.h +++ b/source/blender/depsgraph/intern/depsgraph_type.h @@ -34,8 +34,7 @@ struct Depsgraph; struct CustomData_MeshMasks; -namespace blender { -namespace deg { +namespace blender::deg { /* Commonly used types. */ using std::deque; @@ -153,5 +152,4 @@ struct DEGCustomDataMeshMasks { } }; -} // namespace deg -} // namespace blender +} // namespace blender::deg diff --git a/source/blender/depsgraph/intern/depsgraph_update.h b/source/blender/depsgraph/intern/depsgraph_update.h index 18f8e6acab6..c5a72157bd2 100644 --- a/source/blender/depsgraph/intern/depsgraph_update.h +++ b/source/blender/depsgraph/intern/depsgraph_update.h @@ -10,12 +10,10 @@ struct DEGEditorUpdateContext; struct ID; -namespace blender { -namespace deg { +namespace blender::deg { void deg_editors_id_update(const DEGEditorUpdateContext *update_ctx, struct ID *id); void deg_editors_scene_update(const DEGEditorUpdateContext *update_ctx, bool updated); -} // namespace deg -} // namespace blender +} // namespace blender::deg diff --git a/source/blender/depsgraph/intern/eval/deg_eval.h b/source/blender/depsgraph/intern/eval/deg_eval.h index ba86e1a349d..6937231d81a 100644 --- a/source/blender/depsgraph/intern/eval/deg_eval.h +++ b/source/blender/depsgraph/intern/eval/deg_eval.h @@ -9,8 +9,7 @@ #pragma once -namespace blender { -namespace deg { +namespace blender::deg { struct Depsgraph; @@ -23,5 +22,4 @@ struct Depsgraph; */ void deg_evaluate_on_refresh(Depsgraph *graph); -} // namespace deg -} // namespace blender +} // namespace blender::deg diff --git a/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.h b/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.h index 29322d58218..cbf450aa3f1 100644 --- a/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.h +++ b/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.h @@ -27,8 +27,7 @@ struct ID; struct Depsgraph; -namespace blender { -namespace deg { +namespace blender::deg { struct Depsgraph; class DepsgraphNodeBuilder; @@ -77,5 +76,4 @@ bool deg_copy_on_write_is_expanded(const struct ID *id_cow); bool deg_copy_on_write_is_needed(const ID *id_orig); bool deg_copy_on_write_is_needed(const ID_Type id_type); -} // namespace deg -} // namespace blender +} // namespace blender::deg diff --git a/source/blender/depsgraph/intern/eval/deg_eval_flush.h b/source/blender/depsgraph/intern/eval/deg_eval_flush.h index 6eb232e76e5..614ca66faed 100644 --- a/source/blender/depsgraph/intern/eval/deg_eval_flush.h +++ b/source/blender/depsgraph/intern/eval/deg_eval_flush.h @@ -9,8 +9,7 @@ #pragma once -namespace blender { -namespace deg { +namespace blender::deg { struct Depsgraph; @@ -24,5 +23,4 @@ void deg_graph_flush_updates(struct Depsgraph *graph); */ void deg_graph_clear_tags(struct Depsgraph *graph); -} // namespace deg -} // namespace blender +} // namespace blender::deg diff --git a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup.h b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup.h index deb21715a28..3d9b308c5ad 100644 --- a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup.h +++ b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup.h @@ -17,8 +17,7 @@ #include "intern/eval/deg_eval_runtime_backup_sound.h" #include "intern/eval/deg_eval_runtime_backup_volume.h" -namespace blender { -namespace deg { +namespace blender::deg { struct Depsgraph; @@ -59,5 +58,4 @@ class RuntimeBackup { GPencilBackup gpencil_backup; }; -} // namespace deg -} // namespace blender +} // namespace blender::deg diff --git a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_animation.h b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_animation.h index 92847b330e8..807cc91242e 100644 --- a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_animation.h +++ b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_animation.h @@ -11,8 +11,7 @@ #include "intern/depsgraph_type.h" -namespace blender { -namespace deg { +namespace blender::deg { struct Depsgraph; @@ -46,5 +45,4 @@ class AnimationBackup { Vector values_backup; }; -} // namespace deg -} // namespace blender +} // namespace blender::deg diff --git a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_gpencil.h b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_gpencil.h index 68eff01fd60..95c0ca3a2fe 100644 --- a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_gpencil.h +++ b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_gpencil.h @@ -9,8 +9,7 @@ struct bGPdata; -namespace blender { -namespace deg { +namespace blender::deg { struct Depsgraph; @@ -25,5 +24,4 @@ class GPencilBackup { const Depsgraph *depsgraph; }; -} // namespace deg -} // namespace blender +} // namespace blender::deg diff --git a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_modifier.h b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_modifier.h index faec8f7c065..ee51204b24c 100644 --- a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_modifier.h +++ b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_modifier.h @@ -11,8 +11,7 @@ struct ModifierData; -namespace blender { -namespace deg { +namespace blender::deg { class ModifierDataBackup { public: @@ -22,5 +21,4 @@ class ModifierDataBackup { void *runtime; }; -} // namespace deg -} // namespace blender +} // namespace blender::deg diff --git a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_movieclip.h b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_movieclip.h index 0e826e8f72a..aa13914d8c8 100644 --- a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_movieclip.h +++ b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_movieclip.h @@ -11,8 +11,7 @@ struct MovieClip; struct MovieClipCache; struct anim; -namespace blender { -namespace deg { +namespace blender::deg { struct Depsgraph; @@ -30,5 +29,4 @@ class MovieClipBackup { struct MovieClipCache *cache; }; -} // namespace deg -} // namespace blender +} // namespace blender::deg diff --git a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_object.h b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_object.h index 3b138feec0b..c9cc167d927 100644 --- a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_object.h +++ b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_object.h @@ -17,8 +17,7 @@ struct Object; -namespace blender { -namespace deg { +namespace blender::deg { struct Depsgraph; @@ -46,5 +45,4 @@ class ObjectRuntimeBackup { Map pose_channel_runtime_data; }; -} // namespace deg -} // namespace blender +} // namespace blender::deg diff --git a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_pose.h b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_pose.h index 8fd5de44001..2f6a3dd4371 100644 --- a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_pose.h +++ b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_pose.h @@ -11,8 +11,6 @@ #include "DNA_action_types.h" -namespace blender { -namespace deg { +namespace blender::deg { -} -} // namespace blender +} // namespace blender::deg diff --git a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_scene.h b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_scene.h index 17683966a22..155cb42a96d 100644 --- a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_scene.h +++ b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_scene.h @@ -11,8 +11,7 @@ struct Scene; -namespace blender { -namespace deg { +namespace blender::deg { struct Depsgraph; @@ -40,5 +39,4 @@ class SceneBackup { SequencerBackup sequencer_backup; }; -} // namespace deg -} // namespace blender +} // namespace blender::deg diff --git a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_sequence.h b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_sequence.h index 95fbd2a3e4e..f97b6b200e9 100644 --- a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_sequence.h +++ b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_sequence.h @@ -11,8 +11,7 @@ struct Sequence; -namespace blender { -namespace deg { +namespace blender::deg { struct Depsgraph; @@ -32,5 +31,4 @@ class SequenceBackup { ListBase anims; }; -} // namespace deg -} // namespace blender +} // namespace blender::deg diff --git a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_sequencer.h b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_sequencer.h index 19e1a63ab4c..38fb8e81cc3 100644 --- a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_sequencer.h +++ b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_sequencer.h @@ -16,8 +16,7 @@ struct Scene; -namespace blender { -namespace deg { +namespace blender::deg { struct Depsgraph; @@ -34,5 +33,4 @@ class SequencerBackup { Map sequences_backup; }; -} // namespace deg -} // namespace blender +} // namespace blender::deg diff --git a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_sound.h b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_sound.h index c4267be1421..9e1d15251e8 100644 --- a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_sound.h +++ b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_sound.h @@ -9,8 +9,7 @@ struct bSound; -namespace blender { -namespace deg { +namespace blender::deg { struct Depsgraph; @@ -29,5 +28,4 @@ class SoundBackup { void *playback_handle; }; -} // namespace deg -} // namespace blender +} // namespace blender::deg diff --git a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_volume.h b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_volume.h index bc263cc58f8..60cba64f120 100644 --- a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_volume.h +++ b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_volume.h @@ -10,8 +10,7 @@ struct Volume; struct VolumeGridVector; -namespace blender { -namespace deg { +namespace blender::deg { struct Depsgraph; @@ -27,5 +26,4 @@ class VolumeBackup { char filepath[1024]; /* FILE_MAX */ }; -} // namespace deg -} // namespace blender +} // namespace blender::deg diff --git a/source/blender/depsgraph/intern/eval/deg_eval_stats.h b/source/blender/depsgraph/intern/eval/deg_eval_stats.h index a1e3cdaca76..c24c5f07135 100644 --- a/source/blender/depsgraph/intern/eval/deg_eval_stats.h +++ b/source/blender/depsgraph/intern/eval/deg_eval_stats.h @@ -7,13 +7,11 @@ #pragma once -namespace blender { -namespace deg { +namespace blender::deg { struct Depsgraph; /* Aggregate operation timings to overall component and ID nodes timing. */ void deg_eval_stats_aggregate(Depsgraph *graph); -} // namespace deg -} // namespace blender +} // namespace blender::deg diff --git a/source/blender/depsgraph/intern/node/deg_node.h b/source/blender/depsgraph/intern/node/deg_node.h index 509867ad47a..5b33528df33 100644 --- a/source/blender/depsgraph/intern/node/deg_node.h +++ b/source/blender/depsgraph/intern/node/deg_node.h @@ -18,8 +18,7 @@ struct ID; struct Scene; -namespace blender { -namespace deg { +namespace blender::deg { struct Depsgraph; struct OperationNode; @@ -217,5 +216,4 @@ struct Node { void deg_register_base_depsnodes(); -} // namespace deg -} // namespace blender +} // namespace blender::deg diff --git a/source/blender/depsgraph/intern/node/deg_node_component.h b/source/blender/depsgraph/intern/node/deg_node_component.h index fd6af43f40e..375a26ab49b 100644 --- a/source/blender/depsgraph/intern/node/deg_node_component.h +++ b/source/blender/depsgraph/intern/node/deg_node_component.h @@ -22,8 +22,7 @@ struct ID; struct bPoseChannel; -namespace blender { -namespace deg { +namespace blender::deg { struct BoneComponentNode; struct Depsgraph; @@ -249,5 +248,4 @@ struct ParametersComponentNode : public ComponentNode { void deg_register_component_depsnodes(); -} // namespace deg -} // namespace blender +} // namespace blender::deg diff --git a/source/blender/depsgraph/intern/node/deg_node_factory.h b/source/blender/depsgraph/intern/node/deg_node_factory.h index 6caf99ac9ba..ec5029e8352 100644 --- a/source/blender/depsgraph/intern/node/deg_node_factory.h +++ b/source/blender/depsgraph/intern/node/deg_node_factory.h @@ -14,9 +14,7 @@ struct ID; -namespace blender { -namespace deg { - +namespace blender::deg { struct DepsNodeFactory { virtual NodeType type() const = 0; virtual const char *type_name() const = 0; @@ -41,7 +39,6 @@ void register_node_typeinfo(DepsNodeFactory *factory); /* Get typeinfo for specified type */ DepsNodeFactory *type_get_factory(NodeType type); -} // namespace deg -} // namespace blender +} // namespace blender::deg #include "intern/node/deg_node_factory_impl.h" diff --git a/source/blender/depsgraph/intern/node/deg_node_factory_impl.h b/source/blender/depsgraph/intern/node/deg_node_factory_impl.h index 76a91860cc1..d9d0a1c1e3e 100644 --- a/source/blender/depsgraph/intern/node/deg_node_factory_impl.h +++ b/source/blender/depsgraph/intern/node/deg_node_factory_impl.h @@ -11,8 +11,7 @@ struct ID; -namespace blender { -namespace deg { +namespace blender::deg { template NodeType DepsNodeFactoryImpl::type() const { @@ -48,5 +47,4 @@ Node *DepsNodeFactoryImpl::create_node(const ID *id, return node; } -} // namespace deg -} // namespace blender +} // namespace blender::deg diff --git a/source/blender/depsgraph/intern/node/deg_node_id.h b/source/blender/depsgraph/intern/node/deg_node_id.h index a8b6294b482..406ca828049 100644 --- a/source/blender/depsgraph/intern/node/deg_node_id.h +++ b/source/blender/depsgraph/intern/node/deg_node_id.h @@ -12,8 +12,7 @@ #include "DNA_ID.h" #include "intern/node/deg_node.h" -namespace blender { -namespace deg { +namespace blender::deg { struct ComponentNode; @@ -117,5 +116,4 @@ struct IDNode : public Node { DEG_DEPSNODE_DECLARE; }; -} // namespace deg -} // namespace blender +} // namespace blender::deg diff --git a/source/blender/depsgraph/intern/node/deg_node_operation.h b/source/blender/depsgraph/intern/node/deg_node_operation.h index 4808e004dc2..fd772fbce9d 100644 --- a/source/blender/depsgraph/intern/node/deg_node_operation.h +++ b/source/blender/depsgraph/intern/node/deg_node_operation.h @@ -13,8 +13,7 @@ struct Depsgraph; -namespace blender { -namespace deg { +namespace blender::deg { struct ComponentNode; @@ -272,5 +271,4 @@ struct OperationNode : public Node { void deg_register_operation_depsnodes(); -} // namespace deg -} // namespace blender +} // namespace blender::deg diff --git a/source/blender/depsgraph/intern/node/deg_node_time.h b/source/blender/depsgraph/intern/node/deg_node_time.h index fcfe9a0fa80..3946b63384d 100644 --- a/source/blender/depsgraph/intern/node/deg_node_time.h +++ b/source/blender/depsgraph/intern/node/deg_node_time.h @@ -9,8 +9,7 @@ #include "intern/node/deg_node.h" -namespace blender { -namespace deg { +namespace blender::deg { /* Time Source Node. */ struct TimeSourceNode : public Node { @@ -25,5 +24,4 @@ struct TimeSourceNode : public Node { DEG_DEPSNODE_DECLARE; }; -} // namespace deg -} // namespace blender +} // namespace blender::deg -- cgit v1.2.3 From 44f1495b570037f06c698b0fb0a7cb21fdf0ac97 Mon Sep 17 00:00:00 2001 From: Ethan-Hall Date: Tue, 19 Jul 2022 15:20:01 +0200 Subject: EEVEE: use mipmaps of compressed textures (DDS) Currently Blender generates mipmaps that override the existing ones. This patch disables generating new mipmaps for compressed textures. Reviewed By: fclem Differential Revision: https://developer.blender.org/D14459 --- source/blender/gpu/opengl/gl_texture.cc | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/source/blender/gpu/opengl/gl_texture.cc b/source/blender/gpu/opengl/gl_texture.cc index 055c8d104e2..cfb3184c4a5 100644 --- a/source/blender/gpu/opengl/gl_texture.cc +++ b/source/blender/gpu/opengl/gl_texture.cc @@ -310,6 +310,12 @@ void GLTexture::update_sub( */ void GLTexture::generate_mipmap() { + /* Allow users to provide mipmaps stored in compressed textures. + * Skip generating mipmaps to avoid overriding the existing ones. */ + if (format_flag_ & GPU_FORMAT_COMPRESSED) { + return; + } + /* Some drivers have bugs when using #glGenerateMipmap with depth textures (see T56789). * In this case we just create a complete texture with mipmaps manually without * down-sampling. You must initialize the texture levels using other methods like -- cgit v1.2.3 From bc6b612d8b673a6dcfce110466cd8e924103fb1d Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Tue, 19 Jul 2022 15:23:44 +0200 Subject: Depsgraph: Make variable naming more clear Disambiguate from nodes visibility flags. --- source/blender/depsgraph/intern/depsgraph.cc | 4 ++-- source/blender/depsgraph/intern/depsgraph.h | 4 ++-- source/blender/depsgraph/intern/depsgraph_tag.cc | 12 ++++++------ 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/source/blender/depsgraph/intern/depsgraph.cc b/source/blender/depsgraph/intern/depsgraph.cc index 4514084059a..787b4210cff 100644 --- a/source/blender/depsgraph/intern/depsgraph.cc +++ b/source/blender/depsgraph/intern/depsgraph.cc @@ -46,8 +46,8 @@ namespace blender::deg { Depsgraph::Depsgraph(Main *bmain, Scene *scene, ViewLayer *view_layer, eEvaluationMode mode) : time_source(nullptr), need_update(true), - need_visibility_update(true), - need_visibility_time_update(false), + need_tag_id_on_graph_visibility_update(true), + need_tag_id_on_graph_visibility_time_update(false), bmain(bmain), scene(scene), view_layer(view_layer), diff --git a/source/blender/depsgraph/intern/depsgraph.h b/source/blender/depsgraph/intern/depsgraph.h index fc92580f5f5..fc24e98acb7 100644 --- a/source/blender/depsgraph/intern/depsgraph.h +++ b/source/blender/depsgraph/intern/depsgraph.h @@ -93,8 +93,8 @@ struct Depsgraph { /* Indicated whether IDs in this graph are to be tagged as if they first appear visible, with * an optional tag for their animation (time) update. */ - bool need_visibility_update; - bool need_visibility_time_update; + bool need_tag_id_on_graph_visibility_update; + bool need_tag_id_on_graph_visibility_time_update; /* Indicates which ID types were updated. */ char id_type_updated[INDEX_ID_MAX]; diff --git a/source/blender/depsgraph/intern/depsgraph_tag.cc b/source/blender/depsgraph/intern/depsgraph_tag.cc index b50081458ad..9cd5980d8fe 100644 --- a/source/blender/depsgraph/intern/depsgraph_tag.cc +++ b/source/blender/depsgraph/intern/depsgraph_tag.cc @@ -494,19 +494,19 @@ void deg_graph_node_tag_zero(Main *bmain, void graph_tag_on_visible_update(Depsgraph *graph, const bool do_time) { - graph->need_visibility_update = true; - graph->need_visibility_time_update |= do_time; + graph->need_tag_id_on_graph_visibility_update = true; + graph->need_tag_id_on_graph_visibility_time_update |= do_time; } } /* namespace */ void graph_tag_ids_for_visible_update(Depsgraph *graph) { - if (!graph->need_visibility_update) { + if (!graph->need_tag_id_on_graph_visibility_update) { return; } - const bool do_time = graph->need_visibility_time_update; + const bool do_time = graph->need_tag_id_on_graph_visibility_time_update; Main *bmain = graph->bmain; /* NOTE: It is possible to have this function called with `do_time=false` first and later (prior @@ -561,8 +561,8 @@ void graph_tag_ids_for_visible_update(Depsgraph *graph) id_node->previously_visible_components_mask = id_node->visible_components_mask; } - graph->need_visibility_update = false; - graph->need_visibility_time_update = false; + graph->need_tag_id_on_graph_visibility_update = false; + graph->need_tag_id_on_graph_visibility_time_update = false; } NodeType geometry_tag_to_component(const ID *id) -- cgit v1.2.3 From 95fd7c3679566e87ceff7a6df8b24a8da33534ab Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Tue, 19 Jul 2022 15:27:20 +0200 Subject: Depsgraph: Cleanup, Make variable less ambiguous and more clear --- source/blender/depsgraph/intern/builder/pipeline.cc | 2 +- source/blender/depsgraph/intern/depsgraph.cc | 2 +- source/blender/depsgraph/intern/depsgraph.h | 2 +- source/blender/depsgraph/intern/depsgraph_build.cc | 4 ++-- source/blender/depsgraph/intern/depsgraph_query.cc | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/source/blender/depsgraph/intern/builder/pipeline.cc b/source/blender/depsgraph/intern/builder/pipeline.cc index 540b8e173f1..815a06d03d1 100644 --- a/source/blender/depsgraph/intern/builder/pipeline.cc +++ b/source/blender/depsgraph/intern/builder/pipeline.cc @@ -90,7 +90,7 @@ void AbstractBuilderPipeline::build_step_finalize() } #endif /* Relations are up to date. */ - deg_graph_->need_update = false; + deg_graph_->need_update_relations = false; } unique_ptr AbstractBuilderPipeline::construct_node_builder() diff --git a/source/blender/depsgraph/intern/depsgraph.cc b/source/blender/depsgraph/intern/depsgraph.cc index 787b4210cff..d460a68747d 100644 --- a/source/blender/depsgraph/intern/depsgraph.cc +++ b/source/blender/depsgraph/intern/depsgraph.cc @@ -45,7 +45,7 @@ namespace blender::deg { Depsgraph::Depsgraph(Main *bmain, Scene *scene, ViewLayer *view_layer, eEvaluationMode mode) : time_source(nullptr), - need_update(true), + need_update_relations(true), need_tag_id_on_graph_visibility_update(true), need_tag_id_on_graph_visibility_time_update(false), bmain(bmain), diff --git a/source/blender/depsgraph/intern/depsgraph.h b/source/blender/depsgraph/intern/depsgraph.h index fc24e98acb7..33d97e4b8b2 100644 --- a/source/blender/depsgraph/intern/depsgraph.h +++ b/source/blender/depsgraph/intern/depsgraph.h @@ -89,7 +89,7 @@ struct Depsgraph { TimeSourceNode *time_source; /* Indicates whether relations needs to be updated. */ - bool need_update; + bool need_update_relations; /* Indicated whether IDs in this graph are to be tagged as if they first appear visible, with * an optional tag for their animation (time) update. */ diff --git a/source/blender/depsgraph/intern/depsgraph_build.cc b/source/blender/depsgraph/intern/depsgraph_build.cc index c64b7bc1eb7..a207c13d646 100644 --- a/source/blender/depsgraph/intern/depsgraph_build.cc +++ b/source/blender/depsgraph/intern/depsgraph_build.cc @@ -270,7 +270,7 @@ void DEG_graph_tag_relations_update(Depsgraph *graph) { DEG_DEBUG_PRINTF(graph, TAG, "%s: Tagging relations for update.\n", __func__); deg::Depsgraph *deg_graph = reinterpret_cast(graph); - deg_graph->need_update = true; + deg_graph->need_update_relations = true; /* NOTE: When relations are updated, it's quite possible that * we've got new bases in the scene. This means, we need to * re-create flat array of bases in view layer. @@ -286,7 +286,7 @@ void DEG_graph_tag_relations_update(Depsgraph *graph) void DEG_graph_relations_update(Depsgraph *graph) { deg::Depsgraph *deg_graph = (deg::Depsgraph *)graph; - if (!deg_graph->need_update) { + if (!deg_graph->need_update_relations) { /* Graph is up to date, nothing to do. */ return; } diff --git a/source/blender/depsgraph/intern/depsgraph_query.cc b/source/blender/depsgraph/intern/depsgraph_query.cc index 6ffc711a475..9a047c70d01 100644 --- a/source/blender/depsgraph/intern/depsgraph_query.cc +++ b/source/blender/depsgraph/intern/depsgraph_query.cc @@ -328,7 +328,7 @@ bool DEG_is_fully_evaluated(const struct Depsgraph *depsgraph) { const deg::Depsgraph *deg_graph = (const deg::Depsgraph *)depsgraph; /* Check whether relations are up to date. */ - if (deg_graph->need_update) { + if (deg_graph->need_update_relations) { return false; } /* Check whether IDs are up to date. */ -- cgit v1.2.3 From 2232855b50e90ef49d07df8ee4e3e0d0efb2cc4c Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Tue, 19 Jul 2022 15:49:29 +0200 Subject: Curves: align surface and curves object in Empty Hair operator Without this, symmetry does not work by default when the surface object was not at the same location as the 3d cursor. --- source/blender/editors/object/object_add.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/source/blender/editors/object/object_add.cc b/source/blender/editors/object/object_add.cc index 35d23edfbf0..a8e11afba65 100644 --- a/source/blender/editors/object/object_add.cc +++ b/source/blender/editors/object/object_add.cc @@ -2071,16 +2071,16 @@ static int object_curves_empty_hair_add_exec(bContext *C, wmOperator *op) Scene *scene = CTX_data_scene(C); ushort local_view_bits; - blender::float3 loc, rot; if (!ED_object_add_generic_get_opts( - C, op, 'Z', loc, rot, nullptr, nullptr, &local_view_bits, nullptr)) { + C, op, 'Z', nullptr, nullptr, nullptr, nullptr, &local_view_bits, nullptr)) { return OPERATOR_CANCELLED; } Object *surface_ob = CTX_data_active_object(C); BLI_assert(surface_ob != nullptr); - Object *curves_ob = ED_object_add_type(C, OB_CURVES, nullptr, loc, rot, false, local_view_bits); + Object *curves_ob = ED_object_add_type(C, OB_CURVES, nullptr, nullptr, nullptr, false, local_view_bits); + BKE_object_apply_mat4(curves_ob, surface_ob->obmat, false, false); /* Set surface object. */ Curves *curves_id = static_cast(curves_ob->data); -- cgit v1.2.3 From 4812eda3c5d14c672e91ef11182e1a875c070b10 Mon Sep 17 00:00:00 2001 From: Colin Basnett Date: Tue, 19 Jul 2022 15:57:31 +0200 Subject: Animation RNA: Add `clear()` method to FCurveKeyframePoints Add `FCurveKeyframePoints.clear()` method to delete all keyframe points from an FCurve. Reviewed By: sybren Differential Revision: https://developer.blender.org/D15283 --- source/blender/makesrna/intern/rna_fcurve.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/source/blender/makesrna/intern/rna_fcurve.c b/source/blender/makesrna/intern/rna_fcurve.c index 461536ffb8a..727d329781d 100644 --- a/source/blender/makesrna/intern/rna_fcurve.c +++ b/source/blender/makesrna/intern/rna_fcurve.c @@ -1031,6 +1031,13 @@ static void rna_FKeyframe_points_remove( rna_tag_animation_update(bmain, id); } +static void rna_FKeyframe_points_clear(ID *id, FCurve *fcu, Main *bmain) +{ + BKE_fcurve_delete_keys_all(fcu); + + rna_tag_animation_update(bmain, id); +} + static FCM_EnvelopeData *rna_FModifierEnvelope_points_add( ID *id, FModifier *fmod, Main *bmain, ReportList *reports, float frame) { @@ -2310,6 +2317,10 @@ static void rna_def_fcurve_keyframe_points(BlenderRNA *brna, PropertyRNA *cprop) /* optional */ RNA_def_boolean( func, "fast", 0, "Fast", "Fast keyframe removal to avoid recalculating the curve each time"); + + func = RNA_def_function(srna, "clear", "rna_FKeyframe_points_clear"); + RNA_def_function_ui_description(func, "Remove all keyframes from an F-Curve"); + RNA_def_function_flag(func, FUNC_USE_SELF_ID | FUNC_USE_MAIN); } static void rna_def_fcurve(BlenderRNA *brna) -- cgit v1.2.3 From 2f834bfc14824c224f99ab7d9a9e561fa86aef6b Mon Sep 17 00:00:00 2001 From: Colin Basnett Date: Tue, 19 Jul 2022 16:06:00 +0200 Subject: Fix T97559: Undoing of NLA strip duplication requires two undo steps Fix the issue where undoing a "duplicate NLA strip" operation would require two undo steps. The cause of this was that the operator was not using the operator macro system to combine both the duplication and the translate operators into one. Instead, the old code was simply manually invoking invoking the translate operator after the duplicate operator had completed. This patch requires the default keymap to be modified to include the two new macro operators, `NLA_OT_duplicate_move` and `NLA_OT_duplicate_linked_move` in favour of the old keymap that simply called `NLA_OT_duplicate` and passed along a `linked` argument. `duplicate_move` and `duplicate_move_linked` are two different enough operations to justify having their own operators from user's point-of-view, especially since we cannot yet have different tool-tips based on an operator's settings. Reviewed By: sybren, mont29 Differential Revision: https://developer.blender.org/D15086 --- .../keyconfig/keymap_data/blender_default.py | 6 ++---- source/blender/editors/include/ED_anim_api.h | 2 ++ source/blender/editors/space_api/spacetypes.c | 1 + source/blender/editors/space_nla/nla_edit.c | 8 +------- source/blender/editors/space_nla/nla_ops.c | 24 ++++++++++++++++++++++ 5 files changed, 30 insertions(+), 11 deletions(-) diff --git a/release/scripts/presets/keyconfig/keymap_data/blender_default.py b/release/scripts/presets/keyconfig/keymap_data/blender_default.py index e1e88a0e48d..d08efc6909b 100644 --- a/release/scripts/presets/keyconfig/keymap_data/blender_default.py +++ b/release/scripts/presets/keyconfig/keymap_data/blender_default.py @@ -2578,10 +2578,8 @@ def km_nla_editor(params): ("nla.soundclip_add", {"type": 'K', "value": 'PRESS', "shift": True}, None), ("nla.meta_add", {"type": 'G', "value": 'PRESS', "ctrl": True}, None), ("nla.meta_remove", {"type": 'G', "value": 'PRESS', "ctrl": True, "alt": True}, None), - ("nla.duplicate", {"type": 'D', "value": 'PRESS', "shift": True}, - {"properties": [("linked", False)]}), - ("nla.duplicate", {"type": 'D', "value": 'PRESS', "alt": True}, - {"properties": [("linked", True)]}), + ("nla.duplicate_move", {"type": 'D', "value": 'PRESS', "shift": True}, None), + ("nla.duplicate_linked_move", {"type": 'D', "value": 'PRESS', "alt": True}, None), ("nla.make_single_user", {"type": 'U', "value": 'PRESS'}, None), ("nla.delete", {"type": 'X', "value": 'PRESS'}, None), ("nla.delete", {"type": 'DEL', "value": 'PRESS'}, None), diff --git a/source/blender/editors/include/ED_anim_api.h b/source/blender/editors/include/ED_anim_api.h index ac3b4133007..cc3c68abc55 100644 --- a/source/blender/editors/include/ED_anim_api.h +++ b/source/blender/editors/include/ED_anim_api.h @@ -1046,6 +1046,8 @@ void ED_keymap_anim(struct wmKeyConfig *keyconf); void ED_operatormacros_graph(void); /* space_action */ void ED_operatormacros_action(void); +/* space_nla*/ +void ED_operatormacros_nla(void); /** \} */ diff --git a/source/blender/editors/space_api/spacetypes.c b/source/blender/editors/space_api/spacetypes.c index d53fe2efb03..3d964a95bc0 100644 --- a/source/blender/editors/space_api/spacetypes.c +++ b/source/blender/editors/space_api/spacetypes.c @@ -163,6 +163,7 @@ void ED_spacemacros_init(void) ED_operatormacros_sequencer(); ED_operatormacros_paint(); ED_operatormacros_gpencil(); + ED_operatormacros_nla(); /* Register dropboxes (can use macros). */ ED_dropboxes_ui(); diff --git a/source/blender/editors/space_nla/nla_edit.c b/source/blender/editors/space_nla/nla_edit.c index d1a667c6e4e..801d032a861 100644 --- a/source/blender/editors/space_nla/nla_edit.c +++ b/source/blender/editors/space_nla/nla_edit.c @@ -1216,13 +1216,10 @@ static int nlaedit_duplicate_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } -static int nlaedit_duplicate_invoke(bContext *C, wmOperator *op, const wmEvent *event) +static int nlaedit_duplicate_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) { nlaedit_duplicate_exec(C, op); - RNA_enum_set(op->ptr, "mode", TFM_TRANSLATION); - WM_operator_name_call(C, "TRANSFORM_OT_transform", WM_OP_INVOKE_REGION_WIN, op->ptr, event); - return OPERATOR_FINISHED; } @@ -1248,9 +1245,6 @@ void NLA_OT_duplicate(wmOperatorType *ot) false, "Linked", "When duplicating strips, assign new copies of the actions they use"); - - /* to give to transform */ - RNA_def_enum(ot->srna, "mode", rna_enum_transform_mode_types, TFM_TRANSLATION, "Mode", ""); } /** \} */ diff --git a/source/blender/editors/space_nla/nla_ops.c b/source/blender/editors/space_nla/nla_ops.c index 902e7a176a3..3ae73282230 100644 --- a/source/blender/editors/space_nla/nla_ops.c +++ b/source/blender/editors/space_nla/nla_ops.c @@ -16,6 +16,8 @@ #include "ED_anim_api.h" #include "ED_screen.h" +#include "RNA_access.h" + #include "WM_api.h" #include "WM_types.h" @@ -138,6 +140,28 @@ void nla_operatortypes(void) WM_operatortype_append(NLA_OT_fmodifier_paste); } +void ED_operatormacros_nla() +{ + wmOperatorType *ot; + wmOperatorTypeMacro *otmacro; + + ot = WM_operatortype_append_macro("NLA_OT_duplicate_move", + "Duplicate", + "Duplicate selected strips and their Actions and move them", + OPTYPE_UNDO | OPTYPE_REGISTER); + otmacro = WM_operatortype_macro_define(ot, "NLA_OT_duplicate"); + RNA_boolean_set(otmacro->ptr, "linked", false); + WM_operatortype_macro_define(ot, "TRANSFORM_OT_translate"); + + ot = WM_operatortype_append_macro("NLA_OT_duplicate_linked_move", + "Duplicate Linked", + "Duplicate selected strips and move them", + OPTYPE_UNDO | OPTYPE_REGISTER); + otmacro = WM_operatortype_macro_define(ot, "NLA_OT_duplicate"); + RNA_boolean_set(otmacro->ptr, "linked", true); + WM_operatortype_macro_define(ot, "TRANSFORM_OT_translate"); +} + /* ************************** registration - keymaps **********************************/ void nla_keymap(wmKeyConfig *keyconf) -- cgit v1.2.3 From 348ec37f52452614cb26baa8be40a161e1446b15 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Mon, 18 Jul 2022 16:51:57 +0200 Subject: UI: Add AbstractViewItem base class No user visible changes expected. Similar to rBc355be6faeac, but for view items now instead of the view. Not much of the item code is ported to use it yet, it's actually a bit tricky for the most part. But just introducing the base class already allows me to start unifying the view item buttons (`uiButTreeRow` and `uiButGridTile`). This would be a nice improvement. --- source/blender/editors/include/UI_abstract_view.hh | 27 ++++++++++++++++++++++ source/blender/editors/include/UI_grid_view.hh | 11 +-------- source/blender/editors/include/UI_tree_view.hh | 11 +++------ source/blender/editors/interface/CMakeLists.txt | 1 + .../editors/interface/abstract_view_item.cc | 22 ++++++++++++++++++ source/blender/editors/interface/grid_view.cc | 5 ---- source/blender/editors/interface/tree_view.cc | 10 ++++---- 7 files changed, 60 insertions(+), 27 deletions(-) create mode 100644 source/blender/editors/interface/abstract_view_item.cc diff --git a/source/blender/editors/include/UI_abstract_view.hh b/source/blender/editors/include/UI_abstract_view.hh index 82f81f1702b..fdb7069e7c4 100644 --- a/source/blender/editors/include/UI_abstract_view.hh +++ b/source/blender/editors/include/UI_abstract_view.hh @@ -15,12 +15,17 @@ #include #include +#include "DNA_defs.h" + #include "BLI_span.hh" struct wmNotifier; +struct uiBlock; namespace blender::ui { +class AbstractViewItem; + class AbstractView { bool is_reconstructed_ = false; /** @@ -66,4 +71,26 @@ class AbstractView { bool is_reconstructed() const; }; +class AbstractViewItem { + protected: + bool is_active_ = false; + + public: + virtual ~AbstractViewItem() = default; + + protected: + AbstractViewItem() = default; + + /** + * Copy persistent state (e.g. active, selection, etc.) from a matching item of + * the last redraw to this item. If sub-classes introduce more advanced state they should + * override this and make it update their state accordingly. + * + * \note Always call the base class implementation when overriding this! + */ + virtual void update_from_old(const AbstractViewItem &old); + + void set_view(AbstractView &view); +}; + } // namespace blender::ui diff --git a/source/blender/editors/include/UI_grid_view.hh b/source/blender/editors/include/UI_grid_view.hh index cabc49e411c..74ce16ccd85 100644 --- a/source/blender/editors/include/UI_grid_view.hh +++ b/source/blender/editors/include/UI_grid_view.hh @@ -32,14 +32,12 @@ class AbstractGridView; /** \name Grid-View Item Type * \{ */ -class AbstractGridViewItem { +class AbstractGridViewItem : public AbstractViewItem { friend class AbstractGridView; friend class GridViewLayoutBuilder; const AbstractGridView *view_; - bool is_active_ = false; - protected: /** Reference to a string that uniquely identifies this item in the view. */ StringRef identifier_{}; @@ -77,13 +75,6 @@ class AbstractGridViewItem { */ virtual std::optional should_be_active() const; - /** - * Copy persistent state (e.g. active, selection, etc.) from a matching item of - * the last redraw to this item. If sub-classes introduce more advanced state they should - * override this and make it update their state accordingly. - */ - virtual void update_from_old(const AbstractGridViewItem &old); - /** * Activates this item, deactivates other items, and calls the * #AbstractGridViewItem::on_activate() function. diff --git a/source/blender/editors/include/UI_tree_view.hh b/source/blender/editors/include/UI_tree_view.hh index 9527df590b7..fa427d96dd8 100644 --- a/source/blender/editors/include/UI_tree_view.hh +++ b/source/blender/editors/include/UI_tree_view.hh @@ -153,7 +153,7 @@ class AbstractTreeView : public AbstractView, public TreeViewItemContainer { * It also stores state information that needs to be persistent over redraws, like the collapsed * state. */ -class AbstractTreeViewItem : public TreeViewItemContainer { +class AbstractTreeViewItem : public AbstractViewItem, public TreeViewItemContainer { friend class AbstractTreeView; friend class TreeViewLayoutBuilder; /* Higher-level API. */ @@ -161,7 +161,6 @@ class AbstractTreeViewItem : public TreeViewItemContainer { private: bool is_open_ = false; - bool is_active_ = false; bool is_renaming_ = false; protected: @@ -222,12 +221,8 @@ class AbstractTreeViewItem : public TreeViewItemContainer { */ virtual bool supports_collapsing() const; - /** - * Copy persistent state (e.g. is-collapsed flag, selection, etc.) from a matching item of - * the last redraw to this item. If sub-classes introduce more advanced state they should - * override this and make it update their state accordingly. - */ - virtual void update_from_old(const AbstractTreeViewItem &old); + /** See #AbstractViewItem::update_from_old(). */ + virtual void update_from_old(const AbstractViewItem &old) override; /** * Compare this item to \a other to check if they represent the same data. diff --git a/source/blender/editors/interface/CMakeLists.txt b/source/blender/editors/interface/CMakeLists.txt index 1bdec57ac59..c6c9f1f80c8 100644 --- a/source/blender/editors/interface/CMakeLists.txt +++ b/source/blender/editors/interface/CMakeLists.txt @@ -26,6 +26,7 @@ set(INC set(SRC abstract_view.cc + abstract_view_item.cc grid_view.cc interface.cc interface_align.c diff --git a/source/blender/editors/interface/abstract_view_item.cc b/source/blender/editors/interface/abstract_view_item.cc new file mode 100644 index 00000000000..fc71dbe8b95 --- /dev/null +++ b/source/blender/editors/interface/abstract_view_item.cc @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup edinterface + */ + +#include "UI_abstract_view.hh" + +namespace blender::ui { + +/* ---------------------------------------------------------------------- */ +/** \name View Reconstruction + * \{ */ + +void AbstractViewItem::update_from_old(const AbstractViewItem &old) +{ + is_active_ = old.is_active_; +} + +/** \} */ + +} // namespace blender::ui diff --git a/source/blender/editors/interface/grid_view.cc b/source/blender/editors/interface/grid_view.cc index 19a2326fba1..362642b0846 100644 --- a/source/blender/editors/interface/grid_view.cc +++ b/source/blender/editors/interface/grid_view.cc @@ -158,11 +158,6 @@ void AbstractGridViewItem::change_state_delayed() } } -void AbstractGridViewItem::update_from_old(const AbstractGridViewItem &old) -{ - is_active_ = old.is_active_; -} - void AbstractGridViewItem::activate() { BLI_assert_msg(get_view().is_reconstructed(), diff --git a/source/blender/editors/interface/tree_view.cc b/source/blender/editors/interface/tree_view.cc index d29cf367e59..ec1140e8efb 100644 --- a/source/blender/editors/interface/tree_view.cc +++ b/source/blender/editors/interface/tree_view.cc @@ -339,11 +339,13 @@ void AbstractTreeViewItem::build_context_menu(bContext & /*C*/, uiLayout & /*col /* No context menu by default. */ } -void AbstractTreeViewItem::update_from_old(const AbstractTreeViewItem &old) +void AbstractTreeViewItem::update_from_old(const AbstractViewItem &old) { - is_open_ = old.is_open_; - is_active_ = old.is_active_; - is_renaming_ = old.is_renaming_; + AbstractViewItem::update_from_old(old); + + const AbstractTreeViewItem &old_tree_item = dynamic_cast(old); + is_open_ = old_tree_item.is_open_; + is_renaming_ = old_tree_item.is_renaming_; } bool AbstractTreeViewItem::matches(const AbstractTreeViewItem &other) const -- cgit v1.2.3 From 5bee991132ea8aa7fea827df5b5153f3211a3431 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Tue, 19 Jul 2022 16:14:42 +0200 Subject: UI: Port view item features to base class, merge view item button types No user visible changes expected. Merges the tree row and grid tile button types, which were mostly doing the same things. The idea is that there is a button type for highlighting, as well as supporting general view item features (e.g. renaming, drag/drop, etc.). So instead there is a view item button type now. Also ports view item features like renaming, custom context menus, drag controllers and drop controllers to `ui::AbstractViewItem` (the new base class for all view items). This should be quite an improvement because: - Merges code that was duplicated over view items. - Mentioned features (renaming, drag & drop, ...) are much easier to implement in new view types now. Most of it comes "for free". - Further features will immediately become availalbe to all views (e.g. selection). - Simplifies APIs, there don't have to be functions for individual view item types anymore. - View item classes are split and thus less overwhelming visually. - View item buttons now share all code (drawing, handling, etc.) - We're soon running out of available button types, this commit merges two into one. I was hoping I could do this in multiple smaller commits, but things were quite intertwined so that would've taken quite some effort. --- source/blender/editors/include/UI_abstract_view.hh | 194 ++++++++++- source/blender/editors/include/UI_grid_view.hh | 22 +- source/blender/editors/include/UI_interface.h | 73 ++--- source/blender/editors/include/UI_tree_view.hh | 140 +------- source/blender/editors/interface/abstract_view.cc | 7 + .../editors/interface/abstract_view_item.cc | 351 ++++++++++++++++++++ source/blender/editors/interface/grid_view.cc | 45 +-- source/blender/editors/interface/interface.cc | 72 +---- .../editors/interface/interface_context_menu.c | 10 +- .../editors/interface/interface_dropboxes.cc | 32 +- .../blender/editors/interface/interface_handlers.c | 88 ++--- .../blender/editors/interface/interface_intern.h | 25 +- source/blender/editors/interface/interface_ops.c | 64 ++-- .../blender/editors/interface/interface_query.cc | 32 +- source/blender/editors/interface/interface_view.cc | 43 ++- .../blender/editors/interface/interface_widgets.c | 57 +--- source/blender/editors/interface/tree_view.cc | 354 ++------------------- .../editors/space_file/asset_catalog_tree_view.cc | 58 ++-- .../space_spreadsheet/spreadsheet_dataset_draw.cc | 2 +- 19 files changed, 807 insertions(+), 862 deletions(-) diff --git a/source/blender/editors/include/UI_abstract_view.hh b/source/blender/editors/include/UI_abstract_view.hh index fdb7069e7c4..dfddace8899 100644 --- a/source/blender/editors/include/UI_abstract_view.hh +++ b/source/blender/editors/include/UI_abstract_view.hh @@ -3,11 +3,16 @@ /** \file * \ingroup editorui * - * Base for all views (UIs to display data sets), supporting common features. + * Base class for all views (UIs to display data sets) and view items, supporting common features. * https://wiki.blender.org/wiki/Source/Interface/Views * * One of the most important responsibilities of the base class is managing reconstruction, - * enabling state that is persistent over reconstructions/redraws. + * enabling state that is persistent over reconstructions/redraws. Other features: + * - Renaming + * - Custom context menus + * - Notifier listening + * - Drag controllers (dragging view items) + * - Drop controllers (dropping onto/into view items) */ #pragma once @@ -18,15 +23,25 @@ #include "DNA_defs.h" #include "BLI_span.hh" +#include "BLI_string_ref.hh" -struct wmNotifier; +struct bContext; struct uiBlock; +struct uiBut; +struct uiLayout; +struct uiViewItemHandle; +struct wmDrag; +struct wmNotifier; namespace blender::ui { class AbstractViewItem; +class AbstractViewItemDropController; +class AbstractViewItemDragController; class AbstractView { + friend class AbstractViewItem; + bool is_reconstructed_ = false; /** * Only one item can be renamed at a time. So rather than giving each item an own rename buffer @@ -43,6 +58,12 @@ class AbstractView { /** Listen to a notifier, returning true if a redraw is needed. */ virtual bool listen(const wmNotifier &) const; + /** + * Makes \a item valid for display in this view. Behavior is undefined for items not registered + * with this. + */ + void register_item(AbstractViewItem &item); + /** Only one item can be renamed at a time. */ bool is_renaming() const; /** \return If renaming was started successfully. */ @@ -63,7 +84,6 @@ class AbstractView { * After this, reconstruction is complete (see #is_reconstructed()). */ void update_from_old(uiBlock &new_block); - /** * Check if the view is fully (re-)constructed. That means, both the build function and * #update_from_old() have finished. @@ -72,15 +92,85 @@ class AbstractView { }; class AbstractViewItem { + friend class AbstractView; + friend class ViewItemAPIWrapper; + protected: + /** + * The view this item is a part of, and was registered for using #AbstractView::register_item(). + * If this wasn't done, the behavior of items is undefined. + */ + AbstractView *view_ = nullptr; bool is_active_ = false; + bool is_renaming_ = false; public: virtual ~AbstractViewItem() = default; + virtual void build_context_menu(bContext &C, uiLayout &column) const; + + /** + * Queries if the view item supports renaming in principle. Renaming may still fail, e.g. if + * another item is already being renamed. + */ + virtual bool supports_renaming() const; + /** + * Try renaming the item, or the data it represents. Can assume + * #AbstractViewItem::supports_renaming() returned true. Sub-classes that override this should + * usually call this, unless they have a custom #AbstractViewItem.matches() implementation. + * + * \return True if the renaming was successful. + */ + virtual bool rename(StringRefNull new_name); + /** + * Get the string that should be used for renaming, typically the item's label. This string will + * not be modified, but if the renaming is canceled, the value will be reset to this. + */ + virtual StringRef get_rename_string() const; + + /** + * If an item wants to support being dragged, it has to return a drag controller here. + * That is an object implementing #AbstractViewItemDragController. + */ + virtual std::unique_ptr create_drag_controller() const; + /** + * If an item wants to support dropping data into it, it has to return a drop controller here. + * That is an object implementing #AbstractViewItemDropController. + * + * \note This drop controller may be requested for each event. The view doesn't keep a drop + * controller around currently. So it can not contain persistent state. + */ + virtual std::unique_ptr create_drop_controller() const; + + /** Get the view this item is registered for using #AbstractView::register_item(). */ + AbstractView &get_view() const; + + /** + * Requires the view to have completed reconstruction, see #is_reconstructed(). Otherwise we + * can't be sure about the item state. + */ + bool is_active() const; + + bool is_renaming() const; + void begin_renaming(); + void end_renaming(); + void rename_apply(); + + template + static ToType *from_item_handle(uiViewItemHandle *handle); + protected: AbstractViewItem() = default; + /** + * Compare this item's identity to \a other to check if they represent the same data. + * Implementations can assume that the types match already (caller must check). + * + * Used to recognize an item from a previous redraw, to be able to keep its state (e.g. active, + * renaming, etc.). + */ + virtual bool matches(const AbstractViewItem &other) const = 0; + /** * Copy persistent state (e.g. active, selection, etc.) from a matching item of * the last redraw to this item. If sub-classes introduce more advanced state they should @@ -90,7 +180,101 @@ class AbstractViewItem { */ virtual void update_from_old(const AbstractViewItem &old); - void set_view(AbstractView &view); + /** + * Add a text button for renaming the item to \a block. This must be used for the built-in + * renaming to work. This button is meant to appear temporarily. It is removed when renaming is + * done. + */ + void add_rename_button(uiBlock &block); }; +template ToType *AbstractViewItem::from_item_handle(uiViewItemHandle *handle) +{ + static_assert(std::is_base_of::value, + "Type must derive from and implement the AbstractViewItem interface"); + + return dynamic_cast(reinterpret_cast(handle)); +} + +/* ---------------------------------------------------------------------- */ +/** \name Drag 'n Drop + * \{ */ + +/** + * Class to enable dragging a view item. An item can return a drop controller for itself by + * implementing #AbstractViewItem::create_drag_controller(). + */ +class AbstractViewItemDragController { + protected: + AbstractView &view_; + + public: + AbstractViewItemDragController(AbstractView &view); + virtual ~AbstractViewItemDragController() = default; + + virtual int get_drag_type() const = 0; + virtual void *create_drag_data() const = 0; + virtual void on_drag_start(); + + /** Request the view the item is registered for as type #ViewType. Throws a `std::bad_cast` + * exception if the view is not of the requested type. */ + template inline ViewType &get_view() const; +}; + +/** + * Class to define the behavior when dropping something onto/into a view item, plus the behavior + * when dragging over this item. An item can return a drop controller for itself via a custom + * implementation of #AbstractViewItem::create_drop_controller(). + */ +class AbstractViewItemDropController { + protected: + AbstractView &view_; + + public: + AbstractViewItemDropController(AbstractView &view); + virtual ~AbstractViewItemDropController() = default; + + /** + * Check if the data dragged with \a drag can be dropped on the item this controller is for. + * \param r_disabled_hint: Return a static string to display to the user, explaining why dropping + * isn't possible on this item. Shouldn't be done too aggressively, e.g. + * don't set this if the drag-type can't be dropped here; only if it can + * but there's another reason it can't be dropped. + * Can assume this is a non-null pointer. + */ + virtual bool can_drop(const wmDrag &drag, const char **r_disabled_hint) const = 0; + /** + * Custom text to display when dragging over a view item. Should explain what happens when + * dropping the data onto this item. Will only be used if #AbstractViewItem::can_drop() + * returns true, so the implementing override doesn't have to check that again. + * The returned value must be a translated string. + */ + virtual std::string drop_tooltip(const wmDrag &drag) const = 0; + /** + * Execute the logic to apply a drop of the data dragged with \a drag onto/into the item this + * controller is for. + */ + virtual bool on_drop(struct bContext *C, const wmDrag &drag) = 0; + + /** Request the view the item is registered for as type #ViewType. Throws a `std::bad_cast` + * exception if the view is not of the requested type. */ + template inline ViewType &get_view() const; +}; + +template ViewType &AbstractViewItemDragController::get_view() const +{ + static_assert(std::is_base_of::value, + "Type must derive from and implement the ui::AbstractView interface"); + return dynamic_cast(view_); +} + +template ViewType &AbstractViewItemDropController::get_view() const +{ + static_assert(std::is_base_of::value, + "Type must derive from and implement the ui::AbstractView interface"); + return dynamic_cast(view_); +} + +/** \} */ + } // namespace blender::ui diff --git a/source/blender/editors/include/UI_grid_view.hh b/source/blender/editors/include/UI_grid_view.hh index 74ce16ccd85..805198f38ef 100644 --- a/source/blender/editors/include/UI_grid_view.hh +++ b/source/blender/editors/include/UI_grid_view.hh @@ -19,7 +19,7 @@ struct bContext; struct PreviewImage; struct uiBlock; -struct uiButGridTile; +struct uiButViewItem; struct uiLayout; struct View2D; struct wmNotifier; @@ -41,32 +41,22 @@ class AbstractGridViewItem : public AbstractViewItem { protected: /** Reference to a string that uniquely identifies this item in the view. */ StringRef identifier_{}; - /** Every visible item gets a button of type #UI_BTYPE_GRID_TILE during the layout building. */ - uiButGridTile *grid_tile_but_ = nullptr; + /** Every visible item gets a button of type #UI_BTYPE_VIEW_ITEM during the layout building. */ + uiButViewItem *view_item_but_ = nullptr; public: virtual ~AbstractGridViewItem() = default; virtual void build_grid_tile(uiLayout &layout) const = 0; - /** - * Compare this item's identifier to \a other to check if they represent the same data. - * Used to recognize an item from a previous redraw, to be able to keep its state (e.g. active, - * renaming, etc.). - */ - bool matches(const AbstractGridViewItem &other) const; - const AbstractGridView &get_view() const; - /** - * Requires the tree to have completed reconstruction, see #is_reconstructed(). Otherwise we - * can't be sure about the item state. - */ - bool is_active() const; - protected: AbstractGridViewItem(StringRef identifier); + /** See AbstractViewItem::matches(). */ + virtual bool matches(const AbstractViewItem &other) const override; + /** Called when the item's state changes from inactive to active. */ virtual void on_activate(); /** diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h index f39c62d8e2b..99a1d038cca 100644 --- a/source/blender/editors/include/UI_interface.h +++ b/source/blender/editors/include/UI_interface.h @@ -74,10 +74,8 @@ typedef struct uiLayout uiLayout; typedef struct uiPopupBlockHandle uiPopupBlockHandle; /* C handle for C++ #ui::AbstractView type. */ typedef struct uiViewHandle uiViewHandle; -/* C handle for C++ #ui::AbstractTreeViewItem type. */ -typedef struct uiTreeViewItemHandle uiTreeViewItemHandle; -/* C handle for C++ #ui::AbstractGridViewItem type. */ -typedef struct uiGridViewItemHandle uiGridViewItemHandle; +/* C handle for C++ #ui::AbstractViewItem type. */ +typedef struct uiViewItemHandle uiViewItemHandle; /* Defines */ @@ -391,10 +389,8 @@ typedef enum { /** Resize handle (resize uilist). */ UI_BTYPE_GRIP = 57 << 9, UI_BTYPE_DECORATOR = 58 << 9, - /* An item in a tree view. Parent items may be collapsible. */ - UI_BTYPE_TREEROW = 59 << 9, - /* An item in a grid view. */ - UI_BTYPE_GRID_TILE = 60 << 9, + /* An item a view (see #ui::AbstractViewItem). */ + UI_BTYPE_VIEW_ITEM = 59 << 9, } eButType; #define BUTTYPE (63 << 9) @@ -1685,8 +1681,6 @@ int UI_search_items_find_index(uiSearchItems *items, const char *name); */ void UI_but_hint_drawstr_set(uiBut *but, const char *string); -void UI_but_treerow_indentation_set(uiBut *but, int indentation); - void UI_but_node_link_set(uiBut *but, struct bNodeSocket *socket, const float draw_color[4]); void UI_but_number_step_size_set(uiBut *but, float step_size); @@ -3201,45 +3195,44 @@ void UI_interface_tag_script_reload(void); void UI_block_views_listen(const uiBlock *block, const struct wmRegionListenerParams *listener_params); -bool UI_grid_view_item_is_active(const uiGridViewItemHandle *item_handle); -bool UI_tree_view_item_is_active(const uiTreeViewItemHandle *item); -bool UI_grid_view_item_matches(const uiGridViewItemHandle *a, const uiGridViewItemHandle *b); -bool UI_tree_view_item_matches(const uiTreeViewItemHandle *a, const uiTreeViewItemHandle *b); +bool UI_view_item_is_active(const uiViewItemHandle *item_handle); +bool UI_view_item_matches(const uiViewItemHandle *a_handle, const uiViewItemHandle *b_handle); /** - * Attempt to start dragging the tree-item \a item_. This will not work if the tree item doesn't - * support dragging, i.e. it won't create a drag-controller upon request. - * \return True if dragging started successfully, otherwise false. + * Can \a item_handle be renamed right now? Note that this isn't just a mere wrapper around + * #AbstractViewItem::supports_renaming(). This also checks if there is another item being renamed, + * and returns false if so. */ -bool UI_tree_view_item_drag_start(struct bContext *C, uiTreeViewItemHandle *item_); -bool UI_tree_view_item_can_drop(const uiTreeViewItemHandle *item_, - const struct wmDrag *drag, - const char **r_disabled_hint); -char *UI_tree_view_item_drop_tooltip(const uiTreeViewItemHandle *item, const struct wmDrag *drag); +bool UI_view_item_can_rename(const uiViewItemHandle *item_handle); +void UI_view_item_begin_rename(uiViewItemHandle *item_handle); + +void UI_view_item_context_menu_build(struct bContext *C, + const uiViewItemHandle *item_handle, + uiLayout *column); + /** - * Let a tree-view item handle a drop event. - * \return True if the drop was handled by the tree-view item. + * Attempt to start dragging \a item_. This will not work if the view item doesn't + * support dragging, i.e. if it won't create a drag-controller upon request. + * \return True if dragging started successfully, otherwise false. */ -bool UI_tree_view_item_drop_handle(struct bContext *C, - const uiTreeViewItemHandle *item_, - const struct ListBase *drags); +bool UI_view_item_drag_start(struct bContext *C, const uiViewItemHandle *item_); +bool UI_view_item_can_drop(const uiViewItemHandle *item_, + const struct wmDrag *drag, + const char **r_disabled_hint); +char *UI_view_item_drop_tooltip(const uiViewItemHandle *item, const struct wmDrag *drag); /** - * Can \a item_handle be renamed right now? Not that this isn't just a mere wrapper around - * #AbstractTreeViewItem::can_rename(). This also checks if there is another item being renamed, - * and returns false if so. + * Let a view item handle a drop event. + * \return True if the drop was handled by the view item. */ -bool UI_tree_view_item_can_rename(const uiTreeViewItemHandle *item_handle); -void UI_tree_view_item_begin_rename(uiTreeViewItemHandle *item_handle); - -void UI_tree_view_item_context_menu_build(struct bContext *C, - const uiTreeViewItemHandle *item, - uiLayout *column); +bool UI_view_item_drop_handle(struct bContext *C, + const uiViewItemHandle *item_, + const struct ListBase *drags); /** - * \param xy: Coordinate to find a tree-row item at, in window space. + * \param xy: Coordinate to find a view item at, in window space. */ -uiTreeViewItemHandle *UI_block_tree_view_find_item_at(const struct ARegion *region, - const int xy[2]) ATTR_NONNULL(1, 2); -uiTreeViewItemHandle *UI_block_tree_view_find_active_item(const struct ARegion *region); +uiViewItemHandle *UI_block_view_find_item_at(const struct ARegion *region, const int xy[2]) + ATTR_NONNULL(); +uiViewItemHandle *UI_block_view_find_active_item(const struct ARegion *region); #ifdef __cplusplus } diff --git a/source/blender/editors/include/UI_tree_view.hh b/source/blender/editors/include/UI_tree_view.hh index fa427d96dd8..872a6085060 100644 --- a/source/blender/editors/include/UI_tree_view.hh +++ b/source/blender/editors/include/UI_tree_view.hh @@ -9,7 +9,6 @@ #pragma once -#include #include #include #include @@ -25,18 +24,13 @@ struct bContext; struct uiBlock; struct uiBut; -struct uiButTreeRow; +struct uiButViewItem; struct uiLayout; -struct wmDrag; -struct wmEvent; -struct wmNotifier; namespace blender::ui { class AbstractTreeView; class AbstractTreeViewItem; -class AbstractTreeViewItemDropController; -class AbstractTreeViewItemDragController; /* ---------------------------------------------------------------------- */ /** \name Tree-View Item Container @@ -161,19 +155,17 @@ class AbstractTreeViewItem : public AbstractViewItem, public TreeViewItemContain private: bool is_open_ = false; - bool is_renaming_ = false; protected: /** This label is used as the default way to identifying an item within its parent. */ std::string label_{}; - /** Every visible item gets a button of type #UI_BTYPE_TREEROW during the layout building. */ - uiButTreeRow *tree_row_but_ = nullptr; + /** Every visible item gets a button of type #UI_BTYPE_VIEW_ITEM during the layout building. */ + uiButViewItem *view_item_but_ = nullptr; public: virtual ~AbstractTreeViewItem() = default; virtual void build_row(uiLayout &row) = 0; - virtual void build_context_menu(bContext &C, uiLayout &column) const; AbstractTreeView &get_tree_view() const; @@ -185,11 +177,6 @@ class AbstractTreeViewItem : public AbstractViewItem, public TreeViewItemContain * can't be sure about the item state. */ bool is_collapsed() const; - /** - * Requires the tree to have completed reconstruction, see #is_reconstructed(). Otherwise we - * can't be sure about the item state. - */ - bool is_active() const; protected: /** @@ -202,25 +189,19 @@ class AbstractTreeViewItem : public AbstractViewItem, public TreeViewItemContain */ virtual std::optional should_be_active() const; - /** - * Queries if the tree-view item supports renaming in principle. Renaming may still fail, e.g. if - * another item is already being renamed. - */ - virtual bool supports_renaming() const; - /** - * Try renaming the item, or the data it represents. Can assume - * #AbstractTreeViewItem::supports_renaming() returned true. Sub-classes that override this - * should usually call this, unless they have a custom #AbstractTreeViewItem.matches(). - * - * \return True if the renaming was successful. - */ - virtual bool rename(StringRefNull new_name); + /** See AbstractViewItem::get_rename_string(). */ + virtual StringRef get_rename_string() const override; + /** See AbstractViewItem::rename(). */ + virtual bool rename(StringRefNull new_name) override; /** * Return whether the item can be collapsed. Used to disable collapsing for items with children. */ virtual bool supports_collapsing() const; + /** See #AbstractViewItem::matches(). */ + virtual bool matches(const AbstractViewItem &other) const override; + /** See #AbstractViewItem::update_from_old(). */ virtual void update_from_old(const AbstractViewItem &old) override; @@ -230,22 +211,11 @@ class AbstractTreeViewItem : public AbstractViewItem, public TreeViewItemContain * open/closed, active, etc.). Items are only matched if their parents also match. * By default this just matches the item's label (if the parents match!). If that isn't * good enough for a sub-class, that can override it. - */ - virtual bool matches(const AbstractTreeViewItem &other) const; - - /** - * If an item wants to support being dragged, it has to return a drag controller here. - * That is an object implementing #AbstractTreeViewItemDragController. - */ - virtual std::unique_ptr create_drag_controller() const; - /** - * If an item wants to support dropping data into it, it has to return a drop controller here. - * That is an object implementing #AbstractTreeViewItemDropController. * - * \note This drop controller may be requested for each event. The tree-view doesn't keep a drop - * controller around currently. So it can not contain persistent state. + * TODO #matches_single() is a rather temporary name, used to indicate that this only compares + * the item itself, not the parents. Item matching is expected to change quite a bit anyway. */ - virtual std::unique_ptr create_drop_controller() const; + virtual bool matches_single(const AbstractTreeViewItem &other) const; /** * Activates this item, deactivates other items, calls the #AbstractTreeViewItem::on_activate() @@ -263,98 +233,30 @@ class AbstractTreeViewItem : public AbstractViewItem, public TreeViewItemContain */ bool is_hovered() const; bool is_collapsible() const; - bool is_renaming() const; void ensure_parents_uncollapsed(); - uiButTreeRow *tree_row_button(); + uiButViewItem *view_item_button(); private: - static void rename_button_fn(bContext *, void *, char *); - static AbstractTreeViewItem *find_tree_item_from_rename_button(const uiBut &but); static void tree_row_click_fn(struct bContext *, void *, void *); static void collapse_chevron_click_fn(bContext *, void *but_arg1, void *); static bool is_collapse_chevron_but(const uiBut *but); /** See #AbstractTreeView::change_state_delayed() */ void change_state_delayed(); - void end_renaming(); void add_treerow_button(uiBlock &block); void add_indent(uiLayout &row) const; void add_collapse_chevron(uiBlock &block) const; void add_rename_button(uiLayout &row); - bool matches_including_parents(const AbstractTreeViewItem &other) const; bool has_active_child() const; int count_parents() const; }; /** \} */ -/* ---------------------------------------------------------------------- */ -/** \name Drag 'n Drop - * \{ */ - -/** - * Class to enable dragging a tree-item. An item can return a drop controller for itself via a - * custom implementation of #AbstractTreeViewItem::create_drag_controller(). - */ -class AbstractTreeViewItemDragController { - protected: - AbstractTreeView &tree_view_; - - public: - AbstractTreeViewItemDragController(AbstractTreeView &tree_view); - virtual ~AbstractTreeViewItemDragController() = default; - - virtual int get_drag_type() const = 0; - virtual void *create_drag_data() const = 0; - virtual void on_drag_start(); - - template inline TreeViewType &tree_view() const; -}; - -/** - * Class to customize the drop behavior of a tree-item, plus the behavior when dragging over this - * item. An item can return a drop controller for itself via a custom implementation of - * #AbstractTreeViewItem::create_drop_controller(). - */ -class AbstractTreeViewItemDropController { - protected: - AbstractTreeView &tree_view_; - - public: - AbstractTreeViewItemDropController(AbstractTreeView &tree_view); - virtual ~AbstractTreeViewItemDropController() = default; - - /** - * Check if the data dragged with \a drag can be dropped on the item this controller is for. - * \param r_disabled_hint: Return a static string to display to the user, explaining why dropping - * isn't possible on this item. Shouldn't be done too aggressively, e.g. - * don't set this if the drag-type can't be dropped here; only if it can - * but there's another reason it can't be dropped. - * Can assume this is a non-null pointer. - */ - virtual bool can_drop(const wmDrag &drag, const char **r_disabled_hint) const = 0; - /** - * Custom text to display when dragging over a tree item. Should explain what happens when - * dropping the data onto this item. Will only be used if #AbstractTreeViewItem::can_drop() - * returns true, so the implementing override doesn't have to check that again. - * The returned value must be a translated string. - */ - virtual std::string drop_tooltip(const wmDrag &drag) const = 0; - /** - * Execute the logic to apply a drop of the data dragged with \a drag onto/into the item this - * controller is for. - */ - virtual bool on_drop(struct bContext *C, const wmDrag &drag) = 0; - - template inline TreeViewType &tree_view() const; -}; - -/** \} */ - /* ---------------------------------------------------------------------- */ /** \name Predefined Tree-View Item Types * @@ -426,18 +328,4 @@ inline ItemT &TreeViewItemContainer::add_tree_item(Args &&...args) add_tree_item(std::make_unique(std::forward(args)...))); } -template TreeViewType &AbstractTreeViewItemDragController::tree_view() const -{ - static_assert(std::is_base_of::value, - "Type must derive from and implement the AbstractTreeView interface"); - return static_cast(tree_view_); -} - -template TreeViewType &AbstractTreeViewItemDropController::tree_view() const -{ - static_assert(std::is_base_of::value, - "Type must derive from and implement the AbstractTreeView interface"); - return static_cast(tree_view_); -} - } // namespace blender::ui diff --git a/source/blender/editors/interface/abstract_view.cc b/source/blender/editors/interface/abstract_view.cc index dea9600fde4..077c76a08f1 100644 --- a/source/blender/editors/interface/abstract_view.cc +++ b/source/blender/editors/interface/abstract_view.cc @@ -10,6 +10,13 @@ namespace blender::ui { +void AbstractView::register_item(AbstractViewItem &item) +{ + /* Actually modifies the item, not the view. But for the public API it "feels" a bit nicer to + * have the view base class register the items, rather than setting the view on the item. */ + item.view_ = this; +} + /* ---------------------------------------------------------------------- */ /** \name View Reconstruction * \{ */ diff --git a/source/blender/editors/interface/abstract_view_item.cc b/source/blender/editors/interface/abstract_view_item.cc index fc71dbe8b95..f73183d07e9 100644 --- a/source/blender/editors/interface/abstract_view_item.cc +++ b/source/blender/editors/interface/abstract_view_item.cc @@ -4,6 +4,16 @@ * \ingroup edinterface */ +#include "BKE_context.h" + +#include "BLI_listbase.h" +#include "BLI_string.h" + +#include "WM_api.h" + +#include "UI_interface.h" +#include "interface_intern.h" + #include "UI_abstract_view.hh" namespace blender::ui { @@ -15,8 +25,349 @@ namespace blender::ui { void AbstractViewItem::update_from_old(const AbstractViewItem &old) { is_active_ = old.is_active_; + is_renaming_ = old.is_renaming_; +} + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Renaming + * \{ */ + +bool AbstractViewItem::supports_renaming() const +{ + /* No renaming by default. */ + return false; +} +bool AbstractViewItem::rename(StringRefNull /*new_name*/) +{ + /* No renaming by default. */ + return false; +} + +StringRef AbstractViewItem::get_rename_string() const +{ + /* No rename string by default. */ + return {}; +} + +bool AbstractViewItem::is_renaming() const +{ + return is_renaming_; +} + +void AbstractViewItem::begin_renaming() +{ + AbstractView &view = get_view(); + if (view.is_renaming() || !supports_renaming()) { + return; + } + + if (view.begin_renaming()) { + is_renaming_ = true; + } + + StringRef initial_str = get_rename_string(); + std::copy(std::begin(initial_str), std::end(initial_str), std::begin(view.get_rename_buffer())); +} + +void AbstractViewItem::rename_apply() +{ + const AbstractView &view = get_view(); + rename(view.get_rename_buffer().data()); + end_renaming(); +} + +void AbstractViewItem::end_renaming() +{ + if (!is_renaming()) { + return; + } + + is_renaming_ = false; + + AbstractView &view = get_view(); + view.end_renaming(); +} + +static AbstractViewItem *find_item_from_rename_button(const uiBut &rename_but) +{ + /* A minimal sanity check, can't do much more here. */ + BLI_assert(rename_but.type == UI_BTYPE_TEXT && rename_but.poin); + + LISTBASE_FOREACH (uiBut *, but, &rename_but.block->buttons) { + if (but->type != UI_BTYPE_VIEW_ITEM) { + continue; + } + + uiButViewItem *view_item_but = (uiButViewItem *)but; + AbstractViewItem *item = reinterpret_cast(view_item_but->view_item); + const AbstractView &view = item->get_view(); + + if (item->is_renaming() && (view.get_rename_buffer().data() == rename_but.poin)) { + return item; + } + } + + return nullptr; +} + +static void rename_button_fn(bContext *UNUSED(C), void *arg, char *UNUSED(origstr)) +{ + const uiBut *rename_but = static_cast(arg); + AbstractViewItem *item = find_item_from_rename_button(*rename_but); + BLI_assert(item); + item->rename_apply(); +} + +void AbstractViewItem::add_rename_button(uiBlock &block) +{ + AbstractView &view = get_view(); + uiBut *rename_but = uiDefBut(&block, + UI_BTYPE_TEXT, + 1, + "", + 0, + 0, + UI_UNIT_X * 10, + UI_UNIT_Y, + view.get_rename_buffer().data(), + 1.0f, + view.get_rename_buffer().size(), + 0, + 0, + ""); + + /* Gotta be careful with what's passed to the `arg1` here. Any view data will be freed once the + * callback is executed. */ + UI_but_func_rename_set(rename_but, rename_button_fn, rename_but); + UI_but_flag_disable(rename_but, UI_BUT_UNDO); + + const bContext *evil_C = reinterpret_cast(block.evil_C); + ARegion *region = CTX_wm_region(evil_C); + /* Returns false if the button was removed. */ + if (UI_but_active_only(evil_C, region, &block, rename_but) == false) { + end_renaming(); + } +} + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Context Menu + * \{ */ + +void AbstractViewItem::build_context_menu(bContext & /*C*/, uiLayout & /*column*/) const +{ + /* No context menu by default. */ +} + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Drag 'n Drop + * \{ */ + +std::unique_ptr AbstractViewItem::create_drag_controller() const +{ + /* There's no drag controller (and hence no drag support) by default. */ + return nullptr; +} + +std::unique_ptr AbstractViewItem::create_drop_controller() const +{ + /* There's no drop controller (and hence no drop support) by default. */ + return nullptr; +} + +AbstractViewItemDragController::AbstractViewItemDragController(AbstractView &view) : view_(view) +{ +} + +void AbstractViewItemDragController::on_drag_start() +{ + /* Do nothing by default. */ +} + +AbstractViewItemDropController::AbstractViewItemDropController(AbstractView &view) : view_(view) +{ +} + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name General Getters & Setters + * \{ */ + +AbstractView &AbstractViewItem::get_view() const +{ + if (UNLIKELY(!view_)) { + throw std::runtime_error( + "Invalid state, item must be registered through AbstractView::register_item()"); + } + return *view_; +} + +bool AbstractViewItem::is_active() const +{ + BLI_assert_msg(get_view().is_reconstructed(), + "State can't be queried until reconstruction is completed"); + return is_active_; } /** \} */ } // namespace blender::ui + +/* ---------------------------------------------------------------------- */ +/** \name C-API + * \{ */ + +namespace blender::ui { + +/** + * Helper class to provide a higher level public (C-)API. Has access to private/protected view item + * members and ensures some invariants that way. + */ +class ViewItemAPIWrapper { + public: + static bool matches(const AbstractViewItem &a, const AbstractViewItem &b) + { + if (typeid(a) != typeid(b)) { + return false; + } + /* TODO should match the view as well. */ + return a.matches(b); + } + + static bool can_rename(const AbstractViewItem &item) + { + const AbstractView &view = item.get_view(); + return !view.is_renaming() && item.supports_renaming(); + } + + static bool drag_start(bContext &C, const AbstractViewItem &item) + { + const std::unique_ptr drag_controller = + item.create_drag_controller(); + if (!drag_controller) { + return false; + } + + WM_event_start_drag(&C, + ICON_NONE, + drag_controller->get_drag_type(), + drag_controller->create_drag_data(), + 0, + WM_DRAG_FREE_DATA); + drag_controller->on_drag_start(); + + return true; + } + + static bool can_drop(const AbstractViewItem &item, + const wmDrag &drag, + const char **r_disabled_hint) + { + const std::unique_ptr drop_controller = + item.create_drop_controller(); + if (!drop_controller) { + return false; + } + + return drop_controller->can_drop(drag, r_disabled_hint); + } + + static std::string drop_tooltip(const AbstractViewItem &item, const wmDrag &drag) + { + const std::unique_ptr drop_controller = + item.create_drop_controller(); + if (!drop_controller) { + return {}; + } + + return drop_controller->drop_tooltip(drag); + } + + static bool drop_handle(bContext &C, const AbstractViewItem &item, const ListBase &drags) + { + std::unique_ptr drop_controller = + item.create_drop_controller(); + + const char *disabled_hint_dummy = nullptr; + LISTBASE_FOREACH (const wmDrag *, drag, &drags) { + if (drop_controller->can_drop(*drag, &disabled_hint_dummy)) { + return drop_controller->on_drop(&C, *drag); + } + } + + return false; + } +}; + +} // namespace blender::ui + +using namespace blender::ui; + +bool UI_view_item_is_active(const uiViewItemHandle *item_handle) +{ + const AbstractViewItem &item = reinterpret_cast(*item_handle); + return item.is_active(); +} + +bool UI_view_item_matches(const uiViewItemHandle *a_handle, const uiViewItemHandle *b_handle) +{ + const AbstractViewItem &a = reinterpret_cast(*a_handle); + const AbstractViewItem &b = reinterpret_cast(*b_handle); + return ViewItemAPIWrapper::matches(a, b); +} + +bool UI_view_item_can_rename(const uiViewItemHandle *item_handle) +{ + const AbstractViewItem &item = reinterpret_cast(*item_handle); + return ViewItemAPIWrapper::can_rename(item); +} + +void UI_view_item_begin_rename(uiViewItemHandle *item_handle) +{ + AbstractViewItem &item = reinterpret_cast(*item_handle); + item.begin_renaming(); +} + +void UI_view_item_context_menu_build(bContext *C, + const uiViewItemHandle *item_handle, + uiLayout *column) +{ + const AbstractViewItem &item = reinterpret_cast(*item_handle); + item.build_context_menu(*C, *column); +} + +bool UI_view_item_drag_start(bContext *C, const uiViewItemHandle *item_) +{ + const AbstractViewItem &item = reinterpret_cast(*item_); + return ViewItemAPIWrapper::drag_start(*C, item); +} + +bool UI_view_item_can_drop(const uiViewItemHandle *item_, + const wmDrag *drag, + const char **r_disabled_hint) +{ + const AbstractViewItem &item = reinterpret_cast(*item_); + return ViewItemAPIWrapper::can_drop(item, *drag, r_disabled_hint); +} + +char *UI_view_item_drop_tooltip(const uiViewItemHandle *item_, const wmDrag *drag) +{ + const AbstractViewItem &item = reinterpret_cast(*item_); + + const std::string tooltip = ViewItemAPIWrapper::drop_tooltip(item, *drag); + return tooltip.empty() ? nullptr : BLI_strdup(tooltip.c_str()); +} + +bool UI_view_item_drop_handle(bContext *C, const uiViewItemHandle *item_, const ListBase *drags) +{ + const AbstractViewItem &item = reinterpret_cast(*item_); + return ViewItemAPIWrapper::drop_handle(*C, item, *drags); +} + +/** \} */ diff --git a/source/blender/editors/interface/grid_view.cc b/source/blender/editors/interface/grid_view.cc index 362642b0846..37fbb33f83b 100644 --- a/source/blender/editors/interface/grid_view.cc +++ b/source/blender/editors/interface/grid_view.cc @@ -95,18 +95,19 @@ AbstractGridViewItem::AbstractGridViewItem(StringRef identifier) : identifier_(i { } -bool AbstractGridViewItem::matches(const AbstractGridViewItem &other) const +bool AbstractGridViewItem::matches(const AbstractViewItem &other) const { - return identifier_ == other.identifier_; + const AbstractGridViewItem &other_grid_item = dynamic_cast(other); + return identifier_ == other_grid_item.identifier_; } void AbstractGridViewItem::grid_tile_click_fn(struct bContext * /*C*/, void *but_arg1, void * /*arg2*/) { - uiButGridTile *grid_tile_but = (uiButGridTile *)but_arg1; + uiButViewItem *view_item_but = (uiButViewItem *)but_arg1; AbstractGridViewItem &grid_item = reinterpret_cast( - *grid_tile_but->view_item); + *view_item_but->view_item); grid_item.activate(); } @@ -114,8 +115,8 @@ void AbstractGridViewItem::grid_tile_click_fn(struct bContext * /*C*/, void AbstractGridViewItem::add_grid_tile_button(uiBlock &block) { const GridViewStyle &style = get_view().get_style(); - grid_tile_but_ = (uiButGridTile *)uiDefBut(&block, - UI_BTYPE_GRID_TILE, + view_item_but_ = (uiButViewItem *)uiDefBut(&block, + UI_BTYPE_VIEW_ITEM, 0, "", 0, @@ -129,15 +130,8 @@ void AbstractGridViewItem::add_grid_tile_button(uiBlock &block) 0, ""); - grid_tile_but_->view_item = reinterpret_cast(this); - UI_but_func_set(&grid_tile_but_->but, grid_tile_click_fn, grid_tile_but_, nullptr); -} - -bool AbstractGridViewItem::is_active() const -{ - BLI_assert_msg(get_view().is_reconstructed(), - "State can't be queried until reconstruction is completed"); - return is_active_; + view_item_but_->view_item = reinterpret_cast(this); + UI_but_func_set(&view_item_but_->but, grid_tile_click_fn, view_item_but_, nullptr); } void AbstractGridViewItem::on_activate() @@ -468,24 +462,3 @@ std::optional PreviewGridItem::should_be_active() const } } // namespace blender::ui - -using namespace blender::ui; - -/* ---------------------------------------------------------------------- */ -/* C-API */ - -using namespace blender::ui; - -bool UI_grid_view_item_is_active(const uiGridViewItemHandle *item_handle) -{ - const AbstractGridViewItem &item = reinterpret_cast(*item_handle); - return item.is_active(); -} - -bool UI_grid_view_item_matches(const uiGridViewItemHandle *a_handle, - const uiGridViewItemHandle *b_handle) -{ - const AbstractGridViewItem &a = reinterpret_cast(*a_handle); - const AbstractGridViewItem &b = reinterpret_cast(*b_handle); - return a.matches(b); -} diff --git a/source/blender/editors/interface/interface.cc b/source/blender/editors/interface/interface.cc index c0df193de87..2f9e69137ed 100644 --- a/source/blender/editors/interface/interface.cc +++ b/source/blender/editors/interface/interface.cc @@ -769,20 +769,11 @@ static bool ui_but_equals_old(const uiBut *but, const uiBut *oldbut) return false; } - if ((but->type == UI_BTYPE_TREEROW) && (oldbut->type == UI_BTYPE_TREEROW)) { - uiButTreeRow *but_treerow = (uiButTreeRow *)but; - uiButTreeRow *oldbut_treerow = (uiButTreeRow *)oldbut; - if (!but_treerow->tree_item || !oldbut_treerow->tree_item || - !UI_tree_view_item_matches(but_treerow->tree_item, oldbut_treerow->tree_item)) { - return false; - } - } - - if ((but->type == UI_BTYPE_GRID_TILE) && (oldbut->type == UI_BTYPE_GRID_TILE)) { - uiButGridTile *but_gridtile = (uiButGridTile *)but; - uiButGridTile *oldbut_gridtile = (uiButGridTile *)oldbut; - if (!but_gridtile->view_item || !oldbut_gridtile->view_item || - !UI_grid_view_item_matches(but_gridtile->view_item, oldbut_gridtile->view_item)) { + if ((but->type == UI_BTYPE_VIEW_ITEM) && (oldbut->type == UI_BTYPE_VIEW_ITEM)) { + uiButViewItem *but_item = (uiButViewItem *)but; + uiButViewItem *oldbut_item = (uiButViewItem *)oldbut; + if (!but_item->view_item || !oldbut_item->view_item || + !UI_view_item_matches(but_item->view_item, oldbut_item->view_item)) { return false; } } @@ -907,16 +898,10 @@ static void ui_but_update_old_active_from_new(uiBut *oldbut, uiBut *but) progress_oldbut->progress = progress_but->progress; break; } - case UI_BTYPE_TREEROW: { - uiButTreeRow *treerow_oldbut = (uiButTreeRow *)oldbut; - uiButTreeRow *treerow_newbut = (uiButTreeRow *)but; - SWAP(uiTreeViewItemHandle *, treerow_newbut->tree_item, treerow_oldbut->tree_item); - break; - } - case UI_BTYPE_GRID_TILE: { - uiButGridTile *gridtile_oldbut = (uiButGridTile *)oldbut; - uiButGridTile *gridtile_newbut = (uiButGridTile *)but; - SWAP(uiGridViewItemHandle *, gridtile_newbut->view_item, gridtile_oldbut->view_item); + case UI_BTYPE_VIEW_ITEM: { + uiButViewItem *view_item_oldbut = (uiButViewItem *)oldbut; + uiButViewItem *view_item_newbut = (uiButViewItem *)but; + SWAP(uiViewItemHandle *, view_item_newbut->view_item, view_item_oldbut->view_item); break; } default: @@ -1013,7 +998,7 @@ static bool ui_but_update_from_old_block(const bContext *C, /* Stupid special case: The active button may be inside (as in, overlapped on top) a view-item * button which we also want to keep highlighted then. */ - if (ui_but_is_view_item(but)) { + if (but->type == UI_BTYPE_VIEW_ITEM) { flag_copy |= UI_ACTIVE; } @@ -2245,21 +2230,12 @@ int ui_but_is_pushed_ex(uiBut *but, double *value) } } break; - case UI_BTYPE_TREEROW: { - uiButTreeRow *tree_row_but = (uiButTreeRow *)but; - - is_push = -1; - if (tree_row_but->tree_item) { - is_push = UI_tree_view_item_is_active(tree_row_but->tree_item); - } - break; - } - case UI_BTYPE_GRID_TILE: { - uiButGridTile *grid_tile_but = (uiButGridTile *)but; + case UI_BTYPE_VIEW_ITEM: { + const uiButViewItem *view_item_but = (const uiButViewItem *)but; is_push = -1; - if (grid_tile_but->view_item) { - is_push = UI_grid_view_item_is_active(grid_tile_but->view_item); + if (view_item_but->view_item) { + is_push = UI_view_item_is_active(view_item_but->view_item); } break; } @@ -4011,17 +3987,13 @@ static void ui_but_alloc_info(const eButType type, alloc_size = sizeof(uiButCurveProfile); alloc_str = "uiButCurveProfile"; break; - case UI_BTYPE_TREEROW: - alloc_size = sizeof(uiButTreeRow); - alloc_str = "uiButTreeRow"; - break; case UI_BTYPE_HOTKEY_EVENT: alloc_size = sizeof(uiButHotkeyEvent); alloc_str = "uiButHotkeyEvent"; break; - case UI_BTYPE_GRID_TILE: - alloc_size = sizeof(uiButGridTile); - alloc_str = "uiButGridTile"; + case UI_BTYPE_VIEW_ITEM: + alloc_size = sizeof(uiButViewItem); + alloc_str = "uiButViewItem"; break; default: alloc_size = sizeof(uiBut); @@ -4214,7 +4186,6 @@ static uiBut *ui_def_but(uiBlock *block, UI_BTYPE_BLOCK, UI_BTYPE_BUT_MENU, UI_BTYPE_SEARCH_MENU, - UI_BTYPE_TREEROW, UI_BTYPE_POPOVER)) { but->drawflag |= (UI_BUT_TEXT_LEFT | UI_BUT_ICON_LEFT); } @@ -6469,15 +6440,6 @@ uiBut *uiDefSearchButO_ptr(uiBlock *block, return but; } -void UI_but_treerow_indentation_set(uiBut *but, int indentation) -{ - uiButTreeRow *but_row = (uiButTreeRow *)but; - BLI_assert(but->type == UI_BTYPE_TREEROW); - - but_row->indentation = indentation; - BLI_assert(indentation >= 0); -} - void UI_but_hint_drawstr_set(uiBut *but, const char *string) { ui_but_add_shortcut(but, string, false); diff --git a/source/blender/editors/interface/interface_context_menu.c b/source/blender/editors/interface/interface_context_menu.c index e58298cdaee..518fe65ee09 100644 --- a/source/blender/editors/interface/interface_context_menu.c +++ b/source/blender/editors/interface/interface_context_menu.c @@ -927,11 +927,11 @@ bool ui_popup_context_menu_for_button(bContext *C, uiBut *but, const wmEvent *ev { const ARegion *region = CTX_wm_menu(C) ? CTX_wm_menu(C) : CTX_wm_region(C); - uiButTreeRow *treerow_but = (uiButTreeRow *)ui_tree_row_find_mouse_over(region, event->xy); - if (treerow_but) { - BLI_assert(treerow_but->but.type == UI_BTYPE_TREEROW); - UI_tree_view_item_context_menu_build( - C, treerow_but->tree_item, uiLayoutColumn(layout, false)); + uiButViewItem *view_item_but = (uiButViewItem *)ui_view_item_find_mouse_over(region, + event->xy); + if (view_item_but) { + BLI_assert(view_item_but->but.type == UI_BTYPE_VIEW_ITEM); + UI_view_item_context_menu_build(C, view_item_but->view_item, uiLayoutColumn(layout, false)); uiItemS(layout); } } diff --git a/source/blender/editors/interface/interface_dropboxes.cc b/source/blender/editors/interface/interface_dropboxes.cc index 9d3c1372b15..df488fb9127 100644 --- a/source/blender/editors/interface/interface_dropboxes.cc +++ b/source/blender/editors/interface/interface_dropboxes.cc @@ -22,15 +22,14 @@ #include "UI_interface.h" /* -------------------------------------------------------------------- */ -/** \name Tree View Drag/Drop Callbacks +/** \name View Drag/Drop Callbacks * \{ */ -static bool ui_tree_view_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event) +static bool ui_view_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event) { const ARegion *region = CTX_wm_region(C); - const uiTreeViewItemHandle *hovered_tree_item = UI_block_tree_view_find_item_at(region, - event->xy); - if (!hovered_tree_item) { + const uiViewItemHandle *hovered_item = UI_block_view_find_item_at(region, event->xy); + if (!hovered_item) { return false; } @@ -39,21 +38,21 @@ static bool ui_tree_view_drop_poll(bContext *C, wmDrag *drag, const wmEvent *eve } drag->drop_state.free_disabled_info = false; - return UI_tree_view_item_can_drop(hovered_tree_item, drag, &drag->drop_state.disabled_info); + return UI_view_item_can_drop(hovered_item, drag, &drag->drop_state.disabled_info); } -static char *ui_tree_view_drop_tooltip(bContext *C, - wmDrag *drag, - const int xy[2], - wmDropBox *UNUSED(drop)) +static char *ui_view_drop_tooltip(bContext *C, + wmDrag *drag, + const int xy[2], + wmDropBox *UNUSED(drop)) { const ARegion *region = CTX_wm_region(C); - const uiTreeViewItemHandle *hovered_tree_item = UI_block_tree_view_find_item_at(region, xy); - if (!hovered_tree_item) { + const uiViewItemHandle *hovered_item = UI_block_view_find_item_at(region, xy); + if (!hovered_item) { return nullptr; } - return UI_tree_view_item_drop_tooltip(hovered_tree_item, drag); + return UI_view_item_drop_tooltip(hovered_item, drag); } /** \} */ @@ -140,12 +139,7 @@ void ED_dropboxes_ui() { ListBase *lb = WM_dropboxmap_find("User Interface", SPACE_EMPTY, 0); - WM_dropbox_add(lb, - "UI_OT_tree_view_drop", - ui_tree_view_drop_poll, - nullptr, - nullptr, - ui_tree_view_drop_tooltip); + WM_dropbox_add(lb, "UI_OT_view_drop", ui_view_drop_poll, nullptr, nullptr, ui_view_drop_tooltip); WM_dropbox_add(lb, "UI_OT_drop_name", ui_drop_name_poll, diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c index 2496136883d..0a50522a141 100644 --- a/source/blender/editors/interface/interface_handlers.c +++ b/source/blender/editors/interface/interface_handlers.c @@ -1151,7 +1151,10 @@ static void ui_apply_but_ROW(bContext *C, uiBlock *block, uiBut *but, uiHandleBu data->applied = true; } -static void ui_apply_but_TREEROW(bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data) +static void ui_apply_but_VIEW_ITEM(bContext *C, + uiBlock *block, + uiBut *but, + uiHandleButtonData *data) { if (data->apply_through_extra_icon) { /* Don't apply this, it would cause unintended tree-row toggling when clicking on extra icons. @@ -2128,10 +2131,10 @@ static bool ui_but_drag_init(bContext *C, return false; } } - else if (but->type == UI_BTYPE_TREEROW) { - uiButTreeRow *tree_row_but = (uiButTreeRow *)but; - if (tree_row_but->tree_item) { - UI_tree_view_item_drag_start(C, tree_row_but->tree_item); + else if (but->type == UI_BTYPE_VIEW_ITEM) { + const uiButViewItem *view_item_but = (uiButViewItem *)but; + if (view_item_but->view_item) { + UI_view_item_drag_start(C, view_item_but->view_item); } } else { @@ -2289,11 +2292,8 @@ static void ui_apply_but( case UI_BTYPE_ROW: ui_apply_but_ROW(C, block, but, data); break; - case UI_BTYPE_GRID_TILE: - ui_apply_but_ROW(C, block, but, data); - break; - case UI_BTYPE_TREEROW: - ui_apply_but_TREEROW(C, block, but, data); + case UI_BTYPE_VIEW_ITEM: + ui_apply_but_VIEW_ITEM(C, block, but, data); break; case UI_BTYPE_LISTROW: ui_apply_but_LISTROW(C, block, but, data); @@ -4764,53 +4764,13 @@ static int ui_do_but_TOG(bContext *C, uiBut *but, uiHandleButtonData *data, cons return WM_UI_HANDLER_CONTINUE; } -static int ui_do_but_TREEROW(bContext *C, - uiBut *but, - uiHandleButtonData *data, - const wmEvent *event) -{ - uiButTreeRow *tree_row_but = (uiButTreeRow *)but; - BLI_assert(tree_row_but->but.type == UI_BTYPE_TREEROW); - - if (data->state == BUTTON_STATE_HIGHLIGHT) { - if (event->type == LEFTMOUSE) { - switch (event->val) { - case KM_PRESS: - /* Extra icons have priority, don't mess with them. */ - if (ui_but_extra_operator_icon_mouse_over_get(but, data->region, event)) { - return WM_UI_HANDLER_BREAK; - } - button_activate_state(C, but, BUTTON_STATE_WAIT_DRAG); - data->dragstartx = event->xy[0]; - data->dragstarty = event->xy[1]; - return WM_UI_HANDLER_CONTINUE; - - case KM_CLICK: - button_activate_state(C, but, BUTTON_STATE_EXIT); - return WM_UI_HANDLER_BREAK; - - case KM_DBL_CLICK: - data->cancel = true; - UI_tree_view_item_begin_rename(tree_row_but->tree_item); - ED_region_tag_redraw(CTX_wm_region(C)); - return WM_UI_HANDLER_BREAK; - } - } - } - else if (data->state == BUTTON_STATE_WAIT_DRAG) { - /* Let "default" button handling take care of the drag logic. */ - return ui_do_but_EXIT(C, but, data, event); - } - - return WM_UI_HANDLER_CONTINUE; -} - -static int ui_do_but_GRIDTILE(bContext *C, - uiBut *but, - uiHandleButtonData *data, - const wmEvent *event) +static int ui_do_but_VIEW_ITEM(bContext *C, + uiBut *but, + uiHandleButtonData *data, + const wmEvent *event) { - BLI_assert(but->type == UI_BTYPE_GRID_TILE); + uiButViewItem *view_item_but = (uiButViewItem *)but; + BLI_assert(view_item_but->but.type == UI_BTYPE_VIEW_ITEM); if (data->state == BUTTON_STATE_HIGHLIGHT) { if (event->type == LEFTMOUSE) { @@ -4831,8 +4791,7 @@ static int ui_do_but_GRIDTILE(bContext *C, case KM_DBL_CLICK: data->cancel = true; - // uiButGridTile *grid_tile_but = (uiButGridTile *)but; - // UI_tree_view_item_begin_rename(grid_tile_but->tree_item); + UI_view_item_begin_rename(view_item_but->view_item); ED_region_tag_redraw(CTX_wm_region(C)); return WM_UI_HANDLER_BREAK; } @@ -7980,7 +7939,7 @@ static int ui_do_button(bContext *C, uiBlock *block, uiBut *but, const wmEvent * * to spawn the context menu should also activate the item. This makes it clear which item * will be operated on. * Apply the button immediately, so context menu polls get the right active item. */ - if (ELEM(but->type, UI_BTYPE_TREEROW)) { + if (ELEM(but->type, UI_BTYPE_VIEW_ITEM)) { ui_apply_but(C, but->block, but, but->active, true); } @@ -8045,11 +8004,8 @@ static int ui_do_button(bContext *C, uiBlock *block, uiBut *but, const wmEvent * case UI_BTYPE_ROW: retval = ui_do_but_TOG(C, but, data, event); break; - case UI_BTYPE_GRID_TILE: - retval = ui_do_but_GRIDTILE(C, but, data, event); - break; - case UI_BTYPE_TREEROW: - retval = ui_do_but_TREEROW(C, but, data, event); + case UI_BTYPE_VIEW_ITEM: + retval = ui_do_but_VIEW_ITEM(C, but, data, event); break; case UI_BTYPE_SCROLL: retval = ui_do_but_SCROLL(C, block, but, data, event); @@ -9725,7 +9681,7 @@ static int ui_handle_view_items_hover(const wmEvent *event, const ARegion *regio } LISTBASE_FOREACH (uiBut *, but, &block->buttons) { - if (ui_but_is_view_item(but)) { + if (but->type == UI_BTYPE_VIEW_ITEM) { but->flag &= ~UI_ACTIVE; has_view_item = true; } @@ -9752,7 +9708,7 @@ static int ui_handle_view_item_event(bContext *C, ARegion *region, uiBut *view_but) { - BLI_assert(ui_but_is_view_item(view_but)); + BLI_assert(view_but->type == UI_BTYPE_VIEW_ITEM); if (event->type == LEFTMOUSE) { /* Will free active button if there already is one. */ ui_handle_button_activate(C, region, view_but, BUTTON_ACTIVATE_OVER); diff --git a/source/blender/editors/interface/interface_intern.h b/source/blender/editors/interface/interface_intern.h index 5e0382f73a9..03b9d03a6e3 100644 --- a/source/blender/editors/interface/interface_intern.h +++ b/source/blender/editors/interface/interface_intern.h @@ -343,20 +343,12 @@ typedef struct uiButProgressbar { float progress; } uiButProgressbar; -/** Derived struct for #UI_BTYPE_TREEROW. */ -typedef struct uiButTreeRow { +typedef struct uiButViewItem { uiBut but; - uiTreeViewItemHandle *tree_item; - int indentation; -} uiButTreeRow; - -/** Derived struct for #UI_BTYPE_GRID_TILE. */ -typedef struct uiButGridTile { - uiBut but; - - uiGridViewItemHandle *view_item; -} uiButGridTile; + /* C-Handle to the view item this button was created for. */ + uiViewItemHandle *view_item; +} uiButViewItem; /** Derived struct for #UI_BTYPE_HSVCUBE. */ typedef struct uiButHSVCube { @@ -1372,7 +1364,6 @@ void ui_but_anim_decorate_update_from_flag(uiButDecorator *but); bool ui_but_is_editable(const uiBut *but) ATTR_WARN_UNUSED_RESULT; bool ui_but_is_editable_as_text(const uiBut *but) ATTR_WARN_UNUSED_RESULT; bool ui_but_is_toggle(const uiBut *but) ATTR_WARN_UNUSED_RESULT; -bool ui_but_is_view_item(const uiBut *but) ATTR_WARN_UNUSED_RESULT; /** * Can we mouse over the button or is it hidden/disabled/layout. * \note ctrl is kind of a hack currently, @@ -1406,9 +1397,7 @@ uiBut *ui_list_row_find_from_index(const struct ARegion *region, uiBut *listbox) ATTR_WARN_UNUSED_RESULT; uiBut *ui_view_item_find_mouse_over(const struct ARegion *region, const int xy[2]) ATTR_NONNULL(1, 2); -uiBut *ui_tree_row_find_mouse_over(const struct ARegion *region, const int xy[2]) - ATTR_NONNULL(1, 2); -uiBut *ui_tree_row_find_active(const struct ARegion *region); +uiBut *ui_view_item_find_active(const struct ARegion *region); typedef bool (*uiButFindPollFn)(const uiBut *but, const void *customdata); /** @@ -1546,8 +1535,8 @@ void ui_block_free_views(struct uiBlock *block); uiViewHandle *ui_block_view_find_matching_in_old_block(const uiBlock *new_block, const uiViewHandle *new_view); -uiButTreeRow *ui_block_view_find_treerow_in_old_block(const uiBlock *new_block, - const uiTreeViewItemHandle *new_item_handle); +uiButViewItem *ui_block_view_find_matching_view_item_but_in_old_block( + const uiBlock *new_block, const uiViewItemHandle *new_item_handle); /* interface_templates.c */ diff --git a/source/blender/editors/interface/interface_ops.c b/source/blender/editors/interface/interface_ops.c index aafb56119ae..7a51ed23677 100644 --- a/source/blender/editors/interface/interface_ops.c +++ b/source/blender/editors/interface/interface_ops.c @@ -2054,40 +2054,39 @@ static void UI_OT_list_start_filter(wmOperatorType *ot) /** \name UI Tree-View Drop Operator * \{ */ -static bool ui_tree_view_drop_poll(bContext *C) +static bool ui_view_drop_poll(bContext *C) { const wmWindow *win = CTX_wm_window(C); const ARegion *region = CTX_wm_region(C); - const uiTreeViewItemHandle *hovered_tree_item = UI_block_tree_view_find_item_at( - region, win->eventstate->xy); + const uiViewItemHandle *hovered_item = UI_block_view_find_item_at(region, win->eventstate->xy); - return hovered_tree_item != NULL; + return hovered_item != NULL; } -static int ui_tree_view_drop_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *event) +static int ui_view_drop_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *event) { if (event->custom != EVT_DATA_DRAGDROP) { return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH; } const ARegion *region = CTX_wm_region(C); - uiTreeViewItemHandle *hovered_tree_item = UI_block_tree_view_find_item_at(region, event->xy); + uiViewItemHandle *hovered_item = UI_block_view_find_item_at(region, event->xy); - if (!UI_tree_view_item_drop_handle(C, hovered_tree_item, event->customdata)) { + if (!UI_view_item_drop_handle(C, hovered_item, event->customdata)) { return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH; } return OPERATOR_FINISHED; } -static void UI_OT_tree_view_drop(wmOperatorType *ot) +static void UI_OT_view_drop(wmOperatorType *ot) { - ot->name = "Tree View drop"; - ot->idname = "UI_OT_tree_view_drop"; - ot->description = "Drag and drop items onto a tree item"; + ot->name = "View drop"; + ot->idname = "UI_OT_view_drop"; + ot->description = "Drag and drop items onto a data-set item"; - ot->invoke = ui_tree_view_drop_invoke; - ot->poll = ui_tree_view_drop_poll; + ot->invoke = ui_view_drop_invoke; + ot->poll = ui_view_drop_poll; ot->flag = OPTYPE_INTERNAL; } @@ -2095,43 +2094,42 @@ static void UI_OT_tree_view_drop(wmOperatorType *ot) /** \} */ /* -------------------------------------------------------------------- */ -/** \name UI Tree-View Item Rename Operator +/** \name UI View Item Rename Operator * - * General purpose renaming operator for tree-views. Thanks to this, to add a rename button to - * context menus for example, tree-view API users don't have to implement their own renaming - * operators with the same logic as they already have for their #ui::AbstractTreeViewItem::rename() - * override. + * General purpose renaming operator for views. Thanks to this, to add a rename button to context + * menus for example, view API users don't have to implement their own renaming operators with the + * same logic as they already have for their #ui::AbstractViewItem::rename() override. * * \{ */ -static bool ui_tree_view_item_rename_poll(bContext *C) +static bool ui_view_item_rename_poll(bContext *C) { const ARegion *region = CTX_wm_region(C); - const uiTreeViewItemHandle *active_item = UI_block_tree_view_find_active_item(region); - return active_item != NULL && UI_tree_view_item_can_rename(active_item); + const uiViewItemHandle *active_item = UI_block_view_find_active_item(region); + return active_item != NULL && UI_view_item_can_rename(active_item); } -static int ui_tree_view_item_rename_exec(bContext *C, wmOperator *UNUSED(op)) +static int ui_view_item_rename_exec(bContext *C, wmOperator *UNUSED(op)) { ARegion *region = CTX_wm_region(C); - uiTreeViewItemHandle *active_item = UI_block_tree_view_find_active_item(region); + uiViewItemHandle *active_item = UI_block_view_find_active_item(region); - UI_tree_view_item_begin_rename(active_item); + UI_view_item_begin_rename(active_item); ED_region_tag_redraw(region); return OPERATOR_FINISHED; } -static void UI_OT_tree_view_item_rename(wmOperatorType *ot) +static void UI_OT_view_item_rename(wmOperatorType *ot) { - ot->name = "Rename Tree-View Item"; - ot->idname = "UI_OT_tree_view_item_rename"; - ot->description = "Rename the active item in the tree"; + ot->name = "Rename View Item"; + ot->idname = "UI_OT_view_item_rename"; + ot->description = "Rename the active item in the data-set view"; - ot->exec = ui_tree_view_item_rename_exec; - ot->poll = ui_tree_view_item_rename_poll; + ot->exec = ui_view_item_rename_exec; + ot->poll = ui_view_item_rename_poll; /* Could get a custom tooltip via the `get_description()` callback and another overridable - * function of the tree-view. */ + * function of the view. */ ot->flag = OPTYPE_INTERNAL; } @@ -2235,8 +2233,8 @@ void ED_operatortypes_ui(void) WM_operatortype_append(UI_OT_list_start_filter); - WM_operatortype_append(UI_OT_tree_view_drop); - WM_operatortype_append(UI_OT_tree_view_item_rename); + WM_operatortype_append(UI_OT_view_drop); + WM_operatortype_append(UI_OT_view_item_rename); /* external */ WM_operatortype_append(UI_OT_eyedropper_color); diff --git a/source/blender/editors/interface/interface_query.cc b/source/blender/editors/interface/interface_query.cc index 71cf60985df..f084f3e06cb 100644 --- a/source/blender/editors/interface/interface_query.cc +++ b/source/blender/editors/interface/interface_query.cc @@ -54,13 +54,7 @@ bool ui_but_is_toggle(const uiBut *but) UI_BTYPE_TOGGLE_N, UI_BTYPE_CHECKBOX, UI_BTYPE_CHECKBOX_N, - UI_BTYPE_ROW, - UI_BTYPE_TREEROW); -} - -bool ui_but_is_view_item(const uiBut *but) -{ - return ELEM(but->type, UI_BTYPE_TREEROW, UI_BTYPE_GRID_TILE); + UI_BTYPE_ROW); } bool ui_but_is_interactive_ex(const uiBut *but, const bool labeledit, const bool for_tooltip) @@ -462,14 +456,9 @@ uiBut *ui_list_row_find_from_index(const ARegion *region, const int index, uiBut return ui_but_find(region, ui_but_is_listrow_at_index, &data); } -static bool ui_but_is_treerow(const uiBut *but, const void *UNUSED(customdata)) -{ - return but->type == UI_BTYPE_TREEROW; -} - static bool ui_but_is_view_item_fn(const uiBut *but, const void *UNUSED(customdata)) { - return ui_but_is_view_item(but); + return but->type == UI_BTYPE_VIEW_ITEM; } uiBut *ui_view_item_find_mouse_over(const ARegion *region, const int xy[2]) @@ -477,24 +466,19 @@ uiBut *ui_view_item_find_mouse_over(const ARegion *region, const int xy[2]) return ui_but_find_mouse_over_ex(region, xy, false, false, ui_but_is_view_item_fn, nullptr); } -uiBut *ui_tree_row_find_mouse_over(const ARegion *region, const int xy[2]) -{ - return ui_but_find_mouse_over_ex(region, xy, false, false, ui_but_is_treerow, nullptr); -} - -static bool ui_but_is_active_treerow(const uiBut *but, const void *customdata) +static bool ui_but_is_active_view_item(const uiBut *but, const void *UNUSED(customdata)) { - if (!ui_but_is_treerow(but, customdata)) { + if (but->type != UI_BTYPE_VIEW_ITEM) { return false; } - const uiButTreeRow *treerow_but = (const uiButTreeRow *)but; - return UI_tree_view_item_is_active(treerow_but->tree_item); + const uiButViewItem *view_item_but = (const uiButViewItem *)but; + return UI_view_item_is_active(view_item_but->view_item); } -uiBut *ui_tree_row_find_active(const ARegion *region) +uiBut *ui_view_item_find_active(const ARegion *region) { - return ui_but_find(region, ui_but_is_active_treerow, nullptr); + return ui_but_find(region, ui_but_is_active_view_item, nullptr); } /** \} */ diff --git a/source/blender/editors/interface/interface_view.cc b/source/blender/editors/interface/interface_view.cc index 70728565263..b35f6d2c969 100644 --- a/source/blender/editors/interface/interface_view.cc +++ b/source/blender/editors/interface/interface_view.cc @@ -86,24 +86,24 @@ void UI_block_views_listen(const uiBlock *block, const wmRegionListenerParams *l } } -uiTreeViewItemHandle *UI_block_tree_view_find_item_at(const ARegion *region, const int xy[2]) +uiViewItemHandle *UI_block_view_find_item_at(const ARegion *region, const int xy[2]) { - uiButTreeRow *tree_row_but = (uiButTreeRow *)ui_tree_row_find_mouse_over(region, xy); - if (!tree_row_but) { + uiButViewItem *item_but = (uiButViewItem *)ui_view_item_find_mouse_over(region, xy); + if (!item_but) { return nullptr; } - return tree_row_but->tree_item; + return item_but->view_item; } -uiTreeViewItemHandle *UI_block_tree_view_find_active_item(const ARegion *region) +uiViewItemHandle *UI_block_view_find_active_item(const ARegion *region) { - uiButTreeRow *tree_row_but = (uiButTreeRow *)ui_tree_row_find_active(region); - if (!tree_row_but) { + uiButViewItem *item_but = (uiButViewItem *)ui_view_item_find_active(region); + if (!item_but) { return nullptr; } - return tree_row_but->tree_item; + return item_but->view_item; } static StringRef ui_block_view_find_idname(const uiBlock &block, const AbstractView &view) @@ -151,39 +151,38 @@ uiViewHandle *ui_block_view_find_matching_in_old_block(const uiBlock *new_block, return reinterpret_cast(old_view); } -uiButTreeRow *ui_block_view_find_treerow_in_old_block(const uiBlock *new_block, - const uiTreeViewItemHandle *new_item_handle) +uiButViewItem *ui_block_view_find_matching_view_item_but_in_old_block( + const uiBlock *new_block, const uiViewItemHandle *new_item_handle) { uiBlock *old_block = new_block->oldblock; if (!old_block) { return nullptr; } - const AbstractTreeViewItem &new_item = *reinterpret_cast( - new_item_handle); + const AbstractViewItem &new_item = *reinterpret_cast(new_item_handle); const AbstractView *old_view = ui_block_view_find_matching_in_old_block_impl( - *new_block, new_item.get_tree_view()); + *new_block, new_item.get_view()); if (!old_view) { return nullptr; } LISTBASE_FOREACH (uiBut *, old_but, &old_block->buttons) { - if (old_but->type != UI_BTYPE_TREEROW) { + if (old_but->type != UI_BTYPE_VIEW_ITEM) { continue; } - uiButTreeRow *old_treerow_but = (uiButTreeRow *)old_but; - if (!old_treerow_but->tree_item) { + uiButViewItem *old_item_but = (uiButViewItem *)old_but; + if (!old_item_but->view_item) { continue; } - AbstractTreeViewItem &old_item = *reinterpret_cast( - old_treerow_but->tree_item); - /* Check if the row is from the expected tree-view. */ - if (&old_item.get_tree_view() != old_view) { + AbstractViewItem &old_item = *reinterpret_cast(old_item_but->view_item); + /* Check if the item is from the expected view. */ + if (&old_item.get_view() != old_view) { continue; } - if (UI_tree_view_item_matches(new_item_handle, old_treerow_but->tree_item)) { - return old_treerow_but; + if (UI_view_item_matches(reinterpret_cast(&new_item), + reinterpret_cast(&old_item))) { + return old_item_but; } } diff --git a/source/blender/editors/interface/interface_widgets.c b/source/blender/editors/interface/interface_widgets.c index e2df2d77817..855e72788d2 100644 --- a/source/blender/editors/interface/interface_widgets.c +++ b/source/blender/editors/interface/interface_widgets.c @@ -104,8 +104,7 @@ typedef enum { UI_WTYPE_LISTITEM, UI_WTYPE_PROGRESSBAR, UI_WTYPE_NODESOCKET, - UI_WTYPE_TREEROW, - UI_WTYPE_GRID_TILE, + UI_WTYPE_VIEW_ITEM, } uiWidgetTypeEnum; /** @@ -3672,12 +3671,11 @@ static void widget_progressbar(uiBut *but, widgetbase_draw(&wtb_bar, wcol); } -static void widget_treerow_exec(uiWidgetColors *wcol, - rcti *rect, - const uiWidgetStateInfo *state, - int UNUSED(roundboxalign), - int indentation, - const float zoom) +static void widget_view_item(uiWidgetColors *wcol, + rcti *rect, + const uiWidgetStateInfo *state, + int UNUSED(roundboxalign), + const float zoom) { uiWidgetBase wtb; widget_init(&wtb); @@ -3690,31 +3688,6 @@ static void widget_treerow_exec(uiWidgetColors *wcol, if ((state->but_flag & UI_ACTIVE) || (state->but_flag & UI_SELECT)) { widgetbase_draw(&wtb, wcol); } - - BLI_rcti_resize(rect, BLI_rcti_size_x(rect) - UI_UNIT_X * indentation, BLI_rcti_size_y(rect)); - BLI_rcti_translate(rect, 0.5f * UI_UNIT_X * indentation, 0); -} - -static void widget_treerow(uiBut *but, - uiWidgetColors *wcol, - rcti *rect, - const uiWidgetStateInfo *state, - int roundboxalign, - const float zoom) -{ - uiButTreeRow *tree_row = (uiButTreeRow *)but; - BLI_assert(but->type == UI_BTYPE_TREEROW); - widget_treerow_exec(wcol, rect, state, roundboxalign, tree_row->indentation, zoom); -} - -static void widget_gridtile(uiWidgetColors *wcol, - rcti *rect, - const uiWidgetStateInfo *state, - int roundboxalign, - const float zoom) -{ - /* TODO Reuse tree-row drawing. */ - widget_treerow_exec(wcol, rect, state, roundboxalign, 0, zoom); } static void widget_nodesocket(uiBut *but, @@ -4608,14 +4581,9 @@ static uiWidgetType *widget_type(uiWidgetTypeEnum type) wt.custom = widget_progressbar; break; - case UI_WTYPE_TREEROW: - wt.wcol_theme = &btheme->tui.wcol_view_item; - wt.custom = widget_treerow; - break; - - case UI_WTYPE_GRID_TILE: + case UI_WTYPE_VIEW_ITEM: wt.wcol_theme = &btheme->tui.wcol_view_item; - wt.draw = widget_gridtile; + wt.draw = widget_view_item; break; case UI_WTYPE_NODESOCKET: @@ -4949,13 +4917,8 @@ void ui_draw_but(const bContext *C, struct ARegion *region, uiStyle *style, uiBu fstyle = &style->widgetlabel; break; - case UI_BTYPE_TREEROW: - wt = widget_type(UI_WTYPE_TREEROW); - fstyle = &style->widgetlabel; - break; - - case UI_BTYPE_GRID_TILE: - wt = widget_type(UI_WTYPE_GRID_TILE); + case UI_BTYPE_VIEW_ITEM: + wt = widget_type(UI_WTYPE_VIEW_ITEM); fstyle = &style->widgetlabel; break; diff --git a/source/blender/editors/interface/tree_view.cc b/source/blender/editors/interface/tree_view.cc index ec1140e8efb..c224226ba17 100644 --- a/source/blender/editors/interface/tree_view.cc +++ b/source/blender/editors/interface/tree_view.cc @@ -37,9 +37,11 @@ AbstractTreeViewItem &TreeViewItemContainer::add_tree_item( if (root_ == nullptr) { root_ = this; } - + AbstractTreeView &tree_view = static_cast(*root_); AbstractTreeViewItem &added_item = *children_.last(); added_item.root_ = root_; + tree_view.register_item(added_item); + if (root_ != this) { /* Any item that isn't the root can be assumed to the a #AbstractTreeViewItem. Not entirely * nice to static_cast this, but well... */ @@ -95,7 +97,7 @@ AbstractTreeViewItem *AbstractTreeView::find_matching_child( const AbstractTreeViewItem &lookup_item, const TreeViewOrItem &items) { for (const auto &iter_item : items.children_) { - if (lookup_item.matches(*iter_item)) { + if (lookup_item.matches_single(*iter_item)) { /* We have a matching item! */ return iter_item.get(); } @@ -118,9 +120,8 @@ void AbstractTreeViewItem::tree_row_click_fn(struct bContext * /*C*/, void *but_arg1, void * /*arg2*/) { - uiButTreeRow *tree_row_but = (uiButTreeRow *)but_arg1; - AbstractTreeViewItem &tree_item = reinterpret_cast( - *tree_row_but->tree_item); + uiButViewItem *item_but = (uiButViewItem *)but_arg1; + AbstractTreeViewItem &tree_item = reinterpret_cast(*item_but->view_item); tree_item.activate(); /* Not only activate the item, also show its children. Maybe this should be optional, or @@ -131,11 +132,11 @@ void AbstractTreeViewItem::tree_row_click_fn(struct bContext * /*C*/, void AbstractTreeViewItem::add_treerow_button(uiBlock &block) { /* For some reason a width > (UI_UNIT_X * 2) make the layout system use all available width. */ - tree_row_but_ = (uiButTreeRow *)uiDefBut( - &block, UI_BTYPE_TREEROW, 0, "", 0, 0, UI_UNIT_X * 10, UI_UNIT_Y, nullptr, 0, 0, 0, 0, ""); + view_item_but_ = (uiButViewItem *)uiDefBut( + &block, UI_BTYPE_VIEW_ITEM, 0, "", 0, 0, UI_UNIT_X * 10, UI_UNIT_Y, nullptr, 0, 0, 0, 0, ""); - tree_row_but_->tree_item = reinterpret_cast(this); - UI_but_func_set(&tree_row_but_->but, tree_row_click_fn, tree_row_but_, nullptr); + view_item_but_->view_item = reinterpret_cast(this); + UI_but_func_set(&view_item_but_->but, tree_row_click_fn, view_item_but_, nullptr); } void AbstractTreeViewItem::add_indent(uiLayout &row) const @@ -167,10 +168,9 @@ void AbstractTreeViewItem::collapse_chevron_click_fn(struct bContext *C, const wmWindow *win = CTX_wm_window(C); const ARegion *region = CTX_wm_region(C); - uiTreeViewItemHandle *hovered_item_handle = UI_block_tree_view_find_item_at(region, - win->eventstate->xy); - AbstractTreeViewItem *hovered_item = reinterpret_cast( - hovered_item_handle); + uiViewItemHandle *hovered_item_handle = UI_block_view_find_item_at(region, win->eventstate->xy); + + AbstractTreeViewItem *hovered_item = from_item_handle(hovered_item_handle); BLI_assert(hovered_item != nullptr); hovered_item->toggle_collapsed(); @@ -204,40 +204,6 @@ void AbstractTreeViewItem::add_collapse_chevron(uiBlock &block) const BLI_assert(is_collapse_chevron_but(but)); } -AbstractTreeViewItem *AbstractTreeViewItem::find_tree_item_from_rename_button( - const uiBut &rename_but) -{ - /* A minimal sanity check, can't do much more here. */ - BLI_assert(rename_but.type == UI_BTYPE_TEXT && rename_but.poin); - - LISTBASE_FOREACH (uiBut *, but, &rename_but.block->buttons) { - if (but->type != UI_BTYPE_TREEROW) { - continue; - } - - uiButTreeRow *tree_row_but = (uiButTreeRow *)but; - AbstractTreeViewItem *item = reinterpret_cast(tree_row_but->tree_item); - const AbstractTreeView &tree_view = item->get_tree_view(); - - if (item->is_renaming() && (tree_view.get_rename_buffer().data() == rename_but.poin)) { - return item; - } - } - - return nullptr; -} - -void AbstractTreeViewItem::rename_button_fn(bContext *UNUSED(C), void *arg, char *UNUSED(origstr)) -{ - const uiBut *rename_but = static_cast(arg); - AbstractTreeViewItem *item = find_tree_item_from_rename_button(*rename_but); - BLI_assert(item); - - const AbstractTreeView &tree_view = item->get_tree_view(); - item->rename(tree_view.get_rename_buffer().data()); - item->end_renaming(); -} - void AbstractTreeViewItem::add_rename_button(uiLayout &row) { uiBlock *block = uiLayoutGetBlock(&row); @@ -247,33 +213,7 @@ void AbstractTreeViewItem::add_rename_button(uiLayout &row) /* Enable emboss for the text button. */ UI_block_emboss_set(block, UI_EMBOSS); - AbstractTreeView &tree_view = get_tree_view(); - uiBut *rename_but = uiDefBut(block, - UI_BTYPE_TEXT, - 1, - "", - 0, - 0, - UI_UNIT_X * 10, - UI_UNIT_Y, - tree_view.get_rename_buffer().data(), - 1.0f, - tree_view.get_rename_buffer().size(), - 0, - 0, - ""); - - /* Gotta be careful with what's passed to the `arg1` here. Any tree data will be freed once the - * callback is executed. */ - UI_but_func_rename_set(rename_but, AbstractTreeViewItem::rename_button_fn, rename_but); - UI_but_flag_disable(rename_but, UI_BUT_UNDO); - - const bContext *evil_C = static_cast(block->evil_C); - ARegion *region = CTX_wm_region(evil_C); - /* Returns false if the button was removed. */ - if (UI_but_active_only(evil_C, region, block, rename_but) == false) { - end_renaming(); - } + AbstractViewItem::add_rename_button(*block); UI_block_emboss_set(block, previous_emboss); UI_block_layout_set_current(block, &row); @@ -306,82 +246,35 @@ bool AbstractTreeViewItem::supports_collapsing() const return true; } -std::unique_ptr AbstractTreeViewItem::create_drag_controller() - const -{ - /* There's no drag controller (and hence no drag support) by default. */ - return nullptr; -} - -std::unique_ptr AbstractTreeViewItem::create_drop_controller() - const -{ - /* There's no drop controller (and hence no drop support) by default. */ - return nullptr; -} - -bool AbstractTreeViewItem::supports_renaming() const +StringRef AbstractTreeViewItem::get_rename_string() const { - /* No renaming by default. */ - return false; + return label_; } bool AbstractTreeViewItem::rename(StringRefNull new_name) { - /* It is important to update the label after renaming, so #AbstractTreeViewItem::matches() + /* It is important to update the label after renaming, so #AbstractTreeViewItem::matches_single() * recognizes the item. (It only compares labels by default.) */ label_ = new_name; return true; } -void AbstractTreeViewItem::build_context_menu(bContext & /*C*/, uiLayout & /*column*/) const -{ - /* No context menu by default. */ -} - void AbstractTreeViewItem::update_from_old(const AbstractViewItem &old) { AbstractViewItem::update_from_old(old); const AbstractTreeViewItem &old_tree_item = dynamic_cast(old); is_open_ = old_tree_item.is_open_; - is_renaming_ = old_tree_item.is_renaming_; } -bool AbstractTreeViewItem::matches(const AbstractTreeViewItem &other) const +bool AbstractTreeViewItem::matches_single(const AbstractTreeViewItem &other) const { return label_ == other.label_; } -void AbstractTreeViewItem::begin_renaming() -{ - AbstractTreeView &tree_view = get_tree_view(); - if (tree_view.is_renaming() || !supports_renaming()) { - return; - } - - if (tree_view.begin_renaming()) { - is_renaming_ = true; - } - - std::copy(std::begin(label_), std::end(label_), std::begin(tree_view.get_rename_buffer())); -} - -void AbstractTreeViewItem::end_renaming() -{ - if (!is_renaming()) { - return; - } - - is_renaming_ = false; - - AbstractTreeView &tree_view = get_tree_view(); - tree_view.end_renaming(); -} - AbstractTreeView &AbstractTreeViewItem::get_tree_view() const { - return static_cast(*root_); + return dynamic_cast(get_view()); } int AbstractTreeViewItem::count_parents() const @@ -417,26 +310,19 @@ void AbstractTreeViewItem::deactivate() is_active_ = false; } -bool AbstractTreeViewItem::is_active() const -{ - BLI_assert_msg(get_tree_view().is_reconstructed(), - "State can't be queried until reconstruction is completed"); - return is_active_; -} - bool AbstractTreeViewItem::is_hovered() const { BLI_assert_msg(get_tree_view().is_reconstructed(), "State can't be queried until reconstruction is completed"); - BLI_assert_msg(tree_row_but_ != nullptr, + BLI_assert_msg(view_item_but_ != nullptr, "Hovered state can't be queried before the tree row is being built"); - const uiTreeViewItemHandle *this_handle = reinterpret_cast(this); + const uiViewItemHandle *this_item_handle = reinterpret_cast(this); /* The new layout hasn't finished construction yet, so the final state of the button is unknown. * Get the matching button from the previous redraw instead. */ - uiButTreeRow *old_treerow_but = ui_block_view_find_treerow_in_old_block(tree_row_but_->but.block, - this_handle); - return old_treerow_but && (old_treerow_but->but.flag & UI_ACTIVE); + uiButViewItem *old_item_but = ui_block_view_find_matching_view_item_but_in_old_block( + view_item_but_->but.block, this_item_handle); + return old_item_but && (old_item_but->but.flag & UI_ACTIVE); } bool AbstractTreeViewItem::is_collapsed() const @@ -464,11 +350,6 @@ bool AbstractTreeViewItem::is_collapsible() const return this->supports_collapsing(); } -bool AbstractTreeViewItem::is_renaming() const -{ - return is_renaming_; -} - void AbstractTreeViewItem::ensure_parents_uncollapsed() { for (AbstractTreeViewItem *parent = parent_; parent; parent = parent->parent_) { @@ -476,19 +357,21 @@ void AbstractTreeViewItem::ensure_parents_uncollapsed() } } -bool AbstractTreeViewItem::matches_including_parents(const AbstractTreeViewItem &other) const +bool AbstractTreeViewItem::matches(const AbstractViewItem &other) const { - if (!matches(other)) { + const AbstractTreeViewItem &other_tree_item = dynamic_cast(other); + + if (!matches_single(other_tree_item)) { return false; } - if (count_parents() != other.count_parents()) { + if (count_parents() != other_tree_item.count_parents()) { return false; } - for (AbstractTreeViewItem *parent = parent_, *other_parent = other.parent_; + for (AbstractTreeViewItem *parent = parent_, *other_parent = other_tree_item.parent_; parent && other_parent; parent = parent->parent_, other_parent = other_parent->parent_) { - if (!parent->matches(*other_parent)) { + if (!parent->matches_single(*other_parent)) { return false; } } @@ -496,9 +379,9 @@ bool AbstractTreeViewItem::matches_including_parents(const AbstractTreeViewItem return true; } -uiButTreeRow *AbstractTreeViewItem::tree_row_button() +uiButViewItem *AbstractTreeViewItem::view_item_button() { - return tree_row_but_; + return view_item_but_; } void AbstractTreeViewItem::change_state_delayed() @@ -511,25 +394,6 @@ void AbstractTreeViewItem::change_state_delayed() /* ---------------------------------------------------------------------- */ -AbstractTreeViewItemDragController::AbstractTreeViewItemDragController(AbstractTreeView &tree_view) - : tree_view_(tree_view) -{ -} - -void AbstractTreeViewItemDragController::on_drag_start() -{ - /* Do nothing by default. */ -} - -/* ---------------------------------------------------------------------- */ - -AbstractTreeViewItemDropController::AbstractTreeViewItemDropController(AbstractTreeView &tree_view) - : tree_view_(tree_view) -{ -} - -/* ---------------------------------------------------------------------- */ - class TreeViewLayoutBuilder { uiBlock &block_; @@ -575,7 +439,7 @@ void TreeViewLayoutBuilder::polish_layout(const uiBlock &block) UI_but_drawflag_enable(static_cast(but->next), UI_BUT_NO_TEXT_PADDING); } - if (but->type == UI_BTYPE_TREEROW) { + if (but->type == UI_BTYPE_VIEW_ITEM) { break; } } @@ -687,154 +551,4 @@ std::optional BasicTreeViewItem::should_be_active() const return std::nullopt; } -/* ---------------------------------------------------------------------- */ - -/** - * Helper for a public (C-)API, presenting higher level functionality. Has access to internal - * data/functionality (friend of #AbstractTreeViewItem), which is sometimes needed when - * functionality of the API needs to be constructed from multiple internal conditions and/or - * functions that on their own shouldn't be part of the API. - */ -class TreeViewItemAPIWrapper { - public: - static bool matches(const AbstractTreeViewItem &a, const AbstractTreeViewItem &b) - { - /* TODO should match the tree-view as well. */ - return a.matches_including_parents(b); - } - - static bool drag_start(bContext &C, const AbstractTreeViewItem &item) - { - const std::unique_ptr drag_controller = - item.create_drag_controller(); - if (!drag_controller) { - return false; - } - - WM_event_start_drag(&C, - ICON_NONE, - drag_controller->get_drag_type(), - drag_controller->create_drag_data(), - 0, - WM_DRAG_FREE_DATA); - drag_controller->on_drag_start(); - - return true; - } - - static bool can_drop(const AbstractTreeViewItem &item, - const wmDrag &drag, - const char **r_disabled_hint) - { - const std::unique_ptr drop_controller = - item.create_drop_controller(); - if (!drop_controller) { - return false; - } - - return drop_controller->can_drop(drag, r_disabled_hint); - } - - static std::string drop_tooltip(const AbstractTreeViewItem &item, const wmDrag &drag) - { - const std::unique_ptr drop_controller = - item.create_drop_controller(); - if (!drop_controller) { - return {}; - } - - return drop_controller->drop_tooltip(drag); - } - - static bool drop_handle(bContext &C, const AbstractTreeViewItem &item, const ListBase &drags) - { - std::unique_ptr drop_controller = - item.create_drop_controller(); - - const char *disabled_hint_dummy = nullptr; - LISTBASE_FOREACH (const wmDrag *, drag, &drags) { - if (drop_controller->can_drop(*drag, &disabled_hint_dummy)) { - return drop_controller->on_drop(&C, *drag); - } - } - - return false; - } - - static bool can_rename(const AbstractTreeViewItem &item) - { - const AbstractTreeView &tree_view = item.get_tree_view(); - return !tree_view.is_renaming() && item.supports_renaming(); - } -}; - } // namespace blender::ui - -/* ---------------------------------------------------------------------- */ -/* C-API */ - -using namespace blender::ui; - -bool UI_tree_view_item_is_active(const uiTreeViewItemHandle *item_handle) -{ - const AbstractTreeViewItem &item = reinterpret_cast(*item_handle); - return item.is_active(); -} - -bool UI_tree_view_item_matches(const uiTreeViewItemHandle *a_handle, - const uiTreeViewItemHandle *b_handle) -{ - const AbstractTreeViewItem &a = reinterpret_cast(*a_handle); - const AbstractTreeViewItem &b = reinterpret_cast(*b_handle); - return TreeViewItemAPIWrapper::matches(a, b); -} - -bool UI_tree_view_item_drag_start(bContext *C, uiTreeViewItemHandle *item_) -{ - const AbstractTreeViewItem &item = reinterpret_cast(*item_); - return TreeViewItemAPIWrapper::drag_start(*C, item); -} - -bool UI_tree_view_item_can_drop(const uiTreeViewItemHandle *item_, - const wmDrag *drag, - const char **r_disabled_hint) -{ - const AbstractTreeViewItem &item = reinterpret_cast(*item_); - return TreeViewItemAPIWrapper::can_drop(item, *drag, r_disabled_hint); -} - -char *UI_tree_view_item_drop_tooltip(const uiTreeViewItemHandle *item_, const wmDrag *drag) -{ - const AbstractTreeViewItem &item = reinterpret_cast(*item_); - - const std::string tooltip = TreeViewItemAPIWrapper::drop_tooltip(item, *drag); - return tooltip.empty() ? nullptr : BLI_strdup(tooltip.c_str()); -} - -bool UI_tree_view_item_drop_handle(bContext *C, - const uiTreeViewItemHandle *item_, - const ListBase *drags) -{ - const AbstractTreeViewItem &item = reinterpret_cast(*item_); - return TreeViewItemAPIWrapper::drop_handle(*C, item, *drags); -} - -bool UI_tree_view_item_can_rename(const uiTreeViewItemHandle *item_handle) -{ - const AbstractTreeViewItem &item = reinterpret_cast(*item_handle); - return TreeViewItemAPIWrapper::can_rename(item); -} - -void UI_tree_view_item_begin_rename(uiTreeViewItemHandle *item_handle) -{ - AbstractTreeViewItem &item = reinterpret_cast(*item_handle); - item.begin_renaming(); -} - -void UI_tree_view_item_context_menu_build(bContext *C, - const uiTreeViewItemHandle *item_handle, - uiLayout *column) -{ - const AbstractTreeViewItem &item = reinterpret_cast(*item_handle); - item.build_context_menu(*C, *column); -} diff --git a/source/blender/editors/space_file/asset_catalog_tree_view.cc b/source/blender/editors/space_file/asset_catalog_tree_view.cc index 87595ecdb88..1829f19bfd6 100644 --- a/source/blender/editors/space_file/asset_catalog_tree_view.cc +++ b/source/blender/editors/space_file/asset_catalog_tree_view.cc @@ -86,12 +86,12 @@ class AssetCatalogTreeViewItem : public ui::BasicTreeViewItem { bool rename(StringRefNull new_name) override; /** Add drag support for catalog items. */ - std::unique_ptr create_drag_controller() const override; + std::unique_ptr create_drag_controller() const override; /** Add dropping support for catalog items. */ - std::unique_ptr create_drop_controller() const override; + std::unique_ptr create_drop_controller() const override; }; -class AssetCatalogDragController : public ui::AbstractTreeViewItemDragController { +class AssetCatalogDragController : public ui::AbstractViewItemDragController { AssetCatalogTreeItem &catalog_item_; public: @@ -103,7 +103,7 @@ class AssetCatalogDragController : public ui::AbstractTreeViewItemDragController void on_drag_start() override; }; -class AssetCatalogDropController : public ui::AbstractTreeViewItemDropController { +class AssetCatalogDropController : public ui::AbstractViewItemDropController { AssetCatalogTreeItem &catalog_item_; public: @@ -142,7 +142,7 @@ class AssetCatalogTreeViewAllItem : public ui::BasicTreeViewItem { void build_row(uiLayout &row) override; - struct DropController : public ui::AbstractTreeViewItemDropController { + struct DropController : public ui::AbstractViewItemDropController { DropController(AssetCatalogTreeView &tree_view); bool can_drop(const wmDrag &drag, const char **r_disabled_hint) const override; @@ -150,13 +150,13 @@ class AssetCatalogTreeViewAllItem : public ui::BasicTreeViewItem { bool on_drop(struct bContext *C, const wmDrag &drag) override; }; - std::unique_ptr create_drop_controller() const override; + std::unique_ptr create_drop_controller() const override; }; class AssetCatalogTreeViewUnassignedItem : public ui::BasicTreeViewItem { using BasicTreeViewItem::BasicTreeViewItem; - struct DropController : public ui::AbstractTreeViewItemDropController { + struct DropController : public ui::AbstractViewItemDropController { DropController(AssetCatalogTreeView &tree_view); bool can_drop(const wmDrag &drag, const char **r_disabled_hint) const override; @@ -164,7 +164,7 @@ class AssetCatalogTreeViewUnassignedItem : public ui::BasicTreeViewItem { bool on_drop(struct bContext *C, const wmDrag &drag) override; }; - std::unique_ptr create_drop_controller() const override; + std::unique_ptr create_drop_controller() const override; }; /* ---------------------------------------------------------------------- */ @@ -272,11 +272,11 @@ void AssetCatalogTreeViewItem::build_row(uiLayout &row) return; } - uiButTreeRow *tree_row_but = tree_row_button(); + uiButViewItem *view_item_but = view_item_button(); PointerRNA *props; props = UI_but_extra_operator_icon_add( - (uiBut *)tree_row_but, "ASSET_OT_catalog_new", WM_OP_INVOKE_DEFAULT, ICON_ADD); + (uiBut *)view_item_but, "ASSET_OT_catalog_new", WM_OP_INVOKE_DEFAULT, ICON_ADD); RNA_string_set(props, "parent_path", catalog_item_.catalog_path().c_str()); } @@ -305,7 +305,7 @@ void AssetCatalogTreeViewItem::build_context_menu(bContext &C, uiLayout &column) 0, &props); RNA_string_set(&props, "catalog_id", catalog_id_str_buffer); - uiItemO(&column, "Rename", ICON_NONE, "UI_OT_tree_view_item_rename"); + uiItemO(&column, "Rename", ICON_NONE, "UI_OT_view_item_rename"); /* Doesn't actually exist right now, but could be defined in Python. Reason that this isn't done * in Python yet is that catalogs are not exposed in BPY, and we'd somehow pass the clicked on @@ -333,14 +333,14 @@ bool AssetCatalogTreeViewItem::rename(StringRefNull new_name) return true; } -std::unique_ptr AssetCatalogTreeViewItem:: +std::unique_ptr AssetCatalogTreeViewItem:: create_drop_controller() const { return std::make_unique( static_cast(get_tree_view()), catalog_item_); } -std::unique_ptr AssetCatalogTreeViewItem:: +std::unique_ptr AssetCatalogTreeViewItem:: create_drag_controller() const { return std::make_unique( @@ -351,7 +351,7 @@ std::unique_ptr AssetCatalogTreeViewItem AssetCatalogDropController::AssetCatalogDropController(AssetCatalogTreeView &tree_view, AssetCatalogTreeItem &catalog_item) - : ui::AbstractTreeViewItemDropController(tree_view), catalog_item_(catalog_item) + : ui::AbstractViewItemDropController(tree_view), catalog_item_(catalog_item) { } @@ -422,10 +422,10 @@ bool AssetCatalogDropController::on_drop(struct bContext *C, const wmDrag &drag) { if (drag.type == WM_DRAG_ASSET_CATALOG) { return drop_asset_catalog_into_catalog( - drag, tree_view(), catalog_item_.get_catalog_id()); + drag, get_view(), catalog_item_.get_catalog_id()); } return drop_assets_into_catalog(C, - tree_view(), + get_view(), drag, catalog_item_.get_catalog_id(), catalog_item_.get_simple_name()); @@ -512,14 +512,14 @@ bool AssetCatalogDropController::has_droppable_asset(const wmDrag &drag, ::AssetLibrary &AssetCatalogDropController::get_asset_library() const { - return *tree_view().asset_library_; + return *get_view().asset_library_; } /* ---------------------------------------------------------------------- */ AssetCatalogDragController::AssetCatalogDragController(AssetCatalogTreeView &tree_view, AssetCatalogTreeItem &catalog_item) - : ui::AbstractTreeViewItemDragController(tree_view), catalog_item_(catalog_item) + : ui::AbstractViewItemDragController(tree_view), catalog_item_(catalog_item) { } @@ -538,7 +538,7 @@ void *AssetCatalogDragController::create_drag_data() const void AssetCatalogDragController::on_drag_start() { - AssetCatalogTreeView &tree_view_ = tree_view(); + AssetCatalogTreeView &tree_view_ = get_view(); tree_view_.activate_catalog_by_id(catalog_item_.get_catalog_id()); } @@ -551,15 +551,15 @@ void AssetCatalogTreeViewAllItem::build_row(uiLayout &row) PointerRNA *props; UI_but_extra_operator_icon_add( - (uiBut *)tree_row_button(), "ASSET_OT_catalogs_save", WM_OP_INVOKE_DEFAULT, ICON_FILE_TICK); + (uiBut *)view_item_button(), "ASSET_OT_catalogs_save", WM_OP_INVOKE_DEFAULT, ICON_FILE_TICK); props = UI_but_extra_operator_icon_add( - (uiBut *)tree_row_button(), "ASSET_OT_catalog_new", WM_OP_INVOKE_DEFAULT, ICON_ADD); + (uiBut *)view_item_button(), "ASSET_OT_catalog_new", WM_OP_INVOKE_DEFAULT, ICON_ADD); /* No parent path to use the root level. */ RNA_string_set(props, "parent_path", nullptr); } -std::unique_ptr AssetCatalogTreeViewAllItem:: +std::unique_ptr AssetCatalogTreeViewAllItem:: create_drop_controller() const { return std::make_unique( @@ -567,7 +567,7 @@ std::unique_ptr AssetCatalogTreeViewAllI } AssetCatalogTreeViewAllItem::DropController::DropController(AssetCatalogTreeView &tree_view) - : ui::AbstractTreeViewItemDropController(tree_view) + : ui::AbstractViewItemDropController(tree_view) { } @@ -579,7 +579,7 @@ bool AssetCatalogTreeViewAllItem::DropController::can_drop(const wmDrag &drag, } const AssetCatalog *drag_catalog = AssetCatalogDropController::get_drag_catalog( - drag, *tree_view().asset_library_); + drag, *get_view().asset_library_); if (drag_catalog->path.parent() == "") { *r_disabled_hint = "Catalog is already placed at the highest level"; return false; @@ -592,7 +592,7 @@ std::string AssetCatalogTreeViewAllItem::DropController::drop_tooltip(const wmDr { BLI_assert(drag.type == WM_DRAG_ASSET_CATALOG); const AssetCatalog *drag_catalog = AssetCatalogDropController::get_drag_catalog( - drag, *tree_view().asset_library_); + drag, *get_view().asset_library_); return std::string(TIP_("Move Catalog")) + " '" + drag_catalog->path.name() + "' " + TIP_("to the top level of the tree"); @@ -604,14 +604,14 @@ bool AssetCatalogTreeViewAllItem::DropController::on_drop(struct bContext *UNUSE BLI_assert(drag.type == WM_DRAG_ASSET_CATALOG); return AssetCatalogDropController::drop_asset_catalog_into_catalog( drag, - tree_view(), + get_view(), /* No value to drop into the root level. */ std::nullopt); } /* ---------------------------------------------------------------------- */ -std::unique_ptr AssetCatalogTreeViewUnassignedItem:: +std::unique_ptr AssetCatalogTreeViewUnassignedItem:: create_drop_controller() const { return std::make_unique( @@ -619,7 +619,7 @@ std::unique_ptr AssetCatalogTreeViewUnas } AssetCatalogTreeViewUnassignedItem::DropController::DropController(AssetCatalogTreeView &tree_view) - : ui::AbstractTreeViewItemDropController(tree_view) + : ui::AbstractViewItemDropController(tree_view) { } @@ -647,7 +647,7 @@ bool AssetCatalogTreeViewUnassignedItem::DropController::on_drop(struct bContext { /* Assign to nil catalog ID. */ return AssetCatalogDropController::drop_assets_into_catalog( - C, tree_view(), drag, CatalogID{}); + C, get_view(), drag, CatalogID{}); } } // namespace blender::ed::asset_browser diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_dataset_draw.cc b/source/blender/editors/space_spreadsheet/spreadsheet_dataset_draw.cc index aa9b867264a..146b6091bde 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_dataset_draw.cc +++ b/source/blender/editors/space_spreadsheet/spreadsheet_dataset_draw.cc @@ -145,7 +145,7 @@ void GeometryDataSetTreeViewItem::build_row(uiLayout &row) * to the right side of the number, which it didn't have with the button. */ char element_count[7]; BLI_str_format_decimal_unit(element_count, *count); - UI_but_hint_drawstr_set((uiBut *)this->tree_row_button(), element_count); + UI_but_hint_drawstr_set((uiBut *)this->view_item_button(), element_count); } } -- cgit v1.2.3 From 2e3fb581280d637d92eb639b90e82f71e7f37ca3 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Tue, 19 Jul 2022 16:30:46 +0200 Subject: Cleanup: Apply clang-format --- source/blender/blenkernel/intern/camera.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/source/blender/blenkernel/intern/camera.c b/source/blender/blenkernel/intern/camera.c index 2e6439f7e1a..9aea3b2768f 100644 --- a/source/blender/blenkernel/intern/camera.c +++ b/source/blender/blenkernel/intern/camera.c @@ -25,8 +25,8 @@ #include "BLI_string.h" #include "BLI_utildefines.h" -#include "BKE_anim_data.h" #include "BKE_action.h" +#include "BKE_anim_data.h" #include "BKE_camera.h" #include "BKE_idtype.h" #include "BKE_layer.h" @@ -222,7 +222,8 @@ float BKE_camera_object_dof_distance(const Object *ob) if (cam->dof.focus_object) { float view_dir[3], dof_dir[3]; normalize_v3_v3(view_dir, ob->obmat[2]); - bPoseChannel *pchan = BKE_pose_channel_find_name(cam->dof.focus_object->pose, cam->dof.focus_subtarget); + bPoseChannel *pchan = BKE_pose_channel_find_name(cam->dof.focus_object->pose, + cam->dof.focus_subtarget); if (pchan) { float posemat[4][4]; mul_m4_m4m4(posemat, cam->dof.focus_object->obmat, pchan->pose_mat); -- cgit v1.2.3 From ad5e3d30a2d2d21771140c9d877e1d7dedd560f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cle=CC=81ment=20Foucault?= Date: Tue, 19 Jul 2022 16:35:53 +0200 Subject: Nishita sky: Increase elevation range MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Nishita sky texture currently only allows moving the sun to the zenith. The problem is if you want to animate the passing of a full night-day-night cycle. Currently it's not easy to do due to this limitation. The patch makes it so users can easily animate the sun moving from sunrise to sunset by expanding the max sun elevation to go 360° instead of 90°. Reviewed By: fclem Differential Revision: https://developer.blender.org/D13724 --- source/blender/makesrna/intern/rna_nodetree.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c index 386ef3f74a3..80217decb13 100644 --- a/source/blender/makesrna/intern/rna_nodetree.c +++ b/source/blender/makesrna/intern/rna_nodetree.c @@ -5282,7 +5282,7 @@ static void def_sh_tex_sky(StructRNA *srna) prop = RNA_def_property(srna, "sun_elevation", PROP_FLOAT, PROP_ANGLE); RNA_def_property_ui_text(prop, "Sun Elevation", "Sun angle from horizon"); - RNA_def_property_range(prop, -M_PI_2, M_PI_2); + RNA_def_property_ui_range(prop, -M_PI, M_PI, 1, 2); RNA_def_property_float_default(prop, M_PI_2); RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); -- cgit v1.2.3 From 3370c1a8a7b24cf542e368e7ae34dd0064a2d963 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Tue, 19 Jul 2022 09:54:20 -0500 Subject: Cleanup: Add comment for geometry nodes lazyness --- source/blender/blenkernel/BKE_node.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index b7962ade312..ea43fba1a85 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -314,6 +314,10 @@ typedef struct bNodeType { /* Execute a geometry node. */ NodeGeometryExecFunction geometry_node_execute; + /** + * If true, the geometry nodes evaluator can call the execute function multiple times to improve + * performance by specifying required data in one call and using it for calculations in another. + */ bool geometry_node_execute_supports_laziness; /* Declares which sockets the node has. */ -- cgit v1.2.3 From 6bba4d864e13e37b0c95efbf5dab4edb75d110a3 Mon Sep 17 00:00:00 2001 From: Jason Fielder Date: Tue, 19 Jul 2022 16:59:42 +0200 Subject: Metal: MTLQueryPool implementation adding support for occlusion queries. When a query begins, the current visibility result buffer needs to be associated with the currently active Render Pass. The MTLContext and MTLCommandBuffer are responsible for ensuring new render pass objects are created if the visibility state changes. Authored by Apple: Michael Parkin-White Ref T96261 Reviewed By: fclem Maniphest Tasks: T96261 Differential Revision: https://developer.blender.org/D15356 --- source/blender/gpu/CMakeLists.txt | 2 + source/blender/gpu/metal/mtl_backend.mm | 4 +- source/blender/gpu/metal/mtl_command_buffer.mm | 6 ++ source/blender/gpu/metal/mtl_context.hh | 18 ++++ source/blender/gpu/metal/mtl_context.mm | 42 ++++++++- source/blender/gpu/metal/mtl_query.hh | 39 ++++++++ source/blender/gpu/metal/mtl_query.mm | 120 +++++++++++++++++++++++++ 7 files changed, 228 insertions(+), 3 deletions(-) create mode 100644 source/blender/gpu/metal/mtl_query.hh create mode 100644 source/blender/gpu/metal/mtl_query.mm diff --git a/source/blender/gpu/CMakeLists.txt b/source/blender/gpu/CMakeLists.txt index 774f2a0f312..5a1816c5829 100644 --- a/source/blender/gpu/CMakeLists.txt +++ b/source/blender/gpu/CMakeLists.txt @@ -195,6 +195,7 @@ set(METAL_SRC metal/mtl_debug.mm metal/mtl_framebuffer.mm metal/mtl_memory.mm + metal/mtl_query.mm metal/mtl_state.mm metal/mtl_texture.mm metal/mtl_texture_util.mm @@ -206,6 +207,7 @@ set(METAL_SRC metal/mtl_debug.hh metal/mtl_framebuffer.hh metal/mtl_memory.hh + metal/mtl_query.hh metal/mtl_state.hh metal/mtl_texture.hh ) diff --git a/source/blender/gpu/metal/mtl_backend.mm b/source/blender/gpu/metal/mtl_backend.mm index d2524cc16e3..af1fd6a06bb 100644 --- a/source/blender/gpu/metal/mtl_backend.mm +++ b/source/blender/gpu/metal/mtl_backend.mm @@ -10,6 +10,7 @@ #include "mtl_backend.hh" #include "mtl_context.hh" #include "mtl_framebuffer.hh" +#include "mtl_query.hh" #include "gpu_capabilities_private.hh" #include "gpu_platform_private.hh" @@ -64,8 +65,7 @@ IndexBuf *MTLBackend::indexbuf_alloc() QueryPool *MTLBackend::querypool_alloc() { - /* TODO(Metal): Implement MTLQueryPool. */ - return nullptr; + return new MTLQueryPool(); }; Shader *MTLBackend::shader_alloc(const char *name) diff --git a/source/blender/gpu/metal/mtl_command_buffer.mm b/source/blender/gpu/metal/mtl_command_buffer.mm index 7b6066b3d86..9a9a2d55103 100644 --- a/source/blender/gpu/metal/mtl_command_buffer.mm +++ b/source/blender/gpu/metal/mtl_command_buffer.mm @@ -308,6 +308,12 @@ id MTLCommandBufferManager::ensure_begin_render_command active_pass_descriptor_ = active_frame_buffer_->bake_render_pass_descriptor( is_rebind && (!active_frame_buffer_->get_pending_clear())); + /* Determine if there is a visibility buffer assigned to the context. */ + gpu::MTLBuffer *visibility_buffer = context_.get_visibility_buffer(); + this->active_pass_descriptor_.visibilityResultBuffer = + (visibility_buffer) ? visibility_buffer->get_metal_buffer() : nil; + context_.clear_visibility_dirty(); + /* Ensure we have already cleaned up our previous render command encoder. */ BLI_assert(active_render_command_encoder_ == nil); diff --git a/source/blender/gpu/metal/mtl_context.hh b/source/blender/gpu/metal/mtl_context.hh index 4b87b994a3d..caabd59a1b5 100644 --- a/source/blender/gpu/metal/mtl_context.hh +++ b/source/blender/gpu/metal/mtl_context.hh @@ -3,6 +3,8 @@ /** \file * \ingroup gpu */ +#pragma once + #include "MEM_guardedalloc.h" #include "gpu_context_private.hh" @@ -588,6 +590,10 @@ class MTLContext : public Context { bool is_inside_frame_ = false; uint current_frame_index_; + /* Visibility buffer for MTLQuery results. */ + gpu::MTLBuffer *visibility_buffer_ = nullptr; + bool visibility_is_dirty_ = false; + public: /* Shaders and Pipeline state. */ MTLContextGlobalShaderPipelineState pipeline_state; @@ -660,6 +666,18 @@ class MTLContext : public Context { void set_scissor(int scissor_x, int scissor_y, int scissor_width, int scissor_height); void set_scissor_enabled(bool scissor_enabled); + /* Visibility buffer control. */ + void set_visibility_buffer(gpu::MTLBuffer *buffer); + gpu::MTLBuffer *get_visibility_buffer() const; + + /* Flag whether the visibility buffer for query results + * has changed. This requires a new RenderPass in order + * to update.*/ + bool is_visibility_dirty() const; + + /* Reset dirty flag state for visibility buffer. */ + void clear_visibility_dirty(); + /* Texture utilities. */ MTLContextTextureUtils &get_texture_utils() { diff --git a/source/blender/gpu/metal/mtl_context.mm b/source/blender/gpu/metal/mtl_context.mm index 2ab2e20158a..26cfe6632ef 100644 --- a/source/blender/gpu/metal/mtl_context.mm +++ b/source/blender/gpu/metal/mtl_context.mm @@ -188,7 +188,8 @@ id MTLContext::ensure_begin_render_pass() * framebuffer state has been modified (is_dirty). */ if (!this->main_command_buffer.is_inside_render_pass() || this->active_fb != this->main_command_buffer.get_active_framebuffer() || - this->main_command_buffer.get_active_framebuffer()->get_dirty()) { + this->main_command_buffer.get_active_framebuffer()->get_dirty() || + this->is_visibility_dirty()) { /* Validate bound framebuffer before beginning render pass. */ if (!static_cast(this->active_fb)->validate_render_pass()) { @@ -371,6 +372,45 @@ void MTLContext::set_scissor_enabled(bool scissor_enabled) /** \} */ +/* -------------------------------------------------------------------- */ +/** \name Visibility buffer control for MTLQueryPool. + * \{ */ + +void MTLContext::set_visibility_buffer(gpu::MTLBuffer *buffer) +{ + /* Flag visibility buffer as dirty if the buffer being used for visibility has changed -- + * This is required by the render pass, and we will break the pass if the results destination + * buffer is modified. */ + if (buffer) { + visibility_is_dirty_ = (buffer != visibility_buffer_) || visibility_is_dirty_; + visibility_buffer_ = buffer; + visibility_buffer_->debug_ensure_used(); + } + else { + /* If buffer is null, reset visibility state, mark dirty to break render pass if results are no + * longer needed. */ + visibility_is_dirty_ = (visibility_buffer_ != nullptr) || visibility_is_dirty_; + visibility_buffer_ = nullptr; + } +} + +gpu::MTLBuffer *MTLContext::get_visibility_buffer() const +{ + return visibility_buffer_; +} + +void MTLContext::clear_visibility_dirty() +{ + visibility_is_dirty_ = false; +} + +bool MTLContext::is_visibility_dirty() const +{ + return visibility_is_dirty_; +} + +/** \} */ + /* -------------------------------------------------------------------- */ /** \name Texture State Management * \{ */ diff --git a/source/blender/gpu/metal/mtl_query.hh b/source/blender/gpu/metal/mtl_query.hh new file mode 100644 index 00000000000..c8ee6998a68 --- /dev/null +++ b/source/blender/gpu/metal/mtl_query.hh @@ -0,0 +1,39 @@ +/** \file + * \ingroup gpu + */ + +#pragma once + +#include "BLI_vector.hh" + +#include "gpu_query.hh" +#include "mtl_context.hh" + +namespace blender::gpu { + +class MTLQueryPool : public QueryPool { + private: + /** Number of queries that have been issued since last initialization. + * Should be equal to query_ids_.size(). */ + uint32_t query_issued_; + /** Type of this query pool. */ + GPUQueryType type_; + /** Can only be initialized once. */ + bool initialized_ = false; + MTLVisibilityResultMode mtl_type_; + Vector buffer_; + + void allocate_buffer(); + + public: + MTLQueryPool(); + ~MTLQueryPool(); + + void init(GPUQueryType type) override; + + void begin_query() override; + void end_query() override; + + void get_occlusion_result(MutableSpan r_values) override; +}; +} // namespace blender::gpu diff --git a/source/blender/gpu/metal/mtl_query.mm b/source/blender/gpu/metal/mtl_query.mm new file mode 100644 index 00000000000..22f99f1718c --- /dev/null +++ b/source/blender/gpu/metal/mtl_query.mm @@ -0,0 +1,120 @@ +/** \file + * \ingroup gpu + */ + +#include "mtl_query.hh" + +namespace blender::gpu { + +static const size_t VISIBILITY_COUNT_PER_BUFFER = 512; +/* defined in the documentation but not queryable programmatically: + * https://developer.apple.com/documentation/metal/mtlvisibilityresultmode/mtlvisibilityresultmodeboolean?language=objc + */ +static const size_t VISIBILITY_RESULT_SIZE_IN_BYTES = 8; + +MTLQueryPool::MTLQueryPool() +{ + allocate_buffer(); +} +MTLQueryPool::~MTLQueryPool() +{ + for (gpu::MTLBuffer *buf : buffer_) { + BLI_assert(buf); + buf->free(); + } +} + +void MTLQueryPool::allocate_buffer() +{ + /* Allocate Metal buffer for visibility results. */ + size_t buffer_size_in_bytes = VISIBILITY_COUNT_PER_BUFFER * VISIBILITY_RESULT_SIZE_IN_BYTES; + gpu::MTLBuffer *buffer = MTLContext::get_global_memory_manager().allocate_buffer( + buffer_size_in_bytes, true); + BLI_assert(buffer); + buffer_.append(buffer); +} + +static inline MTLVisibilityResultMode to_mtl_type(GPUQueryType type) +{ + if (type == GPU_QUERY_OCCLUSION) { + return MTLVisibilityResultModeBoolean; + } + BLI_assert(0); + return MTLVisibilityResultModeBoolean; +} + +void MTLQueryPool::init(GPUQueryType type) +{ + BLI_assert(initialized_ == false); + initialized_ = true; + type_ = type; + mtl_type_ = to_mtl_type(type); + query_issued_ = 0; +} + +void MTLQueryPool::begin_query() +{ + MTLContext *ctx = reinterpret_cast(GPU_context_active_get()); + + /* Ensure our allocated buffer pool has enough space for the current queries. */ + int query_id = query_issued_; + int requested_buffer = query_id / VISIBILITY_COUNT_PER_BUFFER; + if (requested_buffer >= buffer_.size()) { + allocate_buffer(); + } + + BLI_assert(requested_buffer < buffer_.size()); + gpu::MTLBuffer *buffer = buffer_[requested_buffer]; + + /* Ensure visibility buffer is set on the context. If visibility buffer changes, + * we need to begin a new render pass with an updated reference in the + * MTLRenderPassDescriptor. */ + ctx->set_visibility_buffer(buffer); + + ctx->ensure_begin_render_pass(); + id rec = ctx->main_command_buffer.get_active_render_command_encoder(); + [rec setVisibilityResultMode:mtl_type_ + offset:(query_id % VISIBILITY_COUNT_PER_BUFFER) * + VISIBILITY_RESULT_SIZE_IN_BYTES]; + query_issued_ += 1; +} + +void MTLQueryPool::end_query() +{ + MTLContext *ctx = reinterpret_cast(GPU_context_active_get()); + + id rec = ctx->main_command_buffer.get_active_render_command_encoder(); + [rec setVisibilityResultMode:MTLVisibilityResultModeDisabled offset:0]; +} + +void MTLQueryPool::get_occlusion_result(MutableSpan r_values) +{ + MTLContext *ctx = reinterpret_cast(GPU_context_active_get()); + + /* Create a blit encoder to synchronize the query buffer results between + * GPU and CPU when not using shared-memory. */ + if ([ctx->device hasUnifiedMemory] == false) { + id blit_encoder = ctx->main_command_buffer.ensure_begin_blit_encoder(); + BLI_assert(blit_encoder); + for (gpu::MTLBuffer *buf : buffer_) { + [blit_encoder synchronizeResource:buf->get_metal_buffer()]; + } + BLI_assert(ctx->get_inside_frame()); + } + + /* Wait for GPU operatiosn to complete and for query buffer contents + * to be synchronised back to host memory. */ + GPU_finish(); + + /* Iterate through all possible visibility buffers and copy results into provided + * container. */ + for (const int i : IndexRange(query_issued_)) { + int requested_buffer = i / VISIBILITY_COUNT_PER_BUFFER; + const uint64_t *queries = static_cast( + buffer_[requested_buffer]->get_host_ptr()); + r_values[i] = static_cast(queries[i % VISIBILITY_COUNT_PER_BUFFER]); + } + ctx->set_visibility_buffer(nullptr); +} + +} // namespace blender::gpu -- cgit v1.2.3 From 9835d5e58ba28ff69abec74c1cb3f3959ff9451a Mon Sep 17 00:00:00 2001 From: Jason Fielder Date: Tue, 19 Jul 2022 17:11:03 +0200 Subject: Metal: MTLUniformBuffer module implementation Initial implementation. Authored by Apple: Michael Parkin-White Ref T96261 Reviewed By: fclem Differential Revision: https://developer.blender.org/D15357 --- source/blender/gpu/CMakeLists.txt | 2 + source/blender/gpu/metal/mtl_backend.mm | 4 +- source/blender/gpu/metal/mtl_context.hh | 6 + source/blender/gpu/metal/mtl_memory.hh | 18 +-- source/blender/gpu/metal/mtl_memory.mm | 48 +++++--- source/blender/gpu/metal/mtl_uniform_buffer.hh | 48 ++++++++ source/blender/gpu/metal/mtl_uniform_buffer.mm | 160 +++++++++++++++++++++++++ 7 files changed, 260 insertions(+), 26 deletions(-) create mode 100644 source/blender/gpu/metal/mtl_uniform_buffer.hh create mode 100644 source/blender/gpu/metal/mtl_uniform_buffer.mm diff --git a/source/blender/gpu/CMakeLists.txt b/source/blender/gpu/CMakeLists.txt index 5a1816c5829..5e97909a2b8 100644 --- a/source/blender/gpu/CMakeLists.txt +++ b/source/blender/gpu/CMakeLists.txt @@ -199,6 +199,7 @@ set(METAL_SRC metal/mtl_state.mm metal/mtl_texture.mm metal/mtl_texture_util.mm + metal/mtl_uniform_buffer.mm metal/mtl_backend.hh metal/mtl_capabilities.hh @@ -210,6 +211,7 @@ set(METAL_SRC metal/mtl_query.hh metal/mtl_state.hh metal/mtl_texture.hh + metal/mtl_uniform_buffer.hh ) # Select Backend source based on availability diff --git a/source/blender/gpu/metal/mtl_backend.mm b/source/blender/gpu/metal/mtl_backend.mm index af1fd6a06bb..3328855bbf8 100644 --- a/source/blender/gpu/metal/mtl_backend.mm +++ b/source/blender/gpu/metal/mtl_backend.mm @@ -10,6 +10,7 @@ #include "mtl_backend.hh" #include "mtl_context.hh" #include "mtl_framebuffer.hh" +#include "mtl_uniform_buffer.hh" #include "mtl_query.hh" #include "gpu_capabilities_private.hh" @@ -81,8 +82,7 @@ Texture *MTLBackend::texture_alloc(const char *name) UniformBuf *MTLBackend::uniformbuf_alloc(int size, const char *name) { - /* TODO(Metal): Implement MTLUniformBuf. */ - return nullptr; + return new MTLUniformBuf(size, name); }; StorageBuf *MTLBackend::storagebuf_alloc(int size, GPUUsageType usage, const char *name) diff --git a/source/blender/gpu/metal/mtl_context.hh b/source/blender/gpu/metal/mtl_context.hh index caabd59a1b5..0db87bf5da5 100644 --- a/source/blender/gpu/metal/mtl_context.hh +++ b/source/blender/gpu/metal/mtl_context.hh @@ -3,6 +3,7 @@ /** \file * \ingroup gpu */ + #pragma once #include "MEM_guardedalloc.h" @@ -625,6 +626,11 @@ class MTLContext : public Context { void memory_statistics_get(int *total_mem, int *free_mem) override; + static MTLContext *get() + { + return static_cast(Context::get()); + } + void debug_group_begin(const char *name, int index) override; void debug_group_end() override; diff --git a/source/blender/gpu/metal/mtl_memory.hh b/source/blender/gpu/metal/mtl_memory.hh index daa049e78af..dc5417dc11a 100644 --- a/source/blender/gpu/metal/mtl_memory.hh +++ b/source/blender/gpu/metal/mtl_memory.hh @@ -78,8 +78,10 @@ * Usage: * MTLContext::get_global_memory_manager(); - static routine to fetch global memory manager. * - * gpu::MTLBuffer *allocate_buffer(size, is_cpu_visibile, bytes=nullptr) - * gpu::MTLBuffer *allocate_buffer_aligned(size, alignment, is_cpu_visibile, bytes=nullptr) + * gpu::MTLBuffer *allocate(size, is_cpu_visibile) + * gpu::MTLBuffer *allocate_aligned(size, alignment, is_cpu_visibile) + * gpu::MTLBuffer *allocate_with_data(size, is_cpu_visibile, data_ptr) + * gpu::MTLBuffer *allocate_aligned_with_data(size, alignment, is_cpu_visibile, data_ptr) */ /* Debug memory statistics: Disabled by Macro rather than guarded for @@ -389,11 +391,13 @@ class MTLBufferPool { void init(id device); ~MTLBufferPool(); - gpu::MTLBuffer *allocate_buffer(uint64_t size, bool cpu_visible, const void *bytes = nullptr); - gpu::MTLBuffer *allocate_buffer_aligned(uint64_t size, - uint alignment, - bool cpu_visible, - const void *bytes = nullptr); + gpu::MTLBuffer *allocate(uint64_t size, bool cpu_visible); + gpu::MTLBuffer *allocate_aligned(uint64_t size, uint alignment, bool cpu_visible); + gpu::MTLBuffer *allocate_with_data(uint64_t size, bool cpu_visible, const void *data = nullptr); + gpu::MTLBuffer *allocate_aligned_with_data(uint64_t size, + uint alignment, + bool cpu_visible, + const void *data = nullptr); bool free_buffer(gpu::MTLBuffer *buffer); /* Flush MTLSafeFreeList buffers, for completed lists in `completed_safelist_queue_`, diff --git a/source/blender/gpu/metal/mtl_memory.mm b/source/blender/gpu/metal/mtl_memory.mm index e5db32ed1b1..48e27dd2bb6 100644 --- a/source/blender/gpu/metal/mtl_memory.mm +++ b/source/blender/gpu/metal/mtl_memory.mm @@ -57,17 +57,23 @@ void MTLBufferPool::free() buffer_pools_.clear(); } -gpu::MTLBuffer *MTLBufferPool::allocate_buffer(uint64_t size, bool cpu_visible, const void *bytes) +gpu::MTLBuffer *MTLBufferPool::allocate(uint64_t size, bool cpu_visible) { /* Allocate buffer with default HW-compatible alignment of 256 bytes. * See https://developer.apple.com/metal/Metal-Feature-Set-Tables.pdf for more. */ - return this->allocate_buffer_aligned(size, 256, cpu_visible, bytes); + return this->allocate_aligned(size, 256, cpu_visible); } -gpu::MTLBuffer *MTLBufferPool::allocate_buffer_aligned(uint64_t size, - uint alignment, - bool cpu_visible, - const void *bytes) +gpu::MTLBuffer *MTLBufferPool::allocate_with_data(uint64_t size, + bool cpu_visible, + const void *data) +{ + /* Allocate buffer with default HW-compatible alignemnt of 256 bytes. + * See https://developer.apple.com/metal/Metal-Feature-Set-Tables.pdf for more. */ + return this->allocate_aligned_with_data(size, 256, cpu_visible, data); +} + +gpu::MTLBuffer *MTLBufferPool::allocate_aligned(uint64_t size, uint alignment, bool cpu_visible) { /* Check not required. Main GPU module usage considered thread-safe. */ // BLI_assert(BLI_thread_is_main()); @@ -153,15 +159,6 @@ gpu::MTLBuffer *MTLBufferPool::allocate_buffer_aligned(uint64_t size, /* Flag buffer as actively in-use. */ new_buffer->flag_in_use(true); - /* Upload initial data if provided -- Size based on original size param, not aligned size*/ - if (bytes) { - BLI_assert(!(options & MTLResourceStorageModePrivate)); - BLI_assert(size <= aligned_alloc_size); - BLI_assert(size <= [new_buffer->get_metal_buffer() length]); - memcpy(new_buffer->get_host_ptr(), bytes, size); - new_buffer->flush_range(0, size); - } - #if MTL_DEBUG_MEMORY_STATISTICS == 1 this->per_frame_allocation_count++; #endif @@ -169,6 +166,23 @@ gpu::MTLBuffer *MTLBufferPool::allocate_buffer_aligned(uint64_t size, return new_buffer; } +gpu::MTLBuffer *MTLBufferPool::allocate_aligned_with_data(uint64_t size, + uint alignment, + bool cpu_visible, + const void *data) +{ + gpu::MTLBuffer *buf = this->allocate_aligned(size, 256, cpu_visible); + + /* Upload initial data. */ + BLI_assert(data != nullptr); + BLI_assert(!(buf->get_resource_options() & MTLResourceStorageModePrivate)); + BLI_assert(size <= buf->get_size()); + BLI_assert(size <= [buf->get_metal_buffer() length]); + memcpy(buf->get_host_ptr(), data, size); + buf->flush_range(0, size); + return buf; +} + bool MTLBufferPool::free_buffer(gpu::MTLBuffer *buffer) { /* Ensure buffer is flagged as in-use. I.e. has not already been returned to memory pools. */ @@ -356,7 +370,7 @@ void MTLBufferPool::insert_buffer_into_pool(MTLResourceOptions options, gpu::MTL #if MTL_DEBUG_MEMORY_STATISTICS == 1 /* Debug statistics. */ - allocations_in_pool_ += buffer->size; + allocations_in_pool_ += buffer->get_size(); buffers_in_pool_++; #endif } @@ -413,7 +427,7 @@ void MTLSafeFreeList::decrement_reference() { lock_.lock(); BLI_assert(in_free_queue_ == false); - int ref_count = reference_count_--; + int ref_count = --reference_count_; if (ref_count == 0) { MTLContext::get_global_memory_manager().push_completed_safe_list(this); diff --git a/source/blender/gpu/metal/mtl_uniform_buffer.hh b/source/blender/gpu/metal/mtl_uniform_buffer.hh new file mode 100644 index 00000000000..351194c2ff9 --- /dev/null +++ b/source/blender/gpu/metal/mtl_uniform_buffer.hh @@ -0,0 +1,48 @@ +/** \file + * \ingroup gpu + */ + +#pragma once + +#include "MEM_guardedalloc.h" +#include "gpu_uniform_buffer_private.hh" + +#include "mtl_context.hh" + +namespace blender::gpu { + +/** + * Implementation of Uniform Buffers using Metal. + **/ +class MTLUniformBuf : public UniformBuf { + private: + /* Allocation Handle. */ + gpu::MTLBuffer *metal_buffer_ = nullptr; + + /* Whether buffer has contents, if false, no GPU buffer will + * have yet been allocated. */ + bool has_data_ = false; + + /* Bindstate tracking. */ + int bind_slot_ = -1; + MTLContext *bound_ctx_ = nullptr; + + public: + MTLUniformBuf(size_t size, const char *name); + ~MTLUniformBuf(); + + void update(const void *data) override; + void bind(int slot) override; + void unbind() override; + + id get_metal_buffer(int *r_offset); + int get_size(); + const char *get_name() + { + return name_; + } + + MEM_CXX_CLASS_ALLOC_FUNCS("MTLUniformBuf"); +}; + +} // namespace blender::gpu diff --git a/source/blender/gpu/metal/mtl_uniform_buffer.mm b/source/blender/gpu/metal/mtl_uniform_buffer.mm new file mode 100644 index 00000000000..e45ae76c921 --- /dev/null +++ b/source/blender/gpu/metal/mtl_uniform_buffer.mm @@ -0,0 +1,160 @@ +/** \file + * \ingroup gpu + */ + +#include "BKE_global.h" + +#include "BLI_string.h" + +#include "gpu_backend.hh" +#include "gpu_context_private.hh" + +#include "mtl_backend.hh" +#include "mtl_context.hh" +#include "mtl_debug.hh" +#include "mtl_uniform_buffer.hh" + +namespace blender::gpu { + +MTLUniformBuf::MTLUniformBuf(size_t size, const char *name) : UniformBuf(size, name) +{ +} + +MTLUniformBuf::~MTLUniformBuf() +{ + if (metal_buffer_ != nullptr) { + metal_buffer_->free(); + metal_buffer_ = nullptr; + } + has_data_ = false; + + /* Ensure UBO is not bound to active CTX. + * UBO bindings are reset upon Context-switch so we do not need + * to check deactivated context's. */ + MTLContext *ctx = MTLContext::get(); + if (ctx) { + for (int i = 0; i < MTL_MAX_UNIFORM_BUFFER_BINDINGS; i++) { + MTLUniformBufferBinding &slot = ctx->pipeline_state.ubo_bindings[i]; + if (slot.bound && slot.ubo == this) { + slot.bound = false; + slot.ubo = nullptr; + } + } + } +} + +void MTLUniformBuf::update(const void *data) +{ + BLI_assert(this); + BLI_assert(size_in_bytes_ > 0); + + /* Free existing allocation. + * The previous UBO resource will be tracked by the memory manager, + * in case dependent GPU work is still executing. */ + if (metal_buffer_ != nullptr) { + metal_buffer_->free(); + metal_buffer_ = nullptr; + } + + /* Allocate MTL buffer */ + MTLContext *ctx = static_cast(unwrap(GPU_context_active_get())); + BLI_assert(ctx); + BLI_assert(ctx->device); + UNUSED_VARS_NDEBUG(ctx); + + if (data != nullptr) { + metal_buffer_ = MTLContext::get_global_memory_manager().allocate_with_data( + size_in_bytes_, true, data); + has_data_ = true; + + metal_buffer_->set_label(@"Uniform Buffer"); + BLI_assert(metal_buffer_ != nullptr); + BLI_assert(metal_buffer_->get_metal_buffer() != nil); + } + else { + /* If data is not yet present, no buffer will be allocated and MTLContext will use an empty + * null buffer, containing zeroes, if the UBO is bound. */ + metal_buffer_ = nullptr; + has_data_ = false; + } +} + +void MTLUniformBuf::bind(int slot) +{ + if (slot < 0) { + MTL_LOG_WARNING("Failed to bind UBO %p. uniform location %d invalid.\n", this, slot); + return; + } + + BLI_assert(slot < MTL_MAX_UNIFORM_BUFFER_BINDINGS); + + /* Bind current UBO to active context. */ + MTLContext *ctx = MTLContext::get(); + BLI_assert(ctx); + + MTLUniformBufferBinding &ctx_ubo_bind_slot = ctx->pipeline_state.ubo_bindings[slot]; + ctx_ubo_bind_slot.ubo = this; + ctx_ubo_bind_slot.bound = true; + + bind_slot_ = slot; + bound_ctx_ = ctx; + + /* Check if we have any deferred data to upload. */ + if (data_ != nullptr) { + this->update(data_); + MEM_SAFE_FREE(data_); + } + + /* Ensure there is atleast an empty dummy buffer. */ + if (metal_buffer_ == nullptr) { + this->update(nullptr); + } +} + +void MTLUniformBuf::unbind() +{ + /* Unbind in debug mode to validate missing binds. + * Otherwise, only perform a full unbind upon destruction + * to ensure no lingering references. */ +#ifndef NDEBUG + if (true) { +#else + if (G.debug & G_DEBUG_GPU) { +#endif + if (bound_ctx_ != nullptr && bind_slot_ > -1) { + MTLUniformBufferBinding &ctx_ubo_bind_slot = + bound_ctx_->pipeline_state.ubo_bindings[bind_slot_]; + if (ctx_ubo_bind_slot.bound && ctx_ubo_bind_slot.ubo == this) { + ctx_ubo_bind_slot.bound = false; + ctx_ubo_bind_slot.ubo = nullptr; + } + } + } + + /* Reset bind index. */ + bind_slot_ = -1; + bound_ctx_ = nullptr; +} + +id MTLUniformBuf::get_metal_buffer(int *r_offset) +{ + BLI_assert(this); + *r_offset = 0; + if (metal_buffer_ != nullptr && has_data_) { + *r_offset = 0; + metal_buffer_->debug_ensure_used(); + return metal_buffer_->get_metal_buffer(); + } + else { + *r_offset = 0; + return nil; + } +} + +int MTLUniformBuf::get_size() +{ + BLI_assert(this); + return size_in_bytes_; +} + +} // blender::gpu -- cgit v1.2.3 From 9246ff373a19df24255d924979c78793f97b2834 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cle=CC=81ment=20Foucault?= Date: Tue, 19 Jul 2022 17:14:39 +0200 Subject: Metal: Add license header to new files --- source/blender/gpu/metal/mtl_query.hh | 2 ++ source/blender/gpu/metal/mtl_query.mm | 2 ++ source/blender/gpu/metal/mtl_uniform_buffer.hh | 2 ++ source/blender/gpu/metal/mtl_uniform_buffer.mm | 2 ++ 4 files changed, 8 insertions(+) diff --git a/source/blender/gpu/metal/mtl_query.hh b/source/blender/gpu/metal/mtl_query.hh index c8ee6998a68..c1ec9a2a0f5 100644 --- a/source/blender/gpu/metal/mtl_query.hh +++ b/source/blender/gpu/metal/mtl_query.hh @@ -1,3 +1,5 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + /** \file * \ingroup gpu */ diff --git a/source/blender/gpu/metal/mtl_query.mm b/source/blender/gpu/metal/mtl_query.mm index 22f99f1718c..33ae8f554c3 100644 --- a/source/blender/gpu/metal/mtl_query.mm +++ b/source/blender/gpu/metal/mtl_query.mm @@ -1,3 +1,5 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + /** \file * \ingroup gpu */ diff --git a/source/blender/gpu/metal/mtl_uniform_buffer.hh b/source/blender/gpu/metal/mtl_uniform_buffer.hh index 351194c2ff9..722d819cf96 100644 --- a/source/blender/gpu/metal/mtl_uniform_buffer.hh +++ b/source/blender/gpu/metal/mtl_uniform_buffer.hh @@ -1,3 +1,5 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + /** \file * \ingroup gpu */ diff --git a/source/blender/gpu/metal/mtl_uniform_buffer.mm b/source/blender/gpu/metal/mtl_uniform_buffer.mm index e45ae76c921..3415c41f2cc 100644 --- a/source/blender/gpu/metal/mtl_uniform_buffer.mm +++ b/source/blender/gpu/metal/mtl_uniform_buffer.mm @@ -1,3 +1,5 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + /** \file * \ingroup gpu */ -- cgit v1.2.3 From 6a1ab4747b7758017721cb191046a9db800f7894 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Tue, 19 Jul 2022 10:14:46 -0500 Subject: Geometry Nodes: Copy parameters when copying a curves data-block Previously, things like materials, symmetry, and selection options stored on `Curves` weren't copied to the result in nodes like the subdivide and resample nodes. Now they are, which fixes some unexpected behavior and allows visualization of the sculpt mode selection. In the realize instances and join nodes the behavior is the same as for meshes, the parameters are taken from the first (top) input. I also refactored some functions to return a `CurvesGeometry` by-value, which makes it the responsibility of the node to copy the parameters. That should make the algorithms more reusable in other situations. Differential Revision: https://developer.blender.org/D15408 --- source/blender/blenkernel/BKE_curves.hh | 6 ++ source/blender/blenkernel/BKE_curves_utils.hh | 7 ++ source/blender/blenkernel/intern/curves.cc | 17 +++++ source/blender/blenkernel/intern/curves_utils.cc | 12 ++++ source/blender/geometry/GEO_mesh_to_curve.hh | 12 ++-- source/blender/geometry/GEO_set_curve_type.hh | 18 ++--- source/blender/geometry/GEO_subdivide_curves.hh | 10 ++- .../geometry/intern/mesh_to_curve_convert.cc | 23 +++---- .../blender/geometry/intern/realize_instances.cc | 5 ++ source/blender/geometry/intern/set_curve_type.cc | 76 +++++++--------------- source/blender/geometry/intern/subdivide_curves.cc | 38 ++--------- .../nodes/geometry/nodes/node_geo_curve_fillet.cc | 5 +- .../geometry/nodes/node_geo_curve_resample.cc | 18 +++-- .../geometry/nodes/node_geo_curve_spline_type.cc | 13 ++-- .../geometry/nodes/node_geo_curve_subdivide.cc | 13 ++-- .../nodes/geometry/nodes/node_geo_curve_trim.cc | 8 ++- .../geometry/nodes/node_geo_duplicate_elements.cc | 2 + .../nodes/geometry/nodes/node_geo_mesh_to_curve.cc | 6 +- 18 files changed, 147 insertions(+), 142 deletions(-) diff --git a/source/blender/blenkernel/BKE_curves.hh b/source/blender/blenkernel/BKE_curves.hh index fb97e52f6da..25a912b8825 100644 --- a/source/blender/blenkernel/BKE_curves.hh +++ b/source/blender/blenkernel/BKE_curves.hh @@ -735,6 +735,12 @@ Curves *curves_new_nomain(CurvesGeometry curves); */ Curves *curves_new_nomain_single(int points_num, CurveType type); +/** + * Copy data from #src to #dst, except the geometry data in #CurvesGeometry. Typically used to + * copy high-level parameters when a geometry-altering operation creates a new curves data-block. + */ +void curves_copy_parameters(const Curves &src, Curves &dst); + std::array calculate_type_counts(const VArray &types); /* -------------------------------------------------------------------- */ diff --git a/source/blender/blenkernel/BKE_curves_utils.hh b/source/blender/blenkernel/BKE_curves_utils.hh index f223e173ea9..0fbd33002e1 100644 --- a/source/blender/blenkernel/BKE_curves_utils.hh +++ b/source/blender/blenkernel/BKE_curves_utils.hh @@ -55,6 +55,13 @@ void fill_points(const CurvesGeometry &curves, fill_points(curves, curve_selection, &value, dst); } +/** + * Copy only the information on the point domain, but not the offsets or any point attributes, + * meant for operations that change the number of points but not the number of curves. + * \warning The returned curves have invalid offsets! + */ +bke::CurvesGeometry copy_only_curve_domain(const bke::CurvesGeometry &src_curves); + /** * Copy the size of every curve in #curve_ranges to the corresponding index in #counts. */ diff --git a/source/blender/blenkernel/intern/curves.cc b/source/blender/blenkernel/intern/curves.cc index 78791b55b4d..7e9f994313b 100644 --- a/source/blender/blenkernel/intern/curves.cc +++ b/source/blender/blenkernel/intern/curves.cc @@ -388,6 +388,23 @@ Curves *curves_new_nomain(CurvesGeometry curves) return curves_id; } +void curves_copy_parameters(const Curves &src, Curves &dst) +{ + dst.flag = src.flag; + dst.attributes_active_index = src.attributes_active_index; + MEM_SAFE_FREE(dst.mat); + dst.mat = static_cast(MEM_malloc_arrayN(src.totcol, sizeof(Material *), __func__)); + dst.totcol = src.totcol; + MutableSpan(dst.mat, dst.totcol).copy_from(Span(src.mat, src.totcol)); + dst.symmetry = src.symmetry; + dst.selection_domain = src.selection_domain; + dst.surface = src.surface; + MEM_SAFE_FREE(dst.surface_uv_map); + if (src.surface_uv_map != nullptr) { + dst.surface_uv_map = BLI_strdup(src.surface_uv_map); + } +} + CurvesSurfaceTransforms::CurvesSurfaceTransforms(const Object &curves_ob, const Object *surface_ob) { this->curves_to_world = curves_ob.obmat; diff --git a/source/blender/blenkernel/intern/curves_utils.cc b/source/blender/blenkernel/intern/curves_utils.cc index f7db60ee588..d98832e796c 100644 --- a/source/blender/blenkernel/intern/curves_utils.cc +++ b/source/blender/blenkernel/intern/curves_utils.cc @@ -84,6 +84,18 @@ void fill_points(const CurvesGeometry &curves, }); } +bke::CurvesGeometry copy_only_curve_domain(const bke::CurvesGeometry &src_curves) +{ + bke::CurvesGeometry dst_curves(0, src_curves.curves_num()); + CustomData_copy(&src_curves.curve_data, + &dst_curves.curve_data, + CD_MASK_ALL, + CD_DUPLICATE, + src_curves.curves_num()); + dst_curves.runtime->type_counts = src_curves.runtime->type_counts; + return dst_curves; +} + IndexMask indices_for_type(const VArray &types, const std::array &type_counts, const CurveType type, diff --git a/source/blender/geometry/GEO_mesh_to_curve.hh b/source/blender/geometry/GEO_mesh_to_curve.hh index c480e4178cf..f619aaff217 100644 --- a/source/blender/geometry/GEO_mesh_to_curve.hh +++ b/source/blender/geometry/GEO_mesh_to_curve.hh @@ -1,12 +1,12 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ +#pragma once + #include "BLI_index_mask.hh" -#pragma once +#include "BKE_curves.hh" struct Mesh; -struct Curves; -class MeshComponent; /** \file * \ingroup geo @@ -15,10 +15,10 @@ class MeshComponent; namespace blender::geometry { /** - * Convert the mesh into one or many poly splines. Since splines cannot have branches, - * intersections of more than three edges will become breaks in splines. Attributes that + * Convert the mesh into one or many poly curves. Since curves cannot have branches, + * intersections of more than three edges will become breaks in curves. Attributes that * are not built-in on meshes and not curves are transferred to the result curve. */ -Curves *mesh_to_curve_convert(const MeshComponent &mesh_component, const IndexMask selection); +bke::CurvesGeometry mesh_to_curve_convert(const Mesh &mesh, const IndexMask selection); } // namespace blender::geometry diff --git a/source/blender/geometry/GEO_set_curve_type.hh b/source/blender/geometry/GEO_set_curve_type.hh index 6a75450006b..f38e63b1fc8 100644 --- a/source/blender/geometry/GEO_set_curve_type.hh +++ b/source/blender/geometry/GEO_set_curve_type.hh @@ -2,17 +2,10 @@ #pragma once -#include "DNA_curves_types.h" - #include "BLI_function_ref.hh" #include "BLI_index_mask.hh" -struct Curves; -class CurveComponent; - -namespace blender::bke { -class CurvesGeometry; -} +#include "BKE_curves.hh" namespace blender::geometry { @@ -27,14 +20,13 @@ namespace blender::geometry { */ bool try_curves_conversion_in_place(IndexMask selection, CurveType dst_type, - FunctionRef get_writable_curves_fn); + FunctionRef get_writable_curves_fn); /** * Change the types of the selected curves, potentially changing the total point count. */ -Curves *convert_curves(const CurveComponent &src_component, - const bke::CurvesGeometry &src_curves, - IndexMask selection, - CurveType dst_type); +bke::CurvesGeometry convert_curves(const bke::CurvesGeometry &src_curves, + IndexMask selection, + CurveType dst_type); } // namespace blender::geometry diff --git a/source/blender/geometry/GEO_subdivide_curves.hh b/source/blender/geometry/GEO_subdivide_curves.hh index 66c2eb53496..ba55118baa4 100644 --- a/source/blender/geometry/GEO_subdivide_curves.hh +++ b/source/blender/geometry/GEO_subdivide_curves.hh @@ -4,11 +4,10 @@ #include "BLI_function_ref.hh" #include "BLI_index_mask.hh" +#include "BLI_virtual_array.hh" #include "BKE_curves.hh" -class CurveComponent; - namespace blender::geometry { /** @@ -18,9 +17,8 @@ namespace blender::geometry { * * \param selection: A selection of curves to consider when subdividing. */ -Curves *subdivide_curves(const CurveComponent &src_component, - const bke::CurvesGeometry &src_curves, - IndexMask selection, - const VArray &cuts); +bke::CurvesGeometry subdivide_curves(const bke::CurvesGeometry &src_curves, + IndexMask selection, + const VArray &cuts); } // namespace blender::geometry diff --git a/source/blender/geometry/intern/mesh_to_curve_convert.cc b/source/blender/geometry/intern/mesh_to_curve_convert.cc index 681dfd15137..fdacb174462 100644 --- a/source/blender/geometry/intern/mesh_to_curve_convert.cc +++ b/source/blender/geometry/intern/mesh_to_curve_convert.cc @@ -30,13 +30,12 @@ static void copy_with_map(const VArray &src, Span map, MutableSpan ds }); } -static Curves *create_curve_from_vert_indices(const MeshComponent &mesh_component, - const Span vert_indices, - const Span curve_offsets, - const IndexRange cyclic_curves) +static bke::CurvesGeometry create_curve_from_vert_indices(const Mesh &mesh, + const Span vert_indices, + const Span curve_offsets, + const IndexRange cyclic_curves) { - Curves *curves_id = bke::curves_new_nomain(vert_indices.size(), curve_offsets.size()); - bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id->geometry); + bke::CurvesGeometry curves(vert_indices.size(), curve_offsets.size()); curves.offsets_for_write().drop_back(1).copy_from(curve_offsets); curves.offsets_for_write().last() = vert_indices.size(); curves.fill_curve_types(CURVE_TYPE_POLY); @@ -44,8 +43,8 @@ static Curves *create_curve_from_vert_indices(const MeshComponent &mesh_componen curves.cyclic_for_write().fill(false); curves.cyclic_for_write().slice(cyclic_curves).fill(true); + const bke::AttributeAccessor mesh_attributes = bke::mesh_attributes(mesh); bke::MutableAttributeAccessor curves_attributes = curves.attributes_for_write(); - const bke::AttributeAccessor mesh_attributes = *mesh_component.attributes(); Set source_attribute_ids = mesh_attributes.all_ids(); @@ -76,7 +75,7 @@ static Curves *create_curve_from_vert_indices(const MeshComponent &mesh_componen }); } - return curves_id; + return curves; } struct CurveFromEdgesOutput { @@ -220,16 +219,14 @@ static Vector> get_selected_edges(const Mesh &mesh, const In return selected_edges; } -Curves *mesh_to_curve_convert(const MeshComponent &mesh_component, const IndexMask selection) +bke::CurvesGeometry mesh_to_curve_convert(const Mesh &mesh, const IndexMask selection) { - const Mesh &mesh = *mesh_component.get_for_read(); - Vector> selected_edges = get_selected_edges(*mesh_component.get_for_read(), - selection); + Vector> selected_edges = get_selected_edges(mesh, selection); CurveFromEdgesOutput output = edges_to_curve_point_indices({mesh.mvert, mesh.totvert}, selected_edges); return create_curve_from_vert_indices( - mesh_component, output.vert_indices, output.curve_offsets, output.cyclic_curves); + mesh, output.vert_indices, output.curve_offsets, output.cyclic_curves); } } // namespace blender::geometry diff --git a/source/blender/geometry/intern/realize_instances.cc b/source/blender/geometry/intern/realize_instances.cc index 66b856ee0c4..25bcead09b4 100644 --- a/source/blender/geometry/intern/realize_instances.cc +++ b/source/blender/geometry/intern/realize_instances.cc @@ -1238,6 +1238,11 @@ static void execute_realize_curve_tasks(const RealizeInstancesOptions &options, dst_component.replace(dst_curves_id); bke::MutableAttributeAccessor dst_attributes = dst_curves.attributes_for_write(); + /* Copy settings from the first input geometry set with curves. */ + const RealizeCurveTask &first_task = tasks.first(); + const Curves &first_curves_id = *first_task.curve_info->curves; + bke::curves_copy_parameters(first_curves_id, *dst_curves_id); + /* Prepare id attribute. */ SpanAttributeWriter point_ids; if (all_curves_info.create_id_attribute) { diff --git a/source/blender/geometry/intern/set_curve_type.cc b/source/blender/geometry/intern/set_curve_type.cc index 08888a74973..40ee2488a4b 100644 --- a/source/blender/geometry/intern/set_curve_type.cc +++ b/source/blender/geometry/intern/set_curve_type.cc @@ -1,9 +1,9 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ +#include "BKE_attribute.hh" #include "BKE_attribute_math.hh" #include "BKE_curves.hh" #include "BKE_curves_utils.hh" -#include "BKE_geometry_set.hh" #include "BLI_task.hh" @@ -322,39 +322,16 @@ static void retrieve_generic_point_attributes(const bke::AttributeAccessor &src_ }); } -static Curves *create_result_curves(const bke::CurvesGeometry &src_curves, - const IndexMask selection, - const CurveType dst_type) -{ - Curves *dst_curves_id = bke::curves_new_nomain(0, src_curves.curves_num()); - bke::CurvesGeometry &dst_curves = bke::CurvesGeometry::wrap(dst_curves_id->geometry); - CurveComponent dst_component; - dst_component.replace(dst_curves_id, GeometryOwnershipType::Editable); - /* Directly copy curve attributes, since they stay the same (except for curve types). */ - CustomData_copy(&src_curves.curve_data, - &dst_curves.curve_data, - CD_MASK_ALL, - CD_DUPLICATE, - src_curves.curves_num()); - - dst_curves.fill_curve_types(selection, dst_type); - - return dst_curves_id; -} - -static Curves *convert_curves_to_bezier(const CurveComponent &src_component, - const bke::CurvesGeometry &src_curves, - const IndexMask selection) +static bke::CurvesGeometry convert_curves_to_bezier(const bke::CurvesGeometry &src_curves, + const IndexMask selection) { const VArray src_knot_modes = src_curves.nurbs_knots_modes(); const VArray src_types = src_curves.curve_types(); const VArray src_cyclic = src_curves.cyclic(); const Span src_positions = src_curves.positions(); - Curves *dst_curves_id = create_result_curves(src_curves, selection, CURVE_TYPE_BEZIER); - bke::CurvesGeometry &dst_curves = bke::CurvesGeometry::wrap(dst_curves_id->geometry); - CurveComponent dst_component; - dst_component.replace(dst_curves_id, GeometryOwnershipType::Editable); + bke::CurvesGeometry dst_curves = bke::curves::copy_only_curve_domain(src_curves); + dst_curves.fill_curve_types(selection, CURVE_TYPE_BEZIER); MutableSpan dst_offsets = dst_curves.offsets_for_write(); retrieve_curve_sizes(src_curves, dst_curves.offsets_for_write()); @@ -367,8 +344,8 @@ static Curves *convert_curves_to_bezier(const CurveComponent &src_component, bke::curves::accumulate_counts_to_offsets(dst_offsets); dst_curves.resize(dst_offsets.last(), dst_curves.curves_num()); - const bke::AttributeAccessor src_attributes = *src_component.attributes(); - bke::MutableAttributeAccessor dst_attributes = *dst_component.attributes_for_write(); + const bke::AttributeAccessor src_attributes = src_curves.attributes(); + bke::MutableAttributeAccessor dst_attributes = dst_curves.attributes_for_write(); GenericAttributes attributes; retrieve_generic_point_attributes(src_attributes, dst_attributes, attributes); @@ -501,21 +478,18 @@ static Curves *convert_curves_to_bezier(const CurveComponent &src_component, attribute.finish(); } - return dst_curves_id; + return dst_curves; } -static Curves *convert_curves_to_nurbs(const CurveComponent &src_component, - const bke::CurvesGeometry &src_curves, - const IndexMask selection) +static bke::CurvesGeometry convert_curves_to_nurbs(const bke::CurvesGeometry &src_curves, + const IndexMask selection) { const VArray src_types = src_curves.curve_types(); const VArray src_cyclic = src_curves.cyclic(); const Span src_positions = src_curves.positions(); - Curves *dst_curves_id = create_result_curves(src_curves, selection, CURVE_TYPE_NURBS); - bke::CurvesGeometry &dst_curves = bke::CurvesGeometry::wrap(dst_curves_id->geometry); - CurveComponent dst_component; - dst_component.replace(dst_curves_id, GeometryOwnershipType::Editable); + bke::CurvesGeometry dst_curves = bke::curves::copy_only_curve_domain(src_curves); + dst_curves.fill_curve_types(selection, CURVE_TYPE_NURBS); MutableSpan dst_offsets = dst_curves.offsets_for_write(); retrieve_curve_sizes(src_curves, dst_curves.offsets_for_write()); @@ -527,8 +501,8 @@ static Curves *convert_curves_to_nurbs(const CurveComponent &src_component, bke::curves::accumulate_counts_to_offsets(dst_offsets); dst_curves.resize(dst_offsets.last(), dst_curves.curves_num()); - const bke::AttributeAccessor src_attributes = *src_component.attributes(); - bke::MutableAttributeAccessor dst_attributes = *dst_component.attributes_for_write(); + const bke::AttributeAccessor src_attributes = src_curves.attributes(); + bke::MutableAttributeAccessor dst_attributes = dst_curves.attributes_for_write(); GenericAttributes attributes; retrieve_generic_point_attributes(src_attributes, dst_attributes, attributes); @@ -669,7 +643,7 @@ static Curves *convert_curves_to_nurbs(const CurveComponent &src_component, attribute.finish(); } - return dst_curves_id; + return dst_curves; } static bke::CurvesGeometry convert_curves_trivial(const bke::CurvesGeometry &src_curves, @@ -682,33 +656,31 @@ static bke::CurvesGeometry convert_curves_trivial(const bke::CurvesGeometry &src return dst_curves; } -Curves *convert_curves(const CurveComponent &src_component, - const bke::CurvesGeometry &src_curves, - const IndexMask selection, - const CurveType dst_type) +bke::CurvesGeometry convert_curves(const bke::CurvesGeometry &src_curves, + const IndexMask selection, + const CurveType dst_type) { switch (dst_type) { case CURVE_TYPE_CATMULL_ROM: case CURVE_TYPE_POLY: - return bke::curves_new_nomain(convert_curves_trivial(src_curves, selection, dst_type)); + return convert_curves_trivial(src_curves, selection, dst_type); case CURVE_TYPE_BEZIER: - return convert_curves_to_bezier(src_component, src_curves, selection); + return convert_curves_to_bezier(src_curves, selection); case CURVE_TYPE_NURBS: - return convert_curves_to_nurbs(src_component, src_curves, selection); + return convert_curves_to_nurbs(src_curves, selection); } BLI_assert_unreachable(); - return nullptr; + return {}; } bool try_curves_conversion_in_place(const IndexMask selection, const CurveType dst_type, - FunctionRef get_writable_curves_fn) + FunctionRef get_writable_curves_fn) { if (conversion_can_change_point_num(dst_type)) { return false; } - Curves &curves_id = get_writable_curves_fn(); - bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id.geometry); + bke::CurvesGeometry &curves = get_writable_curves_fn(); curves.fill_curve_types(selection, dst_type); curves.remove_attributes_based_on_types(); return true; diff --git a/source/blender/geometry/intern/subdivide_curves.cc b/source/blender/geometry/intern/subdivide_curves.cc index 914174235cd..b6476d19818 100644 --- a/source/blender/geometry/intern/subdivide_curves.cc +++ b/source/blender/geometry/intern/subdivide_curves.cc @@ -11,26 +11,6 @@ namespace blender::geometry { -/** - * \warning Only the curve domain of the input is copied, so the result is invalid! - */ -static Curves *create_result_curves(const bke::CurvesGeometry &src_curves) -{ - Curves *dst_curves_id = bke::curves_new_nomain(0, src_curves.curves_num()); - bke::CurvesGeometry &dst_curves = bke::CurvesGeometry::wrap(dst_curves_id->geometry); - CurveComponent dst_component; - dst_component.replace(dst_curves_id, GeometryOwnershipType::Editable); - /* Directly copy curve attributes, since they stay the same. */ - CustomData_copy(&src_curves.curve_data, - &dst_curves.curve_data, - CD_MASK_ALL, - CD_DUPLICATE, - src_curves.curves_num()); - dst_curves.runtime->type_counts = src_curves.runtime->type_counts; - - return dst_curves_id; -} - /** * Return a range used to retrieve values from an array of values stored per point, but with an * extra element at the end of each curve. This is useful for offsets within curves, where it is @@ -342,10 +322,9 @@ static void subdivide_bezier_positions(const Span src_positions, cyclic, dst_types_l, dst_types_r, dst_positions, dst_handles_l, dst_handles_r); } -Curves *subdivide_curves(const CurveComponent &src_component, - const bke::CurvesGeometry &src_curves, - const IndexMask selection, - const VArray &cuts) +bke::CurvesGeometry subdivide_curves(const bke::CurvesGeometry &src_curves, + const IndexMask selection, + const VArray &cuts) { const Vector unselected_ranges = selection.extract_ranges_invert( src_curves.curves_range()); @@ -353,10 +332,7 @@ Curves *subdivide_curves(const CurveComponent &src_component, /* Cyclic is accessed a lot, it's probably worth it to make sure it's a span. */ const VArraySpan cyclic{src_curves.cyclic()}; - Curves *dst_curves_id = create_result_curves(src_curves); - bke::CurvesGeometry &dst_curves = bke::CurvesGeometry::wrap(dst_curves_id->geometry); - CurveComponent dst_component; - dst_component.replace(dst_curves_id, GeometryOwnershipType::Editable); + bke::CurvesGeometry dst_curves = bke::curves::copy_only_curve_domain(src_curves); /* For each point, this contains the point offset in the corresponding result curve, * starting at zero. For example for two curves with four points each, the values might @@ -385,8 +361,8 @@ Curves *subdivide_curves(const CurveComponent &src_component, dst_curves.resize(dst_curves.offsets().last(), dst_curves.curves_num()); - const bke::AttributeAccessor src_attributes = *src_component.attributes(); - bke::MutableAttributeAccessor dst_attributes = *dst_component.attributes_for_write(); + const bke::AttributeAccessor src_attributes = src_curves.attributes(); + bke::MutableAttributeAccessor dst_attributes = dst_curves.attributes_for_write(); auto subdivide_catmull_rom = [&](IndexMask selection) { for (auto &attribute : retrieve_point_attributes(src_attributes, dst_attributes)) { @@ -476,7 +452,7 @@ Curves *subdivide_curves(const CurveComponent &src_component, } } - return dst_curves_id; + return dst_curves; } } // namespace blender::geometry diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc index e200350dc18..057e08cc33d 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc @@ -603,10 +603,13 @@ static void calculate_curve_fillet(GeometrySet &geometry_set, fillet_param.limit_radius = limit_radius; + const Curves &src_curves_id = *geometry_set.get_curves_for_read(); const std::unique_ptr input_curve = curves_to_curve_eval(*component.get_for_read()); std::unique_ptr output_curve = fillet_curve(*input_curve, fillet_param); - geometry_set.replace_curves(curve_eval_to_curves(*output_curve)); + Curves *dst_curves_id = curve_eval_to_curves(*output_curve); + bke::curves_copy_parameters(src_curves_id, *dst_curves_id); + geometry_set.replace_curves(dst_curves_id); } static void node_geo_exec(GeoNodeExecParams params) diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc index 78a132064ed..2815dd5b2e8 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc @@ -65,8 +65,10 @@ static void node_geo_exec(GeoNodeExecParams params) Field count = params.extract_input>("Count"); geometry_set.modify_geometry_sets([&](GeometrySet &geometry) { if (const CurveComponent *component = geometry.get_component_for_read()) { - if (!component->is_empty()) { - geometry.replace_curves(geometry::resample_to_count(*component, selection, count)); + if (const Curves *src_curves = component->get_for_read()) { + Curves *dst_curves = geometry::resample_to_count(*component, selection, count); + bke::curves_copy_parameters(*src_curves, *dst_curves); + geometry.replace_curves(dst_curves); } } }); @@ -76,8 +78,10 @@ static void node_geo_exec(GeoNodeExecParams params) Field length = params.extract_input>("Length"); geometry_set.modify_geometry_sets([&](GeometrySet &geometry) { if (const CurveComponent *component = geometry.get_component_for_read()) { - if (!component->is_empty()) { - geometry.replace_curves(geometry::resample_to_length(*component, selection, length)); + if (const Curves *src_curves = component->get_for_read()) { + Curves *dst_curves = geometry::resample_to_length(*component, selection, length); + bke::curves_copy_parameters(*src_curves, *dst_curves); + geometry.replace_curves(dst_curves); } } }); @@ -86,8 +90,10 @@ static void node_geo_exec(GeoNodeExecParams params) case GEO_NODE_CURVE_RESAMPLE_EVALUATED: geometry_set.modify_geometry_sets([&](GeometrySet &geometry) { if (const CurveComponent *component = geometry.get_component_for_read()) { - if (!component->is_empty()) { - geometry.replace_curves(geometry::resample_to_evaluated(*component, selection)); + if (const Curves *src_curves = component->get_for_read()) { + Curves *dst_curves = geometry::resample_to_evaluated(*component, selection); + bke::curves_copy_parameters(*src_curves, *dst_curves); + geometry.replace_curves(dst_curves); } } }); diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc index 183c98e9c9f..a92479bc5f1 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc @@ -61,15 +61,18 @@ static void node_geo_exec(GeoNodeExecParams params) return; } - if (geometry::try_curves_conversion_in_place(selection, dst_type, [&]() -> Curves & { - return *geometry_set.get_curves_for_write(); - })) { + if (geometry::try_curves_conversion_in_place( + selection, dst_type, [&]() -> bke::CurvesGeometry & { + return bke::CurvesGeometry::wrap(geometry_set.get_curves_for_write()->geometry); + })) { return; } - Curves *dst_curves = geometry::convert_curves(src_component, src_curves, selection, dst_type); + bke::CurvesGeometry dst_curves = geometry::convert_curves(src_curves, selection, dst_type); - geometry_set.replace_curves(dst_curves); + Curves *dst_curves_id = bke::curves_new_nomain(std::move(dst_curves)); + bke::curves_copy_parameters(src_curves_id, *dst_curves_id); + geometry_set.replace_curves(dst_curves_id); }); params.set_output("Curve", std::move(geometry_set)); diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc index 864e6289135..bd44adb35a2 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc @@ -35,11 +35,11 @@ static void node_geo_exec(GeoNodeExecParams params) } const CurveComponent &component = *geometry_set.get_component_for_read(); - const bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap( - component.get_for_read()->geometry); + const Curves &src_curves_id = *component.get_for_read(); + const bke::CurvesGeometry &src_curves = bke::CurvesGeometry::wrap(src_curves_id.geometry); GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_POINT}; - fn::FieldEvaluator evaluator{field_context, curves.points_num()}; + fn::FieldEvaluator evaluator{field_context, src_curves.points_num()}; evaluator.add(cuts_field); evaluator.evaluate(); const VArray cuts = evaluator.get_evaluated(0); @@ -47,9 +47,12 @@ static void node_geo_exec(GeoNodeExecParams params) return; } - Curves *result = geometry::subdivide_curves(component, curves, curves.curves_range(), cuts); + bke::CurvesGeometry dst_curves = geometry::subdivide_curves( + src_curves, src_curves.curves_range(), cuts); - geometry_set.replace_curves(result); + Curves *dst_curves_id = bke::curves_new_nomain(std::move(dst_curves)); + bke::curves_copy_parameters(src_curves_id, *dst_curves_id); + geometry_set.replace_curves(dst_curves_id); }); params.set_output("Curve", geometry_set); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc index 00a79168087..51994cb8a41 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc @@ -1,5 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ +#include "BKE_curves.hh" #include "BKE_spline.hh" #include "BLI_task.hh" @@ -515,7 +516,8 @@ static void geometry_set_curve_trim(GeometrySet &geometry_set, const VArray starts = evaluator.get_evaluated(0); const VArray ends = evaluator.get_evaluated(1); - std::unique_ptr curve = curves_to_curve_eval(*geometry_set.get_curves_for_read()); + const Curves &src_curves_id = *geometry_set.get_curves_for_read(); + std::unique_ptr curve = curves_to_curve_eval(src_curves_id); MutableSpan splines = curve->splines(); threading::parallel_for(splines.index_range(), 128, [&](IndexRange range) { @@ -566,7 +568,9 @@ static void geometry_set_curve_trim(GeometrySet &geometry_set, } }); - geometry_set.replace_curves(curve_eval_to_curves(*curve)); + Curves *dst_curves_id = curve_eval_to_curves(*curve); + bke::curves_copy_parameters(src_curves_id, *dst_curves_id); + geometry_set.replace_curves(dst_curves_id); } static void node_geo_exec(GeoNodeExecParams params) diff --git a/source/blender/nodes/geometry/nodes/node_geo_duplicate_elements.cc b/source/blender/nodes/geometry/nodes/node_geo_duplicate_elements.cc index 41136060ab7..8160bc8d589 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_duplicate_elements.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_duplicate_elements.cc @@ -369,6 +369,7 @@ static void duplicate_curves(GeometrySet &geometry_set, point_offsets.last() = dst_points_num; Curves *new_curves_id = bke::curves_new_nomain(dst_points_num, dst_curves_num); + bke::curves_copy_parameters(curves_id, *new_curves_id); bke::CurvesGeometry &new_curves = bke::CurvesGeometry::wrap(new_curves_id->geometry); MutableSpan all_dst_offsets = new_curves.offsets_for_write(); @@ -830,6 +831,7 @@ static void duplicate_points_curve(GeometrySet &geometry_set, }); Curves *new_curves_id = bke::curves_new_nomain(dst_num, dst_num); + bke::curves_copy_parameters(src_curves_id, *new_curves_id); bke::CurvesGeometry &new_curves = bke::CurvesGeometry::wrap(new_curves_id->geometry); MutableSpan new_curve_offsets = new_curves.offsets_for_write(); for (const int i : new_curves.curves_range()) { diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc index f6ee3d00dee..4a79ec159f4 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc @@ -18,7 +18,8 @@ static void node_geo_exec(GeoNodeExecParams params) GeometrySet geometry_set = params.extract_input("Mesh"); geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { - if (!geometry_set.has_mesh()) { + const Mesh *mesh = geometry_set.get_mesh_for_read(); + if (mesh == nullptr) { geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES}); return; } @@ -34,7 +35,8 @@ static void node_geo_exec(GeoNodeExecParams params) return; } - geometry_set.replace_curves(geometry::mesh_to_curve_convert(component, selection)); + bke::CurvesGeometry curves = geometry::mesh_to_curve_convert(*mesh, selection); + geometry_set.replace_curves(bke::curves_new_nomain(std::move(curves))); geometry_set.keep_only({GEO_COMPONENT_TYPE_CURVE, GEO_COMPONENT_TYPE_INSTANCES}); }); -- cgit v1.2.3 From c771dd5e9c85335aa5e4cf60cfa61bc286c26229 Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Tue, 19 Jul 2022 17:21:11 +0200 Subject: Depsgraph: Make animated properties API receive const ID Semantically it is more correct as the cache does not modify the ID. There is need to do couple of const casts since the BKE (which is in C) does not easily allow to iterate into f-curves of const ID. Should be no functional changes. --- source/blender/depsgraph/intern/builder/deg_builder.cc | 11 ++++++----- source/blender/depsgraph/intern/builder/deg_builder.h | 8 ++++---- .../depsgraph/intern/builder/deg_builder_cache.cc | 15 ++++++++------- .../depsgraph/intern/builder/deg_builder_cache.h | 18 +++++++++--------- .../depsgraph/intern/builder/pipeline_all_objects.cc | 4 ++-- .../depsgraph/intern/builder/pipeline_from_ids.cc | 4 ++-- 6 files changed, 31 insertions(+), 29 deletions(-) diff --git a/source/blender/depsgraph/intern/builder/deg_builder.cc b/source/blender/depsgraph/intern/builder/deg_builder.cc index 888e0649065..1fec1fb3219 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder.cc @@ -63,7 +63,7 @@ DepsgraphBuilder::DepsgraphBuilder(Main *bmain, Depsgraph *graph, DepsgraphBuild { } -bool DepsgraphBuilder::need_pull_base_into_graph(Base *base) +bool DepsgraphBuilder::need_pull_base_into_graph(const Base *base) { /* Simple check: enabled bases are always part of dependency graph. */ const int base_flag = (graph_->mode == DAG_EVAL_VIEWPORT) ? BASE_ENABLED_VIEWPORT : @@ -74,7 +74,7 @@ bool DepsgraphBuilder::need_pull_base_into_graph(Base *base) /* More involved check: since we don't support dynamic changes in dependency graph topology and * all visible objects are to be part of dependency graph, we pull all objects which has animated * visibility. */ - Object *object = base->object; + const Object *object = base->object; AnimatedPropertyID property_id; if (graph_->mode == DAG_EVAL_VIEWPORT) { property_id = AnimatedPropertyID(&object->id, &RNA_Object, "hide_viewport"); @@ -89,7 +89,7 @@ bool DepsgraphBuilder::need_pull_base_into_graph(Base *base) return cache_->isPropertyAnimated(&object->id, property_id); } -bool DepsgraphBuilder::check_pchan_has_bbone(Object *object, const bPoseChannel *pchan) +bool DepsgraphBuilder::check_pchan_has_bbone(const Object *object, const bPoseChannel *pchan) { BLI_assert(object->type == OB_ARMATURE); if (pchan == nullptr || pchan->bone == nullptr) { @@ -109,12 +109,13 @@ bool DepsgraphBuilder::check_pchan_has_bbone(Object *object, const bPoseChannel cache_->isPropertyAnimated(&armature->id, property_id); } -bool DepsgraphBuilder::check_pchan_has_bbone_segments(Object *object, const bPoseChannel *pchan) +bool DepsgraphBuilder::check_pchan_has_bbone_segments(const Object *object, + const bPoseChannel *pchan) { return check_pchan_has_bbone(object, pchan); } -bool DepsgraphBuilder::check_pchan_has_bbone_segments(Object *object, const char *bone_name) +bool DepsgraphBuilder::check_pchan_has_bbone_segments(const Object *object, const char *bone_name) { const bPoseChannel *pchan = BKE_pose_channel_find_name(object->pose, bone_name); return check_pchan_has_bbone_segments(object, pchan); diff --git a/source/blender/depsgraph/intern/builder/deg_builder.h b/source/blender/depsgraph/intern/builder/deg_builder.h index 6f2bde3a2ff..950ebfb35ba 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder.h +++ b/source/blender/depsgraph/intern/builder/deg_builder.h @@ -22,11 +22,11 @@ class DepsgraphBuilder { public: virtual ~DepsgraphBuilder() = default; - virtual bool need_pull_base_into_graph(Base *base); + virtual bool need_pull_base_into_graph(const Base *base); - virtual bool check_pchan_has_bbone(Object *object, const bPoseChannel *pchan); - virtual bool check_pchan_has_bbone_segments(Object *object, const bPoseChannel *pchan); - virtual bool check_pchan_has_bbone_segments(Object *object, const char *bone_name); + 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); + virtual bool check_pchan_has_bbone_segments(const Object *object, const char *bone_name); protected: /* NOTE: The builder does NOT take ownership over any of those resources. */ diff --git a/source/blender/depsgraph/intern/builder/deg_builder_cache.cc b/source/blender/depsgraph/intern/builder/deg_builder_cache.cc index 7f88f54fdca..6474f853390 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_cache.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_cache.cc @@ -35,13 +35,13 @@ AnimatedPropertyID::AnimatedPropertyID(const PointerRNA &pointer_rna, { } -AnimatedPropertyID::AnimatedPropertyID(ID *id, StructRNA *type, const char *property_name) +AnimatedPropertyID::AnimatedPropertyID(const ID *id, StructRNA *type, const char *property_name) : data(id) { property_rna = RNA_struct_type_find_property(type, property_name); } -AnimatedPropertyID::AnimatedPropertyID(ID * /*id*/, +AnimatedPropertyID::AnimatedPropertyID(const ID * /*id*/, StructRNA *type, void *data, const char *property_name) @@ -100,13 +100,13 @@ AnimatedPropertyStorage::AnimatedPropertyStorage() : is_fully_initialized(false) { } -void AnimatedPropertyStorage::initializeFromID(DepsgraphBuilderCache *builder_cache, ID *id) +void AnimatedPropertyStorage::initializeFromID(DepsgraphBuilderCache *builder_cache, const ID *id) { AnimatedPropertyCallbackData data; - RNA_id_pointer_create(id, &data.pointer_rna); + RNA_id_pointer_create(const_cast(id), &data.pointer_rna); data.animated_property_storage = this; data.builder_cache = builder_cache; - BKE_fcurves_id_cb(id, animated_property_cb, &data); + BKE_fcurves_id_cb(const_cast(id), animated_property_cb, &data); } void AnimatedPropertyStorage::tagPropertyAsAnimated(const AnimatedPropertyID &property_id) @@ -147,13 +147,14 @@ DepsgraphBuilderCache::~DepsgraphBuilderCache() } } -AnimatedPropertyStorage *DepsgraphBuilderCache::ensureAnimatedPropertyStorage(ID *id) +AnimatedPropertyStorage *DepsgraphBuilderCache::ensureAnimatedPropertyStorage(const ID *id) { return animated_property_storage_map_.lookup_or_add_cb( id, []() { return new AnimatedPropertyStorage(); }); } -AnimatedPropertyStorage *DepsgraphBuilderCache::ensureInitializedAnimatedPropertyStorage(ID *id) +AnimatedPropertyStorage *DepsgraphBuilderCache::ensureInitializedAnimatedPropertyStorage( + const ID *id) { AnimatedPropertyStorage *animated_property_storage = ensureAnimatedPropertyStorage(id); if (!animated_property_storage->is_fully_initialized) { diff --git a/source/blender/depsgraph/intern/builder/deg_builder_cache.h b/source/blender/depsgraph/intern/builder/deg_builder_cache.h index 5568967f163..d85269c0f8b 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_cache.h +++ b/source/blender/depsgraph/intern/builder/deg_builder_cache.h @@ -27,14 +27,14 @@ class AnimatedPropertyID { AnimatedPropertyID(); AnimatedPropertyID(const PointerRNA *pointer_rna, const PropertyRNA *property_rna); AnimatedPropertyID(const PointerRNA &pointer_rna, const PropertyRNA *property_rna); - AnimatedPropertyID(ID *id, StructRNA *type, const char *property_name); - AnimatedPropertyID(ID *id, StructRNA *type, void *data, const char *property_name); + AnimatedPropertyID(const ID *id, StructRNA *type, const char *property_name); + AnimatedPropertyID(const ID *id, StructRNA *type, void *data, const char *property_name); uint64_t hash() const; friend bool operator==(const AnimatedPropertyID &a, const AnimatedPropertyID &b); /* Corresponds to PointerRNA.data. */ - void *data; + const void *data; const PropertyRNA *property_rna; MEM_CXX_CLASS_ALLOC_FUNCS("AnimatedPropertyID"); @@ -44,7 +44,7 @@ class AnimatedPropertyStorage { public: AnimatedPropertyStorage(); - void initializeFromID(DepsgraphBuilderCache *builder_cache, ID *id); + void initializeFromID(DepsgraphBuilderCache *builder_cache, const ID *id); void tagPropertyAsAnimated(const AnimatedPropertyID &property_id); void tagPropertyAsAnimated(const PointerRNA *pointer_rna, const PropertyRNA *property_rna); @@ -58,7 +58,7 @@ class AnimatedPropertyStorage { bool is_fully_initialized; /* indexed by PointerRNA.data. */ - Set animated_objects_set; + Set animated_objects_set; Set animated_properties_set; MEM_CXX_CLASS_ALLOC_FUNCS("AnimatedPropertyStorage"); @@ -70,8 +70,8 @@ class DepsgraphBuilderCache { ~DepsgraphBuilderCache(); /* Makes sure storage for animated properties exists and initialized for the given ID. */ - AnimatedPropertyStorage *ensureAnimatedPropertyStorage(ID *id); - AnimatedPropertyStorage *ensureInitializedAnimatedPropertyStorage(ID *id); + AnimatedPropertyStorage *ensureAnimatedPropertyStorage(const ID *id); + AnimatedPropertyStorage *ensureInitializedAnimatedPropertyStorage(const ID *id); /* Shortcuts to go through ensureInitializedAnimatedPropertyStorage and its * isPropertyAnimated. @@ -81,7 +81,7 @@ class DepsgraphBuilderCache { * * TODO(sergey): Technically, this makes this class something else than just a cache, but what is * the better name? */ - template bool isPropertyAnimated(ID *id, Args... args) + template bool isPropertyAnimated(const ID *id, Args... args) { AnimatedPropertyStorage *animated_property_storage = ensureInitializedAnimatedPropertyStorage( id); @@ -95,7 +95,7 @@ class DepsgraphBuilderCache { return animated_property_storage->isAnyPropertyAnimated(ptr); } - Map animated_property_storage_map_; + Map animated_property_storage_map_; MEM_CXX_CLASS_ALLOC_FUNCS("DepsgraphBuilderCache"); }; diff --git a/source/blender/depsgraph/intern/builder/pipeline_all_objects.cc b/source/blender/depsgraph/intern/builder/pipeline_all_objects.cc index 74d151c65d7..6bc3b59a9d6 100644 --- a/source/blender/depsgraph/intern/builder/pipeline_all_objects.cc +++ b/source/blender/depsgraph/intern/builder/pipeline_all_objects.cc @@ -20,7 +20,7 @@ class AllObjectsNodeBuilder : public DepsgraphNodeBuilder { { } - bool need_pull_base_into_graph(Base * /*base*/) override + bool need_pull_base_into_graph(const Base * /*base*/) override { return true; } @@ -33,7 +33,7 @@ class AllObjectsRelationBuilder : public DepsgraphRelationBuilder { { } - bool need_pull_base_into_graph(Base * /*base*/) override + bool need_pull_base_into_graph(const Base * /*base*/) override { return true; } diff --git a/source/blender/depsgraph/intern/builder/pipeline_from_ids.cc b/source/blender/depsgraph/intern/builder/pipeline_from_ids.cc index ee10b28a306..e256c8271f2 100644 --- a/source/blender/depsgraph/intern/builder/pipeline_from_ids.cc +++ b/source/blender/depsgraph/intern/builder/pipeline_from_ids.cc @@ -39,7 +39,7 @@ class DepsgraphFromIDsNodeBuilder : public DepsgraphNodeBuilder { { } - bool need_pull_base_into_graph(Base *base) override + bool need_pull_base_into_graph(const Base *base) override { if (!filter_.contains(&base->object->id)) { return false; @@ -61,7 +61,7 @@ class DepsgraphFromIDsRelationBuilder : public DepsgraphRelationBuilder { { } - bool need_pull_base_into_graph(Base *base) override + bool need_pull_base_into_graph(const Base *base) override { if (!filter_.contains(&base->object->id)) { return false; -- cgit v1.2.3 From 35b4e3a3501c9c797f2269421bc778e949fb46af Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Tue, 19 Jul 2022 10:30:10 -0500 Subject: RNA: Don't allocate empty char pointer properties Instead of allocating a single 0 char, set the `char *` DNA pointer to null. This avoids the overhead of allocating and copying single-bytes. rBeed45b655c9f didn't do this for safety reasons, but I checked the existing uses of this behavior in DNA/RNA. Out of 43 total `char *` members, this change only affects 7 recently added properties. For a complete list, see the patch description. Differential Revision: https://developer.blender.org/D14779 --- source/blender/makesrna/intern/makesrna.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/source/blender/makesrna/intern/makesrna.c b/source/blender/makesrna/intern/makesrna.c index b5354514205..a25fe201fa2 100644 --- a/source/blender/makesrna/intern/makesrna.c +++ b/source/blender/makesrna/intern/makesrna.c @@ -1117,8 +1117,10 @@ static char *rna_def_property_set_func( fprintf( f, " if (data->%s != NULL) { MEM_freeN(data->%s); }\n", dp->dnaname, dp->dnaname); fprintf(f, " const int length = strlen(value);\n"); - fprintf(f, " data->%s = MEM_mallocN(length + 1, __func__);\n", dp->dnaname); - fprintf(f, " %s(data->%s, value, length + 1);\n", string_copy_func, dp->dnaname); + fprintf(f, " if (length > 0) {\n"); + fprintf(f, " data->%s = MEM_mallocN(length + 1, __func__);\n", dp->dnaname); + fprintf(f, " %s(data->%s, value, length + 1);\n", string_copy_func, dp->dnaname); + fprintf(f, " } else { data->%s = NULL; }\n", dp->dnaname); } else { /* Handle char array properties. */ -- cgit v1.2.3 From 87ae10a05047c026e75519a2e8175fe51f7e8ff2 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Tue, 19 Jul 2022 10:45:01 -0500 Subject: Curves: Hide snapping menu in curves sculpt mode The menu/popover doesn't affect anything in the mode, and precision operations that would use snapping are meant for edit mode anyway. --- release/scripts/startup/bl_ui/space_view3d.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/release/scripts/startup/bl_ui/space_view3d.py b/release/scripts/startup/bl_ui/space_view3d.py index 92dc4138530..1a30c666bcb 100644 --- a/release/scripts/startup/bl_ui/space_view3d.py +++ b/release/scripts/startup/bl_ui/space_view3d.py @@ -603,8 +603,8 @@ class VIEW3D_HT_header(Header): show_snap = True else: if (object_mode not in { - 'SCULPT', 'VERTEX_PAINT', 'WEIGHT_PAINT', 'TEXTURE_PAINT', - 'PAINT_GPENCIL', 'SCULPT_GPENCIL', 'WEIGHT_GPENCIL', 'VERTEX_GPENCIL' + 'SCULPT', 'SCULPT_CURVES', 'VERTEX_PAINT', 'WEIGHT_PAINT', 'TEXTURE_PAINT', + 'PAINT_GPENCIL', 'SCULPT_GPENCIL', 'WEIGHT_GPENCIL', 'VERTEX_GPENCIL', }) or has_pose_mode: show_snap = True else: -- cgit v1.2.3 From 801513efa068d2d181534a599d21e425331b275d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Am=C3=A9lie=20Fondevilla?= Date: Tue, 19 Jul 2022 17:15:10 +0200 Subject: Fix T99732: Crash on F3 in the dopesheet Adding the `FCURVESONLY` filter in the filter function of the Grease Pencil dopesheet prevents calls to F-curves related functions to grease pencil channels, thereby fixing the crash. Reviewed By: antoniov, sybren Maniphest Tasks: T99732 Differential Revision: https://developer.blender.org/D15490 --- source/blender/editors/animation/anim_filter.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/source/blender/editors/animation/anim_filter.c b/source/blender/editors/animation/anim_filter.c index e20932fa53e..d9eeed94868 100644 --- a/source/blender/editors/animation/anim_filter.c +++ b/source/blender/editors/animation/anim_filter.c @@ -1807,11 +1807,13 @@ static size_t animdata_filter_gpencil_data(ListBase *anim_data, ListBase tmp_data = {NULL, NULL}; size_t tmp_items = 0; - /* add gpencil animation channels */ - BEGIN_ANIMFILTER_SUBCHANNELS (EXPANDED_GPD(gpd)) { - tmp_items += animdata_filter_gpencil_layers_data(&tmp_data, ads, gpd, filter_mode); + if (!(filter_mode & ANIMFILTER_FCURVESONLY)) { + /* add gpencil animation channels */ + BEGIN_ANIMFILTER_SUBCHANNELS (EXPANDED_GPD(gpd)) { + tmp_items += animdata_filter_gpencil_layers_data(&tmp_data, ads, gpd, filter_mode); + } + END_ANIMFILTER_SUBCHANNELS; } - END_ANIMFILTER_SUBCHANNELS; /* did we find anything? */ if (tmp_items) { -- cgit v1.2.3 From fb9dc810f17c463496381bbc9fba3b96c6437ce0 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Tue, 19 Jul 2022 16:42:17 +0200 Subject: UI Code Quality: Move view related files to own folder Part of T98518. --- source/blender/editors/interface/CMakeLists.txt | 11 +- source/blender/editors/interface/abstract_view.cc | 109 ---- .../editors/interface/abstract_view_item.cc | 373 -------------- source/blender/editors/interface/grid_view.cc | 464 ----------------- source/blender/editors/interface/interface_view.cc | 190 ------- source/blender/editors/interface/tree_view.cc | 554 --------------------- .../editors/interface/views/abstract_view.cc | 109 ++++ .../editors/interface/views/abstract_view_item.cc | 373 ++++++++++++++ .../blender/editors/interface/views/grid_view.cc | 464 +++++++++++++++++ .../editors/interface/views/interface_view.cc | 190 +++++++ .../blender/editors/interface/views/tree_view.cc | 554 +++++++++++++++++++++ 11 files changed, 1696 insertions(+), 1695 deletions(-) delete mode 100644 source/blender/editors/interface/abstract_view.cc delete mode 100644 source/blender/editors/interface/abstract_view_item.cc delete mode 100644 source/blender/editors/interface/grid_view.cc delete mode 100644 source/blender/editors/interface/interface_view.cc delete mode 100644 source/blender/editors/interface/tree_view.cc create mode 100644 source/blender/editors/interface/views/abstract_view.cc create mode 100644 source/blender/editors/interface/views/abstract_view_item.cc create mode 100644 source/blender/editors/interface/views/grid_view.cc create mode 100644 source/blender/editors/interface/views/interface_view.cc create mode 100644 source/blender/editors/interface/views/tree_view.cc diff --git a/source/blender/editors/interface/CMakeLists.txt b/source/blender/editors/interface/CMakeLists.txt index c6c9f1f80c8..4c206f9c057 100644 --- a/source/blender/editors/interface/CMakeLists.txt +++ b/source/blender/editors/interface/CMakeLists.txt @@ -1,6 +1,7 @@ # SPDX-License-Identifier: GPL-2.0-or-later set(INC + . ../include ../../blenfont ../../blenkernel @@ -25,9 +26,6 @@ set(INC ) set(SRC - abstract_view.cc - abstract_view_item.cc - grid_view.cc interface.cc interface_align.c interface_anim.c @@ -69,15 +67,18 @@ set(SRC interface_templates.c interface_undo.c interface_utils.cc - interface_view.cc interface_widgets.c resources.c - tree_view.cc view2d.cc view2d_draw.cc view2d_edge_pan.cc view2d_gizmo_navigate.cc view2d_ops.cc + views/abstract_view.cc + views/abstract_view_item.cc + views/grid_view.cc + views/interface_view.cc + views/tree_view.cc interface_eyedropper_intern.h interface_intern.h diff --git a/source/blender/editors/interface/abstract_view.cc b/source/blender/editors/interface/abstract_view.cc deleted file mode 100644 index 077c76a08f1..00000000000 --- a/source/blender/editors/interface/abstract_view.cc +++ /dev/null @@ -1,109 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ - -/** \file - * \ingroup edinterface - */ - -#include "interface_intern.h" - -#include "UI_abstract_view.hh" - -namespace blender::ui { - -void AbstractView::register_item(AbstractViewItem &item) -{ - /* Actually modifies the item, not the view. But for the public API it "feels" a bit nicer to - * have the view base class register the items, rather than setting the view on the item. */ - item.view_ = this; -} - -/* ---------------------------------------------------------------------- */ -/** \name View Reconstruction - * \{ */ - -bool AbstractView::is_reconstructed() const -{ - return is_reconstructed_; -} - -void AbstractView::update_from_old(uiBlock &new_block) -{ - uiBlock *old_block = new_block.oldblock; - if (!old_block) { - is_reconstructed_ = true; - return; - } - - uiViewHandle *old_view_handle = ui_block_view_find_matching_in_old_block( - &new_block, reinterpret_cast(this)); - if (old_view_handle == nullptr) { - /* Initial construction, nothing to update. */ - is_reconstructed_ = true; - return; - } - - AbstractView &old_view = reinterpret_cast(*old_view_handle); - - /* Update own persistent data. */ - /* Keep the rename buffer persistent while renaming! The rename button uses the buffer's - * pointer to identify itself over redraws. */ - rename_buffer_ = std::move(old_view.rename_buffer_); - old_view.rename_buffer_ = nullptr; - - update_children_from_old(old_view); - - /* Finished (re-)constructing the tree. */ - is_reconstructed_ = true; -} - -/** \} */ - -/* ---------------------------------------------------------------------- */ -/** \name Default implementations of virtual functions - * \{ */ - -bool AbstractView::listen(const wmNotifier & /*notifier*/) const -{ - /* Nothing by default. */ - return false; -} - -/** \} */ - -/* ---------------------------------------------------------------------- */ -/** \name Renaming - * \{ */ - -bool AbstractView::is_renaming() const -{ - return rename_buffer_ != nullptr; -} - -bool AbstractView::begin_renaming() -{ - if (is_renaming()) { - return false; - } - - rename_buffer_ = std::make_unique(); - return true; -} - -void AbstractView::end_renaming() -{ - BLI_assert(is_renaming()); - rename_buffer_ = nullptr; -} - -Span AbstractView::get_rename_buffer() const -{ - return *rename_buffer_; -} -MutableSpan AbstractView::get_rename_buffer() -{ - return *rename_buffer_; -} - -/** \} */ - -} // namespace blender::ui diff --git a/source/blender/editors/interface/abstract_view_item.cc b/source/blender/editors/interface/abstract_view_item.cc deleted file mode 100644 index f73183d07e9..00000000000 --- a/source/blender/editors/interface/abstract_view_item.cc +++ /dev/null @@ -1,373 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ - -/** \file - * \ingroup edinterface - */ - -#include "BKE_context.h" - -#include "BLI_listbase.h" -#include "BLI_string.h" - -#include "WM_api.h" - -#include "UI_interface.h" -#include "interface_intern.h" - -#include "UI_abstract_view.hh" - -namespace blender::ui { - -/* ---------------------------------------------------------------------- */ -/** \name View Reconstruction - * \{ */ - -void AbstractViewItem::update_from_old(const AbstractViewItem &old) -{ - is_active_ = old.is_active_; - is_renaming_ = old.is_renaming_; -} - -/** \} */ - -/* ---------------------------------------------------------------------- */ -/** \name Renaming - * \{ */ - -bool AbstractViewItem::supports_renaming() const -{ - /* No renaming by default. */ - return false; -} -bool AbstractViewItem::rename(StringRefNull /*new_name*/) -{ - /* No renaming by default. */ - return false; -} - -StringRef AbstractViewItem::get_rename_string() const -{ - /* No rename string by default. */ - return {}; -} - -bool AbstractViewItem::is_renaming() const -{ - return is_renaming_; -} - -void AbstractViewItem::begin_renaming() -{ - AbstractView &view = get_view(); - if (view.is_renaming() || !supports_renaming()) { - return; - } - - if (view.begin_renaming()) { - is_renaming_ = true; - } - - StringRef initial_str = get_rename_string(); - std::copy(std::begin(initial_str), std::end(initial_str), std::begin(view.get_rename_buffer())); -} - -void AbstractViewItem::rename_apply() -{ - const AbstractView &view = get_view(); - rename(view.get_rename_buffer().data()); - end_renaming(); -} - -void AbstractViewItem::end_renaming() -{ - if (!is_renaming()) { - return; - } - - is_renaming_ = false; - - AbstractView &view = get_view(); - view.end_renaming(); -} - -static AbstractViewItem *find_item_from_rename_button(const uiBut &rename_but) -{ - /* A minimal sanity check, can't do much more here. */ - BLI_assert(rename_but.type == UI_BTYPE_TEXT && rename_but.poin); - - LISTBASE_FOREACH (uiBut *, but, &rename_but.block->buttons) { - if (but->type != UI_BTYPE_VIEW_ITEM) { - continue; - } - - uiButViewItem *view_item_but = (uiButViewItem *)but; - AbstractViewItem *item = reinterpret_cast(view_item_but->view_item); - const AbstractView &view = item->get_view(); - - if (item->is_renaming() && (view.get_rename_buffer().data() == rename_but.poin)) { - return item; - } - } - - return nullptr; -} - -static void rename_button_fn(bContext *UNUSED(C), void *arg, char *UNUSED(origstr)) -{ - const uiBut *rename_but = static_cast(arg); - AbstractViewItem *item = find_item_from_rename_button(*rename_but); - BLI_assert(item); - item->rename_apply(); -} - -void AbstractViewItem::add_rename_button(uiBlock &block) -{ - AbstractView &view = get_view(); - uiBut *rename_but = uiDefBut(&block, - UI_BTYPE_TEXT, - 1, - "", - 0, - 0, - UI_UNIT_X * 10, - UI_UNIT_Y, - view.get_rename_buffer().data(), - 1.0f, - view.get_rename_buffer().size(), - 0, - 0, - ""); - - /* Gotta be careful with what's passed to the `arg1` here. Any view data will be freed once the - * callback is executed. */ - UI_but_func_rename_set(rename_but, rename_button_fn, rename_but); - UI_but_flag_disable(rename_but, UI_BUT_UNDO); - - const bContext *evil_C = reinterpret_cast(block.evil_C); - ARegion *region = CTX_wm_region(evil_C); - /* Returns false if the button was removed. */ - if (UI_but_active_only(evil_C, region, &block, rename_but) == false) { - end_renaming(); - } -} - -/** \} */ - -/* ---------------------------------------------------------------------- */ -/** \name Context Menu - * \{ */ - -void AbstractViewItem::build_context_menu(bContext & /*C*/, uiLayout & /*column*/) const -{ - /* No context menu by default. */ -} - -/** \} */ - -/* ---------------------------------------------------------------------- */ -/** \name Drag 'n Drop - * \{ */ - -std::unique_ptr AbstractViewItem::create_drag_controller() const -{ - /* There's no drag controller (and hence no drag support) by default. */ - return nullptr; -} - -std::unique_ptr AbstractViewItem::create_drop_controller() const -{ - /* There's no drop controller (and hence no drop support) by default. */ - return nullptr; -} - -AbstractViewItemDragController::AbstractViewItemDragController(AbstractView &view) : view_(view) -{ -} - -void AbstractViewItemDragController::on_drag_start() -{ - /* Do nothing by default. */ -} - -AbstractViewItemDropController::AbstractViewItemDropController(AbstractView &view) : view_(view) -{ -} - -/** \} */ - -/* ---------------------------------------------------------------------- */ -/** \name General Getters & Setters - * \{ */ - -AbstractView &AbstractViewItem::get_view() const -{ - if (UNLIKELY(!view_)) { - throw std::runtime_error( - "Invalid state, item must be registered through AbstractView::register_item()"); - } - return *view_; -} - -bool AbstractViewItem::is_active() const -{ - BLI_assert_msg(get_view().is_reconstructed(), - "State can't be queried until reconstruction is completed"); - return is_active_; -} - -/** \} */ - -} // namespace blender::ui - -/* ---------------------------------------------------------------------- */ -/** \name C-API - * \{ */ - -namespace blender::ui { - -/** - * Helper class to provide a higher level public (C-)API. Has access to private/protected view item - * members and ensures some invariants that way. - */ -class ViewItemAPIWrapper { - public: - static bool matches(const AbstractViewItem &a, const AbstractViewItem &b) - { - if (typeid(a) != typeid(b)) { - return false; - } - /* TODO should match the view as well. */ - return a.matches(b); - } - - static bool can_rename(const AbstractViewItem &item) - { - const AbstractView &view = item.get_view(); - return !view.is_renaming() && item.supports_renaming(); - } - - static bool drag_start(bContext &C, const AbstractViewItem &item) - { - const std::unique_ptr drag_controller = - item.create_drag_controller(); - if (!drag_controller) { - return false; - } - - WM_event_start_drag(&C, - ICON_NONE, - drag_controller->get_drag_type(), - drag_controller->create_drag_data(), - 0, - WM_DRAG_FREE_DATA); - drag_controller->on_drag_start(); - - return true; - } - - static bool can_drop(const AbstractViewItem &item, - const wmDrag &drag, - const char **r_disabled_hint) - { - const std::unique_ptr drop_controller = - item.create_drop_controller(); - if (!drop_controller) { - return false; - } - - return drop_controller->can_drop(drag, r_disabled_hint); - } - - static std::string drop_tooltip(const AbstractViewItem &item, const wmDrag &drag) - { - const std::unique_ptr drop_controller = - item.create_drop_controller(); - if (!drop_controller) { - return {}; - } - - return drop_controller->drop_tooltip(drag); - } - - static bool drop_handle(bContext &C, const AbstractViewItem &item, const ListBase &drags) - { - std::unique_ptr drop_controller = - item.create_drop_controller(); - - const char *disabled_hint_dummy = nullptr; - LISTBASE_FOREACH (const wmDrag *, drag, &drags) { - if (drop_controller->can_drop(*drag, &disabled_hint_dummy)) { - return drop_controller->on_drop(&C, *drag); - } - } - - return false; - } -}; - -} // namespace blender::ui - -using namespace blender::ui; - -bool UI_view_item_is_active(const uiViewItemHandle *item_handle) -{ - const AbstractViewItem &item = reinterpret_cast(*item_handle); - return item.is_active(); -} - -bool UI_view_item_matches(const uiViewItemHandle *a_handle, const uiViewItemHandle *b_handle) -{ - const AbstractViewItem &a = reinterpret_cast(*a_handle); - const AbstractViewItem &b = reinterpret_cast(*b_handle); - return ViewItemAPIWrapper::matches(a, b); -} - -bool UI_view_item_can_rename(const uiViewItemHandle *item_handle) -{ - const AbstractViewItem &item = reinterpret_cast(*item_handle); - return ViewItemAPIWrapper::can_rename(item); -} - -void UI_view_item_begin_rename(uiViewItemHandle *item_handle) -{ - AbstractViewItem &item = reinterpret_cast(*item_handle); - item.begin_renaming(); -} - -void UI_view_item_context_menu_build(bContext *C, - const uiViewItemHandle *item_handle, - uiLayout *column) -{ - const AbstractViewItem &item = reinterpret_cast(*item_handle); - item.build_context_menu(*C, *column); -} - -bool UI_view_item_drag_start(bContext *C, const uiViewItemHandle *item_) -{ - const AbstractViewItem &item = reinterpret_cast(*item_); - return ViewItemAPIWrapper::drag_start(*C, item); -} - -bool UI_view_item_can_drop(const uiViewItemHandle *item_, - const wmDrag *drag, - const char **r_disabled_hint) -{ - const AbstractViewItem &item = reinterpret_cast(*item_); - return ViewItemAPIWrapper::can_drop(item, *drag, r_disabled_hint); -} - -char *UI_view_item_drop_tooltip(const uiViewItemHandle *item_, const wmDrag *drag) -{ - const AbstractViewItem &item = reinterpret_cast(*item_); - - const std::string tooltip = ViewItemAPIWrapper::drop_tooltip(item, *drag); - return tooltip.empty() ? nullptr : BLI_strdup(tooltip.c_str()); -} - -bool UI_view_item_drop_handle(bContext *C, const uiViewItemHandle *item_, const ListBase *drags) -{ - const AbstractViewItem &item = reinterpret_cast(*item_); - return ViewItemAPIWrapper::drop_handle(*C, item, *drags); -} - -/** \} */ diff --git a/source/blender/editors/interface/grid_view.cc b/source/blender/editors/interface/grid_view.cc deleted file mode 100644 index 37fbb33f83b..00000000000 --- a/source/blender/editors/interface/grid_view.cc +++ /dev/null @@ -1,464 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ - -/** \file - * \ingroup edinterface - */ - -#include -#include - -#include "BLI_index_range.hh" - -#include "WM_types.h" - -#include "UI_interface.h" -#include "interface_intern.h" - -#include "UI_grid_view.hh" - -namespace blender::ui { - -/* ---------------------------------------------------------------------- */ - -AbstractGridView::AbstractGridView() : style_(UI_preview_tile_size_x(), UI_preview_tile_size_y()) -{ -} - -AbstractGridViewItem &AbstractGridView::add_item(std::unique_ptr item) -{ - items_.append(std::move(item)); - - AbstractGridViewItem &added_item = *items_.last(); - added_item.view_ = this; - - item_map_.add(added_item.identifier_, &added_item); - - return added_item; -} - -void AbstractGridView::foreach_item(ItemIterFn iter_fn) const -{ - for (const auto &item_ptr : items_) { - iter_fn(*item_ptr); - } -} - -AbstractGridViewItem *AbstractGridView::find_matching_item( - const AbstractGridViewItem &item_to_match, const AbstractGridView &view_to_search_in) const -{ - AbstractGridViewItem *const *match = view_to_search_in.item_map_.lookup_ptr( - item_to_match.identifier_); - BLI_assert(!match || item_to_match.matches(**match)); - - return match ? *match : nullptr; -} - -void AbstractGridView::change_state_delayed() -{ - BLI_assert_msg( - is_reconstructed(), - "These state changes are supposed to be delayed until reconstruction is completed"); - foreach_item([](AbstractGridViewItem &item) { item.change_state_delayed(); }); -} - -void AbstractGridView::update_children_from_old(const AbstractView &old_view) -{ - const AbstractGridView &old_grid_view = dynamic_cast(old_view); - - foreach_item([this, &old_grid_view](AbstractGridViewItem &new_item) { - const AbstractGridViewItem *matching_old_item = find_matching_item(new_item, old_grid_view); - if (!matching_old_item) { - return; - } - - new_item.update_from_old(*matching_old_item); - }); -} - -const GridViewStyle &AbstractGridView::get_style() const -{ - return style_; -} - -int AbstractGridView::get_item_count() const -{ - return items_.size(); -} - -GridViewStyle::GridViewStyle(int width, int height) : tile_width(width), tile_height(height) -{ -} - -/* ---------------------------------------------------------------------- */ - -AbstractGridViewItem::AbstractGridViewItem(StringRef identifier) : identifier_(identifier) -{ -} - -bool AbstractGridViewItem::matches(const AbstractViewItem &other) const -{ - const AbstractGridViewItem &other_grid_item = dynamic_cast(other); - return identifier_ == other_grid_item.identifier_; -} - -void AbstractGridViewItem::grid_tile_click_fn(struct bContext * /*C*/, - void *but_arg1, - void * /*arg2*/) -{ - uiButViewItem *view_item_but = (uiButViewItem *)but_arg1; - AbstractGridViewItem &grid_item = reinterpret_cast( - *view_item_but->view_item); - - grid_item.activate(); -} - -void AbstractGridViewItem::add_grid_tile_button(uiBlock &block) -{ - const GridViewStyle &style = get_view().get_style(); - view_item_but_ = (uiButViewItem *)uiDefBut(&block, - UI_BTYPE_VIEW_ITEM, - 0, - "", - 0, - 0, - style.tile_width, - style.tile_height, - nullptr, - 0, - 0, - 0, - 0, - ""); - - view_item_but_->view_item = reinterpret_cast(this); - UI_but_func_set(&view_item_but_->but, grid_tile_click_fn, view_item_but_, nullptr); -} - -void AbstractGridViewItem::on_activate() -{ - /* Do nothing by default. */ -} - -std::optional AbstractGridViewItem::should_be_active() const -{ - return std::nullopt; -} - -void AbstractGridViewItem::change_state_delayed() -{ - const std::optional should_be_active = this->should_be_active(); - if (should_be_active.has_value() && *should_be_active) { - activate(); - } -} - -void AbstractGridViewItem::activate() -{ - BLI_assert_msg(get_view().is_reconstructed(), - "Item activation can't be done until reconstruction is completed"); - - if (is_active()) { - return; - } - - /* Deactivate other items in the tree. */ - get_view().foreach_item([](auto &item) { item.deactivate(); }); - - on_activate(); - - is_active_ = true; -} - -void AbstractGridViewItem::deactivate() -{ - is_active_ = false; -} - -const AbstractGridView &AbstractGridViewItem::get_view() const -{ - if (UNLIKELY(!view_)) { - throw std::runtime_error( - "Invalid state, item must be added through AbstractGridView::add_item()"); - } - return *view_; -} - -/* ---------------------------------------------------------------------- */ - -/** - * Helper for only adding layout items for grid items that are actually in view. 3 main functions: - * - #is_item_visible(): Query if an item of a given index is visible in the view (others should be - * skipped when building the layout). - * - #fill_layout_before_visible(): Add empty space to the layout before a visible row is drawn, so - * the layout height is the same as if all items were added (important to get the correct scroll - * height). - * - #fill_layout_after_visible(): Same thing, just adds empty space for after the last visible - * row. - * - * Does two assumptions: - * - Top-to-bottom flow (ymax = 0 and ymin < 0). If that's not good enough, View2D should - * probably provide queries for the scroll offset. - * - Only vertical scrolling. For horizontal scrolling, spacers would have to be added on the - * side(s) as well. - */ -class BuildOnlyVisibleButtonsHelper { - const View2D &v2d_; - const AbstractGridView &grid_view_; - const GridViewStyle &style_; - const int cols_per_row_ = 0; - /* Indices of items within the view. Calculated by constructor */ - IndexRange visible_items_range_{}; - - public: - BuildOnlyVisibleButtonsHelper(const View2D &v2d, - const AbstractGridView &grid_view, - int cols_per_row); - - bool is_item_visible(int item_idx) const; - void fill_layout_before_visible(uiBlock &block) const; - void fill_layout_after_visible(uiBlock &block) const; - - private: - IndexRange get_visible_range() const; - void add_spacer_button(uiBlock &block, int row_count) const; -}; - -BuildOnlyVisibleButtonsHelper::BuildOnlyVisibleButtonsHelper(const View2D &v2d, - const AbstractGridView &grid_view, - const int cols_per_row) - : v2d_(v2d), grid_view_(grid_view), style_(grid_view.get_style()), cols_per_row_(cols_per_row) -{ - visible_items_range_ = get_visible_range(); -} - -IndexRange BuildOnlyVisibleButtonsHelper::get_visible_range() const -{ - int first_idx_in_view = 0; - int max_items_in_view = 0; - - const float scroll_ofs_y = abs(v2d_.cur.ymax - v2d_.tot.ymax); - if (!IS_EQF(scroll_ofs_y, 0)) { - const int scrolled_away_rows = (int)scroll_ofs_y / style_.tile_height; - - first_idx_in_view = scrolled_away_rows * cols_per_row_; - } - - const float view_height = BLI_rctf_size_y(&v2d_.cur); - const int count_rows_in_view = std::max(round_fl_to_int(view_height / style_.tile_height), 1); - max_items_in_view = (count_rows_in_view + 1) * cols_per_row_; - - BLI_assert(max_items_in_view > 0); - return IndexRange(first_idx_in_view, max_items_in_view); -} - -bool BuildOnlyVisibleButtonsHelper::is_item_visible(const int item_idx) const -{ - return visible_items_range_.contains(item_idx); -} - -void BuildOnlyVisibleButtonsHelper::fill_layout_before_visible(uiBlock &block) const -{ - const float scroll_ofs_y = abs(v2d_.cur.ymax - v2d_.tot.ymax); - - if (IS_EQF(scroll_ofs_y, 0)) { - return; - } - - const int scrolled_away_rows = (int)scroll_ofs_y / style_.tile_height; - add_spacer_button(block, scrolled_away_rows); -} - -void BuildOnlyVisibleButtonsHelper::fill_layout_after_visible(uiBlock &block) const -{ - const int last_item_idx = grid_view_.get_item_count() - 1; - const int last_visible_idx = visible_items_range_.last(); - - if (last_item_idx > last_visible_idx) { - const int remaining_rows = (cols_per_row_ > 0) ? - (last_item_idx - last_visible_idx) / cols_per_row_ : - 0; - BuildOnlyVisibleButtonsHelper::add_spacer_button(block, remaining_rows); - } -} - -void BuildOnlyVisibleButtonsHelper::add_spacer_button(uiBlock &block, const int row_count) const -{ - /* UI code only supports button dimensions of `signed short` size, the layout height we want to - * fill may be bigger than that. So add multiple labels of the maximum size if necessary. */ - for (int remaining_rows = row_count; remaining_rows > 0;) { - const short row_count_this_iter = std::min( - std::numeric_limits::max() / style_.tile_height, remaining_rows); - - uiDefBut(&block, - UI_BTYPE_LABEL, - 0, - "", - 0, - 0, - UI_UNIT_X, - row_count_this_iter * style_.tile_height, - nullptr, - 0, - 0, - 0, - 0, - ""); - remaining_rows -= row_count_this_iter; - } -} - -/* ---------------------------------------------------------------------- */ - -class GridViewLayoutBuilder { - uiBlock &block_; - - friend class GridViewBuilder; - - public: - GridViewLayoutBuilder(uiBlock &block); - - void build_from_view(const AbstractGridView &grid_view, const View2D &v2d) const; - - private: - void build_grid_tile(uiLayout &grid_layout, AbstractGridViewItem &item) const; - - uiLayout *current_layout() const; -}; - -GridViewLayoutBuilder::GridViewLayoutBuilder(uiBlock &block) : block_(block) -{ -} - -void GridViewLayoutBuilder::build_grid_tile(uiLayout &grid_layout, - AbstractGridViewItem &item) const -{ - uiLayout *overlap = uiLayoutOverlap(&grid_layout); - - item.add_grid_tile_button(block_); - item.build_grid_tile(*uiLayoutRow(overlap, false)); -} - -void GridViewLayoutBuilder::build_from_view(const AbstractGridView &grid_view, - const View2D &v2d) const -{ - uiLayout *prev_layout = current_layout(); - - uiLayout &layout = *uiLayoutColumn(current_layout(), false); - const GridViewStyle &style = grid_view.get_style(); - - const int cols_per_row = std::max(uiLayoutGetWidth(&layout) / style.tile_width, 1); - - BuildOnlyVisibleButtonsHelper build_visible_helper(v2d, grid_view, cols_per_row); - - build_visible_helper.fill_layout_before_visible(block_); - - /* Use `-cols_per_row` because the grid layout uses a multiple of the passed absolute value for - * the number of columns then, rather than distributing the number of items evenly over rows and - * stretching the items to fit (see #uiLayoutItemGridFlow.columns_len). */ - uiLayout *grid_layout = uiLayoutGridFlow(&layout, true, -cols_per_row, true, true, true); - - int item_idx = 0; - grid_view.foreach_item([&](AbstractGridViewItem &item) { - /* Skip if item isn't visible. */ - if (!build_visible_helper.is_item_visible(item_idx)) { - item_idx++; - return; - } - - build_grid_tile(*grid_layout, item); - item_idx++; - }); - - /* If there are not enough items to fill the layout, add padding items so the layout doesn't - * stretch over the entire width. */ - if (grid_view.get_item_count() < cols_per_row) { - for (int padding_item_idx = 0; padding_item_idx < (cols_per_row - grid_view.get_item_count()); - padding_item_idx++) { - uiItemS(grid_layout); - } - } - - UI_block_layout_set_current(&block_, prev_layout); - - build_visible_helper.fill_layout_after_visible(block_); -} - -uiLayout *GridViewLayoutBuilder::current_layout() const -{ - return block_.curlayout; -} - -/* ---------------------------------------------------------------------- */ - -GridViewBuilder::GridViewBuilder(uiBlock &block) : block_(block) -{ -} - -void GridViewBuilder::build_grid_view(AbstractGridView &grid_view, const View2D &v2d) -{ - grid_view.build_items(); - grid_view.update_from_old(block_); - grid_view.change_state_delayed(); - - GridViewLayoutBuilder builder(block_); - builder.build_from_view(grid_view, v2d); -} - -/* ---------------------------------------------------------------------- */ - -PreviewGridItem::PreviewGridItem(StringRef identifier, StringRef label, int preview_icon_id) - : AbstractGridViewItem(identifier), label(label), preview_icon_id(preview_icon_id) -{ -} - -void PreviewGridItem::build_grid_tile(uiLayout &layout) const -{ - const GridViewStyle &style = get_view().get_style(); - uiBlock *block = uiLayoutGetBlock(&layout); - - uiBut *but = uiDefBut(block, - UI_BTYPE_PREVIEW_TILE, - 0, - label.c_str(), - 0, - 0, - style.tile_width, - style.tile_height, - nullptr, - 0, - 0, - 0, - 0, - ""); - ui_def_but_icon(but, - preview_icon_id, - /* NOLINTNEXTLINE: bugprone-suspicious-enum-usage */ - UI_HAS_ICON | UI_BUT_ICON_PREVIEW); -} - -void PreviewGridItem::set_on_activate_fn(ActivateFn fn) -{ - activate_fn_ = fn; -} - -void PreviewGridItem::set_is_active_fn(IsActiveFn fn) -{ - is_active_fn_ = fn; -} - -void PreviewGridItem::on_activate() -{ - if (activate_fn_) { - activate_fn_(*this); - } -} - -std::optional PreviewGridItem::should_be_active() const -{ - if (is_active_fn_) { - return is_active_fn_(); - } - return std::nullopt; -} - -} // namespace blender::ui diff --git a/source/blender/editors/interface/interface_view.cc b/source/blender/editors/interface/interface_view.cc deleted file mode 100644 index b35f6d2c969..00000000000 --- a/source/blender/editors/interface/interface_view.cc +++ /dev/null @@ -1,190 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ - -/** \file - * \ingroup edinterface - * - * This part of the UI-View API is mostly needed to support persistent state of items within the - * view. Views are stored in #uiBlock's, and kept alive with it until after the next redraw. So we - * can compare the old view items with the new view items and keep state persistent for matching - * ones. - */ - -#include -#include -#include - -#include "DNA_screen_types.h" - -#include "BKE_screen.h" - -#include "BLI_listbase.h" - -#include "ED_screen.h" - -#include "interface_intern.h" - -#include "UI_interface.hh" - -#include "UI_abstract_view.hh" -#include "UI_grid_view.hh" -#include "UI_tree_view.hh" - -using namespace blender; -using namespace blender::ui; - -/** - * Wrapper to store views in a #ListBase, addressable via an identifier. - */ -struct ViewLink : public Link { - std::string idname; - std::unique_ptr view; -}; - -template -static T *ui_block_add_view_impl(uiBlock &block, - StringRef idname, - std::unique_ptr view) -{ - ViewLink *view_link = MEM_new(__func__); - BLI_addtail(&block.views, view_link); - - view_link->view = std::move(view); - view_link->idname = idname; - - return dynamic_cast(view_link->view.get()); -} - -AbstractGridView *UI_block_add_view(uiBlock &block, - StringRef idname, - std::unique_ptr grid_view) -{ - return ui_block_add_view_impl(block, idname, std::move(grid_view)); -} - -AbstractTreeView *UI_block_add_view(uiBlock &block, - StringRef idname, - std::unique_ptr tree_view) -{ - return ui_block_add_view_impl(block, idname, std::move(tree_view)); -} - -void ui_block_free_views(uiBlock *block) -{ - LISTBASE_FOREACH_MUTABLE (ViewLink *, link, &block->views) { - MEM_delete(link); - } -} - -void UI_block_views_listen(const uiBlock *block, const wmRegionListenerParams *listener_params) -{ - ARegion *region = listener_params->region; - - LISTBASE_FOREACH (ViewLink *, view_link, &block->views) { - if (view_link->view->listen(*listener_params->notifier)) { - ED_region_tag_redraw(region); - } - } -} - -uiViewItemHandle *UI_block_view_find_item_at(const ARegion *region, const int xy[2]) -{ - uiButViewItem *item_but = (uiButViewItem *)ui_view_item_find_mouse_over(region, xy); - if (!item_but) { - return nullptr; - } - - return item_but->view_item; -} - -uiViewItemHandle *UI_block_view_find_active_item(const ARegion *region) -{ - uiButViewItem *item_but = (uiButViewItem *)ui_view_item_find_active(region); - if (!item_but) { - return nullptr; - } - - return item_but->view_item; -} - -static StringRef ui_block_view_find_idname(const uiBlock &block, const AbstractView &view) -{ - /* First get the idname the of the view we're looking for. */ - LISTBASE_FOREACH (ViewLink *, view_link, &block.views) { - if (view_link->view.get() == &view) { - return view_link->idname; - } - } - - return {}; -} - -template -static T *ui_block_view_find_matching_in_old_block_impl(const uiBlock &new_block, - const T &new_view) -{ - uiBlock *old_block = new_block.oldblock; - if (!old_block) { - return nullptr; - } - - StringRef idname = ui_block_view_find_idname(new_block, new_view); - if (idname.is_empty()) { - return nullptr; - } - - LISTBASE_FOREACH (ViewLink *, old_view_link, &old_block->views) { - if (old_view_link->idname == idname) { - return dynamic_cast(old_view_link->view.get()); - } - } - - return nullptr; -} - -uiViewHandle *ui_block_view_find_matching_in_old_block(const uiBlock *new_block, - const uiViewHandle *new_view_handle) -{ - BLI_assert(new_block && new_view_handle); - const AbstractView &new_view = reinterpret_cast(*new_view_handle); - - AbstractView *old_view = ui_block_view_find_matching_in_old_block_impl(*new_block, new_view); - return reinterpret_cast(old_view); -} - -uiButViewItem *ui_block_view_find_matching_view_item_but_in_old_block( - const uiBlock *new_block, const uiViewItemHandle *new_item_handle) -{ - uiBlock *old_block = new_block->oldblock; - if (!old_block) { - return nullptr; - } - - const AbstractViewItem &new_item = *reinterpret_cast(new_item_handle); - const AbstractView *old_view = ui_block_view_find_matching_in_old_block_impl( - *new_block, new_item.get_view()); - if (!old_view) { - return nullptr; - } - - LISTBASE_FOREACH (uiBut *, old_but, &old_block->buttons) { - if (old_but->type != UI_BTYPE_VIEW_ITEM) { - continue; - } - uiButViewItem *old_item_but = (uiButViewItem *)old_but; - if (!old_item_but->view_item) { - continue; - } - AbstractViewItem &old_item = *reinterpret_cast(old_item_but->view_item); - /* Check if the item is from the expected view. */ - if (&old_item.get_view() != old_view) { - continue; - } - - if (UI_view_item_matches(reinterpret_cast(&new_item), - reinterpret_cast(&old_item))) { - return old_item_but; - } - } - - return nullptr; -} diff --git a/source/blender/editors/interface/tree_view.cc b/source/blender/editors/interface/tree_view.cc deleted file mode 100644 index c224226ba17..00000000000 --- a/source/blender/editors/interface/tree_view.cc +++ /dev/null @@ -1,554 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ - -/** \file - * \ingroup edinterface - */ - -#include "DNA_userdef_types.h" -#include "DNA_windowmanager_types.h" - -#include "BKE_context.h" - -#include "BLT_translation.h" - -#include "interface_intern.h" - -#include "UI_interface.h" - -#include "WM_api.h" -#include "WM_types.h" - -#include "UI_tree_view.hh" - -namespace blender::ui { - -/* ---------------------------------------------------------------------- */ - -/** - * Add a tree-item to the container. This is the only place where items should be added, it - * handles important invariants! - */ -AbstractTreeViewItem &TreeViewItemContainer::add_tree_item( - std::unique_ptr item) -{ - children_.append(std::move(item)); - - /* The first item that will be added to the root sets this. */ - if (root_ == nullptr) { - root_ = this; - } - AbstractTreeView &tree_view = static_cast(*root_); - AbstractTreeViewItem &added_item = *children_.last(); - added_item.root_ = root_; - tree_view.register_item(added_item); - - if (root_ != this) { - /* Any item that isn't the root can be assumed to the a #AbstractTreeViewItem. Not entirely - * nice to static_cast this, but well... */ - added_item.parent_ = static_cast(this); - } - - return added_item; -} - -void TreeViewItemContainer::foreach_item_recursive(ItemIterFn iter_fn, IterOptions options) const -{ - for (const auto &child : children_) { - iter_fn(*child); - if (bool(options & IterOptions::SkipCollapsed) && child->is_collapsed()) { - continue; - } - - child->foreach_item_recursive(iter_fn, options); - } -} - -/* ---------------------------------------------------------------------- */ - -void AbstractTreeView::foreach_item(ItemIterFn iter_fn, IterOptions options) const -{ - foreach_item_recursive(iter_fn, options); -} - -void AbstractTreeView::update_children_from_old(const AbstractView &old_view) -{ - const AbstractTreeView &old_tree_view = dynamic_cast(old_view); - - update_children_from_old_recursive(*this, old_tree_view); -} - -void AbstractTreeView::update_children_from_old_recursive(const TreeViewOrItem &new_items, - const TreeViewOrItem &old_items) -{ - for (const auto &new_item : new_items.children_) { - AbstractTreeViewItem *matching_old_item = find_matching_child(*new_item, old_items); - if (!matching_old_item) { - continue; - } - - new_item->update_from_old(*matching_old_item); - - /* Recurse into children of the matched item. */ - update_children_from_old_recursive(*new_item, *matching_old_item); - } -} - -AbstractTreeViewItem *AbstractTreeView::find_matching_child( - const AbstractTreeViewItem &lookup_item, const TreeViewOrItem &items) -{ - for (const auto &iter_item : items.children_) { - if (lookup_item.matches_single(*iter_item)) { - /* We have a matching item! */ - return iter_item.get(); - } - } - - return nullptr; -} - -void AbstractTreeView::change_state_delayed() -{ - BLI_assert_msg( - is_reconstructed(), - "These state changes are supposed to be delayed until reconstruction is completed"); - foreach_item([](AbstractTreeViewItem &item) { item.change_state_delayed(); }); -} - -/* ---------------------------------------------------------------------- */ - -void AbstractTreeViewItem::tree_row_click_fn(struct bContext * /*C*/, - void *but_arg1, - void * /*arg2*/) -{ - uiButViewItem *item_but = (uiButViewItem *)but_arg1; - AbstractTreeViewItem &tree_item = reinterpret_cast(*item_but->view_item); - - tree_item.activate(); - /* Not only activate the item, also show its children. Maybe this should be optional, or - * controlled by the specific tree-view. */ - tree_item.set_collapsed(false); -} - -void AbstractTreeViewItem::add_treerow_button(uiBlock &block) -{ - /* For some reason a width > (UI_UNIT_X * 2) make the layout system use all available width. */ - view_item_but_ = (uiButViewItem *)uiDefBut( - &block, UI_BTYPE_VIEW_ITEM, 0, "", 0, 0, UI_UNIT_X * 10, UI_UNIT_Y, nullptr, 0, 0, 0, 0, ""); - - view_item_but_->view_item = reinterpret_cast(this); - UI_but_func_set(&view_item_but_->but, tree_row_click_fn, view_item_but_, nullptr); -} - -void AbstractTreeViewItem::add_indent(uiLayout &row) const -{ - uiBlock *block = uiLayoutGetBlock(&row); - uiLayout *subrow = uiLayoutRow(&row, true); - uiLayoutSetFixedSize(subrow, true); - - const float indent_size = count_parents() * UI_DPI_ICON_SIZE; - uiDefBut(block, UI_BTYPE_SEPR, 0, "", 0, 0, indent_size, 0, nullptr, 0.0, 0.0, 0, 0, ""); - - /* Indent items without collapsing icon some more within their parent. Makes it clear that they - * are actually nested and not just a row at the same level without a chevron. */ - if (!is_collapsible() && parent_) { - uiDefBut(block, UI_BTYPE_SEPR, 0, "", 0, 0, 0.2f * UI_UNIT_X, 0, nullptr, 0.0, 0.0, 0, 0, ""); - } - - /* Restore. */ - UI_block_layout_set_current(block, &row); -} - -void AbstractTreeViewItem::collapse_chevron_click_fn(struct bContext *C, - void * /*but_arg1*/, - void * /*arg2*/) -{ - /* There's no data we could pass to this callback. It must be either the button itself or a - * consistent address to match buttons over redraws. So instead of passing it somehow, just - * lookup the hovered item via context here. */ - - const wmWindow *win = CTX_wm_window(C); - const ARegion *region = CTX_wm_region(C); - uiViewItemHandle *hovered_item_handle = UI_block_view_find_item_at(region, win->eventstate->xy); - - AbstractTreeViewItem *hovered_item = from_item_handle(hovered_item_handle); - BLI_assert(hovered_item != nullptr); - - hovered_item->toggle_collapsed(); - /* When collapsing an item with an active child, make this collapsed item active instead so the - * active item stays visible. */ - if (hovered_item->has_active_child()) { - hovered_item->activate(); - } -} - -bool AbstractTreeViewItem::is_collapse_chevron_but(const uiBut *but) -{ - return but->type == UI_BTYPE_BUT_TOGGLE && ELEM(but->icon, ICON_TRIA_RIGHT, ICON_TRIA_DOWN) && - (but->func == collapse_chevron_click_fn); -} - -void AbstractTreeViewItem::add_collapse_chevron(uiBlock &block) const -{ - if (!is_collapsible()) { - return; - } - - const BIFIconID icon = is_collapsed() ? ICON_TRIA_RIGHT : ICON_TRIA_DOWN; - uiBut *but = uiDefIconBut( - &block, UI_BTYPE_BUT_TOGGLE, 0, icon, 0, 0, UI_UNIT_X, UI_UNIT_Y, nullptr, 0, 0, 0, 0, ""); - /* Note that we're passing the tree-row button here, not the chevron one. */ - UI_but_func_set(but, collapse_chevron_click_fn, nullptr, nullptr); - UI_but_flag_disable(but, UI_BUT_UNDO); - - /* Check if the query for the button matches the created button. */ - BLI_assert(is_collapse_chevron_but(but)); -} - -void AbstractTreeViewItem::add_rename_button(uiLayout &row) -{ - uiBlock *block = uiLayoutGetBlock(&row); - eUIEmbossType previous_emboss = UI_block_emboss_get(block); - - uiLayoutRow(&row, false); - /* Enable emboss for the text button. */ - UI_block_emboss_set(block, UI_EMBOSS); - - AbstractViewItem::add_rename_button(*block); - - UI_block_emboss_set(block, previous_emboss); - UI_block_layout_set_current(block, &row); -} - -bool AbstractTreeViewItem::has_active_child() const -{ - bool found = false; - foreach_item_recursive([&found](const AbstractTreeViewItem &item) { - if (item.is_active()) { - found = true; - } - }); - - return found; -} - -void AbstractTreeViewItem::on_activate() -{ - /* Do nothing by default. */ -} - -std::optional AbstractTreeViewItem::should_be_active() const -{ - return std::nullopt; -} - -bool AbstractTreeViewItem::supports_collapsing() const -{ - return true; -} - -StringRef AbstractTreeViewItem::get_rename_string() const -{ - return label_; -} - -bool AbstractTreeViewItem::rename(StringRefNull new_name) -{ - /* It is important to update the label after renaming, so #AbstractTreeViewItem::matches_single() - * recognizes the item. (It only compares labels by default.) */ - label_ = new_name; - return true; -} - -void AbstractTreeViewItem::update_from_old(const AbstractViewItem &old) -{ - AbstractViewItem::update_from_old(old); - - const AbstractTreeViewItem &old_tree_item = dynamic_cast(old); - is_open_ = old_tree_item.is_open_; -} - -bool AbstractTreeViewItem::matches_single(const AbstractTreeViewItem &other) const -{ - return label_ == other.label_; -} - -AbstractTreeView &AbstractTreeViewItem::get_tree_view() const -{ - return dynamic_cast(get_view()); -} - -int AbstractTreeViewItem::count_parents() const -{ - int i = 0; - for (AbstractTreeViewItem *parent = parent_; parent; parent = parent->parent_) { - i++; - } - return i; -} - -void AbstractTreeViewItem::activate() -{ - BLI_assert_msg(get_tree_view().is_reconstructed(), - "Item activation can't be done until reconstruction is completed"); - - if (is_active()) { - return; - } - - /* Deactivate other items in the tree. */ - get_tree_view().foreach_item([](auto &item) { item.deactivate(); }); - - on_activate(); - /* Make sure the active item is always visible. */ - ensure_parents_uncollapsed(); - - is_active_ = true; -} - -void AbstractTreeViewItem::deactivate() -{ - is_active_ = false; -} - -bool AbstractTreeViewItem::is_hovered() const -{ - BLI_assert_msg(get_tree_view().is_reconstructed(), - "State can't be queried until reconstruction is completed"); - BLI_assert_msg(view_item_but_ != nullptr, - "Hovered state can't be queried before the tree row is being built"); - - const uiViewItemHandle *this_item_handle = reinterpret_cast(this); - /* The new layout hasn't finished construction yet, so the final state of the button is unknown. - * Get the matching button from the previous redraw instead. */ - uiButViewItem *old_item_but = ui_block_view_find_matching_view_item_but_in_old_block( - view_item_but_->but.block, this_item_handle); - return old_item_but && (old_item_but->but.flag & UI_ACTIVE); -} - -bool AbstractTreeViewItem::is_collapsed() const -{ - BLI_assert_msg(get_tree_view().is_reconstructed(), - "State can't be queried until reconstruction is completed"); - return is_collapsible() && !is_open_; -} - -void AbstractTreeViewItem::toggle_collapsed() -{ - is_open_ = !is_open_; -} - -void AbstractTreeViewItem::set_collapsed(bool collapsed) -{ - is_open_ = !collapsed; -} - -bool AbstractTreeViewItem::is_collapsible() const -{ - if (children_.is_empty()) { - return false; - } - return this->supports_collapsing(); -} - -void AbstractTreeViewItem::ensure_parents_uncollapsed() -{ - for (AbstractTreeViewItem *parent = parent_; parent; parent = parent->parent_) { - parent->set_collapsed(false); - } -} - -bool AbstractTreeViewItem::matches(const AbstractViewItem &other) const -{ - const AbstractTreeViewItem &other_tree_item = dynamic_cast(other); - - if (!matches_single(other_tree_item)) { - return false; - } - if (count_parents() != other_tree_item.count_parents()) { - return false; - } - - for (AbstractTreeViewItem *parent = parent_, *other_parent = other_tree_item.parent_; - parent && other_parent; - parent = parent->parent_, other_parent = other_parent->parent_) { - if (!parent->matches_single(*other_parent)) { - return false; - } - } - - return true; -} - -uiButViewItem *AbstractTreeViewItem::view_item_button() -{ - return view_item_but_; -} - -void AbstractTreeViewItem::change_state_delayed() -{ - const std::optional should_be_active = this->should_be_active(); - if (should_be_active.has_value() && *should_be_active) { - activate(); - } -} - -/* ---------------------------------------------------------------------- */ - -class TreeViewLayoutBuilder { - uiBlock &block_; - - friend TreeViewBuilder; - - public: - void build_from_tree(const AbstractTreeView &tree_view); - void build_row(AbstractTreeViewItem &item) const; - - uiBlock &block() const; - uiLayout *current_layout() const; - - private: - /* Created through #TreeViewBuilder. */ - TreeViewLayoutBuilder(uiBlock &block); - - static void polish_layout(const uiBlock &block); -}; - -TreeViewLayoutBuilder::TreeViewLayoutBuilder(uiBlock &block) : block_(block) -{ -} - -void TreeViewLayoutBuilder::build_from_tree(const AbstractTreeView &tree_view) -{ - uiLayout *prev_layout = current_layout(); - - uiLayout *box = uiLayoutBox(prev_layout); - uiLayoutColumn(box, false); - - tree_view.foreach_item([this](AbstractTreeViewItem &item) { build_row(item); }, - AbstractTreeView::IterOptions::SkipCollapsed); - - UI_block_layout_set_current(&block(), prev_layout); -} - -void TreeViewLayoutBuilder::polish_layout(const uiBlock &block) -{ - LISTBASE_FOREACH_BACKWARD (uiBut *, but, &block.buttons) { - if (AbstractTreeViewItem::is_collapse_chevron_but(but) && but->next && - /* Embossed buttons with padding-less text padding look weird, so don't touch them. */ - ELEM(but->next->emboss, UI_EMBOSS_NONE, UI_EMBOSS_NONE_OR_STATUS)) { - UI_but_drawflag_enable(static_cast(but->next), UI_BUT_NO_TEXT_PADDING); - } - - if (but->type == UI_BTYPE_VIEW_ITEM) { - break; - } - } -} - -void TreeViewLayoutBuilder::build_row(AbstractTreeViewItem &item) const -{ - uiBlock &block_ = block(); - - uiLayout *prev_layout = current_layout(); - eUIEmbossType previous_emboss = UI_block_emboss_get(&block_); - - uiLayout *overlap = uiLayoutOverlap(prev_layout); - - uiLayoutRow(overlap, false); - /* Every item gets one! Other buttons can be overlapped on top. */ - item.add_treerow_button(block_); - - /* After adding tree-row button (would disable hover highlighting). */ - UI_block_emboss_set(&block_, UI_EMBOSS_NONE); - - uiLayout *row = uiLayoutRow(overlap, true); - item.add_indent(*row); - item.add_collapse_chevron(block_); - - if (item.is_renaming()) { - item.add_rename_button(*row); - } - else { - item.build_row(*row); - } - polish_layout(block_); - - UI_block_emboss_set(&block_, previous_emboss); - UI_block_layout_set_current(&block_, prev_layout); -} - -uiBlock &TreeViewLayoutBuilder::block() const -{ - return block_; -} - -uiLayout *TreeViewLayoutBuilder::current_layout() const -{ - return block().curlayout; -} - -/* ---------------------------------------------------------------------- */ - -TreeViewBuilder::TreeViewBuilder(uiBlock &block) : block_(block) -{ -} - -void TreeViewBuilder::build_tree_view(AbstractTreeView &tree_view) -{ - tree_view.build_tree(); - tree_view.update_from_old(block_); - tree_view.change_state_delayed(); - - TreeViewLayoutBuilder builder(block_); - builder.build_from_tree(tree_view); -} - -/* ---------------------------------------------------------------------- */ - -BasicTreeViewItem::BasicTreeViewItem(StringRef label, BIFIconID icon_) : icon(icon_) -{ - label_ = label; -} - -void BasicTreeViewItem::build_row(uiLayout &row) -{ - add_label(row); -} - -void BasicTreeViewItem::add_label(uiLayout &layout, StringRefNull label_override) -{ - const StringRefNull label = label_override.is_empty() ? StringRefNull(label_) : label_override; - - /* Some padding for labels without collapse chevron and no icon. Looks weird without. */ - if (icon == ICON_NONE && !is_collapsible()) { - uiItemS_ex(&layout, 0.8f); - } - uiItemL(&layout, IFACE_(label.c_str()), icon); -} - -void BasicTreeViewItem::on_activate() -{ - if (activate_fn_) { - activate_fn_(*this); - } -} - -void BasicTreeViewItem::set_on_activate_fn(ActivateFn fn) -{ - activate_fn_ = fn; -} - -void BasicTreeViewItem::set_is_active_fn(IsActiveFn is_active_fn) -{ - is_active_fn_ = is_active_fn; -} - -std::optional BasicTreeViewItem::should_be_active() const -{ - if (is_active_fn_) { - return is_active_fn_(); - } - return std::nullopt; -} - -} // namespace blender::ui diff --git a/source/blender/editors/interface/views/abstract_view.cc b/source/blender/editors/interface/views/abstract_view.cc new file mode 100644 index 00000000000..077c76a08f1 --- /dev/null +++ b/source/blender/editors/interface/views/abstract_view.cc @@ -0,0 +1,109 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup edinterface + */ + +#include "interface_intern.h" + +#include "UI_abstract_view.hh" + +namespace blender::ui { + +void AbstractView::register_item(AbstractViewItem &item) +{ + /* Actually modifies the item, not the view. But for the public API it "feels" a bit nicer to + * have the view base class register the items, rather than setting the view on the item. */ + item.view_ = this; +} + +/* ---------------------------------------------------------------------- */ +/** \name View Reconstruction + * \{ */ + +bool AbstractView::is_reconstructed() const +{ + return is_reconstructed_; +} + +void AbstractView::update_from_old(uiBlock &new_block) +{ + uiBlock *old_block = new_block.oldblock; + if (!old_block) { + is_reconstructed_ = true; + return; + } + + uiViewHandle *old_view_handle = ui_block_view_find_matching_in_old_block( + &new_block, reinterpret_cast(this)); + if (old_view_handle == nullptr) { + /* Initial construction, nothing to update. */ + is_reconstructed_ = true; + return; + } + + AbstractView &old_view = reinterpret_cast(*old_view_handle); + + /* Update own persistent data. */ + /* Keep the rename buffer persistent while renaming! The rename button uses the buffer's + * pointer to identify itself over redraws. */ + rename_buffer_ = std::move(old_view.rename_buffer_); + old_view.rename_buffer_ = nullptr; + + update_children_from_old(old_view); + + /* Finished (re-)constructing the tree. */ + is_reconstructed_ = true; +} + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Default implementations of virtual functions + * \{ */ + +bool AbstractView::listen(const wmNotifier & /*notifier*/) const +{ + /* Nothing by default. */ + return false; +} + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Renaming + * \{ */ + +bool AbstractView::is_renaming() const +{ + return rename_buffer_ != nullptr; +} + +bool AbstractView::begin_renaming() +{ + if (is_renaming()) { + return false; + } + + rename_buffer_ = std::make_unique(); + return true; +} + +void AbstractView::end_renaming() +{ + BLI_assert(is_renaming()); + rename_buffer_ = nullptr; +} + +Span AbstractView::get_rename_buffer() const +{ + return *rename_buffer_; +} +MutableSpan AbstractView::get_rename_buffer() +{ + return *rename_buffer_; +} + +/** \} */ + +} // namespace blender::ui diff --git a/source/blender/editors/interface/views/abstract_view_item.cc b/source/blender/editors/interface/views/abstract_view_item.cc new file mode 100644 index 00000000000..f73183d07e9 --- /dev/null +++ b/source/blender/editors/interface/views/abstract_view_item.cc @@ -0,0 +1,373 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup edinterface + */ + +#include "BKE_context.h" + +#include "BLI_listbase.h" +#include "BLI_string.h" + +#include "WM_api.h" + +#include "UI_interface.h" +#include "interface_intern.h" + +#include "UI_abstract_view.hh" + +namespace blender::ui { + +/* ---------------------------------------------------------------------- */ +/** \name View Reconstruction + * \{ */ + +void AbstractViewItem::update_from_old(const AbstractViewItem &old) +{ + is_active_ = old.is_active_; + is_renaming_ = old.is_renaming_; +} + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Renaming + * \{ */ + +bool AbstractViewItem::supports_renaming() const +{ + /* No renaming by default. */ + return false; +} +bool AbstractViewItem::rename(StringRefNull /*new_name*/) +{ + /* No renaming by default. */ + return false; +} + +StringRef AbstractViewItem::get_rename_string() const +{ + /* No rename string by default. */ + return {}; +} + +bool AbstractViewItem::is_renaming() const +{ + return is_renaming_; +} + +void AbstractViewItem::begin_renaming() +{ + AbstractView &view = get_view(); + if (view.is_renaming() || !supports_renaming()) { + return; + } + + if (view.begin_renaming()) { + is_renaming_ = true; + } + + StringRef initial_str = get_rename_string(); + std::copy(std::begin(initial_str), std::end(initial_str), std::begin(view.get_rename_buffer())); +} + +void AbstractViewItem::rename_apply() +{ + const AbstractView &view = get_view(); + rename(view.get_rename_buffer().data()); + end_renaming(); +} + +void AbstractViewItem::end_renaming() +{ + if (!is_renaming()) { + return; + } + + is_renaming_ = false; + + AbstractView &view = get_view(); + view.end_renaming(); +} + +static AbstractViewItem *find_item_from_rename_button(const uiBut &rename_but) +{ + /* A minimal sanity check, can't do much more here. */ + BLI_assert(rename_but.type == UI_BTYPE_TEXT && rename_but.poin); + + LISTBASE_FOREACH (uiBut *, but, &rename_but.block->buttons) { + if (but->type != UI_BTYPE_VIEW_ITEM) { + continue; + } + + uiButViewItem *view_item_but = (uiButViewItem *)but; + AbstractViewItem *item = reinterpret_cast(view_item_but->view_item); + const AbstractView &view = item->get_view(); + + if (item->is_renaming() && (view.get_rename_buffer().data() == rename_but.poin)) { + return item; + } + } + + return nullptr; +} + +static void rename_button_fn(bContext *UNUSED(C), void *arg, char *UNUSED(origstr)) +{ + const uiBut *rename_but = static_cast(arg); + AbstractViewItem *item = find_item_from_rename_button(*rename_but); + BLI_assert(item); + item->rename_apply(); +} + +void AbstractViewItem::add_rename_button(uiBlock &block) +{ + AbstractView &view = get_view(); + uiBut *rename_but = uiDefBut(&block, + UI_BTYPE_TEXT, + 1, + "", + 0, + 0, + UI_UNIT_X * 10, + UI_UNIT_Y, + view.get_rename_buffer().data(), + 1.0f, + view.get_rename_buffer().size(), + 0, + 0, + ""); + + /* Gotta be careful with what's passed to the `arg1` here. Any view data will be freed once the + * callback is executed. */ + UI_but_func_rename_set(rename_but, rename_button_fn, rename_but); + UI_but_flag_disable(rename_but, UI_BUT_UNDO); + + const bContext *evil_C = reinterpret_cast(block.evil_C); + ARegion *region = CTX_wm_region(evil_C); + /* Returns false if the button was removed. */ + if (UI_but_active_only(evil_C, region, &block, rename_but) == false) { + end_renaming(); + } +} + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Context Menu + * \{ */ + +void AbstractViewItem::build_context_menu(bContext & /*C*/, uiLayout & /*column*/) const +{ + /* No context menu by default. */ +} + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Drag 'n Drop + * \{ */ + +std::unique_ptr AbstractViewItem::create_drag_controller() const +{ + /* There's no drag controller (and hence no drag support) by default. */ + return nullptr; +} + +std::unique_ptr AbstractViewItem::create_drop_controller() const +{ + /* There's no drop controller (and hence no drop support) by default. */ + return nullptr; +} + +AbstractViewItemDragController::AbstractViewItemDragController(AbstractView &view) : view_(view) +{ +} + +void AbstractViewItemDragController::on_drag_start() +{ + /* Do nothing by default. */ +} + +AbstractViewItemDropController::AbstractViewItemDropController(AbstractView &view) : view_(view) +{ +} + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name General Getters & Setters + * \{ */ + +AbstractView &AbstractViewItem::get_view() const +{ + if (UNLIKELY(!view_)) { + throw std::runtime_error( + "Invalid state, item must be registered through AbstractView::register_item()"); + } + return *view_; +} + +bool AbstractViewItem::is_active() const +{ + BLI_assert_msg(get_view().is_reconstructed(), + "State can't be queried until reconstruction is completed"); + return is_active_; +} + +/** \} */ + +} // namespace blender::ui + +/* ---------------------------------------------------------------------- */ +/** \name C-API + * \{ */ + +namespace blender::ui { + +/** + * Helper class to provide a higher level public (C-)API. Has access to private/protected view item + * members and ensures some invariants that way. + */ +class ViewItemAPIWrapper { + public: + static bool matches(const AbstractViewItem &a, const AbstractViewItem &b) + { + if (typeid(a) != typeid(b)) { + return false; + } + /* TODO should match the view as well. */ + return a.matches(b); + } + + static bool can_rename(const AbstractViewItem &item) + { + const AbstractView &view = item.get_view(); + return !view.is_renaming() && item.supports_renaming(); + } + + static bool drag_start(bContext &C, const AbstractViewItem &item) + { + const std::unique_ptr drag_controller = + item.create_drag_controller(); + if (!drag_controller) { + return false; + } + + WM_event_start_drag(&C, + ICON_NONE, + drag_controller->get_drag_type(), + drag_controller->create_drag_data(), + 0, + WM_DRAG_FREE_DATA); + drag_controller->on_drag_start(); + + return true; + } + + static bool can_drop(const AbstractViewItem &item, + const wmDrag &drag, + const char **r_disabled_hint) + { + const std::unique_ptr drop_controller = + item.create_drop_controller(); + if (!drop_controller) { + return false; + } + + return drop_controller->can_drop(drag, r_disabled_hint); + } + + static std::string drop_tooltip(const AbstractViewItem &item, const wmDrag &drag) + { + const std::unique_ptr drop_controller = + item.create_drop_controller(); + if (!drop_controller) { + return {}; + } + + return drop_controller->drop_tooltip(drag); + } + + static bool drop_handle(bContext &C, const AbstractViewItem &item, const ListBase &drags) + { + std::unique_ptr drop_controller = + item.create_drop_controller(); + + const char *disabled_hint_dummy = nullptr; + LISTBASE_FOREACH (const wmDrag *, drag, &drags) { + if (drop_controller->can_drop(*drag, &disabled_hint_dummy)) { + return drop_controller->on_drop(&C, *drag); + } + } + + return false; + } +}; + +} // namespace blender::ui + +using namespace blender::ui; + +bool UI_view_item_is_active(const uiViewItemHandle *item_handle) +{ + const AbstractViewItem &item = reinterpret_cast(*item_handle); + return item.is_active(); +} + +bool UI_view_item_matches(const uiViewItemHandle *a_handle, const uiViewItemHandle *b_handle) +{ + const AbstractViewItem &a = reinterpret_cast(*a_handle); + const AbstractViewItem &b = reinterpret_cast(*b_handle); + return ViewItemAPIWrapper::matches(a, b); +} + +bool UI_view_item_can_rename(const uiViewItemHandle *item_handle) +{ + const AbstractViewItem &item = reinterpret_cast(*item_handle); + return ViewItemAPIWrapper::can_rename(item); +} + +void UI_view_item_begin_rename(uiViewItemHandle *item_handle) +{ + AbstractViewItem &item = reinterpret_cast(*item_handle); + item.begin_renaming(); +} + +void UI_view_item_context_menu_build(bContext *C, + const uiViewItemHandle *item_handle, + uiLayout *column) +{ + const AbstractViewItem &item = reinterpret_cast(*item_handle); + item.build_context_menu(*C, *column); +} + +bool UI_view_item_drag_start(bContext *C, const uiViewItemHandle *item_) +{ + const AbstractViewItem &item = reinterpret_cast(*item_); + return ViewItemAPIWrapper::drag_start(*C, item); +} + +bool UI_view_item_can_drop(const uiViewItemHandle *item_, + const wmDrag *drag, + const char **r_disabled_hint) +{ + const AbstractViewItem &item = reinterpret_cast(*item_); + return ViewItemAPIWrapper::can_drop(item, *drag, r_disabled_hint); +} + +char *UI_view_item_drop_tooltip(const uiViewItemHandle *item_, const wmDrag *drag) +{ + const AbstractViewItem &item = reinterpret_cast(*item_); + + const std::string tooltip = ViewItemAPIWrapper::drop_tooltip(item, *drag); + return tooltip.empty() ? nullptr : BLI_strdup(tooltip.c_str()); +} + +bool UI_view_item_drop_handle(bContext *C, const uiViewItemHandle *item_, const ListBase *drags) +{ + const AbstractViewItem &item = reinterpret_cast(*item_); + return ViewItemAPIWrapper::drop_handle(*C, item, *drags); +} + +/** \} */ diff --git a/source/blender/editors/interface/views/grid_view.cc b/source/blender/editors/interface/views/grid_view.cc new file mode 100644 index 00000000000..37fbb33f83b --- /dev/null +++ b/source/blender/editors/interface/views/grid_view.cc @@ -0,0 +1,464 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup edinterface + */ + +#include +#include + +#include "BLI_index_range.hh" + +#include "WM_types.h" + +#include "UI_interface.h" +#include "interface_intern.h" + +#include "UI_grid_view.hh" + +namespace blender::ui { + +/* ---------------------------------------------------------------------- */ + +AbstractGridView::AbstractGridView() : style_(UI_preview_tile_size_x(), UI_preview_tile_size_y()) +{ +} + +AbstractGridViewItem &AbstractGridView::add_item(std::unique_ptr item) +{ + items_.append(std::move(item)); + + AbstractGridViewItem &added_item = *items_.last(); + added_item.view_ = this; + + item_map_.add(added_item.identifier_, &added_item); + + return added_item; +} + +void AbstractGridView::foreach_item(ItemIterFn iter_fn) const +{ + for (const auto &item_ptr : items_) { + iter_fn(*item_ptr); + } +} + +AbstractGridViewItem *AbstractGridView::find_matching_item( + const AbstractGridViewItem &item_to_match, const AbstractGridView &view_to_search_in) const +{ + AbstractGridViewItem *const *match = view_to_search_in.item_map_.lookup_ptr( + item_to_match.identifier_); + BLI_assert(!match || item_to_match.matches(**match)); + + return match ? *match : nullptr; +} + +void AbstractGridView::change_state_delayed() +{ + BLI_assert_msg( + is_reconstructed(), + "These state changes are supposed to be delayed until reconstruction is completed"); + foreach_item([](AbstractGridViewItem &item) { item.change_state_delayed(); }); +} + +void AbstractGridView::update_children_from_old(const AbstractView &old_view) +{ + const AbstractGridView &old_grid_view = dynamic_cast(old_view); + + foreach_item([this, &old_grid_view](AbstractGridViewItem &new_item) { + const AbstractGridViewItem *matching_old_item = find_matching_item(new_item, old_grid_view); + if (!matching_old_item) { + return; + } + + new_item.update_from_old(*matching_old_item); + }); +} + +const GridViewStyle &AbstractGridView::get_style() const +{ + return style_; +} + +int AbstractGridView::get_item_count() const +{ + return items_.size(); +} + +GridViewStyle::GridViewStyle(int width, int height) : tile_width(width), tile_height(height) +{ +} + +/* ---------------------------------------------------------------------- */ + +AbstractGridViewItem::AbstractGridViewItem(StringRef identifier) : identifier_(identifier) +{ +} + +bool AbstractGridViewItem::matches(const AbstractViewItem &other) const +{ + const AbstractGridViewItem &other_grid_item = dynamic_cast(other); + return identifier_ == other_grid_item.identifier_; +} + +void AbstractGridViewItem::grid_tile_click_fn(struct bContext * /*C*/, + void *but_arg1, + void * /*arg2*/) +{ + uiButViewItem *view_item_but = (uiButViewItem *)but_arg1; + AbstractGridViewItem &grid_item = reinterpret_cast( + *view_item_but->view_item); + + grid_item.activate(); +} + +void AbstractGridViewItem::add_grid_tile_button(uiBlock &block) +{ + const GridViewStyle &style = get_view().get_style(); + view_item_but_ = (uiButViewItem *)uiDefBut(&block, + UI_BTYPE_VIEW_ITEM, + 0, + "", + 0, + 0, + style.tile_width, + style.tile_height, + nullptr, + 0, + 0, + 0, + 0, + ""); + + view_item_but_->view_item = reinterpret_cast(this); + UI_but_func_set(&view_item_but_->but, grid_tile_click_fn, view_item_but_, nullptr); +} + +void AbstractGridViewItem::on_activate() +{ + /* Do nothing by default. */ +} + +std::optional AbstractGridViewItem::should_be_active() const +{ + return std::nullopt; +} + +void AbstractGridViewItem::change_state_delayed() +{ + const std::optional should_be_active = this->should_be_active(); + if (should_be_active.has_value() && *should_be_active) { + activate(); + } +} + +void AbstractGridViewItem::activate() +{ + BLI_assert_msg(get_view().is_reconstructed(), + "Item activation can't be done until reconstruction is completed"); + + if (is_active()) { + return; + } + + /* Deactivate other items in the tree. */ + get_view().foreach_item([](auto &item) { item.deactivate(); }); + + on_activate(); + + is_active_ = true; +} + +void AbstractGridViewItem::deactivate() +{ + is_active_ = false; +} + +const AbstractGridView &AbstractGridViewItem::get_view() const +{ + if (UNLIKELY(!view_)) { + throw std::runtime_error( + "Invalid state, item must be added through AbstractGridView::add_item()"); + } + return *view_; +} + +/* ---------------------------------------------------------------------- */ + +/** + * Helper for only adding layout items for grid items that are actually in view. 3 main functions: + * - #is_item_visible(): Query if an item of a given index is visible in the view (others should be + * skipped when building the layout). + * - #fill_layout_before_visible(): Add empty space to the layout before a visible row is drawn, so + * the layout height is the same as if all items were added (important to get the correct scroll + * height). + * - #fill_layout_after_visible(): Same thing, just adds empty space for after the last visible + * row. + * + * Does two assumptions: + * - Top-to-bottom flow (ymax = 0 and ymin < 0). If that's not good enough, View2D should + * probably provide queries for the scroll offset. + * - Only vertical scrolling. For horizontal scrolling, spacers would have to be added on the + * side(s) as well. + */ +class BuildOnlyVisibleButtonsHelper { + const View2D &v2d_; + const AbstractGridView &grid_view_; + const GridViewStyle &style_; + const int cols_per_row_ = 0; + /* Indices of items within the view. Calculated by constructor */ + IndexRange visible_items_range_{}; + + public: + BuildOnlyVisibleButtonsHelper(const View2D &v2d, + const AbstractGridView &grid_view, + int cols_per_row); + + bool is_item_visible(int item_idx) const; + void fill_layout_before_visible(uiBlock &block) const; + void fill_layout_after_visible(uiBlock &block) const; + + private: + IndexRange get_visible_range() const; + void add_spacer_button(uiBlock &block, int row_count) const; +}; + +BuildOnlyVisibleButtonsHelper::BuildOnlyVisibleButtonsHelper(const View2D &v2d, + const AbstractGridView &grid_view, + const int cols_per_row) + : v2d_(v2d), grid_view_(grid_view), style_(grid_view.get_style()), cols_per_row_(cols_per_row) +{ + visible_items_range_ = get_visible_range(); +} + +IndexRange BuildOnlyVisibleButtonsHelper::get_visible_range() const +{ + int first_idx_in_view = 0; + int max_items_in_view = 0; + + const float scroll_ofs_y = abs(v2d_.cur.ymax - v2d_.tot.ymax); + if (!IS_EQF(scroll_ofs_y, 0)) { + const int scrolled_away_rows = (int)scroll_ofs_y / style_.tile_height; + + first_idx_in_view = scrolled_away_rows * cols_per_row_; + } + + const float view_height = BLI_rctf_size_y(&v2d_.cur); + const int count_rows_in_view = std::max(round_fl_to_int(view_height / style_.tile_height), 1); + max_items_in_view = (count_rows_in_view + 1) * cols_per_row_; + + BLI_assert(max_items_in_view > 0); + return IndexRange(first_idx_in_view, max_items_in_view); +} + +bool BuildOnlyVisibleButtonsHelper::is_item_visible(const int item_idx) const +{ + return visible_items_range_.contains(item_idx); +} + +void BuildOnlyVisibleButtonsHelper::fill_layout_before_visible(uiBlock &block) const +{ + const float scroll_ofs_y = abs(v2d_.cur.ymax - v2d_.tot.ymax); + + if (IS_EQF(scroll_ofs_y, 0)) { + return; + } + + const int scrolled_away_rows = (int)scroll_ofs_y / style_.tile_height; + add_spacer_button(block, scrolled_away_rows); +} + +void BuildOnlyVisibleButtonsHelper::fill_layout_after_visible(uiBlock &block) const +{ + const int last_item_idx = grid_view_.get_item_count() - 1; + const int last_visible_idx = visible_items_range_.last(); + + if (last_item_idx > last_visible_idx) { + const int remaining_rows = (cols_per_row_ > 0) ? + (last_item_idx - last_visible_idx) / cols_per_row_ : + 0; + BuildOnlyVisibleButtonsHelper::add_spacer_button(block, remaining_rows); + } +} + +void BuildOnlyVisibleButtonsHelper::add_spacer_button(uiBlock &block, const int row_count) const +{ + /* UI code only supports button dimensions of `signed short` size, the layout height we want to + * fill may be bigger than that. So add multiple labels of the maximum size if necessary. */ + for (int remaining_rows = row_count; remaining_rows > 0;) { + const short row_count_this_iter = std::min( + std::numeric_limits::max() / style_.tile_height, remaining_rows); + + uiDefBut(&block, + UI_BTYPE_LABEL, + 0, + "", + 0, + 0, + UI_UNIT_X, + row_count_this_iter * style_.tile_height, + nullptr, + 0, + 0, + 0, + 0, + ""); + remaining_rows -= row_count_this_iter; + } +} + +/* ---------------------------------------------------------------------- */ + +class GridViewLayoutBuilder { + uiBlock &block_; + + friend class GridViewBuilder; + + public: + GridViewLayoutBuilder(uiBlock &block); + + void build_from_view(const AbstractGridView &grid_view, const View2D &v2d) const; + + private: + void build_grid_tile(uiLayout &grid_layout, AbstractGridViewItem &item) const; + + uiLayout *current_layout() const; +}; + +GridViewLayoutBuilder::GridViewLayoutBuilder(uiBlock &block) : block_(block) +{ +} + +void GridViewLayoutBuilder::build_grid_tile(uiLayout &grid_layout, + AbstractGridViewItem &item) const +{ + uiLayout *overlap = uiLayoutOverlap(&grid_layout); + + item.add_grid_tile_button(block_); + item.build_grid_tile(*uiLayoutRow(overlap, false)); +} + +void GridViewLayoutBuilder::build_from_view(const AbstractGridView &grid_view, + const View2D &v2d) const +{ + uiLayout *prev_layout = current_layout(); + + uiLayout &layout = *uiLayoutColumn(current_layout(), false); + const GridViewStyle &style = grid_view.get_style(); + + const int cols_per_row = std::max(uiLayoutGetWidth(&layout) / style.tile_width, 1); + + BuildOnlyVisibleButtonsHelper build_visible_helper(v2d, grid_view, cols_per_row); + + build_visible_helper.fill_layout_before_visible(block_); + + /* Use `-cols_per_row` because the grid layout uses a multiple of the passed absolute value for + * the number of columns then, rather than distributing the number of items evenly over rows and + * stretching the items to fit (see #uiLayoutItemGridFlow.columns_len). */ + uiLayout *grid_layout = uiLayoutGridFlow(&layout, true, -cols_per_row, true, true, true); + + int item_idx = 0; + grid_view.foreach_item([&](AbstractGridViewItem &item) { + /* Skip if item isn't visible. */ + if (!build_visible_helper.is_item_visible(item_idx)) { + item_idx++; + return; + } + + build_grid_tile(*grid_layout, item); + item_idx++; + }); + + /* If there are not enough items to fill the layout, add padding items so the layout doesn't + * stretch over the entire width. */ + if (grid_view.get_item_count() < cols_per_row) { + for (int padding_item_idx = 0; padding_item_idx < (cols_per_row - grid_view.get_item_count()); + padding_item_idx++) { + uiItemS(grid_layout); + } + } + + UI_block_layout_set_current(&block_, prev_layout); + + build_visible_helper.fill_layout_after_visible(block_); +} + +uiLayout *GridViewLayoutBuilder::current_layout() const +{ + return block_.curlayout; +} + +/* ---------------------------------------------------------------------- */ + +GridViewBuilder::GridViewBuilder(uiBlock &block) : block_(block) +{ +} + +void GridViewBuilder::build_grid_view(AbstractGridView &grid_view, const View2D &v2d) +{ + grid_view.build_items(); + grid_view.update_from_old(block_); + grid_view.change_state_delayed(); + + GridViewLayoutBuilder builder(block_); + builder.build_from_view(grid_view, v2d); +} + +/* ---------------------------------------------------------------------- */ + +PreviewGridItem::PreviewGridItem(StringRef identifier, StringRef label, int preview_icon_id) + : AbstractGridViewItem(identifier), label(label), preview_icon_id(preview_icon_id) +{ +} + +void PreviewGridItem::build_grid_tile(uiLayout &layout) const +{ + const GridViewStyle &style = get_view().get_style(); + uiBlock *block = uiLayoutGetBlock(&layout); + + uiBut *but = uiDefBut(block, + UI_BTYPE_PREVIEW_TILE, + 0, + label.c_str(), + 0, + 0, + style.tile_width, + style.tile_height, + nullptr, + 0, + 0, + 0, + 0, + ""); + ui_def_but_icon(but, + preview_icon_id, + /* NOLINTNEXTLINE: bugprone-suspicious-enum-usage */ + UI_HAS_ICON | UI_BUT_ICON_PREVIEW); +} + +void PreviewGridItem::set_on_activate_fn(ActivateFn fn) +{ + activate_fn_ = fn; +} + +void PreviewGridItem::set_is_active_fn(IsActiveFn fn) +{ + is_active_fn_ = fn; +} + +void PreviewGridItem::on_activate() +{ + if (activate_fn_) { + activate_fn_(*this); + } +} + +std::optional PreviewGridItem::should_be_active() const +{ + if (is_active_fn_) { + return is_active_fn_(); + } + return std::nullopt; +} + +} // namespace blender::ui diff --git a/source/blender/editors/interface/views/interface_view.cc b/source/blender/editors/interface/views/interface_view.cc new file mode 100644 index 00000000000..b35f6d2c969 --- /dev/null +++ b/source/blender/editors/interface/views/interface_view.cc @@ -0,0 +1,190 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup edinterface + * + * This part of the UI-View API is mostly needed to support persistent state of items within the + * view. Views are stored in #uiBlock's, and kept alive with it until after the next redraw. So we + * can compare the old view items with the new view items and keep state persistent for matching + * ones. + */ + +#include +#include +#include + +#include "DNA_screen_types.h" + +#include "BKE_screen.h" + +#include "BLI_listbase.h" + +#include "ED_screen.h" + +#include "interface_intern.h" + +#include "UI_interface.hh" + +#include "UI_abstract_view.hh" +#include "UI_grid_view.hh" +#include "UI_tree_view.hh" + +using namespace blender; +using namespace blender::ui; + +/** + * Wrapper to store views in a #ListBase, addressable via an identifier. + */ +struct ViewLink : public Link { + std::string idname; + std::unique_ptr view; +}; + +template +static T *ui_block_add_view_impl(uiBlock &block, + StringRef idname, + std::unique_ptr view) +{ + ViewLink *view_link = MEM_new(__func__); + BLI_addtail(&block.views, view_link); + + view_link->view = std::move(view); + view_link->idname = idname; + + return dynamic_cast(view_link->view.get()); +} + +AbstractGridView *UI_block_add_view(uiBlock &block, + StringRef idname, + std::unique_ptr grid_view) +{ + return ui_block_add_view_impl(block, idname, std::move(grid_view)); +} + +AbstractTreeView *UI_block_add_view(uiBlock &block, + StringRef idname, + std::unique_ptr tree_view) +{ + return ui_block_add_view_impl(block, idname, std::move(tree_view)); +} + +void ui_block_free_views(uiBlock *block) +{ + LISTBASE_FOREACH_MUTABLE (ViewLink *, link, &block->views) { + MEM_delete(link); + } +} + +void UI_block_views_listen(const uiBlock *block, const wmRegionListenerParams *listener_params) +{ + ARegion *region = listener_params->region; + + LISTBASE_FOREACH (ViewLink *, view_link, &block->views) { + if (view_link->view->listen(*listener_params->notifier)) { + ED_region_tag_redraw(region); + } + } +} + +uiViewItemHandle *UI_block_view_find_item_at(const ARegion *region, const int xy[2]) +{ + uiButViewItem *item_but = (uiButViewItem *)ui_view_item_find_mouse_over(region, xy); + if (!item_but) { + return nullptr; + } + + return item_but->view_item; +} + +uiViewItemHandle *UI_block_view_find_active_item(const ARegion *region) +{ + uiButViewItem *item_but = (uiButViewItem *)ui_view_item_find_active(region); + if (!item_but) { + return nullptr; + } + + return item_but->view_item; +} + +static StringRef ui_block_view_find_idname(const uiBlock &block, const AbstractView &view) +{ + /* First get the idname the of the view we're looking for. */ + LISTBASE_FOREACH (ViewLink *, view_link, &block.views) { + if (view_link->view.get() == &view) { + return view_link->idname; + } + } + + return {}; +} + +template +static T *ui_block_view_find_matching_in_old_block_impl(const uiBlock &new_block, + const T &new_view) +{ + uiBlock *old_block = new_block.oldblock; + if (!old_block) { + return nullptr; + } + + StringRef idname = ui_block_view_find_idname(new_block, new_view); + if (idname.is_empty()) { + return nullptr; + } + + LISTBASE_FOREACH (ViewLink *, old_view_link, &old_block->views) { + if (old_view_link->idname == idname) { + return dynamic_cast(old_view_link->view.get()); + } + } + + return nullptr; +} + +uiViewHandle *ui_block_view_find_matching_in_old_block(const uiBlock *new_block, + const uiViewHandle *new_view_handle) +{ + BLI_assert(new_block && new_view_handle); + const AbstractView &new_view = reinterpret_cast(*new_view_handle); + + AbstractView *old_view = ui_block_view_find_matching_in_old_block_impl(*new_block, new_view); + return reinterpret_cast(old_view); +} + +uiButViewItem *ui_block_view_find_matching_view_item_but_in_old_block( + const uiBlock *new_block, const uiViewItemHandle *new_item_handle) +{ + uiBlock *old_block = new_block->oldblock; + if (!old_block) { + return nullptr; + } + + const AbstractViewItem &new_item = *reinterpret_cast(new_item_handle); + const AbstractView *old_view = ui_block_view_find_matching_in_old_block_impl( + *new_block, new_item.get_view()); + if (!old_view) { + return nullptr; + } + + LISTBASE_FOREACH (uiBut *, old_but, &old_block->buttons) { + if (old_but->type != UI_BTYPE_VIEW_ITEM) { + continue; + } + uiButViewItem *old_item_but = (uiButViewItem *)old_but; + if (!old_item_but->view_item) { + continue; + } + AbstractViewItem &old_item = *reinterpret_cast(old_item_but->view_item); + /* Check if the item is from the expected view. */ + if (&old_item.get_view() != old_view) { + continue; + } + + if (UI_view_item_matches(reinterpret_cast(&new_item), + reinterpret_cast(&old_item))) { + return old_item_but; + } + } + + return nullptr; +} diff --git a/source/blender/editors/interface/views/tree_view.cc b/source/blender/editors/interface/views/tree_view.cc new file mode 100644 index 00000000000..c224226ba17 --- /dev/null +++ b/source/blender/editors/interface/views/tree_view.cc @@ -0,0 +1,554 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup edinterface + */ + +#include "DNA_userdef_types.h" +#include "DNA_windowmanager_types.h" + +#include "BKE_context.h" + +#include "BLT_translation.h" + +#include "interface_intern.h" + +#include "UI_interface.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "UI_tree_view.hh" + +namespace blender::ui { + +/* ---------------------------------------------------------------------- */ + +/** + * Add a tree-item to the container. This is the only place where items should be added, it + * handles important invariants! + */ +AbstractTreeViewItem &TreeViewItemContainer::add_tree_item( + std::unique_ptr item) +{ + children_.append(std::move(item)); + + /* The first item that will be added to the root sets this. */ + if (root_ == nullptr) { + root_ = this; + } + AbstractTreeView &tree_view = static_cast(*root_); + AbstractTreeViewItem &added_item = *children_.last(); + added_item.root_ = root_; + tree_view.register_item(added_item); + + if (root_ != this) { + /* Any item that isn't the root can be assumed to the a #AbstractTreeViewItem. Not entirely + * nice to static_cast this, but well... */ + added_item.parent_ = static_cast(this); + } + + return added_item; +} + +void TreeViewItemContainer::foreach_item_recursive(ItemIterFn iter_fn, IterOptions options) const +{ + for (const auto &child : children_) { + iter_fn(*child); + if (bool(options & IterOptions::SkipCollapsed) && child->is_collapsed()) { + continue; + } + + child->foreach_item_recursive(iter_fn, options); + } +} + +/* ---------------------------------------------------------------------- */ + +void AbstractTreeView::foreach_item(ItemIterFn iter_fn, IterOptions options) const +{ + foreach_item_recursive(iter_fn, options); +} + +void AbstractTreeView::update_children_from_old(const AbstractView &old_view) +{ + const AbstractTreeView &old_tree_view = dynamic_cast(old_view); + + update_children_from_old_recursive(*this, old_tree_view); +} + +void AbstractTreeView::update_children_from_old_recursive(const TreeViewOrItem &new_items, + const TreeViewOrItem &old_items) +{ + for (const auto &new_item : new_items.children_) { + AbstractTreeViewItem *matching_old_item = find_matching_child(*new_item, old_items); + if (!matching_old_item) { + continue; + } + + new_item->update_from_old(*matching_old_item); + + /* Recurse into children of the matched item. */ + update_children_from_old_recursive(*new_item, *matching_old_item); + } +} + +AbstractTreeViewItem *AbstractTreeView::find_matching_child( + const AbstractTreeViewItem &lookup_item, const TreeViewOrItem &items) +{ + for (const auto &iter_item : items.children_) { + if (lookup_item.matches_single(*iter_item)) { + /* We have a matching item! */ + return iter_item.get(); + } + } + + return nullptr; +} + +void AbstractTreeView::change_state_delayed() +{ + BLI_assert_msg( + is_reconstructed(), + "These state changes are supposed to be delayed until reconstruction is completed"); + foreach_item([](AbstractTreeViewItem &item) { item.change_state_delayed(); }); +} + +/* ---------------------------------------------------------------------- */ + +void AbstractTreeViewItem::tree_row_click_fn(struct bContext * /*C*/, + void *but_arg1, + void * /*arg2*/) +{ + uiButViewItem *item_but = (uiButViewItem *)but_arg1; + AbstractTreeViewItem &tree_item = reinterpret_cast(*item_but->view_item); + + tree_item.activate(); + /* Not only activate the item, also show its children. Maybe this should be optional, or + * controlled by the specific tree-view. */ + tree_item.set_collapsed(false); +} + +void AbstractTreeViewItem::add_treerow_button(uiBlock &block) +{ + /* For some reason a width > (UI_UNIT_X * 2) make the layout system use all available width. */ + view_item_but_ = (uiButViewItem *)uiDefBut( + &block, UI_BTYPE_VIEW_ITEM, 0, "", 0, 0, UI_UNIT_X * 10, UI_UNIT_Y, nullptr, 0, 0, 0, 0, ""); + + view_item_but_->view_item = reinterpret_cast(this); + UI_but_func_set(&view_item_but_->but, tree_row_click_fn, view_item_but_, nullptr); +} + +void AbstractTreeViewItem::add_indent(uiLayout &row) const +{ + uiBlock *block = uiLayoutGetBlock(&row); + uiLayout *subrow = uiLayoutRow(&row, true); + uiLayoutSetFixedSize(subrow, true); + + const float indent_size = count_parents() * UI_DPI_ICON_SIZE; + uiDefBut(block, UI_BTYPE_SEPR, 0, "", 0, 0, indent_size, 0, nullptr, 0.0, 0.0, 0, 0, ""); + + /* Indent items without collapsing icon some more within their parent. Makes it clear that they + * are actually nested and not just a row at the same level without a chevron. */ + if (!is_collapsible() && parent_) { + uiDefBut(block, UI_BTYPE_SEPR, 0, "", 0, 0, 0.2f * UI_UNIT_X, 0, nullptr, 0.0, 0.0, 0, 0, ""); + } + + /* Restore. */ + UI_block_layout_set_current(block, &row); +} + +void AbstractTreeViewItem::collapse_chevron_click_fn(struct bContext *C, + void * /*but_arg1*/, + void * /*arg2*/) +{ + /* There's no data we could pass to this callback. It must be either the button itself or a + * consistent address to match buttons over redraws. So instead of passing it somehow, just + * lookup the hovered item via context here. */ + + const wmWindow *win = CTX_wm_window(C); + const ARegion *region = CTX_wm_region(C); + uiViewItemHandle *hovered_item_handle = UI_block_view_find_item_at(region, win->eventstate->xy); + + AbstractTreeViewItem *hovered_item = from_item_handle(hovered_item_handle); + BLI_assert(hovered_item != nullptr); + + hovered_item->toggle_collapsed(); + /* When collapsing an item with an active child, make this collapsed item active instead so the + * active item stays visible. */ + if (hovered_item->has_active_child()) { + hovered_item->activate(); + } +} + +bool AbstractTreeViewItem::is_collapse_chevron_but(const uiBut *but) +{ + return but->type == UI_BTYPE_BUT_TOGGLE && ELEM(but->icon, ICON_TRIA_RIGHT, ICON_TRIA_DOWN) && + (but->func == collapse_chevron_click_fn); +} + +void AbstractTreeViewItem::add_collapse_chevron(uiBlock &block) const +{ + if (!is_collapsible()) { + return; + } + + const BIFIconID icon = is_collapsed() ? ICON_TRIA_RIGHT : ICON_TRIA_DOWN; + uiBut *but = uiDefIconBut( + &block, UI_BTYPE_BUT_TOGGLE, 0, icon, 0, 0, UI_UNIT_X, UI_UNIT_Y, nullptr, 0, 0, 0, 0, ""); + /* Note that we're passing the tree-row button here, not the chevron one. */ + UI_but_func_set(but, collapse_chevron_click_fn, nullptr, nullptr); + UI_but_flag_disable(but, UI_BUT_UNDO); + + /* Check if the query for the button matches the created button. */ + BLI_assert(is_collapse_chevron_but(but)); +} + +void AbstractTreeViewItem::add_rename_button(uiLayout &row) +{ + uiBlock *block = uiLayoutGetBlock(&row); + eUIEmbossType previous_emboss = UI_block_emboss_get(block); + + uiLayoutRow(&row, false); + /* Enable emboss for the text button. */ + UI_block_emboss_set(block, UI_EMBOSS); + + AbstractViewItem::add_rename_button(*block); + + UI_block_emboss_set(block, previous_emboss); + UI_block_layout_set_current(block, &row); +} + +bool AbstractTreeViewItem::has_active_child() const +{ + bool found = false; + foreach_item_recursive([&found](const AbstractTreeViewItem &item) { + if (item.is_active()) { + found = true; + } + }); + + return found; +} + +void AbstractTreeViewItem::on_activate() +{ + /* Do nothing by default. */ +} + +std::optional AbstractTreeViewItem::should_be_active() const +{ + return std::nullopt; +} + +bool AbstractTreeViewItem::supports_collapsing() const +{ + return true; +} + +StringRef AbstractTreeViewItem::get_rename_string() const +{ + return label_; +} + +bool AbstractTreeViewItem::rename(StringRefNull new_name) +{ + /* It is important to update the label after renaming, so #AbstractTreeViewItem::matches_single() + * recognizes the item. (It only compares labels by default.) */ + label_ = new_name; + return true; +} + +void AbstractTreeViewItem::update_from_old(const AbstractViewItem &old) +{ + AbstractViewItem::update_from_old(old); + + const AbstractTreeViewItem &old_tree_item = dynamic_cast(old); + is_open_ = old_tree_item.is_open_; +} + +bool AbstractTreeViewItem::matches_single(const AbstractTreeViewItem &other) const +{ + return label_ == other.label_; +} + +AbstractTreeView &AbstractTreeViewItem::get_tree_view() const +{ + return dynamic_cast(get_view()); +} + +int AbstractTreeViewItem::count_parents() const +{ + int i = 0; + for (AbstractTreeViewItem *parent = parent_; parent; parent = parent->parent_) { + i++; + } + return i; +} + +void AbstractTreeViewItem::activate() +{ + BLI_assert_msg(get_tree_view().is_reconstructed(), + "Item activation can't be done until reconstruction is completed"); + + if (is_active()) { + return; + } + + /* Deactivate other items in the tree. */ + get_tree_view().foreach_item([](auto &item) { item.deactivate(); }); + + on_activate(); + /* Make sure the active item is always visible. */ + ensure_parents_uncollapsed(); + + is_active_ = true; +} + +void AbstractTreeViewItem::deactivate() +{ + is_active_ = false; +} + +bool AbstractTreeViewItem::is_hovered() const +{ + BLI_assert_msg(get_tree_view().is_reconstructed(), + "State can't be queried until reconstruction is completed"); + BLI_assert_msg(view_item_but_ != nullptr, + "Hovered state can't be queried before the tree row is being built"); + + const uiViewItemHandle *this_item_handle = reinterpret_cast(this); + /* The new layout hasn't finished construction yet, so the final state of the button is unknown. + * Get the matching button from the previous redraw instead. */ + uiButViewItem *old_item_but = ui_block_view_find_matching_view_item_but_in_old_block( + view_item_but_->but.block, this_item_handle); + return old_item_but && (old_item_but->but.flag & UI_ACTIVE); +} + +bool AbstractTreeViewItem::is_collapsed() const +{ + BLI_assert_msg(get_tree_view().is_reconstructed(), + "State can't be queried until reconstruction is completed"); + return is_collapsible() && !is_open_; +} + +void AbstractTreeViewItem::toggle_collapsed() +{ + is_open_ = !is_open_; +} + +void AbstractTreeViewItem::set_collapsed(bool collapsed) +{ + is_open_ = !collapsed; +} + +bool AbstractTreeViewItem::is_collapsible() const +{ + if (children_.is_empty()) { + return false; + } + return this->supports_collapsing(); +} + +void AbstractTreeViewItem::ensure_parents_uncollapsed() +{ + for (AbstractTreeViewItem *parent = parent_; parent; parent = parent->parent_) { + parent->set_collapsed(false); + } +} + +bool AbstractTreeViewItem::matches(const AbstractViewItem &other) const +{ + const AbstractTreeViewItem &other_tree_item = dynamic_cast(other); + + if (!matches_single(other_tree_item)) { + return false; + } + if (count_parents() != other_tree_item.count_parents()) { + return false; + } + + for (AbstractTreeViewItem *parent = parent_, *other_parent = other_tree_item.parent_; + parent && other_parent; + parent = parent->parent_, other_parent = other_parent->parent_) { + if (!parent->matches_single(*other_parent)) { + return false; + } + } + + return true; +} + +uiButViewItem *AbstractTreeViewItem::view_item_button() +{ + return view_item_but_; +} + +void AbstractTreeViewItem::change_state_delayed() +{ + const std::optional should_be_active = this->should_be_active(); + if (should_be_active.has_value() && *should_be_active) { + activate(); + } +} + +/* ---------------------------------------------------------------------- */ + +class TreeViewLayoutBuilder { + uiBlock &block_; + + friend TreeViewBuilder; + + public: + void build_from_tree(const AbstractTreeView &tree_view); + void build_row(AbstractTreeViewItem &item) const; + + uiBlock &block() const; + uiLayout *current_layout() const; + + private: + /* Created through #TreeViewBuilder. */ + TreeViewLayoutBuilder(uiBlock &block); + + static void polish_layout(const uiBlock &block); +}; + +TreeViewLayoutBuilder::TreeViewLayoutBuilder(uiBlock &block) : block_(block) +{ +} + +void TreeViewLayoutBuilder::build_from_tree(const AbstractTreeView &tree_view) +{ + uiLayout *prev_layout = current_layout(); + + uiLayout *box = uiLayoutBox(prev_layout); + uiLayoutColumn(box, false); + + tree_view.foreach_item([this](AbstractTreeViewItem &item) { build_row(item); }, + AbstractTreeView::IterOptions::SkipCollapsed); + + UI_block_layout_set_current(&block(), prev_layout); +} + +void TreeViewLayoutBuilder::polish_layout(const uiBlock &block) +{ + LISTBASE_FOREACH_BACKWARD (uiBut *, but, &block.buttons) { + if (AbstractTreeViewItem::is_collapse_chevron_but(but) && but->next && + /* Embossed buttons with padding-less text padding look weird, so don't touch them. */ + ELEM(but->next->emboss, UI_EMBOSS_NONE, UI_EMBOSS_NONE_OR_STATUS)) { + UI_but_drawflag_enable(static_cast(but->next), UI_BUT_NO_TEXT_PADDING); + } + + if (but->type == UI_BTYPE_VIEW_ITEM) { + break; + } + } +} + +void TreeViewLayoutBuilder::build_row(AbstractTreeViewItem &item) const +{ + uiBlock &block_ = block(); + + uiLayout *prev_layout = current_layout(); + eUIEmbossType previous_emboss = UI_block_emboss_get(&block_); + + uiLayout *overlap = uiLayoutOverlap(prev_layout); + + uiLayoutRow(overlap, false); + /* Every item gets one! Other buttons can be overlapped on top. */ + item.add_treerow_button(block_); + + /* After adding tree-row button (would disable hover highlighting). */ + UI_block_emboss_set(&block_, UI_EMBOSS_NONE); + + uiLayout *row = uiLayoutRow(overlap, true); + item.add_indent(*row); + item.add_collapse_chevron(block_); + + if (item.is_renaming()) { + item.add_rename_button(*row); + } + else { + item.build_row(*row); + } + polish_layout(block_); + + UI_block_emboss_set(&block_, previous_emboss); + UI_block_layout_set_current(&block_, prev_layout); +} + +uiBlock &TreeViewLayoutBuilder::block() const +{ + return block_; +} + +uiLayout *TreeViewLayoutBuilder::current_layout() const +{ + return block().curlayout; +} + +/* ---------------------------------------------------------------------- */ + +TreeViewBuilder::TreeViewBuilder(uiBlock &block) : block_(block) +{ +} + +void TreeViewBuilder::build_tree_view(AbstractTreeView &tree_view) +{ + tree_view.build_tree(); + tree_view.update_from_old(block_); + tree_view.change_state_delayed(); + + TreeViewLayoutBuilder builder(block_); + builder.build_from_tree(tree_view); +} + +/* ---------------------------------------------------------------------- */ + +BasicTreeViewItem::BasicTreeViewItem(StringRef label, BIFIconID icon_) : icon(icon_) +{ + label_ = label; +} + +void BasicTreeViewItem::build_row(uiLayout &row) +{ + add_label(row); +} + +void BasicTreeViewItem::add_label(uiLayout &layout, StringRefNull label_override) +{ + const StringRefNull label = label_override.is_empty() ? StringRefNull(label_) : label_override; + + /* Some padding for labels without collapse chevron and no icon. Looks weird without. */ + if (icon == ICON_NONE && !is_collapsible()) { + uiItemS_ex(&layout, 0.8f); + } + uiItemL(&layout, IFACE_(label.c_str()), icon); +} + +void BasicTreeViewItem::on_activate() +{ + if (activate_fn_) { + activate_fn_(*this); + } +} + +void BasicTreeViewItem::set_on_activate_fn(ActivateFn fn) +{ + activate_fn_ = fn; +} + +void BasicTreeViewItem::set_is_active_fn(IsActiveFn is_active_fn) +{ + is_active_fn_ = is_active_fn; +} + +std::optional BasicTreeViewItem::should_be_active() const +{ + if (is_active_fn_) { + return is_active_fn_(); + } + return std::nullopt; +} + +} // namespace blender::ui -- cgit v1.2.3 From 16b145bc628d277228f5211a9dc076e516c443fb Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Tue, 19 Jul 2022 18:00:56 +0200 Subject: Cleanup: Improve API and documentation of interface_view.cc File documentation was outdated and could use general improvement. Function names didn't really reflect the level they are operating on. --- source/blender/editors/include/UI_interface.h | 4 ++-- .../blender/editors/interface/interface_dropboxes.cc | 4 ++-- source/blender/editors/interface/interface_ops.c | 8 ++++---- .../blender/editors/interface/views/interface_view.cc | 18 ++++++++++++------ source/blender/editors/interface/views/tree_view.cc | 2 +- 5 files changed, 21 insertions(+), 15 deletions(-) diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h index 99a1d038cca..a8d25b75036 100644 --- a/source/blender/editors/include/UI_interface.h +++ b/source/blender/editors/include/UI_interface.h @@ -3230,9 +3230,9 @@ bool UI_view_item_drop_handle(struct bContext *C, /** * \param xy: Coordinate to find a view item at, in window space. */ -uiViewItemHandle *UI_block_view_find_item_at(const struct ARegion *region, const int xy[2]) +uiViewItemHandle *UI_region_views_find_item_at(const struct ARegion *region, const int xy[2]) ATTR_NONNULL(); -uiViewItemHandle *UI_block_view_find_active_item(const struct ARegion *region); +uiViewItemHandle *UI_region_views_find_active_item(const struct ARegion *region); #ifdef __cplusplus } diff --git a/source/blender/editors/interface/interface_dropboxes.cc b/source/blender/editors/interface/interface_dropboxes.cc index df488fb9127..b72d8d2c238 100644 --- a/source/blender/editors/interface/interface_dropboxes.cc +++ b/source/blender/editors/interface/interface_dropboxes.cc @@ -28,7 +28,7 @@ static bool ui_view_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event) { const ARegion *region = CTX_wm_region(C); - const uiViewItemHandle *hovered_item = UI_block_view_find_item_at(region, event->xy); + const uiViewItemHandle *hovered_item = UI_region_views_find_item_at(region, event->xy); if (!hovered_item) { return false; } @@ -47,7 +47,7 @@ static char *ui_view_drop_tooltip(bContext *C, wmDropBox *UNUSED(drop)) { const ARegion *region = CTX_wm_region(C); - const uiViewItemHandle *hovered_item = UI_block_view_find_item_at(region, xy); + const uiViewItemHandle *hovered_item = UI_region_views_find_item_at(region, xy); if (!hovered_item) { return nullptr; } diff --git a/source/blender/editors/interface/interface_ops.c b/source/blender/editors/interface/interface_ops.c index 7a51ed23677..ddd805f6010 100644 --- a/source/blender/editors/interface/interface_ops.c +++ b/source/blender/editors/interface/interface_ops.c @@ -2058,7 +2058,7 @@ static bool ui_view_drop_poll(bContext *C) { const wmWindow *win = CTX_wm_window(C); const ARegion *region = CTX_wm_region(C); - const uiViewItemHandle *hovered_item = UI_block_view_find_item_at(region, win->eventstate->xy); + const uiViewItemHandle *hovered_item = UI_region_views_find_item_at(region, win->eventstate->xy); return hovered_item != NULL; } @@ -2070,7 +2070,7 @@ static int ui_view_drop_invoke(bContext *C, wmOperator *UNUSED(op), const wmEven } const ARegion *region = CTX_wm_region(C); - uiViewItemHandle *hovered_item = UI_block_view_find_item_at(region, event->xy); + uiViewItemHandle *hovered_item = UI_region_views_find_item_at(region, event->xy); if (!UI_view_item_drop_handle(C, hovered_item, event->customdata)) { return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH; @@ -2105,14 +2105,14 @@ static void UI_OT_view_drop(wmOperatorType *ot) static bool ui_view_item_rename_poll(bContext *C) { const ARegion *region = CTX_wm_region(C); - const uiViewItemHandle *active_item = UI_block_view_find_active_item(region); + const uiViewItemHandle *active_item = UI_region_views_find_active_item(region); return active_item != NULL && UI_view_item_can_rename(active_item); } static int ui_view_item_rename_exec(bContext *C, wmOperator *UNUSED(op)) { ARegion *region = CTX_wm_region(C); - uiViewItemHandle *active_item = UI_block_view_find_active_item(region); + uiViewItemHandle *active_item = UI_region_views_find_active_item(region); UI_view_item_begin_rename(active_item); ED_region_tag_redraw(region); diff --git a/source/blender/editors/interface/views/interface_view.cc b/source/blender/editors/interface/views/interface_view.cc index b35f6d2c969..c568a8cab74 100644 --- a/source/blender/editors/interface/views/interface_view.cc +++ b/source/blender/editors/interface/views/interface_view.cc @@ -3,10 +3,16 @@ /** \file * \ingroup edinterface * - * This part of the UI-View API is mostly needed to support persistent state of items within the - * view. Views are stored in #uiBlock's, and kept alive with it until after the next redraw. So we - * can compare the old view items with the new view items and keep state persistent for matching - * ones. + * Code to manage views as part of the regular screen hierarchy. E.g. managing ownership of views + * inside blocks (#uiBlock.views), looking up items in the region, passing WM notifiers to views, + * etc. + * + * Blocks and their contained views are reconstructed on every redraw. This file also contains + * functions related to this recreation of views inside blocks. For example to query state + * information before the view is done reconstructing (#AbstractView.is_reconstructed() returns + * false), it may be enough to query the previous version of the block/view/view-item. Since such + * queries rely on the details of the UI reconstruction process, they should remain internal to + * `interface/` code. */ #include @@ -86,7 +92,7 @@ void UI_block_views_listen(const uiBlock *block, const wmRegionListenerParams *l } } -uiViewItemHandle *UI_block_view_find_item_at(const ARegion *region, const int xy[2]) +uiViewItemHandle *UI_region_views_find_item_at(const ARegion *region, const int xy[2]) { uiButViewItem *item_but = (uiButViewItem *)ui_view_item_find_mouse_over(region, xy); if (!item_but) { @@ -96,7 +102,7 @@ uiViewItemHandle *UI_block_view_find_item_at(const ARegion *region, const int xy return item_but->view_item; } -uiViewItemHandle *UI_block_view_find_active_item(const ARegion *region) +uiViewItemHandle *UI_region_views_find_active_item(const ARegion *region) { uiButViewItem *item_but = (uiButViewItem *)ui_view_item_find_active(region); if (!item_but) { diff --git a/source/blender/editors/interface/views/tree_view.cc b/source/blender/editors/interface/views/tree_view.cc index c224226ba17..21078b711c7 100644 --- a/source/blender/editors/interface/views/tree_view.cc +++ b/source/blender/editors/interface/views/tree_view.cc @@ -168,7 +168,7 @@ void AbstractTreeViewItem::collapse_chevron_click_fn(struct bContext *C, const wmWindow *win = CTX_wm_window(C); const ARegion *region = CTX_wm_region(C); - uiViewItemHandle *hovered_item_handle = UI_block_view_find_item_at(region, win->eventstate->xy); + uiViewItemHandle *hovered_item_handle = UI_region_views_find_item_at(region, win->eventstate->xy); AbstractTreeViewItem *hovered_item = from_item_handle(hovered_item_handle); BLI_assert(hovered_item != nullptr); -- cgit v1.2.3 From 597955d0a82eb20805d1c9d567b82b1a51b39ccf Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Tue, 19 Jul 2022 12:10:30 -0500 Subject: Cleanup: Remove compile option for curves object After becb1530b1c81a408e20 the new curves object type isn't hidden behind an experimental flag anymore, and other areas depend on this, so disabling curves at compile time doesn't make sense anymore. --- intern/cycles/blender/CMakeLists.txt | 4 ---- intern/cycles/blender/curves.cpp | 21 ++------------------- intern/cycles/blender/geometry.cpp | 8 -------- source/blender/editors/object/CMakeLists.txt | 1 - source/blender/editors/space_buttons/CMakeLists.txt | 1 - .../blender/editors/space_buttons/buttons_context.c | 6 ------ source/blender/makesrna/intern/CMakeLists.txt | 1 - source/blender/makesrna/intern/makesrna.c | 2 -- source/blender/makesrna/intern/rna_ID.c | 6 ------ source/blender/makesrna/intern/rna_internal.h | 2 -- source/blender/makesrna/intern/rna_main.c | 4 ---- source/blender/makesrna/intern/rna_main_api.c | 6 ------ source/blender/makesrna/intern/rna_object.c | 4 ---- source/blender/modifiers/CMakeLists.txt | 1 - 14 files changed, 2 insertions(+), 65 deletions(-) diff --git a/intern/cycles/blender/CMakeLists.txt b/intern/cycles/blender/CMakeLists.txt index 4919b99cfe0..63d89221d20 100644 --- a/intern/cycles/blender/CMakeLists.txt +++ b/intern/cycles/blender/CMakeLists.txt @@ -128,10 +128,6 @@ if(WITH_OPENIMAGEDENOISE) ) endif() -if(WITH_EXPERIMENTAL_FEATURES) - add_definitions(-DWITH_NEW_CURVES_TYPE) -endif() - blender_add_lib(bf_intern_cycles "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") add_dependencies(bf_intern_cycles bf_rna) diff --git a/intern/cycles/blender/curves.cpp b/intern/cycles/blender/curves.cpp index 10012720bd8..263a5fc0e02 100644 --- a/intern/cycles/blender/curves.cpp +++ b/intern/cycles/blender/curves.cpp @@ -613,8 +613,6 @@ void BlenderSync::sync_particle_hair( } } -#ifdef WITH_NEW_CURVES_TYPE - static std::optional find_curves_radius_attribute(BL::Curves b_curves) { for (BL::Attribute &b_attribute : b_curves.attributes) { @@ -990,15 +988,6 @@ void BlenderSync::sync_hair(Hair *hair, BObjectInfo &b_ob_info, bool motion, int export_hair_curves(scene, hair, b_curves, need_motion, motion_scale); } } -#else -void BlenderSync::sync_hair(Hair *hair, BObjectInfo &b_ob_info, bool motion, int motion_step) -{ - (void)hair; - (void)b_ob_info; - (void)motion; - (void)motion_step; -} -#endif void BlenderSync::sync_hair(BL::Depsgraph b_depsgraph, BObjectInfo &b_ob_info, Hair *hair) { @@ -1010,14 +999,11 @@ void BlenderSync::sync_hair(BL::Depsgraph b_depsgraph, BObjectInfo &b_ob_info, H new_hair.set_used_shaders(used_shaders); if (view_layer.use_hair) { -#ifdef WITH_NEW_CURVES_TYPE if (b_ob_info.object_data.is_a(&RNA_Curves)) { /* Hair object. */ sync_hair(&new_hair, b_ob_info, false); } - else -#endif - { + else { /* Particle hair. */ bool need_undeformed = new_hair.need_attribute(scene, ATTR_STD_GENERATED); BL::Mesh b_mesh = object_to_mesh( @@ -1064,15 +1050,12 @@ void BlenderSync::sync_hair_motion(BL::Depsgraph b_depsgraph, /* Export deformed coordinates. */ if (ccl::BKE_object_is_deform_modified(b_ob_info, b_scene, preview)) { -#ifdef WITH_NEW_CURVES_TYPE if (b_ob_info.object_data.is_a(&RNA_Curves)) { /* Hair object. */ sync_hair(hair, b_ob_info, true, motion_step); return; } - else -#endif - { + else { /* Particle hair. */ BL::Mesh b_mesh = object_to_mesh( b_data, b_ob_info, b_depsgraph, false, Mesh::SUBDIVISION_NONE); diff --git a/intern/cycles/blender/geometry.cpp b/intern/cycles/blender/geometry.cpp index 215860f59e6..fc03ca6e489 100644 --- a/intern/cycles/blender/geometry.cpp +++ b/intern/cycles/blender/geometry.cpp @@ -18,11 +18,7 @@ CCL_NAMESPACE_BEGIN static Geometry::Type determine_geom_type(BObjectInfo &b_ob_info, bool use_particle_hair) { -#ifdef WITH_NEW_CURVES_TYPE if (b_ob_info.object_data.is_a(&RNA_Curves) || use_particle_hair) { -#else - if (use_particle_hair) { -#endif return Geometry::HAIR; } @@ -217,11 +213,7 @@ void BlenderSync::sync_geometry_motion(BL::Depsgraph &b_depsgraph, if (progress.get_cancel()) return; -#ifdef WITH_NEW_CURVES_TYPE if (b_ob_info.object_data.is_a(&RNA_Curves) || use_particle_hair) { -#else - if (use_particle_hair) { -#endif Hair *hair = static_cast(geom); sync_hair_motion(b_depsgraph, b_ob_info, hair, motion_step); } diff --git a/source/blender/editors/object/CMakeLists.txt b/source/blender/editors/object/CMakeLists.txt index 97376a495c1..a2f993c92b9 100644 --- a/source/blender/editors/object/CMakeLists.txt +++ b/source/blender/editors/object/CMakeLists.txt @@ -76,7 +76,6 @@ endif() if(WITH_EXPERIMENTAL_FEATURES) add_definitions(-DWITH_SIMULATION_DATABLOCK) add_definitions(-DWITH_POINT_CLOUD) - add_definitions(-DWITH_NEW_CURVES_TYPE) endif() blender_add_lib(bf_editor_object "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") diff --git a/source/blender/editors/space_buttons/CMakeLists.txt b/source/blender/editors/space_buttons/CMakeLists.txt index e2f1df74446..7d4f38b1841 100644 --- a/source/blender/editors/space_buttons/CMakeLists.txt +++ b/source/blender/editors/space_buttons/CMakeLists.txt @@ -35,7 +35,6 @@ endif() if(WITH_EXPERIMENTAL_FEATURES) add_definitions(-DWITH_SIMULATION_DATABLOCK) add_definitions(-DWITH_POINT_CLOUD) - add_definitions(-DWITH_NEW_CURVES_TYPE) endif() blender_add_lib(bf_editor_space_buttons "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") diff --git a/source/blender/editors/space_buttons/buttons_context.c b/source/blender/editors/space_buttons/buttons_context.c index 5780b0c9df7..c3479409f0d 100644 --- a/source/blender/editors/space_buttons/buttons_context.c +++ b/source/blender/editors/space_buttons/buttons_context.c @@ -258,11 +258,9 @@ static bool buttons_context_path_data(ButsContextPath *path, int type) if (RNA_struct_is_a(ptr->type, &RNA_GreasePencil) && (ELEM(type, -1, OB_GPENCIL))) { return true; } -#ifdef WITH_NEW_CURVES_TYPE if (RNA_struct_is_a(ptr->type, &RNA_Curves) && (ELEM(type, -1, OB_CURVES))) { return true; } -#endif #ifdef WITH_POINT_CLOUD if (RNA_struct_is_a(ptr->type, &RNA_PointCloud) && (ELEM(type, -1, OB_POINTCLOUD))) { return true; @@ -830,9 +828,7 @@ const char *buttons_context_dir[] = { "line_style", "collection", "gpencil", -#ifdef WITH_NEW_CURVES_TYPE "curves", -#endif #ifdef WITH_POINT_CLOUD "pointcloud", #endif @@ -926,12 +922,10 @@ int /*eContextResult*/ buttons_context(const bContext *C, set_pointer_type(path, result, &RNA_LightProbe); return CTX_RESULT_OK; } -#ifdef WITH_NEW_CURVES_TYPE if (CTX_data_equals(member, "curves")) { set_pointer_type(path, result, &RNA_Curves); return CTX_RESULT_OK; } -#endif #ifdef WITH_POINT_CLOUD if (CTX_data_equals(member, "pointcloud")) { set_pointer_type(path, result, &RNA_PointCloud); diff --git a/source/blender/makesrna/intern/CMakeLists.txt b/source/blender/makesrna/intern/CMakeLists.txt index af8767a1220..778c6a6bfdd 100644 --- a/source/blender/makesrna/intern/CMakeLists.txt +++ b/source/blender/makesrna/intern/CMakeLists.txt @@ -84,7 +84,6 @@ set(DEFSRC if(WITH_EXPERIMENTAL_FEATURES) add_definitions(-DWITH_SIMULATION_DATABLOCK) - add_definitions(-DWITH_NEW_CURVES_TYPE) list(APPEND DEFSRC rna_curves.c rna_simulation.c diff --git a/source/blender/makesrna/intern/makesrna.c b/source/blender/makesrna/intern/makesrna.c index a25fe201fa2..2b24bd0b39c 100644 --- a/source/blender/makesrna/intern/makesrna.c +++ b/source/blender/makesrna/intern/makesrna.c @@ -4435,9 +4435,7 @@ static RNAProcessItem PROCESS_ITEMS[] = { {"rna_dynamicpaint.c", NULL, RNA_def_dynamic_paint}, {"rna_fcurve.c", "rna_fcurve_api.c", RNA_def_fcurve}, {"rna_gpencil.c", NULL, RNA_def_gpencil}, -#ifdef WITH_NEW_CURVES_TYPE {"rna_curves.c", NULL, RNA_def_curves}, -#endif {"rna_image.c", "rna_image_api.c", RNA_def_image}, {"rna_key.c", NULL, RNA_def_key}, {"rna_light.c", NULL, RNA_def_light}, diff --git a/source/blender/makesrna/intern/rna_ID.c b/source/blender/makesrna/intern/rna_ID.c index 8d2ee0ab534..4003df4d685 100644 --- a/source/blender/makesrna/intern/rna_ID.c +++ b/source/blender/makesrna/intern/rna_ID.c @@ -375,11 +375,9 @@ short RNA_type_to_ID_code(const StructRNA *type) if (base_type == &RNA_FreestyleLineStyle) { return ID_LS; } -# ifdef WITH_NEW_CURVES_TYPE if (base_type == &RNA_Curves) { return ID_CV; } -# endif if (base_type == &RNA_Lattice) { return ID_LT; } @@ -483,11 +481,7 @@ StructRNA *ID_code_to_RNA_type(short idcode) case ID_GR: return &RNA_Collection; case ID_CV: -# ifdef WITH_NEW_CURVES_TYPE return &RNA_Curves; -# else - return &RNA_ID; -# endif case ID_IM: return &RNA_Image; case ID_KE: diff --git a/source/blender/makesrna/intern/rna_internal.h b/source/blender/makesrna/intern/rna_internal.h index 6ca8e668fa0..833060e40f8 100644 --- a/source/blender/makesrna/intern/rna_internal.h +++ b/source/blender/makesrna/intern/rna_internal.h @@ -504,9 +504,7 @@ void RNA_def_main_cachefiles(BlenderRNA *brna, PropertyRNA *cprop); void RNA_def_main_paintcurves(BlenderRNA *brna, PropertyRNA *cprop); void RNA_def_main_workspaces(BlenderRNA *brna, PropertyRNA *cprop); void RNA_def_main_lightprobes(BlenderRNA *brna, PropertyRNA *cprop); -#ifdef WITH_NEW_CURVES_TYPE void RNA_def_main_hair_curves(BlenderRNA *brna, PropertyRNA *cprop); -#endif void RNA_def_main_pointclouds(BlenderRNA *brna, PropertyRNA *cprop); void RNA_def_main_volumes(BlenderRNA *brna, PropertyRNA *cprop); #ifdef WITH_SIMULATION_DATABLOCK diff --git a/source/blender/makesrna/intern/rna_main.c b/source/blender/makesrna/intern/rna_main.c index 7200bcaa2a6..4aedb1fc611 100644 --- a/source/blender/makesrna/intern/rna_main.c +++ b/source/blender/makesrna/intern/rna_main.c @@ -96,9 +96,7 @@ RNA_MAIN_LISTBASE_FUNCS_DEF(collections) RNA_MAIN_LISTBASE_FUNCS_DEF(curves) RNA_MAIN_LISTBASE_FUNCS_DEF(fonts) RNA_MAIN_LISTBASE_FUNCS_DEF(gpencils) -# ifdef WITH_NEW_CURVES_TYPE RNA_MAIN_LISTBASE_FUNCS_DEF(hair_curves) -# endif RNA_MAIN_LISTBASE_FUNCS_DEF(images) RNA_MAIN_LISTBASE_FUNCS_DEF(lattices) RNA_MAIN_LISTBASE_FUNCS_DEF(libraries) @@ -375,7 +373,6 @@ void RNA_def_main(BlenderRNA *brna) "Light Probes", "Light Probe data-blocks", RNA_def_main_lightprobes}, -# ifdef WITH_NEW_CURVES_TYPE /** * \note The name `hair_curves` is chosen to be different than `curves`, * but they are generic curve data-blocks, not just for hair. @@ -386,7 +383,6 @@ void RNA_def_main(BlenderRNA *brna) "Hair Curves", "Hair curve data-blocks", RNA_def_main_hair_curves}, -# endif {"pointclouds", "PointCloud", "rna_Main_pointclouds_begin", diff --git a/source/blender/makesrna/intern/rna_main_api.c b/source/blender/makesrna/intern/rna_main_api.c index 6c621604e40..1f21fa3fab9 100644 --- a/source/blender/makesrna/intern/rna_main_api.c +++ b/source/blender/makesrna/intern/rna_main_api.c @@ -749,7 +749,6 @@ static bGPdata *rna_Main_gpencils_new(Main *bmain, const char *name) return gpd; } -# ifdef WITH_NEW_CURVES_TYPE static Curves *rna_Main_hair_curves_new(Main *bmain, const char *name) { char safe_name[MAX_ID_NAME - 2]; @@ -762,7 +761,6 @@ static Curves *rna_Main_hair_curves_new(Main *bmain, const char *name) return curves; } -# endif static PointCloud *rna_Main_pointclouds_new(Main *bmain, const char *name) { @@ -847,9 +845,7 @@ RNA_MAIN_ID_TAG_FUNCS_DEF(cachefiles, cachefiles, ID_CF) RNA_MAIN_ID_TAG_FUNCS_DEF(paintcurves, paintcurves, ID_PC) RNA_MAIN_ID_TAG_FUNCS_DEF(workspaces, workspaces, ID_WS) RNA_MAIN_ID_TAG_FUNCS_DEF(lightprobes, lightprobes, ID_LP) -# ifdef WITH_NEW_CURVES_TYPE RNA_MAIN_ID_TAG_FUNCS_DEF(hair_curves, hair_curves, ID_CV) -# endif RNA_MAIN_ID_TAG_FUNCS_DEF(pointclouds, pointclouds, ID_PT) RNA_MAIN_ID_TAG_FUNCS_DEF(volumes, volumes, ID_VO) # ifdef WITH_SIMULATION_DATABLOCK @@ -2255,7 +2251,6 @@ void RNA_def_main_lightprobes(BlenderRNA *brna, PropertyRNA *cprop) RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); } -# ifdef WITH_NEW_CURVES_TYPE void RNA_def_main_hair_curves(BlenderRNA *brna, PropertyRNA *cprop) { StructRNA *srna; @@ -2299,7 +2294,6 @@ void RNA_def_main_hair_curves(BlenderRNA *brna, PropertyRNA *cprop) parm = RNA_def_boolean(func, "value", 0, "Value", ""); RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); } -# endif void RNA_def_main_pointclouds(BlenderRNA *brna, PropertyRNA *cprop) { diff --git a/source/blender/makesrna/intern/rna_object.c b/source/blender/makesrna/intern/rna_object.c index a68ef361f04..103c77fa808 100644 --- a/source/blender/makesrna/intern/rna_object.c +++ b/source/blender/makesrna/intern/rna_object.c @@ -607,11 +607,7 @@ static StructRNA *rna_Object_data_typef(PointerRNA *ptr) case OB_GPENCIL: return &RNA_GreasePencil; case OB_CURVES: -# ifdef WITH_NEW_CURVES_TYPE return &RNA_Curves; -# else - return &RNA_ID; -# endif case OB_POINTCLOUD: return &RNA_PointCloud; case OB_VOLUME: diff --git a/source/blender/modifiers/CMakeLists.txt b/source/blender/modifiers/CMakeLists.txt index 1aac3c2191d..73daabec9b3 100644 --- a/source/blender/modifiers/CMakeLists.txt +++ b/source/blender/modifiers/CMakeLists.txt @@ -217,7 +217,6 @@ endif() if(WITH_EXPERIMENTAL_FEATURES) add_definitions(-DWITH_SIMULATION_DATABLOCK) - add_definitions(-DWITH_NEW_CURVES_TYPE) endif() # So we can have special tricks in modifier system. -- cgit v1.2.3 From d14c2d549b2fdde2a116f6a37837a1e3776da3cb Mon Sep 17 00:00:00 2001 From: Germano Cavalcante Date: Tue, 19 Jul 2022 11:43:38 -0300 Subject: Fix T99643: Vertex Crease fails with symmetry Create a transform conversion type that only considers the Vertex Custom Data. This reduces the complexity of converting Meshes and slightly optimizes the transformation. --- source/blender/editors/transform/CMakeLists.txt | 1 + source/blender/editors/transform/transform.h | 1 + .../blender/editors/transform/transform_convert.c | 15 +- .../blender/editors/transform/transform_convert.h | 5 + .../editors/transform/transform_convert_mesh.c | 26 +- .../transform/transform_convert_mesh_vert_cdata.c | 290 +++++++++++++++++++++ .../transform/transform_mode_edge_bevelweight.c | 11 +- .../editors/transform/transform_mode_edge_crease.c | 11 +- 8 files changed, 319 insertions(+), 41 deletions(-) create mode 100644 source/blender/editors/transform/transform_convert_mesh_vert_cdata.c diff --git a/source/blender/editors/transform/CMakeLists.txt b/source/blender/editors/transform/CMakeLists.txt index 68c4f4e76ca..6984dcb18d4 100644 --- a/source/blender/editors/transform/CMakeLists.txt +++ b/source/blender/editors/transform/CMakeLists.txt @@ -39,6 +39,7 @@ set(SRC transform_convert_mesh_edge.c transform_convert_mesh_skin.c transform_convert_mesh_uv.c + transform_convert_mesh_vert_cdata.c transform_convert_nla.c transform_convert_node.c transform_convert_object.c diff --git a/source/blender/editors/transform/transform.h b/source/blender/editors/transform/transform.h index 049d1b6a90e..b01affc7307 100644 --- a/source/blender/editors/transform/transform.h +++ b/source/blender/editors/transform/transform.h @@ -222,6 +222,7 @@ typedef enum { TC_MESH_EDGES, TC_MESH_SKIN, TC_MESH_UV, + TC_MESH_VERT_CDATA, TC_NLA_DATA, TC_NODE_DATA, TC_OBJECT, diff --git a/source/blender/editors/transform/transform_convert.c b/source/blender/editors/transform/transform_convert.c index bab700560fe..b0148ce508c 100644 --- a/source/blender/editors/transform/transform_convert.c +++ b/source/blender/editors/transform/transform_convert.c @@ -823,6 +823,7 @@ void special_aftertrans_update(bContext *C, TransInfo *t) case TC_GPENCIL: case TC_LATTICE_VERTS: case TC_MBALL_VERTS: + case TC_MESH_VERT_CDATA: case TC_MESH_UV: case TC_MESH_SKIN: case TC_OBJECT_TEXSPACE: @@ -905,6 +906,7 @@ static void init_proportional_edit(TransInfo *t) case TC_MESH_EDGES: case TC_MESH_SKIN: case TC_MESH_UV: + case TC_MESH_VERT_CDATA: case TC_NODE_DATA: case TC_OBJECT: case TC_PARTICLE_VERTS: @@ -939,7 +941,7 @@ static void init_proportional_edit(TransInfo *t) if (ELEM(convert_type, TC_ACTION_DATA, TC_GRAPH_EDIT_DATA)) { /* Distance has already been set. */ } - else if (ELEM(convert_type, TC_MESH_VERTS, TC_MESH_SKIN)) { + else if (ELEM(convert_type, TC_MESH_VERTS, TC_MESH_SKIN, TC_MESH_VERT_CDATA)) { if (t->flag & T_PROP_CONNECTED) { /* Already calculated by transform_convert_mesh_connectivity_distance. */ } @@ -984,6 +986,7 @@ static void init_TransDataContainers(TransInfo *t, case TC_MESH_EDGES: case TC_MESH_SKIN: case TC_MESH_UV: + case TC_MESH_VERT_CDATA: break; case TC_ACTION_DATA: case TC_GRAPH_EDIT_DATA: @@ -1095,6 +1098,7 @@ static eTFlag flags_from_data_type(eTConvertType data_type) case TC_MBALL_VERTS: case TC_MESH_VERTS: case TC_MESH_SKIN: + case TC_MESH_VERT_CDATA: return T_EDIT | T_POINTS; case TC_MESH_EDGES: return T_EDIT; @@ -1194,6 +1198,9 @@ static eTConvertType convert_type_get(const TransInfo *t, Object **r_obj_armatur if (t->mode == TFM_SKIN_RESIZE) { convert_type = TC_MESH_SKIN; } + else if (ELEM(t->mode, TFM_BWEIGHT, TFM_VERT_CREASE)) { + convert_type = TC_MESH_VERT_CDATA; + } else { convert_type = TC_MESH_VERTS; } @@ -1317,6 +1324,9 @@ void createTransData(bContext *C, TransInfo *t) case TC_NLA_DATA: createTransNlaData(C, t); break; + case TC_MESH_VERT_CDATA: + createTransMeshVertCData(t); + break; case TC_NODE_DATA: createTransNodeData(t); break; @@ -1623,6 +1633,9 @@ void recalcData(TransInfo *t) case TC_MESH_UV: recalcData_uv(t); break; + case TC_MESH_VERT_CDATA: + recalcData_mesh_cdata(t); + break; case TC_NLA_DATA: recalcData_nla(t); break; diff --git a/source/blender/editors/transform/transform_convert.h b/source/blender/editors/transform/transform_convert.h index f25a3db3f75..1b7c33e443c 100644 --- a/source/blender/editors/transform/transform_convert.h +++ b/source/blender/editors/transform/transform_convert.h @@ -252,6 +252,11 @@ void createTransUVs(bContext *C, TransInfo *t); /* helper for recalcData() - for Image Editor transforms */ void recalcData_uv(TransInfo *t); +/* transform_convert_mesh_vert_cdata.c */ + +void createTransMeshVertCData(TransInfo *t); +void recalcData_mesh_cdata(TransInfo *t); + /* transform_convert_nla.c */ void createTransNlaData(bContext *C, TransInfo *t); diff --git a/source/blender/editors/transform/transform_convert_mesh.c b/source/blender/editors/transform/transform_convert_mesh.c index c8d943df3fa..de736fad94e 100644 --- a/source/blender/editors/transform/transform_convert_mesh.c +++ b/source/blender/editors/transform/transform_convert_mesh.c @@ -1408,7 +1408,6 @@ static void VertsToTransData(TransInfo *t, TransDataExtension *tx, BMEditMesh *em, BMVert *eve, - float *bweight, const struct TransIslandData *island_data, const int island_index) { @@ -1449,11 +1448,7 @@ static void VertsToTransData(TransInfo *t, td->ext = NULL; td->val = NULL; td->extra = eve; - if (ELEM(t->mode, TFM_BWEIGHT, TFM_VERT_CREASE)) { - td->val = bweight; - td->ival = *bweight; - } - else if (t->mode == TFM_SHRINKFATTEN) { + if (t->mode == TFM_SHRINKFATTEN) { td->ext = tx; tx->isize[0] = BM_vert_calc_shell_factor_ex(eve, no, BM_ELEM_SELECT); } @@ -1589,17 +1584,6 @@ void createTransEditVerts(TransInfo *t) "TransObData ext"); } - int cd_vert_bweight_offset = -1; - int cd_vert_crease_offset = -1; - if (t->mode == TFM_BWEIGHT) { - BM_mesh_cd_flag_ensure(bm, BKE_mesh_from_object(tc->obedit), ME_CDFLAG_VERT_BWEIGHT); - cd_vert_bweight_offset = CustomData_get_offset(&bm->vdata, CD_BWEIGHT); - } - else if (t->mode == TFM_VERT_CREASE) { - BM_mesh_cd_flag_ensure(bm, BKE_mesh_from_object(tc->obedit), ME_CDFLAG_VERT_CREASE); - cd_vert_crease_offset = CustomData_get_offset(&bm->vdata, CD_CREASE); - } - TransData *tob = tc->data; TransDataMirror *td_mirror = tc->data_mirror; BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, a) { @@ -1632,15 +1616,9 @@ void createTransEditVerts(TransInfo *t) td_mirror++; } else if (prop_mode || BM_elem_flag_test(eve, BM_ELEM_SELECT)) { - float *bweight = (cd_vert_bweight_offset != -1) ? - BM_ELEM_CD_GET_VOID_P(eve, cd_vert_bweight_offset) : - (cd_vert_crease_offset != -1) ? - BM_ELEM_CD_GET_VOID_P(eve, cd_vert_crease_offset) : - NULL; - /* Do not use the island center in case we are using islands * only to get axis for snap/rotate to normal... */ - VertsToTransData(t, tob, tx, em, eve, bweight, &island_data, island_index); + VertsToTransData(t, tob, tx, em, eve, &island_data, island_index); if (tx) { tx++; } diff --git a/source/blender/editors/transform/transform_convert_mesh_vert_cdata.c b/source/blender/editors/transform/transform_convert_mesh_vert_cdata.c new file mode 100644 index 00000000000..ff58c2c888b --- /dev/null +++ b/source/blender/editors/transform/transform_convert_mesh_vert_cdata.c @@ -0,0 +1,290 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2001-2002 NaN Holding BV. All rights reserved. */ + +/** \file + * \ingroup edtransform + */ + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" + +#include "MEM_guardedalloc.h" + +#include "BLI_math.h" + +#include "BKE_context.h" +#include "BKE_crazyspace.h" +#include "BKE_editmesh.h" +#include "BKE_modifier.h" +#include "BKE_scene.h" + +#include "ED_mesh.h" + +#include "DEG_depsgraph_query.h" + +#include "transform.h" +#include "transform_orientations.h" + +#include "transform_convert.h" + +/* -------------------------------------------------------------------- */ +/** \name Edit Mesh #CD_BWEIGHT and #CD_CREASE Transform Creation + * \{ */ + +static float *tc_mesh_cdata_transdata_center(const struct TransIslandData *island_data, + const int island_index, + BMVert *eve) +{ + if (island_data->center && island_index != -1) { + return island_data->center[island_index]; + } + return eve->co; +} + +static void tc_mesh_cdata_transdata_create(TransDataBasic *td, + BMEditMesh *em, + BMVert *eve, + float *weight, + const struct TransIslandData *island_data, + const int island_index) +{ + BLI_assert(BM_elem_flag_test(eve, BM_ELEM_HIDDEN) == 0); + + td->loc = weight; + td->iloc[0] = *weight; + + if (BM_elem_flag_test(eve, BM_ELEM_SELECT)) { + td->flag |= TD_SELECTED; + } + + copy_v3_v3(td->center, tc_mesh_cdata_transdata_center(island_data, island_index, eve)); + td->extra = eve; +} + +void createTransMeshVertCData(TransInfo *t) +{ + BLI_assert(ELEM(t->mode, TFM_BWEIGHT, TFM_VERT_CREASE)); + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + BMEditMesh *em = BKE_editmesh_from_object(tc->obedit); + Mesh *me = tc->obedit->data; + BMesh *bm = em->bm; + BMVert *eve; + BMIter iter; + float mtx[3][3], smtx[3][3]; + int a; + const int prop_mode = (t->flag & T_PROP_EDIT) ? (t->flag & T_PROP_EDIT_ALL) : 0; + + struct TransIslandData island_data = {NULL}; + struct TransMirrorData mirror_data = {NULL}; + struct TransMeshDataCrazySpace crazyspace_data = {NULL}; + + /* Support other objects using PET to adjust these, unless connected is enabled. */ + if ((!prop_mode || (prop_mode & T_PROP_CONNECTED)) && (bm->totvertsel == 0)) { + continue; + } + + int cd_offset = -1; + if (t->mode == TFM_BWEIGHT) { + BM_mesh_cd_flag_ensure(bm, me, ME_CDFLAG_VERT_BWEIGHT); + cd_offset = CustomData_get_offset(&bm->vdata, CD_BWEIGHT); + } + else { + BM_mesh_cd_flag_ensure(bm, me, ME_CDFLAG_VERT_CREASE); + cd_offset = CustomData_get_offset(&bm->vdata, CD_CREASE); + } + + if (cd_offset == -1) { + continue; + } + + int data_len = 0; + if (prop_mode) { + BM_ITER_MESH (eve, &iter, bm, BM_VERTS_OF_MESH) { + if (!BM_elem_flag_test(eve, BM_ELEM_HIDDEN)) { + data_len++; + } + } + } + else { + data_len = bm->totvertsel; + } + + if (data_len == 0) { + continue; + } + + const bool is_island_center = (t->around == V3D_AROUND_LOCAL_ORIGINS); + if (is_island_center) { + /* In this specific case, near-by vertices will need to know + * the island of the nearest connected vertex. */ + const bool calc_single_islands = ((prop_mode & T_PROP_CONNECTED) && + (t->around == V3D_AROUND_LOCAL_ORIGINS) && + (em->selectmode & SCE_SELECT_VERTEX)); + + const bool calc_island_center = false; + const bool calc_island_axismtx = false; + + transform_convert_mesh_islands_calc( + em, calc_single_islands, calc_island_center, calc_island_axismtx, &island_data); + } + + copy_m3_m4(mtx, tc->obedit->obmat); + /* we use a pseudo-inverse so that when one of the axes is scaled to 0, + * matrix inversion still works and we can still moving along the other */ + pseudoinverse_m3_m3(smtx, mtx, PSEUDOINVERSE_EPSILON); + + /* Original index of our connected vertex when connected distances are calculated. + * Optional, allocate if needed. */ + int *dists_index = NULL; + float *dists = NULL; + if (prop_mode & T_PROP_CONNECTED) { + dists = MEM_mallocN(bm->totvert * sizeof(float), __func__); + if (is_island_center) { + dists_index = MEM_mallocN(bm->totvert * sizeof(int), __func__); + } + transform_convert_mesh_connectivity_distance(em->bm, mtx, dists, dists_index); + } + + /* Create TransDataMirror. */ + if (tc->use_mirror_axis_any) { + bool use_topology = (me->editflag & ME_EDIT_MIRROR_TOPO) != 0; + bool use_select = (t->flag & T_PROP_EDIT) == 0; + const bool mirror_axis[3] = { + tc->use_mirror_axis_x, tc->use_mirror_axis_y, tc->use_mirror_axis_z}; + transform_convert_mesh_mirrordata_calc( + em, use_select, use_topology, mirror_axis, &mirror_data); + + if (mirror_data.vert_map) { + tc->data_mirror_len = mirror_data.mirror_elem_len; + tc->data_mirror = MEM_mallocN(mirror_data.mirror_elem_len * sizeof(*tc->data_mirror), + __func__); + + BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, a) { + if (prop_mode || BM_elem_flag_test(eve, BM_ELEM_SELECT)) { + if (mirror_data.vert_map[a].index != -1) { + data_len--; + } + } + } + } + } + + /* Detect CrazySpace [tm]. */ + transform_convert_mesh_crazyspace_detect(t, tc, em, &crazyspace_data); + + /* Create TransData. */ + BLI_assert(data_len >= 1); + tc->data_len = data_len; + tc->data = MEM_callocN(data_len * sizeof(TransData), "TransObData(Mesh EditMode)"); + + TransData *td = tc->data; + TransDataMirror *td_mirror = tc->data_mirror; + BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, a) { + if (BM_elem_flag_test(eve, BM_ELEM_HIDDEN)) { + continue; + } + + int island_index = -1; + if (island_data.island_vert_map) { + const int connected_index = (dists_index && dists_index[a] != -1) ? dists_index[a] : a; + island_index = island_data.island_vert_map[connected_index]; + } + + float *weight = BM_ELEM_CD_GET_VOID_P(eve, cd_offset); + if (mirror_data.vert_map && mirror_data.vert_map[a].index != -1) { + tc_mesh_cdata_transdata_create( + (TransDataBasic *)td_mirror, em, eve, weight, &island_data, island_index); + + int elem_index = mirror_data.vert_map[a].index; + BMVert *v_src = BM_vert_at_index(bm, elem_index); + + td_mirror->flag |= mirror_data.vert_map[a].flag; + td_mirror->loc_src = BM_ELEM_CD_GET_VOID_P(v_src, cd_offset); + td_mirror++; + } + else if (prop_mode || BM_elem_flag_test(eve, BM_ELEM_SELECT)) { + tc_mesh_cdata_transdata_create( + (TransDataBasic *)td, em, eve, weight, &island_data, island_index); + + if (t->around == V3D_AROUND_LOCAL_ORIGINS) { + createSpaceNormal(td->axismtx, eve->no); + } + else { + /* Setting normals */ + copy_v3_v3(td->axismtx[2], eve->no); + td->axismtx[0][0] = td->axismtx[0][1] = td->axismtx[0][2] = td->axismtx[1][0] = + td->axismtx[1][1] = td->axismtx[1][2] = 0.0f; + } + + if (prop_mode) { + if (prop_mode & T_PROP_CONNECTED) { + td->dist = dists[a]; + } + else { + td->flag |= TD_NOTCONNECTED; + td->dist = FLT_MAX; + } + } + + /* CrazySpace */ + transform_convert_mesh_crazyspace_transdata_set( + mtx, + smtx, + crazyspace_data.defmats ? crazyspace_data.defmats[a] : NULL, + crazyspace_data.quats && BM_elem_flag_test(eve, BM_ELEM_TAG) ? + crazyspace_data.quats[a] : + NULL, + td); + + td++; + } + } + + transform_convert_mesh_islanddata_free(&island_data); + transform_convert_mesh_mirrordata_free(&mirror_data); + transform_convert_mesh_crazyspace_free(&crazyspace_data); + if (dists) { + MEM_freeN(dists); + } + if (dists_index) { + MEM_freeN(dists_index); + } + } +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Recalc Mesh Data + * \{ */ + +static void tc_mesh_cdata_apply_to_mirror(TransInfo *t) +{ + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + if (tc->use_mirror_axis_any) { + TransDataMirror *td_mirror = tc->data_mirror; + for (int i = 0; i < tc->data_mirror_len; i++, td_mirror++) { + td_mirror->loc[0] = td_mirror->loc_src[0]; + } + } + } +} + +void recalcData_mesh_cdata(TransInfo *t) +{ + bool is_canceling = t->state == TRANS_CANCEL; + /* mirror modifier clipping? */ + if (!is_canceling) { + if (!(t->flag & T_NO_MIRROR)) { + tc_mesh_cdata_apply_to_mirror(t); + } + } + + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + DEG_id_tag_update(tc->obedit->data, ID_RECALC_GEOMETRY); + BMEditMesh *em = BKE_editmesh_from_object(tc->obedit); + BKE_editmesh_looptri_and_normals_calc(em); + } +} + +/** \} */ diff --git a/source/blender/editors/transform/transform_mode_edge_bevelweight.c b/source/blender/editors/transform/transform_mode_edge_bevelweight.c index 987d8396907..e96e74b596c 100644 --- a/source/blender/editors/transform/transform_mode_edge_bevelweight.c +++ b/source/blender/editors/transform/transform_mode_edge_bevelweight.c @@ -44,16 +44,11 @@ static void transdata_elem_bevel_weight(const TransInfo *UNUSED(t), TransData *td, const float weight) { - if (td->val == NULL) { + if (td->loc == NULL) { return; } - *td->val = td->ival + weight * td->factor; - if (*td->val < 0.0f) { - *td->val = 0.0f; - } - if (*td->val > 1.0f) { - *td->val = 1.0f; - } + *td->loc = td->iloc[0] + weight * td->factor; + CLAMP(*td->loc, 0.0f, 1.0f); } static void transdata_elem_bevel_weight_fn(void *__restrict iter_data_v, diff --git a/source/blender/editors/transform/transform_mode_edge_crease.c b/source/blender/editors/transform/transform_mode_edge_crease.c index f1acc2a4c9a..1a3ccf30387 100644 --- a/source/blender/editors/transform/transform_mode_edge_crease.c +++ b/source/blender/editors/transform/transform_mode_edge_crease.c @@ -44,17 +44,12 @@ static void transdata_elem_crease(const TransInfo *UNUSED(t), TransData *td, const float crease) { - if (td->val == NULL) { + if (td->loc == NULL) { return; } - *td->val = td->ival + crease * td->factor; - if (*td->val < 0.0f) { - *td->val = 0.0f; - } - if (*td->val > 1.0f) { - *td->val = 1.0f; - } + *td->loc = td->iloc[0] + crease * td->factor; + CLAMP(*td->loc, 0.0f, 1.0f); } static void transdata_elem_crease_fn(void *__restrict iter_data_v, -- cgit v1.2.3 From edc89c7f46f25122d88eb096f34626da035032be Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Tue, 19 Jul 2022 19:21:38 +0200 Subject: Fix build error when not using unity build --- source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc index 057e08cc33d..dd8471d2dac 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc @@ -9,6 +9,7 @@ #include "node_geometry_util.hh" +#include "BKE_curves.hh" #include "BKE_spline.hh" namespace blender::nodes::node_geo_curve_fillet_cc { -- cgit v1.2.3 From cd478fbfb3d0bf6c80b112fadbe4c0e1f550dc90 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Tue, 19 Jul 2022 18:04:34 +0200 Subject: Fix error printed in console when running Shade Flat operator Only the Shade Smooth operator has autosmooth settings. --- source/blender/editors/object/object_edit.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/source/blender/editors/object/object_edit.c b/source/blender/editors/object/object_edit.c index e65b5a61299..4896ddb5258 100644 --- a/source/blender/editors/object/object_edit.c +++ b/source/blender/editors/object/object_edit.c @@ -1469,8 +1469,6 @@ void OBJECT_OT_paths_clear(wmOperatorType *ot) static int shade_smooth_exec(bContext *C, wmOperator *op) { const bool use_smooth = STREQ(op->idname, "OBJECT_OT_shade_smooth"); - const bool use_auto_smooth = RNA_boolean_get(op->ptr, "use_auto_smooth"); - const float auto_smooth_angle = RNA_float_get(op->ptr, "auto_smooth_angle"); bool changed_multi = false; bool has_linked_data = false; @@ -1518,7 +1516,11 @@ static int shade_smooth_exec(bContext *C, wmOperator *op) bool changed = false; if (ob->type == OB_MESH) { BKE_mesh_smooth_flag_set(ob->data, use_smooth); - BKE_mesh_auto_smooth_flag_set(ob->data, use_auto_smooth, auto_smooth_angle); + if (use_smooth) { + const bool use_auto_smooth = RNA_boolean_get(op->ptr, "use_auto_smooth"); + const float auto_smooth_angle = RNA_float_get(op->ptr, "auto_smooth_angle"); + BKE_mesh_auto_smooth_flag_set(ob->data, use_auto_smooth, auto_smooth_angle); + } BKE_mesh_batch_cache_dirty_tag(ob->data, BKE_MESH_BATCH_DIRTY_ALL); changed = true; } -- cgit v1.2.3 From 75e62df4297093b2a65e295a43b0ff0fbffe44b7 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Tue, 19 Jul 2022 19:21:51 +0200 Subject: Cleanup: compiler warning --- source/blender/editors/transform/transform_convert_mesh_vert_cdata.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/source/blender/editors/transform/transform_convert_mesh_vert_cdata.c b/source/blender/editors/transform/transform_convert_mesh_vert_cdata.c index ff58c2c888b..104b45de138 100644 --- a/source/blender/editors/transform/transform_convert_mesh_vert_cdata.c +++ b/source/blender/editors/transform/transform_convert_mesh_vert_cdata.c @@ -42,7 +42,6 @@ static float *tc_mesh_cdata_transdata_center(const struct TransIslandData *islan } static void tc_mesh_cdata_transdata_create(TransDataBasic *td, - BMEditMesh *em, BMVert *eve, float *weight, const struct TransIslandData *island_data, @@ -193,7 +192,7 @@ void createTransMeshVertCData(TransInfo *t) float *weight = BM_ELEM_CD_GET_VOID_P(eve, cd_offset); if (mirror_data.vert_map && mirror_data.vert_map[a].index != -1) { tc_mesh_cdata_transdata_create( - (TransDataBasic *)td_mirror, em, eve, weight, &island_data, island_index); + (TransDataBasic *)td_mirror, eve, weight, &island_data, island_index); int elem_index = mirror_data.vert_map[a].index; BMVert *v_src = BM_vert_at_index(bm, elem_index); @@ -204,7 +203,7 @@ void createTransMeshVertCData(TransInfo *t) } else if (prop_mode || BM_elem_flag_test(eve, BM_ELEM_SELECT)) { tc_mesh_cdata_transdata_create( - (TransDataBasic *)td, em, eve, weight, &island_data, island_index); + (TransDataBasic *)td, eve, weight, &island_data, island_index); if (t->around == V3D_AROUND_LOCAL_ORIGINS) { createSpaceNormal(td->axismtx, eve->no); -- cgit v1.2.3 From 3b7ac10d6252a2a9cecbb9be7850a1dea41e281d Mon Sep 17 00:00:00 2001 From: Tomek Gubala Date: Tue, 19 Jul 2022 19:24:20 +0200 Subject: UV: add Snap Cursor to Origin Similar to snapping to the world origin in the 3D viewport. This can be found in the Shift+S pie menu and UV > Snap menu. Differential Revision: https://developer.blender.org/D15055 --- release/scripts/startup/bl_ui/space_image.py | 6 ++++++ source/blender/editors/uvedit/uvedit_ops.c | 11 +++++++++++ 2 files changed, 17 insertions(+) diff --git a/release/scripts/startup/bl_ui/space_image.py b/release/scripts/startup/bl_ui/space_image.py index 5d25d02d98e..ab3c863ea2d 100644 --- a/release/scripts/startup/bl_ui/space_image.py +++ b/release/scripts/startup/bl_ui/space_image.py @@ -306,6 +306,7 @@ class IMAGE_MT_uvs_snap(Menu): layout.operator("uv.snap_cursor", text="Cursor to Pixels").target = 'PIXELS' layout.operator("uv.snap_cursor", text="Cursor to Selected").target = 'SELECTED' + layout.operator("uv.snap_cursor", text="Cursor to Origin").target = 'ORIGIN' class IMAGE_MT_uvs_mirror(Menu): @@ -572,6 +573,11 @@ class IMAGE_MT_uvs_snap_pie(Menu): text="Selected to Adjacent Unselected", icon='RESTRICT_SELECT_OFF', ).target = 'ADJACENT_UNSELECTED' + pie.operator( + "uv.snap_cursor", + text="Cursor to Origin", + icon='PIVOT_CURSOR', + ).target = 'ORIGIN' class IMAGE_MT_view_pie(Menu): diff --git a/source/blender/editors/uvedit/uvedit_ops.c b/source/blender/editors/uvedit/uvedit_ops.c index 4e99eb3fc0f..74a9989f550 100644 --- a/source/blender/editors/uvedit/uvedit_ops.c +++ b/source/blender/editors/uvedit/uvedit_ops.c @@ -1036,6 +1036,12 @@ static bool uv_snap_cursor_to_selection(Scene *scene, return ED_uvedit_center_multi(scene, objects_edit, objects_len, sima->cursor, sima->around); } +static void uv_snap_cursor_to_origin(float uvco[2]) +{ + uvco[0] = 0; + uvco[1] = 0; +} + static int uv_snap_cursor_exec(bContext *C, wmOperator *op) { SpaceImage *sima = CTX_wm_space_image(C); @@ -1058,6 +1064,10 @@ static int uv_snap_cursor_exec(bContext *C, wmOperator *op) MEM_freeN(objects); break; } + case 2: + uv_snap_cursor_to_origin(sima->cursor); + changed = true; + break; } if (!changed) { @@ -1074,6 +1084,7 @@ static void UV_OT_snap_cursor(wmOperatorType *ot) static const EnumPropertyItem target_items[] = { {0, "PIXELS", 0, "Pixels", ""}, {1, "SELECTED", 0, "Selected", ""}, + {2, "ORIGIN", 0, "Origin", ""}, {0, NULL, 0, NULL, NULL}, }; -- cgit v1.2.3 From 43d04c7eeb8a3ec50b1c022522fb8fa6b42a883c Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Tue, 19 Jul 2022 19:31:26 +0200 Subject: UI Code Quality: Move eyedropper files to own folder Part of T98518 --- source/blender/editors/interface/CMakeLists.txt | 16 +- .../interface/eyedroppers/eyedropper_color.c | 554 +++++++++++++++++++++ .../interface/eyedroppers/eyedropper_colorband.c | 367 ++++++++++++++ .../interface/eyedroppers/eyedropper_datablock.c | 378 ++++++++++++++ .../interface/eyedroppers/eyedropper_depth.c | 381 ++++++++++++++ .../interface/eyedroppers/eyedropper_driver.c | 220 ++++++++ .../eyedroppers/eyedropper_gpencil_color.c | 373 ++++++++++++++ .../interface/eyedroppers/eyedropper_intern.h | 57 +++ .../interface/eyedroppers/interface_eyedropper.c | 161 ++++++ .../editors/interface/interface_eyedropper.c | 161 ------ .../editors/interface/interface_eyedropper_color.c | 554 --------------------- .../interface/interface_eyedropper_colorband.c | 367 -------------- .../interface/interface_eyedropper_datablock.c | 378 -------------- .../editors/interface/interface_eyedropper_depth.c | 381 -------------- .../interface/interface_eyedropper_driver.c | 220 -------- .../interface/interface_eyedropper_gpencil_color.c | 373 -------------- .../interface/interface_eyedropper_intern.h | 57 --- 17 files changed, 2499 insertions(+), 2499 deletions(-) create mode 100644 source/blender/editors/interface/eyedroppers/eyedropper_color.c create mode 100644 source/blender/editors/interface/eyedroppers/eyedropper_colorband.c create mode 100644 source/blender/editors/interface/eyedroppers/eyedropper_datablock.c create mode 100644 source/blender/editors/interface/eyedroppers/eyedropper_depth.c create mode 100644 source/blender/editors/interface/eyedroppers/eyedropper_driver.c create mode 100644 source/blender/editors/interface/eyedroppers/eyedropper_gpencil_color.c create mode 100644 source/blender/editors/interface/eyedroppers/eyedropper_intern.h create mode 100644 source/blender/editors/interface/eyedroppers/interface_eyedropper.c delete mode 100644 source/blender/editors/interface/interface_eyedropper.c delete mode 100644 source/blender/editors/interface/interface_eyedropper_color.c delete mode 100644 source/blender/editors/interface/interface_eyedropper_colorband.c delete mode 100644 source/blender/editors/interface/interface_eyedropper_datablock.c delete mode 100644 source/blender/editors/interface/interface_eyedropper_depth.c delete mode 100644 source/blender/editors/interface/interface_eyedropper_driver.c delete mode 100644 source/blender/editors/interface/interface_eyedropper_gpencil_color.c delete mode 100644 source/blender/editors/interface/interface_eyedropper_intern.h diff --git a/source/blender/editors/interface/CMakeLists.txt b/source/blender/editors/interface/CMakeLists.txt index 4c206f9c057..07c3bfdafbf 100644 --- a/source/blender/editors/interface/CMakeLists.txt +++ b/source/blender/editors/interface/CMakeLists.txt @@ -26,6 +26,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 interface.cc interface_align.c interface_anim.c @@ -35,13 +42,6 @@ set(SRC interface_drag.cc interface_draw.c interface_dropboxes.cc - interface_eyedropper.c - interface_eyedropper_color.c - interface_eyedropper_colorband.c - interface_eyedropper_datablock.c - interface_eyedropper_depth.c - interface_eyedropper_driver.c - interface_eyedropper_gpencil_color.c interface_handlers.c interface_icons.c interface_icons_event.c @@ -80,7 +80,7 @@ set(SRC views/interface_view.cc views/tree_view.cc - interface_eyedropper_intern.h + eyedroppers/eyedropper_intern.h interface_intern.h interface_regions_intern.h ) diff --git a/source/blender/editors/interface/eyedroppers/eyedropper_color.c b/source/blender/editors/interface/eyedroppers/eyedropper_color.c new file mode 100644 index 00000000000..a2161008ec4 --- /dev/null +++ b/source/blender/editors/interface/eyedroppers/eyedropper_color.c @@ -0,0 +1,554 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2009 Blender Foundation. All rights reserved. */ + +/** \file + * \ingroup edinterface + * + * Eyedropper (RGB Color) + * + * Defines: + * - #UI_OT_eyedropper_color + */ + +#include "MEM_guardedalloc.h" + +#include "DNA_screen_types.h" +#include "DNA_space_types.h" + +#include "BLI_listbase.h" +#include "BLI_math_vector.h" +#include "BLI_string.h" + +#include "BKE_context.h" +#include "BKE_cryptomatte.h" +#include "BKE_image.h" +#include "BKE_main.h" +#include "BKE_node.h" +#include "BKE_screen.h" + +#include "NOD_composite.h" + +#include "RNA_access.h" +#include "RNA_prototypes.h" + +#include "UI_interface.h" + +#include "IMB_colormanagement.h" +#include "IMB_imbuf_types.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "RNA_define.h" + +#include "interface_intern.h" + +#include "ED_clip.h" +#include "ED_image.h" +#include "ED_node.h" +#include "ED_screen.h" + +#include "RE_pipeline.h" + +#include "eyedropper_intern.h" + +typedef struct Eyedropper { + struct ColorManagedDisplay *display; + + PointerRNA ptr; + PropertyRNA *prop; + int index; + bool is_undo; + + bool is_set; + float init_col[3]; /* for resetting on cancel */ + + bool accum_start; /* has mouse been pressed */ + float accum_col[3]; + int accum_tot; + + void *draw_handle_sample_text; + char sample_text[MAX_NAME]; + + bNode *crypto_node; + struct CryptomatteSession *cryptomatte_session; +} Eyedropper; + +static void eyedropper_draw_cb(const wmWindow *window, void *arg) +{ + Eyedropper *eye = arg; + eyedropper_draw_cursor_text_window(window, eye->sample_text); +} + +static bool eyedropper_init(bContext *C, wmOperator *op) +{ + Eyedropper *eye = MEM_callocN(sizeof(Eyedropper), __func__); + + uiBut *but = UI_context_active_but_prop_get(C, &eye->ptr, &eye->prop, &eye->index); + const enum PropertySubType prop_subtype = eye->prop ? RNA_property_subtype(eye->prop) : 0; + + if ((eye->ptr.data == NULL) || (eye->prop == NULL) || + (RNA_property_editable(&eye->ptr, eye->prop) == false) || + (RNA_property_array_length(&eye->ptr, eye->prop) < 3) || + (RNA_property_type(eye->prop) != PROP_FLOAT) || + (ELEM(prop_subtype, PROP_COLOR, PROP_COLOR_GAMMA) == 0)) { + MEM_freeN(eye); + return false; + } + op->customdata = eye; + + eye->is_undo = UI_but_flag_is_set(but, UI_BUT_UNDO); + + float col[4]; + RNA_property_float_get_array(&eye->ptr, eye->prop, col); + if (eye->ptr.type == &RNA_CompositorNodeCryptomatteV2) { + eye->crypto_node = (bNode *)eye->ptr.data; + eye->cryptomatte_session = ntreeCompositCryptomatteSession(CTX_data_scene(C), + eye->crypto_node); + eye->draw_handle_sample_text = WM_draw_cb_activate(CTX_wm_window(C), eyedropper_draw_cb, eye); + } + + if (prop_subtype != PROP_COLOR) { + Scene *scene = CTX_data_scene(C); + const char *display_device; + + display_device = scene->display_settings.display_device; + eye->display = IMB_colormanagement_display_get_named(display_device); + + /* store initial color */ + if (eye->display) { + IMB_colormanagement_display_to_scene_linear_v3(col, eye->display); + } + } + copy_v3_v3(eye->init_col, col); + + return true; +} + +static void eyedropper_exit(bContext *C, wmOperator *op) +{ + Eyedropper *eye = op->customdata; + wmWindow *window = CTX_wm_window(C); + WM_cursor_modal_restore(window); + + if (eye->draw_handle_sample_text) { + WM_draw_cb_exit(window, eye->draw_handle_sample_text); + eye->draw_handle_sample_text = NULL; + } + + if (eye->cryptomatte_session) { + BKE_cryptomatte_free(eye->cryptomatte_session); + eye->cryptomatte_session = NULL; + } + + MEM_SAFE_FREE(op->customdata); +} + +/* *** eyedropper_color_ helper functions *** */ + +static bool eyedropper_cryptomatte_sample_renderlayer_fl(RenderLayer *render_layer, + const char *prefix, + const float fpos[2], + float r_col[3]) +{ + if (!render_layer) { + return false; + } + + const int render_layer_name_len = BLI_strnlen(render_layer->name, sizeof(render_layer->name)); + if (strncmp(prefix, render_layer->name, render_layer_name_len) != 0) { + return false; + } + + const int prefix_len = strlen(prefix); + if (prefix_len <= render_layer_name_len + 1) { + return false; + } + + /* RenderResult from images can have no render layer name. */ + const char *render_pass_name_prefix = render_layer_name_len ? + prefix + 1 + render_layer_name_len : + prefix; + + LISTBASE_FOREACH (RenderPass *, render_pass, &render_layer->passes) { + if (STRPREFIX(render_pass->name, render_pass_name_prefix) && + !STREQLEN(render_pass->name, render_pass_name_prefix, sizeof(render_pass->name))) { + BLI_assert(render_pass->channels == 4); + const int x = (int)(fpos[0] * render_pass->rectx); + const int y = (int)(fpos[1] * render_pass->recty); + const int offset = 4 * (y * render_pass->rectx + x); + zero_v3(r_col); + r_col[0] = render_pass->rect[offset]; + return true; + } + } + + return false; +} +static bool eyedropper_cryptomatte_sample_render_fl(const bNode *node, + const char *prefix, + const float fpos[2], + float r_col[3]) +{ + bool success = false; + Scene *scene = (Scene *)node->id; + BLI_assert(GS(scene->id.name) == ID_SCE); + Render *re = RE_GetSceneRender(scene); + + if (re) { + RenderResult *rr = RE_AcquireResultRead(re); + if (rr) { + LISTBASE_FOREACH (ViewLayer *, view_layer, &scene->view_layers) { + RenderLayer *render_layer = RE_GetRenderLayer(rr, view_layer->name); + success = eyedropper_cryptomatte_sample_renderlayer_fl(render_layer, prefix, fpos, r_col); + if (success) { + break; + } + } + } + RE_ReleaseResult(re); + } + return success; +} + +static bool eyedropper_cryptomatte_sample_image_fl(const bNode *node, + NodeCryptomatte *crypto, + const char *prefix, + const float fpos[2], + float r_col[3]) +{ + bool success = false; + Image *image = (Image *)node->id; + BLI_assert((image == NULL) || (GS(image->id.name) == ID_IM)); + ImageUser *iuser = &crypto->iuser; + + if (image && image->type == IMA_TYPE_MULTILAYER) { + ImBuf *ibuf = BKE_image_acquire_ibuf(image, iuser, NULL); + if (image->rr) { + LISTBASE_FOREACH (RenderLayer *, render_layer, &image->rr->layers) { + success = eyedropper_cryptomatte_sample_renderlayer_fl(render_layer, prefix, fpos, r_col); + if (success) { + break; + } + } + } + BKE_image_release_ibuf(image, ibuf, NULL); + } + return success; +} + +static bool eyedropper_cryptomatte_sample_fl(bContext *C, + Eyedropper *eye, + const int m_xy[2], + float r_col[3]) +{ + bNode *node = eye->crypto_node; + NodeCryptomatte *crypto = node ? ((NodeCryptomatte *)node->storage) : NULL; + + if (!crypto) { + return false; + } + + bScreen *screen = CTX_wm_screen(C); + ScrArea *area = BKE_screen_find_area_xy(screen, SPACE_TYPE_ANY, m_xy); + if (!area || !ELEM(area->spacetype, SPACE_IMAGE, SPACE_NODE, SPACE_CLIP)) { + return false; + } + + ARegion *region = BKE_area_find_region_xy(area, RGN_TYPE_WINDOW, m_xy); + if (!region) { + return false; + } + + int mval[2] = {m_xy[0] - region->winrct.xmin, m_xy[1] - region->winrct.ymin}; + float fpos[2] = {-1.0f, -1.0}; + switch (area->spacetype) { + case SPACE_IMAGE: { + SpaceImage *sima = area->spacedata.first; + ED_space_image_get_position(sima, region, mval, fpos); + break; + } + case SPACE_NODE: { + Main *bmain = CTX_data_main(C); + SpaceNode *snode = area->spacedata.first; + ED_space_node_get_position(bmain, snode, region, mval, fpos); + break; + } + case SPACE_CLIP: { + SpaceClip *sc = area->spacedata.first; + ED_space_clip_get_position(sc, region, mval, fpos); + break; + } + default: { + break; + } + } + + if (fpos[0] < 0.0f || fpos[1] < 0.0f || fpos[0] >= 1.0f || fpos[1] >= 1.0f) { + return false; + } + + /* CMP_CRYPTOMATTE_SRC_RENDER and CMP_CRYPTOMATTE_SRC_IMAGE require a referenced image/scene to + * work properly. */ + if (!node->id) { + return false; + } + + /* TODO(jbakker): Migrate this file to cc and use std::string as return param. */ + char prefix[MAX_NAME + 1]; + const Scene *scene = CTX_data_scene(C); + ntreeCompositCryptomatteLayerPrefix(scene, node, prefix, sizeof(prefix) - 1); + prefix[MAX_NAME] = '\0'; + + if (node->custom1 == CMP_CRYPTOMATTE_SRC_RENDER) { + return eyedropper_cryptomatte_sample_render_fl(node, prefix, fpos, r_col); + } + if (node->custom1 == CMP_CRYPTOMATTE_SRC_IMAGE) { + return eyedropper_cryptomatte_sample_image_fl(node, crypto, prefix, fpos, r_col); + } + return false; +} + +void eyedropper_color_sample_fl(bContext *C, const int m_xy[2], float r_col[3]) +{ + /* we could use some clever */ + Main *bmain = CTX_data_main(C); + wmWindowManager *wm = CTX_wm_manager(C); + const char *display_device = CTX_data_scene(C)->display_settings.display_device; + struct ColorManagedDisplay *display = IMB_colormanagement_display_get_named(display_device); + + int mval[2]; + wmWindow *win; + ScrArea *area; + datadropper_win_area_find(C, m_xy, mval, &win, &area); + + if (area) { + if (area->spacetype == SPACE_IMAGE) { + ARegion *region = BKE_area_find_region_xy(area, RGN_TYPE_WINDOW, mval); + if (region) { + SpaceImage *sima = area->spacedata.first; + const int region_mval[2] = {mval[0] - region->winrct.xmin, mval[1] - region->winrct.ymin}; + + if (ED_space_image_color_sample(sima, region, region_mval, r_col, NULL)) { + return; + } + } + } + else if (area->spacetype == SPACE_NODE) { + ARegion *region = BKE_area_find_region_xy(area, RGN_TYPE_WINDOW, mval); + if (region) { + SpaceNode *snode = area->spacedata.first; + const int region_mval[2] = {mval[0] - region->winrct.xmin, mval[1] - region->winrct.ymin}; + + if (ED_space_node_color_sample(bmain, snode, region, region_mval, r_col)) { + return; + } + } + } + else if (area->spacetype == SPACE_CLIP) { + ARegion *region = BKE_area_find_region_xy(area, RGN_TYPE_WINDOW, mval); + if (region) { + SpaceClip *sc = area->spacedata.first; + const int region_mval[2] = {mval[0] - region->winrct.xmin, mval[1] - region->winrct.ymin}; + + if (ED_space_clip_color_sample(sc, region, region_mval, r_col)) { + return; + } + } + } + } + + if (win) { + /* Fallback to simple opengl picker. */ + WM_window_pixel_sample_read(wm, win, mval, r_col); + IMB_colormanagement_display_to_scene_linear_v3(r_col, display); + } + else { + zero_v3(r_col); + } +} + +/* sets the sample color RGB, maintaining A */ +static void eyedropper_color_set(bContext *C, Eyedropper *eye, const float col[3]) +{ + float col_conv[4]; + + /* to maintain alpha */ + RNA_property_float_get_array(&eye->ptr, eye->prop, col_conv); + + /* convert from linear rgb space to display space */ + if (eye->display) { + copy_v3_v3(col_conv, col); + IMB_colormanagement_scene_linear_to_display_v3(col_conv, eye->display); + } + else { + copy_v3_v3(col_conv, col); + } + + RNA_property_float_set_array(&eye->ptr, eye->prop, col_conv); + eye->is_set = true; + + RNA_property_update(C, &eye->ptr, eye->prop); +} + +static void eyedropper_color_sample(bContext *C, Eyedropper *eye, const int m_xy[2]) +{ + /* Accumulate color. */ + float col[3]; + if (eye->crypto_node) { + if (!eyedropper_cryptomatte_sample_fl(C, eye, m_xy, col)) { + return; + } + } + else { + eyedropper_color_sample_fl(C, m_xy, col); + } + + if (!eye->crypto_node) { + add_v3_v3(eye->accum_col, col); + eye->accum_tot++; + } + else { + copy_v3_v3(eye->accum_col, col); + eye->accum_tot = 1; + } + + /* Apply to property. */ + float accum_col[3]; + if (eye->accum_tot > 1) { + mul_v3_v3fl(accum_col, eye->accum_col, 1.0f / (float)eye->accum_tot); + } + else { + copy_v3_v3(accum_col, eye->accum_col); + } + eyedropper_color_set(C, eye, accum_col); +} + +static void eyedropper_color_sample_text_update(bContext *C, Eyedropper *eye, const int m_xy[2]) +{ + float col[3]; + eye->sample_text[0] = '\0'; + + if (eye->cryptomatte_session) { + if (eyedropper_cryptomatte_sample_fl(C, eye, m_xy, col)) { + BKE_cryptomatte_find_name( + eye->cryptomatte_session, col[0], eye->sample_text, sizeof(eye->sample_text)); + eye->sample_text[sizeof(eye->sample_text) - 1] = '\0'; + } + } +} + +static void eyedropper_cancel(bContext *C, wmOperator *op) +{ + Eyedropper *eye = op->customdata; + if (eye->is_set) { + eyedropper_color_set(C, eye, eye->init_col); + } + eyedropper_exit(C, op); +} + +/* main modal status check */ +static int eyedropper_modal(bContext *C, wmOperator *op, const wmEvent *event) +{ + Eyedropper *eye = (Eyedropper *)op->customdata; + + /* handle modal keymap */ + if (event->type == EVT_MODAL_MAP) { + switch (event->val) { + case EYE_MODAL_CANCEL: + eyedropper_cancel(C, op); + return OPERATOR_CANCELLED; + case EYE_MODAL_SAMPLE_CONFIRM: { + const bool is_undo = eye->is_undo; + if (eye->accum_tot == 0) { + eyedropper_color_sample(C, eye, event->xy); + } + eyedropper_exit(C, op); + /* Could support finished & undo-skip. */ + return is_undo ? OPERATOR_FINISHED : OPERATOR_CANCELLED; + } + case EYE_MODAL_SAMPLE_BEGIN: + /* enable accum and make first sample */ + eye->accum_start = true; + eyedropper_color_sample(C, eye, event->xy); + break; + case EYE_MODAL_SAMPLE_RESET: + eye->accum_tot = 0; + zero_v3(eye->accum_col); + eyedropper_color_sample(C, eye, event->xy); + break; + } + } + else if (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) { + if (eye->accum_start) { + /* button is pressed so keep sampling */ + eyedropper_color_sample(C, eye, event->xy); + } + + if (eye->draw_handle_sample_text) { + eyedropper_color_sample_text_update(C, eye, event->xy); + ED_region_tag_redraw(CTX_wm_region(C)); + } + } + + return OPERATOR_RUNNING_MODAL; +} + +/* Modal Operator init */ +static int eyedropper_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) +{ + /* init */ + if (eyedropper_init(C, op)) { + wmWindow *win = CTX_wm_window(C); + /* Workaround for de-activating the button clearing the cursor, see T76794 */ + UI_context_active_but_clear(C, win, CTX_wm_region(C)); + WM_cursor_modal_set(win, WM_CURSOR_EYEDROPPER); + + /* add temp handler */ + WM_event_add_modal_handler(C, op); + + return OPERATOR_RUNNING_MODAL; + } + return OPERATOR_PASS_THROUGH; +} + +/* Repeat operator */ +static int eyedropper_exec(bContext *C, wmOperator *op) +{ + /* init */ + if (eyedropper_init(C, op)) { + + /* do something */ + + /* cleanup */ + eyedropper_exit(C, op); + + return OPERATOR_FINISHED; + } + return OPERATOR_PASS_THROUGH; +} + +static bool eyedropper_poll(bContext *C) +{ + /* Actual test for active button happens later, since we don't + * know which one is active until mouse over. */ + return (CTX_wm_window(C) != NULL); +} + +void UI_OT_eyedropper_color(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Eyedropper"; + ot->idname = "UI_OT_eyedropper_color"; + ot->description = "Sample a color from the Blender window to store in a property"; + + /* api callbacks */ + ot->invoke = eyedropper_invoke; + ot->modal = eyedropper_modal; + ot->cancel = eyedropper_cancel; + ot->exec = eyedropper_exec; + ot->poll = eyedropper_poll; + + /* flags */ + ot->flag = OPTYPE_UNDO | OPTYPE_BLOCKING | OPTYPE_INTERNAL; +} diff --git a/source/blender/editors/interface/eyedroppers/eyedropper_colorband.c b/source/blender/editors/interface/eyedroppers/eyedropper_colorband.c new file mode 100644 index 00000000000..3f63a8020ed --- /dev/null +++ b/source/blender/editors/interface/eyedroppers/eyedropper_colorband.c @@ -0,0 +1,367 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2009 Blender Foundation. All rights reserved. */ + +/** \file + * \ingroup edinterface + * + * Eyedropper (Color Band). + * + * Operates by either: + * - Dragging a straight line, sampling pixels formed by the line to extract a gradient. + * - Clicking on points, adding each color to the end of the color-band. + * + * Defines: + * - #UI_OT_eyedropper_colorramp + * - #UI_OT_eyedropper_colorramp_point + */ + +#include "MEM_guardedalloc.h" + +#include "DNA_screen_types.h" + +#include "BLI_bitmap_draw_2d.h" +#include "BLI_math_vector.h" + +#include "BKE_colorband.h" +#include "BKE_context.h" + +#include "RNA_access.h" +#include "RNA_prototypes.h" + +#include "UI_interface.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "interface_intern.h" + +#include "eyedropper_intern.h" + +typedef struct Colorband_RNAUpdateCb { + PointerRNA ptr; + PropertyRNA *prop; +} Colorband_RNAUpdateCb; + +typedef struct EyedropperColorband { + int event_xy_last[2]; + /* Alpha is currently fixed at 1.0, may support in future. */ + float (*color_buffer)[4]; + int color_buffer_alloc; + int color_buffer_len; + bool sample_start; + ColorBand init_color_band; + ColorBand *color_band; + PointerRNA ptr; + PropertyRNA *prop; + bool is_undo; + bool is_set; +} EyedropperColorband; + +/* For user-data only. */ +struct EyedropperColorband_Context { + bContext *context; + EyedropperColorband *eye; +}; + +static bool eyedropper_colorband_init(bContext *C, wmOperator *op) +{ + ColorBand *band = NULL; + + uiBut *but = UI_context_active_but_get(C); + + PointerRNA rna_update_ptr = PointerRNA_NULL; + PropertyRNA *rna_update_prop = NULL; + bool is_undo = true; + + if (but == NULL) { + /* pass */ + } + else { + if (but->type == UI_BTYPE_COLORBAND) { + /* When invoked with a hotkey, we can find the band in 'but->poin'. */ + band = (ColorBand *)but->poin; + } + else { + /* When invoked from a button it's in custom_data field. */ + band = (ColorBand *)but->custom_data; + } + + if (band) { + rna_update_ptr = ((Colorband_RNAUpdateCb *)but->func_argN)->ptr; + rna_update_prop = ((Colorband_RNAUpdateCb *)but->func_argN)->prop; + is_undo = UI_but_flag_is_set(but, UI_BUT_UNDO); + } + } + + if (!band) { + const PointerRNA ptr = CTX_data_pointer_get_type(C, "color_ramp", &RNA_ColorRamp); + if (ptr.data != NULL) { + band = ptr.data; + + /* Set this to a sub-member of the property to trigger an update. */ + rna_update_ptr = ptr; + rna_update_prop = &rna_ColorRamp_color_mode; + is_undo = RNA_struct_undo_check(ptr.type); + } + } + + if (!band) { + return false; + } + + EyedropperColorband *eye = MEM_callocN(sizeof(EyedropperColorband), __func__); + eye->color_buffer_alloc = 16; + eye->color_buffer = MEM_mallocN(sizeof(*eye->color_buffer) * eye->color_buffer_alloc, __func__); + eye->color_buffer_len = 0; + eye->color_band = band; + eye->init_color_band = *eye->color_band; + eye->ptr = rna_update_ptr; + eye->prop = rna_update_prop; + eye->is_undo = is_undo; + + op->customdata = eye; + + return true; +} + +static void eyedropper_colorband_sample_point(bContext *C, + EyedropperColorband *eye, + const int m_xy[2]) +{ + if (eye->event_xy_last[0] != m_xy[0] || eye->event_xy_last[1] != m_xy[1]) { + float col[4]; + col[3] = 1.0f; /* TODO: sample alpha */ + eyedropper_color_sample_fl(C, m_xy, col); + if (eye->color_buffer_len + 1 == eye->color_buffer_alloc) { + eye->color_buffer_alloc *= 2; + eye->color_buffer = MEM_reallocN(eye->color_buffer, + sizeof(*eye->color_buffer) * eye->color_buffer_alloc); + } + copy_v4_v4(eye->color_buffer[eye->color_buffer_len], col); + eye->color_buffer_len += 1; + copy_v2_v2_int(eye->event_xy_last, m_xy); + eye->is_set = true; + } +} + +static bool eyedropper_colorband_sample_callback(int mx, int my, void *userdata) +{ + struct EyedropperColorband_Context *data = userdata; + bContext *C = data->context; + EyedropperColorband *eye = data->eye; + const int cursor[2] = {mx, my}; + eyedropper_colorband_sample_point(C, eye, cursor); + return true; +} + +static void eyedropper_colorband_sample_segment(bContext *C, + EyedropperColorband *eye, + const int m_xy[2]) +{ + /* Since the mouse tends to move rather rapidly we use #BLI_bitmap_draw_2d_line_v2v2i + * to interpolate between the reported coordinates */ + struct EyedropperColorband_Context userdata = {C, eye}; + BLI_bitmap_draw_2d_line_v2v2i( + eye->event_xy_last, m_xy, eyedropper_colorband_sample_callback, &userdata); +} + +static void eyedropper_colorband_exit(bContext *C, wmOperator *op) +{ + WM_cursor_modal_restore(CTX_wm_window(C)); + + if (op->customdata) { + EyedropperColorband *eye = op->customdata; + MEM_freeN(eye->color_buffer); + MEM_freeN(eye); + op->customdata = NULL; + } +} + +static void eyedropper_colorband_apply(bContext *C, wmOperator *op) +{ + EyedropperColorband *eye = op->customdata; + /* Always filter, avoids noise in resulting color-band. */ + const bool filter_samples = true; + BKE_colorband_init_from_table_rgba( + eye->color_band, eye->color_buffer, eye->color_buffer_len, filter_samples); + eye->is_set = true; + if (eye->prop) { + RNA_property_update(C, &eye->ptr, eye->prop); + } +} + +static void eyedropper_colorband_cancel(bContext *C, wmOperator *op) +{ + EyedropperColorband *eye = op->customdata; + if (eye->is_set) { + *eye->color_band = eye->init_color_band; + if (eye->prop) { + RNA_property_update(C, &eye->ptr, eye->prop); + } + } + eyedropper_colorband_exit(C, op); +} + +/* main modal status check */ +static int eyedropper_colorband_modal(bContext *C, wmOperator *op, const wmEvent *event) +{ + EyedropperColorband *eye = op->customdata; + /* handle modal keymap */ + if (event->type == EVT_MODAL_MAP) { + switch (event->val) { + case EYE_MODAL_CANCEL: + eyedropper_colorband_cancel(C, op); + return OPERATOR_CANCELLED; + case EYE_MODAL_SAMPLE_CONFIRM: { + const bool is_undo = eye->is_undo; + eyedropper_colorband_sample_segment(C, eye, event->xy); + eyedropper_colorband_apply(C, op); + eyedropper_colorband_exit(C, op); + /* Could support finished & undo-skip. */ + return is_undo ? OPERATOR_FINISHED : OPERATOR_CANCELLED; + } + case EYE_MODAL_SAMPLE_BEGIN: + /* enable accum and make first sample */ + eye->sample_start = true; + eyedropper_colorband_sample_point(C, eye, event->xy); + eyedropper_colorband_apply(C, op); + copy_v2_v2_int(eye->event_xy_last, event->xy); + break; + case EYE_MODAL_SAMPLE_RESET: + break; + } + } + else if (event->type == MOUSEMOVE) { + if (eye->sample_start) { + eyedropper_colorband_sample_segment(C, eye, event->xy); + eyedropper_colorband_apply(C, op); + } + } + return OPERATOR_RUNNING_MODAL; +} + +static int eyedropper_colorband_point_modal(bContext *C, wmOperator *op, const wmEvent *event) +{ + EyedropperColorband *eye = op->customdata; + /* handle modal keymap */ + if (event->type == EVT_MODAL_MAP) { + switch (event->val) { + case EYE_MODAL_POINT_CANCEL: + eyedropper_colorband_cancel(C, op); + return OPERATOR_CANCELLED; + case EYE_MODAL_POINT_CONFIRM: + eyedropper_colorband_apply(C, op); + eyedropper_colorband_exit(C, op); + return OPERATOR_FINISHED; + case EYE_MODAL_POINT_REMOVE_LAST: + if (eye->color_buffer_len > 0) { + eye->color_buffer_len -= 1; + eyedropper_colorband_apply(C, op); + } + break; + case EYE_MODAL_POINT_SAMPLE: + eyedropper_colorband_sample_point(C, eye, event->xy); + eyedropper_colorband_apply(C, op); + if (eye->color_buffer_len == MAXCOLORBAND) { + eyedropper_colorband_exit(C, op); + return OPERATOR_FINISHED; + } + break; + case EYE_MODAL_SAMPLE_RESET: + *eye->color_band = eye->init_color_band; + if (eye->prop) { + RNA_property_update(C, &eye->ptr, eye->prop); + } + eye->color_buffer_len = 0; + break; + } + } + return OPERATOR_RUNNING_MODAL; +} + +/* Modal Operator init */ +static int eyedropper_colorband_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) +{ + /* init */ + if (eyedropper_colorband_init(C, op)) { + wmWindow *win = CTX_wm_window(C); + /* Workaround for de-activating the button clearing the cursor, see T76794 */ + UI_context_active_but_clear(C, win, CTX_wm_region(C)); + WM_cursor_modal_set(win, WM_CURSOR_EYEDROPPER); + + /* add temp handler */ + WM_event_add_modal_handler(C, op); + + return OPERATOR_RUNNING_MODAL; + } + return OPERATOR_CANCELLED; +} + +/* Repeat operator */ +static int eyedropper_colorband_exec(bContext *C, wmOperator *op) +{ + /* init */ + if (eyedropper_colorband_init(C, op)) { + + /* do something */ + + /* cleanup */ + eyedropper_colorband_exit(C, op); + + return OPERATOR_FINISHED; + } + return OPERATOR_CANCELLED; +} + +static bool eyedropper_colorband_poll(bContext *C) +{ + uiBut *but = UI_context_active_but_get(C); + if (but && but->type == UI_BTYPE_COLORBAND) { + return true; + } + const PointerRNA ptr = CTX_data_pointer_get_type(C, "color_ramp", &RNA_ColorRamp); + if (ptr.data != NULL) { + return true; + } + return false; +} + +void UI_OT_eyedropper_colorramp(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Eyedropper Colorband"; + ot->idname = "UI_OT_eyedropper_colorramp"; + ot->description = "Sample a color band"; + + /* api callbacks */ + ot->invoke = eyedropper_colorband_invoke; + ot->modal = eyedropper_colorband_modal; + ot->cancel = eyedropper_colorband_cancel; + ot->exec = eyedropper_colorband_exec; + ot->poll = eyedropper_colorband_poll; + + /* flags */ + ot->flag = OPTYPE_UNDO | OPTYPE_BLOCKING | OPTYPE_INTERNAL; + + /* properties */ +} + +void UI_OT_eyedropper_colorramp_point(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Eyedropper Colorband (Points)"; + ot->idname = "UI_OT_eyedropper_colorramp_point"; + ot->description = "Point-sample a color band"; + + /* api callbacks */ + ot->invoke = eyedropper_colorband_invoke; + ot->modal = eyedropper_colorband_point_modal; + ot->cancel = eyedropper_colorband_cancel; + ot->exec = eyedropper_colorband_exec; + ot->poll = eyedropper_colorband_poll; + + /* flags */ + ot->flag = OPTYPE_UNDO | OPTYPE_BLOCKING | OPTYPE_INTERNAL; + + /* properties */ +} diff --git a/source/blender/editors/interface/eyedroppers/eyedropper_datablock.c b/source/blender/editors/interface/eyedroppers/eyedropper_datablock.c new file mode 100644 index 00000000000..1710d0faa4d --- /dev/null +++ b/source/blender/editors/interface/eyedroppers/eyedropper_datablock.c @@ -0,0 +1,378 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2009 Blender Foundation. All rights reserved. */ + +/** \file + * \ingroup edinterface + * + * Eyedropper (ID data-blocks) + * + * Defines: + * - #UI_OT_eyedropper_id + */ + +#include "MEM_guardedalloc.h" + +#include "DNA_object_types.h" +#include "DNA_screen_types.h" +#include "DNA_space_types.h" + +#include "BLI_math_vector.h" +#include "BLI_string.h" + +#include "BLT_translation.h" + +#include "BKE_context.h" +#include "BKE_idtype.h" +#include "BKE_report.h" +#include "BKE_screen.h" + +#include "RNA_access.h" + +#include "UI_interface.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "ED_outliner.h" +#include "ED_screen.h" +#include "ED_space_api.h" +#include "ED_view3d.h" + +#include "eyedropper_intern.h" +#include "interface_intern.h" + +/** + * \note #DataDropper is only internal name to avoid confusion with other kinds of eye-droppers. + */ +typedef struct DataDropper { + PointerRNA ptr; + PropertyRNA *prop; + short idcode; + const char *idcode_name; + bool is_undo; + + ID *init_id; /* for resetting on cancel */ + + ScrArea *cursor_area; /* Area under the cursor */ + ARegionType *art; + void *draw_handle_pixel; + int name_pos[2]; + char name[200]; +} DataDropper; + +static void datadropper_draw_cb(const struct bContext *UNUSED(C), + ARegion *UNUSED(region), + void *arg) +{ + DataDropper *ddr = arg; + eyedropper_draw_cursor_text_region(ddr->name_pos, ddr->name); +} + +static int datadropper_init(bContext *C, wmOperator *op) +{ + int index_dummy; + StructRNA *type; + + SpaceType *st; + ARegionType *art; + + st = BKE_spacetype_from_id(SPACE_VIEW3D); + art = BKE_regiontype_from_id(st, RGN_TYPE_WINDOW); + + DataDropper *ddr = MEM_callocN(sizeof(DataDropper), __func__); + + uiBut *but = UI_context_active_but_prop_get(C, &ddr->ptr, &ddr->prop, &index_dummy); + + if ((ddr->ptr.data == NULL) || (ddr->prop == NULL) || + (RNA_property_editable(&ddr->ptr, ddr->prop) == false) || + (RNA_property_type(ddr->prop) != PROP_POINTER)) { + MEM_freeN(ddr); + return false; + } + op->customdata = ddr; + + ddr->is_undo = UI_but_flag_is_set(but, UI_BUT_UNDO); + + ddr->cursor_area = CTX_wm_area(C); + ddr->art = art; + ddr->draw_handle_pixel = ED_region_draw_cb_activate( + art, datadropper_draw_cb, ddr, REGION_DRAW_POST_PIXEL); + + type = RNA_property_pointer_type(&ddr->ptr, ddr->prop); + ddr->idcode = RNA_type_to_ID_code(type); + BLI_assert(ddr->idcode != 0); + /* Note we can translate here (instead of on draw time), + * because this struct has very short lifetime. */ + ddr->idcode_name = TIP_(BKE_idtype_idcode_to_name(ddr->idcode)); + + const PointerRNA ptr = RNA_property_pointer_get(&ddr->ptr, ddr->prop); + ddr->init_id = ptr.owner_id; + + return true; +} + +static void datadropper_exit(bContext *C, wmOperator *op) +{ + wmWindow *win = CTX_wm_window(C); + + WM_cursor_modal_restore(win); + + if (op->customdata) { + DataDropper *ddr = (DataDropper *)op->customdata; + + if (ddr->art) { + ED_region_draw_cb_exit(ddr->art, ddr->draw_handle_pixel); + } + + MEM_freeN(op->customdata); + + op->customdata = NULL; + } + + WM_event_add_mousemove(win); +} + +/* *** datadropper id helper functions *** */ +/** + * \brief get the ID from the 3D view or outliner. + */ +static void datadropper_id_sample_pt( + bContext *C, wmWindow *win, ScrArea *area, DataDropper *ddr, const int m_xy[2], ID **r_id) +{ + wmWindow *win_prev = CTX_wm_window(C); + ScrArea *area_prev = CTX_wm_area(C); + ARegion *region_prev = CTX_wm_region(C); + + ddr->name[0] = '\0'; + + if (area) { + if (ELEM(area->spacetype, SPACE_VIEW3D, SPACE_OUTLINER)) { + ARegion *region = BKE_area_find_region_xy(area, RGN_TYPE_WINDOW, m_xy); + if (region) { + const int mval[2] = {m_xy[0] - region->winrct.xmin, m_xy[1] - region->winrct.ymin}; + Base *base; + + CTX_wm_window_set(C, win); + CTX_wm_area_set(C, area); + CTX_wm_region_set(C, region); + + /* Unfortunately it's necessary to always draw else we leave stale text. */ + ED_region_tag_redraw(region); + + if (area->spacetype == SPACE_VIEW3D) { + base = ED_view3d_give_base_under_cursor(C, mval); + } + else { + base = ED_outliner_give_base_under_cursor(C, mval); + } + + if (base) { + Object *ob = base->object; + ID *id = NULL; + if (ddr->idcode == ID_OB) { + id = (ID *)ob; + } + else if (ob->data) { + if (GS(((ID *)ob->data)->name) == ddr->idcode) { + id = (ID *)ob->data; + } + else { + BLI_snprintf( + ddr->name, sizeof(ddr->name), "Incompatible, expected a %s", ddr->idcode_name); + } + } + + PointerRNA idptr; + RNA_id_pointer_create(id, &idptr); + + if (id && RNA_property_pointer_poll(&ddr->ptr, ddr->prop, &idptr)) { + BLI_snprintf(ddr->name, sizeof(ddr->name), "%s: %s", ddr->idcode_name, id->name + 2); + *r_id = id; + } + + copy_v2_v2_int(ddr->name_pos, mval); + } + } + } + } + + CTX_wm_window_set(C, win_prev); + CTX_wm_area_set(C, area_prev); + CTX_wm_region_set(C, region_prev); +} + +/* sets the ID, returns success */ +static bool datadropper_id_set(bContext *C, DataDropper *ddr, ID *id) +{ + PointerRNA ptr_value; + + RNA_id_pointer_create(id, &ptr_value); + + RNA_property_pointer_set(&ddr->ptr, ddr->prop, ptr_value, NULL); + + RNA_property_update(C, &ddr->ptr, ddr->prop); + + ptr_value = RNA_property_pointer_get(&ddr->ptr, ddr->prop); + + return (ptr_value.owner_id == id); +} + +/* single point sample & set */ +static bool datadropper_id_sample(bContext *C, DataDropper *ddr, const int m_xy[2]) +{ + ID *id = NULL; + + int mval[2]; + wmWindow *win; + ScrArea *area; + datadropper_win_area_find(C, m_xy, mval, &win, &area); + + datadropper_id_sample_pt(C, win, area, ddr, mval, &id); + return datadropper_id_set(C, ddr, id); +} + +static void datadropper_cancel(bContext *C, wmOperator *op) +{ + DataDropper *ddr = op->customdata; + datadropper_id_set(C, ddr, ddr->init_id); + datadropper_exit(C, op); +} + +/* To switch the draw callback when region under mouse event changes */ +static void datadropper_set_draw_callback_region(ScrArea *area, DataDropper *ddr) +{ + if (area) { + /* If spacetype changed */ + if (area->spacetype != ddr->cursor_area->spacetype) { + /* Remove old callback */ + ED_region_draw_cb_exit(ddr->art, ddr->draw_handle_pixel); + + /* Redraw old area */ + ARegion *region = BKE_area_find_region_type(ddr->cursor_area, RGN_TYPE_WINDOW); + ED_region_tag_redraw(region); + + /* Set draw callback in new region */ + ARegionType *art = BKE_regiontype_from_id(area->type, RGN_TYPE_WINDOW); + + ddr->cursor_area = area; + ddr->art = art; + ddr->draw_handle_pixel = ED_region_draw_cb_activate( + art, datadropper_draw_cb, ddr, REGION_DRAW_POST_PIXEL); + } + } +} + +/* main modal status check */ +static int datadropper_modal(bContext *C, wmOperator *op, const wmEvent *event) +{ + DataDropper *ddr = (DataDropper *)op->customdata; + + /* handle modal keymap */ + if (event->type == EVT_MODAL_MAP) { + switch (event->val) { + case EYE_MODAL_CANCEL: + datadropper_cancel(C, op); + return OPERATOR_CANCELLED; + case EYE_MODAL_SAMPLE_CONFIRM: { + const bool is_undo = ddr->is_undo; + const bool success = datadropper_id_sample(C, ddr, event->xy); + datadropper_exit(C, op); + if (success) { + /* Could support finished & undo-skip. */ + return is_undo ? OPERATOR_FINISHED : OPERATOR_CANCELLED; + } + BKE_report(op->reports, RPT_WARNING, "Failed to set value"); + return OPERATOR_CANCELLED; + } + } + } + else if (event->type == MOUSEMOVE) { + ID *id = NULL; + + int mval[2]; + wmWindow *win; + ScrArea *area; + datadropper_win_area_find(C, event->xy, mval, &win, &area); + + /* Set the region for eyedropper cursor text drawing */ + datadropper_set_draw_callback_region(area, ddr); + + datadropper_id_sample_pt(C, win, area, ddr, mval, &id); + } + + return OPERATOR_RUNNING_MODAL; +} + +/* Modal Operator init */ +static int datadropper_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) +{ + /* init */ + if (datadropper_init(C, op)) { + wmWindow *win = CTX_wm_window(C); + /* Workaround for de-activating the button clearing the cursor, see T76794 */ + UI_context_active_but_clear(C, win, CTX_wm_region(C)); + WM_cursor_modal_set(win, WM_CURSOR_EYEDROPPER); + + /* add temp handler */ + WM_event_add_modal_handler(C, op); + + return OPERATOR_RUNNING_MODAL; + } + return OPERATOR_CANCELLED; +} + +/* Repeat operator */ +static int datadropper_exec(bContext *C, wmOperator *op) +{ + /* init */ + if (datadropper_init(C, op)) { + /* cleanup */ + datadropper_exit(C, op); + + return OPERATOR_FINISHED; + } + return OPERATOR_CANCELLED; +} + +static bool datadropper_poll(bContext *C) +{ + PointerRNA ptr; + PropertyRNA *prop; + int index_dummy; + uiBut *but; + + /* data dropper only supports object data */ + if ((CTX_wm_window(C) != NULL) && + (but = UI_context_active_but_prop_get(C, &ptr, &prop, &index_dummy)) && + (but->type == UI_BTYPE_SEARCH_MENU) && (but->flag & UI_BUT_VALUE_CLEAR)) { + if (prop && RNA_property_type(prop) == PROP_POINTER) { + StructRNA *type = RNA_property_pointer_type(&ptr, prop); + const short idcode = RNA_type_to_ID_code(type); + if ((idcode == ID_OB) || OB_DATA_SUPPORT_ID(idcode)) { + return true; + } + } + } + + return false; +} + +void UI_OT_eyedropper_id(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Eyedropper Data-Block"; + ot->idname = "UI_OT_eyedropper_id"; + ot->description = "Sample a data-block from the 3D View to store in a property"; + + /* api callbacks */ + ot->invoke = datadropper_invoke; + ot->modal = datadropper_modal; + ot->cancel = datadropper_cancel; + ot->exec = datadropper_exec; + ot->poll = datadropper_poll; + + /* flags */ + ot->flag = OPTYPE_UNDO | OPTYPE_BLOCKING | OPTYPE_INTERNAL; + + /* properties */ +} diff --git a/source/blender/editors/interface/eyedroppers/eyedropper_depth.c b/source/blender/editors/interface/eyedroppers/eyedropper_depth.c new file mode 100644 index 00000000000..3fb5a74944b --- /dev/null +++ b/source/blender/editors/interface/eyedroppers/eyedropper_depth.c @@ -0,0 +1,381 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2009 Blender Foundation. All rights reserved. */ + +/** \file + * \ingroup edinterface + * + * This file defines an eyedropper for picking 3D depth value (primary use is depth-of-field). + * + * Defines: + * - #UI_OT_eyedropper_depth + */ + +#include "MEM_guardedalloc.h" + +#include "DNA_camera_types.h" +#include "DNA_object_types.h" +#include "DNA_screen_types.h" +#include "DNA_space_types.h" +#include "DNA_view3d_types.h" + +#include "BLI_math_vector.h" +#include "BLI_string.h" + +#include "BKE_context.h" +#include "BKE_lib_id.h" +#include "BKE_screen.h" +#include "BKE_unit.h" + +#include "RNA_access.h" +#include "RNA_prototypes.h" + +#include "UI_interface.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "ED_screen.h" +#include "ED_space_api.h" +#include "ED_view3d.h" + +#include "eyedropper_intern.h" +#include "interface_intern.h" + +/** + * \note #DepthDropper is only internal name to avoid confusion with other kinds of eye-droppers. + */ +typedef struct DepthDropper { + PointerRNA ptr; + PropertyRNA *prop; + bool is_undo; + + bool is_set; + float init_depth; /* For resetting on cancel. */ + + bool accum_start; /* Has mouse been pressed. */ + float accum_depth; + int accum_tot; + + ARegionType *art; + void *draw_handle_pixel; + int name_pos[2]; + char name[200]; +} DepthDropper; + +static void depthdropper_draw_cb(const struct bContext *UNUSED(C), + ARegion *UNUSED(region), + void *arg) +{ + DepthDropper *ddr = arg; + eyedropper_draw_cursor_text_region(ddr->name_pos, ddr->name); +} + +static int depthdropper_init(bContext *C, wmOperator *op) +{ + int index_dummy; + + SpaceType *st; + ARegionType *art; + + st = BKE_spacetype_from_id(SPACE_VIEW3D); + art = BKE_regiontype_from_id(st, RGN_TYPE_WINDOW); + + DepthDropper *ddr = MEM_callocN(sizeof(DepthDropper), __func__); + + uiBut *but = UI_context_active_but_prop_get(C, &ddr->ptr, &ddr->prop, &index_dummy); + + /* fallback to the active camera's dof */ + if (ddr->prop == NULL) { + RegionView3D *rv3d = CTX_wm_region_view3d(C); + if (rv3d && rv3d->persp == RV3D_CAMOB) { + View3D *v3d = CTX_wm_view3d(C); + if (v3d->camera && v3d->camera->data && + BKE_id_is_editable(CTX_data_main(C), v3d->camera->data)) { + Camera *camera = (Camera *)v3d->camera->data; + RNA_pointer_create(&camera->id, &RNA_CameraDOFSettings, &camera->dof, &ddr->ptr); + ddr->prop = RNA_struct_find_property(&ddr->ptr, "focus_distance"); + ddr->is_undo = true; + } + } + } + else { + ddr->is_undo = UI_but_flag_is_set(but, UI_BUT_UNDO); + } + + if ((ddr->ptr.data == NULL) || (ddr->prop == NULL) || + (RNA_property_editable(&ddr->ptr, ddr->prop) == false) || + (RNA_property_type(ddr->prop) != PROP_FLOAT)) { + MEM_freeN(ddr); + return false; + } + op->customdata = ddr; + + ddr->art = art; + ddr->draw_handle_pixel = ED_region_draw_cb_activate( + art, depthdropper_draw_cb, ddr, REGION_DRAW_POST_PIXEL); + ddr->init_depth = RNA_property_float_get(&ddr->ptr, ddr->prop); + + return true; +} + +static void depthdropper_exit(bContext *C, wmOperator *op) +{ + WM_cursor_modal_restore(CTX_wm_window(C)); + + if (op->customdata) { + DepthDropper *ddr = (DepthDropper *)op->customdata; + + if (ddr->art) { + ED_region_draw_cb_exit(ddr->art, ddr->draw_handle_pixel); + } + + MEM_freeN(op->customdata); + + op->customdata = NULL; + } +} + +/* *** depthdropper id helper functions *** */ +/** + * \brief get the ID from the screen. + */ +static void depthdropper_depth_sample_pt(bContext *C, + DepthDropper *ddr, + const int m_xy[2], + float *r_depth) +{ + /* we could use some clever */ + bScreen *screen = CTX_wm_screen(C); + ScrArea *area = BKE_screen_find_area_xy(screen, SPACE_TYPE_ANY, m_xy); + Scene *scene = CTX_data_scene(C); + + ScrArea *area_prev = CTX_wm_area(C); + ARegion *region_prev = CTX_wm_region(C); + + ddr->name[0] = '\0'; + + if (area) { + if (area->spacetype == SPACE_VIEW3D) { + ARegion *region = BKE_area_find_region_xy(area, RGN_TYPE_WINDOW, m_xy); + if (region) { + struct Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); + View3D *v3d = area->spacedata.first; + RegionView3D *rv3d = region->regiondata; + /* weak, we could pass in some reference point */ + const float *view_co = v3d->camera ? v3d->camera->obmat[3] : rv3d->viewinv[3]; + const int mval[2] = {m_xy[0] - region->winrct.xmin, m_xy[1] - region->winrct.ymin}; + copy_v2_v2_int(ddr->name_pos, mval); + + float co[3]; + + CTX_wm_area_set(C, area); + CTX_wm_region_set(C, region); + + /* Unfortunately it's necessary to always draw otherwise we leave stale text. */ + ED_region_tag_redraw(region); + + view3d_operator_needs_opengl(C); + + if (ED_view3d_autodist(depsgraph, region, v3d, mval, co, true, NULL)) { + const float mval_center_fl[2] = {(float)region->winx / 2, (float)region->winy / 2}; + float co_align[3]; + + /* quick way to get view-center aligned point */ + ED_view3d_win_to_3d(v3d, region, co, mval_center_fl, co_align); + + *r_depth = len_v3v3(view_co, co_align); + + BKE_unit_value_as_string(ddr->name, + sizeof(ddr->name), + (double)*r_depth, + 4, + B_UNIT_LENGTH, + &scene->unit, + false); + } + else { + BLI_strncpy(ddr->name, "Nothing under cursor", sizeof(ddr->name)); + } + } + } + } + + CTX_wm_area_set(C, area_prev); + CTX_wm_region_set(C, region_prev); +} + +/* sets the sample depth RGB, maintaining A */ +static void depthdropper_depth_set(bContext *C, DepthDropper *ddr, const float depth) +{ + RNA_property_float_set(&ddr->ptr, ddr->prop, depth); + ddr->is_set = true; + RNA_property_update(C, &ddr->ptr, ddr->prop); +} + +/* set sample from accumulated values */ +static void depthdropper_depth_set_accum(bContext *C, DepthDropper *ddr) +{ + float depth = ddr->accum_depth; + if (ddr->accum_tot) { + depth /= (float)ddr->accum_tot; + } + depthdropper_depth_set(C, ddr, depth); +} + +/* single point sample & set */ +static void depthdropper_depth_sample(bContext *C, DepthDropper *ddr, const int m_xy[2]) +{ + float depth = -1.0f; + if (depth != -1.0f) { + depthdropper_depth_sample_pt(C, ddr, m_xy, &depth); + depthdropper_depth_set(C, ddr, depth); + } +} + +static void depthdropper_depth_sample_accum(bContext *C, DepthDropper *ddr, const int m_xy[2]) +{ + float depth = -1.0f; + depthdropper_depth_sample_pt(C, ddr, m_xy, &depth); + if (depth != -1.0f) { + ddr->accum_depth += depth; + ddr->accum_tot++; + } +} + +static void depthdropper_cancel(bContext *C, wmOperator *op) +{ + DepthDropper *ddr = op->customdata; + if (ddr->is_set) { + depthdropper_depth_set(C, ddr, ddr->init_depth); + } + depthdropper_exit(C, op); +} + +/* main modal status check */ +static int depthdropper_modal(bContext *C, wmOperator *op, const wmEvent *event) +{ + DepthDropper *ddr = (DepthDropper *)op->customdata; + + /* handle modal keymap */ + if (event->type == EVT_MODAL_MAP) { + switch (event->val) { + case EYE_MODAL_CANCEL: + depthdropper_cancel(C, op); + return OPERATOR_CANCELLED; + case EYE_MODAL_SAMPLE_CONFIRM: { + const bool is_undo = ddr->is_undo; + if (ddr->accum_tot == 0) { + depthdropper_depth_sample(C, ddr, event->xy); + } + else { + depthdropper_depth_set_accum(C, ddr); + } + depthdropper_exit(C, op); + /* Could support finished & undo-skip. */ + return is_undo ? OPERATOR_FINISHED : OPERATOR_CANCELLED; + } + case EYE_MODAL_SAMPLE_BEGIN: + /* enable accum and make first sample */ + ddr->accum_start = true; + depthdropper_depth_sample_accum(C, ddr, event->xy); + break; + case EYE_MODAL_SAMPLE_RESET: + ddr->accum_tot = 0; + ddr->accum_depth = 0.0f; + depthdropper_depth_sample_accum(C, ddr, event->xy); + depthdropper_depth_set_accum(C, ddr); + break; + } + } + else if (event->type == MOUSEMOVE) { + if (ddr->accum_start) { + /* button is pressed so keep sampling */ + depthdropper_depth_sample_accum(C, ddr, event->xy); + depthdropper_depth_set_accum(C, ddr); + } + } + + return OPERATOR_RUNNING_MODAL; +} + +/* Modal Operator init */ +static int depthdropper_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) +{ + /* init */ + if (depthdropper_init(C, op)) { + wmWindow *win = CTX_wm_window(C); + /* Workaround for de-activating the button clearing the cursor, see T76794 */ + UI_context_active_but_clear(C, win, CTX_wm_region(C)); + WM_cursor_modal_set(win, WM_CURSOR_EYEDROPPER); + + /* add temp handler */ + WM_event_add_modal_handler(C, op); + + return OPERATOR_RUNNING_MODAL; + } + return OPERATOR_CANCELLED; +} + +/* Repeat operator */ +static int depthdropper_exec(bContext *C, wmOperator *op) +{ + /* init */ + if (depthdropper_init(C, op)) { + /* cleanup */ + depthdropper_exit(C, op); + + return OPERATOR_FINISHED; + } + return OPERATOR_CANCELLED; +} + +static bool depthdropper_poll(bContext *C) +{ + PointerRNA ptr; + PropertyRNA *prop; + int index_dummy; + uiBut *but; + + /* check if there's an active button taking depth value */ + if ((CTX_wm_window(C) != NULL) && + (but = UI_context_active_but_prop_get(C, &ptr, &prop, &index_dummy)) && + (but->type == UI_BTYPE_NUM) && (prop != NULL)) { + if ((RNA_property_type(prop) == PROP_FLOAT) && + (RNA_property_subtype(prop) & PROP_UNIT_LENGTH) && + (RNA_property_array_check(prop) == false)) { + return true; + } + } + else { + RegionView3D *rv3d = CTX_wm_region_view3d(C); + if (rv3d && rv3d->persp == RV3D_CAMOB) { + View3D *v3d = CTX_wm_view3d(C); + if (v3d->camera && v3d->camera->data && + BKE_id_is_editable(CTX_data_main(C), v3d->camera->data)) { + return true; + } + } + } + + return false; +} + +void UI_OT_eyedropper_depth(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Eyedropper Depth"; + ot->idname = "UI_OT_eyedropper_depth"; + ot->description = "Sample depth from the 3D view"; + + /* api callbacks */ + ot->invoke = depthdropper_invoke; + ot->modal = depthdropper_modal; + ot->cancel = depthdropper_cancel; + ot->exec = depthdropper_exec; + ot->poll = depthdropper_poll; + + /* flags */ + ot->flag = OPTYPE_UNDO | OPTYPE_BLOCKING | OPTYPE_INTERNAL; + + /* properties */ +} diff --git a/source/blender/editors/interface/eyedroppers/eyedropper_driver.c b/source/blender/editors/interface/eyedroppers/eyedropper_driver.c new file mode 100644 index 00000000000..14c00c21a5c --- /dev/null +++ b/source/blender/editors/interface/eyedroppers/eyedropper_driver.c @@ -0,0 +1,220 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2009 Blender Foundation. All rights reserved. */ + +/** \file + * \ingroup edinterface + * + * Eyedropper (Animation Driver Targets). + * + * Defines: + * - #UI_OT_eyedropper_driver + */ + +#include "MEM_guardedalloc.h" + +#include "DNA_anim_types.h" +#include "DNA_object_types.h" +#include "DNA_screen_types.h" + +#include "BKE_animsys.h" +#include "BKE_context.h" + +#include "DEG_depsgraph.h" +#include "DEG_depsgraph_build.h" + +#include "RNA_access.h" +#include "RNA_define.h" + +#include "UI_interface.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "ED_keyframing.h" + +#include "eyedropper_intern.h" +#include "interface_intern.h" + +typedef struct DriverDropper { + /* Destination property (i.e. where we'll add a driver) */ + PointerRNA ptr; + PropertyRNA *prop; + int index; + bool is_undo; + + /* TODO: new target? */ +} DriverDropper; + +static bool driverdropper_init(bContext *C, wmOperator *op) +{ + DriverDropper *ddr = MEM_callocN(sizeof(DriverDropper), __func__); + + uiBut *but = UI_context_active_but_prop_get(C, &ddr->ptr, &ddr->prop, &ddr->index); + + if ((ddr->ptr.data == NULL) || (ddr->prop == NULL) || + (RNA_property_editable(&ddr->ptr, ddr->prop) == false) || + (RNA_property_animateable(&ddr->ptr, ddr->prop) == false) || (but->flag & UI_BUT_DRIVEN)) { + MEM_freeN(ddr); + return false; + } + op->customdata = ddr; + + ddr->is_undo = UI_but_flag_is_set(but, UI_BUT_UNDO); + + return true; +} + +static void driverdropper_exit(bContext *C, wmOperator *op) +{ + WM_cursor_modal_restore(CTX_wm_window(C)); + + MEM_SAFE_FREE(op->customdata); +} + +static void driverdropper_sample(bContext *C, wmOperator *op, const wmEvent *event) +{ + DriverDropper *ddr = (DriverDropper *)op->customdata; + uiBut *but = eyedropper_get_property_button_under_mouse(C, event); + + const short mapping_type = RNA_enum_get(op->ptr, "mapping_type"); + const short flag = 0; + + /* we can only add a driver if we know what RNA property it corresponds to */ + if (but == NULL) { + return; + } + /* Get paths for src... */ + PointerRNA *target_ptr = &but->rnapoin; + PropertyRNA *target_prop = but->rnaprop; + const int target_index = but->rnaindex; + + char *target_path = RNA_path_from_ID_to_property(target_ptr, target_prop); + + /* ... and destination */ + char *dst_path = RNA_path_from_ID_to_property(&ddr->ptr, ddr->prop); + + /* Now create driver(s) */ + if (target_path && dst_path) { + int success = ANIM_add_driver_with_target(op->reports, + ddr->ptr.owner_id, + dst_path, + ddr->index, + target_ptr->owner_id, + target_path, + target_index, + flag, + DRIVER_TYPE_PYTHON, + mapping_type); + + if (success) { + /* send updates */ + UI_context_update_anim_flag(C); + DEG_relations_tag_update(CTX_data_main(C)); + DEG_id_tag_update(ddr->ptr.owner_id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); + WM_event_add_notifier(C, NC_ANIMATION | ND_FCURVES_ORDER, NULL); /* XXX */ + } + } + + /* cleanup */ + if (target_path) { + MEM_freeN(target_path); + } + if (dst_path) { + MEM_freeN(dst_path); + } +} + +static void driverdropper_cancel(bContext *C, wmOperator *op) +{ + driverdropper_exit(C, op); +} + +/* main modal status check */ +static int driverdropper_modal(bContext *C, wmOperator *op, const wmEvent *event) +{ + DriverDropper *ddr = op->customdata; + + /* handle modal keymap */ + if (event->type == EVT_MODAL_MAP) { + switch (event->val) { + case EYE_MODAL_CANCEL: { + driverdropper_cancel(C, op); + return OPERATOR_CANCELLED; + } + case EYE_MODAL_SAMPLE_CONFIRM: { + const bool is_undo = ddr->is_undo; + driverdropper_sample(C, op, event); + driverdropper_exit(C, op); + /* Could support finished & undo-skip. */ + return is_undo ? OPERATOR_FINISHED : OPERATOR_CANCELLED; + } + } + } + + return OPERATOR_RUNNING_MODAL; +} + +/* Modal Operator init */ +static int driverdropper_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) +{ + /* init */ + if (driverdropper_init(C, op)) { + wmWindow *win = CTX_wm_window(C); + /* Workaround for de-activating the button clearing the cursor, see T76794 */ + UI_context_active_but_clear(C, win, CTX_wm_region(C)); + WM_cursor_modal_set(win, WM_CURSOR_EYEDROPPER); + + /* add temp handler */ + WM_event_add_modal_handler(C, op); + + return OPERATOR_RUNNING_MODAL; + } + return OPERATOR_CANCELLED; +} + +/* Repeat operator */ +static int driverdropper_exec(bContext *C, wmOperator *op) +{ + /* init */ + if (driverdropper_init(C, op)) { + /* cleanup */ + driverdropper_exit(C, op); + + return OPERATOR_FINISHED; + } + return OPERATOR_CANCELLED; +} + +static bool driverdropper_poll(bContext *C) +{ + if (!CTX_wm_window(C)) { + return false; + } + return true; +} + +void UI_OT_eyedropper_driver(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Eyedropper Driver"; + ot->idname = "UI_OT_eyedropper_driver"; + ot->description = "Pick a property to use as a driver target"; + + /* api callbacks */ + ot->invoke = driverdropper_invoke; + ot->modal = driverdropper_modal; + ot->cancel = driverdropper_cancel; + ot->exec = driverdropper_exec; + ot->poll = driverdropper_poll; + + /* flags */ + ot->flag = OPTYPE_UNDO | OPTYPE_BLOCKING | OPTYPE_INTERNAL; + + /* properties */ + RNA_def_enum(ot->srna, + "mapping_type", + prop_driver_create_mapping_types, + 0, + "Mapping Type", + "Method used to match target and driven properties"); +} diff --git a/source/blender/editors/interface/eyedroppers/eyedropper_gpencil_color.c b/source/blender/editors/interface/eyedroppers/eyedropper_gpencil_color.c new file mode 100644 index 00000000000..c3879fe8bbd --- /dev/null +++ b/source/blender/editors/interface/eyedroppers/eyedropper_gpencil_color.c @@ -0,0 +1,373 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2009 Blender Foundation. All rights reserved. */ + +/** \file + * \ingroup edinterface + * + * Eyedropper (RGB Color) + * + * Defines: + * - #UI_OT_eyedropper_gpencil_color + */ + +#include "MEM_guardedalloc.h" + +#include "BLI_listbase.h" +#include "BLI_string.h" + +#include "BLT_translation.h" + +#include "DNA_gpencil_types.h" +#include "DNA_material_types.h" +#include "DNA_space_types.h" + +#include "BKE_context.h" +#include "BKE_gpencil.h" +#include "BKE_lib_id.h" +#include "BKE_main.h" +#include "BKE_material.h" +#include "BKE_paint.h" +#include "BKE_report.h" + +#include "UI_interface.h" + +#include "IMB_colormanagement.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "RNA_access.h" +#include "RNA_define.h" + +#include "ED_gpencil.h" +#include "ED_screen.h" +#include "ED_undo.h" + +#include "DEG_depsgraph.h" +#include "DEG_depsgraph_build.h" + +#include "eyedropper_intern.h" +#include "interface_intern.h" + +typedef struct EyedropperGPencil { + struct ColorManagedDisplay *display; + /** color under cursor RGB */ + float color[3]; + /** Mode */ + int mode; +} EyedropperGPencil; + +/* Helper: Draw status message while the user is running the operator */ +static void eyedropper_gpencil_status_indicators(bContext *C) +{ + char msg_str[UI_MAX_DRAW_STR]; + BLI_strncpy( + msg_str, TIP_("LMB: Stroke - Shift: Fill - Shift+Ctrl: Stroke + Fill"), UI_MAX_DRAW_STR); + + ED_workspace_status_text(C, msg_str); +} + +/* Initialize. */ +static bool eyedropper_gpencil_init(bContext *C, wmOperator *op) +{ + EyedropperGPencil *eye = MEM_callocN(sizeof(EyedropperGPencil), __func__); + + op->customdata = eye; + Scene *scene = CTX_data_scene(C); + + const char *display_device; + display_device = scene->display_settings.display_device; + eye->display = IMB_colormanagement_display_get_named(display_device); + + eye->mode = RNA_enum_get(op->ptr, "mode"); + return true; +} + +/* Exit and free memory. */ +static void eyedropper_gpencil_exit(bContext *C, wmOperator *op) +{ + /* Clear status message area. */ + ED_workspace_status_text(C, NULL); + + MEM_SAFE_FREE(op->customdata); +} + +static void eyedropper_add_material(bContext *C, + const float col_conv[4], + const bool only_stroke, + const bool only_fill, + const bool both) +{ + Main *bmain = CTX_data_main(C); + Object *ob = CTX_data_active_object(C); + Material *ma = NULL; + + bool found = false; + + /* Look for a similar material in grease pencil slots. */ + short *totcol = BKE_object_material_len_p(ob); + for (short i = 0; i < *totcol; i++) { + ma = BKE_object_material_get(ob, i + 1); + if (ma == NULL) { + continue; + } + + MaterialGPencilStyle *gp_style = ma->gp_style; + if (gp_style != NULL) { + /* Check stroke color. */ + bool found_stroke = compare_v3v3(gp_style->stroke_rgba, col_conv, 0.01f) && + (gp_style->flag & GP_MATERIAL_STROKE_SHOW); + /* Check fill color. */ + bool found_fill = compare_v3v3(gp_style->fill_rgba, col_conv, 0.01f) && + (gp_style->flag & GP_MATERIAL_FILL_SHOW); + + if ((only_stroke) && (found_stroke) && ((gp_style->flag & GP_MATERIAL_FILL_SHOW) == 0)) { + found = true; + } + else if ((only_fill) && (found_fill) && ((gp_style->flag & GP_MATERIAL_STROKE_SHOW) == 0)) { + found = true; + } + else if ((both) && (found_stroke) && (found_fill)) { + found = true; + } + + /* Found existing material. */ + if (found) { + ob->actcol = i + 1; + WM_main_add_notifier(NC_MATERIAL | ND_SHADING_LINKS, NULL); + WM_main_add_notifier(NC_SPACE | ND_SPACE_VIEW3D, NULL); + return; + } + } + } + + /* If material was not found add a new material with stroke and/or fill color + * depending of the secondary key (LMB: Stroke, Shift: Fill, Shift+Ctrl: Stroke/Fill) + */ + int idx; + Material *ma_new = BKE_gpencil_object_material_new(bmain, ob, "Material", &idx); + WM_main_add_notifier(NC_OBJECT | ND_OB_SHADING, &ob->id); + WM_main_add_notifier(NC_MATERIAL | ND_SHADING_LINKS, NULL); + DEG_relations_tag_update(bmain); + + BLI_assert(ma_new != NULL); + + MaterialGPencilStyle *gp_style_new = ma_new->gp_style; + BLI_assert(gp_style_new != NULL); + + /* Only create Stroke (default option). */ + if (only_stroke) { + /* Stroke color. */ + gp_style_new->flag |= GP_MATERIAL_STROKE_SHOW; + gp_style_new->flag &= ~GP_MATERIAL_FILL_SHOW; + copy_v3_v3(gp_style_new->stroke_rgba, col_conv); + zero_v4(gp_style_new->fill_rgba); + } + /* Fill Only. */ + else if (only_fill) { + /* Fill color. */ + gp_style_new->flag &= ~GP_MATERIAL_STROKE_SHOW; + gp_style_new->flag |= GP_MATERIAL_FILL_SHOW; + zero_v4(gp_style_new->stroke_rgba); + copy_v3_v3(gp_style_new->fill_rgba, col_conv); + } + /* Stroke and Fill. */ + else if (both) { + gp_style_new->flag |= GP_MATERIAL_STROKE_SHOW | GP_MATERIAL_FILL_SHOW; + copy_v3_v3(gp_style_new->stroke_rgba, col_conv); + copy_v3_v3(gp_style_new->fill_rgba, col_conv); + } + /* Push undo for new created material. */ + ED_undo_push(C, "Add Grease Pencil Material"); +} + +/* Create a new palette color and palette if needed. */ +static void eyedropper_add_palette_color(bContext *C, const float col_conv[4]) +{ + Main *bmain = CTX_data_main(C); + Scene *scene = CTX_data_scene(C); + ToolSettings *ts = scene->toolsettings; + GpPaint *gp_paint = ts->gp_paint; + GpVertexPaint *gp_vertexpaint = ts->gp_vertexpaint; + Paint *paint = &gp_paint->paint; + Paint *vertexpaint = &gp_vertexpaint->paint; + + /* Check for Palette in Draw and Vertex Paint Mode. */ + if (paint->palette == NULL) { + Palette *palette = BKE_palette_add(bmain, "Grease Pencil"); + id_us_min(&palette->id); + + BKE_paint_palette_set(paint, palette); + + if (vertexpaint->palette == NULL) { + BKE_paint_palette_set(vertexpaint, palette); + } + } + /* Check if the color exist already. */ + Palette *palette = paint->palette; + LISTBASE_FOREACH (PaletteColor *, palcolor, &palette->colors) { + if (compare_v3v3(palcolor->rgb, col_conv, 0.01f)) { + return; + } + } + + /* Create Colors. */ + PaletteColor *palcol = BKE_palette_color_add(palette); + if (palcol) { + copy_v3_v3(palcol->rgb, col_conv); + } +} + +/* Set the material or the palette color. */ +static void eyedropper_gpencil_color_set(bContext *C, const wmEvent *event, EyedropperGPencil *eye) +{ + + const bool only_stroke = (event->modifier & (KM_CTRL | KM_SHIFT)) == 0; + const bool only_fill = ((event->modifier & KM_CTRL) == 0 && (event->modifier & KM_SHIFT)); + const bool both = ((event->modifier & KM_CTRL) && (event->modifier & KM_SHIFT)); + + float col_conv[4]; + + /* Convert from linear rgb space to display space because grease pencil colors are in display + * space, and this conversion is needed to undo the conversion to linear performed by + * eyedropper_color_sample_fl. */ + if (eye->display) { + copy_v3_v3(col_conv, eye->color); + IMB_colormanagement_scene_linear_to_display_v3(col_conv, eye->display); + } + else { + copy_v3_v3(col_conv, eye->color); + } + + /* Add material or Palette color. */ + if (eye->mode == 0) { + eyedropper_add_material(C, col_conv, only_stroke, only_fill, both); + } + else { + eyedropper_add_palette_color(C, col_conv); + } +} + +/* Sample the color below cursor. */ +static void eyedropper_gpencil_color_sample(bContext *C, EyedropperGPencil *eye, const int m_xy[2]) +{ + eyedropper_color_sample_fl(C, m_xy, eye->color); +} + +/* Cancel operator. */ +static void eyedropper_gpencil_cancel(bContext *C, wmOperator *op) +{ + eyedropper_gpencil_exit(C, op); +} + +/* Main modal status check. */ +static int eyedropper_gpencil_modal(bContext *C, wmOperator *op, const wmEvent *event) +{ + EyedropperGPencil *eye = (EyedropperGPencil *)op->customdata; + /* Handle modal keymap */ + switch (event->type) { + case EVT_MODAL_MAP: { + switch (event->val) { + case EYE_MODAL_SAMPLE_BEGIN: { + return OPERATOR_RUNNING_MODAL; + } + case EYE_MODAL_CANCEL: { + eyedropper_gpencil_cancel(C, op); + return OPERATOR_CANCELLED; + } + case EYE_MODAL_SAMPLE_CONFIRM: { + eyedropper_gpencil_color_sample(C, eye, event->xy); + + /* Create material. */ + eyedropper_gpencil_color_set(C, event, eye); + WM_main_add_notifier(NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + eyedropper_gpencil_exit(C, op); + return OPERATOR_FINISHED; + } + default: { + break; + } + } + break; + } + case MOUSEMOVE: + case INBETWEEN_MOUSEMOVE: { + eyedropper_gpencil_color_sample(C, eye, event->xy); + break; + } + default: { + break; + } + } + + return OPERATOR_RUNNING_MODAL; +} + +/* Modal Operator init */ +static int eyedropper_gpencil_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) +{ + /* Init. */ + if (eyedropper_gpencil_init(C, op)) { + /* Add modal temp handler. */ + WM_event_add_modal_handler(C, op); + /* Status message. */ + eyedropper_gpencil_status_indicators(C); + + return OPERATOR_RUNNING_MODAL; + } + return OPERATOR_PASS_THROUGH; +} + +/* Repeat operator */ +static int eyedropper_gpencil_exec(bContext *C, wmOperator *op) +{ + /* init */ + if (eyedropper_gpencil_init(C, op)) { + + /* cleanup */ + eyedropper_gpencil_exit(C, op); + + return OPERATOR_FINISHED; + } + return OPERATOR_PASS_THROUGH; +} + +static bool eyedropper_gpencil_poll(bContext *C) +{ + /* Only valid if the current active object is grease pencil. */ + Object *obact = CTX_data_active_object(C); + if ((obact == NULL) || (obact->type != OB_GPENCIL)) { + return false; + } + + /* Test we have a window below. */ + return (CTX_wm_window(C) != NULL); +} + +void UI_OT_eyedropper_gpencil_color(wmOperatorType *ot) +{ + static const EnumPropertyItem items_mode[] = { + {0, "MATERIAL", 0, "Material", ""}, + {1, "PALETTE", 0, "Palette", ""}, + {0, NULL, 0, NULL, NULL}, + }; + + /* identifiers */ + ot->name = "Grease Pencil Eyedropper"; + ot->idname = "UI_OT_eyedropper_gpencil_color"; + ot->description = "Sample a color from the Blender Window and create Grease Pencil material"; + + /* api callbacks */ + ot->invoke = eyedropper_gpencil_invoke; + ot->modal = eyedropper_gpencil_modal; + ot->cancel = eyedropper_gpencil_cancel; + ot->exec = eyedropper_gpencil_exec; + ot->poll = eyedropper_gpencil_poll; + + /* flags */ + ot->flag = OPTYPE_UNDO | OPTYPE_BLOCKING; + + /* properties */ + ot->prop = RNA_def_enum(ot->srna, "mode", items_mode, 0, "Mode", ""); +} diff --git a/source/blender/editors/interface/eyedroppers/eyedropper_intern.h b/source/blender/editors/interface/eyedroppers/eyedropper_intern.h new file mode 100644 index 00000000000..946f2145d1d --- /dev/null +++ b/source/blender/editors/interface/eyedroppers/eyedropper_intern.h @@ -0,0 +1,57 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup edinterface + * + * Share between `interface/eyedropper/` files. + */ + +#pragma once + +/* interface_eyedropper.c */ + +void eyedropper_draw_cursor_text_window(const struct wmWindow *window, const char *name); +void eyedropper_draw_cursor_text_region(const int xy[2], const char *name); +/** + * Utility to retrieve a button representing a RNA property that is currently under the cursor. + * + * This is to be used by any eyedroppers which fetch properties (e.g. UI_OT_eyedropper_driver). + * Especially during modal operations (e.g. as with the eyedroppers), context cannot be relied + * upon to provide this information, as it is not updated until the operator finishes. + * + * \return A button under the mouse which relates to some RNA Property, or NULL + */ +uiBut *eyedropper_get_property_button_under_mouse(bContext *C, const wmEvent *event); +void datadropper_win_area_find(const struct bContext *C, + const int mval[2], + int r_mval[2], + struct wmWindow **r_win, + struct ScrArea **r_area); + +/* interface_eyedropper_color.c (expose for color-band picker) */ + +/** + * \brief get the color from the screen. + * + * Special check for image or nodes where we MAY have HDR pixels which don't display. + * + * \note Exposed by 'eyedropper_intern.h' for use with color band picking. + */ +void eyedropper_color_sample_fl(bContext *C, const int m_xy[2], float r_col[3]); + +/* Used for most eye-dropper operators. */ +enum { + EYE_MODAL_CANCEL = 1, + EYE_MODAL_SAMPLE_CONFIRM, + EYE_MODAL_SAMPLE_BEGIN, + EYE_MODAL_SAMPLE_RESET, +}; + +/* Color-band point sample. */ +enum { + EYE_MODAL_POINT_CANCEL = 1, + EYE_MODAL_POINT_SAMPLE, + EYE_MODAL_POINT_CONFIRM, + EYE_MODAL_POINT_RESET, + EYE_MODAL_POINT_REMOVE_LAST, +}; diff --git a/source/blender/editors/interface/eyedroppers/interface_eyedropper.c b/source/blender/editors/interface/eyedroppers/interface_eyedropper.c new file mode 100644 index 00000000000..c6fb8f0ab68 --- /dev/null +++ b/source/blender/editors/interface/eyedroppers/interface_eyedropper.c @@ -0,0 +1,161 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2009 Blender Foundation. All rights reserved. */ + +/** \file + * \ingroup edinterface + */ + +#include "DNA_screen_types.h" +#include "DNA_space_types.h" + +#include "BLI_math_color.h" +#include "BLI_math_vector.h" + +#include "BKE_context.h" +#include "BKE_screen.h" + +#include "UI_interface.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "interface_intern.h" + +#include "eyedropper_intern.h" /* own include */ + +/* -------------------------------------------------------------------- */ +/* Keymap + */ +/** \name Modal Keymap + * \{ */ + +wmKeyMap *eyedropper_modal_keymap(wmKeyConfig *keyconf) +{ + static const EnumPropertyItem modal_items[] = { + {EYE_MODAL_CANCEL, "CANCEL", 0, "Cancel", ""}, + {EYE_MODAL_SAMPLE_CONFIRM, "SAMPLE_CONFIRM", 0, "Confirm Sampling", ""}, + {EYE_MODAL_SAMPLE_BEGIN, "SAMPLE_BEGIN", 0, "Start Sampling", ""}, + {EYE_MODAL_SAMPLE_RESET, "SAMPLE_RESET", 0, "Reset Sampling", ""}, + {0, NULL, 0, NULL, NULL}, + }; + + wmKeyMap *keymap = WM_modalkeymap_find(keyconf, "Eyedropper Modal Map"); + + /* this function is called for each spacetype, only needs to add map once */ + if (keymap && keymap->modal_items) { + return NULL; + } + + keymap = WM_modalkeymap_ensure(keyconf, "Eyedropper Modal Map", modal_items); + + /* assign to operators */ + WM_modalkeymap_assign(keymap, "UI_OT_eyedropper_colorramp"); + WM_modalkeymap_assign(keymap, "UI_OT_eyedropper_color"); + WM_modalkeymap_assign(keymap, "UI_OT_eyedropper_id"); + WM_modalkeymap_assign(keymap, "UI_OT_eyedropper_depth"); + WM_modalkeymap_assign(keymap, "UI_OT_eyedropper_driver"); + WM_modalkeymap_assign(keymap, "UI_OT_eyedropper_gpencil_color"); + + return keymap; +} + +wmKeyMap *eyedropper_colorband_modal_keymap(wmKeyConfig *keyconf) +{ + static const EnumPropertyItem modal_items_point[] = { + {EYE_MODAL_POINT_CANCEL, "CANCEL", 0, "Cancel", ""}, + {EYE_MODAL_POINT_SAMPLE, "SAMPLE_SAMPLE", 0, "Sample a Point", ""}, + {EYE_MODAL_POINT_CONFIRM, "SAMPLE_CONFIRM", 0, "Confirm Sampling", ""}, + {EYE_MODAL_POINT_RESET, "SAMPLE_RESET", 0, "Reset Sampling", ""}, + {0, NULL, 0, NULL, NULL}, + }; + + wmKeyMap *keymap = WM_modalkeymap_find(keyconf, "Eyedropper ColorRamp PointSampling Map"); + if (keymap && keymap->modal_items) { + return keymap; + } + + keymap = WM_modalkeymap_ensure( + keyconf, "Eyedropper ColorRamp PointSampling Map", modal_items_point); + + /* assign to operators */ + WM_modalkeymap_assign(keymap, "UI_OT_eyedropper_colorramp_point"); + + return keymap; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/* Utility Functions + */ + +/** \name Generic Shared Functions + * \{ */ + +static void eyedropper_draw_cursor_text_ex(const int xy[2], const char *name) +{ + const uiFontStyle *fstyle = UI_FSTYLE_WIDGET; + + /* Use the theme settings from tooltips. */ + const bTheme *btheme = UI_GetTheme(); + const uiWidgetColors *wcol = &btheme->tui.wcol_tooltip; + + float col_fg[4], col_bg[4]; + rgba_uchar_to_float(col_fg, wcol->text); + rgba_uchar_to_float(col_bg, wcol->inner); + + UI_fontstyle_draw_simple_backdrop(fstyle, xy[0], xy[1] + U.widget_unit, name, col_fg, col_bg); +} + +void eyedropper_draw_cursor_text_window(const struct wmWindow *window, const char *name) +{ + if (name[0] == '\0') { + return; + } + + eyedropper_draw_cursor_text_ex(window->eventstate->xy, name); +} + +void eyedropper_draw_cursor_text_region(const int xy[2], const char *name) +{ + if (name[0] == '\0') { + return; + } + + eyedropper_draw_cursor_text_ex(xy, name); +} + +uiBut *eyedropper_get_property_button_under_mouse(bContext *C, const wmEvent *event) +{ + bScreen *screen = CTX_wm_screen(C); + ScrArea *area = BKE_screen_find_area_xy(screen, SPACE_TYPE_ANY, event->xy); + const ARegion *region = BKE_area_find_region_xy(area, RGN_TYPE_ANY, event->xy); + + uiBut *but = ui_but_find_mouse_over(region, event); + + if (ELEM(NULL, but, but->rnapoin.data, but->rnaprop)) { + return NULL; + } + return but; +} + +void datadropper_win_area_find( + const bContext *C, const int mval[2], int r_mval[2], wmWindow **r_win, ScrArea **r_area) +{ + bScreen *screen = CTX_wm_screen(C); + + *r_win = CTX_wm_window(C); + *r_area = BKE_screen_find_area_xy(screen, SPACE_TYPE_ANY, mval); + if (*r_area == NULL) { + *r_win = WM_window_find_under_cursor(*r_win, mval, r_mval); + if (*r_win) { + screen = WM_window_get_active_screen(*r_win); + *r_area = BKE_screen_find_area_xy(screen, SPACE_TYPE_ANY, r_mval); + } + } + else if (mval != r_mval) { + copy_v2_v2_int(r_mval, mval); + } +} + +/** \} */ diff --git a/source/blender/editors/interface/interface_eyedropper.c b/source/blender/editors/interface/interface_eyedropper.c deleted file mode 100644 index eaec1e249b7..00000000000 --- a/source/blender/editors/interface/interface_eyedropper.c +++ /dev/null @@ -1,161 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later - * Copyright 2009 Blender Foundation. All rights reserved. */ - -/** \file - * \ingroup edinterface - */ - -#include "DNA_screen_types.h" -#include "DNA_space_types.h" - -#include "BLI_math_color.h" -#include "BLI_math_vector.h" - -#include "BKE_context.h" -#include "BKE_screen.h" - -#include "UI_interface.h" - -#include "WM_api.h" -#include "WM_types.h" - -#include "interface_intern.h" - -#include "interface_eyedropper_intern.h" /* own include */ - -/* -------------------------------------------------------------------- */ -/* Keymap - */ -/** \name Modal Keymap - * \{ */ - -wmKeyMap *eyedropper_modal_keymap(wmKeyConfig *keyconf) -{ - static const EnumPropertyItem modal_items[] = { - {EYE_MODAL_CANCEL, "CANCEL", 0, "Cancel", ""}, - {EYE_MODAL_SAMPLE_CONFIRM, "SAMPLE_CONFIRM", 0, "Confirm Sampling", ""}, - {EYE_MODAL_SAMPLE_BEGIN, "SAMPLE_BEGIN", 0, "Start Sampling", ""}, - {EYE_MODAL_SAMPLE_RESET, "SAMPLE_RESET", 0, "Reset Sampling", ""}, - {0, NULL, 0, NULL, NULL}, - }; - - wmKeyMap *keymap = WM_modalkeymap_find(keyconf, "Eyedropper Modal Map"); - - /* this function is called for each spacetype, only needs to add map once */ - if (keymap && keymap->modal_items) { - return NULL; - } - - keymap = WM_modalkeymap_ensure(keyconf, "Eyedropper Modal Map", modal_items); - - /* assign to operators */ - WM_modalkeymap_assign(keymap, "UI_OT_eyedropper_colorramp"); - WM_modalkeymap_assign(keymap, "UI_OT_eyedropper_color"); - WM_modalkeymap_assign(keymap, "UI_OT_eyedropper_id"); - WM_modalkeymap_assign(keymap, "UI_OT_eyedropper_depth"); - WM_modalkeymap_assign(keymap, "UI_OT_eyedropper_driver"); - WM_modalkeymap_assign(keymap, "UI_OT_eyedropper_gpencil_color"); - - return keymap; -} - -wmKeyMap *eyedropper_colorband_modal_keymap(wmKeyConfig *keyconf) -{ - static const EnumPropertyItem modal_items_point[] = { - {EYE_MODAL_POINT_CANCEL, "CANCEL", 0, "Cancel", ""}, - {EYE_MODAL_POINT_SAMPLE, "SAMPLE_SAMPLE", 0, "Sample a Point", ""}, - {EYE_MODAL_POINT_CONFIRM, "SAMPLE_CONFIRM", 0, "Confirm Sampling", ""}, - {EYE_MODAL_POINT_RESET, "SAMPLE_RESET", 0, "Reset Sampling", ""}, - {0, NULL, 0, NULL, NULL}, - }; - - wmKeyMap *keymap = WM_modalkeymap_find(keyconf, "Eyedropper ColorRamp PointSampling Map"); - if (keymap && keymap->modal_items) { - return keymap; - } - - keymap = WM_modalkeymap_ensure( - keyconf, "Eyedropper ColorRamp PointSampling Map", modal_items_point); - - /* assign to operators */ - WM_modalkeymap_assign(keymap, "UI_OT_eyedropper_colorramp_point"); - - return keymap; -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/* Utility Functions - */ - -/** \name Generic Shared Functions - * \{ */ - -static void eyedropper_draw_cursor_text_ex(const int xy[2], const char *name) -{ - const uiFontStyle *fstyle = UI_FSTYLE_WIDGET; - - /* Use the theme settings from tooltips. */ - const bTheme *btheme = UI_GetTheme(); - const uiWidgetColors *wcol = &btheme->tui.wcol_tooltip; - - float col_fg[4], col_bg[4]; - rgba_uchar_to_float(col_fg, wcol->text); - rgba_uchar_to_float(col_bg, wcol->inner); - - UI_fontstyle_draw_simple_backdrop(fstyle, xy[0], xy[1] + U.widget_unit, name, col_fg, col_bg); -} - -void eyedropper_draw_cursor_text_window(const struct wmWindow *window, const char *name) -{ - if (name[0] == '\0') { - return; - } - - eyedropper_draw_cursor_text_ex(window->eventstate->xy, name); -} - -void eyedropper_draw_cursor_text_region(const int xy[2], const char *name) -{ - if (name[0] == '\0') { - return; - } - - eyedropper_draw_cursor_text_ex(xy, name); -} - -uiBut *eyedropper_get_property_button_under_mouse(bContext *C, const wmEvent *event) -{ - bScreen *screen = CTX_wm_screen(C); - ScrArea *area = BKE_screen_find_area_xy(screen, SPACE_TYPE_ANY, event->xy); - const ARegion *region = BKE_area_find_region_xy(area, RGN_TYPE_ANY, event->xy); - - uiBut *but = ui_but_find_mouse_over(region, event); - - if (ELEM(NULL, but, but->rnapoin.data, but->rnaprop)) { - return NULL; - } - return but; -} - -void datadropper_win_area_find( - const bContext *C, const int mval[2], int r_mval[2], wmWindow **r_win, ScrArea **r_area) -{ - bScreen *screen = CTX_wm_screen(C); - - *r_win = CTX_wm_window(C); - *r_area = BKE_screen_find_area_xy(screen, SPACE_TYPE_ANY, mval); - if (*r_area == NULL) { - *r_win = WM_window_find_under_cursor(*r_win, mval, r_mval); - if (*r_win) { - screen = WM_window_get_active_screen(*r_win); - *r_area = BKE_screen_find_area_xy(screen, SPACE_TYPE_ANY, r_mval); - } - } - else if (mval != r_mval) { - copy_v2_v2_int(r_mval, mval); - } -} - -/** \} */ diff --git a/source/blender/editors/interface/interface_eyedropper_color.c b/source/blender/editors/interface/interface_eyedropper_color.c deleted file mode 100644 index c015a60de89..00000000000 --- a/source/blender/editors/interface/interface_eyedropper_color.c +++ /dev/null @@ -1,554 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later - * Copyright 2009 Blender Foundation. All rights reserved. */ - -/** \file - * \ingroup edinterface - * - * Eyedropper (RGB Color) - * - * Defines: - * - #UI_OT_eyedropper_color - */ - -#include "MEM_guardedalloc.h" - -#include "DNA_screen_types.h" -#include "DNA_space_types.h" - -#include "BLI_listbase.h" -#include "BLI_math_vector.h" -#include "BLI_string.h" - -#include "BKE_context.h" -#include "BKE_cryptomatte.h" -#include "BKE_image.h" -#include "BKE_main.h" -#include "BKE_node.h" -#include "BKE_screen.h" - -#include "NOD_composite.h" - -#include "RNA_access.h" -#include "RNA_prototypes.h" - -#include "UI_interface.h" - -#include "IMB_colormanagement.h" -#include "IMB_imbuf_types.h" - -#include "WM_api.h" -#include "WM_types.h" - -#include "RNA_define.h" - -#include "interface_intern.h" - -#include "ED_clip.h" -#include "ED_image.h" -#include "ED_node.h" -#include "ED_screen.h" - -#include "RE_pipeline.h" - -#include "interface_eyedropper_intern.h" - -typedef struct Eyedropper { - struct ColorManagedDisplay *display; - - PointerRNA ptr; - PropertyRNA *prop; - int index; - bool is_undo; - - bool is_set; - float init_col[3]; /* for resetting on cancel */ - - bool accum_start; /* has mouse been pressed */ - float accum_col[3]; - int accum_tot; - - void *draw_handle_sample_text; - char sample_text[MAX_NAME]; - - bNode *crypto_node; - struct CryptomatteSession *cryptomatte_session; -} Eyedropper; - -static void eyedropper_draw_cb(const wmWindow *window, void *arg) -{ - Eyedropper *eye = arg; - eyedropper_draw_cursor_text_window(window, eye->sample_text); -} - -static bool eyedropper_init(bContext *C, wmOperator *op) -{ - Eyedropper *eye = MEM_callocN(sizeof(Eyedropper), __func__); - - uiBut *but = UI_context_active_but_prop_get(C, &eye->ptr, &eye->prop, &eye->index); - const enum PropertySubType prop_subtype = eye->prop ? RNA_property_subtype(eye->prop) : 0; - - if ((eye->ptr.data == NULL) || (eye->prop == NULL) || - (RNA_property_editable(&eye->ptr, eye->prop) == false) || - (RNA_property_array_length(&eye->ptr, eye->prop) < 3) || - (RNA_property_type(eye->prop) != PROP_FLOAT) || - (ELEM(prop_subtype, PROP_COLOR, PROP_COLOR_GAMMA) == 0)) { - MEM_freeN(eye); - return false; - } - op->customdata = eye; - - eye->is_undo = UI_but_flag_is_set(but, UI_BUT_UNDO); - - float col[4]; - RNA_property_float_get_array(&eye->ptr, eye->prop, col); - if (eye->ptr.type == &RNA_CompositorNodeCryptomatteV2) { - eye->crypto_node = (bNode *)eye->ptr.data; - eye->cryptomatte_session = ntreeCompositCryptomatteSession(CTX_data_scene(C), - eye->crypto_node); - eye->draw_handle_sample_text = WM_draw_cb_activate(CTX_wm_window(C), eyedropper_draw_cb, eye); - } - - if (prop_subtype != PROP_COLOR) { - Scene *scene = CTX_data_scene(C); - const char *display_device; - - display_device = scene->display_settings.display_device; - eye->display = IMB_colormanagement_display_get_named(display_device); - - /* store initial color */ - if (eye->display) { - IMB_colormanagement_display_to_scene_linear_v3(col, eye->display); - } - } - copy_v3_v3(eye->init_col, col); - - return true; -} - -static void eyedropper_exit(bContext *C, wmOperator *op) -{ - Eyedropper *eye = op->customdata; - wmWindow *window = CTX_wm_window(C); - WM_cursor_modal_restore(window); - - if (eye->draw_handle_sample_text) { - WM_draw_cb_exit(window, eye->draw_handle_sample_text); - eye->draw_handle_sample_text = NULL; - } - - if (eye->cryptomatte_session) { - BKE_cryptomatte_free(eye->cryptomatte_session); - eye->cryptomatte_session = NULL; - } - - MEM_SAFE_FREE(op->customdata); -} - -/* *** eyedropper_color_ helper functions *** */ - -static bool eyedropper_cryptomatte_sample_renderlayer_fl(RenderLayer *render_layer, - const char *prefix, - const float fpos[2], - float r_col[3]) -{ - if (!render_layer) { - return false; - } - - const int render_layer_name_len = BLI_strnlen(render_layer->name, sizeof(render_layer->name)); - if (strncmp(prefix, render_layer->name, render_layer_name_len) != 0) { - return false; - } - - const int prefix_len = strlen(prefix); - if (prefix_len <= render_layer_name_len + 1) { - return false; - } - - /* RenderResult from images can have no render layer name. */ - const char *render_pass_name_prefix = render_layer_name_len ? - prefix + 1 + render_layer_name_len : - prefix; - - LISTBASE_FOREACH (RenderPass *, render_pass, &render_layer->passes) { - if (STRPREFIX(render_pass->name, render_pass_name_prefix) && - !STREQLEN(render_pass->name, render_pass_name_prefix, sizeof(render_pass->name))) { - BLI_assert(render_pass->channels == 4); - const int x = (int)(fpos[0] * render_pass->rectx); - const int y = (int)(fpos[1] * render_pass->recty); - const int offset = 4 * (y * render_pass->rectx + x); - zero_v3(r_col); - r_col[0] = render_pass->rect[offset]; - return true; - } - } - - return false; -} -static bool eyedropper_cryptomatte_sample_render_fl(const bNode *node, - const char *prefix, - const float fpos[2], - float r_col[3]) -{ - bool success = false; - Scene *scene = (Scene *)node->id; - BLI_assert(GS(scene->id.name) == ID_SCE); - Render *re = RE_GetSceneRender(scene); - - if (re) { - RenderResult *rr = RE_AcquireResultRead(re); - if (rr) { - LISTBASE_FOREACH (ViewLayer *, view_layer, &scene->view_layers) { - RenderLayer *render_layer = RE_GetRenderLayer(rr, view_layer->name); - success = eyedropper_cryptomatte_sample_renderlayer_fl(render_layer, prefix, fpos, r_col); - if (success) { - break; - } - } - } - RE_ReleaseResult(re); - } - return success; -} - -static bool eyedropper_cryptomatte_sample_image_fl(const bNode *node, - NodeCryptomatte *crypto, - const char *prefix, - const float fpos[2], - float r_col[3]) -{ - bool success = false; - Image *image = (Image *)node->id; - BLI_assert((image == NULL) || (GS(image->id.name) == ID_IM)); - ImageUser *iuser = &crypto->iuser; - - if (image && image->type == IMA_TYPE_MULTILAYER) { - ImBuf *ibuf = BKE_image_acquire_ibuf(image, iuser, NULL); - if (image->rr) { - LISTBASE_FOREACH (RenderLayer *, render_layer, &image->rr->layers) { - success = eyedropper_cryptomatte_sample_renderlayer_fl(render_layer, prefix, fpos, r_col); - if (success) { - break; - } - } - } - BKE_image_release_ibuf(image, ibuf, NULL); - } - return success; -} - -static bool eyedropper_cryptomatte_sample_fl(bContext *C, - Eyedropper *eye, - const int m_xy[2], - float r_col[3]) -{ - bNode *node = eye->crypto_node; - NodeCryptomatte *crypto = node ? ((NodeCryptomatte *)node->storage) : NULL; - - if (!crypto) { - return false; - } - - bScreen *screen = CTX_wm_screen(C); - ScrArea *area = BKE_screen_find_area_xy(screen, SPACE_TYPE_ANY, m_xy); - if (!area || !ELEM(area->spacetype, SPACE_IMAGE, SPACE_NODE, SPACE_CLIP)) { - return false; - } - - ARegion *region = BKE_area_find_region_xy(area, RGN_TYPE_WINDOW, m_xy); - if (!region) { - return false; - } - - int mval[2] = {m_xy[0] - region->winrct.xmin, m_xy[1] - region->winrct.ymin}; - float fpos[2] = {-1.0f, -1.0}; - switch (area->spacetype) { - case SPACE_IMAGE: { - SpaceImage *sima = area->spacedata.first; - ED_space_image_get_position(sima, region, mval, fpos); - break; - } - case SPACE_NODE: { - Main *bmain = CTX_data_main(C); - SpaceNode *snode = area->spacedata.first; - ED_space_node_get_position(bmain, snode, region, mval, fpos); - break; - } - case SPACE_CLIP: { - SpaceClip *sc = area->spacedata.first; - ED_space_clip_get_position(sc, region, mval, fpos); - break; - } - default: { - break; - } - } - - if (fpos[0] < 0.0f || fpos[1] < 0.0f || fpos[0] >= 1.0f || fpos[1] >= 1.0f) { - return false; - } - - /* CMP_CRYPTOMATTE_SRC_RENDER and CMP_CRYPTOMATTE_SRC_IMAGE require a referenced image/scene to - * work properly. */ - if (!node->id) { - return false; - } - - /* TODO(jbakker): Migrate this file to cc and use std::string as return param. */ - char prefix[MAX_NAME + 1]; - const Scene *scene = CTX_data_scene(C); - ntreeCompositCryptomatteLayerPrefix(scene, node, prefix, sizeof(prefix) - 1); - prefix[MAX_NAME] = '\0'; - - if (node->custom1 == CMP_CRYPTOMATTE_SRC_RENDER) { - return eyedropper_cryptomatte_sample_render_fl(node, prefix, fpos, r_col); - } - if (node->custom1 == CMP_CRYPTOMATTE_SRC_IMAGE) { - return eyedropper_cryptomatte_sample_image_fl(node, crypto, prefix, fpos, r_col); - } - return false; -} - -void eyedropper_color_sample_fl(bContext *C, const int m_xy[2], float r_col[3]) -{ - /* we could use some clever */ - Main *bmain = CTX_data_main(C); - wmWindowManager *wm = CTX_wm_manager(C); - const char *display_device = CTX_data_scene(C)->display_settings.display_device; - struct ColorManagedDisplay *display = IMB_colormanagement_display_get_named(display_device); - - int mval[2]; - wmWindow *win; - ScrArea *area; - datadropper_win_area_find(C, m_xy, mval, &win, &area); - - if (area) { - if (area->spacetype == SPACE_IMAGE) { - ARegion *region = BKE_area_find_region_xy(area, RGN_TYPE_WINDOW, mval); - if (region) { - SpaceImage *sima = area->spacedata.first; - const int region_mval[2] = {mval[0] - region->winrct.xmin, mval[1] - region->winrct.ymin}; - - if (ED_space_image_color_sample(sima, region, region_mval, r_col, NULL)) { - return; - } - } - } - else if (area->spacetype == SPACE_NODE) { - ARegion *region = BKE_area_find_region_xy(area, RGN_TYPE_WINDOW, mval); - if (region) { - SpaceNode *snode = area->spacedata.first; - const int region_mval[2] = {mval[0] - region->winrct.xmin, mval[1] - region->winrct.ymin}; - - if (ED_space_node_color_sample(bmain, snode, region, region_mval, r_col)) { - return; - } - } - } - else if (area->spacetype == SPACE_CLIP) { - ARegion *region = BKE_area_find_region_xy(area, RGN_TYPE_WINDOW, mval); - if (region) { - SpaceClip *sc = area->spacedata.first; - const int region_mval[2] = {mval[0] - region->winrct.xmin, mval[1] - region->winrct.ymin}; - - if (ED_space_clip_color_sample(sc, region, region_mval, r_col)) { - return; - } - } - } - } - - if (win) { - /* Fallback to simple opengl picker. */ - WM_window_pixel_sample_read(wm, win, mval, r_col); - IMB_colormanagement_display_to_scene_linear_v3(r_col, display); - } - else { - zero_v3(r_col); - } -} - -/* sets the sample color RGB, maintaining A */ -static void eyedropper_color_set(bContext *C, Eyedropper *eye, const float col[3]) -{ - float col_conv[4]; - - /* to maintain alpha */ - RNA_property_float_get_array(&eye->ptr, eye->prop, col_conv); - - /* convert from linear rgb space to display space */ - if (eye->display) { - copy_v3_v3(col_conv, col); - IMB_colormanagement_scene_linear_to_display_v3(col_conv, eye->display); - } - else { - copy_v3_v3(col_conv, col); - } - - RNA_property_float_set_array(&eye->ptr, eye->prop, col_conv); - eye->is_set = true; - - RNA_property_update(C, &eye->ptr, eye->prop); -} - -static void eyedropper_color_sample(bContext *C, Eyedropper *eye, const int m_xy[2]) -{ - /* Accumulate color. */ - float col[3]; - if (eye->crypto_node) { - if (!eyedropper_cryptomatte_sample_fl(C, eye, m_xy, col)) { - return; - } - } - else { - eyedropper_color_sample_fl(C, m_xy, col); - } - - if (!eye->crypto_node) { - add_v3_v3(eye->accum_col, col); - eye->accum_tot++; - } - else { - copy_v3_v3(eye->accum_col, col); - eye->accum_tot = 1; - } - - /* Apply to property. */ - float accum_col[3]; - if (eye->accum_tot > 1) { - mul_v3_v3fl(accum_col, eye->accum_col, 1.0f / (float)eye->accum_tot); - } - else { - copy_v3_v3(accum_col, eye->accum_col); - } - eyedropper_color_set(C, eye, accum_col); -} - -static void eyedropper_color_sample_text_update(bContext *C, Eyedropper *eye, const int m_xy[2]) -{ - float col[3]; - eye->sample_text[0] = '\0'; - - if (eye->cryptomatte_session) { - if (eyedropper_cryptomatte_sample_fl(C, eye, m_xy, col)) { - BKE_cryptomatte_find_name( - eye->cryptomatte_session, col[0], eye->sample_text, sizeof(eye->sample_text)); - eye->sample_text[sizeof(eye->sample_text) - 1] = '\0'; - } - } -} - -static void eyedropper_cancel(bContext *C, wmOperator *op) -{ - Eyedropper *eye = op->customdata; - if (eye->is_set) { - eyedropper_color_set(C, eye, eye->init_col); - } - eyedropper_exit(C, op); -} - -/* main modal status check */ -static int eyedropper_modal(bContext *C, wmOperator *op, const wmEvent *event) -{ - Eyedropper *eye = (Eyedropper *)op->customdata; - - /* handle modal keymap */ - if (event->type == EVT_MODAL_MAP) { - switch (event->val) { - case EYE_MODAL_CANCEL: - eyedropper_cancel(C, op); - return OPERATOR_CANCELLED; - case EYE_MODAL_SAMPLE_CONFIRM: { - const bool is_undo = eye->is_undo; - if (eye->accum_tot == 0) { - eyedropper_color_sample(C, eye, event->xy); - } - eyedropper_exit(C, op); - /* Could support finished & undo-skip. */ - return is_undo ? OPERATOR_FINISHED : OPERATOR_CANCELLED; - } - case EYE_MODAL_SAMPLE_BEGIN: - /* enable accum and make first sample */ - eye->accum_start = true; - eyedropper_color_sample(C, eye, event->xy); - break; - case EYE_MODAL_SAMPLE_RESET: - eye->accum_tot = 0; - zero_v3(eye->accum_col); - eyedropper_color_sample(C, eye, event->xy); - break; - } - } - else if (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) { - if (eye->accum_start) { - /* button is pressed so keep sampling */ - eyedropper_color_sample(C, eye, event->xy); - } - - if (eye->draw_handle_sample_text) { - eyedropper_color_sample_text_update(C, eye, event->xy); - ED_region_tag_redraw(CTX_wm_region(C)); - } - } - - return OPERATOR_RUNNING_MODAL; -} - -/* Modal Operator init */ -static int eyedropper_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) -{ - /* init */ - if (eyedropper_init(C, op)) { - wmWindow *win = CTX_wm_window(C); - /* Workaround for de-activating the button clearing the cursor, see T76794 */ - UI_context_active_but_clear(C, win, CTX_wm_region(C)); - WM_cursor_modal_set(win, WM_CURSOR_EYEDROPPER); - - /* add temp handler */ - WM_event_add_modal_handler(C, op); - - return OPERATOR_RUNNING_MODAL; - } - return OPERATOR_PASS_THROUGH; -} - -/* Repeat operator */ -static int eyedropper_exec(bContext *C, wmOperator *op) -{ - /* init */ - if (eyedropper_init(C, op)) { - - /* do something */ - - /* cleanup */ - eyedropper_exit(C, op); - - return OPERATOR_FINISHED; - } - return OPERATOR_PASS_THROUGH; -} - -static bool eyedropper_poll(bContext *C) -{ - /* Actual test for active button happens later, since we don't - * know which one is active until mouse over. */ - return (CTX_wm_window(C) != NULL); -} - -void UI_OT_eyedropper_color(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Eyedropper"; - ot->idname = "UI_OT_eyedropper_color"; - ot->description = "Sample a color from the Blender window to store in a property"; - - /* api callbacks */ - ot->invoke = eyedropper_invoke; - ot->modal = eyedropper_modal; - ot->cancel = eyedropper_cancel; - ot->exec = eyedropper_exec; - ot->poll = eyedropper_poll; - - /* flags */ - ot->flag = OPTYPE_UNDO | OPTYPE_BLOCKING | OPTYPE_INTERNAL; -} diff --git a/source/blender/editors/interface/interface_eyedropper_colorband.c b/source/blender/editors/interface/interface_eyedropper_colorband.c deleted file mode 100644 index a69c36fefbd..00000000000 --- a/source/blender/editors/interface/interface_eyedropper_colorband.c +++ /dev/null @@ -1,367 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later - * Copyright 2009 Blender Foundation. All rights reserved. */ - -/** \file - * \ingroup edinterface - * - * Eyedropper (Color Band). - * - * Operates by either: - * - Dragging a straight line, sampling pixels formed by the line to extract a gradient. - * - Clicking on points, adding each color to the end of the color-band. - * - * Defines: - * - #UI_OT_eyedropper_colorramp - * - #UI_OT_eyedropper_colorramp_point - */ - -#include "MEM_guardedalloc.h" - -#include "DNA_screen_types.h" - -#include "BLI_bitmap_draw_2d.h" -#include "BLI_math_vector.h" - -#include "BKE_colorband.h" -#include "BKE_context.h" - -#include "RNA_access.h" -#include "RNA_prototypes.h" - -#include "UI_interface.h" - -#include "WM_api.h" -#include "WM_types.h" - -#include "interface_intern.h" - -#include "interface_eyedropper_intern.h" - -typedef struct Colorband_RNAUpdateCb { - PointerRNA ptr; - PropertyRNA *prop; -} Colorband_RNAUpdateCb; - -typedef struct EyedropperColorband { - int event_xy_last[2]; - /* Alpha is currently fixed at 1.0, may support in future. */ - float (*color_buffer)[4]; - int color_buffer_alloc; - int color_buffer_len; - bool sample_start; - ColorBand init_color_band; - ColorBand *color_band; - PointerRNA ptr; - PropertyRNA *prop; - bool is_undo; - bool is_set; -} EyedropperColorband; - -/* For user-data only. */ -struct EyedropperColorband_Context { - bContext *context; - EyedropperColorband *eye; -}; - -static bool eyedropper_colorband_init(bContext *C, wmOperator *op) -{ - ColorBand *band = NULL; - - uiBut *but = UI_context_active_but_get(C); - - PointerRNA rna_update_ptr = PointerRNA_NULL; - PropertyRNA *rna_update_prop = NULL; - bool is_undo = true; - - if (but == NULL) { - /* pass */ - } - else { - if (but->type == UI_BTYPE_COLORBAND) { - /* When invoked with a hotkey, we can find the band in 'but->poin'. */ - band = (ColorBand *)but->poin; - } - else { - /* When invoked from a button it's in custom_data field. */ - band = (ColorBand *)but->custom_data; - } - - if (band) { - rna_update_ptr = ((Colorband_RNAUpdateCb *)but->func_argN)->ptr; - rna_update_prop = ((Colorband_RNAUpdateCb *)but->func_argN)->prop; - is_undo = UI_but_flag_is_set(but, UI_BUT_UNDO); - } - } - - if (!band) { - const PointerRNA ptr = CTX_data_pointer_get_type(C, "color_ramp", &RNA_ColorRamp); - if (ptr.data != NULL) { - band = ptr.data; - - /* Set this to a sub-member of the property to trigger an update. */ - rna_update_ptr = ptr; - rna_update_prop = &rna_ColorRamp_color_mode; - is_undo = RNA_struct_undo_check(ptr.type); - } - } - - if (!band) { - return false; - } - - EyedropperColorband *eye = MEM_callocN(sizeof(EyedropperColorband), __func__); - eye->color_buffer_alloc = 16; - eye->color_buffer = MEM_mallocN(sizeof(*eye->color_buffer) * eye->color_buffer_alloc, __func__); - eye->color_buffer_len = 0; - eye->color_band = band; - eye->init_color_band = *eye->color_band; - eye->ptr = rna_update_ptr; - eye->prop = rna_update_prop; - eye->is_undo = is_undo; - - op->customdata = eye; - - return true; -} - -static void eyedropper_colorband_sample_point(bContext *C, - EyedropperColorband *eye, - const int m_xy[2]) -{ - if (eye->event_xy_last[0] != m_xy[0] || eye->event_xy_last[1] != m_xy[1]) { - float col[4]; - col[3] = 1.0f; /* TODO: sample alpha */ - eyedropper_color_sample_fl(C, m_xy, col); - if (eye->color_buffer_len + 1 == eye->color_buffer_alloc) { - eye->color_buffer_alloc *= 2; - eye->color_buffer = MEM_reallocN(eye->color_buffer, - sizeof(*eye->color_buffer) * eye->color_buffer_alloc); - } - copy_v4_v4(eye->color_buffer[eye->color_buffer_len], col); - eye->color_buffer_len += 1; - copy_v2_v2_int(eye->event_xy_last, m_xy); - eye->is_set = true; - } -} - -static bool eyedropper_colorband_sample_callback(int mx, int my, void *userdata) -{ - struct EyedropperColorband_Context *data = userdata; - bContext *C = data->context; - EyedropperColorband *eye = data->eye; - const int cursor[2] = {mx, my}; - eyedropper_colorband_sample_point(C, eye, cursor); - return true; -} - -static void eyedropper_colorband_sample_segment(bContext *C, - EyedropperColorband *eye, - const int m_xy[2]) -{ - /* Since the mouse tends to move rather rapidly we use #BLI_bitmap_draw_2d_line_v2v2i - * to interpolate between the reported coordinates */ - struct EyedropperColorband_Context userdata = {C, eye}; - BLI_bitmap_draw_2d_line_v2v2i( - eye->event_xy_last, m_xy, eyedropper_colorband_sample_callback, &userdata); -} - -static void eyedropper_colorband_exit(bContext *C, wmOperator *op) -{ - WM_cursor_modal_restore(CTX_wm_window(C)); - - if (op->customdata) { - EyedropperColorband *eye = op->customdata; - MEM_freeN(eye->color_buffer); - MEM_freeN(eye); - op->customdata = NULL; - } -} - -static void eyedropper_colorband_apply(bContext *C, wmOperator *op) -{ - EyedropperColorband *eye = op->customdata; - /* Always filter, avoids noise in resulting color-band. */ - const bool filter_samples = true; - BKE_colorband_init_from_table_rgba( - eye->color_band, eye->color_buffer, eye->color_buffer_len, filter_samples); - eye->is_set = true; - if (eye->prop) { - RNA_property_update(C, &eye->ptr, eye->prop); - } -} - -static void eyedropper_colorband_cancel(bContext *C, wmOperator *op) -{ - EyedropperColorband *eye = op->customdata; - if (eye->is_set) { - *eye->color_band = eye->init_color_band; - if (eye->prop) { - RNA_property_update(C, &eye->ptr, eye->prop); - } - } - eyedropper_colorband_exit(C, op); -} - -/* main modal status check */ -static int eyedropper_colorband_modal(bContext *C, wmOperator *op, const wmEvent *event) -{ - EyedropperColorband *eye = op->customdata; - /* handle modal keymap */ - if (event->type == EVT_MODAL_MAP) { - switch (event->val) { - case EYE_MODAL_CANCEL: - eyedropper_colorband_cancel(C, op); - return OPERATOR_CANCELLED; - case EYE_MODAL_SAMPLE_CONFIRM: { - const bool is_undo = eye->is_undo; - eyedropper_colorband_sample_segment(C, eye, event->xy); - eyedropper_colorband_apply(C, op); - eyedropper_colorband_exit(C, op); - /* Could support finished & undo-skip. */ - return is_undo ? OPERATOR_FINISHED : OPERATOR_CANCELLED; - } - case EYE_MODAL_SAMPLE_BEGIN: - /* enable accum and make first sample */ - eye->sample_start = true; - eyedropper_colorband_sample_point(C, eye, event->xy); - eyedropper_colorband_apply(C, op); - copy_v2_v2_int(eye->event_xy_last, event->xy); - break; - case EYE_MODAL_SAMPLE_RESET: - break; - } - } - else if (event->type == MOUSEMOVE) { - if (eye->sample_start) { - eyedropper_colorband_sample_segment(C, eye, event->xy); - eyedropper_colorband_apply(C, op); - } - } - return OPERATOR_RUNNING_MODAL; -} - -static int eyedropper_colorband_point_modal(bContext *C, wmOperator *op, const wmEvent *event) -{ - EyedropperColorband *eye = op->customdata; - /* handle modal keymap */ - if (event->type == EVT_MODAL_MAP) { - switch (event->val) { - case EYE_MODAL_POINT_CANCEL: - eyedropper_colorband_cancel(C, op); - return OPERATOR_CANCELLED; - case EYE_MODAL_POINT_CONFIRM: - eyedropper_colorband_apply(C, op); - eyedropper_colorband_exit(C, op); - return OPERATOR_FINISHED; - case EYE_MODAL_POINT_REMOVE_LAST: - if (eye->color_buffer_len > 0) { - eye->color_buffer_len -= 1; - eyedropper_colorband_apply(C, op); - } - break; - case EYE_MODAL_POINT_SAMPLE: - eyedropper_colorband_sample_point(C, eye, event->xy); - eyedropper_colorband_apply(C, op); - if (eye->color_buffer_len == MAXCOLORBAND) { - eyedropper_colorband_exit(C, op); - return OPERATOR_FINISHED; - } - break; - case EYE_MODAL_SAMPLE_RESET: - *eye->color_band = eye->init_color_band; - if (eye->prop) { - RNA_property_update(C, &eye->ptr, eye->prop); - } - eye->color_buffer_len = 0; - break; - } - } - return OPERATOR_RUNNING_MODAL; -} - -/* Modal Operator init */ -static int eyedropper_colorband_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) -{ - /* init */ - if (eyedropper_colorband_init(C, op)) { - wmWindow *win = CTX_wm_window(C); - /* Workaround for de-activating the button clearing the cursor, see T76794 */ - UI_context_active_but_clear(C, win, CTX_wm_region(C)); - WM_cursor_modal_set(win, WM_CURSOR_EYEDROPPER); - - /* add temp handler */ - WM_event_add_modal_handler(C, op); - - return OPERATOR_RUNNING_MODAL; - } - return OPERATOR_CANCELLED; -} - -/* Repeat operator */ -static int eyedropper_colorband_exec(bContext *C, wmOperator *op) -{ - /* init */ - if (eyedropper_colorband_init(C, op)) { - - /* do something */ - - /* cleanup */ - eyedropper_colorband_exit(C, op); - - return OPERATOR_FINISHED; - } - return OPERATOR_CANCELLED; -} - -static bool eyedropper_colorband_poll(bContext *C) -{ - uiBut *but = UI_context_active_but_get(C); - if (but && but->type == UI_BTYPE_COLORBAND) { - return true; - } - const PointerRNA ptr = CTX_data_pointer_get_type(C, "color_ramp", &RNA_ColorRamp); - if (ptr.data != NULL) { - return true; - } - return false; -} - -void UI_OT_eyedropper_colorramp(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Eyedropper Colorband"; - ot->idname = "UI_OT_eyedropper_colorramp"; - ot->description = "Sample a color band"; - - /* api callbacks */ - ot->invoke = eyedropper_colorband_invoke; - ot->modal = eyedropper_colorband_modal; - ot->cancel = eyedropper_colorband_cancel; - ot->exec = eyedropper_colorband_exec; - ot->poll = eyedropper_colorband_poll; - - /* flags */ - ot->flag = OPTYPE_UNDO | OPTYPE_BLOCKING | OPTYPE_INTERNAL; - - /* properties */ -} - -void UI_OT_eyedropper_colorramp_point(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Eyedropper Colorband (Points)"; - ot->idname = "UI_OT_eyedropper_colorramp_point"; - ot->description = "Point-sample a color band"; - - /* api callbacks */ - ot->invoke = eyedropper_colorband_invoke; - ot->modal = eyedropper_colorband_point_modal; - ot->cancel = eyedropper_colorband_cancel; - ot->exec = eyedropper_colorband_exec; - ot->poll = eyedropper_colorband_poll; - - /* flags */ - ot->flag = OPTYPE_UNDO | OPTYPE_BLOCKING | OPTYPE_INTERNAL; - - /* properties */ -} diff --git a/source/blender/editors/interface/interface_eyedropper_datablock.c b/source/blender/editors/interface/interface_eyedropper_datablock.c deleted file mode 100644 index 01b958576b6..00000000000 --- a/source/blender/editors/interface/interface_eyedropper_datablock.c +++ /dev/null @@ -1,378 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later - * Copyright 2009 Blender Foundation. All rights reserved. */ - -/** \file - * \ingroup edinterface - * - * Eyedropper (ID data-blocks) - * - * Defines: - * - #UI_OT_eyedropper_id - */ - -#include "MEM_guardedalloc.h" - -#include "DNA_object_types.h" -#include "DNA_screen_types.h" -#include "DNA_space_types.h" - -#include "BLI_math_vector.h" -#include "BLI_string.h" - -#include "BLT_translation.h" - -#include "BKE_context.h" -#include "BKE_idtype.h" -#include "BKE_report.h" -#include "BKE_screen.h" - -#include "RNA_access.h" - -#include "UI_interface.h" - -#include "WM_api.h" -#include "WM_types.h" - -#include "ED_outliner.h" -#include "ED_screen.h" -#include "ED_space_api.h" -#include "ED_view3d.h" - -#include "interface_eyedropper_intern.h" -#include "interface_intern.h" - -/** - * \note #DataDropper is only internal name to avoid confusion with other kinds of eye-droppers. - */ -typedef struct DataDropper { - PointerRNA ptr; - PropertyRNA *prop; - short idcode; - const char *idcode_name; - bool is_undo; - - ID *init_id; /* for resetting on cancel */ - - ScrArea *cursor_area; /* Area under the cursor */ - ARegionType *art; - void *draw_handle_pixel; - int name_pos[2]; - char name[200]; -} DataDropper; - -static void datadropper_draw_cb(const struct bContext *UNUSED(C), - ARegion *UNUSED(region), - void *arg) -{ - DataDropper *ddr = arg; - eyedropper_draw_cursor_text_region(ddr->name_pos, ddr->name); -} - -static int datadropper_init(bContext *C, wmOperator *op) -{ - int index_dummy; - StructRNA *type; - - SpaceType *st; - ARegionType *art; - - st = BKE_spacetype_from_id(SPACE_VIEW3D); - art = BKE_regiontype_from_id(st, RGN_TYPE_WINDOW); - - DataDropper *ddr = MEM_callocN(sizeof(DataDropper), __func__); - - uiBut *but = UI_context_active_but_prop_get(C, &ddr->ptr, &ddr->prop, &index_dummy); - - if ((ddr->ptr.data == NULL) || (ddr->prop == NULL) || - (RNA_property_editable(&ddr->ptr, ddr->prop) == false) || - (RNA_property_type(ddr->prop) != PROP_POINTER)) { - MEM_freeN(ddr); - return false; - } - op->customdata = ddr; - - ddr->is_undo = UI_but_flag_is_set(but, UI_BUT_UNDO); - - ddr->cursor_area = CTX_wm_area(C); - ddr->art = art; - ddr->draw_handle_pixel = ED_region_draw_cb_activate( - art, datadropper_draw_cb, ddr, REGION_DRAW_POST_PIXEL); - - type = RNA_property_pointer_type(&ddr->ptr, ddr->prop); - ddr->idcode = RNA_type_to_ID_code(type); - BLI_assert(ddr->idcode != 0); - /* Note we can translate here (instead of on draw time), - * because this struct has very short lifetime. */ - ddr->idcode_name = TIP_(BKE_idtype_idcode_to_name(ddr->idcode)); - - const PointerRNA ptr = RNA_property_pointer_get(&ddr->ptr, ddr->prop); - ddr->init_id = ptr.owner_id; - - return true; -} - -static void datadropper_exit(bContext *C, wmOperator *op) -{ - wmWindow *win = CTX_wm_window(C); - - WM_cursor_modal_restore(win); - - if (op->customdata) { - DataDropper *ddr = (DataDropper *)op->customdata; - - if (ddr->art) { - ED_region_draw_cb_exit(ddr->art, ddr->draw_handle_pixel); - } - - MEM_freeN(op->customdata); - - op->customdata = NULL; - } - - WM_event_add_mousemove(win); -} - -/* *** datadropper id helper functions *** */ -/** - * \brief get the ID from the 3D view or outliner. - */ -static void datadropper_id_sample_pt( - bContext *C, wmWindow *win, ScrArea *area, DataDropper *ddr, const int m_xy[2], ID **r_id) -{ - wmWindow *win_prev = CTX_wm_window(C); - ScrArea *area_prev = CTX_wm_area(C); - ARegion *region_prev = CTX_wm_region(C); - - ddr->name[0] = '\0'; - - if (area) { - if (ELEM(area->spacetype, SPACE_VIEW3D, SPACE_OUTLINER)) { - ARegion *region = BKE_area_find_region_xy(area, RGN_TYPE_WINDOW, m_xy); - if (region) { - const int mval[2] = {m_xy[0] - region->winrct.xmin, m_xy[1] - region->winrct.ymin}; - Base *base; - - CTX_wm_window_set(C, win); - CTX_wm_area_set(C, area); - CTX_wm_region_set(C, region); - - /* Unfortunately it's necessary to always draw else we leave stale text. */ - ED_region_tag_redraw(region); - - if (area->spacetype == SPACE_VIEW3D) { - base = ED_view3d_give_base_under_cursor(C, mval); - } - else { - base = ED_outliner_give_base_under_cursor(C, mval); - } - - if (base) { - Object *ob = base->object; - ID *id = NULL; - if (ddr->idcode == ID_OB) { - id = (ID *)ob; - } - else if (ob->data) { - if (GS(((ID *)ob->data)->name) == ddr->idcode) { - id = (ID *)ob->data; - } - else { - BLI_snprintf( - ddr->name, sizeof(ddr->name), "Incompatible, expected a %s", ddr->idcode_name); - } - } - - PointerRNA idptr; - RNA_id_pointer_create(id, &idptr); - - if (id && RNA_property_pointer_poll(&ddr->ptr, ddr->prop, &idptr)) { - BLI_snprintf(ddr->name, sizeof(ddr->name), "%s: %s", ddr->idcode_name, id->name + 2); - *r_id = id; - } - - copy_v2_v2_int(ddr->name_pos, mval); - } - } - } - } - - CTX_wm_window_set(C, win_prev); - CTX_wm_area_set(C, area_prev); - CTX_wm_region_set(C, region_prev); -} - -/* sets the ID, returns success */ -static bool datadropper_id_set(bContext *C, DataDropper *ddr, ID *id) -{ - PointerRNA ptr_value; - - RNA_id_pointer_create(id, &ptr_value); - - RNA_property_pointer_set(&ddr->ptr, ddr->prop, ptr_value, NULL); - - RNA_property_update(C, &ddr->ptr, ddr->prop); - - ptr_value = RNA_property_pointer_get(&ddr->ptr, ddr->prop); - - return (ptr_value.owner_id == id); -} - -/* single point sample & set */ -static bool datadropper_id_sample(bContext *C, DataDropper *ddr, const int m_xy[2]) -{ - ID *id = NULL; - - int mval[2]; - wmWindow *win; - ScrArea *area; - datadropper_win_area_find(C, m_xy, mval, &win, &area); - - datadropper_id_sample_pt(C, win, area, ddr, mval, &id); - return datadropper_id_set(C, ddr, id); -} - -static void datadropper_cancel(bContext *C, wmOperator *op) -{ - DataDropper *ddr = op->customdata; - datadropper_id_set(C, ddr, ddr->init_id); - datadropper_exit(C, op); -} - -/* To switch the draw callback when region under mouse event changes */ -static void datadropper_set_draw_callback_region(ScrArea *area, DataDropper *ddr) -{ - if (area) { - /* If spacetype changed */ - if (area->spacetype != ddr->cursor_area->spacetype) { - /* Remove old callback */ - ED_region_draw_cb_exit(ddr->art, ddr->draw_handle_pixel); - - /* Redraw old area */ - ARegion *region = BKE_area_find_region_type(ddr->cursor_area, RGN_TYPE_WINDOW); - ED_region_tag_redraw(region); - - /* Set draw callback in new region */ - ARegionType *art = BKE_regiontype_from_id(area->type, RGN_TYPE_WINDOW); - - ddr->cursor_area = area; - ddr->art = art; - ddr->draw_handle_pixel = ED_region_draw_cb_activate( - art, datadropper_draw_cb, ddr, REGION_DRAW_POST_PIXEL); - } - } -} - -/* main modal status check */ -static int datadropper_modal(bContext *C, wmOperator *op, const wmEvent *event) -{ - DataDropper *ddr = (DataDropper *)op->customdata; - - /* handle modal keymap */ - if (event->type == EVT_MODAL_MAP) { - switch (event->val) { - case EYE_MODAL_CANCEL: - datadropper_cancel(C, op); - return OPERATOR_CANCELLED; - case EYE_MODAL_SAMPLE_CONFIRM: { - const bool is_undo = ddr->is_undo; - const bool success = datadropper_id_sample(C, ddr, event->xy); - datadropper_exit(C, op); - if (success) { - /* Could support finished & undo-skip. */ - return is_undo ? OPERATOR_FINISHED : OPERATOR_CANCELLED; - } - BKE_report(op->reports, RPT_WARNING, "Failed to set value"); - return OPERATOR_CANCELLED; - } - } - } - else if (event->type == MOUSEMOVE) { - ID *id = NULL; - - int mval[2]; - wmWindow *win; - ScrArea *area; - datadropper_win_area_find(C, event->xy, mval, &win, &area); - - /* Set the region for eyedropper cursor text drawing */ - datadropper_set_draw_callback_region(area, ddr); - - datadropper_id_sample_pt(C, win, area, ddr, mval, &id); - } - - return OPERATOR_RUNNING_MODAL; -} - -/* Modal Operator init */ -static int datadropper_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) -{ - /* init */ - if (datadropper_init(C, op)) { - wmWindow *win = CTX_wm_window(C); - /* Workaround for de-activating the button clearing the cursor, see T76794 */ - UI_context_active_but_clear(C, win, CTX_wm_region(C)); - WM_cursor_modal_set(win, WM_CURSOR_EYEDROPPER); - - /* add temp handler */ - WM_event_add_modal_handler(C, op); - - return OPERATOR_RUNNING_MODAL; - } - return OPERATOR_CANCELLED; -} - -/* Repeat operator */ -static int datadropper_exec(bContext *C, wmOperator *op) -{ - /* init */ - if (datadropper_init(C, op)) { - /* cleanup */ - datadropper_exit(C, op); - - return OPERATOR_FINISHED; - } - return OPERATOR_CANCELLED; -} - -static bool datadropper_poll(bContext *C) -{ - PointerRNA ptr; - PropertyRNA *prop; - int index_dummy; - uiBut *but; - - /* data dropper only supports object data */ - if ((CTX_wm_window(C) != NULL) && - (but = UI_context_active_but_prop_get(C, &ptr, &prop, &index_dummy)) && - (but->type == UI_BTYPE_SEARCH_MENU) && (but->flag & UI_BUT_VALUE_CLEAR)) { - if (prop && RNA_property_type(prop) == PROP_POINTER) { - StructRNA *type = RNA_property_pointer_type(&ptr, prop); - const short idcode = RNA_type_to_ID_code(type); - if ((idcode == ID_OB) || OB_DATA_SUPPORT_ID(idcode)) { - return true; - } - } - } - - return false; -} - -void UI_OT_eyedropper_id(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Eyedropper Data-Block"; - ot->idname = "UI_OT_eyedropper_id"; - ot->description = "Sample a data-block from the 3D View to store in a property"; - - /* api callbacks */ - ot->invoke = datadropper_invoke; - ot->modal = datadropper_modal; - ot->cancel = datadropper_cancel; - ot->exec = datadropper_exec; - ot->poll = datadropper_poll; - - /* flags */ - ot->flag = OPTYPE_UNDO | OPTYPE_BLOCKING | OPTYPE_INTERNAL; - - /* properties */ -} diff --git a/source/blender/editors/interface/interface_eyedropper_depth.c b/source/blender/editors/interface/interface_eyedropper_depth.c deleted file mode 100644 index 3c6f127582a..00000000000 --- a/source/blender/editors/interface/interface_eyedropper_depth.c +++ /dev/null @@ -1,381 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later - * Copyright 2009 Blender Foundation. All rights reserved. */ - -/** \file - * \ingroup edinterface - * - * This file defines an eyedropper for picking 3D depth value (primary use is depth-of-field). - * - * Defines: - * - #UI_OT_eyedropper_depth - */ - -#include "MEM_guardedalloc.h" - -#include "DNA_camera_types.h" -#include "DNA_object_types.h" -#include "DNA_screen_types.h" -#include "DNA_space_types.h" -#include "DNA_view3d_types.h" - -#include "BLI_math_vector.h" -#include "BLI_string.h" - -#include "BKE_context.h" -#include "BKE_lib_id.h" -#include "BKE_screen.h" -#include "BKE_unit.h" - -#include "RNA_access.h" -#include "RNA_prototypes.h" - -#include "UI_interface.h" - -#include "WM_api.h" -#include "WM_types.h" - -#include "ED_screen.h" -#include "ED_space_api.h" -#include "ED_view3d.h" - -#include "interface_eyedropper_intern.h" -#include "interface_intern.h" - -/** - * \note #DepthDropper is only internal name to avoid confusion with other kinds of eye-droppers. - */ -typedef struct DepthDropper { - PointerRNA ptr; - PropertyRNA *prop; - bool is_undo; - - bool is_set; - float init_depth; /* For resetting on cancel. */ - - bool accum_start; /* Has mouse been pressed. */ - float accum_depth; - int accum_tot; - - ARegionType *art; - void *draw_handle_pixel; - int name_pos[2]; - char name[200]; -} DepthDropper; - -static void depthdropper_draw_cb(const struct bContext *UNUSED(C), - ARegion *UNUSED(region), - void *arg) -{ - DepthDropper *ddr = arg; - eyedropper_draw_cursor_text_region(ddr->name_pos, ddr->name); -} - -static int depthdropper_init(bContext *C, wmOperator *op) -{ - int index_dummy; - - SpaceType *st; - ARegionType *art; - - st = BKE_spacetype_from_id(SPACE_VIEW3D); - art = BKE_regiontype_from_id(st, RGN_TYPE_WINDOW); - - DepthDropper *ddr = MEM_callocN(sizeof(DepthDropper), __func__); - - uiBut *but = UI_context_active_but_prop_get(C, &ddr->ptr, &ddr->prop, &index_dummy); - - /* fallback to the active camera's dof */ - if (ddr->prop == NULL) { - RegionView3D *rv3d = CTX_wm_region_view3d(C); - if (rv3d && rv3d->persp == RV3D_CAMOB) { - View3D *v3d = CTX_wm_view3d(C); - if (v3d->camera && v3d->camera->data && - BKE_id_is_editable(CTX_data_main(C), v3d->camera->data)) { - Camera *camera = (Camera *)v3d->camera->data; - RNA_pointer_create(&camera->id, &RNA_CameraDOFSettings, &camera->dof, &ddr->ptr); - ddr->prop = RNA_struct_find_property(&ddr->ptr, "focus_distance"); - ddr->is_undo = true; - } - } - } - else { - ddr->is_undo = UI_but_flag_is_set(but, UI_BUT_UNDO); - } - - if ((ddr->ptr.data == NULL) || (ddr->prop == NULL) || - (RNA_property_editable(&ddr->ptr, ddr->prop) == false) || - (RNA_property_type(ddr->prop) != PROP_FLOAT)) { - MEM_freeN(ddr); - return false; - } - op->customdata = ddr; - - ddr->art = art; - ddr->draw_handle_pixel = ED_region_draw_cb_activate( - art, depthdropper_draw_cb, ddr, REGION_DRAW_POST_PIXEL); - ddr->init_depth = RNA_property_float_get(&ddr->ptr, ddr->prop); - - return true; -} - -static void depthdropper_exit(bContext *C, wmOperator *op) -{ - WM_cursor_modal_restore(CTX_wm_window(C)); - - if (op->customdata) { - DepthDropper *ddr = (DepthDropper *)op->customdata; - - if (ddr->art) { - ED_region_draw_cb_exit(ddr->art, ddr->draw_handle_pixel); - } - - MEM_freeN(op->customdata); - - op->customdata = NULL; - } -} - -/* *** depthdropper id helper functions *** */ -/** - * \brief get the ID from the screen. - */ -static void depthdropper_depth_sample_pt(bContext *C, - DepthDropper *ddr, - const int m_xy[2], - float *r_depth) -{ - /* we could use some clever */ - bScreen *screen = CTX_wm_screen(C); - ScrArea *area = BKE_screen_find_area_xy(screen, SPACE_TYPE_ANY, m_xy); - Scene *scene = CTX_data_scene(C); - - ScrArea *area_prev = CTX_wm_area(C); - ARegion *region_prev = CTX_wm_region(C); - - ddr->name[0] = '\0'; - - if (area) { - if (area->spacetype == SPACE_VIEW3D) { - ARegion *region = BKE_area_find_region_xy(area, RGN_TYPE_WINDOW, m_xy); - if (region) { - struct Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); - View3D *v3d = area->spacedata.first; - RegionView3D *rv3d = region->regiondata; - /* weak, we could pass in some reference point */ - const float *view_co = v3d->camera ? v3d->camera->obmat[3] : rv3d->viewinv[3]; - const int mval[2] = {m_xy[0] - region->winrct.xmin, m_xy[1] - region->winrct.ymin}; - copy_v2_v2_int(ddr->name_pos, mval); - - float co[3]; - - CTX_wm_area_set(C, area); - CTX_wm_region_set(C, region); - - /* Unfortunately it's necessary to always draw otherwise we leave stale text. */ - ED_region_tag_redraw(region); - - view3d_operator_needs_opengl(C); - - if (ED_view3d_autodist(depsgraph, region, v3d, mval, co, true, NULL)) { - const float mval_center_fl[2] = {(float)region->winx / 2, (float)region->winy / 2}; - float co_align[3]; - - /* quick way to get view-center aligned point */ - ED_view3d_win_to_3d(v3d, region, co, mval_center_fl, co_align); - - *r_depth = len_v3v3(view_co, co_align); - - BKE_unit_value_as_string(ddr->name, - sizeof(ddr->name), - (double)*r_depth, - 4, - B_UNIT_LENGTH, - &scene->unit, - false); - } - else { - BLI_strncpy(ddr->name, "Nothing under cursor", sizeof(ddr->name)); - } - } - } - } - - CTX_wm_area_set(C, area_prev); - CTX_wm_region_set(C, region_prev); -} - -/* sets the sample depth RGB, maintaining A */ -static void depthdropper_depth_set(bContext *C, DepthDropper *ddr, const float depth) -{ - RNA_property_float_set(&ddr->ptr, ddr->prop, depth); - ddr->is_set = true; - RNA_property_update(C, &ddr->ptr, ddr->prop); -} - -/* set sample from accumulated values */ -static void depthdropper_depth_set_accum(bContext *C, DepthDropper *ddr) -{ - float depth = ddr->accum_depth; - if (ddr->accum_tot) { - depth /= (float)ddr->accum_tot; - } - depthdropper_depth_set(C, ddr, depth); -} - -/* single point sample & set */ -static void depthdropper_depth_sample(bContext *C, DepthDropper *ddr, const int m_xy[2]) -{ - float depth = -1.0f; - if (depth != -1.0f) { - depthdropper_depth_sample_pt(C, ddr, m_xy, &depth); - depthdropper_depth_set(C, ddr, depth); - } -} - -static void depthdropper_depth_sample_accum(bContext *C, DepthDropper *ddr, const int m_xy[2]) -{ - float depth = -1.0f; - depthdropper_depth_sample_pt(C, ddr, m_xy, &depth); - if (depth != -1.0f) { - ddr->accum_depth += depth; - ddr->accum_tot++; - } -} - -static void depthdropper_cancel(bContext *C, wmOperator *op) -{ - DepthDropper *ddr = op->customdata; - if (ddr->is_set) { - depthdropper_depth_set(C, ddr, ddr->init_depth); - } - depthdropper_exit(C, op); -} - -/* main modal status check */ -static int depthdropper_modal(bContext *C, wmOperator *op, const wmEvent *event) -{ - DepthDropper *ddr = (DepthDropper *)op->customdata; - - /* handle modal keymap */ - if (event->type == EVT_MODAL_MAP) { - switch (event->val) { - case EYE_MODAL_CANCEL: - depthdropper_cancel(C, op); - return OPERATOR_CANCELLED; - case EYE_MODAL_SAMPLE_CONFIRM: { - const bool is_undo = ddr->is_undo; - if (ddr->accum_tot == 0) { - depthdropper_depth_sample(C, ddr, event->xy); - } - else { - depthdropper_depth_set_accum(C, ddr); - } - depthdropper_exit(C, op); - /* Could support finished & undo-skip. */ - return is_undo ? OPERATOR_FINISHED : OPERATOR_CANCELLED; - } - case EYE_MODAL_SAMPLE_BEGIN: - /* enable accum and make first sample */ - ddr->accum_start = true; - depthdropper_depth_sample_accum(C, ddr, event->xy); - break; - case EYE_MODAL_SAMPLE_RESET: - ddr->accum_tot = 0; - ddr->accum_depth = 0.0f; - depthdropper_depth_sample_accum(C, ddr, event->xy); - depthdropper_depth_set_accum(C, ddr); - break; - } - } - else if (event->type == MOUSEMOVE) { - if (ddr->accum_start) { - /* button is pressed so keep sampling */ - depthdropper_depth_sample_accum(C, ddr, event->xy); - depthdropper_depth_set_accum(C, ddr); - } - } - - return OPERATOR_RUNNING_MODAL; -} - -/* Modal Operator init */ -static int depthdropper_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) -{ - /* init */ - if (depthdropper_init(C, op)) { - wmWindow *win = CTX_wm_window(C); - /* Workaround for de-activating the button clearing the cursor, see T76794 */ - UI_context_active_but_clear(C, win, CTX_wm_region(C)); - WM_cursor_modal_set(win, WM_CURSOR_EYEDROPPER); - - /* add temp handler */ - WM_event_add_modal_handler(C, op); - - return OPERATOR_RUNNING_MODAL; - } - return OPERATOR_CANCELLED; -} - -/* Repeat operator */ -static int depthdropper_exec(bContext *C, wmOperator *op) -{ - /* init */ - if (depthdropper_init(C, op)) { - /* cleanup */ - depthdropper_exit(C, op); - - return OPERATOR_FINISHED; - } - return OPERATOR_CANCELLED; -} - -static bool depthdropper_poll(bContext *C) -{ - PointerRNA ptr; - PropertyRNA *prop; - int index_dummy; - uiBut *but; - - /* check if there's an active button taking depth value */ - if ((CTX_wm_window(C) != NULL) && - (but = UI_context_active_but_prop_get(C, &ptr, &prop, &index_dummy)) && - (but->type == UI_BTYPE_NUM) && (prop != NULL)) { - if ((RNA_property_type(prop) == PROP_FLOAT) && - (RNA_property_subtype(prop) & PROP_UNIT_LENGTH) && - (RNA_property_array_check(prop) == false)) { - return true; - } - } - else { - RegionView3D *rv3d = CTX_wm_region_view3d(C); - if (rv3d && rv3d->persp == RV3D_CAMOB) { - View3D *v3d = CTX_wm_view3d(C); - if (v3d->camera && v3d->camera->data && - BKE_id_is_editable(CTX_data_main(C), v3d->camera->data)) { - return true; - } - } - } - - return false; -} - -void UI_OT_eyedropper_depth(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Eyedropper Depth"; - ot->idname = "UI_OT_eyedropper_depth"; - ot->description = "Sample depth from the 3D view"; - - /* api callbacks */ - ot->invoke = depthdropper_invoke; - ot->modal = depthdropper_modal; - ot->cancel = depthdropper_cancel; - ot->exec = depthdropper_exec; - ot->poll = depthdropper_poll; - - /* flags */ - ot->flag = OPTYPE_UNDO | OPTYPE_BLOCKING | OPTYPE_INTERNAL; - - /* properties */ -} diff --git a/source/blender/editors/interface/interface_eyedropper_driver.c b/source/blender/editors/interface/interface_eyedropper_driver.c deleted file mode 100644 index 0cacb55c60c..00000000000 --- a/source/blender/editors/interface/interface_eyedropper_driver.c +++ /dev/null @@ -1,220 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later - * Copyright 2009 Blender Foundation. All rights reserved. */ - -/** \file - * \ingroup edinterface - * - * Eyedropper (Animation Driver Targets). - * - * Defines: - * - #UI_OT_eyedropper_driver - */ - -#include "MEM_guardedalloc.h" - -#include "DNA_anim_types.h" -#include "DNA_object_types.h" -#include "DNA_screen_types.h" - -#include "BKE_animsys.h" -#include "BKE_context.h" - -#include "DEG_depsgraph.h" -#include "DEG_depsgraph_build.h" - -#include "RNA_access.h" -#include "RNA_define.h" - -#include "UI_interface.h" - -#include "WM_api.h" -#include "WM_types.h" - -#include "ED_keyframing.h" - -#include "interface_eyedropper_intern.h" -#include "interface_intern.h" - -typedef struct DriverDropper { - /* Destination property (i.e. where we'll add a driver) */ - PointerRNA ptr; - PropertyRNA *prop; - int index; - bool is_undo; - - /* TODO: new target? */ -} DriverDropper; - -static bool driverdropper_init(bContext *C, wmOperator *op) -{ - DriverDropper *ddr = MEM_callocN(sizeof(DriverDropper), __func__); - - uiBut *but = UI_context_active_but_prop_get(C, &ddr->ptr, &ddr->prop, &ddr->index); - - if ((ddr->ptr.data == NULL) || (ddr->prop == NULL) || - (RNA_property_editable(&ddr->ptr, ddr->prop) == false) || - (RNA_property_animateable(&ddr->ptr, ddr->prop) == false) || (but->flag & UI_BUT_DRIVEN)) { - MEM_freeN(ddr); - return false; - } - op->customdata = ddr; - - ddr->is_undo = UI_but_flag_is_set(but, UI_BUT_UNDO); - - return true; -} - -static void driverdropper_exit(bContext *C, wmOperator *op) -{ - WM_cursor_modal_restore(CTX_wm_window(C)); - - MEM_SAFE_FREE(op->customdata); -} - -static void driverdropper_sample(bContext *C, wmOperator *op, const wmEvent *event) -{ - DriverDropper *ddr = (DriverDropper *)op->customdata; - uiBut *but = eyedropper_get_property_button_under_mouse(C, event); - - const short mapping_type = RNA_enum_get(op->ptr, "mapping_type"); - const short flag = 0; - - /* we can only add a driver if we know what RNA property it corresponds to */ - if (but == NULL) { - return; - } - /* Get paths for src... */ - PointerRNA *target_ptr = &but->rnapoin; - PropertyRNA *target_prop = but->rnaprop; - const int target_index = but->rnaindex; - - char *target_path = RNA_path_from_ID_to_property(target_ptr, target_prop); - - /* ... and destination */ - char *dst_path = RNA_path_from_ID_to_property(&ddr->ptr, ddr->prop); - - /* Now create driver(s) */ - if (target_path && dst_path) { - int success = ANIM_add_driver_with_target(op->reports, - ddr->ptr.owner_id, - dst_path, - ddr->index, - target_ptr->owner_id, - target_path, - target_index, - flag, - DRIVER_TYPE_PYTHON, - mapping_type); - - if (success) { - /* send updates */ - UI_context_update_anim_flag(C); - DEG_relations_tag_update(CTX_data_main(C)); - DEG_id_tag_update(ddr->ptr.owner_id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); - WM_event_add_notifier(C, NC_ANIMATION | ND_FCURVES_ORDER, NULL); /* XXX */ - } - } - - /* cleanup */ - if (target_path) { - MEM_freeN(target_path); - } - if (dst_path) { - MEM_freeN(dst_path); - } -} - -static void driverdropper_cancel(bContext *C, wmOperator *op) -{ - driverdropper_exit(C, op); -} - -/* main modal status check */ -static int driverdropper_modal(bContext *C, wmOperator *op, const wmEvent *event) -{ - DriverDropper *ddr = op->customdata; - - /* handle modal keymap */ - if (event->type == EVT_MODAL_MAP) { - switch (event->val) { - case EYE_MODAL_CANCEL: { - driverdropper_cancel(C, op); - return OPERATOR_CANCELLED; - } - case EYE_MODAL_SAMPLE_CONFIRM: { - const bool is_undo = ddr->is_undo; - driverdropper_sample(C, op, event); - driverdropper_exit(C, op); - /* Could support finished & undo-skip. */ - return is_undo ? OPERATOR_FINISHED : OPERATOR_CANCELLED; - } - } - } - - return OPERATOR_RUNNING_MODAL; -} - -/* Modal Operator init */ -static int driverdropper_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) -{ - /* init */ - if (driverdropper_init(C, op)) { - wmWindow *win = CTX_wm_window(C); - /* Workaround for de-activating the button clearing the cursor, see T76794 */ - UI_context_active_but_clear(C, win, CTX_wm_region(C)); - WM_cursor_modal_set(win, WM_CURSOR_EYEDROPPER); - - /* add temp handler */ - WM_event_add_modal_handler(C, op); - - return OPERATOR_RUNNING_MODAL; - } - return OPERATOR_CANCELLED; -} - -/* Repeat operator */ -static int driverdropper_exec(bContext *C, wmOperator *op) -{ - /* init */ - if (driverdropper_init(C, op)) { - /* cleanup */ - driverdropper_exit(C, op); - - return OPERATOR_FINISHED; - } - return OPERATOR_CANCELLED; -} - -static bool driverdropper_poll(bContext *C) -{ - if (!CTX_wm_window(C)) { - return false; - } - return true; -} - -void UI_OT_eyedropper_driver(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Eyedropper Driver"; - ot->idname = "UI_OT_eyedropper_driver"; - ot->description = "Pick a property to use as a driver target"; - - /* api callbacks */ - ot->invoke = driverdropper_invoke; - ot->modal = driverdropper_modal; - ot->cancel = driverdropper_cancel; - ot->exec = driverdropper_exec; - ot->poll = driverdropper_poll; - - /* flags */ - ot->flag = OPTYPE_UNDO | OPTYPE_BLOCKING | OPTYPE_INTERNAL; - - /* properties */ - RNA_def_enum(ot->srna, - "mapping_type", - prop_driver_create_mapping_types, - 0, - "Mapping Type", - "Method used to match target and driven properties"); -} diff --git a/source/blender/editors/interface/interface_eyedropper_gpencil_color.c b/source/blender/editors/interface/interface_eyedropper_gpencil_color.c deleted file mode 100644 index f3c70e6a96a..00000000000 --- a/source/blender/editors/interface/interface_eyedropper_gpencil_color.c +++ /dev/null @@ -1,373 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later - * Copyright 2009 Blender Foundation. All rights reserved. */ - -/** \file - * \ingroup edinterface - * - * Eyedropper (RGB Color) - * - * Defines: - * - #UI_OT_eyedropper_gpencil_color - */ - -#include "MEM_guardedalloc.h" - -#include "BLI_listbase.h" -#include "BLI_string.h" - -#include "BLT_translation.h" - -#include "DNA_gpencil_types.h" -#include "DNA_material_types.h" -#include "DNA_space_types.h" - -#include "BKE_context.h" -#include "BKE_gpencil.h" -#include "BKE_lib_id.h" -#include "BKE_main.h" -#include "BKE_material.h" -#include "BKE_paint.h" -#include "BKE_report.h" - -#include "UI_interface.h" - -#include "IMB_colormanagement.h" - -#include "WM_api.h" -#include "WM_types.h" - -#include "RNA_access.h" -#include "RNA_define.h" - -#include "ED_gpencil.h" -#include "ED_screen.h" -#include "ED_undo.h" - -#include "DEG_depsgraph.h" -#include "DEG_depsgraph_build.h" - -#include "interface_eyedropper_intern.h" -#include "interface_intern.h" - -typedef struct EyedropperGPencil { - struct ColorManagedDisplay *display; - /** color under cursor RGB */ - float color[3]; - /** Mode */ - int mode; -} EyedropperGPencil; - -/* Helper: Draw status message while the user is running the operator */ -static void eyedropper_gpencil_status_indicators(bContext *C) -{ - char msg_str[UI_MAX_DRAW_STR]; - BLI_strncpy( - msg_str, TIP_("LMB: Stroke - Shift: Fill - Shift+Ctrl: Stroke + Fill"), UI_MAX_DRAW_STR); - - ED_workspace_status_text(C, msg_str); -} - -/* Initialize. */ -static bool eyedropper_gpencil_init(bContext *C, wmOperator *op) -{ - EyedropperGPencil *eye = MEM_callocN(sizeof(EyedropperGPencil), __func__); - - op->customdata = eye; - Scene *scene = CTX_data_scene(C); - - const char *display_device; - display_device = scene->display_settings.display_device; - eye->display = IMB_colormanagement_display_get_named(display_device); - - eye->mode = RNA_enum_get(op->ptr, "mode"); - return true; -} - -/* Exit and free memory. */ -static void eyedropper_gpencil_exit(bContext *C, wmOperator *op) -{ - /* Clear status message area. */ - ED_workspace_status_text(C, NULL); - - MEM_SAFE_FREE(op->customdata); -} - -static void eyedropper_add_material(bContext *C, - const float col_conv[4], - const bool only_stroke, - const bool only_fill, - const bool both) -{ - Main *bmain = CTX_data_main(C); - Object *ob = CTX_data_active_object(C); - Material *ma = NULL; - - bool found = false; - - /* Look for a similar material in grease pencil slots. */ - short *totcol = BKE_object_material_len_p(ob); - for (short i = 0; i < *totcol; i++) { - ma = BKE_object_material_get(ob, i + 1); - if (ma == NULL) { - continue; - } - - MaterialGPencilStyle *gp_style = ma->gp_style; - if (gp_style != NULL) { - /* Check stroke color. */ - bool found_stroke = compare_v3v3(gp_style->stroke_rgba, col_conv, 0.01f) && - (gp_style->flag & GP_MATERIAL_STROKE_SHOW); - /* Check fill color. */ - bool found_fill = compare_v3v3(gp_style->fill_rgba, col_conv, 0.01f) && - (gp_style->flag & GP_MATERIAL_FILL_SHOW); - - if ((only_stroke) && (found_stroke) && ((gp_style->flag & GP_MATERIAL_FILL_SHOW) == 0)) { - found = true; - } - else if ((only_fill) && (found_fill) && ((gp_style->flag & GP_MATERIAL_STROKE_SHOW) == 0)) { - found = true; - } - else if ((both) && (found_stroke) && (found_fill)) { - found = true; - } - - /* Found existing material. */ - if (found) { - ob->actcol = i + 1; - WM_main_add_notifier(NC_MATERIAL | ND_SHADING_LINKS, NULL); - WM_main_add_notifier(NC_SPACE | ND_SPACE_VIEW3D, NULL); - return; - } - } - } - - /* If material was not found add a new material with stroke and/or fill color - * depending of the secondary key (LMB: Stroke, Shift: Fill, Shift+Ctrl: Stroke/Fill) - */ - int idx; - Material *ma_new = BKE_gpencil_object_material_new(bmain, ob, "Material", &idx); - WM_main_add_notifier(NC_OBJECT | ND_OB_SHADING, &ob->id); - WM_main_add_notifier(NC_MATERIAL | ND_SHADING_LINKS, NULL); - DEG_relations_tag_update(bmain); - - BLI_assert(ma_new != NULL); - - MaterialGPencilStyle *gp_style_new = ma_new->gp_style; - BLI_assert(gp_style_new != NULL); - - /* Only create Stroke (default option). */ - if (only_stroke) { - /* Stroke color. */ - gp_style_new->flag |= GP_MATERIAL_STROKE_SHOW; - gp_style_new->flag &= ~GP_MATERIAL_FILL_SHOW; - copy_v3_v3(gp_style_new->stroke_rgba, col_conv); - zero_v4(gp_style_new->fill_rgba); - } - /* Fill Only. */ - else if (only_fill) { - /* Fill color. */ - gp_style_new->flag &= ~GP_MATERIAL_STROKE_SHOW; - gp_style_new->flag |= GP_MATERIAL_FILL_SHOW; - zero_v4(gp_style_new->stroke_rgba); - copy_v3_v3(gp_style_new->fill_rgba, col_conv); - } - /* Stroke and Fill. */ - else if (both) { - gp_style_new->flag |= GP_MATERIAL_STROKE_SHOW | GP_MATERIAL_FILL_SHOW; - copy_v3_v3(gp_style_new->stroke_rgba, col_conv); - copy_v3_v3(gp_style_new->fill_rgba, col_conv); - } - /* Push undo for new created material. */ - ED_undo_push(C, "Add Grease Pencil Material"); -} - -/* Create a new palette color and palette if needed. */ -static void eyedropper_add_palette_color(bContext *C, const float col_conv[4]) -{ - Main *bmain = CTX_data_main(C); - Scene *scene = CTX_data_scene(C); - ToolSettings *ts = scene->toolsettings; - GpPaint *gp_paint = ts->gp_paint; - GpVertexPaint *gp_vertexpaint = ts->gp_vertexpaint; - Paint *paint = &gp_paint->paint; - Paint *vertexpaint = &gp_vertexpaint->paint; - - /* Check for Palette in Draw and Vertex Paint Mode. */ - if (paint->palette == NULL) { - Palette *palette = BKE_palette_add(bmain, "Grease Pencil"); - id_us_min(&palette->id); - - BKE_paint_palette_set(paint, palette); - - if (vertexpaint->palette == NULL) { - BKE_paint_palette_set(vertexpaint, palette); - } - } - /* Check if the color exist already. */ - Palette *palette = paint->palette; - LISTBASE_FOREACH (PaletteColor *, palcolor, &palette->colors) { - if (compare_v3v3(palcolor->rgb, col_conv, 0.01f)) { - return; - } - } - - /* Create Colors. */ - PaletteColor *palcol = BKE_palette_color_add(palette); - if (palcol) { - copy_v3_v3(palcol->rgb, col_conv); - } -} - -/* Set the material or the palette color. */ -static void eyedropper_gpencil_color_set(bContext *C, const wmEvent *event, EyedropperGPencil *eye) -{ - - const bool only_stroke = (event->modifier & (KM_CTRL | KM_SHIFT)) == 0; - const bool only_fill = ((event->modifier & KM_CTRL) == 0 && (event->modifier & KM_SHIFT)); - const bool both = ((event->modifier & KM_CTRL) && (event->modifier & KM_SHIFT)); - - float col_conv[4]; - - /* Convert from linear rgb space to display space because grease pencil colors are in display - * space, and this conversion is needed to undo the conversion to linear performed by - * eyedropper_color_sample_fl. */ - if (eye->display) { - copy_v3_v3(col_conv, eye->color); - IMB_colormanagement_scene_linear_to_display_v3(col_conv, eye->display); - } - else { - copy_v3_v3(col_conv, eye->color); - } - - /* Add material or Palette color. */ - if (eye->mode == 0) { - eyedropper_add_material(C, col_conv, only_stroke, only_fill, both); - } - else { - eyedropper_add_palette_color(C, col_conv); - } -} - -/* Sample the color below cursor. */ -static void eyedropper_gpencil_color_sample(bContext *C, EyedropperGPencil *eye, const int m_xy[2]) -{ - eyedropper_color_sample_fl(C, m_xy, eye->color); -} - -/* Cancel operator. */ -static void eyedropper_gpencil_cancel(bContext *C, wmOperator *op) -{ - eyedropper_gpencil_exit(C, op); -} - -/* Main modal status check. */ -static int eyedropper_gpencil_modal(bContext *C, wmOperator *op, const wmEvent *event) -{ - EyedropperGPencil *eye = (EyedropperGPencil *)op->customdata; - /* Handle modal keymap */ - switch (event->type) { - case EVT_MODAL_MAP: { - switch (event->val) { - case EYE_MODAL_SAMPLE_BEGIN: { - return OPERATOR_RUNNING_MODAL; - } - case EYE_MODAL_CANCEL: { - eyedropper_gpencil_cancel(C, op); - return OPERATOR_CANCELLED; - } - case EYE_MODAL_SAMPLE_CONFIRM: { - eyedropper_gpencil_color_sample(C, eye, event->xy); - - /* Create material. */ - eyedropper_gpencil_color_set(C, event, eye); - WM_main_add_notifier(NC_GPENCIL | ND_DATA | NA_EDITED, NULL); - - eyedropper_gpencil_exit(C, op); - return OPERATOR_FINISHED; - } - default: { - break; - } - } - break; - } - case MOUSEMOVE: - case INBETWEEN_MOUSEMOVE: { - eyedropper_gpencil_color_sample(C, eye, event->xy); - break; - } - default: { - break; - } - } - - return OPERATOR_RUNNING_MODAL; -} - -/* Modal Operator init */ -static int eyedropper_gpencil_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) -{ - /* Init. */ - if (eyedropper_gpencil_init(C, op)) { - /* Add modal temp handler. */ - WM_event_add_modal_handler(C, op); - /* Status message. */ - eyedropper_gpencil_status_indicators(C); - - return OPERATOR_RUNNING_MODAL; - } - return OPERATOR_PASS_THROUGH; -} - -/* Repeat operator */ -static int eyedropper_gpencil_exec(bContext *C, wmOperator *op) -{ - /* init */ - if (eyedropper_gpencil_init(C, op)) { - - /* cleanup */ - eyedropper_gpencil_exit(C, op); - - return OPERATOR_FINISHED; - } - return OPERATOR_PASS_THROUGH; -} - -static bool eyedropper_gpencil_poll(bContext *C) -{ - /* Only valid if the current active object is grease pencil. */ - Object *obact = CTX_data_active_object(C); - if ((obact == NULL) || (obact->type != OB_GPENCIL)) { - return false; - } - - /* Test we have a window below. */ - return (CTX_wm_window(C) != NULL); -} - -void UI_OT_eyedropper_gpencil_color(wmOperatorType *ot) -{ - static const EnumPropertyItem items_mode[] = { - {0, "MATERIAL", 0, "Material", ""}, - {1, "PALETTE", 0, "Palette", ""}, - {0, NULL, 0, NULL, NULL}, - }; - - /* identifiers */ - ot->name = "Grease Pencil Eyedropper"; - ot->idname = "UI_OT_eyedropper_gpencil_color"; - ot->description = "Sample a color from the Blender Window and create Grease Pencil material"; - - /* api callbacks */ - ot->invoke = eyedropper_gpencil_invoke; - ot->modal = eyedropper_gpencil_modal; - ot->cancel = eyedropper_gpencil_cancel; - ot->exec = eyedropper_gpencil_exec; - ot->poll = eyedropper_gpencil_poll; - - /* flags */ - ot->flag = OPTYPE_UNDO | OPTYPE_BLOCKING; - - /* properties */ - ot->prop = RNA_def_enum(ot->srna, "mode", items_mode, 0, "Mode", ""); -} diff --git a/source/blender/editors/interface/interface_eyedropper_intern.h b/source/blender/editors/interface/interface_eyedropper_intern.h deleted file mode 100644 index 76316a85807..00000000000 --- a/source/blender/editors/interface/interface_eyedropper_intern.h +++ /dev/null @@ -1,57 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ - -/** \file - * \ingroup edinterface - * - * Share between interface_eyedropper_*.c files. - */ - -#pragma once - -/* interface_eyedropper.c */ - -void eyedropper_draw_cursor_text_window(const struct wmWindow *window, const char *name); -void eyedropper_draw_cursor_text_region(const int xy[2], const char *name); -/** - * Utility to retrieve a button representing a RNA property that is currently under the cursor. - * - * This is to be used by any eyedroppers which fetch properties (e.g. UI_OT_eyedropper_driver). - * Especially during modal operations (e.g. as with the eyedroppers), context cannot be relied - * upon to provide this information, as it is not updated until the operator finishes. - * - * \return A button under the mouse which relates to some RNA Property, or NULL - */ -uiBut *eyedropper_get_property_button_under_mouse(bContext *C, const wmEvent *event); -void datadropper_win_area_find(const struct bContext *C, - const int mval[2], - int r_mval[2], - struct wmWindow **r_win, - struct ScrArea **r_area); - -/* interface_eyedropper_color.c (expose for color-band picker) */ - -/** - * \brief get the color from the screen. - * - * Special check for image or nodes where we MAY have HDR pixels which don't display. - * - * \note Exposed by 'interface_eyedropper_intern.h' for use with color band picking. - */ -void eyedropper_color_sample_fl(bContext *C, const int m_xy[2], float r_col[3]); - -/* Used for most eye-dropper operators. */ -enum { - EYE_MODAL_CANCEL = 1, - EYE_MODAL_SAMPLE_CONFIRM, - EYE_MODAL_SAMPLE_BEGIN, - EYE_MODAL_SAMPLE_RESET, -}; - -/* Color-band point sample. */ -enum { - EYE_MODAL_POINT_CANCEL = 1, - EYE_MODAL_POINT_SAMPLE, - EYE_MODAL_POINT_CONFIRM, - EYE_MODAL_POINT_RESET, - EYE_MODAL_POINT_REMOVE_LAST, -}; -- cgit v1.2.3 From 28985ccc05ff15dd22184863b8c57a0851fc9614 Mon Sep 17 00:00:00 2001 From: Pratik Borhade Date: Tue, 19 Jul 2022 14:20:44 -0500 Subject: Fix T99583: Missing update for option in particle edit mode Missing Ui refresh when property is accessed through shortcut. Use RNA_def_property_update to solve this. Differential Revision: https://developer.blender.org/D15431 --- source/blender/makesrna/intern/rna_sculpt_paint.c | 1 + 1 file changed, 1 insertion(+) diff --git a/source/blender/makesrna/intern/rna_sculpt_paint.c b/source/blender/makesrna/intern/rna_sculpt_paint.c index 3de9e632ff6..2e1fa8db7fe 100644 --- a/source/blender/makesrna/intern/rna_sculpt_paint.c +++ b/source/blender/makesrna/intern/rna_sculpt_paint.c @@ -1303,6 +1303,7 @@ static void rna_def_particle_edit(BlenderRNA *brna) RNA_def_property_boolean_sdna(prop, NULL, "flag", PE_INTERPOLATE_ADDED); RNA_def_property_ui_text( prop, "Interpolate", "Interpolate new particles from the existing ones"); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); prop = RNA_def_property(srna, "default_key_count", PROP_INT, PROP_NONE); RNA_def_property_int_sdna(prop, NULL, "totaddkey"); -- cgit v1.2.3 From e9f82d3dc7eebadcc52fdc43858d060c3a8214b2 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Tue, 19 Jul 2022 18:01:04 -0500 Subject: Curves: Remove redundant custom data pointers These mutable pointers present problems with ownership in relation to proper copy-on-write for attributes. The simplest solution is to just remove them and retrieve the layers from `CustomData` when they are needed. This also removes the complexity and redundancy of having to update the pointers as the curves change. A similar change will apply to meshes and point clouds. One downside of this change is that it makes random access with RNA slower. However, it's simple to just use the RNA attribute API instead, which is unaffected. In this patch I updated Cycles to do that. With the future attribute CoW changes, this generic approach makes sense because Cycles can just request ownership of the existing arrays. Differential Revision: https://developer.blender.org/D15486 --- intern/cycles/blender/curves.cpp | 37 +++++++--- source/blender/blenkernel/BKE_curves.hh | 2 - source/blender/blenkernel/intern/curves.cc | 10 --- .../blender/blenkernel/intern/curves_geometry.cc | 34 ++-------- .../blenkernel/intern/geometry_component_curves.cc | 10 +-- source/blender/geometry/intern/resample_curves.cc | 2 - source/blender/makesdna/DNA_curves_types.h | 17 ----- source/blender/makesrna/intern/rna_curves.c | 78 +++++++++++++++------- 8 files changed, 93 insertions(+), 97 deletions(-) diff --git a/intern/cycles/blender/curves.cpp b/intern/cycles/blender/curves.cpp index 263a5fc0e02..c4154bce022 100644 --- a/intern/cycles/blender/curves.cpp +++ b/intern/cycles/blender/curves.cpp @@ -630,6 +630,25 @@ static std::optional find_curves_radius_attribute(BL::Curves return std::nullopt; } +static BL::FloatVectorAttribute find_curves_position_attribute(BL::Curves b_curves) +{ + for (BL::Attribute &b_attribute : b_curves.attributes) { + if (b_attribute.name() != "position") { + continue; + } + if (b_attribute.domain() != BL::Attribute::domain_POINT) { + continue; + } + if (b_attribute.data_type() != BL::Attribute::data_type_FLOAT_VECTOR) { + continue; + } + return BL::FloatVectorAttribute{b_attribute}; + } + /* The position attribute must exist. */ + assert(false); + return BL::FloatVectorAttribute{b_curves.attributes[0]}; +} + template static void fill_generic_attribute(BL::Curves &b_curves, TypeInCycles *data, @@ -793,16 +812,16 @@ static void attr_create_generic(Scene *scene, } } -static float4 hair_point_as_float4(BL::Curves b_curves, +static float4 hair_point_as_float4(BL::FloatVectorAttribute b_attr_position, std::optional b_attr_radius, const int index) { - float4 mP = float3_to_float4(get_float3(b_curves.position_data[index].vector())); + float4 mP = float3_to_float4(get_float3(b_attr_position.data[index].vector())); mP.w = b_attr_radius ? b_attr_radius->data[index].value() : 0.0f; return mP; } -static float4 interpolate_hair_points(BL::Curves b_curves, +static float4 interpolate_hair_points(BL::FloatVectorAttribute b_attr_position, std::optional b_attr_radius, const int first_point_index, const int num_points, @@ -812,8 +831,8 @@ static float4 interpolate_hair_points(BL::Curves b_curves, const int point_a = clamp((int)curve_t, 0, num_points - 1); const int point_b = min(point_a + 1, num_points - 1); const float t = curve_t - (float)point_a; - return lerp(hair_point_as_float4(b_curves, b_attr_radius, first_point_index + point_a), - hair_point_as_float4(b_curves, b_attr_radius, first_point_index + point_b), + return lerp(hair_point_as_float4(b_attr_position, b_attr_radius, first_point_index + point_a), + hair_point_as_float4(b_attr_position, b_attr_radius, first_point_index + point_b), t); } @@ -846,6 +865,7 @@ static void export_hair_curves(Scene *scene, hair->reserve_curves(num_curves, num_keys); + BL::FloatVectorAttribute b_attr_position = find_curves_position_attribute(b_curves); std::optional b_attr_radius = find_curves_radius_attribute(b_curves); /* Export curves and points. */ @@ -864,7 +884,7 @@ static void export_hair_curves(Scene *scene, /* Position and radius. */ for (int i = 0; i < num_points; i++) { - const float3 co = get_float3(b_curves.position_data[first_point_index + i].vector()); + const float3 co = get_float3(b_attr_position.data[first_point_index + i].vector()); const float radius = b_attr_radius ? b_attr_radius->data[first_point_index + i].value() : 0.005f; hair->add_curve_key(co, radius); @@ -921,6 +941,7 @@ static void export_hair_curves_motion(Hair *hair, BL::Curves b_curves, int motio int num_motion_keys = 0; int curve_index = 0; + BL::FloatVectorAttribute b_attr_position = find_curves_position_attribute(b_curves); std::optional b_attr_radius = find_curves_radius_attribute(b_curves); for (int i = 0; i < num_curves; i++) { @@ -936,7 +957,7 @@ static void export_hair_curves_motion(Hair *hair, BL::Curves b_curves, int motio int point_index = first_point_index + i; if (point_index < num_keys) { - mP[num_motion_keys] = hair_point_as_float4(b_curves, b_attr_radius, point_index); + mP[num_motion_keys] = hair_point_as_float4(b_attr_position, b_attr_radius, point_index); num_motion_keys++; if (!have_motion) { @@ -956,7 +977,7 @@ static void export_hair_curves_motion(Hair *hair, BL::Curves b_curves, int motio for (int i = 0; i < curve.num_keys; i++) { const float step = i * step_size; mP[num_motion_keys] = interpolate_hair_points( - b_curves, b_attr_radius, first_point_index, num_points, step); + b_attr_position, b_attr_radius, first_point_index, num_points, step); num_motion_keys++; } have_motion = true; diff --git a/source/blender/blenkernel/BKE_curves.hh b/source/blender/blenkernel/BKE_curves.hh index 25a912b8825..68c90a45031 100644 --- a/source/blender/blenkernel/BKE_curves.hh +++ b/source/blender/blenkernel/BKE_curves.hh @@ -385,8 +385,6 @@ class CurvesGeometry : public ::CurvesGeometry { void calculate_bezier_auto_handles(); - void update_customdata_pointers(); - void remove_points(IndexMask points_to_delete); void remove_curves(IndexMask curves_to_delete); diff --git a/source/blender/blenkernel/intern/curves.cc b/source/blender/blenkernel/intern/curves.cc index 7e9f994313b..5684a2e5b07 100644 --- a/source/blender/blenkernel/intern/curves.cc +++ b/source/blender/blenkernel/intern/curves.cc @@ -53,8 +53,6 @@ using blender::Vector; static const char *ATTR_POSITION = "position"; -static void update_custom_data_pointers(Curves &curves); - static void curves_init_data(ID *id) { Curves *curves = (Curves *)id; @@ -97,8 +95,6 @@ static void curves_copy_data(Main *UNUSED(bmain), ID *id_dst, const ID *id_src, dst.runtime->type_counts = src.runtime->type_counts; - dst.update_customdata_pointers(); - curves_dst->batch_cache = nullptr; } @@ -170,7 +166,6 @@ static void curves_blend_read_data(BlendDataReader *reader, ID *id) /* Geometry */ CustomData_blend_read(reader, &curves->geometry.point_data, curves->geometry.point_num); CustomData_blend_read(reader, &curves->geometry.curve_data, curves->geometry.curve_num); - update_custom_data_pointers(*curves); BLO_read_int32_array(reader, curves->geometry.curve_num + 1, &curves->geometry.curve_offsets); @@ -233,11 +228,6 @@ IDTypeInfo IDType_ID_CV = { /*lib_override_apply_post */ nullptr, }; -static void update_custom_data_pointers(Curves &curves) -{ - blender::bke::CurvesGeometry::wrap(curves.geometry).update_customdata_pointers(); -} - void *BKE_curves_add(Main *bmain, const char *name) { Curves *curves = static_cast(BKE_id_new(bmain, ID_CV, name)); diff --git a/source/blender/blenkernel/intern/curves_geometry.cc b/source/blender/blenkernel/intern/curves_geometry.cc index 8d955a46275..7fc660cfbfc 100644 --- a/source/blender/blenkernel/intern/curves_geometry.cc +++ b/source/blender/blenkernel/intern/curves_geometry.cc @@ -68,8 +68,6 @@ CurvesGeometry::CurvesGeometry(const int point_num, const int curve_num) #endif this->offsets_for_write().first() = 0; - this->update_customdata_pointers(); - this->runtime = MEM_new(__func__); /* Fill the type counts with the default so they're in a valid state. */ this->runtime->type_counts[CURVE_TYPE_CATMULL_ROM] = curve_num; @@ -95,8 +93,6 @@ static void copy_curves_geometry(CurvesGeometry &dst, const CurvesGeometry &src) /* Though type counts are a cache, they must be copied because they are calculated eagerly. */ dst.runtime->type_counts = src.runtime->type_counts; - - dst.update_customdata_pointers(); } CurvesGeometry::CurvesGeometry(const CurvesGeometry &other) @@ -130,9 +126,6 @@ static void move_curves_geometry(CurvesGeometry &dst, CurvesGeometry &src) MEM_SAFE_FREE(src.curve_offsets); std::swap(dst.runtime, src.runtime); - - src.update_customdata_pointers(); - dst.update_customdata_pointers(); } CurvesGeometry::CurvesGeometry(CurvesGeometry &&other) @@ -306,13 +299,11 @@ void CurvesGeometry::update_curve_types() Span CurvesGeometry::positions() const { - return {(const float3 *)this->position, this->point_num}; + return get_span_attribute(*this, ATTR_DOMAIN_POINT, ATTR_POSITION); } MutableSpan CurvesGeometry::positions_for_write() { - this->position = (float(*)[3])CustomData_duplicate_referenced_layer_named( - &this->point_data, CD_PROP_FLOAT3, ATTR_POSITION.c_str(), this->point_num); - return {(float3 *)this->position, this->point_num}; + return get_mutable_attribute(*this, ATTR_DOMAIN_POINT, ATTR_POSITION); } Span CurvesGeometry::offsets() const @@ -961,7 +952,6 @@ void CurvesGeometry::resize(const int points_num, const int curves_num) this->curve_offsets = (int *)MEM_reallocN(this->curve_offsets, sizeof(int) * (curves_num + 1)); } this->tag_topology_changed(); - this->update_customdata_pointers(); } void CurvesGeometry::tag_positions_changed() @@ -1060,10 +1050,11 @@ void CurvesGeometry::transform(const float4x4 &matrix) static std::optional> curves_bounds(const CurvesGeometry &curves) { - Span positions = curves.positions(); - if (curves.radius) { - Span radii{curves.radius, curves.points_num()}; - return bounds::min_max_with_radii(positions, radii); + const Span positions = curves.positions(); + const VArray radii = curves.attributes().lookup_or_default( + ATTR_RADIUS, ATTR_DOMAIN_POINT, 0.0f); + if (!(radii.is_single() && radii.get_internal_single() == 0.0f)) { + return bounds::min_max_with_radii(positions, radii.get_internal_span()); } return bounds::min_max(positions); } @@ -1079,16 +1070,6 @@ bool CurvesGeometry::bounds_min_max(float3 &min, float3 &max) const return true; } -void CurvesGeometry::update_customdata_pointers() -{ - this->position = (float(*)[3])CustomData_get_layer_named( - &this->point_data, CD_PROP_FLOAT3, ATTR_POSITION.c_str()); - this->radius = (float *)CustomData_get_layer_named( - &this->point_data, CD_PROP_FLOAT, ATTR_RADIUS.c_str()); - this->curve_type = (int8_t *)CustomData_get_layer_named( - &this->point_data, CD_PROP_INT8, ATTR_CURVE_TYPE.c_str()); -} - static void *ensure_customdata_layer(CustomData &custom_data, const StringRefNull name, const eCustomDataType data_type, @@ -1497,7 +1478,6 @@ void CurvesGeometry::remove_attributes_based_on_types() if (!this->has_curve_with_type({CURVE_TYPE_BEZIER, CURVE_TYPE_CATMULL_ROM, CURVE_TYPE_NURBS})) { CustomData_free_layer_named(&this->curve_data, ATTR_RESOLUTION.c_str(), curves_num); } - this->update_customdata_pointers(); } /** \} */ diff --git a/source/blender/blenkernel/intern/geometry_component_curves.cc b/source/blender/blenkernel/intern/geometry_component_curves.cc index f803b08e740..2714c78e381 100644 --- a/source/blender/blenkernel/intern/geometry_component_curves.cc +++ b/source/blender/blenkernel/intern/geometry_component_curves.cc @@ -358,10 +358,7 @@ static ComponentAttributeProviders create_attribute_providers_for_curve() const CurvesGeometry &curves = *static_cast(owner); return curves.curves_num(); }, - [](void *owner) { - CurvesGeometry &curves = *static_cast(owner); - curves.update_customdata_pointers(); - }}; + [](void * /*owner*/) {}}; static CustomDataAccessInfo point_access = { [](void *owner) -> CustomData * { CurvesGeometry &curves = *static_cast(owner); @@ -375,10 +372,7 @@ static ComponentAttributeProviders create_attribute_providers_for_curve() const CurvesGeometry &curves = *static_cast(owner); return curves.points_num(); }, - [](void *owner) { - CurvesGeometry &curves = *static_cast(owner); - curves.update_customdata_pointers(); - }}; + [](void * /*owner*/) {}}; static BuiltinCustomDataLayerProvider position("position", ATTR_DOMAIN_POINT, diff --git a/source/blender/geometry/intern/resample_curves.cc b/source/blender/geometry/intern/resample_curves.cc index c9b8a032ce6..2c3a6c6e0cf 100644 --- a/source/blender/geometry/intern/resample_curves.cc +++ b/source/blender/geometry/intern/resample_curves.cc @@ -162,8 +162,6 @@ static void gather_point_attributes_to_interpolate(const CurveComponent &src_com result.src_no_interpolation, result.dst_no_interpolation, result.dst_attributes); - - dst_curves.update_customdata_pointers(); } static Curves *resample_to_uniform(const CurveComponent &src_component, diff --git a/source/blender/makesdna/DNA_curves_types.h b/source/blender/makesdna/DNA_curves_types.h index ed909c283c4..89deeec898b 100644 --- a/source/blender/makesdna/DNA_curves_types.h +++ b/source/blender/makesdna/DNA_curves_types.h @@ -66,23 +66,6 @@ typedef enum NormalMode { * curve-processing algorithms for multiple Blender data-block types. */ typedef struct CurvesGeometry { - /** - * A runtime pointer to the "position" attribute data. - * \note This data is owned by #point_data. - */ - float (*position)[3]; - /** - * A runtime pointer to the "radius" attribute data. - * \note This data is owned by #point_data. - */ - float *radius; - - /** - * The type of each curve. #CurveType. - * \note This data is owned by #curve_data. - */ - int8_t *curve_type; - /** * The start index of each curve in the point data. The size of each curve can be calculated by * subtracting the offset from the next offset. That is valid even for the last curve because diff --git a/source/blender/makesrna/intern/rna_curves.c b/source/blender/makesrna/intern/rna_curves.c index bc3e5203ed0..cb8b36f41d2 100644 --- a/source/blender/makesrna/intern/rna_curves.c +++ b/source/blender/makesrna/intern/rna_curves.c @@ -64,7 +64,24 @@ static int rna_CurvePoint_index_get_const(const PointerRNA *ptr) { const Curves *curves = rna_curves(ptr); const float(*co)[3] = ptr->data; - return (int)(co - curves->geometry.position); + const float(*positions)[3] = (const float(*)[3])CustomData_get_layer_named( + &curves->geometry.point_data, CD_PROP_FLOAT3, "position"); + return (int)(co - positions); +} + +static int rna_Curves_position_data_length(PointerRNA *ptr) +{ + const Curves *curves = rna_curves(ptr); + return curves->geometry.point_num; +} + +static void rna_Curves_position_data_begin(CollectionPropertyIterator *iter, PointerRNA *ptr) +{ + const Curves *curves = rna_curves(ptr); + const float(*positions)[3] = (const float(*)[3])CustomData_get_layer_named( + &curves->geometry.point_data, CD_PROP_FLOAT3, "position"); + rna_iterator_array_begin( + iter, (void *)positions, sizeof(float[3]), curves->geometry.point_num, false, NULL); } static int rna_CurvePoint_index_get(PointerRNA *ptr) @@ -85,21 +102,23 @@ static void rna_CurvePoint_location_set(PointerRNA *ptr, const float value[3]) static float rna_CurvePoint_radius_get(PointerRNA *ptr) { const Curves *curves = rna_curves(ptr); - if (curves->geometry.radius == NULL) { + const float *radii = (const float *)CustomData_get_layer_named( + &curves->geometry.point_data, CD_PROP_FLOAT, "radius"); + if (radii == NULL) { return 0.0f; } - const float(*co)[3] = ptr->data; - return curves->geometry.radius[co - curves->geometry.position]; + return radii[rna_CurvePoint_index_get_const(ptr)]; } static void rna_CurvePoint_radius_set(PointerRNA *ptr, float value) { const Curves *curves = rna_curves(ptr); - if (curves->geometry.radius == NULL) { + float *radii = (float *)CustomData_get_layer_named( + &curves->geometry.point_data, CD_PROP_FLOAT, "radius"); + if (radii == NULL) { return; } - const float(*co)[3] = ptr->data; - curves->geometry.radius[co - curves->geometry.position] = value; + radii[rna_CurvePoint_index_get_const(ptr)] = value; } static char *rna_CurvePoint_path(const PointerRNA *ptr) @@ -123,16 +142,6 @@ static char *rna_CurveSlice_path(const PointerRNA *ptr) return BLI_sprintfN("curves[%d]", rna_CurveSlice_index_get_const(ptr)); } -static void rna_CurveSlice_points_begin(CollectionPropertyIterator *iter, PointerRNA *ptr) -{ - Curves *curves = rna_curves(ptr); - const int *offset_ptr = (int *)ptr->data; - const int offset = *offset_ptr; - const int size = *(offset_ptr + 1) - offset; - float(*co)[3] = curves->geometry.position + *offset_ptr; - rna_iterator_array_begin(iter, co, sizeof(float[3]), size, 0, NULL); -} - static int rna_CurveSlice_first_point_index_get(PointerRNA *ptr) { const int *offset_ptr = (int *)ptr->data; @@ -146,6 +155,17 @@ static int rna_CurveSlice_points_length_get(PointerRNA *ptr) return *(offset_ptr + 1) - offset; } +static void rna_CurveSlice_points_begin(CollectionPropertyIterator *iter, PointerRNA *ptr) +{ + Curves *curves = rna_curves(ptr); + const int offset = rna_CurveSlice_first_point_index_get(ptr); + const int size = rna_CurveSlice_points_length_get(ptr); + float(*positions)[3] = (float(*)[3])CustomData_get_layer_named( + &curves->geometry.point_data, CD_PROP_FLOAT3, "position"); + float(*co)[3] = positions + offset; + rna_iterator_array_begin(iter, co, sizeof(float[3]), size, 0, NULL); +} + static void rna_Curves_update_data(struct Main *UNUSED(bmain), struct Scene *UNUSED(scene), PointerRNA *ptr) @@ -252,20 +272,32 @@ static void rna_def_curves(BlenderRNA *brna) RNA_def_property_struct_type(prop, "CurveSlice"); RNA_def_property_ui_text(prop, "Curves", "All curves in the data-block"); - /* TODO: better solution for (*co)[3] parsing issue. */ - - RNA_define_verify_sdna(0); prop = RNA_def_property(srna, "points", PROP_COLLECTION, PROP_NONE); - RNA_def_property_collection_sdna(prop, NULL, "geometry.position", "geometry.point_num"); RNA_def_property_struct_type(prop, "CurvePoint"); + RNA_def_property_collection_funcs(prop, + "rna_Curves_position_data_begin", + "rna_iterator_array_next", + "rna_iterator_array_end", + "rna_iterator_array_get", + "rna_Curves_position_data_length", + NULL, + NULL, + NULL); RNA_def_property_ui_text(prop, "Points", "Control points of all curves"); - RNA_define_verify_sdna(1); /* Direct access to built-in attributes. */ RNA_define_verify_sdna(0); prop = RNA_def_property(srna, "position_data", PROP_COLLECTION, PROP_NONE); - RNA_def_property_collection_sdna(prop, NULL, "geometry.position", "geometry.point_num"); + RNA_def_property_collection_funcs(prop, + "rna_Curves_position_data_begin", + "rna_iterator_array_next", + "rna_iterator_array_end", + "rna_iterator_array_get", + "rna_Curves_position_data_length", + NULL, + NULL, + NULL); RNA_def_property_struct_type(prop, "FloatVectorAttributeValue"); RNA_def_property_update(prop, 0, "rna_Curves_update_data"); RNA_define_verify_sdna(1); -- cgit v1.2.3 From 410a6efb747f188da30c75074d6bf318b862d5d5 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Tue, 19 Jul 2022 18:06:56 -0500 Subject: Point Cloud: Remove redundant custom data pointers Similar to e9f82d3dc7eebadcc52, but for point clouds instead. Differential Revision: https://developer.blender.org/D15487 --- intern/cycles/blender/pointcloud.cpp | 61 +++++++++++++++++----- source/blender/blenkernel/BKE_pointcloud.h | 1 - source/blender/blenkernel/intern/bvhutils.cc | 11 ++-- .../intern/geometry_component_pointcloud.cc | 5 +- source/blender/blenkernel/intern/mesh_convert.cc | 19 ++++--- source/blender/blenkernel/intern/pointcloud.cc | 48 +++++++++-------- .../draw/intern/draw_cache_impl_pointcloud.c | 13 +++-- .../geometry/intern/point_merge_by_distance.cc | 14 ++--- .../blender/geometry/intern/realize_instances.cc | 22 +++++--- source/blender/makesdna/DNA_pointcloud_types.h | 4 -- source/blender/makesrna/intern/rna_pointcloud.c | 48 ++++++++++++----- .../nodes/geometry/nodes/node_geo_convex_hull.cc | 11 ++-- .../nodes/node_geo_distribute_points_on_faces.cc | 13 ++++- .../geometry/nodes/node_geo_instances_to_points.cc | 20 ++++--- .../nodes/geometry/nodes/node_geo_transform.cc | 21 ++++---- 15 files changed, 205 insertions(+), 106 deletions(-) diff --git a/intern/cycles/blender/pointcloud.cpp b/intern/cycles/blender/pointcloud.cpp index 0312ad87a70..b4e90859877 100644 --- a/intern/cycles/blender/pointcloud.cpp +++ b/intern/cycles/blender/pointcloud.cpp @@ -1,8 +1,10 @@ /* SPDX-License-Identifier: Apache-2.0 * Copyright 2011-2022 Blender Foundation */ -#include "scene/pointcloud.h" +#include + #include "scene/attribute.h" +#include "scene/pointcloud.h" #include "scene/scene.h" #include "blender/sync.h" @@ -138,6 +140,36 @@ static void copy_attributes(PointCloud *pointcloud, } } +static std::optional find_radius_attribute(BL::PointCloud b_pointcloud) +{ + for (BL::Attribute &b_attribute : b_pointcloud.attributes) { + if (b_attribute.name() != "radius") { + continue; + } + if (b_attribute.data_type() != BL::Attribute::data_type_FLOAT) { + continue; + } + return BL::FloatAttribute{b_attribute}; + } + return std::nullopt; +} + +static BL::FloatVectorAttribute find_position_attribute(BL::PointCloud b_pointcloud) +{ + for (BL::Attribute &b_attribute : b_pointcloud.attributes) { + if (b_attribute.name() != "position") { + continue; + } + if (b_attribute.data_type() != BL::Attribute::data_type_FLOAT_VECTOR) { + continue; + } + return BL::FloatVectorAttribute{b_attribute}; + } + /* The position attribute must exist. */ + assert(false); + return BL::FloatVectorAttribute{b_pointcloud.attributes[0]}; +} + static void export_pointcloud(Scene *scene, PointCloud *pointcloud, BL::PointCloud b_pointcloud, @@ -156,18 +188,18 @@ static void export_pointcloud(Scene *scene, const int num_points = b_pointcloud.points.length(); pointcloud->reserve(num_points); + BL::FloatVectorAttribute b_attr_position = find_position_attribute(b_pointcloud); + std::optional b_attr_radius = find_radius_attribute(b_pointcloud); + /* Export points. */ - BL::PointCloud::points_iterator b_point_iter; - for (b_pointcloud.points.begin(b_point_iter); b_point_iter != b_pointcloud.points.end(); - ++b_point_iter) { - BL::Point b_point = *b_point_iter; - const float3 co = get_float3(b_point.co()); - const float radius = b_point.radius(); + for (int i = 0; i < num_points; i++) { + const float3 co = get_float3(b_attr_position.data[i].vector()); + const float radius = b_attr_radius ? b_attr_radius->data[i].value() : 0.0f; pointcloud->add_point(co, radius); /* Random number per point. */ if (attr_random != NULL) { - attr_random->add(hash_uint2_to_float(b_point.index(), 0)); + attr_random->add(hash_uint2_to_float(i, 0)); } } @@ -195,14 +227,15 @@ static void export_pointcloud_motion(PointCloud *pointcloud, int num_motion_points = 0; const array &pointcloud_points = pointcloud->get_points(); - BL::PointCloud::points_iterator b_point_iter; - for (b_pointcloud.points.begin(b_point_iter); b_point_iter != b_pointcloud.points.end(); - ++b_point_iter) { - BL::Point b_point = *b_point_iter; + BL::FloatVectorAttribute b_attr_position = find_position_attribute(b_pointcloud); + std::optional b_attr_radius = find_radius_attribute(b_pointcloud); + for (int i = 0; i < num_points; i++) { if (num_motion_points < num_points) { - float3 P = get_float3(b_point.co()); - P.w = b_point.radius(); + const float3 co = get_float3(b_attr_position.data[i].vector()); + const float radius = b_attr_radius ? b_attr_radius->data[i].value() : 0.0f; + float3 P = co; + P.w = radius; mP[num_motion_points] = P; have_motion = have_motion || (P != pointcloud_points[num_motion_points]); num_motion_points++; diff --git a/source/blender/blenkernel/BKE_pointcloud.h b/source/blender/blenkernel/BKE_pointcloud.h index 6dbba11a56d..ee90fea6506 100644 --- a/source/blender/blenkernel/BKE_pointcloud.h +++ b/source/blender/blenkernel/BKE_pointcloud.h @@ -29,7 +29,6 @@ struct PointCloud *BKE_pointcloud_new_nomain(int totpoint); struct BoundBox *BKE_pointcloud_boundbox_get(struct Object *ob); bool BKE_pointcloud_minmax(const struct PointCloud *pointcloud, float r_min[3], float r_max[3]); -void BKE_pointcloud_update_customdata_pointers(struct PointCloud *pointcloud); bool BKE_pointcloud_customdata_required(const struct PointCloud *pointcloud, const char *name); /* Dependency Graph */ diff --git a/source/blender/blenkernel/intern/bvhutils.cc b/source/blender/blenkernel/intern/bvhutils.cc index 35c2039634a..03dd5c89b70 100644 --- a/source/blender/blenkernel/intern/bvhutils.cc +++ b/source/blender/blenkernel/intern/bvhutils.cc @@ -19,6 +19,7 @@ #include "BLI_threads.h" #include "BLI_utildefines.h" +#include "BKE_attribute.hh" #include "BKE_bvhutils.h" #include "BKE_editmesh.h" #include "BKE_mesh.h" @@ -1430,13 +1431,17 @@ BVHTree *BKE_bvhtree_from_pointcloud_get(BVHTreeFromPointCloud *data, return nullptr; } - for (int i = 0; i < pointcloud->totpoint; i++) { - BLI_bvhtree_insert(tree, i, pointcloud->co[i], 1); + blender::bke::AttributeAccessor attributes = blender::bke::pointcloud_attributes(*pointcloud); + blender::VArraySpan positions = attributes.lookup_or_default( + "position", ATTR_DOMAIN_POINT, blender::float3(0)); + + for (const int i : positions.index_range()) { + BLI_bvhtree_insert(tree, i, positions[i], 1); } BLI_assert(BLI_bvhtree_get_len(tree) == pointcloud->totpoint); bvhtree_balance(tree, false); - data->coords = pointcloud->co; + data->coords = (const float(*)[3])positions.data(); data->tree = tree; data->nearest_callback = nullptr; diff --git a/source/blender/blenkernel/intern/geometry_component_pointcloud.cc b/source/blender/blenkernel/intern/geometry_component_pointcloud.cc index ccc97f92dbc..4953da8a5ee 100644 --- a/source/blender/blenkernel/intern/geometry_component_pointcloud.cc +++ b/source/blender/blenkernel/intern/geometry_component_pointcloud.cc @@ -111,10 +111,7 @@ namespace blender::bke { */ static ComponentAttributeProviders create_attribute_providers_for_point_cloud() { - static auto update_custom_data_pointers = [](void *owner) { - PointCloud *pointcloud = static_cast(owner); - BKE_pointcloud_update_customdata_pointers(pointcloud); - }; + static auto update_custom_data_pointers = [](void * /*owner*/) {}; static CustomDataAccessInfo point_access = { [](void *owner) -> CustomData * { PointCloud *pointcloud = static_cast(owner); diff --git a/source/blender/blenkernel/intern/mesh_convert.cc b/source/blender/blenkernel/intern/mesh_convert.cc index 7ebb3e25fd4..923d2703960 100644 --- a/source/blender/blenkernel/intern/mesh_convert.cc +++ b/source/blender/blenkernel/intern/mesh_convert.cc @@ -751,6 +751,8 @@ void BKE_mesh_to_curve(Main *bmain, Depsgraph *depsgraph, Scene *UNUSED(scene), void BKE_pointcloud_from_mesh(Mesh *me, PointCloud *pointcloud) { + using namespace blender; + BLI_assert(me != nullptr); pointcloud->totpoint = me->totvert; @@ -758,14 +760,17 @@ void BKE_pointcloud_from_mesh(Mesh *me, PointCloud *pointcloud) /* Copy over all attributes. */ CustomData_merge(&me->vdata, &pointcloud->pdata, CD_MASK_PROP_ALL, CD_DUPLICATE, me->totvert); - BKE_pointcloud_update_customdata_pointers(pointcloud); - CustomData_update_typemap(&pointcloud->pdata); - MVert *mvert; - mvert = me->mvert; - for (int i = 0; i < me->totvert; i++, mvert++) { - copy_v3_v3(pointcloud->co[i], mvert->co); - } + bke::AttributeAccessor mesh_attributes = bke::mesh_attributes(*me); + bke::MutableAttributeAccessor point_attributes = bke::pointcloud_attributes_for_write( + *pointcloud); + + const VArray mesh_positions = mesh_attributes.lookup_or_default( + "position", ATTR_DOMAIN_POINT, float3(0)); + bke::SpanAttributeWriter point_positions = + point_attributes.lookup_or_add_for_write_only_span("position", ATTR_DOMAIN_POINT); + mesh_positions.materialize(point_positions.span); + point_positions.finish(); } void BKE_mesh_to_pointcloud(Main *bmain, Depsgraph *depsgraph, Scene *UNUSED(scene), Object *ob) diff --git a/source/blender/blenkernel/intern/pointcloud.cc b/source/blender/blenkernel/intern/pointcloud.cc index e38c20d8eb7..261b053e4f9 100644 --- a/source/blender/blenkernel/intern/pointcloud.cc +++ b/source/blender/blenkernel/intern/pointcloud.cc @@ -68,7 +68,6 @@ static void pointcloud_init_data(ID *id) nullptr, pointcloud->totpoint, POINTCLOUD_ATTR_POSITION); - BKE_pointcloud_update_customdata_pointers(pointcloud); } static void pointcloud_copy_data(Main *UNUSED(bmain), ID *id_dst, const ID *id_src, const int flag) @@ -83,7 +82,6 @@ static void pointcloud_copy_data(Main *UNUSED(bmain), ID *id_dst, const ID *id_s CD_MASK_ALL, alloc_type, pointcloud_dst->totpoint); - BKE_pointcloud_update_customdata_pointers(pointcloud_dst); pointcloud_dst->batch_cache = nullptr; } @@ -138,7 +136,6 @@ static void pointcloud_blend_read_data(BlendDataReader *reader, ID *id) /* Geometry */ CustomData_blend_read(reader, &pointcloud->pdata, pointcloud->totpoint); - BKE_pointcloud_update_customdata_pointers(pointcloud); /* Materials */ BLO_read_pointer_array(reader, (void **)&pointcloud->mat); @@ -194,17 +191,28 @@ static void pointcloud_random(PointCloud *pointcloud) { pointcloud->totpoint = 400; CustomData_realloc(&pointcloud->pdata, pointcloud->totpoint); - BKE_pointcloud_update_customdata_pointers(pointcloud); RNG *rng = BLI_rng_new(0); - for (int i = 0; i < pointcloud->totpoint; i++) { - pointcloud->co[i][0] = 2.0f * BLI_rng_get_float(rng) - 1.0f; - pointcloud->co[i][1] = 2.0f * BLI_rng_get_float(rng) - 1.0f; - pointcloud->co[i][2] = 2.0f * BLI_rng_get_float(rng) - 1.0f; - pointcloud->radius[i] = 0.05f * BLI_rng_get_float(rng); + blender::bke::MutableAttributeAccessor attributes = + blender::bke::pointcloud_attributes_for_write(*pointcloud); + blender::bke::SpanAttributeWriter positions = + attributes.lookup_or_add_for_write_only_span(POINTCLOUD_ATTR_POSITION, + ATTR_DOMAIN_POINT); + blender::bke::SpanAttributeWriter radii = + attributes.lookup_or_add_for_write_only_span(POINTCLOUD_ATTR_RADIUS, + ATTR_DOMAIN_POINT); + + for (const int i : positions.span.index_range()) { + positions.span[i] = + float3(BLI_rng_get_float(rng), BLI_rng_get_float(rng), BLI_rng_get_float(rng)) * 2.0f - + 1.0f; + radii.span[i] = 0.05f * BLI_rng_get_float(rng); } + positions.finish(); + radii.finish(); + BLI_rng_free(rng); } @@ -250,7 +258,6 @@ PointCloud *BKE_pointcloud_new_nomain(const int totpoint) pointcloud->totpoint = totpoint; CustomData_realloc(&pointcloud->pdata, pointcloud->totpoint); - BKE_pointcloud_update_customdata_pointers(pointcloud); return pointcloud; } @@ -258,10 +265,14 @@ PointCloud *BKE_pointcloud_new_nomain(const int totpoint) static std::optional> point_cloud_bounds( const PointCloud &pointcloud) { - Span positions{reinterpret_cast(pointcloud.co), pointcloud.totpoint}; - if (pointcloud.radius) { - Span radii{pointcloud.radius, pointcloud.totpoint}; - return blender::bounds::min_max_with_radii(positions, radii); + blender::bke::AttributeAccessor attributes = blender::bke::pointcloud_attributes(pointcloud); + blender::VArraySpan positions = attributes.lookup_or_default( + POINTCLOUD_ATTR_POSITION, ATTR_DOMAIN_POINT, float3(0)); + blender::VArray radii = attributes.lookup_or_default( + POINTCLOUD_ATTR_RADIUS, ATTR_DOMAIN_POINT, 0.0f); + + if (!(radii.is_single() && radii.get_internal_single() == 0.0f)) { + return blender::bounds::min_max_with_radii(positions, radii.get_internal_span()); } return blender::bounds::min_max(positions); } @@ -307,14 +318,6 @@ BoundBox *BKE_pointcloud_boundbox_get(Object *ob) return ob->runtime.bb; } -void BKE_pointcloud_update_customdata_pointers(PointCloud *pointcloud) -{ - pointcloud->co = static_cast( - CustomData_get_layer_named(&pointcloud->pdata, CD_PROP_FLOAT3, POINTCLOUD_ATTR_POSITION)); - pointcloud->radius = static_cast( - CustomData_get_layer_named(&pointcloud->pdata, CD_PROP_FLOAT, POINTCLOUD_ATTR_RADIUS)); -} - bool BKE_pointcloud_customdata_required(const PointCloud *UNUSED(pointcloud), const char *name) { return STREQ(name, POINTCLOUD_ATTR_POSITION); @@ -334,7 +337,6 @@ PointCloud *BKE_pointcloud_new_for_eval(const PointCloud *pointcloud_src, int to pointcloud_dst->totpoint = totpoint; CustomData_copy( &pointcloud_src->pdata, &pointcloud_dst->pdata, CD_MASK_ALL, CD_CALLOC, totpoint); - BKE_pointcloud_update_customdata_pointers(pointcloud_dst); return pointcloud_dst; } diff --git a/source/blender/draw/intern/draw_cache_impl_pointcloud.c b/source/blender/draw/intern/draw_cache_impl_pointcloud.c index d715899e291..55d0eee00e5 100644 --- a/source/blender/draw/intern/draw_cache_impl_pointcloud.c +++ b/source/blender/draw/intern/draw_cache_impl_pointcloud.c @@ -18,6 +18,7 @@ #include "DNA_object_types.h" #include "DNA_pointcloud_types.h" +#include "BKE_customdata.h" #include "BKE_pointcloud.h" #include "GPU_batch.h" @@ -139,7 +140,11 @@ static void pointcloud_batch_cache_ensure_pos(Object *ob, PointCloudBatchCache * } PointCloud *pointcloud = ob->data; - const bool has_radius = pointcloud->radius != NULL; + 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}; @@ -162,14 +167,14 @@ static void pointcloud_batch_cache_ensure_pos(Object *ob, PointCloudBatchCache * 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], pointcloud->co[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] = pointcloud->radius[i] * 100.0f; + vbo_data[i][3] = radii[i] * 100.0f; } } else { - GPU_vertbuf_attr_fill(cache->pos, pos, pointcloud->co); + GPU_vertbuf_attr_fill(cache->pos, pos, positions); } } diff --git a/source/blender/geometry/intern/point_merge_by_distance.cc b/source/blender/geometry/intern/point_merge_by_distance.cc index 40bc02cbd34..ac10f4ef00c 100644 --- a/source/blender/geometry/intern/point_merge_by_distance.cc +++ b/source/blender/geometry/intern/point_merge_by_distance.cc @@ -18,8 +18,10 @@ PointCloud *point_merge_by_distance(const PointCloudComponent &src_points, const IndexMask selection) { const PointCloud &src_pointcloud = *src_points.get_for_read(); - const int src_size = src_pointcloud.totpoint; - Span positions{reinterpret_cast(src_pointcloud.co), src_size}; + bke::AttributeAccessor attributes = bke::pointcloud_attributes(src_pointcloud); + VArraySpan positions = attributes.lookup_or_default( + "position", ATTR_DOMAIN_POINT, float3(0)); + const int src_size = positions.size(); /* Create the KD tree based on only the selected points, to speed up merge detection and * balancing. */ @@ -106,10 +108,10 @@ PointCloud *point_merge_by_distance(const PointCloudComponent &src_points, const bke::AttributeAccessor src_attributes = *src_points.attributes(); bke::MutableAttributeAccessor dst_attributes = *dst_points.attributes_for_write(); - Set attributes = src_attributes.all_ids(); + Set attribute_ids = src_attributes.all_ids(); /* Transfer the ID attribute if it exists, using the ID of the first merged point. */ - if (attributes.contains("id")) { + if (attribute_ids.contains("id")) { VArraySpan src = src_attributes.lookup_or_default("id", ATTR_DOMAIN_POINT, 0); bke::SpanAttributeWriter dst = dst_attributes.lookup_or_add_for_write_only_span( "id", ATTR_DOMAIN_POINT); @@ -122,11 +124,11 @@ PointCloud *point_merge_by_distance(const PointCloudComponent &src_points, }); dst.finish(); - attributes.remove_contained("id"); + attribute_ids.remove_contained("id"); } /* Transfer all other attributes. */ - for (const bke::AttributeIDRef &id : attributes) { + for (const bke::AttributeIDRef &id : attribute_ids) { if (!id.should_be_kept()) { continue; } diff --git a/source/blender/geometry/intern/realize_instances.cc b/source/blender/geometry/intern/realize_instances.cc index 25bcead09b4..0544f304283 100644 --- a/source/blender/geometry/intern/realize_instances.cc +++ b/source/blender/geometry/intern/realize_instances.cc @@ -69,6 +69,7 @@ struct PointCloudRealizeInfo { /** Matches the order stored in #AllPointCloudsInfo.attributes. */ Array> attributes; /** Id attribute on the point cloud. If there are no ids, this #Span is empty. */ + Span positions; Span stored_ids; }; @@ -665,6 +666,9 @@ static AllPointCloudsInfo preprocess_pointclouds(const GeometrySet &geometry_set pointcloud_info.stored_ids = ids_attribute.varray.get_internal_span().typed(); } } + const VArray position_attribute = attributes.lookup_or_default( + "position", ATTR_DOMAIN_POINT, float3(0)); + pointcloud_info.positions = position_attribute.get_internal_span(); } return info; } @@ -673,18 +677,16 @@ static void execute_realize_pointcloud_task( const RealizeInstancesOptions &options, const RealizePointCloudTask &task, const OrderedAttributes &ordered_attributes, - PointCloud &dst_pointcloud, MutableSpan dst_attribute_writers, - MutableSpan all_dst_ids) + MutableSpan all_dst_ids, + MutableSpan all_dst_positions) { const PointCloudRealizeInfo &pointcloud_info = *task.pointcloud_info; const PointCloud &pointcloud = *pointcloud_info.pointcloud; - const Span src_positions{(float3 *)pointcloud.co, pointcloud.totpoint}; const IndexRange point_slice{task.start_index, pointcloud.totpoint}; - MutableSpan dst_positions{(float3 *)dst_pointcloud.co + task.start_index, - pointcloud.totpoint}; - copy_transformed_positions(src_positions, task.transform, dst_positions); + copy_transformed_positions( + pointcloud_info.positions, task.transform, all_dst_positions.slice(point_slice)); /* Create point ids. */ if (!all_dst_ids.is_empty()) { @@ -726,6 +728,9 @@ static void execute_realize_pointcloud_tasks(const RealizeInstancesOptions &opti bke::MutableAttributeAccessor dst_attributes = bke::pointcloud_attributes_for_write( *dst_pointcloud); + SpanAttributeWriter positions = dst_attributes.lookup_or_add_for_write_only_span( + "position", ATTR_DOMAIN_POINT); + /* Prepare id attribute. */ SpanAttributeWriter point_ids; if (all_pointclouds_info.create_id_attribute) { @@ -748,9 +753,9 @@ static void execute_realize_pointcloud_tasks(const RealizeInstancesOptions &opti execute_realize_pointcloud_task(options, task, ordered_attributes, - *dst_pointcloud, dst_attribute_writers, - point_ids.span); + point_ids.span, + positions.span); } }); @@ -758,6 +763,7 @@ static void execute_realize_pointcloud_tasks(const RealizeInstancesOptions &opti for (GSpanAttributeWriter &dst_attribute : dst_attribute_writers) { dst_attribute.finish(); } + positions.finish(); if (point_ids) { point_ids.finish(); } diff --git a/source/blender/makesdna/DNA_pointcloud_types.h b/source/blender/makesdna/DNA_pointcloud_types.h index 1cb490190c3..ee829ebcf6e 100644 --- a/source/blender/makesdna/DNA_pointcloud_types.h +++ b/source/blender/makesdna/DNA_pointcloud_types.h @@ -18,13 +18,9 @@ typedef struct PointCloud { struct AnimData *adt; /* animation data (must be immediately after id) */ int flag; - int _pad1[1]; /* Geometry */ - float (*co)[3]; - float *radius; int totpoint; - int _pad2[1]; /* Custom Data */ struct CustomData pdata; diff --git a/source/blender/makesrna/intern/rna_pointcloud.c b/source/blender/makesrna/intern/rna_pointcloud.c index 4c5dcd5a587..df09bff1aea 100644 --- a/source/blender/makesrna/intern/rna_pointcloud.c +++ b/source/blender/makesrna/intern/rna_pointcloud.c @@ -20,6 +20,7 @@ # include "BLI_math_vector.h" +# include "BKE_customdata.h" # include "BKE_pointcloud.h" # include "DEG_depsgraph.h" @@ -36,7 +37,9 @@ static int rna_Point_index_get_const(const PointerRNA *ptr) { const PointCloud *pointcloud = rna_pointcloud(ptr); const float(*co)[3] = ptr->data; - return (int)(co - pointcloud->co); + const float(*positions)[3] = (const float(*)[3])CustomData_get_layer_named( + &pointcloud->pdata, CD_PROP_FLOAT3, "position"); + return (int)(co - positions); } static int rna_Point_index_get(PointerRNA *ptr) @@ -44,6 +47,21 @@ static int rna_Point_index_get(PointerRNA *ptr) return rna_Point_index_get_const(ptr); } +static int rna_PointCloud_points_length(PointerRNA *ptr) +{ + const PointCloud *pointcloud = rna_pointcloud(ptr); + return pointcloud->totpoint; +} + +static void rna_PointCloud_points_begin(CollectionPropertyIterator *iter, PointerRNA *ptr) +{ + const PointCloud *pointcloud = rna_pointcloud(ptr); + const float(*positions)[3] = (const float(*)[3])CustomData_get_layer_named( + &pointcloud->pdata, CD_PROP_FLOAT3, "position"); + rna_iterator_array_begin( + iter, (void *)positions, sizeof(float[3]), pointcloud->totpoint, false, NULL); +} + static void rna_Point_location_get(PointerRNA *ptr, float value[3]) { copy_v3_v3(value, (const float *)ptr->data); @@ -57,21 +75,22 @@ static void rna_Point_location_set(PointerRNA *ptr, const float value[3]) static float rna_Point_radius_get(PointerRNA *ptr) { const PointCloud *pointcloud = rna_pointcloud(ptr); - if (pointcloud->radius == NULL) { + const float *radii = (const float *)CustomData_get_layer_named( + &pointcloud->pdata, CD_PROP_FLOAT, "radius"); + if (radii == NULL) { return 0.0f; } - const float(*co)[3] = ptr->data; - return pointcloud->radius[co - pointcloud->co]; + return radii[rna_Point_index_get_const(ptr)]; } static void rna_Point_radius_set(PointerRNA *ptr, float value) { - const PointCloud *pointcloud = rna_pointcloud(ptr); - if (pointcloud->radius == NULL) { + PointCloud *pointcloud = rna_pointcloud(ptr); + float *radii = (float *)CustomData_get_layer_named(&pointcloud->pdata, CD_PROP_FLOAT, "radius"); + if (radii == NULL) { return; } - const float(*co)[3] = ptr->data; - pointcloud->radius[co - pointcloud->co] = value; + radii[rna_Point_index_get_const(ptr)] = value; } static char *rna_Point_path(const PointerRNA *ptr) @@ -130,13 +149,18 @@ static void rna_def_pointcloud(BlenderRNA *brna) RNA_def_struct_ui_icon(srna, ICON_POINTCLOUD_DATA); /* geometry */ - /* TODO: better solution for (*co)[3] parsing issue. */ - RNA_define_verify_sdna(0); prop = RNA_def_property(srna, "points", PROP_COLLECTION, PROP_NONE); - RNA_def_property_collection_sdna(prop, NULL, "co", "totpoint"); RNA_def_property_struct_type(prop, "Point"); + RNA_def_property_collection_funcs(prop, + "rna_PointCloud_points_begin", + "rna_iterator_array_next", + "rna_iterator_array_end", + "rna_iterator_array_get", + "rna_PointCloud_points_length", + NULL, + NULL, + NULL); RNA_def_property_ui_text(prop, "Points", ""); - RNA_define_verify_sdna(1); /* materials */ prop = RNA_def_property(srna, "materials", PROP_COLLECTION, PROP_NONE); diff --git a/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc b/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc index 3394a7cad62..3d0bc9cd462 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc @@ -149,12 +149,17 @@ static Mesh *compute_hull(const GeometrySet &geometry_set) if (geometry_set.has_pointcloud()) { count++; - span_count++; const PointCloudComponent *component = geometry_set.get_component_for_read(); const PointCloud *pointcloud = component->get_for_read(); - positions_span = {reinterpret_cast(pointcloud->co), pointcloud->totpoint}; - total_num += pointcloud->totpoint; + const bke::AttributeAccessor attributes = bke::pointcloud_attributes(*pointcloud); + const VArray positions = attributes.lookup_or_default( + "position", ATTR_DOMAIN_POINT, float3(0)); + if (positions.is_span()) { + span_count++; + positions_span = positions.get_internal_span(); + } + total_num += positions.size(); } if (geometry_set.has_curves()) { 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 faf5b7f65fa..44793926bbd 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 @@ -497,8 +497,17 @@ static void point_distribution_calculate(GeometrySet &geometry_set, } PointCloud *pointcloud = BKE_pointcloud_new_nomain(positions.size()); - memcpy(pointcloud->co, positions.data(), sizeof(float3) * positions.size()); - uninitialized_fill_n(pointcloud->radius, pointcloud->totpoint, 0.05f); + bke::MutableAttributeAccessor point_attributes = bke::pointcloud_attributes_for_write( + *pointcloud); + bke::SpanAttributeWriter point_positions = + point_attributes.lookup_or_add_for_write_only_span("position", ATTR_DOMAIN_POINT); + bke::SpanAttributeWriter point_radii = + point_attributes.lookup_or_add_for_write_only_span("radius", ATTR_DOMAIN_POINT); + point_positions.span.copy_from(positions); + point_radii.span.fill(0.05f); + point_positions.finish(); + point_radii.finish(); + geometry_set.replace_pointcloud(pointcloud); PointCloudComponent &point_component = diff --git a/source/blender/nodes/geometry/nodes/node_geo_instances_to_points.cc b/source/blender/nodes/geometry/nodes/node_geo_instances_to_points.cc index edbe6e1593f..b3f04186c63 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_instances_to_points.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_instances_to_points.cc @@ -51,16 +51,24 @@ static void convert_instances_to_points(GeometrySet &geometry_set, if (selection.is_empty()) { return; } + const VArray &positions = evaluator.get_evaluated(0); + const VArray radii = evaluator.get_evaluated(1); PointCloud *pointcloud = BKE_pointcloud_new_nomain(selection.size()); geometry_set.replace_pointcloud(pointcloud); - PointCloudComponent &points = geometry_set.get_component_for_write(); + bke::MutableAttributeAccessor point_attributes = bke::pointcloud_attributes_for_write( + *pointcloud); - const VArray &positions = evaluator.get_evaluated(0); - copy_attribute_to_points(positions, selection, {(float3 *)pointcloud->co, pointcloud->totpoint}); - const VArray radii = evaluator.get_evaluated(1); - copy_attribute_to_points(radii, selection, {pointcloud->radius, pointcloud->totpoint}); + bke::SpanAttributeWriter point_positions = + point_attributes.lookup_or_add_for_write_only_span("position", ATTR_DOMAIN_POINT); + bke::SpanAttributeWriter point_radii = + point_attributes.lookup_or_add_for_write_only_span("radius", ATTR_DOMAIN_POINT); + + copy_attribute_to_points(positions, selection, point_positions.span); + copy_attribute_to_points(radii, selection, point_radii.span); + point_positions.finish(); + point_radii.finish(); Map attributes_to_propagate; geometry_set.gather_attributes_for_propagation({GEO_COMPONENT_TYPE_INSTANCES}, @@ -78,7 +86,7 @@ static void convert_instances_to_points(GeometrySet &geometry_set, const GVArray src = instances.attributes()->lookup_or_default( attribute_id, ATTR_DOMAIN_INSTANCE, attribute_kind.data_type); BLI_assert(src); - GSpanAttributeWriter dst = points.attributes_for_write()->lookup_or_add_for_write_only_span( + GSpanAttributeWriter dst = point_attributes.lookup_or_add_for_write_only_span( attribute_id, ATTR_DOMAIN_POINT, attribute_kind.data_type); BLI_assert(dst); diff --git a/source/blender/nodes/geometry/nodes/node_geo_transform.cc b/source/blender/nodes/geometry/nodes/node_geo_transform.cc index e95db205920..8e65e73d1e2 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_transform.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_transform.cc @@ -47,21 +47,24 @@ static void transform_mesh(Mesh &mesh, const float4x4 &transform) static void translate_pointcloud(PointCloud &pointcloud, const float3 translation) { - CustomData_duplicate_referenced_layer(&pointcloud.pdata, CD_PROP_FLOAT3, pointcloud.totpoint); - BKE_pointcloud_update_customdata_pointers(&pointcloud); - for (const int i : IndexRange(pointcloud.totpoint)) { - add_v3_v3(pointcloud.co[i], translation); + MutableAttributeAccessor attributes = bke::pointcloud_attributes_for_write(pointcloud); + SpanAttributeWriter position = attributes.lookup_or_add_for_write_span( + "position", ATTR_DOMAIN_POINT); + for (const int i : position.span.index_range()) { + position.span[i] += translation; } + position.finish(); } static void transform_pointcloud(PointCloud &pointcloud, const float4x4 &transform) { - CustomData_duplicate_referenced_layer(&pointcloud.pdata, CD_PROP_FLOAT3, pointcloud.totpoint); - BKE_pointcloud_update_customdata_pointers(&pointcloud); - for (const int i : IndexRange(pointcloud.totpoint)) { - float3 &co = *(float3 *)pointcloud.co[i]; - co = transform * co; + MutableAttributeAccessor attributes = bke::pointcloud_attributes_for_write(pointcloud); + SpanAttributeWriter position = attributes.lookup_or_add_for_write_span( + "position", ATTR_DOMAIN_POINT); + for (const int i : position.span.index_range()) { + position.span[i] = transform * position.span[i]; } + position.finish(); } static void translate_instances(InstancesComponent &instances, const float3 translation) -- cgit v1.2.3 From 40ffb94ab47c29cf989d561922e1970e89a836ca Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Tue, 19 Jul 2022 18:16:12 -0500 Subject: Cleanup: Use generic utility for copying compressed attribute In the future, `materialize_compressed_to_uninitialized_threaded` could be moved somewhere else and reused. --- .../geometry/nodes/node_geo_instances_to_points.cc | 19 +++---------------- .../geometry/nodes/node_geo_points_to_vertices.cc | 15 +-------------- 2 files changed, 4 insertions(+), 30 deletions(-) diff --git a/source/blender/nodes/geometry/nodes/node_geo_instances_to_points.cc b/source/blender/nodes/geometry/nodes/node_geo_instances_to_points.cc index b3f04186c63..f14329c96da 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_instances_to_points.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_instances_to_points.cc @@ -22,16 +22,6 @@ static void node_declare(NodeDeclarationBuilder &b) b.add_output(N_("Points")); } -template -static void copy_attribute_to_points(const VArray &src, - const IndexMask mask, - MutableSpan dst) -{ - for (const int i : mask.index_range()) { - dst[i] = src[mask[i]]; - } -} - static void convert_instances_to_points(GeometrySet &geometry_set, Field position_field, Field radius_field, @@ -65,8 +55,8 @@ static void convert_instances_to_points(GeometrySet &geometry_set, bke::SpanAttributeWriter point_radii = point_attributes.lookup_or_add_for_write_only_span("radius", ATTR_DOMAIN_POINT); - copy_attribute_to_points(positions, selection, point_positions.span); - copy_attribute_to_points(radii, selection, point_radii.span); + positions.materialize_compressed_to_uninitialized(selection, point_positions.span); + radii.materialize_compressed_to_uninitialized(selection, point_radii.span); point_positions.finish(); point_radii.finish(); @@ -90,10 +80,7 @@ static void convert_instances_to_points(GeometrySet &geometry_set, attribute_id, ATTR_DOMAIN_POINT, attribute_kind.data_type); BLI_assert(dst); - attribute_math::convert_to_static_type(attribute_kind.data_type, [&](auto dummy) { - using T = decltype(dummy); - copy_attribute_to_points(src.typed(), selection, dst.span.typed()); - }); + src.materialize_compressed_to_uninitialized(selection, dst.span.data()); dst.finish(); } } diff --git a/source/blender/nodes/geometry/nodes/node_geo_points_to_vertices.cc b/source/blender/nodes/geometry/nodes/node_geo_points_to_vertices.cc index 9cc64d4bc44..74fff8efeee 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_points_to_vertices.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_points_to_vertices.cc @@ -18,14 +18,6 @@ static void node_declare(NodeDeclarationBuilder &b) b.add_output(N_("Mesh")); } -template -static void copy_attribute_to_vertices(const Span src, const IndexMask mask, MutableSpan dst) -{ - for (const int i : mask.index_range()) { - dst[i] = src[mask[i]]; - } -} - /* One improvement would be to move the attribute arrays directly to the mesh when possible. */ static void geometry_set_points_to_vertices(GeometrySet &geometry_set, Field &selection_field) @@ -66,12 +58,7 @@ static void geometry_set_points_to_vertices(GeometrySet &geometry_set, mesh_component.attributes_for_write()->lookup_or_add_for_write_only_span( attribute_id, ATTR_DOMAIN_POINT, data_type); if (dst && src) { - attribute_math::convert_to_static_type(data_type, [&](auto dummy) { - using T = decltype(dummy); - VArray src_typed = src.typed(); - VArraySpan src_typed_span{src_typed}; - copy_attribute_to_vertices(src_typed_span, selection, dst.span.typed()); - }); + src.materialize_compressed_to_uninitialized(selection, dst.span.data()); dst.finish(); } } -- cgit v1.2.3 From 5d6e4822d85bf4db099e2810555af17962929e49 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Tue, 19 Jul 2022 18:29:23 -0500 Subject: Cleanup: Combine geometry null checks in if statements Testing if components or virtual arrays are null in the same line they are retrieved can make this boilerplate code a bit easier to read. --- .../nodes/node_geo_attribute_domain_size.cc | 28 +++++----- .../nodes/geometry/nodes/node_geo_convex_hull.cc | 61 ++++++++++------------ 2 files changed, 42 insertions(+), 47 deletions(-) diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_domain_size.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_domain_size.cc index 7e4904a7a6a..f6ea6073459 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_domain_size.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_domain_size.cc @@ -65,13 +65,12 @@ static void node_update(bNodeTree *ntree, bNode *node) static void node_geo_exec(GeoNodeExecParams params) { - GeometryComponentType component = (GeometryComponentType)params.node().custom1; - GeometrySet geometry_set = params.extract_input("Geometry"); + const GeometryComponentType component = (GeometryComponentType)params.node().custom1; + const GeometrySet geometry_set = params.extract_input("Geometry"); switch (component) { case GEO_COMPONENT_TYPE_MESH: { - if (geometry_set.has_mesh()) { - const MeshComponent *component = geometry_set.get_component_for_read(); + if (const MeshComponent *component = geometry_set.get_component_for_read()) { const AttributeAccessor attributes = *component->attributes(); params.set_output("Point Count", attributes.domain_size(ATTR_DOMAIN_POINT)); params.set_output("Edge Count", attributes.domain_size(ATTR_DOMAIN_EDGE)); @@ -84,8 +83,8 @@ static void node_geo_exec(GeoNodeExecParams params) break; } case GEO_COMPONENT_TYPE_CURVE: { - if (geometry_set.has_curves()) { - const CurveComponent *component = geometry_set.get_component_for_read(); + if (const CurveComponent *component = + geometry_set.get_component_for_read()) { const AttributeAccessor attributes = *component->attributes(); params.set_output("Point Count", attributes.domain_size(ATTR_DOMAIN_POINT)); params.set_output("Spline Count", attributes.domain_size(ATTR_DOMAIN_CURVE)); @@ -96,10 +95,10 @@ static void node_geo_exec(GeoNodeExecParams params) break; } case GEO_COMPONENT_TYPE_POINT_CLOUD: { - if (geometry_set.has_pointcloud()) { - const PointCloudComponent *component = - geometry_set.get_component_for_read(); - params.set_output("Point Count", component->attributes()->domain_size(ATTR_DOMAIN_POINT)); + if (const PointCloudComponent *component = + geometry_set.get_component_for_read()) { + const AttributeAccessor attributes = *component->attributes(); + params.set_output("Point Count", attributes.domain_size(ATTR_DOMAIN_POINT)); } else { params.set_default_remaining_outputs(); @@ -107,11 +106,10 @@ static void node_geo_exec(GeoNodeExecParams params) break; } case GEO_COMPONENT_TYPE_INSTANCES: { - if (geometry_set.has_instances()) { - const InstancesComponent *component = - geometry_set.get_component_for_read(); - params.set_output("Instance Count", - component->attributes()->domain_size(ATTR_DOMAIN_INSTANCE)); + if (const InstancesComponent *component = + geometry_set.get_component_for_read()) { + const AttributeAccessor attributes = *component->attributes(); + params.set_output("Instance Count", attributes.domain_size(ATTR_DOMAIN_INSTANCE)); } else { params.set_default_remaining_outputs(); diff --git a/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc b/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc index 3d0bc9cd462..7c26ffc2099 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc @@ -140,33 +140,35 @@ static Mesh *compute_hull(const GeometrySet &geometry_set) Span positions_span; - if (geometry_set.has_mesh()) { + if (const MeshComponent *component = geometry_set.get_component_for_read()) { count++; - const MeshComponent *component = geometry_set.get_component_for_read(); - const Mesh *mesh = component->get_for_read(); - total_num += mesh->totvert; + if (const VArray positions = component->attributes()->lookup( + "position", ATTR_DOMAIN_POINT)) { + if (positions.is_span()) { + span_count++; + positions_span = positions.get_internal_span(); + } + total_num += positions.size(); + } } - if (geometry_set.has_pointcloud()) { + if (const PointCloudComponent *component = + geometry_set.get_component_for_read()) { count++; - const PointCloudComponent *component = - geometry_set.get_component_for_read(); - const PointCloud *pointcloud = component->get_for_read(); - const bke::AttributeAccessor attributes = bke::pointcloud_attributes(*pointcloud); - const VArray positions = attributes.lookup_or_default( - "position", ATTR_DOMAIN_POINT, float3(0)); - if (positions.is_span()) { - span_count++; - positions_span = positions.get_internal_span(); + if (const VArray positions = component->attributes()->lookup( + "position", ATTR_DOMAIN_POINT)) { + if (positions.is_span()) { + span_count++; + positions_span = positions.get_internal_span(); + } + total_num += positions.size(); } - total_num += positions.size(); } - if (geometry_set.has_curves()) { + if (const Curves *curves_id = geometry_set.get_curves_for_read()) { count++; span_count++; - const Curves &curves_id = *geometry_set.get_curves_for_read(); - const bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id.geometry); + const bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id->geometry); positions_span = curves.evaluated_positions(); total_num += positions_span.size(); } @@ -184,30 +186,25 @@ static Mesh *compute_hull(const GeometrySet &geometry_set) Array positions(total_num); int offset = 0; - if (geometry_set.has_mesh()) { - const MeshComponent *component = geometry_set.get_component_for_read(); - const VArray varray = component->attributes()->lookup("position", - ATTR_DOMAIN_POINT); - if (varray) { + if (const MeshComponent *component = geometry_set.get_component_for_read()) { + if (const VArray varray = component->attributes()->lookup("position", + ATTR_DOMAIN_POINT)) { varray.materialize(positions.as_mutable_span().slice(offset, varray.size())); offset += varray.size(); } } - if (geometry_set.has_pointcloud()) { - const PointCloudComponent *component = - geometry_set.get_component_for_read(); - const VArray varray = component->attributes()->lookup("position", - ATTR_DOMAIN_POINT); - if (varray) { + if (const PointCloudComponent *component = + geometry_set.get_component_for_read()) { + if (const VArray varray = component->attributes()->lookup("position", + ATTR_DOMAIN_POINT)) { varray.materialize(positions.as_mutable_span().slice(offset, varray.size())); offset += varray.size(); } } - if (geometry_set.has_curves()) { - const Curves &curves_id = *geometry_set.get_curves_for_read(); - const bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id.geometry); + if (const Curves *curves_id = geometry_set.get_curves_for_read()) { + const bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id->geometry); Span array = curves.evaluated_positions(); positions.as_mutable_span().slice(offset, array.size()).copy_from(array); offset += array.size(); -- cgit v1.2.3 From 2551cf908732bab2865654571164d74e8bbad47e Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Tue, 19 Jul 2022 18:50:27 -0500 Subject: Curves: Port fillet node to the new data-block This commit ports the fillet curves node to the new curves data-block, and moves the fillet node implementation to the geometry module to help separate the implementation from the node. The changes are similar to the subdivide node or resample node. I've resused common utilities where it makes sense, though some things like the iteration over attributes can be generalized further. The node is now multi-threaded per-curve and inside each curve, and some buffers are reused per curve to avoid many allocations. The code is more explicit now, and though there is more boilerplate to pass around many spans, the more complex logic should be more readable. Differential Revision: https://developer.blender.org/D15346 --- source/blender/blenkernel/BKE_attribute.hh | 16 + .../blender/blenkernel/intern/attribute_access.cc | 31 ++ source/blender/blenlib/BLI_index_range.hh | 10 + source/blender/blenlib/BLI_math_rotation.hh | 9 + source/blender/blenlib/intern/math_rotation.cc | 13 + .../blender/blenlib/tests/BLI_index_range_test.cc | 6 + source/blender/geometry/CMakeLists.txt | 2 + source/blender/geometry/GEO_fillet_curves.hh | 23 + source/blender/geometry/intern/fillet_curves.cc | 561 +++++++++++++++++++ source/blender/geometry/intern/subdivide_curves.cc | 58 +- .../nodes/geometry/nodes/node_geo_curve_fillet.cc | 600 ++------------------- 11 files changed, 722 insertions(+), 607 deletions(-) create mode 100644 source/blender/geometry/GEO_fillet_curves.hh create mode 100644 source/blender/geometry/intern/fillet_curves.cc diff --git a/source/blender/blenkernel/BKE_attribute.hh b/source/blender/blenkernel/BKE_attribute.hh index 05ab4f1f1f1..108993d91c0 100644 --- a/source/blender/blenkernel/BKE_attribute.hh +++ b/source/blender/blenkernel/BKE_attribute.hh @@ -666,6 +666,22 @@ class MutableAttributeAccessor : public AttributeAccessor { void remove_anonymous(); }; +struct AttributeTransferData { + /* Expect that if an attribute exists, it is stored as a contiguous array internally anyway. */ + GVArraySpan src; + AttributeMetaData meta_data; + bke::GSpanAttributeWriter dst; +}; +/** + * Retrieve attribute arrays and writers for attributes that should be transferred between + * data-blocks of the same type. + */ +Vector retrieve_attributes_for_transfer( + const bke::AttributeAccessor &src_attributes, + bke::MutableAttributeAccessor &dst_attributes, + eAttrDomainMask domain_mask, + const Set &skip = {}); + bool allow_procedural_attribute_access(StringRef attribute_name); extern const char *no_procedural_access_message; diff --git a/source/blender/blenkernel/intern/attribute_access.cc b/source/blender/blenkernel/intern/attribute_access.cc index ac1ee19927c..a834b77d65e 100644 --- a/source/blender/blenkernel/intern/attribute_access.cc +++ b/source/blender/blenkernel/intern/attribute_access.cc @@ -1011,6 +1011,37 @@ GSpanAttributeWriter MutableAttributeAccessor::lookup_or_add_for_write_only_span return {}; } +Vector retrieve_attributes_for_transfer( + const bke::AttributeAccessor &src_attributes, + bke::MutableAttributeAccessor &dst_attributes, + const eAttrDomainMask domain_mask, + const Set &skip) +{ + Vector attributes; + src_attributes.for_all( + [&](const bke::AttributeIDRef &id, const bke::AttributeMetaData meta_data) { + if (!(ATTR_DOMAIN_AS_MASK(meta_data.domain) & domain_mask)) { + return true; + } + if (id.is_named() && skip.contains(id.name())) { + return true; + } + if (!id.should_be_kept()) { + return true; + } + + GVArray src = src_attributes.lookup(id, meta_data.domain); + BLI_assert(src); + bke::GSpanAttributeWriter dst = dst_attributes.lookup_or_add_for_write_only_span( + id, meta_data.domain, meta_data.data_type); + BLI_assert(dst); + attributes.append({std::move(src), meta_data, std::move(dst)}); + + return true; + }); + return attributes; +} + } // namespace blender::bke /** \} */ diff --git a/source/blender/blenlib/BLI_index_range.hh b/source/blender/blenlib/BLI_index_range.hh index bd0a7e5bb7a..6fcc560d856 100644 --- a/source/blender/blenlib/BLI_index_range.hh +++ b/source/blender/blenlib/BLI_index_range.hh @@ -197,6 +197,16 @@ class IndexRange { return start_ + size_ - 1 - n; } + /** + * Get the element one before the beginning. The returned value is undefined when the range is + * empty, and the range must start after zero already. + */ + constexpr int64_t one_before_start() const + { + BLI_assert(start_ > 0); + return start_ - 1; + } + /** * Get the element one after the end. The returned value is undefined when the range is empty. */ diff --git a/source/blender/blenlib/BLI_math_rotation.hh b/source/blender/blenlib/BLI_math_rotation.hh index e8b746b34df..50a062162ad 100644 --- a/source/blender/blenlib/BLI_math_rotation.hh +++ b/source/blender/blenlib/BLI_math_rotation.hh @@ -15,4 +15,13 @@ namespace blender::math { */ float3 rotate_direction_around_axis(const float3 &direction, const float3 &axis, float angle); +/** + * Rotate any arbitrary \a vector around the \a center position, with a unit-length \a axis + * and the specified \a angle. + */ +float3 rotate_around_axis(const float3 &vector, + const float3 ¢er, + const float3 &axis, + float angle); + } // namespace blender::math diff --git a/source/blender/blenlib/intern/math_rotation.cc b/source/blender/blenlib/intern/math_rotation.cc index 74300d55954..091e8af85d9 100644 --- a/source/blender/blenlib/intern/math_rotation.cc +++ b/source/blender/blenlib/intern/math_rotation.cc @@ -23,4 +23,17 @@ float3 rotate_direction_around_axis(const float3 &direction, const float3 &axis, return axis_scaled + diff * std::cos(angle) + cross * std::sin(angle); } +float3 rotate_around_axis(const float3 &vector, + const float3 ¢er, + const float3 &axis, + const float angle) + +{ + float3 result = vector - center; + float mat[3][3]; + axis_angle_normalized_to_mat3(mat, axis, angle); + mul_m3_v3(mat, result); + return result + center; +} + } // namespace blender::math diff --git a/source/blender/blenlib/tests/BLI_index_range_test.cc b/source/blender/blenlib/tests/BLI_index_range_test.cc index 10f6784cd44..f5b994d409a 100644 --- a/source/blender/blenlib/tests/BLI_index_range_test.cc +++ b/source/blender/blenlib/tests/BLI_index_range_test.cc @@ -105,6 +105,12 @@ TEST(index_range, OneAfterEnd) EXPECT_EQ(range.one_after_last(), 8); } +TEST(index_range, OneBeforeStart) +{ + IndexRange range = IndexRange(5, 3); + EXPECT_EQ(range.one_before_start(), 4); +} + TEST(index_range, Start) { IndexRange range = IndexRange(6, 2); diff --git a/source/blender/geometry/CMakeLists.txt b/source/blender/geometry/CMakeLists.txt index df66a806c16..da83d9e8957 100644 --- a/source/blender/geometry/CMakeLists.txt +++ b/source/blender/geometry/CMakeLists.txt @@ -16,6 +16,7 @@ set(INC set(SRC intern/add_curves_on_mesh.cc + intern/fillet_curves.cc intern/mesh_merge_by_distance.cc intern/mesh_primitive_cuboid.cc intern/mesh_to_curve_convert.cc @@ -29,6 +30,7 @@ set(SRC intern/uv_parametrizer.c GEO_add_curves_on_mesh.hh + GEO_fillet_curves.hh GEO_mesh_merge_by_distance.hh GEO_mesh_primitive_cuboid.hh GEO_mesh_to_curve.hh diff --git a/source/blender/geometry/GEO_fillet_curves.hh b/source/blender/geometry/GEO_fillet_curves.hh new file mode 100644 index 00000000000..1f832f8b6cc --- /dev/null +++ b/source/blender/geometry/GEO_fillet_curves.hh @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#include "BLI_function_ref.hh" +#include "BLI_index_mask.hh" + +#include "BKE_curves.hh" + +namespace blender::geometry { + +bke::CurvesGeometry fillet_curves_poly(const bke::CurvesGeometry &src_curves, + IndexMask curve_selection, + const VArray &radius, + const VArray &counts, + bool limit_radius); + +bke::CurvesGeometry fillet_curves_bezier(const bke::CurvesGeometry &src_curves, + IndexMask curve_selection, + const VArray &radius, + bool limit_radius); + +} // namespace blender::geometry diff --git a/source/blender/geometry/intern/fillet_curves.cc b/source/blender/geometry/intern/fillet_curves.cc new file mode 100644 index 00000000000..2cca91f40ae --- /dev/null +++ b/source/blender/geometry/intern/fillet_curves.cc @@ -0,0 +1,561 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "BKE_attribute_math.hh" +#include "BKE_curves.hh" +#include "BKE_curves_utils.hh" +#include "BKE_geometry_set.hh" + +#include "BLI_devirtualize_parameters.hh" +#include "BLI_math_geom.h" +#include "BLI_math_rotation.hh" +#include "BLI_task.hh" + +#include "GEO_fillet_curves.hh" + +namespace blender::geometry { + +/** + * Return a range used to retrieve values from an array of values stored per point, but with an + * extra element at the end of each curve. This is useful for offsets within curves, where it is + * convenient to store the first 0 and have the last offset be the total result curve size. + */ +static IndexRange curve_dst_offsets(const IndexRange points, const int curve_index) +{ + return {curve_index + points.start(), points.size() + 1}; +} + +template +static void threaded_slice_fill(const Span src, const Span offsets, MutableSpan dst) +{ + threading::parallel_for(src.index_range(), 512, [&](IndexRange range) { + for (const int i : range) { + dst.slice(bke::offsets_to_range(offsets, i)).fill(src[i]); + } + }); +} + +template +static void duplicate_fillet_point_data(const bke::CurvesGeometry &src_curves, + const bke::CurvesGeometry &dst_curves, + const IndexMask curve_selection, + const Span point_offsets, + const Span src, + MutableSpan dst) +{ + threading::parallel_for(curve_selection.index_range(), 512, [&](IndexRange range) { + for (const int curve_i : curve_selection.slice(range)) { + const IndexRange src_points = src_curves.points_for_curve(curve_i); + const IndexRange dst_points = dst_curves.points_for_curve(curve_i); + const Span offsets = point_offsets.slice(curve_dst_offsets(src_points, curve_i)); + threaded_slice_fill(src.slice(src_points), offsets, dst.slice(dst_points)); + } + }); +} + +static void duplicate_fillet_point_data(const bke::CurvesGeometry &src_curves, + const bke::CurvesGeometry &dst_curves, + const IndexMask selection, + const Span point_offsets, + const GSpan src, + GMutableSpan dst) +{ + attribute_math::convert_to_static_type(dst.type(), [&](auto dummy) { + using T = decltype(dummy); + duplicate_fillet_point_data( + src_curves, dst_curves, selection, point_offsets, src.typed(), dst.typed()); + }); +} + +static void calculate_result_offsets(const bke::CurvesGeometry &src_curves, + const IndexMask selection, + const Span unselected_ranges, + const VArray &radii, + const VArray &counts, + const Span cyclic, + MutableSpan dst_curve_offsets, + MutableSpan dst_point_offsets) +{ + /* Fill the offsets array with the curve point counts, then accumulate them to form offsets. */ + bke::curves::fill_curve_counts(src_curves, unselected_ranges, dst_curve_offsets); + threading::parallel_for(selection.index_range(), 512, [&](IndexRange range) { + for (const int curve_i : selection.slice(range)) { + const IndexRange src_points = src_curves.points_for_curve(curve_i); + const IndexRange offsets_range = curve_dst_offsets(src_points, curve_i); + + MutableSpan point_offsets = dst_point_offsets.slice(offsets_range); + MutableSpan point_counts = point_offsets.drop_back(1); + + counts.materialize_compressed(src_points, point_counts); + for (int &count : point_counts) { + /* Make sure the number of cuts is greater than zero and add one for the existing point. */ + count = std::max(count, 0) + 1; + } + if (!cyclic[curve_i]) { + /* Endpoints on non-cyclic curves cannot be filleted. */ + point_counts.first() = 1; + point_counts.last() = 1; + } + /* Implicitly "deselect" points with zero radius. */ + devirtualize_varray(radii, [&](const auto radii) { + for (const int i : IndexRange(src_points.size())) { + if (radii[i] == 0.0f) { + point_counts[i] = 1; + } + } + }); + + bke::curves::accumulate_counts_to_offsets(point_offsets); + + dst_curve_offsets[curve_i] = point_offsets.last(); + } + }); + bke::curves::accumulate_counts_to_offsets(dst_curve_offsets); +} + +static void calculate_directions(const Span positions, MutableSpan directions) +{ + for (const int i : positions.index_range().drop_back(1)) { + directions[i] = math::normalize(positions[i + 1] - positions[i]); + } + directions.last() = math::normalize(positions.first() - positions.last()); +} + +static void calculate_angles(const Span directions, MutableSpan angles) +{ + angles.first() = M_PI - angle_v3v3(-directions.last(), directions.first()); + for (const int i : directions.index_range().drop_front(1)) { + angles[i] = M_PI - angle_v3v3(-directions[i - 1], directions[i]); + } +} + +/** + * Find the portion of the previous and next segments used by the current and next point fillets. + * If more than the total length of the segment would be used, scale the current point's radius + * just enough to make the two points meet in the middle. + */ +static float limit_radius(const float3 &position_prev, + const float3 &position, + const float3 &position_next, + const float angle_prev, + const float angle, + const float angle_next, + const float radius_prev, + const float radius, + const float radius_next) +{ + const float displacement = radius * std::tan(angle / 2.0f); + + const float displacement_prev = radius_prev * std::tan(angle_prev / 2.0f); + const float segment_length_prev = math::distance(position, position_prev); + const float total_displacement_prev = displacement_prev + displacement; + const float factor_prev = std::clamp(segment_length_prev / total_displacement_prev, 0.0f, 1.0f); + + const float displacement_next = radius_next * std::tan(angle_next / 2.0f); + const float segment_length_next = math::distance(position, position_next); + const float total_displacement_next = displacement_next + displacement; + const float factor_next = std::clamp(segment_length_next / total_displacement_next, 0.0f, 1.0f); + + return radius * std::min(factor_prev, factor_next); +} + +static void limit_radii(const Span positions, + const Span angles, + const Span radii, + const bool cyclic, + MutableSpan radii_clamped) +{ + if (cyclic) { + /* First point. */ + radii_clamped.first() = limit_radius(positions.last(), + positions.first(), + positions[1], + angles.last(), + angles.first(), + angles[1], + radii.last(), + radii.first(), + radii[1]); + /* All middle points. */ + for (const int i : positions.index_range().drop_back(1).drop_front(1)) { + const int i_prev = i - 1; + const int i_next = i + 1; + radii_clamped[i] = limit_radius(positions[i_prev], + positions[i], + positions[i_next], + angles[i_prev], + angles[i], + angles[i_next], + radii[i_prev], + radii[i], + radii[i_next]); + } + /* Last point. */ + radii_clamped.last() = limit_radius(positions.last(1), + positions.last(), + positions.first(), + angles.last(1), + angles.last(), + angles.first(), + radii.last(1), + radii.last(), + radii.first()); + } + else { + const int i_last = positions.index_range().last(); + /* First point. */ + radii_clamped.first() = 0.0f; + /* All middle points. */ + for (const int i : positions.index_range().drop_back(1).drop_front(1)) { + const int i_prev = i - 1; + const int i_next = i + 1; + /* Use a zero radius for the first and last points, because they don't have fillets. + * This logic could potentially be unrolled, but it doesn't seem worth it. */ + const float radius_prev = i_prev == 0 ? 0.0f : radii[i_prev]; + const float radius_next = i_next == i_last ? 0.0f : radii[i_next]; + radii_clamped[i] = limit_radius(positions[i_prev], + positions[i], + positions[i_next], + angles[i_prev], + angles[i], + angles[i_next], + radius_prev, + radii[i], + radius_next); + } + /* Last point. */ + radii_clamped.last() = 0.0f; + } +} + +static void calculate_fillet_positions(const Span src_positions, + const Span angles, + const Span radii, + const Span directions, + const Span dst_offsets, + MutableSpan dst) +{ + const int i_src_last = src_positions.index_range().last(); + threading::parallel_for(src_positions.index_range(), 512, [&](IndexRange range) { + for (const int i_src : range) { + const IndexRange arc = bke::offsets_to_range(dst_offsets, i_src); + const float3 &src = src_positions[i_src]; + if (arc.size() == 1) { + dst[arc.first()] = src; + continue; + } + + const int i_src_prev = i_src == 0 ? i_src_last : i_src - 1; + const float angle = angles[i_src]; + const float radius = radii[i_src]; + const float displacement = radius * std::tan(angle / 2.0f); + const float3 prev_dir = -directions[i_src_prev]; + const float3 &next_dir = directions[i_src]; + const float3 arc_start = src + prev_dir * displacement; + const float3 arc_end = src + next_dir * displacement; + + dst[arc.first()] = arc_start; + dst[arc.last()] = arc_end; + + const IndexRange middle = arc.drop_front(1).drop_back(1); + if (middle.is_empty()) { + continue; + } + + const float3 axis = -math::normalize(math::cross(prev_dir, next_dir)); + const float3 center_direction = math::normalize(math::midpoint(next_dir, prev_dir)); + const float distance_to_center = std::sqrt(pow2f(radius) + pow2f(displacement)); + const float3 center = src + center_direction * distance_to_center; + + /* Rotate each middle fillet point around the center. */ + const float segment_angle = angle / (middle.size() + 1); + for (const int i : IndexRange(middle.size())) { + const int point_i = middle[i]; + dst[point_i] = math::rotate_around_axis(arc_start, center, axis, segment_angle * (i + 1)); + } + } + }); +} + +/** + * Set handles for the "Bezier" mode where we rely on setting the inner handles to approximate a + * circular arc. The outer (previous and next) handles outside the result fillet segment are set + * to vector handles. + */ +static void calculate_bezier_handles_bezier_mode(const Span src_handles_l, + const Span src_handles_r, + const Span src_types_l, + const Span src_types_r, + const Span angles, + const Span radii, + const Span directions, + const Span dst_offsets, + const Span dst_positions, + MutableSpan dst_handles_l, + MutableSpan dst_handles_r, + MutableSpan dst_types_l, + MutableSpan dst_types_r) +{ + const int i_src_last = src_handles_l.index_range().last(); + const int i_dst_last = dst_positions.index_range().last(); + threading::parallel_for(src_handles_l.index_range(), 512, [&](IndexRange range) { + for (const int i_src : range) { + const IndexRange arc = bke::offsets_to_range(dst_offsets, i_src); + if (arc.size() == 1) { + dst_handles_l[arc.first()] = src_handles_l[i_src]; + dst_handles_r[arc.first()] = src_handles_r[i_src]; + dst_types_l[arc.first()] = src_types_l[i_src]; + dst_types_r[arc.first()] = src_types_r[i_src]; + continue; + } + BLI_assert(arc.size() == 2); + const int i_dst_a = arc.first(); + const int i_dst_b = arc.last(); + + const int i_src_prev = i_src == 0 ? i_src_last : i_src - 1; + const float angle = angles[i_src]; + const float radius = radii[i_src]; + const float3 prev_dir = -directions[i_src_prev]; + const float3 &next_dir = directions[i_src]; + + const float3 &arc_start = dst_positions[arc.first()]; + const float3 &arc_end = dst_positions[arc.last()]; + + /* Calculate the point's handles on the outside of the fillet segment, + * connecting to the next or previous result points. */ + const int i_dst_prev = i_dst_a == 0 ? i_dst_last : i_dst_a - 1; + const int i_dst_next = i_dst_b == i_dst_last ? 0 : i_dst_b + 1; + dst_handles_l[i_dst_a] = bke::curves::bezier::calculate_vector_handle( + dst_positions[i_dst_a], dst_positions[i_dst_prev]); + dst_handles_r[i_dst_b] = bke::curves::bezier::calculate_vector_handle( + dst_positions[i_dst_b], dst_positions[i_dst_next]); + dst_types_l[i_dst_a] = BEZIER_HANDLE_VECTOR; + dst_types_r[i_dst_b] = BEZIER_HANDLE_VECTOR; + + /* The inner handles are aligned with the aligned with the outer vector + * handles, but have a specific length to best approximate a circle. */ + const float handle_length = (4.0f / 3.0f) * radius * std::tan(angle / 4.0f); + dst_handles_r[i_dst_a] = arc_start - prev_dir * handle_length; + dst_handles_l[i_dst_b] = arc_end - next_dir * handle_length; + dst_types_r[i_dst_a] = BEZIER_HANDLE_ALIGN; + dst_types_l[i_dst_b] = BEZIER_HANDLE_ALIGN; + } + }); +} + +/** + * In the poly fillet mode, all the inner handles are set to vector handles, along with the "outer" + * (previous and next) handles at each fillet. + */ +static void calculate_bezier_handles_poly_mode(const Span src_handles_l, + const Span src_handles_r, + const Span src_types_l, + const Span src_types_r, + const Span dst_offsets, + const Span dst_positions, + MutableSpan dst_handles_l, + MutableSpan dst_handles_r, + MutableSpan dst_types_l, + MutableSpan dst_types_r) +{ + const int i_dst_last = dst_positions.index_range().last(); + threading::parallel_for(src_handles_l.index_range(), 512, [&](IndexRange range) { + for (const int i_src : range) { + const IndexRange arc = bke::offsets_to_range(dst_offsets, i_src); + if (arc.size() == 1) { + dst_handles_l[arc.first()] = src_handles_l[i_src]; + dst_handles_r[arc.first()] = src_handles_r[i_src]; + dst_types_l[arc.first()] = src_types_l[i_src]; + dst_types_r[arc.first()] = src_types_r[i_src]; + continue; + } + + /* The fillet's next and previous handles are vector handles, as are the inner handles. */ + dst_types_l.slice(arc).fill(BEZIER_HANDLE_VECTOR); + dst_types_r.slice(arc).fill(BEZIER_HANDLE_VECTOR); + + /* Calculate the point's handles on the outside of the fillet segment. This point + * won't be selected for a fillet if it is the first or last in a non-cyclic curve. */ + + const int i_dst_prev = arc.first() == 0 ? i_dst_last : arc.one_before_start(); + const int i_dst_next = arc.last() == i_dst_last ? 0 : arc.one_after_last(); + dst_handles_l[arc.first()] = bke::curves::bezier::calculate_vector_handle( + dst_positions[arc.first()], dst_positions[i_dst_prev]); + dst_handles_r[arc.last()] = bke::curves::bezier::calculate_vector_handle( + dst_positions[arc.last()], dst_positions[i_dst_next]); + + /* Set the values for the inner handles. */ + const IndexRange middle = arc.drop_front(1).drop_back(1); + for (const int i : middle) { + dst_handles_r[i] = bke::curves::bezier::calculate_vector_handle(dst_positions[i], + dst_positions[i - 1]); + dst_handles_l[i] = bke::curves::bezier::calculate_vector_handle(dst_positions[i], + dst_positions[i + 1]); + } + } + }); +} + +static bke::CurvesGeometry fillet_curves(const bke::CurvesGeometry &src_curves, + const IndexMask curve_selection, + const VArray &radius_input, + const VArray &counts, + const bool limit_radius, + const bool use_bezier_mode) +{ + const Vector unselected_ranges = curve_selection.extract_ranges_invert( + src_curves.curves_range()); + + const Span positions = src_curves.positions(); + const VArraySpan cyclic{src_curves.cyclic()}; + const bke::AttributeAccessor src_attributes = src_curves.attributes(); + + bke::CurvesGeometry dst_curves = bke::curves::copy_only_curve_domain(src_curves); + /* Stores the offset of every result point for every original point. + * The extra length is used in order to store an extra zero for every curve. */ + Array dst_point_offsets(src_curves.points_num() + src_curves.curves_num()); + calculate_result_offsets(src_curves, + curve_selection, + unselected_ranges, + radius_input, + counts, + cyclic, + dst_curves.offsets_for_write(), + dst_point_offsets); + const Span point_offsets = dst_point_offsets.as_span(); + + dst_curves.resize(dst_curves.offsets().last(), dst_curves.curves_num()); + bke::MutableAttributeAccessor dst_attributes = dst_curves.attributes_for_write(); + MutableSpan dst_positions = dst_curves.positions_for_write(); + + VArraySpan src_types_l; + VArraySpan src_types_r; + Span src_handles_l; + Span src_handles_r; + MutableSpan dst_types_l; + MutableSpan dst_types_r; + MutableSpan dst_handles_l; + MutableSpan dst_handles_r; + if (src_curves.has_curve_with_type(CURVE_TYPE_BEZIER)) { + src_types_l = src_curves.handle_types_left(); + src_types_r = src_curves.handle_types_right(); + src_handles_l = src_curves.handle_positions_left(); + src_handles_r = src_curves.handle_positions_right(); + + dst_types_l = dst_curves.handle_types_left_for_write(); + dst_types_r = dst_curves.handle_types_right_for_write(); + dst_handles_l = dst_curves.handle_positions_left_for_write(); + dst_handles_r = dst_curves.handle_positions_right_for_write(); + } + + threading::parallel_for(curve_selection.index_range(), 512, [&](IndexRange range) { + Array directions; + Array angles; + Array radii; + Array input_radii_buffer; + + for (const int curve_i : curve_selection.slice(range)) { + const IndexRange src_points = src_curves.points_for_curve(curve_i); + const Span offsets = point_offsets.slice(curve_dst_offsets(src_points, curve_i)); + const IndexRange dst_points = dst_curves.points_for_curve(curve_i); + const Span src_positions = positions.slice(src_points); + + directions.reinitialize(src_points.size()); + calculate_directions(src_positions, directions); + + angles.reinitialize(src_points.size()); + calculate_angles(directions, angles); + + radii.reinitialize(src_points.size()); + if (limit_radius) { + input_radii_buffer.reinitialize(src_points.size()); + radius_input.materialize_compressed(src_points, input_radii_buffer); + limit_radii(src_positions, angles, input_radii_buffer, cyclic[curve_i], radii); + } + else { + radius_input.materialize_compressed(src_points, radii); + } + + calculate_fillet_positions(positions.slice(src_points), + angles, + radii, + directions, + offsets, + dst_positions.slice(dst_points)); + + if (src_curves.has_curve_with_type(CURVE_TYPE_BEZIER)) { + if (use_bezier_mode) { + calculate_bezier_handles_bezier_mode(src_handles_l.slice(src_points), + src_handles_r.slice(src_points), + src_types_l.slice(src_points), + src_types_r.slice(src_points), + angles, + radii, + directions, + offsets, + dst_positions.slice(dst_points), + dst_handles_l.slice(dst_points), + dst_handles_r.slice(dst_points), + dst_types_l.slice(dst_points), + dst_types_r.slice(dst_points)); + } + else { + calculate_bezier_handles_poly_mode(src_handles_l.slice(src_points), + src_handles_r.slice(src_points), + src_types_l.slice(src_points), + src_types_r.slice(src_points), + offsets, + dst_positions.slice(dst_points), + dst_handles_l.slice(dst_points), + dst_handles_r.slice(dst_points), + dst_types_l.slice(dst_points), + dst_types_r.slice(dst_points)); + } + } + } + }); + + for (auto &attribute : bke::retrieve_attributes_for_transfer( + src_attributes, + dst_attributes, + ATTR_DOMAIN_MASK_POINT, + {"position", "handle_type_left", "handle_type_right", "handle_right", "handle_left"})) { + duplicate_fillet_point_data( + src_curves, dst_curves, curve_selection, point_offsets, attribute.src, attribute.dst.span); + attribute.dst.finish(); + } + + if (!unselected_ranges.is_empty()) { + for (auto &attribute : bke::retrieve_attributes_for_transfer( + src_attributes, dst_attributes, ATTR_DOMAIN_MASK_POINT)) { + bke::curves::copy_point_data( + src_curves, dst_curves, unselected_ranges, attribute.src, attribute.dst.span); + attribute.dst.finish(); + } + } + + return dst_curves; +} + +bke::CurvesGeometry fillet_curves_poly(const bke::CurvesGeometry &src_curves, + const IndexMask curve_selection, + const VArray &radius, + const VArray &count, + const bool limit_radius) +{ + return fillet_curves(src_curves, curve_selection, radius, count, limit_radius, false); +} + +bke::CurvesGeometry fillet_curves_bezier(const bke::CurvesGeometry &src_curves, + const IndexMask curve_selection, + const VArray &radius, + const bool limit_radius) +{ + return fillet_curves(src_curves, + curve_selection, + radius, + VArray::ForSingle(1, src_curves.points_num()), + limit_radius, + true); +} + +} // namespace blender::geometry diff --git a/source/blender/geometry/intern/subdivide_curves.cc b/source/blender/geometry/intern/subdivide_curves.cc index b6476d19818..8a6f3cbd5e3 100644 --- a/source/blender/geometry/intern/subdivide_curves.cc +++ b/source/blender/geometry/intern/subdivide_curves.cc @@ -56,40 +56,6 @@ static void calculate_result_offsets(const bke::CurvesGeometry &src_curves, bke::curves::accumulate_counts_to_offsets(dst_curve_offsets); } -struct AttributeTransferData { - /* Expect that if an attribute exists, it is stored as a contiguous array internally anyway. */ - GVArraySpan src; - bke::GSpanAttributeWriter dst; -}; - -static Vector retrieve_point_attributes( - const bke::AttributeAccessor &src_attributes, - bke::MutableAttributeAccessor &dst_attributes, - const Set &skip = {}) -{ - Vector attributes; - src_attributes.for_all( - [&](const bke::AttributeIDRef &id, const bke::AttributeMetaData meta_data) { - if (meta_data.domain != ATTR_DOMAIN_POINT) { - /* Curve domain attributes are all copied directly to the result in one step. */ - return true; - } - if (id.is_named() && skip.contains(id.name())) { - return true; - } - - GVArray src = src_attributes.lookup(id, ATTR_DOMAIN_POINT); - BLI_assert(src); - bke::GSpanAttributeWriter dst = dst_attributes.lookup_or_add_for_write_only_span( - id, ATTR_DOMAIN_POINT, meta_data.data_type); - BLI_assert(dst); - attributes.append({std::move(src), std::move(dst)}); - - return true; - }); - return attributes; -} - template static inline void linear_interpolation(const T &a, const T &b, MutableSpan dst) { @@ -365,7 +331,8 @@ bke::CurvesGeometry subdivide_curves(const bke::CurvesGeometry &src_curves, bke::MutableAttributeAccessor dst_attributes = dst_curves.attributes_for_write(); auto subdivide_catmull_rom = [&](IndexMask selection) { - for (auto &attribute : retrieve_point_attributes(src_attributes, dst_attributes)) { + for (auto &attribute : bke::retrieve_attributes_for_transfer( + src_attributes, dst_attributes, ATTR_DOMAIN_MASK_POINT)) { subdivide_attribute_catmull_rom(src_curves, dst_curves, selection, @@ -378,7 +345,8 @@ bke::CurvesGeometry subdivide_curves(const bke::CurvesGeometry &src_curves, }; auto subdivide_poly = [&](IndexMask selection) { - for (auto &attribute : retrieve_point_attributes(src_attributes, dst_attributes)) { + for (auto &attribute : bke::retrieve_attributes_for_transfer( + src_attributes, dst_attributes, ATTR_DOMAIN_MASK_POINT)) { subdivide_attribute_linear( src_curves, dst_curves, selection, point_offsets, attribute.src, attribute.dst.span); attribute.dst.finish(); @@ -419,13 +387,14 @@ bke::CurvesGeometry subdivide_curves(const bke::CurvesGeometry &src_curves, } }); - for (auto &attribute : retrieve_point_attributes(src_attributes, - dst_attributes, - {"position", - "handle_type_left", - "handle_type_right", - "handle_right", - "handle_left"})) { + for (auto &attribute : bke::retrieve_attributes_for_transfer(src_attributes, + dst_attributes, + ATTR_DOMAIN_MASK_POINT, + {"position", + "handle_type_left", + "handle_type_right", + "handle_right", + "handle_left"})) { subdivide_attribute_linear( src_curves, dst_curves, selection, point_offsets, attribute.src, attribute.dst.span); attribute.dst.finish(); @@ -445,7 +414,8 @@ bke::CurvesGeometry subdivide_curves(const bke::CurvesGeometry &src_curves, subdivide_nurbs); if (!unselected_ranges.is_empty()) { - for (auto &attribute : retrieve_point_attributes(src_attributes, dst_attributes)) { + for (auto &attribute : bke::retrieve_attributes_for_transfer( + src_attributes, dst_attributes, ATTR_DOMAIN_MASK_POINT)) { bke::curves::copy_point_data( src_curves, dst_curves, unselected_ranges, attribute.src, attribute.dst.span); attribute.dst.finish(); diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc index dd8471d2dac..ab1f8269c39 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc @@ -1,17 +1,12 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ -#include "BLI_task.hh" - #include "UI_interface.h" #include "UI_resources.h" -#include "DNA_node_types.h" +#include "GEO_fillet_curves.hh" #include "node_geometry_util.hh" -#include "BKE_curves.hh" -#include "BKE_spline.hh" - namespace blender::nodes::node_geo_curve_fillet_cc { NODE_STORAGE_FUNCS(NodeGeometryCurveFillet) @@ -45,574 +40,18 @@ static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) static void node_init(bNodeTree *UNUSED(tree), bNode *node) { NodeGeometryCurveFillet *data = MEM_cnew(__func__); - data->mode = GEO_NODE_CURVE_FILLET_BEZIER; node->storage = data; } -struct FilletParam { - GeometryNodeCurveFilletMode mode; - - /* Number of points to be added. */ - VArray counts; - - /* Radii for fillet arc at all vertices. */ - VArray radii; - - /* Whether or not fillets are allowed to overlap. */ - bool limit_radius; -}; - -/* A data structure used to store fillet data about all vertices to be filleted. */ -struct FilletData { - Span positions; - Array directions, axes; - Array radii, angles; - Array counts; -}; - static void node_update(bNodeTree *ntree, bNode *node) { const NodeGeometryCurveFillet &storage = node_storage(*node); const GeometryNodeCurveFilletMode mode = (GeometryNodeCurveFilletMode)storage.mode; - bNodeSocket *poly_socket = ((bNodeSocket *)node->inputs.first)->next; - nodeSetSocketAvailability(ntree, poly_socket, mode == GEO_NODE_CURVE_FILLET_POLY); } -/* Function to get the center of a fillet. */ -static float3 get_center(const float3 vec_pos2prev, - const float3 pos, - const float3 axis, - const float angle) -{ - float3 vec_pos2center; - rotate_normalized_v3_v3v3fl(vec_pos2center, vec_pos2prev, axis, M_PI_2 - angle / 2.0f); - vec_pos2center *= 1.0f / sinf(angle / 2.0f); - - return vec_pos2center + pos; -} - -/* Function to get the center of the fillet using fillet data */ -static float3 get_center(const float3 vec_pos2prev, const FilletData &fd, const int index) -{ - const float angle = fd.angles[index]; - const float3 axis = fd.axes[index]; - const float3 pos = fd.positions[index]; - - return get_center(vec_pos2prev, pos, axis, angle); -} - -/* Calculate the direction vectors from each vertex to their previous vertex. */ -static Array calculate_directions(const Span positions) -{ - const int num = positions.size(); - Array directions(num); - - for (const int i : IndexRange(num - 1)) { - directions[i] = math::normalize(positions[i + 1] - positions[i]); - } - directions[num - 1] = math::normalize(positions[0] - positions[num - 1]); - - return directions; -} - -/* Calculate the axes around which the fillet is built. */ -static Array calculate_axes(const Span directions) -{ - const int num = directions.size(); - Array axes(num); - - axes[0] = math::normalize(math::cross(-directions[num - 1], directions[0])); - for (const int i : IndexRange(1, num - 1)) { - axes[i] = math::normalize(math::cross(-directions[i - 1], directions[i])); - } - - return axes; -} - -/* Calculate the angle of the arc formed by the fillet. */ -static Array calculate_angles(const Span directions) -{ - const int num = directions.size(); - Array angles(num); - - angles[0] = M_PI - angle_v3v3(-directions[num - 1], directions[0]); - for (const int i : IndexRange(1, num - 1)) { - angles[i] = M_PI - angle_v3v3(-directions[i - 1], directions[i]); - } - - return angles; -} - -/* Calculate the segment count in each filleted arc. */ -static Array calculate_counts(const FilletParam &fillet_param, - const int num, - const int spline_offset, - const bool cyclic) -{ - Array counts(num, 1); - if (fillet_param.mode == GEO_NODE_CURVE_FILLET_POLY) { - for (const int i : IndexRange(num)) { - counts[i] = fillet_param.counts[spline_offset + i]; - } - } - if (!cyclic) { - counts[0] = counts[num - 1] = 0; - } - - return counts; -} - -/* Calculate the radii for the vertices to be filleted. */ -static Array calculate_radii(const FilletParam &fillet_param, - const int num, - const int spline_offset) -{ - Array radii(num, 0.0f); - if (fillet_param.limit_radius) { - for (const int i : IndexRange(num)) { - radii[i] = std::max(fillet_param.radii[spline_offset + i], 0.0f); - } - } - else { - for (const int i : IndexRange(num)) { - radii[i] = fillet_param.radii[spline_offset + i]; - } - } - - return radii; -} - -/* Calculate the number of vertices added per vertex on the source spline. */ -static int calculate_point_counts(MutableSpan point_counts, - const Span radii, - const Span counts) -{ - int added_count = 0; - for (const int i : IndexRange(point_counts.size())) { - /* Calculate number of points to be added for the vertex. */ - if (radii[i] != 0.0f) { - added_count += counts[i]; - point_counts[i] = counts[i] + 1; - } - } - - return added_count; -} - -static FilletData calculate_fillet_data(const Spline &spline, - const FilletParam &fillet_param, - int &added_count, - MutableSpan point_counts, - const int spline_offset) -{ - const int num = spline.size(); - - FilletData fd; - fd.directions = calculate_directions(spline.positions()); - fd.positions = spline.positions(); - fd.axes = calculate_axes(fd.directions); - fd.angles = calculate_angles(fd.directions); - fd.counts = calculate_counts(fillet_param, num, spline_offset, spline.is_cyclic()); - fd.radii = calculate_radii(fillet_param, num, spline_offset); - - added_count = calculate_point_counts(point_counts, fd.radii, fd.counts); - - return fd; -} - -/* Limit the radius based on angle and radii to prevent overlapping. */ -static void limit_radii(FilletData &fd, const bool cyclic) -{ - MutableSpan radii(fd.radii); - Span angles(fd.angles); - Span positions(fd.positions); - - const int num = radii.size(); - const int fillet_count = cyclic ? num : num - 2; - const int start = cyclic ? 0 : 1; - Array max_radii(num, FLT_MAX); - - if (cyclic) { - /* Calculate lengths between adjacent control points. */ - const float len_prev = math::distance(positions[0], positions[num - 1]); - const float len_next = math::distance(positions[0], positions[1]); - - /* Calculate tangent lengths of fillets in control points. */ - const float tan_len = radii[0] * tan(angles[0] / 2.0f); - const float tan_len_prev = radii[num - 1] * tan(angles[num - 1] / 2.0f); - const float tan_len_next = radii[1] * tan(angles[1] / 2.0f); - - float factor_prev = 1.0f, factor_next = 1.0f; - if (tan_len + tan_len_prev > len_prev) { - factor_prev = len_prev / (tan_len + tan_len_prev); - } - if (tan_len + tan_len_next > len_next) { - factor_next = len_next / (tan_len + tan_len_next); - } - - /* Scale max radii by calculated factors. */ - max_radii[0] = radii[0] * std::min(factor_next, factor_prev); - max_radii[1] = radii[1] * factor_next; - max_radii[num - 1] = radii[num - 1] * factor_prev; - } - - /* Initialize max_radii to largest possible radii. */ - float prev_dist = math::distance(positions[1], positions[0]); - for (const int i : IndexRange(1, num - 2)) { - const float temp_dist = math::distance(positions[i], positions[i + 1]); - max_radii[i] = std::min(prev_dist, temp_dist) / tan(angles[i] / 2.0f); - prev_dist = temp_dist; - } - - /* Max radii calculations for each index. */ - for (const int i : IndexRange(start, fillet_count - 1)) { - const float len_next = math::distance(positions[i], positions[i + 1]); - const float tan_len = radii[i] * tan(angles[i] / 2.0f); - const float tan_len_next = radii[i + 1] * tan(angles[i + 1] / 2.0f); - - /* Scale down radii if too large for segment. */ - float factor = 1.0f; - if (tan_len + tan_len_next > len_next) { - factor = len_next / (tan_len + tan_len_next); - } - max_radii[i] = std::min(max_radii[i], radii[i] * factor); - max_radii[i + 1] = std::min(max_radii[i + 1], radii[i + 1] * factor); - } - - /* Assign the max_radii to the fillet data's radii. */ - for (const int i : IndexRange(num)) { - radii[i] = std::min(radii[i], max_radii[i]); - } -} - -/* - * Create a mapping from each vertex in the destination spline to that of the source spline. - * Used for copying the data from the source spline. - */ -static Array create_dst_to_src_map(const Span point_counts, const int total_points) -{ - Array map(total_points); - MutableSpan map_span{map}; - int index = 0; - - for (const int i : point_counts.index_range()) { - map_span.slice(index, point_counts[i]).fill(i); - index += point_counts[i]; - } - - BLI_assert(index == total_points); - - return map; -} - -template -static void copy_attribute_by_mapping(const Span src, - MutableSpan dst, - const Span mapping) -{ - for (const int i : dst.index_range()) { - dst[i] = src[mapping[i]]; - } -} - -/* Copy radii and tilts from source spline to destination. Positions are handled later in update - * positions methods. */ -static void copy_common_attributes_by_mapping(const Spline &src, - Spline &dst, - const Span mapping) -{ - copy_attribute_by_mapping(src.radii(), dst.radii(), mapping); - copy_attribute_by_mapping(src.tilts(), dst.tilts(), mapping); - - src.attributes.foreach_attribute( - [&](const AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) { - std::optional src_attribute = src.attributes.get_for_read(attribute_id); - if (dst.attributes.create(attribute_id, meta_data.data_type)) { - std::optional dst_attribute = dst.attributes.get_for_write(attribute_id); - if (dst_attribute) { - attribute_math::convert_to_static_type(dst_attribute->type(), [&](auto dummy) { - using T = decltype(dummy); - copy_attribute_by_mapping( - src_attribute->typed(), dst_attribute->typed(), mapping); - }); - return true; - } - } - BLI_assert_unreachable(); - return false; - }, - ATTR_DOMAIN_POINT); -} - -/* Update the vertex positions and handle positions of a Bezier spline based on fillet data. */ -static void update_bezier_positions(const FilletData &fd, - BezierSpline &dst_spline, - const BezierSpline &src_spline, - const Span point_counts) -{ - Span radii(fd.radii); - Span angles(fd.angles); - Span axes(fd.axes); - Span positions(fd.positions); - Span directions(fd.directions); - - const int num = radii.size(); - - int i_dst = 0; - for (const int i_src : IndexRange(num)) { - const int count = point_counts[i_src]; - - /* Skip if the point count for the vertex is 1. */ - if (count == 1) { - dst_spline.positions()[i_dst] = src_spline.positions()[i_src]; - dst_spline.handle_types_left()[i_dst] = src_spline.handle_types_left()[i_src]; - dst_spline.handle_types_right()[i_dst] = src_spline.handle_types_right()[i_src]; - dst_spline.handle_positions_left()[i_dst] = src_spline.handle_positions_left()[i_src]; - dst_spline.handle_positions_right()[i_dst] = src_spline.handle_positions_right()[i_src]; - i_dst++; - continue; - } - - /* Calculate the angle to be formed between any 2 adjacent vertices within the fillet. */ - const float segment_angle = angles[i_src] / (count - 1); - /* Calculate the handle length for each added vertex. Equation: L = 4R/3 * tan(A/4) */ - const float handle_length = 4.0f * radii[i_src] / 3.0f * tan(segment_angle / 4.0f); - /* Calculate the distance by which each vertex should be displaced from their initial position. - */ - const float displacement = radii[i_src] * tan(angles[i_src] / 2.0f); - - /* Position the end points of the arc and their handles. */ - const int end_i = i_dst + count - 1; - const float3 prev_dir = i_src == 0 ? -directions[num - 1] : -directions[i_src - 1]; - const float3 next_dir = directions[i_src]; - dst_spline.positions()[i_dst] = positions[i_src] + displacement * prev_dir; - dst_spline.positions()[end_i] = positions[i_src] + displacement * next_dir; - dst_spline.handle_positions_right()[i_dst] = dst_spline.positions()[i_dst] - - handle_length * prev_dir; - dst_spline.handle_positions_left()[end_i] = dst_spline.positions()[end_i] - - handle_length * next_dir; - dst_spline.handle_types_right()[i_dst] = dst_spline.handle_types_left()[end_i] = - BEZIER_HANDLE_ALIGN; - dst_spline.handle_types_left()[i_dst] = dst_spline.handle_types_right()[end_i] = - BEZIER_HANDLE_VECTOR; - dst_spline.mark_cache_invalid(); - - /* Calculate the center of the radius to be formed. */ - const float3 center = get_center(dst_spline.positions()[i_dst] - positions[i_src], fd, i_src); - /* Calculate the vector of the radius formed by the first vertex. */ - float3 radius_vec = dst_spline.positions()[i_dst] - center; - float radius; - radius_vec = math::normalize_and_get_length(radius_vec, radius); - - dst_spline.handle_types_right().slice(1, count - 2).fill(BEZIER_HANDLE_ALIGN); - dst_spline.handle_types_left().slice(1, count - 2).fill(BEZIER_HANDLE_ALIGN); - - /* For each of the vertices in between the end points. */ - for (const int j : IndexRange(1, count - 2)) { - int index = i_dst + j; - /* Rotate the radius by the segment angle and determine its tangent (used for getting handle - * directions). */ - float3 new_radius_vec, tangent_vec; - rotate_normalized_v3_v3v3fl(new_radius_vec, radius_vec, -axes[i_src], segment_angle); - rotate_normalized_v3_v3v3fl(tangent_vec, new_radius_vec, axes[i_src], M_PI_2); - radius_vec = new_radius_vec; - tangent_vec *= handle_length; - - /* Adjust the positions of the respective vertex and its handles. */ - dst_spline.positions()[index] = center + new_radius_vec * radius; - dst_spline.handle_positions_left()[index] = dst_spline.positions()[index] + tangent_vec; - dst_spline.handle_positions_right()[index] = dst_spline.positions()[index] - tangent_vec; - } - - i_dst += count; - } -} - -/* Update the vertex positions of a Poly spline based on fillet data. */ -static void update_poly_positions(const FilletData &fd, - Spline &dst_spline, - const Spline &src_spline, - const Span point_counts) -{ - Span radii(fd.radii); - Span angles(fd.angles); - Span axes(fd.axes); - Span positions(fd.positions); - Span directions(fd.directions); - - const int num = radii.size(); - - int i_dst = 0; - for (const int i_src : IndexRange(num)) { - const int count = point_counts[i_src]; - - /* Skip if the point count for the vertex is 1. */ - if (count == 1) { - dst_spline.positions()[i_dst] = src_spline.positions()[i_src]; - i_dst++; - continue; - } - - const float segment_angle = angles[i_src] / (count - 1); - const float displacement = radii[i_src] * tan(angles[i_src] / 2.0f); - - /* Position the end points of the arc. */ - const int end_i = i_dst + count - 1; - const float3 prev_dir = i_src == 0 ? -directions[num - 1] : -directions[i_src - 1]; - const float3 next_dir = directions[i_src]; - dst_spline.positions()[i_dst] = positions[i_src] + displacement * prev_dir; - dst_spline.positions()[end_i] = positions[i_src] + displacement * next_dir; - - /* Calculate the center of the radius to be formed. */ - const float3 center = get_center(dst_spline.positions()[i_dst] - positions[i_src], fd, i_src); - /* Calculate the vector of the radius formed by the first vertex. */ - float3 radius_vec = dst_spline.positions()[i_dst] - center; - - for (const int j : IndexRange(1, count - 2)) { - /* Rotate the radius by the segment angle */ - float3 new_radius_vec; - rotate_normalized_v3_v3v3fl(new_radius_vec, radius_vec, -axes[i_src], segment_angle); - radius_vec = new_radius_vec; - - dst_spline.positions()[i_dst + j] = center + new_radius_vec; - } - - i_dst += count; - } -} - -static SplinePtr fillet_spline(const Spline &spline, - const FilletParam &fillet_param, - const int spline_offset) -{ - const int num = spline.size(); - const bool cyclic = spline.is_cyclic(); - - if (num < 3) { - return spline.copy(); - } - - /* Initialize the point_counts with 1s (at least one vertex on dst for each vertex on src). */ - Array point_counts(num, 1); - - int added_count = 0; - /* Update point_counts array and added_count. */ - FilletData fd = calculate_fillet_data( - spline, fillet_param, added_count, point_counts, spline_offset); - if (fillet_param.limit_radius) { - limit_radii(fd, cyclic); - } - - const int total_points = added_count + num; - const Array dst_to_src = create_dst_to_src_map(point_counts, total_points); - SplinePtr dst_spline_ptr = spline.copy_only_settings(); - (*dst_spline_ptr).resize(total_points); - copy_common_attributes_by_mapping(spline, *dst_spline_ptr, dst_to_src); - - switch (spline.type()) { - case CURVE_TYPE_BEZIER: { - const BezierSpline &src_spline = static_cast(spline); - BezierSpline &dst_spline = static_cast(*dst_spline_ptr); - if (fillet_param.mode == GEO_NODE_CURVE_FILLET_POLY) { - dst_spline.handle_types_left().fill(BEZIER_HANDLE_VECTOR); - dst_spline.handle_types_right().fill(BEZIER_HANDLE_VECTOR); - update_poly_positions(fd, dst_spline, src_spline, point_counts); - } - else { - update_bezier_positions(fd, dst_spline, src_spline, point_counts); - } - break; - } - case CURVE_TYPE_POLY: { - update_poly_positions(fd, *dst_spline_ptr, spline, point_counts); - break; - } - case CURVE_TYPE_NURBS: { - const NURBSpline &src_spline = static_cast(spline); - NURBSpline &dst_spline = static_cast(*dst_spline_ptr); - copy_attribute_by_mapping(src_spline.weights(), dst_spline.weights(), dst_to_src); - update_poly_positions(fd, dst_spline, src_spline, point_counts); - break; - } - case CURVE_TYPE_CATMULL_ROM: { - BLI_assert_unreachable(); - break; - } - } - - return dst_spline_ptr; -} - -static std::unique_ptr fillet_curve(const CurveEval &input_curve, - const FilletParam &fillet_param) -{ - Span input_splines = input_curve.splines(); - - std::unique_ptr output_curve = std::make_unique(); - const int splines_num = input_splines.size(); - output_curve->resize(splines_num); - MutableSpan output_splines = output_curve->splines(); - Array spline_offsets = input_curve.control_point_offsets(); - - threading::parallel_for(input_splines.index_range(), 128, [&](IndexRange range) { - for (const int i : range) { - output_splines[i] = fillet_spline(*input_splines[i], fillet_param, spline_offsets[i]); - } - }); - output_curve->attributes = input_curve.attributes; - - return output_curve; -} - -static void calculate_curve_fillet(GeometrySet &geometry_set, - const GeometryNodeCurveFilletMode mode, - const Field &radius_field, - const std::optional> &count_field, - const bool limit_radius) -{ - if (!geometry_set.has_curves()) { - return; - } - - FilletParam fillet_param; - fillet_param.mode = mode; - - CurveComponent &component = geometry_set.get_component_for_write(); - GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_POINT}; - const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_POINT); - fn::FieldEvaluator field_evaluator{field_context, domain_size}; - - field_evaluator.add(radius_field); - - if (mode == GEO_NODE_CURVE_FILLET_POLY) { - field_evaluator.add(*count_field); - } - - field_evaluator.evaluate(); - - fillet_param.radii = field_evaluator.get_evaluated(0); - if (fillet_param.radii.is_single() && fillet_param.radii.get_internal_single() < 0.0f) { - return; - } - - if (mode == GEO_NODE_CURVE_FILLET_POLY) { - fillet_param.counts = field_evaluator.get_evaluated(1); - } - - fillet_param.limit_radius = limit_radius; - - const Curves &src_curves_id = *geometry_set.get_curves_for_read(); - const std::unique_ptr input_curve = curves_to_curve_eval(*component.get_for_read()); - std::unique_ptr output_curve = fillet_curve(*input_curve, fillet_param); - - Curves *dst_curves_id = curve_eval_to_curves(*output_curve); - bke::curves_copy_parameters(src_curves_id, *dst_curves_id); - geometry_set.replace_curves(dst_curves_id); -} - static void node_geo_exec(GeoNodeExecParams params) { GeometrySet geometry_set = params.extract_input("Curve"); @@ -629,7 +68,42 @@ static void node_geo_exec(GeoNodeExecParams params) } geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { - calculate_curve_fillet(geometry_set, mode, radius_field, count_field, limit_radius); + if (!geometry_set.has_curves()) { + return; + } + + const CurveComponent &component = *geometry_set.get_component_for_read(); + const Curves &curves_id = *component.get_for_read(); + const bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id.geometry); + GeometryComponentFieldContext context{component, ATTR_DOMAIN_POINT}; + fn::FieldEvaluator evaluator{context, curves.points_num()}; + evaluator.add(radius_field); + + switch (mode) { + case GEO_NODE_CURVE_FILLET_BEZIER: { + evaluator.evaluate(); + bke::CurvesGeometry dst_curves = geometry::fillet_curves_bezier( + curves, curves.curves_range(), evaluator.get_evaluated(0), limit_radius); + Curves *dst_curves_id = bke::curves_new_nomain(std::move(dst_curves)); + bke::curves_copy_parameters(curves_id, *dst_curves_id); + geometry_set.replace_curves(dst_curves_id); + break; + } + case GEO_NODE_CURVE_FILLET_POLY: { + evaluator.add(*count_field); + evaluator.evaluate(); + bke::CurvesGeometry dst_curves = geometry::fillet_curves_poly( + curves, + curves.curves_range(), + evaluator.get_evaluated(0), + evaluator.get_evaluated(1), + limit_radius); + Curves *dst_curves_id = bke::curves_new_nomain(std::move(dst_curves)); + bke::curves_copy_parameters(curves_id, *dst_curves_id); + geometry_set.replace_curves(dst_curves_id); + break; + } + } }); params.set_output("Curve", std::move(geometry_set)); -- cgit v1.2.3 From 215f805ce6b540177dedd71721e62c56a764a5ea Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Tue, 19 Jul 2022 21:48:32 -0500 Subject: Curves: Remove use of CurveEval in sculpt brushes This commit removes the use of PolySpline for resampling curves and replaces it with the length parameterization utility for that purpose. I didn't test performance, but I would expect the shrinking to be slightly faster because I reused some arrays to avoid allocating them for every curve. I noted some potential improvements in the "add curves" function. Differential Revision: https://developer.blender.org/D15342 --- .../sculpt_paint/curves_sculpt_grow_shrink.cc | 73 ++++++++++++---------- .../blender/geometry/intern/add_curves_on_mesh.cc | 67 ++++++++++++-------- 2 files changed, 80 insertions(+), 60 deletions(-) diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_grow_shrink.cc b/source/blender/editors/sculpt_paint/curves_sculpt_grow_shrink.cc index 709ecc79967..408139d6653 100644 --- a/source/blender/editors/sculpt_paint/curves_sculpt_grow_shrink.cc +++ b/source/blender/editors/sculpt_paint/curves_sculpt_grow_shrink.cc @@ -4,8 +4,7 @@ #include "BLI_enumerable_thread_specific.hh" #include "BLI_float4x4.hh" -#include "BLI_kdtree.h" -#include "BLI_rand.hh" +#include "BLI_length_parameterize.hh" #include "BLI_vector.hh" #include "PIL_time.h" @@ -14,19 +13,13 @@ #include "BKE_attribute_math.hh" #include "BKE_brush.h" -#include "BKE_bvhutils.h" #include "BKE_context.h" #include "BKE_curves.hh" -#include "BKE_mesh.h" -#include "BKE_mesh_runtime.h" #include "BKE_paint.h" -#include "BKE_spline.hh" #include "DNA_brush_enums.h" #include "DNA_brush_types.h" #include "DNA_curves_types.h" -#include "DNA_mesh_types.h" -#include "DNA_meshdata_types.h" #include "DNA_object_types.h" #include "DNA_screen_types.h" #include "DNA_space_types.h" @@ -70,6 +63,24 @@ class ShrinkCurvesEffect : public CurvesEffect { private: const Brush &brush_; + /** Storage of per-curve parameterization data to avoid reallocation. */ + struct ParameterizationBuffers { + Array old_positions; + Array old_lengths; + Array sample_lengths; + Array indices; + Array factors; + + void reinitialize(const int points_num) + { + this->old_positions.reinitialize(points_num); + this->old_lengths.reinitialize(length_parameterize::segments_num(points_num, false)); + this->sample_lengths.reinitialize(points_num); + this->indices.reinitialize(points_num); + this->factors.reinitialize(points_num); + } + }; + public: ShrinkCurvesEffect(const Brush &brush) : brush_(brush) { @@ -81,46 +92,42 @@ class ShrinkCurvesEffect : public CurvesEffect { { MutableSpan positions_cu = curves.positions_for_write(); threading::parallel_for(curve_indices.index_range(), 256, [&](const IndexRange range) { + ParameterizationBuffers data; for (const int influence_i : range) { const int curve_i = curve_indices[influence_i]; const float move_distance_cu = move_distances_cu[influence_i]; - const IndexRange curve_points = curves.points_for_curve(curve_i); - this->shrink_curve(positions_cu, curve_points, move_distance_cu); + const IndexRange points = curves.points_for_curve(curve_i); + this->shrink_curve(positions_cu.slice(points), move_distance_cu, data); } }); } + private: void shrink_curve(MutableSpan positions, - const IndexRange curve_points, - const float shrink_length) const + const float shrink_length, + ParameterizationBuffers &data) const { - PolySpline spline; - spline.resize(curve_points.size()); - MutableSpan spline_positions = spline.positions(); - spline_positions.copy_from(positions.slice(curve_points)); - spline.mark_cache_invalid(); + namespace lp = length_parameterize; + data.reinitialize(positions.size()); + + /* Copy the old positions to facilitate mixing from neighbors for the resulting curve. */ + data.old_positions.as_mutable_span().copy_from(positions); + + lp::accumulate_lengths(data.old_positions, false, data.old_lengths); + const float min_length = brush_.curves_sculpt_settings->minimum_length; - const float old_length = spline.length(); + const float old_length = data.old_lengths.last(); const float new_length = std::max(min_length, old_length - shrink_length); const float length_factor = std::clamp(new_length / old_length, 0.0f, 1.0f); - Vector old_point_lengths; - old_point_lengths.append(0.0f); - for (const int i : spline_positions.index_range().drop_back(1)) { - const float3 &p1 = spline_positions[i]; - const float3 &p2 = spline_positions[i + 1]; - const float length = math::distance(p1, p2); - old_point_lengths.append(old_point_lengths.last() + length); + data.sample_lengths.first() = 0.0f; + for (const int i : data.old_lengths.index_range()) { + data.sample_lengths[i + 1] = data.old_lengths[i] * length_factor; } - for (const int i : spline_positions.index_range()) { - const float eval_length = old_point_lengths[i] * length_factor; - const Spline::LookupResult lookup = spline.lookup_evaluated_length(eval_length); - const float index_factor = lookup.evaluated_index + lookup.factor; - float3 p; - spline.sample_with_index_factors(spline_positions, {&index_factor, 1}, {&p, 1}); - positions[curve_points[i]] = p; - } + lp::sample_at_lengths(data.old_lengths, data.sample_lengths, data.indices, data.factors); + + lp::linear_interpolation(data.old_positions, data.indices, data.factors, positions); } }; diff --git a/source/blender/geometry/intern/add_curves_on_mesh.cc b/source/blender/geometry/intern/add_curves_on_mesh.cc index 34551bd474f..a69073af207 100644 --- a/source/blender/geometry/intern/add_curves_on_mesh.cc +++ b/source/blender/geometry/intern/add_curves_on_mesh.cc @@ -1,7 +1,9 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ +#include "BLI_length_parameterize.hh" + +#include "BKE_attribute_math.hh" #include "BKE_mesh_sample.hh" -#include "BKE_spline.hh" #include "GEO_add_curves_on_mesh.hh" @@ -145,16 +147,16 @@ static void interpolate_position_with_interpolation(CurvesGeometry &curves, const int added_curves_num = root_positions_cu.size(); threading::parallel_for(IndexRange(added_curves_num), 256, [&](const IndexRange range) { - for (const int i : range) { - const NeighborCurves &neighbors = neighbors_per_curve[i]; - const int curve_i = old_curves_num + i; + for (const int added_curve_i : range) { + const NeighborCurves &neighbors = neighbors_per_curve[added_curve_i]; + const int curve_i = old_curves_num + added_curve_i; const IndexRange points = curves.points_for_curve(curve_i); - const float length_cu = new_lengths_cu[i]; - const float3 &normal_su = new_normals_su[i]; + const float length_cu = new_lengths_cu[added_curve_i]; + const float3 &normal_su = new_normals_su[added_curve_i]; const float3 normal_cu = math::normalize(surface_to_curves_normal_mat * normal_su); - const float3 &root_cu = root_positions_cu[i]; + const float3 &root_cu = root_positions_cu[added_curve_i]; if (neighbors.is_empty()) { /* If there are no neighbors, just make a straight line. */ @@ -197,30 +199,41 @@ static void interpolate_position_with_interpolation(CurvesGeometry &curves, const IndexRange neighbor_points = curves.points_for_curve(neighbor_curve_i); const float3 &neighbor_root_cu = positions_cu[neighbor_points[0]]; - /* Use a temporary #PolySpline, because that's the easiest way to resample an - * existing curve right now. Resampling is necessary if the length of the new curve - * does not match the length of the neighbors or the number of handle points is - * different. */ - PolySpline neighbor_spline; - neighbor_spline.resize(neighbor_points.size()); - neighbor_spline.positions().copy_from(positions_cu.slice(neighbor_points)); - neighbor_spline.mark_cache_invalid(); + /* Sample the positions on neighbors and mix them into the final positions of the curve. + * Resampling is necessary if the length of the new curve does not match the length of the + * neighbors or the number of handle points is different. + * + * TODO: The lengths can be cached so they aren't recomputed if a curve is a neighbor for + * multiple new curves. Also, allocations could be avoided by reusing some arrays. */ + + const Span neighbor_positions_cu = positions_cu.slice(neighbor_points); + if (neighbor_positions_cu.size() == 1) { + /* Skip interpolating positions from neighbors with only one point. */ + continue; + } + Array lengths(length_parameterize::segments_num(neighbor_points.size(), false)); + length_parameterize::accumulate_lengths(neighbor_positions_cu, false, lengths); + const float neighbor_length_cu = lengths.last(); - const float neighbor_length_cu = neighbor_spline.length(); + Array sample_lengths(points.size()); const float length_factor = std::min(1.0f, length_cu / neighbor_length_cu); - const float resample_factor = (1.0f / (points.size() - 1.0f)) * length_factor; - for (const int j : IndexRange(points.size())) { - const Spline::LookupResult lookup = neighbor_spline.lookup_evaluated_factor( - j * resample_factor); - const float index_factor = lookup.evaluated_index + lookup.factor; - float3 p; - neighbor_spline.sample_with_index_factors( - neighbor_spline.positions(), {&index_factor, 1}, {&p, 1}); - const float3 relative_coord = p - neighbor_root_cu; - float3 rotated_relative_coord = relative_coord; + for (const int i : sample_lengths.index_range()) { + sample_lengths[i] = i * resample_factor * neighbor_length_cu; + } + + Array indices(points.size()); + Array factors(points.size()); + length_parameterize::sample_at_lengths(lengths, sample_lengths, indices, factors); + + for (const int i : IndexRange(points.size())) { + const float3 sample_cu = math::interpolate(neighbor_positions_cu[indices[i]], + neighbor_positions_cu[indices[i] + 1], + factors[i]); + const float3 relative_to_root_cu = sample_cu - neighbor_root_cu; + float3 rotated_relative_coord = relative_to_root_cu; mul_m3_v3(normal_rotation_cu, rotated_relative_coord); - positions_cu[points[j]] += neighbor.weight * rotated_relative_coord; + positions_cu[points[i]] += neighbor.weight * rotated_relative_coord; } } } -- cgit v1.2.3