GPencil and Annotation: Use cached depth to perform depth testing operations

Operations such as erasing with occlusion and drawing on the surface
require reading the depth buffer.

However, this is being done with minimal efficiency.

Currently, to read the depth corresponding to each point of the new stroke,
a ReadPixel is called to send a message to the GPU and read the depth of
the corresponding pixel in the VRAM.

The communication between GPU and CPU is known to be a slow operation so
it is good to be avoided.

Therefore, save the entire depth buffer in a cache to be read directly
from the RAM.

(Also the `ED_view3d_autodist_depth` and `ED_view3d_autodist_depth_seg` have
been removed since they are no longer used).

Reviewed By: antoniov, fclem

Differential Revision: https://developer.blender.org/D10894
This commit is contained in:
Germano Cavalcante 2021-11-02 12:33:28 -03:00 committed by Germano Cavalcante
parent 8b516d8712
commit aa0ac0035a
Notes: blender-bot 2023-02-14 11:20:29 +01:00
Referenced by commit 6ae34bb071, Fix drawing annotations on surface
Referenced by commit bc4c20d414, Fix T93360: 'Iteractive Light Track' do not work over empty background
Referenced by commit c0fdaf700a, Fix the GPencil stroke not sticking to other strokes
Referenced by issue #93360, Regresion: Shift+t aiming with light source do not work over empty background
8 changed files with 65 additions and 42 deletions

View File

