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:
Diffstat (limited to 'source/blender/draw/engines/gpencil/gpencil_engine.c')
-rw-r--r--source/blender/draw/engines/gpencil/gpencil_engine.c1725
1 files changed, 754 insertions, 971 deletions
diff --git a/source/blender/draw/engines/gpencil/gpencil_engine.c b/source/blender/draw/engines/gpencil/gpencil_engine.c
index 96560d986a0..80afbb5c7a3 100644
--- a/source/blender/draw/engines/gpencil/gpencil_engine.c
+++ b/source/blender/draw/engines/gpencil/gpencil_engine.c
@@ -29,11 +29,19 @@
#include "BKE_paint.h"
#include "BKE_shader_fx.h"
+#include "BKE_camera.h"
+#include "BKE_global.h" /* for G.debug */
+
+#include "BLI_link_utils.h"
+#include "BLI_memblock.h"
+
+#include "DNA_camera_types.h"
#include "DNA_gpencil_types.h"
#include "DNA_screen_types.h"
#include "DNA_view3d_types.h"
#include "GPU_texture.h"
+#include "GPU_uniformbuffer.h"
#include "gpencil_engine.h"
@@ -44,1129 +52,904 @@
#include "UI_resources.h"
-extern char datatoc_gpencil_fill_vert_glsl[];
-extern char datatoc_gpencil_fill_frag_glsl[];
-extern char datatoc_gpencil_stroke_vert_glsl[];
-extern char datatoc_gpencil_stroke_geom_glsl[];
-extern char datatoc_gpencil_stroke_frag_glsl[];
-extern char datatoc_gpencil_zdepth_mix_frag_glsl[];
-extern char datatoc_gpencil_simple_mix_frag_glsl[];
-extern char datatoc_gpencil_point_vert_glsl[];
-extern char datatoc_gpencil_point_geom_glsl[];
-extern char datatoc_gpencil_point_frag_glsl[];
-extern char datatoc_gpencil_background_frag_glsl[];
-extern char datatoc_gpencil_paper_frag_glsl[];
-extern char datatoc_gpencil_edit_point_vert_glsl[];
-extern char datatoc_gpencil_edit_point_geom_glsl[];
-extern char datatoc_gpencil_edit_point_frag_glsl[];
-extern char datatoc_gpencil_blend_frag_glsl[];
-
-extern char datatoc_gpu_shader_3D_smooth_color_frag_glsl[];
-
-extern char datatoc_common_colormanagement_lib_glsl[];
-extern char datatoc_common_view_lib_glsl[];
-
-/* *********** STATIC *********** */
-static GPENCIL_e_data e_data = {NULL}; /* Engine data */
-
/* *********** FUNCTIONS *********** */
-/* create a multisample buffer if not present */
-void gpencil_multisample_ensure(GPENCIL_Data *vedata, int rect_w, int rect_h)
+void GPENCIL_engine_init(void *ved)
{
+ GPENCIL_Data *vedata = (GPENCIL_Data *)ved;
+ GPENCIL_StorageList *stl = vedata->stl;
+ GPENCIL_TextureList *txl = vedata->txl;
GPENCIL_FramebufferList *fbl = vedata->fbl;
- GPENCIL_StorageList *stl = ((GPENCIL_Data *)vedata)->stl;
- GPENCIL_TextureList *txl = ((GPENCIL_Data *)vedata)->txl;
-
- short samples = stl->storage->multisamples;
-
- if (samples > 0) {
- if (!fbl->multisample_fb) {
- fbl->multisample_fb = GPU_framebuffer_create();
- if (fbl->multisample_fb) {
- if (txl->multisample_color == NULL) {
- txl->multisample_color = GPU_texture_create_2d_multisample(
- rect_w, rect_h, GPU_RGBA16F, NULL, samples, NULL);
- }
- if (txl->multisample_depth == NULL) {
- txl->multisample_depth = GPU_texture_create_2d_multisample(
- rect_w, rect_h, GPU_DEPTH24_STENCIL8, NULL, samples, NULL);
- }
- GPU_framebuffer_ensure_config(&fbl->multisample_fb,
- {GPU_ATTACHMENT_TEXTURE(txl->multisample_depth),
- GPU_ATTACHMENT_TEXTURE(txl->multisample_color)});
- }
- }
+ DefaultTextureList *dtxl = DRW_viewport_texture_list_get();
+ DefaultFramebufferList *dfbl = DRW_viewport_framebuffer_list_get();
+ const DRWContextState *ctx = DRW_context_state_get();
+ const View3D *v3d = ctx->v3d;
+
+ if (!stl->pd) {
+ stl->pd = MEM_callocN(sizeof(GPENCIL_PrivateData), "GPENCIL_PrivateData");
+ }
+
+ if (txl->dummy_texture == NULL) {
+ float pixels[1][4] = {{1.0f, 0.0f, 1.0f, 1.0f}};
+ txl->dummy_texture = DRW_texture_create_2d(1, 1, GPU_RGBA8, DRW_TEX_WRAP, (float *)pixels);
+ }
+
+ GPENCIL_ViewLayerData *vldata = GPENCIL_view_layer_data_ensure();
+
+ /* Resize and reset memblocks. */
+ BLI_memblock_clear(vldata->gp_light_pool, gpencil_light_pool_free);
+ BLI_memblock_clear(vldata->gp_material_pool, gpencil_material_pool_free);
+ BLI_memblock_clear(vldata->gp_object_pool, NULL);
+ BLI_memblock_clear(vldata->gp_layer_pool, NULL);
+ BLI_memblock_clear(vldata->gp_vfx_pool, NULL);
+ BLI_memblock_clear(vldata->gp_maskbit_pool, NULL);
+
+ stl->pd->gp_light_pool = vldata->gp_light_pool;
+ stl->pd->gp_material_pool = vldata->gp_material_pool;
+ stl->pd->gp_maskbit_pool = vldata->gp_maskbit_pool;
+ stl->pd->gp_object_pool = vldata->gp_object_pool;
+ stl->pd->gp_layer_pool = vldata->gp_layer_pool;
+ stl->pd->gp_vfx_pool = vldata->gp_vfx_pool;
+ stl->pd->scene = ctx->scene;
+ stl->pd->last_light_pool = NULL;
+ stl->pd->last_material_pool = NULL;
+ stl->pd->tobjects.first = NULL;
+ stl->pd->tobjects.last = NULL;
+ stl->pd->tobjects_infront.first = NULL;
+ stl->pd->tobjects_infront.last = NULL;
+ stl->pd->sbuffer_tobjects.first = NULL;
+ stl->pd->sbuffer_tobjects.last = NULL;
+ stl->pd->dummy_tx = txl->dummy_texture;
+ stl->pd->draw_depth_only = !DRW_state_is_fbo();
+ stl->pd->draw_wireframe = (v3d && v3d->shading.type == OB_WIRE) && !stl->pd->draw_depth_only;
+ stl->pd->scene_depth_tx = stl->pd->draw_depth_only ? txl->dummy_texture : dtxl->depth;
+ stl->pd->scene_fb = dfbl->default_fb;
+ stl->pd->is_render = txl->render_depth_tx || (v3d && v3d->shading.type == OB_RENDER);
+ stl->pd->is_viewport = (v3d != NULL);
+ stl->pd->global_light_pool = gpencil_light_pool_add(stl->pd);
+ stl->pd->shadeless_light_pool = gpencil_light_pool_add(stl->pd);
+ /* Small HACK: we don't want the global pool to be reused,
+ * so we set the last light pool to NULL. */
+ stl->pd->last_light_pool = NULL;
+
+ bool use_scene_lights = false;
+ bool use_scene_world = false;
+
+ if (v3d) {
+ use_scene_lights = ((v3d->shading.type == OB_MATERIAL) &&
+ (v3d->shading.flag & V3D_SHADING_SCENE_LIGHTS)) ||
+ ((v3d->shading.type == OB_RENDER) &&
+ (v3d->shading.flag & V3D_SHADING_SCENE_LIGHTS_RENDER));
+
+ use_scene_world = ((v3d->shading.type == OB_MATERIAL) &&
+ (v3d->shading.flag & V3D_SHADING_SCENE_WORLD)) ||
+ ((v3d->shading.type == OB_RENDER) &&
+ (v3d->shading.flag & V3D_SHADING_SCENE_WORLD_RENDER));
+
+ stl->pd->v3d_color_type = (v3d->shading.type == OB_SOLID) ? v3d->shading.color_type : -1;
+ /* Special case: If Vertex Paint mode, use always Vertex mode. */
+ if (v3d->shading.type == OB_SOLID && ctx->obact && ctx->obact->type == OB_GPENCIL &&
+ ctx->obact->mode == OB_MODE_VERTEX_GPENCIL) {
+ stl->pd->v3d_color_type = V3D_SHADING_VERTEX_COLOR;
+ }
+
+ copy_v3_v3(stl->pd->v3d_single_color, v3d->shading.single_color);
+
+ /* For non active frame, use only lines in multiedit mode. */
+ const bool overlays_on = (v3d->flag2 & V3D_HIDE_OVERLAYS) == 0;
+ stl->pd->use_multiedit_lines_only = !overlays_on ||
+ (v3d->gp_flag & V3D_GP_SHOW_MULTIEDIT_LINES) != 0;
+
+ const bool shmode_xray_support = v3d->shading.type <= OB_SOLID;
+ stl->pd->xray_alpha = (shmode_xray_support && XRAY_ENABLED(v3d)) ? XRAY_ALPHA(v3d) : 1.0f;
+ }
+ else if (stl->pd->is_render) {
+ use_scene_lights = true;
+ use_scene_world = true;
+ stl->pd->use_multiedit_lines_only = false;
+ stl->pd->xray_alpha = 1.0f;
+ stl->pd->v3d_color_type = -1;
+ }
+
+ stl->pd->use_lighting = (v3d && v3d->shading.type > OB_SOLID) || stl->pd->is_render;
+ stl->pd->use_lights = use_scene_lights;
+
+ if (txl->render_depth_tx != NULL) {
+ stl->pd->scene_depth_tx = txl->render_depth_tx;
+ stl->pd->scene_fb = fbl->render_fb;
+ }
+
+ gpencil_light_ambient_add(stl->pd->shadeless_light_pool, (float[3]){1.0f, 1.0f, 1.0f});
+
+ World *world = ctx->scene->world;
+ if (world != NULL && use_scene_world) {
+ gpencil_light_ambient_add(stl->pd->global_light_pool, &world->horr);
+ }
+ else if (v3d) {
+ float world_light[3];
+ copy_v3_fl(world_light, v3d->shading.studiolight_intensity);
+ gpencil_light_ambient_add(stl->pd->global_light_pool, world_light);
+ }
+
+ float viewmatinv[4][4];
+ DRW_view_viewmat_get(NULL, viewmatinv, true);
+ copy_v3_v3(stl->pd->camera_z_axis, viewmatinv[2]);
+ copy_v3_v3(stl->pd->camera_pos, viewmatinv[3]);
+ stl->pd->camera_z_offset = dot_v3v3(viewmatinv[3], viewmatinv[2]);
+
+ if (ctx && ctx->rv3d && v3d) {
+ stl->pd->camera = (ctx->rv3d->persp == RV3D_CAMOB) ? v3d->camera : NULL;
+ }
+ else {
+ stl->pd->camera = NULL;
}
}
-static void GPENCIL_create_framebuffers(void *vedata)
+void GPENCIL_cache_init(void *ved)
{
- GPENCIL_FramebufferList *fbl = ((GPENCIL_Data *)vedata)->fbl;
- GPENCIL_StorageList *stl = ((GPENCIL_Data *)vedata)->stl;
- GPENCIL_TextureList *txl = ((GPENCIL_Data *)vedata)->txl;
-
- /* Go full 32bits for rendering */
- eGPUTextureFormat fb_format = DRW_state_is_image_render() ? GPU_RGBA32F : GPU_RGBA16F;
-
- if (DRW_state_is_fbo()) {
- const float *viewport_size = DRW_viewport_size_get();
- const int size[2] = {(int)viewport_size[0], (int)viewport_size[1]};
-
- /* create multisample framebuffer for AA */
- if ((stl->storage->framebuffer_flag & GP_FRAMEBUFFER_MULTISAMPLE) &&
- (stl->storage->multisamples > 0)) {
- gpencil_multisample_ensure(vedata, size[0], size[1]);
- }
-
- /* Framebufers for basic object drawing */
- if (stl->storage->framebuffer_flag & GP_FRAMEBUFFER_BASIC) {
- /* temp textures for ping-pong buffers */
- stl->g_data->temp_depth_tx_a = DRW_texture_pool_query_2d(
- size[0], size[1], GPU_DEPTH24_STENCIL8, &draw_engine_gpencil_type);
- stl->g_data->temp_color_tx_a = DRW_texture_pool_query_2d(
- size[0], size[1], fb_format, &draw_engine_gpencil_type);
- GPU_framebuffer_ensure_config(&fbl->temp_fb_a,
- {
- GPU_ATTACHMENT_TEXTURE(stl->g_data->temp_depth_tx_a),
- GPU_ATTACHMENT_TEXTURE(stl->g_data->temp_color_tx_a),
- });
-
- stl->g_data->temp_depth_tx_b = DRW_texture_pool_query_2d(
- size[0], size[1], GPU_DEPTH24_STENCIL8, &draw_engine_gpencil_type);
- stl->g_data->temp_color_tx_b = DRW_texture_pool_query_2d(
- size[0], size[1], fb_format, &draw_engine_gpencil_type);
- GPU_framebuffer_ensure_config(&fbl->temp_fb_b,
- {
- GPU_ATTACHMENT_TEXTURE(stl->g_data->temp_depth_tx_b),
- GPU_ATTACHMENT_TEXTURE(stl->g_data->temp_color_tx_b),
- });
+ GPENCIL_Data *vedata = (GPENCIL_Data *)ved;
+ GPENCIL_PassList *psl = vedata->psl;
+ GPENCIL_TextureList *txl = vedata->txl;
+ GPENCIL_FramebufferList *fbl = vedata->fbl;
+ GPENCIL_PrivateData *pd = vedata->stl->pd;
+ DRWShadingGroup *grp;
- /* used for FX effects and Layer blending */
- stl->g_data->temp_depth_tx_fx = DRW_texture_pool_query_2d(
- size[0], size[1], GPU_DEPTH24_STENCIL8, &draw_engine_gpencil_type);
- stl->g_data->temp_color_tx_fx = DRW_texture_pool_query_2d(
- size[0], size[1], fb_format, &draw_engine_gpencil_type);
- GPU_framebuffer_ensure_config(&fbl->temp_fb_fx,
- {
- GPU_ATTACHMENT_TEXTURE(stl->g_data->temp_depth_tx_fx),
- GPU_ATTACHMENT_TEXTURE(stl->g_data->temp_color_tx_fx),
- });
- }
+ const DRWContextState *draw_ctx = DRW_context_state_get();
+ pd->cfra = (int)DEG_get_ctime(draw_ctx->depsgraph);
+ pd->simplify_antialias = GPENCIL_SIMPLIFY_AA(draw_ctx->scene);
+ pd->use_layer_fb = false;
+ pd->use_object_fb = false;
+ pd->use_mask_fb = false;
+ pd->use_signed_fb = false;
+
+ if (draw_ctx->v3d) {
+ const bool hide_overlay = ((draw_ctx->v3d->flag2 & V3D_HIDE_OVERLAYS) != 0);
+ const bool show_onion = ((draw_ctx->v3d->gp_flag & V3D_GP_SHOW_ONION_SKIN) != 0);
+ const bool playing = (draw_ctx->evil_C != NULL) ?
+ ED_screen_animation_playing(CTX_wm_manager(draw_ctx->evil_C)) !=
+ NULL :
+ false;
+ pd->do_onion = show_onion && !hide_overlay && !playing;
+ /* Save simplify flags (can change while drawing, so it's better to save). */
+ Scene *scene = draw_ctx->scene;
+ pd->simplify_fill = GPENCIL_SIMPLIFY_FILL(scene, playing);
+ pd->simplify_fx = GPENCIL_SIMPLIFY_FX(scene, playing) ||
+ (draw_ctx->v3d->shading.type < OB_RENDER);
+
+ /* Fade Layer. */
+ const bool is_fade_layer = ((!hide_overlay) && (!pd->is_render) &&
+ (draw_ctx->v3d->gp_flag & V3D_GP_FADE_NOACTIVE_LAYERS));
+ pd->fade_layer_opacity = (is_fade_layer) ? draw_ctx->v3d->overlay.gpencil_fade_layer : -1.0f;
+ /* Fade GPencil Objects. */
+ const bool is_fade_object = ((!hide_overlay) && (!pd->is_render) &&
+ (draw_ctx->v3d->gp_flag & V3D_GP_FADE_OBJECTS) &&
+ (draw_ctx->v3d->gp_flag & V3D_GP_FADE_NOACTIVE_GPENCIL));
+ pd->fade_gp_object_opacity = (is_fade_object) ? draw_ctx->v3d->overlay.gpencil_paper_opacity :
+ -1.0f;
+ pd->fade_3d_object_opacity = ((!hide_overlay) && (!pd->is_render) &&
+ (draw_ctx->v3d->gp_flag & V3D_GP_FADE_OBJECTS)) ?
+ draw_ctx->v3d->overlay.gpencil_paper_opacity :
+ -1.0f;
+ }
+ else {
+ pd->do_onion = true;
+ pd->simplify_fill = false;
+ pd->simplify_fx = false;
+ pd->fade_layer_opacity = -1.0f;
+ }
- /* background framebuffer to speed up drawing process */
- if (stl->storage->framebuffer_flag & GP_FRAMEBUFFER_DRAW) {
- if (txl->background_color_tx == NULL) {
- stl->storage->background_ready = false;
+ {
+ pd->sbuffer_stroke = NULL;
+ pd->sbuffer_gpd = NULL;
+ pd->sbuffer_layer = NULL;
+ pd->stroke_batch = NULL;
+ pd->fill_batch = NULL;
+ pd->do_fast_drawing = false;
+
+ pd->obact = draw_ctx->obact;
+ if (pd->obact && pd->obact->type == OB_GPENCIL) {
+ /* Check if active object has a temp stroke data. */
+ bGPdata *gpd = (bGPdata *)pd->obact->data;
+ if (gpd->runtime.sbuffer_used > 0) {
+ pd->sbuffer_gpd = gpd;
+ pd->sbuffer_stroke = DRW_cache_gpencil_sbuffer_stroke_data_get(pd->obact);
+ pd->sbuffer_layer = BKE_gpencil_layer_active_get(pd->sbuffer_gpd);
+ pd->do_fast_drawing = false; /* TODO option */
}
- DRW_texture_ensure_2d(
- &txl->background_depth_tx, size[0], size[1], GPU_DEPTH_COMPONENT24, DRW_TEX_FILTER);
- DRW_texture_ensure_2d(
- &txl->background_color_tx, size[0], size[1], GPU_RGBA16F, DRW_TEX_FILTER);
- GPU_framebuffer_ensure_config(&fbl->background_fb,
- {
- GPU_ATTACHMENT_TEXTURE(txl->background_depth_tx),
- GPU_ATTACHMENT_TEXTURE(txl->background_color_tx),
- });
- }
- else {
- DRW_TEXTURE_FREE_SAFE(txl->background_depth_tx);
- DRW_TEXTURE_FREE_SAFE(txl->background_color_tx);
}
}
-}
-static void GPENCIL_create_shaders(void)
-{
- /* blank texture used if no texture defined for fill shader */
- if (!e_data.gpencil_blank_texture) {
- float rect[1][1][4] = {{{0.0f}}};
- e_data.gpencil_blank_texture = DRW_texture_create_2d(
- 1, 1, GPU_RGBA8, DRW_TEX_FILTER, (float *)rect);
+ if (pd->do_fast_drawing) {
+ pd->snapshot_buffer_dirty = (txl->snapshot_color_tx == NULL);
+ const float *size = DRW_viewport_size_get();
+ DRW_texture_ensure_2d(&txl->snapshot_depth_tx, size[0], size[1], GPU_DEPTH24_STENCIL8, 0);
+ DRW_texture_ensure_2d(&txl->snapshot_color_tx, size[0], size[1], GPU_R11F_G11F_B10F, 0);
+ DRW_texture_ensure_2d(&txl->snapshot_reveal_tx, size[0], size[1], GPU_R11F_G11F_B10F, 0);
+
+ GPU_framebuffer_ensure_config(&fbl->snapshot_fb,
+ {
+ GPU_ATTACHMENT_TEXTURE(txl->snapshot_depth_tx),
+ GPU_ATTACHMENT_TEXTURE(txl->snapshot_color_tx),
+ GPU_ATTACHMENT_TEXTURE(txl->snapshot_reveal_tx),
+ });
}
- /* normal fill shader */
- if (!e_data.gpencil_fill_sh) {
- e_data.gpencil_fill_sh = GPU_shader_create_from_arrays({
- .vert =
- (const char *[]){datatoc_common_view_lib_glsl, datatoc_gpencil_fill_vert_glsl, NULL},
- .frag = (const char *[]){datatoc_common_colormanagement_lib_glsl,
- datatoc_gpencil_fill_frag_glsl,
- NULL},
- });
+ else {
+ /* Free uneeded buffers. */
+ GPU_FRAMEBUFFER_FREE_SAFE(fbl->snapshot_fb);
+ DRW_TEXTURE_FREE_SAFE(txl->snapshot_depth_tx);
+ DRW_TEXTURE_FREE_SAFE(txl->snapshot_color_tx);
+ DRW_TEXTURE_FREE_SAFE(txl->snapshot_reveal_tx);
}
- /* normal stroke shader using geometry to display lines (line mode) */
- if (!e_data.gpencil_stroke_sh) {
- e_data.gpencil_stroke_sh = GPU_shader_create_from_arrays({
- .vert =
- (const char *[]){datatoc_common_view_lib_glsl, datatoc_gpencil_stroke_vert_glsl, NULL},
- .geom = (const char *[]){datatoc_gpencil_stroke_geom_glsl, NULL},
- .frag = (const char *[]){datatoc_common_colormanagement_lib_glsl,
- datatoc_gpencil_stroke_frag_glsl,
- NULL},
- });
- }
+ {
+ DRWState state = DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS;
+ DRW_PASS_CREATE(psl->merge_depth_ps, state);
- /* dot/rectangle mode for normal strokes using geometry */
- if (!e_data.gpencil_point_sh) {
- e_data.gpencil_point_sh = GPU_shader_create_from_arrays({
- .vert =
- (const char *[]){datatoc_common_view_lib_glsl, datatoc_gpencil_point_vert_glsl, NULL},
- .geom = (const char *[]){datatoc_gpencil_point_geom_glsl, NULL},
- .frag = (const char *[]){datatoc_common_colormanagement_lib_glsl,
- datatoc_gpencil_point_frag_glsl,
- NULL},
- });
- }
- /* used for edit points or strokes with one point only */
- if (!e_data.gpencil_edit_point_sh) {
- e_data.gpencil_edit_point_sh = DRW_shader_create_with_lib(datatoc_gpencil_edit_point_vert_glsl,
- datatoc_gpencil_edit_point_geom_glsl,
- datatoc_gpencil_edit_point_frag_glsl,
- datatoc_common_view_lib_glsl,
- NULL);
+ GPUShader *sh = GPENCIL_shader_depth_merge_get();
+ grp = DRW_shgroup_create(sh, psl->merge_depth_ps);
+ DRW_shgroup_uniform_texture_ref(grp, "depthBuf", &pd->depth_tx);
+ DRW_shgroup_uniform_bool(grp, "strokeOrder3d", &pd->is_stroke_order_3d, 1);
+ DRW_shgroup_uniform_vec4(grp, "gpModelMatrix[0]", pd->object_bound_mat[0], 4);
+ DRW_shgroup_call_procedural_triangles(grp, NULL, 1);
}
+ {
+ DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_LOGIC_INVERT;
+ DRW_PASS_CREATE(psl->mask_invert_ps, state);
- /* used for edit lines for edit modes */
- if (!e_data.gpencil_line_sh) {
- e_data.gpencil_line_sh = DRW_shader_create_with_lib(
- datatoc_gpencil_edit_point_vert_glsl,
- NULL,
- datatoc_gpu_shader_3D_smooth_color_frag_glsl,
- datatoc_common_view_lib_glsl,
- NULL);
+ GPUShader *sh = GPENCIL_shader_mask_invert_get();
+ grp = DRW_shgroup_create(sh, psl->mask_invert_ps);
+ DRW_shgroup_call_procedural_triangles(grp, NULL, 1);
}
- /* used to filling during drawing */
- if (!e_data.gpencil_drawing_fill_sh) {
- e_data.gpencil_drawing_fill_sh = GPU_shader_get_builtin_shader(GPU_SHADER_3D_SMOOTH_COLOR);
- }
+ Camera *cam = (pd->camera != NULL) ? pd->camera->data : NULL;
- /* full screen for mix zdepth*/
- if (!e_data.gpencil_fullscreen_sh) {
- e_data.gpencil_fullscreen_sh = DRW_shader_create_fullscreen(
- datatoc_gpencil_zdepth_mix_frag_glsl, NULL);
- }
- if (!e_data.gpencil_simple_fullscreen_sh) {
- e_data.gpencil_simple_fullscreen_sh = DRW_shader_create_fullscreen(
- datatoc_gpencil_simple_mix_frag_glsl, NULL);
- }
+ /* Pseudo DOF setup. */
+ if (cam && (cam->dof.flag & CAM_DOF_ENABLED)) {
+ const float *vp_size = DRW_viewport_size_get();
+ float fstop = cam->dof.aperture_fstop;
+ float sensor = BKE_camera_sensor_size(cam->sensor_fit, cam->sensor_x, cam->sensor_y);
+ float focus_dist = BKE_camera_object_dof_distance(pd->camera);
+ float focal_len = cam->lens;
- /* blend */
- if (!e_data.gpencil_blend_fullscreen_sh) {
- e_data.gpencil_blend_fullscreen_sh = DRW_shader_create_fullscreen(
- datatoc_gpencil_blend_frag_glsl, NULL);
- }
+ const float scale_camera = 0.001f;
+ /* we want radius here for the aperture number */
+ float aperture = 0.5f * scale_camera * focal_len / fstop;
+ float focal_len_scaled = scale_camera * focal_len;
+ float sensor_scaled = scale_camera * sensor;
- /* shaders for use when drawing */
- if (!e_data.gpencil_background_sh) {
- e_data.gpencil_background_sh = DRW_shader_create_fullscreen(
- datatoc_gpencil_background_frag_glsl, NULL);
+ if (draw_ctx->rv3d != NULL) {
+ sensor_scaled *= draw_ctx->rv3d->viewcamtexcofac[0];
+ }
+
+ pd->dof_params[1] = aperture * fabsf(focal_len_scaled / (focus_dist - focal_len_scaled));
+ pd->dof_params[1] *= vp_size[0] / sensor_scaled;
+ pd->dof_params[0] = -focus_dist * pd->dof_params[1];
}
- if (!e_data.gpencil_paper_sh) {
- e_data.gpencil_paper_sh = DRW_shader_create_fullscreen(datatoc_gpencil_paper_frag_glsl, NULL);
+ else {
+ /* Disable DoF blur scalling. */
+ pd->camera = NULL;
}
}
-void GPENCIL_engine_init(void *vedata)
+#define DRAW_NOW 2
+
+typedef struct gpIterPopulateData {
+ Object *ob;
+ GPENCIL_tObject *tgp_ob;
+ GPENCIL_PrivateData *pd;
+ GPENCIL_MaterialPool *matpool;
+ DRWShadingGroup *grp;
+ /* Last material UBO bound. Used to avoid uneeded buffer binding. */
+ GPUUniformBuffer *ubo_mat;
+ GPUUniformBuffer *ubo_lights;
+ /* Last texture bound. */
+ GPUTexture *tex_fill;
+ GPUTexture *tex_stroke;
+ /* Offset in the material pool to the first material of this object. */
+ int mat_ofs;
+ /* Is the sbuffer call need to be issued. */
+ int do_sbuffer_call;
+ /* Indices to do correct insertion of the sbuffer stroke. */
+ int stroke_index_last;
+ int stroke_index_offset;
+ /* Infos for call batching. */
+ struct GPUBatch *geom;
+ bool instancing;
+ int vfirst, vcount;
+} gpIterPopulateData;
+
+#define DISABLE_BATCHING 0
+
+static void gpencil_drawcall_flush(gpIterPopulateData *iter)
{
- GPENCIL_StorageList *stl = ((GPENCIL_Data *)vedata)->stl;
- /* init storage */
- if (!stl->storage) {
- stl->storage = MEM_callocN(sizeof(GPENCIL_Storage), "GPENCIL_Storage");
- stl->storage->shade_render[0] = OB_RENDER;
- stl->storage->shade_render[1] = 0;
+#if !DISABLE_BATCHING
+ if (iter->geom != NULL) {
+ if (iter->instancing) {
+ DRW_shgroup_call_instance_range(iter->grp, iter->ob, iter->geom, iter->vfirst, iter->vcount);
+ }
+ else {
+ DRW_shgroup_call_range(iter->grp, iter->ob, iter->geom, iter->vfirst, iter->vcount);
+ }
}
+#endif
- /* Not supported anymore. */
- stl->storage->multisamples = 0;
-
- /* create shaders */
- GPENCIL_create_shaders();
- GPENCIL_create_fx_shaders(&e_data);
+ iter->geom = NULL;
+ iter->vfirst = -1;
+ iter->vcount = 0;
}
-static void GPENCIL_engine_free(void)
+/* Group drawcalls that are consecutive and with the same type. Reduces GPU driver overhead. */
+static void gp_drawcall_add(
+ gpIterPopulateData *iter, struct GPUBatch *geom, bool instancing, int v_first, int v_count)
{
- /* only free custom shaders, builtin shaders are freed in blender close */
- DRW_SHADER_FREE_SAFE(e_data.gpencil_fill_sh);
- DRW_SHADER_FREE_SAFE(e_data.gpencil_stroke_sh);
- DRW_SHADER_FREE_SAFE(e_data.gpencil_point_sh);
- DRW_SHADER_FREE_SAFE(e_data.gpencil_edit_point_sh);
- DRW_SHADER_FREE_SAFE(e_data.gpencil_line_sh);
- DRW_SHADER_FREE_SAFE(e_data.gpencil_fullscreen_sh);
- DRW_SHADER_FREE_SAFE(e_data.gpencil_simple_fullscreen_sh);
- DRW_SHADER_FREE_SAFE(e_data.gpencil_blend_fullscreen_sh);
- DRW_SHADER_FREE_SAFE(e_data.gpencil_background_sh);
- DRW_SHADER_FREE_SAFE(e_data.gpencil_paper_sh);
-
- DRW_TEXTURE_FREE_SAFE(e_data.gpencil_blank_texture);
-
- /* effects */
- GPENCIL_delete_fx_shaders(&e_data);
+#if DISABLE_BATCHING
+ if (instancing) {
+ DRW_shgroup_call_instance_range(iter->grp, iter->ob, geom, v_first, v_count);
+ }
+ else {
+ DRW_shgroup_call_range(iter->grp, iter->ob, geom, v_first, v_count);
+ }
+#endif
+
+ int last = iter->vfirst + iter->vcount;
+ /* Interupt drawcall grouping if the sequence is not consecutive. */
+ if ((geom != iter->geom) || (v_first - last > 3)) {
+ gpencil_drawcall_flush(iter);
+ }
+ iter->geom = geom;
+ iter->instancing = instancing;
+ if (iter->vfirst == -1) {
+ iter->vfirst = v_first;
+ }
+ iter->vcount = v_first + v_count - iter->vfirst;
}
-/* Helper: Check if the main overlay and onion switches are enabled in any screen.
- *
- * This is required to generate the onion skin and limit the times the cache is updated because the
- * cache is generated only in the first screen and if the first screen has the onion disabled the
- * cache for onion skin is not generated. The loop adds time, but always is faster than regenerate
- * the cache all the times.
- */
-static void gpencil_check_screen_switches(const DRWContextState *draw_ctx,
- GPENCIL_StorageList *stl)
+static void gpencil_stroke_cache_populate(bGPDlayer *gpl,
+ bGPDframe *gpf,
+ bGPDstroke *gps,
+ void *thunk);
+
+static void gp_sbuffer_cache_populate(gpIterPopulateData *iter)
{
- stl->storage->is_main_overlay = false;
- stl->storage->is_main_onion = false;
- /* Check if main onion switch is enabled in any screen. */
- Main *bmain = CTX_data_main(draw_ctx->evil_C);
-
- for (bScreen *screen = bmain->screens.first; screen; screen = screen->id.next) {
- for (const ScrArea *sa = screen->areabase.first; sa; sa = sa->next) {
- if (sa && sa->spacetype == SPACE_VIEW3D) {
- View3D *v3d = sa->spacedata.first;
- if (v3d == NULL) {
- continue;
- }
- if ((v3d->flag2 & V3D_HIDE_OVERLAYS) == 0) {
- stl->storage->is_main_overlay = true;
- }
- if (v3d->gp_flag & V3D_GP_SHOW_ONION_SKIN) {
- stl->storage->is_main_onion = true;
- }
- }
- /* If found, don't need loop more. */
- if ((stl->storage->is_main_overlay) && (stl->storage->is_main_onion)) {
- return;
- }
- }
- }
+ iter->do_sbuffer_call = DRAW_NOW;
+ /* In order to draw the sbuffer stroke correctly mixed with other strokes,
+ * we need to offset the stroke index of the sbuffer stroke and the subsequent strokes.
+ * Remember, sbuffer stroke indices start from 0. So we add last index to avoid
+ * masking issues. */
+ iter->grp = DRW_shgroup_create_sub(iter->grp);
+ DRW_shgroup_uniform_block(iter->grp, "gpMaterialBlock", iter->ubo_mat);
+ DRW_shgroup_uniform_float_copy(iter->grp, "strokeIndexOffset", iter->stroke_index_last);
+
+ const DRWContextState *ctx = DRW_context_state_get();
+ ToolSettings *ts = ctx->scene->toolsettings;
+ if (ts->gpencil_v3d_align & (GP_PROJECT_DEPTH_VIEW | GP_PROJECT_DEPTH_STROKE)) {
+ /* In this case we can't do correct projection during stroke. We just disable depth test. */
+ DRW_shgroup_uniform_texture(iter->grp, "gpSceneDepthTexture", iter->pd->dummy_tx);
+ }
+
+ gpencil_stroke_cache_populate(NULL, NULL, iter->pd->sbuffer_stroke, iter);
+ gpencil_drawcall_flush(iter);
+
+ iter->stroke_index_offset = iter->pd->sbuffer_stroke->totpoints + 1;
+ iter->do_sbuffer_call = 0;
}
-void GPENCIL_cache_init(void *vedata)
+static void gpencil_layer_cache_populate(bGPDlayer *gpl,
+ bGPDframe *gpf,
+ bGPDstroke *UNUSED(gps),
+ void *thunk)
{
- GPENCIL_PassList *psl = ((GPENCIL_Data *)vedata)->psl;
- GPENCIL_StorageList *stl = ((GPENCIL_Data *)vedata)->stl;
- GPENCIL_TextureList *txl = ((GPENCIL_Data *)vedata)->txl;
- const DRWContextState *draw_ctx = DRW_context_state_get();
- Scene *scene = draw_ctx->scene;
- ToolSettings *ts = scene->toolsettings;
- View3D *v3d = draw_ctx->v3d;
- Brush *brush = BKE_paint_brush(&ts->gp_paint->paint);
- const View3DCursor *cursor = &scene->cursor;
-
- /* Special handling for when active object is GP object (e.g. for draw mode) */
- Object *obact = draw_ctx->obact;
- bGPdata *obact_gpd = NULL;
- MaterialGPencilStyle *gp_style = NULL;
-
- if (obact && (obact->type == OB_GPENCIL) && (obact->data)) {
- obact_gpd = (bGPdata *)obact->data;
- /* use the brush material */
- Material *ma = BKE_gpencil_object_material_get_from_brush(obact, brush);
- if (ma != NULL) {
- gp_style = ma->gp_style;
- }
- /* this is not common, but avoid any special situations when brush could be without material */
- if (gp_style == NULL) {
- gp_style = BKE_gpencil_material_settings(obact, obact->actcol);
- }
- }
+ gpIterPopulateData *iter = (gpIterPopulateData *)thunk;
+ GPENCIL_PrivateData *pd = iter->pd;
+ bGPdata *gpd = (bGPdata *)iter->ob->data;
- if (!stl->g_data) {
- /* Alloc transient pointers */
- stl->g_data = MEM_mallocN(sizeof(g_data), "g_data");
- stl->storage->xray = GP_XRAY_FRONT; /* used for drawing */
- stl->storage->stroke_style = GP_STYLE_STROKE_STYLE_SOLID; /* used for drawing */
+ gpencil_drawcall_flush(iter);
+
+ if (iter->do_sbuffer_call) {
+ gp_sbuffer_cache_populate(iter);
}
- stl->storage->tonemapping = 0;
-
- stl->g_data->shgrps_edit_line = NULL;
- stl->g_data->shgrps_edit_point = NULL;
-
- /* reset textures */
- stl->g_data->batch_buffer_stroke = NULL;
- stl->g_data->batch_buffer_fill = NULL;
- stl->g_data->batch_buffer_ctrlpoint = NULL;
- stl->g_data->batch_grid = NULL;
-
- if (!stl->shgroups) {
- /* Alloc maximum size because count strokes is very slow and can be very complex due onion
- * skinning.
- */
- stl->shgroups = MEM_mallocN(sizeof(GPENCIL_shgroup) * GPENCIL_MAX_SHGROUPS, "GPENCIL_shgroup");
+ else {
+ iter->do_sbuffer_call = !pd->do_fast_drawing && (gpd == pd->sbuffer_gpd) &&
+ (gpl == pd->sbuffer_layer) &&
+ (gpf == NULL || gpf->runtime.onion_id == 0.0f);
}
- /* init gp objects cache */
- stl->g_data->gp_cache_used = 0;
- stl->g_data->gp_cache_size = 0;
- stl->g_data->gp_object_cache = NULL;
- stl->g_data->do_instances = false;
+ GPENCIL_tLayer *tgp_layer = gpencil_layer_cache_add(pd, iter->ob, gpl, gpf, iter->tgp_ob);
- {
- /* Stroke pass 2D */
- psl->stroke_pass_2d = DRW_pass_create("GPencil Stroke Pass",
- DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH |
- DRW_STATE_DEPTH_ALWAYS | DRW_STATE_BLEND_ALPHA |
- DRW_STATE_WRITE_STENCIL | DRW_STATE_STENCIL_NEQUAL);
- stl->storage->shgroup_id = 0;
- /* Stroke pass 3D */
- psl->stroke_pass_3d = DRW_pass_create("GPencil Stroke Pass",
- DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH |
- DRW_STATE_DEPTH_LESS_EQUAL | DRW_STATE_BLEND_ALPHA |
- DRW_STATE_WRITE_STENCIL | DRW_STATE_STENCIL_NEQUAL);
- stl->storage->shgroup_id = 0;
-
- /* edit pass */
- psl->edit_pass = DRW_pass_create("GPencil Edit Pass",
- DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_ALPHA);
-
- /* detect if playing animation */
- if (draw_ctx->evil_C) {
-
- bool playing = ED_screen_animation_playing(CTX_wm_manager(draw_ctx->evil_C)) != NULL;
- if (playing != stl->storage->is_playing) {
- stl->storage->reset_cache = true;
- }
- stl->storage->is_playing = playing;
+ const bool use_lights = pd->use_lighting && ((gpl->flag & GP_LAYER_USE_LIGHTS) != 0) &&
+ (iter->ob->dtx & OB_USE_GPENCIL_LIGHTS);
- /* Found if main overlay and onion switches are enabled in any screen. */
- gpencil_check_screen_switches(draw_ctx, stl);
- }
- else {
- stl->storage->is_playing = false;
- stl->storage->reset_cache = false;
- stl->storage->is_main_overlay = false;
- stl->storage->is_main_onion = false;
- }
- /* save render state */
- stl->storage->is_render = DRW_state_is_scene_render();
- stl->storage->is_mat_preview = (bool)stl->storage->is_render &&
- STREQ(scene->id.name + 2, "preview");
-
- if (obact_gpd) {
- /* for some reason, when press play there is a delay in the animation flag check
- * and this produces errors. To be sure, we set cache as dirty because the frame
- * is changing.
- */
- if (stl->storage->is_playing == true) {
- obact_gpd->flag |= GP_DATA_CACHE_IS_DIRTY;
- }
- /* if render, set as dirty to update all data */
- else if (stl->storage->is_render == true) {
- obact_gpd->flag |= GP_DATA_CACHE_IS_DIRTY;
- }
- }
+ iter->ubo_lights = (use_lights) ? pd->global_light_pool->ubo : pd->shadeless_light_pool->ubo;
- /* save simplify flags (can change while drawing, so it's better to save) */
- stl->storage->simplify_fill = GPENCIL_SIMPLIFY_FILL(scene, stl->storage->is_playing);
- stl->storage->simplify_modif = GPENCIL_SIMPLIFY_MODIF(scene, stl->storage->is_playing);
- stl->storage->simplify_fx = GPENCIL_SIMPLIFY_FX(scene, stl->storage->is_playing);
- stl->storage->simplify_blend = GPENCIL_SIMPLIFY_BLEND(scene, stl->storage->is_playing);
+ gpencil_material_resources_get(iter->matpool, 0, NULL, NULL, &iter->ubo_mat);
- /* xray mode */
- if (v3d) {
- stl->storage->is_xray = XRAY_ACTIVE(v3d);
- }
- else {
- stl->storage->is_xray = 0;
- }
+ /* Iterator dependent uniforms. */
+ DRWShadingGroup *grp = iter->grp = tgp_layer->base_shgrp;
+ DRW_shgroup_uniform_block_persistent(grp, "gpLightBlock", iter->ubo_lights);
+ DRW_shgroup_uniform_block(grp, "gpMaterialBlock", iter->ubo_mat);
+ DRW_shgroup_uniform_texture(grp, "gpFillTexture", iter->tex_fill);
+ DRW_shgroup_uniform_texture(grp, "gpStrokeTexture", iter->tex_stroke);
+ DRW_shgroup_uniform_int_copy(grp, "gpMaterialOffset", iter->mat_ofs);
+ DRW_shgroup_uniform_float_copy(grp, "strokeIndexOffset", iter->stroke_index_offset);
+}
- /* save pixsize */
- stl->storage->pixsize = DRW_viewport_pixelsize_get();
- if ((!DRW_state_is_opengl_render()) && (stl->storage->is_render)) {
- stl->storage->pixsize = &stl->storage->render_pixsize;
- }
+static void gpencil_stroke_cache_populate(bGPDlayer *gpl,
+ bGPDframe *gpf,
+ bGPDstroke *gps,
+ void *thunk)
+{
+ gpIterPopulateData *iter = (gpIterPopulateData *)thunk;
- /* detect if painting session */
- if ((obact_gpd) && (obact_gpd->flag & GP_DATA_STROKE_PAINTMODE) &&
- (stl->storage->is_playing == false)) {
- /* need the original to avoid cow overhead while drawing */
- bGPdata *gpd_orig = (bGPdata *)DEG_get_original_id(&obact_gpd->id);
- if (((gpd_orig->runtime.sbuffer_sflag & GP_STROKE_ERASER) == 0) &&
- (gpd_orig->runtime.sbuffer_used > 0) &&
- ((gpd_orig->flag & GP_DATA_STROKE_POLYGON) == 0) && !DRW_state_is_depth() &&
- (stl->storage->background_ready == true)) {
- stl->g_data->session_flag |= GP_DRW_PAINT_PAINTING;
- }
- else {
- stl->g_data->session_flag = GP_DRW_PAINT_IDLE;
- }
- }
- else {
- /* if not drawing mode */
- stl->g_data->session_flag = GP_DRW_PAINT_HOLD;
- }
+ MaterialGPencilStyle *gp_style = BKE_gpencil_material_settings(iter->ob, gps->mat_nr + 1);
- if (gp_style) {
- stl->storage->stroke_style = gp_style->stroke_style;
- stl->storage->color_type = GPENCIL_COLOR_SOLID;
- if (gp_style->stroke_style == GP_STYLE_STROKE_STYLE_TEXTURE) {
- stl->storage->color_type = GPENCIL_COLOR_TEXTURE;
- if (gp_style->flag & GP_STYLE_STROKE_PATTERN) {
- stl->storage->color_type = GPENCIL_COLOR_PATTERN;
- }
- }
+ bool hide_material = (gp_style->flag & GP_MATERIAL_HIDE) != 0;
+ bool show_stroke = (gp_style->flag & GP_MATERIAL_STROKE_SHOW) != 0;
+ bool show_fill = (gps->tot_triangles > 0) && ((gp_style->flag & GP_MATERIAL_FILL_SHOW) != 0) &&
+ (!iter->pd->simplify_fill) && ((gps->flag & GP_STROKE_NOFILL) == 0);
+
+ bool only_lines = gpl && gpf && gpl->actframe != gpf && iter->pd->use_multiedit_lines_only;
+
+ if (hide_material || (!show_stroke && !show_fill) || only_lines) {
+ return;
+ }
+
+ GPUUniformBuffer *ubo_mat;
+ GPUTexture *tex_stroke, *tex_fill;
+ gpencil_material_resources_get(
+ iter->matpool, iter->mat_ofs + gps->mat_nr, &tex_stroke, &tex_fill, &ubo_mat);
+
+ bool resource_changed = (iter->ubo_mat != ubo_mat) ||
+ (tex_fill && (iter->tex_fill != tex_fill)) ||
+ (tex_stroke && (iter->tex_stroke != tex_stroke));
+
+ if (resource_changed) {
+ gpencil_drawcall_flush(iter);
+
+ iter->grp = DRW_shgroup_create_sub(iter->grp);
+ if (iter->ubo_mat != ubo_mat) {
+ DRW_shgroup_uniform_block(iter->grp, "gpMaterialBlock", ubo_mat);
+ iter->ubo_mat = ubo_mat;
}
- else {
- stl->storage->stroke_style = GP_STYLE_STROKE_STYLE_SOLID;
- stl->storage->color_type = GPENCIL_COLOR_SOLID;
+ if (tex_fill) {
+ DRW_shgroup_uniform_texture(iter->grp, "gpFillTexture", tex_fill);
+ iter->tex_fill = tex_fill;
}
-
- /* drawing buffer pass for drawing the stroke that is being drawing by the user. The data
- * is stored in sbuffer
- */
- psl->drawing_pass = DRW_pass_create("GPencil Drawing Pass",
- DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_ALPHA |
- DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_ALWAYS |
- DRW_STATE_WRITE_STENCIL | DRW_STATE_STENCIL_NEQUAL);
-
- /* full screen pass to combine the result with default framebuffer */
- struct GPUBatch *quad = DRW_cache_fullscreen_quad_get();
- psl->mix_pass = DRW_pass_create("GPencil Mix Pass",
- DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_ALPHA |
- DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS);
- DRWShadingGroup *mix_shgrp = DRW_shgroup_create(e_data.gpencil_fullscreen_sh, psl->mix_pass);
- DRW_shgroup_call(mix_shgrp, quad, NULL);
- DRW_shgroup_uniform_texture_ref(mix_shgrp, "strokeColor", &stl->g_data->input_color_tx);
- DRW_shgroup_uniform_texture_ref(mix_shgrp, "strokeDepth", &stl->g_data->input_depth_tx);
- DRW_shgroup_uniform_int(mix_shgrp, "tonemapping", &stl->storage->tonemapping, 1);
- DRW_shgroup_uniform_int(mix_shgrp, "do_select", &stl->storage->do_select_outline, 1);
- DRW_shgroup_uniform_vec4(mix_shgrp, "select_color", stl->storage->select_color, 1);
-
- /* Mix pass no blend used to copy between passes. A separated pass is required
- * because if mix_pass is used, the accumulation of blend degrade the colors.
- *
- * This pass is used too to take the snapshot used for background_pass. This image
- * will be used as the background while the user is drawing.
- */
- psl->mix_pass_noblend = DRW_pass_create("GPencil Mix Pass no blend",
- DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH |
- DRW_STATE_DEPTH_LESS);
- DRWShadingGroup *mix_shgrp_noblend = DRW_shgroup_create(e_data.gpencil_fullscreen_sh,
- psl->mix_pass_noblend);
- DRW_shgroup_call(mix_shgrp_noblend, quad, NULL);
- DRW_shgroup_uniform_texture_ref(
- mix_shgrp_noblend, "strokeColor", &stl->g_data->input_color_tx);
- DRW_shgroup_uniform_texture_ref(
- mix_shgrp_noblend, "strokeDepth", &stl->g_data->input_depth_tx);
- DRW_shgroup_uniform_int(mix_shgrp_noblend, "tonemapping", &stl->storage->tonemapping, 1);
- DRW_shgroup_uniform_int(mix_shgrp_noblend, "do_select", &stl->storage->do_select_outline, 1);
- DRW_shgroup_uniform_vec4(mix_shgrp_noblend, "select_color", stl->storage->select_color, 1);
-
- /* Painting session pass (used only to speedup while the user is drawing )
- * This pass is used to show the snapshot of the current grease pencil strokes captured
- * when the user starts to draw (see comments above).
- * In this way, the previous strokes don't need to be redraw and the drawing process
- * is far to agile.
- */
- psl->background_pass = DRW_pass_create("GPencil Background Painting Session Pass",
- DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_ALPHA |
- DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS);
- DRWShadingGroup *background_shgrp = DRW_shgroup_create(e_data.gpencil_background_sh,
- psl->background_pass);
- DRW_shgroup_call(background_shgrp, quad, NULL);
- DRW_shgroup_uniform_texture_ref(background_shgrp, "strokeColor", &txl->background_color_tx);
- DRW_shgroup_uniform_texture_ref(background_shgrp, "strokeDepth", &txl->background_depth_tx);
-
- /* pass for drawing paper (only if viewport)
- * In render, the v3d is null so the paper is disabled
- * The paper is way to isolate the drawing in complex scene and to have a cleaner
- * drawing area.
- */
- if (v3d) {
- psl->paper_pass = DRW_pass_create("GPencil Paper Pass",
- DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_ALPHA);
- DRWShadingGroup *paper_shgrp = DRW_shgroup_create(e_data.gpencil_paper_sh, psl->paper_pass);
- DRW_shgroup_call(paper_shgrp, quad, NULL);
- DRW_shgroup_uniform_vec3(paper_shgrp, "color", v3d->shading.background_color, 1);
- DRW_shgroup_uniform_float(paper_shgrp, "opacity", &v3d->overlay.gpencil_paper_opacity, 1);
+ if (tex_stroke) {
+ DRW_shgroup_uniform_texture(iter->grp, "gpStrokeTexture", tex_stroke);
+ iter->tex_stroke = tex_stroke;
}
- /* grid pass */
- if ((v3d) && (obact) && (obact->type == OB_GPENCIL)) {
- psl->grid_pass = DRW_pass_create("GPencil Grid Pass",
- DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_ALPHA |
- DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_ALWAYS);
- stl->g_data->shgrps_grid = DRW_shgroup_create(e_data.gpencil_line_sh, psl->grid_pass);
-
- /* define grid orientation */
- switch (ts->gp_sculpt.lock_axis) {
- case GP_LOCKAXIS_VIEW: {
- /* align always to view */
- invert_m4_m4(stl->storage->grid_matrix, draw_ctx->rv3d->viewmat);
- /* copy ob location */
- copy_v3_v3(stl->storage->grid_matrix[3], obact->obmat[3]);
- break;
- }
- case GP_LOCKAXIS_CURSOR: {
- float scale[3] = {1.0f, 1.0f, 1.0f};
- loc_eul_size_to_mat4(
- stl->storage->grid_matrix, cursor->location, cursor->rotation_euler, scale);
- break;
- }
- default: {
- copy_m4_m4(stl->storage->grid_matrix, obact->obmat);
- break;
- }
- }
+ /* TODO(fclem): This is a quick workaround but
+ * ideally we should have this as a permanent bind. */
+ const bool is_masked = iter->tgp_ob->layers.last->mask_bits != NULL;
+ GPUTexture **mask_tex = (is_masked) ? &iter->pd->mask_tx : &iter->pd->dummy_tx;
+ DRW_shgroup_uniform_texture_ref(iter->grp, "gpMaskTexture", mask_tex);
+ }
- /* Move the origin to Object or Cursor */
- if (ts->gpencil_v3d_align & GP_PROJECT_CURSOR) {
- copy_v3_v3(stl->storage->grid_matrix[3], cursor->location);
- }
- else {
- copy_v3_v3(stl->storage->grid_matrix[3], obact->obmat[3]);
- }
- DRW_shgroup_uniform_mat4(
- stl->g_data->shgrps_grid, "gpModelMatrix", stl->storage->grid_matrix);
- }
+ bool do_sbuffer = (iter->do_sbuffer_call == DRAW_NOW);
- /* blend layers pass */
- psl->blend_pass = DRW_pass_create("GPencil Blend Layers Pass",
- DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_ALPHA |
- DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS);
- DRWShadingGroup *blend_shgrp = DRW_shgroup_create(e_data.gpencil_blend_fullscreen_sh,
- psl->blend_pass);
- DRW_shgroup_call(blend_shgrp, quad, NULL);
- DRW_shgroup_uniform_texture_ref(blend_shgrp, "strokeColor", &stl->g_data->temp_color_tx_a);
- DRW_shgroup_uniform_texture_ref(blend_shgrp, "strokeDepth", &stl->g_data->temp_depth_tx_a);
- DRW_shgroup_uniform_texture_ref(blend_shgrp, "blendColor", &stl->g_data->temp_color_tx_fx);
- DRW_shgroup_uniform_texture_ref(blend_shgrp, "blendDepth", &stl->g_data->temp_depth_tx_fx);
- DRW_shgroup_uniform_int(blend_shgrp, "mode", &stl->storage->blend_mode, 1);
- DRW_shgroup_uniform_int(blend_shgrp, "mask_layer", &stl->storage->mask_layer, 1);
- DRW_shgroup_uniform_int(mix_shgrp, "tonemapping", &stl->storage->tonemapping, 1);
-
- /* create effects passes */
- if (!stl->storage->simplify_fx) {
- GPENCIL_create_fx_passes(psl);
- }
+ if (show_fill) {
+ GPUBatch *geom = do_sbuffer ? DRW_cache_gpencil_sbuffer_fill_get(iter->ob) :
+ DRW_cache_gpencil_fills_get(iter->ob, iter->pd->cfra);
+ int vfirst = gps->runtime.fill_start * 3;
+ int vcount = gps->tot_triangles * 3;
+ gp_drawcall_add(iter, geom, false, vfirst, vcount);
}
+
+ if (show_stroke) {
+ GPUBatch *geom = do_sbuffer ? DRW_cache_gpencil_sbuffer_stroke_get(iter->ob) :
+ DRW_cache_gpencil_strokes_get(iter->ob, iter->pd->cfra);
+ /* Start one vert before to have gl_InstanceID > 0 (see shader). */
+ int vfirst = gps->runtime.stroke_start - 1;
+ /* Include "potential" cyclic vertex and start adj vertex (see shader). */
+ int vcount = gps->totpoints + 1 + 1;
+ gp_drawcall_add(iter, geom, true, vfirst, vcount);
+ }
+
+ iter->stroke_index_last = gps->runtime.stroke_start + gps->totpoints + 1;
}
-static void gpencil_add_draw_data(void *vedata, Object *ob)
+static void gp_sbuffer_cache_populate_fast(GPENCIL_Data *vedata, gpIterPopulateData *iter)
{
- GPENCIL_StorageList *stl = ((GPENCIL_Data *)vedata)->stl;
- bGPdata *gpd = (bGPdata *)ob->data;
- const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd);
- const DRWContextState *draw_ctx = DRW_context_state_get();
- const View3D *v3d = draw_ctx->v3d;
+ bGPdata *gpd = (bGPdata *)iter->ob->data;
+ if (gpd != iter->pd->sbuffer_gpd) {
+ return;
+ }
- int i = stl->g_data->gp_cache_used - 1;
- tGPencilObjectCache *cache_ob = &stl->g_data->gp_object_cache[i];
+ GPENCIL_TextureList *txl = vedata->txl;
+ GPUTexture *depth_texture = iter->pd->scene_depth_tx;
+ GPENCIL_tObject *last_tgp_ob = iter->pd->tobjects.last;
+ /* Create another temp object that only contain the stroke. */
+ iter->tgp_ob = gpencil_object_cache_add(iter->pd, iter->ob);
+ /* Remove from the main list. */
+ iter->pd->tobjects.last = last_tgp_ob;
+ last_tgp_ob->next = NULL;
+ /* Add to sbuffer tgpobject list. */
+ BLI_LINKS_APPEND(&iter->pd->sbuffer_tobjects, iter->tgp_ob);
+ /* Remove depth test with scene (avoid self occlusion). */
+ iter->pd->scene_depth_tx = txl->dummy_texture;
- if (!cache_ob->is_dup_ob) {
- /* fill shading groups */
- if ((!is_multiedit) || (stl->storage->is_render)) {
- gpencil_populate_datablock(&e_data, vedata, ob, cache_ob);
- }
- else {
- gpencil_populate_multiedit(&e_data, vedata, ob, cache_ob);
- }
- }
+ gpencil_layer_cache_populate(
+ iter->pd->sbuffer_layer, iter->pd->sbuffer_layer->actframe, NULL, iter);
- /* FX passses */
- cache_ob->has_fx = false;
- if ((!stl->storage->simplify_fx) &&
- ((!ELEM(cache_ob->shading_type[0], OB_WIRE, OB_SOLID)) ||
- ((v3d->spacetype != SPACE_VIEW3D))) &&
- (BKE_shaderfx_has_gpencil(ob))) {
- cache_ob->has_fx = true;
- if ((!stl->storage->simplify_fx) && (!is_multiedit)) {
- gpencil_fx_prepare(&e_data, vedata, cache_ob);
- }
+ const DRWContextState *ctx = DRW_context_state_get();
+ ToolSettings *ts = ctx->scene->toolsettings;
+ if (ts->gpencil_v3d_align & (GP_PROJECT_DEPTH_VIEW | GP_PROJECT_DEPTH_STROKE)) {
+ /* In this case we can't do correct projection during stroke. We just disable depth test. */
+ DRW_shgroup_uniform_texture(iter->grp, "gpSceneDepthTexture", iter->pd->dummy_tx);
}
+
+ iter->do_sbuffer_call = DRAW_NOW;
+ gpencil_stroke_cache_populate(NULL, NULL, iter->pd->sbuffer_stroke, iter);
+ gpencil_drawcall_flush(iter);
+
+ gpencil_vfx_cache_populate(vedata, iter->ob, iter->tgp_ob);
+
+ /* Restore state. */
+ iter->do_sbuffer_call = 0;
+ iter->pd->scene_depth_tx = depth_texture;
}
-void GPENCIL_cache_populate(void *vedata, Object *ob)
+void GPENCIL_cache_populate(void *ved, Object *ob)
{
+ GPENCIL_Data *vedata = (GPENCIL_Data *)ved;
+ GPENCIL_PrivateData *pd = vedata->stl->pd;
+ GPENCIL_TextureList *txl = vedata->txl;
+
/* object must be visible */
if (!(DRW_object_visibility_in_active_context(ob) & OB_VISIBLE_SELF)) {
return;
}
- GPENCIL_StorageList *stl = ((GPENCIL_Data *)vedata)->stl;
- const DRWContextState *draw_ctx = DRW_context_state_get();
- Scene *scene = draw_ctx->scene;
- ToolSettings *ts = scene->toolsettings;
- View3D *v3d = draw_ctx->v3d;
+ if (ob->data && (ob->type == OB_GPENCIL) && (ob->dt >= OB_SOLID)) {
+ gpIterPopulateData iter = {0};
+ iter.ob = ob;
+ iter.pd = pd;
+ iter.tgp_ob = gpencil_object_cache_add(pd, ob);
+ iter.matpool = gpencil_material_pool_create(pd, ob, &iter.mat_ofs);
+ iter.tex_fill = txl->dummy_texture;
+ iter.tex_stroke = txl->dummy_texture;
- if (ob->type == OB_GPENCIL && ob->data) {
+ /* Special case for rendering onion skin. */
bGPdata *gpd = (bGPdata *)ob->data;
+ bool do_onion = (!pd->is_render) ? pd->do_onion : (gpd->onion_flag & GP_ONION_GHOST_ALWAYS);
- /* enable multisample and basic framebuffer creation */
- stl->storage->framebuffer_flag |= GP_FRAMEBUFFER_MULTISAMPLE;
- stl->storage->framebuffer_flag |= GP_FRAMEBUFFER_BASIC;
-
- /* when start/stop animation the cache must be set as dirty to reset all data */
- if (stl->storage->reset_cache) {
- gpd->flag |= GP_DATA_CACHE_IS_DIRTY;
- stl->storage->reset_cache = false;
- }
+ BKE_gpencil_visible_stroke_iter(ob,
+ gpencil_layer_cache_populate,
+ gpencil_stroke_cache_populate,
+ &iter,
+ do_onion,
+ pd->cfra);
- if ((stl->g_data->session_flag & GP_DRW_PAINT_READY) == 0) {
- /* bound box object are not visible, only external box*/
- if (ob->dt != OB_BOUNDBOX) {
- /* save gp objects for drawing later */
- stl->g_data->gp_object_cache = gpencil_object_cache_add(stl->g_data->gp_object_cache,
- ob,
- &stl->g_data->gp_cache_size,
- &stl->g_data->gp_cache_used);
-
- /* enable instance loop */
- if (!stl->g_data->do_instances) {
- tGPencilObjectCache *cache_ob =
- &stl->g_data->gp_object_cache[stl->g_data->gp_cache_used - 1];
- stl->g_data->do_instances = cache_ob->is_dup_ob;
- }
-
- /* load drawing data */
- gpencil_add_draw_data(vedata, ob);
- }
- }
+ gpencil_drawcall_flush(&iter);
- /* draw current painting strokes
- * (only if region is equal to originated paint region)
- *
- * Need to use original data because to use the copy of data, the paint
- * operator must update depsgraph and this makes that first events of the
- * mouse are missed if the datablock is very big due the time required to
- * copy the datablock. The search of the original data is faster than a
- * full datablock copy.
- * Using the original data doesn't require a copy and the feel when drawing
- * is far better.
- */
-
- bGPdata *gpd_orig = (bGPdata *)DEG_get_original_id(&gpd->id);
- if ((draw_ctx->obact == ob) &&
- ((gpd_orig->runtime.ar == NULL) || (gpd_orig->runtime.ar == draw_ctx->region))) {
- gpencil_populate_buffer_strokes(&e_data, vedata, ts, ob);
+ if (iter.do_sbuffer_call) {
+ gp_sbuffer_cache_populate(&iter);
}
- /* grid */
- if ((v3d) && ((v3d->flag2 & V3D_HIDE_OVERLAYS) == 0) && (v3d->gp_flag & V3D_GP_SHOW_GRID) &&
- (ob->type == OB_GPENCIL) && (ob == draw_ctx->obact) &&
- ((ts->gpencil_v3d_align & GP_PROJECT_DEPTH_VIEW) == 0) &&
- ((ts->gpencil_v3d_align & GP_PROJECT_DEPTH_STROKE) == 0)) {
+ gpencil_vfx_cache_populate(vedata, ob, iter.tgp_ob);
- stl->g_data->batch_grid = gpencil_get_grid(ob);
- DRW_shgroup_call(stl->g_data->shgrps_grid, stl->g_data->batch_grid, NULL);
+ if (pd->do_fast_drawing) {
+ gp_sbuffer_cache_populate_fast(vedata, &iter);
}
}
+
+ if (ob->type == OB_LAMP && pd->use_lights) {
+ gpencil_light_pool_populate(pd->global_light_pool, ob);
+ }
}
-void GPENCIL_cache_finish(void *vedata)
+void GPENCIL_cache_finish(void *ved)
{
- GPENCIL_StorageList *stl = ((GPENCIL_Data *)vedata)->stl;
- tGPencilObjectCache *cache_ob = NULL;
- Object *ob = NULL;
-
- /* create data for instances */
- if (stl->g_data->do_instances) {
- GHash *gh_objects = BLI_ghash_str_new(__func__);
- /* create hash of real object (non duplicated) */
- for (int i = 0; i < stl->g_data->gp_cache_used; i++) {
- cache_ob = &stl->g_data->gp_object_cache[i];
- if (!cache_ob->is_dup_ob) {
- ob = cache_ob->ob;
- char *name = BKE_id_to_unique_string_key(&ob->id);
- BLI_ghash_insert(gh_objects, name, cache_ob->ob);
- }
- }
-
- /* draw particles */
- gpencil_populate_particles(&e_data, gh_objects, vedata);
+ GPENCIL_Data *vedata = (GPENCIL_Data *)ved;
+ GPENCIL_PrivateData *pd = vedata->stl->pd;
+ GPENCIL_FramebufferList *fbl = vedata->fbl;
- /* free hash */
- BLI_ghash_free(gh_objects, MEM_freeN, NULL);
+ /* Upload UBO data. */
+ BLI_memblock_iter iter;
+ BLI_memblock_iternew(pd->gp_material_pool, &iter);
+ GPENCIL_MaterialPool *pool;
+ while ((pool = (GPENCIL_MaterialPool *)BLI_memblock_iterstep(&iter))) {
+ GPU_uniformbuffer_update(pool->ubo, pool->mat_data);
}
- if (stl->g_data->session_flag & (GP_DRW_PAINT_IDLE | GP_DRW_PAINT_FILLING)) {
- stl->storage->framebuffer_flag |= GP_FRAMEBUFFER_DRAW;
+ BLI_memblock_iternew(pd->gp_light_pool, &iter);
+ GPENCIL_LightPool *lpool;
+ while ((lpool = (GPENCIL_LightPool *)BLI_memblock_iterstep(&iter))) {
+ GPU_uniformbuffer_update(lpool->ubo, lpool->light_data);
}
- /* create framebuffers (only for normal drawing) */
- if (!DRW_state_is_select() || !DRW_state_is_depth()) {
- GPENCIL_create_framebuffers(vedata);
+ /* Sort object by decreasing Z to avoid most of alpha ordering issues. */
+ gpencil_object_cache_sort(pd);
+
+ /* Create framebuffers only if needed. */
+ if (pd->tobjects.first) {
+ eGPUTextureFormat format = pd->use_signed_fb ? GPU_RGBA16F : GPU_R11F_G11F_B10F;
+
+ const float *size = DRW_viewport_size_get();
+ pd->depth_tx = DRW_texture_pool_query_2d(
+ size[0], size[1], GPU_DEPTH24_STENCIL8, &draw_engine_gpencil_type);
+ pd->color_tx = DRW_texture_pool_query_2d(size[0], size[1], format, &draw_engine_gpencil_type);
+ pd->reveal_tx = DRW_texture_pool_query_2d(size[0], size[1], format, &draw_engine_gpencil_type);
+
+ GPU_framebuffer_ensure_config(&fbl->gpencil_fb,
+ {
+ GPU_ATTACHMENT_TEXTURE(pd->depth_tx),
+ GPU_ATTACHMENT_TEXTURE(pd->color_tx),
+ GPU_ATTACHMENT_TEXTURE(pd->reveal_tx),
+ });
+
+ if (pd->use_layer_fb) {
+ pd->color_layer_tx = DRW_texture_pool_query_2d(
+ size[0], size[1], format, &draw_engine_gpencil_type);
+ pd->reveal_layer_tx = DRW_texture_pool_query_2d(
+ size[0], size[1], format, &draw_engine_gpencil_type);
+
+ GPU_framebuffer_ensure_config(&fbl->layer_fb,
+ {
+ GPU_ATTACHMENT_TEXTURE(pd->depth_tx),
+ GPU_ATTACHMENT_TEXTURE(pd->color_layer_tx),
+ GPU_ATTACHMENT_TEXTURE(pd->reveal_layer_tx),
+ });
+ };
+
+ if (pd->use_object_fb) {
+ pd->color_object_tx = DRW_texture_pool_query_2d(
+ size[0], size[1], format, &draw_engine_gpencil_type);
+ pd->reveal_object_tx = DRW_texture_pool_query_2d(
+ size[0], size[1], format, &draw_engine_gpencil_type);
+
+ GPU_framebuffer_ensure_config(&fbl->object_fb,
+ {
+ GPU_ATTACHMENT_TEXTURE(pd->depth_tx),
+ GPU_ATTACHMENT_TEXTURE(pd->color_object_tx),
+ GPU_ATTACHMENT_TEXTURE(pd->reveal_object_tx),
+ });
+ }
+
+ if (pd->use_mask_fb) {
+ /* We need an extra depth to not disturb the normal drawing.
+ * The color_tx is needed for framebuffer cmpleteness. */
+ GPUTexture *color_tx, *depth_tx;
+ depth_tx = DRW_texture_pool_query_2d(
+ size[0], size[1], GPU_DEPTH24_STENCIL8, &draw_engine_gpencil_type);
+ color_tx = DRW_texture_pool_query_2d(size[0], size[1], GPU_R8, &draw_engine_gpencil_type);
+ /* Use high quality format for render. */
+ eGPUTextureFormat mask_format = pd->is_render ? GPU_R16 : GPU_R8;
+ pd->mask_tx = DRW_texture_pool_query_2d(
+ size[0], size[1], mask_format, &draw_engine_gpencil_type);
+
+ GPU_framebuffer_ensure_config(&fbl->mask_fb,
+ {
+ GPU_ATTACHMENT_TEXTURE(depth_tx),
+ GPU_ATTACHMENT_TEXTURE(color_tx),
+ GPU_ATTACHMENT_TEXTURE(pd->mask_tx),
+ });
+ }
+
+ GPENCIL_antialiasing_init(vedata);
}
}
-/* helper function to sort inverse gpencil objects using qsort */
-static int gpencil_object_cache_compare_zdepth(const void *a1, const void *a2)
+static void GPENCIL_draw_scene_depth_only(void *ved)
{
- const tGPencilObjectCache *ps1 = a1, *ps2 = a2;
+ GPENCIL_Data *vedata = (GPENCIL_Data *)ved;
+ GPENCIL_PrivateData *pd = vedata->stl->pd;
+ DefaultFramebufferList *dfbl = DRW_viewport_framebuffer_list_get();
- if (ps1->zdepth < ps2->zdepth) {
- return 1;
- }
- else if (ps1->zdepth > ps2->zdepth) {
- return -1;
+ if (DRW_state_is_fbo()) {
+ GPU_framebuffer_bind(dfbl->depth_only_fb);
}
- return 0;
-}
-
-/* prepare a texture with full viewport screenshot for fast drawing */
-static void gpencil_prepare_fast_drawing(GPENCIL_StorageList *stl,
- DefaultFramebufferList *dfbl,
- GPENCIL_FramebufferList *fbl,
- DRWPass *pass,
- const float clearcol[4])
-{
- if (stl->g_data->session_flag & (GP_DRW_PAINT_IDLE | GP_DRW_PAINT_FILLING)) {
- GPU_framebuffer_bind(fbl->background_fb);
- /* clean only in first loop cycle */
- if (stl->g_data->session_flag & GP_DRW_PAINT_IDLE) {
- GPU_framebuffer_clear_color_depth_stencil(fbl->background_fb, clearcol, 1.0f, 0x0);
- stl->g_data->session_flag = GP_DRW_PAINT_FILLING;
+ for (GPENCIL_tObject *ob = pd->tobjects.first; ob; ob = ob->next) {
+ for (GPENCIL_tLayer *layer = ob->layers.first; layer; layer = layer->next) {
+ DRW_draw_pass(layer->geom_ps);
}
- /* repeat pass to fill temp texture */
- DRW_draw_pass(pass);
- /* set default framebuffer again */
+ }
+
+ if (DRW_state_is_fbo()) {
GPU_framebuffer_bind(dfbl->default_fb);
+ }
+
+ pd->gp_object_pool = pd->gp_layer_pool = pd->gp_vfx_pool = pd->gp_maskbit_pool = NULL;
- stl->storage->background_ready = true;
+ /* Free temp stroke buffers. */
+ if (pd->sbuffer_gpd) {
+ DRW_cache_gpencil_sbuffer_clear(pd->obact);
}
}
-void DRW_gpencil_free_runtime_data(void *ved)
+static void gpencil_draw_mask(GPENCIL_Data *vedata, GPENCIL_tObject *ob, GPENCIL_tLayer *layer)
{
- GPENCIL_Data *vedata = (GPENCIL_Data *)ved;
- GPENCIL_StorageList *stl = ((GPENCIL_Data *)vedata)->stl;
+ GPENCIL_PassList *psl = vedata->psl;
+ GPENCIL_FramebufferList *fbl = vedata->fbl;
+ float clear_col[4] = {1.0f, 1.0f, 1.0f, 1.0f};
+ float clear_depth = ob->is_drawmode3d ? 1.0f : 0.0f;
+ bool inverted = false;
+ /* OPTI(fclem) we could optimize by only clearing if the new mask_bits does not contain all
+ * the masks already rendered in the buffer, and drawing only the layers not already drawn. */
+ bool cleared = false;
- /* free gpu data */
- GPU_BATCH_DISCARD_SAFE(stl->g_data->batch_buffer_stroke);
- MEM_SAFE_FREE(stl->g_data->batch_buffer_stroke);
+ DRW_stats_group_start("GPencil Mask");
- GPU_BATCH_DISCARD_SAFE(stl->g_data->batch_buffer_fill);
- MEM_SAFE_FREE(stl->g_data->batch_buffer_fill);
+ GPU_framebuffer_bind(fbl->mask_fb);
- GPU_BATCH_DISCARD_SAFE(stl->g_data->batch_buffer_ctrlpoint);
- MEM_SAFE_FREE(stl->g_data->batch_buffer_ctrlpoint);
+ for (int i = 0; i < GP_MAX_MASKBITS; i++) {
+ if (!BLI_BITMAP_TEST(layer->mask_bits, i)) {
+ continue;
+ }
- GPU_BATCH_DISCARD_SAFE(stl->g_data->batch_grid);
- MEM_SAFE_FREE(stl->g_data->batch_grid);
+ if (BLI_BITMAP_TEST_BOOL(layer->mask_invert_bits, i) != inverted) {
+ if (cleared) {
+ DRW_draw_pass(psl->mask_invert_ps);
+ }
+ inverted = !inverted;
+ }
- if (stl->g_data->gp_object_cache == NULL) {
- return;
+ if (!cleared) {
+ cleared = true;
+ GPU_framebuffer_clear_color_depth(fbl->mask_fb, clear_col, clear_depth);
+ }
+
+ GPENCIL_tLayer *mask_layer = gpencil_layer_cache_get(ob, i);
+ BLI_assert(mask_layer);
+
+ DRW_draw_pass(mask_layer->geom_ps);
}
- /* reset all cache flags */
- for (int i = 0; i < stl->g_data->gp_cache_used; i++) {
- tGPencilObjectCache *cache_ob = &stl->g_data->gp_object_cache[i];
- if (cache_ob) {
- bGPdata *gpd = cache_ob->gpd;
- gpd->flag &= ~GP_DATA_CACHE_IS_DIRTY;
-
- /* free shgrp array */
- cache_ob->tot_layers = 0;
- MEM_SAFE_FREE(cache_ob->name);
- MEM_SAFE_FREE(cache_ob->shgrp_array);
- }
+ if (!inverted) {
+ /* Blend shader expect an opacity mask not a reavealage buffer. */
+ DRW_draw_pass(psl->mask_invert_ps);
}
- /* free the cache itself */
- MEM_SAFE_FREE(stl->g_data->gp_object_cache);
+ DRW_stats_group_end();
}
-static void gpencil_draw_pass_range(GPENCIL_FramebufferList *fbl,
- GPENCIL_StorageList *stl,
- GPENCIL_PassList *psl,
- GPUFrameBuffer *fb,
- Object *ob,
- bGPdata *gpd,
- DRWShadingGroup *init_shgrp,
- DRWShadingGroup *end_shgrp,
- bool multi)
+static void GPENCIL_draw_object(GPENCIL_Data *vedata, GPENCIL_tObject *ob)
{
- if (init_shgrp == NULL) {
- return;
+ GPENCIL_PassList *psl = vedata->psl;
+ GPENCIL_PrivateData *pd = vedata->stl->pd;
+ GPENCIL_FramebufferList *fbl = vedata->fbl;
+ float clear_cols[2][4] = {{0.0f, 0.0f, 0.0f, 0.0f}, {1.0f, 1.0f, 1.0f, 1.0f}};
+
+ DRW_stats_group_start("GPencil Object");
+
+ GPUFrameBuffer *fb_object = (ob->vfx.first) ? fbl->object_fb : fbl->gpencil_fb;
+
+ GPU_framebuffer_bind(fb_object);
+ GPU_framebuffer_clear_depth_stencil(fb_object, ob->is_drawmode3d ? 1.0f : 0.0f, 0x00);
+
+ if (ob->vfx.first) {
+ GPU_framebuffer_multi_clear(fb_object, clear_cols);
}
- const bool do_antialiasing = ((!stl->storage->is_mat_preview) && (multi));
+ for (GPENCIL_tLayer *layer = ob->layers.first; layer; layer = layer->next) {
+ if (layer->mask_bits) {
+ gpencil_draw_mask(vedata, ob, layer);
+ }
+
+ if (layer->blend_ps) {
+ GPU_framebuffer_bind(fbl->layer_fb);
+ GPU_framebuffer_multi_clear(fbl->layer_fb, clear_cols);
+ }
+ else {
+ GPU_framebuffer_bind(fb_object);
+ }
+
+ DRW_draw_pass(layer->geom_ps);
- if (do_antialiasing) {
- MULTISAMPLE_GP_SYNC_ENABLE(stl->storage->multisamples, fbl);
+ if (layer->blend_ps) {
+ GPU_framebuffer_bind(fb_object);
+ DRW_draw_pass(layer->blend_ps);
+ }
+ }
+
+ for (GPENCIL_tVfx *vfx = ob->vfx.first; vfx; vfx = vfx->next) {
+ GPU_framebuffer_bind(*(vfx->target_fb));
+ DRW_draw_pass(vfx->vfx_ps);
}
- DRW_draw_pass_subset(GPENCIL_3D_DRAWMODE(ob, gpd) ? psl->stroke_pass_3d : psl->stroke_pass_2d,
- init_shgrp,
- end_shgrp);
+ copy_m4_m4(pd->object_bound_mat, ob->plane_mat);
+ pd->is_stroke_order_3d = ob->is_drawmode3d;
- if (do_antialiasing) {
- MULTISAMPLE_GP_SYNC_DISABLE(stl->storage->multisamples, fbl, fb, txl);
+ if (pd->scene_fb) {
+ GPU_framebuffer_bind(pd->scene_fb);
+ DRW_draw_pass(psl->merge_depth_ps);
}
+
+ DRW_stats_group_end();
}
-/* draw strokes to use for selection */
-static void drw_gpencil_select_render(GPENCIL_StorageList *stl, GPENCIL_PassList *psl)
+static void GPENCIL_fast_draw_start(GPENCIL_Data *vedata)
{
- tGPencilObjectCache *cache_ob;
- tGPencilObjectCache_shgrp *array_elm = NULL;
- DRWShadingGroup *init_shgrp = NULL;
- DRWShadingGroup *end_shgrp = NULL;
-
- /* Draw all pending objects */
- if ((stl->g_data->gp_cache_used > 0) && (stl->g_data->gp_object_cache)) {
- /* sort by zdepth */
- qsort(stl->g_data->gp_object_cache,
- stl->g_data->gp_cache_used,
- sizeof(tGPencilObjectCache),
- gpencil_object_cache_compare_zdepth);
-
- for (int i = 0; i < stl->g_data->gp_cache_used; i++) {
- cache_ob = &stl->g_data->gp_object_cache[i];
- if (cache_ob) {
- Object *ob = cache_ob->ob;
- bGPdata *gpd = cache_ob->gpd;
- init_shgrp = NULL;
- if (cache_ob->tot_layers > 0) {
- for (int e = 0; e < cache_ob->tot_layers; e++) {
- array_elm = &cache_ob->shgrp_array[e];
- if (init_shgrp == NULL) {
- init_shgrp = array_elm->init_shgrp;
- }
- end_shgrp = array_elm->end_shgrp;
- }
- /* draw group */
- DRW_draw_pass_subset(GPENCIL_3D_DRAWMODE(ob, gpd) ? psl->stroke_pass_3d :
- psl->stroke_pass_2d,
- init_shgrp,
- end_shgrp);
- }
- /* the cache must be dirty for next loop */
- gpd->flag |= GP_DATA_CACHE_IS_DIRTY;
- }
- }
+ GPENCIL_PrivateData *pd = vedata->stl->pd;
+ GPENCIL_FramebufferList *fbl = vedata->fbl;
+ DefaultFramebufferList *dfbl = DRW_viewport_framebuffer_list_get();
+
+ if (!pd->snapshot_buffer_dirty) {
+ /* Copy back cached render. */
+ GPU_framebuffer_blit(fbl->snapshot_fb, 0, dfbl->default_fb, 0, GPU_DEPTH_BIT);
+ GPU_framebuffer_blit(fbl->snapshot_fb, 0, fbl->gpencil_fb, 0, GPU_COLOR_BIT);
+ GPU_framebuffer_blit(fbl->snapshot_fb, 1, fbl->gpencil_fb, 1, GPU_COLOR_BIT);
+ /* Bypass drawing. */
+ pd->tobjects.first = pd->tobjects.last = NULL;
}
}
-/* draw scene */
-void GPENCIL_draw_scene(void *ved)
+static void GPENCIL_fast_draw_end(GPENCIL_Data *vedata)
{
- GPENCIL_Data *vedata = (GPENCIL_Data *)ved;
- GPENCIL_StorageList *stl = ((GPENCIL_Data *)vedata)->stl;
-
- GPENCIL_PassList *psl = ((GPENCIL_Data *)vedata)->psl;
- GPENCIL_FramebufferList *fbl = ((GPENCIL_Data *)vedata)->fbl;
+ GPENCIL_PrivateData *pd = vedata->stl->pd;
+ GPENCIL_FramebufferList *fbl = vedata->fbl;
DefaultFramebufferList *dfbl = DRW_viewport_framebuffer_list_get();
- tGPencilObjectCache *cache_ob;
- tGPencilObjectCache_shgrp *array_elm = NULL;
- DRWShadingGroup *init_shgrp = NULL;
- DRWShadingGroup *end_shgrp = NULL;
-
- const float clearcol[4] = {0.0f, 0.0f, 0.0f, 0.0f};
-
- const DRWContextState *draw_ctx = DRW_context_state_get();
- View3D *v3d = draw_ctx->v3d;
- Object *obact = draw_ctx->obact;
- const bool playing = stl->storage->is_playing;
- const bool is_render = stl->storage->is_render;
- bGPdata *gpd_act = (obact) && (obact->type == OB_GPENCIL) ? (bGPdata *)obact->data : NULL;
- const bool is_edit = GPENCIL_ANY_EDIT_MODE(gpd_act);
- const bool overlay = v3d != NULL ? (bool)((v3d->flag2 & V3D_HIDE_OVERLAYS) == 0) : true;
-
- /* if the draw is for select, do a basic drawing and return */
- if (DRW_state_is_select() || DRW_state_is_depth()) {
- drw_gpencil_select_render(stl, psl);
- return;
+ if (pd->snapshot_buffer_dirty) {
+ /* Save to snapshot buffer. */
+ GPU_framebuffer_blit(dfbl->default_fb, 0, fbl->snapshot_fb, 0, GPU_DEPTH_BIT);
+ GPU_framebuffer_blit(fbl->gpencil_fb, 0, fbl->snapshot_fb, 0, GPU_COLOR_BIT);
+ GPU_framebuffer_blit(fbl->gpencil_fb, 1, fbl->snapshot_fb, 1, GPU_COLOR_BIT);
+ pd->snapshot_buffer_dirty = false;
}
-
- /* paper pass to display a comfortable area to draw over complex scenes with geometry */
- if ((!is_render) && (obact) && (obact->type == OB_GPENCIL)) {
- if (((v3d->flag2 & V3D_HIDE_OVERLAYS) == 0) && (v3d->gp_flag & V3D_GP_SHOW_PAPER)) {
- DRW_draw_pass(psl->paper_pass);
- }
+ /* Draw the sbuffer stroke(s). */
+ for (GPENCIL_tObject *ob = pd->sbuffer_tobjects.first; ob; ob = ob->next) {
+ GPENCIL_draw_object(vedata, ob);
}
+}
- /* if we have a painting session, we use fast viewport drawing method */
- if ((!is_render) && (stl->g_data->session_flag & GP_DRW_PAINT_PAINTING)) {
- GPU_framebuffer_bind(dfbl->default_fb);
-
- if (obact->dt != OB_BOUNDBOX) {
- DRW_draw_pass(psl->background_pass);
- }
+void GPENCIL_draw_scene(void *ved)
+{
+ GPENCIL_Data *vedata = (GPENCIL_Data *)ved;
+ GPENCIL_PrivateData *pd = vedata->stl->pd;
+ GPENCIL_FramebufferList *fbl = vedata->fbl;
+ float clear_cols[2][4] = {{0.0f, 0.0f, 0.0f, 0.0f}, {1.0f, 1.0f, 1.0f, 1.0f}};
- MULTISAMPLE_GP_SYNC_ENABLE(stl->storage->multisamples, fbl);
+ /* Fade 3D objects. */
+ if ((!pd->is_render) && (pd->fade_3d_object_opacity > -1.0f)) {
+ mul_v4_fl(clear_cols[1], pd->fade_3d_object_opacity);
+ }
- DRW_draw_pass(psl->drawing_pass);
+ if (pd->draw_depth_only) {
+ GPENCIL_draw_scene_depth_only(vedata);
+ return;
+ }
- MULTISAMPLE_GP_SYNC_DISABLE(stl->storage->multisamples, fbl, dfbl->default_fb, txl);
+ if (pd->tobjects.first == NULL) {
+ return;
+ }
- /* grid pass */
- if ((!is_render) && (obact) && (obact->type == OB_GPENCIL)) {
- if (((v3d->flag2 & V3D_HIDE_OVERLAYS) == 0) && (v3d->gp_flag & V3D_GP_SHOW_GRID)) {
- DRW_draw_pass(psl->grid_pass);
- }
- }
+ if (pd->do_fast_drawing) {
+ GPENCIL_fast_draw_start(vedata);
+ }
- /* free memory */
- DRW_gpencil_free_runtime_data(ved);
+ if (pd->tobjects.first) {
+ GPU_framebuffer_bind(fbl->gpencil_fb);
+ GPU_framebuffer_multi_clear(fbl->gpencil_fb, clear_cols);
+ }
- return;
+ for (GPENCIL_tObject *ob = pd->tobjects.first; ob; ob = ob->next) {
+ GPENCIL_draw_object(vedata, ob);
}
- if (DRW_state_is_fbo()) {
+ if (pd->do_fast_drawing) {
+ GPENCIL_fast_draw_end(vedata);
+ }
- /* Draw all pending objects */
- if (stl->g_data->gp_cache_used > 0) {
- /* sort by zdepth */
- qsort(stl->g_data->gp_object_cache,
- stl->g_data->gp_cache_used,
- sizeof(tGPencilObjectCache),
- gpencil_object_cache_compare_zdepth);
-
- for (int i = 0; i < stl->g_data->gp_cache_used; i++) {
- cache_ob = &stl->g_data->gp_object_cache[i];
- Object *ob = cache_ob->ob;
- bGPdata *gpd = cache_ob->gpd;
- init_shgrp = NULL;
- /* Render stroke in separated framebuffer */
- GPU_framebuffer_bind(fbl->temp_fb_a);
- GPU_framebuffer_clear_color_depth_stencil(fbl->temp_fb_a, clearcol, 1.0f, 0x0);
- /* Stroke Pass:
- * draw only a subset that usually starts with a fill and ends with stroke
- */
- bool use_blend = false;
- if (cache_ob->tot_layers > 0) {
- for (int e = 0; e < cache_ob->tot_layers; e++) {
- bool is_last = (e == cache_ob->tot_layers - 1) ? true : false;
- array_elm = &cache_ob->shgrp_array[e];
-
- if (((array_elm->mode == eGplBlendMode_Regular) && (!use_blend) &&
- (!array_elm->mask_layer)) ||
- (e == 0)) {
- if (init_shgrp == NULL) {
- init_shgrp = array_elm->init_shgrp;
- }
- end_shgrp = array_elm->end_shgrp;
- }
- else {
- use_blend = true;
- /* draw pending groups */
- gpencil_draw_pass_range(
- fbl, stl, psl, fbl->temp_fb_a, ob, gpd, init_shgrp, end_shgrp, is_last);
-
- /* Draw current group in separated texture to blend later */
- init_shgrp = array_elm->init_shgrp;
- end_shgrp = array_elm->end_shgrp;
-
- GPU_framebuffer_bind(fbl->temp_fb_fx);
- GPU_framebuffer_clear_color_depth_stencil(fbl->temp_fb_fx, clearcol, 1.0f, 0x0);
- gpencil_draw_pass_range(
- fbl, stl, psl, fbl->temp_fb_fx, ob, gpd, init_shgrp, end_shgrp, is_last);
-
- /* Blend A texture and FX texture */
- GPU_framebuffer_bind(fbl->temp_fb_b);
- GPU_framebuffer_clear_color_depth_stencil(fbl->temp_fb_b, clearcol, 1.0f, 0x0);
- stl->storage->blend_mode = array_elm->mode;
- stl->storage->mask_layer = (int)array_elm->mask_layer;
- stl->storage->tonemapping = 1;
- DRW_draw_pass(psl->blend_pass);
- stl->storage->tonemapping = 0;
-
- /* Copy B texture to A texture to follow loop */
- stl->g_data->input_depth_tx = stl->g_data->temp_depth_tx_b;
- stl->g_data->input_color_tx = stl->g_data->temp_color_tx_b;
-
- GPU_framebuffer_bind(fbl->temp_fb_a);
- GPU_framebuffer_clear_color_depth_stencil(fbl->temp_fb_a, clearcol, 1.0f, 0x0);
- DRW_draw_pass(psl->mix_pass_noblend);
-
- /* prepare next group */
- init_shgrp = NULL;
- }
- }
- /* last group */
- gpencil_draw_pass_range(
- fbl, stl, psl, fbl->temp_fb_a, ob, gpd, init_shgrp, end_shgrp, true);
- }
-
- /* Current buffer drawing */
- if ((!is_render) && (cache_ob->is_dup_ob == false)) {
- DRW_draw_pass(psl->drawing_pass);
- }
- /* fx passes */
- if (cache_ob->has_fx == true) {
- stl->storage->tonemapping = 0;
- gpencil_fx_draw(&e_data, vedata, cache_ob);
- }
-
- stl->g_data->input_depth_tx = stl->g_data->temp_depth_tx_a;
- stl->g_data->input_color_tx = stl->g_data->temp_color_tx_a;
-
- /* Combine with scene buffer */
- if ((!is_render) || (fbl->main == NULL)) {
- GPU_framebuffer_bind(dfbl->default_fb);
- }
- else {
- GPU_framebuffer_bind(fbl->main);
- }
- /* tonemapping */
- stl->storage->tonemapping = 1;
-
- /* active select flag and selection color */
- if (!is_render) {
- UI_GetThemeColorShadeAlpha4fv(
- (ob == draw_ctx->obact) ? TH_ACTIVE : TH_SELECT, 0, -40, stl->storage->select_color);
- }
- stl->storage->do_select_outline = ((overlay) && (ob->base_flag & BASE_SELECTED) &&
- (ob->mode == OB_MODE_OBJECT) && (!is_render) &&
- (!playing) && (v3d->flag & V3D_SELECT_OUTLINE));
-
- /* if active object is not object mode, disable for all objects */
- if ((stl->storage->do_select_outline) && (draw_ctx->obact) &&
- (draw_ctx->obact->mode != OB_MODE_OBJECT)) {
- stl->storage->do_select_outline = 0;
- }
-
- /* draw mix pass */
- DRW_draw_pass(psl->mix_pass);
-
- /* disable select flag */
- stl->storage->do_select_outline = 0;
-
- /* prepare for fast drawing */
- if (!is_render) {
- if (!playing) {
- gpencil_prepare_fast_drawing(stl, dfbl, fbl, psl->mix_pass_noblend, clearcol);
- }
- }
- else {
- /* if render, the cache must be dirty for next loop */
- gpd->flag |= GP_DATA_CACHE_IS_DIRTY;
- }
- }
- /* edit points */
- if ((!is_render) && (!playing) && (is_edit)) {
- DRW_draw_pass(psl->edit_pass);
- }
- }
- /* grid pass */
- if ((!is_render) && (obact) && (obact->type == OB_GPENCIL)) {
- if (((v3d->flag2 & V3D_HIDE_OVERLAYS) == 0) && (v3d->gp_flag & V3D_GP_SHOW_GRID)) {
- DRW_draw_pass(psl->grid_pass);
- }
- }
+ if (pd->scene_fb) {
+ GPENCIL_antialiasing_draw(vedata);
}
- /* free memory */
- DRW_gpencil_free_runtime_data(ved);
- /* reset */
- if (DRW_state_is_fbo()) {
- /* attach again default framebuffer */
- if (!is_render) {
- GPU_framebuffer_bind(dfbl->default_fb);
- }
+ pd->gp_object_pool = pd->gp_layer_pool = pd->gp_vfx_pool = pd->gp_maskbit_pool = NULL;
- /* the temp texture is ready. Now we can use fast screen drawing */
- if (stl->g_data->session_flag & GP_DRW_PAINT_FILLING) {
- stl->g_data->session_flag = GP_DRW_PAINT_READY;
- }
+ /* Free temp stroke buffers. */
+ if (pd->sbuffer_gpd) {
+ DRW_cache_gpencil_sbuffer_clear(pd->obact);
}
}
+static void GPENCIL_engine_free(void)
+{
+ GPENCIL_shader_free();
+}
+
static const DrawEngineDataSize GPENCIL_data_size = DRW_VIEWPORT_DATA_SIZE(GPENCIL_Data);
DrawEngineType draw_engine_gpencil_type = {