diff options
Diffstat (limited to 'source/blender/draw/intern')
-rw-r--r-- | source/blender/draw/intern/DRW_render.h | 22 | ||||
-rw-r--r-- | source/blender/draw/intern/draw_cache.c | 27 | ||||
-rw-r--r-- | source/blender/draw/intern/draw_cache.h | 17 | ||||
-rw-r--r-- | source/blender/draw/intern/draw_cache_impl_gpencil.c | 745 | ||||
-rw-r--r-- | source/blender/draw/intern/draw_common.c | 2 | ||||
-rw-r--r-- | source/blender/draw/intern/draw_common.h | 2 | ||||
-rw-r--r-- | source/blender/draw/intern/draw_manager.c | 34 | ||||
-rw-r--r-- | source/blender/draw/intern/draw_manager.h | 18 | ||||
-rw-r--r-- | source/blender/draw/intern/draw_manager_data.c | 65 | ||||
-rw-r--r-- | source/blender/draw/intern/draw_manager_exec.c | 91 | ||||
-rw-r--r-- | source/blender/draw/intern/shaders/common_globals_lib.glsl | 2 | ||||
-rw-r--r-- | source/blender/draw/intern/shaders/common_smaa_lib.glsl | 11 | ||||
-rw-r--r-- | source/blender/draw/intern/shaders/common_view_lib.glsl | 13 |
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. */ |