ImageEngine: Improve Performance and Quality. #107213

Merged
Jeroen Bakker merged 1 commits from Jeroen-Bakker/blender:v3.5-106092 into blender-v3.5-release 2023-04-21 11:03:56 +02:00
7 changed files with 147 additions and 58 deletions

View File

@ -46,24 +46,8 @@ class BatchUpdater {
GPU_batch_init_ex(info.batch, GPU_PRIM_TRI_FAN, vbo, nullptr, GPU_BATCH_OWNS_VBO); GPU_batch_init_ex(info.batch, GPU_PRIM_TRI_FAN, vbo, nullptr, GPU_BATCH_OWNS_VBO);
} }
GPUVertBuf *create_vbo() template<typename DataType, typename RectType>
{ static void fill_tri_fan_from_rect(DataType result[4][2], RectType &rect)
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)
{ {
result[0][0] = rect.xmin; result[0][0] = rect.xmin;
result[0][1] = rect.ymin; result[0][1] = rect.ymin;
@ -75,10 +59,27 @@ class BatchUpdater {
result[3][1] = rect.ymax; 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() void ensure_format()
{ {
if (format.attr_len == 0) { 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); GPU_vertformat_attr_add(&format, "uv", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
pos_id = GPU_vertformat_attr_id_get(&format, "pos"); pos_id = GPU_vertformat_attr_id_get(&format, "pos");

View File

@ -21,11 +21,88 @@ namespace blender::draw::image_engine {
constexpr float EPSILON_UV_BOUNDS = 0.00001f; 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. * \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: public:
static const size_t TexturesPerDimension = Divisions + 1; static const size_t TexturesPerDimension = Divisions + 1;
static const size_t TexturesRequired = TexturesPerDimension * TexturesPerDimension; static const size_t TexturesRequired = TexturesPerDimension * TexturesPerDimension;
@ -38,19 +115,19 @@ template<size_t Divisions> class ScreenTileTextures {
struct TextureInfoBounds { struct TextureInfoBounds {
TextureInfo *info = nullptr; TextureInfo *info = nullptr;
rctf uv_bounds; 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: 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`. * \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); instance_data->texture_infos.resize(TexturesRequired);
} }
@ -58,7 +135,7 @@ template<size_t Divisions> class ScreenTileTextures {
/** /**
* \brief Update the uv and region bounds of all texture_infos of instance_data. * \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. */ /* determine uv_area of the region. */
Vector<TextureInfo *> unassigned_textures; Vector<TextureInfo *> unassigned_textures;
@ -78,13 +155,22 @@ template<size_t Divisions> class ScreenTileTextures {
rctf region_uv_bounds; rctf region_uv_bounds;
BLI_rctf_init( BLI_rctf_init(
&region_uv_bounds, region_uv_min.x, region_uv_max.x, region_uv_min.y, region_uv_max.y); &region_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(); float2 viewport_size = DRW_viewport_size_get();
int2 texture_size(ceil(viewport_size.x / Divisions), ceil(viewport_size.y / Divisions)); 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) { for (TextureInfo &info : instance_data->texture_infos) {
info.ensure_gpu_texture(texture_size); info.ensure_gpu_texture(texture_size);
} }
@ -109,6 +195,7 @@ template<size_t Divisions> class ScreenTileTextures {
for (int x = 0; x < TexturesPerDimension; x++) { for (int x = 0; x < TexturesPerDimension; x++) {
for (int y = 0; y < TexturesPerDimension; y++) { for (int y = 0; y < TexturesPerDimension; y++) {
TextureInfoBounds texture_info_bounds; TextureInfoBounds texture_info_bounds;
texture_info_bounds.tile_id = int2(x, y);
BLI_rctf_init(&texture_info_bounds.uv_bounds, BLI_rctf_init(&texture_info_bounds.uv_bounds,
uv_coords[x][y].x, uv_coords[x][y].x,
uv_coords[x + 1][y + 1].x, uv_coords[x + 1][y + 1].x,
@ -129,6 +216,7 @@ template<size_t Divisions> class ScreenTileTextures {
if (info_bound.info == nullptr && if (info_bound.info == nullptr &&
BLI_rctf_compare(&info_bound.uv_bounds, &info.clipping_uv_bounds, 0.001)) { BLI_rctf_compare(&info_bound.uv_bounds, &info.clipping_uv_bounds, 0.001)) {
info_bound.info = &info; info_bound.info = &info;
info.tile_id = info_bound.tile_id;
assigned = true; assigned = true;
break; break;
} }
@ -145,20 +233,34 @@ template<size_t Divisions> class ScreenTileTextures {
for (TextureInfoBounds &info_bound : info_bounds) { for (TextureInfoBounds &info_bound : info_bounds) {
if (info_bound.info == nullptr) { if (info_bound.info == nullptr) {
info_bound.info = unassigned_textures.pop_last(); 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->need_full_update = true;
info_bound.info->clipping_uv_bounds = info_bound.uv_bounds; info_bound.info->clipping_uv_bounds = info_bound.uv_bounds;
} }
} }
} }
void update_region_bounds_from_uv_bounds(const rctf &region_uv_bounds, const float2 region_size) void update_region_bounds_from_uv_bounds(const rctf &region_uv_bounds, const int2 region_size)
{ {
rctf region_bounds; rctf region_bounds;
BLI_rctf_init(&region_bounds, 0.0, region_size.x, 0.0, region_size.y); BLI_rctf_init(&region_bounds, 0.0, region_size.x, 0.0, region_size.y);
float4x4 uv_to_screen; float4x4 uv_to_screen;
BLI_rctf_transform_calc_m4_pivot_min(&region_uv_bounds, &region_bounds, uv_to_screen.ptr()); BLI_rctf_transform_calc_m4_pivot_min(&region_uv_bounds, &region_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) { 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<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.ymin - image_tile.get_tile_y_offset()),
tile_buffer.y * (texture_info.clipping_uv_bounds.ymax - 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()); 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;
rctf *crop_rect_ptr = nullptr; rctf *crop_rect_ptr = nullptr;
@ -585,6 +687,6 @@ template<typename TextureMethod> class ScreenSpaceDrawingMode : public AbstractD
DRW_view_set_active(nullptr); DRW_view_set_active(nullptr);
GPU_framebuffer_bind(dfbl->default_fb); GPU_framebuffer_bind(dfbl->default_fb);
} }
}; // namespace clipping };
} // namespace blender::draw::image_engine } // namespace blender::draw::image_engine

View File

@ -53,7 +53,7 @@ template<
* *
* Useful during development to switch between drawing implementations. * Useful during development to switch between drawing implementations.
*/ */
typename DrawingMode = ScreenSpaceDrawingMode<ScreenTileTextures<1>>> typename DrawingMode = ScreenSpaceDrawingMode<OneTexture>>
class ImageEngine { class ImageEngine {
private: private:
const DRWContextState *draw_ctx; const DRWContextState *draw_ctx;

View File

@ -24,15 +24,19 @@ struct TextureInfo {
bool need_full_update : 1; bool need_full_update : 1;
/** \brief area of the texture in screen space. */ /** \brief area of the texture in screen space. */
rctf clipping_bounds; rcti clipping_bounds;
/** \brief uv area of the texture in screen space. */ /** \brief uv area of the texture in screen space. */
rctf clipping_uv_bounds; 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. * \brief Batch to draw the associated text on the screen.
* *
* Contains a VBO with `pos` and `uv`. * 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. * `uv` (2xF32) reflect the uv bounds.
*/ */
GPUBatch *batch = nullptr; GPUBatch *batch = nullptr;
@ -68,22 +72,6 @@ struct TextureInfo {
return int2(clipping_bounds.xmin, clipping_bounds.ymin); 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) void ensure_gpu_texture(int2 texture_size)
{ {
const bool is_allocated = texture != nullptr; const bool is_allocated = texture != nullptr;

View File

@ -2,10 +2,9 @@
void main() void main()
{ {
vec3 image_pos = vec3(pos, 0.0); vec3 image_pos = vec3(pos.x, pos.y, 0.0);
uv_screen = image_pos.xy; uv_screen = image_pos.xy;
vec3 world_pos = point_object_to_world(image_pos); vec4 position = point_world_to_ndc(image_pos);
vec4 position = point_world_to_ndc(world_pos);
gl_Position = position; gl_Position = position;
} }

View File

@ -2,10 +2,9 @@
void main() void main()
{ {
vec3 image_pos = vec3(pos, 0.0); vec3 image_pos = vec3(pos.x, pos.y, 0.0);
uv_image = uv; uv_image = uv;
vec3 world_pos = point_object_to_world(image_pos); vec4 position = point_world_to_ndc(image_pos);
vec4 position = point_world_to_ndc(world_pos);
gl_Position = position; gl_Position = position;
} }

View File

@ -5,7 +5,7 @@
GPU_SHADER_INTERFACE_INFO(image_engine_color_iface, "").smooth(Type::VEC2, "uv_screen"); GPU_SHADER_INTERFACE_INFO(image_engine_color_iface, "").smooth(Type::VEC2, "uv_screen");
GPU_SHADER_CREATE_INFO(image_engine_color_shader) 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) .vertex_out(image_engine_color_iface)
.fragment_out(0, Type::VEC4, "fragColor") .fragment_out(0, Type::VEC4, "fragColor")
.push_constant(Type::VEC4, "shuffle") .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_INTERFACE_INFO(image_engine_depth_iface, "").smooth(Type::VEC2, "uv_image");
GPU_SHADER_CREATE_INFO(image_engine_depth_shader) 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_in(1, Type::VEC2, "uv")
.vertex_out(image_engine_depth_iface) .vertex_out(image_engine_depth_iface)
.push_constant(Type::VEC4, "min_max_uv") .push_constant(Type::VEC4, "min_max_uv")