Initial Grease Pencil 3.0 stage #106848

Merged
Falk David merged 224 commits from filedescriptor/blender:grease-pencil-v3 into main 2023-05-30 11:14:22 +02:00
6 changed files with 351 additions and 13 deletions
Showing only changes of commit 7136099ae5 - Show all commits

View File

@ -442,6 +442,9 @@ class GreasePencilRuntime {
{
return root_group_;
}
public:
void *batch_cache = nullptr;
};
} // namespace blender::bke

View File

@ -73,6 +73,7 @@ set(SRC
intern/draw_cache_impl_curve.cc
intern/draw_cache_impl_curves.cc
intern/draw_cache_impl_gpencil_legacy.cc
intern/draw_cache_impl_grease_pencil.cc
intern/draw_cache_impl_lattice.c
intern/draw_cache_impl_mesh.cc
intern/draw_cache_impl_particles.c

View File

@ -138,11 +138,9 @@ class Instance {
void object_sync(Manager &manager, ObjectRef &object_ref)
{
switch (object_ref.object->type) {
// case OB_GPENCIL_LEGACY:
// objects.sync_gpencil(manager, object_ref, main_fb_, main_ps_);
// break;
case OB_GREASE_PENCIL:
// objects.sync_gpencil(manager, object_ref, main_fb_, main_ps_);
objects.sync_grease_pencil(manager, object_ref, main_fb_, main_ps_);
break;
case OB_LAMP:
lights.sync(object_ref);
break;

View File

@ -112,10 +112,10 @@ class ObjectModule {
// objects_buf_.shrink();
}
void sync_gpencil(Manager &manager,
ObjectRef &object_ref,
Framebuffer &main_fb,
PassSortable &main_ps)
void sync_grease_pencil(Manager &manager,
ObjectRef &object_ref,
Framebuffer &main_fb,
PassSortable &main_ps)
{
using namespace blender::bke::gpencil;
@ -126,7 +126,7 @@ class ObjectModule {
return;
}
const bool is_stroke_order_3d = true; /* TODO */
const bool is_stroke_order_3d = false; /* TODO */
bool do_material_holdout = false;
bool do_layer_blending = false;
bool object_has_vfx = false; // TODO vfx.object_has_vfx(gpd);
@ -171,9 +171,10 @@ class ObjectModule {
ob.layer_offset = layer_offset;
ob.material_offset = material_offset;
GPUVertBuf *position_tx = DRW_cache_gpencil_position_buffer_get(object, current_frame_);
GPUVertBuf *color_tx = DRW_cache_gpencil_color_buffer_get(object, current_frame_);
GPUBatch *geom = DRW_cache_gpencil_get(object, current_frame_);
GPUVertBuf *position_tx = DRW_cache_grease_pencil_position_buffer_get(object,
current_frame_);
GPUVertBuf *color_tx = DRW_cache_grease_pencil_color_buffer_get(object, current_frame_);
GPUBatch *geom = DRW_cache_grease_pencil_get(object, current_frame_);
if (do_layer_blending) {
// for (const LayerData &layer : frame.layers) {

View File

@ -253,7 +253,7 @@ DRWVolumeGrid *DRW_volume_batch_cache_get_grid(struct Volume *volume,
struct GPUBatch *DRW_cache_volume_face_wireframe_get(struct Object *ob);
struct GPUBatch *DRW_cache_volume_selection_surface_get(struct Object *ob);
/* GPencil */
/* GPencil (legacy) */
struct GPUBatch *DRW_cache_gpencil_get(struct Object *ob, int cfra);
Review

Can those be renamed to DRW_cache_gpencil_legacy_* ?

Can those be renamed to `DRW_cache_gpencil_legacy_*` ?
struct GPUVertBuf *DRW_cache_gpencil_position_buffer_get(struct Object *ob, int cfra);
@ -276,6 +276,12 @@ struct bGPDstroke *DRW_cache_gpencil_sbuffer_stroke_data_get(struct Object *ob);
*/
void DRW_cache_gpencil_sbuffer_clear(struct Object *ob);
/* Grease Pencil */
struct GPUBatch *DRW_cache_grease_pencil_get(struct Object *ob, int cfra);
struct GPUVertBuf *DRW_cache_grease_pencil_position_buffer_get(struct Object *ob, int cfra);
struct GPUVertBuf *DRW_cache_grease_pencil_color_buffer_get(struct Object *ob, int cfra);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,329 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup draw
*
* \brief Grease Pencil API for render engines
*/
#include "BKE_curves.hh"
#include "BKE_grease_pencil.hh"
#include "BLI_task.hh"
#include "DNA_grease_pencil_types.h"
#include "DRW_engine.h"
#include "DRW_render.h"
#include "GPU_batch.h"
#include "../engines/gpencil/gpencil_defines.h"
namespace blender::draw {
struct GreasePencilBatchCache {
/** Instancing Data */
GPUVertBuf *vbo;
GPUVertBuf *vbo_col;
/** Indices in material order, then stroke order with fill first.
* Strokes can be individually rendered using `gps->runtime.stroke_start` and
* `gps->runtime.fill_start`. */
GPUIndexBuf *ibo;
/** Batches */
GPUBatch *geom_batch;
filedescriptor marked this conversation as resolved Outdated

Update this comment. Out of date.

Update this comment. Out of date.
/** Stroke lines only */
GPUBatch *lines_batch;
/** Cache is dirty. */
bool is_dirty;
/** Last cached frame. */
int cache_frame;
filedescriptor marked this conversation as resolved
Review

Remove while unused?

Remove while unused?
};
/* -------------------------------------------------------------------- */
/** \name Vertex Formats
* \{ */
/* MUST match the format below. */
struct GreasePencilStrokeVert {
/** Position and radius packed in the same attribute. */
float pos[3], radius;
/** Material Index, Stroke Index, Point Index, Packed aspect + hardness + rotation. */
int32_t mat, stroke_id, point_id, packed_asp_hard_rot;
/** UV and opacity packed in the same attribute. */
float uv_fill[2], u_stroke, opacity;
};
static GPUVertFormat *grease_pencil_stroke_format()
{
static GPUVertFormat format = {0};
if (format.attr_len == 0) {
GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
GPU_vertformat_attr_add(&format, "ma", GPU_COMP_I32, 4, GPU_FETCH_INT);
GPU_vertformat_attr_add(&format, "uv", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
}
return &format;
}
/* MUST match the format below. */
struct GreasePencilColorVert {
float vcol[4]; /* Vertex color */
float fcol[4]; /* Fill color */
};
static GPUVertFormat *grease_pencil_color_format()
{
static GPUVertFormat format = {0};
if (format.attr_len == 0) {
GPU_vertformat_attr_add(&format, "col", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
GPU_vertformat_attr_add(&format, "fcol", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
}
return &format;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Internal Utilities
* \{ */
static bool grease_pencil_batch_cache_valid(const GreasePencil &grease_pencil)
{
BLI_assert(grease_pencil.runtime != nullptr);
const GreasePencilBatchCache *cache = static_cast<GreasePencilBatchCache *>(
grease_pencil.runtime->batch_cache);
return (cache && cache->is_dirty == false);
}
static GreasePencilBatchCache *grease_pencil_batch_cache_init(GreasePencil &grease_pencil,
int cfra)
{
BLI_assert(grease_pencil.runtime != nullptr);
GreasePencilBatchCache *cache = static_cast<GreasePencilBatchCache *>(
grease_pencil.runtime->batch_cache);
if (!cache) {
cache = MEM_new<GreasePencilBatchCache>(__func__);
grease_pencil.runtime->batch_cache = cache;
}
else {
grease_pencil.runtime->batch_cache = {};
}
cache->is_dirty = false;
cache->cache_frame = cfra;
return cache;
}
static void grese_pencil_batch_cache_clear(GreasePencil &grease_pencil)
{
BLI_assert(grease_pencil.runtime != nullptr);
GreasePencilBatchCache *cache = static_cast<GreasePencilBatchCache *>(
grease_pencil.runtime->batch_cache);
if (cache == nullptr) {
return;
}
GPU_BATCH_DISCARD_SAFE(cache->lines_batch);
GPU_BATCH_DISCARD_SAFE(cache->geom_batch);
GPU_VERTBUF_DISCARD_SAFE(cache->vbo);
GPU_VERTBUF_DISCARD_SAFE(cache->vbo_col);
GPU_INDEXBUF_DISCARD_SAFE(cache->ibo);
cache->is_dirty = true;
}
static GreasePencilBatchCache *grease_pencil_batch_cache_get(GreasePencil &grease_pencil, int cfra)
{
BLI_assert(grease_pencil.runtime != nullptr);
GreasePencilBatchCache *cache = static_cast<GreasePencilBatchCache *>(
grease_pencil.runtime->batch_cache);
if (!grease_pencil_batch_cache_valid(grease_pencil)) {
grese_pencil_batch_cache_clear(grease_pencil);
return grease_pencil_batch_cache_init(grease_pencil, cfra);
}
return cache;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Vertex Buffers
* \{ */
static int count_cyclic_curves(bke::CurvesGeometry &curves)
{
VArray<bool> cyclic = curves.cyclic();
const CommonVArrayInfo info = cyclic.common_info();
if (info.type == CommonVArrayInfo::Type::Single) {
const bool single = *static_cast<const bool *>(info.data);
return single ? curves.curves_num() : 0;
}
else if (info.type == CommonVArrayInfo::Type::Span) {
const Span<bool> span(static_cast<const bool *>(info.data), cyclic.size());
return span.count(true);
}
return threading::parallel_reduce(
cyclic.index_range(),
2048,
0,
[&](const IndexRange range, const int init) {
/* Alternatively, this could use #materialize to retrieve many values at once. */
int sum = init;
for (const int64_t i : range) {
if (cyclic[i]) {
sum++;
}
}
return sum;
},
[&](const int a, const int b) { return a + b; });
}
static void grease_pencil_batches_ensure(GreasePencil &grease_pencil, int cfra)
{
BLI_assert(grease_pencil.runtime != nullptr);
GreasePencilBatchCache *cache = static_cast<GreasePencilBatchCache *>(
grease_pencil.runtime->batch_cache);
if (cache->vbo != nullptr) {
return;
}
/* Should be discarded together. */
BLI_assert(cache->vbo == nullptr && cache->ibo == nullptr);
BLI_assert(cache->geom_batch == nullptr);

Looks like this can be Vector<Array<int>>, which is better since they don't need to be resized anyway

Looks like this can be `Vector<Array<int>>`, which is better since they don't need to be resized anyway
/* First count how many vertices and triangles are needed for the whole object. */
int total_points_num = 0;

What about building a vector of the drawings once, to avoid putting most of the code inside the lambda here and the need to call "foreach_visible_drawing" twice:

  Vector<const GreasePencilDrawing *> drawings;
  grease_pencil.foreach_visible_drawing(
      cfra, [&](GreasePencilDrawing &drawing) { drawings.append(&drawing); });

...

  Array<Array<int>> verts_start_offsets_per_visible_drawing;
  Array<Array<int>> tris_start_offsets_per_visible_drawing;

  for (const int drawing_i : drawings.index_range()) {
    const GreasePencilDrawing &drawing = *drawings[drawing_i];

...

  /* Fill buffers with data. */
  for (const int drawing_i : drawings.index_range()) {
    const GreasePencilDrawing &drawing = *drawings[drawing_i];

What about building a vector of the drawings once, to avoid putting most of the code inside the lambda here and the need to call "foreach_visible_drawing" twice: ``` Vector<const GreasePencilDrawing *> drawings; grease_pencil.foreach_visible_drawing( cfra, [&](GreasePencilDrawing &drawing) { drawings.append(&drawing); }); ... Array<Array<int>> verts_start_offsets_per_visible_drawing; Array<Array<int>> tris_start_offsets_per_visible_drawing; for (const int drawing_i : drawings.index_range()) { const GreasePencilDrawing &drawing = *drawings[drawing_i]; ... /* Fill buffers with data. */ for (const int drawing_i : drawings.index_range()) { const GreasePencilDrawing &drawing = *drawings[drawing_i]; ```
int total_triangles_num = 0; // TODO
grease_pencil.foreach_visible_drawing(
cfra, [&](GreasePencilDrawing &drawing, blender::bke::gpencil::Layer &layer) {
bke::CurvesGeometry &curves = drawing.geometry.wrap();
int num_cyclic = count_cyclic_curves(curves);
/* One vertex is stored before and after as padding. Cyclic strokes have one extra
* vertex.*/
total_points_num += curves.points_num() + num_cyclic + curves.curves_num() * 2;

More specific/helpful variable names than t and v would be nice here

More specific/helpful variable names than `t` and `v` would be nice here
});
GPUUsageType vbo_flag = GPU_USAGE_STATIC | GPU_USAGE_FLAG_BUFFER_TEXTURE_ONLY;
/* Create VBOs. */
GPUVertFormat *format = grease_pencil_stroke_format();
GPUVertFormat *format_col = grease_pencil_color_format();
cache->vbo = GPU_vertbuf_create_with_format_ex(format, vbo_flag);
cache->vbo_col = GPU_vertbuf_create_with_format_ex(format_col, vbo_flag);
/* Add extra space at the end of the buffer because of quad load. */
GPU_vertbuf_data_alloc(cache->vbo, total_points_num + 2);
GPU_vertbuf_data_alloc(cache->vbo_col, total_points_num + 2);
GPUIndexBufBuilder ibo;
MutableSpan<GreasePencilStrokeVert> verts = {
static_cast<GreasePencilStrokeVert *>(GPU_vertbuf_get_data(cache->vbo)),
GPU_vertbuf_get_vertex_len(cache->vbo)};
Review

Not clear what's going on here-- 1 + ... + 1?

Not clear what's going on here-- `1 + ... + 1`?
Review

Looks like my comment about this got lost somehow. There is one extra vertex before and after each stroke. It's written in this way to make it more clear where that padding is added.

Looks like my comment about this got lost somehow. There is one extra vertex before and after each stroke. It's written in this way to make it more clear where that padding is added.
MutableSpan<GreasePencilColorVert> cols = {
static_cast<GreasePencilColorVert *>(GPU_vertbuf_get_data(cache->vbo_col)),
GPU_vertbuf_get_vertex_len(cache->vbo_col)};
/* Create IBO. */
GPU_indexbuf_init(&ibo, GPU_PRIM_TRIS, total_triangles_num, 0xFFFFFFFFu);
/* Fill buffers with data. */
int v = 0;
grease_pencil.foreach_visible_drawing(
cfra, [&](GreasePencilDrawing &drawing, blender::bke::gpencil::Layer &layer) {
bke::CurvesGeometry &curves = drawing.geometry.wrap();
bke::AttributeAccessor attributes = curves.attributes();
offset_indices::OffsetIndices<int> points_by_curve = curves.points_by_curve();
const Span<float3> positions = curves.positions();
const VArray<bool> cyclic = curves.cyclic();
VArray<float> radii = attributes.lookup_or_default<float>(
"radius", ATTR_DOMAIN_POINT, 1.0f);
VArray<float> opacities = attributes.lookup_or_default<float>(
"opacity", ATTR_DOMAIN_POINT, 1.0f);
VArray<int> materials = attributes.lookup_or_default<int>(
"material_index", ATTR_DOMAIN_CURVE, -1);
for (const int curve_i : curves.curves_range()) {
IndexRange points = points_by_curve[curve_i];
const bool is_cyclic = cyclic[curve_i];
const int v_start = v;
/* First point is not drawn. */
v++;
for (const int point_i : points) {
copy_v3_v3(verts[v].pos, positions[point_i]);
verts[v].radius = radii[point_i];
verts[v].opacity = opacities[point_i];
verts[v].point_id = v;
verts[v].stroke_id = v_start;
verts[v].mat = materials[curve_i] % GPENCIL_MATERIAL_BUFFER_LEN;
v++;
}
if (is_cyclic) {
copy_v3_v3(verts[v].pos, positions[points.first()]);
verts[v].radius = radii[points.first()];
verts[v].opacity = opacities[points.first()];
verts[v].point_id = v;
verts[v].stroke_id = v_start;
verts[v].mat = materials[curve_i] % GPENCIL_MATERIAL_BUFFER_LEN;
v++;
}

Use the * overload rather than adding .varray at the end

Use the `*` overload rather than adding `.varray` at the end
/* Last point is not drawn. */
v++;
}
});
/* Mark last 2 verts as invalid. */
verts[total_points_num + 0].mat = -1;
verts[total_points_num + 1].mat = -1;
/* Also mark first vert as invalid. */
verts[0].mat = -1;
/* Finish the IBO. */

These .as_span() calls shouldn't be necessary

These `.as_span()` calls shouldn't be necessary
cache->ibo = GPU_indexbuf_build(&ibo);
/* Create the batches */
cache->geom_batch = GPU_batch_create(GPU_PRIM_TRIS, cache->vbo, cache->ibo);
/* Allow creation of buffer texture. */
GPU_vertbuf_use(cache->vbo);
GPU_vertbuf_use(cache->vbo_col);
cache->is_dirty = false;
}
/** \} */
} // namespace blender::draw
GPUBatch *DRW_cache_grease_pencil_get(Object *ob, int cfra)
{
using namespace blender::draw;
GreasePencil &grease_pencil = *static_cast<GreasePencil *>(ob->data);
GreasePencilBatchCache *cache = grease_pencil_batch_cache_get(grease_pencil, cfra);
grease_pencil_batches_ensure(grease_pencil, cfra);

Add details about TODO. Something that helps others to pick up a task, or to understand that specific case is not expected to be yet working.

Add details about TODO. Something that helps others to pick up a task, or to understand that specific case is not expected to be yet working.
return cache->geom_batch;
}
GPUVertBuf *DRW_cache_grease_pencil_position_buffer_get(Object *ob, int cfra)
{
using namespace blender::draw;
GreasePencil &grease_pencil = *static_cast<GreasePencil *>(ob->data);
GreasePencilBatchCache *cache = grease_pencil_batch_cache_get(grease_pencil, cfra);
grease_pencil_batches_ensure(grease_pencil, cfra);
return cache->vbo;
}
GPUVertBuf *DRW_cache_grease_pencil_color_buffer_get(Object *ob, int cfra)
{
using namespace blender::draw;
GreasePencil &grease_pencil = *static_cast<GreasePencil *>(ob->data);
GreasePencilBatchCache *cache = grease_pencil_batch_cache_get(grease_pencil, cfra);
grease_pencil_batches_ensure(grease_pencil, cfra);
return cache->vbo_col;
}