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:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
395
source/blender/draw/engines/image/image_drawing_mode.cc
Normal file
395
source/blender/draw/engines/image/image_drawing_mode.cc
Normal 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
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
111
source/blender/draw/engines/image/image_instance.hh
Normal file
111
source/blender/draw/engines/image/image_instance.hh
Normal 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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user