Commit: 2ffc9b72ad8db4bc2565119c0a74dbfab9a76e3d
Author: Jeroen Bakker
Date: Wed Dec 7 15:09:25 2022 +0100
Branches: master
https://developer.blender.org/rB2ffc9b72ad8db4bc2565119c0a74dbfab9a76e3d
Draw: Improve performance image engine.
Image engine is used to draw the image inside the image editor, uv editor and
node editor. The
performance during scrolling wasn't smooth when using larger textures on a
dedicated GPU. Main
reason was the data transfers that happens when panning the image.
The original idea of the image engine was to have 4 textures that are as large
as the editor.
Those textures would be used to simulate a larger canvas where if the texture
is out of the
visible area the texture would be reused to contain the data of a new visible
area. This would
reduce the data transfers to only on certain x/y coordinates. Between those
coordinates no
data transfers would be needed.
This patch implements the mechanism described above. During development other
areas to
improve have been detected (incorrect color management for float textures,
using different
image formats to reduce data transfer bandwidths, using different render
techniques for
images upto 8k). More improvements will follow.
===================================================================
M source/blender/draw/engines/image/image_buffer_cache.hh
M source/blender/draw/engines/image/image_drawing_mode.hh
M source/blender/draw/engines/image/image_engine.cc
M source/blender/draw/engines/image/image_instance_data.hh
M source/blender/draw/engines/image/image_texture_info.hh
M source/blender/draw/engines/image/shaders/image_engine_color_frag.glsl
M source/blender/draw/engines/image/shaders/infos/engine_image_info.hh
===================================================================
diff --git a/source/blender/draw/engines/image/image_buffer_cache.hh
b/source/blender/draw/engines/image/image_buffer_cache.hh
index 470e9f225b4..266476cf8aa 100644
--- a/source/blender/draw/engines/image/image_buffer_cache.hh
+++ b/source/blender/draw/engines/image/image_buffer_cache.hh
@@ -49,12 +49,26 @@ struct FloatImageBuffer {
}
};
+/**
+ * \brief Float buffer cache for image buffers.
+ *
+ * Image buffers might not have float buffers which are required for the image
engine.
+ * Image buffers are not allowed to have both a float buffer and a byte buffer
as some
+ * functionality doesn't know what to do.
+ *
+ * For this reason we store the float buffer in separate image buffers. The
FloatBufferCache keep
+ * track of the cached buffers and if they are still used.
+ *
+ * TODO: When an image buffer has a float buffer but not stored in scene
linear, it currently
+ * doesn't apply color management. In this case we still need to create
another float buffer, but
+ * with the buffer converted to scene linear.
+ */
struct FloatBufferCache {
private:
blender::Vector<FloatImageBuffer> cache_;
public:
- ImBuf *ensure_float_buffer(ImBuf *image_buffer)
+ ImBuf *cached_float_buffer(ImBuf *image_buffer)
{
/* Check if we can use the float buffer of the given image_buffer. */
if (image_buffer->rect_float != nullptr) {
diff --git a/source/blender/draw/engines/image/image_drawing_mode.hh
b/source/blender/draw/engines/image/image_drawing_mode.hh
index 8913b7469ed..70e8fc3b47a 100644
--- a/source/blender/draw/engines/image/image_drawing_mode.hh
+++ b/source/blender/draw/engines/image/image_drawing_mode.hh
@@ -22,52 +22,117 @@ namespace blender::draw::image_engine {
constexpr float EPSILON_UV_BOUNDS = 0.00001f;
/**
- * \brief Screen space method using a single texture spawning the whole screen.
+ * \brief Screen space method using a 4 textures spawning the whole screen.
*/
-struct OneTextureMethod {
+struct FullScreenTextures {
IMAGE_InstanceData *instance_data;
- OneTextureMethod(IMAGE_InstanceData *instance_data) :
instance_data(instance_data)
+ FullScreenTextures(IMAGE_InstanceData *instance_data) :
instance_data(instance_data)
{
}
- /** \brief Update the texture slot uv and screen space bounds. */
- void update_screen_space_bounds(const ARegion *region)
+ /**
+ * \brief Update the uv and region bounds of all texture_infos of
instance_data.
+ */
+ void update_bounds(const ARegion *region)
{
- /* Create a single texture that covers the visible screen space. */
+ // determine uv_area of the region.
+ float4x4 mat = float4x4(instance_data->ss_to_texture).inverted();
+ float2 region_uv_min = float2(mat * float3(0.0f, 0.0f, 0.0f));
+ float2 region_uv_max = float2(mat * float3(1.0f, 1.0f, 0.0f));
+ float2 region_uv_span = region_uv_max - region_uv_min;
+ rctf region_uv_bounds;
BLI_rctf_init(
- &instance_data->texture_infos[0].clipping_bounds, 0, region->winx, 0,
region->winy);
- instance_data->texture_infos[0].visible = true;
-
- /* Mark the other textures as invalid. */
- for (int i = 1; i < SCREEN_SPACE_DRAWING_MODE_TEXTURE_LEN; i++) {
- BLI_rctf_init_minmax(&instance_data->texture_infos[i].clipping_bounds);
- instance_data->texture_infos[i].visible = false;
+ ®ion_uv_bounds, region_uv_min.x, region_uv_max.x, region_uv_min.y,
region_uv_max.y);
+
+ /* Calculate 9 coordinates that will be used as uv bounds of the 4
textures. */
+ float2 onscreen_multiple = (blender::math::floor(region_uv_min /
region_uv_span) +
+ float2(1.0f)) *
+ region_uv_span;
+ BLI_assert(onscreen_multiple.x > region_uv_min.x);
+ BLI_assert(onscreen_multiple.y > region_uv_min.y);
+ BLI_assert(onscreen_multiple.x < region_uv_max.x);
+ BLI_assert(onscreen_multiple.y < region_uv_max.y);
+ float2 uv_coords[3][3];
+ uv_coords[0][0] = onscreen_multiple + float2(-region_uv_span.x,
-region_uv_span.y);
+ uv_coords[0][1] = onscreen_multiple + float2(-region_uv_span.x, 0.0);
+ uv_coords[0][2] = onscreen_multiple + float2(-region_uv_span.x,
region_uv_span.y);
+ uv_coords[1][0] = onscreen_multiple + float2(0.0f, -region_uv_span.y);
+ uv_coords[1][1] = onscreen_multiple + float2(0.0f, 0.0);
+ uv_coords[1][2] = onscreen_multiple + float2(0.0f, region_uv_span.y);
+ uv_coords[2][0] = onscreen_multiple + float2(region_uv_span.x,
-region_uv_span.y);
+ uv_coords[2][1] = onscreen_multiple + float2(region_uv_span.x, 0.0);
+ uv_coords[2][2] = onscreen_multiple + float2(region_uv_span.x,
region_uv_span.y);
+
+ /* Construct the uv bounds of the 4 textures that are needed to fill the
region. */
+ Vector<TextureInfo *> unassigned_textures;
+ struct TextureInfoBounds {
+ TextureInfo *info = nullptr;
+ rctf uv_bounds;
+ };
+ TextureInfoBounds bottom_left;
+ TextureInfoBounds bottom_right;
+ TextureInfoBounds top_left;
+ TextureInfoBounds top_right;
+
+ BLI_rctf_init(&bottom_left.uv_bounds,
+ uv_coords[0][0].x,
+ uv_coords[1][1].x,
+ uv_coords[0][0].y,
+ uv_coords[1][1].y);
+ BLI_rctf_init(&bottom_right.uv_bounds,
+ uv_coords[1][0].x,
+ uv_coords[2][1].x,
+ uv_coords[1][0].y,
+ uv_coords[2][1].y);
+ BLI_rctf_init(&top_left.uv_bounds,
+ uv_coords[0][1].x,
+ uv_coords[1][2].x,
+ uv_coords[0][1].y,
+ uv_coords[1][2].y);
+ BLI_rctf_init(&top_right.uv_bounds,
+ uv_coords[1][1].x,
+ uv_coords[2][2].x,
+ uv_coords[1][1].y,
+ uv_coords[2][2].y);
+ Vector<TextureInfoBounds *> info_bounds;
+ info_bounds.append(&bottom_left);
+ info_bounds.append(&bottom_right);
+ info_bounds.append(&top_left);
+ info_bounds.append(&top_right);
+
+ /* Assign any existing texture that matches uv bounds. */
+ for (TextureInfo &info : instance_data->texture_infos) {
+ bool assigned = false;
+ for (TextureInfoBounds *info_bound : info_bounds) {
+ if (info_bound->info == nullptr &&
+ BLI_rctf_compare(&info_bound->uv_bounds, &info.clipping_uv_bounds,
0.001)) {
+ info_bound->info = &info;
+ assigned = true;
+ break;
+ }
+ }
+ if (!assigned) {
+ unassigned_textures.append(&info);
+ }
}
- }
- void update_screen_uv_bounds()
- {
- for (int i = 0; i < SCREEN_SPACE_DRAWING_MODE_TEXTURE_LEN; i++) {
- update_screen_uv_bounds(instance_data->texture_infos[0]);
+ /* Assign free textures to bounds that weren't found. */
+ for (TextureInfoBounds *info_bound : info_bounds) {
+ if (info_bound->info == nullptr) {
+ info_bound->info = unassigned_textures.pop_last();
+ info_bound->info->need_full_update = true;
+ info_bound->info->clipping_uv_bounds = info_bound->uv_bounds;
+ }
}
- }
- void update_screen_uv_bounds(TextureInfo &info)
- {
- /* Although this works, computing an inverted matrix adds some precision
issues and leads to
- * tearing artifacts. This should be modified to use the scaling and
transformation from the
- * not inverted matrix. */
- float4x4 mat(instance_data->ss_to_texture);
- float4x4 mat_inv = mat.inverted();
- float3 min_uv = mat_inv * float3(0.0f, 0.0f, 0.0f);
- float3 max_uv = mat_inv * float3(1.0f, 1.0f, 0.0f);
- rctf new_clipping_bounds;
- BLI_rctf_init(&new_clipping_bounds, min_uv[0], max_uv[0], min_uv[1],
max_uv[1]);
-
- if (!BLI_rctf_compare(&info.clipping_uv_bounds, &new_clipping_bounds,
EPSILON_UV_BOUNDS)) {
- info.clipping_uv_bounds = new_clipping_bounds;
- info.dirty = true;
+ /* Calculate the region bounds from the uv bounds. */
+ rctf region_bounds;
+ BLI_rctf_init(®ion_bounds, 0.0, region->winx, 0.0, region->winy);
+ float4x4 uv_to_screen;
+ BLI_rctf_transform_calc_m4_pivot_min(®ion_uv_bounds, ®ion_bounds,
uv_to_screen.ptr());
+ for (TextureInfo &info : instance_data->texture_infos) {
+ info.calc_region_bounds_from_uv_bounds(uv_to_screen);
}
}
};
@@ -108,11 +173,8 @@ template<typename TextureMethod> class
ScreenSpaceDrawingMode : public AbstractD
unit_m4(image_mat);
for (int i = 0; i < SCREEN_SPACE_DRAWING_MODE_TEXTURE_LEN; i++) {
const TextureInfo &info = instance_data->texture_infos[i];
- if (!info.visible) {
- continue;
- }
-
DRWShadingGroup *shgrp_sub = DRW_shgroup_create_sub(shgrp);
+ DRW_shgroup_uniform_ivec2_copy(shgrp_sub, "offset", info.offset());
DRW_shgroup_uniform_texture_ex(shgrp_sub, "imageTexture", info.texture,
GPU_SAMPLER_DEFAULT);
DRW_shgroup_call_obmat(shgrp_sub, info.batch, image_mat);
}
@@ -140,10 +202,6 @@ template<typename TextureMethod> class
ScreenSpaceDrawingMode : public AbstractD
for (int i = 0; i < SCREEN_SPACE_DRAWING_MODE_TEXTURE_LEN; i++) {
const TextureInfo &info = instance_data.texture_infos[i];
- if (!info.visible) {
- continue;
- }
-
LISTBASE_FOREACH (ImageTile *, image_tile_ptr, &image->tiles) {
const ImageTileWrapper image_tile(image_tile_ptr);
const int tile_x = image_tile.get_tile_x_offset();
@@ -238,7 +296,8 @@ template<typename TextureMethod> class
ScreenSpaceDrawingMode : public AbstractD
if (iterator.tile_data.tile_buffer == nullptr) {
continue;
}
- ImBuf *tile_buffer = ensure_float_buffer(instance_data,
iterator.tile_data.tile_buffer);
+ ImBuf *tile_buffer = instance_data.float_buffers.cached_float_buffer(
+ iterator.tile_data.tile_buffer);
if (tile_buffer != iterator.tile_data.tile_buffer) {
do_partial_update_float_buffer(tile_buffer, iterator);
}
@@ -249,10 +308,7 @@ template<typename TextureMethod> class
ScreenSpaceDrawingMode : public AbstractD
for (int i = 0; i < SCREEN_SPACE_DRAWING_MODE_TEXTURE_LEN; i++) {
const TextureInfo &info = instance_data.texture_infos[i];
/* Dirty images will recei
@@ Diff output truncated at 10240 characters. @@
_______________________________________________
Bf-blender-cvs mailing list
[email protected]
List details, subscription details or unsubscribe:
https://lists.blender.org/mailman/listinfo/bf-blender-cvs