Speedup of regular 2D painting

Yet another commit which makes painting aware of multi-threaded systems.
This commit is contained in:
Sergey Sharybin 2016-05-06 11:48:07 +02:00
parent 204f55c189
commit ef0c02cb4d
4 changed files with 211 additions and 68 deletions

View File

@ -42,6 +42,7 @@
#include "BLI_math_color_blend.h"
#include "BLI_stack.h"
#include "BLI_bitmap.h"
#include "BLI_task.h"
#include "BKE_context.h"
#include "BKE_depsgraph.h"
@ -1019,6 +1020,64 @@ static void paint_2d_convert_brushco(ImBuf *ibufb, const float pos[2], int ipos[
ipos[1] = (int)floorf((pos[1] - ibufb->y / 2));
}
static void paint_2d_do_making_brush(ImagePaintState *s,
ImagePaintRegion *region,
unsigned short *curveb,
unsigned short *texmaskb,
ImBuf *frombuf,
float mask_max,
short blend,
int tilex, int tiley,
int tilew, int tileh)
{
ImBuf tmpbuf;
IMB_initImBuf(&tmpbuf, IMAPAINT_TILE_SIZE, IMAPAINT_TILE_SIZE, 32, 0);
for (int ty = tiley; ty <= tileh; ty++) {
for (int tx = tilex; tx <= tilew; tx++) {
/* retrieve original pixels + mask from undo buffer */
unsigned short *mask;
int origx = region->destx - tx * IMAPAINT_TILE_SIZE;
int origy = region->desty - ty * IMAPAINT_TILE_SIZE;
if (s->canvas->rect_float)
tmpbuf.rect_float = image_undo_find_tile(s->image, s->canvas, tx, ty, &mask, false);
else
tmpbuf.rect = image_undo_find_tile(s->image, s->canvas, tx, ty, &mask, false);
IMB_rectblend(s->canvas, &tmpbuf, frombuf, mask,
curveb, texmaskb, mask_max,
region->destx, region->desty,
origx, origy,
region->srcx, region->srcy,
region->width, region->height,
blend, ((s->brush->flag & BRUSH_ACCUMULATE) != 0));
}
}
}
typedef struct Paint2DForeachData {
ImagePaintState *s;
ImagePaintRegion *region;
unsigned short *curveb;
unsigned short *texmaskb;
ImBuf *frombuf;
float mask_max;
short blend;
int tilex;
int tilew;
} Paint2DForeachData;
static void paint_2d_op_foreach_do(void *data_v, const int iter)
{
Paint2DForeachData *data = (Paint2DForeachData *)data_v;
paint_2d_do_making_brush(data->s, data->region, data->curveb,
data->texmaskb, data->frombuf, data->mask_max,
data->blend,
data->tilex, iter,
data->tilew, iter);
}
static int paint_2d_op(void *state, ImBuf *ibufb, unsigned short *curveb, unsigned short *texmaskb, const float lastpos[2], const float pos[2])
{
ImagePaintState *s = ((ImagePaintState *)state);
@ -1072,45 +1131,40 @@ static int paint_2d_op(void *state, ImBuf *ibufb, unsigned short *curveb, unsign
if (s->do_masking) {
/* masking, find original pixels tiles from undo buffer to composite over */
int tilex, tiley, tilew, tileh, tx, ty;
ImBuf *tmpbuf;
int tilex, tiley, tilew, tileh;
imapaint_region_tiles(s->canvas, region[a].destx, region[a].desty,
region[a].width, region[a].height,
&tilex, &tiley, &tilew, &tileh);
tmpbuf = IMB_allocImBuf(IMAPAINT_TILE_SIZE, IMAPAINT_TILE_SIZE, 32, 0);
for (ty = tiley; ty <= tileh; ty++) {
for (tx = tilex; tx <= tilew; tx++) {
/* retrieve original pixels + mask from undo buffer */
unsigned short *mask;
int origx = region[a].destx - tx * IMAPAINT_TILE_SIZE;
int origy = region[a].desty - ty * IMAPAINT_TILE_SIZE;
if (s->canvas->rect_float)
tmpbuf->rect_float = image_undo_find_tile(s->image, s->canvas, tx, ty, &mask, false);
else
tmpbuf->rect = image_undo_find_tile(s->image, s->canvas, tx, ty, &mask, false);
IMB_rectblend(s->canvas, tmpbuf, frombuf, mask,
curveb, texmaskb, mask_max,
region[a].destx, region[a].desty,
origx, origy,
region[a].srcx, region[a].srcy,
region[a].width, region[a].height, blend, ((s->brush->flag & BRUSH_ACCUMULATE) != 0));
}
if (tiley == tileh) {
paint_2d_do_making_brush(s, &region[a], curveb, texmaskb, frombuf,
mask_max, blend, tilex, tiley, tilew, tileh);
}
else {
Paint2DForeachData data;
data.s = s;
data.region = &region[a];
data.curveb = curveb;
data.texmaskb = texmaskb;
data.frombuf = frombuf;
data.mask_max = mask_max;
data.blend = blend;
data.tilex = tilex;
data.tilew = tilew;
BLI_task_parallel_range(tiley, tileh + 1, &data,
paint_2d_op_foreach_do,
true);
IMB_freeImBuf(tmpbuf);
}
}
else {
/* no masking, composite brush directly onto canvas */
IMB_rectblend(s->canvas, s->canvas, frombuf, NULL, curveb, texmaskb, mask_max,
region[a].destx, region[a].desty,
region[a].destx, region[a].desty,
region[a].srcx, region[a].srcy,
region[a].width, region[a].height, blend, false);
IMB_rectblend_threaded(s->canvas, s->canvas, frombuf, NULL, curveb, texmaskb, mask_max,
region[a].destx, region[a].desty,
region[a].destx, region[a].desty,
region[a].srcx, region[a].srcy,
region[a].width, region[a].height, blend, false);
}
}

View File

@ -130,7 +130,18 @@ void IMB_freeImBuf(struct ImBuf *ibuf);
* \attention Defined in allocimbuf.c
*/
struct ImBuf *IMB_allocImBuf(unsigned int x, unsigned int y,
unsigned char d, unsigned int flags);
unsigned char planes, unsigned int flags);
/**
* Initialize given ImBuf.
*
* Use in cases when temporary image buffer is allocated on stack.
*
* \attention Defined in allocimbuf.c
*/
bool IMB_initImBuf(struct ImBuf *ibuf,
unsigned int x, unsigned int y,
unsigned char planes, unsigned int flags);
/**
* Create a copy of a pixel buffer and wrap it to a new ImBuf
@ -213,6 +224,10 @@ void IMB_rectblend(struct ImBuf *dbuf, struct ImBuf *obuf, struct ImBuf *sbuf,
unsigned short *dmask, unsigned short *curvemask, unsigned short *mmask, float mask_max,
int destx, int desty, int origx, int origy, int srcx, int srcy,
int width, int height, IMB_BlendMode mode, bool accumulate);
void IMB_rectblend_threaded(struct ImBuf *dbuf, struct ImBuf *obuf, struct ImBuf *sbuf,
unsigned short *dmask, unsigned short *curvemask, unsigned short *mmask, float mask_max,
int destx, int desty, int origx, int origy, int srcx, int srcy,
int width, int height, IMB_BlendMode mode, bool accumulate);
/**
*

View File

@ -446,51 +446,62 @@ ImBuf *IMB_allocImBuf(unsigned int x, unsigned int y, uchar planes, unsigned int
{
ImBuf *ibuf;
ibuf = MEM_callocN(sizeof(ImBuf), "ImBuf_struct");
ibuf = MEM_mallocN(sizeof(ImBuf), "ImBuf_struct");
if (ibuf) {
ibuf->x = x;
ibuf->y = y;
ibuf->planes = planes;
ibuf->ftype = IMB_FTYPE_PNG;
ibuf->foptions.quality = 15; /* the 15 means, set compression to low ratio but not time consuming */
ibuf->channels = 4; /* float option, is set to other values when buffers get assigned */
ibuf->ppm[0] = ibuf->ppm[1] = IMB_DPI_DEFAULT / 0.0254f; /* IMB_DPI_DEFAULT -> pixels-per-meter */
if (flags & IB_rect) {
if (imb_addrectImBuf(ibuf) == false) {
IMB_freeImBuf(ibuf);
return NULL;
}
if (!IMB_initImBuf(ibuf, x, y, planes, flags)) {
IMB_freeImBuf(ibuf);
return NULL;
}
if (flags & IB_rectfloat) {
if (imb_addrectfloatImBuf(ibuf) == false) {
IMB_freeImBuf(ibuf);
return NULL;
}
}
if (flags & IB_zbuf) {
if (addzbufImBuf(ibuf) == false) {
IMB_freeImBuf(ibuf);
return NULL;
}
}
if (flags & IB_zbuffloat) {
if (addzbuffloatImBuf(ibuf) == false) {
IMB_freeImBuf(ibuf);
return NULL;
}
}
/* assign default spaces */
colormanage_imbuf_set_default_spaces(ibuf);
}
return (ibuf);
}
bool IMB_initImBuf(struct ImBuf *ibuf,
unsigned int x, unsigned int y,
unsigned char planes, unsigned int flags)
{
memset(ibuf, 0, sizeof(ImBuf));
ibuf->x = x;
ibuf->y = y;
ibuf->planes = planes;
ibuf->ftype = IMB_FTYPE_PNG;
ibuf->foptions.quality = 15; /* the 15 means, set compression to low ratio but not time consuming */
ibuf->channels = 4; /* float option, is set to other values when buffers get assigned */
ibuf->ppm[0] = ibuf->ppm[1] = IMB_DPI_DEFAULT / 0.0254f; /* IMB_DPI_DEFAULT -> pixels-per-meter */
if (flags & IB_rect) {
if (imb_addrectImBuf(ibuf) == false) {
return false;
}
}
if (flags & IB_rectfloat) {
if (imb_addrectfloatImBuf(ibuf) == false) {
return false;
}
}
if (flags & IB_zbuf) {
if (addzbufImBuf(ibuf) == false) {
return false;
}
}
if (flags & IB_zbuffloat) {
if (addzbuffloatImBuf(ibuf) == false) {
return false;
}
}
/* assign default spaces */
colormanage_imbuf_set_default_spaces(ibuf);
return true;
}
/* does no zbuffers? */
ImBuf *IMB_dupImBuf(ImBuf *ibuf1)
{

View File

@ -693,6 +693,69 @@ void IMB_rectblend(ImBuf *dbuf, ImBuf *obuf, ImBuf *sbuf, unsigned short *dmask,
}
}
typedef struct RectBlendThreadData {
ImBuf *dbuf, *obuf, *sbuf;
unsigned short *dmask, *curvemask, *texmask;
float mask_max;
int destx, desty, origx, origy;
int srcx, srcy, width;
IMB_BlendMode mode;
bool accumulate;
} RectBlendThreadData;
static void rectblend_thread_do(void *data_v,
int start_scanline,
int num_scanlines)
{
RectBlendThreadData *data = (RectBlendThreadData *)data_v;
IMB_rectblend(data->dbuf, data->obuf, data->sbuf,
data->dmask, data->curvemask, data->texmask,
data->mask_max,
data->destx,
data->desty + start_scanline,
data->origx,
data->origy + start_scanline,
data->srcx,
data->srcy + start_scanline,
data->width, num_scanlines,
data->mode, data->accumulate);
}
void IMB_rectblend_threaded(ImBuf *dbuf, ImBuf *obuf, ImBuf *sbuf,
unsigned short *dmask, unsigned short *curvemask,
unsigned short *texmask, float mask_max,
int destx, int desty, int origx, int origy,
int srcx, int srcy, int width, int height,
IMB_BlendMode mode, bool accumulate)
{
if (((size_t)width) * height < 64 * 64) {
IMB_rectblend(dbuf, obuf, sbuf, dmask, curvemask, texmask,
mask_max, destx, desty, origx, origy,
srcx, srcy, width, height, mode, accumulate);
}
else {
RectBlendThreadData data;
data.dbuf = dbuf;
data.obuf = obuf;
data.sbuf = sbuf;
data.dmask = dmask;
data.curvemask = curvemask;
data.texmask = texmask;
data.mask_max = mask_max;
data.destx = destx;
data.desty = desty;
data.origx = origx;
data.origy = origy;
data.srcx = srcx;
data.srcy = srcy;
data.width = width;
data.mode = mode;
data.accumulate = accumulate;
IMB_processor_apply_threaded_scanlines(
height, rectblend_thread_do, &data);
}
}
/* fill */
void IMB_rectfill(ImBuf *drect, const float col[4])