From a7c1eb585e4a23781eec4b25effc319cf3fa62c5 Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Tue, 11 Apr 2023 16:20:26 +0200 Subject: [PATCH] ImageEngine: Improve Performance and Quality. Blender 3.5 has a performance regression in the image engine that made the image engine 3-4x slower then 3.4. The cause of this was the new way how panning was implemented. This PR disables the new panning for now as a short term fix. In the future the panning and improvements we did ensured better performance when dealing with higher resolution images. But the regression for regular images weren't acceptable. This fix might introduce other performance regressions on lower end systems. In the future we still want to improve the performance to get back to Blender 3.0 performance, but that requires more work and has a different priority. Pull Request: https://projects.blender.org/blender/blender/pulls/106803 --- .../draw/engines/image/image_batches.hh | 39 +++--- .../draw/engines/image/image_drawing_mode.hh | 126 ++++++++++++++++-- .../draw/engines/image/image_engine.cc | 2 +- .../draw/engines/image/image_texture_info.hh | 24 +--- .../shaders/image_engine_color_vert.glsl | 5 +- .../shaders/image_engine_depth_vert.glsl | 5 +- .../image/shaders/infos/engine_image_info.hh | 4 +- 7 files changed, 147 insertions(+), 58 deletions(-) diff --git a/source/blender/draw/engines/image/image_batches.hh b/source/blender/draw/engines/image/image_batches.hh index 1d7e7a0b548..85ddc7f48ee 100644 --- a/source/blender/draw/engines/image/image_batches.hh +++ b/source/blender/draw/engines/image/image_batches.hh @@ -46,24 +46,8 @@ class BatchUpdater { GPU_batch_init_ex(info.batch, GPU_PRIM_TRI_FAN, vbo, nullptr, GPU_BATCH_OWNS_VBO); } - GPUVertBuf *create_vbo() - { - GPUVertBuf *vbo = GPU_vertbuf_create_with_format(&format); - GPU_vertbuf_data_alloc(vbo, 4); - float pos[4][2]; - fill_tri_fan_from_rctf(pos, info.clipping_bounds); - float uv[4][2]; - fill_tri_fan_from_rctf(uv, info.clipping_uv_bounds); - - for (int i = 0; i < 4; i++) { - GPU_vertbuf_attr_set(vbo, pos_id, i, pos[i]); - GPU_vertbuf_attr_set(vbo, uv_id, i, uv[i]); - } - - return vbo; - } - - static void fill_tri_fan_from_rctf(float result[4][2], rctf &rect) + template + static void fill_tri_fan_from_rect(DataType result[4][2], RectType &rect) { result[0][0] = rect.xmin; result[0][1] = rect.ymin; @@ -75,10 +59,27 @@ class BatchUpdater { result[3][1] = rect.ymax; } + GPUVertBuf *create_vbo() + { + GPUVertBuf *vbo = GPU_vertbuf_create_with_format(&format); + GPU_vertbuf_data_alloc(vbo, 4); + int pos[4][2]; + fill_tri_fan_from_rect(pos, info.clipping_bounds); + float uv[4][2]; + fill_tri_fan_from_rect(uv, info.clipping_uv_bounds); + + for (int i = 0; i < 4; i++) { + GPU_vertbuf_attr_set(vbo, pos_id, i, pos[i]); + GPU_vertbuf_attr_set(vbo, uv_id, i, uv[i]); + } + + return vbo; + } + void ensure_format() { if (format.attr_len == 0) { - GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + GPU_vertformat_attr_add(&format, "pos", GPU_COMP_I32, 2, GPU_FETCH_INT); GPU_vertformat_attr_add(&format, "uv", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); pos_id = GPU_vertformat_attr_id_get(&format, "pos"); diff --git a/source/blender/draw/engines/image/image_drawing_mode.hh b/source/blender/draw/engines/image/image_drawing_mode.hh index b77150fbb02..2d3e41f5148 100644 --- a/source/blender/draw/engines/image/image_drawing_mode.hh +++ b/source/blender/draw/engines/image/image_drawing_mode.hh @@ -21,11 +21,88 @@ namespace blender::draw::image_engine { constexpr float EPSILON_UV_BOUNDS = 0.00001f; +class BaseTextureMethod { + protected: + IMAGE_InstanceData *instance_data; + + protected: + BaseTextureMethod(IMAGE_InstanceData *instance_data) : instance_data(instance_data) + { + } + + public: + /** + * \brief Ensure enough texture infos are allocated in `instance_data`. + */ + virtual void ensure_texture_infos() = 0; + + /** + * \brief Update the uv and region bounds of all texture_infos of instance_data. + */ + virtual void update_bounds(const ARegion *region) = 0; + + virtual void ensure_gpu_textures_allocation() = 0; +}; + +/** + * Uses a single texture that covers the area. Every zoom/pan change requires a full + * update of the texture. + */ +class OneTexture : public BaseTextureMethod { + public: + OneTexture(IMAGE_InstanceData *instance_data) : BaseTextureMethod(instance_data) + { + } + void ensure_texture_infos() override + { + instance_data->texture_infos.resize(1); + } + + void update_bounds(const ARegion *region) override + { + float4x4 mat = math::invert(float4x4(instance_data->ss_to_texture)); + float2 region_uv_min = math::transform_point(mat, float3(0.0f, 0.0f, 0.0f)).xy(); + float2 region_uv_max = math::transform_point(mat, float3(1.0f, 1.0f, 0.0f)).xy(); + + TextureInfo &texture_info = instance_data->texture_infos[0]; + texture_info.tile_id = int2(0); + texture_info.need_full_update = false; + rctf new_clipping_uv_bounds; + BLI_rctf_init(&new_clipping_uv_bounds, + region_uv_min.x, + region_uv_max.x, + region_uv_min.y, + region_uv_max.y); + + if (memcmp(&new_clipping_uv_bounds, &texture_info.clipping_uv_bounds, sizeof(rctf))) { + texture_info.clipping_uv_bounds = new_clipping_uv_bounds; + texture_info.need_full_update = true; + } + + rcti new_clipping_bounds; + BLI_rcti_init(&new_clipping_bounds, 0, region->winx, 0, region->winy); + if (memcmp(&new_clipping_bounds, &texture_info.clipping_bounds, sizeof(rcti))) { + texture_info.clipping_bounds = new_clipping_bounds; + texture_info.need_full_update = true; + } + } + + void ensure_gpu_textures_allocation() override + { + TextureInfo &texture_info = instance_data->texture_infos[0]; + int2 texture_size = int2(BLI_rcti_size_x(&texture_info.clipping_bounds), + BLI_rcti_size_y(&texture_info.clipping_bounds)); + texture_info.ensure_gpu_texture(texture_size); + } +}; + /** * \brief Screen space method using a multiple textures covering the region. * + * This method improves panning speed, but has some drawing artifacts and + * therefore isn't selected. */ -template class ScreenTileTextures { +template class ScreenTileTextures : public BaseTextureMethod { public: static const size_t TexturesPerDimension = Divisions + 1; static const size_t TexturesRequired = TexturesPerDimension * TexturesPerDimension; @@ -38,19 +115,19 @@ template class ScreenTileTextures { struct TextureInfoBounds { TextureInfo *info = nullptr; rctf uv_bounds; + /* Offset of this tile to be drawn on the screen (number of tiles from bottom left corner). */ + int2 tile_id; }; - IMAGE_InstanceData *instance_data; - public: - ScreenTileTextures(IMAGE_InstanceData *instance_data) : instance_data(instance_data) + ScreenTileTextures(IMAGE_InstanceData *instance_data) : BaseTextureMethod(instance_data) { } /** * \brief Ensure enough texture infos are allocated in `instance_data`. */ - void ensure_texture_infos() + void ensure_texture_infos() override { instance_data->texture_infos.resize(TexturesRequired); } @@ -58,7 +135,7 @@ template class ScreenTileTextures { /** * \brief Update the uv and region bounds of all texture_infos of instance_data. */ - void update_bounds(const ARegion *region) + void update_bounds(const ARegion *region) override { /* determine uv_area of the region. */ Vector unassigned_textures; @@ -78,13 +155,22 @@ template class ScreenTileTextures { rctf region_uv_bounds; BLI_rctf_init( ®ion_uv_bounds, region_uv_min.x, region_uv_max.x, region_uv_min.y, region_uv_max.y); - update_region_bounds_from_uv_bounds(region_uv_bounds, float2(region->winx, region->winy)); + update_region_bounds_from_uv_bounds(region_uv_bounds, int2(region->winx, region->winy)); } - void ensure_gpu_textures_allocation() + /** + * Get the texture size of a single texture for the current settings. + */ + int2 gpu_texture_size() const { float2 viewport_size = DRW_viewport_size_get(); int2 texture_size(ceil(viewport_size.x / Divisions), ceil(viewport_size.y / Divisions)); + return texture_size; + } + + void ensure_gpu_textures_allocation() override + { + int2 texture_size = gpu_texture_size(); for (TextureInfo &info : instance_data->texture_infos) { info.ensure_gpu_texture(texture_size); } @@ -109,6 +195,7 @@ template class ScreenTileTextures { for (int x = 0; x < TexturesPerDimension; x++) { for (int y = 0; y < TexturesPerDimension; y++) { TextureInfoBounds texture_info_bounds; + texture_info_bounds.tile_id = int2(x, y); BLI_rctf_init(&texture_info_bounds.uv_bounds, uv_coords[x][y].x, uv_coords[x + 1][y + 1].x, @@ -129,6 +216,7 @@ template class ScreenTileTextures { if (info_bound.info == nullptr && BLI_rctf_compare(&info_bound.uv_bounds, &info.clipping_uv_bounds, 0.001)) { info_bound.info = &info; + info.tile_id = info_bound.tile_id; assigned = true; break; } @@ -145,20 +233,34 @@ template class ScreenTileTextures { for (TextureInfoBounds &info_bound : info_bounds) { if (info_bound.info == nullptr) { info_bound.info = unassigned_textures.pop_last(); + info_bound.info->tile_id = info_bound.tile_id; info_bound.info->need_full_update = true; info_bound.info->clipping_uv_bounds = info_bound.uv_bounds; } } } - void update_region_bounds_from_uv_bounds(const rctf ®ion_uv_bounds, const float2 region_size) + void update_region_bounds_from_uv_bounds(const rctf ®ion_uv_bounds, const int2 region_size) { rctf region_bounds; BLI_rctf_init(®ion_bounds, 0.0, region_size.x, 0.0, region_size.y); float4x4 uv_to_screen; BLI_rctf_transform_calc_m4_pivot_min(®ion_uv_bounds, ®ion_bounds, uv_to_screen.ptr()); + int2 tile_origin(0); + for (const TextureInfo &info : instance_data->texture_infos) { + if (info.tile_id == int2(0)) { + tile_origin = int2(math::transform_point( + uv_to_screen, + float3(info.clipping_uv_bounds.xmin, info.clipping_uv_bounds.ymin, 0.0))); + break; + } + } + + const int2 texture_size = gpu_texture_size(); for (TextureInfo &info : instance_data->texture_infos) { - info.update_region_bounds_from_uv_bounds(uv_to_screen); + int2 bottom_left = tile_origin + texture_size * info.tile_id; + int2 top_right = bottom_left + texture_size; + BLI_rcti_init(&info.clipping_bounds, bottom_left.x, top_right.x, bottom_left.y, top_right.y); } } }; @@ -498,7 +600,7 @@ template class ScreenSpaceDrawingMode : public AbstractD tile_buffer.y * (texture_info.clipping_uv_bounds.ymin - image_tile.get_tile_y_offset()), tile_buffer.y * (texture_info.clipping_uv_bounds.ymax - image_tile.get_tile_y_offset())); BLI_rctf_transform_calc_m4_pivot_min(&tile_area, &texture_area, uv_to_texel.ptr()); - invert_m4(uv_to_texel.ptr()); + uv_to_texel = math::invert(uv_to_texel); rctf crop_rect; rctf *crop_rect_ptr = nullptr; @@ -585,6 +687,6 @@ template class ScreenSpaceDrawingMode : public AbstractD DRW_view_set_active(nullptr); GPU_framebuffer_bind(dfbl->default_fb); } -}; // namespace clipping +}; } // namespace blender::draw::image_engine diff --git a/source/blender/draw/engines/image/image_engine.cc b/source/blender/draw/engines/image/image_engine.cc index e055dc5836c..cc356990052 100644 --- a/source/blender/draw/engines/image/image_engine.cc +++ b/source/blender/draw/engines/image/image_engine.cc @@ -53,7 +53,7 @@ template< * * Useful during development to switch between drawing implementations. */ - typename DrawingMode = ScreenSpaceDrawingMode>> + typename DrawingMode = ScreenSpaceDrawingMode> class ImageEngine { private: const DRWContextState *draw_ctx; diff --git a/source/blender/draw/engines/image/image_texture_info.hh b/source/blender/draw/engines/image/image_texture_info.hh index ead037196d6..1e0eb2162fb 100644 --- a/source/blender/draw/engines/image/image_texture_info.hh +++ b/source/blender/draw/engines/image/image_texture_info.hh @@ -24,15 +24,19 @@ struct TextureInfo { bool need_full_update : 1; /** \brief area of the texture in screen space. */ - rctf clipping_bounds; + rcti clipping_bounds; /** \brief uv area of the texture in screen space. */ rctf clipping_uv_bounds; + /* Which tile of the screen is used with this texture. Used to safely calculate the correct + * offset of the textures. */ + int2 tile_id; + /** * \brief Batch to draw the associated text on the screen. * * Contains a VBO with `pos` and `uv`. - * `pos` (2xF32) is relative to the origin of the space. + * `pos` (2xI32) is relative to the origin of the space. * `uv` (2xF32) reflect the uv bounds. */ GPUBatch *batch = nullptr; @@ -68,22 +72,6 @@ struct TextureInfo { return int2(clipping_bounds.xmin, clipping_bounds.ymin); } - /** - * \brief Update the region bounds from the uv bounds by applying the given transform matrix. - */ - void update_region_bounds_from_uv_bounds(const float4x4 &uv_to_region) - { - float3 bottom_left_uv = float3(clipping_uv_bounds.xmin, clipping_uv_bounds.ymin, 0.0f); - float3 top_right_uv = float3(clipping_uv_bounds.xmax, clipping_uv_bounds.ymax, 0.0f); - float3 bottom_left_region = math::transform_point(uv_to_region, bottom_left_uv); - float3 top_right_region = math::transform_point(uv_to_region, top_right_uv); - BLI_rctf_init(&clipping_bounds, - bottom_left_region.x, - top_right_region.x, - bottom_left_region.y, - top_right_region.y); - } - void ensure_gpu_texture(int2 texture_size) { const bool is_allocated = texture != nullptr; diff --git a/source/blender/draw/engines/image/shaders/image_engine_color_vert.glsl b/source/blender/draw/engines/image/shaders/image_engine_color_vert.glsl index fb72a132613..53b67032815 100644 --- a/source/blender/draw/engines/image/shaders/image_engine_color_vert.glsl +++ b/source/blender/draw/engines/image/shaders/image_engine_color_vert.glsl @@ -2,10 +2,9 @@ void main() { - vec3 image_pos = vec3(pos, 0.0); + vec3 image_pos = vec3(pos.x, pos.y, 0.0); uv_screen = image_pos.xy; - vec3 world_pos = point_object_to_world(image_pos); - vec4 position = point_world_to_ndc(world_pos); + vec4 position = point_world_to_ndc(image_pos); gl_Position = position; } diff --git a/source/blender/draw/engines/image/shaders/image_engine_depth_vert.glsl b/source/blender/draw/engines/image/shaders/image_engine_depth_vert.glsl index 3181a85ff55..c1516e09213 100644 --- a/source/blender/draw/engines/image/shaders/image_engine_depth_vert.glsl +++ b/source/blender/draw/engines/image/shaders/image_engine_depth_vert.glsl @@ -2,10 +2,9 @@ void main() { - vec3 image_pos = vec3(pos, 0.0); + vec3 image_pos = vec3(pos.x, pos.y, 0.0); uv_image = uv; - vec3 world_pos = point_object_to_world(image_pos); - vec4 position = point_world_to_ndc(world_pos); + vec4 position = point_world_to_ndc(image_pos); gl_Position = position; } diff --git a/source/blender/draw/engines/image/shaders/infos/engine_image_info.hh b/source/blender/draw/engines/image/shaders/infos/engine_image_info.hh index 3e6673b79a2..6495ee03326 100644 --- a/source/blender/draw/engines/image/shaders/infos/engine_image_info.hh +++ b/source/blender/draw/engines/image/shaders/infos/engine_image_info.hh @@ -5,7 +5,7 @@ GPU_SHADER_INTERFACE_INFO(image_engine_color_iface, "").smooth(Type::VEC2, "uv_screen"); GPU_SHADER_CREATE_INFO(image_engine_color_shader) - .vertex_in(0, Type::VEC2, "pos") + .vertex_in(0, Type::IVEC2, "pos") .vertex_out(image_engine_color_iface) .fragment_out(0, Type::VEC4, "fragColor") .push_constant(Type::VEC4, "shuffle") @@ -23,7 +23,7 @@ GPU_SHADER_CREATE_INFO(image_engine_color_shader) GPU_SHADER_INTERFACE_INFO(image_engine_depth_iface, "").smooth(Type::VEC2, "uv_image"); GPU_SHADER_CREATE_INFO(image_engine_depth_shader) - .vertex_in(0, Type::VEC2, "pos") + .vertex_in(0, Type::IVEC2, "pos") .vertex_in(1, Type::VEC2, "uv") .vertex_out(image_engine_depth_iface) .push_constant(Type::VEC4, "min_max_uv") -- 2.30.2