Merge branch 'blender-v4.5-release'

This commit is contained in:
Falk David
2025-06-09 14:16:12 +02:00
31 changed files with 295 additions and 136 deletions

View File

@@ -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}},

View File

@@ -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"),

View File

@@ -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):

View File

@@ -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):

View File

@@ -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.

View File

@@ -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.");
}
}

View File

@@ -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
{

View File

@@ -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<float2> src, MutableSpan<float3> dst) const;
/**

View File

@@ -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<int32_t> find_nested_node_id_in_root(const SpaceNode &snode, const bNode &node);
std::optional<nodes::FoundNestedNodeID> find_nested_node_id_in_root(const SpaceNode &snode,
const bNode &node);
std::optional<nodes::FoundNestedNodeID> 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

View File

@@ -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 {

View File

@@ -295,6 +295,9 @@ static void gather_socket_link_operations(const bContext &C,
}
const bNodeTreeInterfaceSocket &interface_socket =
reinterpret_cast<const bNodeTreeInterfaceSocket &>(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);

View File

@@ -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<int32_t> find_nested_node_id_in_root(const SpaceNode &snode, const bNode &query_node)
std::optional<nodes::FoundNestedNodeID> find_nested_node_id_in_root(const SpaceNode &snode,
const bNode &query_node)
{
BLI_assert(snode.edittree->runtime->nodes_by_id.contains(const_cast<bNode *>(&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<int32_t> 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<nodes::FoundNestedNodeID> find_nested_node_id_in_root(
const bNodeTree &root_tree, const ComputeContext *compute_context, const int node_id)
{
nodes::FoundNestedNodeID found;
Vector<int> node_ids;
for (const ComputeContext *context = compute_context; context != nullptr;
context = context->parent())
{
if (const auto *node_context = dynamic_cast<const bke::GroupNodeComputeContext *>(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<const bke::RepeatZoneComputeContext *>(context) != nullptr) {
found.is_in_loop = true;
}
if (!found) {
return std::nullopt;
else if (dynamic_cast<const bke::SimulationZoneComputeContext *>(context) != nullptr) {
found.is_in_simulation = true;
}
else if (dynamic_cast<const bke::ForeachGeometryElementZoneComputeContext *>(context) !=
nullptr)
{
found.is_in_loop = true;
}
else if (dynamic_cast<const bke::EvaluateClosureComputeContext *>(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<ObjectAndModifier> 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<const bke::bNodeTreeZone *> 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*/)

View File

@@ -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();

View File

@@ -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()

View File

@@ -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;

View File

@@ -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
}

View File

@@ -574,16 +574,8 @@ bool gpu_shader_create_info_compile(const char *name_starts_with_filter)
}
}
Vector<GPUShader *> 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<GPUShader *> result = GPU_shader_batch_finalize(batch);
for (int i : result.index_range()) {
const ShaderCreateInfo *info = reinterpret_cast<const ShaderCreateInfo *>(infos[i]);

View File

@@ -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)

View File

@@ -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) {

View File

@@ -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<GLSubprocessShaderCompiler>(__func__);
}
else {

View File

@@ -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;
};

View File

@@ -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 {

View File

@@ -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)

View File

@@ -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. */

View File

@@ -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

View File

@@ -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<FoundNestedNodeID> find_nested_node_id(const GeoNodesUserData &user_data,
const int node_id);

View File

@@ -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

View File

@@ -111,6 +111,7 @@ struct BakeDrawContext {
bool bake_still;
bool is_baked;
std::optional<NodesModifierBakeTarget> bake_target;
bool is_bakeable_in_current_context;
};
[[nodiscard]] bool get_bake_draw_context(const bContext *C,

View File

@@ -489,6 +489,12 @@ static void node_extra_info(NodeExtraInfoParams &params)
if (!get_bake_draw_context(&params.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<int32_t> bake_id = ed::space_node::find_nested_node_id_in_root(*r_ctx.snode,
*r_ctx.node);
const std::optional<FoundNestedNodeID> 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<bke::bake::BakeNodeCache> *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<bke::bake::SimulationNodeCache> *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() &&

View File

@@ -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 &params)
if (!get_bake_draw_context(&params.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);

View File

@@ -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<FoundNestedNodeID> find_nested_node_id(const GeoNodesUserData &user_data,
const int node_id)
{
FoundNestedNodeID found;
Vector<int> node_ids;
for (const ComputeContext *context = user_data.compute_context; context != nullptr;
context = context->parent())
{
if (const auto *node_context = dynamic_cast<const bke::GroupNodeComputeContext *>(context)) {
node_ids.append(node_context->node_id());
}
else if (dynamic_cast<const bke::RepeatZoneComputeContext *>(context) != nullptr) {
found.is_in_loop = true;
}
else if (dynamic_cast<const bke::SimulationZoneComputeContext *>(context) != nullptr) {
found.is_in_simulation = true;
}
else if (dynamic_cast<const bke::ForeachGeometryElementZoneComputeContext *>(context) !=
nullptr)
{
found.is_in_loop = true;
}
else if (dynamic_cast<const bke::EvaluateClosureComputeContext *>(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()