Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJacques Lucke <jacques@blender.org>2020-04-14 12:59:47 +0300
committerJacques Lucke <jacques@blender.org>2020-04-14 12:59:47 +0300
commit6692ca602ca6f934ce00da4563434e870e954133 (patch)
treefe49d439d269f05109d7e29a7776776a9e2bc464
parentdabd59ba23f877f68aaf73e79f0d58118723d9b7 (diff)
parentb07e8a24f5c69578c5ccae31848bb0f51fd18700 (diff)
Merge branch 'master' into functionsfunctions
-rw-r--r--intern/cycles/blender/addon/ui.py2
-rw-r--r--intern/cycles/device/device_optix.cpp8
-rw-r--r--intern/cycles/kernel/CMakeLists.txt4
-rw-r--r--intern/cycles/kernel/shaders/node_noise.h52
-rw-r--r--intern/cycles/kernel/shaders/node_noise_texture.osl44
-rw-r--r--intern/cycles/kernel/shaders/node_wave_texture.osl9
-rw-r--r--intern/cycles/kernel/svm/svm_fractal_noise.h52
-rw-r--r--intern/cycles/kernel/svm/svm_noisetex.h91
-rw-r--r--intern/cycles/kernel/svm/svm_wave.h32
-rw-r--r--intern/cycles/render/nodes.cpp46
-rw-r--r--intern/cycles/render/nodes.h4
-rw-r--r--intern/ghost/intern/GHOST_WindowWin32.cpp5
-rw-r--r--release/scripts/presets/keyconfig/keymap_data/blender_default.py11
-rw-r--r--release/scripts/startup/bl_ui/properties_data_modifier.py15
-rw-r--r--release/scripts/startup/bl_ui/properties_paint_common.py2
-rw-r--r--release/scripts/startup/bl_ui/space_image.py8
-rw-r--r--release/scripts/startup/bl_ui/space_toolsystem_toolbar.py17
-rw-r--r--release/scripts/startup/bl_ui/space_view3d.py3
-rw-r--r--source/blender/blenkernel/BKE_layer.h1
-rw-r--r--source/blender/blenkernel/BKE_sequencer.h9
-rw-r--r--source/blender/blenkernel/intern/constraint.c35
-rw-r--r--source/blender/blenkernel/intern/curve.c5
-rw-r--r--source/blender/blenkernel/intern/customdata.c3
-rw-r--r--source/blender/blenkernel/intern/editmesh_tangent.c5
-rw-r--r--source/blender/blenkernel/intern/fluid.c7
-rw-r--r--source/blender/blenkernel/intern/layer.c43
-rw-r--r--source/blender/blenkernel/intern/lib_id.c34
-rw-r--r--source/blender/blenkernel/intern/mesh_evaluate.c6
-rw-r--r--source/blender/blenkernel/intern/mesh_tangent.c5
-rw-r--r--source/blender/blenkernel/intern/ocean.c18
-rw-r--r--source/blender/blenkernel/intern/particle.c6
-rw-r--r--source/blender/blenkernel/intern/particle_distribute.c6
-rw-r--r--source/blender/blenkernel/intern/seqcache.c15
-rw-r--r--source/blender/blenkernel/intern/sequencer.c212
-rw-r--r--source/blender/blenlib/BLI_math_base.h3
-rw-r--r--source/blender/blenlib/BLI_task.h23
-rw-r--r--source/blender/blenlib/CMakeLists.txt3
-rw-r--r--source/blender/blenlib/intern/math_base_inline.c9
-rw-r--r--source/blender/blenlib/intern/storage.c5
-rw-r--r--source/blender/blenlib/intern/task_iterator.c (renamed from source/blender/blenlib/intern/task.c)1045
-rw-r--r--source/blender/blenlib/intern/task_pool.cc1029
-rw-r--r--source/blender/blenloader/intern/readfile.c23
-rw-r--r--source/blender/blenloader/intern/versioning_280.c25
-rw-r--r--source/blender/blenloader/intern/versioning_defaults.c172
-rw-r--r--source/blender/blenloader/intern/writefile.c35
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_relations_rig.cc6
-rw-r--r--source/blender/depsgraph/intern/depsgraph_eval.cc2
-rw-r--r--source/blender/depsgraph/intern/depsgraph_tag.cc2
-rw-r--r--source/blender/depsgraph/intern/eval/deg_eval.cc5
-rw-r--r--source/blender/draw/engines/eevee/eevee_lightcache.c5
-rw-r--r--source/blender/draw/intern/draw_cache_extract_mesh.c6
-rw-r--r--source/blender/draw/intern/draw_cache_impl_pointcloud.c2
-rw-r--r--source/blender/editors/animation/anim_filter.c12
-rw-r--r--source/blender/editors/armature/armature_select.c54
-rw-r--r--source/blender/editors/gpencil/annotate_draw.c238
-rw-r--r--source/blender/editors/include/ED_sculpt.h5
-rw-r--r--source/blender/editors/include/ED_util_imbuf.h52
-rw-r--r--source/blender/editors/include/UI_interface.h7
-rw-r--r--source/blender/editors/interface/interface.c12
-rw-r--r--source/blender/editors/interface/interface_intern.h4
-rw-r--r--source/blender/editors/interface/interface_layout.c1
-rw-r--r--source/blender/editors/interface/interface_region_search.c57
-rw-r--r--source/blender/editors/interface/interface_style.c11
-rw-r--r--source/blender/editors/interface/interface_templates.c32
-rw-r--r--source/blender/editors/interface/interface_utils.c21
-rw-r--r--source/blender/editors/interface/interface_widgets.c39
-rw-r--r--source/blender/editors/mesh/editmesh_undo.c6
-rw-r--r--source/blender/editors/object/object_modifier.c7
-rw-r--r--source/blender/editors/object/object_volume.c14
-rw-r--r--source/blender/editors/render/render_opengl.c7
-rw-r--r--source/blender/editors/sculpt_paint/paint_image.c4
-rw-r--r--source/blender/editors/sculpt_paint/paint_image_proj.c5
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_intern.h1
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_undo.c94
-rw-r--r--source/blender/editors/space_clip/clip_editor.c4
-rw-r--r--source/blender/editors/space_clip/clip_ops.c4
-rw-r--r--source/blender/editors/space_file/filelist.c13
-rw-r--r--source/blender/editors/space_image/image_ops.c389
-rw-r--r--source/blender/editors/space_image/image_sequence.c9
-rw-r--r--source/blender/editors/space_node/node_relationships.c4
-rw-r--r--source/blender/editors/space_node/node_select.c2
-rw-r--r--source/blender/editors/space_outliner/outliner_collections.c27
-rw-r--r--source/blender/editors/space_outliner/outliner_edit.c9
-rw-r--r--source/blender/editors/space_outliner/outliner_select.c2
-rw-r--r--source/blender/editors/space_outliner/outliner_tools.c2
-rw-r--r--source/blender/editors/space_sequencer/sequencer_edit.c8
-rw-r--r--source/blender/editors/space_sequencer/sequencer_view.c221
-rw-r--r--source/blender/editors/transform/transform.c2
-rw-r--r--source/blender/editors/transform/transform_convert_armature.c66
-rw-r--r--source/blender/editors/undo/memfile_undo.c6
-rw-r--r--source/blender/editors/util/CMakeLists.txt3
-rw-r--r--source/blender/editors/util/ed_util_imbuf.c571
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_fractal_noise.glsl52
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_tex_noise.glsl64
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_tex_wave.glsl5
-rw-r--r--source/blender/imbuf/intern/imageprocess.c13
-rw-r--r--source/blender/io/usd/intern/abstract_hierarchy_iterator.cc26
-rw-r--r--source/blender/makesdna/DNA_ID.h15
-rw-r--r--source/blender/makesdna/DNA_layer_types.h1
-rw-r--r--source/blender/makesdna/DNA_modifier_types.h13
-rw-r--r--source/blender/makesrna/intern/rna_gpencil.c10
-rw-r--r--source/blender/makesrna/intern/rna_layer.c15
-rw-r--r--source/blender/makesrna/intern/rna_modifier.c19
-rw-r--r--source/blender/makesrna/intern/rna_sequencer.c72
-rw-r--r--source/blender/makesrna/intern/rna_sequencer_api.c4
-rw-r--r--source/blender/modifiers/intern/MOD_displace.c29
-rw-r--r--source/blender/modifiers/intern/MOD_solidify.c1
-rw-r--r--source/blender/modifiers/intern/MOD_solidify_nonmanifold.c77
-rw-r--r--source/blender/modifiers/intern/MOD_util.c36
-rw-r--r--source/blender/modifiers/intern/MOD_util.h4
-rw-r--r--source/blender/modifiers/intern/MOD_uvwarp.c20
-rw-r--r--source/blender/modifiers/intern/MOD_warp.c39
-rw-r--r--source/blender/modifiers/intern/MOD_wave.c23
-rw-r--r--source/blender/modifiers/intern/MOD_weightvg_util.c2
-rw-r--r--source/blender/modifiers/intern/MOD_weightvg_util.h1
-rw-r--r--source/blender/modifiers/intern/MOD_weightvgedit.c25
-rw-r--r--source/blender/modifiers/intern/MOD_weightvgmix.c26
-rw-r--r--source/blender/modifiers/intern/MOD_weightvgproximity.c25
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_tex_noise.c1
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_tex_wave.c1
-rw-r--r--source/blender/windowmanager/intern/wm_toolsystem.c2
-rw-r--r--tests/gtests/blenlib/BLI_linklist_lockfree_test.cc4
-rw-r--r--tests/gtests/usd/abstract_hierarchy_iterator_test.cc17
123 files changed, 3289 insertions, 2593 deletions
diff --git a/intern/cycles/blender/addon/ui.py b/intern/cycles/blender/addon/ui.py
index 0ef813d9636..7cf615620a3 100644
--- a/intern/cycles/blender/addon/ui.py
+++ b/intern/cycles/blender/addon/ui.py
@@ -253,8 +253,8 @@ class CYCLES_RENDER_PT_sampling_adaptive(CyclesButtonsPanel, Panel):
layout.active = cscene.use_adaptive_sampling
col = layout.column(align=True)
- col.prop(cscene, "adaptive_min_samples", text="Min Samples")
col.prop(cscene, "adaptive_threshold", text="Noise Threshold")
+ col.prop(cscene, "adaptive_min_samples", text="Min Samples")
class CYCLES_RENDER_PT_sampling_advanced(CyclesButtonsPanel, Panel):
bl_label = "Advanced"
diff --git a/intern/cycles/device/device_optix.cpp b/intern/cycles/device/device_optix.cpp
index 42d7b00314c..37d6ae3d041 100644
--- a/intern/cycles/device/device_optix.cpp
+++ b/intern/cycles/device/device_optix.cpp
@@ -383,7 +383,13 @@ class OptiXDevice : public CUDADevice {
{ // Load and compile PTX module with OptiX kernels
string ptx_data, ptx_filename = path_get("lib/kernel_optix.ptx");
- if (use_adaptive_compilation()) {
+ if (use_adaptive_compilation() || path_file_size(ptx_filename) == -1) {
+ if (!getenv("OPTIX_ROOT_DIR")) {
+ set_error(
+ "OPTIX_ROOT_DIR environment variable not set, must be set with the path to the "
+ "Optix SDK in order to compile the Optix kernel on demand.");
+ return false;
+ }
ptx_filename = compile_kernel(requested_features, "kernel_optix", "optix", true);
}
if (ptx_filename.empty() || !path_read_text(ptx_filename, ptx_data)) {
diff --git a/intern/cycles/kernel/CMakeLists.txt b/intern/cycles/kernel/CMakeLists.txt
index 3264b5afea2..ac3a85089c2 100644
--- a/intern/cycles/kernel/CMakeLists.txt
+++ b/intern/cycles/kernel/CMakeLists.txt
@@ -507,7 +507,7 @@ endif()
# OptiX PTX modules
-if(WITH_CYCLES_DEVICE_OPTIX)
+if(WITH_CYCLES_DEVICE_OPTIX AND WITH_CYCLES_CUDA_BINARIES)
foreach(input ${SRC_OPTIX_KERNELS})
get_filename_component(input_we ${input} NAME_WE)
@@ -677,7 +677,7 @@ source_group("svm" FILES ${SRC_SVM_HEADERS})
if(WITH_CYCLES_CUDA)
add_dependencies(cycles_kernel cycles_kernel_cuda)
endif()
-if(WITH_CYCLES_DEVICE_OPTIX)
+if(WITH_CYCLES_DEVICE_OPTIX AND WITH_CYCLES_CUDA_BINARIES)
add_dependencies(cycles_kernel cycles_kernel_optix)
endif()
diff --git a/intern/cycles/kernel/shaders/node_noise.h b/intern/cycles/kernel/shaders/node_noise.h
index 23d1987a00e..ab4cd7792cc 100644
--- a/intern/cycles/kernel/shaders/node_noise.h
+++ b/intern/cycles/kernel/shaders/node_noise.h
@@ -84,114 +84,118 @@ float safe_snoise(vector4 p)
}
/* The fractal_noise functions are all exactly the same except for the input type. */
-float fractal_noise(float p, float details)
+float fractal_noise(float p, float details, float roughness)
{
float fscale = 1.0;
float amp = 1.0;
+ float maxamp = 0.0;
float sum = 0.0;
float octaves = clamp(details, 0.0, 16.0);
int n = (int)octaves;
for (int i = 0; i <= n; i++) {
float t = safe_noise(fscale * p);
sum += t * amp;
- amp *= 0.5;
+ maxamp += amp;
+ amp *= clamp(roughness, 0.0, 1.0);
fscale *= 2.0;
}
float rmd = octaves - floor(octaves);
if (rmd != 0.0) {
float t = safe_noise(fscale * p);
float sum2 = sum + t * amp;
- sum *= ((float)(1 << n) / (float)((1 << (n + 1)) - 1));
- sum2 *= ((float)(1 << (n + 1)) / (float)((1 << (n + 2)) - 1));
+ sum /= maxamp;
+ sum2 /= maxamp + amp;
return (1.0 - rmd) * sum + rmd * sum2;
}
else {
- sum *= ((float)(1 << n) / (float)((1 << (n + 1)) - 1));
- return sum;
+ return sum / maxamp;
}
}
/* The fractal_noise functions are all exactly the same except for the input type. */
-float fractal_noise(vector2 p, float details)
+float fractal_noise(vector2 p, float details, float roughness)
{
float fscale = 1.0;
float amp = 1.0;
+ float maxamp = 0.0;
float sum = 0.0;
float octaves = clamp(details, 0.0, 16.0);
int n = (int)octaves;
for (int i = 0; i <= n; i++) {
float t = safe_noise(fscale * p);
sum += t * amp;
- amp *= 0.5;
+ maxamp += amp;
+ amp *= clamp(roughness, 0.0, 1.0);
fscale *= 2.0;
}
float rmd = octaves - floor(octaves);
if (rmd != 0.0) {
float t = safe_noise(fscale * p);
float sum2 = sum + t * amp;
- sum *= ((float)(1 << n) / (float)((1 << (n + 1)) - 1));
- sum2 *= ((float)(1 << (n + 1)) / (float)((1 << (n + 2)) - 1));
+ sum /= maxamp;
+ sum2 /= maxamp + amp;
return (1.0 - rmd) * sum + rmd * sum2;
}
else {
- sum *= ((float)(1 << n) / (float)((1 << (n + 1)) - 1));
- return sum;
+ return sum / maxamp;
}
}
/* The fractal_noise functions are all exactly the same except for the input type. */
-float fractal_noise(vector3 p, float details)
+float fractal_noise(vector3 p, float details, float roughness)
{
float fscale = 1.0;
float amp = 1.0;
+ float maxamp = 0.0;
float sum = 0.0;
float octaves = clamp(details, 0.0, 16.0);
int n = (int)octaves;
for (int i = 0; i <= n; i++) {
float t = safe_noise(fscale * p);
sum += t * amp;
- amp *= 0.5;
+ maxamp += amp;
+ amp *= clamp(roughness, 0.0, 1.0);
fscale *= 2.0;
}
float rmd = octaves - floor(octaves);
if (rmd != 0.0) {
float t = safe_noise(fscale * p);
float sum2 = sum + t * amp;
- sum *= ((float)(1 << n) / (float)((1 << (n + 1)) - 1));
- sum2 *= ((float)(1 << (n + 1)) / (float)((1 << (n + 2)) - 1));
+ sum /= maxamp;
+ sum2 /= maxamp + amp;
return (1.0 - rmd) * sum + rmd * sum2;
}
else {
- sum *= ((float)(1 << n) / (float)((1 << (n + 1)) - 1));
- return sum;
+ return sum / maxamp;
}
}
/* The fractal_noise functions are all exactly the same except for the input type. */
-float fractal_noise(vector4 p, float details)
+float fractal_noise(vector4 p, float details, float roughness)
{
float fscale = 1.0;
float amp = 1.0;
+ float maxamp = 0.0;
float sum = 0.0;
float octaves = clamp(details, 0.0, 16.0);
int n = (int)octaves;
for (int i = 0; i <= n; i++) {
float t = safe_noise(fscale * p);
sum += t * amp;
- amp *= 0.5;
+ maxamp += amp;
+ amp *= clamp(roughness, 0.0, 1.0);
fscale *= 2.0;
}
float rmd = octaves - floor(octaves);
if (rmd != 0.0) {
float t = safe_noise(fscale * p);
float sum2 = sum + t * amp;
- sum *= ((float)(1 << n) / (float)((1 << (n + 1)) - 1));
- sum2 *= ((float)(1 << (n + 1)) / (float)((1 << (n + 2)) - 1));
+ sum /= maxamp;
+ sum2 /= maxamp + amp;
return (1.0 - rmd) * sum + rmd * sum2;
}
else {
- sum *= ((float)(1 << n) / (float)((1 << (n + 1)) - 1));
- return sum;
+ return sum / maxamp;
}
}
diff --git a/intern/cycles/kernel/shaders/node_noise_texture.osl b/intern/cycles/kernel/shaders/node_noise_texture.osl
index 4121b415673..61c0216910b 100644
--- a/intern/cycles/kernel/shaders/node_noise_texture.osl
+++ b/intern/cycles/kernel/shaders/node_noise_texture.osl
@@ -55,21 +55,22 @@ vector4 random_vector4_offset(float seed)
100.0 + noise("hash", seed, 3.0) * 100.0);
}
-float noise_texture(float co, float detail, float distortion, output color Color)
+float noise_texture(float co, float detail, float roughness, float distortion, output color Color)
{
float p = co;
if (distortion != 0.0) {
p += safe_snoise(p + random_float_offset(0.0)) * distortion;
}
- float value = fractal_noise(p, detail);
+ float value = fractal_noise(p, detail, roughness);
Color = color(value,
- fractal_noise(p + random_float_offset(1.0), detail),
- fractal_noise(p + random_float_offset(2.0), detail));
+ fractal_noise(p + random_float_offset(1.0), detail, roughness),
+ fractal_noise(p + random_float_offset(2.0), detail, roughness));
return value;
}
-float noise_texture(vector2 co, float detail, float distortion, output color Color)
+float noise_texture(
+ vector2 co, float detail, float roughness, float distortion, output color Color)
{
vector2 p = co;
if (distortion != 0.0) {
@@ -77,14 +78,15 @@ float noise_texture(vector2 co, float detail, float distortion, output color Col
safe_snoise(p + random_vector2_offset(1.0)) * distortion);
}
- float value = fractal_noise(p, detail);
+ float value = fractal_noise(p, detail, roughness);
Color = color(value,
- fractal_noise(p + random_vector2_offset(2.0), detail),
- fractal_noise(p + random_vector2_offset(3.0), detail));
+ fractal_noise(p + random_vector2_offset(2.0), detail, roughness),
+ fractal_noise(p + random_vector2_offset(3.0), detail, roughness));
return value;
}
-float noise_texture(vector3 co, float detail, float distortion, output color Color)
+float noise_texture(
+ vector3 co, float detail, float roughness, float distortion, output color Color)
{
vector3 p = co;
if (distortion != 0.0) {
@@ -93,14 +95,15 @@ float noise_texture(vector3 co, float detail, float distortion, output color Col
safe_snoise(p + random_vector3_offset(2.0)) * distortion);
}
- float value = fractal_noise(p, detail);
+ float value = fractal_noise(p, detail, roughness);
Color = color(value,
- fractal_noise(p + random_vector3_offset(3.0), detail),
- fractal_noise(p + random_vector3_offset(4.0), detail));
+ fractal_noise(p + random_vector3_offset(3.0), detail, roughness),
+ fractal_noise(p + random_vector3_offset(4.0), detail, roughness));
return value;
}
-float noise_texture(vector4 co, float detail, float distortion, output color Color)
+float noise_texture(
+ vector4 co, float detail, float roughness, float distortion, output color Color)
{
vector4 p = co;
if (distortion != 0.0) {
@@ -110,10 +113,10 @@ float noise_texture(vector4 co, float detail, float distortion, output color Col
safe_snoise(p + random_vector4_offset(3.0)) * distortion);
}
- float value = fractal_noise(p, detail);
+ float value = fractal_noise(p, detail, roughness);
Color = color(value,
- fractal_noise(p + random_vector4_offset(4.0), detail),
- fractal_noise(p + random_vector4_offset(5.0), detail));
+ fractal_noise(p + random_vector4_offset(4.0), detail, roughness),
+ fractal_noise(p + random_vector4_offset(5.0), detail, roughness));
return value;
}
@@ -124,6 +127,7 @@ shader node_noise_texture(int use_mapping = 0,
float W = 0.0,
float Scale = 5.0,
float Detail = 2.0,
+ float Roughness = 0.5,
float Distortion = 0.0,
output float Fac = 0.0,
output color Color = 0.0)
@@ -136,13 +140,13 @@ shader node_noise_texture(int use_mapping = 0,
float w = W * Scale;
if (dimensions == "1D")
- Fac = noise_texture(w, Detail, Distortion, Color);
+ Fac = noise_texture(w, Detail, Roughness, Distortion, Color);
else if (dimensions == "2D")
- Fac = noise_texture(vector2(p[0], p[1]), Detail, Distortion, Color);
+ Fac = noise_texture(vector2(p[0], p[1]), Detail, Roughness, Distortion, Color);
else if (dimensions == "3D")
- Fac = noise_texture(p, Detail, Distortion, Color);
+ Fac = noise_texture(p, Detail, Roughness, Distortion, Color);
else if (dimensions == "4D")
- Fac = noise_texture(vector4(p[0], p[1], p[2], w), Detail, Distortion, Color);
+ Fac = noise_texture(vector4(p[0], p[1], p[2], w), Detail, Roughness, Distortion, Color);
else
error("Unknown dimension!");
}
diff --git a/intern/cycles/kernel/shaders/node_wave_texture.osl b/intern/cycles/kernel/shaders/node_wave_texture.osl
index f17397be243..874bfb8d3af 100644
--- a/intern/cycles/kernel/shaders/node_wave_texture.osl
+++ b/intern/cycles/kernel/shaders/node_wave_texture.osl
@@ -24,9 +24,10 @@ float wave(point p_input,
string bands_direction,
string rings_direction,
string profile,
- float detail,
float distortion,
+ float detail,
float dscale,
+ float droughness,
float phase)
{
/* Prevent precision issues on unit coordinates. */
@@ -67,7 +68,7 @@ float wave(point p_input,
n += phase;
if (distortion != 0.0) {
- n = n + (distortion * (fractal_noise(p * dscale, detail) * 2.0 - 1.0));
+ n = n + (distortion * (fractal_noise(p * dscale, detail, droughness) * 2.0 - 1.0));
}
if (profile == "sine") {
@@ -93,6 +94,7 @@ shader node_wave_texture(int use_mapping = 0,
float Distortion = 0.0,
float Detail = 2.0,
float DetailScale = 1.0,
+ float DetailRoughness = 0.5,
float PhaseOffset = 0.0,
point Vector = P,
output float Fac = 0.0,
@@ -108,9 +110,10 @@ shader node_wave_texture(int use_mapping = 0,
bands_direction,
rings_direction,
profile,
- Detail,
Distortion,
+ Detail,
DetailScale,
+ DetailRoughness,
PhaseOffset);
Color = Fac;
}
diff --git a/intern/cycles/kernel/svm/svm_fractal_noise.h b/intern/cycles/kernel/svm/svm_fractal_noise.h
index 5b2e4a28fce..57fa8c690ac 100644
--- a/intern/cycles/kernel/svm/svm_fractal_noise.h
+++ b/intern/cycles/kernel/svm/svm_fractal_noise.h
@@ -17,114 +17,118 @@
CCL_NAMESPACE_BEGIN
/* The fractal_noise_[1-4] functions are all exactly the same except for the input type. */
-ccl_device_noinline float fractal_noise_1d(float p, float octaves)
+ccl_device_noinline float fractal_noise_1d(float p, float octaves, float roughness)
{
float fscale = 1.0f;
float amp = 1.0f;
+ float maxamp = 0.0f;
float sum = 0.0f;
octaves = clamp(octaves, 0.0f, 16.0f);
int n = float_to_int(octaves);
for (int i = 0; i <= n; i++) {
float t = noise_1d(fscale * p);
sum += t * amp;
- amp *= 0.5f;
+ maxamp += amp;
+ amp *= clamp(roughness, 0.0f, 1.0f);
fscale *= 2.0f;
}
float rmd = octaves - floorf(octaves);
if (rmd != 0.0f) {
float t = noise_1d(fscale * p);
float sum2 = sum + t * amp;
- sum *= ((float)(1 << n) / (float)((1 << (n + 1)) - 1));
- sum2 *= ((float)(1 << (n + 1)) / (float)((1 << (n + 2)) - 1));
+ sum /= maxamp;
+ sum2 /= maxamp + amp;
return (1.0f - rmd) * sum + rmd * sum2;
}
else {
- sum *= ((float)(1 << n) / (float)((1 << (n + 1)) - 1));
- return sum;
+ return sum / maxamp;
}
}
/* The fractal_noise_[1-4] functions are all exactly the same except for the input type. */
-ccl_device_noinline float fractal_noise_2d(float2 p, float octaves)
+ccl_device_noinline float fractal_noise_2d(float2 p, float octaves, float roughness)
{
float fscale = 1.0f;
float amp = 1.0f;
+ float maxamp = 0.0f;
float sum = 0.0f;
octaves = clamp(octaves, 0.0f, 16.0f);
int n = float_to_int(octaves);
for (int i = 0; i <= n; i++) {
float t = noise_2d(fscale * p);
sum += t * amp;
- amp *= 0.5f;
+ maxamp += amp;
+ amp *= clamp(roughness, 0.0f, 1.0f);
fscale *= 2.0f;
}
float rmd = octaves - floorf(octaves);
if (rmd != 0.0f) {
float t = noise_2d(fscale * p);
float sum2 = sum + t * amp;
- sum *= ((float)(1 << n) / (float)((1 << (n + 1)) - 1));
- sum2 *= ((float)(1 << (n + 1)) / (float)((1 << (n + 2)) - 1));
+ sum /= maxamp;
+ sum2 /= maxamp + amp;
return (1.0f - rmd) * sum + rmd * sum2;
}
else {
- sum *= ((float)(1 << n) / (float)((1 << (n + 1)) - 1));
- return sum;
+ return sum / maxamp;
}
}
/* The fractal_noise_[1-4] functions are all exactly the same except for the input type. */
-ccl_device_noinline float fractal_noise_3d(float3 p, float octaves)
+ccl_device_noinline float fractal_noise_3d(float3 p, float octaves, float roughness)
{
float fscale = 1.0f;
float amp = 1.0f;
+ float maxamp = 0.0f;
float sum = 0.0f;
octaves = clamp(octaves, 0.0f, 16.0f);
int n = float_to_int(octaves);
for (int i = 0; i <= n; i++) {
float t = noise_3d(fscale * p);
sum += t * amp;
- amp *= 0.5f;
+ maxamp += amp;
+ amp *= clamp(roughness, 0.0f, 1.0f);
fscale *= 2.0f;
}
float rmd = octaves - floorf(octaves);
if (rmd != 0.0f) {
float t = noise_3d(fscale * p);
float sum2 = sum + t * amp;
- sum *= ((float)(1 << n) / (float)((1 << (n + 1)) - 1));
- sum2 *= ((float)(1 << (n + 1)) / (float)((1 << (n + 2)) - 1));
+ sum /= maxamp;
+ sum2 /= maxamp + amp;
return (1.0f - rmd) * sum + rmd * sum2;
}
else {
- sum *= ((float)(1 << n) / (float)((1 << (n + 1)) - 1));
- return sum;
+ return sum / maxamp;
}
}
/* The fractal_noise_[1-4] functions are all exactly the same except for the input type. */
-ccl_device_noinline float fractal_noise_4d(float4 p, float octaves)
+ccl_device_noinline float fractal_noise_4d(float4 p, float octaves, float roughness)
{
float fscale = 1.0f;
float amp = 1.0f;
+ float maxamp = 0.0f;
float sum = 0.0f;
octaves = clamp(octaves, 0.0f, 16.0f);
int n = float_to_int(octaves);
for (int i = 0; i <= n; i++) {
float t = noise_4d(fscale * p);
sum += t * amp;
- amp *= 0.5f;
+ maxamp += amp;
+ amp *= clamp(roughness, 0.0f, 1.0f);
fscale *= 2.0f;
}
float rmd = octaves - floorf(octaves);
if (rmd != 0.0f) {
float t = noise_4d(fscale * p);
float sum2 = sum + t * amp;
- sum *= ((float)(1 << n) / (float)((1 << (n + 1)) - 1));
- sum2 *= ((float)(1 << (n + 1)) / (float)((1 << (n + 2)) - 1));
+ sum /= maxamp;
+ sum2 /= maxamp + amp;
return (1.0f - rmd) * sum + rmd * sum2;
}
else {
- sum *= ((float)(1 << n) / (float)((1 << (n + 1)) - 1));
- return sum;
+ return sum / maxamp;
}
}
diff --git a/intern/cycles/kernel/svm/svm_noisetex.h b/intern/cycles/kernel/svm/svm_noisetex.h
index 12884c6cb25..920dd7d9d02 100644
--- a/intern/cycles/kernel/svm/svm_noisetex.h
+++ b/intern/cycles/kernel/svm/svm_noisetex.h
@@ -50,24 +50,34 @@ ccl_device_inline float4 random_float4_offset(float seed)
100.0f + hash_float2_to_float(make_float2(seed, 3.0f)) * 100.0f);
}
-ccl_device void noise_texture_1d(
- float co, float detail, float distortion, bool color_is_needed, float *value, float3 *color)
+ccl_device void noise_texture_1d(float co,
+ float detail,
+ float roughness,
+ float distortion,
+ bool color_is_needed,
+ float *value,
+ float3 *color)
{
float p = co;
if (distortion != 0.0f) {
p += snoise_1d(p + random_float_offset(0.0f)) * distortion;
}
- *value = fractal_noise_1d(p, detail);
+ *value = fractal_noise_1d(p, detail, roughness);
if (color_is_needed) {
*color = make_float3(*value,
- fractal_noise_1d(p + random_float_offset(1.0f), detail),
- fractal_noise_1d(p + random_float_offset(2.0f), detail));
+ fractal_noise_1d(p + random_float_offset(1.0f), detail, roughness),
+ fractal_noise_1d(p + random_float_offset(2.0f), detail, roughness));
}
}
-ccl_device void noise_texture_2d(
- float2 co, float detail, float distortion, bool color_is_needed, float *value, float3 *color)
+ccl_device void noise_texture_2d(float2 co,
+ float detail,
+ float roughness,
+ float distortion,
+ bool color_is_needed,
+ float *value,
+ float3 *color)
{
float2 p = co;
if (distortion != 0.0f) {
@@ -75,16 +85,21 @@ ccl_device void noise_texture_2d(
snoise_2d(p + random_float2_offset(1.0f)) * distortion);
}
- *value = fractal_noise_2d(p, detail);
+ *value = fractal_noise_2d(p, detail, roughness);
if (color_is_needed) {
*color = make_float3(*value,
- fractal_noise_2d(p + random_float2_offset(2.0f), detail),
- fractal_noise_2d(p + random_float2_offset(3.0f), detail));
+ fractal_noise_2d(p + random_float2_offset(2.0f), detail, roughness),
+ fractal_noise_2d(p + random_float2_offset(3.0f), detail, roughness));
}
}
-ccl_device void noise_texture_3d(
- float3 co, float detail, float distortion, bool color_is_needed, float *value, float3 *color)
+ccl_device void noise_texture_3d(float3 co,
+ float detail,
+ float roughness,
+ float distortion,
+ bool color_is_needed,
+ float *value,
+ float3 *color)
{
float3 p = co;
if (distortion != 0.0f) {
@@ -93,16 +108,21 @@ ccl_device void noise_texture_3d(
snoise_3d(p + random_float3_offset(2.0f)) * distortion);
}
- *value = fractal_noise_3d(p, detail);
+ *value = fractal_noise_3d(p, detail, roughness);
if (color_is_needed) {
*color = make_float3(*value,
- fractal_noise_3d(p + random_float3_offset(3.0f), detail),
- fractal_noise_3d(p + random_float3_offset(4.0f), detail));
+ fractal_noise_3d(p + random_float3_offset(3.0f), detail, roughness),
+ fractal_noise_3d(p + random_float3_offset(4.0f), detail, roughness));
}
}
-ccl_device void noise_texture_4d(
- float4 co, float detail, float distortion, bool color_is_needed, float *value, float3 *color)
+ccl_device void noise_texture_4d(float4 co,
+ float detail,
+ float roughness,
+ float distortion,
+ bool color_is_needed,
+ float *value,
+ float3 *color)
{
float4 p = co;
if (distortion != 0.0f) {
@@ -112,11 +132,11 @@ ccl_device void noise_texture_4d(
snoise_4d(p + random_float4_offset(3.0f)) * distortion);
}
- *value = fractal_noise_4d(p, detail);
+ *value = fractal_noise_4d(p, detail, roughness);
if (color_is_needed) {
*color = make_float3(*value,
- fractal_noise_4d(p + random_float4_offset(4.0f), detail),
- fractal_noise_4d(p + random_float4_offset(5.0f), detail));
+ fractal_noise_4d(p + random_float4_offset(4.0f), detail, roughness),
+ fractal_noise_4d(p + random_float4_offset(5.0f), detail, roughness));
}
}
@@ -128,21 +148,27 @@ ccl_device void svm_node_tex_noise(KernelGlobals *kg,
uint offsets2,
int *offset)
{
- uint vector_stack_offset, w_stack_offset, scale_stack_offset, detail_stack_offset;
- uint distortion_stack_offset, value_stack_offset, color_stack_offset;
+ uint vector_stack_offset, w_stack_offset, scale_stack_offset;
+ uint detail_stack_offset, roughness_stack_offset, distortion_stack_offset;
+ uint value_stack_offset, color_stack_offset;
svm_unpack_node_uchar4(
offsets1, &vector_stack_offset, &w_stack_offset, &scale_stack_offset, &detail_stack_offset);
- svm_unpack_node_uchar3(
- offsets2, &distortion_stack_offset, &value_stack_offset, &color_stack_offset);
+ svm_unpack_node_uchar4(offsets2,
+ &roughness_stack_offset,
+ &distortion_stack_offset,
+ &value_stack_offset,
+ &color_stack_offset);
- uint4 defaults = read_node(kg, offset);
+ uint4 defaults1 = read_node(kg, offset);
+ uint4 defaults2 = read_node(kg, offset);
float3 vector = stack_load_float3(stack, vector_stack_offset);
- float w = stack_load_float_default(stack, w_stack_offset, defaults.x);
- float scale = stack_load_float_default(stack, scale_stack_offset, defaults.y);
- float detail = stack_load_float_default(stack, detail_stack_offset, defaults.z);
- float distortion = stack_load_float_default(stack, distortion_stack_offset, defaults.w);
+ float w = stack_load_float_default(stack, w_stack_offset, defaults1.x);
+ float scale = stack_load_float_default(stack, scale_stack_offset, defaults1.y);
+ float detail = stack_load_float_default(stack, detail_stack_offset, defaults1.z);
+ float roughness = stack_load_float_default(stack, roughness_stack_offset, defaults1.w);
+ float distortion = stack_load_float_default(stack, distortion_stack_offset, defaults2.x);
vector *= scale;
w *= scale;
@@ -151,11 +177,13 @@ ccl_device void svm_node_tex_noise(KernelGlobals *kg,
float3 color;
switch (dimensions) {
case 1:
- noise_texture_1d(w, detail, distortion, stack_valid(color_stack_offset), &value, &color);
+ noise_texture_1d(
+ w, detail, roughness, distortion, stack_valid(color_stack_offset), &value, &color);
break;
case 2:
noise_texture_2d(make_float2(vector.x, vector.y),
detail,
+ roughness,
distortion,
stack_valid(color_stack_offset),
&value,
@@ -163,11 +191,12 @@ ccl_device void svm_node_tex_noise(KernelGlobals *kg,
break;
case 3:
noise_texture_3d(
- vector, detail, distortion, stack_valid(color_stack_offset), &value, &color);
+ vector, detail, roughness, distortion, stack_valid(color_stack_offset), &value, &color);
break;
case 4:
noise_texture_4d(make_float4(vector.x, vector.y, vector.z, w),
detail,
+ roughness,
distortion,
stack_valid(color_stack_offset),
&value,
diff --git a/intern/cycles/kernel/svm/svm_wave.h b/intern/cycles/kernel/svm/svm_wave.h
index 64102535f7d..c4763475b47 100644
--- a/intern/cycles/kernel/svm/svm_wave.h
+++ b/intern/cycles/kernel/svm/svm_wave.h
@@ -23,9 +23,10 @@ ccl_device_noinline_cpu float svm_wave(NodeWaveType type,
NodeWaveRingsDirection rings_dir,
NodeWaveProfile profile,
float3 p,
- float detail,
float distortion,
+ float detail,
float dscale,
+ float droughness,
float phase)
{
/* Prevent precision issues on unit coordinates. */
@@ -66,7 +67,7 @@ ccl_device_noinline_cpu float svm_wave(NodeWaveType type,
n += phase;
if (distortion != 0.0f)
- n += distortion * (fractal_noise_3d(p * dscale, detail) * 2.0f - 1.0f);
+ n += distortion * (fractal_noise_3d(p * dscale, detail, droughness) * 2.0f - 1.0f);
if (profile == NODE_WAVE_PROFILE_SIN) {
return 0.5f + 0.5f * sinf(n - M_PI_2_F);
@@ -84,35 +85,40 @@ ccl_device_noinline_cpu float svm_wave(NodeWaveType type,
ccl_device void svm_node_tex_wave(
KernelGlobals *kg, ShaderData *sd, float *stack, uint4 node, int *offset)
{
- uint4 defaults1 = read_node(kg, offset);
- uint4 defaults2 = read_node(kg, offset);
+ uint4 node2 = read_node(kg, offset);
+ uint4 node3 = read_node(kg, offset);
/* RNA properties */
uint type_offset, bands_dir_offset, rings_dir_offset, profile_offset;
/* Inputs, Outputs */
- uint co_offset, scale_offset, distortion_offset, detail_offset, dscale_offset, phase_offset;
+ uint co_offset, scale_offset, distortion_offset, detail_offset, dscale_offset, droughness_offset,
+ phase_offset;
uint color_offset, fac_offset;
svm_unpack_node_uchar4(
node.y, &type_offset, &bands_dir_offset, &rings_dir_offset, &profile_offset);
- svm_unpack_node_uchar4(node.z, &co_offset, &scale_offset, &distortion_offset, &detail_offset);
- svm_unpack_node_uchar4(node.w, &dscale_offset, &phase_offset, &color_offset, &fac_offset);
+ svm_unpack_node_uchar3(node.z, &co_offset, &scale_offset, &distortion_offset);
+ svm_unpack_node_uchar4(
+ node.w, &detail_offset, &dscale_offset, &droughness_offset, &phase_offset);
+ svm_unpack_node_uchar2(node2.x, &color_offset, &fac_offset);
float3 co = stack_load_float3(stack, co_offset);
- float scale = stack_load_float_default(stack, scale_offset, defaults1.x);
- float detail = stack_load_float_default(stack, detail_offset, defaults1.y);
- float distortion = stack_load_float_default(stack, distortion_offset, defaults1.z);
- float dscale = stack_load_float_default(stack, dscale_offset, defaults1.w);
- float phase = stack_load_float_default(stack, phase_offset, defaults2.x);
+ float scale = stack_load_float_default(stack, scale_offset, node2.y);
+ float distortion = stack_load_float_default(stack, distortion_offset, node2.z);
+ float detail = stack_load_float_default(stack, detail_offset, node2.w);
+ float dscale = stack_load_float_default(stack, dscale_offset, node3.x);
+ float droughness = stack_load_float_default(stack, droughness_offset, node3.y);
+ float phase = stack_load_float_default(stack, phase_offset, node3.z);
float f = svm_wave((NodeWaveType)type_offset,
(NodeWaveBandsDirection)bands_dir_offset,
(NodeWaveRingsDirection)rings_dir_offset,
(NodeWaveProfile)profile_offset,
co * scale,
- detail,
distortion,
+ detail,
dscale,
+ droughness,
phase);
if (stack_valid(fac_offset))
diff --git a/intern/cycles/render/nodes.cpp b/intern/cycles/render/nodes.cpp
index ac07d91c4ca..4b4958fe3da 100644
--- a/intern/cycles/render/nodes.cpp
+++ b/intern/cycles/render/nodes.cpp
@@ -922,6 +922,7 @@ NODE_DEFINE(NoiseTextureNode)
SOCKET_IN_FLOAT(w, "W", 0.0f);
SOCKET_IN_FLOAT(scale, "Scale", 1.0f);
SOCKET_IN_FLOAT(detail, "Detail", 2.0f);
+ SOCKET_IN_FLOAT(roughness, "Roughness", 0.5f);
SOCKET_IN_FLOAT(distortion, "Distortion", 0.0f);
SOCKET_OUT_FLOAT(fac, "Fac");
@@ -940,6 +941,7 @@ void NoiseTextureNode::compile(SVMCompiler &compiler)
ShaderInput *w_in = input("W");
ShaderInput *scale_in = input("Scale");
ShaderInput *detail_in = input("Detail");
+ ShaderInput *roughness_in = input("Roughness");
ShaderInput *distortion_in = input("Distortion");
ShaderOutput *fac_out = output("Fac");
ShaderOutput *color_out = output("Color");
@@ -948,6 +950,7 @@ void NoiseTextureNode::compile(SVMCompiler &compiler)
int w_stack_offset = compiler.stack_assign_if_linked(w_in);
int scale_stack_offset = compiler.stack_assign_if_linked(scale_in);
int detail_stack_offset = compiler.stack_assign_if_linked(detail_in);
+ int roughness_stack_offset = compiler.stack_assign_if_linked(roughness_in);
int distortion_stack_offset = compiler.stack_assign_if_linked(distortion_in);
int fac_stack_offset = compiler.stack_assign_if_linked(fac_out);
int color_stack_offset = compiler.stack_assign_if_linked(color_out);
@@ -957,11 +960,13 @@ void NoiseTextureNode::compile(SVMCompiler &compiler)
dimensions,
compiler.encode_uchar4(
vector_stack_offset, w_stack_offset, scale_stack_offset, detail_stack_offset),
- compiler.encode_uchar4(distortion_stack_offset, fac_stack_offset, color_stack_offset));
- compiler.add_node(__float_as_int(w),
- __float_as_int(scale),
- __float_as_int(detail),
- __float_as_int(distortion));
+ compiler.encode_uchar4(
+ roughness_stack_offset, distortion_stack_offset, fac_stack_offset, color_stack_offset));
+ compiler.add_node(
+ __float_as_int(w), __float_as_int(scale), __float_as_int(detail), __float_as_int(roughness));
+
+ compiler.add_node(
+ __float_as_int(distortion), SVM_STACK_INVALID, SVM_STACK_INVALID, SVM_STACK_INVALID);
tex_mapping.compile_end(compiler, vector_in, vector_stack_offset);
}
@@ -1343,14 +1348,14 @@ NODE_DEFINE(WaveTextureNode)
profile_enum.insert("tri", NODE_WAVE_PROFILE_TRI);
SOCKET_ENUM(profile, "Profile", profile_enum, NODE_WAVE_PROFILE_SIN);
+ SOCKET_IN_POINT(
+ vector, "Vector", make_float3(0.0f, 0.0f, 0.0f), SocketType::LINK_TEXTURE_GENERATED);
SOCKET_IN_FLOAT(scale, "Scale", 1.0f);
SOCKET_IN_FLOAT(distortion, "Distortion", 0.0f);
SOCKET_IN_FLOAT(detail, "Detail", 2.0f);
SOCKET_IN_FLOAT(detail_scale, "Detail Scale", 0.0f);
+ SOCKET_IN_FLOAT(detail_roughness, "Detail Roughness", 0.5f);
SOCKET_IN_FLOAT(phase, "Phase Offset", 0.0f);
- SOCKET_IN_POINT(
- vector, "Vector", make_float3(0.0f, 0.0f, 0.0f), SocketType::LINK_TEXTURE_GENERATED);
-
SOCKET_OUT_COLOR(color, "Color");
SOCKET_OUT_FLOAT(fac, "Fac");
@@ -1368,6 +1373,7 @@ void WaveTextureNode::compile(SVMCompiler &compiler)
ShaderInput *distortion_in = input("Distortion");
ShaderInput *detail_in = input("Detail");
ShaderInput *dscale_in = input("Detail Scale");
+ ShaderInput *droughness_in = input("Detail Roughness");
ShaderInput *phase_in = input("Phase Offset");
ShaderOutput *color_out = output("Color");
ShaderOutput *fac_out = output("Fac");
@@ -1378,20 +1384,22 @@ void WaveTextureNode::compile(SVMCompiler &compiler)
compiler.encode_uchar4(type, bands_direction, rings_direction, profile),
compiler.encode_uchar4(vector_offset,
compiler.stack_assign_if_linked(scale_in),
- compiler.stack_assign_if_linked(distortion_in),
- compiler.stack_assign_if_linked(detail_in)),
- compiler.encode_uchar4(compiler.stack_assign_if_linked(dscale_in),
- compiler.stack_assign_if_linked(phase_in),
- compiler.stack_assign_if_linked(color_out),
- compiler.stack_assign_if_linked(fac_out)));
+ compiler.stack_assign_if_linked(distortion_in)),
+ compiler.encode_uchar4(compiler.stack_assign_if_linked(detail_in),
+ compiler.stack_assign_if_linked(dscale_in),
+ compiler.stack_assign_if_linked(droughness_in),
+ compiler.stack_assign_if_linked(phase_in)));
- compiler.add_node(__float_as_int(scale),
- __float_as_int(detail),
+ compiler.add_node(compiler.encode_uchar4(compiler.stack_assign_if_linked(color_out),
+ compiler.stack_assign_if_linked(fac_out)),
+ __float_as_int(scale),
__float_as_int(distortion),
- __float_as_int(detail_scale));
+ __float_as_int(detail));
- compiler.add_node(
- __float_as_int(phase), SVM_STACK_INVALID, SVM_STACK_INVALID, SVM_STACK_INVALID);
+ compiler.add_node(__float_as_int(detail_scale),
+ __float_as_int(detail_roughness),
+ __float_as_int(phase),
+ SVM_STACK_INVALID);
tex_mapping.compile_end(compiler, vector_in, vector_offset);
}
diff --git a/intern/cycles/render/nodes.h b/intern/cycles/render/nodes.h
index e201118574b..8316fa3cf9b 100644
--- a/intern/cycles/render/nodes.h
+++ b/intern/cycles/render/nodes.h
@@ -230,7 +230,7 @@ class NoiseTextureNode : public TextureNode {
SHADER_NODE_CLASS(NoiseTextureNode)
int dimensions;
- float w, scale, detail, distortion;
+ float w, scale, detail, roughness, distortion;
float3 vector;
};
@@ -291,7 +291,7 @@ class WaveTextureNode : public TextureNode {
NodeWaveRingsDirection rings_direction;
NodeWaveProfile profile;
- float scale, distortion, detail, detail_scale, phase;
+ float scale, distortion, detail, detail_scale, detail_roughness, phase;
float3 vector;
};
diff --git a/intern/ghost/intern/GHOST_WindowWin32.cpp b/intern/ghost/intern/GHOST_WindowWin32.cpp
index d4fe7af8861..1ca0dd47cbe 100644
--- a/intern/ghost/intern/GHOST_WindowWin32.cpp
+++ b/intern/ghost/intern/GHOST_WindowWin32.cpp
@@ -1072,11 +1072,10 @@ void GHOST_WindowWin32::initializeWintab()
// Wintab provides no way to determine the maximum queue size aside from checking if attempts
// to change the queue size are successful.
const int maxQueue = 500;
- int initialQueueSize = m_wintab.queueSizeGet(m_wintab.context);
- int queueSize = initialQueueSize;
+ int queueSize = m_wintab.queueSizeGet(m_wintab.context);
while (queueSize < maxQueue) {
- int testSize = min(queueSize + initialQueueSize, maxQueue);
+ int testSize = min(queueSize + 16, maxQueue);
if (m_wintab.queueSizeSet(m_wintab.context, testSize)) {
queueSize = testSize;
}
diff --git a/release/scripts/presets/keyconfig/keymap_data/blender_default.py b/release/scripts/presets/keyconfig/keymap_data/blender_default.py
index 3944f8f0817..90df48581bf 100644
--- a/release/scripts/presets/keyconfig/keymap_data/blender_default.py
+++ b/release/scripts/presets/keyconfig/keymap_data/blender_default.py
@@ -6514,6 +6514,16 @@ def km_sequencer_editor_tool_select_box(params):
)
+def km_sequencer_editor_tool_generic_sample(params):
+ return (
+ "Sequencer Tool: Sample",
+ {"space_type": 'SEQUENCE_EDITOR', "region_type": 'WINDOW'},
+ {"items": [
+ ("sequencer.sample", {"type": params.tool_mouse, "value": 'PRESS'}, None),
+ ]},
+ )
+
+
def km_sequencer_editor_tool_blade(_params):
return (
"Sequencer Tool: Blade",
@@ -6758,6 +6768,7 @@ def generate_keymaps(params=None):
km_sequencer_editor_tool_select(params),
km_sequencer_editor_tool_select_box(params),
km_sequencer_editor_tool_blade(params),
+ km_sequencer_editor_tool_generic_sample(params),
]
# ------------------------------------------------------------------------------
diff --git a/release/scripts/startup/bl_ui/properties_data_modifier.py b/release/scripts/startup/bl_ui/properties_data_modifier.py
index 623c8e998da..5a1935f915a 100644
--- a/release/scripts/startup/bl_ui/properties_data_modifier.py
+++ b/release/scripts/startup/bl_ui/properties_data_modifier.py
@@ -396,6 +396,10 @@ class DATA_PT_modifiers(ModifierButtonsPanel, Panel):
if md.texture_coords == 'OBJECT':
col.label(text="Object:")
col.prop(md, "texture_coords_object", text="")
+ obj = md.texture_coords_object
+ if obj and obj.type == 'ARMATURE':
+ col.label(text="Bone:")
+ col.prop_search(md, "texture_coords_bone", obj.data, "bones", text="")
elif md.texture_coords == 'UV' and ob.type == 'MESH':
col.label(text="UV Map:")
col.prop_search(md, "uv_layer", ob.data, "uv_layers", text="")
@@ -1045,6 +1049,8 @@ class DATA_PT_modifiers(ModifierButtonsPanel, Panel):
col.prop(md, "edge_crease_inner", text="Inner")
col.prop(md, "edge_crease_outer", text="Outer")
col.prop(md, "edge_crease_rim", text="Rim")
+ else:
+ col.prop(md, "nonmanifold_merge_threshold")
col = split.column()
@@ -1244,6 +1250,9 @@ class DATA_PT_modifiers(ModifierButtonsPanel, Panel):
if md.texture_coords == 'OBJECT':
layout.prop(md, "texture_coords_object", text="Object")
+ obj = md.texture_coords_object
+ if obj and obj.type == 'ARMATURE':
+ layout.prop_search(md, "texture_coords_bone", obj.data, "bones", text="Bone")
elif md.texture_coords == 'UV' and ob.type == 'MESH':
layout.prop_search(md, "uv_layer", ob.data, "uv_layers")
@@ -1296,6 +1305,9 @@ class DATA_PT_modifiers(ModifierButtonsPanel, Panel):
layout.prop_search(md, "uv_layer", ob.data, "uv_layers")
elif md.texture_coords == 'OBJECT':
layout.prop(md, "texture_coords_object")
+ obj = md.texture_coords_object
+ if obj and obj.type == 'ARMATURE':
+ layout.prop_search(md, "texture_coords_bone", obj.data, "bones")
layout.separator()
@@ -1366,6 +1378,9 @@ class DATA_PT_modifiers(ModifierButtonsPanel, Panel):
if md.mask_tex_mapping == 'OBJECT':
layout.prop(md, "mask_tex_map_object", text="Object")
+ obj = md.mask_text_map_object
+ if obj and obj.type == 'ARMATURE':
+ layout.prop_search(md, "mask_tex_map_bone", obj.data, "bones", text="Bone")
elif md.mask_tex_mapping == 'UV' and ob.type == 'MESH':
layout.prop_search(md, "mask_tex_uv_layer", ob.data, "uv_layers")
diff --git a/release/scripts/startup/bl_ui/properties_paint_common.py b/release/scripts/startup/bl_ui/properties_paint_common.py
index 0b006c9cea3..32b386bf0ab 100644
--- a/release/scripts/startup/bl_ui/properties_paint_common.py
+++ b/release/scripts/startup/bl_ui/properties_paint_common.py
@@ -902,7 +902,7 @@ def draw_color_settings(context, layout, brush, color_type=False):
UnifiedPaintPanel.prop_unified_color(row, context, brush, "secondary_color", text="")
row.separator()
row.operator("paint.brush_colors_flip", icon='FILE_REFRESH', text="", emboss=False)
- row.prop(ups, "use_unified_color", text="", icon='WORLD')
+ row.prop(ups, "use_unified_color", text="", icon='BRUSHES_ALL')
# Gradient
elif brush.color_type == 'GRADIENT':
layout.template_color_ramp(brush, "gradient", expand=True)
diff --git a/release/scripts/startup/bl_ui/space_image.py b/release/scripts/startup/bl_ui/space_image.py
index 980f89eaaa4..4506307067e 100644
--- a/release/scripts/startup/bl_ui/space_image.py
+++ b/release/scripts/startup/bl_ui/space_image.py
@@ -703,7 +703,7 @@ class IMAGE_HT_header(Header):
layout.prop(tool_settings, "uv_select_mode", text="", expand=True)
layout.prop(uvedit, "sticky_select_mode", icon_only=True)
- MASK_MT_editor_menus.draw_collapsible(context, layout)
+ IMAGE_MT_editor_menus.draw_collapsible(context, layout)
layout.separator_spacer()
@@ -749,8 +749,8 @@ class IMAGE_HT_header(Header):
row.operator("image.play_composite", icon='PLAY')
-class MASK_MT_editor_menus(Menu):
- bl_idname = "MASK_MT_editor_menus"
+class IMAGE_MT_editor_menus(Menu):
+ bl_idname = "IMAGE_MT_editor_menus"
bl_label = ""
def draw(self, context):
@@ -1465,7 +1465,7 @@ classes = (
IMAGE_MT_uvs_snap_pie,
IMAGE_HT_tool_header,
IMAGE_HT_header,
- MASK_MT_editor_menus,
+ IMAGE_MT_editor_menus,
IMAGE_PT_active_tool,
IMAGE_PT_mask,
IMAGE_PT_mask_layers,
diff --git a/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py b/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py
index cb3d2909205..5b7db2342e5 100644
--- a/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py
+++ b/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py
@@ -1807,6 +1807,21 @@ class _defs_sequencer_generic:
draw_settings=draw_settings,
)
+ @ToolDef.from_fn
+ def sample():
+ def draw_settings(_context, layout, tool):
+ props = tool.operator_properties("sequencer.sample")
+ return dict(
+ idname="builtin.sample",
+ label="Sample",
+ description=(
+ "Sample pixel values under the cursor"
+ ),
+ icon="ops.paint.weight_sample", # XXX, needs own icon.
+ keymap="Sequencer Tool: Sample",
+ draw_settings=draw_settings,
+ )
+
class _defs_sequencer_select:
@ToolDef.from_fn
@@ -2348,6 +2363,7 @@ class SEQUENCER_PT_tools_active(ToolSelectPanelHelper, Panel):
None: [
],
'PREVIEW': [
+ _defs_sequencer_generic.sample,
*_tools_annotate,
],
'SEQUENCER': [
@@ -2355,6 +2371,7 @@ class SEQUENCER_PT_tools_active(ToolSelectPanelHelper, Panel):
_defs_sequencer_generic.blade,
],
'SEQUENCER_PREVIEW': [
+ _defs_sequencer_generic.sample,
*_tools_select,
*_tools_annotate,
_defs_sequencer_generic.blade,
diff --git a/release/scripts/startup/bl_ui/space_view3d.py b/release/scripts/startup/bl_ui/space_view3d.py
index e7b21426d03..af86a55dfe7 100644
--- a/release/scripts/startup/bl_ui/space_view3d.py
+++ b/release/scripts/startup/bl_ui/space_view3d.py
@@ -3883,6 +3883,8 @@ class VIEW3D_MT_edit_mesh_extrude(Menu):
return menu
def draw(self, context):
+ from math import pi
+
layout = self.layout
layout.operator_context = 'INVOKE_REGION_WIN'
@@ -3892,6 +3894,7 @@ class VIEW3D_MT_edit_mesh_extrude(Menu):
layout.separator()
layout.operator("mesh.extrude_repeat")
+ layout.operator("mesh.spin").angle = pi * 2
class VIEW3D_MT_edit_mesh_vertices(Menu):
diff --git a/source/blender/blenkernel/BKE_layer.h b/source/blender/blenkernel/BKE_layer.h
index 7059675ec7d..7a0252e6813 100644
--- a/source/blender/blenkernel/BKE_layer.h
+++ b/source/blender/blenkernel/BKE_layer.h
@@ -137,6 +137,7 @@ void BKE_layer_collection_set_visible(struct ViewLayer *view_layer,
struct LayerCollection *lc,
const bool visible,
const bool hierarchy);
+void BKE_layer_collection_set_flag(struct LayerCollection *lc, const int flag, const bool value);
/* evaluation */
diff --git a/source/blender/blenkernel/BKE_sequencer.h b/source/blender/blenkernel/BKE_sequencer.h
index 1b4ca1350ad..9268fdf1391 100644
--- a/source/blender/blenkernel/BKE_sequencer.h
+++ b/source/blender/blenkernel/BKE_sequencer.h
@@ -265,7 +265,7 @@ void BKE_sequencer_base_clipboard_pointers_free(struct ListBase *seqbase);
void BKE_sequencer_base_clipboard_pointers_store(struct Main *bmain, struct ListBase *seqbase);
void BKE_sequencer_base_clipboard_pointers_restore(struct ListBase *seqbase, struct Main *bmain);
-void BKE_sequence_free(struct Scene *scene, struct Sequence *seq);
+void BKE_sequence_free(struct Scene *scene, struct Sequence *seq, const bool do_clean_animdata);
void BKE_sequence_free_anim(struct Sequence *seq);
const char *BKE_sequence_give_name(struct Sequence *seq);
ListBase *BKE_sequence_seqbase_get(struct Sequence *seq, int *r_offset);
@@ -336,7 +336,8 @@ void BKE_sequencer_cache_cleanup(struct Scene *scene);
void BKE_sequencer_cache_cleanup_sequence(struct Scene *scene,
struct Sequence *seq,
struct Sequence *seq_changed,
- int invalidate_types);
+ int invalidate_types,
+ bool force_seq_changed_range);
void BKE_sequencer_cache_iterate(struct Scene *scene,
void *userdata,
bool callback_init(void *userdata, size_t item_count),
@@ -434,6 +435,10 @@ void BKE_sequence_invalidate_cache_composite(struct Scene *scene, struct Sequenc
void BKE_sequence_invalidate_dependent(struct Scene *scene, struct Sequence *seq);
void BKE_sequence_invalidate_scene_strips(struct Main *bmain, struct Scene *scene_target);
void BKE_sequence_invalidate_movieclip_strips(struct Main *bmain, struct MovieClip *clip_target);
+void BKE_sequence_invalidate_cache_in_range(struct Scene *scene,
+ struct Sequence *seq,
+ struct Sequence *range_mask,
+ int invalidate_types);
void BKE_sequencer_update_sound_bounds_all(struct Scene *scene);
void BKE_sequencer_update_sound_bounds(struct Scene *scene, struct Sequence *seq);
diff --git a/source/blender/blenkernel/intern/constraint.c b/source/blender/blenkernel/intern/constraint.c
index c91bf4e7f5b..099fdacf401 100644
--- a/source/blender/blenkernel/intern/constraint.c
+++ b/source/blender/blenkernel/intern/constraint.c
@@ -894,55 +894,64 @@ static void childof_evaluate(bConstraint *con, bConstraintOb *cob, ListBase *tar
}
float parmat[4][4];
+ float inverse_matrix[4][4];
/* Simple matrix parenting. */
if ((data->flag & CHILDOF_ALL) == CHILDOF_ALL) {
copy_m4_m4(parmat, ct->matrix);
+ copy_m4_m4(inverse_matrix, data->invmat);
}
/* Filter the parent matrix by channel. */
else {
float loc[3], eul[3], size[3];
+ float loco[3], eulo[3], sizeo[3];
/* extract components of both matrices */
copy_v3_v3(loc, ct->matrix[3]);
mat4_to_eulO(eul, ct->rotOrder, ct->matrix);
mat4_to_size(size, ct->matrix);
- /* disable channels not enabled */
+ copy_v3_v3(loco, data->invmat[3]);
+ mat4_to_eulO(eulo, cob->rotOrder, data->invmat);
+ mat4_to_size(sizeo, data->invmat);
+
+ /* Reset the locked channels to their no-op values. */
if (!(data->flag & CHILDOF_LOCX)) {
- loc[0] = 0.0f;
+ loc[0] = loco[0] = 0.0f;
}
if (!(data->flag & CHILDOF_LOCY)) {
- loc[1] = 0.0f;
+ loc[1] = loco[1] = 0.0f;
}
if (!(data->flag & CHILDOF_LOCZ)) {
- loc[2] = 0.0f;
+ loc[2] = loco[2] = 0.0f;
}
if (!(data->flag & CHILDOF_ROTX)) {
- eul[0] = 0.0f;
+ eul[0] = eulo[0] = 0.0f;
}
if (!(data->flag & CHILDOF_ROTY)) {
- eul[1] = 0.0f;
+ eul[1] = eulo[1] = 0.0f;
}
if (!(data->flag & CHILDOF_ROTZ)) {
- eul[2] = 0.0f;
+ eul[2] = eulo[2] = 0.0f;
}
if (!(data->flag & CHILDOF_SIZEX)) {
- size[0] = 1.0f;
+ size[0] = sizeo[0] = 1.0f;
}
if (!(data->flag & CHILDOF_SIZEY)) {
- size[1] = 1.0f;
+ size[1] = sizeo[1] = 1.0f;
}
if (!(data->flag & CHILDOF_SIZEZ)) {
- size[2] = 1.0f;
+ size[2] = sizeo[2] = 1.0f;
}
- /* make new target mat and offset mat */
+ /* Construct the new matrices given the disabled channels. */
loc_eulO_size_to_mat4(parmat, loc, eul, size, ct->rotOrder);
+ loc_eulO_size_to_mat4(inverse_matrix, loco, eulo, sizeo, cob->rotOrder);
}
- /* Compute the inverse matrix if requested. */
+ /* If requested, compute the inverse matrix from the computed parent matrix. */
if (data->flag & CHILDOF_SET_INVERSE) {
invert_m4_m4(data->invmat, parmat);
+ copy_m4_m4(inverse_matrix, data->invmat);
data->flag &= ~CHILDOF_SET_INVERSE;
@@ -962,7 +971,7 @@ static void childof_evaluate(bConstraint *con, bConstraintOb *cob, ListBase *tar
* (i.e. owner is 'parented' to parent). */
float orig_cob_matrix[4][4];
copy_m4_m4(orig_cob_matrix, cob->matrix);
- mul_m4_series(cob->matrix, parmat, data->invmat, orig_cob_matrix);
+ mul_m4_series(cob->matrix, parmat, inverse_matrix, orig_cob_matrix);
/* Without this, changes to scale and rotation can change location
* of a parentless bone or a disconnected bone. Even though its set
diff --git a/source/blender/blenkernel/intern/curve.c b/source/blender/blenkernel/intern/curve.c
index 5b463059f18..b4a8625c0bb 100644
--- a/source/blender/blenkernel/intern/curve.c
+++ b/source/blender/blenkernel/intern/curve.c
@@ -1877,7 +1877,10 @@ void BKE_curve_bevel_make(Object *ob, ListBase *disp)
}
/* Don't duplicate the last back vertex. */
angle = (cu->ext1 == 0.0f && (cu->flag & CU_BACK)) ? dangle : 0;
- for (a = 0; a < cu->bevresol + 2; a++) {
+ int front_len = (cu->ext1 == 0.0f && ((cu->flag & CU_BACK) || !(cu->flag & CU_FRONT))) ?
+ cu->bevresol + 1 :
+ cu->bevresol + 2;
+ for (a = 0; a < front_len; a++) {
fp[0] = 0.0;
fp[1] = (float)(cosf(angle) * (cu->ext2));
fp[2] = (float)(sinf(angle) * (cu->ext2)) + cu->ext1;
diff --git a/source/blender/blenkernel/intern/customdata.c b/source/blender/blenkernel/intern/customdata.c
index 754c2ce097f..eb5a90a7c4a 100644
--- a/source/blender/blenkernel/intern/customdata.c
+++ b/source/blender/blenkernel/intern/customdata.c
@@ -315,6 +315,9 @@ static void layerInterp_mdeformvert(const void **sources,
if (totweight) {
dvert->totweight = totweight;
for (i = 0, node = dest_dwlink; node; node = node->next, i++) {
+ if (node->dw.weight > 1.0f) {
+ node->dw.weight = 1.0f;
+ }
dvert->dw[i] = node->dw;
}
}
diff --git a/source/blender/blenkernel/intern/editmesh_tangent.c b/source/blender/blenkernel/intern/editmesh_tangent.c
index e291a68a4b1..c3ae2a54e13 100644
--- a/source/blender/blenkernel/intern/editmesh_tangent.c
+++ b/source/blender/blenkernel/intern/editmesh_tangent.c
@@ -364,7 +364,7 @@ void BKE_editmesh_loop_tangent_calc(BMEditMesh *em,
if (em->tottri != 0) {
TaskScheduler *scheduler = BLI_task_scheduler_get();
TaskPool *task_pool;
- task_pool = BLI_task_pool_create(scheduler, NULL);
+ task_pool = BLI_task_pool_create(scheduler, NULL, TASK_PRIORITY_LOW);
tangent_mask_curr = 0;
/* Calculate tangent layers */
@@ -417,8 +417,7 @@ void BKE_editmesh_loop_tangent_calc(BMEditMesh *em,
mesh2tangent->looptris = (const BMLoop *(*)[3])em->looptris;
mesh2tangent->tangent = loopdata_out->layers[index].data;
- BLI_task_pool_push(
- task_pool, emDM_calc_loop_tangents_thread, mesh2tangent, false, TASK_PRIORITY_LOW);
+ BLI_task_pool_push(task_pool, emDM_calc_loop_tangents_thread, mesh2tangent, false, NULL);
}
BLI_assert(tangent_mask_curr == tangent_mask);
diff --git a/source/blender/blenkernel/intern/fluid.c b/source/blender/blenkernel/intern/fluid.c
index e96f65751de..8076cc0967b 100644
--- a/source/blender/blenkernel/intern/fluid.c
+++ b/source/blender/blenkernel/intern/fluid.c
@@ -3788,6 +3788,13 @@ static void BKE_fluid_modifier_processDomain(FluidModifierData *mmd,
prev_particles = manta_has_particles(mds->fluid, mmd, scene_framenr - 1);
prev_guide = manta_has_guiding(mds->fluid, mmd, scene_framenr - 1, guide_parent);
+ /* Unused for now, but needed for proper caching. */
+ UNUSED_VARS(prev_guide);
+ UNUSED_VARS(next_noise);
+ UNUSED_VARS(next_mesh);
+ UNUSED_VARS(next_particles);
+ UNUSED_VARS(next_guide);
+
bool with_gdomain;
with_gdomain = (mds->guide_source == FLUID_DOMAIN_GUIDE_SRC_DOMAIN);
diff --git a/source/blender/blenkernel/intern/layer.c b/source/blender/blenkernel/intern/layer.c
index d8f0bda8c22..f6e80d66ad1 100644
--- a/source/blender/blenkernel/intern/layer.c
+++ b/source/blender/blenkernel/intern/layer.c
@@ -1359,6 +1359,49 @@ void BKE_layer_collection_set_visible(ViewLayer *view_layer,
}
}
+/**
+ * Set layer collection hide/exclude/indirect flag on a layer collection.
+ * recursively.
+ */
+static void layer_collection_flag_recursive_set(LayerCollection *lc,
+ const int flag,
+ const bool value,
+ const bool restore_flag)
+{
+ if (flag == LAYER_COLLECTION_EXCLUDE) {
+ /* For exclude flag, we remember the state the children had before
+ * excluding and restoring it when enabling the parent collection again. */
+ if (value) {
+ if (restore_flag) {
+ SET_FLAG_FROM_TEST(
+ lc->flag, (lc->flag & LAYER_COLLECTION_EXCLUDE), LAYER_COLLECTION_PREVIOUSLY_EXCLUDED);
+ }
+ else {
+ lc->flag &= ~LAYER_COLLECTION_PREVIOUSLY_EXCLUDED;
+ }
+
+ lc->flag |= flag;
+ }
+ else {
+ if (!(lc->flag & LAYER_COLLECTION_PREVIOUSLY_EXCLUDED)) {
+ lc->flag &= ~flag;
+ }
+ }
+ }
+ else {
+ SET_FLAG_FROM_TEST(lc->flag, value, flag);
+ }
+
+ LISTBASE_FOREACH (LayerCollection *, nlc, &lc->layer_collections) {
+ layer_collection_flag_recursive_set(nlc, flag, value, true);
+ }
+}
+
+void BKE_layer_collection_set_flag(LayerCollection *lc, const int flag, const bool value)
+{
+ layer_collection_flag_recursive_set(lc, flag, value, false);
+}
+
/* ---------------------------------------------------------------------- */
static LayerCollection *find_layer_collection_by_scene_collection(LayerCollection *lc,
diff --git a/source/blender/blenkernel/intern/lib_id.c b/source/blender/blenkernel/intern/lib_id.c
index 487ec0bf161..80f29a55b28 100644
--- a/source/blender/blenkernel/intern/lib_id.c
+++ b/source/blender/blenkernel/intern/lib_id.c
@@ -1720,21 +1720,31 @@ static void library_make_local_copying_check(ID *id,
/* Used_to_user stores ID pointer, not pointer to ID pointer. */
ID *par_id = (ID *)entry->id_pointer;
- /* Our oh-so-beloved 'from' pointers... */
+ /* Our oh-so-beloved 'from' pointers... Those should always be ignored here, since the actual
+ * relation we want to check is in the other way around. */
if (entry->usage_flag & IDWALK_CB_LOOPBACK) {
- /* We totally disregard Object->proxy_from 'usage' here,
- * this one would only generate fake positives. */
- if (GS(par_id->name) == ID_OB) {
- BLI_assert(((Object *)par_id)->proxy_from == (Object *)id);
- continue;
+#ifndef NDEBUG
+ /* Some debug checks to ensure we explicitly are aware of all 'loop-back' cases, since those
+ * may not always be manageable in the same way... */
+ switch (GS(par_id->name)) {
+ case ID_OB:
+ BLI_assert(((Object *)par_id)->proxy_from == (Object *)id);
+ break;
+ case ID_KE:
+ BLI_assert(((Key *)par_id)->from == id);
+ break;
+ default:
+ BLI_assert(0);
}
+#endif
+ continue;
+ }
- /* Shapekeys are considered 'private' to their owner ID here, and never tagged
- * (since they cannot be linked), so we have to switch effective parent to their owner.
- */
- if (GS(par_id->name) == ID_KE) {
- par_id = ((Key *)par_id)->from;
- }
+ /* Shapekeys are considered 'private' to their owner ID here, and never tagged
+ * (since they cannot be linked), so we have to switch effective parent to their owner.
+ */
+ if (GS(par_id->name) == ID_KE) {
+ par_id = ((Key *)par_id)->from;
}
if (par_id->lib == NULL) {
diff --git a/source/blender/blenkernel/intern/mesh_evaluate.c b/source/blender/blenkernel/intern/mesh_evaluate.c
index 17fd7b18bab..e5be85b5ec7 100644
--- a/source/blender/blenkernel/intern/mesh_evaluate.c
+++ b/source/blender/blenkernel/intern/mesh_evaluate.c
@@ -1555,7 +1555,7 @@ static void loop_split_generator(TaskPool *pool, LoopSplitTaskDataCommon *common
if (pool) {
data_idx++;
if (data_idx == LOOP_SPLIT_TASK_BLOCK_SIZE) {
- BLI_task_pool_push(pool, loop_split_worker, data_buff, true, TASK_PRIORITY_LOW);
+ BLI_task_pool_push(pool, loop_split_worker, data_buff, true, NULL);
data_idx = 0;
}
}
@@ -1572,7 +1572,7 @@ static void loop_split_generator(TaskPool *pool, LoopSplitTaskDataCommon *common
/* Last block of data... Since it is calloc'ed and we use first NULL item as stopper,
* everything is fine. */
if (pool && data_idx) {
- BLI_task_pool_push(pool, loop_split_worker, data_buff, true, TASK_PRIORITY_LOW);
+ BLI_task_pool_push(pool, loop_split_worker, data_buff, true, NULL);
}
if (edge_vectors) {
@@ -1708,7 +1708,7 @@ void BKE_mesh_normals_loop_split(const MVert *mverts,
TaskPool *task_pool;
task_scheduler = BLI_task_scheduler_get();
- task_pool = BLI_task_pool_create(task_scheduler, &common_data);
+ task_pool = BLI_task_pool_create(task_scheduler, &common_data, TASK_PRIORITY_HIGH);
loop_split_generator(task_pool, &common_data);
diff --git a/source/blender/blenkernel/intern/mesh_tangent.c b/source/blender/blenkernel/intern/mesh_tangent.c
index ebc3e9c490a..a2a198cdb0d 100644
--- a/source/blender/blenkernel/intern/mesh_tangent.c
+++ b/source/blender/blenkernel/intern/mesh_tangent.c
@@ -660,7 +660,7 @@ void BKE_mesh_calc_loop_tangent_ex(const MVert *mvert,
if (looptri_len != 0) {
TaskScheduler *scheduler = BLI_task_scheduler_get();
TaskPool *task_pool;
- task_pool = BLI_task_pool_create(scheduler, NULL);
+ task_pool = BLI_task_pool_create(scheduler, NULL, TASK_PRIORITY_LOW);
tangent_mask_curr = 0;
/* Calculate tangent layers */
@@ -707,8 +707,7 @@ void BKE_mesh_calc_loop_tangent_ex(const MVert *mvert,
}
mesh2tangent->tangent = loopdata_out->layers[index].data;
- BLI_task_pool_push(
- task_pool, DM_calc_loop_tangents_thread, mesh2tangent, false, TASK_PRIORITY_LOW);
+ BLI_task_pool_push(task_pool, DM_calc_loop_tangents_thread, mesh2tangent, false, NULL);
}
BLI_assert(tangent_mask_curr == tangent_mask);
diff --git a/source/blender/blenkernel/intern/ocean.c b/source/blender/blenkernel/intern/ocean.c
index 26485d10fbd..2683d384bc7 100644
--- a/source/blender/blenkernel/intern/ocean.c
+++ b/source/blender/blenkernel/intern/ocean.c
@@ -680,7 +680,7 @@ void BKE_ocean_simulate(struct Ocean *o, float t, float scale, float chop_amount
osd.scale = scale;
osd.chop_amount = chop_amount;
- pool = BLI_task_pool_create(scheduler, &osd);
+ pool = BLI_task_pool_create(scheduler, &osd, TASK_PRIORITY_HIGH);
BLI_rw_mutex_lock(&o->oceanmutex, THREAD_LOCK_WRITE);
@@ -698,23 +698,23 @@ void BKE_ocean_simulate(struct Ocean *o, float t, float scale, float chop_amount
BLI_task_parallel_range(0, o->_M, &osd, ocean_compute_htilda, &settings);
if (o->_do_disp_y) {
- BLI_task_pool_push(pool, ocean_compute_displacement_y, NULL, false, TASK_PRIORITY_HIGH);
+ BLI_task_pool_push(pool, ocean_compute_displacement_y, NULL, false, NULL);
}
if (o->_do_chop) {
- BLI_task_pool_push(pool, ocean_compute_displacement_x, NULL, false, TASK_PRIORITY_HIGH);
- BLI_task_pool_push(pool, ocean_compute_displacement_z, NULL, false, TASK_PRIORITY_HIGH);
+ BLI_task_pool_push(pool, ocean_compute_displacement_x, NULL, false, NULL);
+ BLI_task_pool_push(pool, ocean_compute_displacement_z, NULL, false, NULL);
}
if (o->_do_jacobian) {
- BLI_task_pool_push(pool, ocean_compute_jacobian_jxx, NULL, false, TASK_PRIORITY_HIGH);
- BLI_task_pool_push(pool, ocean_compute_jacobian_jzz, NULL, false, TASK_PRIORITY_HIGH);
- BLI_task_pool_push(pool, ocean_compute_jacobian_jxz, NULL, false, TASK_PRIORITY_HIGH);
+ BLI_task_pool_push(pool, ocean_compute_jacobian_jxx, NULL, false, NULL);
+ BLI_task_pool_push(pool, ocean_compute_jacobian_jzz, NULL, false, NULL);
+ BLI_task_pool_push(pool, ocean_compute_jacobian_jxz, NULL, false, NULL);
}
if (o->_do_normals) {
- BLI_task_pool_push(pool, ocean_compute_normal_x, NULL, false, TASK_PRIORITY_HIGH);
- BLI_task_pool_push(pool, ocean_compute_normal_z, NULL, false, TASK_PRIORITY_HIGH);
+ BLI_task_pool_push(pool, ocean_compute_normal_x, NULL, false, NULL);
+ BLI_task_pool_push(pool, ocean_compute_normal_z, NULL, false, NULL);
o->_N_y = 1.0f / scale;
}
diff --git a/source/blender/blenkernel/intern/particle.c b/source/blender/blenkernel/intern/particle.c
index 587dd5be2f2..0e35fa5d19f 100644
--- a/source/blender/blenkernel/intern/particle.c
+++ b/source/blender/blenkernel/intern/particle.c
@@ -2827,7 +2827,7 @@ void psys_cache_child_paths(ParticleSimulationData *sim,
}
task_scheduler = BLI_task_scheduler_get();
- task_pool = BLI_task_pool_create(task_scheduler, &ctx);
+ task_pool = BLI_task_pool_create(task_scheduler, &ctx, TASK_PRIORITY_LOW);
totchild = ctx.totchild;
totparent = ctx.totparent;
@@ -2850,7 +2850,7 @@ void psys_cache_child_paths(ParticleSimulationData *sim,
ParticleTask *task = &tasks_parent[i];
psys_task_init_path(task, sim);
- BLI_task_pool_push(task_pool, exec_child_path_cache, task, false, TASK_PRIORITY_LOW);
+ BLI_task_pool_push(task_pool, exec_child_path_cache, task, false, NULL);
}
BLI_task_pool_work_and_wait(task_pool);
@@ -2861,7 +2861,7 @@ void psys_cache_child_paths(ParticleSimulationData *sim,
ParticleTask *task = &tasks_child[i];
psys_task_init_path(task, sim);
- BLI_task_pool_push(task_pool, exec_child_path_cache, task, false, TASK_PRIORITY_LOW);
+ BLI_task_pool_push(task_pool, exec_child_path_cache, task, false, NULL);
}
BLI_task_pool_work_and_wait(task_pool);
diff --git a/source/blender/blenkernel/intern/particle_distribute.c b/source/blender/blenkernel/intern/particle_distribute.c
index 9069f549e61..d91e27a92d8 100644
--- a/source/blender/blenkernel/intern/particle_distribute.c
+++ b/source/blender/blenkernel/intern/particle_distribute.c
@@ -1337,7 +1337,7 @@ static void distribute_particles_on_dm(ParticleSimulationData *sim, int from)
}
task_scheduler = BLI_task_scheduler_get();
- task_pool = BLI_task_pool_create(task_scheduler, &ctx);
+ task_pool = BLI_task_pool_create(task_scheduler, &ctx, TASK_PRIORITY_LOW);
totpart = (from == PART_FROM_CHILD ? sim->psys->totchild : sim->psys->totpart);
psys_tasks_create(&ctx, 0, totpart, &tasks, &numtasks);
@@ -1346,10 +1346,10 @@ static void distribute_particles_on_dm(ParticleSimulationData *sim, int from)
psys_task_init_distribute(task, sim);
if (from == PART_FROM_CHILD) {
- BLI_task_pool_push(task_pool, exec_distribute_child, task, false, TASK_PRIORITY_LOW);
+ BLI_task_pool_push(task_pool, exec_distribute_child, task, false, NULL);
}
else {
- BLI_task_pool_push(task_pool, exec_distribute_parent, task, false, TASK_PRIORITY_LOW);
+ BLI_task_pool_push(task_pool, exec_distribute_parent, task, false, NULL);
}
}
BLI_task_pool_work_and_wait(task_pool);
diff --git a/source/blender/blenkernel/intern/seqcache.c b/source/blender/blenkernel/intern/seqcache.c
index 12c5821e858..f999a98faac 100644
--- a/source/blender/blenkernel/intern/seqcache.c
+++ b/source/blender/blenkernel/intern/seqcache.c
@@ -1153,7 +1153,8 @@ void BKE_sequencer_cache_cleanup(Scene *scene)
void BKE_sequencer_cache_cleanup_sequence(Scene *scene,
Sequence *seq,
Sequence *seq_changed,
- int invalidate_types)
+ int invalidate_types,
+ bool force_seq_changed_range)
{
SeqCache *cache = seq_cache_get_from_scene(scene);
if (!cache) {
@@ -1169,12 +1170,14 @@ void BKE_sequencer_cache_cleanup_sequence(Scene *scene,
int range_start = seq_changed->startdisp;
int range_end = seq_changed->enddisp;
- if (seq->startdisp > range_start) {
- range_start = seq->startdisp;
- }
+ if (!force_seq_changed_range) {
+ if (seq->startdisp > range_start) {
+ range_start = seq->startdisp;
+ }
- if (seq->enddisp < range_end) {
- range_end = seq->enddisp;
+ if (seq->enddisp < range_end) {
+ range_end = seq->enddisp;
+ }
}
int invalidate_composite = invalidate_types & SEQ_CACHE_STORE_FINAL_OUT;
diff --git a/source/blender/blenkernel/intern/sequencer.c b/source/blender/blenkernel/intern/sequencer.c
index bb1d8cb2e9b..93e5a3bbd74 100644
--- a/source/blender/blenkernel/intern/sequencer.c
+++ b/source/blender/blenkernel/intern/sequencer.c
@@ -106,6 +106,14 @@ static ImBuf *seq_render_strip_stack(const SeqRenderData *context,
ListBase *seqbasep,
float cfra,
int chanshown);
+static ImBuf *seq_render_preprocess_ibuf(const SeqRenderData *context,
+ Sequence *seq,
+ ImBuf *ibuf,
+ float cfra,
+ clock_t begin,
+ bool use_preprocess,
+ const bool is_proxy_image,
+ const bool is_preprocessed);
static ImBuf *seq_render_strip(const SeqRenderData *context,
SeqRenderState *state,
Sequence *seq,
@@ -228,7 +236,8 @@ static void seq_free_strip(Strip *strip)
static void BKE_sequence_free_ex(Scene *scene,
Sequence *seq,
const bool do_cache,
- const bool do_id_user)
+ const bool do_id_user,
+ const bool do_clean_animdata)
{
if (seq->strip) {
seq_free_strip(seq->strip);
@@ -263,7 +272,10 @@ static void BKE_sequence_free_ex(Scene *scene,
BKE_sound_remove_scene_sound(scene, seq->scene_sound);
}
- seq_free_animdata(scene, seq);
+ /* XXX This must not be done in BKE code. */
+ if (do_clean_animdata) {
+ seq_free_animdata(scene, seq);
+ }
}
if (seq->prop) {
@@ -291,9 +303,9 @@ static void BKE_sequence_free_ex(Scene *scene,
MEM_freeN(seq);
}
-void BKE_sequence_free(Scene *scene, Sequence *seq)
+void BKE_sequence_free(Scene *scene, Sequence *seq, const bool do_clean_animdata)
{
- BKE_sequence_free_ex(scene, seq, true, true);
+ BKE_sequence_free_ex(scene, seq, true, true, do_clean_animdata);
}
/* Function to free imbuf and anim data on changes */
@@ -323,7 +335,7 @@ static void seq_free_sequence_recurse(Scene *scene, Sequence *seq, const bool do
seq_free_sequence_recurse(scene, iseq, do_id_user);
}
- BKE_sequence_free_ex(scene, seq, false, do_id_user);
+ BKE_sequence_free_ex(scene, seq, false, do_id_user, true);
}
Editing *BKE_sequencer_editing_get(Scene *scene, bool alloc)
@@ -493,7 +505,7 @@ void BKE_sequencer_editing_free(Scene *scene, const bool do_id_user)
SEQ_BEGIN (ed, seq) {
/* handle cache freeing above */
- BKE_sequence_free_ex(scene, seq, false, do_id_user);
+ BKE_sequence_free_ex(scene, seq, false, do_id_user, false);
}
SEQ_END;
@@ -3026,7 +3038,6 @@ static ImBuf *seq_render_image_strip(const SeqRenderData *context,
struct ImBuf **ibufs_arr;
char prefix[FILE_MAX];
const char *ext = NULL;
- int i;
if (totfiles > 1) {
BKE_scene_multiview_view_prefix_get(context->scene, name, prefix, &ext);
@@ -3041,21 +3052,21 @@ static ImBuf *seq_render_image_strip(const SeqRenderData *context,
totviews = BKE_scene_multiview_num_views_get(&context->scene->r);
ibufs_arr = MEM_callocN(sizeof(ImBuf *) * totviews, "Sequence Image Views Imbufs");
- for (i = 0; i < totfiles; i++) {
+ for (int view_id = 0; view_id < totfiles; view_id++) {
if (prefix[0] == '\0') {
- ibufs_arr[i] = IMB_loadiffname(name, flag, seq->strip->colorspace_settings.name);
+ ibufs_arr[view_id] = IMB_loadiffname(name, flag, seq->strip->colorspace_settings.name);
}
else {
char str[FILE_MAX];
- seq_multiview_name(context->scene, i, prefix, ext, str, FILE_MAX);
- ibufs_arr[i] = IMB_loadiffname(str, flag, seq->strip->colorspace_settings.name);
+ seq_multiview_name(context->scene, view_id, prefix, ext, str, FILE_MAX);
+ ibufs_arr[view_id] = IMB_loadiffname(str, flag, seq->strip->colorspace_settings.name);
}
- if (ibufs_arr[i]) {
+ if (ibufs_arr[view_id]) {
/* we don't need both (speed reasons)! */
- if (ibufs_arr[i]->rect_float && ibufs_arr[i]->rect) {
- imb_freerectImBuf(ibufs_arr[i]);
+ if (ibufs_arr[view_id]->rect_float && ibufs_arr[view_id]->rect) {
+ imb_freerectImBuf(ibufs_arr[view_id]);
}
}
}
@@ -3064,17 +3075,17 @@ static ImBuf *seq_render_image_strip(const SeqRenderData *context,
IMB_ImBufFromStereo3d(seq->stereo3d_format, ibufs_arr[0], &ibufs_arr[0], &ibufs_arr[1]);
}
- for (i = 0; i < totviews; i++) {
- if (ibufs_arr[i]) {
+ for (int view_id = 0; view_id < totviews; view_id++) {
+ if (ibufs_arr[view_id]) {
SeqRenderData localcontext = *context;
- localcontext.view_id = i;
+ localcontext.view_id = view_id;
/* all sequencer color is done in SRGB space, linear gives odd crossfades */
- BKE_sequencer_imbuf_to_sequencer_space(context->scene, ibufs_arr[i], false);
+ BKE_sequencer_imbuf_to_sequencer_space(context->scene, ibufs_arr[view_id], false);
- if (i != context->view_id) {
- BKE_sequencer_cache_put(
- &localcontext, seq, cfra, SEQ_CACHE_STORE_PREPROCESSED, ibufs_arr[i], 0, false);
+ if (view_id != context->view_id) {
+ ibufs_arr[view_id] = seq_render_preprocess_ibuf(
+ &localcontext, seq, ibufs_arr[view_id], cfra, clock(), true, false, false);
}
}
}
@@ -3087,9 +3098,9 @@ static ImBuf *seq_render_image_strip(const SeqRenderData *context,
}
/* "remove" the others (decrease their refcount) */
- for (i = 0; i < totviews; i++) {
- if (ibufs_arr[i] != ibuf) {
- IMB_freeImBuf(ibufs_arr[i]);
+ for (int view_id = 0; view_id < totviews; view_id++) {
+ if (ibufs_arr[view_id] != ibuf) {
+ IMB_freeImBuf(ibufs_arr[view_id]);
}
}
@@ -3137,7 +3148,7 @@ static ImBuf *seq_render_movie_strip(const SeqRenderData *context,
ImBuf **ibuf_arr;
const int totfiles = seq_num_files(context->scene, seq->views_format, true);
int totviews;
- int i;
+ int ibuf_view_id;
if (totfiles != BLI_listbase_count_at_most(&seq->anims, totfiles + 1)) {
goto monoview_movie;
@@ -3146,28 +3157,28 @@ static ImBuf *seq_render_movie_strip(const SeqRenderData *context,
totviews = BKE_scene_multiview_num_views_get(&context->scene->r);
ibuf_arr = MEM_callocN(sizeof(ImBuf *) * totviews, "Sequence Image Views Imbufs");
- for (i = 0, sanim = seq->anims.first; sanim; sanim = sanim->next, i++) {
+ for (ibuf_view_id = 0, sanim = seq->anims.first; sanim; sanim = sanim->next, ibuf_view_id++) {
if (sanim->anim) {
IMB_anim_set_preseek(sanim->anim, seq->anim_preseek);
- ibuf_arr[i] = IMB_anim_absolute(sanim->anim,
- nr + seq->anim_startofs,
- seq->strip->proxy ? seq->strip->proxy->tc :
- IMB_TC_RECORD_RUN,
- psize);
+ ibuf_arr[ibuf_view_id] = IMB_anim_absolute(sanim->anim,
+ nr + seq->anim_startofs,
+ seq->strip->proxy ? seq->strip->proxy->tc :
+ IMB_TC_RECORD_RUN,
+ psize);
/* fetching for requested proxy size failed, try fetching the original instead */
- if (!ibuf_arr[i] && psize != IMB_PROXY_NONE) {
- ibuf_arr[i] = IMB_anim_absolute(sanim->anim,
- nr + seq->anim_startofs,
- seq->strip->proxy ? seq->strip->proxy->tc :
- IMB_TC_RECORD_RUN,
- IMB_PROXY_NONE);
+ if (!ibuf_arr[ibuf_view_id] && psize != IMB_PROXY_NONE) {
+ ibuf_arr[ibuf_view_id] = IMB_anim_absolute(sanim->anim,
+ nr + seq->anim_startofs,
+ seq->strip->proxy ? seq->strip->proxy->tc :
+ IMB_TC_RECORD_RUN,
+ IMB_PROXY_NONE);
}
- if (ibuf_arr[i]) {
+ if (ibuf_arr[ibuf_view_id]) {
/* we don't need both (speed reasons)! */
- if (ibuf_arr[i]->rect_float && ibuf_arr[i]->rect) {
- imb_freerectImBuf(ibuf_arr[i]);
+ if (ibuf_arr[ibuf_view_id]->rect_float && ibuf_arr[ibuf_view_id]->rect) {
+ imb_freerectImBuf(ibuf_arr[ibuf_view_id]);
}
}
}
@@ -3184,17 +3195,17 @@ static ImBuf *seq_render_movie_strip(const SeqRenderData *context,
}
}
- for (i = 0; i < totviews; i++) {
+ for (int view_id = 0; view_id < totviews; view_id++) {
SeqRenderData localcontext = *context;
- localcontext.view_id = i;
+ localcontext.view_id = view_id;
- if (ibuf_arr[i]) {
+ if (ibuf_arr[view_id]) {
/* all sequencer color is done in SRGB space, linear gives odd crossfades */
- BKE_sequencer_imbuf_to_sequencer_space(context->scene, ibuf_arr[i], false);
+ BKE_sequencer_imbuf_to_sequencer_space(context->scene, ibuf_arr[view_id], false);
}
- if (i != context->view_id) {
- BKE_sequencer_cache_put(
- &localcontext, seq, cfra, SEQ_CACHE_STORE_PREPROCESSED, ibuf_arr[i], 0, false);
+ if (view_id != context->view_id) {
+ ibuf_arr[view_id] = seq_render_preprocess_ibuf(
+ &localcontext, seq, ibuf_arr[view_id], cfra, clock(), true, false, false);
}
}
@@ -3206,9 +3217,9 @@ static ImBuf *seq_render_movie_strip(const SeqRenderData *context,
}
/* "remove" the others (decrease their refcount) */
- for (i = 0; i < totviews; i++) {
- if (ibuf_arr[i] != ibuf) {
- IMB_freeImBuf(ibuf_arr[i]);
+ for (int view_id = 0; view_id < totviews; view_id++) {
+ if (ibuf_arr[view_id] != ibuf) {
+ IMB_freeImBuf(ibuf_arr[view_id]);
}
}
@@ -3542,7 +3553,6 @@ static ImBuf *seq_render_scene_strip(const SeqRenderData *context,
else {
Render *re = RE_GetSceneRender(scene);
const int totviews = BKE_scene_multiview_num_views_get(&scene->r);
- int i;
ImBuf **ibufs_arr;
ibufs_arr = MEM_callocN(sizeof(ImBuf *) * totviews, "Sequence Image Views Imbufs");
@@ -3567,34 +3577,37 @@ static ImBuf *seq_render_scene_strip(const SeqRenderData *context,
G.is_rendering = is_rendering;
}
- for (i = 0; i < totviews; i++) {
+ for (int view_id = 0; view_id < totviews; view_id++) {
SeqRenderData localcontext = *context;
RenderResult rres;
- localcontext.view_id = i;
+ localcontext.view_id = view_id;
- RE_AcquireResultImage(re, &rres, i);
+ RE_AcquireResultImage(re, &rres, view_id);
if (rres.rectf) {
- ibufs_arr[i] = IMB_allocImBuf(rres.rectx, rres.recty, 32, IB_rectfloat);
- memcpy(ibufs_arr[i]->rect_float, rres.rectf, 4 * sizeof(float) * rres.rectx * rres.recty);
+ ibufs_arr[view_id] = IMB_allocImBuf(rres.rectx, rres.recty, 32, IB_rectfloat);
+ memcpy(ibufs_arr[view_id]->rect_float,
+ rres.rectf,
+ 4 * sizeof(float) * rres.rectx * rres.recty);
if (rres.rectz) {
- addzbuffloatImBuf(ibufs_arr[i]);
- memcpy(ibufs_arr[i]->zbuf_float, rres.rectz, sizeof(float) * rres.rectx * rres.recty);
+ addzbuffloatImBuf(ibufs_arr[view_id]);
+ memcpy(
+ ibufs_arr[view_id]->zbuf_float, rres.rectz, sizeof(float) * rres.rectx * rres.recty);
}
/* float buffers in the sequencer are not linear */
- BKE_sequencer_imbuf_to_sequencer_space(context->scene, ibufs_arr[i], false);
+ BKE_sequencer_imbuf_to_sequencer_space(context->scene, ibufs_arr[view_id], false);
}
else if (rres.rect32) {
- ibufs_arr[i] = IMB_allocImBuf(rres.rectx, rres.recty, 32, IB_rect);
- memcpy(ibufs_arr[i]->rect, rres.rect32, 4 * rres.rectx * rres.recty);
+ ibufs_arr[view_id] = IMB_allocImBuf(rres.rectx, rres.recty, 32, IB_rect);
+ memcpy(ibufs_arr[view_id]->rect, rres.rect32, 4 * rres.rectx * rres.recty);
}
- if (i != context->view_id) {
+ if (view_id != context->view_id) {
BKE_sequencer_cache_put(
- &localcontext, seq, cfra, SEQ_CACHE_STORE_RAW, ibufs_arr[i], 0, false);
+ &localcontext, seq, cfra, SEQ_CACHE_STORE_RAW, ibufs_arr[view_id], 0, false);
}
RE_ReleaseResultImage(re);
@@ -3604,9 +3617,9 @@ static ImBuf *seq_render_scene_strip(const SeqRenderData *context,
ibuf = ibufs_arr[context->view_id];
/* "remove" the others (decrease their refcount) */
- for (i = 0; i < totviews; i++) {
- if (ibufs_arr[i] != ibuf) {
- IMB_freeImBuf(ibufs_arr[i]);
+ for (int view_id = 0; view_id < totviews; view_id++) {
+ if (ibufs_arr[view_id] != ibuf) {
+ IMB_freeImBuf(ibufs_arr[view_id]);
}
}
MEM_freeN(ibufs_arr);
@@ -3791,6 +3804,34 @@ static float seq_estimate_render_cost_end(Scene *scene, clock_t begin)
}
}
+static ImBuf *seq_render_preprocess_ibuf(const SeqRenderData *context,
+ Sequence *seq,
+ ImBuf *ibuf,
+ float cfra,
+ clock_t begin,
+ bool use_preprocess,
+ const bool is_proxy_image,
+ const bool is_preprocessed)
+{
+ if (context->is_proxy_render == false &&
+ (ibuf->x != context->rectx || ibuf->y != context->recty)) {
+ use_preprocess = true;
+ }
+
+ if (use_preprocess) {
+ float cost = seq_estimate_render_cost_end(context->scene, begin);
+ BKE_sequencer_cache_put(context, seq, cfra, SEQ_CACHE_STORE_RAW, ibuf, cost, false);
+
+ /* Reset timer so we can get partial render time. */
+ begin = seq_estimate_render_cost_begin();
+ ibuf = input_preprocess(context, seq, cfra, ibuf, is_proxy_image, is_preprocessed);
+ }
+
+ float cost = seq_estimate_render_cost_end(context->scene, begin);
+ BKE_sequencer_cache_put(context, seq, cfra, SEQ_CACHE_STORE_PREPROCESSED, ibuf, cost, false);
+ return ibuf;
+}
+
static ImBuf *seq_render_strip(const SeqRenderData *context,
SeqRenderState *state,
Sequence *seq,
@@ -3839,22 +3880,8 @@ static ImBuf *seq_render_strip(const SeqRenderData *context,
sequencer_imbuf_assign_spaces(context->scene, ibuf);
}
- if (context->is_proxy_render == false &&
- (ibuf->x != context->rectx || ibuf->y != context->recty)) {
- use_preprocess = true;
- }
-
- if (use_preprocess) {
- float cost = seq_estimate_render_cost_end(context->scene, begin);
- BKE_sequencer_cache_put(context, seq, cfra, SEQ_CACHE_STORE_RAW, ibuf, cost, false);
-
- /* reset timer so we can get partial render time */
- begin = seq_estimate_render_cost_begin();
- ibuf = input_preprocess(context, seq, cfra, ibuf, is_proxy_image, is_preprocessed);
- }
-
- float cost = seq_estimate_render_cost_end(context->scene, begin);
- BKE_sequencer_cache_put(context, seq, cfra, SEQ_CACHE_STORE_PREPROCESSED, ibuf, cost, false);
+ ibuf = seq_render_preprocess_ibuf(
+ context, seq, ibuf, cfra, begin, use_preprocess, is_proxy_image, is_preprocessed);
}
return ibuf;
}
@@ -4139,8 +4166,15 @@ static void sequence_do_invalidate_dependent(Scene *scene, Sequence *seq, ListBa
}
if (BKE_sequence_check_depend(seq, cur)) {
- BKE_sequencer_cache_cleanup_sequence(
- scene, cur, seq, SEQ_CACHE_STORE_COMPOSITE | SEQ_CACHE_STORE_FINAL_OUT);
+ /* Effect must be invalidated completely if they depend on invalidated seq. */
+ if ((cur->type & SEQ_TYPE_EFFECT) != 0) {
+ BKE_sequencer_cache_cleanup_sequence(scene, cur, seq, SEQ_CACHE_ALL_TYPES, false);
+ }
+ else {
+ /* In case of alpha over for example only invalidate composite image */
+ BKE_sequencer_cache_cleanup_sequence(
+ scene, cur, seq, SEQ_CACHE_STORE_COMPOSITE | SEQ_CACHE_STORE_FINAL_OUT, false);
+ }
}
if (cur->seqbase.first) {
@@ -4158,7 +4192,7 @@ static void sequence_invalidate_cache(Scene *scene,
if (invalidate_self) {
BKE_sequence_free_anim(seq);
- BKE_sequencer_cache_cleanup_sequence(scene, seq, seq, invalidate_types);
+ BKE_sequencer_cache_cleanup_sequence(scene, seq, seq, invalidate_types, false);
}
if (seq->effectdata && seq->type == SEQ_TYPE_SPEED) {
@@ -4170,6 +4204,14 @@ static void sequence_invalidate_cache(Scene *scene,
BKE_sequencer_prefetch_stop(scene);
}
+void BKE_sequence_invalidate_cache_in_range(Scene *scene,
+ Sequence *seq,
+ Sequence *range_mask,
+ int invalidate_types)
+{
+ BKE_sequencer_cache_cleanup_sequence(scene, seq, range_mask, invalidate_types, true);
+}
+
void BKE_sequence_invalidate_cache_raw(Scene *scene, Sequence *seq)
{
sequence_invalidate_cache(scene, seq, true, SEQ_CACHE_ALL_TYPES);
diff --git a/source/blender/blenlib/BLI_math_base.h b/source/blender/blenlib/BLI_math_base.h
index 7a9d17d2b05..ae9617d2f16 100644
--- a/source/blender/blenlib/BLI_math_base.h
+++ b/source/blender/blenlib/BLI_math_base.h
@@ -151,6 +151,9 @@ MINLINE int max_iiii(int a, int b, int c, int d);
MINLINE size_t min_zz(size_t a, size_t b);
MINLINE size_t max_zz(size_t a, size_t b);
+MINLINE char min_cc(char a, char b);
+MINLINE char max_cc(char a, char b);
+
MINLINE int clamp_i(int value, int min, int max);
MINLINE float clamp_f(float value, float min, float max);
MINLINE size_t clamp_z(size_t value, size_t min, size_t max);
diff --git a/source/blender/blenlib/BLI_task.h b/source/blender/blenlib/BLI_task.h
index 83bcdff214d..898e4be5f92 100644
--- a/source/blender/blenlib/BLI_task.h
+++ b/source/blender/blenlib/BLI_task.h
@@ -73,27 +73,25 @@ typedef struct TaskPool TaskPool;
typedef void (*TaskRunFunction)(TaskPool *__restrict pool, void *taskdata, int threadid);
typedef void (*TaskFreeFunction)(TaskPool *__restrict pool, void *taskdata, int threadid);
-TaskPool *BLI_task_pool_create(TaskScheduler *scheduler, void *userdata);
-TaskPool *BLI_task_pool_create_background(TaskScheduler *scheduler, void *userdata);
-TaskPool *BLI_task_pool_create_suspended(TaskScheduler *scheduler, void *userdata);
+TaskPool *BLI_task_pool_create(TaskScheduler *scheduler, void *userdata, TaskPriority priority);
+TaskPool *BLI_task_pool_create_background(TaskScheduler *scheduler,
+ void *userdata,
+ TaskPriority priority);
+TaskPool *BLI_task_pool_create_suspended(TaskScheduler *scheduler,
+ void *userdata,
+ TaskPriority priority);
void BLI_task_pool_free(TaskPool *pool);
-void BLI_task_pool_push_ex(TaskPool *pool,
- TaskRunFunction run,
- void *taskdata,
- bool free_taskdata,
- TaskFreeFunction freedata,
- TaskPriority priority);
void BLI_task_pool_push(TaskPool *pool,
TaskRunFunction run,
void *taskdata,
bool free_taskdata,
- TaskPriority priority);
+ TaskFreeFunction freedata);
void BLI_task_pool_push_from_thread(TaskPool *pool,
TaskRunFunction run,
void *taskdata,
bool free_taskdata,
- TaskPriority priority,
+ TaskFreeFunction freedata,
int thread_id);
/* work and wait until all tasks are done */
@@ -112,6 +110,9 @@ void *BLI_task_pool_userdata(TaskPool *pool);
/* optional mutex to use from run function */
ThreadMutex *BLI_task_pool_user_mutex(TaskPool *pool);
+/* Thread ID of thread that created the task pool. */
+int BLI_task_pool_creator_thread_id(TaskPool *pool);
+
/* Delayed push, use that to reduce thread overhead by accumulating
* all new tasks into local queue first and pushing it to scheduler
* from within a single mutex lock.
diff --git a/source/blender/blenlib/CMakeLists.txt b/source/blender/blenlib/CMakeLists.txt
index f1f5089c2d6..c0a8a80ca56 100644
--- a/source/blender/blenlib/CMakeLists.txt
+++ b/source/blender/blenlib/CMakeLists.txt
@@ -118,7 +118,8 @@ set(SRC
intern/string_utf8.c
intern/string_utils.c
intern/system.c
- intern/task.c
+ intern/task_pool.cc
+ intern/task_iterator.c
intern/threads.c
intern/time.c
intern/timecode.c
diff --git a/source/blender/blenlib/intern/math_base_inline.c b/source/blender/blenlib/intern/math_base_inline.c
index c4b68e9164c..e0cac508d28 100644
--- a/source/blender/blenlib/intern/math_base_inline.c
+++ b/source/blender/blenlib/intern/math_base_inline.c
@@ -524,6 +524,15 @@ MINLINE size_t max_zz(size_t a, size_t b)
return (b < a) ? a : b;
}
+MINLINE char min_cc(char a, char b)
+{
+ return (a < b) ? a : b;
+}
+MINLINE char max_cc(char a, char b)
+{
+ return (b < a) ? a : b;
+}
+
MINLINE int clamp_i(int value, int min, int max)
{
return min_ii(max_ii(value, min), max);
diff --git a/source/blender/blenlib/intern/storage.c b/source/blender/blenlib/intern/storage.c
index 9b437b02c0a..154fc966ca2 100644
--- a/source/blender/blenlib/intern/storage.c
+++ b/source/blender/blenlib/intern/storage.c
@@ -232,8 +232,9 @@ eFileAttributes BLI_file_attributes(const char *path)
int ret = 0;
# ifdef WIN32
- wchar_t wline[FILE_MAXDIR];
- BLI_strncpy_wchar_from_utf8(wline, path, ARRAY_SIZE(wline));
+ WCHAR wline[FILE_MAXDIR];
+ size_t bsize = count_utf_16_from_8(path);
+ conv_utf_8_to_16(path, wline, bsize);
DWORD attr = GetFileAttributesW(wline);
if (attr & FILE_ATTRIBUTE_READONLY) {
ret |= FILE_ATTR_READONLY;
diff --git a/source/blender/blenlib/intern/task.c b/source/blender/blenlib/intern/task_iterator.c
index 293275c402d..4ac012fa578 100644
--- a/source/blender/blenlib/intern/task.c
+++ b/source/blender/blenlib/intern/task_iterator.c
@@ -34,994 +34,6 @@
#include "atomic_ops.h"
-/* Define this to enable some detailed statistic print. */
-#undef DEBUG_STATS
-
-/* Types */
-
-/* Number of per-thread pre-allocated tasks.
- *
- * For more details see description of TaskMemPool.
- */
-#define MEMPOOL_SIZE 256
-
-/* Number of tasks which are pushed directly to local thread queue.
- *
- * This allows thread to fetch next task without locking the whole queue.
- */
-#define LOCAL_QUEUE_SIZE 1
-
-/* Number of tasks which are allowed to be scheduled in a delayed manner.
- *
- * This allows to use less locks per graph node children schedule. More details
- * could be found at TaskThreadLocalStorage::do_delayed_push.
- */
-#define DELAYED_QUEUE_SIZE 4096
-
-#ifndef NDEBUG
-# define ASSERT_THREAD_ID(scheduler, thread_id) \
- do { \
- if (!BLI_thread_is_main()) { \
- TaskThread *thread = pthread_getspecific(scheduler->tls_id_key); \
- if (thread == NULL) { \
- BLI_assert(thread_id == 0); \
- } \
- else { \
- BLI_assert(thread_id == thread->id); \
- } \
- } \
- else { \
- BLI_assert(thread_id == 0); \
- } \
- } while (false)
-#else
-# define ASSERT_THREAD_ID(scheduler, thread_id)
-#endif
-
-typedef struct Task {
- struct Task *next, *prev;
-
- TaskRunFunction run;
- void *taskdata;
- bool free_taskdata;
- TaskFreeFunction freedata;
- TaskPool *pool;
-} Task;
-
-/* This is a per-thread storage of pre-allocated tasks.
- *
- * The idea behind this is simple: reduce amount of malloc() calls when pushing
- * new task to the pool. This is done by keeping memory from the tasks which
- * were finished already, so instead of freeing that memory we put it to the
- * pool for the later re-use.
- *
- * The tricky part here is to avoid any inter-thread synchronization, hence no
- * lock must exist around this pool. The pool will become an owner of the pointer
- * from freed task, and only corresponding thread will be able to use this pool
- * (no memory stealing and such).
- *
- * This leads to the following use of the pool:
- *
- * - task_push() should provide proper thread ID from which the task is being
- * pushed from.
- *
- * - Task allocation function which check corresponding memory pool and if there
- * is any memory in there it'll mark memory as re-used, remove it from the pool
- * and use that memory for the new task.
- *
- * At this moment task queue owns the memory.
- *
- * - When task is done and task_free() is called the memory will be put to the
- * pool which corresponds to a thread which handled the task.
- */
-typedef struct TaskMemPool {
- /* Number of pre-allocated tasks in the pool. */
- int num_tasks;
- /* Pre-allocated task memory pointers. */
- Task *tasks[MEMPOOL_SIZE];
-} TaskMemPool;
-
-#ifdef DEBUG_STATS
-typedef struct TaskMemPoolStats {
- /* Number of allocations. */
- int num_alloc;
- /* Number of avoided allocations (pointer was re-used from the pool). */
- int num_reuse;
- /* Number of discarded memory due to pool saturation, */
- int num_discard;
-} TaskMemPoolStats;
-#endif
-
-typedef struct TaskThreadLocalStorage {
- /* Memory pool for faster task allocation.
- * The idea is to re-use memory of finished/discarded tasks by this thread.
- */
- TaskMemPool task_mempool;
-
- /* Local queue keeps thread alive by keeping small amount of tasks ready
- * to be picked up without causing global thread locks for synchronization.
- */
- int num_local_queue;
- Task *local_queue[LOCAL_QUEUE_SIZE];
-
- /* Thread can be marked for delayed tasks push. This is helpful when it's
- * know that lots of subsequent task pushed will happen from the same thread
- * without "interrupting" for task execution.
- *
- * We try to accumulate as much tasks as possible in a local queue without
- * any locks first, and then we push all of them into a scheduler's queue
- * from within a single mutex lock.
- */
- bool do_delayed_push;
- int num_delayed_queue;
- Task *delayed_queue[DELAYED_QUEUE_SIZE];
-} TaskThreadLocalStorage;
-
-struct TaskPool {
- TaskScheduler *scheduler;
-
- volatile size_t num;
- ThreadMutex num_mutex;
- ThreadCondition num_cond;
-
- void *userdata;
- ThreadMutex user_mutex;
-
- volatile bool do_cancel;
- volatile bool do_work;
-
- volatile bool is_suspended;
- bool start_suspended;
- ListBase suspended_queue;
- size_t num_suspended;
-
- /* If set, this pool may never be work_and_wait'ed, which means TaskScheduler
- * has to use its special background fallback thread in case we are in
- * single-threaded situation.
- */
- bool run_in_background;
-
- /* This is a task scheduler's ID of a thread at which pool was constructed.
- * It will be used to access task TLS.
- */
- int thread_id;
-
- /* For the pools which are created from non-main thread which is not a
- * scheduler worker thread we can't re-use any of scheduler's threads TLS
- * and have to use our own one.
- */
- bool use_local_tls;
- TaskThreadLocalStorage local_tls;
-#ifndef NDEBUG
- pthread_t creator_thread_id;
-#endif
-
-#ifdef DEBUG_STATS
- TaskMemPoolStats *mempool_stats;
-#endif
-};
-
-struct TaskScheduler {
- pthread_t *threads;
- struct TaskThread *task_threads;
- int num_threads;
- bool background_thread_only;
-
- ListBase queue;
- ThreadMutex queue_mutex;
- ThreadCondition queue_cond;
-
- ThreadMutex startup_mutex;
- ThreadCondition startup_cond;
- volatile int num_thread_started;
-
- volatile bool do_exit;
-
- /* NOTE: In pthread's TLS we store the whole TaskThread structure. */
- pthread_key_t tls_id_key;
-};
-
-typedef struct TaskThread {
- TaskScheduler *scheduler;
- int id;
- TaskThreadLocalStorage tls;
-} TaskThread;
-
-/* Helper */
-BLI_INLINE void task_data_free(Task *task, const int thread_id)
-{
- if (task->free_taskdata) {
- if (task->freedata) {
- task->freedata(task->pool, task->taskdata, thread_id);
- }
- else {
- MEM_freeN(task->taskdata);
- }
- }
-}
-
-BLI_INLINE void initialize_task_tls(TaskThreadLocalStorage *tls)
-{
- memset(tls, 0, sizeof(TaskThreadLocalStorage));
-}
-
-BLI_INLINE TaskThreadLocalStorage *get_task_tls(TaskPool *pool, const int thread_id)
-{
- TaskScheduler *scheduler = pool->scheduler;
- BLI_assert(thread_id >= 0);
- BLI_assert(thread_id <= scheduler->num_threads);
- if (pool->use_local_tls && thread_id == 0) {
- BLI_assert(pool->thread_id == 0);
- BLI_assert(!BLI_thread_is_main());
- BLI_assert(pthread_equal(pthread_self(), pool->creator_thread_id));
- return &pool->local_tls;
- }
- if (thread_id == 0) {
- BLI_assert(BLI_thread_is_main());
- return &scheduler->task_threads[pool->thread_id].tls;
- }
- return &scheduler->task_threads[thread_id].tls;
-}
-
-BLI_INLINE void free_task_tls(TaskThreadLocalStorage *tls)
-{
- TaskMemPool *task_mempool = &tls->task_mempool;
- for (int i = 0; i < task_mempool->num_tasks; i++) {
- MEM_freeN(task_mempool->tasks[i]);
- }
-}
-
-static Task *task_alloc(TaskPool *pool, const int thread_id)
-{
- BLI_assert(thread_id <= pool->scheduler->num_threads);
- if (thread_id != -1) {
- BLI_assert(thread_id >= 0);
- BLI_assert(thread_id <= pool->scheduler->num_threads);
- TaskThreadLocalStorage *tls = get_task_tls(pool, thread_id);
- TaskMemPool *task_mempool = &tls->task_mempool;
- /* Try to re-use task memory from a thread local storage. */
- if (task_mempool->num_tasks > 0) {
- --task_mempool->num_tasks;
- /* Success! We've just avoided task allocation. */
-#ifdef DEBUG_STATS
- pool->mempool_stats[thread_id].num_reuse++;
-#endif
- return task_mempool->tasks[task_mempool->num_tasks];
- }
- /* We are doomed to allocate new task data. */
-#ifdef DEBUG_STATS
- pool->mempool_stats[thread_id].num_alloc++;
-#endif
- }
- return MEM_mallocN(sizeof(Task), "New task");
-}
-
-static void task_free(TaskPool *pool, Task *task, const int thread_id)
-{
- task_data_free(task, thread_id);
- BLI_assert(thread_id >= 0);
- BLI_assert(thread_id <= pool->scheduler->num_threads);
- if (thread_id == 0) {
- BLI_assert(pool->use_local_tls || BLI_thread_is_main());
- }
- TaskThreadLocalStorage *tls = get_task_tls(pool, thread_id);
- TaskMemPool *task_mempool = &tls->task_mempool;
- if (task_mempool->num_tasks < MEMPOOL_SIZE - 1) {
- /* Successfully allowed the task to be re-used later. */
- task_mempool->tasks[task_mempool->num_tasks] = task;
- ++task_mempool->num_tasks;
- }
- else {
- /* Local storage saturated, no other way than just discard
- * the memory.
- *
- * TODO(sergey): We can perhaps store such pointer in a global
- * scheduler pool, maybe it'll be faster than discarding and
- * allocating again.
- */
- MEM_freeN(task);
-#ifdef DEBUG_STATS
- pool->mempool_stats[thread_id].num_discard++;
-#endif
- }
-}
-
-/* Task Scheduler */
-
-static void task_pool_num_decrease(TaskPool *pool, size_t done)
-{
- BLI_mutex_lock(&pool->num_mutex);
-
- BLI_assert(pool->num >= done);
-
- pool->num -= done;
-
- if (pool->num == 0) {
- BLI_condition_notify_all(&pool->num_cond);
- }
-
- BLI_mutex_unlock(&pool->num_mutex);
-}
-
-static void task_pool_num_increase(TaskPool *pool, size_t new)
-{
- BLI_mutex_lock(&pool->num_mutex);
-
- pool->num += new;
- BLI_condition_notify_all(&pool->num_cond);
-
- BLI_mutex_unlock(&pool->num_mutex);
-}
-
-static bool task_scheduler_thread_wait_pop(TaskScheduler *scheduler, Task **task)
-{
- bool found_task = false;
- BLI_mutex_lock(&scheduler->queue_mutex);
-
- while (!scheduler->queue.first && !scheduler->do_exit) {
- BLI_condition_wait(&scheduler->queue_cond, &scheduler->queue_mutex);
- }
-
- do {
- Task *current_task;
-
- /* Assuming we can only have a void queue in 'exit' case here seems logical
- * (we should only be here after our worker thread has been woken up from a
- * condition_wait(), which only happens after a new task was added to the queue),
- * but it is wrong.
- * Waiting on condition may wake up the thread even if condition is not signaled
- * (spurious wake-ups), and some race condition may also empty the queue **after**
- * condition has been signaled, but **before** awoken thread reaches this point...
- * See http://stackoverflow.com/questions/8594591
- *
- * So we only abort here if do_exit is set.
- */
- if (scheduler->do_exit) {
- BLI_mutex_unlock(&scheduler->queue_mutex);
- return false;
- }
-
- for (current_task = scheduler->queue.first; current_task != NULL;
- current_task = current_task->next) {
- TaskPool *pool = current_task->pool;
-
- if (scheduler->background_thread_only && !pool->run_in_background) {
- continue;
- }
-
- *task = current_task;
- found_task = true;
- BLI_remlink(&scheduler->queue, *task);
- break;
- }
- if (!found_task) {
- BLI_condition_wait(&scheduler->queue_cond, &scheduler->queue_mutex);
- }
- } while (!found_task);
-
- BLI_mutex_unlock(&scheduler->queue_mutex);
-
- return true;
-}
-
-BLI_INLINE void handle_local_queue(TaskThreadLocalStorage *tls, const int thread_id)
-{
- BLI_assert(!tls->do_delayed_push);
- while (tls->num_local_queue > 0) {
- /* We pop task from queue before handling it so handler of the task can
- * push next job to the local queue.
- */
- tls->num_local_queue--;
- Task *local_task = tls->local_queue[tls->num_local_queue];
- /* TODO(sergey): Double-check work_and_wait() doesn't handle other's
- * pool tasks.
- */
- TaskPool *local_pool = local_task->pool;
- local_task->run(local_pool, local_task->taskdata, thread_id);
- task_free(local_pool, local_task, thread_id);
- }
- BLI_assert(!tls->do_delayed_push);
-}
-
-static void *task_scheduler_thread_run(void *thread_p)
-{
- TaskThread *thread = (TaskThread *)thread_p;
- TaskThreadLocalStorage *tls = &thread->tls;
- TaskScheduler *scheduler = thread->scheduler;
- int thread_id = thread->id;
- Task *task;
-
- pthread_setspecific(scheduler->tls_id_key, thread);
-
- /* signal the main thread when all threads have started */
- BLI_mutex_lock(&scheduler->startup_mutex);
- scheduler->num_thread_started++;
- if (scheduler->num_thread_started == scheduler->num_threads) {
- BLI_condition_notify_one(&scheduler->startup_cond);
- }
- BLI_mutex_unlock(&scheduler->startup_mutex);
-
- /* keep popping off tasks */
- while (task_scheduler_thread_wait_pop(scheduler, &task)) {
- TaskPool *pool = task->pool;
-
- /* run task */
- BLI_assert(!tls->do_delayed_push);
- task->run(pool, task->taskdata, thread_id);
- BLI_assert(!tls->do_delayed_push);
-
- /* delete task */
- task_free(pool, task, thread_id);
-
- /* Handle all tasks from local queue. */
- handle_local_queue(tls, thread_id);
-
- /* notify pool task was done */
- task_pool_num_decrease(pool, 1);
- }
-
- return NULL;
-}
-
-TaskScheduler *BLI_task_scheduler_create(int num_threads)
-{
- TaskScheduler *scheduler = MEM_callocN(sizeof(TaskScheduler), "TaskScheduler");
-
- /* multiple places can use this task scheduler, sharing the same
- * threads, so we keep track of the number of users. */
- scheduler->do_exit = false;
-
- BLI_listbase_clear(&scheduler->queue);
- BLI_mutex_init(&scheduler->queue_mutex);
- BLI_condition_init(&scheduler->queue_cond);
-
- BLI_mutex_init(&scheduler->startup_mutex);
- BLI_condition_init(&scheduler->startup_cond);
- scheduler->num_thread_started = 0;
-
- if (num_threads == 0) {
- /* automatic number of threads will be main thread + num cores */
- num_threads = BLI_system_thread_count();
- }
-
- /* main thread will also work, so we count it too */
- num_threads -= 1;
-
- /* Add background-only thread if needed. */
- if (num_threads == 0) {
- scheduler->background_thread_only = true;
- num_threads = 1;
- }
-
- scheduler->task_threads = MEM_mallocN(sizeof(TaskThread) * (num_threads + 1),
- "TaskScheduler task threads");
-
- /* Initialize TLS for main thread. */
- initialize_task_tls(&scheduler->task_threads[0].tls);
-
- pthread_key_create(&scheduler->tls_id_key, NULL);
-
- /* launch threads that will be waiting for work */
- if (num_threads > 0) {
- int i;
-
- scheduler->num_threads = num_threads;
- scheduler->threads = MEM_callocN(sizeof(pthread_t) * num_threads, "TaskScheduler threads");
-
- for (i = 0; i < num_threads; i++) {
- TaskThread *thread = &scheduler->task_threads[i + 1];
- thread->scheduler = scheduler;
- thread->id = i + 1;
- initialize_task_tls(&thread->tls);
-
- if (pthread_create(&scheduler->threads[i], NULL, task_scheduler_thread_run, thread) != 0) {
- fprintf(stderr, "TaskScheduler failed to launch thread %d/%d\n", i, num_threads);
- }
- }
- }
-
- /* Wait for all worker threads to start before returning to caller to prevent the case where
- * threads are still starting and pthread_join is called, which causes a deadlock on pthreads4w.
- */
- BLI_mutex_lock(&scheduler->startup_mutex);
- /* NOTE: Use loop here to avoid false-positive everything-is-ready caused by spontaneous thread
- * wake up. */
- while (scheduler->num_thread_started != num_threads) {
- BLI_condition_wait(&scheduler->startup_cond, &scheduler->startup_mutex);
- }
- BLI_mutex_unlock(&scheduler->startup_mutex);
-
- return scheduler;
-}
-
-void BLI_task_scheduler_free(TaskScheduler *scheduler)
-{
- Task *task;
-
- /* stop all waiting threads */
- BLI_mutex_lock(&scheduler->queue_mutex);
- scheduler->do_exit = true;
- BLI_condition_notify_all(&scheduler->queue_cond);
- BLI_mutex_unlock(&scheduler->queue_mutex);
-
- pthread_key_delete(scheduler->tls_id_key);
-
- /* delete threads */
- if (scheduler->threads) {
- int i;
-
- for (i = 0; i < scheduler->num_threads; i++) {
- if (pthread_join(scheduler->threads[i], NULL) != 0) {
- fprintf(stderr, "TaskScheduler failed to join thread %d/%d\n", i, scheduler->num_threads);
- }
- }
-
- MEM_freeN(scheduler->threads);
- }
-
- /* Delete task thread data */
- if (scheduler->task_threads) {
- for (int i = 0; i < scheduler->num_threads + 1; i++) {
- TaskThreadLocalStorage *tls = &scheduler->task_threads[i].tls;
- free_task_tls(tls);
- }
-
- MEM_freeN(scheduler->task_threads);
- }
-
- /* delete leftover tasks */
- for (task = scheduler->queue.first; task; task = task->next) {
- task_data_free(task, 0);
- }
- BLI_freelistN(&scheduler->queue);
-
- /* delete mutex/condition */
- BLI_mutex_end(&scheduler->queue_mutex);
- BLI_condition_end(&scheduler->queue_cond);
- BLI_mutex_end(&scheduler->startup_mutex);
- BLI_condition_end(&scheduler->startup_cond);
-
- MEM_freeN(scheduler);
-}
-
-int BLI_task_scheduler_num_threads(TaskScheduler *scheduler)
-{
- return scheduler->num_threads + 1;
-}
-
-static void task_scheduler_push(TaskScheduler *scheduler, Task *task, TaskPriority priority)
-{
- task_pool_num_increase(task->pool, 1);
-
- /* add task to queue */
- BLI_mutex_lock(&scheduler->queue_mutex);
-
- if (priority == TASK_PRIORITY_HIGH) {
- BLI_addhead(&scheduler->queue, task);
- }
- else {
- BLI_addtail(&scheduler->queue, task);
- }
-
- BLI_condition_notify_one(&scheduler->queue_cond);
- BLI_mutex_unlock(&scheduler->queue_mutex);
-}
-
-static void task_scheduler_push_all(TaskScheduler *scheduler,
- TaskPool *pool,
- Task **tasks,
- int num_tasks)
-{
- if (num_tasks == 0) {
- return;
- }
-
- task_pool_num_increase(pool, num_tasks);
-
- BLI_mutex_lock(&scheduler->queue_mutex);
-
- for (int i = 0; i < num_tasks; i++) {
- BLI_addhead(&scheduler->queue, tasks[i]);
- }
-
- BLI_condition_notify_all(&scheduler->queue_cond);
- BLI_mutex_unlock(&scheduler->queue_mutex);
-}
-
-static void task_scheduler_clear(TaskScheduler *scheduler, TaskPool *pool)
-{
- Task *task, *nexttask;
- size_t done = 0;
-
- BLI_mutex_lock(&scheduler->queue_mutex);
-
- /* free all tasks from this pool from the queue */
- for (task = scheduler->queue.first; task; task = nexttask) {
- nexttask = task->next;
-
- if (task->pool == pool) {
- task_data_free(task, pool->thread_id);
- BLI_freelinkN(&scheduler->queue, task);
-
- done++;
- }
- }
-
- BLI_mutex_unlock(&scheduler->queue_mutex);
-
- /* notify done */
- task_pool_num_decrease(pool, done);
-}
-
-/* Task Pool */
-
-static TaskPool *task_pool_create_ex(TaskScheduler *scheduler,
- void *userdata,
- const bool is_background,
- const bool is_suspended)
-{
- TaskPool *pool = MEM_mallocN(sizeof(TaskPool), "TaskPool");
-
-#ifndef NDEBUG
- /* Assert we do not try to create a background pool from some parent task -
- * those only work OK from main thread. */
- if (is_background) {
- const pthread_t thread_id = pthread_self();
- int i = scheduler->num_threads;
-
- while (i--) {
- BLI_assert(!pthread_equal(scheduler->threads[i], thread_id));
- }
- }
-#endif
-
- pool->scheduler = scheduler;
- pool->num = 0;
- pool->do_cancel = false;
- pool->do_work = false;
- pool->is_suspended = is_suspended;
- pool->start_suspended = is_suspended;
- pool->num_suspended = 0;
- pool->suspended_queue.first = pool->suspended_queue.last = NULL;
- pool->run_in_background = is_background;
- pool->use_local_tls = false;
-
- BLI_mutex_init(&pool->num_mutex);
- BLI_condition_init(&pool->num_cond);
-
- pool->userdata = userdata;
- BLI_mutex_init(&pool->user_mutex);
-
- if (BLI_thread_is_main()) {
- pool->thread_id = 0;
- }
- else {
- TaskThread *thread = pthread_getspecific(scheduler->tls_id_key);
- if (thread == NULL) {
- /* NOTE: Task pool is created from non-main thread which is not
- * managed by the task scheduler. We identify ourselves as thread ID
- * 0 but we do not use scheduler's TLS storage and use our own
- * instead to avoid any possible threading conflicts.
- */
- pool->thread_id = 0;
- pool->use_local_tls = true;
-#ifndef NDEBUG
- pool->creator_thread_id = pthread_self();
-#endif
- initialize_task_tls(&pool->local_tls);
- }
- else {
- pool->thread_id = thread->id;
- }
- }
-
-#ifdef DEBUG_STATS
- pool->mempool_stats = MEM_callocN(sizeof(*pool->mempool_stats) * (scheduler->num_threads + 1),
- "per-taskpool mempool stats");
-#endif
-
- /* Ensure malloc will go fine from threads,
- *
- * This is needed because we could be in main thread here
- * and malloc could be non-thread safe at this point because
- * no other jobs are running.
- */
- BLI_threaded_malloc_begin();
-
- return pool;
-}
-
-/**
- * Create a normal task pool. Tasks will be executed as soon as they are added.
- */
-TaskPool *BLI_task_pool_create(TaskScheduler *scheduler, void *userdata)
-{
- return task_pool_create_ex(scheduler, userdata, false, false);
-}
-
-/**
- * Create a background task pool.
- * In multi-threaded context, there is no differences with #BLI_task_pool_create(),
- * but in single-threaded case it is ensured to have at least one worker thread to run on
- * (i.e. you don't have to call #BLI_task_pool_work_and_wait
- * on it to be sure it will be processed).
- *
- * \note Background pools are non-recursive
- * (that is, you should not create other background pools in tasks assigned to a background pool,
- * they could end never being executed, since the 'fallback' background thread is already
- * busy with parent task in single-threaded context).
- */
-TaskPool *BLI_task_pool_create_background(TaskScheduler *scheduler, void *userdata)
-{
- return task_pool_create_ex(scheduler, userdata, true, false);
-}
-
-/**
- * Similar to BLI_task_pool_create() but does not schedule any tasks for execution
- * for until BLI_task_pool_work_and_wait() is called. This helps reducing threading
- * overhead when pushing huge amount of small initial tasks from the main thread.
- */
-TaskPool *BLI_task_pool_create_suspended(TaskScheduler *scheduler, void *userdata)
-{
- return task_pool_create_ex(scheduler, userdata, false, true);
-}
-
-void BLI_task_pool_free(TaskPool *pool)
-{
- BLI_task_pool_cancel(pool);
-
- BLI_mutex_end(&pool->num_mutex);
- BLI_condition_end(&pool->num_cond);
-
- BLI_mutex_end(&pool->user_mutex);
-
-#ifdef DEBUG_STATS
- printf("Thread ID Allocated Reused Discarded\n");
- for (int i = 0; i < pool->scheduler->num_threads + 1; i++) {
- printf("%02d %05d %05d %05d\n",
- i,
- pool->mempool_stats[i].num_alloc,
- pool->mempool_stats[i].num_reuse,
- pool->mempool_stats[i].num_discard);
- }
- MEM_freeN(pool->mempool_stats);
-#endif
-
- if (pool->use_local_tls) {
- free_task_tls(&pool->local_tls);
- }
-
- MEM_freeN(pool);
-
- BLI_threaded_malloc_end();
-}
-
-BLI_INLINE bool task_can_use_local_queues(TaskPool *pool, int thread_id)
-{
- return (thread_id != -1 && (thread_id != pool->thread_id || pool->do_work));
-}
-
-static void task_pool_push(TaskPool *pool,
- TaskRunFunction run,
- void *taskdata,
- bool free_taskdata,
- TaskFreeFunction freedata,
- TaskPriority priority,
- int thread_id)
-{
- /* Allocate task and fill it's properties. */
- Task *task = task_alloc(pool, thread_id);
- task->run = run;
- task->taskdata = taskdata;
- task->free_taskdata = free_taskdata;
- task->freedata = freedata;
- task->pool = pool;
- /* For suspended pools we put everything yo a global queue first
- * and exit as soon as possible.
- *
- * This tasks will be moved to actual execution when pool is
- * activated by work_and_wait().
- */
- if (pool->is_suspended) {
- BLI_addhead(&pool->suspended_queue, task);
- atomic_fetch_and_add_z(&pool->num_suspended, 1);
- return;
- }
- /* Populate to any local queue first, this is cheapest push ever. */
- if (task_can_use_local_queues(pool, thread_id)) {
- ASSERT_THREAD_ID(pool->scheduler, thread_id);
- TaskThreadLocalStorage *tls = get_task_tls(pool, thread_id);
- /* Try to push to a local execution queue.
- * These tasks will be picked up next.
- */
- if (tls->num_local_queue < LOCAL_QUEUE_SIZE) {
- tls->local_queue[tls->num_local_queue] = task;
- tls->num_local_queue++;
- return;
- }
- /* If we are in the delayed tasks push mode, we push tasks to a
- * temporary local queue first without any locks, and then move them
- * to global execution queue with a single lock.
- */
- if (tls->do_delayed_push && tls->num_delayed_queue < DELAYED_QUEUE_SIZE) {
- tls->delayed_queue[tls->num_delayed_queue] = task;
- tls->num_delayed_queue++;
- return;
- }
- }
- /* Do push to a global execution pool, slowest possible method,
- * causes quite reasonable amount of threading overhead.
- */
- task_scheduler_push(pool->scheduler, task, priority);
-}
-
-void BLI_task_pool_push_ex(TaskPool *pool,
- TaskRunFunction run,
- void *taskdata,
- bool free_taskdata,
- TaskFreeFunction freedata,
- TaskPriority priority)
-{
- task_pool_push(pool, run, taskdata, free_taskdata, freedata, priority, -1);
-}
-
-void BLI_task_pool_push(
- TaskPool *pool, TaskRunFunction run, void *taskdata, bool free_taskdata, TaskPriority priority)
-{
- BLI_task_pool_push_ex(pool, run, taskdata, free_taskdata, NULL, priority);
-}
-
-void BLI_task_pool_push_from_thread(TaskPool *pool,
- TaskRunFunction run,
- void *taskdata,
- bool free_taskdata,
- TaskPriority priority,
- int thread_id)
-{
- task_pool_push(pool, run, taskdata, free_taskdata, NULL, priority, thread_id);
-}
-
-void BLI_task_pool_work_and_wait(TaskPool *pool)
-{
- TaskThreadLocalStorage *tls = get_task_tls(pool, pool->thread_id);
- TaskScheduler *scheduler = pool->scheduler;
-
- if (atomic_fetch_and_and_uint8((uint8_t *)&pool->is_suspended, 0)) {
- if (pool->num_suspended) {
- task_pool_num_increase(pool, pool->num_suspended);
- BLI_mutex_lock(&scheduler->queue_mutex);
-
- BLI_movelisttolist(&scheduler->queue, &pool->suspended_queue);
-
- BLI_condition_notify_all(&scheduler->queue_cond);
- BLI_mutex_unlock(&scheduler->queue_mutex);
-
- pool->num_suspended = 0;
- }
- }
-
- pool->do_work = true;
-
- ASSERT_THREAD_ID(pool->scheduler, pool->thread_id);
-
- handle_local_queue(tls, pool->thread_id);
-
- BLI_mutex_lock(&pool->num_mutex);
-
- while (pool->num != 0) {
- Task *task, *work_task = NULL;
- bool found_task = false;
-
- BLI_mutex_unlock(&pool->num_mutex);
-
- BLI_mutex_lock(&scheduler->queue_mutex);
-
- /* find task from this pool. if we get a task from another pool,
- * we can get into deadlock */
-
- for (task = scheduler->queue.first; task; task = task->next) {
- if (task->pool == pool) {
- work_task = task;
- found_task = true;
- BLI_remlink(&scheduler->queue, task);
- break;
- }
- }
-
- BLI_mutex_unlock(&scheduler->queue_mutex);
-
- /* if found task, do it, otherwise wait until other tasks are done */
- if (found_task) {
- /* run task */
- BLI_assert(!tls->do_delayed_push);
- work_task->run(pool, work_task->taskdata, pool->thread_id);
- BLI_assert(!tls->do_delayed_push);
-
- /* delete task */
- task_free(pool, task, pool->thread_id);
-
- /* Handle all tasks from local queue. */
- handle_local_queue(tls, pool->thread_id);
-
- /* notify pool task was done */
- task_pool_num_decrease(pool, 1);
- }
-
- BLI_mutex_lock(&pool->num_mutex);
- if (pool->num == 0) {
- break;
- }
-
- if (!found_task) {
- BLI_condition_wait(&pool->num_cond, &pool->num_mutex);
- }
- }
-
- BLI_mutex_unlock(&pool->num_mutex);
-
- BLI_assert(tls->num_local_queue == 0);
-}
-
-void BLI_task_pool_work_wait_and_reset(TaskPool *pool)
-{
- BLI_task_pool_work_and_wait(pool);
-
- pool->do_work = false;
- pool->is_suspended = pool->start_suspended;
-}
-
-void BLI_task_pool_cancel(TaskPool *pool)
-{
- pool->do_cancel = true;
-
- task_scheduler_clear(pool->scheduler, pool);
-
- /* wait until all entries are cleared */
- BLI_mutex_lock(&pool->num_mutex);
- while (pool->num) {
- BLI_condition_wait(&pool->num_cond, &pool->num_mutex);
- }
- BLI_mutex_unlock(&pool->num_mutex);
-
- pool->do_cancel = false;
-}
-
-bool BLI_task_pool_canceled(TaskPool *pool)
-{
- return pool->do_cancel;
-}
-
-void *BLI_task_pool_userdata(TaskPool *pool)
-{
- return pool->userdata;
-}
-
-ThreadMutex *BLI_task_pool_user_mutex(TaskPool *pool)
-{
- return &pool->user_mutex;
-}
-
-void BLI_task_pool_delayed_push_begin(TaskPool *pool, int thread_id)
-{
- if (task_can_use_local_queues(pool, thread_id)) {
- ASSERT_THREAD_ID(pool->scheduler, thread_id);
- TaskThreadLocalStorage *tls = get_task_tls(pool, thread_id);
- tls->do_delayed_push = true;
- }
-}
-
-void BLI_task_pool_delayed_push_end(TaskPool *pool, int thread_id)
-{
- if (task_can_use_local_queues(pool, thread_id)) {
- ASSERT_THREAD_ID(pool->scheduler, thread_id);
- TaskThreadLocalStorage *tls = get_task_tls(pool, thread_id);
- BLI_assert(tls->do_delayed_push);
- task_scheduler_push_all(pool->scheduler, pool, tls->delayed_queue, tls->num_delayed_queue);
- tls->do_delayed_push = false;
- tls->num_delayed_queue = 0;
- }
-}
-
/* Parallel range routines */
/**
@@ -1331,8 +343,8 @@ void BLI_task_parallel_range(const int start,
return;
}
- TaskPool *task_pool = range_pool.pool = BLI_task_pool_create_suspended(task_scheduler,
- &range_pool);
+ TaskPool *task_pool = range_pool.pool = BLI_task_pool_create_suspended(
+ task_scheduler, &range_pool, TASK_PRIORITY_HIGH);
range_pool.current_state = &state;
@@ -1341,18 +353,15 @@ void BLI_task_parallel_range(const int start,
state.tls_data_size = tls_data_size;
}
+ const int thread_id = BLI_task_pool_creator_thread_id(task_pool);
for (i = 0; i < num_tasks; i++) {
if (use_tls_data) {
void *userdata_chunk_local = (char *)flatten_tls_storage + (tls_data_size * (size_t)i);
memcpy(userdata_chunk_local, tls_data, tls_data_size);
}
/* Use this pool's pre-allocated tasks. */
- BLI_task_pool_push_from_thread(task_pool,
- parallel_range_func,
- POINTER_FROM_INT(i),
- false,
- TASK_PRIORITY_HIGH,
- task_pool->thread_id);
+ BLI_task_pool_push_from_thread(
+ task_pool, parallel_range_func, POINTER_FROM_INT(i), false, NULL, thread_id);
}
BLI_task_pool_work_and_wait(task_pool);
@@ -1497,18 +506,14 @@ void BLI_task_parallel_range_pool_work_and_wait(TaskParallelRangePool *range_poo
}
}
- TaskPool *task_pool = range_pool->pool = BLI_task_pool_create_suspended(task_scheduler,
- range_pool);
+ TaskPool *task_pool = range_pool->pool = BLI_task_pool_create_suspended(
+ task_scheduler, range_pool, TASK_PRIORITY_HIGH);
range_pool->current_state = range_pool->parallel_range_states;
-
+ const int thread_id = BLI_task_pool_creator_thread_id(task_pool);
for (int i = 0; i < num_tasks; i++) {
- BLI_task_pool_push_from_thread(task_pool,
- parallel_range_func,
- POINTER_FROM_INT(i),
- false,
- TASK_PRIORITY_HIGH,
- task_pool->thread_id);
+ BLI_task_pool_push_from_thread(
+ task_pool, parallel_range_func, POINTER_FROM_INT(i), false, NULL, thread_id);
}
BLI_task_pool_work_and_wait(task_pool);
@@ -1527,12 +532,8 @@ void BLI_task_parallel_range_pool_work_and_wait(TaskParallelRangePool *range_poo
}
if (state->func_finalize != NULL) {
- BLI_task_pool_push_from_thread(task_pool,
- parallel_range_func_finalize,
- state,
- false,
- TASK_PRIORITY_HIGH,
- task_pool->thread_id);
+ BLI_task_pool_push_from_thread(
+ task_pool, parallel_range_func_finalize, state, false, NULL, thread_id);
}
}
@@ -1719,24 +720,21 @@ static void task_parallel_iterator_do(const TaskParallelSettings *settings,
void *userdata_chunk_array = NULL;
const bool use_userdata_chunk = (userdata_chunk_size != 0) && (userdata_chunk != NULL);
- TaskPool *task_pool = BLI_task_pool_create_suspended(task_scheduler, state);
+ TaskPool *task_pool = BLI_task_pool_create_suspended(task_scheduler, state, TASK_PRIORITY_HIGH);
if (use_userdata_chunk) {
userdata_chunk_array = MALLOCA(userdata_chunk_size * num_tasks);
}
+ const int thread_id = BLI_task_pool_creator_thread_id(task_pool);
for (size_t i = 0; i < num_tasks; i++) {
if (use_userdata_chunk) {
userdata_chunk_local = (char *)userdata_chunk_array + (userdata_chunk_size * i);
memcpy(userdata_chunk_local, userdata_chunk, userdata_chunk_size);
}
/* Use this pool's pre-allocated tasks. */
- BLI_task_pool_push_from_thread(task_pool,
- parallel_iterator_func,
- userdata_chunk_local,
- false,
- TASK_PRIORITY_HIGH,
- task_pool->thread_id);
+ BLI_task_pool_push_from_thread(
+ task_pool, parallel_iterator_func, userdata_chunk_local, false, NULL, thread_id);
}
BLI_task_pool_work_and_wait(task_pool);
@@ -1898,7 +896,7 @@ void BLI_task_parallel_mempool(BLI_mempool *mempool,
}
task_scheduler = BLI_task_scheduler_get();
- task_pool = BLI_task_pool_create_suspended(task_scheduler, &state);
+ task_pool = BLI_task_pool_create_suspended(task_scheduler, &state, TASK_PRIORITY_HIGH);
num_threads = BLI_task_scheduler_num_threads(task_scheduler);
/* The idea here is to prevent creating task for each of the loop iterations
@@ -1913,14 +911,11 @@ void BLI_task_parallel_mempool(BLI_mempool *mempool,
BLI_mempool_iter *mempool_iterators = BLI_mempool_iter_threadsafe_create(mempool,
(size_t)num_tasks);
+ const int thread_id = BLI_task_pool_creator_thread_id(task_pool);
for (i = 0; i < num_tasks; i++) {
/* Use this pool's pre-allocated tasks. */
- BLI_task_pool_push_from_thread(task_pool,
- parallel_mempool_func,
- &mempool_iterators[i],
- false,
- TASK_PRIORITY_HIGH,
- task_pool->thread_id);
+ BLI_task_pool_push_from_thread(
+ task_pool, parallel_mempool_func, &mempool_iterators[i], false, NULL, thread_id);
}
BLI_task_pool_work_and_wait(task_pool);
diff --git a/source/blender/blenlib/intern/task_pool.cc b/source/blender/blenlib/intern/task_pool.cc
new file mode 100644
index 00000000000..8085d495248
--- /dev/null
+++ b/source/blender/blenlib/intern/task_pool.cc
@@ -0,0 +1,1029 @@
+/*
+ * 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.
+ */
+
+/** \file
+ * \ingroup bli
+ *
+ * A generic task system which can be used for any task based subsystem.
+ */
+
+#include <stdlib.h>
+
+#include "MEM_guardedalloc.h"
+
+#include "DNA_listBase.h"
+
+#include "BLI_listbase.h"
+#include "BLI_math.h"
+#include "BLI_mempool.h"
+#include "BLI_task.h"
+#include "BLI_threads.h"
+
+#include "atomic_ops.h"
+
+/* Define this to enable some detailed statistic print. */
+#undef DEBUG_STATS
+
+/* Types */
+
+/* Number of per-thread pre-allocated tasks.
+ *
+ * For more details see description of TaskMemPool.
+ */
+#define MEMPOOL_SIZE 256
+
+/* Number of tasks which are pushed directly to local thread queue.
+ *
+ * This allows thread to fetch next task without locking the whole queue.
+ */
+#define LOCAL_QUEUE_SIZE 1
+
+/* Number of tasks which are allowed to be scheduled in a delayed manner.
+ *
+ * This allows to use less locks per graph node children schedule. More details
+ * could be found at TaskThreadLocalStorage::do_delayed_push.
+ */
+#define DELAYED_QUEUE_SIZE 4096
+
+#ifndef NDEBUG
+# define ASSERT_THREAD_ID(scheduler, thread_id) \
+ do { \
+ if (!BLI_thread_is_main()) { \
+ TaskThread *thread = (TaskThread *)pthread_getspecific(scheduler->tls_id_key); \
+ if (thread == NULL) { \
+ BLI_assert(thread_id == 0); \
+ } \
+ else { \
+ BLI_assert(thread_id == thread->id); \
+ } \
+ } \
+ else { \
+ BLI_assert(thread_id == 0); \
+ } \
+ } while (false)
+#else
+# define ASSERT_THREAD_ID(scheduler, thread_id)
+#endif
+
+typedef struct Task {
+ struct Task *next, *prev;
+
+ TaskRunFunction run;
+ void *taskdata;
+ bool free_taskdata;
+ TaskFreeFunction freedata;
+ TaskPool *pool;
+} Task;
+
+/* This is a per-thread storage of pre-allocated tasks.
+ *
+ * The idea behind this is simple: reduce amount of malloc() calls when pushing
+ * new task to the pool. This is done by keeping memory from the tasks which
+ * were finished already, so instead of freeing that memory we put it to the
+ * pool for the later re-use.
+ *
+ * The tricky part here is to avoid any inter-thread synchronization, hence no
+ * lock must exist around this pool. The pool will become an owner of the pointer
+ * from freed task, and only corresponding thread will be able to use this pool
+ * (no memory stealing and such).
+ *
+ * This leads to the following use of the pool:
+ *
+ * - task_push() should provide proper thread ID from which the task is being
+ * pushed from.
+ *
+ * - Task allocation function which check corresponding memory pool and if there
+ * is any memory in there it'll mark memory as re-used, remove it from the pool
+ * and use that memory for the new task.
+ *
+ * At this moment task queue owns the memory.
+ *
+ * - When task is done and task_free() is called the memory will be put to the
+ * pool which corresponds to a thread which handled the task.
+ */
+typedef struct TaskMemPool {
+ /* Number of pre-allocated tasks in the pool. */
+ int num_tasks;
+ /* Pre-allocated task memory pointers. */
+ Task *tasks[MEMPOOL_SIZE];
+} TaskMemPool;
+
+#ifdef DEBUG_STATS
+typedef struct TaskMemPoolStats {
+ /* Number of allocations. */
+ int num_alloc;
+ /* Number of avoided allocations (pointer was re-used from the pool). */
+ int num_reuse;
+ /* Number of discarded memory due to pool saturation, */
+ int num_discard;
+} TaskMemPoolStats;
+#endif
+
+typedef struct TaskThreadLocalStorage {
+ /* Memory pool for faster task allocation.
+ * The idea is to re-use memory of finished/discarded tasks by this thread.
+ */
+ TaskMemPool task_mempool;
+
+ /* Local queue keeps thread alive by keeping small amount of tasks ready
+ * to be picked up without causing global thread locks for synchronization.
+ */
+ int num_local_queue;
+ Task *local_queue[LOCAL_QUEUE_SIZE];
+
+ /* Thread can be marked for delayed tasks push. This is helpful when it's
+ * know that lots of subsequent task pushed will happen from the same thread
+ * without "interrupting" for task execution.
+ *
+ * We try to accumulate as much tasks as possible in a local queue without
+ * any locks first, and then we push all of them into a scheduler's queue
+ * from within a single mutex lock.
+ */
+ bool do_delayed_push;
+ int num_delayed_queue;
+ Task *delayed_queue[DELAYED_QUEUE_SIZE];
+} TaskThreadLocalStorage;
+
+struct TaskPool {
+ TaskScheduler *scheduler;
+
+ volatile size_t num;
+ ThreadMutex num_mutex;
+ ThreadCondition num_cond;
+
+ void *userdata;
+ ThreadMutex user_mutex;
+
+ volatile bool do_cancel;
+ volatile bool do_work;
+
+ volatile bool is_suspended;
+ bool start_suspended;
+ ListBase suspended_queue;
+ size_t num_suspended;
+
+ TaskPriority priority;
+
+ /* If set, this pool may never be work_and_wait'ed, which means TaskScheduler
+ * has to use its special background fallback thread in case we are in
+ * single-threaded situation.
+ */
+ bool run_in_background;
+
+ /* This is a task scheduler's ID of a thread at which pool was constructed.
+ * It will be used to access task TLS.
+ */
+ int thread_id;
+
+ /* For the pools which are created from non-main thread which is not a
+ * scheduler worker thread we can't re-use any of scheduler's threads TLS
+ * and have to use our own one.
+ */
+ bool use_local_tls;
+ TaskThreadLocalStorage local_tls;
+#ifndef NDEBUG
+ pthread_t creator_thread_id;
+#endif
+
+#ifdef DEBUG_STATS
+ TaskMemPoolStats *mempool_stats;
+#endif
+};
+
+struct TaskScheduler {
+ pthread_t *threads;
+ struct TaskThread *task_threads;
+ int num_threads;
+ bool background_thread_only;
+
+ ListBase queue;
+ ThreadMutex queue_mutex;
+ ThreadCondition queue_cond;
+
+ ThreadMutex startup_mutex;
+ ThreadCondition startup_cond;
+ volatile int num_thread_started;
+
+ volatile bool do_exit;
+
+ /* NOTE: In pthread's TLS we store the whole TaskThread structure. */
+ pthread_key_t tls_id_key;
+};
+
+typedef struct TaskThread {
+ TaskScheduler *scheduler;
+ int id;
+ TaskThreadLocalStorage tls;
+} TaskThread;
+
+/* Helper */
+BLI_INLINE void task_data_free(Task *task, const int thread_id)
+{
+ if (task->free_taskdata) {
+ if (task->freedata) {
+ task->freedata(task->pool, task->taskdata, thread_id);
+ }
+ else {
+ MEM_freeN(task->taskdata);
+ }
+ }
+}
+
+BLI_INLINE void initialize_task_tls(TaskThreadLocalStorage *tls)
+{
+ memset(tls, 0, sizeof(TaskThreadLocalStorage));
+}
+
+BLI_INLINE TaskThreadLocalStorage *get_task_tls(TaskPool *pool, const int thread_id)
+{
+ TaskScheduler *scheduler = pool->scheduler;
+ BLI_assert(thread_id >= 0);
+ BLI_assert(thread_id <= scheduler->num_threads);
+ if (pool->use_local_tls && thread_id == 0) {
+ BLI_assert(pool->thread_id == 0);
+ BLI_assert(!BLI_thread_is_main());
+ BLI_assert(pthread_equal(pthread_self(), pool->creator_thread_id));
+ return &pool->local_tls;
+ }
+ if (thread_id == 0) {
+ BLI_assert(BLI_thread_is_main());
+ return &scheduler->task_threads[pool->thread_id].tls;
+ }
+ return &scheduler->task_threads[thread_id].tls;
+}
+
+BLI_INLINE void free_task_tls(TaskThreadLocalStorage *tls)
+{
+ TaskMemPool *task_mempool = &tls->task_mempool;
+ for (int i = 0; i < task_mempool->num_tasks; i++) {
+ MEM_freeN(task_mempool->tasks[i]);
+ }
+}
+
+static Task *task_alloc(TaskPool *pool, const int thread_id)
+{
+ BLI_assert(thread_id <= pool->scheduler->num_threads);
+ if (thread_id != -1) {
+ BLI_assert(thread_id >= 0);
+ BLI_assert(thread_id <= pool->scheduler->num_threads);
+ TaskThreadLocalStorage *tls = get_task_tls(pool, thread_id);
+ TaskMemPool *task_mempool = &tls->task_mempool;
+ /* Try to re-use task memory from a thread local storage. */
+ if (task_mempool->num_tasks > 0) {
+ --task_mempool->num_tasks;
+ /* Success! We've just avoided task allocation. */
+#ifdef DEBUG_STATS
+ pool->mempool_stats[thread_id].num_reuse++;
+#endif
+ return task_mempool->tasks[task_mempool->num_tasks];
+ }
+ /* We are doomed to allocate new task data. */
+#ifdef DEBUG_STATS
+ pool->mempool_stats[thread_id].num_alloc++;
+#endif
+ }
+ return (Task *)MEM_mallocN(sizeof(Task), "New task");
+}
+
+static void task_free(TaskPool *pool, Task *task, const int thread_id)
+{
+ task_data_free(task, thread_id);
+ BLI_assert(thread_id >= 0);
+ BLI_assert(thread_id <= pool->scheduler->num_threads);
+ if (thread_id == 0) {
+ BLI_assert(pool->use_local_tls || BLI_thread_is_main());
+ }
+ TaskThreadLocalStorage *tls = get_task_tls(pool, thread_id);
+ TaskMemPool *task_mempool = &tls->task_mempool;
+ if (task_mempool->num_tasks < MEMPOOL_SIZE - 1) {
+ /* Successfully allowed the task to be re-used later. */
+ task_mempool->tasks[task_mempool->num_tasks] = task;
+ ++task_mempool->num_tasks;
+ }
+ else {
+ /* Local storage saturated, no other way than just discard
+ * the memory.
+ *
+ * TODO(sergey): We can perhaps store such pointer in a global
+ * scheduler pool, maybe it'll be faster than discarding and
+ * allocating again.
+ */
+ MEM_freeN(task);
+#ifdef DEBUG_STATS
+ pool->mempool_stats[thread_id].num_discard++;
+#endif
+ }
+}
+
+/* Task Scheduler */
+
+static void task_pool_num_decrease(TaskPool *pool, size_t done)
+{
+ BLI_mutex_lock(&pool->num_mutex);
+
+ BLI_assert(pool->num >= done);
+
+ pool->num -= done;
+
+ if (pool->num == 0) {
+ BLI_condition_notify_all(&pool->num_cond);
+ }
+
+ BLI_mutex_unlock(&pool->num_mutex);
+}
+
+static void task_pool_num_increase(TaskPool *pool, size_t new_num)
+{
+ BLI_mutex_lock(&pool->num_mutex);
+
+ pool->num += new_num;
+ BLI_condition_notify_all(&pool->num_cond);
+
+ BLI_mutex_unlock(&pool->num_mutex);
+}
+
+static bool task_scheduler_thread_wait_pop(TaskScheduler *scheduler, Task **task)
+{
+ bool found_task = false;
+ BLI_mutex_lock(&scheduler->queue_mutex);
+
+ while (!scheduler->queue.first && !scheduler->do_exit) {
+ BLI_condition_wait(&scheduler->queue_cond, &scheduler->queue_mutex);
+ }
+
+ do {
+ Task *current_task;
+
+ /* Assuming we can only have a void queue in 'exit' case here seems logical
+ * (we should only be here after our worker thread has been woken up from a
+ * condition_wait(), which only happens after a new task was added to the queue),
+ * but it is wrong.
+ * Waiting on condition may wake up the thread even if condition is not signaled
+ * (spurious wake-ups), and some race condition may also empty the queue **after**
+ * condition has been signaled, but **before** awoken thread reaches this point...
+ * See http://stackoverflow.com/questions/8594591
+ *
+ * So we only abort here if do_exit is set.
+ */
+ if (scheduler->do_exit) {
+ BLI_mutex_unlock(&scheduler->queue_mutex);
+ return false;
+ }
+
+ for (current_task = (Task *)scheduler->queue.first; current_task != NULL;
+ current_task = current_task->next) {
+ TaskPool *pool = current_task->pool;
+
+ if (scheduler->background_thread_only && !pool->run_in_background) {
+ continue;
+ }
+
+ *task = current_task;
+ found_task = true;
+ BLI_remlink(&scheduler->queue, *task);
+ break;
+ }
+ if (!found_task) {
+ BLI_condition_wait(&scheduler->queue_cond, &scheduler->queue_mutex);
+ }
+ } while (!found_task);
+
+ BLI_mutex_unlock(&scheduler->queue_mutex);
+
+ return true;
+}
+
+BLI_INLINE void handle_local_queue(TaskThreadLocalStorage *tls, const int thread_id)
+{
+ BLI_assert(!tls->do_delayed_push);
+ while (tls->num_local_queue > 0) {
+ /* We pop task from queue before handling it so handler of the task can
+ * push next job to the local queue.
+ */
+ tls->num_local_queue--;
+ Task *local_task = tls->local_queue[tls->num_local_queue];
+ /* TODO(sergey): Double-check work_and_wait() doesn't handle other's
+ * pool tasks.
+ */
+ TaskPool *local_pool = local_task->pool;
+ local_task->run(local_pool, local_task->taskdata, thread_id);
+ task_free(local_pool, local_task, thread_id);
+ }
+ BLI_assert(!tls->do_delayed_push);
+}
+
+static void *task_scheduler_thread_run(void *thread_p)
+{
+ TaskThread *thread = (TaskThread *)thread_p;
+ TaskThreadLocalStorage *tls = &thread->tls;
+ TaskScheduler *scheduler = thread->scheduler;
+ int thread_id = thread->id;
+ Task *task;
+
+ pthread_setspecific(scheduler->tls_id_key, thread);
+
+ /* signal the main thread when all threads have started */
+ BLI_mutex_lock(&scheduler->startup_mutex);
+ scheduler->num_thread_started++;
+ if (scheduler->num_thread_started == scheduler->num_threads) {
+ BLI_condition_notify_one(&scheduler->startup_cond);
+ }
+ BLI_mutex_unlock(&scheduler->startup_mutex);
+
+ /* keep popping off tasks */
+ while (task_scheduler_thread_wait_pop(scheduler, &task)) {
+ TaskPool *pool = task->pool;
+
+ /* run task */
+ BLI_assert(!tls->do_delayed_push);
+ task->run(pool, task->taskdata, thread_id);
+ BLI_assert(!tls->do_delayed_push);
+
+ /* delete task */
+ task_free(pool, task, thread_id);
+
+ /* Handle all tasks from local queue. */
+ handle_local_queue(tls, thread_id);
+
+ /* notify pool task was done */
+ task_pool_num_decrease(pool, 1);
+ }
+
+ return NULL;
+}
+
+TaskScheduler *BLI_task_scheduler_create(int num_threads)
+{
+ TaskScheduler *scheduler = (TaskScheduler *)MEM_callocN(sizeof(TaskScheduler), "TaskScheduler");
+
+ /* multiple places can use this task scheduler, sharing the same
+ * threads, so we keep track of the number of users. */
+ scheduler->do_exit = false;
+
+ BLI_listbase_clear(&scheduler->queue);
+ BLI_mutex_init(&scheduler->queue_mutex);
+ BLI_condition_init(&scheduler->queue_cond);
+
+ BLI_mutex_init(&scheduler->startup_mutex);
+ BLI_condition_init(&scheduler->startup_cond);
+ scheduler->num_thread_started = 0;
+
+ if (num_threads == 0) {
+ /* automatic number of threads will be main thread + num cores */
+ num_threads = BLI_system_thread_count();
+ }
+
+ /* main thread will also work, so we count it too */
+ num_threads -= 1;
+
+ /* Add background-only thread if needed. */
+ if (num_threads == 0) {
+ scheduler->background_thread_only = true;
+ num_threads = 1;
+ }
+
+ scheduler->task_threads = (TaskThread *)MEM_mallocN(sizeof(TaskThread) * (num_threads + 1),
+ "TaskScheduler task threads");
+
+ /* Initialize TLS for main thread. */
+ initialize_task_tls(&scheduler->task_threads[0].tls);
+
+ pthread_key_create(&scheduler->tls_id_key, NULL);
+
+ /* launch threads that will be waiting for work */
+ if (num_threads > 0) {
+ int i;
+
+ scheduler->num_threads = num_threads;
+ scheduler->threads = (pthread_t *)MEM_callocN(sizeof(pthread_t) * num_threads,
+ "TaskScheduler threads");
+
+ for (i = 0; i < num_threads; i++) {
+ TaskThread *thread = &scheduler->task_threads[i + 1];
+ thread->scheduler = scheduler;
+ thread->id = i + 1;
+ initialize_task_tls(&thread->tls);
+
+ if (pthread_create(&scheduler->threads[i], NULL, task_scheduler_thread_run, thread) != 0) {
+ fprintf(stderr, "TaskScheduler failed to launch thread %d/%d\n", i, num_threads);
+ }
+ }
+ }
+
+ /* Wait for all worker threads to start before returning to caller to prevent the case where
+ * threads are still starting and pthread_join is called, which causes a deadlock on pthreads4w.
+ */
+ BLI_mutex_lock(&scheduler->startup_mutex);
+ /* NOTE: Use loop here to avoid false-positive everything-is-ready caused by spontaneous thread
+ * wake up. */
+ while (scheduler->num_thread_started != num_threads) {
+ BLI_condition_wait(&scheduler->startup_cond, &scheduler->startup_mutex);
+ }
+ BLI_mutex_unlock(&scheduler->startup_mutex);
+
+ return scheduler;
+}
+
+void BLI_task_scheduler_free(TaskScheduler *scheduler)
+{
+ Task *task;
+
+ /* stop all waiting threads */
+ BLI_mutex_lock(&scheduler->queue_mutex);
+ scheduler->do_exit = true;
+ BLI_condition_notify_all(&scheduler->queue_cond);
+ BLI_mutex_unlock(&scheduler->queue_mutex);
+
+ pthread_key_delete(scheduler->tls_id_key);
+
+ /* delete threads */
+ if (scheduler->threads) {
+ int i;
+
+ for (i = 0; i < scheduler->num_threads; i++) {
+ if (pthread_join(scheduler->threads[i], NULL) != 0) {
+ fprintf(stderr, "TaskScheduler failed to join thread %d/%d\n", i, scheduler->num_threads);
+ }
+ }
+
+ MEM_freeN(scheduler->threads);
+ }
+
+ /* Delete task thread data */
+ if (scheduler->task_threads) {
+ for (int i = 0; i < scheduler->num_threads + 1; i++) {
+ TaskThreadLocalStorage *tls = &scheduler->task_threads[i].tls;
+ free_task_tls(tls);
+ }
+
+ MEM_freeN(scheduler->task_threads);
+ }
+
+ /* delete leftover tasks */
+ for (task = (Task *)scheduler->queue.first; task; task = task->next) {
+ task_data_free(task, 0);
+ }
+ BLI_freelistN(&scheduler->queue);
+
+ /* delete mutex/condition */
+ BLI_mutex_end(&scheduler->queue_mutex);
+ BLI_condition_end(&scheduler->queue_cond);
+ BLI_mutex_end(&scheduler->startup_mutex);
+ BLI_condition_end(&scheduler->startup_cond);
+
+ MEM_freeN(scheduler);
+}
+
+int BLI_task_scheduler_num_threads(TaskScheduler *scheduler)
+{
+ return scheduler->num_threads + 1;
+}
+
+static void task_scheduler_push(TaskScheduler *scheduler, Task *task, TaskPriority priority)
+{
+ task_pool_num_increase(task->pool, 1);
+
+ /* add task to queue */
+ BLI_mutex_lock(&scheduler->queue_mutex);
+
+ if (priority == TASK_PRIORITY_HIGH) {
+ BLI_addhead(&scheduler->queue, task);
+ }
+ else {
+ BLI_addtail(&scheduler->queue, task);
+ }
+
+ BLI_condition_notify_one(&scheduler->queue_cond);
+ BLI_mutex_unlock(&scheduler->queue_mutex);
+}
+
+static void task_scheduler_push_all(TaskScheduler *scheduler,
+ TaskPool *pool,
+ Task **tasks,
+ int num_tasks)
+{
+ if (num_tasks == 0) {
+ return;
+ }
+
+ task_pool_num_increase(pool, num_tasks);
+
+ BLI_mutex_lock(&scheduler->queue_mutex);
+
+ for (int i = 0; i < num_tasks; i++) {
+ BLI_addhead(&scheduler->queue, tasks[i]);
+ }
+
+ BLI_condition_notify_all(&scheduler->queue_cond);
+ BLI_mutex_unlock(&scheduler->queue_mutex);
+}
+
+static void task_scheduler_clear(TaskScheduler *scheduler, TaskPool *pool)
+{
+ Task *task, *nexttask;
+ size_t done = 0;
+
+ BLI_mutex_lock(&scheduler->queue_mutex);
+
+ /* free all tasks from this pool from the queue */
+ for (task = (Task *)scheduler->queue.first; task; task = nexttask) {
+ nexttask = task->next;
+
+ if (task->pool == pool) {
+ task_data_free(task, pool->thread_id);
+ BLI_freelinkN(&scheduler->queue, task);
+
+ done++;
+ }
+ }
+
+ BLI_mutex_unlock(&scheduler->queue_mutex);
+
+ /* notify done */
+ task_pool_num_decrease(pool, done);
+}
+
+/* Task Pool */
+
+static TaskPool *task_pool_create_ex(TaskScheduler *scheduler,
+ void *userdata,
+ const bool is_background,
+ const bool is_suspended,
+ TaskPriority priority)
+{
+ TaskPool *pool = (TaskPool *)MEM_mallocN(sizeof(TaskPool), "TaskPool");
+
+#ifndef NDEBUG
+ /* Assert we do not try to create a background pool from some parent task -
+ * those only work OK from main thread. */
+ if (is_background) {
+ const pthread_t thread_id = pthread_self();
+ int i = scheduler->num_threads;
+
+ while (i--) {
+ BLI_assert(!pthread_equal(scheduler->threads[i], thread_id));
+ }
+ }
+#endif
+
+ pool->scheduler = scheduler;
+ pool->num = 0;
+ pool->do_cancel = false;
+ pool->do_work = false;
+ pool->is_suspended = is_suspended;
+ pool->start_suspended = is_suspended;
+ pool->num_suspended = 0;
+ pool->suspended_queue.first = pool->suspended_queue.last = NULL;
+ pool->priority = priority;
+ pool->run_in_background = is_background;
+ pool->use_local_tls = false;
+
+ BLI_mutex_init(&pool->num_mutex);
+ BLI_condition_init(&pool->num_cond);
+
+ pool->userdata = userdata;
+ BLI_mutex_init(&pool->user_mutex);
+
+ if (BLI_thread_is_main()) {
+ pool->thread_id = 0;
+ }
+ else {
+ TaskThread *thread = (TaskThread *)pthread_getspecific(scheduler->tls_id_key);
+ if (thread == NULL) {
+ /* NOTE: Task pool is created from non-main thread which is not
+ * managed by the task scheduler. We identify ourselves as thread ID
+ * 0 but we do not use scheduler's TLS storage and use our own
+ * instead to avoid any possible threading conflicts.
+ */
+ pool->thread_id = 0;
+ pool->use_local_tls = true;
+#ifndef NDEBUG
+ pool->creator_thread_id = pthread_self();
+#endif
+ initialize_task_tls(&pool->local_tls);
+ }
+ else {
+ pool->thread_id = thread->id;
+ }
+ }
+
+#ifdef DEBUG_STATS
+ pool->mempool_stats = (TaskMemPoolStats *)MEM_callocN(
+ sizeof(*pool->mempool_stats) * (scheduler->num_threads + 1), "per-taskpool mempool stats");
+#endif
+
+ /* Ensure malloc will go fine from threads,
+ *
+ * This is needed because we could be in main thread here
+ * and malloc could be non-thread safe at this point because
+ * no other jobs are running.
+ */
+ BLI_threaded_malloc_begin();
+
+ return pool;
+}
+
+/**
+ * Create a normal task pool. Tasks will be executed as soon as they are added.
+ */
+TaskPool *BLI_task_pool_create(TaskScheduler *scheduler, void *userdata, TaskPriority priority)
+{
+ return task_pool_create_ex(scheduler, userdata, false, false, priority);
+}
+
+/**
+ * Create a background task pool.
+ * In multi-threaded context, there is no differences with #BLI_task_pool_create(),
+ * but in single-threaded case it is ensured to have at least one worker thread to run on
+ * (i.e. you don't have to call #BLI_task_pool_work_and_wait
+ * on it to be sure it will be processed).
+ *
+ * \note Background pools are non-recursive
+ * (that is, you should not create other background pools in tasks assigned to a background pool,
+ * they could end never being executed, since the 'fallback' background thread is already
+ * busy with parent task in single-threaded context).
+ */
+TaskPool *BLI_task_pool_create_background(TaskScheduler *scheduler,
+ void *userdata,
+ TaskPriority priority)
+{
+ return task_pool_create_ex(scheduler, userdata, true, false, priority);
+}
+
+/**
+ * Similar to BLI_task_pool_create() but does not schedule any tasks for execution
+ * for until BLI_task_pool_work_and_wait() is called. This helps reducing threading
+ * overhead when pushing huge amount of small initial tasks from the main thread.
+ */
+TaskPool *BLI_task_pool_create_suspended(TaskScheduler *scheduler,
+ void *userdata,
+ TaskPriority priority)
+{
+ return task_pool_create_ex(scheduler, userdata, false, true, priority);
+}
+
+void BLI_task_pool_free(TaskPool *pool)
+{
+ BLI_task_pool_cancel(pool);
+
+ BLI_mutex_end(&pool->num_mutex);
+ BLI_condition_end(&pool->num_cond);
+
+ BLI_mutex_end(&pool->user_mutex);
+
+#ifdef DEBUG_STATS
+ printf("Thread ID Allocated Reused Discarded\n");
+ for (int i = 0; i < pool->scheduler->num_threads + 1; i++) {
+ printf("%02d %05d %05d %05d\n",
+ i,
+ pool->mempool_stats[i].num_alloc,
+ pool->mempool_stats[i].num_reuse,
+ pool->mempool_stats[i].num_discard);
+ }
+ MEM_freeN(pool->mempool_stats);
+#endif
+
+ if (pool->use_local_tls) {
+ free_task_tls(&pool->local_tls);
+ }
+
+ MEM_freeN(pool);
+
+ BLI_threaded_malloc_end();
+}
+
+BLI_INLINE bool task_can_use_local_queues(TaskPool *pool, int thread_id)
+{
+ return (thread_id != -1 && (thread_id != pool->thread_id || pool->do_work));
+}
+
+static void task_pool_push(TaskPool *pool,
+ TaskRunFunction run,
+ void *taskdata,
+ bool free_taskdata,
+ TaskFreeFunction freedata,
+ int thread_id)
+{
+ /* Allocate task and fill it's properties. */
+ Task *task = task_alloc(pool, thread_id);
+ task->run = run;
+ task->taskdata = taskdata;
+ task->free_taskdata = free_taskdata;
+ task->freedata = freedata;
+ task->pool = pool;
+ /* For suspended pools we put everything yo a global queue first
+ * and exit as soon as possible.
+ *
+ * This tasks will be moved to actual execution when pool is
+ * activated by work_and_wait().
+ */
+ if (pool->is_suspended) {
+ BLI_addhead(&pool->suspended_queue, task);
+ atomic_fetch_and_add_z(&pool->num_suspended, 1);
+ return;
+ }
+ /* Populate to any local queue first, this is cheapest push ever. */
+ if (task_can_use_local_queues(pool, thread_id)) {
+ ASSERT_THREAD_ID(pool->scheduler, thread_id);
+ TaskThreadLocalStorage *tls = get_task_tls(pool, thread_id);
+ /* Try to push to a local execution queue.
+ * These tasks will be picked up next.
+ */
+ if (tls->num_local_queue < LOCAL_QUEUE_SIZE) {
+ tls->local_queue[tls->num_local_queue] = task;
+ tls->num_local_queue++;
+ return;
+ }
+ /* If we are in the delayed tasks push mode, we push tasks to a
+ * temporary local queue first without any locks, and then move them
+ * to global execution queue with a single lock.
+ */
+ if (tls->do_delayed_push && tls->num_delayed_queue < DELAYED_QUEUE_SIZE) {
+ tls->delayed_queue[tls->num_delayed_queue] = task;
+ tls->num_delayed_queue++;
+ return;
+ }
+ }
+ /* Do push to a global execution pool, slowest possible method,
+ * causes quite reasonable amount of threading overhead.
+ */
+ task_scheduler_push(pool->scheduler, task, pool->priority);
+}
+
+void BLI_task_pool_push(TaskPool *pool,
+ TaskRunFunction run,
+ void *taskdata,
+ bool free_taskdata,
+ TaskFreeFunction freedata)
+{
+ task_pool_push(pool, run, taskdata, free_taskdata, freedata, -1);
+}
+
+void BLI_task_pool_push_from_thread(TaskPool *pool,
+ TaskRunFunction run,
+ void *taskdata,
+ bool free_taskdata,
+ TaskFreeFunction freedata,
+ int thread_id)
+{
+ task_pool_push(pool, run, taskdata, free_taskdata, freedata, thread_id);
+}
+
+void BLI_task_pool_work_and_wait(TaskPool *pool)
+{
+ TaskThreadLocalStorage *tls = get_task_tls(pool, pool->thread_id);
+ TaskScheduler *scheduler = pool->scheduler;
+
+ if (atomic_fetch_and_and_uint8((uint8_t *)&pool->is_suspended, 0)) {
+ if (pool->num_suspended) {
+ task_pool_num_increase(pool, pool->num_suspended);
+ BLI_mutex_lock(&scheduler->queue_mutex);
+
+ BLI_movelisttolist(&scheduler->queue, &pool->suspended_queue);
+
+ BLI_condition_notify_all(&scheduler->queue_cond);
+ BLI_mutex_unlock(&scheduler->queue_mutex);
+
+ pool->num_suspended = 0;
+ }
+ }
+
+ pool->do_work = true;
+
+ ASSERT_THREAD_ID(pool->scheduler, pool->thread_id);
+
+ handle_local_queue(tls, pool->thread_id);
+
+ BLI_mutex_lock(&pool->num_mutex);
+
+ while (pool->num != 0) {
+ Task *task, *work_task = NULL;
+ bool found_task = false;
+
+ BLI_mutex_unlock(&pool->num_mutex);
+
+ BLI_mutex_lock(&scheduler->queue_mutex);
+
+ /* find task from this pool. if we get a task from another pool,
+ * we can get into deadlock */
+
+ for (task = (Task *)scheduler->queue.first; task; task = task->next) {
+ if (task->pool == pool) {
+ work_task = task;
+ found_task = true;
+ BLI_remlink(&scheduler->queue, task);
+ break;
+ }
+ }
+
+ BLI_mutex_unlock(&scheduler->queue_mutex);
+
+ /* if found task, do it, otherwise wait until other tasks are done */
+ if (found_task) {
+ /* run task */
+ BLI_assert(!tls->do_delayed_push);
+ work_task->run(pool, work_task->taskdata, pool->thread_id);
+ BLI_assert(!tls->do_delayed_push);
+
+ /* delete task */
+ task_free(pool, task, pool->thread_id);
+
+ /* Handle all tasks from local queue. */
+ handle_local_queue(tls, pool->thread_id);
+
+ /* notify pool task was done */
+ task_pool_num_decrease(pool, 1);
+ }
+
+ BLI_mutex_lock(&pool->num_mutex);
+ if (pool->num == 0) {
+ break;
+ }
+
+ if (!found_task) {
+ BLI_condition_wait(&pool->num_cond, &pool->num_mutex);
+ }
+ }
+
+ BLI_mutex_unlock(&pool->num_mutex);
+
+ BLI_assert(tls->num_local_queue == 0);
+}
+
+void BLI_task_pool_work_wait_and_reset(TaskPool *pool)
+{
+ BLI_task_pool_work_and_wait(pool);
+
+ pool->do_work = false;
+ pool->is_suspended = pool->start_suspended;
+}
+
+void BLI_task_pool_cancel(TaskPool *pool)
+{
+ pool->do_cancel = true;
+
+ task_scheduler_clear(pool->scheduler, pool);
+
+ /* wait until all entries are cleared */
+ BLI_mutex_lock(&pool->num_mutex);
+ while (pool->num) {
+ BLI_condition_wait(&pool->num_cond, &pool->num_mutex);
+ }
+ BLI_mutex_unlock(&pool->num_mutex);
+
+ pool->do_cancel = false;
+}
+
+bool BLI_task_pool_canceled(TaskPool *pool)
+{
+ return pool->do_cancel;
+}
+
+void *BLI_task_pool_userdata(TaskPool *pool)
+{
+ return pool->userdata;
+}
+
+ThreadMutex *BLI_task_pool_user_mutex(TaskPool *pool)
+{
+ return &pool->user_mutex;
+}
+
+int BLI_task_pool_creator_thread_id(TaskPool *pool)
+{
+ return pool->thread_id;
+}
+
+void BLI_task_pool_delayed_push_begin(TaskPool *pool, int thread_id)
+{
+ if (task_can_use_local_queues(pool, thread_id)) {
+ ASSERT_THREAD_ID(pool->scheduler, thread_id);
+ TaskThreadLocalStorage *tls = get_task_tls(pool, thread_id);
+ tls->do_delayed_push = true;
+ }
+}
+
+void BLI_task_pool_delayed_push_end(TaskPool *pool, int thread_id)
+{
+ if (task_can_use_local_queues(pool, thread_id)) {
+ ASSERT_THREAD_ID(pool->scheduler, thread_id);
+ TaskThreadLocalStorage *tls = get_task_tls(pool, thread_id);
+ BLI_assert(tls->do_delayed_push);
+ task_scheduler_push_all(pool->scheduler, pool, tls->delayed_queue, tls->num_delayed_queue);
+ tls->do_delayed_push = false;
+ tls->num_delayed_queue = 0;
+ }
+}
diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c
index 96c7339c6da..53c22b7ce9b 100644
--- a/source/blender/blenloader/intern/readfile.c
+++ b/source/blender/blenloader/intern/readfile.c
@@ -2845,11 +2845,11 @@ static int direct_link_id_restore_recalc(const FileData *fd,
* that we need to perform again. */
if (fd->undo_direction < 0) {
/* Undo: tags from target to the current state. */
- recalc |= id_current->recalc_undo_accumulated;
+ recalc |= id_current->recalc_up_to_undo_push;
}
else {
/* Redo: tags from current to the target state. */
- recalc |= id_target->recalc_undo_accumulated;
+ recalc |= id_target->recalc_up_to_undo_push;
}
}
@@ -2880,11 +2880,11 @@ static void direct_link_id_common(FileData *fd, ID *id, ID *id_old, const int ta
* the version the file has been saved with. */
if (fd->memfile == NULL) {
id->recalc = 0;
- id->recalc_undo_accumulated = 0;
+ id->recalc_after_undo_push = 0;
}
else if ((fd->skip_flags & BLO_READ_SKIP_UNDO_OLD_MAIN) == 0) {
id->recalc = direct_link_id_restore_recalc(fd, id, id_old, false);
- id->recalc_undo_accumulated = 0;
+ id->recalc_after_undo_push = 0;
}
/* Link direct data of overrides. */
@@ -3834,20 +3834,11 @@ typedef struct tConstraintLinkData {
/* callback function used to relink constraint ID-links */
static void lib_link_constraint_cb(bConstraint *UNUSED(con),
ID **idpoin,
- bool is_reference,
+ bool UNUSED(is_reference),
void *userdata)
{
tConstraintLinkData *cld = (tConstraintLinkData *)userdata;
-
- /* for reference types, we need to increment the user-counts on load... */
- if (is_reference) {
- /* reference type - with usercount */
- *idpoin = newlibadr(cld->fd, cld->id->lib, *idpoin);
- }
- else {
- /* target type - no usercount needed */
- *idpoin = newlibadr(cld->fd, cld->id->lib, *idpoin);
- }
+ *idpoin = newlibadr(cld->fd, cld->id->lib, *idpoin);
}
static void lib_link_constraints(FileData *fd, ID *id, ListBase *conlist)
@@ -9579,7 +9570,7 @@ static void read_libblock_undo_restore_identical(
/* Recalc flags, mostly these just remain as they are. */
id_old->recalc |= direct_link_id_restore_recalc_exceptions(id_old);
- id_old->recalc_undo_accumulated = 0;
+ id_old->recalc_after_undo_push = 0;
/* As usual, proxies require some special love...
* In `blo_clear_proxy_pointers_from_lib()` we clear all `proxy_from` pointers to local IDs, for
diff --git a/source/blender/blenloader/intern/versioning_280.c b/source/blender/blenloader/intern/versioning_280.c
index ff3aef4588f..d53b6cde4ae 100644
--- a/source/blender/blenloader/intern/versioning_280.c
+++ b/source/blender/blenloader/intern/versioning_280.c
@@ -4405,6 +4405,17 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain)
}
if (!MAIN_VERSION_ATLEAST(bmain, 283, 3)) {
+ /* Color Management Look. */
+ for (Scene *scene = bmain->scenes.first; scene; scene = scene->id.next) {
+ ColorManagedViewSettings *view_settings;
+ view_settings = &scene->view_settings;
+ if (BLI_str_startswith(view_settings->look, "Filmic - ")) {
+ STRNCPY(view_settings->look, view_settings->look + strlen("Filmic - "));
+ }
+ else if (BLI_str_startswith(view_settings->look, "Standard - ")) {
+ STRNCPY(view_settings->look, view_settings->look + strlen("Standard - "));
+ }
+ }
/* Sequencer Tool region */
do_versions_area_ensure_tool_region(bmain, SPACE_SEQ, RGN_FLAG_HIDDEN);
@@ -4879,7 +4890,6 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain)
}
if (!MAIN_VERSION_ATLEAST(bmain, 283, 12)) {
-
/* Activate f-curve drawing in the sequencer. */
for (bScreen *screen = bmain->screens.first; screen; screen = screen->id.next) {
for (ScrArea *area = screen->areabase.first; area; area = area->next) {
@@ -4918,5 +4928,18 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain)
*/
{
/* Keep this block, even when empty. */
+
+ /* Solidify modifier merge tolerance. */
+ if (!DNA_struct_elem_find(fd->filesdna, "SolidifyModifierData", "float", "merge_tolerance")) {
+ for (Object *ob = bmain->objects.first; ob; ob = ob->id.next) {
+ for (ModifierData *md = ob->modifiers.first; md; md = md->next) {
+ if (md->type == eModifierType_Solidify) {
+ SolidifyModifierData *smd = (SolidifyModifierData *)md;
+ /* set to 0.0003 since that is what was used before, default now is 0.0001 */
+ smd->merge_tolerance = 0.0003f;
+ }
+ }
+ }
+ }
}
}
diff --git a/source/blender/blenloader/intern/versioning_defaults.c b/source/blender/blenloader/intern/versioning_defaults.c
index f4288b1d174..19606b92479 100644
--- a/source/blender/blenloader/intern/versioning_defaults.c
+++ b/source/blender/blenloader/intern/versioning_defaults.c
@@ -376,6 +376,92 @@ void BLO_update_defaults_startup_blend(Main *bmain, const char *app_template)
BLO_update_defaults_workspace(workspace, app_template);
}
+ /* New grease pencil brushes and vertex paint setup. */
+ {
+ /* Update Grease Pencil brushes. */
+ Brush *brush;
+
+ /* Pencil brush. */
+ rename_id_for_versioning(bmain, ID_BR, "Draw Pencil", "Pencil");
+
+ /* Pen brush. */
+ rename_id_for_versioning(bmain, ID_BR, "Draw Pen", "Pen");
+
+ /* Pen Soft brush. */
+ brush = (Brush *)rename_id_for_versioning(bmain, ID_BR, "Draw Soft", "Pencil Soft");
+ if (brush) {
+ brush->gpencil_settings->icon_id = GP_BRUSH_ICON_PEN;
+ }
+
+ /* Ink Pen brush. */
+ rename_id_for_versioning(bmain, ID_BR, "Draw Ink", "Ink Pen");
+
+ /* Ink Pen Rough brush. */
+ rename_id_for_versioning(bmain, ID_BR, "Draw Noise", "Ink Pen Rough");
+
+ /* Marker Bold brush. */
+ rename_id_for_versioning(bmain, ID_BR, "Draw Marker", "Marker Bold");
+
+ /* Marker Chisel brush. */
+ rename_id_for_versioning(bmain, ID_BR, "Draw Block", "Marker Chisel");
+
+ /* Remove useless Fill Area.001 brush. */
+ brush = BLI_findstring(&bmain->brushes, "Fill Area.001", offsetof(ID, name) + 2);
+ if (brush) {
+ BKE_id_delete(bmain, brush);
+ }
+
+ /* Rename and fix materials and enable default object lights on. */
+ if (app_template && STREQ(app_template, "2D_Animation")) {
+ Material *ma = NULL;
+ rename_id_for_versioning(bmain, ID_MA, "Black", "Solid Stroke");
+ rename_id_for_versioning(bmain, ID_MA, "Red", "Squares Stroke");
+ rename_id_for_versioning(bmain, ID_MA, "Grey", "Solid Fill");
+ rename_id_for_versioning(bmain, ID_MA, "Black Dots", "Dots Stroke");
+
+ /* Dots Stroke. */
+ ma = BLI_findstring(&bmain->materials, "Dots Stroke", offsetof(ID, name) + 2);
+ if (ma == NULL) {
+ ma = BKE_gpencil_material_add(bmain, "Dots Stroke");
+ }
+ ma->gp_style->mode = GP_MATERIAL_MODE_DOT;
+
+ /* Squares Stroke. */
+ ma = BLI_findstring(&bmain->materials, "Squares Stroke", offsetof(ID, name) + 2);
+ if (ma == NULL) {
+ ma = BKE_gpencil_material_add(bmain, "Squares Stroke");
+ }
+ ma->gp_style->mode = GP_MATERIAL_MODE_SQUARE;
+
+ /* Change Solid Fill settings. */
+ ma = BLI_findstring(&bmain->materials, "Solid Fill", offsetof(ID, name) + 2);
+ if (ma != NULL) {
+ ma->gp_style->flag &= ~GP_MATERIAL_STROKE_SHOW;
+ }
+
+ Object *ob = BLI_findstring(&bmain->objects, "Stroke", offsetof(ID, name) + 2);
+ if (ob && ob->type == OB_GPENCIL) {
+ ob->dtx |= OB_USE_GPENCIL_LIGHTS;
+ }
+ }
+
+ /* Reset all grease pencil brushes. */
+ Scene *scene = bmain->scenes.first;
+ BKE_brush_gpencil_paint_presets(bmain, scene->toolsettings);
+
+ /* Ensure new Paint modes. */
+ BKE_paint_ensure_from_paintmode(scene, PAINT_MODE_VERTEX_GPENCIL);
+ BKE_paint_ensure_from_paintmode(scene, PAINT_MODE_SCULPT_GPENCIL);
+ BKE_paint_ensure_from_paintmode(scene, PAINT_MODE_WEIGHT_GPENCIL);
+
+ /* Enable cursor. */
+ GpPaint *gp_paint = scene->toolsettings->gp_paint;
+ gp_paint->paint.flags |= PAINT_SHOW_BRUSH;
+
+ /* Ensure Palette by default. */
+ BKE_gpencil_palette_ensure(bmain, scene);
+ }
+
/* For builtin templates only. */
if (!blo_is_builtin_template(app_template)) {
return;
@@ -598,90 +684,4 @@ void BLO_update_defaults_startup_blend(Main *bmain, const char *app_template)
}
}
}
-
- /* New grease pencil brushes and vertex paint setup. */
- {
- /* Update Grease Pencil brushes. */
- Brush *brush;
-
- /* Pencil brush. */
- rename_id_for_versioning(bmain, ID_BR, "Draw Pencil", "Pencil");
-
- /* Pen brush. */
- rename_id_for_versioning(bmain, ID_BR, "Draw Pen", "Pen");
-
- /* Pen Soft brush. */
- brush = (Brush *)rename_id_for_versioning(bmain, ID_BR, "Draw Soft", "Pencil Soft");
- if (brush) {
- brush->gpencil_settings->icon_id = GP_BRUSH_ICON_PEN;
- }
-
- /* Ink Pen brush. */
- rename_id_for_versioning(bmain, ID_BR, "Draw Ink", "Ink Pen");
-
- /* Ink Pen Rough brush. */
- rename_id_for_versioning(bmain, ID_BR, "Draw Noise", "Ink Pen Rough");
-
- /* Marker Bold brush. */
- rename_id_for_versioning(bmain, ID_BR, "Draw Marker", "Marker Bold");
-
- /* Marker Chisel brush. */
- rename_id_for_versioning(bmain, ID_BR, "Draw Block", "Marker Chisel");
-
- /* Remove useless Fill Area.001 brush. */
- brush = BLI_findstring(&bmain->brushes, "Fill Area.001", offsetof(ID, name) + 2);
- if (brush) {
- BKE_id_delete(bmain, brush);
- }
-
- /* Rename and fix materials and enable default object lights on. */
- if (app_template && STREQ(app_template, "2D_Animation")) {
- Material *ma = NULL;
- rename_id_for_versioning(bmain, ID_MA, "Black", "Solid Stroke");
- rename_id_for_versioning(bmain, ID_MA, "Red", "Squares Stroke");
- rename_id_for_versioning(bmain, ID_MA, "Grey", "Solid Fill");
- rename_id_for_versioning(bmain, ID_MA, "Black Dots", "Dots Stroke");
-
- /* Dots Stroke. */
- ma = BLI_findstring(&bmain->materials, "Dots Stroke", offsetof(ID, name) + 2);
- if (ma == NULL) {
- ma = BKE_gpencil_material_add(bmain, "Dots Stroke");
- }
- ma->gp_style->mode = GP_MATERIAL_MODE_DOT;
-
- /* Squares Stroke. */
- ma = BLI_findstring(&bmain->materials, "Squares Stroke", offsetof(ID, name) + 2);
- if (ma == NULL) {
- ma = BKE_gpencil_material_add(bmain, "Squares Stroke");
- }
- ma->gp_style->mode = GP_MATERIAL_MODE_SQUARE;
-
- /* Change Solid Fill settings. */
- ma = BLI_findstring(&bmain->materials, "Solid Fill", offsetof(ID, name) + 2);
- if (ma != NULL) {
- ma->gp_style->flag &= ~GP_MATERIAL_STROKE_SHOW;
- }
-
- Object *ob = BLI_findstring(&bmain->objects, "Stroke", offsetof(ID, name) + 2);
- if (ob && ob->type == OB_GPENCIL) {
- ob->dtx |= OB_USE_GPENCIL_LIGHTS;
- }
- }
-
- /* Reset all grease pencil brushes. */
- Scene *scene = bmain->scenes.first;
- BKE_brush_gpencil_paint_presets(bmain, scene->toolsettings);
-
- /* Ensure new Paint modes. */
- BKE_paint_ensure_from_paintmode(scene, PAINT_MODE_VERTEX_GPENCIL);
- BKE_paint_ensure_from_paintmode(scene, PAINT_MODE_SCULPT_GPENCIL);
- BKE_paint_ensure_from_paintmode(scene, PAINT_MODE_WEIGHT_GPENCIL);
-
- /* Enable cursor. */
- GpPaint *gp_paint = scene->toolsettings->gp_paint;
- gp_paint->paint.flags |= PAINT_SHOW_BRUSH;
-
- /* Ensure Palette by default. */
- BKE_gpencil_palette_ensure(bmain, scene);
- }
}
diff --git a/source/blender/blenloader/intern/writefile.c b/source/blender/blenloader/intern/writefile.c
index 68be8683f84..8bc6a90befb 100644
--- a/source/blender/blenloader/intern/writefile.c
+++ b/source/blender/blenloader/intern/writefile.c
@@ -1133,11 +1133,6 @@ static void write_nodetree_nolib(WriteData *wd, bNodeTree *ntree)
for (sock = ntree->outputs.first; sock; sock = sock->next) {
write_node_socket_interface(wd, sock);
}
-
- /* Clear the accumulated recalc flags in case of undo step saving. */
- if (wd->use_memfile) {
- ntree->id.recalc_undo_accumulated = 0;
- }
}
/**
@@ -2485,11 +2480,6 @@ static void write_collection_nolib(WriteData *wd, Collection *collection)
LISTBASE_FOREACH (CollectionChild *, child, &collection->children) {
writestruct(wd, DATA, CollectionChild, 1, child);
}
-
- /* Clear the accumulated recalc flags in case of undo step saving. */
- if (wd->use_memfile) {
- collection->id.recalc_undo_accumulated = 0;
- }
}
static void write_collection(WriteData *wd, Collection *collection, const void *id_address)
@@ -4100,6 +4090,28 @@ static bool write_file_handle(Main *mainvar,
BKE_lib_override_library_operations_store_start(bmain, override_storage, id);
}
+ if (wd->use_memfile) {
+ /* Record the changes that happened up to this undo push in
+ * recalc_up_to_undo_push, and clear recalc_after_undo_push again
+ * to start accumulating for the next undo push. */
+ id->recalc_up_to_undo_push = id->recalc_after_undo_push;
+ id->recalc_after_undo_push = 0;
+
+ bNodeTree *nodetree = ntreeFromID(id);
+ if (nodetree != NULL) {
+ nodetree->id.recalc_up_to_undo_push = nodetree->id.recalc_after_undo_push;
+ nodetree->id.recalc_after_undo_push = 0;
+ }
+ if (GS(id->name) == ID_SCE) {
+ Scene *scene = (Scene *)id;
+ if (scene->master_collection != NULL) {
+ scene->master_collection->id.recalc_up_to_undo_push =
+ scene->master_collection->id.recalc_after_undo_push;
+ scene->master_collection->id.recalc_after_undo_push = 0;
+ }
+ }
+ }
+
memcpy(id_buffer, id, idtype_struct_size);
((ID *)id_buffer)->tag = 0;
@@ -4237,9 +4249,6 @@ static bool write_file_handle(Main *mainvar,
/* Very important to do it after every ID write now, otherwise we cannot know whether a
* specific ID changed or not. */
mywrite_flush(wd);
-
- /* Clear the accumulated recalc flags in case of undo step saving. */
- id->recalc_undo_accumulated = 0;
}
}
diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations_rig.cc b/source/blender/depsgraph/intern/builder/deg_builder_relations_rig.cc
index 6fe9a3c2a00..d05385a7d7c 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder_relations_rig.cc
+++ b/source/blender/depsgraph/intern/builder/deg_builder_relations_rig.cc
@@ -68,6 +68,12 @@ void DepsgraphRelationBuilder::build_ik_pose(Object *object,
bConstraint *con,
RootPChanMap *root_map)
{
+ if ((con->flag & CONSTRAINT_DISABLE) != 0) {
+ /* Do not add disabled IK constraints to the relations. If these needs to be temporarly
+ * enabled, they will be added as temporary constraints during transform. */
+ return;
+ }
+
bKinematicConstraint *data = (bKinematicConstraint *)con->data;
/* Attach owner to IK Solver to. */
bPoseChannel *rootchan = BKE_armature_ik_solver_find_root(pchan, data);
diff --git a/source/blender/depsgraph/intern/depsgraph_eval.cc b/source/blender/depsgraph/intern/depsgraph_eval.cc
index 7e7ab07825f..9251d975125 100644
--- a/source/blender/depsgraph/intern/depsgraph_eval.cc
+++ b/source/blender/depsgraph/intern/depsgraph_eval.cc
@@ -20,7 +20,7 @@
/** \file
* \ingroup depsgraph
*
- * Evaluation engine entrypoints for Depsgraph Engine.
+ * Evaluation engine entry-points for Depsgraph Engine.
*/
#include "MEM_guardedalloc.h"
diff --git a/source/blender/depsgraph/intern/depsgraph_tag.cc b/source/blender/depsgraph/intern/depsgraph_tag.cc
index 627a93b5869..1c56808ea82 100644
--- a/source/blender/depsgraph/intern/depsgraph_tag.cc
+++ b/source/blender/depsgraph/intern/depsgraph_tag.cc
@@ -625,7 +625,7 @@ void id_tag_update(Main *bmain, ID *id, int flag, eUpdateSource update_source)
/* Accumulate all tags for an ID between two undo steps, so they can be
* replayed for undo. */
- id->recalc_undo_accumulated |= deg_recalc_flags_effective(NULL, flag);
+ id->recalc_after_undo_push |= deg_recalc_flags_effective(NULL, flag);
}
void graph_id_tag_update(
diff --git a/source/blender/depsgraph/intern/eval/deg_eval.cc b/source/blender/depsgraph/intern/eval/deg_eval.cc
index 4540cda7e99..c7f3c5cb2b6 100644
--- a/source/blender/depsgraph/intern/eval/deg_eval.cc
+++ b/source/blender/depsgraph/intern/eval/deg_eval.cc
@@ -72,8 +72,7 @@ void schedule_children(DepsgraphEvalState *state,
void schedule_node_to_pool(OperationNode *node, const int thread_id, TaskPool *pool)
{
- BLI_task_pool_push_from_thread(
- pool, deg_task_run_func, node, false, TASK_PRIORITY_HIGH, thread_id);
+ BLI_task_pool_push_from_thread(pool, deg_task_run_func, node, false, NULL, thread_id);
}
/* Denotes which part of dependency graph is being evaluated. */
@@ -389,7 +388,7 @@ void deg_evaluate_on_refresh(Depsgraph *graph)
task_scheduler = BLI_task_scheduler_get();
need_free_scheduler = false;
}
- TaskPool *task_pool = BLI_task_pool_create_suspended(task_scheduler, &state);
+ TaskPool *task_pool = BLI_task_pool_create_suspended(task_scheduler, &state, TASK_PRIORITY_HIGH);
/* Prepare all nodes for evaluation. */
initialize_execution(&state, graph);
diff --git a/source/blender/draw/engines/eevee/eevee_lightcache.c b/source/blender/draw/engines/eevee/eevee_lightcache.c
index 9447c365c48..614c749b9aa 100644
--- a/source/blender/draw/engines/eevee/eevee_lightcache.c
+++ b/source/blender/draw/engines/eevee/eevee_lightcache.c
@@ -777,8 +777,6 @@ static void eevee_lightbake_delete_resources(EEVEE_LightBake *lbake)
if (!lbake->resource_only) {
BLI_mutex_unlock(lbake->mutex);
}
-
- EEVEE_volumes_free_smoke_textures();
}
/* Cache as in draw cache not light cache. */
@@ -1347,6 +1345,9 @@ void EEVEE_lightbake_job(void *custom_data, short *stop, short *do_update, float
}
eevee_lightbake_delete_resources(lbake);
+
+ /* Free GPU smoke textures and the smoke domain list correctly: See also T73921.*/
+ EEVEE_volumes_free_smoke_textures();
}
/* This is to update the world irradiance and reflection contribution from
diff --git a/source/blender/draw/intern/draw_cache_extract_mesh.c b/source/blender/draw/intern/draw_cache_extract_mesh.c
index 0449601adf7..fe63dec1294 100644
--- a/source/blender/draw/intern/draw_cache_extract_mesh.c
+++ b/source/blender/draw/intern/draw_cache_extract_mesh.c
@@ -4524,7 +4524,7 @@ static void extract_range_task_create(
taskdata->iter_type = type;
taskdata->start = start;
taskdata->end = start + length;
- BLI_task_pool_push(task_pool, extract_run, taskdata, true, TASK_PRIORITY_HIGH);
+ BLI_task_pool_push(task_pool, extract_run, taskdata, true, NULL);
}
static void extract_task_create(TaskPool *task_pool,
@@ -4583,7 +4583,7 @@ static void extract_task_create(TaskPool *task_pool,
else if (use_thread) {
/* One task for the whole VBO. */
(*task_counter)++;
- BLI_task_pool_push(task_pool, extract_run, taskdata, true, TASK_PRIORITY_HIGH);
+ BLI_task_pool_push(task_pool, extract_run, taskdata, true, NULL);
}
else {
/* Single threaded extraction. */
@@ -4682,7 +4682,7 @@ void mesh_buffer_cache_create_requested(MeshBatchCache *cache,
TaskPool *task_pool;
task_scheduler = BLI_task_scheduler_get();
- task_pool = BLI_task_pool_create_suspended(task_scheduler, NULL);
+ task_pool = BLI_task_pool_create_suspended(task_scheduler, NULL, TASK_PRIORITY_HIGH);
size_t counters_size = (sizeof(mbc) / sizeof(void *)) * sizeof(int32_t);
int32_t *task_counters = MEM_callocN(counters_size, __func__);
diff --git a/source/blender/draw/intern/draw_cache_impl_pointcloud.c b/source/blender/draw/intern/draw_cache_impl_pointcloud.c
index 83757cb714a..53939b35285 100644
--- a/source/blender/draw/intern/draw_cache_impl_pointcloud.c
+++ b/source/blender/draw/intern/draw_cache_impl_pointcloud.c
@@ -134,7 +134,7 @@ static void pointcloud_batch_cache_ensure_pos(Object *ob, PointCloudBatchCache *
/* initialize vertex format */
pos_id = GPU_vertformat_attr_add(&format, "pointcloud_pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
radius_id = GPU_vertformat_attr_add(
- &format, "pointcloud_radius", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
+ &format, "pointcloud_radius", GPU_COMP_F32, 1, GPU_FETCH_FLOAT);
}
GPU_VERTBUF_DISCARD_SAFE(cache->pos);
diff --git a/source/blender/editors/animation/anim_filter.c b/source/blender/editors/animation/anim_filter.c
index c054691ca38..4dc0bef3a1b 100644
--- a/source/blender/editors/animation/anim_filter.c
+++ b/source/blender/editors/animation/anim_filter.c
@@ -1840,7 +1840,6 @@ static size_t animdata_filter_gpencil(bAnimContext *ac,
bDopeSheet *ads = ac->ads;
size_t items = 0;
- Scene *scene = (Scene *)ads->source;
ViewLayer *view_layer = (ViewLayer *)ac->view_layer;
Base *base;
@@ -1881,13 +1880,12 @@ static size_t animdata_filter_gpencil(bAnimContext *ac,
}
}
- /* check selection and object type filters only for Object mode */
- if (ob->mode == OB_MODE_OBJECT) {
- if ((ads->filterflag & ADS_FILTER_ONLYSEL) && !((base->flag & BASE_SELECTED))) {
- /* only selected should be shown */
- continue;
- }
+ /* check selection and object type filters */
+ if ((ads->filterflag & ADS_FILTER_ONLYSEL) && !((base->flag & BASE_SELECTED))) {
+ /* only selected should be shown */
+ continue;
}
+
/* check if object belongs to the filtering group if option to filter
* objects by the grouped status is on
* - used to ease the process of doing multiple-character choreographies
diff --git a/source/blender/editors/armature/armature_select.c b/source/blender/editors/armature/armature_select.c
index 98f067af148..b946c19dbe5 100644
--- a/source/blender/editors/armature/armature_select.c
+++ b/source/blender/editors/armature/armature_select.c
@@ -741,10 +741,14 @@ cache_end:
else {
int bias_max = INT_MIN;
- /* Track cycle variables. */
+ /* Track Cycle Variables
+ * - Offset is always set to the active bone.
+ * - The object & bone indices subtracted by the 'offset.as_u32' value.
+ * Unsigned subtraction wrapping means we always select the next bone in the cycle.
+ */
struct {
union {
- uint32_t cmp;
+ uint32_t as_u32;
struct {
#ifdef __BIG_ENDIAN__
uint16_t ob;
@@ -753,17 +757,19 @@ cache_end:
uint16_t bone;
uint16_t ob;
#endif
- } index;
- } active, test, best;
+ };
+ } offset, test, best;
} cycle_order;
if (use_cycle) {
bArmature *arm = obedit_orig->data;
int ob_index = obedit_orig->runtime.select_id & 0xFFFF;
int bone_index = BLI_findindex(arm->edbo, ebone_active_orig);
- cycle_order.active.index.ob = ob_index;
- cycle_order.active.index.bone = bone_index;
- cycle_order.best.cmp = 0xffffffff;
+ /* Offset from the current active bone, so we cycle onto the next. */
+ cycle_order.offset.ob = ob_index;
+ cycle_order.offset.bone = bone_index;
+ /* The value of the active bone (with offset subtracted, a signal to always overwrite). */
+ cycle_order.best.as_u32 = 0;
}
for (int i = 0; i < hits; i++) {
@@ -823,25 +829,25 @@ cache_end:
/* Cycle selected items (objects & bones). */
if (use_cycle) {
- bool found = false;
- cycle_order.test.index.ob = hitresult & 0xFFFF;
- cycle_order.test.index.bone = (hitresult & ~BONESEL_ANY) >> 16;
+ cycle_order.test.ob = hitresult & 0xFFFF;
+ cycle_order.test.bone = (hitresult & ~BONESEL_ANY) >> 16;
if (ebone == ebone_active_orig) {
- BLI_assert(cycle_order.test.index.ob == cycle_order.active.index.ob);
- BLI_assert(cycle_order.test.index.bone == cycle_order.active.index.bone);
+ BLI_assert(cycle_order.test.ob == cycle_order.offset.ob);
+ BLI_assert(cycle_order.test.bone == cycle_order.offset.bone);
}
- cycle_order.test.cmp -= cycle_order.active.cmp;
-
- if (cycle_order.test.cmp < cycle_order.best.cmp && ebone != ebone_active_orig) {
- cycle_order.best.cmp = cycle_order.test.cmp;
- found = true;
- }
- else if (ELEM(result_cycle.ebone, NULL, ebone_active_orig)) {
- /* Let the active bone become selected, but don't set the cycle order. */
- found = true;
- }
-
- if (found) {
+ /* Subtraction as a single value is needed to support cycling through bones
+ * from multiple objects. So once the last bone is selected,
+ * the bits for the bone index wrap into the object,
+ * causing the next object to be stepped onto. */
+ cycle_order.test.as_u32 -= cycle_order.offset.as_u32;
+
+ /* Even though this logic avoids stepping onto the active bone,
+ * always set the 'best' value for the first time.
+ * Otherwise ensure the value is the smallest it can be,
+ * relative to the active bone, as long as it's not the active bone. */
+ if ((cycle_order.best.as_u32 == 0) ||
+ (cycle_order.test.as_u32 && (cycle_order.test.as_u32 < cycle_order.best.as_u32))) {
+ cycle_order.best = cycle_order.test;
result_cycle.hitresult = hitresult;
result_cycle.base = base;
result_cycle.ebone = ebone;
diff --git a/source/blender/editors/gpencil/annotate_draw.c b/source/blender/editors/gpencil/annotate_draw.c
index 26ba2661072..adaf4ab2459 100644
--- a/source/blender/editors/gpencil/annotate_draw.c
+++ b/source/blender/editors/gpencil/annotate_draw.c
@@ -328,13 +328,10 @@ static void annotation_draw_stroke_3d(
immUnbindProgram();
}
-/* ----- Fancy 2D-Stroke Drawing ------ */
-
-/* draw a given stroke in 2d */
+/* Draw a given stroke in 2d. */
static void annotation_draw_stroke_2d(const bGPDspoint *points,
int totpoints,
short thickness_s,
- short dflag,
short sflag,
int offsx,
int offsy,
@@ -342,167 +339,84 @@ static void annotation_draw_stroke_2d(const bGPDspoint *points,
int winy,
const float ink[4])
{
- /* otherwise thickness is twice that of the 3D view */
- float thickness = (float)thickness_s * 0.5f;
-
- /* strokes in Image Editor need a scale factor, since units there are not pixels! */
- float scalefac = 1.0f;
- if ((dflag & GP_DRAWDATA_IEDITHACK) && (dflag & GP_DRAWDATA_ONLYV2D)) {
- scalefac = 0.001f;
+ if (totpoints == 0) {
+ return;
}
+ float thickness = (float)thickness_s;
- /* Tessellation code - draw stroke as series of connected quads
- * (triangle strips in fact) with connection edges rotated to minimize shrinking artifacts,
- * and rounded end-caps.
- */
- {
- const bGPDspoint *pt1, *pt2;
- float s0[2], s1[2]; /* segment 'center' points */
- float pm[2]; /* normal from previous segment. */
- int i;
+ GPUVertFormat *format = immVertexFormat();
+ uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- GPUVertFormat *format = immVertexFormat();
- uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
+ const bGPDspoint *pt;
+ const bGPDspoint *pt_prev;
+ int draw_points = 0;
+ float co[2];
+ float oldpressure = points[0].pressure;
+ if (totpoints == 1) {
+ /* if drawing a single point, draw it larger */
+ GPU_point_size((float)(thickness + 2) * points->pressure);
+ immBindBuiltinProgram(GPU_SHADER_3D_POINT_UNIFORM_SIZE_UNIFORM_COLOR_AA);
+ immUniformColor3fvAlpha(ink, ink[3]);
+ immBegin(GPU_PRIM_POINTS, 1);
+
+ annotation_calc_2d_stroke_fxy(&points->x, sflag, offsx, offsy, winx, winy, co);
+ immVertex2fv(pos, co);
+ }
+ else {
+ /* draw stroke curve */
+ GPU_line_width(max_ff(oldpressure * thickness, 1.0));
immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
immUniformColor3fvAlpha(ink, ink[3]);
- immBegin(GPU_PRIM_TRI_STRIP, totpoints * 2 + 4);
-
- /* get x and y coordinates from first point */
- annotation_calc_2d_stroke_fxy(&points->x, sflag, offsx, offsy, winx, winy, s0);
-
- for (i = 0, pt1 = points, pt2 = points + 1; i < (totpoints - 1); i++, pt1++, pt2++) {
- float t0[2], t1[2]; /* tessellated coordinates */
- float m1[2], m2[2]; /* gradient and normal */
- float mt[2], sc[2]; /* gradient for thickness, point for end-cap */
- float pthick; /* thickness at segment point */
-
- /* Get x and y coordinates from point2
- * (point1 has already been computed in previous iteration). */
- annotation_calc_2d_stroke_fxy(&pt2->x, sflag, offsx, offsy, winx, winy, s1);
-
- /* calculate gradient and normal - 'angle'=(ny/nx) */
- m1[1] = s1[1] - s0[1];
- m1[0] = s1[0] - s0[0];
- normalize_v2(m1);
- m2[1] = -m1[0];
- m2[0] = m1[1];
-
- /* always use pressure from first point here */
- pthick = (pt1->pressure * thickness * scalefac);
-
- /* if the first segment, start of segment is segment's normal */
- if (i == 0) {
- /* draw start cap first
- * - make points slightly closer to center (about halfway across)
- */
- mt[0] = m2[0] * pthick * 0.5f;
- mt[1] = m2[1] * pthick * 0.5f;
- sc[0] = s0[0] - (m1[0] * pthick * 0.75f);
- sc[1] = s0[1] - (m1[1] * pthick * 0.75f);
-
- t0[0] = sc[0] - mt[0];
- t0[1] = sc[1] - mt[1];
- t1[0] = sc[0] + mt[0];
- t1[1] = sc[1] + mt[1];
-
- /* First two points of cap. */
- immVertex2fv(pos, t0);
- immVertex2fv(pos, t1);
-
- /* calculate points for start of segment */
- mt[0] = m2[0] * pthick;
- mt[1] = m2[1] * pthick;
-
- t0[0] = s0[0] - mt[0];
- t0[1] = s0[1] - mt[1];
- t1[0] = s0[0] + mt[0];
- t1[1] = s0[1] + mt[1];
-
- /* Last two points of start cap (and first two points of first segment). */
- immVertex2fv(pos, t0);
- immVertex2fv(pos, t1);
- }
- /* if not the first segment, use bisector of angle between segments */
- else {
- float mb[2]; /* bisector normal */
- float athick, dfac; /* actual thickness, difference between thicknesses */
-
- /* calculate gradient of bisector (as average of normals) */
- mb[0] = (pm[0] + m2[0]) / 2;
- mb[1] = (pm[1] + m2[1]) / 2;
- normalize_v2(mb);
-
- /* calculate gradient to apply
- * - as basis, use just pthick * bisector gradient
- * - if cross-section not as thick as it should be, add extra padding to fix it
- */
- mt[0] = mb[0] * pthick;
- mt[1] = mb[1] * pthick;
- athick = len_v2(mt);
- dfac = pthick - (athick * 2);
-
- if (((athick * 2.0f) < pthick) && (IS_EQF(athick, pthick) == 0)) {
- mt[0] += (mb[0] * dfac);
- mt[1] += (mb[1] * dfac);
+
+ immBeginAtMost(GPU_PRIM_LINE_STRIP, totpoints);
+
+ for (int i = 0; i < totpoints; i++) {
+ pt = &points[i];
+ /* If there was a significant pressure change,
+ * stop the curve, change the thickness of the stroke,
+ * and continue drawing again (since line-width cannot change in middle of GL_LINE_STRIP).
+ */
+ if (fabsf(pt->pressure - oldpressure) > 0.2f) {
+ /* need to have 2 points to avoid immEnd assert error */
+ if (draw_points < 2) {
+ pt_prev = &points[i - 1];
+ annotation_calc_2d_stroke_fxy(&pt_prev->x, sflag, offsx, offsy, winx, winy, co);
+ immVertex2fv(pos, co);
}
- /* calculate points for start of segment */
- t0[0] = s0[0] - mt[0];
- t0[1] = s0[1] - mt[1];
- t1[0] = s0[0] + mt[0];
- t1[1] = s0[1] + mt[1];
+ immEnd();
+ draw_points = 0;
- /* Last two points of previous segment, and first two points of current segment. */
- immVertex2fv(pos, t0);
- immVertex2fv(pos, t1);
- }
+ GPU_line_width(max_ff(pt->pressure * thickness, 1.0f));
+ immBeginAtMost(GPU_PRIM_LINE_STRIP, totpoints - i + 1);
+
+ /* need to roll-back one point to ensure that there are no gaps in the stroke */
+ if (i != 0) {
+ pt_prev = &points[i - 1];
+ annotation_calc_2d_stroke_fxy(&pt_prev->x, sflag, offsx, offsy, winx, winy, co);
+ immVertex2fv(pos, co);
+ draw_points++;
+ }
- /* if last segment, also draw end of segment (defined as segment's normal) */
- if (i == totpoints - 2) {
- /* for once, we use second point's pressure (otherwise it won't be drawn) */
- pthick = (pt2->pressure * thickness * scalefac);
-
- /* calculate points for end of segment */
- mt[0] = m2[0] * pthick;
- mt[1] = m2[1] * pthick;
-
- t0[0] = s1[0] - mt[0];
- t0[1] = s1[1] - mt[1];
- t1[0] = s1[0] + mt[0];
- t1[1] = s1[1] + mt[1];
-
- /* Last two points of last segment (and first two points of end cap). */
- immVertex2fv(pos, t0);
- immVertex2fv(pos, t1);
-
- /* draw end cap as last step
- * - make points slightly closer to center (about halfway across)
- */
- mt[0] = m2[0] * pthick * 0.5f;
- mt[1] = m2[1] * pthick * 0.5f;
- sc[0] = s1[0] + (m1[0] * pthick * 0.75f);
- sc[1] = s1[1] + (m1[1] * pthick * 0.75f);
-
- t0[0] = sc[0] - mt[0];
- t0[1] = sc[1] - mt[1];
- t1[0] = sc[0] + mt[0];
- t1[1] = sc[1] + mt[1];
-
- /* Last two points of end cap. */
- immVertex2fv(pos, t0);
- immVertex2fv(pos, t1);
+ oldpressure = pt->pressure; /* reset our threshold */
}
- /* store computed point2 coordinates as point1 ones of next segment. */
- copy_v2_v2(s0, s1);
- /* store stroke's 'natural' normal for next stroke to use */
- copy_v2_v2(pm, m2);
+ /* now the point we want */
+ annotation_calc_2d_stroke_fxy(&pt->x, sflag, offsx, offsy, winx, winy, co);
+ immVertex2fv(pos, co);
+ draw_points++;
+ }
+ /* need to have 2 points to avoid immEnd assert error */
+ if (draw_points < 2) {
+ pt_prev = &points[0];
+ annotation_calc_2d_stroke_fxy(&pt_prev->x, sflag, offsx, offsy, winx, winy, co);
+ immVertex2fv(pos, co);
}
-
- immEnd();
- immUnbindProgram();
}
+
+ immEnd();
+ immUnbindProgram();
}
/* ----- Strokes Drawing ------ */
@@ -601,16 +515,8 @@ static void annotation_draw_strokes(const bGPDframe *gpf,
gps->points, lthick, gps->flag, offsx, offsy, winx, winy, color);
}
else {
- annotation_draw_stroke_2d(gps->points,
- gps->totpoints,
- lthick,
- dflag,
- gps->flag,
- offsx,
- offsy,
- winx,
- winy,
- color);
+ annotation_draw_stroke_2d(
+ gps->points, gps->totpoints, lthick, gps->flag, offsx, offsy, winx, winy, color);
}
}
}
@@ -862,7 +768,8 @@ static void annotation_draw_data_layers(
annotation_draw_strokes(gpf, offsx, offsy, winx, winy, dflag, lthick, ink);
/* Draw verts of selected strokes:
- * - when doing OpenGL renders, we don't want to be showing these, as that ends up flickering
+ * - when doing OpenGL renders, we don't want to be showing these, as that ends up
+ * flickering
* - locked layers can't be edited, so there's no point showing these verts
* as they will have no bearings on what gets edited
* - only show when in editmode, since operators shouldn't work otherwise
@@ -1126,8 +1033,8 @@ void ED_annotation_draw_view2d(const bContext *C, bool onlyv2d)
}
}
-/* draw annotations sketches to specified 3d-view assuming that matrices are already set correctly
- * Note: this gets called twice - first time with only3d=true to draw 3d-strokes,
+/* draw annotations sketches to specified 3d-view assuming that matrices are already set
+ * correctly Note: this gets called twice - first time with only3d=true to draw 3d-strokes,
* second time with only3d=false for screen-aligned strokes */
void ED_annotation_draw_view3d(
Scene *scene, struct Depsgraph *depsgraph, View3D *v3d, ARegion *region, bool only3d)
@@ -1137,7 +1044,8 @@ void ED_annotation_draw_view3d(
int offsx, offsy, winx, winy;
/* check that we have grease-pencil stuff to draw */
- /* XXX: Hardcoded reference here may get out of sync if we change how we fetch annotation data */
+ /* XXX: Hardcoded reference here may get out of sync if we change how we fetch annotation data
+ */
bGPdata *gpd = scene->gpd;
if (gpd == NULL) {
return;
diff --git a/source/blender/editors/include/ED_sculpt.h b/source/blender/editors/include/ED_sculpt.h
index 26871cf8dd0..e61c7be5216 100644
--- a/source/blender/editors/include/ED_sculpt.h
+++ b/source/blender/editors/include/ED_sculpt.h
@@ -54,6 +54,11 @@ void ED_sculpt_undosys_type(struct UndoType *ut);
void ED_sculpt_undo_geometry_begin(struct Object *ob, const char *name);
void ED_sculpt_undo_geometry_end(struct Object *ob);
+/* Undo for changes happening on a base mesh for multires sculpting.
+ * if there is no multires sculpt active regular undo is used. */
+void ED_sculpt_undo_push_multires_mesh_begin(struct bContext *C, const char *str);
+void ED_sculpt_undo_push_multires_mesh_end(struct bContext *C, const char *str);
+
#ifdef __cplusplus
}
#endif
diff --git a/source/blender/editors/include/ED_util_imbuf.h b/source/blender/editors/include/ED_util_imbuf.h
new file mode 100644
index 00000000000..76171383b49
--- /dev/null
+++ b/source/blender/editors/include/ED_util_imbuf.h
@@ -0,0 +1,52 @@
+/*
+ * 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.
+ *
+ * The Original Code is Copyright (C) 2008 Blender Foundation.
+ * All rights reserved.
+ */
+
+/** \file
+ * \ingroup editors
+ */
+
+#ifndef __ED_UTIL_IMBUF_H__
+#define __ED_UTIL_IMBUF_H__
+
+#include "BLI_compiler_attrs.h"
+#include "BLI_sys_types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct ARegion;
+struct Main;
+struct bContext;
+struct wmEvent;
+struct wmOperator;
+
+/* ed_util_imbuf.c */
+void ED_imbuf_sample_draw(const struct bContext *C, struct ARegion *region, void *arg_info);
+void ED_imbuf_sample_exit(struct bContext *C, struct wmOperator *op);
+int ED_imbuf_sample_invoke(struct bContext *C, struct wmOperator *op, const struct wmEvent *event);
+int ED_imbuf_sample_modal(struct bContext *C, struct wmOperator *op, const struct wmEvent *event);
+void ED_imbuf_sample_cancel(struct bContext *C, struct wmOperator *op);
+bool ED_imbuf_sample_poll(struct bContext *C);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __ED_UTIL_IMBUF_H__ */
diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h
index eb134646649..774cf8c509f 100644
--- a/source/blender/editors/include/UI_interface.h
+++ b/source/blender/editors/include/UI_interface.h
@@ -49,6 +49,7 @@ struct PanelType;
struct PointerRNA;
struct PropertyRNA;
struct ReportList;
+struct ResultBLF;
struct ScrArea;
struct bContext;
struct bContextStore;
@@ -1576,6 +1577,7 @@ void UI_but_func_search_set(uiBut *but,
void *arg,
uiButSearchArgFreeFunc search_arg_free_func,
uiButHandleFunc bfunc,
+ const char *search_sep_string,
void *active);
/* height in pixels, it's using hardcoded values still */
int UI_searchbox_size_y(void);
@@ -2415,8 +2417,9 @@ void UI_fontstyle_draw_ex(const struct uiFontStyle *fs,
const uchar col[4],
const struct uiFontStyleDraw_Params *fs_params,
size_t len,
- float *r_xofs,
- float *r_yofs);
+ int *r_xofs,
+ int *r_yofs,
+ struct ResultBLF *r_info);
void UI_fontstyle_draw(const struct uiFontStyle *fs,
const struct rcti *rect,
const char *str,
diff --git a/source/blender/editors/interface/interface.c b/source/blender/editors/interface/interface.c
index 18666daa8b8..9ec660a9714 100644
--- a/source/blender/editors/interface/interface.c
+++ b/source/blender/editors/interface/interface.c
@@ -1299,9 +1299,8 @@ static bool ui_but_event_property_operator_string(const bContext *C,
}
else if (GS(id->name) == ID_SCE) {
if (RNA_struct_is_a(ptr->type, &RNA_ToolSettings)) {
- /* toolsettings property
- * NOTE: toolsettings is usually accessed directly (i.e. not through scene)
- */
+ /* Tool-settings property:
+ * NOTE: tool-settings is usually accessed directly (i.e. not through scene). */
data_path = RNA_path_from_ID_to_property(ptr, prop);
}
else {
@@ -6333,7 +6332,9 @@ uiBut *uiDefSearchBut(uiBlock *block,
/**
* \param search_func, bfunc: both get it as \a arg.
* \param arg: user value,
- * \param active: when set, button opens with this item visible and selected.
+ * \param active: when set, button opens with this item visible and selected.
+ * \param separator_string: when not NULL, this string is used as a separator,
+ * showing the icon and highlighted text after the last instance of this string.
*/
void UI_but_func_search_set(uiBut *but,
uiButSearchCreateFunc search_create_func,
@@ -6341,6 +6342,7 @@ void UI_but_func_search_set(uiBut *but,
void *arg,
uiButSearchArgFreeFunc search_arg_free_func,
uiButHandleFunc bfunc,
+ const char *search_sep_string,
void *active)
{
/* needed since callers don't have access to internal functions
@@ -6359,6 +6361,7 @@ void UI_but_func_search_set(uiBut *but,
but->search_arg = arg;
but->search_arg_free_func = search_arg_free_func;
+ but->search_sep_string = search_sep_string;
if (bfunc) {
#ifdef DEBUG
@@ -6468,6 +6471,7 @@ uiBut *uiDefSearchButO_ptr(uiBlock *block,
but,
NULL,
operator_enum_call_cb,
+ NULL,
NULL);
but->optype = ot;
diff --git a/source/blender/editors/interface/interface_intern.h b/source/blender/editors/interface/interface_intern.h
index 5573d9b2edb..9142aebd1ef 100644
--- a/source/blender/editors/interface/interface_intern.h
+++ b/source/blender/editors/interface/interface_intern.h
@@ -205,6 +205,7 @@ struct uiBut {
uiButSearchFunc search_func;
void *search_arg;
uiButSearchArgFreeFunc search_arg_free_func;
+ const char *search_sep_string;
uiButHandleRenameFunc rename_func;
void *rename_arg1;
@@ -851,7 +852,8 @@ void ui_draw_menu_item(const struct uiFontStyle *fstyle,
const char *name,
int iconid,
int state,
- bool use_sep);
+ bool use_sep,
+ int *r_name_width);
void ui_draw_preview_item(
const struct uiFontStyle *fstyle, rcti *rect, const char *name, int iconid, int state);
diff --git a/source/blender/editors/interface/interface_layout.c b/source/blender/editors/interface/interface_layout.c
index 92779c83d9a..cbea32f179a 100644
--- a/source/blender/editors/interface/interface_layout.c
+++ b/source/blender/editors/interface/interface_layout.c
@@ -2633,6 +2633,7 @@ void ui_but_add_search(
coll_search,
ui_rna_collection_search_free_cb,
NULL,
+ NULL,
NULL);
}
else if (but->type == UI_BTYPE_SEARCH_MENU) {
diff --git a/source/blender/editors/interface/interface_region_search.c b/source/blender/editors/interface/interface_region_search.c
index 48779fd86dc..e2c87891169 100644
--- a/source/blender/editors/interface/interface_region_search.c
+++ b/source/blender/editors/interface/interface_region_search.c
@@ -28,6 +28,7 @@
#include <stdlib.h>
#include <string.h>
+#include "DNA_ID.h"
#include "MEM_guardedalloc.h"
#include "DNA_userdef_types.h"
@@ -89,9 +90,14 @@ typedef struct uiSearchboxData {
bool noback;
/** draw thumbnail previews, rather than list */
bool preview;
- /** use the UI_SEP_CHAR char for splitting shortcuts (good for operators, bad for data) */
+ /** Use the #UI_SEP_CHAR char for splitting shortcuts (good for operators, bad for data). */
bool use_sep;
int prv_rows, prv_cols;
+ /**
+ * Show the active icon and text after the last instance of this string.
+ * Used so we can show leading text to menu items less prominently (not related to 'use_sep').
+ */
+ const char *sep_string;
} uiSearchboxData;
#define SEARCH_ITEMS 10
@@ -465,19 +471,50 @@ static void ui_searchbox_region_draw_cb(const bContext *C, ARegion *region)
}
}
else {
+ const int search_sep_len = data->sep_string ? strlen(data->sep_string) : 0;
/* draw items */
for (a = 0; a < data->items.totitem; a++) {
const int state = ((a == data->active) ? UI_ACTIVE : 0) | data->items.states[a];
+ char *name = data->items.names[a];
+ int icon = data->items.icons[a];
+ char *name_sep_test = NULL;
ui_searchbox_butrect(&rect, data, a);
/* widget itself */
- ui_draw_menu_item(&data->fstyle,
- &rect,
- data->items.names[a],
- data->items.icons[a],
- state,
- data->use_sep);
+ if ((search_sep_len == 0) ||
+ !(name_sep_test = strstr(data->items.names[a], data->sep_string))) {
+
+ /* Simple menu item. */
+ ui_draw_menu_item(&data->fstyle, &rect, name, icon, state, data->use_sep, NULL);
+ }
+ else {
+ /* Split menu item, faded text before the separator. */
+ char *name_sep = NULL;
+ do {
+ name_sep = name_sep_test;
+ name_sep_test = strstr(name_sep + search_sep_len, data->sep_string);
+ } while (name_sep_test != NULL);
+
+ name_sep += search_sep_len;
+ const char name_sep_prev = *name_sep;
+ *name_sep = '\0';
+ int name_width = 0;
+ ui_draw_menu_item(
+ &data->fstyle, &rect, name, 0, state | UI_BUT_INACTIVE, false, &name_width);
+ *name_sep = name_sep_prev;
+ rect.xmin += name_width;
+ rect.xmin += UI_UNIT_X / 4;
+
+ if (icon == ICON_BLANK1) {
+ icon = ICON_NONE;
+ rect.xmin -= UI_DPI_ICON_SIZE / 4;
+ }
+
+ /* The previous menu item draws the active selection. */
+ ui_draw_menu_item(
+ &data->fstyle, &rect, name_sep, icon, state & ~UI_ACTIVE, data->use_sep, NULL);
+ }
}
/* indicate more */
if (data->items.more) {
@@ -566,6 +603,7 @@ ARegion *ui_searchbox_create_generic(bContext *C, ARegion *butregion, uiBut *but
if (but->optype != NULL || (but->drawflag & UI_BUT_HAS_SHORTCUT) != 0) {
data->use_sep = true;
}
+ data->sep_string = but->search_sep_string;
/* compute position */
if (but->block->flag & UI_BLOCK_SEARCH_MENU) {
@@ -762,9 +800,10 @@ static void ui_searchbox_region_draw_cb__operator(const bContext *UNUSED(C), ARe
CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, text_pre),
data->items.icons[a],
state,
- false);
+ false,
+ NULL);
ui_draw_menu_item(
- &data->fstyle, &rect_post, data->items.names[a], 0, state, data->use_sep);
+ &data->fstyle, &rect_post, data->items.names[a], 0, state, data->use_sep, NULL);
}
}
/* indicate more */
diff --git a/source/blender/editors/interface/interface_style.c b/source/blender/editors/interface/interface_style.c
index 63fc7825b26..d14c9e69e55 100644
--- a/source/blender/editors/interface/interface_style.c
+++ b/source/blender/editors/interface/interface_style.c
@@ -147,8 +147,9 @@ void UI_fontstyle_draw_ex(const uiFontStyle *fs,
const uchar col[4],
const struct uiFontStyleDraw_Params *fs_params,
size_t len,
- float *r_xofs,
- float *r_yofs)
+ int *r_xofs,
+ int *r_yofs,
+ struct ResultBLF *r_info)
{
int xofs = 0, yofs;
int font_flag = BLF_CLIPPING;
@@ -196,7 +197,7 @@ void UI_fontstyle_draw_ex(const uiFontStyle *fs,
BLF_position(fs->uifont_id, rect->xmin + xofs, rect->ymin + yofs, 0.0f);
BLF_color4ubv(fs->uifont_id, col);
- BLF_draw(fs->uifont_id, str, len);
+ BLF_draw_ex(fs->uifont_id, str, len, r_info);
BLF_disable(fs->uifont_id, font_flag);
@@ -210,9 +211,9 @@ void UI_fontstyle_draw(const uiFontStyle *fs,
const uchar col[4],
const struct uiFontStyleDraw_Params *fs_params)
{
- float xofs, yofs;
+ int xofs, yofs;
- UI_fontstyle_draw_ex(fs, rect, str, col, fs_params, BLF_DRAW_STR_DUMMY_MAX, &xofs, &yofs);
+ UI_fontstyle_draw_ex(fs, rect, str, col, fs_params, BLF_DRAW_STR_DUMMY_MAX, &xofs, &yofs, NULL);
}
/* drawn same as above, but at 90 degree angle */
diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c
index 98d5bae0ff1..f730e4c0e52 100644
--- a/source/blender/editors/interface/interface_templates.c
+++ b/source/blender/editors/interface/interface_templates.c
@@ -287,8 +287,14 @@ static uiBlock *template_common_search_menu(const bContext *C,
0,
"");
}
- UI_but_func_search_set(
- but, ui_searchbox_create_generic, search_func, search_arg, NULL, handle_func, active_item);
+ UI_but_func_search_set(but,
+ ui_searchbox_create_generic,
+ search_func,
+ search_arg,
+ NULL,
+ handle_func,
+ NULL,
+ active_item);
UI_block_bounds_set_normal(block, 0.3f * U.widget_unit);
UI_block_direction_set(block, UI_DIR_DOWN);
@@ -6660,8 +6666,14 @@ static void operator_search_cb(const bContext *C,
void UI_but_func_operator_search(uiBut *but)
{
- UI_but_func_search_set(
- but, ui_searchbox_create_operator, operator_search_cb, NULL, false, operator_call_cb, NULL);
+ UI_but_func_search_set(but,
+ ui_searchbox_create_operator,
+ operator_search_cb,
+ NULL,
+ false,
+ operator_call_cb,
+ NULL,
+ NULL);
}
void uiTemplateOperatorSearch(uiLayout *layout)
@@ -6684,6 +6696,9 @@ void uiTemplateOperatorSearch(uiLayout *layout)
/** \name Menu Search Template
* \{ */
+/* Unicode arrow. */
+#define MENU_SEP "\xe2\x96\xb6"
+
struct MenuSearch_Parent {
struct MenuSearch_Parent *parent;
MenuType *parent_mt;
@@ -6952,7 +6967,7 @@ static struct MenuSearch_Data *menu_items_from_ui_create(bContext *C,
SPACE_MENU_MAP(SPACE_INFO, "INFO_MT_editor_menus");
SPACE_MENU_MAP(SPACE_SEQ, "SEQUENCER_MT_editor_menus");
SPACE_MENU_MAP(SPACE_TEXT, "TEXT_MT_editor_menus");
- SPACE_MENU_MAP(SPACE_ACTION, "ACTION_MT_editor_menus");
+ SPACE_MENU_MAP(SPACE_ACTION, "DOPESHEET_MT_editor_menus");
SPACE_MENU_MAP(SPACE_NLA, "NLA_MT_editor_menus");
SPACE_MENU_MAP(SPACE_NODE, "NODE_MT_editor_menus");
SPACE_MENU_MAP(SPACE_CONSOLE, "CONSOLE_MT_editor_menus");
@@ -7127,9 +7142,6 @@ static struct MenuSearch_Data *menu_items_from_ui_create(bContext *C,
/* NOTE: currently this builds the full path for each menu item,
* that could be moved into the parent menu. */
- /* Unicode arrow. */
-#define MENU_SEP "\xe2\x86\x92"
-
/* Set names as full paths. */
LISTBASE_FOREACH (struct MenuSearch_Item *, item, &data->items) {
if (item->menu_parent != NULL) {
@@ -7176,7 +7188,6 @@ static struct MenuSearch_Data *menu_items_from_ui_create(bContext *C,
BLI_dynstr_clear(dyn_str);
}
BLI_dynstr_free(dyn_str);
-#undef MENU_SEP
/* Finally sort menu items.
*
@@ -7309,6 +7320,7 @@ void UI_but_func_menu_search(uiBut *but)
data,
menu_items_from_ui_destroy,
menu_call_fn,
+ MENU_SEP,
NULL);
}
@@ -7326,6 +7338,8 @@ void uiTemplateMenuSearch(uiLayout *layout)
UI_but_func_menu_search(but);
}
+#undef MENU_SEP
+
/** \} */
/* -------------------------------------------------------------------- */
diff --git a/source/blender/editors/interface/interface_utils.c b/source/blender/editors/interface/interface_utils.c
index 2d687781b61..79a90d27373 100644
--- a/source/blender/editors/interface/interface_utils.c
+++ b/source/blender/editors/interface/interface_utils.c
@@ -36,6 +36,7 @@
#include "BLT_translation.h"
+#include "BKE_lib_id.h"
#include "BKE_report.h"
#include "MEM_guardedalloc.h"
@@ -395,11 +396,12 @@ void ui_rna_collection_search_cb(const struct bContext *C,
uiSearchItems *items)
{
uiRNACollectionSearch *data = arg;
- char *name;
int i = 0, iconid = 0, flag = RNA_property_flag(data->target_prop);
ListBase *items_list = MEM_callocN(sizeof(ListBase), "items_list");
CollItemSearch *cis;
const bool skip_filter = data->search_but && !data->search_but->changed;
+ char name_buf[UI_MAX_DRAW_STR];
+ char *name;
/* build a temporary list of relevant items first */
RNA_PROP_BEGIN (&data->search_ptr, itemptr, data->search_prop) {
@@ -417,24 +419,31 @@ void ui_rna_collection_search_cb(const struct bContext *C,
}
}
- /* Could use the string length here. */
- name = RNA_struct_name_get_alloc(&itemptr, NULL, 0, NULL);
-
iconid = 0;
if (itemptr.type && RNA_struct_is_ID(itemptr.type)) {
iconid = ui_id_icon_get(C, itemptr.data, false);
+
+ BKE_id_full_name_ui_prefix_get(name_buf, itemptr.data);
+ BLI_STATIC_ASSERT(sizeof(name_buf) >= MAX_ID_FULL_NAME_UI,
+ "Name string buffer should be big enough to hold full UI ID name");
+ name = name_buf;
+ }
+ else {
+ name = RNA_struct_name_get_alloc(&itemptr, name_buf, sizeof(name_buf), NULL);
}
if (name) {
if (skip_filter || BLI_strcasestr(name, str)) {
cis = MEM_callocN(sizeof(CollItemSearch), "CollectionItemSearch");
cis->data = itemptr.data;
- cis->name = MEM_dupallocN(name);
+ cis->name = BLI_strdup(name);
cis->index = i;
cis->iconid = iconid;
BLI_addtail(items_list, cis);
}
- MEM_freeN(name);
+ if (name != name_buf) {
+ MEM_freeN(name);
+ }
}
i++;
diff --git a/source/blender/editors/interface/interface_widgets.c b/source/blender/editors/interface/interface_widgets.c
index fa4a0a1e07d..86a5fd5dabb 100644
--- a/source/blender/editors/interface/interface_widgets.c
+++ b/source/blender/editors/interface/interface_widgets.c
@@ -2300,7 +2300,7 @@ static void widget_draw_text(const uiFontStyle *fstyle,
if (!use_right_only) {
/* for underline drawing */
- float font_xofs, font_yofs;
+ int font_xofs, font_yofs;
int drawlen = (drawstr_left_len == INT_MAX) ? strlen(drawstr + but->ofs) :
(drawstr_left_len - but->ofs);
@@ -2315,7 +2315,8 @@ static void widget_draw_text(const uiFontStyle *fstyle,
},
drawlen,
&font_xofs,
- &font_yofs);
+ &font_yofs,
+ NULL);
if (but->menu_key != '\0') {
char fixedbuf[128];
@@ -2342,7 +2343,7 @@ static void widget_draw_text(const uiFontStyle *fstyle,
ul_advance = BLF_width(fstyle->uifont_id, fixedbuf, ul_index) + (1.0f * UI_DPI_FAC);
BLF_position(fstyle->uifont_id,
- rect->xmin + font_xofs + ul_advance,
+ rect->xmin + font_xofs + (int)ul_advance,
rect->ymin + font_yofs,
0.0f);
BLF_color4ubv(fstyle->uifont_id, wcol->text);
@@ -5267,8 +5268,13 @@ void ui_draw_tooltip_background(const uiStyle *UNUSED(style), uiBlock *UNUSED(bl
/* helper call to draw a menu item without button */
/* state: UI_ACTIVE or 0 */
-void ui_draw_menu_item(
- const uiFontStyle *fstyle, rcti *rect, const char *name, int iconid, int state, bool use_sep)
+void ui_draw_menu_item(const uiFontStyle *fstyle,
+ rcti *rect,
+ const char *name,
+ int iconid,
+ int state,
+ bool use_sep,
+ int *r_name_width)
{
uiWidgetType *wt = widget_type(UI_WTYPE_MENU_ITEM);
rcti _rect = *rect;
@@ -5318,13 +5324,22 @@ void ui_draw_menu_item(
UI_text_clip_middle_ex(fstyle, drawstr, okwidth, minwidth, max_len, '\0');
}
- UI_fontstyle_draw(fstyle,
- rect,
- drawstr,
- wt->wcol.text,
- &(struct uiFontStyleDraw_Params){
- .align = UI_STYLE_TEXT_LEFT,
- });
+ int xofs = 0, yofs = 0;
+ struct ResultBLF info;
+ UI_fontstyle_draw_ex(fstyle,
+ rect,
+ drawstr,
+ wt->wcol.text,
+ &(struct uiFontStyleDraw_Params){
+ .align = UI_STYLE_TEXT_LEFT,
+ },
+ BLF_DRAW_STR_DUMMY_MAX,
+ &xofs,
+ &yofs,
+ &info);
+ if (r_name_width != NULL) {
+ *r_name_width = xofs + info.width;
+ }
}
/* part text right aligned */
diff --git a/source/blender/editors/mesh/editmesh_undo.c b/source/blender/editors/mesh/editmesh_undo.c
index 1f857ad4710..48ec41027ff 100644
--- a/source/blender/editors/mesh/editmesh_undo.c
+++ b/source/blender/editors/mesh/editmesh_undo.c
@@ -542,15 +542,15 @@ static void *undomesh_from_editmesh(UndoMesh *um, BMEditMesh *em, Key *key)
# ifdef USE_ARRAY_STORE_THREAD
if (um_arraystore.task_pool == NULL) {
TaskScheduler *scheduler = BLI_task_scheduler_get();
- um_arraystore.task_pool = BLI_task_pool_create_background(scheduler, NULL);
+ um_arraystore.task_pool = BLI_task_pool_create_background(
+ scheduler, NULL, TASK_PRIORITY_LOW);
}
struct UMArrayData *um_data = MEM_mallocN(sizeof(*um_data), __func__);
um_data->um = um;
um_data->um_ref = um_ref;
- BLI_task_pool_push(
- um_arraystore.task_pool, um_arraystore_compact_cb, um_data, true, TASK_PRIORITY_LOW);
+ BLI_task_pool_push(um_arraystore.task_pool, um_arraystore_compact_cb, um_data, true, NULL);
# else
um_arraystore_compact_with_info(um, um_ref);
# endif
diff --git a/source/blender/editors/object/object_modifier.c b/source/blender/editors/object/object_modifier.c
index a050b71325b..04dbc60b7b7 100644
--- a/source/blender/editors/object/object_modifier.c
+++ b/source/blender/editors/object/object_modifier.c
@@ -86,6 +86,7 @@
#include "ED_mesh.h"
#include "ED_object.h"
#include "ED_screen.h"
+#include "ED_sculpt.h"
#include "WM_api.h"
#include "WM_types.h"
@@ -1699,8 +1700,12 @@ static int multires_base_apply_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
+ ED_sculpt_undo_push_multires_mesh_begin(C, op->type->name);
+
multiresModifier_base_apply(depsgraph, object, mmd);
+ ED_sculpt_undo_push_multires_mesh_end(C, op->type->name);
+
DEG_id_tag_update(&object->id, ID_RECALC_GEOMETRY);
WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, object);
@@ -1728,7 +1733,7 @@ void OBJECT_OT_multires_base_apply(wmOperatorType *ot)
ot->exec = multires_base_apply_exec;
/* flags */
- ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
+ ot->flag = OPTYPE_REGISTER | OPTYPE_INTERNAL;
edit_modifier_properties(ot);
}
diff --git a/source/blender/editors/object/object_volume.c b/source/blender/editors/object/object_volume.c
index bb619972e80..3c1f7da2bd6 100644
--- a/source/blender/editors/object/object_volume.c
+++ b/source/blender/editors/object/object_volume.c
@@ -109,11 +109,6 @@ static int volume_import_exec(bContext *C, wmOperator *op)
BLI_path_rel(volume->filepath, BKE_main_blendfile_path(bmain));
}
- volume->is_sequence = (range->length > 1);
- volume->frame_duration = (volume->is_sequence) ? range->length : 0;
- volume->frame_start = 1;
- volume->frame_offset = (volume->is_sequence) ? range->offset - 1 : 0;
-
if (!BKE_volume_load(volume, bmain)) {
BKE_reportf(op->reports,
RPT_WARNING,
@@ -134,10 +129,19 @@ static int volume_import_exec(bContext *C, wmOperator *op)
continue;
}
+ /* Set sequence parameters after trying to load the first frame, for file validation we want
+ * to use a consistent frame rather than whatever corresponds to the current scene frame. */
+ volume->is_sequence = (range->length > 1);
+ volume->frame_duration = (volume->is_sequence) ? range->length : 0;
+ volume->frame_start = 1;
+ volume->frame_offset = (volume->is_sequence) ? range->offset - 1 : 0;
+
if (BKE_volume_is_y_up(volume)) {
object->rot[0] += M_PI_2;
}
+ BKE_volume_unload(volume);
+
imported = true;
}
BLI_freelistN(&ranges);
diff --git a/source/blender/editors/render/render_opengl.c b/source/blender/editors/render/render_opengl.c
index c23ce8a8a9e..f35f92feaec 100644
--- a/source/blender/editors/render/render_opengl.c
+++ b/source/blender/editors/render/render_opengl.c
@@ -859,11 +859,12 @@ static bool screen_opengl_render_init(bContext *C, wmOperator *op)
if (BKE_imtype_is_movie(scene->r.im_format.imtype)) {
task_scheduler = BLI_task_scheduler_create(1);
oglrender->task_scheduler = task_scheduler;
- oglrender->task_pool = BLI_task_pool_create_background(task_scheduler, oglrender);
+ oglrender->task_pool = BLI_task_pool_create_background(
+ task_scheduler, oglrender, TASK_PRIORITY_LOW);
}
else {
oglrender->task_scheduler = NULL;
- oglrender->task_pool = BLI_task_pool_create(task_scheduler, oglrender);
+ oglrender->task_pool = BLI_task_pool_create(task_scheduler, oglrender, TASK_PRIORITY_LOW);
}
oglrender->pool_ok = true;
BLI_spin_init(&oglrender->reports_lock);
@@ -1123,7 +1124,7 @@ static bool schedule_write_result(OGLRender *oglrender, RenderResult *rr)
BLI_condition_wait(&oglrender->task_condition, &oglrender->task_mutex);
}
BLI_mutex_unlock(&oglrender->task_mutex);
- BLI_task_pool_push(oglrender->task_pool, write_result_func, task_data, true, TASK_PRIORITY_LOW);
+ BLI_task_pool_push(oglrender->task_pool, write_result_func, task_data, true, NULL);
return true;
}
diff --git a/source/blender/editors/sculpt_paint/paint_image.c b/source/blender/editors/sculpt_paint/paint_image.c
index 4d4af028570..1a2b44a03b4 100644
--- a/source/blender/editors/sculpt_paint/paint_image.c
+++ b/source/blender/editors/sculpt_paint/paint_image.c
@@ -1288,9 +1288,9 @@ static bool brush_colors_flip_poll(bContext *C)
void PAINT_OT_brush_colors_flip(wmOperatorType *ot)
{
/* identifiers */
- ot->name = "Brush Colors Flip";
+ ot->name = "Swap Colors";
ot->idname = "PAINT_OT_brush_colors_flip";
- ot->description = "Toggle foreground and background brush colors";
+ ot->description = "Swap primary and secondary brush colors";
/* api callbacks */
ot->exec = brush_colors_flip_exec;
diff --git a/source/blender/editors/sculpt_paint/paint_image_proj.c b/source/blender/editors/sculpt_paint/paint_image_proj.c
index 0b99806109a..6af9ec01fc3 100644
--- a/source/blender/editors/sculpt_paint/paint_image_proj.c
+++ b/source/blender/editors/sculpt_paint/paint_image_proj.c
@@ -5617,7 +5617,7 @@ static bool project_paint_op(void *state, const float lastpos[2], const float po
if (ps->thread_tot > 1) {
scheduler = BLI_task_scheduler_get();
- task_pool = BLI_task_pool_create_suspended(scheduler, NULL);
+ task_pool = BLI_task_pool_create_suspended(scheduler, NULL, TASK_PRIORITY_HIGH);
}
image_pool = BKE_image_pool_new();
@@ -5652,8 +5652,7 @@ static bool project_paint_op(void *state, const float lastpos[2], const float po
handles[a].pool = image_pool;
if (task_pool != NULL) {
- BLI_task_pool_push(
- task_pool, do_projectpaint_thread, &handles[a], false, TASK_PRIORITY_HIGH);
+ BLI_task_pool_push(task_pool, do_projectpaint_thread, &handles[a], false, NULL);
}
}
diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.h b/source/blender/editors/sculpt_paint/sculpt_intern.h
index e2fd54596e7..b379c1ab8af 100644
--- a/source/blender/editors/sculpt_paint/sculpt_intern.h
+++ b/source/blender/editors/sculpt_paint/sculpt_intern.h
@@ -478,6 +478,7 @@ typedef struct SculptUndoNode {
* the object when undoing the operation
*
* Modified geometry is stored after the modification and is used to redo the modification. */
+ bool geometry_clear_pbvh;
SculptUndoNodeGeometry geometry_original;
SculptUndoNodeGeometry geometry_modified;
diff --git a/source/blender/editors/sculpt_paint/sculpt_undo.c b/source/blender/editors/sculpt_paint/sculpt_undo.c
index 340f7191b95..c07ebf790d4 100644
--- a/source/blender/editors/sculpt_paint/sculpt_undo.c
+++ b/source/blender/editors/sculpt_paint/sculpt_undo.c
@@ -55,6 +55,9 @@
#include "BKE_subsurf.h"
#include "BKE_undo_system.h"
+// XXX: Ideally should be no direct call to such low level things.
+#include "BKE_subdiv_eval.h"
+
#include "DEG_depsgraph.h"
#include "WM_api.h"
@@ -552,7 +555,9 @@ static void sculpt_undo_geometry_free_data(SculptUndoNodeGeometry *geometry)
static void sculpt_undo_geometry_restore(SculptUndoNode *unode, Object *object)
{
- SCULPT_pbvh_clear(object);
+ if (unode->geometry_clear_pbvh) {
+ SCULPT_pbvh_clear(object);
+ }
if (unode->applied) {
sculpt_undo_geometry_restore_data(&unode->geometry_modified, object);
@@ -740,6 +745,10 @@ static void sculpt_undo_restore_list(bContext *C, Depsgraph *depsgraph, ListBase
}
}
+ if (subdiv_ccg != NULL) {
+ BKE_subdiv_eval_refine_from_mesh(subdiv_ccg->subdiv, ob->data, NULL);
+ }
+
if (update || rebuild) {
bool tag_update = false;
/* We update all nodes still, should be more clever, but also
@@ -1079,6 +1088,7 @@ static SculptUndoNode *sculpt_undo_geometry_push(Object *object, SculptUndoType
{
SculptUndoNode *unode = sculpt_undo_find_or_alloc_node_type(object, type);
unode->applied = false;
+ unode->geometry_clear_pbvh = true;
SculptUndoNodeGeometry *geometry = sculpt_undo_geometry_get(unode);
sculpt_undo_geometry_store_data(geometry, object);
@@ -1510,3 +1520,85 @@ static UndoSculpt *sculpt_undo_get_nodes(void)
}
/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Undo for changes happening on a base mesh for multires sculpting.
+ *
+ * Use this for multires operators which changes base mesh and which are to be
+ * possible. Example of such operators is Apply Base.
+ *
+ * Usage:
+ *
+ * static int operator_exec((bContext *C, wmOperator *op) {
+ *
+ * ED_sculpt_undo_push_mixed_begin(C, op->type->name);
+ * // Modify base mesh.
+ * ED_sculpt_undo_push_mixed_end(C, op->type->name);
+ *
+ * return OPERATOR_FINISHED;
+ * }
+ *
+ * If object is not in sculpt mode or sculpt does not happen on multires then
+ * regular ED_undo_push() is used.
+ * *
+ * \{ */
+
+static bool sculpt_undo_use_multires_mesh(bContext *C)
+{
+ if (BKE_paintmode_get_active_from_context(C) != PAINT_MODE_SCULPT) {
+ return false;
+ }
+
+ Object *object = CTX_data_active_object(C);
+ SculptSession *sculpt_session = object->sculpt;
+
+ return sculpt_session->multires.active;
+}
+
+static void sculpt_undo_push_all_grids(Object *object)
+{
+ SculptSession *ss = object->sculpt;
+ PBVHNode **nodes;
+ int totnodes;
+
+ BKE_pbvh_search_gather(ss->pbvh, NULL, NULL, &nodes, &totnodes);
+ for (int i = 0; i < totnodes; i++) {
+ SculptUndoNode *unode = SCULPT_undo_push_node(object, nodes[i], SCULPT_UNDO_COORDS);
+ unode->node = NULL;
+ }
+
+ MEM_SAFE_FREE(nodes);
+}
+
+void ED_sculpt_undo_push_multires_mesh_begin(bContext *C, const char *str)
+{
+ if (!sculpt_undo_use_multires_mesh(C)) {
+ return;
+ }
+
+ Object *object = CTX_data_active_object(C);
+
+ SCULPT_undo_push_begin(str);
+
+ SculptUndoNode *geometry_unode = SCULPT_undo_push_node(object, NULL, SCULPT_UNDO_GEOMETRY);
+ geometry_unode->geometry_clear_pbvh = false;
+
+ sculpt_undo_push_all_grids(object);
+}
+
+void ED_sculpt_undo_push_multires_mesh_end(bContext *C, const char *str)
+{
+ if (!sculpt_undo_use_multires_mesh(C)) {
+ ED_undo_push(C, str);
+ return;
+ }
+
+ Object *object = CTX_data_active_object(C);
+
+ SculptUndoNode *geometry_unode = SCULPT_undo_push_node(object, NULL, SCULPT_UNDO_GEOMETRY);
+ geometry_unode->geometry_clear_pbvh = false;
+
+ SCULPT_undo_push_end();
+}
+
+/** \} */
diff --git a/source/blender/editors/space_clip/clip_editor.c b/source/blender/editors/space_clip/clip_editor.c
index ffc50407917..cce01947ab7 100644
--- a/source/blender/editors/space_clip/clip_editor.c
+++ b/source/blender/editors/space_clip/clip_editor.c
@@ -961,9 +961,9 @@ static void start_prefetch_threads(MovieClip *clip,
queue.do_update = do_update;
queue.progress = progress;
- task_pool = BLI_task_pool_create(task_scheduler, &queue);
+ task_pool = BLI_task_pool_create(task_scheduler, &queue, TASK_PRIORITY_LOW);
for (i = 0; i < tot_thread; i++) {
- BLI_task_pool_push(task_pool, prefetch_task_func, clip, false, TASK_PRIORITY_LOW);
+ BLI_task_pool_push(task_pool, prefetch_task_func, clip, false, NULL);
}
BLI_task_pool_work_and_wait(task_pool);
BLI_task_pool_free(task_pool);
diff --git a/source/blender/editors/space_clip/clip_ops.c b/source/blender/editors/space_clip/clip_ops.c
index 266212fdfd7..3204374b747 100644
--- a/source/blender/editors/space_clip/clip_ops.c
+++ b/source/blender/editors/space_clip/clip_ops.c
@@ -1434,7 +1434,7 @@ static void do_sequence_proxy(void *pjv,
queue.do_update = do_update;
queue.progress = progress;
- task_pool = BLI_task_pool_create(task_scheduler, &queue);
+ task_pool = BLI_task_pool_create(task_scheduler, &queue, TASK_PRIORITY_LOW);
handles = MEM_callocN(sizeof(ProxyThread) * tot_thread, "proxy threaded handles");
for (i = 0; i < tot_thread; i++) {
ProxyThread *handle = &handles[i];
@@ -1451,7 +1451,7 @@ static void do_sequence_proxy(void *pjv,
handle->distortion = BKE_tracking_distortion_new(&clip->tracking, width, height);
}
- BLI_task_pool_push(task_pool, proxy_task_func, handle, false, TASK_PRIORITY_LOW);
+ BLI_task_pool_push(task_pool, proxy_task_func, handle, false, NULL);
}
BLI_task_pool_work_and_wait(task_pool);
diff --git a/source/blender/editors/space_file/filelist.c b/source/blender/editors/space_file/filelist.c
index 778c27e1a17..ac8fa413f07 100644
--- a/source/blender/editors/space_file/filelist.c
+++ b/source/blender/editors/space_file/filelist.c
@@ -1329,7 +1329,7 @@ static void filelist_cache_preview_ensure_running(FileListEntryCache *cache)
if (!cache->previews_pool) {
TaskScheduler *scheduler = BLI_task_scheduler_get();
- cache->previews_pool = BLI_task_pool_create_background(scheduler, cache);
+ cache->previews_pool = BLI_task_pool_create_background(scheduler, cache, TASK_PRIORITY_LOW);
cache->previews_done = BLI_thread_queue_init();
IMB_thumb_locks_acquire();
@@ -1393,12 +1393,11 @@ static void filelist_cache_previews_push(FileList *filelist, FileDirEntry *entry
FileListEntryPreviewTaskData *preview_taskdata = MEM_mallocN(sizeof(*preview_taskdata),
__func__);
preview_taskdata->preview = preview;
- BLI_task_pool_push_ex(cache->previews_pool,
- filelist_cache_preview_runf,
- preview_taskdata,
- true,
- filelist_cache_preview_freef,
- TASK_PRIORITY_LOW);
+ BLI_task_pool_push(cache->previews_pool,
+ filelist_cache_preview_runf,
+ preview_taskdata,
+ true,
+ filelist_cache_preview_freef);
}
}
diff --git a/source/blender/editors/space_image/image_ops.c b/source/blender/editors/space_image/image_ops.c
index 14245327bdd..992727e3b11 100644
--- a/source/blender/editors/space_image/image_ops.c
+++ b/source/blender/editors/space_image/image_ops.c
@@ -91,6 +91,7 @@
#include "ED_screen.h"
#include "ED_space_api.h"
#include "ED_util.h"
+#include "ED_util_imbuf.h"
#include "ED_uvedit.h"
#include "UI_interface.h"
@@ -277,28 +278,6 @@ static bool space_image_main_area_not_uv_brush_poll(bContext *C)
return 0;
}
-static bool image_sample_poll(bContext *C)
-{
- SpaceImage *sima = CTX_wm_space_image(C);
- if (sima == NULL) {
- return false;
- }
-
- Object *obedit = CTX_data_edit_object(C);
- if (obedit) {
- /* Disable when UV editing so it doesn't swallow all click events
- * (use for setting cursor). */
- if (ED_space_image_show_uvedit(sima, obedit)) {
- return false;
- }
- }
- else if (sima->mode != SI_MODE_VIEW) {
- return false;
- }
-
- return true;
-}
-
/** \} */
/* -------------------------------------------------------------------- */
@@ -3099,85 +3078,6 @@ void IMAGE_OT_unpack(wmOperatorType *ot)
/** \name Sample Image Operator
* \{ */
-typedef struct ImageSampleInfo {
- ARegionType *art;
- void *draw_handle;
- int x, y;
- int channels;
-
- int width, height;
- int sample_size;
-
- uchar col[4];
- float colf[4];
- float linearcol[4];
- int z;
- float zf;
-
- uchar *colp;
- const float *colfp;
- int *zp;
- float *zfp;
-
- bool draw;
- bool color_manage;
- int use_default_view;
-} ImageSampleInfo;
-
-static void image_sample_draw(const bContext *C, ARegion *region, void *arg_info)
-{
- ImageSampleInfo *info = arg_info;
- if (!info->draw) {
- return;
- }
-
- Scene *scene = CTX_data_scene(C);
- ED_image_draw_info(scene,
- region,
- info->color_manage,
- info->use_default_view,
- info->channels,
- info->x,
- info->y,
- info->colp,
- info->colfp,
- info->linearcol,
- info->zp,
- info->zfp);
-
- if (info->sample_size > 1) {
- const wmWindow *win = CTX_wm_window(C);
- const wmEvent *event = win->eventstate;
-
- SpaceImage *sima = CTX_wm_space_image(C);
- GPUVertFormat *format = immVertexFormat();
- uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
-
- const float color[3] = {1, 1, 1};
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
- immUniformColor3fv(color);
-
- /* TODO(campbell): lock to pixels. */
- rctf sample_rect_fl;
- BLI_rctf_init_pt_radius(
- &sample_rect_fl,
- (float[2]){event->x - region->winrct.xmin, event->y - region->winrct.ymin},
- (float)(info->sample_size / 2.0f) * sima->zoom);
-
- glEnable(GL_COLOR_LOGIC_OP);
- glLogicOp(GL_XOR);
- GPU_line_width(1.0f);
- imm_draw_box_wire_2d(pos,
- (float)sample_rect_fl.xmin,
- (float)sample_rect_fl.ymin,
- (float)sample_rect_fl.xmax,
- (float)sample_rect_fl.ymax);
- glDisable(GL_COLOR_LOGIC_OP);
-
- immUnbindProgram();
- }
-}
-
/* Returns color in linear space, matching ED_space_node_color_sample(). */
bool ED_space_image_color_sample(SpaceImage *sima, ARegion *region, int mval[2], float r_col[3])
{
@@ -3222,279 +3122,6 @@ bool ED_space_image_color_sample(SpaceImage *sima, ARegion *region, int mval[2],
return ret;
}
-/* -------------------------------------------------------------------- */
-/** \name Image Pixel Sample
- * \{ */
-
-static void image_sample_pixel_color_ubyte(const ImBuf *ibuf,
- const int coord[2],
- uchar r_col[4],
- float r_col_linear[4])
-{
- const uchar *cp = (uchar *)(ibuf->rect + coord[1] * ibuf->x + coord[0]);
- copy_v4_v4_uchar(r_col, cp);
- rgba_uchar_to_float(r_col_linear, r_col);
- IMB_colormanagement_colorspace_to_scene_linear_v4(r_col_linear, false, ibuf->rect_colorspace);
-}
-
-static void image_sample_pixel_color_float(ImBuf *ibuf, const int coord[2], float r_col[4])
-{
- const float *cp = ibuf->rect_float + (ibuf->channels) * (coord[1] * ibuf->x + coord[0]);
- copy_v4_v4(r_col, cp);
-}
-
-/** \} */
-
-/* -------------------------------------------------------------------- */
-/** \name Image Pixel Region Sample
- * \{ */
-
-static void image_sample_rect_color_ubyte(const ImBuf *ibuf,
- const rcti *rect,
- uchar r_col[4],
- float r_col_linear[4])
-{
- uint col_accum_ub[4] = {0, 0, 0, 0};
- zero_v4(r_col_linear);
- int col_tot = 0;
- int coord[2];
- for (coord[0] = rect->xmin; coord[0] <= rect->xmax; coord[0]++) {
- for (coord[1] = rect->ymin; coord[1] <= rect->ymax; coord[1]++) {
- float col_temp_fl[4];
- uchar col_temp_ub[4];
- image_sample_pixel_color_ubyte(ibuf, coord, col_temp_ub, col_temp_fl);
- add_v4_v4(r_col_linear, col_temp_fl);
- col_accum_ub[0] += (uint)col_temp_ub[0];
- col_accum_ub[1] += (uint)col_temp_ub[1];
- col_accum_ub[2] += (uint)col_temp_ub[2];
- col_accum_ub[3] += (uint)col_temp_ub[3];
- col_tot += 1;
- }
- }
- mul_v4_fl(r_col_linear, 1.0 / (float)col_tot);
-
- r_col[0] = MIN2(col_accum_ub[0] / col_tot, 255);
- r_col[1] = MIN2(col_accum_ub[1] / col_tot, 255);
- r_col[2] = MIN2(col_accum_ub[2] / col_tot, 255);
- r_col[3] = MIN2(col_accum_ub[3] / col_tot, 255);
-}
-
-static void image_sample_rect_color_float(ImBuf *ibuf, const rcti *rect, float r_col[4])
-{
- zero_v4(r_col);
- int col_tot = 0;
- int coord[2];
- for (coord[0] = rect->xmin; coord[0] <= rect->xmax; coord[0]++) {
- for (coord[1] = rect->ymin; coord[1] <= rect->ymax; coord[1]++) {
- float col_temp_fl[4];
- image_sample_pixel_color_float(ibuf, coord, col_temp_fl);
- add_v4_v4(r_col, col_temp_fl);
- col_tot += 1;
- }
- }
- mul_v4_fl(r_col, 1.0 / (float)col_tot);
-}
-
-/** \} */
-
-static void image_sample_apply(bContext *C, wmOperator *op, const wmEvent *event)
-{
- SpaceImage *sima = CTX_wm_space_image(C);
- ARegion *region = CTX_wm_region(C);
- Image *image = ED_space_image(sima);
-
- float uv[2];
- UI_view2d_region_to_view(&region->v2d, event->mval[0], event->mval[1], &uv[0], &uv[1]);
- int tile = BKE_image_get_tile_from_pos(sima->image, uv, uv, NULL);
-
- void *lock;
- ImBuf *ibuf = ED_space_image_acquire_buffer(sima, &lock, tile);
- ImageSampleInfo *info = op->customdata;
- Scene *scene = CTX_data_scene(C);
- CurveMapping *curve_mapping = scene->view_settings.curve_mapping;
-
- if (ibuf == NULL) {
- ED_space_image_release_buffer(sima, ibuf, lock);
- info->draw = false;
- return;
- }
-
- if (uv[0] >= 0.0f && uv[1] >= 0.0f && uv[0] < 1.0f && uv[1] < 1.0f) {
- int x = (int)(uv[0] * ibuf->x), y = (int)(uv[1] * ibuf->y);
-
- CLAMP(x, 0, ibuf->x - 1);
- CLAMP(y, 0, ibuf->y - 1);
-
- info->width = ibuf->x;
- info->height = ibuf->y;
- info->x = x;
- info->y = y;
-
- info->draw = true;
- info->channels = ibuf->channels;
-
- info->colp = NULL;
- info->colfp = NULL;
- info->zp = NULL;
- info->zfp = NULL;
-
- info->use_default_view = (image->flag & IMA_VIEW_AS_RENDER) ? false : true;
-
- rcti sample_rect;
- sample_rect.xmin = max_ii(0, x - info->sample_size / 2);
- sample_rect.ymin = max_ii(0, y - info->sample_size / 2);
- sample_rect.xmax = min_ii(ibuf->x, sample_rect.xmin + info->sample_size) - 1;
- sample_rect.ymax = min_ii(ibuf->y, sample_rect.ymin + info->sample_size) - 1;
-
- if (ibuf->rect) {
- image_sample_rect_color_ubyte(ibuf, &sample_rect, info->col, info->linearcol);
- rgba_uchar_to_float(info->colf, info->col);
-
- info->colp = info->col;
- info->colfp = info->colf;
- info->color_manage = true;
- }
- if (ibuf->rect_float) {
- image_sample_rect_color_float(ibuf, &sample_rect, info->colf);
-
- if (ibuf->channels == 4) {
- /* pass */
- }
- else if (ibuf->channels == 3) {
- info->colf[3] = 1.0f;
- }
- else {
- info->colf[1] = info->colf[0];
- info->colf[2] = info->colf[0];
- info->colf[3] = 1.0f;
- }
- info->colfp = info->colf;
-
- copy_v4_v4(info->linearcol, info->colf);
-
- info->color_manage = true;
- }
-
- if (ibuf->zbuf) {
- /* TODO, blend depth (not urgent). */
- info->z = ibuf->zbuf[y * ibuf->x + x];
- info->zp = &info->z;
- if (ibuf->zbuf == (int *)ibuf->rect) {
- info->colp = NULL;
- }
- }
- if (ibuf->zbuf_float) {
- /* TODO, blend depth (not urgent). */
- info->zf = ibuf->zbuf_float[y * ibuf->x + x];
- info->zfp = &info->zf;
- if (ibuf->zbuf_float == ibuf->rect_float) {
- info->colfp = NULL;
- }
- }
-
- if (curve_mapping && ibuf->channels == 4) {
- /* we reuse this callback for set curves point operators */
- if (RNA_struct_find_property(op->ptr, "point")) {
- int point = RNA_enum_get(op->ptr, "point");
-
- if (point == 1) {
- BKE_curvemapping_set_black_white(curve_mapping, NULL, info->linearcol);
- }
- else if (point == 0) {
- BKE_curvemapping_set_black_white(curve_mapping, info->linearcol, NULL);
- }
- WM_event_add_notifier(C, NC_WINDOW, NULL);
- }
- }
-
- // XXX node curve integration ..
-#if 0
- {
- ScrArea *area, *cur = curarea;
-
- node_curvemap_sample(fp); /* sends global to node editor */
- for (area = G.curscreen->areabase.first; area; area = area->next) {
- if (area->spacetype == SPACE_NODE) {
- areawinset(area->win);
- scrarea_do_windraw(area);
- }
- }
- node_curvemap_sample(NULL); /* clears global in node editor */
- curarea = cur;
- }
-#endif
- }
- else {
- info->draw = 0;
- }
-
- ED_space_image_release_buffer(sima, ibuf, lock);
- ED_area_tag_redraw(CTX_wm_area(C));
-}
-
-static void image_sample_exit(bContext *C, wmOperator *op)
-{
- ImageSampleInfo *info = op->customdata;
-
- ED_region_draw_cb_exit(info->art, info->draw_handle);
- ED_area_tag_redraw(CTX_wm_area(C));
- MEM_freeN(info);
-}
-
-static int image_sample_invoke(bContext *C, wmOperator *op, const wmEvent *event)
-{
- SpaceImage *sima = CTX_wm_space_image(C);
- ARegion *region = CTX_wm_region(C);
- ImageSampleInfo *info;
-
- if (region->regiontype == RGN_TYPE_WINDOW) {
- if (event->mval[1] <= 16 && ED_space_image_show_cache(sima)) {
- return OPERATOR_PASS_THROUGH;
- }
- }
-
- if (!ED_space_image_has_buffer(sima)) {
- return OPERATOR_CANCELLED;
- }
-
- info = MEM_callocN(sizeof(ImageSampleInfo), "ImageSampleInfo");
-
- info->art = region->type;
- info->draw_handle = ED_region_draw_cb_activate(
- region->type, image_sample_draw, info, REGION_DRAW_POST_PIXEL);
- info->sample_size = RNA_int_get(op->ptr, "size");
- op->customdata = info;
-
- image_sample_apply(C, op, event);
-
- WM_event_add_modal_handler(C, op);
-
- return OPERATOR_RUNNING_MODAL;
-}
-
-static int image_sample_modal(bContext *C, wmOperator *op, const wmEvent *event)
-{
- switch (event->type) {
- case LEFTMOUSE:
- case RIGHTMOUSE: // XXX hardcoded
- if (event->val == KM_RELEASE) {
- image_sample_exit(C, op);
- return OPERATOR_CANCELLED;
- }
- break;
- case MOUSEMOVE:
- image_sample_apply(C, op, event);
- break;
- }
-
- return OPERATOR_RUNNING_MODAL;
-}
-
-static void image_sample_cancel(bContext *C, wmOperator *op)
-{
- image_sample_exit(C, op);
-}
-
void IMAGE_OT_sample(wmOperatorType *ot)
{
/* identifiers */
@@ -3503,10 +3130,10 @@ void IMAGE_OT_sample(wmOperatorType *ot)
ot->description = "Use mouse to sample a color in current image";
/* api callbacks */
- ot->invoke = image_sample_invoke;
- ot->modal = image_sample_modal;
- ot->cancel = image_sample_cancel;
- ot->poll = image_sample_poll;
+ ot->invoke = ED_imbuf_sample_invoke;
+ ot->modal = ED_imbuf_sample_modal;
+ ot->cancel = ED_imbuf_sample_cancel;
+ ot->poll = ED_imbuf_sample_poll;
/* flags */
ot->flag = OPTYPE_BLOCKING;
@@ -3632,9 +3259,9 @@ void IMAGE_OT_curves_point_set(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* api callbacks */
- ot->invoke = image_sample_invoke;
- ot->modal = image_sample_modal;
- ot->cancel = image_sample_cancel;
+ ot->invoke = ED_imbuf_sample_invoke;
+ ot->modal = ED_imbuf_sample_modal;
+ ot->cancel = ED_imbuf_sample_cancel;
ot->poll = space_image_main_area_not_uv_brush_poll;
/* properties */
diff --git a/source/blender/editors/space_image/image_sequence.c b/source/blender/editors/space_image/image_sequence.c
index e65924d0eb9..0817bdda88d 100644
--- a/source/blender/editors/space_image/image_sequence.c
+++ b/source/blender/editors/space_image/image_sequence.c
@@ -64,6 +64,7 @@ static void image_sequence_get_frame_ranges(wmOperator *op, ListBase *ranges)
char dir[FILE_MAXDIR];
const bool do_frame_range = RNA_boolean_get(op->ptr, "use_sequence_detection");
ImageFrameRange *range = NULL;
+ int range_first_frame = 0;
RNA_string_get(op->ptr, "directory", dir);
RNA_BEGIN (op->ptr, itemptr, "files") {
@@ -79,7 +80,11 @@ static void image_sequence_get_frame_ranges(wmOperator *op, ListBase *ranges)
/* still in the same sequence */
if (do_frame_range && (range != NULL) && (STREQLEN(base_head, head, FILE_MAX)) &&
(STREQLEN(base_tail, tail, FILE_MAX))) {
- /* pass */
+ /* Set filepath to first frame in the range. */
+ if (frame->framenr < range_first_frame) {
+ BLI_join_dirfile(range->filepath, sizeof(range->filepath), dir, filename);
+ range_first_frame = frame->framenr;
+ }
}
else {
/* start a new frame range */
@@ -89,6 +94,8 @@ static void image_sequence_get_frame_ranges(wmOperator *op, ListBase *ranges)
BLI_strncpy(base_head, head, sizeof(base_head));
BLI_strncpy(base_tail, tail, sizeof(base_tail));
+
+ range_first_frame = frame->framenr;
}
BLI_addtail(&range->frames, frame);
diff --git a/source/blender/editors/space_node/node_relationships.c b/source/blender/editors/space_node/node_relationships.c
index 8f8f945a600..8c2f79109f6 100644
--- a/source/blender/editors/space_node/node_relationships.c
+++ b/source/blender/editors/space_node/node_relationships.c
@@ -1622,8 +1622,8 @@ static void node_parent_offset_apply(NodeInsertOfsData *data, bNode *parent, con
node_offset_apply(parent, offset_x);
- /* flag all childs as offset to prevent them from being offset
- * separately (they've already moved with the parent) */
+ /* Flag all children as offset to prevent them from being offset
+ * separately (they've already moved with the parent). */
for (node = data->ntree->nodes.first; node; node = node->next) {
if (nodeIsChildOf(parent, node)) {
/* NODE_TEST is used to flag nodes that shouldn't be offset (again) */
diff --git a/source/blender/editors/space_node/node_select.c b/source/blender/editors/space_node/node_select.c
index 38ec855e845..50c32da4b5a 100644
--- a/source/blender/editors/space_node/node_select.c
+++ b/source/blender/editors/space_node/node_select.c
@@ -1179,7 +1179,7 @@ static uiBlock *node_find_menu(bContext *C, ARegion *region, void *arg_op)
0,
0,
"");
- UI_but_func_search_set(but, NULL, node_find_cb, op->type, NULL, node_find_call_cb, NULL);
+ UI_but_func_search_set(but, NULL, node_find_cb, op->type, NULL, node_find_call_cb, NULL, NULL);
UI_but_flag_enable(but, UI_BUT_ACTIVATE_ON_INIT);
/* fake button, it holds space for search items */
diff --git a/source/blender/editors/space_outliner/outliner_collections.c b/source/blender/editors/space_outliner/outliner_collections.c
index 5a0ed954909..4b6241d45ee 100644
--- a/source/blender/editors/space_outliner/outliner_collections.c
+++ b/source/blender/editors/space_outliner/outliner_collections.c
@@ -845,20 +845,6 @@ static bool collections_indirect_only_clear_poll(bContext *C)
return collections_view_layer_poll(C, true, LAYER_COLLECTION_INDIRECT_ONLY);
}
-static void layer_collection_flag_recursive_set(LayerCollection *lc, int flag)
-{
- LISTBASE_FOREACH (LayerCollection *, nlc, &lc->layer_collections) {
- if (lc->flag & flag) {
- nlc->flag |= flag;
- }
- else {
- nlc->flag &= ~flag;
- }
-
- layer_collection_flag_recursive_set(nlc, flag);
- }
-}
-
static int collection_view_layer_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
@@ -883,15 +869,7 @@ static int collection_view_layer_exec(bContext *C, wmOperator *op)
GSetIterator collections_to_edit_iter;
GSET_ITER (collections_to_edit_iter, data.collections_to_edit) {
LayerCollection *lc = BLI_gsetIterator_getKey(&collections_to_edit_iter);
-
- if (clear) {
- lc->flag &= ~flag;
- }
- else {
- lc->flag |= flag;
- }
-
- layer_collection_flag_recursive_set(lc, flag);
+ BKE_layer_collection_set_flag(lc, flag, !clear);
}
BLI_gset_free(data.collections_to_edit, NULL);
@@ -1468,8 +1446,7 @@ static int outliner_unhide_all_exec(bContext *C, wmOperator *UNUSED(op))
/* Unhide all the collections. */
LayerCollection *lc_master = view_layer->layer_collections.first;
LISTBASE_FOREACH (LayerCollection *, lc_iter, &lc_master->layer_collections) {
- lc_iter->flag &= ~LAYER_COLLECTION_HIDE;
- layer_collection_flag_recursive_set(lc_iter, LAYER_COLLECTION_HIDE);
+ BKE_layer_collection_set_flag(lc_iter, LAYER_COLLECTION_HIDE, false);
}
/* Unhide all objects. */
diff --git a/source/blender/editors/space_outliner/outliner_edit.c b/source/blender/editors/space_outliner/outliner_edit.c
index 60e6b423720..3ae100b6209 100644
--- a/source/blender/editors/space_outliner/outliner_edit.c
+++ b/source/blender/editors/space_outliner/outliner_edit.c
@@ -54,6 +54,7 @@
#include "BKE_outliner_treehash.h"
#include "BKE_report.h"
#include "BKE_scene.h"
+#include "BKE_workspace.h"
#include "DEG_depsgraph.h"
#include "DEG_depsgraph_build.h"
@@ -473,6 +474,14 @@ static void id_delete(bContext *C, ReportList *reports, TreeElement *te, TreeSto
id->name);
return;
}
+ else if (te->idcode == ID_WS) {
+ BKE_workspace_id_tag_all_visible(bmain, LIB_TAG_DOIT);
+ if (id->tag & LIB_TAG_DOIT) {
+ BKE_reportf(
+ reports, RPT_WARNING, "Cannot delete currently visible workspace id '%s'", id->name);
+ return;
+ }
+ }
BKE_id_delete(bmain, id);
diff --git a/source/blender/editors/space_outliner/outliner_select.c b/source/blender/editors/space_outliner/outliner_select.c
index 64d86293fb7..0971d3526ad 100644
--- a/source/blender/editors/space_outliner/outliner_select.c
+++ b/source/blender/editors/space_outliner/outliner_select.c
@@ -308,7 +308,7 @@ static eOLDrawState tree_element_set_active_object(bContext *C,
bool recursive)
{
TreeStoreElem *tselem = TREESTORE(te);
- TreeStoreElem *parent_tselem;
+ TreeStoreElem *parent_tselem = NULL;
Scene *sce;
Base *base;
Object *ob = NULL;
diff --git a/source/blender/editors/space_outliner/outliner_tools.c b/source/blender/editors/space_outliner/outliner_tools.c
index 34ca2267b7a..fe62272614f 100644
--- a/source/blender/editors/space_outliner/outliner_tools.c
+++ b/source/blender/editors/space_outliner/outliner_tools.c
@@ -590,7 +590,7 @@ static uiBlock *merged_element_search_menu(bContext *C, ARegion *region, void *d
but = uiDefSearchBut(
block, search, 0, ICON_VIEWZOOM, sizeof(search), 10, 10, menu_width, UI_UNIT_Y, 0, 0, "");
UI_but_func_search_set(
- but, NULL, merged_element_search_cb, data, NULL, merged_element_search_call_cb, NULL);
+ but, NULL, merged_element_search_cb, data, NULL, merged_element_search_call_cb, NULL, NULL);
UI_but_flag_enable(but, UI_BUT_ACTIVATE_ON_INIT);
/* Fake button to hold space for search items */
diff --git a/source/blender/editors/space_sequencer/sequencer_edit.c b/source/blender/editors/space_sequencer/sequencer_edit.c
index d34eed622d4..83671a0d600 100644
--- a/source/blender/editors/space_sequencer/sequencer_edit.c
+++ b/source/blender/editors/space_sequencer/sequencer_edit.c
@@ -720,7 +720,7 @@ static void recurs_del_seq_flag(Scene *scene, ListBase *lb, short flag, short de
if (seq->type == SEQ_TYPE_META) {
recurs_del_seq_flag(scene, &seq->seqbase, flag, 1);
}
- BKE_sequence_free(scene, seq);
+ BKE_sequence_free(scene, seq, true);
}
seq = seqn;
}
@@ -838,6 +838,7 @@ static Sequence *split_seq_hard(
BKE_sequence_reload_new_file(bmain, scene, seqn, false);
BKE_sequence_calc(scene, seqn);
+ BKE_sequence_invalidate_cache_in_range(scene, seq, seqn, SEQ_CACHE_ALL_TYPES);
}
return seqn;
}
@@ -937,6 +938,7 @@ static Sequence *split_seq_soft(
}
BKE_sequence_calc(scene, seqn);
+ BKE_sequence_invalidate_cache_in_range(scene, seq, seqn, SEQ_CACHE_ALL_TYPES);
}
return seqn;
}
@@ -2641,7 +2643,7 @@ static int sequencer_separate_images_exec(bContext *C, wmOperator *op)
}
seq_next = seq->next;
- BKE_sequence_free(scene, seq);
+ BKE_sequence_free(scene, seq, true);
seq = seq_next;
}
else {
@@ -2866,7 +2868,7 @@ static int sequencer_meta_separate_exec(bContext *C, wmOperator *UNUSED(op))
BLI_listbase_clear(&last_seq->seqbase);
BLI_remlink(ed->seqbasep, last_seq);
- BKE_sequence_free(scene, last_seq);
+ BKE_sequence_free(scene, last_seq, true);
/* Empty meta strip, delete all effects depending on it. */
for (seq = ed->seqbasep->first; seq; seq = seq->next) {
diff --git a/source/blender/editors/space_sequencer/sequencer_view.c b/source/blender/editors/space_sequencer/sequencer_view.c
index a890b770c83..d397c255b03 100644
--- a/source/blender/editors/space_sequencer/sequencer_view.c
+++ b/source/blender/editors/space_sequencer/sequencer_view.c
@@ -21,219 +21,16 @@
* \ingroup spseq
*/
-#include "MEM_guardedalloc.h"
+#include "ED_util_imbuf.h"
-#include "BLI_math.h"
-#include "BLI_utildefines.h"
+#include "RNA_define.h"
-#include "DNA_scene_types.h"
-
-#include "BKE_context.h"
-#include "BKE_main.h"
-#include "BKE_screen.h"
-#include "BKE_sequencer.h"
-
-#include "WM_api.h"
#include "WM_types.h"
-#include "ED_image.h"
-#include "ED_screen.h"
-#include "ED_space_api.h"
-
-#include "IMB_colormanagement.h"
-#include "IMB_imbuf.h"
-#include "IMB_imbuf_types.h"
-
-#include "UI_view2d.h"
-
/* Own include. */
#include "sequencer_intern.h"
/******************** sample backdrop operator ********************/
-
-typedef struct ImageSampleInfo {
- ARegionType *art;
- void *draw_handle;
- int x, y;
- int channels;
-
- uchar col[4];
- float colf[4];
- float linearcol[4];
-
- uchar *colp;
- const float *colfp;
-
- int draw;
- int color_manage;
-} ImageSampleInfo;
-
-static void sample_draw(const bContext *C, ARegion *region, void *arg_info)
-{
- Scene *scene = CTX_data_scene(C);
- ImageSampleInfo *info = arg_info;
-
- if (info->draw) {
- ED_image_draw_info(scene,
- region,
- info->color_manage,
- false,
- info->channels,
- info->x,
- info->y,
- info->colp,
- info->colfp,
- info->linearcol,
- NULL,
- NULL);
- }
-}
-
-static void sample_apply(bContext *C, wmOperator *op, const wmEvent *event)
-{
- Main *bmain = CTX_data_main(C);
- struct Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
- Scene *scene = CTX_data_scene(C);
- SpaceSeq *sseq = (SpaceSeq *)CTX_wm_space_data(C);
- ARegion *region = CTX_wm_region(C);
- ImBuf *ibuf = sequencer_ibuf_get(bmain, depsgraph, scene, sseq, CFRA, 0, NULL);
- ImageSampleInfo *info = op->customdata;
- float fx, fy;
-
- if (ibuf == NULL) {
- IMB_freeImBuf(ibuf);
- info->draw = 0;
- return;
- }
-
- UI_view2d_region_to_view(&region->v2d, event->mval[0], event->mval[1], &fx, &fy);
-
- fx /= scene->r.xasp / scene->r.yasp;
-
- fx += (float)scene->r.xsch / 2.0f;
- fy += (float)scene->r.ysch / 2.0f;
- fx *= (float)ibuf->x / (float)scene->r.xsch;
- fy *= (float)ibuf->y / (float)scene->r.ysch;
-
- if (fx >= 0.0f && fy >= 0.0f && fx < ibuf->x && fy < ibuf->y) {
- const float *fp;
- uchar *cp;
- int x = (int)fx, y = (int)fy;
-
- info->x = x;
- info->y = y;
- info->draw = 1;
- info->channels = ibuf->channels;
-
- info->colp = NULL;
- info->colfp = NULL;
-
- if (ibuf->rect) {
- cp = (uchar *)(ibuf->rect + y * ibuf->x + x);
-
- info->col[0] = cp[0];
- info->col[1] = cp[1];
- info->col[2] = cp[2];
- info->col[3] = cp[3];
- info->colp = info->col;
-
- info->colf[0] = (float)cp[0] / 255.0f;
- info->colf[1] = (float)cp[1] / 255.0f;
- info->colf[2] = (float)cp[2] / 255.0f;
- info->colf[3] = (float)cp[3] / 255.0f;
- info->colfp = info->colf;
-
- copy_v4_v4(info->linearcol, info->colf);
- IMB_colormanagement_colorspace_to_scene_linear_v4(
- info->linearcol, false, ibuf->rect_colorspace);
-
- info->color_manage = true;
- }
- if (ibuf->rect_float) {
- fp = (ibuf->rect_float + (ibuf->channels) * (y * ibuf->x + x));
-
- info->colf[0] = fp[0];
- info->colf[1] = fp[1];
- info->colf[2] = fp[2];
- info->colf[3] = fp[3];
- info->colfp = info->colf;
-
- /* Sequencer's image buffers are in non-linear space, need to make them linear. */
- copy_v4_v4(info->linearcol, info->colf);
- BKE_sequencer_pixel_from_sequencer_space_v4(scene, info->linearcol);
-
- info->color_manage = true;
- }
- }
- else {
- info->draw = 0;
- }
-
- IMB_freeImBuf(ibuf);
- ED_area_tag_redraw(CTX_wm_area(C));
-}
-
-static void sample_exit(bContext *C, wmOperator *op)
-{
- ImageSampleInfo *info = op->customdata;
-
- ED_region_draw_cb_exit(info->art, info->draw_handle);
- ED_area_tag_redraw(CTX_wm_area(C));
- MEM_freeN(info);
-}
-
-static int sample_invoke(bContext *C, wmOperator *op, const wmEvent *event)
-{
- ARegion *region = CTX_wm_region(C);
- SpaceSeq *sseq = CTX_wm_space_seq(C);
- ImageSampleInfo *info;
-
- if (sseq->mainb != SEQ_DRAW_IMG_IMBUF) {
- return OPERATOR_CANCELLED;
- }
-
- info = MEM_callocN(sizeof(ImageSampleInfo), "ImageSampleInfo");
- info->art = region->type;
- info->draw_handle = ED_region_draw_cb_activate(
- region->type, sample_draw, info, REGION_DRAW_POST_PIXEL);
- op->customdata = info;
-
- sample_apply(C, op, event);
-
- WM_event_add_modal_handler(C, op);
-
- return OPERATOR_RUNNING_MODAL;
-}
-
-static int sample_modal(bContext *C, wmOperator *op, const wmEvent *event)
-{
- switch (event->type) {
- case LEFTMOUSE:
- case RIGHTMOUSE: /* XXX hardcoded */
- if (event->val == KM_RELEASE) {
- sample_exit(C, op);
- return OPERATOR_CANCELLED;
- }
- break;
- case MOUSEMOVE:
- sample_apply(C, op, event);
- break;
- }
-
- return OPERATOR_RUNNING_MODAL;
-}
-
-static void sample_cancel(bContext *C, wmOperator *op)
-{
- sample_exit(C, op);
-}
-
-static bool sample_poll(bContext *C)
-{
- SpaceSeq *sseq = CTX_wm_space_seq(C);
- return sseq && BKE_sequencer_editing_get(CTX_data_scene(C), false) != NULL;
-}
-
void SEQUENCER_OT_sample(wmOperatorType *ot)
{
/* Identifiers. */
@@ -242,11 +39,17 @@ void SEQUENCER_OT_sample(wmOperatorType *ot)
ot->description = "Use mouse to sample color in current frame";
/* Api callbacks. */
- ot->invoke = sample_invoke;
- ot->modal = sample_modal;
- ot->cancel = sample_cancel;
- ot->poll = sample_poll;
+ ot->invoke = ED_imbuf_sample_invoke;
+ ot->modal = ED_imbuf_sample_modal;
+ ot->cancel = ED_imbuf_sample_cancel;
+ ot->poll = ED_imbuf_sample_poll;
/* Flags. */
ot->flag = OPTYPE_BLOCKING;
+
+ /* Not implemented. */
+ PropertyRNA *prop;
+ prop = RNA_def_int(ot->srna, "size", 1, 1, 128, "Sample Size", "", 1, 64);
+ RNA_def_property_subtype(prop, PROP_PIXEL);
+ RNA_def_property_flag(prop, PROP_SKIP_SAVE | PROP_HIDDEN);
}
diff --git a/source/blender/editors/transform/transform.c b/source/blender/editors/transform/transform.c
index c1e890ed5f1..d0866968daa 100644
--- a/source/blender/editors/transform/transform.c
+++ b/source/blender/editors/transform/transform.c
@@ -77,7 +77,7 @@ static void initSnapSpatial(TransInfo *t, float r_snap[3]);
bool transdata_check_local_islands(TransInfo *t, short around)
{
- return ((around == V3D_AROUND_LOCAL_ORIGINS) && ((ELEM(t->obedit_type, OB_MESH))));
+ return ((around == V3D_AROUND_LOCAL_ORIGINS) && ((ELEM(t->obedit_type, OB_MESH, OB_GPENCIL))));
}
/* ************************** SPACE DEPENDENT CODE **************************** */
diff --git a/source/blender/editors/transform/transform_convert_armature.c b/source/blender/editors/transform/transform_convert_armature.c
index e52bd15b0d5..0106f4f41c7 100644
--- a/source/blender/editors/transform/transform_convert_armature.c
+++ b/source/blender/editors/transform/transform_convert_armature.c
@@ -58,6 +58,34 @@ typedef struct BoneInitData {
float zwidth;
} BoneInitData;
+static void add_temporary_ik_constraint(bPoseChannel *pchan, bKinematicConstraint *targetless_con)
+{
+ bConstraint *con = BKE_constraint_add_for_pose(
+ NULL, pchan, "TempConstraint", CONSTRAINT_TYPE_KINEMATIC);
+
+ /* for draw, but also for detecting while pose solving */
+ pchan->constflag |= (PCHAN_HAS_IK | PCHAN_HAS_TARGET);
+
+ bKinematicConstraint *temp_con_data = con->data;
+
+ if (targetless_con) {
+ /* if exists, use values from last targetless (but disabled) IK-constraint as base */
+ *temp_con_data = *targetless_con;
+ }
+ else {
+ temp_con_data->flag = CONSTRAINT_IK_TIP;
+ }
+
+ temp_con_data->flag |= CONSTRAINT_IK_TEMP | CONSTRAINT_IK_AUTO | CONSTRAINT_IK_POS;
+}
+
+static void update_deg_with_temporary_ik(Main *bmain, Object *ob)
+{
+ BIK_clear_data(ob->pose);
+ /* TODO(sergey): Consider doing partial update only. */
+ DEG_relations_tag_update(bmain);
+}
+
static void add_pose_transdata(
TransInfo *t, bPoseChannel *pchan, Object *ob, TransDataContainer *tc, TransData *td)
{
@@ -200,8 +228,16 @@ static void add_pose_transdata(
}
td->loc = data->grabtarget;
copy_v3_v3(td->iloc, td->loc);
+
data->flag |= CONSTRAINT_IK_AUTO;
+ /* Add a temporary auto IK constraint here, as we will only temporarily active this
+ * targetless bone during transform. (Targetless IK constraints are treated as if they are
+ * disabled unless they are transformed). */
+ add_temporary_ik_constraint(pchan, data);
+ Main *bmain = CTX_data_main(t->context);
+ update_deg_with_temporary_ik(bmain, ob);
+
/* only object matrix correction */
copy_m3_m3(td->mtx, omat);
pseudoinverse_m3_m3(td->smtx, td->mtx, PSEUDOINVERSE_EPSILON);
@@ -221,7 +257,8 @@ bKinematicConstraint *has_targetless_ik(bPoseChannel *pchan)
bConstraint *con = pchan->constraints.first;
for (; con; con = con->next) {
- if (con->type == CONSTRAINT_TYPE_KINEMATIC && (con->enforce != 0.0f)) {
+ if (con->type == CONSTRAINT_TYPE_KINEMATIC && (con->flag & CONSTRAINT_OFF) == 0 &&
+ (con->enforce != 0.0f)) {
bKinematicConstraint *data = con->data;
if (data->tar == NULL) {
@@ -278,8 +315,6 @@ static short pose_grab_with_ik_add(bPoseChannel *pchan)
}
}
}
-
- return 0;
}
}
@@ -289,20 +324,7 @@ static short pose_grab_with_ik_add(bPoseChannel *pchan)
}
}
- con = BKE_constraint_add_for_pose(NULL, pchan, "TempConstraint", CONSTRAINT_TYPE_KINEMATIC);
-
- /* for draw, but also for detecting while pose solving */
- pchan->constflag |= (PCHAN_HAS_IK | PCHAN_HAS_TARGET);
-
- data = con->data;
- if (targetless) {
- /* if exists, use values from last targetless (but disabled) IK-constraint as base */
- *data = *targetless;
- }
- else {
- data->flag = CONSTRAINT_IK_TIP;
- }
- data->flag |= CONSTRAINT_IK_TEMP | CONSTRAINT_IK_AUTO | CONSTRAINT_IK_POS;
+ add_temporary_ik_constraint(pchan, targetless);
copy_v3_v3(data->grabtarget, pchan->pose_tail);
/* watch-it! has to be 0 here, since we're still on the
@@ -415,9 +437,7 @@ static short pose_grab_with_ik(Main *bmain, Object *ob)
/* iTaSC needs clear for new IK constraints */
if (tot_ik) {
- BIK_clear_data(ob->pose);
- /* TODO(sergey): Consider doing partial update only. */
- DEG_relations_tag_update(bmain);
+ update_deg_with_temporary_ik(bmain, ob);
}
return (tot_ik) ? 1 : 0;
@@ -585,6 +605,12 @@ void pose_transform_mirror_update(TransInfo *t, TransDataContainer *tc, Object *
/* TODO(germano): Realitve Mirror support */
}
data->flag |= CONSTRAINT_IK_AUTO;
+ /* Add a temporary auto IK constraint here, as we will only temporarly active this targetless
+ * bone during transform. (Targetless IK constraints are treated as if they are disabled
+ * unless they are transformed) */
+ add_temporary_ik_constraint(pchan, data);
+ Main *bmain = CTX_data_main(t->context);
+ update_deg_with_temporary_ik(bmain, ob);
}
if (pid) {
diff --git a/source/blender/editors/undo/memfile_undo.c b/source/blender/editors/undo/memfile_undo.c
index ebd5b2272b1..9954cf85157 100644
--- a/source/blender/editors/undo/memfile_undo.c
+++ b/source/blender/editors/undo/memfile_undo.c
@@ -234,15 +234,15 @@ static void memfile_undosys_step_decode(struct bContext *C,
/* We only start accumulating from this point, any tags set up to here
* are already part of the current undo state. This is done in a second
* loop because DEG_id_tag_update may set tags on other datablocks. */
- id->recalc_undo_accumulated = 0;
+ id->recalc_after_undo_push = 0;
bNodeTree *nodetree = ntreeFromID(id);
if (nodetree != NULL) {
- nodetree->id.recalc_undo_accumulated = 0;
+ nodetree->id.recalc_after_undo_push = 0;
}
if (GS(id->name) == ID_SCE) {
Scene *scene = (Scene *)id;
if (scene->master_collection != NULL) {
- scene->master_collection->id.recalc_undo_accumulated = 0;
+ scene->master_collection->id.recalc_after_undo_push = 0;
}
}
}
diff --git a/source/blender/editors/util/CMakeLists.txt b/source/blender/editors/util/CMakeLists.txt
index 987327eefc1..17a90d10ca7 100644
--- a/source/blender/editors/util/CMakeLists.txt
+++ b/source/blender/editors/util/CMakeLists.txt
@@ -17,6 +17,7 @@
set(INC
../include
+ ../space_sequencer
../../blenkernel
../../blenlib
../../blentranslation
@@ -39,6 +40,7 @@ set(INC_SYS
set(SRC
ed_transverts.c
ed_util.c
+ ed_util_imbuf.c
gizmo_utils.c
numinput.c
select_utils.c
@@ -91,6 +93,7 @@ set(SRC
../include/ED_undo.h
../include/ED_userpref.h
../include/ED_util.h
+ ../include/ED_util_imbuf.h
../include/ED_uvedit.h
../include/ED_view3d.h
../include/UI_icons.h
diff --git a/source/blender/editors/util/ed_util_imbuf.c b/source/blender/editors/util/ed_util_imbuf.c
new file mode 100644
index 00000000000..132a63a8249
--- /dev/null
+++ b/source/blender/editors/util/ed_util_imbuf.c
@@ -0,0 +1,571 @@
+/*
+ * 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.
+ *
+ * The Original Code is Copyright (C) 2008 Blender Foundation.
+ * All rights reserved.
+ */
+
+/** \file
+ * \ingroup edutil
+ */
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_rect.h"
+
+#include "BKE_colortools.h"
+#include "BKE_context.h"
+#include "BKE_image.h"
+#include "BKE_main.h"
+#include "BKE_screen.h"
+#include "BKE_sequencer.h"
+
+#include "ED_image.h"
+#include "ED_screen.h"
+#include "ED_space_api.h"
+
+#include "GPU_immediate.h"
+#include "GPU_state.h"
+
+#include "IMB_colormanagement.h"
+#include "IMB_imbuf.h"
+#include "IMB_imbuf_types.h"
+
+#include "UI_view2d.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+#include "sequencer_intern.h"
+
+/* Own define. */
+#include "ED_util_imbuf.h"
+
+/* -------------------------------------------------------------------- */
+/** \name Image Pixel Sample Struct (Operator Custom Data)
+ * \{ */
+
+typedef struct ImageSampleInfo {
+ ARegionType *art;
+ void *draw_handle;
+ int x, y;
+ int channels;
+
+ int width, height;
+ int sample_size;
+
+ unsigned char col[4];
+ float colf[4];
+ float linearcol[4];
+ int z;
+ float zf;
+
+ unsigned char *colp;
+ const float *colfp;
+ int *zp;
+ float *zfp;
+
+ bool draw;
+ bool color_manage;
+ int use_default_view;
+} ImageSampleInfo;
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Image Pixel Sample
+ * \{ */
+
+static void image_sample_pixel_color_ubyte(const ImBuf *ibuf,
+ const int coord[2],
+ uchar r_col[4],
+ float r_col_linear[4])
+{
+ const uchar *cp = (unsigned char *)(ibuf->rect + coord[1] * ibuf->x + coord[0]);
+ copy_v4_v4_uchar(r_col, cp);
+ rgba_uchar_to_float(r_col_linear, r_col);
+ IMB_colormanagement_colorspace_to_scene_linear_v4(r_col_linear, false, ibuf->rect_colorspace);
+}
+
+static void image_sample_pixel_color_float(ImBuf *ibuf, const int coord[2], float r_col[4])
+{
+ const float *cp = ibuf->rect_float + (ibuf->channels) * (coord[1] * ibuf->x + coord[0]);
+ copy_v4_v4(r_col, cp);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Image Pixel Region Sample
+ * \{ */
+
+static void image_sample_rect_color_ubyte(const ImBuf *ibuf,
+ const rcti *rect,
+ uchar r_col[4],
+ float r_col_linear[4])
+{
+ uint col_accum_ub[4] = {0, 0, 0, 0};
+ zero_v4(r_col_linear);
+ int col_tot = 0;
+ int coord[2];
+ for (coord[0] = rect->xmin; coord[0] <= rect->xmax; coord[0]++) {
+ for (coord[1] = rect->ymin; coord[1] <= rect->ymax; coord[1]++) {
+ float col_temp_fl[4];
+ uchar col_temp_ub[4];
+ image_sample_pixel_color_ubyte(ibuf, coord, col_temp_ub, col_temp_fl);
+ add_v4_v4(r_col_linear, col_temp_fl);
+ col_accum_ub[0] += (uint)col_temp_ub[0];
+ col_accum_ub[1] += (uint)col_temp_ub[1];
+ col_accum_ub[2] += (uint)col_temp_ub[2];
+ col_accum_ub[3] += (uint)col_temp_ub[3];
+ col_tot += 1;
+ }
+ }
+ mul_v4_fl(r_col_linear, 1.0 / (float)col_tot);
+
+ r_col[0] = MIN2(col_accum_ub[0] / col_tot, 255);
+ r_col[1] = MIN2(col_accum_ub[1] / col_tot, 255);
+ r_col[2] = MIN2(col_accum_ub[2] / col_tot, 255);
+ r_col[3] = MIN2(col_accum_ub[3] / col_tot, 255);
+}
+
+static void image_sample_rect_color_float(ImBuf *ibuf, const rcti *rect, float r_col[4])
+{
+ zero_v4(r_col);
+ int col_tot = 0;
+ int coord[2];
+ for (coord[0] = rect->xmin; coord[0] <= rect->xmax; coord[0]++) {
+ for (coord[1] = rect->ymin; coord[1] <= rect->ymax; coord[1]++) {
+ float col_temp_fl[4];
+ image_sample_pixel_color_float(ibuf, coord, col_temp_fl);
+ add_v4_v4(r_col, col_temp_fl);
+ col_tot += 1;
+ }
+ }
+ mul_v4_fl(r_col, 1.0 / (float)col_tot);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Image Pixel Sample (Internal Utilities)
+ * \{ */
+
+static void image_sample_apply(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ SpaceImage *sima = CTX_wm_space_image(C);
+ ARegion *region = CTX_wm_region(C);
+ Image *image = ED_space_image(sima);
+
+ float uv[2];
+ UI_view2d_region_to_view(&region->v2d, event->mval[0], event->mval[1], &uv[0], &uv[1]);
+ int tile = BKE_image_get_tile_from_pos(sima->image, uv, uv, NULL);
+
+ void *lock;
+ ImBuf *ibuf = ED_space_image_acquire_buffer(sima, &lock, tile);
+ ImageSampleInfo *info = op->customdata;
+ Scene *scene = CTX_data_scene(C);
+ CurveMapping *curve_mapping = scene->view_settings.curve_mapping;
+
+ if (ibuf == NULL) {
+ ED_space_image_release_buffer(sima, ibuf, lock);
+ info->draw = false;
+ return;
+ }
+
+ if (uv[0] >= 0.0f && uv[1] >= 0.0f && uv[0] < 1.0f && uv[1] < 1.0f) {
+ int x = (int)(uv[0] * ibuf->x), y = (int)(uv[1] * ibuf->y);
+
+ CLAMP(x, 0, ibuf->x - 1);
+ CLAMP(y, 0, ibuf->y - 1);
+
+ info->width = ibuf->x;
+ info->height = ibuf->y;
+ info->x = x;
+ info->y = y;
+
+ info->draw = true;
+ info->channels = ibuf->channels;
+
+ info->colp = NULL;
+ info->colfp = NULL;
+ info->zp = NULL;
+ info->zfp = NULL;
+
+ info->use_default_view = (image->flag & IMA_VIEW_AS_RENDER) ? false : true;
+
+ rcti sample_rect;
+ sample_rect.xmin = max_ii(0, x - info->sample_size / 2);
+ sample_rect.ymin = max_ii(0, y - info->sample_size / 2);
+ sample_rect.xmax = min_ii(ibuf->x, sample_rect.xmin + info->sample_size) - 1;
+ sample_rect.ymax = min_ii(ibuf->y, sample_rect.ymin + info->sample_size) - 1;
+
+ if (ibuf->rect) {
+ image_sample_rect_color_ubyte(ibuf, &sample_rect, info->col, info->linearcol);
+ rgba_uchar_to_float(info->colf, info->col);
+
+ info->colp = info->col;
+ info->colfp = info->colf;
+ info->color_manage = true;
+ }
+ if (ibuf->rect_float) {
+ image_sample_rect_color_float(ibuf, &sample_rect, info->colf);
+
+ if (ibuf->channels == 4) {
+ /* pass */
+ }
+ else if (ibuf->channels == 3) {
+ info->colf[3] = 1.0f;
+ }
+ else {
+ info->colf[1] = info->colf[0];
+ info->colf[2] = info->colf[0];
+ info->colf[3] = 1.0f;
+ }
+ info->colfp = info->colf;
+
+ copy_v4_v4(info->linearcol, info->colf);
+
+ info->color_manage = true;
+ }
+
+ if (ibuf->zbuf) {
+ /* TODO, blend depth (not urgent). */
+ info->z = ibuf->zbuf[y * ibuf->x + x];
+ info->zp = &info->z;
+ if (ibuf->zbuf == (int *)ibuf->rect) {
+ info->colp = NULL;
+ }
+ }
+ if (ibuf->zbuf_float) {
+ /* TODO, blend depth (not urgent). */
+ info->zf = ibuf->zbuf_float[y * ibuf->x + x];
+ info->zfp = &info->zf;
+ if (ibuf->zbuf_float == ibuf->rect_float) {
+ info->colfp = NULL;
+ }
+ }
+
+ if (curve_mapping && ibuf->channels == 4) {
+ /* we reuse this callback for set curves point operators */
+ if (RNA_struct_find_property(op->ptr, "point")) {
+ int point = RNA_enum_get(op->ptr, "point");
+
+ if (point == 1) {
+ BKE_curvemapping_set_black_white(curve_mapping, NULL, info->linearcol);
+ }
+ else if (point == 0) {
+ BKE_curvemapping_set_black_white(curve_mapping, info->linearcol, NULL);
+ }
+ WM_event_add_notifier(C, NC_WINDOW, NULL);
+ }
+ }
+
+ // XXX node curve integration ..
+#if 0
+ {
+ ScrArea *sa, *cur = curarea;
+
+ node_curvemap_sample(fp); /* sends global to node editor */
+ for (sa = G.curscreen->areabase.first; sa; sa = sa->next) {
+ if (sa->spacetype == SPACE_NODE) {
+ areawinset(sa->win);
+ scrarea_do_windraw(sa);
+ }
+ }
+ node_curvemap_sample(NULL); /* clears global in node editor */
+ curarea = cur;
+ }
+#endif
+ }
+ else {
+ info->draw = 0;
+ }
+
+ ED_space_image_release_buffer(sima, ibuf, lock);
+ ED_area_tag_redraw(CTX_wm_area(C));
+}
+
+static void sequencer_sample_apply(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ Main *bmain = CTX_data_main(C);
+ struct Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
+ Scene *scene = CTX_data_scene(C);
+ SpaceSeq *sseq = (SpaceSeq *)CTX_wm_space_data(C);
+ ARegion *region = CTX_wm_region(C);
+ ImBuf *ibuf = sequencer_ibuf_get(bmain, depsgraph, scene, sseq, CFRA, 0, NULL);
+ ImageSampleInfo *info = op->customdata;
+ float fx, fy;
+
+ if (ibuf == NULL) {
+ IMB_freeImBuf(ibuf);
+ info->draw = 0;
+ return;
+ }
+
+ UI_view2d_region_to_view(&region->v2d, event->mval[0], event->mval[1], &fx, &fy);
+
+ fx /= scene->r.xasp / scene->r.yasp;
+
+ fx += (float)scene->r.xsch / 2.0f;
+ fy += (float)scene->r.ysch / 2.0f;
+ fx *= (float)ibuf->x / (float)scene->r.xsch;
+ fy *= (float)ibuf->y / (float)scene->r.ysch;
+
+ if (fx >= 0.0f && fy >= 0.0f && fx < ibuf->x && fy < ibuf->y) {
+ const float *fp;
+ unsigned char *cp;
+ int x = (int)fx, y = (int)fy;
+
+ info->x = x;
+ info->y = y;
+ info->draw = 1;
+ info->channels = ibuf->channels;
+
+ info->colp = NULL;
+ info->colfp = NULL;
+
+ if (ibuf->rect) {
+ cp = (unsigned char *)(ibuf->rect + y * ibuf->x + x);
+
+ info->col[0] = cp[0];
+ info->col[1] = cp[1];
+ info->col[2] = cp[2];
+ info->col[3] = cp[3];
+ info->colp = info->col;
+
+ info->colf[0] = (float)cp[0] / 255.0f;
+ info->colf[1] = (float)cp[1] / 255.0f;
+ info->colf[2] = (float)cp[2] / 255.0f;
+ info->colf[3] = (float)cp[3] / 255.0f;
+ info->colfp = info->colf;
+
+ copy_v4_v4(info->linearcol, info->colf);
+ IMB_colormanagement_colorspace_to_scene_linear_v4(
+ info->linearcol, false, ibuf->rect_colorspace);
+
+ info->color_manage = true;
+ }
+ if (ibuf->rect_float) {
+ fp = (ibuf->rect_float + (ibuf->channels) * (y * ibuf->x + x));
+
+ info->colf[0] = fp[0];
+ info->colf[1] = fp[1];
+ info->colf[2] = fp[2];
+ info->colf[3] = fp[3];
+ info->colfp = info->colf;
+
+ /* sequencer's image buffers are in non-linear space, need to make them linear */
+ copy_v4_v4(info->linearcol, info->colf);
+ BKE_sequencer_pixel_from_sequencer_space_v4(scene, info->linearcol);
+
+ info->color_manage = true;
+ }
+ }
+ else {
+ info->draw = 0;
+ }
+
+ IMB_freeImBuf(ibuf);
+ ED_area_tag_redraw(CTX_wm_area(C));
+}
+
+static void ed_imbuf_sample_apply(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ ScrArea *sa = CTX_wm_area(C);
+
+ if (sa && sa->spacetype == SPACE_IMAGE) {
+ image_sample_apply(C, op, event);
+ }
+
+ if (sa && sa->spacetype == SPACE_SEQ) {
+ sequencer_sample_apply(C, op, event);
+ }
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Image Pixel Sample (Public Operator Callback)
+ *
+ * Callbacks for the sample operator, used by sequencer and image spaces.
+ * \{ */
+
+void ED_imbuf_sample_draw(const bContext *C, ARegion *region, void *arg_info)
+{
+ ImageSampleInfo *info = arg_info;
+ if (!info->draw) {
+ return;
+ }
+
+ Scene *scene = CTX_data_scene(C);
+ ED_image_draw_info(scene,
+ region,
+ info->color_manage,
+ info->use_default_view,
+ info->channels,
+ info->x,
+ info->y,
+ info->colp,
+ info->colfp,
+ info->linearcol,
+ info->zp,
+ info->zfp);
+
+ if (info->sample_size > 1) {
+ ScrArea *sa = CTX_wm_area(C);
+
+ if (sa && sa->spacetype == SPACE_IMAGE) {
+
+ const wmWindow *win = CTX_wm_window(C);
+ const wmEvent *event = win->eventstate;
+
+ SpaceImage *sima = CTX_wm_space_image(C);
+ GPUVertFormat *format = immVertexFormat();
+ uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
+
+ const float color[3] = {1, 1, 1};
+ immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immUniformColor3fv(color);
+
+ /* TODO(campbell): lock to pixels. */
+ rctf sample_rect_fl;
+ BLI_rctf_init_pt_radius(
+ &sample_rect_fl,
+ (float[2]){event->x - region->winrct.xmin, event->y - region->winrct.ymin},
+ (float)(info->sample_size / 2.0f) * sima->zoom);
+
+ glEnable(GL_COLOR_LOGIC_OP);
+ glLogicOp(GL_XOR);
+ GPU_line_width(1.0f);
+ imm_draw_box_wire_2d(pos,
+ (float)sample_rect_fl.xmin,
+ (float)sample_rect_fl.ymin,
+ (float)sample_rect_fl.xmax,
+ (float)sample_rect_fl.ymax);
+ glDisable(GL_COLOR_LOGIC_OP);
+
+ immUnbindProgram();
+ }
+ }
+}
+
+void ED_imbuf_sample_exit(bContext *C, wmOperator *op)
+{
+ ImageSampleInfo *info = op->customdata;
+
+ ED_region_draw_cb_exit(info->art, info->draw_handle);
+ ED_area_tag_redraw(CTX_wm_area(C));
+ MEM_freeN(info);
+}
+
+int ED_imbuf_sample_invoke(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ ARegion *region = CTX_wm_region(C);
+ ImageSampleInfo *info;
+
+ info = MEM_callocN(sizeof(ImageSampleInfo), "ImageSampleInfo");
+
+ info->art = region->type;
+ info->draw_handle = ED_region_draw_cb_activate(
+ region->type, ED_imbuf_sample_draw, info, REGION_DRAW_POST_PIXEL);
+ info->sample_size = RNA_int_get(op->ptr, "size");
+ op->customdata = info;
+
+ ScrArea *sa = CTX_wm_area(C);
+
+ if (sa && sa->spacetype == SPACE_IMAGE) {
+ SpaceImage *sima = CTX_wm_space_image(C);
+
+ if (region->regiontype == RGN_TYPE_WINDOW) {
+ if (event->mval[1] <= 16 && ED_space_image_show_cache(sima)) {
+ return OPERATOR_PASS_THROUGH;
+ }
+ }
+
+ if (!ED_space_image_has_buffer(sima)) {
+ return OPERATOR_CANCELLED;
+ }
+ }
+
+ ed_imbuf_sample_apply(C, op, event);
+
+ WM_event_add_modal_handler(C, op);
+
+ return OPERATOR_RUNNING_MODAL;
+}
+
+int ED_imbuf_sample_modal(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ switch (event->type) {
+ case LEFTMOUSE:
+ case RIGHTMOUSE: // XXX hardcoded
+ if (event->val == KM_RELEASE) {
+ ED_imbuf_sample_exit(C, op);
+ return OPERATOR_CANCELLED;
+ }
+ break;
+ case MOUSEMOVE:
+ ed_imbuf_sample_apply(C, op, event);
+ break;
+ }
+
+ return OPERATOR_RUNNING_MODAL;
+}
+
+void ED_imbuf_sample_cancel(bContext *C, wmOperator *op)
+{
+ ED_imbuf_sample_exit(C, op);
+}
+
+bool ED_imbuf_sample_poll(bContext *C)
+{
+ ScrArea *sa = CTX_wm_area(C);
+
+ if (sa && sa->spacetype == SPACE_IMAGE) {
+ SpaceImage *sima = CTX_wm_space_image(C);
+ if (sima == NULL) {
+ return false;
+ }
+
+ Object *obedit = CTX_data_edit_object(C);
+ if (obedit) {
+ /* Disable when UV editing so it doesn't swallow all click events
+ * (use for setting cursor). */
+ if (ED_space_image_show_uvedit(sima, obedit)) {
+ return false;
+ }
+ }
+ else if (sima->mode != SI_MODE_VIEW) {
+ return false;
+ }
+
+ return true;
+ }
+
+ if (sa && sa->spacetype == SPACE_SEQ) {
+ SpaceSeq *sseq = CTX_wm_space_seq(C);
+
+ if (sseq->mainb != SEQ_DRAW_IMG_IMBUF) {
+ return false;
+ }
+
+ return sseq && BKE_sequencer_editing_get(CTX_data_scene(C), false) != NULL;
+ }
+
+ return false;
+}
+
+/** \} */
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_fractal_noise.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_fractal_noise.glsl
index 701b07b4aae..f25691c1a83 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_fractal_noise.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_fractal_noise.glsl
@@ -1,111 +1,115 @@
/* The fractal_noise functions are all exactly the same except for the input type. */
-float fractal_noise(float p, float octaves)
+float fractal_noise(float p, float octaves, float roughness)
{
float fscale = 1.0;
float amp = 1.0;
+ float maxamp = 0.0;
float sum = 0.0;
octaves = clamp(octaves, 0.0, 16.0);
int n = int(octaves);
for (int i = 0; i <= n; i++) {
float t = noise(fscale * p);
sum += t * amp;
- amp *= 0.5;
+ maxamp += amp;
+ amp *= clamp(roughness, 0.0, 1.0);
fscale *= 2.0;
}
float rmd = octaves - floor(octaves);
if (rmd != 0.0) {
float t = noise(fscale * p);
float sum2 = sum + t * amp;
- sum *= (float(1 << n) / float((1 << (n + 1)) - 1));
- sum2 *= (float(1 << (n + 1)) / float((1 << (n + 2)) - 1));
+ sum /= maxamp;
+ sum2 /= maxamp + amp;
return (1.0 - rmd) * sum + rmd * sum2;
}
else {
- sum *= (float(1 << n) / float((1 << (n + 1)) - 1));
- return sum;
+ return sum / maxamp;
}
}
/* The fractal_noise functions are all exactly the same except for the input type. */
-float fractal_noise(vec2 p, float octaves)
+float fractal_noise(vec2 p, float octaves, float roughness)
{
float fscale = 1.0;
float amp = 1.0;
+ float maxamp = 0.0;
float sum = 0.0;
octaves = clamp(octaves, 0.0, 16.0);
int n = int(octaves);
for (int i = 0; i <= n; i++) {
float t = noise(fscale * p);
sum += t * amp;
- amp *= 0.5;
+ maxamp += amp;
+ amp *= clamp(roughness, 0.0, 1.0);
fscale *= 2.0;
}
float rmd = octaves - floor(octaves);
if (rmd != 0.0) {
float t = noise(fscale * p);
float sum2 = sum + t * amp;
- sum *= (float(1 << n) / float((1 << (n + 1)) - 1));
- sum2 *= (float(1 << (n + 1)) / float((1 << (n + 2)) - 1));
+ sum /= maxamp;
+ sum2 /= maxamp + amp;
return (1.0 - rmd) * sum + rmd * sum2;
}
else {
- sum *= (float(1 << n) / float((1 << (n + 1)) - 1));
- return sum;
+ return sum / maxamp;
}
}
/* The fractal_noise functions are all exactly the same except for the input type. */
-float fractal_noise(vec3 p, float octaves)
+float fractal_noise(vec3 p, float octaves, float roughness)
{
float fscale = 1.0;
float amp = 1.0;
+ float maxamp = 0.0;
float sum = 0.0;
octaves = clamp(octaves, 0.0, 16.0);
int n = int(octaves);
for (int i = 0; i <= n; i++) {
float t = noise(fscale * p);
sum += t * amp;
- amp *= 0.5;
+ maxamp += amp;
+ amp *= clamp(roughness, 0.0, 1.0);
fscale *= 2.0;
}
float rmd = octaves - floor(octaves);
if (rmd != 0.0) {
float t = noise(fscale * p);
float sum2 = sum + t * amp;
- sum *= (float(1 << n) / float((1 << (n + 1)) - 1));
- sum2 *= (float(1 << (n + 1)) / float((1 << (n + 2)) - 1));
+ sum /= maxamp;
+ sum2 /= maxamp + amp;
return (1.0 - rmd) * sum + rmd * sum2;
}
else {
- sum *= (float(1 << n) / float((1 << (n + 1)) - 1));
- return sum;
+ return sum / maxamp;
}
}
/* The fractal_noise functions are all exactly the same except for the input type. */
-float fractal_noise(vec4 p, float octaves)
+float fractal_noise(vec4 p, float octaves, float roughness)
{
float fscale = 1.0;
float amp = 1.0;
+ float maxamp = 0.0;
float sum = 0.0;
octaves = clamp(octaves, 0.0, 16.0);
int n = int(octaves);
for (int i = 0; i <= n; i++) {
float t = noise(fscale * p);
sum += t * amp;
- amp *= 0.5;
+ maxamp += amp;
+ amp *= clamp(roughness, 0.0, 1.0);
fscale *= 2.0;
}
float rmd = octaves - floor(octaves);
if (rmd != 0.0) {
float t = noise(fscale * p);
float sum2 = sum + t * amp;
- sum *= (float(1 << n) / float((1 << (n + 1)) - 1));
- sum2 *= (float(1 << (n + 1)) / float((1 << (n + 2)) - 1));
+ sum /= maxamp;
+ sum2 /= maxamp + amp;
return (1.0 - rmd) * sum + rmd * sum2;
}
else {
- sum *= (float(1 << n) / float((1 << (n + 1)) - 1));
- return sum;
+ return sum / maxamp;
}
}
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_tex_noise.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_tex_noise.glsl
index 6aeb23b1f99..d8d9ecdf287 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_tex_noise.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_tex_noise.glsl
@@ -32,23 +32,35 @@ vec4 random_vec4_offset(float seed)
100.0 + hash_vec2_to_float(vec2(seed, 3.0)) * 100.0);
}
-void node_noise_texture_1d(
- vec3 co, float w, float scale, float detail, float distortion, out float value, out vec4 color)
+void node_noise_texture_1d(vec3 co,
+ float w,
+ float scale,
+ float detail,
+ float roughness,
+ float distortion,
+ out float value,
+ out vec4 color)
{
float p = w * scale;
if (distortion != 0.0) {
p += snoise(p + random_float_offset(0.0)) * distortion;
}
- value = fractal_noise(p, detail);
+ value = fractal_noise(p, detail, roughness);
color = vec4(value,
- fractal_noise(p + random_float_offset(1.0), detail),
- fractal_noise(p + random_float_offset(2.0), detail),
+ fractal_noise(p + random_float_offset(1.0), detail, roughness),
+ fractal_noise(p + random_float_offset(2.0), detail, roughness),
1.0);
}
-void node_noise_texture_2d(
- vec3 co, float w, float scale, float detail, float distortion, out float value, out vec4 color)
+void node_noise_texture_2d(vec3 co,
+ float w,
+ float scale,
+ float detail,
+ float roughness,
+ float distortion,
+ out float value,
+ out vec4 color)
{
vec2 p = co.xy * scale;
if (distortion != 0.0) {
@@ -56,15 +68,21 @@ void node_noise_texture_2d(
snoise(p + random_vec2_offset(1.0)) * distortion);
}
- value = fractal_noise(p, detail);
+ value = fractal_noise(p, detail, roughness);
color = vec4(value,
- fractal_noise(p + random_vec2_offset(2.0), detail),
- fractal_noise(p + random_vec2_offset(3.0), detail),
+ fractal_noise(p + random_vec2_offset(2.0), detail, roughness),
+ fractal_noise(p + random_vec2_offset(3.0), detail, roughness),
1.0);
}
-void node_noise_texture_3d(
- vec3 co, float w, float scale, float detail, float distortion, out float value, out vec4 color)
+void node_noise_texture_3d(vec3 co,
+ float w,
+ float scale,
+ float detail,
+ float roughness,
+ float distortion,
+ out float value,
+ out vec4 color)
{
vec3 p = co * scale;
if (distortion != 0.0) {
@@ -73,15 +91,21 @@ void node_noise_texture_3d(
snoise(p + random_vec3_offset(2.0)) * distortion);
}
- value = fractal_noise(p, detail);
+ value = fractal_noise(p, detail, roughness);
color = vec4(value,
- fractal_noise(p + random_vec3_offset(3.0), detail),
- fractal_noise(p + random_vec3_offset(4.0), detail),
+ fractal_noise(p + random_vec3_offset(3.0), detail, roughness),
+ fractal_noise(p + random_vec3_offset(4.0), detail, roughness),
1.0);
}
-void node_noise_texture_4d(
- vec3 co, float w, float scale, float detail, float distortion, out float value, out vec4 color)
+void node_noise_texture_4d(vec3 co,
+ float w,
+ float scale,
+ float detail,
+ float roughness,
+ float distortion,
+ out float value,
+ out vec4 color)
{
vec4 p = vec4(co, w) * scale;
if (distortion != 0.0) {
@@ -91,9 +115,9 @@ void node_noise_texture_4d(
snoise(p + random_vec4_offset(3.0)) * distortion);
}
- value = fractal_noise(p, detail);
+ value = fractal_noise(p, detail, roughness);
color = vec4(value,
- fractal_noise(p + random_vec4_offset(4.0), detail),
- fractal_noise(p + random_vec4_offset(5.0), detail),
+ fractal_noise(p + random_vec4_offset(4.0), detail, roughness),
+ fractal_noise(p + random_vec4_offset(5.0), detail, roughness),
1.0);
}
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_tex_wave.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_tex_wave.glsl
index c72f9717af3..070f42a5e30 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_tex_wave.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_tex_wave.glsl
@@ -2,6 +2,7 @@ float calc_wave(vec3 p,
float distortion,
float detail,
float detail_scale,
+ float detail_roughness,
float phase,
int wave_type,
int bands_dir,
@@ -46,7 +47,7 @@ float calc_wave(vec3 p,
n += phase;
if (distortion != 0.0) {
- n += distortion * (fractal_noise(p * detail_scale, detail) * 2.0 - 1.0);
+ n += distortion * (fractal_noise(p * detail_scale, detail, detail_roughness) * 2.0 - 1.0);
}
if (wave_profile == 0) { /* profile sin */
@@ -67,6 +68,7 @@ void node_tex_wave(vec3 co,
float distortion,
float detail,
float detail_scale,
+ float detail_roughness,
float phase,
float wave_type,
float bands_dir,
@@ -80,6 +82,7 @@ void node_tex_wave(vec3 co,
distortion,
detail,
detail_scale,
+ detail_roughness,
phase,
int(wave_type),
int(bands_dir),
diff --git a/source/blender/imbuf/intern/imageprocess.c b/source/blender/imbuf/intern/imageprocess.c
index 523b57cc162..aa82487d69f 100644
--- a/source/blender/imbuf/intern/imageprocess.c
+++ b/source/blender/imbuf/intern/imageprocess.c
@@ -360,7 +360,7 @@ void IMB_processor_apply_threaded(
int total_tasks = (buffer_lines + lines_per_task - 1) / lines_per_task;
int i, start_line;
- task_pool = BLI_task_pool_create(task_scheduler, do_thread);
+ task_pool = BLI_task_pool_create(task_scheduler, do_thread, TASK_PRIORITY_LOW);
handles = MEM_callocN(handle_size * total_tasks, "processor apply threaded handles");
@@ -379,7 +379,7 @@ void IMB_processor_apply_threaded(
init_handle(handle, start_line, lines_per_current_task, init_customdata);
- BLI_task_pool_push(task_pool, processor_apply_func, handle, false, TASK_PRIORITY_LOW);
+ BLI_task_pool_push(task_pool, processor_apply_func, handle, false, NULL);
start_line += lines_per_task;
}
@@ -421,13 +421,10 @@ void IMB_processor_apply_threaded_scanlines(int total_scanlines,
data.total_scanlines = total_scanlines;
const int total_tasks = (total_scanlines + scanlines_per_task - 1) / scanlines_per_task;
TaskScheduler *task_scheduler = BLI_task_scheduler_get();
- TaskPool *task_pool = BLI_task_pool_create(task_scheduler, &data);
+ TaskPool *task_pool = BLI_task_pool_create(task_scheduler, &data, TASK_PRIORITY_LOW);
for (int i = 0, start_line = 0; i < total_tasks; i++) {
- BLI_task_pool_push(task_pool,
- processor_apply_scanline_func,
- POINTER_FROM_INT(start_line),
- false,
- TASK_PRIORITY_LOW);
+ BLI_task_pool_push(
+ task_pool, processor_apply_scanline_func, POINTER_FROM_INT(start_line), false, NULL);
start_line += scanlines_per_task;
}
diff --git a/source/blender/io/usd/intern/abstract_hierarchy_iterator.cc b/source/blender/io/usd/intern/abstract_hierarchy_iterator.cc
index 71cab020e57..d837d3d8dd3 100644
--- a/source/blender/io/usd/intern/abstract_hierarchy_iterator.cc
+++ b/source/blender/io/usd/intern/abstract_hierarchy_iterator.cc
@@ -21,6 +21,7 @@
#include <iostream>
#include <limits.h>
#include <sstream>
+#include <stdio.h>
#include <string>
extern "C" {
@@ -259,24 +260,22 @@ void AbstractHierarchyIterator::connect_loose_objects()
for (const ExportGraph::value_type &map_iter : loose_objects_graph) {
const DupliAndDuplicator &export_info = map_iter.first;
Object *object = export_info.first;
- Object *export_parent = object->parent;
while (true) {
// Loose objects will all be real objects, as duplicated objects always have
// their duplicator or other exported duplicated object as ancestor.
- ExportGraph::iterator found_parent_iter = export_graph_.find(
- std::make_pair(export_parent, nullptr));
- visit_object(object, export_parent, true);
+ ExportGraph::iterator found_parent_iter = export_graph_.find(
+ std::make_pair(object->parent, nullptr));
+ visit_object(object, object->parent, true);
if (found_parent_iter != export_graph_.end()) {
break;
}
- // 'export_parent' will never be nullptr here, as the export graph contains the
+ // 'object->parent' will never be nullptr here, as the export graph contains the
// tuple <nullptr, nullptr> as root and thus will cause a break.
- BLI_assert(export_parent != nullptr);
+ BLI_assert(object->parent != nullptr);
- object = export_parent;
- export_parent = export_parent->parent;
+ object = object->parent;
}
}
}
@@ -346,7 +345,18 @@ void AbstractHierarchyIterator::visit_object(Object *object,
context->original_export_path = "";
copy_m4_m4(context->matrix_world, object->obmat);
+ // Store this HierarchyContext as child of the export parent.
export_graph_[std::make_pair(export_parent, nullptr)].insert(context);
+
+ // Create an empty entry for this object to indicate it is part of the export. This will be used
+ // by connect_loose_objects(). Having such an "indicator" will make it possible to do an O(log n)
+ // check on whether an object is part of the export, rather than having to check all objects in
+ // the map. Note that it's not possible to simply search for (object->parent, nullptr), as the
+ // object's parent in Blender may not be the same as its export-parent.
+ ExportGraph::key_type object_key = std::make_pair(object, nullptr);
+ if (export_graph_.find(object_key) == export_graph_.end()) {
+ export_graph_[object_key] = ExportChildren();
+ }
}
void AbstractHierarchyIterator::visit_dupli_object(DupliObject *dupli_object,
diff --git a/source/blender/makesdna/DNA_ID.h b/source/blender/makesdna/DNA_ID.h
index 1e894d44f87..dd3964dfc15 100644
--- a/source/blender/makesdna/DNA_ID.h
+++ b/source/blender/makesdna/DNA_ID.h
@@ -246,11 +246,16 @@ typedef struct ID {
int icon_id;
int recalc;
/**
- * Used by undo code. Value of recalc is stored there when reading an ID from memfile, and not
- * touched by anything, which means it can be used as 'reference' recalc value for the next undo
- * step, when going backward (i.e. actual undo, redo can just use recalc value directly).
+ * Used by undo code. recalc_after_undo_push contains the changes between the
+ * last undo push and the current state. This is accumulated as IDs are tagged
+ * for update in the depsgraph, and only cleared on undo push.
+ *
+ * recalc_up_to_undo_push is saved to undo memory, and is the value of
+ * recalc_after_undo_push at the time of the undo push. This means it can be
+ * used to find the changes between undo states.
*/
- int recalc_undo_accumulated;
+ int recalc_up_to_undo_push;
+ int recalc_after_undo_push;
/**
* A session-wide unique identifier for a given ID, that remain the same across potential
@@ -258,8 +263,6 @@ typedef struct ID {
*/
unsigned int session_uuid;
- char _pad[4];
-
IDProperty *properties;
/** Reference linked ID which this one overrides. */
diff --git a/source/blender/makesdna/DNA_layer_types.h b/source/blender/makesdna/DNA_layer_types.h
index a9293d18d41..4676e7fa313 100644
--- a/source/blender/makesdna/DNA_layer_types.h
+++ b/source/blender/makesdna/DNA_layer_types.h
@@ -173,6 +173,7 @@ enum {
LAYER_COLLECTION_HOLDOUT = (1 << 5),
LAYER_COLLECTION_INDIRECT_ONLY = (1 << 6),
LAYER_COLLECTION_HIDE = (1 << 7),
+ LAYER_COLLECTION_PREVIOUSLY_EXCLUDED = (1 << 8),
};
/* Layer Collection->runtime_flag
diff --git a/source/blender/makesdna/DNA_modifier_types.h b/source/blender/makesdna/DNA_modifier_types.h
index 61d9ee79ec5..8bbc2493632 100644
--- a/source/blender/makesdna/DNA_modifier_types.h
+++ b/source/blender/makesdna/DNA_modifier_types.h
@@ -142,6 +142,7 @@ typedef struct MappingInfoModifierData {
struct Tex *texture;
struct Object *map_object;
+ char map_bone[64];
/** MAX_CUSTOMDATA_LAYER_NAME. */
char uvlayer_name[64];
int uvlayer_tmp;
@@ -500,6 +501,7 @@ typedef struct DisplaceModifierData {
/* keep in sync with MappingInfoModifierData */
struct Tex *texture;
struct Object *map_object;
+ char map_bone[64];
/** MAX_CUSTOMDATA_LAYER_NAME. */
char uvlayer_name[64];
int uvlayer_tmp;
@@ -655,6 +657,7 @@ typedef struct WaveModifierData {
/* keep in sync with MappingInfoModifierData */
struct Tex *texture;
struct Object *map_object;
+ char map_bone[64];
/** MAX_CUSTOMDATA_LAYER_NAME. */
char uvlayer_name[64];
int uvlayer_tmp;
@@ -1176,6 +1179,9 @@ typedef struct SolidifyModifierData {
int flag;
short mat_ofs;
short mat_ofs_rim;
+
+ float merge_tolerance;
+ char _pad1[4];
} SolidifyModifierData;
/** #SolidifyModifierData.flag */
@@ -1322,6 +1328,7 @@ typedef struct WarpModifierData {
/* keep in sync with MappingInfoModifierData */
struct Tex *texture;
struct Object *map_object;
+ char map_bone[64];
/** MAX_CUSTOMDATA_LAYER_NAME. */
char uvlayer_name[64];
int uvlayer_tmp;
@@ -1397,6 +1404,8 @@ typedef struct WeightVGEditModifierData {
struct Tex *mask_texture;
/** Name of the map object. */
struct Object *mask_tex_map_obj;
+ /** Name of the map bone. */
+ char mask_tex_map_bone[64];
/** How to map the texture (using MOD_DISP_MAP_* enums). */
int mask_tex_mapping;
/** Name of the UV map. MAX_CUSTOMDATA_LAYER_NAME. */
@@ -1448,6 +1457,8 @@ typedef struct WeightVGMixModifierData {
struct Tex *mask_texture;
/** Name of the map object. */
struct Object *mask_tex_map_obj;
+ /** Name of the map bone. */
+ char mask_tex_map_bone[64];
/** How to map the texture!. */
int mask_tex_mapping;
/** Name of the UV map. MAX_CUSTOMDATA_LAYER_NAME. */
@@ -1522,6 +1533,8 @@ typedef struct WeightVGProximityModifierData {
struct Tex *mask_texture;
/** Name of the map object. */
struct Object *mask_tex_map_obj;
+ /** Name of the map bone. */
+ char mask_tex_map_bone[64];
/** How to map the texture!. */
int mask_tex_mapping;
/** Name of the UV Map. MAX_CUSTOMDATA_LAYER_NAME. */
diff --git a/source/blender/makesrna/intern/rna_gpencil.c b/source/blender/makesrna/intern/rna_gpencil.c
index 94814003e99..4a20df048bd 100644
--- a/source/blender/makesrna/intern/rna_gpencil.c
+++ b/source/blender/makesrna/intern/rna_gpencil.c
@@ -716,10 +716,13 @@ static bGPDstroke *rna_GPencil_stroke_new(bGPDframe *frame)
return stroke;
}
-static void rna_GPencil_stroke_remove(bGPDframe *frame,
+static void rna_GPencil_stroke_remove(ID *id,
+ bGPDframe *frame,
ReportList *reports,
PointerRNA *stroke_ptr)
{
+ bGPdata *gpd = (bGPdata *)id;
+
bGPDstroke *stroke = stroke_ptr->data;
if (BLI_findindex(&frame->strokes, stroke) == -1) {
BKE_report(reports, RPT_ERROR, "Stroke not found in grease pencil frame");
@@ -729,7 +732,8 @@ static void rna_GPencil_stroke_remove(bGPDframe *frame,
BLI_freelinkN(&frame->strokes, stroke);
RNA_POINTER_INVALIDATE(stroke_ptr);
- WM_main_add_notifier(NC_GPENCIL | NA_EDITED, NULL);
+ DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY | ID_RECALC_COPY_ON_WRITE);
+ WM_main_add_notifier(NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
}
static void rna_GPencil_stroke_close(ID *id,
@@ -1282,7 +1286,7 @@ static void rna_def_gpencil_strokes_api(BlenderRNA *brna, PropertyRNA *cprop)
func = RNA_def_function(srna, "remove", "rna_GPencil_stroke_remove");
RNA_def_function_ui_description(func, "Remove a grease pencil stroke");
- RNA_def_function_flag(func, FUNC_USE_REPORTS);
+ RNA_def_function_flag(func, FUNC_USE_REPORTS | FUNC_USE_SELF_ID);
parm = RNA_def_pointer(func, "stroke", "GPencilStroke", "Stroke", "The stroke to remove");
RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED | PARM_RNAPTR);
RNA_def_parameter_clear_flags(parm, PROP_THICK_WRAP, 0);
diff --git a/source/blender/makesrna/intern/rna_layer.c b/source/blender/makesrna/intern/rna_layer.c
index 46a32256114..b99457056fe 100644
--- a/source/blender/makesrna/intern/rna_layer.c
+++ b/source/blender/makesrna/intern/rna_layer.c
@@ -299,19 +299,6 @@ static void rna_LayerCollection_hide_viewport_set(PointerRNA *ptr, bool value)
rna_LayerCollection_flag_set(ptr, value, LAYER_COLLECTION_HIDE);
}
-static void rna_LayerCollection_exclude_update_recursive(ListBase *lb, const bool exclude)
-{
- LISTBASE_FOREACH (LayerCollection *, lc, lb) {
- if (exclude) {
- lc->flag |= LAYER_COLLECTION_EXCLUDE;
- }
- else {
- lc->flag &= ~LAYER_COLLECTION_EXCLUDE;
- }
- rna_LayerCollection_exclude_update_recursive(&lc->layer_collections, exclude);
- }
-}
-
static void rna_LayerCollection_exclude_update(Main *bmain, Scene *UNUSED(scene), PointerRNA *ptr)
{
Scene *scene = (Scene *)ptr->owner_id;
@@ -320,7 +307,7 @@ static void rna_LayerCollection_exclude_update(Main *bmain, Scene *UNUSED(scene)
/* Set/Unset it recursively to match the behavior of excluding via the menu or shortcuts. */
const bool exclude = (lc->flag & LAYER_COLLECTION_EXCLUDE) != 0;
- rna_LayerCollection_exclude_update_recursive(&lc->layer_collections, exclude);
+ BKE_layer_collection_set_flag(lc, LAYER_COLLECTION_EXCLUDE, exclude);
BKE_layer_collection_sync(scene, view_layer);
diff --git a/source/blender/makesrna/intern/rna_modifier.c b/source/blender/makesrna/intern/rna_modifier.c
index a951c0731a6..1130b822f75 100644
--- a/source/blender/makesrna/intern/rna_modifier.c
+++ b/source/blender/makesrna/intern/rna_modifier.c
@@ -1797,6 +1797,11 @@ static void rna_def_modifier_generic_map_info(StructRNA *srna)
RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_SELF_CHECK);
RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_update(prop, 0, "rna_Modifier_dependency_update");
+
+ prop = RNA_def_property(srna, "texture_coords_bone", PROP_STRING, PROP_NONE);
+ RNA_def_property_string_sdna(prop, NULL, "map_bone");
+ RNA_def_property_ui_text(prop, "Texture Coordinate Bone", "Bone to set the texture coordinates");
+ RNA_def_property_update(prop, 0, "rna_Modifier_dependency_update");
}
static void rna_def_modifier_warp(BlenderRNA *brna)
@@ -4530,6 +4535,14 @@ static void rna_def_modifier_solidify(BlenderRNA *brna)
RNA_def_property_enum_items(prop, nonmanifold_boundary_mode_items);
RNA_def_property_ui_text(prop, "Boundary Shape", "Selects the boundary adjustment algorithm");
RNA_def_property_update(prop, 0, "rna_Modifier_update");
+
+ prop = RNA_def_property(srna, "nonmanifold_merge_threshold", PROP_FLOAT, PROP_DISTANCE);
+ RNA_def_property_float_sdna(prop, NULL, "merge_tolerance");
+ RNA_def_property_range(prop, 0.0, 1.0);
+ RNA_def_property_ui_range(prop, 0.0, 1.0, 0.01, 4);
+ RNA_def_property_ui_text(
+ prop, "Merge Threshold", "Distance within which degenerated geometry is merged");
+ RNA_def_property_update(prop, 0, "rna_Modifier_update");
}
static void rna_def_modifier_screw(BlenderRNA *brna)
@@ -4804,6 +4817,12 @@ static void rna_def_modifier_weightvg_mask(BlenderRNA *UNUSED(brna),
RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_SELF_CHECK);
RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_update(prop, 0, "rna_Modifier_dependency_update");
+
+ prop = RNA_def_property(srna, "mask_tex_map_bone", PROP_STRING, PROP_NONE);
+ RNA_def_property_string_sdna(prop, NULL, "mask_tex_map_bone");
+ RNA_def_property_ui_text(
+ prop, "Texture Coordinate Bone", "Which bone to take texture coordinates from");
+ RNA_def_property_update(prop, 0, "rna_Modifier_dependency_update");
}
static void rna_def_modifier_weightvgedit(BlenderRNA *brna)
diff --git a/source/blender/makesrna/intern/rna_sequencer.c b/source/blender/makesrna/intern/rna_sequencer.c
index 04729f87cab..d3eae4562ec 100644
--- a/source/blender/makesrna/intern/rna_sequencer.c
+++ b/source/blender/makesrna/intern/rna_sequencer.c
@@ -287,11 +287,12 @@ static void do_sequence_frame_change_update(Scene *scene, Sequence *seq)
/* A simple wrapper around above func, directly usable as prop update func.
* Also invalidate cache if needed.
*/
-static void rna_Sequence_frame_change_update(Main *bmain, Scene *UNUSED(scene), PointerRNA *ptr)
+static void rna_Sequence_frame_change_update(Main *UNUSED(bmain),
+ Scene *UNUSED(scene),
+ PointerRNA *ptr)
{
Scene *scene = (Scene *)ptr->owner_id;
do_sequence_frame_change_update(scene, (Sequence *)ptr->data);
- rna_Sequence_invalidate_preprocessed_update(bmain, scene, ptr);
}
static void rna_Sequence_start_frame_set(PointerRNA *ptr, int value)
@@ -299,8 +300,10 @@ static void rna_Sequence_start_frame_set(PointerRNA *ptr, int value)
Sequence *seq = (Sequence *)ptr->data;
Scene *scene = (Scene *)ptr->owner_id;
+ BKE_sequence_invalidate_cache_composite(scene, seq);
BKE_sequence_translate(scene, seq, value - seq->start);
do_sequence_frame_change_update(scene, seq);
+ BKE_sequence_invalidate_cache_composite(scene, seq);
}
static void rna_Sequence_start_frame_final_set(PointerRNA *ptr, int value)
@@ -308,9 +311,11 @@ static void rna_Sequence_start_frame_final_set(PointerRNA *ptr, int value)
Sequence *seq = (Sequence *)ptr->data;
Scene *scene = (Scene *)ptr->owner_id;
+ BKE_sequence_invalidate_cache_composite(scene, seq);
BKE_sequence_tx_set_final_left(seq, value);
BKE_sequence_single_fix(seq);
do_sequence_frame_change_update(scene, seq);
+ BKE_sequence_invalidate_cache_composite(scene, seq);
}
static void rna_Sequence_end_frame_final_set(PointerRNA *ptr, int value)
@@ -318,9 +323,47 @@ static void rna_Sequence_end_frame_final_set(PointerRNA *ptr, int value)
Sequence *seq = (Sequence *)ptr->data;
Scene *scene = (Scene *)ptr->owner_id;
+ BKE_sequence_invalidate_cache_composite(scene, seq);
BKE_sequence_tx_set_final_right(seq, value);
BKE_sequence_single_fix(seq);
do_sequence_frame_change_update(scene, seq);
+ BKE_sequence_invalidate_cache_composite(scene, seq);
+}
+
+static void rna_Sequence_frame_offset_start_set(PointerRNA *ptr, int value)
+{
+ Sequence *seq = (Sequence *)ptr->data;
+ Scene *scene = (Scene *)ptr->owner_id;
+
+ BKE_sequence_invalidate_cache_composite(scene, seq);
+ seq->startofs = value;
+}
+
+static void rna_Sequence_frame_offset_end_set(PointerRNA *ptr, int value)
+{
+ Sequence *seq = (Sequence *)ptr->data;
+ Scene *scene = (Scene *)ptr->owner_id;
+
+ BKE_sequence_invalidate_cache_composite(scene, seq);
+ seq->endofs = value;
+}
+
+static void rna_Sequence_frame_still_start_set(PointerRNA *ptr, int value)
+{
+ Sequence *seq = (Sequence *)ptr->data;
+ Scene *scene = (Scene *)ptr->owner_id;
+
+ BKE_sequence_invalidate_cache_composite(scene, seq);
+ seq->startstill = value;
+}
+
+static void rna_Sequence_frame_still_end_set(PointerRNA *ptr, int value)
+{
+ Sequence *seq = (Sequence *)ptr->data;
+ Scene *scene = (Scene *)ptr->owner_id;
+
+ BKE_sequence_invalidate_cache_composite(scene, seq);
+ seq->endstill = value;
}
static void rna_Sequence_anim_startofs_final_set(PointerRNA *ptr, int value)
@@ -350,8 +393,10 @@ static void rna_Sequence_frame_length_set(PointerRNA *ptr, int value)
Sequence *seq = (Sequence *)ptr->data;
Scene *scene = (Scene *)ptr->owner_id;
+ BKE_sequence_invalidate_cache_composite(scene, seq);
BKE_sequence_tx_set_final_right(seq, BKE_sequence_tx_get_final_left(seq, false) + value);
do_sequence_frame_change_update(scene, seq);
+ BKE_sequence_invalidate_cache_composite(scene, seq);
}
static int rna_Sequence_frame_length_get(PointerRNA *ptr)
@@ -374,6 +419,7 @@ static void rna_Sequence_channel_set(PointerRNA *ptr, int value)
Editing *ed = BKE_sequencer_editing_get(scene, false);
ListBase *seqbase = BKE_sequence_seqbase(&ed->seqbase, seq);
+ BKE_sequence_invalidate_cache_composite(scene, seq);
/* check channel increment or decrement */
const int channel_delta = (value >= seq->machine) ? 1 : -1;
seq->machine = value;
@@ -383,6 +429,7 @@ static void rna_Sequence_channel_set(PointerRNA *ptr, int value)
BKE_sequence_base_shuffle_ex(seqbase, seq, scene, channel_delta);
}
BKE_sequencer_sort(scene);
+ BKE_sequence_invalidate_cache_composite(scene, seq);
}
static void rna_Sequence_frame_offset_range(
@@ -1627,6 +1674,7 @@ static void rna_def_sequence(BlenderRNA *brna)
RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, NULL);
/* strip positioning */
+ /* Cache has to be invalidated before and after transformation. */
prop = RNA_def_property(srna, "frame_final_duration", PROP_INT, PROP_TIME);
RNA_def_property_range(prop, 1, MAXFRAME);
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
@@ -1684,14 +1732,16 @@ static void rna_def_sequence(BlenderRNA *brna)
RNA_def_property_int_sdna(prop, NULL, "startofs");
// RNA_def_property_clear_flag(prop, PROP_EDITABLE); /* overlap tests */
RNA_def_property_ui_text(prop, "Start Offset", "");
- RNA_def_property_int_funcs(prop, NULL, NULL, "rna_Sequence_frame_offset_range");
+ RNA_def_property_int_funcs(
+ prop, NULL, "rna_Sequence_frame_offset_start_set", "rna_Sequence_frame_offset_range");
RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_frame_change_update");
prop = RNA_def_property(srna, "frame_offset_end", PROP_INT, PROP_TIME);
RNA_def_property_int_sdna(prop, NULL, "endofs");
// RNA_def_property_clear_flag(prop, PROP_EDITABLE); /* overlap tests */
RNA_def_property_ui_text(prop, "End Offset", "");
- RNA_def_property_int_funcs(prop, NULL, NULL, "rna_Sequence_frame_offset_range");
+ RNA_def_property_int_funcs(
+ prop, NULL, "rna_Sequence_frame_offset_end_set", "rna_Sequence_frame_offset_range");
RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_frame_change_update");
prop = RNA_def_property(srna, "frame_still_start", PROP_INT, PROP_TIME);
@@ -1699,6 +1749,7 @@ static void rna_def_sequence(BlenderRNA *brna)
// RNA_def_property_clear_flag(prop, PROP_EDITABLE); /* overlap tests */
RNA_def_property_range(prop, 0, MAXFRAME);
RNA_def_property_ui_text(prop, "Start Still", "");
+ RNA_def_property_int_funcs(prop, NULL, "rna_Sequence_frame_still_start_set", NULL);
RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_frame_change_update");
prop = RNA_def_property(srna, "frame_still_end", PROP_INT, PROP_TIME);
@@ -1706,6 +1757,7 @@ static void rna_def_sequence(BlenderRNA *brna)
// RNA_def_property_clear_flag(prop, PROP_EDITABLE); /* overlap tests */
RNA_def_property_range(prop, 0, MAXFRAME);
RNA_def_property_ui_text(prop, "End Still", "");
+ RNA_def_property_int_funcs(prop, NULL, "rna_Sequence_frame_still_end_set", NULL);
RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_frame_change_update");
prop = RNA_def_property(srna, "channel", PROP_INT, PROP_UNSIGNED);
@@ -2467,12 +2519,12 @@ static void rna_def_wipe(StructRNA *srna)
PropertyRNA *prop;
static const EnumPropertyItem wipe_type_items[] = {
- {0, "SINGLE", 0, "Single", ""},
- {1, "DOUBLE", 0, "Double", ""},
- /* not used yet {2, "BOX", 0, "Box", ""}, */
- /* not used yet {3, "CROSS", 0, "Cross", ""}, */
- {4, "IRIS", 0, "Iris", ""},
- {5, "CLOCK", 0, "Clock", ""},
+ {DO_SINGLE_WIPE, "SINGLE", 0, "Single", ""},
+ {DO_DOUBLE_WIPE, "DOUBLE", 0, "Double", ""},
+ /* not used yet {DO_BOX_WIPE, "BOX", 0, "Box", ""}, */
+ /* not used yet {DO_CROSS_WIPE, "CROSS", 0, "Cross", ""}, */
+ {DO_IRIS_WIPE, "IRIS", 0, "Iris", ""},
+ {DO_CLOCK_WIPE, "CLOCK", 0, "Clock", ""},
{0, NULL, 0, NULL, NULL},
};
diff --git a/source/blender/makesrna/intern/rna_sequencer_api.c b/source/blender/makesrna/intern/rna_sequencer_api.c
index d49a2ed1ea0..6c8f51f97a1 100644
--- a/source/blender/makesrna/intern/rna_sequencer_api.c
+++ b/source/blender/makesrna/intern/rna_sequencer_api.c
@@ -193,7 +193,7 @@ static Sequence *rna_Sequences_new_image(ID *id,
if (seq->strip->stripdata->name[0] == '\0') {
BKE_report(reports, RPT_ERROR, "Sequences.new_image: unable to open image file");
BLI_remlink(&ed->seqbase, seq);
- BKE_sequence_free(scene, seq);
+ BKE_sequence_free(scene, seq, true);
return NULL;
}
@@ -382,7 +382,7 @@ static void rna_Sequences_remove(
return;
}
- BKE_sequence_free(scene, seq);
+ BKE_sequence_free(scene, seq, true);
RNA_POINTER_INVALIDATE(seq_ptr);
DEG_relations_tag_update(bmain);
diff --git a/source/blender/modifiers/intern/MOD_displace.c b/source/blender/modifiers/intern/MOD_displace.c
index a1b02e201d9..19a4e855153 100644
--- a/source/blender/modifiers/intern/MOD_displace.c
+++ b/source/blender/modifiers/intern/MOD_displace.c
@@ -134,19 +134,28 @@ static bool isDisabled(const struct Scene *UNUSED(scene),
static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphContext *ctx)
{
DisplaceModifierData *dmd = (DisplaceModifierData *)md;
- if (dmd->map_object != NULL && dmd->texmapping == MOD_DISP_MAP_OBJECT) {
- DEG_add_modifier_to_transform_relation(ctx->node, "Displace Modifier");
- DEG_add_object_relation(
- ctx->node, dmd->map_object, DEG_OB_COMP_TRANSFORM, "Displace Modifier");
- }
- if (dmd->texmapping == MOD_DISP_MAP_GLOBAL ||
- (ELEM(
- dmd->direction, MOD_DISP_DIR_X, MOD_DISP_DIR_Y, MOD_DISP_DIR_Z, MOD_DISP_DIR_RGB_XYZ) &&
- dmd->space == MOD_DISP_SPACE_GLOBAL)) {
- DEG_add_modifier_to_transform_relation(ctx->node, "Displace Modifier");
+ bool need_transform_relation = false;
+
+ if (dmd->space == MOD_DISP_SPACE_GLOBAL &&
+ ELEM(dmd->direction, MOD_DISP_DIR_X, MOD_DISP_DIR_Y, MOD_DISP_DIR_Z, MOD_DISP_DIR_RGB_XYZ)) {
+ need_transform_relation = true;
}
+
if (dmd->texture != NULL) {
DEG_add_generic_id_relation(ctx->node, &dmd->texture->id, "Displace Modifier");
+
+ if (dmd->map_object != NULL && dmd->texmapping == MOD_DISP_MAP_OBJECT) {
+ MOD_depsgraph_update_object_bone_relation(
+ ctx->node, dmd->map_object, dmd->map_bone, "Displace Modifier");
+ need_transform_relation = true;
+ }
+ if (dmd->texmapping == MOD_DISP_MAP_GLOBAL) {
+ need_transform_relation = true;
+ }
+ }
+
+ if (need_transform_relation) {
+ DEG_add_modifier_to_transform_relation(ctx->node, "Displace Modifier");
}
}
diff --git a/source/blender/modifiers/intern/MOD_solidify.c b/source/blender/modifiers/intern/MOD_solidify.c
index 0eed2335ed7..74d9df7d093 100644
--- a/source/blender/modifiers/intern/MOD_solidify.c
+++ b/source/blender/modifiers/intern/MOD_solidify.c
@@ -53,6 +53,7 @@ static void initData(ModifierData *md)
smd->mode = MOD_SOLIDIFY_MODE_EXTRUDE;
smd->nonmanifold_offset_mode = MOD_SOLIDIFY_NONMANIFOLD_OFFSET_MODE_CONSTRAINTS;
smd->nonmanifold_boundary_mode = MOD_SOLIDIFY_NONMANIFOLD_BOUNDARY_MODE_NONE;
+ smd->merge_tolerance = 0.0001f;
}
static void requiredDataMask(Object *UNUSED(ob),
diff --git a/source/blender/modifiers/intern/MOD_solidify_nonmanifold.c b/source/blender/modifiers/intern/MOD_solidify_nonmanifold.c
index 0f9f5952a26..1cf9def46fa 100644
--- a/source/blender/modifiers/intern/MOD_solidify_nonmanifold.c
+++ b/source/blender/modifiers/intern/MOD_solidify_nonmanifold.c
@@ -291,6 +291,15 @@ Mesh *MOD_solidify_nonmanifold_applyModifier(ModifierData *md,
/* Vert edge adjacent map. */
OldVertEdgeRef **vert_adj_edges = MEM_calloc_arrayN(
numVerts, sizeof(*vert_adj_edges), "vert_adj_edges in solidify");
+ /* Original vertex positions (changed for degenerated geometry). */
+ float(*orig_mvert_co)[3] = MEM_malloc_arrayN(
+ numVerts, sizeof(*orig_mvert_co), "orig_mvert_co in solidify");
+ /* Fill in the original vertex positions. */
+ for (uint i = 0; i < numVerts; i++) {
+ orig_mvert_co[i][0] = orig_mvert[i].co[0];
+ orig_mvert_co[i][1] = orig_mvert[i].co[1];
+ orig_mvert_co[i][2] = orig_mvert[i].co[2];
+ }
/* Create edge to #NewEdgeRef map. */
{
@@ -344,33 +353,35 @@ Mesh *MOD_solidify_nonmanifold_applyModifier(ModifierData *md,
bool *face_singularity = MEM_calloc_arrayN(
numPolys, sizeof(*face_singularity), "face_sides_arr in solidify");
+ const float merge_tolerance_sqr = smd->merge_tolerance * smd->merge_tolerance;
+ uint *combined_verts = MEM_calloc_arrayN(
+ numVerts, sizeof(*combined_verts), "combined_verts in solidify");
+
ed = orig_medge;
for (uint i = 0; i < numEdges; i++, ed++) {
if (edge_adj_faces_len[i] > 0) {
- const uint v1 = vm[ed->v1];
- const uint v2 = vm[ed->v2];
+ uint v1 = vm[ed->v1];
+ uint v2 = vm[ed->v2];
if (v1 != v2) {
- sub_v3_v3v3(edgedir, orig_mvert[v2].co, orig_mvert[v1].co);
+ if (v2 < v1) {
+ SWAP(uint, v1, v2);
+ }
+ sub_v3_v3v3(edgedir, orig_mvert_co[v2], orig_mvert_co[v1]);
orig_edge_lengths[i] = len_squared_v3(edgedir);
- if (orig_edge_lengths[i] <= FLT_EPSILON) {
- if (v2 > v1) {
- for (uint j = v2; j < numVerts; j++) {
- if (vm[j] == v2) {
- vm[j] = v1;
- vert_adj_edges_len[v1] += vert_adj_edges_len[j];
- vert_adj_edges_len[j] = 0;
- }
- }
- }
- else if (v2 < v1) {
- for (uint j = v1; j < numVerts; j++) {
- if (vm[j] == v1) {
- vm[j] = v2;
- vert_adj_edges_len[v2] += vert_adj_edges_len[j];
- vert_adj_edges_len[j] = 0;
- }
+ if (orig_edge_lengths[i] <= merge_tolerance_sqr) {
+ mul_v3_fl(edgedir,
+ (combined_verts[v2] + 1) /
+ (float)(combined_verts[v1] + combined_verts[v2] + 2));
+ add_v3_v3(orig_mvert_co[v1], edgedir);
+ for (uint j = v2; j < numVerts; j++) {
+ if (vm[j] == v2) {
+ vm[j] = v1;
}
}
+ vert_adj_edges_len[v1] += vert_adj_edges_len[v2];
+ vert_adj_edges_len[v2] = 0;
+ combined_verts[v1] += combined_verts[v2] + 1;
+
if (do_shell) {
numNewLoops -= edge_adj_faces_len[i] * 2;
}
@@ -429,6 +440,7 @@ Mesh *MOD_solidify_nonmanifold_applyModifier(ModifierData *md,
}
MEM_freeN(face_singularity);
+ MEM_freeN(combined_verts);
}
/* Create vert_adj_edges for verts. */
@@ -668,7 +680,7 @@ Mesh *MOD_solidify_nonmanifold_applyModifier(ModifierData *md,
const uint v1 = vm[ed->v1];
const uint v2 = vm[ed->v2];
if (edge_adj_faces_len[i] > 0) {
- sub_v3_v3v3(edgedir, orig_mvert[v2].co, orig_mvert[v1].co);
+ sub_v3_v3v3(edgedir, orig_mvert_co[v2], orig_mvert_co[v1]);
mul_v3_fl(edgedir, 1.0f / orig_edge_lengths[i]);
OldEdgeFaceRef *adj_faces = edge_adj_faces[i];
@@ -1496,8 +1508,9 @@ Mesh *MOD_solidify_nonmanifold_applyModifier(ModifierData *md,
ml_prev = ml;
ml = ml_next;
}
- angle = angle_v3v3v3(
- orig_mvert[vm[ml_prev->v]].co, mv->co, orig_mvert[vm[ml_next->v]].co);
+ angle = angle_v3v3v3(orig_mvert_co[vm[ml_prev->v]],
+ orig_mvert_co[i],
+ orig_mvert_co[vm[ml_next->v]]);
if (face->reversed) {
total_angle_back += angle * ofs * ofs;
}
@@ -1591,7 +1604,8 @@ Mesh *MOD_solidify_nonmanifold_applyModifier(ModifierData *md,
uint k;
for (k = 1; k + 1 < g->edges_len; k++, edge_ptr++) {
MEdge *e = orig_medge + (*edge_ptr)->old_edge;
- sub_v3_v3v3(tmp, orig_mvert[vm[e->v1] == i ? e->v2 : e->v1].co, mv->co);
+ sub_v3_v3v3(
+ tmp, orig_mvert_co[vm[e->v1] == i ? e->v2 : e->v1], orig_mvert_co[i]);
add_v3_v3(move_nor, tmp);
}
if (k == 1) {
@@ -1613,10 +1627,12 @@ Mesh *MOD_solidify_nonmanifold_applyModifier(ModifierData *md,
MEdge *e1_edge = orig_medge + g->edges[g->edges_len - 1]->old_edge;
float e0[3];
float e1[3];
- sub_v3_v3v3(
- e0, orig_mvert[vm[e0_edge->v1] == i ? e0_edge->v2 : e0_edge->v1].co, mv->co);
- sub_v3_v3v3(
- e1, orig_mvert[vm[e1_edge->v1] == i ? e1_edge->v2 : e1_edge->v1].co, mv->co);
+ sub_v3_v3v3(e0,
+ orig_mvert_co[vm[e0_edge->v1] == i ? e0_edge->v2 : e0_edge->v1],
+ orig_mvert_co[i]);
+ sub_v3_v3v3(e1,
+ orig_mvert_co[vm[e1_edge->v1] == i ? e1_edge->v2 : e1_edge->v1],
+ orig_mvert_co[i]);
if (smd->nonmanifold_boundary_mode == MOD_SOLIDIFY_NONMANIFOLD_BOUNDARY_MODE_FLAT) {
cross_v3_v3v3(constr_nor, e0, e1);
}
@@ -1702,16 +1718,17 @@ Mesh *MOD_solidify_nonmanifold_applyModifier(ModifierData *md,
}
}
mul_v3_fl(nor, scalar_vgroup);
- add_v3_v3v3(g->co, nor, mv->co);
+ add_v3_v3v3(g->co, nor, orig_mvert_co[i]);
}
else {
- copy_v3_v3(g->co, mv->co);
+ copy_v3_v3(g->co, orig_mvert_co[i]);
}
}
}
}
}
+ MEM_freeN(orig_mvert_co);
if (null_faces) {
MEM_freeN(null_faces);
}
diff --git a/source/blender/modifiers/intern/MOD_util.c b/source/blender/modifiers/intern/MOD_util.c
index 0472d8fccd7..21680e77654 100644
--- a/source/blender/modifiers/intern/MOD_util.c
+++ b/source/blender/modifiers/intern/MOD_util.c
@@ -36,6 +36,7 @@
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
+#include "BKE_action.h" /* BKE_pose_channel_find_name */
#include "BKE_deform.h"
#include "BKE_editmesh.h"
#include "BKE_image.h"
@@ -81,12 +82,25 @@ void MOD_get_texture_coords(MappingInfoModifierData *dmd,
const int numVerts = mesh->totvert;
int i;
int texmapping = dmd->texmapping;
- float mapob_imat[4][4];
+ float mapref_imat[4][4];
if (texmapping == MOD_DISP_MAP_OBJECT) {
if (dmd->map_object != NULL) {
Object *map_object = dmd->map_object;
- invert_m4_m4(mapob_imat, map_object->obmat);
+ if (dmd->map_bone[0] != '\0') {
+ bPoseChannel *pchan = BKE_pose_channel_find_name(map_object->pose, dmd->map_bone);
+ if (pchan) {
+ float mat_bone_world[4][4];
+ mul_m4_m4m4(mat_bone_world, map_object->obmat, pchan->pose_mat);
+ invert_m4_m4(mapref_imat, mat_bone_world);
+ }
+ else {
+ invert_m4_m4(mapref_imat, map_object->obmat);
+ }
+ }
+ else {
+ invert_m4_m4(mapref_imat, map_object->obmat);
+ }
}
else { /* if there is no map object, default to local */
texmapping = MOD_DISP_MAP_LOCAL;
@@ -145,7 +159,7 @@ void MOD_get_texture_coords(MappingInfoModifierData *dmd,
break;
case MOD_DISP_MAP_OBJECT:
mul_v3_m4v3(*r_texco, ob->obmat, cos != NULL ? *cos : mv->co);
- mul_m4_v3(mapob_imat, *r_texco);
+ mul_m4_v3(mapref_imat, *r_texco);
break;
}
if (cos != NULL) {
@@ -248,6 +262,22 @@ void MOD_get_vgroup(
}
}
+void MOD_depsgraph_update_object_bone_relation(struct DepsNodeHandle *node,
+ Object *object,
+ const char *bonename,
+ const char *description)
+{
+ if (object == NULL) {
+ return;
+ }
+ if (bonename[0] != '\0' && object->type == OB_ARMATURE) {
+ DEG_add_object_relation(node, object, DEG_OB_COMP_EVAL_POSE, description);
+ }
+ else {
+ DEG_add_object_relation(node, object, DEG_OB_COMP_TRANSFORM, description);
+ }
+}
+
/* only called by BKE_modifier.h/modifier.c */
void modifier_type_init(ModifierTypeInfo *types[])
{
diff --git a/source/blender/modifiers/intern/MOD_util.h b/source/blender/modifiers/intern/MOD_util.h
index e1991de3bb8..38e2083082d 100644
--- a/source/blender/modifiers/intern/MOD_util.h
+++ b/source/blender/modifiers/intern/MOD_util.h
@@ -56,4 +56,8 @@ void MOD_get_vgroup(struct Object *ob,
struct MDeformVert **dvert,
int *defgrp_index);
+void MOD_depsgraph_update_object_bone_relation(struct DepsNodeHandle *node,
+ struct Object *object,
+ const char *bonename,
+ const char *description);
#endif /* __MOD_UTIL_H__ */
diff --git a/source/blender/modifiers/intern/MOD_uvwarp.c b/source/blender/modifiers/intern/MOD_uvwarp.c
index 46298c0e00d..6545159bdde 100644
--- a/source/blender/modifiers/intern/MOD_uvwarp.c
+++ b/source/blender/modifiers/intern/MOD_uvwarp.c
@@ -233,26 +233,14 @@ static void foreachObjectLink(ModifierData *md, Object *ob, ObjectWalkFunc walk,
walk(userData, ob, &umd->object_src, IDWALK_CB_NOP);
}
-static void uv_warp_deps_object_bone_new(struct DepsNodeHandle *node,
- Object *object,
- const char *bonename)
-{
- if (object != NULL) {
- if (object->type == OB_ARMATURE && bonename[0]) {
- DEG_add_object_relation(node, object, DEG_OB_COMP_EVAL_POSE, "UVWarp Modifier");
- }
- else {
- DEG_add_object_relation(node, object, DEG_OB_COMP_TRANSFORM, "UVWarp Modifier");
- }
- }
-}
-
static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphContext *ctx)
{
UVWarpModifierData *umd = (UVWarpModifierData *)md;
- uv_warp_deps_object_bone_new(ctx->node, umd->object_src, umd->bone_src);
- uv_warp_deps_object_bone_new(ctx->node, umd->object_dst, umd->bone_dst);
+ MOD_depsgraph_update_object_bone_relation(
+ ctx->node, umd->object_src, umd->bone_src, "UVWarp Modifier");
+ MOD_depsgraph_update_object_bone_relation(
+ ctx->node, umd->object_dst, umd->bone_dst, "UVWarp Modifier");
DEG_add_modifier_to_transform_relation(ctx->node, "UVWarp Modifier");
}
diff --git a/source/blender/modifiers/intern/MOD_warp.c b/source/blender/modifiers/intern/MOD_warp.c
index c3515578e42..30d45d1fc65 100644
--- a/source/blender/modifiers/intern/MOD_warp.c
+++ b/source/blender/modifiers/intern/MOD_warp.c
@@ -152,35 +152,34 @@ static void foreachTexLink(ModifierData *md, Object *ob, TexWalkFunc walk, void
walk(userData, ob, md, "texture");
}
-static void warp_deps_object_bone_new(struct DepsNodeHandle *node,
- Object *object,
- const char *bonename)
-{
- if (bonename[0] && object->type == OB_ARMATURE) {
- DEG_add_object_relation(node, object, DEG_OB_COMP_EVAL_POSE, "Warp Modifier");
- }
- else {
- DEG_add_object_relation(node, object, DEG_OB_COMP_TRANSFORM, "Warp Modifier");
- }
-}
-
static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphContext *ctx)
{
WarpModifierData *wmd = (WarpModifierData *)md;
+ bool need_transform_relation = false;
if (wmd->object_from != NULL && wmd->object_to != NULL) {
- warp_deps_object_bone_new(ctx->node, wmd->object_from, wmd->bone_from);
- warp_deps_object_bone_new(ctx->node, wmd->object_to, wmd->bone_to);
-
- DEG_add_modifier_to_transform_relation(ctx->node, "Warp Modifier");
+ MOD_depsgraph_update_object_bone_relation(
+ ctx->node, wmd->object_from, wmd->bone_from, "Warp Modifier");
+ MOD_depsgraph_update_object_bone_relation(
+ ctx->node, wmd->object_to, wmd->bone_to, "Warp Modifier");
+ need_transform_relation = true;
}
- if ((wmd->texmapping == MOD_DISP_MAP_OBJECT) && wmd->map_object != NULL) {
- DEG_add_object_relation(
- ctx->node, wmd->map_object, DEG_OB_COMP_TRANSFORM, "Warp Modifier map");
- }
if (wmd->texture != NULL) {
DEG_add_generic_id_relation(ctx->node, &wmd->texture->id, "Warp Modifier");
+
+ if ((wmd->texmapping == MOD_DISP_MAP_OBJECT) && wmd->map_object != NULL) {
+ MOD_depsgraph_update_object_bone_relation(
+ ctx->node, wmd->map_object, wmd->map_bone, "Warp Modifier");
+ need_transform_relation = true;
+ }
+ else if (wmd->texmapping == MOD_DISP_MAP_GLOBAL) {
+ need_transform_relation = true;
+ }
+ }
+
+ if (need_transform_relation) {
+ DEG_add_modifier_to_transform_relation(ctx->node, "Warp Modifier");
}
}
diff --git a/source/blender/modifiers/intern/MOD_wave.c b/source/blender/modifiers/intern/MOD_wave.c
index 56b2a81fdb1..fd481018cc2 100644
--- a/source/blender/modifiers/intern/MOD_wave.c
+++ b/source/blender/modifiers/intern/MOD_wave.c
@@ -98,17 +98,28 @@ static void foreachTexLink(ModifierData *md, Object *ob, TexWalkFunc walk, void
static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphContext *ctx)
{
WaveModifierData *wmd = (WaveModifierData *)md;
+ bool need_transform_relation = false;
+
if (wmd->objectcenter != NULL) {
DEG_add_object_relation(ctx->node, wmd->objectcenter, DEG_OB_COMP_TRANSFORM, "Wave Modifier");
+ need_transform_relation = true;
}
- if (wmd->map_object != NULL) {
- DEG_add_object_relation(ctx->node, wmd->map_object, DEG_OB_COMP_TRANSFORM, "Wave Modifier");
- }
- if (wmd->objectcenter != NULL || wmd->map_object != NULL) {
- DEG_add_modifier_to_transform_relation(ctx->node, "Wave Modifier");
- }
+
if (wmd->texture != NULL) {
DEG_add_generic_id_relation(ctx->node, &wmd->texture->id, "Wave Modifier");
+
+ if ((wmd->texmapping == MOD_DISP_MAP_OBJECT) && wmd->map_object != NULL) {
+ MOD_depsgraph_update_object_bone_relation(
+ ctx->node, wmd->map_object, wmd->map_bone, "Wave Modifier");
+ need_transform_relation = true;
+ }
+ else if (wmd->texmapping == MOD_DISP_MAP_GLOBAL) {
+ need_transform_relation = true;
+ }
+ }
+
+ if (need_transform_relation) {
+ DEG_add_modifier_to_transform_relation(ctx->node, "Wave Modifier");
}
}
diff --git a/source/blender/modifiers/intern/MOD_weightvg_util.c b/source/blender/modifiers/intern/MOD_weightvg_util.c
index 23e4da32ed7..633bbb1421b 100644
--- a/source/blender/modifiers/intern/MOD_weightvg_util.c
+++ b/source/blender/modifiers/intern/MOD_weightvg_util.c
@@ -137,6 +137,7 @@ void weightvg_do_mask(const ModifierEvalContext *ctx,
const int tex_use_channel,
const int tex_mapping,
Object *tex_map_object,
+ const char *text_map_bone,
const char *tex_uvlayer_name,
const bool invert_vgroup_mask)
{
@@ -163,6 +164,7 @@ void weightvg_do_mask(const ModifierEvalContext *ctx,
*/
t_map.texture = texture;
t_map.map_object = tex_map_object;
+ BLI_strncpy(t_map.map_bone, text_map_bone, sizeof(t_map.map_bone));
BLI_strncpy(t_map.uvlayer_name, tex_uvlayer_name, sizeof(t_map.uvlayer_name));
t_map.texmapping = tex_mapping;
diff --git a/source/blender/modifiers/intern/MOD_weightvg_util.h b/source/blender/modifiers/intern/MOD_weightvg_util.h
index bcd1076eac6..6a43ac69fe9 100644
--- a/source/blender/modifiers/intern/MOD_weightvg_util.h
+++ b/source/blender/modifiers/intern/MOD_weightvg_util.h
@@ -73,6 +73,7 @@ void weightvg_do_mask(const ModifierEvalContext *ctx,
const int tex_use_channel,
const int tex_mapping,
Object *tex_map_object,
+ const char *text_map_bone,
const char *tex_uvlayer_name,
const bool invert_vgroup_mask);
diff --git a/source/blender/modifiers/intern/MOD_weightvgedit.c b/source/blender/modifiers/intern/MOD_weightvgedit.c
index ba1745f7b5e..006191e29fc 100644
--- a/source/blender/modifiers/intern/MOD_weightvgedit.c
+++ b/source/blender/modifiers/intern/MOD_weightvgedit.c
@@ -45,6 +45,7 @@
#include "MEM_guardedalloc.h"
#include "MOD_modifiertypes.h"
+#include "MOD_util.h"
#include "MOD_weightvg_util.h"
/**************************************
@@ -134,16 +135,23 @@ static void foreachTexLink(ModifierData *md, Object *ob, TexWalkFunc walk, void
static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphContext *ctx)
{
WeightVGEditModifierData *wmd = (WeightVGEditModifierData *)md;
- if (wmd->mask_tex_map_obj != NULL && wmd->mask_tex_mapping == MOD_DISP_MAP_OBJECT) {
- DEG_add_object_relation(
- ctx->node, wmd->mask_tex_map_obj, DEG_OB_COMP_TRANSFORM, "WeightVGEdit Modifier");
- DEG_add_modifier_to_transform_relation(ctx->node, "WeightVGEdit Modifier");
- }
- else if (wmd->mask_tex_mapping == MOD_DISP_MAP_GLOBAL) {
- DEG_add_modifier_to_transform_relation(ctx->node, "WeightVGEdit Modifier");
- }
+ bool need_transform_relation = false;
+
if (wmd->mask_texture != NULL) {
DEG_add_generic_id_relation(ctx->node, &wmd->mask_texture->id, "WeightVGEdit Modifier");
+
+ if (wmd->mask_tex_map_obj != NULL && wmd->mask_tex_mapping == MOD_DISP_MAP_OBJECT) {
+ MOD_depsgraph_update_object_bone_relation(
+ ctx->node, wmd->mask_tex_map_obj, wmd->mask_tex_map_bone, "WeightVGEdit Modifier");
+ need_transform_relation = true;
+ }
+ else if (wmd->mask_tex_mapping == MOD_DISP_MAP_GLOBAL) {
+ need_transform_relation = true;
+ }
+ }
+
+ if (need_transform_relation) {
+ DEG_add_modifier_to_transform_relation(ctx->node, "WeightVGEdit Modifier");
}
}
@@ -261,6 +269,7 @@ static Mesh *applyModifier(ModifierData *md, const ModifierEvalContext *ctx, Mes
wmd->mask_tex_use_channel,
wmd->mask_tex_mapping,
wmd->mask_tex_map_obj,
+ wmd->mask_tex_map_bone,
wmd->mask_tex_uvlayer_name,
invert_vgroup_mask);
diff --git a/source/blender/modifiers/intern/MOD_weightvgmix.c b/source/blender/modifiers/intern/MOD_weightvgmix.c
index 4984853d41e..f256045f53a 100644
--- a/source/blender/modifiers/intern/MOD_weightvgmix.c
+++ b/source/blender/modifiers/intern/MOD_weightvgmix.c
@@ -43,6 +43,7 @@
#include "MEM_guardedalloc.h"
#include "MOD_modifiertypes.h"
+#include "MOD_util.h"
#include "MOD_weightvg_util.h"
/**
@@ -180,19 +181,23 @@ static void foreachTexLink(ModifierData *md, Object *ob, TexWalkFunc walk, void
static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphContext *ctx)
{
WeightVGMixModifierData *wmd = (WeightVGMixModifierData *)md;
- if (wmd->mask_tex_map_obj != NULL && wmd->mask_tex_mapping == MOD_DISP_MAP_OBJECT) {
- DEG_add_object_relation(
- ctx->node, wmd->mask_tex_map_obj, DEG_OB_COMP_TRANSFORM, "WeightVGMix Modifier");
- DEG_add_object_relation(
- ctx->node, wmd->mask_tex_map_obj, DEG_OB_COMP_GEOMETRY, "WeightVGMix Modifier");
+ bool need_transform_relation = false;
- DEG_add_modifier_to_transform_relation(ctx->node, "WeightVGMix Modifier");
- }
- else if (wmd->mask_tex_mapping == MOD_DISP_MAP_GLOBAL) {
- DEG_add_modifier_to_transform_relation(ctx->node, "WeightVGMix Modifier");
- }
if (wmd->mask_texture != NULL) {
DEG_add_generic_id_relation(ctx->node, &wmd->mask_texture->id, "WeightVGMix Modifier");
+
+ if (wmd->mask_tex_map_obj != NULL && wmd->mask_tex_mapping == MOD_DISP_MAP_OBJECT) {
+ MOD_depsgraph_update_object_bone_relation(
+ ctx->node, wmd->mask_tex_map_obj, wmd->mask_tex_map_bone, "WeightVGMix Modifier");
+ need_transform_relation = true;
+ }
+ else if (wmd->mask_tex_mapping == MOD_DISP_MAP_GLOBAL) {
+ need_transform_relation = true;
+ }
+ }
+
+ if (need_transform_relation) {
+ DEG_add_modifier_to_transform_relation(ctx->node, "WeightVGMix Modifier");
}
}
@@ -395,6 +400,7 @@ static Mesh *applyModifier(ModifierData *md, const ModifierEvalContext *ctx, Mes
wmd->mask_tex_use_channel,
wmd->mask_tex_mapping,
wmd->mask_tex_map_obj,
+ wmd->mask_tex_map_bone,
wmd->mask_tex_uvlayer_name,
invert_vgroup_mask);
diff --git a/source/blender/modifiers/intern/MOD_weightvgproximity.c b/source/blender/modifiers/intern/MOD_weightvgproximity.c
index 7c9242ed900..7b7aaaeb654 100644
--- a/source/blender/modifiers/intern/MOD_weightvgproximity.c
+++ b/source/blender/modifiers/intern/MOD_weightvgproximity.c
@@ -366,6 +366,8 @@ static void foreachTexLink(ModifierData *md, Object *ob, TexWalkFunc walk, void
static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphContext *ctx)
{
WeightVGProximityModifierData *wmd = (WeightVGProximityModifierData *)md;
+ bool need_transform_relation = false;
+
if (wmd->proximity_ob_target != NULL) {
DEG_add_object_relation(
ctx->node, wmd->proximity_ob_target, DEG_OB_COMP_TRANSFORM, "WeightVGProximity Modifier");
@@ -374,17 +376,25 @@ static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphConte
DEG_add_object_relation(
ctx->node, wmd->proximity_ob_target, DEG_OB_COMP_GEOMETRY, "WeightVGProximity Modifier");
}
+ need_transform_relation = true;
}
- if (wmd->mask_tex_map_obj != NULL && wmd->mask_tex_mapping == MOD_DISP_MAP_OBJECT) {
- DEG_add_object_relation(
- ctx->node, wmd->mask_tex_map_obj, DEG_OB_COMP_TRANSFORM, "WeightVGProximity Modifier");
- DEG_add_object_relation(
- ctx->node, wmd->mask_tex_map_obj, DEG_OB_COMP_GEOMETRY, "WeightVGProximity Modifier");
- }
+
if (wmd->mask_texture != NULL) {
DEG_add_generic_id_relation(ctx->node, &wmd->mask_texture->id, "WeightVGProximity Modifier");
+
+ if (wmd->mask_tex_map_obj != NULL && wmd->mask_tex_mapping == MOD_DISP_MAP_OBJECT) {
+ MOD_depsgraph_update_object_bone_relation(
+ ctx->node, wmd->mask_tex_map_obj, wmd->mask_tex_map_bone, "WeightVGProximity Modifier");
+ need_transform_relation = true;
+ }
+ else if (wmd->mask_tex_mapping == MOD_DISP_MAP_GLOBAL) {
+ need_transform_relation = true;
+ }
+ }
+
+ if (need_transform_relation) {
+ DEG_add_modifier_to_transform_relation(ctx->node, "WeightVGProximity Modifier");
}
- DEG_add_modifier_to_transform_relation(ctx->node, "WeightVGProximity Modifier");
}
static bool isDisabled(const struct Scene *UNUSED(scene),
@@ -589,6 +599,7 @@ static Mesh *applyModifier(ModifierData *md, const ModifierEvalContext *ctx, Mes
wmd->mask_tex_use_channel,
wmd->mask_tex_mapping,
wmd->mask_tex_map_obj,
+ wmd->mask_tex_map_bone,
wmd->mask_tex_uvlayer_name,
invert_vgroup_mask);
diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_noise.c b/source/blender/nodes/shader/nodes/node_shader_tex_noise.c
index 2205a1a86a3..7b67c2d1f2e 100644
--- a/source/blender/nodes/shader/nodes/node_shader_tex_noise.c
+++ b/source/blender/nodes/shader/nodes/node_shader_tex_noise.c
@@ -26,6 +26,7 @@ static bNodeSocketTemplate sh_node_tex_noise_in[] = {
{SOCK_FLOAT, N_("W"), 0.0f, 0.0f, 0.0f, 0.0f, -1000.0f, 1000.0f},
{SOCK_FLOAT, N_("Scale"), 5.0f, 0.0f, 0.0f, 0.0f, -1000.0f, 1000.0f},
{SOCK_FLOAT, N_("Detail"), 2.0f, 0.0f, 0.0f, 0.0f, 0.0f, 16.0f},
+ {SOCK_FLOAT, N_("Roughness"), 0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR},
{SOCK_FLOAT, N_("Distortion"), 0.0f, 0.0f, 0.0f, 0.0f, -1000.0f, 1000.0f},
{-1, ""},
};
diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_wave.c b/source/blender/nodes/shader/nodes/node_shader_tex_wave.c
index 0b6cd7ee4db..bba568ed5b7 100644
--- a/source/blender/nodes/shader/nodes/node_shader_tex_wave.c
+++ b/source/blender/nodes/shader/nodes/node_shader_tex_wave.c
@@ -27,6 +27,7 @@ static bNodeSocketTemplate sh_node_tex_wave_in[] = {
{SOCK_FLOAT, N_("Distortion"), 0.0f, 0.0f, 0.0f, 0.0f, -1000.0f, 1000.0f},
{SOCK_FLOAT, N_("Detail"), 2.0f, 0.0f, 0.0f, 0.0f, 0.0f, 16.0f},
{SOCK_FLOAT, N_("Detail Scale"), 1.0f, 0.0f, 0.0f, 0.0f, -1000.0f, 1000.0f},
+ {SOCK_FLOAT, N_("Detail Roughness"), 0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR},
{SOCK_FLOAT, N_("Phase Offset"), 0.0f, 0.0f, 0.0f, 0.0f, -1000.0f, 1000.0f},
{-1, ""},
};
diff --git a/source/blender/windowmanager/intern/wm_toolsystem.c b/source/blender/windowmanager/intern/wm_toolsystem.c
index 38fa24f0416..72969f34162 100644
--- a/source/blender/windowmanager/intern/wm_toolsystem.c
+++ b/source/blender/windowmanager/intern/wm_toolsystem.c
@@ -707,7 +707,7 @@ static const char *toolsystem_default_tool(const bToolKey *tkey)
case SEQ_VIEW_SEQUENCE:
return "builtin.select";
case SEQ_VIEW_PREVIEW:
- return "builtin.annotate";
+ return "builtin.sample";
case SEQ_VIEW_SEQUENCE_PREVIEW:
return "builtin.select";
}
diff --git a/tests/gtests/blenlib/BLI_linklist_lockfree_test.cc b/tests/gtests/blenlib/BLI_linklist_lockfree_test.cc
index f1bd02e0d9e..9934d1a3337 100644
--- a/tests/gtests/blenlib/BLI_linklist_lockfree_test.cc
+++ b/tests/gtests/blenlib/BLI_linklist_lockfree_test.cc
@@ -83,10 +83,10 @@ TEST(LockfreeLinkList, InsertMultipleConcurrent)
BLI_linklist_lockfree_init(&list);
/* Initialize task scheduler and pool. */
TaskScheduler *scheduler = BLI_task_scheduler_create(num_threads);
- TaskPool *pool = BLI_task_pool_create_suspended(scheduler, &list);
+ TaskPool *pool = BLI_task_pool_create_suspended(scheduler, &list, TASK_PRIORITY_HIGH);
/* Push tasks to the pool. */
for (int i = 0; i < num_nodes; ++i) {
- BLI_task_pool_push(pool, concurrent_insert, POINTER_FROM_INT(i), false, TASK_PRIORITY_HIGH);
+ BLI_task_pool_push(pool, concurrent_insert, POINTER_FROM_INT(i), false, NULL);
}
/* Run all the tasks. */
BLI_threaded_malloc_begin();
diff --git a/tests/gtests/usd/abstract_hierarchy_iterator_test.cc b/tests/gtests/usd/abstract_hierarchy_iterator_test.cc
index e87ef547052..56d8100c30a 100644
--- a/tests/gtests/usd/abstract_hierarchy_iterator_test.cc
+++ b/tests/gtests/usd/abstract_hierarchy_iterator_test.cc
@@ -36,9 +36,11 @@ using namespace USD;
class TestHierarchyWriter : public AbstractHierarchyWriter {
public:
+ std::string writer_type;
created_writers &writers_map;
- TestHierarchyWriter(created_writers &writers_map) : writers_map(writers_map)
+ TestHierarchyWriter(const std::string &writer_type, created_writers &writers_map)
+ : writer_type(writer_type), writers_map(writers_map)
{
}
@@ -47,7 +49,10 @@ class TestHierarchyWriter : public AbstractHierarchyWriter {
const char *id_name = context.object->id.name;
created_writers::mapped_type &writers = writers_map[id_name];
- BLI_assert(writers.find(context.export_path) == writers.end());
+ if (writers.find(context.export_path) != writers.end()) {
+ ADD_FAILURE() << "Unexpectedly found another " << writer_type << " writer for " << id_name
+ << " to export to " << context.export_path;
+ }
writers.insert(context.export_path);
}
};
@@ -81,19 +86,19 @@ class TestingHierarchyIterator : public AbstractHierarchyIterator {
protected:
AbstractHierarchyWriter *create_transform_writer(const HierarchyContext *context) override
{
- return new TestHierarchyWriter(transform_writers);
+ return new TestHierarchyWriter("transform", transform_writers);
}
AbstractHierarchyWriter *create_data_writer(const HierarchyContext *context) override
{
- return new TestHierarchyWriter(data_writers);
+ return new TestHierarchyWriter("data", data_writers);
}
AbstractHierarchyWriter *create_hair_writer(const HierarchyContext *context) override
{
- return new TestHierarchyWriter(hair_writers);
+ return new TestHierarchyWriter("hair", hair_writers);
}
AbstractHierarchyWriter *create_particle_writer(const HierarchyContext *context) override
{
- return new TestHierarchyWriter(particle_writers);
+ return new TestHierarchyWriter("particle", particle_writers);
}
void delete_object_writer(AbstractHierarchyWriter *writer) override