Add Chromium-only Blender WebEngine parity work
This commit is contained in:
@@ -0,0 +1,90 @@
|
||||
/* SPDX-FileCopyrightText: 2021 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup draw_engine
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "image_texture_info.hh"
|
||||
|
||||
namespace blender::image_engine {
|
||||
|
||||
/** \brief Create gpu::Batch for a IMAGE_ScreenSpaceTextureInfo. */
|
||||
class BatchUpdater {
|
||||
TextureInfo &info;
|
||||
|
||||
GPUVertFormat format = {0};
|
||||
int pos_id;
|
||||
int uv_id;
|
||||
|
||||
public:
|
||||
BatchUpdater(TextureInfo &info) : info(info) {}
|
||||
|
||||
void update_batch()
|
||||
{
|
||||
ensure_clear_batch();
|
||||
ensure_format();
|
||||
init_batch();
|
||||
}
|
||||
|
||||
private:
|
||||
void ensure_clear_batch()
|
||||
{
|
||||
GPU_BATCH_CLEAR_SAFE(info.batch);
|
||||
if (info.batch == nullptr) {
|
||||
info.batch = GPU_batch_calloc();
|
||||
}
|
||||
}
|
||||
|
||||
void init_batch()
|
||||
{
|
||||
gpu::VertBuf *vbo = create_vbo();
|
||||
GPU_batch_init_ex(info.batch, GPU_PRIM_TRI_FAN, vbo, nullptr, GPU_BATCH_OWNS_VBO);
|
||||
}
|
||||
|
||||
template<typename DataType, typename RectType>
|
||||
static void fill_tri_fan_from_rect(DataType result[4][2], RectType &rect)
|
||||
{
|
||||
result[0][0] = rect.xmin;
|
||||
result[0][1] = rect.ymin;
|
||||
result[1][0] = rect.xmax;
|
||||
result[1][1] = rect.ymin;
|
||||
result[2][0] = rect.xmax;
|
||||
result[2][1] = rect.ymax;
|
||||
result[3][0] = rect.xmin;
|
||||
result[3][1] = rect.ymax;
|
||||
}
|
||||
|
||||
gpu::VertBuf *create_vbo()
|
||||
{
|
||||
gpu::VertBuf *vbo = GPU_vertbuf_create_with_format(format);
|
||||
GPU_vertbuf_data_alloc(*vbo, 4);
|
||||
int pos[4][2];
|
||||
fill_tri_fan_from_rect<int, rcti>(pos, info.clipping_bounds);
|
||||
float uv[4][2];
|
||||
fill_tri_fan_from_rect<float, rctf>(uv, info.clipping_uv_bounds);
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
GPU_vertbuf_attr_set(vbo, pos_id, i, pos[i]);
|
||||
GPU_vertbuf_attr_set(vbo, uv_id, i, uv[i]);
|
||||
}
|
||||
|
||||
return vbo;
|
||||
}
|
||||
|
||||
void ensure_format()
|
||||
{
|
||||
if (format.attr_len == 0) {
|
||||
GPU_vertformat_attr_add(&format, "pos", gpu::VertAttrType::SINT_32_32);
|
||||
GPU_vertformat_attr_add(&format, "uv", gpu::VertAttrType::SFLOAT_32_32);
|
||||
|
||||
pos_id = GPU_vertformat_attr_id_get(&format, "pos");
|
||||
uv_id = GPU_vertformat_attr_id_get(&format, "uv");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace blender::image_engine
|
||||
@@ -0,0 +1,137 @@
|
||||
/* SPDX-FileCopyrightText: 2022 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup draw_engine
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BLI_vector.hh"
|
||||
|
||||
#include "IMB_colormanagement.hh"
|
||||
#include "IMB_imbuf.hh"
|
||||
#include "IMB_imbuf_types.hh"
|
||||
|
||||
namespace blender::image_engine {
|
||||
|
||||
struct FloatImageBuffer {
|
||||
ImBuf *source_buffer = nullptr;
|
||||
ImBuf *float_buffer = nullptr;
|
||||
bool is_used = true;
|
||||
|
||||
FloatImageBuffer(ImBuf *source_buffer, ImBuf *float_buffer)
|
||||
: source_buffer(source_buffer), float_buffer(float_buffer)
|
||||
{
|
||||
}
|
||||
|
||||
FloatImageBuffer(FloatImageBuffer &&other) noexcept
|
||||
{
|
||||
source_buffer = other.source_buffer;
|
||||
float_buffer = other.float_buffer;
|
||||
is_used = other.is_used;
|
||||
other.source_buffer = nullptr;
|
||||
other.float_buffer = nullptr;
|
||||
}
|
||||
|
||||
virtual ~FloatImageBuffer()
|
||||
{
|
||||
IMB_freeImBuf(float_buffer);
|
||||
float_buffer = nullptr;
|
||||
source_buffer = nullptr;
|
||||
}
|
||||
|
||||
FloatImageBuffer &operator=(FloatImageBuffer &&other) noexcept
|
||||
{
|
||||
this->source_buffer = other.source_buffer;
|
||||
this->float_buffer = other.float_buffer;
|
||||
is_used = other.is_used;
|
||||
other.source_buffer = nullptr;
|
||||
other.float_buffer = nullptr;
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Float buffer cache for image buffers.
|
||||
*
|
||||
* Image buffers might not have float buffers which are required for the image engine.
|
||||
* Image buffers are not allowed to have both a float buffer and a byte buffer as some
|
||||
* functionality doesn't know what to do.
|
||||
*
|
||||
* For this reason we store the float buffer in separate image buffers. The FloatBufferCache keep
|
||||
* track of the cached buffers and if they are still used.
|
||||
*/
|
||||
struct FloatBufferCache {
|
||||
private:
|
||||
Vector<FloatImageBuffer> cache_;
|
||||
|
||||
public:
|
||||
ImBuf *cached_float_buffer(ImBuf *image_buffer)
|
||||
{
|
||||
/* Check if we can use the float buffer of the given image_buffer. */
|
||||
if (image_buffer->float_data() != nullptr) {
|
||||
BLI_assert_msg(
|
||||
IMB_colormanagement_space_name_is_scene_linear(
|
||||
IMB_colormanagement_get_float_colorspace(image_buffer)) ||
|
||||
IMB_colormanagement_space_name_is_data(
|
||||
IMB_colormanagement_get_float_colorspace(image_buffer)),
|
||||
"Expected float buffer to be scene_linear or data - if there are code paths where this "
|
||||
"isn't the case we should convert those and add to the FloatBufferCache as well.");
|
||||
return image_buffer;
|
||||
}
|
||||
|
||||
/* Do we have a cached float buffer. */
|
||||
for (FloatImageBuffer &item : cache_) {
|
||||
if (item.source_buffer == image_buffer) {
|
||||
item.is_used = true;
|
||||
return item.float_buffer;
|
||||
}
|
||||
}
|
||||
|
||||
/* Generate a new float buffer. */
|
||||
IMB_float_from_byte(image_buffer);
|
||||
ImBuf *new_imbuf = IMB_allocImBuf(image_buffer->x, image_buffer->y, ImBufFlags::Zero);
|
||||
new_imbuf->color_mode = image_buffer->color_mode;
|
||||
|
||||
new_imbuf->float_buffer = image_buffer->float_buffer;
|
||||
image_buffer->float_buffer = {};
|
||||
|
||||
cache_.append(FloatImageBuffer(image_buffer, new_imbuf));
|
||||
return new_imbuf;
|
||||
}
|
||||
|
||||
void reset_usage_flags()
|
||||
{
|
||||
for (FloatImageBuffer &buffer : cache_) {
|
||||
buffer.is_used = false;
|
||||
}
|
||||
}
|
||||
|
||||
void mark_used(const ImBuf *image_buffer)
|
||||
{
|
||||
for (FloatImageBuffer &item : cache_) {
|
||||
if (item.source_buffer == image_buffer) {
|
||||
item.is_used = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void remove_unused_buffers()
|
||||
{
|
||||
for (int64_t i = cache_.size() - 1; i >= 0; i--) {
|
||||
if (!cache_[i].is_used) {
|
||||
cache_.remove_and_reorder(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
cache_.clear();
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace blender::image_engine
|
||||
@@ -0,0 +1,69 @@
|
||||
/* SPDX-FileCopyrightText: 2026 Blender Foundation.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup draw_engine
|
||||
*/
|
||||
|
||||
#include "image_drawing_mode_image_space.hh"
|
||||
#include "image_instance.hh"
|
||||
#include "image_shader.hh"
|
||||
|
||||
namespace blender::image_engine {
|
||||
|
||||
ImageSpaceDrawingMode::ImageSpaceDrawingMode(Instance &instance,
|
||||
gpu::Texture *texture,
|
||||
gpu::Texture *tile_mapping_texture)
|
||||
: instance_(instance), texture_(texture), tile_mapping_texture_(tile_mapping_texture)
|
||||
{
|
||||
GPU_texture_ref(texture_);
|
||||
if (tile_mapping_texture_) {
|
||||
GPU_texture_ref(tile_mapping_texture_);
|
||||
}
|
||||
}
|
||||
|
||||
ImageSpaceDrawingMode::~ImageSpaceDrawingMode()
|
||||
{
|
||||
GPU_texture_free(texture_);
|
||||
if (tile_mapping_texture_) {
|
||||
GPU_texture_free(tile_mapping_texture_);
|
||||
}
|
||||
}
|
||||
|
||||
void ImageSpaceDrawingMode::begin_sync() const {}
|
||||
|
||||
void ImageSpaceDrawingMode::image_sync(blender::Image * /*image*/, ImageUser * /*iuser*/) const {}
|
||||
|
||||
void ImageSpaceDrawingMode::draw_viewport() const
|
||||
{
|
||||
PassSimple &pass = instance_.state.image_ps;
|
||||
pass.init();
|
||||
pass.state_set(DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_ALWAYS);
|
||||
pass.shader_set(tile_mapping_texture_ ? ShaderModule::module_get().image_tiled.get() :
|
||||
ShaderModule::module_get().image.get());
|
||||
pass.push_constant("image_matrix", float4x4(instance_.state.ss_to_texture));
|
||||
pass.push_constant("far_near_distances", instance_.state.sh_params.far_near);
|
||||
pass.push_constant("shuffle", instance_.state.sh_params.shuffle);
|
||||
pass.push_constant("draw_flags", int32_t(instance_.state.sh_params.flags));
|
||||
pass.push_constant("is_image_premultiplied", instance_.state.sh_params.use_premul_alpha);
|
||||
|
||||
/* The shader will discard fragments that are outside of the image if repeating is disabled, so
|
||||
* we just always have repeat mode enabled. */
|
||||
const GPUSamplerState sampler = {.filtering = GPU_SAMPLER_FILTERING_DEFAULT,
|
||||
.extend_x = GPU_SAMPLER_EXTEND_MODE_REPEAT,
|
||||
.extend_yz = GPU_SAMPLER_EXTEND_MODE_REPEAT};
|
||||
|
||||
if (tile_mapping_texture_) {
|
||||
pass.bind_texture("image_tile_array", texture_, sampler);
|
||||
pass.bind_texture("image_tile_data", tile_mapping_texture_, sampler);
|
||||
}
|
||||
else {
|
||||
pass.push_constant("is_repeated", instance_.state.flags.do_tile_drawing);
|
||||
pass.bind_texture("image_tx", texture_, sampler);
|
||||
}
|
||||
pass.draw_procedural(GPU_PRIM_TRIS, 1, 3);
|
||||
instance_.manager->submit(instance_.state.image_ps, instance_.state.view);
|
||||
}
|
||||
|
||||
}; // namespace blender::image_engine
|
||||
@@ -0,0 +1,38 @@
|
||||
/* SPDX-FileCopyrightText: 2026 Blender Foundation.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup draw_engine
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "image_private.hh"
|
||||
|
||||
namespace blender::image_engine {
|
||||
|
||||
class Instance;
|
||||
|
||||
/**
|
||||
* Drawing mode optimized for textures that fits within the GPU specifications.
|
||||
*
|
||||
* Each GPU has a max texture size. Textures larger than this size aren't able to be allocated on
|
||||
* the GPU. For large textures use #ScreenSpaceDrawingMode.
|
||||
*/
|
||||
class ImageSpaceDrawingMode : public AbstractDrawingMode {
|
||||
private:
|
||||
Instance &instance_;
|
||||
gpu::Texture *texture_;
|
||||
gpu::Texture *tile_mapping_texture_ = nullptr;
|
||||
|
||||
public:
|
||||
ImageSpaceDrawingMode(Instance &instance,
|
||||
gpu::Texture *texture,
|
||||
gpu::Texture *tile_mapping_texture = nullptr);
|
||||
~ImageSpaceDrawingMode() override;
|
||||
void begin_sync() const override;
|
||||
void image_sync(blender::Image *image, blender::ImageUser *iuser) const override;
|
||||
void draw_viewport() const override;
|
||||
};
|
||||
}; // namespace blender::image_engine
|
||||
@@ -0,0 +1,390 @@
|
||||
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "draw_view_data.hh"
|
||||
|
||||
#include "image_drawing_mode_screen_space.hh"
|
||||
#include "image_instance.hh"
|
||||
#include "image_shader.hh"
|
||||
|
||||
#include "BKE_image.hh"
|
||||
#include "BKE_image_partial_update.hh"
|
||||
|
||||
namespace blender::image_engine {
|
||||
|
||||
void ScreenSpaceDrawingMode::add_shgroups() const
|
||||
{
|
||||
PassSimple &pass = instance_.state.image_ps;
|
||||
gpu::Shader *shader = ShaderModule::module_get().color.get();
|
||||
const ShaderParameters &sh_params = instance_.state.sh_params;
|
||||
DefaultTextureList *dtxl = DRW_context_get()->viewport_texture_list_get();
|
||||
|
||||
pass.shader_set(shader);
|
||||
pass.push_constant("far_near_distances", sh_params.far_near);
|
||||
pass.push_constant("shuffle", sh_params.shuffle);
|
||||
pass.push_constant("draw_flags", int32_t(sh_params.flags));
|
||||
pass.push_constant("is_image_premultiplied", sh_params.use_premul_alpha);
|
||||
pass.bind_texture("depth_tx", dtxl->depth);
|
||||
|
||||
float4x4 image_mat = float4x4::identity();
|
||||
ResourceHandleRange handle = instance_.manager->resource_handle(image_mat);
|
||||
for (const TextureInfo &info : instance_.state.texture_infos) {
|
||||
PassSimple::Sub &sub = pass.sub("Texture");
|
||||
sub.push_constant("offset", info.offset());
|
||||
sub.bind_texture("image_tx", info.texture);
|
||||
sub.draw(info.batch, handle);
|
||||
}
|
||||
}
|
||||
|
||||
void ScreenSpaceDrawingMode::add_depth_shgroups(blender::Image *image, ImageUser *image_user) const
|
||||
{
|
||||
PassSimple &pass = instance_.state.depth_ps;
|
||||
gpu::Shader *shader = ShaderModule::module_get().depth.get();
|
||||
pass.shader_set(shader);
|
||||
|
||||
float4x4 image_mat = float4x4::identity();
|
||||
ResourceHandleRange handle = instance_.manager->resource_handle(image_mat);
|
||||
|
||||
ImageUser tile_user = {nullptr};
|
||||
if (image_user) {
|
||||
tile_user = *image_user;
|
||||
}
|
||||
|
||||
for (const TextureInfo &info : instance_.state.texture_infos) {
|
||||
for (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);
|
||||
PassSimple::Sub &sub = pass.sub("Tile");
|
||||
float4 min_max_uv(tile_x, tile_y, tile_x + 1, tile_y + 1);
|
||||
sub.push_constant("min_max_uv", min_max_uv);
|
||||
sub.draw(info.batch, handle);
|
||||
}
|
||||
BKE_image_release_ibuf(image, tile_buffer, lock);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ScreenSpaceDrawingMode::update_textures(blender::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_data() != nullptr);
|
||||
BLI_assert(float_buffer->byte_data() == nullptr);
|
||||
BLI_assert(src->float_data() == nullptr);
|
||||
BLI_assert(src->byte_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_byte_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;
|
||||
}
|
||||
gpu::Texture *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)));
|
||||
gpu_texture_region_to_update.xmax = min_ii(gpu_texture_region_to_update.xmax,
|
||||
info.clipping_bounds.xmax);
|
||||
gpu_texture_region_to_update.ymax = min_ii(gpu_texture_region_to_update.ymax,
|
||||
info.clipping_bounds.ymax);
|
||||
|
||||
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, ImBufFlags::FloatData);
|
||||
|
||||
int offset = 0;
|
||||
float *float_data = extracted_buffer.float_data_for_write();
|
||||
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, &float_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,
|
||||
float_data,
|
||||
gpu_texture_region_to_update.xmin,
|
||||
gpu_texture_region_to_update.ymin,
|
||||
0,
|
||||
extracted_buffer.x,
|
||||
extracted_buffer.y,
|
||||
0);
|
||||
IMB_free_all_data(&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, ImBufFlags::FloatData);
|
||||
ImageUser tile_user = {nullptr};
|
||||
if (image_user) {
|
||||
tile_user = *image_user;
|
||||
}
|
||||
|
||||
void *lock;
|
||||
|
||||
blender::Image *image = instance_.state.image;
|
||||
for (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_data());
|
||||
IMB_free_all_data(&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. */
|
||||
float3x3 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_m3_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,
|
||||
crop_rect_ptr);
|
||||
}
|
||||
|
||||
void ScreenSpaceDrawingMode::begin_sync() const
|
||||
{
|
||||
{
|
||||
DefaultTextureList *dtxl = DRW_context_get()->viewport_texture_list_get();
|
||||
instance_.state.depth_fb.ensure(GPU_ATTACHMENT_TEXTURE(dtxl->depth));
|
||||
instance_.state.color_fb.ensure(GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(dtxl->color));
|
||||
}
|
||||
{
|
||||
PassSimple &pass = instance_.state.image_ps;
|
||||
pass.init();
|
||||
pass.state_set(DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_ALWAYS | DRW_STATE_BLEND_ALPHA_PREMUL);
|
||||
}
|
||||
{
|
||||
PassSimple &pass = instance_.state.depth_ps;
|
||||
pass.init();
|
||||
pass.state_set(DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS_EQUAL);
|
||||
}
|
||||
}
|
||||
|
||||
void ScreenSpaceDrawingMode::image_sync(blender::Image *image, ImageUser *iuser) const
|
||||
{
|
||||
State &state = instance_.state;
|
||||
|
||||
state.partial_update.ensure_image(image);
|
||||
state.clear_need_full_update_flag();
|
||||
|
||||
/* 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_viewport() const
|
||||
{
|
||||
float clear_depth = instance_.state.flags.do_tile_drawing ? 0.75 : 1.0f;
|
||||
GPU_framebuffer_bind(instance_.state.depth_fb);
|
||||
instance_.state.depth_fb.clear_depth(clear_depth);
|
||||
instance_.manager->submit(instance_.state.depth_ps, instance_.state.view);
|
||||
|
||||
GPU_framebuffer_bind(instance_.state.color_fb);
|
||||
GPU_framebuffer_clear_color(instance_.state.color_fb, double4(0.0));
|
||||
instance_.manager->submit(instance_.state.image_ps, instance_.state.view);
|
||||
}
|
||||
|
||||
} // namespace blender::image_engine
|
||||
@@ -0,0 +1,316 @@
|
||||
/* SPDX-FileCopyrightText: 2021 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup draw_engine
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "IMB_imbuf_types.hh"
|
||||
#include "IMB_interp.hh"
|
||||
|
||||
#include "BLI_math_matrix_types.hh"
|
||||
#include "BLI_math_vector_types.hh"
|
||||
|
||||
#include "image_batches.hh"
|
||||
#include "image_private.hh"
|
||||
|
||||
#include "DNA_windowmanager_types.h"
|
||||
|
||||
namespace blender::image_engine {
|
||||
class Instance;
|
||||
|
||||
constexpr float EPSILON_UV_BOUNDS = 0.00001f;
|
||||
|
||||
class BaseTextureMethod {
|
||||
protected:
|
||||
State *instance_data;
|
||||
|
||||
protected:
|
||||
BaseTextureMethod(State *instance_data) : instance_data(instance_data) {}
|
||||
|
||||
public:
|
||||
/**
|
||||
* \brief Ensure enough texture infos are allocated in `instance_data`.
|
||||
*/
|
||||
virtual void ensure_texture_infos() = 0;
|
||||
|
||||
/**
|
||||
* \brief Update the uv and region bounds of all texture_infos of instance_data.
|
||||
*/
|
||||
virtual void update_bounds(const ARegion *region) = 0;
|
||||
|
||||
virtual void ensure_gpu_textures_allocation() = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Uses a single texture that covers the area. Every zoom/pan change requires a full
|
||||
* update of the texture.
|
||||
*/
|
||||
class OneTexture : public BaseTextureMethod {
|
||||
public:
|
||||
OneTexture(State *instance_data) : BaseTextureMethod(instance_data) {}
|
||||
void ensure_texture_infos() override
|
||||
{
|
||||
instance_data->texture_infos.resize(1);
|
||||
}
|
||||
|
||||
void update_bounds(const ARegion *region) override
|
||||
{
|
||||
float3x3 mat = instance_data->ss_to_texture;
|
||||
float2 region_uv_min = math::transform_point(mat, float2(0.0f, 0.0f));
|
||||
float2 region_uv_max = math::transform_point(mat, float2(1.0f, 1.0f));
|
||||
|
||||
TextureInfo &texture_info = instance_data->texture_infos[0];
|
||||
texture_info.tile_id = int2(0);
|
||||
texture_info.need_full_update = false;
|
||||
rctf new_clipping_uv_bounds;
|
||||
BLI_rctf_init(&new_clipping_uv_bounds,
|
||||
region_uv_min.x,
|
||||
region_uv_max.x,
|
||||
region_uv_min.y,
|
||||
region_uv_max.y);
|
||||
|
||||
if (memcmp(&new_clipping_uv_bounds, &texture_info.clipping_uv_bounds, sizeof(rctf))) {
|
||||
texture_info.clipping_uv_bounds = new_clipping_uv_bounds;
|
||||
texture_info.need_full_update = true;
|
||||
}
|
||||
|
||||
rcti new_clipping_bounds;
|
||||
BLI_rcti_init(&new_clipping_bounds, 0, region->winx, 0, region->winy);
|
||||
if (memcmp(&new_clipping_bounds, &texture_info.clipping_bounds, sizeof(rcti))) {
|
||||
texture_info.clipping_bounds = new_clipping_bounds;
|
||||
texture_info.need_full_update = true;
|
||||
}
|
||||
}
|
||||
|
||||
void ensure_gpu_textures_allocation() override
|
||||
{
|
||||
TextureInfo &texture_info = instance_data->texture_infos[0];
|
||||
int2 texture_size = int2(BLI_rcti_size_x(&texture_info.clipping_bounds),
|
||||
BLI_rcti_size_y(&texture_info.clipping_bounds));
|
||||
texture_info.ensure_gpu_texture(texture_size);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Screen space method using a multiple textures covering the region.
|
||||
*
|
||||
* This method improves panning speed, but has some drawing artifacts and
|
||||
* therefore isn't selected.
|
||||
*/
|
||||
template<size_t Divisions> class ScreenTileTextures : public BaseTextureMethod {
|
||||
public:
|
||||
static const size_t TexturesPerDimension = Divisions + 1;
|
||||
static const size_t TexturesRequired = TexturesPerDimension * TexturesPerDimension;
|
||||
static const size_t VerticesPerDimension = TexturesPerDimension + 1;
|
||||
|
||||
private:
|
||||
/**
|
||||
* \brief Helper struct to pair a texture info and a region in uv space of the area.
|
||||
*/
|
||||
struct TextureInfoBounds {
|
||||
TextureInfo *info = nullptr;
|
||||
rctf uv_bounds;
|
||||
/* Offset of this tile to be drawn on the screen (number of tiles from bottom left corner). */
|
||||
int2 tile_id;
|
||||
};
|
||||
|
||||
public:
|
||||
ScreenTileTextures(State *instance_data) : BaseTextureMethod(instance_data) {}
|
||||
|
||||
/**
|
||||
* \brief Ensure enough texture infos are allocated in `instance_data`.
|
||||
*/
|
||||
void ensure_texture_infos() override
|
||||
{
|
||||
instance_data->texture_infos.resize(TexturesRequired);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Update the uv and region bounds of all texture_infos of instance_data.
|
||||
*/
|
||||
void update_bounds(const ARegion *region) override
|
||||
{
|
||||
/* determine uv_area of the region. */
|
||||
Vector<TextureInfo *> unassigned_textures;
|
||||
float3x3 mat = instance_data->ss_to_texture;
|
||||
float2 region_uv_min = math::transform_point(mat, float2(0.0f, 0.0f));
|
||||
float2 region_uv_max = math::transform_point(mat, float2(1.0f, 1.0f));
|
||||
float2 region_uv_span = region_uv_max - region_uv_min;
|
||||
|
||||
/* Calculate uv coordinates of each vert in the grid of textures. */
|
||||
|
||||
/* Construct the uv bounds of the 4 textures that are needed to fill the region. */
|
||||
Vector<TextureInfoBounds> info_bounds = create_uv_bounds(region_uv_span, region_uv_min);
|
||||
assign_texture_infos_by_uv_bounds(info_bounds, unassigned_textures);
|
||||
assign_unused_texture_infos(info_bounds, unassigned_textures);
|
||||
|
||||
/* Calculate the region bounds from the uv bounds. */
|
||||
rctf region_uv_bounds;
|
||||
BLI_rctf_init(
|
||||
®ion_uv_bounds, region_uv_min.x, region_uv_max.x, region_uv_min.y, region_uv_max.y);
|
||||
update_region_bounds_from_uv_bounds(region_uv_bounds, int2(region->winx, region->winy));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the texture size of a single texture for the current settings.
|
||||
*/
|
||||
int2 gpu_texture_size() const
|
||||
{
|
||||
float2 viewport_size = DRW_context_get()->viewport_size_get();
|
||||
int2 texture_size(ceil(viewport_size.x / Divisions), ceil(viewport_size.y / Divisions));
|
||||
return texture_size;
|
||||
}
|
||||
|
||||
void ensure_gpu_textures_allocation() override
|
||||
{
|
||||
int2 texture_size = gpu_texture_size();
|
||||
for (TextureInfo &info : instance_data->texture_infos) {
|
||||
info.ensure_gpu_texture(texture_size);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
Vector<TextureInfoBounds> create_uv_bounds(float2 region_uv_span, float2 region_uv_min)
|
||||
{
|
||||
float2 uv_coords[VerticesPerDimension][VerticesPerDimension];
|
||||
float2 region_tile_uv_span = region_uv_span / float2(float(Divisions));
|
||||
float2 onscreen_multiple = (math::floor(region_uv_min / region_tile_uv_span) + float2(1.0f)) *
|
||||
region_tile_uv_span;
|
||||
for (int y = 0; y < VerticesPerDimension; y++) {
|
||||
for (int x = 0; x < VerticesPerDimension; x++) {
|
||||
uv_coords[x][y] = region_tile_uv_span * float2(float(x - 1), float(y - 1)) +
|
||||
onscreen_multiple;
|
||||
}
|
||||
}
|
||||
|
||||
Vector<TextureInfoBounds> info_bounds;
|
||||
for (int x = 0; x < TexturesPerDimension; x++) {
|
||||
for (int y = 0; y < TexturesPerDimension; y++) {
|
||||
TextureInfoBounds texture_info_bounds;
|
||||
texture_info_bounds.tile_id = int2(x, y);
|
||||
BLI_rctf_init(&texture_info_bounds.uv_bounds,
|
||||
uv_coords[x][y].x,
|
||||
uv_coords[x + 1][y + 1].x,
|
||||
uv_coords[x][y].y,
|
||||
uv_coords[x + 1][y + 1].y);
|
||||
info_bounds.append(texture_info_bounds);
|
||||
}
|
||||
}
|
||||
return info_bounds;
|
||||
}
|
||||
|
||||
void assign_texture_infos_by_uv_bounds(Vector<TextureInfoBounds> &info_bounds,
|
||||
Vector<TextureInfo *> &r_unassigned_textures)
|
||||
{
|
||||
for (TextureInfo &info : instance_data->texture_infos) {
|
||||
bool assigned = false;
|
||||
for (TextureInfoBounds &info_bound : info_bounds) {
|
||||
if (info_bound.info == nullptr &&
|
||||
BLI_rctf_compare(&info_bound.uv_bounds, &info.clipping_uv_bounds, 0.001))
|
||||
{
|
||||
info_bound.info = &info;
|
||||
info.tile_id = info_bound.tile_id;
|
||||
assigned = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!assigned) {
|
||||
r_unassigned_textures.append(&info);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void assign_unused_texture_infos(Vector<TextureInfoBounds> &info_bounds,
|
||||
Vector<TextureInfo *> &unassigned_textures)
|
||||
{
|
||||
for (TextureInfoBounds &info_bound : info_bounds) {
|
||||
if (info_bound.info == nullptr) {
|
||||
info_bound.info = unassigned_textures.pop_last();
|
||||
info_bound.info->tile_id = info_bound.tile_id;
|
||||
info_bound.info->need_full_update = true;
|
||||
info_bound.info->clipping_uv_bounds = info_bound.uv_bounds;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void update_region_bounds_from_uv_bounds(const rctf ®ion_uv_bounds, const int2 region_size)
|
||||
{
|
||||
rctf region_bounds;
|
||||
BLI_rctf_init(®ion_bounds, 0.0, region_size.x, 0.0, region_size.y);
|
||||
float4x4 uv_to_screen;
|
||||
BLI_rctf_transform_calc_m4_pivot_min(®ion_uv_bounds, ®ion_bounds, uv_to_screen.ptr());
|
||||
int2 tile_origin(0);
|
||||
for (const TextureInfo &info : instance_data->texture_infos) {
|
||||
if (info.tile_id == int2(0)) {
|
||||
tile_origin = int2(math::transform_point(
|
||||
uv_to_screen,
|
||||
float3(info.clipping_uv_bounds.xmin, info.clipping_uv_bounds.ymin, 0.0)));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const int2 texture_size = gpu_texture_size();
|
||||
for (TextureInfo &info : instance_data->texture_infos) {
|
||||
int2 bottom_left = tile_origin + texture_size * info.tile_id;
|
||||
int2 top_right = bottom_left + texture_size;
|
||||
BLI_rcti_init(&info.clipping_bounds, bottom_left.x, top_right.x, bottom_left.y, top_right.y);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
using namespace blender::bke::image::partial_update;
|
||||
using namespace blender::bke::image;
|
||||
|
||||
class ScreenSpaceDrawingMode : public AbstractDrawingMode {
|
||||
private:
|
||||
Instance &instance_;
|
||||
|
||||
public:
|
||||
ScreenSpaceDrawingMode(Instance &instance) : instance_(instance) {}
|
||||
|
||||
private:
|
||||
void add_shgroups() const;
|
||||
|
||||
/**
|
||||
* \brief add depth drawing calls.
|
||||
*
|
||||
* The depth is used to identify if the tile exist or transparent.
|
||||
*/
|
||||
void add_depth_shgroups(blender::Image *image, blender::ImageUser *image_user) const;
|
||||
|
||||
/**
|
||||
* \brief Update GPUTextures for drawing the image.
|
||||
*
|
||||
* GPUTextures that are marked dirty are rebuild. GPUTextures that aren't marked dirty are
|
||||
* updated with changed region of the image.
|
||||
*/
|
||||
void update_textures(blender::Image *image, blender::ImageUser *image_user) const;
|
||||
|
||||
/**
|
||||
* Update the float buffer in the region given by the partial update checker.
|
||||
*/
|
||||
void do_partial_update_float_buffer(
|
||||
ImBuf *float_buffer, PartialUpdateChecker<ImageTileData>::CollectResult &iterator) const;
|
||||
void do_partial_update(PartialUpdateChecker<ImageTileData>::CollectResult &iterator) const;
|
||||
void do_full_update_for_dirty_textures(const blender::ImageUser *image_user) const;
|
||||
void do_full_update_gpu_texture(TextureInfo &info, const blender::ImageUser *image_user) const;
|
||||
/**
|
||||
* texture_buffer is the image buffer belonging to the texture_info.
|
||||
* tile_buffer is the image buffer of the tile.
|
||||
*/
|
||||
void do_full_update_texture_slot(const TextureInfo &texture_info,
|
||||
ImBuf &texture_buffer,
|
||||
ImBuf &tile_buffer,
|
||||
const ImageTileWrapper &image_tile) const;
|
||||
|
||||
public:
|
||||
void begin_sync() const override;
|
||||
void image_sync(blender::Image *image, blender::ImageUser *iuser) const override;
|
||||
void draw_viewport() const override;
|
||||
};
|
||||
|
||||
} // namespace blender::image_engine
|
||||
@@ -0,0 +1,27 @@
|
||||
/* SPDX-FileCopyrightText: 2020 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup draw_engine
|
||||
*
|
||||
* Draw engine to draw the Image/UV editor
|
||||
*/
|
||||
|
||||
#include "image_engine.h"
|
||||
#include "image_instance.hh"
|
||||
#include "image_shader.hh"
|
||||
|
||||
namespace blender::image_engine {
|
||||
|
||||
DrawEngine *Engine::create_instance()
|
||||
{
|
||||
return new Instance();
|
||||
}
|
||||
|
||||
void Engine::free_static()
|
||||
{
|
||||
ShaderModule::module_free();
|
||||
}
|
||||
|
||||
} // namespace blender::image_engine
|
||||
@@ -0,0 +1,21 @@
|
||||
/* SPDX-FileCopyrightText: 2020 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup draw_engine
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "DRW_render.hh"
|
||||
|
||||
namespace blender::image_engine {
|
||||
|
||||
struct Engine : public DrawEngine::Pointer {
|
||||
DrawEngine *create_instance() final;
|
||||
|
||||
static void free_static();
|
||||
};
|
||||
|
||||
} // namespace blender::image_engine
|
||||
@@ -0,0 +1,273 @@
|
||||
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <DRW_render.hh>
|
||||
|
||||
#include "BKE_context.hh"
|
||||
|
||||
#include "GPU_capabilities.hh"
|
||||
|
||||
#include "DRW_engine.hh"
|
||||
#include "draw_view_data.hh"
|
||||
|
||||
#include "image_drawing_mode_image_space.hh"
|
||||
#include "image_drawing_mode_screen_space.hh"
|
||||
#include "image_private.hh"
|
||||
#include "image_space.hh"
|
||||
#include "image_space_image.hh"
|
||||
#include "image_space_node.hh"
|
||||
|
||||
#include "BLI_math_matrix.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 : public DrawEngine {
|
||||
private:
|
||||
std::unique_ptr<AbstractSpaceAccessor> space_;
|
||||
std::unique_ptr<AbstractDrawingMode> drawing_mode_;
|
||||
Main *main_;
|
||||
|
||||
public:
|
||||
const ARegion *region;
|
||||
State state;
|
||||
Manager *manager = nullptr;
|
||||
|
||||
public:
|
||||
StringRefNull name_get() final
|
||||
{
|
||||
return "UV/Image";
|
||||
}
|
||||
|
||||
void init() final
|
||||
{
|
||||
const DRWContext *ctx_state = DRW_context_get();
|
||||
main_ = CTX_data_main(ctx_state->evil_C);
|
||||
region = ctx_state->region;
|
||||
space_ = space_accessor_from_space(ctx_state->space_data);
|
||||
manager = DRW_manager_get();
|
||||
}
|
||||
|
||||
/* Constructs either a screen space or an image space drawing mode depending on if the image can
|
||||
* fit in a GPU texture. So we just need to retrieve the image buffer and check if its size is
|
||||
* safe for GPU use. */
|
||||
std::unique_ptr<AbstractDrawingMode> get_drawing_mode()
|
||||
{
|
||||
if (this->state.image->source != IMA_SRC_TILED) {
|
||||
void *lock;
|
||||
ImBuf *buffer = BKE_image_acquire_ibuf_gpu(
|
||||
this->state.image, space_->get_image_user(), &lock);
|
||||
BLI_SCOPED_DEFER([&]() { BKE_image_release_ibuf(this->state.image, buffer, lock); });
|
||||
|
||||
/* The image buffer already has a GPU texture, so use image space drawing. */
|
||||
if (buffer && buffer->gpu.texture) {
|
||||
return std::make_unique<ImageSpaceDrawingMode>(*this, buffer->gpu.texture);
|
||||
}
|
||||
|
||||
/* Buffer does not exist or image will not fit in a GPU texture, use screen space drawing. */
|
||||
if (!buffer || (!buffer->float_data() && !buffer->byte_data()) ||
|
||||
!GPU_is_safe_texture_size(buffer->x, buffer->y))
|
||||
{
|
||||
return std::make_unique<ScreenSpaceDrawingMode>(*this);
|
||||
}
|
||||
|
||||
/* GPU drawing will limit image resolution due to the GPU back-end having a lower maximum
|
||||
* texture size or a resolution limit in the preferences, so use screen space drawing to get
|
||||
* the full resolution. */
|
||||
if (GPU_texture_size_with_limit(buffer->x) != buffer->x ||
|
||||
GPU_texture_size_with_limit(buffer->y) != buffer->y)
|
||||
{
|
||||
return std::make_unique<ScreenSpaceDrawingMode>(*this);
|
||||
}
|
||||
|
||||
/* Image can fit in a GPU texture, use image space drawing. */
|
||||
BKE_image_ensure_gpu_texture(this->state.image, space_->get_image_user());
|
||||
gpu::Texture *texture = BKE_image_get_gpu_viewer_texture(
|
||||
this->state.image, space_->get_image_user(), buffer);
|
||||
return std::make_unique<ImageSpaceDrawingMode>(*this, texture);
|
||||
}
|
||||
|
||||
for (ImageTile &tile : this->state.image->tiles) {
|
||||
ImageTileWrapper image_tile(&tile);
|
||||
ImageUser tile_user = space_->get_image_user() ? *space_->get_image_user() :
|
||||
ImageUser{.scene = nullptr};
|
||||
tile_user.tile = image_tile.get_tile_number();
|
||||
ImBuf *buffer = BKE_image_acquire_ibuf_gpu(this->state.image, &tile_user, nullptr);
|
||||
BLI_SCOPED_DEFER([&]() { BKE_image_release_ibuf(this->state.image, buffer, nullptr); });
|
||||
if (!buffer) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Image will not fit in a GPU texture, use screen space drawing. */
|
||||
if (!GPU_is_safe_texture_size(buffer->x, buffer->y)) {
|
||||
return std::make_unique<ScreenSpaceDrawingMode>(*this);
|
||||
}
|
||||
|
||||
/* GPU drawing will limit image resolution due to the GPU back-end having a lower maximum
|
||||
* texture size or a resolution limit in the preferences, so use screen space drawing to get
|
||||
* the full resolution. */
|
||||
if (GPU_texture_size_with_limit(buffer->x) != buffer->x ||
|
||||
GPU_texture_size_with_limit(buffer->y) != buffer->y)
|
||||
{
|
||||
return std::make_unique<ScreenSpaceDrawingMode>(*this);
|
||||
}
|
||||
}
|
||||
|
||||
/* Image can fit in a GPU texture, use image space drawing. */
|
||||
BKE_image_ensure_gpu_texture(this->state.image, space_->get_image_user());
|
||||
ImageGPUTextures gpu_tiles_textures = BKE_image_get_gpu_material_texture(
|
||||
this->state.image, space_->get_image_user(), true);
|
||||
return std::make_unique<ImageSpaceDrawingMode>(
|
||||
*this, *gpu_tiles_textures.texture, *gpu_tiles_textures.tile_mapping);
|
||||
}
|
||||
|
||||
void begin_sync() final
|
||||
{
|
||||
/* Setup full screen view matrix. */
|
||||
float4x4 viewmat = math::projection::orthographic(
|
||||
0.0f, float(region->winx), 0.0f, float(region->winy), 0.0f, 1.0f);
|
||||
float4x4 winmat = float4x4::identity();
|
||||
state.view.sync(viewmat, winmat);
|
||||
state.flags.do_tile_drawing = false;
|
||||
|
||||
this->image_sync();
|
||||
drawing_mode_.reset();
|
||||
this->state.float_buffers.reset_usage_flags();
|
||||
if (this->state.image) {
|
||||
this->drawing_mode_ = this->get_drawing_mode();
|
||||
drawing_mode_->begin_sync();
|
||||
drawing_mode_->image_sync(state.image, space_->get_image_user());
|
||||
}
|
||||
}
|
||||
|
||||
/* Computes a transformation matrix from the normalized screen space coordinates with half pixel
|
||||
* offsets into the image sampler space. */
|
||||
float3x3 compute_screen_space_to_sampler_space_transformation(const float2 output_size,
|
||||
const float2 image_offset,
|
||||
const float2 image_size,
|
||||
const float2 pan_offset,
|
||||
const float zoom,
|
||||
const float aspect_ratio) const
|
||||
{
|
||||
/* Transforms output normalized screen coordinates with half pixel offsets into integer data
|
||||
* coordinates. */
|
||||
const float3x3 output_screen_uv_to_output_texel = math::from_scale<float3x3, 2>(output_size);
|
||||
const float3x3 output_texel_to_output_data = math::from_location<float3x3>(float2(-0.5f));
|
||||
|
||||
/* Transforms output data coordinates into the centered virtual space. */
|
||||
const float2 output_center = float2(output_size) / 2.0f;
|
||||
const float2 output_translation = -output_center;
|
||||
const float3x3 output_data_to_virtual = math::from_location<float3x3>(output_translation);
|
||||
|
||||
/* Transform the image in pixel space based on the pan offset, zoom, and aspect ratio. */
|
||||
const float3x3 image_transformation = math::from_loc_scale<float3x3>(
|
||||
pan_offset, float2(zoom) * float2(1.0f, aspect_ratio));
|
||||
|
||||
/* Transforms image data coordinates into the centered virtual space with the image offset. We
|
||||
* also bias translations to avoids the round-to-even behavior of some GPUs at pixel
|
||||
* boundaries. */
|
||||
const float2 image_center = image_size / 2.0f;
|
||||
const float2 corrective_translation = float2(std::numeric_limits<float>::epsilon() * 10e3f);
|
||||
const float2 image_translation = image_offset - image_center + corrective_translation;
|
||||
const float3x3 image_data_to_virtual = math::translate(image_transformation,
|
||||
image_translation);
|
||||
|
||||
/* Transforms the output data space to the image data space. */
|
||||
const float3x3 virtual_to_image_data = math::invert(image_data_to_virtual);
|
||||
const float3x3 output_data_to_image_data = virtual_to_image_data * output_data_to_virtual;
|
||||
|
||||
/* Transform from image data coordinates to image sampler normalized coordinates with half
|
||||
* pixel offsets. */
|
||||
const float3x3 image_data_to_image_texel = math::from_location<float3x3>(float2(0.5f));
|
||||
const float3x3 image_texel_to_image_sampler = math::from_scale<float3x3, 2>(1.0f / image_size);
|
||||
|
||||
const float3x3 output_screen_uv_to_image_sampler = image_texel_to_image_sampler *
|
||||
image_data_to_image_texel *
|
||||
output_data_to_image_data *
|
||||
output_texel_to_output_data *
|
||||
output_screen_uv_to_output_texel;
|
||||
return output_screen_uv_to_image_sampler;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
const float2 image_size = float2(image_buffer ? image_buffer->x : 1024.0f,
|
||||
image_buffer ? image_buffer->y : 1024.0f);
|
||||
float2 offset = float2(0.0f);
|
||||
if (image_buffer && space_->use_display_window() &&
|
||||
flag_is_set(image_buffer->flags, ImBufFlags::HasDisplayWindow))
|
||||
{
|
||||
offset = float2(image_buffer->display_offset);
|
||||
}
|
||||
|
||||
state.ss_to_texture = this->compute_screen_space_to_sampler_space_transformation(
|
||||
float2(region->winx, region->winy),
|
||||
offset,
|
||||
image_size,
|
||||
space_->get_pan_offset(),
|
||||
space_->get_zoom(),
|
||||
space_->get_aspect_ratio());
|
||||
|
||||
const Scene *scene = DRW_context_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);
|
||||
}
|
||||
}
|
||||
|
||||
void object_sync(ObjectRef & /*obref*/, Manager & /*manager*/) final {}
|
||||
|
||||
void end_sync() final {}
|
||||
|
||||
void draw(Manager & /*manager*/) final
|
||||
{
|
||||
DRW_submission_start();
|
||||
if (drawing_mode_) {
|
||||
drawing_mode_->draw_viewport();
|
||||
}
|
||||
else {
|
||||
GPU_framebuffer_clear_color_depth(
|
||||
DRW_context_get()->viewport_framebuffer_list_get()->default_fb, double4(0.0), 1.0f);
|
||||
}
|
||||
this->state.float_buffers.remove_unused_buffers();
|
||||
state.image = nullptr;
|
||||
drawing_mode_.reset();
|
||||
DRW_submission_end();
|
||||
}
|
||||
};
|
||||
} // namespace blender::image_engine
|
||||
@@ -0,0 +1,68 @@
|
||||
/* SPDX-FileCopyrightText: 2021 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup draw_engine
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BKE_image.hh"
|
||||
#include "BKE_image_partial_update.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
struct PartialImageUpdater {
|
||||
PartialUpdateUser *user;
|
||||
const Image *image;
|
||||
|
||||
/**
|
||||
* \brief Ensure that there is a partial update user for the given image.
|
||||
*/
|
||||
void ensure_image(const Image *new_image)
|
||||
{
|
||||
if (!is_valid(new_image)) {
|
||||
free();
|
||||
create(new_image);
|
||||
}
|
||||
}
|
||||
|
||||
virtual ~PartialImageUpdater()
|
||||
{
|
||||
free();
|
||||
}
|
||||
|
||||
private:
|
||||
/**
|
||||
* \brief check if the partial update user can still be used for the given image.
|
||||
*
|
||||
* When switching to a different image the partial update user should be recreated.
|
||||
*/
|
||||
bool is_valid(const Image *new_image) const
|
||||
{
|
||||
if (image != new_image) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return user != nullptr;
|
||||
}
|
||||
|
||||
void create(const Image *new_image)
|
||||
{
|
||||
BLI_assert(user == nullptr);
|
||||
user = BKE_image_partial_update_create(new_image);
|
||||
image = new_image;
|
||||
}
|
||||
|
||||
void free()
|
||||
{
|
||||
if (user != nullptr) {
|
||||
BKE_image_partial_update_free(user);
|
||||
user = nullptr;
|
||||
image = nullptr;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,45 @@
|
||||
/* SPDX-FileCopyrightText: 2020 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup draw_engine
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
|
||||
#include "BKE_image.hh"
|
||||
|
||||
#include "image_state.hh"
|
||||
#include "image_texture_info.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
/* Forward declarations */
|
||||
extern "C" {
|
||||
struct Image;
|
||||
}
|
||||
|
||||
/* *********** LISTS *********** */
|
||||
|
||||
namespace image_engine {
|
||||
|
||||
/**
|
||||
* Abstract class for a drawing mode of the image engine.
|
||||
*
|
||||
* The drawing mode decides how to draw the image on the screen. Each way how to draw would have
|
||||
* its own subclass.
|
||||
*/
|
||||
class AbstractDrawingMode {
|
||||
public:
|
||||
virtual ~AbstractDrawingMode() = default;
|
||||
virtual void begin_sync() const = 0;
|
||||
virtual void image_sync(blender::Image *image, blender::ImageUser *iuser) const = 0;
|
||||
virtual void draw_viewport() const = 0;
|
||||
};
|
||||
|
||||
} // namespace image_engine
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,29 @@
|
||||
/* SPDX-FileCopyrightText: 2020 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup draw_engine
|
||||
*/
|
||||
|
||||
#include "image_shader.hh"
|
||||
|
||||
namespace blender::image_engine {
|
||||
|
||||
ShaderModule *ShaderModule::g_shader_module = nullptr;
|
||||
|
||||
ShaderModule &ShaderModule::module_get()
|
||||
{
|
||||
if (g_shader_module == nullptr) {
|
||||
g_shader_module = new ShaderModule();
|
||||
}
|
||||
return *g_shader_module;
|
||||
}
|
||||
|
||||
void ShaderModule::module_free()
|
||||
{
|
||||
delete g_shader_module;
|
||||
g_shader_module = nullptr;
|
||||
}
|
||||
|
||||
} // namespace blender::image_engine
|
||||
@@ -0,0 +1,52 @@
|
||||
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup draw_engine
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "DRW_render.hh"
|
||||
|
||||
#include "GPU_shader.hh"
|
||||
|
||||
namespace blender::image_engine {
|
||||
|
||||
/**
|
||||
* Shader module. Shared between instances.
|
||||
*/
|
||||
class ShaderModule {
|
||||
private:
|
||||
struct ShaderDeleter {
|
||||
void operator()(gpu::Shader *shader)
|
||||
{
|
||||
GPU_SHADER_FREE_SAFE(shader);
|
||||
}
|
||||
};
|
||||
using ShaderPtr = std::unique_ptr<gpu::Shader, ShaderDeleter>;
|
||||
|
||||
/** Shared shader module across all engine instances. */
|
||||
static ShaderModule *g_shader_module;
|
||||
|
||||
public:
|
||||
/** Shaders */
|
||||
ShaderPtr depth = shader("image_engine_depth_shader");
|
||||
ShaderPtr color = shader("image_engine_color_shader");
|
||||
ShaderPtr image = shader("image_engine_image_shader");
|
||||
ShaderPtr image_tiled = shader("image_engine_image_tiled_shader");
|
||||
|
||||
/** Module */
|
||||
/** Only to be used by Instance constructor. */
|
||||
static ShaderModule &module_get();
|
||||
static void module_free();
|
||||
|
||||
private:
|
||||
ShaderPtr shader(const char *create_info_name)
|
||||
{
|
||||
return ShaderPtr(GPU_shader_create_from_info_name(create_info_name));
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace blender::image_engine
|
||||
@@ -0,0 +1,53 @@
|
||||
/* SPDX-FileCopyrightText: 2021 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup draw_engine
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "DNA_camera_types.h"
|
||||
#include "DNA_image_types.h"
|
||||
#include "DNA_scene_types.h"
|
||||
|
||||
#include "BLI_math_vector.h"
|
||||
|
||||
#include "IMB_imbuf_types.hh"
|
||||
|
||||
#include "BKE_image.hh"
|
||||
|
||||
#include "DRW_render.hh"
|
||||
|
||||
#include "image_shader_shared.hh"
|
||||
#include "image_space.hh"
|
||||
|
||||
namespace blender::image_engine {
|
||||
|
||||
struct ShaderParameters {
|
||||
eImageDrawFlags flags = IMAGE_DRAW_FLAG_DEFAULT;
|
||||
float4 shuffle;
|
||||
float2 far_near;
|
||||
bool use_premul_alpha = false;
|
||||
|
||||
void update(AbstractSpaceAccessor *space,
|
||||
const Scene *scene,
|
||||
blender::Image *image,
|
||||
ImBuf *image_buffer)
|
||||
{
|
||||
flags = IMAGE_DRAW_FLAG_DEFAULT;
|
||||
shuffle = float4(1.0f);
|
||||
far_near = float2(100.0f, 0.0f);
|
||||
|
||||
use_premul_alpha = BKE_image_has_gpu_texture_premultiplied_alpha(image, image_buffer);
|
||||
|
||||
if (scene->camera && scene->camera->type == OB_CAMERA) {
|
||||
const Camera &camera = DRW_object_get_data_for_drawing<const Camera>(*scene->camera);
|
||||
far_near = float2(camera.clip_end, camera.clip_start);
|
||||
}
|
||||
space->get_shader_parameters(*this, image_buffer);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace blender::image_engine
|
||||
@@ -0,0 +1,18 @@
|
||||
/* SPDX-FileCopyrightText: 2026 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "GPU_shader_shared_utils.hh"
|
||||
|
||||
enum [[host_shared]] eImageDrawFlags : uint32_t {
|
||||
IMAGE_DRAW_FLAG_DEFAULT = 0,
|
||||
IMAGE_DRAW_FLAG_SHOW_ALPHA = (1 << 0),
|
||||
IMAGE_DRAW_FLAG_APPLY_ALPHA = (1 << 1),
|
||||
IMAGE_DRAW_FLAG_SHUFFLING = (1 << 2),
|
||||
IMAGE_DRAW_FLAG_DEPTH = (1 << 3),
|
||||
};
|
||||
#ifndef GPU_SHADER
|
||||
ENUM_OPERATORS(eImageDrawFlags)
|
||||
#endif
|
||||
@@ -0,0 +1,93 @@
|
||||
/* SPDX-FileCopyrightText: 2021 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup draw_engine
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BLI_math_matrix_types.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
struct ARegion;
|
||||
struct ImBuf;
|
||||
struct Image;
|
||||
struct ImageUser;
|
||||
struct Main;
|
||||
|
||||
namespace image_engine {
|
||||
|
||||
struct ShaderParameters;
|
||||
|
||||
/**
|
||||
* Space accessor.
|
||||
*
|
||||
* Image engine is used to draw the images inside multiple spaces \see SpaceLink.
|
||||
* The #AbstractSpaceAccessor is an interface to communicate with a space.
|
||||
*/
|
||||
class AbstractSpaceAccessor {
|
||||
public:
|
||||
virtual ~AbstractSpaceAccessor() = default;
|
||||
|
||||
/**
|
||||
* Return the active image of the space.
|
||||
*
|
||||
* The returned image will be drawn in the space.
|
||||
*
|
||||
* The return value is optional.
|
||||
*/
|
||||
virtual blender::Image *get_image(Main *bmain) = 0;
|
||||
|
||||
/**
|
||||
* Return the #ImageUser of the space.
|
||||
*
|
||||
* The return value is optional.
|
||||
*/
|
||||
virtual blender::ImageUser *get_image_user() = 0;
|
||||
|
||||
/**
|
||||
* Acquire the image buffer of the image.
|
||||
*
|
||||
* \param image: Image to get the buffer from. Image is the same as returned from the #get_image
|
||||
* member.
|
||||
* \param lock: pointer to a lock object.
|
||||
* \return Image buffer of the given image.
|
||||
*/
|
||||
virtual ImBuf *acquire_image_buffer(blender::Image *image, void **lock) = 0;
|
||||
|
||||
/**
|
||||
* Release a previous locked image from #acquire_image_buffer.
|
||||
*/
|
||||
virtual void release_buffer(blender::Image *image, ImBuf *image_buffer, void *lock) = 0;
|
||||
|
||||
/**
|
||||
* Update the r_shader_parameters with space specific settings.
|
||||
*
|
||||
* Only update the #ShaderParameters.flags and #ShaderParameters.shuffle. Other parameters
|
||||
* are updated inside the image engine.
|
||||
*/
|
||||
virtual void get_shader_parameters(ShaderParameters &r_shader_parameters,
|
||||
ImBuf *image_buffer) = 0;
|
||||
|
||||
/** \brief Is (wrap) repeat option enabled in the space. */
|
||||
virtual bool use_tile_drawing() const = 0;
|
||||
|
||||
/** \brief Draw image with display window offsets. */
|
||||
virtual bool use_display_window() const = 0;
|
||||
|
||||
/** \brief Gets the zoom factor of the space. A factor of 2 is a zoom-in by two times. */
|
||||
virtual float get_zoom() const = 0;
|
||||
|
||||
/** \brief Gets the aspect ratio of the image. The ratio is for the vertical axis. */
|
||||
virtual float get_aspect_ratio() const = 0;
|
||||
|
||||
/** \brief Gets the pan offset of the space in image pixel space. */
|
||||
virtual float2 get_pan_offset() const = 0;
|
||||
};
|
||||
|
||||
} // namespace image_engine
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,119 @@
|
||||
/* SPDX-FileCopyrightText: 2021 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup draw_engine
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ED_image.hh"
|
||||
|
||||
#include "DNA_screen_types.h"
|
||||
|
||||
#include "image_private.hh"
|
||||
#include "image_shader_shared.hh"
|
||||
|
||||
namespace blender::image_engine {
|
||||
|
||||
class SpaceImageAccessor : public AbstractSpaceAccessor {
|
||||
SpaceImage *sima;
|
||||
|
||||
public:
|
||||
SpaceImageAccessor(SpaceImage *sima) : sima(sima) {}
|
||||
|
||||
blender::Image *get_image(Main * /*bmain*/) override
|
||||
{
|
||||
return ED_space_image(sima);
|
||||
}
|
||||
|
||||
ImageUser *get_image_user() override
|
||||
{
|
||||
return &sima->iuser;
|
||||
}
|
||||
|
||||
ImBuf *acquire_image_buffer(blender::Image * /*image*/, void **lock) override
|
||||
{
|
||||
return ED_space_image_acquire_buffer(sima, lock, 0, false);
|
||||
}
|
||||
|
||||
void release_buffer(blender::Image * /*image*/, ImBuf *image_buffer, void *lock) override
|
||||
{
|
||||
ED_space_image_release_buffer(sima, image_buffer, lock);
|
||||
}
|
||||
|
||||
void get_shader_parameters(ShaderParameters &r_shader_parameters, ImBuf *image_buffer) override
|
||||
{
|
||||
const int sima_flag = sima->flag & ED_space_image_get_display_channel_mask(image_buffer);
|
||||
if ((sima_flag & SI_USE_ALPHA) != 0) {
|
||||
/* Show RGBA */
|
||||
r_shader_parameters.flags |= IMAGE_DRAW_FLAG_SHOW_ALPHA | IMAGE_DRAW_FLAG_APPLY_ALPHA;
|
||||
}
|
||||
else if ((sima_flag & SI_SHOW_ALPHA) != 0) {
|
||||
r_shader_parameters.flags |= IMAGE_DRAW_FLAG_SHUFFLING;
|
||||
r_shader_parameters.shuffle = float4(0.0f, 0.0f, 0.0f, 1.0f);
|
||||
}
|
||||
else if ((sima_flag & SI_SHOW_ZBUF) != 0) {
|
||||
r_shader_parameters.flags |= IMAGE_DRAW_FLAG_DEPTH | IMAGE_DRAW_FLAG_SHUFFLING;
|
||||
r_shader_parameters.shuffle = float4(1.0f, 0.0f, 0.0f, 0.0f);
|
||||
}
|
||||
else if ((sima_flag & SI_SHOW_R) != 0) {
|
||||
r_shader_parameters.flags |= IMAGE_DRAW_FLAG_SHUFFLING;
|
||||
if (IMB_alpha_affects_rgb(image_buffer)) {
|
||||
r_shader_parameters.flags |= IMAGE_DRAW_FLAG_APPLY_ALPHA;
|
||||
}
|
||||
r_shader_parameters.shuffle = float4(1.0f, 0.0f, 0.0f, 0.0f);
|
||||
}
|
||||
else if ((sima_flag & SI_SHOW_G) != 0) {
|
||||
r_shader_parameters.flags |= IMAGE_DRAW_FLAG_SHUFFLING;
|
||||
if (IMB_alpha_affects_rgb(image_buffer)) {
|
||||
r_shader_parameters.flags |= IMAGE_DRAW_FLAG_APPLY_ALPHA;
|
||||
}
|
||||
r_shader_parameters.shuffle = float4(0.0f, 1.0f, 0.0f, 0.0f);
|
||||
}
|
||||
else if ((sima_flag & SI_SHOW_B) != 0) {
|
||||
r_shader_parameters.flags |= IMAGE_DRAW_FLAG_SHUFFLING;
|
||||
if (IMB_alpha_affects_rgb(image_buffer)) {
|
||||
r_shader_parameters.flags |= IMAGE_DRAW_FLAG_APPLY_ALPHA;
|
||||
}
|
||||
r_shader_parameters.shuffle = float4(0.0f, 0.0f, 1.0f, 0.0f);
|
||||
}
|
||||
else /* RGB */ {
|
||||
if (IMB_alpha_affects_rgb(image_buffer)) {
|
||||
r_shader_parameters.flags |= IMAGE_DRAW_FLAG_APPLY_ALPHA;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool use_tile_drawing() const override
|
||||
{
|
||||
return (sima->flag & SI_DRAW_TILE) != 0;
|
||||
}
|
||||
|
||||
bool use_display_window() const override
|
||||
{
|
||||
return sima->mode == SI_MODE_VIEW;
|
||||
}
|
||||
|
||||
float get_zoom() const override
|
||||
{
|
||||
return this->sima->zoom;
|
||||
}
|
||||
|
||||
float get_aspect_ratio() const override
|
||||
{
|
||||
float2 aspect_ratio;
|
||||
ED_space_image_get_aspect(this->sima, &aspect_ratio.x, &aspect_ratio.y);
|
||||
return aspect_ratio.y;
|
||||
}
|
||||
|
||||
float2 get_pan_offset() const override
|
||||
{
|
||||
/* The offsets are stored with zooming, so retrieve original offsets by multiplying the zoom.
|
||||
* Furthermore, take the negatives since we want the offset of the image, not the space. */
|
||||
return -float2(sima->xof, sima->yof) * sima->zoom;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace blender::image_engine
|
||||
@@ -0,0 +1,108 @@
|
||||
/* SPDX-FileCopyrightText: 2021 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup draw_engine
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "DNA_screen_types.h"
|
||||
#include "DNA_space_types.h"
|
||||
|
||||
#include "image_private.hh"
|
||||
|
||||
namespace blender::image_engine {
|
||||
|
||||
class SpaceNodeAccessor : public AbstractSpaceAccessor {
|
||||
SpaceNode *snode;
|
||||
|
||||
public:
|
||||
SpaceNodeAccessor(SpaceNode *snode) : snode(snode) {}
|
||||
|
||||
blender::Image *get_image(Main *bmain) override
|
||||
{
|
||||
return BKE_image_ensure_viewer(bmain, IMA_TYPE_COMPOSITE, "Viewer Node");
|
||||
}
|
||||
|
||||
ImageUser *get_image_user() override
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ImBuf *acquire_image_buffer(blender::Image *image, void **lock) override
|
||||
{
|
||||
return BKE_image_acquire_ibuf_gpu(image, nullptr, lock);
|
||||
}
|
||||
|
||||
void release_buffer(blender::Image *image, ImBuf *ibuf, void *lock) override
|
||||
{
|
||||
BKE_image_release_ibuf(image, ibuf, lock);
|
||||
}
|
||||
|
||||
void get_shader_parameters(ShaderParameters &r_shader_parameters, ImBuf *ibuf) override
|
||||
{
|
||||
if ((snode->flag & SNODE_USE_ALPHA) != 0) {
|
||||
/* Show RGBA */
|
||||
r_shader_parameters.flags |= IMAGE_DRAW_FLAG_SHOW_ALPHA | IMAGE_DRAW_FLAG_APPLY_ALPHA;
|
||||
}
|
||||
else if ((snode->flag & SNODE_SHOW_ALPHA) != 0) {
|
||||
r_shader_parameters.flags |= IMAGE_DRAW_FLAG_SHUFFLING;
|
||||
r_shader_parameters.shuffle = float4(0.0f, 0.0f, 0.0f, 1.0f);
|
||||
}
|
||||
else if ((snode->flag & SNODE_SHOW_R) != 0) {
|
||||
r_shader_parameters.flags |= IMAGE_DRAW_FLAG_SHUFFLING;
|
||||
if (IMB_alpha_affects_rgb(ibuf)) {
|
||||
r_shader_parameters.flags |= IMAGE_DRAW_FLAG_APPLY_ALPHA;
|
||||
}
|
||||
r_shader_parameters.shuffle = float4(1.0f, 0.0f, 0.0f, 0.0f);
|
||||
}
|
||||
else if ((snode->flag & SNODE_SHOW_G) != 0) {
|
||||
r_shader_parameters.flags |= IMAGE_DRAW_FLAG_SHUFFLING;
|
||||
if (IMB_alpha_affects_rgb(ibuf)) {
|
||||
r_shader_parameters.flags |= IMAGE_DRAW_FLAG_APPLY_ALPHA;
|
||||
}
|
||||
r_shader_parameters.shuffle = float4(0.0f, 1.0f, 0.0f, 0.0f);
|
||||
}
|
||||
else if ((snode->flag & SNODE_SHOW_B) != 0) {
|
||||
r_shader_parameters.flags |= IMAGE_DRAW_FLAG_SHUFFLING;
|
||||
if (IMB_alpha_affects_rgb(ibuf)) {
|
||||
r_shader_parameters.flags |= IMAGE_DRAW_FLAG_APPLY_ALPHA;
|
||||
}
|
||||
r_shader_parameters.shuffle = float4(0.0f, 0.0f, 1.0f, 0.0f);
|
||||
}
|
||||
else /* RGB */ {
|
||||
if (IMB_alpha_affects_rgb(ibuf)) {
|
||||
r_shader_parameters.flags |= IMAGE_DRAW_FLAG_APPLY_ALPHA;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool use_tile_drawing() const override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool use_display_window() const override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
float get_zoom() const override
|
||||
{
|
||||
return this->snode->zoom;
|
||||
}
|
||||
|
||||
float get_aspect_ratio() const override
|
||||
{
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
float2 get_pan_offset() const override
|
||||
{
|
||||
return float2(snode->xof, snode->yof);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace blender::image_engine
|
||||
106
blender-5.2.0/source/blender/draw/engines/image/image_state.hh
Normal file
106
blender-5.2.0/source/blender/draw/engines/image/image_state.hh
Normal file
@@ -0,0 +1,106 @@
|
||||
/* SPDX-FileCopyrightText: 2021 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup draw_engine
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BLI_math_matrix_types.hh"
|
||||
|
||||
#include "BKE_image_wrappers.hh"
|
||||
|
||||
#include "image_batches.hh"
|
||||
#include "image_buffer_cache.hh"
|
||||
#include "image_partial_updater.hh"
|
||||
#include "image_private.hh"
|
||||
#include "image_shader_params.hh"
|
||||
#include "image_texture_info.hh"
|
||||
#include "image_usage.hh"
|
||||
|
||||
#include "DRW_render.hh"
|
||||
#include "draw_command.hh"
|
||||
#include "draw_manager.hh"
|
||||
#include "draw_pass.hh"
|
||||
|
||||
namespace blender::image_engine {
|
||||
using namespace blender::draw;
|
||||
|
||||
struct State {
|
||||
blender::Image *image = nullptr;
|
||||
/** Usage data of the previous time, to identify changes that require a full update. */
|
||||
ImageUsage last_usage;
|
||||
|
||||
PartialImageUpdater partial_update = {};
|
||||
|
||||
View view = {"Image.View"};
|
||||
ShaderParameters sh_params;
|
||||
struct {
|
||||
/**
|
||||
* \brief should we perform tiled drawing (wrap repeat).
|
||||
*
|
||||
* Option is true when image is capable of tile drawing (image is not tile) and the tiled
|
||||
* option is set in the space.
|
||||
*/
|
||||
bool do_tile_drawing : 1;
|
||||
} flags;
|
||||
|
||||
Framebuffer depth_fb = {"Image.Depth"};
|
||||
Framebuffer color_fb = {"Image.Color"};
|
||||
|
||||
PassSimple depth_ps = {"Image.Depth"};
|
||||
PassSimple image_ps = {"Image.Color"};
|
||||
|
||||
/**
|
||||
* Cache containing the float buffers when drawing byte images.
|
||||
*/
|
||||
FloatBufferCache float_buffers;
|
||||
|
||||
/** \brief Transform matrix to convert a normalized screen space coordinates to texture space. */
|
||||
float3x3 ss_to_texture;
|
||||
|
||||
Vector<TextureInfo> texture_infos;
|
||||
|
||||
public:
|
||||
virtual ~State() = default;
|
||||
|
||||
void clear_need_full_update_flag()
|
||||
{
|
||||
reset_need_full_update(false);
|
||||
}
|
||||
void mark_all_texture_slots_dirty()
|
||||
{
|
||||
reset_need_full_update(true);
|
||||
}
|
||||
|
||||
void update_batches()
|
||||
{
|
||||
for (TextureInfo &info : texture_infos) {
|
||||
BatchUpdater batch_updater(info);
|
||||
batch_updater.update_batch();
|
||||
}
|
||||
}
|
||||
|
||||
void update_image_usage(const ImageUser *image_user)
|
||||
{
|
||||
ImageUsage usage(image, image_user, flags.do_tile_drawing);
|
||||
if (last_usage != usage) {
|
||||
last_usage = usage;
|
||||
reset_need_full_update(true);
|
||||
float_buffers.clear();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
/** \brief Set dirty flag of all texture slots to the given value. */
|
||||
void reset_need_full_update(bool new_value)
|
||||
{
|
||||
for (TextureInfo &info : texture_infos) {
|
||||
info.need_full_update = new_value;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace blender::image_engine
|
||||
@@ -0,0 +1,97 @@
|
||||
/* SPDX-FileCopyrightText: 2022 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup draw_engine
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BLI_math_matrix.hh"
|
||||
#include "BLI_rect.h"
|
||||
|
||||
#include "GPU_batch.hh"
|
||||
#include "GPU_texture.hh"
|
||||
|
||||
#include "DRW_gpu_wrapper.hh"
|
||||
#include "DRW_render.hh"
|
||||
|
||||
namespace blender::image_engine {
|
||||
using namespace blender::draw;
|
||||
|
||||
struct TextureInfo : NonCopyable {
|
||||
/**
|
||||
* \brief does this texture need a full update.
|
||||
*
|
||||
* When set to false the texture can be updated using a partial update.
|
||||
*/
|
||||
bool need_full_update : 1;
|
||||
|
||||
/** \brief area of the texture in screen space. */
|
||||
rcti clipping_bounds;
|
||||
/** \brief uv area of the texture in screen space. */
|
||||
rctf clipping_uv_bounds;
|
||||
|
||||
/* Which tile of the screen is used with this texture. Used to safely calculate the correct
|
||||
* offset of the textures. */
|
||||
int2 tile_id;
|
||||
|
||||
/**
|
||||
* \brief Batch to draw the associated text on the screen.
|
||||
*
|
||||
* Contains a VBO with `pos` and `uv`.
|
||||
* `pos` (2xI32) is relative to the origin of the space.
|
||||
* `uv` (2xF32) reflect the uv bounds.
|
||||
*/
|
||||
gpu::Batch *batch = nullptr;
|
||||
|
||||
/**
|
||||
* \brief GPU Texture for a partial region of the image editor.
|
||||
*/
|
||||
Texture texture = {"Image.Tile"};
|
||||
|
||||
int2 last_texture_size = int2(0);
|
||||
|
||||
TextureInfo() = default;
|
||||
TextureInfo(TextureInfo &&other) = default;
|
||||
|
||||
~TextureInfo()
|
||||
{
|
||||
if (batch != nullptr) {
|
||||
GPU_batch_discard(batch);
|
||||
batch = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief return the offset of the texture with the area.
|
||||
*
|
||||
* A texture covers only a part of the area. The offset if the offset in screen coordinates
|
||||
* between the area and the part that the texture covers.
|
||||
*/
|
||||
int2 offset() const
|
||||
{
|
||||
return int2(clipping_bounds.xmin, clipping_bounds.ymin);
|
||||
}
|
||||
|
||||
void ensure_gpu_texture(int2 texture_size)
|
||||
{
|
||||
const bool is_allocated = texture.is_valid();
|
||||
const bool resolution_changed = assign_if_different(last_texture_size, texture_size);
|
||||
const bool should_be_freed = is_allocated && resolution_changed;
|
||||
const bool should_be_created = !is_allocated || resolution_changed;
|
||||
|
||||
if (should_be_freed) {
|
||||
texture.free();
|
||||
}
|
||||
|
||||
if (should_be_created) {
|
||||
texture.ensure_2d(
|
||||
gpu::TextureFormat::SFLOAT_16_16_16_16, texture_size, GPU_TEXTURE_USAGE_SHADER_READ);
|
||||
}
|
||||
need_full_update |= should_be_created;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace blender::image_engine
|
||||
@@ -0,0 +1,63 @@
|
||||
/* SPDX-FileCopyrightText: 2022 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup draw_engine
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include "DNA_color_types.h"
|
||||
#include "DNA_image_types.h"
|
||||
|
||||
namespace blender::image_engine {
|
||||
|
||||
/**
|
||||
* ImageUsage contains data of the image and image user to identify changes that require a rebuild
|
||||
* the texture slots.
|
||||
*/
|
||||
struct ImageUsage {
|
||||
/** Render pass of the image that is used. */
|
||||
short pass = 0;
|
||||
/** Layer of the image that is used. */
|
||||
short layer = 0;
|
||||
/** View of the image that is used. */
|
||||
short view = 0;
|
||||
|
||||
ColorManagedColorspaceSettings colorspace_settings;
|
||||
/** IMA_ALPHA_* */
|
||||
char alpha_mode;
|
||||
bool last_tile_drawing;
|
||||
|
||||
const void *last_image = nullptr;
|
||||
const void *last_scene = nullptr;
|
||||
|
||||
ImageUsage() = default;
|
||||
ImageUsage(const blender::Image *image,
|
||||
const blender::ImageUser *image_user,
|
||||
bool do_tile_drawing)
|
||||
{
|
||||
pass = image_user ? image_user->pass : 0;
|
||||
layer = image_user ? image_user->layer : 0;
|
||||
view = image_user ? image_user->multi_index : 0;
|
||||
colorspace_settings = image->colorspace_settings;
|
||||
alpha_mode = image->alpha_mode;
|
||||
last_image = static_cast<const void *>(image);
|
||||
last_scene = image_user ? static_cast<const void *>(image_user->scene) : nullptr;
|
||||
last_tile_drawing = do_tile_drawing;
|
||||
}
|
||||
|
||||
bool operator==(const ImageUsage &other) const
|
||||
{
|
||||
return memcmp(this, &other, sizeof(ImageUsage)) == 0;
|
||||
}
|
||||
bool operator!=(const ImageUsage &other) const
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace blender::image_engine
|
||||
@@ -0,0 +1,23 @@
|
||||
/* SPDX-FileCopyrightText: 2022-2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "infos/engine_image_infos.hh"
|
||||
|
||||
#include "draw_colormanagement_lib.glsl"
|
||||
#include "image_engine_lib.glsl"
|
||||
|
||||
void main()
|
||||
{
|
||||
int2 uvs_clamped = int2(uv_screen);
|
||||
float depth = texelFetch(depth_tx, uvs_clamped, 0).r;
|
||||
if (depth == 1.0f) {
|
||||
gpu_discard_fragment();
|
||||
return;
|
||||
}
|
||||
|
||||
float4 tex_color = texelFetch(image_tx, uvs_clamped - offset, 0);
|
||||
|
||||
out_color = image_engine_apply_parameters(
|
||||
tex_color, draw_flags, is_image_premultiplied, shuffle, FAR_DISTANCE, NEAR_DISTANCE);
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
/* SPDX-FileCopyrightText: 2020-2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "draw_model_lib.glsl"
|
||||
#include "draw_view_lib.glsl"
|
||||
|
||||
void main()
|
||||
{
|
||||
float3 image_pos = float3(pos.x, pos.y, 0.0f);
|
||||
uv_screen = image_pos.xy;
|
||||
|
||||
float4 position = drw_point_world_to_homogenous(image_pos);
|
||||
position.z = 0.0f;
|
||||
gl_Position = position;
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
/* SPDX-FileCopyrightText: 2022 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "infos/engine_image_infos.hh"
|
||||
|
||||
#include "draw_colormanagement_lib.glsl"
|
||||
#include "image_engine_lib.glsl"
|
||||
|
||||
bool is_border(float2 uv)
|
||||
{
|
||||
return (uv.x < min_max_uv.x || uv.y < min_max_uv.y || uv.x >= min_max_uv.z ||
|
||||
uv.y >= min_max_uv.w);
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
bool border = is_border(uv_image);
|
||||
gl_FragDepth = border ? Z_DEPTH_BORDER : Z_DEPTH_IMAGE;
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
/* SPDX-FileCopyrightText: 2020-2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "draw_model_lib.glsl"
|
||||
#include "draw_view_lib.glsl"
|
||||
|
||||
void main()
|
||||
{
|
||||
float3 image_pos = float3(pos.x, pos.y, 0.0f);
|
||||
uv_image = uv;
|
||||
|
||||
float4 position = drw_point_world_to_homogenous(image_pos);
|
||||
position.z = 0.0f;
|
||||
gl_Position = position;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
/* SPDX-FileCopyrightText: 2026 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "infos/engine_image_infos.hh"
|
||||
|
||||
#include "gpu_shader_math_matrix_transform_lib.glsl"
|
||||
#include "image_engine_lib.glsl"
|
||||
|
||||
void main()
|
||||
{
|
||||
const float2 coordinates = transform_point(to_float3x3(image_matrix), screen_uv);
|
||||
if (!is_repeated &&
|
||||
(any(lessThan(coordinates, float2(0.0))) || any(greaterThan(coordinates, float2(1.0)))))
|
||||
{
|
||||
out_color = float4(0.0f);
|
||||
gl_FragDepth = Z_DEPTH_BORDER;
|
||||
return;
|
||||
}
|
||||
|
||||
const float4 image_color = texture(image_tx, coordinates);
|
||||
out_color = image_engine_apply_parameters(
|
||||
image_color, draw_flags, is_image_premultiplied, shuffle, FAR_DISTANCE, NEAR_DISTANCE);
|
||||
gl_FragDepth = Z_DEPTH_IMAGE;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
/* SPDX-FileCopyrightText: 2026 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "infos/engine_image_infos.hh"
|
||||
|
||||
#include "gpu_shader_math_matrix_transform_lib.glsl"
|
||||
#include "gpu_shader_tiled_image_lookup_lib.glsl"
|
||||
#include "image_engine_lib.glsl"
|
||||
|
||||
void main()
|
||||
{
|
||||
const float2 coordinates = transform_point(to_float3x3(image_matrix), screen_uv);
|
||||
float3 tiled_coordinates = float3(coordinates, 0.0f);
|
||||
if (!tiled_image_lookup(tiled_coordinates, image_tile_array, image_tile_data)) {
|
||||
out_color = float4(0.0f);
|
||||
gl_FragDepth = Z_DEPTH_BORDER;
|
||||
return;
|
||||
}
|
||||
|
||||
const float4 image_color = texture(image_tile_array, tiled_coordinates);
|
||||
out_color = image_engine_apply_parameters(
|
||||
image_color, draw_flags, is_image_premultiplied, shuffle, FAR_DISTANCE, NEAR_DISTANCE);
|
||||
gl_FragDepth = Z_DEPTH_IMAGE;
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/* SPDX-FileCopyrightText: 2026 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "image_shader_shared.hh"
|
||||
|
||||
#define Z_DEPTH_BORDER 1.0f
|
||||
#define Z_DEPTH_IMAGE 0.75f
|
||||
|
||||
#define FAR_DISTANCE far_near_distances.x
|
||||
#define NEAR_DISTANCE far_near_distances.y
|
||||
|
||||
float4 image_engine_apply_parameters(float4 color,
|
||||
int flags,
|
||||
bool is_image_premultiplied,
|
||||
float4 shuffle_color,
|
||||
float far_distance,
|
||||
float near_distance)
|
||||
{
|
||||
float4 result = color;
|
||||
if ((flags & IMAGE_DRAW_FLAG_APPLY_ALPHA) != 0) {
|
||||
if (!is_image_premultiplied) {
|
||||
result.rgb *= result.a;
|
||||
}
|
||||
}
|
||||
if ((flags & IMAGE_DRAW_FLAG_DEPTH) != 0) {
|
||||
result = smoothstep(far_distance, near_distance, result);
|
||||
}
|
||||
|
||||
if ((flags & IMAGE_DRAW_FLAG_SHUFFLING) != 0) {
|
||||
result = float4(dot(result, shuffle_color));
|
||||
}
|
||||
if ((flags & IMAGE_DRAW_FLAG_SHOW_ALPHA) == 0) {
|
||||
result.a = 1.0f;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#ifdef GPU_SHADER
|
||||
# pragma once
|
||||
|
||||
# include "draw_view_infos.hh"
|
||||
# include "gpu_shader_fullscreen_infos.hh"
|
||||
#endif
|
||||
|
||||
#include "gpu_shader_create_info.hh"
|
||||
|
||||
GPU_SHADER_INTERFACE_INFO(image_engine_color_iface)
|
||||
SMOOTH(float2, uv_screen)
|
||||
GPU_SHADER_INTERFACE_END()
|
||||
|
||||
GPU_SHADER_CREATE_INFO(image_engine_color_shader)
|
||||
VERTEX_IN(0, int2, pos)
|
||||
VERTEX_OUT(image_engine_color_iface)
|
||||
FRAGMENT_OUT(0, float4, out_color)
|
||||
PUSH_CONSTANT(float4, shuffle)
|
||||
PUSH_CONSTANT(float2, far_near_distances)
|
||||
PUSH_CONSTANT(int2, offset)
|
||||
PUSH_CONSTANT(int, draw_flags)
|
||||
PUSH_CONSTANT(bool, is_image_premultiplied)
|
||||
SAMPLER(0, sampler2D, image_tx)
|
||||
SAMPLER(1, sampler2DDepth, depth_tx)
|
||||
VERTEX_SOURCE("image_engine_color_vert.glsl")
|
||||
FRAGMENT_SOURCE("image_engine_color_frag.glsl")
|
||||
ADDITIONAL_INFO(draw_view)
|
||||
ADDITIONAL_INFO(draw_modelmat)
|
||||
DO_STATIC_COMPILATION()
|
||||
GPU_SHADER_CREATE_END()
|
||||
|
||||
GPU_SHADER_INTERFACE_INFO(image_engine_depth_iface)
|
||||
SMOOTH(float2, uv_image)
|
||||
GPU_SHADER_INTERFACE_END()
|
||||
|
||||
GPU_SHADER_CREATE_INFO(image_engine_depth_shader)
|
||||
VERTEX_IN(0, int2, pos)
|
||||
VERTEX_IN(1, float2, uv)
|
||||
VERTEX_OUT(image_engine_depth_iface)
|
||||
PUSH_CONSTANT(float4, min_max_uv)
|
||||
VERTEX_SOURCE("image_engine_depth_vert.glsl")
|
||||
FRAGMENT_SOURCE("image_engine_depth_frag.glsl")
|
||||
ADDITIONAL_INFO(draw_view)
|
||||
ADDITIONAL_INFO(draw_modelmat)
|
||||
DEPTH_WRITE(DepthWrite::ANY)
|
||||
DO_STATIC_COMPILATION()
|
||||
GPU_SHADER_CREATE_END()
|
||||
|
||||
GPU_SHADER_CREATE_INFO(image_engine_image_shared)
|
||||
FRAGMENT_OUT(0, float4, out_color)
|
||||
PUSH_CONSTANT(float4x4, image_matrix)
|
||||
PUSH_CONSTANT(float4, shuffle)
|
||||
PUSH_CONSTANT(float2, far_near_distances)
|
||||
PUSH_CONSTANT(int, draw_flags)
|
||||
PUSH_CONSTANT(bool, is_image_premultiplied)
|
||||
ADDITIONAL_INFO(gpu_fullscreen)
|
||||
DEPTH_WRITE(DepthWrite::ANY)
|
||||
GPU_SHADER_CREATE_END()
|
||||
|
||||
GPU_SHADER_CREATE_INFO(image_engine_image_shader)
|
||||
ADDITIONAL_INFO(image_engine_image_shared)
|
||||
PUSH_CONSTANT(bool, is_repeated)
|
||||
SAMPLER(0, sampler2D, image_tx)
|
||||
FRAGMENT_SOURCE("image_engine_image_frag.glsl")
|
||||
DO_STATIC_COMPILATION()
|
||||
GPU_SHADER_CREATE_END()
|
||||
|
||||
GPU_SHADER_CREATE_INFO(image_engine_image_tiled_shader)
|
||||
ADDITIONAL_INFO(image_engine_image_shared)
|
||||
SAMPLER(0, sampler2DArray, image_tile_array)
|
||||
SAMPLER(1, sampler1DArray, image_tile_data)
|
||||
FRAGMENT_SOURCE("image_engine_image_tiled_frag.glsl")
|
||||
DO_STATIC_COMPILATION()
|
||||
GPU_SHADER_CREATE_END()
|
||||
Reference in New Issue
Block a user