From 151a53110c3fe030cda2ade964bb37c19a77e4f3 Mon Sep 17 00:00:00 2001 From: Omar Emara Date: Tue, 25 Apr 2023 09:04:35 +0200 Subject: [PATCH] Realtime Compositor: Implement Texture node This patch implements the Texture node for the realtime compositor. The evaluation of the texture is not GPU accelerated, but is cached as a form of temporary implementation since the Texture node is deprecated and will be removed in the future. Furthermore, texture node evaluation is not supported for now. This patch also introduces the concept of an ID static cache, which uses the DrawDataList mechanism to invalidate the cache as needed, consequently, a DrawDataList was added to the Tex ID structure. An improvement that should be implemented outside of this patch is to implement support for proxy textures in results to avoid redundant copies in the execute method of the texture node. This should be straightforward bit will be implemented in a separate patch. Pull Request: https://projects.blender.org/blender/blender/pulls/107291 --- source/blender/blenkernel/intern/texture.cc | 6 ++ .../realtime_compositor/CMakeLists.txt | 14 +++ .../realtime_compositor/COM_context.hh | 9 ++ .../COM_static_cache_manager.hh | 21 ++++ .../cached_resources/COM_cached_texture.hh | 55 ++++++++++ .../cached_resources/intern/cached_texture.cc | 102 ++++++++++++++++++ .../intern/static_cache_manager.cc | 34 ++++++ .../engines/compositor/compositor_engine.cc | 10 ++ source/blender/draw/intern/draw_manager.c | 1 + source/blender/makesdna/DNA_texture_types.h | 12 ++- .../composite/nodes/node_composite_texture.cc | 56 ++++++++-- 11 files changed, 308 insertions(+), 12 deletions(-) create mode 100644 source/blender/compositor/realtime_compositor/cached_resources/COM_cached_texture.hh create mode 100644 source/blender/compositor/realtime_compositor/cached_resources/intern/cached_texture.cc diff --git a/source/blender/blenkernel/intern/texture.cc b/source/blender/blenkernel/intern/texture.cc index d2cb4c4b201..7f9e4bbdc07 100644 --- a/source/blender/blenkernel/intern/texture.cc +++ b/source/blender/blenkernel/intern/texture.cc @@ -56,6 +56,8 @@ #include "RE_texture.h" +#include "DRW_engine.h" + #include "BLO_read_write.h" static void texture_init_data(ID *id) @@ -100,6 +102,8 @@ static void texture_copy_data(Main *bmain, ID *id_dst, const ID *id_src, const i texture_dst->nodetree->owner_id = &texture_dst->id; } + BLI_listbase_clear((ListBase *)&texture_dst->drawdata); + if ((flag & LIB_ID_COPY_NO_PREVIEW) == 0) { BKE_previewimg_id_copy(&texture_dst->id, &texture_src->id); } @@ -112,6 +116,8 @@ static void texture_free_data(ID *id) { Tex *texture = (Tex *)id; + DRW_drawdata_free(id); + /* is no lib link block, but texture extension */ if (texture->nodetree) { ntreeFreeEmbeddedTree(texture->nodetree); diff --git a/source/blender/compositor/realtime_compositor/CMakeLists.txt b/source/blender/compositor/realtime_compositor/CMakeLists.txt index ab584423dd1..914d6c3e220 100644 --- a/source/blender/compositor/realtime_compositor/CMakeLists.txt +++ b/source/blender/compositor/realtime_compositor/CMakeLists.txt @@ -70,12 +70,14 @@ set(SRC algorithms/COM_algorithm_smaa.hh algorithms/COM_algorithm_symmetric_separable_blur.hh + cached_resources/intern/cached_texture.cc cached_resources/intern/morphological_distance_feather_weights.cc cached_resources/intern/smaa_precomputed_textures.cc cached_resources/intern/symmetric_blur_weights.cc cached_resources/intern/symmetric_separable_blur_weights.cc cached_resources/COM_cached_resource.hh + cached_resources/COM_cached_texture.hh cached_resources/COM_morphological_distance_feather_weights.hh cached_resources/COM_smaa_precomputed_textures.hh cached_resources/COM_symmetric_blur_weights.hh @@ -248,4 +250,16 @@ endforeach() set(shader_create_info_list_file "${CMAKE_CURRENT_BINARY_DIR}/compositor_shader_create_info_list.hh") file(GENERATE OUTPUT ${shader_create_info_list_file} CONTENT "${SHADER_CREATE_INFOS_CONTENT}") +if(WITH_TBB) + list(APPEND INC_SYS + ${TBB_INCLUDE_DIRS} + ) + add_definitions(-DWITH_TBB) + if(WIN32) + # TBB includes Windows.h which will define min/max macros + # that will collide with the stl versions. + add_definitions(-DNOMINMAX) + endif() +endif() + blender_add_lib(bf_realtime_compositor "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") diff --git a/source/blender/compositor/realtime_compositor/COM_context.hh b/source/blender/compositor/realtime_compositor/COM_context.hh index 99eac679640..58a422416d6 100644 --- a/source/blender/compositor/realtime_compositor/COM_context.hh +++ b/source/blender/compositor/realtime_compositor/COM_context.hh @@ -5,6 +5,7 @@ #include "BLI_math_vector_types.hh" #include "BLI_string_ref.hh" +#include "DNA_ID.h" #include "DNA_scene_types.h" #include "DNA_vec_types.h" @@ -70,6 +71,14 @@ class Context { * appropriate place, which can be directly in the UI or just logged to the output stream. */ virtual void set_info_message(StringRef message) const = 0; + /* Returns the ID recalculate flag of the given ID and reset it to zero. The given ID is assumed + * to be one that has a DrawDataList and conforms to the IdDdtTemplate. + * + * The ID recalculate flag is a mechanism through which one can identify if an ID has changed + * since the last time the flag was reset, hence why the method reset the flag after querying it, + * that is, to ready it to track the next change. */ + virtual IDRecalcFlag query_id_recalc_flag(ID *id) const = 0; + /* Get the size of the compositing region. See get_compositing_region(). */ int2 get_compositing_region_size() const; diff --git a/source/blender/compositor/realtime_compositor/COM_static_cache_manager.hh b/source/blender/compositor/realtime_compositor/COM_static_cache_manager.hh index 37d95624f2b..ee0c7ee4bfc 100644 --- a/source/blender/compositor/realtime_compositor/COM_static_cache_manager.hh +++ b/source/blender/compositor/realtime_compositor/COM_static_cache_manager.hh @@ -7,6 +7,11 @@ #include "BLI_map.hh" #include "BLI_math_vector_types.hh" +#include "DNA_scene_types.h" +#include "DNA_texture_types.h" + +#include "COM_cached_texture.hh" +#include "COM_context.hh" #include "COM_morphological_distance_feather_weights.hh" #include "COM_smaa_precomputed_textures.hh" #include "COM_symmetric_blur_weights.hh" @@ -14,6 +19,8 @@ namespace blender::realtime_compositor { +class Context; + /* ------------------------------------------------------------------------------------------------- * Static Cache Manager * @@ -48,6 +55,11 @@ class StaticCacheManager { Map> morphological_distance_feather_weights_; + /* A nested map that stores all CachedTexture cached resources. The outer map identifies the + * textures using their ID name, while the inner map identifies the textures using their + * parameters. */ + Map>> cached_textures_; + /* A unique pointers that stores the cached SMAAPrecomputedTextures, if one is cached. */ std::unique_ptr smaa_precomputed_textures_; @@ -77,6 +89,15 @@ class StaticCacheManager { MorphologicalDistanceFeatherWeights &get_morphological_distance_feather_weights(int type, int radius); + /* Check if the given texture ID has changed since the last time it was retrieved through its + * recalculate flag, and if so, invalidate its corresponding cached textures and reset the + * recalculate flag to ready it to track the next change. Then, check if there is an available + * CachedTexture cached resource with the given parameters in the manager, if one exists, return + * it, otherwise, return a newly created one and add it to the manager. In both cases, tag the + * cached resource as needed to keep it cached for the next evaluation. */ + CachedTexture &get_cached_texture( + Context &context, Tex *texture, const Scene *scene, int2 size, float2 offset, float2 scale); + /* Check if a cached SMAA precomputed texture exists, if it does, return it, otherwise, return * a newly created one and store it in the manager. In both cases, tag the cached resource as * needed to keep it cached for the next evaluation. */ diff --git a/source/blender/compositor/realtime_compositor/cached_resources/COM_cached_texture.hh b/source/blender/compositor/realtime_compositor/cached_resources/COM_cached_texture.hh new file mode 100644 index 00000000000..2c79ff6b322 --- /dev/null +++ b/source/blender/compositor/realtime_compositor/cached_resources/COM_cached_texture.hh @@ -0,0 +1,55 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#include + +#include "BLI_math_vector_types.hh" + +#include "GPU_texture.h" + +#include "DNA_scene_types.h" +#include "DNA_texture_types.h" + +#include "COM_cached_resource.hh" + +namespace blender::realtime_compositor { + +/* ------------------------------------------------------------------------------------------------ + * Cached Texture Key. + */ +class CachedTextureKey { + public: + int2 size; + float2 offset; + float2 scale; + + CachedTextureKey(int2 size, float2 offset, float2 scale); + + uint64_t hash() const; +}; + +bool operator==(const CachedTextureKey &a, const CachedTextureKey &b); + +/* ------------------------------------------------------------------------------------------------- + * Cached Texture. + * + * A cached resource that computes and caches a GPU texture containing the the result of evaluating + * the given texture ID on a space that spans the given size, modified by the given offset and + * scale. */ +class CachedTexture : public CachedResource { + private: + GPUTexture *color_texture_ = nullptr; + GPUTexture *value_texture_ = nullptr; + + public: + CachedTexture(Tex *texture, const Scene *scene, int2 size, float2 offset, float2 scale); + + ~CachedTexture(); + + GPUTexture *color_texture(); + + GPUTexture *value_texture(); +}; + +} // namespace blender::realtime_compositor diff --git a/source/blender/compositor/realtime_compositor/cached_resources/intern/cached_texture.cc b/source/blender/compositor/realtime_compositor/cached_resources/intern/cached_texture.cc new file mode 100644 index 00000000000..c0a80045a8f --- /dev/null +++ b/source/blender/compositor/realtime_compositor/cached_resources/intern/cached_texture.cc @@ -0,0 +1,102 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include + +#include "BLI_array.hh" +#include "BLI_hash.hh" +#include "BLI_index_range.hh" +#include "BLI_math_vector_types.hh" +#include "BLI_task.hh" + +#include "GPU_texture.h" + +#include "BKE_texture.h" + +#include "DNA_scene_types.h" +#include "DNA_texture_types.h" + +#include "RE_texture.h" + +#include "COM_cached_texture.hh" + +namespace blender::realtime_compositor { + +/* -------------------------------------------------------------------- + * Cached Texture Key. + */ + +CachedTextureKey::CachedTextureKey(int2 size, float2 offset, float2 scale) + : size(size), offset(offset), scale(scale) +{ +} + +uint64_t CachedTextureKey::hash() const +{ + return get_default_hash_3(size, offset, scale); +} + +bool operator==(const CachedTextureKey &a, const CachedTextureKey &b) +{ + return a.size == b.size && a.offset == b.offset && a.scale == b.scale; +} + +/* -------------------------------------------------------------------- + * Cached Texture. + */ + +CachedTexture::CachedTexture( + Tex *texture, const Scene *scene, int2 size, float2 offset, float2 scale) +{ + Array color_pixels(size.x * size.y); + Array value_pixels(size.x * size.y); + threading::parallel_for(IndexRange(size.y), 1, [&](const IndexRange sub_y_range) { + for (const int64_t y : sub_y_range) { + for (const int64_t x : IndexRange(size.x)) { + /* Compute the coordinates in the [0, 1] range and add 0.5 to evaluate the texture at the + * center of pixels in case it was interpolated. */ + float2 coordinates = ((float2(x, y) + 0.5f) / float2(size)) * 2.0f - 1.0f; + /* Note that it is expected that the offset is scaled by the scale. */ + coordinates = (coordinates + offset) * scale; + TexResult texture_result; + BKE_texture_get_value(scene, texture, coordinates, &texture_result, true); + color_pixels[y * size.x + x] = float4(texture_result.trgba); + value_pixels[y * size.x + x] = texture_result.talpha ? texture_result.trgba[3] : + texture_result.tin; + } + } + }); + + color_texture_ = GPU_texture_create_2d("Cached Color Texture", + size.x, + size.y, + 1, + GPU_RGBA16F, + GPU_TEXTURE_USAGE_SHADER_READ, + *color_pixels.data()); + + value_texture_ = GPU_texture_create_2d("Cached Value Texture", + size.x, + size.y, + 1, + GPU_R16F, + GPU_TEXTURE_USAGE_SHADER_READ, + value_pixels.data()); +} + +CachedTexture::~CachedTexture() +{ + GPU_texture_free(color_texture_); + GPU_texture_free(value_texture_); +} + +GPUTexture *CachedTexture::color_texture() +{ + return color_texture_; +} + +GPUTexture *CachedTexture::value_texture() +{ + return value_texture_; +} + +} // namespace blender::realtime_compositor diff --git a/source/blender/compositor/realtime_compositor/intern/static_cache_manager.cc b/source/blender/compositor/realtime_compositor/intern/static_cache_manager.cc index 55f86b7d0fe..8d723d4a985 100644 --- a/source/blender/compositor/realtime_compositor/intern/static_cache_manager.cc +++ b/source/blender/compositor/realtime_compositor/intern/static_cache_manager.cc @@ -4,6 +4,11 @@ #include "BLI_math_vector_types.hh" +#include "DNA_ID.h" +#include "DNA_scene_types.h" +#include "DNA_texture_types.h" + +#include "COM_context.hh" #include "COM_morphological_distance_feather_weights.hh" #include "COM_smaa_precomputed_textures.hh" #include "COM_symmetric_blur_weights.hh" @@ -23,6 +28,12 @@ void StaticCacheManager::reset() symmetric_blur_weights_.remove_if([](auto item) { return !item.value->needed; }); symmetric_separable_blur_weights_.remove_if([](auto item) { return !item.value->needed; }); morphological_distance_feather_weights_.remove_if([](auto item) { return !item.value->needed; }); + + for (auto &cached_textures_for_id : cached_textures_.values()) { + cached_textures_for_id.remove_if([](auto item) { return !item.value->needed; }); + } + cached_textures_.remove_if([](auto item) { return item.value.is_empty(); }); + if (smaa_precomputed_textures_ && !smaa_precomputed_textures_->needed) { smaa_precomputed_textures_.reset(); } @@ -38,6 +49,11 @@ void StaticCacheManager::reset() for (auto &value : morphological_distance_feather_weights_.values()) { value->needed = false; } + for (auto &cached_textures_for_id : cached_textures_.values()) { + for (auto &value : cached_textures_for_id.values()) { + value->needed = false; + } + } if (smaa_precomputed_textures_) { smaa_precomputed_textures_->needed = false; } @@ -78,6 +94,24 @@ MorphologicalDistanceFeatherWeights &StaticCacheManager:: return weights; } +CachedTexture &StaticCacheManager::get_cached_texture( + Context &context, Tex *texture, const Scene *scene, int2 size, float2 offset, float2 scale) +{ + const CachedTextureKey key(size, offset, scale); + + auto &cached_textures_for_id = cached_textures_.lookup_or_add_default(texture->id.name); + + if (context.query_id_recalc_flag(reinterpret_cast(texture)) & ID_RECALC_ALL) { + cached_textures_for_id.clear(); + } + + auto &cached_texture = *cached_textures_for_id.lookup_or_add_cb( + key, [&]() { return std::make_unique(texture, scene, size, offset, scale); }); + + cached_texture.needed = true; + return cached_texture; +} + SMAAPrecomputedTextures &StaticCacheManager::get_smaa_precomputed_textures() { if (!smaa_precomputed_textures_) { diff --git a/source/blender/draw/engines/compositor/compositor_engine.cc b/source/blender/draw/engines/compositor/compositor_engine.cc index f024bc706b7..3c6a9bab765 100644 --- a/source/blender/draw/engines/compositor/compositor_engine.cc +++ b/source/blender/draw/engines/compositor/compositor_engine.cc @@ -8,6 +8,7 @@ #include "BLT_translation.h" +#include "DNA_ID.h" #include "DNA_ID_enums.h" #include "DNA_camera_types.h" #include "DNA_object_types.h" @@ -143,6 +144,15 @@ class Context : public realtime_compositor::Context { { message.copy(info_message_, GPU_INFO_SIZE); } + + IDRecalcFlag query_id_recalc_flag(ID *id) const override + { + DrawEngineType *owner = &draw_engine_compositor_type; + DrawData *draw_data = DRW_drawdata_ensure(id, owner, sizeof(DrawData), nullptr, nullptr); + IDRecalcFlag recalc_flag = IDRecalcFlag(draw_data->recalc); + draw_data->recalc = IDRecalcFlag(0); + return recalc_flag; + } }; class Engine { diff --git a/source/blender/draw/intern/draw_manager.c b/source/blender/draw/intern/draw_manager.c index 9ea93637b01..15c63bf6560 100644 --- a/source/blender/draw/intern/draw_manager.c +++ b/source/blender/draw/intern/draw_manager.c @@ -832,6 +832,7 @@ static bool id_type_can_have_drawdata(const short id_type) case ID_OB: case ID_WO: case ID_SCE: + case ID_TE: return true; /* no DrawData */ diff --git a/source/blender/makesdna/DNA_texture_types.h b/source/blender/makesdna/DNA_texture_types.h index 4b3daf92d8b..f37c62f862d 100644 --- a/source/blender/makesdna/DNA_texture_types.h +++ b/source/blender/makesdna/DNA_texture_types.h @@ -153,6 +153,8 @@ typedef struct Tex { ID id; /** Animation data (must be immediately after id for utilities to use it). */ struct AnimData *adt; + /* runtime (must be immediately after id for utilities to use it). */ + DrawDataList drawdata; float noisesize, turbul; float bright, contrast, saturation, rfac, gfac, bfac; @@ -264,14 +266,14 @@ typedef struct ColorMapping { #define TEX_STUCCI 6 #define TEX_NOISE 7 #define TEX_IMAGE 8 -//#define TEX_PLUGIN 9 /* Deprecated */ -//#define TEX_ENVMAP 10 /* Deprecated */ +// #define TEX_PLUGIN 9 /* Deprecated */ +// #define TEX_ENVMAP 10 /* Deprecated */ #define TEX_MUSGRAVE 11 #define TEX_VORONOI 12 #define TEX_DISTNOISE 13 -//#define TEX_POINTDENSITY 14 /* Deprecated */ -//#define TEX_VOXELDATA 15 /* Deprecated */ -//#define TEX_OCEAN 16 /* Deprecated */ +// #define TEX_POINTDENSITY 14 /* Deprecated */ +// #define TEX_VOXELDATA 15 /* Deprecated */ +// #define TEX_OCEAN 16 /* Deprecated */ /* musgrave stype */ #define TEX_MFRACTAL 0 diff --git a/source/blender/nodes/composite/nodes/node_composite_texture.cc b/source/blender/nodes/composite/nodes/node_composite_texture.cc index 64acbc6ed27..c5f41fbbae1 100644 --- a/source/blender/nodes/composite/nodes/node_composite_texture.cc +++ b/source/blender/nodes/composite/nodes/node_composite_texture.cc @@ -7,7 +7,9 @@ #include "BLT_translation.h" +#include "COM_cached_texture.hh" #include "COM_node_operation.hh" +#include "COM_utilities.hh" #include "node_composite_util.hh" @@ -17,12 +19,17 @@ namespace blender::nodes::node_composite_texture_cc { static void cmp_node_texture_declare(NodeDeclarationBuilder &b) { - b.add_input(N_("Offset")).min(-2.0f).max(2.0f).subtype(PROP_TRANSLATION); + b.add_input(N_("Offset")) + .min(-2.0f) + .max(2.0f) + .subtype(PROP_TRANSLATION) + .compositor_expects_single_value(); b.add_input(N_("Scale")) .default_value({1.0f, 1.0f, 1.0f}) .min(-10.0f) .max(10.0f) - .subtype(PROP_XYZ); + .subtype(PROP_XYZ) + .compositor_expects_single_value(); b.add_output(N_("Value")); b.add_output(N_("Color")); } @@ -35,9 +42,46 @@ class TextureOperation : public NodeOperation { void execute() override { - get_result("Value").allocate_invalid(); - get_result("Color").allocate_invalid(); - context().set_info_message("Viewport compositor setup not fully supported"); + Result &color_result = get_result("Color"); + Result &value_result = get_result("Value"); + if (!get_texture()) { + if (color_result.should_compute()) { + color_result.allocate_invalid(); + } + if (value_result.should_compute()) { + value_result.allocate_invalid(); + } + return; + } + + const Domain domain = compute_domain(); + CachedTexture &cached_texture = context().cache_manager().get_cached_texture( + context(), + get_texture(), + context().get_scene(), + domain.size, + get_input("Offset").get_vector_value_default(float4(0.0f)).xy(), + get_input("Scale").get_vector_value_default(float4(0.0f)).xy()); + + if (color_result.should_compute()) { + color_result.allocate_texture(domain); + GPU_texture_copy(color_result.texture(), cached_texture.color_texture()); + } + + if (value_result.should_compute()) { + value_result.allocate_texture(domain); + GPU_texture_copy(value_result.texture(), cached_texture.value_texture()); + } + } + + Domain compute_domain() override + { + return Domain(context().get_compositing_region_size()); + } + + Tex *get_texture() + { + return (Tex *)bnode().id; } }; @@ -58,8 +102,6 @@ void register_node_type_cmp_texture() ntype.declare = file_ns::cmp_node_texture_declare; ntype.flag |= NODE_PREVIEW; ntype.get_compositor_operation = file_ns::get_compositor_operation; - ntype.realtime_compositor_unsupported_message = N_( - "Node not supported in the Viewport compositor"); nodeRegisterType(&ntype); }