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")