Blender V4.5
draw_cache_impl_grease_pencil.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
10
11#include "BKE_attribute.hh"
12#include "BKE_curves.hh"
13#include "BKE_grease_pencil.h"
14#include "BKE_grease_pencil.hh"
15
16#include "BLI_array_utils.hh"
17#include "BLI_listbase.h"
18#include "BLI_offset_indices.hh"
19#include "BLI_task.hh"
20
22
23#include "DRW_engine.hh"
24#include "DRW_render.hh"
25
26#include "ED_curves.hh"
27#include "ED_grease_pencil.hh"
28
29#include "GPU_batch.hh"
30
31#include "draw_cache.hh"
32#include "draw_cache_impl.hh"
33
36
37namespace blender::draw {
38
46 gpu::Batch *geom_batch;
47 gpu::Batch *lines_batch;
48 gpu::Batch *edit_points;
49 gpu::Batch *edit_lines;
50
51 /* Crazy-space point positions for original points. */
53 /* Selection of original points. */
55 /* vflag of original points. */
57 /* Indices of visible points. */
59
60 /* Crazy-space point positions for all line points. */
62 /* Selection of line points. */
64 /* Indices for lines segments. */
66
71};
72
73/* -------------------------------------------------------------------- */
76
77/* MUST match the format below. */
86
98
99/* MUST match the format below. */
101 float vcol[4]; /* Vertex color */
102 float fcol[4]; /* Fill color */
103};
104
106{
107 static const GPUVertFormat format = []() {
111 return format;
112 }();
113 return &format;
114}
115
117
118/* -------------------------------------------------------------------- */
121
122static bool grease_pencil_batch_cache_valid(const GreasePencil &grease_pencil)
123{
124 BLI_assert(grease_pencil.runtime != nullptr);
125 const GreasePencilBatchCache *cache = static_cast<GreasePencilBatchCache *>(
126 grease_pencil.runtime->batch_cache);
127 return (cache && cache->is_dirty == false &&
128 cache->cache_frame == grease_pencil.runtime->eval_frame);
129}
130
132{
133 BLI_assert(grease_pencil.runtime != nullptr);
134 GreasePencilBatchCache *cache = static_cast<GreasePencilBatchCache *>(
135 grease_pencil.runtime->batch_cache);
136 if (cache == nullptr) {
137 cache = MEM_new<GreasePencilBatchCache>(__func__);
138 grease_pencil.runtime->batch_cache = cache;
139 }
140 else {
141 *cache = {};
142 }
143
144 cache->is_dirty = false;
145 cache->cache_frame = grease_pencil.runtime->eval_frame;
146
147 return cache;
148}
149
151{
152 BLI_assert(grease_pencil.runtime != nullptr);
153 GreasePencilBatchCache *cache = static_cast<GreasePencilBatchCache *>(
154 grease_pencil.runtime->batch_cache);
155 if (cache == nullptr) {
156 return;
157 }
158
163
167
172
176
177 cache->is_dirty = true;
178}
179
181{
182 BLI_assert(grease_pencil.runtime != nullptr);
183 GreasePencilBatchCache *cache = static_cast<GreasePencilBatchCache *>(
184 grease_pencil.runtime->batch_cache);
185 if (!grease_pencil_batch_cache_valid(grease_pencil)) {
186 grease_pencil_batch_cache_clear(grease_pencil);
187 return grease_pencil_batch_cache_init(grease_pencil);
188 }
189
190 return cache;
191}
192
194
195/* -------------------------------------------------------------------- */
198
199BLI_INLINE int32_t pack_rotation_aspect_hardness(float rot, float asp, float softness)
200{
201 int32_t packed = 0;
202 /* Aspect uses 9 bits */
203 float asp_normalized = (asp > 1.0f) ? (1.0f / asp) : asp;
204 packed |= int32_t(unit_float_to_uchar_clamp(asp_normalized));
205 /* Store if inverted in the 9th bit. */
206 if (asp > 1.0f) {
207 packed |= 1 << 8;
208 }
209 /* Rotation uses 9 bits */
210 /* Rotation are in [-90..90] degree range, so we can encode the sign of the angle + the cosine
211 * because the cosine will always be positive. */
213 /* Store sine sign in 9th bit. */
214 if (rot < 0.0f) {
215 packed |= 1 << 17;
216 }
217 /* Hardness uses 8 bits */
218 packed |= int32_t(unit_float_to_uchar_clamp(1.0f - softness)) << 18;
219 return packed;
220}
221
222static void copy_transformed_positions(const Span<float3> src_positions,
223 const IndexRange range,
224 const float4x4 &transform,
225 MutableSpan<float3> dst_positions)
226{
227 for (const int point_i : range) {
228 dst_positions[point_i] = math::transform_point(transform, src_positions[point_i]);
229 }
230}
231
234{
235 return cache->edit_points_pos == nullptr && cache->edit_line_indices == nullptr &&
236 cache->edit_points_indices == nullptr && cache->edit_points == nullptr &&
237 cache->edit_lines == nullptr;
238}
239
241 const GreasePencil &grease_pencil,
242 const Scene &scene)
243{
244 using namespace blender::bke::greasepencil;
245
246 constexpr float no_active_weight = 666.0f;
247
248 BLI_assert(grease_pencil.runtime != nullptr);
249 GreasePencilBatchCache *cache = static_cast<GreasePencilBatchCache *>(
250 grease_pencil.runtime->batch_cache);
251
252 if (cache->edit_points_pos != nullptr) {
253 return;
254 }
255
256 /* Should be discarded together. */
258
259 /* Get active vertex group. */
260 const bDeformGroup *active_defgroup = static_cast<bDeformGroup *>(BLI_findlink(
261 &grease_pencil.vertex_group_names, grease_pencil.vertex_group_active_index - 1));
262 const char *active_defgroup_name = (active_defgroup == nullptr) ? "" : active_defgroup->name;
263
264 /* Get the visible drawings. */
266 ed::greasepencil::retrieve_visible_drawings(scene, grease_pencil, false);
267
268 const Span<const Layer *> layers = grease_pencil.layers();
269
270 static const GPUVertFormat format_points_pos = GPU_vertformat_from_attribute(
271 "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
272
273 static const GPUVertFormat format_points_weight = GPU_vertformat_from_attribute(
274 "selection", GPU_COMP_F32, 1, GPU_FETCH_FLOAT);
275
277 cache->edit_points_pos = GPU_vertbuf_create_with_format_ex(format_points_pos, vbo_flag);
278 cache->edit_points_selection = GPU_vertbuf_create_with_format_ex(format_points_weight, vbo_flag);
279
280 int visible_points_num = 0;
281 int total_line_ids_num = 0;
282 int total_points_num = 0;
283 for (const ed::greasepencil::DrawingInfo &info : drawings) {
284 const bke::CurvesGeometry &curves = info.drawing.strokes();
285 total_points_num += curves.points_num();
286 }
287
288 if (total_points_num == 0) {
289 return;
290 }
291
292 GPU_vertbuf_data_alloc(*cache->edit_points_pos, total_points_num);
293 GPU_vertbuf_data_alloc(*cache->edit_points_selection, total_points_num);
294
295 MutableSpan<float3> points_pos = cache->edit_points_pos->data<float3>();
296 MutableSpan<float> points_weight = cache->edit_points_selection->data<float>();
297
298 int drawing_start_offset = 0;
299 for (const ed::greasepencil::DrawingInfo &info : drawings) {
300 const Layer &layer = *layers[info.layer_index];
301 const float4x4 layer_space_to_object_space = layer.to_object_space(object);
302 const bke::CurvesGeometry &curves = info.drawing.strokes();
303 const OffsetIndices<int> points_by_curve = curves.points_by_curve();
304 const VArray<bool> cyclic = curves.cyclic();
305 IndexMaskMemory memory;
307 object, info.drawing, memory);
308
309 const IndexRange points(drawing_start_offset, curves.points_num());
310 const Span<float3> positions = curves.positions();
311 MutableSpan<float3> positions_slice = points_pos.slice(points);
312 threading::parallel_for(curves.points_range(), 1024, [&](const IndexRange range) {
313 copy_transformed_positions(positions, range, layer_space_to_object_space, positions_slice);
314 });
315
316 /* Get vertex weights of the active vertex group in this drawing. */
317 const VArray<float> weights = *curves.attributes().lookup_or_default<float>(
318 active_defgroup_name, bke::AttrDomain::Point, no_active_weight);
319 MutableSpan<float> weights_slice = points_weight.slice(points);
320 weights.materialize(weights_slice);
321
322 drawing_start_offset += curves.points_num();
323
324 const int drawing_visible_points_num = offset_indices::sum_group_sizes(points_by_curve,
325 visible_strokes);
326
327 /* Add one id for the restart after every curve. */
328 total_line_ids_num += visible_strokes.size();
329 /* Add one id for every non-cyclic segment. */
330 total_line_ids_num += drawing_visible_points_num;
331 /* Add one id for the last segment of every cyclic curve. */
332 total_line_ids_num += array_utils::count_booleans(curves.cyclic(), visible_strokes);
333
334 /* Do not show weights for locked layers. */
335 if (layer.is_locked()) {
336 continue;
337 }
338
339 visible_points_num += drawing_visible_points_num;
340 }
341
342 GPUIndexBufBuilder lines_builder;
343 GPU_indexbuf_init_ex(&lines_builder, GPU_PRIM_LINE_STRIP, total_line_ids_num, total_points_num);
344 MutableSpan<uint> lines_data = GPU_indexbuf_get_data(&lines_builder);
345 int lines_ibo_index = 0;
346
347 GPUIndexBufBuilder points_builder;
348 GPU_indexbuf_init(&points_builder, GPU_PRIM_POINTS, visible_points_num, total_points_num);
349 MutableSpan<uint> points_data = GPU_indexbuf_get_data(&points_builder);
350 int points_ibo_index = 0;
351
352 /* Fill point index buffer with data. */
353 drawing_start_offset = 0;
354 for (const ed::greasepencil::DrawingInfo &info : drawings) {
355 const Layer *layer = layers[info.layer_index];
356 const bke::CurvesGeometry &curves = info.drawing.strokes();
357 const OffsetIndices<int> points_by_curve = curves.points_by_curve();
358 const VArray<bool> cyclic = curves.cyclic();
359 IndexMaskMemory memory;
361 object, info.drawing, memory);
362
363 /* Fill line indices. */
364 visible_strokes.foreach_index([&](const int curve_i) {
365 const IndexRange points = points_by_curve[curve_i];
366 const bool is_cyclic = cyclic[curve_i];
367
368 for (const int point : points) {
369 lines_data[lines_ibo_index++] = point + drawing_start_offset;
370 }
371
372 if (is_cyclic) {
373 lines_data[lines_ibo_index++] = points.first() + drawing_start_offset;
374 }
375
376 lines_data[lines_ibo_index++] = gpu::RESTART_INDEX;
377 });
378
379 /* Fill point indices. */
380 if (!layer->is_locked()) {
381 visible_strokes.foreach_index([&](const int curve_i) {
382 const IndexRange points = points_by_curve[curve_i];
383 for (const int point : points) {
384 points_data[points_ibo_index++] = point + drawing_start_offset;
385 }
386 });
387 }
388
389 drawing_start_offset += curves.points_num();
390 }
391
392 cache->edit_line_indices = GPU_indexbuf_build_ex(&lines_builder, 0, total_points_num, true);
393 cache->edit_points_indices = GPU_indexbuf_build_ex(&points_builder, 0, total_points_num, false);
394
395 /* Create the batches. */
399
403
404 /* Allow creation of buffer texture. */
407
408 cache->is_dirty = false;
409}
410
412 const bke::greasepencil::Drawing &drawing,
413 int layer_index,
414 IndexMaskMemory &memory)
415{
416 const bke::CurvesGeometry &curves = drawing.strokes();
417
418 if (!curves.has_curve_with_type(CURVE_TYPE_NURBS)) {
419 return IndexMask(0);
420 }
421
422 const Array<int> point_to_curve_map = curves.point_to_curve_map();
423 const VArray<int8_t> types = curves.curve_types();
424
425 const IndexMask editable_and_selected_curves =
427 object, drawing, layer_index, memory);
428
429 const IndexMask nurbs_points = IndexMask::from_predicate(
430 curves.points_range(), GrainSize(4096), memory, [&](const int64_t point_i) {
431 const int curve_i = point_to_curve_map[point_i];
432 const bool is_selected = editable_and_selected_curves.contains(curve_i);
433 const bool is_nurbs = types[curve_i] == CURVE_TYPE_NURBS;
434 return is_selected && is_nurbs;
435 });
436
437 return nurbs_points;
438}
439
441 const bke::greasepencil::Drawing &drawing,
442 int layer_index,
443 IndexMaskMemory &memory)
444{
445 const bke::CurvesGeometry &curves = drawing.strokes();
446
447 if (!curves.has_curve_with_type(CURVE_TYPE_NURBS)) {
448 return IndexMask(0);
449 }
450
451 const IndexMask selected_editable_strokes =
453 object, drawing, layer_index, memory);
454
455 const VArray<int8_t> types = curves.curve_types();
457 selected_editable_strokes, GrainSize(4096), memory, [&](const int64_t curve_i) {
458 return types[curve_i] == CURVE_TYPE_NURBS;
459 });
460}
461
463 Object &object,
464 const bke::greasepencil::Drawing &drawing,
465 const int layer_index,
466 IndexMaskMemory &memory)
467{
468 const bke::CurvesGeometry &curves = drawing.strokes();
470 object, drawing, layer_index, memory);
471 if (!curves.has_curve_with_type(CURVE_TYPE_NURBS)) {
472 return visible_strokes;
473 }
474
475 const VArray<int8_t> types = curves.curve_types();
477 visible_strokes, GrainSize(4096), memory, [&](const int64_t curve) {
478 return types[curve] != CURVE_TYPE_NURBS;
479 });
480}
481
483 const bke::greasepencil::Drawing &drawing,
484 const int layer_index,
485 IndexMaskMemory &memory,
486 const VArray<float> &selected_point,
487 const float4x4 &layer_space_to_object_space,
488 MutableSpan<float3> edit_line_points,
489 MutableSpan<float> edit_line_selection,
490 int *r_drawing_line_start_offset,
491 int *r_total_line_ids_num)
492{
494 object, drawing, layer_index, memory);
495 if (nurbs_curves.is_empty()) {
496 return;
497 }
498
499 const bke::CurvesGeometry &curves = drawing.strokes();
500 const Span<float3> positions = curves.positions();
501
503 object, drawing, layer_index, memory);
504 const IndexRange eval_slice = IndexRange(*r_drawing_line_start_offset, nurbs_points.size());
505
506 MutableSpan<float3> positions_eval_slice = edit_line_points.slice(eval_slice);
507
508 /* This will copy over the position but without the layer transform. */
509 array_utils::gather(positions, nurbs_points, positions_eval_slice);
510
511 /* Go through the position and apply the layer transform. */
512 threading::parallel_for(nurbs_points.index_range(), 1024, [&](const IndexRange range) {
513 copy_transformed_positions(
514 positions_eval_slice, range, layer_space_to_object_space, positions_eval_slice);
515 });
516
517 MutableSpan<float> selection_eval_slice = edit_line_selection.slice(eval_slice);
518
519 array_utils::gather(selected_point, nurbs_points, selection_eval_slice);
520
521 /* Add one point for each NURBS point. */
522 *r_drawing_line_start_offset += nurbs_points.size();
523 *r_total_line_ids_num += nurbs_points.size();
524
525 /* Add one id for the restart after every NURBS. */
526 *r_total_line_ids_num += nurbs_curves.size();
527}
528
530 const bke::greasepencil::Drawing &drawing,
531 const int layer_index,
532 IndexMaskMemory &memory,
533 MutableSpan<uint> lines_data,
534 int *r_drawing_line_index,
535 int *r_drawing_line_start_offset)
536{
537 const bke::CurvesGeometry &curves = drawing.strokes();
538 const VArray<bool> cyclic = curves.cyclic();
539 const OffsetIndices<int> points_by_curve_eval = curves.evaluated_points_by_curve();
540
541 const IndexMask visible_strokes_for_lines = grease_pencil_get_visible_non_nurbs_curves(
542 object, drawing, layer_index, memory);
543
544 const int offset = *r_drawing_line_start_offset;
545 int line_index = *r_drawing_line_index;
546
547 /* Fill line indices. */
548 visible_strokes_for_lines.foreach_index([&](const int curve_i) {
549 const IndexRange points = points_by_curve_eval[curve_i];
550 const bool is_cyclic = cyclic[curve_i];
551
552 for (const int point : points) {
553 lines_data[line_index++] = point + offset;
554 }
555
556 if (is_cyclic) {
557 lines_data[line_index++] = points.first() + offset;
558 }
559
560 lines_data[line_index++] = gpu::RESTART_INDEX;
561 });
562
563 *r_drawing_line_index = line_index;
564 *r_drawing_line_start_offset += curves.evaluated_points_num();
565}
566
568 const bke::greasepencil::Drawing &drawing,
569 int layer_index,
570 IndexMaskMemory &memory,
571 MutableSpan<uint> lines_data,
572 int *r_drawing_line_index,
573 int *r_drawing_line_start_offset)
574{
575 const bke::CurvesGeometry &curves = drawing.strokes();
576 const OffsetIndices<int> points_by_curve = curves.points_by_curve();
578 object, drawing, layer_index, memory);
579 if (nurbs_curves.is_empty()) {
580 return;
581 }
582
583 int line_index = *r_drawing_line_index;
584
585 /* Add all NURBS points. */
586 nurbs_curves.foreach_index([&](const int curve_i) {
587 const IndexRange points = points_by_curve[curve_i];
588
589 for (const int point : points.index_range()) {
590 lines_data[line_index++] = point + *r_drawing_line_start_offset;
591 }
592
593 lines_data[line_index++] = gpu::RESTART_INDEX;
594
595 *r_drawing_line_start_offset += points.size();
596 });
597
598 *r_drawing_line_index = line_index;
599}
600
602 const bke::greasepencil::Drawing &drawing,
603 int layer_index,
604 IndexMaskMemory &memory,
605 MutableSpan<uint> lines_data,
606 int *r_drawing_line_index,
607 int *r_drawing_line_start_offset)
608{
610 object, drawing, layer_index, memory);
611 if (bezier_points.is_empty()) {
612 return;
613 }
614
615 const int offset = *r_drawing_line_start_offset;
616 int line_index = *r_drawing_line_index;
617
618 /* Add all bezier points. */
619 for (const int point : bezier_points.index_range()) {
620 lines_data[line_index++] = point + bezier_points.size() * 0 + offset;
621 lines_data[line_index++] = point + bezier_points.size() * 1 + offset;
622 lines_data[line_index++] = point + bezier_points.size() * 2 + offset;
623
624 lines_data[line_index++] = gpu::RESTART_INDEX;
625 }
626
627 *r_drawing_line_index = line_index;
628 *r_drawing_line_start_offset += bezier_points.size() * 3;
629}
630
631static void index_buf_add_points(Object &object,
632 const bke::greasepencil::Drawing &drawing,
633 int layer_index,
634 IndexMaskMemory &memory,
635 MutableSpan<uint> points_data,
636 int *r_drawing_point_index,
637 int *r_drawing_start_offset)
638{
639 const bke::CurvesGeometry &curves = drawing.strokes();
640 const OffsetIndices<int> points_by_curve = curves.points_by_curve();
641
642 /* Fill point indices. */
643 const IndexMask selected_editable_strokes =
645 object, drawing, layer_index, memory);
646
647 const int offset = *r_drawing_start_offset;
648 int ibo_index = *r_drawing_point_index;
649
650 selected_editable_strokes.foreach_index([&](const int curve_i) {
651 const IndexRange points = points_by_curve[curve_i];
652 for (const int point : points) {
653 points_data[ibo_index++] = point + offset;
654 }
655 });
656
657 *r_drawing_point_index = ibo_index;
658 *r_drawing_start_offset += curves.points_num();
659}
660
662 const bke::greasepencil::Drawing &drawing,
663 int layer_index,
664 IndexMaskMemory &memory,
665 MutableSpan<uint> points_data,
666 int *r_drawing_point_index,
667 int *r_drawing_start_offset)
668{
670 object, drawing, layer_index, memory);
671 if (bezier_points.is_empty()) {
672 return;
673 }
674
675 const int offset = *r_drawing_start_offset;
676 int ibo_index = *r_drawing_point_index;
677
678 /* Add all bezier points. */
679 for (const int point : IndexRange(bezier_points.size() * 2)) {
680 points_data[ibo_index++] = point + offset;
681 }
682
683 *r_drawing_point_index = ibo_index;
684 *r_drawing_start_offset += bezier_points.size() * 2;
685}
686
687/* Still use legacy vflag for GPv3 for now due to common shader defines. */
688#define GREASE_PENCIL_EDIT_POINT_SELECTED (1 << 0)
689#define GREASE_PENCIL_EDIT_STROKE_SELECTED (1 << 1)
690#define GREASE_PENCIL_EDIT_MULTIFRAME (1 << 2)
691#define GREASE_PENCIL_EDIT_STROKE_START (1 << 3)
692#define GREASE_PENCIL_EDIT_STROKE_END (1 << 4)
693#define GREASE_PENCIL_EDIT_POINT_DIMMED (1 << 5)
694
696 const GreasePencil &grease_pencil,
697 const Scene &scene)
698{
699 using namespace blender::bke::greasepencil;
700 BLI_assert(grease_pencil.runtime != nullptr);
701 GreasePencilBatchCache *cache = static_cast<GreasePencilBatchCache *>(
702 grease_pencil.runtime->batch_cache);
703
704 if (cache->edit_points_pos != nullptr) {
705 return;
706 }
707
708 /* Should be discarded together. */
710
711 /* Get the visible drawings. */
713 ed::greasepencil::retrieve_visible_drawings(scene, grease_pencil, false);
714
715 const Span<const Layer *> layers = grease_pencil.layers();
716
717 static const GPUVertFormat format_edit_points_pos = GPU_vertformat_from_attribute(
718 "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
719
720 static const GPUVertFormat format_edit_line_pos = GPU_vertformat_from_attribute(
721 "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
722
723 static const GPUVertFormat format_edit_points_selection = GPU_vertformat_from_attribute(
724 "selection", GPU_COMP_F32, 1, GPU_FETCH_FLOAT);
725
726 static const GPUVertFormat format_edit_points_vflag = GPU_vertformat_from_attribute(
727 "vflag", GPU_COMP_U32, 1, GPU_FETCH_INT);
728
729 static const GPUVertFormat format_edit_line_selection = GPU_vertformat_from_attribute(
730 "selection", GPU_COMP_F32, 1, GPU_FETCH_FLOAT);
731
733 cache->edit_points_pos = GPU_vertbuf_create_with_format_ex(format_edit_points_pos, vbo_flag);
734 cache->edit_points_selection = GPU_vertbuf_create_with_format_ex(format_edit_points_selection,
735 vbo_flag);
736 cache->edit_points_vflag = GPU_vertbuf_create_with_format_ex(format_edit_points_vflag, vbo_flag);
737 cache->edit_line_pos = GPU_vertbuf_create_with_format_ex(format_edit_line_pos, vbo_flag);
738 cache->edit_line_selection = GPU_vertbuf_create_with_format_ex(format_edit_line_selection,
739 vbo_flag);
740
741 int total_points_num = 0;
742 for (const ed::greasepencil::DrawingInfo &info : drawings) {
743 const Layer &layer = *layers[info.layer_index];
744 /* Do not show points for locked layers. */
745 if (layer.is_locked()) {
746 continue;
747 }
748
749 const bke::CurvesGeometry &curves = info.drawing.strokes();
750 total_points_num += curves.points_num();
751 }
752
753 int total_line_points_num = 0;
754 for (const ed::greasepencil::DrawingInfo &info : drawings) {
755 const bke::CurvesGeometry &curves = info.drawing.strokes();
756 total_line_points_num += curves.evaluated_points_num();
757 }
758
759 int total_bezier_point_num = 0;
760 for (const ed::greasepencil::DrawingInfo &info : drawings) {
761 IndexMaskMemory memory;
763 object, info.drawing, info.layer_index, memory);
764
765 total_bezier_point_num += bezier_points.size();
766 }
767
768 for (const ed::greasepencil::DrawingInfo &info : drawings) {
769 IndexMaskMemory memory;
771 object, info.drawing, info.layer_index, memory);
772
773 /* Add one point for each NURBS point. */
774 total_line_points_num += nurbs_points.size();
775 }
776
777 /* Add two for each bezier point, (one left, one right). */
778 total_points_num += total_bezier_point_num * 2;
779 /* Add three for each bezier point, (one left, one right and one for the center point). */
780 total_line_points_num += total_bezier_point_num * 3;
781
782 if (total_points_num == 0) {
783 return;
784 }
785
786 GPU_vertbuf_data_alloc(*cache->edit_points_pos, total_points_num);
787 GPU_vertbuf_data_alloc(*cache->edit_points_selection, total_points_num);
788 GPU_vertbuf_data_alloc(*cache->edit_points_vflag, total_points_num);
789 GPU_vertbuf_data_alloc(*cache->edit_line_pos, total_line_points_num);
790 GPU_vertbuf_data_alloc(*cache->edit_line_selection, total_line_points_num);
791
792 MutableSpan<float3> edit_points = cache->edit_points_pos->data<float3>();
793 MutableSpan<float> edit_points_selection = cache->edit_points_selection->data<float>();
794 MutableSpan<uint32_t> edit_points_vflag = cache->edit_points_vflag->data<uint32_t>();
795 MutableSpan<float3> edit_line_points = cache->edit_line_pos->data<float3>();
796 MutableSpan<float> edit_line_selection = cache->edit_line_selection->data<float>();
797 edit_points_selection.fill(0.0f);
798 edit_points_vflag.fill(0);
799 edit_line_selection.fill(0.0f);
800
801 int visible_points_num = 0;
802 int total_line_ids_num = 0;
803 int drawing_start_offset = 0;
804 int drawing_line_start_offset = 0;
805 for (const ed::greasepencil::DrawingInfo &info : drawings) {
806 const Layer &layer = *layers[info.layer_index];
807 const float4x4 layer_space_to_object_space = layer.to_object_space(object);
808 const bke::CurvesGeometry &curves = info.drawing.strokes();
809 const OffsetIndices<int> points_by_curve_eval = curves.evaluated_points_by_curve();
810 const OffsetIndices<int> points_by_curve = curves.points_by_curve();
811
812 IndexMaskMemory memory;
813 const IndexMask visible_strokes_for_lines = grease_pencil_get_visible_non_nurbs_curves(
814 object, info.drawing, info.layer_index, memory);
815
816 const IndexRange points(drawing_start_offset, curves.points_num());
817 const IndexRange points_eval(drawing_line_start_offset, curves.evaluated_points_num());
818
819 const Span<float3> positions = curves.positions();
820 if (!layer.is_locked()) {
821 MutableSpan<float3> positions_slice = edit_points.slice(points);
822 threading::parallel_for(curves.points_range(), 1024, [&](const IndexRange range) {
823 copy_transformed_positions(positions, range, layer_space_to_object_space, positions_slice);
824 });
825 }
826
827 const Span<float3> positions_eval = curves.evaluated_positions();
828
829 MutableSpan<float3> positions_eval_slice = edit_line_points.slice(points_eval);
831 IndexRange(curves.evaluated_points_num()), 1024, [&](const IndexRange range) {
832 copy_transformed_positions(
833 positions_eval, range, layer_space_to_object_space, positions_eval_slice);
834 });
835
836 /* Do not show selection for locked layers. */
837 if (!layer.is_locked()) {
838
839 /* Flag the start and end points. */
840 for (const int curve_i : curves.curves_range()) {
841 const IndexRange points = points_by_curve[curve_i].shift(drawing_start_offset);
842 edit_points_vflag[points.first()] |= GREASE_PENCIL_EDIT_STROKE_START;
843 edit_points_vflag[points.last()] |= GREASE_PENCIL_EDIT_STROKE_END;
844 }
845
846 const IndexMask selected_editable_points =
848 object, info.drawing, info.layer_index, memory);
849
850 MutableSpan<float> selection_slice = edit_points_selection.slice(points);
851 index_mask::masked_fill(selection_slice, 1.0f, selected_editable_points);
852
853 MutableSpan<float> line_selection_slice = edit_line_selection.slice(points_eval);
854
855 /* Poly curves evaluated points match the curve points, no need to interpolate. */
856 if (curves.is_single_type(CURVE_TYPE_POLY)) {
857 array_utils::copy(selection_slice.as_span(), line_selection_slice);
858 }
859 else {
860 curves.ensure_can_interpolate_to_evaluated();
861 curves.interpolate_to_evaluated(selection_slice.as_span(), line_selection_slice);
862 }
863 }
864
865 drawing_line_start_offset += curves.evaluated_points_num();
866
867 /* Add one id for the restart after every curve. */
868 total_line_ids_num += visible_strokes_for_lines.size();
869 /* Add one id for every non-cyclic segment. */
870 total_line_ids_num += offset_indices::sum_group_sizes(points_by_curve_eval,
871 visible_strokes_for_lines);
872 /* Add one id for the last segment of every cyclic curve. */
873 total_line_ids_num += array_utils::count_booleans(curves.cyclic(), visible_strokes_for_lines);
874
875 /* Do not show points for locked layers. */
876 if (layer.is_locked()) {
877 continue;
878 }
879
880 drawing_start_offset += curves.points_num();
881 const IndexMask selected_editable_strokes =
883 object, info.drawing, info.layer_index, memory);
884
885 /* Add one id for every point in a selected curve. */
886 visible_points_num += offset_indices::sum_group_sizes(points_by_curve,
887 selected_editable_strokes);
888
889 const VArray<float> selected_point = *curves.attributes().lookup_or_default<float>(
890 ".selection", bke::AttrDomain::Point, true);
891
893 info.drawing,
894 info.layer_index,
895 memory,
896 selected_point,
897 layer_space_to_object_space,
898 edit_line_points,
899 edit_line_selection,
900 &drawing_line_start_offset,
901 &total_line_ids_num);
902
904 object, info.drawing, info.layer_index, memory);
905 if (bezier_points.is_empty()) {
906 continue;
907 }
908
909 const IndexRange left_slice = IndexRange(drawing_start_offset, bezier_points.size());
910 const IndexRange right_slice = IndexRange(drawing_start_offset + bezier_points.size(),
911 bezier_points.size());
912
913 MutableSpan<float3> positions_slice_left = edit_points.slice(left_slice);
914 MutableSpan<float3> positions_slice_right = edit_points.slice(right_slice);
915
916 const Span<float3> handles_left = curves.handle_positions_left();
917 const Span<float3> handles_right = curves.handle_positions_right();
918
919 /* This will copy over the position but without the layer transform. */
920 array_utils::gather(handles_left, bezier_points, positions_slice_left);
921 array_utils::gather(handles_right, bezier_points, positions_slice_right);
922
923 /* Go through the position and apply the layer transform. */
924 threading::parallel_for(bezier_points.index_range(), 1024, [&](const IndexRange range) {
925 copy_transformed_positions(
926 positions_slice_left, range, layer_space_to_object_space, positions_slice_left);
927 copy_transformed_positions(
928 positions_slice_right, range, layer_space_to_object_space, positions_slice_right);
929 });
930
931 const VArray<float> selected_left = *curves.attributes().lookup_or_default<float>(
932 ".selection_handle_left", bke::AttrDomain::Point, true);
933 const VArray<float> selected_right = *curves.attributes().lookup_or_default<float>(
934 ".selection_handle_right", bke::AttrDomain::Point, true);
935
936 MutableSpan<float> selection_slice_left = edit_points_selection.slice(left_slice);
937 MutableSpan<float> selection_slice_right = edit_points_selection.slice(right_slice);
938 array_utils::gather(selected_left, bezier_points, selection_slice_left);
939 array_utils::gather(selected_right, bezier_points, selection_slice_right);
940
941 const IndexRange eval_left_slice = IndexRange(drawing_line_start_offset, bezier_points.size());
942 const IndexRange eval_center_slice = IndexRange(
943 drawing_line_start_offset + bezier_points.size(), bezier_points.size());
944 const IndexRange eval_right_slice = IndexRange(
945 drawing_line_start_offset + bezier_points.size() * 2, bezier_points.size());
946
947 MutableSpan<float3> positions_eval_left_slice = edit_line_points.slice(eval_left_slice);
948 MutableSpan<float3> positions_eval_center_slice = edit_line_points.slice(eval_center_slice);
949 MutableSpan<float3> positions_eval_right_slice = edit_line_points.slice(eval_right_slice);
950
951 array_utils::copy(positions_slice_left.as_span(), positions_eval_left_slice);
952 array_utils::copy(positions_slice_right.as_span(), positions_eval_right_slice);
953
954 /* This will copy over the position but without the layer transform. */
955 array_utils::gather(positions, bezier_points, positions_eval_center_slice);
956
957 /* Go through the position and apply the layer transform. */
958 threading::parallel_for(bezier_points.index_range(), 1024, [&](const IndexRange range) {
959 copy_transformed_positions(positions_eval_center_slice,
960 range,
961 layer_space_to_object_space,
962 positions_eval_center_slice);
963 });
964
965 MutableSpan<float> selection_eval_slice_left = edit_line_selection.slice(eval_left_slice);
966 MutableSpan<float> selection_eval_slice_center = edit_line_selection.slice(eval_center_slice);
967 MutableSpan<float> selection_eval_slice_right = edit_line_selection.slice(eval_right_slice);
968 array_utils::copy(selection_slice_left.as_span(), selection_eval_slice_left);
969 array_utils::copy(selection_slice_right.as_span(), selection_eval_slice_right);
970
971 array_utils::gather(selected_point, bezier_points, selection_eval_slice_center);
972
973 /* Add two for each bezier point, (one left, one right). */
974 visible_points_num += bezier_points.size() * 2;
975 drawing_start_offset += bezier_points.size() * 2;
976
977 /* Add three for each bezier point, (one left, one right and one for the center point). */
978 drawing_line_start_offset += bezier_points.size() * 3;
979 total_line_ids_num += bezier_points.size() * 3;
980
981 /* Add one id for the restart after every bezier. */
982 total_line_ids_num += bezier_points.size();
983 }
984
985 GPUIndexBufBuilder lines_builder;
987 &lines_builder, GPU_PRIM_LINE_STRIP, total_line_ids_num, total_line_points_num);
988 MutableSpan<uint> lines_data = GPU_indexbuf_get_data(&lines_builder);
989 int lines_ibo_index = 0;
990
991 GPUIndexBufBuilder points_builder;
992 GPU_indexbuf_init(&points_builder, GPU_PRIM_POINTS, visible_points_num, total_points_num);
993 MutableSpan<uint> points_data = GPU_indexbuf_get_data(&points_builder);
994 int points_ibo_index = 0;
995
996 /* Fill line index and point index buffers with data. */
997 drawing_start_offset = 0;
998 drawing_line_start_offset = 0;
999 for (const ed::greasepencil::DrawingInfo &info : drawings) {
1000 const Layer *layer = layers[info.layer_index];
1001 IndexMaskMemory memory;
1002
1004 info.drawing,
1005 info.layer_index,
1006 memory,
1007 lines_data,
1008 &lines_ibo_index,
1009 &drawing_line_start_offset);
1010
1011 if (!layer->is_locked()) {
1013 info.drawing,
1014 info.layer_index,
1015 memory,
1016 lines_data,
1017 &lines_ibo_index,
1018 &drawing_line_start_offset);
1020 info.drawing,
1021 info.layer_index,
1022 memory,
1023 lines_data,
1024 &lines_ibo_index,
1025 &drawing_line_start_offset);
1026 index_buf_add_points(object,
1027 info.drawing,
1028 info.layer_index,
1029 memory,
1030 points_data,
1031 &points_ibo_index,
1032 &drawing_start_offset);
1034 info.drawing,
1035 info.layer_index,
1036 memory,
1037 points_data,
1038 &points_ibo_index,
1039 &drawing_start_offset);
1040 }
1041 }
1042
1043 cache->edit_line_indices = GPU_indexbuf_build_ex(&lines_builder, 0, INT_MAX, true);
1044 cache->edit_points_indices = GPU_indexbuf_build_ex(&points_builder, 0, INT_MAX, false);
1045
1046 /* Create the batches */
1051
1055
1056 /* Allow creation of buffer texture. */
1062
1063 cache->is_dirty = false;
1064}
1065
1066template<typename T>
1068{
1069 if (curves.is_single_type(CURVE_TYPE_POLY)) {
1070 return input;
1071 }
1072
1073 Array<T> out(curves.evaluated_points_num());
1074 curves.interpolate_to_evaluated(VArraySpan(input), out.as_mutable_span());
1075 return VArray<T>::ForContainer(std::move(out));
1076};
1077
1079 const GreasePencil &grease_pencil,
1080 const Scene &scene)
1081{
1082 using namespace blender::bke::greasepencil;
1083 BLI_assert(grease_pencil.runtime != nullptr);
1084 GreasePencilBatchCache *cache = static_cast<GreasePencilBatchCache *>(
1085 grease_pencil.runtime->batch_cache);
1086
1087 if (cache->vbo != nullptr) {
1088 return;
1089 }
1090
1091 /* Should be discarded together. */
1092 BLI_assert(cache->vbo == nullptr && cache->ibo == nullptr);
1093 BLI_assert(cache->geom_batch == nullptr);
1094
1095 /* Get the visible drawings. */
1097 ed::greasepencil::retrieve_visible_drawings(scene, grease_pencil, true);
1098
1099 /* First, count how many vertices and triangles are needed for the whole object. Also record the
1100 * offsets into the curves for the vertices and triangles. */
1101 int total_verts_num = 0;
1102 int total_triangles_num = 0;
1103 int v_offset = 0;
1104 Vector<Array<int>> verts_start_offsets_per_visible_drawing;
1105 Vector<Array<int>> tris_start_offsets_per_visible_drawing;
1106 for (const ed::greasepencil::DrawingInfo &info : drawings) {
1107 const bke::CurvesGeometry &curves = info.drawing.strokes();
1108 const OffsetIndices<int> points_by_curve = curves.evaluated_points_by_curve();
1109 const VArray<bool> cyclic = curves.cyclic();
1110 IndexMaskMemory memory;
1112 object, info.drawing, memory);
1113
1114 const int num_curves = visible_strokes.size();
1115 const int verts_start_offsets_size = num_curves;
1116 const int tris_start_offsets_size = num_curves;
1117 Array<int> verts_start_offsets(verts_start_offsets_size);
1118 Array<int> tris_start_offsets(tris_start_offsets_size);
1119
1120 /* Calculate the triangle offsets for all the visible curves. */
1121 int t_offset = 0;
1122 int pos = 0;
1123 for (const int curve_i : curves.curves_range()) {
1124 IndexRange points = points_by_curve[curve_i];
1125 if (visible_strokes.contains(curve_i)) {
1126 tris_start_offsets[pos] = t_offset;
1127 pos++;
1128 }
1129 if (points.size() >= 3) {
1130 t_offset += points.size() - 2;
1131 }
1132 }
1133
1134 /* Calculate the vertex offsets for all the visible curves. */
1135 int num_cyclic = 0;
1136 int num_points = 0;
1137 visible_strokes.foreach_index([&](const int curve_i, const int pos) {
1138 IndexRange points = points_by_curve[curve_i];
1139 const bool is_cyclic = cyclic[curve_i] && (points.size() > 2);
1140
1141 if (is_cyclic) {
1142 num_cyclic++;
1143 }
1144
1145 verts_start_offsets[pos] = v_offset;
1146 v_offset += 1 + points.size() + (is_cyclic ? 1 : 0) + 1;
1147 num_points += points.size();
1148 });
1149
1150 /* One vertex is stored before and after as padding. Cyclic strokes have one extra vertex. */
1151 total_verts_num += num_points + num_cyclic + num_curves * 2;
1152 total_triangles_num += (num_points + num_cyclic) * 2;
1153 total_triangles_num += info.drawing.triangles().size();
1154
1155 verts_start_offsets_per_visible_drawing.append(std::move(verts_start_offsets));
1156 tris_start_offsets_per_visible_drawing.append(std::move(tris_start_offsets));
1157 }
1158
1160 /* Create VBOs. */
1162 const GPUVertFormat *format_col = grease_pencil_color_format();
1163 cache->vbo = GPU_vertbuf_create_with_format_ex(*format, vbo_flag);
1164 cache->vbo_col = GPU_vertbuf_create_with_format_ex(*format_col, vbo_flag);
1165 /* Add extra space at the end of the buffer because of quad load. */
1166 GPU_vertbuf_data_alloc(*cache->vbo, total_verts_num + 2);
1167 GPU_vertbuf_data_alloc(*cache->vbo_col, total_verts_num + 2);
1168
1172 /* Create IBO. */
1173 GPU_indexbuf_init(&ibo, GPU_PRIM_TRIS, total_triangles_num, 0xFFFFFFFFu);
1174
1175 /* Fill buffers with data. */
1176 for (const int drawing_i : drawings.index_range()) {
1177 const ed::greasepencil::DrawingInfo &info = drawings[drawing_i];
1178 const Layer &layer = grease_pencil.layer(info.layer_index);
1179 const float4x4 layer_space_to_object_space = layer.to_object_space(object);
1180 const float4x4 object_space_to_layer_space = math::invert(layer_space_to_object_space);
1181 const bke::CurvesGeometry &curves = info.drawing.strokes();
1182 if (curves.evaluated_points_num() == 0) {
1183 continue;
1184 }
1185
1186 const bke::AttributeAccessor attributes = curves.attributes();
1187 const OffsetIndices<int> points_by_curve = curves.evaluated_points_by_curve();
1188 const Span<float3> positions = curves.evaluated_positions();
1189 const VArray<bool> cyclic = curves.cyclic();
1190
1191 curves.ensure_can_interpolate_to_evaluated();
1192
1196 *attributes.lookup_or_default<float>("rotation", bke::AttrDomain::Point, 0.0f), curves);
1199 "vertex_color", bke::AttrDomain::Point, ColorGeometry4f(0.0f, 0.0f, 0.0f, 0.0f)),
1200 curves);
1201
1202 /* Assumes that if the ".selection" attribute does not exist, all points are selected. */
1203 const VArray<float> selection_float = *attributes.lookup_or_default<float>(
1204 ".selection", bke::AttrDomain::Point, true);
1205 const VArray<int8_t> start_caps = *attributes.lookup_or_default<int8_t>(
1207 const VArray<int8_t> end_caps = *attributes.lookup_or_default<int8_t>(
1208 "end_cap", bke::AttrDomain::Curve, 0);
1209 const VArray<float> stroke_softness = *attributes.lookup_or_default<float>(
1210 "softness", bke::AttrDomain::Curve, 0.0f);
1211 const VArray<float> stroke_point_aspect_ratios = *attributes.lookup_or_default<float>(
1212 "aspect_ratio", bke::AttrDomain::Curve, 1.0f);
1213 const VArray<ColorGeometry4f> stroke_fill_colors = info.drawing.fill_colors();
1214 const VArray<int> materials = *attributes.lookup_or_default<int>(
1215 "material_index", bke::AttrDomain::Curve, 0);
1216 const VArray<float> u_translations = *attributes.lookup_or_default<float>(
1217 "u_translation", bke::AttrDomain::Curve, 0.0f);
1218 const VArray<float> u_scales = *attributes.lookup_or_default<float>(
1219 "u_scale", bke::AttrDomain::Curve, 1.0f);
1220 const VArray<float> fill_opacities = *attributes.lookup_or_default<float>(
1221 "fill_opacity", bke::AttrDomain::Curve, 1.0f);
1222
1223 const Span<int3> triangles = info.drawing.triangles();
1224 const Span<float4x2> texture_matrices = info.drawing.texture_matrices();
1225 const Span<int> verts_start_offsets = verts_start_offsets_per_visible_drawing[drawing_i];
1226 const Span<int> tris_start_offsets = tris_start_offsets_per_visible_drawing[drawing_i];
1227 IndexMaskMemory memory;
1229 object, info.drawing, memory);
1230
1231 curves.ensure_evaluated_lengths();
1232
1233 auto populate_point = [&](IndexRange verts_range,
1234 int curve_i,
1235 int8_t start_cap,
1236 int8_t end_cap,
1237 int point_i,
1238 int idx,
1239 float u_stroke,
1240 const float4x2 &texture_matrix,
1241 GreasePencilStrokeVert &s_vert,
1242 GreasePencilColorVert &c_vert) {
1243 const float3 pos = math::transform_point(layer_space_to_object_space, positions[point_i]);
1244 copy_v3_v3(s_vert.pos, pos);
1245 /* GP data itself does not constrain radii to be positive, but drawing code expects it, and
1246 * use negative values as a special 'flag' to get rounded caps. */
1247 s_vert.radius = math::max(radii[point_i], 0.0f) *
1248 ((end_cap == GP_STROKE_CAP_TYPE_ROUND) ? 1.0f : -1.0f);
1249 s_vert.opacity = opacities[point_i] *
1250 ((start_cap == GP_STROKE_CAP_TYPE_ROUND) ? 1.0f : -1.0f);
1251 s_vert.point_id = verts_range[idx];
1252 s_vert.stroke_id = verts_range.first();
1253 /* The material index is allowed to be negative as it's stored as a generic attribute. To
1254 * ensure the material used by the shader is valid this needs to be clamped to zero. */
1255 s_vert.mat = std::max(materials[curve_i], 0) % GPENCIL_MATERIAL_BUFFER_LEN;
1256
1257 s_vert.packed_asp_hard_rot = pack_rotation_aspect_hardness(
1258 rotations[point_i], stroke_point_aspect_ratios[curve_i], stroke_softness[curve_i]);
1259 s_vert.u_stroke = u_stroke;
1260 copy_v2_v2(s_vert.uv_fill, texture_matrix * float4(pos, 1.0f));
1261
1262 copy_v4_v4(c_vert.vcol, vertex_colors[point_i]);
1263 copy_v4_v4(c_vert.fcol, stroke_fill_colors[curve_i]);
1264 c_vert.fcol[3] = (int(c_vert.fcol[3] * 10000.0f) * 10.0f) + fill_opacities[curve_i];
1265
1266 int v_mat = (verts_range[idx] << GP_VERTEX_ID_SHIFT) | GP_IS_STROKE_VERTEX_BIT;
1267 GPU_indexbuf_add_tri_verts(&ibo, v_mat + 0, v_mat + 1, v_mat + 2);
1268 GPU_indexbuf_add_tri_verts(&ibo, v_mat + 2, v_mat + 1, v_mat + 3);
1269 };
1270
1271 visible_strokes.foreach_index([&](const int curve_i, const int pos) {
1272 const IndexRange points = points_by_curve[curve_i];
1273 const bool is_cyclic = cyclic[curve_i] && (points.size() > 2);
1274 const int verts_start_offset = verts_start_offsets[pos];
1275 const int tris_start_offset = tris_start_offsets[pos];
1276 const int num_verts = 1 + points.size() + (is_cyclic ? 1 : 0) + 1;
1277 const IndexRange verts_range = IndexRange(verts_start_offset, num_verts);
1278 MutableSpan<GreasePencilStrokeVert> verts_slice = verts.slice(verts_range);
1279 MutableSpan<GreasePencilColorVert> cols_slice = cols.slice(verts_range);
1280 const float4x2 texture_matrix = texture_matrices[curve_i] * object_space_to_layer_space;
1281
1282 const Span<float> lengths = curves.evaluated_lengths_for_curve(curve_i, cyclic[curve_i]);
1283
1284 /* First vertex is not drawn. */
1285 verts_slice.first().mat = -1;
1286
1287 /* If the stroke has more than 2 points, add the triangle indices to the index buffer. */
1288 if (points.size() >= 3) {
1289 const Span<int3> tris_slice = triangles.slice(tris_start_offset, points.size() - 2);
1290 for (const int3 tri : tris_slice) {
1292 (verts_range[1] + tri.x) << GP_VERTEX_ID_SHIFT,
1293 (verts_range[1] + tri.y) << GP_VERTEX_ID_SHIFT,
1294 (verts_range[1] + tri.z) << GP_VERTEX_ID_SHIFT);
1295 }
1296 }
1297
1298 /* Write all the point attributes to the vertex buffers. Create a quad for each point. */
1299 const float u_scale = u_scales[curve_i];
1300 const float u_translation = u_translations[curve_i];
1301 for (const int i : IndexRange(points.size())) {
1302 const int idx = i + 1;
1303 const float u_stroke = u_scale * (i > 0 ? lengths[i - 1] : 0.0f) + u_translation;
1304 populate_point(verts_range,
1305 curve_i,
1306 start_caps[curve_i],
1307 end_caps[curve_i],
1308 points[i],
1309 idx,
1310 u_stroke,
1311 texture_matrix,
1312 verts_slice[idx],
1313 cols_slice[idx]);
1314 }
1315
1316 if (is_cyclic) {
1317 const int idx = points.size() + 1;
1318 const float u = points.size() > 1 ? lengths[points.size() - 1] : 0.0f;
1319 const float u_stroke = u_scale * u + u_translation;
1320 populate_point(verts_range,
1321 curve_i,
1322 start_caps[curve_i],
1323 end_caps[curve_i],
1324 points[0],
1325 idx,
1326 u_stroke,
1327 texture_matrix,
1328 verts_slice[idx],
1329 cols_slice[idx]);
1330 }
1331
1332 /* Last vertex is not drawn. */
1333 verts_slice.last().mat = -1;
1334 });
1335 }
1336
1337 /* Mark last 2 verts as invalid. */
1338 verts[total_verts_num + 0].mat = -1;
1339 verts[total_verts_num + 1].mat = -1;
1340 /* Also mark first vert as invalid. */
1341 verts[0].mat = -1;
1342
1343 /* Finish the IBO. */
1344 cache->ibo = GPU_indexbuf_build(&ibo);
1345 /* Create the batches */
1346 cache->geom_batch = GPU_batch_create(GPU_PRIM_TRIS, cache->vbo, cache->ibo);
1347 /* Allow creation of buffer texture. */
1348 GPU_vertbuf_use(cache->vbo);
1349 GPU_vertbuf_use(cache->vbo_col);
1350
1351 cache->is_dirty = false;
1352}
1353
1355 const GreasePencil &grease_pencil,
1356 const Scene &scene)
1357{
1358 using namespace blender::bke::greasepencil;
1359
1360 BLI_assert(grease_pencil.runtime != nullptr);
1361 GreasePencilBatchCache *cache = static_cast<GreasePencilBatchCache *>(
1362 grease_pencil.runtime->batch_cache);
1363
1364 if (cache->lines_batch != nullptr) {
1365 return;
1366 }
1367
1368 grease_pencil_geom_batch_ensure(object, grease_pencil, scene);
1369 uint32_t max_index = GPU_vertbuf_get_vertex_len(cache->vbo);
1370
1371 /* Get the visible drawings. */
1373 ed::greasepencil::retrieve_visible_drawings(scene, grease_pencil, true);
1374
1375 Vector<int> index_start_per_curve;
1376 Vector<bool> cyclic_per_curve;
1377 Vector<bool> is_onion_per_curve;
1378
1379 int index_len = 0;
1380 for (const ed::greasepencil::DrawingInfo &info : drawings) {
1381 const bke::CurvesGeometry &curves = info.drawing.strokes();
1382 const OffsetIndices<int> points_by_curve = curves.evaluated_points_by_curve();
1383 const VArray<bool> cyclic = curves.cyclic();
1384 IndexMaskMemory memory;
1386 object, info.drawing, memory);
1387
1388 visible_strokes.foreach_index([&](const int curve_i) {
1389 const IndexRange points = points_by_curve[curve_i];
1390 const int point_len = points.size();
1391 const int point_start = index_len;
1392 const bool is_cyclic = cyclic[curve_i] && (point_len > 2);
1393 /* Count the primitive restart. */
1394 index_len += point_len + (is_cyclic ? 1 : 0) + 1;
1395 /* Don't draw the onion frames in wireframe mode. */
1396 index_start_per_curve.append(point_start);
1397 cyclic_per_curve.append(is_cyclic);
1398 is_onion_per_curve.append(info.onion_id != 0);
1399 });
1400 }
1401 index_start_per_curve.append(index_len);
1402 const OffsetIndices<int> range_per_curve(index_start_per_curve, offset_indices::NoSortCheck{});
1403
1405 GPU_indexbuf_init_ex(&elb, GPU_PRIM_LINE_STRIP, index_len, max_index);
1406
1408
1409 threading::parallel_for(cyclic_per_curve.index_range(), 1024, [&](const IndexRange range) {
1410 for (const int curve : range) {
1411 /* Drop the trailing restart index. */
1412 const IndexRange offset_range = range_per_curve[curve].drop_back(1);
1413 /* Shift the range by `curve` to account for the second padding vertices.
1414 * The first one is already accounted for during counting (as primitive restart). */
1415 const IndexRange index_range = offset_range.shift(curve + 1);
1416 if (is_onion_per_curve[curve]) {
1417 for (const int i : offset_range.index_range()) {
1418 indices[offset_range[i]] = gpu::RESTART_INDEX;
1419 }
1420 if (cyclic_per_curve[curve]) {
1421 indices[offset_range.last()] = gpu::RESTART_INDEX;
1422 }
1423 }
1424 else {
1425 for (const int i : offset_range.index_range()) {
1426 indices[offset_range[i]] = index_range[i];
1427 }
1428 if (cyclic_per_curve[curve]) {
1429 indices[offset_range.last()] = index_range.first();
1430 }
1431 }
1432 indices[offset_range.one_after_last()] = gpu::RESTART_INDEX;
1433 }
1434 });
1435
1436 gpu::IndexBuf *ibo = GPU_indexbuf_build_ex(&elb, 0, max_index, true);
1437
1438 cache->lines_batch = GPU_batch_create_ex(
1439 GPU_PRIM_LINE_STRIP, cache->vbo, ibo, GPU_BATCH_OWNS_INDEX);
1440
1441 cache->is_dirty = false;
1442}
1443
1445
1447{
1448 BLI_assert(grease_pencil->runtime != nullptr);
1449 GreasePencilBatchCache *cache = static_cast<GreasePencilBatchCache *>(
1450 grease_pencil->runtime->batch_cache);
1451 if (cache == nullptr) {
1452 return;
1453 }
1454 switch (mode) {
1456 cache->is_dirty = true;
1457 break;
1458 default:
1460 }
1461}
1462
1464{
1465 BLI_assert(grease_pencil->runtime != nullptr);
1466 if (!grease_pencil_batch_cache_valid(*grease_pencil)) {
1467 grease_pencil_batch_cache_clear(*grease_pencil);
1468 grease_pencil_batch_cache_init(*grease_pencil);
1469 }
1470}
1471
1473{
1474 grease_pencil_batch_cache_clear(*grease_pencil);
1475 MEM_delete(static_cast<GreasePencilBatchCache *>(grease_pencil->runtime->batch_cache));
1476 grease_pencil->runtime->batch_cache = nullptr;
1477}
1478
1479gpu::Batch *DRW_cache_grease_pencil_get(const Scene *scene, Object *ob)
1480{
1483 grease_pencil_geom_batch_ensure(*ob, grease_pencil, *scene);
1484
1485 return cache->geom_batch;
1486}
1487
1489{
1492 grease_pencil_edit_batch_ensure(*ob, grease_pencil, *scene);
1493
1494 /* Can be `nullptr` when there's no grease pencil drawing visible. */
1495 return cache->edit_points;
1496}
1497
1499{
1502 grease_pencil_edit_batch_ensure(*ob, grease_pencil, *scene);
1503
1504 /* Can be `nullptr` when there's no grease pencil drawing visible. */
1505 return cache->edit_lines;
1506}
1507
1509{
1512 grease_pencil_geom_batch_ensure(*ob, grease_pencil, *scene);
1513
1514 return cache->vbo;
1515}
1516
1518{
1521 grease_pencil_geom_batch_ensure(*ob, grease_pencil, *scene);
1522
1523 return cache->vbo_col;
1524}
1525
1527{
1530 grease_pencil_weight_batch_ensure(*ob, grease_pencil, *scene);
1531
1532 /* Can be `nullptr` when there's no grease pencil drawing visible. */
1533 return cache->edit_points;
1534}
1535
1537{
1540 grease_pencil_weight_batch_ensure(*ob, grease_pencil, *scene);
1541
1542 /* Can be `nullptr` when there's no grease pencil drawing visible. */
1543 return cache->edit_lines;
1544}
1545
1547{
1550 grease_pencil_wire_batch_ensure(*ob, grease_pencil, *scene);
1551
1552 return cache->lines_batch;
1553}
1554
1555} // namespace blender::draw
Low-level operations for curves.
Low-level operations for grease pencil that cannot be defined in the C++ header yet.
@ BKE_GREASEPENCIL_BATCH_DIRTY_ALL
Low-level operations for grease pencil.
#define BLI_assert_unreachable()
Definition BLI_assert.h:93
#define BLI_assert(a)
Definition BLI_assert.h:46
#define BLI_INLINE
void * BLI_findlink(const ListBase *listbase, int number) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition listbase.cc:534
MINLINE void copy_v4_v4(float r[4], const float a[4])
MINLINE void copy_v2_v2(float r[2], const float a[2])
MINLINE void copy_v3_v3(float r[3], const float a[3])
@ CURVE_TYPE_NURBS
@ CURVE_TYPE_POLY
@ GP_STROKE_CAP_TYPE_ROUND
blender::gpu::Batch * GPU_batch_create_ex(GPUPrimType primitive_type, blender::gpu::VertBuf *vertex_buf, blender::gpu::IndexBuf *index_buf, eGPUBatchFlag owns_flag)
Definition gpu_batch.cc:51
#define GPU_batch_create(primitive_type, vertex_buf, index_buf)
Definition GPU_batch.hh:148
int GPU_batch_vertbuf_add(blender::gpu::Batch *batch, blender::gpu::VertBuf *vertex_buf, bool own_vbo)
#define GPU_BATCH_DISCARD_SAFE(batch)
Definition GPU_batch.hh:204
@ GPU_BATCH_OWNS_INDEX
Definition GPU_batch.hh:50
blender::MutableSpan< uint32_t > GPU_indexbuf_get_data(GPUIndexBufBuilder *)
#define GPU_INDEXBUF_DISCARD_SAFE(elem)
void GPU_indexbuf_init(GPUIndexBufBuilder *, GPUPrimType, uint prim_len, uint vertex_len)
blender::gpu::IndexBuf * GPU_indexbuf_build_ex(GPUIndexBufBuilder *builder, uint index_min, uint index_max, bool uses_restart_indices)
blender::gpu::IndexBuf * GPU_indexbuf_build(GPUIndexBufBuilder *)
void GPU_indexbuf_init_ex(GPUIndexBufBuilder *, GPUPrimType, uint index_len, uint vertex_len)
void GPU_indexbuf_add_tri_verts(GPUIndexBufBuilder *, uint v1, uint v2, uint v3)
@ GPU_PRIM_POINTS
@ GPU_PRIM_LINE_STRIP
@ GPU_PRIM_TRIS
void GPU_vertbuf_use(blender::gpu::VertBuf *)
blender::gpu::VertBuf * GPU_vertbuf_create_with_format_ex(const GPUVertFormat &format, GPUUsageType usage)
#define GPU_VERTBUF_DISCARD_SAFE(verts)
void GPU_vertbuf_data_alloc(blender::gpu::VertBuf &verts, uint v_len)
uint GPU_vertbuf_get_vertex_len(const blender::gpu::VertBuf *verts)
@ GPU_USAGE_STATIC
@ GPU_USAGE_FLAG_BUFFER_TEXTURE_ONLY
@ GPU_FETCH_FLOAT
@ GPU_FETCH_INT
uint GPU_vertformat_attr_add(GPUVertFormat *, blender::StringRef name, GPUVertCompType, uint comp_len, GPUVertFetchMode)
GPUVertFormat GPU_vertformat_from_attribute(blender::StringRef name, const GPUVertCompType comp_type, const uint comp_len, const GPUVertFetchMode fetch_mode)
@ GPU_COMP_F32
@ GPU_COMP_I32
@ GPU_COMP_U32
SIMD_FORCE_INLINE btVector3 transform(const btVector3 &point) const
long long int int64_t
static IndexMask from_predicate(const IndexMask &universe, GrainSize grain_size, IndexMaskMemory &memory, Fn &&predicate)
constexpr int64_t first() const
constexpr int64_t last(const int64_t n=0) const
constexpr int64_t size() const
constexpr IndexRange index_range() const
constexpr MutableSpan slice(const int64_t start, const int64_t size) const
Definition BLI_span.hh:573
constexpr Span< T > as_span() const
Definition BLI_span.hh:661
constexpr T & first() const
Definition BLI_span.hh:679
constexpr T & last(const int64_t n=0) const
Definition BLI_span.hh:689
constexpr Span slice(int64_t start, int64_t size) const
Definition BLI_span.hh:137
void materialize(MutableSpan< T > r_span) const
static VArray ForContainer(ContainerT container)
void append(const T &value)
IndexRange index_range() const
GAttributeReader lookup_or_default(StringRef attribute_id, AttrDomain domain, eCustomDataType data_type, const void *default_value=nullptr) const
Span< float4x2 > texture_matrices() const
const bke::CurvesGeometry & strokes() const
VArray< ColorGeometry4f > fill_colors() const
VArray< float > opacities() const
float4x4 to_object_space(const Object &object) const
MutableSpan< T > data()
bool contains(int64_t query_index) const
void foreach_index(Fn &&fn) const
#define cosf(x)
#define GREASE_PENCIL_EDIT_STROKE_START
#define GREASE_PENCIL_EDIT_STROKE_END
Mesh & DRW_object_get_data_for_drawing(const Object &object)
static bool is_cyclic(const Nurb *nu)
#define rot(x, k)
static ushort indices[]
static float verts[][3]
#define GPENCIL_MATERIAL_BUFFER_LEN
#define GP_IS_STROKE_VERTEX_BIT
#define GP_VERTEX_ID_SHIFT
uint pos
#define packed
#define input
#define out
format
static char ** types
Definition makesdna.cc:71
MINLINE unsigned char unit_float_to_uchar_clamp(float val)
void copy(const GVArray &src, GMutableSpan dst, int64_t grain_size=4096)
void gather(const GVArray &src, const IndexMask &indices, GMutableSpan dst, int64_t grain_size=4096)
int64_t count_booleans(const VArray< bool > &varray)
static bool grease_pencil_batch_cache_valid(const GreasePencil &grease_pencil)
static void index_buf_add_points(Object &object, const bke::greasepencil::Drawing &drawing, int layer_index, IndexMaskMemory &memory, MutableSpan< uint > points_data, int *r_drawing_point_index, int *r_drawing_start_offset)
static void grease_pencil_cache_add_nurbs(Object &object, const bke::greasepencil::Drawing &drawing, const int layer_index, IndexMaskMemory &memory, const VArray< float > &selected_point, const float4x4 &layer_space_to_object_space, MutableSpan< float3 > edit_line_points, MutableSpan< float > edit_line_selection, int *r_drawing_line_start_offset, int *r_total_line_ids_num)
gpu::Batch * DRW_cache_grease_pencil_get(const Scene *scene, Object *ob)
void DRW_grease_pencil_batch_cache_validate(GreasePencil *grease_pencil)
static const GPUVertFormat * grease_pencil_stroke_format()
static void index_buf_add_bezier_lines(Object &object, const bke::greasepencil::Drawing &drawing, int layer_index, IndexMaskMemory &memory, MutableSpan< uint > lines_data, int *r_drawing_line_index, int *r_drawing_line_start_offset)
static void grease_pencil_batch_cache_clear(GreasePencil &grease_pencil)
static void index_buf_add_nurbs_lines(Object &object, const bke::greasepencil::Drawing &drawing, int layer_index, IndexMaskMemory &memory, MutableSpan< uint > lines_data, int *r_drawing_line_index, int *r_drawing_line_start_offset)
static IndexMask grease_pencil_get_visible_nurbs_points(Object &object, const bke::greasepencil::Drawing &drawing, int layer_index, IndexMaskMemory &memory)
static IndexMask grease_pencil_get_visible_non_nurbs_curves(Object &object, const bke::greasepencil::Drawing &drawing, const int layer_index, IndexMaskMemory &memory)
void DRW_grease_pencil_batch_cache_free(GreasePencil *grease_pencil)
gpu::Batch * DRW_cache_grease_pencil_face_wireframe_get(const Scene *scene, Object *ob)
static GreasePencilBatchCache * grease_pencil_batch_cache_init(GreasePencil &grease_pencil)
static void grease_pencil_edit_batch_ensure(Object &object, const GreasePencil &grease_pencil, const Scene &scene)
void DRW_grease_pencil_batch_cache_dirty_tag(GreasePencil *grease_pencil, int mode)
static void grease_pencil_weight_batch_ensure(Object &object, const GreasePencil &grease_pencil, const Scene &scene)
static VArray< T > attribute_interpolate(const VArray< T > &input, const bke::CurvesGeometry &curves)
static GreasePencilBatchCache * grease_pencil_batch_cache_get(GreasePencil &grease_pencil)
gpu::VertBuf * DRW_cache_grease_pencil_position_buffer_get(const Scene *scene, Object *ob)
gpu::Batch * DRW_cache_grease_pencil_edit_lines_get(const Scene *scene, Object *ob)
static IndexMask grease_pencil_get_visible_nurbs_curves(Object &object, const bke::greasepencil::Drawing &drawing, int layer_index, IndexMaskMemory &memory)
static const GPUVertFormat * grease_pencil_color_format()
static void index_buf_add_bezier_line_points(Object &object, const bke::greasepencil::Drawing &drawing, int layer_index, IndexMaskMemory &memory, MutableSpan< uint > points_data, int *r_drawing_point_index, int *r_drawing_start_offset)
gpu::Batch * DRW_cache_grease_pencil_weight_lines_get(const Scene *scene, Object *ob)
static bool grease_pencil_batch_cache_is_edit_discarded(GreasePencilBatchCache *cache)
gpu::Batch * DRW_cache_grease_pencil_edit_points_get(const Scene *scene, Object *ob)
gpu::Batch * DRW_cache_grease_pencil_weight_points_get(const Scene *scene, Object *ob)
static void grease_pencil_wire_batch_ensure(Object &object, const GreasePencil &grease_pencil, const Scene &scene)
static void copy_transformed_positions(const Span< float3 > src_positions, const IndexRange range, const float4x4 &transform, MutableSpan< float3 > dst_positions)
static void index_buf_add_line_points(Object &object, const bke::greasepencil::Drawing &drawing, const int layer_index, IndexMaskMemory &memory, MutableSpan< uint > lines_data, int *r_drawing_line_index, int *r_drawing_line_start_offset)
static void grease_pencil_geom_batch_ensure(Object &object, const GreasePencil &grease_pencil, const Scene &scene)
BLI_INLINE int32_t pack_rotation_aspect_hardness(float rot, float asp, float softness)
gpu::VertBuf * DRW_cache_grease_pencil_color_buffer_get(const Scene *scene, Object *ob)
IndexMask retrieve_editable_and_selected_strokes(Object &object, const bke::greasepencil::Drawing &drawing, int layer_index, IndexMaskMemory &memory)
IndexMask retrieve_visible_bezier_handle_points(Object &object, const bke::greasepencil::Drawing &drawing, const int layer_index, IndexMaskMemory &memory)
Vector< DrawingInfo > retrieve_visible_drawings(const Scene &scene, const GreasePencil &grease_pencil, const bool do_onion_skinning)
IndexMask retrieve_editable_and_selected_points(Object &object, const bke::greasepencil::Drawing &drawing, int layer_index, IndexMaskMemory &memory)
IndexMask retrieve_visible_strokes(Object &object, const bke::greasepencil::Drawing &drawing, IndexMaskMemory &memory)
IndexMask retrieve_editable_strokes(Object &object, const bke::greasepencil::Drawing &drawing, int layer_index, IndexMaskMemory &memory)
constexpr uint32_t RESTART_INDEX
void masked_fill(MutableSpan< T > data, const T &value, const IndexMask &mask)
CartesianBasis invert(const CartesianBasis &basis)
T max(const T &a, const T &b)
VecBase< T, 3 > transform_point(const CartesianBasis &basis, const VecBase< T, 3 > &v)
int sum_group_sizes(OffsetIndices< int > offsets, const IndexMask &mask)
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:93
MatBase< float, 4, 4 > float4x4
VecBase< float, 4 > float4
VecBase< int32_t, 3 > int3
MatBase< float, 4, 2 > float4x2
ColorSceneLinear4f< eAlpha::Premultiplied > ColorGeometry4f
Definition BLI_color.hh:342
VecBase< float, 3 > float3
GreasePencilRuntimeHandle * runtime
const bke::greasepencil::Drawing & drawing
i
Definition text_draw.cc:230