From b7b60e2a3c6cab9bbd29eeadb746b9b8100f8f55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20T=C3=B6nne?= Date: Thu, 20 Feb 2025 13:49:53 +0100 Subject: [PATCH 1/4] Fix #134808: Grease Pencil: Interpolation tool crashes when stroke has zero length The interpolation tool is using stroke length to map points between strokes with uneven number of points. This fails when a source stroke has zero length (all points in the same location). Added a special case check to ensure uniform mapping by index is used in this case. Segment mapping has been moved to a separate function for clarity. Pull Request: https://projects.blender.org/blender/blender/pulls/134849 --- .../sculpt_paint/grease_pencil_interpolate.cc | 87 +++++++++++++------ 1 file changed, 61 insertions(+), 26 deletions(-) diff --git a/source/blender/editors/sculpt_paint/grease_pencil_interpolate.cc b/source/blender/editors/sculpt_paint/grease_pencil_interpolate.cc index cf42d03e154..4d0b2bb2a33 100644 --- a/source/blender/editors/sculpt_paint/grease_pencil_interpolate.cc +++ b/source/blender/editors/sculpt_paint/grease_pencil_interpolate.cc @@ -455,6 +455,61 @@ static bool compute_auto_flip(const Span from_positions, const Span src_positions, + const bool cyclic, + MutableSpan dst_sample_offsets) +{ + const IndexRange src_points = src_positions.index_range(); + /* Extra segment at the end for cyclic curves. */ + const int num_src_segments = src_points.size() - 1 + cyclic; + const int num_free_samples = num_dst_points - num_src_segments; + BLI_assert(dst_sample_offsets.size() == num_src_segments + 1); + + Array segment_lengths(num_src_segments + 1); + segment_lengths[0] = 0.0f; + for (const int i : src_points.drop_front(1)) { + segment_lengths[i] = segment_lengths[i - 1] + math::distance(src_positions[src_points[i - 1]], + src_positions[src_points[i]]); + } + if (cyclic) { + const int i = src_points.size(); + segment_lengths[i] = segment_lengths[i - 1] + math::distance(src_positions[src_points[i - 1]], + src_positions[src_points[0]]); + } + const float total_length = segment_lengths.last(); + + constexpr float length_epsilon = 1e-4f; + if (total_length > length_epsilon) { + /* Factor for computing the fraction of remaining samples in a segment. */ + const float length_to_free_sample_count = math::safe_divide(float(num_free_samples), + total_length); + int samples_start = 0; + for (const int segment : IndexRange(num_src_segments)) { + const int free_samples_start = math::round(segment_lengths[segment] * + length_to_free_sample_count); + const int free_samples_end = math::round(segment_lengths[segment + 1] * + length_to_free_sample_count); + dst_sample_offsets[segment] = samples_start; + samples_start += 1 + free_samples_end - free_samples_start; + } + } + else { + /* If source segment lengths are zero use uniform mapping by index as fallback. */ + const float index_to_free_sample_count = math::safe_divide(float(num_free_samples), + float(num_src_segments)); + int samples_start = 0; + for (const int segment : IndexRange(num_src_segments)) { + dst_sample_offsets[segment] = samples_start; + const int free_samples_start = math::round(segment * index_to_free_sample_count); + const int free_samples_end = math::round((segment + 1) * index_to_free_sample_count); + samples_start += 1 + free_samples_end - free_samples_start; + } + } + /* This also assigns any remaining samples in case of rounding error. */ + dst_sample_offsets.last() = num_dst_points; +} + /** * Copy existing sample positions and insert new samples in between to reach the final count. */ @@ -471,38 +526,19 @@ static void sample_curve_padded(const bke::CurvesGeometry &curves, const int num_src_segments = src_points.size() - 1 + cyclic; /* There should be at least one source point for every output sample. */ BLI_assert(num_dst_points >= num_src_segments); - const Span src_positions = curves.positions(); if (src_points.is_empty()) { return; } - Array segment_lengths(num_src_segments + 1); - segment_lengths[0] = 0.0f; - for (const int i : src_points.index_range().drop_front(1)) { - segment_lengths[i] = segment_lengths[i - 1] + math::distance(src_positions[src_points[i - 1]], - src_positions[src_points[i]]); - } - if (cyclic) { - const int i = src_points.size(); - segment_lengths[i] = segment_lengths[i - 1] + math::distance(src_positions[src_points[i - 1]], - src_positions[src_points[0]]); - } - const float total_length = segment_lengths.last(); - const int num_free_samples = num_dst_points - num_src_segments; - /* Factor for computing the fraction of remaining samples in a segment. */ - const float length_to_free_sample_count = math::safe_divide(float(num_free_samples), - total_length); + /* First destination point in each source segment. */ + Array dst_sample_offsets(num_src_segments + 1); + assign_samples_to_segments( + num_dst_points, curves.positions().slice(src_points), cyclic, dst_sample_offsets); - int samples_start = 0; + OffsetIndices dst_samples_by_src_segment = OffsetIndices(dst_sample_offsets); for (const int segment : IndexRange(num_src_segments)) { - const int free_samples_start = math::round(segment_lengths[segment] * - length_to_free_sample_count); - const int free_samples_end = math::round(segment_lengths[segment + 1] * - length_to_free_sample_count); - const IndexRange samples = IndexRange::from_begin_size( - samples_start, 1 + free_samples_end - free_samples_start); + const IndexRange samples = dst_samples_by_src_segment[segment]; BLI_assert(samples.size() >= 1); - samples_start = samples.one_after_last(); const int point_index = segment < src_points.size() ? segment : 0; r_segment_indices.slice(samples).fill(point_index); @@ -520,7 +556,6 @@ static void sample_curve_padded(const bke::CurvesGeometry &curves, r_segment_indices.last() = src_points.size() - 1; r_factors.last() = 0.0f; } - BLI_assert(samples_start == num_dst_points); } static bke::CurvesGeometry interpolate_between_curves(const GreasePencil &grease_pencil, From 9924ad9bafe4e842364b3752a7b7eb02a17bb2cd Mon Sep 17 00:00:00 2001 From: Janne Nylander Date: Thu, 20 Feb 2025 16:27:11 +0100 Subject: [PATCH 2/4] Fix: Grease Pencil: "Extrude" operator breaks vertex group weights The Grease Pencil extrusion operator didn't transfer the vertex group names from the old `CurvesGeometry` to the new one, resulting in the `gather_attributes` function not transferring the vertex group weights correctly. This PR adds `BKE_defgroup_copy_list` to the operator to remedy this. Pull Request: https://projects.blender.org/blender/blender/pulls/134695 --- .../blender/editors/grease_pencil/intern/grease_pencil_edit.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/source/blender/editors/grease_pencil/intern/grease_pencil_edit.cc b/source/blender/editors/grease_pencil/intern/grease_pencil_edit.cc index 147c330f1a4..a5a02033a5c 100644 --- a/source/blender/editors/grease_pencil/intern/grease_pencil_edit.cc +++ b/source/blender/editors/grease_pencil/intern/grease_pencil_edit.cc @@ -3083,6 +3083,7 @@ static bke::CurvesGeometry extrude_grease_pencil_curves(const bke::CurvesGeometr const int new_curves_num = dst_to_src_curves.size(); bke::CurvesGeometry dst(new_points_num, new_curves_num); + BKE_defgroup_copy_list(&dst.vertex_group_names, &src.vertex_group_names); /* Setup curve offsets, based on the number of points in each curve. */ MutableSpan new_curve_offsets = dst.offsets_for_write(); From 4b348453db14590085e31d5025b549b98f17c7c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gracjan=20Je=C5=BCewski?= Date: Thu, 20 Feb 2025 16:32:24 +0100 Subject: [PATCH 3/4] Fix: Hydra: Make final render run render loop Hydra's viewport and final engines conflict on whether the rendering loop is internal or external. The old approach works for Hydra Delegates that converge with a single sample (hdStorm) but causes engines relying on sample accumulation (hdEmrbee) to become stuck. This minimal change retains compatibility with delegates like Storm while replicating the behavior of the viewport engine in the final engine. Pull Request: https://projects.blender.org/blender/blender/pulls/134804 --- source/blender/render/hydra/final_engine.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/source/blender/render/hydra/final_engine.cc b/source/blender/render/hydra/final_engine.cc index 2f5584bf065..ee5c3ef3bea 100644 --- a/source/blender/render/hydra/final_engine.cc +++ b/source/blender/render/hydra/final_engine.cc @@ -69,13 +69,14 @@ void FinalEngine::render() render_task_delegate_->bind(); auto t = tasks(); - engine_->Execute(render_index_.get(), &t); char elapsed_time[32]; double time_begin = BLI_time_now_seconds(); float percent_done = 0.0; while (true) { + engine_->Execute(render_index_.get(), &t); + if (RE_engine_test_break(bl_engine_)) { break; } From b0fcd55aed210cb9aa4a9724123e63a2aa248b16 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Thu, 20 Feb 2025 16:32:44 +0100 Subject: [PATCH 4/4] Fix: Cycles OSL random texture issue on macOS ARM Shader compilation is multithreaded, this needs to be atomic. Pull Request: https://projects.blender.org/blender/blender/pulls/134854 --- intern/cycles/scene/osl.cpp | 2 +- intern/cycles/scene/osl.h | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/intern/cycles/scene/osl.cpp b/intern/cycles/scene/osl.cpp index 5bc5fe58cba..0364393f7fd 100644 --- a/intern/cycles/scene/osl.cpp +++ b/intern/cycles/scene/osl.cpp @@ -48,7 +48,7 @@ map> OSLShaderManager::ss_shared; int OSLShaderManager::ss_shared_users = 0; thread_mutex OSLShaderManager::ss_shared_mutex; -int OSLCompiler::texture_shared_unique_id = 0; +std::atomic OSLCompiler::texture_shared_unique_id = 0; /* Shader Manager */ diff --git a/intern/cycles/scene/osl.h b/intern/cycles/scene/osl.h index bf6d260f292..ba4e88fbaac 100644 --- a/intern/cycles/scene/osl.h +++ b/intern/cycles/scene/osl.h @@ -4,6 +4,8 @@ #pragma once +#include + #include "util/array.h" #include "util/set.h" #include "util/string.h" @@ -180,7 +182,7 @@ class OSLCompiler { ShaderType current_type; Shader *current_shader; - static int texture_shared_unique_id; + static std::atomic texture_shared_unique_id; }; CCL_NAMESPACE_END