Blender V4.3
index_mask_expression.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2024 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
14#include "BLI_array.hh"
16#include "BLI_bit_span_ops.hh"
19#include "BLI_stack.hh"
20#include "BLI_strict_flags.h"
21#include "BLI_task.hh"
22#include "BLI_timeit.hh"
23
24namespace blender::index_mask {
25
30
35 enum class Type {
40 Unknown,
42 Full,
44 Copy,
45 };
49 const IndexMask *mask = nullptr;
50};
51
56
70
75
81 enum class Type {
83 Full,
85 Copy,
87 Indices,
88 };
89
93 const IndexMask *copy_mask = nullptr;
96};
97
102enum class ExactEvalMode {
107 Indices,
114 Bits,
115};
116
118{
119 std::sort(boundaries.begin(),
120 boundaries.end(),
121 [](const CourseBoundary &a, const CourseBoundary &b) { return a.index < b.index; });
122}
123
125{
126 std::sort(boundaries.begin(),
127 boundaries.end(),
129 return a.index < b.index;
130 });
131}
132
134static constexpr int64_t segment_size_threshold = 32;
135
138 const int64_t prev_boundary_index,
139 const int64_t current_boundary_index,
140 CoarseResult &result)
141{
142 const int64_t size = current_boundary_index - prev_boundary_index;
143 if (prev_segment) {
144 if (prev_segment->type == CoarseSegment::Type::Full &&
145 prev_segment->bounds.one_after_last() == prev_boundary_index)
146 {
147 prev_segment->bounds = prev_segment->bounds.with_new_end(current_boundary_index);
148 return *prev_segment;
149 }
150 if (current_boundary_index - prev_segment->bounds.start() < max_segment_size) {
151 if (prev_segment->bounds.size() + size < segment_size_threshold) {
152 /* Extend the previous segment because it's so small and change it into an unknown one. */
153 prev_segment->bounds = prev_segment->bounds.with_new_end(current_boundary_index);
154 prev_segment->type = CoarseSegment::Type::Unknown;
155 return *prev_segment;
156 }
157 }
158 }
159 result.segments.append(
160 {CoarseSegment::Type::Full, IndexRange::from_begin_size(prev_boundary_index, size)});
161 return result.segments.last();
162}
163
166 const int64_t prev_boundary_index,
167 const int64_t current_boundary_index,
168 CoarseResult &result)
169{
170 if (prev_segment) {
171 if (prev_segment->bounds.start() + segment_size_threshold >= prev_boundary_index) {
172 /* The previous segment is very short, so extend it. */
173 prev_segment->type = CoarseSegment::Type::Unknown;
174 prev_segment->bounds = prev_segment->bounds.with_new_end(current_boundary_index);
175 return *prev_segment;
176 }
177 }
178 result.segments.append(
180 IndexRange::from_begin_end(prev_boundary_index, current_boundary_index)});
181 return result.segments.last();
182}
183
186 const int64_t prev_boundary_index,
187 const int64_t current_boundary_index,
188 const IndexMask &copy_from_mask,
189 CoarseResult &result)
190{
191 if (prev_segment) {
192 if (prev_segment->type == CoarseSegment::Type::Copy &&
193 prev_segment->bounds.one_after_last() == prev_boundary_index &&
194 prev_segment->mask == &copy_from_mask)
195 {
196 /* Can extend the previous copy segment. */
197 prev_segment->bounds = prev_segment->bounds.with_new_end(current_boundary_index);
198 return *prev_segment;
199 }
200 if (prev_segment->bounds.start() + segment_size_threshold >= current_boundary_index) {
201 /* The previous and this segment together are very short, so better merge them together. */
202 prev_segment->bounds = prev_segment->bounds.with_new_end(current_boundary_index);
203 prev_segment->type = CoarseSegment::Type::Unknown;
204 return *prev_segment;
205 }
206 }
207 result.segments.append({CoarseSegment::Type::Copy,
208 IndexRange::from_begin_end(prev_boundary_index, current_boundary_index),
209 &copy_from_mask});
210 return result.segments.last();
211}
212
213static void evaluate_coarse_union(const Span<CourseBoundary> boundaries, CoarseResult &r_result)
214{
215 if (boundaries.is_empty()) {
216 return;
217 }
218
219 CoarseResult &result = r_result;
220 CoarseSegment *prev_segment = nullptr;
221 Vector<const CoarseSegment *, 16> active_segments;
222 int64_t prev_boundary_index = boundaries[0].index;
223
224 for (const CourseBoundary &boundary : boundaries) {
225 if (prev_boundary_index < boundary.index) {
226 /* Compute some properties of the input segments that were active between the current and the
227 * previous boundary. */
228 bool has_full = false;
229 bool has_unknown = false;
230 bool copy_from_single_mask = true;
231 const IndexMask *copy_from_mask = nullptr;
232 for (const CoarseSegment *active_segment : active_segments) {
233 switch (active_segment->type) {
235 has_unknown = true;
236 break;
237 }
239 has_full = true;
240 break;
241 }
243 if (!ELEM(copy_from_mask, nullptr, active_segment->mask)) {
244 copy_from_single_mask = false;
245 }
246 copy_from_mask = active_segment->mask;
247 break;
248 }
249 }
250 }
251 /* Determine the resulting coarse segment type based on the properties computed above. */
252 if (has_full) {
253 prev_segment = &add_coarse_segment__full(
254 prev_segment, prev_boundary_index, boundary.index, result);
255 }
256 else if (has_unknown || !copy_from_single_mask) {
257 prev_segment = &add_coarse_segment__unknown(
258 prev_segment, prev_boundary_index, boundary.index, result);
259 }
260 else if (copy_from_mask != nullptr && copy_from_single_mask) {
261 prev_segment = &add_coarse_segment__copy(
262 prev_segment, prev_boundary_index, boundary.index, *copy_from_mask, result);
263 }
264
265 prev_boundary_index = boundary.index;
266 }
267
268 /* Update active segments. */
269 if (boundary.is_begin) {
270 active_segments.append(boundary.segment);
271 }
272 else {
273 active_segments.remove_first_occurrence_and_reorder(boundary.segment);
274 }
275 }
276}
277
279 const int64_t terms_num,
280 CoarseResult &r_result)
281{
282 if (boundaries.is_empty()) {
283 return;
284 }
285
286 CoarseResult &result = r_result;
287 CoarseSegment *prev_segment = nullptr;
288 Vector<const CoarseSegment *, 16> active_segments;
289 int64_t prev_boundary_index = boundaries[0].index;
290
291 for (const CourseBoundary &boundary : boundaries) {
292 if (prev_boundary_index < boundary.index) {
293 /* Only if one segment of each term is active, it's possible that the output contains
294 * anything. */
295 if (active_segments.size() == terms_num) {
296 /* Compute some properties of the input segments that were active between the current and
297 * previous boundary. */
298 int full_count = 0;
299 int unknown_count = 0;
300 int copy_count = 0;
301 bool copy_from_single_mask = true;
302 const IndexMask *copy_from_mask = nullptr;
303 for (const CoarseSegment *active_segment : active_segments) {
304 switch (active_segment->type) {
306 unknown_count++;
307 break;
308 }
310 full_count++;
311 break;
312 }
314 copy_count++;
315 if (!ELEM(copy_from_mask, nullptr, active_segment->mask)) {
316 copy_from_single_mask = false;
317 }
318 copy_from_mask = active_segment->mask;
319 break;
320 }
321 }
322 }
323 /* Determine the resulting coarse segment type based on the properties computed above. */
324 BLI_assert(full_count + unknown_count + copy_count == terms_num);
325 if (full_count == terms_num) {
326 prev_segment = &add_coarse_segment__full(
327 prev_segment, prev_boundary_index, boundary.index, result);
328 }
329 else if (unknown_count > 0 || copy_count < terms_num || !copy_from_single_mask) {
330 prev_segment = &add_coarse_segment__unknown(
331 prev_segment, prev_boundary_index, boundary.index, result);
332 }
333 else if (copy_count == terms_num && copy_from_single_mask) {
334 prev_segment = &add_coarse_segment__copy(
335 prev_segment, prev_boundary_index, boundary.index, *copy_from_mask, result);
336 }
337 }
338
339 prev_boundary_index = boundary.index;
340 }
341
342 /* Update active segments. */
343 if (boundary.is_begin) {
344 active_segments.append(boundary.segment);
345 }
346 else {
347 active_segments.remove_first_occurrence_and_reorder(boundary.segment);
348 }
349 }
350}
351
353 CoarseResult &r_result)
354{
355 if (boundaries.is_empty()) {
356 return;
357 }
358
359 CoarseResult &result = r_result;
360 CoarseSegment *prev_segment = nullptr;
361 Vector<const CoarseSegment *> active_main_segments;
362 Vector<const CoarseSegment *, 16> active_subtract_segments;
363 int64_t prev_boundary_index = boundaries[0].index;
364
365 for (const DifferenceCourseBoundary &boundary : boundaries) {
366 if (prev_boundary_index < boundary.index) {
367 /* There is only one main term, so at most one main segment can be active at once. */
368 BLI_assert(active_main_segments.size() <= 1);
369 if (active_main_segments.size() == 1) {
370 const CoarseSegment &active_main_segment = *active_main_segments[0];
371 /* Compute some properties of the input segments that were active between the current and
372 * the previous boundary. */
373 bool has_subtract_full = false;
374 bool has_subtract_same_mask = false;
375 for (const CoarseSegment *active_subtract_segment : active_subtract_segments) {
376 switch (active_subtract_segment->type) {
378 break;
379 }
381 has_subtract_full = true;
382 break;
383 }
385 if (active_main_segment.type == CoarseSegment::Type::Copy) {
386 if (active_main_segment.mask == active_subtract_segment->mask) {
387 has_subtract_same_mask = true;
388 }
389 }
390 break;
391 }
392 }
393 }
394 /* Determine the resulting coarse segment type based on the properties computed above. */
395 if (has_subtract_full) {
396 /* Do nothing, the resulting segment is empty for the current range. */
397 }
398 else {
399 switch (active_main_segment.type) {
401 prev_segment = &add_coarse_segment__unknown(
402 prev_segment, prev_boundary_index, boundary.index, result);
403 break;
404 }
406 if (active_subtract_segments.is_empty()) {
407 prev_segment = &add_coarse_segment__full(
408 prev_segment, prev_boundary_index, boundary.index, result);
409 }
410 else {
411 prev_segment = &add_coarse_segment__unknown(
412 prev_segment, prev_boundary_index, boundary.index, result);
413 }
414 break;
415 }
417 if (active_subtract_segments.is_empty()) {
418 prev_segment = &add_coarse_segment__copy(prev_segment,
419 prev_boundary_index,
420 boundary.index,
421 *active_main_segment.mask,
422 result);
423 }
424 else if (has_subtract_same_mask) {
425 /* Do nothing, subtracting a mask from itself results in an empty mask. */
426 }
427 else {
428 prev_segment = &add_coarse_segment__unknown(
429 prev_segment, prev_boundary_index, boundary.index, result);
430 }
431 break;
432 }
433 }
434 }
435 }
436
437 prev_boundary_index = boundary.index;
438 }
439
440 /* Update active segments. */
441 if (boundary.is_main) {
442 if (boundary.is_begin) {
443 active_main_segments.append(boundary.segment);
444 }
445 else {
446 active_main_segments.remove_first_occurrence_and_reorder(boundary.segment);
447 }
448 }
449 else {
450 if (boundary.is_begin) {
451 active_subtract_segments.append(boundary.segment);
452 }
453 else {
454 active_subtract_segments.remove_first_occurrence_and_reorder(boundary.segment);
455 }
456 }
457 }
458}
459
476static CoarseResult evaluate_coarse(const Expr &root_expression,
477 const Span<const Expr *> eval_order,
478 const std::optional<IndexRange> eval_bounds = std::nullopt)
479{
480 /* An expression result for each intermediate expression. */
482 root_expression.expression_array_size());
483
484 /* Process expressions in a pre-determined order. */
485 for (const Expr *expression : eval_order) {
486 CoarseResult &expr_result = expression_results[expression->index].emplace();
487 switch (expression->type) {
488 case Expr::Type::Atomic: {
489 const AtomicExpr &expr = expression->as_atomic();
490
492 if (eval_bounds.has_value()) {
493 mask = expr.mask->slice_content(*eval_bounds);
494 }
495 else {
496 mask = *expr.mask;
497 }
498
499 if (!mask.is_empty()) {
500 const IndexRange bounds = mask.bounds();
501 if (const std::optional<IndexRange> range = mask.to_range()) {
502 expr_result.segments.append({CoarseSegment::Type::Full, bounds});
503 }
504 else {
505 expr_result.segments.append({CoarseSegment::Type::Copy, bounds, expr.mask});
506 }
507 }
508 break;
509 }
510 case Expr::Type::Union: {
511 const UnionExpr &expr = expression->as_union();
513 for (const Expr *term : expr.terms) {
514 const CoarseResult &term_result = *expression_results[term->index];
515 for (const CoarseSegment &segment : term_result.segments) {
516 boundaries.append({segment.bounds.first(), true, &segment});
517 boundaries.append({segment.bounds.one_after_last(), false, &segment});
518 }
519 }
520 sort_course_boundaries(boundaries);
521 evaluate_coarse_union(boundaries, expr_result);
522 break;
523 }
525 const IntersectionExpr &expr = expression->as_intersection();
527 for (const Expr *term : expr.terms) {
528 const CoarseResult &term_result = *expression_results[term->index];
529 for (const CoarseSegment &segment : term_result.segments) {
530 boundaries.append({segment.bounds.first(), true, &segment});
531 boundaries.append({segment.bounds.one_after_last(), false, &segment});
532 }
533 }
534 sort_course_boundaries(boundaries);
535 evaluate_coarse_intersection(boundaries, expr.terms.size(), expr_result);
536 break;
537 }
539 const DifferenceExpr &expr = expression->as_difference();
541 const CoarseResult &main_term_result = *expression_results[expr.terms[0]->index];
542 for (const CoarseSegment &segment : main_term_result.segments) {
543 boundaries.append({{segment.bounds.first(), true, &segment}, true});
544 boundaries.append({{segment.bounds.one_after_last(), false, &segment}, true});
545 }
546 for (const Expr *term : expr.terms.as_span().drop_front(1)) {
547 const CoarseResult &term_result = *expression_results[term->index];
548 for (const CoarseSegment &segment : term_result.segments) {
549 boundaries.append({{segment.bounds.first(), true, &segment}, false});
550 boundaries.append({{segment.bounds.one_after_last(), false, &segment}, false});
551 }
552 }
553 sort_course_boundaries(boundaries);
554 evaluate_coarse_difference(boundaries, expr_result);
555 break;
556 }
557 }
558 }
559
560 CoarseResult &final_result = *expression_results[root_expression.index];
561 return std::move(final_result);
562}
563
565{
566 /* TODO: Could first count the number of set bits. */
568 bits::foreach_1_index(bits, [&](const int64_t i) {
570 indices_vec.append_unchecked(int16_t(i));
571 });
572 return allocator.construct_array_copy<int16_t>(indices_vec);
573}
574
586static IndexMaskSegment evaluate_exact_with_bits(const Expr &root_expression,
587 LinearAllocator<> &allocator,
588 const IndexRange bounds,
589 const Span<const Expr *> eval_order)
590{
591 BLI_assert(bounds.size() <= max_segment_size);
592 const int64_t bounds_min = bounds.start();
593 const int expr_array_size = root_expression.expression_array_size();
594
595 /* Make bit span sizes a multiple of `BitsPerInt`. This allows the bit-wise operations to run a
596 * bit more efficiently, because only full integers are processed. */
597 const int64_t ints_in_bounds = ceil_division(bounds.size(), bits::BitsPerInt);
598 BitGroupVector<16 * 1024> expression_results(
599 expr_array_size, ints_in_bounds * bits::BitsPerInt, false);
600
601 for (const Expr *expression : eval_order) {
602 MutableBoundedBitSpan expr_result = expression_results[expression->index];
603 switch (expression->type) {
604 case Expr::Type::Atomic: {
605 const AtomicExpr &expr = expression->as_atomic();
606 const IndexMask mask = expr.mask->slice_content(bounds);
607 mask.to_bits(expr_result, -bounds_min);
608 break;
609 }
610 case Expr::Type::Union: {
611 for (const Expr *term : expression->terms) {
612 expr_result |= expression_results[term->index];
613 }
614 break;
615 }
617 bits::copy_from_or(expr_result, expression_results[expression->terms[0]->index]);
618 for (const Expr *term : expression->terms.as_span().drop_front(1)) {
619 expr_result &= expression_results[term->index];
620 }
621 break;
622 }
624 bits::copy_from_or(expr_result, expression_results[expression->terms[0]->index]);
625 for (const Expr *term : expression->terms.as_span().drop_front(1)) {
627 [](const bits::BitInt a, const bits::BitInt b) { return a & ~b; },
628 expr_result,
629 expression_results[term->index]);
630 }
631 break;
632 }
633 }
634 }
635 const BoundedBitSpan final_bits = expression_results[root_expression.index];
636 const Span<int16_t> indices = bits_to_indices(final_bits, allocator);
637 return IndexMaskSegment(bounds_min, indices);
638}
639
642 const int64_t bounds_min,
643 int16_t *r_values)
644{
645 if (segments.is_empty()) {
646 return {};
647 }
648 if (segments.size() == 1) {
649 return segments[0];
650 }
651 if (segments.size() == 2) {
652 const IndexMaskSegment a = segments[0].shift(-bounds_min);
653 const IndexMaskSegment b = segments[1].shift(-bounds_min);
654 const int64_t size = std::set_union(a.begin(), a.end(), b.begin(), b.end(), r_values) -
655 r_values;
656 return {bounds_min, {r_values, size}};
657 }
658
659 /* Sort input segments by their size, so that smaller segments are unioned first. This results in
660 * smaller intermediate arrays and thus less work overall. */
661 Vector<IndexMaskSegment> sorted_segments(segments);
662 std::sort(
663 sorted_segments.begin(),
664 sorted_segments.end(),
665 [](const IndexMaskSegment &a, const IndexMaskSegment &b) { return a.size() < b.size(); });
666
667 std::array<int16_t, max_segment_size> tmp_indices;
668 /* Can use r_values for temporary values because if it's large enough for the final result, it's
669 * also large enough for intermediate results. */
670 int16_t *buffer_a = r_values;
671 int16_t *buffer_b = tmp_indices.data();
672
673 if (sorted_segments.size() % 2 == 1) {
674 /* Swap buffers so that the result is in #r_values in the end. */
675 std::swap(buffer_a, buffer_b);
676 }
677
678 int64_t count = 0;
679 {
680 /* Initial union. */
681 const IndexMaskSegment a = sorted_segments[0].shift(-bounds_min);
682 const IndexMaskSegment b = sorted_segments[1].shift(-bounds_min);
683 int16_t *dst = buffer_a;
684 count = std::set_union(a.begin(), a.end(), b.begin(), b.end(), dst) - dst;
685 }
686
687 /* Union one input into the result at a time. In theory, one could write an algorithm that unions
688 * multiple sorted arrays at once, but that's more complex and it's not obvious that it would be
689 * faster in the end. */
690 for (const int64_t segment_i : sorted_segments.index_range().drop_front(2)) {
691 const int16_t *a = buffer_a;
692 const IndexMaskSegment b = sorted_segments[segment_i].shift(-bounds_min);
693 int16_t *dst = buffer_b;
694 count = std::set_union(a, a + count, b.begin(), b.end(), dst) - dst;
695 std::swap(buffer_a, buffer_b);
696 }
697 return {bounds_min, {r_values, count}};
698}
699
702 const int64_t bounds_min,
703 int16_t *r_values)
704{
705 if (segments.is_empty()) {
706 return {};
707 }
708 if (segments.size() == 1) {
709 return segments[0];
710 }
711 if (segments.size() == 2) {
712 const IndexMaskSegment a = segments[0].shift(-bounds_min);
713 const IndexMaskSegment b = segments[1].shift(-bounds_min);
714 const int64_t size = std::set_intersection(a.begin(), a.end(), b.begin(), b.end(), r_values) -
715 r_values;
716 return {bounds_min, {r_values, size}};
717 }
718
719 /* Intersect smaller segments first, because then the intermediate results will generally be
720 * smaller. */
721 Vector<IndexMaskSegment> sorted_segments(segments);
722 std::sort(
723 sorted_segments.begin(),
724 sorted_segments.end(),
725 [](const IndexMaskSegment &a, const IndexMaskSegment &b) { return a.size() < b.size(); });
726
727 std::array<int16_t, max_segment_size> tmp_indices_1;
728 std::array<int16_t, max_segment_size> tmp_indices_2;
729 int16_t *buffer_a = tmp_indices_1.data();
730 int16_t *buffer_b = tmp_indices_2.data();
731
732 int64_t count = 0;
733 {
734 /* Initial intersection. */
735 const IndexMaskSegment a = sorted_segments[0].shift(-bounds_min);
736 const IndexMaskSegment b = sorted_segments[1].shift(-bounds_min);
737 int16_t *dst = buffer_a;
738 count = std::set_intersection(a.begin(), a.end(), b.begin(), b.end(), dst) - dst;
739 }
740
741 for (const int64_t segment_i : sorted_segments.index_range().drop_front(2)) {
742 const int16_t *a = buffer_a;
743 const IndexMaskSegment b = sorted_segments[segment_i].shift(-bounds_min);
744 /* The result of the final intersection should be written directly to #r_values to avoid an
745 * additional copy in the end. */
746 int16_t *dst = (segment_i == sorted_segments.size() - 1) ? r_values : buffer_b;
747 count = std::set_intersection(a, a + count, b.begin(), b.end(), dst) - dst;
748 std::swap(buffer_a, buffer_b);
749 }
750 return {bounds_min, {r_values, count}};
751}
752
758 const IndexMaskSegment main_segment,
759 const Span<IndexMaskSegment> subtract_segments,
760 const int64_t bounds_min,
761 int16_t *r_values)
762{
763 if (main_segment.is_empty()) {
764 return {};
765 }
766 if (subtract_segments.is_empty()) {
767 return main_segment;
768 }
769 if (subtract_segments.size() == 1) {
770 const IndexMaskSegment shifted_main_segment = main_segment.shift(-bounds_min);
771 const IndexMaskSegment subtract_segment = subtract_segments[0].shift(-bounds_min);
772 const int64_t size = std::set_difference(shifted_main_segment.begin(),
773 shifted_main_segment.end(),
774 subtract_segment.begin(),
775 subtract_segment.end(),
776 r_values) -
777 r_values;
778 return {bounds_min, {r_values, size}};
779 }
780
781 int64_t subtract_count = 0;
782 for (const IndexMaskSegment &segment : subtract_segments) {
783 subtract_count += segment.size();
784 }
785 if (subtract_count < main_segment.size() / 2) {
786 /* Can be more efficient to union all the subtract indices first before computing the
787 * difference. This avoids potentially multiple larger intermediate arrays. */
788 std::array<int16_t, max_segment_size> union_indices;
789 const IndexMaskSegment shifted_main_segment = main_segment.shift(-bounds_min);
790 const IndexMaskSegment unioned_subtract_segment =
791 union_index_mask_segments(subtract_segments, bounds_min, union_indices.data())
792 .shift(-bounds_min);
793 const int64_t size = std::set_difference(shifted_main_segment.begin(),
794 shifted_main_segment.end(),
795 unioned_subtract_segment.begin(),
796 unioned_subtract_segment.end(),
797 r_values) -
798 r_values;
799 return {bounds_min, {r_values, size}};
800 }
801
802 /* Sort larger segments to the front. This way the intermediate arrays are likely smaller. */
803 Vector<IndexMaskSegment> sorted_subtract_segments(subtract_segments);
804 std::sort(
805 sorted_subtract_segments.begin(),
806 sorted_subtract_segments.end(),
807 [](const IndexMaskSegment &a, const IndexMaskSegment &b) { return a.size() > b.size(); });
808
809 std::array<int16_t, max_segment_size> tmp_indices_1;
810 std::array<int16_t, max_segment_size> tmp_indices_2;
811 int16_t *buffer_a = tmp_indices_1.data();
812 int16_t *buffer_b = tmp_indices_2.data();
813
814 int64_t count = 0;
815 {
816 /* Initial difference. */
817 const IndexMaskSegment shifted_main_segment = main_segment.shift(-bounds_min);
818 const IndexMaskSegment subtract_segment = sorted_subtract_segments[0].shift(-bounds_min);
819 int16_t *dst = buffer_a;
820 count = std::set_difference(shifted_main_segment.begin(),
821 shifted_main_segment.end(),
822 subtract_segment.begin(),
823 subtract_segment.end(),
824 dst) -
825 dst;
826 }
827
828 for (const int64_t segment_i : sorted_subtract_segments.index_range().drop_front(1)) {
829 const IndexMaskSegment &subtract_segment = sorted_subtract_segments[segment_i].shift(
830 -bounds_min);
831 /* The final result should be written directly to #r_values to avoid an additional copy. */
832 int16_t *dst = (segment_i == sorted_subtract_segments.size() - 1) ? r_values : buffer_b;
833 count = std::set_difference(buffer_a,
834 buffer_a + count,
835 subtract_segment.begin(),
836 subtract_segment.end(),
837 dst) -
838 dst;
839 std::swap(buffer_a, buffer_b);
840 }
841 return {bounds_min, {r_values, count}};
842}
843
850 LinearAllocator<> &allocator,
851 const IndexRange bounds,
852 const Span<const Expr *> eval_order)
853{
854 BLI_assert(bounds.size() <= max_segment_size);
855 const int64_t bounds_min = bounds.start();
856 const int expr_array_size = root_expression.expression_array_size();
858 for (const Expr *expression : eval_order) {
859 switch (expression->type) {
860 case Expr::Type::Atomic: {
861 const AtomicExpr &expr = expression->as_atomic();
862 const IndexMask mask = expr.mask->slice_content(bounds);
863 /* The caller should make sure that the bounds are aligned to segment bounds. */
864 BLI_assert(mask.segments_num() <= 1);
865 if (mask.segments_num() == 1) {
866 results[expression->index] = mask.segment(0);
867 }
868 break;
869 }
870 case Expr::Type::Union: {
871 const UnionExpr &expr = expression->as_union();
872 Array<IndexMaskSegment> term_segments(expr.terms.size());
873 int64_t result_size_upper_bound = 0;
874 bool used_short_circuit = false;
875 for (const int64_t term_i : expr.terms.index_range()) {
876 const Expr &term = *expr.terms[term_i];
877 const IndexMaskSegment term_segment = results[term.index];
878 if (term_segment.size() == bounds.size()) {
879 /* Can skip computing the union if we know that one of the inputs contains all possible
880 * indices already. */
881 results[expression->index] = term_segment;
882 used_short_circuit = true;
883 break;
884 }
885 term_segments[term_i] = term_segment;
886 result_size_upper_bound += term_segment.size();
887 }
888 if (used_short_circuit) {
889 break;
890 }
891 result_size_upper_bound = std::min(result_size_upper_bound, bounds.size());
892 MutableSpan<int16_t> dst = allocator.allocate_array<int16_t>(result_size_upper_bound);
893 const IndexMaskSegment result_segment = union_index_mask_segments(
894 term_segments, bounds_min, dst.data());
896 result_segment.base_span().end());
897 results[expression->index] = result_segment;
898 break;
899 }
901 const IntersectionExpr &expr = expression->as_intersection();
902 Array<IndexMaskSegment> term_segments(expr.terms.size());
903 int64_t result_size_upper_bound = bounds.size();
904 bool used_short_circuit = false;
905 for (const int64_t term_i : expr.terms.index_range()) {
906 const Expr &term = *expr.terms[term_i];
907 const IndexMaskSegment term_segment = results[term.index];
908 if (term_segment.is_empty()) {
909 /* Can skip computing the intersection if we know that one of the inputs is empty. */
910 results[expression->index] = {};
911 used_short_circuit = true;
912 break;
913 }
914 result_size_upper_bound = std::min(result_size_upper_bound, term_segment.size());
915 term_segments[term_i] = term_segment;
916 }
917 if (used_short_circuit) {
918 break;
919 }
920 MutableSpan<int16_t> dst = allocator.allocate_array<int16_t>(result_size_upper_bound);
921 const IndexMaskSegment result_segment = intersect_index_mask_segments(
922 term_segments, bounds_min, dst.data());
924 result_segment.base_span().end());
925 results[expression->index] = result_segment;
926 break;
927 }
929 const DifferenceExpr &expr = expression->as_difference();
930 const Expr &main_term = *expr.terms[0];
931 const IndexMaskSegment main_segment = results[main_term.index];
932 if (main_segment.is_empty()) {
933 /* Can skip the computation of the main segment is empty. */
934 results[expression->index] = {};
935 break;
936 }
937 int64_t result_size_upper_bound = main_segment.size();
938 bool used_short_circuit = false;
939 Array<IndexMaskSegment> subtract_segments(expr.terms.size() - 1);
940 for (const int64_t term_i : expr.terms.index_range().drop_front(1)) {
941 const Expr &subtract_term = *expr.terms[term_i];
942 const IndexMaskSegment term_segment = results[subtract_term.index];
943 if (term_segment.size() == bounds.size()) {
944 /* Can skip computing the difference if we know that one of the subtract-terms is
945 * full. */
946 results[expression->index] = {};
947 used_short_circuit = true;
948 break;
949 }
950 result_size_upper_bound = std::min(result_size_upper_bound,
951 bounds.size() - term_segment.size());
952 subtract_segments[term_i - 1] = term_segment;
953 }
954 if (used_short_circuit) {
955 break;
956 }
957 MutableSpan<int16_t> dst = allocator.allocate_array<int16_t>(result_size_upper_bound);
959 main_segment, subtract_segments, bounds_min, dst.data());
961 result_segment.base_span().end());
962 results[expression->index] = result_segment;
963 break;
964 }
965 }
966 }
967 return results[root_expression.index];
968}
969
975 const Span<EvaluatedSegment> evaluated_segments)
976{
977 const std::array<int16_t, max_segment_size> &static_indices_array = get_static_indices_array();
978
979 Vector<IndexMaskSegment> result_mask_segments;
980 for (const EvaluatedSegment &evaluated_segment : evaluated_segments) {
981 switch (evaluated_segment.type) {
983 const int64_t full_size = evaluated_segment.bounds.size();
984 for (int64_t i = 0; i < full_size; i += max_segment_size) {
985 const int64_t size = std::min(i + max_segment_size, full_size) - i;
986 result_mask_segments.append(IndexMaskSegment(
987 evaluated_segment.bounds.first() + i, Span(static_indices_array).take_front(size)));
988 }
989 break;
990 }
992 const IndexMask sliced_mask = evaluated_segment.copy_mask->slice_content(
993 evaluated_segment.bounds);
994 sliced_mask.foreach_segment(
995 [&](const IndexMaskSegment &segment) { result_mask_segments.append(segment); });
996 break;
997 }
999 result_mask_segments.append(evaluated_segment.indices);
1000 break;
1001 }
1002 }
1003 }
1004 return result_mask_segments;
1005}
1006
1012{
1014 if (root_expression.type == Expr::Type::Atomic) {
1015 eval_order.append(&root_expression);
1016 return eval_order;
1017 }
1018
1019 Array<bool, inline_expr_array_size> is_evaluated_states(root_expression.expression_array_size(),
1020 false);
1022 expr_stack.push(&root_expression);
1023
1024 while (!expr_stack.is_empty()) {
1025 const Expr &expression = *expr_stack.peek();
1026 bool &is_evaluated = is_evaluated_states[expression.index];
1027 if (is_evaluated) {
1028 expr_stack.pop();
1029 continue;
1030 }
1031 bool all_terms_evaluated = true;
1032 for (const Expr *term : expression.terms) {
1033 bool &term_evaluated = is_evaluated_states[term->index];
1034 if (!term_evaluated) {
1035 if (term->type == Expr::Type::Atomic) {
1036 eval_order.append(term);
1037 term_evaluated = true;
1038 }
1039 else {
1040 expr_stack.push(term);
1041 all_terms_evaluated = false;
1042 }
1043 }
1044 }
1045 if (all_terms_evaluated) {
1046 eval_order.append(&expression);
1047 is_evaluated = true;
1048 expr_stack.pop();
1049 }
1050 }
1051
1052 return eval_order;
1053}
1054
1056static ExactEvalMode determine_exact_eval_mode(const Expr &root_expression)
1057{
1058 for (const Expr *term : root_expression.terms) {
1059 if (!term->terms.is_empty()) {
1060 /* Use bits when there are nested expressions as this is often faster. */
1061 return ExactEvalMode::Bits;
1062 }
1063 }
1065}
1066
1068 const Expr &root_expression,
1069 const Span<const Expr *> eval_order,
1070 Vector<EvaluatedSegment, 16> &r_evaluated_segments,
1071 Vector<IndexRange, 16> &r_short_unknown_segments)
1072{
1073 /* Coarse evaluation splits the full range into segments. Long segments are split up and get
1074 * another coarse evaluation. Short segments will be evaluated exactly. */
1075 Stack<IndexRange, 16> long_unknown_segments;
1076
1077 /* The point at which a range starts being "short". */
1078 const int64_t coarse_segment_size_threshold = max_segment_size;
1079
1080 /* Checks the coarse results and inserts its segments into either `long_unknown_segments` for
1081 * further coarse evaluation, `r_short_unknown_segments` for exact evaluation or
1082 * `r_evaluated_segments` if no further evaluation is necessary. */
1083 auto handle_coarse_result = [&](const CoarseResult &coarse_result) {
1084 for (const CoarseSegment &segment : coarse_result.segments) {
1085 switch (segment.type) {
1087 if (segment.bounds.size() > coarse_segment_size_threshold) {
1088 long_unknown_segments.push(segment.bounds);
1089 }
1090 else {
1091 r_short_unknown_segments.append(segment.bounds);
1092 }
1093 break;
1094 }
1096 BLI_assert(segment.mask);
1097 r_evaluated_segments.append(
1098 {EvaluatedSegment::Type::Copy, segment.bounds, segment.mask});
1099 break;
1100 }
1102 r_evaluated_segments.append({EvaluatedSegment::Type::Full, segment.bounds});
1103 break;
1104 }
1105 }
1106 }
1107 };
1108
1109 /* Initial coarse evaluation without any explicit bounds. The bounds are implied by the index
1110 * masks used in the expression. */
1111 const CoarseResult initial_coarse_result = evaluate_coarse(root_expression, eval_order);
1112 handle_coarse_result(initial_coarse_result);
1113
1114 /* Do coarse evaluation until all unknown segments are short enough to do exact evaluation. */
1115 while (!long_unknown_segments.is_empty()) {
1116 const IndexRange unknown_bounds = long_unknown_segments.pop();
1117 const int64_t split_pos = unknown_bounds.size() / 2;
1118 const IndexRange left_half = unknown_bounds.take_front(split_pos);
1119 const IndexRange right_half = unknown_bounds.drop_front(split_pos);
1120 const CoarseResult left_result = evaluate_coarse(root_expression, eval_order, left_half);
1121 const CoarseResult right_result = evaluate_coarse(root_expression, eval_order, right_half);
1122 handle_coarse_result(left_result);
1123 handle_coarse_result(right_result);
1124 }
1125}
1126
1128 const Expr &root_expression,
1129 const ExactEvalMode exact_eval_mode,
1130 const Span<const Expr *> eval_order,
1131 const Span<IndexRange> short_unknown_segments,
1132 IndexMaskMemory &memory,
1133 Vector<EvaluatedSegment, 16> &r_evaluated_segments)
1134{
1135 /* Evaluate a segment exactly. */
1136 auto evaluate_unknown_segment = [&](const IndexRange bounds,
1137 LinearAllocator<> &allocator,
1138 Vector<EvaluatedSegment, 16> &r_local_evaluated_segments) {
1139 /* Use the predetermined evaluation mode. */
1140 switch (exact_eval_mode) {
1141 case ExactEvalMode::Bits: {
1143 root_expression, allocator, bounds, eval_order);
1144 if (!indices.is_empty()) {
1145 r_local_evaluated_segments.append(
1146 {EvaluatedSegment::Type::Indices, bounds, nullptr, indices});
1147 }
1148 break;
1149 }
1151 /* #evaluate_exact_with_indices requires that all index masks have a single segment in the
1152 * provided bounds. So split up the range into sub-ranges first if necessary. */
1153 Vector<int64_t, 16> split_indices;
1154 /* Always adding the beginning and end of the bounds simplifies the code below. */
1155 split_indices.extend({bounds.first(), bounds.one_after_last()});
1156 for (const int64_t eval_order_i : eval_order.index_range()) {
1157 const Expr &expr = *eval_order[eval_order_i];
1158 if (expr.type != Expr::Type::Atomic) {
1159 continue;
1160 }
1161 const AtomicExpr &atomic_expr = expr.as_atomic();
1162 const IndexMask mask = atomic_expr.mask->slice_content(bounds);
1163 const int64_t segments_num = mask.segments_num();
1164 if (segments_num <= 1) {
1165 /* This mask only has a single segment in the bounds anyway, so no extra split-position
1166 * is necessary. */
1167 continue;
1168 }
1169 /* Split at the beginning of each segment. Skipping the first, because that does not need
1170 * an extra split position. Alternatively, one could also split at the end of each
1171 * segment except the last one. It doesn't matter much. */
1172 for (const int64_t segment_i : IndexRange(segments_num).drop_front(1)) {
1173 const IndexMaskSegment segment = mask.segment(segment_i);
1174 split_indices.append(segment[0]);
1175 }
1176 }
1177 std::sort(split_indices.begin(), split_indices.end());
1178 for (const int64_t boundary_i : split_indices.index_range().drop_back(1)) {
1179 const IndexRange sub_bounds = IndexRange::from_begin_end(split_indices[boundary_i],
1180 split_indices[boundary_i + 1]);
1181 if (sub_bounds.is_empty()) {
1182 continue;
1183 }
1185 root_expression, allocator, sub_bounds, eval_order);
1186 if (!indices.is_empty()) {
1187 r_local_evaluated_segments.append(
1188 {EvaluatedSegment::Type::Indices, sub_bounds, nullptr, indices});
1189 }
1190 }
1191 break;
1192 }
1193 }
1194 };
1195
1196 /* Decide whether multi-threading should be used or not. There is some extra overhead even when
1197 * just attempting to use multi-threading. */
1198 const int64_t unknown_segment_eval_grain_size = 8;
1199 if (short_unknown_segments.size() < unknown_segment_eval_grain_size) {
1200 for (const IndexRange &bounds : short_unknown_segments) {
1201 evaluate_unknown_segment(bounds, memory, r_evaluated_segments);
1202 }
1203 }
1204 else {
1205 /* Do exact evaluation in multiple threads. The allocators and evaluated segments created by
1206 * each thread are merged in the end. */
1207 struct LocalData {
1208 LinearAllocator<> allocator;
1209 Vector<EvaluatedSegment, 16> evaluated_segments;
1210 };
1212 threading::parallel_for(short_unknown_segments.index_range(),
1213 unknown_segment_eval_grain_size,
1214 [&](const IndexRange range) {
1215 LocalData &data = data_by_thread.local();
1216 for (const IndexRange &bounds : short_unknown_segments.slice(range))
1217 {
1218 evaluate_unknown_segment(
1219 bounds, data.allocator, data.evaluated_segments);
1220 }
1221 });
1222 for (LocalData &data : data_by_thread) {
1223 if (!data.evaluated_segments.is_empty()) {
1224 r_evaluated_segments.extend(data.evaluated_segments);
1225 memory.transfer_ownership_from(data.allocator);
1226 }
1227 }
1228 }
1229}
1230
1232 IndexMaskMemory &memory)
1233{
1234 if (evaluated_segments.is_empty()) {
1235 return {};
1236 }
1237 if (evaluated_segments.size() == 1) {
1238 const EvaluatedSegment &evaluated_segment = evaluated_segments[0];
1239 switch (evaluated_segment.type) {
1240 case EvaluatedSegment::Type::Full: {
1241 return IndexMask(IndexRange(evaluated_segment.bounds));
1242 }
1243 case EvaluatedSegment::Type::Copy: {
1244 return evaluated_segment.copy_mask->slice_content(evaluated_segment.bounds);
1245 }
1246 case EvaluatedSegment::Type::Indices: {
1247 return IndexMask::from_segments({evaluated_segment.indices}, memory);
1248 }
1249 }
1250 }
1251
1252 std::sort(evaluated_segments.begin(),
1253 evaluated_segments.end(),
1254 [](const EvaluatedSegment &a, const EvaluatedSegment &b) {
1255 return a.bounds.start() < b.bounds.start();
1256 });
1257
1258 Vector<IndexMaskSegment> result_segments = build_result_mask_segments(evaluated_segments);
1259 return IndexMask::from_segments(result_segments, memory);
1260}
1261
1262static IndexMask evaluate_expression_impl(const Expr &root_expression,
1263 IndexMaskMemory &memory,
1264 const ExactEvalMode exact_eval_mode)
1265{
1266 /* Precompute the evaluation order here, because it's used potentially many times throughout the
1267 * algorithm. */
1269 root_expression);
1270
1271 /* Non-overlapping evaluated segments which become the resulting index mask in the end. Note that
1272 * these segments are only sorted in the end. */
1273 Vector<EvaluatedSegment, 16> evaluated_segments;
1274 Vector<IndexRange, 16> short_unknown_segments;
1275
1277 root_expression, eval_order, evaluated_segments, short_unknown_segments);
1279 exact_eval_mode,
1280 eval_order,
1281 short_unknown_segments,
1282 memory,
1283 evaluated_segments);
1284 return evaluated_segments_to_index_mask(evaluated_segments, memory);
1285}
1286
1288{
1289 const ExactEvalMode exact_eval_mode = determine_exact_eval_mode(expression);
1290 IndexMask mask = evaluate_expression_impl(expression, memory, exact_eval_mode);
1291#ifndef NDEBUG
1292 {
1293 /* Check that both exact eval modes have the same result. */
1294 const ExactEvalMode other_exact_eval_mode = (exact_eval_mode == ExactEvalMode::Bits) ?
1295 ExactEvalMode::Indices :
1296 ExactEvalMode::Bits;
1297 IndexMask other_mask = evaluate_expression_impl(expression, memory, other_exact_eval_mode);
1298 BLI_assert(mask == other_mask);
1299 }
1300#endif
1301 return mask;
1302}
1303
1304const UnionExpr &ExprBuilder::merge(const Span<Term> terms)
1305{
1306 Vector<const Expr *> term_expressions;
1307 for (const Term &term : terms) {
1308 term_expressions.append(&this->term_to_expr(term));
1309 }
1310 UnionExpr &expr = scope_.construct<UnionExpr>();
1311 expr.type = Expr::Type::Union;
1312 expr.index = expr_count_++;
1313 expr.terms = std::move(term_expressions);
1314 return expr;
1315}
1316
1317const DifferenceExpr &ExprBuilder::subtract(const Term &main_term, const Span<Term> subtract_terms)
1318{
1319 Vector<const Expr *> term_expressions;
1320 term_expressions.append(&this->term_to_expr(main_term));
1321 for (const Term &subtract_term : subtract_terms) {
1322 term_expressions.append(&this->term_to_expr(subtract_term));
1323 }
1324 DifferenceExpr &expr = scope_.construct<DifferenceExpr>();
1325 expr.type = Expr::Type::Difference;
1326 expr.index = expr_count_++;
1327 expr.terms = std::move(term_expressions);
1328 return expr;
1329}
1330
1331const IntersectionExpr &ExprBuilder::intersect(const Span<Term> terms)
1332{
1333 Vector<const Expr *> term_expressions;
1334 for (const Term &term : terms) {
1335 term_expressions.append(&this->term_to_expr(term));
1336 }
1337 IntersectionExpr &expr = scope_.construct<IntersectionExpr>();
1338 expr.type = Expr::Type::Intersection;
1339 expr.index += expr_count_++;
1340 expr.terms = std::move(term_expressions);
1341 return expr;
1342}
1343
1344const Expr &ExprBuilder::term_to_expr(const Term &term)
1345{
1346 if (const Expr *const *expr = std::get_if<const Expr *>(&term)) {
1347 return **expr;
1348 }
1349 AtomicExpr &expr = scope_.construct<AtomicExpr>();
1350 expr.type = Expr::Type::Atomic;
1351 expr.index = expr_count_++;
1352 if (const IndexRange *range = std::get_if<IndexRange>(&term)) {
1353 expr.mask = &scope_.construct<IndexMask>(*range);
1354 }
1355 else {
1356 expr.mask = std::get<const IndexMask *>(term);
1357 }
1358 return expr;
1359}
1360
1361} // namespace blender::index_mask
#define BLI_assert(a)
Definition BLI_assert.h:50
#define ELEM(...)
constexpr int64_t first() const
constexpr int64_t one_after_last() const
constexpr IndexRange drop_back(int64_t n) const
constexpr int64_t size() const
constexpr bool is_empty() const
static constexpr IndexRange from_begin_end(const int64_t begin, const int64_t end)
constexpr IndexRange with_new_end(const int64_t new_end) const
static constexpr IndexRange from_begin_size(const int64_t begin, const int64_t size)
constexpr int64_t start() const
constexpr IndexRange take_front(int64_t n) const
constexpr IndexRange drop_front(int64_t n) const
MutableSpan< T > allocate_array(int64_t size)
void free_end_of_previous_allocation(const int64_t original_allocation_size, const void *free_after)
MutableSpan< T > construct_array_copy(Span< T > src)
void transfer_ownership_from(LinearAllocator<> &other)
constexpr int64_t size() const
Definition BLI_span.hh:494
constexpr bool is_empty() const
Definition BLI_span.hh:510
constexpr T * data() const
Definition BLI_span.hh:540
constexpr int64_t size_in_bytes() const
Definition BLI_span.hh:502
constexpr T * end() const
Definition BLI_span.hh:549
constexpr T * begin() const
Definition BLI_span.hh:545
Iterator begin() const
int64_t size() const
Iterator end() const
constexpr const T * data() const
Definition BLI_span.hh:216
constexpr int64_t size() const
Definition BLI_span.hh:253
constexpr IndexRange index_range() const
Definition BLI_span.hh:402
constexpr bool is_empty() const
Definition BLI_span.hh:261
bool is_empty() const
Definition BLI_stack.hh:308
void push(const T &value)
Definition BLI_stack.hh:213
int64_t size() const
void append(const T &value)
bool is_empty() const
void remove_first_occurrence_and_reorder(const T &value)
IndexRange index_range() const
void append_unchecked(const T &value)
void extend(Span< T > array)
std::variant< const Expr *, const IndexMask *, IndexRange > Term
IndexMaskSegment shift(const int64_t shift) const
IndexMask slice_content(IndexRange range) const
void to_bits(MutableBitSpan r_bits, int64_t offset=0) const
void foreach_segment(Fn &&fn) const
local_group_size(16, 16) .push_constant(Type b
int count
ccl_device_inline float4 mask(const int4 mask, const float4 a)
uint64_t BitInt
static constexpr int64_t BitsPerInt
void mix_into_first_expr(ExprFn &&expr, FirstBitSpanT &&first_arg, const BitSpanT &...args)
void copy_from_or(FirstBitSpanT &first_arg, const BitSpanT &...args)
void foreach_1_index(const BitSpanT &data, Fn &&fn)
static CoarseSegment & add_coarse_segment__full(CoarseSegment *prev_segment, const int64_t prev_boundary_index, const int64_t current_boundary_index, CoarseResult &result)
static IndexMaskSegment evaluate_exact_with_indices(const Expr &root_expression, LinearAllocator<> &allocator, const IndexRange bounds, const Span< const Expr * > eval_order)
static void evaluate_coarse_and_split_until_segments_are_short(const Expr &root_expression, const Span< const Expr * > eval_order, Vector< EvaluatedSegment, 16 > &r_evaluated_segments, Vector< IndexRange, 16 > &r_short_unknown_segments)
IndexMask evaluate_expression(const Expr &expression, IndexMaskMemory &memory)
static void evaluate_short_unknown_segments_exactly(const Expr &root_expression, const ExactEvalMode exact_eval_mode, const Span< const Expr * > eval_order, const Span< IndexRange > short_unknown_segments, IndexMaskMemory &memory, Vector< EvaluatedSegment, 16 > &r_evaluated_segments)
static IndexMaskSegment union_index_mask_segments(const Span< IndexMaskSegment > segments, const int64_t bounds_min, int16_t *r_values)
static constexpr int64_t segment_size_threshold
static void sort_course_boundaries(MutableSpan< CourseBoundary > boundaries)
static ExactEvalMode determine_exact_eval_mode(const Expr &root_expression)
static CoarseResult evaluate_coarse(const Expr &root_expression, const Span< const Expr * > eval_order, const std::optional< IndexRange > eval_bounds=std::nullopt)
static IndexMaskSegment evaluate_exact_with_bits(const Expr &root_expression, LinearAllocator<> &allocator, const IndexRange bounds, const Span< const Expr * > eval_order)
static IndexMask evaluated_segments_to_index_mask(MutableSpan< EvaluatedSegment > evaluated_segments, IndexMaskMemory &memory)
static Span< int16_t > bits_to_indices(const BoundedBitSpan bits, LinearAllocator<> &allocator)
static constexpr int64_t max_segment_size
static Vector< const Expr *, inline_expr_array_size > compute_eval_order(const Expr &root_expression)
static IndexMaskSegment intersect_index_mask_segments(const Span< IndexMaskSegment > segments, const int64_t bounds_min, int16_t *r_values)
static CoarseSegment & add_coarse_segment__unknown(CoarseSegment *prev_segment, const int64_t prev_boundary_index, const int64_t current_boundary_index, CoarseResult &result)
static Vector< IndexMaskSegment > build_result_mask_segments(const Span< EvaluatedSegment > evaluated_segments)
static void evaluate_coarse_difference(const Span< DifferenceCourseBoundary > boundaries, CoarseResult &r_result)
static IndexMask evaluate_expression_impl(const Expr &root_expression, IndexMaskMemory &memory, const ExactEvalMode exact_eval_mode)
static CoarseSegment & add_coarse_segment__copy(CoarseSegment *prev_segment, const int64_t prev_boundary_index, const int64_t current_boundary_index, const IndexMask &copy_from_mask, CoarseResult &result)
static IndexMaskSegment difference_index_mask_segments(const IndexMaskSegment main_segment, const Span< IndexMaskSegment > subtract_segments, const int64_t bounds_min, int16_t *r_values)
static void evaluate_coarse_intersection(const Span< CourseBoundary > boundaries, const int64_t terms_num, CoarseResult &r_result)
static void evaluate_coarse_union(const Span< CourseBoundary > boundaries, CoarseResult &r_result)
const std::array< int16_t, max_segment_size > & get_static_indices_array()
constexpr int64_t inline_expr_array_size
void parallel_for(const IndexRange range, const int64_t grain_size, const Function &function, const TaskSizeHints &size_hints=detail::TaskSizeHints_Static(1))
Definition BLI_task.hh:95
constexpr IntT ceil_division(const IntT x, const IntT y)
signed short int16_t
Definition stdint.h:76
__int64 int64_t
Definition stdint.h:89
const DifferenceExpr & as_difference() const
const IntersectionExpr & as_intersection() const
const AtomicExpr & as_atomic() const
const UnionExpr & as_union() const