@ -123,6 +123,8 @@ typedef struct tGPsdata {
ARegion *region;
/** needed for GP_STROKE_2DSPACE. */
View2D *v2d;
/** For operations that require occlusion testing. */
ViewDepths *depths;
/** for using the camera rect within the 3d view. */
rctf *subrect;
rctf subrect_data;
@ -972,12 +974,13 @@ static void annotation_stroke_newfrombuffer(tGPsdata *p)
depth_arr = MEM_mallocN(sizeof(float) * gpd->runtime.sbuffer_used, "depth_points");
const ViewDepths *depths = p->depths;
for (i = 0, ptc = gpd->runtime.sbuffer; i < gpd->runtime.sbuffer_used; i++, ptc++, pt++) {
round_v2i_v2fl(mval_i, &ptc->x);
if ((ED_view3d_autodist_depth(p->region, mval_i, depth_margin, depth_arr + i) == 0) &&
(i && (ED_view3d_autodist_depth_seg(
p->region, mval_i, mval_prev, depth_margin + 1, depth_arr + i) == 0))) {
if ((ED_view3d_depth_read_cached(depths, mval_i, depth_margin, depth_arr + i) == 0) &&
(i && (ED_view3d_depth_read_cached_seg(
depths, mval_i, mval_prev, depth_margin + 1, depth_arr + i) == 0))) {
interp_depth = true;
}
else {
@ -1086,7 +1089,10 @@ static bool annotation_stroke_eraser_is_occluded(tGPsdata *p,
const int mval_i[2] = {x, y};
float mval_3d[3];
if (ED_view3d_autodist_simple(p->region, mval_i, mval_3d, 0, NULL)) {
float p_depth;
if (ED_view3d_depth_read_cached(p->depths, mval_i, 0, &p_depth)) {
ED_view3d_depth_unproject_v3(p->region, mval_i, (double)p_depth, mval_3d);
const float depth_mval = ED_view3d_calc_depth_for_comparison(rv3d, mval_3d);
const float depth_pt = ED_view3d_calc_depth_for_comparison(rv3d, &pt->x);
@ -1211,7 +1217,8 @@ static void annotation_stroke_doeraser(tGPsdata *p)
if (p->flags & GP_PAINTFLAG_V3D_ERASER_DEPTH) {
View3D *v3d = p->area->spacedata.first;
view3d_region_operator_needs_opengl(p->win, p->region);
ED_view3d_depth_override(p->depsgraph, p->region, v3d, NULL, V3D_DEPTH_NO_GPENCIL, NULL);
ED_view3d_depth_override(
p->depsgraph, p->region, v3d, NULL, V3D_DEPTH_NO_GPENCIL, &p->depths);
}
}
@ -1499,6 +1506,9 @@ static void annotation_session_cleanup(tGPsdata *p)
static void annotation_session_free(tGPsdata *p)
{
if (p->depths) {
ED_view3d_depths_free(p->depths);
}
MEM_freeN(p);
}

View File

@ -127,6 +127,8 @@ typedef struct tGPDfill {
struct bGPDstroke *gps_mouse;
/** Pointer to report messages. */
struct ReportList *reports;
/** For operations that require occlusion testing. */
struct ViewDepths *depths;
/** flags */
short flag;
/** avoid too fast events */
@ -1374,7 +1376,7 @@ static void gpencil_get_depth_array(tGPDfill *tgpf)
/* need to restore the original projection settings before packing up */
view3d_region_operator_needs_opengl(tgpf->win, tgpf->region);
ED_view3d_depth_override(
tgpf->depsgraph, tgpf->region, tgpf->v3d, NULL, V3D_DEPTH_NO_GPENCIL, NULL);
tgpf->depsgraph, tgpf->region, tgpf->v3d, NULL, V3D_DEPTH_NO_GPENCIL, &tgpf->depths);
/* Since strokes are so fine, when using their depth we need a margin
* otherwise they might get missed. */
@ -1385,6 +1387,7 @@ static void gpencil_get_depth_array(tGPDfill *tgpf)
int interp_depth = 0;
int found_depth = 0;
const ViewDepths *depths = tgpf->depths;
tgpf->depth_arr = MEM_mallocN(sizeof(float) * totpoints, "depth_points");
for (i = 0, ptc = tgpf->sbuffer; i < totpoints; i++, ptc++) {
@ -1392,11 +1395,9 @@ static void gpencil_get_depth_array(tGPDfill *tgpf)
int mval_i[2];
round_v2i_v2fl(mval_i, &ptc->x);
if ((ED_view3d_autodist_depth(tgpf->region, mval_i, depth_margin, tgpf->depth_arr + i) ==
0) &&
(i &&
(ED_view3d_autodist_depth_seg(
tgpf->region, mval_i, mval_prev, depth_margin + 1, tgpf->depth_arr + i) == 0))) {
if ((ED_view3d_depth_read_cached(depths, mval_i, depth_margin, tgpf->depth_arr + i) == 0) &&
(i && (ED_view3d_depth_read_cached_seg(
depths, mval_i, mval_prev, depth_margin + 1, tgpf->depth_arr + i) == 0))) {
interp_depth = true;
}
else {
@ -1771,6 +1772,11 @@ static void gpencil_fill_exit(bContext *C, wmOperator *op)
ED_region_draw_cb_exit(tgpf->region->type, tgpf->draw_handle_3d);
}
/* Remove depth buffer in cache. */
if (tgpf->depths) {
ED_view3d_depths_free(tgpf->depths);
}
/* finally, free memory used by temp data */
MEM_freeN(tgpf);
}

View File

@ -155,6 +155,8 @@ typedef struct tGPDprimitive {
struct Material *material;
/** current brush */
struct Brush *brush;
/** For operations that require occlusion testing. */
struct ViewDepths *depths;
/** Settings to pass to gp_points_to_xy(). */
GP_SpaceConversion gsc;

View File

@ -161,6 +161,8 @@ typedef struct tGPsdata {
ARegion *region;
/** needed for GP_STROKE_2DSPACE. */
View2D *v2d;
/** For operations that require occlusion testing. */
ViewDepths *depths;
/** for using the camera rect within the 3d view. */
rctf *subrect;
rctf subrect_data;
@ -1090,14 +1092,16 @@ static void gpencil_stroke_newfrombuffer(tGPsdata *p)
int found_depth = 0;
depth_arr = MEM_mallocN(sizeof(float) * gpd->runtime.sbuffer_used, "depth_points");
const ViewDepths *depths = p->depths;
int i;
for (i = 0, ptc = gpd->runtime.sbuffer; i < gpd->runtime.sbuffer_used; i++, ptc++, pt++) {
round_v2i_v2fl(mval_i, &ptc->x);
if ((ED_view3d_autodist_depth(p->region, mval_i, depth_margin, depth_arr + i) == 0) &&
(i && (ED_view3d_autodist_depth_seg(
p->region, mval_i, mval_prev, depth_margin + 1, depth_arr + i) == 0))) {
if ((ED_view3d_depth_read_cached(depths, mval_i, depth_margin, depth_arr + i) == 0) &&
(i && (ED_view3d_depth_read_cached_seg(
depths, mval_i, mval_prev, depth_margin + 1, depth_arr + i) == 0))) {
interp_depth = true;
}
else {
@ -1346,7 +1350,10 @@ static bool gpencil_stroke_eraser_is_occluded(
/* calculate difference matrix if parent object */
BKE_gpencil_layer_transform_matrix_get(p->depsgraph, obact, gpl, diff_mat);
if (ED_view3d_autodist_simple(p->region, mval_i, mval_3d, 0, NULL)) {
float p_depth;
if (ED_view3d_depth_read_cached(p->depths, mval_i, 0, &p_depth)) {
ED_view3d_depth_unproject_v3(p->region, mval_i, (double)p_depth, mval_3d);
const float depth_mval = ED_view3d_calc_depth_for_comparison(rv3d, mval_3d);
mul_v3_m4v3(fpt, diff_mat, &pt->x);
@ -1733,7 +1740,7 @@ static void gpencil_stroke_doeraser(tGPsdata *p)
if ((gp_settings != NULL) && (gp_settings->flag & GP_BRUSH_OCCLUDE_ERASER)) {
View3D *v3d = p->area->spacedata.first;
view3d_region_operator_needs_opengl(p->win, p->region);
ED_view3d_depth_override(p->depsgraph, p->region, v3d, NULL, V3D_DEPTH_NO_GPENCIL, NULL);
ED_view3d_depth_override(p->depsgraph, p->region, v3d, NULL, V3D_DEPTH_NO_GPENCIL, &p->depths);
}
/* loop over all layers too, since while it's easy to restrict editing to
@ -2087,6 +2094,9 @@ static void gpencil_session_free(tGPsdata *p)
if (p->rng != NULL) {
BLI_rng_free(p->rng);
}
if (p->depths != NULL) {
ED_view3d_depths_free(p->depths);
}
MEM_freeN(p);
}

View File

@ -795,15 +795,16 @@ static void gpencil_primitive_update_strokes(bContext *C, tGPDprimitive *tgpi)
(ts->gpencil_v3d_align & GP_PROJECT_DEPTH_STROKE) ?
V3D_DEPTH_GPENCIL_ONLY :
V3D_DEPTH_NO_GPENCIL,
NULL);
&tgpi->depths);
depth_arr = MEM_mallocN(sizeof(float) * gps->totpoints, "depth_points");
const ViewDepths *depths = tgpi->depths;
tGPspoint *ptc = &points2D[0];
for (int i = 0; i < gps->totpoints; i++, ptc++) {
round_v2i_v2fl(mval_i, &ptc->x);
if ((ED_view3d_autodist_depth(tgpi->region, mval_i, depth_margin, depth_arr + i) == 0) &&
(i && (ED_view3d_autodist_depth_seg(
tgpi->region, mval_i, mval_prev, depth_margin + 1, depth_arr + i) == 0))) {
if ((ED_view3d_depth_read_cached(depths, mval_i, depth_margin, depth_arr + i) == 0) &&
(i && (ED_view3d_depth_read_cached_seg(
depths, mval_i, mval_prev, depth_margin + 1, depth_arr + i) == 0))) {
interp_depth = true;
}
else {
@ -1154,6 +1155,11 @@ static void gpencil_primitive_exit(bContext *C, wmOperator *op)
BLI_rng_free(tgpi->rng);
}
/* Remove depth buffer in cache. */
if (tgpi->depths) {
ED_view3d_depths_free(tgpi->depths);
}
MEM_freeN(tgpi);
}

View File

@ -979,7 +979,7 @@ bool gpencil_point_xy_to_3d(const GP_SpaceConversion *gsc,
* to 3D coordinates.
*
* \param point2D: The screen-space 2D point data to convert.
* \param depth: Depth array (via #ED_view3d_autodist_depth()).
* \param depth: Depth array (via #ED_view3d_depth_read_cached()).
* \param r_out: The resulting 2D point data.
*/
void gpencil_stroke_convertcoords_tpoint(Scene *scene,

View File

@ -609,12 +609,8 @@ bool ED_view3d_autodist_simple(struct ARegion *region,
float mouse_worldloc[3],
int margin,
const float *force_depth);
bool ED_view3d_autodist_depth(struct ARegion *region, const int mval[2], int margin, float *depth);
bool ED_view3d_autodist_depth_seg(struct ARegion *region,
const int mval_sta[2],
const int mval_end[2],
int margin,
float *depth);
bool ED_view3d_depth_read_cached_seg(
const ViewDepths *vd, const int mval_sta[2], const int mval_end[2], int margin, float *depth);
/* select */
#define MAXPICKELEMS 2500

View File

@ -1094,17 +1094,10 @@ bool ED_view3d_autodist_simple(ARegion *region,
return ED_view3d_unproject_v3(region, centx, centy, depth, mouse_worldloc);
}
bool ED_view3d_autodist_depth(ARegion *region, const int mval[2], int margin, float *depth)
{
*depth = view_autodist_depth_margin(region, mval, margin);
return (*depth != FLT_MAX);
}
static bool depth_segment_cb(int x, int y, void *userData)
{
struct {
ARegion *region;
const ViewDepths *vd;
int margin;
float depth;
} *data = userData;
@ -1114,27 +1107,25 @@ static bool depth_segment_cb(int x, int y, void *userData)
mval[0] = x;
mval[1] = y;
depth = view_autodist_depth_margin(data->region, mval, data->margin);
if (depth != FLT_MAX) {
if (ED_view3d_depth_read_cached(data->vd, mval, data->margin, &depth)) {
data->depth = depth;
return false;
}
return true;
}
bool ED_view3d_autodist_depth_seg(
ARegion *region, const int mval_sta[2], const int mval_end[2], int margin, float *depth)
bool ED_view3d_depth_read_cached_seg(
const ViewDepths *vd, const int mval_sta[2], const int mval_end[2], int margin, float *depth)
{
struct {
ARegion *region;
const ViewDepths *vd;
int margin;
float depth;
} data = {NULL};
int p1[2];
int p2[2];
data.region = region;
data.vd = vd;
data.margin = margin;
data.depth = FLT_MAX;
@ -1691,6 +1682,8 @@ bool ED_view3d_depth_read_cached(const ViewDepths *vd,
return true;
}
/* GPencil and Anotations also need the returned depth value to be high so that it is invalid. */
*r_depth = FLT_MAX;
return false;
}