Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAntonio Vazquez <blendergit@gmail.com>2020-03-09 18:27:24 +0300
committerAntonio Vazquez <blendergit@gmail.com>2020-03-09 18:27:24 +0300
commit29f3af95272590d26f610ae828b2eeee89c82a00 (patch)
treea696a58a2561c48f7ec6166e369e22081e0a64d8 /source/blender/draw/intern
parentdcb93126876879d969a30a7865700abd072066f8 (diff)
GPencil: Refactor of Draw Engine, Vertex Paint and all internal functions
This commit is a full refactor of the grease pencil modules including Draw Engine, Modifiers, VFX, depsgraph update, improvements in operators and conversion of Sculpt and Weight paint tools to real brushes. Also, a huge code cleanup has been done at all levels. Thanks to @fclem for his work and yo @pepeland and @mendio for the testing and help in the development. Differential Revision: https://developer.blender.org/D6293
Diffstat (limited to 'source/blender/draw/intern')
-rw-r--r--source/blender/draw/intern/DRW_render.h22
-rw-r--r--source/blender/draw/intern/draw_cache.c27
-rw-r--r--source/blender/draw/intern/draw_cache.h17
-rw-r--r--source/blender/draw/intern/draw_cache_impl_gpencil.c745
-rw-r--r--source/blender/draw/intern/draw_common.c2
-rw-r--r--source/blender/draw/intern/draw_common.h2
-rw-r--r--source/blender/draw/intern/draw_manager.c34
-rw-r--r--source/blender/draw/intern/draw_manager.h18
-rw-r--r--source/blender/draw/intern/draw_manager_data.c65
-rw-r--r--source/blender/draw/intern/draw_manager_exec.c91
-rw-r--r--source/blender/draw/intern/shaders/common_globals_lib.glsl2
-rw-r--r--source/blender/draw/intern/shaders/common_smaa_lib.glsl11
-rw-r--r--source/blender/draw/intern/shaders/common_view_lib.glsl13
13 files changed, 978 insertions, 71 deletions
diff --git a/source/blender/draw/intern/DRW_render.h b/source/blender/draw/intern/DRW_render.h
index fc7ca6c7d67..cc257b80daf 100644
--- a/source/blender/draw/intern/DRW_render.h
+++ b/source/blender/draw/intern/DRW_render.h
@@ -280,8 +280,9 @@ typedef enum {
DRW_STATE_BLEND_BACKGROUND = (1 << 20),
DRW_STATE_BLEND_OIT = (1 << 21),
DRW_STATE_BLEND_MUL = (1 << 22),
+ DRW_STATE_BLEND_SUB = (1 << 23),
/** Use dual source blending. WARNING: Only one color buffer allowed. */
- DRW_STATE_BLEND_CUSTOM = (1 << 23),
+ DRW_STATE_BLEND_CUSTOM = (1 << 24),
DRW_STATE_IN_FRONT_SELECT = (1 << 25),
DRW_STATE_LOGIC_INVERT = (1 << 26),
@@ -362,10 +363,10 @@ void DRW_shgroup_call_ex(DRWShadingGroup *shgroup,
#define DRW_shgroup_call_no_cull(shgrp, geom, ob) \
DRW_shgroup_call_ex(shgrp, ob, NULL, geom, true, NULL)
-void DRW_shgroup_call_range(DRWShadingGroup *shgroup,
- struct GPUBatch *geom,
- uint v_sta,
- uint v_ct);
+void DRW_shgroup_call_range(
+ DRWShadingGroup *shgroup, Object *ob, struct GPUBatch *geom, uint v_sta, uint v_ct);
+void DRW_shgroup_call_instance_range(
+ DRWShadingGroup *shgroup, Object *ob, struct GPUBatch *geom, uint v_sta, uint v_ct);
void DRW_shgroup_call_procedural_points(DRWShadingGroup *sh, Object *ob, uint point_ct);
void DRW_shgroup_call_procedural_lines(DRWShadingGroup *sh, Object *ob, uint line_ct);
@@ -402,6 +403,17 @@ void DRW_buffer_add_entry_array(DRWCallBuffer *buffer, const void *attr[], uint
void DRW_shgroup_state_enable(DRWShadingGroup *shgroup, DRWState state);
void DRW_shgroup_state_disable(DRWShadingGroup *shgroup, DRWState state);
+
+/* Reminders:
+ * - (compare_mask & reference) is what is tested against (compare_mask & stencil_value)
+ * stencil_value being the value stored in the stencil buffer.
+ * - (writemask & reference) is what gets written if the test condition is fullfiled.
+ **/
+void DRW_shgroup_stencil_set(DRWShadingGroup *shgroup,
+ uint write_mask,
+ uint reference,
+ uint comp_mask);
+/* TODO remove this function. Obsolete version. mask is actually reference value. */
void DRW_shgroup_stencil_mask(DRWShadingGroup *shgroup, uint mask);
/* Issue a clear command. */
diff --git a/source/blender/draw/intern/draw_cache.c b/source/blender/draw/intern/draw_cache.c
index f37e5b14d83..d0cea5b8c5c 100644
--- a/source/blender/draw/intern/draw_cache.c
+++ b/source/blender/draw/intern/draw_cache.c
@@ -136,6 +136,7 @@ static struct DRWShapeCache {
GPUBatch *drw_particle_cross;
GPUBatch *drw_particle_circle;
GPUBatch *drw_particle_axis;
+ GPUBatch *drw_gpencil_dummy_quad;
} SHC = {NULL};
void DRW_shape_cache_free(void)
@@ -747,6 +748,29 @@ GPUBatch *DRW_cache_normal_arrow_get(void)
}
/* -------------------------------------------------------------------- */
+/** \name Dummy vbos
+ *
+ * We need a dummy vbo containing the vertex count to draw instances ranges.
+ *
+ * \{ */
+
+GPUBatch *DRW_gpencil_dummy_buffer_get(void)
+{
+ if (SHC.drw_gpencil_dummy_quad == NULL) {
+ GPUVertFormat format = {0};
+ GPU_vertformat_attr_add(&format, "dummy", GPU_COMP_U8, 1, GPU_FETCH_INT);
+ GPUVertBuf *vbo = GPU_vertbuf_create_with_format(&format);
+ GPU_vertbuf_data_alloc(vbo, 4);
+
+ SHC.drw_gpencil_dummy_quad = GPU_batch_create_ex(
+ GPU_PRIM_TRI_FAN, vbo, NULL, GPU_BATCH_OWNS_VBO);
+ }
+ return SHC.drw_gpencil_dummy_quad;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
/** \name Common Object API
* \{ */
@@ -793,6 +817,9 @@ GPUBatch *DRW_cache_object_face_wireframe_get(Object *ob)
return DRW_cache_text_face_wireframe_get(ob);
case OB_MBALL:
return DRW_cache_mball_face_wireframe_get(ob);
+ case OB_GPENCIL: {
+ return DRW_cache_gpencil_face_wireframe_get(ob);
+ }
default:
return NULL;
}
diff --git a/source/blender/draw/intern/draw_cache.h b/source/blender/draw/intern/draw_cache.h
index cb81bdafe62..8ac0d7ada21 100644
--- a/source/blender/draw/intern/draw_cache.h
+++ b/source/blender/draw/intern/draw_cache.h
@@ -29,6 +29,7 @@ struct ModifierData;
struct Object;
struct PTCacheEdit;
struct ParticleSystem;
+struct bGPDstroke;
void DRW_shape_cache_free(void);
void DRW_shape_cache_reset(void);
@@ -46,6 +47,9 @@ struct GPUBatch *DRW_cache_cube_get(void);
struct GPUBatch *DRW_cache_sphere_get(void);
struct GPUBatch *DRW_cache_normal_arrow_get(void);
+/* Dummy VBOs */
+struct GPUBatch *DRW_gpencil_dummy_buffer_get(void);
+
/* Common Object */
struct GPUBatch *DRW_cache_object_all_edges_get(struct Object *ob);
struct GPUBatch *DRW_cache_object_edge_detection_get(struct Object *ob, bool *r_is_manifold);
@@ -196,4 +200,17 @@ struct GPUBatch **DRW_cache_mball_surface_shaded_get(struct Object *ob,
struct GPUBatch *DRW_cache_mball_face_wireframe_get(struct Object *ob);
struct GPUBatch *DRW_cache_mball_edge_detection_get(struct Object *ob, bool *r_is_manifold);
+/* GPencil */
+struct GPUBatch *DRW_cache_gpencil_strokes_get(struct Object *ob, int cfra);
+struct GPUBatch *DRW_cache_gpencil_fills_get(struct Object *ob, int cfra);
+struct GPUBatch *DRW_cache_gpencil_edit_lines_get(struct Object *ob, int cfra);
+struct GPUBatch *DRW_cache_gpencil_edit_points_get(struct Object *ob, int cfra);
+struct GPUBatch *DRW_cache_gpencil_sbuffer_stroke_get(struct Object *ob);
+struct GPUBatch *DRW_cache_gpencil_sbuffer_fill_get(struct Object *ob);
+
+struct GPUBatch *DRW_cache_gpencil_face_wireframe_get(struct Object *ob);
+
+struct bGPDstroke *DRW_cache_gpencil_sbuffer_stroke_data_get(struct Object *ob);
+void DRW_cache_gpencil_sbuffer_clear(struct Object *ob);
+
#endif /* __DRAW_CACHE_H__ */
diff --git a/source/blender/draw/intern/draw_cache_impl_gpencil.c b/source/blender/draw/intern/draw_cache_impl_gpencil.c
new file mode 100644
index 00000000000..c6cedd3f1d2
--- /dev/null
+++ b/source/blender/draw/intern/draw_cache_impl_gpencil.c
@@ -0,0 +1,745 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2020, Blender Foundation
+ * This is a new part of Blender
+ */
+
+/** \file
+ * \ingroup draw
+ */
+
+#include "DNA_gpencil_types.h"
+#include "DNA_meshdata_types.h"
+#include "DNA_screen_types.h"
+
+#include "BKE_deform.h"
+#include "BKE_gpencil.h"
+
+#include "DRW_engine.h"
+#include "DRW_render.h"
+
+#include "GPU_batch.h"
+#include "ED_gpencil.h"
+
+#include "DEG_depsgraph_query.h"
+
+#include "BLI_polyfill_2d.h"
+#include "BLI_hash.h"
+
+#include "draw_cache.h"
+#include "draw_cache_impl.h"
+
+/* ---------------------------------------------------------------------- */
+typedef struct GpencilBatchCache {
+ /** Instancing Data */
+ GPUVertBuf *vbo;
+ GPUVertBuf *vbo_col;
+ /** Fill Topology */
+ GPUIndexBuf *ibo;
+ /** Instancing Batches */
+ GPUBatch *stroke_batch;
+ GPUBatch *fill_batch;
+ GPUBatch *lines_batch;
+
+ /** Edit Mode */
+ GPUVertBuf *edit_vbo;
+ GPUBatch *edit_lines_batch;
+ GPUBatch *edit_points_batch;
+
+ /** Cache is dirty */
+ bool is_dirty;
+ /** Edit mode flag */
+ bool is_editmode;
+ /** Last cache frame */
+ int cache_frame;
+} GpencilBatchCache;
+
+static bool gpencil_batch_cache_valid(GpencilBatchCache *cache, bGPdata *gpd, int cfra)
+{
+ bool valid = true;
+ if (cache == NULL) {
+ return false;
+ }
+
+ cache->is_editmode = GPENCIL_ANY_EDIT_MODE(gpd);
+ if (cfra != cache->cache_frame) {
+ valid = false;
+ }
+ else if (gpd->flag & GP_DATA_CACHE_IS_DIRTY) {
+ valid = false;
+ }
+ else if (gpd->flag & GP_DATA_PYTHON_UPDATED) {
+ gpd->flag &= ~GP_DATA_PYTHON_UPDATED;
+ valid = false;
+ }
+ else if (cache->is_dirty) {
+ valid = false;
+ }
+
+ return valid;
+}
+
+static GpencilBatchCache *gpencil_batch_cache_init(Object *ob, int cfra)
+{
+ bGPdata *gpd = (bGPdata *)ob->data;
+
+ GpencilBatchCache *cache = gpd->runtime.gpencil_cache;
+
+ if (!cache) {
+ cache = gpd->runtime.gpencil_cache = MEM_callocN(sizeof(*cache), __func__);
+ }
+ else {
+ memset(cache, 0, sizeof(*cache));
+ }
+
+ cache->is_editmode = GPENCIL_ANY_EDIT_MODE(gpd);
+ cache->is_dirty = true;
+ cache->cache_frame = cfra;
+ return cache;
+}
+
+static void gpencil_batch_cache_clear(GpencilBatchCache *cache)
+{
+ if (!cache) {
+ return;
+ }
+
+ GPU_BATCH_DISCARD_SAFE(cache->lines_batch);
+ GPU_BATCH_DISCARD_SAFE(cache->fill_batch);
+ GPU_BATCH_DISCARD_SAFE(cache->stroke_batch);
+ GPU_VERTBUF_DISCARD_SAFE(cache->vbo);
+ GPU_VERTBUF_DISCARD_SAFE(cache->vbo_col);
+ GPU_INDEXBUF_DISCARD_SAFE(cache->ibo);
+
+ GPU_BATCH_DISCARD_SAFE(cache->edit_lines_batch);
+ GPU_BATCH_DISCARD_SAFE(cache->edit_points_batch);
+ GPU_VERTBUF_DISCARD_SAFE(cache->edit_vbo);
+
+ cache->is_dirty = true;
+}
+
+static GpencilBatchCache *gpencil_batch_cache_get(Object *ob, int cfra)
+{
+ bGPdata *gpd = (bGPdata *)ob->data;
+
+ GpencilBatchCache *cache = gpd->runtime.gpencil_cache;
+ if (!gpencil_batch_cache_valid(cache, gpd, cfra)) {
+ gpencil_batch_cache_clear(cache);
+ return gpencil_batch_cache_init(ob, cfra);
+ }
+ else {
+ return cache;
+ }
+}
+
+void DRW_gpencil_batch_cache_dirty_tag(bGPdata *gpd)
+{
+ gpd->flag |= GP_DATA_CACHE_IS_DIRTY;
+}
+
+void DRW_gpencil_batch_cache_free(bGPdata *gpd)
+{
+ gpencil_batch_cache_clear(gpd->runtime.gpencil_cache);
+ MEM_SAFE_FREE(gpd->runtime.gpencil_cache);
+ gpd->flag |= GP_DATA_CACHE_IS_DIRTY;
+ return;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Vertex Formats.
+ * \{ */
+
+/* MUST match the format below. */
+typedef struct gpStrokeVert {
+ /** Mat is float because we need to pack other float attribs with it. */
+ float mat, strength, stroke_id, point_id;
+ /** Position and thickness packed in the same attribute. */
+ float pos[3], thickness;
+ float uv_fill[2], u_stroke, v_rot;
+ /** Aspect ratio and hardnes. */
+ float aspect_ratio, hardness;
+} gpStrokeVert;
+
+static GPUVertFormat *gpencil_stroke_format(void)
+{
+ static GPUVertFormat format = {0};
+ if (format.attr_len == 0) {
+ GPU_vertformat_attr_add(&format, "ma", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
+ GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
+ GPU_vertformat_attr_add(&format, "uv", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
+ GPU_vertformat_attr_add(&format, "hard", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
+ /* IMPORTANT: This means having only 4 attributes to fit into GPU module limit of 16 attrib. */
+ GPU_vertformat_multiload_enable(&format, 4);
+ }
+ return &format;
+}
+
+/* MUST match the format below. */
+typedef struct gpEditVert {
+ uint vflag;
+ float weight;
+} gpEditVert;
+
+static GPUVertFormat *gpencil_edit_stroke_format(void)
+{
+ static GPUVertFormat format = {0};
+ if (format.attr_len == 0) {
+ GPU_vertformat_attr_add(&format, "vflag", GPU_COMP_U32, 1, GPU_FETCH_INT);
+ GPU_vertformat_attr_add(&format, "weight", GPU_COMP_F32, 1, GPU_FETCH_FLOAT);
+ }
+ return &format;
+}
+
+/* MUST match the format below. */
+typedef struct gpColorVert {
+ float vcol[4]; /* Vertex color */
+ float fcol[4]; /* Fill color */
+} gpColorVert;
+
+static GPUVertFormat *gpencil_color_format(void)
+{
+ 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);
+ /* IMPORTANT: This means having only 4 attributes to fit into GPU module limit of 16 attrib. */
+ GPU_vertformat_multiload_enable(&format, 4);
+ }
+ return &format;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Vertex Buffers.
+ * \{ */
+
+typedef struct gpIterData {
+ bGPdata *gpd;
+ gpStrokeVert *verts;
+ gpColorVert *cols;
+ GPUIndexBufBuilder ibo;
+ int vert_len;
+ int tri_len;
+} gpIterData;
+
+static GPUVertBuf *gpencil_dummy_buffer_get(void)
+{
+ GPUBatch *batch = DRW_gpencil_dummy_buffer_get();
+ return batch->verts[0];
+}
+
+static int gpencil_stroke_is_cyclic(const bGPDstroke *gps)
+{
+ return ((gps->flag & GP_STROKE_CYCLIC) != 0) && (gps->totpoints > 2);
+}
+
+static void gpencil_buffer_add_point(gpStrokeVert *verts,
+ gpColorVert *cols,
+ const bGPDstroke *gps,
+ const bGPDspoint *pt,
+ int v,
+ bool is_endpoint)
+{
+ /* Note: we use the sign of stength and thickness to pass cap flag. */
+ const bool round_cap0 = (gps->caps[0] == GP_STROKE_CAP_ROUND);
+ const bool round_cap1 = (gps->caps[1] == GP_STROKE_CAP_ROUND);
+ gpStrokeVert *vert = &verts[v];
+ gpColorVert *col = &cols[v];
+ copy_v3_v3(vert->pos, &pt->x);
+ copy_v2_v2(vert->uv_fill, pt->uv_fill);
+ copy_v4_v4(col->vcol, pt->vert_color);
+ copy_v4_v4(col->fcol, gps->vert_color_fill);
+
+ /* Encode fill opacity defined by opacity modifier in vertex color alpha. If
+ * no opacity modifier, the value will be always 1.0f. The opacity factor can be any
+ * value between 0.0f and 2.0f */
+ col->fcol[3] = (((int)(col->fcol[3] * 10000.0f)) * 10.0f) + gps->fill_opacity_fac;
+
+ vert->strength = (round_cap0) ? pt->strength : -pt->strength;
+ vert->u_stroke = pt->uv_fac;
+ vert->stroke_id = gps->runtime.stroke_start;
+ vert->point_id = v;
+ /* Rotation are in [-90°..90°] range, so we can encode the sign of the angle + the cosine
+ * because the cosine will always be positive. */
+ vert->v_rot = cosf(pt->uv_rot) * signf(pt->uv_rot);
+ vert->thickness = max_ff(0.0f, gps->thickness * pt->pressure) * (round_cap1 ? 1.0 : -1.0);
+ /* Tag endpoint material to -1 so they get discarded by vertex shader. */
+ vert->mat = (is_endpoint) ? -1 : (gps->mat_nr % GP_MATERIAL_BUFFER_LEN);
+
+ vert->aspect_ratio = gps->aspect_ratio[0] / max_ff(gps->aspect_ratio[1], 1e-8);
+ vert->hardness = gps->hardeness;
+}
+
+static void gpencil_buffer_add_stroke(gpStrokeVert *verts,
+ gpColorVert *cols,
+ const bGPDstroke *gps)
+{
+ const bGPDspoint *pts = gps->points;
+ int pts_len = gps->totpoints;
+ bool is_cyclic = gpencil_stroke_is_cyclic(gps);
+ int v = gps->runtime.stroke_start;
+
+ /* First point for adjacency (not drawn). */
+ int adj_idx = (is_cyclic) ? (pts_len - 1) : min_ii(pts_len - 1, 1);
+ gpencil_buffer_add_point(verts, cols, gps, &pts[adj_idx], v++, true);
+
+ for (int i = 0; i < pts_len; i++) {
+ gpencil_buffer_add_point(verts, cols, gps, &pts[i], v++, false);
+ }
+ /* Draw line to first point to complete the loop for cyclic strokes. */
+ if (is_cyclic) {
+ gpencil_buffer_add_point(verts, cols, gps, &pts[0], v++, false);
+ }
+ /* Last adjacency point (not drawn). */
+ adj_idx = (is_cyclic) ? 1 : max_ii(0, pts_len - 2);
+ gpencil_buffer_add_point(verts, cols, gps, &pts[adj_idx], v++, true);
+}
+
+static void gpencil_buffer_add_fill(GPUIndexBufBuilder *ibo, const bGPDstroke *gps)
+{
+ int tri_len = gps->tot_triangles;
+ int v = gps->runtime.stroke_start;
+ for (int i = 0; i < tri_len; i++) {
+ uint *tri = gps->triangles[i].verts;
+ GPU_indexbuf_add_tri_verts(ibo, v + tri[0], v + tri[1], v + tri[2]);
+ }
+}
+
+static void gpencil_stroke_iter_cb(bGPDlayer *UNUSED(gpl),
+ bGPDframe *UNUSED(gpf),
+ bGPDstroke *gps,
+ void *thunk)
+{
+ gpIterData *iter = (gpIterData *)thunk;
+ gpencil_buffer_add_stroke(iter->verts, iter->cols, gps);
+ if (gps->tot_triangles > 0) {
+ gpencil_buffer_add_fill(&iter->ibo, gps);
+ }
+}
+
+static void gp_object_verts_count_cb(bGPDlayer *UNUSED(gpl),
+ bGPDframe *UNUSED(gpf),
+ bGPDstroke *gps,
+ void *thunk)
+{
+ gpIterData *iter = (gpIterData *)thunk;
+
+ /* Store first index offset */
+ gps->runtime.stroke_start = iter->vert_len;
+ gps->runtime.fill_start = iter->tri_len;
+ iter->vert_len += gps->totpoints + 2 + gpencil_stroke_is_cyclic(gps);
+ iter->tri_len += gps->tot_triangles;
+}
+
+static void gpencil_batches_ensure(Object *ob, GpencilBatchCache *cache, int cfra)
+{
+ bGPdata *gpd = (bGPdata *)ob->data;
+
+ if (cache->vbo == NULL) {
+ /* Should be discarded together. */
+ BLI_assert(cache->vbo == NULL && cache->ibo == NULL);
+ BLI_assert(cache->stroke_batch == NULL && cache->stroke_batch == NULL);
+ /* TODO/PERF: Could be changed to only do it if needed.
+ * For now it's simpler to assume we always need it
+ * since multiple viewport could or could not need it.
+ * Ideally we should have a dedicated onion skin geom batch. */
+ /* IMPORTANT: Keep in sync with gpencil_edit_batches_ensure() */
+ bool do_onion = true;
+
+ /* First count how many vertices and triangles are needed for the whole object. */
+ gpIterData iter = {
+ .gpd = gpd,
+ .verts = NULL,
+ .ibo = {0},
+ .vert_len = 1, /* Start at 1 for the gl_InstanceID trick to work (see vert shader). */
+ .tri_len = 0,
+ };
+ BKE_gpencil_visible_stroke_iter(ob, NULL, gp_object_verts_count_cb, &iter, do_onion, cfra);
+
+ /* Create VBOs. */
+ GPUVertFormat *format = gpencil_stroke_format();
+ GPUVertFormat *format_col = gpencil_color_format();
+ cache->vbo = GPU_vertbuf_create_with_format(format);
+ cache->vbo_col = GPU_vertbuf_create_with_format(format_col);
+ /* Add extra space at the end of the buffer because of quad load. */
+ GPU_vertbuf_data_alloc(cache->vbo, iter.vert_len + 2);
+ GPU_vertbuf_data_alloc(cache->vbo_col, iter.vert_len + 2);
+ iter.verts = (gpStrokeVert *)cache->vbo->data;
+ iter.cols = (gpColorVert *)cache->vbo_col->data;
+ /* Create IBO. */
+ GPU_indexbuf_init(&iter.ibo, GPU_PRIM_TRIS, iter.tri_len, iter.vert_len);
+
+ /* Fill buffers with data. */
+ BKE_gpencil_visible_stroke_iter(ob, NULL, gpencil_stroke_iter_cb, &iter, do_onion, cfra);
+
+ /* Mark last 2 verts as invalid. */
+ for (int i = 0; i < 2; i++) {
+ iter.verts[iter.vert_len + i].mat = -1;
+ }
+
+ /* Finish the IBO. */
+ cache->ibo = GPU_indexbuf_build(&iter.ibo);
+
+ /* Create the batches */
+ cache->fill_batch = GPU_batch_create(GPU_PRIM_TRIS, cache->vbo, cache->ibo);
+ GPU_batch_vertbuf_add(cache->fill_batch, cache->vbo_col);
+ cache->stroke_batch = GPU_batch_create(GPU_PRIM_TRI_STRIP, gpencil_dummy_buffer_get(), NULL);
+ GPU_batch_instbuf_add_ex(cache->stroke_batch, cache->vbo, 0);
+ GPU_batch_instbuf_add_ex(cache->stroke_batch, cache->vbo_col, 0);
+
+ gpd->flag &= ~GP_DATA_CACHE_IS_DIRTY;
+ cache->is_dirty = false;
+ }
+}
+
+GPUBatch *DRW_cache_gpencil_strokes_get(Object *ob, int cfra)
+{
+ GpencilBatchCache *cache = gpencil_batch_cache_get(ob, cfra);
+ gpencil_batches_ensure(ob, cache, cfra);
+
+ return cache->stroke_batch;
+}
+
+GPUBatch *DRW_cache_gpencil_fills_get(Object *ob, int cfra)
+{
+ GpencilBatchCache *cache = gpencil_batch_cache_get(ob, cfra);
+ gpencil_batches_ensure(ob, cache, cfra);
+
+ return cache->fill_batch;
+}
+
+static void gp_lines_indices_cb(bGPDlayer *UNUSED(gpl),
+ bGPDframe *UNUSED(gpf),
+ bGPDstroke *gps,
+ void *thunk)
+{
+ gpIterData *iter = (gpIterData *)thunk;
+ int pts_len = gps->totpoints + gpencil_stroke_is_cyclic(gps);
+
+ int start = gps->runtime.stroke_start + 1;
+ int end = start + pts_len;
+ for (int i = start; i < end; i++) {
+ GPU_indexbuf_add_generic_vert(&iter->ibo, i);
+ }
+ GPU_indexbuf_add_primitive_restart(&iter->ibo);
+}
+
+GPUBatch *DRW_cache_gpencil_face_wireframe_get(Object *ob)
+{
+ const DRWContextState *draw_ctx = DRW_context_state_get();
+ int cfra = DEG_get_ctime(draw_ctx->depsgraph);
+
+ GpencilBatchCache *cache = gpencil_batch_cache_get(ob, cfra);
+ gpencil_batches_ensure(ob, cache, cfra);
+
+ if (cache->lines_batch == NULL) {
+ GPUVertBuf *vbo = cache->vbo;
+
+ gpIterData iter = {
+ .gpd = ob->data,
+ .ibo = {0},
+ };
+
+ GPU_indexbuf_init_ex(&iter.ibo, GPU_PRIM_LINE_STRIP, vbo->vertex_len, vbo->vertex_len);
+
+ /* IMPORTANT: Keep in sync with gpencil_edit_batches_ensure() */
+ bool do_onion = true;
+ BKE_gpencil_visible_stroke_iter(ob, NULL, gp_lines_indices_cb, &iter, do_onion, cfra);
+
+ GPUIndexBuf *ibo = GPU_indexbuf_build(&iter.ibo);
+
+ cache->lines_batch = GPU_batch_create_ex(GPU_PRIM_LINE_STRIP, vbo, ibo, GPU_BATCH_OWNS_INDEX);
+ }
+ return cache->lines_batch;
+}
+
+/** \} */
+
+/* ---------------------------------------------------------------------- */
+/** \name Sbuffer stroke batches.
+ * \{ */
+
+bGPDstroke *DRW_cache_gpencil_sbuffer_stroke_data_get(Object *ob)
+{
+ bGPdata *gpd = (bGPdata *)ob->data;
+ Brush *brush = gpd->runtime.sbuffer_brush;
+ /* Convert the sbuffer to a bGPDstroke. */
+ if (gpd->runtime.sbuffer_gps == NULL) {
+ bGPDstroke *gps = MEM_callocN(sizeof(*gps), "bGPDstroke sbuffer");
+ gps->totpoints = gpd->runtime.sbuffer_used;
+ gps->mat_nr = max_ii(0, gpd->runtime.matid - 1);
+ gps->flag = gpd->runtime.sbuffer_sflag;
+ gps->thickness = brush->size;
+ gps->hardeness = brush->gpencil_settings->hardeness;
+ copy_v2_v2(gps->aspect_ratio, brush->gpencil_settings->aspect_ratio);
+
+ /* Reduce slightly the opacity of fill to make easy fill areas while drawing. */
+ gps->fill_opacity_fac = 0.8f;
+
+ gps->tot_triangles = max_ii(0, gpd->runtime.sbuffer_used - 2);
+ gps->caps[0] = gps->caps[1] = GP_STROKE_CAP_ROUND;
+ gps->runtime.stroke_start = 1; /* Add one for the adjacency index. */
+ copy_v4_v4(gps->vert_color_fill, gpd->runtime.vert_color_fill);
+ gpd->runtime.sbuffer_gps = gps;
+ }
+ return gpd->runtime.sbuffer_gps;
+}
+
+static void gpencil_sbuffer_stroke_ensure(bGPdata *gpd, bool do_stroke, bool do_fill)
+{
+ tGPspoint *tpoints = gpd->runtime.sbuffer;
+ bGPDstroke *gps = gpd->runtime.sbuffer_gps;
+ int vert_len = gpd->runtime.sbuffer_used;
+
+ /* DRW_cache_gpencil_sbuffer_stroke_data_get need to have been called previously. */
+ BLI_assert(gps != NULL);
+
+ if (do_stroke && (gpd->runtime.sbuffer_stroke_batch == NULL)) {
+ gps->points = MEM_mallocN(vert_len * sizeof(*gps->points), __func__);
+
+ const DRWContextState *draw_ctx = DRW_context_state_get();
+ Scene *scene = draw_ctx->scene;
+ ARegion *region = draw_ctx->region;
+ Object *ob = draw_ctx->obact;
+
+ BLI_assert(ob && (ob->type == OB_GPENCIL));
+
+ /* Get origin to reproject points. */
+ float origin[3];
+ bGPDlayer *gpl = BKE_gpencil_layer_active_get(gpd);
+ ToolSettings *ts = scene->toolsettings;
+ ED_gpencil_drawing_reference_get(scene, ob, gpl, ts->gpencil_v3d_align, origin);
+
+ for (int i = 0; i < vert_len; i++) {
+ ED_gpencil_tpoint_to_point(region, origin, &tpoints[i], &gps->points[i]);
+ mul_m4_v3(ob->imat, &gps->points[i].x);
+ bGPDspoint *pt = &gps->points[i];
+ copy_v4_v4(pt->vert_color, gpd->runtime.vert_color);
+ }
+ /* Calc uv data along the stroke. */
+ BKE_gpencil_stroke_uv_update(gps);
+
+ /* Create VBO. */
+ GPUVertFormat *format = gpencil_stroke_format();
+ GPUVertFormat *format_color = gpencil_color_format();
+ GPUVertBuf *vbo = GPU_vertbuf_create_with_format(format);
+ GPUVertBuf *vbo_col = GPU_vertbuf_create_with_format(format_color);
+ /* Add extra space at the end (and start) of the buffer because of quad load and cyclic. */
+ GPU_vertbuf_data_alloc(vbo, 1 + vert_len + 1 + 2);
+ GPU_vertbuf_data_alloc(vbo_col, 1 + vert_len + 1 + 2);
+ gpStrokeVert *verts = (gpStrokeVert *)vbo->data;
+ gpColorVert *cols = (gpColorVert *)vbo_col->data;
+
+ /* Fill buffers with data. */
+ gpencil_buffer_add_stroke(verts, cols, gps);
+
+ GPUBatch *batch = GPU_batch_create(GPU_PRIM_TRI_STRIP, gpencil_dummy_buffer_get(), NULL);
+ GPU_batch_instbuf_add_ex(batch, vbo, true);
+ GPU_batch_instbuf_add_ex(batch, vbo_col, true);
+
+ gpd->runtime.sbuffer_stroke_batch = batch;
+
+ MEM_freeN(gps->points);
+ }
+
+ if (do_fill && (gpd->runtime.sbuffer_fill_batch == NULL)) {
+ /* Create IBO. */
+ GPUIndexBufBuilder ibo_builder;
+ GPU_indexbuf_init(&ibo_builder, GPU_PRIM_TRIS, gps->tot_triangles, vert_len);
+
+ if (gps->tot_triangles > 0) {
+ float(*tpoints2d)[2] = MEM_mallocN(sizeof(*tpoints2d) * vert_len, __func__);
+ /* Triangulate in 2D. */
+ for (int i = 0; i < vert_len; i++) {
+ copy_v2_v2(tpoints2d[i], &tpoints[i].x);
+ }
+ /* Compute directly inside the IBO data buffer. */
+ /* OPTI: This is a bottleneck if the stroke is very long. */
+ BLI_polyfill_calc(tpoints2d, (uint)vert_len, 0, (uint(*)[3])ibo_builder.data);
+ /* Add stroke start offset. */
+ for (int i = 0; i < gps->tot_triangles * 3; i++) {
+ ibo_builder.data[i] += gps->runtime.stroke_start;
+ }
+ /* HACK since we didn't use the builder API to avoid another malloc and copy,
+ * we need to set the number of indices manually. */
+ ibo_builder.index_len = gps->tot_triangles * 3;
+
+ MEM_freeN(tpoints2d);
+ }
+
+ GPUIndexBuf *ibo = GPU_indexbuf_build(&ibo_builder);
+ GPUVertBuf *vbo = gpd->runtime.sbuffer_stroke_batch->inst[0];
+ GPUVertBuf *vbo_col = gpd->runtime.sbuffer_stroke_batch->inst[1];
+
+ GPUBatch *batch = GPU_batch_create_ex(GPU_PRIM_TRIS, vbo, ibo, GPU_BATCH_OWNS_INDEX);
+ GPU_batch_vertbuf_add(batch, vbo_col);
+
+ gpd->runtime.sbuffer_fill_batch = batch;
+ }
+}
+
+GPUBatch *DRW_cache_gpencil_sbuffer_stroke_get(Object *ob)
+{
+ bGPdata *gpd = (bGPdata *)ob->data;
+ gpencil_sbuffer_stroke_ensure(gpd, true, false);
+
+ return gpd->runtime.sbuffer_stroke_batch;
+}
+
+GPUBatch *DRW_cache_gpencil_sbuffer_fill_get(Object *ob)
+{
+ bGPdata *gpd = (bGPdata *)ob->data;
+ /* Fill batch also need stroke batch to be created (vbo is shared). */
+ gpencil_sbuffer_stroke_ensure(gpd, true, true);
+
+ return gpd->runtime.sbuffer_fill_batch;
+}
+
+/* Sbuffer batches are temporary. We need to clear it after drawing */
+void DRW_cache_gpencil_sbuffer_clear(Object *ob)
+{
+ bGPdata *gpd = (bGPdata *)ob->data;
+ MEM_SAFE_FREE(gpd->runtime.sbuffer_gps);
+ GPU_BATCH_DISCARD_SAFE(gpd->runtime.sbuffer_fill_batch);
+ GPU_BATCH_DISCARD_SAFE(gpd->runtime.sbuffer_stroke_batch);
+}
+
+/** \} */
+
+/* ---------------------------------------------------------------------- */
+/* Edit GPencil Batches */
+
+#define GP_EDIT_POINT_SELECTED (1 << 0)
+#define GP_EDIT_STROKE_SELECTED (1 << 1)
+#define GP_EDIT_MULTIFRAME (1 << 2)
+#define GP_EDIT_STROKE_START (1 << 3)
+#define GP_EDIT_STROKE_END (1 << 4)
+
+typedef struct gpEditIterData {
+ gpEditVert *verts;
+ int vgindex;
+} gpEditIterData;
+
+static uint32_t gpencil_point_edit_flag(const bool layer_lock,
+ const bGPDspoint *pt,
+ int v,
+ int v_len)
+{
+ uint32_t sflag = 0;
+ SET_FLAG_FROM_TEST(sflag, (!layer_lock) && pt->flag & GP_SPOINT_SELECT, GP_EDIT_POINT_SELECTED);
+ SET_FLAG_FROM_TEST(sflag, v == 0, GP_EDIT_STROKE_START);
+ SET_FLAG_FROM_TEST(sflag, v == (v_len - 1), GP_EDIT_STROKE_END);
+ return sflag;
+}
+
+static float gpencil_point_edit_weight(const MDeformVert *dvert, int v, int vgindex)
+{
+ return (dvert && dvert[v].dw) ? BKE_defvert_find_weight(&dvert[v], vgindex) : -1.0f;
+}
+
+static void gpencil_edit_stroke_iter_cb(bGPDlayer *gpl,
+ bGPDframe *gpf,
+ bGPDstroke *gps,
+ void *thunk)
+{
+ gpEditIterData *iter = (gpEditIterData *)thunk;
+ const int v_len = gps->totpoints;
+ const int v = gps->runtime.stroke_start + 1;
+ MDeformVert *dvert = ((iter->vgindex > -1) && gps->dvert) ? gps->dvert : NULL;
+ gpEditVert *vert_ptr = iter->verts + v;
+
+ const bool layer_lock = (gpl->flag & GP_LAYER_LOCKED);
+ uint32_t sflag = 0;
+ SET_FLAG_FROM_TEST(
+ sflag, (!layer_lock) && gps->flag & GP_STROKE_SELECT, GP_EDIT_STROKE_SELECTED);
+ SET_FLAG_FROM_TEST(sflag, gpf->runtime.onion_id != 0.0f, GP_EDIT_MULTIFRAME);
+
+ for (int i = 0; i < v_len; i++) {
+ vert_ptr->vflag = sflag | gpencil_point_edit_flag(layer_lock, &gps->points[i], i, v_len);
+ vert_ptr->weight = gpencil_point_edit_weight(dvert, i, iter->vgindex);
+ vert_ptr++;
+ }
+ /* Draw line to first point to complete the loop for cyclic strokes. */
+ vert_ptr->vflag = sflag | gpencil_point_edit_flag(layer_lock, &gps->points[0], 0, v_len);
+ vert_ptr->weight = gpencil_point_edit_weight(dvert, 0, iter->vgindex);
+}
+
+static void gpencil_edit_batches_ensure(Object *ob, GpencilBatchCache *cache, int cfra)
+{
+ bGPdata *gpd = (bGPdata *)ob->data;
+
+ if (cache->edit_vbo == NULL) {
+ /* TODO/PERF: Could be changed to only do it if needed.
+ * For now it's simpler to assume we always need it
+ * since multiple viewport could or could not need it.
+ * Ideally we should have a dedicated onion skin geom batch. */
+ /* IMPORTANT: Keep in sync with gpencil_batches_ensure() */
+ bool do_onion = true;
+
+ /* Vertex counting has already been done for cache->vbo. */
+ BLI_assert(cache->vbo);
+ int vert_len = cache->vbo->vertex_len;
+
+ gpEditIterData iter;
+ iter.vgindex = ob->actdef - 1;
+ if (!BLI_findlink(&ob->defbase, iter.vgindex)) {
+ iter.vgindex = -1;
+ }
+
+ /* Create VBO. */
+ GPUVertFormat *format = gpencil_edit_stroke_format();
+ cache->edit_vbo = GPU_vertbuf_create_with_format(format);
+ /* Add extra space at the end of the buffer because of quad load. */
+ GPU_vertbuf_data_alloc(cache->edit_vbo, vert_len);
+ iter.verts = (gpEditVert *)cache->edit_vbo->data;
+
+ /* Fill buffers with data. */
+ BKE_gpencil_visible_stroke_iter(ob, NULL, gpencil_edit_stroke_iter_cb, &iter, do_onion, cfra);
+
+ /* Create the batches */
+ cache->edit_points_batch = GPU_batch_create(GPU_PRIM_POINTS, cache->vbo, NULL);
+ GPU_batch_vertbuf_add(cache->edit_points_batch, cache->edit_vbo);
+
+ cache->edit_lines_batch = GPU_batch_create(GPU_PRIM_LINE_STRIP, cache->vbo, NULL);
+ GPU_batch_vertbuf_add(cache->edit_lines_batch, cache->edit_vbo);
+
+ gpd->flag &= ~GP_DATA_CACHE_IS_DIRTY;
+ cache->is_dirty = false;
+ }
+}
+
+GPUBatch *DRW_cache_gpencil_edit_lines_get(Object *ob, int cfra)
+{
+ GpencilBatchCache *cache = gpencil_batch_cache_get(ob, cfra);
+ gpencil_batches_ensure(ob, cache, cfra);
+ gpencil_edit_batches_ensure(ob, cache, cfra);
+
+ return cache->edit_lines_batch;
+}
+
+GPUBatch *DRW_cache_gpencil_edit_points_get(Object *ob, int cfra)
+{
+ GpencilBatchCache *cache = gpencil_batch_cache_get(ob, cfra);
+ gpencil_batches_ensure(ob, cache, cfra);
+ gpencil_edit_batches_ensure(ob, cache, cfra);
+
+ return cache->edit_points_batch;
+}
+
+/** \} */
diff --git a/source/blender/draw/intern/draw_common.c b/source/blender/draw/intern/draw_common.c
index a4e46b3b59f..b1ad1455d8c 100644
--- a/source/blender/draw/intern/draw_common.c
+++ b/source/blender/draw/intern/draw_common.c
@@ -72,6 +72,8 @@ void DRW_globals_update(void)
UI_COLOR_RGBA_FROM_U8(0xB0, 0x00, 0xB0, 0xFF, gb->colorVertexMissingData);
UI_GetThemeColor4fv(TH_EDITMESH_ACTIVE, gb->colorEditMeshActive);
UI_GetThemeColor4fv(TH_EDGE_SELECT, gb->colorEdgeSelect);
+ UI_GetThemeColor4fv(TH_GP_VERTEX, gb->colorGpencilVertex);
+ UI_GetThemeColor4fv(TH_GP_VERTEX_SELECT, gb->colorGpencilVertexSelect);
UI_GetThemeColor4fv(TH_EDGE_SEAM, gb->colorEdgeSeam);
UI_GetThemeColor4fv(TH_EDGE_SHARP, gb->colorEdgeSharp);
diff --git a/source/blender/draw/intern/draw_common.h b/source/blender/draw/intern/draw_common.h
index bea287da4e9..24d3b7fa7b6 100644
--- a/source/blender/draw/intern/draw_common.h
+++ b/source/blender/draw/intern/draw_common.h
@@ -68,6 +68,8 @@ typedef struct GlobalsUboStorage {
float colorFace[4];
float colorFaceSelect[4];
float colorFaceFreestyle[4];
+ float colorGpencilVertex[4];
+ float colorGpencilVertexSelect[4];
float colorNormal[4];
float colorVNormal[4];
float colorLNormal[4];
diff --git a/source/blender/draw/intern/draw_manager.c b/source/blender/draw/intern/draw_manager.c
index 4ec49536211..85caf0825e0 100644
--- a/source/blender/draw/intern/draw_manager.c
+++ b/source/blender/draw/intern/draw_manager.c
@@ -1183,7 +1183,7 @@ static void drw_engines_enable(ViewLayer *UNUSED(view_layer),
const bool use_xray = XRAY_ENABLED(v3d);
drw_engines_enable_from_engine(engine_type, drawtype, use_xray);
- if (gpencil_engine_needed) {
+ if (gpencil_engine_needed && ((drawtype >= OB_SOLID) || !use_xray)) {
use_drw_engine(&draw_engine_gpencil_type);
}
drw_engines_enable_overlays();
@@ -1654,14 +1654,6 @@ static void DRW_render_gpencil_to_image(RenderEngine *engine,
void DRW_render_gpencil(struct RenderEngine *engine, struct Depsgraph *depsgraph)
{
- /* This function is only valid for Cycles & Workbench
- * Eevee does all work in the Eevee render directly.
- * Maybe it can be done equal for all engines?
- */
- if (STREQ(engine->type->name, "Eevee")) {
- return;
- }
-
/* Early out if there are no grease pencil objects, especially important
* to avoid failing in in background renders without OpenGL context. */
if (!DRW_render_check_grease_pencil(depsgraph)) {
@@ -1740,8 +1732,13 @@ void DRW_render_gpencil(struct RenderEngine *engine, struct Depsgraph *depsgraph
GPU_framebuffer_restore();
/* Changing Context */
- /* GPXX Review this context */
- DRW_opengl_context_disable();
+ if (re_gl_context != NULL) {
+ DRW_gpu_render_context_disable(re_gpu_context);
+ DRW_opengl_render_context_disable(re_gl_context);
+ }
+ else {
+ DRW_opengl_context_disable();
+ }
DST.buffer_finish_called = false;
}
@@ -1838,14 +1835,6 @@ void DRW_render_to_image(RenderEngine *engine, struct Depsgraph *depsgraph)
drw_view_reset();
engine_type->draw_engine->render_to_image(data, engine, render_layer, &render_rect);
DST.buffer_finish_called = false;
-
- /* grease pencil: render result is merged in the previous render result. */
- if (DRW_render_check_grease_pencil(depsgraph)) {
- DRW_state_reset();
- drw_view_reset();
- DRW_render_gpencil_to_image(engine, render_layer, &render_rect);
- DST.buffer_finish_called = false;
- }
}
RE_engine_end_result(engine, render_result, false, false, false);
@@ -2215,13 +2204,6 @@ void DRW_draw_select_loop(struct Depsgraph *depsgraph,
}
}
- /* TODO: GPXX Workaround for grease pencil selection while draw manager support a callback from
- * scene finish */
- void *data = GPU_viewport_engine_data_get(DST.viewport, &draw_engine_gpencil_type);
- if (data != NULL) {
- DRW_gpencil_free_runtime_data(data);
- }
-
DRW_state_lock(0);
DRW_state_reset();
diff --git a/source/blender/draw/intern/draw_manager.h b/source/blender/draw/intern/draw_manager.h
index 6d415ee95b3..cb825becd73 100644
--- a/source/blender/draw/intern/draw_manager.h
+++ b/source/blender/draw/intern/draw_manager.h
@@ -181,7 +181,8 @@ typedef enum {
DRW_CMD_DRAW = 0, /* Only sortable type. Must be 0. */
DRW_CMD_DRAW_RANGE = 1,
DRW_CMD_DRAW_INSTANCE = 2,
- DRW_CMD_DRAW_PROCEDURAL = 3,
+ DRW_CMD_DRAW_INSTANCE_RANGE = 3,
+ DRW_CMD_DRAW_PROCEDURAL = 4,
/* Other Commands */
DRW_CMD_CLEAR = 12,
DRW_CMD_DRWSTATE = 13,
@@ -200,6 +201,7 @@ typedef struct DRWCommandDraw {
/* Assume DRWResourceHandle to be 0. */
typedef struct DRWCommandDrawRange {
GPUBatch *batch;
+ DRWResourceHandle handle;
uint vert_first;
uint vert_count;
} DRWCommandDrawRange;
@@ -211,6 +213,13 @@ typedef struct DRWCommandDrawInstance {
uint use_attribs; /* bool */
} DRWCommandDrawInstance;
+typedef struct DRWCommandDrawInstanceRange {
+ GPUBatch *batch;
+ DRWResourceHandle handle;
+ uint inst_first;
+ uint inst_count;
+} DRWCommandDrawInstanceRange;
+
typedef struct DRWCommandDrawProcedural {
GPUBatch *batch;
DRWResourceHandle handle;
@@ -224,7 +233,9 @@ typedef struct DRWCommandSetMutableState {
} DRWCommandSetMutableState;
typedef struct DRWCommandSetStencil {
- uint mask;
+ uint write_mask;
+ uint comp_mask;
+ uint ref;
} DRWCommandSetStencil;
typedef struct DRWCommandSetSelectID {
@@ -243,6 +254,7 @@ typedef union DRWCommand {
DRWCommandDraw draw;
DRWCommandDrawRange range;
DRWCommandDrawInstance instance;
+ DRWCommandDrawInstanceRange instance_range;
DRWCommandDrawProcedural procedural;
DRWCommandSetMutableState state;
DRWCommandSetStencil stencil;
@@ -273,6 +285,7 @@ typedef enum {
DRW_UNIFORM_BLOCK_OBMATS,
DRW_UNIFORM_BLOCK_OBINFOS,
DRW_UNIFORM_RESOURCE_CHUNK,
+ DRW_UNIFORM_RESOURCE_ID,
/** Legacy / Fallback */
DRW_UNIFORM_BASE_INSTANCE,
DRW_UNIFORM_MODEL_MATRIX,
@@ -469,7 +482,6 @@ typedef struct DRWManager {
/* Managed by `DRW_state_set`, `DRW_state_reset` */
DRWState state;
DRWState state_lock;
- uint stencil_mask;
/* Per viewport */
GPUViewport *viewport;
diff --git a/source/blender/draw/intern/draw_manager_data.c b/source/blender/draw/intern/draw_manager_data.c
index 28d5daf011c..83142da051a 100644
--- a/source/blender/draw/intern/draw_manager_data.c
+++ b/source/blender/draw/intern/draw_manager_data.c
@@ -637,13 +637,12 @@ static void drw_command_draw(DRWShadingGroup *shgroup, GPUBatch *batch, DRWResou
cmd->handle = handle;
}
-static void drw_command_draw_range(DRWShadingGroup *shgroup,
- GPUBatch *batch,
- uint start,
- uint count)
+static void drw_command_draw_range(
+ DRWShadingGroup *shgroup, GPUBatch *batch, DRWResourceHandle handle, uint start, uint count)
{
DRWCommandDrawRange *cmd = drw_command_create(shgroup, DRW_CMD_DRAW_RANGE);
cmd->batch = batch;
+ cmd->handle = handle;
cmd->vert_first = start;
cmd->vert_count = count;
}
@@ -661,6 +660,16 @@ static void drw_command_draw_instance(DRWShadingGroup *shgroup,
cmd->use_attribs = use_attrib;
}
+static void drw_command_draw_intance_range(
+ DRWShadingGroup *shgroup, GPUBatch *batch, DRWResourceHandle handle, uint start, uint count)
+{
+ DRWCommandDrawInstanceRange *cmd = drw_command_create(shgroup, DRW_CMD_DRAW_INSTANCE_RANGE);
+ cmd->batch = batch;
+ cmd->handle = handle;
+ cmd->inst_first = start;
+ cmd->inst_count = count;
+}
+
static void drw_command_draw_procedural(DRWShadingGroup *shgroup,
GPUBatch *batch,
DRWResourceHandle handle,
@@ -681,11 +690,18 @@ static void drw_command_set_select_id(DRWShadingGroup *shgroup, GPUVertBuf *buf,
cmd->select_id = select_id;
}
-static void drw_command_set_stencil_mask(DRWShadingGroup *shgroup, uint mask)
+static void drw_command_set_stencil_mask(DRWShadingGroup *shgroup,
+ uint write_mask,
+ uint reference,
+ uint comp_mask)
{
- BLI_assert(mask <= 0xFF);
+ BLI_assert(write_mask <= 0xFF);
+ BLI_assert(reference <= 0xFF);
+ BLI_assert(comp_mask <= 0xFF);
DRWCommandSetStencil *cmd = drw_command_create(shgroup, DRW_CMD_STENCIL);
- cmd->mask = mask;
+ cmd->write_mask = write_mask;
+ cmd->comp_mask = comp_mask;
+ cmd->ref = reference;
}
static void drw_command_clear(DRWShadingGroup *shgroup,
@@ -746,13 +762,27 @@ void DRW_shgroup_call_ex(DRWShadingGroup *shgroup,
}
}
-void DRW_shgroup_call_range(DRWShadingGroup *shgroup, struct GPUBatch *geom, uint v_sta, uint v_ct)
+void DRW_shgroup_call_range(
+ DRWShadingGroup *shgroup, struct Object *ob, GPUBatch *geom, uint v_sta, uint v_ct)
+{
+ BLI_assert(geom != NULL);
+ if (G.f & G_FLAG_PICKSEL) {
+ drw_command_set_select_id(shgroup, NULL, DST.select_id);
+ }
+ DRWResourceHandle handle = drw_resource_handle(shgroup, ob ? ob->obmat : NULL, ob);
+ drw_command_draw_range(shgroup, geom, handle, v_sta, v_ct);
+}
+
+void DRW_shgroup_call_instance_range(
+ DRWShadingGroup *shgroup, Object *ob, struct GPUBatch *geom, uint i_sta, uint i_ct)
{
+ BLI_assert(i_ct > 0);
BLI_assert(geom != NULL);
if (G.f & G_FLAG_PICKSEL) {
drw_command_set_select_id(shgroup, NULL, DST.select_id);
}
- drw_command_draw_range(shgroup, geom, v_sta, v_ct);
+ DRWResourceHandle handle = drw_resource_handle(shgroup, ob ? ob->obmat : NULL, ob);
+ drw_command_draw_intance_range(shgroup, geom, handle, i_sta, i_ct);
}
static void drw_shgroup_call_procedural_add_ex(DRWShadingGroup *shgroup,
@@ -1101,12 +1131,18 @@ static void drw_shgroup_init(DRWShadingGroup *shgroup, GPUShader *shader)
int info_ubo_location = GPU_shader_get_uniform_block(shader, "infoBlock");
int baseinst_location = GPU_shader_get_builtin_uniform(shader, GPU_UNIFORM_BASE_INSTANCE);
int chunkid_location = GPU_shader_get_builtin_uniform(shader, GPU_UNIFORM_RESOURCE_CHUNK);
+ int resourceid_location = GPU_shader_get_builtin_uniform(shader, GPU_UNIFORM_RESOURCE_ID);
if (chunkid_location != -1) {
drw_shgroup_uniform_create_ex(
shgroup, chunkid_location, DRW_UNIFORM_RESOURCE_CHUNK, NULL, 0, 1);
}
+ if (resourceid_location != -1) {
+ drw_shgroup_uniform_create_ex(
+ shgroup, resourceid_location, DRW_UNIFORM_RESOURCE_ID, NULL, 0, 1);
+ }
+
if (baseinst_location != -1) {
drw_shgroup_uniform_create_ex(
shgroup, baseinst_location, DRW_UNIFORM_BASE_INSTANCE, NULL, 0, 1);
@@ -1302,9 +1338,18 @@ void DRW_shgroup_state_disable(DRWShadingGroup *shgroup, DRWState state)
drw_command_set_mutable_state(shgroup, 0x0, state);
}
+void DRW_shgroup_stencil_set(DRWShadingGroup *shgroup,
+ uint write_mask,
+ uint reference,
+ uint comp_mask)
+{
+ drw_command_set_stencil_mask(shgroup, write_mask, reference, comp_mask);
+}
+
+/* TODO remove this function. */
void DRW_shgroup_stencil_mask(DRWShadingGroup *shgroup, uint mask)
{
- drw_command_set_stencil_mask(shgroup, mask);
+ drw_command_set_stencil_mask(shgroup, 0xFF, mask, 0xFF);
}
void DRW_shgroup_clear_framebuffer(DRWShadingGroup *shgroup,
diff --git a/source/blender/draw/intern/draw_manager_exec.c b/source/blender/draw/intern/draw_manager_exec.c
index 90de20c0d30..49c71a3f212 100644
--- a/source/blender/draw/intern/draw_manager_exec.c
+++ b/source/blender/draw/intern/draw_manager_exec.c
@@ -50,6 +50,7 @@ void DRW_select_load_id(uint id)
typedef struct DRWCommandsState {
GPUBatch *batch;
int resource_chunk;
+ int resource_id;
int base_inst;
int inst_count;
int v_first;
@@ -60,6 +61,7 @@ typedef struct DRWCommandsState {
int obinfos_loc;
int baseinst_loc;
int chunkid_loc;
+ int resourceid_loc;
/* Legacy matrix support. */
int obmat_loc;
int obinv_loc;
@@ -221,7 +223,6 @@ void drw_state_set(DRWState state)
{
int test;
if (CHANGED_ANY_STORE_VAR(DRW_STATE_STENCIL_TEST_ENABLED, test)) {
- DST.stencil_mask = STENCIL_UNDEFINED;
if (test) {
glEnable(GL_STENCIL_TEST);
}
@@ -234,11 +235,12 @@ void drw_state_set(DRWState state)
/* Blending (all buffer) */
{
int test;
- if (CHANGED_ANY_STORE_VAR(
- DRW_STATE_BLEND_ALPHA | DRW_STATE_BLEND_ALPHA_PREMUL | DRW_STATE_BLEND_ADD |
- DRW_STATE_BLEND_MUL | DRW_STATE_BLEND_ADD_FULL | DRW_STATE_BLEND_OIT |
- DRW_STATE_BLEND_BACKGROUND | DRW_STATE_BLEND_CUSTOM | DRW_STATE_LOGIC_INVERT,
- test)) {
+ if (CHANGED_ANY_STORE_VAR(DRW_STATE_BLEND_ALPHA | DRW_STATE_BLEND_ALPHA_PREMUL |
+ DRW_STATE_BLEND_ADD | DRW_STATE_BLEND_MUL |
+ DRW_STATE_BLEND_ADD_FULL | DRW_STATE_BLEND_OIT |
+ DRW_STATE_BLEND_BACKGROUND | DRW_STATE_BLEND_CUSTOM |
+ DRW_STATE_LOGIC_INVERT | DRW_STATE_BLEND_SUB,
+ test)) {
if (test) {
glEnable(GL_BLEND);
@@ -278,6 +280,9 @@ void drw_state_set(DRWState state)
/* Let alpha accumulate. */
glBlendFunc(GL_ONE, GL_ONE);
}
+ else if ((state & DRW_STATE_BLEND_SUB) != 0) {
+ glBlendFunc(GL_ONE, GL_ONE);
+ }
else if ((state & DRW_STATE_BLEND_CUSTOM) != 0) {
/* Custom blend parameters using dual source blending.
* Can only be used with one Draw Buffer. */
@@ -293,6 +298,13 @@ void drw_state_set(DRWState state)
else {
BLI_assert(0);
}
+
+ if ((state & DRW_STATE_BLEND_SUB) != 0) {
+ glBlendEquation(GL_FUNC_REVERSE_SUBTRACT);
+ }
+ else {
+ glBlendEquation(GL_FUNC_ADD);
+ }
}
else {
glDisable(GL_BLEND);
@@ -385,19 +397,23 @@ void drw_state_set(DRWState state)
DST.state = state;
}
-static void drw_stencil_set(uint mask)
+static void drw_stencil_state_set(uint write_mask, uint reference, uint compare_mask)
{
- if (DST.stencil_mask != mask) {
- DST.stencil_mask = mask;
- if ((DST.state & DRW_STATE_STENCIL_ALWAYS) != 0) {
- glStencilFunc(GL_ALWAYS, mask, 0xFF);
- }
- else if ((DST.state & DRW_STATE_STENCIL_EQUAL) != 0) {
- glStencilFunc(GL_EQUAL, mask, 0xFF);
- }
- else if ((DST.state & DRW_STATE_STENCIL_NEQUAL) != 0) {
- glStencilFunc(GL_NOTEQUAL, mask, 0xFF);
- }
+ /* Reminders:
+ * - (compare_mask & reference) is what is tested against (compare_mask & stencil_value)
+ * stencil_value being the value stored in the stencil buffer.
+ * - (writemask & reference) is what gets written if the test condition is fullfiled.
+ **/
+ glStencilMask(write_mask);
+
+ if ((DST.state & DRW_STATE_STENCIL_ALWAYS) != 0) {
+ glStencilFunc(GL_ALWAYS, reference, compare_mask);
+ }
+ else if ((DST.state & DRW_STATE_STENCIL_EQUAL) != 0) {
+ glStencilFunc(GL_EQUAL, reference, compare_mask);
+ }
+ else if ((DST.state & DRW_STATE_STENCIL_NEQUAL) != 0) {
+ glStencilFunc(GL_NOTEQUAL, reference, compare_mask);
}
}
@@ -978,6 +994,9 @@ static void draw_update_uniforms(DRWShadingGroup *shgroup,
state->chunkid_loc = uni->location;
GPU_shader_uniform_int(shgroup->shader, uni->location, 0);
break;
+ case DRW_UNIFORM_RESOURCE_ID:
+ state->resourceid_loc = uni->location;
+ break;
case DRW_UNIFORM_TFEEDBACK_TARGET:
BLI_assert(data && (*use_tfeedback == false));
*use_tfeedback = GPU_shader_transform_feedback_enable(shgroup->shader,
@@ -1096,6 +1115,14 @@ static void draw_call_resource_bind(DRWCommandsState *state, const DRWResourceHa
}
state->resource_chunk = chunk;
}
+
+ if (state->resourceid_loc != -1) {
+ int id = DRW_handle_id_get(handle);
+ if (state->resource_id != id) {
+ GPU_shader_uniform_int(NULL, state->resourceid_loc, id);
+ state->resource_id = id;
+ }
+ }
}
static void draw_call_batching_flush(DRWShadingGroup *shgroup, DRWCommandsState *state)
@@ -1114,6 +1141,7 @@ static void draw_call_single_do(DRWShadingGroup *shgroup,
DRWResourceHandle handle,
int vert_first,
int vert_count,
+ int inst_first,
int inst_count,
bool do_base_instance)
{
@@ -1142,7 +1170,7 @@ static void draw_call_single_do(DRWShadingGroup *shgroup,
batch,
vert_first,
vert_count,
- do_base_instance ? DRW_handle_id_get(&handle) : 0,
+ do_base_instance ? DRW_handle_id_get(&handle) : inst_first,
inst_count,
state->baseinst_loc);
}
@@ -1151,6 +1179,7 @@ static void draw_call_batching_start(DRWCommandsState *state)
{
state->neg_scale = false;
state->resource_chunk = 0;
+ state->resource_id = -1;
state->base_inst = 0;
state->inst_count = 0;
state->v_first = 0;
@@ -1227,6 +1256,7 @@ static void draw_shgroup(DRWShadingGroup *shgroup, DRWState pass_state)
.obinfos_loc = -1,
.baseinst_loc = -1,
.chunkid_loc = -1,
+ .resourceid_loc = -1,
.obmat_loc = -1,
.obinv_loc = -1,
.mvp_loc = -1,
@@ -1307,7 +1337,7 @@ static void draw_shgroup(DRWShadingGroup *shgroup, DRWState pass_state)
drw_state_set((pass_state & ~state.drw_state_disabled) | state.drw_state_enabled);
break;
case DRW_CMD_STENCIL:
- drw_stencil_set(cmd->stencil.mask);
+ drw_stencil_state_set(cmd->stencil.write_mask, cmd->stencil.ref, cmd->stencil.comp_mask);
break;
case DRW_CMD_SELECTID:
state.select_id = cmd->select_id.select_id;
@@ -1315,8 +1345,9 @@ static void draw_shgroup(DRWShadingGroup *shgroup, DRWState pass_state)
break;
case DRW_CMD_DRAW:
if (!USE_BATCHING || state.obmats_loc == -1 || (G.f & G_FLAG_PICKSEL) ||
- cmd->draw.batch->inst[0]) {
- draw_call_single_do(shgroup, &state, cmd->draw.batch, cmd->draw.handle, 0, 0, 0, true);
+ cmd->draw.batch->inst) {
+ draw_call_single_do(
+ shgroup, &state, cmd->draw.batch, cmd->draw.handle, 0, 0, 0, 0, true);
}
else {
draw_call_batching_do(shgroup, &state, &cmd->draw);
@@ -1329,6 +1360,7 @@ static void draw_shgroup(DRWShadingGroup *shgroup, DRWState pass_state)
cmd->procedural.handle,
0,
cmd->procedural.vert_count,
+ 0,
1,
true);
break;
@@ -1339,6 +1371,7 @@ static void draw_shgroup(DRWShadingGroup *shgroup, DRWState pass_state)
cmd->instance.handle,
0,
0,
+ 0,
cmd->instance.inst_count,
cmd->instance.use_attribs == 0);
break;
@@ -1346,12 +1379,24 @@ static void draw_shgroup(DRWShadingGroup *shgroup, DRWState pass_state)
draw_call_single_do(shgroup,
&state,
cmd->range.batch,
- (DRWResourceHandle)0,
+ cmd->range.handle,
cmd->range.vert_first,
cmd->range.vert_count,
+ 0,
1,
true);
break;
+ case DRW_CMD_DRAW_INSTANCE_RANGE:
+ draw_call_single_do(shgroup,
+ &state,
+ cmd->instance_range.batch,
+ cmd->instance_range.handle,
+ 0,
+ 0,
+ cmd->instance_range.inst_first,
+ cmd->instance_range.inst_count,
+ false);
+ break;
}
}
diff --git a/source/blender/draw/intern/shaders/common_globals_lib.glsl b/source/blender/draw/intern/shaders/common_globals_lib.glsl
index 6a3bf150095..cfadb87819c 100644
--- a/source/blender/draw/intern/shaders/common_globals_lib.glsl
+++ b/source/blender/draw/intern/shaders/common_globals_lib.glsl
@@ -32,6 +32,8 @@ layout(std140) uniform globalsBlock
vec4 colorFace;
vec4 colorFaceSelect;
vec4 colorFaceFreestyle;
+ vec4 colorGpencilVertex;
+ vec4 colorGpencilVertexSelect;
vec4 colorNormal;
vec4 colorVNormal;
vec4 colorLNormal;
diff --git a/source/blender/draw/intern/shaders/common_smaa_lib.glsl b/source/blender/draw/intern/shaders/common_smaa_lib.glsl
index 09b573d4bb5..45d9f54d943 100644
--- a/source/blender/draw/intern/shaders/common_smaa_lib.glsl
+++ b/source/blender/draw/intern/shaders/common_smaa_lib.glsl
@@ -692,6 +692,10 @@ void SMAANeighborhoodBlendingVS(float2 texcoord, out float4 offset)
//-----------------------------------------------------------------------------
// Edge Detection Pixel Shaders (First Pass)
+# ifndef SMAA_LUMA_WEIGHT
+# define SMAA_LUMA_WEIGHT float4(0.2126, 0.7152, 0.0722, 0.0)
+# endif
+
/**
* Luma Edge Detection
*
@@ -716,7 +720,8 @@ float2 SMAALumaEdgeDetectionPS(float2 texcoord,
# endif
// Calculate lumas:
- float4 weights = float4(0.2126 * 0.5, 0.7152 * 0.5, 0.0722 * 0.5, 0.5);
+ // float4 weights = float4(0.2126, 0.7152, 0.0722, 0.0);
+ float4 weights = SMAA_LUMA_WEIGHT;
float L = dot(SMAASamplePoint(colorTex, texcoord).rgba, weights);
float Lleft = dot(SMAASamplePoint(colorTex, offset[0].xy).rgba, weights);
@@ -727,9 +732,11 @@ float2 SMAALumaEdgeDetectionPS(float2 texcoord,
delta.xy = abs(L - float2(Lleft, Ltop));
float2 edges = step(threshold, delta.xy);
+# ifndef SMAA_NO_DISCARD
// Then discard if there is no edge:
if (dot(edges, float2(1.0, 1.0)) == 0.0)
discard;
+# endif
// Calculate right and bottom deltas:
float Lright = dot(SMAASamplePoint(colorTex, offset[1].xy).rgba, weights);
@@ -793,9 +800,11 @@ float2 SMAAColorEdgeDetectionPS(float2 texcoord,
// We do the usual threshold:
float2 edges = step(threshold, delta.xy);
+# ifndef SMAA_NO_DISCARD
// Then discard if there is no edge:
if (dot(edges, float2(1.0, 1.0)) == 0.0)
discard;
+# endif
// Calculate right and bottom deltas:
float3 Cright = SMAASamplePoint(colorTex, offset[1].xy).rgb;
diff --git a/source/blender/draw/intern/shaders/common_view_lib.glsl b/source/blender/draw/intern/shaders/common_view_lib.glsl
index 6605e1165d4..3faefd485bf 100644
--- a/source/blender/draw/intern/shaders/common_view_lib.glsl
+++ b/source/blender/draw/intern/shaders/common_view_lib.glsl
@@ -60,11 +60,11 @@ vec4 pack_line_data(vec2 frag_co, vec2 edge_start, vec2 edge_pos)
vec2 perp = vec2(-edge.y, edge.x);
float dist = dot(perp, frag_co - edge_start);
/* Add 0.1 to diffenrentiate with cleared pixels. */
- return vec4(perp * 0.5 + 0.5, dist * 0.25 + 0.5 + 0.1, 0.0);
+ return vec4(perp * 0.5 + 0.5, dist * 0.25 + 0.5 + 0.1, 1.0);
}
else {
/* Default line if the origin is perfectly aligned with a pixel. */
- return vec4(1.0, 0.0, 0.5 + 0.1, 0.0);
+ return vec4(1.0, 0.0, 0.5 + 0.1, 1.0);
}
}
@@ -89,7 +89,14 @@ uniform int baseInstance;
# define instanceId gl_InstanceID
# endif
-# define resource_id (baseInstance + instanceId)
+# ifdef UNIFORM_RESOURCE_ID
+/* This is in the case we want to do a special instance drawcall but still want to have the
+ * right resourceId and all the correct ubo datas. */
+uniform int resourceId;
+# define resource_id resourceId
+# else
+# define resource_id (baseInstance + instanceId)
+# endif
/* Use this to declare and pass the value if
* the fragment shader uses the resource_id. */