diff --git a/intern/cycles/device/device.h b/intern/cycles/device/device.h index a8b5e0842f4..f7c64de234f 100644 --- a/intern/cycles/device/device.h +++ b/intern/cycles/device/device.h @@ -182,7 +182,7 @@ class Device { { } - /* Return true if device is ready for rendering, or report status if not. */ + /* Report status and return true if device is ready for rendering. */ virtual bool is_ready(string & /*status*/) const { return true; diff --git a/intern/cycles/device/metal/device_impl.mm b/intern/cycles/device/metal/device_impl.mm index d196a880b66..67c2fdeebf5 100644 --- a/intern/cycles/device/metal/device_impl.mm +++ b/intern/cycles/device/metal/device_impl.mm @@ -490,6 +490,9 @@ bool MetalDevice::make_source_and_check_if_compile_needed(MetalPipelineType pso_ MD5Hash md5; md5.append(constant_values); md5.append(source[pso_type]); + if (use_metalrt) { + md5.append(string_printf("metalrt_features=%d", kernel_features & METALRT_FEATURE_MASK)); + } kernels_md5[pso_type] = md5.get_hex(); return MetalDeviceKernels::should_load_kernels(this, pso_type); @@ -934,6 +937,17 @@ bool MetalDevice::is_ready(string &status) const DEVICE_KERNEL_NUM); return false; } + + if (int num_requests = MetalDeviceKernels::num_incomplete_specialization_requests()) { + status = string_printf("%d kernels to optimize", num_requests); + } + else if (kernel_specialization_level == PSO_SPECIALIZED_INTERSECT) { + status = "Using optimized intersection kernels"; + } + else if (kernel_specialization_level == PSO_SPECIALIZED_SHADE) { + status = "Using optimized kernels"; + } + metal_printf("MetalDevice::is_ready(...) --> true\n"); return true; } @@ -970,7 +984,7 @@ void MetalDevice::optimize_for_scene(Scene *scene) } if (specialize_in_background) { - if (!MetalDeviceKernels::any_specialization_happening_now()) { + if (MetalDeviceKernels::num_incomplete_specialization_requests() == 0) { dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), specialize_kernels_fn); } diff --git a/intern/cycles/device/metal/kernel.h b/intern/cycles/device/metal/kernel.h index a74ef91923f..5946c53562b 100644 --- a/intern/cycles/device/metal/kernel.h +++ b/intern/cycles/device/metal/kernel.h @@ -63,8 +63,7 @@ enum MetalPipelineType { }; # define METALRT_FEATURE_MASK \ - (KERNEL_FEATURE_HAIR | KERNEL_FEATURE_HAIR_THICK | KERNEL_FEATURE_POINTCLOUD | \ - KERNEL_FEATURE_OBJECT_MOTION) + (KERNEL_FEATURE_HAIR | KERNEL_FEATURE_HAIR_THICK | KERNEL_FEATURE_POINTCLOUD) const char *kernel_type_as_string(MetalPipelineType pso_type); @@ -81,7 +80,7 @@ struct MetalKernelPipeline { KernelData kernel_data_; bool use_metalrt; - uint32_t metalrt_features = 0; + uint32_t kernel_features = 0; int threads_per_threadgroup; @@ -104,7 +103,7 @@ struct MetalKernelPipeline { /* Cache of Metal kernels for each DeviceKernel. */ namespace MetalDeviceKernels { -bool any_specialization_happening_now(); +int num_incomplete_specialization_requests(); int get_loaded_kernel_count(MetalDevice const *device, MetalPipelineType pso_type); bool should_load_kernels(MetalDevice const *device, MetalPipelineType pso_type); bool load(MetalDevice *device, MetalPipelineType pso_type); diff --git a/intern/cycles/device/metal/kernel.mm b/intern/cycles/device/metal/kernel.mm index 327e47befab..fea25172f21 100644 --- a/intern/cycles/device/metal/kernel.mm +++ b/intern/cycles/device/metal/kernel.mm @@ -344,9 +344,7 @@ void ShaderCache::load_kernel(DeviceKernel device_kernel, /* metalrt options */ pipeline->use_metalrt = device->use_metalrt; - pipeline->metalrt_features = device->use_metalrt ? - (device->kernel_features & METALRT_FEATURE_MASK) : - 0; + pipeline->kernel_features = device->kernel_features; { thread_scoped_lock lock(cache_mutex); @@ -357,65 +355,36 @@ void ShaderCache::load_kernel(DeviceKernel device_kernel, MetalKernelPipeline *ShaderCache::get_best_pipeline(DeviceKernel kernel, const MetalDevice *device) { - /* metalrt options */ - bool use_metalrt = device->use_metalrt; - bool device_metalrt_hair = use_metalrt && device->kernel_features & KERNEL_FEATURE_HAIR; - bool device_metalrt_hair_thick = use_metalrt && - device->kernel_features & KERNEL_FEATURE_HAIR_THICK; - bool device_metalrt_pointcloud = use_metalrt && - device->kernel_features & KERNEL_FEATURE_POINTCLOUD; - bool device_metalrt_motion = use_metalrt && - device->kernel_features & KERNEL_FEATURE_OBJECT_MOTION; - - MetalKernelPipeline *best_pipeline = nullptr; - while (!best_pipeline) { + while (running) { + /* Search all loaded pipelines with matching kernels_md5 checksums. */ + MetalKernelPipeline *best_match = nullptr; { thread_scoped_lock lock(cache_mutex); - for (auto &pipeline : pipelines[kernel]) { - if (!pipeline->loaded) { - /* still loading - ignore */ - continue; - } - - bool pipeline_metalrt_hair = pipeline->metalrt_features & KERNEL_FEATURE_HAIR; - bool pipeline_metalrt_hair_thick = pipeline->metalrt_features & KERNEL_FEATURE_HAIR_THICK; - bool pipeline_metalrt_pointcloud = pipeline->metalrt_features & KERNEL_FEATURE_POINTCLOUD; - bool pipeline_metalrt_motion = use_metalrt && - pipeline->metalrt_features & KERNEL_FEATURE_OBJECT_MOTION; - - if (pipeline->use_metalrt != use_metalrt || pipeline_metalrt_hair != device_metalrt_hair || - pipeline_metalrt_hair_thick != device_metalrt_hair_thick || - pipeline_metalrt_pointcloud != device_metalrt_pointcloud || - pipeline_metalrt_motion != device_metalrt_motion) { - /* wrong combination of metalrt options */ - continue; - } - - if (pipeline->pso_type != PSO_GENERIC) { - if (pipeline->kernels_md5 == device->kernels_md5[PSO_SPECIALIZED_INTERSECT] || - pipeline->kernels_md5 == device->kernels_md5[PSO_SPECIALIZED_SHADE]) { - best_pipeline = pipeline.get(); + for (auto &candidate : pipelines[kernel]) { + if (candidate->loaded && + candidate->kernels_md5 == device->kernels_md5[candidate->pso_type]) { + /* Replace existing match if candidate is more specialized. */ + if (!best_match || candidate->pso_type > best_match->pso_type) { + best_match = candidate.get(); } } - else if (!best_pipeline) { - best_pipeline = pipeline.get(); - } } } - if (!best_pipeline) { - std::this_thread::sleep_for(std::chrono::milliseconds(100)); + if (best_match) { + if (best_match->usage_count == 0 && best_match->pso_type != PSO_GENERIC) { + metal_printf("Swapping in %s version of %s\n", + kernel_type_as_string(best_match->pso_type), + device_kernel_as_string(kernel)); + } + best_match->usage_count += 1; + return best_match; } - } - 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)); + /* Spin until a matching kernel is loaded, or we're shutting down. */ + std::this_thread::sleep_for(std::chrono::milliseconds(100)); } - best_pipeline->usage_count += 1; - - return best_pipeline; + return nullptr; } bool MetalKernelPipeline::should_use_binary_archive() const @@ -570,18 +539,14 @@ void MetalKernelPipeline::compile() NSArray *table_functions[METALRT_TABLE_NUM] = {nil}; NSArray *linked_functions = nil; - bool metalrt_hair = use_metalrt && (metalrt_features & KERNEL_FEATURE_HAIR); - bool metalrt_hair_thick = use_metalrt && (metalrt_features & KERNEL_FEATURE_HAIR_THICK); - bool metalrt_pointcloud = use_metalrt && (metalrt_features & KERNEL_FEATURE_POINTCLOUD); - if (use_metalrt) { id curve_intersect_default = nil; id curve_intersect_shadow = nil; id point_intersect_default = nil; id point_intersect_shadow = nil; - if (metalrt_hair) { + if (kernel_features & KERNEL_FEATURE_HAIR) { /* Add curve intersection programs. */ - if (metalrt_hair_thick) { + if (kernel_features & KERNEL_FEATURE_HAIR_THICK) { /* Slower programs for thick hair since that also slows down ribbons. * Ideally this should not be needed. */ curve_intersect_default = rt_intersection_function[METALRT_FUNC_CURVE_ALL]; @@ -592,7 +557,7 @@ void MetalKernelPipeline::compile() curve_intersect_shadow = rt_intersection_function[METALRT_FUNC_CURVE_RIBBON_SHADOW]; } } - if (metalrt_pointcloud) { + if (kernel_features & KERNEL_FEATURE_POINTCLOUD) { point_intersect_default = rt_intersection_function[METALRT_FUNC_POINT]; point_intersect_shadow = rt_intersection_function[METALRT_FUNC_POINT_SHADOW]; } @@ -682,15 +647,6 @@ void MetalKernelPipeline::compile() local_md5.append((uint8_t *)&this->threads_per_threadgroup, sizeof(this->threads_per_threadgroup)); - string options; - if (use_metalrt && kernel_has_intersection(device_kernel)) { - /* incorporate any MetalRT specializations into the archive name */ - options += string_printf(".hair_%d.hair_thick_%d.pointcloud_%d", - metalrt_hair ? 1 : 0, - metalrt_hair_thick ? 1 : 0, - metalrt_pointcloud ? 1 : 0); - } - /* Replace non-alphanumerical characters with underscores. */ string device_name = [mtlDevice.name UTF8String]; for (char &c : device_name) { @@ -702,7 +658,7 @@ void MetalKernelPipeline::compile() metalbin_name = device_name; metalbin_name = path_join(metalbin_name, device_kernel_as_string(device_kernel)); metalbin_name = path_join(metalbin_name, kernel_type_as_string(pso_type)); - metalbin_name = path_join(metalbin_name, local_md5.get_hex() + options + ".bin"); + metalbin_name = path_join(metalbin_name, local_md5.get_hex() + ".bin"); metalbin_path = path_cache_get(path_join("kernels", metalbin_name)); path_create_directories(metalbin_path); @@ -860,16 +816,15 @@ void MetalDeviceKernels::wait_for_all() } } -bool MetalDeviceKernels::any_specialization_happening_now() +int MetalDeviceKernels::num_incomplete_specialization_requests() { /* Return true if any ShaderCaches have ongoing specialization requests (typically there will be * only 1). */ + int total = 0; for (int i = 0; i < g_shaderCacheCount; i++) { - if (g_shaderCache[i].second->incomplete_specialization_requests > 0) { - return true; - } + total += g_shaderCache[i].second->incomplete_specialization_requests; } - return false; + return total; } int MetalDeviceKernels::get_loaded_kernel_count(MetalDevice const *device, diff --git a/intern/cycles/session/session.cpp b/intern/cycles/session/session.cpp index 9e7e0e6e2a4..581cb474985 100644 --- a/intern/cycles/session/session.cpp +++ b/intern/cycles/session/session.cpp @@ -706,6 +706,12 @@ void Session::update_status_time(bool show_pause, bool show_done) string_printf("Sample %d/%d", current_sample, num_samples)); } + /* Append any device-specific status (such as background kernel optimization) */ + string device_status; + if (device->is_ready(device_status) && !device_status.empty()) { + substatus += string_printf(" (%s)", device_status.c_str()); + } + /* TODO(sergey): Denoising status from the path trace. */ if (show_pause) { diff --git a/source/blender/blenkernel/BKE_node_runtime.hh b/source/blender/blenkernel/BKE_node_runtime.hh index c5322b7c6ba..e620b1b980a 100644 --- a/source/blender/blenkernel/BKE_node_runtime.hh +++ b/source/blender/blenkernel/BKE_node_runtime.hh @@ -6,6 +6,7 @@ #include #include "BLI_cache_mutex.hh" +#include "BLI_math_vector_types.hh" #include "BLI_multi_value_map.hh" #include "BLI_resource_scope.hh" #include "BLI_utility_mixins.hh" @@ -150,6 +151,13 @@ class bNodeTreeRuntime : NonCopyable, NonMovable { Vector root_frames; Vector interface_inputs; Vector interface_outputs; + + /** + * The location of all sockets in the tree, calculated while drawing the nodes. + * Indexed with #bNodeSocket::index_in_tree(). In the node tree's "world space" + * (the same as #bNode::runtime::totr). + */ + Vector all_socket_locations; }; /** diff --git a/source/blender/blenkernel/intern/gpencil_geom.cc b/source/blender/blenkernel/intern/gpencil_geom.cc index 69dfae402bd..742a00a01f5 100644 --- a/source/blender/blenkernel/intern/gpencil_geom.cc +++ b/source/blender/blenkernel/intern/gpencil_geom.cc @@ -493,7 +493,13 @@ bool BKE_gpencil_stroke_sample(bGPdata *gpd, copy_v3_v3(&pt2->x, last_coord); new_pt[i].pressure = pt[0].pressure; new_pt[i].strength = pt[0].strength; - memcpy(new_pt[i].vert_color, pt[0].vert_color, sizeof(float[4])); + copy_v3_v3(&pt2->x, last_coord); + new_pt[i].pressure = pt[0].pressure; + new_pt[i].strength = pt[0].strength; + new_pt[i].uv_fac = pt[0].uv_fac; + new_pt[i].uv_rot = pt[0].uv_rot; + copy_v2_v2(new_pt[i].uv_fill, pt[0].uv_fill); + copy_v4_v4(new_pt[i].vert_color, pt[0].vert_color); if (select) { new_pt[i].flag |= GP_SPOINT_SELECT; } diff --git a/source/blender/editors/space_node/drawnode.cc b/source/blender/editors/space_node/drawnode.cc index efe3adb2fa5..ceb1d78a941 100644 --- a/source/blender/editors/space_node/drawnode.cc +++ b/source/blender/editors/space_node/drawnode.cc @@ -2039,9 +2039,11 @@ static NodeLinkDrawConfig nodelink_get_draw_config(const bContext &C, draw_config.th_col2 = th_col2; draw_config.th_col3 = th_col3; + const bNodeTree &node_tree = *snode.edittree; + draw_config.dim_factor = selected ? 1.0f : node_link_dim_factor( - snode.runtime->all_socket_locations, v2d, link); + node_tree.runtime->all_socket_locations, v2d, link); bTheme *btheme = UI_GetTheme(); draw_config.dash_alpha = btheme->space_node.dash_alpha; @@ -2063,24 +2065,21 @@ static NodeLinkDrawConfig nodelink_get_draw_config(const bContext &C, if (snode.overlay.flag & SN_OVERLAY_SHOW_OVERLAYS && snode.overlay.flag & SN_OVERLAY_SHOW_WIRE_COLORS) { PointerRNA from_node_ptr, to_node_ptr; - RNA_pointer_create((ID *)snode.edittree, &RNA_Node, link.fromnode, &from_node_ptr); - RNA_pointer_create((ID *)snode.edittree, &RNA_Node, link.tonode, &to_node_ptr); + RNA_pointer_create((ID *)&node_tree, &RNA_Node, link.fromnode, &from_node_ptr); + RNA_pointer_create((ID *)&node_tree, &RNA_Node, link.tonode, &to_node_ptr); if (link.fromsock) { - node_socket_color_get( - C, *snode.edittree, from_node_ptr, *link.fromsock, draw_config.start_color); + node_socket_color_get(C, node_tree, from_node_ptr, *link.fromsock, draw_config.start_color); } else { - node_socket_color_get( - C, *snode.edittree, to_node_ptr, *link.tosock, draw_config.start_color); + node_socket_color_get(C, node_tree, to_node_ptr, *link.tosock, draw_config.start_color); } if (link.tosock) { - node_socket_color_get(C, *snode.edittree, to_node_ptr, *link.tosock, draw_config.end_color); + node_socket_color_get(C, node_tree, to_node_ptr, *link.tosock, draw_config.end_color); } else { - node_socket_color_get( - C, *snode.edittree, from_node_ptr, *link.fromsock, draw_config.end_color); + node_socket_color_get(C, node_tree, from_node_ptr, *link.fromsock, draw_config.end_color); } } else { @@ -2167,8 +2166,9 @@ void node_draw_link_bezier(const bContext &C, const int th_col3, const bool selected) { - const std::array points = node_link_bezier_points(snode.runtime->all_socket_locations, - link); + const bNodeTree &node_tree = *snode.edittree; + const std::array points = node_link_bezier_points( + node_tree.runtime->all_socket_locations, link); if (!node_link_draw_is_visible(v2d, points)) { return; } @@ -2227,15 +2227,18 @@ void node_draw_link(const bContext &C, static std::array node_link_bezier_points_dragged(const SpaceNode &snode, const bNodeLink &link) { + const bNodeTree &node_tree = *snode.edittree; const float2 cursor = snode.runtime->cursor * UI_DPI_FAC; std::array points; points[0] = link.fromsock ? - socket_link_connection_location( - snode.runtime->all_socket_locations, *link.fromnode, *link.fromsock, link) : + socket_link_connection_location(node_tree.runtime->all_socket_locations, + *link.fromnode, + *link.fromsock, + link) : cursor; points[3] = link.tosock ? socket_link_connection_location( - snode.runtime->all_socket_locations, *link.tonode, *link.tosock, link) : + node_tree.runtime->all_socket_locations, *link.tonode, *link.tosock, link) : cursor; calculate_inner_link_bezier_points(points); return points; diff --git a/source/blender/editors/space_node/node_add.cc b/source/blender/editors/space_node/node_add.cc index 05b481035ba..473ae4a6f83 100644 --- a/source/blender/editors/space_node/node_add.cc +++ b/source/blender/editors/space_node/node_add.cc @@ -138,7 +138,7 @@ static int add_reroute_exec(bContext *C, wmOperator *op) const ARegion ®ion = *CTX_wm_region(C); SpaceNode &snode = *CTX_wm_space_node(C); bNodeTree &ntree = *snode.edittree; - const Span socket_locations = snode.runtime->all_socket_locations; + const Span socket_locations = ntree.runtime->all_socket_locations; Vector path; RNA_BEGIN (op->ptr, itemptr, "path") { diff --git a/source/blender/editors/space_node/node_draw.cc b/source/blender/editors/space_node/node_draw.cc index 31dc2695c97..44d66422f10 100644 --- a/source/blender/editors/space_node/node_draw.cc +++ b/source/blender/editors/space_node/node_draw.cc @@ -268,6 +268,9 @@ void node_sort(bNodeTree &ntree) ntree.runtime->nodes_by_id.add_new(sort_nodes[i]); sort_nodes[i]->runtime->index_in_tree = i; } + + /* Nodes have been reordered; the socket locations are invalid until the node tree is redrawn. */ + ntree.runtime->all_socket_locations.clear(); } static Array node_uiblocks_init(const bContext &C, const Span nodes) @@ -3178,16 +3181,16 @@ static void draw_nodetree(const bContext &C, else if (ntree.type == NTREE_COMPOSIT) { tree_draw_ctx.used_by_realtime_compositor = realtime_compositor_is_in_use(C); } - snode->runtime->all_socket_locations.reinitialize(ntree.all_sockets().size()); + ntree.runtime->all_socket_locations.reinitialize(ntree.all_sockets().size()); node_update_nodetree( - C, tree_draw_ctx, ntree, nodes, blocks, snode->runtime->all_socket_locations); + C, tree_draw_ctx, ntree, nodes, blocks, ntree.runtime->all_socket_locations); node_draw_nodetree(C, tree_draw_ctx, region, *snode, ntree, - snode->runtime->all_socket_locations, + ntree.runtime->all_socket_locations, nodes, blocks, parent_key); diff --git a/source/blender/editors/space_node/node_edit.cc b/source/blender/editors/space_node/node_edit.cc index 54dc0772c96..79473957f33 100644 --- a/source/blender/editors/space_node/node_edit.cc +++ b/source/blender/editors/space_node/node_edit.cc @@ -1129,13 +1129,14 @@ bNodeSocket *node_find_indicated_socket(SpaceNode &snode, rctf rect; const float size_sock_padded = NODE_SOCKSIZE + 4; - snode.edittree->ensure_topology_cache(); - const Span socket_locations = snode.runtime->all_socket_locations; - if (socket_locations.size() != snode.edittree->all_sockets().size()) { + bNodeTree &node_tree = *snode.edittree; + node_tree.ensure_topology_cache(); + const Span socket_locations = node_tree.runtime->all_socket_locations; + if (socket_locations.size() != node_tree.all_sockets().size()) { /* Sockets haven't been drawn yet, e.g. when the file is currently opening. */ return nullptr; } - const Span nodes = snode.edittree->all_nodes(); + const Span nodes = node_tree.all_nodes(); if (nodes.is_empty()) { return nullptr; } diff --git a/source/blender/editors/space_node/node_intern.hh b/source/blender/editors/space_node/node_intern.hh index b8cb4a4e9ae..11e64bb2f4c 100644 --- a/source/blender/editors/space_node/node_intern.hh +++ b/source/blender/editors/space_node/node_intern.hh @@ -79,12 +79,6 @@ struct bNodeLinkDrag { }; struct SpaceNode_Runtime { - /** - * The location of all sockets in the tree, calculated while drawing the nodes. - * To be indexed with #bNodeSocket::index_in_tree(). - */ - Vector all_socket_locations; - float aspect; /** Mouse position for drawing socket-less links and adding nodes. */ diff --git a/source/blender/editors/space_node/node_relationships.cc b/source/blender/editors/space_node/node_relationships.cc index 73cc64bbf61..97ab6b7786c 100644 --- a/source/blender/editors/space_node/node_relationships.cc +++ b/source/blender/editors/space_node/node_relationships.cc @@ -121,7 +121,11 @@ static void pick_input_link_by_link_intersect(const bContext &C, const float2 &cursor) { SpaceNode *snode = CTX_wm_space_node(&C); - const Span socket_locations = snode->runtime->all_socket_locations; + bNodeTree &node_tree = *snode->edittree; + const Span socket_locations = node_tree.runtime->all_socket_locations; + if (socket_locations.is_empty()) { + return; + } float2 drag_start; RNA_float_get_array(op.ptr, "drag_start", drag_start); @@ -132,7 +136,7 @@ static void pick_input_link_by_link_intersect(const bContext &C, const float cursor_link_touch_distance = 12.5f * UI_DPI_FAC; bNodeLink *link_to_pick = nullptr; - clear_picking_highlight(&snode->edittree->links); + clear_picking_highlight(&node_tree.links); for (bNodeLink *link : socket->directly_linked_links()) { /* Test if the cursor is near a link. */ std::array coords; @@ -643,7 +647,7 @@ static int view_socket(const bContext &C, } if (viewer_node == nullptr) { const float2 socket_location = - snode.runtime->all_socket_locations[bsocket_to_view.index_in_tree()]; + btree.runtime->all_socket_locations[bsocket_to_view.index_in_tree()]; const int viewer_type = get_default_viewer_type(&C); const float2 location{socket_location.x / UI_DPI_FAC + 100, socket_location.y / UI_DPI_FAC}; viewer_node = add_static_node(C, viewer_type, location); @@ -1072,7 +1076,12 @@ static void node_link_cancel(bContext *C, wmOperator *op) static void node_link_find_socket(bContext &C, wmOperator &op, const float2 &cursor) { SpaceNode &snode = *CTX_wm_space_node(&C); + bNodeTree &node_tree = *snode.edittree; bNodeLinkDrag &nldrag = *static_cast(op.customdata); + const Span socket_locations = node_tree.runtime->all_socket_locations; + if (socket_locations.is_empty()) { + return; + } if (nldrag.in_out == SOCK_OUT) { if (bNodeSocket *tsock = node_find_indicated_socket(snode, cursor, SOCK_IN)) { @@ -1103,8 +1112,7 @@ static void node_link_find_socket(bContext &C, wmOperator &op, const float2 &cur continue; } if (tsock && tsock->is_multi_input()) { - sort_multi_input_socket_links_with_drag( - snode.runtime->all_socket_locations, *tsock, link, cursor); + sort_multi_input_socket_links_with_drag(socket_locations, *tsock, link, cursor); } } } @@ -1477,7 +1485,7 @@ static int cut_links_exec(bContext *C, wmOperator *op) bNodeTree &node_tree = *snode.edittree; node_tree.ensure_topology_cache(); - const Span socket_locations = snode.runtime->all_socket_locations; + const Span socket_locations = node_tree.runtime->all_socket_locations; Set links_to_remove; LISTBASE_FOREACH (bNodeLink *, link, &node_tree.links) { @@ -1563,7 +1571,7 @@ static int mute_links_exec(bContext *C, wmOperator *op) SpaceNode &snode = *CTX_wm_space_node(C); const ARegion ®ion = *CTX_wm_region(C); bNodeTree &ntree = *snode.edittree; - const Span socket_locations = snode.runtime->all_socket_locations; + const Span socket_locations = ntree.runtime->all_socket_locations; Vector path; RNA_BEGIN (op->ptr, itemptr, "path") { @@ -2046,7 +2054,10 @@ void node_insert_on_link_flags_set(SpaceNode &snode, const ARegion ®ion) { bNodeTree &node_tree = *snode.edittree; node_tree.ensure_topology_cache(); - const Span socket_locations = snode.runtime->all_socket_locations; + const Span socket_locations = node_tree.runtime->all_socket_locations; + if (socket_locations.is_empty()) { + return; + } node_insert_on_link_flags_clear(node_tree);