diff --git a/release/datafiles/userdef/userdef_default.c b/release/datafiles/userdef/userdef_default.c index e81d5a32c26..dbf9f37a22e 100644 --- a/release/datafiles/userdef/userdef_default.c +++ b/release/datafiles/userdef/userdef_default.c @@ -116,7 +116,8 @@ const UserDef U_default = { #else .gpu_backend = GPU_BACKEND_OPENGL, #endif - .max_shader_compilation_subprocesses = 0, + .gpu_shader_workers = 0, + .shader_compilation_method = USER_SHADER_COMPILE_THREAD, /** Initialized by: #BKE_studiolight_default. */ .light_param = {{0}}, diff --git a/scripts/modules/rna_manual_reference.py b/scripts/modules/rna_manual_reference.py index e6692e490f3..0ca5a63ead4 100644 --- a/scripts/modules/rna_manual_reference.py +++ b/scripts/modules/rna_manual_reference.py @@ -30,7 +30,6 @@ url_manual_mapping = ( ("bpy.types.clothcollisionsettings.vertex_group_object_collisions*", "physics/cloth/settings/collisions.html#bpy-types-clothcollisionsettings-vertex-group-object-collisions"), ("bpy.types.gpencilsculptsettings.use_automasking_material_active*", "grease_pencil/modes/sculpting/introduction.html#bpy-types-gpencilsculptsettings-use-automasking-material-active"), ("bpy.types.gpencilsculptsettings.use_automasking_material_stroke*", "grease_pencil/modes/sculpting/introduction.html#bpy-types-gpencilsculptsettings-use-automasking-material-stroke"), - ("bpy.types.preferencessystem.max_shader_compilation_subprocesses*", "editors/preferences/system.html#bpy-types-preferencessystem-max-shader-compilation-subprocesses"), ("bpy.types.cycleslightsettings.use_multiple_importance_sampling*", "render/cycles/light_settings.html#bpy-types-cycleslightsettings-use-multiple-importance-sampling"), ("bpy.types.fluiddomainsettings.sndparticle_potential_max_energy*", "physics/fluid/type/domain/liquid/particles.html#bpy-types-fluiddomainsettings-sndparticle-potential-max-energy"), ("bpy.types.fluiddomainsettings.sndparticle_potential_min_energy*", "physics/fluid/type/domain/liquid/particles.html#bpy-types-fluiddomainsettings-sndparticle-potential-min-energy"), @@ -80,6 +79,7 @@ url_manual_mapping = ( ("bpy.types.preferencesedit.grease_pencil_euclidean_distance*", "editors/preferences/editing.html#bpy-types-preferencesedit-grease-pencil-euclidean-distance"), ("bpy.types.preferencesedit.grease_pencil_manhattan_distance*", "editors/preferences/editing.html#bpy-types-preferencesedit-grease-pencil-manhattan-distance"), ("bpy.types.preferencesinput.mouse_emulate_3_button_modifier*", "editors/preferences/input.html#bpy-types-preferencesinput-mouse-emulate-3-button-modifier"), + ("bpy.types.preferencessystem.max_shader_compilation_workers*", "editors/preferences/system.html#bpy-types-preferencessystem-max-shader-compilation-workers"), ("bpy.types.brushgpencilsettings.use_stroke_random_strength*", "grease_pencil/modes/draw/brushes/draw.html#bpy-types-brushgpencilsettings-use-stroke-random-strength"), ("bpy.types.clothsettings.vertex_group_structural_stiffness*", "physics/cloth/settings/property_weights.html#bpy-types-clothsettings-vertex-group-structural-stiffness"), ("bpy.types.cyclesrendersettings.film_transparent_roughness*", "render/cycles/render_settings/film.html#bpy-types-cyclesrendersettings-film-transparent-roughness"), diff --git a/scripts/startup/bl_ui/space_spreadsheet.py b/scripts/startup/bl_ui/space_spreadsheet.py index 67b82c4633f..9d4e9da5448 100644 --- a/scripts/startup/bl_ui/space_spreadsheet.py +++ b/scripts/startup/bl_ui/space_spreadsheet.py @@ -23,7 +23,8 @@ class SPREADSHEET_HT_header(bpy.types.Header): row.prop(space, "use_filter", toggle=True, icon='FILTER', icon_only=True) def selection_filter_available(self, space): - root_context = space.viewer_path.path[0] + if (root_context := next(iter(space.viewer_path.path), None)) is None: + return False if root_context.type != 'ID': return False if not isinstance(root_context.id, bpy.types.Object): diff --git a/scripts/startup/bl_ui/space_userpref.py b/scripts/startup/bl_ui/space_userpref.py index 9952ca2498f..71e2e251a28 100644 --- a/scripts/startup/bl_ui/space_userpref.py +++ b/scripts/startup/bl_ui/space_userpref.py @@ -809,8 +809,11 @@ class USERPREF_PT_system_memory(SystemPanel, CenterAlignMixIn, Panel): if sys.platform != "darwin": layout.separator() - col = layout.column() - col.prop(system, "max_shader_compilation_subprocesses") + col = layout.column(align=True) + col.active = system.gpu_backend != 'VULKAN' + col.row().prop(system, "shader_compilation_method", expand=True) + label = "Threads" if system.shader_compilation_method == 'THREAD' else "Subprocesses" + col.prop(system, "gpu_shader_workers", text=label) class USERPREF_PT_system_video_sequencer(SystemPanel, CenterAlignMixIn, Panel): diff --git a/source/blender/blenloader/intern/versioning_userdef.cc b/source/blender/blenloader/intern/versioning_userdef.cc index afa4b9197b4..ba1d39df4b6 100644 --- a/source/blender/blenloader/intern/versioning_userdef.cc +++ b/source/blender/blenloader/intern/versioning_userdef.cc @@ -1496,6 +1496,12 @@ void blo_do_versions_userdef(UserDef *userdef) } } + if (!USER_VERSION_ATLEAST(405, 86)) { + if (userdef->gpu_shader_workers > 0) { + userdef->shader_compilation_method = USER_SHADER_COMPILE_SUBPROCESS; + } + } + /** * Always bump subversion in BKE_blender_version.h when adding versioning * code here, and wrap it inside a USER_VERSION_ATLEAST check. diff --git a/source/blender/draw/engines/eevee/eevee_instance.cc b/source/blender/draw/engines/eevee/eevee_instance.cc index 509c49ce817..d566e1870e8 100644 --- a/source/blender/draw/engines/eevee/eevee_instance.cc +++ b/source/blender/draw/engines/eevee/eevee_instance.cc @@ -722,11 +722,14 @@ void Instance::draw_viewport() } if (materials.queued_shaders_count > 0) { info_append_i18n("Compiling shaders ({} remaining)", materials.queued_shaders_count); - if (!GPU_use_parallel_compilation() && - GPU_type_matches_ex(GPU_DEVICE_ANY, GPU_OS_ANY, GPU_DRIVER_ANY, GPU_BACKEND_OPENGL)) + if (GPU_backend_get_type() == GPU_BACKEND_OPENGL && !GPU_use_subprocess_compilation() && + /* Only recommend subprocesses when there is known gain. */ + (GPU_type_matches(GPU_DEVICE_NVIDIA, GPU_OS_ANY, GPU_DRIVER_ANY) || + GPU_type_matches(GPU_DEVICE_INTEL, GPU_OS_WIN, GPU_DRIVER_ANY) || + GPU_type_matches(GPU_DEVICE_ATI, GPU_OS_ANY, GPU_DRIVER_OFFICIAL))) { info_append_i18n( - "Increasing Preferences > System > Max Shader Compilation Subprocesses may improve " + "Setting Preferences > System > Shader Compilation Method to Subprocess might improve " "compilation time."); } } diff --git a/source/blender/editors/grease_pencil/intern/grease_pencil_utils.cc b/source/blender/editors/grease_pencil/intern/grease_pencil_utils.cc index 5e7b192ff49..402ef743501 100644 --- a/source/blender/editors/grease_pencil/intern/grease_pencil_utils.cc +++ b/source/blender/editors/grease_pencil/intern/grease_pencil_utils.cc @@ -347,23 +347,30 @@ float3 DrawingPlacement::try_project_depth(const float2 co) const return proj_point; } -float3 DrawingPlacement::project(const float2 co) const +float3 DrawingPlacement::project(const float2 co, bool &r_clipped) const { float3 proj_point; if (depth_ == DrawingPlacementDepth::Surface) { /* Project using the viewport depth cache. */ proj_point = this->try_project_depth(co); + r_clipped = false; } else { if (placement_plane_) { - ED_view3d_win_to_3d_on_plane(region_, *placement_plane_, co, false, proj_point); + r_clipped = !ED_view3d_win_to_3d_on_plane(region_, *placement_plane_, co, true, proj_point); } else { ED_view3d_win_to_3d(view3d_, region_, placement_loc_, co, proj_point); + r_clipped = false; } } return math::transform_point(world_space_to_layer_space_, proj_point); } +float3 DrawingPlacement::project(const float2 co) const +{ + [[maybe_unused]] bool clipped_unused; + return this->project(co, clipped_unused); +} float3 DrawingPlacement::project_with_shift(const float2 co) const { diff --git a/source/blender/editors/include/ED_grease_pencil.hh b/source/blender/editors/include/ED_grease_pencil.hh index fea58ec2ec9..83a6ac514a9 100644 --- a/source/blender/editors/include/ED_grease_pencil.hh +++ b/source/blender/editors/include/ED_grease_pencil.hh @@ -175,6 +175,7 @@ class DrawingPlacement { /** * Projects a screen space coordinate to the local drawing space. */ + float3 project(float2 co, bool &clipped) const; float3 project(float2 co) const; void project(Span src, MutableSpan dst) const; /** diff --git a/source/blender/editors/include/ED_node.hh b/source/blender/editors/include/ED_node.hh index 476d35d34ca..9ce12992737 100644 --- a/source/blender/editors/include/ED_node.hh +++ b/source/blender/editors/include/ED_node.hh @@ -14,6 +14,7 @@ #include "BKE_compute_context_cache_fwd.hh" #include "NOD_geometry_nodes_closure_location.hh" +#include "NOD_nested_node_id.hh" #include "ED_node_c.hh" @@ -82,7 +83,10 @@ void std_node_socket_colors_get(int socket_type, float *r_color); /** * Find the nested node id of a currently visible node in the root tree. */ -std::optional find_nested_node_id_in_root(const SpaceNode &snode, const bNode &node); +std::optional find_nested_node_id_in_root(const SpaceNode &snode, + const bNode &node); +std::optional find_nested_node_id_in_root( + const bNodeTree &root_tree, const ComputeContext *compute_context, const int node_id); struct ObjectAndModifier { const Object *object; @@ -112,6 +116,9 @@ bool node_editor_is_for_geometry_nodes_modifier(const SpaceNode &snode, bke::ComputeContextCache &compute_context_cache, const bNodeSocket &socket); +[[nodiscard]] const ComputeContext *compute_context_for_edittree_node( + const SpaceNode &snode, bke::ComputeContextCache &compute_context_cache, const bNode &node); + /** * Attempts to find a compute context that the closure is evaluated in. If none is found, null is * returned. If multiple are found, it currently picks the first one it finds which is somewhat diff --git a/source/blender/editors/sculpt_paint/paint_cursor.cc b/source/blender/editors/sculpt_paint/paint_cursor.cc index 9ed9a8444b7..094113b51b6 100644 --- a/source/blender/editors/sculpt_paint/paint_cursor.cc +++ b/source/blender/editors/sculpt_paint/paint_cursor.cc @@ -542,6 +542,27 @@ static int project_brush_radius(ViewContext *vc, float radius, const float locat return 0; } +static int project_brush_radius_grease_pencil(ViewContext *vc, + const float radius, + const float3 world_location, + const float4x4 &to_world) +{ + const float2 xy_delta = float2(1.0f, 0.0f); + + bool z_flip; + const float zfac = ED_view3d_calc_zfac_ex(vc->rv3d, world_location, &z_flip); + if (z_flip) { + /* Location is behind camera. Return 0 to make the cursor disappear. */ + return 0; + } + float3 delta; + ED_view3d_win_to_delta(vc->region, xy_delta, zfac, delta); + + const float scale = math::length( + math::transform_direction(to_world, float3(math::numbers::inv_sqrt3))); + return math::safe_divide(scale * radius, math::length(delta)); +} + /* Draw an overlay that shows what effect the brush's texture will * have on brush strength. */ static bool paint_draw_tex_overlay(UnifiedPaintSettings *ups, @@ -1584,11 +1605,18 @@ static void grease_pencil_brush_cursor_draw(PaintCursorContext &pcontext) const bke::greasepencil::Layer *layer = grease_pencil->get_active_layer(); const ed::greasepencil::DrawingPlacement placement( *pcontext.scene, *pcontext.region, *pcontext.vc.v3d, *object, layer); - const float3 location = math::transform_point( - placement.to_world_space(), - placement.project(float2(pcontext.mval.x, pcontext.mval.y))); - pcontext.pixel_radius = project_brush_radius( - &pcontext.vc, brush->unprojected_radius, location); + const float2 coordinate = float2(pcontext.mval.x - pcontext.region->winrct.xmin, + pcontext.mval.y - pcontext.region->winrct.ymin); + bool clipped = false; + const float3 pos = placement.project(coordinate, clipped); + if (!clipped) { + const float3 world_location = math::transform_point(placement.to_world_space(), pos); + pcontext.pixel_radius = project_brush_radius_grease_pencil( + &pcontext.vc, brush->unprojected_radius, world_location, placement.to_world_space()); + } + else { + pcontext.pixel_radius = 0; + } brush->size = std::max(pcontext.pixel_radius, 1); } else { diff --git a/source/blender/editors/space_node/link_drag_search.cc b/source/blender/editors/space_node/link_drag_search.cc index 51fa1528fe4..7f1be80b741 100644 --- a/source/blender/editors/space_node/link_drag_search.cc +++ b/source/blender/editors/space_node/link_drag_search.cc @@ -295,6 +295,9 @@ static void gather_socket_link_operations(const bContext &C, } const bNodeTreeInterfaceSocket &interface_socket = reinterpret_cast(item); + if (!(interface_socket.flag & NODE_INTERFACE_SOCKET_INPUT)) { + return true; + } { const bke::bNodeSocketType *from_typeinfo = bke::node_socket_type_find( interface_socket.socket_type); diff --git a/source/blender/editors/space_node/space_node.cc b/source/blender/editors/space_node/space_node.cc index be03f905179..b04369adfa9 100644 --- a/source/blender/editors/space_node/space_node.cc +++ b/source/blender/editors/space_node/space_node.cc @@ -268,51 +268,53 @@ float2 space_node_group_offset(const SpaceNode &snode) return float2(0); } -static const bNode *group_node_by_name(const bNodeTree &ntree, StringRef name) -{ - for (const bNode *node : ntree.group_nodes()) { - if (node->name == name) { - return node; - } - } - return nullptr; -} - -std::optional find_nested_node_id_in_root(const SpaceNode &snode, const bNode &query_node) +std::optional find_nested_node_id_in_root(const SpaceNode &snode, + const bNode &query_node) { BLI_assert(snode.edittree->runtime->nodes_by_id.contains(const_cast(&query_node))); + bke::ComputeContextCache compute_context_cache; + const ComputeContext *compute_context = compute_context_for_edittree_node( + snode, compute_context_cache, query_node); + if (!compute_context) { + return {}; + } + return find_nested_node_id_in_root(*snode.nodetree, compute_context, query_node.identifier); +} - std::optional id_in_node; - const char *group_node_name = nullptr; - const bNode *node = &query_node; - LISTBASE_FOREACH_BACKWARD (const bNodeTreePath *, path, &snode.treepath) { - const bNodeTree *ntree = path->nodetree; - ntree->ensure_topology_cache(); - if (group_node_name) { - node = group_node_by_name(*ntree, group_node_name); +std::optional find_nested_node_id_in_root( + const bNodeTree &root_tree, const ComputeContext *compute_context, const int node_id) +{ + nodes::FoundNestedNodeID found; + Vector node_ids; + for (const ComputeContext *context = compute_context; context != nullptr; + context = context->parent()) + { + if (const auto *node_context = dynamic_cast(context)) { + node_ids.append(node_context->node_id()); } - bool found = false; - for (const bNestedNodeRef &ref : ntree->nested_node_refs_span()) { - if (node->is_group()) { - if (ref.path.node_id == node->identifier && ref.path.id_in_node == id_in_node) { - group_node_name = path->node_name; - id_in_node = ref.id; - found = true; - break; - } - } - else if (ref.path.node_id == node->identifier) { - group_node_name = path->node_name; - id_in_node = ref.id; - found = true; - break; - } + else if (dynamic_cast(context) != nullptr) { + found.is_in_loop = true; } - if (!found) { - return std::nullopt; + else if (dynamic_cast(context) != nullptr) { + found.is_in_simulation = true; + } + else if (dynamic_cast(context) != + nullptr) + { + found.is_in_loop = true; + } + else if (dynamic_cast(context) != nullptr) { + found.is_in_closure = true; } } - return id_in_node; + std::reverse(node_ids.begin(), node_ids.end()); + node_ids.append(node_id); + const bNestedNodeRef *nested_node_ref = root_tree.nested_node_ref_from_node_id_path(node_ids); + if (nested_node_ref == nullptr) { + return std::nullopt; + } + found.id = nested_node_ref->id; + return found; } std::optional get_modifier_for_node_editor(const SpaceNode &snode) @@ -645,6 +647,22 @@ const ComputeContext *compute_context_for_edittree_socket( return compute_context_for_zones(zone_stack, compute_context_cache, context); } +const ComputeContext *compute_context_for_edittree_node( + const SpaceNode &snode, bke::ComputeContextCache &compute_context_cache, const bNode &node) +{ + const ComputeContext *context = compute_context_for_edittree(snode, compute_context_cache); + if (!context) { + return nullptr; + } + const bke::bNodeTreeZones *zones = snode.edittree->zones(); + if (!zones) { + return nullptr; + } + const bke::bNodeTreeZone *zone = zones->get_zone_by_node(node.identifier); + const Vector zone_stack = zones->get_zones_to_enter_from_root(zone); + return compute_context_for_zones(zone_stack, compute_context_cache, context); +} + /* ******************** default callbacks for node space ***************** */ static SpaceLink *node_create(const ScrArea * /*area*/, const Scene * /*scene*/) diff --git a/source/blender/gpu/GPU_capabilities.hh b/source/blender/gpu/GPU_capabilities.hh index f8a36498127..62bb7ab6af6 100644 --- a/source/blender/gpu/GPU_capabilities.hh +++ b/source/blender/gpu/GPU_capabilities.hh @@ -43,7 +43,7 @@ const char *GPU_extension_get(int i); int GPU_texture_size_with_limit(int res); -bool GPU_use_parallel_compilation(); +bool GPU_use_subprocess_compilation(); int GPU_max_parallel_compilations(); bool GPU_stencil_clasify_buffer_workaround(); diff --git a/source/blender/gpu/intern/gpu_capabilities.cc b/source/blender/gpu/intern/gpu_capabilities.cc index d460d1743da..83e89a85070 100644 --- a/source/blender/gpu/intern/gpu_capabilities.cc +++ b/source/blender/gpu/intern/gpu_capabilities.cc @@ -131,9 +131,9 @@ int GPU_max_samplers() return GCaps.max_samplers; } -bool GPU_use_parallel_compilation() +bool GPU_use_subprocess_compilation() { - return GCaps.max_parallel_compilations > 0; + return GCaps.use_subprocess_shader_compilations; } int GPU_max_parallel_compilations() diff --git a/source/blender/gpu/intern/gpu_capabilities_private.hh b/source/blender/gpu/intern/gpu_capabilities_private.hh index 6d5c887efb2..982c009fb9b 100644 --- a/source/blender/gpu/intern/gpu_capabilities_private.hh +++ b/source/blender/gpu/intern/gpu_capabilities_private.hh @@ -63,6 +63,8 @@ struct GPUCapabilities { bool node_link_instancing_workaround = false; bool line_directive_workaround = false; + bool use_subprocess_shader_compilations = false; + /* Vulkan related workarounds. */ bool render_pass_workaround = false; diff --git a/source/blender/gpu/intern/gpu_pass.cc b/source/blender/gpu/intern/gpu_pass.cc index 7845127833e..f78ab33cc26 100644 --- a/source/blender/gpu/intern/gpu_pass.cc +++ b/source/blender/gpu/intern/gpu_pass.cc @@ -165,9 +165,8 @@ bool GPU_pass_should_optimize(GPUPass *pass) return (GPU_backend_get_type() == GPU_BACKEND_METAL) && pass->should_optimize; #if 0 - /* Returns optimization heuristic prepared during initial codegen. - * NOTE: Optimization limited to parallel compilation as it causes CPU stalls otherwise. */ - return pass->should_optimize && GPU_use_parallel_compilation(); + /* Returns optimization heuristic prepared during initial codegen. */ + return pass->should_optimize; #endif } diff --git a/source/blender/gpu/intern/gpu_shader_create_info.cc b/source/blender/gpu/intern/gpu_shader_create_info.cc index 6e10d388888..0bcd518a7c3 100644 --- a/source/blender/gpu/intern/gpu_shader_create_info.cc +++ b/source/blender/gpu/intern/gpu_shader_create_info.cc @@ -574,16 +574,8 @@ bool gpu_shader_create_info_compile(const char *name_starts_with_filter) } } - Vector result; - if (GPU_use_parallel_compilation() == false) { - for (const GPUShaderCreateInfo *info : infos) { - result.append(GPU_shader_create_from_info(info)); - } - } - else { - BatchHandle batch = GPU_shader_batch_create_from_infos(infos); - result = GPU_shader_batch_finalize(batch); - } + BatchHandle batch = GPU_shader_batch_create_from_infos(infos); + Vector result = GPU_shader_batch_finalize(batch); for (int i : result.index_range()) { const ShaderCreateInfo *info = reinterpret_cast(infos[i]); diff --git a/source/blender/gpu/metal/mtl_shader.mm b/source/blender/gpu/metal/mtl_shader.mm index 5c1bed55b60..78d40b466a8 100644 --- a/source/blender/gpu/metal/mtl_shader.mm +++ b/source/blender/gpu/metal/mtl_shader.mm @@ -1551,7 +1551,6 @@ MTLComputePipelineStateInstance *MTLShader::bake_compute_pipeline_state( MTLShaderCompiler::MTLShaderCompiler() : ShaderCompiler(GPU_max_parallel_compilations(), GPUWorker::ContextType::PerThread, true) { - BLI_assert(GPU_use_parallel_compilation()); } Shader *MTLShaderCompiler::compile_shader(const shader::ShaderCreateInfo &info) diff --git a/source/blender/gpu/opengl/gl_backend.cc b/source/blender/gpu/opengl/gl_backend.cc index c0d6b17d219..e2d7ebbfe94 100644 --- a/source/blender/gpu/opengl/gl_backend.cc +++ b/source/blender/gpu/opengl/gl_backend.cc @@ -723,17 +723,66 @@ void GLBackend::capabilities_init() detect_workarounds(); #if BLI_SUBPROCESS_SUPPORT - if (GCaps.max_parallel_compilations == -1) { - GCaps.max_parallel_compilations = std::min(int(U.max_shader_compilation_subprocesses), - BLI_system_thread_count()); - } + GCaps.use_subprocess_shader_compilations = U.shader_compilation_method == + USER_SHADER_COMPILE_SUBPROCESS; +#else + GCaps.use_subprocess_shader_compilations = false; +#endif if (G.debug & G_DEBUG_GPU_RENDERDOC) { /* Avoid crashes on RenderDoc sessions. */ - GCaps.max_parallel_compilations = 0; + GCaps.use_subprocess_shader_compilations = false; } -#else - GCaps.max_parallel_compilations = 0; -#endif + + int thread_count = U.gpu_shader_workers; + + if (thread_count == 0) { + /* Good default based on measurements. */ + + /* Always have at least 1 worker. */ + thread_count = 1; + + if (GCaps.use_subprocess_shader_compilations) { + /* Use reasonable number of worker by default when there are known gains. */ + if (GPU_type_matches(GPU_DEVICE_NVIDIA, GPU_OS_ANY, GPU_DRIVER_OFFICIAL) || + GPU_type_matches(GPU_DEVICE_ATI, GPU_OS_ANY, GPU_DRIVER_OFFICIAL) || + GPU_type_matches(GPU_DEVICE_INTEL, GPU_OS_WIN, GPU_DRIVER_ANY)) + { + /* Subprocess is too costly in memory (>150MB per worker) to have better defaults. */ + thread_count = std::max(1, std::min(4, BLI_system_thread_count() / 2)); + } + } + else if (GPU_type_matches(GPU_DEVICE_NVIDIA, GPU_OS_ANY, GPU_DRIVER_OFFICIAL)) { + /* Best middle ground between memory usage and speedup as Nvidia context memory footprint + * is quite heavy (~25MB). Moreover we have diminishing return after this because of PSO + * compilation blocking the main thread. + * Can be revisited if we find a way to delete the worker thread context after finishing + * compilation, and fix the scheduling bubbles (#139775). */ + thread_count = 4; + } + else if (GPU_type_matches(GPU_DEVICE_ATI, GPU_OS_ANY, GPU_DRIVER_OPENSOURCE) || + GPU_type_matches(GPU_DEVICE_INTEL, GPU_OS_UNIX, GPU_DRIVER_ANY)) + { + /* Mesa has very good compilation time and doesn't block the main thread. + * The memory footprint of the worker context is rather small (<10MB). + * Shader compilation gets much slower as the number of threads increases. */ + thread_count = 8; + } + else if (GPU_type_matches(GPU_DEVICE_ATI, GPU_OS_ANY, GPU_DRIVER_OFFICIAL)) { + /* AMD proprietary driver's context have huge memory footprint (~45MB). + * There is also not much gain from parallelization. */ + thread_count = 1; + } + else if (GPU_type_matches(GPU_DEVICE_INTEL, GPU_OS_WIN, GPU_DRIVER_ANY)) { + /* Intel windows driver offer almost no speedup with parallel compilation. */ + thread_count = 1; + } + } + + /* Allow thread count override option to limit the number of workers and avoid allocating more + * workers than needed. Also ensures that there is always 1 thread available for the UI. */ + int max_thread_count = std::max(1, BLI_system_thread_count() - 1); + + GCaps.max_parallel_compilations = std::min(thread_count, max_thread_count); /* Disable this feature entirely when not debugging. */ if ((G.debug & G_DEBUG_GPU) == 0) { diff --git a/source/blender/gpu/opengl/gl_backend.hh b/source/blender/gpu/opengl/gl_backend.hh index 2fe45d0fa66..9313d1f87cf 100644 --- a/source/blender/gpu/opengl/gl_backend.hh +++ b/source/blender/gpu/opengl/gl_backend.hh @@ -9,10 +9,15 @@ #pragma once #include "GPU_capabilities.hh" +#include "GPU_platform.hh" + #include "gpu_backend.hh" +#include "BLI_threads.h" #include "BLI_vector.hh" +#include "gpu_capabilities_private.hh" + #ifdef WITH_RENDERDOC # include "renderdoc_api.hh" #endif @@ -56,7 +61,7 @@ class GLBackend : public GPUBackend { void init_resources() override { - if (GPU_use_parallel_compilation()) { + if (GCaps.use_subprocess_shader_compilations) { compiler_ = MEM_new(__func__); } else { diff --git a/source/blender/gpu/opengl/gl_shader.hh b/source/blender/gpu/opengl/gl_shader.hh index 2d1257c2380..1c6de5daa66 100644 --- a/source/blender/gpu/opengl/gl_shader.hh +++ b/source/blender/gpu/opengl/gl_shader.hh @@ -198,8 +198,8 @@ class GLShader : public Shader { class GLShaderCompiler : public ShaderCompiler { public: - GLShaderCompiler(uint32_t threads_count = 1) - : ShaderCompiler(threads_count, GPUWorker::ContextType::PerThread, true){}; + GLShaderCompiler() + : ShaderCompiler(GPU_max_parallel_compilations(), GPUWorker::ContextType::PerThread, true){}; virtual void specialize_shader(ShaderSpecialization &specialization) override; }; diff --git a/source/blender/makesdna/DNA_userdef_types.h b/source/blender/makesdna/DNA_userdef_types.h index 13b1600c4c8..71a2c1b5fcf 100644 --- a/source/blender/makesdna/DNA_userdef_types.h +++ b/source/blender/makesdna/DNA_userdef_types.h @@ -487,13 +487,17 @@ typedef struct UserDef { int gpu_preferred_index; uint32_t gpu_preferred_vendor_id; uint32_t gpu_preferred_device_id; - char _pad16[4]; + + /** Max number of parallel shader compilation workers. */ + short gpu_shader_workers; + /** eUserpref_ShaderCompileMethod (OpenGL only). */ + short shader_compilation_method; + + char _pad16[2]; + /** #eGPUBackendType */ short gpu_backend; - /** Max number of parallel shader compilation subprocesses. */ - short max_shader_compilation_subprocesses; - /** Number of samples for FPS display calculations. */ short playback_fps_samples; @@ -1125,6 +1129,11 @@ typedef enum eUserpref_SeqEditorFlags { USER_SEQ_ED_CONNECT_STRIPS_BY_DEFAULT = (1 << 1), } eUserpref_SeqEditorFlags; +typedef enum eUserpref_ShaderCompileMethod { + USER_SHADER_COMPILE_THREAD = 0, + USER_SHADER_COMPILE_SUBPROCESS = 1, +} eUserpref_ShaderCompileMethod; + /* Locale Ids. Auto will try to get local from OS. Our default is English though. */ /** #UserDef.language */ enum { diff --git a/source/blender/makesdna/intern/dna_rename_defs.h b/source/blender/makesdna/intern/dna_rename_defs.h index 40e0553b2cc..690194786d7 100644 --- a/source/blender/makesdna/intern/dna_rename_defs.h +++ b/source/blender/makesdna/intern/dna_rename_defs.h @@ -215,6 +215,7 @@ DNA_STRUCT_RENAME_MEMBER(UVProjectModifierData, num_projectors, projectors_num) DNA_STRUCT_RENAME_MEMBER(UserDef, autokey_flag, keying_flag) DNA_STRUCT_RENAME_MEMBER(UserDef, gp_manhattendist, gp_manhattandist) DNA_STRUCT_RENAME_MEMBER(UserDef, pythondir, pythondir_legacy) +DNA_STRUCT_RENAME_MEMBER(UserDef, max_shader_compilation_subprocesses, gpu_shader_workers) DNA_STRUCT_RENAME_MEMBER(VFont, name, filepath) DNA_STRUCT_RENAME_MEMBER(View3D, far, clip_end) DNA_STRUCT_RENAME_MEMBER(View3D, local_collections_uuid, local_collections_uid) diff --git a/source/blender/makesrna/intern/rna_userdef.cc b/source/blender/makesrna/intern/rna_userdef.cc index c2543906a96..e7e0aa9927c 100644 --- a/source/blender/makesrna/intern/rna_userdef.cc +++ b/source/blender/makesrna/intern/rna_userdef.cc @@ -6349,14 +6349,36 @@ static void rna_def_userdef_system(BlenderRNA *brna) "Preferred device to select during detection (requires restarting " "Blender for changes to take effect)"); - prop = RNA_def_property(srna, "max_shader_compilation_subprocesses", PROP_INT, PROP_NONE); - RNA_def_property_range(prop, 0, INT16_MAX); + prop = RNA_def_property(srna, "gpu_shader_workers", PROP_INT, PROP_NONE); + RNA_def_property_range(prop, 0, 32); RNA_def_property_ui_text(prop, - "Max Shader Compilation Subprocesses", - "Max number of parallel shader compilation subprocesses, " + "Shader Compilation Workers", + "Number of shader compilation threads or subprocesses, " "clamped at the max threads supported by the CPU " "(requires restarting Blender for changes to take effect). " - "Setting it to 0 disables subprocess shader compilation."); + "A higher number increases the RAM usage while reducing " + "compilation time. A value of 0 will use automatic configuration. " + "(OpenGL only)"); + + static const EnumPropertyItem shader_compilation_method_items[] = { + {USER_SHADER_COMPILE_THREAD, "THREAD", 0, "Thread", "Use threads for compiling shaders"}, + {USER_SHADER_COMPILE_SUBPROCESS, + "SUBPROCESS", + 0, + "Subprocess", + "Use subprocesses for compiling shaders"}, + {0, nullptr, 0, nullptr, nullptr}, + }; + + prop = RNA_def_property(srna, "shader_compilation_method", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, shader_compilation_method_items); + RNA_def_property_ui_text(prop, + "Shader Compilation Method", + "Compilation method used for compiling shaders in parallel. " + "Subprocess requires a lot more RAM for each worker " + "but might compile shaders faster on some systems. " + "Requires restarting Blender for changes to take effect. " + "(OpenGL only)"); /* Network. */ diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt index cc44f827003..2176230e60f 100644 --- a/source/blender/nodes/CMakeLists.txt +++ b/source/blender/nodes/CMakeLists.txt @@ -122,6 +122,7 @@ set(SRC NOD_inverse_eval_run.hh NOD_math_functions.hh NOD_multi_function.hh + NOD_nested_node_id.hh NOD_node_declaration.hh NOD_node_extra_info.hh NOD_node_in_compute_context.hh diff --git a/source/blender/nodes/NOD_geometry_nodes_lazy_function.hh b/source/blender/nodes/NOD_geometry_nodes_lazy_function.hh index b4dc788238b..39cfe4a00a7 100644 --- a/source/blender/nodes/NOD_geometry_nodes_lazy_function.hh +++ b/source/blender/nodes/NOD_geometry_nodes_lazy_function.hh @@ -27,6 +27,7 @@ #include "NOD_geometry_nodes_log.hh" #include "NOD_multi_function.hh" +#include "NOD_nested_node_id.hh" #include "BLI_compute_context.hh" #include "BLI_math_quaternion_types.hh" @@ -461,13 +462,6 @@ std::string make_anonymous_attribute_socket_inspection_string(const bNodeSocket std::string make_anonymous_attribute_socket_inspection_string(StringRef node_name, StringRef socket_name); -struct FoundNestedNodeID { - int id; - bool is_in_simulation = false; - bool is_in_loop = false; - bool is_in_closure = false; -}; - std::optional find_nested_node_id(const GeoNodesUserData &user_data, const int node_id); diff --git a/source/blender/nodes/NOD_nested_node_id.hh b/source/blender/nodes/NOD_nested_node_id.hh new file mode 100644 index 00000000000..5bccc267c10 --- /dev/null +++ b/source/blender/nodes/NOD_nested_node_id.hh @@ -0,0 +1,20 @@ +/* SPDX-FileCopyrightText: 2025 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +namespace blender::nodes { + +/** + * Utility struct to store information about a nested node id. Also see #bNestedNodeRef. + * Sometimes these IDs can only be used when they are at the top level and not within zones. + */ +struct FoundNestedNodeID { + int id; + bool is_in_simulation = false; + bool is_in_loop = false; + bool is_in_closure = false; +}; + +} // namespace blender::nodes diff --git a/source/blender/nodes/geometry/include/NOD_geo_bake.hh b/source/blender/nodes/geometry/include/NOD_geo_bake.hh index 4112aa96962..e5644b88add 100644 --- a/source/blender/nodes/geometry/include/NOD_geo_bake.hh +++ b/source/blender/nodes/geometry/include/NOD_geo_bake.hh @@ -111,6 +111,7 @@ struct BakeDrawContext { bool bake_still; bool is_baked; std::optional bake_target; + bool is_bakeable_in_current_context; }; [[nodiscard]] bool get_bake_draw_context(const bContext *C, diff --git a/source/blender/nodes/geometry/nodes/node_geo_bake.cc b/source/blender/nodes/geometry/nodes/node_geo_bake.cc index 29a2b402718..117dad29c4f 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_bake.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_bake.cc @@ -489,6 +489,12 @@ static void node_extra_info(NodeExtraInfoParams ¶ms) if (!get_bake_draw_context(¶ms.C, params.node, ctx)) { return; } + if (!ctx.is_bakeable_in_current_context) { + NodeExtraInfoRow row; + row.text = TIP_("Can't bake in zone"); + row.icon = ICON_ERROR; + params.rows.append(std::move(row)); + } if (ctx.is_baked) { NodeExtraInfoRow row; row.text = get_baked_string(ctx); @@ -503,7 +509,7 @@ static void node_layout(uiLayout *layout, bContext *C, PointerRNA *ptr) if (!get_bake_draw_context(C, node, ctx)) { return; } - + uiLayoutSetActive(layout, ctx.is_bakeable_in_current_context); uiLayoutSetEnabled(layout, ID_IS_EDITABLE(ctx.object)); uiLayout *col = &layout->column(false); { @@ -524,6 +530,7 @@ static void node_layout_ex(uiLayout *layout, bContext *C, PointerRNA *ptr) return; } + uiLayoutSetActive(layout, ctx.is_bakeable_in_current_context); uiLayoutSetEnabled(layout, ID_IS_EDITABLE(ctx.object)); { @@ -631,14 +638,15 @@ bool get_bake_draw_context(const bContext *C, const bNode &node, BakeDrawContext } r_ctx.object = object_and_modifier->object; r_ctx.nmd = object_and_modifier->nmd; - const std::optional bake_id = ed::space_node::find_nested_node_id_in_root(*r_ctx.snode, - *r_ctx.node); + const std::optional bake_id = ed::space_node::find_nested_node_id_in_root( + *r_ctx.snode, *r_ctx.node); if (!bake_id) { return false; } + r_ctx.is_bakeable_in_current_context = !bake_id->is_in_loop && !bake_id->is_in_closure; r_ctx.bake = nullptr; for (const NodesModifierBake &iter_bake : Span(r_ctx.nmd->bakes, r_ctx.nmd->bakes_num)) { - if (iter_bake.id == *bake_id) { + if (iter_bake.id == bake_id->id) { r_ctx.bake = &iter_bake; break; } @@ -653,7 +661,7 @@ bool get_bake_draw_context(const bContext *C, const bNode &node, BakeDrawContext const bke::bake::ModifierCache &cache = *r_ctx.nmd->runtime->cache; std::lock_guard lock{cache.mutex}; if (const std::unique_ptr *node_cache_ptr = - cache.bake_cache_by_id.lookup_ptr(*bake_id)) + cache.bake_cache_by_id.lookup_ptr(bake_id->id)) { const bke::bake::BakeNodeCache &node_cache = **node_cache_ptr; if (!node_cache.bake.frames.is_empty()) { @@ -663,7 +671,7 @@ bool get_bake_draw_context(const bContext *C, const bNode &node, BakeDrawContext } } else if (const std::unique_ptr *node_cache_ptr = - cache.simulation_cache_by_id.lookup_ptr(*bake_id)) + cache.simulation_cache_by_id.lookup_ptr(bake_id->id)) { const bke::bake::SimulationNodeCache &node_cache = **node_cache_ptr; if (!node_cache.bake.frames.is_empty() && diff --git a/source/blender/nodes/geometry/nodes/node_geo_simulation.cc b/source/blender/nodes/geometry/nodes/node_geo_simulation.cc index 0d0bf8013e5..f4f9f616b9c 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_simulation.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_simulation.cc @@ -225,6 +225,7 @@ static void node_layout_ex(uiLayout *layout, bContext *C, PointerRNA *current_no if (!get_bake_draw_context(C, output_node, ctx)) { return; } + uiLayoutSetActive(layout, ctx.is_bakeable_in_current_context); draw_simulation_state(C, layout, ntree, output_node); @@ -865,6 +866,12 @@ static void node_extra_info(NodeExtraInfoParams ¶ms) if (!get_bake_draw_context(¶ms.C, params.node, ctx)) { return; } + if (!ctx.is_bakeable_in_current_context) { + NodeExtraInfoRow row; + row.text = TIP_("Can't bake in zone"); + row.icon = ICON_ERROR; + params.rows.append(std::move(row)); + } if (ctx.is_baked) { NodeExtraInfoRow row; row.text = get_baked_string(ctx); diff --git a/source/blender/nodes/intern/geometry_nodes_lazy_function.cc b/source/blender/nodes/intern/geometry_nodes_lazy_function.cc index b1bf2481b0b..89a068ac5d9 100644 --- a/source/blender/nodes/intern/geometry_nodes_lazy_function.cc +++ b/source/blender/nodes/intern/geometry_nodes_lazy_function.cc @@ -47,6 +47,8 @@ #include "BKE_node_tree_zones.hh" #include "BKE_type_conversions.hh" +#include "ED_node.hh" + #include "FN_lazy_function_graph_executor.hh" #include "DEG_depsgraph_query.hh" @@ -4302,38 +4304,8 @@ void GeoNodesLocalUserData::ensure_tree_logger(const GeoNodesUserData &user_data std::optional find_nested_node_id(const GeoNodesUserData &user_data, const int node_id) { - FoundNestedNodeID found; - Vector node_ids; - for (const ComputeContext *context = user_data.compute_context; context != nullptr; - context = context->parent()) - { - if (const auto *node_context = dynamic_cast(context)) { - node_ids.append(node_context->node_id()); - } - else if (dynamic_cast(context) != nullptr) { - found.is_in_loop = true; - } - else if (dynamic_cast(context) != nullptr) { - found.is_in_simulation = true; - } - else if (dynamic_cast(context) != - nullptr) - { - found.is_in_loop = true; - } - else if (dynamic_cast(context) != nullptr) { - found.is_in_closure = true; - } - } - std::reverse(node_ids.begin(), node_ids.end()); - node_ids.append(node_id); - const bNestedNodeRef *nested_node_ref = - user_data.call_data->root_ntree->nested_node_ref_from_node_id_path(node_ids); - if (nested_node_ref == nullptr) { - return std::nullopt; - } - found.id = nested_node_ref->id; - return found; + return ed::space_node::find_nested_node_id_in_root( + *user_data.call_data->root_ntree, user_data.compute_context, node_id); } GeoNodesOperatorDepsgraphs::~GeoNodesOperatorDepsgraphs()