Cleanup: Image Engine

Before we start porting image engine to the new drawing manager
we should do some cleanups.

- Use similar namespace as other draw engines (`blender::image_engine`)
- Switched InstanceData and Instance
- Reduce parameters as drawing mode can access Instance.
- Remove some templates to improve readability.

Pull Request: https://projects.blender.org/blender/blender/pulls/131146
This commit is contained in:
Jeroen Bakker
2024-11-29 15:46:44 +01:00
parent e104735eeb
commit 9a4b232bd5
17 changed files with 605 additions and 558 deletions

View File

@@ -106,6 +106,7 @@ set(SRC
engines/basic/basic_shader.cc
engines/compositor/compositor_engine.cc
engines/image/image_engine.cc
engines/image/image_drawing_mode.cc
engines/image/image_shader.cc
engines/eevee_next/eevee_ambient_occlusion.cc
engines/eevee_next/eevee_camera.cc
@@ -276,7 +277,7 @@ set(SRC
engines/image/image_drawing_mode.hh
engines/image/image_engine.h
engines/image/image_enums.hh
engines/image/image_instance_data.hh
engines/image/image_state.hh
engines/image/image_partial_updater.hh
engines/image/image_private.hh
engines/image/image_shader_params.hh

View File

@@ -10,7 +10,7 @@
#include "image_texture_info.hh"
namespace blender::draw::image_engine {
namespace blender::image_engine {
/** \brief Create gpu::Batch for a IMAGE_ScreenSpaceTextureInfo. */
class BatchUpdater {
@@ -87,4 +87,4 @@ class BatchUpdater {
}
};
} // namespace blender::draw::image_engine
} // namespace blender::image_engine

View File

@@ -14,7 +14,7 @@
#include "IMB_imbuf.hh"
#include "IMB_imbuf_types.hh"
namespace blender::draw::image_engine {
namespace blender::image_engine {
struct FloatImageBuffer {
ImBuf *source_buffer = nullptr;
@@ -130,4 +130,4 @@ struct FloatBufferCache {
}
};
} // namespace blender::draw::image_engine
} // namespace blender::image_engine

View File

@@ -0,0 +1,395 @@
/* SPDX-FileCopyrightText: 2024 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "image_drawing_mode.hh"
#include "image_instance.hh"
#include "BKE_image.hh"
#include "BKE_image_partial_update.hh"
namespace blender::image_engine {
DRWPass *ScreenSpaceDrawingMode::create_image_pass() const
{
DRWState state = static_cast<DRWState>(DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_ALWAYS |
DRW_STATE_BLEND_ALPHA_PREMUL);
return DRW_pass_create("Image", state);
}
DRWPass *ScreenSpaceDrawingMode::create_depth_pass() const
{
DRWState state = static_cast<DRWState>(DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS_EQUAL);
return DRW_pass_create("Depth", state);
}
void ScreenSpaceDrawingMode::add_shgroups() const
{
const ShaderParameters &sh_params = instance_.state.sh_params;
GPUShader *shader = IMAGE_shader_image_get();
DefaultTextureList *dtxl = DRW_viewport_texture_list_get();
DRWShadingGroup *shgrp = DRW_shgroup_create(shader, instance_.state.passes.image_pass);
DRW_shgroup_uniform_vec2_copy(shgrp, "farNearDistances", sh_params.far_near);
DRW_shgroup_uniform_vec4_copy(shgrp, "shuffle", sh_params.shuffle);
DRW_shgroup_uniform_int_copy(shgrp, "drawFlags", static_cast<int32_t>(sh_params.flags));
DRW_shgroup_uniform_bool_copy(shgrp, "imgPremultiplied", sh_params.use_premul_alpha);
DRW_shgroup_uniform_texture(shgrp, "depth_texture", dtxl->depth);
float image_mat[4][4];
unit_m4(image_mat);
for (const TextureInfo &info : instance_.state.texture_infos) {
DRWShadingGroup *shgrp_sub = DRW_shgroup_create_sub(shgrp);
DRW_shgroup_uniform_ivec2_copy(shgrp_sub, "offset", info.offset());
DRW_shgroup_uniform_texture_ex(
shgrp_sub, "imageTexture", info.texture, GPUSamplerState::default_sampler());
DRW_shgroup_call_obmat(shgrp_sub, info.batch, image_mat);
}
}
void ScreenSpaceDrawingMode::add_depth_shgroups(Image *image, ImageUser *image_user) const
{
GPUShader *shader = IMAGE_shader_depth_get();
DRWShadingGroup *shgrp = DRW_shgroup_create(shader, instance_.state.passes.depth_pass);
float image_mat[4][4];
unit_m4(image_mat);
ImageUser tile_user = {0};
if (image_user) {
tile_user = *image_user;
}
for (const TextureInfo &info : instance_.state.texture_infos) {
LISTBASE_FOREACH (ImageTile *, image_tile_ptr, &image->tiles) {
const ImageTileWrapper image_tile(image_tile_ptr);
const int tile_x = image_tile.get_tile_x_offset();
const int tile_y = image_tile.get_tile_y_offset();
tile_user.tile = image_tile.get_tile_number();
/* NOTE: `BKE_image_has_ibuf` doesn't work as it fails for render results. That could be a
* bug or a feature. For now we just acquire to determine if there is a texture. */
void *lock;
ImBuf *tile_buffer = BKE_image_acquire_ibuf(image, &tile_user, &lock);
if (tile_buffer != nullptr) {
instance_.state.float_buffers.mark_used(tile_buffer);
DRWShadingGroup *shsub = DRW_shgroup_create_sub(shgrp);
float4 min_max_uv(tile_x, tile_y, tile_x + 1, tile_y + 1);
DRW_shgroup_uniform_vec4_copy(shsub, "min_max_uv", min_max_uv);
DRW_shgroup_call_obmat(shsub, info.batch, image_mat);
}
BKE_image_release_ibuf(image, tile_buffer, lock);
}
}
}
void ScreenSpaceDrawingMode::update_textures(Image *image, ImageUser *image_user) const
{
State &state = instance_.state;
PartialUpdateChecker<ImageTileData> checker(image, image_user, state.partial_update.user);
PartialUpdateChecker<ImageTileData>::CollectResult changes = checker.collect_changes();
switch (changes.get_result_code()) {
case ePartialUpdateCollectResult::FullUpdateNeeded:
state.mark_all_texture_slots_dirty();
state.float_buffers.clear();
break;
case ePartialUpdateCollectResult::NoChangesDetected:
break;
case ePartialUpdateCollectResult::PartialChangesDetected:
/* Partial update when wrap repeat is enabled is not supported. */
if (state.flags.do_tile_drawing) {
state.float_buffers.clear();
state.mark_all_texture_slots_dirty();
}
else {
do_partial_update(changes);
}
break;
}
do_full_update_for_dirty_textures(image_user);
}
void ScreenSpaceDrawingMode::do_partial_update_float_buffer(
ImBuf *float_buffer, PartialUpdateChecker<ImageTileData>::CollectResult &iterator) const
{
ImBuf *src = iterator.tile_data.tile_buffer;
BLI_assert(float_buffer->float_buffer.data != nullptr);
BLI_assert(float_buffer->byte_buffer.data == nullptr);
BLI_assert(src->float_buffer.data == nullptr);
BLI_assert(src->byte_buffer.data != nullptr);
/* Calculate the overlap between the updated region and the buffer size. Partial Update Checker
* always returns a tile (256x256). Which could lay partially outside the buffer when using
* different resolutions.
*/
rcti buffer_rect;
BLI_rcti_init(&buffer_rect, 0, float_buffer->x, 0, float_buffer->y);
rcti clipped_update_region;
const bool has_overlap = BLI_rcti_isect(
&buffer_rect, &iterator.changed_region.region, &clipped_update_region);
if (!has_overlap) {
return;
}
IMB_float_from_rect_ex(float_buffer, src, &clipped_update_region);
}
void ScreenSpaceDrawingMode::do_partial_update(
PartialUpdateChecker<ImageTileData>::CollectResult &iterator) const
{
while (iterator.get_next_change() == ePartialUpdateIterResult::ChangeAvailable) {
/* Quick exit when tile_buffer isn't available. */
if (iterator.tile_data.tile_buffer == nullptr) {
continue;
}
ImBuf *tile_buffer = instance_.state.float_buffers.cached_float_buffer(
iterator.tile_data.tile_buffer);
if (tile_buffer != iterator.tile_data.tile_buffer) {
do_partial_update_float_buffer(tile_buffer, iterator);
}
const float tile_width = float(iterator.tile_data.tile_buffer->x);
const float tile_height = float(iterator.tile_data.tile_buffer->y);
for (const TextureInfo &info : instance_.state.texture_infos) {
/* Dirty images will receive a full update. No need to do a partial one now. */
if (info.need_full_update) {
continue;
}
GPUTexture *texture = info.texture;
const float texture_width = GPU_texture_width(texture);
const float texture_height = GPU_texture_height(texture);
/* TODO: early bound check. */
ImageTileWrapper tile_accessor(iterator.tile_data.tile);
float tile_offset_x = float(tile_accessor.get_tile_x_offset());
float tile_offset_y = float(tile_accessor.get_tile_y_offset());
rcti *changed_region_in_texel_space = &iterator.changed_region.region;
rctf changed_region_in_uv_space;
BLI_rctf_init(
&changed_region_in_uv_space,
float(changed_region_in_texel_space->xmin) / float(iterator.tile_data.tile_buffer->x) +
tile_offset_x,
float(changed_region_in_texel_space->xmax) / float(iterator.tile_data.tile_buffer->x) +
tile_offset_x,
float(changed_region_in_texel_space->ymin) / float(iterator.tile_data.tile_buffer->y) +
tile_offset_y,
float(changed_region_in_texel_space->ymax) / float(iterator.tile_data.tile_buffer->y) +
tile_offset_y);
rctf changed_overlapping_region_in_uv_space;
const bool region_overlap = BLI_rctf_isect(&info.clipping_uv_bounds,
&changed_region_in_uv_space,
&changed_overlapping_region_in_uv_space);
if (!region_overlap) {
continue;
}
/* Convert the overlapping region to texel space and to ss_pixel space...
* TODO: first convert to ss_pixel space as integer based. and from there go back to texel
* space. But perhaps this isn't needed and we could use an extraction offset somehow. */
rcti gpu_texture_region_to_update;
BLI_rcti_init(
&gpu_texture_region_to_update,
floor((changed_overlapping_region_in_uv_space.xmin - info.clipping_uv_bounds.xmin) *
texture_width / BLI_rctf_size_x(&info.clipping_uv_bounds)),
floor((changed_overlapping_region_in_uv_space.xmax - info.clipping_uv_bounds.xmin) *
texture_width / BLI_rctf_size_x(&info.clipping_uv_bounds)),
ceil((changed_overlapping_region_in_uv_space.ymin - info.clipping_uv_bounds.ymin) *
texture_height / BLI_rctf_size_y(&info.clipping_uv_bounds)),
ceil((changed_overlapping_region_in_uv_space.ymax - info.clipping_uv_bounds.ymin) *
texture_height / BLI_rctf_size_y(&info.clipping_uv_bounds)));
rcti tile_region_to_extract;
BLI_rcti_init(
&tile_region_to_extract,
floor((changed_overlapping_region_in_uv_space.xmin - tile_offset_x) * tile_width),
floor((changed_overlapping_region_in_uv_space.xmax - tile_offset_x) * tile_width),
ceil((changed_overlapping_region_in_uv_space.ymin - tile_offset_y) * tile_height),
ceil((changed_overlapping_region_in_uv_space.ymax - tile_offset_y) * tile_height));
/* Create an image buffer with a size.
* Extract and scale into an imbuf. */
const int texture_region_width = BLI_rcti_size_x(&gpu_texture_region_to_update);
const int texture_region_height = BLI_rcti_size_y(&gpu_texture_region_to_update);
ImBuf extracted_buffer;
IMB_initImBuf(
&extracted_buffer, texture_region_width, texture_region_height, 32, IB_rectfloat);
int offset = 0;
for (int y = gpu_texture_region_to_update.ymin; y < gpu_texture_region_to_update.ymax; y++) {
float yf = y / (float)texture_height;
float v = info.clipping_uv_bounds.ymax * yf + info.clipping_uv_bounds.ymin * (1.0 - yf) -
tile_offset_y;
for (int x = gpu_texture_region_to_update.xmin; x < gpu_texture_region_to_update.xmax; x++)
{
float xf = x / (float)texture_width;
float u = info.clipping_uv_bounds.xmax * xf + info.clipping_uv_bounds.xmin * (1.0 - xf) -
tile_offset_x;
imbuf::interpolate_nearest_border_fl(tile_buffer,
&extracted_buffer.float_buffer.data[offset * 4],
u * tile_buffer->x,
v * tile_buffer->y);
offset++;
}
}
IMB_gpu_clamp_half_float(&extracted_buffer);
GPU_texture_update_sub(texture,
GPU_DATA_FLOAT,
extracted_buffer.float_buffer.data,
gpu_texture_region_to_update.xmin,
gpu_texture_region_to_update.ymin,
0,
extracted_buffer.x,
extracted_buffer.y,
0);
imb_freerectImbuf_all(&extracted_buffer);
}
}
}
void ScreenSpaceDrawingMode::do_full_update_for_dirty_textures(const ImageUser *image_user) const
{
for (TextureInfo &info : instance_.state.texture_infos) {
if (!info.need_full_update) {
continue;
}
do_full_update_gpu_texture(info, image_user);
}
}
void ScreenSpaceDrawingMode::do_full_update_gpu_texture(TextureInfo &info,
const ImageUser *image_user) const
{
ImBuf texture_buffer;
const int texture_width = GPU_texture_width(info.texture);
const int texture_height = GPU_texture_height(info.texture);
IMB_initImBuf(&texture_buffer, texture_width, texture_height, 0, IB_rectfloat);
ImageUser tile_user = {0};
if (image_user) {
tile_user = *image_user;
}
void *lock;
Image *image = instance_.state.image;
LISTBASE_FOREACH (ImageTile *, image_tile_ptr, &image->tiles) {
const ImageTileWrapper image_tile(image_tile_ptr);
tile_user.tile = image_tile.get_tile_number();
ImBuf *tile_buffer = BKE_image_acquire_ibuf(image, &tile_user, &lock);
if (tile_buffer != nullptr) {
do_full_update_texture_slot(info, texture_buffer, *tile_buffer, image_tile);
}
BKE_image_release_ibuf(image, tile_buffer, lock);
}
IMB_gpu_clamp_half_float(&texture_buffer);
GPU_texture_update(info.texture, GPU_DATA_FLOAT, texture_buffer.float_buffer.data);
imb_freerectImbuf_all(&texture_buffer);
}
void ScreenSpaceDrawingMode::do_full_update_texture_slot(const TextureInfo &texture_info,
ImBuf &texture_buffer,
ImBuf &tile_buffer,
const ImageTileWrapper &image_tile) const
{
const int texture_width = texture_buffer.x;
const int texture_height = texture_buffer.y;
ImBuf *float_tile_buffer = instance_.state.float_buffers.cached_float_buffer(&tile_buffer);
/* IMB_transform works in a non-consistent space. This should be documented or fixed!.
* Construct a variant of the info_uv_to_texture that adds the texel space
* transformation. */
float4x4 uv_to_texel;
rctf texture_area;
rctf tile_area;
BLI_rctf_init(&texture_area, 0.0, texture_width, 0.0, texture_height);
BLI_rctf_init(
&tile_area,
tile_buffer.x * (texture_info.clipping_uv_bounds.xmin - image_tile.get_tile_x_offset()),
tile_buffer.x * (texture_info.clipping_uv_bounds.xmax - image_tile.get_tile_x_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()));
BLI_rctf_transform_calc_m4_pivot_min(&tile_area, &texture_area, uv_to_texel.ptr());
uv_to_texel = math::invert(uv_to_texel);
rctf crop_rect;
const rctf *crop_rect_ptr = nullptr;
eIMBTransformMode transform_mode;
if (instance_.state.flags.do_tile_drawing) {
transform_mode = IMB_TRANSFORM_MODE_WRAP_REPEAT;
}
else {
BLI_rctf_init(&crop_rect, 0.0, tile_buffer.x, 0.0, tile_buffer.y);
crop_rect_ptr = &crop_rect;
transform_mode = IMB_TRANSFORM_MODE_CROP_SRC;
}
IMB_transform(float_tile_buffer,
&texture_buffer,
transform_mode,
IMB_FILTER_NEAREST,
uv_to_texel.ptr(),
crop_rect_ptr);
}
void ScreenSpaceDrawingMode::begin_sync() const
{
instance_.state.passes.image_pass = create_image_pass();
instance_.state.passes.depth_pass = create_depth_pass();
}
void ScreenSpaceDrawingMode::image_sync(Image *image, ImageUser *iuser) const
{
State &state = instance_.state;
state.partial_update.ensure_image(image);
state.clear_need_full_update_flag();
state.float_buffers.reset_usage_flags();
/* Step: Find out which screen space textures are needed to draw on the screen. Recycle
* textures that are not on screen anymore. */
OneTexture method(&state);
method.ensure_texture_infos();
method.update_bounds(instance_.region);
/* Step: Check for changes in the image user compared to the last time. */
state.update_image_usage(iuser);
/* Step: Update the GPU textures based on the changes in the image. */
method.ensure_gpu_textures_allocation();
update_textures(image, iuser);
/* Step: Add the GPU textures to the shgroup. */
state.update_batches();
if (!state.flags.do_tile_drawing) {
add_depth_shgroups(image, iuser);
}
add_shgroups();
}
void ScreenSpaceDrawingMode::draw_finish() const
{
instance_.state.float_buffers.remove_unused_buffers();
}
void ScreenSpaceDrawingMode::draw_viewport() const
{
State *instance_data = &instance_.state;
DefaultFramebufferList *dfbl = DRW_viewport_framebuffer_list_get();
GPU_framebuffer_bind(dfbl->default_fb);
static float clear_col[4] = {0.0f, 0.0f, 0.0f, 0.0f};
float clear_depth = instance_data->flags.do_tile_drawing ? 0.75 : 1.0f;
GPU_framebuffer_clear_color_depth(dfbl->default_fb, clear_col, clear_depth);
DRW_view_set_active(instance_data->view);
DRW_draw_pass(instance_data->passes.depth_pass);
GPU_framebuffer_bind(dfbl->color_only_fb);
DRW_draw_pass(instance_data->passes.image_pass);
DRW_view_set_active(nullptr);
GPU_framebuffer_bind(dfbl->default_fb);
}
} // namespace blender::image_engine

