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);
}
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;
@ -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<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");

View File

@ -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<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,19 +115,19 @@ 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);
}
@ -58,7 +135,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;
@ -78,13 +155,22 @@ template<size_t Divisions> class ScreenTileTextures {
rctf region_uv_bounds;
BLI_rctf_init(
&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();
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<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,
@ -129,6 +216,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;
}
@ -145,20 +233,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 &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;
BLI_rctf_init(&region_bounds, 0.0, region_size.x, 0.0, region_size.y);
float4x4 uv_to_screen;
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) {
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.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<typename TextureMethod> class ScreenSpaceDrawingMode : public AbstractD
DRW_view_set_active(nullptr);
GPU_framebuffer_bind(dfbl->default_fb);
}
}; // namespace clipping
};
} // namespace blender::draw::image_engine

View File

@ -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;

View File

@ -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;

View File

@ -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;
}

View File

@ -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;
}

View File

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