diff options
author | Jacques Lucke <jacques@blender.org> | 2020-04-14 12:59:47 +0300 |
---|---|---|
committer | Jacques Lucke <jacques@blender.org> | 2020-04-14 12:59:47 +0300 |
commit | 6692ca602ca6f934ce00da4563434e870e954133 (patch) | |
tree | fe49d439d269f05109d7e29a7776776a9e2bc464 | |
parent | dabd59ba23f877f68aaf73e79f0d58118723d9b7 (diff) | |
parent | b07e8a24f5c69578c5ccae31848bb0f51fd18700 (diff) |
Merge branch 'master' into functionsfunctions
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(®ion->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(®ion->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(®ion->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(®ion->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 |