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
This commit is contained in:
@@ -44,24 +44,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<typename DataType, typename RectType>
|
||||
static void fill_tri_fan_from_rect(DataType result[4][2], RectType &rect)
|
||||
{
|
||||
result[0][0] = rect.xmin;
|
||||
result[0][1] = rect.ymin;
|
||||
@@ -73,10 +57,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<int, rcti>(pos, info.clipping_bounds);
|
||||
float uv[4][2];
|
||||
fill_tri_fan_from_rect<float, 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;
|
||||
}
|
||||
|
||||
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");
|
||||
|
||||
@@ -21,11 +21,84 @@ 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<size_t Divisions> class ScreenTileTextures {
|
||||
template<size_t Divisions> class ScreenTileTextures : public BaseTextureMethod {
|
||||
public:
|
||||
static const size_t TexturesPerDimension = Divisions + 1;
|
||||
static const size_t TexturesRequired = TexturesPerDimension * TexturesPerDimension;
|
||||
@@ -38,17 +111,17 @@ template<size_t Divisions> 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);
|
||||
}
|
||||
@@ -56,7 +129,7 @@ template<size_t Divisions> 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<TextureInfo *> unassigned_textures;
|
||||
@@ -76,13 +149,22 @@ template<size_t Divisions> 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);
|
||||
}
|
||||
@@ -107,6 +189,7 @@ template<size_t Divisions> 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,
|
||||
@@ -127,6 +210,7 @@ template<size_t Divisions> 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;
|
||||
}
|
||||
@@ -143,20 +227,34 @@ template<size_t Divisions> 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);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -497,7 +595,7 @@ template<typename TextureMethod> 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;
|
||||
@@ -584,6 +682,6 @@ template<typename TextureMethod> class ScreenSpaceDrawingMode : public AbstractD
|
||||
DRW_view_set_active(nullptr);
|
||||
GPU_framebuffer_bind(dfbl->default_fb);
|
||||
}
|
||||
}; // namespace clipping
|
||||
};
|
||||
|
||||
} // namespace blender::draw::image_engine
|
||||
|
||||
@@ -53,7 +53,7 @@ template<
|
||||
*
|
||||
* Useful during development to switch between drawing implementations.
|
||||
*/
|
||||
typename DrawingMode = ScreenSpaceDrawingMode<ScreenTileTextures<1>>>
|
||||
typename DrawingMode = ScreenSpaceDrawingMode<OneTexture>>
|
||||
class ImageEngine {
|
||||
private:
|
||||
const DRWContextState *draw_ctx;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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")
|
||||
|
||||
Reference in New Issue
Block a user