Files
test2/source/blender/draw/engines/image/image_drawing_mode.hh
Jeroen Bakker 53eadcc533 ImageEngine: Migrate to new draw manager
Converts the image engine to the new draw manager.
This is an as is migration without just a few tweaks to improve the code.

Pull Request: https://projects.blender.org/blender/blender/pulls/131142
2024-12-02 16:10:02 +01:00

319 lines
11 KiB
C++

/* SPDX-FileCopyrightText: 2021 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup draw_engine
*/
#pragma once
#include "IMB_imbuf_types.hh"
#include "IMB_interp.hh"
#include "BLI_math_matrix_types.hh"
#include "BLI_math_vector_types.hh"
#include "image_batches.hh"
#include "image_private.hh"
#include "DNA_windowmanager_types.h"
namespace blender::image_engine {
class Instance;
constexpr float EPSILON_UV_BOUNDS = 0.00001f;
class BaseTextureMethod {
protected:
State *instance_data;
protected:
BaseTextureMethod(State *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(State *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 : public BaseTextureMethod {
public:
static const size_t TexturesPerDimension = Divisions + 1;
static const size_t TexturesRequired = TexturesPerDimension * TexturesPerDimension;
static const size_t VerticesPerDimension = TexturesPerDimension + 1;
private:
/**
* \brief Helper struct to pair a texture info and a region in uv space of the area.
*/
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;
};
public:
ScreenTileTextures(State *instance_data) : BaseTextureMethod(instance_data) {}
/**
* \brief Ensure enough texture infos are allocated in `instance_data`.
*/
void ensure_texture_infos() override
{
instance_data->texture_infos.resize(TexturesRequired);
}
/**
* \brief Update the uv and region bounds of all texture_infos of instance_data.
*/
void update_bounds(const ARegion *region) override
{
/* determine uv_area of the region. */
Vector<TextureInfo *> unassigned_textures;
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();
float2 region_uv_span = region_uv_max - region_uv_min;
/* Calculate uv coordinates of each vert in the grid of textures. */
/* Construct the uv bounds of the 4 textures that are needed to fill the region. */
Vector<TextureInfoBounds> info_bounds = create_uv_bounds(region_uv_span, region_uv_min);
assign_texture_infos_by_uv_bounds(info_bounds, unassigned_textures);
assign_unused_texture_infos(info_bounds, unassigned_textures);
/* Calculate the region bounds from the uv bounds. */
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, int2(region->winx, region->winy));
}
/**
* 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);
}
}
private:
Vector<TextureInfoBounds> create_uv_bounds(float2 region_uv_span, float2 region_uv_min)
{
float2 uv_coords[VerticesPerDimension][VerticesPerDimension];
float2 region_tile_uv_span = region_uv_span / float2(float(Divisions));
float2 onscreen_multiple = (blender::math::floor(region_uv_min / region_tile_uv_span) +
float2(1.0f)) *
region_tile_uv_span;
for (int y = 0; y < VerticesPerDimension; y++) {
for (int x = 0; x < VerticesPerDimension; x++) {
uv_coords[x][y] = region_tile_uv_span * float2(float(x - 1), float(y - 1)) +
onscreen_multiple;
}
}
Vector<TextureInfoBounds> info_bounds;
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,
uv_coords[x][y].y,
uv_coords[x + 1][y + 1].y);
info_bounds.append(texture_info_bounds);
}
}
return info_bounds;
}
void assign_texture_infos_by_uv_bounds(Vector<TextureInfoBounds> &info_bounds,
Vector<TextureInfo *> &r_unassigned_textures)
{
for (TextureInfo &info : instance_data->texture_infos) {
bool assigned = false;
for (TextureInfoBounds &info_bound : info_bounds) {
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;
}
}
if (!assigned) {
r_unassigned_textures.append(&info);
}
}
}
void assign_unused_texture_infos(Vector<TextureInfoBounds> &info_bounds,
Vector<TextureInfo *> &unassigned_textures)
{
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 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) {
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);
}
}
};
using namespace blender::bke::image::partial_update;
using namespace blender::bke::image;
class ScreenSpaceDrawingMode : public AbstractDrawingMode {
private:
Instance &instance_;
public:
ScreenSpaceDrawingMode(Instance &instance) : instance_(instance) {}
private:
void add_shgroups() const;
/**
* \brief add depth drawing calls.
*
* The depth is used to identify if the tile exist or transparent.
*/
void add_depth_shgroups(::Image *image, ::ImageUser *image_user) const;
/**
* \brief Update GPUTextures for drawing the image.
*
* GPUTextures that are marked dirty are rebuild. GPUTextures that aren't marked dirty are
* updated with changed region of the image.
*/
void update_textures(::Image *image, ::ImageUser *image_user) const;
/**
* Update the float buffer in the region given by the partial update checker.
*/
void do_partial_update_float_buffer(
ImBuf *float_buffer, PartialUpdateChecker<ImageTileData>::CollectResult &iterator) const;
void do_partial_update(PartialUpdateChecker<ImageTileData>::CollectResult &iterator) const;
void do_full_update_for_dirty_textures(const ::ImageUser *image_user) const;
void do_full_update_gpu_texture(TextureInfo &info, const ::ImageUser *image_user) const;
/**
* texture_buffer is the image buffer belonging to the texture_info.
* tile_buffer is the image buffer of the tile.
*/
void do_full_update_texture_slot(const TextureInfo &texture_info,
ImBuf &texture_buffer,
ImBuf &tile_buffer,
const ImageTileWrapper &image_tile) const;
public:
void begin_sync() const override;
void image_sync(::Image *image, ::ImageUser *iuser) const override;
void draw_finish() const override;
void draw_viewport() const override;
};
} // namespace blender::image_engine