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:
-rw-r--r--source/blender/blenkernel/BKE_image.h2
-rw-r--r--source/blender/blenkernel/intern/image.c4
-rw-r--r--source/blender/blenkernel/intern/image_gpu.c146
-rw-r--r--source/blender/editors/render/render_internal.c134
-rw-r--r--source/blender/editors/space_image/image_edit.c3
-rw-r--r--source/blender/makesdna/DNA_image_types.h9
-rw-r--r--source/blender/makesrna/intern/rna_image.c2
7 files changed, 224 insertions, 76 deletions
diff --git a/source/blender/blenkernel/BKE_image.h b/source/blender/blenkernel/BKE_image.h
index 303945dbf64..c51a5f7e5e1 100644
--- a/source/blender/blenkernel/BKE_image.h
+++ b/source/blender/blenkernel/BKE_image.h
@@ -382,6 +382,8 @@ struct GPUTexture *BKE_image_get_gpu_tilemap(struct Image *image,
bool BKE_image_has_gpu_texture_premultiplied_alpha(struct Image *image, struct ImBuf *ibuf);
void BKE_image_update_gputexture(
struct Image *ima, struct ImageUser *iuser, int x, int y, int w, int h);
+void BKE_image_update_gputexture_delayed(
+ struct Image *ima, struct ImBuf *ibuf, int x, int y, int w, int h);
void BKE_image_paint_set_mipmap(struct Main *bmain, bool mipmap);
/* Delayed free of OpenGL buffers by main thread */
diff --git a/source/blender/blenkernel/intern/image.c b/source/blender/blenkernel/intern/image.c
index 228aed265cf..d1c6b552d2e 100644
--- a/source/blender/blenkernel/intern/image.c
+++ b/source/blender/blenkernel/intern/image.c
@@ -190,6 +190,7 @@ static void image_free_data(ID *id)
BKE_previewimg_free(&image->preview);
BLI_freelistN(&image->tiles);
+ BLI_freelistN(&image->gpu_refresh_areas);
}
static void image_foreach_cache(ID *id,
@@ -298,6 +299,8 @@ static void image_blend_read_data(BlendDataReader *reader, ID *id)
LISTBASE_FOREACH (ImageTile *, tile, &ima->tiles) {
tile->ok = IMA_OK;
}
+ ima->gpuflag = 0;
+ BLI_listbase_clear(&ima->gpu_refresh_areas);
}
static void image_blend_read_lib(BlendLibReader *UNUSED(reader), ID *id)
@@ -3897,6 +3900,7 @@ RenderResult *BKE_image_acquire_renderresult(Scene *scene, Image *ima)
}
else {
rr = BKE_image_get_renderslot(ima, ima->render_slot)->render;
+ ima->gpuflag |= IMA_GPU_REFRESH;
}
/* set proper views */
diff --git a/source/blender/blenkernel/intern/image_gpu.c b/source/blender/blenkernel/intern/image_gpu.c
index 50138b34fa3..05aa3c89a84 100644
--- a/source/blender/blenkernel/intern/image_gpu.c
+++ b/source/blender/blenkernel/intern/image_gpu.c
@@ -23,6 +23,7 @@
#include "MEM_guardedalloc.h"
+#include "BLI_bitmap.h"
#include "BLI_boxpack_2d.h"
#include "BLI_linklist.h"
#include "BLI_listbase.h"
@@ -48,6 +49,16 @@
/* Prototypes. */
static void gpu_free_unused_buffers(void);
static void image_free_gpu(Image *ima, const bool immediate);
+static void image_update_gputexture_ex(
+ Image *ima, ImageTile *tile, ImBuf *ibuf, int x, int y, int w, int h);
+
+/* Internal structs. */
+#define IMA_PARTIAL_REFRESH_TILE_SIZE 256
+typedef struct ImagePartialRefresh {
+ struct ImagePartialRefresh *next, *prev;
+ int tile_x;
+ int tile_y;
+} ImagePartialRefresh;
/* Is the alpha of the `GPUTexture` for a given image/ibuf premultiplied. */
bool BKE_image_has_gpu_texture_premultiplied_alpha(Image *image, ImBuf *ibuf)
@@ -299,19 +310,35 @@ static GPUTexture *image_get_gpu_texture(Image *ima,
* the current `pass` and `layer` should be 0. */
short requested_pass = iuser ? iuser->pass : 0;
short requested_layer = iuser ? iuser->layer : 0;
- short requested_slot = ima->render_slot;
- if (ima->gpu_pass != requested_pass || ima->gpu_layer != requested_layer ||
- ima->gpu_slot != requested_slot) {
+ if (ima->gpu_pass != requested_pass || ima->gpu_layer != requested_layer) {
ima->gpu_pass = requested_pass;
ima->gpu_layer = requested_layer;
- ima->gpu_slot = requested_slot;
ima->gpuflag |= IMA_GPU_REFRESH;
}
- /* currently, gpu refresh tagging is used by ima sequences */
- if (ima->gpuflag & IMA_GPU_REFRESH) {
+ /* Check if image has been updated and tagged to be updated (full or partial). */
+ ImageTile *tile = BKE_image_get_tile(ima, 0);
+ if (((ima->gpuflag & IMA_GPU_REFRESH) != 0) ||
+ ((ibuf == NULL || tile == NULL || !tile->ok) &&
+ ((ima->gpuflag & IMA_GPU_PARTIAL_REFRESH) != 0))) {
image_free_gpu(ima, true);
- ima->gpuflag &= ~IMA_GPU_REFRESH;
+ BLI_freelistN(&ima->gpu_refresh_areas);
+ ima->gpuflag &= ~(IMA_GPU_REFRESH | IMA_GPU_PARTIAL_REFRESH);
+ }
+ else if (ima->gpuflag & IMA_GPU_PARTIAL_REFRESH) {
+ BLI_assert(ibuf);
+ BLI_assert(tile && tile->ok);
+ ImagePartialRefresh *refresh_area;
+ while ((refresh_area = BLI_pophead(&ima->gpu_refresh_areas))) {
+ const int tile_offset_x = refresh_area->tile_x * IMA_PARTIAL_REFRESH_TILE_SIZE;
+ const int tile_offset_y = refresh_area->tile_y * IMA_PARTIAL_REFRESH_TILE_SIZE;
+ const int tile_width = MIN2(IMA_PARTIAL_REFRESH_TILE_SIZE, ibuf->x - tile_offset_x);
+ const int tile_height = MIN2(IMA_PARTIAL_REFRESH_TILE_SIZE, ibuf->y - tile_offset_y);
+ image_update_gputexture_ex(
+ ima, tile, ibuf, tile_offset_x, tile_offset_y, tile_width, tile_height);
+ MEM_freeN(refresh_area);
+ }
+ ima->gpuflag &= ~IMA_GPU_PARTIAL_REFRESH;
}
/* Tag as in active use for garbage collector. */
@@ -328,7 +355,6 @@ static GPUTexture *image_get_gpu_texture(Image *ima,
/* Check if we have a valid image. If not, we return a dummy
* texture with zero bind-code so we don't keep trying. */
- ImageTile *tile = BKE_image_get_tile(ima, 0);
if (tile == NULL || tile->ok == 0) {
*tex = image_gpu_texture_error_create(textarget);
return *tex;
@@ -590,8 +616,8 @@ static void gpu_texture_update_scaled(GPUTexture *tex,
}
else {
/* Partial update with scaling. */
- int limit_w = smaller_power_of_2_limit(full_w);
- int limit_h = smaller_power_of_2_limit(full_h);
+ int limit_w = GPU_texture_width(tex);
+ int limit_h = GPU_texture_height(tex);
ibuf = update_do_scale(rect, rect_float, &x, &y, &w, &h, limit_w, limit_h, full_w, full_h);
}
@@ -643,7 +669,7 @@ static void gpu_texture_update_from_ibuf(
scaled = (ibuf->x != tilesize[0]) || (ibuf->y != tilesize[1]);
}
else {
- scaled = is_over_resolution_limit(ibuf->x, ibuf->y);
+ scaled = (GPU_texture_width(tex) != ibuf->x) || (GPU_texture_height(tex) != ibuf->y);
}
if (scaled) {
@@ -746,6 +772,22 @@ static void gpu_texture_update_from_ibuf(
GPU_texture_unbind(tex);
}
+static void image_update_gputexture_ex(
+ Image *ima, ImageTile *tile, ImBuf *ibuf, int x, int y, int w, int h)
+{
+ GPUTexture *tex = ima->gputexture[TEXTARGET_2D][0];
+ /* Check if we need to update the main gputexture. */
+ if (tex != NULL && tile == ima->tiles.first) {
+ gpu_texture_update_from_ibuf(tex, ima, ibuf, NULL, x, y, w, h);
+ }
+
+ /* Check if we need to update the array gputexture. */
+ tex = ima->gputexture[TEXTARGET_2D_ARRAY][0];
+ if (tex != NULL) {
+ gpu_texture_update_from_ibuf(tex, ima, ibuf, tile, x, y, w, h);
+ }
+}
+
/* Partial update of texture for texture painting. This is often much
* quicker than fully updating the texture for high resolution images. */
void BKE_image_update_gputexture(Image *ima, ImageUser *iuser, int x, int y, int w, int h)
@@ -757,20 +799,84 @@ void BKE_image_update_gputexture(Image *ima, ImageUser *iuser, int x, int y, int
/* Full reload of texture. */
BKE_image_free_gputextures(ima);
}
+ image_update_gputexture_ex(ima, tile, ibuf, x, y, w, h);
+ BKE_image_release_ibuf(ima, ibuf, NULL);
+}
- GPUTexture *tex = ima->gputexture[TEXTARGET_2D][0];
- /* Check if we need to update the main gputexture. */
- if (tex != NULL && tile == ima->tiles.first) {
- gpu_texture_update_from_ibuf(tex, ima, ibuf, NULL, x, y, w, h);
+/* Mark areas on the GPUTexture that needs to be updated. The areas are marked in chunks.
+ * The next time the GPUTexture is used these tiles will be refreshes. This saves time
+ * when writing to the same place multiple times This happens for during foreground
+ * rendering. */
+void BKE_image_update_gputexture_delayed(
+ struct Image *ima, struct ImBuf *ibuf, int x, int y, int w, int h)
+{
+ /* Check for full refresh. */
+ if (ibuf && x == 0 && y == 0 && w == ibuf->x && h == ibuf->y) {
+ ima->gpuflag |= IMA_GPU_REFRESH;
+ }
+ /* Check if we can promote partial refresh to a full refresh. */
+ if ((ima->gpuflag & (IMA_GPU_REFRESH | IMA_GPU_PARTIAL_REFRESH)) ==
+ (IMA_GPU_REFRESH | IMA_GPU_PARTIAL_REFRESH)) {
+ ima->gpuflag &= ~IMA_GPU_PARTIAL_REFRESH;
+ BLI_freelistN(&ima->gpu_refresh_areas);
+ }
+ /* Image is already marked for complete refresh. */
+ if (ima->gpuflag & IMA_GPU_REFRESH) {
+ return;
}
- /* Check if we need to update the array gputexture. */
- tex = ima->gputexture[TEXTARGET_2D_ARRAY][0];
- if (tex != NULL) {
- gpu_texture_update_from_ibuf(tex, ima, ibuf, tile, x, y, w, h);
+ /* Schedule the tiles that covers the requested area. */
+ const int start_tile_x = x / IMA_PARTIAL_REFRESH_TILE_SIZE;
+ const int start_tile_y = y / IMA_PARTIAL_REFRESH_TILE_SIZE;
+ const int end_tile_x = (x + w) / IMA_PARTIAL_REFRESH_TILE_SIZE;
+ const int end_tile_y = (y + h) / IMA_PARTIAL_REFRESH_TILE_SIZE;
+ const int num_tiles_x = (end_tile_x + 1) - (start_tile_x);
+ const int num_tiles_y = (end_tile_y + 1) - (start_tile_y);
+ const int num_tiles = num_tiles_x * num_tiles_y;
+ const bool allocate_on_heap = BLI_BITMAP_SIZE(num_tiles) > 16;
+ BLI_bitmap *requested_tiles = NULL;
+ if (allocate_on_heap) {
+ requested_tiles = BLI_BITMAP_NEW(num_tiles, __func__);
+ }
+ else {
+ requested_tiles = BLI_BITMAP_NEW_ALLOCA(num_tiles);
}
- BKE_image_release_ibuf(ima, ibuf, NULL);
+ /* Mark the tiles that have already been requested. They don't need to be requested again. */
+ int num_tiles_not_scheduled = num_tiles;
+ LISTBASE_FOREACH (ImagePartialRefresh *, area, &ima->gpu_refresh_areas) {
+ if (area->tile_x < start_tile_x || area->tile_x > end_tile_x || area->tile_y < start_tile_y ||
+ area->tile_y > end_tile_y) {
+ continue;
+ }
+ int requested_tile_index = (area->tile_x - start_tile_x) +
+ (area->tile_y - start_tile_y) * num_tiles_x;
+ BLI_BITMAP_ENABLE(requested_tiles, requested_tile_index);
+ num_tiles_not_scheduled--;
+ if (num_tiles_not_scheduled == 0) {
+ break;
+ }
+ }
+
+ /* Schedule the tiles that aren't requested yet. */
+ if (num_tiles_not_scheduled) {
+ int tile_index = 0;
+ for (int tile_y = start_tile_y; tile_y <= end_tile_y; tile_y++) {
+ for (int tile_x = start_tile_x; tile_x <= end_tile_x; tile_x++) {
+ if (!BLI_BITMAP_TEST_BOOL(requested_tiles, tile_index)) {
+ ImagePartialRefresh *area = MEM_mallocN(sizeof(ImagePartialRefresh), __func__);
+ area->tile_x = tile_x;
+ area->tile_y = tile_y;
+ BLI_addtail(&ima->gpu_refresh_areas, area);
+ }
+ tile_index++;
+ }
+ }
+ ima->gpuflag |= IMA_GPU_PARTIAL_REFRESH;
+ }
+ if (allocate_on_heap) {
+ MEM_freeN(requested_tiles);
+ }
}
/* these two functions are called on entering and exiting texture paint mode,
diff --git a/source/blender/editors/render/render_internal.c b/source/blender/editors/render/render_internal.c
index a035ee3e342..69c1e887392 100644
--- a/source/blender/editors/render/render_internal.c
+++ b/source/blender/editors/render/render_internal.c
@@ -29,6 +29,7 @@
#include "BLI_listbase.h"
#include "BLI_math.h"
+#include "BLI_rect.h"
#include "BLI_threads.h"
#include "BLI_timecode.h"
#include "BLI_utildefines.h"
@@ -121,72 +122,90 @@ typedef struct RenderJob {
} RenderJob;
/* called inside thread! */
-static void image_buffer_rect_update(RenderJob *rj,
- RenderResult *rr,
- ImBuf *ibuf,
- ImageUser *iuser,
- volatile rcti *renrect,
- const char *viewname)
+static bool image_buffer_calc_tile_rect(const RenderResult *rr,
+ const ImBuf *ibuf,
+ volatile rcti *renrect,
+ rcti *r_ibuf_rect,
+ int *r_offset_x,
+ int *r_offset_y)
{
- Scene *scene = rj->scene;
- const float *rectf = NULL;
- int ymin, ymax, xmin, xmax;
- int rymin, rxmin;
- int linear_stride, linear_offset_x, linear_offset_y;
- ColorManagedViewSettings *view_settings;
- ColorManagedDisplaySettings *display_settings;
-
- if (ibuf->userflags & IB_DISPLAY_BUFFER_INVALID) {
- /* The whole image buffer it so be color managed again anyway. */
- return;
- }
+ int tile_y, tile_height, tile_x, tile_width;
/* if renrect argument, we only refresh scanlines */
if (renrect) {
- /* if (ymax == recty), rendering of layer is ready,
+ /* if (tile_height == recty), rendering of layer is ready,
* we should not draw, other things happen... */
if (rr->renlay == NULL || renrect->ymax >= rr->recty) {
- return;
+ return false;
}
- /* xmin here is first subrect x coord, xmax defines subrect width */
- xmin = renrect->xmin;
- xmax = renrect->xmax - xmin;
- if (xmax < 2) {
- return;
+ /* tile_x here is first subrect x coord, tile_width defines subrect width */
+ tile_x = renrect->xmin;
+ tile_width = renrect->xmax - tile_x;
+ if (tile_width < 2) {
+ return false;
}
- ymin = renrect->ymin;
- ymax = renrect->ymax - ymin;
- if (ymax < 2) {
- return;
+ tile_y = renrect->ymin;
+ tile_height = renrect->ymax - tile_y;
+ if (tile_height < 2) {
+ return false;
}
renrect->ymin = renrect->ymax;
}
else {
- xmin = ymin = 0;
- xmax = rr->rectx;
- ymax = rr->recty;
+ tile_x = tile_y = 0;
+ tile_width = rr->rectx;
+ tile_height = rr->recty;
}
- /* xmin ymin is in tile coords. transform to ibuf */
- rxmin = rr->tilerect.xmin;
- if (rxmin >= ibuf->x) {
- return;
+ /* tile_x tile_y is in tile coords. transform to ibuf */
+ int offset_x = rr->tilerect.xmin;
+ if (offset_x >= ibuf->x) {
+ return false;
}
- rymin = rr->tilerect.ymin;
- if (rymin >= ibuf->y) {
- return;
+ int offset_y = rr->tilerect.ymin;
+ if (offset_y >= ibuf->y) {
+ return false;
}
- if (rxmin + xmax > ibuf->x) {
- xmax = ibuf->x - rxmin;
+ if (offset_x + tile_width > ibuf->x) {
+ tile_width = ibuf->x - offset_x;
}
- if (rymin + ymax > ibuf->y) {
- ymax = ibuf->y - rymin;
+ if (offset_y + tile_height > ibuf->y) {
+ tile_height = ibuf->y - offset_y;
}
- if (xmax < 1 || ymax < 1) {
+ if (tile_width < 1 || tile_height < 1) {
+ return false;
+ }
+
+ r_ibuf_rect->xmax = tile_x + tile_width;
+ r_ibuf_rect->ymax = tile_y + tile_height;
+ r_ibuf_rect->xmin = tile_x;
+ r_ibuf_rect->ymin = tile_y;
+ *r_offset_x = offset_x;
+ *r_offset_y = offset_y;
+ return true;
+}
+
+static void image_buffer_rect_update(RenderJob *rj,
+ RenderResult *rr,
+ ImBuf *ibuf,
+ ImageUser *iuser,
+ const rcti *tile_rect,
+ int offset_x,
+ int offset_y,
+ const char *viewname)
+{
+ Scene *scene = rj->scene;
+ const float *rectf = NULL;
+ int linear_stride, linear_offset_x, linear_offset_y;
+ ColorManagedViewSettings *view_settings;
+ ColorManagedDisplaySettings *display_settings;
+
+ if (ibuf->userflags & IB_DISPLAY_BUFFER_INVALID) {
+ /* The whole image buffer is to be color managed again anyway. */
return;
}
@@ -230,10 +249,10 @@ static void image_buffer_rect_update(RenderJob *rj,
return;
}
- rectf += 4 * (rr->rectx * ymin + xmin);
+ rectf += 4 * (rr->rectx * tile_rect->ymin + tile_rect->xmin);
linear_stride = rr->rectx;
- linear_offset_x = rxmin;
- linear_offset_y = rymin;
+ linear_offset_x = offset_x;
+ linear_offset_y = offset_y;
}
else {
rectf = ibuf->rect_float;
@@ -253,10 +272,10 @@ static void image_buffer_rect_update(RenderJob *rj,
linear_offset_y,
view_settings,
display_settings,
- rxmin,
- rymin,
- rxmin + xmax,
- rymin + ymax);
+ offset_x,
+ offset_y,
+ offset_x + BLI_rcti_size_x(tile_rect),
+ offset_y + BLI_rcti_size_y(tile_rect));
}
/* ****************************** render invoking ***************** */
@@ -578,8 +597,16 @@ static void image_rect_update(void *rjv, RenderResult *rr, volatile rcti *renrec
/* update part of render */
render_image_update_pass_and_layer(rj, rr, &rj->iuser);
+ rcti tile_rect;
+ int offset_x;
+ int offset_y;
ibuf = BKE_image_acquire_ibuf(ima, &rj->iuser, &lock);
if (ibuf) {
+ if (!image_buffer_calc_tile_rect(rr, ibuf, renrect, &tile_rect, &offset_x, &offset_y)) {
+ BKE_image_release_ibuf(ima, ibuf, lock);
+ return;
+ }
+
/* Don't waste time on CPU side color management if
* image will be displayed using GLSL.
*
@@ -589,9 +616,10 @@ static void image_rect_update(void *rjv, RenderResult *rr, volatile rcti *renrec
*/
if (!rj->supports_glsl_draw || ibuf->channels == 1 ||
ED_draw_imbuf_method(ibuf) != IMAGE_DRAW_METHOD_GLSL) {
- image_buffer_rect_update(rj, rr, ibuf, &rj->iuser, renrect, viewname);
+ image_buffer_rect_update(rj, rr, ibuf, &rj->iuser, &tile_rect, offset_x, offset_y, viewname);
}
- ima->gpuflag |= IMA_GPU_REFRESH;
+ BKE_image_update_gputexture_delayed(
+ ima, ibuf, offset_x, offset_y, BLI_rcti_size_x(&tile_rect), BLI_rcti_size_y(&tile_rect));
/* make jobs timer to send notifier */
*(rj->do_update) = true;
diff --git a/source/blender/editors/space_image/image_edit.c b/source/blender/editors/space_image/image_edit.c
index 180f1fb183c..c26f92c5463 100644
--- a/source/blender/editors/space_image/image_edit.c
+++ b/source/blender/editors/space_image/image_edit.c
@@ -407,6 +407,9 @@ bool ED_image_slot_cycle(struct Image *image, int direction)
image->render_slot = ((cur == 1) ? 0 : 1);
}
+ if ((cur != image->render_slot)) {
+ image->gpuflag |= IMA_GPU_REFRESH;
+ }
return (cur != image->render_slot);
}
diff --git a/source/blender/makesdna/DNA_image_types.h b/source/blender/makesdna/DNA_image_types.h
index 4edf06f90f7..eb03e047c90 100644
--- a/source/blender/makesdna/DNA_image_types.h
+++ b/source/blender/makesdna/DNA_image_types.h
@@ -151,12 +151,13 @@ typedef struct Image {
int lastframe;
/* GPU texture flag. */
+ /* Contains `ImagePartialRefresh`. */
+ ListBase gpu_refresh_areas;
int gpuframenr;
short gpuflag;
short gpu_pass;
short gpu_layer;
- short gpu_slot;
- char _pad2[4];
+ char _pad2[6];
/** Deprecated. */
struct PackedFile *packedfile DNA_DEPRECATED;
@@ -223,8 +224,10 @@ enum {
enum {
/** GPU texture needs to be refreshed. */
IMA_GPU_REFRESH = (1 << 0),
+ /** GPU texture needs to be partially refreshed. */
+ IMA_GPU_PARTIAL_REFRESH = (1 << 1),
/** All mipmap levels in OpenGL texture set? */
- IMA_GPU_MIPMAP_COMPLETE = (1 << 1),
+ IMA_GPU_MIPMAP_COMPLETE = (1 << 2),
};
/* Image.source, where the image comes from */
diff --git a/source/blender/makesrna/intern/rna_image.c b/source/blender/makesrna/intern/rna_image.c
index 2373045d95a..cf4b343e2f7 100644
--- a/source/blender/makesrna/intern/rna_image.c
+++ b/source/blender/makesrna/intern/rna_image.c
@@ -598,6 +598,7 @@ static void rna_render_slots_active_set(PointerRNA *ptr,
int index = BLI_findindex(&image->renderslots, slot);
if (index != -1) {
image->render_slot = index;
+ image->gpuflag |= IMA_GPU_REFRESH;
}
}
}
@@ -613,6 +614,7 @@ static void rna_render_slots_active_index_set(PointerRNA *ptr, int value)
Image *image = (Image *)ptr->owner_id;
int num_slots = BLI_listbase_count(&image->renderslots);
image->render_slot = value;
+ image->gpuflag |= IMA_GPU_REFRESH;
CLAMP(image->render_slot, 0, num_slots - 1);
}