View File

@@ -8,8 +8,6 @@
#pragma once
#include "BKE_image_partial_update.hh"
#include "IMB_imbuf_types.hh"
#include "IMB_interp.hh"
@@ -19,16 +17,19 @@
#include "image_batches.hh"
#include "image_private.hh"
namespace blender::draw::image_engine {
#include "DNA_windowmanager_types.h"
namespace blender::image_engine {
class Instance;
constexpr float EPSILON_UV_BOUNDS = 0.00001f;
class BaseTextureMethod {
protected:
IMAGE_InstanceData *instance_data;
State *instance_data;
protected:
BaseTextureMethod(IMAGE_InstanceData *instance_data) : instance_data(instance_data) {}
BaseTextureMethod(State *instance_data) : instance_data(instance_data) {}
public:
/**
@@ -50,7 +51,7 @@ class BaseTextureMethod {
*/
class OneTexture : public BaseTextureMethod {
public:
OneTexture(IMAGE_InstanceData *instance_data) : BaseTextureMethod(instance_data) {}
OneTexture(State *instance_data) : BaseTextureMethod(instance_data) {}
void ensure_texture_infos() override
{
instance_data->texture_infos.resize(1);
@@ -118,7 +119,7 @@ template<size_t Divisions> class ScreenTileTextures : public BaseTextureMethod {
};
public:
ScreenTileTextures(IMAGE_InstanceData *instance_data) : BaseTextureMethod(instance_data) {}
ScreenTileTextures(State *instance_data) : BaseTextureMethod(instance_data) {}
/**
* \brief Ensure enough texture infos are allocated in `instance_data`.
@@ -265,89 +266,24 @@ template<size_t Divisions> class ScreenTileTextures : public BaseTextureMethod {
using namespace blender::bke::image::partial_update;
using namespace blender::bke::image;
template<typename TextureMethod> class ScreenSpaceDrawingMode : public AbstractDrawingMode {
class ScreenSpaceDrawingMode : public AbstractDrawingMode {
private:
DRWPass *create_image_pass() const
{
DRWState state = static_cast<DRWState>(DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_ALWAYS |
DRW_STATE_BLEND_ALPHA_PREMUL);
return DRW_pass_create("Image", state);
}
Instance &instance_;
DRWPass *create_depth_pass() const
{
/* Depth is needed for background overlay rendering. Near depth is used for
* transparency checker and Far depth is used for indicating the image size. */
DRWState state = static_cast<DRWState>(DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS_EQUAL);
return DRW_pass_create("Depth", state);
}
public:
ScreenSpaceDrawingMode(Instance &instance) : instance_(instance) {}
void add_shgroups(const IMAGE_InstanceData *instance_data) const
{
const ShaderParameters &sh_params = instance_data->sh_params;
GPUShader *shader = IMAGE_shader_image_get();
DefaultTextureList *dtxl = DRW_viewport_texture_list_get();
DRWShadingGroup *shgrp = DRW_shgroup_create(shader, instance_data->passes.image_pass);
DRW_shgroup_uniform_vec2_copy(shgrp, "farNearDistances", sh_params.far_near);
DRW_shgroup_uniform_vec4_copy(shgrp, "shuffle", sh_params.shuffle);
DRW_shgroup_uniform_int_copy(shgrp, "drawFlags", static_cast<int32_t>(sh_params.flags));
DRW_shgroup_uniform_bool_copy(shgrp, "imgPremultiplied", sh_params.use_premul_alpha);
DRW_shgroup_uniform_texture(shgrp, "depth_texture", dtxl->depth);
float image_mat[4][4];
unit_m4(image_mat);
for (const TextureInfo &info : instance_data->texture_infos) {
DRWShadingGroup *shgrp_sub = DRW_shgroup_create_sub(shgrp);
DRW_shgroup_uniform_ivec2_copy(shgrp_sub, "offset", info.offset());
DRW_shgroup_uniform_texture_ex(
shgrp_sub, "imageTexture", info.texture, GPUSamplerState::default_sampler());
DRW_shgroup_call_obmat(shgrp_sub, info.batch, image_mat);
}
}
private:
DRWPass *create_image_pass() const;
DRWPass *create_depth_pass() const;
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_InstanceData &instance_data,
Image *image,
ImageUser *image_user) const
{
GPUShader *shader = IMAGE_shader_depth_get();
DRWShadingGroup *shgrp = DRW_shgroup_create(shader, instance_data.passes.depth_pass);
float image_mat[4][4];
unit_m4(image_mat);
ImageUser tile_user = {0};
if (image_user) {
tile_user = *image_user;
}
for (const TextureInfo &info : instance_data.texture_infos) {
LISTBASE_FOREACH (ImageTile *, image_tile_ptr, &image->tiles) {
const ImageTileWrapper image_tile(image_tile_ptr);
const int tile_x = image_tile.get_tile_x_offset();
const int tile_y = image_tile.get_tile_y_offset();
tile_user.tile = image_tile.get_tile_number();
/* NOTE: `BKE_image_has_ibuf` doesn't work as it fails for render results. That could be a
* bug or a feature. For now we just acquire to determine if there is a texture. */
void *lock;
ImBuf *tile_buffer = BKE_image_acquire_ibuf(image, &tile_user, &lock);
if (tile_buffer != nullptr) {
instance_data.float_buffers.mark_used(tile_buffer);
DRWShadingGroup *shsub = DRW_shgroup_create_sub(shgrp);
float4 min_max_uv(tile_x, tile_y, tile_x + 1, tile_y + 1);
DRW_shgroup_uniform_vec4_copy(shsub, "min_max_uv", min_max_uv);
DRW_shgroup_call_obmat(shsub, info.batch, image_mat);
}
BKE_image_release_ibuf(image, tile_buffer, lock);
}
}
}
void add_depth_shgroups(Image *image, ImageUser *image_user) const;
/**
* \brief Update GPUTextures for drawing the image.
@@ -355,335 +291,30 @@ template<typename TextureMethod> class ScreenSpaceDrawingMode : public AbstractD
* 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_InstanceData &instance_data,
Image *image,
ImageUser *image_user) const
{
PartialUpdateChecker<ImageTileData> checker(
image, image_user, instance_data.partial_update.user);
PartialUpdateChecker<ImageTileData>::CollectResult changes = checker.collect_changes();
switch (changes.get_result_code()) {
case ePartialUpdateCollectResult::FullUpdateNeeded:
instance_data.mark_all_texture_slots_dirty();
instance_data.float_buffers.clear();
break;
case ePartialUpdateCollectResult::NoChangesDetected:
break;
case ePartialUpdateCollectResult::PartialChangesDetected:
/* Partial update when wrap repeat is enabled is not supported. */
if (instance_data.flags.do_tile_drawing) {
instance_data.float_buffers.clear();
instance_data.mark_all_texture_slots_dirty();
}
else {
do_partial_update(changes, instance_data);
}
break;
}
do_full_update_for_dirty_textures(instance_data, image_user);
}
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
{
ImBuf *src = iterator.tile_data.tile_buffer;
BLI_assert(float_buffer->float_buffer.data != nullptr);
BLI_assert(float_buffer->byte_buffer.data == nullptr);
BLI_assert(src->float_buffer.data == nullptr);
BLI_assert(src->byte_buffer.data != nullptr);
/* Calculate the overlap between the updated region and the buffer size. Partial Update Checker
* always returns a tile (256x256). Which could lay partially outside the buffer when using
* different resolutions.
*/
rcti buffer_rect;
BLI_rcti_init(&buffer_rect, 0, float_buffer->x, 0, float_buffer->y);
rcti clipped_update_region;
const bool has_overlap = BLI_rcti_isect(
&buffer_rect, &iterator.changed_region.region, &clipped_update_region);
if (!has_overlap) {
return;
}
IMB_float_from_rect_ex(float_buffer, src, &clipped_update_region);
}
void do_partial_update(PartialUpdateChecker<ImageTileData>::CollectResult &iterator,
IMAGE_InstanceData &instance_data) const
{
while (iterator.get_next_change() == ePartialUpdateIterResult::ChangeAvailable) {
/* Quick exit when tile_buffer isn't available. */
if (iterator.tile_data.tile_buffer == nullptr) {
continue;
}
ImBuf *tile_buffer = instance_data.float_buffers.cached_float_buffer(
iterator.tile_data.tile_buffer);
if (tile_buffer != iterator.tile_data.tile_buffer) {
do_partial_update_float_buffer(tile_buffer, iterator);
}
const float tile_width = float(iterator.tile_data.tile_buffer->x);
const float tile_height = float(iterator.tile_data.tile_buffer->y);
for (const TextureInfo &info : instance_data.texture_infos) {
/* Dirty images will receive a full update. No need to do a partial one now. */
if (info.need_full_update) {
continue;
}
GPUTexture *texture = info.texture;
const float texture_width = GPU_texture_width(texture);
const float texture_height = GPU_texture_height(texture);
/* TODO: early bound check. */
ImageTileWrapper tile_accessor(iterator.tile_data.tile);
float tile_offset_x = float(tile_accessor.get_tile_x_offset());
float tile_offset_y = float(tile_accessor.get_tile_y_offset());
rcti *changed_region_in_texel_space = &iterator.changed_region.region;
rctf changed_region_in_uv_space;
BLI_rctf_init(
&changed_region_in_uv_space,
float(changed_region_in_texel_space->xmin) / float(iterator.tile_data.tile_buffer->x) +
tile_offset_x,
float(changed_region_in_texel_space->xmax) / float(iterator.tile_data.tile_buffer->x) +
tile_offset_x,
float(changed_region_in_texel_space->ymin) / float(iterator.tile_data.tile_buffer->y) +
tile_offset_y,
float(changed_region_in_texel_space->ymax) / float(iterator.tile_data.tile_buffer->y) +
tile_offset_y);
rctf changed_overlapping_region_in_uv_space;
const bool region_overlap = BLI_rctf_isect(&info.clipping_uv_bounds,
&changed_region_in_uv_space,
&changed_overlapping_region_in_uv_space);
if (!region_overlap) {
continue;
}
/* Convert the overlapping region to texel space and to ss_pixel space...
* TODO: first convert to ss_pixel space as integer based. and from there go back to texel
* space. But perhaps this isn't needed and we could use an extraction offset somehow. */
rcti gpu_texture_region_to_update;
BLI_rcti_init(
&gpu_texture_region_to_update,
floor((changed_overlapping_region_in_uv_space.xmin - info.clipping_uv_bounds.xmin) *
texture_width / BLI_rctf_size_x(&info.clipping_uv_bounds)),
floor((changed_overlapping_region_in_uv_space.xmax - info.clipping_uv_bounds.xmin) *
texture_width / BLI_rctf_size_x(&info.clipping_uv_bounds)),
ceil((changed_overlapping_region_in_uv_space.ymin - info.clipping_uv_bounds.ymin) *
texture_height / BLI_rctf_size_y(&info.clipping_uv_bounds)),
ceil((changed_overlapping_region_in_uv_space.ymax - info.clipping_uv_bounds.ymin) *
texture_height / BLI_rctf_size_y(&info.clipping_uv_bounds)));
rcti tile_region_to_extract;
BLI_rcti_init(
&tile_region_to_extract,
floor((changed_overlapping_region_in_uv_space.xmin - tile_offset_x) * tile_width),
floor((changed_overlapping_region_in_uv_space.xmax - tile_offset_x) * tile_width),
ceil((changed_overlapping_region_in_uv_space.ymin - tile_offset_y) * tile_height),
ceil((changed_overlapping_region_in_uv_space.ymax - tile_offset_y) * tile_height));
/* Create an image buffer with a size.
* Extract and scale into an imbuf. */
const int texture_region_width = BLI_rcti_size_x(&gpu_texture_region_to_update);
const int texture_region_height = BLI_rcti_size_y(&gpu_texture_region_to_update);
ImBuf extracted_buffer;
IMB_initImBuf(
&extracted_buffer, texture_region_width, texture_region_height, 32, IB_rectfloat);
int offset = 0;
for (int y = gpu_texture_region_to_update.ymin; y < gpu_texture_region_to_update.ymax; y++)
{
float yf = y / (float)texture_height;
float v = info.clipping_uv_bounds.ymax * yf + info.clipping_uv_bounds.ymin * (1.0 - yf) -
tile_offset_y;
for (int x = gpu_texture_region_to_update.xmin; x < gpu_texture_region_to_update.xmax;
x++)
{
float xf = x / (float)texture_width;
float u = info.clipping_uv_bounds.xmax * xf +
info.clipping_uv_bounds.xmin * (1.0 - xf) - tile_offset_x;
imbuf::interpolate_nearest_border_fl(tile_buffer,
&extracted_buffer.float_buffer.data[offset * 4],
u * tile_buffer->x,
v * tile_buffer->y);
offset++;
}
}
IMB_gpu_clamp_half_float(&extracted_buffer);
GPU_texture_update_sub(texture,
GPU_DATA_FLOAT,
extracted_buffer.float_buffer.data,
gpu_texture_region_to_update.xmin,
gpu_texture_region_to_update.ymin,
0,
extracted_buffer.x,
extracted_buffer.y,
0);
imb_freerectImbuf_all(&extracted_buffer);
}
}
}
void do_full_update_for_dirty_textures(IMAGE_InstanceData &instance_data,
const ImageUser *image_user) const
{
for (TextureInfo &info : instance_data.texture_infos) {
if (!info.need_full_update) {
continue;
}
do_full_update_gpu_texture(info, instance_data, image_user);
}
}
void do_full_update_gpu_texture(TextureInfo &info,
IMAGE_InstanceData &instance_data,
const ImageUser *image_user) const
{
ImBuf texture_buffer;
const int texture_width = GPU_texture_width(info.texture);
const int texture_height = GPU_texture_height(info.texture);
IMB_initImBuf(&texture_buffer, texture_width, texture_height, 0, IB_rectfloat);
ImageUser tile_user = {0};
if (image_user) {
tile_user = *image_user;
}
void *lock;
Image *image = instance_data.image;
LISTBASE_FOREACH (ImageTile *, image_tile_ptr, &image->tiles) {
const ImageTileWrapper image_tile(image_tile_ptr);
tile_user.tile = image_tile.get_tile_number();
ImBuf *tile_buffer = BKE_image_acquire_ibuf(image, &tile_user, &lock);
if (tile_buffer != nullptr) {
do_full_update_texture_slot(instance_data, info, texture_buffer, *tile_buffer, image_tile);
}
BKE_image_release_ibuf(image, tile_buffer, lock);
}
IMB_gpu_clamp_half_float(&texture_buffer);
GPU_texture_update(info.texture, GPU_DATA_FLOAT, texture_buffer.float_buffer.data);
imb_freerectImbuf_all(&texture_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(IMAGE_InstanceData &instance_data,
const TextureInfo &texture_info,
void do_full_update_texture_slot(const TextureInfo &texture_info,
ImBuf &texture_buffer,
ImBuf &tile_buffer,
const ImageTileWrapper &image_tile) const
{
const int texture_width = texture_buffer.x;
const int texture_height = texture_buffer.y;
ImBuf *float_tile_buffer = instance_data.float_buffers.cached_float_buffer(&tile_buffer);
/* IMB_transform works in a non-consistent space. This should be documented or fixed!.
* Construct a variant of the info_uv_to_texture that adds the texel space
* transformation. */
float4x4 uv_to_texel;
rctf texture_area;
rctf tile_area;
BLI_rctf_init(&texture_area, 0.0, texture_width, 0.0, texture_height);
BLI_rctf_init(
&tile_area,
tile_buffer.x * (texture_info.clipping_uv_bounds.xmin - image_tile.get_tile_x_offset()),
tile_buffer.x * (texture_info.clipping_uv_bounds.xmax - image_tile.get_tile_x_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()));
BLI_rctf_transform_calc_m4_pivot_min(&tile_area, &texture_area, uv_to_texel.ptr());
uv_to_texel = math::invert(uv_to_texel);
rctf crop_rect;
const rctf *crop_rect_ptr = nullptr;
eIMBTransformMode transform_mode;
if (instance_data.flags.do_tile_drawing) {
transform_mode = IMB_TRANSFORM_MODE_WRAP_REPEAT;
}
else {
BLI_rctf_init(&crop_rect, 0.0, tile_buffer.x, 0.0, tile_buffer.y);
crop_rect_ptr = &crop_rect;
transform_mode = IMB_TRANSFORM_MODE_CROP_SRC;
}
IMB_transform(float_tile_buffer,
&texture_buffer,
transform_mode,
IMB_FILTER_NEAREST,
uv_to_texel.ptr(),
crop_rect_ptr);
}
const ImageTileWrapper &image_tile) const;
public:
void begin_sync(IMAGE_Data *vedata) const override
{
IMAGE_InstanceData *instance_data = vedata->instance_data;
instance_data->passes.image_pass = create_image_pass();
instance_data->passes.depth_pass = create_depth_pass();
}
void image_sync(IMAGE_Data *vedata, Image *image, ImageUser *iuser) const override
{
const DRWContextState *draw_ctx = DRW_context_state_get();
IMAGE_InstanceData *instance_data = vedata->instance_data;
TextureMethod method(instance_data);
method.ensure_texture_infos();
instance_data->partial_update.ensure_image(image);
instance_data->clear_need_full_update_flag();
instance_data->float_buffers.reset_usage_flags();
/* Step: Find out which screen space textures are needed to draw on the screen. Recycle
* textures that are not on screen anymore. */
const ARegion *region = draw_ctx->region;
method.update_bounds(region);
/* Step: Check for changes in the image user compared to the last time. */
instance_data->update_image_usage(iuser);
/* Step: Update the GPU textures based on the changes in the image. */
method.ensure_gpu_textures_allocation();
update_textures(*instance_data, image, iuser);
/* Step: Add the GPU textures to the shgroup. */
instance_data->update_batches();
if (!instance_data->flags.do_tile_drawing) {
add_depth_shgroups(*instance_data, image, iuser);
}
add_shgroups(instance_data);
}
void draw_finish(IMAGE_Data *vedata) const override
{
IMAGE_InstanceData *instance_data = vedata->instance_data;
instance_data->float_buffers.remove_unused_buffers();
}
void draw_viewport(IMAGE_Data *vedata) const override
{
IMAGE_InstanceData *instance_data = vedata->instance_data;
DefaultFramebufferList *dfbl = DRW_viewport_framebuffer_list_get();
GPU_framebuffer_bind(dfbl->default_fb);
static float clear_col[4] = {0.0f, 0.0f, 0.0f, 0.0f};
float clear_depth = instance_data->flags.do_tile_drawing ? 0.75 : 1.0f;
GPU_framebuffer_clear_color_depth(dfbl->default_fb, clear_col, clear_depth);
DRW_view_set_active(instance_data->view);
DRW_draw_pass(instance_data->passes.depth_pass);
GPU_framebuffer_bind(dfbl->color_only_fb);
DRW_draw_pass(instance_data->passes.image_pass);
DRW_view_set_active(nullptr);
GPU_framebuffer_bind(dfbl->default_fb);
}
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::draw::image_engine
} // namespace blender::image_engine

View File

@@ -29,128 +29,44 @@
#include "image_drawing_mode.hh"
#include "image_engine.h"
#include "image_instance.hh"
#include "image_private.hh"
#include "image_space_image.hh"
#include "image_space_node.hh"
namespace blender::draw::image_engine {
namespace blender::image_engine {
static std::unique_ptr<AbstractSpaceAccessor> space_accessor_from_context(
const DRWContextState *draw_ctx)
{
const char space_type = draw_ctx->space_data->spacetype;
if (space_type == SPACE_IMAGE) {
return std::make_unique<SpaceImageAccessor>((SpaceImage *)draw_ctx->space_data);
}
if (space_type == SPACE_NODE) {
return std::make_unique<SpaceNodeAccessor>((SpaceNode *)draw_ctx->space_data);
}
BLI_assert_unreachable();
return nullptr;
}
template<
/** \brief Drawing mode to use.
*
* Useful during development to switch between drawing implementations.
*/
typename DrawingMode = ScreenSpaceDrawingMode<OneTexture>>
class ImageEngine {
private:
const DRWContextState *draw_ctx;
IMAGE_Data *vedata;
std::unique_ptr<AbstractSpaceAccessor> space;
DrawingMode drawing_mode;
public:
ImageEngine(const DRWContextState *draw_ctx, IMAGE_Data *vedata)
: draw_ctx(draw_ctx), vedata(vedata), space(space_accessor_from_context(draw_ctx))
{
}
virtual ~ImageEngine() = default;
void begin_sync()
{
IMAGE_InstanceData *instance_data = vedata->instance_data;
drawing_mode.begin_sync(vedata);
/* Setup full screen view matrix. */
const ARegion *region = draw_ctx->region;
float winmat[4][4], viewmat[4][4];
orthographic_m4(viewmat, 0.0, region->winx, 0.0, region->winy, 0.0, 1.0);
unit_m4(winmat);
instance_data->view = DRW_view_create(viewmat, winmat, nullptr, nullptr, nullptr);
}
void image_sync()
{
IMAGE_InstanceData *instance_data = vedata->instance_data;
Main *bmain = CTX_data_main(draw_ctx->evil_C);
instance_data->image = space->get_image(bmain);
if (instance_data->image == nullptr) {
/* Early exit, nothing to draw. */
return;
}
instance_data->flags.do_tile_drawing = instance_data->image->source != IMA_SRC_TILED &&
space->use_tile_drawing();
void *lock;
ImBuf *image_buffer = space->acquire_image_buffer(instance_data->image, &lock);
/* Setup the matrix to go from screen UV coordinates to UV texture space coordinates. */
float image_resolution[2] = {image_buffer ? image_buffer->x : 1024.0f,
image_buffer ? image_buffer->y : 1024.0f};
space->init_ss_to_texture_matrix(draw_ctx->region,
instance_data->image->runtime.backdrop_offset,
image_resolution,
instance_data->ss_to_texture);
const Scene *scene = DRW_context_state_get()->scene;
instance_data->sh_params.update(space.get(), scene, instance_data->image, image_buffer);
space->release_buffer(instance_data->image, image_buffer, lock);
ImageUser *iuser = space->get_image_user();
if (instance_data->image->rr != nullptr) {
BKE_image_multilayer_index(instance_data->image->rr, iuser);
}
else {
BKE_image_multiview_index(instance_data->image, iuser);
}
drawing_mode.image_sync(vedata, instance_data->image, iuser);
}
void draw_finish()
{
drawing_mode.draw_finish(vedata);
IMAGE_InstanceData *instance_data = vedata->instance_data;
instance_data->image = nullptr;
}
void draw_viewport()
{
drawing_mode.draw_viewport(vedata);
}
struct IMAGE_Data {
void *engine_type;
DRWViewportEmptyList *fbl;
DRWViewportEmptyList *txl;
DRWViewportEmptyList *psl;
DRWViewportEmptyList *stl;
Instance *instance;
char info[GPU_INFO_SIZE];
};
/* -------------------------------------------------------------------- */
/** \name Engine Callbacks
* \{ */
static void IMAGE_engine_init(void *ved)
static void IMAGE_engine_init(void *vedata)
{
IMAGE_Data *vedata = (IMAGE_Data *)ved;
if (vedata->instance_data == nullptr) {
vedata->instance_data = MEM_new<IMAGE_InstanceData>(__func__);
IMAGE_Data *ved = reinterpret_cast<IMAGE_Data *>(vedata);
if (ved->instance == nullptr) {
ved->instance = new image_engine::Instance();
}
const DRWContextState *ctx_state = DRW_context_state_get();
Main *bmain = CTX_data_main(ctx_state->evil_C);
ved->instance->init(bmain, ctx_state->space_data, ctx_state->region);
}
static void IMAGE_cache_init(void *vedata)
{
const DRWContextState *draw_ctx = DRW_context_state_get();
ImageEngine image_engine(draw_ctx, static_cast<IMAGE_Data *>(vedata));
image_engine.begin_sync();
image_engine.image_sync();
IMAGE_Data *ved = reinterpret_cast<IMAGE_Data *>(vedata);
ved->instance->begin_sync();
ved->instance->image_sync();
}
static void IMAGE_cache_populate(void * /*vedata*/, Object * /*ob*/)
@@ -160,10 +76,9 @@ static void IMAGE_cache_populate(void * /*vedata*/, Object * /*ob*/)
static void IMAGE_draw_scene(void *vedata)
{
const DRWContextState *draw_ctx = DRW_context_state_get();
ImageEngine image_engine(draw_ctx, static_cast<IMAGE_Data *>(vedata));
image_engine.draw_viewport();
image_engine.draw_finish();
IMAGE_Data *ved = reinterpret_cast<IMAGE_Data *>(vedata);
ved->instance->draw_viewport();
ved->instance->draw_finish();
}
static void IMAGE_engine_free()
@@ -171,21 +86,20 @@ static void IMAGE_engine_free()
IMAGE_shader_free();
}
static void IMAGE_instance_free(void *_instance_data)
static void IMAGE_instance_free(void *instance)
{
IMAGE_InstanceData *instance_data = reinterpret_cast<IMAGE_InstanceData *>(_instance_data);
MEM_delete(instance_data);
delete reinterpret_cast<image_engine::Instance *>(instance);
}
/** \} */
static const DrawEngineDataSize IMAGE_data_size = DRW_VIEWPORT_DATA_SIZE(IMAGE_Data);
} // namespace blender::draw::image_engine
} // namespace blender::image_engine
extern "C" {
using namespace blender::draw::image_engine;
using namespace blender::image_engine;
DrawEngineType draw_engine_image_type = {
/*next*/ nullptr,

View File

@@ -10,7 +10,7 @@
#include "BLI_utildefines.h"
namespace blender::draw::image_engine {
namespace blender::image_engine {
/* Shader parameters. */
enum class ImageDrawFlags {
@@ -22,4 +22,4 @@ enum class ImageDrawFlags {
};
ENUM_OPERATORS(ImageDrawFlags, ImageDrawFlags::Depth);
} // namespace blender::draw::image_engine
} // namespace blender::image_engine

View File

@@ -0,0 +1,111 @@
/* SPDX-FileCopyrightText: 2024 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
#include <DRW_render.hh>
#include "image_drawing_mode.hh"
#include "image_private.hh"
#include "image_space.hh"
#include "image_space_image.hh"
#include "image_space_node.hh"
#include "DNA_space_types.h"
namespace blender::image_engine {
static inline std::unique_ptr<AbstractSpaceAccessor> space_accessor_from_space(
SpaceLink *space_link)
{
if (space_link->spacetype == SPACE_IMAGE) {
return std::make_unique<SpaceImageAccessor>(
static_cast<SpaceImage *>(static_cast<void *>(space_link)));
}
if (space_link->spacetype == SPACE_NODE) {
return std::make_unique<SpaceNodeAccessor>(
static_cast<SpaceNode *>(static_cast<void *>(space_link)));
}
BLI_assert_unreachable();
return nullptr;
}
class Instance {
private:
std::unique_ptr<AbstractSpaceAccessor> space_;
Main *main_;
ScreenSpaceDrawingMode drawing_mode_;
public:
const ARegion *region;
State state;
public:
Instance() : drawing_mode_(*this) {}
void init(Main *main, SpaceLink *space_link, const ARegion *_region)
{
main_ = main;
region = _region;
space_ = space_accessor_from_space(space_link);
}
virtual ~Instance() = default;
void begin_sync()
{
drawing_mode_.begin_sync();
/* Setup full screen view matrix. */
float winmat[4][4], viewmat[4][4];
orthographic_m4(viewmat, 0.0, region->winx, 0.0, region->winy, 0.0, 1.0);
unit_m4(winmat);
state.view = DRW_view_create(viewmat, winmat, nullptr, nullptr, nullptr);
}
void image_sync()
{
state.image = space_->get_image(main_);
if (state.image == nullptr) {
/* Early exit, nothing to draw. */
return;
}
state.flags.do_tile_drawing = state.image->source != IMA_SRC_TILED &&
space_->use_tile_drawing();
void *lock;
ImBuf *image_buffer = space_->acquire_image_buffer(state.image, &lock);
/* Setup the matrix to go from screen UV coordinates to UV texture space coordinates. */
float image_resolution[2] = {image_buffer ? image_buffer->x : 1024.0f,
image_buffer ? image_buffer->y : 1024.0f};
space_->init_ss_to_texture_matrix(
region, state.image->runtime.backdrop_offset, image_resolution, state.ss_to_texture);
const Scene *scene = DRW_context_state_get()->scene;
state.sh_params.update(space_.get(), scene, state.image, image_buffer);
space_->release_buffer(state.image, image_buffer, lock);
ImageUser *iuser = space_->get_image_user();
if (state.image->rr != nullptr) {
BKE_image_multilayer_index(state.image->rr, iuser);
}
else {
BKE_image_multiview_index(state.image, iuser);
}
drawing_mode_.image_sync(state.image, iuser);
}
void draw_finish()
{
drawing_mode_.draw_finish();
state.image = nullptr;
}
void draw_viewport()
{
drawing_mode_.draw_viewport();
}
};
} // namespace blender::image_engine

View File

@@ -12,7 +12,7 @@
#include "BKE_image.hh"
#include "image_instance_data.hh"
#include "image_state.hh"
#include "image_texture_info.hh"
/* Forward declarations */
@@ -22,16 +22,7 @@ struct Image;
/* *********** LISTS *********** */
namespace blender::draw::image_engine {
struct IMAGE_Data {
void *engine_type;
DRWViewportEmptyList *fbl;
DRWViewportEmptyList *txl;
DRWViewportEmptyList *psl;
DRWViewportEmptyList *stl;
IMAGE_InstanceData *instance_data;
};
namespace blender::image_engine {
/**
* Abstract class for a drawing mode of the image engine.
@@ -42,10 +33,10 @@ struct IMAGE_Data {
class AbstractDrawingMode {
public:
virtual ~AbstractDrawingMode() = default;
virtual void begin_sync(IMAGE_Data *vedata) const = 0;
virtual void image_sync(IMAGE_Data *vedata, Image *image, ImageUser *iuser) const = 0;
virtual void draw_viewport(IMAGE_Data *vedata) const = 0;
virtual void draw_finish(IMAGE_Data *vedata) const = 0;
virtual void begin_sync() const = 0;
virtual void image_sync(Image *image, ImageUser *iuser) const = 0;
virtual void draw_viewport() const = 0;
virtual void draw_finish() const = 0;
};
/* `image_shader.cc` */
@@ -54,4 +45,4 @@ GPUShader *IMAGE_shader_image_get();
GPUShader *IMAGE_shader_depth_get();
void IMAGE_shader_free();
} // namespace blender::draw::image_engine
} // namespace blender::image_engine

View File

@@ -15,7 +15,7 @@
#include "image_engine.h"
#include "image_private.hh"
namespace blender::draw::image_engine {
namespace blender::image_engine {
struct IMAGE_Shaders {
GPUShader *image_sh;
@@ -52,4 +52,4 @@ void IMAGE_shader_free()
}
}
} // namespace blender::draw::image_engine
} // namespace blender::image_engine

View File

@@ -19,7 +19,7 @@
#include "image_enums.hh"
#include "image_space.hh"
namespace blender::draw::image_engine {
namespace blender::image_engine {
struct ShaderParameters {
ImageDrawFlags flags = ImageDrawFlags::Default;
@@ -43,4 +43,4 @@ struct ShaderParameters {
}
};
} // namespace blender::draw::image_engine
} // namespace blender::image_engine

View File

@@ -8,7 +8,7 @@
#pragma once
namespace blender::draw::image_engine {
namespace blender::image_engine {
struct ShaderParameters;
@@ -75,4 +75,4 @@ class AbstractSpaceAccessor {
float r_uv_to_texture[4][4]) const = 0;
};
} // namespace blender::draw::image_engine
} // namespace blender::image_engine

View File

@@ -8,9 +8,11 @@
#pragma once
#include "ED_image.hh"
#include "image_private.hh"
namespace blender::draw::image_engine {
namespace blender::image_engine {
class SpaceImageAccessor : public AbstractSpaceAccessor {
SpaceImage *sima;
@@ -108,4 +110,4 @@ class SpaceImageAccessor : public AbstractSpaceAccessor {
}
};
} // namespace blender::draw::image_engine
} // namespace blender::image_engine

View File

@@ -10,7 +10,7 @@
#include "image_private.hh"
namespace blender::draw::image_engine {
namespace blender::image_engine {
class SpaceNodeAccessor : public AbstractSpaceAccessor {
SpaceNode *snode;
@@ -111,4 +111,4 @@ class SpaceNodeAccessor : public AbstractSpaceAccessor {
}
};
} // namespace blender::draw::image_engine
} // namespace blender::image_engine

View File

@@ -20,16 +20,16 @@
#include "DRW_render.hh"
namespace blender::draw::image_engine {
namespace blender::image_engine {
struct IMAGE_InstanceData {
Image *image;
struct State {
Image *image = nullptr;
/** Usage data of the previous time, to identify changes that require a full update. */
ImageUsage last_usage;
PartialImageUpdater partial_update;
PartialImageUpdater partial_update = {};
DRWView *view;
DRWView *view = nullptr;
ShaderParameters sh_params;
struct {
/**
@@ -42,8 +42,8 @@ struct IMAGE_InstanceData {
} flags;
struct {
DRWPass *image_pass;
DRWPass *depth_pass;
DRWPass *image_pass = nullptr;
DRWPass *depth_pass = nullptr;
} passes;
/**
@@ -57,7 +57,7 @@ struct IMAGE_InstanceData {
Vector<TextureInfo> texture_infos;
public:
virtual ~IMAGE_InstanceData() = default;
virtual ~State() = default;
void clear_need_full_update_flag()
{
@@ -96,4 +96,4 @@ struct IMAGE_InstanceData {
}
};
} // namespace blender::draw::image_engine
} // namespace blender::image_engine

View File

@@ -14,7 +14,9 @@
#include "GPU_batch.hh"
#include "GPU_texture.hh"
namespace blender::draw::image_engine {
#include "DRW_render.hh"
namespace blender::image_engine {
struct TextureInfo {
/**
@@ -96,4 +98,4 @@ struct TextureInfo {
}
};
} // namespace blender::draw::image_engine
} // namespace blender::image_engine

View File

@@ -8,7 +8,7 @@
#pragma once
namespace blender::draw::image_engine {
namespace blender::image_engine {
/**
* ImageUsage contains data of the image and image user to identify changes that require a rebuild
@@ -51,4 +51,4 @@ struct ImageUsage {
}
};
} // namespace blender::draw::image_engine
} // namespace blender::image_engine